Add label details panel
This commit is contained in:
parent
27688b9471
commit
1b401dda57
@ -19,9 +19,9 @@ steps:
|
||||
commands:
|
||||
- npm config set registry https://registry.npm.taobao.org
|
||||
- npm ci
|
||||
- npm run release-lab
|
||||
- rm -rf ./build_lab/*
|
||||
- cp ./build/* ./build_lab
|
||||
# - npm run release-lab
|
||||
# - rm -rf ./build_lab/*
|
||||
# - cp ./build/* ./build_lab
|
||||
- npm run release-web
|
||||
- rm -rf ./build_web/*
|
||||
- cp ./build/* ./build_web
|
||||
|
@ -38,6 +38,7 @@ div.label {
|
||||
}
|
||||
|
||||
div.dark.label {
|
||||
transition: none;
|
||||
background-color: $lt-bg-color-lvl3-dark;
|
||||
|
||||
div.label-color {
|
||||
@ -45,12 +46,12 @@ div.dark.label {
|
||||
}
|
||||
|
||||
div.delete-button:hover {
|
||||
color: $lt-font-color-lvl2-dark;
|
||||
background-color: $lt-bg-color-lvl2-dark;
|
||||
color: $lt-red;
|
||||
}
|
||||
}
|
||||
|
||||
div.light.label {
|
||||
transition: none;
|
||||
background-color: $lt-bg-color-lvl3-light;
|
||||
|
||||
div.label-color {
|
||||
@ -58,7 +59,18 @@ div.light.label {
|
||||
}
|
||||
|
||||
div.delete-button:hover {
|
||||
color: $lt-font-color-lvl2-light;
|
||||
background-color: $lt-bg-color-lvl2-light;
|
||||
color: $lt-red;
|
||||
}
|
||||
}
|
||||
|
||||
div.dark.label:hover,
|
||||
div.dark.label.focus {
|
||||
color: $lt-font-color-lvl2-dark;
|
||||
background-color: $lt-bg-color-lvl2-dark;
|
||||
}
|
||||
|
||||
div.light.label:hover,
|
||||
div.light.label.focus {
|
||||
color: $lt-font-color-lvl2-light;
|
||||
background-color: $lt-bg-color-lvl2-light;
|
||||
}
|
@ -2,54 +2,82 @@ import { Component } from "react";
|
||||
import { Label } from "@Model/Label";
|
||||
import { Icon } from "@fluentui/react";
|
||||
import { useSetting, IMixinSettingProps, Themes } from "@Context/Setting";
|
||||
import { ErrorMessage } from "@Component/ErrorMessage/ErrorMessage";
|
||||
import "./LabelList.scss";
|
||||
|
||||
interface ILabelListProps {
|
||||
labels: Label[];
|
||||
canDelete?: boolean;
|
||||
}
|
||||
|
||||
interface ILabelListState {
|
||||
focusLabel?: Label;
|
||||
clickLabel?: (label: Label) => any;
|
||||
deleteLabel?: (label: Label) => any;
|
||||
}
|
||||
|
||||
@useSetting
|
||||
class LabelList extends Component<ILabelListProps & IMixinSettingProps, ILabelListState> {
|
||||
|
||||
public state: Readonly<ILabelListState> = {
|
||||
focusLabel: undefined
|
||||
};
|
||||
class LabelList extends Component<ILabelListProps & IMixinSettingProps> {
|
||||
|
||||
private isDeleteClick: boolean = false;
|
||||
|
||||
private renderLabel(label: Label) {
|
||||
|
||||
const theme = this.props.setting?.themes ?? Themes.dark;
|
||||
const themeClassName = theme === Themes.dark ? "dark" : "light";
|
||||
const classList:string[] = ["label"];
|
||||
classList.push( theme === Themes.dark ? "dark" : "light" );
|
||||
const isFocus = this.props.focusLabel && this.props.focusLabel.equal(label);
|
||||
if (isFocus) {
|
||||
classList.push("focus");
|
||||
}
|
||||
const colorCss = `rgb(${label.color.join(",")})`;
|
||||
|
||||
return <div className={`label ${themeClassName}`} key={label.id}>
|
||||
return <div
|
||||
className={classList.join(" ")}
|
||||
key={label.id}
|
||||
onClick={() => {
|
||||
if (this.props.clickLabel && !this.isDeleteClick) {
|
||||
this.props.clickLabel(label);
|
||||
}
|
||||
this.isDeleteClick = false;
|
||||
}}
|
||||
style={{
|
||||
borderColor: isFocus ? colorCss : undefined
|
||||
}}
|
||||
>
|
||||
<div className="label-color" style={{
|
||||
backgroundColor: colorCss
|
||||
backgroundColor: colorCss,
|
||||
borderRadius: isFocus ? 0 : 3
|
||||
}}/>
|
||||
<div className="label-name">
|
||||
{label.name}
|
||||
</div>
|
||||
{
|
||||
this.props.canDelete ?
|
||||
<div className="delete-button">
|
||||
<div
|
||||
className="delete-button"
|
||||
onClick={() => {
|
||||
this.isDeleteClick = true;
|
||||
if (this.props.deleteLabel) {
|
||||
this.props.deleteLabel(label);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon iconName="delete"></Icon>
|
||||
</div> : null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
private renderAllLabels(labels: Label[]) {
|
||||
return this.props.labels.map((label) => {
|
||||
return this.renderLabel(label);
|
||||
});
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <>
|
||||
{
|
||||
this.props.labels.map((label) => {
|
||||
return this.renderLabel(label);
|
||||
})
|
||||
}
|
||||
</>
|
||||
if (this.props.labels.length > 0) {
|
||||
return this.renderAllLabels(this.props.labels);
|
||||
} else {
|
||||
return <ErrorMessage i18nKey="Panel.Info.Label.List.Error.Nodata"/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { createContext, Component, FunctionComponent, useState, useEffect, ReactNode } from "react";
|
||||
import { Emitter } from "@Model/Emitter";
|
||||
import { Model, ObjectID } from "@Model/Model";
|
||||
import { Label } from "@Model/Label";
|
||||
import { Range } from "@Model/Range";
|
||||
import { Archive } from "@Model/Archive";
|
||||
import { AbstractRenderer } from "@Model/Renderer";
|
||||
@ -27,6 +28,7 @@ interface IStatusEvent {
|
||||
physicsLoop: number;
|
||||
mouseModChange: void;
|
||||
focusObjectChange: void;
|
||||
focusLabelChange: void;
|
||||
objectChange: void;
|
||||
labelChange: void;
|
||||
rangeAttrChange: void;
|
||||
@ -62,6 +64,11 @@ class Status extends Emitter<IStatusEvent> {
|
||||
*/
|
||||
public focusObject: Set<ObjectID> = new Set();
|
||||
|
||||
/**
|
||||
* 焦点标签
|
||||
*/
|
||||
public focusLabel?: Label;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
@ -92,6 +99,14 @@ class Status extends Emitter<IStatusEvent> {
|
||||
this.emit("focusObjectChange");
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新焦点标签
|
||||
*/
|
||||
public setLabelObject(focusLabel?: Label) {
|
||||
this.focusLabel = focusLabel;
|
||||
this.emit("focusLabelChange");
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改范围属性
|
||||
*/
|
||||
|
@ -36,7 +36,9 @@ const EN_US = {
|
||||
"Panel.Title.Range.Details.View": "Range attributes",
|
||||
"Panel.Info.Range.Details.View": "Edit view range attributes",
|
||||
"Panel.Title.Label.List.View": "Label list",
|
||||
"Panel.Info.Label.List.View": "Edit view label attributes",
|
||||
"Panel.Info.Label.List.View": "Edit view label list",
|
||||
"Panel.Title.Label.Details.View": "Label attributes",
|
||||
"Panel.Info.Label.Details.View": "Edit view label attributes",
|
||||
"Common.Attr.Key.Display.Name": "Display name",
|
||||
"Common.Attr.Key.Position.X": "Position X",
|
||||
"Common.Attr.Key.Position.Y": "Position Y",
|
||||
@ -50,5 +52,7 @@ const EN_US = {
|
||||
"Common.Attr.Key.Error.Multiple": "Multiple values",
|
||||
"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",
|
||||
"Panel.Info.Label.List.Error.Nodata": "There are no labels in the model, click the button to create",
|
||||
}
|
||||
export default EN_US;
|
@ -32,11 +32,13 @@ const ZH_CN = {
|
||||
"Panel.Title.Render.View": "实时预览",
|
||||
"Panel.Info.Render.View": "实时仿真结果预览",
|
||||
"Panel.Title.Object.List.View": "对象列表",
|
||||
"Panel.Info.Object.List.View": "编辑查看全部对象属性",
|
||||
"Panel.Info.Object.List.View": "编辑查看全部对象列表",
|
||||
"Panel.Title.Range.Details.View": "范围属性",
|
||||
"Panel.Info.Range.Details.View": "编辑查看范围属性",
|
||||
"Panel.Title.Label.List.View": "标签列表",
|
||||
"Panel.Info.Label.List.View": "编辑查看标签属性",
|
||||
"Panel.Info.Label.List.View": "编辑查看标签列表",
|
||||
"Panel.Title.Label.Details.View": "标签属性",
|
||||
"Panel.Info.Label.Details.View": "编辑查看标签属性",
|
||||
"Common.Attr.Key.Display.Name": "显示名称",
|
||||
"Common.Attr.Key.Position.X": "X 坐标",
|
||||
"Common.Attr.Key.Position.Y": "Y 坐标",
|
||||
@ -50,5 +52,7 @@ const ZH_CN = {
|
||||
"Common.Attr.Key.Error.Multiple": "多重数值",
|
||||
"Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围",
|
||||
"Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象",
|
||||
"Panel.Info.Label.Details.Error.Unspecified": "未指定标签对象",
|
||||
"Panel.Info.Label.List.Error.Nodata": "模型中没有标签,点击按钮以创建",
|
||||
}
|
||||
export default ZH_CN;
|
@ -77,7 +77,7 @@ class SimulatorWeb extends Component {
|
||||
items: [{
|
||||
panels: ["ObjectList", "Test tab"]
|
||||
}, {
|
||||
panels: ["RangeDetails", "Label e"]
|
||||
panels: ["RangeDetails", "LabelDetails"]
|
||||
}],
|
||||
layout: LayoutDirection.Y
|
||||
}
|
||||
|
0
source/Panel/LabelDetails/LabelDetails.scss
Normal file
0
source/Panel/LabelDetails/LabelDetails.scss
Normal file
54
source/Panel/LabelDetails/LabelDetails.tsx
Normal file
54
source/Panel/LabelDetails/LabelDetails.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
import { Component, ReactNode } from "react";
|
||||
import { AttrInput } from "@Component/AttrInput/AttrInput";
|
||||
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
||||
import { AllI18nKeys } from "@Component/Localization/Localization";
|
||||
import { ErrorMessage } from "@Component/ErrorMessage/ErrorMessage";
|
||||
import { ColorInput } from "@Component/ColorInput/ColorInput";
|
||||
import "./LabelDetails.scss";
|
||||
import { LabelList } from "@Component/LabelList/LabelList";
|
||||
import { Label } from "@Model/Label";
|
||||
|
||||
@useStatusWithEvent("focusLabelChange")
|
||||
class LabelDetails extends Component<IMixinStatusProps> {
|
||||
|
||||
public readonly AttrI18nKey: AllI18nKeys[] = [
|
||||
"Common.Attr.Key.Display.Name",
|
||||
"Common.Attr.Key.Color",
|
||||
]
|
||||
|
||||
private renderFrom(label: Label) {
|
||||
return <>
|
||||
|
||||
<LabelList
|
||||
labels={[label]}
|
||||
canDelete
|
||||
deleteLabel={() => {
|
||||
if (this.props.status) {
|
||||
this.props.status.model.deleteLabel(label);
|
||||
this.props.status.setLabelObject();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<AttrInput keyI18n="Common.Attr.Key.Display.Name" maxLength={15} value={label.name}/>
|
||||
|
||||
<ColorInput keyI18n="Common.Attr.Key.Color" value={label.color} valueChange={(color) => {
|
||||
if (this.props.status) {
|
||||
|
||||
}
|
||||
}}/>
|
||||
|
||||
</>;
|
||||
}
|
||||
|
||||
public render(): ReactNode {
|
||||
if (this.props.status) {
|
||||
if (this.props.status.focusLabel) {
|
||||
return this.renderFrom(this.props.status.focusLabel);
|
||||
}
|
||||
}
|
||||
return <ErrorMessage i18nKey="Panel.Info.Label.Details.Error.Unspecified"/>;
|
||||
}
|
||||
}
|
||||
|
||||
export { LabelDetails };
|
@ -1,33 +1,8 @@
|
||||
@import "../../Component/Theme/Theme.scss";
|
||||
|
||||
div.label-list-command-bar {
|
||||
div.label-list-panel-root {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
|
||||
div.command-item {
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
div.dark.label-list-command-bar {
|
||||
|
||||
div.command-item:hover {
|
||||
background-color: $lt-bg-color-lvl3-dark;
|
||||
}
|
||||
}
|
||||
|
||||
div.light.label-list-command-bar {
|
||||
|
||||
div.command-item:hover {
|
||||
background-color: $lt-bg-color-lvl3-light;
|
||||
}
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { Theme } from "@Component/Theme/Theme";
|
||||
import { LabelList as LabelListComponent } from "@Component/LabelList/LabelList";
|
||||
import { Component } from "react";
|
||||
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
|
||||
import { useSetting, IMixinSettingProps } from "@Context/Setting";
|
||||
import { Label } from "@Model/Label";
|
||||
import "./LabelList.scss";
|
||||
|
||||
@ -9,15 +9,48 @@ interface ILabelListProps {
|
||||
|
||||
}
|
||||
|
||||
@useStatusWithEvent("labelChange")
|
||||
class LabelList extends Component<ILabelListProps & IMixinStatusProps> {
|
||||
@useSetting
|
||||
@useStatusWithEvent("labelChange", "focusLabelChange")
|
||||
class LabelList extends Component<ILabelListProps & IMixinStatusProps & IMixinSettingProps> {
|
||||
|
||||
private labelInnerClick: boolean = false;
|
||||
|
||||
public render() {
|
||||
let labels: Label[] = [];
|
||||
if (this.props.status) {
|
||||
labels = this.props.status.model.labelPool.concat([]);
|
||||
}
|
||||
return <LabelListComponent labels={labels} canDelete/>
|
||||
return <div
|
||||
className="label-list-panel-root"
|
||||
onClick={() => {
|
||||
if (this.props.status && !this.labelInnerClick) {
|
||||
this.props.status.setLabelObject();
|
||||
}
|
||||
this.labelInnerClick = false;
|
||||
}}
|
||||
>
|
||||
<LabelListComponent
|
||||
canDelete
|
||||
labels={labels}
|
||||
focusLabel={this.props.status ? this.props.status.focusLabel : undefined}
|
||||
clickLabel={(label) => {
|
||||
if (this.props.status) {
|
||||
this.props.status.setLabelObject(label);
|
||||
}
|
||||
if (this.props.setting) {
|
||||
this.props.setting.layout.focus("LabelDetails");
|
||||
}
|
||||
this.labelInnerClick = true;
|
||||
}}
|
||||
deleteLabel={(label) => {
|
||||
if (this.props.status) {
|
||||
this.props.status.model.deleteLabel(label);
|
||||
this.props.status.setLabelObject();
|
||||
}
|
||||
this.labelInnerClick = true;
|
||||
}}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,12 +36,15 @@ class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> {
|
||||
}
|
||||
}))}
|
||||
clickLine={(item) => {
|
||||
if (this.props.setting) {
|
||||
this.props.setting.layout.focus("ObjectList");
|
||||
}
|
||||
if (this.props.status) {
|
||||
this.props.status.setFocusObject(new Set<ObjectID>().add(item.key));
|
||||
}
|
||||
if (this.props.setting) {
|
||||
if (item.key.slice(0, 1) === "R") {
|
||||
this.props.setting.layout.focus("RangeDetails");
|
||||
}
|
||||
this.props.setting.layout.focus("ObjectList");
|
||||
}
|
||||
}}
|
||||
checkBox={(item) => {
|
||||
if (this.props.setting) {
|
||||
|
@ -6,6 +6,7 @@ import { ObjectList } from "./ObjectList/ObjectList";
|
||||
import { ObjectCommand } from "./ObjectList/ObjectCommand";
|
||||
import { RangeDetails } from "./RangeDetails/RangeDetails";
|
||||
import { LabelList } from "./LabelList/LabelList";
|
||||
import { LabelDetails } from "./LabelDetails/LabelDetails";
|
||||
|
||||
interface IPanelInfo {
|
||||
nameKey: string;
|
||||
@ -23,6 +24,7 @@ type PanelId = ""
|
||||
| "ObjectList" // 对象列表
|
||||
| "RangeDetails" // 范围属性
|
||||
| "LabelList" // 标签列表
|
||||
| "LabelDetails" // 标签属性
|
||||
;
|
||||
|
||||
const PanelInfoMap = new Map<PanelId, IPanelInfo>();
|
||||
@ -40,7 +42,11 @@ PanelInfoMap.set("RangeDetails", {
|
||||
})
|
||||
PanelInfoMap.set("LabelList", {
|
||||
nameKey: "Panel.Title.Label.List.View", introKay: "Panel.Info.Label.List.View",
|
||||
class: LabelList
|
||||
class: LabelList, hidePadding: true
|
||||
})
|
||||
PanelInfoMap.set("LabelDetails", {
|
||||
nameKey: "Panel.Title.Label.Details.View", introKay: "Panel.Info.Label.Details.View",
|
||||
class: LabelDetails
|
||||
})
|
||||
|
||||
function getPanelById(panelId: PanelId): ReactNode {
|
||||
|
Loading…
Reference in New Issue
Block a user