fix(plex): the player's ⋯ and tracks lists are real menus, and every close returns the focus
They were hand-built divs: no menu roles, no arrow keys, and Escape left the focus on <body>. Both run on the popover engine now (media-tone rows), and the tuning panel is a labelled dialog that takes the focus and hands it back to ⋯.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { type RefObject } from "react";
|
||||
import { useLayoutEffect, type RefObject } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Slider, Toggle } from "./plexPlayerControls";
|
||||
@@ -18,6 +18,9 @@ export function PlexPlayerSettings(props: {
|
||||
// which is the honest shape — the element is null until the div mounts. The old `RefObject<T>` lied
|
||||
// about that and 18's types let it.
|
||||
menuRef: RefObject<HTMLDivElement | null>;
|
||||
/** The ⋯ that opened the panel: the focus goes back to it when the panel closes with the focus
|
||||
* inside — by Escape, Back or the ⋯ itself — instead of falling to <body> (E28 review D3). */
|
||||
returnFocusTo: RefObject<HTMLElement | null>;
|
||||
settingsTabs: { key: SettingsTab; label: string }[];
|
||||
activeTab: SettingsTab;
|
||||
setSettingsTab: (tab: SettingsTab) => void;
|
||||
@@ -28,6 +31,7 @@ export function PlexPlayerSettings(props: {
|
||||
}) {
|
||||
const {
|
||||
menuRef,
|
||||
returnFocusTo,
|
||||
settingsTabs,
|
||||
activeTab,
|
||||
setSettingsTab,
|
||||
@@ -37,9 +41,23 @@ export function PlexPlayerSettings(props: {
|
||||
now,
|
||||
} = props;
|
||||
const { t } = useTranslation();
|
||||
// Into the panel on open (its first tab), and back to the ⋯ on close. The cleanup runs while the
|
||||
// panel is still in the DOM, so "the focus was inside" is read before its removal drops it to
|
||||
// <body>; focus the reader moved elsewhere (a click on the video) stays where they put it.
|
||||
useLayoutEffect(() => {
|
||||
const panel = menuRef.current;
|
||||
const back = returnFocusTo.current; // the ⋯ outlives the panel: it is the panel's own trigger
|
||||
panel?.querySelector<HTMLElement>("button")?.focus({ preventScroll: true });
|
||||
return () => {
|
||||
if (panel?.contains(document.activeElement))
|
||||
queueMicrotask(() => back?.focus({ preventScroll: true }));
|
||||
};
|
||||
}, [menuRef, returnFocusTo]);
|
||||
return (
|
||||
<div
|
||||
ref={menuRef}
|
||||
role="dialog"
|
||||
aria-label={t("plex.player.settings")}
|
||||
onWheel={(e) => e.stopPropagation()}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="border border-white/15 bg-media-chrome/95 absolute bottom-full right-0 z-menu mb-2 w-80 rounded-xl p-2 text-sm text-white shadow-2xl"
|
||||
|
||||
Reference in New Issue
Block a user