@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":["clampTranslate","useRef","useState","useCallback","useLayoutEffect","useEffect","sg","jsx","jsxs"],"mappings":"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","file":"index.js","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/styles.css
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @jekrch/react-viewport-lightbox styles.
|
|
3
|
+
*
|
|
4
|
+
* No Tailwind / framework dependency — plain CSS keyed off stable `rvl-`
|
|
5
|
+
* class names. Theme by overriding the custom properties below, or pass
|
|
6
|
+
* per-slot `className`s via the `classNames` prop for finer control.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
--rvl-accent: #4c538d;
|
|
11
|
+
--rvl-overlay-bg: rgba(0, 0, 0, 0.9);
|
|
12
|
+
--rvl-bar-fg: rgba(255, 255, 255, 0.9);
|
|
13
|
+
--rvl-bar-fg-muted: rgba(255, 255, 255, 0.5);
|
|
14
|
+
--rvl-bar-fg-faint: rgba(255, 255, 255, 0.3);
|
|
15
|
+
--rvl-btn-bg: rgba(255, 255, 255, 0.1);
|
|
16
|
+
--rvl-btn-bg-hover: rgba(255, 255, 255, 0.2);
|
|
17
|
+
--rvl-radius: 4px;
|
|
18
|
+
--rvl-anim-duration: 250ms;
|
|
19
|
+
--rvl-z: 50;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Root overlay -------------------------------------------------------------- */
|
|
23
|
+
|
|
24
|
+
.rvl-root {
|
|
25
|
+
position: fixed;
|
|
26
|
+
inset: 0;
|
|
27
|
+
z-index: var(--rvl-z);
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
touch-action: none;
|
|
32
|
+
background-color: rgba(0, 0, 0, 0);
|
|
33
|
+
transition: background-color var(--rvl-anim-duration) ease-out;
|
|
34
|
+
-webkit-tap-highlight-color: transparent;
|
|
35
|
+
}
|
|
36
|
+
.rvl-root.rvl-visible {
|
|
37
|
+
background-color: var(--rvl-overlay-bg);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.rvl-backdrop {
|
|
41
|
+
position: absolute;
|
|
42
|
+
inset: 0;
|
|
43
|
+
z-index: 0;
|
|
44
|
+
backdrop-filter: blur(0);
|
|
45
|
+
transition: backdrop-filter var(--rvl-anim-duration) ease-out;
|
|
46
|
+
}
|
|
47
|
+
.rvl-root.rvl-visible .rvl-backdrop {
|
|
48
|
+
backdrop-filter: blur(4px);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Bars ---------------------------------------------------------------------- */
|
|
52
|
+
|
|
53
|
+
.rvl-bar {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inset-inline: 0;
|
|
56
|
+
z-index: 20;
|
|
57
|
+
pointer-events: none;
|
|
58
|
+
transition:
|
|
59
|
+
opacity var(--rvl-anim-duration) ease-out,
|
|
60
|
+
transform var(--rvl-anim-duration) ease-out;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.rvl-top-bar {
|
|
64
|
+
top: 0;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: flex-start;
|
|
67
|
+
justify-content: space-between;
|
|
68
|
+
gap: 0.75rem;
|
|
69
|
+
padding: 0.75rem 1rem;
|
|
70
|
+
padding-top: max(0.75rem, env(safe-area-inset-top));
|
|
71
|
+
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0));
|
|
72
|
+
opacity: 0;
|
|
73
|
+
transform: translateY(-0.75rem);
|
|
74
|
+
}
|
|
75
|
+
.rvl-bottom-bar {
|
|
76
|
+
bottom: 0;
|
|
77
|
+
padding-top: 1rem;
|
|
78
|
+
padding-bottom: max(0.3rem, env(safe-area-inset-bottom));
|
|
79
|
+
opacity: 0;
|
|
80
|
+
transform: translateY(0.5rem);
|
|
81
|
+
}
|
|
82
|
+
.rvl-root.rvl-visible .rvl-bar {
|
|
83
|
+
opacity: 1;
|
|
84
|
+
transform: translateY(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@media (min-width: 640px) {
|
|
88
|
+
.rvl-top-bar {
|
|
89
|
+
padding: 1rem 1.5rem;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.rvl-header {
|
|
94
|
+
min-width: 0;
|
|
95
|
+
flex: 1 1 0;
|
|
96
|
+
pointer-events: auto;
|
|
97
|
+
}
|
|
98
|
+
.rvl-header-title {
|
|
99
|
+
margin: 0;
|
|
100
|
+
font-size: 0.875rem;
|
|
101
|
+
line-height: 1.35;
|
|
102
|
+
color: var(--rvl-bar-fg);
|
|
103
|
+
}
|
|
104
|
+
.rvl-header-subtitle {
|
|
105
|
+
margin: 0.125rem 0 0;
|
|
106
|
+
font-size: 0.75rem;
|
|
107
|
+
line-height: 1.35;
|
|
108
|
+
color: var(--rvl-bar-fg-muted);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.rvl-header-actions {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: 0.25rem;
|
|
115
|
+
pointer-events: auto;
|
|
116
|
+
flex-shrink: 0;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Buttons ------------------------------------------------------------------- */
|
|
120
|
+
|
|
121
|
+
.rvl-btn {
|
|
122
|
+
display: inline-flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: center;
|
|
125
|
+
height: 2rem;
|
|
126
|
+
min-width: 2rem;
|
|
127
|
+
padding: 0 0.5rem;
|
|
128
|
+
border: none;
|
|
129
|
+
border-radius: 0.375rem;
|
|
130
|
+
background: var(--rvl-btn-bg);
|
|
131
|
+
color: rgba(255, 255, 255, 0.7);
|
|
132
|
+
font: inherit;
|
|
133
|
+
font-size: 0.75rem;
|
|
134
|
+
cursor: pointer;
|
|
135
|
+
backdrop-filter: blur(4px);
|
|
136
|
+
transition:
|
|
137
|
+
color 0.15s ease-out,
|
|
138
|
+
background-color 0.15s ease-out,
|
|
139
|
+
transform 0.15s ease-out;
|
|
140
|
+
}
|
|
141
|
+
.rvl-btn:hover {
|
|
142
|
+
color: #fff;
|
|
143
|
+
background: var(--rvl-btn-bg-hover);
|
|
144
|
+
}
|
|
145
|
+
.rvl-btn:active {
|
|
146
|
+
transform: scale(0.95);
|
|
147
|
+
}
|
|
148
|
+
.rvl-btn:focus-visible {
|
|
149
|
+
outline: 2px solid var(--rvl-accent);
|
|
150
|
+
outline-offset: 2px;
|
|
151
|
+
}
|
|
152
|
+
.rvl-btn.is-active {
|
|
153
|
+
color: #fff;
|
|
154
|
+
background: var(--rvl-accent);
|
|
155
|
+
}
|
|
156
|
+
.rvl-btn-scale {
|
|
157
|
+
font-variant-numeric: tabular-nums;
|
|
158
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/* Image stage --------------------------------------------------------------- */
|
|
162
|
+
|
|
163
|
+
.rvl-stage {
|
|
164
|
+
position: relative;
|
|
165
|
+
z-index: 10;
|
|
166
|
+
width: 100%;
|
|
167
|
+
height: 100%;
|
|
168
|
+
transition: transform 0.35s cubic-bezier(0.25, 0.1, 0.25, 1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.rvl-track {
|
|
172
|
+
position: relative;
|
|
173
|
+
display: flex;
|
|
174
|
+
align-items: center;
|
|
175
|
+
justify-content: center;
|
|
176
|
+
width: 100%;
|
|
177
|
+
height: 100%;
|
|
178
|
+
touch-action: none;
|
|
179
|
+
pointer-events: none;
|
|
180
|
+
opacity: 0;
|
|
181
|
+
transition: opacity var(--rvl-anim-duration) ease-out;
|
|
182
|
+
}
|
|
183
|
+
.rvl-root.rvl-visible .rvl-track {
|
|
184
|
+
opacity: 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.rvl-adjacent {
|
|
188
|
+
position: absolute;
|
|
189
|
+
inset: 0;
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: center;
|
|
193
|
+
user-select: none;
|
|
194
|
+
pointer-events: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.rvl-img-wrapper {
|
|
198
|
+
position: relative;
|
|
199
|
+
display: flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
justify-content: center;
|
|
202
|
+
user-select: none;
|
|
203
|
+
overflow: hidden;
|
|
204
|
+
cursor: default;
|
|
205
|
+
touch-action: none;
|
|
206
|
+
pointer-events: auto;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.rvl-img {
|
|
210
|
+
display: block;
|
|
211
|
+
width: auto;
|
|
212
|
+
height: auto;
|
|
213
|
+
object-fit: contain;
|
|
214
|
+
border-radius: var(--rvl-radius);
|
|
215
|
+
max-width: 96vw;
|
|
216
|
+
will-change: transform;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Bottom nav row ------------------------------------------------------------ */
|
|
220
|
+
|
|
221
|
+
.rvl-nav-row {
|
|
222
|
+
width: 100%;
|
|
223
|
+
padding: 0 1rem;
|
|
224
|
+
pointer-events: auto;
|
|
225
|
+
}
|
|
226
|
+
@media (min-width: 640px) {
|
|
227
|
+
.rvl-nav-row {
|
|
228
|
+
padding: 0 1.5rem;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
.rvl-nav-inner {
|
|
232
|
+
position: relative;
|
|
233
|
+
display: flex;
|
|
234
|
+
align-items: center;
|
|
235
|
+
justify-content: center;
|
|
236
|
+
min-height: 2.25rem;
|
|
237
|
+
max-width: 42rem;
|
|
238
|
+
margin: 0 auto;
|
|
239
|
+
}
|
|
240
|
+
.rvl-nav-group {
|
|
241
|
+
display: flex;
|
|
242
|
+
align-items: center;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
gap: 0.75rem;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Edge slots: pinned to the nav-inner box edges so the nav group stays
|
|
248
|
+
optically centered while a details/info toggle costs no extra height. */
|
|
249
|
+
.rvl-nav-start,
|
|
250
|
+
.rvl-nav-end {
|
|
251
|
+
position: absolute;
|
|
252
|
+
top: 50%;
|
|
253
|
+
transform: translateY(-50%);
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
}
|
|
257
|
+
.rvl-nav-start {
|
|
258
|
+
left: 0;
|
|
259
|
+
}
|
|
260
|
+
.rvl-nav-end {
|
|
261
|
+
right: 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.rvl-nav-btn {
|
|
265
|
+
display: inline-flex;
|
|
266
|
+
align-items: center;
|
|
267
|
+
justify-content: center;
|
|
268
|
+
padding: 0 1rem;
|
|
269
|
+
border: none;
|
|
270
|
+
background: none;
|
|
271
|
+
border-radius: 9999px;
|
|
272
|
+
cursor: pointer;
|
|
273
|
+
color: rgba(255, 255, 255, 0.4);
|
|
274
|
+
transition: color 0.15s ease-out;
|
|
275
|
+
}
|
|
276
|
+
.rvl-nav-btn:hover:not(:disabled) {
|
|
277
|
+
color: rgba(255, 255, 255, 0.7);
|
|
278
|
+
}
|
|
279
|
+
.rvl-nav-btn:active:not(:disabled) {
|
|
280
|
+
color: rgba(255, 255, 255, 0.85);
|
|
281
|
+
}
|
|
282
|
+
.rvl-nav-btn:disabled {
|
|
283
|
+
color: rgba(255, 255, 255, 0.1);
|
|
284
|
+
cursor: default;
|
|
285
|
+
}
|
|
286
|
+
.rvl-nav-btn:focus-visible {
|
|
287
|
+
outline: 2px solid var(--rvl-accent);
|
|
288
|
+
outline-offset: 2px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.rvl-counter {
|
|
292
|
+
display: inline-block;
|
|
293
|
+
text-align: center;
|
|
294
|
+
font-size: 0.6875rem;
|
|
295
|
+
letter-spacing: 0.02em;
|
|
296
|
+
color: var(--rvl-bar-fg-muted);
|
|
297
|
+
font-variant-numeric: tabular-nums;
|
|
298
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
299
|
+
user-select: none;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.rvl-hint {
|
|
303
|
+
margin: 0.25rem auto 0.25rem;
|
|
304
|
+
width: fit-content;
|
|
305
|
+
text-align: center;
|
|
306
|
+
pointer-events: auto;
|
|
307
|
+
}
|
|
308
|
+
.rvl-hint span {
|
|
309
|
+
font-size: 0.6875rem;
|
|
310
|
+
letter-spacing: 0.02em;
|
|
311
|
+
color: var(--rvl-bar-fg-faint);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* Footer + overlay slots ---------------------------------------------------- */
|
|
315
|
+
|
|
316
|
+
.rvl-footer {
|
|
317
|
+
pointer-events: auto;
|
|
318
|
+
}
|
|
319
|
+
.rvl-overlay {
|
|
320
|
+
position: absolute;
|
|
321
|
+
inset: 0;
|
|
322
|
+
/* Sits above the image stage (10) but below the bars (20) so drawers/graphs
|
|
323
|
+
layer over the image while the top/bottom chrome (close, nav, counter,
|
|
324
|
+
edge slots) stays visible and on top — even while an overlay slides in. */
|
|
325
|
+
z-index: 15;
|
|
326
|
+
pointer-events: none;
|
|
327
|
+
}
|
|
328
|
+
.rvl-overlay > * {
|
|
329
|
+
pointer-events: auto;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/* Reduced motion ------------------------------------------------------------ */
|
|
333
|
+
|
|
334
|
+
@media (prefers-reduced-motion: reduce) {
|
|
335
|
+
.rvl-root,
|
|
336
|
+
.rvl-backdrop,
|
|
337
|
+
.rvl-bar,
|
|
338
|
+
.rvl-stage,
|
|
339
|
+
.rvl-track,
|
|
340
|
+
.rvl-btn,
|
|
341
|
+
.rvl-nav-btn {
|
|
342
|
+
transition-duration: 0.01ms !important;
|
|
343
|
+
}
|
|
344
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jekrch/react-viewport-lightbox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A headless-capable, touch-friendly React image viewer and lightbox with zoom, pan, and swipe.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"**/*.css"
|
|
8
|
+
],
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./styles.css": "./dist/styles.css"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"dev": "tsup --watch",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"test:coverage": "vitest run --coverage",
|
|
30
|
+
"lint": "eslint .",
|
|
31
|
+
"format": "prettier --write .",
|
|
32
|
+
"format:check": "prettier --check .",
|
|
33
|
+
"playground": "vite playground",
|
|
34
|
+
"playground:build": "vite build playground",
|
|
35
|
+
"prepublishOnly": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18",
|
|
39
|
+
"react-dom": ">=18"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@changesets/cli": "^2.27.0",
|
|
43
|
+
"@eslint/js": "^9.0.0",
|
|
44
|
+
"@testing-library/jest-dom": "^6.4.0",
|
|
45
|
+
"@types/node": "^20.0.0",
|
|
46
|
+
"@testing-library/react": "^16.0.0",
|
|
47
|
+
"@types/react": "^19.0.0",
|
|
48
|
+
"@types/react-dom": "^19.0.0",
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
50
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
51
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
52
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
53
|
+
"eslint": "^9.0.0",
|
|
54
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
55
|
+
"jsdom": "^25.0.0",
|
|
56
|
+
"prettier": "^3.3.0",
|
|
57
|
+
"react": "^19.0.0",
|
|
58
|
+
"react-dom": "^19.0.0",
|
|
59
|
+
"tsup": "^8.3.0",
|
|
60
|
+
"typescript": "^5.6.0",
|
|
61
|
+
"vite": "^5.4.0",
|
|
62
|
+
"vitest": "^2.1.0"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public",
|
|
66
|
+
"provenance": true
|
|
67
|
+
},
|
|
68
|
+
"repository": {
|
|
69
|
+
"type": "git",
|
|
70
|
+
"url": "git+https://github.com/jekrch/react-viewport-lightbox.git"
|
|
71
|
+
},
|
|
72
|
+
"homepage": "https://github.com/jekrch/react-viewport-lightbox#readme",
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/jekrch/react-viewport-lightbox/issues"
|
|
75
|
+
},
|
|
76
|
+
"author": "jekrch",
|
|
77
|
+
"license": "MIT",
|
|
78
|
+
"keywords": [
|
|
79
|
+
"react",
|
|
80
|
+
"lightbox",
|
|
81
|
+
"react-viewport-lightbox",
|
|
82
|
+
"gallery",
|
|
83
|
+
"image-viewer",
|
|
84
|
+
"zoom",
|
|
85
|
+
"pan",
|
|
86
|
+
"swipe",
|
|
87
|
+
"headless"
|
|
88
|
+
]
|
|
89
|
+
}
|