@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.
@@ -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
- // Convert to rem for consistent sizing
63
- return `${height / 16}rem`
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
- // Image size and positioning
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
- const imageElements = imageContainerRef.value?.querySelectorAll('.image-display img, .image-display .video-component') as NodeListOf<HTMLElement>
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
- if (!imageElements || imageElements.length === 0) return
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
- imageElements.forEach((img) => {
159
- // Reset to ensure proper recalculation
160
- img.style.maxHeight = ''
161
- // Force browser to recalculate styles
162
- void img.offsetHeight
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
- // Set proper height and width
165
- img.style.maxHeight = availableHeight.value
197
+ img.style.maxHeight = `${availableHeightPx}px`
198
+ img.style.maxWidth = `${availableWidthPx}px`
199
+ }
166
200
 
167
- // Adjust image size based on screen size
168
- if (windowWidth.value <= 768) {
169
- img.style.maxWidth = '95vw'
170
- img.style.maxHeight = `calc(${windowHeight.value * 0.7 / 16}rem)`
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.style.maxWidth = sidePanel.value ? 'calc(100vw - 17rem)' : '94vw'
207
+ img.onload = () => adjustImageSize(img, availableHeightPx)
174
208
  }
175
- })
209
+ }
176
210
  })
177
- }, 50)
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
- }, 50)
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-20': infoPanel }"
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 pb-1 px-1">
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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.3.15",
3
+ "version": "2.3.17",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",