import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks'
import * as ChipInputStories from './ChipInput.stories'

<Meta of={ChipInputStories} />

# ChipInput

A multi-select input: chips for the chosen, a typeahead for the rest.
Nothing in it knows what is being picked — people, labels, files — the
caller hands in `options` and, when the entries have faces, the two
leading slots.

<Canvas of={ChipInputStories.WithChips} />

## When

- Choosing several entries from a directory: members for a group,
  recipients, tags.
- **InputChip** is exported on its own for the display-only case — chosen
  things shown somewhere no editing happens.

<Canvas of={ChipInputStories.TheChipItself} />

## When not

- One value → **Select**.
- Free text that becomes an entry — it only picks from `options`; if
  typing must create things, that is your surface's logic around it.

## How

```tsx
import { ChipInput } from '@estiva-app/ui'

const directory: { id: string; label: string; picture?: string }[] = people

<ChipInput
  value={chosen}
  onChange={setChosen}
  options={directory}
  chipLeading={(o) => <Avatar name={o.label} src={o.picture} size={16} />}
  rowLeading={(o) => <Avatar name={o.label} src={o.picture} size={32} />}
  excludeIds={[viewer.id]}
/>
```

- An option is `{ id, label, description? }` — the description is the
  suggestion row's second line, and is searched too. The component is
  **generic over your option type**: `onChange` hands back your own
  objects, extra fields and all, no re-mapping.
- `chipLeading` is 16px in a chip; `rowLeading` is 32px in a suggestion
  row. The chip's padding follows its contents — a face sits 2px from the
  edge, a bare label gets 8px.
- Suggestions appear **only once the user types** — focusing must not drop
  the whole directory over the surface below.
- It is a **combobox over a listbox**: focus stays in the text while the
  arrow keys move through the suggestions, and a screen reader is told which
  one is highlighted. The list is as wide as the field and hangs below it,
  or above when there is no room.
- **The field's name.** Inside a `Field`, the label names it — pass nothing.
  Anywhere else, pass `aria-label`, or `aria-labelledby` pointing at a
  visible label such as a "To:". With neither, the placeholder is the name,
  and it stays the name after the first chip stops it being drawn.

```tsx
<span id="to-label">To:</span>
<ChipInput aria-labelledby="to-label" value={chosen} onChange={setChosen} options={directory} />
```

- **`InputChip` on its own** names its ✕ `Remove <label>`. Where the ✕ does
  something else, say what with `removeLabel` (a chip for a group you belong to: "Leave Design").
- To cap a long label, give the chip a `max-w-*` and `truncate`: the label is
  cut and the ✕ keeps its size. `truncate` is off unless asked, because
  cutting clips a letter's soft edge by up to 4px at 1x even when the label
  fits.

<Canvas of={ChipInputStories.ALongLabelCut} />

## What it owns

It does these by itself. Write none of them around it.

| It does | So you never write | Refused when written by hand |
|---|---|---|
| Floats on top of the page: its suggestions | a portal, or a `z-index` to win | ✓ |
| Moves through its items with the arrow keys, through the suggestions | an arrow-key handler | ✓ |
| Says what it is to assistive technology: a combobox and its options | `role="combobox"`, `listbox` or `option` | ✓ |
| Filters as you type; Enter adds the highlighted entry, Backspace on an empty query removes the last chip | a filter, or key handlers |  |
| Escape clears the query first, and only then lets the surface around it act | an Escape handler |  |

## Keys

| Input | What happens |
|---|---|
| typing | filters; suggestions appear |
| ↑ / ↓ | move the highlight |
| Enter | adds the highlighted entry |
| click a suggestion | adds it; focus stays in the text |
| ✕ on a chip | removes that chip |
| Backspace, empty query | removes the last chip — consumed, never the surface's "back" |
| Escape, with a query | clears the query — consumed |
| Escape, idle | bubbles, so the dialog or launcher around it can act |

## Props

<Controls of={ChipInputStories.TypeToSearch} />
