ListBox
A listbox is a list of options with a selection and a keyboard. It stays on the page — a Select is the same list inside a popover with a trigger in front of it, and a Menu looks similar but fires actions rather than holding a choice.
import { ListBox, ListBoxItem, ListBoxItemIndicator, ListBoxSection } from "ropav";Each ListBoxItem needs an id — that is the key selection is carried in. ListBoxItemIndicator is the tick on a selected row, and ListBoxSection groups rows under a heading.
list-box-selection.vue
<script setup lang="ts">
import { Label, ListBox, ListBoxItem, ListBoxItemIndicator, ListBoxSection, Surface } from "ropav";
const people = [
{ id: "ada", name: "Ada Lovelace" },
{ id: "grace", name: "Grace Hopper" },
{ id: "katherine", name: "Katherine Johnson" },
] as const;
</script>
<template>
<Surface class="surface">
<ListBox aria-label="Reviewers" :default-selected-keys="['ada']" selection-mode="multiple">
<ListBoxSection>
<ListBoxItem v-for="person in people" :id="person.id" :key="person.id">
<Label>{{ person.name }}</Label>
<ListBoxItemIndicator />
</ListBoxItem>
</ListBoxSection>
</ListBox>
</Surface>
</template>
<style scoped>
.surface {
width: calc(var(--rp-spacing) * 56);
padding: 0;
}
</style>Selection
selection-mode is none by default, which makes a listbox a plain list. single and multiple turn it into a control, and selection-behavior decides what a press means when several rows can be chosen: toggle flips the row under the pointer, replace clears the rest unless a modifier is held.
disallow-empty-selection keeps one row chosen; disabled-keys takes rows out of play, and disabled-behavior says whether a disabled row can still be focused — which matters, because a row nobody can land on is a row nobody can be told about.
Long lists
For a list too long to render, wrap it in a Virtualizer and give the listbox its items. It then calls its slot once per rendered row instead of once per datum. Two props exist only for that case: item-key names each row, and item-text-value is what typeahead matches on — a rendered row names itself from its content, but the rows that are not rendered can only be reached through the data.
ListBoxLoadMoreItem is the row at the end that fetches the next page.
Accessibility
- The list is a
role="listbox"and each row arole="option", so the count and the position are announced along with the row. - Give the listbox a name.
aria-labeloraria-labelledby— without one, a reader is told they are in a listbox and nothing about which. - Typeahead moves focus by typing, which is why
item-text-valuematters on a virtualized list: without it, typing can only reach what happens to be on screen. - The indicator is a picture of the selection, not the selection itself —
aria-selectedon the row is what is announced, so a row reads correctly with no indicator at all.
API
ListBox
| Prop | Type | Default | Description |
|---|---|---|---|
defaultSelectedKeys | "all" | Iterable<CollectionKey> | — | Selected keys the listbox starts with. |
disabledBehavior | "all" | "selection" | "all" | Whether a disabled key is also unfocusable. |
disabledKeys | Iterable<CollectionKey> | — | Keys that cannot be selected, and by default cannot be focused either. |
disallowEmptySelection | boolean | — | Whether the listbox must always keep something selected. |
dragAndDropHooks | DragAndDropHooks | — | Drag and drop behaviour, from useDragAndDrop. |
itemKey | ((item: T, index: number) => CollectionKey) | — | An item's key. Defaults to its own id, then key, then its index. |
items | readonly T[] | — | The data to render, for a virtualized listbox. Only meaningful inside a Virtualizer, which renders a window of the items rather than all of them. The listbox then calls its default slot once per rendered item. |
itemTextValue | ((item: T) => string | undefined) | — | Text an item is matched on by typeahead. A rendered item names itself from its content, as in the React build. This is for the items that are *not* rendered: typeahead has to reach them, and only the data can answer. |
selectedKeys | "all" | Iterable<CollectionKey> | — | Selected keys, for controlled use. |
selectionBehavior | "replace" | "toggle" | "toggle" | What a press means when several items can be selected. |
selectionMode | "none" | "single" | "multiple" | "none" | Whether one, many or no items can be selected. |
size | "xs" | "sm" | "md" | "lg" | "md" | How tall a row stands, matching a field of the same size. |
variant | "danger" | "default" | — | Visual variant. |
Slots: default, empty.
| Event | Payload |
|---|---|
action | CollectionKey |
selectionChange | CollectionSelection |
update:selectedKeys | CollectionSelection |
ListBoxDropIndicator
| Prop | Type | Default | Description |
|---|---|---|---|
target* | DropTarget | — | The position this indicator stands for. |
ListBoxLoadMoreItem
| Prop | Type | Default | Description |
|---|---|---|---|
isLoading | boolean | — | Whether the next page is on its way, which is when the indicator row is rendered. |
scrollOffset | number | 1 | How far from the end of the scroll box loading starts, as a multiple of the box's own height. 1 is one screen ahead; 0 waits until the very end comes into view. |
Slots: default.
| Event | Payload |
|---|---|
loadMore | — |
Rows and their groups are separate components.
ListBoxItem
| Prop | Type | Default | Description |
|---|---|---|---|
id | CollectionKey | — | The item's key within the collection. Generated when omitted. |
isDisabled | boolean | — | Disables the item: it can be neither focused nor selected. |
textValue | string | — | Text the item is matched on by typeahead. Read from its content when omitted. |
variant | "danger" | "default" | — | Visual variant. |
The default slot receives:
| Slot prop | Type |
|---|---|
isDisabled | boolean |
isDragging | boolean | undefined |
isDropTarget | boolean | undefined |
isFocused | boolean |
isFocusVisible | boolean |
isHovered | boolean |
isPressed | boolean |
isSelected | boolean |
selectionMode | "multiple" | "none" | "single" |
ListBoxItemIndicator
The default slot receives:
| Slot prop | Type |
|---|---|
isSelected | boolean |
| Prop | Type | Default | Description |
|---|---|---|---|
ariaLabel | string | — | Accessible name for the section, for when it has no visible heading. |
Slots: default.