From fa540c046d126449f77e46edd379bbc84e02d05d Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Jun 2021 00:00:54 +0800 Subject: ... --- FrontEnd/src/views/common/ConfirmDialog.tsx | 18 +++++++------- FrontEnd/src/views/common/button/Button.css | 0 FrontEnd/src/views/common/button/Button.tsx | 30 ++++++++++++++++++++++++ FrontEnd/src/views/common/button/FlatButton.tsx | 31 ++++--------------------- FrontEnd/src/views/common/button/TextButton.tsx | 31 ++++--------------------- FrontEnd/src/views/common/button/common.ts | 31 +++++++++++++++++++++++++ 6 files changed, 79 insertions(+), 62 deletions(-) create mode 100644 FrontEnd/src/views/common/button/Button.css create mode 100644 FrontEnd/src/views/common/button/Button.tsx create mode 100644 FrontEnd/src/views/common/button/common.ts (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/ConfirmDialog.tsx b/FrontEnd/src/views/common/ConfirmDialog.tsx index 72940c51..70dc83f5 100644 --- a/FrontEnd/src/views/common/ConfirmDialog.tsx +++ b/FrontEnd/src/views/common/ConfirmDialog.tsx @@ -1,8 +1,9 @@ import { convertI18nText, I18nText } from "@/common"; import React from "react"; -import { Modal, Button } from "react-bootstrap"; import { useTranslation } from "react-i18next"; +import Button from "./button/Button"; + const ConfirmDialog: React.FC<{ onClose: () => void; onConfirm: () => void; @@ -20,18 +21,19 @@ const ConfirmDialog: React.FC<{ {convertI18nText(body, t)} - + /> ); diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css new file mode 100644 index 00000000..e69de29b diff --git a/FrontEnd/src/views/common/button/Button.tsx b/FrontEnd/src/views/common/button/Button.tsx new file mode 100644 index 00000000..11710647 --- /dev/null +++ b/FrontEnd/src/views/common/button/Button.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +import { calculateProps, CommonButtonProps } from "./common"; + +import "./Button.css"; + +function _Button( + props: CommonButtonProps & { customButtonClassName?: string }, + ref: React.ForwardedRef +): React.ReactElement | null { + const { t } = useTranslation(); + + const { customButtonClassName, ...otherProps } = props; + + const { newProps, children } = calculateProps( + otherProps, + customButtonClassName ?? "cru-button", + t + ); + + return ( + + ); +} + +const Button = React.forwardRef(_Button); +export default Button; diff --git a/FrontEnd/src/views/common/button/FlatButton.tsx b/FrontEnd/src/views/common/button/FlatButton.tsx index 6351971a..266ea908 100644 --- a/FrontEnd/src/views/common/button/FlatButton.tsx +++ b/FrontEnd/src/views/common/button/FlatButton.tsx @@ -1,39 +1,16 @@ import React from "react"; -import { useTranslation } from "react-i18next"; -import classNames from "classnames"; -import { convertI18nText, I18nText } from "@/common"; -import { PaletteColorType } from "@/palette"; +import { CommonButtonProps } from "./common"; +import Button from "./Button"; import "./FlatButton.css"; function _FlatButton( - { - text, - color, - onClick, - className, - style, - }: { - text: I18nText; - color?: PaletteColorType; - onClick?: (e: React.MouseEvent) => void; - className?: string; - style?: React.CSSProperties; - }, + props: CommonButtonProps, ref: React.ForwardedRef ): React.ReactElement | null { - const { t } = useTranslation(); - return ( - + + - ); -}; - -export default LoadingButton; diff --git a/FrontEnd/src/views/common/OperationDialog.tsx b/FrontEnd/src/views/common/OperationDialog.tsx deleted file mode 100644 index ac4c51b9..00000000 --- a/FrontEnd/src/views/common/OperationDialog.tsx +++ /dev/null @@ -1,471 +0,0 @@ -import React, { useState } from "react"; -import { useTranslation } from "react-i18next"; -import { Form, Button, Modal } from "react-bootstrap"; -import { TwitterPicker } from "react-color"; -import moment from "moment"; - -import { convertI18nText, I18nText, UiLogicError } from "@/common"; - -import LoadingButton from "./LoadingButton"; - -interface DefaultErrorPromptProps { - error?: string; -} - -const DefaultErrorPrompt: React.FC = (props) => { - const { t } = useTranslation(); - - let result =

{t("operationDialog.error")}

; - - if (props.error != null) { - result = ( - <> - {result} -

{props.error}

- - ); - } - - return result; -}; - -export interface OperationDialogTextInput { - type: "text"; - label?: I18nText; - password?: boolean; - initValue?: string; - textFieldProps?: Omit< - React.InputHTMLAttributes, - "type" | "value" | "onChange" | "aria-relevant" - >; - helperText?: string; -} - -export interface OperationDialogBoolInput { - type: "bool"; - label: I18nText; - initValue?: boolean; -} - -export interface OperationDialogSelectInputOption { - value: string; - label: I18nText; - icon?: React.ReactElement; -} - -export interface OperationDialogSelectInput { - type: "select"; - label: I18nText; - options: OperationDialogSelectInputOption[]; - initValue?: string; -} - -export interface OperationDialogColorInput { - type: "color"; - label?: I18nText; - initValue?: string | null; - canBeNull?: boolean; -} - -export interface OperationDialogDateTimeInput { - type: "datetime"; - label?: I18nText; - initValue?: string; -} - -export type OperationDialogInput = - | OperationDialogTextInput - | OperationDialogBoolInput - | OperationDialogSelectInput - | OperationDialogColorInput - | OperationDialogDateTimeInput; - -interface OperationInputTypeStringToValueTypeMap { - text: string; - bool: boolean; - select: string; - color: string | null; - datetime: string; -} - -type MapOperationInputTypeStringToValueType = - Type extends keyof OperationInputTypeStringToValueTypeMap - ? OperationInputTypeStringToValueTypeMap[Type] - : never; - -type MapOperationInputInfoValueType = T extends OperationDialogInput - ? MapOperationInputTypeStringToValueType - : T; - -const initValueMapperMap: { - [T in OperationDialogInput as T["type"]]: ( - item: T - ) => MapOperationInputInfoValueType; -} = { - bool: (item) => item.initValue ?? false, - color: (item) => item.initValue ?? null, - datetime: (item) => { - if (item.initValue != null) { - return moment(item.initValue).format("YYYY-MM-DDTHH:mm:ss"); - } else { - return ""; - } - }, - select: (item) => item.initValue ?? item.options[0].value, - text: (item) => item.initValue ?? "", -}; - -type MapOperationInputInfoValueTypeList< - Tuple extends readonly OperationDialogInput[] -> = { - [Index in keyof Tuple]: MapOperationInputInfoValueType; -} & { length: Tuple["length"] }; - -export type OperationInputError = - | { - [index: number]: I18nText | null | undefined; - } - | null - | undefined; - -const isNoError = (error: OperationInputError): boolean => { - if (error == null) return true; - for (const key in error) { - if (error[key] != null) return false; - } - return true; -}; - -export interface OperationDialogProps< - TData, - OperationInputInfoList extends readonly OperationDialogInput[] -> { - open: boolean; - close: () => void; - title: I18nText | (() => React.ReactNode); - themeColor?: "danger" | "success" | string; - onProcess: ( - inputs: MapOperationInputInfoValueTypeList - ) => Promise; - inputScheme?: OperationInputInfoList; - inputValidator?: ( - inputs: MapOperationInputInfoValueTypeList - ) => OperationInputError; - inputPrompt?: I18nText | (() => React.ReactNode); - processPrompt?: () => React.ReactNode; - successPrompt?: (data: TData) => React.ReactNode; - failurePrompt?: (error: unknown) => React.ReactNode; - onSuccessAndClose?: (data: TData) => void; -} - -const OperationDialog = < - TData, - OperationInputInfoList extends readonly OperationDialogInput[] ->( - props: OperationDialogProps -): React.ReactElement => { - const inputScheme = (props.inputScheme ?? - []) as readonly OperationDialogInput[]; - - const { t } = useTranslation(); - - type Step = - | { type: "input" } - | { type: "process" } - | { - type: "success"; - data: TData; - } - | { - type: "failure"; - data: unknown; - }; - const [step, setStep] = useState({ type: "input" }); - - type ValueType = boolean | string | null | undefined; - - const [values, setValues] = useState( - inputScheme.map((item) => { - if (item.type in initValueMapperMap) { - return ( - initValueMapperMap[item.type] as ( - i: OperationDialogInput - ) => ValueType - )(item); - } else { - throw new UiLogicError("Unknown input scheme."); - } - }) - ); - const [dirtyList, setDirtyList] = useState(() => - inputScheme.map(() => false) - ); - const [inputError, setInputError] = useState(); - - const close = (): void => { - if (step.type !== "process") { - props.close(); - if (step.type === "success" && props.onSuccessAndClose) { - props.onSuccessAndClose(step.data); - } - } else { - console.log("Attempt to close modal when processing."); - } - }; - - const onConfirm = (): void => { - setStep({ type: "process" }); - props - .onProcess( - values.map((v, index) => { - if (inputScheme[index].type === "datetime" && v !== "") - return new Date(v as string).toISOString(); - else return v; - }) as unknown as MapOperationInputInfoValueTypeList - ) - .then( - (d) => { - setStep({ - type: "success", - data: d, - }); - }, - (e: unknown) => { - setStep({ - type: "failure", - data: e, - }); - } - ); - }; - - let body: React.ReactNode; - if (step.type === "input" || step.type === "process") { - const process = step.type === "process"; - - let inputPrompt = - typeof props.inputPrompt === "function" - ? props.inputPrompt() - : convertI18nText(props.inputPrompt, t); - inputPrompt =
{inputPrompt}
; - - const validate = (values: ValueType[]): boolean => { - const { inputValidator } = props; - if (inputValidator != null) { - const result = inputValidator( - values as unknown as MapOperationInputInfoValueTypeList - ); - setInputError(result); - return isNoError(result); - } - return true; - }; - - const updateValue = (index: number, newValue: ValueType): void => { - const oldValues = values; - const newValues = oldValues.slice(); - newValues[index] = newValue; - setValues(newValues); - if (dirtyList[index] === false) { - const newDirtyList = dirtyList.slice(); - newDirtyList[index] = true; - setDirtyList(newDirtyList); - } - validate(newValues); - }; - - const canProcess = isNoError(inputError); - - body = ( - <> - - {inputPrompt} - {inputScheme.map((item, index) => { - const value = values[index]; - const error: string | null = - dirtyList[index] && inputError != null - ? convertI18nText(inputError[index], t) - : null; - - if (item.type === "text") { - return ( - - {item.label && ( - {convertI18nText(item.label, t)} - )} - { - const v = e.target.value; - updateValue(index, v); - }} - isInvalid={error != null} - disabled={process} - /> - {error != null && ( - - {error} - - )} - {item.helperText && ( - {t(item.helperText)} - )} - - ); - } else if (item.type === "bool") { - return ( - - - type="checkbox" - checked={value as boolean} - onChange={(event) => { - updateValue(index, event.currentTarget.checked); - }} - label={convertI18nText(item.label, t)} - disabled={process} - /> - - ); - } else if (item.type === "select") { - return ( - - {convertI18nText(item.label, t)} - { - updateValue(index, event.target.value); - }} - disabled={process} - > - {item.options.map((option, i) => { - return ( - - ); - })} - - - ); - } else if (item.type === "color") { - return ( - - {item.canBeNull ? ( - - 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} - /> - ) : ( - {convertI18nText(item.label, t)} - )} - {value !== null && ( - updateValue(index, result.hex)} - /> - )} - - ); - } else if (item.type === "datetime") { - return ( - - {item.label && ( - {convertI18nText(item.label, t)} - )} - { - const v = e.target.value; - updateValue(index, v); - }} - isInvalid={error != null} - disabled={process} - /> - {error != null && ( - - {error} - - )} - - ); - } - })} - - - - { - setDirtyList(inputScheme.map(() => true)); - if (validate(values)) { - onConfirm(); - } - }} - > - {t("operationDialog.confirm")} - - - - ); - } else { - let content: React.ReactNode; - const result = step; - if (result.type === "success") { - content = - props.successPrompt?.(result.data) ?? t("operationDialog.success"); - if (typeof content === "string") - content =

{content}

; - } else { - content = props.failurePrompt?.(result.data) ?? ; - if (typeof content === "string") - content = ; - } - body = ( - <> - {content} - - - - - ); - } - - const title = - typeof props.title === "function" - ? props.title() - : convertI18nText(props.title, t); - - return ( - - - {title} - - {body} - - ); -}; - -export default OperationDialog; diff --git a/FrontEnd/src/views/common/ToggleIconButton.tsx b/FrontEnd/src/views/common/ToggleIconButton.tsx deleted file mode 100644 index c4d2d132..00000000 --- a/FrontEnd/src/views/common/ToggleIconButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import classnames from "classnames"; - -export interface ToggleIconButtonProps - extends React.HTMLAttributes { - state: boolean; - trueIconClassName: string; - falseIconClassName: string; -} - -const ToggleIconButton: React.FC = ({ - state, - className, - trueIconClassName, - falseIconClassName, - ...otherProps -}) => { - return ( - - ); -}; - -export default ToggleIconButton; diff --git a/FrontEnd/src/views/common/button/LoadingButton.tsx b/FrontEnd/src/views/common/button/LoadingButton.tsx new file mode 100644 index 00000000..fd1c19b3 --- /dev/null +++ b/FrontEnd/src/views/common/button/LoadingButton.tsx @@ -0,0 +1,28 @@ +import React from "react"; + +const LoadingButton: React.FC<{ loading?: boolean } & ButtonProps> = ({ + loading, + variant, + disabled, + ...otherProps +}) => { + return ( + + ); +}; + +export default LoadingButton; diff --git a/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx new file mode 100644 index 00000000..1ad52350 --- /dev/null +++ b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx @@ -0,0 +1,40 @@ +import { convertI18nText, I18nText } from "@/common"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +import Button from "../button/Button"; +import Dialog from "./Dialog"; + +const ConfirmDialog: React.FC<{ + open?: boolean; + onClose: () => void; + onConfirm: () => void; + title: I18nText; + body: I18nText; +}> = ({ open, onClose, onConfirm, title, body }) => { + const { t } = useTranslation(); + + return ( + +

{convertI18nText(title, t)}

+

{convertI18nText(body, t)}

+
+
+
+ ); +}; + +export default ConfirmDialog; diff --git a/FrontEnd/src/views/common/dailog/Dialog.css b/FrontEnd/src/views/common/dailog/Dialog.css new file mode 100644 index 00000000..e69de29b diff --git a/FrontEnd/src/views/common/dailog/Dialog.tsx b/FrontEnd/src/views/common/dailog/Dialog.tsx new file mode 100644 index 00000000..5a3902c4 --- /dev/null +++ b/FrontEnd/src/views/common/dailog/Dialog.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +export interface DialogProps { + onClose: () => void; + open?: boolean; + children?: React.ReactNode; +} + +export default function Dialog(props: DialogProps): React.ReactElement | null { + const { open, onClose, children } = props; + + return
{children}
; +} diff --git a/FrontEnd/src/views/common/dailog/OperationDialog.tsx b/FrontEnd/src/views/common/dailog/OperationDialog.tsx new file mode 100644 index 00000000..129e85d5 --- /dev/null +++ b/FrontEnd/src/views/common/dailog/OperationDialog.tsx @@ -0,0 +1,472 @@ +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { TwitterPicker } from "react-color"; +import moment from "moment"; + +import { convertI18nText, I18nText, UiLogicError } from "@/common"; + +import Button from "../button/Button"; +import LoadingButton from "../button/LoadingButton"; +import Dialog from "./Dialog"; + +interface DefaultErrorPromptProps { + error?: string; +} + +const DefaultErrorPrompt: React.FC = (props) => { + const { t } = useTranslation(); + + let result =

{t("operationDialog.error")}

; + + if (props.error != null) { + result = ( + <> + {result} +

{props.error}

+ + ); + } + + return result; +}; + +export interface OperationDialogTextInput { + type: "text"; + label?: I18nText; + password?: boolean; + initValue?: string; + textFieldProps?: Omit< + React.InputHTMLAttributes, + "type" | "value" | "onChange" | "aria-relevant" + >; + helperText?: string; +} + +export interface OperationDialogBoolInput { + type: "bool"; + label: I18nText; + initValue?: boolean; +} + +export interface OperationDialogSelectInputOption { + value: string; + label: I18nText; + icon?: React.ReactElement; +} + +export interface OperationDialogSelectInput { + type: "select"; + label: I18nText; + options: OperationDialogSelectInputOption[]; + initValue?: string; +} + +export interface OperationDialogColorInput { + type: "color"; + label?: I18nText; + initValue?: string | null; + canBeNull?: boolean; +} + +export interface OperationDialogDateTimeInput { + type: "datetime"; + label?: I18nText; + initValue?: string; +} + +export type OperationDialogInput = + | OperationDialogTextInput + | OperationDialogBoolInput + | OperationDialogSelectInput + | OperationDialogColorInput + | OperationDialogDateTimeInput; + +interface OperationInputTypeStringToValueTypeMap { + text: string; + bool: boolean; + select: string; + color: string | null; + datetime: string; +} + +type MapOperationInputTypeStringToValueType = + Type extends keyof OperationInputTypeStringToValueTypeMap + ? OperationInputTypeStringToValueTypeMap[Type] + : never; + +type MapOperationInputInfoValueType = T extends OperationDialogInput + ? MapOperationInputTypeStringToValueType + : T; + +const initValueMapperMap: { + [T in OperationDialogInput as T["type"]]: ( + item: T + ) => MapOperationInputInfoValueType; +} = { + bool: (item) => item.initValue ?? false, + color: (item) => item.initValue ?? null, + datetime: (item) => { + if (item.initValue != null) { + return moment(item.initValue).format("YYYY-MM-DDTHH:mm:ss"); + } else { + return ""; + } + }, + select: (item) => item.initValue ?? item.options[0].value, + text: (item) => item.initValue ?? "", +}; + +type MapOperationInputInfoValueTypeList< + Tuple extends readonly OperationDialogInput[] +> = { + [Index in keyof Tuple]: MapOperationInputInfoValueType; +} & { length: Tuple["length"] }; + +export type OperationInputError = + | { + [index: number]: I18nText | null | undefined; + } + | null + | undefined; + +const isNoError = (error: OperationInputError): boolean => { + if (error == null) return true; + for (const key in error) { + if (error[key] != null) return false; + } + return true; +}; + +export interface OperationDialogProps< + TData, + OperationInputInfoList extends readonly OperationDialogInput[] +> { + open: boolean; + onClose: () => void; + title: I18nText | (() => React.ReactNode); + themeColor?: "danger" | "success" | string; + onProcess: ( + inputs: MapOperationInputInfoValueTypeList + ) => Promise; + inputScheme?: OperationInputInfoList; + inputValidator?: ( + inputs: MapOperationInputInfoValueTypeList + ) => OperationInputError; + inputPrompt?: I18nText | (() => React.ReactNode); + processPrompt?: () => React.ReactNode; + successPrompt?: (data: TData) => React.ReactNode; + failurePrompt?: (error: unknown) => React.ReactNode; + onSuccessAndClose?: (data: TData) => void; +} + +const OperationDialog = < + TData, + OperationInputInfoList extends readonly OperationDialogInput[] +>( + props: OperationDialogProps +): React.ReactElement => { + const inputScheme = (props.inputScheme ?? + []) as readonly OperationDialogInput[]; + + const { t } = useTranslation(); + + type Step = + | { type: "input" } + | { type: "process" } + | { + type: "success"; + data: TData; + } + | { + type: "failure"; + data: unknown; + }; + const [step, setStep] = useState({ type: "input" }); + + type ValueType = boolean | string | null | undefined; + + const [values, setValues] = useState( + inputScheme.map((item) => { + if (item.type in initValueMapperMap) { + return ( + initValueMapperMap[item.type] as ( + i: OperationDialogInput + ) => ValueType + )(item); + } else { + throw new UiLogicError("Unknown input scheme."); + } + }) + ); + const [dirtyList, setDirtyList] = useState(() => + inputScheme.map(() => false) + ); + const [inputError, setInputError] = useState(); + + const close = (): void => { + if (step.type !== "process") { + props.onClose(); + if (step.type === "success" && props.onSuccessAndClose) { + props.onSuccessAndClose(step.data); + } + } else { + console.log("Attempt to close modal when processing."); + } + }; + + const onConfirm = (): void => { + setStep({ type: "process" }); + props + .onProcess( + values.map((v, index) => { + if (inputScheme[index].type === "datetime" && v !== "") + return new Date(v as string).toISOString(); + else return v; + }) as unknown as MapOperationInputInfoValueTypeList + ) + .then( + (d) => { + setStep({ + type: "success", + data: d, + }); + }, + (e: unknown) => { + setStep({ + type: "failure", + data: e, + }); + } + ); + }; + + let body: React.ReactNode; + if (step.type === "input" || step.type === "process") { + const process = step.type === "process"; + + let inputPrompt = + typeof props.inputPrompt === "function" + ? props.inputPrompt() + : convertI18nText(props.inputPrompt, t); + inputPrompt =
{inputPrompt}
; + + const validate = (values: ValueType[]): boolean => { + const { inputValidator } = props; + if (inputValidator != null) { + const result = inputValidator( + values as unknown as MapOperationInputInfoValueTypeList + ); + setInputError(result); + return isNoError(result); + } + return true; + }; + + const updateValue = (index: number, newValue: ValueType): void => { + const oldValues = values; + const newValues = oldValues.slice(); + newValues[index] = newValue; + setValues(newValues); + if (dirtyList[index] === false) { + const newDirtyList = dirtyList.slice(); + newDirtyList[index] = true; + setDirtyList(newDirtyList); + } + validate(newValues); + }; + + const canProcess = isNoError(inputError); + + body = ( + <> +
+ {inputPrompt} + {inputScheme.map((item, index) => { + const value = values[index]; + const error: string | null = + dirtyList[index] && inputError != null + ? convertI18nText(inputError[index], t) + : null; + + if (item.type === "text") { + return ( + + {item.label && ( + {convertI18nText(item.label, t)} + )} + { + const v = e.target.value; + updateValue(index, v); + }} + isInvalid={error != null} + disabled={process} + /> + {error != null && ( + + {error} + + )} + {item.helperText && ( + {t(item.helperText)} + )} + + ); + } else if (item.type === "bool") { + return ( + + + type="checkbox" + checked={value as boolean} + onChange={(event) => { + updateValue(index, event.currentTarget.checked); + }} + label={convertI18nText(item.label, t)} + disabled={process} + /> + + ); + } else if (item.type === "select") { + return ( + + {convertI18nText(item.label, t)} + { + updateValue(index, event.target.value); + }} + disabled={process} + > + {item.options.map((option, i) => { + return ( + + ); + })} + + + ); + } else if (item.type === "color") { + return ( + + {item.canBeNull ? ( + + 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} + /> + ) : ( + {convertI18nText(item.label, t)} + )} + {value !== null && ( + updateValue(index, result.hex)} + /> + )} + + ); + } else if (item.type === "datetime") { + return ( + + {item.label && ( + {convertI18nText(item.label, t)} + )} + { + const v = e.target.value; + updateValue(index, v); + }} + isInvalid={error != null} + disabled={process} + /> + {error != null && ( + + {error} + + )} + + ); + } + })} +
+
+
+ + ); + } else { + let content: React.ReactNode; + const result = step; + if (result.type === "success") { + content = + props.successPrompt?.(result.data) ?? t("operationDialog.success"); + if (typeof content === "string") + content =

{content}

; + } else { + content = props.failurePrompt?.(result.data) ?? ; + if (typeof content === "string") + content = ; + } + body = ( + <> +
{content}
+
+
+ + ); + } + + const title = + typeof props.title === "function" + ? props.title() + : convertI18nText(props.title, t); + + return ( + +

+ {title} +

+ {body} +
+ ); +}; + +export default OperationDialog; diff --git a/FrontEnd/src/views/login/index.tsx b/FrontEnd/src/views/login/index.tsx index 55bd2f8c..89aeb47a 100644 --- a/FrontEnd/src/views/login/index.tsx +++ b/FrontEnd/src/views/login/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import { useUser, userService } from "@/services/user"; import AppBar from "../common/AppBar"; -import LoadingButton from "../common/LoadingButton"; +import LoadingButton from "../common/button/LoadingButton"; import "./index.css"; diff --git a/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx b/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx index 1baab1cc..0bf51c21 100644 --- a/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx +++ b/FrontEnd/src/views/settings/ChangeAvatarDialog.tsx @@ -10,6 +10,7 @@ import { getHttpUserClient } from "@/http/user"; import ImageCropper, { Clip, applyClipToImage } from "../common/ImageCropper"; import Button from "../common/button/Button"; +import Dialog from "../common/dailog/Dialog"; export interface ChangeAvatarDialogProps { open: boolean; @@ -159,29 +160,27 @@ const ChangeAvatarDialog: React.FC = (props) => { }; return ( - - - {t("settings.dialogChangeAvatar.title")} - + +

{t("settings.dialogChangeAvatar.title")}

{(() => { if (state === "select") { return ( <> - +
{t("settings.dialogChangeAvatar.prompt.select")}
- - +
+
); } else if (state === "crop") { @@ -190,7 +189,7 @@ const ChangeAvatarDialog: React.FC = (props) => { } return ( <> - +
= (props) => {
{t("settings.dialogChangeAvatar.prompt.crop")}
- - +
+
); } else if (state === "processcrop") { return ( <> - +
{t("settings.dialogChangeAvatar.prompt.processingCrop")}
- - +
+
); } else if (state === "preview") { return ( <> - +
{createPreviewRow()}
{t("settings.dialogChangeAvatar.prompt.preview")}
- - +
+
); } else if (state === "uploading") { return ( <> - +
{createPreviewRow()}
{t("settings.dialogChangeAvatar.prompt.uploading")}
- - +
+
); } else if (state === "success") { return ( <> - +
{t("operationDialog.success")}
- - +
+
); } else { return ( <> - +
{createPreviewRow()}
{trueMessage}
- - +
+
); } })()} - +
); }; diff --git a/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx b/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx index 4b44cdd6..605796ca 100644 --- a/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx +++ b/FrontEnd/src/views/settings/ChangeNicknameDialog.tsx @@ -2,7 +2,7 @@ import { getHttpUserClient } from "@/http/user"; import { useUserLoggedIn } from "@/services/user"; import React from "react"; -import OperationDialog from "../common/OperationDialog"; +import OperationDialog from "../common/dailog/OperationDialog"; export interface ChangeNicknameDialogProps { open: boolean; @@ -24,7 +24,7 @@ const ChangeNicknameDialog: React.FC = (props) => { nickname: newNickname, }); }} - close={props.close} + onClose={props.close} /> ); }; diff --git a/FrontEnd/src/views/settings/ChangePasswordDialog.tsx b/FrontEnd/src/views/settings/ChangePasswordDialog.tsx index 21eeeb09..944fdaed 100644 --- a/FrontEnd/src/views/settings/ChangePasswordDialog.tsx +++ b/FrontEnd/src/views/settings/ChangePasswordDialog.tsx @@ -3,7 +3,7 @@ import { useHistory } from "react-router"; import { userService } from "@/services/user"; -import OperationDialog from "../common/OperationDialog"; +import OperationDialog from "../common/dailog/OperationDialog"; export interface ChangePasswordDialogProps { open: boolean; @@ -55,7 +55,7 @@ const ChangePasswordDialog: React.FC = (props) => { await userService.changePassword(oldPassword, newPassword); setRedirect(true); }} - close={() => { + onClose={() => { props.close(); if (redirect) { history.push("/login"); diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 005da933..0e43cb6e 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -9,7 +9,7 @@ import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import FlatButton from "../common/button/FlatButton"; import TabPages from "../common/TabPages"; import TimelinePostBuilder from "@/services/TimelinePostBuilder"; -import ConfirmDialog from "../common/ConfirmDialog"; +import ConfirmDialog from "../common/dailog/ConfirmDialog"; export interface MarkdownPostEditProps { timeline: string; diff --git a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx index 001e52d7..988124b6 100644 --- a/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx +++ b/FrontEnd/src/views/timeline-common/PostPropertyChangeDialog.tsx @@ -2,7 +2,7 @@ import React from "react"; import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; -import OperationDialog from "../common/OperationDialog"; +import OperationDialog from "../common/dailog/OperationDialog"; function PostPropertyChangeDialog(props: { onClose: () => void; @@ -14,7 +14,7 @@ function PostPropertyChangeDialog(props: { return ( ] as const } open={props.open} - close={props.close} + onClose={props.close} onProcess={([newTitle, newVisibility, newDescription, newColor]) => { const req: HttpTimelinePatchRequest = {}; if (newTitle !== timeline.title) { diff --git a/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx b/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx index dbca62ca..aedf4f29 100644 --- a/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx +++ b/FrontEnd/src/views/timeline/TimelineDeleteDialog.tsx @@ -4,7 +4,7 @@ import { Trans } from "react-i18next"; import { getHttpTimelineClient, HttpTimelineInfo } from "@/http/timeline"; -import OperationDialog from "../common/OperationDialog"; +import OperationDialog from "../common/dailog/OperationDialog"; interface TimelineDeleteDialog { timeline: HttpTimelineInfo; @@ -20,7 +20,7 @@ const TimelineDeleteDialog: React.FC = (props) => { return ( { -- cgit v1.2.3 From b468fd9a0119b97d1ecc1090a028975e917aa75f Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Jun 2021 18:50:27 +0800 Subject: ... --- FrontEnd/src/views/common/SearchInput.tsx | 7 ++-- FrontEnd/src/views/login/index.tsx | 5 +-- FrontEnd/src/views/settings/index.tsx | 40 ++++------------------ .../src/views/timeline-common/MarkdownPostEdit.tsx | 5 ++- .../src/views/timeline-common/TimelinePostEdit.tsx | 5 ++- 5 files changed, 15 insertions(+), 47 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/SearchInput.tsx b/FrontEnd/src/views/common/SearchInput.tsx index ccb6dad6..79eb2732 100644 --- a/FrontEnd/src/views/common/SearchInput.tsx +++ b/FrontEnd/src/views/common/SearchInput.tsx @@ -38,14 +38,15 @@ const SearchInput: React.FC = (props) => { ); return ( -
- = (props) => { )} - + ); }; diff --git a/FrontEnd/src/views/login/index.tsx b/FrontEnd/src/views/login/index.tsx index 89aeb47a..6c0aaf67 100644 --- a/FrontEnd/src/views/login/index.tsx +++ b/FrontEnd/src/views/login/index.tsx @@ -80,8 +80,7 @@ const LoginPage: React.FC = (_) => { return (

{t("welcome")}

-
- +
{t("user.username")} { {t("login.emptyUsername")} )} - - {t("user.password")} void; - onConfirm: () => void; -}> = ({ onClose, onConfirm }) => { - const { t } = useTranslation(); - - return ( - - - - {t("settings.dialogConfirmLogout.title")} - - - {t("settings.dialogConfirmLogout.prompt")} - -
- { void i18n.changeLanguage(e.target.value); @@ -109,7 +79,7 @@ const SettingsPage: React.FC = (_) => { > - +
@@ -120,7 +90,9 @@ const SettingsPage: React.FC = (_) => { return setDialog(null)} />; case "logout": return ( - setDialog(null)} onConfirm={() => { void userService.logout().then(() => { diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 0e43cb6e..6cb64dd3 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -126,8 +126,7 @@ const MarkdownPostEdit: React.FC = ({ id: "text", tabText: "edit", page: ( - { @@ -161,7 +160,7 @@ const MarkdownPostEdit: React.FC = ({ /> ))} - ) => { diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx index 06e508e6..5e59bee4 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx @@ -35,8 +35,7 @@ const TimelinePostEditText: React.FC = (props) => { const { text, disabled, onChange, className, style } = props; return ( - { @@ -80,7 +79,7 @@ const TimelinePostEditImage: React.FC = (props) => { return ( <> - Date: Sat, 26 Jun 2021 19:13:19 +0800 Subject: ... --- FrontEnd/src/views/admin/UserAdmin.tsx | 9 +- FrontEnd/src/views/common/Menu.tsx | 26 ++--- FrontEnd/src/views/common/SearchInput.tsx | 13 +-- FrontEnd/src/views/common/Spinner.tsx | 13 +++ FrontEnd/src/views/common/TabPages.tsx | 20 +--- FrontEnd/src/views/common/alert/AlertHost.tsx | 11 +- FrontEnd/src/views/common/button/LoadingButton.tsx | 23 ++--- .../src/views/common/dailog/OperationDialog.tsx | 71 ++++++------- FrontEnd/src/views/login/index.tsx | 114 ++++++++++----------- .../src/views/timeline-common/MarkdownPostEdit.tsx | 8 +- .../src/views/timeline-common/TimelineMember.tsx | 21 ++-- .../views/timeline-common/TimelinePageTemplate.tsx | 1 - .../TimelinePostDeleteConfirmDialog.tsx | 40 -------- .../src/views/timeline-common/TimelinePostEdit.tsx | 1 - .../src/views/timeline-common/TimelinePostView.tsx | 6 +- 15 files changed, 142 insertions(+), 235 deletions(-) create mode 100644 FrontEnd/src/views/common/Spinner.tsx delete mode 100644 FrontEnd/src/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx index 125219c3..481db1cc 100644 --- a/FrontEnd/src/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -15,6 +15,7 @@ import { import { Trans, useTranslation } from "react-i18next"; import Button from "../common/button/Button"; import TextButton from "../common/button/TextButton"; +import Spinner from "../common/Spinner"; interface DialogProps { open: boolean; @@ -203,7 +204,7 @@ const UserItem: React.FC = ({ user, on }) => { const [editMaskVisible, setEditMaskVisible] = React.useState(false); return ( - +
setEditMaskVisible(true)} @@ -242,7 +243,7 @@ const UserItem: React.FC = ({ user, on }) => { onClick={on[kDelete]} />
-
+ ); }; @@ -251,8 +252,6 @@ interface UserAdminProps { } const UserAdmin: React.FC = () => { - const { t } = useTranslation(); - type DialogInfo = | null | { @@ -390,7 +389,7 @@ const UserAdmin: React.FC = () => { ); } else { - return ; + return ; } }; diff --git a/FrontEnd/src/views/common/Menu.tsx b/FrontEnd/src/views/common/Menu.tsx index ae73a331..a5d2ec2c 100644 --- a/FrontEnd/src/views/common/Menu.tsx +++ b/FrontEnd/src/views/common/Menu.tsx @@ -1,9 +1,9 @@ import React from "react"; import classnames from "classnames"; -import { OverlayTrigger, OverlayTriggerProps, Popover } from "react-bootstrap"; import { useTranslation } from "react-i18next"; -import { BootstrapThemeColor, convertI18nText, I18nText } from "@/common"; +import { convertI18nText, I18nText } from "@/common"; +import { PaletteColorType } from "@/palette"; export type MenuItem = | { @@ -13,7 +13,7 @@ export type MenuItem = type: "button"; text: I18nText; iconClassName?: string; - color?: BootstrapThemeColor; + color?: PaletteColorType; onClick: () => void; }; @@ -67,26 +67,14 @@ export default Menu; export interface PopupMenuProps { items: MenuItems; - children: OverlayTriggerProps["children"]; + children: React.ReactElement; } export const PopupMenu: React.FC = ({ items, children }) => { const [show, setShow] = React.useState(false); const toggle = (): void => setShow(!show); - return ( - - setShow(false)} /> - - } - show={show} - onToggle={toggle} - > - {children} - - ); + // TODO: + + return setShow(false)} />; }; diff --git a/FrontEnd/src/views/common/SearchInput.tsx b/FrontEnd/src/views/common/SearchInput.tsx index 79eb2732..fff11b95 100644 --- a/FrontEnd/src/views/common/SearchInput.tsx +++ b/FrontEnd/src/views/common/SearchInput.tsx @@ -1,7 +1,8 @@ import React, { useCallback } from "react"; import classnames from "classnames"; import { useTranslation } from "react-i18next"; -import { Spinner, Form, Button } from "react-bootstrap"; + +import LoadingButton from "./button/LoadingButton"; export interface SearchInputProps { value: string; @@ -64,13 +65,9 @@ const SearchInput: React.FC = (props) => { "flex-shrink-0" )} > - {props.loading ? ( - - ) : ( - - )} + + {props.buttonText ?? t("search")} + ); diff --git a/FrontEnd/src/views/common/Spinner.tsx b/FrontEnd/src/views/common/Spinner.tsx new file mode 100644 index 00000000..783c9be2 --- /dev/null +++ b/FrontEnd/src/views/common/Spinner.tsx @@ -0,0 +1,13 @@ +import { PaletteColorType } from "@/palette"; +import React from "react"; + +export interface SpinnerProps { + size?: "sm" | "md" | "lg" | number; + color?: PaletteColorType; +} + +export default function Spinner( + props: SpinnerProps +): React.ReactElement | null { + return ; +} diff --git a/FrontEnd/src/views/common/TabPages.tsx b/FrontEnd/src/views/common/TabPages.tsx index 2b1d91cb..b7a9fb36 100644 --- a/FrontEnd/src/views/common/TabPages.tsx +++ b/FrontEnd/src/views/common/TabPages.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Nav } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { convertI18nText, I18nText, UiLogicError } from "@/common"; @@ -31,6 +30,8 @@ const TabPages: React.FC = ({ pageContainerClassName, pageContainerStyle, }) => { + // TODO: + if (pages.length === 0) { throw new UiLogicError("Page list can't be empty."); } @@ -47,23 +48,6 @@ const TabPages: React.FC = ({ return (
-
{currentPage.page}
diff --git a/FrontEnd/src/views/common/alert/AlertHost.tsx b/FrontEnd/src/views/common/alert/AlertHost.tsx index 949be7ed..21b9882d 100644 --- a/FrontEnd/src/views/common/alert/AlertHost.tsx +++ b/FrontEnd/src/views/common/alert/AlertHost.tsx @@ -1,7 +1,6 @@ import React from "react"; import without from "lodash/without"; import { useTranslation } from "react-i18next"; -import { Alert } from "react-bootstrap"; import { alertService, @@ -52,13 +51,7 @@ export const AutoCloseAlert: React.FC = (props) => { }; return ( - +
{(() => { const { message } = alert; if (typeof message === "function") { @@ -66,7 +59,7 @@ export const AutoCloseAlert: React.FC = (props) => { return ; } else return convertI18nText(message, t); })()} - +
); }; diff --git a/FrontEnd/src/views/common/button/LoadingButton.tsx b/FrontEnd/src/views/common/button/LoadingButton.tsx index fd1c19b3..aee83aa2 100644 --- a/FrontEnd/src/views/common/button/LoadingButton.tsx +++ b/FrontEnd/src/views/common/button/LoadingButton.tsx @@ -1,26 +1,19 @@ import React from "react"; -const LoadingButton: React.FC<{ loading?: boolean } & ButtonProps> = ({ +import { CommonButtonProps } from "./common"; +import Button from "./Button"; +import Spinner from "../Spinner"; + +const LoadingButton: React.FC<{ loading?: boolean } & CommonButtonProps> = ({ loading, - variant, disabled, + color, ...otherProps }) => { return ( - ); }; diff --git a/FrontEnd/src/views/common/dailog/OperationDialog.tsx b/FrontEnd/src/views/common/dailog/OperationDialog.tsx index 129e85d5..1e765276 100644 --- a/FrontEnd/src/views/common/dailog/OperationDialog.tsx +++ b/FrontEnd/src/views/common/dailog/OperationDialog.tsx @@ -5,6 +5,8 @@ import moment from "moment"; import { convertI18nText, I18nText, UiLogicError } from "@/common"; +import { PaletteColorType } from "@/palette"; + import Button from "../button/Button"; import LoadingButton from "../button/LoadingButton"; import Dialog from "./Dialog"; @@ -144,7 +146,7 @@ export interface OperationDialogProps< open: boolean; onClose: () => void; title: I18nText | (() => React.ReactNode); - themeColor?: "danger" | "success" | string; + themeColor?: PaletteColorType; onProcess: ( inputs: MapOperationInputInfoValueTypeList ) => Promise; @@ -290,50 +292,42 @@ const OperationDialog = < if (item.type === "text") { return ( - +
{item.label && ( - {convertI18nText(item.label, t)} + )} - { const v = e.target.value; updateValue(index, v); }} - isInvalid={error != null} disabled={process} /> - {error != null && ( - - {error} - - )} - {item.helperText && ( - {t(item.helperText)} - )} - + {error != null &&
{error}
} + {item.helperText &&
{t(item.helperText)}
} +
); } else if (item.type === "bool") { return ( - - +
+ { updateValue(index, event.currentTarget.checked); }} - label={convertI18nText(item.label, t)} disabled={process} /> - + +
); } else if (item.type === "select") { return ( - - {convertI18nText(item.label, t)} - + + +
); } else if (item.type === "color") { return ( - +
{item.canBeNull ? ( - + { @@ -365,42 +359,35 @@ const OperationDialog = < updateValue(index, null); } }} - label={convertI18nText(item.label, t)} disabled={process} /> - ) : ( - {convertI18nText(item.label, t)} - )} + ) : null} + {value !== null && ( updateValue(index, result.hex)} /> )} - +
); } else if (item.type === "datetime") { return ( - +
{item.label && ( - {convertI18nText(item.label, t)} + )} - { const v = e.target.value; updateValue(index, v); }} - isInvalid={error != null} disabled={process} /> - {error != null && ( - - {error} - - )} - + {error != null &&
{error}
} +
); } })} @@ -412,7 +399,7 @@ const OperationDialog = < onClick={close} /> { diff --git a/FrontEnd/src/views/login/index.tsx b/FrontEnd/src/views/login/index.tsx index 6c0aaf67..6d70c64a 100644 --- a/FrontEnd/src/views/login/index.tsx +++ b/FrontEnd/src/views/login/index.tsx @@ -81,67 +81,59 @@ const LoginPage: React.FC = (_) => {

{t("welcome")}

- {t("user.username")} - { - setUsername(e.target.value); - setUsernameDirty(true); - }} - value={username} - isInvalid={usernameDirty && username === ""} - /> - {usernameDirty && username === "" && ( - - {t("login.emptyUsername")} - - )} - {t("user.password")} - { - setPassword(e.target.value); - setPasswordDirty(true); - }} - value={password} - onKeyDown={onEnterPressInPassword} - isInvalid={passwordDirty && password === ""} - /> - {passwordDirty && password === "" && ( - - {t("login.emptyPassword")} - - )} - - - - id="remember-me" - type="checkbox" - checked={rememberMe} - onChange={(e) => { - setRememberMe(e.currentTarget.checked); - }} - label={t("user.rememberMe")} - /> - - {error ?

{t(error)}

: null} -
- { - submit(); - e.preventDefault(); - }} - disabled={username === "" || password === "" ? true : undefined} - > - {t("user.login")} - -
- + + { + setUsername(e.target.value); + setUsernameDirty(true); + }} + value={username} + /> + {usernameDirty && username === "" && ( +
{t("login.emptyUsername")}
+ )} + + { + setPassword(e.target.value); + setPasswordDirty(true); + }} + value={password} + onKeyDown={onEnterPressInPassword} + /> + {passwordDirty && password === "" && ( +
{t("login.emptyPassword")}
+ )} +
+
+ { + setRememberMe(e.currentTarget.checked); + }} + /> + +
+ {error ?

{t(error)}

: null} +
+ { + submit(); + e.preventDefault(); + }} + disabled={username === "" || password === "" ? true : undefined} + > + {t("user.login")} + +
); }; diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 6cb64dd3..9f0753b9 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -1,15 +1,17 @@ +/* eslint-disable react/jsx-no-undef */ import React from "react"; import classnames from "classnames"; -import { Form, Spinner } from "react-bootstrap"; import { useTranslation } from "react-i18next"; import { Prompt } from "react-router"; import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; +import TimelinePostBuilder from "@/services/TimelinePostBuilder"; + import FlatButton from "../common/button/FlatButton"; import TabPages from "../common/TabPages"; -import TimelinePostBuilder from "@/services/TimelinePostBuilder"; import ConfirmDialog from "../common/dailog/ConfirmDialog"; +import Spinner from "../common/Spinner"; export interface MarkdownPostEditProps { timeline: string; @@ -102,7 +104,7 @@ const MarkdownPostEdit: React.FC = ({ pageContainerClassName="py-2" actions={ process ? ( - + ) : ( <> void; }> = ({ user, add, onAction }) => { - const { t } = useTranslation(); - return ( - +
@@ -42,7 +41,7 @@ const TimelineMemberItem: React.FC<{
) : null}
- +
); }; @@ -109,7 +108,7 @@ const TimelineMemberUserSearch: React.FC<{ return
{t("timeline.member.noUserAvailableToAdd")}
; } else { return ( - +
{users.map((user) => ( ))} - +
); } } else if (userSearchState.type === "error") { @@ -152,7 +151,7 @@ const TimelineMember: React.FC = (props) => { return (
- +
{members.map((member, index) => ( = (props) => { } /> ))} - +
{timeline.manageable ? ( ) : null} @@ -187,8 +186,8 @@ export const TimelineMemberDialog: React.FC = ( props ) => { return ( - + - + ); }; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx index d05f18d4..ea6e8d40 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageTemplate.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Container } from "react-bootstrap"; import { HubConnectionState } from "@microsoft/signalr"; import { HttpTimelineInfo } from "@/http/timeline"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx b/FrontEnd/src/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx deleted file mode 100644 index e04bb7e1..00000000 --- a/FrontEnd/src/views/timeline-common/TimelinePostDeleteConfirmDialog.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; - -import Button from "../common/button/Button"; - -const TimelinePostDeleteConfirmDialog: React.FC<{ - onClose: () => void; - onConfirm: () => void; -}> = ({ onClose, onConfirm }) => { - const { t } = useTranslation(); - - return ( - - - - {t("timeline.post.deleteDialog.title")} - - - {t("timeline.post.deleteDialog.prompt")} - - - - - ); -}; - -export default TimelinePostDeleteConfirmDialog; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx index 5e59bee4..13aacb54 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx @@ -265,7 +265,6 @@ const TimelinePostEdit: React.FC = (props) => {
= (props) => { ) : null} {dialog === "delete" ? ( - { setDialog(null); setOperationMaskVisible(false); -- cgit v1.2.3 From c3f17f1dd1099c244e36d09b14c3e131d703830e Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Jun 2021 19:54:24 +0800 Subject: ... --- FrontEnd/index.html | 1 + FrontEnd/src/index.css | 109 ++++++++++++++++----- FrontEnd/src/index.tsx | 4 - FrontEnd/src/palette.ts | 51 ++++++++-- FrontEnd/src/views/admin/AdminNav.tsx | 50 ++++------ FrontEnd/src/views/center/TimelineBoard.tsx | 4 +- FrontEnd/src/views/center/index.tsx | 2 +- FrontEnd/src/views/common/AppBar.css | 90 +++++++++++++++++ FrontEnd/src/views/common/AppBar.tsx | 2 +- FrontEnd/src/views/common/FullPage.tsx | 39 -------- FrontEnd/src/views/common/LoadingPage.tsx | 5 +- FrontEnd/src/views/common/SearchInput.css | 4 + FrontEnd/src/views/common/SearchInput.tsx | 2 + FrontEnd/src/views/common/TabPages.tsx | 58 ----------- FrontEnd/src/views/common/alert/alert.css | 4 + FrontEnd/src/views/common/alert/alert.sass | 15 --- FrontEnd/src/views/common/button/Button.css | 72 ++++++++++++++ FrontEnd/src/views/common/button/Button.tsx | 9 +- FrontEnd/src/views/common/button/FlatButton.css | 8 +- FrontEnd/src/views/common/button/TextButton.css | 8 +- .../src/views/common/dailog/FullPageDialog.tsx | 39 ++++++++ FrontEnd/src/views/common/index.css | 95 +----------------- FrontEnd/src/views/common/tab/TabPages.tsx | 58 +++++++++++ FrontEnd/src/views/common/tab/Tabs.tsx | 19 ++++ .../src/views/timeline-common/MarkdownPostEdit.tsx | 2 +- .../timeline-common/TimelinePageCardTemplate.tsx | 6 +- FrontEnd/src/views/timeline-common/index.css | 6 +- 27 files changed, 465 insertions(+), 297 deletions(-) create mode 100644 FrontEnd/src/views/common/AppBar.css delete mode 100644 FrontEnd/src/views/common/FullPage.tsx create mode 100644 FrontEnd/src/views/common/SearchInput.css delete mode 100644 FrontEnd/src/views/common/TabPages.tsx create mode 100644 FrontEnd/src/views/common/alert/alert.css delete mode 100644 FrontEnd/src/views/common/alert/alert.sass create mode 100644 FrontEnd/src/views/common/dailog/FullPageDialog.tsx create mode 100644 FrontEnd/src/views/common/tab/TabPages.tsx create mode 100644 FrontEnd/src/views/common/tab/Tabs.tsx (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/index.html b/FrontEnd/index.html index 87e19743..ccd6a21b 100644 --- a/FrontEnd/index.html +++ b/FrontEnd/index.html @@ -24,6 +24,7 @@
+
diff --git a/FrontEnd/src/index.css b/FrontEnd/src/index.css index 3ca9f10c..d4efabed 100644 --- a/FrontEnd/src/index.css +++ b/FrontEnd/src/index.css @@ -1,36 +1,101 @@ -@import url("./bootstrap-grid.css"); +@import "bootstrap/dist/css/bootstrap-reboot.css"; +@import "bootstrap/dist/css/bootstrap-grid.css"; +@import "bootstrap-icons/font/bootstrap-icons.css"; :root { --tl-background-color: #f8f9fa; --tl-primary-color: rgb(0, 123, 255); - --tl-primary-inactive-color: rgb(26, 136, 255); - --tl-primary-lighter-color: rgb(26, 136, 255); - --tl-primary-darker-color: rgb(0, 111, 230); + --tl-primary-l1-color: rgb(26, 136, 255); + --tl-primary-l2-color: rgb(51, 149, 255); + --tl-primary-l3-color: rgb(77, 163, 255); + --tl-primary-d1-color: rgb(0, 111, 230); + --tl-primary-d2-color: rgb(0, 98, 204); + --tl-primary-d3-color: rgb(0, 86, 179); + --tl-primary-f1-color: rgb(0, 111, 230); + --tl-primary-f2-color: rgb(0, 98, 204); + --tl-primary-f3-color: rgb(0, 86, 179); + --tl-primary-r1-color: rgb(26, 136, 255); + --tl-primary-r2-color: rgb(51, 149, 255); + --tl-primary-r3-color: rgb(77, 163, 255); --tl-primary-enhance-color: rgb(77, 163, 255); - --tl-primary-enhance-inactive-color: rgb(43, 145, 255); - --tl-primary-enhance-lighter-color: rgb(94, 172, 255); - --tl-primary-enhance-darker-color: rgb(43, 145, 255); + --tl-primary-enhance-l1-color: rgb(94, 172, 255); + --tl-primary-enhance-l2-color: rgb(112, 181, 255); + --tl-primary-enhance-l3-color: rgb(130, 190, 255); + --tl-primary-enhance-d1-color: rgb(43, 145, 255); + --tl-primary-enhance-d2-color: rgb(10, 128, 255); + --tl-primary-enhance-d3-color: rgb(0, 112, 232); + --tl-primary-enhance-f1-color: rgb(94, 172, 255); + --tl-primary-enhance-f2-color: rgb(112, 181, 255); + --tl-primary-enhance-f3-color: rgb(130, 190, 255); + --tl-primary-enhance-r1-color: rgb(43, 145, 255); + --tl-primary-enhance-r2-color: rgb(10, 128, 255); + --tl-primary-enhance-r3-color: rgb(0, 112, 232); --tl-secondary-color: rgb(255, 0, 250); - --tl-secondary-inactive-color: rgb(255, 26, 251); - --tl-secondary-lighter-color: rgb(255, 26, 251); - --tl-secondary-darker-color: rgb(230, 0, 225); + --tl-secondary-l1-color: rgb(255, 26, 251); + --tl-secondary-l2-color: rgb(255, 51, 251); + --tl-secondary-l3-color: rgb(255, 77, 252); + --tl-secondary-d1-color: rgb(230, 0, 225); + --tl-secondary-d2-color: rgb(204, 0, 200); + --tl-secondary-d3-color: rgb(179, 0, 175); + --tl-secondary-f1-color: rgb(230, 0, 225); + --tl-secondary-f2-color: rgb(204, 0, 200); + --tl-secondary-f3-color: rgb(179, 0, 175); + --tl-secondary-r1-color: rgb(255, 26, 251); + --tl-secondary-r2-color: rgb(255, 51, 251); + --tl-secondary-r3-color: rgb(255, 77, 252); --tl-text-primary-color: rgb(17, 17, 17); - --tl-text-primary-inactive-color: rgb(41, 41, 41); - --tl-text-primary-lighter-color: rgb(41, 41, 41); - --tl-text-primary-darker-color: rgb(15, 15, 15); + --tl-text-primary-l1-color: rgb(41, 41, 41); + --tl-text-primary-l2-color: rgb(65, 65, 65); + --tl-text-primary-l3-color: rgb(88, 88, 88); + --tl-text-primary-d1-color: rgb(15, 15, 15); + --tl-text-primary-d2-color: rgb(14, 14, 14); + --tl-text-primary-d3-color: rgb(12, 12, 12); + --tl-text-primary-f1-color: rgb(15, 15, 15); + --tl-text-primary-f2-color: rgb(14, 14, 14); + --tl-text-primary-f3-color: rgb(12, 12, 12); + --tl-text-primary-r1-color: rgb(41, 41, 41); + --tl-text-primary-r2-color: rgb(65, 65, 65); + --tl-text-primary-r3-color: rgb(88, 88, 88); --tl-text-on-primary-color: rgb(255, 255, 255); - --tl-text-on-primary-inactive-color: rgb(230, 230, 230); - --tl-text-on-primary-lighter-color: rgb(255, 255, 255); - --tl-text-on-primary-darker-color: rgb(230, 230, 230); + --tl-text-on-primary-l1-color: rgb(255, 255, 255); + --tl-text-on-primary-l2-color: rgb(255, 255, 255); + --tl-text-on-primary-l3-color: rgb(255, 255, 255); + --tl-text-on-primary-d1-color: rgb(230, 230, 230); + --tl-text-on-primary-d2-color: rgb(204, 204, 204); + --tl-text-on-primary-d3-color: rgb(179, 179, 179); + --tl-text-on-primary-f1-color: rgb(255, 255, 255); + --tl-text-on-primary-f2-color: rgb(255, 255, 255); + --tl-text-on-primary-f3-color: rgb(255, 255, 255); + --tl-text-on-primary-r1-color: rgb(230, 230, 230); + --tl-text-on-primary-r2-color: rgb(204, 204, 204); + --tl-text-on-primary-r3-color: rgb(179, 179, 179); --tl-danger-color: rgb(255, 0, 0); - --tl-danger-inactive-color: rgb(255, 26, 26); - --tl-danger-lighter-color: rgb(255, 26, 26); - --tl-danger-darker-color: rgb(230, 0, 0); + --tl-danger-l1-color: rgb(255, 26, 26); + --tl-danger-l2-color: rgb(255, 51, 51); + --tl-danger-l3-color: rgb(255, 77, 77); + --tl-danger-d1-color: rgb(230, 0, 0); + --tl-danger-d2-color: rgb(204, 0, 0); + --tl-danger-d3-color: rgb(179, 0, 0); + --tl-danger-f1-color: rgb(230, 0, 0); + --tl-danger-f2-color: rgb(204, 0, 0); + --tl-danger-f3-color: rgb(179, 0, 0); + --tl-danger-r1-color: rgb(255, 26, 26); + --tl-danger-r2-color: rgb(255, 51, 51); + --tl-danger-r3-color: rgb(255, 77, 77); --tl-success-color: rgb(0, 128, 0); - --tl-success-inactive-color: rgb(0, 166, 0); - --tl-success-lighter-color: rgb(0, 166, 0); - --tl-success-darker-color: rgb(0, 115, 0); + --tl-success-l1-color: rgb(0, 166, 0); + --tl-success-l2-color: rgb(0, 204, 0); + --tl-success-l3-color: rgb(0, 243, 0); + --tl-success-d1-color: rgb(0, 115, 0); + --tl-success-d2-color: rgb(0, 102, 0); + --tl-success-d3-color: rgb(0, 90, 0); + --tl-success-f1-color: rgb(0, 115, 0); + --tl-success-f2-color: rgb(0, 102, 0); + --tl-success-f3-color: rgb(0, 90, 0); + --tl-success-r1-color: rgb(0, 166, 0); + --tl-success-r2-color: rgb(0, 204, 0); + --tl-success-r3-color: rgb(0, 243, 0); } body { diff --git a/FrontEnd/src/index.tsx b/FrontEnd/src/index.tsx index 156fb2d9..e2132de0 100644 --- a/FrontEnd/src/index.tsx +++ b/FrontEnd/src/index.tsx @@ -3,10 +3,6 @@ import "core-js/modules/es.promise"; import "core-js/modules/es.array.iterator"; import "pepjs"; -import "bootstrap/dist/css/bootstrap-reboot.css"; -import "bootstrap/dist/css/bootstrap-grid.css"; -import "bootstrap-icons/font/bootstrap-icons.css"; - import React from "react"; import ReactDOM from "react-dom"; diff --git a/FrontEnd/src/palette.ts b/FrontEnd/src/palette.ts index ab3d6b54..fa99364f 100644 --- a/FrontEnd/src/palette.ts +++ b/FrontEnd/src/palette.ts @@ -15,9 +15,18 @@ function darkenBy(color: Color, ratio: number): Color { export interface PaletteColor { color: string; - lighter: string; - darker: string; - inactive: string; + l1: string; + l2: string; + l3: string; + d1: string; + d2: string; + d3: string; + f1: string; + f2: string; + f3: string; + r1: string; + r2: string; + r3: string; [key: string]: string; } @@ -37,13 +46,34 @@ export type Palette = Record; export function generatePaletteColor(color: string): PaletteColor { const c = Color(color); + const light = c.lightness() > 60; + const l1 = lightenBy(c, 0.1).rgb().toString(); + const l2 = lightenBy(c, 0.2).rgb().toString(); + const l3 = lightenBy(c, 0.3).rgb().toString(); + const d1 = darkenBy(c, 0.1).rgb().toString(); + const d2 = darkenBy(c, 0.2).rgb().toString(); + const d3 = darkenBy(c, 0.3).rgb().toString(); + const f1 = light ? l1 : d1; + const f2 = light ? l2 : d2; + const f3 = light ? l3 : d3; + const r1 = light ? d1 : l1; + const r2 = light ? d2 : l2; + const r3 = light ? d3 : l3; + return { color: c.rgb().toString(), - inactive: (c.lightness() > 60 ? darkenBy(c, 0.1) : lightenBy(c, 0.1)) - .rgb() - .toString(), - lighter: lightenBy(c, 0.1).rgb().toString(), - darker: darkenBy(c, 0.1).rgb().toString(), + l1, + l2, + l3, + d1, + d2, + d3, + f1, + f2, + f3, + r1, + r2, + r3, }; } @@ -91,7 +121,10 @@ export function generatePaletteCSS(palette: Palette): string { } const paletteSubject: BehaviorSubject = - new BehaviorSubject(null); + new BehaviorSubject( + // generatePalette({ primary: "rgb(0, 123, 255)" }) + null + ); export const palette$: Observable = paletteSubject.asObservable(); diff --git a/FrontEnd/src/views/admin/AdminNav.tsx b/FrontEnd/src/views/admin/AdminNav.tsx index 47e2138f..8b4c5fda 100644 --- a/FrontEnd/src/views/admin/AdminNav.tsx +++ b/FrontEnd/src/views/admin/AdminNav.tsx @@ -1,43 +1,29 @@ import React from "react"; -import { Nav } from "react-bootstrap"; -import { useTranslation } from "react-i18next"; -import { useHistory, useRouteMatch } from "react-router"; +import { useRouteMatch } from "react-router"; + +import Tabs from "../common/tab/Tabs"; const AdminNav: React.FC = () => { const match = useRouteMatch<{ name: string }>(); - const history = useHistory(); - - const { t } = useTranslation(); const name = match.params.name; - function toggle(newTab: string): void { - history.push(`/admin/${newTab}`); - } - return ( - + ); }; diff --git a/FrontEnd/src/views/center/TimelineBoard.tsx b/FrontEnd/src/views/center/TimelineBoard.tsx index d6f6228d..d7aa39ab 100644 --- a/FrontEnd/src/views/center/TimelineBoard.tsx +++ b/FrontEnd/src/views/center/TimelineBoard.tsx @@ -1,7 +1,6 @@ import React from "react"; import classnames from "classnames"; import { Link } from "react-router-dom"; -import { Spinner } from "react-bootstrap"; import { HttpTimelineInfo } from "@/http/timeline"; @@ -10,6 +9,7 @@ import UserTimelineLogo from "../common/UserTimelineLogo"; import LoadFailReload from "../common/LoadFailReload"; import FlatButton from "../common/button/FlatButton"; import Card from "../common/Card"; +import Spinner from "../common/Spinner"; interface TimelineBoardItemProps { timeline: HttpTimelineInfo; @@ -249,7 +249,7 @@ const TimelineBoardUI: React.FC = (props) => { if (timelines === "loading") { return (
- +
); } else if (timelines === "offline") { diff --git a/FrontEnd/src/views/center/index.tsx b/FrontEnd/src/views/center/index.tsx index dfad082a..77bb6ec8 100644 --- a/FrontEnd/src/views/center/index.tsx +++ b/FrontEnd/src/views/center/index.tsx @@ -21,7 +21,7 @@ const HomePage: React.FC = () => { return ( <> -
+
{ const { t } = useTranslation(); diff --git a/FrontEnd/src/views/common/FullPage.tsx b/FrontEnd/src/views/common/FullPage.tsx deleted file mode 100644 index 1b59045a..00000000 --- a/FrontEnd/src/views/common/FullPage.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from "react"; -import classnames from "classnames"; - -export interface FullPageProps { - show: boolean; - onBack: () => void; - contentContainerClassName?: string; -} - -const FullPage: React.FC = ({ - show, - onBack, - children, - contentContainerClassName, -}) => { - return ( -
-
- -
-
- {children} -
-
- ); -}; - -export default FullPage; diff --git a/FrontEnd/src/views/common/LoadingPage.tsx b/FrontEnd/src/views/common/LoadingPage.tsx index 590fafa0..8c1e681a 100644 --- a/FrontEnd/src/views/common/LoadingPage.tsx +++ b/FrontEnd/src/views/common/LoadingPage.tsx @@ -1,10 +1,11 @@ import React from "react"; -import { Spinner } from "react-bootstrap"; + +import Spinner from "./Spinner"; const LoadingPage: React.FC = () => { return (
- +
); }; diff --git a/FrontEnd/src/views/common/SearchInput.css b/FrontEnd/src/views/common/SearchInput.css new file mode 100644 index 00000000..2943b3a2 --- /dev/null +++ b/FrontEnd/src/views/common/SearchInput.css @@ -0,0 +1,4 @@ +.cru-search-input { + display: flex; + flex-wrap: wrap; +} diff --git a/FrontEnd/src/views/common/SearchInput.tsx b/FrontEnd/src/views/common/SearchInput.tsx index fff11b95..da3f1c19 100644 --- a/FrontEnd/src/views/common/SearchInput.tsx +++ b/FrontEnd/src/views/common/SearchInput.tsx @@ -4,6 +4,8 @@ import { useTranslation } from "react-i18next"; import LoadingButton from "./button/LoadingButton"; +import "./SearchInput.css"; + export interface SearchInputProps { value: string; onChange: (value: string) => void; diff --git a/FrontEnd/src/views/common/TabPages.tsx b/FrontEnd/src/views/common/TabPages.tsx deleted file mode 100644 index b7a9fb36..00000000 --- a/FrontEnd/src/views/common/TabPages.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from "react"; -import { useTranslation } from "react-i18next"; - -import { convertI18nText, I18nText, UiLogicError } from "@/common"; - -export interface TabPage { - id: string; - tabText: I18nText; - page: React.ReactNode; -} - -export interface TabPagesProps { - pages: TabPage[]; - actions?: React.ReactNode; - className?: string; - style?: React.CSSProperties; - navClassName?: string; - navStyle?: React.CSSProperties; - pageContainerClassName?: string; - pageContainerStyle?: React.CSSProperties; -} - -const TabPages: React.FC = ({ - pages, - actions, - className, - style, - navClassName, - navStyle, - pageContainerClassName, - pageContainerStyle, -}) => { - // TODO: - - if (pages.length === 0) { - throw new UiLogicError("Page list can't be empty."); - } - - const { t } = useTranslation(); - - const [tab, setTab] = React.useState(pages[0].id); - - const currentPage = pages.find((p) => p.id === tab); - - if (currentPage == null) { - throw new UiLogicError("Current tab value is bad."); - } - - return ( -
-
- {currentPage.page} -
-
- ); -}; - -export default TabPages; diff --git a/FrontEnd/src/views/common/alert/alert.css b/FrontEnd/src/views/common/alert/alert.css new file mode 100644 index 00000000..12ce294e --- /dev/null +++ b/FrontEnd/src/views/common/alert/alert.css @@ -0,0 +1,4 @@ +.alert-container { + position: fixed; + z-index: 1040; +} diff --git a/FrontEnd/src/views/common/alert/alert.sass b/FrontEnd/src/views/common/alert/alert.sass deleted file mode 100644 index c3560b87..00000000 --- a/FrontEnd/src/views/common/alert/alert.sass +++ /dev/null @@ -1,15 +0,0 @@ -.alert-container - position: fixed - z-index: $zindex-popover - -@include media-breakpoint-up(sm) - .alert-container - bottom: 0 - right: 0 - -@include media-breakpoint-down(sm) - .alert-container - bottom: 0 - right: 0 - left: 0 - text-align: center diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index e69de29b..b6df222f 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -0,0 +1,72 @@ +.cru-button { + color: white; + cursor: pointer; + padding: 0.2em 0.5em; + border-radius: 0.2em; + border: none; + transition: all 0.6s; +} + +.cru-button.primary { + background-color: var(--tl-primary-color); +} + +.cru-button.primary:hover { + background-color: var(--tl-primary-f1-color); +} + +.cru-button.primary:active { + background-color: var(--tl-primary-f2-color); +} + +.cru-button.primary.disabled { + background-color: var(--tl-primary-f3-color); +} + +.cru-button.secondary { + background-color: var(--tl-secondary-color); +} + +.cru-button.secondary:hover { + background-color: var(--tl-secondary-f1-color); +} + +.cru-button.secondary:active { + background-color: var(--tl-secondary-f2-color); +} + +.cru-button.secondary.disabled { + background-color: var(--tl-secondary-f3-color); +} + +.cru-button.success { + background-color: var(--tl-success-color); +} + +.cru-button.success:hover { + background-color: var(--tl-success-f1-color); +} + +.cru-button.success:active { + background-color: var(--tl-success-f2-color); +} + +.cru-button.success.disabled { + background-color: var(--tl-success-f3-color); +} + +.cru-button.danger { + background-color: var(--tl-danger-color); +} + +.cru-button.danger:hover { + background-color: var(--tl-danger-f1-color); +} + +.cru-button.danger:active { + background-color: var(--tl-danger-f2-color); +} + +.cru-button.danger.disabled { + background-color: var(--tl-danger-f3-color); +} diff --git a/FrontEnd/src/views/common/button/Button.tsx b/FrontEnd/src/views/common/button/Button.tsx index 11710647..a39ef8a7 100644 --- a/FrontEnd/src/views/common/button/Button.tsx +++ b/FrontEnd/src/views/common/button/Button.tsx @@ -6,16 +6,19 @@ import { calculateProps, CommonButtonProps } from "./common"; import "./Button.css"; function _Button( - props: CommonButtonProps & { customButtonClassName?: string }, + props: CommonButtonProps & { + outline?: boolean; + customButtonClassName?: string; + }, ref: React.ForwardedRef ): React.ReactElement | null { const { t } = useTranslation(); - const { customButtonClassName, ...otherProps } = props; + const { customButtonClassName, outline, ...otherProps } = props; const { newProps, children } = calculateProps( otherProps, - customButtonClassName ?? "cru-button", + customButtonClassName ?? "cru-button" + (outline ? " outline" : ""), t ); diff --git a/FrontEnd/src/views/common/button/FlatButton.css b/FrontEnd/src/views/common/button/FlatButton.css index 522563b9..c3c0dbb3 100644 --- a/FrontEnd/src/views/common/button/FlatButton.css +++ b/FrontEnd/src/views/common/button/FlatButton.css @@ -20,7 +20,7 @@ } .cru-flat-button.primary.disabled { - color: var(--tl-primary-lighter-color); + color: var(--tl-primary-l1-color); } .cru-flat-button.secondary { @@ -28,7 +28,7 @@ } .cru-flat-button.secondary.disabled { - color: var(--tl-secondary-lighter-color); + color: var(--tl-secondary-l1-color); } .cru-flat-button.success { @@ -36,7 +36,7 @@ } .cru-flat-button.success.disabled { - color: var(--tl-success-lighter-color); + color: var(--tl-success-l1-color); } .cru-flat-button.danger { @@ -44,5 +44,5 @@ } .cru-flat-button.danger.disabled { - color: var(--tl-danger-ligher-color); + color: var(--tl-danger-l1-color); } diff --git a/FrontEnd/src/views/common/button/TextButton.css b/FrontEnd/src/views/common/button/TextButton.css index dc5abaaa..d267fb38 100644 --- a/FrontEnd/src/views/common/button/TextButton.css +++ b/FrontEnd/src/views/common/button/TextButton.css @@ -8,7 +8,7 @@ } .cru-text-button.primary:hover { - color: var(--tl-primary-lighter-color); + color: var(--tl-primary-l1-color); } .cru-text-button.secondary { @@ -16,7 +16,7 @@ } .cru-text-button.secondary:hover { - color: var(--tl-secondary-lighter-color); + color: var(--tl-secondary-l1-color); } .cru-text-button.success { @@ -24,7 +24,7 @@ } .cru-text-button.success:hover { - color: var(--tl-success-lighter-color); + color: var(--tl-success-l1-color); } .cru-text-button.danger { @@ -32,5 +32,5 @@ } .cru-text-button.danger:hover { - color: var(--tl-danger-lighter-color); + color: var(--tl-danger-l1-color); } diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx new file mode 100644 index 00000000..88c90bbc --- /dev/null +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import classnames from "classnames"; + +export interface FullPageDialogProps { + show: boolean; + onBack: () => void; + contentContainerClassName?: string; +} + +const FullPageDialog: React.FC = ({ + show, + onBack, + children, + contentContainerClassName, +}) => { + return ( +
+
+ +
+
+ {children} +
+
+ ); +}; + +export default FullPageDialog; diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index bfd82b58..f580c781 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -37,96 +37,6 @@ touch-action: none; } -.app-bar { - display: flex; - align-items: center; - height: 56px; - position: fixed; - z-index: 1030; - top: 0; - left: 0; - right: 0; - background-color: var(--tl-primary-color); - transition: background-color 1s; -} -.app-bar a { - color: var(--tl-text-on-primary-inactive-color); - text-decoration: none; - margin: 0 1em; -} -.app-bar a:hover { - color: var(--tl-text-on-primary-color); -} -.app-bar a.active { - color: var(--tl-text-on-primary-color); -} - -.app-bar-brand { - display: flex; - align-items: center; -} - -.app-bar-brand-icon { - height: 2em; -} - -.app-bar-main-area { - display: flex; - flex-grow: 1; -} - -.app-bar-link-area { - display: flex; - align-items: center; - flex-shrink: 0; -} - -.app-bar-user-area { - display: flex; - align-items: center; - flex-shrink: 0; - margin-left: auto; -} - -.small-screen .app-bar-main-area { - position: absolute; - top: 56px; - left: 0; - right: 0; - transform-origin: top; - transition: transform 0.6s, background-color 1s; - background-color: var(--tl-primary-color); - flex-direction: column; -} -.small-screen .app-bar-main-area.app-bar-collapse { - transform: scale(1, 0); -} -.small-screen .app-bar-main-area a { - text-align: left; - padding: 0.5em 0.5em; -} -.small-screen .app-bar-link-area { - flex-direction: column; - align-items: stretch; -} -.small-screen .app-bar-user-area { - flex-direction: column; - align-items: stretch; - margin-left: unset; -} -.small-screen .app-bar-avatar { - align-self: flex-end; -} - -.app-bar-toggler { - margin-left: auto; - font-size: 2em; - margin-right: 1em; - color: var(--tl-text-on-primary-color); - cursor: pointer; - user-select: none; -} - .cru-skeleton { padding: 0 1em; } @@ -247,10 +157,7 @@ align-items: center; } -.cru-search-input { - display: flex; - flex-wrap: wrap; -} + .alert-container { position: fixed; diff --git a/FrontEnd/src/views/common/tab/TabPages.tsx b/FrontEnd/src/views/common/tab/TabPages.tsx new file mode 100644 index 00000000..b7a9fb36 --- /dev/null +++ b/FrontEnd/src/views/common/tab/TabPages.tsx @@ -0,0 +1,58 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +import { convertI18nText, I18nText, UiLogicError } from "@/common"; + +export interface TabPage { + id: string; + tabText: I18nText; + page: React.ReactNode; +} + +export interface TabPagesProps { + pages: TabPage[]; + actions?: React.ReactNode; + className?: string; + style?: React.CSSProperties; + navClassName?: string; + navStyle?: React.CSSProperties; + pageContainerClassName?: string; + pageContainerStyle?: React.CSSProperties; +} + +const TabPages: React.FC = ({ + pages, + actions, + className, + style, + navClassName, + navStyle, + pageContainerClassName, + pageContainerStyle, +}) => { + // TODO: + + if (pages.length === 0) { + throw new UiLogicError("Page list can't be empty."); + } + + const { t } = useTranslation(); + + const [tab, setTab] = React.useState(pages[0].id); + + const currentPage = pages.find((p) => p.id === tab); + + if (currentPage == null) { + throw new UiLogicError("Current tab value is bad."); + } + + return ( +
+
+ {currentPage.page} +
+
+ ); +}; + +export default TabPages; diff --git a/FrontEnd/src/views/common/tab/Tabs.tsx b/FrontEnd/src/views/common/tab/Tabs.tsx new file mode 100644 index 00000000..29ebcbd8 --- /dev/null +++ b/FrontEnd/src/views/common/tab/Tabs.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +import { I18nText } from "@/common"; + +export interface Tab { + name: string; + text: I18nText; + link?: string; + onClick?: () => void; +} + +export interface TabsProps { + activeTabName?: string; + tabs: Tab[]; +} + +export default function Tabs(props: TabsProps): React.ReactElement | null { + return
; +} diff --git a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx index 9f0753b9..b16bf43d 100644 --- a/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/MarkdownPostEdit.tsx @@ -9,7 +9,7 @@ import { getHttpTimelineClient, HttpTimelinePostInfo } from "@/http/timeline"; import TimelinePostBuilder from "@/services/TimelinePostBuilder"; import FlatButton from "../common/button/FlatButton"; -import TabPages from "../common/TabPages"; +import TabPages from "../common/tab/TabPages"; import ConfirmDialog from "../common/dailog/ConfirmDialog"; import Spinner from "../common/Spinner"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 851dfa55..78057be2 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -18,7 +18,7 @@ import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import ConnectionStatusBadge from "./ConnectionStatusBadge"; import { MenuItems, PopupMenu } from "../common/Menu"; -import FullPage from "../common/FullPage"; +import FullPageDialog from "../common/dailog/FullPageDialog"; import Card from "../common/Card"; export interface TimelineCardTemplateProps extends TimelinePageCardProps { @@ -120,13 +120,13 @@ const TimelinePageCardTemplate: React.FC = ({
{isSmallScreen ? ( - {content} - + ) : (
{content}
)} diff --git a/FrontEnd/src/views/timeline-common/index.css b/FrontEnd/src/views/timeline-common/index.css index e38d0ba7..b78564a3 100644 --- a/FrontEnd/src/views/timeline-common/index.css +++ b/FrontEnd/src/views/timeline-common/index.css @@ -13,19 +13,19 @@ @keyframes timeline-line-node-noncurrent { to { - box-shadow: 0 0 20px 3px var(--tl-primary-lighter-color); + box-shadow: 0 0 20px 3px var(--tl-primary-l1-color); } } @keyframes timeline-line-node-current { to { - box-shadow: 0 0 20px 3px var(--tl-primary-enhance-lighter-color); + box-shadow: 0 0 20px 3px var(--tl-primary-enhance-l1-color); } } @keyframes timeline-line-node-loading { to { - box-shadow: 0 0 20px 3px var(--tl-primary-lighter-color); + box-shadow: 0 0 20px 3px var(--tl-primary-l1-color); } } -- cgit v1.2.3 From b767c7d23d031c58c594570d55933e40065495ae Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 26 Jun 2021 20:03:40 +0800 Subject: ... --- FrontEnd/src/index.css | 41 ++++++++++------------------- FrontEnd/src/views/about/index.tsx | 10 +++---- FrontEnd/src/views/common/AppBar.css | 5 ++++ FrontEnd/src/views/common/AppBar.tsx | 2 +- FrontEnd/src/views/common/ImageCropper.css | 38 +++++++++++++++++++++++++++ FrontEnd/src/views/common/ImageCropper.tsx | 2 ++ FrontEnd/src/views/common/index.css | 42 +++++++----------------------- 7 files changed, 75 insertions(+), 65 deletions(-) create mode 100644 FrontEnd/src/views/common/ImageCropper.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/index.css b/FrontEnd/src/index.css index d4efabed..4adc1844 100644 --- a/FrontEnd/src/index.css +++ b/FrontEnd/src/index.css @@ -2,6 +2,8 @@ @import "bootstrap/dist/css/bootstrap-grid.css"; @import "bootstrap-icons/font/bootstrap-icons.css"; +@import "./views/common/index.css"; + :root { --tl-background-color: #f8f9fa; @@ -98,18 +100,26 @@ --tl-success-r3-color: rgb(0, 243, 0); } -body { - background: var(--tl-background-color); -} - .tl-color-primary { color: var(--tl-primary-color); } +.tl-color-secondary { + color: var(--tl-secondary-color); +} + +.tl-color-success { + color: var(--tl-success-color); +} + .tl-color-danger { color: var(--tl-danger-color); } +body { + background: var(--tl-background-color); +} + small { line-height: 1.2; } @@ -119,21 +129,6 @@ small { flex-shrink: 0; } -.avatar { - width: 60px; - height: 60px; -} - -.avatar.large { - width: 100px; - height: 100px; -} - -.avatar.small { - width: 40px; - height: 40px; -} - .icon-button { font-size: 1.4rem; cursor: pointer; @@ -168,14 +163,6 @@ textarea { align-items: center; } -.text-orange { - color: #fd7e14; -} - -.text-yellow { - color: #ffc107; -} - .touch-action-none { touch-action: none; } diff --git a/FrontEnd/src/views/about/index.tsx b/FrontEnd/src/views/about/index.tsx index 7a72d5ec..8a358014 100644 --- a/FrontEnd/src/views/about/index.tsx +++ b/FrontEnd/src/views/about/index.tsx @@ -72,16 +72,16 @@ const AboutPage: React.FC = () => {

{t("about.author.fullname")} - 杨宇千 + 杨宇千

{t("about.author.nickname")} - crupest + crupest

{t("about.author.introduction")} @@ -96,7 +96,7 @@ const AboutPage: React.FC = () => { target="_blank" rel="noopener noreferrer" > - +

@@ -105,7 +105,7 @@ const AboutPage: React.FC = () => {

{t("about.site.title")}

- 01234 + 01234 56

diff --git a/FrontEnd/src/views/common/AppBar.css b/FrontEnd/src/views/common/AppBar.css index c6f4e184..16572817 100644 --- a/FrontEnd/src/views/common/AppBar.css +++ b/FrontEnd/src/views/common/AppBar.css @@ -10,6 +10,11 @@ background-color: var(--tl-primary-color); transition: background-color 1s; } + +.app-bar .cru-avatar { + background-color: white; +} + .app-bar a { color: var(--tl-text-on-primary-r1-color); text-decoration: none; diff --git a/FrontEnd/src/views/common/AppBar.tsx b/FrontEnd/src/views/common/AppBar.tsx index 7ec0a5eb..5d62a88d 100644 --- a/FrontEnd/src/views/common/AppBar.tsx +++ b/FrontEnd/src/views/common/AppBar.tsx @@ -68,7 +68,7 @@ const AppBar: React.FC = (_) => { "/", , "app-bar-avatar" ) diff --git a/FrontEnd/src/views/common/ImageCropper.css b/FrontEnd/src/views/common/ImageCropper.css new file mode 100644 index 00000000..2c4d0a8c --- /dev/null +++ b/FrontEnd/src/views/common/ImageCropper.css @@ -0,0 +1,38 @@ +.image-cropper-container { + position: relative; + box-sizing: border-box; + user-select: none; +} + +.image-cropper-container img { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; +} + +.image-cropper-mask-container { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; +} + +.image-cropper-mask { + position: absolute; + box-shadow: 0 0 0 10000px rgba(255, 255, 255, 0.8); + touch-action: none; +} + +.image-cropper-handler { + position: absolute; + width: 26px; + height: 26px; + border: black solid 2px; + border-radius: 50%; + background: white; + touch-action: none; +} diff --git a/FrontEnd/src/views/common/ImageCropper.tsx b/FrontEnd/src/views/common/ImageCropper.tsx index 2ef5b7ed..be44200a 100644 --- a/FrontEnd/src/views/common/ImageCropper.tsx +++ b/FrontEnd/src/views/common/ImageCropper.tsx @@ -3,6 +3,8 @@ import classnames from "classnames"; import { UiLogicError } from "@/common"; +import "./ImageCropper.css"; + export interface Clip { left: number; top: number; diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index f580c781..aaaaad58 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -1,40 +1,20 @@ -.image-cropper-container { - position: relative; - box-sizing: border-box; - user-select: none; +.cru-avatar { + width: 60px; + height: 60px; } -.image-cropper-container img { - position: absolute; - left: 0; - top: 0; - width: 100%; - height: 100%; -} - -.image-cropper-mask-container { - position: absolute; - left: 0; - top: 0; - right: 0; - bottom: 0; - overflow: hidden; +.cru-avatar.large { + width: 100px; + height: 100px; } -.image-cropper-mask { - position: absolute; - box-shadow: 0 0 0 10000px rgba(255, 255, 255, 0.8); - touch-action: none; +.cru-avatar.small { + width: 40px; + height: 40px; } -.image-cropper-handler { - position: absolute; - width: 26px; - height: 26px; - border: black solid 2px; +.cru-round { border-radius: 50%; - background: white; - touch-action: none; } .cru-skeleton { @@ -157,8 +137,6 @@ align-items: center; } - - .alert-container { position: fixed; z-index: 1070; -- cgit v1.2.3 From de7df0e4fb38863b830a104a5a38d0e5247679f8 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 29 Jun 2021 23:50:55 +0800 Subject: ... --- FrontEnd/index.html | 2 +- FrontEnd/src/index.css | 127 +----------- FrontEnd/src/palette.ts | 14 +- FrontEnd/src/views/common/AppBar.css | 12 +- FrontEnd/src/views/common/Card.css | 2 +- FrontEnd/src/views/common/Menu.tsx | 80 -------- FrontEnd/src/views/common/button/Button.css | 32 +-- FrontEnd/src/views/common/button/FlatButton.css | 16 +- FrontEnd/src/views/common/button/TextButton.css | 16 +- FrontEnd/src/views/common/dailog/Dialog.css | 22 ++ FrontEnd/src/views/common/dailog/Dialog.tsx | 30 ++- FrontEnd/src/views/common/index.css | 225 ++++++++++++++------- FrontEnd/src/views/common/menu/Menu.css | 35 ++++ FrontEnd/src/views/common/menu/Menu.tsx | 66 ++++++ FrontEnd/src/views/common/menu/PopupMenu.tsx | 17 ++ .../timeline-common/TimelinePageCardTemplate.tsx | 2 +- .../src/views/timeline-common/TimelinePostEdit.tsx | 2 +- FrontEnd/src/views/timeline-common/index.css | 24 +-- 18 files changed, 384 insertions(+), 340 deletions(-) delete mode 100644 FrontEnd/src/views/common/Menu.tsx create mode 100644 FrontEnd/src/views/common/menu/Menu.css create mode 100644 FrontEnd/src/views/common/menu/Menu.tsx create mode 100644 FrontEnd/src/views/common/menu/PopupMenu.tsx (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/index.html b/FrontEnd/index.html index ccd6a21b..3cc5a580 100644 --- a/FrontEnd/index.html +++ b/FrontEnd/index.html @@ -24,7 +24,7 @@
-
+
diff --git a/FrontEnd/src/index.css b/FrontEnd/src/index.css index 4adc1844..14f0bc43 100644 --- a/FrontEnd/src/index.css +++ b/FrontEnd/src/index.css @@ -4,120 +4,8 @@ @import "./views/common/index.css"; -:root { - --tl-background-color: #f8f9fa; - - --tl-primary-color: rgb(0, 123, 255); - --tl-primary-l1-color: rgb(26, 136, 255); - --tl-primary-l2-color: rgb(51, 149, 255); - --tl-primary-l3-color: rgb(77, 163, 255); - --tl-primary-d1-color: rgb(0, 111, 230); - --tl-primary-d2-color: rgb(0, 98, 204); - --tl-primary-d3-color: rgb(0, 86, 179); - --tl-primary-f1-color: rgb(0, 111, 230); - --tl-primary-f2-color: rgb(0, 98, 204); - --tl-primary-f3-color: rgb(0, 86, 179); - --tl-primary-r1-color: rgb(26, 136, 255); - --tl-primary-r2-color: rgb(51, 149, 255); - --tl-primary-r3-color: rgb(77, 163, 255); - --tl-primary-enhance-color: rgb(77, 163, 255); - --tl-primary-enhance-l1-color: rgb(94, 172, 255); - --tl-primary-enhance-l2-color: rgb(112, 181, 255); - --tl-primary-enhance-l3-color: rgb(130, 190, 255); - --tl-primary-enhance-d1-color: rgb(43, 145, 255); - --tl-primary-enhance-d2-color: rgb(10, 128, 255); - --tl-primary-enhance-d3-color: rgb(0, 112, 232); - --tl-primary-enhance-f1-color: rgb(94, 172, 255); - --tl-primary-enhance-f2-color: rgb(112, 181, 255); - --tl-primary-enhance-f3-color: rgb(130, 190, 255); - --tl-primary-enhance-r1-color: rgb(43, 145, 255); - --tl-primary-enhance-r2-color: rgb(10, 128, 255); - --tl-primary-enhance-r3-color: rgb(0, 112, 232); - --tl-secondary-color: rgb(255, 0, 250); - --tl-secondary-l1-color: rgb(255, 26, 251); - --tl-secondary-l2-color: rgb(255, 51, 251); - --tl-secondary-l3-color: rgb(255, 77, 252); - --tl-secondary-d1-color: rgb(230, 0, 225); - --tl-secondary-d2-color: rgb(204, 0, 200); - --tl-secondary-d3-color: rgb(179, 0, 175); - --tl-secondary-f1-color: rgb(230, 0, 225); - --tl-secondary-f2-color: rgb(204, 0, 200); - --tl-secondary-f3-color: rgb(179, 0, 175); - --tl-secondary-r1-color: rgb(255, 26, 251); - --tl-secondary-r2-color: rgb(255, 51, 251); - --tl-secondary-r3-color: rgb(255, 77, 252); - --tl-text-primary-color: rgb(17, 17, 17); - --tl-text-primary-l1-color: rgb(41, 41, 41); - --tl-text-primary-l2-color: rgb(65, 65, 65); - --tl-text-primary-l3-color: rgb(88, 88, 88); - --tl-text-primary-d1-color: rgb(15, 15, 15); - --tl-text-primary-d2-color: rgb(14, 14, 14); - --tl-text-primary-d3-color: rgb(12, 12, 12); - --tl-text-primary-f1-color: rgb(15, 15, 15); - --tl-text-primary-f2-color: rgb(14, 14, 14); - --tl-text-primary-f3-color: rgb(12, 12, 12); - --tl-text-primary-r1-color: rgb(41, 41, 41); - --tl-text-primary-r2-color: rgb(65, 65, 65); - --tl-text-primary-r3-color: rgb(88, 88, 88); - --tl-text-on-primary-color: rgb(255, 255, 255); - --tl-text-on-primary-l1-color: rgb(255, 255, 255); - --tl-text-on-primary-l2-color: rgb(255, 255, 255); - --tl-text-on-primary-l3-color: rgb(255, 255, 255); - --tl-text-on-primary-d1-color: rgb(230, 230, 230); - --tl-text-on-primary-d2-color: rgb(204, 204, 204); - --tl-text-on-primary-d3-color: rgb(179, 179, 179); - --tl-text-on-primary-f1-color: rgb(255, 255, 255); - --tl-text-on-primary-f2-color: rgb(255, 255, 255); - --tl-text-on-primary-f3-color: rgb(255, 255, 255); - --tl-text-on-primary-r1-color: rgb(230, 230, 230); - --tl-text-on-primary-r2-color: rgb(204, 204, 204); - --tl-text-on-primary-r3-color: rgb(179, 179, 179); - --tl-danger-color: rgb(255, 0, 0); - --tl-danger-l1-color: rgb(255, 26, 26); - --tl-danger-l2-color: rgb(255, 51, 51); - --tl-danger-l3-color: rgb(255, 77, 77); - --tl-danger-d1-color: rgb(230, 0, 0); - --tl-danger-d2-color: rgb(204, 0, 0); - --tl-danger-d3-color: rgb(179, 0, 0); - --tl-danger-f1-color: rgb(230, 0, 0); - --tl-danger-f2-color: rgb(204, 0, 0); - --tl-danger-f3-color: rgb(179, 0, 0); - --tl-danger-r1-color: rgb(255, 26, 26); - --tl-danger-r2-color: rgb(255, 51, 51); - --tl-danger-r3-color: rgb(255, 77, 77); - --tl-success-color: rgb(0, 128, 0); - --tl-success-l1-color: rgb(0, 166, 0); - --tl-success-l2-color: rgb(0, 204, 0); - --tl-success-l3-color: rgb(0, 243, 0); - --tl-success-d1-color: rgb(0, 115, 0); - --tl-success-d2-color: rgb(0, 102, 0); - --tl-success-d3-color: rgb(0, 90, 0); - --tl-success-f1-color: rgb(0, 115, 0); - --tl-success-f2-color: rgb(0, 102, 0); - --tl-success-f3-color: rgb(0, 90, 0); - --tl-success-r1-color: rgb(0, 166, 0); - --tl-success-r2-color: rgb(0, 204, 0); - --tl-success-r3-color: rgb(0, 243, 0); -} - -.tl-color-primary { - color: var(--tl-primary-color); -} - -.tl-color-secondary { - color: var(--tl-secondary-color); -} - -.tl-color-success { - color: var(--tl-success-color); -} - -.tl-color-danger { - color: var(--tl-danger-color); -} - body { - background: var(--tl-background-color); + background: var(--cru-background-color); } small { @@ -129,19 +17,6 @@ small { flex-shrink: 0; } -.icon-button { - font-size: 1.4rem; - cursor: pointer; -} - -.icon-button.large { - font-size: 1.6rem; -} - -.icon-button.primary-enhance { - color: var(--tl-primary-enhance-color); -} - .cursor-pointer { cursor: pointer; } diff --git a/FrontEnd/src/palette.ts b/FrontEnd/src/palette.ts index fa99364f..fb54f042 100644 --- a/FrontEnd/src/palette.ts +++ b/FrontEnd/src/palette.ts @@ -36,6 +36,8 @@ const paletteColorList = [ "secondary", "text-primary", "text-on-primary", + "text-on-primary-enhance", + "text-on-secondary", "danger", "success", ] as const; @@ -98,6 +100,12 @@ export function generatePalette(options: { "text-on-primary": generatePaletteColor( p.lightness() > 60 ? "black" : "white" ), + "text-on-primary-enhance": generatePaletteColor( + pe.lightness() > 60 ? "black" : "white" + ), + "text-on-secondary": generatePaletteColor( + s.lightness() > 60 ? "black" : "white" + ), danger: generatePaletteColor("red"), success: generatePaletteColor("green"), }; @@ -108,7 +116,7 @@ export function generatePaletteCSS(palette: Palette): string { for (const colorType of paletteColorList) { const paletteColor = palette[colorType]; for (const variant in paletteColor) { - let key = `--tl-${colorType}`; + let key = `--cru-${colorType}`; if (variant !== "color") key += `-${variant}`; key += "-color"; colors.push([key, paletteColor[variant]]); @@ -122,8 +130,8 @@ export function generatePaletteCSS(palette: Palette): string { const paletteSubject: BehaviorSubject = new BehaviorSubject( - // generatePalette({ primary: "rgb(0, 123, 255)" }) - null + generatePalette({ primary: "rgb(0, 123, 255)" }) + // null ); export const palette$: Observable = diff --git a/FrontEnd/src/views/common/AppBar.css b/FrontEnd/src/views/common/AppBar.css index 16572817..e4bdf852 100644 --- a/FrontEnd/src/views/common/AppBar.css +++ b/FrontEnd/src/views/common/AppBar.css @@ -7,7 +7,7 @@ top: 0; left: 0; right: 0; - background-color: var(--tl-primary-color); + background-color: var(--cru-primary-color); transition: background-color 1s; } @@ -16,16 +16,16 @@ } .app-bar a { - color: var(--tl-text-on-primary-r1-color); + color: var(--cru-text-on-primary-r1-color); text-decoration: none; margin: 0 1em; transition: color 1s; } .app-bar a:hover { - color: var(--tl-text-on-primary-color); + color: var(--cru-text-on-primary-color); } .app-bar a.active { - color: var(--tl-text-on-primary-color); + color: var(--cru-text-on-primary-color); } .app-bar-brand { @@ -62,7 +62,7 @@ right: 0; transform-origin: top; transition: transform 0.6s, background-color 1s; - background-color: var(--tl-primary-color); + background-color: var(--cru-primary-color); flex-direction: column; } .small-screen .app-bar-main-area.app-bar-collapse { @@ -89,7 +89,7 @@ margin-left: auto; font-size: 2em; margin-right: 1em; - color: var(--tl-text-on-primary-color); + color: var(--cru-text-on-primary-color); cursor: pointer; user-select: none; } diff --git a/FrontEnd/src/views/common/Card.css b/FrontEnd/src/views/common/Card.css index fb90bd59..6de0dd8e 100644 --- a/FrontEnd/src/views/common/Card.css +++ b/FrontEnd/src/views/common/Card.css @@ -11,5 +11,5 @@ } .cru-card:hover { - border-color: var(--tl-primary-color); + border-color: var(--cru-primary-color); } diff --git a/FrontEnd/src/views/common/Menu.tsx b/FrontEnd/src/views/common/Menu.tsx deleted file mode 100644 index a5d2ec2c..00000000 --- a/FrontEnd/src/views/common/Menu.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from "react"; -import classnames from "classnames"; -import { useTranslation } from "react-i18next"; - -import { convertI18nText, I18nText } from "@/common"; -import { PaletteColorType } from "@/palette"; - -export type MenuItem = - | { - type: "divider"; - } - | { - type: "button"; - text: I18nText; - iconClassName?: string; - color?: PaletteColorType; - onClick: () => void; - }; - -export type MenuItems = MenuItem[]; - -export interface MenuProps { - items: MenuItems; - className?: string; - onItemClicked?: () => void; -} - -const Menu: React.FC = ({ items, className, onItemClicked }) => { - const { t } = useTranslation(); - - return ( -
- {items.map((item, index) => { - if (item.type === "divider") { - return
; - } else { - return ( -
{ - item.onClick(); - onItemClicked?.(); - }} - > - {item.iconClassName != null ? ( - - ) : null} - {convertI18nText(item.text, t)} -
- ); - } - })} -
- ); -}; - -export default Menu; - -export interface PopupMenuProps { - items: MenuItems; - children: React.ReactElement; -} - -export const PopupMenu: React.FC = ({ items, children }) => { - const [show, setShow] = React.useState(false); - const toggle = (): void => setShow(!show); - - // TODO: - - return setShow(false)} />; -}; diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index b6df222f..8b5b74a7 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -8,65 +8,65 @@ } .cru-button.primary { - background-color: var(--tl-primary-color); + background-color: var(--cru-primary-color); } .cru-button.primary:hover { - background-color: var(--tl-primary-f1-color); + background-color: var(--cru-primary-f1-color); } .cru-button.primary:active { - background-color: var(--tl-primary-f2-color); + background-color: var(--cru-primary-f2-color); } .cru-button.primary.disabled { - background-color: var(--tl-primary-f3-color); + background-color: var(--cru-primary-f3-color); } .cru-button.secondary { - background-color: var(--tl-secondary-color); + background-color: var(--cru-secondary-color); } .cru-button.secondary:hover { - background-color: var(--tl-secondary-f1-color); + background-color: var(--cru-secondary-f1-color); } .cru-button.secondary:active { - background-color: var(--tl-secondary-f2-color); + background-color: var(--cru-secondary-f2-color); } .cru-button.secondary.disabled { - background-color: var(--tl-secondary-f3-color); + background-color: var(--cru-secondary-f3-color); } .cru-button.success { - background-color: var(--tl-success-color); + background-color: var(--cru-success-color); } .cru-button.success:hover { - background-color: var(--tl-success-f1-color); + background-color: var(--cru-success-f1-color); } .cru-button.success:active { - background-color: var(--tl-success-f2-color); + background-color: var(--cru-success-f2-color); } .cru-button.success.disabled { - background-color: var(--tl-success-f3-color); + background-color: var(--cru-success-f3-color); } .cru-button.danger { - background-color: var(--tl-danger-color); + background-color: var(--cru-danger-color); } .cru-button.danger:hover { - background-color: var(--tl-danger-f1-color); + background-color: var(--cru-danger-f1-color); } .cru-button.danger:active { - background-color: var(--tl-danger-f2-color); + background-color: var(--cru-danger-f2-color); } .cru-button.danger.disabled { - background-color: var(--tl-danger-f3-color); + background-color: var(--cru-danger-f3-color); } diff --git a/FrontEnd/src/views/common/button/FlatButton.css b/FrontEnd/src/views/common/button/FlatButton.css index c3c0dbb3..986fb061 100644 --- a/FrontEnd/src/views/common/button/FlatButton.css +++ b/FrontEnd/src/views/common/button/FlatButton.css @@ -16,33 +16,33 @@ } .cru-flat-button.primary { - color: var(--tl-primary-color); + color: var(--cru-primary-color); } .cru-flat-button.primary.disabled { - color: var(--tl-primary-l1-color); + color: var(--cru-primary-l1-color); } .cru-flat-button.secondary { - color: var(--tl-secondary-color); + color: var(--cru-secondary-color); } .cru-flat-button.secondary.disabled { - color: var(--tl-secondary-l1-color); + color: var(--cru-secondary-l1-color); } .cru-flat-button.success { - color: var(--tl-success-color); + color: var(--cru-success-color); } .cru-flat-button.success.disabled { - color: var(--tl-success-l1-color); + color: var(--cru-success-l1-color); } .cru-flat-button.danger { - color: var(--tl-danger-color); + color: var(--cru-danger-color); } .cru-flat-button.danger.disabled { - color: var(--tl-danger-l1-color); + color: var(--cru-danger-l1-color); } diff --git a/FrontEnd/src/views/common/button/TextButton.css b/FrontEnd/src/views/common/button/TextButton.css index d267fb38..f5c8aefa 100644 --- a/FrontEnd/src/views/common/button/TextButton.css +++ b/FrontEnd/src/views/common/button/TextButton.css @@ -4,33 +4,33 @@ } .cru-text-button.primary { - color: var(--tl-primary-color); + color: var(--cru-primary-color); } .cru-text-button.primary:hover { - color: var(--tl-primary-l1-color); + color: var(--cru-primary-l1-color); } .cru-text-button.secondary { - color: var(--tl-secondary-color); + color: var(--cru-secondary-color); } .cru-text-button.secondary:hover { - color: var(--tl-secondary-l1-color); + color: var(--cru-secondary-l1-color); } .cru-text-button.success { - color: var(--tl-success-color); + color: var(--cru-success-color); } .cru-text-button.success:hover { - color: var(--tl-success-l1-color); + color: var(--cru-success-l1-color); } .cru-text-button.danger { - color: var(--tl-danger-color); + color: var(--cru-danger-color); } .cru-text-button.danger:hover { - color: var(--tl-danger-l1-color); + color: var(--cru-danger-l1-color); } diff --git a/FrontEnd/src/views/common/dailog/Dialog.css b/FrontEnd/src/views/common/dailog/Dialog.css index e69de29b..abce04fd 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.css +++ b/FrontEnd/src/views/common/dailog/Dialog.css @@ -0,0 +1,22 @@ +.cru-dialog-overlay { + position: fixed; + z-index: 1040; + left: 0; + top: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.92); + + display: flex; + justify-content: center; + align-items: center; +} + +.cru-dialog-container { + max-width: 100%; + + border: var(--cru-primary-color) 1px solid; + border-radius: 5px; + padding: 1.5em; + background-color: white; +} diff --git a/FrontEnd/src/views/common/dailog/Dialog.tsx b/FrontEnd/src/views/common/dailog/Dialog.tsx index 5a3902c4..ee58080f 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.tsx +++ b/FrontEnd/src/views/common/dailog/Dialog.tsx @@ -1,13 +1,39 @@ import React from "react"; +import ReactDOM from "react-dom"; + +import "./Dialog.css"; export interface DialogProps { onClose: () => void; open?: boolean; children?: React.ReactNode; + disableCloseOnClickOnOverlay?: boolean; } export default function Dialog(props: DialogProps): React.ReactElement | null { - const { open, onClose, children } = props; + const { open, onClose, children, disableCloseOnClickOnOverlay } = props; - return
{children}
; + return open + ? ReactDOM.createPortal( +
{ + onClose(); + } + } + > +
e.stopPropagation()} + > + {children} +
+
, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + document.getElementById("portal")! + ) + : null; } diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index aaaaad58..e2769fe4 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -1,3 +1,152 @@ +:root { + --cru-primary-color: rgb(255, 105, 0); + --cru-primary-l1-color: rgb(255, 120, 26); + --cru-primary-l2-color: rgb(255, 135, 51); + --cru-primary-l3-color: rgb(255, 150, 77); + --cru-primary-d1-color: rgb(230, 95, 0); + --cru-primary-d2-color: rgb(204, 84, 0); + --cru-primary-d3-color: rgb(179, 74, 0); + --cru-primary-f1-color: rgb(230, 95, 0); + --cru-primary-f2-color: rgb(204, 84, 0); + --cru-primary-f3-color: rgb(179, 74, 0); + --cru-primary-r1-color: rgb(255, 120, 26); + --cru-primary-r2-color: rgb(255, 135, 51); + --cru-primary-r3-color: rgb(255, 150, 77); + --cru-primary-enhance-color: rgb(255, 150, 77); + --cru-primary-enhance-l1-color: rgb(255, 161, 94); + --cru-primary-enhance-l2-color: rgb(255, 171, 112); + --cru-primary-enhance-l3-color: rgb(255, 182, 130); + --cru-primary-enhance-d1-color: rgb(255, 131, 43); + --cru-primary-enhance-d2-color: rgb(255, 111, 10); + --cru-primary-enhance-d3-color: rgb(232, 96, 0); + --cru-primary-enhance-f1-color: rgb(255, 161, 94); + --cru-primary-enhance-f2-color: rgb(255, 171, 112); + --cru-primary-enhance-f3-color: rgb(255, 182, 130); + --cru-primary-enhance-r1-color: rgb(255, 131, 43); + --cru-primary-enhance-r2-color: rgb(255, 111, 10); + --cru-primary-enhance-r3-color: rgb(232, 96, 0); + --cru-secondary-color: rgb(23, 255, 0); + --cru-secondary-l1-color: rgb(46, 255, 26); + --cru-secondary-l2-color: rgb(69, 255, 51); + --cru-secondary-l3-color: rgb(92, 255, 77); + --cru-secondary-d1-color: rgb(20, 230, 0); + --cru-secondary-d2-color: rgb(18, 204, 0); + --cru-secondary-d3-color: rgb(16, 179, 0); + --cru-secondary-f1-color: rgb(20, 230, 0); + --cru-secondary-f2-color: rgb(18, 204, 0); + --cru-secondary-f3-color: rgb(16, 179, 0); + --cru-secondary-r1-color: rgb(46, 255, 26); + --cru-secondary-r2-color: rgb(69, 255, 51); + --cru-secondary-r3-color: rgb(92, 255, 77); + --cru-text-primary-color: rgb(17, 17, 17); + --cru-text-primary-l1-color: rgb(41, 41, 41); + --cru-text-primary-l2-color: rgb(65, 65, 65); + --cru-text-primary-l3-color: rgb(88, 88, 88); + --cru-text-primary-d1-color: rgb(15, 15, 15); + --cru-text-primary-d2-color: rgb(14, 14, 14); + --cru-text-primary-d3-color: rgb(12, 12, 12); + --cru-text-primary-f1-color: rgb(15, 15, 15); + --cru-text-primary-f2-color: rgb(14, 14, 14); + --cru-text-primary-f3-color: rgb(12, 12, 12); + --cru-text-primary-r1-color: rgb(41, 41, 41); + --cru-text-primary-r2-color: rgb(65, 65, 65); + --cru-text-primary-r3-color: rgb(88, 88, 88); + --cru-text-on-primary-color: rgb(255, 255, 255); + --cru-text-on-primary-l1-color: rgb(255, 255, 255); + --cru-text-on-primary-l2-color: rgb(255, 255, 255); + --cru-text-on-primary-l3-color: rgb(255, 255, 255); + --cru-text-on-primary-d1-color: rgb(230, 230, 230); + --cru-text-on-primary-d2-color: rgb(204, 204, 204); + --cru-text-on-primary-d3-color: rgb(179, 179, 179); + --cru-text-on-primary-f1-color: rgb(255, 255, 255); + --cru-text-on-primary-f2-color: rgb(255, 255, 255); + --cru-text-on-primary-f3-color: rgb(255, 255, 255); + --cru-text-on-primary-r1-color: rgb(230, 230, 230); + --cru-text-on-primary-r2-color: rgb(204, 204, 204); + --cru-text-on-primary-r3-color: rgb(179, 179, 179); + --cru-text-on-primary-enhance-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-l1-color: rgb(26, 26, 26); + --cru-text-on-primary-enhance-l2-color: rgb(51, 51, 51); + --cru-text-on-primary-enhance-l3-color: rgb(77, 77, 77); + --cru-text-on-primary-enhance-d1-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-d2-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-d3-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-f1-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-f2-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-f3-color: rgb(0, 0, 0); + --cru-text-on-primary-enhance-r1-color: rgb(26, 26, 26); + --cru-text-on-primary-enhance-r2-color: rgb(51, 51, 51); + --cru-text-on-primary-enhance-r3-color: rgb(77, 77, 77); + --cru-text-on-secondary-color: rgb(255, 255, 255); + --cru-text-on-secondary-l1-color: rgb(255, 255, 255); + --cru-text-on-secondary-l2-color: rgb(255, 255, 255); + --cru-text-on-secondary-l3-color: rgb(255, 255, 255); + --cru-text-on-secondary-d1-color: rgb(230, 230, 230); + --cru-text-on-secondary-d2-color: rgb(204, 204, 204); + --cru-text-on-secondary-d3-color: rgb(179, 179, 179); + --cru-text-on-secondary-f1-color: rgb(255, 255, 255); + --cru-text-on-secondary-f2-color: rgb(255, 255, 255); + --cru-text-on-secondary-f3-color: rgb(255, 255, 255); + --cru-text-on-secondary-r1-color: rgb(230, 230, 230); + --cru-text-on-secondary-r2-color: rgb(204, 204, 204); + --cru-text-on-secondary-r3-color: rgb(179, 179, 179); + --cru-danger-color: rgb(255, 0, 0); + --cru-danger-l1-color: rgb(255, 26, 26); + --cru-danger-l2-color: rgb(255, 51, 51); + --cru-danger-l3-color: rgb(255, 77, 77); + --cru-danger-d1-color: rgb(230, 0, 0); + --cru-danger-d2-color: rgb(204, 0, 0); + --cru-danger-d3-color: rgb(179, 0, 0); + --cru-danger-f1-color: rgb(230, 0, 0); + --cru-danger-f2-color: rgb(204, 0, 0); + --cru-danger-f3-color: rgb(179, 0, 0); + --cru-danger-r1-color: rgb(255, 26, 26); + --cru-danger-r2-color: rgb(255, 51, 51); + --cru-danger-r3-color: rgb(255, 77, 77); + --cru-success-color: rgb(0, 128, 0); + --cru-success-l1-color: rgb(0, 166, 0); + --cru-success-l2-color: rgb(0, 204, 0); + --cru-success-l3-color: rgb(0, 243, 0); + --cru-success-d1-color: rgb(0, 115, 0); + --cru-success-d2-color: rgb(0, 102, 0); + --cru-success-d3-color: rgb(0, 90, 0); + --cru-success-f1-color: rgb(0, 115, 0); + --cru-success-f2-color: rgb(0, 102, 0); + --cru-success-f3-color: rgb(0, 90, 0); + --cru-success-r1-color: rgb(0, 166, 0); + --cru-success-r2-color: rgb(0, 204, 0); + --cru-success-r3-color: rgb(0, 243, 0); +} + +.tl-color-primary { + color: var(--cru-primary-color); +} + +.tl-color-secondary { + color: var(--cru-secondary-color); +} + +.tl-color-success { + color: var(--cru-success-color); +} + +.tl-color-danger { + color: var(--cru-danger-color); +} + +.icon-button { + font-size: 1.4rem; + cursor: pointer; +} + +.icon-button.large { + font-size: 1.6rem; +} + +.icon-button.primary-enhance { + color: var(--cru-primary-enhance-color); +} + .cru-avatar { width: 60px; height: 60px; @@ -49,7 +198,7 @@ left: 0; right: 0; z-index: 1; - background-color: var(--tl-primary-color); + background-color: var(--cru-primary-color); display: flex; align-items: center; } @@ -58,80 +207,6 @@ overflow: scroll; } -.cru-menu { - min-width: 200px; -} - -.cru-menu-item { - font-size: 1.2em; - padding: 0.5em 1.5em; - cursor: pointer; -} -.cru-menu-item.color-primary { - color: #0d6efd; -} -.cru-menu-item.color-primary:hover { - color: white; - background-color: #0d6efd; -} -.cru-menu-item.color-secondary { - color: #6c757d; -} -.cru-menu-item.color-secondary:hover { - color: white; - background-color: #6c757d; -} -.cru-menu-item.color-success { - color: #198754; -} -.cru-menu-item.color-success:hover { - color: white; - background-color: #198754; -} -.cru-menu-item.color-info { - color: #0dcaf0; -} -.cru-menu-item.color-info:hover { - color: white; - background-color: #0dcaf0; -} -.cru-menu-item.color-warning { - color: #ffc107; -} -.cru-menu-item.color-warning:hover { - color: white; - background-color: #ffc107; -} -.cru-menu-item.color-danger { - color: #dc3545; -} -.cru-menu-item.color-danger:hover { - color: white; - background-color: #dc3545; -} -.cru-menu-item.color-light { - color: #f8f9fa; -} -.cru-menu-item.color-light:hover { - color: white; - background-color: #f8f9fa; -} -.cru-menu-item.color-dark { - color: #212529; -} -.cru-menu-item.color-dark:hover { - color: white; - background-color: #212529; -} - -.cru-menu-item-icon { - margin-right: 1em; -} - -.cru-menu-divider { - border-top: 1px solid #e9ecef; -} - .cru-tab-pages-action-area { display: flex; align-items: center; diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css new file mode 100644 index 00000000..0b455baa --- /dev/null +++ b/FrontEnd/src/views/common/menu/Menu.css @@ -0,0 +1,35 @@ +.cru-menu { + min-width: 200px; +} + +.cru-menu-item { + font-size: 1.2em; + padding: 0.5em 1.5em; + cursor: pointer; +} + +.cru-menu-item.color-primary { + color: var(--cru-primary-color); +} + +.cru-menu-item.color-primary:hover { + color: var(--cru-text-on-primary-color); + background-color: var(--cru-primary-color); +} + +.cru-menu-item.color-secondary { + color: var(--cru-secondary-color); +} + +.cru-menu-item.color-secondary:hover { + color: var(--cru-text-on-secondary-color); + background-color: var(--cru-secondary-color); +} + +.cru-menu-item-icon { + margin-right: 1em; +} + +.cru-menu-divider { + border-top: 1px solid #e9ecef; +} diff --git a/FrontEnd/src/views/common/menu/Menu.tsx b/FrontEnd/src/views/common/menu/Menu.tsx new file mode 100644 index 00000000..7523247d --- /dev/null +++ b/FrontEnd/src/views/common/menu/Menu.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import classnames from "classnames"; +import { useTranslation } from "react-i18next"; + +import { convertI18nText, I18nText } from "@/common"; +import { PaletteColorType } from "@/palette"; + +export type MenuItem = + | { + type: "divider"; + } + | { + type: "button"; + text: I18nText; + iconClassName?: string; + color?: PaletteColorType; + onClick: () => void; + }; + +export type MenuItems = MenuItem[]; + +export interface MenuProps { + items: MenuItems; + className?: string; + onItemClicked?: () => void; +} + +const Menu: React.FC = ({ items, className, onItemClicked }) => { + const { t } = useTranslation(); + + return ( +
+ {items.map((item, index) => { + if (item.type === "divider") { + return
; + } else { + return ( +
{ + item.onClick(); + onItemClicked?.(); + }} + > + {item.iconClassName != null ? ( + + ) : null} + {convertI18nText(item.text, t)} +
+ ); + } + })} +
+ ); +}; + +export default Menu; diff --git a/FrontEnd/src/views/common/menu/PopupMenu.tsx b/FrontEnd/src/views/common/menu/PopupMenu.tsx new file mode 100644 index 00000000..0d447f09 --- /dev/null +++ b/FrontEnd/src/views/common/menu/PopupMenu.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +import Menu, { MenuItems } from "./Menu"; + +export interface PopupMenuProps { + items: MenuItems; + children: React.ReactElement; +} + +export const PopupMenu: React.FC = ({ items, children }) => { + const [show, setShow] = React.useState(false); + const toggle = (): void => setShow(!show); + + // TODO: + + return setShow(false)} />; +}; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 78057be2..78af1131 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -17,7 +17,7 @@ import CollapseButton from "./CollapseButton"; import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import ConnectionStatusBadge from "./ConnectionStatusBadge"; -import { MenuItems, PopupMenu } from "../common/Menu"; +import { MenuItems, PopupMenu } from "../common/menu/Menu"; import FullPageDialog from "../common/dailog/FullPageDialog"; import Card from "../common/Card"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx index 13aacb54..1e6591c0 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx @@ -16,7 +16,7 @@ import { base64 } from "@/http/common"; import BlobImage from "../common/BlobImage"; import LoadingButton from "../common/button/LoadingButton"; -import { PopupMenu } from "../common/Menu"; +import { PopupMenu } from "../common/menu/Menu"; import Card from "../common/Card"; import MarkdownPostEdit from "./MarkdownPostEdit"; import TimelineLine from "./TimelineLine"; diff --git a/FrontEnd/src/views/timeline-common/index.css b/FrontEnd/src/views/timeline-common/index.css index b78564a3..70a1646d 100644 --- a/FrontEnd/src/views/timeline-common/index.css +++ b/FrontEnd/src/views/timeline-common/index.css @@ -13,19 +13,19 @@ @keyframes timeline-line-node-noncurrent { to { - box-shadow: 0 0 20px 3px var(--tl-primary-l1-color); + box-shadow: 0 0 20px 3px var(--cru-primary-l1-color); } } @keyframes timeline-line-node-current { to { - box-shadow: 0 0 20px 3px var(--tl-primary-enhance-l1-color); + box-shadow: 0 0 20px 3px var(--cru-primary-enhance-l1-color); } } @keyframes timeline-line-node-loading { to { - box-shadow: 0 0 20px 3px var(--tl-primary-l1-color); + box-shadow: 0 0 20px 3px var(--cru-primary-l1-color); } } @@ -79,7 +79,7 @@ .timeline-line .segment { width: 7px; - background: var(--tl-primary-color); + background: var(--cru-primary-color); } .timeline-line .segment.start { height: 1.8em; @@ -91,7 +91,7 @@ .timeline-line .segment.current-end { height: 2em; flex: 0 0 auto; - background: linear-gradient(var(--tl-primary-enhance-color), white); + background: linear-gradient(var(--cru-primary-enhance-color), white); } .timeline-line .node-container { flex: 0 0 auto; @@ -103,7 +103,7 @@ width: 20px; height: 20px; position: absolute; - background: var(--tl-primary-color); + background: var(--cru-primary-color); left: -1px; top: -1px; border-radius: 50%; @@ -113,7 +113,7 @@ animation-name: timeline-line-node-noncurrent; } .timeline-line .node-loading-edge { - color: var(--tl-primary-color); + color: var(--cru-primary-color); width: 38px; height: 38px; position: absolute; @@ -125,22 +125,22 @@ } .timeline-line.current .segment.start { background: linear-gradient( - var(--tl-primary-color), - var(--tl-primary-enhance-color) + var(--cru-primary-color), + var(--cru-primary-enhance-color) ); } .timeline-line.current .segment.end { - background: var(--tl-primary-enhance-color); + background: var(--cru-primary-enhance-color); } .timeline-line.current .node { - background: var(--tl-primary-enhance-color); + background: var(--cru-primary-enhance-color); animation-name: timeline-line-node-current; } .timeline-line.loading .node { - background: var(--tl-primary-color); + background: var(--cru-primary-color); animation-name: timeline-line-node-loading; } -- cgit v1.2.3 From 6b5d88a6a1a3fe12721f3159a6837d3f41d69022 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 00:15:20 +0800 Subject: ... --- FrontEnd/package.json | 2 + FrontEnd/src/views/common/menu/Menu.tsx | 22 +++++---- FrontEnd/src/views/common/menu/PopupMenu.css | 3 ++ FrontEnd/src/views/common/menu/PopupMenu.tsx | 56 ++++++++++++++++++++-- .../timeline-common/TimelinePageCardTemplate.tsx | 3 +- .../src/views/timeline-common/TimelinePostEdit.tsx | 2 +- 6 files changed, 73 insertions(+), 15 deletions(-) create mode 100644 FrontEnd/src/views/common/menu/PopupMenu.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/package.json b/FrontEnd/package.json index 55c7c95f..2c92d6b2 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -7,6 +7,7 @@ "description": "Timeline app.", "dependencies": { "@microsoft/signalr": "^5.0.7", + "@popperjs/core": "^2.9.2", "axios": "^0.21.1", "bootstrap": "^5.0.2", "bootstrap-icons": "^1.5.0", @@ -23,6 +24,7 @@ "react-color": "^2.19.3", "react-dom": "^17.0.2", "react-i18next": "^11.11.0", + "react-popper": "^2.2.5", "react-responsive": "^8.2.0", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", diff --git a/FrontEnd/src/views/common/menu/Menu.tsx b/FrontEnd/src/views/common/menu/Menu.tsx index 7523247d..04243fa3 100644 --- a/FrontEnd/src/views/common/menu/Menu.tsx +++ b/FrontEnd/src/views/common/menu/Menu.tsx @@ -5,6 +5,8 @@ import { useTranslation } from "react-i18next"; import { convertI18nText, I18nText } from "@/common"; import { PaletteColorType } from "@/palette"; +import "./Menu.css"; + export type MenuItem = | { type: "divider"; @@ -19,17 +21,23 @@ export type MenuItem = export type MenuItems = MenuItem[]; -export interface MenuProps { +export type MenuProps = { items: MenuItems; - className?: string; onItemClicked?: () => void; -} + className?: string; + style?: React.CSSProperties; +}; -const Menu: React.FC = ({ items, className, onItemClicked }) => { +export default function _Menu({ + items, + onItemClicked, + className, + style, +}: MenuProps): React.ReactElement | null { const { t } = useTranslation(); return ( -
+
{items.map((item, index) => { if (item.type === "divider") { return
; @@ -61,6 +69,4 @@ const Menu: React.FC = ({ items, className, onItemClicked }) => { })}
); -}; - -export default Menu; +} diff --git a/FrontEnd/src/views/common/menu/PopupMenu.css b/FrontEnd/src/views/common/menu/PopupMenu.css new file mode 100644 index 00000000..8465a1bb --- /dev/null +++ b/FrontEnd/src/views/common/menu/PopupMenu.css @@ -0,0 +1,3 @@ +.cru-popup-menu-menu-container { + +} diff --git a/FrontEnd/src/views/common/menu/PopupMenu.tsx b/FrontEnd/src/views/common/menu/PopupMenu.tsx index 0d447f09..50f80a91 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.tsx +++ b/FrontEnd/src/views/common/menu/PopupMenu.tsx @@ -1,17 +1,63 @@ +import classNames from "classnames"; import React from "react"; +import { usePopper } from "react-popper"; import Menu, { MenuItems } from "./Menu"; +import "./PopupMenu.css"; + export interface PopupMenuProps { items: MenuItems; - children: React.ReactElement; + children?: React.ReactNode; + containerClassName?: string; + containerStyle?: React.CSSProperties; } -export const PopupMenu: React.FC = ({ items, children }) => { +const PopupMenu: React.FC = ({ + items, + children, + containerClassName, + containerStyle, +}) => { const [show, setShow] = React.useState(false); - const toggle = (): void => setShow(!show); - // TODO: + const [referenceElement, setReferenceElement] = + React.useState(null); + const [popperElement, setPopperElement] = + React.useState(null); + const [arrowElement, setArrowElement] = React.useState( + null + ); + const { styles, attributes } = usePopper(referenceElement, popperElement, { + modifiers: [{ name: "arrow", options: { element: arrowElement } }], + }); - return setShow(false)} />; + return ( + <> +
setShow(true)} + > + {children} +
+ {show ? ( +
+ +
+
+ ) : null} + + ); }; + +export default PopupMenu; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 78af1131..75f2d400 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -17,7 +17,8 @@ import CollapseButton from "./CollapseButton"; import { TimelineMemberDialog } from "./TimelineMember"; import TimelinePropertyChangeDialog from "./TimelinePropertyChangeDialog"; import ConnectionStatusBadge from "./ConnectionStatusBadge"; -import { MenuItems, PopupMenu } from "../common/menu/Menu"; +import { MenuItems } from "../common/menu/Menu"; +import PopupMenu from "../common/menu/PopupMenu"; import FullPageDialog from "../common/dailog/FullPageDialog"; import Card from "../common/Card"; diff --git a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx index 1e6591c0..e2045429 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostEdit.tsx @@ -16,7 +16,7 @@ import { base64 } from "@/http/common"; import BlobImage from "../common/BlobImage"; import LoadingButton from "../common/button/LoadingButton"; -import { PopupMenu } from "../common/menu/Menu"; +import PopupMenu from "../common/menu/PopupMenu"; import Card from "../common/Card"; import MarkdownPostEdit from "./MarkdownPostEdit"; import TimelineLine from "./TimelineLine"; -- cgit v1.2.3 From 979ad8556be3576b09e318c9e85ae0138ecf11ec Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 18:18:04 +0800 Subject: ... --- FrontEnd/src/views/common/index.css | 2 ++ FrontEnd/src/views/common/menu/PopupMenu.css | 4 +++- FrontEnd/src/views/common/menu/PopupMenu.tsx | 32 +++++++++++++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index e2769fe4..cb1cccd4 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -1,4 +1,6 @@ :root { + --cru-background-color: #f8f9fa; + --cru-primary-color: rgb(255, 105, 0); --cru-primary-l1-color: rgb(255, 120, 26); --cru-primary-l2-color: rgb(255, 135, 51); diff --git a/FrontEnd/src/views/common/menu/PopupMenu.css b/FrontEnd/src/views/common/menu/PopupMenu.css index 8465a1bb..8f8fd20e 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.css +++ b/FrontEnd/src/views/common/menu/PopupMenu.css @@ -1,3 +1,5 @@ .cru-popup-menu-menu-container { - + border-radius: 5px; + border: var(--cru-primary-color) 1px solid; + background-color: var(--cru-background-color); } diff --git a/FrontEnd/src/views/common/menu/PopupMenu.tsx b/FrontEnd/src/views/common/menu/PopupMenu.tsx index 50f80a91..851f3bee 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.tsx +++ b/FrontEnd/src/views/common/menu/PopupMenu.tsx @@ -25,12 +25,24 @@ const PopupMenu: React.FC = ({ React.useState(null); const [popperElement, setPopperElement] = React.useState(null); - const [arrowElement, setArrowElement] = React.useState( - null - ); - const { styles, attributes } = usePopper(referenceElement, popperElement, { - modifiers: [{ name: "arrow", options: { element: arrowElement } }], - }); + const { styles, attributes } = usePopper(referenceElement, popperElement); + + React.useEffect(() => { + const handler = (event: MouseEvent): void => { + let element: HTMLElement | null = event.target as HTMLElement; + while (element) { + if (element == referenceElement || element == popperElement) { + return; + } + element = element.parentElement; + } + setShow(false); + }; + document.addEventListener("click", handler); + return () => { + document.removeEventListener("click", handler); + }; + }, [referenceElement, popperElement]); return ( <> @@ -52,8 +64,12 @@ const PopupMenu: React.FC = ({ style={styles.popper} {...attributes.popper} > - -
+ { + setShow(false); + }} + />
) : null} -- cgit v1.2.3 From 839daa0eac6fdbe84f45e8572e5ab07126b7d67c Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 18:36:47 +0800 Subject: ... --- FrontEnd/src/views/about/index.tsx | 6 +-- .../src/views/common/dailog/FullPageDialog.css | 26 ++++++++++++ .../src/views/common/dailog/FullPageDialog.tsx | 9 ++++- FrontEnd/src/views/common/index.css | 47 ++++++++-------------- .../src/views/timeline-common/CollapseButton.tsx | 2 +- .../timeline-common/TimelinePageCardTemplate.tsx | 18 ++++----- FrontEnd/src/views/timeline/TimelineCard.tsx | 2 +- FrontEnd/src/views/user/UserCard.tsx | 2 +- 8 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 FrontEnd/src/views/common/dailog/FullPageDialog.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/about/index.tsx b/FrontEnd/src/views/about/index.tsx index 8a358014..11618086 100644 --- a/FrontEnd/src/views/about/index.tsx +++ b/FrontEnd/src/views/about/index.tsx @@ -77,11 +77,11 @@ const AboutPage: React.FC = () => {

{t("about.author.fullname")} - 杨宇千 + 杨宇千

{t("about.author.nickname")} - crupest + crupest

{t("about.author.introduction")} @@ -105,7 +105,7 @@ const AboutPage: React.FC = () => {

{t("about.site.title")}

- 01234 + 01234 56

diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.css b/FrontEnd/src/views/common/dailog/FullPageDialog.css new file mode 100644 index 00000000..65f16041 --- /dev/null +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.css @@ -0,0 +1,26 @@ +.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; +} diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx index 88c90bbc..72c9b269 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx @@ -1,6 +1,9 @@ import React from "react"; +import { createPortal } from "react-dom"; import classnames from "classnames"; +import "./FullPageDialog.css"; + export interface FullPageDialogProps { show: boolean; onBack: () => void; @@ -13,7 +16,7 @@ const FullPageDialog: React.FC = ({ children, contentContainerClassName, }) => { - return ( + return createPortal(
= ({ > {children}
-
+
, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + document.getElementById("portal")! ); }; diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index cb1cccd4..d7501f98 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -120,22 +120,34 @@ --cru-success-r3-color: rgb(0, 243, 0); } -.tl-color-primary { +.cru-color-primary { color: var(--cru-primary-color); } -.tl-color-secondary { +.cru-color-secondary { color: var(--cru-secondary-color); } -.tl-color-success { +.cru-color-success { color: var(--cru-success-color); } -.tl-color-danger { +.cru-color-danger { color: var(--cru-danger-color); } +.cru-text-end { + text-align: end; +} + +.cru-float-right { + float: right; +} + +.cru-clearfix::after { + clear: both; +} + .icon-button { font-size: 1.4rem; cursor: pointer; @@ -182,33 +194,6 @@ width: 50%; } -.cru-full-page { - position: fixed; - z-index: 1031; - 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-tab-pages-action-area { display: flex; align-items: center; diff --git a/FrontEnd/src/views/timeline-common/CollapseButton.tsx b/FrontEnd/src/views/timeline-common/CollapseButton.tsx index 12a3b710..31976228 100644 --- a/FrontEnd/src/views/timeline-common/CollapseButton.tsx +++ b/FrontEnd/src/views/timeline-common/CollapseButton.tsx @@ -12,7 +12,7 @@ const CollapseButton: React.FC<{ onClick={onClick} className={classnames( collapse ? "bi-arrows-angle-expand" : "bi-arrows-angle-contract", - "text-primary icon-button", + "cru-color-primary icon-button", className )} style={style} diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 75f2d400..f57fda2e 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -54,11 +54,11 @@ const TimelinePageCardTemplate: React.FC = ({ {t(timelineVisibilityTooltipTranslationMap[timeline.visibility])} -
+
= ({ { getHttpBookmarkClient() @@ -98,12 +98,12 @@ const TimelinePageCardTemplate: React.FC = ({ /> ) : null} setDialog("member")} /> {manageItems != null ? ( - - + + ) : null}
@@ -113,10 +113,10 @@ const TimelinePageCardTemplate: React.FC = ({ return ( <> -
+
@@ -129,7 +129,7 @@ const TimelinePageCardTemplate: React.FC = ({ {content} ) : ( -
{content}
+
{content}
)} {(() => { diff --git a/FrontEnd/src/views/timeline/TimelineCard.tsx b/FrontEnd/src/views/timeline/TimelineCard.tsx index 86063843..311aa112 100644 --- a/FrontEnd/src/views/timeline/TimelineCard.tsx +++ b/FrontEnd/src/views/timeline/TimelineCard.tsx @@ -18,7 +18,7 @@ const TimelineCard: React.FC = (props) => { -

+

{timeline.title} {timeline.name}

diff --git a/FrontEnd/src/views/user/UserCard.tsx b/FrontEnd/src/views/user/UserCard.tsx index 06bb2143..8a532512 100644 --- a/FrontEnd/src/views/user/UserCard.tsx +++ b/FrontEnd/src/views/user/UserCard.tsx @@ -16,7 +16,7 @@ const UserCard: React.FC = (props) => { -

+

{timeline.title} {timeline.name}

-- cgit v1.2.3 From bb7a017dba4c466eaf167627b3605cf539b1e516 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 19:00:13 +0800 Subject: ... --- FrontEnd/src/palette.ts | 6 +- FrontEnd/src/views/common/index.css | 78 +++++++++++----------- FrontEnd/src/views/common/menu/Menu.css | 2 +- FrontEnd/src/views/common/menu/PopupMenu.css | 1 + FrontEnd/src/views/common/menu/PopupMenu.tsx | 35 +++++----- .../timeline-common/TimelinePageCardTemplate.tsx | 2 +- FrontEnd/src/views/timeline/TimelineCard.tsx | 12 ++-- FrontEnd/src/views/user/UserCard.tsx | 8 ++- 8 files changed, 77 insertions(+), 67 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/palette.ts b/FrontEnd/src/palette.ts index fb54f042..ca1f76cf 100644 --- a/FrontEnd/src/palette.ts +++ b/FrontEnd/src/palette.ts @@ -90,7 +90,7 @@ export function generatePalette(options: { primaryEnhance == null ? lightenBy(p, 0.3).saturate(0.3) : Color(primaryEnhance); - const s = secondary == null ? p.rotate(90) : Color(secondary); + const s = secondary == null ? Color("gray") : Color(secondary); return { primary: generatePaletteColor(p.toString()), @@ -130,8 +130,8 @@ export function generatePaletteCSS(palette: Palette): string { const paletteSubject: BehaviorSubject = new BehaviorSubject( - generatePalette({ primary: "rgb(0, 123, 255)" }) - // null + // generatePalette({ primary: "rgb(0, 123, 255)" }) + null ); export const palette$: Observable = diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index d7501f98..4a860e95 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -1,45 +1,45 @@ :root { --cru-background-color: #f8f9fa; - --cru-primary-color: rgb(255, 105, 0); - --cru-primary-l1-color: rgb(255, 120, 26); - --cru-primary-l2-color: rgb(255, 135, 51); - --cru-primary-l3-color: rgb(255, 150, 77); - --cru-primary-d1-color: rgb(230, 95, 0); - --cru-primary-d2-color: rgb(204, 84, 0); - --cru-primary-d3-color: rgb(179, 74, 0); - --cru-primary-f1-color: rgb(230, 95, 0); - --cru-primary-f2-color: rgb(204, 84, 0); - --cru-primary-f3-color: rgb(179, 74, 0); - --cru-primary-r1-color: rgb(255, 120, 26); - --cru-primary-r2-color: rgb(255, 135, 51); - --cru-primary-r3-color: rgb(255, 150, 77); - --cru-primary-enhance-color: rgb(255, 150, 77); - --cru-primary-enhance-l1-color: rgb(255, 161, 94); - --cru-primary-enhance-l2-color: rgb(255, 171, 112); - --cru-primary-enhance-l3-color: rgb(255, 182, 130); - --cru-primary-enhance-d1-color: rgb(255, 131, 43); - --cru-primary-enhance-d2-color: rgb(255, 111, 10); - --cru-primary-enhance-d3-color: rgb(232, 96, 0); - --cru-primary-enhance-f1-color: rgb(255, 161, 94); - --cru-primary-enhance-f2-color: rgb(255, 171, 112); - --cru-primary-enhance-f3-color: rgb(255, 182, 130); - --cru-primary-enhance-r1-color: rgb(255, 131, 43); - --cru-primary-enhance-r2-color: rgb(255, 111, 10); - --cru-primary-enhance-r3-color: rgb(232, 96, 0); - --cru-secondary-color: rgb(23, 255, 0); - --cru-secondary-l1-color: rgb(46, 255, 26); - --cru-secondary-l2-color: rgb(69, 255, 51); - --cru-secondary-l3-color: rgb(92, 255, 77); - --cru-secondary-d1-color: rgb(20, 230, 0); - --cru-secondary-d2-color: rgb(18, 204, 0); - --cru-secondary-d3-color: rgb(16, 179, 0); - --cru-secondary-f1-color: rgb(20, 230, 0); - --cru-secondary-f2-color: rgb(18, 204, 0); - --cru-secondary-f3-color: rgb(16, 179, 0); - --cru-secondary-r1-color: rgb(46, 255, 26); - --cru-secondary-r2-color: rgb(69, 255, 51); - --cru-secondary-r3-color: rgb(92, 255, 77); + --cru-primary-color: rgb(0, 123, 255); + --cru-primary-l1-color: rgb(26, 136, 255); + --cru-primary-l2-color: rgb(51, 149, 255); + --cru-primary-l3-color: rgb(77, 163, 255); + --cru-primary-d1-color: rgb(0, 111, 230); + --cru-primary-d2-color: rgb(0, 98, 204); + --cru-primary-d3-color: rgb(0, 86, 179); + --cru-primary-f1-color: rgb(0, 111, 230); + --cru-primary-f2-color: rgb(0, 98, 204); + --cru-primary-f3-color: rgb(0, 86, 179); + --cru-primary-r1-color: rgb(26, 136, 255); + --cru-primary-r2-color: rgb(51, 149, 255); + --cru-primary-r3-color: rgb(77, 163, 255); + --cru-primary-enhance-color: rgb(77, 163, 255); + --cru-primary-enhance-l1-color: rgb(94, 172, 255); + --cru-primary-enhance-l2-color: rgb(112, 181, 255); + --cru-primary-enhance-l3-color: rgb(130, 190, 255); + --cru-primary-enhance-d1-color: rgb(43, 145, 255); + --cru-primary-enhance-d2-color: rgb(10, 128, 255); + --cru-primary-enhance-d3-color: rgb(0, 112, 232); + --cru-primary-enhance-f1-color: rgb(94, 172, 255); + --cru-primary-enhance-f2-color: rgb(112, 181, 255); + --cru-primary-enhance-f3-color: rgb(130, 190, 255); + --cru-primary-enhance-r1-color: rgb(43, 145, 255); + --cru-primary-enhance-r2-color: rgb(10, 128, 255); + --cru-primary-enhance-r3-color: rgb(0, 112, 232); + --cru-secondary-color: rgb(128, 128, 128); + --cru-secondary-l1-color: rgb(141, 141, 141); + --cru-secondary-l2-color: rgb(153, 153, 153); + --cru-secondary-l3-color: rgb(166, 166, 166); + --cru-secondary-d1-color: rgb(115, 115, 115); + --cru-secondary-d2-color: rgb(102, 102, 102); + --cru-secondary-d3-color: rgb(90, 90, 90); + --cru-secondary-f1-color: rgb(115, 115, 115); + --cru-secondary-f2-color: rgb(102, 102, 102); + --cru-secondary-f3-color: rgb(90, 90, 90); + --cru-secondary-r1-color: rgb(141, 141, 141); + --cru-secondary-r2-color: rgb(153, 153, 153); + --cru-secondary-r3-color: rgb(166, 166, 166); --cru-text-primary-color: rgb(17, 17, 17); --cru-text-primary-l1-color: rgb(41, 41, 41); --cru-text-primary-l2-color: rgb(65, 65, 65); diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css index 0b455baa..e0ea2cad 100644 --- a/FrontEnd/src/views/common/menu/Menu.css +++ b/FrontEnd/src/views/common/menu/Menu.css @@ -3,7 +3,7 @@ } .cru-menu-item { - font-size: 1.2em; + font-size: 1em; padding: 0.5em 1.5em; cursor: pointer; } diff --git a/FrontEnd/src/views/common/menu/PopupMenu.css b/FrontEnd/src/views/common/menu/PopupMenu.css index 8f8fd20e..658532d3 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.css +++ b/FrontEnd/src/views/common/menu/PopupMenu.css @@ -1,4 +1,5 @@ .cru-popup-menu-menu-container { + z-index: 1040; border-radius: 5px; border: var(--cru-primary-color) 1px solid; background-color: var(--cru-background-color); diff --git a/FrontEnd/src/views/common/menu/PopupMenu.tsx b/FrontEnd/src/views/common/menu/PopupMenu.tsx index 851f3bee..d7b81f49 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.tsx +++ b/FrontEnd/src/views/common/menu/PopupMenu.tsx @@ -1,5 +1,6 @@ import classNames from "classnames"; import React from "react"; +import { createPortal } from "react-dom"; import { usePopper } from "react-popper"; import Menu, { MenuItems } from "./Menu"; @@ -57,21 +58,25 @@ const PopupMenu: React.FC = ({ > {children}
- {show ? ( -
- { - setShow(false); - }} - /> -
- ) : null} + {show + ? createPortal( +
+ { + setShow(false); + }} + /> +
, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + document.getElementById("portal")! + ) + : null} ); }; diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index f57fda2e..4802ca93 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -23,7 +23,7 @@ import FullPageDialog from "../common/dailog/FullPageDialog"; import Card from "../common/Card"; export interface TimelineCardTemplateProps extends TimelinePageCardProps { - infoArea: React.ReactElement; + infoArea: React.ReactNode; manageItems?: MenuItems; dialog: string | "property" | "member" | null; setDialog: (dialog: "property" | "member" | null) => void; diff --git a/FrontEnd/src/views/timeline/TimelineCard.tsx b/FrontEnd/src/views/timeline/TimelineCard.tsx index 311aa112..56057560 100644 --- a/FrontEnd/src/views/timeline/TimelineCard.tsx +++ b/FrontEnd/src/views/timeline/TimelineCard.tsx @@ -20,16 +20,18 @@ const TimelineCard: React.FC = (props) => { <>

{timeline.title} - {timeline.name} + + {timeline.name} +

-
+
{timeline.owner.nickname} - - src{timeline.owner.username} + + @{timeline.owner.username}
diff --git a/FrontEnd/src/views/user/UserCard.tsx b/FrontEnd/src/views/user/UserCard.tsx index 8a532512..739d26ee 100644 --- a/FrontEnd/src/views/user/UserCard.tsx +++ b/FrontEnd/src/views/user/UserCard.tsx @@ -16,11 +16,13 @@ const UserCard: React.FC = (props) => { -

+

{timeline.title} - {timeline.name} + + {timeline.name} +

-
+
Date: Wed, 30 Jun 2021 19:09:23 +0800 Subject: ... --- FrontEnd/src/views/common/dailog/FullPageDialog.css | 4 ++++ FrontEnd/src/views/common/dailog/FullPageDialog.tsx | 2 +- FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx | 5 +---- FrontEnd/src/views/timeline-common/index.css | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.css b/FrontEnd/src/views/common/dailog/FullPageDialog.css index 65f16041..a7033398 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.css +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.css @@ -24,3 +24,7 @@ .cru-full-page-content-container { overflow: scroll; } + +.cru-full-page-back-button { + color: var(--cru-text-on-primary-color); +} diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx index 72c9b269..2e77dbb0 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.tsx +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.tsx @@ -23,7 +23,7 @@ const FullPageDialog: React.FC = ({ >
diff --git a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx index 4802ca93..5c2fb275 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePageCardTemplate.tsx @@ -112,10 +112,7 @@ const TimelinePageCardTemplate: React.FC = ({ return ( <> - +
diff --git a/FrontEnd/src/views/timeline-common/index.css b/FrontEnd/src/views/timeline-common/index.css index 70a1646d..6929f9ae 100644 --- a/FrontEnd/src/views/timeline-common/index.css +++ b/FrontEnd/src/views/timeline-common/index.css @@ -239,6 +239,7 @@ .timeline-template-card { position: fixed; + z-index: 1029; top: 56px; right: 0; margin: 0.5em; -- cgit v1.2.3 From fdc2d4a971d608bb230cd8aa1e602197c7775231 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 22:46:42 +0800 Subject: ... --- FrontEnd/src/views/center/TimelineBoard.tsx | 26 ++++++++++++++----------- FrontEnd/src/views/common/Spinner.css | 13 +++++++++++++ FrontEnd/src/views/common/Spinner.tsx | 30 ++++++++++++++++++++++++++--- FrontEnd/vite.config.js | 2 +- 4 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 FrontEnd/src/views/common/Spinner.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/center/TimelineBoard.tsx b/FrontEnd/src/views/center/TimelineBoard.tsx index d7aa39ab..3961a7bc 100644 --- a/FrontEnd/src/views/center/TimelineBoard.tsx +++ b/FrontEnd/src/views/center/TimelineBoard.tsx @@ -208,7 +208,8 @@ const TimelineBoardItemContainer: React.FC = ({ interface TimelineBoardUIProps { title?: string; - timelines: HttpTimelineInfo[] | "offline" | "loading"; + state: "offline" | "loading" | "loaded"; + timelines: HttpTimelineInfo[]; onReload: () => void; className?: string; editHandler?: { @@ -218,7 +219,7 @@ interface TimelineBoardUIProps { } const TimelineBoardUI: React.FC = (props) => { - const { title, timelines, className, editHandler } = props; + const { title, state, timelines, className, editHandler } = props; const editable = editHandler != null; @@ -246,13 +247,13 @@ const TimelineBoardUI: React.FC = (props) => { ))}
{(() => { - if (timelines === "loading") { + if (state === "loading") { return (
); - } else if (timelines === "offline") { + } else if (state === "offline") { return (
@@ -301,36 +302,39 @@ const TimelineBoard: React.FC = ({ load, editHandler, }) => { - const [timelines, setTimelines] = React.useState< - HttpTimelineInfo[] | "offline" | "loading" - >("loading"); + const [state, setState] = React.useState<"offline" | "loading" | "loaded">( + "loading" + ); + const [timelines, setTimelines] = React.useState([]); React.useEffect(() => { let subscribe = true; - if (timelines === "loading") { + if (state === "loading") { void load().then( (timelines) => { if (subscribe) { + setState("loaded"); setTimelines(timelines); } }, () => { - setTimelines("offline"); + setState("offline"); } ); } return () => { subscribe = false; }; - }, [load, timelines]); + }, [load, state]); return ( { - setTimelines("loading"); + setState("loaded"); }} editHandler={ typeof timelines === "object" && editHandler != null diff --git a/FrontEnd/src/views/common/Spinner.css b/FrontEnd/src/views/common/Spinner.css new file mode 100644 index 00000000..a1de68d2 --- /dev/null +++ b/FrontEnd/src/views/common/Spinner.css @@ -0,0 +1,13 @@ +@keyframes cru-spinner-animation { + from { + transform: scale(0,0); + } +} + +.cru-spinner { + display: inline-block; + animation: cru-spinner-animation 0.5s infinite alternate; + background-color: currentColor; + border-radius: 50%; + transform-origin: center; +} diff --git a/FrontEnd/src/views/common/Spinner.tsx b/FrontEnd/src/views/common/Spinner.tsx index 783c9be2..b591d8ab 100644 --- a/FrontEnd/src/views/common/Spinner.tsx +++ b/FrontEnd/src/views/common/Spinner.tsx @@ -1,13 +1,37 @@ -import { PaletteColorType } from "@/palette"; import React from "react"; +import classnames from "classnames"; + +import { PaletteColorType } from "@/palette"; + +import "./Spinner.css"; export interface SpinnerProps { - size?: "sm" | "md" | "lg" | number; + size?: "sm" | "md" | "lg" | number | string; color?: PaletteColorType; } export default function Spinner( props: SpinnerProps ): React.ReactElement | null { - return ; + const { size, color } = props; + const calculatedSize = + size === "sm" + ? "18px" + : size === "md" + ? "30px" + : size === "lg" + ? "42px" + : typeof size === "number" + ? size + : size == null + ? "20px" + : size; + const calculatedColor = color ?? "primary"; + + return ( + + ); } diff --git a/FrontEnd/vite.config.js b/FrontEnd/vite.config.js index 88529a33..64e0457f 100644 --- a/FrontEnd/vite.config.js +++ b/FrontEnd/vite.config.js @@ -22,7 +22,7 @@ export default defineConfig({ alias: [{ find: "@", replacement: "/src" }], }, server: { - port: 13000, + port: 10030, proxy: { "/api": { target: "http://localhost:5000", -- cgit v1.2.3 From 1d320f3e363d46900a420650360b68ba9a4f3f2f Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 23:02:41 +0800 Subject: ... --- FrontEnd/src/views/common/Spinner.tsx | 12 ++- FrontEnd/src/views/common/button/Button.css | 99 ++++++++++++---------- FrontEnd/src/views/common/button/LoadingButton.tsx | 11 ++- FrontEnd/src/views/common/index.css | 8 ++ 4 files changed, 80 insertions(+), 50 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/Spinner.tsx b/FrontEnd/src/views/common/Spinner.tsx index b591d8ab..4c735fef 100644 --- a/FrontEnd/src/views/common/Spinner.tsx +++ b/FrontEnd/src/views/common/Spinner.tsx @@ -8,12 +8,14 @@ import "./Spinner.css"; export interface SpinnerProps { size?: "sm" | "md" | "lg" | number | string; color?: PaletteColorType; + className?: string; + style?: React.CSSProperties; } export default function Spinner( props: SpinnerProps ): React.ReactElement | null { - const { size, color } = props; + const { size, color, className, style } = props; const calculatedSize = size === "sm" ? "18px" @@ -30,8 +32,12 @@ export default function Spinner( return ( ); } diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index 8b5b74a7..3e408d8d 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -1,72 +1,81 @@ -.cru-button { - color: white; - cursor: pointer; - padding: 0.2em 0.5em; - border-radius: 0.2em; - border: none; - transition: all 0.6s; -} - .cru-button.primary { - background-color: var(--cru-primary-color); -} - -.cru-button.primary:hover { - background-color: var(--cru-primary-f1-color); + --cru-button-color: var(--cru-primary-color); + --cru-button-f1-color: var(--cru-primary-f1-color); + --cru-button-f2-color: var(--cru-primary-f2-color); + --cru-button-f3-color: var(--cru-primary-f3-color); } -.cru-button.primary:active { - background-color: var(--cru-primary-f2-color); -} - -.cru-button.primary.disabled { - background-color: var(--cru-primary-f3-color); +.cru-button.primary-enhance { + --cru-button-color: var(--cru-primary-enhance-color); + --cru-button-f1-color: var(--cru-primary-enhance-f1-color); + --cru-button-f2-color: var(--cru-primary-enhance-f2-color); + --cru-button-f3-color: var(--cru-primary-enhance-f3-color); } .cru-button.secondary { - background-color: var(--cru-secondary-color); -} - -.cru-button.secondary:hover { - background-color: var(--cru-secondary-f1-color); + --cru-button-color: var(--cru-secondary-color); + --cru-button-f1-color: var(--cru-secondary-f1-color); + --cru-button-f2-color: var(--cru-secondary-f2-color); + --cru-button-f3-color: var(--cru-secondary-f3-color); } -.cru-button.secondary:active { - background-color: var(--cru-secondary-f2-color); +.cru-button.success { + --cru-button-color: var(--cru-success-color); + --cru-button-f1-color: var(--cru-success-f1-color); + --cru-button-f2-color: var(--cru-success-f2-color); + --cru-button-f3-color: var(--cru-success-f3-color); } -.cru-button.secondary.disabled { - background-color: var(--cru-secondary-f3-color); +.cru-button.danger { + --cru-button-color: var(--cru-danger-color); + --cru-button-f1-color: var(--cru-danger-f1-color); + --cru-button-f2-color: var(--cru-danger-f2-color); + --cru-button-f3-color: var(--cru-danger-f3-color); } -.cru-button.success { - background-color: var(--cru-success-color); +.cru-button:not(.outline) { + color: white; + cursor: pointer; + padding: 0.2em 0.5em; + border-radius: 0.2em; + border: none; + transition: all 0.5s; + background-color: var(--cru-button-color); } -.cru-button.success:hover { - background-color: var(--cru-success-f1-color); +.cru-button:not(.outline):hover { + background-color: var(--cru-button-f1-color); } -.cru-button.success:active { - background-color: var(--cru-success-f2-color); +.cru-button:not(.outline):active { + background-color: var(--cru-button-f2-color); } -.cru-button.success.disabled { - background-color: var(--cru-success-f3-color); +.cru-button:not(.outline):disabled { + background-color: var(--cru-button-f3-color); } -.cru-button.danger { - background-color: var(--cru-danger-color); +.cru-button.outline { + color: var(--cru-button-color); + border: var(--cru-button-color) 1px solid; + cursor: pointer; + padding: 0.2em 0.5em; + border-radius: 0.2em; + transition: all 0.6s; + background-color: white; } -.cru-button.danger:hover { - background-color: var(--cru-danger-f1-color); +.cru-button.outline:hover { + color: var(--cru-button-f1-color); + border-color: var(--cru-button-f1-color); } -.cru-button.danger:active { - background-color: var(--cru-danger-f2-color); +.cru-button.outline:active { + color: var(--cru-button-f2-color); + border-color: var(--cru-button-f2-color); } -.cru-button.danger.disabled { - background-color: var(--cru-danger-f3-color); +.cru-button.outline:disabled { + color: var(--cru-button-f3-color); + border-color: var(--cru-button-f3-color); } diff --git a/FrontEnd/src/views/common/button/LoadingButton.tsx b/FrontEnd/src/views/common/button/LoadingButton.tsx index aee83aa2..a7e34f91 100644 --- a/FrontEnd/src/views/common/button/LoadingButton.tsx +++ b/FrontEnd/src/views/common/button/LoadingButton.tsx @@ -11,9 +11,16 @@ const LoadingButton: React.FC<{ loading?: boolean } & CommonButtonProps> = ({ ...otherProps }) => { return ( - ); }; diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index 4a860e95..8eb24e1d 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -144,6 +144,14 @@ float: right; } +.cru-align-text-bottom { + vertical-align: text-bottom; +} + +.cru-align-middle { + vertical-align: middle; +} + .cru-clearfix::after { clear: both; } -- cgit v1.2.3 From 840f3bb6d5bcd69e7092373870ca9b9e2a12b1bf Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 23:13:08 +0800 Subject: ... --- FrontEnd/src/palette.ts | 28 ++++----- FrontEnd/src/views/common/AppBar.css | 8 +-- FrontEnd/src/views/common/button/Button.css | 7 ++- .../src/views/common/dailog/FullPageDialog.css | 2 +- FrontEnd/src/views/common/index.css | 72 ++++++---------------- FrontEnd/src/views/common/menu/Menu.css | 2 +- 6 files changed, 46 insertions(+), 73 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/palette.ts b/FrontEnd/src/palette.ts index ca1f76cf..dedad5e2 100644 --- a/FrontEnd/src/palette.ts +++ b/FrontEnd/src/palette.ts @@ -27,6 +27,10 @@ export interface PaletteColor { r1: string; r2: string; r3: string; + t: string; + t1: string; + t2: string; + t3: string; [key: string]: string; } @@ -34,10 +38,6 @@ const paletteColorList = [ "primary", "primary-enhance", "secondary", - "text-primary", - "text-on-primary", - "text-on-primary-enhance", - "text-on-secondary", "danger", "success", ] as const; @@ -61,6 +61,12 @@ export function generatePaletteColor(color: string): PaletteColor { const r1 = light ? d1 : l1; const r2 = light ? d2 : l2; const r3 = light ? d3 : l3; + const _t = light ? Color("black") : Color("white"); + const t = _t.rgb().toString(); + const _b = light ? lightenBy : darkenBy; + const t1 = _b(_t, 0.1).rgb().toString(); + const t2 = _b(_t, 0.2).rgb().toString(); + const t3 = _b(_t, 0.3).rgb().toString(); return { color: c.rgb().toString(), @@ -76,6 +82,10 @@ export function generatePaletteColor(color: string): PaletteColor { r1, r2, r3, + t, + t1, + t2, + t3, }; } @@ -96,16 +106,6 @@ export function generatePalette(options: { primary: generatePaletteColor(p.toString()), "primary-enhance": generatePaletteColor(pe.toString()), secondary: generatePaletteColor(s.toString()), - "text-primary": generatePaletteColor("#111111"), - "text-on-primary": generatePaletteColor( - p.lightness() > 60 ? "black" : "white" - ), - "text-on-primary-enhance": generatePaletteColor( - pe.lightness() > 60 ? "black" : "white" - ), - "text-on-secondary": generatePaletteColor( - s.lightness() > 60 ? "black" : "white" - ), danger: generatePaletteColor("red"), success: generatePaletteColor("green"), }; diff --git a/FrontEnd/src/views/common/AppBar.css b/FrontEnd/src/views/common/AppBar.css index e4bdf852..3ec4fa36 100644 --- a/FrontEnd/src/views/common/AppBar.css +++ b/FrontEnd/src/views/common/AppBar.css @@ -16,16 +16,16 @@ } .app-bar a { - color: var(--cru-text-on-primary-r1-color); + color: var(--cru-primary-t1-color); text-decoration: none; margin: 0 1em; transition: color 1s; } .app-bar a:hover { - color: var(--cru-text-on-primary-color); + color: var(--cru-primary-t-color); } .app-bar a.active { - color: var(--cru-text-on-primary-color); + color: var(--cru-primary-t-color); } .app-bar-brand { @@ -89,7 +89,7 @@ margin-left: auto; font-size: 2em; margin-right: 1em; - color: var(--cru-text-on-primary-color); + color: var(--cru-primary-t-color); cursor: pointer; user-select: none; } diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index 3e408d8d..11d211c5 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -1,5 +1,6 @@ .cru-button.primary { --cru-button-color: var(--cru-primary-color); + --cru-button-t-color: var(--cru-primary-t-color); --cru-button-f1-color: var(--cru-primary-f1-color); --cru-button-f2-color: var(--cru-primary-f2-color); --cru-button-f3-color: var(--cru-primary-f3-color); @@ -7,6 +8,7 @@ .cru-button.primary-enhance { --cru-button-color: var(--cru-primary-enhance-color); + --cru-button-t-color: var(--cru-primary-enhance-t-color); --cru-button-f1-color: var(--cru-primary-enhance-f1-color); --cru-button-f2-color: var(--cru-primary-enhance-f2-color); --cru-button-f3-color: var(--cru-primary-enhance-f3-color); @@ -14,6 +16,7 @@ .cru-button.secondary { --cru-button-color: var(--cru-secondary-color); + --cru-button-t-color: var(--cru-secondary-t-color); --cru-button-f1-color: var(--cru-secondary-f1-color); --cru-button-f2-color: var(--cru-secondary-f2-color); --cru-button-f3-color: var(--cru-secondary-f3-color); @@ -21,6 +24,7 @@ .cru-button.success { --cru-button-color: var(--cru-success-color); + --cru-button-t-color: var(--cru-success-t-color); --cru-button-f1-color: var(--cru-success-f1-color); --cru-button-f2-color: var(--cru-success-f2-color); --cru-button-f3-color: var(--cru-success-f3-color); @@ -28,13 +32,14 @@ .cru-button.danger { --cru-button-color: var(--cru-danger-color); + --cru-button-t-color: var(--cru-danger-t-color); --cru-button-f1-color: var(--cru-danger-f1-color); --cru-button-f2-color: var(--cru-danger-f2-color); --cru-button-f3-color: var(--cru-danger-f3-color); } .cru-button:not(.outline) { - color: white; + color: var(--cru-button-t-color); cursor: pointer; padding: 0.2em 0.5em; border-radius: 0.2em; diff --git a/FrontEnd/src/views/common/dailog/FullPageDialog.css b/FrontEnd/src/views/common/dailog/FullPageDialog.css index a7033398..a196981c 100644 --- a/FrontEnd/src/views/common/dailog/FullPageDialog.css +++ b/FrontEnd/src/views/common/dailog/FullPageDialog.css @@ -26,5 +26,5 @@ } .cru-full-page-back-button { - color: var(--cru-text-on-primary-color); + color: var(--cru-primary-t-color); } diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index 8eb24e1d..75ec6485 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -14,6 +14,10 @@ --cru-primary-r1-color: rgb(26, 136, 255); --cru-primary-r2-color: rgb(51, 149, 255); --cru-primary-r3-color: rgb(77, 163, 255); + --cru-primary-t-color: rgb(255, 255, 255); + --cru-primary-t1-color: rgb(230, 230, 230); + --cru-primary-t2-color: rgb(204, 204, 204); + --cru-primary-t3-color: rgb(179, 179, 179); --cru-primary-enhance-color: rgb(77, 163, 255); --cru-primary-enhance-l1-color: rgb(94, 172, 255); --cru-primary-enhance-l2-color: rgb(112, 181, 255); @@ -27,6 +31,10 @@ --cru-primary-enhance-r1-color: rgb(43, 145, 255); --cru-primary-enhance-r2-color: rgb(10, 128, 255); --cru-primary-enhance-r3-color: rgb(0, 112, 232); + --cru-primary-enhance-t-color: rgb(0, 0, 0); + --cru-primary-enhance-t1-color: rgb(26, 26, 26); + --cru-primary-enhance-t2-color: rgb(51, 51, 51); + --cru-primary-enhance-t3-color: rgb(77, 77, 77); --cru-secondary-color: rgb(128, 128, 128); --cru-secondary-l1-color: rgb(141, 141, 141); --cru-secondary-l2-color: rgb(153, 153, 153); @@ -40,58 +48,10 @@ --cru-secondary-r1-color: rgb(141, 141, 141); --cru-secondary-r2-color: rgb(153, 153, 153); --cru-secondary-r3-color: rgb(166, 166, 166); - --cru-text-primary-color: rgb(17, 17, 17); - --cru-text-primary-l1-color: rgb(41, 41, 41); - --cru-text-primary-l2-color: rgb(65, 65, 65); - --cru-text-primary-l3-color: rgb(88, 88, 88); - --cru-text-primary-d1-color: rgb(15, 15, 15); - --cru-text-primary-d2-color: rgb(14, 14, 14); - --cru-text-primary-d3-color: rgb(12, 12, 12); - --cru-text-primary-f1-color: rgb(15, 15, 15); - --cru-text-primary-f2-color: rgb(14, 14, 14); - --cru-text-primary-f3-color: rgb(12, 12, 12); - --cru-text-primary-r1-color: rgb(41, 41, 41); - --cru-text-primary-r2-color: rgb(65, 65, 65); - --cru-text-primary-r3-color: rgb(88, 88, 88); - --cru-text-on-primary-color: rgb(255, 255, 255); - --cru-text-on-primary-l1-color: rgb(255, 255, 255); - --cru-text-on-primary-l2-color: rgb(255, 255, 255); - --cru-text-on-primary-l3-color: rgb(255, 255, 255); - --cru-text-on-primary-d1-color: rgb(230, 230, 230); - --cru-text-on-primary-d2-color: rgb(204, 204, 204); - --cru-text-on-primary-d3-color: rgb(179, 179, 179); - --cru-text-on-primary-f1-color: rgb(255, 255, 255); - --cru-text-on-primary-f2-color: rgb(255, 255, 255); - --cru-text-on-primary-f3-color: rgb(255, 255, 255); - --cru-text-on-primary-r1-color: rgb(230, 230, 230); - --cru-text-on-primary-r2-color: rgb(204, 204, 204); - --cru-text-on-primary-r3-color: rgb(179, 179, 179); - --cru-text-on-primary-enhance-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-l1-color: rgb(26, 26, 26); - --cru-text-on-primary-enhance-l2-color: rgb(51, 51, 51); - --cru-text-on-primary-enhance-l3-color: rgb(77, 77, 77); - --cru-text-on-primary-enhance-d1-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-d2-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-d3-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-f1-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-f2-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-f3-color: rgb(0, 0, 0); - --cru-text-on-primary-enhance-r1-color: rgb(26, 26, 26); - --cru-text-on-primary-enhance-r2-color: rgb(51, 51, 51); - --cru-text-on-primary-enhance-r3-color: rgb(77, 77, 77); - --cru-text-on-secondary-color: rgb(255, 255, 255); - --cru-text-on-secondary-l1-color: rgb(255, 255, 255); - --cru-text-on-secondary-l2-color: rgb(255, 255, 255); - --cru-text-on-secondary-l3-color: rgb(255, 255, 255); - --cru-text-on-secondary-d1-color: rgb(230, 230, 230); - --cru-text-on-secondary-d2-color: rgb(204, 204, 204); - --cru-text-on-secondary-d3-color: rgb(179, 179, 179); - --cru-text-on-secondary-f1-color: rgb(255, 255, 255); - --cru-text-on-secondary-f2-color: rgb(255, 255, 255); - --cru-text-on-secondary-f3-color: rgb(255, 255, 255); - --cru-text-on-secondary-r1-color: rgb(230, 230, 230); - --cru-text-on-secondary-r2-color: rgb(204, 204, 204); - --cru-text-on-secondary-r3-color: rgb(179, 179, 179); + --cru-secondary-t-color: rgb(255, 255, 255); + --cru-secondary-t1-color: rgb(230, 230, 230); + --cru-secondary-t2-color: rgb(204, 204, 204); + --cru-secondary-t3-color: rgb(179, 179, 179); --cru-danger-color: rgb(255, 0, 0); --cru-danger-l1-color: rgb(255, 26, 26); --cru-danger-l2-color: rgb(255, 51, 51); @@ -105,6 +65,10 @@ --cru-danger-r1-color: rgb(255, 26, 26); --cru-danger-r2-color: rgb(255, 51, 51); --cru-danger-r3-color: rgb(255, 77, 77); + --cru-danger-t-color: rgb(255, 255, 255); + --cru-danger-t1-color: rgb(230, 230, 230); + --cru-danger-t2-color: rgb(204, 204, 204); + --cru-danger-t3-color: rgb(179, 179, 179); --cru-success-color: rgb(0, 128, 0); --cru-success-l1-color: rgb(0, 166, 0); --cru-success-l2-color: rgb(0, 204, 0); @@ -118,6 +82,10 @@ --cru-success-r1-color: rgb(0, 166, 0); --cru-success-r2-color: rgb(0, 204, 0); --cru-success-r3-color: rgb(0, 243, 0); + --cru-success-t-color: rgb(255, 255, 255); + --cru-success-t1-color: rgb(230, 230, 230); + --cru-success-t2-color: rgb(204, 204, 204); + --cru-success-t3-color: rgb(179, 179, 179); } .cru-color-primary { diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css index e0ea2cad..c933b34f 100644 --- a/FrontEnd/src/views/common/menu/Menu.css +++ b/FrontEnd/src/views/common/menu/Menu.css @@ -13,7 +13,7 @@ } .cru-menu-item.color-primary:hover { - color: var(--cru-text-on-primary-color); + color: var(--cru-primary-t-color); background-color: var(--cru-primary-color); } -- cgit v1.2.3 From 4183e458436b0919385baaa50633d994daa407e1 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 30 Jun 2021 23:20:36 +0800 Subject: ... --- FrontEnd/src/views/common/button/Button.css | 5 ++++- FrontEnd/src/views/common/index.css | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index 11d211c5..ec2dd798 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -67,20 +67,23 @@ padding: 0.2em 0.5em; border-radius: 0.2em; transition: all 0.6s; - background-color: white; + background-color: var(--cru-background-color); } .cru-button.outline:hover { color: var(--cru-button-f1-color); border-color: var(--cru-button-f1-color); + background-color: var(--cru-background-1-color); } .cru-button.outline:active { color: var(--cru-button-f2-color); border-color: var(--cru-button-f2-color); + background-color: var(--cru-background-2-color); } .cru-button.outline:disabled { color: var(--cru-button-f3-color); border-color: var(--cru-button-f3-color); + background-color: var(--cru-background-3-color); } diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index 75ec6485..02eed6d9 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -1,5 +1,8 @@ :root { --cru-background-color: #f8f9fa; + --cru-background-1-color: #e9ecef; + --cru-background-2-color: #dee2e6; + --cru-background-3-color: #ced4da; --cru-primary-color: rgb(0, 123, 255); --cru-primary-l1-color: rgb(26, 136, 255); -- cgit v1.2.3 From 81e54efaae0ec1d5f1f90c3ca40448f8c61f0e80 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 1 Jul 2021 16:43:59 +0800 Subject: ... --- FrontEnd/src/views/admin/UserAdmin.tsx | 12 ++++++------ FrontEnd/src/views/center/TimelineBoard.tsx | 6 +++--- FrontEnd/src/views/common/button/Button.css | 16 +++++++++------- FrontEnd/src/views/common/dailog/ConfirmDialog.tsx | 7 +++++-- FrontEnd/src/views/common/dailog/Dialog.css | 9 +++++++++ FrontEnd/src/views/common/dailog/OperationDialog.css | 0 FrontEnd/src/views/common/dailog/OperationDialog.tsx | 16 +++++++++++----- FrontEnd/src/views/common/index.css | 3 ++- FrontEnd/src/views/common/menu/Menu.css | 2 +- FrontEnd/src/views/login/index.tsx | 2 +- FrontEnd/src/views/settings/index.tsx | 11 ++++++----- FrontEnd/src/views/timeline-common/TimelinePostView.tsx | 2 +- 12 files changed, 54 insertions(+), 32 deletions(-) create mode 100644 FrontEnd/src/views/common/dailog/OperationDialog.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/admin/UserAdmin.tsx b/FrontEnd/src/views/admin/UserAdmin.tsx index 481db1cc..6d2760f2 100644 --- a/FrontEnd/src/views/admin/UserAdmin.tsx +++ b/FrontEnd/src/views/admin/UserAdmin.tsx @@ -206,23 +206,23 @@ const UserItem: React.FC = ({ user, on }) => { return (
setEditMaskVisible(true)} /> -

{user.username}

-
+

{user.username}

+
{t("admin:user.nickname")} {user.nickname}
-
+
{t("admin:user.uniqueId")} {user.uniqueId}
-
+
{t("admin:user.permissions")} {user.permissions.map((permission) => { return ( - + {permission}{" "} ); diff --git a/FrontEnd/src/views/center/TimelineBoard.tsx b/FrontEnd/src/views/center/TimelineBoard.tsx index 3961a7bc..8c1f5fac 100644 --- a/FrontEnd/src/views/center/TimelineBoard.tsx +++ b/FrontEnd/src/views/center/TimelineBoard.tsx @@ -48,16 +48,16 @@ const TimelineBoardItem: React.FC = ({ )} {title} - {name} + {name} {actions != null ? (
{ e.currentTarget.setPointerCapture(e.pointerId); actions.onMove.start(e); diff --git a/FrontEnd/src/views/common/button/Button.css b/FrontEnd/src/views/common/button/Button.css index ec2dd798..54127f05 100644 --- a/FrontEnd/src/views/common/button/Button.css +++ b/FrontEnd/src/views/common/button/Button.css @@ -57,7 +57,8 @@ } .cru-button:not(.outline):disabled { - background-color: var(--cru-button-f3-color); + background-color: var(--cru-disable-color); + cursor: auto; } .cru-button.outline { @@ -67,23 +68,24 @@ padding: 0.2em 0.5em; border-radius: 0.2em; transition: all 0.6s; - background-color: var(--cru-background-color); + background-color: white; } .cru-button.outline:hover { color: var(--cru-button-f1-color); border-color: var(--cru-button-f1-color); - background-color: var(--cru-background-1-color); + background-color: var(--cru-background-color); } .cru-button.outline:active { color: var(--cru-button-f2-color); border-color: var(--cru-button-f2-color); - background-color: var(--cru-background-2-color); + background-color: var(--cru-background-1-color); } .cru-button.outline:disabled { - color: var(--cru-button-f3-color); - border-color: var(--cru-button-f3-color); - background-color: var(--cru-background-3-color); + color: var(--cru-disable-color); + border-color: var(--cru-disable-color); + background-color: white; + cursor: auto; } diff --git a/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx index 1ad52350..c10b1cdb 100644 --- a/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx +++ b/FrontEnd/src/views/common/dailog/ConfirmDialog.tsx @@ -16,12 +16,15 @@ const ConfirmDialog: React.FC<{ return ( -

{convertI18nText(title, t)}

+

{convertI18nText(title, t)}

+

{convertI18nText(body, t)}

-
+
+
-
+
+
@@ -446,11 +451,12 @@ const OperationDialog = <

{title}

+
{body}
); diff --git a/FrontEnd/src/views/common/index.css b/FrontEnd/src/views/common/index.css index 02eed6d9..62167cfc 100644 --- a/FrontEnd/src/views/common/index.css +++ b/FrontEnd/src/views/common/index.css @@ -2,7 +2,8 @@ --cru-background-color: #f8f9fa; --cru-background-1-color: #e9ecef; --cru-background-2-color: #dee2e6; - --cru-background-3-color: #ced4da; + + --cru-disable-color: #ced4da; --cru-primary-color: rgb(0, 123, 255); --cru-primary-l1-color: rgb(26, 136, 255); diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css index c933b34f..a30eb5c6 100644 --- a/FrontEnd/src/views/common/menu/Menu.css +++ b/FrontEnd/src/views/common/menu/Menu.css @@ -22,7 +22,7 @@ } .cru-menu-item.color-secondary:hover { - color: var(--cru-text-on-secondary-color); + color: var(--cru-secondary-t-color); background-color: var(--cru-secondary-color); } diff --git a/FrontEnd/src/views/login/index.tsx b/FrontEnd/src/views/login/index.tsx index 6d70c64a..782acdaa 100644 --- a/FrontEnd/src/views/login/index.tsx +++ b/FrontEnd/src/views/login/index.tsx @@ -122,7 +122,7 @@ const LoginPage: React.FC = (_) => {
{error ?

{t(error)}

: null} -
+
{ diff --git a/FrontEnd/src/views/settings/index.tsx b/FrontEnd/src/views/settings/index.tsx index f25911d7..69a74327 100644 --- a/FrontEnd/src/views/settings/index.tsx +++ b/FrontEnd/src/views/settings/index.tsx @@ -28,7 +28,7 @@ const SettingsPage: React.FC = (_) => {
{user ? ( -

+

{t("settings.subheaders.account")}

{ {t("settings.changeNickname")}
setDialog("changepassword")} > {t("settings.changePassword")}
{ setDialog("logout"); }} @@ -60,13 +60,13 @@ const SettingsPage: React.FC = (_) => { ) : null} -

+

{t("settings.subheaders.customization")}

{t("settings.languagePrimary")}
- + {t("settings.languageSecondary")}
@@ -94,6 +94,7 @@ const SettingsPage: React.FC = (_) => { title="settings.dialogConfirmLogout.title" body="settings.dialogConfirmLogout.prompt" onClose={() => setDialog(null)} + open onConfirm={() => { void userService.logout().then(() => { history.push("/"); diff --git a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx index 995c43df..652ff9c9 100644 --- a/FrontEnd/src/views/timeline-common/TimelinePostView.tsx +++ b/FrontEnd/src/views/timeline-common/TimelinePostView.tsx @@ -64,7 +64,7 @@ const TimelinePostView: React.FC = (props) => { > {post.editable ? ( { setOperationMaskVisible(true); e.stopPropagation(); -- cgit v1.2.3 From ee5341ffcf02aa99a935dd4a3f523b14ab9ab5ad Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 1 Jul 2021 16:58:34 +0800 Subject: ... --- FrontEnd/src/views/common/dailog/Dialog.css | 9 ++++++--- FrontEnd/src/views/timeline-common/TimelineMember.css | 8 ++++++++ FrontEnd/src/views/timeline-common/TimelineMember.tsx | 12 ++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 FrontEnd/src/views/timeline-common/TimelineMember.css (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/dailog/Dialog.css b/FrontEnd/src/views/common/dailog/Dialog.css index c1209d99..67bfe2a9 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.css +++ b/FrontEnd/src/views/common/dailog/Dialog.css @@ -8,13 +8,16 @@ background-color: rgba(255, 255, 255, 0.92); display: flex; - justify-content: center; - align-items: center; + padding: 2em 0; + + overflow: auto; } .cru-dialog-container { max-width: 100%; + margin: auto; + border: var(--cru-primary-color) 1px solid; border-radius: 5px; padding: 1.5em; @@ -27,5 +30,5 @@ } .cru-dialog-bottom-area > * { - margin: 0 0.5em + margin: 0 0.5em; } diff --git a/FrontEnd/src/views/timeline-common/TimelineMember.css b/FrontEnd/src/views/timeline-common/TimelineMember.css new file mode 100644 index 00000000..adb78764 --- /dev/null +++ b/FrontEnd/src/views/timeline-common/TimelineMember.css @@ -0,0 +1,8 @@ +.timeline-member-item { + border: var(--cru-background-1-color) solid; + border-width: 0.5px 1px; +} + +.timeline-member-item > div { + padding: 0.5em; +} diff --git a/FrontEnd/src/views/timeline-common/TimelineMember.tsx b/FrontEnd/src/views/timeline-common/TimelineMember.tsx index c8f26da7..0ebecbb9 100644 --- a/FrontEnd/src/views/timeline-common/TimelineMember.tsx +++ b/FrontEnd/src/views/timeline-common/TimelineMember.tsx @@ -12,22 +12,22 @@ import UserAvatar from "../common/user/UserAvatar"; import Button from "../common/button/Button"; import Dialog from "../common/dailog/Dialog"; +import "./TimelineMember.css"; + const TimelineMemberItem: React.FC<{ user: HttpUser; add?: boolean; onAction?: (username: string) => void; }> = ({ user, add, onAction }) => { return ( -
+
- +
{user.nickname}
-
- {"@" + user.username} -
+ {"@" + user.username}
{onAction ? (
@@ -130,7 +130,7 @@ const TimelineMemberUserSearch: React.FC<{ } } else if (userSearchState.type === "error") { return ( -
+
{convertI18nText(userSearchState.data, t)}
); -- cgit v1.2.3 From 1f83bb6867b270aced3e1b71ca7a73bc6ac38cf4 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 1 Jul 2021 17:02:12 +0800 Subject: ... --- FrontEnd/src/views/common/menu/Menu.css | 28 ++++++++++++++++++++++++++++ FrontEnd/src/views/common/menu/PopupMenu.css | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/menu/Menu.css b/FrontEnd/src/views/common/menu/Menu.css index a30eb5c6..923c7d3c 100644 --- a/FrontEnd/src/views/common/menu/Menu.css +++ b/FrontEnd/src/views/common/menu/Menu.css @@ -6,6 +6,7 @@ font-size: 1em; padding: 0.5em 1.5em; cursor: pointer; + transition: all 0.5s; } .cru-menu-item.color-primary { @@ -26,6 +27,33 @@ background-color: var(--cru-secondary-color); } +.cru-menu-item.color-primary-enhance { + color: var(--cru-primary-enhance-color); +} + +.cru-menu-item.color-primary-enhance:hover { + color: var(--cru-primary-enhance-t-color); + background-color: var(--cru-primary-enhance-color); +} + +.cru-menu-item.color-success { + color: var(--cru-success-color); +} + +.cru-menu-item.color-success:hover { + color: var(--cru-success-t-color); + background-color: var(--cru-success-color); +} + +.cru-menu-item.color-danger { + color: var(--cru-danger-color); +} + +.cru-menu-item.color-danger:hover { + color: var(--cru-danger-t-color); + background-color: var(--cru-danger-color); +} + .cru-menu-item-icon { margin-right: 1em; } diff --git a/FrontEnd/src/views/common/menu/PopupMenu.css b/FrontEnd/src/views/common/menu/PopupMenu.css index 658532d3..f6654f68 100644 --- a/FrontEnd/src/views/common/menu/PopupMenu.css +++ b/FrontEnd/src/views/common/menu/PopupMenu.css @@ -2,5 +2,5 @@ z-index: 1040; border-radius: 5px; border: var(--cru-primary-color) 1px solid; - background-color: var(--cru-background-color); + background-color: white; } -- cgit v1.2.3 From 672778ca5d9de5513c86d70394b2dd048639cdea Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 1 Jul 2021 17:16:20 +0800 Subject: ... --- FrontEnd/src/views/common/dailog/Dialog.css | 1 + .../src/views/common/dailog/OperationDialog.css | 26 ++++++++++++ .../src/views/common/dailog/OperationDialog.tsx | 49 ++++++++++++++++++---- .../src/views/timeline/TimelineDeleteDialog.tsx | 2 +- 4 files changed, 68 insertions(+), 10 deletions(-) (limited to 'FrontEnd/src/views/common') diff --git a/FrontEnd/src/views/common/dailog/Dialog.css b/FrontEnd/src/views/common/dailog/Dialog.css index 67bfe2a9..22b420fc 100644 --- a/FrontEnd/src/views/common/dailog/Dialog.css +++ b/FrontEnd/src/views/common/dailog/Dialog.css @@ -15,6 +15,7 @@ .cru-dialog-container { max-width: 100%; + min-width: 30vw; margin: auto; diff --git a/FrontEnd/src/views/common/dailog/OperationDialog.css b/FrontEnd/src/views/common/dailog/OperationDialog.css index e69de29b..26c3920b 100644 --- a/FrontEnd/src/views/common/dailog/OperationDialog.css +++ b/FrontEnd/src/views/common/dailog/OperationDialog.css @@ -0,0 +1,26 @@ +.cru-operation-dialog-group { + display: block; + margin: 0.4em 0; +} + +.cru-operation-dialog-label { + display: block; + font-size: 0.8em; + color: var(--cru-primary-color); +} + +.cru-operation-dialog-inline-label { + margin-inline-start: 0.5em; +} + +.cru-operation-dialog-error-text { + display: block; + font-size: 0.8em; + color: var(--cru-danger-color); +} + +.cru-operation-dialog-helper-text { + display: block; + font-size: 0.8em; + color: var(--cru-primary-color); +} diff --git a/FrontEnd/src/views/common/dailog/OperationDialog.tsx b/FrontEnd/src/views/common/dailog/OperationDialog.tsx index f394a3d3..083d60da 100644 --- a/FrontEnd/src/views/common/dailog/OperationDialog.tsx +++ b/FrontEnd/src/views/common/dailog/OperationDialog.tsx @@ -12,6 +12,7 @@ import LoadingButton from "../button/LoadingButton"; import Dialog from "./Dialog"; import "./OperationDialog.css"; +import classNames from "classnames"; interface DefaultErrorPromptProps { error?: string; @@ -294,9 +295,17 @@ const OperationDialog = < if (item.type === "text") { return ( -
+
{item.label && ( - + )} - {error != null &&
{error}
} - {item.helperText &&
{t(item.helperText)}
} + {error != null && ( +
+ {error} +
+ )} + {item.helperText && ( +
+ {t(item.helperText)} +
+ )}
); } else if (item.type === "bool") { @@ -327,8 +344,16 @@ const OperationDialog = < ); } else if (item.type === "select") { return ( -
- +
+ +
-
+
+
-
+
+
-
+
+
@@ -255,15 +270,18 @@ const ChangeAvatarDialog: React.FC = (props) => { {t("settings.dialogChangeAvatar.prompt.preview")}
-
+
+
-
); } else if (state === "success") { @@ -294,7 +311,8 @@ const ChangeAvatarDialog: React.FC = (props) => { {t("operationDialog.success")}
-
+
+
+