2022-05-25 11:40:20 +02:00
|
|
|
import { useRef, useEffect, RefObject } from 'react';
|
|
|
|
|
|
|
|
export type MoveListItem = (
|
|
|
|
dragIndex: number,
|
|
|
|
dropIndex: number,
|
|
|
|
save?: boolean
|
|
|
|
) => void;
|
|
|
|
|
2022-11-11 11:24:56 +01:00
|
|
|
export const useDragItem = <T extends HTMLElement>(
|
2022-05-25 11:40:20 +02:00
|
|
|
listItemIndex: number,
|
2022-11-11 11:24:56 +01:00
|
|
|
moveListItem: MoveListItem,
|
|
|
|
handle?: RefObject<HTMLElement>
|
|
|
|
): RefObject<T> => {
|
|
|
|
const ref = useRef<T>(null);
|
2022-05-25 11:40:20 +02:00
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (ref.current) {
|
|
|
|
ref.current.dataset.index = String(listItemIndex);
|
2022-11-11 11:24:56 +01:00
|
|
|
return addEventListeners(
|
|
|
|
ref.current,
|
|
|
|
moveListItem,
|
|
|
|
handle?.current ?? undefined
|
|
|
|
);
|
2022-05-25 11:40:20 +02:00
|
|
|
}
|
|
|
|
}, [listItemIndex, moveListItem]);
|
|
|
|
|
|
|
|
return ref;
|
|
|
|
};
|
|
|
|
|
|
|
|
const addEventListeners = (
|
2022-11-11 11:24:56 +01:00
|
|
|
el: HTMLElement,
|
|
|
|
moveListItem: MoveListItem,
|
|
|
|
handle?: HTMLElement
|
2022-05-25 11:40:20 +02:00
|
|
|
): (() => void) => {
|
|
|
|
const moveDraggedElement = (save: boolean) => {
|
|
|
|
if (globalDraggedElement) {
|
|
|
|
moveListItem(
|
|
|
|
Number(globalDraggedElement.dataset.index),
|
|
|
|
Number(el.dataset.index),
|
|
|
|
save
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-11 11:24:56 +01:00
|
|
|
const handleEl = handle ?? el;
|
|
|
|
|
|
|
|
const onMouseEnter = (e: MouseEvent) => {
|
|
|
|
if (e.target === handleEl) {
|
|
|
|
el.draggable = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const onMouseLeave = () => {
|
|
|
|
el.draggable = false;
|
|
|
|
};
|
|
|
|
|
2022-05-25 11:40:20 +02:00
|
|
|
const onDragStart = () => {
|
2022-11-11 11:24:56 +01:00
|
|
|
el.draggable = true;
|
2022-05-25 11:40:20 +02:00
|
|
|
globalDraggedElement = el;
|
|
|
|
};
|
|
|
|
|
|
|
|
const onDragEnter = () => {
|
|
|
|
moveDraggedElement(false);
|
|
|
|
};
|
|
|
|
|
|
|
|
const onDragOver = (event: DragEvent) => {
|
|
|
|
event.preventDefault();
|
|
|
|
};
|
|
|
|
|
|
|
|
const onDrop = () => {
|
|
|
|
moveDraggedElement(true);
|
|
|
|
globalDraggedElement = null;
|
|
|
|
};
|
|
|
|
|
2022-11-11 11:24:56 +01:00
|
|
|
handleEl.addEventListener('mouseenter', onMouseEnter);
|
|
|
|
handleEl.addEventListener('mouseleave', onMouseLeave);
|
2022-11-14 09:25:20 +01:00
|
|
|
if (handle) {
|
|
|
|
el.addEventListener('mouseenter', onMouseLeave);
|
|
|
|
}
|
2022-05-25 11:40:20 +02:00
|
|
|
el.addEventListener('dragstart', onDragStart);
|
|
|
|
el.addEventListener('dragenter', onDragEnter);
|
|
|
|
el.addEventListener('dragover', onDragOver);
|
|
|
|
el.addEventListener('drop', onDrop);
|
|
|
|
|
|
|
|
return () => {
|
2022-11-11 11:24:56 +01:00
|
|
|
handleEl.removeEventListener('mouseenter', onMouseEnter);
|
|
|
|
handleEl.removeEventListener('mouseleave', onMouseLeave);
|
2022-11-14 09:25:20 +01:00
|
|
|
if (handle) {
|
|
|
|
el.removeEventListener('mouseenter', onMouseLeave);
|
|
|
|
}
|
2022-05-25 11:40:20 +02:00
|
|
|
el.removeEventListener('dragstart', onDragStart);
|
|
|
|
el.removeEventListener('dragenter', onDragEnter);
|
|
|
|
el.removeEventListener('dragover', onDragOver);
|
|
|
|
el.removeEventListener('drop', onDrop);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
// The element being dragged in the browser.
|
2022-11-11 11:24:56 +01:00
|
|
|
let globalDraggedElement: HTMLElement | null;
|