This commit is contained in:
Vula Builder
2026-10-11 14:20:30 +00:00
parent 7f5d6b135a
commit 71b8d68d6a
+79
View File
@@ -0,0 +1,79 @@
'use client'
import { useEffect } from 'react'
import { X } from 'lucide-react'
import { cn } from "@/lib/utils"
interface ModalProps {
isOpen: boolean
onClose: () => void
title?: string
description?: string
className?: string
children: React.ReactNode
}
export default function Modal({
isOpen,
onClose,
title,
description,
className,
children
}: ModalProps) {
useEffect(() => {
if (!isOpen) return
const handleKey = (event: KeyboardEvent) => {
if (event.key === 'Escape') onClose()
}
document.body.style.overflow = 'hidden'
window.addEventListener('keydown', handleKey)
return () => {
document.body.style.overflow = ''
window.removeEventListener('keydown', handleKey)
}
}, [isOpen, onClose])
if (!isOpen) return null
return (
<div
role="dialog"
aria-modal="true"
aria-label={title || 'Dialog'}
className="fixed inset-0 z-50 flex items-center justify-center p-4"
>
<div
className="absolute inset-0 bg-background/80 backdrop-blur-sm"
onClick={onClose}
aria-hidden="true"
/>
<div
className={cn(
"relative z-10 w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-lg border border-border bg-background p-6 shadow-2xl",
className
)}
>
<button
type="button"
onClick={onClose}
aria-label="Close dialog"
className="absolute right-4 top-4 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-accent/10 hover:text-accent focus:outline-none focus-visible:border-primary"
>
<X className="h-5 w-5" />
</button>
{title && (
<h2 className="pr-8 text-2xl font-bold tracking-tight text-foreground">
{title}
</h2>
)}
{description && (
<p className="mt-1.5 text-sm text-muted-foreground">{description}</p>
)}
<div className={cn(title || description ? "mt-5" : "mt-0")}>
{children}
</div>
</div>
</div>
)
}