Compare commits
	
		
			13 Commits
		
	
	
		
			e8935403ea
			...
			7923b2d802
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7923b2d802 | |||
| 8b44ae953e | |||
| 4d40ddf468 | |||
| 90398b593e | |||
| 93e1a8d3ef | |||
| 3357960f61 | |||
| 04060902e0 | |||
| 7570e965cf | |||
| 41a2618b73 | |||
| 7d8a216115 | |||
| 3f4318e193 | |||
| 8d5f2a7dfb | |||
| 30a785eabb | 
| @ -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,7 +2,7 @@ import { Localization } from "@Component/Localization/Localization"; | |||||||
| import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; | import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; | ||||||
| import { Themes } from "@Context/Setting"; | import { Themes } from "@Context/Setting"; | ||||||
| import { DirectionalHint } from "@fluentui/react"; | import { DirectionalHint } from "@fluentui/react"; | ||||||
| import { ILayout, LayoutDirection } from "@Model/Layout"; | import { ILayout, LayoutDirection } from "@Context/Layout"; | ||||||
| import { Component, ReactNode, MouseEvent } from "react"; | import { Component, ReactNode, MouseEvent } from "react"; | ||||||
| import { getPanelById, getPanelInfoById } from "../../Panel/Panel"; | import { getPanelById, getPanelInfoById } from "../../Panel/Panel"; | ||||||
| import { LocalizationTooltipHost } from "../Localization/LocalizationTooltipHost"; | import { LocalizationTooltipHost } from "../Localization/LocalizationTooltipHost"; | ||||||
|  | |||||||
| @ -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 { useSetting, 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 = useSetting(MessageView); | const Message = useSettingWithEvent("language", "themes")(MessageView); | ||||||
| export { Message }; | export { Message }; | ||||||
							
								
								
									
										114
									
								
								source/Component/Popup/Popup.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								source/Component/Popup/Popup.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,114 @@ | |||||||
|  | @import "../Theme/Theme.scss"; | ||||||
|  | 
 | ||||||
|  | $header-height: 32px; | ||||||
|  | 
 | ||||||
|  | @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: $header-height; | ||||||
|  |         max-height: $header-height; | ||||||
|  |         height: $header-height; | ||||||
|  |         display: flex; | ||||||
|  |         width: 100%; | ||||||
|  | 
 | ||||||
|  |         div.header-text { | ||||||
|  |             width: calc( 100% - 32px ); | ||||||
|  |             flex-shrink: 1; | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             user-select: none; | ||||||
|  |              | ||||||
|  |             span { | ||||||
|  |                 padding-left: 8px; | ||||||
|  |                 display: inline-block; | ||||||
|  |                 vertical-align: middle; | ||||||
|  |                 white-space: nowrap; | ||||||
|  |                 text-overflow: ellipsis; | ||||||
|  |                 overflow: hidden; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         div.header-close-icon { | ||||||
|  |             width: $header-height; | ||||||
|  |             height: $header-height; | ||||||
|  |             flex-shrink: 0; | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             justify-content: center; | ||||||
|  |             user-select: none; | ||||||
|  |             cursor: pointer; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer.dark { | ||||||
|  | 
 | ||||||
|  |     div.popup-layer-header { | ||||||
|  |         background-color: $lt-bg-color-lvl3-dark; | ||||||
|  | 
 | ||||||
|  |         div.header-close-icon:hover { | ||||||
|  |             background-color: $lt-bg-color-lvl2-dark; | ||||||
|  |             color: $lt-red; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.popup-layer.light { | ||||||
|  | 
 | ||||||
|  |     div.popup-layer-header { | ||||||
|  |         background-color: $lt-bg-color-lvl3-light; | ||||||
|  | 
 | ||||||
|  |         div.header-close-icon:hover { | ||||||
|  |             background-color: $lt-bg-color-lvl2-light; | ||||||
|  |             color: $lt-red; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.dark.popup-mask { | ||||||
|  |     background-color: rgba(0, 0, 0, 0.55); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | div.light.popup-mask { | ||||||
|  |     background-color: rgba(0, 0, 0, 0.15); | ||||||
|  | } | ||||||
							
								
								
									
										112
									
								
								source/Component/Popup/Popup.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								source/Component/Popup/Popup.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,112 @@ | |||||||
|  | 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 { Icon } from "@fluentui/react"; | ||||||
|  | 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 renderHeader(popup: PopupModel): ReactNode { | ||||||
|  |         return <div className="popup-layer-header"> | ||||||
|  |             <div className="header-text"> | ||||||
|  |                 {popup.onRenderHeader()} | ||||||
|  |             </div> | ||||||
|  |             <div | ||||||
|  |                 className="header-close-icon" | ||||||
|  |                 onClick={() => { | ||||||
|  |                     popup.onClose(); | ||||||
|  |                 }} | ||||||
|  |             > | ||||||
|  |                 <Icon iconName="CalculatorMultiply"/> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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" | ||||||
|  |         > | ||||||
|  |             {this.renderHeader(popup)} | ||||||
|  |         </Theme> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         return <> | ||||||
|  |             {this.renderRootMask()} | ||||||
|  |             {this.renderMaskList()} | ||||||
|  |             {this.props.status?.popup.popups.map((popup) => { | ||||||
|  |                 return this.renderLayer(popup); | ||||||
|  |             })} | ||||||
|  |         </>; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { Popup }; | ||||||
							
								
								
									
										78
									
								
								source/Context/Context.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								source/Context/Context.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | |||||||
|  | import { Emitter, EventType } from "@Model/Emitter"; | ||||||
|  | import { Component, FunctionComponent, ReactNode, Consumer } from "react"; | ||||||
|  | 
 | ||||||
|  | type RenderComponent = (new (...p: any) => Component<any, any, any>) | FunctionComponent<any>; | ||||||
|  | 
 | ||||||
|  | function superConnectWithEvent<C extends Emitter<E>, E extends Record<EventType, any>>( | ||||||
|  | 	consumer: Consumer<C>, keyName: string | ||||||
|  | ) { | ||||||
|  | 	return (...events: Array<keyof E>) => { | ||||||
|  | 		return <R extends RenderComponent>(components: R): R => { | ||||||
|  | 			const Components = components as any; | ||||||
|  | 			const Consumer = consumer; | ||||||
|  | 			return class extends Component<R> { | ||||||
|  | 	 | ||||||
|  | 				private status: C | undefined; | ||||||
|  | 				private isEventMount: boolean = false; | ||||||
|  | 				private propsObject: Record<string, C> = {}; | ||||||
|  | 	 | ||||||
|  | 				private handelChange = () => { | ||||||
|  | 					this.forceUpdate(); | ||||||
|  | 				} | ||||||
|  | 	 | ||||||
|  | 				private mountEvent() { | ||||||
|  | 					if (this.status && !this.isEventMount) { | ||||||
|  | 						this.isEventMount = true; | ||||||
|  | 						console.log("Component dep event mount: " + events.join(", ")); | ||||||
|  | 						for (let i = 0; i < events.length; i++) { | ||||||
|  | 							this.status.on(events[i], this.handelChange); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 	 | ||||||
|  | 				private unmountEvent() { | ||||||
|  | 					if (this.status) { | ||||||
|  | 						for (let i = 0; i < events.length; i++) { | ||||||
|  | 							this.status.off(events[i], this.handelChange); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 	 | ||||||
|  | 				public render(): ReactNode { | ||||||
|  | 					return <Consumer> | ||||||
|  | 						{(status: C) => { | ||||||
|  | 							this.status = status; | ||||||
|  | 							this.propsObject[keyName] = status; | ||||||
|  | 							this.mountEvent(); | ||||||
|  | 							return <Components {...this.props} {...this.propsObject}/>; | ||||||
|  | 						}} | ||||||
|  | 					</Consumer> | ||||||
|  | 				} | ||||||
|  | 	 | ||||||
|  | 				public componentWillUnmount() { | ||||||
|  | 					this.unmountEvent(); | ||||||
|  | 				} | ||||||
|  | 	 | ||||||
|  | 			} as any; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function superConnect<C extends Emitter<any>>(consumer: Consumer<C>, keyName: string) { | ||||||
|  | 	return <R extends RenderComponent>(components: R): R => { | ||||||
|  | 		return ((props: any) => { | ||||||
|  | 
 | ||||||
|  | 			const Components = components as any; | ||||||
|  | 			const Consumer = consumer; | ||||||
|  | 
 | ||||||
|  | 			return <Consumer> | ||||||
|  | 				{(status: C) => <Components | ||||||
|  | 					{...props} | ||||||
|  | 					{...{[keyName]: status}} | ||||||
|  | 				/>} | ||||||
|  | 			</Consumer> | ||||||
|  | 		}) as any; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { superConnectWithEvent, superConnect }; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { Emitter } from "./Emitter"; | import { Emitter } from "@Model/Emitter"; | ||||||
| 
 | 
 | ||||||
| enum LayoutDirection { | enum LayoutDirection { | ||||||
| 	X = 1, | 	X = 1, | ||||||
							
								
								
									
										175
									
								
								source/Context/Popups.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								source/Context/Popups.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | import { ReactNode, createElement } from "react"; | ||||||
|  | import { Emitter } from "@Model/Emitter"; | ||||||
|  | import { Localization } from "@Component/Localization/Localization"; | ||||||
|  | 
 | ||||||
|  | type IPopupConstructor = new (controller: PopupController, id: string) => 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 needMask: boolean = true; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 单独遮挡下层的蒙版 | ||||||
|  |      */ | ||||||
|  |     public maskForSelf: boolean = false; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 唯一标识符 | ||||||
|  |      */ | ||||||
|  |     public id: string; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 控制器 | ||||||
|  |      */ | ||||||
|  |     public controller: PopupController; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 渲染层级 | ||||||
|  |      */ | ||||||
|  |     public index: number = Infinity; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * react 节点 | ||||||
|  |      */ | ||||||
|  |     public reactNode: ReactNode; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 渲染标题 | ||||||
|  |      */ | ||||||
|  |     public onRenderHeader(): ReactNode { | ||||||
|  |         return createElement(Localization, {i18nKey: "Popup.Title.Unnamed"}); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 渲染函数 | ||||||
|  |      */ | ||||||
|  |     public onRender(p: Popup): ReactNode { | ||||||
|  |         return null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 关闭回调 | ||||||
|  |      */ | ||||||
|  |     public onClose(): void { | ||||||
|  |         this.close(); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 渲染节点 | ||||||
|  |      */ | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         this.reactNode = this.onRender(this); | ||||||
|  |         return this.reactNode; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     public close() { | ||||||
|  |         return this.controller.closePopup(this); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public constructor(controller: PopupController, id: string) { | ||||||
|  |         this.controller = controller; | ||||||
|  |         this.id = id; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | interface IPopupControllerEvent { | ||||||
|  |     popupChange: void; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * 弹窗模型 | ||||||
|  |  */ | ||||||
|  | class PopupController extends Emitter<IPopupControllerEvent> { | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * ID 序列号 | ||||||
|  |      */ | ||||||
|  |     private idIndex = 0; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 最小弹窗 Index | ||||||
|  |      */ | ||||||
|  |     public zIndex = 100; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 弹窗列表 | ||||||
|  |      */ | ||||||
|  |     public popups: Popup[] = []; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 排序并重置序号 | ||||||
|  |      */ | ||||||
|  |     public sortPopup() { | ||||||
|  |         this.popups = this.popups.sort((a, b) => a.index - b.index); | ||||||
|  |         this.popups = this.popups.map((popup, index) => { | ||||||
|  |             popup.index = (index + 1); | ||||||
|  |             return popup; | ||||||
|  |         }); | ||||||
|  |         this.emit("popupChange"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 实例化并开启一个弹窗 | ||||||
|  |      */ | ||||||
|  |     public showPopup<P extends IPopupConstructor>(popup?: P): Popup { | ||||||
|  |         let newPopup = new (popup ?? Popup)(this, `P-${this.idIndex ++}`); | ||||||
|  |         this.popups.push(newPopup); | ||||||
|  |         this.sortPopup(); | ||||||
|  |         return newPopup; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * 关闭一个弹窗 | ||||||
|  |      */ | ||||||
|  |     public closePopup(popup: Popup | string): Popup | undefined { | ||||||
|  |         let id: string; | ||||||
|  |         if (popup instanceof Popup) { | ||||||
|  |             id = popup.id; | ||||||
|  |         } else { | ||||||
|  |             id = popup; | ||||||
|  |         } | ||||||
|  |         let closePopup: Popup | undefined; | ||||||
|  |         this.popups = this.popups.filter( | ||||||
|  |             currentPopup => { | ||||||
|  |                 let isDelete = currentPopup.id === id; | ||||||
|  |                 if (isDelete) { | ||||||
|  |                     closePopup = currentPopup; | ||||||
|  |                     currentPopup.isClose = true; | ||||||
|  |                     return false; | ||||||
|  |                 } else { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         ); | ||||||
|  |         if (closePopup) { | ||||||
|  |             this.sortPopup(); | ||||||
|  |             this.emit("popupChange"); | ||||||
|  |         } | ||||||
|  |         return closePopup; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { Popup, PopupController } | ||||||
| @ -1,6 +1,7 @@ | |||||||
| import { createContext, Component, FunctionComponent } from "react"; | import { createContext } from "react"; | ||||||
|  | import { superConnect, superConnectWithEvent } from "./Context"; | ||||||
| import { Emitter } from "@Model/Emitter"; | import { Emitter } from "@Model/Emitter"; | ||||||
| import { Layout } from "@Model/Layout"; | import { Layout } from "./Layout"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 主题模式 |  * 主题模式 | ||||||
| @ -12,9 +13,11 @@ enum Themes { | |||||||
| 
 | 
 | ||||||
| type Language = "ZH_CN" | "EN_US"; | type Language = "ZH_CN" | "EN_US"; | ||||||
| 
 | 
 | ||||||
| class Setting extends Emitter< | interface ISettingEvents extends Setting { | ||||||
|     Setting & {change: keyof Setting} |     attrChange: keyof Setting; | ||||||
| > { | } | ||||||
|  | 
 | ||||||
|  | class Setting extends Emitter<ISettingEvents> { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * 主题 |      * 主题 | ||||||
| @ -36,7 +39,7 @@ class Setting extends Emitter< | |||||||
|      */ |      */ | ||||||
|     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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -51,21 +54,14 @@ SettingContext.displayName = "Setting"; | |||||||
| const SettingProvider = SettingContext.Provider; | const SettingProvider = SettingContext.Provider; | ||||||
| const SettingConsumer = SettingContext.Consumer; | const SettingConsumer = SettingContext.Consumer; | ||||||
| 
 | 
 | ||||||
| type RenderComponent = (new (...p: any) => Component<any, any, any>) | FunctionComponent<any>; |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * 修饰器 |  * 修饰器 | ||||||
|  */ |  */ | ||||||
| function useSetting<R extends RenderComponent>(components: R): R { | const useSetting = superConnect<Setting>(SettingConsumer, "setting"); | ||||||
|     return ((props: any) => { | 
 | ||||||
|         const C = components; | const useSettingWithEvent = superConnectWithEvent<Setting, ISettingEvents>(SettingConsumer, "setting"); | ||||||
|         return <SettingConsumer> |  | ||||||
|             {(setting: Setting) => <C {...props} setting={setting}></C>} |  | ||||||
|         </SettingConsumer> |  | ||||||
|     }) as any; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|     Themes, Setting, SettingContext, useSetting, Language, |     Themes, Setting, SettingContext, useSetting, Language, useSettingWithEvent, | ||||||
|     IMixinSettingProps, SettingProvider, SettingConsumer |     IMixinSettingProps, SettingProvider, SettingConsumer | ||||||
| }; | }; | ||||||
| @ -1,4 +1,4 @@ | |||||||
| import { createContext, Component, FunctionComponent, useState, useEffect, 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"; | ||||||
| @ -9,6 +9,8 @@ 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"; | ||||||
| import { I18N } from "@Component/Localization/Localization"; | import { I18N } from "@Component/Localization/Localization"; | ||||||
|  | import { superConnectWithEvent, superConnect } from "./Context"; | ||||||
|  | import { PopupController } from "./Popups"; | ||||||
| 
 | 
 | ||||||
| function randomColor(unNormal: boolean = false) { | function randomColor(unNormal: boolean = false) { | ||||||
|     const color = [ |     const color = [ | ||||||
| @ -38,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> { | ||||||
| @ -65,6 +68,11 @@ class Status extends Emitter<IStatusEvent> { | |||||||
|      */ |      */ | ||||||
|     public model: Model = new Model(); |     public model: Model = new Model(); | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 弹窗 | ||||||
|  |      */ | ||||||
|  |     public popup: PopupController = new PopupController(); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 焦点对象 |      * 焦点对象 | ||||||
|      */ |      */ | ||||||
| @ -95,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); | ||||||
| @ -258,67 +269,12 @@ StatusContext.displayName = "Status"; | |||||||
| const StatusProvider = StatusContext.Provider; | const StatusProvider = StatusContext.Provider; | ||||||
| const StatusConsumer = StatusContext.Consumer; | const StatusConsumer = StatusContext.Consumer; | ||||||
| 
 | 
 | ||||||
| type RenderComponent = (new (...p: any) => Component<any, any, any>) | FunctionComponent<any>; |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * 修饰器 |  * 修饰器 | ||||||
|  */ |  */ | ||||||
| function useStatus<R extends RenderComponent>(components: R): R { | const useStatus = superConnect<Status>(StatusConsumer, "status"); | ||||||
|     return ((props: any) => { |  | ||||||
|         const C = components; |  | ||||||
|         return <StatusConsumer> |  | ||||||
|             {(status: Status) => <C {...props} status={status}></C>} |  | ||||||
|         </StatusConsumer> |  | ||||||
|     }) as any; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| function useStatusWithEvent(...events: Array<keyof IStatusEvent>) { | const useStatusWithEvent = superConnectWithEvent<Status, IStatusEvent>(StatusConsumer, "status"); | ||||||
|     return <R extends RenderComponent>(components: R): R => { |  | ||||||
|         const C = components as any; |  | ||||||
|         return class extends Component<R> { |  | ||||||
| 
 |  | ||||||
|             private status: Status | undefined; |  | ||||||
|             private isEventMount: boolean = false; |  | ||||||
| 
 |  | ||||||
|             private handelChange = () => { |  | ||||||
|                 this.forceUpdate(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             private mountEvent() { |  | ||||||
|                 if (this.status && !this.isEventMount) { |  | ||||||
|                     this.isEventMount = true; |  | ||||||
|                     console.log("Component dep event mount: " + events.join(", ")); |  | ||||||
|                     for (let i = 0; i < events.length; i++) { |  | ||||||
|                         this.status.on(events[i], this.handelChange); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             private unmountEvent() { |  | ||||||
|                 if (this.status) { |  | ||||||
|                     for (let i = 0; i < events.length; i++) { |  | ||||||
|                         this.status.off(events[i], this.handelChange); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public render(): ReactNode { |  | ||||||
|                 return <StatusConsumer> |  | ||||||
|                     {(status: Status) => { |  | ||||||
|                         this.status = status; |  | ||||||
|                         this.mountEvent(); |  | ||||||
|                         return <C {...this.props} status={status}></C>; |  | ||||||
|                     }} |  | ||||||
|                 </StatusConsumer> |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             public componentWillUnmount() { |  | ||||||
|                 this.unmountEvent(); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } as any; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|     Status, StatusContext, useStatus, useStatusWithEvent, |     Status, StatusContext, useStatus, useStatusWithEvent, | ||||||
|  | |||||||
| @ -44,6 +44,7 @@ const EN_US = { | |||||||
|     "Panel.Info.Label.Details.View": "Edit view label attributes", |     "Panel.Info.Label.Details.View": "Edit view label attributes", | ||||||
|     "Panel.Title.Group.Details.View": "Group", |     "Panel.Title.Group.Details.View": "Group", | ||||||
|     "Panel.Info.Group.Details.View": "Edit view group attributes", |     "Panel.Info.Group.Details.View": "Edit view group attributes", | ||||||
|  |     "Popup.Title.Unnamed": "Popup message", | ||||||
|     "Build.In.Label.Name.All.Group": "All group", |     "Build.In.Label.Name.All.Group": "All group", | ||||||
|     "Build.In.Label.Name.All.Range": "All range", |     "Build.In.Label.Name.All.Range": "All range", | ||||||
|     "Common.No.Data": "No Data", |     "Common.No.Data": "No Data", | ||||||
| @ -51,6 +52,7 @@ const EN_US = { | |||||||
|     "Common.Attr.Title.Basic": "Basic properties", |     "Common.Attr.Title.Basic": "Basic properties", | ||||||
|     "Common.Attr.Title.Spatial": "Spatial property", |     "Common.Attr.Title.Spatial": "Spatial property", | ||||||
|     "Common.Attr.Title.Individual.Generation": "Individual generation", |     "Common.Attr.Title.Individual.Generation": "Individual generation", | ||||||
|  |     "Common.Attr.Title.Individual.kill": "Individual kill", | ||||||
|     "Common.Attr.Key.Display.Name": "Display name", |     "Common.Attr.Key.Display.Name": "Display name", | ||||||
|     "Common.Attr.Key.Position.X": "Position X", |     "Common.Attr.Key.Position.X": "Position X", | ||||||
|     "Common.Attr.Key.Position.Y": "Position Y", |     "Common.Attr.Key.Position.Y": "Position Y", | ||||||
| @ -79,6 +81,8 @@ const EN_US = { | |||||||
|     "Common.Attr.Key.Generation.Error.Empty.Range.List": "The specified label does not contain any scope objects", |     "Common.Attr.Key.Generation.Error.Empty.Range.List": "The specified label does not contain any scope objects", | ||||||
|     "Common.Attr.Key.Generation.Error.Invalid.Range": "The specified scope object is invalid", |     "Common.Attr.Key.Generation.Error.Invalid.Range": "The specified scope object is invalid", | ||||||
|     "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired", |     "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired", | ||||||
|  |     "Common.Attr.Key.Kill.Random": "Random kill", | ||||||
|  |     "Common.Attr.Key.Kill.Count": "Kill count", | ||||||
|     "Panel.Info.Range.Details.Attr.Error.Not.Range": "Object is not a Range", |     "Panel.Info.Range.Details.Attr.Error.Not.Range": "Object is not a Range", | ||||||
|     "Panel.Info.Range.Details.Attr.Error.Unspecified": "Unspecified range object", |     "Panel.Info.Range.Details.Attr.Error.Unspecified": "Unspecified range object", | ||||||
|     "Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group", |     "Panel.Info.Group.Details.Attr.Error.Not.Group": "Object is not a Group", | ||||||
|  | |||||||
| @ -44,13 +44,15 @@ const ZH_CN = { | |||||||
|     "Panel.Info.Label.Details.View": "编辑查看标签属性", |     "Panel.Info.Label.Details.View": "编辑查看标签属性", | ||||||
|     "Panel.Title.Group.Details.View": "群", |     "Panel.Title.Group.Details.View": "群", | ||||||
|     "Panel.Info.Group.Details.View": "编辑查看群属性", |     "Panel.Info.Group.Details.View": "编辑查看群属性", | ||||||
|  |     "Popup.Title.Unnamed": "弹窗消息", | ||||||
|     "Build.In.Label.Name.All.Group": "全部群", |     "Build.In.Label.Name.All.Group": "全部群", | ||||||
|     "Build.In.Label.Name.All.Range": "全部范围", |     "Build.In.Label.Name.All.Range": "全部范围", | ||||||
|     "Common.No.Data": "暂无数据", |     "Common.No.Data": "暂无数据", | ||||||
|     "Common.No.Unknown.Error": "未知错误", |     "Common.No.Unknown.Error": "未知错误", | ||||||
|     "Common.Attr.Title.Basic": "基础属性", |     "Common.Attr.Title.Basic": "基础属性", | ||||||
|     "Common.Attr.Title.Spatial": "空间属性", |     "Common.Attr.Title.Spatial": "空间属性", | ||||||
|     "Common.Attr.Title.Individual.Generation": "个体生成", |     "Common.Attr.Title.Individual.Generation": "生成个体", | ||||||
|  |     "Common.Attr.Title.Individual.kill": "消除个体", | ||||||
|     "Common.Attr.Key.Display.Name": "显示名称", |     "Common.Attr.Key.Display.Name": "显示名称", | ||||||
|     "Common.Attr.Key.Position.X": "X 坐标", |     "Common.Attr.Key.Position.X": "X 坐标", | ||||||
|     "Common.Attr.Key.Position.Y": "Y 坐标", |     "Common.Attr.Key.Position.Y": "Y 坐标", | ||||||
| @ -79,6 +81,8 @@ const ZH_CN = { | |||||||
|     "Common.Attr.Key.Generation.Error.Empty.Range.List": "指定的标签中没有包含任何范围对象", |     "Common.Attr.Key.Generation.Error.Empty.Range.List": "指定的标签中没有包含任何范围对象", | ||||||
|     "Common.Attr.Key.Generation.Error.Invalid.Range": "指定的范围对象已失效", |     "Common.Attr.Key.Generation.Error.Invalid.Range": "指定的范围对象已失效", | ||||||
|     "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效", |     "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效", | ||||||
|  |     "Common.Attr.Key.Kill.Random": "随机消除", | ||||||
|  |     "Common.Attr.Key.Kill.Count": "消除数量", | ||||||
|     "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", |     "Panel.Info.Range.Details.Attr.Error.Not.Range": "对象不是一个范围", | ||||||
|     "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", |     "Panel.Info.Range.Details.Attr.Error.Unspecified": "未指定范围对象", | ||||||
|     "Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群", |     "Panel.Info.Group.Details.Attr.Error.Not.Group": "对象不是一个群", | ||||||
|  | |||||||
| @ -49,6 +49,11 @@ class Group extends CtrlObject { | |||||||
|      */ |      */ | ||||||
|     public genErrorMessageShowCount: number = 0; |     public genErrorMessageShowCount: number = 0; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 删除个数 | ||||||
|  |      */ | ||||||
|  |     public killCount: number = 1; | ||||||
|  | 
 | ||||||
|     private genInSingleRange(count: number, range: Range) { |     private genInSingleRange(count: number, range: Range) { | ||||||
|         for (let i = 0; i < count; i++) { |         for (let i = 0; i < count; i++) { | ||||||
|             let individual = new Individual(this); |             let individual = new Individual(this); | ||||||
| @ -188,6 +193,42 @@ class Group extends CtrlObject { | |||||||
|         return success; |         return success; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 随机杀死个体 | ||||||
|  |      */ | ||||||
|  |     public killIndividuals(): boolean { | ||||||
|  |         let success = false; | ||||||
|  |         let killCount = this.killCount; | ||||||
|  |         if (killCount > this.individuals.size) { | ||||||
|  |             killCount = this.individuals.size; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // 生成索引数组
 | ||||||
|  |         const allIndex = new Array(this.individuals.size).fill(0).map((_, i) => i); | ||||||
|  |         const deleteIndex: Set<number> = new Set(); | ||||||
|  | 
 | ||||||
|  |         for (let i = 0; i < killCount; i++) { | ||||||
|  |             let randomIndex = Math.floor(Math.random() * allIndex.length); | ||||||
|  |             deleteIndex.add(allIndex[randomIndex]); | ||||||
|  |             allIndex.splice(randomIndex, 1); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let j = 0; | ||||||
|  |         this.individuals.forEach((individual) => { | ||||||
|  |             if (deleteIndex.has(j)) { | ||||||
|  |                 this.remove(individual); | ||||||
|  |                 success = true; | ||||||
|  |             } | ||||||
|  |             j++; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (success) { | ||||||
|  |             this.model.emit("individualChange", this); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return success | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * 创建个体 | 	 * 创建个体 | ||||||
|      * @param count 创建数量 |      * @param count 创建数量 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ class Label { | |||||||
|      * 判断是否为相同标签 |      * 判断是否为相同标签 | ||||||
|      */ |      */ | ||||||
|     public equal(label: Label): boolean { |     public equal(label: Label): boolean { | ||||||
|         if (this.isDeleted() || label.isDeleted()) return false; |         // if (this.isDeleted() || label.isDeleted()) return false;
 | ||||||
|         return this === label || this.id === label.id; |         return this === label || this.id === label.id; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,9 +7,10 @@ import { StatusProvider, Status } from "@Context/Status"; | |||||||
| import { ClassicRenderer } from "@GLRender/ClassicRenderer"; | import { ClassicRenderer } from "@GLRender/ClassicRenderer"; | ||||||
| import { initializeIcons } from '@fluentui/font-icons-mdl2'; | import { initializeIcons } from '@fluentui/font-icons-mdl2'; | ||||||
| import { RootContainer } from "@Component/Container/RootContainer"; | import { RootContainer } from "@Component/Container/RootContainer"; | ||||||
| import { LayoutDirection } from "@Model/Layout"; | import { LayoutDirection } from "@Context/Layout"; | ||||||
| import "./SimulatorWeb.scss"; |  | ||||||
| import { CommandBar } from "@Component/CommandBar/CommandBar"; | import { CommandBar } from "@Component/CommandBar/CommandBar"; | ||||||
|  | import { Popup } from "@Component/Popup/Popup"; | ||||||
|  | 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)` | ||||||
|  | |||||||
| @ -136,6 +136,24 @@ class GroupDetails extends Component<IGroupDetailsProps & IMixinStatusProps> { | |||||||
| 				}} | 				}} | ||||||
| 			/> | 			/> | ||||||
| 
 | 
 | ||||||
|  |             <Message i18nKey="Common.Attr.Title.Individual.kill" isTitle/> | ||||||
|  | 
 | ||||||
|  |             <AttrInput | ||||||
|  |                 id={group.id} isNumber={true} step={1} keyI18n="Common.Attr.Key.Kill.Count" | ||||||
|  |                 value={group.killCount} min={1} max={1000} | ||||||
|  |                 valueChange={(val) => { | ||||||
|  |                     this.props.status?.changeGroupAttrib(group.id, "killCount", (val as any) / 1); | ||||||
|  |                 }} | ||||||
|  |             /> | ||||||
|  | 
 | ||||||
|  |             <TogglesInput | ||||||
|  | 				keyI18n="Common.Attr.Key.Generation" | ||||||
|  | 				onIconName="RemoveFilter" offIconName="RemoveFilter" | ||||||
|  | 				valueChange={() => { | ||||||
|  | 					group.killIndividuals() | ||||||
|  | 				}} | ||||||
|  | 			/> | ||||||
|  | 
 | ||||||
| 		</> | 		</> | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -31,6 +31,12 @@ | |||||||
|             ], |             ], | ||||||
|             "@Component/*": [ |             "@Component/*": [ | ||||||
|                 "./source/Component/*" |                 "./source/Component/*" | ||||||
|  |             ], | ||||||
|  |             "@Localization/*": [ | ||||||
|  |                 "./source/Localization/*" | ||||||
|  |             ], | ||||||
|  |             "@Panel/*": [ | ||||||
|  |                 "./source/Panel/*" | ||||||
|             ] |             ] | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user