import React, { useEffect, useRef } from 'react' import { createPortal } from 'react-dom' /** * Nova Modal – accessible dialog rendered in a portal. * * @prop {boolean} open - controls visibility * @prop {function} onClose - called on backdrop click / Escape * @prop {string} title - dialog header title * @prop {React.ReactNode} footer - rendered in footer area * @prop {string} size - 'sm' | 'md' | 'lg' | 'xl' | 'full' * @prop {boolean} closeOnBackdrop - close when clicking outside (default true) * @prop {string} variant - 'default' | 'danger' */ const sizeClass = { sm: 'max-w-sm', md: 'max-w-md', lg: 'max-w-lg', xl: 'max-w-xl', '2xl':'max-w-2xl', full: 'max-w-full h-full rounded-none', } export default function Modal({ open, onClose, title, footer, size = 'md', closeOnBackdrop = true, variant = 'default', children, className = '', }) { const panelRef = useRef(null) // Lock scroll when open useEffect(() => { if (!open) return const prev = document.body.style.overflow document.body.style.overflow = 'hidden' return () => { document.body.style.overflow = prev } }, [open]) // Trap focus + handle Escape useEffect(() => { if (!open) return const handleKey = (e) => { if (e.key === 'Escape') onClose?.() } window.addEventListener('keydown', handleKey) // Focus first focusable element const firstFocusable = panelRef.current?.querySelector( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ) firstFocusable?.focus() return () => window.removeEventListener('keydown', handleKey) }, [open, onClose]) if (!open) return null const borderClass = variant === 'danger' ? 'border-red-500/30' : 'border-white/10' const sClass = sizeClass[size] ?? sizeClass.md return createPortal(