UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

1 lines 7.8 kB
{"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 [active, setActive] = useState(false);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\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 cleanupRef.current = () => {\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 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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"}