Files
vula-bf109bd0/src/components/ui/Modal.tsx
T
Vula Builder 434e53a79a Deploy
2026-06-04 11:25:13 +00:00

73 lines
1.9 KiB
TypeScript

'use client'
import { cn } from "@/lib/utils"
import { X } from 'lucide-react'
import { useEffect, useCallback } from "react"
interface ModalProps {
isOpen: boolean
onClose: () => void
title?: string
children: React.ReactNode
className?: string
}
export default function Modal({ isOpen, onClose, title, children, className }: ModalProps) {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === "Escape") onClose()
},
[onClose]
)
useEffect(() => {
if (isOpen) {
document.addEventListener("keydown", handleKeyDown)
document.body.style.overflow = "hidden"
} else {
document.body.style.overflow = "unset"
}
return () => {
document.removeEventListener("keydown", handleKeyDown)
document.body.style.overflow = "unset"
}
}, [isOpen, handleKeyDown])
if (!isOpen) return null
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div
className="fixed inset-0 bg-black/50"
aria-hidden="true"
onClick={onClose}
/>
<div
role="dialog"
aria-modal="true"
aria-labelledby={title ? "modal-title" : undefined}
className={cn(
"relative z-10 w-full max-w-lg bg-background rounded-lg shadow-xl p-6",
className
)}
>
<div className="flex items-center justify-between mb-4">
{title && (
<h2 id="modal-title" className="text-xl font-semibold">
{title}
</h2>
)}
<button
type="button"
onClick={onClose}
className="ml-auto rounded-md p-1 hover:bg-accent focus:outline-none focus:ring-2 focus:ring-[#000080]"
aria-label="Close modal"
>
<X className="h-5 w-5" />
</button>
</div>
{children}
</div>
</div>
)
}