|
import React, { useEffect } from 'react' |
|
import { X } from 'lucide-react' |
|
|
|
interface ModalProps { |
|
isOpen: boolean |
|
onClose: () => void |
|
title: React.ReactNode |
|
children: React.ReactNode |
|
maxWidth?: |
|
| 'sm' |
|
| 'md' |
|
| 'lg' |
|
| 'xl' |
|
| '2xl' |
|
| '3xl' |
|
| '4xl' |
|
| '5xl' |
|
| '6xl' |
|
| '7xl' |
|
} |
|
|
|
const Modal: React.FC<ModalProps> = ({ |
|
isOpen, |
|
onClose, |
|
title, |
|
children, |
|
maxWidth = '4xl' |
|
}) => { |
|
useEffect(() => { |
|
const handleEscape = (e: KeyboardEvent) => { |
|
if (e.key === 'Escape') { |
|
onClose() |
|
} |
|
} |
|
|
|
if (isOpen) { |
|
document.addEventListener('keydown', handleEscape) |
|
document.body.style.overflow = 'hidden' |
|
} |
|
|
|
return () => { |
|
document.removeEventListener('keydown', handleEscape) |
|
document.body.style.overflow = 'unset' |
|
} |
|
}, [isOpen, onClose]) |
|
|
|
if (!isOpen) return null |
|
|
|
const maxWidthClasses = { |
|
sm: 'max-w-sm', |
|
md: 'max-w-md', |
|
lg: 'max-w-lg', |
|
xl: 'max-w-xl', |
|
'2xl': 'max-w-2xl', |
|
'3xl': 'max-w-3xl', |
|
'4xl': 'max-w-4xl', |
|
'5xl': 'max-w-5xl', |
|
'6xl': 'max-w-6xl', |
|
'7xl': 'max-w-7xl' |
|
} |
|
|
|
return ( |
|
<div className="fixed inset-0 z-50 overflow-y-auto"> |
|
{/* Backdrop */} |
|
<div |
|
className="fixed inset-0 bg-black opacity-50 transition-opacity" |
|
onClick={onClose} |
|
/> |
|
|
|
{/* Modal */} |
|
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0"> |
|
<div |
|
className={`relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full ${maxWidthClasses[maxWidth]}`} |
|
onClick={(e) => e.stopPropagation()} |
|
> |
|
{/* Header */} |
|
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200"> |
|
<h3 className="text-lg font-semibold text-gray-900">{title}</h3> |
|
<button |
|
onClick={onClose} |
|
className="rounded-md p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 focus:outline-hidden focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" |
|
> |
|
<span className="sr-only">Close</span> |
|
<X className="h-5 w-5" /> |
|
</button> |
|
</div> |
|
|
|
{/* Content */} |
|
<div className="px-6 py-4 max-h-[calc(100vh-200px)] overflow-y-auto"> |
|
{children} |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
) |
|
} |
|
|
|
export default Modal |
|
|