Auto complete
Auto complete is a text field that suggests matching options as you type. Unlike a select it does not restrict the input. What the user types is the value, and the menu helps them get there faster.
It accepts every text field attribute, such as variant, label, supportingText and required.
Usage
Pass an array of options. Each one needs a label. A value,
a supportingText and leading or trailing content are optional. By default the menu shows
the options whose label contains the typed text. Picking one writes its label into the field.
<script lang="ts">
import { AutoComplete } from 'noph-ui'
</script>
<AutoComplete
variant="filled"
label="Fruits"
name="fruit"
options={[{ label: 'Apple' }, { label: 'Banana' }]}
/>
Multiple values
To collect more than one value, keep the chosen options in your own state and render them as input chips inside the field. Use two props. onoptionselect replaces the default behaviour of writing the label into the input. optionsFilter replaces the filtering, so you can hide options that are already picked.
<script lang="ts">
import { AutoComplete, ChipSet, InputChip } from 'noph-ui'
import type { AutoCompleteOption } from 'noph-ui/types'
const fruitOptions: AutoCompleteOption[] = [
{ value: 1, label: 'Apple' },
{ value: 2, label: 'Banana' },
{ value: 3, label: 'Orange' },
{ value: 4, label: 'Grape' },
{ value: 5, label: 'Pineapple' },
{ value: 6, label: 'Strawberry' },
{ value: 7, label: 'Mango' },
{ value: 8, label: 'Melon' },
]
let fruits: AutoCompleteOption[] = $state([{ value: 1, label: 'Apple' }])
let fruitValue = $state('')
let chipSet: HTMLDivElement | undefined = $state()
let input: HTMLElement | undefined
</script>
<AutoComplete
options={fruitOptions}
placeholder="Add fruit..."
style="width:340px"
label="Fruits"
name="fruit"
populated={fruits.length > 0}
bind:value={fruitValue}
onfocus={(e) => (input = e.currentTarget)}
onkeydown={(e) => {
// Backspace in the empty field moves to the last fruit, and Backspace there removes it.
if (e.key === 'Backspace' && !e.currentTarget.value && fruits.length > 0) {
e.preventDefault()
chipSet
?.querySelectorAll('button')
.item(fruits.length - 1)
?.focus()
}
}}
onoptionselect={(option) => {
fruits.push(option)
}}
optionsFilter={(option) => {
return (
(!fruitValue || option.label.toLocaleLowerCase().includes(fruitValue.toLocaleLowerCase())) &&
!fruits.find((f) => f.value === option.value)
)
}}
>
<ChipSet bind:element={chipSet}>
{#each fruits as fruit (fruit.value)}
<InputChip
name="fruit"
value={fruit.value}
label={fruit.label}
onremove={() => {
fruits = fruits.filter((f) => f !== fruit)
// With the last fruit gone there is no chip left to focus, so go back to the field.
if (!fruits.length) input?.focus()
}}
/>
{/each}
</ChipSet>
</AutoComplete>
Long option lists
When more than virtualThreshold options are visible at once, the menu switches to a
virtual list and only renders what is on screen. A list of thousands of entries stays responsive.
The threshold defaults to 300. Lower it if your options are expensive to render. In this mode the
menu has the width of the field, as with clampMenuWidth.
<AutoComplete
label="City"
options={cities}
virtualThreshold={100}
clampMenuWidth
/>Accessibility
The field is a combobox: it renders role="combobox" with aria-expanded, aria-controls and aria-activedescendant, and
the menu renders as a listbox whose options carry role="option". Focus stays in the
input while the user moves through the list.
↓ and ↑ move through the suggestions and open the menu if it is closed. Home and End move the caret in the input. Once the arrow keys have moved into the suggestions, they jump to the first and the last one. Enter picks the active option. Escape closes the menu without changing the value. Typing reopens the menu with the filtered list.
Always pass a label. The field must make sense before the menu opens.
Theming
Auto complete has no tokens of its own. The field follows the text field tokens and the suggestion list follows the menu and list tokens.
<AutoComplete
label="Fruits"
options={fruitOptions}
--np-outlined-text-field-focus-outline-color="var(--np-color-tertiary)"
--np-menu-container-color="var(--np-color-surface-container-highest)"
/>To style a single option, give it a class or a style. Use the style to
set list item tokens for that option. Use a class to reach the parts of the item from your own
CSS.
<script lang="ts">
import { AutoComplete } from 'noph-ui'
</script>
<AutoComplete
variant="filled"
label="Fruits"
name="fruit"
options={[
{ label: 'Apple' },
{
label: 'Banana',
supportingText: 'Out of stock',
style: '--np-item-supporting-text-color: var(--np-color-error)',
},
{ label: 'Cherry', supportingText: 'New', class: 'new-option' },
]}
/>
<style>
:global(.new-option .np-item-supporting-text) {
font-style: italic;
}
</style>
API
Attributes
Auto complete takes every TextField attribute in addition to the ones below.
| Attribute | Type | Default | Description |
|---|---|---|---|
options | AutoCompleteOption[] | [] | The suggestions to offer. |
optionsFilter | (option: AutoCompleteOption) => boolean | Label contains the input | Replaces the built-in filtering. Use it to match on more than the label, or to hide options that have already been picked. |
onoptionselect | (option, menuElement) => void | Writes the label into the field | Called when an option is picked. Overriding it takes over what selecting does, including closing the menu. |
open | boolean | false | Bindable. Whether the suggestion menu is open. Set it to open or close the menu. |
clampMenuWidth | boolean | false | Fixes the menu to the width of the field. Without it the menu uses the field width as a minimum and grows for long labels. |
virtualThreshold | number | 300 | The menu renders as a virtual list when more than this many options are visible. |
AutoCompleteOption
| Property | Type | Description |
|---|---|---|
label | string | Required. Text of the option, and what is matched against the input. |
value | string | number | undefined | Identifier of the option, for telling two equal labels apart in your own state. |
supportingText | string | Snippet | undefined | Second line below the label. Pass a snippet to render your own markup. |
class | ClassValue | undefined | Classes added to the option element. |
style | string | undefined | Inline style of the option element. Use it to set list item tokens such as --np-item-supporting-text-color for a single option. |
start | Snippet | undefined | Leading content of the option, typically an icon or an avatar. |
end | Snippet | undefined | Trailing content of the option. |