@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.
@@ -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"]}
@@ -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
+ }