@tmagic/stage 1.7.7 → 1.7.8-beta.2

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.
@@ -1,2833 +0,0 @@
1
- import EventEmitter, { EventEmitter as EventEmitter$1 } from 'events';
2
- import { removeClassName, getIdFromEl, guid, setIdToEl, getElById, calcValueByFontsize, addClassName, removeClassNameByClassName, Env, getDocument, createDiv, injectStyle, isSameDomain, getHost } from '@tmagic/core';
3
- import KeyController from 'keycon';
4
- import { merge, throttle } from 'lodash-es';
5
- import MoveableHelper from 'moveable-helper';
6
- import Moveable__default from 'moveable';
7
- export * from 'moveable';
8
- import Guides from '@scena/guides';
9
-
10
- const GHOST_EL_ID_PREFIX = "ghost_el_";
11
- const DRAG_EL_ID_PREFIX = "drag_el_";
12
- const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
13
- const CONTAINER_HIGHLIGHT_CLASS_NAME = "tmagic-stage-container-highlight";
14
- const PAGE_CLASS = "magic-ui-page";
15
- const DEFAULT_ZOOM = 1;
16
- var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
17
- GuidesType2["HORIZONTAL"] = "horizontal";
18
- GuidesType2["VERTICAL"] = "vertical";
19
- return GuidesType2;
20
- })(GuidesType || {});
21
- var ZIndex = /* @__PURE__ */ ((ZIndex2) => {
22
- ZIndex2["MASK"] = "99999";
23
- ZIndex2["SELECTED_EL"] = "666";
24
- ZIndex2["GHOST_EL"] = "700";
25
- ZIndex2["DRAG_EL"] = "9";
26
- ZIndex2["HIGHLIGHT_EL"] = "8";
27
- return ZIndex2;
28
- })(ZIndex || {});
29
- var MouseButton = /* @__PURE__ */ ((MouseButton2) => {
30
- MouseButton2[MouseButton2["LEFT"] = 0] = "LEFT";
31
- MouseButton2[MouseButton2["MIDDLE"] = 1] = "MIDDLE";
32
- MouseButton2[MouseButton2["RIGHT"] = 2] = "RIGHT";
33
- return MouseButton2;
34
- })(MouseButton || {});
35
- var Mode = /* @__PURE__ */ ((Mode2) => {
36
- Mode2["ABSOLUTE"] = "absolute";
37
- Mode2["FIXED"] = "fixed";
38
- Mode2["SORTABLE"] = "sortable";
39
- return Mode2;
40
- })(Mode || {});
41
- const SELECTED_CLASS = "tmagic-stage-selected-area";
42
- var AbleActionEventType = /* @__PURE__ */ ((AbleActionEventType2) => {
43
- AbleActionEventType2["SELECT_PARENT"] = "select-parent";
44
- AbleActionEventType2["REMOVE"] = "remove";
45
- AbleActionEventType2["RERENDER"] = "rerender";
46
- return AbleActionEventType2;
47
- })(AbleActionEventType || {});
48
- var ContainerHighlightType = /* @__PURE__ */ ((ContainerHighlightType2) => {
49
- ContainerHighlightType2["DEFAULT"] = "default";
50
- ContainerHighlightType2["ALT"] = "alt";
51
- return ContainerHighlightType2;
52
- })(ContainerHighlightType || {});
53
- var RenderType = /* @__PURE__ */ ((RenderType2) => {
54
- RenderType2["IFRAME"] = "iframe";
55
- RenderType2["NATIVE"] = "native";
56
- return RenderType2;
57
- })(RenderType || {});
58
- var SelectStatus = /* @__PURE__ */ ((SelectStatus2) => {
59
- SelectStatus2["SELECT"] = "select";
60
- SelectStatus2["MULTI_SELECT"] = "multiSelect";
61
- return SelectStatus2;
62
- })(SelectStatus || {});
63
- var StageDragStatus = /* @__PURE__ */ ((StageDragStatus2) => {
64
- StageDragStatus2["START"] = "start";
65
- StageDragStatus2["ING"] = "ing";
66
- StageDragStatus2["END"] = "end";
67
- return StageDragStatus2;
68
- })(StageDragStatus || {});
69
-
70
- const getParents = (el, relative) => {
71
- let cur = el.parentElement;
72
- const parents = [];
73
- while (cur && cur !== relative) {
74
- parents.push(cur);
75
- cur = cur.parentElement;
76
- }
77
- return parents;
78
- };
79
- const getOffset = (el) => {
80
- const htmlEl = el;
81
- const { offsetParent } = htmlEl;
82
- const left = htmlEl.offsetLeft || 0;
83
- const top = htmlEl.offsetTop || 0;
84
- if (offsetParent) {
85
- const parentOffset = getOffset(offsetParent);
86
- return {
87
- left: left + parentOffset.left,
88
- top: top + parentOffset.top
89
- };
90
- }
91
- return {
92
- left,
93
- top
94
- };
95
- };
96
- const getTargetElStyle = (el, zIndex) => {
97
- const offset = getOffset(el);
98
- const { transform, border } = getComputedStyle(el);
99
- return `
100
- position: absolute;
101
- transform: ${transform};
102
- left: ${offset.left}px;
103
- top: ${offset.top}px;
104
- width: ${el.clientWidth}px;
105
- height: ${el.clientHeight}px;
106
- border: ${border};
107
- opacity: 0;
108
- ${typeof zIndex !== "undefined" ? `z-index: ${zIndex};` : ""}
109
- `;
110
- };
111
- const getAbsolutePosition = (el, { top, left }) => {
112
- const { offsetParent } = el;
113
- if (offsetParent) {
114
- const parentOffset = getOffset(offsetParent);
115
- return {
116
- left: left - parentOffset.left,
117
- top: top - parentOffset.top
118
- };
119
- }
120
- return { left, top };
121
- };
122
- const isAbsolute = (style) => style.position === "absolute";
123
- const isRelative = (style) => style.position === "relative";
124
- const isStatic = (style) => style.position === "static";
125
- const isFixed = (style) => style.position === "fixed";
126
- const isFixedParent = (el) => {
127
- let fixed = false;
128
- let dom = el;
129
- while (dom) {
130
- fixed = isFixed(getComputedStyle(dom));
131
- if (fixed) {
132
- break;
133
- }
134
- const { parentElement } = dom;
135
- if (!parentElement || parentElement.tagName === "BODY") {
136
- break;
137
- }
138
- dom = parentElement;
139
- }
140
- return fixed;
141
- };
142
- const getMode = (el) => {
143
- if (isFixedParent(el)) return Mode.FIXED;
144
- const style = getComputedStyle(el);
145
- if (isStatic(style) || isRelative(style)) return Mode.SORTABLE;
146
- return Mode.ABSOLUTE;
147
- };
148
- const getScrollParent = (element, includeHidden = false) => {
149
- let style = getComputedStyle(element);
150
- const overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/;
151
- if (isFixed(style)) return null;
152
- for (let parent = element; parent.parentElement; ) {
153
- parent = parent.parentElement;
154
- if (parent.tagName === "HTML") return parent;
155
- style = getComputedStyle(parent);
156
- if (isAbsolute(style) && isStatic(style)) continue;
157
- if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)) return parent;
158
- }
159
- return null;
160
- };
161
- const removeSelectedClassName = (doc) => {
162
- const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
163
- if (oldEl) {
164
- removeClassName(oldEl, SELECTED_CLASS);
165
- if (oldEl.parentNode) removeClassName(oldEl.parentNode, `${SELECTED_CLASS}-parent`);
166
- doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
167
- removeClassName(item, `${SELECTED_CLASS}-parents`);
168
- });
169
- }
170
- };
171
- const addSelectedClassName = (el, doc) => {
172
- el.classList.add(SELECTED_CLASS);
173
- el.parentNode?.classList.add(`${SELECTED_CLASS}-parent`);
174
- getParents(el, doc.body).forEach((item) => {
175
- item.classList.add(`${SELECTED_CLASS}-parents`);
176
- });
177
- };
178
- const down = (deltaTop, target) => {
179
- let swapIndex = 0;
180
- let addUpH = target.clientHeight;
181
- const brothers = Array.from(target.parentNode?.children || []).filter(
182
- (child) => !getIdFromEl()(child)?.startsWith(GHOST_EL_ID_PREFIX)
183
- );
184
- const index = brothers.indexOf(target);
185
- const downEls = brothers.slice(index + 1);
186
- for (let i = 0; i < downEls.length; i++) {
187
- const ele = downEls[i];
188
- if (ele.style?.position === "fixed") {
189
- continue;
190
- }
191
- addUpH += ele.clientHeight / 2;
192
- if (deltaTop <= addUpH) {
193
- break;
194
- }
195
- addUpH += ele.clientHeight / 2;
196
- swapIndex = i;
197
- }
198
- const src = getIdFromEl()(target) || "";
199
- return {
200
- src,
201
- dist: downEls.length && swapIndex > -1 ? getIdFromEl()(downEls[swapIndex]) || "" : src
202
- };
203
- };
204
- const up = (deltaTop, target) => {
205
- const brothers = Array.from(target.parentNode?.children || []).filter(
206
- (child) => !getIdFromEl()(child)?.startsWith(GHOST_EL_ID_PREFIX)
207
- );
208
- const index = brothers.indexOf(target);
209
- const upEls = brothers.slice(0, index);
210
- let addUpH = target.clientHeight;
211
- let swapIndex = upEls.length - 1;
212
- for (let i = upEls.length - 1; i >= 0; i--) {
213
- const ele = upEls[i];
214
- if (!ele) continue;
215
- if (ele.style.position === "fixed") continue;
216
- addUpH += ele.clientHeight / 2;
217
- if (-deltaTop <= addUpH) break;
218
- addUpH += ele.clientHeight / 2;
219
- swapIndex = i;
220
- }
221
- const src = getIdFromEl()(target) || "";
222
- return {
223
- src,
224
- dist: upEls.length && swapIndex > -1 ? getIdFromEl()(upEls[swapIndex]) || "" : src
225
- };
226
- };
227
- const isMoveableButton = (target) => target.classList.contains("moveable-button") || target.parentElement?.classList.contains("moveable-button");
228
- const getMarginValue = (el) => {
229
- if (!el)
230
- return {
231
- marginLeft: 0,
232
- marginTop: 0
233
- };
234
- const { marginLeft, marginTop } = getComputedStyle(el);
235
- const marginLeftValue = parseFloat(marginLeft) || 0;
236
- const marginTopValue = parseFloat(marginTop) || 0;
237
- return {
238
- marginLeft: marginLeftValue,
239
- marginTop: marginTopValue
240
- };
241
- };
242
- const getBorderWidth = (el) => {
243
- if (!el)
244
- return {
245
- borderLeftWidth: 0,
246
- borderRightWidth: 0,
247
- borderTopWidth: 0,
248
- borderBottomWidth: 0
249
- };
250
- const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getComputedStyle(el);
251
- return {
252
- borderLeftWidth: parseFloat(borderLeftWidth) || 0,
253
- borderRightWidth: parseFloat(borderRightWidth) || 0,
254
- borderTopWidth: parseFloat(borderTopWidth) || 0,
255
- borderBottomWidth: parseFloat(borderBottomWidth) || 0
256
- };
257
- };
258
-
259
- class TargetShadow {
260
- el;
261
- els = [];
262
- idPrefix = `target_calibrate_${guid()}`;
263
- container;
264
- scrollLeft = 0;
265
- scrollTop = 0;
266
- zIndex;
267
- updateDragEl;
268
- constructor(config) {
269
- this.container = config.container;
270
- if (config.updateDragEl) {
271
- this.updateDragEl = config.updateDragEl;
272
- }
273
- if (typeof config.zIndex !== "undefined") {
274
- this.zIndex = config.zIndex;
275
- }
276
- if (config.idPrefix) {
277
- this.idPrefix = `${config.idPrefix}_${guid()}`;
278
- }
279
- this.container.addEventListener("customScroll", this.scrollHandler);
280
- }
281
- update(target) {
282
- this.el = this.updateEl(target, this.el);
283
- return this.el;
284
- }
285
- updateGroup(targetGroup) {
286
- if (this.els.length > targetGroup.length) {
287
- this.els.slice(targetGroup.length - 1).forEach((el) => {
288
- el.remove();
289
- });
290
- }
291
- this.els = targetGroup.map((target, index) => this.updateEl(target, this.els[index]));
292
- return this.els;
293
- }
294
- destroyEl() {
295
- this.el?.remove();
296
- this.el = void 0;
297
- }
298
- destroyEls() {
299
- this.els.forEach((el) => {
300
- el.remove();
301
- });
302
- this.els = [];
303
- }
304
- destroy() {
305
- this.container.removeEventListener("customScroll", this.scrollHandler);
306
- this.destroyEl();
307
- this.destroyEls();
308
- }
309
- updateEl(target, src) {
310
- const el = src || globalThis.document.createElement("div");
311
- setIdToEl()(el, `${this.idPrefix}_${getIdFromEl()(target)}`);
312
- el.style.cssText = getTargetElStyle(target, this.zIndex);
313
- if (typeof this.updateDragEl === "function") {
314
- this.updateDragEl(el, target, this.container);
315
- }
316
- const isFixed = isFixedParent(target);
317
- const mode = this.container.dataset.mode || Mode.ABSOLUTE;
318
- if (isFixed && mode !== Mode.FIXED) {
319
- el.style.transform = `translate3d(${this.scrollLeft}px, ${this.scrollTop}px, 0)`;
320
- } else if (!isFixed && mode === Mode.FIXED) {
321
- el.style.transform = `translate3d(${-this.scrollLeft}px, ${-this.scrollTop}px, 0)`;
322
- }
323
- if (!getElById()(globalThis.document, getIdFromEl()(el))) {
324
- this.container.append(el);
325
- }
326
- return el;
327
- }
328
- scrollHandler = (e) => {
329
- this.scrollLeft = e.detail.scrollLeft;
330
- this.scrollTop = e.detail.scrollTop;
331
- };
332
- }
333
-
334
- class DragResizeHelper {
335
- /** 目标节点在蒙层上的占位节点,用于跟鼠标交互,避免鼠标事件直接作用到目标节点 */
336
- targetShadow;
337
- /** 要操作的原始目标节点 */
338
- target = null;
339
- /** 多选:目标节点组 */
340
- targetList = [];
341
- /** 响应拖拽的状态事件,修改绝对定位布局下targetShadow的dom。
342
- * MoveableHelper里面的方法是成员属性,如果DragResizeHelper用继承的方式将无法通过super去调这些方法 */
343
- moveableHelper;
344
- /** 流式布局下,目标节点的镜像节点 */
345
- ghostEl;
346
- /** 用于记录节点被改变前的位置 */
347
- frameSnapShot = {
348
- left: 0,
349
- top: 0
350
- };
351
- /** 多选模式下的多个节点 */
352
- framesSnapShot = [];
353
- /** 布局方式:流式布局、绝对定位、固定定位 */
354
- mode = Mode.ABSOLUTE;
355
- constructor(config) {
356
- this.moveableHelper = MoveableHelper.create({
357
- useBeforeRender: true,
358
- useRender: false,
359
- createAuto: true
360
- });
361
- this.targetShadow = new TargetShadow({
362
- container: config.container,
363
- updateDragEl: config.updateDragEl,
364
- zIndex: ZIndex.DRAG_EL,
365
- idPrefix: DRAG_EL_ID_PREFIX
366
- });
367
- }
368
- destroy() {
369
- this.target = null;
370
- this.targetList = [];
371
- this.targetShadow.destroy();
372
- this.destroyGhostEl();
373
- this.moveableHelper.clear();
374
- }
375
- destroyShadowEl() {
376
- this.targetShadow.destroyEl();
377
- }
378
- getShadowEl() {
379
- return this.targetShadow.el;
380
- }
381
- updateShadowEl(el) {
382
- this.destroyGhostEl();
383
- this.target = el;
384
- this.targetShadow.update(el);
385
- }
386
- setMode(mode) {
387
- this.mode = mode;
388
- }
389
- /**
390
- * 改变大小事件开始
391
- * @param e 包含了拖拽节点的dom,moveableHelper会直接修改拖拽节点
392
- */
393
- onResizeStart(e) {
394
- this.moveableHelper.onResizeStart(e);
395
- this.frameSnapShot.top = this.target.offsetTop;
396
- this.frameSnapShot.left = this.target.offsetLeft;
397
- }
398
- onResize(e) {
399
- const { width, height, drag } = e;
400
- const { beforeTranslate } = drag;
401
- if (this.mode === Mode.SORTABLE) {
402
- this.target.style.top = "0px";
403
- if (this.targetShadow.el) {
404
- this.targetShadow.el.style.width = `${width}px`;
405
- this.targetShadow.el.style.height = `${height}px`;
406
- }
407
- } else {
408
- this.moveableHelper.onResize(e);
409
- const { marginLeft, marginTop } = getMarginValue(this.target);
410
- this.target.style.left = `${this.frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
411
- this.target.style.top = `${this.frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
412
- }
413
- const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(this.target);
414
- this.target.style.width = `${width + borderLeftWidth + borderRightWidth}px`;
415
- this.target.style.height = `${height + borderTopWidth + borderBottomWidth}px`;
416
- }
417
- onDragStart(e) {
418
- this.moveableHelper.onDragStart(e);
419
- if (this.mode === Mode.SORTABLE) {
420
- this.ghostEl = this.generateGhostEl(this.target);
421
- }
422
- this.frameSnapShot.top = this.target.offsetTop;
423
- this.frameSnapShot.left = this.target.offsetLeft;
424
- }
425
- onDrag(e) {
426
- if (this.ghostEl) {
427
- this.ghostEl.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1]}px`;
428
- return;
429
- }
430
- this.moveableHelper.onDrag(e);
431
- const { marginLeft, marginTop } = getMarginValue(this.target);
432
- this.target.style.left = `${this.frameSnapShot.left + e.beforeTranslate[0] - marginLeft}px`;
433
- this.target.style.top = `${this.frameSnapShot.top + e.beforeTranslate[1] - marginTop}px`;
434
- }
435
- onRotateStart(e) {
436
- this.moveableHelper.onRotateStart(e);
437
- }
438
- onRotate(e) {
439
- this.moveableHelper.onRotate(e);
440
- const frame = this.moveableHelper.getFrame(e.target);
441
- this.target.style.transform = frame?.toCSSObject().transform || "";
442
- }
443
- onScaleStart(e) {
444
- this.moveableHelper.onScaleStart(e);
445
- }
446
- onScale(e) {
447
- this.moveableHelper.onScale(e);
448
- const frame = this.moveableHelper.getFrame(e.target);
449
- this.target.style.transform = frame?.toCSSObject().transform || "";
450
- }
451
- getGhostEl() {
452
- return this.ghostEl;
453
- }
454
- destroyGhostEl() {
455
- this.ghostEl?.remove();
456
- this.ghostEl = void 0;
457
- }
458
- clear() {
459
- this.moveableHelper.clear();
460
- }
461
- getFrame(el) {
462
- return this.moveableHelper.getFrame(el);
463
- }
464
- getShadowEls() {
465
- return this.targetShadow.els;
466
- }
467
- updateGroup(els) {
468
- this.targetList = els;
469
- this.framesSnapShot = [];
470
- this.targetShadow.updateGroup(els);
471
- }
472
- setTargetList(targetList) {
473
- this.targetList = targetList;
474
- }
475
- clearMultiSelectStatus() {
476
- this.targetList = [];
477
- this.targetShadow.destroyEls();
478
- }
479
- onResizeGroupStart(e) {
480
- const { events } = e;
481
- this.moveableHelper.onResizeGroupStart(e);
482
- this.setFramesSnapShot(events);
483
- }
484
- /**
485
- * 多选状态下通过拖拽边框改变大小,所有选中组件会一起改变大小
486
- */
487
- onResizeGroup(e) {
488
- const { events } = e;
489
- this.moveableHelper.onResizeGroup(e);
490
- events.forEach((ev) => {
491
- const { width, height, beforeTranslate } = ev.drag;
492
- const frameSnapShot = this.framesSnapShot.find(
493
- (frameItem) => frameItem.id === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, "")
494
- );
495
- if (!frameSnapShot) return;
496
- const targeEl = this.targetList.find(
497
- (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(ev.target)?.replace(DRAG_EL_ID_PREFIX, "")
498
- );
499
- if (!targeEl) return;
500
- const isParentIncluded = this.targetList.find(
501
- (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement)
502
- );
503
- if (!isParentIncluded) {
504
- const { marginLeft, marginTop } = getMarginValue(targeEl);
505
- targeEl.style.left = `${frameSnapShot.left + beforeTranslate[0] - marginLeft}px`;
506
- targeEl.style.top = `${frameSnapShot.top + beforeTranslate[1] - marginTop}px`;
507
- }
508
- targeEl.style.width = `${width}px`;
509
- targeEl.style.height = `${height}px`;
510
- });
511
- }
512
- onDragGroupStart(e) {
513
- this.moveableHelper.onDragGroupStart(e);
514
- const { events } = e;
515
- this.setFramesSnapShot(events);
516
- }
517
- onDragGroup(e) {
518
- this.moveableHelper.onDragGroup(e);
519
- const { events } = e;
520
- events.forEach((ev) => {
521
- const frameSnapShot = this.framesSnapShot.find(
522
- (frameItem) => getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getIdFromEl()(ev.target)?.endsWith(frameItem.id)
523
- );
524
- if (!frameSnapShot) return;
525
- const findTargetElFuctin = (targetItem) => {
526
- const getId = getIdFromEl();
527
- const targetId = getId(ev.target);
528
- const targetItemId = getId(targetItem);
529
- return targetId?.startsWith(DRAG_EL_ID_PREFIX) && targetItemId && targetId?.endsWith(targetItemId);
530
- };
531
- const targeEl = this.targetList.find(findTargetElFuctin);
532
- if (!targeEl) return;
533
- const isParentIncluded = this.targetList.find(
534
- (targetItem) => getIdFromEl()(targetItem) === getIdFromEl()(targeEl.parentElement)
535
- );
536
- if (!isParentIncluded) {
537
- const { marginLeft, marginTop } = getMarginValue(targeEl);
538
- targeEl.style.left = `${frameSnapShot.left + ev.beforeTranslate[0] - marginLeft}px`;
539
- targeEl.style.top = `${frameSnapShot.top + ev.beforeTranslate[1] - marginTop}px`;
540
- }
541
- });
542
- }
543
- getUpdatedElRect(el, parentEl, doc) {
544
- const offset = this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: el.offsetLeft, top: el.offsetTop };
545
- const { marginLeft, marginTop } = getMarginValue(el);
546
- let left = calcValueByFontsize(doc, offset.left) - marginLeft;
547
- let top = calcValueByFontsize(doc, offset.top) - marginTop;
548
- const { borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidth } = getBorderWidth(el);
549
- const width = calcValueByFontsize(doc, el.clientWidth + borderLeftWidth + borderRightWidth);
550
- const height = calcValueByFontsize(doc, el.clientHeight + borderTopWidth + borderBottomWidth);
551
- let shadowEl = this.getShadowEl();
552
- const shadowEls = this.getShadowEls();
553
- if (shadowEls.length) {
554
- shadowEl = shadowEls.find((item) => getIdFromEl()(item)?.endsWith(getIdFromEl()(el) || ""));
555
- }
556
- if (parentEl && this.mode === Mode.ABSOLUTE && shadowEl) {
557
- const targetShadowHtmlEl = shadowEl;
558
- const targetShadowElOffsetLeft = targetShadowHtmlEl.offsetLeft || 0;
559
- const targetShadowElOffsetTop = targetShadowHtmlEl.offsetTop || 0;
560
- const frame = this.getFrame(shadowEl);
561
- const [translateX, translateY] = frame?.properties.transform.translate.value;
562
- const { left: parentLeft, top: parentTop } = getOffset(parentEl);
563
- left = calcValueByFontsize(doc, targetShadowElOffsetLeft) + parseFloat(translateX) - calcValueByFontsize(doc, parentLeft);
564
- top = calcValueByFontsize(doc, targetShadowElOffsetTop) + parseFloat(translateY) - calcValueByFontsize(doc, parentTop);
565
- }
566
- return { width, height, left, top };
567
- }
568
- /**
569
- * 多选状态设置多个节点的快照
570
- */
571
- setFramesSnapShot(events) {
572
- if (this.framesSnapShot.length > 0) return;
573
- events.forEach((ev) => {
574
- const matchEventTarget = this.targetList.find(
575
- (targetItem) => getIdFromEl()(ev.target)?.startsWith(DRAG_EL_ID_PREFIX) && getIdFromEl()(ev.target)?.endsWith(getIdFromEl()(targetItem) || "")
576
- );
577
- if (!matchEventTarget) return;
578
- const id = getIdFromEl()(matchEventTarget);
579
- id && this.framesSnapShot.push({
580
- left: matchEventTarget.offsetLeft,
581
- top: matchEventTarget.offsetTop,
582
- id
583
- });
584
- });
585
- }
586
- /**
587
- * 流式布局把目标节点复制一份进行拖拽,在拖拽结束前不影响页面原布局样式
588
- */
589
- generateGhostEl(el) {
590
- if (this.ghostEl) {
591
- this.destroyGhostEl();
592
- }
593
- const ghostEl = document.createElement("div");
594
- const { top, left } = getAbsolutePosition(el, getOffset(el));
595
- setIdToEl()(ghostEl, `${GHOST_EL_ID_PREFIX}${getIdFromEl()(el)}`);
596
- ghostEl.style.cssText = `
597
- z-index: ${ZIndex.GHOST_EL};
598
- opacity: .6;
599
- position: absolute;
600
- left: ${left}px;
601
- top: ${top}px;
602
- margin: 0;
603
- background: blue;
604
- width: ${el.clientWidth}px;
605
- height: ${el.clientHeight}px;
606
- `;
607
- el.after(ghostEl);
608
- return ghostEl;
609
- }
610
- }
611
-
612
- const ableCss = ".moveable-button {\n width: 20px;\n height: 20px;\n background: #4af;\n border-radius: 4px;\n appearance: none;\n border: 0;\n color: white;\n font-size: 12px;\n font-weight: bold;\n margin-left: 2px;\n position: relative;\n cursor: pointer;\n}\n.moveable-remove-button:before, .moveable-remove-button:after {\n content: \"\";\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%) rotate(45deg);\n width: 14px;\n height: 2px;\n background: #fff;\n border-radius: 1px;\n cursor: pointer;\n}\n.moveable-remove-button:after {\n transform: translate(-50%, -50%) rotate(-45deg);\n}\n\n.moveable-select-parent-arrow-top-icon {\n transform: rotateZ(-45deg);\n width: 4px;\n height: 4px;\n border-color: #fff;\n border-width: 2px 2px 0 0;\n border-style: solid;\n position: absolute;\n left: 4px;\n top: 4px;\n}\n\n.moveable-select-parent-arrow-body-icon {\n width: 7px;\n height: 11px;\n border-color: #fff;\n border-width: 0 0 2px 2px;\n border-style: solid;\n}\n\n.moveable-drag-area-button {\n cursor: move;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon {\n width: 2px;\n height: 2px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-top {\n transform: rotateZ(-45deg) translateX(-50%);\n left: 50%;\n top: 3px;\n transform-origin: left;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-bottom {\n transform: rotateZ(135deg) translateX(-50%);\n transform-origin: left;\n left: 50%;\n top: auto;\n bottom: 3px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-right {\n transform: rotateZ(45deg) translateY(-50%);\n transform-origin: top;\n right: 3px;\n left: auto;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-top-icon-left {\n transform: rotateZ(235deg) translateY(-50%);\n transform-origin: top;\n left: 3px;\n top: 50%;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-horizontal {\n width: 2px;\n height: 11px;\n background-color: #fff;\n position: absolute;\n transform: translateX(-50%);\n left: 50%;\n top: 4px;\n}\n\n.moveable-drag-area-button .moveable-select-parent-arrow-body-icon-vertical {\n width: 11px;\n height: 2px;\n background-color: #fff;\n position: absolute;\n transform: translateY(-50%);\n left: 4px;\n top: 50%;;\n}\n\n.moveable-rerender-button img {\n position: absolute;\n left: 2px;\n top: 2px;\n}\n";
613
-
614
- const MoveableActionsAble = (handler, customizedButton = []) => ({
615
- name: "actions",
616
- props: [],
617
- always: true,
618
- events: [],
619
- render(moveable, React) {
620
- const rect = moveable.getRect();
621
- const { pos2 } = moveable.state;
622
- const editableViewer = moveable.useCSS(
623
- "div",
624
- `
625
- {
626
- position: absolute;
627
- left: 0px;
628
- top: 0px;
629
- will-change: transform;
630
- transform-origin: 60px 28px;
631
- display: flex;
632
- }
633
- ${ableCss}
634
- `
635
- );
636
- return React.createElement(
637
- editableViewer,
638
- {
639
- className: "moveable-editable",
640
- style: {
641
- transform: `translate(${pos2[0] - (customizedButton.length + 3) * 20}px, ${pos2[1] - 28}px) rotate(${rect.rotation}deg)`
642
- }
643
- },
644
- [
645
- ...customizedButton.map((buttonRenderer) => {
646
- const options = buttonRenderer(React);
647
- return React.createElement("button", options.props || {}, ...options.children || []);
648
- }),
649
- React.createElement(
650
- "button",
651
- {
652
- className: "moveable-button moveable-rerender-button",
653
- title: "重新收集依赖后渲染",
654
- onClick: () => {
655
- handler(AbleActionEventType.RERENDER);
656
- }
657
- },
658
- React.createElement("img", {
659
- src: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJpY29uIGljb24tdGFibGVyIGljb24tdGFibGVyLXJlcGxhY2UiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZT0iI2ZmZmZmZiIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KICA8cGF0aCBzdHJva2U9Im5vbmUiIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz4KICA8cmVjdCB4PSIzIiB5PSIzIiB3aWR0aD0iNiIgaGVpZ2h0PSI2IiByeD0iMSIgLz4KICA8cmVjdCB4PSIxNSIgeT0iMTUiIHdpZHRoPSI2IiBoZWlnaHQ9IjYiIHJ4PSIxIiAvPgogIDxwYXRoIGQ9Ik0yMSAxMXYtM2EyIDIgMCAwIDAgLTIgLTJoLTZsMyAzbTAgLTZsLTMgMyIgLz4KICA8cGF0aCBkPSJNMyAxM3YzYTIgMiAwIDAgMCAyIDJoNmwtMyAtM20wIDZsMyAtMyIgLz4KPC9zdmc+CgoK",
660
- width: "16",
661
- height: "16"
662
- })
663
- ),
664
- React.createElement(
665
- "button",
666
- {
667
- className: "moveable-button",
668
- title: "选中父组件",
669
- onClick: () => {
670
- handler(AbleActionEventType.SELECT_PARENT);
671
- }
672
- },
673
- React.createElement("div", {
674
- className: "moveable-select-parent-arrow-top-icon"
675
- }),
676
- React.createElement("div", {
677
- className: "moveable-select-parent-arrow-body-icon"
678
- })
679
- ),
680
- React.createElement("button", {
681
- className: "moveable-button moveable-remove-button",
682
- title: "删除",
683
- onClick: () => {
684
- handler(AbleActionEventType.REMOVE);
685
- }
686
- }),
687
- React.createElement(
688
- "button",
689
- {
690
- className: "moveable-button moveable-drag-area-button",
691
- title: "拖动"
692
- },
693
- React.createElement("div", {
694
- className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-top"
695
- }),
696
- React.createElement("div", {
697
- className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-bottom"
698
- }),
699
- React.createElement("div", {
700
- className: "moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-left"
701
- }),
702
- React.createElement("div", {
703
- className: " moveable-select-parent-arrow-top-icon moveable-select-parent-arrow-top-icon-right"
704
- }),
705
- React.createElement("div", {
706
- className: "moveable-select-parent-arrow-body-icon-horizontal"
707
- }),
708
- React.createElement("div", {
709
- className: "moveable-select-parent-arrow-body-icon-vertical"
710
- })
711
- )
712
- ]
713
- );
714
- }
715
- });
716
-
717
- class MoveableOptionsManager extends EventEmitter {
718
- /** 布局方式:流式布局、绝对定位、固定定位 */
719
- mode = Mode.ABSOLUTE;
720
- /** 画布容器 */
721
- container;
722
- options = {};
723
- /** 水平参考线 */
724
- horizontalGuidelines = [];
725
- /** 垂直参考线 */
726
- verticalGuidelines = [];
727
- /** 对齐元素集合 */
728
- elementGuidelines = [];
729
- /** 由外部调用方(编辑器)传入进来的moveable默认参数,可以为空,也可以是一个回调函数 */
730
- customizedOptions;
731
- /** 获取整个画布的根元素(在StageCore的mount函数中挂载的container) */
732
- getRootContainer;
733
- constructor(config) {
734
- super();
735
- this.customizedOptions = config.moveableOptions;
736
- this.container = config.container;
737
- this.getRootContainer = config.getRootContainer;
738
- }
739
- getOption(key) {
740
- return this.options[key];
741
- }
742
- /**
743
- * 设置水平/垂直参考线
744
- * @param type 参考线类型
745
- * @param guidelines 参考线坐标数组
746
- */
747
- setGuidelines(type, guidelines) {
748
- if (type === GuidesType.HORIZONTAL) {
749
- this.horizontalGuidelines = guidelines;
750
- } else if (type === GuidesType.VERTICAL) {
751
- this.verticalGuidelines = guidelines;
752
- }
753
- this.emit("update-moveable");
754
- }
755
- /**
756
- * 清除横向和纵向的参考线
757
- */
758
- clearGuides() {
759
- this.horizontalGuidelines = [];
760
- this.verticalGuidelines = [];
761
- this.emit("update-moveable");
762
- }
763
- /**
764
- * 设置有哪些元素要辅助对齐
765
- * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
766
- */
767
- setElementGuidelines(selectedElList) {
768
- this.elementGuidelines.forEach((node) => {
769
- node.remove();
770
- });
771
- this.elementGuidelines = [];
772
- const elementGuidelines = this.getCustomizeOptions()?.elementGuidelines || Array.from(selectedElList[0]?.parentElement?.children || []);
773
- if (this.mode === Mode.ABSOLUTE) {
774
- this.container.append(this.createGuidelineElements(selectedElList, elementGuidelines));
775
- }
776
- }
777
- /**
778
- * 获取moveable参数
779
- * @param isMultiSelect 是否多选模式
780
- * @param runtimeOptions 调用时实时传进来的的moveable参数
781
- * @returns moveable所需参数
782
- */
783
- getOptions(isMultiSelect, runtimeOptions = {}) {
784
- const defaultOptions = this.getDefaultOptions(isMultiSelect);
785
- const customizedOptions = this.getCustomizeOptions() || {};
786
- this.options = merge(defaultOptions, customizedOptions, runtimeOptions);
787
- return this.options;
788
- }
789
- /**
790
- * 获取单选和多选的moveable公共参数
791
- * @returns moveable公共参数
792
- */
793
- getDefaultOptions(isMultiSelect) {
794
- const isSortable = this.mode === Mode.SORTABLE;
795
- const commonOptions = {
796
- draggable: true,
797
- resizable: true,
798
- rootContainer: this.getRootContainer(),
799
- zoom: 1,
800
- throttleDrag: 0,
801
- snappable: true,
802
- horizontalGuidelines: this.horizontalGuidelines,
803
- verticalGuidelines: this.verticalGuidelines,
804
- elementGuidelines: this.elementGuidelines,
805
- bounds: {
806
- top: 0,
807
- left: 0,
808
- right: this.container.clientWidth,
809
- bottom: isSortable ? void 0 : this.container.clientHeight
810
- }
811
- };
812
- const differenceOptions = isMultiSelect ? this.getMultiOptions() : this.getSingleOptions();
813
- return merge(commonOptions, differenceOptions);
814
- }
815
- /**
816
- * 获取单选下的差异化参数
817
- * @returns {MoveableOptions} moveable options参数
818
- */
819
- getSingleOptions() {
820
- const isAbsolute = this.mode === Mode.ABSOLUTE;
821
- const isFixed = this.mode === Mode.FIXED;
822
- return {
823
- origin: false,
824
- dragArea: false,
825
- scalable: false,
826
- rotatable: false,
827
- snapGap: isAbsolute || isFixed,
828
- snapThreshold: 5,
829
- snapDigit: 0,
830
- isDisplaySnapDigit: isAbsolute,
831
- snapDirections: {
832
- top: isAbsolute,
833
- right: isAbsolute,
834
- bottom: isAbsolute,
835
- left: isAbsolute,
836
- center: isAbsolute,
837
- middle: isAbsolute
838
- },
839
- elementSnapDirections: {
840
- top: isAbsolute,
841
- right: isAbsolute,
842
- bottom: isAbsolute,
843
- left: isAbsolute
844
- },
845
- isDisplayInnerSnapDigit: true,
846
- dragTarget: ".moveable-drag-area-button",
847
- dragTargetSelf: true,
848
- props: {
849
- actions: true
850
- },
851
- ables: [MoveableActionsAble(this.actionHandler.bind(this))]
852
- };
853
- }
854
- /**
855
- * 获取多选下的差异化参数
856
- * @returns {MoveableOptions} moveable options参数
857
- */
858
- getMultiOptions() {
859
- return {
860
- defaultGroupRotate: 0,
861
- defaultGroupOrigin: "50% 50%",
862
- startDragRotate: 0,
863
- throttleDragRotate: 0,
864
- origin: true,
865
- padding: { left: 0, top: 0, right: 0, bottom: 0 }
866
- };
867
- }
868
- /**
869
- * 获取业务方自定义的moveable参数
870
- */
871
- getCustomizeOptions() {
872
- if (typeof this.customizedOptions === "function") {
873
- return this.customizedOptions();
874
- }
875
- return this.customizedOptions;
876
- }
877
- /**
878
- * 这是给selectParentAbles的回调函数,用于触发选中父元素事件
879
- */
880
- actionHandler(type) {
881
- this.emit(type);
882
- }
883
- /**
884
- * 为需要辅助对齐的元素创建div
885
- * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
886
- * @param allElList 全部元素列表
887
- * @returns frame 辅助对齐元素集合的页面片
888
- */
889
- createGuidelineElements(selectedElList, allElList) {
890
- const frame = globalThis.document.createDocumentFragment();
891
- for (const element of allElList) {
892
- let node = element.element || element;
893
- if (!node || typeof node === "string") continue;
894
- if (typeof node === "function") {
895
- node = node();
896
- }
897
- if (this.isInElementList(node, selectedElList)) continue;
898
- const { width, height } = node.getBoundingClientRect();
899
- if (!width || !height) continue;
900
- const { left, top } = getOffset(node);
901
- const elementGuideline = globalThis.document.createElement("div");
902
- elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
903
- this.elementGuidelines.push(elementGuideline);
904
- frame.append(elementGuideline);
905
- }
906
- return frame;
907
- }
908
- /**
909
- * 判断一个元素是否在元素列表里面
910
- * @param ele 元素
911
- * @param eleList 元素列表
912
- * @returns 是否在元素列表里面
913
- */
914
- isInElementList(ele, eleList) {
915
- for (const eleItem of eleList) {
916
- if (ele === eleItem) return true;
917
- }
918
- return false;
919
- }
920
- }
921
-
922
- class StageDragResize extends MoveableOptionsManager {
923
- /** 目标节点 */
924
- target = null;
925
- /** Moveable拖拽类实例 */
926
- moveable;
927
- /** 拖动状态 */
928
- dragStatus = StageDragStatus.END;
929
- dragResizeHelper;
930
- disabledDragStart;
931
- getRenderDocument;
932
- markContainerEnd;
933
- delayedMarkContainer;
934
- constructor(config) {
935
- super(config);
936
- this.getRenderDocument = config.getRenderDocument;
937
- this.markContainerEnd = config.markContainerEnd;
938
- this.delayedMarkContainer = config.delayedMarkContainer;
939
- this.disabledDragStart = config.disabledDragStart;
940
- this.dragResizeHelper = config.dragResizeHelper;
941
- this.on("update-moveable", () => {
942
- if (this.moveable) {
943
- this.updateMoveable();
944
- }
945
- });
946
- }
947
- getTarget() {
948
- return this.target;
949
- }
950
- /**
951
- * 将选中框渲染并覆盖到选中的组件Dom节点上方
952
- * 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标
953
- * @param el 选中组件的Dom节点元素
954
- * @param event 鼠标事件
955
- */
956
- select(el, event) {
957
- if (!el) {
958
- this.moveable?.destroy();
959
- this.moveable = void 0;
960
- return;
961
- }
962
- if (!this.moveable || el !== this.target) {
963
- this.initMoveable(el);
964
- } else {
965
- this.updateMoveable(el);
966
- }
967
- if (event && !this.disabledDragStart) {
968
- this.moveable?.dragStart(event);
969
- }
970
- }
971
- /**
972
- * 初始化选中框并渲染出来
973
- */
974
- updateMoveable(el = this.target) {
975
- if (!this.moveable) return;
976
- if (!el) throw new Error("未选中任何节点");
977
- const options = this.init(el);
978
- Object.entries(options).forEach(([key, value]) => {
979
- this.moveable[key] = value;
980
- });
981
- this.moveable.updateRect();
982
- }
983
- clearSelectStatus() {
984
- if (!this.moveable) return;
985
- this.dragResizeHelper.destroyShadowEl();
986
- this.moveable.target = null;
987
- this.moveable.updateRect();
988
- }
989
- getDragStatus() {
990
- return this.dragStatus;
991
- }
992
- /**
993
- * 销毁实例
994
- */
995
- destroy() {
996
- this.target = null;
997
- this.moveable?.destroy();
998
- this.dragResizeHelper.destroy();
999
- this.dragStatus = StageDragStatus.END;
1000
- this.removeAllListeners();
1001
- }
1002
- on(eventName, listener) {
1003
- return super.on(eventName, listener);
1004
- }
1005
- emit(eventName, ...args) {
1006
- return super.emit(eventName, ...args);
1007
- }
1008
- init(el) {
1009
- if (/(auto|scroll)/.test(el.style.overflow)) {
1010
- el.style.overflow = "hidden";
1011
- }
1012
- this.target = el;
1013
- this.mode = getMode(el);
1014
- this.dragResizeHelper.updateShadowEl(el);
1015
- this.dragResizeHelper.setMode(this.mode);
1016
- this.setElementGuidelines([this.target]);
1017
- return this.getOptions(false, {
1018
- target: this.dragResizeHelper.getShadowEl()
1019
- });
1020
- }
1021
- initMoveable(el) {
1022
- const options = this.init(el);
1023
- this.dragResizeHelper.clear();
1024
- this.moveable?.destroy();
1025
- this.moveable = new Moveable__default(this.container, {
1026
- ...options
1027
- });
1028
- this.bindResizeEvent();
1029
- this.bindDragEvent();
1030
- this.bindRotateEvent();
1031
- this.bindScaleEvent();
1032
- }
1033
- bindResizeEvent() {
1034
- if (!this.moveable) throw new Error("moveable 未初始化");
1035
- this.moveable.on("resizeStart", (e) => {
1036
- if (!this.target) return;
1037
- this.dragStatus = StageDragStatus.START;
1038
- this.dragResizeHelper.onResizeStart(e);
1039
- }).on("resize", (e) => {
1040
- if (!this.moveable || !this.target || !this.dragResizeHelper.getShadowEl()) return;
1041
- this.dragStatus = StageDragStatus.ING;
1042
- this.dragResizeHelper.onResize(e);
1043
- }).on("resizeEnd", () => {
1044
- this.dragStatus = StageDragStatus.END;
1045
- this.update(true);
1046
- });
1047
- }
1048
- bindDragEvent() {
1049
- if (!this.moveable) throw new Error("moveable 未初始化");
1050
- let timeout;
1051
- this.moveable.on("dragStart", (e) => {
1052
- if (!this.target) throw new Error("未选中组件");
1053
- this.dragStatus = StageDragStatus.START;
1054
- this.dragResizeHelper.onDragStart(e);
1055
- this.emit("drag-start", e);
1056
- }).on("drag", (e) => {
1057
- if (!this.target || !this.dragResizeHelper.getShadowEl()) return;
1058
- if (timeout) {
1059
- globalThis.clearTimeout(timeout);
1060
- timeout = void 0;
1061
- }
1062
- timeout = this.delayedMarkContainer(e.inputEvent, [this.target]);
1063
- this.dragStatus = StageDragStatus.ING;
1064
- this.dragResizeHelper.onDrag(e);
1065
- }).on("dragEnd", () => {
1066
- if (timeout) {
1067
- globalThis.clearTimeout(timeout);
1068
- timeout = void 0;
1069
- }
1070
- const parentEl = this.markContainerEnd();
1071
- if (this.dragStatus === StageDragStatus.ING) {
1072
- if (parentEl) {
1073
- this.update(false, parentEl);
1074
- } else {
1075
- switch (this.mode) {
1076
- case Mode.SORTABLE:
1077
- this.sort();
1078
- break;
1079
- default:
1080
- this.update();
1081
- }
1082
- }
1083
- }
1084
- this.dragStatus = StageDragStatus.END;
1085
- this.dragResizeHelper.destroyGhostEl();
1086
- });
1087
- }
1088
- bindRotateEvent() {
1089
- if (!this.moveable) throw new Error("moveable 未初始化");
1090
- this.moveable.on("rotateStart", (e) => {
1091
- this.dragStatus = StageDragStatus.START;
1092
- this.dragResizeHelper.onRotateStart(e);
1093
- }).on("rotate", (e) => {
1094
- if (!this.target || !this.dragResizeHelper.getShadowEl()) return;
1095
- this.dragStatus = StageDragStatus.ING;
1096
- this.dragResizeHelper.onRotate(e);
1097
- }).on("rotateEnd", (e) => {
1098
- this.dragStatus = StageDragStatus.END;
1099
- const frame = this.dragResizeHelper?.getFrame(e.target);
1100
- if (this.target && frame) {
1101
- this.emit("update", {
1102
- data: [
1103
- {
1104
- el: this.target,
1105
- style: {
1106
- transform: frame.get("transform")
1107
- }
1108
- }
1109
- ],
1110
- parentEl: null
1111
- });
1112
- }
1113
- });
1114
- }
1115
- bindScaleEvent() {
1116
- if (!this.moveable) throw new Error("moveable 未初始化");
1117
- this.moveable.on("scaleStart", (e) => {
1118
- this.dragStatus = StageDragStatus.START;
1119
- this.dragResizeHelper.onScaleStart(e);
1120
- }).on("scale", (e) => {
1121
- if (!this.target || !this.dragResizeHelper.getShadowEl()) return;
1122
- this.dragStatus = StageDragStatus.ING;
1123
- this.dragResizeHelper.onScale(e);
1124
- }).on("scaleEnd", (e) => {
1125
- this.dragStatus = StageDragStatus.END;
1126
- const frame = this.dragResizeHelper.getFrame(e.target);
1127
- if (this.target && frame) {
1128
- this.emit("update", {
1129
- data: [
1130
- {
1131
- el: this.target,
1132
- style: {
1133
- transform: frame.get("transform")
1134
- }
1135
- }
1136
- ],
1137
- parentEl: null
1138
- });
1139
- }
1140
- });
1141
- }
1142
- sort() {
1143
- if (!this.target || !this.dragResizeHelper.getGhostEl()) throw new Error("未知错误");
1144
- const { top } = this.dragResizeHelper.getGhostEl().getBoundingClientRect();
1145
- const { top: oriTop } = this.target.getBoundingClientRect();
1146
- const deltaTop = top - oriTop;
1147
- if (Math.abs(deltaTop) >= this.target.clientHeight / 2) {
1148
- if (deltaTop > 0) {
1149
- this.emit("sort", down(deltaTop, this.target));
1150
- } else {
1151
- this.emit("sort", up(deltaTop, this.target));
1152
- }
1153
- } else {
1154
- const id = getIdFromEl()(this.target);
1155
- id && this.emit("sort", {
1156
- src: id,
1157
- dist: id
1158
- });
1159
- }
1160
- }
1161
- update(isResize = false, parentEl = null) {
1162
- if (!this.target) return;
1163
- const doc = this.getRenderDocument();
1164
- if (!doc) return;
1165
- const rect = this.dragResizeHelper.getUpdatedElRect(this.target, parentEl, doc);
1166
- this.emit("update", {
1167
- data: [
1168
- {
1169
- el: this.target,
1170
- style: isResize ? rect : { left: rect.left, top: rect.top }
1171
- }
1172
- ],
1173
- parentEl
1174
- });
1175
- }
1176
- }
1177
-
1178
- class StageHighlight extends EventEmitter$1 {
1179
- container;
1180
- target;
1181
- moveable;
1182
- targetShadow;
1183
- getRootContainer;
1184
- constructor(config) {
1185
- super();
1186
- this.container = config.container;
1187
- this.getRootContainer = config.getRootContainer;
1188
- this.targetShadow = new TargetShadow({
1189
- container: config.container,
1190
- updateDragEl: config.updateDragEl,
1191
- zIndex: ZIndex.HIGHLIGHT_EL,
1192
- idPrefix: HIGHLIGHT_EL_ID_PREFIX
1193
- });
1194
- }
1195
- /**
1196
- * 高亮鼠标悬停的组件
1197
- * @param el 选中组件的Dom节点元素
1198
- */
1199
- highlight(el) {
1200
- if (!el || el === this.target) return;
1201
- this.target = el;
1202
- this.targetShadow?.update(el);
1203
- if (this.moveable) {
1204
- this.moveable.zoom = 2;
1205
- this.moveable.updateRect();
1206
- } else {
1207
- this.moveable = new Moveable__default(this.container, {
1208
- target: this.targetShadow?.el,
1209
- origin: false,
1210
- rootContainer: this.getRootContainer(),
1211
- zoom: 2
1212
- });
1213
- }
1214
- }
1215
- /**
1216
- * 清空高亮
1217
- */
1218
- clearHighlight() {
1219
- if (!this.moveable || !this.target) return;
1220
- this.moveable.zoom = 0;
1221
- this.moveable.updateRect();
1222
- this.target = void 0;
1223
- }
1224
- /**
1225
- * 销毁实例
1226
- */
1227
- destroy() {
1228
- this.target = void 0;
1229
- this.moveable?.destroy();
1230
- this.targetShadow?.destroy();
1231
- this.moveable = void 0;
1232
- this.targetShadow = void 0;
1233
- }
1234
- }
1235
-
1236
- class StageMultiDragResize extends MoveableOptionsManager {
1237
- /** 画布容器 */
1238
- container;
1239
- /** 多选:目标节点组 */
1240
- targetList = [];
1241
- /** Moveable多选拖拽类实例 */
1242
- moveableForMulti;
1243
- dragStatus = StageDragStatus.END;
1244
- dragResizeHelper;
1245
- getRenderDocument;
1246
- delayedMarkContainer;
1247
- markContainerEnd;
1248
- constructor(config) {
1249
- const moveableOptionsManagerConfig = {
1250
- container: config.container,
1251
- moveableOptions: config.moveableOptions,
1252
- getRootContainer: config.getRootContainer
1253
- };
1254
- super(moveableOptionsManagerConfig);
1255
- this.delayedMarkContainer = config.delayedMarkContainer;
1256
- this.markContainerEnd = config.markContainerEnd;
1257
- this.container = config.container;
1258
- this.getRenderDocument = config.getRenderDocument;
1259
- this.dragResizeHelper = config.dragResizeHelper;
1260
- this.on("update-moveable", () => {
1261
- if (this.moveableForMulti) {
1262
- this.updateMoveable();
1263
- }
1264
- });
1265
- }
1266
- /**
1267
- * 多选
1268
- * @param els
1269
- */
1270
- multiSelect(els) {
1271
- if (els.length === 0) {
1272
- return;
1273
- }
1274
- this.mode = getMode(els[0]);
1275
- this.targetList = els;
1276
- this.dragResizeHelper.updateGroup(els);
1277
- this.setElementGuidelines(this.targetList);
1278
- this.moveableForMulti?.destroy();
1279
- this.dragResizeHelper.clear();
1280
- this.moveableForMulti = new Moveable__default(
1281
- this.container,
1282
- this.getOptions(true, {
1283
- target: this.dragResizeHelper.getShadowEls()
1284
- })
1285
- );
1286
- let timeout;
1287
- this.moveableForMulti.on("resizeGroupStart", (e) => {
1288
- this.dragResizeHelper.onResizeGroupStart(e);
1289
- this.dragStatus = StageDragStatus.START;
1290
- }).on("resizeGroup", (e) => {
1291
- this.dragResizeHelper.onResizeGroup(e);
1292
- this.dragStatus = StageDragStatus.ING;
1293
- }).on("resizeGroupEnd", () => {
1294
- this.update(true);
1295
- this.dragStatus = StageDragStatus.END;
1296
- }).on("dragGroupStart", (e) => {
1297
- this.dragResizeHelper.onDragGroupStart(e);
1298
- this.dragStatus = StageDragStatus.START;
1299
- }).on("dragGroup", (e) => {
1300
- if (timeout) {
1301
- globalThis.clearTimeout(timeout);
1302
- timeout = void 0;
1303
- }
1304
- timeout = this.delayedMarkContainer(e.inputEvent, this.targetList);
1305
- this.dragResizeHelper.onDragGroup(e);
1306
- this.dragStatus = StageDragStatus.ING;
1307
- }).on("dragGroupEnd", () => {
1308
- if (timeout) {
1309
- globalThis.clearTimeout(timeout);
1310
- timeout = void 0;
1311
- }
1312
- const parentEl = this.markContainerEnd();
1313
- this.update(false, parentEl);
1314
- this.dragStatus = StageDragStatus.END;
1315
- }).on("clickGroup", (e) => {
1316
- const { inputTarget, targets } = e;
1317
- if (targets.length > 1 && targets.includes(inputTarget)) {
1318
- const id = getIdFromEl()(inputTarget)?.replace(DRAG_EL_ID_PREFIX, "");
1319
- id && this.emit("change-to-select", id, e.inputEvent);
1320
- }
1321
- });
1322
- }
1323
- canSelect(el, selectedEl) {
1324
- const currentTargetMode = getMode(el);
1325
- let selectedElMode = "";
1326
- if (currentTargetMode === Mode.SORTABLE) {
1327
- return false;
1328
- }
1329
- if (this.targetList.length === 0 && selectedEl) {
1330
- selectedElMode = getMode(selectedEl);
1331
- } else if (this.targetList.length > 0) {
1332
- selectedElMode = getMode(this.targetList[0]);
1333
- }
1334
- if (currentTargetMode !== selectedElMode) {
1335
- return false;
1336
- }
1337
- return true;
1338
- }
1339
- updateMoveable(eleList = this.targetList) {
1340
- if (!this.moveableForMulti) return;
1341
- if (!eleList) throw new Error("未选中任何节点");
1342
- this.targetList = eleList;
1343
- this.dragResizeHelper.setTargetList(eleList);
1344
- const options = this.getOptions(true, {
1345
- target: this.dragResizeHelper.getShadowEls()
1346
- });
1347
- Object.entries(options).forEach(([key, value]) => {
1348
- this.moveableForMulti[key] = value;
1349
- });
1350
- this.moveableForMulti.updateRect();
1351
- }
1352
- /**
1353
- * 清除多选状态
1354
- */
1355
- clearSelectStatus() {
1356
- if (!this.moveableForMulti) return;
1357
- this.dragResizeHelper.clearMultiSelectStatus();
1358
- this.moveableForMulti.target = null;
1359
- this.moveableForMulti.updateTarget();
1360
- this.targetList = [];
1361
- }
1362
- /**
1363
- * 销毁实例
1364
- */
1365
- destroy() {
1366
- this.moveableForMulti?.destroy();
1367
- this.dragResizeHelper.destroy();
1368
- }
1369
- on(eventName, listener) {
1370
- return super.on(eventName, listener);
1371
- }
1372
- emit(eventName, ...args) {
1373
- return super.emit(eventName, ...args);
1374
- }
1375
- /**
1376
- * 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
1377
- * @param isResize 是否进行大小缩放
1378
- */
1379
- update(isResize = false, parentEl = null) {
1380
- if (this.targetList.length === 0) return;
1381
- const doc = this.getRenderDocument();
1382
- if (!doc) return;
1383
- const data = this.targetList.map((targetItem) => {
1384
- const rect = this.dragResizeHelper.getUpdatedElRect(targetItem, parentEl, doc);
1385
- return {
1386
- el: targetItem,
1387
- style: isResize ? rect : { left: rect.left, top: rect.top }
1388
- };
1389
- });
1390
- this.emit("update", { data, parentEl });
1391
- }
1392
- }
1393
-
1394
- const throttleTime = 100;
1395
- const defaultContainerHighlightDuration = 800;
1396
- class ActionManager extends EventEmitter {
1397
- dr = null;
1398
- multiDr = null;
1399
- highlightLayer = null;
1400
- /** 单选、多选、高亮的容器(蒙层的content) */
1401
- container;
1402
- /** 当前选中的节点 */
1403
- selectedEl = null;
1404
- /** 多选选中的节点组 */
1405
- selectedElList = [];
1406
- /** 当前高亮的节点 */
1407
- highlightedEl;
1408
- /** 当前是否处于多选状态 */
1409
- isMultiSelectStatus = false;
1410
- /** 当拖拽组件到容器上方进入可加入容器状态时,给容器添加的一个class名称 */
1411
- containerHighlightClassName;
1412
- /** 当拖拽组件到容器上方时,需要悬停多久才能将组件加入容器 */
1413
- containerHighlightDuration;
1414
- /** 将组件加入容器的操作方式 */
1415
- containerHighlightType;
1416
- isAltKeydown = false;
1417
- getTargetElement;
1418
- getElementsFromPoint;
1419
- canSelect;
1420
- isContainer;
1421
- getRenderDocument;
1422
- disabledMultiSelect = false;
1423
- config;
1424
- mouseMoveHandler = throttle((event) => {
1425
- const handler = async () => {
1426
- if (event.target?.classList?.contains("moveable-direction")) {
1427
- return;
1428
- }
1429
- const el = await this.getElementFromPoint(event);
1430
- const id = getIdFromEl()(el);
1431
- if (!id) {
1432
- this.clearHighlight();
1433
- return;
1434
- }
1435
- this.emit("mousemove", event);
1436
- this.highlight(id);
1437
- };
1438
- handler();
1439
- }, throttleTime);
1440
- constructor(config) {
1441
- super();
1442
- this.config = config;
1443
- this.container = config.container;
1444
- this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS_NAME;
1445
- this.containerHighlightDuration = config.containerHighlightDuration || defaultContainerHighlightDuration;
1446
- this.containerHighlightType = config.containerHighlightType;
1447
- this.disabledMultiSelect = config.disabledMultiSelect ?? false;
1448
- this.getTargetElement = config.getTargetElement;
1449
- this.getElementsFromPoint = config.getElementsFromPoint;
1450
- this.canSelect = config.canSelect || ((el) => Boolean(getIdFromEl()(el)));
1451
- this.getRenderDocument = config.getRenderDocument;
1452
- this.isContainer = config.isContainer;
1453
- this.dr = this.createDr(config);
1454
- if (!this.disabledMultiSelect) {
1455
- this.multiDr = this.createMultiDr(config);
1456
- }
1457
- this.highlightLayer = new StageHighlight({
1458
- container: config.container,
1459
- updateDragEl: config.updateDragEl,
1460
- getRootContainer: config.getRootContainer
1461
- });
1462
- this.initMouseEvent();
1463
- this.initKeyEvent();
1464
- }
1465
- disableMultiSelect() {
1466
- this.disabledMultiSelect = true;
1467
- if (this.multiDr) {
1468
- this.multiDr.destroy();
1469
- this.multiDr = null;
1470
- }
1471
- }
1472
- enableMultiSelect() {
1473
- this.disabledMultiSelect = false;
1474
- if (!this.multiDr) {
1475
- this.multiDr = this.createMultiDr(this.config);
1476
- }
1477
- }
1478
- /**
1479
- * 设置水平/垂直参考线
1480
- * @param type 参考线类型
1481
- * @param guidelines 参考线坐标数组
1482
- */
1483
- setGuidelines(type, guidelines) {
1484
- this.dr?.setGuidelines(type, guidelines);
1485
- this.multiDr?.setGuidelines(type, guidelines);
1486
- }
1487
- /**
1488
- * 清空所有参考线
1489
- */
1490
- clearGuides() {
1491
- this.dr?.clearGuides();
1492
- this.multiDr?.clearGuides();
1493
- }
1494
- /**
1495
- * 更新moveable,外部主要调用场景是元素配置变更、页面大小变更
1496
- * @param el 变更的元素
1497
- */
1498
- updateMoveable(el) {
1499
- this.dr?.updateMoveable(el);
1500
- this.multiDr?.updateMoveable();
1501
- }
1502
- /**
1503
- * 判断是否单选选中的元素
1504
- */
1505
- isSelectedEl(el) {
1506
- return getIdFromEl()(el) === getIdFromEl()(this.selectedEl);
1507
- }
1508
- setSelectedEl(el) {
1509
- this.selectedEl = el;
1510
- }
1511
- getSelectedEl() {
1512
- return this.selectedEl;
1513
- }
1514
- getSelectedElList() {
1515
- return this.selectedElList;
1516
- }
1517
- getMoveableOption(key) {
1518
- if (this.dr?.getTarget()) {
1519
- return this.dr.getOption(key);
1520
- }
1521
- if (this.multiDr?.targetList.length) {
1522
- return this.multiDr.getOption(key);
1523
- }
1524
- }
1525
- /**
1526
- * 获取鼠标下方第一个可选中元素,如果元素层叠,返回到是最上层元素
1527
- * @param event 鼠标事件
1528
- * @returns 鼠标下方第一个可选中元素
1529
- */
1530
- async getElementFromPoint(event) {
1531
- const els = this.getElementsFromPoint(event);
1532
- this.emit("get-elements-from-point", els);
1533
- let stopped = false;
1534
- const stop = () => stopped = true;
1535
- for (const el of els) {
1536
- if (!getIdFromEl()(el)?.startsWith(GHOST_EL_ID_PREFIX) && await this.isElCanSelect(el, event, stop)) {
1537
- if (stopped) break;
1538
- return el;
1539
- }
1540
- }
1541
- return null;
1542
- }
1543
- /**
1544
- * 判断一个元素能否在当前场景被选中
1545
- * @param el 被判断的元素
1546
- * @param event 鼠标事件
1547
- * @param stop 通过该元素如果得知剩下的元素都不可被选中,通知调用方终止对剩下元素的判断
1548
- * @returns 能否选中
1549
- */
1550
- async isElCanSelect(el, event, stop) {
1551
- const canSelectByProp = await this.canSelect(el, event, stop);
1552
- if (!canSelectByProp) return false;
1553
- if (this.isMultiSelectStatus) {
1554
- return this.canMultiSelect(el, stop);
1555
- }
1556
- return true;
1557
- }
1558
- /**
1559
- * 判断一个元素是否可以被多选,如果当前元素是page,则调stop函数告诉调用方不必继续判断其它元素了
1560
- */
1561
- canMultiSelect(el, stop) {
1562
- if (el.className.includes(PAGE_CLASS)) {
1563
- stop();
1564
- return false;
1565
- }
1566
- const selectedEl = this.getSelectedEl();
1567
- if (selectedEl?.className.includes(PAGE_CLASS)) {
1568
- return true;
1569
- }
1570
- return this.multiDr?.canSelect(el, selectedEl) || false;
1571
- }
1572
- select(el, event) {
1573
- this.setSelectedEl(el);
1574
- this.clearSelectStatus(SelectStatus.MULTI_SELECT);
1575
- this.dr?.select(el, event);
1576
- }
1577
- multiSelect(ids) {
1578
- this.selectedElList = [];
1579
- ids.forEach((id) => {
1580
- const el = this.getTargetElement(id);
1581
- if (el) {
1582
- this.selectedElList.push(el);
1583
- }
1584
- });
1585
- this.clearSelectStatus(SelectStatus.SELECT);
1586
- this.multiDr?.multiSelect(this.selectedElList);
1587
- }
1588
- getHighlightEl() {
1589
- return this.highlightedEl;
1590
- }
1591
- setHighlightEl(el) {
1592
- this.highlightedEl = el;
1593
- }
1594
- highlight(id) {
1595
- let el;
1596
- try {
1597
- el = this.getTargetElement(id);
1598
- } catch (error) {
1599
- this.clearHighlight();
1600
- console.warn("getTargetElement error:", error);
1601
- return;
1602
- }
1603
- if (el === this.getSelectedEl() || this.multiDr?.dragStatus === StageDragStatus.ING) {
1604
- this.clearHighlight();
1605
- return;
1606
- }
1607
- if (el === this.highlightedEl || !el) return;
1608
- this.highlightLayer?.highlight(el);
1609
- this.highlightedEl = el;
1610
- this.emit("highlight", el);
1611
- }
1612
- clearHighlight() {
1613
- this.setHighlightEl(void 0);
1614
- this.highlightLayer?.clearHighlight();
1615
- }
1616
- /**
1617
- * 用于在切换选择模式时清除上一次的状态
1618
- * @param selectType 需要清理的选择模式
1619
- */
1620
- clearSelectStatus(selectType) {
1621
- if (selectType === SelectStatus.MULTI_SELECT) {
1622
- this.multiDr?.clearSelectStatus();
1623
- this.selectedElList = [];
1624
- } else {
1625
- this.dr?.clearSelectStatus();
1626
- }
1627
- }
1628
- /**
1629
- * 找到鼠标下方的容器,通过添加className对容器进行标记
1630
- * @param event 鼠标事件
1631
- * @param excludeElList 计算鼠标点所在容器时要排除的元素列表
1632
- */
1633
- async addContainerHighlightClassName(event, excludeElList) {
1634
- const doc = this.getRenderDocument();
1635
- if (!doc) return;
1636
- const els = this.getElementsFromPoint(event);
1637
- for (const el of els) {
1638
- if (!getIdFromEl()(el)?.startsWith(GHOST_EL_ID_PREFIX) && await this.isContainer?.(el) && !excludeElList.includes(el)) {
1639
- addClassName(el, doc, this.containerHighlightClassName);
1640
- break;
1641
- }
1642
- }
1643
- }
1644
- /**
1645
- * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
1646
- * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
1647
- * @param event 鼠标事件
1648
- * @param excludeElList 计算鼠标所在容器时要排除的元素列表
1649
- * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
1650
- */
1651
- delayedMarkContainer(event, excludeElList = []) {
1652
- if (this.canAddToContainer()) {
1653
- return globalThis.setTimeout(() => {
1654
- this.addContainerHighlightClassName(event, excludeElList);
1655
- }, this.containerHighlightDuration);
1656
- }
1657
- return void 0;
1658
- }
1659
- getDragStatus() {
1660
- return this.dr?.getDragStatus();
1661
- }
1662
- updateMoveableOptions() {
1663
- this.dr?.updateMoveable();
1664
- this.multiDr?.updateMoveable();
1665
- }
1666
- destroy() {
1667
- this.container.removeEventListener("mousedown", this.mouseDownHandler);
1668
- this.container.removeEventListener("mousemove", this.mouseMoveHandler);
1669
- this.container.removeEventListener("mouseleave", this.mouseLeaveHandler);
1670
- this.container.removeEventListener("wheel", this.mouseWheelHandler);
1671
- this.container.removeEventListener("dblclick", this.dblclickHandler);
1672
- this.selectedEl = null;
1673
- this.selectedElList = [];
1674
- this.dr?.destroy();
1675
- this.multiDr?.destroy();
1676
- this.highlightLayer?.destroy();
1677
- this.dr = null;
1678
- this.multiDr = null;
1679
- this.highlightLayer = null;
1680
- }
1681
- on(eventName, listener) {
1682
- return super.on(eventName, listener);
1683
- }
1684
- emit(eventName, ...args) {
1685
- return super.emit(eventName, ...args);
1686
- }
1687
- createDr(config) {
1688
- const createDrHelper = () => new DragResizeHelper({
1689
- container: config.container,
1690
- updateDragEl: config.updateDragEl
1691
- });
1692
- const dr = new StageDragResize({
1693
- container: config.container,
1694
- disabledDragStart: config.disabledDragStart,
1695
- moveableOptions: config.moveableOptions && this.changeCallback(config.moveableOptions, false),
1696
- dragResizeHelper: createDrHelper(),
1697
- getRootContainer: config.getRootContainer,
1698
- getRenderDocument: config.getRenderDocument,
1699
- markContainerEnd: this.markContainerEnd.bind(this),
1700
- delayedMarkContainer: this.delayedMarkContainer.bind(this)
1701
- });
1702
- dr.on("update", (data) => {
1703
- setTimeout(() => this.emit("update", data));
1704
- }).on("sort", (data) => {
1705
- setTimeout(() => this.emit("sort", data));
1706
- }).on(AbleActionEventType.SELECT_PARENT, () => {
1707
- this.emit("select-parent");
1708
- }).on(AbleActionEventType.REMOVE, () => {
1709
- const drTarget = this.dr?.getTarget();
1710
- if (!drTarget) return;
1711
- const data = {
1712
- data: [{ el: drTarget }]
1713
- };
1714
- this.emit("remove", data);
1715
- }).on(AbleActionEventType.RERENDER, () => {
1716
- this.emit("rerender");
1717
- }).on("drag-start", (e) => {
1718
- this.emit("drag-start", e);
1719
- });
1720
- return dr;
1721
- }
1722
- createMultiDr(config) {
1723
- const createDrHelper = () => new DragResizeHelper({
1724
- container: config.container,
1725
- updateDragEl: config.updateDragEl
1726
- });
1727
- const multiDr = new StageMultiDragResize({
1728
- container: config.container,
1729
- moveableOptions: config.moveableOptions && this.changeCallback(config.moveableOptions, true),
1730
- dragResizeHelper: createDrHelper(),
1731
- getRootContainer: config.getRootContainer,
1732
- getRenderDocument: config.getRenderDocument,
1733
- markContainerEnd: this.markContainerEnd.bind(this),
1734
- delayedMarkContainer: this.delayedMarkContainer.bind(this)
1735
- });
1736
- multiDr?.on("update", (data) => {
1737
- this.emit("multi-update", data);
1738
- }).on("change-to-select", (id, e) => {
1739
- if (this.isMultiSelectStatus) return;
1740
- this.emit("change-to-select", id, e);
1741
- });
1742
- return multiDr;
1743
- }
1744
- changeCallback(options, isMulti) {
1745
- if (typeof options === "function") {
1746
- return () => {
1747
- if (typeof options === "function") {
1748
- const cfg = {
1749
- targetEl: this.selectedEl,
1750
- targetElId: getIdFromEl()(this.selectedEl),
1751
- targetEls: this.selectedElList,
1752
- targetElIds: this.selectedElList?.map((item) => getIdFromEl()(item) || ""),
1753
- isMulti,
1754
- document: this.getRenderDocument()
1755
- };
1756
- return options(cfg);
1757
- }
1758
- return options;
1759
- };
1760
- }
1761
- return options;
1762
- }
1763
- /**
1764
- * 在执行多选逻辑前,先准备好多选选中元素
1765
- * @param el 新选中的元素
1766
- * @returns 多选选中的元素列表
1767
- */
1768
- async beforeMultiSelect(event) {
1769
- const el = await this.getElementFromPoint(event);
1770
- if (!el) return;
1771
- if (this.selectedEl && !this.selectedEl.className.includes(PAGE_CLASS)) {
1772
- this.selectedElList.push(this.selectedEl);
1773
- this.setSelectedEl(null);
1774
- }
1775
- const existIndex = this.selectedElList.findIndex((selectedDom) => getIdFromEl()(selectedDom) === getIdFromEl()(el));
1776
- if (existIndex !== -1) {
1777
- if (this.selectedElList.length > 1) {
1778
- this.selectedElList.splice(existIndex, 1);
1779
- }
1780
- } else {
1781
- this.selectedElList.push(el);
1782
- }
1783
- }
1784
- /**
1785
- * 当前状态下能否将组件加入容器,默认是鼠标悬停一段时间加入,alt模式则是按住alt+鼠标悬停一段时间加入
1786
- */
1787
- canAddToContainer() {
1788
- return this.containerHighlightType === ContainerHighlightType.DEFAULT || this.containerHighlightType === ContainerHighlightType.ALT && this.isAltKeydown;
1789
- }
1790
- /**
1791
- * 结束对container的标记状态
1792
- * @returns 标记的容器元素,没有标记的容器时返回null
1793
- */
1794
- markContainerEnd() {
1795
- const doc = this.getRenderDocument();
1796
- if (doc && this.canAddToContainer()) {
1797
- return removeClassNameByClassName(doc, this.containerHighlightClassName);
1798
- }
1799
- return null;
1800
- }
1801
- initMouseEvent() {
1802
- this.container.addEventListener("mousedown", this.mouseDownHandler);
1803
- this.container.addEventListener("mousemove", this.mouseMoveHandler);
1804
- this.container.addEventListener("mouseleave", this.mouseLeaveHandler);
1805
- this.container.addEventListener("wheel", this.mouseWheelHandler);
1806
- this.container.addEventListener("dblclick", this.dblclickHandler);
1807
- }
1808
- /**
1809
- * 初始化键盘事件监听
1810
- */
1811
- initKeyEvent() {
1812
- const { isMac } = new Env();
1813
- const ctrl = isMac ? "meta" : "ctrl";
1814
- KeyController.global.keydown(ctrl, (e) => {
1815
- e.inputEvent.preventDefault();
1816
- if (!this.disabledMultiSelect) {
1817
- this.isMultiSelectStatus = true;
1818
- }
1819
- });
1820
- KeyController.global.on("blur", () => {
1821
- if (!this.disabledMultiSelect) {
1822
- this.isMultiSelectStatus = false;
1823
- }
1824
- this.isAltKeydown = false;
1825
- });
1826
- KeyController.global.keyup(ctrl, (e) => {
1827
- e.inputEvent.preventDefault();
1828
- if (!this.disabledMultiSelect) {
1829
- this.isMultiSelectStatus = false;
1830
- }
1831
- });
1832
- KeyController.global.keydown("alt", (e) => {
1833
- e.inputEvent.preventDefault();
1834
- this.isAltKeydown = true;
1835
- });
1836
- KeyController.global.keyup("alt", (e) => {
1837
- e.inputEvent.preventDefault();
1838
- this.markContainerEnd();
1839
- this.isAltKeydown = false;
1840
- });
1841
- }
1842
- /**
1843
- * 在down事件中集中cpu处理画布中选中操作渲染,在up事件中再通知外面的编辑器更新
1844
- */
1845
- mouseDownHandler = (event) => {
1846
- const handler = async () => {
1847
- this.clearHighlight();
1848
- event.stopImmediatePropagation();
1849
- event.stopPropagation();
1850
- if (this.isStopTriggerSelect(event)) return;
1851
- this.container.removeEventListener("mousemove", this.mouseMoveHandler);
1852
- if (this.isMultiSelectStatus) {
1853
- await this.beforeMultiSelect(event);
1854
- if (this.selectedElList.length > 0) {
1855
- this.emit("before-multi-select", this.selectedElList);
1856
- }
1857
- } else {
1858
- const el = await this.getElementFromPoint(event);
1859
- if (!el) return;
1860
- this.emit("before-select", el, event);
1861
- }
1862
- getDocument().addEventListener("mouseup", this.mouseUpHandler);
1863
- };
1864
- handler();
1865
- };
1866
- isStopTriggerSelect(event) {
1867
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return true;
1868
- if (!event.target) return true;
1869
- const targetClassList = event.target.classList;
1870
- if (!this.isMultiSelectStatus && targetClassList.contains("moveable-area")) {
1871
- return true;
1872
- }
1873
- if (targetClassList.contains("moveable-control") || isMoveableButton(event.target)) {
1874
- return true;
1875
- }
1876
- return false;
1877
- }
1878
- /**
1879
- * 在up事件中负责对外通知选中事件,通知画布之外的编辑器更新
1880
- */
1881
- mouseUpHandler = (event) => {
1882
- getDocument().removeEventListener("mouseup", this.mouseUpHandler);
1883
- this.container.addEventListener("mousemove", this.mouseMoveHandler);
1884
- if (this.isMultiSelectStatus) {
1885
- this.emit("multi-select", this.selectedElList, event);
1886
- } else {
1887
- this.emit("select", this.selectedEl, event);
1888
- }
1889
- };
1890
- mouseLeaveHandler = (event) => {
1891
- setTimeout(() => this.clearHighlight(), throttleTime);
1892
- this.emit("mouseleave", event);
1893
- };
1894
- mouseWheelHandler = () => {
1895
- this.clearHighlight();
1896
- };
1897
- dblclickHandler = (event) => {
1898
- this.emit("dblclick", event);
1899
- };
1900
- }
1901
-
1902
- const guidesClass = "tmagic-stage-guides";
1903
- class Rule extends EventEmitter {
1904
- hGuides;
1905
- vGuides;
1906
- horizontalGuidelines = [];
1907
- verticalGuidelines = [];
1908
- container;
1909
- containerResizeObserver;
1910
- isShowGuides = true;
1911
- guidesOptions;
1912
- constructor(container, options) {
1913
- super();
1914
- if (options?.disabledRule) {
1915
- return;
1916
- }
1917
- this.guidesOptions = options?.guidesOptions || {};
1918
- this.container = container;
1919
- this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
1920
- this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
1921
- this.containerResizeObserver = new ResizeObserver(() => {
1922
- this.vGuides?.resize();
1923
- this.hGuides?.resize();
1924
- });
1925
- this.containerResizeObserver.observe(this.container);
1926
- }
1927
- /**
1928
- * 是否显示辅助线
1929
- * @param isShowGuides 是否显示
1930
- */
1931
- showGuides(isShowGuides = true) {
1932
- this.isShowGuides = isShowGuides;
1933
- this.hGuides?.setState({
1934
- showGuides: isShowGuides
1935
- });
1936
- this.vGuides?.setState({
1937
- showGuides: isShowGuides
1938
- });
1939
- }
1940
- setGuides([hLines, vLines]) {
1941
- this.horizontalGuidelines = hLines;
1942
- this.verticalGuidelines = vLines;
1943
- this.hGuides?.setState({
1944
- defaultGuides: hLines
1945
- });
1946
- this.vGuides?.setState({
1947
- defaultGuides: vLines
1948
- });
1949
- this.emit("change-guides", {
1950
- type: GuidesType.HORIZONTAL,
1951
- guides: hLines
1952
- });
1953
- this.emit("change-guides", {
1954
- type: GuidesType.VERTICAL,
1955
- guides: vLines
1956
- });
1957
- }
1958
- /**
1959
- * 清空所有参考线
1960
- */
1961
- clearGuides() {
1962
- this.setGuides([[], []]);
1963
- }
1964
- /**
1965
- * 是否显示标尺
1966
- * @param show 是否显示
1967
- */
1968
- showRule(show = true) {
1969
- if (show) {
1970
- this.destroyGuides();
1971
- this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
1972
- this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
1973
- } else {
1974
- this.hGuides?.setState({
1975
- rulerStyle: {
1976
- visibility: "hidden"
1977
- }
1978
- });
1979
- this.vGuides?.setState({
1980
- rulerStyle: {
1981
- visibility: "hidden"
1982
- }
1983
- });
1984
- }
1985
- }
1986
- scrollRule(scrollTop) {
1987
- this.hGuides?.scrollGuides(scrollTop);
1988
- this.hGuides?.scroll(0);
1989
- this.vGuides?.scrollGuides(0);
1990
- this.vGuides?.scroll(scrollTop);
1991
- }
1992
- destroy() {
1993
- this.destroyGuides();
1994
- this.hGuides?.off("changeGuides", this.hGuidesChangeGuidesHandler);
1995
- this.vGuides?.off("changeGuides", this.vGuidesChangeGuidesHandler);
1996
- this.containerResizeObserver?.disconnect();
1997
- this.removeAllListeners();
1998
- }
1999
- destroyGuides() {
2000
- this.hGuides?.destroy();
2001
- this.vGuides?.destroy();
2002
- this.container?.querySelectorAll(`.${guidesClass}`).forEach((el) => {
2003
- el.remove();
2004
- });
2005
- this.hGuides = void 0;
2006
- this.vGuides = void 0;
2007
- this.container = void 0;
2008
- }
2009
- getGuidesStyle = (type) => ({
2010
- position: "fixed",
2011
- zIndex: 1,
2012
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
2013
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
2014
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
2015
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
2016
- });
2017
- createGuides = (type, defaultGuides = []) => {
2018
- if (!this.container) {
2019
- return;
2020
- }
2021
- const guides = new Guides(this.container, {
2022
- type,
2023
- defaultGuides,
2024
- displayDragPos: true,
2025
- className: guidesClass,
2026
- backgroundColor: "#fff",
2027
- lineColor: "#000",
2028
- textColor: "#000",
2029
- style: this.getGuidesStyle(type),
2030
- showGuides: this.isShowGuides,
2031
- ...this.guidesOptions
2032
- });
2033
- const changEventHandler = {
2034
- [GuidesType.HORIZONTAL]: this.hGuidesChangeGuidesHandler,
2035
- [GuidesType.VERTICAL]: this.vGuidesChangeGuidesHandler
2036
- }[type];
2037
- if (changEventHandler) {
2038
- guides.on("changeGuides", changEventHandler);
2039
- }
2040
- return guides;
2041
- };
2042
- hGuidesChangeGuidesHandler = (e) => {
2043
- this.horizontalGuidelines = e.guides;
2044
- this.emit("change-guides", {
2045
- type: GuidesType.HORIZONTAL,
2046
- guides: this.horizontalGuidelines
2047
- });
2048
- };
2049
- vGuidesChangeGuidesHandler = (e) => {
2050
- this.verticalGuidelines = e.guides;
2051
- this.emit("change-guides", {
2052
- type: GuidesType.VERTICAL,
2053
- guides: this.verticalGuidelines
2054
- });
2055
- };
2056
- }
2057
-
2058
- const wrapperClassName = "editor-mask-wrapper";
2059
- const hideScrollbar = () => {
2060
- injectStyle(getDocument(), `.${wrapperClassName}::-webkit-scrollbar { width: 0 !important; display: none }`);
2061
- };
2062
- const createContent = () => createDiv({
2063
- className: "editor-mask",
2064
- cssText: `
2065
- position: absolute;
2066
- top: 0;
2067
- left: 0;
2068
- transform: translate3d(0, 0, 0);
2069
- `
2070
- });
2071
- const createWrapper = () => {
2072
- const el = createDiv({
2073
- className: wrapperClassName,
2074
- cssText: `
2075
- position: absolute;
2076
- top: 0;
2077
- left: 0;
2078
- height: 100%;
2079
- width: 100%;
2080
- overflow: hidden;
2081
- z-index: ${ZIndex.MASK};
2082
- `
2083
- });
2084
- hideScrollbar();
2085
- return el;
2086
- };
2087
- class StageMask extends Rule {
2088
- content = createContent();
2089
- wrapper;
2090
- page = null;
2091
- scrollTop = 0;
2092
- scrollLeft = 0;
2093
- width = 0;
2094
- height = 0;
2095
- wrapperHeight = 0;
2096
- wrapperWidth = 0;
2097
- maxScrollTop = 0;
2098
- maxScrollLeft = 0;
2099
- mode = Mode.ABSOLUTE;
2100
- pageScrollParent = null;
2101
- intersectionObserver = null;
2102
- wrapperResizeObserver = null;
2103
- constructor(options) {
2104
- const wrapper = createWrapper();
2105
- super(wrapper, options);
2106
- this.wrapper = wrapper;
2107
- this.content.addEventListener("wheel", this.mouseWheelHandler);
2108
- this.wrapper.appendChild(this.content);
2109
- }
2110
- setMode(mode) {
2111
- this.mode = mode;
2112
- this.scroll();
2113
- this.content.dataset.mode = mode;
2114
- if (mode === Mode.FIXED) {
2115
- this.content.style.width = `${this.wrapperWidth}px`;
2116
- this.content.style.height = `${this.wrapperHeight}px`;
2117
- } else {
2118
- this.content.style.width = `${this.width}px`;
2119
- this.content.style.height = `${this.height}px`;
2120
- }
2121
- }
2122
- /**
2123
- * 初始化视窗和蒙层监听,监听元素是否在视窗区域、监听mask蒙层所在的wrapper大小变化
2124
- * @description 初始化视窗和蒙层监听
2125
- * @param page 页面Dom节点
2126
- */
2127
- observe(page) {
2128
- if (!page) return;
2129
- this.page = page;
2130
- this.initObserverIntersection();
2131
- this.initObserverWrapper();
2132
- }
2133
- /**
2134
- * 处理页面大小变更,同步页面和mask大小
2135
- * @param entries ResizeObserverEntry,获取页面最新大小
2136
- */
2137
- pageResize(entries) {
2138
- const [entry] = entries;
2139
- const { clientHeight, clientWidth } = entry.target;
2140
- this.setHeight(clientHeight);
2141
- this.setWidth(clientWidth);
2142
- this.scroll();
2143
- }
2144
- /**
2145
- * 监听一个组件是否在画布可视区域内
2146
- * @param el 被选中的组件,可能是左侧目录树中选中的
2147
- */
2148
- observerIntersection(el) {
2149
- this.intersectionObserver?.observe(el);
2150
- }
2151
- /**
2152
- * 挂载Dom节点
2153
- * @param el 将蒙层挂载到该Dom节点上
2154
- */
2155
- mount(el) {
2156
- if (!this.content) throw new Error("content 不存在");
2157
- el.appendChild(this.wrapper);
2158
- }
2159
- setLayout(el) {
2160
- this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
2161
- }
2162
- scrollIntoView(el) {
2163
- if (!this.page || el.getBoundingClientRect().left >= this.page.scrollWidth) return;
2164
- const scrollParent = getScrollParent(el);
2165
- if (scrollParent && scrollParent !== this.pageScrollParent) {
2166
- this.scrollIntoView(scrollParent);
2167
- return;
2168
- }
2169
- el.scrollIntoView();
2170
- if (!this.pageScrollParent) return;
2171
- this.scrollLeft = this.pageScrollParent.scrollLeft;
2172
- this.scrollTop = this.pageScrollParent.scrollTop;
2173
- this.scroll();
2174
- }
2175
- /**
2176
- * 销毁实例
2177
- */
2178
- destroy() {
2179
- super.destroy();
2180
- this.content?.remove();
2181
- this.page = null;
2182
- this.pageScrollParent = null;
2183
- this.wrapperResizeObserver?.disconnect();
2184
- }
2185
- on(eventName, listener) {
2186
- return super.on(eventName, listener);
2187
- }
2188
- emit(eventName, ...args) {
2189
- return super.emit(eventName, ...args);
2190
- }
2191
- /**
2192
- * 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域
2193
- */
2194
- initObserverIntersection() {
2195
- this.pageScrollParent = getScrollParent(this.page) || null;
2196
- this.intersectionObserver?.disconnect();
2197
- if (typeof IntersectionObserver !== "undefined") {
2198
- this.intersectionObserver = new IntersectionObserver(
2199
- (entries) => {
2200
- entries.forEach((entry) => {
2201
- const { target, intersectionRatio } = entry;
2202
- if (intersectionRatio <= 0) {
2203
- this.scrollIntoView(target);
2204
- }
2205
- this.intersectionObserver?.unobserve(target);
2206
- });
2207
- },
2208
- {
2209
- root: this.pageScrollParent,
2210
- rootMargin: "0px",
2211
- threshold: 1
2212
- }
2213
- );
2214
- }
2215
- }
2216
- /**
2217
- * 监听mask的容器大小变化
2218
- */
2219
- initObserverWrapper() {
2220
- this.wrapperResizeObserver?.disconnect();
2221
- if (typeof ResizeObserver !== "undefined") {
2222
- this.wrapperResizeObserver = new ResizeObserver((entries) => {
2223
- const [entry] = entries;
2224
- const { clientHeight, clientWidth } = entry.target;
2225
- this.wrapperHeight = clientHeight;
2226
- this.wrapperWidth = clientWidth;
2227
- if (this.mode === Mode.FIXED) {
2228
- this.content.style.width = `${this.wrapperWidth}px`;
2229
- this.content.style.height = `${this.wrapperHeight}px`;
2230
- }
2231
- this.setMaxScrollLeft();
2232
- this.setMaxScrollTop();
2233
- });
2234
- this.wrapperResizeObserver.observe(this.wrapper);
2235
- }
2236
- }
2237
- scroll() {
2238
- this.fixScrollValue();
2239
- let { scrollLeft, scrollTop } = this;
2240
- if (this.pageScrollParent) {
2241
- this.pageScrollParent.scrollTo({
2242
- top: scrollTop,
2243
- left: scrollLeft
2244
- });
2245
- }
2246
- if (this.mode === Mode.FIXED) {
2247
- scrollLeft = 0;
2248
- scrollTop = 0;
2249
- }
2250
- this.scrollRule(scrollTop);
2251
- this.scrollTo(scrollLeft, scrollTop);
2252
- }
2253
- scrollTo(scrollLeft, scrollTop) {
2254
- this.content.style.transform = `translate3d(${-scrollLeft}px, ${-scrollTop}px, 0)`;
2255
- const event = new CustomEvent("customScroll", {
2256
- detail: {
2257
- scrollLeft: this.scrollLeft,
2258
- scrollTop: this.scrollTop
2259
- }
2260
- });
2261
- this.content.dispatchEvent(event);
2262
- }
2263
- /**
2264
- * 设置蒙层高度
2265
- * @param height 高度
2266
- */
2267
- setHeight(height) {
2268
- this.height = height;
2269
- this.setMaxScrollTop();
2270
- if (this.mode !== Mode.FIXED) {
2271
- this.content.style.height = `${height}px`;
2272
- }
2273
- }
2274
- /**
2275
- * 设置蒙层宽度
2276
- * @param width 宽度
2277
- */
2278
- setWidth(width) {
2279
- this.width = width;
2280
- this.setMaxScrollLeft();
2281
- if (this.mode !== Mode.FIXED) {
2282
- this.content.style.width = `${width}px`;
2283
- }
2284
- }
2285
- /**
2286
- * 计算并设置最大滚动宽度
2287
- */
2288
- setMaxScrollLeft() {
2289
- this.maxScrollLeft = Math.max(this.width - this.wrapperWidth, 0);
2290
- }
2291
- /**
2292
- * 计算并设置最大滚动高度
2293
- */
2294
- setMaxScrollTop() {
2295
- this.maxScrollTop = Math.max(this.height - this.wrapperHeight, 0);
2296
- }
2297
- /**
2298
- * 修复滚动距离
2299
- * 由于滚动容器变化等因素,会导致当前滚动的距离不正确
2300
- */
2301
- fixScrollValue() {
2302
- if (this.scrollTop < 0) this.scrollTop = 0;
2303
- if (this.scrollLeft < 0) this.scrollLeft = 0;
2304
- if (this.maxScrollTop < this.scrollTop) this.scrollTop = this.maxScrollTop;
2305
- if (this.maxScrollLeft < this.scrollLeft) this.scrollLeft = this.maxScrollLeft;
2306
- }
2307
- mouseWheelHandler = (event) => {
2308
- if (!this.page) throw new Error("page 未初始化");
2309
- const { deltaY, deltaX } = event;
2310
- if (this.page.clientHeight < this.wrapperHeight && deltaY) return;
2311
- if (this.page.clientWidth < this.wrapperWidth && deltaX) return;
2312
- if (this.maxScrollTop > 0) {
2313
- this.scrollTop = this.scrollTop + deltaY;
2314
- }
2315
- if (this.maxScrollLeft > 0) {
2316
- this.scrollLeft = this.scrollLeft + deltaX;
2317
- }
2318
- this.scroll();
2319
- this.emit("scroll", event);
2320
- };
2321
- }
2322
-
2323
- const style = ".tmagic-stage-container-highlight::after {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n background-color: #000;\n opacity: .1;\n pointer-events: none;\n}\n\n.magic-ui-container.magic-layout-relative {\n min-height: 50px;\n}\n";
2324
-
2325
- class StageRender extends EventEmitter$1 {
2326
- /** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
2327
- contentWindow = null;
2328
- runtime = null;
2329
- iframe;
2330
- nativeContainer;
2331
- runtimeUrl;
2332
- zoom = DEFAULT_ZOOM;
2333
- renderType;
2334
- customizedRender;
2335
- constructor({ runtimeUrl, zoom, customizedRender, renderType = RenderType.IFRAME }) {
2336
- super();
2337
- this.renderType = renderType;
2338
- this.runtimeUrl = runtimeUrl || "";
2339
- this.customizedRender = customizedRender;
2340
- this.setZoom(zoom);
2341
- if (this.renderType === RenderType.IFRAME) {
2342
- this.createIframe();
2343
- } else if (this.renderType === RenderType.NATIVE) {
2344
- this.createNativeContainer();
2345
- }
2346
- }
2347
- getMagicApi = () => ({
2348
- id: guid(),
2349
- onPageElUpdate: (el) => {
2350
- this.emit("page-el-update", el);
2351
- },
2352
- onRuntimeReady: (runtime) => {
2353
- if (this.runtime) {
2354
- return;
2355
- }
2356
- this.runtime = runtime;
2357
- globalThis.runtime = runtime;
2358
- this.emit("runtime-ready", runtime);
2359
- }
2360
- });
2361
- async add(data) {
2362
- const runtime = await this.getRuntime();
2363
- return runtime?.add?.(data);
2364
- }
2365
- async remove(data) {
2366
- const runtime = await this.getRuntime();
2367
- return runtime?.remove?.(data);
2368
- }
2369
- async update(data) {
2370
- const runtime = await this.getRuntime();
2371
- runtime?.update?.(data);
2372
- }
2373
- async select(ids) {
2374
- const runtime = await this.getRuntime();
2375
- for (const id of ids) {
2376
- await runtime?.select?.(id);
2377
- this.flagSelectedEl(this.getTargetElement(id));
2378
- }
2379
- }
2380
- setZoom(zoom = DEFAULT_ZOOM) {
2381
- this.zoom = zoom;
2382
- }
2383
- /**
2384
- * 挂载Dom节点
2385
- * @param el 将页面挂载到该Dom节点上
2386
- */
2387
- async mount(el) {
2388
- if (this.iframe) {
2389
- if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
2390
- let html = await fetch(this.runtimeUrl).then((res) => res.text());
2391
- const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
2392
- html = html.replace("<head>", `<head>
2393
- <base href="${base}">`);
2394
- this.iframe.srcdoc = html;
2395
- }
2396
- el.appendChild(this.iframe);
2397
- this.postTmagicRuntimeReady();
2398
- } else if (this.nativeContainer) {
2399
- el.appendChild(this.nativeContainer);
2400
- }
2401
- }
2402
- getRuntime = () => {
2403
- if (this.runtime) return Promise.resolve(this.runtime);
2404
- return new Promise((resolve) => {
2405
- const listener = (runtime) => {
2406
- this.off("runtime-ready", listener);
2407
- resolve(runtime);
2408
- };
2409
- this.on("runtime-ready", listener);
2410
- });
2411
- };
2412
- getDocument() {
2413
- return this.contentWindow?.document;
2414
- }
2415
- /**
2416
- * 通过坐标获得坐标下所有HTML元素数组
2417
- * @param point 坐标
2418
- * @returns 坐标下方所有HTML元素数组,会包含父元素直至html,元素层叠时返回顺序是从上到下
2419
- */
2420
- getElementsFromPoint(point) {
2421
- let x = point.clientX;
2422
- let y = point.clientY;
2423
- if (this.iframe) {
2424
- const rect = this.iframe.getClientRects()[0];
2425
- if (rect) {
2426
- x = x - rect.left;
2427
- y = y - rect.top;
2428
- }
2429
- }
2430
- return this.getDocument()?.elementsFromPoint(x / this.zoom, y / this.zoom);
2431
- }
2432
- getTargetElement(id) {
2433
- return getElById()(this.getDocument(), id);
2434
- }
2435
- postTmagicRuntimeReady() {
2436
- this.contentWindow = this.iframe?.contentWindow;
2437
- this.contentWindow.magic = this.getMagicApi();
2438
- this.contentWindow.postMessage(
2439
- {
2440
- tmagicRuntimeReady: true
2441
- },
2442
- "*"
2443
- );
2444
- }
2445
- reloadIframe(url) {
2446
- if (this.renderType !== RenderType.IFRAME) return;
2447
- const el = this.iframe?.parentElement;
2448
- this.destroyIframe();
2449
- this.runtimeUrl = url;
2450
- this.createIframe();
2451
- this.mount(el);
2452
- this.runtime = null;
2453
- }
2454
- destroyIframe() {
2455
- this.iframe?.removeEventListener("load", this.iframeLoadHandler);
2456
- this.contentWindow = null;
2457
- this.iframe?.remove();
2458
- this.iframe = void 0;
2459
- }
2460
- /**
2461
- * 销毁实例
2462
- */
2463
- destroy() {
2464
- this.destroyIframe();
2465
- globalThis.runtime = void 0;
2466
- this.removeAllListeners();
2467
- }
2468
- on(eventName, listener) {
2469
- return super.on(eventName, listener);
2470
- }
2471
- emit(eventName, ...args) {
2472
- return super.emit(eventName, ...args);
2473
- }
2474
- createIframe() {
2475
- this.iframe = globalThis.document.createElement("iframe");
2476
- this.iframe.src = this.runtimeUrl && isSameDomain(this.runtimeUrl) ? this.runtimeUrl : "";
2477
- this.iframe.style.cssText = `
2478
- border: 0;
2479
- width: 100%;
2480
- height: 100%;
2481
- `;
2482
- this.iframe.addEventListener("load", this.iframeLoadHandler);
2483
- return this.iframe;
2484
- }
2485
- async createNativeContainer() {
2486
- this.contentWindow = globalThis;
2487
- this.nativeContainer = globalThis.document.createElement("div");
2488
- this.contentWindow.magic = this.getMagicApi();
2489
- if (this.customizedRender) {
2490
- const el = await this.customizedRender();
2491
- if (el) {
2492
- this.nativeContainer.appendChild(el);
2493
- }
2494
- }
2495
- }
2496
- /**
2497
- * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
2498
- * @param el 被选中的元素
2499
- */
2500
- flagSelectedEl(el) {
2501
- const doc = this.getDocument();
2502
- if (doc) {
2503
- removeSelectedClassName(doc);
2504
- el && addSelectedClassName(el, doc);
2505
- }
2506
- }
2507
- iframeLoadHandler = () => {
2508
- const handler = async () => {
2509
- if (!this.contentWindow?.magic) {
2510
- this.postTmagicRuntimeReady();
2511
- }
2512
- if (!this.contentWindow) return;
2513
- if (this.customizedRender) {
2514
- const el = await this.customizedRender();
2515
- if (el) {
2516
- this.contentWindow.document?.body?.appendChild(el);
2517
- }
2518
- }
2519
- this.emit("onload");
2520
- injectStyle(this.contentWindow.document, style);
2521
- };
2522
- handler();
2523
- };
2524
- }
2525
-
2526
- class StageCore extends EventEmitter$1 {
2527
- container;
2528
- renderer = null;
2529
- mask = null;
2530
- actionManager = null;
2531
- pageResizeObserver = null;
2532
- autoScrollIntoView;
2533
- customizedRender;
2534
- constructor(config) {
2535
- super();
2536
- this.autoScrollIntoView = config.autoScrollIntoView;
2537
- this.customizedRender = config.render;
2538
- this.renderer = new StageRender({
2539
- runtimeUrl: config.runtimeUrl,
2540
- zoom: config.zoom,
2541
- renderType: config.renderType,
2542
- customizedRender: async () => {
2543
- if (this?.customizedRender) {
2544
- return await this.customizedRender(this);
2545
- }
2546
- return null;
2547
- }
2548
- });
2549
- this.mask = new StageMask({
2550
- guidesOptions: config.guidesOptions,
2551
- disabledRule: config.disabledRule
2552
- });
2553
- this.actionManager = new ActionManager(this.getActionManagerConfig(config));
2554
- this.initRenderEvent();
2555
- this.initActionEvent();
2556
- this.initMaskEvent();
2557
- }
2558
- /**
2559
- * 单选选中元素
2560
- * @param id 选中的id
2561
- */
2562
- async select(id, event) {
2563
- const el = this.renderer?.getTargetElement(id) || null;
2564
- if (el === this.actionManager?.getSelectedEl()) return;
2565
- await this.renderer?.select([id]);
2566
- if (el) {
2567
- this.mask?.setLayout(el);
2568
- }
2569
- this.actionManager?.select(el, event);
2570
- if (el && (this.autoScrollIntoView || el.dataset.autoScrollIntoView)) {
2571
- this.mask?.observerIntersection(el);
2572
- }
2573
- }
2574
- /**
2575
- * 多选选中多个元素
2576
- * @param ids 选中元素的id列表
2577
- */
2578
- async multiSelect(ids) {
2579
- const els = ids.map((id) => this.renderer?.getTargetElement(id)).filter((el) => Boolean(el));
2580
- if (els.length === 0) return;
2581
- const lastEl = els[els.length - 1];
2582
- const isReduceSelect = els.length < this.actionManager.getSelectedElList().length;
2583
- await this.renderer?.select(ids);
2584
- lastEl && this.mask?.setLayout(lastEl);
2585
- this.actionManager?.multiSelect(ids);
2586
- if (lastEl && (this.autoScrollIntoView || lastEl.dataset.autoScrollIntoView) && !isReduceSelect) {
2587
- this.mask?.observerIntersection(lastEl);
2588
- }
2589
- }
2590
- /**
2591
- * 高亮选中元素
2592
- * @param el 要高亮的元素
2593
- */
2594
- highlight(id) {
2595
- this.actionManager?.highlight(id);
2596
- }
2597
- clearHighlight() {
2598
- this.actionManager?.clearHighlight();
2599
- }
2600
- /**
2601
- * 更新组件
2602
- * @param data 更新组件的数据
2603
- */
2604
- async update(data) {
2605
- const { config } = data;
2606
- await this.renderer?.update(data);
2607
- setTimeout(() => {
2608
- const el = this.renderer?.getTargetElement(`${config.id}`);
2609
- if (el && this.actionManager?.isSelectedEl(el)) {
2610
- this.mask?.setLayout(el);
2611
- this.actionManager.setSelectedEl(el);
2612
- this.actionManager.updateMoveable(el);
2613
- }
2614
- });
2615
- }
2616
- /**
2617
- * 往画布增加一个组件
2618
- * @param data 组件信息数据
2619
- */
2620
- async add(data) {
2621
- return await this.renderer?.add(data);
2622
- }
2623
- /**
2624
- * 从画布删除一个组件
2625
- * @param data 组件信息数据
2626
- */
2627
- async remove(data) {
2628
- return await this.renderer?.remove(data);
2629
- }
2630
- setZoom(zoom = DEFAULT_ZOOM) {
2631
- this.renderer?.setZoom(zoom);
2632
- }
2633
- /**
2634
- * 挂载Dom节点
2635
- * @param el 将stage挂载到该Dom节点上
2636
- */
2637
- async mount(el) {
2638
- this.container = el;
2639
- const { mask, renderer } = this;
2640
- await renderer?.mount(el);
2641
- mask?.mount(el);
2642
- this.emit("mounted");
2643
- }
2644
- /**
2645
- * 清空所有参考线
2646
- */
2647
- clearGuides() {
2648
- this.mask?.clearGuides();
2649
- this.actionManager?.clearGuides();
2650
- }
2651
- /**
2652
- * @deprecated 废弃接口,建议用delayedMarkContainer代替
2653
- */
2654
- getAddContainerHighlightClassNameTimeout(event, excludeElList = []) {
2655
- return this.delayedMarkContainer(event, excludeElList);
2656
- }
2657
- /**
2658
- * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
2659
- * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
2660
- * @param event 鼠标事件
2661
- * @param excludeElList 计算鼠标所在容器时要排除的元素列表
2662
- * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
2663
- */
2664
- delayedMarkContainer(event, excludeElList = []) {
2665
- return this.actionManager?.delayedMarkContainer(event, excludeElList);
2666
- }
2667
- getMoveableOption(key) {
2668
- return this.actionManager?.getMoveableOption(key);
2669
- }
2670
- getDragStatus() {
2671
- return this.actionManager?.getDragStatus();
2672
- }
2673
- disableMultiSelect() {
2674
- this.actionManager?.disableMultiSelect();
2675
- }
2676
- enableMultiSelect() {
2677
- this.actionManager?.enableMultiSelect();
2678
- }
2679
- reloadIframe(url) {
2680
- this.renderer?.reloadIframe(url);
2681
- }
2682
- /**
2683
- * 销毁实例
2684
- */
2685
- destroy() {
2686
- const { mask, renderer, actionManager, pageResizeObserver } = this;
2687
- renderer?.destroy();
2688
- mask?.destroy();
2689
- actionManager?.destroy();
2690
- pageResizeObserver?.disconnect();
2691
- this.removeAllListeners();
2692
- this.container = void 0;
2693
- this.renderer = null;
2694
- this.mask = null;
2695
- this.actionManager = null;
2696
- this.pageResizeObserver = null;
2697
- }
2698
- on(eventName, listener) {
2699
- return super.on(eventName, listener);
2700
- }
2701
- emit(eventName, ...args) {
2702
- return super.emit(eventName, ...args);
2703
- }
2704
- /**
2705
- * 监听页面大小变化
2706
- */
2707
- observePageResize(page) {
2708
- if (this.pageResizeObserver) {
2709
- this.pageResizeObserver.disconnect();
2710
- }
2711
- if (typeof ResizeObserver !== "undefined") {
2712
- this.pageResizeObserver = new ResizeObserver((entries) => {
2713
- this.mask?.pageResize(entries);
2714
- this.actionManager?.updateMoveable();
2715
- });
2716
- this.pageResizeObserver.observe(page);
2717
- }
2718
- }
2719
- getActionManagerConfig(config) {
2720
- const actionManagerConfig = {
2721
- containerHighlightClassName: config.containerHighlightClassName,
2722
- containerHighlightDuration: config.containerHighlightDuration,
2723
- containerHighlightType: config.containerHighlightType,
2724
- moveableOptions: config.moveableOptions,
2725
- container: this.mask.content,
2726
- disabledDragStart: config.disabledDragStart,
2727
- disabledMultiSelect: config.disabledMultiSelect,
2728
- canSelect: config.canSelect,
2729
- isContainer: config.isContainer,
2730
- updateDragEl: config.updateDragEl,
2731
- getRootContainer: () => this.container,
2732
- getRenderDocument: () => this.renderer.getDocument(),
2733
- getTargetElement: (id) => this.renderer.getTargetElement(id),
2734
- getElementsFromPoint: (point) => this.renderer.getElementsFromPoint(point)
2735
- };
2736
- return actionManagerConfig;
2737
- }
2738
- initRenderEvent() {
2739
- this.renderer?.on("runtime-ready", (runtime) => {
2740
- this.emit("runtime-ready", runtime);
2741
- });
2742
- this.renderer?.on("page-el-update", (el) => {
2743
- this.mask?.observe(el);
2744
- this.observePageResize(el);
2745
- this.emit("page-el-update", el);
2746
- });
2747
- }
2748
- initMaskEvent() {
2749
- this.mask?.on("change-guides", (data) => {
2750
- this.actionManager?.setGuidelines(data.type, data.guides);
2751
- this.emit("change-guides", data);
2752
- });
2753
- }
2754
- /**
2755
- * 初始化操作相关事件监听
2756
- */
2757
- initActionEvent() {
2758
- this.initActionManagerEvent();
2759
- this.initDrEvent();
2760
- this.initMulDrEvent();
2761
- this.initHighlightEvent();
2762
- this.initMouseEvent();
2763
- }
2764
- /**
2765
- * 初始化ActionManager类本身抛出来的事件监听
2766
- */
2767
- initActionManagerEvent() {
2768
- this.actionManager?.on("before-select", (el, event) => {
2769
- const id = getIdFromEl()(el);
2770
- id && this.select(id, event);
2771
- }).on("select", (selectedEl, event) => {
2772
- this.emit("select", selectedEl, event);
2773
- }).on("before-multi-select", (els) => {
2774
- this.multiSelect(els.map((el) => getIdFromEl()(el)).filter((id) => Boolean(id)));
2775
- }).on("multi-select", (selectedElList, event) => {
2776
- this.emit("multi-select", selectedElList, event);
2777
- }).on("dblclick", (event) => {
2778
- this.emit("dblclick", event);
2779
- });
2780
- }
2781
- /**
2782
- * 初始化DragResize类通过ActionManager抛出来的事件监听
2783
- */
2784
- initDrEvent() {
2785
- this.actionManager?.on("update", (data) => {
2786
- this.emit("update", data);
2787
- }).on("sort", (data) => {
2788
- this.emit("sort", data);
2789
- }).on("select-parent", () => {
2790
- this.emit("select-parent");
2791
- }).on("rerender", () => {
2792
- this.emit("rerender");
2793
- }).on("remove", (data) => {
2794
- this.emit("remove", data);
2795
- });
2796
- }
2797
- /**
2798
- * 初始化MultiDragResize类通过ActionManager抛出来的事件监听
2799
- */
2800
- initMulDrEvent() {
2801
- this.actionManager?.on("change-to-select", (id, e) => {
2802
- this.select(id);
2803
- setTimeout(() => {
2804
- const el = this.renderer?.getTargetElement(id);
2805
- el && this.emit("select", el, e);
2806
- });
2807
- }).on("multi-update", (data) => {
2808
- this.emit("update", data);
2809
- });
2810
- }
2811
- /**
2812
- * 初始化Highlight类通过ActionManager抛出来的事件监听
2813
- */
2814
- initHighlightEvent() {
2815
- this.actionManager?.on("highlight", (highlightEl) => {
2816
- this.emit("highlight", highlightEl);
2817
- });
2818
- }
2819
- /**
2820
- * 初始化Highlight类通过ActionManager抛出来的事件监听
2821
- */
2822
- initMouseEvent() {
2823
- this.actionManager?.on("mousemove", (event) => {
2824
- this.emit("mousemove", event);
2825
- }).on("mouseleave", (event) => {
2826
- this.emit("mouseleave", event);
2827
- }).on("drag-start", (e) => {
2828
- this.emit("drag-start", e);
2829
- });
2830
- }
2831
- }
2832
-
2833
- export { AbleActionEventType, CONTAINER_HIGHLIGHT_CLASS_NAME, ContainerHighlightType, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, MoveableActionsAble, PAGE_CLASS, RenderType, SELECTED_CLASS, SelectStatus, StageDragResize, StageDragStatus, StageMask, StageRender, ZIndex, addSelectedClassName, StageCore as default, down, getAbsolutePosition, getBorderWidth, getMarginValue, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isMoveableButton, isRelative, isStatic, removeSelectedClassName, up };