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

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.
@@ -12,6 +12,8 @@
12
12
  var Guides__default = /*#__PURE__*/_interopDefaultLegacy(Guides);
13
13
 
14
14
  const GHOST_EL_ID_PREFIX = "ghost_el_";
15
+ const DRAG_EL_ID_PREFIX = "drag_el_";
16
+ const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
15
17
  const DEFAULT_ZOOM = 1;
16
18
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
17
19
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -37,6 +39,15 @@
37
39
  })(Mode || {});
38
40
  const SELECTED_CLASS = "tmagic-stage-selected-area";
39
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
+ };
40
51
  const getOffset = (el) => {
41
52
  const { transform } = getComputedStyle(el);
42
53
  const { offsetParent } = el;
@@ -161,58 +172,59 @@
161
172
  if (oldEl) {
162
173
  oldEl.classList.remove(SELECTED_CLASS);
163
174
  oldEl.parentNode?.classList.remove(`${SELECTED_CLASS}-parent`);
175
+ doc.querySelectorAll(`.${SELECTED_CLASS}-parents`).forEach((item) => {
176
+ item.classList.remove(`${SELECTED_CLASS}-parents`);
177
+ });
164
178
  }
165
179
  };
166
- const addSelectedClassName = (el) => {
180
+ const addSelectedClassName = (el, doc) => {
167
181
  el.classList.add(SELECTED_CLASS);
168
182
  el.parentNode?.classList.add(`${SELECTED_CLASS}-parent`);
183
+ getParents(el, doc.body).forEach((item) => {
184
+ item.classList.add(`${SELECTED_CLASS}-parents`);
185
+ });
169
186
  };
170
187
 
171
188
  class StageDragResize extends EventEmitter.EventEmitter {
172
- core;
173
- container;
174
- target;
175
- dragEl;
176
- moveable;
177
- horizontalGuidelines = [];
178
- verticalGuidelines = [];
179
- moveableOptions = {};
180
- dragStatus = "end" /* END */;
181
- ghostEl;
182
- mode = Mode.ABSOLUTE;
183
- moveableHelper;
184
189
  constructor(config) {
185
190
  super();
191
+ this.horizontalGuidelines = [];
192
+ this.verticalGuidelines = [];
193
+ this.elementGuidelines = [];
194
+ this.mode = Mode.ABSOLUTE;
195
+ this.moveableOptions = {};
196
+ this.dragStatus = "end" /* END */;
186
197
  this.core = config.core;
187
198
  this.container = config.container;
199
+ this.dragEl = globalThis.document.createElement("div");
200
+ this.container.append(this.dragEl);
188
201
  }
189
- async select(el, event) {
202
+ select(el, event) {
203
+ const oldTarget = this.target;
190
204
  this.target = el;
191
- if (/(auto|scroll)/.test(this.target.style.overflow)) {
192
- 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();
193
215
  }
194
- this.mode = getMode(el);
195
- this.destroyDragEl();
196
- this.destroyGhostEl();
197
- this.dragEl = this.generateDragEl(el);
198
- this.moveableOptions = await this.getOptions({
199
- target: this.dragEl || this.target
200
- });
201
- this.moveableHelper = MoveableHelper__default["default"].create({
202
- useBeforeRender: true,
203
- useRender: false,
204
- createAuto: true
205
- });
206
- this.initMoveable();
207
216
  if (event) {
208
217
  this.moveable?.dragStart(event);
209
218
  }
210
219
  }
211
- async refresh() {
220
+ updateMoveable(el = this.target) {
212
221
  if (!this.moveable)
213
222
  throw new Error("\u672A\u521D\u59CB\u5316moveable");
214
- const options = await this.getOptions();
215
- 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]) => {
216
228
  this.moveable[key] = value;
217
229
  });
218
230
  this.moveable.updateTarget();
@@ -220,15 +232,19 @@
220
232
  setGuidelines(type, guidelines) {
221
233
  if (type === GuidesType.HORIZONTAL) {
222
234
  this.horizontalGuidelines = guidelines;
235
+ this.moveableOptions.horizontalGuidelines = guidelines;
223
236
  } else if (type === GuidesType.VERTICAL) {
224
237
  this.verticalGuidelines = guidelines;
238
+ this.moveableOptions.verticalGuidelines = guidelines;
225
239
  }
226
- this.refresh();
240
+ this.updateMoveable();
227
241
  }
228
242
  clearGuides() {
229
- this.verticalGuidelines = [];
230
243
  this.horizontalGuidelines = [];
231
- this.refresh();
244
+ this.verticalGuidelines = [];
245
+ this.moveableOptions.horizontalGuidelines = [];
246
+ this.moveableOptions.verticalGuidelines = [];
247
+ this.updateMoveable();
232
248
  }
233
249
  destroy() {
234
250
  this.moveable?.destroy();
@@ -237,6 +253,17 @@
237
253
  this.dragStatus = "end" /* END */;
238
254
  this.removeAllListeners();
239
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
+ }
240
267
  initMoveable() {
241
268
  this.moveable?.destroy();
242
269
  this.moveable = new Moveable__default["default"](this.container, {
@@ -281,22 +308,21 @@
281
308
  bindDragEvent() {
282
309
  if (!this.moveable)
283
310
  throw new Error("moveable \u4E3A\u521D\u59CB\u5316");
284
- let offset = {
285
- left: 0,
286
- top: 0
287
- };
311
+ let offset = null;
288
312
  this.moveable.on("dragStart", (e) => {
289
313
  if (!this.target)
290
314
  throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
291
315
  this.dragStatus = "start" /* START */;
292
316
  this.moveableHelper?.onDragStart(e);
293
- offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
294
317
  if (this.mode === Mode.SORTABLE) {
295
318
  this.ghostEl = this.generateGhostEl(this.target);
296
319
  }
297
320
  }).on("drag", (e) => {
298
321
  if (!this.target || !this.dragEl)
299
322
  return;
323
+ if (!offset) {
324
+ offset = getAbsolutePosition(this.target, { left: 0, top: 0 });
325
+ }
300
326
  this.dragStatus = "ing" /* ING */;
301
327
  const { left, top } = e;
302
328
  if (this.ghostEl) {
@@ -317,17 +343,18 @@
317
343
  this.update();
318
344
  }
319
345
  }
346
+ offset = null;
320
347
  this.dragStatus = "end" /* END */;
321
348
  this.destroyGhostEl();
322
349
  });
323
350
  }
324
- async getSnapElements(el) {
325
- const { renderer } = this.core;
326
- const getSnapElements = (await renderer.getRuntime())?.getSnapElements || (() => {
351
+ getSnapElements(runtime, el) {
352
+ const { renderer, mask } = this.core;
353
+ const getSnapElements = runtime?.getSnapElements || (() => {
327
354
  const doc = renderer.contentWindow?.document;
328
355
  return doc ? Array.from(doc.querySelectorAll("[id]")) : [];
329
356
  });
330
- 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);
331
358
  }
332
359
  sort() {
333
360
  if (!this.target || !this.ghostEl)
@@ -366,7 +393,7 @@
366
393
  }
367
394
  const ghostEl = el.cloneNode(true);
368
395
  const { top, left } = getAbsolutePosition(el, getOffset(el));
369
- ghostEl.id = `${GHOST_EL_ID_PREFIX}${ghostEl.id}`;
396
+ ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
370
397
  ghostEl.style.zIndex = "5";
371
398
  ghostEl.style.opacity = ".5";
372
399
  ghostEl.style.position = "absolute";
@@ -379,44 +406,40 @@
379
406
  this.ghostEl?.remove();
380
407
  this.ghostEl = void 0;
381
408
  }
382
- generateDragEl(el) {
383
- if (this.dragEl) {
384
- this.destroyDragEl();
385
- }
409
+ updateDragEl(el) {
386
410
  const { width, height } = el.getBoundingClientRect();
387
411
  const offset = getOffset(el);
388
- const dragEl = globalThis.document.createElement("div");
389
- dragEl.style.cssText = `
412
+ this.dragEl.style.cssText = `
390
413
  position: absolute;
391
414
  left: ${offset.left}px;
392
415
  top: ${offset.top}px;
393
416
  width: ${width}px;
394
417
  height: ${height}px;
418
+ z-index: 9;
395
419
  `;
396
- this.container.append(dragEl);
397
- return dragEl;
420
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
398
421
  }
399
422
  destroyDragEl() {
400
423
  this.dragEl?.remove();
401
- this.dragEl = void 0;
402
424
  }
403
- async getOptions(options = {}) {
425
+ getOptions(options = {}) {
404
426
  if (!this.target)
405
427
  return {};
406
428
  const isAbsolute = this.mode === Mode.ABSOLUTE;
429
+ const isFixed = this.mode === Mode.FIXED;
407
430
  let { moveableOptions = {} } = this.core.config;
408
431
  if (typeof moveableOptions === "function") {
409
432
  moveableOptions = moveableOptions(this.core);
410
433
  }
411
434
  return {
412
- origin: true,
435
+ origin: false,
413
436
  rootContainer: this.core.container,
414
437
  zoom: 1,
415
438
  dragArea: false,
416
439
  draggable: true,
417
440
  resizable: true,
418
- snappable: isAbsolute,
419
- snapGap: isAbsolute,
441
+ snappable: isAbsolute || isFixed,
442
+ snapGap: isAbsolute || isFixed,
420
443
  snapThreshold: 5,
421
444
  snapDigit: 0,
422
445
  throttleDrag: 0,
@@ -429,8 +452,16 @@
429
452
  center: isAbsolute,
430
453
  middle: isAbsolute
431
454
  },
455
+ elementSnapDirections: {
456
+ top: isAbsolute,
457
+ right: isAbsolute,
458
+ bottom: isAbsolute,
459
+ left: isAbsolute
460
+ },
461
+ isDisplayInnerSnapDigit: true,
432
462
  horizontalGuidelines: this.horizontalGuidelines,
433
463
  verticalGuidelines: this.verticalGuidelines,
464
+ elementGuidelines: this.elementGuidelines,
434
465
  bounds: {
435
466
  top: 0,
436
467
  left: 0,
@@ -499,15 +530,97 @@
499
530
  };
500
531
  };
501
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
+
502
614
  class StageHighlight extends EventEmitter.EventEmitter {
503
- core;
504
- container;
505
- target;
506
- moveable;
507
615
  constructor(config) {
508
616
  super();
509
617
  this.core = config.core;
510
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
+ });
511
624
  }
512
625
  highlight(el) {
513
626
  if (!el || el === this.target)
@@ -515,29 +628,61 @@
515
628
  this.target = el;
516
629
  this.moveable?.destroy();
517
630
  this.moveable = new Moveable__default["default"](this.container, {
518
- target: this.target
631
+ target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
632
+ origin: false,
633
+ rootContainer: this.core.container,
634
+ zoom: 1
519
635
  });
520
636
  }
521
637
  clearHighlight() {
522
638
  if (!this.moveable)
523
639
  return;
640
+ this.target = void 0;
524
641
  this.moveable.target = null;
525
642
  this.moveable.updateTarget();
526
643
  }
527
644
  destroy() {
528
645
  this.moveable?.destroy();
646
+ this.calibrationTarget.destroy();
529
647
  }
530
648
  }
531
649
 
532
650
  class Rule extends EventEmitter__default["default"] {
533
- hGuides;
534
- vGuides;
535
- horizontalGuidelines = [];
536
- verticalGuidelines = [];
537
- container;
538
- containerResizeObserver;
539
651
  constructor(container) {
540
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
+ };
541
686
  this.container = container;
542
687
  this.hGuides = this.createGuides(GuidesType.HORIZONTAL);
543
688
  this.vGuides = this.createGuides(GuidesType.VERTICAL);
@@ -606,37 +751,6 @@
606
751
  this.containerResizeObserver.disconnect();
607
752
  this.removeAllListeners();
608
753
  }
609
- getGuidesStyle = (type) => ({
610
- position: "fixed",
611
- zIndex: 1,
612
- left: type === GuidesType.HORIZONTAL ? 0 : "-30px",
613
- top: type === GuidesType.HORIZONTAL ? "-30px" : 0,
614
- width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
615
- height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
616
- });
617
- createGuides = (type, defaultGuides = []) => new Guides__default["default"](this.container, {
618
- type,
619
- defaultGuides,
620
- displayDragPos: true,
621
- backgroundColor: "#fff",
622
- lineColor: "#000",
623
- textColor: "#000",
624
- style: this.getGuidesStyle(type)
625
- });
626
- hGuidesChangeGuidesHandler = (e) => {
627
- this.horizontalGuidelines = e.guides;
628
- this.emit("changeGuides", {
629
- type: GuidesType.HORIZONTAL,
630
- guides: this.horizontalGuidelines
631
- });
632
- };
633
- vGuidesChangeGuidesHandler = (e) => {
634
- this.verticalGuidelines = e.guides;
635
- this.emit("changeGuides", {
636
- type: GuidesType.VERTICAL,
637
- guides: this.verticalGuidelines
638
- });
639
- };
640
754
  }
641
755
 
642
756
  const wrapperClassName = "editor-mask-wrapper";
@@ -674,32 +788,74 @@
674
788
  return el;
675
789
  };
676
790
  class StageMask extends Rule {
677
- content = createContent();
678
- wrapper;
679
- core;
680
- page = null;
681
- pageScrollParent = null;
682
- scrollTop = 0;
683
- scrollLeft = 0;
684
- width = 0;
685
- height = 0;
686
- wrapperHeight = 0;
687
- wrapperWidth = 0;
688
- mode = Mode.ABSOLUTE;
689
- pageResizeObserver = null;
690
- wrapperResizeObserver = null;
691
- highlightHandler = lodashEs.throttle((event) => {
692
- this.emit("highlight", event);
693
- }, throttleTime);
694
791
  constructor(config) {
695
792
  const wrapper = createWrapper();
696
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.intersectionObserver = null;
806
+ this.mode = Mode.ABSOLUTE;
807
+ this.pageResizeObserver = null;
808
+ this.wrapperResizeObserver = null;
809
+ this.highlightHandler = lodashEs.throttle((event) => {
810
+ this.emit("highlight", event);
811
+ }, throttleTime);
812
+ this.mouseDownHandler = (event) => {
813
+ this.emit("clearHighlight");
814
+ event.stopImmediatePropagation();
815
+ event.stopPropagation();
816
+ if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
817
+ return;
818
+ if (event.target.className.indexOf("moveable-control") !== -1) {
819
+ return;
820
+ }
821
+ this.content.removeEventListener("mousemove", this.highlightHandler);
822
+ this.emit("beforeSelect", event);
823
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
824
+ };
825
+ this.mouseUpHandler = () => {
826
+ globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
827
+ this.content.addEventListener("mousemove", this.highlightHandler);
828
+ this.emit("select");
829
+ };
830
+ this.mouseWheelHandler = (event) => {
831
+ this.emit("clearHighlight");
832
+ if (!this.page)
833
+ throw new Error("page \u672A\u521D\u59CB\u5316");
834
+ const { deltaY, deltaX } = event;
835
+ if (this.page.clientHeight < this.wrapperHeight && deltaY)
836
+ return;
837
+ if (this.page.clientWidth < this.wrapperWidth && deltaX)
838
+ return;
839
+ if (this.maxScrollTop > 0) {
840
+ this.scrollTop = this.scrollTop + deltaY;
841
+ }
842
+ if (this.maxScrollLeft > 0) {
843
+ this.scrollLeft = this.scrollLeft + deltaX;
844
+ }
845
+ this.fixScrollValue();
846
+ this.scroll();
847
+ this.emit("scroll", event);
848
+ };
849
+ this.mouseLeaveHandler = () => {
850
+ setTimeout(() => this.emit("clearHighlight"), throttleTime);
851
+ };
697
852
  this.wrapper = wrapper;
698
853
  this.core = config.core;
699
854
  this.content.addEventListener("mousedown", this.mouseDownHandler);
700
855
  this.wrapper.appendChild(this.content);
701
856
  this.content.addEventListener("wheel", this.mouseWheelHandler);
702
857
  this.content.addEventListener("mousemove", this.highlightHandler);
858
+ this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
703
859
  }
704
860
  setMode(mode) {
705
861
  this.mode = mode;
@@ -718,12 +874,32 @@
718
874
  this.page = page;
719
875
  this.pageScrollParent = getScrollParent(page) || this.core.renderer.contentWindow?.document.documentElement || null;
720
876
  this.pageResizeObserver?.disconnect();
877
+ this.wrapperResizeObserver?.disconnect();
878
+ this.intersectionObserver?.disconnect();
879
+ if (typeof IntersectionObserver !== "undefined") {
880
+ this.intersectionObserver = new IntersectionObserver((entries) => {
881
+ entries.forEach((entry) => {
882
+ const { target, intersectionRatio } = entry;
883
+ if (intersectionRatio <= 0) {
884
+ this.scrollIntoView(target);
885
+ }
886
+ this.intersectionObserver?.unobserve(target);
887
+ });
888
+ }, {
889
+ root: this.pageScrollParent,
890
+ rootMargin: "0px",
891
+ threshold: 1
892
+ });
893
+ }
721
894
  if (typeof ResizeObserver !== "undefined") {
722
895
  this.pageResizeObserver = new ResizeObserver((entries) => {
723
896
  const [entry] = entries;
724
897
  const { clientHeight, clientWidth } = entry.target;
725
898
  this.setHeight(clientHeight);
726
899
  this.setWidth(clientWidth);
900
+ this.fixScrollValue();
901
+ this.scroll();
902
+ this.core.dr.updateMoveable();
727
903
  });
728
904
  this.pageResizeObserver.observe(page);
729
905
  this.wrapperResizeObserver = new ResizeObserver((entries) => {
@@ -731,6 +907,8 @@
731
907
  const { clientHeight, clientWidth } = entry.target;
732
908
  this.wrapperHeight = clientHeight;
733
909
  this.wrapperWidth = clientWidth;
910
+ this.setMaxScrollLeft();
911
+ this.setMaxScrollTop();
734
912
  });
735
913
  this.wrapperResizeObserver.observe(this.wrapper);
736
914
  }
@@ -743,16 +921,31 @@
743
921
  setLayout(el) {
744
922
  this.setMode(isFixedParent(el) ? Mode.FIXED : Mode.ABSOLUTE);
745
923
  }
924
+ scrollIntoView(el) {
925
+ el.scrollIntoView();
926
+ if (!this.pageScrollParent)
927
+ return;
928
+ this.scrollLeft = this.pageScrollParent.scrollLeft;
929
+ this.scrollTop = this.pageScrollParent.scrollTop;
930
+ this.scroll();
931
+ }
746
932
  destroy() {
747
933
  this.content?.remove();
748
934
  this.page = null;
749
935
  this.pageScrollParent = null;
750
936
  this.pageResizeObserver?.disconnect();
751
937
  this.wrapperResizeObserver?.disconnect();
938
+ this.content.removeEventListener("mouseleave", this.mouseLeaveHandler);
752
939
  super.destroy();
753
940
  }
754
941
  scroll() {
755
942
  let { scrollLeft, scrollTop } = this;
943
+ if (this.pageScrollParent) {
944
+ this.pageScrollParent.scrollTo({
945
+ top: scrollTop,
946
+ left: scrollLeft
947
+ });
948
+ }
756
949
  if (this.mode === Mode.FIXED) {
757
950
  scrollLeft = 0;
758
951
  scrollTop = 0;
@@ -765,74 +958,75 @@
765
958
  }
766
959
  setHeight(height) {
767
960
  this.height = height;
961
+ this.setMaxScrollTop();
768
962
  this.content.style.height = `${height}px`;
769
963
  }
770
964
  setWidth(width) {
771
965
  this.width = width;
966
+ this.setMaxScrollLeft();
772
967
  this.content.style.width = `${width}px`;
773
968
  }
774
- mouseDownHandler = (event) => {
775
- event.stopImmediatePropagation();
776
- event.stopPropagation();
777
- if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
778
- return;
779
- if (event.target.className.indexOf("moveable-control") !== -1) {
780
- return;
781
- }
782
- this.emit("beforeSelect", event);
783
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
784
- this.content.removeEventListener("mousemove", this.highlightHandler);
785
- this.emit("clearHighlight");
786
- };
787
- mouseUpHandler = () => {
788
- globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
789
- this.content.addEventListener("mousemove", this.highlightHandler);
790
- this.emit("select");
791
- };
792
- mouseWheelHandler = (event) => {
793
- if (!this.page)
794
- throw new Error("page \u672A\u521D\u59CB\u5316");
795
- const { deltaY, deltaX } = event;
796
- const { height, wrapperHeight, width, wrapperWidth } = this;
797
- const maxScrollTop = height - wrapperHeight;
798
- const maxScrollLeft = width - wrapperWidth;
799
- if (maxScrollTop > 0) {
800
- if (deltaY > 0) {
801
- this.scrollTop = this.scrollTop + Math.min(maxScrollTop - this.scrollTop, deltaY);
802
- } else {
803
- this.scrollTop = Math.max(this.scrollTop + deltaY, 0);
804
- }
805
- }
806
- if (width > wrapperWidth) {
807
- if (deltaX > 0) {
808
- this.scrollLeft = this.scrollLeft + Math.min(maxScrollLeft - this.scrollLeft, deltaX);
809
- } else {
810
- this.scrollLeft = Math.max(this.scrollLeft + deltaX, 0);
811
- }
812
- }
813
- if (this.mode !== Mode.FIXED) {
814
- this.scrollTo(this.scrollLeft, this.scrollTop);
815
- }
816
- if (this.pageScrollParent) {
817
- this.pageScrollParent.scrollTo({
818
- top: this.scrollTop,
819
- left: this.scrollLeft
820
- });
821
- }
822
- this.scroll();
823
- this.emit("scroll", event);
824
- };
969
+ setMaxScrollLeft() {
970
+ this.maxScrollLeft = this.width - this.wrapperWidth;
971
+ }
972
+ setMaxScrollTop() {
973
+ this.maxScrollTop = this.height - this.wrapperHeight;
974
+ }
975
+ fixScrollValue() {
976
+ if (this.scrollTop < 0)
977
+ this.scrollTop = 0;
978
+ if (this.scrollLeft < 0)
979
+ this.scrollLeft = 0;
980
+ if (this.maxScrollTop < this.scrollTop)
981
+ this.scrollTop = this.maxScrollTop;
982
+ if (this.maxScrollLeft < this.scrollLeft)
983
+ this.scrollLeft = this.maxScrollLeft;
984
+ }
825
985
  }
826
986
 
827
987
  class StageRender extends EventEmitter.EventEmitter {
828
- contentWindow = null;
829
- runtime = null;
830
- iframe;
831
- runtimeUrl;
832
- core;
833
- render;
834
988
  constructor({ core }) {
835
989
  super();
990
+ this.contentWindow = null;
991
+ this.runtime = null;
992
+ this.getMagicApi = () => ({
993
+ onPageElUpdate: (el) => this.emit("page-el-update", el),
994
+ onRuntimeReady: (runtime) => {
995
+ this.runtime = runtime;
996
+ globalThis.runtime = runtime;
997
+ this.emit("runtime-ready", runtime);
998
+ }
999
+ });
1000
+ this.getRuntime = () => {
1001
+ if (this.runtime)
1002
+ return Promise.resolve(this.runtime);
1003
+ return new Promise((resolve) => {
1004
+ const listener = (runtime) => {
1005
+ this.off("runtime-ready", listener);
1006
+ resolve(runtime);
1007
+ };
1008
+ this.on("runtime-ready", listener);
1009
+ });
1010
+ };
1011
+ this.loadHandler = async () => {
1012
+ this.emit("onload");
1013
+ if (this.render) {
1014
+ const el = await this.render(this.core);
1015
+ if (el) {
1016
+ this.iframe?.contentDocument?.body?.appendChild(el);
1017
+ }
1018
+ }
1019
+ if (this.contentWindow) {
1020
+ const style = this.contentWindow.document.createElement("style");
1021
+ style.id = "tmagic-stage-render";
1022
+ style.innerHTML = `
1023
+ .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
1024
+ z-index: ${ZIndex.SELECTED_EL};
1025
+ }
1026
+ `;
1027
+ this.contentWindow.document.head.appendChild(style);
1028
+ }
1029
+ };
836
1030
  this.core = core;
837
1031
  this.runtimeUrl = core.config.runtimeUrl || "";
838
1032
  this.render = core.config.render;
@@ -845,14 +1039,6 @@
845
1039
  `;
846
1040
  this.iframe.addEventListener("load", this.loadHandler);
847
1041
  }
848
- getMagicApi = () => ({
849
- onPageElUpdate: (el) => this.emit("page-el-update", el),
850
- onRuntimeReady: (runtime) => {
851
- this.runtime = runtime;
852
- globalThis.runtime = runtime;
853
- this.emit("runtime-ready", runtime);
854
- }
855
- });
856
1042
  async mount(el) {
857
1043
  if (this.iframe) {
858
1044
  if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
@@ -869,17 +1055,6 @@
869
1055
  throw Error("mount \u5931\u8D25");
870
1056
  }
871
1057
  }
872
- getRuntime = () => {
873
- if (this.runtime)
874
- return Promise.resolve(this.runtime);
875
- return new Promise((resolve) => {
876
- const listener = (runtime) => {
877
- this.off("runtime-ready", listener);
878
- resolve(runtime);
879
- };
880
- this.on("runtime-ready", listener);
881
- });
882
- };
883
1058
  destroy() {
884
1059
  this.iframe?.removeEventListener("load", this.loadHandler);
885
1060
  this.contentWindow = null;
@@ -887,49 +1062,25 @@
887
1062
  this.iframe = void 0;
888
1063
  this.removeAllListeners();
889
1064
  }
890
- loadHandler = async () => {
891
- this.emit("onload");
892
- if (this.render) {
893
- const el = await this.render(this.core);
894
- if (el) {
895
- this.iframe?.contentDocument?.body?.appendChild(el);
896
- }
897
- }
898
- if (this.contentWindow) {
899
- const style = this.contentWindow.document.createElement("style");
900
- style.id = "tmagic-stage-render";
901
- style.innerHTML = `
902
- .${SELECTED_CLASS}, .${SELECTED_CLASS}-parent {
903
- z-index: ${ZIndex.SELECTED_EL};
904
- }
905
- `;
906
- this.contentWindow.document.head.appendChild(style);
907
- }
908
- };
909
1065
  }
910
1066
 
911
1067
  class StageCore extends EventEmitter.EventEmitter {
912
- selectedDom;
913
- highlightedDom;
914
- renderer;
915
- mask;
916
- dr;
917
- highlightLayer;
918
- config;
919
- zoom = DEFAULT_ZOOM;
920
- container;
921
- canSelect;
922
1068
  constructor(config) {
923
1069
  super();
1070
+ this.zoom = DEFAULT_ZOOM;
924
1071
  this.config = config;
925
1072
  this.setZoom(config.zoom);
926
1073
  this.canSelect = config.canSelect || ((el) => !!el.id);
927
1074
  this.renderer = new StageRender({ core: this });
928
1075
  this.mask = new StageMask({ core: this });
929
1076
  this.dr = new StageDragResize({ core: this, container: this.mask.content });
930
- this.highlightLayer = new StageHighlight({ core: this, container: this.mask.content });
931
- this.renderer.on("runtime-ready", (runtime) => this.emit("runtime-ready", runtime));
932
- this.renderer.on("page-el-update", (el) => this.mask?.observe(el));
1077
+ this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1078
+ this.renderer.on("runtime-ready", (runtime) => {
1079
+ this.emit("runtime-ready", runtime);
1080
+ });
1081
+ this.renderer.on("page-el-update", (el) => {
1082
+ this.mask?.observe(el);
1083
+ });
933
1084
  this.mask.on("beforeSelect", (event) => {
934
1085
  this.setElementFromPoint(event);
935
1086
  }).on("select", () => {
@@ -939,6 +1090,10 @@
939
1090
  this.emit("changeGuides", data);
940
1091
  }).on("highlight", async (event) => {
941
1092
  await this.setElementFromPoint(event);
1093
+ if (this.highlightedDom === this.selectedDom) {
1094
+ this.highlightLayer.clearHighlight();
1095
+ return;
1096
+ }
942
1097
  this.highlightLayer.highlight(this.highlightedDom);
943
1098
  this.emit("highlight", this.highlightedDom);
944
1099
  }).on("clearHighlight", async () => {
@@ -966,7 +1121,7 @@
966
1121
  let stopped = false;
967
1122
  const stop = () => stopped = true;
968
1123
  for (const el of els) {
969
- if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, stop)) {
1124
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
970
1125
  if (stopped)
971
1126
  break;
972
1127
  if (event.type === "mousemove") {
@@ -983,47 +1138,58 @@
983
1138
  if (el === this.selectedDom)
984
1139
  return;
985
1140
  const runtime = await this.renderer.getRuntime();
1141
+ await runtime?.select?.(el.id);
986
1142
  if (runtime?.beforeSelect) {
987
1143
  await runtime.beforeSelect(el);
988
1144
  }
989
1145
  this.mask.setLayout(el);
990
- this.dr?.select(el, event);
1146
+ this.dr.select(el, event);
1147
+ if (this.config.autoScrollIntoView || el.dataset.autoScrollIntoView) {
1148
+ this.mask.intersectionObserver?.observe(el);
1149
+ }
991
1150
  this.selectedDom = el;
992
1151
  if (this.renderer.contentWindow) {
993
1152
  removeSelectedClassName(this.renderer.contentWindow.document);
994
1153
  if (this.selectedDom) {
995
- addSelectedClassName(this.selectedDom);
1154
+ addSelectedClassName(this.selectedDom, this.renderer.contentWindow.document);
996
1155
  }
997
1156
  }
998
1157
  }
999
1158
  update(data) {
1000
1159
  const { config } = data;
1001
- this.renderer?.getRuntime().then((runtime) => {
1160
+ return this.renderer?.getRuntime().then((runtime) => {
1002
1161
  runtime?.update?.(data);
1003
1162
  setTimeout(() => {
1004
1163
  const el = this.renderer.contentWindow?.document.getElementById(`${config.id}`);
1005
- if (el) {
1164
+ if (el && el.id === this.selectedDom?.id) {
1165
+ this.selectedDom = el;
1006
1166
  this.mask.setLayout(el);
1007
- this.dr?.select(el);
1167
+ this.dr.updateMoveable(el);
1008
1168
  }
1009
1169
  }, 0);
1010
1170
  });
1011
1171
  }
1012
1172
  async highlight(idOrEl) {
1013
- const el = await this.getTargetElement(idOrEl);
1173
+ let el;
1174
+ try {
1175
+ el = await this.getTargetElement(idOrEl);
1176
+ } catch (error) {
1177
+ this.highlightLayer.clearHighlight();
1178
+ return;
1179
+ }
1014
1180
  if (el === this.highlightedDom)
1015
1181
  return;
1016
1182
  this.highlightLayer.highlight(el);
1017
1183
  this.highlightedDom = el;
1018
1184
  }
1019
1185
  sortNode(data) {
1020
- this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1186
+ return this.renderer?.getRuntime().then((runtime) => runtime?.sortNode?.(data));
1021
1187
  }
1022
1188
  add(data) {
1023
- this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1189
+ return this.renderer?.getRuntime().then((runtime) => runtime?.add?.(data));
1024
1190
  }
1025
1191
  remove(data) {
1026
- this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1192
+ return this.renderer?.getRuntime().then((runtime) => runtime?.remove?.(data));
1027
1193
  }
1028
1194
  setZoom(zoom = DEFAULT_ZOOM) {
1029
1195
  this.zoom = zoom;
@@ -1049,16 +1215,13 @@
1049
1215
  this.container = void 0;
1050
1216
  }
1051
1217
  async getTargetElement(idOrEl) {
1052
- let el;
1053
1218
  if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1054
- const runtime = await this.renderer?.getRuntime();
1055
- el = await runtime?.select?.(`${idOrEl}`);
1219
+ const el = this.renderer.contentWindow?.document.getElementById(`${idOrEl}`);
1056
1220
  if (!el)
1057
1221
  throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
1058
- } else {
1059
- el = idOrEl;
1222
+ return el;
1060
1223
  }
1061
- return el;
1224
+ return idOrEl;
1062
1225
  }
1063
1226
  }
1064
1227
 
@@ -1067,8 +1230,7 @@
1067
1230
  exports.StageRender = StageRender;
1068
1231
  exports["default"] = StageCore;
1069
1232
 
1070
- Object.defineProperty(exports, '__esModule', { value: true });
1071
- exports[Symbol.toStringTag] = 'Module';
1233
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
1072
1234
 
1073
1235
  }));
1074
1236
  //# sourceMappingURL=tmagic-stage.umd.js.map