- Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config)
using direct eslint-plugin-solid + @typescript-eslint/parser approach
- Add @typescript-eslint/parser to root devDependencies
- Add main/module/types top-level fields to packages/core/package.json
- Add resolve.conditions to packages/core/vite.config.ts
- Create packages/core/tsconfig.test.json for test type-checking
- Remove empty paths:{} from packages/core/tsconfig.json
1464 lines
43 KiB
Markdown
1464 lines
43 KiB
Markdown
# Select
|
|
|
|
Displays a list of options for the user to pick from — triggered by a button.
|
|
|
|
## Import
|
|
|
|
```
|
|
Copyts
|
|
import { Select } from "@kobalte/core/select";
|
|
// or
|
|
import { Root, Label, ... } from "@kobalte/core/select";
|
|
// or (deprecated)
|
|
import { Select } from "@kobalte/core";
|
|
```
|
|
|
|
```
|
|
Copyts
|
|
import { Select } from "@kobalte/core/select";
|
|
// or
|
|
import { Root, Label, ... } from "@kobalte/core/select";
|
|
// or (deprecated)
|
|
import { Select } from "@kobalte/core";
|
|
```
|
|
|
|
## Features
|
|
|
|
- Exposed to assistive technology as a button with a listbox popup using the [WAI ARIA Listbox](https://www.w3.org/WAI/ARIA/apg/patterns/listbox/) design pattern.
|
|
- Support for single and multiple selection.
|
|
- Support for disabled options.
|
|
- Labeling support for accessibility.
|
|
- Support for description and error message help text linked to the button via ARIA.
|
|
- Tab stop focus management.
|
|
- Keyboard support for opening the listbox using the arrow keys, including automatically focusing the first or last item accordingly.
|
|
- Typeahead to allow selecting options by typing text, even without opening the listbox.
|
|
- Browser autofill integration via a hidden native `<select>` element.
|
|
- Supports items, and groups of items.
|
|
- Supports custom placeholder.
|
|
- Can be controlled or uncontrolled.
|
|
|
|
## Anatomy
|
|
|
|
The select consists of:
|
|
|
|
- **Select:** The root container for a select component.
|
|
- **Select.Label:** The label that gives the user information on the select.
|
|
- **Select.Description:** The description that gives the user more information on the select.
|
|
- **Select.ErrorMessage:** The error message that gives the user information about how to fix a validation error on the select.
|
|
- **Select.Trigger:** The button that opens the select.
|
|
- **Select.Value:** The part that reflects the selected value.
|
|
- **Select.Icon:** A small icon often displayed next to the value as a visual affordance for the fact it can be open.
|
|
- **Select.Portal:** Portals its children into the `body` when the select is open.
|
|
- **Select.Content:** Contains the content to be rendered when the select is open.
|
|
- **Select.Arrow:** An optional arrow element to render alongside the select content.
|
|
- **Select.Listbox:** Contains a list of items and allows a user to select one or more of them.
|
|
- **Select.Section:** Used to render the label of an option group. It won't be focusable using arrow keys.
|
|
- **Select.Item:** An item of the select.
|
|
- **Select.ItemLabel:** An accessible label to be announced for the item.
|
|
- **Select.ItemDescription:** An optional accessible description to be announced for the item.
|
|
- **Select.ItemIndicator:** The visual indicator rendered when the item is selected.
|
|
|
|
```
|
|
Copytsx
|
|
<Select>
|
|
<Select.Label />
|
|
<Select.Trigger>
|
|
<Select.Value />
|
|
<Select.Icon />
|
|
</Select.Trigger>
|
|
<Select.Description />
|
|
<Select.ErrorMessage />
|
|
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Arrow />
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
<Select>
|
|
<Select.Label />
|
|
<Select.Trigger>
|
|
<Select.Value />
|
|
<Select.Icon />
|
|
</Select.Trigger>
|
|
<Select.Description />
|
|
<Select.ErrorMessage />
|
|
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Arrow />
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
## Example
|
|
|
|
Select a fruit…Sort
|
|
|
|
index.tsxstyle.css
|
|
|
|
```
|
|
Copytsx
|
|
import { Select } from "@kobalte/core/select";
|
|
import { CaretSortIcon, CheckIcon } from "some-icon-library";
|
|
import "./style.css";
|
|
|
|
function App() {
|
|
return (
|
|
<Select
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item} class="select__item">
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator class="select__item-indicator">
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger class="select__trigger" aria-label="Fruit">
|
|
<Select.Value class="select__value">
|
|
{state => state.selectedOption()}
|
|
</Select.Value>
|
|
<Select.Icon class="select__icon">
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content class="select__content">
|
|
<Select.Listbox class="select__listbox" />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
import { Select } from "@kobalte/core/select";
|
|
import { CaretSortIcon, CheckIcon } from "some-icon-library";
|
|
import "./style.css";
|
|
|
|
function App() {
|
|
return (
|
|
<Select
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item} class="select__item">
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator class="select__item-indicator">
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger class="select__trigger" aria-label="Fruit">
|
|
<Select.Value class="select__value">
|
|
{state => state.selectedOption()}
|
|
</Select.Value>
|
|
<Select.Icon class="select__icon">
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content class="select__content">
|
|
<Select.Listbox class="select__listbox" />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Default value
|
|
|
|
An initial, uncontrolled value can be provided using the `defaultValue` prop, which accepts a value corresponding with the `options`.
|
|
|
|
BlueberrySort
|
|
|
|
```
|
|
Copytsx
|
|
<Select
|
|
defaultValue="Blueberry"
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
<Select
|
|
defaultValue="Blueberry"
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
### Controlled value
|
|
|
|
The `value` prop, which accepts a value corresponding with the `options` prop, can be used to make the value controlled. The `onChange` event is fired when the user selects an option, and receives the selected option.
|
|
|
|
BlueberrySort
|
|
|
|
Your favorite fruit is: Blueberry.
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
export function ControlledExample() {
|
|
const [value, setValue] = createSignal("Blueberry");
|
|
|
|
return (
|
|
<>
|
|
<Select
|
|
value={value()}
|
|
onChange={setValue}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<p>Your favorite fruit is: {value()}.</p>
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
export function ControlledExample() {
|
|
const [value, setValue] = createSignal("Blueberry");
|
|
|
|
return (
|
|
<>
|
|
<Select
|
|
value={value()}
|
|
onChange={setValue}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<p>Your favorite fruit is: {value()}.</p>
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Description
|
|
|
|
The `Select.Description` component can be used to associate additional help text with a select.
|
|
|
|
Select a fruit…Sort
|
|
|
|
Choose the fruit you like the most.
|
|
|
|
```
|
|
Copytsx
|
|
<Select
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Description>Choose the fruit you like the most.</Select.Description>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
<Select
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Description>Choose the fruit you like the most.</Select.Description>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
```
|
|
|
|
### Error message
|
|
|
|
The `Select.ErrorMessage` component can be used to help the user fix a validation error. It should be combined with the `validationState` prop to semantically mark the select as invalid for assistive technologies.
|
|
|
|
By default, it will render only when the `validationState` prop is set to `invalid`, use the `forceMount` prop to always render the error message (ex: for usage with animation libraries).
|
|
|
|
GrapesSort
|
|
|
|
Hmm, I prefer apples.
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
export function ErrorMessageExample() {
|
|
const [value, setValue] = createSignal("Grapes");
|
|
|
|
return (
|
|
<Select
|
|
value={value()}
|
|
onChange={setValue}
|
|
validationState={value() !== "Apple" ? "invalid" : "valid"}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.ErrorMessage>Hmm, I prefer apples.</Select.ErrorMessage>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
export function ErrorMessageExample() {
|
|
const [value, setValue] = createSignal("Grapes");
|
|
|
|
return (
|
|
<Select
|
|
value={value()}
|
|
onChange={setValue}
|
|
validationState={value() !== "Apple" ? "invalid" : "valid"}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.ErrorMessage>Hmm, I prefer apples.</Select.ErrorMessage>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
### HTML forms
|
|
|
|
The select `name` prop, paired with the `Select.HiddenSelect` component, can be used for integration with HTML forms.
|
|
|
|
AppleBananaBlueberryGrapesPineapple
|
|
|
|
Select a fruit…Sort
|
|
|
|
ResetSubmit
|
|
|
|
```
|
|
Copytsx
|
|
function HTMLFormExample() {
|
|
const onSubmit = (e: SubmitEvent) => {
|
|
// handle form submission.
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={onSubmit}>
|
|
<Select
|
|
name="fruit"
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.HiddenSelect />
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<div>
|
|
<button type="reset">Reset</button>
|
|
<button>Submit</button>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
function HTMLFormExample() {
|
|
const onSubmit = (e: SubmitEvent) => {
|
|
// handle form submission.
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={onSubmit}>
|
|
<Select
|
|
name="fruit"
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.HiddenSelect />
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<string>>{state => state.selectedOption()}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<div>
|
|
<button type="reset">Reset</button>
|
|
<button>Submit</button>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Using object as options
|
|
|
|
Objects can be used as options instead of plain strings. In this case you have to tell the select how it should work with the provided options. For this you **have to use** the following props :
|
|
|
|
- `optionValue`: The property name to use as the value of an option (submitted with `<form>`).
|
|
- `optionTextValue`: The property name to use as the text value of an option for keyboard navigation.
|
|
- `optionDisabled`: The property name to use as the disabled flag of an option.
|
|
|
|
Select a food…Sort
|
|
|
|
```
|
|
Copytsx
|
|
interface Fruit {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
const options: Fruit[] = [\
|
|
{ value: "apple", label: "Apple", disabled: false },\
|
|
{ value: "banana", label: "Banana", disabled: false },\
|
|
{ value: "blueberry", label: "Blueberry", disabled: false },\
|
|
{ value: "grapes", label: "Grapes", disabled: true },\
|
|
{ value: "pineapple", label: "Pineapple", disabled: false },\
|
|
];
|
|
|
|
function ObjectExample() {
|
|
return (
|
|
<Select
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<Fruit>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
interface Fruit {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
const options: Fruit[] = [\
|
|
{ value: "apple", label: "Apple", disabled: false },\
|
|
{ value: "banana", label: "Banana", disabled: false },\
|
|
{ value: "blueberry", label: "Blueberry", disabled: false },\
|
|
{ value: "grapes", label: "Grapes", disabled: true },\
|
|
{ value: "pineapple", label: "Pineapple", disabled: false },\
|
|
];
|
|
|
|
function ObjectExample() {
|
|
return (
|
|
<Select
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
placeholder="Select a fruit…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruit">
|
|
<Select.Value<Fruit>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Using option groups
|
|
|
|
When using option groups you have to tell the select how to distinguish an option from a group. For this you **have to use** the following props :
|
|
|
|
- `optionGroupChildren`: The property name that refers to the children options of an option group.
|
|
|
|
Additionally, the `sectionComponent` prop is used to display the option group label in the select.
|
|
|
|
Select a food…Sort
|
|
|
|
```
|
|
Copytsx
|
|
interface Food {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
interface Category {
|
|
label: string;
|
|
options: Food[];
|
|
}
|
|
|
|
const options: Category[] = [\
|
|
{\
|
|
label: "Fruits",\
|
|
options: [\
|
|
{ value: "apple", label: "Apple", disabled: false },\
|
|
{ value: "banana", label: "Banana", disabled: false },\
|
|
{ value: "blueberry", label: "Blueberry", disabled: false },\
|
|
{ value: "grapes", label: "Grapes", disabled: true },\
|
|
{ value: "pineapple", label: "Pineapple", disabled: false },\
|
|
],\
|
|
},\
|
|
{\
|
|
label: "Meat",\
|
|
options: [\
|
|
{ value: "beef", label: "Beef", disabled: false },\
|
|
{ value: "chicken", label: "Chicken", disabled: false },\
|
|
{ value: "lamb", label: "Lamb", disabled: false },\
|
|
{ value: "pork", label: "Pork", disabled: false },\
|
|
],\
|
|
},\
|
|
];
|
|
|
|
function OptionGroupExample() {
|
|
return (
|
|
<Select<Food, Category>
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
optionGroupChildren="options"
|
|
placeholder="Select a food…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
sectionComponent={props => <Select.Section>{props.section.rawValue.label}</Select.Section>}
|
|
>
|
|
<Select.Trigger aria-label="Food">
|
|
<Select.Value<Food>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
interface Food {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
interface Category {
|
|
label: string;
|
|
options: Food[];
|
|
}
|
|
|
|
const options: Category[] = [\
|
|
{\
|
|
label: "Fruits",\
|
|
options: [\
|
|
{ value: "apple", label: "Apple", disabled: false },\
|
|
{ value: "banana", label: "Banana", disabled: false },\
|
|
{ value: "blueberry", label: "Blueberry", disabled: false },\
|
|
{ value: "grapes", label: "Grapes", disabled: true },\
|
|
{ value: "pineapple", label: "Pineapple", disabled: false },\
|
|
],\
|
|
},\
|
|
{\
|
|
label: "Meat",\
|
|
options: [\
|
|
{ value: "beef", label: "Beef", disabled: false },\
|
|
{ value: "chicken", label: "Chicken", disabled: false },\
|
|
{ value: "lamb", label: "Lamb", disabled: false },\
|
|
{ value: "pork", label: "Pork", disabled: false },\
|
|
],\
|
|
},\
|
|
];
|
|
|
|
function OptionGroupExample() {
|
|
return (
|
|
<Select<Food, Category>
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
optionGroupChildren="options"
|
|
placeholder="Select a food…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
sectionComponent={props => <Select.Section>{props.section.rawValue.label}</Select.Section>}
|
|
>
|
|
<Select.Trigger aria-label="Food">
|
|
<Select.Value<Food>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
Notice the usage of generics on `Select` for proper TypeScript support.
|
|
|
|
### Multiple selection
|
|
|
|
The `multiple` prop can be used to create a select that allow multi-selection. In this case the value provided to `value`, `defaultValue` and `onChange` props is of type `Array<T>`.
|
|
|
|
The `Select.Value` children _render prop_ expose an array of selected options, and two method for removing an option from the selection and clear the selection.
|
|
|
|
Additionally, the example below uses the `as` prop to render a `div` for the `Select.Trigger` since HTML button can't contain interactive elements according to the [W3C](https://html.spec.whatwg.org/multipage/form-elements.html#the-button-element).
|
|
|
|
BlueberryCrossGrapesCross
|
|
CrossSort
|
|
|
|
Your favorite fruits are: Blueberry, Grapes.
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
function MultipleSelectionExample() {
|
|
const [values, setValues] = createSignal(["Blueberry", "Grapes"]);
|
|
|
|
return (
|
|
<>
|
|
<Select<string>
|
|
multiple
|
|
value={values()}
|
|
onChange={setValues}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select some fruits…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruits" as="div">
|
|
<Select.Value<string>>
|
|
{state => (
|
|
<>
|
|
<div>
|
|
<For each={state.selectedOptions()}>
|
|
{option => (
|
|
<span onPointerDown={e => e.stopPropagation()}>
|
|
{option}
|
|
<button onClick={() => state.remove(option)}>
|
|
<CrossIcon />
|
|
</button>
|
|
</span>
|
|
)}
|
|
</For>
|
|
</div>
|
|
<button onPointerDown={e => e.stopPropagation()} onClick={state.clear}>
|
|
<CrossIcon />
|
|
</button>
|
|
</>
|
|
)}
|
|
</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<p>Your favorite fruits are: {values().join(", ")}.</p>
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
import { createSignal } from "solid-js";
|
|
|
|
function MultipleSelectionExample() {
|
|
const [values, setValues] = createSignal(["Blueberry", "Grapes"]);
|
|
|
|
return (
|
|
<>
|
|
<Select<string>
|
|
multiple
|
|
value={values()}
|
|
onChange={setValues}
|
|
options={["Apple", "Banana", "Blueberry", "Grapes", "Pineapple"]}
|
|
placeholder="Select some fruits…"
|
|
itemComponent={props => (
|
|
<Select.Item item={props.item}>
|
|
<Select.ItemLabel>{props.item.rawValue}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
)}
|
|
>
|
|
<Select.Trigger aria-label="Fruits" as="div">
|
|
<Select.Value<string>>
|
|
{state => (
|
|
<>
|
|
<div>
|
|
<For each={state.selectedOptions()}>
|
|
{option => (
|
|
<span onPointerDown={e => e.stopPropagation()}>
|
|
{option}
|
|
<button onClick={() => state.remove(option)}>
|
|
<CrossIcon />
|
|
</button>
|
|
</span>
|
|
)}
|
|
</For>
|
|
</div>
|
|
<button onPointerDown={e => e.stopPropagation()} onClick={state.clear}>
|
|
<CrossIcon />
|
|
</button>
|
|
</>
|
|
)}
|
|
</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<Select.Content>
|
|
<Select.Listbox />
|
|
</Select.Content>
|
|
</Select.Portal>
|
|
</Select>
|
|
<p>Your favorite fruits are: {values().join(", ")}.</p>
|
|
</>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Virtual scrolling
|
|
|
|
When dealing with large collection of items, it's recommended to use a virtual scrolling solution to improve performance.
|
|
|
|
While Kobalte doesn't provide any built-in virtual scrolling API, it can easily be integrated with a 3rd party library. The example below demonstrate how to virtualize an array of 100,000 options using the [`@tanstack/solid-virtual`](https://tanstack.com/virtual/v3) package.
|
|
|
|
Select an item…Sort
|
|
|
|
```
|
|
Copytsx
|
|
import { Select } from "@kobalte/core/select";
|
|
import { createVirtualizer } from "@tanstack/solid-virtual";
|
|
|
|
interface Item {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({
|
|
value: `${i}`,
|
|
label: `Item #${i + 1}`,
|
|
disabled: false,
|
|
}));
|
|
|
|
function SelectContent(props: { options: Item[] }) {
|
|
let listboxRef: HTMLUListElement | undefined;
|
|
|
|
const virtualizer = createVirtualizer({
|
|
count: props.options.length,
|
|
getScrollElement: () => listboxRef,
|
|
getItemKey: (index: number) => props.options[index].value,
|
|
estimateSize: () => 32,
|
|
enableSmoothScroll: false,
|
|
overscan: 5,
|
|
});
|
|
|
|
return (
|
|
<Select.Content>
|
|
<Select.Listbox
|
|
ref={listboxRef}
|
|
scrollToItem={key =>
|
|
virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key))
|
|
}
|
|
style={{ height: "200px", width: "100%", overflow: "auto" }}
|
|
>
|
|
{items => (
|
|
<div
|
|
style={{
|
|
height: `${virtualizer.getTotalSize()}px`,
|
|
width: "100%",
|
|
position: "relative",
|
|
}}
|
|
>
|
|
<For each={virtualizer.getVirtualItems()}>
|
|
{virtualRow => {
|
|
const item = items().getItem(virtualRow.key);
|
|
|
|
if (item) {
|
|
return (
|
|
<Select.Item
|
|
item={item}
|
|
style={{
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
width: "100%",
|
|
height: `${virtualRow.size}px`,
|
|
transform: `translateY(${virtualRow.start}px)`,
|
|
}}
|
|
>
|
|
<Select.ItemLabel>{item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
);
|
|
}
|
|
}}
|
|
</For>
|
|
</div>
|
|
)}
|
|
</Select.Listbox>
|
|
</Select.Content>
|
|
);
|
|
}
|
|
|
|
function VirtualizedExample() {
|
|
return (
|
|
<Select
|
|
virtualized
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
placeholder="Select an item…"
|
|
>
|
|
<Select.Trigger aria-label="Food">
|
|
<Select.Value<Item>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<SelectContent options={options} />
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
```
|
|
Copytsx
|
|
import { Select } from "@kobalte/core/select";
|
|
import { createVirtualizer } from "@tanstack/solid-virtual";
|
|
|
|
interface Item {
|
|
value: string;
|
|
label: string;
|
|
disabled: boolean;
|
|
}
|
|
|
|
const options: Item[] = Array.from({ length: 100_000 }, (_, i) => ({
|
|
value: `${i}`,
|
|
label: `Item #${i + 1}`,
|
|
disabled: false,
|
|
}));
|
|
|
|
function SelectContent(props: { options: Item[] }) {
|
|
let listboxRef: HTMLUListElement | undefined;
|
|
|
|
const virtualizer = createVirtualizer({
|
|
count: props.options.length,
|
|
getScrollElement: () => listboxRef,
|
|
getItemKey: (index: number) => props.options[index].value,
|
|
estimateSize: () => 32,
|
|
enableSmoothScroll: false,
|
|
overscan: 5,
|
|
});
|
|
|
|
return (
|
|
<Select.Content>
|
|
<Select.Listbox
|
|
ref={listboxRef}
|
|
scrollToItem={key =>
|
|
virtualizer.scrollToIndex(props.options.findIndex(option => option.value === key))
|
|
}
|
|
style={{ height: "200px", width: "100%", overflow: "auto" }}
|
|
>
|
|
{items => (
|
|
<div
|
|
style={{
|
|
height: `${virtualizer.getTotalSize()}px`,
|
|
width: "100%",
|
|
position: "relative",
|
|
}}
|
|
>
|
|
<For each={virtualizer.getVirtualItems()}>
|
|
{virtualRow => {
|
|
const item = items().getItem(virtualRow.key);
|
|
|
|
if (item) {
|
|
return (
|
|
<Select.Item
|
|
item={item}
|
|
style={{
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
width: "100%",
|
|
height: `${virtualRow.size}px`,
|
|
transform: `translateY(${virtualRow.start}px)`,
|
|
}}
|
|
>
|
|
<Select.ItemLabel>{item.rawValue.label}</Select.ItemLabel>
|
|
<Select.ItemIndicator>
|
|
<CheckIcon />
|
|
</Select.ItemIndicator>
|
|
</Select.Item>
|
|
);
|
|
}
|
|
}}
|
|
</For>
|
|
</div>
|
|
)}
|
|
</Select.Listbox>
|
|
</Select.Content>
|
|
);
|
|
}
|
|
|
|
function VirtualizedExample() {
|
|
return (
|
|
<Select
|
|
virtualized
|
|
options={options}
|
|
optionValue="value"
|
|
optionTextValue="label"
|
|
optionDisabled="disabled"
|
|
placeholder="Select an item…"
|
|
>
|
|
<Select.Trigger aria-label="Food">
|
|
<Select.Value<Item>>{state => state.selectedOption().label}</Select.Value>
|
|
<Select.Icon>
|
|
<CaretSortIcon />
|
|
</Select.Icon>
|
|
</Select.Trigger>
|
|
<Select.Portal>
|
|
<SelectContent options={options} />
|
|
</Select.Portal>
|
|
</Select>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Origin-aware animations
|
|
|
|
We expose a CSS custom property `--kb-select-content-transform-origin` which can be used to animate the content from its computed origin.
|
|
|
|
```
|
|
Copycss
|
|
/* style.css */
|
|
.select__content {
|
|
transform-origin: var(--kb-select-content-transform-origin);
|
|
animation: contentHide 250ms ease-in forwards;
|
|
}
|
|
|
|
.select__content[data-expanded] {
|
|
animation: contentShow 250ms ease-out;
|
|
}
|
|
|
|
@keyframes contentShow {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-8px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes contentHide {
|
|
from {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translateY(-8px);
|
|
}
|
|
}
|
|
```
|
|
|
|
```
|
|
Copycss
|
|
/* style.css */
|
|
.select__content {
|
|
transform-origin: var(--kb-select-content-transform-origin);
|
|
animation: contentHide 250ms ease-in forwards;
|
|
}
|
|
|
|
.select__content[data-expanded] {
|
|
animation: contentShow 250ms ease-out;
|
|
}
|
|
|
|
@keyframes contentShow {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-8px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes contentHide {
|
|
from {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translateY(-8px);
|
|
}
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Select
|
|
|
|
`Select` is equivalent to the `Root` import from `@kobalte/core/select` (and deprecated `Select.Root`).
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| options | `Array<T | U>`<br> An array of options to display as the available options. |
|
|
| optionValue | `keyof T | ((option: T) => string | number)`<br> Property name or getter function to use as the value of an option. This is the value that will be submitted when the select is part of a `<form>`. |
|
|
| optionTextValue | `keyof T | ((option: T) => string)`<br> Property name or getter function to use as the text value of an option for typeahead purpose. |
|
|
| optionDisabled | `keyof T | ((option: T) => boolean)`<br> Property name or getter function to use as the disabled flag of an option. |
|
|
| optionGroupChildren | `keyof U`<br> Property name that refers to the children options of an option group. |
|
|
| itemComponent | `Component<SelectItemComponentProps<T>>`<br> When NOT virtualized, the component to render as an item in the `Select.Listbox`. |
|
|
| sectionComponent | `Component<SelectSectionComponentProps<U>>`<br> When NOT virtualized, the component to render as a section in the `Select.Listbox`. |
|
|
| multiple | `boolean`<br> Whether the select allows multi-selection. |
|
|
| placeholder | `JSX.Element`<br> The content that will be rendered when no value or defaultValue is set. |
|
|
| value | `T | Array<T>`<br> The controlled value of the select. |
|
|
| defaultValue | `T | Array<T>`<br> The value of the select when initially rendered. Useful when you do not need to control the value. |
|
|
| onChange | `(value: T | Array<T>) => void`<br> Event handler called when the value changes. |
|
|
| open | `boolean`<br> The controlled open state of the select. |
|
|
| defaultOpen | `boolean`<br> The default open state when initially rendered. Useful when you do not need to control the open state. |
|
|
| onOpenChange | `(open: boolean) => void`<br> Event handler called when the open state of the select changes. |
|
|
| allowDuplicateSelectionEvents | `boolean`<br> Whether `onChange` should fire even if the new value is the same as the last. |
|
|
| disallowEmptySelection | `boolean`<br> Whether the select allows empty selection or not. |
|
|
| closeOnSelection | `boolean`<br> Whether the select closes after selection. |
|
|
| selectionBehavior | `'toggle' | 'replace'`<br> How selection should behave in the select. |
|
|
| virtualized | `boolean`<br> Whether the select uses virtual scrolling. |
|
|
| modal | `boolean`<br> Whether the select should be the only visible content for screen readers, when set to `true`: <br> \- interaction with outside elements will be disabled. <br> \- scroll will be locked. <br> \- focus will be locked inside the select content. <br> \- elements outside the select content will not be visible for screen readers. |
|
|
| preventScroll | `boolean`<br> Whether the scroll should be locked even if the select is not modal. |
|
|
| forceMount | `boolean`<br> Used to force mounting the select (portal, positioner and content) when more control is needed. Useful when controlling animation with SolidJS animation libraries. |
|
|
| name | `string`<br> The name of the select. Submitted with its owning form as part of a name/value pair. |
|
|
| validationState | `'valid' | 'invalid'`<br> Whether the select should display its "valid" or "invalid" visual styling. |
|
|
| required | `boolean`<br> Whether the user must select an item before the owning form can be submitted. |
|
|
| disabled | `boolean`<br> Whether the select is disabled. |
|
|
| readOnly | `boolean`<br> Whether the select items can be selected but not changed by the user. |
|
|
| autoComplete | `string`<br> Describes the type of autocomplete functionality the input should provide if any. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefautocomplete) |
|
|
|
|
`Select` also accepts the following props to customize the placement of the `Select.Content`.
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| placement | `Placement`<br> The placement of the select content. |
|
|
| gutter | `number`<br> The distance between the select content and the trigger element. |
|
|
| shift | `number`<br> The skidding of the select content along the trigger element. |
|
|
| flip | `boolean | string`<br> Controls the behavior of the select content when it overflows the viewport: <br> \- If a `boolean`, specifies whether the select content should flip to the opposite side when it overflows.<br> \- If a `string`, indicates the preferred fallback placements when it overflows.<br>The placements must be spaced-delimited, e.g. "top left". |
|
|
| slide | `boolean`<br> Whether the select content should slide when it overflows. |
|
|
| overlap | `boolean`<br> Whether the select content can overlap the trigger element when it overflows. |
|
|
| sameWidth | `boolean`<br> Whether the select content should have the same width as the trigger element. This will be exposed to CSS as `--kb-popper-anchor-width`. |
|
|
| fitViewport | `boolean`<br> Whether the select content should fit the viewport. If this is set to true, the select content will have `maxWidth` and `maxHeight` set to the viewport size. This will be exposed to CSS as `--kb-popper-available-width` and `--kb-popper-available-height`. |
|
|
| hideWhenDetached | `boolean`<br> Whether to hide the select content when the trigger element becomes occluded. |
|
|
| detachedPadding | `number`<br> The minimum padding in order to consider the trigger element occluded. |
|
|
| arrowPadding | `number`<br> The minimum padding between the arrow and the select content corner. |
|
|
| overflowPadding | `number`<br> The minimum padding between the select content and the viewport edge. This will be exposed to CSS as `--kb-popper-overflow-padding`. |
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-valid | Present when the select is valid according to the validation rules. |
|
|
| data-invalid | Present when the select is invalid according to the validation rules. |
|
|
| data-required | Present when the user must select an item before the owning form can be submitted. |
|
|
| data-disabled | Present when the select is disabled. |
|
|
| data-readonly | Present when the select is read only. |
|
|
|
|
`Select.Label`, `Select.Trigger`, `Select.Value`, `Select.Description` and `Select.ErrorMesssage` shares the same data-attributes.
|
|
|
|
### Select.Trigger
|
|
|
|
`Select.Trigger` consists of [Button](https://kobalte.dev/docs/core/components/button).
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-expanded | Present when the select is open. |
|
|
| data-closed | Present when the select is close. |
|
|
|
|
### Select.Value
|
|
|
|
| Render Prop | Description |
|
|
| --- | --- |
|
|
| selectedOption | `Accessor<T>`<br> The first (or only, in case of single select) selected option. |
|
|
| selectedOptions | `Accessor<T[]>`<br> An array of selected options. It will contain only one value in case of single select. |
|
|
| remove | `(option: T) => void`<br> A function to remove an option from the selection. |
|
|
| clear | `() => void`<br> A function to clear the selection. |
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-placeholder-shown | Present when the select placeholder is visible (no value selected). |
|
|
|
|
### Select.Icon
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-expanded | Present when the select is open. |
|
|
| data-closed | Present when the select is close. |
|
|
|
|
### Select.ErrorMessage
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| forceMount | `boolean`<br> Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries. |
|
|
|
|
### Select.Content
|
|
|
|
The popper positioner will copy the same `z-index` as the `Select.Content`.
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-expanded | Present when the select is open. |
|
|
| data-closed | Present when the select is close. |
|
|
|
|
### Select.Arrow
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| size | `number`<br> The size of the arrow. |
|
|
|
|
### Select.Listbox
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| scrollRef | `Accessor<HTMLElement | undefined>`<br> The ref attached to the scrollable element, used to provide automatic scrolling on item focus. If not provided, defaults to the listbox. |
|
|
| scrollToItem | `(key: string) => void`<br> When virtualized, the Virtualizer function used to scroll to the item of the given key. |
|
|
| children | `(items: Accessor<Collection<CollectionNode<T | U>>>) => JSX.Element`<br> When virtualized, a map function that receives an _items_ signal representing all items and sections. |
|
|
|
|
### Select.Item
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| item | `CollectionNode`<br> The collection node to render. |
|
|
|
|
| Data attribute | Description |
|
|
| --- | --- |
|
|
| data-disabled | Present when the item is disabled. |
|
|
| data-selected | Present when the item is selected. |
|
|
| data-highlighted | Present when the item is highlighted. |
|
|
|
|
`Select.ItemLabel`, `Select.ItemDescription` and `Select.ItemIndicator` shares the same data-attributes.
|
|
|
|
### Select.ItemIndicator
|
|
|
|
| Prop | Description |
|
|
| --- | --- |
|
|
| forceMount | `boolean`<br> Used to force mounting when more control is needed. Useful when controlling animation with SolidJS animation libraries. |
|
|
|
|
## Rendered elements
|
|
|
|
| Component | Default rendered element |
|
|
| --- | --- |
|
|
| `Select` | `div` |
|
|
| `Select.Label` | `span` |
|
|
| `Select.Description` | `div` |
|
|
| `Select.ErrorMessage` | `div` |
|
|
| `Select.Trigger` | `button` |
|
|
| `Select.Value` | `span` |
|
|
| `Select.Icon` | `span` |
|
|
| `Select.Portal` | `Portal` |
|
|
| `Select.Content` | `div` |
|
|
| `Select.Arrow` | `div` |
|
|
| `Select.Listbox` | `ul` |
|
|
| `Select.Section` | `li` |
|
|
| `Select.Item` | `li` |
|
|
| `Select.ItemLabel` | `div` |
|
|
| `Select.ItemDescription` | `div` |
|
|
| `Select.ItemIndicator` | `div` |
|
|
|
|
## Accessibility
|
|
|
|
### Keyboard Interactions
|
|
|
|
| Key | Description |
|
|
| --- | --- |
|
|
| `Space` | When focus is on the trigger, opens the select and focuses the first or selected item. <br> When focus is on an item, selects the focused item. |
|
|
| `Enter` | When focus is on the trigger, opens the select and focuses the first or selected item. <br> When focus is on an item, selects the focused item. |
|
|
| `ArrowDown` | When focus is on the trigger, opens the select and focuses the first or selected item. <br> When focus is on an item, moves focus to the next item. |
|
|
| `ArrowUp` | When focus is on the trigger, opens the select and focuses the last or selected item. <br> When focus is on an item, moves focus to the previous item. |
|
|
| `Home` | When focus is on an item, moves focus to first item. |
|
|
| `End` | When focus is on an item, moves focus to last item. |
|
|
| `ArrowRight` | In `Select`, when focus is on the trigger, change the selection to the next item. |
|
|
| `ArrowLeft` | In `Select`, when focus is on the trigger, change the selection to the previous item. |
|
|
| `Shift` \+ `ArrowDown` | In `Select`, moves focus to and toggles the selected state of the next item. |
|
|
| `Shift` \+ `ArrowUp` | In `Select`, moves focus to and toggles the selected state of the previous item. |
|
|
| `Shift` \+ `Space` | In `Select`, selects contiguous items from the most recently selected item to the focused item. |
|
|
| `Ctrl` \+ `Shift` \+ `Home` | In `Select`, selects the focused item and all options up to the first item. |
|
|
| `Ctrl` \+ `Shift` \+ `End` | In `Select`, selects the focused item and all options down to the last item. |
|
|
| `Ctrl` \+ `A` | In `Select`, selects all item in the list. |
|
|
| `Esc` | Closes the select and moves focus to the trigger. |
|
|
|
|
Previous[←Segmented Control](https://kobalte.dev/docs/core/components/segmented-control)Next[Separator→](https://kobalte.dev/docs/core/components/separator) |