import * as 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 InputPanelError = { [index: number]: I18nText | null | undefined; }; export function hasError(e: InputPanelError | null | undefined): boolean { if (e == null) return false; for (const key of Object.keys(e)) { if (e[key as unknown as number] != null) return true; } return false; } export interface InputPanelProps { scheme: InputList; values: MapInputListToValueTypeList; onChange: ( values: MapInputListToValueTypeList, index: number ) => void; error?: InputPanelError; 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;