@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
1 lines • 6.6 kB
Source Map (JSON)
{"version":3,"file":"useGalleryNav.mjs","names":[],"sources":["../../../src/Image/viewer/useGalleryNav.ts"],"sourcesContent":["import type { MotionValue } from 'motion/react';\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport type { Size } from './geometry';\nimport type { PreviewEntry } from './registry';\nimport { isTypingTarget } from './useViewerGestures';\n\nexport const readNatural = (element: HTMLImageElement): Size => {\n if (element.naturalWidth > 0 && element.naturalHeight > 0)\n return { height: element.naturalHeight, width: element.naturalWidth };\n const rect = element.getBoundingClientRect();\n return { height: Math.max(rect.height, 1), width: Math.max(rect.width, 1) };\n};\n\nexport interface UseGalleryNavOptions {\n cancelPendingClose: () => void;\n currentIndex: number;\n entries: PreviewEntry[];\n flipX: MotionValue<boolean>;\n flipY: MotionValue<boolean>;\n getInitialScale: () => number;\n resetCloseTracking: () => void;\n rotate: MotionValue<number>;\n scale: MotionValue<number>;\n setCurrentIndex: (index: number) => void;\n setNatural: (natural: Size) => void;\n setSource: (src: string) => void;\n switchTo: (apply: () => void, direction?: 1 | -1) => void;\n syncZoomNatural: (natural: Size) => void;\n x: MotionValue<number>;\n y: MotionValue<number>;\n}\n\nexport interface UseGalleryNavResult {\n hasNext: boolean;\n hasPrev: boolean;\n next: () => void;\n prev: () => void;\n}\n\nexport const useGalleryNav = ({\n cancelPendingClose,\n currentIndex,\n entries,\n flipX,\n flipY,\n getInitialScale,\n resetCloseTracking,\n rotate,\n scale,\n setCurrentIndex,\n setNatural,\n setSource,\n switchTo,\n syncZoomNatural,\n x,\n y,\n}: UseGalleryNavOptions): UseGalleryNavResult => {\n const indexRef = useRef(currentIndex);\n indexRef.current = currentIndex;\n\n const switchingRef = useRef(false);\n\n const switchToRef = useRef(switchTo);\n switchToRef.current = switchTo;\n\n const cancelPendingCloseRef = useRef(cancelPendingClose);\n cancelPendingCloseRef.current = cancelPendingClose;\n\n const resetCloseTrackingRef = useRef(resetCloseTracking);\n resetCloseTrackingRef.current = resetCloseTracking;\n\n const goTo = useCallback(\n (nextIndex: number) => {\n if (switchingRef.current) return;\n if (nextIndex < 0 || nextIndex >= entries.length || nextIndex === indexRef.current) return;\n switchingRef.current = true;\n // A switch changes the displayed image out from under any pending\n // click-to-close or wheel-close accumulation armed for the old image,\n // so both must clear at this single choke point every switch goes through.\n cancelPendingCloseRef.current();\n resetCloseTrackingRef.current();\n switchToRef.current(\n () => {\n const nextEntry = entries[nextIndex];\n setCurrentIndex(nextIndex);\n setSource(nextEntry.src);\n // Orientation first: syncZoomNatural derives the incoming image's\n // resting scale from the fit rect, which is rotation-dependent.\n rotate.jump(0);\n flipX.jump(false);\n flipY.jump(false);\n const nextNatural = readNatural(nextEntry.element);\n setNatural(nextNatural);\n syncZoomNatural(nextNatural);\n // .jump(), not .set(): a surviving spring from the outgoing image (a\n // dragEnd clamp-back, a wheel snap-back, or reset()) targets the OLD\n // image's bounds and would otherwise silently overwrite this reset on\n // its next tick, since motion's set() never stops an active animation.\n scale.jump(getInitialScale());\n x.jump(0);\n y.jump(0);\n switchingRef.current = false;\n },\n nextIndex > indexRef.current ? 1 : -1,\n );\n },\n [\n entries,\n flipX,\n flipY,\n getInitialScale,\n rotate,\n scale,\n setCurrentIndex,\n setNatural,\n setSource,\n syncZoomNatural,\n x,\n y,\n ],\n );\n\n const prev = useCallback(() => goTo(indexRef.current - 1), [goTo]);\n const next = useCallback(() => goTo(indexRef.current + 1), [goTo]);\n\n const isGallery = entries.length > 1;\n\n useEffect(() => {\n if (!isGallery) return;\n const listener = (event: KeyboardEvent) => {\n if (event.altKey || event.ctrlKey || event.metaKey || event.defaultPrevented) return;\n if (isTypingTarget(event.target)) return;\n if (event.key === 'ArrowLeft') prev();\n else if (event.key === 'ArrowRight') next();\n else return;\n event.preventDefault();\n };\n document.addEventListener('keydown', listener);\n return () => document.removeEventListener('keydown', listener);\n }, [isGallery, next, prev]);\n\n return {\n hasNext: currentIndex < entries.length - 1,\n hasPrev: currentIndex > 0,\n next,\n prev,\n };\n};\n"],"mappings":";;;AAOA,MAAa,eAAe,YAAoC;CAC9D,IAAI,QAAQ,eAAe,KAAK,QAAQ,gBAAgB,GACtD,OAAO;EAAE,QAAQ,QAAQ;EAAe,OAAO,QAAQ;CAAa;CACtE,MAAM,OAAO,QAAQ,sBAAsB;CAC3C,OAAO;EAAE,QAAQ,KAAK,IAAI,KAAK,QAAQ,CAAC;EAAG,OAAO,KAAK,IAAI,KAAK,OAAO,CAAC;CAAE;AAC5E;AA4BA,MAAa,iBAAiB,EAC5B,oBACA,cACA,SACA,OACA,OACA,iBACA,oBACA,QACA,OACA,iBACA,YACA,WACA,UACA,iBACA,GACA,QAC+C;CAC/C,MAAM,WAAW,OAAO,YAAY;CACpC,SAAS,UAAU;CAEnB,MAAM,eAAe,OAAO,KAAK;CAEjC,MAAM,cAAc,OAAO,QAAQ;CACnC,YAAY,UAAU;CAEtB,MAAM,wBAAwB,OAAO,kBAAkB;CACvD,sBAAsB,UAAU;CAEhC,MAAM,wBAAwB,OAAO,kBAAkB;CACvD,sBAAsB,UAAU;CAEhC,MAAM,OAAO,aACV,cAAsB;EACrB,IAAI,aAAa,SAAS;EAC1B,IAAI,YAAY,KAAK,aAAa,QAAQ,UAAU,cAAc,SAAS,SAAS;EACpF,aAAa,UAAU;EAIvB,sBAAsB,QAAQ;EAC9B,sBAAsB,QAAQ;EAC9B,YAAY,cACJ;GACJ,MAAM,YAAY,QAAQ;GAC1B,gBAAgB,SAAS;GACzB,UAAU,UAAU,GAAG;GAGvB,OAAO,KAAK,CAAC;GACb,MAAM,KAAK,KAAK;GAChB,MAAM,KAAK,KAAK;GAChB,MAAM,cAAc,YAAY,UAAU,OAAO;GACjD,WAAW,WAAW;GACtB,gBAAgB,WAAW;GAK3B,MAAM,KAAK,gBAAgB,CAAC;GAC5B,EAAE,KAAK,CAAC;GACR,EAAE,KAAK,CAAC;GACR,aAAa,UAAU;EACzB,GACA,YAAY,SAAS,UAAU,IAAI,EACrC;CACF,GACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,MAAM,OAAO,kBAAkB,KAAK,SAAS,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;CACjE,MAAM,OAAO,kBAAkB,KAAK,SAAS,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;CAEjE,MAAM,YAAY,QAAQ,SAAS;CAEnC,gBAAgB;EACd,IAAI,CAAC,WAAW;EAChB,MAAM,YAAY,UAAyB;GACzC,IAAI,MAAM,UAAU,MAAM,WAAW,MAAM,WAAW,MAAM,kBAAkB;GAC9E,IAAI,eAAe,MAAM,MAAM,GAAG;GAClC,IAAI,MAAM,QAAQ,aAAa,KAAK;QAC/B,IAAI,MAAM,QAAQ,cAAc,KAAK;QACrC;GACL,MAAM,eAAe;EACvB;EACA,SAAS,iBAAiB,WAAW,QAAQ;EAC7C,aAAa,SAAS,oBAAoB,WAAW,QAAQ;CAC/D,GAAG;EAAC;EAAW;EAAM;CAAI,CAAC;CAE1B,OAAO;EACL,SAAS,eAAe,QAAQ,SAAS;EACzC,SAAS,eAAe;EACxB;EACA;CACF;AACF"}