71 lines
2.3 KiB
JavaScript
71 lines
2.3 KiB
JavaScript
import React, { useEffect } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { X } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
const Modal = ({ isOpen, onClose, title, children }) => {
|
|
// Close on ESC key
|
|
useEffect(() => {
|
|
const handleEsc = (e) => {
|
|
if (e.key === 'Escape') onClose();
|
|
};
|
|
window.addEventListener('keydown', handleEsc);
|
|
return () => window.removeEventListener('keydown', handleEsc);
|
|
}, [onClose]);
|
|
|
|
// Prevent scroll when open
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
document.body.style.overflow = 'hidden';
|
|
} else {
|
|
document.body.style.overflow = 'unset';
|
|
}
|
|
}, [isOpen]);
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{isOpen && (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
|
|
{/* Backdrop */}
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
onClick={onClose}
|
|
className="absolute inset-0 bg-slate-950/40 backdrop-blur-sm"
|
|
/>
|
|
|
|
{/* Modal Content */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95, y: 30 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.95, y: 30 }}
|
|
transition={{ type: 'spring', duration: 0.6, bounce: 0.3 }}
|
|
className="relative w-full max-w-md bg-[#1e293b] rounded-2xl shadow-[0_32px_64px_-12px_rgba(0,0,0,0.6)] border border-slate-800 overflow-hidden"
|
|
>
|
|
{/* Header */}
|
|
<div className="p-6 pb-4 border-b border-slate-800 flex justify-between items-center bg-slate-900/40">
|
|
<h3 className="text-sm font-black text-white uppercase tracking-[0.2em]">{title}</h3>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={onClose}
|
|
className="rounded-xl h-8 w-8 hover:bg-slate-800 text-slate-500 hover:text-white transition-all shadow-inner border border-transparent hover:border-slate-700"
|
|
>
|
|
<X className="w-4 h-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Body */}
|
|
<div className="p-8">
|
|
{children}
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
};
|
|
|
|
export default Modal;
|