fix(keys): epic-end review round 3 — the part-way clip count shows, busy buttons keep focus, cards say their names
Targeted round over 3cd0c525's fix surfaces: 14 raised, 14 fixed. - VideoEditor: the "only X of Y clips" toast went silent exactly on the job-cap/quota refusal it was written for (req had already reported the error); it now always shows. The cut list holds still while Create queues, and a retry continues the "(i/N)" series. - ChannelPage: Subscribe/Unsubscribe use Button `loading`, "Load more" and ChannelDiscovery's Subscribe block the click with aria-disabled instead of native disabled; busy Block/Hide no longer light up on hover. - listNav: an item can name itself (`navItemAttrs(i, name)`) — Plex posters, seasons, episodes, message rows and download rows pass their title; focus on a toast's Undo keeps the removed card's slot. - AnchoredPopover: the hand-back only for focus-managed surfaces (a hover card no longer pulls focus back); the dead panel-ref check is gone. - AccountPanel: no confirmation mail offered to the demo account; send_failed copy no longer blames the message for an unreachable server. Pager comment points at scrollParent.
This commit is contained in:
@@ -601,6 +601,11 @@ function PopoverSurfaceInner({
|
||||
// in the accessible-name computation — so an explicit ariaLabel is dead unless we drop the ref.
|
||||
if (ariaLabel) delete floatingProps["aria-labelledby"];
|
||||
|
||||
// A menu/listbox/dialog traps focus into the surface (and returns it on close). A hover card is
|
||||
// passive, and a COMBOBOX must leave focus in the input (its list is navigated virtually) — neither
|
||||
// is focus-managed.
|
||||
const managed = focusManaged ?? (mode !== "hover" && !isCombobox);
|
||||
|
||||
// Where focus goes back on close. floating-ui returns it to the element focused before the open —
|
||||
// but when that element is not a Tab stop itself, to its first TABBABLE descendant instead, even
|
||||
// when handed the element as a ref (its return runs through `getFirstTabbableElement` either way).
|
||||
@@ -611,36 +616,35 @@ function PopoverSurfaceInner({
|
||||
// panel, and only when the focus was still in a floating layer (or nowhere) as the panel closed:
|
||||
// focus the reader moved on to stays where they put it. A menu row's action runs a task later
|
||||
// (`closeThenAct`), so a dialog it opens still captures the card as the element to return to.
|
||||
//
|
||||
// Only for a focus-managed surface, whose return this replaces: a hover card or a combobox never
|
||||
// returned focus, and a hover card closing after the reader clicked a link in it must not pull the
|
||||
// focus back to the card under it (deep-review E11 round 3).
|
||||
const [returnTo] = useState(() => {
|
||||
const active = document.activeElement;
|
||||
return active instanceof HTMLElement && active !== document.body && active.tabIndex < 0
|
||||
return managed &&
|
||||
active instanceof HTMLElement &&
|
||||
active !== document.body &&
|
||||
active.tabIndex < 0
|
||||
? active
|
||||
: null;
|
||||
});
|
||||
const floatingRef = refs.floating;
|
||||
useLayoutEffect(() => {
|
||||
if (!returnTo) return;
|
||||
// Read once here: the panel's ref is attached before layout effects run, and cleared by the time
|
||||
// this cleanup does.
|
||||
const floating = floatingRef.current;
|
||||
return () => {
|
||||
// "Still in the layer" is read off the portal marker, not the panel: FloatingPortal renders the
|
||||
// panel a commit after this surface mounts, so the panel's ref is still empty when this effect
|
||||
// runs (deep-review E11 round 3).
|
||||
const active = document.activeElement;
|
||||
const stillOurs =
|
||||
!active ||
|
||||
active === document.body ||
|
||||
!!floating?.contains(active) ||
|
||||
!!active.closest("[data-floating-ui-portal]");
|
||||
!active || active === document.body || !!active.closest("[data-floating-ui-portal]");
|
||||
queueMicrotask(() => {
|
||||
if (stillOurs && returnTo.isConnected) returnTo.focus({ preventScroll: true });
|
||||
});
|
||||
};
|
||||
}, [returnTo, floatingRef]);
|
||||
}, [returnTo]);
|
||||
|
||||
const zClass = mode === "hover" ? "z-tooltip" : "z-popover";
|
||||
// A menu/listbox/dialog traps focus into the surface (and returns it on close). A hover card is
|
||||
// passive, and a COMBOBOX must leave focus in the input (its list is navigated virtually) — neither
|
||||
// is focus-managed.
|
||||
const managed = focusManaged ?? (mode !== "hover" && !isCombobox);
|
||||
|
||||
const surface = (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user