import { useRef, useState, type CSSProperties, type ReactNode } from "react"; import { UploadCloud } from "lucide-react"; export type FileDropZoneProps = { accept?: string; multiple?: boolean; disabled?: boolean; busy?: boolean; progress?: number | null; label?: ReactNode; actionLabel?: ReactNode; busyLabel?: ReactNode; progressLabel?: ReactNode; note?: ReactNode; className?: string; inputLabel?: string; onFiles: (files: File[]) => void | Promise; }; export default function FileDropZone({ accept, multiple = true, disabled = false, busy = false, progress = null, label = "Drop files here", actionLabel = "or click to select files", busyLabel = "Uploading files", progressLabel, note, className = "", inputLabel = "Drop files here or click to select files", onFiles }: FileDropZoneProps) { const inputRef = useRef(null); const [dragActive, setDragActive] = useState(false); const progressValue = typeof progress === "number" && Number.isFinite(progress) ? Math.max(0, Math.min(100, progress)) : null; const showProgress = busy || progressValue !== null; const interactionDisabled = disabled || busy; const zoneClassName = `file-drop-zone ${dragActive ? "is-active" : ""} ${showProgress ? "is-busy" : ""} ${className}`.trim(); const roundedProgress = progressValue === null ? null : Math.round(progressValue); const progressStyle = progressValue === null ? undefined : { "--file-drop-progress": `${progressValue}%` } as CSSProperties; async function handleFiles(fileList: FileList | File[]) { if (interactionDisabled) return; const files = Array.from(fileList); if (files.length === 0) return; try { await onFiles(files); } finally { if (inputRef.current) inputRef.current.value = ""; } } return ( <>
{ if (!interactionDisabled) inputRef.current?.click(); }} onKeyDown={(event) => { if ((event.key === "Enter" || event.key === " ") && !interactionDisabled) { event.preventDefault(); inputRef.current?.click(); } }} onDragOver={(event) => { event.preventDefault(); if (!interactionDisabled) setDragActive(true); }} onDragLeave={() => setDragActive(false)} onDrop={(event) => { event.preventDefault(); setDragActive(false); if (!interactionDisabled) void handleFiles(event.dataTransfer.files); }} > {showProgress ? ( {roundedProgress === null ? "" : `${roundedProgress}%`} ) : (
event.target.files && void handleFiles(event.target.files)} /> ); }