@fy-/fws-vue 2.3.15 → 2.3.17
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 +115 -27
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# This file has been completely rewritten and optimized
|
|
1
2
|
<script setup lang="ts">
|
|
2
3
|
import type { Component } from 'vue'
|
|
3
4
|
import type { APIPaging } from '../../composables/rest'
|
|
@@ -59,8 +60,8 @@ const availableHeight = computed(() => {
|
|
|
59
60
|
height -= infoPanelHeight.value
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
//
|
|
63
|
-
return `${height
|
|
63
|
+
// Use direct pixel values
|
|
64
|
+
return `${height}px`
|
|
64
65
|
})
|
|
65
66
|
|
|
66
67
|
// Use VueUse's useFullscreen for better fullscreen handling
|
|
@@ -146,40 +147,82 @@ const currentImage = computed(() => {
|
|
|
146
147
|
const imageCount = computed(() => props.images.length)
|
|
147
148
|
const currentIndex = computed(() => modelValue.value + 1)
|
|
148
149
|
|
|
149
|
-
//
|
|
150
|
+
// Helper function to adjust image size based on natural dimensions
|
|
151
|
+
function adjustImageSize(img: HTMLImageElement, availableHeightPx: number) {
|
|
152
|
+
if (img.naturalHeight > 0) {
|
|
153
|
+
const maxHeight = Math.min(img.naturalHeight, availableHeightPx)
|
|
154
|
+
img.style.maxHeight = `${maxHeight}px`
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Image size and positioning - improved to prevent overflow
|
|
150
159
|
const calculateImageSize = useDebounceFn(() => {
|
|
151
160
|
if (!imageContainerRef.value) return
|
|
152
161
|
|
|
153
|
-
nextTick
|
|
154
|
-
|
|
162
|
+
// Execute immediately without waiting for nextTick to prevent delayed resizing
|
|
163
|
+
const imageElements = imageContainerRef.value?.querySelectorAll('.image-display img, .image-display .video-component') as NodeListOf<HTMLElement>
|
|
164
|
+
|
|
165
|
+
if (!imageElements || imageElements.length === 0) return
|
|
155
166
|
|
|
156
|
-
|
|
167
|
+
// Get current panel heights for accurate calculations
|
|
168
|
+
const topControlsCurrentHeight = topControlsRef.value?.offsetHeight || 0
|
|
169
|
+
const infoPanelCurrentHeight = infoPanel.value && infoPanelRef.value ? infoPanelRef.value.offsetHeight : 0
|
|
157
170
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
171
|
+
// Apply sizing to all images
|
|
172
|
+
imageElements.forEach((img) => {
|
|
173
|
+
// Reset immediately to ensure proper recalculation
|
|
174
|
+
img.style.maxHeight = '0'
|
|
175
|
+
img.style.maxWidth = '0'
|
|
176
|
+
|
|
177
|
+
// Force browser to recalculate styles
|
|
178
|
+
void img.offsetHeight
|
|
179
|
+
|
|
180
|
+
// Apply exact pixel measurements based on actual UI elements
|
|
181
|
+
const topHeight = topControlsCurrentHeight || 0
|
|
182
|
+
const infoHeight = infoPanelCurrentHeight || 0
|
|
183
|
+
// Increase padding for reliable containment - prevent overflow
|
|
184
|
+
const availableHeightPx = windowHeight.value - topHeight - infoHeight - 48
|
|
185
|
+
|
|
186
|
+
if (windowWidth.value <= 768) {
|
|
187
|
+
// Mobile specific sizing - more conservative to guarantee fit
|
|
188
|
+
img.style.maxHeight = `${availableHeightPx}px`
|
|
189
|
+
img.style.maxWidth = '85vw' // Reduce from 90vw to ensure it fits
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
// Desktop sizing - account for sidebar if present
|
|
193
|
+
const sidebarWidth = sidePanel.value ? sidePanelRef.value?.offsetWidth || 256 : 0
|
|
194
|
+
// More conservative width calculation to prevent overflow
|
|
195
|
+
const availableWidthPx = windowWidth.value - sidebarWidth - 64
|
|
163
196
|
|
|
164
|
-
|
|
165
|
-
img.style.
|
|
197
|
+
img.style.maxHeight = `${availableHeightPx}px`
|
|
198
|
+
img.style.maxWidth = `${availableWidthPx}px`
|
|
199
|
+
}
|
|
166
200
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
img
|
|
201
|
+
// For images, add special handling for natural dimensions
|
|
202
|
+
if (img instanceof HTMLImageElement) {
|
|
203
|
+
if (img.complete) {
|
|
204
|
+
adjustImageSize(img, availableHeightPx)
|
|
171
205
|
}
|
|
172
206
|
else {
|
|
173
|
-
img.
|
|
207
|
+
img.onload = () => adjustImageSize(img, availableHeightPx)
|
|
174
208
|
}
|
|
175
|
-
}
|
|
209
|
+
}
|
|
176
210
|
})
|
|
177
|
-
},
|
|
211
|
+
}, 10) // Reduced debounce time for faster response
|
|
178
212
|
|
|
179
|
-
// Update all layout measurements
|
|
213
|
+
// Update all layout measurements when any relevant state changes
|
|
214
|
+
// More aggressive calculation to prevent layout issues
|
|
180
215
|
const updateLayout = useDebounceFn(() => {
|
|
216
|
+
// Calculate immediately
|
|
181
217
|
calculateImageSize()
|
|
182
|
-
|
|
218
|
+
|
|
219
|
+
// Then again after a small delay to catch any delayed DOM updates
|
|
220
|
+
setTimeout(() => {
|
|
221
|
+
calculateImageSize()
|
|
222
|
+
// And one more time for extra safety after all transitions
|
|
223
|
+
setTimeout(() => calculateImageSize(), 100)
|
|
224
|
+
}, 10)
|
|
225
|
+
}, 10) // Reduced debounce time for faster response
|
|
183
226
|
|
|
184
227
|
// Modal controls
|
|
185
228
|
function setModal(value: boolean) {
|
|
@@ -236,9 +279,14 @@ const openGalleryImage = useDebounceFn((index: number | undefined) => {
|
|
|
236
279
|
})
|
|
237
280
|
}, 50)
|
|
238
281
|
|
|
239
|
-
// Navigation functions
|
|
282
|
+
// Navigation functions - improved for immediate sizing
|
|
240
283
|
function goNextImage() {
|
|
241
284
|
direction.value = 'next'
|
|
285
|
+
|
|
286
|
+
// Calculate size immediately before changing the image
|
|
287
|
+
// This ensures we have proper sizing before transition starts
|
|
288
|
+
calculateImageSize()
|
|
289
|
+
|
|
242
290
|
if (modelValue.value < props.images.length - 1) {
|
|
243
291
|
modelValue.value++
|
|
244
292
|
}
|
|
@@ -246,10 +294,24 @@ function goNextImage() {
|
|
|
246
294
|
modelValue.value = 0
|
|
247
295
|
}
|
|
248
296
|
resetControlsTimer()
|
|
297
|
+
|
|
298
|
+
// Force immediate layout update when image changes
|
|
299
|
+
calculateImageSize()
|
|
300
|
+
|
|
301
|
+
// Another calculation after DOM is updated to ensure correct sizing
|
|
302
|
+
nextTick(() => {
|
|
303
|
+
calculateImageSize()
|
|
304
|
+
// Second calculation after a small delay to catch any late DOM changes
|
|
305
|
+
setTimeout(() => calculateImageSize(), 50)
|
|
306
|
+
})
|
|
249
307
|
}
|
|
250
308
|
|
|
251
309
|
function goPrevImage() {
|
|
252
310
|
direction.value = 'prev'
|
|
311
|
+
|
|
312
|
+
// Calculate size immediately before changing the image
|
|
313
|
+
calculateImageSize()
|
|
314
|
+
|
|
253
315
|
if (modelValue.value > 0) {
|
|
254
316
|
modelValue.value--
|
|
255
317
|
}
|
|
@@ -257,6 +319,16 @@ function goPrevImage() {
|
|
|
257
319
|
modelValue.value = props.images.length - 1 > 0 ? props.images.length - 1 : 0
|
|
258
320
|
}
|
|
259
321
|
resetControlsTimer()
|
|
322
|
+
|
|
323
|
+
// Force immediate layout update when image changes
|
|
324
|
+
calculateImageSize()
|
|
325
|
+
|
|
326
|
+
// Another calculation after DOM is updated to ensure correct sizing
|
|
327
|
+
nextTick(() => {
|
|
328
|
+
calculateImageSize()
|
|
329
|
+
// Second calculation after a small delay to catch any late DOM changes
|
|
330
|
+
setTimeout(() => calculateImageSize(), 50)
|
|
331
|
+
})
|
|
260
332
|
}
|
|
261
333
|
|
|
262
334
|
// UI control functions
|
|
@@ -447,6 +519,7 @@ watch(
|
|
|
447
519
|
galleryHeight,
|
|
448
520
|
topControlsHeight,
|
|
449
521
|
infoPanelHeight,
|
|
522
|
+
modelValue, // Watch for model value changes to update layout
|
|
450
523
|
],
|
|
451
524
|
() => {
|
|
452
525
|
updateLayout()
|
|
@@ -631,6 +704,7 @@ onUnmounted(() => {
|
|
|
631
704
|
<div
|
|
632
705
|
:key="`image-display-${modelValue}`"
|
|
633
706
|
class="image-display relative w-full h-full flex flex-col items-center justify-center"
|
|
707
|
+
@transitionend="calculateImageSize"
|
|
634
708
|
>
|
|
635
709
|
<!-- Actual Image/Video Content -->
|
|
636
710
|
<template v-if="videoComponent && isVideo(images[modelValue])">
|
|
@@ -650,6 +724,7 @@ onUnmounted(() => {
|
|
|
650
724
|
:style="{ maxHeight: availableHeight }"
|
|
651
725
|
:src="modelValueSrc"
|
|
652
726
|
:alt="`Gallery image ${modelValue + 1}`"
|
|
727
|
+
@load="() => { calculateImageSize(); nextTick(() => calculateImageSize()) }"
|
|
653
728
|
>
|
|
654
729
|
<component
|
|
655
730
|
:is="imageComponent"
|
|
@@ -725,7 +800,7 @@ onUnmounted(() => {
|
|
|
725
800
|
v-if="sidePanel"
|
|
726
801
|
ref="sidePanelRef"
|
|
727
802
|
class="side-panel hidden lg:block absolute right-0 top-0 bottom-0 w-64 bg-fv-neutral-800/90 backdrop-blur-md overflow-y-auto z-40 cool-scroll"
|
|
728
|
-
:style="{ 'padding-top': `${topControlsHeight}px` }"
|
|
803
|
+
:style="{ 'padding-top': `${topControlsHeight + 8}px` }"
|
|
729
804
|
>
|
|
730
805
|
<!-- Paging Controls if needed -->
|
|
731
806
|
<div v-if="paging" class="flex items-center justify-center pt-2">
|
|
@@ -791,10 +866,13 @@ onUnmounted(() => {
|
|
|
791
866
|
>
|
|
792
867
|
<div
|
|
793
868
|
v-if="showControls && images.length > 1 && !sidePanel"
|
|
794
|
-
class="absolute bottom-0 left-0 right-0 p-2 lg:hidden bg-gradient-to-t from-fv-neutral-900/90 to-transparent backdrop-blur-sm z-45"
|
|
795
|
-
:class="{ 'pb-
|
|
869
|
+
class="absolute bottom-0 left-0 right-0 p-1 pb-2 lg:hidden bg-gradient-to-t from-fv-neutral-900/90 to-transparent backdrop-blur-sm z-45"
|
|
870
|
+
:class="{ 'pb-4': infoPanel }"
|
|
871
|
+
@touchstart.stop
|
|
872
|
+
@touchmove.stop
|
|
873
|
+
@touchend.stop
|
|
796
874
|
>
|
|
797
|
-
<div class="overflow-x-auto flex space-x-2
|
|
875
|
+
<div class="overflow-x-auto flex space-x-2 px-1 no-scrollbar">
|
|
798
876
|
<div
|
|
799
877
|
v-for="(image, idx) in images"
|
|
800
878
|
:key="`mobile_thumb_${id}_${idx}`"
|
|
@@ -1124,4 +1202,14 @@ onUnmounted(() => {
|
|
|
1124
1202
|
font-size: 0.75rem;
|
|
1125
1203
|
z-index: 10;
|
|
1126
1204
|
}
|
|
1205
|
+
|
|
1206
|
+
/* Special class to hide scrollbars on mobile */
|
|
1207
|
+
.no-scrollbar {
|
|
1208
|
+
-ms-overflow-style: none; /* IE and Edge */
|
|
1209
|
+
scrollbar-width: none; /* Firefox */
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.no-scrollbar::-webkit-scrollbar {
|
|
1213
|
+
display: none; /* Chrome, Safari and Opera */
|
|
1214
|
+
}
|
|
1127
1215
|
</style>
|