@tmagic/stage 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tmagic-stage.es.js +18045 -0
- package/dist/tmagic-stage.es.js.map +1 -0
- package/dist/tmagic-stage.umd.js +18062 -0
- package/dist/tmagic-stage.umd.js.map +1 -0
- package/dist/types/src/StageCore.d.ts +44 -0
- package/dist/types/src/StageDragResize.d.ts +63 -0
- package/dist/types/src/StageMask.d.ts +79 -0
- package/dist/types/src/StageRender.d.ts +28 -0
- package/dist/types/src/const.d.ts +2 -0
- package/dist/types/src/index.d.ts +7 -0
- package/dist/types/src/types.d.ts +84 -0
- package/dist/types/src/util.d.ts +18 -0
- package/package.json +40 -0
- package/src/StageCore.ts +222 -0
- package/src/StageDragResize.ts +471 -0
- package/src/StageMask.ts +308 -0
- package/src/StageRender.ts +121 -0
- package/src/const.ts +23 -0
- package/src/index.ts +26 -0
- package/src/logger.ts +37 -0
- package/src/types.ts +121 -0
- package/src/util.ts +120 -0
- package/tsconfig.json +9 -0
- package/vite.config.ts +27 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import { Id } from '@tmagic/schema';
|
|
4
|
+
import StageDragResize from './StageDragResize';
|
|
5
|
+
import StageMask from './StageMask';
|
|
6
|
+
import StageRender from './StageRender';
|
|
7
|
+
import { RemoveData, SortEventData, StageCoreConfig, UpdateData } from './types';
|
|
8
|
+
export default class StageCore extends EventEmitter {
|
|
9
|
+
selectedDom: Element | undefined;
|
|
10
|
+
renderer: StageRender;
|
|
11
|
+
mask: StageMask;
|
|
12
|
+
dr: StageDragResize;
|
|
13
|
+
config: StageCoreConfig;
|
|
14
|
+
zoom: number;
|
|
15
|
+
constructor(config: StageCoreConfig);
|
|
16
|
+
/**
|
|
17
|
+
* 选中组件
|
|
18
|
+
* @param idOrEl 组件Dom节点的id属性,或者Dom节点
|
|
19
|
+
*/
|
|
20
|
+
select(idOrEl: Id | HTMLElement): Promise<void>;
|
|
21
|
+
update(data: UpdateData): void;
|
|
22
|
+
sortNode(data: SortEventData): void;
|
|
23
|
+
add(data: UpdateData): void;
|
|
24
|
+
remove(data: RemoveData): void;
|
|
25
|
+
setZoom(zoom?: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* 挂载Dom节点
|
|
28
|
+
* @param el 将stage挂载到该Dom节点上
|
|
29
|
+
*/
|
|
30
|
+
mount(el: HTMLDivElement): void;
|
|
31
|
+
/**
|
|
32
|
+
* 清空所有参考线
|
|
33
|
+
*/
|
|
34
|
+
clearGuides(): void;
|
|
35
|
+
/**
|
|
36
|
+
* 销毁实例
|
|
37
|
+
*/
|
|
38
|
+
destroy(): void;
|
|
39
|
+
private maskScrollHandler;
|
|
40
|
+
private hGuidesChangeGuidesHandler;
|
|
41
|
+
private vGuidesChangeGuidesHandler;
|
|
42
|
+
private setMaskLayout;
|
|
43
|
+
private beforeSelect;
|
|
44
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import Moveable from 'moveable';
|
|
4
|
+
import StageCore from './StageCore';
|
|
5
|
+
import type { SortEventData, StageDragResizeConfig } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* 选中框
|
|
8
|
+
*/
|
|
9
|
+
export default class StageDragResize extends EventEmitter {
|
|
10
|
+
core: StageCore;
|
|
11
|
+
container: HTMLElement;
|
|
12
|
+
target?: HTMLElement;
|
|
13
|
+
moveable?: Moveable;
|
|
14
|
+
private dragStatus;
|
|
15
|
+
private elObserver?;
|
|
16
|
+
private ghostEl;
|
|
17
|
+
private horizontalGuidelines;
|
|
18
|
+
private verticalGuidelines;
|
|
19
|
+
private mode;
|
|
20
|
+
constructor(config: StageDragResizeConfig);
|
|
21
|
+
/**
|
|
22
|
+
* 将选中框渲染并覆盖到选中的组件Dom节点上方
|
|
23
|
+
* @param el 选中组件的Dom节点元素
|
|
24
|
+
*/
|
|
25
|
+
select(el: HTMLElement): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 初始化选中框并渲染出来
|
|
28
|
+
* @param param0
|
|
29
|
+
*/
|
|
30
|
+
refresh(): Promise<void>;
|
|
31
|
+
setVGuidelines(verticalGuidelines: number[]): Promise<void>;
|
|
32
|
+
setHGuidelines(horizontalGuidelines: number[]): Promise<void>;
|
|
33
|
+
updateMoveableTarget(target?: HTMLElement): void;
|
|
34
|
+
/**
|
|
35
|
+
* 销毁实例
|
|
36
|
+
*/
|
|
37
|
+
destroy(): void;
|
|
38
|
+
private bindResizeEvent;
|
|
39
|
+
private bindDragEvent;
|
|
40
|
+
private getSnapElements;
|
|
41
|
+
private sort;
|
|
42
|
+
private drag;
|
|
43
|
+
private generateGhostEl;
|
|
44
|
+
private destroyGhostEl;
|
|
45
|
+
private getOptions;
|
|
46
|
+
private initObserver;
|
|
47
|
+
private syncRect;
|
|
48
|
+
private calcValueByFontsize;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 下移组件位置
|
|
52
|
+
* @param {number} deltaTop 偏移量
|
|
53
|
+
* @param {Object} detail 当前选中的组件配置
|
|
54
|
+
*/
|
|
55
|
+
export declare const down: (deltaTop: number, target: HTMLElement | SVGElement) => SortEventData | void;
|
|
56
|
+
/**
|
|
57
|
+
* 上移组件位置
|
|
58
|
+
* @param {Array} brothers 处于同一容器下的所有子组件配置
|
|
59
|
+
* @param {number} index 当前组件所处的位置
|
|
60
|
+
* @param {number} deltaTop 偏移量
|
|
61
|
+
* @param {Object} detail 当前选中的组件配置
|
|
62
|
+
*/
|
|
63
|
+
export declare const up: (deltaTop: number, target: HTMLElement | SVGElement) => SortEventData | void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import Guides from '@scena/guides';
|
|
4
|
+
import type StageCore from './StageCore';
|
|
5
|
+
import type { StageMaskConfig } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* 蒙层
|
|
8
|
+
* @description 用于拦截页面的点击动作,避免点击时触发组件自身动作;在编辑器中点击组件应当是选中组件;
|
|
9
|
+
*/
|
|
10
|
+
export default class StageMask extends EventEmitter {
|
|
11
|
+
content: HTMLDivElement;
|
|
12
|
+
wrapper: HTMLDivElement;
|
|
13
|
+
core: StageCore;
|
|
14
|
+
page: HTMLElement | null;
|
|
15
|
+
hGuides: Guides;
|
|
16
|
+
vGuides: Guides;
|
|
17
|
+
private target;
|
|
18
|
+
private resizeObserver;
|
|
19
|
+
private parentResizeObserver;
|
|
20
|
+
private canSelect;
|
|
21
|
+
constructor(config: StageMaskConfig);
|
|
22
|
+
/**
|
|
23
|
+
* 设置成固定定位模式
|
|
24
|
+
*/
|
|
25
|
+
setFixed(): void;
|
|
26
|
+
/**
|
|
27
|
+
* 设置成绝对定位模式
|
|
28
|
+
*/
|
|
29
|
+
setAbsolute(): void;
|
|
30
|
+
/**
|
|
31
|
+
* 监听页面大小变化
|
|
32
|
+
* @description 同步页面与mask的大小
|
|
33
|
+
* @param page 页面Dom节点
|
|
34
|
+
*/
|
|
35
|
+
observe(page: HTMLElement): void;
|
|
36
|
+
/**
|
|
37
|
+
* 挂载Dom节点
|
|
38
|
+
* @param el 将蒙层挂载到该Dom节点上
|
|
39
|
+
*/
|
|
40
|
+
mount(el: HTMLDivElement): void;
|
|
41
|
+
/**
|
|
42
|
+
* 销毁实例
|
|
43
|
+
*/
|
|
44
|
+
destroy(): void;
|
|
45
|
+
/**
|
|
46
|
+
* 是否显示标尺
|
|
47
|
+
* @param show 是否显示
|
|
48
|
+
*/
|
|
49
|
+
showGuides(show?: boolean): void;
|
|
50
|
+
/**
|
|
51
|
+
* 是否显示标尺
|
|
52
|
+
* @param show 是否显示
|
|
53
|
+
*/
|
|
54
|
+
showRule(show?: boolean): void;
|
|
55
|
+
/**
|
|
56
|
+
* 清空所有参考线
|
|
57
|
+
*/
|
|
58
|
+
clearGuides(): void;
|
|
59
|
+
/**
|
|
60
|
+
* 设置蒙层高度
|
|
61
|
+
* @param height 高度
|
|
62
|
+
*/
|
|
63
|
+
private setHeight;
|
|
64
|
+
/**
|
|
65
|
+
* 设置蒙层宽度
|
|
66
|
+
* @param width 宽度
|
|
67
|
+
*/
|
|
68
|
+
private setWidth;
|
|
69
|
+
/**
|
|
70
|
+
* 点击事件处理函数
|
|
71
|
+
* @param event 事件对象
|
|
72
|
+
*/
|
|
73
|
+
private mouseDownHandler;
|
|
74
|
+
private mouseUpHandler;
|
|
75
|
+
private contextmenuHandler;
|
|
76
|
+
private select;
|
|
77
|
+
private getGuidesStyle;
|
|
78
|
+
private createGuides;
|
|
79
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import StageCore from './StageCore';
|
|
4
|
+
import type { Runtime, RuntimeWindow, StageRenderConfig } from './types';
|
|
5
|
+
export default class StageRender extends EventEmitter {
|
|
6
|
+
/** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
|
|
7
|
+
contentWindow: RuntimeWindow | null;
|
|
8
|
+
runtime: Runtime | null;
|
|
9
|
+
iframe?: HTMLIFrameElement;
|
|
10
|
+
runtimeUrl?: string;
|
|
11
|
+
core: StageCore;
|
|
12
|
+
private render?;
|
|
13
|
+
constructor({ core }: StageRenderConfig);
|
|
14
|
+
getMagicApi: () => {
|
|
15
|
+
onPageElUpdate: (el: HTMLElement) => boolean;
|
|
16
|
+
onRuntimeReady: (runtime: Runtime) => void;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* 挂载Dom节点
|
|
20
|
+
* @param el 将页面挂载到该Dom节点上
|
|
21
|
+
*/
|
|
22
|
+
mount(el: HTMLDivElement): Promise<void>;
|
|
23
|
+
getRuntime: () => Promise<Runtime>;
|
|
24
|
+
/**
|
|
25
|
+
* 销毁实例
|
|
26
|
+
*/
|
|
27
|
+
destroy(): void;
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import StageCore from './StageCore';
|
|
2
|
+
export type { MoveableOptions } from 'moveable';
|
|
3
|
+
export { default as StageRender } from './StageRender';
|
|
4
|
+
export { default as StageMask } from './StageMask';
|
|
5
|
+
export { default as StageDragResize } from './StageDragResize';
|
|
6
|
+
export * from './types';
|
|
7
|
+
export default StageCore;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { MoveableOptions } from 'react-moveable/declaration/types';
|
|
2
|
+
import { Id, MApp, MNode } from '@tmagic/schema';
|
|
3
|
+
import StageCore from './StageCore';
|
|
4
|
+
export declare type CanSelect = (el: HTMLElement, stop: () => boolean) => boolean | Promise<boolean>;
|
|
5
|
+
export declare type StageCoreConfig = {
|
|
6
|
+
/** 需要对齐的dom节点的CSS选择器字符串 */
|
|
7
|
+
snapElementQuerySelector?: string;
|
|
8
|
+
/** 放大倍数,默认1倍 */
|
|
9
|
+
zoom?: number;
|
|
10
|
+
canSelect?: CanSelect;
|
|
11
|
+
moveableOptions?: ((core?: StageCore) => MoveableOptions) | MoveableOptions;
|
|
12
|
+
/** runtime 的HTML地址,可以是一个HTTP地址,如果和编辑器不同域,需要设置跨域,也可以是一个相对或绝对路径 */
|
|
13
|
+
runtimeUrl?: string;
|
|
14
|
+
render?: (renderer: StageCore) => Promise<HTMLElement> | HTMLElement;
|
|
15
|
+
};
|
|
16
|
+
export interface StageRenderConfig {
|
|
17
|
+
core: StageCore;
|
|
18
|
+
}
|
|
19
|
+
export interface StageMaskConfig {
|
|
20
|
+
core: StageCore;
|
|
21
|
+
}
|
|
22
|
+
export interface StageDragResizeConfig {
|
|
23
|
+
core: StageCore;
|
|
24
|
+
container: HTMLElement;
|
|
25
|
+
}
|
|
26
|
+
export declare type Rect = {
|
|
27
|
+
width: number;
|
|
28
|
+
height: number;
|
|
29
|
+
} & Offset;
|
|
30
|
+
export interface Offset {
|
|
31
|
+
left: number;
|
|
32
|
+
top: number;
|
|
33
|
+
}
|
|
34
|
+
export interface UpdateEventData {
|
|
35
|
+
el: HTMLElement;
|
|
36
|
+
ghostEl: HTMLElement;
|
|
37
|
+
style: {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
left?: number;
|
|
41
|
+
top?: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export interface SortEventData {
|
|
45
|
+
src: Id;
|
|
46
|
+
dist: Id;
|
|
47
|
+
root?: MApp;
|
|
48
|
+
}
|
|
49
|
+
export interface UpdateData {
|
|
50
|
+
config: MNode;
|
|
51
|
+
root: MApp;
|
|
52
|
+
}
|
|
53
|
+
export interface RemoveData {
|
|
54
|
+
id: Id;
|
|
55
|
+
root: MApp;
|
|
56
|
+
}
|
|
57
|
+
export interface Runtime {
|
|
58
|
+
beforeSelect?: (el: HTMLElement) => Promise<boolean> | boolean;
|
|
59
|
+
getSnapElements?: (el: HTMLElement) => HTMLElement[];
|
|
60
|
+
updateRootConfig: (config: MApp) => void;
|
|
61
|
+
updatePageId?: (id: Id) => void;
|
|
62
|
+
select?: (id: Id) => Promise<HTMLElement> | HTMLElement;
|
|
63
|
+
add?: (data: UpdateData) => void;
|
|
64
|
+
update?: (data: UpdateData) => void;
|
|
65
|
+
sortNode?: (data: SortEventData) => void;
|
|
66
|
+
remove?: (data: RemoveData) => void;
|
|
67
|
+
}
|
|
68
|
+
export interface Magic {
|
|
69
|
+
/** 当前页面的根节点变化时调用该方法,编辑器会同步该el和stage的大小,该方法由stage注入到iframe.contentWindow中 */
|
|
70
|
+
onPageElUpdate: (el: HTMLElement) => void;
|
|
71
|
+
onRuntimeReady: (runtime: Runtime) => void;
|
|
72
|
+
}
|
|
73
|
+
export interface RuntimeWindow extends Window {
|
|
74
|
+
magic: Magic;
|
|
75
|
+
}
|
|
76
|
+
export declare enum ZIndex {
|
|
77
|
+
MASK = "99999",
|
|
78
|
+
GHOST_EL = "99998"
|
|
79
|
+
}
|
|
80
|
+
export declare enum MouseButton {
|
|
81
|
+
LEFT = 0,
|
|
82
|
+
MIDDLE = 1,
|
|
83
|
+
RIGHT = 2
|
|
84
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Offset } from './types';
|
|
2
|
+
export declare enum Mode {
|
|
3
|
+
ABSOLUTE = "absolute",
|
|
4
|
+
FIXED = "fixed",
|
|
5
|
+
SORTABLE = "sortable"
|
|
6
|
+
}
|
|
7
|
+
export declare const getOffset: (el: HTMLElement) => Offset;
|
|
8
|
+
export declare const getAbsolutePosition: (el: HTMLElement, { top, left }: Offset) => {
|
|
9
|
+
left: number;
|
|
10
|
+
top: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const getHost: (targetUrl: string) => string | undefined;
|
|
13
|
+
export declare const isSameDomain: (targetUrl?: string, source?: string) => boolean;
|
|
14
|
+
export declare const isAbsolute: (el?: HTMLElement | undefined) => boolean;
|
|
15
|
+
export declare const isRelative: (el?: HTMLElement | undefined) => boolean;
|
|
16
|
+
export declare const isStatic: (el?: HTMLElement | undefined) => boolean;
|
|
17
|
+
export declare const isFixed: (el?: HTMLElement | undefined) => boolean;
|
|
18
|
+
export declare const getMode: (el: HTMLElement) => Mode;
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0-beta.1",
|
|
3
|
+
"name": "@tmagic/stage",
|
|
4
|
+
"main": "dist/magic-stage.umd.js",
|
|
5
|
+
"module": "dist/magic-stage.es.js",
|
|
6
|
+
"types": "dist/types/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/magic-stage.es.js",
|
|
10
|
+
"require": "./dist/magic-stage.umd.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "vite build"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=14"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/Tencent/tmagic-editor.git"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@scena/guides": "^0.17.0",
|
|
25
|
+
"@tmagic/schema": "^1.0.0-beta.1",
|
|
26
|
+
"@tmagic/utils": "^1.0.0-beta.1",
|
|
27
|
+
"events": "^3.3.0",
|
|
28
|
+
"lodash-es": "^4.17.21",
|
|
29
|
+
"moveable": "^0.25.3"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/events": "^3.0.0",
|
|
33
|
+
"@types/lodash-es": "^4.17.4",
|
|
34
|
+
"@types/node": "^15.12.4",
|
|
35
|
+
"sass": "^1.35.1",
|
|
36
|
+
"typescript": "^4.3.4",
|
|
37
|
+
"vite": "^2.3.7",
|
|
38
|
+
"vite-plugin-dts": "^0.9.6"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/StageCore.ts
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { EventEmitter } from 'events';
|
|
20
|
+
|
|
21
|
+
import { GuidesEvents } from '@scena/guides';
|
|
22
|
+
|
|
23
|
+
import { Id } from '@tmagic/schema';
|
|
24
|
+
|
|
25
|
+
import { DEFAULT_ZOOM } from './const';
|
|
26
|
+
import StageDragResize from './StageDragResize';
|
|
27
|
+
import StageMask from './StageMask';
|
|
28
|
+
import StageRender from './StageRender';
|
|
29
|
+
import { RemoveData, Runtime, SortEventData, StageCoreConfig, UpdateData, UpdateEventData } from './types';
|
|
30
|
+
import { isFixed } from './util';
|
|
31
|
+
|
|
32
|
+
export default class StageCore extends EventEmitter {
|
|
33
|
+
public selectedDom: Element | undefined;
|
|
34
|
+
|
|
35
|
+
public renderer: StageRender;
|
|
36
|
+
public mask: StageMask;
|
|
37
|
+
public dr: StageDragResize;
|
|
38
|
+
public config: StageCoreConfig;
|
|
39
|
+
public zoom = DEFAULT_ZOOM;
|
|
40
|
+
|
|
41
|
+
constructor(config: StageCoreConfig) {
|
|
42
|
+
super();
|
|
43
|
+
|
|
44
|
+
this.config = config;
|
|
45
|
+
|
|
46
|
+
this.setZoom(config.zoom);
|
|
47
|
+
|
|
48
|
+
this.renderer = new StageRender({ core: this });
|
|
49
|
+
this.mask = new StageMask({ core: this });
|
|
50
|
+
this.dr = new StageDragResize({ core: this, container: this.mask.content });
|
|
51
|
+
|
|
52
|
+
this.renderer.on('runtime-ready', (runtime: Runtime) => this.emit('runtime-ready', runtime));
|
|
53
|
+
this.renderer.on('page-el-update', (el: HTMLElement) => this.mask?.observe(el));
|
|
54
|
+
|
|
55
|
+
this.mask.on('select', async (el: Element, event: MouseEvent) => {
|
|
56
|
+
await this.dr?.select(el as HTMLElement);
|
|
57
|
+
setTimeout(() => this.dr?.moveable?.dragStart(event));
|
|
58
|
+
});
|
|
59
|
+
this.mask.on('selected', (el: Element) => {
|
|
60
|
+
this.select(el as HTMLElement);
|
|
61
|
+
this.emit('select', el);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.dr.on('update', (data: UpdateEventData) => this.emit('update', data));
|
|
65
|
+
this.dr.on('sort', (data: UpdateEventData) => this.emit('sort', data));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 选中组件
|
|
70
|
+
* @param idOrEl 组件Dom节点的id属性,或者Dom节点
|
|
71
|
+
*/
|
|
72
|
+
public async select(idOrEl: Id | HTMLElement): Promise<void> {
|
|
73
|
+
let el;
|
|
74
|
+
if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
|
|
75
|
+
const runtime = await this.renderer?.getRuntime();
|
|
76
|
+
el = await runtime?.select?.(`${idOrEl}`);
|
|
77
|
+
if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
|
|
78
|
+
} else {
|
|
79
|
+
el = idOrEl;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this.selectedDom === el) return;
|
|
83
|
+
|
|
84
|
+
await this.beforeSelect(el);
|
|
85
|
+
|
|
86
|
+
this.selectedDom = el;
|
|
87
|
+
this.setMaskLayout(el);
|
|
88
|
+
this.dr?.select(el);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public update(data: UpdateData): void {
|
|
92
|
+
const { config } = data;
|
|
93
|
+
|
|
94
|
+
this.renderer?.getRuntime().then((runtime) => {
|
|
95
|
+
runtime?.update?.(data);
|
|
96
|
+
// 更新配置后,需要等组件渲染更新
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
|
|
99
|
+
if (el) {
|
|
100
|
+
// 更新了组件的布局,需要重新设置mask是否可以滚动
|
|
101
|
+
this.setMaskLayout(el);
|
|
102
|
+
}
|
|
103
|
+
this.dr?.refresh();
|
|
104
|
+
}, 0);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public sortNode(data: SortEventData): void {
|
|
109
|
+
this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public add(data: UpdateData): void {
|
|
113
|
+
this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public remove(data: RemoveData): void {
|
|
117
|
+
this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public setZoom(zoom: number = DEFAULT_ZOOM): void {
|
|
121
|
+
this.zoom = zoom;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 挂载Dom节点
|
|
126
|
+
* @param el 将stage挂载到该Dom节点上
|
|
127
|
+
*/
|
|
128
|
+
public mount(el: HTMLDivElement): void {
|
|
129
|
+
const { mask, renderer } = this;
|
|
130
|
+
|
|
131
|
+
renderer.mount(el);
|
|
132
|
+
mask.mount(el);
|
|
133
|
+
|
|
134
|
+
const { wrapper: maskWrapper, hGuides, vGuides } = mask;
|
|
135
|
+
|
|
136
|
+
maskWrapper.addEventListener('scroll', this.maskScrollHandler);
|
|
137
|
+
hGuides.on('changeGuides', this.hGuidesChangeGuidesHandler);
|
|
138
|
+
vGuides.on('changeGuides', this.vGuidesChangeGuidesHandler);
|
|
139
|
+
|
|
140
|
+
this.emit('mounted');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 清空所有参考线
|
|
145
|
+
*/
|
|
146
|
+
public clearGuides() {
|
|
147
|
+
this.mask.clearGuides();
|
|
148
|
+
this.dr.setHGuidelines([]);
|
|
149
|
+
this.dr.setVGuidelines([]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 销毁实例
|
|
154
|
+
*/
|
|
155
|
+
public destroy(): void {
|
|
156
|
+
const { mask, renderer, dr } = this;
|
|
157
|
+
const { wrapper: maskWrapper, hGuides, vGuides } = mask;
|
|
158
|
+
|
|
159
|
+
renderer.destroy();
|
|
160
|
+
mask.destroy();
|
|
161
|
+
dr.destroy();
|
|
162
|
+
maskWrapper.removeEventListener('scroll', this.maskScrollHandler);
|
|
163
|
+
hGuides.off('changeGuides', this.hGuidesChangeGuidesHandler);
|
|
164
|
+
vGuides.off('changeGuides', this.vGuidesChangeGuidesHandler);
|
|
165
|
+
this.removeAllListeners();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private maskScrollHandler = (event: Event) => {
|
|
169
|
+
const { mask, renderer } = this;
|
|
170
|
+
const { wrapper: maskWrapper, hGuides, vGuides } = mask;
|
|
171
|
+
const { scrollTop } = maskWrapper;
|
|
172
|
+
|
|
173
|
+
renderer?.contentWindow?.document.documentElement.scrollTo({ top: scrollTop });
|
|
174
|
+
|
|
175
|
+
hGuides.scrollGuides(scrollTop);
|
|
176
|
+
hGuides.scroll(0);
|
|
177
|
+
|
|
178
|
+
vGuides.scrollGuides(0);
|
|
179
|
+
vGuides.scroll(scrollTop);
|
|
180
|
+
|
|
181
|
+
this.emit('scroll', event);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
private hGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
|
|
185
|
+
this.dr.setHGuidelines(e.guides);
|
|
186
|
+
this.emit('changeGuides');
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
private vGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
|
|
190
|
+
this.dr.setVGuidelines(e.guides);
|
|
191
|
+
this.emit('changeGuides');
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
private setMaskLayout(el: HTMLElement): void {
|
|
195
|
+
let fixed = false;
|
|
196
|
+
let dom = el;
|
|
197
|
+
while (dom) {
|
|
198
|
+
fixed = isFixed(dom);
|
|
199
|
+
if (fixed) {
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
const { parentElement } = dom;
|
|
203
|
+
if (!parentElement || parentElement.tagName === 'BODY') {
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
dom = parentElement;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (fixed) {
|
|
210
|
+
this.mask?.setFixed();
|
|
211
|
+
} else {
|
|
212
|
+
this.mask?.setAbsolute();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private async beforeSelect(el: HTMLElement): Promise<void> {
|
|
217
|
+
const runtime = await this.renderer?.getRuntime();
|
|
218
|
+
if (runtime?.beforeSelect) {
|
|
219
|
+
await runtime.beforeSelect(el);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|