iter(g44): accordion 标题行方向键导航

This commit is contained in:
HIsheR
2026-10-26 12:00:19 +08:00
parent c42eb4b33c
commit b50315bb86
2 changed files with 23 additions and 1 deletions
+2
View File
@@ -703,5 +703,7 @@
}
,
{ "id": "g43", "title": "select/menu/popover/image 的 Esc 接入统一弹层栈", "motivation": "四个弹层组件各自监听 document 的 Esc:多层叠加(如 popover 内再开 image 预览)时一次 Esc 全部关闭,与 modal/drawer 的 pushOverlay 栈行为不一致", "acceptance": "四个组件展开期入 Esc 栈、收起即释放;多层叠加时 Esc 后进先出逐层关闭;焦点归还触发器;npm run check 全绿", "status": "done", "notes": "自补:一致性修复;复用 overlay.ts 的 pushOverlay 栈" }
,
{ "id": "g44", "title": "accordion 标题行方向键导航", "motivation": "标题是原生 button 只有 Enter/Space,行间移动要靠 Tab 逐个跳,与 tabs/tree 的 ↑↓/Home/End 键盘约定不一致", "acceptance": "↑↓ 在标题行间环形移动、Home/End 首尾直达,均跳过禁用行;焦点不在标题行不拦截;npm run check 全绿", "status": "done", "notes": "自补:可达性对齐;复用环形回绕取模模式" }
]
}
+21 -1
View File
@@ -51,5 +51,25 @@ export function accordion(props: AccordionProps): HTMLElement {
),
);
return h('div', { class: 'ua-accordion' }, ...rows);
const root = h('div', { class: 'ua-accordion' }, ...rows);
// —— 键盘导航:↑↓ 行间环形移动、Home/End 首尾直达,均跳过禁用行(对齐 tabs/tree 约定) ——
const heads = (): HTMLButtonElement[] => rows.map((r) => r.firstElementChild as HTMLButtonElement); // 各行标题按钮
const focusFrom = (start: number, dir: 1 | -1): void => {
const len = heads().length;
for (let n = 1; n <= len; n++) {
const cand = heads()[(((start + dir * n) % len) + len) % len]; // 环形取候选:越界回绕保证落在 [0, len)
if (!cand.disabled) { cand.focus(); return; } // 跳过禁用行,绕满一圈即止
}
};
root.addEventListener('keydown', (e: KeyboardEvent) => {
const list = heads();
const idx = list.indexOf(document.activeElement as HTMLButtonElement);
if (idx < 0) return; // 卫语句:焦点不在标题行
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); focusFrom(idx, e.key === 'ArrowDown' ? 1 : -1); }
else if (e.key === 'Home') { e.preventDefault(); focusFrom(-1, 1); } // 从头正向找第一个可用
else if (e.key === 'End') { e.preventDefault(); focusFrom(0, -1); } // 从尾反向找最后一个可用
});
return root;
}