fix(ui): a control removed by its own action leaves the focus in its slot
The second half of the pending-button sweep: where the pressed control does not merely disable but LEAVES the page — an invite row gone after Approve, the inbox's Clear all gone with the last notification, a section gone once its last row is handled — the browser dropped the focus to <body> and the next Tab started over at the top. useFocusKeeper, on the page scroller: when the element holding the focus is removed and the focus fell to <body>, the first focusable thing in its slot takes it (what followed, else what preceded, else the nearest ancestor still on the page). Portaled layers (menus, dialogs) sit outside the scroller and return focus themselves; listNav's keyboard lists are left alone — their `j` already goes on from the slot, and a row scrolled out of a virtualized list must not drag the focus after it.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useCallback, useLayoutEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { useScrollFade } from "../../lib/useScrollFade";
|
||||
import { useFocusKeeper } from "../../lib/useFocusKeeper";
|
||||
import { StickyTopProvider } from "./stickyTop";
|
||||
import { ensureNavId, peekNavId, subscribeNav } from "../../lib/history";
|
||||
|
||||
@@ -105,6 +106,10 @@ export default function PageScroller({
|
||||
[fadeRef],
|
||||
);
|
||||
|
||||
// A focused control removed by its own action (a row gone after Approve, a toolbar after Clear
|
||||
// all) leaves the focus in its slot rather than on <body> — see useFocusKeeper.
|
||||
useFocusKeeper(elRef);
|
||||
|
||||
// Live scrollKey for the event/nav-bus callbacks (which are bound once, not per scrollKey).
|
||||
const scrollKeyRef = useRef(scrollKey);
|
||||
scrollKeyRef.current = scrollKey;
|
||||
|
||||
Reference in New Issue
Block a user