iter: d25 menu 条目支持快捷键提示

This commit is contained in:
ui-arch-bot
2026-10-23 17:06:17 +08:00
parent 6efe3ab57b
commit 6a5e6623fe
9 changed files with 42 additions and 11 deletions
+8
View File
@@ -248,6 +248,14 @@
"acceptance": "README 覆盖定位 / 快速开始 / 28 组件清单 / 目录表 / 三道门禁 / Agent 接入 / 分支流水线;新增 <!-- ua:components --> 标记区块,ua verify 校验区块 id 集合与全部清单 docs.components 完全一致(缺/多都报);负向测试(删 avatar)能拦下;AGENTS.md 同步;npm run check 全绿", "acceptance": "README 覆盖定位 / 快速开始 / 28 组件清单 / 目录表 / 三道门禁 / Agent 接入 / 分支流水线;新增 <!-- ua:components --> 标记区块,ua verify 校验区块 id 集合与全部清单 docs.components 完全一致(缺/多都报);负向测试(删 avatar)能拦下;AGENTS.md 同步;npm run check 全绿",
"status": "done", "status": "done",
"notes": "不做自动写入(避免工具偷偷改文档),只做一致性校验:新增组件时 verify 报「README 组件区块缺少 xxx」,由人补一行;标记区块用 · 分隔便于人读与机器切分" "notes": "不做自动写入(避免工具偷偷改文档),只做一致性校验:新增组件时 verify 报「README 组件区块缺少 xxx」,由人补一行;标记区块用 · 分隔便于人读与机器切分"
},
{
"id": "d25",
"title": "menu 条目支持快捷键提示",
"motivation": "菜单里展示快捷键是桌面级体验的标配;缺字段时调用方只能把快捷键拼进 label,文案与对齐都失控",
"acceptance": "MenuItem 新增 shortcut?: string;条目改为 flex 两端对齐,label 单行省略,右侧 kbd 灰字提示(不绑定按键);demo 文件操作菜单加 F2 / ⌘D;manifest/文档/llms-full.txt 同步;npm run check 全绿",
"status": "done",
"notes": "明确边界:快捷键只做展示,实际按键仍需调用方全局监听(组件不做全局键盘陷阱);两端对齐让长文案截断而快捷键始终可见;kbd 用 inherit 字体避免等宽字跳脱整体排版"
} }
] ]
} }
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"$schema": "ui-arch-registry-index/0.1", "$schema": "ui-arch-registry-index/0.1",
"generatedAt": "2026-10-23T09:05:40.038Z", "generatedAt": "2026-10-23T09:06:10.925Z",
"repoRoot": ".", "repoRoot": ".",
"packages": [ "packages": [
{ {
@@ -517,7 +517,7 @@
"n": "items", "n": "items",
"t": "MenuItem[]", "t": "MenuItem[]",
"r": 1, "r": 1,
"d": "菜单条目:{ value, label, disabled?, danger?, divider? }" "d": "菜单条目:{ value, label, disabled?, danger?, divider?, shortcut? }"
}, },
{ {
"n": "variant", "n": "variant",
@@ -543,6 +543,7 @@
"准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组", "准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组",
"传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'", "传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'",
"点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)", "点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)",
"条目要给快捷键提示时传 shortcut(如 '⌘K'):右侧灰字展示,组件不绑定按键,实际快捷键由调用方在全局监听",
"键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过", "键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过",
"靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true" "靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true"
] ]
+1
View File
@@ -29,3 +29,4 @@
{"time": "2026-10-23T17:03:49+08:00", "id": "d22", "result": "done", "summary": "新增头像 avatar(src 图片 / 首字占位 + size + 四档语义底色),demo 独立 sec-avatar;中途被 verify 的 demo 锚点检查拦下已补;check 全绿"} {"time": "2026-10-23T17:03:49+08:00", "id": "d22", "result": "done", "summary": "新增头像 avatar(src 图片 / 首字占位 + size + 四档语义底色),demo 独立 sec-avatar;中途被 verify 的 demo 锚点检查拦下已补;check 全绿"}
{"time": "2026-10-23T17:04:27+08:00", "id": "d23", "result": "done", "summary": "alert 新增 title / closable / onClose 与 role=alert,关闭走 200ms 退场后摘除;demo 独立 sec-alert;manifest/llms 同步,check 全绿"} {"time": "2026-10-23T17:04:27+08:00", "id": "d23", "result": "done", "summary": "alert 新增 title / closable / onClose 与 role=alert,关闭走 200ms 退场后摘除;demo 独立 sec-alert;manifest/llms 同步,check 全绿"}
{"time": "2026-10-23T17:05:46+08:00", "id": "d24", "result": "done", "summary": "重写 README(定位/快速开始/28 组件/目录/门禁/Agent 接入/流水线),组件清单做成标记区块并由 ua verify 校验防漂移;负向测试通过,check 全绿"} {"time": "2026-10-23T17:05:46+08:00", "id": "d24", "result": "done", "summary": "重写 README(定位/快速开始/28 组件/目录/门禁/Agent 接入/流水线),组件清单做成标记区块并由 ua verify 校验防漂移;负向测试通过,check 全绿"}
{"time": "2026-10-23T17:06:17+08:00", "id": "d25", "result": "done", "summary": "menu 条目新增 shortcut 快捷键提示(右侧 kbd 灰字,两端对齐 + 单行省略,不绑按键);demo/manifest/llms 同步,check 全绿"}
+2 -2
View File
@@ -337,8 +337,8 @@ const content = h(
menu({ menu({
label: '文件操作', label: '文件操作',
items: [ items: [
{ value: 'rename', label: '重命名' }, { value: 'rename', label: '重命名', shortcut: 'F2' },
{ value: 'copy', label: '创建副本' }, { value: 'copy', label: '创建副本', shortcut: '⌘D' },
{ value: 'archive', label: '归档', disabled: true }, { value: 'archive', label: '归档', disabled: true },
{ divider: true, value: '', label: '' }, { divider: true, value: '', label: '' },
{ value: 'delete', label: '删除', danger: true }, { value: 'delete', label: '删除', danger: true },
+5 -4
View File
@@ -1,6 +1,6 @@
# @ui-arch 组件库 Agent 文档(无图形版) # @ui-arch 组件库 Agent 文档(无图形版)
> 自动生成于 2026-10-23T09:05:40.038Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。 > 自动生成于 2026-10-23T09:06:10.925Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。 > 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
## 快速上手 ## 快速上手
@@ -224,7 +224,7 @@ const city = signal(''); select({ options: [{ value: 'bj', label: '北京' }], v
参数: 参数:
- label(必填):string —— 触发器文案 - label(必填):string —— 触发器文案
- items(必填):MenuItem[] —— 菜单条目:{ value, label, disabled?, danger?, divider? } - items(必填):MenuItem[] —— 菜单条目:{ value, label, disabled?, danger?, divider?, shortcut? }
- variant(默认 ghost):'primary' | 'ghost' —— 触发器视觉强度;缺省 ghost - variant(默认 ghost):'primary' | 'ghost' —— 触发器视觉强度;缺省 ghost
- align(默认 start):'start' | 'end' —— 弹层对齐方向;缺省 start(与触发器左对齐) - align(默认 start):'start' | 'end' —— 弹层对齐方向;缺省 start(与触发器左对齐)
- onSelect(回调):(value: string) => void —— 选中回调,禁用项不触发 - onSelect(回调):(value: string) => void —— 选中回调,禁用项不触发
@@ -240,8 +240,9 @@ menu({ label: '操作', items: [{ value: 'edit', label: '编辑' }, { value: 'de
1. 准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组 1. 准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组
2. 传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary' 2. 传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'
3. 点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器) 3. 点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)
4. 键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过 4. 条目要给快捷键提示时传 shortcut(如 '⌘K'):右侧灰字展示,组件不绑定按键,实际快捷键由调用方在全局监听
5. 靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true 5. 键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过
6. 靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true
现场演示:apps/docs/demo.html#sec-menu 现场演示:apps/docs/demo.html#sec-menu
+2 -1
View File
@@ -24,7 +24,8 @@ export function menu(props: MenuProps): HTMLElement {
disabled: item.disabled ? true : undefined, disabled: item.disabled ? true : undefined,
onClick: () => pick(item), onClick: () => pick(item),
}, },
item.label, h('span', { class: 'ua-menu-label' }, item.label),
item.shortcut ? h('kbd', { class: 'ua-menu-key' }, item.shortcut) : null, // 快捷键只做提示,不拦截按键
) as HTMLButtonElement; ) as HTMLButtonElement;
if (!item.disabled) focusables.push(btn); // 禁用项不参与导航 if (!item.disabled) focusables.push(btn); // 禁用项不参与导航
return btn; return btn;
+1
View File
@@ -103,6 +103,7 @@ export interface MenuItem {
label: string; // 条目文案 label: string; // 条目文案
disabled?: boolean; // 禁用该条目:置灰且不参与键盘导航 disabled?: boolean; // 禁用该条目:置灰且不参与键盘导航
danger?: boolean; // 危险操作条目:文案取 danger 语义色 danger?: boolean; // 危险操作条目:文案取 danger 语义色
shortcut?: string; // 右侧快捷键提示(仅展示,不绑定按键),如 '⌘K'
divider?: boolean; // 该条目渲染为分隔线(忽略 value / label) divider?: boolean; // 该条目渲染为分隔线(忽略 value / label)
} }
+2 -1
View File
@@ -388,7 +388,7 @@
"n": "items", "n": "items",
"t": "MenuItem[]", "t": "MenuItem[]",
"r": 1, "r": 1,
"d": "菜单条目:{ value, label, disabled?, danger?, divider? }" "d": "菜单条目:{ value, label, disabled?, danger?, divider?, shortcut? }"
}, },
{ {
"n": "variant", "n": "variant",
@@ -414,6 +414,7 @@
"准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组", "准备 items 数组:value 是回调标识、label 是展示文案,插一条 { divider: true } 即可分出条目组",
"传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'", "传 label 决定触发器文案;默认 ghost 轻量样式,主操作用 variant: 'primary'",
"点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)", "点触发器展开弹层,点条目收起并触发 onSelect;点外部或按 Esc 也会收起(Esc 后焦点回到触发器)",
"条目要给快捷键提示时传 shortcut(如 '⌘K'):右侧灰字展示,组件不绑定按键,实际快捷键由调用方在全局监听",
"键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过", "键盘:Tab 聚焦触发器,↑↓ 展开并在可用条目间环形移动,Enter / Space 选中;disabled 项自动跳过",
"靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true" "靠右对齐避免溢出时传 align: 'end';危险操作给条目加 danger: true"
] ]
+18 -1
View File
@@ -360,8 +360,11 @@
visibility: visible; visibility: visible;
} }
.ua-menu-item { .ua-menu-item {
display: block; display: flex;
width: 100%; width: 100%;
align-items: center;
justify-content: space-between;
gap: var(--ua-space-4);
text-align: left; text-align: left;
padding: 6px 10px; padding: 6px 10px;
border: none; border: none;
@@ -381,6 +384,20 @@
.ua-menu-item.is-danger:hover:not(:disabled) { .ua-menu-item.is-danger:hover:not(:disabled) {
background: color-mix(in srgb, var(--ua-color-danger) 10%, transparent); background: color-mix(in srgb, var(--ua-color-danger) 10%, transparent);
} }
.ua-menu-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ua-menu-key {
flex: none;
color: var(--ua-color-text-muted);
font-family: inherit;
font-size: var(--ua-font-size-sm);
opacity: 0.8;
}
.ua-menu-item:disabled { .ua-menu-item:disabled {
opacity: 0.45; opacity: 0.45;
cursor: not-allowed; cursor: not-allowed;