{"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"}