"use client"; import { useEffect, useRef, useId } from "react"; import { cn } from "@/shared/utils/cn"; import Button from "./Button"; interface ModalProps { isOpen: boolean; onClose: () => void; title?: React.ReactNode; children?: React.ReactNode; footer?: React.ReactNode; size?: "sm" | "md" | "lg" | "xl" | "full"; closeOnOverlay?: boolean; showCloseButton?: boolean; className?: string; maxWidth?: string; } export default function Modal({ isOpen, onClose, title, children, footer, size = "md", closeOnOverlay = true, showCloseButton = true, className, }: ModalProps) { const titleId = useId(); const dialogRef = useRef(null); const sizes = { sm: "max-w-sm", md: "max-w-md", lg: "max-w-lg", xl: "max-w-xl", full: "max-w-4xl", }; // Lock body scroll when modal is open useEffect(() => { if (isOpen) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [isOpen]); // Handle escape key useEffect(() => { const handleEscape = (e) => { if (e.key === "Escape" && isOpen) { onClose(); } }; document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); }, [isOpen, onClose]); // Focus trap useEffect(() => { if (!isOpen || !dialogRef.current) return; const dialog = dialogRef.current; const focusableSelector = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'; // Focus first focusable element const firstFocusable = dialog.querySelector(focusableSelector); if (firstFocusable) { setTimeout(() => firstFocusable.focus(), 50); } const handleTab = (e) => { if (e.key !== "Tab") return; const focusable = [...dialog.querySelectorAll(focusableSelector)]; if (focusable.length === 0) return; const first = focusable[0]; const last = focusable[focusable.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }; dialog.addEventListener("keydown", handleTab); return () => dialog.removeEventListener("keydown", handleTab); }, [isOpen]); if (!isOpen) return null; return (
{message}