@mantine/hooks
Version:
A collection of 50+ hooks for state and UI management
107 lines (104 loc) • 3.63 kB
JavaScript
'use client';
import { useRef, useState, useEffect, useCallback } from 'react';
import { clamp } from '../utils/clamp/clamp.mjs';
function radiansToDegrees(radians) {
return radians * (180 / Math.PI);
}
function getElementCenter(element) {
const rect = element.getBoundingClientRect();
return [rect.left + rect.width / 2, rect.top + rect.height / 2];
}
function getAngle(coordinates, element) {
const center = getElementCenter(element);
const x = coordinates[0] - center[0];
const y = coordinates[1] - center[1];
const deg = radiansToDegrees(Math.atan2(x, y)) + 180;
return 360 - deg;
}
function toFixed(value, digits) {
return parseFloat(value.toFixed(digits));
}
function getDigitsAfterDot(value) {
return value.toString().split(".")[1]?.length || 0;
}
function normalizeRadialValue(degree, step) {
const clamped = clamp(degree, 0, 360);
const high = Math.ceil(clamped / step);
const low = Math.round(clamped / step);
return toFixed(
high >= clamped / step ? high * step === 360 ? 0 : high * step : low * step,
getDigitsAfterDot(step)
);
}
function useRadialMove(onChange, { step = 0.01, onChangeEnd, onScrubStart, onScrubEnd } = {}) {
const mounted = useRef(false);
const [active, setActive] = useState(false);
useEffect(() => {
mounted.current = true;
}, []);
const refCallback = useCallback(
(node) => {
const update = (event, done = false) => {
if (node) {
node.style.userSelect = "none";
const deg = getAngle([event.clientX, event.clientY], node);
const newValue = normalizeRadialValue(deg, step || 1);
onChange(newValue);
done && onChangeEnd?.(newValue);
}
};
const beginTracking = () => {
onScrubStart?.();
setActive(true);
document.addEventListener("mousemove", handleMouseMove, false);
document.addEventListener("mouseup", handleMouseUp, false);
document.addEventListener("touchmove", handleTouchMove, { passive: false });
document.addEventListener("touchend", handleTouchEnd, false);
};
const endTracking = () => {
onScrubEnd?.();
setActive(false);
document.removeEventListener("mousemove", handleMouseMove, false);
document.removeEventListener("mouseup", handleMouseUp, false);
document.removeEventListener("touchmove", handleTouchMove, false);
document.removeEventListener("touchend", handleTouchEnd, false);
};
const onMouseDown = (event) => {
beginTracking();
update(event);
};
const handleMouseMove = (event) => {
update(event);
};
const handleMouseUp = (event) => {
update(event, true);
endTracking();
};
const handleTouchMove = (event) => {
event.preventDefault();
update(event.touches[0]);
};
const handleTouchEnd = (event) => {
update(event.changedTouches[0], true);
endTracking();
};
const handleTouchStart = (event) => {
event.preventDefault();
beginTracking();
update(event.touches[0]);
};
node?.addEventListener("mousedown", onMouseDown);
node?.addEventListener("touchstart", handleTouchStart, { passive: false });
return () => {
if (node) {
node.removeEventListener("mousedown", onMouseDown);
node.removeEventListener("touchstart", handleTouchStart);
}
};
},
[onChange]
);
return { ref: refCallback, active };
}
export { normalizeRadialValue, useRadialMove };
//# sourceMappingURL=use-radial-move.mjs.map