# Combobox A combobox is a widget made up of the combination of two distinct elements: - a single-line textbox - an associated listbox overlay Based on the combobox configuration and entered texbox value, options in the listbox will be filtered, checked, focused and the textbox value may be autocompleted. Optionally the combobox contains a graphical button adjacent to the textbox, indicating the availability of the popup. > Fore more information, consult [Combobox wai-aria design pattern](https://www.w3.org/TR/wai-aria-practices/#combobox) ```js script import { html } from 'lit-html'; import { Required } from '@lion/form-core'; import { loadDefaultFeedbackMessages } from '@lion/validate-messages'; import { listboxData } from '@lion/listbox/docs/listboxData.js'; import '@lion/listbox/lion-option.js'; import './lion-combobox.js'; import './docs/demo-selection-display.js'; import { lazyRender } from './docs/lazyRender.js'; import levenshtein from './docs/levenshtein.js'; export default { title: 'Forms/Combobox', }; ``` ```js preview-story export const main = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ## Features - Multiple autocomplete behaviors - Supports multiple choice - Multiple matching modes - Different interaction behaviors ## How to use ### Installation ```bash npm i --save @lion/combobox ``` ```js import { LionCombobox } from '@lion/combobox'; // or import '@lion/combobox/lion-combobox.js'; ``` ## Autocomplete Below you will find an overview of all possible `autocomplete` behaviors and how they correspond to the configurable values `none`, `list`, `inline` and `both`. | | list | filter | focus | check | complete | | -----: | :--: | :----: | :---: | :---: | :------: | | none | ✓ | | | | | | list | ✓ | ✓ | | | | | inline | ✓ | | ✓ | ✓ | ✓ | | both | ✓ | ✓ | ✓ | ✓ | ✓ | - **list** shows a list on keydown character press - **filter** filters list of potential matches according to `matchmode` or provided `matchCondition` - **focus** automatically focuses closest match (makes it the activedescendant) - **check** automatically checks/selects closest match when `selection-follows-focus` is enabled (this is the default configuration) - **complete** completes the textbox value inline (the 'missing characters' will be added as selected text) When `autocomplete="none"` is configured, the suggested options in the overlay are not filtered based on the characters typed in the textbox. Selection will happen manually by the user. ```js preview-story export const autocompleteNone = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` When `autocomplete="list"` is configured, it will filter listbox suggestions based on textbox value. ```js preview-story export const autocompleteList = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` When `autocomplete="inline"` is configured, it will present a value completion prediction inside the text input itself. It does NOT filter list of potential matches. ```js preview-story export const autocompleteInline = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` When `autocomplete="both"` is configured, it combines the filtered list from `'list'` with the text input value completion prediction from `'inline'`. This is the default value for `autocomplete`. ```js preview-story export const autocompleteBoth = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ## Match Mode When `match-mode="begin"` is applied, the entered text in the textbox only filters options whose values begin with the entered text. For instance, the entered text 'ch' will match with value 'Chard', but not with 'Artichoke'. By default `match-mode="all"` is applied. This will also match parts of a word. So 'ch' will both match 'Chard' and 'Artichoke'. ```js preview-story export const matchModeBegin = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ```js preview-story export const matchModeAll = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` When the preconfigurable `match-mode` conditions are not sufficient, one can define a custom matching function. The example below matches when the Levenshtein distance is below 3 (including some other conditions). ```js preview-story export const customMatchCondition = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ## Options ```js preview-story export const showAllOnEmpty = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ### Changing defaults By default `selection-follows-focus` will be true (aligned with the wai-aria examples and the natve ``). It is possible to disable this behavior, so the active/focused and checked/selected values will be kept track of independently. > Note that, (just like in a listbox), selection-follows-focus will never be applicable for > multiselect comboboxes. ```js preview-story export const noSelectionFollowsFocus = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` By default `rotate-keyboard-navigation` will be true (aligned with the wai-aria examples and the natve ``). It is possible to disable this behavior, see example below ```js preview-story export const noRotateKeyboardNavigation = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ## Multiple choice Add `multiple-choice` flag to allow multiple values to be selected. This will: - keep the listbox overlay open on click of an option - display a list of selected option representations next to the text box - make the value of type `Array` instead of `String` > Please note that the lion-combobox-selection-display below is not exposed and only serves > as an example. The selection part of a multiselect combobox is not yet accessible, please keep > in mind that for now, as a Subclasser, you would have to take care of this part yourself. ```js preview-story export const multipleChoice = () => html` ${lazyRender( listboxData.map( (entry, i) => html` ${entry} `, ), )} `; ``` ## Invoker button ```js preview-story export const invokerButton = () => html` ${lazyRender( listboxData.map(entry => html` ${entry} `), )} `; ``` ## Validation Validation can be used as normal, below is an example of a combobox with a `Required` validator. ```js preview-story export const validation = () => { loadDefaultFeedbackMessages(); Required.getMessage = () => 'Please enter a value'; return html`
Rocky Rocky II Rocky III Rocky IV Rocky V Rocky Balboa
`; }; ``` ## Listbox compatibility All configurations that can be applied to `lion-listbox`, can be applied to `lion-combobox` as well. See the [listbox documentation](?path=/docs/forms-listbox--main) for more information.