Files
deckerr/src/components/Modal.tsx

81 lines
1.9 KiB
TypeScript

import React, { useEffect } from 'react';
import { X } from 'lucide-react';
interface ModalProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
size?: 'sm' | 'md' | 'lg';
showCloseButton?: boolean;
}
export default function Modal({
isOpen,
onClose,
children,
size = 'md',
showCloseButton = true
}: ModalProps) {
// Close modal on ESC key
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose]);
// Prevent body scroll when modal is open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isOpen]);
if (!isOpen) return null;
const sizeClasses = {
sm: 'max-w-md',
md: 'max-w-lg',
lg: 'max-w-2xl',
};
return (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black bg-opacity-50 z-[110] transition-opacity duration-300 animate-fade-in"
onClick={onClose}
/>
{/* Modal */}
<div className="fixed inset-0 z-[120] flex items-center justify-center p-4 pointer-events-none">
<div
className={`${sizeClasses[size]} w-full bg-gray-800 rounded-lg shadow-2xl pointer-events-auto animate-scale-in`}
onClick={(e) => e.stopPropagation()}
>
{showCloseButton && (
<button
onClick={onClose}
className="absolute top-4 right-4 text-gray-400 hover:text-white transition-colors z-10"
>
<X size={24} />
</button>
)}
{children}
</div>
</div>
</>
);
}