diff --git a/src/components/dashboard/anchored-popover.tsx b/src/components/dashboard/anchored-popover.tsx new file mode 100644 index 0000000..d199a19 --- /dev/null +++ b/src/components/dashboard/anchored-popover.tsx @@ -0,0 +1,79 @@ +"use client"; + +// A dropdown that renders into
instead of next to its trigger. +// +// Why: `.dash-topbar` is `position: sticky; z-index: 20`, which opens a stacking context — any +// popover inside it is trapped at the topbar's level no matter how high its own z-index goes. +// Module content can legitimately stack above that (the vendored photo-gallery SDK reaches 1400), +// so an in-topbar dropdown gets covered. Portalling to takes the popover out of that +// context entirely, which is the only fix that survives a module raising its own z-index later. +// +// The panel is positioned from the trigger's viewport rect and closes on scroll/resize rather than +// tracking it — a menu that follows the page while you scroll reads as broken. + +import { useEffect, useLayoutEffect, useRef, useState, type ReactNode, type RefObject } from "react"; +import { createPortal } from "react-dom"; + +/** Above the vendored gallery SDK's maximum (1400) so module chrome can never cover a global menu. */ +const POPOVER_Z = 2000; + +export function AnchoredPopover({ + anchorRef, + open, + onClose, + children, + width = 260, +}: { + anchorRef: RefObject{push.subscribed @@ -67,8 +68,7 @@ export function NotificationBell() { {push.error &&
{push.error}
}