aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/views/common/dialog
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-08-26 21:36:58 +0800
committercrupest <crupest@outlook.com>2023-08-26 21:36:58 +0800
commitf5dfd52f6efece2f4cad227044ecf4dd66301bbc (patch)
tree0d64daae438ac6d95f0848a0b3387134d9528438 /FrontEnd/src/views/common/dialog
parent4daa84ede781cdf6f424d68c967a17c7e1c79f59 (diff)
downloadtimeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.tar.gz
timeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.tar.bz2
timeline-f5dfd52f6efece2f4cad227044ecf4dd66301bbc.zip
...
Diffstat (limited to 'FrontEnd/src/views/common/dialog')
-rw-r--r--FrontEnd/src/views/common/dialog/ConfirmDialog.css0
-rw-r--r--FrontEnd/src/views/common/dialog/ConfirmDialog.tsx59
-rw-r--r--FrontEnd/src/views/common/dialog/Dialog.css60
-rw-r--r--FrontEnd/src/views/common/dialog/Dialog.tsx63
-rw-r--r--FrontEnd/src/views/common/dialog/DialogContainer.css20
-rw-r--r--FrontEnd/src/views/common/dialog/DialogContainer.tsx95
-rw-r--r--FrontEnd/src/views/common/dialog/FullPageDialog.css44
-rw-r--r--FrontEnd/src/views/common/dialog/FullPageDialog.tsx53
-rw-r--r--FrontEnd/src/views/common/dialog/OperationDialog.css8
-rw-r--r--FrontEnd/src/views/common/dialog/OperationDialog.tsx228
-rw-r--r--FrontEnd/src/views/common/dialog/index.ts64
11 files changed, 0 insertions, 694 deletions
diff --git a/FrontEnd/src/views/common/dialog/ConfirmDialog.css b/FrontEnd/src/views/common/dialog/ConfirmDialog.css
deleted file mode 100644
index e69de29b..00000000
--- a/FrontEnd/src/views/common/dialog/ConfirmDialog.css
+++ /dev/null
diff --git a/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx b/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx
deleted file mode 100644
index dbbd15c6..00000000
--- a/FrontEnd/src/views/common/dialog/ConfirmDialog.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { useC, Text, ThemeColor } from "@/views/common/common";
-
-import Dialog from "./Dialog";
-import DialogContainer from "./DialogContainer";
-
-export default function ConfirmDialog({
- open,
- onClose,
- onConfirm,
- title,
- body,
- color,
- bodyColor,
-}: {
- open: boolean;
- onClose: () => void;
- onConfirm: () => void;
- title: Text;
- body: Text;
- color?: ThemeColor;
- bodyColor?: ThemeColor;
-}) {
- const c = useC();
-
- return (
- <Dialog onClose={onClose} open={open}>
- <DialogContainer
- title={title}
- titleColor={color ?? "danger"}
- buttons={[
- {
- key: "cancel",
- type: "normal",
- props: {
- text: "operationDialog.cancel",
- color: "secondary",
- outline: true,
- onClick: onClose,
- },
- },
- {
- key: "confirm",
- type: "normal",
- props: {
- text: "operationDialog.confirm",
- color: "danger",
- onClick: () => {
- onConfirm();
- onClose();
- },
- },
- },
- ]}
- >
- <div className={`cru-${bodyColor ?? "primary"}`}>{c(body)}</div>
- </DialogContainer>
- </Dialog>
- );
-}
diff --git a/FrontEnd/src/views/common/dialog/Dialog.css b/FrontEnd/src/views/common/dialog/Dialog.css
deleted file mode 100644
index e4c61440..00000000
--- a/FrontEnd/src/views/common/dialog/Dialog.css
+++ /dev/null
@@ -1,60 +0,0 @@
-.cru-dialog-overlay {
- position: fixed;
- z-index: 1040;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- display: flex;
- align-items: center;
- overflow: auto;
-}
-
-.cru-dialog-background {
- position: absolute;
- z-index: -1;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background-color: var(--cru-surface-dim-color);
- opacity: 0.8;
-}
-
-.cru-dialog-container {
- max-width: 100%;
- min-width: 30vw;
-
- margin: 2em auto;
-
- border: var(--cru-key-container-color) 1px solid;
- border-radius: 5px;
- padding: 1.5em;
- background-color: var(--cru-surface-color);
-}
-
-@media (min-width: 576px) {
- .cru-dialog-container {
- max-width: 800px;
- }
-}
-
-.cru-dialog-enter .cru-dialog-container {
- transform: scale(0, 0);
- opacity: 0;
- transform-origin: center;
-}
-
-.cru-dialog-enter-active .cru-dialog-container {
- transform: scale(1, 1);
- opacity: 1;
- transition: transform 0.3s, opacity 0.3s;
- transform-origin: center;
-}
-
-.cru-dialog-exit-active .cru-dialog-container {
- transition: transform 0.3s, opacity 0.3s;
- transform: scale(0, 0);
- opacity: 0;
- transform-origin: center;
-} \ No newline at end of file
diff --git a/FrontEnd/src/views/common/dialog/Dialog.tsx b/FrontEnd/src/views/common/dialog/Dialog.tsx
deleted file mode 100644
index 2ff7bea8..00000000
--- a/FrontEnd/src/views/common/dialog/Dialog.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { ReactNode, useRef } from "react";
-import ReactDOM from "react-dom";
-import { CSSTransition } from "react-transition-group";
-import classNames from "classnames";
-
-import { ThemeColor } from "../common";
-
-import "./Dialog.css";
-
-const optionalPortalElement = document.getElementById("portal");
-if (optionalPortalElement == null) {
- throw new Error("Portal element not found");
-}
-const portalElement = optionalPortalElement;
-
-interface DialogProps {
- open: boolean;
- onClose: () => void;
- color?: ThemeColor;
- children?: ReactNode;
- disableCloseOnClickOnOverlay?: boolean;
-}
-
-export default function Dialog({
- open,
- onClose,
- color,
- children,
- disableCloseOnClickOnOverlay,
-}: DialogProps) {
- color = color ?? "primary";
-
- const nodeRef = useRef(null);
-
- return ReactDOM.createPortal(
- <CSSTransition
- nodeRef={nodeRef}
- mountOnEnter
- unmountOnExit
- in={open}
- timeout={300}
- classNames="cru-dialog"
- >
- <div
- ref={nodeRef}
- className={classNames("cru-dialog-overlay", `cru-${color}`)}
- >
- <div
- className="cru-dialog-background"
- onClick={
- disableCloseOnClickOnOverlay
- ? undefined
- : () => {
- onClose();
- }
- }
- />
- <div className="cru-dialog-container">{children}</div>
- </div>
- </CSSTransition>,
- portalElement,
- );
-}
diff --git a/FrontEnd/src/views/common/dialog/DialogContainer.css b/FrontEnd/src/views/common/dialog/DialogContainer.css
deleted file mode 100644
index fbb18e0d..00000000
--- a/FrontEnd/src/views/common/dialog/DialogContainer.css
+++ /dev/null
@@ -1,20 +0,0 @@
-.cru-dialog-container-title {
- font-size: 1.2em;
- font-weight: bold;
- color: var(--cru-key-color);
- margin-bottom: 0.5em;
-}
-
-
-.cru-dialog-container-hr {
- margin: 1em 0;
-}
-
-.cru-dialog-container-button-row {
- display: flex;
- justify-content: flex-end;
-}
-
-.cru-dialog-container-button {
- margin-left: 1em;
-} \ No newline at end of file
diff --git a/FrontEnd/src/views/common/dialog/DialogContainer.tsx b/FrontEnd/src/views/common/dialog/DialogContainer.tsx
deleted file mode 100644
index afee2669..00000000
--- a/FrontEnd/src/views/common/dialog/DialogContainer.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { ComponentProps, Ref, ReactNode } from "react";
-import classNames from "classnames";
-
-import { ThemeColor, Text, useC } from "../common";
-import { ButtonRow, ButtonRowV2 } from "../button";
-
-import "./DialogContainer.css";
-
-interface DialogContainerBaseProps {
- className?: string;
- title: Text;
- titleColor?: ThemeColor;
- titleClassName?: string;
- titleRef?: Ref<HTMLDivElement>;
- bodyContainerClassName?: string;
- bodyContainerRef?: Ref<HTMLDivElement>;
- buttonsClassName?: string;
- buttonsContainerRef?: ComponentProps<typeof ButtonRow>["containerRef"];
- children: ReactNode;
-}
-
-interface DialogContainerWithButtonsProps extends DialogContainerBaseProps {
- buttons: ComponentProps<typeof ButtonRow>["buttons"];
-}
-
-interface DialogContainerWithButtonsV2Props extends DialogContainerBaseProps {
- buttonsV2: ComponentProps<typeof ButtonRowV2>["buttons"];
-}
-
-type DialogContainerProps =
- | DialogContainerWithButtonsProps
- | DialogContainerWithButtonsV2Props;
-
-export default function DialogContainer(props: DialogContainerProps) {
- const {
- className,
- title,
- titleColor,
- titleClassName,
- titleRef,
- bodyContainerClassName,
- bodyContainerRef,
- buttonsClassName,
- buttonsContainerRef,
- children,
- } = props;
-
- const c = useC();
-
- return (
- <div className={classNames(className)}>
- <div
- ref={titleRef}
- className={classNames(
- `cru-dialog-container-title cru-${titleColor ?? "primary"}`,
- titleClassName,
- )}
- >
- {c(title)}
- </div>
- <hr className="cru-dialog-container-hr" />
- <div
- ref={bodyContainerRef}
- className={classNames(
- "cru-dialog-container-body",
- bodyContainerClassName,
- )}
- >
- {children}
- </div>
- <hr className="cru-dialog-container-hr" />
- {"buttons" in props ? (
- <ButtonRow
- containerRef={buttonsContainerRef}
- className={classNames(
- "cru-dialog-container-button-row",
- buttonsClassName,
- )}
- buttons={props.buttons}
- buttonsClassName="cru-dialog-container-button"
- />
- ) : (
- <ButtonRowV2
- containerRef={buttonsContainerRef}
- className={classNames(
- "cru-dialog-container-button-row",
- buttonsClassName,
- )}
- buttons={props.buttonsV2}
- buttonsClassName="cru-dialog-container-button"
- />
- )}
- </div>
- );
-}
diff --git a/FrontEnd/src/views/common/dialog/FullPageDialog.css b/FrontEnd/src/views/common/dialog/FullPageDialog.css
deleted file mode 100644
index 2f1fc636..00000000
--- a/FrontEnd/src/views/common/dialog/FullPageDialog.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.cru-full-page {
- position: fixed;
- z-index: 1030;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- background-color: white;
- padding-top: 56px;
-}
-
-.cru-full-page-top-bar {
- height: 56px;
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- z-index: 1;
- background-color: var(--cru-primary-color);
- display: flex;
- align-items: center;
-}
-
-.cru-full-page-content-container {
- overflow: scroll;
-}
-
-.cru-full-page-back-button {
- color: var(--cru-primary-t-color);
-}
-
-.cru-full-page-enter {
- transform: translate(100%, 0);
-}
-
-.cru-full-page-enter-active {
- transform: none;
- transition: transform 0.3s;
-}
-
-.cru-full-page-exit-active {
- transition: transform 0.3s;
- transform: translate(100%, 0);
-}
diff --git a/FrontEnd/src/views/common/dialog/FullPageDialog.tsx b/FrontEnd/src/views/common/dialog/FullPageDialog.tsx
deleted file mode 100644
index 6368fc0a..00000000
--- a/FrontEnd/src/views/common/dialog/FullPageDialog.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import * as React from "react";
-import { createPortal } from "react-dom";
-import classnames from "classnames";
-import { CSSTransition } from "react-transition-group";
-
-import "./FullPageDialog.css";
-import IconButton from "../button/IconButton";
-
-export interface FullPageDialogProps {
- show: boolean;
- onBack: () => void;
- contentContainerClassName?: string;
- children: React.ReactNode;
-}
-
-const FullPageDialog: React.FC<FullPageDialogProps> = ({
- show,
- onBack,
- children,
- contentContainerClassName,
-}) => {
- return createPortal(
- <CSSTransition
- mountOnEnter
- unmountOnExit
- in={show}
- timeout={300}
- classNames="cru-full-page"
- >
- <div className="cru-full-page">
- <div className="cru-full-page-top-bar">
- <IconButton
- icon="arrow-left"
- className="ms-3 cru-full-page-back-button"
- onClick={onBack}
- />
- </div>
- <div
- className={classnames(
- "cru-full-page-content-container",
- contentContainerClassName
- )}
- >
- {children}
- </div>
- </div>
- </CSSTransition>,
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- document.getElementById("portal")!
- );
-};
-
-export default FullPageDialog;
diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.css b/FrontEnd/src/views/common/dialog/OperationDialog.css
deleted file mode 100644
index f4b7237e..00000000
--- a/FrontEnd/src/views/common/dialog/OperationDialog.css
+++ /dev/null
@@ -1,8 +0,0 @@
-.cru-operation-dialog-prompt {
- color: var(--cru-surface-on-color);
-}
-
-.cru-operation-dialog-input-group {
- display: block;
- margin: 0.5em 0;
-}
diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.tsx b/FrontEnd/src/views/common/dialog/OperationDialog.tsx
deleted file mode 100644
index 4335b2b0..00000000
--- a/FrontEnd/src/views/common/dialog/OperationDialog.tsx
+++ /dev/null
@@ -1,228 +0,0 @@
-import { useState, ReactNode, ComponentProps } from "react";
-import classNames from "classnames";
-
-import { useC, Text, ThemeColor } from "../common";
-
-import {
- useInputs,
- InputGroup,
- Initializer as InputInitializer,
- InputValueDict,
- InputErrorDict,
-} from "../input/InputGroup";
-import Dialog from "./Dialog";
-import DialogContainer from "./DialogContainer";
-
-import "./OperationDialog.css";
-
-export type { InputInitializer, InputValueDict, InputErrorDict };
-
-interface OperationDialogPromptProps {
- message?: Text;
- customMessage?: Text;
- customMessageNode?: ReactNode;
- className?: string;
-}
-
-function OperationDialogPrompt(props: OperationDialogPromptProps) {
- const { message, customMessage, customMessageNode, className } = props;
-
- const c = useC();
-
- return (
- <div className={classNames(className, "cru-operation-dialog-prompt")}>
- {message && <p>{c(message)}</p>}
- {customMessageNode ?? (customMessage != null ? c(customMessage) : null)}
- </div>
- );
-}
-
-export interface OperationDialogProps<TData> {
- open: boolean;
- onClose: () => void;
-
- color?: ThemeColor;
- inputColor?: ThemeColor;
- title: Text;
- inputPrompt?: Text;
- inputPromptNode?: ReactNode;
- successPrompt?: (data: TData) => Text;
- successPromptNode?: (data: TData) => ReactNode;
- failurePrompt?: (error: unknown) => Text;
- failurePromptNode?: (error: unknown) => ReactNode;
-
- inputs: InputInitializer;
-
- onProcess: (inputs: InputValueDict) => Promise<TData>;
- onSuccessAndClose?: (data: TData) => void;
-}
-
-function OperationDialog<TData>(props: OperationDialogProps<TData>) {
- const {
- open,
- onClose,
- color,
- inputColor,
- title,
- inputPrompt,
- inputPromptNode,
- successPrompt,
- successPromptNode,
- failurePrompt,
- failurePromptNode,
- inputs,
- onProcess,
- onSuccessAndClose,
- } = props;
-
- if (process.env.NODE_ENV === "development") {
- if (inputPrompt && inputPromptNode) {
- console.log("InputPrompt and inputPromptNode are both set.");
- }
- if (successPrompt && successPromptNode) {
- console.log("SuccessPrompt and successPromptNode are both set.");
- }
- if (failurePrompt && failurePromptNode) {
- console.log("FailurePrompt and failurePromptNode are both set.");
- }
- }
-
- type Step =
- | { type: "input" }
- | { type: "process" }
- | {
- type: "success";
- data: TData;
- }
- | {
- type: "failure";
- data: unknown;
- };
-
- const [step, setStep] = useState<Step>({ type: "input" });
-
- const { inputGroupProps, hasErrorAndDirty, setAllDisabled, confirm } =
- useInputs({
- init: inputs,
- });
-
- function close() {
- if (step.type !== "process") {
- onClose();
- if (step.type === "success" && onSuccessAndClose) {
- onSuccessAndClose?.(step.data);
- }
- } else {
- console.log("Attempt to close modal dialog when processing.");
- }
- }
-
- function onConfirm() {
- const result = confirm();
- if (result.type === "ok") {
- setStep({ type: "process" });
- setAllDisabled(true);
- onProcess(result.values).then(
- (d) => {
- setStep({
- type: "success",
- data: d,
- });
- },
- (e: unknown) => {
- setStep({
- type: "failure",
- data: e,
- });
- },
- );
- }
- }
-
- let body: ReactNode;
- let buttons: ComponentProps<typeof DialogContainer>["buttons"];
-
- if (step.type === "input" || step.type === "process") {
- const isProcessing = step.type === "process";
-
- body = (
- <div>
- <OperationDialogPrompt
- customMessage={inputPrompt}
- customMessageNode={inputPromptNode}
- />
- <InputGroup
- containerClassName="cru-operation-dialog-input-group"
- color={inputColor ?? "primary"}
- {...inputGroupProps}
- />
- </div>
- );
- buttons = [
- {
- key: "cancel",
- type: "normal",
- props: {
- text: "operationDialog.cancel",
- color: "secondary",
- outline: true,
- onClick: close,
- disabled: isProcessing,
- },
- },
- {
- key: "confirm",
- type: "loading",
- props: {
- text: "operationDialog.confirm",
- color,
- loading: isProcessing,
- disabled: hasErrorAndDirty,
- onClick: onConfirm,
- },
- },
- ];
- } else {
- const result = step;
-
- const promptProps: OperationDialogPromptProps =
- result.type === "success"
- ? {
- message: "operationDialog.success",
- customMessage: successPrompt?.(result.data),
- customMessageNode: successPromptNode?.(result.data),
- }
- : {
- message: "operationDialog.error",
- customMessage: failurePrompt?.(result.data),
- customMessageNode: failurePromptNode?.(result.data),
- };
- body = (
- <div>
- <OperationDialogPrompt {...promptProps} />
- </div>
- );
-
- buttons = [
- {
- key: "ok",
- type: "normal",
- props: {
- text: "operationDialog.ok",
- color: "primary",
- onClick: close,
- },
- },
- ];
- }
-
- return (
- <Dialog open={open} onClose={close}>
- <DialogContainer title={title} titleColor={color} buttons={buttons}>
- {body}
- </DialogContainer>
- </Dialog>
- );
-}
-
-export default OperationDialog;
diff --git a/FrontEnd/src/views/common/dialog/index.ts b/FrontEnd/src/views/common/dialog/index.ts
deleted file mode 100644
index 59f15791..00000000
--- a/FrontEnd/src/views/common/dialog/index.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { useState } from "react";
-
-export { default as Dialog } from "./Dialog";
-export { default as FullPageDialog } from "./FullPageDialog";
-export { default as OperationDialog } from "./OperationDialog";
-export { default as ConfirmDialog } from "./ConfirmDialog";
-
-type DialogMap<D extends string, V> = {
- [K in D]: V;
-};
-
-type DialogKeyMap<D extends string> = DialogMap<D, number>;
-
-type DialogPropsMap<D extends string> = DialogMap<
- D,
- { key: number | string; open: boolean; onClose: () => void }
->;
-
-export function useDialog<D extends string>(
- dialogs: D[],
- options?: {
- initDialog?: D | null;
- onClose?: {
- [K in D]?: () => void;
- };
- },
-): {
- dialog: D | null;
- switchDialog: (newDialog: D | null) => void;
- dialogPropsMap: DialogPropsMap<D>;
- createDialogSwitch: (newDialog: D | null) => () => void;
-} {
- const [dialog, setDialog] = useState<D | null>(options?.initDialog ?? null);
-
- const [dialogKeys, setDialogKeys] = useState<DialogKeyMap<D>>(
- () => Object.fromEntries(dialogs.map((d) => [d, 0])) as DialogKeyMap<D>,
- );
-
- const switchDialog = (newDialog: D | null) => {
- if (dialog !== null) {
- setDialogKeys({ ...dialogKeys, [dialog]: dialogKeys[dialog] + 1 });
- }
- setDialog(newDialog);
- };
-
- return {
- dialog,
- switchDialog,
- dialogPropsMap: Object.fromEntries(
- dialogs.map((d) => [
- d,
- {
- key: `${d}-${dialogKeys[d]}`,
- open: dialog === d,
- onClose: () => {
- switchDialog(null);
- options?.onClose?.[d]?.();
- },
- },
- ]),
- ) as DialogPropsMap<D>,
- createDialogSwitch: (newDialog: D | null) => () => switchDialog(newDialog),
- };
-}