import { useEffect, useId, useState } from "react"; import { createPortal } from "react-dom"; import { XIcon } from "@primer/octicons-react"; import * as styles from "./modal.css"; import { useAppDispatch } from "@renderer/hooks"; import { toggleDragging } from "@renderer/features"; export interface ModalProps { visible: boolean; title: string; description: string; onClose: () => void; children: React.ReactNode; } export function Modal({ visible, title, description, onClose, children, }: ModalProps) { const [isClosing, setIsClosing] = useState(false); const dispatch = useAppDispatch(); const componentId = useId(); const handleCloseClick = () => { setIsClosing(true); const zero = performance.now(); requestAnimationFrame(function animateClosing(time) { if (time - zero <= 400) { requestAnimationFrame(animateClosing); } else { onClose(); setIsClosing(false); } }); }; const isTopMostModal = () => { const openModals = document.getElementsByClassName("modal-container"); return openModals.length && openModals[openModals.length - 1].id === componentId; }; useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape" && isTopMostModal()) { handleCloseClick(); } }; window.addEventListener("keydown", onKeyDown, false); return () => window.removeEventListener("keydown", onKeyDown, false); }, []); useEffect(() => { const onMouseUp = (e: MouseEvent) => { if (!isTopMostModal()) return; const modalContent = document.getElementById( "modal-content-" + componentId ); const clickInsideContent = modalContent.contains(e.target as Node); if (!clickInsideContent) { handleCloseClick(); } }; window.addEventListener("mousedown", onMouseUp); return () => window.removeEventListener("mousedown", onMouseUp); }, []); useEffect(() => { dispatch(toggleDragging(visible)); }, [dispatch, visible]); if (!visible) return null; return createPortal(

{title}

{description}

{children}
, document.body ); }