Vitral 0.2
Form

AutoComplete

A text box that suggests as you type, with suggestions supplied by your app, single or multiple values as chips, and a button for the whole list.

Import

main.ts
import { AutoComplete } from '@vitral/vue';

Basic

Value: null
<script setup lang="ts">import { AutoComplete, FilterService, Label, StackPanel } from '@vitral/vue';import { ref } from 'vue'; interface Country {    name: string;    code: string;    region: string;} const countries: Country[] = [    { name: 'Angola', code: 'AO', region: 'Africa' },    { name: 'Argentina', code: 'AR', region: 'Americas' },    { name: 'Brazil', code: 'BR', region: 'Americas' },    { name: 'Cabo Verde', code: 'CV', region: 'Africa' },    { name: 'Canada', code: 'CA', region: 'Americas' },    { name: 'Chile', code: 'CL', region: 'Americas' },    { name: 'Colombia', code: 'CO', region: 'Americas' },    { name: 'France', code: 'FR', region: 'Europe' },    { name: 'Germany', code: 'DE', region: 'Europe' },    { name: 'Moçambique', code: 'MZ', region: 'Africa' },    { name: 'Peru', code: 'PE', region: 'Americas' },    { name: 'Portugal', code: 'PT', region: 'Europe' },    { name: 'Spain', code: 'ES', region: 'Europe' },    { name: 'São Tomé and Príncipe', code: 'ST', region: 'Africa' },    { name: 'Uruguay', code: 'UY', region: 'Americas' }]; const match = (query: string) => countries.filter((c) => FilterService.matches(c.name, query, 'contains')); const single = ref<Country | string | null>(null);const suggestions = ref<Country[]>([]);</script> <template>    <StackPanel spacing="0.375rem" style="min-width: 16rem">        <Label for="ac-country">Country</Label>        <AutoComplete            id="ac-country"            v-model="single"            :suggestions="suggestions"            option-label="name"            placeholder="Type a country"            @complete="suggestions = match($event.query)"        />        <small style="color: var(--vt-text-muted-color)">Value: {{ typeof single === 'string' ? `"${single}"` : (single?.code ?? 'null') }}</small>    </StackPanel></template>

Multiple

<script setup lang="ts">import { AutoComplete, FilterService, Label, StackPanel } from '@vitral/vue';import { ref } from 'vue'; interface Country {    name: string;    code: string;    region: string;} const countries: Country[] = [    { name: 'Angola', code: 'AO', region: 'Africa' },    { name: 'Argentina', code: 'AR', region: 'Americas' },    { name: 'Brazil', code: 'BR', region: 'Americas' },    { name: 'Cabo Verde', code: 'CV', region: 'Africa' },    { name: 'Canada', code: 'CA', region: 'Americas' },    { name: 'Chile', code: 'CL', region: 'Americas' },    { name: 'Colombia', code: 'CO', region: 'Americas' },    { name: 'France', code: 'FR', region: 'Europe' },    { name: 'Germany', code: 'DE', region: 'Europe' },    { name: 'Moçambique', code: 'MZ', region: 'Africa' },    { name: 'Peru', code: 'PE', region: 'Americas' },    { name: 'Portugal', code: 'PT', region: 'Europe' },    { name: 'Spain', code: 'ES', region: 'Europe' },    { name: 'São Tomé and Príncipe', code: 'ST', region: 'Africa' },    { name: 'Uruguay', code: 'UY', region: 'Americas' }]; const match = (query: string) => countries.filter((c) => FilterService.matches(c.name, query, 'contains')); const many = ref<Country[]>([]);const suggestions = ref<Country[]>([]);</script> <template>    <StackPanel spacing="0.375rem" style="min-width: 22rem">        <Label for="ac-many">Countries</Label>        <AutoComplete id="ac-many" v-model="many" :suggestions="suggestions" option-label="name" multiple fluid @complete="suggestions = match($event.query)" />    </StackPanel></template>

Groups and templates

<script setup lang="ts">import { AutoComplete, FilterService, Label, StackPanel } from '@vitral/vue';import { ref } from 'vue'; interface Country {    name: string;    code: string;    region: string;} const countries: Country[] = [    { name: 'Angola', code: 'AO', region: 'Africa' },    { name: 'Argentina', code: 'AR', region: 'Americas' },    { name: 'Brazil', code: 'BR', region: 'Americas' },    { name: 'Cabo Verde', code: 'CV', region: 'Africa' },    { name: 'Canada', code: 'CA', region: 'Americas' },    { name: 'Chile', code: 'CL', region: 'Americas' },    { name: 'Colombia', code: 'CO', region: 'Americas' },    { name: 'France', code: 'FR', region: 'Europe' },    { name: 'Germany', code: 'DE', region: 'Europe' },    { name: 'Moçambique', code: 'MZ', region: 'Africa' },    { name: 'Peru', code: 'PE', region: 'Americas' },    { name: 'Portugal', code: 'PT', region: 'Europe' },    { name: 'Spain', code: 'ES', region: 'Europe' },    { name: 'São Tomé and Príncipe', code: 'ST', region: 'Africa' },    { name: 'Uruguay', code: 'UY', region: 'Americas' }]; const match = (query: string) => countries.filter((c) => FilterService.matches(c.name, query, 'contains')); const grouped = ref<Country | null>(null);const groupedSuggestions = ref<{ region: string; items: Country[] }[]>([]);function searchGrouped(query: string) {    const hits = match(query);    groupedSuggestions.value = ['Africa', 'Americas', 'Europe']        .map((region) => ({ region, items: hits.filter((c) => c.region === region) }))        .filter((g) => g.items.length);}</script> <template>    <StackPanel spacing="0.375rem" style="min-width: 16rem">        <Label for="ac-grouped">Country by region</Label>        <AutoComplete            id="ac-grouped"            v-model="grouped"            :suggestions="groupedSuggestions"            option-label="name"            option-group-label="region"            auto-option-focus            @complete="searchGrouped($event.query)"        >            <template #option="{ option }">                <span style="flex: 1">{{ (option as Country).name }}</span>                <small style="color: var(--vt-text-muted-color)">{{ (option as Country).code }}</small>            </template>        </AutoComplete>    </StackPanel></template>

Plain strings, sizes and states

<script setup lang="ts">import { AutoComplete } from '@vitral/vue';import { ref } from 'vue'; const words = ref<string | null>(null);const wordSuggestions = ref<string[]>([]);</script> <template>    <AutoComplete        v-model="words"        :suggestions="wordSuggestions"        aria-label="Word"        size="small"        @complete="wordSuggestions = ['alpha', 'beta', 'gamma', 'delta'].filter((w) => w.includes($event.query))"    />    <AutoComplete :suggestions="[]" aria-label="Filled" variant="filled" />    <AutoComplete :suggestions="[]" aria-label="Loading" loading />    <AutoComplete :suggestions="[]" aria-label="Invalid" invalid />    <AutoComplete :suggestions="[]" aria-label="Disabled" disabled model-value="Disabled" dropdown /></template>

API

Read from packages/vue/src/components/AutoComplete/types.ts, so it says what the component actually accepts.

Props

NameTypeDescription
suggestionsany[]What the list offers for the current query; the app sets it in answer to `complete`.
optionLabelstringField (dotted path) holding a suggestion's text. Without it, suggestions are shown as they are.
optionDisabledstring—
optionGroupLabelstringTurns `suggestions` into groups: this field is each group's label…
optionGroupChildrenstring…and this one its suggestions. Defaults to `items`.
dataKeystringCompare object values by this field instead of structurally.
multiplebooleanv-model becomes an array, shown as chips in the field.
dropdownbooleanAdds a button that asks for the whole list.
dropdownMode'blank' | 'current'What the button asks for: every suggestion (`'blank'`, the default) or those matching the current text.
forceSelectionbooleanOnly a chosen suggestion may stay in the box; other text is cleared when it loses focus.
delaynumberMilliseconds to wait after typing before asking. Defaults to 300.
minLengthnumberCharacters to type before asking. Defaults to 1.
completeOnFocusbooleanAsk as soon as the box takes focus.
autoOptionFocusbooleanPut the first suggestion in focus when the list opens, so Enter takes it.
showClearbooleanShows a clear button while there is a value.
loadingbooleanShows a spinner, for while the app is searching.
showEmptyMessagebooleanSay so when nothing matches, rather than keeping the list closed. Defaults to true.
emptySearchMessagestring—
sizeSize—
variantInputVariantDefaults to the plugin's `inputVariant`.
invalidboolean—
disabledboolean—
readonlyboolean—
fluidboolean—
placementOverlayPlacement—
appendTostring`'body'` (the default), `'self'` to render in place, or a selector.

Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.

Emits

EventPayloadDescription
completeevent: AutoCompleteCompleteEventAsks the app for the suggestions matching `query`.
option-selectevent: AutoCompleteOptionEvent—
option-unselectevent: AutoCompleteOptionEvent—
dropdown-clickevent: AutoCompleteCompleteEvent—
clear——
show——
hide——
focusevent: FocusEvent—
blurevent: FocusEvent—

Slots

NameSlot propsDescription
option(props: { option: unknown; index: number; selected: boolean; focused: boolean })—
optiongroup(props: { group: unknown })—
chip(props: { value: unknown; label: string; remove: (event: Event) => void })A chip's content in multiple mode.
header——
footer——
empty——
dropdownicon——