Autocomplete
An autocomplete is a Select with a search box in its popover. The trigger shows the chosen option rather than a text field, which is the difference from a ComboBox: here the reader is always choosing from the list, and the typing only narrows it.
import {
Autocomplete,
AutocompleteFilter,
AutocompleteIndicator,
AutocompletePopover,
AutocompleteTrigger,
AutocompleteValue,
} from "ropav";It takes every prop a select takes, items included — that is what answers for the options while the popover is shut and nothing has been rendered yet.
autocomplete-basic.vue
<script setup lang="ts">
import {
Autocomplete,
AutocompleteFilter,
AutocompleteIndicator,
AutocompletePopover,
AutocompleteTrigger,
AutocompleteValue,
Label,
ListBox,
ListBoxItem,
ListBoxItemIndicator,
SearchField,
SearchFieldClearButton,
SearchFieldGroup,
SearchFieldInput,
SearchFieldSearchIcon,
useFilter,
} from "ropav";
const cities = [
{ id: "lisbon", name: "Lisbon" },
{ id: "osaka", name: "Osaka" },
{ id: "quito", name: "Quito" },
{ id: "tallinn", name: "Tallinn" },
];
const byName = (city: { name: string }) => city.name;
const filter = useFilter({ sensitivity: "base" });
</script>
<template>
<Autocomplete
class="field"
:item-text-value="byName"
:items="cities"
placeholder="Pick a city"
selection-mode="single"
>
<Label>Destination</Label>
<AutocompleteTrigger>
<AutocompleteValue />
<AutocompleteIndicator />
</AutocompleteTrigger>
<AutocompletePopover>
<AutocompleteFilter v-slot="{ items: matches }" :filter="filter.contains">
<SearchField auto-focus aria-label="Search cities" name="search" variant="secondary">
<SearchFieldGroup>
<SearchFieldSearchIcon />
<SearchFieldInput placeholder="Search…" />
<SearchFieldClearButton />
</SearchFieldGroup>
</SearchField>
<ListBox>
<ListBoxItem v-for="city in matches" :id="city.id" :key="city.id" :text-value="city.name">
{{ city.name }}
<ListBoxItemIndicator />
</ListBoxItem>
</ListBox>
</AutocompleteFilter>
</AutocompletePopover>
</Autocomplete>
</template>
<style scoped>
.field {
width: calc(var(--rp-spacing) * 64);
}
</style>AutocompleteFilter is the search box inside the popover, and AutocompleteClearButton empties the selection — which emits clear rather than taking an onClear prop, because Vue would route a listener of that name to an emit anyway and fire the handler twice.
Which of the three to reach for
- A Select when the list is short enough to read.
- An autocomplete when it is long but still a fixed list — the reader is choosing, not writing.
- A ComboBox when what they type may itself be the answer.
Accessibility
AutocompleteTriggeris arole="group"holding the value and the button that opens the popover, and that button is labelled by both — so a reader hears the current choice and the field's name together, rather than an empty text field they might think they have to fill in.- Focus moves into the filter when the popover opens and back to the trigger when it closes, so typing goes where the reader expects without them having to find it.
selection-mode="multiple"announces each option's selected state as it is toggled; the trigger then summarises what was chosen, which is the part that has to stay readable as the list grows.
API
Autocomplete
| Prop | Type | Default | Description |
|---|---|---|---|
allowsEmptyCollection | boolean | — | Whether the popover may open with no options in it. |
ariaDescribedby | string | — | |
ariaLabel | string | — | |
ariaLabelledby | string | — | |
autoComplete | string | — | What the browser may offer to autofill. |
defaultOpen | boolean | — | Whether the popover starts open. |
defaultValue | SelectedValue | — | The key(s) the select starts on. |
disabledBehavior | "all" | "selection" | "all" | Whether a disabled key is also unfocusable. |
disabledKeys | Iterable<CollectionKey> | — | Keys that cannot be chosen. |
form | string | — | Id of the <form> the hidden control belongs to. |
fullWidth | boolean | — | Whether the autocomplete stretches to fill its container. |
isDisabled | boolean | — | |
isInvalid | boolean | — | Controlled validity. Present at all — true or false — shadows validate. |
isOpen | boolean | — | Whether the popover is open. Controlled. |
isRequired | boolean | — | |
itemDisabled | ((item: T) => boolean) | — | Whether an item cannot be chosen. Merges with disabledKeys. |
itemKey | ((item: T, index: number) => CollectionKey) | — | An item's key. Defaults to its own id, then key, then its index. |
items* | readonly T[] | — | The options, as data. **Required.** The React build reads its options out of a hidden render pass of its children. Rendering is what creates DOM in vapor, so there is no such pass — and the options only exist in the DOM while the popover is open. Everything a closed select has to answer for reads this instead: the value in the trigger, the options of the hidden native control, typing to choose, and the arrow keys stepping through choices. Required rather than optional so leaving it out is a type error instead of a select that quietly shows a placeholder over a value it holds. |
itemTextValue | ((item: T) => string | undefined) | — | Text shown for an item in the trigger, and matched on when typing. Defaults to its text. |
name | string | — | Name of the hidden native control, for form submission. |
placeholder | string | — | Text shown in the trigger when nothing is chosen. Defaults to a localized string. |
selectionMode | "single" | "multiple" | "single" | Whether one or several options can be chosen. |
shouldCloseOnSelect | boolean | true when single | Whether choosing an option closes the popover. |
size | "xs" | "sm" | "md" | "lg" | "md" | How tall the trigger stands, matching a button of the same size. Handed to the listbox in the popover, unless it declares one of its own. |
validate | ValidationFunction<SelectedValue> | — | |
validationBehavior | "aria" | "native" | "native" | |
value | SelectedValue | — | The chosen key, or keys when multiple. Controlled. |
variant | "primary" | "secondary" | "primary" | Visual variant. |
The default slot receives:
| Slot prop | Type |
|---|---|
isDisabled | boolean |
isFocused | boolean |
isInvalid | boolean |
isOpen | boolean |
isRequired | boolean |
| Event | Payload |
|---|---|
change | SelectedValue |
clear | — |
focusChange | boolean |
openChange | boolean |
update:isOpen | boolean |
update:value | SelectedValue |
AutocompleteClearButton
AutocompleteClearButton takes nothing but class.
AutocompleteFilter
| Prop | Type | Default | Description |
|---|---|---|---|
defaultInputValue | string | — | Text the search field starts with. |
disableAutoFocusFirst | boolean | — | Whether typing stops moving focus onto the first matching option. |
disableVirtualFocus | boolean | — | Whether the arrows move real focus into the options instead of a nominal one. Off is the whole arrangement: the caret stays in the search field and the field names the focused option, so typing and choosing can go on in one breath. |
filter | ((textValue: string, inputValue: string) => boolean) | — | Whether an option's text matches what has been typed. Without one nothing is filtered. useFilter is where the three usual answers come from, and contains is what the React build's stories pass. |
inputValue | string | — | Text in the search field. Present at all puts the caller in charge of it. |
items | readonly T[] | — | The options to show, already narrowed. Takes the filtering over entirely. This is the seam an asynchronous search or a virtualized list needs: the caller fetches or slices, and filter is then never consulted. |
Slots: default.
| Event | Payload |
|---|---|
inputChange | string |
update:inputValue | string |
AutocompleteIndicator
Slots: default.
AutocompletePopover
| Prop | Type | Default | Description |
|---|---|---|---|
arrowBoundaryOffset | number | — | |
containerPadding | number | — | |
crossOffset | number | — | Shift along the trigger's edge, in pixels. |
isKeyboardDismissDisabled | boolean | — | |
offset | number | 8 | Distance from the trigger, in pixels. |
placement | "bottom" | "bottom end" | "bottom left" | "bottom right" | "bottom start" | "end" | "end bottom" | "end top" | "left" | "left bottom" | "left top" | "right" | "right bottom" | "right top" | "start" | "start bottom" | "start top" | "top" | "top end" | "top left" | "top right" | "top start" | "bottom" | |
shouldFlip | boolean | — | Whether the popover may flip to the other side when it does not fit. |
Slots: default.
AutocompleteTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
isDisabled | boolean | — | Whether this trigger in particular is disabled. Falls back to the autocomplete's own state. |
The default slot receives:
| Slot prop | Type |
|---|---|
isDisabled | boolean |
isFocusWithin | boolean |
isHovered | boolean |
isInvalid | boolean |
AutocompleteValue
Slots: default.