@easy-editor/plugin-dashboard 1.0.1 → 1.0.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { DESIGNER_EVENT, DragObjectType, getConvertedExtraKey } from '@easy-editor/core';
2
- import { observable, computed, action } from 'mobx';
1
+ import { DESIGNER_EVENT, createEventBus, makeEventsHandler, getConvertedExtraKey, DragObjectType } from '@easy-editor/core';
2
+ import { action, observable, computed } from 'mobx';
3
3
 
4
4
  function _applyDecs2311(e, t, n, r, o, i) {
5
5
  var a,
@@ -53,12 +53,12 @@ function _applyDecs2311(e, t, n, r, o, i) {
53
53
  t[4](this, e);
54
54
  }
55
55
  } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) {
56
- if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet");
56
+ if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet");
57
57
  y[+s][r] = o < 3 ? 1 : o;
58
58
  }
59
59
  }
60
60
  for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) {
61
- var T = b(h[O], "A decorator", "be", !0),
61
+ var T = b(h[O], "A decorator", "be", true),
62
62
  z = n ? h[O - 1] : void 0,
63
63
  A = {},
64
64
  H = {
@@ -67,7 +67,7 @@ function _applyDecs2311(e, t, n, r, o, i) {
67
67
  metadata: a,
68
68
  addInitializer: function (e, t) {
69
69
  if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished");
70
- b(t, "An initializer", "be", !0), i.push(t);
70
+ b(t, "An initializer", "be", true), i.push(t);
71
71
  }.bind(null, A)
72
72
  };
73
73
  if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = {
@@ -91,8 +91,8 @@ function _applyDecs2311(e, t, n, r, o, i) {
91
91
  }
92
92
  function w(e) {
93
93
  return m(e, d, {
94
- configurable: !0,
95
- enumerable: !0,
94
+ configurable: true,
95
+ enumerable: true,
96
96
  value: a
97
97
  });
98
98
  }
@@ -138,7 +138,7 @@ function _toPrimitive(t, r) {
138
138
  if ("object" != typeof t || !t) return t;
139
139
  var e = t[Symbol.toPrimitive];
140
140
  if (void 0 !== e) {
141
- var i = e.call(t, r || "default");
141
+ var i = e.call(t, r);
142
142
  if ("object" != typeof i) return i;
143
143
  throw new TypeError("@@toPrimitive must return a primitive value.");
144
144
  }
@@ -149,10 +149,144 @@ function _toPropertyKey(t) {
149
149
  return "symbol" == typeof i ? i : i + "";
150
150
  }
151
151
 
152
- let _initProto, _init_enabled, _init_extra_enabled, _init_guideLines, _init_extra_guideLines, _init_nodeLineMap, _init_extra_nodeLineMap, _init_adsorptionLines, _init_extra_adsorptionLines;
152
+ let _initProto$1;
153
+ let AlignType = /*#__PURE__*/function (AlignType) {
154
+ AlignType["LEFT"] = "left";
155
+ AlignType["RIGHT"] = "right";
156
+ AlignType["TOP"] = "top";
157
+ AlignType["BOTTOM"] = "bottom";
158
+ AlignType["HORIZONTAL_CENTER"] = "horizontalCenter";
159
+ AlignType["VERTICAL_CENTER"] = "verticalCenter";
160
+ return AlignType;
161
+ }({});
162
+ let DistributeType = /*#__PURE__*/function (DistributeType) {
163
+ DistributeType["HORIZONTAL"] = "horizontal";
164
+ DistributeType["VERTICAL"] = "vertical";
165
+ return DistributeType;
166
+ }({});
167
+ class Alignment {
168
+ static {
169
+ [_initProto$1] = _applyDecs2311(this, [], [[action, 2, "align"], [action, 2, "distribute"]]).e;
170
+ }
171
+ constructor(designer) {
172
+ this.designer = designer;
173
+ }
174
+ get selection() {
175
+ return this.designer.selection;
176
+ }
177
+ getSelectedNodes() {
178
+ return this.selection.getTopNodes(false).filter(node => node && !node.isRoot);
179
+ }
180
+ calculateBoundingBox(nodes) {
181
+ let [minX, minY, maxX, maxY] = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
182
+ for (const node of nodes) {
183
+ const rect = node.getDashboardRect();
184
+ minX = Math.min(minX, rect.x);
185
+ minY = Math.min(minY, rect.y);
186
+ maxX = Math.max(maxX, rect.x + rect.width);
187
+ maxY = Math.max(maxY, rect.y + rect.height);
188
+ }
189
+ return {
190
+ minX,
191
+ minY,
192
+ maxX,
193
+ maxY,
194
+ width: maxX - minX,
195
+ height: maxY - minY
196
+ };
197
+ }
198
+ updateNodePosition(node, x, y) {
199
+ if (node.isGroup) {
200
+ const nodeRect = node.getDashboardRect();
201
+ const deltaX = x - nodeRect.x;
202
+ const deltaY = y - nodeRect.y;
203
+ for (const child of node.getAllNodesInGroup()) {
204
+ const childRect = child.getDashboardRect();
205
+ child.updateDashboardRect({
206
+ x: childRect.x + deltaX,
207
+ y: childRect.y + deltaY
208
+ });
209
+ }
210
+ } else {
211
+ node.updateDashboardRect({
212
+ x,
213
+ y
214
+ });
215
+ }
216
+ }
217
+ align(type) {
218
+ const nodes = this.getSelectedNodes();
219
+ if (nodes.length < 2) return false;
220
+ const box = this.calculateBoundingBox(nodes);
221
+ for (const node of nodes) {
222
+ const rect = node.getDashboardRect();
223
+ let newX = rect.x;
224
+ let newY = rect.y;
225
+ switch (type) {
226
+ case AlignType.LEFT:
227
+ newX = box.minX;
228
+ break;
229
+ case AlignType.RIGHT:
230
+ newX = box.maxX - rect.width;
231
+ break;
232
+ case AlignType.TOP:
233
+ newY = box.minY;
234
+ break;
235
+ case AlignType.BOTTOM:
236
+ newY = box.maxY - rect.height;
237
+ break;
238
+ case AlignType.HORIZONTAL_CENTER:
239
+ newX = box.minX + (box.width - rect.width) / 2;
240
+ break;
241
+ case AlignType.VERTICAL_CENTER:
242
+ newY = box.minY + (box.height - rect.height) / 2;
243
+ break;
244
+ }
245
+ this.updateNodePosition(node, newX, newY);
246
+ }
247
+ return true;
248
+ }
249
+ distribute(type) {
250
+ const nodes = this.getSelectedNodes();
251
+ if (nodes.length < 3) return false;
252
+ const box = this.calculateBoundingBox(nodes);
253
+ if (type === DistributeType.HORIZONTAL) {
254
+ const sorted = [...nodes].sort((a, b) => a.getDashboardRect().x - b.getDashboardRect().x);
255
+ const totalWidth = sorted.reduce((sum, n) => sum + n.getDashboardRect().width, 0);
256
+ const gap = (box.width - totalWidth) / (nodes.length - 1);
257
+ let currentX = box.minX;
258
+ for (const node of sorted) {
259
+ const rect = node.getDashboardRect();
260
+ this.updateNodePosition(node, currentX, rect.y);
261
+ currentX += rect.width + gap;
262
+ }
263
+ } else {
264
+ const sorted = [...nodes].sort((a, b) => a.getDashboardRect().y - b.getDashboardRect().y);
265
+ const totalHeight = sorted.reduce((sum, n) => sum + n.getDashboardRect().height, 0);
266
+ const gap = (box.height - totalHeight) / (nodes.length - 1);
267
+ let currentY = box.minY;
268
+ for (const node of sorted) {
269
+ const rect = node.getDashboardRect();
270
+ this.updateNodePosition(node, rect.x, currentY);
271
+ currentY += rect.height + gap;
272
+ }
273
+ }
274
+ return true;
275
+ }
276
+ alignLeft = (_initProto$1(this), () => this.align(AlignType.LEFT));
277
+ alignRight = () => this.align(AlignType.RIGHT);
278
+ alignTop = () => this.align(AlignType.TOP);
279
+ alignBottom = () => this.align(AlignType.BOTTOM);
280
+ alignHorizontalCenter = () => this.align(AlignType.HORIZONTAL_CENTER);
281
+ alignVerticalCenter = () => this.align(AlignType.VERTICAL_CENTER);
282
+ distributeHorizontal = () => this.distribute(DistributeType.HORIZONTAL);
283
+ distributeVertical = () => this.distribute(DistributeType.VERTICAL);
284
+ }
285
+
286
+ let _initProto, _init_enabled, _init_extra_enabled, _init_guideLines, _init_extra_guideLines, _init_nodeLineMap, _init_extra_nodeLineMap, _init_adsorptionLines, _init_extra_adsorptionLines, _init_adsorptionLinesWithDistance, _init_extra_adsorptionLinesWithDistance, _init_userGuideLines, _init_extra_userGuideLines;
153
287
  class GuideLine {
154
288
  static {
155
- [_init_enabled, _init_extra_enabled, _init_guideLines, _init_extra_guideLines, _init_nodeLineMap, _init_extra_nodeLineMap, _init_adsorptionLines, _init_extra_adsorptionLines, _initProto] = _applyDecs2311(this, [], [[observable, 1, "enabled"], [[observable, observable.shallow], 17, "guideLines"], [computed, 3, "guideLinesMap"], [[observable, observable.shallow], 17, "nodeLineMap"], [observable, 1, "adsorptionLines"], [action, 2, "addGuideLine"], [action, 2, "removeGuideLine"], [action, 2, "updateGuideLine"], [action, 2, "calculateGuideLineInfo"], [action, 2, "getAdsorptionPosition"], [action, 2, "resetAdsorptionLines"]]).e;
289
+ [_init_enabled, _init_extra_enabled, _init_guideLines, _init_extra_guideLines, _init_nodeLineMap, _init_extra_nodeLineMap, _init_adsorptionLines, _init_extra_adsorptionLines, _init_adsorptionLinesWithDistance, _init_extra_adsorptionLinesWithDistance, _init_userGuideLines, _init_extra_userGuideLines, _initProto] = _applyDecs2311(this, [], [[observable, 1, "enabled"], [[observable, observable.shallow], 17, "guideLines"], [computed, 3, "guideLinesMap"], [[observable, observable.shallow], 17, "nodeLineMap"], [observable, 1, "adsorptionLines"], [[observable, observable.shallow], 17, "adsorptionLinesWithDistance"], [[observable, observable.shallow], 17, "userGuideLines"], [action, 2, "addGuideLine"], [action, 2, "removeGuideLine"], [action, 2, "updateGuideLine"], [computed, 3, "userGuideLinesMap"], [action, 2, "addUserGuideLine"], [action, 2, "updateUserGuideLine"], [action, 2, "removeUserGuideLine"], [action, 2, "clearUserGuideLines"], [action, 2, "calculateGuideLineInfo"], [action, 2, "getAdsorptionPosition"], [action, 2, "resetAdsorptionLines"]]).e;
156
290
  }
157
291
  #A = (_initProto(this), _init_enabled(this, true));
158
292
  get enabled() {
@@ -195,6 +329,21 @@ class GuideLine {
195
329
  set adsorptionLines(v) {
196
330
  this.#D = v;
197
331
  }
332
+ #E = (_init_extra_adsorptionLines(this), _init_adsorptionLinesWithDistance(this, []));
333
+ get adsorptionLinesWithDistance() {
334
+ return this.#E;
335
+ }
336
+ set adsorptionLinesWithDistance(v) {
337
+ this.#E = v;
338
+ }
339
+ #F = (_init_extra_adsorptionLinesWithDistance(this), _init_userGuideLines(this, []));
340
+ get userGuideLines() {
341
+ return this.#F;
342
+ }
343
+ set userGuideLines(v) {
344
+ this.#F = v;
345
+ }
346
+ userGuideLineIdCounter = (_init_extra_userGuideLines(this), 0);
198
347
  get currentDocument() {
199
348
  return this.designer.currentDocument;
200
349
  }
@@ -250,6 +399,41 @@ class GuideLine {
250
399
  this.guideLines[index] = guideLine;
251
400
  }
252
401
  }
402
+ get userGuideLinesMap() {
403
+ const result = Object.groupBy(this.userGuideLines, item => item.type);
404
+ return {
405
+ verticalLinesMap: new Map(result.vertical?.map(item => [item.position, item])),
406
+ horizontalLinesMap: new Map(result.horizontal?.map(item => [item.position, item]))
407
+ };
408
+ }
409
+ addUserGuideLine(type, position) {
410
+ const id = `user-guideline-${++this.userGuideLineIdCounter}`;
411
+ const guideLine = {
412
+ id,
413
+ type,
414
+ position: Math.round(position)
415
+ };
416
+ this.userGuideLines.push(guideLine);
417
+ return guideLine;
418
+ }
419
+ updateUserGuideLine(id, position) {
420
+ const index = this.userGuideLines.findIndex(item => item.id === id);
421
+ if (index !== -1) {
422
+ this.userGuideLines[index] = {
423
+ ...this.userGuideLines[index],
424
+ position: Math.round(position)
425
+ };
426
+ }
427
+ }
428
+ removeUserGuideLine(id) {
429
+ const index = this.userGuideLines.findIndex(item => item.id === id);
430
+ if (index !== -1) {
431
+ this.userGuideLines.splice(index, 1);
432
+ }
433
+ }
434
+ clearUserGuideLines() {
435
+ this.userGuideLines = [];
436
+ }
253
437
  calculateGuideLineInfo() {
254
438
  if (!this.enabled) return;
255
439
  const verticalLinesMap = new Map();
@@ -286,7 +470,8 @@ class GuideLine {
286
470
  this.nodeLineMap.verticalLinesMap = verticalLinesMap;
287
471
  this.nodeLineMap.horizontalLinesMap = horizontalLinesMap;
288
472
  }
289
- adsorptionSize = (_init_extra_adsorptionLines(this), 10);
473
+ adsorptionSize = 10;
474
+ maxDistanceDisplay = 300;
290
475
  getAdsorptionPosition(rect, adsorption) {
291
476
  if (typeof adsorption === 'number') {
292
477
  adsorption = [adsorption];
@@ -298,42 +483,63 @@ class GuideLine {
298
483
  const currentHorizontalLine = [rect.top, rect.top + rect.height / 2, rect.bottom];
299
484
  currentVerticalLine.forEach((item, index) => {
300
485
  let minDistance = Number.POSITIVE_INFINITY;
486
+ let closestPosition = null;
301
487
  this.nodeLineMap.verticalLinesMap.forEach((_, pos) => {
302
488
  const distance = Math.abs(item - pos);
303
489
  if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
304
- minDistance = pos;
490
+ minDistance = distance;
491
+ closestPosition = pos;
305
492
  }
306
493
  });
307
494
  this.guideLinesMap.verticalLinesMap.forEach((_, pos) => {
308
495
  const distance = Math.abs(item - pos);
309
496
  if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
310
- minDistance = pos;
497
+ minDistance = distance;
498
+ closestPosition = pos;
499
+ }
500
+ });
501
+ this.userGuideLinesMap.verticalLinesMap.forEach((_, pos) => {
502
+ const distance = Math.abs(item - pos);
503
+ if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
504
+ minDistance = distance;
505
+ closestPosition = pos;
311
506
  }
312
507
  });
313
- if (minDistance !== Number.POSITIVE_INFINITY && adsorptionVerticalLines.findIndex(item => item.position === minDistance) === -1) {
508
+ if (closestPosition !== null && adsorptionVerticalLines.findIndex(item => item.position === closestPosition) === -1) {
314
509
  adsorptionVerticalLines.push({
315
510
  adsorption: index,
316
- position: minDistance
511
+ position: closestPosition
317
512
  });
318
513
  }
319
514
  });
320
515
  currentHorizontalLine.forEach((item, index) => {
321
- let minDistance = -1;
516
+ let minDistance = Number.POSITIVE_INFINITY;
517
+ let closestPosition = null;
322
518
  this.nodeLineMap.horizontalLinesMap.forEach((_, pos) => {
323
- if (Math.abs(item - pos) < this.adsorptionSize) {
324
- minDistance = pos;
519
+ const distance = Math.abs(item - pos);
520
+ if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
521
+ minDistance = distance;
522
+ closestPosition = pos;
325
523
  }
326
524
  });
327
525
  this.guideLinesMap.horizontalLinesMap.forEach((_, pos) => {
328
526
  const distance = Math.abs(item - pos);
329
- if (distance < this.adsorptionSize && distance < minDistance) {
330
- minDistance = pos;
527
+ if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
528
+ minDistance = distance;
529
+ closestPosition = pos;
530
+ }
531
+ });
532
+ this.userGuideLinesMap.horizontalLinesMap.forEach((_, pos) => {
533
+ const distance = Math.abs(item - pos);
534
+ if (distance !== 0 && distance < this.adsorptionSize && distance < minDistance) {
535
+ minDistance = distance;
536
+ closestPosition = pos;
331
537
  }
332
538
  });
333
- if (minDistance !== -1 && adsorptionHorizontalLines.findIndex(item => item.position === minDistance) === -1) {
539
+ if (closestPosition !== null && adsorptionHorizontalLines.findIndex(item => item.position === closestPosition) === -1) {
334
540
  adsorptionHorizontalLines.push({
335
541
  adsorption: index,
336
- position: minDistance
542
+ position: closestPosition
337
543
  });
338
544
  }
339
545
  });
@@ -342,9 +548,32 @@ class GuideLine {
342
548
  x: undefined,
343
549
  y: undefined
344
550
  };
551
+ const linesWithDistance = [];
345
552
  if (isAdsorption) {
346
553
  adsorptionVerticalLines.forEach(item => this.adsorptionLines.verticalLines.add(item.position));
347
554
  adsorptionHorizontalLines.forEach(item => this.adsorptionLines.horizontalLines.add(item.position));
555
+ for (const line of adsorptionVerticalLines) {
556
+ const lineInfo = {
557
+ position: line.position,
558
+ type: 'vertical'
559
+ };
560
+ const segments = this.calculateVerticalLineSegments(rect, line.position);
561
+ if (segments.length > 0) {
562
+ lineInfo.distanceSegments = segments;
563
+ }
564
+ linesWithDistance.push(lineInfo);
565
+ }
566
+ for (const line of adsorptionHorizontalLines) {
567
+ const lineInfo = {
568
+ position: line.position,
569
+ type: 'horizontal'
570
+ };
571
+ const segments = this.calculateHorizontalLineSegments(rect, line.position);
572
+ if (segments.length > 0) {
573
+ lineInfo.distanceSegments = segments;
574
+ }
575
+ linesWithDistance.push(lineInfo);
576
+ }
348
577
  if (adsorptionVerticalLines.length > 0) {
349
578
  if (adsorption) {
350
579
  adsorb.x = adsorptionVerticalLines.find(item => adsorption.includes(item.adsorption));
@@ -362,6 +591,7 @@ class GuideLine {
362
591
  }
363
592
  }
364
593
  }
594
+ this.adsorptionLinesWithDistance = linesWithDistance;
365
595
  return {
366
596
  isAdsorption,
367
597
  adsorb
@@ -370,6 +600,250 @@ class GuideLine {
370
600
  resetAdsorptionLines() {
371
601
  this.adsorptionLines.verticalLines.clear();
372
602
  this.adsorptionLines.horizontalLines.clear();
603
+ this.adsorptionLinesWithDistance = [];
604
+ }
605
+ calculateVerticalLineSegments(rect, linePosition) {
606
+ const segments = [];
607
+ const selected = this.getSelectedNodeIds();
608
+ const nodes = [...this.currentDocument.nodesMap.values()].filter(node => !node.hidden && !selected.includes(node.id) && !node.isRoot && !node.isGroup);
609
+ let nearestAbove = null;
610
+ let nearestBelow = null;
611
+ for (const node of nodes) {
612
+ const nodeRect = node.getDashboardRect();
613
+ if (!nodeRect) continue;
614
+ const nodeLines = [nodeRect.left, nodeRect.left + nodeRect.width / 2, nodeRect.right];
615
+ const isOnLine = nodeLines.some(pos => Math.abs(pos - linePosition) < 1);
616
+ if (!isOnLine) continue;
617
+ if (nodeRect.bottom <= rect.top) {
618
+ const distance = rect.top - nodeRect.bottom;
619
+ if (!nearestAbove || distance < nearestAbove.distance) {
620
+ nearestAbove = {
621
+ distance,
622
+ nodeRect
623
+ };
624
+ }
625
+ } else if (nodeRect.top >= rect.bottom) {
626
+ const distance = nodeRect.top - rect.bottom;
627
+ if (!nearestBelow || distance < nearestBelow.distance) {
628
+ nearestBelow = {
629
+ distance,
630
+ nodeRect
631
+ };
632
+ }
633
+ }
634
+ }
635
+ if (nearestAbove && this.isDistanceInDisplayRange(nearestAbove.distance)) {
636
+ segments.push({
637
+ distance: Math.round(nearestAbove.distance),
638
+ start: nearestAbove.nodeRect.bottom,
639
+ end: rect.top,
640
+ fixedPos: linePosition,
641
+ labelPos: nearestAbove.nodeRect.bottom + nearestAbove.distance / 2
642
+ });
643
+ }
644
+ if (nearestBelow && this.isDistanceInDisplayRange(nearestBelow.distance)) {
645
+ segments.push({
646
+ distance: Math.round(nearestBelow.distance),
647
+ start: rect.bottom,
648
+ end: nearestBelow.nodeRect.top,
649
+ fixedPos: linePosition,
650
+ labelPos: rect.bottom + nearestBelow.distance / 2
651
+ });
652
+ }
653
+ return segments;
654
+ }
655
+ calculateHorizontalLineSegments(rect, linePosition) {
656
+ const segments = [];
657
+ const selected = this.getSelectedNodeIds();
658
+ const nodes = [...this.currentDocument.nodesMap.values()].filter(node => !node.hidden && !selected.includes(node.id) && !node.isRoot && !node.isGroup);
659
+ let nearestLeft = null;
660
+ let nearestRight = null;
661
+ for (const node of nodes) {
662
+ const nodeRect = node.getDashboardRect();
663
+ if (!nodeRect) continue;
664
+ const nodeLines = [nodeRect.top, nodeRect.top + nodeRect.height / 2, nodeRect.bottom];
665
+ const isOnLine = nodeLines.some(pos => Math.abs(pos - linePosition) < 1);
666
+ if (!isOnLine) continue;
667
+ if (nodeRect.right <= rect.left) {
668
+ const distance = rect.left - nodeRect.right;
669
+ if (!nearestLeft || distance < nearestLeft.distance) {
670
+ nearestLeft = {
671
+ distance,
672
+ nodeRect
673
+ };
674
+ }
675
+ } else if (nodeRect.left >= rect.right) {
676
+ const distance = nodeRect.left - rect.right;
677
+ if (!nearestRight || distance < nearestRight.distance) {
678
+ nearestRight = {
679
+ distance,
680
+ nodeRect
681
+ };
682
+ }
683
+ }
684
+ }
685
+ if (nearestLeft && this.isDistanceInDisplayRange(nearestLeft.distance)) {
686
+ segments.push({
687
+ distance: Math.round(nearestLeft.distance),
688
+ start: nearestLeft.nodeRect.right,
689
+ end: rect.left,
690
+ fixedPos: linePosition,
691
+ labelPos: nearestLeft.nodeRect.right + nearestLeft.distance / 2
692
+ });
693
+ }
694
+ if (nearestRight && this.isDistanceInDisplayRange(nearestRight.distance)) {
695
+ segments.push({
696
+ distance: Math.round(nearestRight.distance),
697
+ start: rect.right,
698
+ end: nearestRight.nodeRect.left,
699
+ fixedPos: linePosition,
700
+ labelPos: rect.right + nearestRight.distance / 2
701
+ });
702
+ }
703
+ return segments;
704
+ }
705
+ getSelectedNodeIds() {
706
+ const selected = [];
707
+ for (const node of this.designer.selection.getNodes()) {
708
+ if (node.isGroup) {
709
+ selected.push(...node.getAllNodesInGroup().map(n => n.id));
710
+ } else {
711
+ selected.push(node.id);
712
+ }
713
+ }
714
+ return selected;
715
+ }
716
+ isDistanceInDisplayRange(distance) {
717
+ return distance >= this.adsorptionSize && distance <= this.maxDistanceDisplay;
718
+ }
719
+ }
720
+
721
+ class MarqueeSelection {
722
+ emitter = createEventBus('MarqueeEngine');
723
+ isMarqueeSelecting = false;
724
+ enabled = true;
725
+ constructor(designer) {
726
+ this.designer = designer;
727
+ }
728
+ isSelecting() {
729
+ return this.isMarqueeSelecting;
730
+ }
731
+ from(shell, viewport, isClickOnNode) {
732
+ if (!shell) {
733
+ return () => {};
734
+ }
735
+ let startX = 0;
736
+ let startY = 0;
737
+ let isCtrlPressed = false;
738
+ const masterSensors = this.getMasterSensors();
739
+ const calculateRect = (x1, y1, x2, y2) => {
740
+ return {
741
+ x: Math.min(x1, x2),
742
+ y: Math.min(y1, y2),
743
+ width: Math.abs(x2 - x1),
744
+ height: Math.abs(y2 - y1)
745
+ };
746
+ };
747
+ const move = e => {
748
+ const localPoint = viewport.toLocalPoint(e);
749
+ const currentX = localPoint.clientX;
750
+ const currentY = localPoint.clientY;
751
+ const marqueeRect = calculateRect(startX, startY, currentX, currentY);
752
+ this.emitter.emit('marquee', {
753
+ startX,
754
+ startY,
755
+ currentX,
756
+ currentY,
757
+ rect: marqueeRect,
758
+ isCtrlPressed: e.ctrlKey || e.metaKey
759
+ });
760
+ };
761
+ const over = e => {
762
+ const handleEvents = makeEventsHandler(e, masterSensors);
763
+ handleEvents(doc => {
764
+ doc.removeEventListener('mousemove', move, true);
765
+ doc.removeEventListener('mouseup', over, true);
766
+ });
767
+ const localPoint = viewport.toLocalPoint(e);
768
+ const currentX = localPoint.clientX;
769
+ const currentY = localPoint.clientY;
770
+ const marqueeRect = calculateRect(startX, startY, currentX, currentY);
771
+ this.isMarqueeSelecting = false;
772
+ this.designer.detecting.enable = true;
773
+ this.emitter.emit('marqueeEnd', {
774
+ startX,
775
+ startY,
776
+ currentX,
777
+ currentY,
778
+ rect: marqueeRect,
779
+ isCtrlPressed: e.ctrlKey || e.metaKey
780
+ });
781
+ };
782
+ const mousedown = e => {
783
+ if (e.button !== 0) return;
784
+ if (!this.enabled) {
785
+ return;
786
+ }
787
+ const target = e.target;
788
+ if (isClickOnNode(target)) {
789
+ return;
790
+ }
791
+ e.preventDefault();
792
+ const localPoint = viewport.toLocalPoint(e);
793
+ startX = localPoint.clientX;
794
+ startY = localPoint.clientY;
795
+ isCtrlPressed = e.ctrlKey || e.metaKey;
796
+ const handleEvents = makeEventsHandler(e, masterSensors);
797
+ handleEvents(doc => {
798
+ doc.addEventListener('mousemove', move, true);
799
+ doc.addEventListener('mouseup', over, true);
800
+ });
801
+ this.emitter.emit('marqueeStart', {
802
+ startX,
803
+ startY,
804
+ currentX: startX,
805
+ currentY: startY,
806
+ rect: {
807
+ x: startX,
808
+ y: startY,
809
+ width: 0,
810
+ height: 0
811
+ },
812
+ isCtrlPressed
813
+ });
814
+ this.isMarqueeSelecting = true;
815
+ this.designer.detecting.enable = false;
816
+ };
817
+ shell.addEventListener('mousedown', mousedown);
818
+ return () => {
819
+ shell.removeEventListener('mousedown', mousedown);
820
+ };
821
+ }
822
+ onMarqueeStart(func) {
823
+ this.emitter.on('marqueeStart', func);
824
+ return () => {
825
+ this.emitter.off('marqueeStart', func);
826
+ };
827
+ }
828
+ onMarquee(func) {
829
+ this.emitter.on('marquee', func);
830
+ return () => {
831
+ this.emitter.off('marquee', func);
832
+ };
833
+ }
834
+ onMarqueeEnd(func) {
835
+ this.emitter.on('marqueeEnd', func);
836
+ return () => {
837
+ this.emitter.off('marqueeEnd', func);
838
+ };
839
+ }
840
+ getMasterSensors() {
841
+ return this.designer.project.documents.map(doc => {
842
+ if (doc.opened && doc.simulator?.sensorAvailable) {
843
+ return doc.simulator;
844
+ }
845
+ return null;
846
+ }).filter(Boolean);
373
847
  }
374
848
  }
375
849
 
@@ -521,10 +995,11 @@ const DashboardPlugin = options => {
521
995
  x: boxX = 0,
522
996
  y: boxY = 0
523
997
  } = startOffsetNodes.box;
998
+ const currentRect = new DOMRect(e.canvasX - boxX, e.canvasY - boxY, width, height);
524
999
  const {
525
1000
  isAdsorption,
526
1001
  adsorb
527
- } = designer.guideline.getAdsorptionPosition(new DOMRect(e.canvasX - boxX, e.canvasY - boxY, width, height));
1002
+ } = designer.guideline.getAdsorptionPosition(currentRect);
528
1003
  let adsorbX = undefined;
529
1004
  let adsorbY = undefined;
530
1005
  if (isAdsorption) {
@@ -614,6 +1089,8 @@ const DashboardPlugin = options => {
614
1089
  Node
615
1090
  } = extendClass;
616
1091
  let guideline = null;
1092
+ let alignment = null;
1093
+ let marqueeSelection = null;
617
1094
  extend('Designer', {
618
1095
  guideline: {
619
1096
  get() {
@@ -622,6 +1099,22 @@ const DashboardPlugin = options => {
622
1099
  }
623
1100
  return guideline;
624
1101
  }
1102
+ },
1103
+ alignment: {
1104
+ get() {
1105
+ if (!alignment) {
1106
+ alignment = new Alignment(this);
1107
+ }
1108
+ return alignment;
1109
+ }
1110
+ },
1111
+ marqueeSelection: {
1112
+ get() {
1113
+ if (!marqueeSelection) {
1114
+ marqueeSelection = new MarqueeSelection(this);
1115
+ }
1116
+ return marqueeSelection;
1117
+ }
625
1118
  }
626
1119
  });
627
1120
  extend('Document', {
@@ -631,18 +1124,21 @@ const DashboardPlugin = options => {
631
1124
  let nodeList = [];
632
1125
  if (typeof nodeIdList[0] === 'string') {
633
1126
  nodeList = nodeIdList.map(id => this.getNode(id));
1127
+ } else {
1128
+ nodeList = nodeIdList;
634
1129
  }
1130
+ nodeList = nodeList.filter(node => node && !node.isRoot);
1131
+ if (nodeList.length === 0) return;
1132
+ const targetParent = nodeList[0].parent || this.rootNode;
635
1133
  const groupNode = this.createNode(groupInitSchema);
636
- let maxZIndex = Number.POSITIVE_INFINITY;
1134
+ let minIndex = Number.POSITIVE_INFINITY;
637
1135
  for (const node of nodeList) {
638
- if (node.index < maxZIndex) {
639
- maxZIndex = node.index;
640
- }
641
- if (node && !node.isRoot) {
642
- this.migrateNode(node, groupNode);
1136
+ if (node.index < minIndex) {
1137
+ minIndex = node.index;
643
1138
  }
1139
+ this.migrateNode(node, groupNode);
644
1140
  }
645
- this.rootNode?.insert(groupNode, maxZIndex);
1141
+ targetParent?.insert(groupNode, minIndex);
646
1142
  return groupNode;
647
1143
  }
648
1144
  },
@@ -655,11 +1151,12 @@ const DashboardPlugin = options => {
655
1151
  groupNode = group;
656
1152
  }
657
1153
  if (!groupNode || !groupNode.isGroup || !groupNode.children) return;
658
- const nodes = groupNode.childrenNodes;
659
- while (nodes.length > 0) {
660
- if (groupNode.parent) {
661
- this.migrateNode(nodes[0], groupNode.parent);
662
- }
1154
+ const parent = groupNode.parent;
1155
+ if (!parent) return;
1156
+ groupNode.index;
1157
+ const nodes = [...groupNode.childrenNodes];
1158
+ for (let i = 0; i < nodes.length; i++) {
1159
+ this.migrateNode(nodes[i], parent);
663
1160
  }
664
1161
  this.removeNode(groupNode);
665
1162
  }
@@ -880,4 +1377,4 @@ const calculateDashboardRectBox = nodes => {
880
1377
  return new DOMRect(minX, minY, maxX - minX, maxY - minY);
881
1378
  };
882
1379
 
883
- export { DashboardPlugin as default };
1380
+ export { AlignType, Alignment, DistributeType, GuideLine, DashboardPlugin as default, updateNodeRect, updateNodeRectByDOM };