Autocomplete
Campo de texto que exibe sugestões de uma fonte remota e confirma uma delas como valor. O `mk-autocomplete` emite a query digitada e renderiza exatamente as options que você devolver — ele não filtra.
O mk-autocomplete é o campo de busca com sugestões do Makuco UI. Use quando o conjunto de valores possíveis é grande demais para um mk-select — uma base de clientes, um catálogo de produtos, um serviço de CEP. O componente cuida do posicionamento do dropdown, da navegação por teclado e do contrato ARIA 1.2 de combobox.
O componente não filtra
A lista exibida é sempre exatamente options. Escute mkInput para buscar (ou filtrar localmente) e
devolva o resultado em options. Passar três options com uma query que não casa nenhuma exibe as três.
Integração com Angular Forms
Para usar com ngModel ou FormControl, importe MkAutocompleteModule. O model liga em
selectedValue — a seleção confirmada — e não no rascunho digitado. Veja Formulários com
Angular.
Padrão
Digite ao menos dois caracteres. O exemplo abaixo faz uma request real à DummyJSON API e busca usuários pelo nome — sem simulação.
import { useState } from 'react';
import { MkAutocomplete } from '@db1/makuco-ui-react';
import type { AutocompleteOption } from '@db1/makuco-ui-core';
const [options, setOptions] = useState<AutocompleteOption[]>([]);
const [loading, setLoading] = useState(false);
<MkAutocomplete
label="Colaborador"
placeholder="Busque pelo nome"
options={options}
loading={loading}
supportText="Busca em tempo real via DummyJSON API"
onMkInput={async (event) => {
if (!event.detail) return setOptions([]);
setLoading(true);
const res = await fetch(
`https://dummyjson.com/users/search?q=${encodeURIComponent(event.detail)}&limit=8`
);
const data = await res.json();
setOptions(
(data.users ?? []).map((u) => ({ value: u.id, label: `${u.firstName} ${u.lastName}` }))
);
setLoading(false);
}}
onMkChange={(event) => console.log(event.detail)}
/>// component.ts
import type { AutocompleteOption } from '@db1/makuco-ui-core';
options: AutocompleteOption[] = [];
loading = false;
async search(event: CustomEvent<string>) {
if (!event.detail) { this.options = []; return; }
this.loading = true;
const res = await fetch(
`https://dummyjson.com/users/search?q=${encodeURIComponent(event.detail)}&limit=8`
);
const data = await res.json();
this.options = (data.users ?? []).map((u: any) => ({
value: u.id,
label: `${u.firstName} ${u.lastName}`,
}));
this.loading = false;
}<mk-autocomplete
label="Colaborador"
placeholder="Busque pelo nome"
[options]="options"
[loading]="loading"
support-text="Busca em tempo real via DummyJSON API"
(mkInput)="search($event)"
(mkChange)="save($event)"
></mk-autocomplete>Os dois valores
Um autocomplete tem dois estados, e é isso que o separa de todo campo simples: o texto no campo e a seleção confirmada.
| Prop | O que é | Vai para o formulário |
|---|---|---|
value | O rascunho — o texto digitado. | Nunca |
selectedValue | A seleção confirmada — o optionValue da option escolhida. | Sim, via setFormValue |
A sincronização é de uma direção só: definir selectedValue escreve o label da option correspondente em value; digitar não mexe em selectedValue.
Para o label ser resolvido, a option precisa estar em options — ao carregar um registro existente, entregue os dois juntos.
// `options` já contém a option de `selectedValue`, senão o campo aparece vazio.
<MkAutocomplete label="Cidade" options={[{ value: 'cwb', label: 'Curitiba' }]} selectedValue="cwb" /><mk-autocomplete label="Cidade" [options]="options" [(ngModel)]="cityId"></mk-autocomplete>Filtro local
Se os dados já estão em memória, o filtro é uma linha no handler de mkInput.
<MkAutocomplete
label="Cidade"
options={options}
onMkInput={(event) => {
const query = event.detail.toLowerCase();
setOptions(ALL_CITIES.filter((city) => city.label.toLowerCase().includes(query)));
}}
/>Reatribua o array: options.push(x) não muda a referência e o componente não re-renderiza.
filter(event: CustomEvent<string>) {
const query = event.detail.toLowerCase();
// Novo array — mutar in-place não dispara o re-render.
this.options = ALL_CITIES.filter((c) => c.label.toLowerCase().includes(query));
}minLength e debounce
minLength (padrão 2) e debounce (padrão 300) existem para não disparar uma request por tecla. minLength é gate de abertura, não só de emissão: com o rascunho abaixo do piso, o dropdown não abre nem que options esteja populado.
Para sinalizar o piso ao usuário, use supportText — o componente não gera uma frase automática.
<MkAutocomplete
label="Cidade"
options={options}
minLength={3}
debounce={600}
supportText="Digite ao menos 3 caracteres"
/><mk-autocomplete
label="Cidade"
[options]="options"
[minLength]="3"
[debounce]="600"
support-text="Digite ao menos 3 caracteres"
></mk-autocomplete>Carregando
loading troca a lupa (ou o ×) por um spinner no trailing. Com options vazio, o dropdown não abre — só o spinner aparece; loading também suprime a mensagem de vazio, evitando que ela apareça antes da resposta chegar.
<MkAutocomplete label="Cidade" options={options} loading /><mk-autocomplete label="Cidade" [options]="options" [loading]="true"></mk-autocomplete>Sem resultado
Uma linha única com a lupa e a query interpolada, para o usuário reconhecer o que digitou. emptyMessage substitui o texto inteiro — sem interpolação, você monta a frase.
<MkAutocomplete
label="Cidade"
options={options}
emptyMessage="Nenhuma cidade encontrada nesta região."
/><mk-autocomplete
label="Cidade"
[options]="options"
empty-message="Nenhuma cidade encontrada nesta região."
></mk-autocomplete>Valor livre
allowCustomValue confirma o rascunho como está — por Enter sem nada em foco, ou no blur — e emite mkChange com option: undefined.
Combinado com required, a obrigatoriedade passa a ser satisfeita por qualquer texto confirmado, não por uma option da lista. Se o backend precisa de um id da base, não use esta prop.
<MkAutocomplete
label="Cidade"
options={options}
allowCustomValue
onMkChange={(event) => {
// event.detail.option é undefined quando o texto não veio da lista
save(event.detail.value);
}}
/><mk-autocomplete
label="Cidade"
[options]="options"
[allowCustomValue]="true"
(mkChange)="save($event)"
></mk-autocomplete>Obrigatório e erro
required valida a seleção, não o texto: um campo com "Curi" digitado mas nada confirmado continua inválido. errorMessage põe o campo em estado inválido, com borda em --stroke-error e aria-invalid="true" no input.
<MkAutocomplete
label="Cidade"
options={options}
required
errorMessage="Selecione uma cidade da lista"
/><mk-autocomplete
label="Cidade"
[options]="options"
[required]="true"
error-message="Selecione uma cidade da lista"
></mk-autocomplete>Desabilitado e somente leitura
Em ambos o dropdown nunca abre — nem por digitação, nem por seta, nem com open definido junto no primeiro render — e o × não é renderizado.
<MkAutocomplete label="Cidade" options={options} selectedValue="cwb" disabled />
<MkAutocomplete label="Cidade" options={options} selectedValue="cwb" readonly /><mk-autocomplete label="Cidade" [options]="options" [disabled]="true"></mk-autocomplete>
<mk-autocomplete label="Cidade" [options]="options" [readonly]="true"></mk-autocomplete>Regra de blur
Com allowCustomValue = false (o padrão), ao perder o foco:
- O rascunho casa exatamente o
labelda option emselectedValue→ nada acontece. - Existe
selectedValuee o rascunho divergiu → o rascunho volta para olabelda seleção. - Não existe
selectedValue→ o rascunho é esvaziado.
Em todos os casos selectedValue fica intacto e mkChange não é emitido.
Props — mk-autocomplete
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
label | string | — | Obrigatória. Rótulo exibido acima do campo. |
name | string | undefined | Atributo name para envio em formulários. |
value | string | undefined | O rascunho — o que está digitado. Mutável. Nunca é submetido. |
selectedValue | unknown | undefined | A seleção confirmada. Mutável. É o que vai para setFormValue e o que required valida. |
options | AutocompleteOption[] | [] | Lista exibida, exatamente como recebida. Reatribua o array para re-renderizar. |
optionValue | string | "value" | Chave de cada objeto usada como valor confirmado. |
optionLabel | string | "label" | Chave de cada objeto usada como texto exibido. |
placeholder | string | undefined | Texto exibido com o campo vazio. |
supportText | string | undefined | Texto de apoio abaixo do campo quando não há erro. |
errorMessage | string | undefined | Mensagem de erro. Quando definida, o campo entra em estado inválido. |
required | boolean | false | Marca o campo como obrigatório. Valida selectedValue, não o rascunho. |
requiredMessage | string | "This field is required" | Mensagem da validação nativa quando required e sem seleção. |
disabled | boolean | false | Desabilita o campo. O dropdown nunca abre. |
readonly | boolean | false | Somente leitura. O dropdown nunca abre e o × não é renderizado. |
clearable | boolean | true | Exibe o × quando há texto no campo. |
loading | boolean | false | Spinner no trailing. Suprime a mensagem de vazio. |
minLength | number | 2 | Mínimo de caracteres para emitir mkInput e abrir o dropdown ao digitar. |
debounce | number | 300 | Espera em ms após a última tecla antes de emitir mkInput. |
allowCustomValue | boolean | false | Permite confirmar texto que não casa com nenhuma option. |
emptyMessage | string | undefined | Substitui a mensagem de vazio. Sem interpolação. |
open | boolean | false | Estado do dropdown. Mutável e refletido, para controle programático. |
locale | string | undefined | Tag BCP 47 para i18n. Recai para o lang do ancestral, depois <html lang>, depois pt-BR. |
AutocompleteOption
| Campo | Tipo | Descrição |
|---|---|---|
value | T | Valor confirmado quando a option é escolhida. A chave é configurável por optionValue. |
label | string | Texto exibido na lista e escrito no campo ao confirmar. A chave é configurável por optionLabel. |
disabled | boolean | Impede a seleção. A option é anunciada como aria-disabled e pulada pela navegação. |
Eventos
| Evento | Payload | Descrição |
|---|---|---|
mkInput | string | A query digitada, após |
mkChange | AutocompleteChangeDetail | A seleção foi confirmada (clique ou Enter) ou desfeita (limpar). |
mkClear | void | O |
mkOpen | void | O dropdown abriu. Uma vez por transição. Não borbulha. |
mkClose | void | O dropdown fechou. Uma vez por transição. Não borbulha. |
mkFocus | void | O campo recebeu foco. |
mkBlur | void | O campo perdeu o foco, depois de o dropdown fechar e a regra de blur ser aplicada. |
Tokens de componente
Sobrescreva para divergir do padrão sem afetar mk-input ou mk-search. Todos têm override em [data-theme="dark"].
| Variável | Descrição |
|---|---|
--autocomplete-bg-default | Fundo do campo |
--autocomplete-bg-hover | Fundo em hover |
--autocomplete-bg-disabled | Fundo desabilitado |
--autocomplete-bg-read-only | Fundo somente leitura |
--autocomplete-dropdown-bg | Fundo do dropdown |
--autocomplete-option-bg-hover | Fundo da option em hover |
--autocomplete-option-bg-active | Fundo da option em foco de teclado |
--autocomplete-option-bg-disabled | Fundo da option desabilitada |
Acessibilidade
Segue o WAI-ARIA APG — Combobox, variante combobox editável com popup de listbox, em ARIA 1.2. O elemento focado é o <input>, e é ele que carrega role="combobox", aria-expanded, aria-autocomplete="list" e aria-activedescendant.
- O foco DOM nunca sai do
<input>. A navegação entre options é inteiramente virtual, viaaria-activedescendant. As options não são focáveis e não entram na ordem de tabulação. role="listbox"está no próprio<ul>, para que as options sejam filhas diretas do listbox na árvore de acessibilidade.aria-expandedé"true"também quando o dropdown mostra só a mensagem de vazio, porque o popup está exibido.aria-activedescendantestá ausente quando nenhuma option está em foco. A lista começa sem pré-destaque, conforme o APG — Enter sem foco não confirma nada e não submete o formulário por acidente.- Uma região
role="status" aria-live="polite"anuncia a contagem de resultados ("4 resultados disponíveis") e a mensagem de vazio com a query. Sem isso, a lista apareceria sem nenhum sinal para quem não vê. - O spinner de
loadingtemrole="status"e rótulo acessível próprio. aria-required,aria-invalidearia-describedbyacompanhamrequired,errorMessageesupportText.- O
×não é um stop de tabulação (tabindex="-1"), de propósito: um foco no meio do combobox, entre o input e a lista, atrapalha mais do que ajuda. O caminho de teclado para limpar é Escape com o dropdown fechado.
Teclado
| Tecla | Comportamento |
|---|---|
↓ | Fechado: abre e foca a primeira option. Aberto: próxima option habilitada. Sem wrap |
↑ | Fechado: abre e foca a última option. Aberto: option habilitada anterior. Sem wrap |
Alt + ↓ | Abre sem focar nenhuma option |
Enter | Confirma a option em foco. Sem foco e com allowCustomValue, confirma o rascunho. Sem foco e sem allowCustomValue, não faz nada — e com o dropdown aberto não submete o formulário |
Escape | Aberto: fecha, mantém o rascunho. Fechado com texto: limpa |
Home / End | Semântica de texto — move o cursor no rascunho, não navega a lista |
Tab | Fecha, aplica a regra de blur, o foco segue para o próximo elemento |
Não há typeahead sobre a lista: a lista já é o resultado da digitação.