{"version":3,"sources":["../src/hooks/use-pan-zoom.ts"],"names":["useRef","useEffect","useCallback"],"mappings":"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","file":"chunk-T6YELZPJ.cjs","sourcesContent":["import { useCallback, useEffect, useRef } from \"react\";\n\nexport interface ViewportState {\n  panX: number;\n  panY: number;\n  zoom: number;\n}\n\ninterface UsePanZoomOptions {\n  viewport: ViewportState;\n  setViewport: (vp: ViewportState | ((prev: ViewportState) => ViewportState)) => void;\n  minZoom: number;\n  maxZoom: number;\n  pannable: boolean;\n  zoomable: boolean;\n  containerRef: React.RefObject<HTMLElement | null>;\n}\n\ninterface UsePanZoomReturn {\n  containerProps: {\n    onPointerDown: (e: React.PointerEvent) => void;\n    onPointerMove: (e: React.PointerEvent) => void;\n    onPointerUp: (e: React.PointerEvent) => void;\n  };\n  isPanning: boolean;\n}\n\nexport function usePanZoom({\n  viewport,\n  setViewport,\n  minZoom,\n  maxZoom,\n  pannable,\n  zoomable,\n  containerRef,\n}: UsePanZoomOptions): UsePanZoomReturn {\n  const isPanningRef = useRef(false);\n  const startRef = useRef({ x: 0, y: 0, panX: 0, panY: 0 });\n\n  // Store latest values in refs so the native wheel listener always sees current state\n  const viewportRef = useRef(viewport);\n  viewportRef.current = viewport;\n  const setViewportRef = useRef(setViewport);\n  setViewportRef.current = setViewport;\n  const zoomableRef = useRef(zoomable);\n  zoomableRef.current = zoomable;\n  const minZoomRef = useRef(minZoom);\n  minZoomRef.current = minZoom;\n  const maxZoomRef = useRef(maxZoom);\n  maxZoomRef.current = maxZoom;\n\n  // Attach a non-passive wheel listener so preventDefault() actually stops page scroll\n  useEffect(() => {\n    const container = containerRef.current;\n    if (!container) return;\n\n    function handleWheel(e: WheelEvent) {\n      if (!zoomableRef.current) return;\n      // Require Ctrl+wheel (or pinch gesture) to zoom — don't hijack normal page scroll\n      if (!e.ctrlKey && !e.metaKey) return;\n      e.preventDefault();\n\n      const rect = container!.getBoundingClientRect();\n      const mouseX = e.clientX - rect.left;\n      const mouseY = e.clientY - rect.top;\n\n      setViewportRef.current((prev) => {\n        const factor = 1 - e.deltaY * 0.001;\n        const newZoom = Math.min(maxZoomRef.current, Math.max(minZoomRef.current, prev.zoom * factor));\n        const ratio = newZoom / prev.zoom;\n\n        return {\n          zoom: newZoom,\n          panX: mouseX - (mouseX - prev.panX) * ratio,\n          panY: mouseY - (mouseY - prev.panY) * ratio,\n        };\n      });\n    }\n\n    container.addEventListener(\"wheel\", handleWheel, { passive: false });\n    return () => {\n      container.removeEventListener(\"wheel\", handleWheel);\n    };\n  }, [containerRef]);\n\n  const onPointerDown = useCallback(\n    (e: React.PointerEvent) => {\n      if (!pannable) return;\n      // Only pan on primary button and when clicking the background\n      if (e.button !== 0) return;\n      const target = e.target as HTMLElement;\n      if (target !== containerRef.current && !target.hasAttribute(\"data-canvas-bg\")) return;\n\n      isPanningRef.current = true;\n      startRef.current = { x: e.clientX, y: e.clientY, panX: viewport.panX, panY: viewport.panY };\n      (e.target as HTMLElement).setPointerCapture(e.pointerId);\n      e.preventDefault();\n    },\n    [pannable, viewport.panX, viewport.panY, containerRef],\n  );\n\n  const onPointerMove = useCallback(\n    (e: React.PointerEvent) => {\n      if (!isPanningRef.current) return;\n      const dx = e.clientX - startRef.current.x;\n      const dy = e.clientY - startRef.current.y;\n      setViewport((prev) => ({\n        ...prev,\n        panX: startRef.current.panX + dx,\n        panY: startRef.current.panY + dy,\n      }));\n    },\n    [setViewport],\n  );\n\n  const onPointerUp = useCallback(() => {\n    isPanningRef.current = false;\n  }, []);\n\n  return {\n    containerProps: { onPointerDown, onPointerMove, onPointerUp },\n    isPanning: isPanningRef.current,\n  };\n}\n"]}