# Autocomplete

![Status](https://img.shields.io/badge/status-stable-brightgreen)

Componente de autocompletar com sugestões dinâmicas. Suporta seleção simples e múltipla (chips), carregamento lazy, virtualização de lista e acessibilidade ARIA completa. Implementa `ControlValueAccessor` para integração com Angular Forms.

## Quando usar

- Campos de busca onde o usuário precisa selecionar um valor de uma lista grande
- Seleção de países, cidades, categorias ou qualquer entidade com muitas opções
- Campos onde o usuário pode adicionar múltiplos itens (modo múltiplo com chips)
- Quando a lista é carregada de uma API e não é viável trazê-la completa de uma vez (modo lazy)

## Quando não usar

- Quando há poucas opções fixas e conhecidas — use `Select` em vez disso, que é mais simples e acessível
- Quando o usuário não precisa digitar para filtrar — use `Dropdown` com lista completa
- Quando a entrada é livre (qualquer texto) — use `Input` simples

## Instalação

```typescript
import { AutocompleteComponent } from '@seniorsistemas/angular-components/autocomplete';

@Component({ standalone: true, imports: [AutocompleteComponent] })
export class MeuComponent {}
```

## Uso básico

```html
<s-autocomplete
  formControlName="pais"
  [suggestions]="sugestoesFiltradas"
  suggestionLabel="nome"
  placeholder="Buscar país..."
  (completeMethod)="filtrar($event)">
</s-autocomplete>
```

## API

### Inputs

| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|-------------|------|--------|:-----------:|-----------|
| `suggestions` | `T[]` | `[]` | não | Lista estática de sugestões exibidas no dropdown |
| `placeholder` | `string` | `undefined` | não | Texto de placeholder do campo |
| `suggestionLabel` | `keyof T` | `undefined` | não | Chave do objeto usada como texto de exibição |
| `suggestionValue` | `keyof T` | `undefined` | não | Chave do objeto cujo valor é emitido ao formulário |
| `delay` | `number` | `300` | não | Debounce em ms antes de acionar a busca |
| `minLengthToSearch` | `number` | `1` | não | Mínimo de caracteres para acionar a busca |
| `forceSelection` | `boolean` | `false` | não | Exige que o valor seja uma das sugestões |
| `multiple` | `boolean` | `false` | não | Habilita seleção de múltiplos itens como chips |
| `dropdown` | `boolean` | `false` | não | Exibe botão de seta para abrir a lista sem digitar |
| `checkmark` | `boolean` | `false` | não | Exibe ícone de check na opção selecionada |
| `lazy` | `boolean` | `false` | não | Ativa carregamento assíncrono via output `lazyLoad` |
| `virtualScroll` | `boolean` | `false` | não | Virtualiza a lista para melhor performance |
| `virtualScrollItemSize` | `number` | `37` | não | Altura em pixels de cada item no virtual scroll |
| `readonly` | `boolean` | `false` | não | Torna o campo somente leitura |
| `invalid` | `boolean` | `false` | não | Exibe estado de erro visual |
| `emptyMessage` | `string` | `undefined` | não | Mensagem quando nenhuma sugestão é encontrada |
| `dataKey` | `keyof T` | `undefined` | não | Chave para comparação de identidade entre opções |
| `inputClass` | `string` | `''` | não | Classe CSS adicional no `<input>` interno |
| `value` | `T \| null` | `undefined` | não | Valor selecionado (two-way binding, modo simples) |
| `values` | `T[]` | `[]` | não | Valores selecionados (two-way binding, modo múltiplo) |
| `disabled` | `boolean` | `false` | não | Estado desabilitado (two-way binding) |

### Outputs

| Evento | Tipo | Descrição |
|--------|------|-----------|
| `completeMethod` | `OutputEmitterRef<{ query: string }>` | Emitido ao digitar — use para filtrar sugestões localmente |
| `lazyLoad` | `OutputEmitterRef<{ query: string; response: (data: T[]) => void }>` | Emitido no modo lazy — chame `response(data)` para fornecer os dados |
| `selected` | `OutputEmitterRef<T>` | Emitido ao selecionar uma sugestão |
| `unselected` | `OutputEmitterRef<T>` | Emitido ao remover uma sugestão (modo múltiplo) |
| `blurred` | `OutputEmitterRef<Event>` | Emitido quando o campo perde foco |
| `focused` | `OutputEmitterRef<Event>` | Emitido quando o campo recebe foco |
| `cleared` | `OutputEmitterRef<void>` | Emitido quando o campo é limpo |
| `keyUp` | `OutputEmitterRef<Event>` | Emitido a cada tecla pressionada |

## Exemplos

### Seleção simples com filtragem local

```html
<form [formGroup]="form">
  <s-autocomplete
    formControlName="pais"
    [suggestions]="sugestoesFiltradas"
    suggestionLabel="nome"
    placeholder="Buscar país..."
    (completeMethod)="filtrar($event)">
  </s-autocomplete>
</form>
```

```typescript
filtrar(event: { query: string }) {
  this.sugestoesFiltradas = this.paises.filter(p =>
    p.nome.toLowerCase().includes(event.query.toLowerCase())
  );
}
```

### Seleção múltipla (chips)

```html
<s-autocomplete
  formControlName="paises"
  [suggestions]="sugestoesFiltradas"
  suggestionLabel="nome"
  dataKey="codigo"
  [multiple]="true"
  [checkmark]="true"
  placeholder="Adicionar países..."
  (completeMethod)="filtrar($event)">
</s-autocomplete>
```

### Lazy loading (dados de API)

```html
<s-autocomplete
  formControlName="pais"
  [suggestions]="sugestoes"
  suggestionLabel="nome"
  [lazy]="true"
  placeholder="Buscar país..."
  (lazyLoad)="onLazyLoad($event)">
</s-autocomplete>
```

```typescript
onLazyLoad(event: { query: string; response: (data: Pais[]) => void }) {
  this.apiService.buscar(event.query).subscribe(data => event.response(data));
}
```

### Com dropdown (lista sem digitar)

```html
<s-autocomplete
  formControlName="pais"
  [suggestions]="paises"
  suggestionLabel="nome"
  [dropdown]="true"
  placeholder="Selecione um país...">
</s-autocomplete>
```

## Acessibilidade

- O elemento host recebe `role="combobox"` com `aria-expanded` e `aria-haspopup="listbox"`
- Navegação com **Seta para baixo/cima** move o foco entre as sugestões
- **Enter** ou **Tab** confirma a sugestão em foco
- **Escape** fecha a lista sem selecionar
- O dropdown é associado ao input via `aria-owns`

## Componentes relacionados

- [`Chips`](../chips/README.md) — entrada de múltiplos valores em formato de tags sem sugestões de busca
