import { useCallback, useState, DragEvent, useRef } from 'react'; interface UseDragAndDropOptions { onFileDropped: (file: File) => void; acceptedTypes?: string[]; } interface UseDragAndDropReturn { isDragActive: boolean; dragProps: { onDrop: (event: DragEvent) => void; onDragOver: (event: DragEvent) => void; onDragEnter: (event: DragEvent) => void; onDragLeave: (event: DragEvent) => void; }; } const isValidFileType = (file: File, acceptedTypes?: string[]): boolean => { if (!acceptedTypes || acceptedTypes.length === 0) return true; return acceptedTypes.some(type => { if (type.endsWith('/*')) { // Handle MIME type categories like 'video/*' or 'audio/*' const category = type.slice(0, -2); return file.type.startsWith(category + '/'); } else { // Handle exact MIME types return file.type === type; } }); }; export const useDragAndDrop = ({ onFileDropped, acceptedTypes = ['video/*', 'audio/*'] }: UseDragAndDropOptions): UseDragAndDropReturn => { const [isDragActive, setIsDragActive] = useState(false); const dragCounter = useRef(0); const dragLeaveTimeout = useRef(null); const handleDragEnter = useCallback((event: DragEvent) => { event.preventDefault(); event.stopPropagation(); // Clear any pending drag leave timeout if (dragLeaveTimeout.current) { clearTimeout(dragLeaveTimeout.current); dragLeaveTimeout.current = null; } dragCounter.current += 1; if (event.dataTransfer?.items && event.dataTransfer.items.length > 0) { setIsDragActive(true); } }, []); const handleDragLeave = useCallback((event: DragEvent) => { event.preventDefault(); event.stopPropagation(); dragCounter.current -= 1; // Use a small timeout to prevent flickering when moving between child elements dragLeaveTimeout.current = window.setTimeout(() => { if (dragCounter.current === 0) { setIsDragActive(false); } }, 10); }, []); const handleDragOver = useCallback((event: DragEvent) => { event.preventDefault(); event.stopPropagation(); // Set the dropEffect to indicate this is a copy operation if (event.dataTransfer) { event.dataTransfer.dropEffect = 'copy'; } }, []); const handleDrop = useCallback((event: DragEvent) => { event.preventDefault(); event.stopPropagation(); // Clear timeout and reset state if (dragLeaveTimeout.current) { clearTimeout(dragLeaveTimeout.current); dragLeaveTimeout.current = null; } setIsDragActive(false); dragCounter.current = 0; const files = Array.from(event.dataTransfer?.files || []); if (files.length > 0) { const validFile = files.find(file => isValidFileType(file, acceptedTypes)); if (validFile) { onFileDropped(validFile); } else { console.warn('Dropped file is not a supported type:', files[0]?.type); // You could also call an onError callback here if needed } } }, [onFileDropped, acceptedTypes]); return { isDragActive, dragProps: { onDrop: handleDrop, onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, }, }; };