import { useEffect, useRef, type ReactNode, type HTMLAttributes, type ReactElement } from "react"; export interface ModalProps extends HTMLAttributes { isOpen: boolean; onClose: () => void; title?: string; footer?: ReactNode; closeOnOverlayClick?: boolean; closeOnEscape?: boolean; size?: "sm" | "md" | "lg" | "xl" | "full"; } export function Modal({ isOpen, onClose, title, footer, closeOnOverlayClick = true, closeOnEscape = true, size = "md", children, className = "", ...props }: ModalProps): ReactElement | null { const dialogRef = useRef(null); const modalId = useRef(`modal-${Math.random().toString(36).substring(2, 11)}`); type ModalSize = "sm" | "md" | "lg" | "xl" | "full"; const sizeStyles: Record = { sm: "max-w-md", md: "max-w-lg", lg: "max-w-2xl", xl: "max-w-4xl", full: "max-w-full mx-4", }; useEffect(() => { const handleEscape = (event: KeyboardEvent): void => { if (closeOnEscape && event.key === "Escape" && isOpen) { onClose(); } }; if (isOpen) { document.addEventListener("keydown", handleEscape); document.body.style.overflow = "hidden"; // Focus the modal when opened dialogRef.current?.focus(); } return (): void => { document.removeEventListener("keydown", handleEscape); document.body.style.overflow = ""; }; }, [isOpen, closeOnEscape, onClose]); if (!isOpen) { return null; } const handleOverlayClick = (event: React.MouseEvent): void => { if (closeOnOverlayClick && event.target === event.currentTarget) { onClose(); } }; return (
{title && (

{title}

)}
{children}
{footer && (
{footer}
)}
); }