aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/views/common
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-07-29 22:00:59 +0800
committercrupest <crupest@outlook.com>2023-07-29 22:00:59 +0800
commit22e8f24e7f7574915e4c75d3c6a5498f6e621ee8 (patch)
treea164dbcb6bf2e87e275cc736b65a22f34f788b14 /FrontEnd/src/views/common
parent13f6f2a451feede8037d18cfbac64843e108ccbd (diff)
downloadtimeline-22e8f24e7f7574915e4c75d3c6a5498f6e621ee8.tar.gz
timeline-22e8f24e7f7574915e4c75d3c6a5498f6e621ee8.tar.bz2
timeline-22e8f24e7f7574915e4c75d3c6a5498f6e621ee8.zip
...
Diffstat (limited to 'FrontEnd/src/views/common')
-rw-r--r--FrontEnd/src/views/common/button/LoadingButton.tsx5
-rw-r--r--FrontEnd/src/views/common/dialog/OperationDialog.tsx22
-rw-r--r--FrontEnd/src/views/common/dialog/index.ts56
-rw-r--r--FrontEnd/src/views/common/input/InputGroup.tsx7
4 files changed, 77 insertions, 13 deletions
diff --git a/FrontEnd/src/views/common/button/LoadingButton.tsx b/FrontEnd/src/views/common/button/LoadingButton.tsx
index f23369de..bfa5b6b8 100644
--- a/FrontEnd/src/views/common/button/LoadingButton.tsx
+++ b/FrontEnd/src/views/common/button/LoadingButton.tsx
@@ -15,7 +15,8 @@ interface LoadingButtonProps extends React.ComponentPropsWithoutRef<"button"> {
export default function LoadingButton(props: LoadingButtonProps) {
const c = useC();
- const { color, text, loading, className, children, ...otherProps } = props;
+ const { color, text, loading, disabled, className, children, ...otherProps } =
+ props;
if (text != null && children != null) {
console.warn("You can't set both text and children props.");
@@ -23,7 +24,7 @@ export default function LoadingButton(props: LoadingButtonProps) {
return (
<button
- disabled={loading}
+ disabled={disabled || loading}
className={classNames(
`cru-${color ?? "primary"} cru-button outline cru-loading-button`,
className,
diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.tsx b/FrontEnd/src/views/common/dialog/OperationDialog.tsx
index be3f7158..74b4a5fa 100644
--- a/FrontEnd/src/views/common/dialog/OperationDialog.tsx
+++ b/FrontEnd/src/views/common/dialog/OperationDialog.tsx
@@ -39,7 +39,7 @@ function OperationDialogPrompt(props: OperationDialogPromptProps) {
export interface OperationDialogProps<TData> {
open: boolean;
- close: () => void;
+ onClose: () => void;
color?: ThemeColor;
inputColor?: ThemeColor;
@@ -57,7 +57,7 @@ export interface OperationDialogProps<TData> {
function OperationDialog<TData>(props: OperationDialogProps<TData>) {
const {
open,
- close,
+ onClose,
color,
inputColor,
title,
@@ -85,13 +85,14 @@ function OperationDialog<TData>(props: OperationDialogProps<TData>) {
const [step, setStep] = useState<Step>({ type: "input" });
- const { inputGroupProps, hasError, setAllDisabled, confirm } = useInputs({
- init: inputs,
- });
+ const { inputGroupProps, hasErrorAndDirty, setAllDisabled, confirm } =
+ useInputs({
+ init: inputs,
+ });
- function onClose() {
+ function close() {
if (step.type !== "process") {
- close();
+ onClose();
if (step.type === "success" && onSuccessAndClose) {
onSuccessAndClose?.(step.data);
}
@@ -142,12 +143,13 @@ function OperationDialog<TData>(props: OperationDialogProps<TData>) {
text="operationDialog.cancel"
color="secondary"
outline
- onClick={onClose}
+ onClick={close}
disabled={isProcessing}
/>
<LoadingButton
color={color}
loading={isProcessing}
+ disabled={hasErrorAndDirty}
onClick={onConfirm}
>
{c("operationDialog.confirm")}
@@ -173,14 +175,14 @@ function OperationDialog<TData>(props: OperationDialogProps<TData>) {
<OperationDialogPrompt {...promptProps} />
<hr />
<div className="cru-dialog-bottom-area">
- <Button text="operationDialog.ok" color="primary" onClick={onClose} />
+ <Button text="operationDialog.ok" color="primary" onClick={close} />
</div>
</div>
);
}
return (
- <Dialog open={open} onClose={onClose}>
+ <Dialog open={open} onClose={close}>
<div
className={classNames(
"cru-operation-dialog-container",
diff --git a/FrontEnd/src/views/common/dialog/index.ts b/FrontEnd/src/views/common/dialog/index.ts
new file mode 100644
index 00000000..e37b9ed2
--- /dev/null
+++ b/FrontEnd/src/views/common/dialog/index.ts
@@ -0,0 +1,56 @@
+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[],
+ initDialog?: D | null,
+): {
+ dialog: D | null;
+ switchDialog: (newDialog: D | null) => void;
+ dialogPropsMap: DialogPropsMap<D>;
+ createDialogSwitch: (newDialog: D | null) => () => void;
+} {
+ const [dialog, setDialog] = useState<D | null>(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),
+ },
+ ]),
+ ) as DialogPropsMap<D>,
+ createDialogSwitch: (newDialog: D | null) => () => switchDialog(newDialog),
+ };
+}
diff --git a/FrontEnd/src/views/common/input/InputGroup.tsx b/FrontEnd/src/views/common/input/InputGroup.tsx
index 858fa1a5..3d1e3ada 100644
--- a/FrontEnd/src/views/common/input/InputGroup.tsx
+++ b/FrontEnd/src/views/common/input/InputGroup.tsx
@@ -141,6 +141,7 @@ export type ConfirmResult =
export function useInputs(options: { init: Initializer }): {
inputGroupProps: InputGroupProps;
hasError: boolean;
+ hasErrorAndDirty: boolean;
confirm: () => ConfirmResult;
setAllDisabled: (disabled: boolean) => void;
} {
@@ -260,6 +261,9 @@ export function useInputs(options: { init: Initializer }): {
componentInputs.push(componentInput);
}
+ const hasError = Object.keys(data.errors).length > 0;
+ const hasDirty = Object.keys(data.dirties).some((key) => data.dirties[key]);
+
return {
inputGroupProps: {
inputs: componentInputs,
@@ -280,7 +284,8 @@ export function useInputs(options: { init: Initializer }): {
});
},
},
- hasError: Object.keys(data.errors).length > 0,
+ hasError,
+ hasErrorAndDirty: hasError && hasDirty,
confirm() {
const newDirties = createAllDirties();
const newErrors = validator?.(data.values, scheme.inputs) ?? {};