// Adapted from Mantine FileInput (MIT). See THIRD_PARTY_NOTICES.md. import { useId, useRef, useState, type ComponentProps, type CSSProperties, } from "react"; export interface FileInputProps extends Omit< ComponentProps<"input">, "type" | "value" | "defaultValue" | "onChange" | "className" | "style" > { label: string; description?: string; error?: string; placeholder?: string; clearable?: boolean; onChange?: (files: File[]) => void; className?: string; style?: CSSProperties; inputClassName?: string; inputStyle?: CSSProperties; } export function FileInput({ label, description, error, placeholder = "No files selected", clearable = true, onChange, id, multiple, disabled, className = "", style, inputClassName = "", inputStyle, "aria-describedby": describedBy, ...props }: FileInputProps) { const generatedId = useId(); const inputId = id ?? generatedId; const inputRef = useRef(null); const [files, setFiles] = useState([]); const hint = error ?? description; const hintId = `${inputId}-hint`; const fileNames = files.map((file) => file.name).join(", "); const updateFiles = (next: File[]) => { setFiles(next); onChange?.(next); }; return (
updateFiles(Array.from(event.currentTarget.files ?? [])) } aria-invalid={error ? true : props["aria-invalid"]} aria-describedby={[ describedBy, `${inputId}-selection`, hint ? hintId : undefined, ] .filter(Boolean) .join(" ")} /> {fileNames || placeholder} {clearable && files.length > 0 && !disabled && ( )}
{hint && (

{hint}

)}
); }