@basenative /组合框
可访问的预输入组合框原语-从现有列表中选择,或创建新条目。WAI-ARIA APG组合框+列表框模式,ssr优先,信号驱动,可选。
部分 BaseNative 生态系统。
为什么
大多数组合框库都是巨大的(40+ KB),特定于框架,并且带有您必须与之抗争的固执己见的风格。这个正好相反:
- 呈现语义HTML服务器端。没有JS,你会得到一个plain
<input>+<datalist>提前输入(对于表单足够好)。 - 使用JS,它会变成一个完整的apg模式组合框,具有虚拟焦点、键盘导航和内置的“+ Create”功能。
- 主题贯穿始终
--cb-*CSS自定义属性。零捆绑调色板假设。 - Layout-shift-free:打开列表框永远不会刷新页面。
- 信号互操作通道
runtime.effect-传递@basenative/runtime信号和输入镜像它。
安装
pnpm add @basenative/combobox @basenative/runtime
快速开始-选择从现有的选项
import { Combobox } from '@basenative/combobox';
import '@basenative/combobox/css';
const cb = Combobox({
id: 'fruit',
name: 'fruit',
label: 'Pick a fruit',
options: ['Apple', 'Banana', 'Cherry'],
onChange: (value) => console.log('selected', value),
});
document.querySelector('#mount').innerHTML = cb.html;
const handle = cb.hydrate(document.querySelector('[data-bn="combobox"]'));
允许创建新条目
const cb = Combobox({
id: 'tag',
label: 'Tag',
options: existingTags,
allowCreate: true,
createLabel: (input) => `+ New tag "${input}"`,
onChange: (value) => setTag(value),
onCreate: (label) => {
addTag(label); // persist somewhere
setTag(label);
},
});
如果你不及格 onCreate, BaseNative将把用户键入的文本作为值并调用 onChange(typed).
带有选项提示
用于内联显示键盘快捷键、排序元数据或上下文信息:
const cb = Combobox({
options: [
{ value: 'today', label: 'Today', hint: 'T' },
{ value: 'this-week', label: 'This week', hint: 'W' },
{ value: 'all', label: 'All time', hint: 'A' },
],
onChange: (v) => setRange(v),
});
用信号控制
import { signal, effect } from '@basenative/runtime';
import { Combobox } from '@basenative/combobox';
const category = signal('books');
const cb = Combobox({
options: ['books', 'movies', 'music'],
value: category, // function-shape signal accessor
onChange: (v) => category.set(v),
runtime: { effect }, // mirrors signal -> input on every change
});
咏叹调模式
这个包实现了 W3C APG组合框/列表框模式 准确地说应该是:
- 输入有
role="combobox",aria-autocomplete="list",aria-expanded,aria-controls=<listbox id>. - 弹出框中有
role="listbox". 每一行都有role="option"和aria-selected. - 焦点停留在输入。高亮显示的行通过
aria-activedescendant(虚拟聚焦)。- 这在手机平台上是很重要的:将真正的焦点移到弹出窗口将关闭软键盘。
- 有礼貌的居住地区(
aria-live="polite")在每次过滤器更改后宣布选项计数,并单独宣布创建选项提示,以便屏幕阅读器用户知道这是一个不同的操作。 - create-选项使用不同的颜色标记(
--cb-create-fg),data-bn-cb-create="true"用于SR /造型。
键盘
| 关键 | 行为 |
|---|---|
| ArrowDown | 打开弹出窗口(如果关闭)并向下移动活动选项 |
| ArrowUp | 打开弹出窗口(如果关闭)并向上移动活动选项 |
| 首页 | 主动移动到第一个选项 |
| 结束 | 移动到最后一个选项 |
| 输入 | 提交活动选项(或者,如果没有活动行,则提交类型化查询) |
| 逃避 | 关闭弹出窗口,保持输入值不变 |
| 选项卡 | 提交活动选项(如果有的话),然后按自然选项卡顺序移动焦点 |
选择“+ Create”永远不会清除用户键入的输入-键入的文本将成为新值。
主题
在任何祖先重写:
.dark [data-bn="combobox"] {
--cb-bg: #18181b;
--cb-fg: #f4f4f5;
--cb-border: #3f3f46;
--cb-border-focus: #f59e0b;
--cb-listbox-bg: #18181b;
--cb-listbox-border: #3f3f46;
--cb-option-bg-hover: #27272a;
--cb-option-bg-active: #3f3f46;
--cb-create-fg: #fbbf24;
--cb-create-bg: #422006;
}
| 令牌 | 目的 | 默认的 |
|---|---|---|
--cb-bg | 输入域背景 | #ffffff |
--cb-fg | 默认文本 | #111111 |
--cb-border | 场边界 | #c8c6bf |
--cb-border-focus | 现场边界聚焦 | #2E5BFF |
--cb-radius | 场角半径 | 6px |
--cb-input-h | 场的高度 | 44px |
--cb-listbox-bg | 弹出的背景 | #ffffff |
--cb-listbox-border | 弹出边境 | #c8c6bf |
--cb-option-bg-hover | 选项悬停背景 | #f1efe9 |
--cb-option-bg-active | 选项激活(键盘)背景 | #e6e4dc |
--cb-option-fg | 选择文本 | #111111 |
--cb-create-fg | 创建选项文本(故意区分) | #0F5132 |
--cb-create-bg | Create-option背景 | #ECFDF3 |
--cb-min-tap | 最小命中目标(WCAG 2.5.5 AA) | 44px |
过滤策略
import { Combobox, defaultFilter, prefixFilter, fuzzyFilter } from '@basenative/combobox';
Combobox({ options, filter: prefixFilter }); // type "ap" → "Apple"
Combobox({ options, filter: fuzzyFilter }); // type "apl" → "Apple"
Combobox({ options, filter: (o, q) => o.value.startsWith(q.toUpperCase()) });
移动测试笔记
- iOS 16/17 Safari:在iPhone 12 / 14 Pro上测试。点击选项不会关闭软键盘(我们取消了)
mousedown在列表框上)。 inputmode="text"保持系统键盘布局的文本形状 (不是数字键盘)。font-size: 16px最小阻止iOS变焦对焦。- 每个选项的44pt min命中目标。
API
import {
Combobox, // factory: { html, hydrate(rootEl) }
renderCombobox, // SSR: returns HTML string
hydrateCombobox, // wire reactivity to an existing root
normalizeOption, // (raw) => { value, label, hint? }
defaultFilter,
prefixFilter,
fuzzyFilter,
applyAriaAttributes,
escapeOnKeydown,
announce,
} from '@basenative/combobox';
看到 types/index.d.ts 完整签名。
许可证
apache - 2.0