# TagsInput

Lista de marcadores que a pessoa escreve: etiquetas de uma nota, palavras de um
filtro, emails de um convite.

Três gestos que a peça resolve de uma vez, para não serem resolvidos cinco
vezes diferentes: o Enter fecha a ficha, o Backspace com o campo vazio tira a
última (é o gesto que todo mundo tenta primeiro) e a repetida não entra duas
vezes, porque marcar duas vezes a mesma coisa nunca é o que se quis. Sair do
campo também fecha o que estava escrito: texto digitado e não fechado some ao
enviar o formulário, e ninguém entende por quê.

Guarda a própria lista quando recebe só `defaultValue`, e obedece à de fora
quando recebe `value`, o mesmo par das outras peças de formulário. Num
formulário que envia, controle: quem guarda a lista é o app, porque é ele que a
manda. Num filtro de tela, que não envia nada, `defaultValue` poupa o
`useState`.

```tsx
<TagsInput defaultValue={['nf-e']} aria-label="Palavras do filtro" />
```

## O rótulo

Embrulhe num `Field` com `FieldLabel`, como qualquer campo da casa. O campo de
escrever passa pelo `Field.Control`: é ele, e não a moldura das fichas, que
recebe o `id` do rótulo, o `aria-describedby` da ajuda e do erro, e o
`aria-invalid`. Clicar no rótulo foca o campo, e a moldura fica vermelha
quando o `Field` está inválido.

```tsx
<Field>
  <FieldLabel>Marcadores</FieldLabel>
  <TagsInput value={tags} onValueChange={setTags} placeholder="Escreva e tecle Enter" />
  <FieldDescription>Enter fecha a ficha.</FieldDescription>
</Field>
```

O `placeholder` não é rótulo: ele some no instante em que a pessoa digita, e
vários leitores de tela não o anunciam. Fora de um `Field`, dê `aria-label`.

O anel de foco é do campo de escrever, e não da moldura. O xis de cada ficha
tem anel próprio, e os dois nunca acendem juntos.

## O nome do xis

Cada ficha diz o que se remove: `labels.remove` recebe o texto dela e devolve o
nome que o leitor de tela ouve. Sem isso, uma fila de fichas se anuncia
"Remover, Remover, Remover", e quem depende do leitor não sabe qual botão é
qual.

```tsx
<TagsInput
  defaultValue={['nf-e']}
  aria-label="Marcadores"
  labels={{ remove: (tag) => `Tirar o marcador ${tag}` }}
/>
```

## Movimento

Só a ficha que chega depois cresce ao entrar (`animate-pop`, `--rc-duration-fast`). As que já vinham no valor nascem paradas: elas são o formulário, e não algo que acabou de acontecer.

## Quando não usar

Quando as opções já existem, use `Combobox` com `multiple` e as fichas: ele
mostra o catálogo antes de deixar escolher. O `TagsInput` é para quando a lista
nasce do que se digita e não há o que sugerir.

## No React Native

Traduz, com um gesto a menos. O Enter fecha a ficha e o separador digitado também, mas ele é lido no texto, e não na tecla, porque o `onKeyPress` do Android não chega para o teclado do sistema. É esse mesmo evento que faltava para o Backspace com o campo vazio tirar a última ficha, e por isso ele não porta: no celular a ficha se tira pelo xis, que já precisava existir para o dedo. O resto é igual: a peça é controlada, a repetida não entra duas vezes e sair do campo fecha o que estava meio escrito.

## Importação

```tsx
import { TagsInput } from '@rivocode/ui'
```

## Exemplos

### Marcadores da nota

```tsx
import { useState } from 'react'
import { Field, FieldDescription, FieldLabel, TagsInput } from '@rivocode/ui'

export function InvoiceTags() {
  const [tags, setTags] = useState(['nf-e', 'urgente'])

  return (
    <div className="w-80">
      <Field>
        <FieldLabel>Marcadores</FieldLabel>
        <TagsInput value={tags} onValueChange={setTags} placeholder="Escreva e tecle Enter" />
        <FieldDescription>Enter fecha a ficha; apagar com o campo vazio tira a última.</FieldDescription>
      </Field>
    </div>
  )
}
```

## Props

| Prop | Tipo | Obrigatória | Desde | O que faz |
| --- | --- | --- | --- | --- |
| `classNames` | `Partial<Record<"field" \| "input" \| "remove" \| "tag", string>>` |  | 0.5.0 | Classe por parte: `field`, `tag`, `remove`, `input`. |
| `defaultValue` | `string[]` |  | - | As fichas do primeiro desenho, quando a peca guarda a propria lista. |
| `labels` | `{ remove?: ((tag: string) => string) \| undefined; }` |  | - | O que o leitor de tela ouve nos botoes da peca. |
| `onValueChange` | `((value: string[]) => void)` |  | 0.5.0 | Avisado com a lista inteira a cada ficha que entra ou sai. |
| `separators` | `string[]` |  | 0.5.0 | O que fecha uma ficha alem do Enter. |
| `value` | `string[]` |  | 0.5.0 | As fichas de agora, quando quem usa guarda a lista. |

Além dessas: repassa `className`, `style`, `id` e os demais atributos do elemento raiz.

## Ver também

- [Autocomplete](/componentes/autocomplete.md)
- [Calendar](/componentes/calendar.md)
- [Checkbox](/componentes/checkbox.md)
- [CheckboxGroup](/componentes/checkbox-group.md)
- [ColorPicker](/componentes/color-picker.md)
- [Combobox](/componentes/combobox.md)
- [Convenções da biblioteca](/convencoes.md): Provider, tokens e as regras que valem para toda peça
- [Índice completo](/llms.txt)
