1d1027fab4
Added defensive programming by adding null checks for document object before accessing its properties and methods across multiple components (MultiSelect, ConfirmationModal, Dropdown, Modal). Also implemented lazy initialization for Firebase messaging to prevent SSR-related errors and ensure messaging is only initialized on the client side. These changes improve server-side rendering compatibility and prevent potential runtime errors.
94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
import { CrossIcon } from "@/assets/icons";
|
|
import { useIsMutating } from "@tanstack/react-query";
|
|
import { ReactNode, useEffect, useState } from "react";
|
|
import { createPortal } from "react-dom";
|
|
import IsVisible from "./IsVisible";
|
|
|
|
interface ModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onConfirm?: () => void;
|
|
children: ReactNode;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
showButtons?: boolean;
|
|
classNames?: string;
|
|
}
|
|
|
|
const Modal: React.FC<ModalProps> = ({
|
|
isOpen,
|
|
onClose,
|
|
children,
|
|
onConfirm,
|
|
confirmText = "confirm",
|
|
cancelText = "close",
|
|
showButtons = true,
|
|
classNames,
|
|
}) => {
|
|
const [mounted, setMounted] = useState(false);
|
|
const isMutating = useIsMutating();
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
if (document) document.body.style.overflow = "hidden";
|
|
} else {
|
|
if (document) document.body.style.overflow = "unset";
|
|
}
|
|
|
|
return () => {
|
|
if (document) document.body.style.overflow = "unset";
|
|
};
|
|
}, [isOpen]);
|
|
|
|
if (!mounted || !isOpen) return null;
|
|
|
|
const modalContent = (
|
|
<div
|
|
className="fixed inset-0 z-999999 flex items-center justify-center bg-dark/50 dark:bg-black/50"
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className={`max-h-[90vh] max-w-[90vw] overflow-auto rounded-lg bg-white p-5 shadow-4 dark:bg-dark-2 ${classNames}`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<button
|
|
onClick={onClose}
|
|
className="float-right mb-2.5 cursor-pointer border-none bg-transparent p-0 text-2xl text-dark-5 hover:text-primary dark:text-gray-5 dark:hover:text-primary"
|
|
>
|
|
<CrossIcon />
|
|
</button>
|
|
<div className="clear-both">{children}</div>
|
|
<div className="flex gap-5">
|
|
<IsVisible condition={showButtons}>
|
|
<button
|
|
type="submit"
|
|
className="mt-6 flex w-fit justify-center rounded-lg bg-primary p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
|
|
onClick={onConfirm}
|
|
>
|
|
{isMutating ? (
|
|
<div className="h-5 w-5 animate-spin rounded-full border-b-2 border-gray-1"></div>
|
|
) : (
|
|
<span>{confirmText}</span>
|
|
)}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className="mt-6 flex w-fit justify-center rounded-lg bg-error p-[13px] font-medium capitalize text-white hover:bg-opacity-90"
|
|
onClick={onClose}
|
|
>
|
|
{cancelText}
|
|
</button>
|
|
</IsVisible>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return createPortal(modalContent, document.body);
|
|
};
|
|
|
|
export default Modal;
|