# File Input

## Usage

`FileInput` picks one file, or several. The value is a `File`, a `File[]` when `isMultiple` is set, or `null`.

```vue
<script setup lang="ts">
import { FileInput } from '@astryx-vue/core/FileInput'
import { shallowRef } from 'vue'

const resume = shallowRef<File | File[] | null>(null)
</script>

<template>
  <FileInput v-model="resume" label="Resume" accept=".pdf,.docx" />
</template>
```

`mode="input"` is the compact field. `mode="dropzone"` is a dashed target that also accepts a drop. Files that miss `accept`, `maxSize`, or `maxFiles` stay out of the value and the field shows an error. Placeholder, clear, and error text follow `provideAstryxLocale`.

### Anatomy

- **Label**: Text that identifies the field. Always rendered for accessibility even when visually hidden.
- **Description**: Helper text between the label and the drop zone explaining accepted formats or size limits.
- **Drop zone**: The clickable area for file selection. In dropzone mode, also accepts dragged files.
- **Upload icon**: An upload-to-tray icon in the drop zone hinting at the upload action.
- **Placeholder**: Hint text shown when no files are selected.
- **File name display**: Shows the name(s) of selected files.
- **Clear button**: A close button that removes selected files and returns focus to the input.
- **Spinner**: Loading indicator that appears during async upload actions.
- **Status message**: Validation feedback showing error, warning, or success with a message.

## Best practices

| Guideline | Practice |
| --- | --- |
| Do | Name the field with `label`, and put the accepted types in `description`. |
| Do | Set `maxSize` in bytes so an oversized file is rejected before upload. |
| Don't | Use the dropzone for a single required document that people expect to look like a text field. |

## Examples

### Basic

A resume field that accepts a PDF or Word document up to 5 MB.

```vue
<script setup lang="ts">
import { FileInput } from '@astryx-vue/core/FileInput'
import { shallowRef } from 'vue'

const value = shallowRef<File | File[] | null>(null)
</script>

<template>
  <div style="width: 350px">
    <FileInput
      v-model="value"
      label="Resume"
      accept=".pdf,.docx"
      description="PDF or Word document, up to 5 MB"
      :max-size="5 * 1024 * 1024"
    />
  </div>
</template>
```

## FileInput Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `v-model` | `File \| File[] \| null` | `null` | The selected file. An array when `isMultiple` is set. |
| `label` | `string` | — | Required accessible name. |
| `accept` | `string` | — | HTML accept list, such as `.pdf,.docx` or `image/*`. |
| `isMultiple` | `boolean` | `false` | Allow more than one file. |
| `maxSize` | `number` | — | Maximum size of one file, in bytes. |
| `maxFiles` | `number` | — | Maximum number of files when `isMultiple` is set. |
| `mode` | `'input' \| 'dropzone'` | `'input'` | Compact field, or a dashed drop target. |
| `placeholder` | `string` | `Choose file` | Text shown when nothing is selected. Follows the locale. |
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Field height. 28, 32, or 36px. |
| `status` | `FieldStatusInput` | — | Error, warning, or success. A rejected file supplies an error when this is omitted. |
| `statusVariant` | `'attached' \| 'detached' \| 'tooltip'` | `'attached'` | Where the status message sits. |
| `isDisabled` | `boolean` | `false` | Blocks the picker and drops. |
| `disabledMessage` | `string` | — | Explains why the field is disabled. With `isDisabled`, the message shows in a tooltip on hover or keyboard focus and the trigger stays focusable with `aria-disabled`; the picker still cannot open. |
| `labelTooltip` | `string` | — | Hint shown from the info icon at the end of the label. |
| `isRequired` | `boolean` | `false` | Marks the field required. |
| `isLoading` | `boolean` | `false` | Shows a spinner, for an upload in progress. |
| `description` | `string` | — | Help text under the label. |
| `width` | `number \| string` | — | Width of the whole field. Numbers are pixels. |
| `changeAction` | `(files) => void \| Promise<void>` | — | Called after a selection and after a clear. A rejection emits `actionError`. A spinner shows while it is pending. |
| `clearLabel` | `string` | — | Accessible name of the clear button. It includes the field label by default. The tooltip only says “Clear”. |
| `containerClass` | `string \| object \| array` | — | Class for the field container. Has no effect, and warns in development, inside InputGroup and in a horizontal-labels FormLayout. |
| `containerStyle` | `StyleValue` | — | Inline style for the field container. |
| `controlClass` | `string \| object \| array` | — | Class for the file field box. A class written on the component lands here too, with no warning. |
| `controlStyle` | `StyleValue` | — | Inline style for the file field box. A style written on the component lands here too. |

## Events

| Name | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `File \| File[] \| null` | Emitted when the selection changes. |
| `change` | `File \| File[] \| null` | Emitted with the same value, before `changeAction`. |
| `actionError` | `unknown, File \| File[] \| null` | Emitted when `changeAction` rejects. |
