sokko-g/website/src/hooks/usePointerReorder.js
Shinuwa 5c86fd2ceb
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s
structure optimisation
2026-07-22 22:49:36 +02:00

70 lines
2.4 KiB
JavaScript

import { useEffect, useRef, useState } from "react";
function getDefaultPlacement(event, element) {
const rect = element.getBoundingClientRect();
return event.clientY > rect.top + rect.height / 2 ? "after" : "before";
}
export function usePointerReorder({
targetSelector,
getTargetId,
canDropOn = () => true,
getPlacement = getDefaultPlacement,
onMove
}) {
const [draggingId, setDraggingId] = useState("");
const [dropTarget, setDropTarget] = useState({ id: "", placement: "before" });
const optionsRef = useRef({ getTargetId, canDropOn, getPlacement, onMove });
useEffect(() => {
optionsRef.current = { getTargetId, canDropOn, getPlacement, onMove };
}, [canDropOn, getPlacement, getTargetId, onMove]);
useEffect(() => {
if (!draggingId) return undefined;
function getDropTarget(event) {
const { getTargetId, canDropOn, getPlacement } = optionsRef.current;
const element = document.elementFromPoint(event.clientX, event.clientY);
const target = element?.closest?.(targetSelector);
if (!target || !canDropOn(target, draggingId) || getTargetId(target) === draggingId) {
return { id: "", placement: "before" };
}
return {
id: getTargetId(target),
placement: getPlacement(event, target)
};
}
function handlePointerMove(event) {
setDropTarget(getDropTarget(event));
}
function handlePointerUp(event) {
const { onMove } = optionsRef.current;
const target = getDropTarget(event);
if (target.id) onMove(draggingId, target.id, target.placement);
setDraggingId("");
setDropTarget({ id: "", placement: "before" });
}
window.addEventListener("pointermove", handlePointerMove);
window.addEventListener("pointerup", handlePointerUp, { once: true });
window.addEventListener("pointercancel", handlePointerUp, { once: true });
return () => {
window.removeEventListener("pointermove", handlePointerMove);
window.removeEventListener("pointerup", handlePointerUp);
window.removeEventListener("pointercancel", handlePointerUp);
};
}, [draggingId, targetSelector]);
function startDrag(event, id) {
if (event.button !== 0) return;
event.preventDefault();
event.currentTarget.setPointerCapture?.(event.pointerId);
setDraggingId(id);
setDropTarget({ id: "", placement: "before" });
}
return { draggingId, dropTarget, startDrag };
}