Custom modal
code for custom modal
"use client"
import type React from "react"
import { useEffect, useRef } from "react"
import { createPortal } from "react-dom"
interface CustomModalProps {
isOpen: boolean
onClose: () => void
title?: string
children: React.ReactNode
size?: "sm" | "md" | "lg" | "xl"
showCloseButton?: boolean
}
export default function CustomModal({
isOpen,
onClose,
title,
children,
size = "md",
showCloseButton = true,
}: CustomModalProps) {
const modalRef = useRef<HTMLDivElement>(null)
// Handle escape key press
useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
if (event.key === "Escape" && isOpen) {
onClose()
}
}
if (isOpen) {
document.addEventListener("keydown", handleEscape)
document.body.style.overflow = "hidden"
}
return () => {
document.removeEventListener("keydown", handleEscape)
document.body.style.overflow = "unset"
}
}, [isOpen, onClose])
// Focus management
useEffect(() => {
if (isOpen && modalRef.current) {
modalRef.current.focus()
}
}, [isOpen])
// Handle backdrop click
const handleBackdropClick = (event: React.MouseEvent) => {
if (event.target === event.currentTarget) {
onClose()
}
}
// Size classes
const sizeClasses = {
sm: "max-w-sm",
md: "max-w-md",
lg: "max-w-lg",
xl: "max-w-xl",
}
if (!isOpen) return null
const modalContent = (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
{/* Backdrop */}
<div
className={`fixed inset-0 bg-black transition-opacity duration-300 ${isOpen ? "opacity-50" : "opacity-0"}`}
onClick={onClose}
/>
{/* Modal */}
<div
ref={modalRef}
tabIndex={-1}
className={`
relative w-full ${sizeClasses[size]} bg-white rounded-lg shadow-xl
transform transition-all duration-300 ease-out
${isOpen ? "scale-100 opacity-100" : "scale-95 opacity-0"}
dark:bg-gray-800
`}
role="dialog"
aria-modal="true"
aria-labelledby={title ? "modal-title" : undefined}
>
{/* Header */}
{(title || showCloseButton) && (
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
{title && (
<h2 id="modal-title" className="text-xl font-semibold text-gray-900 dark:text-white">
{title}
</h2>
)}
{showCloseButton && (
<button
onClick={onClose}
className="p-1 ml-auto bg-transparent border-0 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors duration-200"
aria-label="Close modal"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
)}
</div>
)}
{/* Content */}
<div className="p-6">{children}</div>
</div>
</div>
)
return typeof document !== "undefined" ? createPortal(modalContent, document.body) : null
}