From 79036a85c6f8dfb75f129eb2e047fc3ce366f73e Mon Sep 17 00:00:00 2001 From: MrKBear Date: Mon, 14 Mar 2022 17:47:38 +0800 Subject: [PATCH 1/2] Add combo input component --- source/Component/AttrInput/AttrInput.tsx | 2 +- source/Component/ComboInput/ComboInput.scss | 74 +++++++++++++++++++ source/Component/ComboInput/ComboInput.tsx | 77 ++++++++++++++++++++ source/Component/LabelPicker/LabelPicker.tsx | 3 +- source/Component/PickerList/PickerList.scss | 1 + source/Component/PickerList/PickerList.tsx | 55 ++++++++++++-- source/Localization/EN-US.ts | 5 ++ source/Localization/ZH-CN.ts | 5 ++ source/Model/Group.ts | 5 ++ source/Panel/GroupDetails/GroupDetails.tsx | 18 +++++ 10 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 source/Component/ComboInput/ComboInput.scss create mode 100644 source/Component/ComboInput/ComboInput.tsx diff --git a/source/Component/AttrInput/AttrInput.tsx b/source/Component/AttrInput/AttrInput.tsx index 148d3fe..7b9941a 100644 --- a/source/Component/AttrInput/AttrInput.tsx +++ b/source/Component/AttrInput/AttrInput.tsx @@ -25,7 +25,7 @@ class AttrInput extends Component { private value: string = ""; private error: ReactNode; - private numberTestReg = [/\.0*$/, /[1-9]+0+$/]; + private numberTestReg = [/\.0*$/, /\.\d*[1-9]+0+$/]; private numberTester(value: string) { return isNaN((value as any) / 1) || diff --git a/source/Component/ComboInput/ComboInput.scss b/source/Component/ComboInput/ComboInput.scss new file mode 100644 index 0000000..8d81a41 --- /dev/null +++ b/source/Component/ComboInput/ComboInput.scss @@ -0,0 +1,74 @@ +@import "../Theme/Theme.scss"; + +$line-min-height: 26px; + +div.combo-input-root { + width: 100%; + display: flex; + min-height: $line-min-height; + padding: 5px 0; + + div.input-intro { + width: 50%; + height: 100%; + max-width: 220px; + min-height: $line-min-height; + display: flex; + align-items: center; + padding-right: 5px; + box-sizing: border-box; + } + + div.root-content { + width: 50%; + height: 100%; + max-width: 180px; + min-height: $line-min-height; + border-radius: 3px; + overflow: hidden; + display: flex; + cursor: pointer; + + div.value-view { + width: 100%; + height: 100%; + min-height: $line-min-height; + display: flex; + align-items: center; + padding-left: 5px; + + span { + display: block; + } + } + } +} + +div.dark.combo-input-root { + + div.root-content { + background-color: $lt-bg-color-lvl3-dark; + color: $lt-font-color-normal-dark; + } + + div.root-content:hover { + background-color: $lt-bg-color-lvl2-dark; + } +} + +div.light.combo-input-root { + + div.root-content { + background-color: $lt-bg-color-lvl3-light; + color: $lt-font-color-normal-light; + } + + div.root-content:hover { + background-color: $lt-bg-color-lvl2-light; + } +} + +div.combo-picker-root { + width: 300px; + height: 340px; +} \ No newline at end of file diff --git a/source/Component/ComboInput/ComboInput.tsx b/source/Component/ComboInput/ComboInput.tsx new file mode 100644 index 0000000..15e1833 --- /dev/null +++ b/source/Component/ComboInput/ComboInput.tsx @@ -0,0 +1,77 @@ +import { Component, createRef, ReactNode } from "react"; +import { FontLevel, Theme } from "@Component/Theme/Theme"; +import { PickerList, IDisplayItem } from "../PickerList/PickerList"; +import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; +import "./ComboInput.scss"; + +interface IComboInputProps { + keyI18n: AllI18nKeys; + infoI18n?: AllI18nKeys; + allOption?: IDisplayItem[]; + value?: IDisplayItem; + valueChange?: (value: IDisplayItem) => any; +} + +interface IComboInputState { + isPickerVisible: boolean; +} + +class ComboInput extends Component { + + public constructor(props: IComboInputProps) { + super(props); + this.state = { + isPickerVisible: false + } + } + + private pickerTarget = createRef(); + + private renderPicker() { + return { + if (this.props.valueChange) { + this.props.valueChange(item); + } + })} + dismiss={() => { + this.setState({ + isPickerVisible: false + }) + }} + /> + } + + public render(): ReactNode { + return <> + +
+ +
+
{ + this.setState({ + isPickerVisible: true + }) + }} + > +
+ { + this.props.value ? + : + null + } +
+
+
+ + {this.state.isPickerVisible ? this.renderPicker(): null} + + } +} + +export { ComboInput }; \ No newline at end of file diff --git a/source/Component/LabelPicker/LabelPicker.tsx b/source/Component/LabelPicker/LabelPicker.tsx index 0989151..94e2d82 100644 --- a/source/Component/LabelPicker/LabelPicker.tsx +++ b/source/Component/LabelPicker/LabelPicker.tsx @@ -69,13 +69,14 @@ class LabelPicker extends Component {this.state.isPickerVisible ? { this.setState({ isPickerVisible: false }); }} - click={(label) => { + clickObjectItems={(label) => { if (label instanceof Label && this.props.labelAdd) { this.props.labelAdd(label) } diff --git a/source/Component/PickerList/PickerList.scss b/source/Component/PickerList/PickerList.scss index 9de93c4..260e3f0 100644 --- a/source/Component/PickerList/PickerList.scss +++ b/source/Component/PickerList/PickerList.scss @@ -37,6 +37,7 @@ div.picker-list-root { div.list-item-name { width: 100%; + box-sizing: border-box; } } diff --git a/source/Component/PickerList/PickerList.tsx b/source/Component/PickerList/PickerList.tsx index 47daa87..fbf8176 100644 --- a/source/Component/PickerList/PickerList.tsx +++ b/source/Component/PickerList/PickerList.tsx @@ -1,4 +1,4 @@ -import { Localization } from "@Component/Localization/Localization"; +import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; import { Callout, DirectionalHint, Icon } from "@fluentui/react"; import { CtrlObject } from "@Model/CtrlObject"; import { Group } from "@Model/Group"; @@ -8,12 +8,19 @@ import { Component, ReactNode, RefObject } from "react"; import "./PickerList.scss"; type IPickerListItem = CtrlObject | Label; +type IDisplayItem = { + nameKey: AllI18nKeys; + key: string; +} interface IPickerListProps { + displayItems?: IDisplayItem[]; objectList?: IPickerListItem[]; target?: RefObject; + noData?: AllI18nKeys; dismiss?: () => any; - click?: (item: IPickerListItem) => any; + clickObjectItems?: (item: IPickerListItem) => any; + clickDisplayItems?: (item: IDisplayItem) => any; } class PickerList extends Component { @@ -46,8 +53,8 @@ class PickerList extends Component { className="picker-list-item" key={item.id} onClick={() => { - if (this.props.click) { - this.props.click(item) + if (this.props.clickObjectItems) { + this.props.clickObjectItems(item) } }} > @@ -65,6 +72,27 @@ class PickerList extends Component { ; } + private renderString(item: IDisplayItem) { + return
{ + if (this.props.clickDisplayItems) { + this.props.clickDisplayItems(item) + } + }} + > +
+ +
+
; + } + public render(): ReactNode { return { {this.props.objectList ? this.props.objectList.map((item) => { return this.renderItem(item); }) : null} - {!this.props.objectList || (this.props.objectList && this.props.objectList.length <= 0) ? - - : null + {this.props.displayItems ? this.props.displayItems.map((item) => { + return this.renderString(item); + }) : null} + { + !(this.props.objectList || this.props.displayItems) || + !( + this.props.objectList && this.props.objectList.length > 0 || + this.props.displayItems && this.props.displayItems.length > 0 + ) ? + + : null } } } -export { PickerList } \ No newline at end of file +export { PickerList, IDisplayItem } \ No newline at end of file diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 818d89b..d341593 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -42,6 +42,7 @@ const EN_US = { "Panel.Info.Label.Details.View": "Edit view label attributes", "Panel.Title.Group.Details.View": "Group", "Panel.Info.Group.Details.View": "Edit view group attributes", + "Common.No.Data": "No Data", "Common.Attr.Title.Basic": "Basic properties", "Common.Attr.Title.Spatial": "Spatial property", "Common.Attr.Title.Individual.Generation": "Individual generation", @@ -57,9 +58,13 @@ const EN_US = { "Common.Attr.Key.Update": "Update", "Common.Attr.Key.Delete": "Delete", "Common.Attr.Key.Label": "Label", + "Common.Attr.Key.Size": "Size", "Common.Attr.Key.Error.Multiple": "Multiple values", "Common.Attr.Key.Label.Picker.Nodata": "No tags can be added", "Common.Attr.Key.Generation": "Generation", + "Common.Attr.Key.Generation.Mod": "Generation model", + "Common.Attr.Key.Generation.Mod.Point": "Point model", + "Common.Attr.Key.Generation.Mod.Range": "Range model", "Panel.Info.Range.Details.Attr.Error.Not.Range": "Object is not a Range", "Panel.Info.Range.Details.Attr.Error.Unspecified": "Unspecified range object", "Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index 7482f58..67c770c 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -42,6 +42,7 @@ const ZH_CN = { "Panel.Info.Label.Details.View": "编辑查看标签属性", "Panel.Title.Group.Details.View": "群", "Panel.Info.Group.Details.View": "编辑查看群属性", + "Common.No.Data": "暂无数据", "Common.Attr.Title.Basic": "基础属性", "Common.Attr.Title.Spatial": "空间属性", "Common.Attr.Title.Individual.Generation": "个体生成", @@ -57,9 +58,13 @@ const ZH_CN = { "Common.Attr.Key.Update": "更新", "Common.Attr.Key.Delete": "删除", "Common.Attr.Key.Label": "标签", + "Common.Attr.Key.Size": "大小", "Common.Attr.Key.Error.Multiple": "多重数值", "Common.Attr.Key.Label.Picker.Nodata": "没有可以被添加的标签", "Common.Attr.Key.Generation": "生成", + "Common.Attr.Key.Generation.Mod": "生成模式", + "Common.Attr.Key.Generation.Mod.Point": "点生成", + "Common.Attr.Key.Generation.Mod.Range": "范围生成", "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", "Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群", diff --git a/source/Model/Group.ts b/source/Model/Group.ts index 58b6cc3..46d23db 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -3,6 +3,11 @@ import { CtrlObject } from "./CtrlObject"; import type { Behavior } from "./Behavior"; import type { Model } from "./Model"; +enum GenMod { + Point = "p", + Range = "R" +} + /** * 群体类型 */ diff --git a/source/Panel/GroupDetails/GroupDetails.tsx b/source/Panel/GroupDetails/GroupDetails.tsx index 195802e..86656bf 100644 --- a/source/Panel/GroupDetails/GroupDetails.tsx +++ b/source/Panel/GroupDetails/GroupDetails.tsx @@ -8,6 +8,7 @@ import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; import { LabelPicker } from "@Component/LabelPicker/LabelPicker"; import { Group } from "@Model/Group"; import { AllI18nKeys } from "@Component/Localization/Localization"; +import { ComboInput } from "@Component/ComboInput/ComboInput"; import "./GroupDetails.scss"; interface IGroupDetailsProps {} @@ -61,6 +62,13 @@ class GroupDetails extends Component { }} /> + {this.renderAttrInput( + group.id, "Common.Attr.Key.Size", group.size, + (val, status) => { + status.changeGroupAttrib(group.id, "size", (val as any) / 1); + }, 10, undefined, 0 + )} + { } }} /> + + + + } From adf1475be88d1ad1ad8fc0b0e3e80a7df7f58be7 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Mon, 14 Mar 2022 21:11:28 +0800 Subject: [PATCH 2/2] Add ComboInput component --- source/Component/ComboInput/ComboInput.scss | 15 +++++++++++- source/Component/ComboInput/ComboInput.tsx | 14 +++++++++-- source/Component/PickerList/PickerList.tsx | 13 +++++----- source/Model/Group.ts | 7 +++++- source/Panel/GroupDetails/GroupDetails.tsx | 27 ++++++++++++++++----- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/source/Component/ComboInput/ComboInput.scss b/source/Component/ComboInput/ComboInput.scss index 8d81a41..a073e7e 100644 --- a/source/Component/ComboInput/ComboInput.scss +++ b/source/Component/ComboInput/ComboInput.scss @@ -35,12 +35,25 @@ div.combo-input-root { min-height: $line-min-height; display: flex; align-items: center; - padding-left: 5px; + padding-left: 8px; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; + overflow: hidden; span { display: block; } } + + div.list-button { + width: $line-min-height; + height: $line-min-height; + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + } } } diff --git a/source/Component/ComboInput/ComboInput.tsx b/source/Component/ComboInput/ComboInput.tsx index 15e1833..55e2c27 100644 --- a/source/Component/ComboInput/ComboInput.tsx +++ b/source/Component/ComboInput/ComboInput.tsx @@ -2,6 +2,7 @@ import { Component, createRef, ReactNode } from "react"; import { FontLevel, Theme } from "@Component/Theme/Theme"; import { PickerList, IDisplayItem } from "../PickerList/PickerList"; import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; +import { Icon } from "@fluentui/react"; import "./ComboInput.scss"; interface IComboInputProps { @@ -30,11 +31,17 @@ class ComboInput extends Component { private renderPicker() { return { + return item.key === this.props.value?.key ? + {...item, mark: true} : item; + })} clickDisplayItems={((item) => { if (this.props.valueChange) { this.props.valueChange(item); } + this.setState({ + isPickerVisible: false + }) })} dismiss={() => { this.setState({ @@ -66,6 +73,9 @@ class ComboInput extends Component { null } +
+ +
@@ -74,4 +84,4 @@ class ComboInput extends Component { } } -export { ComboInput }; \ No newline at end of file +export { ComboInput, IDisplayItem }; \ No newline at end of file diff --git a/source/Component/PickerList/PickerList.tsx b/source/Component/PickerList/PickerList.tsx index fbf8176..7ed5d71 100644 --- a/source/Component/PickerList/PickerList.tsx +++ b/source/Component/PickerList/PickerList.tsx @@ -11,6 +11,7 @@ type IPickerListItem = CtrlObject | Label; type IDisplayItem = { nameKey: AllI18nKeys; key: string; + mark?: boolean; } interface IPickerListProps { @@ -82,12 +83,12 @@ class PickerList extends Component { } }} > -
+
+ +
+
; diff --git a/source/Model/Group.ts b/source/Model/Group.ts index 46d23db..db0087e 100644 --- a/source/Model/Group.ts +++ b/source/Model/Group.ts @@ -18,6 +18,11 @@ class Group extends CtrlObject { */ public individuals: Set = new Set(); + /** + * 个体生成方式 + */ + public genMethod: GenMod = GenMod.Point; + /** * 创建个体 * @param count 创建数量 @@ -148,4 +153,4 @@ class Group extends CtrlObject { } export default Group; -export { Group }; \ No newline at end of file +export { Group, GenMod }; \ No newline at end of file diff --git a/source/Panel/GroupDetails/GroupDetails.tsx b/source/Panel/GroupDetails/GroupDetails.tsx index 86656bf..3606c0b 100644 --- a/source/Panel/GroupDetails/GroupDetails.tsx +++ b/source/Panel/GroupDetails/GroupDetails.tsx @@ -6,13 +6,22 @@ import { ObjectID } from "@Model/Renderer"; import { ColorInput } from "@Component/ColorInput/ColorInput"; import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; import { LabelPicker } from "@Component/LabelPicker/LabelPicker"; -import { Group } from "@Model/Group"; +import { Group, GenMod } from "@Model/Group"; import { AllI18nKeys } from "@Component/Localization/Localization"; -import { ComboInput } from "@Component/ComboInput/ComboInput"; +import { ComboInput, IDisplayItem } from "@Component/ComboInput/ComboInput"; import "./GroupDetails.scss"; interface IGroupDetailsProps {} +const mapGenModToI18nKey = new Map(); +mapGenModToI18nKey.set(GenMod.Point, "Common.Attr.Key.Generation.Mod.Point"); +mapGenModToI18nKey.set(GenMod.Range, "Common.Attr.Key.Generation.Mod.Range"); + +const allOption: IDisplayItem[] = [ + {nameKey: "Common.Attr.Key.Generation.Mod.Point", key: GenMod.Point}, + {nameKey: "Common.Attr.Key.Generation.Mod.Range", key: GenMod.Range} +]; + @useStatusWithEvent("groupAttrChange", "groupLabelChange", "focusObjectChange") class GroupDetails extends Component { @@ -117,10 +126,16 @@ class GroupDetails extends Component { { + if (this.props.status) { + this.props.status.changeGroupAttrib(group.id, "genMethod", value.key as any); + } + }} /> }