aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd')
-rw-r--r--FrontEnd/package-lock.json124
-rw-r--r--FrontEnd/package.json2
-rw-r--r--FrontEnd/src/app/http/timeline.ts1
-rw-r--r--FrontEnd/src/app/locales/en/translation.json3
-rw-r--r--FrontEnd/src/app/locales/zh/translation.json3
-rw-r--r--FrontEnd/src/app/palette.ts4
-rw-r--r--FrontEnd/src/app/views/admin/UserAdmin.tsx4
-rw-r--r--FrontEnd/src/app/views/common/OperationDialog.tsx107
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx57
9 files changed, 246 insertions, 59 deletions
diff --git a/FrontEnd/package-lock.json b/FrontEnd/package-lock.json
index a2b8539e..2524e59d 100644
--- a/FrontEnd/package-lock.json
+++ b/FrontEnd/package-lock.json
@@ -21,6 +21,7 @@
"pepjs": "^0.5.3",
"react": "^17.0.1",
"react-bootstrap": "^2.0.0-alpha.1",
+ "react-color": "^2.19.3",
"react-dom": "^17.0.2",
"react-i18next": "^11.8.15",
"react-inlinesvg": "^2.3.0",
@@ -54,6 +55,7 @@
"@types/lodash": "^4.14.168",
"@types/node": "^15.0.2",
"@types/react": "^17.0.5",
+ "@types/react-color": "^3.0.4",
"@types/react-dom": "^17.0.3",
"@types/react-responsive": "^8.0.2",
"@types/react-router": "^5.1.13",
@@ -1654,6 +1656,14 @@
"@hapi/hoek": "^8.3.0"
}
},
+ "node_modules/@icons/material": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
+ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@@ -2089,6 +2099,16 @@
"csstype": "^3.0.2"
}
},
+ "node_modules/@types/react-color": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.4.tgz",
+ "integrity": "sha512-EswbYJDF1kkrx93/YU+BbBtb46CCtDMvTiGmcOa/c5PETnwTiSWoseJ1oSWeRl/4rUXkhME9bVURvvPg0W5YQw==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*",
+ "@types/reactcss": "*"
+ }
+ },
"node_modules/@types/react-dom": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
@@ -2146,6 +2166,15 @@
"@types/react": "*"
}
},
+ "node_modules/@types/reactcss": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz",
+ "integrity": "sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
"node_modules/@types/remarkable": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/remarkable/-/remarkable-2.0.1.tgz",
@@ -7909,6 +7938,11 @@
"css-mediaquery": "^0.1.2"
}
},
+ "node_modules/material-colors": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
+ "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
+ },
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -10980,6 +11014,23 @@
"react-dom": ">=16.14.0"
}
},
+ "node_modules/react-color": {
+ "version": "2.19.3",
+ "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
+ "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
+ "dependencies": {
+ "@icons/material": "^0.2.4",
+ "lodash": "^4.17.15",
+ "lodash-es": "^4.17.15",
+ "material-colors": "^1.2.1",
+ "prop-types": "^15.5.10",
+ "reactcss": "^1.2.0",
+ "tinycolor2": "^1.4.1"
+ },
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
@@ -11145,6 +11196,14 @@
"react-dom": ">=16.6.0"
}
},
+ "node_modules/reactcss": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
+ "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
+ "dependencies": {
+ "lodash": "^4.0.1"
+ }
+ },
"node_modules/read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -12609,6 +12668,14 @@
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
+ "node_modules/tinycolor2": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
+ "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -15114,6 +15181,12 @@
"@hapi/hoek": "^8.3.0"
}
},
+ "@icons/material": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
+ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
+ "requires": {}
+ },
"@nodelib/fs.scandir": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz",
@@ -15460,6 +15533,16 @@
"csstype": "^3.0.2"
}
},
+ "@types/react-color": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-3.0.4.tgz",
+ "integrity": "sha512-EswbYJDF1kkrx93/YU+BbBtb46CCtDMvTiGmcOa/c5PETnwTiSWoseJ1oSWeRl/4rUXkhME9bVURvvPg0W5YQw==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "@types/reactcss": "*"
+ }
+ },
"@types/react-dom": {
"version": "17.0.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.3.tgz",
@@ -15517,6 +15600,15 @@
"@types/react": "*"
}
},
+ "@types/reactcss": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz",
+ "integrity": "sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*"
+ }
+ },
"@types/remarkable": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/remarkable/-/remarkable-2.0.1.tgz",
@@ -19995,6 +20087,11 @@
"css-mediaquery": "^0.1.2"
}
},
+ "material-colors": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
+ "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
+ },
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -22268,6 +22365,20 @@
"warning": "^4.0.3"
}
},
+ "react-color": {
+ "version": "2.19.3",
+ "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
+ "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
+ "requires": {
+ "@icons/material": "^0.2.4",
+ "lodash": "^4.17.15",
+ "lodash-es": "^4.17.15",
+ "material-colors": "^1.2.1",
+ "prop-types": "^15.5.10",
+ "reactcss": "^1.2.0",
+ "tinycolor2": "^1.4.1"
+ }
+ },
"react-dom": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz",
@@ -22394,6 +22505,14 @@
"prop-types": "^15.6.2"
}
},
+ "reactcss": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
+ "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
+ "requires": {
+ "lodash": "^4.0.1"
+ }
+ },
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -23554,6 +23673,11 @@
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
+ "tinycolor2": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
+ "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
+ },
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
diff --git a/FrontEnd/package.json b/FrontEnd/package.json
index 5abb8d65..c44613cc 100644
--- a/FrontEnd/package.json
+++ b/FrontEnd/package.json
@@ -19,6 +19,7 @@
"pepjs": "^0.5.3",
"react": "^17.0.1",
"react-bootstrap": "^2.0.0-alpha.1",
+ "react-color": "^2.19.3",
"react-dom": "^17.0.2",
"react-i18next": "^11.8.15",
"react-inlinesvg": "^2.3.0",
@@ -69,6 +70,7 @@
"@types/lodash": "^4.14.168",
"@types/node": "^15.0.2",
"@types/react": "^17.0.5",
+ "@types/react-color": "^3.0.4",
"@types/react-dom": "^17.0.3",
"@types/react-responsive": "^8.0.2",
"@types/react-router": "^5.1.13",
diff --git a/FrontEnd/src/app/http/timeline.ts b/FrontEnd/src/app/http/timeline.ts
index 50af259e..efc402c1 100644
--- a/FrontEnd/src/app/http/timeline.ts
+++ b/FrontEnd/src/app/http/timeline.ts
@@ -72,6 +72,7 @@ export interface HttpTimelinePostPostRequest {
export interface HttpTimelinePatchRequest {
name?: string;
title?: string;
+ color?: string;
visibility?: TimelineVisibility;
description?: string;
}
diff --git a/FrontEnd/src/app/locales/en/translation.json b/FrontEnd/src/app/locales/en/translation.json
index 73cee2e6..1261b086 100644
--- a/FrontEnd/src/app/locales/en/translation.json
+++ b/FrontEnd/src/app/locales/en/translation.json
@@ -85,7 +85,8 @@
"title": "Change Timeline Properties",
"titleField": "Title",
"visibility": "Visibility",
- "description": "Description"
+ "description": "Description",
+ "color": "Color"
},
"member": {
"noUserAvailableToAdd": "Sorry, no user available to be a member in search result.",
diff --git a/FrontEnd/src/app/locales/zh/translation.json b/FrontEnd/src/app/locales/zh/translation.json
index 1a1a70ab..b2c651f6 100644
--- a/FrontEnd/src/app/locales/zh/translation.json
+++ b/FrontEnd/src/app/locales/zh/translation.json
@@ -85,7 +85,8 @@
"title": "修改时间线属性",
"titleField": "标题",
"visibility": "可见性",
- "description": "描述"
+ "description": "描述",
+ "color": "颜色"
},
"member": {
"noUserAvailableToAdd": "搜索结果显示没有可以添加为成员的用户。",
diff --git a/FrontEnd/src/app/palette.ts b/FrontEnd/src/app/palette.ts
index 98e7d814..c4f4f4f9 100644
--- a/FrontEnd/src/app/palette.ts
+++ b/FrontEnd/src/app/palette.ts
@@ -95,9 +95,11 @@ const paletteSubject: BehaviorSubject<Palette> = new BehaviorSubject<Palette>(
export const palette$: Observable<Palette> = paletteSubject.asObservable();
palette$.subscribe((palette) => {
- let styleTag = document.getElementById("timeline-palette-css");
+ const styleTagId = "timeline-palette-css";
+ let styleTag = document.getElementById(styleTagId);
if (styleTag == null) {
styleTag = document.createElement("style");
+ styleTag.id = styleTagId;
document.head.append(styleTag);
}
styleTag.innerHTML = generatePaletteCSS(palette);
diff --git a/FrontEnd/src/app/views/admin/UserAdmin.tsx b/FrontEnd/src/app/views/admin/UserAdmin.tsx
index 61140bb1..26c40a33 100644
--- a/FrontEnd/src/app/views/admin/UserAdmin.tsx
+++ b/FrontEnd/src/app/views/admin/UserAdmin.tsx
@@ -3,7 +3,7 @@ import classnames from "classnames";
import { ListGroup, Row, Col, Spinner, Button } from "react-bootstrap";
import OperationDialog, {
- OperationBoolInputInfo,
+ OperationDialogBoolInput,
} from "../common/OperationDialog";
import { AuthUser } from "@/services/user";
@@ -145,7 +145,7 @@ const UserPermissionModifyDialog: React.FC<
0<UsernameLabel>{username}</UsernameLabel>2
</Trans>
)}
- inputScheme={kUserPermissionList.map<OperationBoolInputInfo>(
+ inputScheme={kUserPermissionList.map<OperationDialogBoolInput>(
(permission, index) => ({
type: "bool",
label: permission,
diff --git a/FrontEnd/src/app/views/common/OperationDialog.tsx b/FrontEnd/src/app/views/common/OperationDialog.tsx
index 5887be48..c9fa709f 100644
--- a/FrontEnd/src/app/views/common/OperationDialog.tsx
+++ b/FrontEnd/src/app/views/common/OperationDialog.tsx
@@ -1,6 +1,7 @@
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import { Form, Button, Modal } from "react-bootstrap";
+import { ChromePicker } from "react-color";
import { convertI18nText, I18nText, UiLogicError } from "@/common";
@@ -27,7 +28,7 @@ const DefaultErrorPrompt: React.FC<DefaultErrorPromptProps> = (props) => {
return result;
};
-export interface OperationTextInputInfo {
+export interface OperationDialogTextInput {
type: "text";
label?: I18nText;
password?: boolean;
@@ -39,40 +40,51 @@ export interface OperationTextInputInfo {
helperText?: string;
}
-export interface OperationBoolInputInfo {
+export interface OperationDialogBoolInput {
type: "bool";
label: I18nText;
initValue?: boolean;
}
-export interface OperationSelectInputInfoOption {
+export interface OperationDialogSelectInputOption {
value: string;
label: I18nText;
icon?: React.ReactElement;
}
-export interface OperationSelectInputInfo {
+export interface OperationDialogSelectInput {
type: "select";
label: I18nText;
- options: OperationSelectInputInfoOption[];
+ options: OperationDialogSelectInputOption[];
initValue?: string;
}
-export type OperationInputInfo =
- | OperationTextInputInfo
- | OperationBoolInputInfo
- | OperationSelectInputInfo;
+export interface OperationDialogColorInput {
+ type: "color";
+ label?: I18nText;
+ initValue?: string | null;
+ disableAlpha?: boolean;
+ canBeNull?: boolean;
+}
-type MapOperationInputInfoValueType<T> = T extends OperationTextInputInfo
+export type OperationDialogInput =
+ | OperationDialogTextInput
+ | OperationDialogBoolInput
+ | OperationDialogSelectInput
+ | OperationDialogColorInput;
+
+type MapOperationInputInfoValueType<T> = T extends OperationDialogTextInput
? string
- : T extends OperationBoolInputInfo
+ : T extends OperationDialogBoolInput
? boolean
- : T extends OperationSelectInputInfo
+ : T extends OperationDialogSelectInput
? string
+ : T extends OperationDialogColorInput
+ ? string | null
: never;
type MapOperationInputInfoValueTypeList<
- Tuple extends readonly OperationInputInfo[]
+ Tuple extends readonly OperationDialogInput[]
> = {
[Index in keyof Tuple]: MapOperationInputInfoValueType<Tuple[Index]>;
} & { length: Tuple["length"] };
@@ -94,7 +106,7 @@ const isNoError = (error: OperationInputError): boolean => {
export interface OperationDialogProps<
TData,
- OperationInputInfoList extends readonly OperationInputInfo[]
+ OperationInputInfoList extends readonly OperationDialogInput[]
> {
open: boolean;
close: () => void;
@@ -116,18 +128,18 @@ export interface OperationDialogProps<
const OperationDialog = <
TData,
- OperationInputInfoList extends readonly OperationInputInfo[]
+ OperationInputInfoList extends readonly OperationDialogInput[]
>(
props: OperationDialogProps<TData, OperationInputInfoList>
): React.ReactElement => {
const inputScheme = (props.inputScheme ??
- []) as readonly OperationInputInfo[];
+ []) as readonly OperationDialogInput[];
const { t } = useTranslation();
type Step =
- | "input"
- | "process"
+ | { type: "input" }
+ | { type: "process" }
| {
type: "success";
data: TData;
@@ -136,14 +148,20 @@ const OperationDialog = <
type: "failure";
data: unknown;
};
- const [step, setStep] = useState<Step>("input");
- const [values, setValues] = useState<(boolean | string)[]>(
+ const [step, setStep] = useState<Step>({ type: "input" });
+
+ type ValueType = boolean | string | null | undefined;
+
+ const [values, setValues] = useState<ValueType[]>(
inputScheme.map((i) => {
if (i.type === "bool") {
return i.initValue ?? false;
} else if (i.type === "text" || i.type === "select") {
return i.initValue ?? "";
- } else {
+ } else if (i.type === "color") {
+ return i.initValue ?? null;
+ }
+ {
throw new UiLogicError("Unknown input scheme.");
}
})
@@ -154,13 +172,9 @@ const OperationDialog = <
const [inputError, setInputError] = useState<OperationInputError>();
const close = (): void => {
- if (step !== "process") {
+ if (step.type !== "process") {
props.close();
- if (
- typeof step === "object" &&
- step.type === "success" &&
- props.onSuccessAndClose
- ) {
+ if (step.type === "success" && props.onSuccessAndClose) {
props.onSuccessAndClose(step.data);
}
} else {
@@ -169,7 +183,7 @@ const OperationDialog = <
};
const onConfirm = (): void => {
- setStep("process");
+ setStep({ type: "process" });
props
.onProcess(
(values as unknown) as MapOperationInputInfoValueTypeList<OperationInputInfoList>
@@ -191,8 +205,8 @@ const OperationDialog = <
};
let body: React.ReactNode;
- if (step === "input" || step === "process") {
- const process = step === "process";
+ if (step.type === "input" || step.type === "process") {
+ const process = step.type === "process";
let inputPrompt =
typeof props.inputPrompt === "function"
@@ -200,7 +214,7 @@ const OperationDialog = <
: convertI18nText(props.inputPrompt, t);
inputPrompt = <h6>{inputPrompt}</h6>;
- const validate = (values: (string | boolean)[]): boolean => {
+ const validate = (values: ValueType[]): boolean => {
const { inputValidator } = props;
if (inputValidator != null) {
const result = inputValidator(
@@ -212,7 +226,7 @@ const OperationDialog = <
return true;
};
- const updateValue = (index: number, newValue: string | boolean): void => {
+ const updateValue = (index: number, newValue: ValueType): void => {
const oldValues = values;
const newValues = oldValues.slice();
newValues[index] = newValue;
@@ -301,6 +315,35 @@ const OperationDialog = <
</Form.Control>
</Form.Group>
);
+ } else if (item.type === "color") {
+ return (
+ <Form.Group key={index}>
+ {item.canBeNull ? (
+ <Form.Check<"input">
+ type="checkbox"
+ checked={value !== null}
+ onChange={(event) => {
+ if (event.currentTarget.checked) {
+ updateValue(index, "#007bff");
+ } else {
+ updateValue(index, null);
+ }
+ }}
+ label={convertI18nText(item.label, t)}
+ disabled={process}
+ />
+ ) : (
+ <Form.Label>{convertI18nText(item.label, t)}</Form.Label>
+ )}
+ {value !== null && (
+ <ChromePicker
+ color={value as string}
+ onChange={(result) => updateValue(index, result.hex)}
+ disableAlpha={item.disableAlpha}
+ />
+ )}
+ </Form.Group>
+ );
}
})}
</Modal.Body>
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
index a5628a9a..c65e097d 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePropertyChangeDialog.tsx
@@ -31,30 +31,39 @@ const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps>
return (
<OperationDialog
title={"timeline.dialogChangeProperty.title"}
- inputScheme={[
- {
- type: "text",
- label: "timeline.dialogChangeProperty.titleField",
- initValue: timeline.title,
- },
- {
- type: "select",
- label: "timeline.dialogChangeProperty.visibility",
- options: kTimelineVisibilities.map((v) => ({
- label: labelMap[v],
- value: v,
- })),
- initValue: timeline.visibility,
- },
- {
- type: "text",
- label: "timeline.dialogChangeProperty.description",
- initValue: timeline.description,
- },
- ]}
+ inputScheme={
+ [
+ {
+ type: "text",
+ label: "timeline.dialogChangeProperty.titleField",
+ initValue: timeline.title,
+ },
+ {
+ type: "select",
+ label: "timeline.dialogChangeProperty.visibility",
+ options: kTimelineVisibilities.map((v) => ({
+ label: labelMap[v],
+ value: v,
+ })),
+ initValue: timeline.visibility,
+ },
+ {
+ type: "text",
+ label: "timeline.dialogChangeProperty.description",
+ initValue: timeline.description,
+ },
+ {
+ type: "color",
+ label: "timeline.dialogChangeProperty.color",
+ initValue: timeline.color ?? null,
+ disableAlpha: true,
+ canBeNull: true,
+ },
+ ] as const
+ }
open={props.open}
close={props.close}
- onProcess={([newTitle, newVisibility, newDescription]) => {
+ onProcess={([newTitle, newVisibility, newDescription, newColor]) => {
const req: HttpTimelinePatchRequest = {};
if (newTitle !== timeline.title) {
req.title = newTitle;
@@ -65,6 +74,10 @@ const TimelinePropertyChangeDialog: React.FC<TimelinePropertyChangeDialogProps>
if (newDescription !== timeline.description) {
req.description = newDescription;
}
+ const nc = newColor ?? "#007bff";
+ if (nc !== timeline.color) {
+ req.color = nc;
+ }
return getHttpTimelineClient()
.patchTimeline(timeline.name, req)
.then(onChange);