Compare commits
	
		
			5 Commits
		
	
	
		
			e1a58d2cbe
			...
			aae7e54557
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| aae7e54557 | |||
| ca3a19f5b0 | |||
| e7533fab55 | |||
| 070a9daf42 | |||
| ac88c4d3fd | 
| @ -59,8 +59,16 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | |||||||
|                     active: mouseMod === MouseMod.click, |                     active: mouseMod === MouseMod.click, | ||||||
|                     click: () => this.props.status ? this.props.status.setMouseMod(MouseMod.click) : undefined |                     click: () => this.props.status ? this.props.status.setMouseMod(MouseMod.click) : undefined | ||||||
|                 })} |                 })} | ||||||
|                 {this.getRenderButton({ iconName: "WebAppBuilderFragmentCreate", i18NKey: "Command.Bar.Add.Group.Info" })} |                 {this.getRenderButton({ | ||||||
|                 {this.getRenderButton({ iconName: "CubeShape", i18NKey: "Command.Bar.Add.Range.Info" })} |                     iconName: "WebAppBuilderFragmentCreate", | ||||||
|  |                     i18NKey: "Command.Bar.Add.Group.Info", | ||||||
|  |                     click: () => this.props.status ? this.props.status.newGroup() : undefined | ||||||
|  |                 })} | ||||||
|  |                 {this.getRenderButton({ | ||||||
|  |                     iconName: "CubeShape", | ||||||
|  |                     i18NKey: "Command.Bar.Add.Range.Info", | ||||||
|  |                     click: () => this.props.status ? this.props.status.newRange() : undefined | ||||||
|  |                 })} | ||||||
|                 {this.getRenderButton({ iconName: "StepSharedAdd", i18NKey: "Command.Bar.Add.Behavior.Info" })} |                 {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" })} | ||||||
|                 {this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })} |                 {this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })} | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								source/Component/DetailsList/DetailsList.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								source/Component/DetailsList/DetailsList.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | @import "../Theme/Theme.scss"; | ||||||
|  | 
 | ||||||
|  | div.details-list { | ||||||
|  |     width: 100%; | ||||||
|  | 
 | ||||||
|  |     div.details-list-item { | ||||||
|  |         display: flex; | ||||||
|  |         align-items: stretch; | ||||||
|  |         user-select: none; | ||||||
|  |         cursor: pointer; | ||||||
|  |         min-height: 30px; | ||||||
|  | 
 | ||||||
|  |         div.details-list-value { | ||||||
|  |             padding: 5px 10px; | ||||||
|  |             display: flex; | ||||||
|  |             justify-content: center; | ||||||
|  |             align-items: center; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         div.details-list-checkbox { | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             justify-content: center; | ||||||
|  |             padding: 0 10px; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.light.details-list { | ||||||
|  |      | ||||||
|  |     div.details-list-item:nth-child(2n) { | ||||||
|  |         background-color: rgba($lt-bg-color-lvl5-light, .4); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     div.details-list-item:hover { | ||||||
|  |         background-color: $lt-bg-color-lvl3-light; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.dark.details-list { | ||||||
|  | 
 | ||||||
|  |     div.details-list-item:nth-child(2n) { | ||||||
|  |         background-color: rgba($lt-bg-color-lvl5-dark, .4); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     div.details-list-item:hover { | ||||||
|  |         background-color: $lt-bg-color-lvl3-dark; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								source/Component/DetailsList/DetailsList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								source/Component/DetailsList/DetailsList.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | import { Icon } from "@fluentui/react"; | ||||||
|  | import { Component, ReactNode } from "react"; | ||||||
|  | import { BackgroundLevel, FontLevel, Theme } from "../Theme/Theme"; | ||||||
|  | import "./DetailsList.scss"; | ||||||
|  | 
 | ||||||
|  | type IItems = Record<string, any> & {key: string, select?: boolean}; | ||||||
|  | 
 | ||||||
|  | interface IColumns<D extends IItems, K extends keyof D> { | ||||||
|  |     key: K; | ||||||
|  |     className?: string; | ||||||
|  |     noDefaultStyle?: boolean; | ||||||
|  |     beforeCheckbox?: boolean; | ||||||
|  |     render: (data: D[K]) => ReactNode, | ||||||
|  |     click?: (data: D[K]) => any, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface IDetailsListProps { | ||||||
|  |     items: IItems[]; | ||||||
|  |     columns: IColumns<this["items"][number], keyof this["items"][number]>[]; | ||||||
|  |     hideCheckBox?: boolean; | ||||||
|  |     checkboxClassName?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class DetailsList extends Component<IDetailsListProps> { | ||||||
|  | 
 | ||||||
|  |     private renderValue<D extends IItems, K extends keyof D>(item: IItems, column: IColumns<D, K>) { | ||||||
|  |         const classList: string[] = []; | ||||||
|  |         if (!column.noDefaultStyle) { | ||||||
|  |             classList.push("details-list-value"); | ||||||
|  |         } | ||||||
|  |         if (column.className) { | ||||||
|  |             classList.push(column.className); | ||||||
|  |         } | ||||||
|  |         return <div | ||||||
|  |             className={classList.join(" ")} | ||||||
|  |             key={column.key as any} | ||||||
|  |         > | ||||||
|  |             {column.render(item[column.key as any])} | ||||||
|  |         </div> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         return <Theme | ||||||
|  |             className="details-list" | ||||||
|  |             backgroundLevel={BackgroundLevel.Level4} | ||||||
|  |             fontLevel={FontLevel.normal} | ||||||
|  |         >{ | ||||||
|  |             this.props.items.map((item) => { | ||||||
|  |                 const { checkboxClassName } = this.props; | ||||||
|  |                 return <div className="details-list-item" key={item.key}> | ||||||
|  |                     { | ||||||
|  |                         this.props.columns.map((column) => { | ||||||
|  |                             if (column.beforeCheckbox) { | ||||||
|  |                                 return this.renderValue(item, column); | ||||||
|  |                             } | ||||||
|  |                         }) | ||||||
|  |                     } | ||||||
|  |                     { | ||||||
|  |                         this.props.hideCheckBox ? null : | ||||||
|  |                         <div  | ||||||
|  |                             className={"details-list-checkbox" + (checkboxClassName ? ` ${checkboxClassName}` : "")} | ||||||
|  |                         > | ||||||
|  |                             <Icon iconName="CheckMark"></Icon> | ||||||
|  |                         </div> | ||||||
|  |                     } | ||||||
|  |                     { | ||||||
|  |                         this.props.columns.map((column) => { | ||||||
|  |                             if (!column.beforeCheckbox) { | ||||||
|  |                                 return this.renderValue(item, column); | ||||||
|  |                             } | ||||||
|  |                         }) | ||||||
|  |                     } | ||||||
|  |                 </div> | ||||||
|  |             }) | ||||||
|  |         }</Theme> | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { DetailsList }; | ||||||
| @ -1,6 +1,6 @@ | |||||||
| @import "@fluentui/react/dist/sass/References"; | @import "@fluentui/react/dist/sass/References"; | ||||||
| 
 | 
 | ||||||
| $lt-font-size-normal: $ms-font-size-14; | $lt-font-size-normal: 13px; | ||||||
| $lt-font-size-lvl3: $ms-font-size-16; | $lt-font-size-lvl3: $ms-font-size-16; | ||||||
| $lt-font-size-lvl2: $ms-font-size-18; | $lt-font-size-lvl2: $ms-font-size-18; | ||||||
| $lt-font-size-lvl1: $ms-font-size-24; | $lt-font-size-lvl1: $ms-font-size-24; | ||||||
|  | |||||||
| @ -3,12 +3,29 @@ import { Emitter } from "@Model/Emitter"; | |||||||
| import { Model } from "@Model/Model"; | import { Model } from "@Model/Model"; | ||||||
| import { Archive } from "@Model/Archive"; | import { Archive } from "@Model/Archive"; | ||||||
| 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 { I18N } from "@Component/Localization/Localization"; | ||||||
|  | 
 | ||||||
|  | function randomColor() { | ||||||
|  |     return [ | ||||||
|  |         Math.random() * .8 + .2, | ||||||
|  |         Math.random() * .8 + .2, | ||||||
|  |         Math.random() * .8 + .2, 1 | ||||||
|  |     ] | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| class Status extends Emitter<{ | class Status extends Emitter<{ | ||||||
|     mouseModChange: MouseMod |     mouseModChange: MouseMod | ||||||
| }> { | }> { | ||||||
| 
 | 
 | ||||||
|  |     public setting: Setting = undefined as any; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 对象命名 | ||||||
|  |      */ | ||||||
|  |     public objectNameIndex = 1; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 渲染器 |      * 渲染器 | ||||||
|      */ |      */ | ||||||
| @ -29,6 +46,26 @@ class Status extends Emitter<{ | |||||||
|      */ |      */ | ||||||
|     public mouseMod: MouseMod = MouseMod.Drag; |     public mouseMod: MouseMod = MouseMod.Drag; | ||||||
| 
 | 
 | ||||||
|  |     public newGroup() { | ||||||
|  |         const group = this.model.addGroup(); | ||||||
|  |         group.color = randomColor(); | ||||||
|  |         group.displayName = I18N(this.setting.language, "Object.List.New.Group", { | ||||||
|  |             id: this.objectNameIndex.toString() | ||||||
|  |         }); | ||||||
|  |         this.objectNameIndex ++; | ||||||
|  |         return group; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public newRange() { | ||||||
|  |         const range = this.model.addRange(); | ||||||
|  |         range.color = randomColor(); | ||||||
|  |         range.displayName = I18N(this.setting.language, "Object.List.New.Range", { | ||||||
|  |             id: this.objectNameIndex.toString() | ||||||
|  |         }); | ||||||
|  |         this.objectNameIndex ++; | ||||||
|  |         return range; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public setMouseMod(mod: MouseMod) { |     public setMouseMod(mod: MouseMod) { | ||||||
|         this.mouseMod = mod; |         this.mouseMod = mod; | ||||||
|         if (this.renderer instanceof ClassicRenderer) { |         if (this.renderer instanceof ClassicRenderer) { | ||||||
|  | |||||||
| @ -19,10 +19,15 @@ const EN_US = { | |||||||
|     "Command.Bar.Add.Tag.Info": "Add label object", |     "Command.Bar.Add.Tag.Info": "Add label object", | ||||||
|     "Command.Bar.Camera.Info": "Renderer settings", |     "Command.Bar.Camera.Info": "Renderer settings", | ||||||
|     "Command.Bar.Setting.Info": "Global Settings", |     "Command.Bar.Setting.Info": "Global Settings", | ||||||
|  |     "Object.List.New.Group": "Group object {id}", | ||||||
|  |     "Object.List.New.Range": "Range object {id}", | ||||||
|  |     "Object.List.No.Data": "There are no objects in the model, click the button to create it", | ||||||
|     "Panel.Title.Notfound": "{id}", |     "Panel.Title.Notfound": "{id}", | ||||||
|     "Panel.Info.Notfound": "This panel with id {id} can not found!", |     "Panel.Info.Notfound": "This panel with id {id} can not found!", | ||||||
|     "Panel.Title.Render.View": "Live preview", |     "Panel.Title.Render.View": "Live preview", | ||||||
|     "Panel.Info.Render.View": "Live simulation results 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", | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| export default EN_US; | export default EN_US; | ||||||
| @ -19,9 +19,14 @@ const ZH_CN = { | |||||||
|     "Command.Bar.Add.Tag.Info": "添加标签对象", |     "Command.Bar.Add.Tag.Info": "添加标签对象", | ||||||
|     "Command.Bar.Camera.Info": "渲染器设置", |     "Command.Bar.Camera.Info": "渲染器设置", | ||||||
|     "Command.Bar.Setting.Info": "全局设置", |     "Command.Bar.Setting.Info": "全局设置", | ||||||
|  |     "Object.List.New.Group": "组对象 {id}", | ||||||
|  |     "Object.List.New.Range": "范围对象 {id}", | ||||||
|  |     "Object.List.No.Data": "模型中没有任何对象,点击按钮以创建", | ||||||
|     "Panel.Title.Notfound": "找不到面板: {id}", |     "Panel.Title.Notfound": "找不到面板: {id}", | ||||||
|     "Panel.Info.Notfound": "这个编号为 {id} 的面板无法找到!", |     "Panel.Info.Notfound": "这个编号为 {id} 的面板无法找到!", | ||||||
|     "Panel.Title.Render.View": "实时预览", |     "Panel.Title.Render.View": "实时预览", | ||||||
|     "Panel.Info.Render.View": "实时仿真结果预览", |     "Panel.Info.Render.View": "实时仿真结果预览", | ||||||
|  |     "Panel.Title.Object.List.View": "对象列表", | ||||||
|  |     "Panel.Info.Object.List.View": "编辑查看全部对象属性", | ||||||
| } | } | ||||||
| export default ZH_CN; | export default ZH_CN; | ||||||
| @ -7,6 +7,11 @@ import type { ObjectID } from "./Renderer"; | |||||||
|  */ |  */ | ||||||
| class CtrlObject extends LabelObject { | class CtrlObject extends LabelObject { | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 显示名称 | ||||||
|  |      */ | ||||||
|  |     public displayName: string = ""; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 颜色 |      * 颜色 | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -36,11 +36,12 @@ class SimulatorWeb extends Component { | |||||||
|         this.status = new Status(); |         this.status = new Status(); | ||||||
|         this.status.renderer = new ClassicRenderer({ className: "canvas" }).onLoad(); |         this.status.renderer = new ClassicRenderer({ className: "canvas" }).onLoad(); | ||||||
|         this.status.model.bindRenderer(this.status.renderer); |         this.status.model.bindRenderer(this.status.renderer); | ||||||
|  |         this.status.setting = this.setting; | ||||||
| 
 | 
 | ||||||
|         // 测试代码
 |         // 测试代码
 | ||||||
|         if (true) { |         if (true) { | ||||||
|             let group = this.status.model.addGroup(); |             let group = this.status.newGroup(); | ||||||
|             let range = this.status.model.addRange(); |             let range = this.status.newRange(); | ||||||
|             range.color = [.1, .5, .9]; |             range.color = [.1, .5, .9]; | ||||||
|             group.new(100); |             group.new(100); | ||||||
|             group.color = [.8, .1, .6]; |             group.color = [.8, .1, .6]; | ||||||
| @ -72,7 +73,7 @@ class SimulatorWeb extends Component { | |||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                     items: [{ |                     items: [{ | ||||||
|                         panles: ["Label d"] |                         panles: ["ObjectList"] | ||||||
|                     }, { |                     }, { | ||||||
|                         items: [{panles: ["Label e", "ee"]}, {panles: ["F"]}], |                         items: [{panles: ["Label e", "ee"]}, {panles: ["F"]}], | ||||||
|                         layout: LayoutDirection.Y |                         layout: LayoutDirection.Y | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								source/Panel/ObjectList/ObjectList.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								source/Panel/ObjectList/ObjectList.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | div.object-list-color-value { | ||||||
|  |     height: calc( 100% - 6px); | ||||||
|  |     margin: 3px 0; | ||||||
|  |     margin-left: 3px; | ||||||
|  |     border-radius: 1000px; | ||||||
|  |     width: 3px; | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								source/Panel/ObjectList/ObjectList.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								source/Panel/ObjectList/ObjectList.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | import { Component, ReactNode } from "react"; | ||||||
|  | import { DetailsList } from "@Component/DetailsList/DetailsList"; | ||||||
|  | import { useStatus, IMixinStatusProps } from "@Context/Status"; | ||||||
|  | import { Localization } from "@Component/Localization/Localization"; | ||||||
|  | import "./ObjectList.scss"; | ||||||
|  | 
 | ||||||
|  | @useStatus | ||||||
|  | class ObjectList extends Component<IMixinStatusProps> { | ||||||
|  | 
 | ||||||
|  |     private handelChange = () => { | ||||||
|  |         this.forceUpdate(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public componentDidMount(){ | ||||||
|  |         if (this.props.status) { | ||||||
|  |             this.props.status.model.on("objectChange", this.handelChange); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public componentWillUnmount(){ | ||||||
|  |         if (this.props.status) { | ||||||
|  |             this.props.status.model.off("objectChange", this.handelChange); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private renderList() { | ||||||
|  |         const objList = this.props.status?.model.objectPool ?? []; | ||||||
|  | 
 | ||||||
|  |         if (objList.length <= 0) { | ||||||
|  |             return <Localization i18nKey="Object.List.No.Data" style={{ | ||||||
|  |                 padding: "10px", | ||||||
|  |                 display: "block" | ||||||
|  |             }}/> | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return <DetailsList | ||||||
|  |             items={objList.concat([]).map((object => { | ||||||
|  |                 return { | ||||||
|  |                     key: object.id.toString(), | ||||||
|  |                     name: object.displayName, | ||||||
|  |                     color: object.color, | ||||||
|  |                     display: object.display, | ||||||
|  |                     update: object.update | ||||||
|  |                 } | ||||||
|  |             }))} | ||||||
|  |             columns={[ | ||||||
|  |                 { | ||||||
|  |                     key: "color", | ||||||
|  |                     noDefaultStyle: true, | ||||||
|  |                     beforeCheckbox: true, | ||||||
|  |                     render: (color) => <div | ||||||
|  |                         className="object-list-color-value" | ||||||
|  |                         style={{ | ||||||
|  |                             background: `rgb(${ | ||||||
|  |                                 Math.floor(color[0] * 255) | ||||||
|  |                             }, ${ | ||||||
|  |                                 Math.floor(color[1] * 255) | ||||||
|  |                             }, ${ | ||||||
|  |                                 Math.floor(color[2] * 255) | ||||||
|  |                             })` | ||||||
|  |                         }} | ||||||
|  |                     /> | ||||||
|  |                 }, { | ||||||
|  |                     key: "name", | ||||||
|  |                     render: (name) => <span>{name}</span> | ||||||
|  |                 } | ||||||
|  |             ]} | ||||||
|  |         /> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         return this.renderList(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { ObjectList }; | ||||||
| @ -2,6 +2,7 @@ import { ReactNode, Component, FunctionComponent } from "react"; | |||||||
| import { Theme } from "@Component/Theme/Theme"; | 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"; | ||||||
| 
 | 
 | ||||||
| interface IPanelInfo { | interface IPanelInfo { | ||||||
| 	nameKey: string; | 	nameKey: string; | ||||||
| @ -15,6 +16,7 @@ interface IPanelInfo { | |||||||
| 
 | 
 | ||||||
| type PanelId = "" | type PanelId = "" | ||||||
| | "RenderView" // 主渲染器
 | | "RenderView" // 主渲染器
 | ||||||
|  | | "ObjectList" // 对象列表
 | ||||||
| ; | ; | ||||||
| 
 | 
 | ||||||
| const PanelInfoMap = new Map<PanelId, IPanelInfo>(); | const PanelInfoMap = new Map<PanelId, IPanelInfo>(); | ||||||
| @ -22,6 +24,10 @@ PanelInfoMap.set("RenderView", { | |||||||
| 	nameKey: "Panel.Title.Render.View", introKay: "Panel.Info.Render.View", | 	nameKey: "Panel.Title.Render.View", introKay: "Panel.Info.Render.View", | ||||||
|     class: RenderView, hidePadding: true, hideScrollBar: true, isDeepDark: true |     class: RenderView, hidePadding: true, hideScrollBar: true, isDeepDark: true | ||||||
| }); | }); | ||||||
|  | PanelInfoMap.set("ObjectList", { | ||||||
|  |     nameKey: "Panel.Title.Object.List.View", introKay: "Panel.Info.Object.List.View", | ||||||
|  |     class: ObjectList, hidePadding: true | ||||||
|  | }) | ||||||
| 
 | 
 | ||||||
| function getPanelById(panelId: PanelId): ReactNode { | function getPanelById(panelId: PanelId): ReactNode { | ||||||
| 	switch (panelId) { | 	switch (panelId) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user