# Select

Lista de seleção com gatilho personalizado, listbox flutuante e suporte a teclado.

> Documentação do Sinapse DS. Snippets shadcn usam os componentes do seu projeto (`@/components/ui/*`); snippets HTML usam as classes `itps-*` do CSS Sinapse (`/css/<versão>/`, veja `/comecar.md`). Componentes do site de documentação não fazem parte do DS.

- Categoria: Forms
- Status: stable
- Relacionados: /componentes/field.md, /componentes/textfield.md, /componentes/search.md

## Em cada stack

- HTML e Astro: Classes `itps-*` do CSS Sinapse, com todas as variantes desta página.
- React e Vue: Componentes padrão do shadcn (`select`) com o tema Sinapse: cores, fonte e raio vêm do tema, e as variantes `itps-*` desta página existem só no HTML.

**React/Next (shadcn/ui)**

```tsx
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'

export function SelectExample() {
  return (
    <Select>
      <SelectTrigger className="w-[240px]" aria-label="Convênio">
        <SelectValue placeholder="Selecione o convênio" />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="sus">SUS</SelectItem>
        <SelectItem value="amil">Amil</SelectItem>
        <SelectItem value="bradesco">Bradesco Saúde</SelectItem>
      </SelectContent>
    </Select>
  )
}
```

**Vue 3/Nuxt 4 (shadcn-vue)**

```vue
<script setup lang="ts">
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
</script>

<template>
  <Select>
    <SelectTrigger class="w-[240px]" aria-label="Convênio">
      <SelectValue placeholder="Selecione o convênio" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="sus">SUS</SelectItem>
      <SelectItem value="amil">Amil</SelectItem>
      <SelectItem value="bradesco">Bradesco Saúde</SelectItem>
    </SelectContent>
  </Select>
</template>
```

**HTML (copy-first)**

```html
<div class="itps-select" data-itps-select data-state="closed">
  <button
    type="button"
    id="select-convenio"
    role="combobox"
    aria-label="Convênio"
    aria-haspopup="listbox"
    aria-expanded="false"
    aria-controls="select-convenio-listbox"
    class="itps-select__trigger"
    data-itps-select-trigger
  >
    <span class="itps-select__value" data-itps-select-value data-placeholder="true">
      Selecione o convênio
    </span>
    <i class="itps-select__chevron ti ti-chevron-down" aria-hidden="true"></i>
  </button>

  <input type="hidden" name="convenio" value="" class="itps-select__input" data-itps-select-input />

  <ul
    id="select-convenio-listbox"
    role="listbox"
    aria-labelledby="select-convenio"
    class="itps-select__panel"
    data-itps-select-panel
  >
    <li
      id="select-convenio-listbox-option-0"
      role="option"
      data-value="sus"
      data-itps-select-option
      aria-selected="false"
      tabindex="-1"
      class="itps-select__option"
    >
      <div class="itps-select__option-content">
        <span class="itps-select__option-label" data-itps-select-option-label>SUS</span>
      </div>
    </li>
    <li
      id="select-convenio-listbox-option-1"
      role="option"
      data-value="amil"
      data-itps-select-option
      aria-selected="false"
      tabindex="-1"
      class="itps-select__option"
    >
      <div class="itps-select__option-content">
        <span class="itps-select__option-label" data-itps-select-option-label>Amil</span>
      </div>
    </li>
    <li
      id="select-convenio-listbox-option-2"
      role="option"
      data-value="bradesco"
      data-itps-select-option
      aria-selected="false"
      tabindex="-1"
      class="itps-select__option"
    >
      <div class="itps-select__option-content">
        <span class="itps-select__option-label" data-itps-select-option-label>Bradesco Saúde</span>
      </div>
    </li>
  </ul>
</div>
```

## Instalação

O comando instala o Select da sua stack com o tema Sinapse. Configure o registry uma vez por projeto em [Comece por aqui](/comecar).

**terminal · React/Next**

```sh
pnpm dlx shadcn@latest add @sinapse/select
```

**terminal · Vue 3/Nuxt 4**

```sh
pnpm dlx shadcn-vue@latest add https://dsystem.sinapse.org.br/r/vue/select.json
```

Sem shadcn, não há instalação por componente: use o CSS Sinapse e as classes da API copy-first desta página.

## Uso

Em React/Next com shadcn/ui e Vue 3/Nuxt 4 com shadcn-vue, use o `Select` composto da stack. O tema Sinapse vem dos tokens instalados pelo CLI do shadcn ou do shadcn-vue; o exemplo segue a API oficial de componentes compostos.

Use `itps-select` quando precisar do contrato copy-first em HTML ou wrappers finos. Este site usa `src/scripts/forms.ts` (não incluído no pacote) para inicializar os elementos com `data-itps-select*`; no produto, implemente o mesmo contrato ou use o componente da sua stack.

## Exemplos

### Opções

| Necessidade | React/Next e Vue 3/Nuxt 4 | HTML |
|-------------|--------------------------|------|
| Placeholder | `SelectValue placeholder="..."` | `itps-select__value[data-placeholder="true"]` |
| Opção simples | `SelectItem value="sus"` | `li[role="option"][data-value="sus"]` |
| Opção com descrição | componha conteúdo dentro de `SelectItem` quando a stack permitir | `itps-select__option-description` |
| Valor enviado | estado controlado ou formulário da stack | `input[type="hidden"][data-itps-select-input]` |

### Tamanhos e estados

| Estado | React/Next e Vue 3/Nuxt 4 | HTML |
|--------|--------------------------|------|
| Pequeno | ajuste no `SelectTrigger` instalado | `itps-select--sm` |
| Padrão | padrão do componente | sem modificador |
| Grande | ajuste no `SelectTrigger` instalado | `itps-select--lg` |
| Desabilitado | `disabled` | `disabled` no trigger |
| Inválido | `aria-invalid="true"` | `aria-invalid="true"` no trigger |

Conecte textos de apoio ou erro com `aria-describedby`. Para valor controlado em React/Vue, siga o estado da stack em vez de copiar o JS do exemplo.

### Com Field

Quando houver label, obrigatório, apoio ou erro, componha com `Field` e conecte o texto auxiliar ao trigger.

**HTML (copy-first)**

```html
<div class="itps-field">
  <label for="select-field-convenio" class="itps-field__label">
    Convênio
    <span class="itps-field__required" aria-hidden="true">*</span>
  </label>

  <div class="itps-select" data-itps-select data-state="closed">
    <button
      type="button"
      id="select-field-convenio"
      role="combobox"
      aria-haspopup="listbox"
      aria-expanded="false"
      aria-controls="select-field-convenio-listbox"
      aria-required="true"
      aria-describedby="select-field-convenio-help"
      class="itps-select__trigger"
      data-itps-select-trigger
    >
      <span class="itps-select__value" data-itps-select-value data-placeholder="true">
        Selecione
      </span>
      <i class="itps-select__chevron ti ti-chevron-down" aria-hidden="true"></i>
    </button>

    <input type="hidden" name="field-convenio" value="" class="itps-select__input" data-itps-select-input />

    <ul
      id="select-field-convenio-listbox"
      role="listbox"
      aria-labelledby="select-field-convenio"
      class="itps-select__panel"
      data-itps-select-panel
    >
      <li
        id="select-field-convenio-listbox-option-0"
        role="option"
        data-value="sus"
        data-itps-select-option
        aria-selected="false"
        tabindex="-1"
        class="itps-select__option"
      >
        <div class="itps-select__option-content">
          <span class="itps-select__option-label" data-itps-select-option-label>SUS</span>
          <span class="itps-select__option-description">Atendimento público</span>
        </div>
      </li>
      <li
        id="select-field-convenio-listbox-option-1"
        role="option"
        data-value="particular"
        data-itps-select-option
        aria-selected="false"
        tabindex="-1"
        class="itps-select__option"
      >
        <div class="itps-select__option-content">
          <span class="itps-select__option-label" data-itps-select-option-label>Particular</span>
          <span class="itps-select__option-description">Pagamento direto</span>
        </div>
      </li>
    </ul>
  </div>

  <p id="select-field-convenio-help" class="itps-field__hint">
    Obrigatório para confirmar o cadastro.
  </p>
</div>
```

## API copy-first

| Classe/atributo | Uso |
|-----------------|-----|
| `itps-select` | Wrapper visual e raiz de estado. |
| `data-itps-select` | Marca a raiz inicializável pelo script. |
| `itps-select__trigger` | Botão com `role="combobox"`. |
| `data-itps-select-trigger` | Elemento que abre e fecha o painel. |
| `itps-select__value` | Texto visível ou placeholder. |
| `data-itps-select-value` | Elemento atualizado ao selecionar opção. |
| `itps-select__input` | Campo oculto que envia o valor no formulário. |
| `data-itps-select-input` | Input sincronizado pelo script. |
| `itps-select__panel` | Painel com `role="listbox"`. |
| `data-itps-select-panel` | Lista usada para navegação por teclado. |
| `itps-select__option` | Item com `role="option"`. |
| `data-itps-select-option` | Opção selecionável pelo script. |
| `data-state` | Expõe estado visual aberto ou fechado. |
| `aria-expanded` | Comunica se o painel está aberto. |
| `aria-selected` | Comunica a opção selecionada. |
| `aria-describedby` | Liga o trigger ao texto de apoio ou erro. |

## Acessibilidade

Use `role="combobox"` no trigger, `role="listbox"` no painel e `role="option"` em cada item. Atualize `aria-expanded` no trigger e `aria-selected` na opção selecionada. O teclado esperado é: Enter, Space ou ArrowDown abre; ArrowUp/ArrowDown navegam; Home/End vão aos extremos; Enter/Space seleciona; Escape fecha; Tab segue o fluxo.
