Combobox
combobox自动补全 · 触发按钮 + 弹层内搜索(图4 范式),亦支持内联输入;浮层锚到字段等宽
用法
弹层内搜索(图4 范式)
触发按钮显示已选项,点击展开带搜索框的浮层。
<Combobox items={fruits}>
<ComboboxTrigger placeholder="选择水果" />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>内联自动补全
输入框本身即可见字段,直接打字过滤,clearable 显示清除钮。
<Combobox items={fruits}>
<ComboboxInput placeholder="搜索水果…" clearable />
<ComboboxContent>
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>默认已选值
defaultValue 传入选项对象作为非受控初值。
<Combobox items={fruits} defaultValue={fruits[2]}>
<ComboboxTrigger placeholder="选择水果" />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>禁用与无效态
disabled 整体置灰;invalid 触发器标红。
<>
<Combobox items={fruits} defaultValue={fruits[0]} disabled>
<ComboboxTrigger placeholder="选择水果" />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>
<Combobox items={fruits}>
<ComboboxTrigger placeholder="选择水果" invalid />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>
</>尺寸
size 控制触发器高度(sm / md / lg)。
<Combobox items={fruits}>
<ComboboxTrigger size="sm" placeholder="选择水果" />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxContent>
</Combobox>何时用
选项较多需边打字边过滤(自动补全/可搜索下拉)时用;支持单选、multiple 多选 chips、触发按钮式弹层内搜索,以及内联输入直接过滤三种范式。选项少且全可见用 Select;纯展示已有密钥用 SecretField;@提及场景用 Mentions。
导入
import { Combobox, ComboboxInput, ComboboxTrigger, ComboboxContent, ComboboxItem, ComboboxChips, ComboboxChip } from "@hulianui/ui"Props
Combobox(透传 Base UI Combobox.Root<ComboboxItemData, Multiple>,下表为瑚琏常用项)
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| items | ComboboxItemData[] | — | 选项数据 {value,label},自动以 label 显示、value 提交 |
| value | ComboboxItemData|ComboboxItemData[] | — | 受控选中(multiple 时为数组) |
| defaultValue | 同上 | — | 非受控初始选中 |
| multiple | boolean | false | true 时 value/onValueChange 自动变数组 |
| virtualized | boolean | items 长度 ≥ 100 时为 true | 列表虚拟化(只渲染视口内的项)。不传时按选项数自动决定,见「禁忌 / 坑」 |
| disabled | boolean | false | 禁用 |
ComboboxTrigger(图4 范式:显示已选 label / placeholder,点击展开弹层内搜索)
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| size | "sm"|"md"|"lg" | "md" | 尺寸 |
| placeholder | string | — | 未选中时占位文案 |
| invalid | boolean | false | 独立使用(非 Field 内)时手动置无效态皮肤 |
| className | string | — | — |
ComboboxInput(内联自动补全:输入框本身即可见字段)
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| size | "sm"|"md"|"lg" | "md" | 尺寸 |
| placeholder | string | — | 占位 |
| invalid | boolean | false | 手动置无效态皮肤 |
| clearable | boolean | false | 有值时渲染清除按钮 |
| className | string | — | — |
ComboboxContent
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| searchPlaceholder | string | — | 设置后在浮层顶部渲染搜索框(图4 范式,配合 Trigger);不设则为内联补全态 |
| side | "top"|"bottom" | — | 浮层方位 |
| align | "start"|"center"|"end" | — | 浮层对齐 |
| sideOffset | number | — | 偏移 |
| onListScroll | UIEventHandler<HTMLDivElement> | — | 列表滚动回调,e.currentTarget 即滚动容器(远程分页「滚到底加载更多」用,见 RemoteSelect) |
| footer | ReactNode | — | 列表下方常驻页脚(加载中 / 计数 / 到底提示),不随列表滚动 |
| className | string | — | — |
ComboboxItem
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| value * | ComboboxItemData | — | 选项 {value,label} 对象 |
| disabled | boolean | false | 禁用该项 |
| className | string | — | — |
ComboboxChips(多选 chips 外壳):size、invalid、placeholder、className(外加 children 插槽,见 Slots)。ComboboxChip(单个已选 chip):className(外加 children 插槽,见 Slots)。
Events
Combobox(透传 Base UI Combobox.Root)
| 事件 | 类型 | 说明 |
|---|---|---|
| onValueChange | (value) => void | 选中变化回调(multiple 时 value 为数组) |
Slots
Combobox
| 插槽 | 类型 | 说明 |
|---|---|---|
| children | ReactNode | 内放 Trigger/Input + Content |
ComboboxContent
| 插槽 | 类型 | 说明 |
|---|---|---|
| children * | (item, index) => ReactNode | 渲染函数,List 自动遍历已过滤项调用 |
| emptyMessage | ReactNode | 无匹配时文案 |
ComboboxItem
| 插槽 | 类型 | 说明 |
|---|---|---|
| children * | ReactNode | 渲染内容 |
ComboboxChips
| 插槽 | 类型 | 说明 |
|---|---|---|
| children | ReactNode | 内含 chip 列 + 输入框 |
ComboboxChip
| 插槽 | 类型 | 说明 |
|---|---|---|
| children * | ReactNode | chip 内容 |
禁忌 / 坑
ComboboxItem的value是整个{value,label}对象(非字符串)——render fn 里直接传value={item},Base UI 自动派生 label/value。- 浮层内搜索框由
ComboboxContent的searchPlaceholder触发:配ComboboxTrigger用就给它(图4 范式),配ComboboxInput内联补全则不设(输入框本身即搜索)。 multiple一开 value/onValueChange 即变数组,受控时 state 类型要跟着变。invalid仅用于「非 Field 内」独立使用时手动置无效皮肤;在 Field 内由 Field 接管,不用手传。- `items` 给到 100 项及以上时列表会自动虚拟化(无需传
virtualized):只有视口内的项在 DOM 里,行高按 32px 固定估算,不做逐项测量。默认ComboboxItem恰好是 32px,所以通常无感。如果你的 render fn 返回的项高度不是 32px(两行文案、带头像/副标题、自定义className改了 padding 或字号),那么在 ≥100 项时滚动条长度与项的落位会逐渐偏移——页面不会报任何错,短列表下也复现不出来,只有滚到列表中后段才看得出跳动。这种项请显式传virtualized={false}关掉,或把项高度对齐到 32px。 - 虚拟化同样影响依赖「选项全在 DOM 里」的测试与脚本:
getAllByRole("option")只会拿到视口内那几条,document.querySelector找不到未滚动到的项。断言总数请改用列表容器上的data-hulian-virtual-count,或对该用例传virtualized={false}。 - 走 Combobox 的上层组件同样吃这条:Select 的
searchable皮肤、RemoteSelect 的候选列表,选项攒到 100 条后都会自动虚拟化。
相关
SecretField · Listbox · Mentions · InputOTP · Rating · Upload
Playground
<Combobox items={items} defaultValue={items[0]}>
<ComboboxTrigger size="md" placeholder="选择水果" />
<ComboboxContent searchPlaceholder="搜索水果…">
{(item) => <ComboboxItem key={item.value} value={item}>{item.label}</ComboboxItem>}
</ComboboxContent>
</Combobox>