@fy-/fws-vue 2.3.17 → 2.3.18

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.
@@ -46,23 +46,7 @@ const { width: windowWidth, height: windowHeight } = useWindowSize()
46
46
  const { height: topControlsHeight } = useElementSize(topControlsRef)
47
47
  const { height: infoPanelHeight } = useElementSize(infoPanelRef)
48
48
 
49
- // Derived measurements
50
- const availableHeight = computed(() => {
51
- let height = isFullscreen.value
52
- ? windowHeight.value * 0.95 // 95% of viewport in fullscreen
53
- : windowHeight.value * 0.85 // 85% of viewport in normal mode
54
-
55
- // Subtract top controls
56
- height -= topControlsHeight.value
57
-
58
- // Subtract info panel if visible
59
- if (infoPanel.value && infoPanelHeight.value > 0) {
60
- height -= infoPanelHeight.value
61
- }
62
-
63
- // Use direct pixel values
64
- return `${height}px`
65
- })
49
+ // We no longer need derived measurements as we use CSS variables instead
66
50
 
67
51
  // Use VueUse's useFullscreen for better fullscreen handling
68
52
  const { isFullscreen: isElementFullscreen, enter: enterFullscreen, exit: exitFullscreen } = useFullscreen(galleryRef)
@@ -147,82 +131,33 @@ const currentImage = computed(() => {
147
131
  const imageCount = computed(() => props.images.length)
148
132
  const currentIndex = computed(() => modelValue.value + 1)
149
133
 
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
159
- const calculateImageSize = useDebounceFn(() => {
160
- if (!imageContainerRef.value) return
161
-
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
134
+ // Update CSS variables for layout consistency
135
+ function updateInfoHeight() {
136
+ if (!infoPanelRef.value) return
166
137
 
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
170
-
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'
138
+ const height = infoPanelRef.value.offsetHeight || 0
139
+ document.documentElement.style.setProperty('--info-height', `${height}px`)
140
+ }
176
141
 
177
- // Force browser to recalculate styles
178
- void img.offsetHeight
142
+ function updateControlsHeight() {
143
+ if (!topControlsRef.value) return
179
144
 
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
145
+ const height = topControlsRef.value.offsetHeight || 0
146
+ document.documentElement.style.setProperty('--controls-height', `${height}px`)
147
+ }
185
148
 
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
149
+ // CSS variable-based image sizing
150
+ const updateImageSizes = useDebounceFn(() => {
151
+ // Update CSS variables first
152
+ updateInfoHeight()
153
+ updateControlsHeight()
196
154
 
197
- img.style.maxHeight = `${availableHeightPx}px`
198
- img.style.maxWidth = `${availableWidthPx}px`
199
- }
155
+ // Set sidebar width variable
156
+ const sidebarWidthValue = sidePanel.value ? '16rem' : '0px'
157
+ document.documentElement.style.setProperty('--sidebar-width', sidebarWidthValue)
200
158
 
201
- // For images, add special handling for natural dimensions
202
- if (img instanceof HTMLImageElement) {
203
- if (img.complete) {
204
- adjustImageSize(img, availableHeightPx)
205
- }
206
- else {
207
- img.onload = () => adjustImageSize(img, availableHeightPx)
208
- }
209
- }
210
- })
211
- }, 10) // Reduced debounce time for faster response
212
-
213
- // Update all layout measurements when any relevant state changes
214
- // More aggressive calculation to prevent layout issues
215
- const updateLayout = useDebounceFn(() => {
216
- // Calculate immediately
217
- calculateImageSize()
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
159
+ // No need to manipulate image elements directly - CSS variables will handle it
160
+ }, 10)
226
161
 
227
162
  // Modal controls
228
163
  function setModal(value: boolean) {
@@ -275,18 +210,14 @@ const openGalleryImage = useDebounceFn((index: number | undefined) => {
275
210
 
276
211
  // Update layout after opening
277
212
  nextTick(() => {
278
- updateLayout()
213
+ updateImageSizes()
279
214
  })
280
215
  }, 50)
281
216
 
282
- // Navigation functions - improved for immediate sizing
217
+ // Navigation functions
283
218
  function goNextImage() {
284
219
  direction.value = 'next'
285
220
 
286
- // Calculate size immediately before changing the image
287
- // This ensures we have proper sizing before transition starts
288
- calculateImageSize()
289
-
290
221
  if (modelValue.value < props.images.length - 1) {
291
222
  modelValue.value++
292
223
  }
@@ -294,24 +225,11 @@ function goNextImage() {
294
225
  modelValue.value = 0
295
226
  }
296
227
  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
- })
307
228
  }
308
229
 
309
230
  function goPrevImage() {
310
231
  direction.value = 'prev'
311
232
 
312
- // Calculate size immediately before changing the image
313
- calculateImageSize()
314
-
315
233
  if (modelValue.value > 0) {
316
234
  modelValue.value--
317
235
  }
@@ -319,16 +237,6 @@ function goPrevImage() {
319
237
  modelValue.value = props.images.length - 1 > 0 ? props.images.length - 1 : 0
320
238
  }
321
239
  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
- })
332
240
  }
333
241
 
334
242
  // UI control functions
@@ -360,7 +268,7 @@ function toggleInfoPanel() {
360
268
 
361
269
  // Update layout after panel toggle
362
270
  nextTick(() => {
363
- updateLayout()
271
+ updateImageSizes()
364
272
  })
365
273
  }
366
274
 
@@ -370,7 +278,7 @@ function toggleSidePanel() {
370
278
 
371
279
  // Update layout after panel toggle
372
280
  nextTick(() => {
373
- updateLayout()
281
+ updateImageSizes()
374
282
  })
375
283
  }
376
284
 
@@ -383,7 +291,7 @@ function toggleFullscreen() {
383
291
  // Give browser time to adjust fullscreen before updating sizing
384
292
  if (fullscreenResizeTimeout) clearTimeout(fullscreenResizeTimeout)
385
293
  fullscreenResizeTimeout = window.setTimeout(() => {
386
- updateLayout()
294
+ updateImageSizes()
387
295
  }, 50)
388
296
  })
389
297
  .catch(() => {})
@@ -395,7 +303,7 @@ function toggleFullscreen() {
395
303
  isFullscreen.value = false
396
304
  if (fullscreenResizeTimeout) clearTimeout(fullscreenResizeTimeout)
397
305
  fullscreenResizeTimeout = window.setTimeout(() => {
398
- updateLayout()
306
+ updateImageSizes()
399
307
  }, 50)
400
308
  })
401
309
  .catch(() => {})
@@ -522,7 +430,7 @@ watch(
522
430
  modelValue, // Watch for model value changes to update layout
523
431
  ],
524
432
  () => {
525
- updateLayout()
433
+ updateImageSizes()
526
434
  },
527
435
  )
528
436
 
@@ -532,29 +440,27 @@ onMounted(() => {
532
440
  eventBus.on(`${props.id}Gallery`, openGalleryImage)
533
441
  eventBus.on(`${props.id}GalleryClose`, closeGallery)
534
442
 
535
- // Initialize layout
536
- nextTick(() => {
537
- updateLayout()
538
- })
443
+ // Initialize CSS variables
444
+ updateImageSizes()
539
445
 
540
446
  // Set up observers for dynamic resizing
541
447
  if (topControlsRef.value) {
542
- useResizeObserver(topControlsRef.value, updateLayout)
448
+ useResizeObserver(topControlsRef.value, updateImageSizes)
543
449
  }
544
450
 
545
451
  if (infoPanelRef.value) {
546
- useResizeObserver(infoPanelRef.value, updateLayout)
452
+ useResizeObserver(infoPanelRef.value, updateImageSizes)
547
453
  }
548
454
 
549
455
  if (sidePanelRef.value) {
550
- useResizeObserver(sidePanelRef.value, updateLayout)
456
+ useResizeObserver(sidePanelRef.value, updateImageSizes)
551
457
  }
552
458
 
553
459
  // Listen for fullscreen changes
554
460
  useEventListener(document, 'fullscreenchange', () => {
555
461
  isFullscreen.value = !!document.fullscreenElement
556
462
  nextTick(() => {
557
- updateLayout()
463
+ updateImageSizes()
558
464
  })
559
465
  })
560
466
  })
@@ -658,11 +564,10 @@ onUnmounted(() => {
658
564
  <div
659
565
  ref="galleryContentRef"
660
566
  class="w-full h-full flex flex-col lg:flex-row"
661
- style="margin-top: var(--controls-height, 0px)"
662
567
  >
663
568
  <!-- Main Image Area - Fills available space -->
664
569
  <div
665
- class="relative flex-1 h-full flex items-center justify-center"
570
+ class="relative flex-1 h-full flex items-center justify-center pt-[var(--controls-height)]"
666
571
  :class="{ 'lg:pr-64': sidePanel, 'lg:max-w-[calc(100%-16rem)]': sidePanel }"
667
572
  style="max-width: 100%;"
668
573
  >
@@ -693,18 +598,18 @@ onUnmounted(() => {
693
598
  <div
694
599
  ref="imageContainerRef"
695
600
  class="image-container flex-grow flex items-center justify-center"
696
- :class="{ 'has-info': infoPanel }"
697
601
  @touchstart="touchStart"
698
602
  @touchend="touchEnd"
699
603
  >
700
604
  <transition
701
605
  :name="direction === 'next' ? 'slide-next' : 'slide-prev'"
702
606
  mode="out-in"
607
+ @before-enter="updateImageSizes"
608
+ @after-leave="updateImageSizes"
703
609
  >
704
610
  <div
705
611
  :key="`image-display-${modelValue}`"
706
612
  class="image-display relative w-full h-full flex flex-col items-center justify-center"
707
- @transitionend="calculateImageSize"
708
613
  >
709
614
  <!-- Actual Image/Video Content -->
710
615
  <template v-if="videoComponent && isVideo(images[modelValue])">
@@ -712,19 +617,17 @@ onUnmounted(() => {
712
617
  <component
713
618
  :is="videoComponent"
714
619
  :src="isVideo(images[modelValue])"
715
- class="shadow max-w-full h-auto object-contain video-component"
716
- :style="{ maxHeight: availableHeight }"
620
+ class="shadow max-w-full video-component gallery-image"
717
621
  />
718
622
  </ClientOnly>
719
623
  </template>
720
624
  <template v-else>
721
625
  <img
722
626
  v-if="modelValueSrc && imageComponent === 'img'"
723
- class="shadow max-w-full h-auto object-contain"
724
- :style="{ maxHeight: availableHeight }"
627
+ class="shadow max-w-full gallery-image"
725
628
  :src="modelValueSrc"
726
629
  :alt="`Gallery image ${modelValue + 1}`"
727
- @load="() => { calculateImageSize(); nextTick(() => calculateImageSize()) }"
630
+ @load="updateImageSizes"
728
631
  >
729
632
  <component
730
633
  :is="imageComponent"
@@ -732,8 +635,7 @@ onUnmounted(() => {
732
635
  :image="modelValueSrc.image"
733
636
  :variant="modelValueSrc.variant"
734
637
  :alt="modelValueSrc.alt"
735
- class="shadow max-w-full h-auto object-contain"
736
- :style="{ maxHeight: availableHeight }"
638
+ class="shadow max-w-full gallery-image"
737
639
  :likes="modelValueSrc.likes"
738
640
  :show-likes="modelValueSrc.showLikes"
739
641
  :is-author="modelValueSrc.isAuthor"
@@ -781,6 +683,7 @@ onUnmounted(() => {
781
683
  v-if="infoPanel && images[modelValue]"
782
684
  ref="infoPanelRef"
783
685
  class="info-panel absolute bottom-0 left-0 right-0 px-4 py-3 backdrop-blur-md bg-fv-neutral-900/70 z-45"
686
+ @transitionend="updateImageSizes"
784
687
  >
785
688
  <slot :value="images[modelValue]" />
786
689
  </div>
@@ -799,8 +702,7 @@ onUnmounted(() => {
799
702
  <div
800
703
  v-if="sidePanel"
801
704
  ref="sidePanelRef"
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"
803
- :style="{ 'padding-top': `${topControlsHeight + 8}px` }"
705
+ 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 pt-[calc(var(--controls-height)+8px)]"
804
706
  >
805
707
  <!-- Paging Controls if needed -->
806
708
  <div v-if="paging" class="flex items-center justify-center pt-2">
@@ -866,8 +768,8 @@ onUnmounted(() => {
866
768
  >
867
769
  <div
868
770
  v-if="showControls && images.length > 1 && !sidePanel"
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 }"
771
+ class="absolute bottom-0 left-0 right-0 p-1 lg:hidden bg-gradient-to-t from-fv-neutral-900/90 to-transparent backdrop-blur-sm z-45"
772
+ :class="{ 'pb-4': infoPanel, 'pb-2': !infoPanel }"
871
773
  @touchstart.stop
872
774
  @touchmove.stop
873
775
  @touchend.stop
@@ -1002,6 +904,13 @@ onUnmounted(() => {
1002
904
  </template>
1003
905
 
1004
906
  <style scoped>
907
+ /* CSS variables for dimensions */
908
+ :root {
909
+ --controls-height: 0px;
910
+ --info-height: 0px;
911
+ --sidebar-width: 16rem;
912
+ }
913
+
1005
914
  /* Ensure controls stay fixed at top */
1006
915
  .controls-bar {
1007
916
  height: auto;
@@ -1032,11 +941,24 @@ onUnmounted(() => {
1032
941
  border-top-right-radius: 0.5rem;
1033
942
  }
1034
943
 
1035
- /* Image sizing in different contexts */
1036
- .image-display img,
1037
- .image-display .video-component {
1038
- transition: max-height 0.3s ease-out, max-width 0.3s ease-out;
944
+ /* Image sizing in different contexts - simplified approach like old component */
945
+ .gallery-image {
946
+ height: auto;
1039
947
  object-fit: contain;
948
+ max-width: 92vw;
949
+ max-height: calc(80vh - var(--controls-height) - var(--info-height, 0px));
950
+ transition: max-height 0.3s ease-out, max-width 0.3s ease-out;
951
+ }
952
+
953
+ @media (min-width: 1024px) {
954
+ .gallery-image {
955
+ max-width: calc(92vw - var(--sidebar-width) - 48px);
956
+ }
957
+ }
958
+
959
+ /* Fullscreen mode sizing */
960
+ :is(.gallery-container[style*="fullscreen"]) .gallery-image {
961
+ max-height: calc(92vh - var(--controls-height) - var(--info-height, 0px));
1040
962
  }
1041
963
 
1042
964
  /* Transition styles for next (right) navigation */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.3.17",
3
+ "version": "2.3.18",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",