Compare commits

..

3 Commits

Author SHA1 Message Date
90398b593e Add popup renderer 2022-03-22 17:31:49 +08:00
93e1a8d3ef Update ts config for map url 2022-03-22 14:02:53 +08:00
3357960f61 Add popup model 2022-03-22 13:55:20 +08:00
10 changed files with 248 additions and 15 deletions

View File

@ -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>
} }

View File

@ -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

View File

@ -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 };

View 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);
}

View 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 };

View File

@ -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;

View File

@ -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
}; };

View File

@ -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);

View File

@ -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)`

View File

@ -31,6 +31,12 @@
], ],
"@Component/*": [ "@Component/*": [
"./source/Component/*" "./source/Component/*"
],
"@Localization/*": [
"./source/Localization/*"
],
"@Panel/*": [
"./source/Panel/*"
] ]
} }
}, },