| 1 |
"use client"; |
| 2 |
|
| 3 |
import React, { useEffect } from "react"; |
| 4 |
import { X } from "lucide-react"; |
| 5 |
|
| 6 |
export default function Modal({ isOpen, onClose, title, children, maxWidth = "max-w-2xl" }) { |
| 7 |
useEffect(() => { |
| 8 |
const handleEscape = (e) => { |
| 9 |
if (e.key === "Escape") onClose(); |
| 10 |
}; |
| 11 |
if (isOpen) { |
| 12 |
document.addEventListener("keydown", handleEscape); |
| 13 |
document.body.style.overflow = "hidden"; |
| 14 |
} |
| 15 |
return () => { |
| 16 |
document.removeEventListener("keydown", handleEscape); |
| 17 |
document.body.style.overflow = "unset"; |
| 18 |
}; |
| 19 |
}, [isOpen, onClose]); |
| 20 |
|
| 21 |
if (!isOpen) return null; |
| 22 |
|
| 23 |
return ( |
| 24 |
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4"> |
| 25 |
{/* Backdrop */} |
| 26 |
<div |
| 27 |
className="absolute inset-0 bg-black/60 backdrop-blur-sm animate-in fade-in duration-300" |
| 28 |
onClick={onClose} |
| 29 |
/> |
| 30 |
|
| 31 |
{/* Modal Content */} |
| 32 |
<div |
| 33 |
className={`relative w-full ${maxWidth} bg-sidebar border border-sidebar-border rounded-xl shadow-2xl animate-in zoom-in-95 fade-in duration-300 flex flex-col max-h-[90vh]`} |
| 34 |
onClick={(e) => e.stopPropagation()} |
| 35 |
> |
| 36 |
<div className="flex items-center justify-between p-4 border-b border-sidebar-border bg-card/30 rounded-t-xl"> |
| 37 |
<h3 className="text-lg font-bold text-foreground">{title}</h3> |
| 38 |
<button |
| 39 |
onClick={onClose} |
| 40 |
className="p-1.5 hover:bg-sidebar-accent rounded-lg transition-colors text-muted-foreground hover:text-foreground" |
| 41 |
> |
| 42 |
<X className="w-5 h-5" /> |
| 43 |
</button> |
| 44 |
</div> |
| 45 |
|
| 46 |
<div className="flex-1 overflow-y-auto p-6"> |
| 47 |
{children} |
| 48 |
</div> |
| 49 |
</div> |
| 50 |
</div> |
| 51 |
); |
| 52 |
} |
| 53 |
|