import { useState, useRef } from 'react'; import Box from '@mui/material/Box'; /** * An otherwise invisible dropzone wrapper. * Renders seamlessly around children without altering layout or style, * and highlights with an outline only while a file is dragged over it. */ export function FileDropzone({ accept = 'application/pdf,.pdf', disabled = false, onFileDrop, children, sx, ...props }) { const [isDragging, setIsDragging] = useState(false); const dragCounter = useRef(0); const handleDragEnter = (e) => { e.preventDefault(); e.stopPropagation(); if (disabled) return; dragCounter.current += 1; if (e.dataTransfer.items && e.dataTransfer.items.length > 0) { setIsDragging(true); } }; const handleDragLeave = (e) => { e.preventDefault(); e.stopPropagation(); dragCounter.current -= 1; if (dragCounter.current <= 0) { dragCounter.current = 0; setIsDragging(false); } }; const handleDragOver = (e) => { e.preventDefault(); e.stopPropagation(); if (disabled) { e.dataTransfer.dropEffect = 'none'; return; } e.dataTransfer.dropEffect = 'copy'; }; const handleDrop = (e) => { e.preventDefault(); e.stopPropagation(); dragCounter.current = 0; setIsDragging(false); if (disabled) return; const file = e.dataTransfer.files?.[0]; if (!file) return; if (accept) { const allowed = accept .split(',') .map((s) => s.trim().toLowerCase()) .filter(Boolean); const name = (file.name || '').toLowerCase(); const dotIndex = name.lastIndexOf('.'); const ext = dotIndex !== -1 ? name.slice(dotIndex) : ''; const mimeType = (file.type || '').toLowerCase(); const matches = allowed.some((item) => { if (item.startsWith('.')) { return ext === item; } return mimeType === item; }); if (!matches) return; } onFileDrop?.(file); }; return ( {typeof children === 'function' ? children({ isDragging }) : children} ); } export default FileDropzone;