import { useEffect, useRef, type ReactNode } from 'react' /** * A centered modal dialog over a dimmed backdrop. Closes on Escape or a backdrop * click, unless `busy` (a mutation is in flight) — then it stays put so the * action can finish and report. The caller owns open/closed state (render only * when open). */ export function Modal({ title, onClose, busy = false, children, }: { title: string onClose: () => void busy?: boolean children: ReactNode }) { const cardRef = useRef(null) // Keep the latest onClose/busy in refs so the mount-only effect below never // re-runs (which would re-attach the listener and steal focus on every parent // re-render, e.g. during a background refetch). const onCloseRef = useRef(onClose) onCloseRef.current = onClose const busyRef = useRef(busy) busyRef.current = busy useEffect(() => { cardRef.current?.focus() function onKey(e: KeyboardEvent) { if (e.key === 'Escape' && !busyRef.current) onCloseRef.current() } document.addEventListener('keydown', onKey) return () => document.removeEventListener('keydown', onKey) }, []) return (
{ if (e.target === e.currentTarget && !busy) onClose() }} >

{title}

{children}
) }