Files
mintel.me/apps/web/src/components/Modal.tsx
Marc Mintel 34b35e2f17
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 10s
Build & Deploy / 🧪 QA (push) Failing after 1m26s
Build & Deploy / 🏗️ Build (push) Failing after 3m19s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
refactor: komplettsanierung
2026-02-17 01:56:15 +01:00

83 lines
2.4 KiB
TypeScript

"use client";
import * as React from "react";
import { createPortal } from "react-dom";
import { motion, AnimatePresence } from "framer-motion";
import { X } from "lucide-react";
import { cn } from "../utils/cn";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
maxWidth?: string;
}
export function Modal({
isOpen,
onClose,
title,
children,
maxWidth = "max-w-lg",
}: ModalProps) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
return () => setMounted(false);
}, []);
// Close on escape key
React.useEffect(() => {
const handleEsc = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", handleEsc);
return () => window.removeEventListener("keydown", handleEsc);
}, [onClose]);
if (!mounted) return null;
return createPortal(
<AnimatePresence>
{isOpen && (
<>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
className="fixed inset-0 bg-slate-900/60 backdrop-blur-md z-[9999]"
/>
<div className="fixed inset-0 flex items-center justify-center p-2 z-[10000] pointer-events-none">
<motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }}
className={cn(
"bg-white w-full rounded-[2rem] shadow-2xl pointer-events-auto overflow-hidden border border-slate-100",
maxWidth,
)}
>
<div className="px-6 md:px-8 border-b border-slate-50 flex items-center justify-between bg-slate-50/50">
<h3 className="text-lg md:text-xl font-bold text-slate-900 tracking-tight">
{title}
</h3>
<button
onClick={onClose}
className="p-1.5 hover:bg-slate-100 rounded-full transition-colors text-slate-400 hover:text-slate-900"
>
<X size={18} />
</button>
</div>
<div className="p-6 md:p-8">{children}</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>,
document.body,
);
}