@tmagic/stage 1.0.6 → 1.1.0-beta.10

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,19 +1,22 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('events'), require('moveable'), require('moveable-helper'), require('lodash-es'), require('@scena/guides')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'events', 'moveable', 'moveable-helper', 'lodash-es', '@scena/guides'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.TMagicStage = {}, global.EventEmitter, global.Moveable, global.MoveableHelper, global.lodashEs, global.Guides));
5
- })(this, (function (exports, EventEmitter, Moveable, MoveableHelper, lodashEs, Guides) { 'use strict';
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('events'), require('@tmagic/utils'), require('keycon'), require('moveable'), require('moveable-helper'), require('lodash-es'), require('@scena/guides')) :
3
+ typeof define === 'function' && define.amd ? define(['exports', 'events', '@tmagic/utils', 'keycon', 'moveable', 'moveable-helper', 'lodash-es', '@scena/guides'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.TMagicStage = {}, global.EventEmitter, global.utils, global.KeyController, global.Moveable, global.MoveableHelper, global.lodashEs, global.Guides));
5
+ })(this, (function (exports, EventEmitter, utils, KeyController, Moveable, MoveableHelper, lodashEs, Guides) { 'use strict';
6
6
 
7
- function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
7
+ const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
8
8
 
9
- var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
10
- var Moveable__default = /*#__PURE__*/_interopDefaultLegacy(Moveable);
11
- var MoveableHelper__default = /*#__PURE__*/_interopDefaultLegacy(MoveableHelper);
12
- var Guides__default = /*#__PURE__*/_interopDefaultLegacy(Guides);
9
+ const EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
10
+ const KeyController__default = /*#__PURE__*/_interopDefaultLegacy(KeyController);
11
+ const Moveable__default = /*#__PURE__*/_interopDefaultLegacy(Moveable);
12
+ const MoveableHelper__default = /*#__PURE__*/_interopDefaultLegacy(MoveableHelper);
13
+ const Guides__default = /*#__PURE__*/_interopDefaultLegacy(Guides);
13
14
 
14
15
  const GHOST_EL_ID_PREFIX = "ghost_el_";
15
16
  const DRAG_EL_ID_PREFIX = "drag_el_";
16
17
  const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
18
+ const CONTAINER_HIGHLIGHT_CLASS = "tmagic-stage-container-highlight";
19
+ const PAGE_CLASS = "magic-ui-page";
17
20
  const DEFAULT_ZOOM = 1;
18
21
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
19
22
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -41,6 +44,18 @@
41
44
  })(Mode || {});
42
45
  const SELECTED_CLASS = "tmagic-stage-selected-area";
43
46
 
47
+ var ContainerHighlightType = /* @__PURE__ */ ((ContainerHighlightType2) => {
48
+ ContainerHighlightType2["DEFAULT"] = "default";
49
+ ContainerHighlightType2["ALT"] = "alt";
50
+ return ContainerHighlightType2;
51
+ })(ContainerHighlightType || {});
52
+ var StageDragStatus = /* @__PURE__ */ ((StageDragStatus2) => {
53
+ StageDragStatus2["START"] = "start";
54
+ StageDragStatus2["ING"] = "ing";
55
+ StageDragStatus2["END"] = "end";
56
+ return StageDragStatus2;
57
+ })(StageDragStatus || {});
58
+
44
59
  const getParents = (el, relative) => {
45
60
  let cur = el.parentElement;
46
61
  const parents = [];
@@ -66,6 +81,19 @@
66
81
  top
67
82
  };
68
83
  };
84
+ const getTargetElStyle = (el) => {
85
+ const offset = getOffset(el);
86
+ const { transform } = getComputedStyle(el);
87
+ return `
88
+ position: absolute;
89
+ transform: ${transform};
90
+ left: ${offset.left}px;
91
+ top: ${offset.top}px;
92
+ width: ${el.clientWidth}px;
93
+ height: ${el.clientHeight}px;
94
+ z-index: ${ZIndex.DRAG_EL};
95
+ `;
96
+ };
69
97
  const getAbsolutePosition = (el, { top, left }) => {
70
98
  const { offsetParent } = el;
71
99
  if (offsetParent) {
@@ -77,13 +105,6 @@
77
105
  }
78
106
  return { left, top };
79
107
  };
80
- const getHost = (targetUrl) => targetUrl.match(/\/\/([^/]+)/)?.[1];
81
- const isSameDomain = (targetUrl = "", source = globalThis.location.host) => {
82
- const isHttpUrl = /^(http[s]?:)?\/\//.test(targetUrl);
83
- if (!isHttpUrl)
84
- return true;
85
- return getHost(targetUrl) === source;
86
- };
87
108
  const isAbsolute = (style) => style.position === "absolute";
88
109
  const isRelative = (style) => style.position === "relative";
89
110
  const isStatic = (style) => style.position === "static";
@@ -127,19 +148,14 @@
127
148
  }
128
149
  return null;
129
150
  };
130
- const createDiv = ({ className, cssText }) => {
131
- const el = globalThis.document.createElement("div");
132
- el.className = className;
133
- el.style.cssText = cssText;
134
- return el;
135
- };
136
151
  const removeSelectedClassName = (doc) => {
137
152
  const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
138
153
  if (oldEl) {
139
- oldEl.classList.remove(SELECTED_CLASS);
140
- oldEl.parentNode?.classList.remove(`${SELECTED_CLASS}-parent`);
154
+ utils.removeClassName(oldEl, SELECTED_CLASS);
155
+ if (oldEl.parentNode)
156
+ utils.removeClassName(oldEl.parentNode, `${SELECTED_CLASS}-parent`);
141
157
  doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
142
- item.classList.remove(`${SELECTED_CLASS}-parents`);
158
+ utils.removeClassName(item, `${SELECTED_CLASS}-parents`);
143
159
  });
144
160
  }
145
161
  };
@@ -150,27 +166,101 @@
150
166
  item.classList.add(`${SELECTED_CLASS}-parents`);
151
167
  });
152
168
  };
169
+ const calcValueByFontsize = (doc, value) => {
170
+ const { fontSize } = doc.documentElement.style;
171
+ if (fontSize) {
172
+ const times = globalThis.parseFloat(fontSize) / 100;
173
+ return Number((value / times).toFixed(2));
174
+ }
175
+ return value;
176
+ };
177
+ const down = (deltaTop, target) => {
178
+ let swapIndex = 0;
179
+ let addUpH = target.clientHeight;
180
+ const brothers = Array.from(target.parentNode?.children || []).filter((node) => !node.id.startsWith(GHOST_EL_ID_PREFIX));
181
+ const index = brothers.indexOf(target);
182
+ const downEls = brothers.slice(index + 1);
183
+ for (let i = 0; i < downEls.length; i++) {
184
+ const ele = downEls[i];
185
+ if (ele.style?.position === "fixed") {
186
+ continue;
187
+ }
188
+ addUpH += ele.clientHeight / 2;
189
+ if (deltaTop <= addUpH) {
190
+ break;
191
+ }
192
+ addUpH += ele.clientHeight / 2;
193
+ swapIndex = i;
194
+ }
195
+ return {
196
+ src: target.id,
197
+ dist: downEls.length && swapIndex > -1 ? downEls[swapIndex].id : target.id
198
+ };
199
+ };
200
+ const up = (deltaTop, target) => {
201
+ const brothers = Array.from(target.parentNode?.children || []).filter((node) => !node.id.startsWith(GHOST_EL_ID_PREFIX));
202
+ const index = brothers.indexOf(target);
203
+ const upEls = brothers.slice(0, index);
204
+ let addUpH = target.clientHeight;
205
+ let swapIndex = upEls.length - 1;
206
+ for (let i = upEls.length - 1; i >= 0; i--) {
207
+ const ele = upEls[i];
208
+ if (!ele)
209
+ continue;
210
+ if (ele.style.position === "fixed")
211
+ continue;
212
+ addUpH += ele.clientHeight / 2;
213
+ if (-deltaTop <= addUpH)
214
+ break;
215
+ addUpH += ele.clientHeight / 2;
216
+ swapIndex = i;
217
+ }
218
+ return {
219
+ src: target.id,
220
+ dist: upEls.length && swapIndex > -1 ? upEls[swapIndex].id : target.id
221
+ };
222
+ };
153
223
 
154
224
  class StageDragResize extends EventEmitter.EventEmitter {
225
+ core;
226
+ mask;
227
+ container;
228
+ target;
229
+ dragEl;
230
+ moveable;
231
+ horizontalGuidelines = [];
232
+ verticalGuidelines = [];
233
+ elementGuidelines = [];
234
+ mode = Mode.ABSOLUTE;
235
+ moveableOptions = {};
236
+ dragStatus = StageDragStatus.END;
237
+ ghostEl;
238
+ moveableHelper;
239
+ isContainerHighlight = false;
155
240
  constructor(config) {
156
241
  super();
157
- this.horizontalGuidelines = [];
158
- this.verticalGuidelines = [];
159
- this.elementGuidelines = [];
160
- this.mode = Mode.ABSOLUTE;
161
- this.moveableOptions = {};
162
- this.dragStatus = "end" /* END */;
163
242
  this.core = config.core;
164
243
  this.container = config.container;
165
- this.dragEl = globalThis.document.createElement("div");
166
- this.container.append(this.dragEl);
244
+ this.mask = config.mask;
245
+ KeyController__default.default.global.keydown("alt", (e) => {
246
+ e.inputEvent.preventDefault();
247
+ this.isContainerHighlight = true;
248
+ });
249
+ KeyController__default.default.global.keyup("alt", (e) => {
250
+ e.inputEvent.preventDefault();
251
+ const doc = this.core.renderer.contentWindow?.document;
252
+ if (doc && this.canContainerHighlight()) {
253
+ utils.removeClassNameByClassName(doc, this.core.containerHighlightClassName);
254
+ }
255
+ this.isContainerHighlight = false;
256
+ });
167
257
  }
168
258
  select(el, event) {
169
259
  const oldTarget = this.target;
170
260
  this.target = el;
171
261
  if (!this.moveable || this.target !== oldTarget) {
172
262
  this.init(el);
173
- this.moveableHelper = MoveableHelper__default["default"].create({
263
+ this.moveableHelper = MoveableHelper__default.default.create({
174
264
  useBeforeRender: true,
175
265
  useRender: false,
176
266
  createAuto: true
@@ -214,11 +304,21 @@
214
304
  this.moveableOptions.verticalGuidelines = [];
215
305
  this.updateMoveable();
216
306
  }
307
+ clearSelectStatus() {
308
+ if (!this.moveable)
309
+ return;
310
+ this.destroyDragEl();
311
+ this.moveable.target = null;
312
+ this.moveable.updateTarget();
313
+ }
314
+ destroyDragEl() {
315
+ this.dragEl?.remove();
316
+ }
217
317
  destroy() {
218
318
  this.moveable?.destroy();
219
319
  this.destroyGhostEl();
220
320
  this.destroyDragEl();
221
- this.dragStatus = "end" /* END */;
321
+ this.dragStatus = StageDragStatus.END;
222
322
  this.removeAllListeners();
223
323
  }
224
324
  init(el) {
@@ -227,7 +327,14 @@
227
327
  }
228
328
  this.mode = getMode(el);
229
329
  this.destroyGhostEl();
230
- this.updateDragEl(el);
330
+ this.destroyDragEl();
331
+ this.dragEl = globalThis.document.createElement("div");
332
+ this.container.append(this.dragEl);
333
+ this.dragEl.style.cssText = getTargetElStyle(el);
334
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
335
+ if (typeof this.core.config.updateDragEl === "function") {
336
+ this.core.config.updateDragEl(this.dragEl, el);
337
+ }
231
338
  this.moveableOptions = this.getOptions({
232
339
  target: this.dragEl
233
340
  });
@@ -238,23 +345,26 @@
238
345
  });
239
346
  this.elementGuidelines = [];
240
347
  if (this.mode === Mode.ABSOLUTE) {
241
- const frame = document.createDocumentFragment();
242
- for (const node of nodes) {
243
- const { width, height } = node.getBoundingClientRect();
244
- if (node === this.target)
245
- continue;
246
- const { left, top } = getOffset(node);
247
- const elementGuideline = document.createElement("div");
248
- elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
249
- this.elementGuidelines.push(elementGuideline);
250
- frame.append(elementGuideline);
251
- }
252
- this.container.append(frame);
348
+ this.container.append(this.createGuidelineElements(nodes));
349
+ }
350
+ }
351
+ createGuidelineElements(nodes) {
352
+ const frame = globalThis.document.createDocumentFragment();
353
+ for (const node of nodes) {
354
+ const { width, height } = node.getBoundingClientRect();
355
+ if (node === this.target)
356
+ continue;
357
+ const { left, top } = getOffset(node);
358
+ const elementGuideline = globalThis.document.createElement("div");
359
+ elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
360
+ this.elementGuidelines.push(elementGuideline);
361
+ frame.append(elementGuideline);
253
362
  }
363
+ return frame;
254
364
  }
255
365
  initMoveable() {
256
366
  this.moveable?.destroy();
257
- this.moveable = new Moveable__default["default"](this.container, {
367
+ this.moveable = new Moveable__default.default(this.container, {
258
368
  ...this.moveableOptions
259
369
  });
260
370
  this.bindResizeEvent();
@@ -272,7 +382,7 @@
272
382
  this.moveable.on("resizeStart", (e) => {
273
383
  if (!this.target)
274
384
  return;
275
- this.dragStatus = "start" /* START */;
385
+ this.dragStatus = StageDragStatus.START;
276
386
  this.moveableHelper?.onResizeStart(e);
277
387
  frame.top = this.target.offsetTop;
278
388
  frame.left = this.target.offsetLeft;
@@ -281,7 +391,7 @@
281
391
  if (!this.moveable || !this.target || !this.dragEl)
282
392
  return;
283
393
  const { beforeTranslate } = drag;
284
- this.dragStatus = "ing" /* ING */;
394
+ this.dragStatus = StageDragStatus.ING;
285
395
  this.moveableHelper?.onResize(e);
286
396
  if (this.mode === Mode.SORTABLE) {
287
397
  this.target.style.top = "0px";
@@ -292,7 +402,7 @@
292
402
  this.target.style.width = `${width}px`;
293
403
  this.target.style.height = `${height}px`;
294
404
  }).on("resizeEnd", () => {
295
- this.dragStatus = "end" /* END */;
405
+ this.dragStatus = StageDragStatus.END;
296
406
  this.update(true);
297
407
  });
298
408
  }
@@ -303,10 +413,13 @@
303
413
  left: 0,
304
414
  top: 0
305
415
  };
416
+ let timeout;
417
+ const { contentWindow } = this.core.renderer;
418
+ const doc = contentWindow?.document;
306
419
  this.moveable.on("dragStart", (e) => {
307
420
  if (!this.target)
308
421
  throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
309
- this.dragStatus = "start" /* START */;
422
+ this.dragStatus = StageDragStatus.START;
310
423
  this.moveableHelper?.onDragStart(e);
311
424
  if (this.mode === Mode.SORTABLE) {
312
425
  this.ghostEl = this.generateGhostEl(this.target);
@@ -316,7 +429,14 @@
316
429
  }).on("drag", (e) => {
317
430
  if (!this.target || !this.dragEl)
318
431
  return;
319
- this.dragStatus = "ing" /* ING */;
432
+ if (timeout) {
433
+ globalThis.clearTimeout(timeout);
434
+ timeout = void 0;
435
+ }
436
+ if (this.canContainerHighlight()) {
437
+ timeout = this.core.getAddContainerHighlightClassNameTimeout(e.inputEvent, [this.target]);
438
+ }
439
+ this.dragStatus = StageDragStatus.ING;
320
440
  if (this.ghostEl) {
321
441
  this.ghostEl.style.top = `${frame.top + e.beforeTranslate[1]}px`;
322
442
  return;
@@ -325,16 +445,28 @@
325
445
  this.target.style.left = `${frame.left + e.beforeTranslate[0]}px`;
326
446
  this.target.style.top = `${frame.top + e.beforeTranslate[1]}px`;
327
447
  }).on("dragEnd", () => {
328
- if (this.dragStatus === "ing" /* ING */) {
329
- switch (this.mode) {
330
- case Mode.SORTABLE:
331
- this.sort();
332
- break;
333
- default:
334
- this.update();
448
+ if (timeout) {
449
+ globalThis.clearTimeout(timeout);
450
+ timeout = void 0;
451
+ }
452
+ let parentEl = null;
453
+ if (doc && this.canContainerHighlight()) {
454
+ parentEl = utils.removeClassNameByClassName(doc, this.core.containerHighlightClassName);
455
+ }
456
+ if (this.dragStatus === StageDragStatus.ING) {
457
+ if (parentEl) {
458
+ this.update(false, parentEl);
459
+ } else {
460
+ switch (this.mode) {
461
+ case Mode.SORTABLE:
462
+ this.sort();
463
+ break;
464
+ default:
465
+ this.update();
466
+ }
335
467
  }
336
468
  }
337
- this.dragStatus = "end" /* END */;
469
+ this.dragStatus = StageDragStatus.END;
338
470
  this.destroyGhostEl();
339
471
  });
340
472
  }
@@ -342,17 +474,17 @@
342
474
  if (!this.moveable)
343
475
  throw new Error("moveable \u672A\u521D\u59CB\u5316");
344
476
  this.moveable.on("rotateStart", (e) => {
345
- this.dragStatus = "start" /* START */;
477
+ this.dragStatus = StageDragStatus.START;
346
478
  this.moveableHelper?.onRotateStart(e);
347
479
  }).on("rotate", (e) => {
348
480
  if (!this.target || !this.dragEl)
349
481
  return;
350
- this.dragStatus = "ing" /* ING */;
482
+ this.dragStatus = StageDragStatus.ING;
351
483
  this.moveableHelper?.onRotate(e);
352
484
  const frame = this.moveableHelper?.getFrame(e.target);
353
485
  this.target.style.transform = frame?.toCSSObject().transform || "";
354
486
  }).on("rotateEnd", (e) => {
355
- this.dragStatus = "end" /* END */;
487
+ this.dragStatus = StageDragStatus.END;
356
488
  const frame = this.moveableHelper?.getFrame(e.target);
357
489
  this.emit("update", {
358
490
  el: this.target,
@@ -366,17 +498,17 @@
366
498
  if (!this.moveable)
367
499
  throw new Error("moveable \u672A\u521D\u59CB\u5316");
368
500
  this.moveable.on("scaleStart", (e) => {
369
- this.dragStatus = "start" /* START */;
501
+ this.dragStatus = StageDragStatus.START;
370
502
  this.moveableHelper?.onScaleStart(e);
371
503
  }).on("scale", (e) => {
372
504
  if (!this.target || !this.dragEl)
373
505
  return;
374
- this.dragStatus = "ing" /* ING */;
506
+ this.dragStatus = StageDragStatus.ING;
375
507
  this.moveableHelper?.onScale(e);
376
508
  const frame = this.moveableHelper?.getFrame(e.target);
377
509
  this.target.style.transform = frame?.toCSSObject().transform || "";
378
510
  }).on("scaleEnd", (e) => {
379
- this.dragStatus = "end" /* END */;
511
+ this.dragStatus = StageDragStatus.END;
380
512
  const frame = this.moveableHelper?.getFrame(e.target);
381
513
  this.emit("update", {
382
514
  el: this.target,
@@ -405,17 +537,32 @@
405
537
  });
406
538
  }
407
539
  }
408
- update(isResize = false) {
540
+ update(isResize = false, parentEl = null) {
409
541
  if (!this.target)
410
542
  return;
543
+ const { contentWindow } = this.core.renderer;
544
+ const doc = contentWindow?.document;
545
+ if (!doc)
546
+ return;
411
547
  const offset = this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: this.target.offsetLeft, top: this.target.offsetTop };
412
- const left = this.calcValueByFontsize(offset.left);
413
- const top = this.calcValueByFontsize(offset.top);
414
- const width = this.calcValueByFontsize(this.target.clientWidth);
415
- const height = this.calcValueByFontsize(this.target.clientHeight);
548
+ let left = calcValueByFontsize(doc, offset.left);
549
+ let top = calcValueByFontsize(doc, offset.top);
550
+ const width = calcValueByFontsize(doc, this.target.clientWidth);
551
+ const height = calcValueByFontsize(doc, this.target.clientHeight);
552
+ if (parentEl && this.mode === Mode.ABSOLUTE && this.dragEl) {
553
+ const [translateX, translateY] = this.moveableHelper?.getFrame(this.dragEl).properties.transform.translate.value;
554
+ const { left: parentLeft, top: parentTop } = getOffset(parentEl);
555
+ left = calcValueByFontsize(doc, this.dragEl.offsetLeft) + parseFloat(translateX) - calcValueByFontsize(doc, parentLeft);
556
+ top = calcValueByFontsize(doc, this.dragEl.offsetTop) + parseFloat(translateY) - calcValueByFontsize(doc, parentTop);
557
+ }
416
558
  this.emit("update", {
417
- el: this.target,
418
- style: isResize ? { left, top, width, height } : { left, top }
559
+ data: [
560
+ {
561
+ el: this.target,
562
+ style: isResize ? { left, top, width, height } : { left, top }
563
+ }
564
+ ],
565
+ parentEl
419
566
  });
420
567
  }
421
568
  generateGhostEl(el) {
@@ -423,6 +570,7 @@
423
570
  this.destroyGhostEl();
424
571
  }
425
572
  const ghostEl = el.cloneNode(true);
573
+ this.setGhostElChildrenId(ghostEl);
426
574
  const { top, left } = getAbsolutePosition(el, getOffset(el));
427
575
  ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
428
576
  ghostEl.style.zIndex = ZIndex.GHOST_EL;
@@ -433,30 +581,20 @@
433
581
  el.after(ghostEl);
434
582
  return ghostEl;
435
583
  }
584
+ setGhostElChildrenId(el) {
585
+ for (const child of Array.from(el.children)) {
586
+ if (child.id) {
587
+ child.id = `${GHOST_EL_ID_PREFIX}${child.id}`;
588
+ }
589
+ if (child.children.length) {
590
+ this.setGhostElChildrenId(child);
591
+ }
592
+ }
593
+ }
436
594
  destroyGhostEl() {
437
595
  this.ghostEl?.remove();
438
596
  this.ghostEl = void 0;
439
597
  }
440
- updateDragEl(el) {
441
- const offset = getOffset(el);
442
- const { transform } = getComputedStyle(el);
443
- this.dragEl.style.cssText = `
444
- position: absolute;
445
- transform: ${transform};
446
- left: ${offset.left}px;
447
- top: ${offset.top}px;
448
- width: ${el.clientWidth}px;
449
- height: ${el.clientHeight}px;
450
- z-index: ${ZIndex.DRAG_EL};
451
- `;
452
- this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
453
- if (typeof this.core.config.updateDragEl === "function") {
454
- this.core.config.updateDragEl(this.dragEl, el);
455
- }
456
- }
457
- destroyDragEl() {
458
- this.dragEl?.remove();
459
- }
460
598
  getOptions(options = {}) {
461
599
  if (!this.target)
462
600
  return {};
@@ -507,7 +645,7 @@
507
645
  bounds: {
508
646
  top: 0,
509
647
  left: -1,
510
- right: this.container.clientWidth,
648
+ right: this.container.clientWidth - 1,
511
649
  bottom: this.container.clientHeight,
512
650
  ...moveableOptions.bounds || {}
513
651
  },
@@ -515,64 +653,17 @@
515
653
  ...moveableOptions
516
654
  };
517
655
  }
518
- calcValueByFontsize(value) {
519
- const { contentWindow } = this.core.renderer;
520
- const fontSize = contentWindow?.document.documentElement.style.fontSize;
521
- if (fontSize) {
522
- const times = globalThis.parseFloat(fontSize) / 100;
523
- return (value / times).toFixed(2);
524
- }
525
- return value;
656
+ canContainerHighlight() {
657
+ return this.core.containerHighlightType === ContainerHighlightType.DEFAULT || this.core.containerHighlightType === ContainerHighlightType.ALT && this.isContainerHighlight;
526
658
  }
527
659
  }
528
- const down = (deltaTop, target) => {
529
- let swapIndex = 0;
530
- let addUpH = target.clientHeight;
531
- const brothers = Array.from(target.parentNode?.children || []).filter((node) => !node.id.startsWith(GHOST_EL_ID_PREFIX));
532
- const index = brothers.indexOf(target);
533
- const downEls = brothers.slice(index + 1);
534
- for (let i = 0; i < downEls.length; i++) {
535
- const ele = downEls[i];
536
- if (ele.style?.position === "fixed") {
537
- continue;
538
- }
539
- addUpH += ele.clientHeight / 2;
540
- if (deltaTop <= addUpH) {
541
- break;
542
- }
543
- addUpH += ele.clientHeight / 2;
544
- swapIndex = i;
545
- }
546
- return {
547
- src: target.id,
548
- dist: downEls.length && swapIndex > -1 ? downEls[swapIndex].id : target.id
549
- };
550
- };
551
- const up = (deltaTop, target) => {
552
- const brothers = Array.from(target.parentNode?.children || []).filter((node) => !node.id.startsWith(GHOST_EL_ID_PREFIX));
553
- const index = brothers.indexOf(target);
554
- const upEls = brothers.slice(0, index);
555
- let addUpH = target.clientHeight;
556
- let swapIndex = upEls.length - 1;
557
- for (let i = upEls.length - 1; i >= 0; i--) {
558
- const ele = upEls[i];
559
- if (!ele)
560
- continue;
561
- if (ele.style.position === "fixed")
562
- continue;
563
- addUpH += ele.clientHeight / 2;
564
- if (-deltaTop <= addUpH)
565
- break;
566
- addUpH += ele.clientHeight / 2;
567
- swapIndex = i;
568
- }
569
- return {
570
- src: target.id,
571
- dist: upEls.length && swapIndex > -1 ? upEls[swapIndex].id : target.id
572
- };
573
- };
574
660
 
575
661
  class TargetCalibrate extends EventEmitter.EventEmitter {
662
+ parent;
663
+ mask;
664
+ dr;
665
+ core;
666
+ operationEl;
576
667
  constructor(config) {
577
668
  super();
578
669
  this.parent = config.parent;
@@ -592,6 +683,7 @@
592
683
  top: ${top}px;
593
684
  width: ${el.clientWidth}px;
594
685
  height: ${el.clientHeight}px;
686
+ z-index: ${ZIndex.DRAG_EL};
595
687
  `;
596
688
  this.operationEl.id = `${prefix}${el.id}`;
597
689
  if (typeof this.core.config.updateDragEl === "function") {
@@ -639,6 +731,11 @@
639
731
  }
640
732
 
641
733
  class StageHighlight extends EventEmitter.EventEmitter {
734
+ core;
735
+ container;
736
+ target;
737
+ moveable;
738
+ calibrationTarget;
642
739
  constructor(config) {
643
740
  super();
644
741
  this.core = config.core;
@@ -655,11 +752,11 @@
655
752
  return;
656
753
  this.target = el;
657
754
  this.moveable?.destroy();
658
- this.moveable = new Moveable__default["default"](this.container, {
755
+ this.moveable = new Moveable__default.default(this.container, {
659
756
  target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
660
757
  origin: false,
661
758
  rootContainer: this.core.container,
662
- zoom: 1
759
+ zoom: 2
663
760
  });
664
761
  }
665
762
  clearHighlight() {
@@ -675,42 +772,15 @@
675
772
  }
676
773
  }
677
774
 
678
- class Rule extends EventEmitter__default["default"] {
775
+ class Rule extends EventEmitter__default.default {
776
+ hGuides;
777
+ vGuides;
778
+ horizontalGuidelines = [];
779
+ verticalGuidelines = [];
780
+ container;
781
+ containerResizeObserver;
679
782
  constructor(container) {
680
783
  super();
681
- this.horizontalGuidelines = [];
682
- this.verticalGuidelines = [];
683
- this.getGuidesStyle = (type) => ({
684
- position: "fixed",
685
- zIndex: 1,
686
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
687
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
688
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
689
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
690
- });
691
- this.createGuides = (type, defaultGuides = []) => new Guides__default["default"](this.container, {
692
- type,
693
- defaultGuides,
694
- displayDragPos: true,
695
- backgroundColor: "#fff",
696
- lineColor: "#000",
697
- textColor: "#000",
698
- style: this.getGuidesStyle(type)
699
- });
700
- this.hGuidesChangeGuidesHandler = (e) => {
701
- this.horizontalGuidelines = e.guides;
702
- this.emit("changeGuides", {
703
- type: GuidesType.HORIZONTAL,
704
- guides: this.horizontalGuidelines
705
- });
706
- };
707
- this.vGuidesChangeGuidesHandler = (e) => {
708
- this.verticalGuidelines = e.guides;
709
- this.emit("changeGuides", {
710
- type: GuidesType.VERTICAL,
711
- guides: this.verticalGuidelines
712
- });
713
- };
714
784
  this.container = container;
715
785
  this.hGuides = this.createGuides(GuidesType.HORIZONTAL, this.horizontalGuidelines);
716
786
  this.vGuides = this.createGuides(GuidesType.VERTICAL, this.verticalGuidelines);
@@ -782,18 +852,45 @@
782
852
  this.containerResizeObserver.disconnect();
783
853
  this.removeAllListeners();
784
854
  }
855
+ getGuidesStyle = (type) => ({
856
+ position: "fixed",
857
+ zIndex: 1,
858
+ left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
859
+ top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
860
+ width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
861
+ height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
862
+ });
863
+ createGuides = (type, defaultGuides = []) => new Guides__default.default(this.container, {
864
+ type,
865
+ defaultGuides,
866
+ displayDragPos: true,
867
+ backgroundColor: "#fff",
868
+ lineColor: "#000",
869
+ textColor: "#000",
870
+ style: this.getGuidesStyle(type)
871
+ });
872
+ hGuidesChangeGuidesHandler = (e) => {
873
+ this.horizontalGuidelines = e.guides;
874
+ this.emit("changeGuides", {
875
+ type: GuidesType.HORIZONTAL,
876
+ guides: this.horizontalGuidelines
877
+ });
878
+ };
879
+ vGuidesChangeGuidesHandler = (e) => {
880
+ this.verticalGuidelines = e.guides;
881
+ this.emit("changeGuides", {
882
+ type: GuidesType.VERTICAL,
883
+ guides: this.verticalGuidelines
884
+ });
885
+ };
785
886
  }
786
887
 
787
888
  const wrapperClassName = "editor-mask-wrapper";
788
889
  const throttleTime = 100;
789
890
  const hideScrollbar = () => {
790
- const style = globalThis.document.createElement("style");
791
- style.innerHTML = `
792
- .${wrapperClassName}::-webkit-scrollbar { width: 0 !important; display: none }
793
- `;
794
- globalThis.document.head.appendChild(style);
891
+ utils.injectStyle(globalThis.document, `.${wrapperClassName}::-webkit-scrollbar { width: 0 !important; display: none }`);
795
892
  };
796
- const createContent = () => createDiv({
893
+ const createContent = () => utils.createDiv({
797
894
  className: "editor-mask",
798
895
  cssText: `
799
896
  position: absolute;
@@ -803,7 +900,7 @@
803
900
  `
804
901
  });
805
902
  const createWrapper = () => {
806
- const el = createDiv({
903
+ const el = utils.createDiv({
807
904
  className: wrapperClassName,
808
905
  cssText: `
809
906
  position: absolute;
@@ -819,66 +916,30 @@
819
916
  return el;
820
917
  };
821
918
  class StageMask extends Rule {
919
+ content = createContent();
920
+ wrapper;
921
+ core;
922
+ page = null;
923
+ pageScrollParent = null;
924
+ scrollTop = 0;
925
+ scrollLeft = 0;
926
+ width = 0;
927
+ height = 0;
928
+ wrapperHeight = 0;
929
+ wrapperWidth = 0;
930
+ maxScrollTop = 0;
931
+ maxScrollLeft = 0;
932
+ intersectionObserver = null;
933
+ isMultiSelectStatus = false;
934
+ mode = Mode.ABSOLUTE;
935
+ pageResizeObserver = null;
936
+ wrapperResizeObserver = null;
937
+ highlightHandler = lodashEs.throttle((event) => {
938
+ this.emit("highlight", event);
939
+ }, throttleTime);
822
940
  constructor(config) {
823
941
  const wrapper = createWrapper();
824
942
  super(wrapper);
825
- this.content = createContent();
826
- this.page = null;
827
- this.pageScrollParent = null;
828
- this.scrollTop = 0;
829
- this.scrollLeft = 0;
830
- this.width = 0;
831
- this.height = 0;
832
- this.wrapperHeight = 0;
833
- this.wrapperWidth = 0;
834
- this.maxScrollTop = 0;
835
- this.maxScrollLeft = 0;
836
- this.intersectionObserver = null;
837
- this.mode = Mode.ABSOLUTE;
838
- this.pageResizeObserver = null;
839
- this.wrapperResizeObserver = null;
840
- this.highlightHandler = lodashEs.throttle((event) => {
841
- this.emit("highlight", event);
842
- }, throttleTime);
843
- this.mouseDownHandler = (event) => {
844
- this.emit("clearHighlight");
845
- event.stopImmediatePropagation();
846
- event.stopPropagation();
847
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
848
- return;
849
- if (event.target.className.indexOf("moveable-control") !== -1) {
850
- return;
851
- }
852
- this.content.removeEventListener("mousemove", this.highlightHandler);
853
- this.emit("beforeSelect", event);
854
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
855
- };
856
- this.mouseUpHandler = () => {
857
- globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
858
- this.content.addEventListener("mousemove", this.highlightHandler);
859
- this.emit("select");
860
- };
861
- this.mouseWheelHandler = (event) => {
862
- this.emit("clearHighlight");
863
- if (!this.page)
864
- throw new Error("page \u672A\u521D\u59CB\u5316");
865
- const { deltaY, deltaX } = event;
866
- if (this.page.clientHeight < this.wrapperHeight && deltaY)
867
- return;
868
- if (this.page.clientWidth < this.wrapperWidth && deltaX)
869
- return;
870
- if (this.maxScrollTop > 0) {
871
- this.scrollTop = this.scrollTop + deltaY;
872
- }
873
- if (this.maxScrollLeft > 0) {
874
- this.scrollLeft = this.scrollLeft + deltaX;
875
- }
876
- this.scroll();
877
- this.emit("scroll", event);
878
- };
879
- this.mouseLeaveHandler = () => {
880
- setTimeout(() => this.emit("clearHighlight"), throttleTime);
881
- };
882
943
  this.wrapper = wrapper;
883
944
  this.core = config.core;
884
945
  this.content.addEventListener("mousedown", this.mouseDownHandler);
@@ -886,6 +947,16 @@
886
947
  this.content.addEventListener("wheel", this.mouseWheelHandler);
887
948
  this.content.addEventListener("mousemove", this.highlightHandler);
888
949
  this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
950
+ const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
951
+ const ctrl = isMac ? "meta" : "ctrl";
952
+ KeyController__default.default.global.keydown(ctrl, (e) => {
953
+ e.inputEvent.preventDefault();
954
+ this.isMultiSelectStatus = true;
955
+ });
956
+ KeyController__default.default.global.keyup(ctrl, (e) => {
957
+ e.inputEvent.preventDefault();
958
+ this.isMultiSelectStatus = false;
959
+ });
889
960
  }
890
961
  setMode(mode) {
891
962
  this.mode = mode;
@@ -1014,51 +1085,242 @@
1014
1085
  if (this.maxScrollLeft < this.scrollLeft)
1015
1086
  this.scrollLeft = this.maxScrollLeft;
1016
1087
  }
1088
+ mouseDownHandler = (event) => {
1089
+ this.emit("clearHighlight");
1090
+ event.stopImmediatePropagation();
1091
+ event.stopPropagation();
1092
+ if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
1093
+ return;
1094
+ if (!this.isMultiSelectStatus && event.target.className.indexOf("moveable-area") !== -1) {
1095
+ return;
1096
+ }
1097
+ if (event.target.className.indexOf("moveable-control") !== -1) {
1098
+ return;
1099
+ }
1100
+ this.content.removeEventListener("mousemove", this.highlightHandler);
1101
+ if (this.isMultiSelectStatus) {
1102
+ this.emit("beforeMultiSelect", event);
1103
+ } else {
1104
+ this.emit("beforeSelect", event);
1105
+ }
1106
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
1107
+ };
1108
+ mouseUpHandler = () => {
1109
+ globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
1110
+ this.content.addEventListener("mousemove", this.highlightHandler);
1111
+ if (!this.isMultiSelectStatus) {
1112
+ this.emit("select");
1113
+ }
1114
+ };
1115
+ mouseWheelHandler = (event) => {
1116
+ this.emit("clearHighlight");
1117
+ if (!this.page)
1118
+ throw new Error("page \u672A\u521D\u59CB\u5316");
1119
+ const { deltaY, deltaX } = event;
1120
+ if (this.page.clientHeight < this.wrapperHeight && deltaY)
1121
+ return;
1122
+ if (this.page.clientWidth < this.wrapperWidth && deltaX)
1123
+ return;
1124
+ if (this.maxScrollTop > 0) {
1125
+ this.scrollTop = this.scrollTop + deltaY;
1126
+ }
1127
+ if (this.maxScrollLeft > 0) {
1128
+ this.scrollLeft = this.scrollLeft + deltaX;
1129
+ }
1130
+ this.scroll();
1131
+ this.emit("scroll", event);
1132
+ };
1133
+ mouseLeaveHandler = () => {
1134
+ setTimeout(() => this.emit("clearHighlight"), throttleTime);
1135
+ };
1017
1136
  }
1018
1137
 
1019
- class StageRender extends EventEmitter.EventEmitter {
1020
- constructor({ core }) {
1138
+ class StageMultiDragResize extends EventEmitter.EventEmitter {
1139
+ core;
1140
+ mask;
1141
+ container;
1142
+ targetList = [];
1143
+ dragElList = [];
1144
+ moveableForMulti;
1145
+ dragStatus = StageDragStatus.END;
1146
+ multiMoveableHelper;
1147
+ constructor(config) {
1021
1148
  super();
1022
- this.contentWindow = null;
1023
- this.runtime = null;
1024
- this.getMagicApi = () => ({
1025
- onPageElUpdate: (el) => this.emit("page-el-update", el),
1026
- onRuntimeReady: (runtime) => {
1027
- this.runtime = runtime;
1028
- globalThis.runtime = runtime;
1029
- this.emit("runtime-ready", runtime);
1149
+ this.core = config.core;
1150
+ this.container = config.container;
1151
+ this.mask = config.mask;
1152
+ }
1153
+ multiSelect(els) {
1154
+ this.targetList = els;
1155
+ this.core.dr.destroyDragEl();
1156
+ this.destroyDragElList();
1157
+ this.dragElList = els.map((elItem) => {
1158
+ const dragElDiv = globalThis.document.createElement("div");
1159
+ this.container.append(dragElDiv);
1160
+ dragElDiv.style.cssText = getTargetElStyle(elItem);
1161
+ dragElDiv.id = `${DRAG_EL_ID_PREFIX}${elItem.id}`;
1162
+ if (typeof this.core.config.updateDragEl === "function") {
1163
+ this.core.config.updateDragEl(dragElDiv, elItem);
1030
1164
  }
1165
+ return dragElDiv;
1031
1166
  });
1032
- this.getRuntime = () => {
1033
- if (this.runtime)
1034
- return Promise.resolve(this.runtime);
1035
- return new Promise((resolve) => {
1036
- const listener = (runtime) => {
1037
- this.off("runtime-ready", listener);
1038
- resolve(runtime);
1039
- };
1040
- this.on("runtime-ready", listener);
1167
+ this.moveableForMulti?.destroy();
1168
+ this.multiMoveableHelper?.clear();
1169
+ this.moveableForMulti = new Moveable__default.default(this.container, this.getOptions({
1170
+ target: this.dragElList
1171
+ }));
1172
+ this.multiMoveableHelper = MoveableHelper__default.default.create({
1173
+ useBeforeRender: true,
1174
+ useRender: false,
1175
+ createAuto: true
1176
+ });
1177
+ const frames = [];
1178
+ this.moveableForMulti.on("dragGroupStart", (params) => {
1179
+ const { events } = params;
1180
+ this.multiMoveableHelper?.onDragGroupStart(params);
1181
+ events.forEach((ev) => {
1182
+ const matchEventTarget = this.targetList.find((targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1183
+ if (!matchEventTarget)
1184
+ return;
1185
+ frames.push({
1186
+ left: matchEventTarget.offsetLeft,
1187
+ top: matchEventTarget.offsetTop,
1188
+ id: matchEventTarget.id
1189
+ });
1041
1190
  });
1042
- };
1043
- this.loadHandler = async () => {
1044
- this.contentWindow = this.iframe?.contentWindow;
1045
- this.contentWindow.magic = this.getMagicApi();
1046
- if (this.render) {
1047
- const el = await this.render(this.core);
1048
- if (el) {
1049
- this.iframe?.contentDocument?.body?.appendChild(el);
1191
+ this.dragStatus = StageDragStatus.START;
1192
+ }).on("dragGroup", (params) => {
1193
+ const { events } = params;
1194
+ events.forEach((ev) => {
1195
+ const frameSnapShot = frames.find((frameItem) => frameItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1196
+ if (!frameSnapShot)
1197
+ return;
1198
+ const targeEl = this.targetList.find((targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1199
+ if (!targeEl)
1200
+ return;
1201
+ const isParentIncluded = this.targetList.find((targetItem) => targetItem.id === targeEl.parentElement?.id);
1202
+ if (!isParentIncluded) {
1203
+ targeEl.style.left = `${frameSnapShot.left + ev.beforeTranslate[0]}px`;
1204
+ targeEl.style.top = `${frameSnapShot.top + ev.beforeTranslate[1]}px`;
1050
1205
  }
1206
+ });
1207
+ this.multiMoveableHelper?.onDragGroup(params);
1208
+ this.dragStatus = StageDragStatus.ING;
1209
+ }).on("dragGroupEnd", () => {
1210
+ this.update();
1211
+ this.dragStatus = StageDragStatus.END;
1212
+ }).on("clickGroup", (params) => {
1213
+ const { inputTarget, targets } = params;
1214
+ if (!this.mask.isMultiSelectStatus && targets.length > 1 && targets.includes(inputTarget)) {
1215
+ this.emit("select", inputTarget.id.replace(DRAG_EL_ID_PREFIX, ""));
1051
1216
  }
1052
- this.emit("onload");
1053
- this.contentWindow.postMessage({
1054
- tmagicRuntimeReady: true
1055
- }, "*");
1217
+ });
1218
+ }
1219
+ canSelect(el, stop) {
1220
+ if (el.className.includes(PAGE_CLASS)) {
1221
+ this.core.highlightedDom = void 0;
1222
+ this.core.highlightLayer.clearHighlight();
1223
+ stop();
1224
+ return false;
1225
+ }
1226
+ const currentTargetMode = getMode(el);
1227
+ let selectedDomMode = "";
1228
+ if (this.core.selectedDom?.className.includes(PAGE_CLASS)) {
1229
+ return true;
1230
+ }
1231
+ if (this.targetList.length === 0 && this.core.selectedDom) {
1232
+ selectedDomMode = getMode(this.core.selectedDom);
1233
+ } else if (this.targetList.length > 0) {
1234
+ selectedDomMode = getMode(this.targetList[0]);
1235
+ }
1236
+ if (currentTargetMode !== selectedDomMode) {
1237
+ return false;
1238
+ }
1239
+ return true;
1240
+ }
1241
+ clearSelectStatus() {
1242
+ if (!this.moveableForMulti)
1243
+ return;
1244
+ this.destroyDragElList();
1245
+ this.moveableForMulti.target = null;
1246
+ this.moveableForMulti.updateTarget();
1247
+ this.targetList = [];
1248
+ }
1249
+ destroy() {
1250
+ this.moveableForMulti?.destroy();
1251
+ this.destroyDragElList();
1252
+ }
1253
+ destroyDragElList() {
1254
+ this.dragElList.forEach((dragElItem) => dragElItem?.remove());
1255
+ }
1256
+ update(isResize = false) {
1257
+ if (this.targetList.length === 0)
1258
+ return;
1259
+ const { contentWindow } = this.core.renderer;
1260
+ const doc = contentWindow?.document;
1261
+ if (!doc)
1262
+ return;
1263
+ this.emit("update", {
1264
+ data: this.targetList.map((targetItem) => {
1265
+ const offset = { left: targetItem.offsetLeft, top: targetItem.offsetTop };
1266
+ const left = calcValueByFontsize(doc, offset.left);
1267
+ const top = calcValueByFontsize(doc, offset.top);
1268
+ const width = calcValueByFontsize(doc, targetItem.clientWidth);
1269
+ const height = calcValueByFontsize(doc, targetItem.clientHeight);
1270
+ return {
1271
+ el: targetItem,
1272
+ style: isResize ? { left, top, width, height } : { left, top }
1273
+ };
1274
+ }),
1275
+ parentEl: null
1276
+ });
1277
+ }
1278
+ getOptions(options = {}) {
1279
+ let { multiMoveableOptions = {} } = this.core.config;
1280
+ if (typeof multiMoveableOptions === "function") {
1281
+ multiMoveableOptions = multiMoveableOptions(this.core);
1282
+ }
1283
+ return {
1284
+ defaultGroupRotate: 0,
1285
+ defaultGroupOrigin: "50% 50%",
1286
+ draggable: true,
1287
+ resizable: false,
1288
+ throttleDrag: 0,
1289
+ startDragRotate: 0,
1290
+ throttleDragRotate: 0,
1291
+ zoom: 1,
1292
+ origin: true,
1293
+ padding: { left: 0, top: 0, right: 0, bottom: 0 },
1294
+ snappable: true,
1295
+ bounds: {
1296
+ top: 0,
1297
+ left: -1,
1298
+ right: this.container.clientWidth - 1,
1299
+ bottom: this.container.clientHeight,
1300
+ ...multiMoveableOptions.bounds || {}
1301
+ },
1302
+ ...options,
1303
+ ...multiMoveableOptions
1056
1304
  };
1305
+ }
1306
+ }
1307
+
1308
+ 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";
1309
+
1310
+ class StageRender extends EventEmitter.EventEmitter {
1311
+ contentWindow = null;
1312
+ runtime = null;
1313
+ iframe;
1314
+ runtimeUrl;
1315
+ core;
1316
+ render;
1317
+ constructor({ core }) {
1318
+ super();
1057
1319
  this.core = core;
1058
1320
  this.runtimeUrl = core.config.runtimeUrl || "";
1059
1321
  this.render = core.config.render;
1060
1322
  this.iframe = globalThis.document.createElement("iframe");
1061
- this.iframe.src = isSameDomain(this.runtimeUrl) ? this.runtimeUrl : "";
1323
+ this.iframe.src = utils.isSameDomain(this.runtimeUrl) ? this.runtimeUrl : "";
1062
1324
  this.iframe.style.cssText = `
1063
1325
  border: 0;
1064
1326
  width: 100%;
@@ -1066,20 +1328,39 @@
1066
1328
  `;
1067
1329
  this.iframe.addEventListener("load", this.loadHandler);
1068
1330
  }
1331
+ getMagicApi = () => ({
1332
+ onPageElUpdate: (el) => this.emit("page-el-update", el),
1333
+ onRuntimeReady: (runtime) => {
1334
+ this.runtime = runtime;
1335
+ globalThis.runtime = runtime;
1336
+ this.emit("runtime-ready", runtime);
1337
+ }
1338
+ });
1069
1339
  async mount(el) {
1070
- if (this.iframe) {
1071
- if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
1072
- let html = await fetch(this.runtimeUrl).then((res) => res.text());
1073
- const base = `${location.protocol}//${getHost(this.runtimeUrl)}`;
1074
- html = html.replace("<head>", `<head>
1075
- <base href="${base}">`);
1076
- this.iframe.srcdoc = html;
1077
- }
1078
- el.appendChild(this.iframe);
1079
- } else {
1340
+ if (!this.iframe) {
1080
1341
  throw Error("mount \u5931\u8D25");
1081
1342
  }
1082
- }
1343
+ if (!utils.isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
1344
+ let html = await fetch(this.runtimeUrl).then((res) => res.text());
1345
+ const base = `${location.protocol}//${utils.getHost(this.runtimeUrl)}`;
1346
+ html = html.replace("<head>", `<head>
1347
+ <base href="${base}">`);
1348
+ this.iframe.srcdoc = html;
1349
+ }
1350
+ el.appendChild(this.iframe);
1351
+ this.postTmagicRuntimeReady();
1352
+ }
1353
+ getRuntime = () => {
1354
+ if (this.runtime)
1355
+ return Promise.resolve(this.runtime);
1356
+ return new Promise((resolve) => {
1357
+ const listener = (runtime) => {
1358
+ this.off("runtime-ready", listener);
1359
+ resolve(runtime);
1360
+ };
1361
+ this.on("runtime-ready", listener);
1362
+ });
1363
+ };
1083
1364
  destroy() {
1084
1365
  this.iframe?.removeEventListener("load", this.loadHandler);
1085
1366
  this.contentWindow = null;
@@ -1087,18 +1368,60 @@
1087
1368
  this.iframe = void 0;
1088
1369
  this.removeAllListeners();
1089
1370
  }
1371
+ loadHandler = async () => {
1372
+ if (!this.contentWindow?.magic) {
1373
+ this.postTmagicRuntimeReady();
1374
+ }
1375
+ if (!this.contentWindow)
1376
+ return;
1377
+ if (this.render) {
1378
+ const el = await this.render(this.core);
1379
+ if (el) {
1380
+ this.contentWindow.document?.body?.appendChild(el);
1381
+ }
1382
+ }
1383
+ this.emit("onload");
1384
+ utils.injectStyle(this.contentWindow.document, style);
1385
+ };
1386
+ postTmagicRuntimeReady() {
1387
+ this.contentWindow = this.iframe?.contentWindow;
1388
+ this.contentWindow.magic = this.getMagicApi();
1389
+ this.contentWindow.postMessage({
1390
+ tmagicRuntimeReady: true
1391
+ }, "*");
1392
+ }
1090
1393
  }
1091
1394
 
1092
1395
  class StageCore extends EventEmitter.EventEmitter {
1396
+ container;
1397
+ selectedDom;
1398
+ selectedDomList = [];
1399
+ highlightedDom;
1400
+ renderer;
1401
+ mask;
1402
+ dr;
1403
+ multiDr;
1404
+ highlightLayer;
1405
+ config;
1406
+ zoom = DEFAULT_ZOOM;
1407
+ containerHighlightClassName;
1408
+ containerHighlightDuration;
1409
+ containerHighlightType;
1410
+ isContainer;
1411
+ canSelect;
1093
1412
  constructor(config) {
1094
1413
  super();
1095
- this.zoom = DEFAULT_ZOOM;
1096
1414
  this.config = config;
1097
1415
  this.setZoom(config.zoom);
1098
1416
  this.canSelect = config.canSelect || ((el) => !!el.id);
1417
+ this.isContainer = config.isContainer;
1418
+ this.containerHighlightClassName = config.containerHighlightClassName || CONTAINER_HIGHLIGHT_CLASS;
1419
+ this.containerHighlightDuration = config.containerHighlightDuration || 800;
1420
+ this.containerHighlightType = config.containerHighlightType;
1099
1421
  this.renderer = new StageRender({ core: this });
1100
1422
  this.mask = new StageMask({ core: this });
1101
- this.dr = new StageDragResize({ core: this, container: this.mask.content });
1423
+ this.dr = new StageDragResize({ core: this, container: this.mask.content, mask: this.mask });
1424
+ this.multiDr = new StageMultiDragResize({ core: this, container: this.mask.content, mask: this.mask });
1102
1425
  this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1103
1426
  this.renderer.on("runtime-ready", (runtime) => {
1104
1427
  this.emit("runtime-ready", runtime);
@@ -1106,31 +1429,61 @@
1106
1429
  this.renderer.on("page-el-update", (el) => {
1107
1430
  this.mask?.observe(el);
1108
1431
  });
1109
- this.mask.on("beforeSelect", (event) => {
1110
- this.setElementFromPoint(event);
1432
+ this.mask.on("beforeSelect", async (event) => {
1433
+ this.clearSelectStatus("multiSelect");
1434
+ const el = await this.getElementFromPoint(event);
1435
+ if (!el)
1436
+ return;
1437
+ this.select(el, event);
1111
1438
  }).on("select", () => {
1112
1439
  this.emit("select", this.selectedDom);
1113
1440
  }).on("changeGuides", (data) => {
1114
1441
  this.dr.setGuidelines(data.type, data.guides);
1115
1442
  this.emit("changeGuides", data);
1116
1443
  }).on("highlight", async (event) => {
1117
- await this.setElementFromPoint(event);
1444
+ const el = await this.getElementFromPoint(event);
1445
+ if (!el)
1446
+ return;
1447
+ if (this.multiDr.dragStatus === StageDragStatus.ING)
1448
+ return;
1449
+ await this.highlight(el);
1118
1450
  if (this.highlightedDom === this.selectedDom) {
1119
1451
  this.highlightLayer.clearHighlight();
1120
1452
  return;
1121
1453
  }
1122
- this.highlightLayer.highlight(this.highlightedDom);
1123
1454
  this.emit("highlight", this.highlightedDom);
1124
1455
  }).on("clearHighlight", async () => {
1125
1456
  this.highlightLayer.clearHighlight();
1457
+ }).on("beforeMultiSelect", async (event) => {
1458
+ const el = await this.getElementFromPoint(event);
1459
+ if (!el)
1460
+ return;
1461
+ if (this.selectedDom && !this.selectedDom.className.includes(PAGE_CLASS)) {
1462
+ this.selectedDomList.push(this.selectedDom);
1463
+ this.selectedDom = void 0;
1464
+ }
1465
+ const existIndex = this.selectedDomList.findIndex((selectedDom) => selectedDom.id === el.id);
1466
+ if (existIndex !== -1) {
1467
+ this.selectedDomList.splice(existIndex, 1);
1468
+ } else {
1469
+ this.selectedDomList.push(el);
1470
+ }
1471
+ this.multiSelect(this.selectedDomList);
1126
1472
  });
1127
1473
  this.dr.on("update", (data) => {
1128
1474
  setTimeout(() => this.emit("update", data));
1129
1475
  }).on("sort", (data) => {
1130
1476
  setTimeout(() => this.emit("sort", data));
1131
1477
  });
1478
+ this.multiDr.on("update", (data) => {
1479
+ setTimeout(() => this.emit("update", data));
1480
+ }).on("select", async (id) => {
1481
+ const el = await this.getTargetElement(id);
1482
+ this.select(el);
1483
+ setTimeout(() => this.emit("select", el));
1484
+ });
1132
1485
  }
1133
- async setElementFromPoint(event) {
1486
+ getElementsFromPoint(event) {
1134
1487
  const { renderer, zoom } = this;
1135
1488
  const doc = renderer.contentWindow?.document;
1136
1489
  let x = event.clientX;
@@ -1142,23 +1495,31 @@
1142
1495
  y = y - rect.top;
1143
1496
  }
1144
1497
  }
1145
- const els = doc?.elementsFromPoint(x / zoom, y / zoom);
1498
+ return doc?.elementsFromPoint(x / zoom, y / zoom);
1499
+ }
1500
+ async getElementFromPoint(event) {
1501
+ const els = this.getElementsFromPoint(event);
1146
1502
  let stopped = false;
1147
1503
  const stop = () => stopped = true;
1148
1504
  for (const el of els) {
1149
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
1505
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.isElCanSelect(el, event, stop)) {
1150
1506
  if (stopped)
1151
1507
  break;
1152
- if (event.type === "mousemove") {
1153
- this.highlight(el);
1154
- break;
1155
- }
1156
- this.select(el, event);
1157
- break;
1508
+ return el;
1158
1509
  }
1159
1510
  }
1160
1511
  }
1512
+ async isElCanSelect(el, event, stop) {
1513
+ const canSelectByProp = await this.canSelect(el, event, stop);
1514
+ if (!canSelectByProp)
1515
+ return false;
1516
+ if (this.mask.isMultiSelectStatus) {
1517
+ return this.multiDr.canSelect(el, stop);
1518
+ }
1519
+ return true;
1520
+ }
1161
1521
  async select(idOrEl, event) {
1522
+ this.clearSelectStatus("multiSelect");
1162
1523
  const el = await this.getTargetElement(idOrEl);
1163
1524
  if (el === this.selectedDom)
1164
1525
  return;
@@ -1180,6 +1541,12 @@
1180
1541
  }
1181
1542
  }
1182
1543
  }
1544
+ async multiSelect(idOrElList) {
1545
+ this.clearSelectStatus("select");
1546
+ const elList = await Promise.all(idOrElList.map(async (idOrEl) => await this.getTargetElement(idOrEl)));
1547
+ this.multiDr.multiSelect(elList);
1548
+ this.emit("multiSelect", elList);
1549
+ }
1183
1550
  update(data) {
1184
1551
  const { config } = data;
1185
1552
  return this.renderer?.getRuntime().then((runtime) => {
@@ -1202,7 +1569,7 @@
1202
1569
  this.highlightLayer.clearHighlight();
1203
1570
  return;
1204
1571
  }
1205
- if (el === this.highlightedDom)
1572
+ if (el === this.highlightedDom || !el)
1206
1573
  return;
1207
1574
  this.highlightLayer.highlight(el);
1208
1575
  this.highlightedDom = el;
@@ -1219,6 +1586,14 @@
1219
1586
  setZoom(zoom = DEFAULT_ZOOM) {
1220
1587
  this.zoom = zoom;
1221
1588
  }
1589
+ clearSelectStatus(selectType) {
1590
+ if (selectType === "multiSelect") {
1591
+ this.multiDr.clearSelectStatus();
1592
+ this.selectedDomList = [];
1593
+ } else {
1594
+ this.dr.clearSelectStatus();
1595
+ }
1596
+ }
1222
1597
  async mount(el) {
1223
1598
  this.container = el;
1224
1599
  const { mask, renderer } = this;
@@ -1230,6 +1605,24 @@
1230
1605
  this.mask.clearGuides();
1231
1606
  this.dr.clearGuides();
1232
1607
  }
1608
+ async addContainerHighlightClassName(event, exclude) {
1609
+ const els = this.getElementsFromPoint(event);
1610
+ const { renderer } = this;
1611
+ const doc = renderer.contentWindow?.document;
1612
+ if (!doc)
1613
+ return;
1614
+ for (const el of els) {
1615
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.isContainer(el) && !exclude.includes(el)) {
1616
+ utils.addClassName(el, doc, this.containerHighlightClassName);
1617
+ break;
1618
+ }
1619
+ }
1620
+ }
1621
+ getAddContainerHighlightClassNameTimeout(event, exclude = []) {
1622
+ return globalThis.setTimeout(() => {
1623
+ this.addContainerHighlightClassName(event, exclude);
1624
+ }, this.containerHighlightDuration);
1625
+ }
1233
1626
  destroy() {
1234
1627
  const { mask, renderer, dr, highlightLayer } = this;
1235
1628
  renderer.destroy();
@@ -1250,6 +1643,8 @@
1250
1643
  }
1251
1644
  }
1252
1645
 
1646
+ exports.CONTAINER_HIGHLIGHT_CLASS = CONTAINER_HIGHLIGHT_CLASS;
1647
+ exports.ContainerHighlightType = ContainerHighlightType;
1253
1648
  exports.DEFAULT_ZOOM = DEFAULT_ZOOM;
1254
1649
  exports.DRAG_EL_ID_PREFIX = DRAG_EL_ID_PREFIX;
1255
1650
  exports.GHOST_EL_ID_PREFIX = GHOST_EL_ID_PREFIX;
@@ -1257,12 +1652,29 @@
1257
1652
  exports.HIGHLIGHT_EL_ID_PREFIX = HIGHLIGHT_EL_ID_PREFIX;
1258
1653
  exports.Mode = Mode;
1259
1654
  exports.MouseButton = MouseButton;
1655
+ exports.PAGE_CLASS = PAGE_CLASS;
1260
1656
  exports.SELECTED_CLASS = SELECTED_CLASS;
1261
1657
  exports.StageDragResize = StageDragResize;
1658
+ exports.StageDragStatus = StageDragStatus;
1262
1659
  exports.StageMask = StageMask;
1263
1660
  exports.StageRender = StageRender;
1264
1661
  exports.ZIndex = ZIndex;
1265
- exports["default"] = StageCore;
1662
+ exports.addSelectedClassName = addSelectedClassName;
1663
+ exports.calcValueByFontsize = calcValueByFontsize;
1664
+ exports.default = StageCore;
1665
+ exports.down = down;
1666
+ exports.getAbsolutePosition = getAbsolutePosition;
1667
+ exports.getMode = getMode;
1668
+ exports.getOffset = getOffset;
1669
+ exports.getScrollParent = getScrollParent;
1670
+ exports.getTargetElStyle = getTargetElStyle;
1671
+ exports.isAbsolute = isAbsolute;
1672
+ exports.isFixed = isFixed;
1673
+ exports.isFixedParent = isFixedParent;
1674
+ exports.isRelative = isRelative;
1675
+ exports.isStatic = isStatic;
1676
+ exports.removeSelectedClassName = removeSelectedClassName;
1677
+ exports.up = up;
1266
1678
 
1267
1679
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
1268
1680