import { useEffect, type ReactNode, type KeyboardEvent, type MouseEvent } from 'react' interface ModalProps { open: boolean title: ReactNode onClose: () => void width?: number footer?: ReactNode children: ReactNode closeLabel?: string } export function Modal({ open, title, onClose, width = 420, footer, children, closeLabel = 'Close' }: ModalProps) { useEffect(() => { if (!open) return const onKey = (e: globalThis.KeyboardEvent) => { if (e.key === 'Escape') { e.preventDefault() onClose() } } document.addEventListener('keydown', onKey) // Lock body scroll while the modal is open. const prev = document.body.style.overflow document.body.style.overflow = 'hidden' return () => { document.removeEventListener('keydown', onKey) document.body.style.overflow = prev } }, [open, onClose]) if (!open) return null const onBackdropClick = (e: MouseEvent) => { if (e.target === e.currentTarget) onClose() } const onPanelKey = (e: KeyboardEvent) => { // Stop Escape from bubbling to other key handlers in the page; the // document listener above already handles closing. if (e.key === 'Escape') e.stopPropagation() } return (
{title}
{children}
{footer && (
{footer}
)}
) }