# File Uploader Card

Level: molecule.

## Purpose

`component.file-upload-card` is the compact, single-photo upload target for a
form. Use it when the user needs to select or drag one image and see its preview
in place. It is not a generic document uploader, a multi-file list, or the
wide File Uploader Inline drop zone.

## Confirmed Figma source

- Published `File Uploader Card` component set
  [`1589:9995`](https://www.figma.com/design/EO9tuCsqwuHL8cZ4Eb2psR/%F0%9F%9A%9B-WB-Logistics-UI-kit?node-id=1589-9995),
  key `dd9869467bc9afed2e8fa97c32b5fb848484f899`.
- The master defines only `Filled=No`; it is the source for the empty upload
  card, not for a loaded file preview.
- Its `Show text` property and eight `State` variants are documented in the
  sibling [Figma assembly contract](wbl-file-uploader-card.figma.md).

The nearby published
[`File Card` `4854:78239`](https://www.figma.com/design/EO9tuCsqwuHL8cZ4Eb2psR/%F0%9F%9A%9B-WB-Logistics-UI-kit?node-id=4854-78239)
is a distinct `component.file` master. Its `Image`/`File`, filename/size, and
loading variants do not turn it into a filled state of this component.

## Anatomy

- 120 × 108px upload surface with a 16px radius.
- Native image file input, reached through the visible upload button.
- Shared 24px add icon for every empty-card state, including error.
- Optional centred supporting text; optional error text below it.
- Once a local `file` is supplied, an image preview and an action menu replace
  the empty upload button. The menu exposes download and delete actions.

## Behaviour and Figma state mapping

| Figma empty-card state | Local mapping |
| --- | --- |
| Default | `disabled=false`, `error=false`, `focused=false`, no active drag. |
| Hover | Native pointer hover; no public input is needed. |
| Drag-and-drop | An accepted drag sets the internal active-drag treatment; dropping follows the same validation as file selection. |
| Disabled | `disabled=true`; the button and native input are disabled and drops cannot select a file. |
| Focused | `focused=true` or native `:focus-visible`; the outer focus treatment is shown. |
| Error | `error=true`; local text and error text remain independently configurable. |
| Error Hover | `error=true` with native hover. |
| Error Focus | `error=true` with `focused=true` or native `:focus-visible`. |

Figma `Show text` maps to local `text`. The local `errorText` input is an
intentional separate control; it permits an error message to be hidden or
shown without changing the regular label. Figma has no `Filled=Yes` variant.
The local image preview, download/delete menu, and temporary loading indicator
are runtime behaviour, not a claimed mapping to `component.file`.

The browser picker accepts images only. The component additionally rejects
non-image files and images larger than 10 MiB, emitting `errorChange=true`; a
valid selection emits `errorChange=false` followed by `fileChange(file)`.

## Token usage

- Default: `--color-background-level-1-base`, dashed
  `--stroke-100` / `--color-stroke-secondary`, and `--radius-l`.
- Hover, drag, and disabled background: `--color-background-level-2`.
- Drag border: `--stroke-200` / `--color-stroke-accent`.
- Focus ring: `--stroke-400` / `--color-stroke-focused`.
- Error border and text: `--stroke-200` / `--color-stroke-danger` and
  `--color-text-icon-danger`; error focus uses
  `--color-content-background-red`.
- Label typography: `--typography-description-relaxed-puma`; normal, disabled, and
  error text use the corresponding text/icon semantic tokens.

## Accessibility

- The visible upload control is a native button with the configurable
  `ariaLabel` (`Загрузить фотографию` by default).
- Its empty/error state sets `aria-invalid` when `error=true`; error copy must
  describe the corrective action or failed validation.
- The hidden input is not in the tab sequence. Keyboard users reach the
  visible button and receive the native focus-visible treatment.
- Do not use a preview image as the only description of the required upload;
  give the surrounding form control a meaningful label.

## Deferred non-goals

- Generic document types, filenames, file sizes, and document icons belong to
  the separate Figma-only `component.file`.
- Multi-file limits and the broad M/S drop zone belong to the separate
  Figma-only `component.file-upload-inline`.
- This component does not define upload transport, persistence, or the
  receiving form's validation policy.

## Local component contract

This is the active Angular contract for `component.file-upload-card`; current
local code, focused test, export, and Storybook win over historical provenance.

### Classes and selector

- `WblFileUploaderCardComponent` — `wbl-file-uploader-card`
  (`src/app/design-system/patterns/file-uploader-card/file-uploader-card.component.ts`).

### Inputs

- `disabled = input(false)`
- `error = input(false)`
- `focused = input(false)`
- `text = input(true)`
- `textValue = input('Text')`
- `errorText = input(true)`
- `errorTextValue = input('Error')`
- `file = input<File | null>(null)`
- `ariaLabel = input('Загрузить фотографию')`

### Models and outputs

- Models: none.
- `fileChange = output<File | null>()`
- `errorChange = output<boolean>()`

### Slots and local evidence

- There is no Angular content-projection slot.
- Code: `src/app/design-system/patterns/file-uploader-card/file-uploader-card.component.ts`.
- Focused test: `src/app/design-system/patterns/file-uploader-card/file-uploader-card.component.spec.ts`.
- Public export: `src/app/design-system/index.ts`.
- Storybook: `storybook/stories/file-uploader-card.stories.ts`
  (`Design System/Patterns/wbl-file-uploader-card`).

## Provenance

The archived source document is historical migration evidence only. The active
sources are the local contract above and the verified Figma assembly sidecar.
