import { useEffect, type RefObject } from "react"; import { useLayer } from "./layerStack"; /** While `active`, close (call `onClose`) on Escape or a mousedown outside ALL of the given * element(s). The reusable half of the popover/dropdown pattern that several components each * hand-rolled; positioning stays with the caller (it varies per popover). Pass the panel ref * (and optionally a trigger ref so clicking the trigger doesn't immediately re-close). * * Escape is dispatched through the shared `layerStack` (R8 S3): the popover registers as a layer and * closes only when it is topmost, so a Modal or another popover opened above it takes the Escape * first, and a player below defers to it. This replaces the old blanket `stopPropagation` — which * swallowed a player's own Escape (e.g. PlexPlayer's auto-skip-cancel) whenever any popover was open. */ export function useDismiss( active: boolean, onClose: () => void, refs: RefObject | RefObject[], ): void { // Registration + topmost-only Escape. useLayer(active, onClose); // Outside-press stays per-instance (the refs/positioning are the caller's): close on a mousedown // that lands outside every registered element. useEffect(() => { if (!active) return; const list = Array.isArray(refs) ? refs : [refs]; const onDown = (e: MouseEvent) => { const target = e.target as Node; if (!list.some((r) => r.current?.contains(target))) onClose(); }; document.addEventListener("mousedown", onDown); return () => { document.removeEventListener("mousedown", onDown); }; // Matches the original effect: (re)subscribe only when open/closed toggles. Refs are stable and // onClose is read fresh from this run's closure. // eslint-disable-next-line react-hooks/exhaustive-deps }, [active]); }