Compare commits
	
		
			3 Commits
		
	
	
		
			04060902e0
			...
			90398b593e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 90398b593e | |||
| 93e1a8d3ef | |||
| 3357960f61 | 
| @ -68,7 +68,13 @@ class CommandBar extends Component<ICommandBarProps & IMixinSettingProps & IMixi | |||||||
|                 {this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })} |                 {this.getRenderButton({ iconName: "Camera", i18NKey: "Command.Bar.Camera.Info" })} | ||||||
|             </div> |             </div> | ||||||
|             <div> |             <div> | ||||||
|                 {this.getRenderButton({ iconName: "Settings", i18NKey: "Command.Bar.Setting.Info" })} |                 {this.getRenderButton({ | ||||||
|  |                     iconName: "Settings", | ||||||
|  |                     i18NKey: "Command.Bar.Setting.Info", | ||||||
|  |                     click: () => { | ||||||
|  |                         this.props.status ? this.props.status.popup.showPopup() : undefined; | ||||||
|  |                     } | ||||||
|  |                 })} | ||||||
|             </div> |             </div> | ||||||
|         </Theme> |         </Theme> | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,8 +2,8 @@ import { Component, ReactNode, DetailedHTMLProps, HTMLAttributes } from "react"; | |||||||
| import { useSetting, IMixinSettingProps, Language } from "@Context/Setting"; | import { useSetting, IMixinSettingProps, Language } from "@Context/Setting"; | ||||||
| import "./Localization.scss"; | import "./Localization.scss"; | ||||||
| 
 | 
 | ||||||
| import EN_US from "../../Localization/EN-US"; | import EN_US from "@Localization/EN-US"; | ||||||
| import ZH_CN from "../../Localization/ZH-CN"; | import ZH_CN from "@Localization/ZH-CN"; | ||||||
| 
 | 
 | ||||||
| const LanguageDataBase = { | const LanguageDataBase = { | ||||||
|     EN_US, ZH_CN |     EN_US, ZH_CN | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| import { AllI18nKeys, I18N } from "@Component/Localization/Localization"; | import { AllI18nKeys, I18N } from "@Component/Localization/Localization"; | ||||||
| import { useStatusWithEvent, IMixinSettingProps, Themes, Language } from "@Context/Setting"; | import { useSettingWithEvent, IMixinSettingProps, Themes, Language } from "@Context/Setting"; | ||||||
| import { FunctionComponent } from "react"; | import { FunctionComponent } from "react"; | ||||||
| import "./Message.scss"; | import "./Message.scss"; | ||||||
| 
 | 
 | ||||||
| @ -38,5 +38,5 @@ const MessageView: FunctionComponent<IMessageProps & IMixinSettingProps> = (prop | |||||||
|     </div> |     </div> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Message = useStatusWithEvent("language", "themes")(MessageView); | const Message = useSettingWithEvent("language", "themes")(MessageView); | ||||||
| export { Message }; | export { Message }; | ||||||
							
								
								
									
										71
									
								
								source/Component/Popup/Popup.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								source/Component/Popup/Popup.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | |||||||
|  | @import "../Theme/Theme.scss"; | ||||||
|  | 
 | ||||||
|  | @keyframes show-scale{ | ||||||
|  |     from { | ||||||
|  |         transform: scale3d(1.15, 1.15, 1); | ||||||
|  |     } | ||||||
|  |     to { | ||||||
|  |         transform: scale3d(1, 1, 1); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @keyframes show-fade{ | ||||||
|  |     from { | ||||||
|  |         opacity: 0; | ||||||
|  |     } | ||||||
|  |     to { | ||||||
|  |         opacity: 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-mask.show-fade { | ||||||
|  |     animation: show-fade .1s cubic-bezier(0, 0, 1, 1) both; | ||||||
|  |     opacity: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer.show-scale { | ||||||
|  |     animation: show-scale .3s cubic-bezier(.1, .9, .2, 1) both, | ||||||
|  |     show-fade .1s cubic-bezier(0, 0, 1, 1) both; | ||||||
|  |     transform: scale3d(1, 1, 1); | ||||||
|  |     opacity: 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-mask { | ||||||
|  |     position: absolute; | ||||||
|  |     cursor: pointer; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 100%; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer { | ||||||
|  |     position: absolute; | ||||||
|  |     border-radius: 3px; | ||||||
|  |     overflow: hidden; | ||||||
|  | 
 | ||||||
|  |     div.popup-layer-header { | ||||||
|  |         min-height: 32px; | ||||||
|  |         max-height: 32px; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer.dark { | ||||||
|  | 
 | ||||||
|  |     div.popup-layer-header { | ||||||
|  |         background-color: $lt-bg-color-lvl3-dark; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer.light { | ||||||
|  | 
 | ||||||
|  |     div.popup-layer-header { | ||||||
|  |         background-color: $lt-bg-color-lvl3-light; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.dark.popup-mask { | ||||||
|  |     background-color: rgba(0, 0, 0, 0.55); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.light.popup-mask { | ||||||
|  |     background-color: rgba(0, 0, 0, 0.15); | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								source/Component/Popup/Popup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								source/Component/Popup/Popup.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | import { Component, ReactNode } from "react"; | ||||||
|  | import { IMixinStatusProps, useStatusWithEvent } from "@Context/Status"; | ||||||
|  | import { BackgroundLevel, Theme } from "@Component/Theme/Theme"; | ||||||
|  | import { Popup as PopupModel } from "@Context/Popups"; | ||||||
|  | import "./Popup.scss"; | ||||||
|  | 
 | ||||||
|  | interface IPopupProps {} | ||||||
|  | 
 | ||||||
|  | @useStatusWithEvent("popupChange") | ||||||
|  | class Popup extends Component<IPopupProps & IMixinStatusProps> { | ||||||
|  | 
 | ||||||
|  |     public renderMask(index?: number, click?: () => void, key?: string): ReactNode { | ||||||
|  |         const classList: string[] = ["popup-mask", "show-fade"]; | ||||||
|  |         return <Theme | ||||||
|  |             key={key} | ||||||
|  |             onClick={click} | ||||||
|  |             className={classList.join(" ")} | ||||||
|  |             style={{ | ||||||
|  |                 zIndex: index, | ||||||
|  |             }} | ||||||
|  |         /> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public renderRootMask(): ReactNode { | ||||||
|  |         if (this.props.status) { | ||||||
|  |             const needMask = this.props.status.popup.popups.some(popup => popup.needMask); | ||||||
|  |             if (!needMask) return null; | ||||||
|  |             return this.renderMask(this.props.status.popup.zIndex, | ||||||
|  |                 () => { | ||||||
|  |                     this.props.status?.popup.popups.forEach( | ||||||
|  |                         popup => popup.onClose() | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |             ); | ||||||
|  |         } else { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public renderMaskList(): ReactNode { | ||||||
|  |         if (this.props.status) { | ||||||
|  |             return this.props.status.popup.popups | ||||||
|  |             .filter((popup) => { | ||||||
|  |                 return popup.needMask && popup.maskForSelf; | ||||||
|  |             }) | ||||||
|  |             .filter((_, index) => { | ||||||
|  |                 if (index === 0) return false; | ||||||
|  |                 return true; | ||||||
|  |             }) | ||||||
|  |             .map((popup) => { | ||||||
|  |                 return this.renderMask(popup.zIndex() - 1, | ||||||
|  |                     () => { | ||||||
|  |                         popup.onClose(); | ||||||
|  |                     }, popup.id | ||||||
|  |                 ); | ||||||
|  |             }) | ||||||
|  |         } else { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public renderLayer(popup: PopupModel) { | ||||||
|  |         const pageWidth = document.documentElement.clientWidth; | ||||||
|  |         const pageHeight = document.documentElement.clientHeight; | ||||||
|  |         const top = (pageHeight - popup.height) / 2; | ||||||
|  |         const left = (pageWidth - popup.width) / 2; | ||||||
|  | 
 | ||||||
|  |         return <Theme | ||||||
|  |             style={{ | ||||||
|  |                 width: popup.width, | ||||||
|  |                 height: popup.height, | ||||||
|  |                 zIndex: popup.zIndex(), | ||||||
|  |                 top: top, | ||||||
|  |                 left: left | ||||||
|  |             }} | ||||||
|  |             key={popup.id} | ||||||
|  |             backgroundLevel={BackgroundLevel.Level4} | ||||||
|  |             className="popup-layer show-scale" | ||||||
|  |         > | ||||||
|  |             <div className="popup-layer-header"> | ||||||
|  |                  | ||||||
|  |             </div> | ||||||
|  |         </Theme> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         return <> | ||||||
|  |             {this.renderRootMask()} | ||||||
|  |             {this.renderMaskList()} | ||||||
|  |             {this.props.status?.popup.popups.map((popup) => { | ||||||
|  |                 return this.renderLayer(popup); | ||||||
|  |             })} | ||||||
|  |         </>; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { Popup }; | ||||||
| @ -8,11 +8,33 @@ type IPopupConstructor = new (controller: PopupController, id: string) => Popup; | |||||||
|  */ |  */ | ||||||
| class Popup { | class Popup { | ||||||
| 
 | 
 | ||||||
|  |     public zIndex() { | ||||||
|  |         return this.index * 2 + this.controller.zIndex; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public width: number = 300; | ||||||
|  | 
 | ||||||
|  |     public height: number = 200; | ||||||
|  | 
 | ||||||
|  |     public top: number = 0; | ||||||
|  | 
 | ||||||
|  |     public left: number = 0; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 是否关闭 |      * 是否关闭 | ||||||
|      */ |      */ | ||||||
|     public isClose: boolean = false; |     public isClose: boolean = false; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 需要蒙版 | ||||||
|  |      */ | ||||||
|  |     public needMask: boolean = true; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 单独遮挡下层的蒙版 | ||||||
|  |      */ | ||||||
|  |     public maskForSelf: boolean = false; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 唯一标识符 |      * 唯一标识符 | ||||||
|      */ |      */ | ||||||
| @ -36,18 +58,29 @@ class Popup { | |||||||
|     /** |     /** | ||||||
|      * 渲染函数 |      * 渲染函数 | ||||||
|      */ |      */ | ||||||
|     public rendererFunction: undefined | ((p: Popup) => ReactNode); |     public onRender(p: Popup): ReactNode { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 关闭回调 | ||||||
|  |      */ | ||||||
|  |     public onClose(): void { | ||||||
|  |         this.close(); | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 渲染节点 |      * 渲染节点 | ||||||
|      */ |      */ | ||||||
|     public render(): ReactNode { |     public render(): ReactNode { | ||||||
|         if (this.rendererFunction) { |         this.reactNode = this.onRender(this); | ||||||
|             this.reactNode = this.rendererFunction(this); |  | ||||||
|         } |  | ||||||
|         return this.reactNode; |         return this.reactNode; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  |     public close() { | ||||||
|  |         return this.controller.closePopup(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public constructor(controller: PopupController, id: string) { |     public constructor(controller: PopupController, id: string) { | ||||||
|         this.controller = controller; |         this.controller = controller; | ||||||
|         this.id = id; |         this.id = id; | ||||||
| @ -68,6 +101,11 @@ class PopupController extends Emitter<IPopupControllerEvent> { | |||||||
|      */ |      */ | ||||||
|     private idIndex = 0; |     private idIndex = 0; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 最小弹窗 Index | ||||||
|  |      */ | ||||||
|  |     public zIndex = 100; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 弹窗列表 |      * 弹窗列表 | ||||||
|      */ |      */ | ||||||
| @ -82,6 +120,7 @@ class PopupController extends Emitter<IPopupControllerEvent> { | |||||||
|             popup.index = (index + 1); |             popup.index = (index + 1); | ||||||
|             return popup; |             return popup; | ||||||
|         }); |         }); | ||||||
|  |         this.emit("popupChange"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -90,7 +129,7 @@ class PopupController extends Emitter<IPopupControllerEvent> { | |||||||
|     public showPopup<P extends IPopupConstructor>(popup?: P): Popup { |     public showPopup<P extends IPopupConstructor>(popup?: P): Popup { | ||||||
|         let newPopup = new (popup ?? Popup)(this, `P-${this.idIndex ++}`); |         let newPopup = new (popup ?? Popup)(this, `P-${this.idIndex ++}`); | ||||||
|         this.popups.push(newPopup); |         this.popups.push(newPopup); | ||||||
|         this.emit("popupChange"); |         this.sortPopup(); | ||||||
|         return newPopup; |         return newPopup; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -110,6 +149,7 @@ class PopupController extends Emitter<IPopupControllerEvent> { | |||||||
|                 let isDelete = currentPopup.id === id; |                 let isDelete = currentPopup.id === id; | ||||||
|                 if (isDelete) { |                 if (isDelete) { | ||||||
|                     closePopup = currentPopup; |                     closePopup = currentPopup; | ||||||
|  |                     currentPopup.isClose = true; | ||||||
|                     return false; |                     return false; | ||||||
|                 } else { |                 } else { | ||||||
|                     return true; |                     return true; | ||||||
| @ -117,6 +157,7 @@ class PopupController extends Emitter<IPopupControllerEvent> { | |||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
|         if (closePopup) { |         if (closePopup) { | ||||||
|  |             this.sortPopup(); | ||||||
|             this.emit("popupChange"); |             this.emit("popupChange"); | ||||||
|         } |         } | ||||||
|         return closePopup; |         return closePopup; | ||||||
|  | |||||||
| @ -14,7 +14,7 @@ enum Themes { | |||||||
| type Language = "ZH_CN" | "EN_US"; | type Language = "ZH_CN" | "EN_US"; | ||||||
| 
 | 
 | ||||||
| interface ISettingEvents extends Setting { | interface ISettingEvents extends Setting { | ||||||
|     change: keyof Setting; |     attrChange: keyof Setting; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Setting extends Emitter<ISettingEvents> { | class Setting extends Emitter<ISettingEvents> { | ||||||
| @ -39,7 +39,7 @@ class Setting extends Emitter<ISettingEvents> { | |||||||
|      */ |      */ | ||||||
|     public setProps<P extends keyof Setting>(key: P, value: Setting[P]) { |     public setProps<P extends keyof Setting>(key: P, value: Setting[P]) { | ||||||
|         this[key] = value as any; |         this[key] = value as any; | ||||||
|         this.emit("change", key); |         this.emit("attrChange", key); | ||||||
|         this.emit(key as any, value as any); |         this.emit(key as any, value as any); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -59,9 +59,9 @@ const SettingConsumer = SettingContext.Consumer; | |||||||
|  */ |  */ | ||||||
| const useSetting = superConnect<Setting>(SettingConsumer, "setting"); | const useSetting = superConnect<Setting>(SettingConsumer, "setting"); | ||||||
| 
 | 
 | ||||||
| const useStatusWithEvent = superConnectWithEvent<Setting, ISettingEvents>(SettingConsumer, "setting"); | const useSettingWithEvent = superConnectWithEvent<Setting, ISettingEvents>(SettingConsumer, "setting"); | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|     Themes, Setting, SettingContext, useSetting, Language, useStatusWithEvent, |     Themes, Setting, SettingContext, useSetting, Language, useSettingWithEvent, | ||||||
|     IMixinSettingProps, SettingProvider, SettingConsumer |     IMixinSettingProps, SettingProvider, SettingConsumer | ||||||
| }; | }; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { createContext, Component, FunctionComponent, ReactNode } from "react"; | import { createContext } from "react"; | ||||||
| import { Emitter } from "@Model/Emitter"; | import { Emitter } from "@Model/Emitter"; | ||||||
| import { Model, ObjectID } from "@Model/Model"; | import { Model, ObjectID } from "@Model/Model"; | ||||||
| import { Label } from "@Model/Label"; | import { Label } from "@Model/Label"; | ||||||
| @ -10,6 +10,7 @@ import { ClassicRenderer, MouseMod } from "@GLRender/ClassicRenderer"; | |||||||
| import { Setting } from "./Setting"; | import { Setting } from "./Setting"; | ||||||
| import { I18N } from "@Component/Localization/Localization"; | import { I18N } from "@Component/Localization/Localization"; | ||||||
| import { superConnectWithEvent, superConnect } from "./Context"; | import { superConnectWithEvent, superConnect } from "./Context"; | ||||||
|  | import { PopupController } from "./Popups"; | ||||||
| 
 | 
 | ||||||
| function randomColor(unNormal: boolean = false) { | function randomColor(unNormal: boolean = false) { | ||||||
|     const color = [ |     const color = [ | ||||||
| @ -39,6 +40,7 @@ interface IStatusEvent { | |||||||
|     labelAttrChange: void; |     labelAttrChange: void; | ||||||
|     groupAttrChange: void; |     groupAttrChange: void; | ||||||
|     individualChange: void; |     individualChange: void; | ||||||
|  |     popupChange: void; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Status extends Emitter<IStatusEvent> { | class Status extends Emitter<IStatusEvent> { | ||||||
| @ -66,6 +68,11 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|      */ |      */ | ||||||
|     public model: Model = new Model(); |     public model: Model = new Model(); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 弹窗 | ||||||
|  |      */ | ||||||
|  |     public popup: PopupController = new PopupController(); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 焦点对象 |      * 焦点对象 | ||||||
|      */ |      */ | ||||||
| @ -96,6 +103,9 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|         this.model.on("objectChange", () => this.emit("objectChange")); |         this.model.on("objectChange", () => this.emit("objectChange")); | ||||||
|         this.model.on("labelChange", () => this.emit("labelChange")); |         this.model.on("labelChange", () => this.emit("labelChange")); | ||||||
| 
 | 
 | ||||||
|  |         // 弹窗事件
 | ||||||
|  |         this.popup.on("popupChange", () => this.emit("popupChange")); | ||||||
|  | 
 | ||||||
|         // 对象变换时执行渲染,更新渲染器数据
 |         // 对象变换时执行渲染,更新渲染器数据
 | ||||||
|         this.on("objectChange", this.delayDraw); |         this.on("objectChange", this.delayDraw); | ||||||
|         this.model.on("individualChange", this.delayDraw); |         this.model.on("individualChange", this.delayDraw); | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ import { initializeIcons } from '@fluentui/font-icons-mdl2'; | |||||||
| import { RootContainer } from "@Component/Container/RootContainer"; | import { RootContainer } from "@Component/Container/RootContainer"; | ||||||
| import { LayoutDirection } from "@Context/Layout"; | import { LayoutDirection } from "@Context/Layout"; | ||||||
| import { CommandBar } from "@Component/CommandBar/CommandBar"; | import { CommandBar } from "@Component/CommandBar/CommandBar"; | ||||||
|  | import { Popup } from "@Component/Popup/Popup"; | ||||||
| import "./SimulatorWeb.scss"; | import "./SimulatorWeb.scss"; | ||||||
| 
 | 
 | ||||||
| initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/"); | initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/"); | ||||||
| @ -101,6 +102,7 @@ class SimulatorWeb extends Component { | |||||||
|             backgroundLevel={BackgroundLevel.Level5} |             backgroundLevel={BackgroundLevel.Level5} | ||||||
|             fontLevel={FontLevel.Level3} |             fontLevel={FontLevel.Level3} | ||||||
|         > |         > | ||||||
|  |             <Popup/> | ||||||
|             <HeaderBar height={45}/> |             <HeaderBar height={45}/> | ||||||
|             <div className="app-root-space" style={{ |             <div className="app-root-space" style={{ | ||||||
|                 height: `calc( 100% - ${45}px)` |                 height: `calc( 100% - ${45}px)` | ||||||
|  | |||||||
| @ -31,6 +31,12 @@ | |||||||
|             ], |             ], | ||||||
|             "@Component/*": [ |             "@Component/*": [ | ||||||
|                 "./source/Component/*" |                 "./source/Component/*" | ||||||
|  |             ], | ||||||
|  |             "@Localization/*": [ | ||||||
|  |                 "./source/Localization/*" | ||||||
|  |             ], | ||||||
|  |             "@Panel/*": [ | ||||||
|  |                 "./source/Panel/*" | ||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user