fix(nav): Back restores the focus after a click on the page's blank area

The page region is <main tabIndex=-1>, so a background click focused it and the
Back restore took that for the reader's choice. A resting place is marked and
one predicate (focusIsUnclaimed) decides "the focus is nowhere" everywhere.
This commit is contained in:
2026-09-27 14:06:16 +02:00
parent 383c85231a
commit 9bedd96774
6 changed files with 45 additions and 11 deletions
+6 -5
View File
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState, type RefObject } from "react";
import { dispatch, useKeyScope } from "./keymap";
import { scrollParent } from "./listPaging";
import { isStandInFocus } from "./useFocusKeeper";
import { focusIsUnclaimed } from "./useFocusKeeper";
// ── Keyboard navigation over lists and grids (E11 S4, UX-6) ──────────────────────────────────────
// Before this, a feed card cost ~10 Tab stops (thumb, play, info, title, channel, five actions), so
@@ -148,6 +148,9 @@ function onFocusIn(e: FocusEvent): void {
const list =
item && lists.get(item.parentElement?.closest<HTMLElement>(`[${LIST}]`) as HTMLElement);
if (item && list) last = { list, index: indexOf(item), el: item };
// The focus coming to REST on the page region (a click on its background) is not leaving the list
// for other content — like a click on <body>, which fires no focusin at all.
else if (focusIsUnclaimed(target)) return;
// Focus went on to page content that is not an item — the search box, a filter chip: the reader
// left the list, and whatever it shows next (new results) is not "the slot they were in" (deep-review
// E11 round 2). A layer opened FROM the item — its menu, a confirm, the Undo toast its removal put
@@ -375,8 +378,7 @@ function refocusSlot(list: NavList, index: number, removed: HTMLElement): void {
if (++frames < REMOVE_FRAMES) requestAnimationFrame(wait);
return;
}
const active = document.activeElement;
if (active && active !== document.body && !isStandInFocus(active)) return;
if (!focusIsUnclaimed()) return;
const n = list.count();
if (n > 0) focusIndex(list, Math.min(index, n - 1));
};
@@ -450,8 +452,7 @@ export function restoreNavPosition(pos: NavPosition): () => void {
const deadline = Date.now() + RESTORE_MS;
let timer: ReturnType<typeof setTimeout> | undefined;
const attempt = () => {
const active = document.activeElement;
if (active && active !== document.body && !isStandInFocus(active)) return;
if (!focusIsUnclaimed()) return;
const all = ordered();
const list = pos.label
? all.find((l) => labelOf(l) === pos.label)