import { useEffect, type ReactNode } from 'react'; import { createPortal } from 'react-dom'; import { X } from 'lucide-react'; import { cn } from '../../lib/cn'; export type ModalWidth = 'sm' | 'md' | 'lg' | 'xl'; const WIDTH: Record = { sm: 'max-w-[480px]', md: 'max-w-[640px]', lg: 'max-w-[820px]', xl: 'max-w-[1000px]', }; export interface ModalProps { open: boolean; onClose: () => void; title?: ReactNode; subtitle?: ReactNode; /** @default "md" */ width?: ModalWidth; children?: ReactNode; } /** Portal modal host — backdrop, Esc / click-out close, scroll-locked body. * Aria's equivalent of the renderer's FormPopup; hosts an activity body. */ export function Modal({ open, onClose, title, subtitle, width = 'md', children }: ModalProps) { useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); 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; return createPortal(
{ if (e.target === e.currentTarget) onClose(); }} >
{title &&

{title}

} {subtitle &&
{subtitle}
}
{children}
, document.body, ); }