@plait/core 0.51.2 → 0.51.4

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.
Files changed (71) hide show
  1. package/board/board.component.d.ts +1 -1
  2. package/esm2022/board/board.component.mjs +7 -6
  3. package/esm2022/core/children/children.component.mjs +5 -5
  4. package/esm2022/core/element/context-change.mjs +1 -1
  5. package/esm2022/core/element/element.component.mjs +5 -5
  6. package/esm2022/core/element/plugin-element.mjs +5 -5
  7. package/esm2022/core/island/island-base.component.mjs +9 -9
  8. package/esm2022/interfaces/board.mjs +1 -1
  9. package/esm2022/interfaces/element.mjs +1 -1
  10. package/esm2022/interfaces/group.mjs +6 -0
  11. package/esm2022/interfaces/index.mjs +2 -1
  12. package/esm2022/interfaces/node.mjs +1 -1
  13. package/esm2022/interfaces/operation.mjs +1 -1
  14. package/esm2022/interfaces/path-ref.mjs +1 -1
  15. package/esm2022/interfaces/path.mjs +1 -1
  16. package/esm2022/interfaces/rectangle-client.mjs +1 -1
  17. package/esm2022/interfaces/selection.mjs +1 -1
  18. package/esm2022/plugins/create-board.mjs +1 -1
  19. package/esm2022/plugins/with-board.mjs +1 -1
  20. package/esm2022/plugins/with-group.mjs +27 -0
  21. package/esm2022/plugins/with-hand.mjs +1 -1
  22. package/esm2022/plugins/with-history.mjs +1 -1
  23. package/esm2022/plugins/with-hotkey.mjs +1 -1
  24. package/esm2022/plugins/with-moving.mjs +3 -3
  25. package/esm2022/plugins/with-selection.mjs +96 -69
  26. package/esm2022/plugins/with-viewport.mjs +1 -1
  27. package/esm2022/services/image-context.service.mjs +4 -4
  28. package/esm2022/testing/fake-events/event-objects.mjs +4 -4
  29. package/esm2022/transforms/board.mjs +1 -1
  30. package/esm2022/transforms/general.mjs +1 -1
  31. package/esm2022/transforms/node.mjs +1 -1
  32. package/esm2022/transforms/selection.mjs +2 -2
  33. package/esm2022/utils/angle.mjs +36 -0
  34. package/esm2022/utils/board.mjs +1 -1
  35. package/esm2022/utils/clipboard/clipboard.mjs +1 -1
  36. package/esm2022/utils/clipboard/common.mjs +1 -1
  37. package/esm2022/utils/clipboard/data-transfer.mjs +1 -1
  38. package/esm2022/utils/clipboard/navigator-clipboard.mjs +1 -1
  39. package/esm2022/utils/common.mjs +1 -1
  40. package/esm2022/utils/dom/common.mjs +8 -4
  41. package/esm2022/utils/dom/foreign.mjs +1 -1
  42. package/esm2022/utils/drawing/line.mjs +1 -1
  43. package/esm2022/utils/drawing/rectangle.mjs +1 -1
  44. package/esm2022/utils/element.mjs +17 -5
  45. package/esm2022/utils/group.mjs +209 -0
  46. package/esm2022/utils/history.mjs +1 -1
  47. package/esm2022/utils/hotkeys.mjs +1 -1
  48. package/esm2022/utils/id-creator.mjs +1 -1
  49. package/esm2022/utils/index.mjs +4 -1
  50. package/esm2022/utils/math.mjs +23 -8
  51. package/esm2022/utils/reaction-manager.mjs +1 -1
  52. package/esm2022/utils/selected-element.mjs +1 -1
  53. package/esm2022/utils/selection.mjs +62 -0
  54. package/esm2022/utils/to-image.mjs +1 -1
  55. package/esm2022/utils/touch.mjs +1 -1
  56. package/esm2022/utils/tree.mjs +1 -1
  57. package/esm2022/utils/viewport.mjs +1 -1
  58. package/fesm2022/plait-core.mjs +1134 -756
  59. package/fesm2022/plait-core.mjs.map +1 -1
  60. package/interfaces/element.d.ts +1 -0
  61. package/interfaces/group.d.ts +7 -0
  62. package/interfaces/index.d.ts +1 -0
  63. package/package.json +1 -1
  64. package/plugins/with-group.d.ts +2 -0
  65. package/plugins/with-selection.d.ts +0 -13
  66. package/utils/angle.d.ts +5 -0
  67. package/utils/dom/common.d.ts +2 -2
  68. package/utils/group.d.ts +22 -0
  69. package/utils/index.d.ts +3 -0
  70. package/utils/math.d.ts +9 -1
  71. package/utils/selection.d.ts +13 -0
@@ -535,9 +535,13 @@ function createRect(rectangle, options) {
535
535
  const setStrokeLinecap = (g, value) => {
536
536
  g.setAttribute('stroke-linecap', value);
537
537
  };
538
- const setTransformRotate = (g, rectangle, angle) => {
539
- var centerX = rectangle.x + rectangle.width / 2;
540
- var centerY = rectangle.y + rectangle.height / 2;
538
+ const setAngleForG = (g, centerPoint, angle) => {
539
+ if (angle === 0) {
540
+ g.removeAttribute('transform');
541
+ return;
542
+ }
543
+ var centerX = centerPoint[0];
544
+ var centerY = centerPoint[1];
541
545
  let cosTheta = Math.cos(angle);
542
546
  let sinTheta = Math.sin(angle);
543
547
  let transformMatrix = [
@@ -670,12 +674,12 @@ class PlaitPluginElementComponent {
670
674
  removeSelectedElement(this.board, this.element);
671
675
  (this.rootG || this.g).remove();
672
676
  }
673
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
674
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
677
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
678
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.2.4", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
675
679
  }
676
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
680
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
677
681
  type: Directive
678
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
682
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { context: [{
679
683
  type: Input
680
684
  }] } });
681
685
  const ELEMENT_TO_COMPONENT = new WeakMap();
@@ -795,14 +799,19 @@ const isLineHitLine = (a, b, c, d) => {
795
799
  const cd = [d[0] - c[0], d[1] - c[1]];
796
800
  return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
797
801
  };
798
- const isPolylineHitRectangle = (points, rectangle) => {
802
+ const isPolylineHitRectangle = (points, rectangle, isClose = true) => {
799
803
  const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
800
- for (let i = 1; i < points.length; i++) {
801
- const isIntersect = isLineHitLine(points[i], points[i - 1], rectanglePoints[0], rectanglePoints[1]) ||
802
- isLineHitLine(points[i], points[i - 1], rectanglePoints[1], rectanglePoints[2]) ||
803
- isLineHitLine(points[i], points[i - 1], rectanglePoints[2], rectanglePoints[3]) ||
804
- isLineHitLine(points[i], points[i - 1], rectanglePoints[3], rectanglePoints[0]);
805
- if (isIntersect) {
804
+ const len = points.length;
805
+ for (let i = 0; i < len; i++) {
806
+ if (i === len - 1 && !isClose)
807
+ continue;
808
+ const p1 = points[i];
809
+ const p2 = points[(i + 1) % len];
810
+ const isHit = isLineHitLine(p1, p2, rectanglePoints[0], rectanglePoints[1]) ||
811
+ isLineHitLine(p1, p2, rectanglePoints[1], rectanglePoints[2]) ||
812
+ isLineHitLine(p1, p2, rectanglePoints[2], rectanglePoints[3]) ||
813
+ isLineHitLine(p1, p2, rectanglePoints[3], rectanglePoints[0]);
814
+ if (isHit || isPointInPolygon(p1, rectanglePoints) || isPointInPolygon(p2, rectanglePoints)) {
806
815
  return true;
807
816
  }
808
817
  }
@@ -943,6 +952,16 @@ function toDomPrecision(v) {
943
952
  function toFixed(v) {
944
953
  return +v.toFixed(2);
945
954
  }
955
+ /**
956
+ * Whether two numbers numbers a and b are approximately equal.
957
+ *
958
+ * @param a - The first point.
959
+ * @param b - The second point.
960
+ * @public
961
+ */
962
+ function approximately(a, b, precision = 0.000001) {
963
+ return Math.abs(a - b) <= precision;
964
+ }
946
965
 
947
966
  function isInPlaitBoard(board, x, y) {
948
967
  const plaitBoardElement = PlaitBoard.getBoardContainer(board);
@@ -2171,18 +2190,15 @@ const handleTouchTarget = (board) => {
2171
2190
  }
2172
2191
  };
2173
2192
 
2174
- const PlaitElement = {
2175
- isRootElement(value) {
2176
- const parent = NODE_TO_PARENT.get(value);
2177
- if (parent && PlaitBoard.isBoard(parent)) {
2178
- return true;
2179
- }
2180
- else {
2181
- return false;
2182
- }
2183
- },
2184
- getComponent(value) {
2185
- return ELEMENT_TO_COMPONENT.get(value);
2193
+ const PlaitGroupElement = {
2194
+ isGroup: (value) => {
2195
+ return value.type === 'group';
2196
+ }
2197
+ };
2198
+
2199
+ const Viewport = {
2200
+ isViewport: (value) => {
2201
+ return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
2186
2202
  }
2187
2203
  };
2188
2204
 
@@ -2408,803 +2424,872 @@ const PlaitNode = {
2408
2424
  }
2409
2425
  };
2410
2426
 
2411
- const isSetViewportOperation = (value) => {
2412
- return value.type === 'set_viewport';
2413
- };
2414
- const inverse = (op) => {
2427
+ const applyToDraft = (board, selection, viewport, theme, op) => {
2415
2428
  switch (op.type) {
2416
2429
  case 'insert_node': {
2417
- return { ...op, type: 'remove_node' };
2430
+ const { path, node } = op;
2431
+ const parent = PlaitNode.parent(board, path);
2432
+ const index = path[path.length - 1];
2433
+ if (!parent.children || index > parent.children.length) {
2434
+ throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2435
+ }
2436
+ parent.children.splice(index, 0, node);
2437
+ break;
2418
2438
  }
2419
2439
  case 'remove_node': {
2420
- return { ...op, type: 'insert_node' };
2440
+ const { path } = op;
2441
+ const parent = PlaitNode.parent(board, path);
2442
+ const index = path[path.length - 1];
2443
+ if (!parent.children || index > parent.children.length) {
2444
+ throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2445
+ }
2446
+ parent.children.splice(index, 1);
2447
+ break;
2421
2448
  }
2422
2449
  case 'move_node': {
2423
- const { newPath, path } = op;
2424
- // PERF: in this case the move operation is a no-op anyways.
2425
- if (Path.equals(newPath, path)) {
2426
- return op;
2450
+ const { path, newPath } = op;
2451
+ if (Path.isAncestor(path, newPath)) {
2452
+ throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
2427
2453
  }
2428
- // when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
2429
- // shoud not return [0,2] -> [0,0] #WIK-8981
2430
- // if (Path.isSibling(path, newPath)) {
2431
- // return { ...op, path: newPath, newPath: path };
2432
- // }
2433
- // If the move does not happen within a single parent it is possible
2434
- // for the move to impact the true path to the location where the node
2435
- // was removed from and where it was inserted. We have to adjust for this
2436
- // and find the original path. We can accomplish this (only in non-sibling)
2437
- // moves by looking at the impact of the move operation on the node
2438
- // after the original move path.
2439
- const inversePath = Path.transform(path, op);
2440
- const inverseNewPath = Path.transform(Path.next(path), op);
2441
- return { ...op, path: inversePath, newPath: inverseNewPath };
2454
+ const node = PlaitNode.get(board, path);
2455
+ const parent = PlaitNode.parent(board, path);
2456
+ const index = path[path.length - 1];
2457
+ // This is tricky, but since the `path` and `newPath` both refer to
2458
+ // the same snapshot in time, there's a mismatch. After either
2459
+ // removing the original position, the second step's path can be out
2460
+ // of date. So instead of using the `op.newPath` directly, we
2461
+ // transform `op.path` to ascertain what the `newPath` would be after
2462
+ // the operation was applied.
2463
+ parent.children?.splice(index, 1);
2464
+ const truePath = Path.transform(path, op);
2465
+ const newParent = PlaitNode.get(board, Path.parent(truePath));
2466
+ const newIndex = truePath[truePath.length - 1];
2467
+ newParent.children?.splice(newIndex, 0, node);
2468
+ break;
2442
2469
  }
2443
2470
  case 'set_node': {
2444
- const { properties, newProperties } = op;
2445
- return { ...op, properties: newProperties, newProperties: properties };
2446
- }
2447
- case 'set_selection': {
2448
- const { properties, newProperties } = op;
2449
- if (properties == null) {
2450
- return {
2451
- ...op,
2452
- properties: newProperties,
2453
- newProperties: null
2454
- };
2471
+ const { path, properties, newProperties } = op;
2472
+ if (path.length === 0) {
2473
+ throw new Error(`Cannot set properties on the root node!`);
2455
2474
  }
2456
- else if (newProperties == null) {
2457
- return {
2458
- ...op,
2459
- properties: null,
2460
- newProperties: properties
2461
- };
2475
+ const node = PlaitNode.get(board, path);
2476
+ for (const key in newProperties) {
2477
+ const value = newProperties[key];
2478
+ if (value == null) {
2479
+ delete node[key];
2480
+ }
2481
+ else {
2482
+ node[key] = value;
2483
+ }
2462
2484
  }
2463
- else {
2464
- return { ...op, properties: newProperties, newProperties: properties };
2485
+ // properties that were previously defined, but are now missing, must be deleted
2486
+ for (const key in properties) {
2487
+ if (!newProperties.hasOwnProperty(key)) {
2488
+ delete node[key];
2489
+ }
2465
2490
  }
2491
+ break;
2466
2492
  }
2467
2493
  case 'set_viewport': {
2468
- const { properties, newProperties } = op;
2469
- if (properties == null) {
2470
- return {
2471
- ...op,
2472
- properties: newProperties,
2473
- newProperties: newProperties
2474
- };
2494
+ const { newProperties } = op;
2495
+ if (newProperties == null) {
2496
+ viewport = newProperties;
2475
2497
  }
2476
- else if (newProperties == null) {
2477
- return {
2478
- ...op,
2479
- properties: properties,
2480
- newProperties: properties
2481
- };
2498
+ else {
2499
+ if (viewport == null) {
2500
+ if (!Viewport.isViewport(newProperties)) {
2501
+ throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
2502
+ }
2503
+ viewport = { ...newProperties };
2504
+ }
2505
+ for (const key in newProperties) {
2506
+ const value = newProperties[key];
2507
+ if (value == null) {
2508
+ delete viewport[key];
2509
+ }
2510
+ else {
2511
+ viewport[key] = value;
2512
+ }
2513
+ }
2514
+ }
2515
+ break;
2516
+ }
2517
+ case 'set_selection': {
2518
+ const { newProperties } = op;
2519
+ if (newProperties == null) {
2520
+ selection = newProperties;
2482
2521
  }
2483
2522
  else {
2484
- return { ...op, properties: newProperties, newProperties: properties };
2523
+ if (selection === null) {
2524
+ selection = op.newProperties;
2525
+ }
2526
+ else {
2527
+ selection = newProperties;
2528
+ }
2485
2529
  }
2530
+ break;
2486
2531
  }
2487
2532
  case 'set_theme': {
2488
- const { properties, newProperties } = op;
2489
- return { ...op, properties: newProperties, newProperties: properties };
2533
+ const { newProperties } = op;
2534
+ theme = newProperties;
2535
+ break;
2490
2536
  }
2491
2537
  }
2538
+ return { selection, viewport, theme };
2492
2539
  };
2493
- const PlaitOperation = {
2494
- isSetViewportOperation,
2495
- inverse
2496
- };
2497
-
2498
- const Point = {
2499
- isEquals(point, otherPoint) {
2500
- return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
2501
- },
2502
- isHorizontal(point, otherPoint, tolerance = 0) {
2503
- return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
2504
- },
2505
- isOverHorizontal(points, tolerance = 0) {
2506
- return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
2507
- },
2508
- isVertical(point, otherPoint, tolerance = 0) {
2509
- return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
2510
- },
2511
- isOverVertical(points, tolerance = 0) {
2512
- return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
2513
- },
2514
- isAlign(points, tolerance = 0) {
2515
- return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
2516
- },
2517
- getOffsetX(point1, point2) {
2518
- return point2[0] - point1[0];
2519
- },
2520
- getOffsetY(point1, point2) {
2521
- return point2[1] - point1[1];
2540
+ const GeneralTransforms = {
2541
+ /**
2542
+ * Transform the board by an operation.
2543
+ */
2544
+ transform(board, op) {
2545
+ board.children = createDraft(board.children);
2546
+ let viewport = board.viewport && createDraft(board.viewport);
2547
+ let selection = board.selection && createDraft(board.selection);
2548
+ let theme = board.theme && createDraft(board.theme);
2549
+ try {
2550
+ const state = applyToDraft(board, selection, viewport, theme, op);
2551
+ viewport = state.viewport;
2552
+ selection = state.selection;
2553
+ theme = state.theme;
2554
+ }
2555
+ finally {
2556
+ board.children = finishDraft(board.children);
2557
+ if (selection) {
2558
+ board.selection = isDraft(selection) ? finishDraft(selection) : selection;
2559
+ }
2560
+ else {
2561
+ board.selection = null;
2562
+ }
2563
+ board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
2564
+ board.theme = isDraft(theme) ? finishDraft(theme) : theme;
2565
+ }
2522
2566
  }
2523
2567
  };
2524
2568
 
2525
- const Viewport = {
2526
- isViewport: (value) => {
2527
- return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
2569
+ function insertNode(board, node, path) {
2570
+ const operation = { type: 'insert_node', node, path };
2571
+ board.apply(operation);
2572
+ }
2573
+ function setNode(board, props, path) {
2574
+ const properties = {};
2575
+ const newProperties = {};
2576
+ const node = PlaitNode.get(board, path);
2577
+ for (const k in props) {
2578
+ if (node[k] !== props[k]) {
2579
+ if (node.hasOwnProperty(k)) {
2580
+ properties[k] = node[k];
2581
+ }
2582
+ if (props[k] != null)
2583
+ newProperties[k] = props[k];
2584
+ }
2528
2585
  }
2586
+ const operation = { type: 'set_node', properties, newProperties, path };
2587
+ board.apply(operation);
2588
+ }
2589
+ function removeNode(board, path) {
2590
+ const node = PlaitNode.get(board, path);
2591
+ const operation = { type: 'remove_node', path, node };
2592
+ board.apply(operation);
2593
+ }
2594
+ function moveNode(board, path, newPath) {
2595
+ const operation = { type: 'move_node', path, newPath };
2596
+ board.apply(operation);
2597
+ }
2598
+ const NodeTransforms = {
2599
+ insertNode,
2600
+ setNode,
2601
+ removeNode,
2602
+ moveNode
2529
2603
  };
2530
-
2531
- const SAVING = new WeakMap();
2532
- const MERGING = new WeakMap();
2533
-
2534
- var ThemeColorMode;
2535
- (function (ThemeColorMode) {
2536
- ThemeColorMode["default"] = "default";
2537
- ThemeColorMode["colorful"] = "colorful";
2538
- ThemeColorMode["soft"] = "soft";
2539
- ThemeColorMode["retro"] = "retro";
2540
- ThemeColorMode["dark"] = "dark";
2541
- ThemeColorMode["starry"] = "starry";
2542
- })(ThemeColorMode || (ThemeColorMode = {}));
2543
- const DefaultThemeColor = {
2544
- mode: ThemeColorMode.default,
2545
- boardBackground: '#ffffff',
2546
- textColor: '#333333'
2547
- };
2548
- const ColorfulThemeColor = {
2549
- mode: ThemeColorMode.colorful,
2550
- boardBackground: '#ffffff',
2551
- textColor: '#333333'
2552
- };
2553
- const SoftThemeColor = {
2554
- mode: ThemeColorMode.soft,
2555
- boardBackground: '#f5f5f5',
2556
- textColor: '#333333'
2557
- };
2558
- const RetroThemeColor = {
2559
- mode: ThemeColorMode.retro,
2560
- boardBackground: '#f9f8ed',
2561
- textColor: '#333333'
2604
+
2605
+ function setSelection(board, selection) {
2606
+ const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
2607
+ board.apply(operation);
2608
+ }
2609
+ const SelectionTransforms = {
2610
+ setSelection,
2611
+ addSelectionWithTemporaryElements
2562
2612
  };
2563
- const DarkThemeColor = {
2564
- mode: ThemeColorMode.dark,
2565
- boardBackground: '#141414',
2566
- textColor: '#FFFFFF'
2613
+ function addSelectionWithTemporaryElements(board, elements) {
2614
+ const timeoutId = setTimeout(() => {
2615
+ setSelection(board, { anchor: [0, 0], focus: [0, 0] });
2616
+ }, 0);
2617
+ let ref = getTemporaryRef(board);
2618
+ if (ref) {
2619
+ clearTimeout(ref.timeoutId);
2620
+ const currentElements = ref.elements;
2621
+ ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
2622
+ ref.timeoutId = timeoutId;
2623
+ }
2624
+ else {
2625
+ BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
2626
+ }
2627
+ }
2628
+
2629
+ const removeElements = (board, elements) => {
2630
+ elements
2631
+ .map(element => {
2632
+ const path = PlaitBoard.findPath(board, element);
2633
+ const ref = board.pathRef(path);
2634
+ return () => {
2635
+ removeNode(board, ref.current);
2636
+ ref.unref();
2637
+ removeSelectedElement(board, element, true);
2638
+ };
2639
+ })
2640
+ .forEach(action => {
2641
+ action();
2642
+ });
2567
2643
  };
2568
- const StarryThemeColor = {
2569
- mode: ThemeColorMode.starry,
2570
- boardBackground: '#0d2537',
2571
- textColor: '#FFFFFF'
2644
+ const CoreTransforms = {
2645
+ removeElements
2572
2646
  };
2573
- const ThemeColors = [
2574
- DefaultThemeColor,
2575
- ColorfulThemeColor,
2576
- SoftThemeColor,
2577
- RetroThemeColor,
2578
- DarkThemeColor,
2579
- StarryThemeColor
2580
- ];
2581
2647
 
2582
- var Direction;
2583
- (function (Direction) {
2584
- Direction["left"] = "left";
2585
- Direction["top"] = "top";
2586
- Direction["right"] = "right";
2587
- Direction["bottom"] = "bottom";
2588
- })(Direction || (Direction = {}));
2648
+ const Transforms = {
2649
+ ...GeneralTransforms,
2650
+ ...ViewportTransforms$1,
2651
+ ...SelectionTransforms,
2652
+ ...NodeTransforms
2653
+ };
2589
2654
 
2590
- function getRectangleByElements(board, elements, recursion) {
2591
- const rectangles = [];
2592
- const callback = (node) => {
2593
- const nodeRectangle = board.getRectangle(node);
2594
- if (nodeRectangle) {
2595
- rectangles.push(nodeRectangle);
2596
- }
2597
- else {
2598
- console.error(`can not get rectangle of element:`, node);
2599
- }
2600
- };
2601
- elements.forEach(element => {
2602
- if (recursion) {
2603
- depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
2604
- }
2605
- else {
2606
- callback(element);
2607
- }
2608
- });
2609
- if (rectangles.length > 0) {
2610
- return RectangleClient.getBoundingRectangle(rectangles);
2655
+ function isSelectionMoving(board) {
2656
+ return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
2657
+ }
2658
+ function setSelectionMoving(board) {
2659
+ PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
2660
+ BOARD_TO_IS_SELECTION_MOVING.set(board, true);
2661
+ setDragging(board, true);
2662
+ }
2663
+ function clearSelectionMoving(board) {
2664
+ PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
2665
+ BOARD_TO_IS_SELECTION_MOVING.delete(board);
2666
+ setDragging(board, false);
2667
+ }
2668
+ function isHandleSelection(board) {
2669
+ const options = board.getPluginOptions(PlaitPluginKey.withSelection);
2670
+ return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
2671
+ }
2672
+ function isSetSelectionOperation(board) {
2673
+ return !!board.operations.find(value => value.type === 'set_selection');
2674
+ }
2675
+ function getTemporaryElements(board) {
2676
+ const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2677
+ if (ref) {
2678
+ return ref.elements;
2611
2679
  }
2612
2680
  else {
2613
- return {
2614
- x: 0,
2615
- y: 0,
2616
- width: 0,
2617
- height: 0
2618
- };
2681
+ return undefined;
2619
2682
  }
2620
2683
  }
2621
- function getBoardRectangle(board) {
2622
- return getRectangleByElements(board, board.children, true);
2684
+ function getTemporaryRef(board) {
2685
+ return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2623
2686
  }
2624
- function getElementById(board, id, dataSource) {
2625
- if (!dataSource) {
2626
- dataSource = findElements(board, { match: element => true, recursion: element => true });
2627
- }
2628
- let element = dataSource.find(element => element.id === id);
2629
- return element;
2687
+ function deleteTemporaryElements(board) {
2688
+ BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
2630
2689
  }
2631
- function findElements(board, options) {
2632
- let elements = [];
2633
- const isReverse = options.isReverse ?? true;
2634
- depthFirstRecursion(board, node => {
2635
- if (!PlaitBoard.isBoard(node) && options.match(node)) {
2636
- elements.push(node);
2637
- }
2638
- }, (value) => {
2639
- if (PlaitBoard.isBoard(value)) {
2640
- return true;
2641
- }
2642
- else {
2643
- return getIsRecursionFunc(board)(value) && options.recursion(value);
2690
+ function createSelectionRectangleG(board) {
2691
+ const elements = getSelectedElements(board);
2692
+ const rectangle = getRectangleByElements(board, elements, false);
2693
+ if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
2694
+ const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
2695
+ stroke: SELECTION_BORDER_COLOR,
2696
+ strokeWidth: ACTIVE_STROKE_WIDTH,
2697
+ fillStyle: 'solid'
2698
+ });
2699
+ selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
2700
+ PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
2701
+ const angle = getSelectionAngle(elements);
2702
+ if (angle) {
2703
+ setAngleForG(selectionRectangleG, RectangleClient.getCenterPoint(rectangle), angle);
2644
2704
  }
2645
- }, isReverse);
2646
- return elements;
2705
+ return selectionRectangleG;
2706
+ }
2707
+ return null;
2647
2708
  }
2648
2709
 
2649
- const PlaitBoard = {
2650
- isBoard(value) {
2651
- const cachedIsBoard = IS_BOARD_CACHE.get(value);
2652
- if (cachedIsBoard !== undefined) {
2653
- return cachedIsBoard;
2654
- }
2655
- const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
2656
- IS_BOARD_CACHE.set(value, isBoard);
2657
- return isBoard;
2658
- },
2659
- findPath(board, node) {
2660
- const path = [];
2661
- let child = node;
2662
- while (true) {
2663
- const parent = NODE_TO_PARENT.get(child);
2664
- if (parent == null) {
2665
- if (PlaitBoard.isBoard(child)) {
2666
- return path;
2667
- }
2668
- else {
2669
- break;
2710
+ const getElementsInGroup = (board, group, recursion, includeGroup) => {
2711
+ let result = [];
2712
+ const elements = board.children.filter(value => value.groupId === group.id);
2713
+ if (recursion) {
2714
+ elements.forEach(item => {
2715
+ if (PlaitGroupElement.isGroup(item)) {
2716
+ if (includeGroup) {
2717
+ result.push(item);
2670
2718
  }
2719
+ result.push(...getElementsInGroup(board, item, recursion));
2671
2720
  }
2672
- const i = NODE_TO_INDEX.get(child);
2673
- if (i == null) {
2674
- break;
2721
+ else {
2722
+ result.push(item);
2675
2723
  }
2676
- path.unshift(i);
2677
- child = parent;
2724
+ });
2725
+ }
2726
+ else {
2727
+ result = includeGroup ? elements : elements.filter(item => !PlaitGroupElement.isGroup(item));
2728
+ }
2729
+ return result;
2730
+ };
2731
+ const getRectangleByGroup = (board, group, recursion) => {
2732
+ const elementsInGroup = getElementsInGroup(board, group, recursion);
2733
+ return getRectangleByElements(board, elementsInGroup, false);
2734
+ };
2735
+ const getGroupByElement = (board, element, recursion) => {
2736
+ const group = board.children.find(item => item.id === element?.groupId);
2737
+ if (!group) {
2738
+ return recursion ? [] : null;
2739
+ }
2740
+ if (recursion) {
2741
+ const groups = [group];
2742
+ const grandGroups = getGroupByElement(board, group, recursion);
2743
+ if (grandGroups.length) {
2744
+ groups.push(...grandGroups);
2678
2745
  }
2679
- throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
2680
- },
2681
- getHost(board) {
2682
- return BOARD_TO_HOST.get(board);
2683
- },
2684
- getElementHost(board) {
2685
- return BOARD_TO_ELEMENT_HOST.get(board)?.host;
2686
- },
2687
- getElementUpperHost(board) {
2688
- return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
2689
- },
2690
- getElementActiveHost(board) {
2691
- return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
2692
- },
2693
- getRoughSVG(board) {
2694
- return BOARD_TO_ROUGH_SVG.get(board);
2695
- },
2696
- getComponent(board) {
2697
- return BOARD_TO_COMPONENT.get(board);
2698
- },
2699
- getBoardContainer(board) {
2700
- return BOARD_TO_ELEMENT_HOST.get(board)?.container;
2701
- },
2702
- getRectangle(board) {
2703
- return getRectangleByElements(board, board.children, true);
2704
- },
2705
- getViewportContainer(board) {
2706
- return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
2707
- },
2708
- isFocus(board) {
2709
- return !!board.selection;
2710
- },
2711
- isReadonly(board) {
2712
- return board.options.readonly;
2713
- },
2714
- hasBeenTextEditing(board) {
2715
- return !!IS_TEXT_EDITABLE.get(board);
2716
- },
2717
- getPointer(board) {
2718
- return board.pointer;
2719
- },
2720
- isPointer(board, pointer) {
2721
- return board.pointer === pointer;
2722
- },
2723
- isInPointer(board, pointers) {
2724
- const point = board.pointer;
2725
- return pointers.includes(point);
2726
- },
2727
- getMovingPointInBoard(board) {
2728
- return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
2729
- },
2730
- isMovingPointInBoard(board) {
2731
- const point = BOARD_TO_MOVING_POINT.get(board);
2732
- const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
2733
- if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
2746
+ return groups;
2747
+ }
2748
+ else {
2749
+ return group;
2750
+ }
2751
+ };
2752
+ const getHighestGroup = (board, element) => {
2753
+ const groups = getGroupByElement(board, element, true);
2754
+ if (groups.length) {
2755
+ return groups[groups.length - 1];
2756
+ }
2757
+ return null;
2758
+ };
2759
+ const getElementsInGroupByElement = (board, element) => {
2760
+ const highestGroup = getHighestGroup(board, element);
2761
+ if (highestGroup) {
2762
+ return getElementsInGroup(board, highestGroup, true);
2763
+ }
2764
+ else {
2765
+ return [element];
2766
+ }
2767
+ };
2768
+ const isSelectedElementOrGroup = (board, element) => {
2769
+ const selectedElements = getSelectedElements(board);
2770
+ if (PlaitGroupElement.isGroup(element)) {
2771
+ return isSelectedAllElementsInGroup(board, element);
2772
+ }
2773
+ return selectedElements.includes(element);
2774
+ };
2775
+ const isSelectedAllElementsInGroup = (board, group) => {
2776
+ const selectedElements = getSelectedElements(board);
2777
+ const elementsInGroup = getElementsInGroup(board, group, true);
2778
+ return elementsInGroup.every(item => selectedElements.includes(item));
2779
+ };
2780
+ const getSelectedGroups = (board, groups) => {
2781
+ const selectedGroups = [];
2782
+ groups.forEach(item => {
2783
+ if (isSelectedElementOrGroup(board, item)) {
2784
+ selectedGroups.push(item);
2785
+ }
2786
+ });
2787
+ return selectedGroups;
2788
+ };
2789
+ const getHighestSelectedGroup = (board, element) => {
2790
+ const groups = getGroupByElement(board, element, true);
2791
+ const selectedGroups = getSelectedGroups(board, groups);
2792
+ if (selectedGroups.length) {
2793
+ return selectedGroups[selectedGroups.length - 1];
2794
+ }
2795
+ return null;
2796
+ };
2797
+ const getHighestSelectedGroups = (board) => {
2798
+ let result = [];
2799
+ const selectedElements = getSelectedElements(board);
2800
+ selectedElements.forEach(item => {
2801
+ if (item.groupId) {
2802
+ const group = getHighestSelectedGroup(board, item);
2803
+ if (group && !result.includes(group)) {
2804
+ result.push(group);
2805
+ }
2806
+ }
2807
+ });
2808
+ return result;
2809
+ };
2810
+ const getSelectedIsolatedElements = (board) => {
2811
+ let result = [];
2812
+ const selectedElements = getSelectedElements(board);
2813
+ selectedElements.forEach(item => {
2814
+ if (!item.groupId) {
2815
+ result.push(item);
2816
+ }
2817
+ else {
2818
+ const group = getHighestSelectedGroup(board, item);
2819
+ if (!group) {
2820
+ result.push(item);
2821
+ }
2822
+ }
2823
+ });
2824
+ return result;
2825
+ };
2826
+ const getHighestSelectedElements = (board) => {
2827
+ return [...getHighestSelectedGroups(board), ...getSelectedIsolatedElements(board)];
2828
+ };
2829
+ const createGroupRectangleG = (board, elements) => {
2830
+ const selectedElements = getSelectedElements(board);
2831
+ const groupRectangleG = createG();
2832
+ const isMoving = isSelectionMoving(board);
2833
+ elements.forEach(item => {
2834
+ const isRender = (!selectedElements.includes(item) && !isMoving) || isMoving;
2835
+ if (item.groupId && isRender) {
2836
+ const elements = getElementsInGroupByElement(board, item);
2837
+ const rectangle = getRectangleByElements(board, elements, false);
2838
+ groupRectangleG.append(drawRectangle(board, rectangle, {
2839
+ stroke: SELECTION_BORDER_COLOR,
2840
+ strokeWidth: ACTIVE_STROKE_WIDTH,
2841
+ strokeLineDash: [5]
2842
+ }));
2843
+ }
2844
+ });
2845
+ return groupRectangleG;
2846
+ };
2847
+ const createGroup = () => {
2848
+ return {
2849
+ id: idCreator(),
2850
+ type: 'group'
2851
+ };
2852
+ };
2853
+ const nonGroupInHighestSelectedElements = (elements) => {
2854
+ return elements.every(item => !item.groupId);
2855
+ };
2856
+ const hasSelectedElementsInSameGroup = (elements) => {
2857
+ return elements.every(item => item.groupId && item.groupId === elements[0].groupId);
2858
+ };
2859
+ const canAddGroup = (highestSelectedElements) => {
2860
+ if (highestSelectedElements.length > 1) {
2861
+ return nonGroupInHighestSelectedElements(highestSelectedElements) || hasSelectedElementsInSameGroup(highestSelectedElements);
2862
+ }
2863
+ return false;
2864
+ };
2865
+ const addGroup = (board) => {
2866
+ const selectedGroups = getHighestSelectedGroups(board);
2867
+ const selectedIsolatedElements = getSelectedIsolatedElements(board);
2868
+ const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
2869
+ const group = createGroup();
2870
+ if (canAddGroup(highestSelectedElements)) {
2871
+ highestSelectedElements.forEach(item => {
2872
+ const path = PlaitBoard.findPath(board, item);
2873
+ Transforms.setNode(board, { groupId: group.id }, path);
2874
+ });
2875
+ if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
2876
+ const newGroupId = selectedIsolatedElements[0].groupId;
2877
+ Transforms.insertNode(board, {
2878
+ ...group,
2879
+ groupId: newGroupId
2880
+ }, [board.children.length]);
2881
+ }
2882
+ else {
2883
+ Transforms.insertNode(board, group, [board.children.length]);
2884
+ }
2885
+ }
2886
+ };
2887
+ const canRemoveGroup = (board, selectedGroups) => {
2888
+ const selectedElements = getSelectedElements(board);
2889
+ return selectedElements.length > 0 && selectedGroups.length > 0;
2890
+ };
2891
+ const removeGroup = (board) => {
2892
+ const selectedGroups = getHighestSelectedGroups(board);
2893
+ if (canRemoveGroup(board, selectedGroups)) {
2894
+ selectedGroups.map(group => {
2895
+ const elementsInGroup = findElements(board, {
2896
+ match: item => item.groupId === group.id,
2897
+ recursion: () => false
2898
+ });
2899
+ elementsInGroup.forEach(item => {
2900
+ const path = PlaitBoard.findPath(board, item);
2901
+ Transforms.setNode(board, { groupId: group.groupId || undefined }, path);
2902
+ });
2903
+ const groupPath = PlaitBoard.findPath(board, group);
2904
+ Transforms.removeNode(board, groupPath);
2905
+ });
2906
+ }
2907
+ };
2908
+
2909
+ const rotatePoints = (points, centerPoint, angle) => {
2910
+ if (!angle) {
2911
+ angle = 0;
2912
+ }
2913
+ return points.map(point => {
2914
+ return rotate(point[0], point[1], centerPoint[0], centerPoint[1], angle);
2915
+ });
2916
+ };
2917
+ const getSelectionAngle = (elements) => {
2918
+ let angle = elements[0].angle || 0;
2919
+ elements.forEach(item => {
2920
+ if (item.angle !== angle && !approximately((item.angle % (Math.PI / 2)) - (angle % (Math.PI / 2)), 0)) {
2921
+ angle = 0;
2922
+ }
2923
+ });
2924
+ return angle;
2925
+ };
2926
+ const hasSameAngle = (elements) => {
2927
+ return !!getSelectionAngle(elements);
2928
+ };
2929
+ const getRotatedBoundingRectangle = (rectanglesCornerPoints, angle) => {
2930
+ let rectanglesFromOrigin = [];
2931
+ for (let i = 0; i < rectanglesCornerPoints.length; i++) {
2932
+ const cornerPoints = rectanglesCornerPoints[i];
2933
+ const invertCornerPointsFromOrigin = rotatePoints(cornerPoints, [0, 0], -angle);
2934
+ rectanglesFromOrigin.push(RectangleClient.getRectangleByPoints(invertCornerPointsFromOrigin));
2935
+ }
2936
+ const selectionRectangleFromOrigin = RectangleClient.getBoundingRectangle(rectanglesFromOrigin);
2937
+ const selectionCornerPoints = RectangleClient.getCornerPoints(selectionRectangleFromOrigin);
2938
+ const cornerPointsFromOrigin = rotatePoints(selectionCornerPoints, [0, 0], angle);
2939
+ const centerPoint = RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(cornerPointsFromOrigin));
2940
+ return RectangleClient.getRectangleByPoints(rotatePoints(cornerPointsFromOrigin, centerPoint, -angle));
2941
+ };
2942
+
2943
+ const PlaitElement = {
2944
+ isRootElement(value) {
2945
+ const parent = NODE_TO_PARENT.get(value);
2946
+ if (parent && PlaitBoard.isBoard(parent)) {
2734
2947
  return true;
2735
2948
  }
2736
- return false;
2949
+ else {
2950
+ return false;
2951
+ }
2737
2952
  },
2738
- getThemeColors(board) {
2739
- return (board.options.themeColors || ThemeColors);
2953
+ getComponent(value) {
2954
+ return ELEMENT_TO_COMPONENT.get(value);
2740
2955
  }
2741
2956
  };
2742
2957
 
2743
- const applyToDraft = (board, selection, viewport, theme, op) => {
2958
+ const isSetViewportOperation = (value) => {
2959
+ return value.type === 'set_viewport';
2960
+ };
2961
+ const inverse = (op) => {
2744
2962
  switch (op.type) {
2745
2963
  case 'insert_node': {
2746
- const { path, node } = op;
2747
- const parent = PlaitNode.parent(board, path);
2748
- const index = path[path.length - 1];
2749
- if (!parent.children || index > parent.children.length) {
2750
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2751
- }
2752
- parent.children.splice(index, 0, node);
2753
- break;
2964
+ return { ...op, type: 'remove_node' };
2754
2965
  }
2755
2966
  case 'remove_node': {
2756
- const { path } = op;
2757
- const parent = PlaitNode.parent(board, path);
2758
- const index = path[path.length - 1];
2759
- if (!parent.children || index > parent.children.length) {
2760
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2761
- }
2762
- parent.children.splice(index, 1);
2763
- break;
2967
+ return { ...op, type: 'insert_node' };
2764
2968
  }
2765
2969
  case 'move_node': {
2766
- const { path, newPath } = op;
2767
- if (Path.isAncestor(path, newPath)) {
2768
- throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
2970
+ const { newPath, path } = op;
2971
+ // PERF: in this case the move operation is a no-op anyways.
2972
+ if (Path.equals(newPath, path)) {
2973
+ return op;
2769
2974
  }
2770
- const node = PlaitNode.get(board, path);
2771
- const parent = PlaitNode.parent(board, path);
2772
- const index = path[path.length - 1];
2773
- // This is tricky, but since the `path` and `newPath` both refer to
2774
- // the same snapshot in time, there's a mismatch. After either
2775
- // removing the original position, the second step's path can be out
2776
- // of date. So instead of using the `op.newPath` directly, we
2777
- // transform `op.path` to ascertain what the `newPath` would be after
2778
- // the operation was applied.
2779
- parent.children?.splice(index, 1);
2780
- const truePath = Path.transform(path, op);
2781
- const newParent = PlaitNode.get(board, Path.parent(truePath));
2782
- const newIndex = truePath[truePath.length - 1];
2783
- newParent.children?.splice(newIndex, 0, node);
2784
- break;
2975
+ // when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
2976
+ // shoud not return [0,2] -> [0,0] #WIK-8981
2977
+ // if (Path.isSibling(path, newPath)) {
2978
+ // return { ...op, path: newPath, newPath: path };
2979
+ // }
2980
+ // If the move does not happen within a single parent it is possible
2981
+ // for the move to impact the true path to the location where the node
2982
+ // was removed from and where it was inserted. We have to adjust for this
2983
+ // and find the original path. We can accomplish this (only in non-sibling)
2984
+ // moves by looking at the impact of the move operation on the node
2985
+ // after the original move path.
2986
+ const inversePath = Path.transform(path, op);
2987
+ const inverseNewPath = Path.transform(Path.next(path), op);
2988
+ return { ...op, path: inversePath, newPath: inverseNewPath };
2785
2989
  }
2786
2990
  case 'set_node': {
2787
- const { path, properties, newProperties } = op;
2788
- if (path.length === 0) {
2789
- throw new Error(`Cannot set properties on the root node!`);
2991
+ const { properties, newProperties } = op;
2992
+ return { ...op, properties: newProperties, newProperties: properties };
2993
+ }
2994
+ case 'set_selection': {
2995
+ const { properties, newProperties } = op;
2996
+ if (properties == null) {
2997
+ return {
2998
+ ...op,
2999
+ properties: newProperties,
3000
+ newProperties: null
3001
+ };
2790
3002
  }
2791
- const node = PlaitNode.get(board, path);
2792
- for (const key in newProperties) {
2793
- const value = newProperties[key];
2794
- if (value == null) {
2795
- delete node[key];
2796
- }
2797
- else {
2798
- node[key] = value;
2799
- }
3003
+ else if (newProperties == null) {
3004
+ return {
3005
+ ...op,
3006
+ properties: null,
3007
+ newProperties: properties
3008
+ };
2800
3009
  }
2801
- // properties that were previously defined, but are now missing, must be deleted
2802
- for (const key in properties) {
2803
- if (!newProperties.hasOwnProperty(key)) {
2804
- delete node[key];
2805
- }
3010
+ else {
3011
+ return { ...op, properties: newProperties, newProperties: properties };
2806
3012
  }
2807
- break;
2808
3013
  }
2809
3014
  case 'set_viewport': {
2810
- const { newProperties } = op;
2811
- if (newProperties == null) {
2812
- viewport = newProperties;
3015
+ const { properties, newProperties } = op;
3016
+ if (properties == null) {
3017
+ return {
3018
+ ...op,
3019
+ properties: newProperties,
3020
+ newProperties: newProperties
3021
+ };
3022
+ }
3023
+ else if (newProperties == null) {
3024
+ return {
3025
+ ...op,
3026
+ properties: properties,
3027
+ newProperties: properties
3028
+ };
2813
3029
  }
2814
3030
  else {
2815
- if (viewport == null) {
2816
- if (!Viewport.isViewport(newProperties)) {
2817
- throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
2818
- }
2819
- viewport = { ...newProperties };
2820
- }
2821
- for (const key in newProperties) {
2822
- const value = newProperties[key];
2823
- if (value == null) {
2824
- delete viewport[key];
2825
- }
2826
- else {
2827
- viewport[key] = value;
2828
- }
2829
- }
3031
+ return { ...op, properties: newProperties, newProperties: properties };
2830
3032
  }
2831
- break;
2832
- }
2833
- case 'set_selection': {
2834
- const { newProperties } = op;
2835
- if (newProperties == null) {
2836
- selection = newProperties;
2837
- }
2838
- else {
2839
- if (selection === null) {
2840
- selection = op.newProperties;
2841
- }
2842
- else {
2843
- selection = newProperties;
2844
- }
2845
- }
2846
- break;
2847
3033
  }
2848
3034
  case 'set_theme': {
2849
- const { newProperties } = op;
2850
- theme = newProperties;
2851
- break;
3035
+ const { properties, newProperties } = op;
3036
+ return { ...op, properties: newProperties, newProperties: properties };
2852
3037
  }
2853
3038
  }
2854
- return { selection, viewport, theme };
2855
3039
  };
2856
- const GeneralTransforms = {
2857
- /**
2858
- * Transform the board by an operation.
2859
- */
2860
- transform(board, op) {
2861
- board.children = createDraft(board.children);
2862
- let viewport = board.viewport && createDraft(board.viewport);
2863
- let selection = board.selection && createDraft(board.selection);
2864
- let theme = board.theme && createDraft(board.theme);
2865
- try {
2866
- const state = applyToDraft(board, selection, viewport, theme, op);
2867
- viewport = state.viewport;
2868
- selection = state.selection;
2869
- theme = state.theme;
2870
- }
2871
- finally {
2872
- board.children = finishDraft(board.children);
2873
- if (selection) {
2874
- board.selection = isDraft(selection) ? finishDraft(selection) : selection;
2875
- }
2876
- else {
2877
- board.selection = null;
2878
- }
2879
- board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
2880
- board.theme = isDraft(theme) ? finishDraft(theme) : theme;
2881
- }
2882
- }
3040
+ const PlaitOperation = {
3041
+ isSetViewportOperation,
3042
+ inverse
2883
3043
  };
2884
3044
 
2885
- function insertNode(board, node, path) {
2886
- const operation = { type: 'insert_node', node, path };
2887
- board.apply(operation);
2888
- }
2889
- function setNode(board, props, path) {
2890
- const properties = {};
2891
- const newProperties = {};
2892
- const node = PlaitNode.get(board, path);
2893
- for (const k in props) {
2894
- if (node[k] !== props[k]) {
2895
- if (node.hasOwnProperty(k)) {
2896
- properties[k] = node[k];
2897
- }
2898
- if (props[k] != null)
2899
- newProperties[k] = props[k];
2900
- }
3045
+ const Point = {
3046
+ isEquals(point, otherPoint) {
3047
+ return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
3048
+ },
3049
+ isHorizontal(point, otherPoint, tolerance = 0) {
3050
+ return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
3051
+ },
3052
+ isOverHorizontal(points, tolerance = 0) {
3053
+ return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
3054
+ },
3055
+ isVertical(point, otherPoint, tolerance = 0) {
3056
+ return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
3057
+ },
3058
+ isOverVertical(points, tolerance = 0) {
3059
+ return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
3060
+ },
3061
+ isAlign(points, tolerance = 0) {
3062
+ return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
3063
+ },
3064
+ getOffsetX(point1, point2) {
3065
+ return point2[0] - point1[0];
3066
+ },
3067
+ getOffsetY(point1, point2) {
3068
+ return point2[1] - point1[1];
2901
3069
  }
2902
- const operation = { type: 'set_node', properties, newProperties, path };
2903
- board.apply(operation);
2904
- }
2905
- function removeNode(board, path) {
2906
- const node = PlaitNode.get(board, path);
2907
- const operation = { type: 'remove_node', path, node };
2908
- board.apply(operation);
2909
- }
2910
- function moveNode(board, path, newPath) {
2911
- const operation = { type: 'move_node', path, newPath };
2912
- board.apply(operation);
2913
- }
2914
- const NodeTransforms = {
2915
- insertNode,
2916
- setNode,
2917
- removeNode,
2918
- moveNode
2919
3070
  };
2920
3071
 
2921
- function withSelection(board) {
2922
- const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange } = board;
2923
- let start = null;
2924
- let end = null;
2925
- let selectionMovingG;
2926
- let selectionRectangleG;
2927
- let previousSelectedElements;
2928
- let isShift = false;
2929
- let isTextSelection = false;
2930
- board.pointerDown = (event) => {
2931
- if (!isShift && event.shiftKey) {
2932
- isShift = true;
2933
- }
2934
- if (isShift && !event.shiftKey) {
2935
- isShift = false;
2936
- }
2937
- const isHitText = !!(event.target instanceof Element && event.target.closest('.plait-richtext-container'));
2938
- isTextSelection = isHitText && PlaitBoard.hasBeenTextEditing(board);
2939
- // prevent text from being selected
2940
- if (event.shiftKey && !isTextSelection) {
2941
- event.preventDefault();
3072
+ const SAVING = new WeakMap();
3073
+ const MERGING = new WeakMap();
3074
+
3075
+ var ThemeColorMode;
3076
+ (function (ThemeColorMode) {
3077
+ ThemeColorMode["default"] = "default";
3078
+ ThemeColorMode["colorful"] = "colorful";
3079
+ ThemeColorMode["soft"] = "soft";
3080
+ ThemeColorMode["retro"] = "retro";
3081
+ ThemeColorMode["dark"] = "dark";
3082
+ ThemeColorMode["starry"] = "starry";
3083
+ })(ThemeColorMode || (ThemeColorMode = {}));
3084
+ const DefaultThemeColor = {
3085
+ mode: ThemeColorMode.default,
3086
+ boardBackground: '#ffffff',
3087
+ textColor: '#333333'
3088
+ };
3089
+ const ColorfulThemeColor = {
3090
+ mode: ThemeColorMode.colorful,
3091
+ boardBackground: '#ffffff',
3092
+ textColor: '#333333'
3093
+ };
3094
+ const SoftThemeColor = {
3095
+ mode: ThemeColorMode.soft,
3096
+ boardBackground: '#f5f5f5',
3097
+ textColor: '#333333'
3098
+ };
3099
+ const RetroThemeColor = {
3100
+ mode: ThemeColorMode.retro,
3101
+ boardBackground: '#f9f8ed',
3102
+ textColor: '#333333'
3103
+ };
3104
+ const DarkThemeColor = {
3105
+ mode: ThemeColorMode.dark,
3106
+ boardBackground: '#141414',
3107
+ textColor: '#FFFFFF'
3108
+ };
3109
+ const StarryThemeColor = {
3110
+ mode: ThemeColorMode.starry,
3111
+ boardBackground: '#0d2537',
3112
+ textColor: '#FFFFFF'
3113
+ };
3114
+ const ThemeColors = [
3115
+ DefaultThemeColor,
3116
+ ColorfulThemeColor,
3117
+ SoftThemeColor,
3118
+ RetroThemeColor,
3119
+ DarkThemeColor,
3120
+ StarryThemeColor
3121
+ ];
3122
+
3123
+ var Direction;
3124
+ (function (Direction) {
3125
+ Direction["left"] = "left";
3126
+ Direction["top"] = "top";
3127
+ Direction["right"] = "right";
3128
+ Direction["bottom"] = "bottom";
3129
+ })(Direction || (Direction = {}));
3130
+
3131
+ function getRectangleByElements(board, elements, recursion) {
3132
+ const rectanglesCornerPoints = [];
3133
+ const callback = (node) => {
3134
+ const nodeRectangle = board.getRectangle(node);
3135
+ if (nodeRectangle) {
3136
+ const cornerPoints = RectangleClient.getCornerPoints(nodeRectangle);
3137
+ const rotatedCornerPoints = rotatePoints(cornerPoints, RectangleClient.getCenterPoint(nodeRectangle), node.angle || 0);
3138
+ rectanglesCornerPoints.push(rotatedCornerPoints);
2942
3139
  }
2943
- const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2944
- const selectedElements = getSelectedElements(board);
2945
- const hitElement = getHitElementByPoint(board, point);
2946
- const hitSelectedElements = selectedElements.length > 1 ? getHitSelectedElements(board, point) : [];
2947
- const isHitTarget = hitElement || hitSelectedElements.length > 0;
2948
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
2949
- if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && !isHitTarget && options.isMultiple && !options.isDisabledSelect) {
2950
- preventTouchMove(board, event, true);
2951
- // start rectangle selection
2952
- start = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3140
+ else {
3141
+ console.error(`can not get rectangle of element:`, node);
2953
3142
  }
2954
- pointerDown(event);
2955
3143
  };
2956
- board.pointerMove = (event) => {
2957
- if (!isTextSelection) {
2958
- // prevent text from being selected
2959
- event.preventDefault();
3144
+ elements.forEach(element => {
3145
+ if (recursion) {
3146
+ depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
2960
3147
  }
2961
- if (start && PlaitBoard.isPointer(board, PlaitPointerType.selection)) {
2962
- const movedTarget = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2963
- const rectangle = RectangleClient.getRectangleByPoints([start, movedTarget]);
2964
- selectionMovingG?.remove();
2965
- if (Math.hypot(rectangle.width, rectangle.height) > PRESS_AND_MOVE_BUFFER || isSelectionMoving(board)) {
2966
- end = movedTarget;
2967
- throttleRAF(board, 'with-selection', () => {
2968
- if (start && end) {
2969
- Transforms.setSelection(board, { anchor: start, focus: end });
2970
- }
2971
- });
2972
- setSelectionMoving(board);
2973
- selectionMovingG = drawRectangle(board, rectangle, {
2974
- stroke: SELECTION_BORDER_COLOR,
2975
- strokeWidth: 1,
2976
- fill: SELECTION_FILL_COLOR,
2977
- fillStyle: 'solid'
2978
- });
2979
- PlaitBoard.getElementActiveHost(board).append(selectionMovingG);
2980
- }
3148
+ else {
3149
+ callback(element);
2981
3150
  }
2982
- pointerMove(event);
2983
- };
2984
- // handle the end of click select
2985
- board.pointerUp = (event) => {
2986
- const isSetSelectionPointer = PlaitBoard.isPointer(board, PlaitPointerType.selection) || PlaitBoard.isPointer(board, PlaitPointerType.hand);
2987
- const isSkip = !isMainPointer(event) || isDragging(board) || !isSetSelectionPointer;
2988
- if (isSkip) {
2989
- pointerUp(event);
2990
- return;
3151
+ });
3152
+ if (rectanglesCornerPoints.length > 0) {
3153
+ if (hasSameAngle(elements)) {
3154
+ const angle = getSelectionAngle(elements);
3155
+ return getRotatedBoundingRectangle(rectanglesCornerPoints, angle);
2991
3156
  }
2992
- const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2993
- const selection = { anchor: point, focus: point };
2994
- Transforms.setSelection(board, selection);
2995
- pointerUp(event);
2996
- };
2997
- board.globalPointerUp = (event) => {
2998
- if (start && end) {
2999
- selectionMovingG?.remove();
3000
- clearSelectionMoving(board);
3001
- Transforms.setSelection(board, { anchor: start, focus: end });
3157
+ else {
3158
+ const flatCornerPoints = rectanglesCornerPoints.reduce((acc, val) => {
3159
+ return acc.concat(val);
3160
+ }, []);
3161
+ return RectangleClient.getRectangleByPoints(flatCornerPoints);
3002
3162
  }
3003
- if (PlaitBoard.isFocus(board)) {
3004
- const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
3005
- const isInDocument = event.target instanceof Node && document.contains(event.target);
3006
- const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
3007
- // Clear selection when mouse board outside area
3008
- // The framework needs to determine whether the board is focused through selection
3009
- if (!isInBoard && !start && !isAttachedElement && isInDocument) {
3010
- Transforms.setSelection(board, null);
3011
- }
3163
+ }
3164
+ else {
3165
+ return {
3166
+ x: 0,
3167
+ y: 0,
3168
+ width: 0,
3169
+ height: 0
3170
+ };
3171
+ }
3172
+ }
3173
+ function getBoardRectangle(board) {
3174
+ return getRectangleByElements(board, board.children, true);
3175
+ }
3176
+ function getElementById(board, id, dataSource) {
3177
+ if (!dataSource) {
3178
+ dataSource = findElements(board, { match: element => true, recursion: element => true });
3179
+ }
3180
+ let element = dataSource.find(element => element.id === id);
3181
+ return element;
3182
+ }
3183
+ function findElements(board, options) {
3184
+ let elements = [];
3185
+ const isReverse = options.isReverse ?? true;
3186
+ depthFirstRecursion(board, node => {
3187
+ if (!PlaitBoard.isBoard(node) && options.match(node)) {
3188
+ elements.push(node);
3012
3189
  }
3013
- start = null;
3014
- end = null;
3015
- isTextSelection = false;
3016
- preventTouchMove(board, event, false);
3017
- globalPointerUp(event);
3018
- };
3019
- board.onChange = () => {
3020
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3021
- if (options.isDisabledSelect) {
3022
- clearSelectedElement(board);
3190
+ }, (value) => {
3191
+ if (PlaitBoard.isBoard(value)) {
3192
+ return true;
3023
3193
  }
3024
- // remove selected element if include
3025
- board.operations.forEach(op => {
3026
- if (op.type === 'remove_node') {
3027
- removeSelectedElement(board, op.node, true);
3028
- }
3029
- });
3030
- if (isHandleSelection(board) && isSetSelectionOperation(board)) {
3031
- try {
3032
- if (!isShift) {
3033
- selectionRectangleG?.remove();
3034
- }
3035
- const temporaryElements = getTemporaryElements(board);
3036
- let elements = temporaryElements ? temporaryElements : getHitElementsBySelection(board);
3037
- if (!options.isMultiple && elements.length > 1) {
3038
- elements = [elements[0]];
3039
- }
3040
- if (isShift) {
3041
- if (board.selection && Selection.isCollapsed(board.selection)) {
3042
- const newSelectedElements = [...getSelectedElements(board)];
3043
- elements.forEach(element => {
3044
- if (newSelectedElements.includes(element)) {
3045
- newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
3046
- }
3047
- else {
3048
- newSelectedElements.push(element);
3049
- }
3050
- });
3051
- cacheSelectedElements(board, newSelectedElements);
3052
- }
3053
- else {
3054
- const newSelectedElements = [...getSelectedElements(board)];
3055
- elements.forEach(element => {
3056
- if (!newSelectedElements.includes(element)) {
3057
- newSelectedElements.push(element);
3058
- }
3059
- });
3060
- cacheSelectedElements(board, newSelectedElements);
3061
- }
3062
- }
3063
- else {
3064
- const newSelectedElements = [...elements];
3065
- cacheSelectedElements(board, newSelectedElements);
3066
- }
3067
- const newElements = getSelectedElements(board);
3068
- previousSelectedElements = newElements;
3069
- deleteTemporaryElements(board);
3070
- if (!isSelectionMoving(board) && newElements.length > 1) {
3071
- selectionRectangleG?.remove();
3072
- selectionRectangleG = createSelectionRectangleG(board);
3073
- }
3074
- }
3075
- catch (error) {
3076
- console.error(error);
3077
- }
3194
+ else {
3195
+ return getIsRecursionFunc(board)(value) && options.recursion(value);
3078
3196
  }
3079
- onChange();
3080
- };
3081
- board.afterChange = () => {
3082
- if (isHandleSelection(board) && !isSetSelectionOperation(board)) {
3083
- try {
3084
- const currentSelectedElements = getSelectedElements(board);
3085
- if (currentSelectedElements.length && currentSelectedElements.length > 1) {
3086
- if (currentSelectedElements.length !== previousSelectedElements.length ||
3087
- currentSelectedElements.some((c, index) => c !== previousSelectedElements[index])) {
3088
- selectionRectangleG?.remove();
3089
- selectionRectangleG = createSelectionRectangleG(board);
3090
- previousSelectedElements = currentSelectedElements;
3091
- }
3197
+ }, isReverse);
3198
+ return elements;
3199
+ }
3200
+
3201
+ const PlaitBoard = {
3202
+ isBoard(value) {
3203
+ const cachedIsBoard = IS_BOARD_CACHE.get(value);
3204
+ if (cachedIsBoard !== undefined) {
3205
+ return cachedIsBoard;
3206
+ }
3207
+ const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
3208
+ IS_BOARD_CACHE.set(value, isBoard);
3209
+ return isBoard;
3210
+ },
3211
+ findPath(board, node) {
3212
+ const path = [];
3213
+ let child = node;
3214
+ while (true) {
3215
+ const parent = NODE_TO_PARENT.get(child);
3216
+ if (parent == null) {
3217
+ if (PlaitBoard.isBoard(child)) {
3218
+ return path;
3092
3219
  }
3093
3220
  else {
3094
- selectionRectangleG?.remove();
3221
+ break;
3095
3222
  }
3096
3223
  }
3097
- catch (error) {
3098
- console.error(error);
3224
+ const i = NODE_TO_INDEX.get(child);
3225
+ if (i == null) {
3226
+ break;
3099
3227
  }
3228
+ path.unshift(i);
3229
+ child = parent;
3100
3230
  }
3101
- afterChange();
3102
- };
3103
- board.setPluginOptions(PlaitPluginKey.withSelection, {
3104
- isMultiple: true,
3105
- isDisabledSelect: false
3106
- });
3107
- return board;
3108
- }
3109
- function isHandleSelection(board) {
3110
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3111
- return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
3112
- }
3113
- function isSetSelectionOperation(board) {
3114
- return !!board.operations.find(value => value.type === 'set_selection');
3115
- }
3116
- function getTemporaryElements(board) {
3117
- const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
3118
- if (ref) {
3119
- return ref.elements;
3120
- }
3121
- else {
3122
- return undefined;
3123
- }
3124
- }
3125
- function getTemporaryRef(board) {
3126
- return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
3127
- }
3128
- function deleteTemporaryElements(board) {
3129
- BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
3130
- }
3131
- function isSelectionMoving(board) {
3132
- return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
3133
- }
3134
- function setSelectionMoving(board) {
3135
- PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
3136
- BOARD_TO_IS_SELECTION_MOVING.set(board, true);
3137
- setDragging(board, true);
3138
- }
3139
- function clearSelectionMoving(board) {
3140
- PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
3141
- BOARD_TO_IS_SELECTION_MOVING.delete(board);
3142
- setDragging(board, false);
3143
- }
3144
- function createSelectionRectangleG(board) {
3145
- const elements = getSelectedElements(board);
3146
- const rectangle = getRectangleByElements(board, elements, false);
3147
- if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
3148
- const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
3149
- stroke: SELECTION_BORDER_COLOR,
3150
- strokeWidth: ACTIVE_STROKE_WIDTH,
3151
- fillStyle: 'solid'
3152
- });
3153
- selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
3154
- PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
3155
- return selectionRectangleG;
3156
- }
3157
- return null;
3158
- }
3159
-
3160
- function setSelection(board, selection) {
3161
- const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
3162
- board.apply(operation);
3163
- }
3164
- const SelectionTransforms = {
3165
- setSelection,
3166
- addSelectionWithTemporaryElements
3167
- };
3168
- function addSelectionWithTemporaryElements(board, elements) {
3169
- const timeoutId = setTimeout(() => {
3170
- setSelection(board, { anchor: [0, 0], focus: [0, 0] });
3171
- }, 0);
3172
- let ref = getTemporaryRef(board);
3173
- if (ref) {
3174
- clearTimeout(ref.timeoutId);
3175
- const currentElements = ref.elements;
3176
- ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
3177
- ref.timeoutId = timeoutId;
3178
- }
3179
- else {
3180
- BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
3231
+ throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
3232
+ },
3233
+ getHost(board) {
3234
+ return BOARD_TO_HOST.get(board);
3235
+ },
3236
+ getElementHost(board) {
3237
+ return BOARD_TO_ELEMENT_HOST.get(board)?.host;
3238
+ },
3239
+ getElementUpperHost(board) {
3240
+ return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
3241
+ },
3242
+ getElementActiveHost(board) {
3243
+ return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
3244
+ },
3245
+ getRoughSVG(board) {
3246
+ return BOARD_TO_ROUGH_SVG.get(board);
3247
+ },
3248
+ getComponent(board) {
3249
+ return BOARD_TO_COMPONENT.get(board);
3250
+ },
3251
+ getBoardContainer(board) {
3252
+ return BOARD_TO_ELEMENT_HOST.get(board)?.container;
3253
+ },
3254
+ getRectangle(board) {
3255
+ return getRectangleByElements(board, board.children, true);
3256
+ },
3257
+ getViewportContainer(board) {
3258
+ return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
3259
+ },
3260
+ isFocus(board) {
3261
+ return !!board.selection;
3262
+ },
3263
+ isReadonly(board) {
3264
+ return board.options.readonly;
3265
+ },
3266
+ hasBeenTextEditing(board) {
3267
+ return !!IS_TEXT_EDITABLE.get(board);
3268
+ },
3269
+ getPointer(board) {
3270
+ return board.pointer;
3271
+ },
3272
+ isPointer(board, pointer) {
3273
+ return board.pointer === pointer;
3274
+ },
3275
+ isInPointer(board, pointers) {
3276
+ const point = board.pointer;
3277
+ return pointers.includes(point);
3278
+ },
3279
+ getMovingPointInBoard(board) {
3280
+ return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
3281
+ },
3282
+ isMovingPointInBoard(board) {
3283
+ const point = BOARD_TO_MOVING_POINT.get(board);
3284
+ const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
3285
+ if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
3286
+ return true;
3287
+ }
3288
+ return false;
3289
+ },
3290
+ getThemeColors(board) {
3291
+ return (board.options.themeColors || ThemeColors);
3181
3292
  }
3182
- }
3183
-
3184
- const removeElements = (board, elements) => {
3185
- elements
3186
- .map(element => {
3187
- const path = PlaitBoard.findPath(board, element);
3188
- const ref = board.pathRef(path);
3189
- return () => {
3190
- removeNode(board, ref.current);
3191
- ref.unref();
3192
- removeSelectedElement(board, element, true);
3193
- };
3194
- })
3195
- .forEach(action => {
3196
- action();
3197
- });
3198
- };
3199
- const CoreTransforms = {
3200
- removeElements
3201
- };
3202
-
3203
- const Transforms = {
3204
- ...GeneralTransforms,
3205
- ...ViewportTransforms$1,
3206
- ...SelectionTransforms,
3207
- ...NodeTransforms
3208
3293
  };
3209
3294
 
3210
3295
  const PathRef = {
@@ -3487,6 +3572,274 @@ function withHandPointer(board) {
3487
3572
  return board;
3488
3573
  }
3489
3574
 
3575
+ function withSelection(board) {
3576
+ const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange } = board;
3577
+ let start = null;
3578
+ let end = null;
3579
+ let selectionMovingG;
3580
+ let selectionRectangleG;
3581
+ let previousSelectedElements;
3582
+ let isShift = false;
3583
+ let isTextSelection = false;
3584
+ board.pointerDown = (event) => {
3585
+ if (!isShift && event.shiftKey) {
3586
+ isShift = true;
3587
+ }
3588
+ if (isShift && !event.shiftKey) {
3589
+ isShift = false;
3590
+ }
3591
+ const isHitText = !!(event.target instanceof Element && event.target.closest('.plait-richtext-container'));
3592
+ isTextSelection = isHitText && PlaitBoard.hasBeenTextEditing(board);
3593
+ // prevent text from being selected
3594
+ if (event.shiftKey && !isTextSelection) {
3595
+ event.preventDefault();
3596
+ }
3597
+ const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3598
+ const selectedElements = getSelectedElements(board);
3599
+ const hitElement = getHitElementByPoint(board, point);
3600
+ const hitSelectedElements = selectedElements.length > 1 ? getHitSelectedElements(board, point) : [];
3601
+ const isHitTarget = hitElement || hitSelectedElements.length > 0;
3602
+ const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3603
+ if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && !isHitTarget && options.isMultiple && !options.isDisabledSelect) {
3604
+ preventTouchMove(board, event, true);
3605
+ // start rectangle selection
3606
+ start = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3607
+ }
3608
+ pointerDown(event);
3609
+ };
3610
+ board.pointerMove = (event) => {
3611
+ if (!isTextSelection) {
3612
+ // prevent text from being selected
3613
+ event.preventDefault();
3614
+ }
3615
+ if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && start) {
3616
+ const movedTarget = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3617
+ const rectangle = RectangleClient.getRectangleByPoints([start, movedTarget]);
3618
+ selectionMovingG?.remove();
3619
+ if (Math.hypot(rectangle.width, rectangle.height) > PRESS_AND_MOVE_BUFFER || isSelectionMoving(board)) {
3620
+ end = movedTarget;
3621
+ throttleRAF(board, 'with-selection', () => {
3622
+ if (start && end) {
3623
+ Transforms.setSelection(board, { anchor: start, focus: end });
3624
+ }
3625
+ });
3626
+ setSelectionMoving(board);
3627
+ selectionMovingG = drawRectangle(board, rectangle, {
3628
+ stroke: SELECTION_BORDER_COLOR,
3629
+ strokeWidth: 1,
3630
+ fill: SELECTION_FILL_COLOR,
3631
+ fillStyle: 'solid'
3632
+ });
3633
+ PlaitBoard.getElementActiveHost(board).append(selectionMovingG);
3634
+ }
3635
+ }
3636
+ pointerMove(event);
3637
+ };
3638
+ // handle the end of click select
3639
+ board.pointerUp = (event) => {
3640
+ const isSetSelectionPointer = PlaitBoard.isPointer(board, PlaitPointerType.selection) || PlaitBoard.isPointer(board, PlaitPointerType.hand);
3641
+ const isSkip = !isMainPointer(event) || isDragging(board) || !isSetSelectionPointer;
3642
+ if (isSkip) {
3643
+ pointerUp(event);
3644
+ return;
3645
+ }
3646
+ const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3647
+ const selection = { anchor: point, focus: point };
3648
+ Transforms.setSelection(board, selection);
3649
+ pointerUp(event);
3650
+ };
3651
+ board.globalPointerUp = (event) => {
3652
+ if (start && end) {
3653
+ selectionMovingG?.remove();
3654
+ clearSelectionMoving(board);
3655
+ Transforms.setSelection(board, { anchor: start, focus: end });
3656
+ }
3657
+ if (PlaitBoard.isFocus(board)) {
3658
+ const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
3659
+ const isInDocument = event.target instanceof Node && document.contains(event.target);
3660
+ const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
3661
+ // Clear selection when mouse board outside area
3662
+ // The framework needs to determine whether the board is focused through selection
3663
+ if (!isInBoard && !start && !isAttachedElement && isInDocument) {
3664
+ Transforms.setSelection(board, null);
3665
+ }
3666
+ }
3667
+ start = null;
3668
+ end = null;
3669
+ isTextSelection = false;
3670
+ preventTouchMove(board, event, false);
3671
+ globalPointerUp(event);
3672
+ };
3673
+ board.onChange = () => {
3674
+ const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3675
+ if (options.isDisabledSelect) {
3676
+ clearSelectedElement(board);
3677
+ }
3678
+ // remove selected element if include
3679
+ board.operations.forEach(op => {
3680
+ if (op.type === 'remove_node') {
3681
+ removeSelectedElement(board, op.node, true);
3682
+ }
3683
+ });
3684
+ if (isHandleSelection(board) && isSetSelectionOperation(board)) {
3685
+ try {
3686
+ if (!isShift) {
3687
+ selectionRectangleG?.remove();
3688
+ }
3689
+ const temporaryElements = getTemporaryElements(board);
3690
+ let elements = temporaryElements ? temporaryElements : getHitElementsBySelection(board);
3691
+ if (!options.isMultiple && elements.length > 1) {
3692
+ elements = [elements[0]];
3693
+ }
3694
+ const isHitElementWithGroup = elements.some(item => item.groupId);
3695
+ if (isShift) {
3696
+ const newSelectedElements = [...getSelectedElements(board)];
3697
+ if (board.selection && Selection.isCollapsed(board.selection)) {
3698
+ if (isHitElementWithGroup) {
3699
+ let pendingElements = [...elements];
3700
+ const hitElement = elements[0];
3701
+ const groups = getGroupByElement(board, hitElement, true);
3702
+ const selectedGroups = getSelectedGroups(board, groups);
3703
+ const elementsInHighestGroup = getElementsInGroup(board, groups[groups.length - 1], true);
3704
+ if (selectedGroups.length > 0) {
3705
+ if (selectedGroups.length > 1) {
3706
+ pendingElements = getElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
3707
+ }
3708
+ }
3709
+ else {
3710
+ if (!newSelectedElements.includes(hitElement)) {
3711
+ const selectedElementsInGroup = elementsInHighestGroup.filter(item => newSelectedElements.includes(item));
3712
+ // When partially selected elements belong to a group,
3713
+ // only select those elements along with the hit elements.
3714
+ if (selectedElementsInGroup.length) {
3715
+ pendingElements.push(...selectedElementsInGroup);
3716
+ }
3717
+ else {
3718
+ pendingElements = elementsInHighestGroup;
3719
+ }
3720
+ }
3721
+ else {
3722
+ pendingElements = [];
3723
+ }
3724
+ }
3725
+ elementsInHighestGroup.forEach(element => {
3726
+ if (newSelectedElements.includes(element)) {
3727
+ newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
3728
+ }
3729
+ });
3730
+ if (pendingElements.length) {
3731
+ newSelectedElements.push(...pendingElements);
3732
+ }
3733
+ }
3734
+ else {
3735
+ elements.forEach(element => {
3736
+ if (newSelectedElements.includes(element)) {
3737
+ newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
3738
+ }
3739
+ else {
3740
+ newSelectedElements.push(element);
3741
+ }
3742
+ });
3743
+ }
3744
+ cacheSelectedElements(board, newSelectedElements);
3745
+ }
3746
+ else {
3747
+ let newElements = [...elements];
3748
+ if (isHitElementWithGroup) {
3749
+ elements.forEach(item => {
3750
+ if (!item.groupId) {
3751
+ newElements.push(item);
3752
+ }
3753
+ else {
3754
+ newElements.push(...getElementsInGroupByElement(board, item));
3755
+ }
3756
+ });
3757
+ }
3758
+ newElements.forEach(element => {
3759
+ if (!newSelectedElements.includes(element)) {
3760
+ newSelectedElements.push(element);
3761
+ }
3762
+ });
3763
+ cacheSelectedElements(board, newSelectedElements);
3764
+ }
3765
+ }
3766
+ else {
3767
+ let newSelectedElements = [...elements];
3768
+ if (isHitElementWithGroup) {
3769
+ const isCollapsed = Selection.isCollapsed(board.selection);
3770
+ if (!isCollapsed) {
3771
+ newSelectedElements = [];
3772
+ elements.forEach(item => {
3773
+ if (!item.groupId) {
3774
+ newSelectedElements.push(item);
3775
+ }
3776
+ else {
3777
+ newSelectedElements.push(...getElementsInGroupByElement(board, item));
3778
+ }
3779
+ });
3780
+ }
3781
+ else {
3782
+ const hitElement = elements[0];
3783
+ const groups = getGroupByElement(board, hitElement, true);
3784
+ const selectedGroups = getSelectedGroups(board, groups);
3785
+ if (selectedGroups.length > 0) {
3786
+ if (selectedGroups.length > 1) {
3787
+ newSelectedElements = getElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
3788
+ }
3789
+ }
3790
+ else {
3791
+ newSelectedElements = getElementsInGroup(board, groups[groups.length - 1], true);
3792
+ }
3793
+ }
3794
+ }
3795
+ cacheSelectedElements(board, newSelectedElements);
3796
+ }
3797
+ const newElements = getSelectedElements(board);
3798
+ previousSelectedElements = newElements;
3799
+ deleteTemporaryElements(board);
3800
+ if (!isSelectionMoving(board)) {
3801
+ selectionRectangleG?.remove();
3802
+ if (newElements.length > 1) {
3803
+ selectionRectangleG = createSelectionRectangleG(board);
3804
+ }
3805
+ }
3806
+ }
3807
+ catch (error) {
3808
+ console.error(error);
3809
+ }
3810
+ }
3811
+ onChange();
3812
+ };
3813
+ board.afterChange = () => {
3814
+ if (isHandleSelection(board) && !isSetSelectionOperation(board)) {
3815
+ try {
3816
+ const currentSelectedElements = getSelectedElements(board);
3817
+ if (currentSelectedElements.length && currentSelectedElements.length > 1) {
3818
+ if (previousSelectedElements &&
3819
+ (currentSelectedElements.length !== previousSelectedElements.length ||
3820
+ currentSelectedElements.some((c, index) => c !== previousSelectedElements[index]))) {
3821
+ selectionRectangleG?.remove();
3822
+ selectionRectangleG = createSelectionRectangleG(board);
3823
+ previousSelectedElements = currentSelectedElements;
3824
+ }
3825
+ }
3826
+ else {
3827
+ selectionRectangleG?.remove();
3828
+ }
3829
+ }
3830
+ catch (error) {
3831
+ console.error(error);
3832
+ }
3833
+ }
3834
+ afterChange();
3835
+ };
3836
+ board.setPluginOptions(PlaitPluginKey.withSelection, {
3837
+ isMultiple: true,
3838
+ isDisabledSelect: false
3839
+ });
3840
+ return board;
3841
+ }
3842
+
3490
3843
  function withViewport(board) {
3491
3844
  const { onChange } = board;
3492
3845
  const throttleUpdate = debounce(() => {
@@ -3913,7 +4266,7 @@ function withMoving(board) {
3913
4266
  const targetElement = getHitElementByPoint(board, point, el => board.isMovable(el));
3914
4267
  if (targetElement) {
3915
4268
  startPoint = point;
3916
- activeElements = [targetElement];
4269
+ activeElements = getElementsInGroupByElement(board, targetElement);
3917
4270
  if (targetElements.length > 0) {
3918
4271
  addSelectionWithTemporaryElements(board, []);
3919
4272
  }
@@ -4083,17 +4436,17 @@ class PlaitIslandBaseComponent {
4083
4436
  markForCheck() {
4084
4437
  this.cdr.markForCheck();
4085
4438
  }
4086
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4087
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
4439
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitIslandBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4440
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.2.4", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
4088
4441
  }
4089
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
4442
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
4090
4443
  type: Directive,
4091
4444
  args: [{
4092
4445
  host: {
4093
4446
  class: 'plait-island-container'
4094
4447
  }
4095
4448
  }]
4096
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; } });
4449
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }] });
4097
4450
  class PlaitIslandPopoverBaseComponent {
4098
4451
  constructor(cdr) {
4099
4452
  this.cdr = cdr;
@@ -4119,17 +4472,17 @@ class PlaitIslandPopoverBaseComponent {
4119
4472
  this.subscription?.unsubscribe();
4120
4473
  this.islandOnDestroy();
4121
4474
  }
4122
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandPopoverBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4123
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 }); }
4475
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitIslandPopoverBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4476
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.2.4", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 }); }
4124
4477
  }
4125
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
4478
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
4126
4479
  type: Directive,
4127
4480
  args: [{
4128
4481
  host: {
4129
4482
  class: 'plait-island-popover-container'
4130
4483
  }
4131
4484
  }]
4132
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { board: [{
4485
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { board: [{
4133
4486
  type: Input
4134
4487
  }] } });
4135
4488
  const hasOnBoardChange = (value) => {
@@ -4217,10 +4570,10 @@ class PlaitContextService {
4217
4570
  removeUploadingFile(fileEntry) {
4218
4571
  this.uploadingFiles = this.uploadingFiles.filter(file => file.url !== fileEntry.url);
4219
4572
  }
4220
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4221
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService }); }
4573
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4574
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitContextService }); }
4222
4575
  }
4223
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService, decorators: [{
4576
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitContextService, decorators: [{
4224
4577
  type: Injectable
4225
4578
  }] });
4226
4579
 
@@ -4297,10 +4650,10 @@ class PlaitElementComponent {
4297
4650
  ngOnDestroy() {
4298
4651
  this.board.destroyElement(this.getContext());
4299
4652
  }
4300
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
4301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitElementComponent, isStandalone: true, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4653
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
4654
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.2.4", type: PlaitElementComponent, isStandalone: true, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4302
4655
  }
4303
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitElementComponent, decorators: [{
4656
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitElementComponent, decorators: [{
4304
4657
  type: Component,
4305
4658
  args: [{
4306
4659
  selector: 'plait-element',
@@ -4308,7 +4661,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
4308
4661
  changeDetection: ChangeDetectionStrategy.OnPush,
4309
4662
  standalone: true
4310
4663
  }]
4311
- }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.ViewContainerRef }]; }, propDecorators: { index: [{
4664
+ }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ViewContainerRef }], propDecorators: { index: [{
4312
4665
  type: Input
4313
4666
  }], element: [{
4314
4667
  type: Input
@@ -4336,8 +4689,8 @@ class PlaitChildrenElementComponent {
4336
4689
  this.parentG = PlaitBoard.getElementHost(this.board);
4337
4690
  }
4338
4691
  }
4339
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitChildrenElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4340
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitChildrenElementComponent, isStandalone: true, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
4692
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitChildrenElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4693
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.2.4", type: PlaitChildrenElementComponent, isStandalone: true, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
4341
4694
  <plait-element
4342
4695
  *ngFor="let item of parent.children; let index = index; trackBy: trackBy"
4343
4696
  [index]="index"
@@ -4349,7 +4702,7 @@ class PlaitChildrenElementComponent {
4349
4702
  ></plait-element>
4350
4703
  `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: PlaitElementComponent, selector: "plait-element", inputs: ["index", "element", "parent", "board", "effect", "parentG"] }] }); }
4351
4704
  }
4352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitChildrenElementComponent, decorators: [{
4705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitChildrenElementComponent, decorators: [{
4353
4706
  type: Component,
4354
4707
  args: [{
4355
4708
  selector: 'plait-children',
@@ -4367,7 +4720,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
4367
4720
  standalone: true,
4368
4721
  imports: [NgFor, PlaitElementComponent]
4369
4722
  }]
4370
- }], ctorParameters: function () { return []; }, propDecorators: { board: [{
4723
+ }], ctorParameters: () => [], propDecorators: { board: [{
4371
4724
  type: Input
4372
4725
  }], parent: [{
4373
4726
  type: Input
@@ -4377,6 +4730,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
4377
4730
  type: Input
4378
4731
  }] } });
4379
4732
 
4733
+ function withGroup(board) {
4734
+ const { pointerMove, globalPointerUp } = board;
4735
+ let groupRectangleG;
4736
+ board.pointerMove = (event) => {
4737
+ groupRectangleG?.remove();
4738
+ const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
4739
+ let selection = { anchor: point, focus: point };
4740
+ if (board.selection && !Selection.isCollapsed(board.selection)) {
4741
+ selection = board.selection;
4742
+ }
4743
+ const hitElements = getHitElementsBySelection(board, selection);
4744
+ if (hitElements.length) {
4745
+ groupRectangleG = createGroupRectangleG(board, hitElements);
4746
+ groupRectangleG && PlaitBoard.getElementActiveHost(board).append(groupRectangleG);
4747
+ }
4748
+ pointerMove(event);
4749
+ };
4750
+ board.globalPointerUp = (event) => {
4751
+ groupRectangleG?.remove();
4752
+ groupRectangleG = null;
4753
+ globalPointerUp(event);
4754
+ };
4755
+ return board;
4756
+ }
4757
+
4380
4758
  const ElementHostClass = 'element-host';
4381
4759
  const ElementUpperHostClass = 'element-upper-host';
4382
4760
  const ElementActiveHostClass = 'element-active-host';
@@ -4502,7 +4880,7 @@ class PlaitBoardComponent {
4502
4880
  initializeViewportOffset(this.board);
4503
4881
  }
4504
4882
  initializePlugins() {
4505
- let board = withHotkey(withHandPointer(withHistory(withSelection(withMoving(withBoard(withViewport(withOptions(createBoard(this.plaitValue, this.plaitOptions)))))))));
4883
+ let board = withHotkey(withHandPointer(withHistory(withSelection(withGroup(withMoving(withBoard(withViewport(withOptions(createBoard(this.plaitValue, this.plaitOptions))))))))));
4506
4884
  this.plaitPlugins.forEach(plugin => {
4507
4885
  board = plugin(board);
4508
4886
  });
@@ -4714,8 +5092,8 @@ class PlaitBoardComponent {
4714
5092
  this.updateIslands();
4715
5093
  });
4716
5094
  }
4717
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
4718
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitBoardComponent, isStandalone: true, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, descendants: true }], viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }, { propertyName: "viewportContainer", first: true, predicate: ["viewportContainer"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: `
5095
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
5096
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.2.4", type: PlaitBoardComponent, isStandalone: true, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, descendants: true }], viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }, { propertyName: "viewportContainer", first: true, predicate: ["viewportContainer"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: `
4719
5097
  <div class="viewport-container" #viewportContainer>
4720
5098
  <svg #svg width="100%" height="100%" style="position: relative;" class="board-host-svg">
4721
5099
  <g class="element-host"></g>
@@ -4727,7 +5105,7 @@ class PlaitBoardComponent {
4727
5105
  <ng-content></ng-content>
4728
5106
  `, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElementComponent, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4729
5107
  }
4730
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitBoardComponent, decorators: [{
5108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitBoardComponent, decorators: [{
4731
5109
  type: Component,
4732
5110
  args: [{
4733
5111
  selector: 'plait-board',
@@ -4747,7 +5125,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
4747
5125
  standalone: true,
4748
5126
  imports: [PlaitChildrenElementComponent]
4749
5127
  }]
4750
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }, { type: i0.ElementRef }, { type: i0.NgZone }]; }, propDecorators: { plaitValue: [{
5128
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }, { type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { plaitValue: [{
4751
5129
  type: Input
4752
5130
  }], plaitViewport: [{
4753
5131
  type: Input
@@ -4836,7 +5214,7 @@ function createMouseEvent(type, clientX = 0, clientY = 0, offsetX = 1, offsetY =
4836
5214
  const event = new MouseEvent(type, {
4837
5215
  bubbles: true,
4838
5216
  cancelable: true,
4839
- composed: true,
5217
+ composed: true, // Required for shadow DOM events.
4840
5218
  view: window,
4841
5219
  detail: 0,
4842
5220
  relatedTarget: null,
@@ -4875,7 +5253,7 @@ function createPointerEvent(type, clientX = 0, clientY = 0, offsetX, offsetY, op
4875
5253
  const event = new PointerEvent(type, {
4876
5254
  bubbles: true,
4877
5255
  cancelable: true,
4878
- composed: true,
5256
+ composed: true, // Required for shadow DOM events.
4879
5257
  view: window,
4880
5258
  clientX,
4881
5259
  clientY,
@@ -4915,7 +5293,7 @@ function createKeyboardEvent(type, keyCode = 0, key = '', modifiers = {}) {
4915
5293
  return new KeyboardEvent(type, {
4916
5294
  bubbles: true,
4917
5295
  cancelable: true,
4918
- composed: true,
5296
+ composed: true, // Required for shadow DOM events.
4919
5297
  view: window,
4920
5298
  keyCode: keyCode,
4921
5299
  key: key,
@@ -4952,5 +5330,5 @@ function createModModifierKeys() {
4952
5330
  * Generated bundle index. Do not edit.
4953
5331
  */
4954
5332
 
4955
- export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HIT_DISTANCE_BUFFER, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_DRAGGING, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_INDEX, NODE_TO_PARENT, NS, NUMPAD_DIVIDE, NUMPAD_EIGHT, NUMPAD_FIVE, NUMPAD_FOUR, NUMPAD_MINUS, NUMPAD_MULTIPLY, NUMPAD_NINE, NUMPAD_ONE, NUMPAD_PERIOD, NUMPAD_PLUS, NUMPAD_SEVEN, NUMPAD_SIX, NUMPAD_THREE, NUMPAD_TWO, NUMPAD_ZERO, NUM_CENTER, NUM_LOCK, O, ONE, OPEN_SQUARE_BRACKET, P, PAGE_DOWN, PAGE_UP, PATH_REFS, PAUSE, PERIOD, PLUS_SIGN, POINTER_BUTTON, PRESS_AND_MOVE_BUFFER, PRINT_SCREEN, Path, PlaitBoard, PlaitBoardComponent, PlaitChildrenElementComponent, PlaitContextService, PlaitElement, PlaitElementComponent, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RIGHT_ARROW, RectangleClient, ResizeCursorClass, RetroThemeColor, RgbaToHEX, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, WritableClipboardType, X, Y, Z, ZERO, addClipboardContext, addSelectedElement, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, calcNewViewBox, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createFakeEvent, createForeignObject, createG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createSelectionRectangleG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, findElements, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getEllipseTangentSlope, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByElements, getSelectedElements, getTargetElements, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isContextmenu, isDOMElement, isDOMNode, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedElement, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, normalizePoint, preventTouchMove, removeMovingElements, removeSelectedElement, rotate, scrollToRectangle, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectionMoving, setStrokeLinecap, setTransformRotate, shouldClear, shouldMerge, shouldSave, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
5333
+ export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HIT_DISTANCE_BUFFER, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_DRAGGING, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_INDEX, NODE_TO_PARENT, NS, NUMPAD_DIVIDE, NUMPAD_EIGHT, NUMPAD_FIVE, NUMPAD_FOUR, NUMPAD_MINUS, NUMPAD_MULTIPLY, NUMPAD_NINE, NUMPAD_ONE, NUMPAD_PERIOD, NUMPAD_PLUS, NUMPAD_SEVEN, NUMPAD_SIX, NUMPAD_THREE, NUMPAD_TWO, NUMPAD_ZERO, NUM_CENTER, NUM_LOCK, O, ONE, OPEN_SQUARE_BRACKET, P, PAGE_DOWN, PAGE_UP, PATH_REFS, PAUSE, PERIOD, PLUS_SIGN, POINTER_BUTTON, PRESS_AND_MOVE_BUFFER, PRINT_SCREEN, Path, PlaitBoard, PlaitBoardComponent, PlaitChildrenElementComponent, PlaitContextService, PlaitElement, PlaitElementComponent, PlaitGroupElement, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RIGHT_ARROW, RectangleClient, ResizeCursorClass, RetroThemeColor, RgbaToHEX, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, WritableClipboardType, X, Y, Z, ZERO, addClipboardContext, addGroup, addSelectedElement, approximately, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, calcNewViewBox, canAddGroup, canRemoveGroup, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createFakeEvent, createForeignObject, createG, createGroup, createGroupRectangleG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createSelectionRectangleG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, findElements, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getElementsInGroup, getElementsInGroupByElement, getEllipseTangentSlope, getGroupByElement, getHighestGroup, getHighestSelectedElements, getHighestSelectedGroup, getHighestSelectedGroups, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByElements, getRectangleByGroup, getRotatedBoundingRectangle, getSelectedElements, getSelectedGroups, getSelectedIsolatedElements, getSelectionAngle, getTargetElements, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hasSameAngle, hasSelectedElementsInSameGroup, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isContextmenu, isDOMElement, isDOMNode, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedAllElementsInGroup, isSelectedElement, isSelectedElementOrGroup, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, nonGroupInHighestSelectedElements, normalizePoint, preventTouchMove, removeGroup, removeMovingElements, removeSelectedElement, rotate, rotatePoints, scrollToRectangle, setAngleForG, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
4956
5334
  //# sourceMappingURL=plait-core.mjs.map