@tmagic/stage 1.1.0-beta.2 → 1.1.0-beta.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.
@@ -1,7 +1,8 @@
1
1
  import EventEmitter$1, { EventEmitter } from 'events';
2
+ import { removeClassName, removeClassNameByClassName, createDiv, injectStyle, isSameDomain, getHost, addClassName } from '@tmagic/utils';
2
3
  import Moveable from 'moveable';
3
4
  import MoveableHelper from 'moveable-helper';
4
- import { removeClassName, addClassName, removeClassNameByClassName, createDiv, injectStyle, isSameDomain, getHost } from '@tmagic/utils';
5
+ import KeyController from 'keycon';
5
6
  import { throttle } from 'lodash-es';
6
7
  import Guides from '@scena/guides';
7
8
 
@@ -9,6 +10,7 @@ const GHOST_EL_ID_PREFIX = "ghost_el_";
9
10
  const DRAG_EL_ID_PREFIX = "drag_el_";
10
11
  const HIGHLIGHT_EL_ID_PREFIX = "highlight_el_";
11
12
  const CONTAINER_HIGHLIGHT_CLASS = "tmagic-stage-container-highlight";
13
+ const PAGE_CLASS = "magic-ui-page";
12
14
  const DEFAULT_ZOOM = 1;
13
15
  var GuidesType = /* @__PURE__ */ ((GuidesType2) => {
14
16
  GuidesType2["HORIZONTAL"] = "horizontal";
@@ -61,6 +63,19 @@ const getOffset = (el) => {
61
63
  top
62
64
  };
63
65
  };
66
+ const getTargetElStyle = (el) => {
67
+ const offset = getOffset(el);
68
+ const { transform } = getComputedStyle(el);
69
+ return `
70
+ position: absolute;
71
+ transform: ${transform};
72
+ left: ${offset.left}px;
73
+ top: ${offset.top}px;
74
+ width: ${el.clientWidth}px;
75
+ height: ${el.clientHeight}px;
76
+ z-index: ${ZIndex.DRAG_EL};
77
+ `;
78
+ };
64
79
  const getAbsolutePosition = (el, { top, left }) => {
65
80
  const { offsetParent } = el;
66
81
  if (offsetParent) {
@@ -153,8 +168,7 @@ class StageDragResize extends EventEmitter {
153
168
  this.dragStatus = "end" /* END */;
154
169
  this.core = config.core;
155
170
  this.container = config.container;
156
- this.dragEl = globalThis.document.createElement("div");
157
- this.container.append(this.dragEl);
171
+ this.mask = config.mask;
158
172
  }
159
173
  select(el, event) {
160
174
  const oldTarget = this.target;
@@ -205,6 +219,16 @@ class StageDragResize extends EventEmitter {
205
219
  this.moveableOptions.verticalGuidelines = [];
206
220
  this.updateMoveable();
207
221
  }
222
+ clearSelectStatus() {
223
+ if (!this.moveable)
224
+ return;
225
+ this.destroyDragEl();
226
+ this.moveable.target = null;
227
+ this.moveable.updateTarget();
228
+ }
229
+ destroyDragEl() {
230
+ this.dragEl?.remove();
231
+ }
208
232
  destroy() {
209
233
  this.moveable?.destroy();
210
234
  this.destroyGhostEl();
@@ -218,7 +242,14 @@ class StageDragResize extends EventEmitter {
218
242
  }
219
243
  this.mode = getMode(el);
220
244
  this.destroyGhostEl();
221
- this.updateDragEl(el);
245
+ this.destroyDragEl();
246
+ this.dragEl = globalThis.document.createElement("div");
247
+ this.container.append(this.dragEl);
248
+ this.dragEl.style.cssText = getTargetElStyle(el);
249
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
250
+ if (typeof this.core.config.updateDragEl === "function") {
251
+ this.core.config.updateDragEl(this.dragEl, el);
252
+ }
222
253
  this.moveableOptions = this.getOptions({
223
254
  target: this.dragEl
224
255
  });
@@ -317,15 +348,7 @@ class StageDragResize extends EventEmitter {
317
348
  globalThis.clearTimeout(timeout);
318
349
  timeout = void 0;
319
350
  }
320
- timeout = globalThis.setTimeout(async () => {
321
- const els = this.core.getElementsFromPoint(e.inputEvent);
322
- for (const el of els) {
323
- if (doc && !el.id.startsWith(GHOST_EL_ID_PREFIX) && el !== this.target && await this.core.isContainer(el)) {
324
- addClassName(el, doc, this.core.containerHighlightClassName);
325
- break;
326
- }
327
- }
328
- }, this.core.containerHighlightDuration);
351
+ timeout = this.core.getAddContainerHighlightClassNameTimeout(e.inputEvent, [this.target]);
329
352
  this.dragStatus = "ing" /* ING */;
330
353
  if (this.ghostEl) {
331
354
  this.ghostEl.style.top = `${frame.top + e.beforeTranslate[1]}px`;
@@ -456,6 +479,7 @@ class StageDragResize extends EventEmitter {
456
479
  this.destroyGhostEl();
457
480
  }
458
481
  const ghostEl = el.cloneNode(true);
482
+ this.setGhostElChildrenId(ghostEl);
459
483
  const { top, left } = getAbsolutePosition(el, getOffset(el));
460
484
  ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
461
485
  ghostEl.style.zIndex = ZIndex.GHOST_EL;
@@ -466,30 +490,20 @@ class StageDragResize extends EventEmitter {
466
490
  el.after(ghostEl);
467
491
  return ghostEl;
468
492
  }
493
+ setGhostElChildrenId(el) {
494
+ for (const child of el.children) {
495
+ if (child.id) {
496
+ child.id = `${GHOST_EL_ID_PREFIX}${child.id}`;
497
+ }
498
+ if (child.children.length) {
499
+ this.setGhostElChildrenId(child);
500
+ }
501
+ }
502
+ }
469
503
  destroyGhostEl() {
470
504
  this.ghostEl?.remove();
471
505
  this.ghostEl = void 0;
472
506
  }
473
- updateDragEl(el) {
474
- const offset = getOffset(el);
475
- const { transform } = getComputedStyle(el);
476
- this.dragEl.style.cssText = `
477
- position: absolute;
478
- transform: ${transform};
479
- left: ${offset.left}px;
480
- top: ${offset.top}px;
481
- width: ${el.clientWidth}px;
482
- height: ${el.clientHeight}px;
483
- z-index: ${ZIndex.DRAG_EL};
484
- `;
485
- this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
486
- if (typeof this.core.config.updateDragEl === "function") {
487
- this.core.config.updateDragEl(this.dragEl, el);
488
- }
489
- }
490
- destroyDragEl() {
491
- this.dragEl?.remove();
492
- }
493
507
  getOptions(options = {}) {
494
508
  if (!this.target)
495
509
  return {};
@@ -854,6 +868,7 @@ class StageMask extends Rule {
854
868
  this.maxScrollTop = 0;
855
869
  this.maxScrollLeft = 0;
856
870
  this.intersectionObserver = null;
871
+ this.shiftKeyDown = false;
857
872
  this.mode = Mode.ABSOLUTE;
858
873
  this.pageResizeObserver = null;
859
874
  this.wrapperResizeObserver = null;
@@ -866,12 +881,19 @@ class StageMask extends Rule {
866
881
  event.stopPropagation();
867
882
  if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT)
868
883
  return;
884
+ if (!this.shiftKeyDown && event.target.className.indexOf("moveable-area") !== -1) {
885
+ return;
886
+ }
869
887
  if (event.target.className.indexOf("moveable-control") !== -1) {
870
888
  return;
871
889
  }
872
890
  this.content.removeEventListener("mousemove", this.highlightHandler);
873
- this.emit("beforeSelect", event);
874
- globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
891
+ if (this.shiftKeyDown) {
892
+ this.emit("beforeMultiSelect", event);
893
+ } else {
894
+ this.emit("beforeSelect", event);
895
+ globalThis.document.addEventListener("mouseup", this.mouseUpHandler);
896
+ }
875
897
  };
876
898
  this.mouseUpHandler = () => {
877
899
  globalThis.document.removeEventListener("mouseup", this.mouseUpHandler);
@@ -906,6 +928,14 @@ class StageMask extends Rule {
906
928
  this.content.addEventListener("wheel", this.mouseWheelHandler);
907
929
  this.content.addEventListener("mousemove", this.highlightHandler);
908
930
  this.content.addEventListener("mouseleave", this.mouseLeaveHandler);
931
+ KeyController.global.keydown("shift", (e) => {
932
+ e.inputEvent.preventDefault();
933
+ this.shiftKeyDown = true;
934
+ });
935
+ KeyController.global.keyup("shift", (e) => {
936
+ e.inputEvent.preventDefault();
937
+ this.shiftKeyDown = false;
938
+ });
909
939
  }
910
940
  setMode(mode) {
911
941
  this.mode = mode;
@@ -1036,6 +1066,118 @@ class StageMask extends Rule {
1036
1066
  }
1037
1067
  }
1038
1068
 
1069
+ class StageMultiDragResize extends EventEmitter {
1070
+ constructor(config) {
1071
+ super();
1072
+ this.targetList = [];
1073
+ this.dragElList = [];
1074
+ this.core = config.core;
1075
+ this.container = config.container;
1076
+ this.mask = config.mask;
1077
+ }
1078
+ multiSelect(els) {
1079
+ this.targetList = els;
1080
+ this.core.dr.destroyDragEl();
1081
+ this.destroyDragElList();
1082
+ this.dragElList = els.map((elItem) => {
1083
+ const dragElDiv = globalThis.document.createElement("div");
1084
+ this.container.append(dragElDiv);
1085
+ dragElDiv.style.cssText = getTargetElStyle(elItem);
1086
+ dragElDiv.id = `${DRAG_EL_ID_PREFIX}${elItem.id}`;
1087
+ if (typeof this.core.config.updateDragEl === "function") {
1088
+ this.core.config.updateDragEl(dragElDiv, elItem);
1089
+ }
1090
+ return dragElDiv;
1091
+ });
1092
+ this.moveableForMulti?.destroy();
1093
+ this.multiMoveableHelper?.clear();
1094
+ this.moveableForMulti = new Moveable(this.container, {
1095
+ target: this.dragElList,
1096
+ defaultGroupRotate: 0,
1097
+ defaultGroupOrigin: "50% 50%",
1098
+ draggable: true,
1099
+ resizable: true,
1100
+ throttleDrag: 0,
1101
+ startDragRotate: 0,
1102
+ throttleDragRotate: 0,
1103
+ zoom: 1,
1104
+ origin: true,
1105
+ padding: { left: 0, top: 0, right: 0, bottom: 0 }
1106
+ });
1107
+ this.multiMoveableHelper = MoveableHelper.create({
1108
+ useBeforeRender: true,
1109
+ useRender: false,
1110
+ createAuto: true
1111
+ });
1112
+ const frames = [];
1113
+ this.moveableForMulti.on("dragGroupStart", (params) => {
1114
+ const { events } = params;
1115
+ this.multiMoveableHelper?.onDragGroupStart(params);
1116
+ events.forEach((ev) => {
1117
+ const matchEventTarget = this.targetList.find((targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1118
+ if (!matchEventTarget)
1119
+ return;
1120
+ frames.push({
1121
+ left: matchEventTarget.offsetLeft,
1122
+ top: matchEventTarget.offsetTop,
1123
+ id: matchEventTarget.id
1124
+ });
1125
+ });
1126
+ }).on("dragGroup", (params) => {
1127
+ const { events } = params;
1128
+ events.forEach((ev) => {
1129
+ const frameSnapShot = frames.find((frameItem) => frameItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1130
+ if (!frameSnapShot)
1131
+ return;
1132
+ const targeEl = this.targetList.find((targetItem) => targetItem.id === ev.target.id.replace(DRAG_EL_ID_PREFIX, ""));
1133
+ if (!targeEl)
1134
+ return;
1135
+ const isParentIncluded = this.targetList.find((targetItem) => targetItem.id === targeEl.parentElement?.id);
1136
+ if (!isParentIncluded) {
1137
+ targeEl.style.left = `${frameSnapShot.left + ev.beforeTranslate[0]}px`;
1138
+ targeEl.style.top = `${frameSnapShot.top + ev.beforeTranslate[1]}px`;
1139
+ }
1140
+ });
1141
+ this.multiMoveableHelper?.onDragGroup(params);
1142
+ }).on("dragGroupEnd", () => {
1143
+ this.update();
1144
+ });
1145
+ }
1146
+ clearSelectStatus() {
1147
+ if (!this.moveableForMulti)
1148
+ return;
1149
+ this.destroyDragElList();
1150
+ this.moveableForMulti.target = null;
1151
+ this.moveableForMulti.updateTarget();
1152
+ }
1153
+ destroy() {
1154
+ this.moveableForMulti?.destroy();
1155
+ this.destroyDragElList();
1156
+ }
1157
+ destroyDragElList() {
1158
+ this.dragElList.forEach((dragElItem) => dragElItem?.remove());
1159
+ }
1160
+ update(isResize = false) {
1161
+ if (this.targetList.length === 0)
1162
+ return;
1163
+ const { contentWindow } = this.core.renderer;
1164
+ const doc = contentWindow?.document;
1165
+ if (!doc)
1166
+ return;
1167
+ this.targetList.forEach((targetItem) => {
1168
+ const offset = { left: targetItem.offsetLeft, top: targetItem.offsetTop };
1169
+ const left = calcValueByFontsize(doc, offset.left);
1170
+ const top = calcValueByFontsize(doc, offset.top);
1171
+ const width = calcValueByFontsize(doc, targetItem.clientWidth);
1172
+ const height = calcValueByFontsize(doc, targetItem.clientHeight);
1173
+ this.emit("update", {
1174
+ el: targetItem,
1175
+ style: isResize ? { left, top, width, height } : { left, top }
1176
+ });
1177
+ });
1178
+ }
1179
+ }
1180
+
1039
1181
  var 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}\n";
1040
1182
 
1041
1183
  class StageRender extends EventEmitter {
@@ -1115,6 +1257,7 @@ class StageRender extends EventEmitter {
1115
1257
  class StageCore extends EventEmitter {
1116
1258
  constructor(config) {
1117
1259
  super();
1260
+ this.selectedDomList = [];
1118
1261
  this.zoom = DEFAULT_ZOOM;
1119
1262
  this.config = config;
1120
1263
  this.setZoom(config.zoom);
@@ -1124,7 +1267,8 @@ class StageCore extends EventEmitter {
1124
1267
  this.containerHighlightDuration = config.containerHighlightDuration;
1125
1268
  this.renderer = new StageRender({ core: this });
1126
1269
  this.mask = new StageMask({ core: this });
1127
- this.dr = new StageDragResize({ core: this, container: this.mask.content });
1270
+ this.dr = new StageDragResize({ core: this, container: this.mask.content, mask: this.mask });
1271
+ this.multiDr = new StageMultiDragResize({ core: this, container: this.mask.content, mask: this.mask });
1128
1272
  this.highlightLayer = new StageHighlight({ core: this, container: this.mask.wrapper });
1129
1273
  this.renderer.on("runtime-ready", (runtime) => {
1130
1274
  this.emit("runtime-ready", runtime);
@@ -1132,29 +1276,57 @@ class StageCore extends EventEmitter {
1132
1276
  this.renderer.on("page-el-update", (el) => {
1133
1277
  this.mask?.observe(el);
1134
1278
  });
1135
- this.mask.on("beforeSelect", (event) => {
1136
- this.setElementFromPoint(event);
1279
+ this.mask.on("beforeSelect", async (event) => {
1280
+ this.clearSelectStatus("multiSelect");
1281
+ const el = await this.getElementFromPoint(event);
1282
+ if (!el)
1283
+ return;
1284
+ this.select(el, event);
1137
1285
  }).on("select", () => {
1138
1286
  this.emit("select", this.selectedDom);
1139
1287
  }).on("changeGuides", (data) => {
1140
1288
  this.dr.setGuidelines(data.type, data.guides);
1141
1289
  this.emit("changeGuides", data);
1142
1290
  }).on("highlight", async (event) => {
1143
- await this.setElementFromPoint(event);
1291
+ const el = await this.getElementFromPoint(event, "mousemove");
1292
+ if (!el)
1293
+ return;
1294
+ await this.highlight(el);
1144
1295
  if (this.highlightedDom === this.selectedDom) {
1145
1296
  this.highlightLayer.clearHighlight();
1146
1297
  return;
1147
1298
  }
1148
- this.highlightLayer.highlight(this.highlightedDom);
1149
1299
  this.emit("highlight", this.highlightedDom);
1150
1300
  }).on("clearHighlight", async () => {
1151
1301
  this.highlightLayer.clearHighlight();
1302
+ }).on("beforeMultiSelect", async (event) => {
1303
+ const el = await this.getElementFromPoint(event);
1304
+ if (!el)
1305
+ return;
1306
+ if (el.className.includes(PAGE_CLASS))
1307
+ return;
1308
+ this.clearSelectStatus("select");
1309
+ if (this.selectedDom && !this.selectedDom.className.includes(PAGE_CLASS)) {
1310
+ this.selectedDomList.push(this.selectedDom);
1311
+ this.selectedDom = void 0;
1312
+ }
1313
+ const existIndex = this.selectedDomList.findIndex((selectedDom) => selectedDom.id === el.id);
1314
+ if (existIndex !== -1) {
1315
+ this.selectedDomList.splice(existIndex, 1);
1316
+ } else {
1317
+ this.selectedDomList.push(el);
1318
+ }
1319
+ this.multiDr.multiSelect(this.selectedDomList);
1320
+ this.emit("multiSelect", this.selectedDomList);
1152
1321
  });
1153
1322
  this.dr.on("update", (data) => {
1154
1323
  setTimeout(() => this.emit("update", data));
1155
1324
  }).on("sort", (data) => {
1156
1325
  setTimeout(() => this.emit("sort", data));
1157
1326
  });
1327
+ this.multiDr.on("update", (data) => {
1328
+ setTimeout(() => this.emit("update", data));
1329
+ });
1158
1330
  }
1159
1331
  getElementsFromPoint(event) {
1160
1332
  const { renderer, zoom } = this;
@@ -1170,7 +1342,7 @@ class StageCore extends EventEmitter {
1170
1342
  }
1171
1343
  return doc?.elementsFromPoint(x / zoom, y / zoom);
1172
1344
  }
1173
- async setElementFromPoint(event) {
1345
+ async getElementFromPoint(event, type) {
1174
1346
  const els = this.getElementsFromPoint(event);
1175
1347
  let stopped = false;
1176
1348
  const stop = () => stopped = true;
@@ -1178,12 +1350,10 @@ class StageCore extends EventEmitter {
1178
1350
  if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.canSelect(el, event, stop)) {
1179
1351
  if (stopped)
1180
1352
  break;
1181
- if (event.type === "mousemove") {
1182
- this.highlight(el);
1183
- break;
1353
+ if (event.type === type) {
1354
+ return el;
1184
1355
  }
1185
- this.select(el, event);
1186
- break;
1356
+ return el;
1187
1357
  }
1188
1358
  }
1189
1359
  }
@@ -1197,6 +1367,7 @@ class StageCore extends EventEmitter {
1197
1367
  await runtime.beforeSelect(el);
1198
1368
  }
1199
1369
  this.mask.setLayout(el);
1370
+ this.multiDr.destroyDragElList();
1200
1371
  this.dr.select(el, event);
1201
1372
  if (this.config.autoScrollIntoView || el.dataset.autoScrollIntoView) {
1202
1373
  this.mask.intersectionObserver?.observe(el);
@@ -1231,7 +1402,7 @@ class StageCore extends EventEmitter {
1231
1402
  this.highlightLayer.clearHighlight();
1232
1403
  return;
1233
1404
  }
1234
- if (el === this.highlightedDom)
1405
+ if (el === this.highlightedDom || !el)
1235
1406
  return;
1236
1407
  this.highlightLayer.highlight(el);
1237
1408
  this.highlightedDom = el;
@@ -1248,6 +1419,14 @@ class StageCore extends EventEmitter {
1248
1419
  setZoom(zoom = DEFAULT_ZOOM) {
1249
1420
  this.zoom = zoom;
1250
1421
  }
1422
+ clearSelectStatus(selectType) {
1423
+ if (selectType === "multiSelect") {
1424
+ this.multiDr.clearSelectStatus();
1425
+ this.selectedDomList = [];
1426
+ } else {
1427
+ this.dr.clearSelectStatus();
1428
+ }
1429
+ }
1251
1430
  async mount(el) {
1252
1431
  this.container = el;
1253
1432
  const { mask, renderer } = this;
@@ -1259,6 +1438,24 @@ class StageCore extends EventEmitter {
1259
1438
  this.mask.clearGuides();
1260
1439
  this.dr.clearGuides();
1261
1440
  }
1441
+ async addContainerHighlightClassName(event, exclude) {
1442
+ const els = this.getElementsFromPoint(event);
1443
+ const { renderer } = this;
1444
+ const doc = renderer.contentWindow?.document;
1445
+ if (!doc)
1446
+ return;
1447
+ for (const el of els) {
1448
+ if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && await this.isContainer(el) && !exclude.includes(el)) {
1449
+ addClassName(el, doc, this.containerHighlightClassName);
1450
+ break;
1451
+ }
1452
+ }
1453
+ }
1454
+ getAddContainerHighlightClassNameTimeout(event, exclude = []) {
1455
+ return globalThis.setTimeout(() => {
1456
+ this.addContainerHighlightClassName(event, exclude);
1457
+ }, this.containerHighlightDuration);
1458
+ }
1262
1459
  destroy() {
1263
1460
  const { mask, renderer, dr, highlightLayer } = this;
1264
1461
  renderer.destroy();
@@ -1279,5 +1476,5 @@ class StageCore extends EventEmitter {
1279
1476
  }
1280
1477
  }
1281
1478
 
1282
- export { CONTAINER_HIGHLIGHT_CLASS, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, SELECTED_CLASS, StageDragResize, StageMask, StageRender, ZIndex, addSelectedClassName, calcValueByFontsize, StageCore as default, getAbsolutePosition, getMode, getOffset, getScrollParent, isAbsolute, isFixed, isFixedParent, isRelative, isStatic, removeSelectedClassName };
1479
+ export { CONTAINER_HIGHLIGHT_CLASS, DEFAULT_ZOOM, DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, HIGHLIGHT_EL_ID_PREFIX, Mode, MouseButton, PAGE_CLASS, SELECTED_CLASS, StageDragResize, StageMask, StageRender, ZIndex, addSelectedClassName, calcValueByFontsize, StageCore as default, getAbsolutePosition, getMode, getOffset, getScrollParent, getTargetElStyle, isAbsolute, isFixed, isFixedParent, isRelative, isStatic, removeSelectedClassName };
1283
1480
  //# sourceMappingURL=tmagic-stage.es.js.map