@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.
Files changed (41) hide show
  1. package/README.md +62 -1
  2. package/dist/tmagic-stage.js +2157 -0
  3. package/dist/tmagic-stage.js.map +1 -0
  4. package/dist/{tmagic-stage.umd.js → tmagic-stage.umd.cjs} +1436 -993
  5. package/dist/tmagic-stage.umd.cjs.map +1 -0
  6. package/package.json +9 -8
  7. package/src/ActionManager.ts +534 -0
  8. package/src/DragResizeHelper.ts +338 -0
  9. package/src/MoveableOptionsManager.ts +249 -0
  10. package/src/MoveableSelectParentAble.ts +76 -0
  11. package/src/Rule.ts +13 -9
  12. package/src/StageCore.ts +220 -260
  13. package/src/StageDragResize.ts +83 -339
  14. package/src/StageHighlight.ts +17 -16
  15. package/src/StageMask.ts +88 -140
  16. package/src/StageMultiDragResize.ts +97 -198
  17. package/src/StageRender.ts +90 -15
  18. package/src/TargetShadow.ts +120 -0
  19. package/src/const.ts +1 -1
  20. package/src/types.ts +97 -19
  21. package/src/util.ts +24 -11
  22. package/types/ActionManager.d.ts +141 -0
  23. package/types/DragResizeHelper.d.ts +70 -0
  24. package/types/MoveableOptionsManager.d.ts +86 -0
  25. package/types/MoveableSelectParentAble.d.ts +8 -0
  26. package/types/Rule.d.ts +4 -3
  27. package/types/StageCore.d.ts +65 -39
  28. package/types/StageDragResize.d.ts +11 -40
  29. package/types/StageHighlight.d.ts +3 -4
  30. package/types/StageMask.d.ts +23 -22
  31. package/types/StageMultiDragResize.d.ts +8 -24
  32. package/types/StageRender.d.ts +24 -6
  33. package/types/TargetShadow.d.ts +22 -0
  34. package/types/const.d.ts +1 -1
  35. package/types/types.d.ts +85 -18
  36. package/types/util.d.ts +9 -8
  37. package/dist/tmagic-stage.mjs +0 -1715
  38. package/dist/tmagic-stage.mjs.map +0 -1
  39. package/dist/tmagic-stage.umd.js.map +0 -1
  40. package/src/TargetCalibrate.ts +0 -119
  41. 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 show 是否显示
36
+ * 是否显示辅助线
37
+ * @param isShowGuides 是否显示
37
38
  */
38
- public showGuides(show = true) {
39
+ public showGuides(isShowGuides = true) {
40
+ this.isShowGuides = isShowGuides;
41
+
39
42
  this.hGuides.setState({
40
- showGuides: show,
43
+ showGuides: isShowGuides,
41
44
  });
42
45
 
43
46
  this.vGuides.setState({
44
- showGuides: show,
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('changeGuides', {
63
+ this.emit('change-guides', {
61
64
  type: GuidesType.HORIZONTAL,
62
65
  guides: hLines,
63
66
  });
64
67
 
65
- this.emit('changeGuides', {
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('changeGuides', {
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('changeGuides', {
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 type { Id } from '@tmagic/schema';
22
- import { addClassName } from '@tmagic/utils';
21
+ import { Id } from '@tmagic/schema';
23
22
 
24
- import { CONTAINER_HIGHLIGHT_CLASS, DEFAULT_ZOOM, GHOST_EL_ID_PREFIX, PAGE_CLASS } from './const';
25
- import StageDragResize from './StageDragResize';
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
- CanSelect,
32
- ContainerHighlightType,
28
+ ActionManagerConfig,
29
+ CustomizeRender,
33
30
  GuidesEventData,
34
- IsContainer,
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
- public dr: StageDragResize;
55
- public multiDr: StageMultiDragResize;
56
- public highlightLayer: StageHighlight;
57
- public config: StageCoreConfig;
58
- public zoom = DEFAULT_ZOOM;
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.config = config;
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.mask
92
- .on('beforeSelect', async (event: MouseEvent) => {
93
- this.clearSelectStatus('multiSelect');
94
- const el = await this.getElementFromPoint(event);
95
- if (!el) return;
96
- this.select(el, event);
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
- this.emit('highlight', this.highlightedDom);
116
- })
117
- .on('clearHighlight', async () => {
118
- this.highlightLayer.clearHighlight();
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.multiDr
149
- .on('update', (data: UpdateEventData) => {
150
- setTimeout(() => this.emit('update', data));
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
- public getElementsFromPoint(event: MouseEvent) {
160
- const { renderer, zoom } = this;
161
-
162
- const doc = renderer.contentWindow?.document;
163
- let x = event.clientX;
164
- let y = event.clientY;
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
- if (renderer.iframe) {
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
- return doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[];
175
- }
86
+ this.mask.setLayout(el);
176
87
 
177
- public async getElementFromPoint(event: MouseEvent) {
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
- public async isElCanSelect(el: HTMLElement, event: MouseEvent, stop: () => boolean): Promise<Boolean> {
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 idOrEl 组件Dom节点的id属性,或者Dom节点
96
+ * 多选选中多个元素
97
+ * @param idOrElList 选中元素的id或元素列表
203
98
  */
204
- public async select(idOrEl: Id | HTMLElement, event?: MouseEvent): Promise<void> {
205
- this.clearSelectStatus('multiSelect');
206
- const el = await this.getTargetElement(idOrEl);
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
- this.mask.setLayout(el);
219
- this.dr.select(el, event);
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
- if (this.config.autoScrollIntoView || el.dataset.autoScrollIntoView) {
222
- this.mask.intersectionObserver?.observe(el);
223
- }
108
+ this.mask.setLayout(lastEl);
224
109
 
225
- this.selectedDom = el;
110
+ this.actionManager.multiSelect(idOrElList);
226
111
 
227
- if (this.renderer.contentWindow) {
228
- removeSelectedClassName(this.renderer.contentWindow.document);
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 domList 多选节点
118
+ * 高亮选中元素
119
+ * @param el 要高亮的元素
238
120
  */
239
- public async multiSelect(idOrElList: HTMLElement[] | Id[]): Promise<void> {
240
- this.clearSelectStatus('select');
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
- return this.renderer?.getRuntime().then((runtime) => {
254
- runtime?.update?.(data);
255
- // 更新配置后,需要等组件渲染更新
256
- setTimeout(() => {
257
- const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
258
- // 有可能dom已经重新渲染,不再是原来的dom了,所以这里判断id,而不是判断el === this.selectedDom
259
- if (el && el.id === this.selectedDom?.id) {
260
- this.selectedDom = el;
261
- // 更新了组件的布局,需要重新设置mask是否可以滚动
262
- this.mask.setLayout(el);
263
- this.dr.updateMoveable(el);
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 el 页面Dom节点
147
+ * 往画布增加一个组件
148
+ * @param data 组件信息数据
272
149
  */
273
- public async highlight(idOrEl: HTMLElement | Id): Promise<void> {
274
- let el;
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
- public sortNode(data: SortEventData): Promise<void> {
287
- return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
288
- }
289
-
290
- public add(data: UpdateData): Promise<void> {
291
- return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
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 = 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.dr.clearGuides();
185
+ this.actionManager.clearGuides();
335
186
  }
336
187
 
337
- public async addContainerHighlightClassName(event: MouseEvent, exclude: Element[]) {
338
- const els = this.getElementsFromPoint(event);
339
- const { renderer } = this;
340
- const doc = renderer.contentWindow?.document;
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
- public getAddContainerHighlightClassNameTimeout(event: MouseEvent, exclude: Element[] = []): NodeJS.Timeout {
353
- return globalThis.setTimeout(() => {
354
- this.addContainerHighlightClassName(event, exclude);
355
- }, this.containerHighlightDuration);
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, dr, highlightLayer } = this;
210
+ const { mask, renderer, actionManager, pageResizeObserver } = this;
363
211
 
364
212
  renderer.destroy();
365
213
  mask.destroy();
366
- dr.destroy();
367
- highlightLayer.destroy();
214
+ actionManager.destroy();
215
+ pageResizeObserver?.disconnect();
368
216
 
369
217
  this.removeAllListeners();
370
218
 
371
219
  this.container = undefined;
372
220
  }
373
221
 
374
- private async getTargetElement(idOrEl: Id | HTMLElement): Promise<HTMLElement> {
375
- if (typeof idOrEl === 'string' || typeof idOrEl === 'number') {
376
- const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
377
- if (!el) throw new Error(`不存在ID为${idOrEl}的元素`);
378
- return el;
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
- return idOrEl;
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
  }