@tmagic/stage 1.2.13 → 1.2.14

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.
@@ -289,16 +289,25 @@ class TargetShadow {
289
289
  }
290
290
 
291
291
  class DragResizeHelper {
292
+ /** 目标节点在蒙层上的占位节点,用于跟鼠标交互,避免鼠标事件直接作用到目标节点 */
292
293
  targetShadow;
294
+ /** 要操作的原始目标节点 */
293
295
  target;
296
+ /** 多选:目标节点组 */
294
297
  targetList = [];
298
+ /** 响应拖拽的状态事件,修改绝对定位布局下targetShadow的dom。
299
+ * MoveableHelper里面的方法是成员属性,如果DragResizeHelper用继承的方式将无法通过super去调这些方法 */
295
300
  moveableHelper;
301
+ /** 流式布局下,目标节点的镜像节点 */
296
302
  ghostEl;
303
+ /** 用于记录节点被改变前的位置 */
297
304
  frameSnapShot = {
298
305
  left: 0,
299
306
  top: 0
300
307
  };
308
+ /** 多选模式下的多个节点 */
301
309
  framesSnapShot = [];
310
+ /** 布局方式:流式布局、绝对定位、固定定位 */
302
311
  mode = Mode.ABSOLUTE;
303
312
  constructor(config) {
304
313
  this.moveableHelper = MoveableHelper.create({
@@ -332,6 +341,10 @@ class DragResizeHelper {
332
341
  setMode(mode) {
333
342
  this.mode = mode;
334
343
  }
344
+ /**
345
+ * 改变大小事件开始
346
+ * @param e 包含了拖拽节点的dom,moveableHelper会直接修改拖拽节点
347
+ */
335
348
  onResizeStart(e) {
336
349
  this.moveableHelper.onResizeStart(e);
337
350
  this.frameSnapShot.top = this.target.offsetTop;
@@ -420,6 +433,9 @@ class DragResizeHelper {
420
433
  this.moveableHelper.onResizeGroupStart(e);
421
434
  this.setFramesSnapShot(events);
422
435
  }
436
+ /**
437
+ * 多选状态下通过拖拽边框改变大小,所有选中组件会一起改变大小
438
+ */
423
439
  onResizeGroup(e) {
424
440
  const { events } = e;
425
441
  events.forEach((ev) => {
@@ -493,6 +509,9 @@ class DragResizeHelper {
493
509
  }
494
510
  return { width, height, left, top };
495
511
  }
512
+ /**
513
+ * 多选状态设置多个节点的快照
514
+ */
496
515
  setFramesSnapShot(events) {
497
516
  if (this.framesSnapShot.length > 0)
498
517
  return;
@@ -509,6 +528,9 @@ class DragResizeHelper {
509
528
  });
510
529
  });
511
530
  }
531
+ /**
532
+ * 流式布局把目标节点复制一份进行拖拽,在拖拽结束前不影响页面原布局样式
533
+ */
512
534
  generateGhostEl(el) {
513
535
  if (this.ghostEl) {
514
536
  this.destroyGhostEl();
@@ -622,7 +644,7 @@ const MoveableActionsAble = (handler) => ({
622
644
  "button",
623
645
  {
624
646
  className: "moveable-button",
625
- title: "\u9009\u4E2D\u7236\u7EC4\u4EF6",
647
+ title: "选中父组件",
626
648
  onClick: () => {
627
649
  handler(AbleActionEventType.SELECT_PARENT);
628
650
  }
@@ -651,7 +673,7 @@ const MoveableActionsAble = (handler) => ({
651
673
  ),
652
674
  React.createElement("button", {
653
675
  className: "moveable-button moveable-remove-button",
654
- title: "\u5220\u9664",
676
+ title: "删除",
655
677
  onClick: () => {
656
678
  handler(AbleActionEventType.REMOVE);
657
679
  }
@@ -662,12 +684,19 @@ const MoveableActionsAble = (handler) => ({
662
684
  });
663
685
 
664
686
  class MoveableOptionsManager extends EventEmitter {
687
+ /** 布局方式:流式布局、绝对定位、固定定位 */
665
688
  mode = Mode.ABSOLUTE;
689
+ /** 画布容器 */
666
690
  container;
691
+ /** 水平参考线 */
667
692
  horizontalGuidelines = [];
693
+ /** 垂直参考线 */
668
694
  verticalGuidelines = [];
695
+ /** 对齐元素集合 */
669
696
  elementGuidelines = [];
697
+ /** 由外部调用方(编辑器)传入进来的moveable默认参数,可以为空,也可以是一个回调函数 */
670
698
  customizedOptions;
699
+ /** 获取整个画布的根元素(在StageCore的mount函数中挂载的container) */
671
700
  getRootContainer;
672
701
  constructor(config) {
673
702
  super();
@@ -675,6 +704,11 @@ class MoveableOptionsManager extends EventEmitter {
675
704
  this.container = config.container;
676
705
  this.getRootContainer = config.getRootContainer;
677
706
  }
707
+ /**
708
+ * 设置水平/垂直参考线
709
+ * @param type 参考线类型
710
+ * @param guidelines 参考线坐标数组
711
+ */
678
712
  setGuidelines(type, guidelines) {
679
713
  if (type === GuidesType.HORIZONTAL) {
680
714
  this.horizontalGuidelines = guidelines;
@@ -683,11 +717,19 @@ class MoveableOptionsManager extends EventEmitter {
683
717
  }
684
718
  this.emit("update-moveable");
685
719
  }
720
+ /**
721
+ * 清除横向和纵向的参考线
722
+ */
686
723
  clearGuides() {
687
724
  this.horizontalGuidelines = [];
688
725
  this.verticalGuidelines = [];
689
726
  this.emit("update-moveable");
690
727
  }
728
+ /**
729
+ * 设置有哪些元素要辅助对齐
730
+ * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
731
+ * @param allElList 全部元素列表
732
+ */
691
733
  setElementGuidelines(selectedElList, allElList) {
692
734
  this.elementGuidelines.forEach((node) => {
693
735
  node.remove();
@@ -697,11 +739,21 @@ class MoveableOptionsManager extends EventEmitter {
697
739
  this.container.append(this.createGuidelineElements(selectedElList, allElList));
698
740
  }
699
741
  }
742
+ /**
743
+ * 获取moveable参数
744
+ * @param isMultiSelect 是否多选模式
745
+ * @param runtimeOptions 调用时实时传进来的的moveable参数
746
+ * @returns moveable所需参数
747
+ */
700
748
  getOptions(isMultiSelect, runtimeOptions = {}) {
701
749
  const defaultOptions = this.getDefaultOptions(isMultiSelect);
702
750
  const customizedOptions = this.getCustomizeOptions();
703
751
  return merge(defaultOptions, customizedOptions, runtimeOptions);
704
752
  }
753
+ /**
754
+ * 获取单选和多选的moveable公共参数
755
+ * @returns moveable公共参数
756
+ */
705
757
  getDefaultOptions(isMultiSelect) {
706
758
  const isSortable = this.mode === Mode.SORTABLE;
707
759
  const commonOptions = {
@@ -716,6 +768,7 @@ class MoveableOptionsManager extends EventEmitter {
716
768
  elementGuidelines: this.elementGuidelines,
717
769
  bounds: {
718
770
  top: 0,
771
+ // 设置0的话无法移动到left为0,所以只能设置为-1
719
772
  left: -1,
720
773
  right: this.container.clientWidth - 1,
721
774
  bottom: isSortable ? void 0 : this.container.clientHeight
@@ -724,6 +777,10 @@ class MoveableOptionsManager extends EventEmitter {
724
777
  const differenceOptions = isMultiSelect ? this.getMultiOptions() : this.getSingleOptions();
725
778
  return merge(commonOptions, differenceOptions);
726
779
  }
780
+ /**
781
+ * 获取单选下的差异化参数
782
+ * @returns {MoveableOptions} moveable options参数
783
+ */
727
784
  getSingleOptions() {
728
785
  const isAbsolute = this.mode === Mode.ABSOLUTE;
729
786
  const isFixed = this.mode === Mode.FIXED;
@@ -757,6 +814,10 @@ class MoveableOptionsManager extends EventEmitter {
757
814
  ables: [MoveableActionsAble(this.actionHandler.bind(this))]
758
815
  };
759
816
  }
817
+ /**
818
+ * 获取多选下的差异化参数
819
+ * @returns {MoveableOptions} moveable options参数
820
+ */
760
821
  getMultiOptions() {
761
822
  return {
762
823
  defaultGroupRotate: 0,
@@ -767,15 +828,27 @@ class MoveableOptionsManager extends EventEmitter {
767
828
  padding: { left: 0, top: 0, right: 0, bottom: 0 }
768
829
  };
769
830
  }
831
+ /**
832
+ * 获取业务方自定义的moveable参数
833
+ */
770
834
  getCustomizeOptions() {
771
835
  if (typeof this.customizedOptions === "function") {
772
836
  return this.customizedOptions();
773
837
  }
774
838
  return this.customizedOptions;
775
839
  }
840
+ /**
841
+ * 这是给selectParentAbles的回调函数,用于触发选中父元素事件
842
+ */
776
843
  actionHandler(type) {
777
844
  this.emit(type);
778
845
  }
846
+ /**
847
+ * 为需要辅助对齐的元素创建div
848
+ * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
849
+ * @param allElList 全部元素列表
850
+ * @returns frame 辅助对齐元素集合的页面片
851
+ */
779
852
  createGuidelineElements(selectedElList, allElList) {
780
853
  const frame = globalThis.document.createDocumentFragment();
781
854
  for (const node of allElList) {
@@ -790,6 +863,12 @@ class MoveableOptionsManager extends EventEmitter {
790
863
  }
791
864
  return frame;
792
865
  }
866
+ /**
867
+ * 判断一个元素是否在元素列表里面
868
+ * @param ele 元素
869
+ * @param eleList 元素列表
870
+ * @returns 是否在元素列表里面
871
+ */
793
872
  isInElementList(ele, eleList) {
794
873
  for (const eleItem of eleList) {
795
874
  if (ele === eleItem)
@@ -800,8 +879,11 @@ class MoveableOptionsManager extends EventEmitter {
800
879
  }
801
880
 
802
881
  class StageDragResize extends MoveableOptionsManager {
882
+ /** 目标节点 */
803
883
  target;
884
+ /** Moveable拖拽类实例 */
804
885
  moveable;
886
+ /** 拖动状态 */
805
887
  dragStatus = StageDragStatus.END;
806
888
  dragResizeHelper;
807
889
  disabledDragStart;
@@ -824,6 +906,12 @@ class StageDragResize extends MoveableOptionsManager {
824
906
  getTarget() {
825
907
  return this.target;
826
908
  }
909
+ /**
910
+ * 将选中框渲染并覆盖到选中的组件Dom节点上方
911
+ * 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标
912
+ * @param el 选中组件的Dom节点元素
913
+ * @param event 鼠标事件
914
+ */
827
915
  select(el, event) {
828
916
  if (!this.moveable || el !== this.target) {
829
917
  this.initMoveable(el);
@@ -834,11 +922,14 @@ class StageDragResize extends MoveableOptionsManager {
834
922
  this.moveable?.dragStart(event);
835
923
  }
836
924
  }
925
+ /**
926
+ * 初始化选中框并渲染出来
927
+ */
837
928
  updateMoveable(el = this.target) {
838
929
  if (!this.moveable)
839
930
  return;
840
931
  if (!el)
841
- throw new Error("\u672A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
932
+ throw new Error("未选中任何节点");
842
933
  const options = this.init(el);
843
934
  Object.entries(options).forEach(([key, value]) => {
844
935
  this.moveable[key] = value;
@@ -852,6 +943,9 @@ class StageDragResize extends MoveableOptionsManager {
852
943
  this.moveable.target = null;
853
944
  this.moveable.updateTarget();
854
945
  }
946
+ /**
947
+ * 销毁实例
948
+ */
855
949
  destroy() {
856
950
  this.moveable?.destroy();
857
951
  this.dragResizeHelper.destroy();
@@ -886,7 +980,7 @@ class StageDragResize extends MoveableOptionsManager {
886
980
  }
887
981
  bindResizeEvent() {
888
982
  if (!this.moveable)
889
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
983
+ throw new Error("moveable 未初始化");
890
984
  this.moveable.on("resizeStart", (e) => {
891
985
  if (!this.target)
892
986
  return;
@@ -904,11 +998,11 @@ class StageDragResize extends MoveableOptionsManager {
904
998
  }
905
999
  bindDragEvent() {
906
1000
  if (!this.moveable)
907
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
1001
+ throw new Error("moveable 未初始化");
908
1002
  let timeout;
909
1003
  this.moveable.on("dragStart", (e) => {
910
1004
  if (!this.target)
911
- throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
1005
+ throw new Error("未选中组件");
912
1006
  this.dragStatus = StageDragStatus.START;
913
1007
  this.dragResizeHelper.onDragStart(e);
914
1008
  }).on("drag", (e) => {
@@ -946,7 +1040,7 @@ class StageDragResize extends MoveableOptionsManager {
946
1040
  }
947
1041
  bindRotateEvent() {
948
1042
  if (!this.moveable)
949
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
1043
+ throw new Error("moveable 未初始化");
950
1044
  this.moveable.on("rotateStart", (e) => {
951
1045
  this.dragStatus = StageDragStatus.START;
952
1046
  this.dragResizeHelper.onRotateStart(e);
@@ -972,7 +1066,7 @@ class StageDragResize extends MoveableOptionsManager {
972
1066
  }
973
1067
  bindScaleEvent() {
974
1068
  if (!this.moveable)
975
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
1069
+ throw new Error("moveable 未初始化");
976
1070
  this.moveable.on("scaleStart", (e) => {
977
1071
  this.dragStatus = StageDragStatus.START;
978
1072
  this.dragResizeHelper.onScaleStart(e);
@@ -998,7 +1092,7 @@ class StageDragResize extends MoveableOptionsManager {
998
1092
  }
999
1093
  sort() {
1000
1094
  if (!this.target || !this.dragResizeHelper.getGhostEl())
1001
- throw new Error("\u672A\u77E5\u9519\u8BEF");
1095
+ throw new Error("未知错误");
1002
1096
  const { top } = this.dragResizeHelper.getGhostEl().getBoundingClientRect();
1003
1097
  const { top: oriTop } = this.target.getBoundingClientRect();
1004
1098
  const deltaTop = top - oriTop;
@@ -1051,6 +1145,10 @@ class StageHighlight extends EventEmitter$1 {
1051
1145
  idPrefix: HIGHLIGHT_EL_ID_PREFIX
1052
1146
  });
1053
1147
  }
1148
+ /**
1149
+ * 高亮鼠标悬停的组件
1150
+ * @param el 选中组件的Dom节点元素
1151
+ */
1054
1152
  highlight(el) {
1055
1153
  if (!el || el === this.target)
1056
1154
  return;
@@ -1063,6 +1161,9 @@ class StageHighlight extends EventEmitter$1 {
1063
1161
  zoom: 2
1064
1162
  });
1065
1163
  }
1164
+ /**
1165
+ * 清空高亮
1166
+ */
1066
1167
  clearHighlight() {
1067
1168
  if (!this.moveable || !this.target)
1068
1169
  return;
@@ -1070,6 +1171,9 @@ class StageHighlight extends EventEmitter$1 {
1070
1171
  this.moveable.target = null;
1071
1172
  this.moveable.updateTarget();
1072
1173
  }
1174
+ /**
1175
+ * 销毁实例
1176
+ */
1073
1177
  destroy() {
1074
1178
  this.moveable?.destroy();
1075
1179
  this.targetShadow.destroy();
@@ -1077,8 +1181,11 @@ class StageHighlight extends EventEmitter$1 {
1077
1181
  }
1078
1182
 
1079
1183
  class StageMultiDragResize extends MoveableOptionsManager {
1184
+ /** 画布容器 */
1080
1185
  container;
1186
+ /** 多选:目标节点组 */
1081
1187
  targetList = [];
1188
+ /** Moveable多选拖拽类实例 */
1082
1189
  moveableForMulti;
1083
1190
  dragStatus = StageDragStatus.END;
1084
1191
  dragResizeHelper;
@@ -1103,6 +1210,10 @@ class StageMultiDragResize extends MoveableOptionsManager {
1103
1210
  }
1104
1211
  });
1105
1212
  }
1213
+ /**
1214
+ * 多选
1215
+ * @param els
1216
+ */
1106
1217
  multiSelect(els) {
1107
1218
  if (els.length === 0) {
1108
1219
  return;
@@ -1172,7 +1283,7 @@ class StageMultiDragResize extends MoveableOptionsManager {
1172
1283
  if (!this.moveableForMulti)
1173
1284
  return;
1174
1285
  if (!eleList)
1175
- throw new Error("\u672A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
1286
+ throw new Error("未选中任何节点");
1176
1287
  this.targetList = eleList;
1177
1288
  this.dragResizeHelper.setTargetList(eleList);
1178
1289
  const options = this.getOptions(true, {
@@ -1183,6 +1294,9 @@ class StageMultiDragResize extends MoveableOptionsManager {
1183
1294
  });
1184
1295
  this.moveableForMulti.updateTarget();
1185
1296
  }
1297
+ /**
1298
+ * 清除多选状态
1299
+ */
1186
1300
  clearSelectStatus() {
1187
1301
  if (!this.moveableForMulti)
1188
1302
  return;
@@ -1191,10 +1305,17 @@ class StageMultiDragResize extends MoveableOptionsManager {
1191
1305
  this.moveableForMulti.updateTarget();
1192
1306
  this.targetList = [];
1193
1307
  }
1308
+ /**
1309
+ * 销毁实例
1310
+ */
1194
1311
  destroy() {
1195
1312
  this.moveableForMulti?.destroy();
1196
1313
  this.dragResizeHelper.destroy();
1197
1314
  }
1315
+ /**
1316
+ * 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
1317
+ * @param isResize 是否进行大小缩放
1318
+ */
1198
1319
  update(isResize = false, parentEl = null) {
1199
1320
  if (this.targetList.length === 0)
1200
1321
  return;
@@ -1218,13 +1339,21 @@ class ActionManager extends EventEmitter {
1218
1339
  dr;
1219
1340
  multiDr;
1220
1341
  highlightLayer;
1342
+ /** 单选、多选、高亮的容器(蒙层的content) */
1221
1343
  container;
1344
+ /** 当前选中的节点 */
1222
1345
  selectedEl;
1346
+ /** 多选选中的节点组 */
1223
1347
  selectedElList = [];
1348
+ /** 当前高亮的节点 */
1224
1349
  highlightedEl;
1350
+ /** 当前是否处于多选状态 */
1225
1351
  isMultiSelectStatus = false;
1352
+ /** 当拖拽组件到容器上方进入可加入容器状态时,给容器添加的一个class名称 */
1226
1353
  containerHighlightClassName;
1354
+ /** 当拖拽组件到容器上方时,需要悬停多久才能将组件加入容器 */
1227
1355
  containerHighlightDuration;
1356
+ /** 将组件加入容器的操作方式 */
1228
1357
  containerHighlightType;
1229
1358
  isAltKeydown = false;
1230
1359
  getTargetElement;
@@ -1283,18 +1412,33 @@ class ActionManager extends EventEmitter {
1283
1412
  this.initKeyEvent();
1284
1413
  this.initActionEvent();
1285
1414
  }
1415
+ /**
1416
+ * 设置水平/垂直参考线
1417
+ * @param type 参考线类型
1418
+ * @param guidelines 参考线坐标数组
1419
+ */
1286
1420
  setGuidelines(type, guidelines) {
1287
1421
  this.dr.setGuidelines(type, guidelines);
1288
1422
  this.multiDr.setGuidelines(type, guidelines);
1289
1423
  }
1424
+ /**
1425
+ * 清空所有参考线
1426
+ */
1290
1427
  clearGuides() {
1291
1428
  this.dr.clearGuides();
1292
1429
  this.multiDr.clearGuides();
1293
1430
  }
1431
+ /**
1432
+ * 更新moveable,外部主要调用场景是元素配置变更、页面大小变更
1433
+ * @param el 变更的元素
1434
+ */
1294
1435
  updateMoveable(el) {
1295
1436
  this.dr.updateMoveable(el);
1296
1437
  this.multiDr.updateMoveable();
1297
1438
  }
1439
+ /**
1440
+ * 判断是否单选选中的元素
1441
+ */
1298
1442
  isSelectedEl(el) {
1299
1443
  return el.id === this.selectedEl?.id;
1300
1444
  }
@@ -1307,6 +1451,11 @@ class ActionManager extends EventEmitter {
1307
1451
  getSelectedElList() {
1308
1452
  return this.selectedElList;
1309
1453
  }
1454
+ /**
1455
+ * 获取鼠标下方第一个可选中元素,如果元素层叠,返回到是最上层元素
1456
+ * @param event 鼠标事件
1457
+ * @returns 鼠标下方第一个可选中元素
1458
+ */
1310
1459
  async getElementFromPoint(event) {
1311
1460
  const els = this.getElementsFromPoint(event);
1312
1461
  let stopped = false;
@@ -1319,6 +1468,13 @@ class ActionManager extends EventEmitter {
1319
1468
  }
1320
1469
  }
1321
1470
  }
1471
+ /**
1472
+ * 判断一个元素能否在当前场景被选中
1473
+ * @param el 被判断的元素
1474
+ * @param event 鼠标事件
1475
+ * @param stop 通过该元素如果得知剩下的元素都不可被选中,通知调用方终止对剩下元素的判断
1476
+ * @returns 能否选中
1477
+ */
1322
1478
  async isElCanSelect(el, event, stop) {
1323
1479
  const canSelectByProp = await this.canSelect(el, event, stop);
1324
1480
  if (!canSelectByProp)
@@ -1328,6 +1484,9 @@ class ActionManager extends EventEmitter {
1328
1484
  }
1329
1485
  return true;
1330
1486
  }
1487
+ /**
1488
+ * 判断一个元素是否可以被多选,如果当前元素是page,则调stop函数告诉调用方不必继续判断其它元素了
1489
+ */
1331
1490
  canMultiSelect(el, stop) {
1332
1491
  if (el.className.includes(PAGE_CLASS)) {
1333
1492
  stop();
@@ -1377,6 +1536,10 @@ class ActionManager extends EventEmitter {
1377
1536
  this.setHighlightEl(void 0);
1378
1537
  this.highlightLayer.clearHighlight();
1379
1538
  }
1539
+ /**
1540
+ * 用于在切换选择模式时清除上一次的状态
1541
+ * @param selectType 需要清理的选择模式
1542
+ */
1380
1543
  clearSelectStatus(selectType) {
1381
1544
  if (selectType === SelectStatus.MULTI_SELECT) {
1382
1545
  this.multiDr.clearSelectStatus();
@@ -1385,6 +1548,11 @@ class ActionManager extends EventEmitter {
1385
1548
  this.dr.clearSelectStatus();
1386
1549
  }
1387
1550
  }
1551
+ /**
1552
+ * 找到鼠标下方的容器,通过添加className对容器进行标记
1553
+ * @param event 鼠标事件
1554
+ * @param excludeElList 计算鼠标点所在容器时要排除的元素列表
1555
+ */
1388
1556
  async addContainerHighlightClassName(event, excludeElList) {
1389
1557
  const doc = this.getRenderDocument();
1390
1558
  if (!doc)
@@ -1397,6 +1565,13 @@ class ActionManager extends EventEmitter {
1397
1565
  }
1398
1566
  }
1399
1567
  }
1568
+ /**
1569
+ * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
1570
+ * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
1571
+ * @param event 鼠标事件
1572
+ * @param excludeElList 计算鼠标所在容器时要排除的元素列表
1573
+ * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
1574
+ */
1400
1575
  delayedMarkContainer(event, excludeElList = []) {
1401
1576
  if (this.canAddToContainer()) {
1402
1577
  return globalThis.setTimeout(() => {
@@ -1428,6 +1603,11 @@ class ActionManager extends EventEmitter {
1428
1603
  }
1429
1604
  return options;
1430
1605
  }
1606
+ /**
1607
+ * 在执行多选逻辑前,先准备好多选选中元素
1608
+ * @param el 新选中的元素
1609
+ * @returns 多选选中的元素列表
1610
+ */
1431
1611
  async beforeMultiSelect(event) {
1432
1612
  const el = await this.getElementFromPoint(event);
1433
1613
  if (!el)
@@ -1443,9 +1623,16 @@ class ActionManager extends EventEmitter {
1443
1623
  this.selectedElList.push(el);
1444
1624
  }
1445
1625
  }
1626
+ /**
1627
+ * 当前状态下能否将组件加入容器,默认是鼠标悬停一段时间加入,alt模式则是按住alt+鼠标悬停一段时间加入
1628
+ */
1446
1629
  canAddToContainer() {
1447
1630
  return this.containerHighlightType === ContainerHighlightType.DEFAULT || this.containerHighlightType === ContainerHighlightType.ALT && this.isAltKeydown;
1448
1631
  }
1632
+ /**
1633
+ * 结束对container的标记状态
1634
+ * @returns 标记的容器元素,没有标记的容器时返回null
1635
+ */
1449
1636
  markContainerEnd() {
1450
1637
  const doc = this.getRenderDocument();
1451
1638
  if (doc && this.canAddToContainer()) {
@@ -1459,6 +1646,9 @@ class ActionManager extends EventEmitter {
1459
1646
  this.container.addEventListener("mouseleave", this.mouseLeaveHandler);
1460
1647
  this.container.addEventListener("wheel", this.mouseWheelHandler);
1461
1648
  }
1649
+ /**
1650
+ * 初始化键盘事件监听
1651
+ */
1462
1652
  initKeyEvent() {
1463
1653
  const { isMac } = new Env();
1464
1654
  const ctrl = isMac ? "meta" : "ctrl";
@@ -1483,6 +1673,9 @@ class ActionManager extends EventEmitter {
1483
1673
  this.isAltKeydown = false;
1484
1674
  });
1485
1675
  }
1676
+ /**
1677
+ * 处理单选、多选抛出来的事件
1678
+ */
1486
1679
  initActionEvent() {
1487
1680
  this.dr.on("update", (data) => {
1488
1681
  setTimeout(() => this.emit("update", data));
@@ -1508,6 +1701,9 @@ class ActionManager extends EventEmitter {
1508
1701
  this.emit("change-to-select", el);
1509
1702
  });
1510
1703
  }
1704
+ /**
1705
+ * 在down事件中集中cpu处理画布中选中操作渲染,在up事件中再通知外面的编辑器更新
1706
+ */
1511
1707
  mouseDownHandler = async (event) => {
1512
1708
  this.clearHighlight();
1513
1709
  event.stopImmediatePropagation();
@@ -1542,6 +1738,9 @@ class ActionManager extends EventEmitter {
1542
1738
  }
1543
1739
  return false;
1544
1740
  }
1741
+ /**
1742
+ * 在up事件中负责对外通知选中事件,通知画布之外的编辑器更新
1743
+ */
1545
1744
  mouseUpHandler = () => {
1546
1745
  getDocument().removeEventListener("mouseup", this.mouseUpHandler);
1547
1746
  this.container.addEventListener("mousemove", this.mouseMoveHandler);
@@ -1580,6 +1779,10 @@ class Rule extends EventEmitter {
1580
1779
  });
1581
1780
  this.containerResizeObserver.observe(this.container);
1582
1781
  }
1782
+ /**
1783
+ * 是否显示辅助线
1784
+ * @param isShowGuides 是否显示
1785
+ */
1583
1786
  showGuides(isShowGuides = true) {
1584
1787
  this.isShowGuides = isShowGuides;
1585
1788
  this.hGuides.setState({
@@ -1607,9 +1810,16 @@ class Rule extends EventEmitter {
1607
1810
  guides: vLines
1608
1811
  });
1609
1812
  }
1813
+ /**
1814
+ * 清空所有参考线
1815
+ */
1610
1816
  clearGuides() {
1611
1817
  this.setGuides([[], []]);
1612
1818
  }
1819
+ /**
1820
+ * 是否显示标尺
1821
+ * @param show 是否显示
1822
+ */
1613
1823
  showRule(show = true) {
1614
1824
  if (show) {
1615
1825
  this.hGuides.destroy();
@@ -1739,6 +1949,11 @@ class StageMask extends Rule {
1739
1949
  this.content.style.height = `${this.height}px`;
1740
1950
  }
1741
1951
  }
1952
+ /**
1953
+ * 初始化视窗和蒙层监听,监听元素是否在视窗区域、监听mask蒙层所在的wrapper大小变化
1954
+ * @description 初始化视窗和蒙层监听
1955
+ * @param page 页面Dom节点
1956
+ */
1742
1957
  observe(page) {
1743
1958
  if (!page)
1744
1959
  return;
@@ -1746,6 +1961,10 @@ class StageMask extends Rule {
1746
1961
  this.initObserverIntersection();
1747
1962
  this.initObserverWrapper();
1748
1963
  }
1964
+ /**
1965
+ * 处理页面大小变更,同步页面和mask大小
1966
+ * @param entries ResizeObserverEntry,获取页面最新大小
1967
+ */
1749
1968
  pageResize(entries) {
1750
1969
  const [entry] = entries;
1751
1970
  const { clientHeight, clientWidth } = entry.target;
@@ -1753,12 +1972,20 @@ class StageMask extends Rule {
1753
1972
  this.setWidth(clientWidth);
1754
1973
  this.scroll();
1755
1974
  }
1975
+ /**
1976
+ * 监听一个组件是否在画布可视区域内
1977
+ * @param el 被选中的组件,可能是左侧目录树中选中的
1978
+ */
1756
1979
  observerIntersection(el) {
1757
1980
  this.intersectionObserver?.observe(el);
1758
1981
  }
1982
+ /**
1983
+ * 挂载Dom节点
1984
+ * @param el 将蒙层挂载到该Dom节点上
1985
+ */
1759
1986
  mount(el) {
1760
1987
  if (!this.content)
1761
- throw new Error("content \u4E0D\u5B58\u5728");
1988
+ throw new Error("content 不存在");
1762
1989
  el.appendChild(this.wrapper);
1763
1990
  }
1764
1991
  setLayout(el) {
@@ -1772,6 +1999,9 @@ class StageMask extends Rule {
1772
1999
  this.scrollTop = this.pageScrollParent.scrollTop;
1773
2000
  this.scroll();
1774
2001
  }
2002
+ /**
2003
+ * 销毁实例
2004
+ */
1775
2005
  destroy() {
1776
2006
  this.content?.remove();
1777
2007
  this.page = null;
@@ -1779,6 +2009,9 @@ class StageMask extends Rule {
1779
2009
  this.wrapperResizeObserver?.disconnect();
1780
2010
  super.destroy();
1781
2011
  }
2012
+ /**
2013
+ * 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域
2014
+ */
1782
2015
  initObserverIntersection() {
1783
2016
  this.pageScrollParent = getScrollParent(this.page) || null;
1784
2017
  this.intersectionObserver?.disconnect();
@@ -1801,6 +2034,9 @@ class StageMask extends Rule {
1801
2034
  );
1802
2035
  }
1803
2036
  }
2037
+ /**
2038
+ * 监听mask的容器大小变化
2039
+ */
1804
2040
  initObserverWrapper() {
1805
2041
  this.wrapperResizeObserver?.disconnect();
1806
2042
  if (typeof ResizeObserver !== "undefined") {
@@ -1841,22 +2077,40 @@ class StageMask extends Rule {
1841
2077
  });
1842
2078
  this.content.dispatchEvent(event);
1843
2079
  }
2080
+ /**
2081
+ * 设置蒙层高度
2082
+ * @param height 高度
2083
+ */
1844
2084
  setHeight(height) {
1845
2085
  this.height = height;
1846
2086
  this.setMaxScrollTop();
1847
2087
  this.content.style.height = `${height}px`;
1848
2088
  }
2089
+ /**
2090
+ * 设置蒙层宽度
2091
+ * @param width 宽度
2092
+ */
1849
2093
  setWidth(width) {
1850
2094
  this.width = width;
1851
2095
  this.setMaxScrollLeft();
1852
2096
  this.content.style.width = `${width}px`;
1853
2097
  }
2098
+ /**
2099
+ * 计算并设置最大滚动宽度
2100
+ */
1854
2101
  setMaxScrollLeft() {
1855
2102
  this.maxScrollLeft = Math.max(this.width - this.wrapperWidth, 0);
1856
2103
  }
2104
+ /**
2105
+ * 计算并设置最大滚动高度
2106
+ */
1857
2107
  setMaxScrollTop() {
1858
2108
  this.maxScrollTop = Math.max(this.height - this.wrapperHeight, 0);
1859
2109
  }
2110
+ /**
2111
+ * 修复滚动距离
2112
+ * 由于滚动容器变化等因素,会导致当前滚动的距离不正确
2113
+ */
1860
2114
  fixScrollValue() {
1861
2115
  if (this.scrollTop < 0)
1862
2116
  this.scrollTop = 0;
@@ -1869,7 +2123,7 @@ class StageMask extends Rule {
1869
2123
  }
1870
2124
  mouseWheelHandler = (event) => {
1871
2125
  if (!this.page)
1872
- throw new Error("page \u672A\u521D\u59CB\u5316");
2126
+ throw new Error("page 未初始化");
1873
2127
  const { deltaY, deltaX } = event;
1874
2128
  if (this.page.clientHeight < this.wrapperHeight && deltaY)
1875
2129
  return;
@@ -1889,6 +2143,7 @@ class StageMask extends Rule {
1889
2143
  const style = ".tmagic-stage-container-highlight::after {\n content: '';\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n background-color: #000;\n opacity: .1;\n pointer-events: none;\n}\n\n.magic-ui-container.magic-layout-relative {\n min-height: 50px;\n}\n";
1890
2144
 
1891
2145
  class StageRender extends EventEmitter$1 {
2146
+ /** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
1892
2147
  contentWindow = null;
1893
2148
  runtime = null;
1894
2149
  iframe;
@@ -1942,9 +2197,13 @@ class StageRender extends EventEmitter$1 {
1942
2197
  setZoom(zoom = DEFAULT_ZOOM) {
1943
2198
  this.zoom = zoom;
1944
2199
  }
2200
+ /**
2201
+ * 挂载Dom节点
2202
+ * @param el 将页面挂载到该Dom节点上
2203
+ */
1945
2204
  async mount(el) {
1946
2205
  if (!this.iframe) {
1947
- throw Error("mount \u5931\u8D25");
2206
+ throw Error("mount 失败");
1948
2207
  }
1949
2208
  if (!isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
1950
2209
  let html = await fetch(this.runtimeUrl).then((res) => res.text());
@@ -1970,6 +2229,11 @@ class StageRender extends EventEmitter$1 {
1970
2229
  getDocument() {
1971
2230
  return this.contentWindow?.document;
1972
2231
  }
2232
+ /**
2233
+ * 通过坐标获得坐标下所有HTML元素数组
2234
+ * @param point 坐标
2235
+ * @returns 坐标下方所有HTML元素数组,会包含父元素直至html,元素层叠时返回顺序是从上到下
2236
+ */
1973
2237
  getElementsFromPoint(point) {
1974
2238
  let x = point.clientX;
1975
2239
  let y = point.clientY;
@@ -1986,11 +2250,14 @@ class StageRender extends EventEmitter$1 {
1986
2250
  if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1987
2251
  const el = this.getDocument()?.getElementById(`${idOrEl}`);
1988
2252
  if (!el)
1989
- throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
2253
+ throw new Error(`不存在ID为${idOrEl}的元素`);
1990
2254
  return el;
1991
2255
  }
1992
2256
  return idOrEl;
1993
2257
  }
2258
+ /**
2259
+ * 销毁实例
2260
+ */
1994
2261
  destroy() {
1995
2262
  this.iframe?.removeEventListener("load", this.loadHandler);
1996
2263
  this.contentWindow = null;
@@ -1998,6 +2265,10 @@ class StageRender extends EventEmitter$1 {
1998
2265
  this.iframe = void 0;
1999
2266
  this.removeAllListeners();
2000
2267
  }
2268
+ /**
2269
+ * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
2270
+ * @param el 被选中的元素
2271
+ */
2001
2272
  flagSelectedEl(el) {
2002
2273
  const doc = this.getDocument();
2003
2274
  if (doc) {
@@ -2060,6 +2331,10 @@ class StageCore extends EventEmitter$1 {
2060
2331
  this.initActionEvent();
2061
2332
  this.initMaskEvent();
2062
2333
  }
2334
+ /**
2335
+ * 单选选中元素
2336
+ * @param idOrEl 选中的id或者元素
2337
+ */
2063
2338
  async select(idOrEl, event) {
2064
2339
  const el = this.renderer.getTargetElement(idOrEl);
2065
2340
  if (el === this.actionManager.getSelectedEl())
@@ -2071,6 +2346,10 @@ class StageCore extends EventEmitter$1 {
2071
2346
  this.mask.observerIntersection(el);
2072
2347
  }
2073
2348
  }
2349
+ /**
2350
+ * 多选选中多个元素
2351
+ * @param idOrElList 选中元素的id或元素列表
2352
+ */
2074
2353
  async multiSelect(idOrElList) {
2075
2354
  const els = idOrElList.map((idOrEl) => this.renderer.getTargetElement(idOrEl));
2076
2355
  if (els.length === 0)
@@ -2084,9 +2363,17 @@ class StageCore extends EventEmitter$1 {
2084
2363
  this.mask.observerIntersection(lastEl);
2085
2364
  }
2086
2365
  }
2366
+ /**
2367
+ * 高亮选中元素
2368
+ * @param el 要高亮的元素
2369
+ */
2087
2370
  highlight(idOrEl) {
2088
2371
  this.actionManager.highlight(idOrEl);
2089
2372
  }
2373
+ /**
2374
+ * 更新组件
2375
+ * @param data 更新组件的数据
2376
+ */
2090
2377
  async update(data) {
2091
2378
  const { config } = data;
2092
2379
  await this.renderer.update(data);
@@ -2099,15 +2386,27 @@ class StageCore extends EventEmitter$1 {
2099
2386
  }
2100
2387
  });
2101
2388
  }
2389
+ /**
2390
+ * 往画布增加一个组件
2391
+ * @param data 组件信息数据
2392
+ */
2102
2393
  async add(data) {
2103
2394
  return await this.renderer.add(data);
2104
2395
  }
2396
+ /**
2397
+ * 从画布删除一个组件
2398
+ * @param data 组件信息数据
2399
+ */
2105
2400
  async remove(data) {
2106
2401
  return await this.renderer.remove(data);
2107
2402
  }
2108
2403
  setZoom(zoom = DEFAULT_ZOOM) {
2109
2404
  this.renderer.setZoom(zoom);
2110
2405
  }
2406
+ /**
2407
+ * 挂载Dom节点
2408
+ * @param el 将stage挂载到该Dom节点上
2409
+ */
2111
2410
  async mount(el) {
2112
2411
  this.container = el;
2113
2412
  const { mask, renderer } = this;
@@ -2115,16 +2414,32 @@ class StageCore extends EventEmitter$1 {
2115
2414
  mask.mount(el);
2116
2415
  this.emit("mounted");
2117
2416
  }
2417
+ /**
2418
+ * 清空所有参考线
2419
+ */
2118
2420
  clearGuides() {
2119
2421
  this.mask.clearGuides();
2120
2422
  this.actionManager.clearGuides();
2121
2423
  }
2424
+ /**
2425
+ * @deprecated 废弃接口,建议用delayedMarkContainer代替
2426
+ */
2122
2427
  getAddContainerHighlightClassNameTimeout(event, excludeElList = []) {
2123
2428
  return this.delayedMarkContainer(event, excludeElList);
2124
2429
  }
2430
+ /**
2431
+ * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
2432
+ * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
2433
+ * @param event 鼠标事件
2434
+ * @param excludeElList 计算鼠标所在容器时要排除的元素列表
2435
+ * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
2436
+ */
2125
2437
  delayedMarkContainer(event, excludeElList = []) {
2126
2438
  return this.actionManager.delayedMarkContainer(event, excludeElList);
2127
2439
  }
2440
+ /**
2441
+ * 销毁实例
2442
+ */
2128
2443
  destroy() {
2129
2444
  const { mask, renderer, actionManager, pageResizeObserver } = this;
2130
2445
  renderer.destroy();
@@ -2134,6 +2449,9 @@ class StageCore extends EventEmitter$1 {
2134
2449
  this.removeAllListeners();
2135
2450
  this.container = void 0;
2136
2451
  }
2452
+ /**
2453
+ * 监听页面大小变化
2454
+ */
2137
2455
  observePageResize(page) {
2138
2456
  if (typeof ResizeObserver !== "undefined") {
2139
2457
  this.pageResizeObserver = new ResizeObserver((entries) => {
@@ -2177,12 +2495,18 @@ class StageCore extends EventEmitter$1 {
2177
2495
  this.emit("change-guides", data);
2178
2496
  });
2179
2497
  }
2498
+ /**
2499
+ * 初始化操作相关事件监听
2500
+ */
2180
2501
  initActionEvent() {
2181
2502
  this.initActionManagerEvent();
2182
2503
  this.initDrEvent();
2183
2504
  this.initMulDrEvent();
2184
2505
  this.initHighlightEvent();
2185
2506
  }
2507
+ /**
2508
+ * 初始化ActionManager类本身抛出来的事件监听
2509
+ */
2186
2510
  initActionManagerEvent() {
2187
2511
  this.actionManager.on("before-select", (idOrEl, event) => {
2188
2512
  this.select(idOrEl, event);
@@ -2194,6 +2518,9 @@ class StageCore extends EventEmitter$1 {
2194
2518
  this.emit("multi-select", selectedElList);
2195
2519
  });
2196
2520
  }
2521
+ /**
2522
+ * 初始化DragResize类通过ActionManager抛出来的事件监听
2523
+ */
2197
2524
  initDrEvent() {
2198
2525
  this.actionManager.on("update", (data) => {
2199
2526
  this.emit("update", data);
@@ -2205,6 +2532,9 @@ class StageCore extends EventEmitter$1 {
2205
2532
  this.emit("remove", data);
2206
2533
  });
2207
2534
  }
2535
+ /**
2536
+ * 初始化MultiDragResize类通过ActionManager抛出来的事件监听
2537
+ */
2208
2538
  initMulDrEvent() {
2209
2539
  this.actionManager.on("change-to-select", (el) => {
2210
2540
  this.select(el);
@@ -2213,6 +2543,9 @@ class StageCore extends EventEmitter$1 {
2213
2543
  this.emit("update", data);
2214
2544
  });
2215
2545
  }
2546
+ /**
2547
+ * 初始化Highlight类通过ActionManager抛出来的事件监听
2548
+ */
2216
2549
  initHighlightEvent() {
2217
2550
  this.actionManager.on("highlight", async (highlightEl) => {
2218
2551
  this.emit("highlight", highlightEl);