living-together/source/Context/Popups.ts
2022-04-09 12:21:08 +08:00

206 lines
4.4 KiB
TypeScript

import { ReactNode, createElement } from "react";
import { Emitter } from "@Model/Emitter";
import { Localization } from "@Component/Localization/Localization";
import { IAnyObject } from "@Model/Model";
enum ResizeDragDirection {
top = 1,
rightTop = 2,
right = 3,
rightBottom = 4,
bottom = 5,
leftBottom = 6,
left = 7,
LeftTop = 8
}
/**
* 弹窗类型
*/
class Popup<P extends IAnyObject = IAnyObject> {
public props: P;
public constructor(props: P) {
this.props = props;
}
public zIndex() {
return this.index * 5 + this.controller.zIndex;
}
public width: number = 300;
public height: number = 200;
public minWidth: number = 300;
public minHeight: number = 200;
public top: number = NaN;
public left: number = NaN;
public lastMouseTop: number = 0;
public lastMouseLeft: number = 0;
public isOnMouseDown: boolean = false;
public resizeHoverDirection?: ResizeDragDirection;
public resizeDragDirection?: ResizeDragDirection;
public isResizeMouseDown: boolean = false;
public isResizeOverFlowX: boolean = false;
public isResizeOverFlowY: boolean = false;
public isInit = false;
/**
* 是否关闭
*/
public isClose: boolean = false;
/**
* 需要蒙版
*/
public needMask: boolean = true;
/**
* 单独遮挡下层的蒙版
*/
public maskForSelf: boolean = false;
/**
* 唯一标识符
*/
public id: string = "";
/**
* 控制器
*/
public controller: PopupController = undefined as any;
/**
* 渲染层级
*/
public index: number = Infinity;
/**
* 渲染标题
*/
public onRenderHeader(): ReactNode {
return createElement(Localization, {i18nKey: "Popup.Title.Unnamed"});
}
/**
* 关闭回调
*/
public onClose(): void {
this.close();
};
/**
* 渲染节点
*/
public render(): ReactNode {
return null;
};
public close(): Popup | undefined {
return this.controller.closePopup(this);
}
public init(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 topping(popup: Popup) {
popup.index = Infinity;
this.sortPopup();
}
/**
* 排序并重置序号
*/
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 IAnyObject, T extends Popup<P>>(
popup: (new (props: P) => T) | Popup<P>, props: P
): Popup<P> {
let newPopup: Popup<P>;
if (popup instanceof Popup) {
newPopup = popup;
} else {
newPopup = new (popup ?? Popup)(props);
}
newPopup.init(this, `P-${this.idIndex ++}`);
// 延迟渲染防止焦点冲突
setTimeout(() => {
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, ResizeDragDirection }