All files / web/src/components/toys/euclid/hooks useQuadDrag.ts

0% Statements 0/84
0% Branches 0/1
0% Functions 0/1
0% Lines 0/84

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85                                                                                                                                                                         
import { useState, useRef, useCallback } from 'react'

interface UseQuadDragOptions {
  containerRef: React.RefObject<HTMLDivElement | null>
}

export function useQuadDrag({ containerRef }: UseQuadDragOptions) {
  const quadRef = useRef<HTMLDivElement>(null)
  const quadDragRef = useRef<{
    startX: number
    startY: number
    origX: number
    origY: number
  } | null>(null)
  const [quadOffset, setQuadOffset] = useState({ x: 0, y: 0 })
  const [quadDragging, setQuadDragging] = useState(false)

  const handleQuadPointerDown = useCallback(
    (e: React.PointerEvent) => {
      e.preventDefault()
      e.stopPropagation()
      const el = e.currentTarget
      el.setPointerCapture(e.pointerId)
      quadDragRef.current = {
        startX: e.clientX,
        startY: e.clientY,
        origX: quadOffset.x,
        origY: quadOffset.y,
      }
      setQuadDragging(true)
    },
    [quadOffset]
  )

  const handleQuadPointerMove = useCallback((e: React.PointerEvent) => {
    const drag = quadDragRef.current
    if (!drag) return
    e.preventDefault()
    const dx = e.clientX - drag.startX
    const dy = e.clientY - drag.startY
    let newX = drag.origX + dx
    let newY = drag.origY + dy

    // Clamp so the quad stays within the canvas pane bounds.
    const container = containerRef.current
    const root = container?.parentElement // euclid-canvas root
    if (container && root) {
      const rootW = root.clientWidth
      const rootH = root.clientHeight
      const cw = container.clientWidth
      const ch = container.clientHeight
      const QUAD_SIZE = 76
      const MARGIN = 12

      const baseX = cw - MARGIN - QUAD_SIZE
      const baseY = ch - MARGIN - QUAD_SIZE
      const quadLeft = baseX + newX
      const quadTop = baseY + newY

      const clampedLeft = Math.max(0, Math.min(quadLeft, rootW - QUAD_SIZE))
      const clampedTop = Math.max(0, Math.min(quadTop, rootH - QUAD_SIZE))
      newX = clampedLeft - baseX
      newY = clampedTop - baseY
    }

    setQuadOffset({ x: newX, y: newY })
  }, [])

  const handleQuadPointerUp = useCallback((e: React.PointerEvent) => {
    if (!quadDragRef.current) return
    e.currentTarget.releasePointerCapture(e.pointerId)
    quadDragRef.current = null
    setQuadDragging(false)
  }, [])

  return {
    quadRef,
    quadOffset,
    quadDragging,
    handleQuadPointerDown,
    handleQuadPointerMove,
    handleQuadPointerUp,
  }
}