iter(g44): accordion 标题行方向键导航
This commit is contained in:
@@ -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": "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": "自补:可达性对齐;复用环形回绕取模模式" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user