Combobox(组合框)让人通过键入找到选项并选一个或多个值。值是列表里的选项(multiple 时为多个),输入框里的文本只是查询:未选择就关闭会恢复已选标签,表单提交的是值而非文本。适合国家、负责人或一组标签。当自由文本才是主要路径时用自动完成,选项固定且要紧凑触发器时用选择器。
演示
键入以筛选框架,选中一项后下方显示你的选择:
已选:(无)
组合
Combobox
├── ComboboxLabel
├── ComboboxInput
└── ComboboxContent
└── ComboboxList
├── ComboboxItem
└── ComboboxItem给输入框命名,给每个选项一个唯一 value,把选项放进弹出层里的列表。选中一项会把它的标签放进输入框。
用法
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxItemIndicator,
ComboboxLabel,
ComboboxList,
} from "@ui-sdk/react/combobox";
function Example() {
return (
<Combobox>
<ComboboxLabel>框架</ComboboxLabel>
<ComboboxInput placeholder="搜索框架…" />
<ComboboxContent>
<ComboboxEmpty>没有匹配的框架。</ComboboxEmpty>
<ComboboxList>
<ComboboxItem value="next" label="Next.js">
Next.js
<ComboboxItemIndicator>✓</ComboboxItemIndicator>
</ComboboxItem>
<ComboboxItem value="astro" label="Astro">
Astro
<ComboboxItemIndicator>✓</ComboboxItemIndicator>
</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
);
}键入「as」找到 Astro,按 ArrowDown 高亮再按 Enter 选中。可加 ComboboxTrigger 作为开合按钮。
多选与芯片
加 multiple 让值变成数组:选中即切换、列表保持打开、每次选择后清空查询。把 ComboboxValue 与输入框放进 ComboboxChips,每个值渲染成带移除按钮的芯片。空输入框里按 Backspace 一次即移除最后一个芯片,方向键在芯片间移动。keepQuery 保留查询以便连续选多个匹配,maxSelected 限制数量(达到后其余项禁用),起始值的标签未知时提供 itemToLabel:
<Combobox multiple defaultValue={["next", "astro"]} itemToLabel={frameworkLabel}>
<ComboboxLabel>框架</ComboboxLabel>
<ComboboxChips>
<ComboboxValue>
{(state) =>
(state.value as string[]).map((value, index) => (
<ComboboxChip key={value} value={value}>
{state.labels[index]}
<ComboboxChipRemove aria-label={`移除 ${state.labels[index]}`}>×</ComboboxChipRemove>
</ComboboxChip>
))
}
</ComboboxValue>
<ComboboxInput placeholder="添加一个框架…" />
</ComboboxChips>
<ComboboxContent>
<ComboboxList>{/* 选项 */}</ComboboxList>
</ComboboxContent>
</Combobox>ComboboxChipsOverflow 可让放不下的芯片折叠并显示隐藏数量;ComboboxSelectAll 切换其所在组(或组外)全部可选中的匹配项。
创建新选项与自定义值
ComboboxCreate 在没有标签精确匹配查询、且达到 minLength 时给出「创建」项。onCreate(query) 负责保存,返回 Promise 时该项进入 pending(带 data-pending、aria-busy、aria-disabled),resolve 后列表关闭(multiple 时清空输入)。创建本身不改变选择,需在保存后自行加入并选中新项:
<ComboboxCreate
minLength={2}
onCreate={async (query) => {
await saveLabel(query);
setLabels((list) => [...list, query]);
setValue((list) => [...list, query]);
}}
>
{(state) => (state.pending ? `正在创建「${state.query}」…` : `创建「${state.query}」`)}
</ComboboxCreate>若建议只是可选(类似 datalist),用 allowCustomValue:无高亮项按 Enter 或列表关闭时,未匹配文本本身成为值,但不会在集合里新建项。
过滤、分组与异步
filter 控制键入如何收窄列表:"contains"、"startsWith"、自定义函数(数值结果还用于排序),或 false 保留全部(服务端已过滤)。把整套选项作为 items 传入即由组件渲染并过滤,emptyItems 在输入为空时改显示最近选择;分组用 { type: "group", label, items } 或 ComboboxGroup / ComboboxGroupLabel。autoHighlight 键入时高亮第一条匹配("always" 还在打开时高亮已选或首项)。异步搜索用 status 驱动 ComboboxStatus 与 ComboboxEmpty,onLoadMore 在到达最后一项时加载下一页,announcement 自定义屏幕阅读器播报。
受控与控制器
值用 value / onValueChange 控制,输入文本用 query / onQueryChange,开合用 open / onOpenChange,三者独立。回调先收到提议值再收到 details,details.cancel() 可拒绝。对象值用 itemToValue、itemToLabel、isItemEqualToValue 处理标识与比较。createComboboxController() 创建的控制器提供 open / close / toggle / getValue / setValue / clear / getQuery / setQuery / focus / isOpen。参见状态与事件与控制器。
样式与状态
弹出层就地渲染进浏览器顶层。用 data-highlighted 表示活动项、data-selected 表示已选值;键盘高亮通常不离开输入框,故用内容的 data-modality="keyboard" 来画焦点环。输入框与内容带 data-open / data-closed,内容带 data-starting-style / data-ending-style 供动画;ComboboxItemText 匹配时带 data-matched,芯片带 data-value,创建项带 data-pending。
ComboboxContent 暴露 --anchor-width(跟随输入框或芯片)、--available-height(到视口边缘的可用空间,不继承)、--transform-origin:
[data-slot="combobox-content"] {
width: var(--anchor-width);
max-height: var(--available-height);
transform-origin: var(--transform-origin);
}
[data-slot="combobox-item"][data-selected] {
color: var(--color-brand);
}
[data-slot="combobox-content"][data-modality="keyboard"]
[data-slot="combobox-item"][data-highlighted] {
background: var(--color-accent);
}API
根 Combobox 的常用属性:
value/defaultValue(T | T[] | null):受控值 / 初始值,multiple时为数组,null表示未选。onValueChange((value, details) => void):值想变更时调用,details.reason说明来源,details.cancel()拦截。multiple(boolean):允许多选,值为列表,选择后列表保持打开。query/defaultQuery/onQueryChange(string):输入框文本(查询)。open/defaultOpen/onOpenChange:列表开合。items/emptyItems(ListData<T>)、filter(ListFilter<T>):选项数据与过滤。maxSelected(number)、keepQuery(boolean)、autoHighlight("always" | boolean)、allowCustomValue(boolean)。itemToValue/itemToLabel/isItemEqualToValue:对象值的标识、标签与相等比较。status、onLoadMore、announcement、onHighlight:异步与播报。name/required/disabled/readOnly:表单提交与可用性。controller(object):用createComboboxController()创建。
ComboboxItem 接受 value、label、keywords、disabled、index;ComboboxValue 接受 maxLabels、getMoreLabel;ComboboxCreate 接受 onCreate、minLength。
键盘与无障碍
遵循 WAI-ARIA 带列表自动完成的 combobox 模式。焦点通常留在输入框,由 aria-activedescendant 指明高亮项;触摸屏幕阅读器或 sheet 可把真实焦点移入列表。
| 按键 | 行为 |
|---|---|
| 键入 | 过滤选项并打开列表 |
ArrowDown / ArrowUp |
打开列表并移动高亮,默认首尾循环(loop={false} 关闭) |
Enter |
选中高亮项 |
Escape |
关闭列表,随后恢复已选标签 |
Backspace |
空输入框且有芯片时移除最后一个芯片 |
ArrowLeft / ArrowRight |
在芯片间移动(RTL 文本反向) |