import { createContext, useCallback, useMemo, useState, type ComponentType, type ReactNode, } from "react"; import { idAssert } from "../../util/ts.js"; export interface PopupManagerContextData { openPopup(popup: ComponentType<{ id: string; onClose: () => void }>): string; openPopup( popup: ComponentType<{ id: string; onClose: () => void } & T>, props: T ): string; closePopup(id: string): void; } export const PopupManagerContext = createContext(null); PopupManagerContext.displayName = "PopupManagerContext"; type Popup = { id: string; Component: ComponentType<{ id: string; onClose: () => void }>; props: { id: string; onClose: () => void }; }; export function PopupManager({ children }: { children: ReactNode }) { const [popups, setPopups] = useState([]); const openPopup = useCallback( ( Component: ComponentType<{ id: string; onClose: () => void }>, props = {} ) => { const id = crypto.randomUUID(); Object.assign(props, { id, onClose: () => setPopups((prev) => prev.filter((x) => x.id !== id)), }); idAssert<{ id: string; onClose: () => void }>(props); setPopups((prev) => [...prev, { id, Component, props }]); return id; }, [] ); const closePopup = useCallback((id: string) => { setPopups((prev) => prev.filter((x) => x.id !== id)); }, []); const ctx = useMemo( () => ({ openPopup, closePopup }), [] ); return ( {children} {popups.map((popup) => ( ))} ); }