import { RefObject, useEffect } from 'react'; import { useCallbackRef } from './use-callback-ref'; function preventDefault(e: Event) { e.preventDefault(); } function preventDragDefault(el: HTMLElement) { ['dragenter', 'dragover', 'dragleave', 'drop'].forEach((e) => el.addEventListener(e, preventDefault), ); } if (typeof document !== 'undefined') preventDragDefault(document.body); export function useDropZone( target: RefObject, onDrop: (e: DragEvent) => void, onEnter?: (e: DragEvent) => void, onLeave?: (e: DragEvent) => void, onOver?: (e: DragEvent) => void, ) { const dropRef = useCallbackRef(onDrop); const enterRef = useCallbackRef(onEnter); const leaveRef = useCallbackRef(onLeave); const overRef = useCallbackRef(onOver); useEffect(() => { const el = target.current; if (el) { let eventTarget: EventTarget | null; preventDragDefault(el); const onEnter = (e: DragEvent) => { if (overRef.current) overRef.current(e); if (eventTarget === e.target) return null; eventTarget = e.target; e.preventDefault(); if (enterRef.current) enterRef.current(e); }; const onLeave = (e: DragEvent) => { if (leaveRef.current && e.target === eventTarget) { eventTarget = null; e.preventDefault(); leaveRef.current(e); } }; const onDrop = (e: DragEvent) => { eventTarget = null; e.preventDefault(); if (dropRef.current) dropRef.current(e); }; el.addEventListener('dragenter', onEnter); el.addEventListener('dragover', onEnter); el.addEventListener('dragleave', onLeave); el.addEventListener('drop', onDrop); return () => { el.removeEventListener('dragenter', onEnter); el.removeEventListener('dragover', onEnter); el.removeEventListener('dragleave', onLeave); el.removeEventListener('drop', onDrop); }; } }, [target, dropRef, enterRef, leaveRef, overRef]); }