FileUpload is a file field: a drop zone, the platform's own picker, and a list of what was
chosen. It receives and returns `File` objects and does nothing else with them, it never reads a
file, uploads it or stores it, so where the bytes go is entirely your decision. The one rule the
field applies itself is the size limit. Like every member of the [field family](/concepts/fields)
it draws a label, a description and a message, and reports its own status.

```tsx demo="forms/file-upload-preview"
```

## Usage

```tsx
import { FileUpload } from "@asheeui/web";

const [files, setFiles] = useState<File[]>([]);

<FileUpload
  label="Attachments"
  files={files}
  onFilesSelected={(chosen) => setFiles((current) => [...current...chosen])}
  onRemove={(file) => setFiles((current) => current.filter((next) => next !== file))}
/>
```

`onFilesSelected` reports what the reader chose and `files` tells the field what to list. That is
the whole contract: no `value` to keep in sync, no request, and no list the field maintains
itself. A `File` is the browser's own object, so it can go straight into `FormData`, and it is
also why the field cannot list a file it was not handed.

## The size limit

`maxSize` is a byte count. A file larger than it is left out before `onFilesSelected` runs, so
the consumer never has to filter what it was given. The field reports the refusal itself, in its
message, with the count and the limit in the reader's terms.

```tsx demo="forms/file-upload-limit"
```

Drops and picks take the same path, so dragging a file past the limit is refused exactly as
choosing one is. There is one message channel and the component does not stack two: a `message`
you pass outranks the rejection, because a field never shows two things at once. `accept` and
`multiple` are the picker's settings rather than validation, `accept` narrows what the picker
offers, and a file dragged in is not re-checked against it.

## The list belongs to you

The field lists the array you pass and removes the file you tell it to. Without `onRemove` there
is no remove control on the chips, which is the right shape for a field whose files are managed
by something else.

```tsx demo="forms/file-upload-controlled"
```

The field keeps no copy of the list: the array, the order, the duplicates and the decision to
clear it all belong to the page. That is what lets a form library own the same list, and what
keeps a second upload widget from writing over the first.

## Props

```props FileUpload
```

Three values are registered and can be restated once for the whole application under
`components.fileupload`: `buttonLabel` (`Choose files`), `hint` (`Drag files here`) and `size`
(`md`). `status` is `default`, `isDisabled` and `required` are off, and `maxSize` is unset, which
means no limit rather than a default one. The zone is drawn from the file field's own classes
rather than from the field axes, so it has no `radius` or `variant` to configure.

`required` states the requirement twice from one prop: the marker beside the label, and the
`required` attribute on the underlying `<input type="file">`, so a native form refuses an empty
submission and the two cannot disagree.

## Accessibility

- The picker is a real `<input type="file">`: it is visually hidden but still in the tab order, so
  the keyboard path is the platform's own and nothing about choosing a file is drag-only.
  `isDisabled` disables that input and ignores drops, and dims the zone so the state is visible.
- The zone is a `<label>` for that input, so a pointer anywhere in the zone opens the picker, and
  the zone shows keyboard focus through `focus-within`, the hidden input's focus is drawn on the
  visible control.
- Each listed file is a chip whose close control is labelled `Remove <file name>`, so a screen
  reader announces which file a press removes instead of announcing a row of identical buttons.
- A refusal is reported in the field's message with an `error` status, which is a live region: the
  reader is told which files were left out and why, in the same place a validation message
  appears.

## Related

```related form, chip, input
```
