import { useEffect, MutableRefObject, useState, useRef } from 'react'; import { useCallbackRef } from '../use-callback-ref'; import { getDragData } from './use-drag'; export type SilkeDropState = 'none' | 'over' | 'drop'; export function useDrop({ ref, onOver, onDrop, canDrop, disabled, }: { ref: MutableRefObject; onOver?: (item: T, e: DragEvent) => void; onDrop?: (item: T, e: DragEvent) => void; canDrop?: (item: T, e: DragEvent, state: SilkeDropState) => boolean; disabled?: boolean; }): SilkeDropState { const [state, setState] = useState('none'); const stateRef = useRef(state); stateRef.current = state; const onDropRef = useCallbackRef(onDrop); const onOverRef = useCallbackRef(onOver); const canDropRef = useCallbackRef(canDrop); useEffect(() => { const el = ref.current; if (el && !disabled) { const handleDragEnter = (e: DragEvent) => { (e.dataTransfer as any).dropEffect = 'move'; e.stopPropagation(); e.preventDefault(); const data = getDragData(); if (stateRef.current !== 'over' && canDropRef.current?.(data?.current, e, 'over')) { stateRef.current = 'over'; setState('over'); onOverRef.current?.(data?.current, e); } }; const handleDrop = (e: DragEvent) => { e.stopPropagation(); const data = getDragData(); if (canDropRef.current?.(data?.current, e, 'drop')) { stateRef.current = 'none'; setState('none'); onDropRef.current?.(data?.current, e); } }; const handleDragLeave = () => { stateRef.current = 'none'; setState('none'); }; el.addEventListener('dragenter', handleDragEnter); el.addEventListener('dragover', handleDragEnter); el.addEventListener('dragleave', handleDragLeave); el.addEventListener('drop', handleDrop); return () => { el.removeEventListener('dragenter', handleDragEnter); el.removeEventListener('dragover', handleDragEnter); el.removeEventListener('dragleave', handleDragLeave); el.removeEventListener('drop', handleDrop); }; } }, [onDropRef, onOverRef, canDropRef, ref, disabled]); return state; }