Files
SalonRo/src/components/UI/Modal.tsx
2025-12-24 11:20:50 +03:30

67 lines
2.2 KiB
TypeScript

"use client";
import { useModalStore } from "@/stores/useModalStore";
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useRef } from "react";
import { createPortal } from "react-dom";
const Modal = () => {
const isOpen = useModalStore((s) => s.isOpen);
const content = useModalStore((s) => s.content);
const closeModal = useModalStore((s) => s.closeModal);
const modalRef = useRef<HTMLDivElement>(null);
// Handle escape key to close
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") closeModal();
};
if (isOpen) {
document.addEventListener("keydown", handleEscape);
document.body.style.overflow = "hidden"; // Prevent scrolling
}
return () => {
document.removeEventListener("keydown", handleEscape);
document.body.style.overflow = "unset";
};
}, [isOpen, closeModal]);
const handleClickOutside = (e: React.MouseEvent) => {
if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
closeModal();
}
};
if (!isOpen) return null;
return createPortal(
<AnimatePresence>
<motion.div
role="dialog"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={handleClickOutside}
>
<motion.div
ref={modalRef}
initial={{ scale: 0.95, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.95, opacity: 0 }}
transition={{ duration: 0.3 }}
className="relative rounded-lg shadow-lg"
onClick={(e) => e.stopPropagation()}
>
{content}
</motion.div>
</motion.div>
</AnimatePresence>,
document.body
);
};
export default Modal;