@tmagic/stage 1.0.0-beta.8 → 1.0.0-rc.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,16 +1,19 @@
1
1
  (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('events'), require('moveable'), require('@scena/guides')) :
3
- typeof define === 'function' && define.amd ? define(['exports', 'events', 'moveable', '@scena/guides'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.TMagicStage = {}, global.EventEmitter, global.Moveable, global.Guides));
5
- })(this, (function (exports, EventEmitter, Moveable, Guides) { 'use strict';
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';
6
6
 
7
7
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
8
8
 
9
9
  var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
10
10
  var Moveable__default = /*#__PURE__*/_interopDefaultLegacy(Moveable);
11
+ var MoveableHelper__default = /*#__PURE__*/_interopDefaultLegacy(MoveableHelper);
11
12
  var Guides__default = /*#__PURE__*/_interopDefaultLegacy(Guides);
12
13
 
13
14
  const GHOST_EL_ID_PREFIX = "ghost_el_";
15
+ const DRAG_EL_ID_PREFIX = "drag_el_";
16
+ const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
14
17
  const DEFAULT_ZOOM = 1;
15
18
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
16
19
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -19,6 +22,7 @@
19
22
  })(GuidesType || {});
20
23
  var ZIndex = /* @__PURE__ */ ((ZIndex2) => {
21
24
  ZIndex2["MASK"] = "99999";
25
+ ZIndex2["SELECTED_EL"] = "666";
22
26
  return ZIndex2;
23
27
  })(ZIndex || {});
24
28
  var MouseButton = /* @__PURE__ */ ((MouseButton2) => {
@@ -33,7 +37,17 @@
33
37
  Mode2["SORTABLE"] = "sortable";
34
38
  return Mode2;
35
39
  })(Mode || {});
40
+ const SELECTED_CLASS = "tmagic-stage-selected-area";
36
41
 
42
+ const getParents = (el, relative) => {
43
+ let cur = el.parentElement;
44
+ const parents = [];
45
+ while (cur && cur !== relative) {
46
+ parents.push(cur);
47
+ cur = cur.parentElement;
48
+ }
49
+ return parents;
50
+ };
37
51
  const getOffset = (el) => {
38
52
  const { transform } = getComputedStyle(el);
39
53
  const { offsetParent } = el;
@@ -153,46 +167,64 @@
153
167
  el.style.cssText = cssText;
154
168
  return el;
155
169
  };
170
+ const removeSelectedClassName = (doc) => {
171
+ const oldEl = doc.querySelector(`.${SELECTED_CLASS}`);
172
+ if (oldEl) {
173
+ oldEl.classList.remove(SELECTED_CLASS);
174
+ oldEl.parentNode?.classList.remove(`${SELECTED_CLASS}-parent`);
175
+ doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
176
+ item.classList.remove(`${SELECTED_CLASS}-parents`);
177
+ });
178
+ }
179
+ };
180
+ const addSelectedClassName = (el, doc) => {
181
+ el.classList.add(SELECTED_CLASS);
182
+ el.parentNode?.classList.add(`${SELECTED_CLASS}-parent`);
183
+ getParents(el, doc.body).forEach((item) => {
184
+ item.classList.add(`${SELECTED_CLASS}-parents`);
185
+ });
186
+ };
156
187
 
157
188
  class StageDragResize extends EventEmitter.EventEmitter {
158
- core;
159
- container;
160
- target;
161
- dragEl;
162
- moveable;
163
- horizontalGuidelines = [];
164
- verticalGuidelines = [];
165
- moveableOptions = {};
166
- dragStatus = "end" /* END */;
167
- ghostEl;
168
- mode = Mode.ABSOLUTE;
169
189
  constructor(config) {
170
190
  super();
191
+ this.horizontalGuidelines = [];
192
+ this.verticalGuidelines = [];
193
+ this.elementGuidelines = [];
194
+ this.mode = Mode.ABSOLUTE;
195
+ this.moveableOptions = {};
196
+ this.dragStatus = "end" /* END */;
171
197
  this.core = config.core;
172
198
  this.container = config.container;
199
+ this.dragEl = globalThis.document.createElement("div");
200
+ this.container.append(this.dragEl);
173
201
  }
174
- async select(el, event) {
202
+ select(el, event) {
203
+ const oldTarget = this.target;
175
204
  this.target = el;
176
- if (/(auto|scroll)/.test(this.target.style.overflow)) {
177
- this.target.style.overflow = "hidden";
205
+ this.init(el);
206
+ if (!this.moveable || this.target !== oldTarget) {
207
+ this.moveableHelper = MoveableHelper__default["default"].create({
208
+ useBeforeRender: true,
209
+ useRender: false,
210
+ createAuto: true
211
+ });
212
+ this.initMoveable();
213
+ } else {
214
+ this.updateMoveable();
178
215
  }
179
- this.mode = getMode(el);
180
- this.destroyDragEl();
181
- this.destroyGhostEl();
182
- this.dragEl = this.generateDragEl(el);
183
- this.moveableOptions = await this.getOptions({
184
- target: this.dragEl || this.target
185
- });
186
- this.initMoveable();
187
216
  if (event) {
188
217
  this.moveable?.dragStart(event);
189
218
  }
190
219
  }
191
- async refresh() {
220
+ updateMoveable(el = this.target) {
192
221
  if (!this.moveable)
193
222
  throw new Error("\u672A\u521D\u59CB\u5316moveable");
194
- const options = await this.getOptions();
195
- Object.entries(options).forEach(([key, value]) => {
223
+ if (!el)
224
+ throw new Error("\u4E3A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
225
+ this.target = el;
226
+ this.init(el);
227
+ Object.entries(this.moveableOptions).forEach(([key, value]) => {
196
228
  this.moveable[key] = value;
197
229
  });
198
230
  this.moveable.updateTarget();
@@ -200,15 +232,19 @@
200
232
  setGuidelines(type, guidelines) {
201
233
  if (type === GuidesType.HORIZONTAL) {
202
234
  this.horizontalGuidelines = guidelines;
235
+ this.moveableOptions.horizontalGuidelines = guidelines;
203
236
  } else if (type === GuidesType.VERTICAL) {
204
237
  this.verticalGuidelines = guidelines;
238
+ this.moveableOptions.verticalGuidelines = guidelines;
205
239
  }
206
- this.refresh();
240
+ this.updateMoveable();
207
241
  }
208
242
  clearGuides() {
209
- this.verticalGuidelines = [];
210
243
  this.horizontalGuidelines = [];
211
- this.refresh();
244
+ this.verticalGuidelines = [];
245
+ this.moveableOptions.horizontalGuidelines = [];
246
+ this.moveableOptions.verticalGuidelines = [];
247
+ this.updateMoveable();
212
248
  }
213
249
  destroy() {
214
250
  this.moveable?.destroy();
@@ -217,6 +253,17 @@
217
253
  this.dragStatus = "end" /* END */;
218
254
  this.removeAllListeners();
219
255
  }
256
+ init(el) {
257
+ if (/(auto|scroll)/.test(el.style.overflow)) {
258
+ el.style.overflow = "hidden";
259
+ }
260
+ this.mode = getMode(el);
261
+ this.destroyGhostEl();
262
+ this.updateDragEl(el);
263
+ this.moveableOptions = this.getOptions({
264
+ target: this.dragEl
265
+ });
266
+ }
220
267
  initMoveable() {
221
268
  this.moveable?.destroy();
222
269
  this.moveable = new Moveable__default["default"](this.container, {
@@ -261,27 +308,31 @@
261
308
  bindDragEvent() {
262
309
  if (!this.moveable)
263
310
  throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
264
- this.moveable.on("dragStart", () => {
311
+ let offset = null;
312
+ this.moveable.on("dragStart", (e) => {
265
313
  if (!this.target)
266
314
  throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
267
315
  this.dragStatus = "start" /* START */;
316
+ this.moveableHelper?.onDragStart(e);
268
317
  if (this.mode === Mode.SORTABLE) {
269
318
  this.ghostEl = this.generateGhostEl(this.target);
270
319
  }
271
- }).on("drag", ({ left, top }) => {
320
+ }).on("drag", (e) => {
272
321
  if (!this.target || !this.dragEl)
273
322
  return;
323
+ if (!offset) {
324
+ offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
325
+ }
274
326
  this.dragStatus = "ing" /* ING */;
275
- const offset = getAbsolutePosition(this.target, { left, top });
327
+ const { left, top } = e;
276
328
  if (this.ghostEl) {
277
329
  this.dragEl.style.top = `${top}px`;
278
- this.ghostEl.style.top = `${offset.top}px`;
330
+ this.ghostEl.style.top = `${top + offset.top}px`;
279
331
  return;
280
332
  }
281
- this.dragEl.style.left = `${left}px`;
282
- this.dragEl.style.top = `${top}px`;
283
- this.target.style.left = `${offset.left}px`;
284
- this.target.style.top = `${offset.top}px`;
333
+ this.moveableHelper?.onDrag(e);
334
+ this.target.style.left = `${left + offset.left}px`;
335
+ this.target.style.top = `${top + offset.top}px`;
285
336
  }).on("dragEnd", () => {
286
337
  if (this.dragStatus === "ing" /* ING */) {
287
338
  switch (this.mode) {
@@ -292,17 +343,18 @@
292
343
  this.update();
293
344
  }
294
345
  }
346
+ offset = null;
295
347
  this.dragStatus = "end" /* END */;
296
348
  this.destroyGhostEl();
297
349
  });
298
350
  }
299
- async getSnapElements(el) {
300
- const { renderer } = this.core;
301
- const getSnapElements = (await renderer.getRuntime())?.getSnapElements || (() => {
351
+ getSnapElements(runtime, el) {
352
+ const { renderer, mask } = this.core;
353
+ const getSnapElements = runtime?.getSnapElements || (() => {
302
354
  const doc = renderer.contentWindow?.document;
303
355
  return doc ? Array.from(doc.querySelectorAll("[id]")) : [];
304
356
  });
305
- return getSnapElements(el).filter((element) => element !== this.target && !this.target?.contains(element));
357
+ return getSnapElements(el).filter((element) => element !== this.target && !this.target?.contains(element) && element !== mask.page);
306
358
  }
307
359
  sort() {
308
360
  if (!this.target || !this.ghostEl)
@@ -341,7 +393,7 @@
341
393
  }
342
394
  const ghostEl = el.cloneNode(true);
343
395
  const { top, left } = getAbsolutePosition(el, getOffset(el));
344
- ghostEl.id = `${GHOST_EL_ID_PREFIX}${ghostEl.id}`;
396
+ ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
345
397
  ghostEl.style.zIndex = "5";
346
398
  ghostEl.style.opacity = ".5";
347
399
  ghostEl.style.position = "absolute";
@@ -354,49 +406,62 @@
354
406
  this.ghostEl?.remove();
355
407
  this.ghostEl = void 0;
356
408
  }
357
- generateDragEl(el) {
358
- if (this.dragEl) {
359
- this.destroyDragEl();
360
- }
409
+ updateDragEl(el) {
361
410
  const { width, height } = el.getBoundingClientRect();
362
411
  const offset = getOffset(el);
363
- const dragEl = globalThis.document.createElement("div");
364
- dragEl.style.cssText = `
412
+ this.dragEl.style.cssText = `
365
413
  position: absolute;
366
414
  left: ${offset.left}px;
367
415
  top: ${offset.top}px;
368
416
  width: ${width}px;
369
417
  height: ${height}px;
418
+ z-index: 9;
370
419
  `;
371
- this.container.append(dragEl);
372
- return dragEl;
420
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
373
421
  }
374
422
  destroyDragEl() {
375
423
  this.dragEl?.remove();
376
- this.dragEl = void 0;
377
424
  }
378
- async getOptions(options = {}) {
425
+ getOptions(options = {}) {
379
426
  if (!this.target)
380
427
  return {};
381
428
  const isAbsolute = this.mode === Mode.ABSOLUTE;
429
+ const isFixed = this.mode === Mode.FIXED;
382
430
  let { moveableOptions = {} } = this.core.config;
383
431
  if (typeof moveableOptions === "function") {
384
432
  moveableOptions = moveableOptions(this.core);
385
433
  }
386
434
  return {
387
- scrollable: true,
388
- origin: true,
435
+ origin: false,
436
+ rootContainer: this.core.container,
389
437
  zoom: 1,
390
- dragArea: true,
438
+ dragArea: false,
391
439
  draggable: true,
392
440
  resizable: true,
393
- snappable: isAbsolute,
394
- snapGap: isAbsolute,
395
- snapDirections: { center: isAbsolute, middle: isAbsolute },
396
- elementSnapDirections: { center: isAbsolute, middle: isAbsolute },
397
- elementGuidelines: isAbsolute ? await this.getSnapElements(this.target) : [],
441
+ snappable: isAbsolute || isFixed,
442
+ snapGap: isAbsolute || isFixed,
443
+ snapThreshold: 5,
444
+ snapDigit: 0,
445
+ throttleDrag: 0,
446
+ isDisplaySnapDigit: isAbsolute,
447
+ snapDirections: {
448
+ top: isAbsolute,
449
+ right: isAbsolute,
450
+ bottom: isAbsolute,
451
+ left: isAbsolute,
452
+ center: isAbsolute,
453
+ middle: isAbsolute
454
+ },
455
+ elementSnapDirections: {
456
+ top: isAbsolute,
457
+ right: isAbsolute,
458
+ bottom: isAbsolute,
459
+ left: isAbsolute
460
+ },
461
+ isDisplayInnerSnapDigit: true,
398
462
  horizontalGuidelines: this.horizontalGuidelines,
399
463
  verticalGuidelines: this.verticalGuidelines,
464
+ elementGuidelines: this.elementGuidelines,
400
465
  bounds: {
401
466
  top: 0,
402
467
  left: 0,
@@ -465,15 +530,159 @@
465
530
  };
466
531
  };
467
532
 
533
+ class TargetCalibrate extends EventEmitter.EventEmitter {
534
+ constructor(config) {
535
+ super();
536
+ this.parent = config.parent;
537
+ this.mask = config.mask;
538
+ this.dr = config.dr;
539
+ this.operationEl = globalThis.document.createElement("div");
540
+ this.parent.append(this.operationEl);
541
+ }
542
+ update(el, prefix) {
543
+ const { width, height } = el.getBoundingClientRect();
544
+ const { left, top } = this.getOffset(el);
545
+ this.operationEl.style.cssText = `
546
+ position: absolute;
547
+ left: ${left}px;
548
+ top: ${top}px;
549
+ width: ${width}px;
550
+ height: ${height}px;
551
+ `;
552
+ this.operationEl.id = `${prefix}${el.id}`;
553
+ return this.operationEl;
554
+ }
555
+ destroy() {
556
+ this.operationEl?.remove();
557
+ }
558
+ getOffset(el) {
559
+ const { transform } = getComputedStyle(el);
560
+ const { offsetParent } = el;
561
+ let left = el.offsetLeft;
562
+ let top = el.offsetTop;
563
+ if (transform.indexOf("matrix") > -1) {
564
+ let a = 1;
565
+ let b = 1;
566
+ let c = 1;
567
+ let d = 1;
568
+ let e = 0;
569
+ let f = 0;
570
+ transform.replace(/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/, ($0, $1, $2, $3, $4, $5, $6) => {
571
+ a = +$1;
572
+ b = +$2;
573
+ c = +$3;
574
+ d = +$4;
575
+ e = +$5;
576
+ f = +$6;
577
+ return transform;
578
+ });
579
+ left = a * left + c * top + e;
580
+ top = b * left + d * top + f;
581
+ }
582
+ if (offsetParent) {
583
+ const parentOffset = this.getOffset(offsetParent);
584
+ return {
585
+ left: left + parentOffset.left,
586
+ top: top + parentOffset.top
587
+ };
588
+ }
589
+ if (this.dr.mode === Mode.FIXED) {
590
+ if (getMode(el) === Mode.FIXED) {
591
+ return {
592
+ left,
593
+ top
594
+ };
595
+ }
596
+ return {
597
+ left: left - this.mask.scrollLeft,
598
+ top: top - this.mask.scrollTop
599
+ };
600
+ }
601
+ if (getMode(el) === Mode.FIXED) {
602
+ return {
603
+ left: left + this.mask.scrollLeft,
604
+ top: top + this.mask.scrollTop
605
+ };
606
+ }
607
+ return {
608
+ left,
609
+ top
610
+ };
611
+ }
612
+ }
613
+
614
+ class StageHighlight extends EventEmitter.EventEmitter {
615
+ constructor(config) {
616
+ super();
617
+ this.core = config.core;
618
+ this.container = config.container;
619
+ this.calibrationTarget = new TargetCalibrate({
620
+ parent: this.core.mask.content,
621
+ mask: this.core.mask,
622
+ dr: this.core.dr
623
+ });
624
+ }
625
+ highlight(el) {
626
+ if (!el || el === this.target)
627
+ return;
628
+ this.target = el;
629
+ this.moveable?.destroy();
630
+ this.moveable = new Moveable__default["default"](this.container, {
631
+ target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
632
+ origin: false,
633
+ rootContainer: this.core.container,
634
+ zoom: 1
635
+ });
636
+ }
637
+ clearHighlight() {
638
+ if (!this.moveable)
639
+ return;
640
+ this.target = void 0;
641
+ this.moveable.target = null;
642
+ this.moveable.updateTarget();
643
+ }
644
+ destroy() {
645
+ this.moveable?.destroy();
646
+ this.calibrationTarget.destroy();
647
+ }
648
+ }
649
+
468
650
  class Rule extends EventEmitter__default["default"] {
469
- hGuides;
470
- vGuides;
471
- horizontalGuidelines = [];
472
- verticalGuidelines = [];
473
- container;
474
- containerResizeObserver;
475
651
  constructor(container) {
476
652
  super();
653
+ this.horizontalGuidelines = [];
654
+ this.verticalGuidelines = [];
655
+ this.getGuidesStyle = (type) => ({
656
+ position: "fixed",
657
+ zIndex: 1,
658
+ left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
659
+ top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
660
+ width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
661
+ height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
662
+ });
663
+ this.createGuides = (type, defaultGuides = []) => new Guides__default["default"](this.container, {
664
+ type,
665
+ defaultGuides,
666
+ displayDragPos: true,
667
+ backgroundColor: "#fff",
668
+ lineColor: "#000",
669
+ textColor: "#000",
670
+ style: this.getGuidesStyle(type)
671
+ });
672
+ this.hGuidesChangeGuidesHandler = (e) => {
673
+ this.horizontalGuidelines = e.guides;
674
+ this.emit("changeGuides", {
675
+ type: GuidesType.HORIZONTAL,
676
+ guides: this.horizontalGuidelines
677
+ });
678
+ };
679
+ this.vGuidesChangeGuidesHandler = (e) => {
680
+ this.verticalGuidelines = e.guides;
681
+ this.emit("changeGuides", {
682
+ type: GuidesType.VERTICAL,
683
+ guides: this.verticalGuidelines
684
+ });
685
+ };
477
686
  this.container = container;
478
687
  this.hGuides = this.createGuides(GuidesType.HORIZONTAL);
479
688
  this.vGuides = this.createGuides(GuidesType.VERTICAL);
@@ -542,40 +751,10 @@
542
751
  this.containerResizeObserver.disconnect();
543
752
  this.removeAllListeners();
544
753
  }
545
- getGuidesStyle = (type) => ({
546
- position: "fixed",
547
- zIndex: 1,
548
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
549
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
550
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
551
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
552
- });
553
- createGuides = (type, defaultGuides = []) => new Guides__default["default"](this.container, {
554
- type,
555
- defaultGuides,
556
- displayDragPos: true,
557
- backgroundColor: "#fff",
558
- lineColor: "#000",
559
- textColor: "#000",
560
- style: this.getGuidesStyle(type)
561
- });
562
- hGuidesChangeGuidesHandler = (e) => {
563
- this.horizontalGuidelines = e.guides;
564
- this.emit("changeGuides", {
565
- type: GuidesType.HORIZONTAL,
566
- guides: this.horizontalGuidelines
567
- });
568
- };
569
- vGuidesChangeGuidesHandler = (e) => {
570
- this.verticalGuidelines = e.guides;
571
- this.emit("changeGuides", {
572
- type: GuidesType.VERTICAL,
573
- guides: this.verticalGuidelines
574
- });
575
- };
576
754
  }
577
755
 
578
756
  const wrapperClassName = "editor-mask-wrapper";
757
+ const throttleTime = 100;
579
758
  const hideScrollbar = () => {
580
759
  const style = globalThis.document.createElement("style");
581
760
  style.innerHTML = `
@@ -609,28 +788,73 @@
609
788
  return el;
610
789
  };
611
790
  class StageMask extends Rule {
612
- content = createContent();
613
- wrapper;
614
- core;
615
- page = null;
616
- pageScrollParent = null;
617
- scrollTop = 0;
618
- scrollLeft = 0;
619
- width = 0;
620
- height = 0;
621
- wrapperHeight = 0;
622
- wrapperWidth = 0;
623
- mode = Mode.ABSOLUTE;
624
- pageResizeObserver = null;
625
- wrapperResizeObserver = null;
626
791
  constructor(config) {
627
792
  const wrapper = createWrapper();
628
793
  super(wrapper);
794
+ this.content = createContent();
795
+ this.page = null;
796
+ this.pageScrollParent = null;
797
+ this.scrollTop = 0;
798
+ this.scrollLeft = 0;
799
+ this.width = 0;
800
+ this.height = 0;
801
+ this.wrapperHeight = 0;
802
+ this.wrapperWidth = 0;
803
+ this.maxScrollTop = 0;
804
+ this.maxScrollLeft = 0;
805
+ this.mode = Mode.ABSOLUTE;
806
+ this.pageResizeObserver = null;
807
+ this.wrapperResizeObserver = null;
808
+ this.highlightHandler = lodashEs.throttle((event) => {
809
+ this.emit("highlight", event);
810
+ }, throttleTime);
811
+ this.mouseDownHandler = (event) => {
812
+ this.emit("clearHighlight");
813
+ event.stopImmediatePropagation();
814
+ event.stopPropagation();
815
+ if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
816
+ return;
817
+ if (event.target.className.indexOf("moveable-control") !== -1) {
818
+ return;
819
+ }
820
+ this.content.removeEventListener("mousemove", this.highlightHandler);
821
+ this.emit("beforeSelect", event);
822
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
823
+ };
824
+ this.mouseUpHandler = () => {
825
+ globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
826
+ this.content.addEventListener("mousemove", this.highlightHandler);
827
+ this.emit("select");
828
+ };
829
+ this.mouseWheelHandler = (event) => {
830
+ this.emit("clearHighlight");
831
+ if (!this.page)
832
+ throw new Error("page \u672A\u521D\u59CB\u5316");
833
+ const { deltaY, deltaX } = event;
834
+ if (this.page.clientHeight < this.wrapperHeight && deltaY)
835
+ return;
836
+ if (this.page.clientWidth < this.wrapperWidth && deltaX)
837
+ return;
838
+ if (this.maxScrollTop > 0) {
839
+ this.scrollTop = this.scrollTop + deltaY;
840
+ }
841
+ if (this.maxScrollLeft > 0) {
842
+ this.scrollLeft = this.scrollLeft + deltaX;
843
+ }
844
+ this.fixScrollValue();
845
+ this.scroll();
846
+ this.emit("scroll", event);
847
+ };
848
+ this.mouseLeaveHandler = () => {
849
+ setTimeout(() => this.emit("clearHighlight"), throttleTime);
850
+ };
629
851
  this.wrapper = wrapper;
630
852
  this.core = config.core;
631
853
  this.content.addEventListener("mousedown", this.mouseDownHandler);
632
854
  this.wrapper.appendChild(this.content);
633
855
  this.content.addEventListener("wheel", this.mouseWheelHandler);
856
+ this.content.addEventListener("mousemove", this.highlightHandler);
857
+ this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
634
858
  }
635
859
  setMode(mode) {
636
860
  this.mode = mode;
@@ -655,6 +879,9 @@
655
879
  const { clientHeight, clientWidth } = entry.target;
656
880
  this.setHeight(clientHeight);
657
881
  this.setWidth(clientWidth);
882
+ this.fixScrollValue();
883
+ this.scroll();
884
+ this.core.dr.updateMoveable();
658
885
  });
659
886
  this.pageResizeObserver.observe(page);
660
887
  this.wrapperResizeObserver = new ResizeObserver((entries) => {
@@ -662,6 +889,8 @@
662
889
  const { clientHeight, clientWidth } = entry.target;
663
890
  this.wrapperHeight = clientHeight;
664
891
  this.wrapperWidth = clientWidth;
892
+ this.setMaxScrollLeft();
893
+ this.setMaxScrollTop();
665
894
  });
666
895
  this.wrapperResizeObserver.observe(this.wrapper);
667
896
  }
@@ -674,16 +903,33 @@
674
903
  setLayout(el) {
675
904
  this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
676
905
  }
906
+ scrollIntoView(el) {
907
+ if (this.mode === Mode.FIXED)
908
+ return;
909
+ el.scrollIntoView();
910
+ if (!this.pageScrollParent)
911
+ return;
912
+ this.scrollLeft = this.pageScrollParent.scrollLeft;
913
+ this.scrollTop = this.pageScrollParent.scrollTop;
914
+ this.scroll();
915
+ }
677
916
  destroy() {
678
917
  this.content?.remove();
679
918
  this.page = null;
680
919
  this.pageScrollParent = null;
681
920
  this.pageResizeObserver?.disconnect();
682
921
  this.wrapperResizeObserver?.disconnect();
922
+ this.content.removeEventListener("mouseleave", this.mouseLeaveHandler);
683
923
  super.destroy();
684
924
  }
685
925
  scroll() {
686
926
  let { scrollLeft, scrollTop } = this;
927
+ if (this.pageScrollParent) {
928
+ this.pageScrollParent.scrollTo({
929
+ top: scrollTop,
930
+ left: scrollLeft
931
+ });
932
+ }
687
933
  if (this.mode === Mode.FIXED) {
688
934
  scrollLeft = 0;
689
935
  scrollTop = 0;
@@ -696,71 +942,75 @@
696
942
  }
697
943
  setHeight(height) {
698
944
  this.height = height;
945
+ this.setMaxScrollTop();
699
946
  this.content.style.height = `${height}px`;
700
947
  }
701
948
  setWidth(width) {
702
949
  this.width = width;
950
+ this.setMaxScrollLeft();
703
951
  this.content.style.width = `${width}px`;
704
952
  }
705
- mouseDownHandler = async (event) => {
706
- event.stopImmediatePropagation();
707
- event.stopPropagation();
708
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
709
- return;
710
- if (event.target.className.indexOf("moveable-control") !== -1) {
711
- return;
712
- }
713
- this.emit("beforeSelect", event);
714
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
715
- };
716
- mouseUpHandler = () => {
717
- globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
718
- this.emit("select");
719
- };
720
- mouseWheelHandler = (event) => {
721
- if (!this.page)
722
- throw new Error("page \u672A\u521D\u59CB\u5316");
723
- const { deltaY, deltaX } = event;
724
- const { height, wrapperHeight, width, wrapperWidth } = this;
725
- const maxScrollTop = height - wrapperHeight;
726
- const maxScrollLeft = width - wrapperWidth;
727
- if (maxScrollTop > 0) {
728
- if (deltaY > 0) {
729
- this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
730
- } else {
731
- this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
732
- }
733
- }
734
- if (width > wrapperWidth) {
735
- if (deltaX > 0) {
736
- this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
737
- } else {
738
- this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
739
- }
740
- }
741
- if (this.mode !== Mode.FIXED) {
742
- this.scrollTo(this.scrollLeft, this.scrollTop);
743
- }
744
- if (this.pageScrollParent) {
745
- this.pageScrollParent.scrollTo({
746
- top: this.scrollTop,
747
- left: this.scrollLeft
748
- });
749
- }
750
- this.scroll();
751
- this.emit("scroll", event);
752
- };
953
+ setMaxScrollLeft() {
954
+ this.maxScrollLeft = this.width - this.wrapperWidth;
955
+ }
956
+ setMaxScrollTop() {
957
+ this.maxScrollTop = this.height - this.wrapperHeight;
958
+ }
959
+ fixScrollValue() {
960
+ if (this.scrollTop < 0)
961
+ this.scrollTop = 0;
962
+ if (this.scrollLeft < 0)
963
+ this.scrollLeft = 0;
964
+ if (this.maxScrollTop < this.scrollTop)
965
+ this.scrollTop = this.maxScrollTop;
966
+ if (this.maxScrollLeft < this.scrollLeft)
967
+ this.scrollLeft = this.maxScrollLeft;
968
+ }
753
969
  }
754
970
 
755
971
  class StageRender extends EventEmitter.EventEmitter {
756
- contentWindow = null;
757
- runtime = null;
758
- iframe;
759
- runtimeUrl;
760
- core;
761
- render;
762
972
  constructor({ core }) {
763
973
  super();
974
+ this.contentWindow = null;
975
+ this.runtime = null;
976
+ this.getMagicApi = () => ({
977
+ onPageElUpdate: (el) => this.emit("page-el-update", el),
978
+ onRuntimeReady: (runtime) => {
979
+ this.runtime = runtime;
980
+ globalThis.runtime = runtime;
981
+ this.emit("runtime-ready", runtime);
982
+ }
983
+ });
984
+ this.getRuntime = () => {
985
+ if (this.runtime)
986
+ return Promise.resolve(this.runtime);
987
+ return new Promise((resolve) => {
988
+ const listener = (runtime) => {
989
+ this.off("runtime-ready", listener);
990
+ resolve(runtime);
991
+ };
992
+ this.on("runtime-ready", listener);
993
+ });
994
+ };
995
+ this.loadHandler = async () => {
996
+ this.emit("onload");
997
+ if (this.render) {
998
+ const el = await this.render(this.core);
999
+ if (el) {
1000
+ this.iframe?.contentDocument?.body?.appendChild(el);
1001
+ }
1002
+ }
1003
+ if (this.contentWindow) {
1004
+ const style = this.contentWindow.document.createElement("style");
1005
+ style.id = "tmagic-stage-render";
1006
+ style.innerHTML = `
1007
+ .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
1008
+ z-index: ${ZIndex.SELECTED_EL};
1009
+ }
1010
+ `;
1011
+ this.contentWindow.document.head.appendChild(style);
1012
+ }
1013
+ };
764
1014
  this.core = core;
765
1015
  this.runtimeUrl = core.config.runtimeUrl || "";
766
1016
  this.render = core.config.render;
@@ -773,14 +1023,6 @@
773
1023
  `;
774
1024
  this.iframe.addEventListener("load", this.loadHandler);
775
1025
  }
776
- getMagicApi = () => ({
777
- onPageElUpdate: (el) => this.emit("page-el-update", el),
778
- onRuntimeReady: (runtime) => {
779
- this.runtime = runtime;
780
- globalThis.runtime = runtime;
781
- this.emit("runtime-ready", runtime);
782
- }
783
- });
784
1026
  async mount(el) {
785
1027
  if (this.iframe) {
786
1028
  if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
@@ -797,17 +1039,6 @@
797
1039
  throw Error("mount \u5931\u8D25");
798
1040
  }
799
1041
  }
800
- getRuntime = () => {
801
- if (this.runtime)
802
- return Promise.resolve(this.runtime);
803
- return new Promise((resolve) => {
804
- const listener = (runtime) => {
805
- this.off("runtime-ready", listener);
806
- resolve(runtime);
807
- };
808
- this.on("runtime-ready", listener);
809
- });
810
- };
811
1042
  destroy() {
812
1043
  this.iframe?.removeEventListener("load", this.loadHandler);
813
1044
  this.contentWindow = null;
@@ -815,35 +1046,25 @@
815
1046
  this.iframe = void 0;
816
1047
  this.removeAllListeners();
817
1048
  }
818
- loadHandler = async () => {
819
- this.emit("onload");
820
- if (this.render) {
821
- const el = await this.render(this.core);
822
- if (el) {
823
- this.iframe?.contentDocument?.body?.appendChild(el);
824
- }
825
- }
826
- };
827
1049
  }
828
1050
 
829
1051
  class StageCore extends EventEmitter.EventEmitter {
830
- selectedDom;
831
- renderer;
832
- mask;
833
- dr;
834
- config;
835
- zoom = DEFAULT_ZOOM;
836
- canSelect;
837
1052
  constructor(config) {
838
1053
  super();
1054
+ this.zoom = DEFAULT_ZOOM;
839
1055
  this.config = config;
840
1056
  this.setZoom(config.zoom);
841
1057
  this.canSelect = config.canSelect || ((el) => !!el.id);
842
1058
  this.renderer = new StageRender({ core: this });
843
1059
  this.mask = new StageMask({ core: this });
844
1060
  this.dr = new StageDragResize({ core: this, container: this.mask.content });
845
- this.renderer.on("runtime-ready", (runtime) => this.emit("runtime-ready", runtime));
846
- this.renderer.on("page-el-update", (el) => this.mask?.observe(el));
1061
+ this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1062
+ this.renderer.on("runtime-ready", (runtime) => {
1063
+ this.emit("runtime-ready", runtime);
1064
+ });
1065
+ this.renderer.on("page-el-update", (el) => {
1066
+ this.mask?.observe(el);
1067
+ });
847
1068
  this.mask.on("beforeSelect", (event) => {
848
1069
  this.setElementFromPoint(event);
849
1070
  }).on("select", () => {
@@ -851,6 +1072,16 @@
851
1072
  }).on("changeGuides", (data) => {
852
1073
  this.dr.setGuidelines(data.type, data.guides);
853
1074
  this.emit("changeGuides", data);
1075
+ }).on("highlight", async (event) => {
1076
+ await this.setElementFromPoint(event);
1077
+ if (this.highlightedDom === this.selectedDom) {
1078
+ this.highlightLayer.clearHighlight();
1079
+ return;
1080
+ }
1081
+ this.highlightLayer.highlight(this.highlightedDom);
1082
+ this.emit("highlight", this.highlightedDom);
1083
+ }).on("clearHighlight", async () => {
1084
+ this.highlightLayer.clearHighlight();
854
1085
  });
855
1086
  this.dr.on("update", (data) => {
856
1087
  setTimeout(() => this.emit("update", data));
@@ -874,60 +1105,79 @@
874
1105
  let stopped = false;
875
1106
  const stop = () => stopped = true;
876
1107
  for (const el of els) {
877
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, stop)) {
1108
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
878
1109
  if (stopped)
879
1110
  break;
1111
+ if (event.type === "mousemove") {
1112
+ this.highlight(el);
1113
+ break;
1114
+ }
880
1115
  this.select(el, event);
881
1116
  break;
882
1117
  }
883
1118
  }
884
1119
  }
885
1120
  async select(idOrEl, event) {
886
- let el;
887
- if (typeof idOrEl === "string" || typeof idOrEl === "number") {
888
- const runtime2 = await this.renderer?.getRuntime();
889
- el = await runtime2?.select?.(`${idOrEl}`);
890
- if (!el)
891
- throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
892
- } else {
893
- el = idOrEl;
894
- }
1121
+ const el = await this.getTargetElement(idOrEl);
895
1122
  if (el === this.selectedDom)
896
1123
  return;
897
- const runtime = await this.renderer?.getRuntime();
1124
+ const runtime = await this.renderer.getRuntime();
1125
+ await runtime?.select?.(el.id);
898
1126
  if (runtime?.beforeSelect) {
899
1127
  await runtime.beforeSelect(el);
900
1128
  }
901
1129
  this.mask.setLayout(el);
902
- this.dr?.select(el, event);
1130
+ this.dr.select(el, event);
1131
+ this.mask.scrollIntoView(el);
903
1132
  this.selectedDom = el;
1133
+ if (this.renderer.contentWindow) {
1134
+ removeSelectedClassName(this.renderer.contentWindow.document);
1135
+ if (this.selectedDom) {
1136
+ addSelectedClassName(this.selectedDom, this.renderer.contentWindow.document);
1137
+ }
1138
+ }
904
1139
  }
905
1140
  update(data) {
906
1141
  const { config } = data;
907
- this.renderer?.getRuntime().then((runtime) => {
1142
+ return this.renderer?.getRuntime().then((runtime) => {
908
1143
  runtime?.update?.(data);
909
1144
  setTimeout(() => {
910
1145
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
911
- if (el) {
1146
+ if (el && el.id === this.selectedDom?.id) {
1147
+ this.selectedDom = el;
912
1148
  this.mask.setLayout(el);
913
- this.dr?.select(el);
1149
+ this.dr.updateMoveable(el);
914
1150
  }
915
1151
  }, 0);
916
1152
  });
917
1153
  }
1154
+ async highlight(idOrEl) {
1155
+ let el;
1156
+ try {
1157
+ el = await this.getTargetElement(idOrEl);
1158
+ } catch (error) {
1159
+ this.highlightLayer.clearHighlight();
1160
+ return;
1161
+ }
1162
+ if (el === this.highlightedDom)
1163
+ return;
1164
+ this.highlightLayer.highlight(el);
1165
+ this.highlightedDom = el;
1166
+ }
918
1167
  sortNode(data) {
919
- this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1168
+ return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
920
1169
  }
921
1170
  add(data) {
922
- this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1171
+ return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
923
1172
  }
924
1173
  remove(data) {
925
- this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1174
+ return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
926
1175
  }
927
1176
  setZoom(zoom = DEFAULT_ZOOM) {
928
1177
  this.zoom = zoom;
929
1178
  }
930
1179
  mount(el) {
1180
+ this.container = el;
931
1181
  const { mask, renderer } = this;
932
1182
  renderer.mount(el);
933
1183
  mask.mount(el);
@@ -938,11 +1188,22 @@
938
1188
  this.dr.clearGuides();
939
1189
  }
940
1190
  destroy() {
941
- const { mask, renderer, dr } = this;
1191
+ const { mask, renderer, dr, highlightLayer } = this;
942
1192
  renderer.destroy();
943
1193
  mask.destroy();
944
1194
  dr.destroy();
1195
+ highlightLayer.destroy();
945
1196
  this.removeAllListeners();
1197
+ this.container = void 0;
1198
+ }
1199
+ async getTargetElement(idOrEl) {
1200
+ if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1201
+ const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
1202
+ if (!el)
1203
+ throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
1204
+ return el;
1205
+ }
1206
+ return idOrEl;
946
1207
  }
947
1208
  }
948
1209
 
@@ -951,8 +1212,7 @@
951
1212
  exports.StageRender = StageRender;
952
1213
  exports["default"] = StageCore;
953
1214
 
954
- Object.defineProperty(exports, '__esModule', { value: true });
955
- exports[Symbol.toStringTag] = 'Module';
1215
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
956
1216
 
957
1217
  }));
958
1218
  //# sourceMappingURL=tmagic-stage.umd.js.map