78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import React from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { X } from 'lucide-react';
|
|
|
|
interface ModalProps {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
title?: string;
|
|
icon?: React.ReactNode;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
children: React.ReactNode;
|
|
footer?: React.ReactNode;
|
|
}
|
|
|
|
const sizeClasses = {
|
|
sm: 'max-w-md',
|
|
md: 'max-w-lg',
|
|
lg: 'max-w-2xl',
|
|
};
|
|
|
|
export default function Modal({ open, onClose, title, icon, size = 'md', children, footer }: ModalProps) {
|
|
return (
|
|
<AnimatePresence>
|
|
{open && (
|
|
<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 }}
|
|
className="absolute inset-0 bg-black/60 backdrop-blur-sm"
|
|
onClick={onClose}
|
|
/>
|
|
|
|
{/* Container */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95, y: 10 }}
|
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
exit={{ opacity: 0, scale: 0.95, y: 10 }}
|
|
transition={{ duration: 0.2, ease: 'easeOut' }}
|
|
className={`relative w-full ${sizeClasses[size]} glass-elevated rounded-3xl shadow-premium shimmer-line`}
|
|
>
|
|
{/* Header */}
|
|
{title && (
|
|
<div className="flex items-center gap-3 p-6 pb-0">
|
|
{icon && (
|
|
<div className="w-10 h-10 rounded-xl bg-brand-500/15 flex items-center justify-center text-brand-400">
|
|
{icon}
|
|
</div>
|
|
)}
|
|
<h2 className="text-lg font-bold text-white">{title}</h2>
|
|
</div>
|
|
)}
|
|
|
|
{/* Close */}
|
|
<button
|
|
onClick={onClose}
|
|
className="absolute top-4 right-4 p-2 text-slate-400 hover:text-white hover:bg-white/5 rounded-lg transition-colors"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
|
|
{/* Body */}
|
|
<div className="p-6">{children}</div>
|
|
|
|
{/* Footer */}
|
|
{footer && (
|
|
<div className="border-t border-white/5 px-6 py-4 flex justify-end gap-3">
|
|
{footer}
|
|
</div>
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
)}
|
|
</AnimatePresence>
|
|
);
|
|
}
|