aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/components/dialog/DialogProvider.tsx
blob: bb85e4cf95d8d72a8200192587ce1902918d741e (plain)
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>
  );
}