Files
gerbeur/src/components/form/FileField.tsx
2026-06-30 13:41:36 +02:00

63 lines
1.5 KiB
TypeScript

import {
Controller,
type FieldValues,
type Path,
type RegisterOptions,
useFormContext,
} from "react-hook-form";
import { FileDropZone } from "../FileDropZone.tsx";
interface FileFieldProps<T extends FieldValues> {
name: Path<T>;
label?: string;
hint?: string;
showLimit?: boolean;
disabled?: boolean;
rules?: RegisterOptions<T, Path<T>>;
/** Side effect run after the field value changes (e.g. derive a title). */
onValueChange?: (file: File | null) => void;
}
/** `Controller`-wrapped {@link FileDropZone} bound to react-hook-form. */
export function FileField<T extends FieldValues>({
name,
label,
hint,
showLimit,
disabled,
rules,
onValueChange,
}: FileFieldProps<T>) {
const { control, formState: { errors } } = useFormContext<T>();
const error = errors[name];
return (
<div className="form-field">
{label && <span className="form-label">{label}</span>}
<Controller
name={name}
control={control}
rules={rules}
render={({ field }) => (
<FileDropZone
file={(field.value as File | null) ?? null}
onChange={(file) => {
field.onChange(file);
onValueChange?.(file);
}}
hint={hint}
showLimit={showLimit}
disabled={disabled}
/>
)}
/>
{error?.message && (
<span className="form-hint form-hint--error">
{String(error.message)}
</span>
)}
</div>
);
}