Add object list command bar & checkbox handel func #14

Merged
MrKBear merged 2 commits from dev-mrkbear into master 2022-03-05 21:12:06 +08:00
12 changed files with 293 additions and 29 deletions

View File

@ -6,6 +6,7 @@ div.app-container {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: stretch;
overflow: hidden; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
@ -98,13 +99,24 @@ div.app-container {
padding: 10px; padding: 10px;
} }
div.app-panel-root {
width: 100%;
height: calc( 100% - 32px );
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: stretch;
flex-direction: column;
border: .8px solid rgba($color: #000000, $alpha: 0);
}
div.app-panel { div.app-panel {
width: 100%; width: 100%;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
overflow: scroll; overflow: scroll;
-ms-overflow-style: none; -ms-overflow-style: none;
border: .8px solid rgba($color: #000000, $alpha: 0); flex-shrink: 1;
} }
div.app-panel.hide-scrollbar::-webkit-scrollbar { div.app-panel.hide-scrollbar::-webkit-scrollbar {
@ -128,7 +140,7 @@ div.app-container {
background-color: rgba($color: #000000, $alpha: 0); background-color: rgba($color: #000000, $alpha: 0);
} }
div.app-panel.active { div.app-panel-root.active {
border: .8px solid blue !important; border: .8px solid blue !important;
} }
} }

View File

@ -68,7 +68,7 @@ class Container extends Component<IContainerProps> {
delay={2} delay={2}
key={panelId} key={panelId}
> >
<div <div
className={classList.join(" ")} className={classList.join(" ")}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
@ -90,16 +90,32 @@ class Container extends Component<IContainerProps> {
}</div> : null }</div> : null
} }
<div <div
onClick={() => this.props.onFocusTab ? this.props.onFocusTab(showPanelId) : undefined}
className={[ className={[
"app-panel", "app-panel-root",
hasActivePanel ? "active" : "", hasActivePanel ? "active" : ""
showPanelInfo?.hidePadding ? "" : "has-padding",
showPanelInfo?.hideScrollBar ? "hide-scrollbar" : ""
].filter(x => !!x).join(" ")} ].filter(x => !!x).join(" ")}
draggable={false} onClick={() => this.props.onFocusTab ? this.props.onFocusTab(showPanelId) : undefined}
> >
{getPanelById(showPanelId as any)} {/* 渲染 Command Bar */}
{(() => {
let info = getPanelInfoById(showPanelId as any);
if (info && info.header) {
const Header = info.header;
return <Header></Header>
}
})()}
{/* 渲染 Panel 内容 */}
<div
className={[
"app-panel",
showPanelInfo?.hidePadding ? "" : "has-padding",
showPanelInfo?.hideScrollBar ? "hide-scrollbar" : ""
].filter(x => !!x).join(" ")}
draggable={false}
>
{getPanelById(showPanelId as any)}
</div>
</div> </div>
</> </>
} }

View File

@ -19,31 +19,58 @@ div.details-list {
div.details-list-checkbox { div.details-list-checkbox {
display: flex; display: flex;
width: 30px;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0 10px; opacity: 0;
}
}
div.details-list-item.active,
div.details-list-item:hover {
div.details-list-checkbox {
opacity: 1;
} }
} }
} }
div.light.details-list { div.light.details-list {
div.details-list-item:nth-child(2n) { div.details-list-item:nth-child(2n-1) {
background-color: rgba($lt-bg-color-lvl5-light, .4); background-color: rgba($lt-bg-color-lvl5-light, .4);
} }
div.details-list-item:hover { div.details-list-item:hover {
background-color: $lt-bg-color-lvl3-light; background-color: $lt-bg-color-lvl3-light;
} }
div.details-list-item.active {
background-color: $lt-bg-color-lvl2-light;
color: rgba(0, 0, 0, 0.95);
}
// div.details-list-checkbox:hover {
// background-color: rgba($lt-bg-color-lvl1-light, .4);
// }
} }
div.dark.details-list { div.dark.details-list {
div.details-list-item:nth-child(2n) { div.details-list-item:nth-child(2n-1) {
background-color: rgba($lt-bg-color-lvl5-dark, .4); background-color: rgba($lt-bg-color-lvl5-dark, .8);
} }
div.details-list-item:hover { div.details-list-item:hover {
background-color: $lt-bg-color-lvl3-dark; background-color: $lt-bg-color-lvl3-dark;
} }
div.details-list-item.active {
background-color: $lt-bg-color-lvl2-dark;
color: rgba(255, 255, 255, 0.85);
}
// div.details-list-checkbox:hover {
// background-color: rgba($lt-bg-color-lvl1-dark, .8);
// }
} }

View File

@ -16,9 +16,13 @@ interface IColumns<D extends IItems, K extends keyof D> {
interface IDetailsListProps { interface IDetailsListProps {
items: IItems[]; items: IItems[];
className?: string;
columns: IColumns<this["items"][number], keyof this["items"][number]>[]; columns: IColumns<this["items"][number], keyof this["items"][number]>[];
hideCheckBox?: boolean; hideCheckBox?: boolean;
checkboxClassName?: string; checkboxClassName?: string;
click?: () => void;
clickLine?: (item: IItems) => any;
checkBox?: (data: IItems) => any;
} }
class DetailsList extends Component<IDetailsListProps> { class DetailsList extends Component<IDetailsListProps> {
@ -41,13 +45,27 @@ class DetailsList extends Component<IDetailsListProps> {
public render(): ReactNode { public render(): ReactNode {
return <Theme return <Theme
className="details-list" className={"details-list" + (this.props.className ? ` ${this.props.className}` : "")}
onClick={this.props.click}
backgroundLevel={BackgroundLevel.Level4} backgroundLevel={BackgroundLevel.Level4}
fontLevel={FontLevel.normal} fontLevel={FontLevel.normal}
>{ >{
this.props.items.map((item) => { this.props.items.map((item) => {
const { checkboxClassName } = this.props; const { checkboxClassName } = this.props;
return <div className="details-list-item" key={item.key}> const classList: string[] = ["details-list-item"];
if (item.select) {
classList.push("active");
}
return <div
className={classList.join(" ")}
key={item.key}
onClick={(e) => {
if (this.props.clickLine) {
e.stopPropagation();
this.props.clickLine(item);
}
}}
>
{ {
this.props.columns.map((column) => { this.props.columns.map((column) => {
if (column.beforeCheckbox) { if (column.beforeCheckbox) {
@ -59,6 +77,12 @@ class DetailsList extends Component<IDetailsListProps> {
this.props.hideCheckBox ? null : this.props.hideCheckBox ? null :
<div <div
className={"details-list-checkbox" + (checkboxClassName ? ` ${checkboxClassName}` : "")} className={"details-list-checkbox" + (checkboxClassName ? ` ${checkboxClassName}` : "")}
onClick={(e) => {
if (this.props.checkBox) {
e.stopPropagation();
this.props.checkBox(item);
}
}}
> >
<Icon iconName="CheckMark"></Icon> <Icon iconName="CheckMark"></Icon>
</div> </div>
@ -76,4 +100,4 @@ class DetailsList extends Component<IDetailsListProps> {
} }
} }
export { DetailsList }; export { DetailsList, IItems };

View File

@ -1,7 +1,8 @@
import { createContext, Component, FunctionComponent } from "react"; import { createContext, Component, FunctionComponent } from "react";
import { Emitter } from "@Model/Emitter"; import { Emitter } from "@Model/Emitter";
import { Model } from "@Model/Model"; import { Model, ObjectID } from "@Model/Model";
import { Archive } from "@Model/Archive"; import { Archive } from "@Model/Archive";
import { CtrlObject } from "@Model/CtrlObject";
import { AbstractRenderer } from "@Model/Renderer"; import { AbstractRenderer } from "@Model/Renderer";
import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer"; import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer";
import { Setting } from "./Setting"; import { Setting } from "./Setting";
@ -16,7 +17,8 @@ function randomColor() {
} }
class Status extends Emitter<{ class Status extends Emitter<{
mouseModChange: MouseMod mouseModChange: MouseMod,
focusObjectChange: Set<ObjectID>
}> { }> {
public setting: Setting = undefined as any; public setting: Setting = undefined as any;
@ -41,6 +43,19 @@ class Status extends Emitter<{
*/ */
public model: Model = new Model(); public model: Model = new Model();
/**
*
*/
public focusObject: Set<ObjectID> = new Set();
/**
*
*/
public setFocusObject(focusObject: Set<ObjectID>) {
this.focusObject = focusObject;
this.emit("focusObjectChange", this.focusObject);
}
/** /**
* *
*/ */

View File

@ -37,6 +37,14 @@ class Model extends Emitter<ModelEvent> {
*/ */
public objectPool: CtrlObject[] = []; public objectPool: CtrlObject[] = [];
public getObjectById(id: ObjectID): CtrlObject | undefined {
for (let i = 0; i < this.objectPool.length; i++) {
if (this.objectPool[i].id === id) {
return this.objectPool[i];
}
}
}
/** /**
* *
*/ */
@ -163,9 +171,6 @@ class Model extends Emitter<ModelEvent> {
*/ */
public update(t: number) { public update(t: number) {
// 清除全部渲染状态
this.renderer.clean();
// 第一轮更新 // 第一轮更新
for (let i = 0; i < this.objectPool.length; i++) { for (let i = 0; i < this.objectPool.length; i++) {
let object = this.objectPool[i]; let object = this.objectPool[i];
@ -190,6 +195,16 @@ class Model extends Emitter<ModelEvent> {
} }
} }
this.draw();
this.emit("loop", t);
}
public draw() {
// 清除全部渲染状态
this.renderer.clean();
// 渲染 // 渲染
for (let i = 0; i < this.objectPool.length; i++) { for (let i = 0; i < this.objectPool.length; i++) {
let object = this.objectPool[i]; let object = this.objectPool[i];
@ -206,8 +221,6 @@ class Model extends Emitter<ModelEvent> {
} as any); } as any);
} }
} }
this.emit("loop", t);
} }
} }

View File

@ -73,7 +73,7 @@ class SimulatorWeb extends Component {
}, },
{ {
items: [{ items: [{
panles: ["ObjectList"] panles: ["ObjectList", "Test tab"]
}, { }, {
items: [{panles: ["Label e", "ee"]}, {panles: ["F"]}], items: [{panles: ["Label e", "ee"]}, {panles: ["F"]}],
layout: LayoutDirection.Y layout: LayoutDirection.Y

View File

@ -0,0 +1,78 @@
import { BackgroundLevel, FontLevel, Theme } from "@Component/Theme/Theme";
import { useStatus, IMixinStatusProps } from "../../Context/Status";
import { Icon } from "@fluentui/react";
import { Component, ReactNode } from "react";
import { ObjectID } from "@Model/Renderer";
import "./ObjectList.scss";
@useStatus
class ObjectCommand extends Component<IMixinStatusProps> {
public render(): ReactNode {
return <Theme
className="object-list-command-bar"
backgroundLevel={BackgroundLevel.Level4}
fontLevel={FontLevel.normal}
>
<div
className="command-item"
onClick={() => {
if (this.props.status) {
let allObjSet = new Set<ObjectID>();
this.props.status.model.objectPool.forEach((obj) => {
allObjSet.add(obj.id.toString());
})
this.props.status.setFocusObject(allObjSet);
}
}}
>
<Icon iconName="CheckMark"></Icon>
</div>
<div
className="command-item"
onClick={() => {
if (this.props.status) {
this.props.status.setFocusObject(new Set<ObjectID>());
}
}}
>
<Icon iconName="CalculatorMultiply"></Icon>
</div>
<div
className="command-item"
onClick={() => {
this.props.status ? this.props.status.newGroup() : undefined;
this.props.status ? this.props.status.model.draw() : undefined;
}}
>
<Icon iconName="WebAppBuilderFragmentCreate"></Icon>
</div>
<div
className="command-item"
onClick={() => {
this.props.status ? this.props.status.newRange() : undefined;
this.props.status ? this.props.status.model.draw() : undefined;
}}
>
<Icon iconName="CubeShape"></Icon>
</div>
<div
className="command-item"
onClick={() => {
if (this.props.status) {
let deleteId: ObjectID[] = [];
this.props.status.focusObject.forEach((obj) => {
deleteId.push(obj);
})
this.props.status.model.deleteObject(deleteId);
this.props.status.setFocusObject(new Set<ObjectID>());
this.props.status.model.draw();
}
}}
>
<Icon iconName="Delete"></Icon>
</div>
</Theme>
}
}
export { ObjectCommand };

View File

@ -1,7 +1,44 @@
@import "../../Component/Theme/Theme.scss";
div.object-list-color-value { div.object-list-color-value {
height: calc( 100% - 6px); height: calc( 100% - 6px);
margin: 3px 0; margin: 3px 0;
margin-left: 3px; margin-left: 3px;
border-radius: 1000px; border-radius: 1000px;
width: 3px; width: 3px;
}
div.object-list {
min-height: 100%;
}
div.object-list-command-bar {
width: 100%;
height: 30px;
flex-shrink: 0;
display: flex;
div.command-item{
width: 30px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
cursor: pointer;
}
}
div.dark.object-list-command-bar {
div.command-item:hover {
background-color: $lt-bg-color-lvl3-dark;
}
}
div.light.object-list-command-bar {
div.command-item:hover {
background-color: $lt-bg-color-lvl3-light;
}
} }

View File

@ -1,11 +1,14 @@
import { Component, ReactNode } from "react"; import { Component, ReactNode } from "react";
import { DetailsList } from "@Component/DetailsList/DetailsList"; import { DetailsList } from "@Component/DetailsList/DetailsList";
import { useStatus, IMixinStatusProps } from "@Context/Status"; import { useStatus, IMixinStatusProps } from "@Context/Status";
import { useSetting, IMixinSettingProps } from "@Context/Setting";
import { Localization } from "@Component/Localization/Localization"; import { Localization } from "@Component/Localization/Localization";
import { ObjectID } from "@Model/Renderer";
import "./ObjectList.scss"; import "./ObjectList.scss";
@useSetting
@useStatus @useStatus
class ObjectList extends Component<IMixinStatusProps> { class ObjectList extends Component<IMixinStatusProps & IMixinSettingProps> {
private handelChange = () => { private handelChange = () => {
this.forceUpdate(); this.forceUpdate();
@ -14,12 +17,14 @@ class ObjectList extends Component<IMixinStatusProps> {
public componentDidMount(){ public componentDidMount(){
if (this.props.status) { if (this.props.status) {
this.props.status.model.on("objectChange", this.handelChange); this.props.status.model.on("objectChange", this.handelChange);
this.props.status.on("focusObjectChange", this.handelChange);
} }
} }
public componentWillUnmount(){ public componentWillUnmount(){
if (this.props.status) { if (this.props.status) {
this.props.status.model.off("objectChange", this.handelChange); this.props.status.model.off("objectChange", this.handelChange);
this.props.status.off("focusObjectChange", this.handelChange);
} }
} }
@ -34,15 +39,50 @@ class ObjectList extends Component<IMixinStatusProps> {
} }
return <DetailsList return <DetailsList
className="object-list"
items={objList.concat([]).map((object => { items={objList.concat([]).map((object => {
return { return {
key: object.id.toString(), key: object.id.toString(),
name: object.displayName, name: object.displayName,
color: object.color, color: object.color,
display: object.display, display: object.display,
update: object.update update: object.update,
select: this.props.status ?
this.props.status.focusObject.has(object.id.toString()) ||
this.props.status.focusObject.has(object.id) :
false
} }
}))} }))}
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));
}
}}
checkBox={(item) => {
if (this.props.setting) {
this.props.setting.layout.focus("ObjectList");
}
if (this.props.status) {
if (
this.props.status.focusObject.has(item.key.toString()) ||
this.props.status.focusObject.has(item.key)
) {
this.props.status.focusObject.delete(item.key);
this.props.status.focusObject.delete(item.key.toString());
this.props.status.setFocusObject(this.props.status.focusObject);
} else {
this.props.status.setFocusObject(this.props.status.focusObject.add(item.key));
}
}
}}
click={() => {
if (this.props.status) {
this.props.status.setFocusObject(new Set<ObjectID>());
}
}}
columns={[ columns={[
{ {
key: "color", key: "color",

View File

@ -3,11 +3,13 @@ import { Theme } from "@Component/Theme/Theme";
import { Localization } from "@Component/Localization/Localization"; import { Localization } from "@Component/Localization/Localization";
import { RenderView } from "./RenderView/RenderView"; import { RenderView } from "./RenderView/RenderView";
import { ObjectList } from "./ObjectList/ObjectList"; import { ObjectList } from "./ObjectList/ObjectList";
import { ObjectCommand } from "./ObjectList/ObjectCommand";
interface IPanelInfo { interface IPanelInfo {
nameKey: string; nameKey: string;
introKay: string; introKay: string;
class: (new (...p: any) => Component) | FunctionComponent; class: (new (...p: any) => Component) | FunctionComponent;
header?: (new (...p: any) => Component) | FunctionComponent;
hidePadding?: boolean; hidePadding?: boolean;
hideScrollBar?: boolean; hideScrollBar?: boolean;
isDeepDark?: boolean; isDeepDark?: boolean;
@ -26,7 +28,7 @@ PanelInfoMap.set("RenderView", {
}); });
PanelInfoMap.set("ObjectList", { PanelInfoMap.set("ObjectList", {
nameKey: "Panel.Title.Object.List.View", introKay: "Panel.Info.Object.List.View", nameKey: "Panel.Title.Object.List.View", introKay: "Panel.Info.Object.List.View",
class: ObjectList, hidePadding: true class: ObjectList, header: ObjectCommand, hidePadding: true
}) })
function getPanelById(panelId: PanelId): ReactNode { function getPanelById(panelId: PanelId): ReactNode {

View File

@ -28,7 +28,7 @@ class RenderView extends Component<IMixinStatusProps & IMixinSettingProps> {
public componentDidMount() { public componentDidMount() {
let div = this.rootEle.current; let div = this.rootEle.current;
console.log(div, div?.childNodes, this.props.status, this.props.status?.renderer.dom) // console.log(div, div?.childNodes, this.props.status, this.props.status?.renderer.dom)
if (div && (!div.childNodes || div.childNodes.length <= 0) && this.props.status) { if (div && (!div.childNodes || div.childNodes.length <= 0) && this.props.status) {
div.appendChild(this.props.status.renderer.dom); div.appendChild(this.props.status.renderer.dom);
} }