Files
workspace/code/g3soft-libs/.changeset/table-tab-navigation.md
T
2026-10-13 22:32:50 +08:00

15 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
'@g3soft/ui': minor
---
G3Table:`Tab` / `Shift+Tab` 在可编辑格之间跳格(修复「select 编辑器里按 Tab 没选中下一个」)。
- **新增键盘跳格**:编辑态下 `Tab` / `Shift+Tab` 提交当前格并落到**下一个 / 上一个可编辑格**——没有 `editor` 的列、以及被 `disabled` 按行禁用的格会被跳过;行尾自动接到下一行的第一个可编辑格,到表格两头则**循环**(Excel / AG Grid 的手感)。全表没有任何可编辑格时不接管,Tab 保持浏览器默认行为。规则落在 `utils/editable.ts` 的 `findAdjacentEditableCell()`,与编辑器的「能不能改」判据同源(`editor` 是否存在 + `disabled` 是否对该行为真),单测覆盖跳过只读列 / 跳过按行禁用 / 跨行回绕 / 两端循环 / 无可编辑格。
- **修复:`select` 编辑器里按 Tab,焦点跑到页面其它元素上**(表现为「Tab 没有选中下一个」)。根因是 `G3Select` 展开时会把 DOM 焦点移进**下拉浮层**,而浮层 `Teleport` 到 `body` —— 浮层里的 keydown 不经过 `.stk-table`,底座挂在表格容器上的 Tab 导航永远收不到;浏览器只能按文档顺序找下一个可聚焦元素,于是焦点离开表格(实测落到文档开头)。现在表格在 `document` **捕获阶段**接管 Tab,用 ARIA 关联(浮层 id ↔ 触发器 `aria-controls`)把浮层追回它所属的单元格,再经底座的 `setAreaSelection` 移动选区 —— 激活编辑、聚焦编辑器、滚动进视口全部复用既有链路(鼠标点击与键盘跳格走同一条路)。
- 顺带修掉同一处暴露的另一个问题:原先 `Tab` 走到行尾会落到**只读格**上(`focusCol` 为 null,编辑器不存在),表现为「按 Tab 后焦点凭空消失」,再按一次就掉出表格。新规则只落在能接收焦点的格上。
- **连按 / 长按 Tab 也不会掉队**:跳格是「改选区 → 激活编辑 → 重挂编辑器 → 聚焦」的异步链路,中间焦点会短暂落在 `body`(旧的编辑器已卸载、新的还没聚焦),且编辑态的 `focusCol` 被防抖推迟。接管里因此做了几件事:起点优先取**选区终点**(每次跳格同步更新,连按时才能一格一格往前走,而不是算回同一格)、换格空档期(200ms)内即使焦点在 `body` 也仍认这记 Tab。
- **修复:长按 Tab(或从只读列起步连续 Tab)走到第 3 格就掉出表格。** 这一条是用户实测 100% 复现的:点「订舱号」(只读、没有编辑器)那一格,然后一直按着 Tab 往右走,到「起运港」时焦点就跑到页面其它元素上了。成因是底座的导航优化在长按期间调用 `clearActiveEditing()` —— 它**把 `activeEditingRow` 和 `state.editing` 一起置空**,编辑器全卸载、只留一个待激活目标(`pendingEditingTarget`),焦点因此落在 `body`。旧判据要求 `state.editing === true` 且能取到当前行,于是从第 3 下起就把 Tab 当成「不是给表格的」放行了。现在判据接受「待激活目标还在」这一形态(当前行也从它里面取回来),并按「编辑格不在 DOM 里 → 起点取选区」处理:长按期间一格一格继续走,松手后编辑器落到最后一格。
- **修复:编辑行被虚拟滚动滚出渲染窗口后,按 Tab 会逃出表格**(与按键速度无关,慢速必现)。编辑行 / 编辑列离开渲染窗口时编辑器被虚拟滚动**卸载**,焦点掉到 `body`,而编辑态与选区其实都还在(滚回来就恢复);此时 Tab 的事件源既不在编辑器也不在浮层,会被判定成「不是给表格的」而放行 → 浏览器按文档顺序把焦点送出表格(实测落到侧边栏菜单上)。现在这种情况会被接管:**把表格滚回编辑行**并继续前进一格。判据严格限定为「编辑格此刻不在 DOM 里」,避免把「点了页面空白再按 Tab」也吞掉。
- 接管只针对**本表格自己的编辑器**(含它的浮层):判定靠 DOM 数据属性(`td[data-col-key]`、`tr[data-row-key]`)与编辑态对齐,其余 Tab 一律放行,不影响页面/表单的 Tab 序。
- 文档(`docs/content/ui/data/table.md`)补「跳格」一节;`e2e/table.spec.ts` 增加真浏览器回归(覆盖「焦点在 select 浮层里按 Tab」这条路径,摘掉接管后用例会红)。