Kaneo 项目 coss Autocomplete 组件实战指南:自由输入与建议选择的完整实现方案
【免费下载链接】app🎯 All you need. Nothing you don't. Open source project management that works for you, not against you.项目地址: https://gitcode.com/GitHub_Trending/app116/app
导读
coss是基于 Base UI(@base-ui/react)构建、采用 shadcn 风格开发体验的组件库,本仓库(Kaneo 开源项目管理应用)在apps/web/src/components/ui/autocomplete.tsx中完整落地了 coss Autocomplete 原语。本篇指南以仓库内.agents/skills/coss/references/primitives/autocomplete.md参考文档为主线,系统讲解 Autocomplete 的适用场景、安装方式、组合式 API、过滤/分组/异步搜索等实战模式,并结合仓库源码剖析其底层实现与常见坑点。读完本文,你将能够在项目里独立实现"输入即搜索、键盘可导航"的高质量建议选择器,并理解其与 Select、Combobox、Command 等相邻原语的本质区别。
Autocomplete 是什么:自由输入与建议选择的结合体
Autocomplete(自动补全)是"搜索驱动的建议选择器",核心特征是允许用户自由输入文本,同时基于已知选项空间(option space)提供即时匹配建议,并支持完整的键盘导航。在 coss 组件注册表中(见 component-registry.md),它被归类于"Selection & Input"(选择与输入)一组,与 Select、Combobox 并列:
| 原语 | 定位 | 输入自由度 |
|---|---|---|
| Select | 从预定义列表中单选,无搜索 | 只能选,不能输 |
| Combobox | 可搜索、带过滤的选择 | 受限于严格选项集 |
| Autocomplete | 自由文本 + 建议 | 可以输入任意内容,建议仅供参考 |
| Command | 可搜索的命令面板(操作而非数据选择) | 触发命令 |
什么时候用 Autocomplete(来自参考文档 "When to use"):
- 需要"搜索驱动的建议选择器 + 自由输入"的场景;
- 需要基于已知选项空间做辅助输入、且依赖键盘导航完成的场景。
什么时候不该用 Autocomplete("When NOT to use"),这一点决定了选型正确性:
- 选项完全预定义、不需要搜索 → 用Select;
- 用户必须从严格集合中选取、不允许自由文本 → 用Combobox;
- 需要的是一组操作命令而非数据选择 → 用Command。
从仓库源码看,这种"命令语义"在apps/web/src/components/ui/command.tsx中体现得很明显:它内部直接复用了@/components/ui/autocomplete导出的Autocomplete、AutocompleteInput、AutocompleteList、AutocompleteItem等一批组件来搭建命令面板骨架,说明 Autocomplete 是比 Command 更底层的通用"搜索式列表"构件,Command 是建立在它之上的命令化封装。
安装与依赖
CLI 一键安装
参考文档推荐使用 shadcn CLI 直接添加 coss 组件:
npx shadcn@latest add @coss/autocomplete该命令会从 coss 组件注册表拉取组件文件,并自动完成依赖解析。coss 技能的完整安装/发现工作流见 cli.md。
手动安装(手动 deps)
若需要手动接入,参考文档明确要求的核心运行时依赖只有一个:
npm install @base-ui/react在仓库实现中可以看到该依赖的实际使用:autocomplete.tsx 第一行即是
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete";同时,仓库内的组件实现还依赖以下同级文件与图标库(手动接入时需一并就位):
@/components/ui/input(Input nativeInput,承载输入框外观);@/components/ui/scroll-area(AutocompleteList的滚动容器);@/lib/cn(类名合并工具);lucide-react(ChevronsUpDownIcon触发器图标、XIcon清除图标)。
规范导入:Canonical imports
参考文档给出的规范导入(与本仓库 autocomplete.tsx 的export列表完全一致):
import { Autocomplete, AutocompleteCollection, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompletePopup, AutocompleteSeparator, AutocompleteStatus, useAutocompleteFilter, } from "@/components/ui/autocomplete"除上述之外,仓库实现还额外导出了AutocompleteClear、AutocompleteRow、AutocompleteTrigger、AutocompleteValue,在自定义清除按钮、行渲染、值展示等高级场景中可用。
最小可用模式:Minimal pattern
参考文档给出的最小完整示例:
const items = [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, ] <Autocomplete items={items}> <AutocompleteInput aria-label="Search items" placeholder="Search items…" /> <AutocompletePopup> <AutocompleteEmpty>No items found.</AutocompleteEmpty> <AutocompleteList> {(item) => ( <AutocompleteItem key={item.value} value={item}> {item.label} </AutocompleteItem> )} </AutocompleteList> </AutocompletePopup> </Autocomplete>结构拆解:Autocomplete(Root)持有items数据源;AutocompleteInput是自由输入框;AutocompletePopup承载弹出层(仓库实现中它由Portal + Positioner + Popup三段构成,见 autocomplete.tsx);AutocompleteList以 render-prop 形式接收(item) => ...渲染列表;AutocompleteEmpty提供空结果反馈。
表单绑定建议:参考文档明确提示——对于表单绑定的自动补全控件,优先使用Field包裹,使 label、必填状态(required)与错误输出(error)始终与同一控件保持关联,而不是散落在表单各处。
数据与渲染模型:items与value的映射
理解 Autocomplete 的渲染模型是正确使用的前提。从参考文档与仓库代码交叉印证可以得出:
Autocomplete接收items数组,元素可以是字符串或对象;AutocompleteItem value={item}把整条 item 作为选中值传给 Base UI 内部状态;- 过滤匹配、键盘高亮(
data-highlighted)与选中提交都由AutocompletePrimitive(@base-ui/react/autocomplete)统一管理。
仓库对 Item 的样式实现(autocomplete.tsx)非常典型,可作为视觉基准:
"flex min-h-8 cursor-default select-none items-center rounded-sm px-2 py-1 text-base outline-none ><Autocomplete items={items}> <AutocompleteInput aria-label="Search frameworks" placeholder="Search..." showClear showTrigger startAddon={<SearchIcon aria-hidden="true" />} /> <AutocompletePopup> <AutocompleteEmpty>No results found.</AutocompleteEmpty> <AutocompleteList> {(item) => <AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>} </AutocompleteList> </AutocompletePopup> </Autocomplete>仓库源码完整支持这三种能力(autocomplete.tsx):
startAddon渲染在输入框左侧绝对定位区域,带aria-hidden="true"并自动为输入框追加ps-*内边距,避免文字压到图标;showTrigger渲染AutocompleteTrigger,内部是ChevronsUpDownIcon下拉箭头,点击可展开/收起建议列表;showClear渲染AutocompleteClear,内部是XIcon,点击一键清空输入。
仓库还支持size属性("sm" | "default" | "lg" | number),sm尺寸会同步收缩触发器/清除按钮的定位与内边距,这对应粒子p-autocomplete-1~p-autocomplete-4中的尺寸与禁用态变体。
分组列表(Grouped lists)
当选项需要按类别组织时,用AutocompleteGroup+AutocompleteGroupLabel+AutocompleteCollection三层结构:
<AutocompleteList> <AutocompleteGroup> <AutocompleteGroupLabel>Fruits</AutocompleteGroupLabel> <AutocompleteCollection> {(item) => <AutocompleteItem key={item.value} value={item}>{item.label}</AutocompleteItem>} </AutocompleteCollection> </AutocompleteGroup> </AutocompleteList>仓库对这三者的实现要点(autocomplete.tsx):
AutocompleteGroup:[[role=group]+&]:mt-1.5,连续分组之间自动拉开间距;AutocompleteGroupLabel:text-xs小号字 +text-muted-foreground弱化色,明确表达"分组标题"语义;AutocompleteCollection:Base UI 的 Collection 节点,负责注册组内 item,保证键盘导航(方向键遍历)在分组间正确流转。
异步搜索(Async search)
参考文档给出异步模式的三条关键约定:
filter={null}:关闭内置客户端过滤,完全交由服务端/自定义过滤逻辑;- 受控绑定:自行控制
value/onValueChange,把用户输入发送到异步查询; - 提供
itemToStringValue:当items为对象时,必须指定"对象 → 稳定字符串"的映射,否则内部稳定的字符串映射会被破坏,导致高亮、匹配、值比较全部失真。
典型骨架如下:
<Autocomplete items={remoteItems} filter={null} value={query} onValueChange={setQuery} itemToStringValue={(item) => item.id} > <AutocompleteInput aria-label="Async search" placeholder="Type to search…" /> <AutocompletePopup> <AutocompleteStatus>{statusText}</AutocompleteStatus> <AutocompleteEmpty>No results found.</AutocompleteEmpty> <AutocompleteList> {(item) => <AutocompleteItem key={item.id} value={item}>{item.name}</AutocompleteItem>} </AutocompleteList> </AutocompletePopup> </Autocomplete>表单集成(Form integration)
把Autocomplete放进Field name="..."中,配合FieldLabel/FieldError即可获得与表单状态绑定的校验输出:
<Field name="owner" ...> <FieldLabel>负责人</FieldLabel> <Autocomplete items={members}> ... </Autocomplete> <FieldError /> </Field>参考文档强调:这样 label、必填状态与错误提示始终和同一个控件绑定,避免"视觉上在一个表单、语义上却脱离控件"的 a11y 隐患。表单相关规则的完整说明见 rules/forms.md。
更多示例索引
参考文档将 15 个粒子示例按能力线做了分组,可直接对照查阅(粒子文件位于 coss 仓库apps/ui/registry/default/particles/p-*.tsx):
| 能力线 | 粒子编号 |
|---|---|
| 基线 + 尺寸 + 禁用态 | p-autocomplete-1~p-autocomplete-4 |
标签 + 输入增强(showClear/showTrigger/startAddon) | p-autocomplete-5、p-autocomplete-8、p-autocomplete-9、p-autocomplete-14 |
匹配行为(mode="both"、autoHighlight) | p-autocomplete-6、p-autocomplete-7 |
| 分组选项 | p-autocomplete-10 |
| 限量结果 + 状态提示 | p-autocomplete-11 |
| 异步搜索(loading/error 状态) | p-autocomplete-12 |
| 表单集成 | p-autocomplete-13 |
| 风格变体(pill 输入) | p-autocomplete-15 |
过滤与状态机制:useAutocompleteFilter、AutocompleteStatus的底层作用
useAutocompleteFilter
仓库实现将其直接映射为 Base UI 的过滤钩子(autocomplete.tsx):
const useAutocompleteFilter = AutocompletePrimitive.useFilter;它内置了大小写不敏感匹配、mode(匹配模式)与autoHighlight(自动高亮首个匹配项)等参数,对应粒子p-autocomplete-6/p-autocomplete-7的匹配行为演示。默认情况下 Autocomplete 使用该过滤钩子对items做客户端过滤;异步场景中通过filter={null}关闭它,把过滤职责交给服务端。
AutocompleteStatus
AutocompleteStatus是列表尾部/头部的小字号状态区(仓库样式为px-3 py-2 font-medium text-muted-foreground text-xs,见 autocomplete.tsx)。在受限结果(如 "Showing 5 of 120")和异步 loading/error 场景中,它负责把非选项类的状态信息以视觉弱化、语义独立的方式呈现,不会打断键盘导航流。
常见坑点:Common pitfalls 逐一破解
参考文档列出的五个坑点,结合仓库源码逐一说明规避方法:
- 漏掉
AutocompleteEmpty:空结果时弹出空白面板,用户得不到任何反馈。务必始终保留AutocompleteEmpty节点(仓库实现中它有居中对齐的 muted 样式,见 autocomplete.tsx)。 - 异步/自定义流程中使用对象 item 却不提供
itemToStringValue:会破坏稳定的字符串映射,导致匹配与值比较错乱。对象 item 场景必须显式提供该映射。 - 把 Combobox/Select 的假设混入 Autocomplete API:三者交互语义不同(详见本文选型表),使用前务必核对各自文档,不要想当然地复用 props。
- 输入框缺少显式标签:必须通过
FieldLabel或aria-label提供可访问名称。参考文档把这一点列为强制项,仓库的 Command 面板内部也通过透传aria-label保证可访问性。 - 不处理异步的竞态/错误状态:
loading、error以及过期响应取消(stale response cancellation)必须显式管理,否则会出现"旧请求覆盖新结果"的竞态。参考文档要求结合AutocompleteStatus展示 loading/error 状态。
仓库落地验证:从参考文档到真实实现
本文档对应的组件在仓库中的实现位于 apps/web/src/components/ui/autocomplete.tsx,可以从源码直接验证上述全部行为:
- Root 组合:
Autocomplete = AutocompletePrimitive.Root(第 9 行),完整继承 Base UI 的 items/过滤/导航状态机; - 弹出层三段式:
AutocompletePopup由Portal → Positioner → Popup组合,Positioner支持side(默认bottom)、sideOffset(默认 4)、align(默认start)、anchor等定位参数,且通过min-w-(--anchor-width)、max-w-(--available-width)、max-h-[min(var(--available-height),23rem)]实现跟随锚点宽度、防溢出视口的自适应约束(第 77-121 行); - 列表滚动:
AutocompleteList用ScrollArea包裹AutocompletePrimitive.List,超长列表获得可滚动容器,not-empty:scroll-py-1 not-empty:p-1保证滚动时列表项不被裁剪(第 219-235 行); - 分隔线:
AutocompleteSeparator提供h-px bg-border细线,且last:hidden自动隐藏末尾冗余分隔线(第 142-153 行)。
此外,同目录下的 command.tsx 是 Autocomplete 原语被二次组合的实证:它以@base-ui/react/dialog作为命令面板外壳,内部复用了Autocomplete、AutocompleteInput、AutocompleteList、AutocompleteItem、AutocompleteGroup、AutocompleteGroupLabel等组件构成可搜索命令列表。这印证了参考文档的定位——Autocomplete 是通用"搜索式列表"基座,Command 是在其之上的命令语义封装。
总结
coss Autocomplete 是"自由输入 + 建议选择"场景的首选原语,选型上要严格与 Select(纯选择)、Combobox(严格集)、Command(命令语义)区分。使用时牢记三条主线:组合式 API(Input/Popup/List/Item 按文档层级组合)、数据映射(对象 item 必须提供itemToStringValue,异步场景关闭内置过滤)、状态完整性(AutocompleteEmpty与AutocompleteStatus不可省略)。仓库内 autocomplete.tsx 提供了完整可运行的参考实现,粒子示例p-autocomplete-1~p-autocomplete-15覆盖了从尺寸变体、分组、受限结果到异步与表单集成的全部实战形态,可作为后续开发的直接蓝本。
【免费下载链接】app🎯 All you need. Nothing you don't. Open source project management that works for you, not against you.项目地址: https://gitcode.com/GitHub_Trending/app116/app
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考