Files
bs-manager/src/renderer/components/modal/modal.component.tsx
T
2024-09-21 22:46:54 +08:00

86 lines
3.5 KiB
TypeScript

import { ModalExitCode, ModalObject, ModalService } from "renderer/services/modale.service";
import { AnimatePresence, motion } from "framer-motion";
import { useObservable } from "renderer/hooks/use-observable.hook";
import { useEffect } from "react";
import { BsmIcon } from "../svgs/bsm-icon.component";
import { ThemeColorGradientSpliter } from "../shared/theme-color-gradient-spliter.component";
import { map } from "rxjs";
import { useConstant } from "renderer/hooks/use-constant.hook";
export function Modal() {
const modalSevice = ModalService.getInstance();
const modals$ = useConstant(() => modalSevice.getModalToShow());
const modals = useObservable(() => modals$);
const currentModal = useObservable<ModalObject>(() =>modals$.pipe(map(modals => modals?.at(-1))));
useEffect(() => {
const onEscape = (e: KeyboardEvent) => {
if (currentModal.options?.closable === false || e.key !== "Escape") {
return;
}
currentModal.resolver({ exitCode: ModalExitCode.CLOSED });
};
if (currentModal) {
window.addEventListener("keyup", onEscape);
} else {
window.removeEventListener("keyup", onEscape);
}
return () => {
window.removeEventListener("keyup", onEscape);
};
}, [currentModal]);
const renderCloseButton = (modal: ModalObject) => {
return (
<div
className="w-2.5 h-2.5 absolute top-2.5 right-1.5 cursor-pointer"
onClick={e => {
e.stopPropagation();
modal.resolver({ exitCode: ModalExitCode.CLOSED });
}}
>
<BsmIcon className="size-full" icon="cross" />
</div>
)
}
const renderModal = (modal: ModalObject) => {
if (!modal?.modal) { return null; }
if(modal.options?.noStyle){
return <modal.modal resolver={modal.resolver} options={modal.options} />
}
return (
<div className="relative p-4 text-gray-800 dark:text-gray-200 rounded-md shadow-lg shadow-black bg-gradient-to-br from-light-main-color-3 to-light-main-color-2 dark:from-main-color-3 dark:to-main-color-2">
<ThemeColorGradientSpliter className="absolute top-0 w-full left-0 h-1 rounded-t-md overflow-hidden"/>
{modal.options?.closable === false ? undefined : renderCloseButton(modal)}
<modal.modal resolver={modal.resolver} options={modal.options} />
</div>
)
}
const onOverlayClicked = () => {
if (currentModal.options?.closable === false) {
return;
}
currentModal.resolver({ exitCode: ModalExitCode.NO_CHOICE });
}
return (
<AnimatePresence>
{currentModal ? <motion.span key={crypto.randomUUID()} onClick={onOverlayClicked} className="fixed size-full bg-black z-[90]" initial={{ opacity: 0 }} animate={{ opacity: currentModal && 0.6 }} exit={{ opacity: 0 }} transition={{ duration: 0.2 }} /> : undefined}
{modals?.map(modal => (
<motion.div key={crypto.randomUUID()} className="fixed z-[90] top-1/2 left-1/2" initial={{ y: "100vh", x: "-50%" }} animate={{y: "-50%", scale: modal === currentModal ? 1 : 0, opacity: modal === currentModal ? 1 : 0, display: modal === currentModal ? "block" : ["block", "none"]}} exit={{ y: "100vh" }}>
{renderModal(modal)}
</motion.div>
))}
</AnimatePresence>
);
}