Merge pull request 'Add label list component & panel & message component' (#17) from dev-mrkbear into master
Reviewed-on: http://git.mrkbear.com/MrKBear/living-together/pulls/17
This commit is contained in:
commit
edb24b27fb
@ -24,6 +24,13 @@ class AttrInput extends Component<IAttrInputProps> {
|
||||
|
||||
private value: string = "";
|
||||
private error: ReactNode;
|
||||
private numberTestReg = [/\.0*$/, /[1-9]+0+$/];
|
||||
|
||||
private numberTester(value: string) {
|
||||
return isNaN((value as any) / 1) ||
|
||||
this.numberTestReg[0].test(value) ||
|
||||
this.numberTestReg[1].test(value);
|
||||
}
|
||||
|
||||
private check(value: string): ReactNode {
|
||||
|
||||
@ -37,7 +44,7 @@ class AttrInput extends Component<IAttrInputProps> {
|
||||
const praseNumber = (value as any) / 1;
|
||||
|
||||
// 数字校验
|
||||
if (isNaN(praseNumber) || /\.0*$/.test(value)) {
|
||||
if (this.numberTester(value)) {
|
||||
return <Localization i18nKey="Input.Error.Not.Number" />
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ div.color-input-root {
|
||||
div.color-box {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,13 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi
|
||||
}
|
||||
})}
|
||||
{this.getRenderButton({ iconName: "StepSharedAdd", i18NKey: "Command.Bar.Add.Behavior.Info" })}
|
||||
{this.getRenderButton({ iconName: "Tag", i18NKey: "Command.Bar.Add.Tag.Info" })}
|
||||
{this.getRenderButton({
|
||||
iconName: "Tag",
|
||||
i18NKey: "Command.Bar.Add.Tag.Info",
|
||||
click: () => {
|
||||
this.props.status ? this.props.status.newLabel() : undefined;
|
||||
}
|
||||
})}
|
||||
{this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })}
|
||||
</div>
|
||||
<div>
|
||||
|
@ -34,11 +34,11 @@ class Container extends Component<IContainerProps> {
|
||||
/**
|
||||
* 渲染此 Tab 下的 ELE
|
||||
*/
|
||||
private renderPanel(panles: string[], showBar: boolean, focus?: string) {
|
||||
private renderPanel(panels: string[], showBar: boolean, focus?: string) {
|
||||
|
||||
const classList: string[] = [];
|
||||
const theme: Themes = this.props.theme ?? Themes.dark;
|
||||
const showPanelId = focus ?? panles[0];
|
||||
const showPanelId = focus ?? panels[0];
|
||||
const showPanelInfo = getPanelInfoById(showPanelId as any);
|
||||
|
||||
classList.push(theme === Themes.light ? "light" : "dark");
|
||||
@ -46,14 +46,14 @@ class Container extends Component<IContainerProps> {
|
||||
classList.push(`font-${FontLevel.Level3}`);
|
||||
classList.push("app-tab-header");
|
||||
|
||||
const hasActivePanel = panles.some((id) => id === this.props.focusId);
|
||||
const hasActivePanel = panels.some((id) => id === this.props.focusId);
|
||||
|
||||
return <>
|
||||
{showBar ?
|
||||
<div className={classList.join(" ")} onClick={() => {
|
||||
this.props.onFocusTab ? this.props.onFocusTab("") : undefined
|
||||
}}>{
|
||||
panles.map((panelId: string) => {
|
||||
panels.map((panelId: string) => {
|
||||
|
||||
const classList: string[] = ["app-tab-header-item"];
|
||||
if (panelId === this.props.focusId) classList.push("active");
|
||||
@ -189,7 +189,7 @@ class Container extends Component<IContainerProps> {
|
||||
|
||||
const items: [IContainerProps, IContainerProps] | undefined = props.items;
|
||||
const showBar: boolean = props.showBar ?? true;
|
||||
const panles: string[] = props.panles ?? [];
|
||||
const panels: string[] = props.panels ?? [];
|
||||
const layout: LayoutDirection = props.layout ?? LayoutDirection.Y;
|
||||
const scale: number = props.scale ?? 50;
|
||||
const isRoot: boolean = !!props.isRoot;
|
||||
@ -201,7 +201,7 @@ class Container extends Component<IContainerProps> {
|
||||
classList.push(`background-${BackgroundLevel.Level4}`);
|
||||
classList.push(`font-${FontLevel.normal}`);
|
||||
classList.push("app-container");
|
||||
if (panles.length > 0 && !items) classList.push("end-containe");
|
||||
if (panels.length > 0 && !items) classList.push("end-containe");
|
||||
|
||||
return <div
|
||||
className={classList.join(" ")}
|
||||
@ -216,7 +216,7 @@ class Container extends Component<IContainerProps> {
|
||||
onMouseUp={isRoot ? () => this.focusEdgeId = undefined : undefined}
|
||||
>
|
||||
{/* 渲染 Panel */}
|
||||
{panles.length > 0 && !items ? this.renderPanel(panles, showBar, focusPanel) : null}
|
||||
{panels.length > 0 && !items ? this.renderPanel(panels, showBar, focusPanel) : null}
|
||||
|
||||
{/* 渲染第一部分 */}
|
||||
{items && items[0] ? this.renderContainer(items[0], scale, layout) : null}
|
||||
|
3
source/Component/ErrorMessage/ErrorMessage.scss
Normal file
3
source/Component/ErrorMessage/ErrorMessage.scss
Normal file
@ -0,0 +1,3 @@
|
||||
div.panel-error-message {
|
||||
padding-top: 5px;
|
||||
}
|
16
source/Component/ErrorMessage/ErrorMessage.tsx
Normal file
16
source/Component/ErrorMessage/ErrorMessage.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { AllI18nKeys, Localization } from "@Component/Localization/Localization";
|
||||
import { FunctionComponent } from "react";
|
||||
import "./ErrorMessage.scss";
|
||||
|
||||
interface IErrorMessageProps {
|
||||
i18nKey: AllI18nKeys;
|
||||
options?: Record<string, string>;
|
||||
}
|
||||
|
||||
const ErrorMessage: FunctionComponent<IErrorMessageProps> = (props) => {
|
||||
return <div className="panel-error-message">
|
||||
<Localization i18nKey={props.i18nKey} options={props.options}/>
|
||||
</div>
|
||||
}
|
||||
|
||||
export { ErrorMessage };
|
64
source/Component/LabelList/LabelList.scss
Normal file
64
source/Component/LabelList/LabelList.scss
Normal file
@ -0,0 +1,64 @@
|
||||
@import "../Theme/Theme.scss";
|
||||
|
||||
div.label {
|
||||
width: auto;
|
||||
height: auto;
|
||||
display: inline-flex;
|
||||
margin: 5px 5px;
|
||||
justify-content: center;
|
||||
vertical-align: middle;
|
||||
align-items: stretch;
|
||||
border-radius: 3px;
|
||||
border: .5px solid transparent;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
|
||||
div.label-color {
|
||||
width: 3px;
|
||||
margin-right: 2px;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
div.label-name {
|
||||
padding: 2px 3px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.delete-button {
|
||||
padding: 2px 3px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
div.dark.label {
|
||||
background-color: $lt-bg-color-lvl3-dark;
|
||||
|
||||
div.label-color {
|
||||
color: $lt-bg-color-lvl3-dark;
|
||||
}
|
||||
|
||||
div.delete-button:hover {
|
||||
color: $lt-font-color-lvl2-dark;
|
||||
background-color: $lt-bg-color-lvl2-dark;
|
||||
}
|
||||
}
|
||||
|
||||
div.light.label {
|
||||
background-color: $lt-bg-color-lvl3-light;
|
||||
|
||||
div.label-color {
|
||||
color: $lt-bg-color-lvl3-light;
|
||||
}
|
||||
|
||||
div.delete-button:hover {
|
||||
color: $lt-font-color-lvl2-light;
|
||||
background-color: $lt-bg-color-lvl2-light;
|
||||
}
|
||||
}
|
56
source/Component/LabelList/LabelList.tsx
Normal file
56
source/Component/LabelList/LabelList.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { Component } from "react";
|
||||
import { Label } from "@Model/Label";
|
||||
import { Icon } from "@fluentui/react";
|
||||
import { useSetting, IMixinSettingProps, Themes } from "@Context/Setting";
|
||||
import "./LabelList.scss";
|
||||
|
||||
interface ILabelListProps {
|
||||
labels: Label[];
|
||||
canDelete?: boolean;
|
||||
}
|
||||
|
||||
interface ILabelListState {
|
||||
focusLabel?: Label;
|
||||
}
|
||||
|
||||
@useSetting
|
||||
class LabelList extends Component<ILabelListProps & IMixinSettingProps, ILabelListState> {
|
||||
|
||||
public state: Readonly<ILabelListState> = {
|
||||
focusLabel: undefined
|
||||
};
|
||||
|
||||
private renderLabel(label: Label) {
|
||||
|
||||
const theme = this.props.setting?.themes ?? Themes.dark;
|
||||
const themeClassName = theme === Themes.dark ? "dark" : "light";
|
||||
const colorCss = `rgb(${label.color.join(",")})`;
|
||||
|
||||
return <div className={`label ${themeClassName}`} key={label.id}>
|
||||
<div className="label-color" style={{
|
||||
backgroundColor: colorCss
|
||||
}}/>
|
||||
<div className="label-name">
|
||||
{label.name}
|
||||
</div>
|
||||
{
|
||||
this.props.canDelete ?
|
||||
<div className="delete-button">
|
||||
<Icon iconName="delete"></Icon>
|
||||
</div> : null
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <>
|
||||
{
|
||||
this.props.labels.map((label) => {
|
||||
return this.renderLabel(label);
|
||||
})
|
||||
}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
export { LabelList };
|
63
source/Component/TogglesInput/TogglesInput.scss
Normal file
63
source/Component/TogglesInput/TogglesInput.scss
Normal file
@ -0,0 +1,63 @@
|
||||
@import "../Theme/Theme.scss";
|
||||
|
||||
$line-min-height: 26px;
|
||||
|
||||
div.toggles-input {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
min-height: $line-min-height;
|
||||
padding: 5px 0;
|
||||
|
||||
div.toggles-intro {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
max-width: 220px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
div.toggles-content {
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
max-width: 180px;
|
||||
min-height: $line-min-height;
|
||||
|
||||
div.checkbox {
|
||||
width: $line-min-height;
|
||||
height: $line-min-height;
|
||||
overflow: hidden;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.dark.toggles-input {
|
||||
|
||||
div.toggles-content div.checkbox {
|
||||
background-color: $lt-bg-color-lvl3-dark;
|
||||
color: $lt-font-color-normal-dark;
|
||||
}
|
||||
|
||||
div.toggles-content div.checkbox:hover {
|
||||
background-color: $lt-bg-color-lvl2-dark;
|
||||
}
|
||||
}
|
||||
|
||||
div.light.toggles-input {
|
||||
|
||||
div.toggles-content div.checkbox {
|
||||
background-color: $lt-bg-color-lvl3-light;
|
||||
color: $lt-font-color-normal-light;
|
||||
}
|
||||
|
||||
div.toggles-content div.checkbox:hover {
|
||||
background-color: $lt-bg-color-lvl2-light;
|
||||
}
|
||||
}
|
45
source/Component/TogglesInput/TogglesInput.tsx
Normal file
45
source/Component/TogglesInput/TogglesInput.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { AllI18nKeys, Localization } from "@Component/Localization/Localization";
|
||||
import { Theme } from "@Component/Theme/Theme";
|
||||
import { Icon } from "@fluentui/react";
|
||||
import { Component, ReactNode } from "react";
|
||||
import "./TogglesInput.scss";
|
||||
|
||||
interface ITogglesInputProps {
|
||||
keyI18n: AllI18nKeys;
|
||||
infoI18n?: AllI18nKeys;
|
||||
value?: boolean;
|
||||
disable?: boolean;
|
||||
valueChange?: (color: boolean) => any;
|
||||
}
|
||||
|
||||
class TogglesInput extends Component<ITogglesInputProps> {
|
||||
public render(): ReactNode {
|
||||
return <Theme className="toggles-input">
|
||||
<div className="toggles-intro">
|
||||
<Localization i18nKey={this.props.keyI18n}/>
|
||||
</div>
|
||||
<div className="toggles-content">
|
||||
<div
|
||||
className="checkbox"
|
||||
style={{
|
||||
cursor: this.props.disable ? "not-allowed" : "pointer"
|
||||
}}
|
||||
onClick={(() => {
|
||||
if (this.props.disable) {
|
||||
return;
|
||||
}
|
||||
if (this.props.valueChange) {
|
||||
this.props.valueChange(!this.props.value);
|
||||
}
|
||||
})}
|
||||
>
|
||||
<Icon iconName="CheckMark" style={{
|
||||
display: this.props.value ? "inline-block" : "none"
|
||||
}}></Icon>
|
||||
</div>
|
||||
</div>
|
||||
</Theme>
|
||||
}
|
||||
}
|
||||
|
||||
export { TogglesInput };
|
@ -8,12 +8,18 @@ import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer";
|
||||
import { Setting } from "./Setting";
|
||||
import { I18N } from "@Component/Localization/Localization";
|
||||
|
||||
function randomColor() {
|
||||
return [
|
||||
function randomColor(unNormal: boolean = false) {
|
||||
const color = [
|
||||
Math.random() * .8 + .2,
|
||||
Math.random() * .8 + .2,
|
||||
Math.random() * .8 + .2, 1
|
||||
]
|
||||
if (unNormal) {
|
||||
color[0] = Math.round(color[0] * 255),
|
||||
color[1] = Math.round(color[1] * 255),
|
||||
color[2] = Math.round(color[2] * 255)
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
interface IStatusEvent {
|
||||
@ -34,6 +40,7 @@ class Status extends Emitter<IStatusEvent> {
|
||||
* 对象命名
|
||||
*/
|
||||
public objectNameIndex = 1;
|
||||
public labelNameIndex = 1;
|
||||
|
||||
/**
|
||||
* 渲染器
|
||||
@ -123,6 +130,17 @@ class Status extends Emitter<IStatusEvent> {
|
||||
return range;
|
||||
}
|
||||
|
||||
public newLabel() {
|
||||
const label = this.model.addLabel(
|
||||
I18N(this.setting.language, "Object.List.New.Label", {
|
||||
id: this.labelNameIndex.toString()
|
||||
})
|
||||
);
|
||||
label.color = randomColor(true);
|
||||
this.labelNameIndex ++;
|
||||
return label;
|
||||
}
|
||||
|
||||
public setMouseMod(mod: MouseMod) {
|
||||
this.mouseMod = mod;
|
||||
if (this.renderer instanceof ClassicRenderer) {
|
||||
|
@ -25,15 +25,18 @@ const EN_US = {
|
||||
"Input.Error.Length": "The length of the input content must be less than {num}",
|
||||
"Object.List.New.Group": "Group object {id}",
|
||||
"Object.List.New.Range": "Range object {id}",
|
||||
"Object.List.New.Label": "Label {id}",
|
||||
"Object.List.No.Data": "There are no objects in the model, click the button to create it",
|
||||
"Panel.Title.Notfound": "{id}",
|
||||
"Panel.Info.Notfound": "This panel with id {id} can not found!",
|
||||
"Panel.Title.Render.View": "Live preview",
|
||||
"Panel.Info.Render.View": "Live simulation results preview",
|
||||
"Panel.Title.Object.List.View": "Object list",
|
||||
"Panel.Info.Object.List.View": "Edit View All Object Properties",
|
||||
"Panel.Info.Object.List.View": "Edit view all Object Properties",
|
||||
"Panel.Title.Range.Details.View": "Range attributes",
|
||||
"Panel.Info.Range.Details.View": "Edit 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",
|
||||
"Common.Attr.Key.Display.Name": "Display name",
|
||||
"Common.Attr.Key.Position.X": "Position X",
|
||||
"Common.Attr.Key.Position.Y": "Position Y",
|
||||
@ -42,6 +45,8 @@ const EN_US = {
|
||||
"Common.Attr.Key.Radius.Y": "Radius Y",
|
||||
"Common.Attr.Key.Radius.Z": "Radius Z",
|
||||
"Common.Attr.Key.Color": "Color",
|
||||
"Common.Attr.Key.Display": "Display",
|
||||
"Common.Attr.Key.Update": "Update",
|
||||
"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",
|
||||
|
@ -25,8 +25,9 @@ const ZH_CN = {
|
||||
"Input.Error.Length": "输入内容长度须小于 {number}",
|
||||
"Object.List.New.Group": "组对象 {id}",
|
||||
"Object.List.New.Range": "范围对象 {id}",
|
||||
"Object.List.New.Label": "标签 {id}",
|
||||
"Object.List.No.Data": "模型中没有任何对象,点击按钮以创建",
|
||||
"Panel.Title.Notfound": "找不到面板: {id}",
|
||||
"Panel.Title.Notfound": "{id}",
|
||||
"Panel.Info.Notfound": "这个编号为 {id} 的面板无法找到!",
|
||||
"Panel.Title.Render.View": "实时预览",
|
||||
"Panel.Info.Render.View": "实时仿真结果预览",
|
||||
@ -34,6 +35,8 @@ const ZH_CN = {
|
||||
"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": "编辑查看标签属性",
|
||||
"Common.Attr.Key.Display.Name": "显示名称",
|
||||
"Common.Attr.Key.Position.X": "X 坐标",
|
||||
"Common.Attr.Key.Position.Y": "Y 坐标",
|
||||
@ -42,6 +45,8 @@ const ZH_CN = {
|
||||
"Common.Attr.Key.Radius.Y": "Y 半径",
|
||||
"Common.Attr.Key.Radius.Z": "Z 半径",
|
||||
"Common.Attr.Key.Color": "颜色",
|
||||
"Common.Attr.Key.Display": "显示",
|
||||
"Common.Attr.Key.Update": "更新",
|
||||
"Common.Attr.Key.Error.Multiple": "多重数值",
|
||||
"Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围",
|
||||
"Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象",
|
||||
|
@ -14,12 +14,12 @@ class Label {
|
||||
/**
|
||||
* 用户定义的名称
|
||||
*/
|
||||
public name?: string;
|
||||
public name: string = "";
|
||||
|
||||
/**
|
||||
* CSS 颜色
|
||||
*/
|
||||
public color?: string;
|
||||
public color: number[] = [0, 0, 0];
|
||||
|
||||
/**
|
||||
* 所属模型
|
||||
@ -34,7 +34,7 @@ class Label {
|
||||
public constructor(model: Model, id: ObjectID, name?: string) {
|
||||
this.model = model;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.name = name ?? this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,7 +7,7 @@ enum LayoutDirection {
|
||||
|
||||
class ILayout {
|
||||
items?: [ILayout, ILayout];
|
||||
panles?: string[];
|
||||
panels?: string[];
|
||||
focusPanel?: string;
|
||||
layout?: LayoutDirection;
|
||||
scale?: number;
|
||||
@ -51,8 +51,8 @@ class Layout extends Emitter<ILayoutEvent> {
|
||||
this.id = 0;
|
||||
this.map((layout) => {
|
||||
layout.id = this.id;
|
||||
if (!layout.focusPanel && layout.panles && layout.panles.length > 0) {
|
||||
layout.focusPanel = layout.panles[0]
|
||||
if (!layout.focusPanel && layout.panels && layout.panels.length > 0) {
|
||||
layout.focusPanel = layout.panels[0]
|
||||
}
|
||||
this.id ++;
|
||||
});
|
||||
@ -80,10 +80,10 @@ class Layout extends Emitter<ILayoutEvent> {
|
||||
}
|
||||
|
||||
this.map((layout) => {
|
||||
if (layout.panles && layout.panles.length > 0) {
|
||||
if (layout.panels && layout.panels.length > 0) {
|
||||
let index = -1;
|
||||
for (let i = 0; i < layout.panles.length; i++) {
|
||||
if (layout.panles[i] === panelId) {
|
||||
for (let i = 0; i < layout.panels.length; i++) {
|
||||
if (layout.panels[i] === panelId) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ class Model extends Emitter<ModelEvent> {
|
||||
* 下一个需要分配的 ID
|
||||
*/
|
||||
private idIndex: number = 1;
|
||||
public get nextId(): number {
|
||||
return this.idIndex ++;
|
||||
public nextId(label: string = "U"): string {
|
||||
return `${label}-${this.idIndex ++}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +55,7 @@ class Model extends Emitter<ModelEvent> {
|
||||
*/
|
||||
public addLabel(name: string): Label {
|
||||
console.log(`Model: Creat label with id ${this.idIndex}`);
|
||||
let label = new Label(this, this.nextId, name);
|
||||
let label = new Label(this, this.nextId("L"), name);
|
||||
this.labelPool.push(label);
|
||||
this.emit("labelAdd", label);
|
||||
this.emit("labelChange", this.labelPool);
|
||||
@ -95,7 +95,7 @@ class Model extends Emitter<ModelEvent> {
|
||||
*/
|
||||
public addGroup(): Group {
|
||||
console.log(`Model: Creat group with id ${this.idIndex}`);
|
||||
let group = new Group(this, this.nextId);
|
||||
let group = new Group(this, this.nextId("G"));
|
||||
this.objectPool.push(group);
|
||||
this.emit("groupAdd", group);
|
||||
this.emit("objectAdd", group);
|
||||
@ -108,7 +108,7 @@ class Model extends Emitter<ModelEvent> {
|
||||
*/
|
||||
public addRange(): Range {
|
||||
console.log(`Model: Creat range with id ${this.idIndex}`);
|
||||
let range = new Range(this, this.nextId);
|
||||
let range = new Range(this, this.nextId("R"));
|
||||
this.objectPool.push(range);
|
||||
this.emit("rangeAdd", range);
|
||||
this.emit("objectAdd", range);
|
||||
|
@ -35,7 +35,7 @@ interface ICommonParam {
|
||||
/**
|
||||
* 对象标识符
|
||||
*/
|
||||
type ObjectID = Symbol | string | number;
|
||||
type ObjectID = string;
|
||||
|
||||
/**
|
||||
* 接收的数据类型
|
||||
|
@ -51,6 +51,8 @@ class SimulatorWeb extends Component {
|
||||
individual.position[2] = (Math.random() - .5) * 2;
|
||||
})
|
||||
this.status.model.update(0);
|
||||
this.status.newLabel().name = "New Label";
|
||||
this.status.newLabel().name = "Test Label 01";
|
||||
}
|
||||
|
||||
(window as any).s = this;
|
||||
@ -61,9 +63,9 @@ class SimulatorWeb extends Component {
|
||||
items: [
|
||||
{
|
||||
items: [
|
||||
{panles: ["RenderView", "Label Aa Bb", "Label aaa"]},
|
||||
{panels: ["RenderView", "Label Aa Bb", "Label aaa"]},
|
||||
{
|
||||
items: [{panles: ["Label b", "Label bbb"]}, {panles: ["C"]}],
|
||||
items: [{panels: ["Label b", "Label bbb"]}, {panels: ["LabelList"]}],
|
||||
scale: 80,
|
||||
layout: LayoutDirection.X
|
||||
}
|
||||
@ -73,9 +75,9 @@ class SimulatorWeb extends Component {
|
||||
},
|
||||
{
|
||||
items: [{
|
||||
panles: ["ObjectList", "Test tab"]
|
||||
panels: ["ObjectList", "Test tab"]
|
||||
}, {
|
||||
panles: ["RangeDetails", "Label e"]
|
||||
panels: ["RangeDetails", "Label e"]
|
||||
}],
|
||||
layout: LayoutDirection.Y
|
||||
}
|
||||
|
33
source/Panel/LabelList/LabelList.scss
Normal file
33
source/Panel/LabelList/LabelList.scss
Normal file
@ -0,0 +1,33 @@
|
||||
@import "../../Component/Theme/Theme.scss";
|
||||
|
||||
div.label-list-command-bar {
|
||||
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;
|
||||
}
|
||||
}
|
24
source/Panel/LabelList/LabelList.tsx
Normal file
24
source/Panel/LabelList/LabelList.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
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 { Label } from "@Model/Label";
|
||||
import "./LabelList.scss";
|
||||
|
||||
interface ILabelListProps {
|
||||
|
||||
}
|
||||
|
||||
@useStatusWithEvent("labelChange")
|
||||
class LabelList extends Component<ILabelListProps & IMixinStatusProps> {
|
||||
|
||||
public render() {
|
||||
let labels: Label[] = [];
|
||||
if (this.props.status) {
|
||||
labels = this.props.status.model.labelPool.concat([]);
|
||||
}
|
||||
return <LabelListComponent labels={labels} canDelete/>
|
||||
}
|
||||
}
|
||||
|
||||
export { LabelList };
|
@ -1,10 +1,11 @@
|
||||
import { ReactNode, Component, FunctionComponent } from "react";
|
||||
import { Theme } from "@Component/Theme/Theme";
|
||||
import { Localization } from "@Component/Localization/Localization";
|
||||
import { ErrorMessage } from "@Component/ErrorMessage/ErrorMessage";
|
||||
import { RenderView } from "./RenderView/RenderView";
|
||||
import { ObjectList } from "./ObjectList/ObjectList";
|
||||
import { ObjectCommand } from "./ObjectList/ObjectCommand";
|
||||
import { RangeDetails } from "./RangeDetails/RangeDetails";
|
||||
import { LabelList } from "./LabelList/LabelList";
|
||||
|
||||
interface IPanelInfo {
|
||||
nameKey: string;
|
||||
@ -21,6 +22,7 @@ type PanelId = ""
|
||||
| "RenderView" // 主渲染器
|
||||
| "ObjectList" // 对象列表
|
||||
| "RangeDetails" // 范围属性
|
||||
| "LabelList" // 标签列表
|
||||
;
|
||||
|
||||
const PanelInfoMap = new Map<PanelId, IPanelInfo>();
|
||||
@ -36,6 +38,10 @@ PanelInfoMap.set("RangeDetails", {
|
||||
nameKey: "Panel.Title.Range.Details.View", introKay: "Panel.Info.Range.Details.View",
|
||||
class: RangeDetails
|
||||
})
|
||||
PanelInfoMap.set("LabelList", {
|
||||
nameKey: "Panel.Title.Label.List.View", introKay: "Panel.Info.Label.List.View",
|
||||
class: LabelList
|
||||
})
|
||||
|
||||
function getPanelById(panelId: PanelId): ReactNode {
|
||||
switch (panelId) {
|
||||
@ -45,9 +51,7 @@ function getPanelById(panelId: PanelId): ReactNode {
|
||||
const C = info.class;
|
||||
return <C></C>
|
||||
} else return <Theme>
|
||||
<Localization i18nKey={"Panel.Info.Notfound"} options={{
|
||||
id: panelId
|
||||
}}/>
|
||||
<ErrorMessage i18nKey={"Panel.Info.Notfound"} options={{ id: panelId }}/>
|
||||
</Theme>
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { Component, ReactNode } from "react";
|
||||
import { AttrInput } from "@Component/AttrInput/AttrInput";
|
||||
import { useStatusWithEvent, IMixinStatusProps, Status } from "@Context/Status";
|
||||
import { AllI18nKeys } from "@Component/Localization/Localization";
|
||||
import { AllI18nKeys, Localization } from "@Component/Localization/Localization";
|
||||
import { ErrorMessage } from "@Component/ErrorMessage/ErrorMessage";
|
||||
import { Range } from "@Model/Range";
|
||||
import { ObjectID } from "@Model/Renderer";
|
||||
import { ColorInput } from "@Component/ColorInput/ColorInput";
|
||||
import { TogglesInput } from "@Component/TogglesInput/TogglesInput";
|
||||
import "./RangeDetails.scss";
|
||||
|
||||
@useStatusWithEvent("rangeAttrChange", "focusObjectChange")
|
||||
@ -12,6 +14,8 @@ class RangeDetails extends Component<IMixinStatusProps> {
|
||||
|
||||
public readonly AttrI18nKey: AllI18nKeys[] = [
|
||||
"Common.Attr.Key.Display.Name",
|
||||
"Common.Attr.Key.Display",
|
||||
"Common.Attr.Key.Update",
|
||||
"Common.Attr.Key.Color",
|
||||
"Common.Attr.Key.Position.X",
|
||||
"Common.Attr.Key.Position.Y",
|
||||
@ -21,14 +25,6 @@ class RangeDetails extends Component<IMixinStatusProps> {
|
||||
"Common.Attr.Key.Radius.Z"
|
||||
]
|
||||
|
||||
private renderErrorFrom(error: AllI18nKeys) {
|
||||
return <>
|
||||
{this.AttrI18nKey.map((key, index) => {
|
||||
return <AttrInput key={index} keyI18n={key} disable disableI18n={error}/>
|
||||
})}
|
||||
</>
|
||||
}
|
||||
|
||||
private renderAttrInput(
|
||||
id: ObjectID, key: number, val: string | number | undefined,
|
||||
change: (val: string, status: Status) => any,
|
||||
@ -55,40 +51,54 @@ class RangeDetails extends Component<IMixinStatusProps> {
|
||||
}
|
||||
|
||||
private renderFrom(range: Range) {
|
||||
// console.log(range);
|
||||
|
||||
let keyIndex = 0;
|
||||
|
||||
return <>
|
||||
{this.renderAttrInput(range.id, 0, range.displayName, (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.displayName, (val, status) => {
|
||||
status.changeRangeAttrib(range.id, "displayName", val);
|
||||
})}
|
||||
|
||||
<ColorInput keyI18n="Common.Attr.Key.Color" value={range.color} normal valueChange={(color) => {
|
||||
<TogglesInput keyI18n={this.AttrI18nKey[keyIndex ++]} value={range.display} valueChange={(val) => {
|
||||
if (this.props.status) {
|
||||
this.props.status.changeRangeAttrib(range.id, "display", val);
|
||||
}
|
||||
}}/>
|
||||
|
||||
<TogglesInput keyI18n={this.AttrI18nKey[keyIndex ++]} value={range.update} valueChange={(val) => {
|
||||
if (this.props.status) {
|
||||
this.props.status.changeRangeAttrib(range.id, "update", val);
|
||||
}
|
||||
}}/>
|
||||
|
||||
<ColorInput keyI18n={this.AttrI18nKey[keyIndex ++]} value={range.color} normal valueChange={(color) => {
|
||||
if (this.props.status) {
|
||||
this.props.status.changeRangeAttrib(range.id, "color", color);
|
||||
}
|
||||
}}/>
|
||||
|
||||
{this.renderAttrInput(range.id, 2, range.position[0], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.position[0], (val, status) => {
|
||||
range.position[0] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "position", range.position);
|
||||
}, .1)}
|
||||
{this.renderAttrInput(range.id, 3, range.position[1], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.position[1], (val, status) => {
|
||||
range.position[1] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "position", range.position);
|
||||
}, .1)}
|
||||
{this.renderAttrInput(range.id, 4, range.position[2], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.position[2], (val, status) => {
|
||||
range.position[2] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "position", range.position);
|
||||
}, .1)}
|
||||
|
||||
{this.renderAttrInput(range.id, 5, range.radius[0], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.radius[0], (val, status) => {
|
||||
range.radius[0] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "radius", range.radius);
|
||||
}, .1, undefined, 0)}
|
||||
{this.renderAttrInput(range.id, 6, range.radius[1], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.radius[1], (val, status) => {
|
||||
range.radius[1] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "radius", range.radius);
|
||||
}, .1, undefined, 0)}
|
||||
{this.renderAttrInput(range.id, 7, range.radius[2], (val, status) => {
|
||||
{this.renderAttrInput(range.id, keyIndex ++, range.radius[2], (val, status) => {
|
||||
range.radius[2] = (val as any) / 1;
|
||||
status.changeRangeAttrib(range.id, "radius", range.radius);
|
||||
}, .1, undefined, 0)}
|
||||
@ -98,12 +108,12 @@ class RangeDetails extends Component<IMixinStatusProps> {
|
||||
public render(): ReactNode {
|
||||
if (this.props.status) {
|
||||
if (this.props.status.focusObject.size <= 0) {
|
||||
return this.renderErrorFrom("Panel.Info.Range.Details.Attr.Error.Unspecified");
|
||||
return <ErrorMessage i18nKey="Panel.Info.Range.Details.Attr.Error.Unspecified"/>;
|
||||
}
|
||||
if (this.props.status.focusObject.size > 1) {
|
||||
return this.renderErrorFrom("Common.Attr.Key.Error.Multiple");
|
||||
return <ErrorMessage i18nKey="Common.Attr.Key.Error.Multiple"/>;
|
||||
}
|
||||
let id: ObjectID = 0;
|
||||
let id: ObjectID = "";
|
||||
this.props.status.focusObject.forEach((cid => id = cid));
|
||||
|
||||
let range = this.props.status!.model.getObjectById(id);
|
||||
@ -111,10 +121,10 @@ class RangeDetails extends Component<IMixinStatusProps> {
|
||||
if (range instanceof Range) {
|
||||
return this.renderFrom(range);
|
||||
} else {
|
||||
return this.renderErrorFrom("Panel.Info.Range.Details.Attr.Error.Not.Range");
|
||||
return <ErrorMessage i18nKey="Panel.Info.Range.Details.Attr.Error.Not.Range"/>;
|
||||
}
|
||||
}
|
||||
return this.renderErrorFrom("Panel.Info.Range.Details.Attr.Error.Unspecified");
|
||||
return <ErrorMessage i18nKey="Panel.Info.Range.Details.Attr.Error.Unspecified"/>;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user