aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/views
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2023-07-30 23:47:53 +0800
committercrupest <crupest@outlook.com>2023-07-30 23:47:53 +0800
commit538d6830a0022b49b99695095d85e567b0c86e71 (patch)
treea0c4d164b05d03f636d603b28f77ca881c16ef10 /FrontEnd/src/views
parenta148f11c193d35ba489f887ed393aedf58a1c714 (diff)
downloadtimeline-538d6830a0022b49b99695095d85e567b0c86e71.tar.gz
timeline-538d6830a0022b49b99695095d85e567b0c86e71.tar.bz2
timeline-538d6830a0022b49b99695095d85e567b0c86e71.zip
...
Diffstat (limited to 'FrontEnd/src/views')
-rw-r--r--FrontEnd/src/views/common/dialog/OperationDialog.tsx6
-rw-r--r--FrontEnd/src/views/common/input/InputGroup.tsx47
2 files changed, 36 insertions, 17 deletions
diff --git a/FrontEnd/src/views/common/dialog/OperationDialog.tsx b/FrontEnd/src/views/common/dialog/OperationDialog.tsx
index da1ff0e0..de92e541 100644
--- a/FrontEnd/src/views/common/dialog/OperationDialog.tsx
+++ b/FrontEnd/src/views/common/dialog/OperationDialog.tsx
@@ -43,7 +43,7 @@ export interface OperationDialogProps<TData> {
color?: ThemeColor;
inputColor?: ThemeColor;
title: Text;
- inputPrompt?: Text;
+ inputPrompt?: () => ReactNode;
successPrompt?: (data: TData) => ReactNode;
failurePrompt?: (error: unknown) => ReactNode;
@@ -68,8 +68,6 @@ function OperationDialog<TData>(props: OperationDialogProps<TData>) {
onSuccessAndClose,
} = props;
- const c = useC();
-
type Step =
| { type: "input" }
| { type: "process" }
@@ -130,7 +128,7 @@ function OperationDialog<TData>(props: OperationDialogProps<TData>) {
body = (
<div>
- <OperationDialogPrompt customMessage={c(inputPrompt)} />
+ <OperationDialogPrompt customMessage={inputPrompt?.()} />
<InputGroup
containerClassName="cru-operation-dialog-input-group"
color={inputColor ?? "primary"}
diff --git a/FrontEnd/src/views/common/input/InputGroup.tsx b/FrontEnd/src/views/common/input/InputGroup.tsx
index ee89b05c..5714411c 100644
--- a/FrontEnd/src/views/common/input/InputGroup.tsx
+++ b/FrontEnd/src/views/common/input/InputGroup.tsx
@@ -70,6 +70,13 @@ export type InputErrorDict = Record<string, Text>;
export type InputDisabledDict = Record<string, boolean>;
export type InputDirtyDict = Record<string, boolean>;
+export type GeneralInputErrorDict =
+ | {
+ [key: string]: Text | null | undefined;
+ }
+ | null
+ | undefined;
+
type MakeInputInfo<I extends Input> = Omit<I, "value" | "error" | "disabled">;
export type InputInfo = {
@@ -79,7 +86,7 @@ export type InputInfo = {
export type Validator = (
values: InputValueDict,
inputs: InputInfo[],
-) => InputErrorDict;
+) => GeneralInputErrorDict;
export type InputScheme = {
inputs: InputInfo[];
@@ -98,7 +105,12 @@ export type State = {
data: InputData;
};
-export type DataInitialization = Partial<InputData>;
+export type DataInitialization = {
+ values?: InputValueDict;
+ errors?: GeneralInputErrorDict;
+ disabled?: InputDisabledDict;
+ dirties?: InputDirtyDict;
+};
export type Initialization = {
scheme: InputScheme;
@@ -118,14 +130,14 @@ export interface InputGroupProps {
onChange: (index: number, value: Input["value"]) => void;
}
-function cleanObject<V>(o: Record<string, V>): Record<string, V> {
+function cleanObject<V>(o: Record<string, V>): Record<string, NonNullable<V>> {
const result = { ...o };
for (const key of Object.keys(result)) {
if (result[key] == null) {
delete result[key];
}
}
- return result;
+ return result as never;
}
export type ConfirmResult =
@@ -138,6 +150,14 @@ export type ConfirmResult =
errors: InputErrorDict;
};
+function validate(
+ validator: Validator | null | undefined,
+ values: InputValueDict,
+ inputs: InputInfo[],
+): InputErrorDict {
+ return cleanObject(validator?.(values, inputs) ?? {});
+}
+
export function useInputs(options: { init: Initializer }): {
inputGroupProps: InputGroupProps;
hasError: boolean;
@@ -182,18 +202,22 @@ export function useInputs(options: { init: Initializer }): {
};
checkKeys(dataInit?.values);
- checkKeys(dataInit?.errors);
+ checkKeys(dataInit?.errors ?? {});
checkKeys(dataInit?.disabled);
checkKeys(dataInit?.dirties);
}
- function clean<V>(dict: Record<string, V> | undefined): Record<string, V> {
+ function clean<V>(
+ dict: Record<string, V> | null | undefined,
+ ): Record<string, NonNullable<V>> {
return dict != null ? cleanObject(dict) : {};
}
const values: InputValueDict = {};
const disabled: InputDisabledDict = clean(dataInit?.disabled);
const dirties: InputDirtyDict = clean(dataInit?.dirties);
+ const isErrorSet = dataInit?.errors != null;
+ let errors: InputErrorDict = clean(dataInit?.errors);
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
@@ -202,17 +226,14 @@ export function useInputs(options: { init: Initializer }): {
values[key] = initializeValue(input, dataInit?.values?.[key]);
}
- let errors = dataInit?.errors;
-
- if (errors != null) {
+ if (isErrorSet) {
if (process.env.NODE_ENV === "development") {
console.log(
"You explicitly set errors (not undefined) in initializer, so validator won't run.",
);
}
- errors = cleanObject(errors);
} else {
- errors = validator?.(values, inputs) ?? {};
+ errors = validate(validator, values, inputs);
}
return {
@@ -272,7 +293,7 @@ export function useInputs(options: { init: Initializer }): {
const { key } = input;
const newValues = { ...data.values, [key]: value };
const newDirties = { ...data.dirties, [key]: true };
- const newErrors = validator?.(newValues, scheme.inputs) ?? {};
+ const newErrors = validate(validator, newValues, scheme.inputs);
setState({
scheme,
data: {
@@ -288,7 +309,7 @@ export function useInputs(options: { init: Initializer }): {
hasErrorAndDirty: hasError && hasDirty,
confirm() {
const newDirties = createAllDirties();
- const newErrors = validator?.(data.values, scheme.inputs) ?? {};
+ const newErrors = validate(validator, data.values, scheme.inputs);
setState({
scheme,