@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.11

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.
Files changed (76) hide show
  1. package/dist/CommentSheet.css-DyEc3Sro.d.ts +217 -0
  2. package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
  3. package/dist/chunk-3OB3OVYR.js +349 -0
  4. package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
  5. package/dist/chunk-4RIMQOBR.js +58 -0
  6. package/dist/chunk-4TUBNA2X.js +180 -0
  7. package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
  8. package/dist/{chunk-UXMA4KJZ.js → chunk-CAWE42LH.js} +5 -3
  9. package/dist/{chunk-ANGBSV7L.js → chunk-CIIZ3IHV.js} +10 -5
  10. package/dist/chunk-DR7KR7OT.js +103 -0
  11. package/dist/chunk-DXLCQ4FH.js +102 -0
  12. package/dist/chunk-EDWS2IPH.js +1 -0
  13. package/dist/chunk-FR7UQSZP.js +570 -0
  14. package/dist/chunk-IWSBYOSS.js +91 -0
  15. package/dist/chunk-JEY6R4KJ.js +334 -0
  16. package/dist/chunk-KMJ3PQ7M.js +1262 -0
  17. package/dist/chunk-MFJS65C5.js +368 -0
  18. package/dist/{chunk-HW4LXTFT.js → chunk-OM4L7RE5.js} +18 -6
  19. package/dist/chunk-PBIH2F2Q.js +344 -0
  20. package/dist/chunk-PJ4NMVMY.js +326 -0
  21. package/dist/chunk-Q6MG7AVG.js +531 -0
  22. package/dist/chunk-QCKVF2DR.js +713 -0
  23. package/dist/chunk-QCRRF76W.js +75 -0
  24. package/dist/chunk-QUEJHA24.js +508 -0
  25. package/dist/chunk-VXW7AOGM.js +285 -0
  26. package/dist/chunk-YB7AXTX7.js +430 -0
  27. package/dist/chunk-ZGWSJ6Z5.js +601 -0
  28. package/dist/components/ActionBar/index.js +1 -1
  29. package/dist/components/AdvanceMenu/index.d.ts +78 -0
  30. package/dist/components/AdvanceMenu/index.js +1 -0
  31. package/dist/components/AuthorInfo/index.d.ts +5 -1
  32. package/dist/components/AuthorInfo/index.js +1 -1
  33. package/dist/components/BottomSheet/index.d.ts +82 -0
  34. package/dist/components/BottomSheet/index.js +1 -0
  35. package/dist/components/CleanModeOverlay/index.d.ts +60 -0
  36. package/dist/components/CleanModeOverlay/index.js +1 -0
  37. package/dist/components/CommentSheet/index.d.ts +164 -0
  38. package/dist/components/CommentSheet/index.js +1 -0
  39. package/dist/components/DetailView/index.d.ts +311 -0
  40. package/dist/components/DetailView/index.js +1 -0
  41. package/dist/components/ErrorBoundary/index.js +1 -1
  42. package/dist/components/ImageCarousel/index.d.ts +50 -0
  43. package/dist/components/ImageCarousel/index.js +1 -0
  44. package/dist/components/ImagePostSlot/index.d.ts +207 -0
  45. package/dist/components/ImagePostSlot/index.js +1 -0
  46. package/dist/components/ProgressBar/index.d.ts +30 -2
  47. package/dist/components/ProgressBar/index.js +1 -1
  48. package/dist/components/QualityPicker/index.d.ts +35 -0
  49. package/dist/components/QualityPicker/index.js +1 -0
  50. package/dist/components/ReportSheet/index.d.ts +68 -0
  51. package/dist/components/ReportSheet/index.js +1 -0
  52. package/dist/components/Skeleton/index.js +1 -1
  53. package/dist/components/SpeedPicker/index.d.ts +32 -0
  54. package/dist/components/SpeedPicker/index.js +1 -0
  55. package/dist/components/VideoFeed/index.d.ts +12 -1
  56. package/dist/components/VideoFeed/index.js +1 -1
  57. package/dist/components/VideoInfo/index.d.ts +4 -2
  58. package/dist/components/VideoInfo/index.js +1 -1
  59. package/dist/components/VideoPlayer/index.d.ts +14 -41
  60. package/dist/components/VideoPlayer/index.js +1 -1
  61. package/dist/components/VideoSlot/index.d.ts +84 -65
  62. package/dist/components/VideoSlot/index.js +2 -1
  63. package/dist/components/VirtualSlider/index.d.ts +339 -0
  64. package/dist/components/VirtualSlider/index.js +1 -0
  65. package/dist/components/icons/index.js +1 -1
  66. package/dist/index.d.ts +107 -95
  67. package/dist/index.js +84 -27
  68. package/package.json +51 -7
  69. package/dist/chunk-2PTMP65P.js +0 -738
  70. package/dist/chunk-4MN72OZH.js +0 -148
  71. package/dist/chunk-DHQJBXQW.js +0 -562
  72. package/dist/chunk-SSJDO24Q.js +0 -204
  73. package/dist/chunk-XAOEHLOX.js +0 -1326
  74. package/dist/chunk-YW23IBKF.js +0 -530
  75. package/dist/chunk-ZZDQKP4R.js +0 -418
  76. package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
@@ -0,0 +1,531 @@
1
+ import { clsx2 } from './chunk-EDWS2IPH.js';
2
+ import { injectComponentCSS } from './chunk-CAWE42LH.js';
3
+ import { memo, useInsertionEffect, useState, useRef, useCallback, useEffect, useMemo } from 'react';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ // src/components/ImageCarousel/ImageCarousel.css.ts
7
+ var IMAGE_CAROUSEL_CSS = `.sv-image-carousel{position:relative;width:100%;height:100%;overflow:hidden;touch-action:pan-y pinch-zoom;user-select:none;-webkit-user-select:none;background:var(--sv-carousel-bg,#000)}.sv-image-carousel--dragging{-webkit-user-drag:none}.sv-image-carousel--swiping-horizontal{touch-action:none !important;overscroll-behavior:contain}.sv-image-carousel--swiping-vertical{touch-action:pan-y}.sv-image-carousel-track{display:flex;height:100%;will-change:transform;transition:transform .3s cubic-bezier(.25,.46,.45,.94)}.sv-image-carousel-track--dragging{transition:none}.sv-image-carousel-slide{flex:0 0 100%;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative}.sv-image-carousel-image{max-width:100%;max-height:100%;width:100%;height:100%;object-fit:contain;pointer-events:none;transition:transform .2s ease-out}.sv-image-carousel-image--loading{opacity:0}.sv-image-carousel-image--loaded{opacity:1;transition:opacity .3s ease}.sv-image-carousel-image--zoomed{cursor:grab;pointer-events:auto}.sv-image-carousel-image--zoomed:active{cursor:grabbing}.sv-image-carousel-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--sv-carousel-placeholder-bg,rgba(255,255,255,.1))}.sv-image-carousel-skeleton{width:60%;height:60%;background:linear-gradient(90deg,rgba(255,255,255,.05)25%,rgba(255,255,255,.1)50%,rgba(255,255,255,.05)75%);background-size:200% 100%;animation:sv-carousel-shimmer 1.5s infinite;border-radius:8px}@keyframes sv-carousel-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}.sv-image-carousel-indicators{position:absolute;bottom:var(--sv-carousel-indicators-bottom,16px);left:50%;transform:translateX(-50%);display:flex;gap:var(--sv-carousel-indicators-gap,6px);padding:6px 12px;border-radius:20px;z-index:10}.sv-image-carousel-dot{width:var(--sv-carousel-dot-size,6px);height:var(--sv-carousel-dot-size,6px);border-radius:50%;background:var(--sv-carousel-dot-color,rgba(255,255,255,.5));transition:all .2s ease;cursor:pointer;border:0;padding:0}.sv-image-carousel-dot:hover{background:var(--sv-carousel-dot-hover,rgba(255,255,255,.7))}.sv-image-carousel-dot--active{background:var(--sv-carousel-dot-active,#fff);transform:scale(1.2)}.sv-image-carousel-indicators--compact .sv-image-carousel-dot{width:4px;height:4px}.sv-image-carousel-counter{position:absolute;top:var(--sv-carousel-counter-top,16px);right:var(--sv-carousel-counter-right,16px);padding:4px 10px;background:var(--sv-carousel-counter-bg,rgba(0,0,0,.6));border-radius:12px;font-size:var(--sv-carousel-counter-font-size,12px);font-weight:500;color:var(--sv-carousel-counter-color,#fff);z-index:10}.sv-image-carousel-nav{position:absolute;top:50%;transform:translateY(-50%);width:40px;height:40px;display:flex;align-items:center;justify-content:center;background:var(--sv-carousel-nav-bg,rgba(0,0,0,.4));border:0;border-radius:50%;color:var(--sv-carousel-nav-color,#fff);cursor:pointer;z-index:10;opacity:0;transition:opacity .2s ease}.sv-image-carousel:hover .sv-image-carousel-nav{opacity:1}.sv-image-carousel-nav:hover{background:var(--sv-carousel-nav-hover-bg,rgba(0,0,0,.6))}.sv-image-carousel-nav:disabled{opacity:.3;cursor:not-allowed}.sv-image-carousel-nav--prev{left:12px}.sv-image-carousel-nav--next{right:12px}.sv-image-carousel-nav svg{width:20px;height:20px}.sv-image-carousel-zoom-container{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none}.sv-image-carousel:focus-visible{outline:2px solid var(--sv-carousel-focus-color,#4dabff);outline-offset:2px}.sv-image-carousel-dot:focus-visible{outline:2px solid var(--sv-carousel-focus-color,#4dabff);outline-offset:2px}.sv-image-carousel-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}`;
8
+ var cssInjected = false;
9
+ function injectImageCarouselCSS() {
10
+ if (cssInjected) return void 0;
11
+ cssInjected = true;
12
+ return injectComponentCSS("image-carousel", IMAGE_CAROUSEL_CSS);
13
+ }
14
+ var DEFAULT_SWIPE_THRESHOLD = 0.2;
15
+ var COMPACT_DOTS_THRESHOLD = 5;
16
+ function ChevronLeftIcon() {
17
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: /* @__PURE__ */ jsx("polyline", { points: "15 18 9 12 15 6" }) });
18
+ }
19
+ function ChevronRightIcon() {
20
+ return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: /* @__PURE__ */ jsx("polyline", { points: "9 6 15 12 9 18" }) });
21
+ }
22
+ function CarouselSlide({
23
+ url,
24
+ index,
25
+ shouldLoad,
26
+ isActive,
27
+ isZoomed,
28
+ zoomScale,
29
+ zoomTranslate,
30
+ renderImage,
31
+ renderPlaceholder
32
+ }) {
33
+ const [loaded, setLoaded] = useState(false);
34
+ const imageStyle = isActive && isZoomed ? {
35
+ transform: `scale(${zoomScale}) translate(${zoomTranslate.x}px, ${zoomTranslate.y}px)`
36
+ } : void 0;
37
+ return /* @__PURE__ */ jsx("div", { className: "sv-image-carousel-slide", "aria-hidden": !isActive, children: shouldLoad ? /* @__PURE__ */ jsxs(Fragment, { children: [
38
+ !loaded && /* @__PURE__ */ jsx("div", { className: "sv-image-carousel-placeholder", children: renderPlaceholder?.(index) ?? /* @__PURE__ */ jsx("div", { className: "sv-image-carousel-skeleton" }) }),
39
+ renderImage ? renderImage(url, index, loaded) : /* @__PURE__ */ jsx(
40
+ "img",
41
+ {
42
+ src: url,
43
+ alt: "",
44
+ className: clsx2(
45
+ "sv-image-carousel-image",
46
+ loaded ? "sv-image-carousel-image--loaded" : "sv-image-carousel-image--loading",
47
+ isActive && isZoomed && "sv-image-carousel-image--zoomed"
48
+ ),
49
+ style: imageStyle,
50
+ onLoad: () => setLoaded(true),
51
+ draggable: false
52
+ }
53
+ )
54
+ ] }) : /* @__PURE__ */ jsx("div", { className: "sv-image-carousel-placeholder", children: renderPlaceholder?.(index) ?? /* @__PURE__ */ jsx("div", { className: "sv-image-carousel-skeleton" }) }) });
55
+ }
56
+ function ImageCarouselHeadlessBase({
57
+ images,
58
+ currentIndex: controlledIndex,
59
+ onIndexChange,
60
+ showIndicators = true,
61
+ indicatorStyle = "dots",
62
+ showArrows = false,
63
+ enableZoom = false,
64
+ maxZoomScale = 3,
65
+ lazyLoad = true,
66
+ renderImage,
67
+ renderPlaceholder,
68
+ swipeThreshold = DEFAULT_SWIPE_THRESHOLD,
69
+ onZoomChange,
70
+ onSwipeDirectionChange,
71
+ className
72
+ }) {
73
+ useInsertionEffect(() => {
74
+ return injectImageCarouselCSS();
75
+ }, []);
76
+ const [internalIndex, setInternalIndex] = useState(0);
77
+ const activeIndex = controlledIndex ?? internalIndex;
78
+ const [loadedImages, setLoadedImages] = useState(() => /* @__PURE__ */ new Set([0]));
79
+ const [isDragging, setIsDragging] = useState(false);
80
+ const [dragOffset, setDragOffset] = useState(0);
81
+ const [swipeDirection, setSwipeDirection] = useState(null);
82
+ const [zoomScale, setZoomScale] = useState(1);
83
+ const [zoomTranslate, setZoomTranslate] = useState({ x: 0, y: 0 });
84
+ const isZoomed = zoomScale > 1;
85
+ const containerRef = useRef(null);
86
+ const dragStartRef = useRef(null);
87
+ const touchesRef = useRef(null);
88
+ const wasHorizontalSwipingRef = useRef(false);
89
+ const totalImages = images.length;
90
+ const canGoPrev = activeIndex > 0;
91
+ const canGoNext = activeIndex < totalImages - 1;
92
+ const useCompactDots = totalImages > COMPACT_DOTS_THRESHOLD;
93
+ const goToIndex = useCallback(
94
+ (index) => {
95
+ const clampedIndex = Math.max(0, Math.min(totalImages - 1, index));
96
+ if (clampedIndex !== activeIndex) {
97
+ setInternalIndex(clampedIndex);
98
+ onIndexChange?.(clampedIndex);
99
+ if (isZoomed) {
100
+ setZoomScale(1);
101
+ setZoomTranslate({ x: 0, y: 0 });
102
+ onZoomChange?.(false);
103
+ }
104
+ }
105
+ },
106
+ [activeIndex, totalImages, onIndexChange, isZoomed, onZoomChange]
107
+ );
108
+ const goToPrev = useCallback(() => {
109
+ if (canGoPrev) goToIndex(activeIndex - 1);
110
+ }, [canGoPrev, activeIndex, goToIndex]);
111
+ const goToNext = useCallback(() => {
112
+ if (canGoNext) goToIndex(activeIndex + 1);
113
+ }, [canGoNext, activeIndex, goToIndex]);
114
+ useEffect(() => {
115
+ if (!lazyLoad) return;
116
+ const toLoad = /* @__PURE__ */ new Set();
117
+ toLoad.add(activeIndex);
118
+ if (activeIndex > 0) toLoad.add(activeIndex - 1);
119
+ if (activeIndex < totalImages - 1) toLoad.add(activeIndex + 1);
120
+ setLoadedImages((prev) => {
121
+ const next = new Set(prev);
122
+ for (const idx of toLoad) {
123
+ next.add(idx);
124
+ }
125
+ return next;
126
+ });
127
+ }, [activeIndex, totalImages, lazyLoad]);
128
+ useEffect(() => {
129
+ wasHorizontalSwipingRef.current = swipeDirection === "horizontal";
130
+ onSwipeDirectionChange?.(swipeDirection);
131
+ }, [swipeDirection, onSwipeDirectionChange]);
132
+ useEffect(() => {
133
+ const container = containerRef.current;
134
+ if (!container) return;
135
+ const handleNativeTouchMove = (e) => {
136
+ if (swipeDirection === "horizontal" || wasHorizontalSwipingRef.current) {
137
+ e.preventDefault();
138
+ e.stopPropagation();
139
+ }
140
+ };
141
+ const handleNativeTouchEnd = (e) => {
142
+ if (swipeDirection === "horizontal" || wasHorizontalSwipingRef.current) {
143
+ e.preventDefault();
144
+ e.stopPropagation();
145
+ }
146
+ };
147
+ const handleNativeTouchCancel = (e) => {
148
+ if (swipeDirection === "horizontal" || wasHorizontalSwipingRef.current) {
149
+ e.preventDefault();
150
+ e.stopPropagation();
151
+ }
152
+ };
153
+ container.addEventListener("touchmove", handleNativeTouchMove, { passive: false });
154
+ container.addEventListener("touchend", handleNativeTouchEnd, { passive: false });
155
+ container.addEventListener("touchcancel", handleNativeTouchCancel, { passive: false });
156
+ return () => {
157
+ container.removeEventListener("touchmove", handleNativeTouchMove);
158
+ container.removeEventListener("touchend", handleNativeTouchEnd);
159
+ container.removeEventListener("touchcancel", handleNativeTouchCancel);
160
+ };
161
+ }, [swipeDirection]);
162
+ const handlePointerDown = useCallback(
163
+ (e) => {
164
+ if (isZoomed) return;
165
+ dragStartRef.current = {
166
+ x: e.clientX,
167
+ y: e.clientY,
168
+ time: Date.now()
169
+ };
170
+ setIsDragging(true);
171
+ setSwipeDirection(null);
172
+ wasHorizontalSwipingRef.current = false;
173
+ e.currentTarget.setPointerCapture(e.pointerId);
174
+ },
175
+ [isZoomed]
176
+ );
177
+ const detectSwipeDirection = useCallback(
178
+ (dx, dy) => {
179
+ const DIRECTION_THRESHOLD = 5;
180
+ const totalMovement = Math.abs(dx) + Math.abs(dy);
181
+ if (totalMovement <= DIRECTION_THRESHOLD) {
182
+ return "undecided";
183
+ }
184
+ return Math.abs(dx) > Math.abs(dy) ? "horizontal" : "vertical";
185
+ },
186
+ []
187
+ );
188
+ const releasePointer = useCallback((e) => {
189
+ if (e.currentTarget.hasPointerCapture(e.pointerId)) {
190
+ e.currentTarget.releasePointerCapture(e.pointerId);
191
+ }
192
+ }, []);
193
+ const cancelDragGesture = useCallback(
194
+ (e) => {
195
+ releasePointer(e);
196
+ setIsDragging(false);
197
+ setDragOffset(0);
198
+ setSwipeDirection(null);
199
+ wasHorizontalSwipingRef.current = false;
200
+ dragStartRef.current = null;
201
+ },
202
+ [releasePointer]
203
+ );
204
+ const processHorizontalSwipe = useCallback((e, dx) => {
205
+ e.preventDefault();
206
+ e.stopPropagation();
207
+ setDragOffset(dx);
208
+ }, []);
209
+ const handlePointerMove = useCallback(
210
+ (e) => {
211
+ if (!isDragging || !dragStartRef.current) return;
212
+ const dx = e.clientX - dragStartRef.current.x;
213
+ const dy = e.clientY - dragStartRef.current.y;
214
+ if (swipeDirection === "vertical") return;
215
+ const detectedDirection = detectSwipeDirection(dx, dy);
216
+ if (detectedDirection === "undecided") {
217
+ return;
218
+ }
219
+ if (detectedDirection === "vertical") {
220
+ setSwipeDirection("vertical");
221
+ cancelDragGesture(e);
222
+ return;
223
+ }
224
+ if (detectedDirection === "horizontal") {
225
+ if (swipeDirection !== "horizontal") {
226
+ setSwipeDirection("horizontal");
227
+ wasHorizontalSwipingRef.current = true;
228
+ }
229
+ processHorizontalSwipe(e, dx);
230
+ }
231
+ },
232
+ [isDragging, swipeDirection, detectSwipeDirection, cancelDragGesture, processHorizontalSwipe]
233
+ );
234
+ const resetDragState = useCallback(() => {
235
+ setIsDragging(false);
236
+ setDragOffset(0);
237
+ setSwipeDirection(null);
238
+ dragStartRef.current = null;
239
+ }, []);
240
+ const executeSwipeNavigation = useCallback(
241
+ (offset, velocity, threshold) => {
242
+ const shouldSwipe = Math.abs(offset) > threshold || Math.abs(velocity) > 0.5;
243
+ if (!shouldSwipe) return;
244
+ if (offset > 0 && canGoPrev) {
245
+ goToPrev();
246
+ } else if (offset < 0 && canGoNext) {
247
+ goToNext();
248
+ }
249
+ },
250
+ [canGoPrev, canGoNext, goToPrev, goToNext]
251
+ );
252
+ const handlePointerUp = useCallback(
253
+ (e) => {
254
+ if (!isDragging || !dragStartRef.current) return;
255
+ if (swipeDirection === "horizontal") {
256
+ e.preventDefault();
257
+ e.stopPropagation();
258
+ }
259
+ releasePointer(e);
260
+ const container = containerRef.current;
261
+ if (!container) {
262
+ resetDragState();
263
+ return;
264
+ }
265
+ const containerWidth = container.offsetWidth;
266
+ const threshold = containerWidth * swipeThreshold;
267
+ const velocity = dragOffset / (Date.now() - dragStartRef.current.time);
268
+ executeSwipeNavigation(dragOffset, velocity, threshold);
269
+ const wasHorizontal = swipeDirection === "horizontal";
270
+ setIsDragging(false);
271
+ setDragOffset(0);
272
+ dragStartRef.current = null;
273
+ if (wasHorizontal) {
274
+ setTimeout(() => {
275
+ setSwipeDirection(null);
276
+ wasHorizontalSwipingRef.current = false;
277
+ }, 300);
278
+ } else {
279
+ setSwipeDirection(null);
280
+ }
281
+ },
282
+ [
283
+ isDragging,
284
+ dragOffset,
285
+ swipeThreshold,
286
+ executeSwipeNavigation,
287
+ releasePointer,
288
+ swipeDirection,
289
+ resetDragState
290
+ ]
291
+ );
292
+ const handlePointerCancel = useCallback(
293
+ (e) => {
294
+ if (!isDragging) return;
295
+ releasePointer(e);
296
+ resetDragState();
297
+ },
298
+ [isDragging, resetDragState, releasePointer]
299
+ );
300
+ const handleTouchStart = useCallback(
301
+ (e) => {
302
+ if (e.touches.length === 1) {
303
+ if (isDragging && swipeDirection === "horizontal") {
304
+ e.preventDefault();
305
+ }
306
+ return;
307
+ }
308
+ if (!enableZoom) return;
309
+ if (e.touches.length !== 2) return;
310
+ const touch1 = e.touches[0];
311
+ const touch2 = e.touches[1];
312
+ if (!touch1 || !touch2) return;
313
+ const distance = Math.hypot(touch2.clientX - touch1.clientX, touch2.clientY - touch1.clientY);
314
+ const center = {
315
+ x: (touch1.clientX + touch2.clientX) / 2,
316
+ y: (touch1.clientY + touch2.clientY) / 2
317
+ };
318
+ touchesRef.current = { distance, center };
319
+ if (isDragging) {
320
+ resetDragState();
321
+ }
322
+ },
323
+ [enableZoom, isDragging, swipeDirection, resetDragState]
324
+ );
325
+ const handleTouchMove = useCallback(
326
+ (e) => {
327
+ if (e.touches.length === 1) {
328
+ if (swipeDirection === "horizontal") {
329
+ e.preventDefault();
330
+ e.stopPropagation();
331
+ }
332
+ return;
333
+ }
334
+ if (!enableZoom) return;
335
+ if (e.touches.length !== 2 || !touchesRef.current) return;
336
+ const touch1 = e.touches[0];
337
+ const touch2 = e.touches[1];
338
+ if (!touch1 || !touch2) return;
339
+ e.preventDefault();
340
+ const newDistance = Math.hypot(
341
+ touch2.clientX - touch1.clientX,
342
+ touch2.clientY - touch1.clientY
343
+ );
344
+ const scale = newDistance / touchesRef.current.distance;
345
+ const newScale = Math.max(1, Math.min(maxZoomScale, zoomScale * scale));
346
+ setZoomScale(newScale);
347
+ if (newScale > 1) {
348
+ onZoomChange?.(true);
349
+ }
350
+ touchesRef.current.distance = newDistance;
351
+ },
352
+ [enableZoom, maxZoomScale, zoomScale, onZoomChange, swipeDirection]
353
+ );
354
+ const handleTouchEnd = useCallback(
355
+ (e) => {
356
+ if (swipeDirection === "horizontal" || wasHorizontalSwipingRef.current) {
357
+ e.preventDefault();
358
+ e.stopPropagation();
359
+ }
360
+ touchesRef.current = null;
361
+ if (zoomScale < 1.1) {
362
+ setZoomScale(1);
363
+ setZoomTranslate({ x: 0, y: 0 });
364
+ onZoomChange?.(false);
365
+ }
366
+ },
367
+ [zoomScale, onZoomChange, swipeDirection]
368
+ );
369
+ const handleTouchCancel = useCallback(
370
+ (e) => {
371
+ if (swipeDirection === "horizontal" || wasHorizontalSwipingRef.current) {
372
+ e.preventDefault();
373
+ e.stopPropagation();
374
+ }
375
+ touchesRef.current = null;
376
+ },
377
+ [swipeDirection]
378
+ );
379
+ const handleKeyDown = useCallback(
380
+ (e) => {
381
+ if (isZoomed) {
382
+ if (e.key === "Escape") {
383
+ setZoomScale(1);
384
+ setZoomTranslate({ x: 0, y: 0 });
385
+ onZoomChange?.(false);
386
+ }
387
+ return;
388
+ }
389
+ switch (e.key) {
390
+ case "ArrowLeft":
391
+ e.preventDefault();
392
+ goToPrev();
393
+ break;
394
+ case "ArrowRight":
395
+ e.preventDefault();
396
+ goToNext();
397
+ break;
398
+ case "Home":
399
+ e.preventDefault();
400
+ goToIndex(0);
401
+ break;
402
+ case "End":
403
+ e.preventDefault();
404
+ goToIndex(totalImages - 1);
405
+ break;
406
+ }
407
+ },
408
+ [isZoomed, goToPrev, goToNext, goToIndex, totalImages, onZoomChange]
409
+ );
410
+ const trackTransform = useMemo(() => {
411
+ const baseOffset = -activeIndex * 100;
412
+ const dragPercent = containerRef.current ? dragOffset / containerRef.current.offsetWidth * 100 : 0;
413
+ return `translateX(${baseOffset + dragPercent}%)`;
414
+ }, [activeIndex, dragOffset]);
415
+ if (images.length === 0) {
416
+ return /* @__PURE__ */ jsx("div", { className: clsx2("sv-image-carousel", className) });
417
+ }
418
+ return /* @__PURE__ */ jsxs(
419
+ "div",
420
+ {
421
+ ref: containerRef,
422
+ className: clsx2(
423
+ "sv-image-carousel",
424
+ isDragging && "sv-image-carousel--dragging",
425
+ swipeDirection === "horizontal" && "sv-image-carousel--swiping-horizontal",
426
+ swipeDirection === "vertical" && "sv-image-carousel--swiping-vertical",
427
+ className
428
+ ),
429
+ onPointerDown: handlePointerDown,
430
+ onPointerMove: handlePointerMove,
431
+ onPointerUp: handlePointerUp,
432
+ onPointerCancel: handlePointerCancel,
433
+ onPointerLeave: handlePointerCancel,
434
+ onTouchStart: handleTouchStart,
435
+ onTouchMove: handleTouchMove,
436
+ onTouchEnd: handleTouchEnd,
437
+ onTouchCancel: handleTouchCancel,
438
+ onKeyDown: handleKeyDown,
439
+ "aria-roledescription": "carousel",
440
+ "aria-label": `Image carousel, ${activeIndex + 1} of ${totalImages}`,
441
+ children: [
442
+ /* @__PURE__ */ jsxs("div", { className: "sv-image-carousel-sr-only", "aria-live": "polite", children: [
443
+ "Image ",
444
+ activeIndex + 1,
445
+ " of ",
446
+ totalImages
447
+ ] }),
448
+ /* @__PURE__ */ jsx(
449
+ "div",
450
+ {
451
+ className: clsx2("sv-image-carousel-track", isDragging && "sv-image-carousel-track--dragging"),
452
+ style: { transform: trackTransform },
453
+ children: images.map((url, index) => /* @__PURE__ */ jsx(
454
+ CarouselSlide,
455
+ {
456
+ url,
457
+ index,
458
+ shouldLoad: !lazyLoad || loadedImages.has(index),
459
+ isActive: index === activeIndex,
460
+ isZoomed,
461
+ zoomScale,
462
+ zoomTranslate,
463
+ renderImage,
464
+ renderPlaceholder
465
+ },
466
+ url
467
+ ))
468
+ }
469
+ ),
470
+ showIndicators && totalImages > 1 && (indicatorStyle === "dots" ? /* @__PURE__ */ jsx(
471
+ "div",
472
+ {
473
+ className: clsx2(
474
+ "sv-image-carousel-indicators",
475
+ useCompactDots && "sv-image-carousel-indicators--compact"
476
+ ),
477
+ role: "tablist",
478
+ "aria-label": "Image pagination",
479
+ children: images.map((_, index) => /* @__PURE__ */ jsx(
480
+ "button",
481
+ {
482
+ type: "button",
483
+ className: clsx2(
484
+ "sv-image-carousel-dot",
485
+ index === activeIndex && "sv-image-carousel-dot--active"
486
+ ),
487
+ onClick: () => goToIndex(index),
488
+ role: "tab",
489
+ "aria-selected": index === activeIndex,
490
+ "aria-label": `Go to image ${index + 1}`
491
+ },
492
+ _
493
+ ))
494
+ }
495
+ ) : /* @__PURE__ */ jsxs("div", { className: "sv-image-carousel-counter", children: [
496
+ activeIndex + 1,
497
+ "/",
498
+ totalImages
499
+ ] })),
500
+ showArrows && totalImages > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
501
+ /* @__PURE__ */ jsx(
502
+ "button",
503
+ {
504
+ type: "button",
505
+ className: "sv-image-carousel-nav sv-image-carousel-nav--prev",
506
+ onClick: goToPrev,
507
+ disabled: !canGoPrev,
508
+ "aria-label": "Previous image",
509
+ children: /* @__PURE__ */ jsx(ChevronLeftIcon, {})
510
+ }
511
+ ),
512
+ /* @__PURE__ */ jsx(
513
+ "button",
514
+ {
515
+ type: "button",
516
+ className: "sv-image-carousel-nav sv-image-carousel-nav--next",
517
+ onClick: goToNext,
518
+ disabled: !canGoNext,
519
+ "aria-label": "Next image",
520
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, {})
521
+ }
522
+ )
523
+ ] })
524
+ ]
525
+ }
526
+ );
527
+ }
528
+ var ImageCarouselHeadless = memo(ImageCarouselHeadlessBase);
529
+ ImageCarouselHeadless.displayName = "ImageCarouselHeadless";
530
+
531
+ export { IMAGE_CAROUSEL_CSS, ImageCarouselHeadless };