@tmagic/stage 1.0.0-beta.9 → 1.0.0-rc.1

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.
@@ -5,6 +5,8 @@ import { throttle } from 'lodash-es';
5
5
  import Guides from '@scena/guides';
6
6
 
7
7
  const GHOST_EL_ID_PREFIX = "ghost_el_";
8
+ const DRAG_EL_ID_PREFIX = "drag_el_";
9
+ const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
8
10
  const DEFAULT_ZOOM = 1;
9
11
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
10
12
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -162,50 +164,45 @@ const addSelectedClassName = (el) => {
162
164
  };
163
165
 
164
166
  class StageDragResize extends EventEmitter {
165
- core;
166
- container;
167
- target;
168
- dragEl;
169
- moveable;
170
- horizontalGuidelines = [];
171
- verticalGuidelines = [];
172
- moveableOptions = {};
173
- dragStatus = "end" /* END */;
174
- ghostEl;
175
- mode = Mode.ABSOLUTE;
176
- moveableHelper;
177
167
  constructor(config) {
178
168
  super();
169
+ this.horizontalGuidelines = [];
170
+ this.verticalGuidelines = [];
171
+ this.elementGuidelines = [];
172
+ this.mode = Mode.ABSOLUTE;
173
+ this.moveableOptions = {};
174
+ this.dragStatus = "end" /* END */;
179
175
  this.core = config.core;
180
176
  this.container = config.container;
177
+ this.dragEl = globalThis.document.createElement("div");
178
+ this.container.append(this.dragEl);
181
179
  }
182
- async select(el, event) {
180
+ select(el, event) {
181
+ const oldTarget = this.target;
183
182
  this.target = el;
184
- if (/(auto|scroll)/.test(this.target.style.overflow)) {
185
- this.target.style.overflow = "hidden";
183
+ this.init(el);
184
+ if (!this.moveable || this.target !== oldTarget) {
185
+ this.moveableHelper = MoveableHelper.create({
186
+ useBeforeRender: true,
187
+ useRender: false,
188
+ createAuto: true
189
+ });
190
+ this.initMoveable();
191
+ } else {
192
+ this.updateMoveable();
186
193
  }
187
- this.mode = getMode(el);
188
- this.destroyDragEl();
189
- this.destroyGhostEl();
190
- this.dragEl = this.generateDragEl(el);
191
- this.moveableOptions = await this.getOptions({
192
- target: this.dragEl || this.target
193
- });
194
- this.moveableHelper = MoveableHelper.create({
195
- useBeforeRender: true,
196
- useRender: false,
197
- createAuto: true
198
- });
199
- this.initMoveable();
200
194
  if (event) {
201
195
  this.moveable?.dragStart(event);
202
196
  }
203
197
  }
204
- async refresh() {
198
+ updateMoveable(el = this.target) {
205
199
  if (!this.moveable)
206
200
  throw new Error("\u672A\u521D\u59CB\u5316moveable");
207
- const options = await this.getOptions();
208
- Object.entries(options).forEach(([key, value]) => {
201
+ if (!el)
202
+ throw new Error("\u4E3A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
203
+ this.target = el;
204
+ this.init(el);
205
+ Object.entries(this.moveableOptions).forEach(([key, value]) => {
209
206
  this.moveable[key] = value;
210
207
  });
211
208
  this.moveable.updateTarget();
@@ -213,15 +210,19 @@ class StageDragResize extends EventEmitter {
213
210
  setGuidelines(type, guidelines) {
214
211
  if (type === GuidesType.HORIZONTAL) {
215
212
  this.horizontalGuidelines = guidelines;
213
+ this.moveableOptions.horizontalGuidelines = guidelines;
216
214
  } else if (type === GuidesType.VERTICAL) {
217
215
  this.verticalGuidelines = guidelines;
216
+ this.moveableOptions.verticalGuidelines = guidelines;
218
217
  }
219
- this.refresh();
218
+ this.updateMoveable();
220
219
  }
221
220
  clearGuides() {
222
- this.verticalGuidelines = [];
223
221
  this.horizontalGuidelines = [];
224
- this.refresh();
222
+ this.verticalGuidelines = [];
223
+ this.moveableOptions.horizontalGuidelines = [];
224
+ this.moveableOptions.verticalGuidelines = [];
225
+ this.updateMoveable();
225
226
  }
226
227
  destroy() {
227
228
  this.moveable?.destroy();
@@ -230,6 +231,17 @@ class StageDragResize extends EventEmitter {
230
231
  this.dragStatus = "end" /* END */;
231
232
  this.removeAllListeners();
232
233
  }
234
+ init(el) {
235
+ if (/(auto|scroll)/.test(el.style.overflow)) {
236
+ el.style.overflow = "hidden";
237
+ }
238
+ this.mode = getMode(el);
239
+ this.destroyGhostEl();
240
+ this.updateDragEl(el);
241
+ this.moveableOptions = this.getOptions({
242
+ target: this.dragEl
243
+ });
244
+ }
233
245
  initMoveable() {
234
246
  this.moveable?.destroy();
235
247
  this.moveable = new Moveable(this.container, {
@@ -274,22 +286,21 @@ class StageDragResize extends EventEmitter {
274
286
  bindDragEvent() {
275
287
  if (!this.moveable)
276
288
  throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
277
- let offset = {
278
- left: 0,
279
- top: 0
280
- };
289
+ let offset = null;
281
290
  this.moveable.on("dragStart", (e) => {
282
291
  if (!this.target)
283
292
  throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
284
293
  this.dragStatus = "start" /* START */;
285
294
  this.moveableHelper?.onDragStart(e);
286
- offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
287
295
  if (this.mode === Mode.SORTABLE) {
288
296
  this.ghostEl = this.generateGhostEl(this.target);
289
297
  }
290
298
  }).on("drag", (e) => {
291
299
  if (!this.target || !this.dragEl)
292
300
  return;
301
+ if (!offset) {
302
+ offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
303
+ }
293
304
  this.dragStatus = "ing" /* ING */;
294
305
  const { left, top } = e;
295
306
  if (this.ghostEl) {
@@ -310,17 +321,18 @@ class StageDragResize extends EventEmitter {
310
321
  this.update();
311
322
  }
312
323
  }
324
+ offset = null;
313
325
  this.dragStatus = "end" /* END */;
314
326
  this.destroyGhostEl();
315
327
  });
316
328
  }
317
- async getSnapElements(el) {
318
- const { renderer } = this.core;
319
- const getSnapElements = (await renderer.getRuntime())?.getSnapElements || (() => {
329
+ getSnapElements(runtime, el) {
330
+ const { renderer, mask } = this.core;
331
+ const getSnapElements = runtime?.getSnapElements || (() => {
320
332
  const doc = renderer.contentWindow?.document;
321
333
  return doc ? Array.from(doc.querySelectorAll("[id]")) : [];
322
334
  });
323
- return getSnapElements(el).filter((element) => element !== this.target && !this.target?.contains(element));
335
+ return getSnapElements(el).filter((element) => element !== this.target && !this.target?.contains(element) && element !== mask.page);
324
336
  }
325
337
  sort() {
326
338
  if (!this.target || !this.ghostEl)
@@ -359,7 +371,7 @@ class StageDragResize extends EventEmitter {
359
371
  }
360
372
  const ghostEl = el.cloneNode(true);
361
373
  const { top, left } = getAbsolutePosition(el, getOffset(el));
362
- ghostEl.id = `${GHOST_EL_ID_PREFIX}${ghostEl.id}`;
374
+ ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
363
375
  ghostEl.style.zIndex = "5";
364
376
  ghostEl.style.opacity = ".5";
365
377
  ghostEl.style.position = "absolute";
@@ -372,44 +384,40 @@ class StageDragResize extends EventEmitter {
372
384
  this.ghostEl?.remove();
373
385
  this.ghostEl = void 0;
374
386
  }
375
- generateDragEl(el) {
376
- if (this.dragEl) {
377
- this.destroyDragEl();
378
- }
387
+ updateDragEl(el) {
379
388
  const { width, height } = el.getBoundingClientRect();
380
389
  const offset = getOffset(el);
381
- const dragEl = globalThis.document.createElement("div");
382
- dragEl.style.cssText = `
390
+ this.dragEl.style.cssText = `
383
391
  position: absolute;
384
392
  left: ${offset.left}px;
385
393
  top: ${offset.top}px;
386
394
  width: ${width}px;
387
395
  height: ${height}px;
396
+ z-index: 9;
388
397
  `;
389
- this.container.append(dragEl);
390
- return dragEl;
398
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
391
399
  }
392
400
  destroyDragEl() {
393
401
  this.dragEl?.remove();
394
- this.dragEl = void 0;
395
402
  }
396
- async getOptions(options = {}) {
403
+ getOptions(options = {}) {
397
404
  if (!this.target)
398
405
  return {};
399
406
  const isAbsolute = this.mode === Mode.ABSOLUTE;
407
+ const isFixed = this.mode === Mode.FIXED;
400
408
  let { moveableOptions = {} } = this.core.config;
401
409
  if (typeof moveableOptions === "function") {
402
410
  moveableOptions = moveableOptions(this.core);
403
411
  }
404
412
  return {
405
- origin: true,
413
+ origin: false,
406
414
  rootContainer: this.core.container,
407
415
  zoom: 1,
408
416
  dragArea: false,
409
417
  draggable: true,
410
418
  resizable: true,
411
- snappable: isAbsolute,
412
- snapGap: isAbsolute,
419
+ snappable: isAbsolute || isFixed,
420
+ snapGap: isAbsolute || isFixed,
413
421
  snapThreshold: 5,
414
422
  snapDigit: 0,
415
423
  throttleDrag: 0,
@@ -422,8 +430,16 @@ class StageDragResize extends EventEmitter {
422
430
  center: isAbsolute,
423
431
  middle: isAbsolute
424
432
  },
433
+ elementSnapDirections: {
434
+ top: isAbsolute,
435
+ right: isAbsolute,
436
+ bottom: isAbsolute,
437
+ left: isAbsolute
438
+ },
439
+ isDisplayInnerSnapDigit: true,
425
440
  horizontalGuidelines: this.horizontalGuidelines,
426
441
  verticalGuidelines: this.verticalGuidelines,
442
+ elementGuidelines: this.elementGuidelines,
427
443
  bounds: {
428
444
  top: 0,
429
445
  left: 0,
@@ -492,15 +508,97 @@ const up = (deltaTop, target) => {
492
508
  };
493
509
  };
494
510
 
511
+ class TargetCalibrate extends EventEmitter {
512
+ constructor(config) {
513
+ super();
514
+ this.parent = config.parent;
515
+ this.mask = config.mask;
516
+ this.dr = config.dr;
517
+ this.operationEl = globalThis.document.createElement("div");
518
+ this.parent.append(this.operationEl);
519
+ }
520
+ update(el, prefix) {
521
+ const { width, height } = el.getBoundingClientRect();
522
+ const { left, top } = this.getOffset(el);
523
+ this.operationEl.style.cssText = `
524
+ position: absolute;
525
+ left: ${left}px;
526
+ top: ${top}px;
527
+ width: ${width}px;
528
+ height: ${height}px;
529
+ `;
530
+ this.operationEl.id = `${prefix}${el.id}`;
531
+ return this.operationEl;
532
+ }
533
+ destroy() {
534
+ this.operationEl?.remove();
535
+ }
536
+ getOffset(el) {
537
+ const { transform } = getComputedStyle(el);
538
+ const { offsetParent } = el;
539
+ let left = el.offsetLeft;
540
+ let top = el.offsetTop;
541
+ if (transform.indexOf("matrix") > -1) {
542
+ let a = 1;
543
+ let b = 1;
544
+ let c = 1;
545
+ let d = 1;
546
+ let e = 0;
547
+ let f = 0;
548
+ transform.replace(/matrix\((.+), (.+), (.+), (.+), (.+), (.+)\)/, ($0, $1, $2, $3, $4, $5, $6) => {
549
+ a = +$1;
550
+ b = +$2;
551
+ c = +$3;
552
+ d = +$4;
553
+ e = +$5;
554
+ f = +$6;
555
+ return transform;
556
+ });
557
+ left = a * left + c * top + e;
558
+ top = b * left + d * top + f;
559
+ }
560
+ if (offsetParent) {
561
+ const parentOffset = this.getOffset(offsetParent);
562
+ return {
563
+ left: left + parentOffset.left,
564
+ top: top + parentOffset.top
565
+ };
566
+ }
567
+ if (this.dr.mode === Mode.FIXED) {
568
+ if (getMode(el) === Mode.FIXED) {
569
+ return {
570
+ left,
571
+ top
572
+ };
573
+ }
574
+ return {
575
+ left: left - this.mask.scrollLeft,
576
+ top: top - this.mask.scrollTop
577
+ };
578
+ }
579
+ if (getMode(el) === Mode.FIXED) {
580
+ return {
581
+ left: left + this.mask.scrollLeft,
582
+ top: top + this.mask.scrollTop
583
+ };
584
+ }
585
+ return {
586
+ left,
587
+ top
588
+ };
589
+ }
590
+ }
591
+
495
592
  class StageHighlight extends EventEmitter {
496
- core;
497
- container;
498
- target;
499
- moveable;
500
593
  constructor(config) {
501
594
  super();
502
595
  this.core = config.core;
503
596
  this.container = config.container;
597
+ this.calibrationTarget = new TargetCalibrate({
598
+ parent: this.core.mask.content,
599
+ mask: this.core.mask,
600
+ dr: this.core.dr
601
+ });
504
602
  }
505
603
  highlight(el) {
506
604
  if (!el || el === this.target)
@@ -508,29 +606,61 @@ class StageHighlight extends EventEmitter {
508
606
  this.target = el;
509
607
  this.moveable?.destroy();
510
608
  this.moveable = new Moveable(this.container, {
511
- target: this.target
609
+ target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
610
+ origin: false,
611
+ rootContainer: this.core.container,
612
+ zoom: 1
512
613
  });
513
614
  }
514
615
  clearHighlight() {
515
616
  if (!this.moveable)
516
617
  return;
618
+ this.target = void 0;
517
619
  this.moveable.target = null;
518
620
  this.moveable.updateTarget();
519
621
  }
520
622
  destroy() {
521
623
  this.moveable?.destroy();
624
+ this.calibrationTarget.destroy();
522
625
  }
523
626
  }
524
627
 
525
628
  class Rule extends EventEmitter$1 {
526
- hGuides;
527
- vGuides;
528
- horizontalGuidelines = [];
529
- verticalGuidelines = [];
530
- container;
531
- containerResizeObserver;
532
629
  constructor(container) {
533
630
  super();
631
+ this.horizontalGuidelines = [];
632
+ this.verticalGuidelines = [];
633
+ this.getGuidesStyle = (type) => ({
634
+ position: "fixed",
635
+ zIndex: 1,
636
+ left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
637
+ top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
638
+ width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
639
+ height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
640
+ });
641
+ this.createGuides = (type, defaultGuides = []) => new Guides(this.container, {
642
+ type,
643
+ defaultGuides,
644
+ displayDragPos: true,
645
+ backgroundColor: "#fff",
646
+ lineColor: "#000",
647
+ textColor: "#000",
648
+ style: this.getGuidesStyle(type)
649
+ });
650
+ this.hGuidesChangeGuidesHandler = (e) => {
651
+ this.horizontalGuidelines = e.guides;
652
+ this.emit("changeGuides", {
653
+ type: GuidesType.HORIZONTAL,
654
+ guides: this.horizontalGuidelines
655
+ });
656
+ };
657
+ this.vGuidesChangeGuidesHandler = (e) => {
658
+ this.verticalGuidelines = e.guides;
659
+ this.emit("changeGuides", {
660
+ type: GuidesType.VERTICAL,
661
+ guides: this.verticalGuidelines
662
+ });
663
+ };
534
664
  this.container = container;
535
665
  this.hGuides = this.createGuides(GuidesType.HORIZONTAL);
536
666
  this.vGuides = this.createGuides(GuidesType.VERTICAL);
@@ -599,37 +729,6 @@ class Rule extends EventEmitter$1 {
599
729
  this.containerResizeObserver.disconnect();
600
730
  this.removeAllListeners();
601
731
  }
602
- getGuidesStyle = (type) => ({
603
- position: "fixed",
604
- zIndex: 1,
605
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
606
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
607
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
608
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
609
- });
610
- createGuides = (type, defaultGuides = []) => new Guides(this.container, {
611
- type,
612
- defaultGuides,
613
- displayDragPos: true,
614
- backgroundColor: "#fff",
615
- lineColor: "#000",
616
- textColor: "#000",
617
- style: this.getGuidesStyle(type)
618
- });
619
- hGuidesChangeGuidesHandler = (e) => {
620
- this.horizontalGuidelines = e.guides;
621
- this.emit("changeGuides", {
622
- type: GuidesType.HORIZONTAL,
623
- guides: this.horizontalGuidelines
624
- });
625
- };
626
- vGuidesChangeGuidesHandler = (e) => {
627
- this.verticalGuidelines = e.guides;
628
- this.emit("changeGuides", {
629
- type: GuidesType.VERTICAL,
630
- guides: this.verticalGuidelines
631
- });
632
- };
633
732
  }
634
733
 
635
734
  const wrapperClassName = "editor-mask-wrapper";
@@ -667,32 +766,73 @@ const createWrapper = () => {
667
766
  return el;
668
767
  };
669
768
  class StageMask extends Rule {
670
- content = createContent();
671
- wrapper;
672
- core;
673
- page = null;
674
- pageScrollParent = null;
675
- scrollTop = 0;
676
- scrollLeft = 0;
677
- width = 0;
678
- height = 0;
679
- wrapperHeight = 0;
680
- wrapperWidth = 0;
681
- mode = Mode.ABSOLUTE;
682
- pageResizeObserver = null;
683
- wrapperResizeObserver = null;
684
- highlightHandler = throttle((event) => {
685
- this.emit("highlight", event);
686
- }, throttleTime);
687
769
  constructor(config) {
688
770
  const wrapper = createWrapper();
689
771
  super(wrapper);
772
+ this.content = createContent();
773
+ this.page = null;
774
+ this.pageScrollParent = null;
775
+ this.scrollTop = 0;
776
+ this.scrollLeft = 0;
777
+ this.width = 0;
778
+ this.height = 0;
779
+ this.wrapperHeight = 0;
780
+ this.wrapperWidth = 0;
781
+ this.maxScrollTop = 0;
782
+ this.maxScrollLeft = 0;
783
+ this.mode = Mode.ABSOLUTE;
784
+ this.pageResizeObserver = null;
785
+ this.wrapperResizeObserver = null;
786
+ this.highlightHandler = throttle((event) => {
787
+ this.emit("highlight", event);
788
+ }, throttleTime);
789
+ this.mouseDownHandler = (event) => {
790
+ this.emit("clearHighlight");
791
+ event.stopImmediatePropagation();
792
+ event.stopPropagation();
793
+ if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
794
+ return;
795
+ if (event.target.className.indexOf("moveable-control") !== -1) {
796
+ return;
797
+ }
798
+ this.content.removeEventListener("mousemove", this.highlightHandler);
799
+ this.emit("beforeSelect", event);
800
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
801
+ };
802
+ this.mouseUpHandler = () => {
803
+ globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
804
+ this.content.addEventListener("mousemove", this.highlightHandler);
805
+ this.emit("select");
806
+ };
807
+ this.mouseWheelHandler = (event) => {
808
+ this.emit("clearHighlight");
809
+ if (!this.page)
810
+ throw new Error("page \u672A\u521D\u59CB\u5316");
811
+ const { deltaY, deltaX } = event;
812
+ if (this.page.clientHeight < this.wrapperHeight && deltaY)
813
+ return;
814
+ if (this.page.clientWidth < this.wrapperWidth && deltaX)
815
+ return;
816
+ if (this.maxScrollTop > 0) {
817
+ this.scrollTop = this.scrollTop + deltaY;
818
+ }
819
+ if (this.maxScrollLeft > 0) {
820
+ this.scrollLeft = this.scrollLeft + deltaX;
821
+ }
822
+ this.fixScrollValue();
823
+ this.scroll();
824
+ this.emit("scroll", event);
825
+ };
826
+ this.mouseLeaveHandler = () => {
827
+ setTimeout(() => this.emit("clearHighlight"), throttleTime);
828
+ };
690
829
  this.wrapper = wrapper;
691
830
  this.core = config.core;
692
831
  this.content.addEventListener("mousedown", this.mouseDownHandler);
693
832
  this.wrapper.appendChild(this.content);
694
833
  this.content.addEventListener("wheel", this.mouseWheelHandler);
695
834
  this.content.addEventListener("mousemove", this.highlightHandler);
835
+ this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
696
836
  }
697
837
  setMode(mode) {
698
838
  this.mode = mode;
@@ -717,6 +857,9 @@ class StageMask extends Rule {
717
857
  const { clientHeight, clientWidth } = entry.target;
718
858
  this.setHeight(clientHeight);
719
859
  this.setWidth(clientWidth);
860
+ this.fixScrollValue();
861
+ this.scroll();
862
+ this.core.dr.updateMoveable();
720
863
  });
721
864
  this.pageResizeObserver.observe(page);
722
865
  this.wrapperResizeObserver = new ResizeObserver((entries) => {
@@ -724,6 +867,8 @@ class StageMask extends Rule {
724
867
  const { clientHeight, clientWidth } = entry.target;
725
868
  this.wrapperHeight = clientHeight;
726
869
  this.wrapperWidth = clientWidth;
870
+ this.setMaxScrollLeft();
871
+ this.setMaxScrollTop();
727
872
  });
728
873
  this.wrapperResizeObserver.observe(this.wrapper);
729
874
  }
@@ -742,10 +887,17 @@ class StageMask extends Rule {
742
887
  this.pageScrollParent = null;
743
888
  this.pageResizeObserver?.disconnect();
744
889
  this.wrapperResizeObserver?.disconnect();
890
+ this.content.removeEventListener("mouseleave", this.mouseLeaveHandler);
745
891
  super.destroy();
746
892
  }
747
893
  scroll() {
748
894
  let { scrollLeft, scrollTop } = this;
895
+ if (this.pageScrollParent) {
896
+ this.pageScrollParent.scrollTo({
897
+ top: scrollTop,
898
+ left: scrollLeft
899
+ });
900
+ }
749
901
  if (this.mode === Mode.FIXED) {
750
902
  scrollLeft = 0;
751
903
  scrollTop = 0;
@@ -758,74 +910,75 @@ class StageMask extends Rule {
758
910
  }
759
911
  setHeight(height) {
760
912
  this.height = height;
913
+ this.setMaxScrollTop();
761
914
  this.content.style.height = `${height}px`;
762
915
  }
763
916
  setWidth(width) {
764
917
  this.width = width;
918
+ this.setMaxScrollLeft();
765
919
  this.content.style.width = `${width}px`;
766
920
  }
767
- mouseDownHandler = (event) => {
768
- event.stopImmediatePropagation();
769
- event.stopPropagation();
770
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
771
- return;
772
- if (event.target.className.indexOf("moveable-control") !== -1) {
773
- return;
774
- }
775
- this.emit("beforeSelect", event);
776
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
777
- this.content.removeEventListener("mousemove", this.highlightHandler);
778
- this.emit("clearHighlight");
779
- };
780
- mouseUpHandler = () => {
781
- globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
782
- this.content.addEventListener("mousemove", this.highlightHandler);
783
- this.emit("select");
784
- };
785
- mouseWheelHandler = (event) => {
786
- if (!this.page)
787
- throw new Error("page \u672A\u521D\u59CB\u5316");
788
- const { deltaY, deltaX } = event;
789
- const { height, wrapperHeight, width, wrapperWidth } = this;
790
- const maxScrollTop = height - wrapperHeight;
791
- const maxScrollLeft = width - wrapperWidth;
792
- if (maxScrollTop > 0) {
793
- if (deltaY > 0) {
794
- this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
795
- } else {
796
- this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
797
- }
798
- }
799
- if (width > wrapperWidth) {
800
- if (deltaX > 0) {
801
- this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
802
- } else {
803
- this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
804
- }
805
- }
806
- if (this.mode !== Mode.FIXED) {
807
- this.scrollTo(this.scrollLeft, this.scrollTop);
808
- }
809
- if (this.pageScrollParent) {
810
- this.pageScrollParent.scrollTo({
811
- top: this.scrollTop,
812
- left: this.scrollLeft
813
- });
814
- }
815
- this.scroll();
816
- this.emit("scroll", event);
817
- };
921
+ setMaxScrollLeft() {
922
+ this.maxScrollLeft = this.width - this.wrapperWidth;
923
+ }
924
+ setMaxScrollTop() {
925
+ this.maxScrollTop = this.height - this.wrapperHeight;
926
+ }
927
+ fixScrollValue() {
928
+ if (this.scrollTop < 0)
929
+ this.scrollTop = 0;
930
+ if (this.scrollLeft < 0)
931
+ this.scrollLeft = 0;
932
+ if (this.maxScrollTop < this.scrollTop)
933
+ this.scrollTop = this.maxScrollTop;
934
+ if (this.maxScrollLeft < this.scrollLeft)
935
+ this.scrollLeft = this.maxScrollLeft;
936
+ }
818
937
  }
819
938
 
820
939
  class StageRender extends EventEmitter {
821
- contentWindow = null;
822
- runtime = null;
823
- iframe;
824
- runtimeUrl;
825
- core;
826
- render;
827
940
  constructor({ core }) {
828
941
  super();
942
+ this.contentWindow = null;
943
+ this.runtime = null;
944
+ this.getMagicApi = () => ({
945
+ onPageElUpdate: (el) => this.emit("page-el-update", el),
946
+ onRuntimeReady: (runtime) => {
947
+ this.runtime = runtime;
948
+ globalThis.runtime = runtime;
949
+ this.emit("runtime-ready", runtime);
950
+ }
951
+ });
952
+ this.getRuntime = () => {
953
+ if (this.runtime)
954
+ return Promise.resolve(this.runtime);
955
+ return new Promise((resolve) => {
956
+ const listener = (runtime) => {
957
+ this.off("runtime-ready", listener);
958
+ resolve(runtime);
959
+ };
960
+ this.on("runtime-ready", listener);
961
+ });
962
+ };
963
+ this.loadHandler = async () => {
964
+ this.emit("onload");
965
+ if (this.render) {
966
+ const el = await this.render(this.core);
967
+ if (el) {
968
+ this.iframe?.contentDocument?.body?.appendChild(el);
969
+ }
970
+ }
971
+ if (this.contentWindow) {
972
+ const style = this.contentWindow.document.createElement("style");
973
+ style.id = "tmagic-stage-render";
974
+ style.innerHTML = `
975
+ .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
976
+ z-index: ${ZIndex.SELECTED_EL};
977
+ }
978
+ `;
979
+ this.contentWindow.document.head.appendChild(style);
980
+ }
981
+ };
829
982
  this.core = core;
830
983
  this.runtimeUrl = core.config.runtimeUrl || "";
831
984
  this.render = core.config.render;
@@ -838,14 +991,6 @@ class StageRender extends EventEmitter {
838
991
  `;
839
992
  this.iframe.addEventListener("load", this.loadHandler);
840
993
  }
841
- getMagicApi = () => ({
842
- onPageElUpdate: (el) => this.emit("page-el-update", el),
843
- onRuntimeReady: (runtime) => {
844
- this.runtime = runtime;
845
- globalThis.runtime = runtime;
846
- this.emit("runtime-ready", runtime);
847
- }
848
- });
849
994
  async mount(el) {
850
995
  if (this.iframe) {
851
996
  if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
@@ -862,17 +1007,6 @@ class StageRender extends EventEmitter {
862
1007
  throw Error("mount \u5931\u8D25");
863
1008
  }
864
1009
  }
865
- getRuntime = () => {
866
- if (this.runtime)
867
- return Promise.resolve(this.runtime);
868
- return new Promise((resolve) => {
869
- const listener = (runtime) => {
870
- this.off("runtime-ready", listener);
871
- resolve(runtime);
872
- };
873
- this.on("runtime-ready", listener);
874
- });
875
- };
876
1010
  destroy() {
877
1011
  this.iframe?.removeEventListener("load", this.loadHandler);
878
1012
  this.contentWindow = null;
@@ -880,49 +1014,25 @@ class StageRender extends EventEmitter {
880
1014
  this.iframe = void 0;
881
1015
  this.removeAllListeners();
882
1016
  }
883
- loadHandler = async () => {
884
- this.emit("onload");
885
- if (this.render) {
886
- const el = await this.render(this.core);
887
- if (el) {
888
- this.iframe?.contentDocument?.body?.appendChild(el);
889
- }
890
- }
891
- if (this.contentWindow) {
892
- const style = this.contentWindow.document.createElement("style");
893
- style.id = "tmagic-stage-render";
894
- style.innerHTML = `
895
- .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
896
- z-index: ${ZIndex.SELECTED_EL};
897
- }
898
- `;
899
- this.contentWindow.document.head.appendChild(style);
900
- }
901
- };
902
1017
  }
903
1018
 
904
1019
  class StageCore extends EventEmitter {
905
- selectedDom;
906
- highlightedDom;
907
- renderer;
908
- mask;
909
- dr;
910
- highlightLayer;
911
- config;
912
- zoom = DEFAULT_ZOOM;
913
- container;
914
- canSelect;
915
1020
  constructor(config) {
916
1021
  super();
1022
+ this.zoom = DEFAULT_ZOOM;
917
1023
  this.config = config;
918
1024
  this.setZoom(config.zoom);
919
1025
  this.canSelect = config.canSelect || ((el) => !!el.id);
920
1026
  this.renderer = new StageRender({ core: this });
921
1027
  this.mask = new StageMask({ core: this });
922
1028
  this.dr = new StageDragResize({ core: this, container: this.mask.content });
923
- this.highlightLayer = new StageHighlight({ core: this, container: this.mask.content });
924
- this.renderer.on("runtime-ready", (runtime) => this.emit("runtime-ready", runtime));
925
- this.renderer.on("page-el-update", (el) => this.mask?.observe(el));
1029
+ this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1030
+ this.renderer.on("runtime-ready", (runtime) => {
1031
+ this.emit("runtime-ready", runtime);
1032
+ });
1033
+ this.renderer.on("page-el-update", (el) => {
1034
+ this.mask?.observe(el);
1035
+ });
926
1036
  this.mask.on("beforeSelect", (event) => {
927
1037
  this.setElementFromPoint(event);
928
1038
  }).on("select", () => {
@@ -932,6 +1042,10 @@ class StageCore extends EventEmitter {
932
1042
  this.emit("changeGuides", data);
933
1043
  }).on("highlight", async (event) => {
934
1044
  await this.setElementFromPoint(event);
1045
+ if (this.highlightedDom === this.selectedDom) {
1046
+ this.highlightLayer.clearHighlight();
1047
+ return;
1048
+ }
935
1049
  this.highlightLayer.highlight(this.highlightedDom);
936
1050
  this.emit("highlight", this.highlightedDom);
937
1051
  }).on("clearHighlight", async () => {
@@ -959,7 +1073,7 @@ class StageCore extends EventEmitter {
959
1073
  let stopped = false;
960
1074
  const stop = () => stopped = true;
961
1075
  for (const el of els) {
962
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, stop)) {
1076
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
963
1077
  if (stopped)
964
1078
  break;
965
1079
  if (event.type === "mousemove") {
@@ -976,11 +1090,12 @@ class StageCore extends EventEmitter {
976
1090
  if (el === this.selectedDom)
977
1091
  return;
978
1092
  const runtime = await this.renderer.getRuntime();
1093
+ await runtime?.select?.(el.id);
979
1094
  if (runtime?.beforeSelect) {
980
1095
  await runtime.beforeSelect(el);
981
1096
  }
982
1097
  this.mask.setLayout(el);
983
- this.dr?.select(el, event);
1098
+ this.dr.select(el, event);
984
1099
  this.selectedDom = el;
985
1100
  if (this.renderer.contentWindow) {
986
1101
  removeSelectedClassName(this.renderer.contentWindow.document);
@@ -991,32 +1106,39 @@ class StageCore extends EventEmitter {
991
1106
  }
992
1107
  update(data) {
993
1108
  const { config } = data;
994
- this.renderer?.getRuntime().then((runtime) => {
1109
+ return this.renderer?.getRuntime().then((runtime) => {
995
1110
  runtime?.update?.(data);
996
1111
  setTimeout(() => {
997
1112
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
998
- if (el) {
1113
+ if (el && el.id === this.selectedDom?.id) {
1114
+ this.selectedDom = el;
999
1115
  this.mask.setLayout(el);
1000
- this.dr?.select(el);
1116
+ this.dr.updateMoveable(el);
1001
1117
  }
1002
1118
  }, 0);
1003
1119
  });
1004
1120
  }
1005
1121
  async highlight(idOrEl) {
1006
- const el = await this.getTargetElement(idOrEl);
1122
+ let el;
1123
+ try {
1124
+ el = await this.getTargetElement(idOrEl);
1125
+ } catch (error) {
1126
+ this.highlightLayer.clearHighlight();
1127
+ return;
1128
+ }
1007
1129
  if (el === this.highlightedDom)
1008
1130
  return;
1009
1131
  this.highlightLayer.highlight(el);
1010
1132
  this.highlightedDom = el;
1011
1133
  }
1012
1134
  sortNode(data) {
1013
- this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1135
+ return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1014
1136
  }
1015
1137
  add(data) {
1016
- this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1138
+ return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1017
1139
  }
1018
1140
  remove(data) {
1019
- this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1141
+ return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1020
1142
  }
1021
1143
  setZoom(zoom = DEFAULT_ZOOM) {
1022
1144
  this.zoom = zoom;
@@ -1042,16 +1164,13 @@ class StageCore extends EventEmitter {
1042
1164
  this.container = void 0;
1043
1165
  }
1044
1166
  async getTargetElement(idOrEl) {
1045
- let el;
1046
1167
  if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1047
- const runtime = await this.renderer?.getRuntime();
1048
- el = await runtime?.select?.(`${idOrEl}`);
1168
+ const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
1049
1169
  if (!el)
1050
1170
  throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
1051
- } else {
1052
- el = idOrEl;
1171
+ return el;
1053
1172
  }
1054
- return el;
1173
+ return idOrEl;
1055
1174
  }
1056
1175
  }
1057
1176