@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.
@@ -4,14 +4,6 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.TMagicStage = {}, global.EventEmitter, global.KeyController, global.lodashEs, global.core, global.utils, global.MoveableHelper, global.Moveable, global.Guides));
5
5
  })(this, (function (exports, EventEmitter, KeyController, lodashEs, core, utils, MoveableHelper, Moveable, Guides) { 'use strict';
6
6
 
7
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
8
-
9
- const EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
10
- const KeyController__default = /*#__PURE__*/_interopDefaultLegacy(KeyController);
11
- const MoveableHelper__default = /*#__PURE__*/_interopDefaultLegacy(MoveableHelper);
12
- const Moveable__default = /*#__PURE__*/_interopDefaultLegacy(Moveable);
13
- const Guides__default = /*#__PURE__*/_interopDefaultLegacy(Guides);
14
-
15
7
  const GHOST_EL_ID_PREFIX = "ghost_el_";
16
8
  const DRAG_EL_ID_PREFIX = "drag_el_";
17
9
  const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
@@ -294,19 +286,28 @@
294
286
  }
295
287
 
296
288
  class DragResizeHelper {
289
+ /** 目标节点在蒙层上的占位节点,用于跟鼠标交互,避免鼠标事件直接作用到目标节点 */
297
290
  targetShadow;
291
+ /** 要操作的原始目标节点 */
298
292
  target;
293
+ /** 多选:目标节点组 */
299
294
  targetList = [];
295
+ /** 响应拖拽的状态事件,修改绝对定位布局下targetShadow的dom。
296
+ * MoveableHelper里面的方法是成员属性,如果DragResizeHelper用继承的方式将无法通过super去调这些方法 */
300
297
  moveableHelper;
298
+ /** 流式布局下,目标节点的镜像节点 */
301
299
  ghostEl;
300
+ /** 用于记录节点被改变前的位置 */
302
301
  frameSnapShot = {
303
302
  left: 0,
304
303
  top: 0
305
304
  };
305
+ /** 多选模式下的多个节点 */
306
306
  framesSnapShot = [];
307
+ /** 布局方式:流式布局、绝对定位、固定定位 */
307
308
  mode = Mode.ABSOLUTE;
308
309
  constructor(config) {
309
- this.moveableHelper = MoveableHelper__default.default.create({
310
+ this.moveableHelper = MoveableHelper.create({
310
311
  useBeforeRender: true,
311
312
  useRender: false,
312
313
  createAuto: true
@@ -337,6 +338,10 @@
337
338
  setMode(mode) {
338
339
  this.mode = mode;
339
340
  }
341
+ /**
342
+ * 改变大小事件开始
343
+ * @param e 包含了拖拽节点的dom,moveableHelper会直接修改拖拽节点
344
+ */
340
345
  onResizeStart(e) {
341
346
  this.moveableHelper.onResizeStart(e);
342
347
  this.frameSnapShot.top = this.target.offsetTop;
@@ -425,6 +430,9 @@
425
430
  this.moveableHelper.onResizeGroupStart(e);
426
431
  this.setFramesSnapShot(events);
427
432
  }
433
+ /**
434
+ * 多选状态下通过拖拽边框改变大小,所有选中组件会一起改变大小
435
+ */
428
436
  onResizeGroup(e) {
429
437
  const { events } = e;
430
438
  events.forEach((ev) => {
@@ -498,6 +506,9 @@
498
506
  }
499
507
  return { width, height, left, top };
500
508
  }
509
+ /**
510
+ * 多选状态设置多个节点的快照
511
+ */
501
512
  setFramesSnapShot(events) {
502
513
  if (this.framesSnapShot.length > 0)
503
514
  return;
@@ -514,6 +525,9 @@
514
525
  });
515
526
  });
516
527
  }
528
+ /**
529
+ * 流式布局把目标节点复制一份进行拖拽,在拖拽结束前不影响页面原布局样式
530
+ */
517
531
  generateGhostEl(el) {
518
532
  if (this.ghostEl) {
519
533
  this.destroyGhostEl();
@@ -627,7 +641,7 @@
627
641
  "button",
628
642
  {
629
643
  className: "moveable-button",
630
- title: "\u9009\u4E2D\u7236\u7EC4\u4EF6",
644
+ title: "选中父组件",
631
645
  onClick: () => {
632
646
  handler(AbleActionEventType.SELECT_PARENT);
633
647
  }
@@ -656,7 +670,7 @@
656
670
  ),
657
671
  React.createElement("button", {
658
672
  className: "moveable-button moveable-remove-button",
659
- title: "\u5220\u9664",
673
+ title: "删除",
660
674
  onClick: () => {
661
675
  handler(AbleActionEventType.REMOVE);
662
676
  }
@@ -666,13 +680,20 @@
666
680
  }
667
681
  });
668
682
 
669
- class MoveableOptionsManager extends EventEmitter__default.default {
683
+ class MoveableOptionsManager extends EventEmitter {
684
+ /** 布局方式:流式布局、绝对定位、固定定位 */
670
685
  mode = Mode.ABSOLUTE;
686
+ /** 画布容器 */
671
687
  container;
688
+ /** 水平参考线 */
672
689
  horizontalGuidelines = [];
690
+ /** 垂直参考线 */
673
691
  verticalGuidelines = [];
692
+ /** 对齐元素集合 */
674
693
  elementGuidelines = [];
694
+ /** 由外部调用方(编辑器)传入进来的moveable默认参数,可以为空,也可以是一个回调函数 */
675
695
  customizedOptions;
696
+ /** 获取整个画布的根元素(在StageCore的mount函数中挂载的container) */
676
697
  getRootContainer;
677
698
  constructor(config) {
678
699
  super();
@@ -680,6 +701,11 @@
680
701
  this.container = config.container;
681
702
  this.getRootContainer = config.getRootContainer;
682
703
  }
704
+ /**
705
+ * 设置水平/垂直参考线
706
+ * @param type 参考线类型
707
+ * @param guidelines 参考线坐标数组
708
+ */
683
709
  setGuidelines(type, guidelines) {
684
710
  if (type === GuidesType.HORIZONTAL) {
685
711
  this.horizontalGuidelines = guidelines;
@@ -688,11 +714,19 @@
688
714
  }
689
715
  this.emit("update-moveable");
690
716
  }
717
+ /**
718
+ * 清除横向和纵向的参考线
719
+ */
691
720
  clearGuides() {
692
721
  this.horizontalGuidelines = [];
693
722
  this.verticalGuidelines = [];
694
723
  this.emit("update-moveable");
695
724
  }
725
+ /**
726
+ * 设置有哪些元素要辅助对齐
727
+ * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
728
+ * @param allElList 全部元素列表
729
+ */
696
730
  setElementGuidelines(selectedElList, allElList) {
697
731
  this.elementGuidelines.forEach((node) => {
698
732
  node.remove();
@@ -702,11 +736,21 @@
702
736
  this.container.append(this.createGuidelineElements(selectedElList, allElList));
703
737
  }
704
738
  }
739
+ /**
740
+ * 获取moveable参数
741
+ * @param isMultiSelect 是否多选模式
742
+ * @param runtimeOptions 调用时实时传进来的的moveable参数
743
+ * @returns moveable所需参数
744
+ */
705
745
  getOptions(isMultiSelect, runtimeOptions = {}) {
706
746
  const defaultOptions = this.getDefaultOptions(isMultiSelect);
707
747
  const customizedOptions = this.getCustomizeOptions();
708
748
  return lodashEs.merge(defaultOptions, customizedOptions, runtimeOptions);
709
749
  }
750
+ /**
751
+ * 获取单选和多选的moveable公共参数
752
+ * @returns moveable公共参数
753
+ */
710
754
  getDefaultOptions(isMultiSelect) {
711
755
  const isSortable = this.mode === Mode.SORTABLE;
712
756
  const commonOptions = {
@@ -721,6 +765,7 @@
721
765
  elementGuidelines: this.elementGuidelines,
722
766
  bounds: {
723
767
  top: 0,
768
+ // 设置0的话无法移动到left为0,所以只能设置为-1
724
769
  left: -1,
725
770
  right: this.container.clientWidth - 1,
726
771
  bottom: isSortable ? void 0 : this.container.clientHeight
@@ -729,6 +774,10 @@
729
774
  const differenceOptions = isMultiSelect ? this.getMultiOptions() : this.getSingleOptions();
730
775
  return lodashEs.merge(commonOptions, differenceOptions);
731
776
  }
777
+ /**
778
+ * 获取单选下的差异化参数
779
+ * @returns {MoveableOptions} moveable options参数
780
+ */
732
781
  getSingleOptions() {
733
782
  const isAbsolute = this.mode === Mode.ABSOLUTE;
734
783
  const isFixed = this.mode === Mode.FIXED;
@@ -762,6 +811,10 @@
762
811
  ables: [MoveableActionsAble(this.actionHandler.bind(this))]
763
812
  };
764
813
  }
814
+ /**
815
+ * 获取多选下的差异化参数
816
+ * @returns {MoveableOptions} moveable options参数
817
+ */
765
818
  getMultiOptions() {
766
819
  return {
767
820
  defaultGroupRotate: 0,
@@ -772,15 +825,27 @@
772
825
  padding: { left: 0, top: 0, right: 0, bottom: 0 }
773
826
  };
774
827
  }
828
+ /**
829
+ * 获取业务方自定义的moveable参数
830
+ */
775
831
  getCustomizeOptions() {
776
832
  if (typeof this.customizedOptions === "function") {
777
833
  return this.customizedOptions();
778
834
  }
779
835
  return this.customizedOptions;
780
836
  }
837
+ /**
838
+ * 这是给selectParentAbles的回调函数,用于触发选中父元素事件
839
+ */
781
840
  actionHandler(type) {
782
841
  this.emit(type);
783
842
  }
843
+ /**
844
+ * 为需要辅助对齐的元素创建div
845
+ * @param selectedElList 选中的元素列表,需要排除在对齐元素之外
846
+ * @param allElList 全部元素列表
847
+ * @returns frame 辅助对齐元素集合的页面片
848
+ */
784
849
  createGuidelineElements(selectedElList, allElList) {
785
850
  const frame = globalThis.document.createDocumentFragment();
786
851
  for (const node of allElList) {
@@ -795,6 +860,12 @@
795
860
  }
796
861
  return frame;
797
862
  }
863
+ /**
864
+ * 判断一个元素是否在元素列表里面
865
+ * @param ele 元素
866
+ * @param eleList 元素列表
867
+ * @returns 是否在元素列表里面
868
+ */
798
869
  isInElementList(ele, eleList) {
799
870
  for (const eleItem of eleList) {
800
871
  if (ele === eleItem)
@@ -805,8 +876,11 @@
805
876
  }
806
877
 
807
878
  class StageDragResize extends MoveableOptionsManager {
879
+ /** 目标节点 */
808
880
  target;
881
+ /** Moveable拖拽类实例 */
809
882
  moveable;
883
+ /** 拖动状态 */
810
884
  dragStatus = StageDragStatus.END;
811
885
  dragResizeHelper;
812
886
  disabledDragStart;
@@ -829,6 +903,12 @@
829
903
  getTarget() {
830
904
  return this.target;
831
905
  }
906
+ /**
907
+ * 将选中框渲染并覆盖到选中的组件Dom节点上方
908
+ * 当选中的节点不是absolute时,会创建一个新的节点出来作为拖拽目标
909
+ * @param el 选中组件的Dom节点元素
910
+ * @param event 鼠标事件
911
+ */
832
912
  select(el, event) {
833
913
  if (!this.moveable || el !== this.target) {
834
914
  this.initMoveable(el);
@@ -839,11 +919,14 @@
839
919
  this.moveable?.dragStart(event);
840
920
  }
841
921
  }
922
+ /**
923
+ * 初始化选中框并渲染出来
924
+ */
842
925
  updateMoveable(el = this.target) {
843
926
  if (!this.moveable)
844
927
  return;
845
928
  if (!el)
846
- throw new Error("\u672A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
929
+ throw new Error("未选中任何节点");
847
930
  const options = this.init(el);
848
931
  Object.entries(options).forEach(([key, value]) => {
849
932
  this.moveable[key] = value;
@@ -857,6 +940,9 @@
857
940
  this.moveable.target = null;
858
941
  this.moveable.updateTarget();
859
942
  }
943
+ /**
944
+ * 销毁实例
945
+ */
860
946
  destroy() {
861
947
  this.moveable?.destroy();
862
948
  this.dragResizeHelper.destroy();
@@ -881,7 +967,7 @@
881
967
  const options = this.init(el);
882
968
  this.dragResizeHelper.clear();
883
969
  this.moveable?.destroy();
884
- this.moveable = new Moveable__default.default(this.container, {
970
+ this.moveable = new Moveable(this.container, {
885
971
  ...options
886
972
  });
887
973
  this.bindResizeEvent();
@@ -891,7 +977,7 @@
891
977
  }
892
978
  bindResizeEvent() {
893
979
  if (!this.moveable)
894
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
980
+ throw new Error("moveable 未初始化");
895
981
  this.moveable.on("resizeStart", (e) => {
896
982
  if (!this.target)
897
983
  return;
@@ -909,11 +995,11 @@
909
995
  }
910
996
  bindDragEvent() {
911
997
  if (!this.moveable)
912
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
998
+ throw new Error("moveable 未初始化");
913
999
  let timeout;
914
1000
  this.moveable.on("dragStart", (e) => {
915
1001
  if (!this.target)
916
- throw new Error("\u672A\u9009\u4E2D\u7EC4\u4EF6");
1002
+ throw new Error("未选中组件");
917
1003
  this.dragStatus = StageDragStatus.START;
918
1004
  this.dragResizeHelper.onDragStart(e);
919
1005
  }).on("drag", (e) => {
@@ -951,7 +1037,7 @@
951
1037
  }
952
1038
  bindRotateEvent() {
953
1039
  if (!this.moveable)
954
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
1040
+ throw new Error("moveable 未初始化");
955
1041
  this.moveable.on("rotateStart", (e) => {
956
1042
  this.dragStatus = StageDragStatus.START;
957
1043
  this.dragResizeHelper.onRotateStart(e);
@@ -977,7 +1063,7 @@
977
1063
  }
978
1064
  bindScaleEvent() {
979
1065
  if (!this.moveable)
980
- throw new Error("moveable \u672A\u521D\u59CB\u5316");
1066
+ throw new Error("moveable 未初始化");
981
1067
  this.moveable.on("scaleStart", (e) => {
982
1068
  this.dragStatus = StageDragStatus.START;
983
1069
  this.dragResizeHelper.onScaleStart(e);
@@ -1003,7 +1089,7 @@
1003
1089
  }
1004
1090
  sort() {
1005
1091
  if (!this.target || !this.dragResizeHelper.getGhostEl())
1006
- throw new Error("\u672A\u77E5\u9519\u8BEF");
1092
+ throw new Error("未知错误");
1007
1093
  const { top } = this.dragResizeHelper.getGhostEl().getBoundingClientRect();
1008
1094
  const { top: oriTop } = this.target.getBoundingClientRect();
1009
1095
  const deltaTop = top - oriTop;
@@ -1056,18 +1142,25 @@
1056
1142
  idPrefix: HIGHLIGHT_EL_ID_PREFIX
1057
1143
  });
1058
1144
  }
1145
+ /**
1146
+ * 高亮鼠标悬停的组件
1147
+ * @param el 选中组件的Dom节点元素
1148
+ */
1059
1149
  highlight(el) {
1060
1150
  if (!el || el === this.target)
1061
1151
  return;
1062
1152
  this.target = el;
1063
1153
  this.moveable?.destroy();
1064
- this.moveable = new Moveable__default.default(this.container, {
1154
+ this.moveable = new Moveable(this.container, {
1065
1155
  target: this.targetShadow.update(el),
1066
1156
  origin: false,
1067
1157
  rootContainer: this.getRootContainer(),
1068
1158
  zoom: 2
1069
1159
  });
1070
1160
  }
1161
+ /**
1162
+ * 清空高亮
1163
+ */
1071
1164
  clearHighlight() {
1072
1165
  if (!this.moveable || !this.target)
1073
1166
  return;
@@ -1075,6 +1168,9 @@
1075
1168
  this.moveable.target = null;
1076
1169
  this.moveable.updateTarget();
1077
1170
  }
1171
+ /**
1172
+ * 销毁实例
1173
+ */
1078
1174
  destroy() {
1079
1175
  this.moveable?.destroy();
1080
1176
  this.targetShadow.destroy();
@@ -1082,8 +1178,11 @@
1082
1178
  }
1083
1179
 
1084
1180
  class StageMultiDragResize extends MoveableOptionsManager {
1181
+ /** 画布容器 */
1085
1182
  container;
1183
+ /** 多选:目标节点组 */
1086
1184
  targetList = [];
1185
+ /** Moveable多选拖拽类实例 */
1087
1186
  moveableForMulti;
1088
1187
  dragStatus = StageDragStatus.END;
1089
1188
  dragResizeHelper;
@@ -1108,6 +1207,10 @@
1108
1207
  }
1109
1208
  });
1110
1209
  }
1210
+ /**
1211
+ * 多选
1212
+ * @param els
1213
+ */
1111
1214
  multiSelect(els) {
1112
1215
  if (els.length === 0) {
1113
1216
  return;
@@ -1119,7 +1222,7 @@
1119
1222
  this.setElementGuidelines(this.targetList, elementGuidelines);
1120
1223
  this.moveableForMulti?.destroy();
1121
1224
  this.dragResizeHelper.clear();
1122
- this.moveableForMulti = new Moveable__default.default(
1225
+ this.moveableForMulti = new Moveable(
1123
1226
  this.container,
1124
1227
  this.getOptions(true, {
1125
1228
  target: this.dragResizeHelper.getShadowEls()
@@ -1177,7 +1280,7 @@
1177
1280
  if (!this.moveableForMulti)
1178
1281
  return;
1179
1282
  if (!eleList)
1180
- throw new Error("\u672A\u9009\u4E2D\u4EFB\u4F55\u8282\u70B9");
1283
+ throw new Error("未选中任何节点");
1181
1284
  this.targetList = eleList;
1182
1285
  this.dragResizeHelper.setTargetList(eleList);
1183
1286
  const options = this.getOptions(true, {
@@ -1188,6 +1291,9 @@
1188
1291
  });
1189
1292
  this.moveableForMulti.updateTarget();
1190
1293
  }
1294
+ /**
1295
+ * 清除多选状态
1296
+ */
1191
1297
  clearSelectStatus() {
1192
1298
  if (!this.moveableForMulti)
1193
1299
  return;
@@ -1196,10 +1302,17 @@
1196
1302
  this.moveableForMulti.updateTarget();
1197
1303
  this.targetList = [];
1198
1304
  }
1305
+ /**
1306
+ * 销毁实例
1307
+ */
1199
1308
  destroy() {
1200
1309
  this.moveableForMulti?.destroy();
1201
1310
  this.dragResizeHelper.destroy();
1202
1311
  }
1312
+ /**
1313
+ * 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
1314
+ * @param isResize 是否进行大小缩放
1315
+ */
1203
1316
  update(isResize = false, parentEl = null) {
1204
1317
  if (this.targetList.length === 0)
1205
1318
  return;
@@ -1219,17 +1332,25 @@
1219
1332
 
1220
1333
  const throttleTime = 100;
1221
1334
  const defaultContainerHighlightDuration = 800;
1222
- class ActionManager extends EventEmitter__default.default {
1335
+ class ActionManager extends EventEmitter {
1223
1336
  dr;
1224
1337
  multiDr;
1225
1338
  highlightLayer;
1339
+ /** 单选、多选、高亮的容器(蒙层的content) */
1226
1340
  container;
1341
+ /** 当前选中的节点 */
1227
1342
  selectedEl;
1343
+ /** 多选选中的节点组 */
1228
1344
  selectedElList = [];
1345
+ /** 当前高亮的节点 */
1229
1346
  highlightedEl;
1347
+ /** 当前是否处于多选状态 */
1230
1348
  isMultiSelectStatus = false;
1349
+ /** 当拖拽组件到容器上方进入可加入容器状态时,给容器添加的一个class名称 */
1231
1350
  containerHighlightClassName;
1351
+ /** 当拖拽组件到容器上方时,需要悬停多久才能将组件加入容器 */
1232
1352
  containerHighlightDuration;
1353
+ /** 将组件加入容器的操作方式 */
1233
1354
  containerHighlightType;
1234
1355
  isAltKeydown = false;
1235
1356
  getTargetElement;
@@ -1288,18 +1409,33 @@
1288
1409
  this.initKeyEvent();
1289
1410
  this.initActionEvent();
1290
1411
  }
1412
+ /**
1413
+ * 设置水平/垂直参考线
1414
+ * @param type 参考线类型
1415
+ * @param guidelines 参考线坐标数组
1416
+ */
1291
1417
  setGuidelines(type, guidelines) {
1292
1418
  this.dr.setGuidelines(type, guidelines);
1293
1419
  this.multiDr.setGuidelines(type, guidelines);
1294
1420
  }
1421
+ /**
1422
+ * 清空所有参考线
1423
+ */
1295
1424
  clearGuides() {
1296
1425
  this.dr.clearGuides();
1297
1426
  this.multiDr.clearGuides();
1298
1427
  }
1428
+ /**
1429
+ * 更新moveable,外部主要调用场景是元素配置变更、页面大小变更
1430
+ * @param el 变更的元素
1431
+ */
1299
1432
  updateMoveable(el) {
1300
1433
  this.dr.updateMoveable(el);
1301
1434
  this.multiDr.updateMoveable();
1302
1435
  }
1436
+ /**
1437
+ * 判断是否单选选中的元素
1438
+ */
1303
1439
  isSelectedEl(el) {
1304
1440
  return el.id === this.selectedEl?.id;
1305
1441
  }
@@ -1312,6 +1448,11 @@
1312
1448
  getSelectedElList() {
1313
1449
  return this.selectedElList;
1314
1450
  }
1451
+ /**
1452
+ * 获取鼠标下方第一个可选中元素,如果元素层叠,返回到是最上层元素
1453
+ * @param event 鼠标事件
1454
+ * @returns 鼠标下方第一个可选中元素
1455
+ */
1315
1456
  async getElementFromPoint(event) {
1316
1457
  const els = this.getElementsFromPoint(event);
1317
1458
  let stopped = false;
@@ -1324,6 +1465,13 @@
1324
1465
  }
1325
1466
  }
1326
1467
  }
1468
+ /**
1469
+ * 判断一个元素能否在当前场景被选中
1470
+ * @param el 被判断的元素
1471
+ * @param event 鼠标事件
1472
+ * @param stop 通过该元素如果得知剩下的元素都不可被选中,通知调用方终止对剩下元素的判断
1473
+ * @returns 能否选中
1474
+ */
1327
1475
  async isElCanSelect(el, event, stop) {
1328
1476
  const canSelectByProp = await this.canSelect(el, event, stop);
1329
1477
  if (!canSelectByProp)
@@ -1333,6 +1481,9 @@
1333
1481
  }
1334
1482
  return true;
1335
1483
  }
1484
+ /**
1485
+ * 判断一个元素是否可以被多选,如果当前元素是page,则调stop函数告诉调用方不必继续判断其它元素了
1486
+ */
1336
1487
  canMultiSelect(el, stop) {
1337
1488
  if (el.className.includes(PAGE_CLASS)) {
1338
1489
  stop();
@@ -1382,6 +1533,10 @@
1382
1533
  this.setHighlightEl(void 0);
1383
1534
  this.highlightLayer.clearHighlight();
1384
1535
  }
1536
+ /**
1537
+ * 用于在切换选择模式时清除上一次的状态
1538
+ * @param selectType 需要清理的选择模式
1539
+ */
1385
1540
  clearSelectStatus(selectType) {
1386
1541
  if (selectType === SelectStatus.MULTI_SELECT) {
1387
1542
  this.multiDr.clearSelectStatus();
@@ -1390,6 +1545,11 @@
1390
1545
  this.dr.clearSelectStatus();
1391
1546
  }
1392
1547
  }
1548
+ /**
1549
+ * 找到鼠标下方的容器,通过添加className对容器进行标记
1550
+ * @param event 鼠标事件
1551
+ * @param excludeElList 计算鼠标点所在容器时要排除的元素列表
1552
+ */
1393
1553
  async addContainerHighlightClassName(event, excludeElList) {
1394
1554
  const doc = this.getRenderDocument();
1395
1555
  if (!doc)
@@ -1402,6 +1562,13 @@
1402
1562
  }
1403
1563
  }
1404
1564
  }
1565
+ /**
1566
+ * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
1567
+ * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
1568
+ * @param event 鼠标事件
1569
+ * @param excludeElList 计算鼠标所在容器时要排除的元素列表
1570
+ * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
1571
+ */
1405
1572
  delayedMarkContainer(event, excludeElList = []) {
1406
1573
  if (this.canAddToContainer()) {
1407
1574
  return globalThis.setTimeout(() => {
@@ -1433,6 +1600,11 @@
1433
1600
  }
1434
1601
  return options;
1435
1602
  }
1603
+ /**
1604
+ * 在执行多选逻辑前,先准备好多选选中元素
1605
+ * @param el 新选中的元素
1606
+ * @returns 多选选中的元素列表
1607
+ */
1436
1608
  async beforeMultiSelect(event) {
1437
1609
  const el = await this.getElementFromPoint(event);
1438
1610
  if (!el)
@@ -1448,9 +1620,16 @@
1448
1620
  this.selectedElList.push(el);
1449
1621
  }
1450
1622
  }
1623
+ /**
1624
+ * 当前状态下能否将组件加入容器,默认是鼠标悬停一段时间加入,alt模式则是按住alt+鼠标悬停一段时间加入
1625
+ */
1451
1626
  canAddToContainer() {
1452
1627
  return this.containerHighlightType === ContainerHighlightType.DEFAULT || this.containerHighlightType === ContainerHighlightType.ALT && this.isAltKeydown;
1453
1628
  }
1629
+ /**
1630
+ * 结束对container的标记状态
1631
+ * @returns 标记的容器元素,没有标记的容器时返回null
1632
+ */
1454
1633
  markContainerEnd() {
1455
1634
  const doc = this.getRenderDocument();
1456
1635
  if (doc && this.canAddToContainer()) {
@@ -1464,30 +1643,36 @@
1464
1643
  this.container.addEventListener("mouseleave", this.mouseLeaveHandler);
1465
1644
  this.container.addEventListener("wheel", this.mouseWheelHandler);
1466
1645
  }
1646
+ /**
1647
+ * 初始化键盘事件监听
1648
+ */
1467
1649
  initKeyEvent() {
1468
1650
  const { isMac } = new core.Env();
1469
1651
  const ctrl = isMac ? "meta" : "ctrl";
1470
- KeyController__default.default.global.keydown(ctrl, (e) => {
1652
+ KeyController.global.keydown(ctrl, (e) => {
1471
1653
  e.inputEvent.preventDefault();
1472
1654
  this.isMultiSelectStatus = true;
1473
1655
  });
1474
- KeyController__default.default.global.on("blur", () => {
1656
+ KeyController.global.on("blur", () => {
1475
1657
  this.isMultiSelectStatus = false;
1476
1658
  });
1477
- KeyController__default.default.global.keyup(ctrl, (e) => {
1659
+ KeyController.global.keyup(ctrl, (e) => {
1478
1660
  e.inputEvent.preventDefault();
1479
1661
  this.isMultiSelectStatus = false;
1480
1662
  });
1481
- KeyController__default.default.global.keydown("alt", (e) => {
1663
+ KeyController.global.keydown("alt", (e) => {
1482
1664
  e.inputEvent.preventDefault();
1483
1665
  this.isAltKeydown = true;
1484
1666
  });
1485
- KeyController__default.default.global.keyup("alt", (e) => {
1667
+ KeyController.global.keyup("alt", (e) => {
1486
1668
  e.inputEvent.preventDefault();
1487
1669
  this.markContainerEnd();
1488
1670
  this.isAltKeydown = false;
1489
1671
  });
1490
1672
  }
1673
+ /**
1674
+ * 处理单选、多选抛出来的事件
1675
+ */
1491
1676
  initActionEvent() {
1492
1677
  this.dr.on("update", (data) => {
1493
1678
  setTimeout(() => this.emit("update", data));
@@ -1513,6 +1698,9 @@
1513
1698
  this.emit("change-to-select", el);
1514
1699
  });
1515
1700
  }
1701
+ /**
1702
+ * 在down事件中集中cpu处理画布中选中操作渲染,在up事件中再通知外面的编辑器更新
1703
+ */
1516
1704
  mouseDownHandler = async (event) => {
1517
1705
  this.clearHighlight();
1518
1706
  event.stopImmediatePropagation();
@@ -1547,6 +1735,9 @@
1547
1735
  }
1548
1736
  return false;
1549
1737
  }
1738
+ /**
1739
+ * 在up事件中负责对外通知选中事件,通知画布之外的编辑器更新
1740
+ */
1550
1741
  mouseUpHandler = () => {
1551
1742
  utils.getDocument().removeEventListener("mouseup", this.mouseUpHandler);
1552
1743
  this.container.addEventListener("mousemove", this.mouseMoveHandler);
@@ -1564,7 +1755,7 @@
1564
1755
  };
1565
1756
  }
1566
1757
 
1567
- class Rule extends EventEmitter__default.default {
1758
+ class Rule extends EventEmitter {
1568
1759
  hGuides;
1569
1760
  vGuides;
1570
1761
  horizontalGuidelines = [];
@@ -1585,6 +1776,10 @@
1585
1776
  });
1586
1777
  this.containerResizeObserver.observe(this.container);
1587
1778
  }
1779
+ /**
1780
+ * 是否显示辅助线
1781
+ * @param isShowGuides 是否显示
1782
+ */
1588
1783
  showGuides(isShowGuides = true) {
1589
1784
  this.isShowGuides = isShowGuides;
1590
1785
  this.hGuides.setState({
@@ -1612,9 +1807,16 @@
1612
1807
  guides: vLines
1613
1808
  });
1614
1809
  }
1810
+ /**
1811
+ * 清空所有参考线
1812
+ */
1615
1813
  clearGuides() {
1616
1814
  this.setGuides([[], []]);
1617
1815
  }
1816
+ /**
1817
+ * 是否显示标尺
1818
+ * @param show 是否显示
1819
+ */
1618
1820
  showRule(show = true) {
1619
1821
  if (show) {
1620
1822
  this.hGuides.destroy();
@@ -1654,7 +1856,7 @@
1654
1856
  width: type === GuidesType.HORIZONTAL ? "100%" : "30px",
1655
1857
  height: type === GuidesType.HORIZONTAL ? "30px" : "100%"
1656
1858
  });
1657
- createGuides = (type, defaultGuides = []) => new Guides__default.default(this.container, {
1859
+ createGuides = (type, defaultGuides = []) => new Guides(this.container, {
1658
1860
  type,
1659
1861
  defaultGuides,
1660
1862
  displayDragPos: true,
@@ -1744,6 +1946,11 @@
1744
1946
  this.content.style.height = `${this.height}px`;
1745
1947
  }
1746
1948
  }
1949
+ /**
1950
+ * 初始化视窗和蒙层监听,监听元素是否在视窗区域、监听mask蒙层所在的wrapper大小变化
1951
+ * @description 初始化视窗和蒙层监听
1952
+ * @param page 页面Dom节点
1953
+ */
1747
1954
  observe(page) {
1748
1955
  if (!page)
1749
1956
  return;
@@ -1751,6 +1958,10 @@
1751
1958
  this.initObserverIntersection();
1752
1959
  this.initObserverWrapper();
1753
1960
  }
1961
+ /**
1962
+ * 处理页面大小变更,同步页面和mask大小
1963
+ * @param entries ResizeObserverEntry,获取页面最新大小
1964
+ */
1754
1965
  pageResize(entries) {
1755
1966
  const [entry] = entries;
1756
1967
  const { clientHeight, clientWidth } = entry.target;
@@ -1758,12 +1969,20 @@
1758
1969
  this.setWidth(clientWidth);
1759
1970
  this.scroll();
1760
1971
  }
1972
+ /**
1973
+ * 监听一个组件是否在画布可视区域内
1974
+ * @param el 被选中的组件,可能是左侧目录树中选中的
1975
+ */
1761
1976
  observerIntersection(el) {
1762
1977
  this.intersectionObserver?.observe(el);
1763
1978
  }
1979
+ /**
1980
+ * 挂载Dom节点
1981
+ * @param el 将蒙层挂载到该Dom节点上
1982
+ */
1764
1983
  mount(el) {
1765
1984
  if (!this.content)
1766
- throw new Error("content \u4E0D\u5B58\u5728");
1985
+ throw new Error("content 不存在");
1767
1986
  el.appendChild(this.wrapper);
1768
1987
  }
1769
1988
  setLayout(el) {
@@ -1777,6 +1996,9 @@
1777
1996
  this.scrollTop = this.pageScrollParent.scrollTop;
1778
1997
  this.scroll();
1779
1998
  }
1999
+ /**
2000
+ * 销毁实例
2001
+ */
1780
2002
  destroy() {
1781
2003
  this.content?.remove();
1782
2004
  this.page = null;
@@ -1784,6 +2006,9 @@
1784
2006
  this.wrapperResizeObserver?.disconnect();
1785
2007
  super.destroy();
1786
2008
  }
2009
+ /**
2010
+ * 监听选中元素是否在画布可视区域内,如果目标元素不在可视区域内,通过滚动使该元素出现在可视区域
2011
+ */
1787
2012
  initObserverIntersection() {
1788
2013
  this.pageScrollParent = getScrollParent(this.page) || null;
1789
2014
  this.intersectionObserver?.disconnect();
@@ -1806,6 +2031,9 @@
1806
2031
  );
1807
2032
  }
1808
2033
  }
2034
+ /**
2035
+ * 监听mask的容器大小变化
2036
+ */
1809
2037
  initObserverWrapper() {
1810
2038
  this.wrapperResizeObserver?.disconnect();
1811
2039
  if (typeof ResizeObserver !== "undefined") {
@@ -1846,22 +2074,40 @@
1846
2074
  });
1847
2075
  this.content.dispatchEvent(event);
1848
2076
  }
2077
+ /**
2078
+ * 设置蒙层高度
2079
+ * @param height 高度
2080
+ */
1849
2081
  setHeight(height) {
1850
2082
  this.height = height;
1851
2083
  this.setMaxScrollTop();
1852
2084
  this.content.style.height = `${height}px`;
1853
2085
  }
2086
+ /**
2087
+ * 设置蒙层宽度
2088
+ * @param width 宽度
2089
+ */
1854
2090
  setWidth(width) {
1855
2091
  this.width = width;
1856
2092
  this.setMaxScrollLeft();
1857
2093
  this.content.style.width = `${width}px`;
1858
2094
  }
2095
+ /**
2096
+ * 计算并设置最大滚动宽度
2097
+ */
1859
2098
  setMaxScrollLeft() {
1860
2099
  this.maxScrollLeft = Math.max(this.width - this.wrapperWidth, 0);
1861
2100
  }
2101
+ /**
2102
+ * 计算并设置最大滚动高度
2103
+ */
1862
2104
  setMaxScrollTop() {
1863
2105
  this.maxScrollTop = Math.max(this.height - this.wrapperHeight, 0);
1864
2106
  }
2107
+ /**
2108
+ * 修复滚动距离
2109
+ * 由于滚动容器变化等因素,会导致当前滚动的距离不正确
2110
+ */
1865
2111
  fixScrollValue() {
1866
2112
  if (this.scrollTop < 0)
1867
2113
  this.scrollTop = 0;
@@ -1874,7 +2120,7 @@
1874
2120
  }
1875
2121
  mouseWheelHandler = (event) => {
1876
2122
  if (!this.page)
1877
- throw new Error("page \u672A\u521D\u59CB\u5316");
2123
+ throw new Error("page 未初始化");
1878
2124
  const { deltaY, deltaX } = event;
1879
2125
  if (this.page.clientHeight < this.wrapperHeight && deltaY)
1880
2126
  return;
@@ -1894,6 +2140,7 @@
1894
2140
  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";
1895
2141
 
1896
2142
  class StageRender extends EventEmitter.EventEmitter {
2143
+ /** 组件的js、css执行的环境,直接渲染为当前window,iframe渲染则为iframe.contentWindow */
1897
2144
  contentWindow = null;
1898
2145
  runtime = null;
1899
2146
  iframe;
@@ -1947,9 +2194,13 @@
1947
2194
  setZoom(zoom = DEFAULT_ZOOM) {
1948
2195
  this.zoom = zoom;
1949
2196
  }
2197
+ /**
2198
+ * 挂载Dom节点
2199
+ * @param el 将页面挂载到该Dom节点上
2200
+ */
1950
2201
  async mount(el) {
1951
2202
  if (!this.iframe) {
1952
- throw Error("mount \u5931\u8D25");
2203
+ throw Error("mount 失败");
1953
2204
  }
1954
2205
  if (!utils.isSameDomain(this.runtimeUrl) && this.runtimeUrl) {
1955
2206
  let html = await fetch(this.runtimeUrl).then((res) => res.text());
@@ -1975,6 +2226,11 @@
1975
2226
  getDocument() {
1976
2227
  return this.contentWindow?.document;
1977
2228
  }
2229
+ /**
2230
+ * 通过坐标获得坐标下所有HTML元素数组
2231
+ * @param point 坐标
2232
+ * @returns 坐标下方所有HTML元素数组,会包含父元素直至html,元素层叠时返回顺序是从上到下
2233
+ */
1978
2234
  getElementsFromPoint(point) {
1979
2235
  let x = point.clientX;
1980
2236
  let y = point.clientY;
@@ -1991,11 +2247,14 @@
1991
2247
  if (typeof idOrEl === "string" || typeof idOrEl === "number") {
1992
2248
  const el = this.getDocument()?.getElementById(`${idOrEl}`);
1993
2249
  if (!el)
1994
- throw new Error(`\u4E0D\u5B58\u5728ID\u4E3A${idOrEl}\u7684\u5143\u7D20`);
2250
+ throw new Error(`不存在ID为${idOrEl}的元素`);
1995
2251
  return el;
1996
2252
  }
1997
2253
  return idOrEl;
1998
2254
  }
2255
+ /**
2256
+ * 销毁实例
2257
+ */
1999
2258
  destroy() {
2000
2259
  this.iframe?.removeEventListener("load", this.loadHandler);
2001
2260
  this.contentWindow = null;
@@ -2003,6 +2262,10 @@
2003
2262
  this.iframe = void 0;
2004
2263
  this.removeAllListeners();
2005
2264
  }
2265
+ /**
2266
+ * 在runtime中对被选中的元素进行标记,部分组件有对选中态进行特殊显示的需求
2267
+ * @param el 被选中的元素
2268
+ */
2006
2269
  flagSelectedEl(el) {
2007
2270
  const doc = this.getDocument();
2008
2271
  if (doc) {
@@ -2065,6 +2328,10 @@
2065
2328
  this.initActionEvent();
2066
2329
  this.initMaskEvent();
2067
2330
  }
2331
+ /**
2332
+ * 单选选中元素
2333
+ * @param idOrEl 选中的id或者元素
2334
+ */
2068
2335
  async select(idOrEl, event) {
2069
2336
  const el = this.renderer.getTargetElement(idOrEl);
2070
2337
  if (el === this.actionManager.getSelectedEl())
@@ -2076,6 +2343,10 @@
2076
2343
  this.mask.observerIntersection(el);
2077
2344
  }
2078
2345
  }
2346
+ /**
2347
+ * 多选选中多个元素
2348
+ * @param idOrElList 选中元素的id或元素列表
2349
+ */
2079
2350
  async multiSelect(idOrElList) {
2080
2351
  const els = idOrElList.map((idOrEl) => this.renderer.getTargetElement(idOrEl));
2081
2352
  if (els.length === 0)
@@ -2089,9 +2360,17 @@
2089
2360
  this.mask.observerIntersection(lastEl);
2090
2361
  }
2091
2362
  }
2363
+ /**
2364
+ * 高亮选中元素
2365
+ * @param el 要高亮的元素
2366
+ */
2092
2367
  highlight(idOrEl) {
2093
2368
  this.actionManager.highlight(idOrEl);
2094
2369
  }
2370
+ /**
2371
+ * 更新组件
2372
+ * @param data 更新组件的数据
2373
+ */
2095
2374
  async update(data) {
2096
2375
  const { config } = data;
2097
2376
  await this.renderer.update(data);
@@ -2104,15 +2383,27 @@
2104
2383
  }
2105
2384
  });
2106
2385
  }
2386
+ /**
2387
+ * 往画布增加一个组件
2388
+ * @param data 组件信息数据
2389
+ */
2107
2390
  async add(data) {
2108
2391
  return await this.renderer.add(data);
2109
2392
  }
2393
+ /**
2394
+ * 从画布删除一个组件
2395
+ * @param data 组件信息数据
2396
+ */
2110
2397
  async remove(data) {
2111
2398
  return await this.renderer.remove(data);
2112
2399
  }
2113
2400
  setZoom(zoom = DEFAULT_ZOOM) {
2114
2401
  this.renderer.setZoom(zoom);
2115
2402
  }
2403
+ /**
2404
+ * 挂载Dom节点
2405
+ * @param el 将stage挂载到该Dom节点上
2406
+ */
2116
2407
  async mount(el) {
2117
2408
  this.container = el;
2118
2409
  const { mask, renderer } = this;
@@ -2120,16 +2411,32 @@
2120
2411
  mask.mount(el);
2121
2412
  this.emit("mounted");
2122
2413
  }
2414
+ /**
2415
+ * 清空所有参考线
2416
+ */
2123
2417
  clearGuides() {
2124
2418
  this.mask.clearGuides();
2125
2419
  this.actionManager.clearGuides();
2126
2420
  }
2421
+ /**
2422
+ * @deprecated 废弃接口,建议用delayedMarkContainer代替
2423
+ */
2127
2424
  getAddContainerHighlightClassNameTimeout(event, excludeElList = []) {
2128
2425
  return this.delayedMarkContainer(event, excludeElList);
2129
2426
  }
2427
+ /**
2428
+ * 鼠标拖拽着元素,在容器上方悬停,延迟一段时间后,对容器进行标记,如果悬停时间够长将标记成功,悬停时间短,调用方通过返回的timeoutId取消标记
2429
+ * 标记的作用:1、高亮容器,给用户一个加入容器的交互感知;2、释放鼠标后,通过标记的标志找到要加入的容器
2430
+ * @param event 鼠标事件
2431
+ * @param excludeElList 计算鼠标所在容器时要排除的元素列表
2432
+ * @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
2433
+ */
2130
2434
  delayedMarkContainer(event, excludeElList = []) {
2131
2435
  return this.actionManager.delayedMarkContainer(event, excludeElList);
2132
2436
  }
2437
+ /**
2438
+ * 销毁实例
2439
+ */
2133
2440
  destroy() {
2134
2441
  const { mask, renderer, actionManager, pageResizeObserver } = this;
2135
2442
  renderer.destroy();
@@ -2139,6 +2446,9 @@
2139
2446
  this.removeAllListeners();
2140
2447
  this.container = void 0;
2141
2448
  }
2449
+ /**
2450
+ * 监听页面大小变化
2451
+ */
2142
2452
  observePageResize(page) {
2143
2453
  if (typeof ResizeObserver !== "undefined") {
2144
2454
  this.pageResizeObserver = new ResizeObserver((entries) => {
@@ -2182,12 +2492,18 @@
2182
2492
  this.emit("change-guides", data);
2183
2493
  });
2184
2494
  }
2495
+ /**
2496
+ * 初始化操作相关事件监听
2497
+ */
2185
2498
  initActionEvent() {
2186
2499
  this.initActionManagerEvent();
2187
2500
  this.initDrEvent();
2188
2501
  this.initMulDrEvent();
2189
2502
  this.initHighlightEvent();
2190
2503
  }
2504
+ /**
2505
+ * 初始化ActionManager类本身抛出来的事件监听
2506
+ */
2191
2507
  initActionManagerEvent() {
2192
2508
  this.actionManager.on("before-select", (idOrEl, event) => {
2193
2509
  this.select(idOrEl, event);
@@ -2199,6 +2515,9 @@
2199
2515
  this.emit("multi-select", selectedElList);
2200
2516
  });
2201
2517
  }
2518
+ /**
2519
+ * 初始化DragResize类通过ActionManager抛出来的事件监听
2520
+ */
2202
2521
  initDrEvent() {
2203
2522
  this.actionManager.on("update", (data) => {
2204
2523
  this.emit("update", data);
@@ -2210,6 +2529,9 @@
2210
2529
  this.emit("remove", data);
2211
2530
  });
2212
2531
  }
2532
+ /**
2533
+ * 初始化MultiDragResize类通过ActionManager抛出来的事件监听
2534
+ */
2213
2535
  initMulDrEvent() {
2214
2536
  this.actionManager.on("change-to-select", (el) => {
2215
2537
  this.select(el);
@@ -2218,6 +2540,9 @@
2218
2540
  this.emit("update", data);
2219
2541
  });
2220
2542
  }
2543
+ /**
2544
+ * 初始化Highlight类通过ActionManager抛出来的事件监听
2545
+ */
2221
2546
  initHighlightEvent() {
2222
2547
  this.actionManager.on("highlight", async (highlightEl) => {
2223
2548
  this.emit("highlight", highlightEl);