import { useState, useContext, createContext, ReactNode } from "react"; import { UiLogicError } from "../common"; type DialogMap = { [K in D]: ReactNode; }; interface DialogController { currentDialog: D | null; currentDialogReactNode: ReactNode; canSwitchDialog: boolean; switchDialog: (newDialog: D | null) => void; setCanSwitchDialog: (enable: boolean) => void; closeDialog: () => void; forceSwitchDialog: (newDialog: D | null) => void; forceCloseDialog: () => void; } export function useDialog( dialogs: DialogMap, options?: { initDialog?: D | null; onClose?: { [K in D]?: () => void; }; }, ): { controller: DialogController; switchDialog: (newDialog: D | null) => void; forceSwitchDialog: (newDialog: D | null) => void; createDialogSwitch: (newDialog: D | null) => () => void; } { const [canSwitchDialog, setCanSwitchDialog] = useState(true); const [dialog, setDialog] = useState(options?.initDialog ?? null); const forceSwitchDialog = (newDialog: D | null) => { if (dialog != null) { options?.onClose?.[dialog]?.(); } setDialog(newDialog); setCanSwitchDialog(true); }; const switchDialog = (newDialog: D | null) => { if (canSwitchDialog) { forceSwitchDialog(newDialog); } }; const controller: DialogController = { currentDialog: dialog, currentDialogReactNode: dialog == null ? null : dialogs[dialog], canSwitchDialog, switchDialog, setCanSwitchDialog, closeDialog: () => switchDialog(null), forceSwitchDialog, forceCloseDialog: () => forceSwitchDialog(null), }; return { controller, switchDialog, forceSwitchDialog, createDialogSwitch: (newDialog: D | null) => () => switchDialog(newDialog), }; } const DialogControllerContext = createContext | null>( null, ); export function useDialogController(): DialogController { const controller = useContext(DialogControllerContext); if (controller == null) throw new UiLogicError("not in dialog provider"); return controller; } export function useCloseDialog(): () => void { const controller = useDialogController(); return controller.closeDialog; } export function DialogProvider({ controller, }: { controller: DialogController; }) { return ( {controller.currentDialogReactNode} ); }