Combobox

A field for picking one option: typing filters the list, or searches a server.

Import

ts
import { Combobox } from '@k8ordo/ui';

Usage

Typing opens the list, and the value changes only when an option is picked with a click or Enter. Leaving half-typed text puts the chosen label back; leaving the field empty clears the choice. ↓ / ↑ open the list and move through it (Alt+↓ only opens it), Escape closes it and, pressed again, drops what was typed. To pick several, use Autocomplete.

tsx
<Combobox aria-label="Prefecture" name="prefecture" options={prefectures} />

Searching a server

With search, each keystroke calls it for the options. Typing again aborts the previous call’s signal, so pass it on to fetch. While a search runs the list is busy, and a failure or an empty result is shown and announced. options becomes what is shown before anything is typed.

tsx
<Combobox
  aria-label="City"
  name="city"
  search={async (query, { signal }) => {
    const response = await fetch(
      `/api/cities?q=${encodeURIComponent(query)}`,
      { signal },
    );
    return response.json();
  }}
/>

With @k8ordo/form

Spread the input derived from z.enum() as is. The choice submits through a hidden <select>, so required, rules, reset, and moving focus after a failed submission all work on it.

tsx
// schema.ts
export const addressSchema = z.object({
  prefecture: z.enum(['hokkaido', 'tokyo', 'osaka']),
});

// address-form.tsx
const prefecture = form.field('prefecture');

<FormControl
  errorText={prefecture.error}
  invalid={prefecture.invalid}
  label="Prefecture"
  required={prefecture.required}
  renderInput={(props) => (
    <Combobox {...props} {...prefecture.input} options={prefectures} />
  )}
/>

Props

defaultValue?
Type: string
Default: -
invalid?
Type: boolean
Default: false
onChange?
Type: (value: string) => void
Default: -
options?
Type: readonly Option[]
Default: -
ref?
Type: Ref<HTMLInputElement>
Default: -
search?
Type: ComboboxSearch
Default: -
type?
Type: string
Default: -
value?
Type: string
Default: -

Type base (some attrs are managed internally): InputHTMLAttributes<HTMLInputElement>

Wording this component renders (labels, placeholders, and the like) comes from the message dictionary when no prop sets it. To change it, see: i18n