From b2eff20b6eb3285479524e02707dcd3eb46e2712 Mon Sep 17 00:00:00 2001 From: MrKBear Date: Sat, 12 Mar 2022 21:11:09 +0800 Subject: [PATCH] Add label picker --- source/Component/LabelList/LabelList.tsx | 9 ++- source/Component/LabelPicker/LabelPicker.tsx | 65 ++++++++++++++-- source/Component/PickerList/PickerList.scss | 72 +++++++++++++++++ source/Component/PickerList/PickerList.tsx | 81 +++++++++++++++++++- source/Context/Status.tsx | 19 ++++- source/Localization/EN-US.ts | 1 + source/Localization/ZH-CN.ts | 1 + source/Panel/RangeDetails/RangeDetails.tsx | 15 +++- 8 files changed, 250 insertions(+), 13 deletions(-) create mode 100644 source/Component/PickerList/PickerList.scss diff --git a/source/Component/LabelList/LabelList.tsx b/source/Component/LabelList/LabelList.tsx index 7250fa8..eae20cd 100644 --- a/source/Component/LabelList/LabelList.tsx +++ b/source/Component/LabelList/LabelList.tsx @@ -35,6 +35,7 @@ class LabelList extends Component { classList.push("one-line"); } const colorCss = `rgb(${label.color.join(",")})`; + const isDelete = label.isDeleted(); return
{ backgroundColor: colorCss, borderRadius: isFocus ? 0 : 3 }}/> -
+
{label.name}
{ diff --git a/source/Component/LabelPicker/LabelPicker.tsx b/source/Component/LabelPicker/LabelPicker.tsx index 6abe2ac..0989151 100644 --- a/source/Component/LabelPicker/LabelPicker.tsx +++ b/source/Component/LabelPicker/LabelPicker.tsx @@ -1,6 +1,8 @@ import { AllI18nKeys, Localization } from "@Component/Localization/Localization"; +import { PickerList } from "../PickerList/PickerList"; import { Label } from "@Model/Label"; -import { Component, ReactNode } from "react"; +import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status"; +import { Component, ReactNode, createRef } from "react"; import { LabelList } from "../LabelList/LabelList"; import "./LabelPicker.scss" @@ -8,9 +10,42 @@ interface ILabelPickerProps { keyI18n: AllI18nKeys; infoI18n?: AllI18nKeys; labels: Label[]; + labelAdd?: (label: Label) => any; + labelDelete?: (label: Label) => any; } -class LabelPicker extends Component { +interface ILabelPickerState { + isPickerVisible: boolean; +} + +@useStatusWithEvent("labelAttrChange", "labelChange") +class LabelPicker extends Component { + + public constructor(props: ILabelPickerProps) { + super(props); + this.state = { + isPickerVisible: false + } + } + + private addButtonRef = createRef(); + + private getOtherLabel() { + let res: Label[] = []; + let nowLabel: Label[] = this.props.labels ?? []; + if (this.props.status) { + this.props.status.model.labelPool.forEach((aLabel) => { + let isHas = false; + nowLabel.forEach((nLabel) => { + if (aLabel.equal(nLabel)) isHas = true; + }) + if (!isHas) { + res.push(aLabel); + } + }) + } + return res; + } public render(): ReactNode { return
{
{ - + deleteLabel={(label) => { + this.props.labelDelete ? this.props.labelDelete(label) : 0; }} addLabel={() => { - + this.setState({ + isPickerVisible: true + }); }} /> + {this.state.isPickerVisible ? { + this.setState({ + isPickerVisible: false + }); + }} + click={(label) => { + if (label instanceof Label && this.props.labelAdd) { + this.props.labelAdd(label) + } + this.setState({ + isPickerVisible: false + }); + }} + target={this.addButtonRef} + /> : null}
} diff --git a/source/Component/PickerList/PickerList.scss b/source/Component/PickerList/PickerList.scss new file mode 100644 index 0000000..f080962 --- /dev/null +++ b/source/Component/PickerList/PickerList.scss @@ -0,0 +1,72 @@ +div.picker-list-root { + max-width: 200px; + padding: 0px; + margin: 0px; + overflow-y: auto; + + div.picker-list-item { + width: 200px; + height: 36px; + display: flex; + justify-content: center; + align-items: center; + vertical-align: middle; + cursor: pointer; + user-select: none; + border-radius: 3px; + overflow: hidden; + + div.list-item-color { + width: 3px; + height: calc( 100% - 6px ); + margin: 3px 0 3px 3px; + flex-shrink: 0; + border-radius: 1000px; + overflow: hidden; + background-color: black; + } + + div.list-item-icon { + width: 30px; + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + } + + div.list-item-name { + width: 100%; + } + } + + div.picker-list-item:hover { + background-color: rgba($color: #000000, $alpha: .1); + } + + span.picker-list-nodata { + display: inline-block; + padding: 5px; + } +} + +div.picker-list-root::-webkit-scrollbar { + width : 0; /*高宽分别对应横竖滚动条的尺寸*/ + height: 0; +} + +div.picker-list-root::-webkit-scrollbar { + width : 8px; /*高宽分别对应横竖滚动条的尺寸*/ + height: 0; +} + +div.picker-list-root::-webkit-scrollbar-thumb { + /*滚动条里面小方块*/ + border-radius: 8px; + background-color: rgba($color: #000000, $alpha: .1); +} + +div.picker-list-root::-webkit-scrollbar-track { + /*滚动条里面轨道*/ + border-radius: 8px; + background-color: rgba($color: #000000, $alpha: 0); +} \ No newline at end of file diff --git a/source/Component/PickerList/PickerList.tsx b/source/Component/PickerList/PickerList.tsx index 108a62d..47daa87 100644 --- a/source/Component/PickerList/PickerList.tsx +++ b/source/Component/PickerList/PickerList.tsx @@ -1,11 +1,86 @@ -import { Component, ReactNode } from "react"; +import { Localization } from "@Component/Localization/Localization"; +import { Callout, DirectionalHint, Icon } from "@fluentui/react"; +import { CtrlObject } from "@Model/CtrlObject"; +import { Group } from "@Model/Group"; +import { Label } from "@Model/Label"; +import { Range } from "@Model/Range"; +import { Component, ReactNode, RefObject } from "react"; +import "./PickerList.scss"; -interface IPickerListProps {} +type IPickerListItem = CtrlObject | Label; + +interface IPickerListProps { + objectList?: IPickerListItem[]; + target?: RefObject; + dismiss?: () => any; + click?: (item: IPickerListItem) => any; +} class PickerList extends Component { + private renderItem(item: IPickerListItem) { + + let color: number[] = []; + let icon: string = "tag"; + let name: string = ""; + + if (item instanceof Range) { + icon = "CubeShape" + } + if (item instanceof Group) { + icon = "WebAppBuilderFragment" + } + if (item instanceof CtrlObject) { + color[0] = Math.round(item.color[0] * 255); + color[1] = Math.round(item.color[1] * 255); + color[2] = Math.round(item.color[2] * 255); + name = item.displayName; + } + if (item instanceof Label) { + icon = "tag"; + color = item.color.concat([]); + name = item.name; + } + + return
{ + if (this.props.click) { + this.props.click(item) + } + }} + > +
+
+ +
+
+ {name} +
+
; + } + public render(): ReactNode { - return
+ 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 + } +
+
} } diff --git a/source/Context/Status.tsx b/source/Context/Status.tsx index 27bea80..8853e52 100644 --- a/source/Context/Status.tsx +++ b/source/Context/Status.tsx @@ -30,6 +30,7 @@ interface IStatusEvent { focusObjectChange: void; focusLabelChange: void; objectChange: void; + rangeLabelChange: void; labelChange: void; rangeAttrChange: void; labelAttrChange: void; @@ -121,6 +122,22 @@ class Status extends Emitter { } } + public addRangeLabel(id: ObjectID, val: Label) { + const range = this.model.getObjectById(id); + if (range && range instanceof Range) { + range.addLabel(val); + this.emit("rangeLabelChange"); + } + } + + public deleteRangeLabel(id: ObjectID, val: Label) { + const range = this.model.getObjectById(id); + if (range && range instanceof Range) { + range.removeLabel(val); + this.emit("rangeLabelChange"); + } + } + /** * 修改范围属性 */ @@ -137,7 +154,7 @@ class Status extends Emitter { findLabel[key] = val; this.emit("labelAttrChange"); } - } + } /** * 鼠标工具状态 diff --git a/source/Localization/EN-US.ts b/source/Localization/EN-US.ts index 6712c93..3554142 100644 --- a/source/Localization/EN-US.ts +++ b/source/Localization/EN-US.ts @@ -53,6 +53,7 @@ const EN_US = { "Common.Attr.Key.Delete": "Delete", "Common.Attr.Key.Label": "Label", "Common.Attr.Key.Error.Multiple": "Multiple values", + "Common.Attr.Key.Label.Picker.Nodata": "No tags can be added", "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.Label.Details.Error.Unspecified": "Label object not specified", diff --git a/source/Localization/ZH-CN.ts b/source/Localization/ZH-CN.ts index 8e4bce6..8e63ed4 100644 --- a/source/Localization/ZH-CN.ts +++ b/source/Localization/ZH-CN.ts @@ -53,6 +53,7 @@ const ZH_CN = { "Common.Attr.Key.Delete": "删除", "Common.Attr.Key.Label": "标签", "Common.Attr.Key.Error.Multiple": "多重数值", + "Common.Attr.Key.Label.Picker.Nodata": "没有可以被添加的标签", "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", "Panel.Info.Label.Details.Error.Unspecified": "未指定标签对象", diff --git a/source/Panel/RangeDetails/RangeDetails.tsx b/source/Panel/RangeDetails/RangeDetails.tsx index 6326b64..5a74f57 100644 --- a/source/Panel/RangeDetails/RangeDetails.tsx +++ b/source/Panel/RangeDetails/RangeDetails.tsx @@ -10,7 +10,7 @@ import { TogglesInput } from "@Component/TogglesInput/TogglesInput"; import { LabelPicker } from "@Component/LabelPicker/LabelPicker"; import "./RangeDetails.scss"; -@useStatusWithEvent("rangeAttrChange", "focusObjectChange") +@useStatusWithEvent("rangeAttrChange", "focusObjectChange", "rangeLabelChange") class RangeDetails extends Component { public readonly AttrI18nKey: AllI18nKeys[] = [ @@ -62,8 +62,17 @@ class RangeDetails extends Component { })} { + if (this.props.status) { + this.props.status.addRangeLabel(range.id, label); + } + }} + labelDelete={(label) => { + if (this.props.status) { + this.props.status.deleteRangeLabel(range.id, label); + } + }} /> {