import { cloneElement } from 'preact';
import { useEffect, useRef, useState } from 'preact/hooks';
/**
* A custom Preact hook used to attach drag and drop functionality to a DOM element.
* @example
* function SomeComponent(props) {
* const { setElement } = useDragAndDrop({
* onDrop: someDropHandler,
* onDragOver: someDragOverHandler,
* onDragExit: someDragExitHandler
* });
*
* const someDomRef = useRef(null);
*
* useEffect(() => {
* if (someDomRef.current) {
* setElement(someDomRef.current);
* }
* });
*
* return ;
* };
*
* @param {object} props
* @param {Function} props.onDragOver The handler that runs when the dragover event is fired.
* @param {Function} props.onDragExit The handler that runs when the dragexit/dragleave events are fired.
* @param {Function} props.onDrop The handler that runs when the drop event is fired.
*/
export function useDragAndDrop({ onDragOver, onDragExit, onDrop }) {
const [element, setElement] = useState(null);
useEffect(() => {
if (!element) {
return;
}
const noDragAndDropHandler = (event) => event.preventDefault();
document.addEventListener('dragover', noDragAndDropHandler);
document.addEventListener('drop', noDragAndDropHandler);
element.addEventListener('dragover', onDragOver);
element.addEventListener('dragexit', onDragExit);
element.addEventListener('dragleave', onDragExit);
element.addEventListener('drop', onDrop);
return () => {
document.removeEventListener('dragover', noDragAndDropHandler);
document.removeEventListener('drop', noDragAndDropHandler);
element.removeEventListener('dragover', onDragOver);
element.removeEventListener('dragexit', onDragExit);
element.removeEventListener('dragleave', onDragExit);
element.removeEventListener('drop', onDrop);
};
}, [element, onDragOver, onDragExit, onDrop]);
return { setElement };
}
/**
* Registers drag and drop events for the child element that is wrapped by this component.
*
* @example
*
*
*
*
* @param {object} props
* @param {JSX.Element} props.children The React element that will register it's DOM counterpart with drag and drop events.
* @param {Function} props.onDragOver The handler that runs when the dragover event is fired.
* @param {Function} props.onDragExit The handler that runs when the dragexit/dragleave events are fired.
* @param {Function} props.onDrop The handler that runs when the drop event is fired.
*/
export function DragAndDropZone({ children, onDragOver, onDragExit, onDrop }) {
if (!children) {
throw new Error(
'The component children prop is null or was not specified.',
);
}
const { setElement } = useDragAndDrop({ onDragOver, onDragExit, onDrop });
const dropZoneRef = useRef(null);
if (dropZoneRef.current) {
setElement(dropZoneRef.current);
}
return cloneElement(children, {
ref: dropZoneRef,
});
}