backdrop
组件

选择列表框

允许用户在选中与未选中状态之间切换的控件。
Fruits
Apple
Banana
Blueberry
Grapes
Pineapple
Vegetables
Aubergine
Broccoli
Carrot
Courgette
Leek

功能特性

  • 可被控制或非受控。
  • 支持项目、标签、项目组。
  • 焦点管理完备。
  • 完整的键盘导航支持。
  • 支持从右到左方向。
  • 不同的选择行为。

安装

通过命令行安装该组件。

sh
$ npm add reka-ui

结构构成

导入所有部分并将它们组合在一起。

vue
<script setup>
import { ListboxContent, ListboxFilter, ListboxGroup, ListboxGroupLabel, ListboxItem, ListboxItemIndicator, ListboxRoot, ListboxVirtualizer } from 'reka-ui'
</script>

<template>
  <ListboxRoot>
    <ListboxFilter />

    <ListboxContent>
      <ListboxItem>
        <ListboxItemIndicator />
      </ListboxItem>

      <!-- 或使用分组 -->
      <ListboxGroup>
        <ListboxGroupLabel />
        <ListboxItem>
          <ListboxItemIndicator />
        </ListboxItem>
      </ListboxGroup>

      <!-- 或使用虚拟化 -->
      <ListboxVirtualizer>
        <ListboxItem>
          <ListboxItemIndicator />
        </ListboxItem>
      </ListboxVirtualizer>
    </ListboxContent>
  </ListboxRoot>
</template>

API 参考

Root

包含选择列表框的所有部分。在 form 内使用时还会渲染一个 input 元素,以确保事件能正确传播。

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

by
string | ((a: AcceptableValue, b: AcceptableValue) => boolean)

Use this to compare objects by a particular field, or pass your own comparison function for complete control over how objects are compared.

defaultValue
AcceptableValue | AcceptableValue[]

The value of the listbox when initially rendered. Use when you do not need to control the state of the Listbox

dir
'ltr' | 'rtl'

The reading direction of the listbox when applicable.
If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode.

disabled
boolean

When true, prevents the user from interacting with listbox

highlightOnHover
boolean

When true, hover over item will trigger highlight

modelValue
AcceptableValue | AcceptableValue[]

The controlled value of the listbox. Can be binded with with v-model.

multiple
boolean

Whether multiple options can be selected or not.

name
string

The name of the field. Submitted with its owning form as part of a name/value pair.

orientation
'vertical'
'vertical' | 'horizontal'

The orientation of the listbox.
Mainly so arrow navigation is done accordingly (left & right vs. up & down)

required
boolean

When true, indicates that the user must set the value before the owning form can be submitted.

selectionBehavior
'toggle'
'replace' | 'toggle'

How multiple selection should behave in the collection.

EmitPayload
entryFocus
[event: CustomEvent<any>]

Event handler called when container is being focused. Can be prevented.

highlight
[payload: { ref: HTMLElement; value: AcceptableValue; }]

Event handler when highlighted element changes.

leave
[event: Event]

Event handler called when the mouse leave the container

update:modelValue
[value: AcceptableValue]

Event handler called when the value changes.

Slots (default)Payload
modelValue
AcceptableValue | AcceptableValue[] | undefined

Current active value

Data AttributeValue
[data-disabled]禁用时存在

Filter

用于执行筛选的输入元素。

PropDefaultType
as
'input'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

autoFocus
boolean

Focus on element when mounted.

disabled
boolean

When true, prevents the user from interacting with item

modelValue
string

The controlled value of the filter. Can be binded with with v-model.

EmitPayload
update:modelValue
[string]

Event handler called when the value changes.

Slots (default)Payload
modelValue
string | undefined

Current input values

Data AttributeValue
[data-disabled]禁用时存在

Content

包含所有选择列表框组和项目。

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Item

项目组件。

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

disabled
boolean

When true, prevents the user from interacting with the item.

value*
AcceptableValue

The value given as data when submitted with a name.

EmitPayload
select
[event: SelectEvent<AcceptableValue>]

Event handler called when the selecting item.
It can be prevented by calling event.preventDefault.

Data AttributeValue
[data-state]"checked" | "unchecked"
[data-highlighted]高亮时存在
[data-disabled]禁用时存在

ItemIndicator

在项目被选中时渲染。您可以直接为此元素设置样式,或将其用作包装器来放置图标,或两者兼有。

PropDefaultType
as
'span'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Group

用于对多个项目进行分组。与 ListboxGroupLabel 结合使用,通过自动标签化确保良好的可访问性。

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

GroupLabel

用于渲染组的标签。使用方向键无法使其获得焦点。

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwritten by asChild.

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

for
string

Virtualizer

虚拟容器,用于实现列表虚拟化。

PropDefaultType
estimateSize
number

Estimated size (in px) of each item

options*
AcceptableValue[]

List of items

overscan
number

Number of items rendered outside the visible area

textContent
((option: AcceptableValue) => string)

Text content for each item to achieve type-ahead feature

Slots (default)Payload
option
null | string | number | bigint | Record<string, any>
virtualizer
Virtualizer<HTMLElement, Element>
virtualItem
VirtualItem

示例

绑定对象作为值

与仅允许提供字符串作为值的原生 HTML 表单控件不同,reka-ui 也支持绑定复杂对象。

vue
<script setup lang="ts">
import { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot } from 'reka-ui'
import { ref } from 'vue'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]
const selectedPeople = ref(people[0])
</script>

<template>
  <ListboxRoot v-model="selectedPeople">
    <ListboxContent>
      <ListboxItem
        v-for="person in people"
        :key="person.id"
        :value="person"
        :disabled="person.unavailable"
      >
        {{ person.name }}
      </ListboxItem>
    </ListboxContent>
  </ListboxRoot>
</template>

选择多个值

Listbox 组件允许您选择多个值。您可以通过提供一个值数组而非单个值来启用此功能。

vue
<script setup lang="ts">
import { ListboxRoot } from 'reka-ui'
import { ref } from 'vue'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]
const selectedPeople = ref([people[0], people[1]])
</script>

<template>
  <ListboxRoot
    v-model="selectedPeople"
    multiple
  >
    ...
  </ListboxRoot>
</template>

自定义筛选

vue
<script setup lang="ts">
import { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot, useFilter } from 'reka-ui'
import { ref } from 'vue'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
]
const selectedPeople = ref(people[0])
const searchTerm = ref('')

const { startsWith } = useFilter({ sensitivity: 'base' })
const filteredPeople = computed(() => people.filter(p => startsWith(p.name, searchTerm.value)))
</script>

<template>
  <ListboxRoot v-model="selectedPeople">
    <ListboxFilter v-model="searchTerm" />
    <ListboxContent>
      <ListboxItem
        v-for="person in filteredPeople"
        :key="person.id"
        :value="person"
      >
        {{ person.name }}
      </ListboxItem>
    </ListboxContent>
  </ListboxRoot>
</template>

虚拟列表

渲染长列表可能会降低应用速度,因此使用虚拟化将显著提高性能。

有关虚拟化的更多常规信息,请参阅 虚拟化指南

vue
<script setup lang="ts">
import { ListboxContent, ListboxFilter, ListboxItem, ListboxRoot, ListboxVirtualizer } from 'reka-ui'
import { ref } from 'vue'

const people = [
  { id: 1, name: 'Durward Reynolds' },
  { id: 2, name: 'Kenton Towne' },
  { id: 3, name: 'Therese Wunsch' },
  { id: 4, name: 'Benedict Kessler' },
  { id: 5, name: 'Katelyn Rohan' },
  // 以及更多
]
</script>

<template>
  <ListboxRoot>
    <ListboxContent>
      <ListboxVirtualizer
        v-slot="{ option }"
        :options="people"
        :text-content="(opt) => opt.name"
      >
        <ListboxItem :value="option">
          {{ person.name }}
        </ListboxItem>
      </ListboxVirtualizer>
    </ListboxContent>
  </ListboxRoot>
</template>

可访问性

遵循 选择列表框 WAI-ARIA 设计模式

键盘交互

KeyDescription
Enter
当高亮在 ListboxItem 上时,选中获得焦点的项目。
ArrowDown
当焦点在 ListboxItem 上时,将焦点移动到下一个项目。
ArrowUp
当焦点在 ListboxItem 上时,将焦点移动到上一个项目。
Home
将焦点和高亮移动到第一个项目。
End
将焦点和高亮移动到最后一个项目。
Ctrl/Cmd + A
选择所有项目。