import { useEffect, useRef, type ReactNode } from "react"; import { createPortal } from "react-dom"; import { X } from "lucide-react"; import { useBackToClose } from "../lib/history"; // Stack of open modals so ESC only closes the topmost one — e.g. an error dialog over the // tag editor: pressing ESC dismisses just the error and returns to the editor underneath. let modalStack: number[] = []; let nextModalId = 1; // Small centered modal shell (portaled to
): backdrop + ESC + scroll-lock close. export default function Modal({ title, onClose, children, maxWidth = "max-w-lg", }: { title: ReactNode; onClose: () => void; children: ReactNode; maxWidth?: string; }) { // Browser/mouse Back closes the topmost modal instead of navigating away. useBackToClose(onClose); // Keep a stable handler across renders so the stack id is assigned once per mount. const onCloseRef = useRef(onClose); onCloseRef.current = onClose; useEffect(() => { const id = nextModalId++; modalStack.push(id); const onKey = (e: KeyboardEvent) => { if (e.key === "Escape" && modalStack[modalStack.length - 1] === id) { e.stopPropagation(); onCloseRef.current(); } }; window.addEventListener("keydown", onKey); const prev = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { window.removeEventListener("keydown", onKey); modalStack = modalStack.filter((x) => x !== id); document.body.style.overflow = prev; }; }, []); return createPortal(