This commit is contained in:
parent
34523e78b1
commit
5c86fd2ceb
19 changed files with 724 additions and 519 deletions
70
website/src/hooks/usePointerReorder.js
Normal file
70
website/src/hooks/usePointerReorder.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
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 };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue