@tmagic/stage 1.2.0-beta.2 → 1.2.0-beta.21
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/README.md +62 -1
- package/dist/tmagic-stage.js +2157 -0
- package/dist/tmagic-stage.js.map +1 -0
- package/dist/{tmagic-stage.umd.js → tmagic-stage.umd.cjs} +1436 -993
- package/dist/tmagic-stage.umd.cjs.map +1 -0
- package/package.json +9 -8
- package/src/ActionManager.ts +534 -0
- package/src/DragResizeHelper.ts +338 -0
- package/src/MoveableOptionsManager.ts +249 -0
- package/src/MoveableSelectParentAble.ts +76 -0
- package/src/Rule.ts +13 -9
- package/src/StageCore.ts +220 -260
- package/src/StageDragResize.ts +83 -339
- package/src/StageHighlight.ts +17 -16
- package/src/StageMask.ts +88 -140
- package/src/StageMultiDragResize.ts +97 -198
- package/src/StageRender.ts +90 -15
- package/src/TargetShadow.ts +120 -0
- package/src/const.ts +1 -1
- package/src/types.ts +97 -19
- package/src/util.ts +24 -11
- package/types/ActionManager.d.ts +141 -0
- package/types/DragResizeHelper.d.ts +70 -0
- package/types/MoveableOptionsManager.d.ts +86 -0
- package/types/MoveableSelectParentAble.d.ts +8 -0
- package/types/Rule.d.ts +4 -3
- package/types/StageCore.d.ts +65 -39
- package/types/StageDragResize.d.ts +11 -40
- package/types/StageHighlight.d.ts +3 -4
- package/types/StageMask.d.ts +23 -22
- package/types/StageMultiDragResize.d.ts +8 -24
- package/types/StageRender.d.ts +24 -6
- package/types/TargetShadow.d.ts +22 -0
- package/types/const.d.ts +1 -1
- package/types/types.d.ts +85 -18
- package/types/util.d.ts +9 -8
- package/dist/tmagic-stage.mjs +0 -1715
- package/dist/tmagic-stage.mjs.map +0 -1
- package/dist/tmagic-stage.umd.js.map +0 -1
- package/src/TargetCalibrate.ts +0 -119
- package/types/TargetCalibrate.d.ts +0 -20
package/src/Rule.ts
CHANGED
|
@@ -12,6 +12,7 @@ export default class Rule extends EventEmitter {
|
|
|
12
12
|
|
|
13
13
|
private container: HTMLDivElement;
|
|
14
14
|
private containerResizeObserver: ResizeObserver;
|
|
15
|
+
private isShowGuides = true;
|
|
15
16
|
|
|
16
17
|
constructor(container: HTMLDivElement) {
|
|
17
18
|
super();
|
|
@@ -32,16 +33,18 @@ export default class Rule extends EventEmitter {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
-
* @param
|
|
36
|
+
* 是否显示辅助线
|
|
37
|
+
* @param isShowGuides 是否显示
|
|
37
38
|
*/
|
|
38
|
-
public showGuides(
|
|
39
|
+
public showGuides(isShowGuides = true) {
|
|
40
|
+
this.isShowGuides = isShowGuides;
|
|
41
|
+
|
|
39
42
|
this.hGuides.setState({
|
|
40
|
-
showGuides:
|
|
43
|
+
showGuides: isShowGuides,
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
this.vGuides.setState({
|
|
44
|
-
showGuides:
|
|
47
|
+
showGuides: isShowGuides,
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -57,12 +60,12 @@ export default class Rule extends EventEmitter {
|
|
|
57
60
|
defaultGuides: vLines,
|
|
58
61
|
});
|
|
59
62
|
|
|
60
|
-
this.emit('
|
|
63
|
+
this.emit('change-guides', {
|
|
61
64
|
type: GuidesType.HORIZONTAL,
|
|
62
65
|
guides: hLines,
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
this.emit('
|
|
68
|
+
this.emit('change-guides', {
|
|
66
69
|
type: GuidesType.VERTICAL,
|
|
67
70
|
guides: vLines,
|
|
68
71
|
});
|
|
@@ -135,11 +138,12 @@ export default class Rule extends EventEmitter {
|
|
|
135
138
|
lineColor: '#000',
|
|
136
139
|
textColor: '#000',
|
|
137
140
|
style: this.getGuidesStyle(type),
|
|
141
|
+
showGuides: this.isShowGuides,
|
|
138
142
|
});
|
|
139
143
|
|
|
140
144
|
private hGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
|
|
141
145
|
this.horizontalGuidelines = e.guides;
|
|
142
|
-
this.emit('
|
|
146
|
+
this.emit('change-guides', {
|
|
143
147
|
type: GuidesType.HORIZONTAL,
|
|
144
148
|
guides: this.horizontalGuidelines,
|
|
145
149
|
});
|
|
@@ -147,7 +151,7 @@ export default class Rule extends EventEmitter {
|
|
|
147
151
|
|
|
148
152
|
private vGuidesChangeGuidesHandler = (e: GuidesEvents['changeGuides']) => {
|
|
149
153
|
this.verticalGuidelines = e.guides;
|
|
150
|
-
this.emit('
|
|
154
|
+
this.emit('change-guides', {
|
|
151
155
|
type: GuidesType.VERTICAL,
|
|
152
156
|
guides: this.verticalGuidelines,
|
|
153
157
|
});
|
package/src/StageCore.ts
CHANGED
|
@@ -18,298 +18,149 @@
|
|
|
18
18
|
|
|
19
19
|
import { EventEmitter } from 'events';
|
|
20
20
|
|
|
21
|
-
import
|
|
22
|
-
import { addClassName } from '@tmagic/utils';
|
|
21
|
+
import { Id } from '@tmagic/schema';
|
|
23
22
|
|
|
24
|
-
import
|
|
25
|
-
import
|
|
26
|
-
import StageHighlight from './StageHighlight';
|
|
23
|
+
import ActionManager from './ActionManager';
|
|
24
|
+
import { DEFAULT_ZOOM } from './const';
|
|
27
25
|
import StageMask from './StageMask';
|
|
28
|
-
import StageMultiDragResize from './StageMultiDragResize';
|
|
29
26
|
import StageRender from './StageRender';
|
|
30
27
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
ActionManagerConfig,
|
|
29
|
+
CustomizeRender,
|
|
33
30
|
GuidesEventData,
|
|
34
|
-
|
|
31
|
+
Point,
|
|
35
32
|
RemoveData,
|
|
36
33
|
Runtime,
|
|
37
|
-
SortEventData,
|
|
38
34
|
StageCoreConfig,
|
|
39
|
-
StageDragStatus,
|
|
40
35
|
UpdateData,
|
|
41
36
|
UpdateEventData,
|
|
42
37
|
} from './types';
|
|
43
|
-
import { addSelectedClassName, removeSelectedClassName } from './util';
|
|
44
38
|
|
|
39
|
+
/**
|
|
40
|
+
* 负责管理画布,管理renderer、mask、actionManager三个核心类,并负责统一对外通信,包括提供接口和抛事件
|
|
41
|
+
*/
|
|
45
42
|
export default class StageCore extends EventEmitter {
|
|
46
43
|
public container?: HTMLDivElement;
|
|
47
|
-
// 当前选中的节点
|
|
48
|
-
public selectedDom: HTMLElement | undefined;
|
|
49
|
-
// 多选选中的节点组
|
|
50
|
-
public selectedDomList: HTMLElement[] = [];
|
|
51
|
-
public highlightedDom: Element | undefined;
|
|
52
44
|
public renderer: StageRender;
|
|
53
45
|
public mask: StageMask;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
public containerHighlightClassName: string;
|
|
60
|
-
public containerHighlightDuration: number;
|
|
61
|
-
public containerHighlightType?: ContainerHighlightType;
|
|
62
|
-
public isContainer: IsContainer;
|
|
63
|
-
|
|
64
|
-
private canSelect: CanSelect;
|
|
46
|
+
|
|
47
|
+
private actionManager: ActionManager;
|
|
48
|
+
private pageResizeObserver: ResizeObserver | null = null;
|
|
49
|
+
private autoScrollIntoView: boolean | undefined;
|
|
50
|
+
private customizedRender?: CustomizeRender;
|
|
65
51
|
|
|
66
52
|
constructor(config: StageCoreConfig) {
|
|
67
53
|
super();
|
|
68
54
|
|
|
69
|
-
this.
|
|
70
|
-
|
|
71
|
-
this.setZoom(config.zoom);
|
|
72
|
-
this.canSelect = config.canSelect || ((el: HTMLElement) => !!el.id);
|
|
73
|
-
this.isContainer = config.isContainer;
|
|
74
|
-
this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS;
|
|
75
|
-
this.containerHighlightDuration = config.containerHighlightDuration || 800;
|
|
76
|
-
this.containerHighlightType = config.containerHighlightType;
|
|
77
|
-
|
|
78
|
-
this.renderer = new StageRender({ core: this });
|
|
79
|
-
this.mask = new StageMask({ core: this });
|
|
80
|
-
this.dr = new StageDragResize({ core: this, container: this.mask.content, mask: this.mask });
|
|
81
|
-
this.multiDr = new StageMultiDragResize({ core: this, container: this.mask.content, mask: this.mask });
|
|
82
|
-
this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
|
|
83
|
-
|
|
84
|
-
this.renderer.on('runtime-ready', (runtime: Runtime) => {
|
|
85
|
-
this.emit('runtime-ready', runtime);
|
|
86
|
-
});
|
|
87
|
-
this.renderer.on('page-el-update', (el: HTMLElement) => {
|
|
88
|
-
this.mask?.observe(el);
|
|
89
|
-
});
|
|
55
|
+
this.autoScrollIntoView = config.autoScrollIntoView;
|
|
56
|
+
this.customizedRender = config.render;
|
|
90
57
|
|
|
91
|
-
this.
|
|
92
|
-
.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
})
|
|
98
|
-
.on('select', () => {
|
|
99
|
-
this.emit('select', this.selectedDom);
|
|
100
|
-
})
|
|
101
|
-
.on('changeGuides', (data: GuidesEventData) => {
|
|
102
|
-
this.dr.setGuidelines(data.type, data.guides);
|
|
103
|
-
this.emit('changeGuides', data);
|
|
104
|
-
})
|
|
105
|
-
.on('highlight', async (event: MouseEvent) => {
|
|
106
|
-
const el = await this.getElementFromPoint(event);
|
|
107
|
-
if (!el) return;
|
|
108
|
-
// 如果多选组件处于拖拽状态时不进行组件高亮
|
|
109
|
-
if (this.multiDr.dragStatus === StageDragStatus.ING) return;
|
|
110
|
-
await this.highlight(el);
|
|
111
|
-
if (this.highlightedDom === this.selectedDom) {
|
|
112
|
-
this.highlightLayer.clearHighlight();
|
|
113
|
-
return;
|
|
58
|
+
this.renderer = new StageRender({
|
|
59
|
+
runtimeUrl: config.runtimeUrl,
|
|
60
|
+
zoom: config.zoom,
|
|
61
|
+
customizedRender: async (): Promise<HTMLElement | null> => {
|
|
62
|
+
if (this?.customizedRender) {
|
|
63
|
+
return await this.customizedRender(this);
|
|
114
64
|
}
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.on('beforeMultiSelect', async (event: MouseEvent) => {
|
|
121
|
-
const el = await this.getElementFromPoint(event);
|
|
122
|
-
if (!el) return;
|
|
123
|
-
// 如果已有单选选中元素,不是magic-ui-page就可以加入多选列表
|
|
124
|
-
if (this.selectedDom && !this.selectedDom.className.includes(PAGE_CLASS)) {
|
|
125
|
-
this.selectedDomList.push(this.selectedDom as HTMLElement);
|
|
126
|
-
this.selectedDom = undefined;
|
|
127
|
-
}
|
|
128
|
-
// 判断元素是否已在多选列表
|
|
129
|
-
const existIndex = this.selectedDomList.findIndex((selectedDom) => selectedDom.id === el.id);
|
|
130
|
-
if (existIndex !== -1) {
|
|
131
|
-
// 再次点击取消选中
|
|
132
|
-
this.selectedDomList.splice(existIndex, 1);
|
|
133
|
-
} else {
|
|
134
|
-
this.selectedDomList.push(el);
|
|
135
|
-
}
|
|
136
|
-
this.multiSelect(this.selectedDomList);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// 要先触发select,在触发update
|
|
140
|
-
this.dr
|
|
141
|
-
.on('update', (data: UpdateEventData) => {
|
|
142
|
-
setTimeout(() => this.emit('update', data));
|
|
143
|
-
})
|
|
144
|
-
.on('sort', (data: UpdateEventData) => {
|
|
145
|
-
setTimeout(() => this.emit('sort', data));
|
|
146
|
-
});
|
|
65
|
+
return null;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
this.mask = new StageMask();
|
|
69
|
+
this.actionManager = new ActionManager(this.getActionManagerConfig(config));
|
|
147
70
|
|
|
148
|
-
this.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
152
|
-
.on('select', async (id: Id) => {
|
|
153
|
-
const el = await this.getTargetElement(id);
|
|
154
|
-
this.select(el); // 选中
|
|
155
|
-
setTimeout(() => this.emit('select', el)); // set node
|
|
156
|
-
});
|
|
71
|
+
this.initRenderEvent();
|
|
72
|
+
this.initActionEvent();
|
|
73
|
+
this.initMaskEvent();
|
|
157
74
|
}
|
|
158
75
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 单选选中元素
|
|
78
|
+
* @param idOrEl 选中的id或者元素
|
|
79
|
+
*/
|
|
80
|
+
public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
|
|
81
|
+
const el = this.renderer.getTargetElement(idOrEl);
|
|
82
|
+
if (el === this.actionManager.getSelectedEl()) return;
|
|
165
83
|
|
|
166
|
-
|
|
167
|
-
const rect = renderer.iframe.getClientRects()[0];
|
|
168
|
-
if (rect) {
|
|
169
|
-
x = x - rect.left;
|
|
170
|
-
y = y - rect.top;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
84
|
+
await this.renderer.select([el]);
|
|
173
85
|
|
|
174
|
-
|
|
175
|
-
}
|
|
86
|
+
this.mask.setLayout(el);
|
|
176
87
|
|
|
177
|
-
|
|
178
|
-
const els = this.getElementsFromPoint(event);
|
|
179
|
-
let stopped = false;
|
|
180
|
-
const stop = () => (stopped = true);
|
|
181
|
-
for (const el of els) {
|
|
182
|
-
if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.isElCanSelect(el, event, stop))) {
|
|
183
|
-
if (stopped) break;
|
|
184
|
-
return el;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
88
|
+
this.actionManager.select(el, event);
|
|
188
89
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const canSelectByProp = await this.canSelect(el, event, stop);
|
|
192
|
-
if (!canSelectByProp) return false;
|
|
193
|
-
// 多选规则
|
|
194
|
-
if (this.mask.isMultiSelectStatus) {
|
|
195
|
-
return this.multiDr.canSelect(el, stop);
|
|
90
|
+
if (this.autoScrollIntoView || el.dataset.autoScrollIntoView) {
|
|
91
|
+
this.mask.observerIntersection(el);
|
|
196
92
|
}
|
|
197
|
-
return true;
|
|
198
93
|
}
|
|
199
94
|
|
|
200
95
|
/**
|
|
201
|
-
*
|
|
202
|
-
* @param
|
|
96
|
+
* 多选选中多个元素
|
|
97
|
+
* @param idOrElList 选中元素的id或元素列表
|
|
203
98
|
*/
|
|
204
|
-
public async
|
|
205
|
-
this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (el === this.selectedDom) return;
|
|
209
|
-
|
|
210
|
-
const runtime = await this.renderer.getRuntime();
|
|
211
|
-
|
|
212
|
-
await runtime?.select?.(el.id);
|
|
213
|
-
|
|
214
|
-
if (runtime?.beforeSelect) {
|
|
215
|
-
await runtime.beforeSelect(el);
|
|
216
|
-
}
|
|
99
|
+
public async multiSelect(idOrElList: HTMLElement[] | Id[]): Promise<void> {
|
|
100
|
+
const els = idOrElList.map((idOrEl) => this.renderer.getTargetElement(idOrEl));
|
|
101
|
+
if (els.length === 0) return;
|
|
217
102
|
|
|
218
|
-
|
|
219
|
-
|
|
103
|
+
const lastEl = els[els.length - 1];
|
|
104
|
+
// 是否减少了组件选择
|
|
105
|
+
const isReduceSelect = els.length < this.actionManager.getSelectedElList().length;
|
|
106
|
+
await this.renderer.select(els);
|
|
220
107
|
|
|
221
|
-
|
|
222
|
-
this.mask.intersectionObserver?.observe(el);
|
|
223
|
-
}
|
|
108
|
+
this.mask.setLayout(lastEl);
|
|
224
109
|
|
|
225
|
-
this.
|
|
110
|
+
this.actionManager.multiSelect(idOrElList);
|
|
226
111
|
|
|
227
|
-
if (this.
|
|
228
|
-
|
|
229
|
-
if (this.selectedDom) {
|
|
230
|
-
addSelectedClassName(this.selectedDom, this.renderer.contentWindow.document);
|
|
231
|
-
}
|
|
112
|
+
if ((this.autoScrollIntoView || lastEl.dataset.autoScrollIntoView) && !isReduceSelect) {
|
|
113
|
+
this.mask.observerIntersection(lastEl);
|
|
232
114
|
}
|
|
233
115
|
}
|
|
234
116
|
|
|
235
117
|
/**
|
|
236
|
-
*
|
|
237
|
-
* @param
|
|
118
|
+
* 高亮选中元素
|
|
119
|
+
* @param el 要高亮的元素
|
|
238
120
|
*/
|
|
239
|
-
public
|
|
240
|
-
this.
|
|
241
|
-
const elList = await Promise.all(idOrElList.map(async (idOrEl) => await this.getTargetElement(idOrEl)));
|
|
242
|
-
this.multiDr.multiSelect(elList);
|
|
243
|
-
this.emit('multiSelect', elList);
|
|
121
|
+
public highlight(idOrEl: Id | HTMLElement): void {
|
|
122
|
+
this.actionManager.highlight(idOrEl);
|
|
244
123
|
}
|
|
245
124
|
|
|
246
125
|
/**
|
|
247
|
-
*
|
|
248
|
-
* @param data
|
|
126
|
+
* 更新组件
|
|
127
|
+
* @param data 更新组件的数据
|
|
249
128
|
*/
|
|
250
|
-
public update(data: UpdateData): Promise<void> {
|
|
129
|
+
public async update(data: UpdateData): Promise<void> {
|
|
251
130
|
const { config } = data;
|
|
252
131
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
}, 0);
|
|
132
|
+
await this.renderer.update(data);
|
|
133
|
+
// 通过setTimeout等画布中组件完成渲染更新
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
const el = this.renderer.getTargetElement(`${config.id}`);
|
|
136
|
+
if (el && this.actionManager.isSelectedEl(el)) {
|
|
137
|
+
// 更新了组件的布局,需要重新设置mask是否可以滚动
|
|
138
|
+
this.mask.setLayout(el);
|
|
139
|
+
// 组件有更新,需要set
|
|
140
|
+
this.actionManager.setSelectedEl(el);
|
|
141
|
+
this.actionManager.updateMoveable(el);
|
|
142
|
+
}
|
|
266
143
|
});
|
|
267
144
|
}
|
|
268
145
|
|
|
269
146
|
/**
|
|
270
|
-
*
|
|
271
|
-
* @param
|
|
147
|
+
* 往画布增加一个组件
|
|
148
|
+
* @param data 组件信息数据
|
|
272
149
|
*/
|
|
273
|
-
public async
|
|
274
|
-
|
|
275
|
-
try {
|
|
276
|
-
el = await this.getTargetElement(idOrEl);
|
|
277
|
-
} catch (error) {
|
|
278
|
-
this.highlightLayer.clearHighlight();
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
if (el === this.highlightedDom || !el) return;
|
|
282
|
-
this.highlightLayer.highlight(el);
|
|
283
|
-
this.highlightedDom = el;
|
|
150
|
+
public async add(data: UpdateData): Promise<void> {
|
|
151
|
+
return await this.renderer.add(data);
|
|
284
152
|
}
|
|
285
153
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
public
|
|
291
|
-
return this.renderer
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
public remove(data: RemoveData): Promise<void> {
|
|
295
|
-
return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
|
|
154
|
+
/**
|
|
155
|
+
* 从画布删除一个组件
|
|
156
|
+
* @param data 组件信息数据
|
|
157
|
+
*/
|
|
158
|
+
public async remove(data: RemoveData): Promise<void> {
|
|
159
|
+
return await this.renderer.remove(data);
|
|
296
160
|
}
|
|
297
161
|
|
|
298
162
|
public setZoom(zoom: number = DEFAULT_ZOOM): void {
|
|
299
|
-
this.zoom
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 用于在切换选择模式时清除上一次的状态
|
|
304
|
-
* @param selectType 需要清理的选择模式 多选:multiSelect,单选:select
|
|
305
|
-
*/
|
|
306
|
-
public clearSelectStatus(selectType: String) {
|
|
307
|
-
if (selectType === 'multiSelect') {
|
|
308
|
-
this.multiDr.clearSelectStatus();
|
|
309
|
-
this.selectedDomList = [];
|
|
310
|
-
} else {
|
|
311
|
-
this.dr.clearSelectStatus();
|
|
312
|
-
}
|
|
163
|
+
this.renderer.setZoom(zoom);
|
|
313
164
|
}
|
|
314
165
|
|
|
315
166
|
/**
|
|
@@ -331,52 +182,161 @@ export default class StageCore extends EventEmitter {
|
|
|
331
182
|
*/
|
|
332
183
|
public clearGuides() {
|
|
333
184
|
this.mask.clearGuides();
|
|
334
|
-
this.
|
|
185
|
+
this.actionManager.clearGuides();
|
|
335
186
|
}
|
|
336
187
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (!doc) return;
|
|
343
|
-
|
|
344
|
-
for (const el of els) {
|
|
345
|
-
if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.isContainer(el)) && !exclude.includes(el)) {
|
|
346
|
-
addClassName(el, doc, this.containerHighlightClassName);
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
188
|
+
/**
|
|
189
|
+
* @deprecated 废弃接口,建议用delayedMarkContainer代替
|
|
190
|
+
*/
|
|
191
|
+
public getAddContainerHighlightClassNameTimeout(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout {
|
|
192
|
+
return this.delayedMarkContainer(event, excludeElList);
|
|
350
193
|
}
|
|
351
194
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
195
|
+
/**
|
|
196
|
+
* 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
|
|
197
|
+
* 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
|
|
198
|
+
* @param event 鼠标事件
|
|
199
|
+
* @param excludeElList 计算鼠标所在容器时要排除的元素列表
|
|
200
|
+
* @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
|
|
201
|
+
*/
|
|
202
|
+
public delayedMarkContainer(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout {
|
|
203
|
+
return this.actionManager.delayedMarkContainer(event, excludeElList);
|
|
356
204
|
}
|
|
357
205
|
|
|
358
206
|
/**
|
|
359
207
|
* 销毁实例
|
|
360
208
|
*/
|
|
361
209
|
public destroy(): void {
|
|
362
|
-
const { mask, renderer,
|
|
210
|
+
const { mask, renderer, actionManager, pageResizeObserver } = this;
|
|
363
211
|
|
|
364
212
|
renderer.destroy();
|
|
365
213
|
mask.destroy();
|
|
366
|
-
|
|
367
|
-
|
|
214
|
+
actionManager.destroy();
|
|
215
|
+
pageResizeObserver?.disconnect();
|
|
368
216
|
|
|
369
217
|
this.removeAllListeners();
|
|
370
218
|
|
|
371
219
|
this.container = undefined;
|
|
372
220
|
}
|
|
373
221
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
222
|
+
/**
|
|
223
|
+
* 监听页面大小变化
|
|
224
|
+
*/
|
|
225
|
+
private observePageResize(page: HTMLElement): void {
|
|
226
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
227
|
+
this.pageResizeObserver = new ResizeObserver((entries) => {
|
|
228
|
+
this.mask.pageResize(entries);
|
|
229
|
+
this.actionManager.updateMoveable();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
this.pageResizeObserver.observe(page);
|
|
379
233
|
}
|
|
380
|
-
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private getActionManagerConfig(config: StageCoreConfig): ActionManagerConfig {
|
|
237
|
+
const actionManagerConfig: ActionManagerConfig = {
|
|
238
|
+
containerHighlightClassName: config.containerHighlightClassName,
|
|
239
|
+
containerHighlightDuration: config.containerHighlightDuration,
|
|
240
|
+
containerHighlightType: config.containerHighlightType,
|
|
241
|
+
moveableOptions: config.moveableOptions,
|
|
242
|
+
multiMoveableOptions: config.multiMoveableOptions,
|
|
243
|
+
container: this.mask.content,
|
|
244
|
+
canSelect: config.canSelect,
|
|
245
|
+
isContainer: config.isContainer,
|
|
246
|
+
updateDragEl: config.updateDragEl,
|
|
247
|
+
getRootContainer: () => this.container,
|
|
248
|
+
getRenderDocument: () => this.renderer.getDocument(),
|
|
249
|
+
getTargetElement: (idOrEl: Id | HTMLElement) => this.renderer.getTargetElement(idOrEl),
|
|
250
|
+
getElementsFromPoint: (point: Point) => this.renderer.getElementsFromPoint(point),
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return actionManagerConfig;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private initRenderEvent(): void {
|
|
257
|
+
this.renderer.on('runtime-ready', (runtime: Runtime) => {
|
|
258
|
+
this.emit('runtime-ready', runtime);
|
|
259
|
+
});
|
|
260
|
+
this.renderer.on('page-el-update', (el: HTMLElement) => {
|
|
261
|
+
this.mask?.observe(el);
|
|
262
|
+
this.observePageResize(el);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private initMaskEvent(): void {
|
|
267
|
+
this.mask.on('change-guides', (data: GuidesEventData) => {
|
|
268
|
+
this.actionManager.setGuidelines(data.type, data.guides);
|
|
269
|
+
this.emit('change-guides', data);
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 初始化操作相关事件监听
|
|
275
|
+
*/
|
|
276
|
+
private initActionEvent(): void {
|
|
277
|
+
this.initActionManagerEvent();
|
|
278
|
+
this.initDrEvent();
|
|
279
|
+
this.initMulDrEvent();
|
|
280
|
+
this.initHighlightEvent();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* 初始化ActionManager类本身抛出来的事件监听
|
|
285
|
+
*/
|
|
286
|
+
private initActionManagerEvent(): void {
|
|
287
|
+
this.actionManager
|
|
288
|
+
.on('before-select', (idOrEl: Id | HTMLElement, event?: MouseEvent) => {
|
|
289
|
+
this.select(idOrEl, event);
|
|
290
|
+
})
|
|
291
|
+
.on('select', (selectedEl: HTMLElement) => {
|
|
292
|
+
this.emit('select', selectedEl);
|
|
293
|
+
})
|
|
294
|
+
.on('before-multi-select', (idOrElList: HTMLElement[] | Id[]) => {
|
|
295
|
+
this.multiSelect(idOrElList);
|
|
296
|
+
})
|
|
297
|
+
.on('multi-select', (selectedElList: HTMLElement[]) => {
|
|
298
|
+
this.emit('multi-select', selectedElList);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 初始化DragResize类通过ActionManager抛出来的事件监听
|
|
304
|
+
*/
|
|
305
|
+
private initDrEvent(): void {
|
|
306
|
+
this.actionManager
|
|
307
|
+
.on('update', (data: UpdateEventData) => {
|
|
308
|
+
this.emit('update', data);
|
|
309
|
+
})
|
|
310
|
+
.on('sort', (data: UpdateEventData) => {
|
|
311
|
+
this.emit('sort', data);
|
|
312
|
+
})
|
|
313
|
+
.on('select-parent', () => {
|
|
314
|
+
this.emit('select-parent');
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* 初始化MultiDragResize类通过ActionManager抛出来的事件监听
|
|
320
|
+
*/
|
|
321
|
+
private initMulDrEvent(): void {
|
|
322
|
+
this.actionManager
|
|
323
|
+
// 多选切换到单选
|
|
324
|
+
.on('change-to-select', (el: HTMLElement) => {
|
|
325
|
+
this.select(el);
|
|
326
|
+
// 先保证画布内完成渲染,再通知外部更新
|
|
327
|
+
setTimeout(() => this.emit('select', el));
|
|
328
|
+
})
|
|
329
|
+
.on('multi-update', (data: UpdateEventData, parentEl: HTMLElement | null) => {
|
|
330
|
+
this.emit('update', { data, parentEl });
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* 初始化Highlight类通过ActionManager抛出来的事件监听
|
|
336
|
+
*/
|
|
337
|
+
private initHighlightEvent(): void {
|
|
338
|
+
this.actionManager.on('highlight', async (highlightEl: HTMLElement) => {
|
|
339
|
+
this.emit('highlight', highlightEl);
|
|
340
|
+
});
|
|
381
341
|
}
|
|
382
342
|
}
|