"use client"; import { useEffect, useId, useRef } from "react"; import { createPortal } from "react-dom"; import { ParamHelpEntry } from "./paramHelpContent"; interface Props { entry: ParamHelpEntry; onClose: () => void; } const FOCUSABLE = 'a[href],button:not([disabled]),textarea,input,select,[tabindex]:not([tabindex="-1"])'; /** * Centered dialog with backdrop. Closes on X, Esc, or backdrop click. * Focus is trapped inside the modal and returns to the trigger on * close (the provider handles the return). */ export default function ParamHelpModal({ entry, onClose }: Props) { const titleId = useId(); const containerRef = useRef(null); // Lock body scroll while open. useEffect(() => { const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = prev; }; }, []); // Esc + focus trap. useEffect(() => { function onKey(e: KeyboardEvent) { if (e.key === "Escape") { e.stopPropagation(); onClose(); return; } if (e.key === "Tab" && containerRef.current) { const focusables = containerRef.current.querySelectorAll( FOCUSABLE, ); if (focusables.length === 0) return; const first = focusables[0]; const last = focusables[focusables.length - 1]; const active = document.activeElement as HTMLElement | null; if (e.shiftKey) { if (active === first || !containerRef.current.contains(active)) { e.preventDefault(); last.focus(); } } else { if (active === last) { e.preventDefault(); first.focus(); } } } } document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [onClose]); // Focus the close button once mounted. useEffect(() => { const f = containerRef.current?.querySelector(FOCUSABLE); f?.focus(); }, []); if (typeof document === "undefined") return null; const node = (
{ if (e.target === e.currentTarget) onClose(); }} style={{ position: "fixed", inset: 0, zIndex: 100, background: "rgba(35,48,58,0.32)", display: "flex", alignItems: "flex-start", justifyContent: "center", padding: "min(8vh,72px) 16px", overflowY: "auto", }} >

{entry.title}

{entry.detailed}
); return createPortal(node, document.body); }