@fy-/fws-vue 2.0.87 → 2.0.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/ui/DefaultGallery.vue +99 -86
- package/package.json +1 -1
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { Dialog, DialogPanel, TransitionRoot } from "@headlessui/vue";
|
|
3
|
-
import {
|
|
4
|
-
ref,
|
|
5
|
-
onMounted,
|
|
6
|
-
reactive,
|
|
7
|
-
onUnmounted,
|
|
8
|
-
h,
|
|
9
|
-
computed,
|
|
10
|
-
defineComponent,
|
|
11
|
-
} from "vue";
|
|
3
|
+
import { ref, onMounted, reactive, onUnmounted, h, computed } from "vue";
|
|
12
4
|
import type { APIPaging } from "../../composables/rest";
|
|
13
5
|
import { useEventBus } from "../../composables/event-bus";
|
|
14
6
|
import {
|
|
@@ -20,10 +12,11 @@ import {
|
|
|
20
12
|
} from "@heroicons/vue/24/solid";
|
|
21
13
|
import DefaultPaging from "./DefaultPaging.vue";
|
|
22
14
|
import type { Component } from "vue";
|
|
15
|
+
import CollapseTransition from "./transitions/CollapseTransition.vue";
|
|
16
|
+
|
|
23
17
|
const isGalleryOpen = ref<boolean>(false);
|
|
24
18
|
const eventBus = useEventBus();
|
|
25
19
|
const sidePanel = ref<boolean>(true);
|
|
26
|
-
const direction = ref("next"); // Possible values: 'next' or 'prev'
|
|
27
20
|
|
|
28
21
|
const props = withDefaults(
|
|
29
22
|
defineProps<{
|
|
@@ -63,6 +56,7 @@ const props = withDefaults(
|
|
|
63
56
|
ranking: false,
|
|
64
57
|
},
|
|
65
58
|
);
|
|
59
|
+
|
|
66
60
|
const emit = defineEmits(["update:modelValue"]);
|
|
67
61
|
const modelValue = computed({
|
|
68
62
|
get: () => props.modelValue,
|
|
@@ -70,6 +64,9 @@ const modelValue = computed({
|
|
|
70
64
|
emit("update:modelValue", i);
|
|
71
65
|
},
|
|
72
66
|
});
|
|
67
|
+
|
|
68
|
+
const direction = ref<"left" | "right">("right");
|
|
69
|
+
|
|
73
70
|
const setModal = (value: boolean) => {
|
|
74
71
|
if (value === true) {
|
|
75
72
|
if (props.onOpen) props.onOpen();
|
|
@@ -78,17 +75,17 @@ const setModal = (value: boolean) => {
|
|
|
78
75
|
}
|
|
79
76
|
isGalleryOpen.value = value;
|
|
80
77
|
};
|
|
78
|
+
|
|
81
79
|
const openGalleryImage = (index: number | undefined) => {
|
|
82
|
-
if (index === undefined)
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
direction.value = index > modelValue.value ? "next" : "prev";
|
|
80
|
+
if (index === undefined) modelValue.value = 0;
|
|
81
|
+
else {
|
|
86
82
|
modelValue.value = parseInt(index.toString());
|
|
87
83
|
}
|
|
88
84
|
setModal(true);
|
|
89
85
|
};
|
|
86
|
+
|
|
90
87
|
const goNextImage = () => {
|
|
91
|
-
direction.value = "
|
|
88
|
+
direction.value = "right";
|
|
92
89
|
if (modelValue.value < props.images.length - 1) {
|
|
93
90
|
modelValue.value++;
|
|
94
91
|
} else {
|
|
@@ -97,7 +94,7 @@ const goNextImage = () => {
|
|
|
97
94
|
};
|
|
98
95
|
|
|
99
96
|
const goPrevImage = () => {
|
|
100
|
-
direction.value = "
|
|
97
|
+
direction.value = "left";
|
|
101
98
|
if (modelValue.value > 0) {
|
|
102
99
|
modelValue.value--;
|
|
103
100
|
} else {
|
|
@@ -111,6 +108,7 @@ const modelValueSrc = computed(() => {
|
|
|
111
108
|
if (props.images[modelValue.value] == undefined) return false;
|
|
112
109
|
return props.getImageUrl(props.images[modelValue.value]);
|
|
113
110
|
});
|
|
111
|
+
|
|
114
112
|
const start = reactive({ x: 0, y: 0 });
|
|
115
113
|
|
|
116
114
|
const touchStart = (event: TouchEvent) => {
|
|
@@ -130,10 +128,10 @@ const touchEnd = (event: TouchEvent) => {
|
|
|
130
128
|
// Add a threshold to prevent accidental swipes
|
|
131
129
|
if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) {
|
|
132
130
|
if (diffX > 0) {
|
|
133
|
-
direction.value = "
|
|
131
|
+
direction.value = "right";
|
|
134
132
|
goNextImage();
|
|
135
133
|
} else {
|
|
136
|
-
direction.value = "
|
|
134
|
+
direction.value = "left";
|
|
137
135
|
goPrevImage();
|
|
138
136
|
}
|
|
139
137
|
}
|
|
@@ -145,18 +143,20 @@ const getBorderColor = (i: any) => {
|
|
|
145
143
|
}
|
|
146
144
|
return "";
|
|
147
145
|
};
|
|
146
|
+
|
|
148
147
|
const isKeyPressed = ref<boolean>(false);
|
|
148
|
+
|
|
149
149
|
const handleKeyboardInput = (event: KeyboardEvent) => {
|
|
150
150
|
if (isKeyPressed.value) return;
|
|
151
151
|
switch (event.key) {
|
|
152
152
|
case "ArrowRight":
|
|
153
153
|
isKeyPressed.value = true;
|
|
154
|
-
direction.value = "
|
|
154
|
+
direction.value = "right";
|
|
155
155
|
goNextImage();
|
|
156
156
|
break;
|
|
157
157
|
case "ArrowLeft":
|
|
158
158
|
isKeyPressed.value = true;
|
|
159
|
-
direction.value = "
|
|
159
|
+
direction.value = "left";
|
|
160
160
|
goPrevImage();
|
|
161
161
|
break;
|
|
162
162
|
default:
|
|
@@ -169,9 +169,11 @@ const handleKeyboardRelease = (event: KeyboardEvent) => {
|
|
|
169
169
|
isKeyPressed.value = false;
|
|
170
170
|
}
|
|
171
171
|
};
|
|
172
|
+
|
|
172
173
|
const closeGallery = () => {
|
|
173
174
|
setModal(false);
|
|
174
175
|
};
|
|
176
|
+
|
|
175
177
|
onMounted(() => {
|
|
176
178
|
eventBus.on(`${props.id}GalleryImage`, openGalleryImage);
|
|
177
179
|
eventBus.on(`${props.id}Gallery`, openGalleryImage);
|
|
@@ -181,6 +183,7 @@ onMounted(() => {
|
|
|
181
183
|
window.addEventListener("keyup", handleKeyboardRelease);
|
|
182
184
|
}
|
|
183
185
|
});
|
|
186
|
+
|
|
184
187
|
onUnmounted(() => {
|
|
185
188
|
eventBus.off(`${props.id}Gallery`, openGalleryImage);
|
|
186
189
|
eventBus.off(`${props.id}GalleryImage`, openGalleryImage);
|
|
@@ -190,52 +193,8 @@ onUnmounted(() => {
|
|
|
190
193
|
window.removeEventListener("keyup", handleKeyboardRelease);
|
|
191
194
|
}
|
|
192
195
|
});
|
|
193
|
-
const transitionName = computed(() => {
|
|
194
|
-
return direction.value === "next" ? "slide-next" : "slide-prev";
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
const ImageComponent = defineComponent({
|
|
198
|
-
props: ["src", "imageComponent", "modelValueSrc"],
|
|
199
|
-
template: `
|
|
200
|
-
<div v-if="src" class="media-wrapper">
|
|
201
|
-
<img
|
|
202
|
-
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
203
|
-
:src="modelValueSrc"
|
|
204
|
-
v-if="modelValueSrc && imageComponent == 'img'"
|
|
205
|
-
/>
|
|
206
|
-
<component
|
|
207
|
-
v-else-if="modelValueSrc && imageComponent"
|
|
208
|
-
:is="imageComponent"
|
|
209
|
-
:image="modelValueSrc.image"
|
|
210
|
-
:variant="modelValueSrc.variant"
|
|
211
|
-
:alt="modelValueSrc.alt"
|
|
212
|
-
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
213
|
-
/>
|
|
214
|
-
</div>
|
|
215
|
-
`,
|
|
216
|
-
});
|
|
217
|
-
const VideoComponentWrapper = defineComponent({
|
|
218
|
-
props: ["src", "videoComponent"],
|
|
219
|
-
template: `
|
|
220
|
-
<div v-if="src" class="media-wrapper">
|
|
221
|
-
<ClientOnly>
|
|
222
|
-
<component
|
|
223
|
-
:is="videoComponent"
|
|
224
|
-
:src="isVideo(images[modelValue])"
|
|
225
|
-
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
226
|
-
/>
|
|
227
|
-
</ClientOnly>
|
|
228
|
-
</div>
|
|
229
|
-
`,
|
|
230
|
-
});
|
|
231
|
-
const currentMediaComponent = computed(() => {
|
|
232
|
-
if (props.videoComponent && props.isVideo(props.images[modelValue.value])) {
|
|
233
|
-
return VideoComponentWrapper;
|
|
234
|
-
} else {
|
|
235
|
-
return ImageComponent;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
196
|
</script>
|
|
197
|
+
|
|
239
198
|
<template>
|
|
240
199
|
<div>
|
|
241
200
|
<TransitionRoot
|
|
@@ -283,26 +242,53 @@ const currentMediaComponent = computed(() => {
|
|
|
283
242
|
</button>
|
|
284
243
|
</div>
|
|
285
244
|
<div
|
|
286
|
-
class="flex-1 flex flex-col items-center justify-center max-w-full lg:max-w-[calc(100vw - 256px)]"
|
|
245
|
+
class="flex-1 flex flex-col items-center justify-center max-w-full lg:max-w-[calc(100vw - 256px)] overflow-hidden"
|
|
287
246
|
@touchstart="touchStart"
|
|
288
247
|
@touchend="touchEnd"
|
|
289
248
|
>
|
|
290
|
-
<transition :name="
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
:key="modelValue"
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
249
|
+
<transition :name="`slide-${direction}`">
|
|
250
|
+
<div
|
|
251
|
+
v-if="true"
|
|
252
|
+
:key="`image-display-${modelValue}`"
|
|
253
|
+
class="flex-1 w-full max-w-full flex flex-col items-center justify-center"
|
|
254
|
+
>
|
|
255
|
+
<div
|
|
256
|
+
class="flex-1 w-full max-w-full flex items-center justify-center"
|
|
257
|
+
>
|
|
258
|
+
<template
|
|
259
|
+
v-if="videoComponent && isVideo(images[modelValue])"
|
|
260
|
+
>
|
|
261
|
+
<ClientOnly>
|
|
262
|
+
<component
|
|
263
|
+
:is="videoComponent"
|
|
264
|
+
:src="isVideo(images[modelValue])"
|
|
265
|
+
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
266
|
+
/>
|
|
267
|
+
</ClientOnly>
|
|
268
|
+
</template>
|
|
269
|
+
<template v-else>
|
|
270
|
+
<img
|
|
271
|
+
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
272
|
+
:src="modelValueSrc"
|
|
273
|
+
v-if="modelValueSrc && imageComponent == 'img'"
|
|
274
|
+
/>
|
|
275
|
+
<component
|
|
276
|
+
v-else-if="modelValueSrc && imageComponent"
|
|
277
|
+
:is="imageComponent"
|
|
278
|
+
:image="modelValueSrc.image"
|
|
279
|
+
:variant="modelValueSrc.variant"
|
|
280
|
+
:alt="modelValueSrc.alt"
|
|
281
|
+
class="shadow max-w-full h-auto object-contain max-h-[85vh]"
|
|
282
|
+
/>
|
|
283
|
+
</template>
|
|
284
|
+
</div>
|
|
285
|
+
<div
|
|
286
|
+
class="flex-0 py-2 flex items-center justify-center max-w-full w-full"
|
|
287
|
+
>
|
|
288
|
+
<slot :value="images[modelValue]"></slot>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
299
291
|
</transition>
|
|
300
|
-
|
|
301
|
-
<div
|
|
302
|
-
class="flex-0 py-2 flex items-center justify-center max-w-full w-full"
|
|
303
|
-
>
|
|
304
|
-
<slot :value="images[modelValue]"></slot>
|
|
305
|
-
</div>
|
|
306
292
|
</div>
|
|
307
293
|
<div
|
|
308
294
|
class="hidden lg:flex w-10 flex-shrink-0 items-center justify-center"
|
|
@@ -341,6 +327,7 @@ const currentMediaComponent = computed(() => {
|
|
|
341
327
|
leave-to="translate-x-full"
|
|
342
328
|
class="hidden lg:block flex-shrink-0 w-64 bg-fv-neutral-800 h-[100vh] max-h-[100vh] overflow-y-auto"
|
|
343
329
|
>
|
|
330
|
+
<!-- Side panel content -->
|
|
344
331
|
<div v-if="paging" class="flex items-center justify-center">
|
|
345
332
|
<DefaultPaging :items="paging" :id="id" />
|
|
346
333
|
</div>
|
|
@@ -349,11 +336,10 @@ const currentMediaComponent = computed(() => {
|
|
|
349
336
|
v-for="i in images.length"
|
|
350
337
|
:key="`bg_${id}_${i}`"
|
|
351
338
|
class="hover:!brightness-100"
|
|
352
|
-
:style="
|
|
353
|
-
|
|
354
|
-
? '
|
|
355
|
-
|
|
356
|
-
}`"
|
|
339
|
+
:style="{
|
|
340
|
+
filter:
|
|
341
|
+
i - 1 == modelValue ? 'brightness(1)' : 'brightness(0.5)',
|
|
342
|
+
}"
|
|
357
343
|
>
|
|
358
344
|
<img
|
|
359
345
|
@click="$eventBus.emit(`${id}GalleryImage`, i - 1)"
|
|
@@ -520,3 +506,30 @@ const currentMediaComponent = computed(() => {
|
|
|
520
506
|
transform: translateX(100%);
|
|
521
507
|
}
|
|
522
508
|
</style>
|
|
509
|
+
<style scoped>
|
|
510
|
+
.slide-left-enter-active,
|
|
511
|
+
.slide-left-leave-active {
|
|
512
|
+
transition: transform 0.3s ease;
|
|
513
|
+
}
|
|
514
|
+
.slide-left-enter-from,
|
|
515
|
+
.slide-left-leave-to {
|
|
516
|
+
transform: translateX(100%);
|
|
517
|
+
}
|
|
518
|
+
.slide-left-enter-to,
|
|
519
|
+
.slide-left-leave-from {
|
|
520
|
+
transform: translateX(0);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.slide-right-enter-active,
|
|
524
|
+
.slide-right-leave-active {
|
|
525
|
+
transition: transform 0.3s ease;
|
|
526
|
+
}
|
|
527
|
+
.slide-right-enter-from,
|
|
528
|
+
.slide-right-leave-to {
|
|
529
|
+
transform: translateX(-100%);
|
|
530
|
+
}
|
|
531
|
+
.slide-right-enter-to,
|
|
532
|
+
.slide-right-leave-from {
|
|
533
|
+
transform: translateX(0);
|
|
534
|
+
}
|
|
535
|
+
</style>
|