"use client"; import { useId, useRef, useState, type ChangeEvent } from "react"; export function FileDropzone({ label, onFilesChange, accept = "", multiple = false, maxSize = 10 * 1024 * 1024, disabled, className = "", }: { label: string; onFilesChange: (files: File[]) => void; accept?: string; multiple?: boolean; maxSize?: number; disabled?: boolean; className?: string; }) { const id = useId(), input = useRef(null); const [dragging, setDragging] = useState(false), [errors, setErrors] = useState([]); const receive = (files: File[]) => { if (disabled) return; const accepted: File[] = [], messages: string[] = []; const types = accept .toLowerCase() .split(",") .map((v) => v.trim()) .filter(Boolean); for (const file of files) { if (file.size > maxSize) messages.push( `${file.name} exceeds ${Math.round(maxSize / 1024 / 1024)} MB.`, ); else if ( types.length && !types.some((t) => t.startsWith(".") ? file.name.toLowerCase().endsWith(t) : t.endsWith("/*") ? file.type.toLowerCase().startsWith(t.slice(0, -1)) : file.type.toLowerCase() === t, ) ) messages.push(`${file.name} has an unsupported file type.`); else accepted.push(file); } if (!multiple && accepted.length > 1) messages.push("Choose one file at a time."); setErrors(messages); if (accepted.length && (multiple || accepted.length === 1)) onFilesChange(accepted); }; const change = (e: ChangeEvent) => { receive(Array.from(e.target.files ?? [])); e.target.value = ""; }; return (

{accept || "All file types"} ยท up to {Math.round(maxSize / 1024 / 1024)}{" "} MB each

{!!errors.length && (
    {errors.map((error) => (
  • {error}
  • ))}
)}
); }