Преглед на файлове

New Gallery on properties

master
George Williams преди 5 години
родител
ревизия
585d48f05a
променени са 2 файла, в които са добавени 399 реда и са изтрити 33 реда
  1. 61
    33
      src/components/property/propertyPage.vue
  2. 338
    0
      src/components/shared/gallerySlideShow.vue

+ 61
- 33
src/components/property/propertyPage.vue Целия файл

@@ -6,10 +6,9 @@
6 6
       <div class="row">
7 7
         <div class="col-md-12 col-lg-8">
8 8
           <div class="title-box-d">
9
-            <h1
10
-              class="title-d"
11
-              style="text-align:left; font-size: 250%"
12
-            >{{ property.shortDescription }}</h1>
9
+            <h1 class="title-d" style="text-align:left; font-size: 250%">
10
+              {{ property.shortDescription }}
11
+            </h1>
13 12
           </div>
14 13
         </div>
15 14
       </div>
@@ -20,13 +19,20 @@
20 19
     <section class="property-single nav-arrow-b">
21 20
       <div class="container">
22 21
         <div class="row">
23
-          <lightBox
24
-            :thumbnails="propertyImages"
25
-            :largeImages="propertyImages"
26
-            :caption="false"
27
-            class="lightBox"
28
-          />
22
+          <div id="app" class="scrolling-wrapper">
23
+            <div class="card">
24
+              <img
25
+                class="image"
26
+                v-for="(image, i) in propertyImages"
27
+                :src="image"
28
+                @click="index = i"
29
+                :key="i"
30
+              />
31
+            </div>
32
+            <gallery :images="propertyImages" :index="index" @close="index = null"></gallery>
33
+          </div>
29 34
         </div>
35
+        <br />
30 36
         <div class="row">
31 37
           <div class="col-sm-12">
32 38
             <div class="row justify-content-between">
@@ -69,7 +75,7 @@
69 75
 
70 76
                     <div class="card-title-c align-self-center">
71 77
                       <h5 class="title-c">R{{ formatPrice(property.price) }}</h5>
72
-                      <h6 v-if="property.pricePer">Per {{property.pricePer}}</h6>
78
+                      <h6 v-if="property.pricePer">Per {{ property.pricePer }}</h6>
73 79
                     </div>
74 80
                   </div>
75 81
                   <!-- </div> -->
@@ -90,16 +96,23 @@
90 96
                       </li>
91 97
                       <li class="d-flex justify-content-between">
92 98
                         <strong>Status:</strong>
93
-                        <span
94
-                          v-if="property.status"
95
-                        >{{ property.status.code }} - {{ property.status.description }}</span>
99
+                        <span v-if="property.status"
100
+                          >{{ property.status.code }} - {{ property.status.description }}</span
101
+                        >
96 102
                       </li>
97 103
                       <li class="d-flex justify-content-between">
98 104
                         <strong>Address:</strong>
99 105
                         <span
100 106
                           style="text-align:right"
101 107
                           v-if="property"
102
-                          v-html="formatAddress(property.addressLine1) + formatAddress(property.addressLine2) + formatAddress(property.addressLine3) + formatAddress(property.suburb ? property.suburb.description : '') + formatAddress(property.city ? property.city.description : '') + formatAddress(property.province ? property.province.description : '') "
108
+                          v-html="
109
+                            formatAddress(property.addressLine1) +
110
+                              formatAddress(property.addressLine2) +
111
+                              formatAddress(property.addressLine3) +
112
+                              formatAddress(property.suburb ? property.suburb.description : '') +
113
+                              formatAddress(property.city ? property.city.description : '') +
114
+                              formatAddress(property.province ? property.province.description : '')
115
+                          "
103 116
                         ></span>
104 117
                       </li>
105 118
                     </ul>
@@ -111,7 +124,9 @@
111 124
                     class="btn btn-b-n"
112 125
                     data-toggle="modal"
113 126
                     data-target="#myModal"
114
-                  >Make an Offer</button>
127
+                  >
128
+                    Make an Offer
129
+                  </button>
115 130
                   <div id="myModal" class="modal fade" role="dialog">
116 131
                     <div class="modal-dialog modal-lg">
117 132
                       <!-- Modal content-->
@@ -124,7 +139,12 @@
124 139
                             name="MakeOffer"
125 140
                             :isMakeOffer="true"
126 141
                             :isProperty="true"
127
-                            :item="{id: property.id, shortDescription: property.shortDescription, description: property.description, sellPrice: property.price}"
142
+                            :item="{
143
+                              id: property.id,
144
+                              shortDescription: property.shortDescription,
145
+                              description: property.description,
146
+                              sellPrice: property.price
147
+                            }"
128 148
                           />
129 149
                         </div>
130 150
                       </div>
@@ -249,17 +269,19 @@
249 269
 
250 270
 <script>
251 271
 import { mapState, mapActions } from 'vuex';
252
-import lightBox from '../shared/lightBoxGallery.vue';
253 272
 import makeOffer from '../processFlow/makeOffer.vue';
273
+import gallery from '../shared/gallerySlideShow.vue';
254 274
 
255 275
 export default {
256 276
   name: 'property',
257 277
   components: {
258
-    lightBox,
259 278
     makeOffer,
279
+    gallery,
260 280
   },
261 281
   data() {
262
-    return {};
282
+    return {
283
+      index: null,
284
+    };
263 285
   },
264 286
   mounted() {
265 287
     this.getProperty(this.$route.params.id);
@@ -269,11 +291,7 @@ export default {
269 291
     ...mapState('property', ['property', 'propertyImages']),
270 292
   },
271 293
   methods: {
272
-    ...mapActions('property', [
273
-      'getProperty',
274
-      'getPropertyImages',
275
-      'clearPropertyImages',
276
-    ]),
294
+    ...mapActions('property', ['getProperty', 'getPropertyImages', 'clearPropertyImages']),
277 295
     formatPrice(value) {
278 296
       const val = (value / 1).toFixed(2);
279 297
       return val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
@@ -291,14 +309,24 @@ export default {
291 309
 };
292 310
 </script>
293 311
 
294
-<style lang ="scss">
295
-.light-box {
296
-  &__thumbnail {
297
-    margin: 20px;
298
-    width: 200px;
299
-  }
312
+<style lang="scss">
313
+.image {
314
+  width: 150px;
315
+  height: 150px;
316
+  background-size: cover;
317
+  cursor: pointer;
318
+  margin: 5px;
319
+  border-radius: 3px;
320
+  border: 1px solid lightgray;
321
+  object-fit: contain;
300 322
 }
301
-img {
302
-  max-width: 100%;
323
+.scrolling-wrapper {
324
+  overflow-x: scroll;
325
+  overflow-y: hidden;
326
+  white-space: nowrap;
327
+
328
+  .card {
329
+    display: inline-block;
330
+  }
303 331
 }
304 332
 </style>

+ 338
- 0
src/components/shared/gallerySlideShow.vue Целия файл

@@ -0,0 +1,338 @@
1
+// Source: https://github.com/KitchenStories/vue-gallery-slideshow
2
+<template>
3
+  <transition name="modal">
4
+    <div v-if="imgIndex !== null" class="vgs" @click="close">
5
+      <button type="button" class="vgs__close" @click="close">
6
+        &times;
7
+      </button>
8
+      <button v-if="isMultiple" type="button" class="vgs__prev" @click.stop="onPrev">
9
+        &lsaquo;
10
+      </button>
11
+      <div v-if="images" class="vgs__container" @click.stop="onNext">
12
+        <img class="vgs__container__img" :src="imageUrl" :alt="alt" @click.stop="onNext" />
13
+        <slot></slot>
14
+      </div>
15
+      <button v-if="isMultiple" type="button" class="vgs__next" @click.stop="onNext">
16
+        &rsaquo;
17
+      </button>
18
+      <div v-if="isMultiple" ref="gallery" class="vgs__gallery">
19
+        <div v-if="images" class="vgs__gallery__title">
20
+          {{ imgIndex + 1 }} / {{ images.length }}
21
+        </div>
22
+        <div
23
+          v-if="images"
24
+          class="vgs__gallery__container"
25
+          :style="{ transform: 'translate(' + galleryXPos + 'px, 0)' }"
26
+        >
27
+          <img
28
+            v-for="(img, i) in images"
29
+            :key="i"
30
+            class="vgs__gallery__container__img"
31
+            :src="typeof img === 'string' ? img : img.url"
32
+            :class="{ 'vgs__gallery__container__img--active': i === imgIndex }"
33
+            :alt="typeof img === 'string' ? '' : img.alt"
34
+            @click.stop="onClickThumb(img, i)"
35
+          />
36
+        </div>
37
+      </div>
38
+    </div>
39
+  </transition>
40
+</template>
41
+
42
+<script>
43
+export default {
44
+  props: {
45
+    images: {
46
+      type: Array,
47
+      required: true,
48
+    },
49
+    index: {
50
+      type: Number,
51
+      required: false,
52
+      default: null,
53
+    },
54
+  },
55
+  data() {
56
+    return {
57
+      imgIndex: this.index,
58
+      image: null,
59
+      galleryXPos: 0,
60
+      thumbnailWidth: 120,
61
+    };
62
+  },
63
+  computed: {
64
+    imageUrl() {
65
+      const img = this.images[this.imgIndex];
66
+      if (typeof img === 'string') {
67
+        return img;
68
+      }
69
+      return img.url;
70
+    },
71
+    alt() {
72
+      const img = this.images[this.imgIndex];
73
+      if (typeof img === 'object') {
74
+        return img.alt;
75
+      }
76
+
77
+      return '';
78
+    },
79
+    isMultiple() {
80
+      return this.images.length > 1;
81
+    },
82
+  },
83
+  watch: {
84
+    index(val, prev) {
85
+      this.imgIndex = val;
86
+
87
+      // updateThumbails when popup
88
+      if (prev == null && val != null) {
89
+        this.$nextTick(() => {
90
+          this.updateThumbails();
91
+        });
92
+      }
93
+    },
94
+  },
95
+  mounted() {
96
+    window.addEventListener('keydown', (e) => {
97
+      if (e.keyCode === 37) {
98
+        this.onPrev();
99
+      } else if (e.keyCode === 39) {
100
+        this.onNext();
101
+      } else if (e.keyCode === 27) {
102
+        this.close();
103
+      }
104
+    });
105
+  },
106
+  methods: {
107
+    close() {
108
+      this.imgIndex = null;
109
+      this.$emit('close');
110
+    },
111
+    onPrev() {
112
+      if (this.imgIndex === null) return;
113
+      if (this.imgIndex > 0) {
114
+        this.imgIndex--;
115
+      } else {
116
+        this.imgIndex = this.images.length - 1;
117
+      }
118
+      this.updateThumbails();
119
+    },
120
+    onNext() {
121
+      if (this.imgIndex === null) return;
122
+      if (this.imgIndex < this.images.length - 1) {
123
+        this.imgIndex++;
124
+      } else {
125
+        this.imgIndex = 0;
126
+      }
127
+      this.updateThumbails();
128
+    },
129
+    onClickThumb(image, index) {
130
+      this.imgIndex = index;
131
+      this.updateThumbails();
132
+    },
133
+    updateThumbails() {
134
+      if (!this.$refs.gallery) {
135
+        return;
136
+      }
137
+
138
+      const galleryWidth = this.$refs.gallery.clientWidth;
139
+      const currThumbsWidth = this.imgIndex * this.thumbnailWidth;
140
+      const maxThumbsWidth = this.images.length * this.thumbnailWidth;
141
+      const centerPos = Math.floor(galleryWidth / (this.thumbnailWidth * 2)) * this.thumbnailWidth;
142
+
143
+      // Prevent scrolling of images if not needed
144
+      if (maxThumbsWidth < galleryWidth) {
145
+        return;
146
+      }
147
+
148
+      if (currThumbsWidth < centerPos) {
149
+        this.galleryXPos = 0;
150
+      } else if (
151
+        currThumbsWidth
152
+        > this.images.length * this.thumbnailWidth - galleryWidth + centerPos
153
+      ) {
154
+        this.galleryXPos = -(this.images.length * this.thumbnailWidth - galleryWidth - 20);
155
+      } else {
156
+        this.galleryXPos = -(this.imgIndex * this.thumbnailWidth) + centerPos;
157
+      }
158
+    },
159
+  },
160
+};
161
+</script>
162
+
163
+<style lang="scss">
164
+$black-alpha-80: rgba(0, 0, 0, 0.8);
165
+$black: #000;
166
+$white: #fff;
167
+$radius-medium: 8px;
168
+$radius-large: 12px;
169
+// Breakpoints
170
+$screen-xs: 480px;
171
+$screen-sm: 768px;
172
+$screen-md: 992px;
173
+$screen-lg: 1200px;
174
+// So media queries don't overlap when required, provide a maximum
175
+$screen-xs-max: ($screen-sm - 1);
176
+$screen-sm-max: ($screen-md - 1);
177
+$screen-md-max: ($screen-lg - 1);
178
+@mixin respond-to($media) {
179
+  @if $media==xs {
180
+    @media (max-width: $screen-xs-max) {
181
+      @content;
182
+    }
183
+  } @else if $media==sm {
184
+    @media (min-width: $screen-sm) and (max-width: $screen-sm-max) {
185
+      @content;
186
+    }
187
+  } @else if $media==md {
188
+    @media (min-width: $screen-md) and (max-width: $screen-md-max) {
189
+      @content;
190
+    }
191
+  } @else if $media==lg {
192
+    @media (min-width: $screen-lg) {
193
+      @content;
194
+    }
195
+  }
196
+}
197
+
198
+@mixin modal-base() {
199
+  transition: opacity 0.2s ease;
200
+  position: fixed;
201
+  z-index: 9998;
202
+}
203
+
204
+@mixin modal-mask() {
205
+  @include modal-base();
206
+  top: 0;
207
+  left: 0;
208
+  width: 100%;
209
+  min-height: 100%;
210
+  height: 100vh;
211
+  background-color: $black-alpha-80;
212
+  display: table;
213
+}
214
+
215
+.vgs {
216
+  @include modal-mask();
217
+  &__close {
218
+    color: #fff;
219
+    position: absolute;
220
+    top: 0;
221
+    right: 0;
222
+    background-color: transparent;
223
+    border: none;
224
+    font-size: 25px;
225
+    width: 50px;
226
+    height: 50px;
227
+    cursor: pointer;
228
+    z-index: 999;
229
+    &:focus {
230
+      outline: 0;
231
+    }
232
+  }
233
+  &__prev,
234
+  &__next {
235
+    position: absolute;
236
+    top: 50%;
237
+    margin-top: -25px;
238
+    width: 50px;
239
+    height: 50px;
240
+    z-index: 999;
241
+    cursor: pointer;
242
+    font-size: 40px;
243
+    color: #fff;
244
+    background-color: transparent;
245
+    border: none;
246
+    &:focus {
247
+      outline: 0;
248
+    }
249
+  }
250
+  &__prev {
251
+    left: 0;
252
+  }
253
+  &__next {
254
+    right: 0;
255
+  }
256
+  &__container {
257
+    position: absolute;
258
+    overflow: hidden;
259
+    cursor: pointer;
260
+    overflow: hidden;
261
+    max-width: 100vh;
262
+    margin: 0.5rem auto 0;
263
+    left: 0.5rem;
264
+    right: 0.5rem;
265
+    height: 60vh;
266
+    border-radius: $radius-large;
267
+    background-color: $black;
268
+    @include respond-to(xs) {
269
+      width: 100%;
270
+      max-width: 100%;
271
+      top: 50%;
272
+      margin-top: -140px;
273
+      left: 0;
274
+      right: 0;
275
+      border-radius: 0;
276
+      height: 280px;
277
+    }
278
+
279
+    &__img {
280
+      width: 100%;
281
+      height: 100%;
282
+      object-fit: contain;
283
+    }
284
+  }
285
+}
286
+
287
+.vgs__gallery {
288
+  @include respond-to(xs) {
289
+    display: none;
290
+  }
291
+  overflow-x: hidden;
292
+  overflow-y: hidden;
293
+  position: absolute;
294
+  bottom: 10px;
295
+  margin: auto;
296
+  max-width: 100vh;
297
+  white-space: nowrap;
298
+  left: 0.5rem;
299
+  right: 0.5rem;
300
+  &__title {
301
+    color: $white;
302
+    margin-bottom: 0.5rem;
303
+  }
304
+  &__container {
305
+    overflow: visible;
306
+    display: block;
307
+    height: 250px;
308
+    white-space: nowrap;
309
+    transition: all 200ms ease-in-out;
310
+    width: 100%;
311
+    &__img {
312
+      width: 250px;
313
+      height: 250px;
314
+      object-fit: cover;
315
+      display: inline-block;
316
+      float: none;
317
+      margin-right: 20px;
318
+      cursor: pointer;
319
+      opacity: 0.6;
320
+      border-radius: $radius-medium;
321
+    }
322
+    &__img--active {
323
+      width: 100px;
324
+      display: inline-block;
325
+      float: none;
326
+      opacity: 1;
327
+    }
328
+  }
329
+}
330
+
331
+.modal-enter {
332
+  opacity: 0;
333
+}
334
+
335
+.modal-leave-active {
336
+  opacity: 0;
337
+}
338
+</style>

Loading…
Отказ
Запис