@jekrch/react-viewport-lightbox 0.1.0
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/LICENSE +21 -0
- package/README.md +321 -0
- package/dist/index.cjs +1078 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +277 -0
- package/dist/index.d.ts +277 -0
- package/dist/index.js +1059 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +344 -0
- package/package.json +89 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/math.ts","../src/hooks/useImageZoomPan.ts","../src/hooks/useSlideNavigation.ts","../src/hooks/useGestureHandler.ts","../src/hooks/useBarMeasure.ts","../src/hooks/useBodyScrollLock.ts","../src/hooks/useFocusTrap.ts","../src/components/icons.tsx","../src/components/cx.ts","../src/components/NavButton.tsx","../src/components/ImageViewer.tsx"],"names":["useRef","useState","useCallback","clampTranslate","useLayoutEffect","useEffect","sg","jsxs","jsx","useMemo"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Pure geometry/threshold helpers shared by the interaction hooks. Kept free of\n * React and DOM globals so they can be unit-tested in isolation.\n */\n\nexport interface Dims {\n width: number;\n height: number;\n}\n\n/**\n * Clamp a pan translation so the scaled image edge can't move past the\n * viewport edge. Returns `{ x: 0, y: 0 }` when not zoomed or when base\n * dimensions are unknown.\n */\nexport function clampTranslate(\n x: number,\n y: number,\n scale: number,\n baseDims: Dims,\n viewport: Dims,\n): { x: number; y: number } {\n if (scale <= 1) return { x: 0, y: 0 };\n const { width: baseW, height: baseH } = baseDims;\n if (baseW === 0 || baseH === 0) return { x: 0, y: 0 };\n\n const scaledHalfW = (baseW * scale) / 2;\n const scaledHalfH = (baseH * scale) / 2;\n const vpHalfW = viewport.width / 2;\n const vpHalfH = viewport.height / 2;\n\n const maxX = Math.max(0, scaledHalfW - vpHalfW);\n const maxY = Math.max(0, scaledHalfH - vpHalfH);\n\n return {\n x: Math.max(-maxX, Math.min(maxX, x)),\n y: Math.max(-maxY, Math.min(maxY, y)),\n };\n}\n\nexport type SlideAction = \"prev\" | \"next\" | \"snap\";\n\nexport interface ResolveSlideArgs {\n /** Current horizontal swipe offset in px (positive = dragged right). */\n offset: number;\n /** Elapsed time of the gesture in ms (used for fling velocity). */\n elapsedMs: number;\n viewportWidth: number;\n hasPrev: boolean;\n hasNext: boolean;\n /** Fraction of viewport width past which a drag commits. Default 0.25. */\n distanceThreshold?: number;\n /** px/ms past which a fast fling commits regardless of distance. Default 0.4. */\n velocityThreshold?: number;\n}\n\n/**\n * Decide whether a released swipe should navigate `prev`/`next` or `snap` back,\n * based on distance and fling velocity.\n */\nexport function resolveSlideDirection({\n offset,\n elapsedMs,\n viewportWidth,\n hasPrev,\n hasNext,\n distanceThreshold = 0.25,\n velocityThreshold = 0.4,\n}: ResolveSlideArgs): SlideAction {\n const velocity = Math.abs(offset) / Math.max(elapsedMs, 1);\n const threshold = viewportWidth * distanceThreshold;\n const committed = Math.abs(offset) > threshold || velocity > velocityThreshold;\n\n if (offset > 0 && hasPrev && committed) return \"prev\";\n if (offset < 0 && hasNext && committed) return \"next\";\n return \"snap\";\n}\n","import { useCallback, useEffect, useLayoutEffect, useRef, useState, type RefObject } from \"react\";\nimport { clampTranslate as clampTranslatePure } from \"./math\";\n\nconst MIN_SCALE = 1;\nconst MAX_SCALE = 5;\n\nexport interface ImageTransform {\n scale: number;\n x: number;\n y: number;\n}\n\nexport interface ImageZoomPanState {\n imgRef: RefObject<HTMLImageElement | null>;\n displayScale: number;\n isZoomed: boolean;\n transformRef: React.MutableRefObject<ImageTransform>;\n\n /** Base (unscaled) image dimensions for clamp calculations */\n baseDimsRef: React.MutableRefObject<{ width: number; height: number }>;\n\n resetTransform: () => void;\n setTransform: (t: ImageTransform, animate?: boolean) => void;\n applyTransform: (t: ImageTransform, animate?: boolean) => void;\n clampTranslate: (x: number, y: number, scale: number) => { x: number; y: number };\n measureBaseDims: () => void;\n handleDoubleClick: (e: React.MouseEvent) => void;\n}\n\n/**\n * Manages zoom/pan state for an image viewer.\n *\n * Applies scale + translate transforms to the **wrapper** element rather\n * than the image itself. This avoids an iOS Safari compositing bug where\n * CSS scale() on an element clips its painted output to the element's\n * original layout bounds.\n *\n * When zoomed, the wrapper is positioned absolute inset-0 (full viewport),\n * so its layout bounds already match the viewport and scaling it won't clip.\n * The image stays at its natural constrained size, centered via flexbox.\n */\nexport function useImageZoomPan(\n imgWrapperRef: RefObject<HTMLDivElement | null>,\n currentIndex: number,\n /** When false, wheel-zoom and double-click-zoom are disabled. Default true. */\n enabled = true,\n): ImageZoomPanState {\n const imgRef = useRef<HTMLImageElement>(null);\n const [displayScale, setDisplayScale] = useState(1);\n const transformRef = useRef<ImageTransform>({ scale: 1, x: 0, y: 0 });\n const baseDimsRef = useRef<{ width: number; height: number }>({ width: 0, height: 0 });\n\n // Core helpers\n\n const applyTransform = useCallback(\n (t: ImageTransform, animate = false) => {\n const wrapper = imgWrapperRef.current;\n if (!wrapper) return;\n wrapper.style.transition = animate ? \"transform 0.2s ease-out\" : \"none\";\n\n if (t.scale <= 1) {\n wrapper.style.transform = \"none\";\n wrapper.style.position = \"\";\n wrapper.style.inset = \"\";\n wrapper.style.zIndex = \"\";\n wrapper.style.backgroundColor = \"\";\n wrapper.style.cursor = \"\";\n } else {\n wrapper.style.transform = `scale(${t.scale}) translate(${t.x / t.scale}px, ${t.y / t.scale}px)`;\n wrapper.style.position = \"absolute\";\n wrapper.style.inset = \"0\";\n wrapper.style.zIndex = \"30\";\n wrapper.style.backgroundColor = \"black\";\n wrapper.style.cursor = \"grab\";\n }\n\n // Keep React state in sync so isZoomed reflects reality\n setDisplayScale(t.scale);\n },\n [imgWrapperRef],\n );\n\n const setTransform = useCallback(\n (t: ImageTransform, animate = false) => {\n transformRef.current = t;\n applyTransform(t, animate);\n setDisplayScale(t.scale);\n },\n [applyTransform],\n );\n\n const resetTransform = useCallback(() => {\n setTransform({ scale: 1, x: 0, y: 0 }, true);\n }, [setTransform]);\n\n const measureBaseDims = useCallback(() => {\n const img = imgRef.current;\n if (!img) return;\n baseDimsRef.current = { width: img.offsetWidth, height: img.offsetHeight };\n }, []);\n\n /**\n * Clamp so the image edge can't pan past the viewport edge.\n */\n const clampTranslate = useCallback(\n (x: number, y: number, scale: number): { x: number; y: number } =>\n clampTranslatePure(x, y, scale, baseDimsRef.current, {\n width: window.innerWidth,\n height: window.innerHeight,\n }),\n [],\n );\n\n // Reset on navigation\n\n useLayoutEffect(() => {\n const wrapper = imgWrapperRef.current;\n if (wrapper) {\n wrapper.style.transition = \"none\";\n wrapper.style.transform = \"none\";\n wrapper.style.position = \"\";\n wrapper.style.inset = \"\";\n wrapper.style.zIndex = \"\";\n wrapper.style.backgroundColor = \"\";\n wrapper.style.cursor = \"\";\n }\n transformRef.current = { scale: 1, x: 0, y: 0 };\n }, [currentIndex, imgWrapperRef]);\n\n useEffect(() => {\n setDisplayScale(1);\n }, [currentIndex]);\n\n // Wheel zoom (desktop)\n\n useEffect(() => {\n if (!enabled) return;\n const wrapper = imgWrapperRef.current;\n if (!wrapper) return;\n\n const handleWheel = (e: WheelEvent) => {\n e.preventDefault();\n\n // Ensure base dims\n if (baseDimsRef.current.width === 0) {\n const img = imgRef.current;\n if (img) baseDimsRef.current = { width: img.offsetWidth, height: img.offsetHeight };\n }\n\n const t = transformRef.current;\n\n let dy = e.deltaY;\n if (e.deltaMode === 1) dy *= 16;\n if (e.deltaMode === 2) dy *= 100;\n\n const normalized = Math.max(-100, Math.min(100, dy));\n const step = -(normalized / 100) * 0.05;\n const factor = 1 + step;\n\n const nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, t.scale * factor));\n const clamped = nextScale <= 1 ? { x: 0, y: 0 } : clampTranslate(t.x, t.y, nextScale);\n setTransform({ scale: nextScale, ...clamped });\n };\n\n wrapper.addEventListener(\"wheel\", handleWheel, { passive: false });\n return () => wrapper.removeEventListener(\"wheel\", handleWheel);\n }, [imgWrapperRef, setTransform, clampTranslate, enabled]);\n\n // Double-click toggle\n\n const handleDoubleClick = useCallback(\n (e: React.MouseEvent) => {\n if (!enabled) return;\n e.stopPropagation();\n\n // Ensure base dims\n if (baseDimsRef.current.width === 0) {\n const img = imgRef.current;\n if (img) baseDimsRef.current = { width: img.offsetWidth, height: img.offsetHeight };\n }\n\n if (transformRef.current.scale > 1) {\n resetTransform();\n } else {\n setTransform({ scale: 1.8, x: 0, y: 0 }, true);\n }\n },\n [resetTransform, setTransform, enabled],\n );\n\n return {\n imgRef,\n displayScale,\n isZoomed: displayScale > 1,\n transformRef,\n baseDimsRef,\n resetTransform,\n setTransform,\n applyTransform,\n clampTranslate,\n measureBaseDims,\n handleDoubleClick,\n };\n}\n\nexport { MIN_SCALE, MAX_SCALE };\n","import { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type { ViewerItem } from \"../types\";\nimport { resolveSlideDirection } from \"./math\";\n\nexport interface SlideNavigationState {\n slideTrackRef: React.RefObject<HTMLDivElement | null>;\n slideActive: boolean;\n slideAnimating: boolean;\n swipeOffset: number;\n swipeOffsetRef: React.MutableRefObject<number>;\n commitLockRef: React.MutableRefObject<boolean>;\n\n applySlideOffset: (offset: number, animate?: boolean) => void;\n commitSlide: (direction: \"prev\" | \"next\") => void;\n snapBack: () => void;\n resolveSlide: (gestureStartTime: number) => void;\n setSlideActive: React.Dispatch<React.SetStateAction<boolean>>;\n}\n\n/**\n * Manages the three-slot slide carousel: swipe offset tracking, animated\n * commit/snap-back, and DOM resets on navigation.\n *\n * `items[i].src` is treated as a final, ready-to-load url — the next image is\n * preloaded/decoded before navigation commits so the swipe lands on a painted\n * frame.\n */\nexport function useSlideNavigation(\n items: ViewerItem[],\n currentIndex: number,\n onNavigate: (index: number) => void,\n onSlideStart?: (direction: \"prev\" | \"next\") => void,\n): SlideNavigationState {\n const slideTrackRef = useRef<HTMLDivElement>(null);\n const swipeOffsetRef = useRef(0);\n const [swipeOffset, setSwipeOffset] = useState(0);\n const [slideAnimating, setSlideAnimating] = useState(false);\n const [slideActive, setSlideActive] = useState(false);\n const commitLockRef = useRef(false);\n\n const hasPrev = currentIndex > 0;\n const hasNext = currentIndex < items.length - 1;\n\n const applySlideOffset = useCallback((offset: number, animate = false) => {\n swipeOffsetRef.current = offset;\n const track = slideTrackRef.current;\n if (track) {\n track.style.transition = animate ? \"transform 0.28s cubic-bezier(0.2, 0, 0, 1)\" : \"none\";\n track.style.transform = `translateX(${offset}px)`;\n }\n setSwipeOffset(offset);\n }, []);\n\n const snapBack = useCallback(() => {\n setSlideAnimating(true);\n applySlideOffset(0, true);\n\n const track = slideTrackRef.current;\n let done = false;\n const onEnd = () => {\n if (done) return;\n done = true;\n track?.removeEventListener(\"transitionend\", onEnd);\n setSlideAnimating(false);\n setSlideActive(false);\n };\n if (track) {\n track.addEventListener(\"transitionend\", onEnd, { once: true });\n setTimeout(onEnd, 350);\n }\n }, [applySlideOffset]);\n\n const readyRef = useRef(true);\n\n const commitSlide = useCallback(\n (direction: \"prev\" | \"next\") => {\n if (commitLockRef.current || !readyRef.current) return;\n commitLockRef.current = true;\n readyRef.current = false;\n\n const vw = window.innerWidth;\n const targetOffset = direction === \"prev\" ? vw : -vw;\n setSlideActive(true);\n setSlideAnimating(true);\n // Fire at the START of the slide so overlays (info drawers) can animate\n // out in sync with the image — onNavigate only fires once it completes.\n onSlideStart?.(direction);\n\n requestAnimationFrame(() => {\n applySlideOffset(targetOffset, true);\n\n const track = slideTrackRef.current;\n let cleaned = false;\n\n const cleanup = () => {\n if (cleaned) return;\n cleaned = true;\n track?.removeEventListener(\"transitionend\", onTransitionEnd);\n\n const newIndex = direction === \"prev\" ? currentIndex - 1 : currentIndex + 1;\n if (newIndex < 0 || newIndex >= items.length) {\n commitLockRef.current = false;\n return;\n }\n\n const newItem = items[newIndex];\n const preload = new Image();\n preload.src = newItem.src;\n\n const doNavigate = () => onNavigate(newIndex);\n\n // Add a timeout so a stalled decode can't block navigation forever\n const timeout = setTimeout(doNavigate, 300);\n preload\n .decode()\n .then(() => {\n clearTimeout(timeout);\n doNavigate();\n })\n .catch(() => {\n clearTimeout(timeout);\n doNavigate();\n });\n };\n\n const onTransitionEnd = () => cleanup();\n if (track) {\n track.addEventListener(\"transitionend\", onTransitionEnd, { once: true });\n setTimeout(cleanup, 400);\n }\n });\n },\n [applySlideOffset, currentIndex, items, onNavigate, onSlideStart],\n );\n\n const resolveSlide = useCallback(\n (gestureStartTime: number) => {\n const action = resolveSlideDirection({\n offset: swipeOffsetRef.current,\n elapsedMs: Date.now() - gestureStartTime,\n viewportWidth: window.innerWidth,\n hasPrev,\n hasNext,\n });\n\n if (action === \"prev\") commitSlide(\"prev\");\n else if (action === \"next\") commitSlide(\"next\");\n else snapBack();\n },\n [hasPrev, hasNext, commitSlide, snapBack],\n );\n\n // DOM resets on navigation (pre-paint)\n\n useLayoutEffect(() => {\n const track = slideTrackRef.current;\n if (track) {\n track.style.transition = \"none\";\n track.offsetHeight;\n track.style.transform = \"translateX(0px)\";\n }\n swipeOffsetRef.current = 0;\n commitLockRef.current = false;\n }, [currentIndex]);\n\n // React state cleanup — runs after paint\n useEffect(() => {\n setSwipeOffset(0);\n setSlideAnimating(false);\n setSlideActive(false);\n // Allow next commit only after React has painted the new panel\n readyRef.current = true;\n }, [currentIndex]);\n\n return {\n slideTrackRef,\n slideActive,\n slideAnimating,\n swipeOffset,\n swipeOffsetRef,\n commitLockRef,\n applySlideOffset,\n commitSlide,\n snapBack,\n resolveSlide,\n setSlideActive,\n };\n}\n","import { useCallback, useRef } from \"react\";\nimport type { ImageZoomPanState } from \"./useImageZoomPan\";\nimport type { SlideNavigationState } from \"./useSlideNavigation\";\n\ninterface GestureHandlers {\n handlePointerDown: (e: React.PointerEvent) => void;\n handlePointerMove: (e: React.PointerEvent) => void;\n handlePointerUp: (e: React.PointerEvent) => void;\n handleTouchStart: (e: React.TouchEvent) => void;\n handleTouchMove: (e: React.TouchEvent) => void;\n handleTouchEnd: (e: React.TouchEvent) => void;\n}\n\ninterface PanGesture {\n isDragging: boolean;\n pointerStart: { x: number; y: number };\n translateStart: { x: number; y: number };\n pinchStartDist: number | null;\n pinchStartScale: number;\n pinchMidpoint: { x: number; y: number } | null;\n lastTouchPos: { x: number; y: number } | null;\n}\n\ninterface SlideGesture {\n active: boolean;\n startX: number;\n startY: number;\n startTime: number;\n locked: boolean;\n rejected: boolean;\n}\n\n/**\n * Coordinates zoom/pan and slide gestures, routing pointer and touch events\n * to the appropriate behavior based on current zoom state.\n *\n * When zoomed (scale > 1): pointer/touch drags are pans.\n * When unzoomed (scale === 1): pointer/touch drags are slide-to-navigate.\n * Two-finger touch is always a pinch-zoom.\n */\nexport function useGestureHandler(\n zoomPan: ImageZoomPanState,\n slide: SlideNavigationState,\n hasPrev: boolean,\n hasNext: boolean,\n /** When false, pinch-zoom and double-tap-zoom are disabled. Default true. */\n zoomEnabled = true,\n): GestureHandlers {\n const { transformRef, clampTranslate, setTransform, applyTransform, resetTransform } = zoomPan;\n const { applySlideOffset, resolveSlide, snapBack, setSlideActive, swipeOffsetRef } = slide;\n\n const panRef = useRef<PanGesture>({\n isDragging: false,\n pointerStart: { x: 0, y: 0 },\n translateStart: { x: 0, y: 0 },\n pinchStartDist: null,\n pinchStartScale: 1,\n pinchMidpoint: null,\n lastTouchPos: null,\n });\n\n const slideRef = useRef<SlideGesture>({\n active: false,\n startX: 0,\n startY: 0,\n startTime: 0,\n locked: false,\n rejected: false,\n });\n\n // Double-tap detection for touch\n const lastTapRef = useRef<{ time: number; x: number; y: number }>({\n time: 0,\n x: 0,\n y: 0,\n });\n\n // Shared slide start helper\n\n const beginSlide = useCallback(\n (x: number, y: number) => {\n setSlideActive(true);\n const sg = slideRef.current;\n sg.active = true;\n sg.startX = x;\n sg.startY = y;\n sg.startTime = Date.now();\n sg.locked = false;\n sg.rejected = false;\n },\n [setSlideActive],\n );\n\n const updateSlide = useCallback(\n (clientX: number, clientY: number, lockThreshold: number, angleBias: number) => {\n const sg = slideRef.current;\n if (!sg.active || sg.rejected) return;\n\n const dx = clientX - sg.startX;\n const dy = clientY - sg.startY;\n\n if (!sg.locked) {\n const absDx = Math.abs(dx);\n const absDy = Math.abs(dy);\n if (absDx < lockThreshold && absDy < lockThreshold) return;\n if (absDy > absDx * angleBias) {\n sg.rejected = true;\n return;\n }\n sg.locked = true;\n }\n\n let offset = dx;\n if ((offset > 0 && !hasPrev) || (offset < 0 && !hasNext)) {\n offset *= 0.2; // rubber-band resistance at edges\n }\n applySlideOffset(offset);\n },\n [hasPrev, hasNext, applySlideOffset],\n );\n\n const endSlide = useCallback(\n (allowResolve: boolean) => {\n const sg = slideRef.current;\n if (sg.active && sg.locked && !sg.rejected && allowResolve) {\n const startTime = sg.startTime;\n sg.active = false;\n resolveSlide(startTime);\n } else {\n sg.active = false;\n if (swipeOffsetRef.current === 0) setSlideActive(false);\n }\n },\n [resolveSlide, setSlideActive, swipeOffsetRef],\n );\n\n // Pointer (mouse) handlers\n\n const handlePointerDown = useCallback(\n (e: React.PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n\n if (transformRef.current.scale > 1) {\n e.preventDefault();\n const p = panRef.current;\n p.isDragging = true;\n p.pointerStart = { x: e.clientX, y: e.clientY };\n p.translateStart = { x: transformRef.current.x, y: transformRef.current.y };\n } else {\n beginSlide(e.clientX, e.clientY);\n }\n },\n [transformRef, beginSlide],\n );\n\n const handlePointerMove = useCallback(\n (e: React.PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n\n const p = panRef.current;\n if (p.isDragging && transformRef.current.scale > 1) {\n const dx = e.clientX - p.pointerStart.x;\n const dy = e.clientY - p.pointerStart.y;\n const t = transformRef.current;\n const clamped = clampTranslate(p.translateStart.x + dx, p.translateStart.y + dy, t.scale);\n setTransform({ scale: t.scale, ...clamped });\n return;\n }\n\n // Mouse slide: lockThreshold=4, angleBias=1 (45° cutoff)\n updateSlide(e.clientX, e.clientY, 4, 1);\n },\n [transformRef, clampTranslate, setTransform, updateSlide],\n );\n\n const handlePointerUp = useCallback(\n (e: React.PointerEvent) => {\n if (e.pointerType === \"touch\") return;\n panRef.current.isDragging = false;\n endSlide(true);\n },\n [endSlide],\n );\n\n // Touch handlers\n\n const handleTouchStart = useCallback(\n (e: React.TouchEvent) => {\n const p = panRef.current;\n\n if (e.touches.length === 2 && zoomEnabled) {\n // Pinch start\n const dx = e.touches[0].clientX - e.touches[1].clientX;\n const dy = e.touches[0].clientY - e.touches[1].clientY;\n p.pinchStartDist = Math.hypot(dx, dy);\n p.pinchStartScale = transformRef.current.scale;\n p.pinchMidpoint = {\n x: (e.touches[0].clientX + e.touches[1].clientX) / 2,\n y: (e.touches[0].clientY + e.touches[1].clientY) / 2,\n };\n p.lastTouchPos = null;\n\n // Cancel any slide in progress\n if (slideRef.current.active) {\n slideRef.current.active = false;\n snapBack();\n }\n } else if (e.touches.length === 1) {\n if (transformRef.current.scale > 1) {\n p.lastTouchPos = {\n x: e.touches[0].clientX,\n y: e.touches[0].clientY,\n };\n } else {\n beginSlide(e.touches[0].clientX, e.touches[0].clientY);\n }\n }\n },\n [transformRef, snapBack, beginSlide, zoomEnabled],\n );\n\n const handleTouchMove = useCallback(\n (e: React.TouchEvent) => {\n const p = panRef.current;\n\n if (e.touches.length === 2 && p.pinchStartDist !== null) {\n // Pinch zoom\n const dx = e.touches[0].clientX - e.touches[1].clientX;\n const dy = e.touches[0].clientY - e.touches[1].clientY;\n const dist = Math.hypot(dx, dy);\n const ratio = dist / p.pinchStartDist;\n const nextScale = Math.min(5, Math.max(1, p.pinchStartScale * ratio));\n const t = transformRef.current;\n const clamped = nextScale <= 1 ? { x: 0, y: 0 } : clampTranslate(t.x, t.y, nextScale);\n\n const next = { scale: nextScale, ...clamped };\n transformRef.current = next;\n applyTransform(next);\n // Sync display state for UI\n // (We set displayScale indirectly through setTransform would cause extra work,\n // so we just write to transformRef + applyTransform, and let touchEnd sync.)\n } else if (e.touches.length === 1 && p.lastTouchPos && transformRef.current.scale > 1) {\n // Zoomed pan\n const touch = e.touches[0];\n const dx = touch.clientX - p.lastTouchPos.x;\n const dy = touch.clientY - p.lastTouchPos.y;\n p.lastTouchPos = { x: touch.clientX, y: touch.clientY };\n\n const t = transformRef.current;\n const clamped = clampTranslate(t.x + dx, t.y + dy, t.scale);\n const next = { scale: t.scale, ...clamped };\n transformRef.current = next;\n applyTransform(next);\n } else if (e.touches.length === 1) {\n // Touch slide: lockThreshold=6, angleBias=0.8\n const touch = e.touches[0];\n updateSlide(touch.clientX, touch.clientY, 6, 0.8);\n }\n },\n [transformRef, clampTranslate, applyTransform, updateSlide],\n );\n\n const handleTouchEnd = useCallback(\n (e: React.TouchEvent) => {\n const p = panRef.current;\n const wasPinch = p.pinchStartDist !== null;\n p.pinchStartDist = null;\n p.pinchMidpoint = null;\n\n if (e.touches.length === 0 && transformRef.current.scale <= 1) {\n // Resolve slide if active\n const sg = slideRef.current;\n if (sg.active && sg.locked && !sg.rejected) {\n const startTime = sg.startTime;\n sg.active = false;\n resolveSlide(startTime);\n transformRef.current = { scale: 1, x: 0, y: 0 };\n return;\n }\n sg.active = false;\n resetTransform();\n }\n\n // One finger remaining after pinch → start pan from that finger\n if (e.touches.length === 1 && transformRef.current.scale > 1) {\n p.lastTouchPos = {\n x: e.touches[0].clientX,\n y: e.touches[0].clientY,\n };\n } else {\n p.lastTouchPos = null;\n }\n\n // Double-tap detection (single finger, not after pinch, not after slide)\n const sg = slideRef.current;\n if (\n zoomEnabled &&\n e.touches.length === 0 &&\n e.changedTouches.length === 1 &&\n !wasPinch &&\n !sg.locked\n ) {\n const touch = e.changedTouches[0];\n const now = Date.now();\n const last = lastTapRef.current;\n const timeDelta = now - last.time;\n const distDelta = Math.hypot(touch.clientX - last.x, touch.clientY - last.y);\n\n if (timeDelta < 300 && distDelta < 30) {\n lastTapRef.current = { time: 0, x: 0, y: 0 };\n if (transformRef.current.scale > 1) {\n resetTransform();\n } else {\n setTransform({ scale: 2.5, x: 0, y: 0 }, true);\n }\n } else {\n lastTapRef.current = { time: now, x: touch.clientX, y: touch.clientY };\n }\n }\n\n // Cleanup slide if released without committing\n if (e.touches.length === 0 && sg.active && !sg.locked) {\n sg.active = false;\n if (swipeOffsetRef.current === 0) setSlideActive(false);\n }\n },\n [\n transformRef,\n resetTransform,\n setTransform,\n resolveSlide,\n setSlideActive,\n swipeOffsetRef,\n zoomEnabled,\n ],\n );\n\n return {\n handlePointerDown,\n handlePointerMove,\n handlePointerUp,\n handleTouchStart,\n handleTouchMove,\n handleTouchEnd,\n };\n}\n","import { useEffect, useState, type RefObject } from \"react\";\n\n/**\n * Measures the height of the top and bottom bars so the image area\n * can be constrained to fit between them.\n */\nexport function useBarMeasure(\n topBarRef: RefObject<HTMLDivElement | null>,\n bottomBarRef: RefObject<HTMLDivElement | null>,\n /** Re-measure whenever this key changes (e.g. currentIndex) */\n measureKey: unknown,\n) {\n const [topBarH, setTopBarH] = useState(0);\n const [bottomBarH, setBottomBarH] = useState(0);\n\n useEffect(() => {\n const measure = () => {\n if (topBarRef.current) setTopBarH(topBarRef.current.offsetHeight);\n if (bottomBarRef.current) setBottomBarH(bottomBarRef.current.offsetHeight);\n };\n measure();\n\n const ro = new ResizeObserver(measure);\n if (topBarRef.current) ro.observe(topBarRef.current);\n if (bottomBarRef.current) ro.observe(bottomBarRef.current);\n return () => ro.disconnect();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [measureKey]);\n\n return { topBarH, bottomBarH };\n}\n","import { useEffect } from \"react\";\n\n/**\n * Locks scrolling on the document body while `isLocked` is true (e.g. while the\n * lightbox is open), so content behind the overlay can't be scrolled. To\n * prevent the content from shifting when the scrollbar disappears, the width\n * the scrollbar occupied is added back as right padding while locked. Restores\n * the previous overflow/padding values on unlock/unmount, and reference-counts\n * concurrent locks so closing one overlay doesn't release a lock held by\n * another.\n *\n * SSR-safe: the effect only runs in the browser, so importing this on the\n * server is a no-op.\n */\nlet lockCount = 0;\nlet previousOverflow = \"\";\nlet previousPaddingRight = \"\";\n\nexport function useBodyScrollLock(isLocked: boolean): void {\n useEffect(() => {\n if (!isLocked) return;\n if (typeof document === \"undefined\") return;\n\n if (lockCount === 0) {\n const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;\n\n previousOverflow = document.body.style.overflow;\n previousPaddingRight = document.body.style.paddingRight;\n\n document.body.style.overflow = \"hidden\";\n if (scrollbarWidth > 0) {\n const currentPaddingRight =\n parseFloat(window.getComputedStyle(document.body).paddingRight) || 0;\n document.body.style.paddingRight = `${currentPaddingRight + scrollbarWidth}px`;\n }\n }\n lockCount += 1;\n\n return () => {\n lockCount -= 1;\n if (lockCount === 0) {\n document.body.style.overflow = previousOverflow;\n document.body.style.paddingRight = previousPaddingRight;\n }\n };\n }, [isLocked]);\n}\n","import { useEffect, type RefObject } from \"react\";\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Traps Tab focus within `containerRef` while `active`, and restores focus to\n * the previously-focused element on deactivate/unmount. SSR-safe (effect only\n * runs in the browser).\n */\nexport function useFocusTrap(containerRef: RefObject<HTMLElement | null>, active: boolean): void {\n useEffect(() => {\n if (!active) return;\n if (typeof document === \"undefined\") return;\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n const container = containerRef.current;\n\n // Move focus into the dialog so screen readers/keyboard land inside it.\n container?.focus();\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key !== \"Tab\" || !container) return;\n const focusable = Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE)).filter(\n (el) => el.offsetParent !== null || el === document.activeElement,\n );\n if (focusable.length === 0) {\n e.preventDefault();\n container.focus();\n return;\n }\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const activeEl = document.activeElement;\n\n if (e.shiftKey && (activeEl === first || activeEl === container)) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && activeEl === last) {\n e.preventDefault();\n first.focus();\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n previouslyFocused?.focus?.();\n };\n }, [containerRef, active]);\n}\n","import type { SVGProps } from \"react\";\nimport type { ViewerIcons } from \"../types\";\n\n// Inline default icons so the package has no runtime icon dependency (e.g.\n// lucide-react). Each is a 24x24 stroked glyph that inherits `currentColor`.\n// Consumers can override any of them via the `icons` prop.\n\nfunction base(props: SVGProps<SVGSVGElement>) {\n return {\n width: 18,\n height: 18,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 1.75,\n strokeLinecap: \"round\" as const,\n strokeLinejoin: \"round\" as const,\n \"aria-hidden\": true,\n focusable: false,\n ...props,\n };\n}\n\nexport function CloseIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg {...base(props)}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\nexport function ZoomInIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg {...base(props)}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n <path d=\"M11 8v6\" />\n <path d=\"M8 11h6\" />\n </svg>\n );\n}\n\nexport function ZoomOutIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg {...base(props)}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n <path d=\"M8 11h6\" />\n </svg>\n );\n}\n\nexport function ChevronLeftIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg {...base({ width: 36, height: 36, strokeWidth: 1.5, ...props })}>\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n );\n}\n\nexport function ChevronRightIcon(props: SVGProps<SVGSVGElement>) {\n return (\n <svg {...base({ width: 36, height: 36, strokeWidth: 1.5, ...props })}>\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n );\n}\n\n/** The full default icon set, merged with any consumer overrides. */\nexport const defaultIcons: ViewerIcons = {\n close: <CloseIcon />,\n zoomIn: <ZoomInIcon />,\n zoomOut: <ZoomOutIcon />,\n prev: <ChevronLeftIcon />,\n next: <ChevronRightIcon />,\n};\n","/** Tiny className joiner: drops falsy values and joins with a space. */\nexport function cx(...parts: Array<string | false | null | undefined>): string {\n return parts.filter(Boolean).join(\" \");\n}\n","import type { ReactNode } from \"react\";\nimport { cx } from \"./cx\";\n\ninterface NavButtonProps {\n direction: \"prev\" | \"next\";\n enabled: boolean;\n onClick: () => void;\n icon: ReactNode;\n className?: string;\n}\n\nexport function NavButton({ direction, enabled, onClick, icon, className }: NavButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n if (enabled) onClick();\n }}\n disabled={!enabled}\n className={cx(\"rvl-nav-btn\", className)}\n aria-label={direction === \"prev\" ? \"Previous image\" : \"Next image\"}\n >\n {icon}\n </button>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ImageViewerProps, ViewerContext } from \"../types\";\nimport { useImageZoomPan, MIN_SCALE, MAX_SCALE } from \"../hooks/useImageZoomPan\";\nimport { useSlideNavigation } from \"../hooks/useSlideNavigation\";\nimport { useGestureHandler } from \"../hooks/useGestureHandler\";\nimport { useBarMeasure } from \"../hooks/useBarMeasure\";\nimport { useBodyScrollLock } from \"../hooks/useBodyScrollLock\";\nimport { useFocusTrap } from \"../hooks/useFocusTrap\";\nimport { defaultIcons } from \"./icons\";\nimport { NavButton } from \"./NavButton\";\nimport { cx } from \"./cx\";\n\nconst ANIM_MS = 250;\nconst IMG_PADDING = 44;\n\nfunction prefersReducedMotion(): boolean {\n if (typeof window === \"undefined\" || !window.matchMedia) return false;\n return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * Batteries-included fullscreen image viewer: zoom, pan, pinch, and swipe\n * navigation with themeable chrome and render slots. Controlled via `index` /\n * `onIndexChange`; mount it when open and it runs its own enter/exit animation,\n * calling `onClose` after the exit completes.\n */\nexport function ImageViewer<TData = unknown>({\n items,\n index,\n onIndexChange,\n onNavigate,\n onClose,\n zoom = true,\n showCounter = true,\n loop = false,\n renderHeader,\n renderHeaderActions,\n renderNavStart,\n renderNavEnd,\n renderFooter,\n renderOverlay,\n classNames,\n icons,\n ariaLabel,\n}: ImageViewerProps<TData>) {\n const [visible, setVisible] = useState(false);\n const [closing, setClosing] = useState(false);\n const [isTouchDevice, setIsTouchDevice] = useState(false);\n const [contentShift, setContentShiftState] = useState<{\n transform: string | null;\n animate: boolean;\n }>({ transform: null, animate: true });\n\n const containerRef = useRef<HTMLDivElement>(null);\n const imgWrapperRef = useRef<HTMLDivElement>(null);\n const topBarRef = useRef<HTMLDivElement>(null);\n const bottomBarRef = useRef<HTMLDivElement>(null);\n\n const [viewportWidth, setViewportWidth] = useState(() =>\n typeof window === \"undefined\" ? 0 : window.innerWidth,\n );\n\n const item = items[index];\n\n const hasPrevLinear = index > 0;\n const hasNextLinear = index < items.length - 1;\n const hasPrev = loop ? items.length > 1 : hasPrevLinear;\n const hasNext = loop ? items.length > 1 : hasNextLinear;\n\n const mergedIcons = useMemo(() => ({ ...defaultIcons, ...icons }), [icons]);\n const cn = (slot: keyof NonNullable<ImageViewerProps[\"classNames\"]>) => classNames?.[slot];\n\n useBodyScrollLock(true);\n useFocusTrap(containerRef, visible && !closing);\n const { topBarH, bottomBarH } = useBarMeasure(topBarRef, bottomBarRef, index);\n\n const zoomPan = useImageZoomPan(imgWrapperRef, index, zoom);\n const {\n imgRef,\n displayScale,\n isZoomed,\n transformRef,\n resetTransform,\n setTransform,\n clampTranslate,\n measureBaseDims,\n handleDoubleClick,\n } = zoomPan;\n\n const slide = useSlideNavigation(items, index, onIndexChange, onNavigate);\n const { slideTrackRef, slideActive, slideAnimating, swipeOffset, commitSlide } = slide;\n\n const gestures = useGestureHandler(zoomPan, slide, hasPrevLinear, hasNextLinear, zoom);\n\n useEffect(() => {\n setIsTouchDevice(\"ontouchstart\" in window || navigator.maxTouchPoints > 0);\n }, []);\n\n useEffect(() => {\n const onResize = () => setViewportWidth(window.innerWidth);\n window.addEventListener(\"resize\", onResize);\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n useEffect(() => {\n const raf = requestAnimationFrame(() => setVisible(true));\n return () => cancelAnimationFrame(raf);\n }, []);\n\n const handleClose = useCallback(() => {\n setClosing(true);\n setVisible(false);\n const delay = prefersReducedMotion() ? 0 : ANIM_MS;\n setTimeout(onClose, delay);\n }, [onClose]);\n\n const navigate = useCallback(\n (dir: \"prev\" | \"next\") => {\n if (dir === \"prev\") {\n if (hasPrevLinear) commitSlide(\"prev\");\n else if (loop) {\n // Wrap jumps without a slide animation, so commitSlide's onSlideStart\n // never fires — emit it here so overlays still react.\n onNavigate?.(\"prev\");\n onIndexChange(items.length - 1);\n }\n } else {\n if (hasNextLinear) commitSlide(\"next\");\n else if (loop) {\n onNavigate?.(\"next\");\n onIndexChange(0);\n }\n }\n },\n [hasPrevLinear, hasNextLinear, loop, commitSlide, onIndexChange, onNavigate, items.length],\n );\n\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n handleClose();\n return;\n }\n if (displayScale > 1) return;\n if (e.key === \"ArrowLeft\" && hasPrev) navigate(\"prev\");\n if (e.key === \"ArrowRight\" && hasNext) navigate(\"next\");\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [handleClose, hasPrev, hasNext, displayScale, navigate]);\n\n const setContentShift = useCallback((transform: string | null, animate = true) => {\n setContentShiftState({ transform, animate });\n }, []);\n\n const ctx: ViewerContext<TData> = {\n items,\n index,\n item: item!,\n total: items.length,\n hasPrev,\n hasNext,\n goPrev: () => navigate(\"prev\"),\n goNext: () => navigate(\"next\"),\n goTo: (i: number) => {\n if (i !== index && i >= 0 && i < items.length) onIndexChange(i);\n },\n close: handleClose,\n isZoomed,\n displayScale,\n zoomIn: () => {\n const t = transformRef.current;\n const next = Math.min(MAX_SCALE, t.scale * 1.3);\n const clamped = clampTranslate(t.x, t.y, next);\n setTransform({ scale: next, ...clamped }, true);\n },\n zoomOut: () => {\n const t = transformRef.current;\n const next = Math.max(MIN_SCALE, t.scale / 1.3);\n const clamped = next <= 1 ? { x: 0, y: 0 } : clampTranslate(t.x, t.y, next);\n setTransform({ scale: next, ...clamped }, true);\n },\n resetZoom: resetTransform,\n isTouchDevice,\n topBarHeight: topBarH,\n bottomBarHeight: bottomBarH,\n setContentShift,\n };\n\n if (!item) return null;\n\n const reservedH = bottomBarH + IMG_PADDING * 2;\n const imgMaxHeight = `calc(100vh - ${reservedH}px)`;\n const imgStyle: React.CSSProperties = { maxHeight: imgMaxHeight };\n\n const totalDigits = String(items.length).length;\n const counterMinWidth = `${totalDigits * 2 * 0.6 + 1.5}em`;\n\n const prevItem = hasPrevLinear ? items[index - 1] : null;\n const nextItem = hasNextLinear ? items[index + 1] : null;\n const showAdjacent = slideActive || slideAnimating || swipeOffset !== 0;\n const adjacentOpacity = Math.min(1, Math.abs(swipeOffset) / (viewportWidth * 0.8 || 1));\n\n const showZoomControls = zoom && !isTouchDevice;\n const headerActions = renderHeaderActions?.(ctx);\n const navStart = renderNavStart?.(ctx);\n const navEnd = renderNavEnd?.(ctx);\n const hasNavGroup = hasPrev || hasNext;\n const showNavRow = !isZoomed && (hasNavGroup || navStart != null || navEnd != null);\n\n return (\n <div\n ref={containerRef}\n className={cx(\"rvl-root\", visible && !closing && \"rvl-visible\", cn(\"root\"))}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={ariaLabel ?? item.alt ?? \"Image viewer\"}\n tabIndex={-1}\n >\n <div\n className={cx(\"rvl-backdrop\", cn(\"backdrop\"))}\n onClick={handleClose}\n aria-hidden=\"true\"\n />\n\n <div ref={topBarRef} className={cx(\"rvl-bar\", \"rvl-top-bar\", cn(\"topBar\"))}>\n <div className={cx(\"rvl-header\", cn(\"topBar\"))}>{renderHeader?.(ctx)}</div>\n\n <div className=\"rvl-header-actions\">\n {headerActions}\n\n {showZoomControls && isZoomed && (\n <button\n type=\"button\"\n className={cx(\"rvl-btn\", \"rvl-btn-scale\", cn(\"button\"))}\n onClick={(e) => {\n e.stopPropagation();\n resetTransform();\n }}\n title=\"Reset zoom\"\n aria-label=\"Reset zoom\"\n >\n {Math.round(displayScale * 100)}%\n </button>\n )}\n\n {showZoomControls && (\n <button\n type=\"button\"\n className={cx(\"rvl-btn\", cn(\"button\"))}\n onClick={(e) => {\n e.stopPropagation();\n ctx.zoomIn();\n }}\n title=\"Zoom in\"\n aria-label=\"Zoom in\"\n >\n {mergedIcons.zoomIn}\n </button>\n )}\n\n {showZoomControls && (\n <button\n type=\"button\"\n className={cx(\"rvl-btn\", cn(\"button\"))}\n onClick={(e) => {\n e.stopPropagation();\n ctx.zoomOut();\n }}\n title=\"Zoom out\"\n aria-label=\"Zoom out\"\n >\n {mergedIcons.zoomOut}\n </button>\n )}\n\n <button\n type=\"button\"\n className={cx(\"rvl-btn\", cn(\"button\"))}\n onClick={(e) => {\n e.stopPropagation();\n handleClose();\n }}\n title=\"Close (Esc)\"\n aria-label=\"Close\"\n >\n {mergedIcons.close}\n </button>\n </div>\n </div>\n\n <div\n className=\"rvl-stage\"\n style={{\n transform: contentShift.transform ?? \"translateY(0)\",\n // animate=false snaps with no transition (overrides the CSS transition)\n transition: contentShift.animate ? undefined : \"none\",\n }}\n >\n <div\n ref={slideTrackRef}\n className={cx(\"rvl-track\", visible && !closing && \"rvl-track-visible\")}\n >\n {showAdjacent && prevItem && (\n <div\n className=\"rvl-adjacent\"\n style={{ transform: `translateX(-${viewportWidth}px)`, opacity: adjacentOpacity }}\n >\n <img\n src={prevItem.src}\n alt=\"\"\n className={cx(\"rvl-img\", cn(\"image\"))}\n style={imgStyle}\n draggable={false}\n />\n </div>\n )}\n\n <div\n ref={imgWrapperRef}\n className=\"rvl-img-wrapper\"\n onClick={(e) => e.stopPropagation()}\n onDoubleClick={handleDoubleClick}\n onPointerDown={gestures.handlePointerDown}\n onPointerMove={gestures.handlePointerMove}\n onPointerUp={gestures.handlePointerUp}\n onPointerLeave={gestures.handlePointerUp}\n onTouchStart={gestures.handleTouchStart}\n onTouchMove={gestures.handleTouchMove}\n onTouchEnd={gestures.handleTouchEnd}\n >\n <img\n ref={imgRef}\n src={item.src}\n alt={item.alt ?? \"\"}\n className={cx(\"rvl-img\", cn(\"image\"))}\n style={imgStyle}\n draggable={false}\n onLoad={measureBaseDims}\n />\n </div>\n\n {showAdjacent && nextItem && (\n <div\n className=\"rvl-adjacent\"\n style={{ transform: `translateX(${viewportWidth}px)`, opacity: adjacentOpacity }}\n >\n <img\n src={nextItem.src}\n alt=\"\"\n className={cx(\"rvl-img\", cn(\"image\"))}\n style={imgStyle}\n draggable={false}\n />\n </div>\n )}\n </div>\n </div>\n\n <div ref={bottomBarRef} className={cx(\"rvl-bar\", \"rvl-bottom-bar\", cn(\"bottomBar\"))}>\n {showNavRow && (\n <div className=\"rvl-nav-row\">\n <div className=\"rvl-nav-inner\">\n {navStart != null && (\n <div className={cx(\"rvl-nav-start\", cn(\"navStart\"))}>{navStart}</div>\n )}\n {hasNavGroup && (\n <div className=\"rvl-nav-group\">\n <NavButton\n direction=\"prev\"\n enabled={hasPrev}\n onClick={() => navigate(\"prev\")}\n icon={mergedIcons.prev}\n className={cn(\"navButton\")}\n />\n {showCounter && (\n <span\n className={cx(\"rvl-counter\", cn(\"counter\"))}\n style={{ minWidth: counterMinWidth }}\n >\n {index + 1} / {items.length}\n </span>\n )}\n <NavButton\n direction=\"next\"\n enabled={hasNext}\n onClick={() => navigate(\"next\")}\n icon={mergedIcons.next}\n className={cn(\"navButton\")}\n />\n </div>\n )}\n {navEnd != null && <div className={cx(\"rvl-nav-end\", cn(\"navEnd\"))}>{navEnd}</div>}\n </div>\n </div>\n )}\n\n {renderFooter && <div className=\"rvl-footer\">{renderFooter(ctx)}</div>}\n </div>\n\n {renderOverlay && (\n <div className={cx(\"rvl-overlay\", cn(\"overlay\"))}>{renderOverlay(ctx)}</div>\n )}\n </div>\n );\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, SVGProps, RefObject } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A single image in the viewer. Neutral replacement for app-specific item
|
|
6
|
+
* types (e.g. `Panel` / `Organism`).
|
|
7
|
+
*
|
|
8
|
+
* `TData` is an optional per-slide payload (caption, credit, links, anything).
|
|
9
|
+
* It travels with the item and is surfaced as `ctx.item.data` in every render
|
|
10
|
+
* slot, so details stay paired with their image without a parallel lookup.
|
|
11
|
+
*/
|
|
12
|
+
interface ViewerItem<TData = unknown> {
|
|
13
|
+
id: string;
|
|
14
|
+
/** FINAL url — the consumer resolves any base path before passing it in. */
|
|
15
|
+
src: string;
|
|
16
|
+
alt?: string;
|
|
17
|
+
/** Optional thumbnail url; falls back to `src`. */
|
|
18
|
+
thumbnail?: string;
|
|
19
|
+
/** Arbitrary per-slide payload, surfaced as `ctx.item.data` in render slots. */
|
|
20
|
+
data?: TData;
|
|
21
|
+
}
|
|
22
|
+
/** Named, themeable regions of the viewer that accept a `className` override. */
|
|
23
|
+
type ViewerSlot = "root" | "backdrop" | "topBar" | "bottomBar" | "image" | "button" | "counter" | "navButton" | "navStart" | "navEnd" | "overlay";
|
|
24
|
+
/** Overridable control icons. Each is a React node rendered inside its button. */
|
|
25
|
+
interface ViewerIcons {
|
|
26
|
+
close: ReactNode;
|
|
27
|
+
zoomIn: ReactNode;
|
|
28
|
+
zoomOut: ReactNode;
|
|
29
|
+
prev: ReactNode;
|
|
30
|
+
next: ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Context object handed to every render slot. Exposes navigation, zoom, and
|
|
34
|
+
* layout-measurement state so slot content (info drawers, graphs, custom
|
|
35
|
+
* headers) can coordinate with the viewer.
|
|
36
|
+
*/
|
|
37
|
+
interface ViewerContext<TData = unknown> {
|
|
38
|
+
items: ViewerItem<TData>[];
|
|
39
|
+
index: number;
|
|
40
|
+
item: ViewerItem<TData>;
|
|
41
|
+
total: number;
|
|
42
|
+
hasPrev: boolean;
|
|
43
|
+
hasNext: boolean;
|
|
44
|
+
goPrev: () => void;
|
|
45
|
+
goNext: () => void;
|
|
46
|
+
goTo: (index: number) => void;
|
|
47
|
+
close: () => void;
|
|
48
|
+
isZoomed: boolean;
|
|
49
|
+
displayScale: number;
|
|
50
|
+
zoomIn: () => void;
|
|
51
|
+
zoomOut: () => void;
|
|
52
|
+
resetZoom: () => void;
|
|
53
|
+
isTouchDevice: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Measured bar heights so overlays (info drawers) can size themselves
|
|
56
|
+
* between the bars.
|
|
57
|
+
*/
|
|
58
|
+
topBarHeight: number;
|
|
59
|
+
bottomBarHeight: number;
|
|
60
|
+
/**
|
|
61
|
+
* Lets an overlay push the image track up/down (drawer-open behavior).
|
|
62
|
+
* Pass a CSS transform string, or `null` to reset. `animate` defaults to
|
|
63
|
+
* `true`; pass `false` to apply the shift instantly with no transition — e.g.
|
|
64
|
+
* to snap the image back to center on navigation so it slides in horizontally
|
|
65
|
+
* instead of dropping down.
|
|
66
|
+
*/
|
|
67
|
+
setContentShift: (transform: string | null, animate?: boolean) => void;
|
|
68
|
+
}
|
|
69
|
+
interface ImageViewerProps<TData = unknown> {
|
|
70
|
+
items: ViewerItem<TData>[];
|
|
71
|
+
/** Controlled index of the active item. */
|
|
72
|
+
index: number;
|
|
73
|
+
onIndexChange: (index: number) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Fired when a slide STARTS (button, key, or swipe), before the animation and
|
|
76
|
+
* the resulting `onIndexChange`. Lets overlays (e.g. an info drawer) animate
|
|
77
|
+
* out in sync with the image. `direction` is the swipe direction.
|
|
78
|
+
*/
|
|
79
|
+
onNavigate?: (direction: "prev" | "next") => void;
|
|
80
|
+
/** Called AFTER the exit animation completes. */
|
|
81
|
+
onClose: () => void;
|
|
82
|
+
/** Enable zoom/pan (wheel, pinch, double-tap). Default `true`. */
|
|
83
|
+
zoom?: boolean;
|
|
84
|
+
/** Show the `index / total` counter. Default `true`. */
|
|
85
|
+
showCounter?: boolean;
|
|
86
|
+
/** Wrap around at the ends. Default `false`. */
|
|
87
|
+
loop?: boolean;
|
|
88
|
+
/** Top-left title area. */
|
|
89
|
+
renderHeader?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
90
|
+
/** Extra top-right buttons, rendered before the close button. */
|
|
91
|
+
renderHeaderActions?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
92
|
+
/**
|
|
93
|
+
* Pinned to the LEFT edge of the nav row, vertically centered alongside the
|
|
94
|
+
* prev/counter/next group (which stays optically centered). Ideal for an
|
|
95
|
+
* info/details toggle that should not cost an extra row of vertical space.
|
|
96
|
+
*/
|
|
97
|
+
renderNavStart?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
98
|
+
/** Pinned to the RIGHT edge of the nav row; mirror of `renderNavStart`. */
|
|
99
|
+
renderNavEnd?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
100
|
+
/** Content below the nav row. */
|
|
101
|
+
renderFooter?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
102
|
+
/** Drawers/graphs layered over the image. */
|
|
103
|
+
renderOverlay?: (ctx: ViewerContext<TData>) => ReactNode;
|
|
104
|
+
classNames?: Partial<Record<ViewerSlot, string>>;
|
|
105
|
+
icons?: Partial<ViewerIcons>;
|
|
106
|
+
ariaLabel?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Batteries-included fullscreen image viewer: zoom, pan, pinch, and swipe
|
|
111
|
+
* navigation with themeable chrome and render slots. Controlled via `index` /
|
|
112
|
+
* `onIndexChange`; mount it when open and it runs its own enter/exit animation,
|
|
113
|
+
* calling `onClose` after the exit completes.
|
|
114
|
+
*/
|
|
115
|
+
declare function ImageViewer<TData = unknown>({ items, index, onIndexChange, onNavigate, onClose, zoom, showCounter, loop, renderHeader, renderHeaderActions, renderNavStart, renderNavEnd, renderFooter, renderOverlay, classNames, icons, ariaLabel, }: ImageViewerProps<TData>): react.JSX.Element | null;
|
|
116
|
+
|
|
117
|
+
interface NavButtonProps {
|
|
118
|
+
direction: "prev" | "next";
|
|
119
|
+
enabled: boolean;
|
|
120
|
+
onClick: () => void;
|
|
121
|
+
icon: ReactNode;
|
|
122
|
+
className?: string;
|
|
123
|
+
}
|
|
124
|
+
declare function NavButton({ direction, enabled, onClick, icon, className }: NavButtonProps): react.JSX.Element;
|
|
125
|
+
|
|
126
|
+
declare function CloseIcon(props: SVGProps<SVGSVGElement>): react.JSX.Element;
|
|
127
|
+
declare function ZoomInIcon(props: SVGProps<SVGSVGElement>): react.JSX.Element;
|
|
128
|
+
declare function ZoomOutIcon(props: SVGProps<SVGSVGElement>): react.JSX.Element;
|
|
129
|
+
declare function ChevronLeftIcon(props: SVGProps<SVGSVGElement>): react.JSX.Element;
|
|
130
|
+
declare function ChevronRightIcon(props: SVGProps<SVGSVGElement>): react.JSX.Element;
|
|
131
|
+
/** The full default icon set, merged with any consumer overrides. */
|
|
132
|
+
declare const defaultIcons: ViewerIcons;
|
|
133
|
+
|
|
134
|
+
declare const MIN_SCALE = 1;
|
|
135
|
+
declare const MAX_SCALE = 5;
|
|
136
|
+
interface ImageTransform {
|
|
137
|
+
scale: number;
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
}
|
|
141
|
+
interface ImageZoomPanState {
|
|
142
|
+
imgRef: RefObject<HTMLImageElement | null>;
|
|
143
|
+
displayScale: number;
|
|
144
|
+
isZoomed: boolean;
|
|
145
|
+
transformRef: React.MutableRefObject<ImageTransform>;
|
|
146
|
+
/** Base (unscaled) image dimensions for clamp calculations */
|
|
147
|
+
baseDimsRef: React.MutableRefObject<{
|
|
148
|
+
width: number;
|
|
149
|
+
height: number;
|
|
150
|
+
}>;
|
|
151
|
+
resetTransform: () => void;
|
|
152
|
+
setTransform: (t: ImageTransform, animate?: boolean) => void;
|
|
153
|
+
applyTransform: (t: ImageTransform, animate?: boolean) => void;
|
|
154
|
+
clampTranslate: (x: number, y: number, scale: number) => {
|
|
155
|
+
x: number;
|
|
156
|
+
y: number;
|
|
157
|
+
};
|
|
158
|
+
measureBaseDims: () => void;
|
|
159
|
+
handleDoubleClick: (e: React.MouseEvent) => void;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Manages zoom/pan state for an image viewer.
|
|
163
|
+
*
|
|
164
|
+
* Applies scale + translate transforms to the **wrapper** element rather
|
|
165
|
+
* than the image itself. This avoids an iOS Safari compositing bug where
|
|
166
|
+
* CSS scale() on an element clips its painted output to the element's
|
|
167
|
+
* original layout bounds.
|
|
168
|
+
*
|
|
169
|
+
* When zoomed, the wrapper is positioned absolute inset-0 (full viewport),
|
|
170
|
+
* so its layout bounds already match the viewport and scaling it won't clip.
|
|
171
|
+
* The image stays at its natural constrained size, centered via flexbox.
|
|
172
|
+
*/
|
|
173
|
+
declare function useImageZoomPan(imgWrapperRef: RefObject<HTMLDivElement | null>, currentIndex: number,
|
|
174
|
+
/** When false, wheel-zoom and double-click-zoom are disabled. Default true. */
|
|
175
|
+
enabled?: boolean): ImageZoomPanState;
|
|
176
|
+
|
|
177
|
+
interface SlideNavigationState {
|
|
178
|
+
slideTrackRef: React.RefObject<HTMLDivElement | null>;
|
|
179
|
+
slideActive: boolean;
|
|
180
|
+
slideAnimating: boolean;
|
|
181
|
+
swipeOffset: number;
|
|
182
|
+
swipeOffsetRef: React.MutableRefObject<number>;
|
|
183
|
+
commitLockRef: React.MutableRefObject<boolean>;
|
|
184
|
+
applySlideOffset: (offset: number, animate?: boolean) => void;
|
|
185
|
+
commitSlide: (direction: "prev" | "next") => void;
|
|
186
|
+
snapBack: () => void;
|
|
187
|
+
resolveSlide: (gestureStartTime: number) => void;
|
|
188
|
+
setSlideActive: React.Dispatch<React.SetStateAction<boolean>>;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Manages the three-slot slide carousel: swipe offset tracking, animated
|
|
192
|
+
* commit/snap-back, and DOM resets on navigation.
|
|
193
|
+
*
|
|
194
|
+
* `items[i].src` is treated as a final, ready-to-load url — the next image is
|
|
195
|
+
* preloaded/decoded before navigation commits so the swipe lands on a painted
|
|
196
|
+
* frame.
|
|
197
|
+
*/
|
|
198
|
+
declare function useSlideNavigation(items: ViewerItem[], currentIndex: number, onNavigate: (index: number) => void, onSlideStart?: (direction: "prev" | "next") => void): SlideNavigationState;
|
|
199
|
+
|
|
200
|
+
interface GestureHandlers {
|
|
201
|
+
handlePointerDown: (e: React.PointerEvent) => void;
|
|
202
|
+
handlePointerMove: (e: React.PointerEvent) => void;
|
|
203
|
+
handlePointerUp: (e: React.PointerEvent) => void;
|
|
204
|
+
handleTouchStart: (e: React.TouchEvent) => void;
|
|
205
|
+
handleTouchMove: (e: React.TouchEvent) => void;
|
|
206
|
+
handleTouchEnd: (e: React.TouchEvent) => void;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Coordinates zoom/pan and slide gestures, routing pointer and touch events
|
|
210
|
+
* to the appropriate behavior based on current zoom state.
|
|
211
|
+
*
|
|
212
|
+
* When zoomed (scale > 1): pointer/touch drags are pans.
|
|
213
|
+
* When unzoomed (scale === 1): pointer/touch drags are slide-to-navigate.
|
|
214
|
+
* Two-finger touch is always a pinch-zoom.
|
|
215
|
+
*/
|
|
216
|
+
declare function useGestureHandler(zoomPan: ImageZoomPanState, slide: SlideNavigationState, hasPrev: boolean, hasNext: boolean,
|
|
217
|
+
/** When false, pinch-zoom and double-tap-zoom are disabled. Default true. */
|
|
218
|
+
zoomEnabled?: boolean): GestureHandlers;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Measures the height of the top and bottom bars so the image area
|
|
222
|
+
* can be constrained to fit between them.
|
|
223
|
+
*/
|
|
224
|
+
declare function useBarMeasure(topBarRef: RefObject<HTMLDivElement | null>, bottomBarRef: RefObject<HTMLDivElement | null>,
|
|
225
|
+
/** Re-measure whenever this key changes (e.g. currentIndex) */
|
|
226
|
+
measureKey: unknown): {
|
|
227
|
+
topBarH: number;
|
|
228
|
+
bottomBarH: number;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
declare function useBodyScrollLock(isLocked: boolean): void;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Pure geometry/threshold helpers shared by the interaction hooks. Kept free of
|
|
235
|
+
* React and DOM globals so they can be unit-tested in isolation.
|
|
236
|
+
*/
|
|
237
|
+
interface Dims {
|
|
238
|
+
width: number;
|
|
239
|
+
height: number;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Clamp a pan translation so the scaled image edge can't move past the
|
|
243
|
+
* viewport edge. Returns `{ x: 0, y: 0 }` when not zoomed or when base
|
|
244
|
+
* dimensions are unknown.
|
|
245
|
+
*/
|
|
246
|
+
declare function clampTranslate(x: number, y: number, scale: number, baseDims: Dims, viewport: Dims): {
|
|
247
|
+
x: number;
|
|
248
|
+
y: number;
|
|
249
|
+
};
|
|
250
|
+
type SlideAction = "prev" | "next" | "snap";
|
|
251
|
+
interface ResolveSlideArgs {
|
|
252
|
+
/** Current horizontal swipe offset in px (positive = dragged right). */
|
|
253
|
+
offset: number;
|
|
254
|
+
/** Elapsed time of the gesture in ms (used for fling velocity). */
|
|
255
|
+
elapsedMs: number;
|
|
256
|
+
viewportWidth: number;
|
|
257
|
+
hasPrev: boolean;
|
|
258
|
+
hasNext: boolean;
|
|
259
|
+
/** Fraction of viewport width past which a drag commits. Default 0.25. */
|
|
260
|
+
distanceThreshold?: number;
|
|
261
|
+
/** px/ms past which a fast fling commits regardless of distance. Default 0.4. */
|
|
262
|
+
velocityThreshold?: number;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Decide whether a released swipe should navigate `prev`/`next` or `snap` back,
|
|
266
|
+
* based on distance and fling velocity.
|
|
267
|
+
*/
|
|
268
|
+
declare function resolveSlideDirection({ offset, elapsedMs, viewportWidth, hasPrev, hasNext, distanceThreshold, velocityThreshold, }: ResolveSlideArgs): SlideAction;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Traps Tab focus within `containerRef` while `active`, and restores focus to
|
|
272
|
+
* the previously-focused element on deactivate/unmount. SSR-safe (effect only
|
|
273
|
+
* runs in the browser).
|
|
274
|
+
*/
|
|
275
|
+
declare function useFocusTrap(containerRef: RefObject<HTMLElement | null>, active: boolean): void;
|
|
276
|
+
|
|
277
|
+
export { ChevronLeftIcon, ChevronRightIcon, CloseIcon, type Dims, type ImageTransform, ImageViewer, type ImageViewerProps, type ImageZoomPanState, MAX_SCALE, MIN_SCALE, NavButton, type ResolveSlideArgs, type SlideAction, type SlideNavigationState, type ViewerContext, type ViewerIcons, type ViewerItem, type ViewerSlot, ZoomInIcon, ZoomOutIcon, clampTranslate, defaultIcons, resolveSlideDirection, useBarMeasure, useBodyScrollLock, useFocusTrap, useGestureHandler, useImageZoomPan, useSlideNavigation };
|