Lzh on GitHub

Combobox

从具有完整键盘支持的建议值列表中进行选择。
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { ComboboxAnchor, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxLabel, ComboboxRoot, ComboboxSeparator, ComboboxTrigger, ComboboxViewport } from 'reka-ui'

const options = [
  { name: 'Fruit', children: [
      { name: 'Apple' },
      { name: 'Banana' },
      { name: 'Orange' },
      { name: 'Honeydew' },
      { name: 'Grapes' },
      { name: 'Watermelon' },
      { name: 'Cantaloupe' },
      { name: 'Pear' },
    ] },
  { name: 'Vegetable', children: [
      { name: 'Cabbage' },
      { name: 'Broccoli' },
      { name: 'Carrots' },
      { name: 'Lettuce' },
      { name: 'Spinach' },
      { name: 'Bok Choy' },
      { name: 'Cauliflower' },
      { name: 'Potatoes' },
    ] },
]
</script>

<template>
  <ComboboxRoot
    class="relative"
  >
    <ComboboxAnchor class="min-w-[160px] inline-flex items-center justify-between rounded-lg border px-[15px] text-xs leading-none h-[35px] gap-[5px] bg-white text-grass11 hover:bg-stone-50 shadow-sm focus:shadow-[0_0_0_2px] focus:shadow-black data-[placeholder]:text-grass9 outline-none">
      <ComboboxInput
        class="!bg-transparent outline-none text-grass11 h-full selection:bg-grass5 placeholder-stone-400"
        placeholder="Placeholder..."
      />
      <ComboboxTrigger>
        <Icon
          icon="radix-icons:chevron-down"
          class="h-4 w-4 text-grass11"
        />
      </ComboboxTrigger>
    </ComboboxAnchor>

    <ComboboxContent class="absolute z-10 w-full mt-1 min-w-[160px] bg-white overflow-hidden rounded-lg shadow-sm border will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade">
      <ComboboxViewport class="p-[5px]">
        <ComboboxEmpty class="text-mauve8 text-xs font-medium text-center py-2" />

        <template
          v-for="(group, index) in options"
          :key="group.name"
        >
          <ComboboxGroup>
            <ComboboxSeparator
              v-if="index !== 0"
              class="h-[1px] bg-grass6 m-[5px]"
            />

            <ComboboxLabel class="px-[25px] text-xs leading-[25px] text-mauve11">
              {{ group.name }}
            </ComboboxLabel>

            <ComboboxItem
              v-for="option in group.children"
              :key="option.name"
              :value="option.name"
              class="text-xs leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-grass9 data-[highlighted]:text-grass1"
            >
              <ComboboxItemIndicator
                class="absolute left-0 w-[25px] inline-flex items-center justify-center"
              >
                <Icon icon="radix-icons:check" />
              </ComboboxItemIndicator>
              <span>
                {{ option.name }}
              </span>
            </ComboboxItem>
          </ComboboxGroup>
        </template>
      </ComboboxViewport>
    </ComboboxContent>
  </ComboboxRoot>
</template>

特性

  • 可作为受控非受控组件使用。
  • 提供 2 种定位模式
  • 支持项目标签项目组
  • 焦点完全受管理。
  • 完整的键盘导航
  • 支持自定义占位符
  • 支持从右到左方向。

安装

在命令行中安装该组件:

$ npm add reka-ui

结构

导入所有部件并将其组合起来。

<script setup lang="ts">
import {
  ComboboxAnchor,
  ComboboxArrow,
  ComboboxCancel,
  ComboboxContent,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxItemIndicator,
  ComboboxLabel,
  ComboboxPortal,
  ComboboxRoot,
  ComboboxSeparator,
  ComboboxTrigger,
  ComboboxViewport,
} from 'reka-ui'
</script>

<template>
  <ComboboxRoot>
    <ComboboxAnchor>
      <ComboboxInput />
      <ComboboxTrigger />
      <ComboboxCancel />
    </ComboboxAnchor>
    
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxViewport>
          <ComboboxItem>
            <ComboboxItemIndicator />
          </ComboboxItem>
          
          <ComboboxGroup>
            <ComboboxLabel />
            <ComboboxItem>
              <ComboboxItemIndicator />
            </ComboboxItem>
          </ComboboxGroup>
          <ComboboxSeparator />
        </ComboboxViewport>
        
        <ComboboxArrow />
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

API 参考

Root

包含 Combobox 的所有部分。

Prop默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
bystring | ((a: AcceptableValue, b: AcceptableValue) => boolean)用于按特定字段比较对象,或传递您自己的比较函数以完全控制对象的比较方式。
defaultOpenboolean组合框初始渲染时的打开状态。当您不需要控制其打开状态时使用。
defaultValueAcceptableValue | AcceptableValue[]列表框初始渲染时的值。当您不需要控制列表框的状态时使用。
dir'ltr' | 'rtl'适用时列表框的阅读方向。如果省略,则全局继承自 ConfigProvider 或假定 LTR(从左到右)阅读模式。
disabledboolean当为 true 时,阻止用户与列表框交互。
highlightOnHoverboolean当为 true 时,鼠标悬停在项目上将触发高亮。
ignoreFilterboolean当为 true 时,禁用默认过滤器。
modelValueAcceptableValue | AcceptableValue[]列表框的受控值。可通过 v-model 绑定。
multipleboolean是否可以选择多个选项。
namestring字段的名称。作为名称/值对的一部分与所属表单一起提交。
openboolean组合框的受控打开状态。可通过 v-model:open 绑定。
requiredboolean当为 true 时,表示用户必须设置值,然后才能提交所属表单。
resetSearchTermOnBlurtrueboolean当组合框输入框失去焦点时是否重置搜索词。
resetSearchTermOnSelecttrueboolean当组合框值被选中时是否重置搜索词。

EmitPayload

事件名称Payload描述
highlight[payload: { ref: HTMLElement; value: AcceptableValue; }]高亮元素更改时调用的事件处理程序。
update:modelValue[value: AcceptableValue]值更改时调用的事件处理程序。
update:open[value: boolean]组合框打开状态更改时调用的事件处理程序。

默认插槽

| 插槽参数 | 插槽参数类型 | 描述 | | :---------- | :------------- | | open | boolean | 当前打开状态 | | modelValue | AcceptableValue \| AcceptableValue[] | 当前活动值 |

Anchor

如果您将 ComboboxContentposition 设置为 popper,则用作锚点。

Prop默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
referenceReferenceElement用于定位的参考(或锚点)元素。如果未提供,将使用当前组件作为锚点。

Input

用于搜索组合框项目的输入组件。

Prop默认值类型描述
as'input'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
autoFocusboolean挂载时聚焦元素。
disabledboolean当为 true 时,阻止用户与项目交互。
displayValue((val: any) => string)选中项目输入的显示值。不适用于 multiple
modelValuestring过滤器的受控值。可通过 v-model 绑定。

事件

事件名称Payload描述
update:modelValue[string]值更改时调用的事件处理程序。

Trigger

切换组合框内容的按钮。

Prop默认值类型描述
as'button'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
disabledboolean当为 true 时,阻止用户与项目交互。

数据属性

数据属性
[data-state]"open" | "closed"
[data-disabled]禁用时存在。

Cancel

清除搜索词的按钮。

Prop默认值类型描述
as'button'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南

Empty

当没有项目匹配查询时显示。

Prop默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南

Portal

使用时,将内容部分传送到 body

您需要将 ComboboxContentposition 设置为 "popper",以确保位置像 PopoverDropdownMenu 一样自动计算。

Prop默认值类型描述
deferboolean推迟解析 Teleport 目标,直到应用程序的其他部分挂载(需要 Vue 3.5.0+)。
disabledboolean禁用传送并内联渲染组件。
forceMountboolean当需要更多控制时用于强制挂载。在与 Vue 动画库一起控制动画时很有用。
tostring | HTMLElementVue 原生 teleport 组件的 to 属性。

内容(Content)

当组合框打开时弹出的组件。

Content 组件基于 Presence 构建,支持各种动画技术,同时保留对 Presence 组件发出的事件的访问。

Props

属性名称默认值类型描述
align'start' | 'center' | 'end'相对于触发器的首选对齐方式。当发生碰撞时可能会改变。
alignOffsetnumberstartend 对齐选项的像素偏移量。
arrowPaddingnumber箭头与内容边缘之间的填充。如果您的内容有 border-radius,这将防止它溢出角落。
as'div'AsTag | Component此组件应渲染为的元素或组件。可以通过 asChild 覆盖。
asChildboolean将默认渲染的元素更改为作为子元素传递的元素,合并它们的属性和行为。详情请阅读我们的 组合 (Composition) 指南。
avoidCollisionsboolean当为 true 时,会覆盖 sidealign 偏好设置,以防止与边界边缘发生碰撞。
bodyLockbooleandocument.body 将被锁定,并且滚动将被禁用。
collisionBoundaryElement | (Element | null)[] | null用作碰撞边界的元素。默认情况下是视口,但您可以提供额外的元素以包含在此检查中。
collisionPaddingnumber | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>从边界边缘开始进行碰撞检测的像素距离。接受一个数字(所有边相同),或一个部分填充对象,例如:{ top: 20, left: 20 }
disableOutsidePointerEventsboolean当为 true 时,将禁用 DismissableLayer 外部元素的悬停/焦点/点击交互。用户需要两次点击外部元素才能与其交互:第一次关闭 DismissableLayer,第二次触发元素。
disableUpdateOnLayoutShiftboolean当布局发生变化时,是否禁用内容的位置更新。
forceMountboolean在需要更多控制时用于强制挂载。在与 Vue 动画库控制动画时很有用。
hideWhenDetachedboolean当触发器完全被遮挡时是否隐藏内容。
position'inline' | 'popper'要使用的定位模式。inline 是默认值,您可以使用 CSS 控制位置。popper 以与我们其他原语相同的方式定位内容,例如 PopoverDropdownMenu
positionStrategy'fixed' | 'absolute'要使用的 CSS position 属性类型。
prioritizePositionboolean强制内容定位在视口内。可能会与参考元素重叠,这可能不是期望的结果。
referenceReferenceElement将设置为参考元素以定位浮动元素的自定义元素或虚拟元素。如果提供,它将替换默认的锚点元素。
side'top' | 'right' | 'bottom' | 'left'打开时,相对于触发器首选的渲染边。当发生碰撞且 avoidCollisions 启用时,将反转。
sideOffsetnumber与触发器的像素距离。
sticky'partial' | 'always'沿对齐轴的粘性行为。partial 将使内容保持在边界内,只要触发器至少部分在边界内,而 always 将使内容保持在边界内,无论如何。
updatePositionStrategy'always' | 'optimized'在每个动画帧上更新浮动元素位置的策略。

事件 (Emits)

事件名称Payload描述
escapeKeyDown[event: KeyboardEvent]当按下 Esc 键时触发。可以调用 event.preventDefault() 阻止默认行为。
focusOutside[event: FocusOutsideEvent]当焦点移到 DismissableLayer 外部时触发。可以调用 event.preventDefault() 阻止默认行为。
interactOutside[event: PointerDownOutsideEvent | FocusOutsideEvent]当在 DismissableLayer 外部发生交互时触发。具体来说,当在外部发生 pointerdown 事件或焦点移到外部时。可以调用 event.preventDefault() 阻止默认行为。
pointerDownOutside[event: PointerDownOutsideEvent]pointerdown 事件在 DismissableLayer 外部发生时触发。可以调用 event.preventDefault() 阻止默认行为。

数据属性 (Data Attributes)

数据属性
[data-state]"open" | "closed"
[data-side]"left" | "right" | "bottom" | "top"
[data-align]"start" | "end" | "center"

CSS 变量 (CSS Variables)

CSS 变量描述
--reka-combobox-content-transform-origin从内容和箭头位置/偏移量计算的 transform-origin。仅当 position="popper" 时存在。
--reka-combobox-content-available-width触发器和边界边缘之间的剩余宽度。仅当 position="popper" 时存在。
--reka-combobox-content-available-height触发器和边界边缘之间的剩余高度。仅当 position="popper" 时存在。
--reka-combobox-trigger-width触发器的宽度。仅当 position="popper" 时存在。
--reka-combobox-trigger-height触发器的高度。仅当 position="popper" 时存在。

视口 (Viewport)

包含所有项目可滚动的视口。

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
noncestring将为样式标签添加 nonce 属性,可由内容安全策略使用。如果省略,则全局继承自 ConfigProvider

项目 (Item)

包含组合框项目的组件。

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
disabledboolean当为 true 时,阻止用户与该项目交互。
textValuestring项目内容的字符串表示。如果子元素不是纯文本,则 textValue 属性也必须设置为纯文本表示,该文本将用于组合框中的自动完成。
value*AcceptableValue作为数据提交时随 name 提交的值。

EmitPayload

事件Payload描述
select[event: SelectEvent<AcceptableValue>]选择项目时调用的事件处理程序。可以通过调用 event.preventDefault() 阻止默认行为。

数据属性 (Data Attributes)

数据属性
[data-state]"checked" | "unchecked"
[data-highlighted]高亮时存在。
[data-disabled]禁用时存在。

项目指示器 (ItemIndicator)

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

属性默认值类型描述
as'span'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南

组 (Group)

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

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南

标签 (Label)

用于渲染组的标签。它不能通过箭头键聚焦。

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
forstring

分隔符 (Separator)

用于在组合框中视觉上分隔项目。

属性默认值类型描述
as'div'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南

箭头 (Arrow)

一个可选的箭头元素,与内容一起渲染。这有助于视觉上将触发器与 ComboboxContent 连接起来。必须在 ComboboxContent 内部渲染。仅当 position 设置为 popper 时可用。

属性默认值类型描述
as'svg'AsTag | Component此组件应渲染为的元素或组件。可通过 asChild 覆盖。
asChildboolean将默认渲染的元素替换为作为子元素传递的元素,合并它们的 props 和行为。更多详情请参阅我们的组合指南
height5number箭头的像素高度。
roundedboolean当为 true 时,渲染圆角版本的箭头。不适用于 as/asChild
width10number箭头的像素宽度。

虚拟化器 (Virtualizer)

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

组合框项目必须在传递给虚拟化器之前手动过滤。请参阅 下面的示例

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

属性(Prop)默认值类型描述
estimateSizenumber每个项目的估计大小(像素)。
optionsAcceptableValue[]项目列表。
overscannumber可见区域之外渲染的项目数量。
textContent((option: AcceptableValue) => string)每个项目的文本内容,用于实现预输入功能。

默认插槽

Payload描述
optionnull | string | number | bigint | Record<string, any>
virtualizerVirtualizer<HTMLElement, Element>
virtualItemVirtualItem

示例

将对象绑定为值

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

确保设置 displayValue 属性以在项目选择时设置输入值。

<script setup lang="ts">
import { ComboboxContent, ComboboxInput, ComboboxItem, ComboboxPortal, ComboboxRoot } 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>
  <ComboboxRoot v-model="selectedPeople">
    <ComboboxInput :display-value="(v) => v.name" />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem
          v-for="person in people"
          :key="person.id"
          :value="person"
          :disabled="person.unavailable"
        >
          {{ person.name }}
        </ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

选择多个值

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

<script setup lang="ts">
import { ComboboxRoot } 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>
  <ComboboxRoot
    v-model="selectedPeople"
    multiple
  >
  </ComboboxRoot>
</template>

自定义过滤

在内部,ComboboxRoot 将根据渲染的文本过滤项目。

但是,您也可以提供自己的自定义过滤逻辑,同时设置 ignoreFilter="true"

<script setup lang="ts">
import { ComboboxContent, ComboboxInput, ComboboxItem, ComboboxPortal, ComboboxRoot, useFilter } from 'reka-ui'
import { ref, computed } 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>
  <ComboboxRoot
    v-model="selectedPeople"
    :ignore-filter="true"
  >
    <ComboboxInput v-model="searchTerm" />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem
          v-for="person in filteredPeople"
          :key="person.id"
          :value="person"
        >
          {{ person.name }}
        </ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

自定义标签

默认情况下,Combobox 会使用输入内容作为屏幕阅读器的标签。如果您想更精细地控制辅助技术宣布的内容,请使用 Label 组件。

<script setup lang="ts">
import { ComboboxInput, ComboboxRoot, Label } from 'reka-ui'
import { ref } from 'vue'

const selectedPeople = ref(null) // Or your initial value
</script>

<template>
  <ComboboxRoot v-model="selectedPeople">
    <Label for="person">人物:</Label>
    <ComboboxInput
      id="person"
      placeholder="选择一个人"
    />
  </ComboboxRoot>
</template>

带有禁用项目的组合框

您可以通过 data-disabled 属性为禁用项添加特殊样式。

<script setup lang="ts">
import {
  ComboboxContent,
  ComboboxInput,
  ComboboxItem,
  ComboboxPortal,
  ComboboxRoot,
} from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <ComboboxRoot>
    <ComboboxInput />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem
          class="ComboboxItem"
          disabled
        >
        </ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

<style>
/* styles.css */
.ComboboxItem[data-disabled] {
  color: gainsboro;
}
</style>

带有分隔符的组合框

使用 Separator 部件在项目之间添加分隔符。

<script setup lang="ts">
import {
  ComboboxContent,
  ComboboxInput,
  ComboboxItem,
  ComboboxPortal,
  ComboboxRoot,
  ComboboxSeparator
} from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <ComboboxRoot>
    <ComboboxInput />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem></ComboboxItem>
        <ComboboxItem></ComboboxItem>
        <ComboboxItem></ComboboxItem>
        <ComboboxSeparator />
        <ComboboxItem></ComboboxItem>
        <ComboboxItem></ComboboxItem>
        <ComboboxItem></ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

带有分组项目的组合框

使用 GroupLabel 部件将项目分组到一个部分中。

<script setup lang="ts">
import {
  ComboboxContent,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxPortal,
  ComboboxRoot
} from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <ComboboxRoot>
    <ComboboxInput />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxGroup>
          <ComboboxLabel>标签</ComboboxLabel>
          <ComboboxItem></ComboboxItem>
          <ComboboxItem></ComboboxItem>
          <ComboboxItem></ComboboxItem>
        </ComboboxGroup>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

带有复杂项目的组合框

您可以在项目中使用自定义内容。

<script setup lang="ts">
import {
  ComboboxContent,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxItemIndicator,
  ComboboxLabel,
  ComboboxPortal,
  ComboboxRoot
} from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <ComboboxRoot>
    <ComboboxInput />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem>
          <img src="">
          Adolfo Hess
          <ComboboxItemIndicator />
        </ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

阻止选择行为

默认情况下,选择 ComboboxItem 将关闭内容,并使用提供的值更新 modelValue。您可以通过阻止默认的 @select.prevent 来阻止此行为。

<script setup lang="ts">
import { ComboboxContent, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxLabel, ComboboxPortal, ComboboxRoot } from 'reka-ui'
import { ref } from 'vue'
</script>

<template>
  <ComboboxRoot>
    <ComboboxInput />
    <ComboboxPortal>
      <ComboboxContent>
        <ComboboxItem @select.prevent>
          项目 A
        </ComboboxItem>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

带有工作过滤的虚拟化组合框

组合框项目 必须 在传递给虚拟化器之前手动过滤。

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

<script setup lang="ts">
import { ComboboxContent, ComboboxInput, ComboboxItem, ComboboxPortal, ComboboxRoot, ComboboxViewport, ComboboxVirtualizer, useFilter } from 'reka-ui'
import { computed, ref } from 'vue'

const people = Array.from({ length: 100000 }).map((_, id) => ({ id, name: `Person #${id}` }))

const selectedPeople = ref(people[0])
const searchTerm = ref('')
const { contains } = useFilter({ sensitivity: 'base' })

const filteredPeople = computed(() => people.filter(p => contains(p.name, searchTerm.value)))
</script>

<template>
  <ComboboxRoot v-model="selectedPeople">
    <ComboboxInput v-model="searchTerm" />
    <ComboboxPortal>
      <ComboboxContent class="max-h-[40vh] overflow-hidden">
        <ComboboxViewport>
          <ComboboxVirtualizer
            v-slot="{ option }"
            :options="filteredPeople"
            :text-content="(x) => x.name"
            :estimate-size="24"
          >
            <ComboboxItem :value="option">
              {{ option.name }}
            </ComboboxItem>
          </ComboboxVirtualizer>
        </ComboboxViewport>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>

可访问性

遵循 组合框 WAI-ARIA 设计模式

有关更多信息,请参阅 W3C 组合框自动完成列表 示例。

键盘交互

按键描述
Enter当焦点在 ComboboxItem 上时,选择焦点项目。
ArrowDown当焦点在 ComboboxInput 上时,打开组合框内容。当焦点在项目上时,将焦点移到下一个项目。
ArrowUp当焦点在 ComboboxInput 上时,打开组合框内容。当焦点在项目上时,将焦点移到上一个项目。
Esc关闭组合框并恢复 ComboboxInput 字段中选定的项目。

自定义 API

通过将原始部件抽象为自己的组件来创建您自己的 API。

命令菜单

组合框可以用于构建您自己的命令菜单。

用法

<script setup lang="ts">
import { Command, CommandItem } from './your-command'
</script>

<template>
  <Command>
    <CommandItem value="1">
      项目 1
    </CommandItem>
    <CommandItem value="2">
      项目 2
    </CommandItem>
    <CommandItem value="3">
      项目 3
    </CommandItem>
  </Command>
</template>

实现

your-command.ts
export { default as Command } from 'Command.vue'
export { default as CommandItem } from 'CommandItem.vue'
Command.vue
<script setup lang="ts">
import type { ComboboxRootEmits, ComboboxRootProps } from 'reka-ui'
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from '@radix-icons/vue'
import { ComboboxContent, ComboboxInput, ComboboxPortal, ComboboxRoot, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<ComboboxRootProps>()
const emits = defineEmits<ComboboxRootEmits>()

const forward = useForwardPropsEmits(props, emits)
</script>

<template>
  <ComboboxRoot
    v-bind="forward"
    :open="true"
    model-value=""
  >
    <ComboboxInput placeholder="输入命令或搜索…" />
    
    <ComboboxPortal>
      <ComboboxContent
        @escape-key-down.prevent
        @focus-outside.prevent
        @interact-outside.prevent
        @pointer-down-outside.prevent
      >
        <ComboboxViewport>
          <slot />
        </ComboboxViewport>
      </ComboboxContent>
    </ComboboxPortal>
  </ComboboxRoot>
</template>
ComboboxItem.vue
<script setup lang="ts">
import type { ComboboxItemProps } from 'reka-ui'
import { CheckIcon } from '@radix-icons/vue'
import { ComboboxItem } from 'reka-ui'

const props = defineProps<ComboboxItemProps>()
</script>

<template>
  <ComboboxItem
    v-bind="props"
    @select.prevent
  >
    <slot />
  </ComboboxItem>
</template>