Composants
FileInput
Le champ de téléversement du DS : sélection, glisser-déposer, statut et dropzone. Fonctionne en
mode contrôlé (value/onChange) ou non-contrôlé
(name pour une Server Action). Valeur : File | File[] | null.
<input type="file">
masqué via l'API DataTransfer — Astryx n'expose pas de name. L'attribut
required n'est volontairement pas posé sur l'input caché (piège de validation orpheline) :
la présence du fichier se vérifie côté serveur.
Mode input (compact)
import { FileInput } from "@forme-ch/ui/controls";
<FileInput
label="Pièce jointe"
mode="input"
name="piece"
accept="application/pdf"
placeholder="Choisir un fichier PDF"
/> Mode dropzone
<FileInput
label="Documents"
mode="dropzone"
name="docs"
isMultiple
accept="image/*,application/pdf"
description="PDF ou images — 5 Mo max par fichier."
maxSize={5 * 1024 * 1024}
/> Contrôlé
Avec value + onChange, tu gardes la main sur la liste de fichiers (comptage, aperçus, validation).
const [files, setFiles] = useState<File | File[] | null>(null);
<FileInput
label="Justificatifs"
mode="dropzone"
isMultiple
value={files}
onChange={setFiles}
description={files ? "…" : "Glisse tes fichiers ici."}
/> Props
| Prop | Type | Défaut | Description |
|---|---|---|---|
label * | string | — | Libellé a11y obligatoire. Masqué visuellement si isLabelHidden. |
isLabelHidden | boolean | false | Masque visuellement le libellé (a11y conservée). |
mode | "input" | "dropzone" | — | « input » compact ou « dropzone » (grande zone glisser-déposer). |
name | string | — | Mode non-contrôlé (form / Server Action) : nom du champ FormData. |
value | File | File[] | null | — | Mode contrôlé : fichier(s) sélectionné(s). |
onChange | (files: File | File[] | null) => void | — | Appelé à chaque changement de sélection. |
accept | string | — | Types MIME acceptés (ex. application/pdf). |
isMultiple | boolean | — | Autorise plusieurs fichiers. |
maxSize | number | — | Taille max par fichier (octets). |
maxFiles | number | — | Nombre max de fichiers. |
isDisabled | boolean | — | Désactive le champ. |
isRequired | boolean | — | Champ requis (validé côté Server Action). |
isLoading | boolean | — | État de chargement (upload en cours). |
description | string | — | Texte d'aide sous le champ. |
placeholder | string | — | Texte de la zone vide. |
startIcon | ReactNode | — | Icône de tête. |
Accepte aussi width, className, style et labelTooltip.
Accessibilité
label est obligatoire — c'est le nom accessible du champ,
même si tu le masques visuellement avec isLabelHidden. Le
description énonce les contraintes (formats, poids) et doit rester lisible.
L'input masqué du mode non-contrôlé porte aria-hidden et
tabIndex=-1 pour ne pas doubler la cible clavier.
Do & Don't
✓ À faire
Annoncer les formats et la taille max dans description. Valider la présence du fichier côté Server Action.
✕ À éviter
Compter sur required pour bloquer l'envoi. Masquer le label sans le renseigner.