{"version":3,"file":"use-radial-move.cjs","names":["clamp"],"sources":["../../src/use-radial-move/use-radial-move.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\nimport { clamp } from '../utils';\n\nfunction radiansToDegrees(radians: number) {\n  return radians * (180 / Math.PI);\n}\n\nfunction getElementCenter(element: HTMLElement) {\n  const rect = element.getBoundingClientRect();\n  return [rect.left + rect.width / 2, rect.top + rect.height / 2];\n}\n\nfunction getAngle(coordinates: [number, number], element: HTMLElement) {\n  const center = getElementCenter(element);\n  const x = coordinates[0] - center[0];\n  const y = coordinates[1] - center[1];\n  const deg = radiansToDegrees(Math.atan2(x, y)) + 180;\n  return 360 - deg;\n}\n\nfunction toFixed(value: number, digits: number) {\n  return parseFloat(value.toFixed(digits));\n}\n\nfunction getDigitsAfterDot(value: number) {\n  return value.toString().split('.')[1]?.length || 0;\n}\n\nexport function normalizeRadialValue(degree: number, step: number) {\n  const clamped = clamp(degree, 0, 360);\n  const high = Math.ceil(clamped / step);\n  const low = Math.round(clamped / step);\n  return toFixed(\n    high >= clamped / step ? (high * step === 360 ? 0 : high * step) : low * step,\n    getDigitsAfterDot(step)\n  );\n}\n\nexport interface UseRadialMoveOptions {\n  /** Number by which value is incremented/decremented with mouse and touch events, `0.01` by default */\n  step?: number;\n\n  /** Called in `onMouseUp` and `onTouchEnd` events with the current value */\n  onChangeEnd?: (value: number) => void;\n\n  /** Called in `onMouseDown` and `onTouchStart` events */\n  onScrubStart?: () => void;\n\n  /** Called in `onMouseUp` and `onTouchEnd` events */\n  onScrubEnd?: () => void;\n}\n\nexport interface UseRadialMoveReturnValue<T extends HTMLElement = any> {\n  /** Ref to be passed to the element that should be used for radial move */\n  ref: React.RefCallback<T | null>;\n\n  /** Indicates whether the radial move is active */\n  active: boolean;\n}\n\nexport function useRadialMove<T extends HTMLElement = any>(\n  onChange: (value: number) => void,\n  { step = 0.01, onChangeEnd, onScrubStart, onScrubEnd }: UseRadialMoveOptions = {}\n): UseRadialMoveReturnValue<T> {\n  const mounted = useRef<boolean>(false);\n  const [active, setActive] = useState(false);\n\n  useEffect(() => {\n    mounted.current = true;\n  }, []);\n\n  const refCallback: React.RefCallback<T | null> = useCallback(\n    (node) => {\n      const update = (event: MouseEvent, done = false) => {\n        if (node) {\n          node.style.userSelect = 'none';\n          const deg = getAngle([event.clientX, event.clientY], node);\n          const newValue = normalizeRadialValue(deg, step || 1);\n\n          onChange(newValue);\n          done && onChangeEnd?.(newValue);\n        }\n      };\n\n      const beginTracking = () => {\n        onScrubStart?.();\n        setActive(true);\n        document.addEventListener('mousemove', handleMouseMove, false);\n        document.addEventListener('mouseup', handleMouseUp, false);\n        document.addEventListener('touchmove', handleTouchMove, { passive: false });\n        document.addEventListener('touchend', handleTouchEnd, false);\n      };\n\n      const endTracking = () => {\n        onScrubEnd?.();\n        setActive(false);\n        document.removeEventListener('mousemove', handleMouseMove, false);\n        document.removeEventListener('mouseup', handleMouseUp, false);\n        document.removeEventListener('touchmove', handleTouchMove, false);\n        document.removeEventListener('touchend', handleTouchEnd, false);\n      };\n\n      const onMouseDown = (event: MouseEvent) => {\n        beginTracking();\n        update(event);\n      };\n\n      const handleMouseMove = (event: MouseEvent) => {\n        update(event);\n      };\n\n      const handleMouseUp = (event: MouseEvent) => {\n        update(event, true);\n        endTracking();\n      };\n\n      const handleTouchMove = (event: TouchEvent) => {\n        event.preventDefault();\n        update(event.touches[0] as any);\n      };\n\n      const handleTouchEnd = (event: TouchEvent) => {\n        update(event.changedTouches[0] as any, true);\n        endTracking();\n      };\n\n      const handleTouchStart = (event: TouchEvent) => {\n        event.preventDefault();\n        beginTracking();\n        update(event.touches[0] as any);\n      };\n\n      node?.addEventListener('mousedown', onMouseDown);\n      node?.addEventListener('touchstart', handleTouchStart, { passive: false });\n\n      return () => {\n        if (node) {\n          node.removeEventListener('mousedown', onMouseDown);\n          node.removeEventListener('touchstart', handleTouchStart);\n        }\n      };\n    },\n    [onChange]\n  );\n\n  return { ref: refCallback, active };\n}\n\nexport namespace useRadialMove {\n  export type Options = UseRadialMoveOptions;\n  export type ReturnValue<T extends HTMLElement> = UseRadialMoveReturnValue<T>;\n}\n"],"mappings":"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"}