From 41e872d37677e8eba75a868f07205319889ffe9f Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 28 Apr 2022 14:25:09 +0800 Subject: ... --- FrontEnd/src/views/common/input/InputPanel.css | 25 +++ FrontEnd/src/views/common/input/InputPanel.tsx | 247 +++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 FrontEnd/src/views/common/input/InputPanel.css create mode 100644 FrontEnd/src/views/common/input/InputPanel.tsx (limited to 'FrontEnd/src/views/common/input') diff --git a/FrontEnd/src/views/common/input/InputPanel.css b/FrontEnd/src/views/common/input/InputPanel.css new file mode 100644 index 00000000..f9d6ac8b --- /dev/null +++ b/FrontEnd/src/views/common/input/InputPanel.css @@ -0,0 +1,25 @@ +.cru-input-panel-group { + display: block; + margin: 0.4em 0; +} + +.cru-input-panel-label { + display: block; + color: var(--cru-primary-color); +} + +.cru-input-panel-inline-label { + margin-inline-start: 0.5em; +} + +.cru-input-panel-error-text { + display: block; + font-size: 0.8em; + color: var(--cru-danger-color); +} + +.cru-input-panel-helper-text { + display: block; + font-size: 0.8em; + color: var(--cru-primary-color); +} diff --git a/FrontEnd/src/views/common/input/InputPanel.tsx b/FrontEnd/src/views/common/input/InputPanel.tsx new file mode 100644 index 00000000..1270cc53 --- /dev/null +++ b/FrontEnd/src/views/common/input/InputPanel.tsx @@ -0,0 +1,247 @@ +import React from "react"; +import classNames from "classnames"; +import { useTranslation } from "react-i18next"; +import { TwitterPicker } from "react-color"; + +import { convertI18nText, I18nText } from "@/common"; + +import "./InputPanel.css"; + +export interface TextInput { + type: "text"; + label?: I18nText; + helper?: I18nText; + password?: boolean; +} + +export interface BoolInput { + type: "bool"; + label: I18nText; + helper?: I18nText; +} + +export interface SelectInputOption { + value: string; + label: I18nText; + icon?: React.ReactElement; +} + +export interface SelectInput { + type: "select"; + label: I18nText; + options: SelectInputOption[]; +} + +export interface ColorInput { + type: "color"; + label?: I18nText; +} + +export interface DateTimeInput { + type: "datetime"; + label?: I18nText; + helper?: I18nText; +} + +export type Input = + | TextInput + | BoolInput + | SelectInput + | ColorInput + | DateTimeInput; + +interface InputTypeToValueTypeMap { + text: string; + bool: boolean; + select: string; + color: string; + datetime: string; +} + +type ValueTypes = InputTypeToValueTypeMap[keyof InputTypeToValueTypeMap]; + +type MapInputTypeToValueType = Type extends keyof InputTypeToValueTypeMap + ? InputTypeToValueTypeMap[Type] + : never; + +type MapInputToValueType = T extends Input + ? MapInputTypeToValueType + : T; + +type MapInputListToValueTypeList = { + [Index in keyof Tuple]: MapInputToValueType; +} & { length: Tuple["length"] }; + +export type OperationInputError = (I18nText | null | undefined)[]; + +export interface InputPanelProps { + scheme: InputList; + values: MapInputListToValueTypeList; + onChange: ( + values: MapInputListToValueTypeList, + index: number + ) => void; + error?: OperationInputError; + disable?: boolean; +} + +const InputPanel = ( + props: InputPanelProps +): React.ReactElement => { + const { values, onChange, scheme, error, disable } = props; + + const { t } = useTranslation(); + + const updateValue = (index: number, newValue: ValueTypes): void => { + const oldValues = values; + const newValues = oldValues.slice(); + newValues[index] = newValue; + onChange( + newValues as unknown as MapInputListToValueTypeList, + index + ); + }; + + return ( +
+ {scheme.map((item, index) => { + const v = values[index]; + const e: string | null = convertI18nText(error?.[index], t); + + if (item.type === "text") { + return ( +
+ {item.label && ( + + )} + { + const v = e.target.value; + updateValue(index, v); + }} + disabled={disable} + /> + {e &&
{e}
} + {item.helper && ( +
+ {convertI18nText(item.helper, t)} +
+ )} +
+ ); + } else if (item.type === "bool") { + return ( +
+ { + const value = event.currentTarget.checked; + updateValue(index, value); + }} + disabled={disable} + /> + + {e != null && ( +
{e}
+ )} + {item.helper && ( +
+ {convertI18nText(item.helper, t)} +
+ )} +
+ ); + } else if (item.type === "select") { + return ( +
+ + +
+ ); + } else if (item.type === "color") { + return ( +
+ + updateValue(index, result.hex)} + /> +
+ ); + } else if (item.type === "datetime") { + return ( +
+ {item.label && ( + + )} + { + const v = e.target.value; + updateValue(index, v); + }} + disabled={disable} + /> + {e != null && ( +
{e}
+ )} + {item.helper && ( +
+ {convertI18nText(item.helper, t)} +
+ )} +
+ ); + } + })} +
+ ); +}; + +export default InputPanel; -- cgit v1.2.3