Select
A Select component allows users pick a value from predefined options.
Features
Install
Install the component from your command line.
Anatomy
Import all parts and piece them together.
<script setup lang="ts">import * as select from '@destyler/select'import { normalizeProps, useMachine } from '@destyler/vue'import { computed, useId } from 'vue'
const [state, send] = useMachine( select.machine({ id: useId(), }),)
const api = computed(() => select.connect(state.value, send, normalizeProps))</script>
<template> <button v-bind="api.getTriggerProps()"></button> <div v-bind="api.getPositionerProps()"> <ul v-bind="api.getContentProps()" > <li v-bind="api.getItemProps({ item: '0' })" > <span v-bind="api.getItemIndicatorProps({ item: '0' })"></span> </li> </ul> </div></template>
import { normalizeProps, useMachine } from '@destyler/react'import * as select from '@destyler/select'import { useId } from 'react'
export default function Select() { const [state, send] = useMachine( select.machine({ id: useId(), }), )
const api = select.connect(state, send, normalizeProps)
return ( <> <button {...api.getTriggerProps()} ></button> <div {...api.getPositionerProps()}> <ul {...api.getContentProps()} > <li {...api.getItemProps({ item: '0' })} > <span {...api.getItemIndicatorProps({ item: '0' })} ></span> </li> </ul> </div> </> )}
<script lang="ts"> import * as select from '@destyler/select' import { normalizeProps, useMachine } from '@destyler/svelte'
const id = $props.id()
const [state, send] = useMachine( select.machine({ id, }), )
const api = $derived(select.connect(state, send, normalizeProps))</script>
<button {...api.getTriggerProps()} ></button><div {...api.getPositionerProps()} > <ul {...api.getContentProps()} > <li {...api.getItemProps({ item:'0' })} > <span {...api.getItemIndicatorProps({ item:'0' })} ></span> </li> </ul></div>
import * as select from '@destyler/select'import { normalizeProps, useMachine } from '@destyler/solid'import { createMemo, createUniqueId } from 'solid-js'
export default function Select() { const [state, send] = useMachine( select.machine({ id: createUniqueId(), }), )
const api = createMemo(() => select.connect(state, send, normalizeProps))
return ( <> <button {...api().getTriggerProps()}></button> <div {...api().getPositionerProps()}> <ul {...api().getContentProps()}> <li {...api().getItemProps({ item: '0' })} > <span {...api().getItemIndicatorProps({ item: '0' })}></span> </li> </ul> </div> </> )}
Setting the initial value
To set the initial value of the select, pass the value
property to the select destyler’s context.
const [state, send] = useMachine( select.machine({ value: ["vue"], }),)
Selecting multiple values
To allow selecting multiple values, set the multiple
property in the destyler’s context to true
.
const [state, send] = useMachine( select.machine({ multiple: true, }),)
Use custom object format
By default, the combobox collection expects an array of items with label
and value
properties.
To use a custom object format, pass the itemToString
and itemToValue
properties to the collection function.
-
itemToString
— A function that returns the string representation of an item. Used to compare items when filtering. -
itemToValue
— A function that returns the unique value of an item. -
itemToDisabled
— A function that returns the disabled state of an item.
const collection = combobox.collection({ // custom object format items: [ { id: 1, fruit: "Vue", available: true, quantity: 10 }, { id: 2, fruit: "React", available: false, quantity: 5 }, { id: 3, fruit: "Svelte", available: true, quantity: 3 }, { id: 4, fruit: "Solid", available: false, quantity: 0 }, //... ], // convert item to string itemToString(item) { return item.fruit }, // convert item to value itemToValue(item) { return item.id }, // convert item to disabled state itemToDisabled(item) { return !item.available || item.quantity === 0 },})
// use the collectionconst [state, send] = useMachine( select.machine({ id: useId(), collection, }),)
Usage within a form
To use select within a form, you’ll need to:
-
Pass the
name
property to the select destyler’s context -
Render a
hidden select
element usingapi.getSelectProps()
<script setup lang="ts">import * as select from '@destyler/select'import { normalizeProps, useMachine } from '@destyler/vue'import { computed, useId } from 'vue'
const [state, send] = useMachine( select.machine({ id: useId(), name: "country", }),)
const api = computed(() => select.connect(state.value, send, normalizeProps))</script>
<template> <select v-bind="api.getHiddenSelectProps()"></select> <button v-bind="api.getTriggerProps()"></button> <div v-bind="api.getPositionerProps()"> <ul v-bind="api.getContentProps()" > <li v-bind="api.getItemProps({ item: '0' })" > <span v-bind="api.getItemIndicatorProps({ item: '0' })"></span> </li> </ul> </div></template>
import { normalizeProps, useMachine } from '@destyler/react'import * as select from '@destyler/select'import { useId } from 'react'
export default function Select() { const [state, send] = useMachine( select.machine({ id: useId(), name: "country", }), )
const api = select.connect(state, send, normalizeProps)
return ( <> <select {...api.getHiddenSelectProps()}></select> <button {...api.getTriggerProps()} ></button> <div {...api.getPositionerProps()}> <ul {...api.getContentProps()} > <li {...api.getItemProps({ item: '0' })} > <span {...api.getItemIndicatorProps({ item: '0' })} ></span> </li> </ul> </div> </> )}
<script lang="ts"> import * as select from '@destyler/select' import { normalizeProps, useMachine } from '@destyler/svelte'
const id = $props.id()
const [state, send] = useMachine( select.machine({ id, name: "country", }), )
const api = $derived(select.connect(state, send, normalizeProps))</script>
<select {...api.getHiddenSelectProps()}></select><button {...api.getTriggerProps()} ></button><div {...api.getPositionerProps()} > <ul {...api.getContentProps()} > <li {...api.getItemProps({ item:'0' })} > <span {...api.getItemIndicatorProps({ item:'0' })} ></span> </li> </ul></div>
import * as select from '@destyler/select'import { normalizeProps, useMachine } from '@destyler/solid'import { createMemo, createUniqueId } from 'solid-js'
export default function Select() { const [state, send] = useMachine( select.machine({ id: createUniqueId(), name: "country", }), )
const api = createMemo(() => select.connect(state, send, normalizeProps))
return ( <> <select {...api().getHiddenSelectProps()}></select> <button {...api().getTriggerProps()}></button> <div {...api().getPositionerProps()}> <ul {...api().getContentProps()}> <li {...api().getItemProps({ item: '0' })} > <span {...api().getItemIndicatorProps({ item: '0' })}></span> </li> </ul> </div> </> )}
Disabling the select
To disable the select, set the disabled
property in the destyler’s context to true
.
const [state, send] = useMachine( select.machine({ id: useId(), collection: select.collection({ items: countries, isItemDisabled(item) { return item.disabled }, }), }),)
Close on select
This behaviour ensures that the menu is closed when an item is selected
and is true
by default. It’s only concerned with when an item is selected
with pointer, space key or enter key. To disable the behaviour,
set the closeOnSelect
property in the destyler’s context to false
.
const [state, send] = useMachine( select.machine({ closeOnSelect: false, }),)
Looping the keyboard navigation
When navigating with the select using the arrow down and up keys,
the select stops at the first and last options.
If you need want the navigation to loop back to the first or last option,
set the loop: true
in the destyler’s context.
const [state, send] = useMachine( select.machine({ loop: true, }),)
Listening for highlight changes
When an item is highlighted with the pointer or keyboard, use the onHighlightChange
to listen for the change and do something with it.
const [state, send] = useMachine( select.machine({ onHighlightChange(details) { // details => { // highlightedValue: string | null, // highlightedItem: CollectionItem | null // } console.log(details) }, }),)
Listening for selection changes
When an item is selected, use the onValueChange
property to listen for the change and do something with it.
const [state, send] = useMachine( select.machine({ onValueChange(details) { // details => { value: string[], items: Item[] } console.log(details) }, }),)
Listening for open and close events
When the select is opened or closed, the onOpenChange
callback is called. You can listen for these events and do something with it.
const [state, send] = useMachine( select.machine({ onOpenChange(details) { // details => { open: boolean } console.log("Select opened") }, }),)
Styling guide
Earlier, we mentioned that each Select part has a
data-part
attribute added to them to select and style them in the DOM.
Open and closed state
When the select is open, the trigger and content is given a data-state
attribute.
[data-part="trigger"][data-state="open"] { /* styles for open or closed state */}
[data-part="content"][data-state="open"] { /* styles for open or closed state */}
Selected state
Items are given a data-state
attribute, indicating whether they are selected.
[data-part="item"][data-state="checked"] { /* styles for selected or unselected state */}
Highlighted state
When an item is highlighted, via keyboard navigation or pointer, it is given a data-highlighted
attribute.
[data-part="item"][data-highlighted] { /* styles for highlighted state */}
Invalid state
When the select is invalid, the label and trigger is given a data-invalid
attribute.
[data-part="label"][data-invalid] { /* styles for invalid state */}
[data-part="trigger"][data-invalid] { /* styles for invalid state */}
Disabled state
When the select is disabled, the trigger and label is given a data-disabled
attribute.
[data-part="trigger"][data-disabled] { /* styles for disabled select state */}
[data-part="label"][data-disabled] { /* styles for disabled label state */}
[data-part="item"][data-disabled] { /* styles for disabled option state */}
Empty state
When no option is selected, the trigger is given a data-placeholder-shown
attribute.
[data-part="trigger"][data-placeholder-shown] { /* styles for empty select state */}
Methods and Properties
Machine Context
The combobox machine exposes the following context properties:
ListCollection<any>
Partial<{ root: string; content: string; control: string; trigger: string; clearTrigger: string; label: string; hiddenSelect: string; positioner: string; item(id: string | number): string; itemGroup(id: string | number): string; itemGroupLabel(id: string | number): string; }>
string
string
boolean
boolean
boolean
boolean
boolean
(details: HighlightChangeDetails<T>) => void
(details: ValueChangeDetails<T>) => void
(details: OpenChangeDetails) => void
PositioningOptions
string[]
string
boolean
boolean
boolean
boolean
(details: ScrollToIndexDetails) => void
boolean
boolean
"ltr" | "rtl"
string
() => ShadowRoot | Node | Document
(event: PointerDownOutsideEvent) => void
(event: FocusOutsideEvent) => void
(event: InteractOutsideEvent) => void
Machine API
The select api
exposes the following methods:
boolean
boolean
boolean
string
V
(value: string) => void
V[]
boolean
string[]
string
(value: string) => void
() => void
(value: string[]) => void
(value?: string) => void
() => void
(props: ItemProps<any>) => ItemState
(open: boolean) => void
ListCollection<V>
(collection: ListCollection<V>) => void
(options?: Partial<PositioningOptions>) => void
boolean
boolean
Data Attributes
Root
data-scope
data-part
data-invalid
data-readonly
Label
data-scope
data-part
data-disabled
data-invalid
data-readonly
Control
data-scope
data-part
data-state
data-focus
data-disabled
data-invalid
ValueText
data-scope
data-part
data-disabled
data-invalid
data-focus
Trigger
data-scope
data-part
data-state
data-disabled
data-invalid
data-readonly
data-placement
data-placeholder-shown
Indicator
data-scope
data-part
data-state
data-disabled
data-invalid
data-readonly
Item
data-scope
data-part
data-value
data-state
data-highlighted
data-disabled
ItemText
data-scope
data-part
data-state
data-disabled
data-highlighted
ItemIndicator
data-scope
data-part
data-state
ItemGroup
data-scope
data-part
data-disabled
ClearTrigger
data-scope
data-part
data-invalid
Content
data-scope
data-part
data-state
data-placement
Accessibility
Keyboard Interaction
Space
Enter
ArrowDown
ArrowUp
Esc
A-Z a-z