Skip to content

Commit 2ea79c0

Browse files
committed
Polished thumbnails
1 parent 2a74e83 commit 2ea79c0

File tree

9 files changed

+122
-69
lines changed

9 files changed

+122
-69
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ const vueImgConfig = {
5454
sourceButton: false,
5555
// Event listener to open gallery will be applied to <img> element
5656
openOn: 'click',
57+
// Show thumbnails for all groups with more than 1 image
58+
thumbnails: false,
5759
}
5860
Vue.use(VueImg, vueImgConfig);
5961
```
@@ -85,6 +87,7 @@ Options that could be specified in directive value
8587
| title | Caption that will be displayed | empty string or value of the `alt` attribute, if `altAsTitle` is true | string |
8688
| openOn | Event listener to open gallery will be applied to `<img>`. Available options are 'dblclick', 'mouseover' and all native JS events. | 'click' if another not stated when initializing plugin | string |
8789
| sourceButton | Display 'download' button near 'close' that opens source image in new tab | `false` if `sourceButton` is not set to true when initializing plugin | boolean |
90+
| thumbnails | When opening group by clicking (or other `openOn` event) on this image, thumbnails of images for this group will be visible | `false` if `thumbnails` is not set to true when initializing plugin | boolean |
8891
| opened | Function that will be executed on gallery open | undefined | function |
8992
| closed | Function that will be executed on gallery close | undefined | function |
9093
| changed(imageIndex) | Function that will be executed when switching between images in gallery | undefined | function |

dist/v-img.js

+21-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/v-img.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/v-img.mjs

+21-14
Large diffs are not rendered by default.

dist/v-img.mjs.map

+1-1
Original file line numberDiff line numberDiff line change

lib/ImgScreen.vue

+42-25
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
</span>
3535
</div>
3636
</div>
37+
3738
<!-- Controls start -->
3839
<transition appear name="v-img-fade">
3940
<span v-if="visibleUI && images.length !== 1" class="prev-v-img" @click="prev">
@@ -50,12 +51,20 @@
5051
</span>
5152
</transition>
5253
<!-- Constols end -->
53-
<div class="footer-v-img" v-if="previews.length > 1">
54-
<div v-for="(preview, index) in previews" :key="index" :style="{backgroundImage: 'url('+preview+')'}" @click="select(index)" :class="{'is-selected': currentImageIndex == index}"></div>
54+
55+
<div class="footer-v-img" v-if="thumbnails && images.length > 1">
56+
<img
57+
v-for="(thumbnail, index) in images"
58+
:key="index"
59+
:src="thumbnail"
60+
@click="select(index)"
61+
:class="{'is-selected': currentImageIndex == index}">
5562
</div>
63+
5664
<div class="content-v-img">
5765
<img :src="images[currentImageIndex]" @click="next">
5866
</div>
67+
5968
</div>
6069
</transition>
6170
</template>
@@ -65,14 +74,14 @@ export default {
6574
data() {
6675
return {
6776
images: [],
68-
previews: [],
6977
titles: [],
7078
sourceButtons: [],
7179
visibleUI: true,
7280
currentImageIndex: 0,
7381
closed: true,
7482
uiTimeout: null,
75-
handlers: {}
83+
handlers: {},
84+
thumbnails: false,
7685
};
7786
},
7887
watch: {
@@ -83,7 +92,7 @@ export default {
8392
if (!newVal && this.handlers.opened) {
8493
this.handlers.opened();
8594
}
86-
}
95+
},
8796
},
8897
methods: {
8998
// Not using currentImageIndex watcher because it will
@@ -97,7 +106,7 @@ export default {
97106
},
98107
close() {
99108
if (!this.closed) {
100-
document.querySelector("body").classList.remove("body-fs-v-img");
109+
document.querySelector('body').classList.remove('body-fs-v-img');
101110
this.images = [];
102111
this.currentImageIndex = 0;
103112
this.closed = true;
@@ -138,24 +147,24 @@ export default {
138147
this.visibleUI = false;
139148
}, 3500);
140149
}
141-
}
150+
},
142151
},
143152
created() {
144-
window.addEventListener("keyup", e => {
153+
window.addEventListener('keyup', e => {
145154
// esc key and 'q' for quit
146155
if (e.keyCode === 27 || e.keyCode === 81) this.close();
147156
// arrow right and 'l' key (vim-like binding)
148157
if (e.keyCode === 39 || e.keyCode === 76) this.next();
149158
// arrow left and 'h' key (vim-like binding)
150159
if (e.keyCode === 37 || e.keyCode === 72) this.prev();
151160
});
152-
window.addEventListener("scroll", () => {
161+
window.addEventListener('scroll', () => {
153162
this.close();
154163
});
155-
window.addEventListener("mousemove", () => {
164+
window.addEventListener('mousemove', () => {
156165
this.showUI();
157166
});
158-
}
167+
},
159168
};
160169
</script>
161170

@@ -195,7 +204,8 @@ export default {
195204
user-select: none;
196205
}
197206
198-
.header-v-img, .footer-v-img {
207+
.header-v-img,
208+
.footer-v-img {
199209
position: absolute;
200210
width: 100%;
201211
background-color: rgba(0, 0, 0, 0.3);
@@ -212,27 +222,34 @@ export default {
212222
.footer-v-img {
213223
bottom: 0;
214224
justify-content: center;
225+
height: 70px;
226+
/* scrolling thumbnails on mobile */
227+
overflow-x: auto;
215228
}
216229
217-
.footer-v-img div {
218-
width: 42px;
219-
height: 30px;
220-
background-position: center;
221-
background-size: cover;
230+
.footer-v-img img {
231+
width: 60px;
232+
height: 60px;
222233
cursor: pointer;
234+
-webkit-transition: transform 0.2s ease-out;
235+
transition: transform 0.2s ease-out;
236+
object-fit: cover;
237+
-webkit-user-select: none;
238+
-moz-user-select: none;
239+
-ms-user-select: none;
240+
user-select: none;
223241
}
224242
225-
.footer-v-img div.is-selected {
226-
width: 56px;
227-
height: 40px;
243+
.footer-v-img img.is-selected {
244+
transform: scale(1.1);
228245
}
229246
230-
.footer-v-img div:not(:last-child) {
231-
margin-right: 5px;
247+
.footer-v-img img:not(:last-child) {
248+
margin-right: 7px;
232249
}
233250
234251
.title-v-img {
235-
font-family: "Avenir", Helvetica, Arial, sans-serif;
252+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
236253
font-size: 18px;
237254
font-weight: 400;
238255
color: white;
@@ -244,7 +261,7 @@ export default {
244261
.count-v-img,
245262
.buttons-v-img {
246263
width: 80px;
247-
font-family: "Avenir", Helvetica, Arial, sans-serif;
264+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
248265
}
249266
250267
.count-v-img {
@@ -305,7 +322,7 @@ export default {
305322
top: 50%;
306323
margin-top: -12.5px;
307324
font-size: 15px;
308-
font-family: "Avenir", Helvetica, Arial, sans-serif;
325+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
309326
text-align: center;
310327
background-color: rgba(0, 0, 0, 0.3);
311328
z-index: 1000;

lib/index.js

+23-9
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ const addPluginAttributes = (el, binding, options) => {
1111
let sourceButton;
1212
let src = el.src; // eslint-disable-line prefer-destructuring
1313
let title;
14+
let thumbnails;
1415
const events = {};
1516

1617
if (options.altAsTitle) title = el.alt;
17-
openOn = options.openOn; // eslint-disable-line prefer-destructuring
18-
sourceButton = options.sourceButton; // eslint-disable-line prefer-destructuring
18+
19+
/* eslint-disable prefer-destructuring */
20+
// Assigning values from plugin initialization options here
21+
openOn = options.openOn;
22+
sourceButton = options.sourceButton;
23+
thumbnails = options.thumbnails;
24+
/* eslint-enable prefer-destructuring */
1925

2026
// Overriding options if they're provided in binding.value
2127
if (typeof binding.value !== 'undefined') {
@@ -24,14 +30,19 @@ const addPluginAttributes = (el, binding, options) => {
2430
openOn = binding.value.openOn || openOn;
2531
src = binding.value.src || src;
2632
title = binding.value.title || title;
27-
// binding.value.sourceButton could be set to false, that's why we're
28-
// comparing it to undefined but not using approach as in src, group, title, etc.
29-
if (binding.value.sourceButton !== undefined) {
30-
sourceButton = binding.value.sourceButton; // eslint-disable-line prefer-destructuring
31-
}
33+
// Lifecycle functions
3234
events.opened = binding.value.opened;
3335
events.closed = binding.value.closed;
3436
events.changed = binding.value.changed;
37+
// binding.value.sourceButton could be set to false, (part before || will always be ignored)
38+
// that's why we're comparing it to undefined but not using approach as in src, group, title, etc.
39+
if (binding.value.sourceButton !== undefined) {
40+
sourceButton = binding.value.sourceButton; // eslint-disable-line prefer-destructuring
41+
}
42+
// same as above
43+
if (binding.value.thumbnails !== undefined) {
44+
thumbnails = binding.value.thumbnails; // eslint-disable-line prefer-destructuring
45+
}
3546
}
3647

3748
// Setting up data attributes for dynamic properties
@@ -40,6 +51,7 @@ const addPluginAttributes = (el, binding, options) => {
4051
if (group) el.setAttribute('data-vue-img-group', group);
4152
if (title) el.setAttribute('data-vue-img-title', title);
4253
if (sourceButton) el.setAttribute('data-vue-img-source-button', sourceButton);
54+
if (thumbnails) el.setAttribute('data-vue-img-thumbnails', thumbnails);
4355

4456
if (!src) console.error('v-img element missing src parameter.');
4557

@@ -54,6 +66,7 @@ const addPluginAttributes = (el, binding, options) => {
5466
events,
5567
sourceButton,
5668
openOn,
69+
thumbnails,
5770
};
5871
};
5972

@@ -63,6 +76,7 @@ const install = (Vue, options) => {
6376
const defaultOptions = {
6477
altAsTitle: false,
6578
sourceButton: false,
79+
thumbnails: false,
6680
openOn: 'click',
6781
};
6882

@@ -76,12 +90,12 @@ const install = (Vue, options) => {
7690

7791
if (oldVnode.data.attrs && vnode.data.attrs) {
7892
srcUpdated = oldVnode.data.attrs.src !== vnode.data.attrs.src;
93+
// handle alt tag change only if option altAsTitle is enabled
7994
if (options.altAsTitle) {
8095
altUpdated = oldVnode.data.attrs.alt !== vnode.data.attrs.alt;
8196
}
8297
}
8398

84-
// handle alt tag change only if option is enabled
8599
const bindingValueUpdated = binding.oldValue !== binding.value;
86100

87101
if (srcUpdated || altUpdated || bindingValueUpdated) {
@@ -115,9 +129,9 @@ const install = (Vue, options) => {
115129
];
116130
}
117131
Vue.set(vm, 'images', images.map(e => e.dataset.vueImgSrc));
118-
Vue.set(vm, 'previews', images.map(e => e.src));
119132
Vue.set(vm, 'titles', images.map(e => e.dataset.vueImgTitle));
120133
Vue.set(vm, 'sourceButtons', images.map(e => e.dataset.vueImgSourceButton === 'true'));
134+
Vue.set(vm, 'thumbnails', el.dataset.vueImgThumbnails === 'true');
121135
Vue.set(vm, 'currentImageIndex', images.indexOf(el));
122136
Vue.set(vm, 'handlers', addedAttributes.events);
123137
Vue.set(vm, 'closed', false);

manual-test/src/App.vue

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
<div>
33
<input type="number" v-model="n" id="">
44
<img v-img src="https://unsplash.it/200/300?image=2">
5-
<img v-img src="https://unsplash.it/200/300?image=324">
6-
<img v-img="{ src: `https://unsplash.it/200/300?image=${n}` }" :diabled="n === 2">
7-
<img v-img:v src="https://unsplash.it/200/300?image=3">
5+
<img v-img:v="{ thumbnails: false }" src="https://unsplash.it/200/300?image=324">
6+
<img v-img:v="{ src: `https://unsplash.it/500/300?image=${n}`}" :src="`https://unsplash.it/200/300?image=${n}`">
87
</div>
98
</template>
109

@@ -13,15 +12,20 @@ export default {
1312
data() {
1413
return {
1514
n: 1,
16-
}
15+
};
16+
},
17+
created() {
18+
setInterval(() => {
19+
this.n += 1;
20+
}, 20000);
1721
},
1822
};
1923
</script>
2024

2125
<style>
2226
img {
2327
width: 200px;
24-
height: 100px;
28+
/* height: 100px; */
2529
}
2630
</style>
2731

manual-test/src/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import VueImg from '../../dist/v-img';
55
Vue.use(VueImg, {
66
altAsTitle: true,
77
sourceButton: false,
8+
thumbnails: true,
89
});
910

1011
/* eslint-disable no-new */

0 commit comments

Comments
 (0)