Combobox
Combobox — type to search, arrow to the one you want, Enter to take it.
Walkthrough
1. Basic
Type to filter, arrow to the one you want, Enter to take it — the field for a list too long to scroll, like a country or a person.
<script lang="ts"> import { Combobox } from 'omaris'; let country = $state('iq'); const COUNTRIES = [ { value: 'iq', label: 'Iraq', keywords: ['baghdad', 'erbil'] }, { value: 'tr', label: 'Türkiye', keywords: ['turkey', 'ankara'] }, { value: 'jo', label: 'Jordan', keywords: ['amman'] }, { value: 'ae', label: 'United Arab Emirates', keywords: ['dubai', 'uae'] }, { value: 'sa', label: 'Saudi Arabia', keywords: ['riyadh'] } ];</script><Combobox class="w-72 max-w-full" label="Country" options={COUNTRIES} bind:value={country} /> 2. Keywords
Matching is accent-insensitive and looks at keywords as well as the label, so "turkey" finds Türkiye and "uae" finds the Emirates.
<script lang="ts"> import { Combobox } from 'omaris'; let value = $state(''); const AIRPORTS = [ { value: 'bgw', label: 'Baghdad International', description: 'BGW', keywords: ['iraq'] }, { value: 'ebl', label: 'Erbil International', description: 'EBL', keywords: ['iraq', 'hawler'] }, { value: 'ist', label: 'İstanbul Airport', description: 'IST', keywords: ['turkey', 'istanbul'] }, { value: 'dxb', label: 'Dubai International', description: 'DXB', keywords: ['uae', 'emirates'] } ];</script><Combobox class="w-80 max-w-full" label="Airport" placeholder="Try “turkey” or “uae”" options={AIRPORTS} bind:value/> 3. Async
load is called instead of filtering, debounced, with an AbortSignal — so a slow reply for "ir" can never overwrite the answer for "iraq".
<script lang="ts"> import { Combobox } from 'omaris'; let value = $state(''); const PEOPLE = [ { value: 'ahmed', label: 'Ahmed' }, { value: 'amina', label: 'Amina' }, { value: 'dilan', label: 'Dilan' }, { value: 'hevi', label: 'Hevi' }, { value: 'karwan', label: 'Karwan' }, { value: 'layla', label: 'Layla' }, { value: 'omer', label: 'Omer' }, { value: 'rania', label: 'Rania' }, { value: 'zainab', label: 'Zainab' } ]; async function search(query: string, signal: AbortSignal) { // Stand-in for a real request; the signal is what makes it cancellable. await new Promise((resolve) => setTimeout(resolve, 400)); signal.throwIfAborted(); return PEOPLE.filter((person) => person.label.toLowerCase().includes(query.toLowerCase())); }</script><Combobox class="w-72 max-w-full" label="Assign to" placeholder="Start typing a name" load={search} minChars={1} bind:value/> 4. Allow custom
allowCustom takes whatever is typed when nothing matches — tags, labels.
Nothing chosen yet.
<script lang="ts"> import { Combobox } from 'omaris'; let tag = $state(''); const TAGS = [ { value: 'bug', label: 'bug' }, { value: 'docs', label: 'docs' }, { value: 'design', label: 'design' } ];</script><Combobox class="w-72 max-w-full" label="Tag" placeholder="Pick one or invent one" options={TAGS} allowCustom bind:value={tag} supportingText={tag ? `Value: ${tag}` : 'Nothing chosen yet.'}/> 5. Overridden
Combobox is an Input frame with the shared Listbox under it, so it takes both sets of parts: control is the text field, option a row of the popup.
<script lang="ts"> import { Combobox } from 'omaris'; const CODES = [ { value: 'iq', label: 'IQ +964' }, { value: 'tr', label: 'TR +90' }, { value: 'ae', label: 'AE +971' } ];</script><Combobox class="w-64 max-w-full" label="Dial code" options={CODES} value="iq" classes={{ control: 'font-mono', option: 'font-mono' }}/>