fix(keys): a list inside the topmost layer walks by keyboard
The list keys are the page's and an open layer silences them, so the player's related videos could only be reached by Tab. A layer now names its root element; with the focus on a row inside the topmost one, j/k, the arrows and Enter walk that list, the walk stays inside the layer, and the player's seek and volume arrows leave a focused row alone.
This commit is contained in:
@@ -26,12 +26,21 @@ const passive = new Set<number>();
|
||||
// none, and Tab from anything outside the prompt walked into the page behind the scrim. The two
|
||||
// questions are separate; a layer answers each for itself.
|
||||
const defers = new Set<number>();
|
||||
// The element a layer draws into, for the layers that hold content of their own to navigate (a
|
||||
// dialog, the player): a list inside the TOPMOST such layer is walked by the keyboard although a
|
||||
// layer is open — it is the one the user is looking at, not the page behind.
|
||||
const roots = new Map<number, () => HTMLElement | null>();
|
||||
|
||||
export function pushLayer(opts?: { passive?: boolean; trapsTab?: boolean }): number {
|
||||
export function pushLayer(opts?: {
|
||||
passive?: boolean;
|
||||
trapsTab?: boolean;
|
||||
root?: () => HTMLElement | null;
|
||||
}): number {
|
||||
const id = nextId++;
|
||||
stack.push(id);
|
||||
if (opts?.passive) passive.add(id);
|
||||
if (opts?.trapsTab === false) defers.add(id);
|
||||
if (opts?.root) roots.set(id, opts.root);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -39,6 +48,15 @@ export function removeLayer(id: number): void {
|
||||
stack = stack.filter((x) => x !== id);
|
||||
passive.delete(id);
|
||||
defers.delete(id);
|
||||
roots.delete(id);
|
||||
}
|
||||
|
||||
/** The element of the topmost layer that owns the keyboard — null while none is open, or while that
|
||||
* layer gave none (a menu: its own keys move inside it). */
|
||||
export function topBlockingLayerRoot(): HTMLElement | null {
|
||||
const blocking = stack.filter((id) => !passive.has(id));
|
||||
const top = blocking[blocking.length - 1];
|
||||
return top === undefined ? null : (roots.get(top)?.() ?? null);
|
||||
}
|
||||
|
||||
/** True while a layer that owns the keyboard is open — a modal, a player, a click-opened menu. The
|
||||
|
||||
Reference in New Issue
Block a user