@plait/core 0.1.0 → 0.1.2

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 (47) hide show
  1. package/board/board.component.d.ts +4 -17
  2. package/board/board.component.interface.d.ts +0 -2
  3. package/constants/selection.d.ts +1 -0
  4. package/esm2020/board/board.component.interface.mjs +1 -1
  5. package/esm2020/board/board.component.mjs +49 -248
  6. package/esm2020/constants/selection.mjs +2 -0
  7. package/esm2020/core/element/element.component.mjs +2 -2
  8. package/esm2020/interfaces/board.mjs +11 -2
  9. package/esm2020/interfaces/viewport.mjs +1 -1
  10. package/esm2020/plugins/create-board.mjs +2 -1
  11. package/esm2020/plugins/with-hand.mjs +9 -6
  12. package/esm2020/plugins/with-moving.mjs +91 -0
  13. package/esm2020/plugins/with-selection.mjs +54 -13
  14. package/esm2020/plugins/with-viewport.mjs +11 -0
  15. package/esm2020/public-api.mjs +3 -1
  16. package/esm2020/transforms/selection.mjs +10 -2
  17. package/esm2020/utils/board.mjs +7 -3
  18. package/esm2020/utils/common.mjs +15 -0
  19. package/esm2020/utils/element.mjs +3 -3
  20. package/esm2020/utils/helper.mjs +14 -1
  21. package/esm2020/utils/index.mjs +4 -2
  22. package/esm2020/utils/moving-element.mjs +15 -0
  23. package/esm2020/utils/selected-element.mjs +14 -1
  24. package/esm2020/utils/viewport.mjs +170 -0
  25. package/esm2020/utils/weak-maps.mjs +5 -1
  26. package/fesm2015/plait-core.mjs +567 -562
  27. package/fesm2015/plait-core.mjs.map +1 -1
  28. package/fesm2020/plait-core.mjs +567 -559
  29. package/fesm2020/plait-core.mjs.map +1 -1
  30. package/interfaces/board.d.ts +4 -10
  31. package/interfaces/viewport.d.ts +2 -2
  32. package/package.json +1 -1
  33. package/plugins/with-moving.d.ts +2 -0
  34. package/plugins/with-selection.d.ts +6 -1
  35. package/plugins/with-viewport.d.ts +2 -0
  36. package/public-api.d.ts +2 -0
  37. package/transforms/selection.d.ts +3 -0
  38. package/utils/board.d.ts +1 -1
  39. package/utils/common.d.ts +1 -0
  40. package/utils/helper.d.ts +9 -0
  41. package/utils/index.d.ts +3 -1
  42. package/utils/moving-element.d.ts +5 -0
  43. package/utils/selected-element.d.ts +2 -0
  44. package/utils/viewport.d.ts +29 -0
  45. package/utils/weak-maps.d.ts +3 -0
  46. package/esm2020/utils/matrix.mjs +0 -170
  47. package/utils/matrix.d.ts +0 -82
@@ -4,13 +4,100 @@ import rough from 'roughjs/bin/rough';
4
4
  import { Subject, fromEvent } from 'rxjs';
5
5
  import { takeUntil, filter } from 'rxjs/operators';
6
6
  import produce, { createDraft, finishDraft, isDraft } from 'immer';
7
- import { isKeyHotkey, isHotkey } from 'is-hotkey';
7
+ import isHotkey$1, { isKeyHotkey, isHotkey } from 'is-hotkey';
8
8
  import * as i1 from '@angular/common';
9
9
  import { BrowserModule } from '@angular/platform-browser';
10
10
 
11
- const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
12
- const SCROLL_BAR_WIDTH = 20;
13
- const MAX_RADIUS = 16;
11
+ // record richtext type status
12
+ const FLUSHING = new WeakMap();
13
+ const IS_TEXT_EDITABLE = new WeakMap();
14
+ const BOARD_TO_ON_CHANGE = new WeakMap();
15
+ const BOARD_TO_COMPONENT = new WeakMap();
16
+ const BOARD_TO_ROUGH_SVG = new WeakMap();
17
+ const BOARD_TO_HOST = new WeakMap();
18
+ const BOARD_TO_ELEMENT_HOST = new WeakMap();
19
+ const BOARD_TO_SELECTED_ELEMENT = new WeakMap();
20
+ const BOARD_TO_MOVING_POINT = new WeakMap();
21
+ const BOARD_TO_IS_SELECTION_MOVING = new WeakMap();
22
+ // save no standard selected elements
23
+ const BOARD_TO_TEMPORARY_ELEMENTS = new WeakMap();
24
+ const BOARD_TO_MOVING_ELEMENT = new WeakMap();
25
+
26
+ function depthFirstRecursion(node, callback) {
27
+ var _a;
28
+ (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(child => {
29
+ depthFirstRecursion(child, callback);
30
+ });
31
+ callback(node);
32
+ }
33
+
34
+ function getRectangleByElements(board, elements, recursion) {
35
+ const boundaryBox = {
36
+ left: Number.MAX_VALUE,
37
+ top: Number.MAX_VALUE,
38
+ right: Number.NEGATIVE_INFINITY,
39
+ bottom: Number.NEGATIVE_INFINITY
40
+ };
41
+ const calcRectangleClient = (node) => {
42
+ const nodeRectangle = board.getRectangle(node);
43
+ if (nodeRectangle) {
44
+ boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
45
+ boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
46
+ boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
47
+ boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
48
+ }
49
+ };
50
+ elements.forEach(element => {
51
+ if (recursion) {
52
+ depthFirstRecursion(element, node => calcRectangleClient(node));
53
+ }
54
+ else {
55
+ calcRectangleClient(element);
56
+ }
57
+ });
58
+ return {
59
+ x: boundaryBox.left,
60
+ y: boundaryBox.top,
61
+ width: boundaryBox.right - boundaryBox.left,
62
+ height: boundaryBox.bottom - boundaryBox.top
63
+ };
64
+ }
65
+ function getBoardRectangle(board) {
66
+ return getRectangleByElements(board, board.children, true);
67
+ }
68
+
69
+ const PlaitBoard = {
70
+ getHost(board) {
71
+ return BOARD_TO_HOST.get(board);
72
+ },
73
+ getElementHost(board) {
74
+ return BOARD_TO_ELEMENT_HOST.get(board);
75
+ },
76
+ getRoughSVG(board) {
77
+ return BOARD_TO_ROUGH_SVG.get(board);
78
+ },
79
+ getComponent(board) {
80
+ return BOARD_TO_COMPONENT.get(board);
81
+ },
82
+ getBoardNativeElement(board) {
83
+ return PlaitBoard.getComponent(board).nativeElement;
84
+ },
85
+ getRectangle(board) {
86
+ return getRectangleByElements(board, board.children, true);
87
+ },
88
+ getViewportContainer(board) {
89
+ return PlaitBoard.getHost(board).parentElement;
90
+ },
91
+ isFocus(board) {
92
+ return !!board.selection;
93
+ },
94
+ isReadonly(board) {
95
+ return board.options.readonly;
96
+ },
97
+ hasBeenTextEditing(board) {
98
+ return !!IS_TEXT_EDITABLE.get(board);
99
+ }
100
+ };
14
101
 
15
102
  var PlaitPointerType;
16
103
  (function (PlaitPointerType) {
@@ -21,6 +108,19 @@ var PlaitPointerType;
21
108
  function isNullOrUndefined(value) {
22
109
  return value === null || value === undefined;
23
110
  }
111
+ /**
112
+ * 规范 point
113
+ * @param point
114
+ * @returns point
115
+ */
116
+ function normalizePoint(point) {
117
+ return Array.isArray(point)
118
+ ? {
119
+ x: point[0],
120
+ y: point[1]
121
+ }
122
+ : point;
123
+ }
24
124
 
25
125
  const Viewport = {
26
126
  isViewport: (value) => {
@@ -361,94 +461,71 @@ function setSelection(board, selection) {
361
461
  board.apply(operation);
362
462
  }
363
463
  const SelectionTransforms = {
364
- setSelection
464
+ setSelection,
465
+ setSelectionWithTemporaryElements
365
466
  };
467
+ function setSelectionWithTemporaryElements(board, elements) {
468
+ setTimeout(() => {
469
+ BOARD_TO_TEMPORARY_ELEMENTS.set(board, elements);
470
+ setSelection(board, { ranges: [] });
471
+ });
472
+ }
366
473
 
367
- function setViewport(board, viewport) {
474
+ function setViewport$1(board, viewport) {
368
475
  const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
369
476
  board.apply(operation);
370
477
  }
371
478
  const ViewportTransforms = {
372
- setViewport
479
+ setViewport: setViewport$1
373
480
  };
374
481
 
375
- // record richtext type status
376
- const FLUSHING = new WeakMap();
377
- const IS_TEXT_EDITABLE = new WeakMap();
378
- const BOARD_TO_ON_CHANGE = new WeakMap();
379
- const BOARD_TO_COMPONENT = new WeakMap();
380
- const BOARD_TO_ROUGH_SVG = new WeakMap();
381
- const BOARD_TO_HOST = new WeakMap();
382
- const BOARD_TO_ELEMENT_HOST = new WeakMap();
383
- const BOARD_TO_SELECTED_ELEMENT = new WeakMap();
384
- const BOARD_TO_MOVING_POINT = new WeakMap();
385
-
386
- function depthFirstRecursion(node, callback) {
387
- var _a;
388
- (_a = node.children) === null || _a === void 0 ? void 0 : _a.forEach(child => {
389
- depthFirstRecursion(child, callback);
390
- });
391
- callback(node);
482
+ // https://stackoverflow.com/a/6853926/232122
483
+ function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
484
+ const A = x - x1;
485
+ const B = y - y1;
486
+ const C = x2 - x1;
487
+ const D = y2 - y1;
488
+ const dot = A * C + B * D;
489
+ const lenSquare = C * C + D * D;
490
+ let param = -1;
491
+ if (lenSquare !== 0) {
492
+ // in case of 0 length line
493
+ param = dot / lenSquare;
494
+ }
495
+ let xx, yy;
496
+ if (param < 0) {
497
+ xx = x1;
498
+ yy = y1;
499
+ }
500
+ else if (param > 1) {
501
+ xx = x2;
502
+ yy = y2;
503
+ }
504
+ else {
505
+ xx = x1 + param * C;
506
+ yy = y1 + param * D;
507
+ }
508
+ const dx = x - xx;
509
+ const dy = y - yy;
510
+ return Math.hypot(dx, dy);
392
511
  }
393
-
394
- function getRectangleByElements(board, elements, recursion) {
395
- const boundaryBox = {
396
- left: Number.MAX_VALUE,
397
- top: Number.MAX_VALUE,
398
- right: Number.MIN_VALUE,
399
- bottom: Number.MIN_VALUE
400
- };
401
- const calcRectangleClient = (node) => {
402
- const nodeRectangle = board.getRectangle(node);
403
- if (nodeRectangle) {
404
- boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
405
- boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
406
- boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
407
- boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
408
- }
409
- };
410
- elements.forEach(element => {
411
- if (recursion) {
412
- depthFirstRecursion(element, node => calcRectangleClient(node));
413
- }
414
- else {
415
- calcRectangleClient(element);
416
- }
417
- });
418
- return {
419
- x: boundaryBox.left,
420
- y: boundaryBox.top,
421
- width: boundaryBox.right - boundaryBox.left,
422
- height: boundaryBox.bottom - boundaryBox.top
423
- };
512
+ function rotate(x1, y1, x2, y2, angle) {
513
+ // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
514
+ // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
515
+ // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
516
+ return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
424
517
  }
425
- function getBoardRectangle(board) {
426
- return getRectangleByElements(board, board.children, true);
518
+ function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
519
+ const dx = x1 - x2;
520
+ const dy = y1 - y2;
521
+ return Math.hypot(dx, dy);
522
+ }
523
+ // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
524
+ function distanceBetweenPointAndRectangle(x, y, rect) {
525
+ var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
526
+ var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
527
+ return Math.sqrt(dx * dx + dy * dy);
427
528
  }
428
-
429
- const PlaitBoard = {
430
- getHost(board) {
431
- return BOARD_TO_HOST.get(board);
432
- },
433
- getElementHost(board) {
434
- return BOARD_TO_ELEMENT_HOST.get(board);
435
- },
436
- getRoughSVG(board) {
437
- return BOARD_TO_ROUGH_SVG.get(board);
438
- },
439
- getComponent(board) {
440
- return BOARD_TO_COMPONENT.get(board);
441
- },
442
- getBoardNativeElement(board) {
443
- return PlaitBoard.getComponent(board).nativeElement;
444
- },
445
- getRectangle(board) {
446
- return getRectangleByElements(board, board.children, true);
447
- },
448
- getViewportContainer(board) {
449
- return PlaitBoard.getHost(board).parentElement;
450
- }
451
- };
452
529
 
453
530
  function transformPoints(board, points) {
454
531
  const newPoints = points.map(point => {
@@ -464,9 +541,11 @@ function transformPoint(board, point) {
464
541
  const newPoint = [x, y];
465
542
  return newPoint;
466
543
  }
467
- function isNoSelectionElement(e) {
468
- var _a;
469
- return (_a = e.target) === null || _a === void 0 ? void 0 : _a.closest('.plait-board-attached');
544
+ function isInPlaitBoard(board, x, y) {
545
+ const plaitBoardElement = PlaitBoard.getBoardNativeElement(board);
546
+ const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
547
+ const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
548
+ return distances === 0;
470
549
  }
471
550
 
472
551
  const NS = 'http://www.w3.org/2000/svg';
@@ -787,222 +866,6 @@ function idCreator(length = 5) {
787
866
  return key;
788
867
  }
789
868
 
790
- // https://stackoverflow.com/a/6853926/232122
791
- function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
792
- const A = x - x1;
793
- const B = y - y1;
794
- const C = x2 - x1;
795
- const D = y2 - y1;
796
- const dot = A * C + B * D;
797
- const lenSquare = C * C + D * D;
798
- let param = -1;
799
- if (lenSquare !== 0) {
800
- // in case of 0 length line
801
- param = dot / lenSquare;
802
- }
803
- let xx, yy;
804
- if (param < 0) {
805
- xx = x1;
806
- yy = y1;
807
- }
808
- else if (param > 1) {
809
- xx = x2;
810
- yy = y2;
811
- }
812
- else {
813
- xx = x1 + param * C;
814
- yy = y1 + param * D;
815
- }
816
- const dx = x - xx;
817
- const dy = y - yy;
818
- return Math.hypot(dx, dy);
819
- }
820
- function rotate(x1, y1, x2, y2, angle) {
821
- // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
822
- // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
823
- // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
824
- return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
825
- }
826
- function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
827
- const dx = x1 - x2;
828
- const dy = y1 - y2;
829
- return Math.hypot(dx, dy);
830
- }
831
- // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
832
- function distanceBetweenPointAndRectangle(x, y, rect) {
833
- var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
834
- var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
835
- return Math.sqrt(dx * dx + dy * dy);
836
- }
837
-
838
- /**
839
- * 逆矩阵
840
- * [a c e]
841
- * [b d f]
842
- * [0 0 1]
843
- * @param newMatrix 输出返回矩阵
844
- * @param matrix 新矩阵
845
- * @returns 逆矩阵
846
- */
847
- function invertMatrix(newMatrix, matrix) {
848
- const [n, r, a, i, o, c, l, s, u] = matrix;
849
- const determinant = u * o - c * s;
850
- const h = -u * i + c * l;
851
- const f = s * i - o * l;
852
- const product = n * determinant + r * h + a * f;
853
- if (!product) {
854
- return null;
855
- }
856
- const reciprocal = 1 / product;
857
- newMatrix[0] = determinant * reciprocal;
858
- newMatrix[1] = (-u * r + a * s) * reciprocal;
859
- newMatrix[2] = (c * r - a * o) * reciprocal;
860
- newMatrix[3] = h * reciprocal;
861
- newMatrix[4] = (u * n - a * l) * reciprocal;
862
- newMatrix[5] = (-c * n + a * i) * reciprocal;
863
- newMatrix[6] = f * reciprocal;
864
- newMatrix[7] = (-s * n + r * l) * reciprocal;
865
- newMatrix[8] = (o * n - r * i) * reciprocal;
866
- return newMatrix;
867
- }
868
- /**
869
- * 将视图坐标与反转矩阵相乘,以得到原始坐标
870
- * 使用给定的矩阵进行转换
871
- * 矩阵与向量乘法,3 维向量与3x3矩阵的乘积
872
- * [m11 m12 m13][v1]
873
- * [m21 m22 m23][v2]
874
- * [m31 m32 m33][v3]
875
- * @param out 输出结果向量
876
- * @param t 要转换的向量
877
- * @param n 矩阵转换
878
- * @returns [v1 * m11 + v2 * m12 + v3 * m13, v1 * m21 + v2 * m22 + v3 * m23, v1 * m31 + v2 * m32 + v3 * m33];
879
- */
880
- function transformMat3(out, vector, matrix) {
881
- out = [
882
- vector[0] * matrix[0] + vector[1] * matrix[3] + vector[2] * matrix[6],
883
- vector[0] * matrix[1] + vector[1] * matrix[4] + vector[2] * matrix[7],
884
- vector[0] * matrix[2] + vector[1] * matrix[5] + vector[2] * matrix[8]
885
- ];
886
- return out;
887
- }
888
- /**
889
- * 规范 point
890
- * @param point
891
- * @returns point
892
- */
893
- function normalizePoint(point) {
894
- return Array.isArray(point)
895
- ? {
896
- x: point[0],
897
- y: point[1]
898
- }
899
- : point;
900
- }
901
- /**
902
- * 将一个点坐标反转回它的原始坐标
903
- * @param point 表示要反转的点的视图坐标,它是一个长度为 2 的数组,存储点的 x 和 y 坐标
904
- * @param matrix 表示视图矩阵,是在视图中对图形进行缩放和平移时使用的矩阵
905
- * @returns 最终结果是一个长度为 3 的数组,存储点的 x,y 和 w 坐标(w 坐标是点的齐次坐标)
906
- */
907
- function invertViewportCoordinates(point, matrix) {
908
- const { x, y } = normalizePoint(point);
909
- const invertedMatrix = invertMatrix([], matrix);
910
- return transformMat3([], [x, y, 1], invertedMatrix);
911
- }
912
- function convertToViewportCoordinates(point, matrix) {
913
- const { x, y } = normalizePoint(point);
914
- return transformMat3([], [x, y, 1], matrix);
915
- }
916
- /**
917
- * 获取 contentContainer 的 clientBox
918
- * @param board
919
- * @returns
920
- */
921
- function getViewportContainerBox(board) {
922
- const { hideScrollbar } = board.options;
923
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
924
- const container = PlaitBoard.getViewportContainer(board);
925
- const containerRect = container.getBoundingClientRect();
926
- const x = containerRect.x || containerRect.left;
927
- const y = containerRect.y || containerRect.top;
928
- const width = containerRect.width - scrollBarWidth;
929
- const height = containerRect.height - scrollBarWidth;
930
- return {
931
- minX: x,
932
- minY: y,
933
- maxX: x + width,
934
- maxY: y + height,
935
- x,
936
- y,
937
- width,
938
- height
939
- };
940
- }
941
- /**
942
- * 获取 board.plait-board 的 clientBox
943
- * @param board
944
- * @returns
945
- */
946
- function getBoardClientBox(board) {
947
- const { hideScrollbar } = board.options;
948
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
949
- const viewportRect = PlaitBoard.getViewportContainer(board).getBoundingClientRect();
950
- return {
951
- width: viewportRect.width + scrollBarWidth,
952
- height: viewportRect.height + scrollBarWidth
953
- };
954
- }
955
- /**
956
- * 获取 rootGroup 相对于当前 svg 空间的最小矩阵坐标
957
- */
958
- function getRootGroupBBox(board, zoom) {
959
- const elementHost = PlaitBoard.getElementHost(board);
960
- const rootGroupBox = elementHost.getBBox();
961
- const viewportContainerBox = getViewportContainerBox(board);
962
- const containerWidth = viewportContainerBox.width / zoom;
963
- const containerHeight = viewportContainerBox.height / zoom;
964
- let left;
965
- let right;
966
- let top;
967
- let bottom;
968
- if (rootGroupBox.width < containerWidth) {
969
- const offsetX = rootGroupBox.x + rootGroupBox.width / 2;
970
- const containerX = containerWidth / 2;
971
- left = offsetX - containerX;
972
- right = offsetX + containerX;
973
- }
974
- else {
975
- left = rootGroupBox.x;
976
- right = rootGroupBox.x + rootGroupBox.width;
977
- }
978
- if (rootGroupBox.height < containerHeight) {
979
- const offsetY = rootGroupBox.y + rootGroupBox.height / 2;
980
- const containerY = containerHeight / 2;
981
- top = offsetY - containerY;
982
- bottom = offsetY + containerY;
983
- }
984
- else {
985
- top = rootGroupBox.y;
986
- bottom = rootGroupBox.y + rootGroupBox.height;
987
- }
988
- return {
989
- left,
990
- right,
991
- top,
992
- bottom
993
- };
994
- }
995
- /**
996
- * 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
997
- * @param zoom 缩放比
998
- * @param minZoom 最小缩放比
999
- * @param maxZoom 最大缩放比
1000
- * @returns 正确的缩放比
1001
- */
1002
- function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
1003
- return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
1004
- }
1005
-
1006
869
  const calcElementIntersectionSelection = (board) => {
1007
870
  const selectedElements = [];
1008
871
  depthFirstRecursion(board, node => {
@@ -1016,6 +879,19 @@ const calcElementIntersectionSelection = (board) => {
1016
879
  });
1017
880
  return selectedElements;
1018
881
  };
882
+ const isIntersectionElements = (board, elements, ranges) => {
883
+ let isIntersectionElements = false;
884
+ if (elements.length) {
885
+ elements.map(item => {
886
+ if (!isIntersectionElements) {
887
+ isIntersectionElements = ranges.some(range => {
888
+ return board.isIntersectionSelection(item, range);
889
+ });
890
+ }
891
+ });
892
+ }
893
+ return isIntersectionElements;
894
+ };
1019
895
  const cacheSelectedElements = (board, selectedElements) => {
1020
896
  BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
1021
897
  };
@@ -1036,6 +912,10 @@ const isSelectedElement = (board, element) => {
1036
912
  return !!selectedElements.find(value => value === element);
1037
913
  };
1038
914
 
915
+ const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
916
+ const SCROLL_BAR_WIDTH = 20;
917
+ const MAX_RADIUS = 16;
918
+
1039
919
  /**
1040
920
  * drawRoundRectangle
1041
921
  * @param rs RoughSVG
@@ -1088,6 +968,196 @@ function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 4
1088
968
  return [arrowLineLeft, arrowLineRight];
1089
969
  }
1090
970
 
971
+ function getViewportContainerRect(board) {
972
+ const { hideScrollbar } = board.options;
973
+ const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
974
+ const viewportRect = PlaitBoard.getBoardNativeElement(board).getBoundingClientRect();
975
+ return {
976
+ width: viewportRect.width + scrollBarWidth,
977
+ height: viewportRect.height + scrollBarWidth
978
+ };
979
+ }
980
+ function getElementHostBBox(board, zoom) {
981
+ const childrenRect = getRectangleByElements(board, board.children, true);
982
+ const viewportContainerRect = getViewportContainerRect(board);
983
+ const containerWidth = viewportContainerRect.width / zoom;
984
+ const containerHeight = viewportContainerRect.height / zoom;
985
+ let left;
986
+ let right;
987
+ let top;
988
+ let bottom;
989
+ if (childrenRect.width < containerWidth) {
990
+ const centerX = Math.ceil(childrenRect.x + childrenRect.width / 2);
991
+ const halfContainerWidth = Math.ceil(containerWidth / 2);
992
+ left = centerX - halfContainerWidth;
993
+ right = centerX + halfContainerWidth;
994
+ }
995
+ else {
996
+ left = childrenRect.x;
997
+ right = childrenRect.x + childrenRect.width;
998
+ }
999
+ if (childrenRect.height < containerHeight) {
1000
+ const centerY = Math.ceil(childrenRect.y + childrenRect.height / 2);
1001
+ const halfContainerHeight = Math.ceil(containerHeight / 2);
1002
+ top = centerY - halfContainerHeight;
1003
+ bottom = centerY + halfContainerHeight;
1004
+ }
1005
+ else {
1006
+ top = childrenRect.y;
1007
+ bottom = childrenRect.y + childrenRect.height;
1008
+ }
1009
+ return {
1010
+ left,
1011
+ right,
1012
+ top,
1013
+ bottom
1014
+ };
1015
+ }
1016
+ /**
1017
+ * 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
1018
+ * @param zoom 缩放比
1019
+ * @param minZoom 最小缩放比
1020
+ * @param maxZoom 最大缩放比
1021
+ * @returns 正确的缩放比
1022
+ */
1023
+ function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
1024
+ return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
1025
+ }
1026
+ function getViewBox(board, zoom) {
1027
+ const { hideScrollbar } = board.options;
1028
+ const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1029
+ const viewportContainerRect = getViewportContainerRect(board);
1030
+ const elementHostBBox = getElementHostBBox(board, zoom);
1031
+ const horizontalPadding = viewportContainerRect.width / 2;
1032
+ const verticalPadding = viewportContainerRect.height / 2;
1033
+ const viewportWidth = (elementHostBBox.right - elementHostBBox.left) * zoom + 2 * horizontalPadding + scrollBarWidth;
1034
+ const viewportHeight = (elementHostBBox.bottom - elementHostBBox.top) * zoom + 2 * verticalPadding + scrollBarWidth;
1035
+ const viewBox = [
1036
+ elementHostBBox.left - horizontalPadding / zoom,
1037
+ elementHostBBox.top - verticalPadding / zoom,
1038
+ viewportWidth / zoom,
1039
+ viewportHeight / zoom
1040
+ ];
1041
+ return viewBox;
1042
+ }
1043
+ function setSVGViewBox(board, viewBox) {
1044
+ const zoom = board.viewport.zoom;
1045
+ const hostElement = PlaitBoard.getHost(board);
1046
+ hostElement.style.display = 'block';
1047
+ hostElement.style.width = `${viewBox[2] * zoom}px`;
1048
+ hostElement.style.height = `${viewBox[3] * zoom}px`;
1049
+ if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
1050
+ hostElement.setAttribute('viewBox', viewBox.join(' '));
1051
+ }
1052
+ }
1053
+ function updateViewportContainerOffset(board, origination) {
1054
+ origination = origination !== null && origination !== void 0 ? origination : board.viewport.origination;
1055
+ if (!origination)
1056
+ return;
1057
+ const { zoom } = board.viewport;
1058
+ const viewBox = getViewBox(board, zoom);
1059
+ const scrollLeft = (origination[0] - viewBox[0]) * zoom;
1060
+ const scrollTop = (origination[1] - viewBox[1]) * zoom;
1061
+ setViewportContainerScroll(board, scrollLeft, scrollTop);
1062
+ }
1063
+ function setViewportContainerScroll(board, left, top) {
1064
+ const viewportContainer = PlaitBoard.getViewportContainer(board);
1065
+ viewportContainer.scrollLeft = Math.floor(left);
1066
+ viewportContainer.scrollTop = Math.floor(top);
1067
+ }
1068
+ function initializeViewport(board) {
1069
+ const zoom = board.viewport.zoom;
1070
+ const viewBox = getViewBox(board, zoom);
1071
+ setSVGViewBox(board, viewBox);
1072
+ }
1073
+ function initializeViewportContainerOffset(board) {
1074
+ var _a;
1075
+ if (!((_a = board.viewport) === null || _a === void 0 ? void 0 : _a.origination)) {
1076
+ const zoom = board.viewport.zoom;
1077
+ const viewportContainerBox = PlaitBoard.getBoardNativeElement(board).getBoundingClientRect();
1078
+ const viewBox = getViewBox(board, zoom);
1079
+ const centerX = viewBox[0] + viewBox[2] / 2;
1080
+ const centerY = viewBox[1] + viewBox[3] / 2;
1081
+ const origination = [centerX - viewportContainerBox.width / 2 / zoom, centerY - viewportContainerBox.height / 2 / zoom];
1082
+ updateViewportContainerOffset(board, origination);
1083
+ return;
1084
+ }
1085
+ updateViewportContainerOffset(board);
1086
+ }
1087
+ function setViewport(board, origination, zoom) {
1088
+ zoom = zoom !== null && zoom !== void 0 ? zoom : board.viewport.zoom;
1089
+ Transforms.setViewport(board, Object.assign(Object.assign({}, board.viewport), { zoom,
1090
+ origination }));
1091
+ }
1092
+ function changeZoom(board, newZoom, isCenter = true) {
1093
+ newZoom = clampZoomLevel(newZoom);
1094
+ const mousePoint = BOARD_TO_MOVING_POINT.get(board);
1095
+ const nativeElement = PlaitBoard.getBoardNativeElement(board);
1096
+ const rect = nativeElement.getBoundingClientRect();
1097
+ const viewportContainerRect = getViewportContainerRect(board);
1098
+ let focusPoint = [viewportContainerRect.width / 2, viewportContainerRect.height / 2];
1099
+ if (!isCenter && mousePoint && distanceBetweenPointAndRectangle(mousePoint[0], mousePoint[1], rect) === 0) {
1100
+ focusPoint = toPoint(mousePoint[0], mousePoint[1], nativeElement);
1101
+ }
1102
+ const { origination, zoom } = board.viewport;
1103
+ const centerX = origination[0] + focusPoint[0] / zoom;
1104
+ const centerY = origination[1] + focusPoint[1] / zoom;
1105
+ const viewBox = getViewBox(board, newZoom);
1106
+ const newOrigination = [centerX - focusPoint[0] / newZoom, centerY - focusPoint[1] / newZoom];
1107
+ setSVGViewBox(board, viewBox);
1108
+ setViewport(board, newOrigination, newZoom);
1109
+ }
1110
+ function fitViewport(board) {
1111
+ const viewportContainerRect = getViewportContainerRect(board);
1112
+ const rootGroupBox = getRectangleByElements(board, board.children, true);
1113
+ const zoom = board.viewport.zoom;
1114
+ const autoFitPadding = 8;
1115
+ const viewportWidth = viewportContainerRect.width - 2 * autoFitPadding;
1116
+ const viewportHeight = viewportContainerRect.height - 2 * autoFitPadding;
1117
+ let newZoom = zoom;
1118
+ if (viewportWidth < rootGroupBox.width || viewportHeight < rootGroupBox.height) {
1119
+ newZoom = Math.min(viewportWidth / rootGroupBox.width, viewportHeight / rootGroupBox.height);
1120
+ }
1121
+ else {
1122
+ newZoom = 1;
1123
+ }
1124
+ const viewBox = getViewBox(board, newZoom);
1125
+ const centerX = viewBox[0] + viewBox[2] / 2;
1126
+ const centerY = viewBox[1] + viewBox[3] / 2;
1127
+ const newOrigination = [centerX - viewportContainerRect.width / 2 / newZoom, centerY - viewportContainerRect.height / 2 / newZoom];
1128
+ setViewport(board, newOrigination, newZoom);
1129
+ }
1130
+ function scrollToRectangle(board, client) { }
1131
+
1132
+ let timerId = null;
1133
+ const throttleRAF = (fn) => {
1134
+ const scheduleFunc = () => {
1135
+ timerId = requestAnimationFrame(() => {
1136
+ timerId = null;
1137
+ fn();
1138
+ });
1139
+ };
1140
+ if (timerId !== null) {
1141
+ cancelAnimationFrame(timerId);
1142
+ timerId = null;
1143
+ }
1144
+ scheduleFunc();
1145
+ };
1146
+
1147
+ const getMovingElements = (board) => {
1148
+ return BOARD_TO_MOVING_ELEMENT.get(board) || [];
1149
+ };
1150
+ const addMovingElements = (board, elements) => {
1151
+ const movingElements = getMovingElements(board);
1152
+ cacheMovingElements(board, [...movingElements, ...elements]);
1153
+ };
1154
+ const removeMovingElements = (board) => {
1155
+ BOARD_TO_MOVING_ELEMENT.delete(board);
1156
+ };
1157
+ const cacheMovingElements = (board, elements) => {
1158
+ BOARD_TO_MOVING_ELEMENT.set(board, elements);
1159
+ };
1160
+
1091
1161
  const updatePointerType = (board, pointer) => {
1092
1162
  board.pointer = pointer;
1093
1163
  const boardComponent = BOARD_TO_COMPONENT.get(board);
@@ -1148,6 +1218,7 @@ function createBoard(children, options) {
1148
1218
  destroyElement: (context) => { },
1149
1219
  isWithinSelection: element => false,
1150
1220
  isIntersectionSelection: element => false,
1221
+ isMovable: element => false,
1151
1222
  getRectangle: element => null
1152
1223
  };
1153
1224
  return board;
@@ -1273,11 +1344,13 @@ function withHandPointer(board) {
1273
1344
  };
1274
1345
  board.mousemove = (event) => {
1275
1346
  if (board.pointer === PlaitPointerType.hand && board.selection && isMoving) {
1276
- const boardComponent = PlaitBoard.getComponent(board);
1277
- const left = event.x - plaitBoardMove.x;
1278
- const top = event.y - plaitBoardMove.y;
1279
- const { scrollLeft, scrollTop } = boardComponent.viewportState;
1280
- boardComponent.setScroll(scrollLeft - left, scrollTop - top);
1347
+ const viewportContainer = PlaitBoard.getViewportContainer(board);
1348
+ const left = viewportContainer.scrollLeft + (event.x - plaitBoardMove.x);
1349
+ const top = viewportContainer.scrollTop + (event.y - plaitBoardMove.y);
1350
+ const zoom = board.viewport.zoom;
1351
+ const viewBox = getViewBox(board, zoom);
1352
+ const origination = [left / zoom + viewBox[0], top / zoom + viewBox[1]];
1353
+ setViewport(board, origination);
1281
1354
  plaitBoardMove.x = event.x;
1282
1355
  plaitBoardMove.y = event.y;
1283
1356
  }
@@ -1310,6 +1383,8 @@ function withHandPointer(board) {
1310
1383
  return board;
1311
1384
  }
1312
1385
 
1386
+ const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
1387
+
1313
1388
  function withSelection(board) {
1314
1389
  const { mousedown, globalMousemove, globalMouseup, onChange } = board;
1315
1390
  let start = null;
@@ -1317,12 +1392,22 @@ function withSelection(board) {
1317
1392
  let selectionMovingG;
1318
1393
  let selectionOuterG;
1319
1394
  board.mousedown = (event) => {
1320
- selectionOuterG === null || selectionOuterG === void 0 ? void 0 : selectionOuterG.remove();
1321
1395
  if (event.button === 0) {
1322
1396
  start = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1323
1397
  }
1324
1398
  if (start) {
1325
- Transforms.setSelection(board, { ranges: [{ anchor: start, focus: start }] });
1399
+ const ranges = [{ anchor: start, focus: start }];
1400
+ const selectedElements = getSelectedElements(board);
1401
+ const intersectionSelectedElement = isIntersectionElements(board, selectedElements, ranges);
1402
+ if (intersectionSelectedElement) {
1403
+ start = null;
1404
+ }
1405
+ else {
1406
+ Transforms.setSelection(board, { ranges: ranges });
1407
+ if (calcElementIntersectionSelection(board).length) {
1408
+ start = null;
1409
+ }
1410
+ }
1326
1411
  }
1327
1412
  mousedown(event);
1328
1413
  };
@@ -1330,13 +1415,11 @@ function withSelection(board) {
1330
1415
  if (start) {
1331
1416
  const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1332
1417
  const { x, y, width, height } = RectangleClient.toRectangleClient([start, movedTarget]);
1418
+ selectionMovingG === null || selectionMovingG === void 0 ? void 0 : selectionMovingG.remove();
1333
1419
  if (Math.hypot(width, height) > 5) {
1334
1420
  end = movedTarget;
1335
- if (movedTarget) {
1336
- Transforms.setSelection(board, { ranges: [{ anchor: start, focus: start }] });
1337
- }
1338
- PlaitBoard.getBoardNativeElement(board).classList.add('selection-moving');
1339
- selectionMovingG === null || selectionMovingG === void 0 ? void 0 : selectionMovingG.remove();
1421
+ Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1422
+ setSelectionMoving(board);
1340
1423
  const rough = PlaitBoard.getRoughSVG(board);
1341
1424
  selectionMovingG = rough.rectangle(x, y, width, height, {
1342
1425
  stroke: SELECTION_BORDER_COLOR,
@@ -1351,8 +1434,18 @@ function withSelection(board) {
1351
1434
  };
1352
1435
  board.globalMouseup = (event) => {
1353
1436
  if (start && end) {
1354
- PlaitBoard.getBoardNativeElement(board).classList.remove('selection-moving');
1355
1437
  selectionMovingG === null || selectionMovingG === void 0 ? void 0 : selectionMovingG.remove();
1438
+ clearSelectionMoving(board);
1439
+ Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1440
+ }
1441
+ if (PlaitBoard.isFocus(board)) {
1442
+ const isInBoard = event.target instanceof Node && PlaitBoard.getBoardNativeElement(board).contains(event.target);
1443
+ const isAttachedElement = event.target instanceof HTMLElement && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
1444
+ // Clear selection when mouse board outside area
1445
+ // The framework needs to determine whether the board is focused through selection
1446
+ if (!isInBoard && !start && !isAttachedElement) {
1447
+ Transforms.setSelection(board, null);
1448
+ }
1356
1449
  }
1357
1450
  start = null;
1358
1451
  end = null;
@@ -1361,10 +1454,12 @@ function withSelection(board) {
1361
1454
  board.onChange = () => {
1362
1455
  // calc selected elements entry
1363
1456
  try {
1457
+ selectionOuterG === null || selectionOuterG === void 0 ? void 0 : selectionOuterG.remove();
1364
1458
  if (board.operations.find(value => value.type === 'set_selection')) {
1365
- const elementIds = calcElementIntersectionSelection(board);
1366
- cacheSelectedElements(board, elementIds);
1367
- const { x, y, width, height } = getRectangleByElements(board, elementIds, false);
1459
+ const temporaryElements = getTemporaryElements(board);
1460
+ const elements = temporaryElements ? temporaryElements : calcElementIntersectionSelection(board);
1461
+ cacheSelectedElements(board, elements);
1462
+ const { x, y, width, height } = getRectangleByElements(board, elements, false);
1368
1463
  if (width > 0 && height > 0) {
1369
1464
  const rough = PlaitBoard.getRoughSVG(board);
1370
1465
  selectionOuterG = rough.rectangle(x - 2, y - 2, width + 4, height + 4, {
@@ -1372,9 +1467,11 @@ function withSelection(board) {
1372
1467
  strokeWidth: 1,
1373
1468
  fillStyle: 'solid'
1374
1469
  });
1470
+ selectionOuterG.classList.add('selection-outer');
1375
1471
  PlaitBoard.getHost(board).append(selectionOuterG);
1376
1472
  }
1377
1473
  }
1474
+ deleteTemporaryElements(board);
1378
1475
  }
1379
1476
  catch (error) {
1380
1477
  console.error(error);
@@ -1383,6 +1480,33 @@ function withSelection(board) {
1383
1480
  };
1384
1481
  return board;
1385
1482
  }
1483
+ function getTemporaryElements(board) {
1484
+ return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
1485
+ }
1486
+ function deleteTemporaryElements(board) {
1487
+ BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
1488
+ }
1489
+ function isSelectionMoving(board) {
1490
+ return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
1491
+ }
1492
+ function setSelectionMoving(board) {
1493
+ PlaitBoard.getBoardNativeElement(board).classList.add('selection-moving');
1494
+ BOARD_TO_IS_SELECTION_MOVING.set(board, true);
1495
+ }
1496
+ function clearSelectionMoving(board) {
1497
+ PlaitBoard.getBoardNativeElement(board).classList.remove('selection-moving');
1498
+ BOARD_TO_IS_SELECTION_MOVING.delete(board);
1499
+ }
1500
+
1501
+ function withViewport(board) {
1502
+ const { onChange } = board;
1503
+ board.onChange = () => {
1504
+ initializeViewport(board);
1505
+ updateViewportContainerOffset(board);
1506
+ onChange();
1507
+ };
1508
+ return board;
1509
+ }
1386
1510
 
1387
1511
  class PlaitElementComponent {
1388
1512
  constructor(renderer2, viewContainerRef) {
@@ -1436,7 +1560,7 @@ class PlaitElementComponent {
1436
1560
  const current = {
1437
1561
  element: this.element,
1438
1562
  selection: this.selection,
1439
- board: this.board,
1563
+ board: this.board
1440
1564
  };
1441
1565
  if (this.context) {
1442
1566
  const previous = Object.assign({}, this.context);
@@ -1537,10 +1661,6 @@ class PlaitBoardComponent {
1537
1661
  this.elementRef = elementRef;
1538
1662
  this.hasInitialized = false;
1539
1663
  this.destroy$ = new Subject();
1540
- this.viewportState = {
1541
- zoom: 1,
1542
- autoFitPadding: 8
1543
- };
1544
1664
  this.plaitValue = [];
1545
1665
  this.plaitPlugins = [];
1546
1666
  this.plaitChange = new EventEmitter();
@@ -1549,10 +1669,6 @@ class PlaitBoardComponent {
1549
1669
  return index;
1550
1670
  };
1551
1671
  }
1552
- get isFocused() {
1553
- var _a;
1554
- return (_a = this.board) === null || _a === void 0 ? void 0 : _a.selection;
1555
- }
1556
1672
  get host() {
1557
1673
  return this.svg.nativeElement;
1558
1674
  }
@@ -1562,8 +1678,8 @@ class PlaitBoardComponent {
1562
1678
  get readonly() {
1563
1679
  return this.board.options.readonly;
1564
1680
  }
1565
- get focused() {
1566
- return this.isFocused;
1681
+ get isFocused() {
1682
+ return PlaitBoard.isFocus(this.board);
1567
1683
  }
1568
1684
  get nativeElement() {
1569
1685
  return this.elementRef.nativeElement;
@@ -1573,10 +1689,12 @@ class PlaitBoardComponent {
1573
1689
  const roughSVG = rough.svg(this.host, {
1574
1690
  options: { roughness: 0, strokeWidth: 1 }
1575
1691
  });
1692
+ this.roughSVG = roughSVG;
1576
1693
  this.initializePlugins();
1577
1694
  this.initializeEvents();
1578
1695
  this.viewportScrollListener();
1579
1696
  this.elementResizeListener();
1697
+ this.mouseLeaveListener();
1580
1698
  BOARD_TO_COMPONENT.set(this.board, this);
1581
1699
  BOARD_TO_ROUGH_SVG.set(this.board, roughSVG);
1582
1700
  BOARD_TO_HOST.set(this.board, this.host);
@@ -1593,13 +1711,19 @@ class PlaitBoardComponent {
1593
1711
  });
1594
1712
  this.hasInitialized = true;
1595
1713
  }
1714
+ mouseLeaveListener() {
1715
+ fromEvent(this.host, 'mouseleave')
1716
+ .pipe(takeUntil(this.destroy$))
1717
+ .subscribe((event) => {
1718
+ BOARD_TO_MOVING_POINT.delete(this.board);
1719
+ });
1720
+ }
1596
1721
  ngOnChanges(changes) {
1597
1722
  if (this.hasInitialized) {
1598
1723
  const valueChange = changes['plaitValue'];
1599
1724
  const options = changes['plaitOptions'];
1600
- if (valueChange) {
1725
+ if (valueChange)
1601
1726
  this.board.children = valueChange.currentValue;
1602
- }
1603
1727
  if (options)
1604
1728
  this.board.options = options.currentValue;
1605
1729
  this.cdr.markForCheck();
@@ -1608,21 +1732,17 @@ class PlaitBoardComponent {
1608
1732
  ngAfterViewInit() {
1609
1733
  this.plaitBoardInitialized.emit(this.board);
1610
1734
  this.initViewportContainer();
1611
- this.calcViewBox(this.viewportState.zoom);
1612
- this.initViewport();
1735
+ initializeViewport(this.board);
1736
+ initializeViewportContainerOffset(this.board);
1613
1737
  }
1614
1738
  initializePlugins() {
1615
- var _a, _b;
1616
- let board = withHandPointer(withHistory(withSelection(withBoard(createBoard(this.plaitValue, this.plaitOptions)))));
1739
+ let board = withHandPointer(withHistory(withSelection(withBoard(withViewport(createBoard(this.plaitValue, this.plaitOptions))))));
1617
1740
  this.plaitPlugins.forEach(plugin => {
1618
1741
  board = plugin(board);
1619
1742
  });
1620
1743
  this.board = board;
1621
1744
  if (this.plaitViewport) {
1622
1745
  this.board.viewport = this.plaitViewport;
1623
- this.updateViewportState({
1624
- zoom: (_b = (_a = this.plaitViewport) === null || _a === void 0 ? void 0 : _a.zoom) !== null && _b !== void 0 ? _b : 1
1625
- });
1626
1746
  }
1627
1747
  }
1628
1748
  initializeEvents() {
@@ -1658,34 +1778,34 @@ class PlaitBoardComponent {
1658
1778
  this.board.dblclick(event);
1659
1779
  });
1660
1780
  fromEvent(document, 'keydown')
1661
- .pipe(takeUntil(this.destroy$), filter(() => {
1662
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1663
- }))
1781
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1664
1782
  .subscribe((event) => {
1665
1783
  var _a;
1784
+ if (isHotkey$1(['mod+=', 'mod++'], { byKey: true })(event)) {
1785
+ event.preventDefault();
1786
+ this.zoomInHandle(false);
1787
+ }
1788
+ if (isHotkey$1('mod+-', { byKey: true })(event)) {
1789
+ this.zoomOutHandle();
1790
+ event.preventDefault();
1791
+ }
1666
1792
  (_a = this.board) === null || _a === void 0 ? void 0 : _a.keydown(event);
1667
1793
  });
1668
1794
  fromEvent(document, 'keyup')
1669
- .pipe(takeUntil(this.destroy$), filter(() => {
1670
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1671
- }))
1795
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1672
1796
  .subscribe((event) => {
1673
1797
  var _a;
1674
1798
  (_a = this.board) === null || _a === void 0 ? void 0 : _a.keyup(event);
1675
1799
  });
1676
1800
  fromEvent(document, 'copy')
1677
- .pipe(takeUntil(this.destroy$), filter(() => {
1678
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1679
- }))
1801
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1680
1802
  .subscribe((event) => {
1681
1803
  var _a;
1682
1804
  event.preventDefault();
1683
1805
  (_a = this.board) === null || _a === void 0 ? void 0 : _a.setFragment(event.clipboardData);
1684
1806
  });
1685
1807
  fromEvent(document, 'paste')
1686
- .pipe(takeUntil(this.destroy$), filter(() => {
1687
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection && !this.readonly;
1688
- }))
1808
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1689
1809
  .subscribe((clipboardEvent) => {
1690
1810
  const mousePoint = BOARD_TO_MOVING_POINT.get(this.board);
1691
1811
  const rect = this.nativeElement.getBoundingClientRect();
@@ -1695,9 +1815,7 @@ class PlaitBoardComponent {
1695
1815
  }
1696
1816
  });
1697
1817
  fromEvent(document, 'cut')
1698
- .pipe(takeUntil(this.destroy$), filter(() => {
1699
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1700
- }))
1818
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1701
1819
  .subscribe((event) => {
1702
1820
  var _a, _b;
1703
1821
  event.preventDefault();
@@ -1707,206 +1825,43 @@ class PlaitBoardComponent {
1707
1825
  }
1708
1826
  viewportScrollListener() {
1709
1827
  fromEvent(this.viewportContainer.nativeElement, 'scroll')
1710
- .pipe(takeUntil(this.destroy$), filter(() => {
1711
- return !!this.isFocused;
1712
- }))
1828
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused))
1713
1829
  .subscribe((event) => {
1714
1830
  const { scrollLeft, scrollTop } = event.target;
1715
- const left = this.viewportState.scrollLeft;
1716
- const top = this.viewportState.scrollTop;
1717
- if (Math.abs(left - scrollLeft) >= 1 || Math.abs(top - scrollTop) >= 1) {
1718
- this.setScrollLeft(scrollLeft);
1719
- this.setScrollTop(scrollTop);
1720
- this.setViewport();
1721
- }
1831
+ const zoom = this.board.viewport.zoom;
1832
+ const viewBox = getViewBox(this.board, zoom);
1833
+ const origination = [scrollLeft / zoom + viewBox[0], scrollTop / zoom + viewBox[1]];
1834
+ setViewport(this.board, origination);
1722
1835
  });
1723
1836
  }
1724
1837
  elementResizeListener() {
1725
1838
  this.resizeObserver = new ResizeObserver(() => {
1726
1839
  this.initViewportContainer();
1727
- this.calcViewBox(this.board.viewport.zoom);
1728
- this.updateViewBoxStyles();
1729
- this.updateViewportScrolling();
1730
- this.setViewport();
1731
1840
  });
1732
1841
  this.resizeObserver.observe(this.nativeElement);
1733
1842
  }
1734
- updateViewportState(state) {
1735
- this.viewportState = Object.assign(Object.assign({}, this.viewportState), state);
1736
- }
1737
1843
  initViewportContainer() {
1738
- const { width, height } = getBoardClientBox(this.board);
1844
+ const { width, height } = getViewportContainerRect(this.board);
1739
1845
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'width', `${width}px`);
1740
1846
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'height', `${height}px`);
1741
1847
  }
1742
- initViewport(viewport = this.board.viewport) {
1743
- var _a;
1744
- const originationCoord = viewport === null || viewport === void 0 ? void 0 : viewport.originationCoord;
1745
- const zoom = (_a = viewport === null || viewport === void 0 ? void 0 : viewport.zoom) !== null && _a !== void 0 ? _a : this.viewportState.zoom;
1746
- if (originationCoord) {
1747
- const matrix = this.getMatrix();
1748
- const [pointX, pointY] = invertViewportCoordinates([0, 0], matrix);
1749
- const scrollLeft = this.viewportState.scrollLeft;
1750
- const scrollTop = this.viewportState.scrollTop;
1751
- const left = scrollLeft + (originationCoord[0] - pointX) * zoom;
1752
- const top = scrollTop + (originationCoord[1] - pointY) * zoom;
1753
- this.setScroll(left, top);
1754
- }
1755
- else {
1756
- this.adaptHandle();
1757
- }
1758
- }
1759
- calcViewBox(zoom = this.viewportState.zoom) {
1760
- var _a;
1761
- zoom = clampZoomLevel(zoom);
1762
- const scrollBarWidth = ((_a = this.plaitOptions) === null || _a === void 0 ? void 0 : _a.hideScrollbar) ? SCROLL_BAR_WIDTH : 0;
1763
- const viewportContainerBox = getViewportContainerBox(this.board);
1764
- const groupBBox = getRootGroupBBox(this.board, zoom);
1765
- const horizontalPadding = viewportContainerBox.width / 2;
1766
- const verticalPadding = viewportContainerBox.height / 2;
1767
- const viewportWidth = (groupBBox.right - groupBBox.left) * zoom + 2 * horizontalPadding + scrollBarWidth;
1768
- const viewportHeight = (groupBBox.bottom - groupBBox.top) * zoom + 2 * verticalPadding + scrollBarWidth;
1769
- const viewBox = [
1770
- groupBBox.left - horizontalPadding / zoom,
1771
- groupBBox.top - verticalPadding / zoom,
1772
- viewportWidth / zoom,
1773
- viewportHeight / zoom
1774
- ];
1775
- const matrix = this.getMatrix();
1776
- let scrollLeft;
1777
- let scrollTop;
1778
- if (matrix.length > 0) {
1779
- // focusPoint
1780
- const focusX = viewportContainerBox.x + viewportContainerBox.width / 2;
1781
- const focusY = viewportContainerBox.y + viewportContainerBox.height / 2;
1782
- const viewportContainerPoint = [focusX - viewportContainerBox.x, focusY - viewportContainerBox.y, 1];
1783
- const point = invertViewportCoordinates([viewportContainerPoint[0], viewportContainerPoint[1]], matrix);
1784
- const newMatrix = [zoom, 0, 0, 0, zoom, 0, -zoom * viewBox[0], -zoom * viewBox[1], 1];
1785
- const newPoint = transformMat3([], point, newMatrix);
1786
- scrollLeft = newPoint[0] - viewportContainerPoint[0];
1787
- scrollTop = newPoint[1] - viewportContainerPoint[1];
1788
- }
1789
- else {
1790
- scrollLeft = horizontalPadding;
1791
- scrollTop = verticalPadding;
1792
- }
1793
- this.updateViewportState({
1794
- viewportWidth,
1795
- viewportHeight,
1796
- zoom,
1797
- viewBox
1798
- });
1799
- this.setScrollLeft(scrollLeft);
1800
- this.setScrollTop(scrollTop);
1801
- }
1802
- getMatrix() {
1803
- const { viewBox, zoom, scrollLeft, scrollTop } = this.viewportState;
1804
- if (scrollLeft >= 0 && scrollTop >= 0) {
1805
- return [zoom, 0, 0, 0, zoom, 0, -scrollLeft - zoom * viewBox[0], -scrollTop - zoom * viewBox[1], 1];
1806
- }
1807
- return [];
1808
- }
1809
- setScrollLeft(left) {
1810
- var _a;
1811
- const viewportContainerBox = getViewportContainerBox(this.board);
1812
- const scrollBarWidth = ((_a = this.plaitOptions) === null || _a === void 0 ? void 0 : _a.hideScrollbar) ? SCROLL_BAR_WIDTH : 0;
1813
- const { viewportWidth } = this.viewportState;
1814
- const width = viewportWidth - viewportContainerBox.width + scrollBarWidth;
1815
- this.viewportState.scrollLeft = left < 0 ? 0 : left > width ? width : left;
1816
- }
1817
- setScrollTop(top) {
1818
- var _a;
1819
- const viewportContainerBox = getViewportContainerBox(this.board);
1820
- const scrollBarWidth = ((_a = this.plaitOptions) === null || _a === void 0 ? void 0 : _a.hideScrollbar) ? SCROLL_BAR_WIDTH : 0;
1821
- const { viewportHeight } = this.viewportState;
1822
- const height = viewportHeight - viewportContainerBox.height + scrollBarWidth;
1823
- this.viewportState.scrollTop = top < 0 ? 0 : top > height ? height : top;
1824
- }
1825
- setScroll(left, top) {
1826
- this.setScrollLeft(left);
1827
- this.setScrollTop(top);
1828
- this.updateViewBoxStyles();
1829
- this.updateViewportScrolling();
1830
- this.setViewport();
1831
- }
1832
- updateViewBoxStyles() {
1833
- const { host, viewportState } = this;
1834
- const { viewportWidth, viewportHeight, viewBox } = viewportState;
1835
- this.renderer2.setStyle(host, 'display', 'block');
1836
- this.renderer2.setStyle(host, 'width', `${viewportWidth}px`);
1837
- this.renderer2.setStyle(host, 'height', `${viewportHeight}px`);
1838
- if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
1839
- this.renderer2.setAttribute(host, 'viewBox', viewBox.join(' '));
1840
- }
1841
- }
1842
- updateViewportScrolling() {
1843
- const { viewportContainer, viewportState } = this;
1844
- const { scrollLeft, scrollTop } = viewportState;
1845
- viewportContainer.nativeElement.scrollLeft = scrollLeft;
1846
- viewportContainer.nativeElement.scrollTop = scrollTop;
1847
- }
1848
- setViewport() {
1849
- var _a, _b;
1850
- const viewport = (_a = this.board) === null || _a === void 0 ? void 0 : _a.viewport;
1851
- const oldOriginationCoord = (_b = viewport === null || viewport === void 0 ? void 0 : viewport.originationCoord) !== null && _b !== void 0 ? _b : [];
1852
- const matrix = this.getMatrix();
1853
- const originationCoord = invertViewportCoordinates([0, 0], matrix);
1854
- if (!originationCoord.every((item, index) => item === oldOriginationCoord[index])) {
1855
- Transforms.setViewport(this.board, Object.assign(Object.assign({}, viewport), { zoom: this.viewportState.zoom, originationCoord }));
1856
- }
1857
- }
1858
1848
  adaptHandle() {
1859
- const containerBox = getViewportContainerBox(this.board);
1860
- const rootGroup = this.host.firstChild;
1861
- const matrix = this.getMatrix();
1862
- const rootGroupBox = rootGroup.getBBox();
1863
- const centerPoint = [containerBox.width / 2, containerBox.height / 2];
1864
- const rootGroupCenterX = rootGroupBox.x + rootGroupBox.width / 2;
1865
- const rootGroupCenterY = rootGroupBox.y + rootGroupBox.height / 2;
1866
- const transformedPoint = transformMat3([], [rootGroupCenterX, rootGroupCenterY, 1], matrix);
1867
- const offsetLeft = centerPoint[0] - transformedPoint[0];
1868
- const offsetTop = centerPoint[1] - transformedPoint[1];
1869
- const { autoFitPadding, zoom, scrollLeft, scrollTop } = this.viewportState;
1870
- const viewportWidth = containerBox.width - 2 * autoFitPadding;
1871
- const viewportHeight = containerBox.height - 2 * autoFitPadding;
1872
- let newZoom = zoom;
1873
- if (viewportWidth < rootGroupBox.width || viewportHeight < rootGroupBox.height) {
1874
- newZoom = Math.min(viewportWidth / rootGroupBox.width, viewportHeight / rootGroupBox.height);
1875
- }
1876
- else {
1877
- newZoom = 1;
1878
- }
1879
- this.setScrollLeft(scrollLeft - offsetLeft);
1880
- this.setScrollTop(scrollTop - offsetTop);
1881
- this.calcViewBox(newZoom);
1882
- this.updateViewBoxStyles();
1883
- this.updateViewportScrolling();
1884
- this.setViewport();
1849
+ fitViewport(this.board);
1885
1850
  }
1886
- zoomInHandle() {
1887
- this.calcViewBox(this.viewportState.zoom + 0.1);
1888
- this.updateViewBoxStyles();
1889
- this.updateViewportScrolling();
1890
- this.setViewport();
1851
+ zoomInHandle(isCenter = true) {
1852
+ changeZoom(this.board, this.board.viewport.zoom + 0.1, isCenter);
1891
1853
  }
1892
1854
  zoomOutHandle() {
1893
- this.calcViewBox(this.viewportState.zoom - 0.1);
1894
- this.updateViewBoxStyles();
1895
- this.updateViewportScrolling();
1896
- this.setViewport();
1855
+ changeZoom(this.board, this.board.viewport.zoom - 0.1);
1897
1856
  }
1898
1857
  resetZoomHandel() {
1899
- this.calcViewBox(1);
1900
- this.updateViewBoxStyles();
1901
- this.updateViewportScrolling();
1902
- this.setViewport();
1858
+ changeZoom(this.board, 1);
1903
1859
  }
1904
1860
  ngOnDestroy() {
1861
+ var _a;
1905
1862
  this.destroy$.next();
1906
1863
  this.destroy$.complete();
1907
- if (this.resizeObserver) {
1908
- this.resizeObserver.disconnect();
1909
- }
1864
+ this.resizeObserver && ((_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect());
1910
1865
  BOARD_TO_ROUGH_SVG.delete(this.board);
1911
1866
  BOARD_TO_COMPONENT.delete(this.board);
1912
1867
  BOARD_TO_ROUGH_SVG.delete(this.board);
@@ -1917,41 +1872,9 @@ class PlaitBoardComponent {
1917
1872
  markForCheck() {
1918
1873
  this.cdr.markForCheck();
1919
1874
  }
1920
- scrollToRectangle(client) {
1921
- var _a;
1922
- this.calcViewBox();
1923
- this.updateViewBoxStyles();
1924
- this.updateViewportScrolling();
1925
- this.setViewport();
1926
- const svgRect = this.host.getBoundingClientRect();
1927
- const viewportContainerBox = getViewportContainerBox(this.board);
1928
- if (svgRect.width > viewportContainerBox.width || svgRect.height > viewportContainerBox.height) {
1929
- const scrollBarWidth = ((_a = this.plaitOptions) === null || _a === void 0 ? void 0 : _a.hideScrollbar) ? SCROLL_BAR_WIDTH : 0;
1930
- const matrix = this.getMatrix();
1931
- const [nodePointX, nodePointY] = convertToViewportCoordinates([client.x, client.y], matrix);
1932
- const [fullNodePointX, fullNodePointY] = convertToViewportCoordinates([client.x + client.width, client.y + client.height], matrix);
1933
- let newLeft = this.viewportState.scrollLeft;
1934
- let newTop = this.viewportState.scrollTop;
1935
- if (nodePointX < 0) {
1936
- newLeft -= Math.abs(nodePointX);
1937
- }
1938
- if (nodePointX > 0 && fullNodePointX > viewportContainerBox.width) {
1939
- newLeft += fullNodePointX - viewportContainerBox.width + scrollBarWidth;
1940
- }
1941
- if (nodePointY < 0) {
1942
- newTop -= Math.abs(nodePointY);
1943
- }
1944
- if (nodePointY > 0 && fullNodePointY > viewportContainerBox.height) {
1945
- newTop += fullNodePointY - viewportContainerBox.height + scrollBarWidth;
1946
- }
1947
- if (newLeft !== this.viewportState.scrollLeft || newTop !== this.viewportState.scrollTop) {
1948
- this.setScroll(newLeft, newTop);
1949
- }
1950
- }
1951
- }
1952
1875
  }
1953
1876
  PlaitBoardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.11", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
1954
- PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.focused" } }, queries: [{ propertyName: "toolbarTemplateRef", first: true, predicate: ["plaitToolbar"], 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: `
1877
+ PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.11", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused" } }, queries: [{ propertyName: "toolbarTemplateRef", first: true, predicate: ["plaitToolbar"], 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: `
1955
1878
  <div class="viewport-container" #viewportContainer>
1956
1879
  <svg #svg width="100%" height="100%" style="position: relative;"><g class="element-host"></g></svg>
1957
1880
  <plait-element
@@ -2017,7 +1940,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2017
1940
  }], readonly: [{
2018
1941
  type: HostBinding,
2019
1942
  args: ['class.readonly']
2020
- }], focused: [{
1943
+ }], isFocused: [{
2021
1944
  type: HostBinding,
2022
1945
  args: ['class.focused']
2023
1946
  }], svg: [{
@@ -2115,6 +2038,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2115
2038
  }]
2116
2039
  }] });
2117
2040
 
2041
+ function withMoving(board) {
2042
+ const { mousedown, mousemove, globalMouseup, globalMousemove } = board;
2043
+ let offsetX = 0;
2044
+ let offsetY = 0;
2045
+ let isPreventDefault = false;
2046
+ let startPoint;
2047
+ let activeElements = [];
2048
+ board.mousedown = event => {
2049
+ const host = BOARD_TO_HOST.get(board);
2050
+ const point = transformPoint(board, toPoint(event.x, event.y, host));
2051
+ const ranges = [{ anchor: point, focus: point }];
2052
+ let movableElements = board.children.filter(item => PlaitElement.isElement(item) && board.isMovable(item));
2053
+ if (movableElements.length) {
2054
+ startPoint = point;
2055
+ const selectedRootElements = getSelectedElements(board).filter(item => movableElements.includes(item));
2056
+ const intersectionSelectedElement = isIntersectionElements(board, selectedRootElements, ranges);
2057
+ if (intersectionSelectedElement) {
2058
+ activeElements = selectedRootElements;
2059
+ }
2060
+ else {
2061
+ activeElements = movableElements.filter(item => ranges.some(range => {
2062
+ return board.isIntersectionSelection(item, range);
2063
+ }));
2064
+ }
2065
+ }
2066
+ mousedown(event);
2067
+ };
2068
+ board.mousemove = event => {
2069
+ if (startPoint && (activeElements === null || activeElements === void 0 ? void 0 : activeElements.length)) {
2070
+ if (!isPreventDefault) {
2071
+ isPreventDefault = true;
2072
+ }
2073
+ const host = BOARD_TO_HOST.get(board);
2074
+ const endPoint = transformPoint(board, toPoint(event.x, event.y, host));
2075
+ offsetX = endPoint[0] - startPoint[0];
2076
+ offsetY = endPoint[1] - startPoint[1];
2077
+ throttleRAF(() => {
2078
+ const currentElements = activeElements.map(activeElement => {
2079
+ const [x, y] = activeElement === null || activeElement === void 0 ? void 0 : activeElement.points[0];
2080
+ const index = board.children.findIndex(item => item.id === activeElement.id);
2081
+ Transforms.setNode(board, {
2082
+ points: [[x + offsetX, y + offsetY]]
2083
+ }, [index]);
2084
+ MERGING.set(board, true);
2085
+ return PlaitNode.get(board, [index]);
2086
+ });
2087
+ addMovingElements(board, currentElements);
2088
+ });
2089
+ }
2090
+ if (isPreventDefault) {
2091
+ // 阻止 move 过程中触发画布滚动行为
2092
+ event.preventDefault();
2093
+ }
2094
+ mousemove(event);
2095
+ };
2096
+ board.globalMousemove = event => {
2097
+ if (startPoint) {
2098
+ const inPliatBordElement = isInPlaitBoard(board, event.x, event.y);
2099
+ if (!inPliatBordElement) {
2100
+ cancelMove(board);
2101
+ }
2102
+ }
2103
+ globalMousemove(event);
2104
+ };
2105
+ board.globalMouseup = event => {
2106
+ isPreventDefault = false;
2107
+ if (startPoint) {
2108
+ cancelMove(board);
2109
+ }
2110
+ globalMouseup(event);
2111
+ };
2112
+ function cancelMove(board) {
2113
+ startPoint = null;
2114
+ offsetX = 0;
2115
+ offsetY = 0;
2116
+ activeElements = [];
2117
+ removeMovingElements(board);
2118
+ MERGING.set(board, false);
2119
+ }
2120
+ return board;
2121
+ }
2122
+
2118
2123
  /*
2119
2124
  * Public API Surface of plait
2120
2125
  */
@@ -2123,5 +2128,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2123
2128
  * Generated bundle index. Do not edit.
2124
2129
  */
2125
2130
 
2126
- export { BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_MOVING_POINT, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BoardTransforms, CLIP_BOARD_FORMAT_KEY, ELEMENT_TO_PLUGIN_COMPONENT, FLUSHING, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, MAX_RADIUS, MERGING, NS, Path, PlaitBoard, PlaitBoardComponent, PlaitElement, PlaitElementComponent, PlaitHistoryBoard, PlaitModule, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPointerType, PlaitToolbarComponent, RectangleClient, SAVING, SCROLL_BAR_WIDTH, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, Selection, Transforms, Viewport, addSelectedElement, arrowPoints, cacheSelectedElements, calcElementIntersectionSelection, clampZoomLevel, convertToViewportCoordinates, createG, createSVG, createText, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, drawArrow, drawRoundRectangle, getBoardClientBox, getBoardRectangle, getRectangleByElements, getRootGroupBBox, getSelectedElements, getViewportContainerBox, hasBeforeContextChange, hotkeys, idCreator, inverse, invertMatrix, invertViewportCoordinates, isNoSelectionElement, isNullOrUndefined, isSelectedElement, isSetViewportOperation, normalizePoint, removeSelectedElement, rotate, shouldClear, shouldMerge, shouldSave, toPoint, transformMat3, transformPoint, transformPoints };
2131
+ export { 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_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BoardTransforms, CLIP_BOARD_FORMAT_KEY, ELEMENT_TO_PLUGIN_COMPONENT, FLUSHING, IS_APPLE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_SAFARI, IS_TEXT_EDITABLE, MAX_RADIUS, MERGING, NS, Path, PlaitBoard, PlaitBoardComponent, PlaitElement, PlaitElementComponent, PlaitHistoryBoard, PlaitModule, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPointerType, PlaitToolbarComponent, RectangleClient, SAVING, SCROLL_BAR_WIDTH, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, Selection, Transforms, Viewport, addMovingElements, addSelectedElement, arrowPoints, cacheMovingElements, cacheSelectedElements, calcElementIntersectionSelection, changeZoom, clampZoomLevel, clearSelectionMoving, createG, createSVG, createText, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, drawArrow, drawRoundRectangle, fitViewport, getBoardRectangle, getElementHostBBox, getMovingElements, getRectangleByElements, getSelectedElements, getTemporaryElements, getViewBox, getViewportContainerRect, hasBeforeContextChange, hotkeys, idCreator, initializeViewport, initializeViewportContainerOffset, inverse, isInPlaitBoard, isIntersectionElements, isNullOrUndefined, isSelectedElement, isSelectionMoving, isSetViewportOperation, normalizePoint, removeMovingElements, removeSelectedElement, rotate, scrollToRectangle, setSVGViewBox, setSelectionMoving, setViewport, setViewportContainerScroll, shouldClear, shouldMerge, shouldSave, throttleRAF, toPoint, transformPoint, transformPoints, updateViewportContainerOffset, withMoving, withSelection };
2127
2132
  //# sourceMappingURL=plait-core.mjs.map