1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
import { useState, useContext, createContext, ReactNode } from "react";
import { UiLogicError } from "../common";
type DialogMap<D extends string> = {
[K in D]: ReactNode;
};
interface DialogController<D extends string> {
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<D extends string>(
dialogs: DialogMap<D>,
options?: {
initDialog?: D | null;
onClose?: {
[K in D]?: () => void;
};
},
): {
controller: DialogController<D>;
switchDialog: (newDialog: D | null) => void;
forceSwitchDialog: (newDialog: D | null) => void;
createDialogSwitch: (newDialog: D | null) => () => void;
} {
const [canSwitchDialog, setCanSwitchDialog] = useState<boolean>(true);
const [dialog, setDialog] = useState<D | null>(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<D> = {
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<DialogController<string> | null>(
null,
);
export function useDialogController(): DialogController<string> {
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<D extends string>({
controller,
}: {
controller: DialogController<D>;
}) {
return (
<DialogControllerContext.Provider value={controller as never}>
{controller.currentDialogReactNode}
</DialogControllerContext.Provider>
);
}
|