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

4.4 KiB
Raw Blame History

@g3soft/ui
@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」这条路径,摘掉接管后用例会红)。