# <aihio-combobox>

A text field for picking one option from a long list: typing filters the options, and the chosen option's value submits with the form. Use it when there are too many options to scan comfortably (roughly more than ten), or when the options come from a search.

Intents: `form-field`, `selection`, `text-entry`.

## Attributes

- `value` (string): Value of the initially chosen option, and the value a form reset returns to. The value property carries the live choice.
- `name` (string): Form field name. The chosen option's value (not its label) is submitted under it. Without it the field submits nothing.
- `form` (string): Id of an external form that owns the field.
- `placeholder` (string): Placeholder text. It is not a label.
- `disabled` (boolean): Disables the field. A disabled field submits nothing. Default: `false`.
- `readonly` (boolean): Shows the chosen option without letting it change. The value still submits. Default: `false`.
- `required` (boolean): Requires a choice for native constraint validation. Default: `false`.
- `error` (boolean): Shows error styling and sets aria-invalid. aihio-field sets this from its error slot. Default: `false`.
- `size` (`sm` | `md` | `lg`): Field size, matching aihio-input. Default: `md`.
- `filter` (`contains` | `starts-with` | `none`): How typing filters the options. contains ranks prefix matches first, then word-start matches, then any match. none shows every option as given, for lists filtered by a server. Default: `contains`.
- `allow-custom` (boolean): Accepts typed text that matches no option; the text becomes the value. Without it, leaving the field reverts unmatched text to the chosen option. Default: `false`.
- `loading` (boolean): Shows a loading row and marks the list busy while options are being fetched. Default: `false`.
- `empty-text` (string): Shown and announced when no option matches. Default: `No results`.
- `loading-text` (string): Shown and announced while loading is set. Default: `Loading…`.
- `results-text` (string): Result-count announcement with a {count} placeholder, for localisation (e.g. "{count} tulosta"). Defaults to "1 result" / "N results".
- `open` (boolean): Reflects whether the list is showing. Set or remove it to open or close the list. Default: `false`.

## Properties

- `value` (`string`): Get or set the chosen option's value (or the free text, with allow-custom). Setting it does not fire aihio-change.
- `defaultValue` (`string`): Get or set the reset value reflected by the value attribute.
- `selectedOption` (`HTMLElement | null`, read-only): The chosen aihio-option, or null.
- `control` (`HTMLInputElement | null`, read-only): The native text input carrying role="combobox".
- `form` (`HTMLFormElement | null`, read-only): The owning form, or null outside one.
- `validity` (`ValidityState | null`, read-only): Native constraint validation state.
- `validationMessage` (`string`, read-only): Native validation message.
- `willValidate` (`boolean`, read-only): Whether the field participates in constraint validation.

## Methods

- `open()`: Opens the list showing every option, with the chosen one highlighted.
- `close()`: Closes the list, reverting unconfirmed text to the chosen option.
- `toggle()`: Opens or closes the list.
- `focus()`: Moves focus to the text input.
- `checkValidity()`: Runs native constraint validation and returns whether the field is valid.
- `reportValidity()`: Runs native constraint validation and shows the browser message if invalid.
- `setCustomValidity()`: Sets a custom validation message.

## Events

- `aihio-change` (detail: value: string, label: string): Fired when the person commits a different choice, or when a form reset changes it. Not fired for programmatic value changes.
- `aihio-search` (detail: query: string): Fired on every keystroke with the typed text. Use it to fetch options for filter="none".
- `aihio-open`: Fired after the list opens.
- `aihio-close` (detail: reason: string): Fired after the list closes.

## <aihio-option>

One choice in an aihio-combobox. Its text (or label attribute) is what typing matches and what the field shows once chosen.

### <aihio-option> attributes

- `value` (string): Submitted value. Defaults to the label.
- `label` (string): Text matched against and shown in the field when the content is richer than a plain label (an icon, a secondary line).
- `disabled` (boolean): Shown but cannot be chosen, and skipped by the arrow keys. Default: `false`.

### Properties

- `value` (`string`): The value attribute, or the label when it is absent.
- `label` (`string`): The label attribute, or the normalised text content.
- `disabled` (`boolean`): Reflects the disabled attribute.
- `selected` (`boolean`, read-only): Whether the owning combobox has this option chosen.

## Accessibility obligations

- (error, combobox-label) When the combobox is not inside an aihio-field: Label it with aria-labelledby pointing at visible text, or aria-label. Placeholder is not a label, and a wrapping <label> would fold the option text into the field's name.
- (error, combobox-option-values) When two aihio-option children share a value: Give every option a distinct value; the value is how the chosen option is found again.
- (warn, combobox-form-name) When the combobox is inside a <form> and its value should be submitted: Set name. A combobox without a name is omitted from FormData.

## Examples

### A searchable list

Typing filters without regard to case or accents.

```html
<aihio-field>
  <label slot="label">Country</label>
  <aihio-combobox name="country" placeholder="Search countries">
    <aihio-option value="fi">Finland</aihio-option>
    <aihio-option value="fr">France</aihio-option>
    <aihio-option value="de">Germany</aihio-option>
    <aihio-option value="is">Iceland</aihio-option>
    <aihio-option value="no">Norway</aihio-option>
    <aihio-option value="se">Sweden</aihio-option>
    <aihio-option value="ax">Åland Islands</aihio-option>
  </aihio-combobox>
  <span slot="description">Type to filter. Accents are optional.</span>
</aihio-field>
```

### Rich options and a preset value

label sets the text shown once an option is chosen; a disabled option cannot be.

```html
<aihio-field>
  <label slot="label">Assignee</label>
  <aihio-combobox name="assignee" value="ada">
    <aihio-option value="ada" label="Ada Lovelace">Ada Lovelace <small>Engineering</small></aihio-option>
    <aihio-option value="grace" label="Grace Hopper">Grace Hopper <small>Compilers</small></aihio-option>
    <aihio-option value="alan" label="Alan Turing" disabled>Alan Turing <small>On leave</small></aihio-option>
  </aihio-combobox>
</aihio-field>
```

### Free text allowed

allow-custom keeps text that matches no option.

```html
<aihio-field>
  <label slot="label">Tag</label>
  <aihio-combobox name="tag" allow-custom placeholder="Pick or type a tag">
    <aihio-option>bug</aihio-option>
    <aihio-option>design</aihio-option>
    <aihio-option>documentation</aihio-option>
  </aihio-combobox>
</aihio-field>
```

### Options from a server

filter="none" leaves filtering to you: listen for aihio-search and replace the options.

```html
<aihio-combobox aria-label="Search users" name="user" filter="none" empty-text="Type to search"></aihio-combobox>
```

## Mistakes

### Placeholder is not a label. Wrap it in aihio-field with a label slot, or add aria-labelledby or aria-label.

Don't (aihio lint: combobox-label):

```html
<aihio-combobox placeholder="Country">
  <aihio-option value="fi">Finland</aihio-option>
</aihio-combobox>
```

Do:

```html
<aihio-field>
  <label slot="label">Country</label>
  <aihio-combobox name="country">
    <aihio-option value="fi">Finland</aihio-option>
  </aihio-combobox>
</aihio-field>
```

### A wrapping <label> takes its name from all of its text, so an open list's options become part of the field's name. Use aihio-field instead.

Don't (aihio lint: combobox-label):

```html
<label>
  Country
  <aihio-combobox>
    <aihio-option value="fi">Finland</aihio-option>
  </aihio-combobox>
</label>
```

Do:

```html
<aihio-field>
  <label slot="label">Country</label>
  <aihio-combobox name="country">
    <aihio-option value="fi">Finland</aihio-option>
  </aihio-combobox>
</aihio-field>
```

### Native <option> elements are not read. Use aihio-option.

Don't (aihio lint: invalid-child):

```html
<aihio-combobox aria-label="Size">
  <option value="s">Small</option>
  <option value="m">Medium</option>
</aihio-combobox>
```

Do:

```html
<aihio-combobox aria-label="Size">
  <aihio-option value="s">Small</aihio-option>
  <aihio-option value="m">Medium</aihio-option>
</aihio-combobox>
```

### An option without a value uses its label as its value, so both options have the value "Pro" and the second can never be told apart from the first.

Don't (aihio lint: combobox-option-values):

```html
<aihio-combobox aria-label="Plan">
  <aihio-option>Pro</aihio-option>
  <aihio-option value="Pro">Pro (annual)</aihio-option>
</aihio-combobox>
```

Do:

```html
<aihio-combobox aria-label="Plan">
  <aihio-option value="pro">Pro</aihio-option>
  <aihio-option value="pro-annual">Pro (annual)</aihio-option>
</aihio-combobox>
```
