@plait/core 0.1.1 → 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 (45) 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 +43 -12
  14. package/esm2020/plugins/with-viewport.mjs +11 -0
  15. package/esm2020/public-api.mjs +2 -1
  16. package/esm2020/utils/board.mjs +7 -3
  17. package/esm2020/utils/common.mjs +15 -0
  18. package/esm2020/utils/element.mjs +3 -3
  19. package/esm2020/utils/helper.mjs +14 -1
  20. package/esm2020/utils/index.mjs +4 -2
  21. package/esm2020/utils/moving-element.mjs +15 -0
  22. package/esm2020/utils/selected-element.mjs +14 -1
  23. package/esm2020/utils/viewport.mjs +170 -0
  24. package/esm2020/utils/weak-maps.mjs +3 -1
  25. package/fesm2015/plait-core.mjs +548 -561
  26. package/fesm2015/plait-core.mjs.map +1 -1
  27. package/fesm2020/plait-core.mjs +548 -558
  28. package/fesm2020/plait-core.mjs.map +1 -1
  29. package/interfaces/board.d.ts +4 -10
  30. package/interfaces/viewport.d.ts +2 -2
  31. package/package.json +1 -1
  32. package/plugins/with-moving.d.ts +2 -0
  33. package/plugins/with-selection.d.ts +4 -1
  34. package/plugins/with-viewport.d.ts +2 -0
  35. package/public-api.d.ts +1 -0
  36. package/utils/board.d.ts +1 -1
  37. package/utils/common.d.ts +1 -0
  38. package/utils/helper.d.ts +9 -0
  39. package/utils/index.d.ts +3 -1
  40. package/utils/moving-element.d.ts +5 -0
  41. package/utils/selected-element.d.ts +2 -0
  42. package/utils/viewport.d.ts +29 -0
  43. package/utils/weak-maps.d.ts +2 -0
  44. package/esm2020/utils/matrix.mjs +0 -170
  45. package/utils/matrix.d.ts +0 -82
@@ -4,13 +4,99 @@ 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
+ node.children?.forEach(child => {
28
+ depthFirstRecursion(child, callback);
29
+ });
30
+ callback(node);
31
+ }
32
+
33
+ function getRectangleByElements(board, elements, recursion) {
34
+ const boundaryBox = {
35
+ left: Number.MAX_VALUE,
36
+ top: Number.MAX_VALUE,
37
+ right: Number.NEGATIVE_INFINITY,
38
+ bottom: Number.NEGATIVE_INFINITY
39
+ };
40
+ const calcRectangleClient = (node) => {
41
+ const nodeRectangle = board.getRectangle(node);
42
+ if (nodeRectangle) {
43
+ boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
44
+ boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
45
+ boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
46
+ boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
47
+ }
48
+ };
49
+ elements.forEach(element => {
50
+ if (recursion) {
51
+ depthFirstRecursion(element, node => calcRectangleClient(node));
52
+ }
53
+ else {
54
+ calcRectangleClient(element);
55
+ }
56
+ });
57
+ return {
58
+ x: boundaryBox.left,
59
+ y: boundaryBox.top,
60
+ width: boundaryBox.right - boundaryBox.left,
61
+ height: boundaryBox.bottom - boundaryBox.top
62
+ };
63
+ }
64
+ function getBoardRectangle(board) {
65
+ return getRectangleByElements(board, board.children, true);
66
+ }
67
+
68
+ const PlaitBoard = {
69
+ getHost(board) {
70
+ return BOARD_TO_HOST.get(board);
71
+ },
72
+ getElementHost(board) {
73
+ return BOARD_TO_ELEMENT_HOST.get(board);
74
+ },
75
+ getRoughSVG(board) {
76
+ return BOARD_TO_ROUGH_SVG.get(board);
77
+ },
78
+ getComponent(board) {
79
+ return BOARD_TO_COMPONENT.get(board);
80
+ },
81
+ getBoardNativeElement(board) {
82
+ return PlaitBoard.getComponent(board).nativeElement;
83
+ },
84
+ getRectangle(board) {
85
+ return getRectangleByElements(board, board.children, true);
86
+ },
87
+ getViewportContainer(board) {
88
+ return PlaitBoard.getHost(board).parentElement;
89
+ },
90
+ isFocus(board) {
91
+ return !!board.selection;
92
+ },
93
+ isReadonly(board) {
94
+ return board.options.readonly;
95
+ },
96
+ hasBeenTextEditing(board) {
97
+ return !!IS_TEXT_EDITABLE.get(board);
98
+ }
99
+ };
14
100
 
15
101
  var PlaitPointerType;
16
102
  (function (PlaitPointerType) {
@@ -21,6 +107,19 @@ var PlaitPointerType;
21
107
  function isNullOrUndefined(value) {
22
108
  return value === null || value === undefined;
23
109
  }
110
+ /**
111
+ * 规范 point
112
+ * @param point
113
+ * @returns point
114
+ */
115
+ function normalizePoint(point) {
116
+ return Array.isArray(point)
117
+ ? {
118
+ x: point[0],
119
+ y: point[1]
120
+ }
121
+ : point;
122
+ }
24
123
 
25
124
  const Viewport = {
26
125
  isViewport: (value) => {
@@ -355,19 +454,6 @@ const NodeTransforms = {
355
454
  moveNode
356
455
  };
357
456
 
358
- // record richtext type status
359
- const FLUSHING = new WeakMap();
360
- const IS_TEXT_EDITABLE = new WeakMap();
361
- const BOARD_TO_ON_CHANGE = new WeakMap();
362
- const BOARD_TO_COMPONENT = new WeakMap();
363
- const BOARD_TO_ROUGH_SVG = new WeakMap();
364
- const BOARD_TO_HOST = new WeakMap();
365
- const BOARD_TO_ELEMENT_HOST = new WeakMap();
366
- const BOARD_TO_SELECTED_ELEMENT = new WeakMap();
367
- const BOARD_TO_MOVING_POINT = new WeakMap();
368
- // save no standard selected elements
369
- const BOARD_TO_TEMPORARY_ELEMENTS = new WeakMap();
370
-
371
457
  function setSelection(board, selection) {
372
458
  const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
373
459
  board.apply(operation);
@@ -383,79 +469,61 @@ function setSelectionWithTemporaryElements(board, elements) {
383
469
  });
384
470
  }
385
471
 
386
- function setViewport(board, viewport) {
472
+ function setViewport$1(board, viewport) {
387
473
  const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
388
474
  board.apply(operation);
389
475
  }
390
476
  const ViewportTransforms = {
391
- setViewport
477
+ setViewport: setViewport$1
392
478
  };
393
479
 
394
- function depthFirstRecursion(node, callback) {
395
- node.children?.forEach(child => {
396
- depthFirstRecursion(child, callback);
397
- });
398
- callback(node);
480
+ // https://stackoverflow.com/a/6853926/232122
481
+ function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
482
+ const A = x - x1;
483
+ const B = y - y1;
484
+ const C = x2 - x1;
485
+ const D = y2 - y1;
486
+ const dot = A * C + B * D;
487
+ const lenSquare = C * C + D * D;
488
+ let param = -1;
489
+ if (lenSquare !== 0) {
490
+ // in case of 0 length line
491
+ param = dot / lenSquare;
492
+ }
493
+ let xx, yy;
494
+ if (param < 0) {
495
+ xx = x1;
496
+ yy = y1;
497
+ }
498
+ else if (param > 1) {
499
+ xx = x2;
500
+ yy = y2;
501
+ }
502
+ else {
503
+ xx = x1 + param * C;
504
+ yy = y1 + param * D;
505
+ }
506
+ const dx = x - xx;
507
+ const dy = y - yy;
508
+ return Math.hypot(dx, dy);
399
509
  }
400
-
401
- function getRectangleByElements(board, elements, recursion) {
402
- const boundaryBox = {
403
- left: Number.MAX_VALUE,
404
- top: Number.MAX_VALUE,
405
- right: Number.MIN_VALUE,
406
- bottom: Number.MIN_VALUE
407
- };
408
- const calcRectangleClient = (node) => {
409
- const nodeRectangle = board.getRectangle(node);
410
- if (nodeRectangle) {
411
- boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
412
- boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
413
- boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
414
- boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
415
- }
416
- };
417
- elements.forEach(element => {
418
- if (recursion) {
419
- depthFirstRecursion(element, node => calcRectangleClient(node));
420
- }
421
- else {
422
- calcRectangleClient(element);
423
- }
424
- });
425
- return {
426
- x: boundaryBox.left,
427
- y: boundaryBox.top,
428
- width: boundaryBox.right - boundaryBox.left,
429
- height: boundaryBox.bottom - boundaryBox.top
430
- };
510
+ function rotate(x1, y1, x2, y2, angle) {
511
+ // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
512
+ // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
513
+ // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
514
+ return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
431
515
  }
432
- function getBoardRectangle(board) {
433
- return getRectangleByElements(board, board.children, true);
516
+ function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
517
+ const dx = x1 - x2;
518
+ const dy = y1 - y2;
519
+ return Math.hypot(dx, dy);
520
+ }
521
+ // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
522
+ function distanceBetweenPointAndRectangle(x, y, rect) {
523
+ var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
524
+ var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
525
+ return Math.sqrt(dx * dx + dy * dy);
434
526
  }
435
-
436
- const PlaitBoard = {
437
- getHost(board) {
438
- return BOARD_TO_HOST.get(board);
439
- },
440
- getElementHost(board) {
441
- return BOARD_TO_ELEMENT_HOST.get(board);
442
- },
443
- getRoughSVG(board) {
444
- return BOARD_TO_ROUGH_SVG.get(board);
445
- },
446
- getComponent(board) {
447
- return BOARD_TO_COMPONENT.get(board);
448
- },
449
- getBoardNativeElement(board) {
450
- return PlaitBoard.getComponent(board).nativeElement;
451
- },
452
- getRectangle(board) {
453
- return getRectangleByElements(board, board.children, true);
454
- },
455
- getViewportContainer(board) {
456
- return PlaitBoard.getHost(board).parentElement;
457
- }
458
- };
459
527
 
460
528
  function transformPoints(board, points) {
461
529
  const newPoints = points.map(point => {
@@ -471,8 +539,11 @@ function transformPoint(board, point) {
471
539
  const newPoint = [x, y];
472
540
  return newPoint;
473
541
  }
474
- function isNoSelectionElement(e) {
475
- return e.target?.closest('.plait-board-attached');
542
+ function isInPlaitBoard(board, x, y) {
543
+ const plaitBoardElement = PlaitBoard.getBoardNativeElement(board);
544
+ const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
545
+ const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
546
+ return distances === 0;
476
547
  }
477
548
 
478
549
  const NS = 'http://www.w3.org/2000/svg';
@@ -813,222 +884,6 @@ function idCreator(length = 5) {
813
884
  return key;
814
885
  }
815
886
 
816
- // https://stackoverflow.com/a/6853926/232122
817
- function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
818
- const A = x - x1;
819
- const B = y - y1;
820
- const C = x2 - x1;
821
- const D = y2 - y1;
822
- const dot = A * C + B * D;
823
- const lenSquare = C * C + D * D;
824
- let param = -1;
825
- if (lenSquare !== 0) {
826
- // in case of 0 length line
827
- param = dot / lenSquare;
828
- }
829
- let xx, yy;
830
- if (param < 0) {
831
- xx = x1;
832
- yy = y1;
833
- }
834
- else if (param > 1) {
835
- xx = x2;
836
- yy = y2;
837
- }
838
- else {
839
- xx = x1 + param * C;
840
- yy = y1 + param * D;
841
- }
842
- const dx = x - xx;
843
- const dy = y - yy;
844
- return Math.hypot(dx, dy);
845
- }
846
- function rotate(x1, y1, x2, y2, angle) {
847
- // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
848
- // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
849
- // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
850
- return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
851
- }
852
- function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
853
- const dx = x1 - x2;
854
- const dy = y1 - y2;
855
- return Math.hypot(dx, dy);
856
- }
857
- // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
858
- function distanceBetweenPointAndRectangle(x, y, rect) {
859
- var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
860
- var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
861
- return Math.sqrt(dx * dx + dy * dy);
862
- }
863
-
864
- /**
865
- * 逆矩阵
866
- * [a c e]
867
- * [b d f]
868
- * [0 0 1]
869
- * @param newMatrix 输出返回矩阵
870
- * @param matrix 新矩阵
871
- * @returns 逆矩阵
872
- */
873
- function invertMatrix(newMatrix, matrix) {
874
- const [n, r, a, i, o, c, l, s, u] = matrix;
875
- const determinant = u * o - c * s;
876
- const h = -u * i + c * l;
877
- const f = s * i - o * l;
878
- const product = n * determinant + r * h + a * f;
879
- if (!product) {
880
- return null;
881
- }
882
- const reciprocal = 1 / product;
883
- newMatrix[0] = determinant * reciprocal;
884
- newMatrix[1] = (-u * r + a * s) * reciprocal;
885
- newMatrix[2] = (c * r - a * o) * reciprocal;
886
- newMatrix[3] = h * reciprocal;
887
- newMatrix[4] = (u * n - a * l) * reciprocal;
888
- newMatrix[5] = (-c * n + a * i) * reciprocal;
889
- newMatrix[6] = f * reciprocal;
890
- newMatrix[7] = (-s * n + r * l) * reciprocal;
891
- newMatrix[8] = (o * n - r * i) * reciprocal;
892
- return newMatrix;
893
- }
894
- /**
895
- * 将视图坐标与反转矩阵相乘,以得到原始坐标
896
- * 使用给定的矩阵进行转换
897
- * 矩阵与向量乘法,3 维向量与3x3矩阵的乘积
898
- * [m11 m12 m13][v1]
899
- * [m21 m22 m23][v2]
900
- * [m31 m32 m33][v3]
901
- * @param out 输出结果向量
902
- * @param t 要转换的向量
903
- * @param n 矩阵转换
904
- * @returns [v1 * m11 + v2 * m12 + v3 * m13, v1 * m21 + v2 * m22 + v3 * m23, v1 * m31 + v2 * m32 + v3 * m33];
905
- */
906
- function transformMat3(out, vector, matrix) {
907
- out = [
908
- vector[0] * matrix[0] + vector[1] * matrix[3] + vector[2] * matrix[6],
909
- vector[0] * matrix[1] + vector[1] * matrix[4] + vector[2] * matrix[7],
910
- vector[0] * matrix[2] + vector[1] * matrix[5] + vector[2] * matrix[8]
911
- ];
912
- return out;
913
- }
914
- /**
915
- * 规范 point
916
- * @param point
917
- * @returns point
918
- */
919
- function normalizePoint(point) {
920
- return Array.isArray(point)
921
- ? {
922
- x: point[0],
923
- y: point[1]
924
- }
925
- : point;
926
- }
927
- /**
928
- * 将一个点坐标反转回它的原始坐标
929
- * @param point 表示要反转的点的视图坐标,它是一个长度为 2 的数组,存储点的 x 和 y 坐标
930
- * @param matrix 表示视图矩阵,是在视图中对图形进行缩放和平移时使用的矩阵
931
- * @returns 最终结果是一个长度为 3 的数组,存储点的 x,y 和 w 坐标(w 坐标是点的齐次坐标)
932
- */
933
- function invertViewportCoordinates(point, matrix) {
934
- const { x, y } = normalizePoint(point);
935
- const invertedMatrix = invertMatrix([], matrix);
936
- return transformMat3([], [x, y, 1], invertedMatrix);
937
- }
938
- function convertToViewportCoordinates(point, matrix) {
939
- const { x, y } = normalizePoint(point);
940
- return transformMat3([], [x, y, 1], matrix);
941
- }
942
- /**
943
- * 获取 contentContainer 的 clientBox
944
- * @param board
945
- * @returns
946
- */
947
- function getViewportContainerBox(board) {
948
- const { hideScrollbar } = board.options;
949
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
950
- const container = PlaitBoard.getViewportContainer(board);
951
- const containerRect = container.getBoundingClientRect();
952
- const x = containerRect.x || containerRect.left;
953
- const y = containerRect.y || containerRect.top;
954
- const width = containerRect.width - scrollBarWidth;
955
- const height = containerRect.height - scrollBarWidth;
956
- return {
957
- minX: x,
958
- minY: y,
959
- maxX: x + width,
960
- maxY: y + height,
961
- x,
962
- y,
963
- width,
964
- height
965
- };
966
- }
967
- /**
968
- * 获取 board.plait-board 的 clientBox
969
- * @param board
970
- * @returns
971
- */
972
- function getBoardClientBox(board) {
973
- const { hideScrollbar } = board.options;
974
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
975
- const viewportRect = PlaitBoard.getViewportContainer(board).getBoundingClientRect();
976
- return {
977
- width: viewportRect.width + scrollBarWidth,
978
- height: viewportRect.height + scrollBarWidth
979
- };
980
- }
981
- /**
982
- * 获取 rootGroup 相对于当前 svg 空间的最小矩阵坐标
983
- */
984
- function getRootGroupBBox(board, zoom) {
985
- const elementHost = PlaitBoard.getElementHost(board);
986
- const rootGroupBox = elementHost.getBBox();
987
- const viewportContainerBox = getViewportContainerBox(board);
988
- const containerWidth = viewportContainerBox.width / zoom;
989
- const containerHeight = viewportContainerBox.height / zoom;
990
- let left;
991
- let right;
992
- let top;
993
- let bottom;
994
- if (rootGroupBox.width < containerWidth) {
995
- const offsetX = rootGroupBox.x + rootGroupBox.width / 2;
996
- const containerX = containerWidth / 2;
997
- left = offsetX - containerX;
998
- right = offsetX + containerX;
999
- }
1000
- else {
1001
- left = rootGroupBox.x;
1002
- right = rootGroupBox.x + rootGroupBox.width;
1003
- }
1004
- if (rootGroupBox.height < containerHeight) {
1005
- const offsetY = rootGroupBox.y + rootGroupBox.height / 2;
1006
- const containerY = containerHeight / 2;
1007
- top = offsetY - containerY;
1008
- bottom = offsetY + containerY;
1009
- }
1010
- else {
1011
- top = rootGroupBox.y;
1012
- bottom = rootGroupBox.y + rootGroupBox.height;
1013
- }
1014
- return {
1015
- left,
1016
- right,
1017
- top,
1018
- bottom
1019
- };
1020
- }
1021
- /**
1022
- * 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
1023
- * @param zoom 缩放比
1024
- * @param minZoom 最小缩放比
1025
- * @param maxZoom 最大缩放比
1026
- * @returns 正确的缩放比
1027
- */
1028
- function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
1029
- return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
1030
- }
1031
-
1032
887
  const calcElementIntersectionSelection = (board) => {
1033
888
  const selectedElements = [];
1034
889
  depthFirstRecursion(board, node => {
@@ -1041,6 +896,19 @@ const calcElementIntersectionSelection = (board) => {
1041
896
  });
1042
897
  return selectedElements;
1043
898
  };
899
+ const isIntersectionElements = (board, elements, ranges) => {
900
+ let isIntersectionElements = false;
901
+ if (elements.length) {
902
+ elements.map(item => {
903
+ if (!isIntersectionElements) {
904
+ isIntersectionElements = ranges.some(range => {
905
+ return board.isIntersectionSelection(item, range);
906
+ });
907
+ }
908
+ });
909
+ }
910
+ return isIntersectionElements;
911
+ };
1044
912
  const cacheSelectedElements = (board, selectedElements) => {
1045
913
  BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
1046
914
  };
@@ -1061,6 +929,10 @@ const isSelectedElement = (board, element) => {
1061
929
  return !!selectedElements.find(value => value === element);
1062
930
  };
1063
931
 
932
+ const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
933
+ const SCROLL_BAR_WIDTH = 20;
934
+ const MAX_RADIUS = 16;
935
+
1064
936
  /**
1065
937
  * drawRoundRectangle
1066
938
  * @param rs RoughSVG
@@ -1113,6 +985,198 @@ function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 4
1113
985
  return [arrowLineLeft, arrowLineRight];
1114
986
  }
1115
987
 
988
+ function getViewportContainerRect(board) {
989
+ const { hideScrollbar } = board.options;
990
+ const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
991
+ const viewportRect = PlaitBoard.getBoardNativeElement(board).getBoundingClientRect();
992
+ return {
993
+ width: viewportRect.width + scrollBarWidth,
994
+ height: viewportRect.height + scrollBarWidth
995
+ };
996
+ }
997
+ function getElementHostBBox(board, zoom) {
998
+ const childrenRect = getRectangleByElements(board, board.children, true);
999
+ const viewportContainerRect = getViewportContainerRect(board);
1000
+ const containerWidth = viewportContainerRect.width / zoom;
1001
+ const containerHeight = viewportContainerRect.height / zoom;
1002
+ let left;
1003
+ let right;
1004
+ let top;
1005
+ let bottom;
1006
+ if (childrenRect.width < containerWidth) {
1007
+ const centerX = Math.ceil(childrenRect.x + childrenRect.width / 2);
1008
+ const halfContainerWidth = Math.ceil(containerWidth / 2);
1009
+ left = centerX - halfContainerWidth;
1010
+ right = centerX + halfContainerWidth;
1011
+ }
1012
+ else {
1013
+ left = childrenRect.x;
1014
+ right = childrenRect.x + childrenRect.width;
1015
+ }
1016
+ if (childrenRect.height < containerHeight) {
1017
+ const centerY = Math.ceil(childrenRect.y + childrenRect.height / 2);
1018
+ const halfContainerHeight = Math.ceil(containerHeight / 2);
1019
+ top = centerY - halfContainerHeight;
1020
+ bottom = centerY + halfContainerHeight;
1021
+ }
1022
+ else {
1023
+ top = childrenRect.y;
1024
+ bottom = childrenRect.y + childrenRect.height;
1025
+ }
1026
+ return {
1027
+ left,
1028
+ right,
1029
+ top,
1030
+ bottom
1031
+ };
1032
+ }
1033
+ /**
1034
+ * 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
1035
+ * @param zoom 缩放比
1036
+ * @param minZoom 最小缩放比
1037
+ * @param maxZoom 最大缩放比
1038
+ * @returns 正确的缩放比
1039
+ */
1040
+ function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
1041
+ return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
1042
+ }
1043
+ function getViewBox(board, zoom) {
1044
+ const { hideScrollbar } = board.options;
1045
+ const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1046
+ const viewportContainerRect = getViewportContainerRect(board);
1047
+ const elementHostBBox = getElementHostBBox(board, zoom);
1048
+ const horizontalPadding = viewportContainerRect.width / 2;
1049
+ const verticalPadding = viewportContainerRect.height / 2;
1050
+ const viewportWidth = (elementHostBBox.right - elementHostBBox.left) * zoom + 2 * horizontalPadding + scrollBarWidth;
1051
+ const viewportHeight = (elementHostBBox.bottom - elementHostBBox.top) * zoom + 2 * verticalPadding + scrollBarWidth;
1052
+ const viewBox = [
1053
+ elementHostBBox.left - horizontalPadding / zoom,
1054
+ elementHostBBox.top - verticalPadding / zoom,
1055
+ viewportWidth / zoom,
1056
+ viewportHeight / zoom
1057
+ ];
1058
+ return viewBox;
1059
+ }
1060
+ function setSVGViewBox(board, viewBox) {
1061
+ const zoom = board.viewport.zoom;
1062
+ const hostElement = PlaitBoard.getHost(board);
1063
+ hostElement.style.display = 'block';
1064
+ hostElement.style.width = `${viewBox[2] * zoom}px`;
1065
+ hostElement.style.height = `${viewBox[3] * zoom}px`;
1066
+ if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
1067
+ hostElement.setAttribute('viewBox', viewBox.join(' '));
1068
+ }
1069
+ }
1070
+ function updateViewportContainerOffset(board, origination) {
1071
+ origination = origination ?? board.viewport.origination;
1072
+ if (!origination)
1073
+ return;
1074
+ const { zoom } = board.viewport;
1075
+ const viewBox = getViewBox(board, zoom);
1076
+ const scrollLeft = (origination[0] - viewBox[0]) * zoom;
1077
+ const scrollTop = (origination[1] - viewBox[1]) * zoom;
1078
+ setViewportContainerScroll(board, scrollLeft, scrollTop);
1079
+ }
1080
+ function setViewportContainerScroll(board, left, top) {
1081
+ const viewportContainer = PlaitBoard.getViewportContainer(board);
1082
+ viewportContainer.scrollLeft = Math.floor(left);
1083
+ viewportContainer.scrollTop = Math.floor(top);
1084
+ }
1085
+ function initializeViewport(board) {
1086
+ const zoom = board.viewport.zoom;
1087
+ const viewBox = getViewBox(board, zoom);
1088
+ setSVGViewBox(board, viewBox);
1089
+ }
1090
+ function initializeViewportContainerOffset(board) {
1091
+ if (!board.viewport?.origination) {
1092
+ const zoom = board.viewport.zoom;
1093
+ const viewportContainerBox = PlaitBoard.getBoardNativeElement(board).getBoundingClientRect();
1094
+ const viewBox = getViewBox(board, zoom);
1095
+ const centerX = viewBox[0] + viewBox[2] / 2;
1096
+ const centerY = viewBox[1] + viewBox[3] / 2;
1097
+ const origination = [centerX - viewportContainerBox.width / 2 / zoom, centerY - viewportContainerBox.height / 2 / zoom];
1098
+ updateViewportContainerOffset(board, origination);
1099
+ return;
1100
+ }
1101
+ updateViewportContainerOffset(board);
1102
+ }
1103
+ function setViewport(board, origination, zoom) {
1104
+ zoom = zoom ?? board.viewport.zoom;
1105
+ Transforms.setViewport(board, {
1106
+ ...board.viewport,
1107
+ zoom,
1108
+ origination
1109
+ });
1110
+ }
1111
+ function changeZoom(board, newZoom, isCenter = true) {
1112
+ newZoom = clampZoomLevel(newZoom);
1113
+ const mousePoint = BOARD_TO_MOVING_POINT.get(board);
1114
+ const nativeElement = PlaitBoard.getBoardNativeElement(board);
1115
+ const rect = nativeElement.getBoundingClientRect();
1116
+ const viewportContainerRect = getViewportContainerRect(board);
1117
+ let focusPoint = [viewportContainerRect.width / 2, viewportContainerRect.height / 2];
1118
+ if (!isCenter && mousePoint && distanceBetweenPointAndRectangle(mousePoint[0], mousePoint[1], rect) === 0) {
1119
+ focusPoint = toPoint(mousePoint[0], mousePoint[1], nativeElement);
1120
+ }
1121
+ const { origination, zoom } = board.viewport;
1122
+ const centerX = origination[0] + focusPoint[0] / zoom;
1123
+ const centerY = origination[1] + focusPoint[1] / zoom;
1124
+ const viewBox = getViewBox(board, newZoom);
1125
+ const newOrigination = [centerX - focusPoint[0] / newZoom, centerY - focusPoint[1] / newZoom];
1126
+ setSVGViewBox(board, viewBox);
1127
+ setViewport(board, newOrigination, newZoom);
1128
+ }
1129
+ function fitViewport(board) {
1130
+ const viewportContainerRect = getViewportContainerRect(board);
1131
+ const rootGroupBox = getRectangleByElements(board, board.children, true);
1132
+ const zoom = board.viewport.zoom;
1133
+ const autoFitPadding = 8;
1134
+ const viewportWidth = viewportContainerRect.width - 2 * autoFitPadding;
1135
+ const viewportHeight = viewportContainerRect.height - 2 * autoFitPadding;
1136
+ let newZoom = zoom;
1137
+ if (viewportWidth < rootGroupBox.width || viewportHeight < rootGroupBox.height) {
1138
+ newZoom = Math.min(viewportWidth / rootGroupBox.width, viewportHeight / rootGroupBox.height);
1139
+ }
1140
+ else {
1141
+ newZoom = 1;
1142
+ }
1143
+ const viewBox = getViewBox(board, newZoom);
1144
+ const centerX = viewBox[0] + viewBox[2] / 2;
1145
+ const centerY = viewBox[1] + viewBox[3] / 2;
1146
+ const newOrigination = [centerX - viewportContainerRect.width / 2 / newZoom, centerY - viewportContainerRect.height / 2 / newZoom];
1147
+ setViewport(board, newOrigination, newZoom);
1148
+ }
1149
+ function scrollToRectangle(board, client) { }
1150
+
1151
+ let timerId = null;
1152
+ const throttleRAF = (fn) => {
1153
+ const scheduleFunc = () => {
1154
+ timerId = requestAnimationFrame(() => {
1155
+ timerId = null;
1156
+ fn();
1157
+ });
1158
+ };
1159
+ if (timerId !== null) {
1160
+ cancelAnimationFrame(timerId);
1161
+ timerId = null;
1162
+ }
1163
+ scheduleFunc();
1164
+ };
1165
+
1166
+ const getMovingElements = (board) => {
1167
+ return BOARD_TO_MOVING_ELEMENT.get(board) || [];
1168
+ };
1169
+ const addMovingElements = (board, elements) => {
1170
+ const movingElements = getMovingElements(board);
1171
+ cacheMovingElements(board, [...movingElements, ...elements]);
1172
+ };
1173
+ const removeMovingElements = (board) => {
1174
+ BOARD_TO_MOVING_ELEMENT.delete(board);
1175
+ };
1176
+ const cacheMovingElements = (board, elements) => {
1177
+ BOARD_TO_MOVING_ELEMENT.set(board, elements);
1178
+ };
1179
+
1116
1180
  const updatePointerType = (board, pointer) => {
1117
1181
  board.pointer = pointer;
1118
1182
  const boardComponent = BOARD_TO_COMPONENT.get(board);
@@ -1178,6 +1242,7 @@ function createBoard(children, options) {
1178
1242
  destroyElement: (context) => { },
1179
1243
  isWithinSelection: element => false,
1180
1244
  isIntersectionSelection: element => false,
1245
+ isMovable: element => false,
1181
1246
  getRectangle: element => null
1182
1247
  };
1183
1248
  return board;
@@ -1303,11 +1368,13 @@ function withHandPointer(board) {
1303
1368
  };
1304
1369
  board.mousemove = (event) => {
1305
1370
  if (board.pointer === PlaitPointerType.hand && board.selection && isMoving) {
1306
- const boardComponent = PlaitBoard.getComponent(board);
1307
- const left = event.x - plaitBoardMove.x;
1308
- const top = event.y - plaitBoardMove.y;
1309
- const { scrollLeft, scrollTop } = boardComponent.viewportState;
1310
- boardComponent.setScroll(scrollLeft - left, scrollTop - top);
1371
+ const viewportContainer = PlaitBoard.getViewportContainer(board);
1372
+ const left = viewportContainer.scrollLeft + (event.x - plaitBoardMove.x);
1373
+ const top = viewportContainer.scrollTop + (event.y - plaitBoardMove.y);
1374
+ const zoom = board.viewport.zoom;
1375
+ const viewBox = getViewBox(board, zoom);
1376
+ const origination = [left / zoom + viewBox[0], top / zoom + viewBox[1]];
1377
+ setViewport(board, origination);
1311
1378
  plaitBoardMove.x = event.x;
1312
1379
  plaitBoardMove.y = event.y;
1313
1380
  }
@@ -1340,6 +1407,8 @@ function withHandPointer(board) {
1340
1407
  return board;
1341
1408
  }
1342
1409
 
1410
+ const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
1411
+
1343
1412
  function withSelection(board) {
1344
1413
  const { mousedown, globalMousemove, globalMouseup, onChange } = board;
1345
1414
  let start = null;
@@ -1347,12 +1416,22 @@ function withSelection(board) {
1347
1416
  let selectionMovingG;
1348
1417
  let selectionOuterG;
1349
1418
  board.mousedown = (event) => {
1350
- selectionOuterG?.remove();
1351
1419
  if (event.button === 0) {
1352
1420
  start = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1353
1421
  }
1354
1422
  if (start) {
1355
- Transforms.setSelection(board, { ranges: [{ anchor: start, focus: start }] });
1423
+ const ranges = [{ anchor: start, focus: start }];
1424
+ const selectedElements = getSelectedElements(board);
1425
+ const intersectionSelectedElement = isIntersectionElements(board, selectedElements, ranges);
1426
+ if (intersectionSelectedElement) {
1427
+ start = null;
1428
+ }
1429
+ else {
1430
+ Transforms.setSelection(board, { ranges: ranges });
1431
+ if (calcElementIntersectionSelection(board).length) {
1432
+ start = null;
1433
+ }
1434
+ }
1356
1435
  }
1357
1436
  mousedown(event);
1358
1437
  };
@@ -1360,13 +1439,11 @@ function withSelection(board) {
1360
1439
  if (start) {
1361
1440
  const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1362
1441
  const { x, y, width, height } = RectangleClient.toRectangleClient([start, movedTarget]);
1442
+ selectionMovingG?.remove();
1363
1443
  if (Math.hypot(width, height) > 5) {
1364
1444
  end = movedTarget;
1365
- if (end) {
1366
- Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1367
- }
1368
- PlaitBoard.getBoardNativeElement(board).classList.add('selection-moving');
1369
- selectionMovingG?.remove();
1445
+ Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1446
+ setSelectionMoving(board);
1370
1447
  const rough = PlaitBoard.getRoughSVG(board);
1371
1448
  selectionMovingG = rough.rectangle(x, y, width, height, {
1372
1449
  stroke: SELECTION_BORDER_COLOR,
@@ -1381,8 +1458,18 @@ function withSelection(board) {
1381
1458
  };
1382
1459
  board.globalMouseup = (event) => {
1383
1460
  if (start && end) {
1384
- PlaitBoard.getBoardNativeElement(board).classList.remove('selection-moving');
1385
1461
  selectionMovingG?.remove();
1462
+ clearSelectionMoving(board);
1463
+ Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1464
+ }
1465
+ if (PlaitBoard.isFocus(board)) {
1466
+ const isInBoard = event.target instanceof Node && PlaitBoard.getBoardNativeElement(board).contains(event.target);
1467
+ const isAttachedElement = event.target instanceof HTMLElement && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
1468
+ // Clear selection when mouse board outside area
1469
+ // The framework needs to determine whether the board is focused through selection
1470
+ if (!isInBoard && !start && !isAttachedElement) {
1471
+ Transforms.setSelection(board, null);
1472
+ }
1386
1473
  }
1387
1474
  start = null;
1388
1475
  end = null;
@@ -1391,6 +1478,7 @@ function withSelection(board) {
1391
1478
  board.onChange = () => {
1392
1479
  // calc selected elements entry
1393
1480
  try {
1481
+ selectionOuterG?.remove();
1394
1482
  if (board.operations.find(value => value.type === 'set_selection')) {
1395
1483
  const temporaryElements = getTemporaryElements(board);
1396
1484
  const elements = temporaryElements ? temporaryElements : calcElementIntersectionSelection(board);
@@ -1398,12 +1486,12 @@ function withSelection(board) {
1398
1486
  const { x, y, width, height } = getRectangleByElements(board, elements, false);
1399
1487
  if (width > 0 && height > 0) {
1400
1488
  const rough = PlaitBoard.getRoughSVG(board);
1401
- selectionOuterG?.remove();
1402
1489
  selectionOuterG = rough.rectangle(x - 2, y - 2, width + 4, height + 4, {
1403
1490
  stroke: SELECTION_BORDER_COLOR,
1404
1491
  strokeWidth: 1,
1405
1492
  fillStyle: 'solid'
1406
1493
  });
1494
+ selectionOuterG.classList.add('selection-outer');
1407
1495
  PlaitBoard.getHost(board).append(selectionOuterG);
1408
1496
  }
1409
1497
  }
@@ -1422,6 +1510,27 @@ function getTemporaryElements(board) {
1422
1510
  function deleteTemporaryElements(board) {
1423
1511
  BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
1424
1512
  }
1513
+ function isSelectionMoving(board) {
1514
+ return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
1515
+ }
1516
+ function setSelectionMoving(board) {
1517
+ PlaitBoard.getBoardNativeElement(board).classList.add('selection-moving');
1518
+ BOARD_TO_IS_SELECTION_MOVING.set(board, true);
1519
+ }
1520
+ function clearSelectionMoving(board) {
1521
+ PlaitBoard.getBoardNativeElement(board).classList.remove('selection-moving');
1522
+ BOARD_TO_IS_SELECTION_MOVING.delete(board);
1523
+ }
1524
+
1525
+ function withViewport(board) {
1526
+ const { onChange } = board;
1527
+ board.onChange = () => {
1528
+ initializeViewport(board);
1529
+ updateViewportContainerOffset(board);
1530
+ onChange();
1531
+ };
1532
+ return board;
1533
+ }
1425
1534
 
1426
1535
  class PlaitElementComponent {
1427
1536
  constructor(renderer2, viewContainerRef) {
@@ -1475,7 +1584,7 @@ class PlaitElementComponent {
1475
1584
  const current = {
1476
1585
  element: this.element,
1477
1586
  selection: this.selection,
1478
- board: this.board,
1587
+ board: this.board
1479
1588
  };
1480
1589
  if (this.context) {
1481
1590
  const previous = { ...this.context };
@@ -1575,10 +1684,6 @@ class PlaitBoardComponent {
1575
1684
  this.elementRef = elementRef;
1576
1685
  this.hasInitialized = false;
1577
1686
  this.destroy$ = new Subject();
1578
- this.viewportState = {
1579
- zoom: 1,
1580
- autoFitPadding: 8
1581
- };
1582
1687
  this.plaitValue = [];
1583
1688
  this.plaitPlugins = [];
1584
1689
  this.plaitChange = new EventEmitter();
@@ -1587,9 +1692,6 @@ class PlaitBoardComponent {
1587
1692
  return index;
1588
1693
  };
1589
1694
  }
1590
- get isFocused() {
1591
- return this.board?.selection;
1592
- }
1593
1695
  get host() {
1594
1696
  return this.svg.nativeElement;
1595
1697
  }
@@ -1599,8 +1701,8 @@ class PlaitBoardComponent {
1599
1701
  get readonly() {
1600
1702
  return this.board.options.readonly;
1601
1703
  }
1602
- get focused() {
1603
- return this.isFocused;
1704
+ get isFocused() {
1705
+ return PlaitBoard.isFocus(this.board);
1604
1706
  }
1605
1707
  get nativeElement() {
1606
1708
  return this.elementRef.nativeElement;
@@ -1610,10 +1712,12 @@ class PlaitBoardComponent {
1610
1712
  const roughSVG = rough.svg(this.host, {
1611
1713
  options: { roughness: 0, strokeWidth: 1 }
1612
1714
  });
1715
+ this.roughSVG = roughSVG;
1613
1716
  this.initializePlugins();
1614
1717
  this.initializeEvents();
1615
1718
  this.viewportScrollListener();
1616
1719
  this.elementResizeListener();
1720
+ this.mouseLeaveListener();
1617
1721
  BOARD_TO_COMPONENT.set(this.board, this);
1618
1722
  BOARD_TO_ROUGH_SVG.set(this.board, roughSVG);
1619
1723
  BOARD_TO_HOST.set(this.board, this.host);
@@ -1630,13 +1734,19 @@ class PlaitBoardComponent {
1630
1734
  });
1631
1735
  this.hasInitialized = true;
1632
1736
  }
1737
+ mouseLeaveListener() {
1738
+ fromEvent(this.host, 'mouseleave')
1739
+ .pipe(takeUntil(this.destroy$))
1740
+ .subscribe((event) => {
1741
+ BOARD_TO_MOVING_POINT.delete(this.board);
1742
+ });
1743
+ }
1633
1744
  ngOnChanges(changes) {
1634
1745
  if (this.hasInitialized) {
1635
1746
  const valueChange = changes['plaitValue'];
1636
1747
  const options = changes['plaitOptions'];
1637
- if (valueChange) {
1748
+ if (valueChange)
1638
1749
  this.board.children = valueChange.currentValue;
1639
- }
1640
1750
  if (options)
1641
1751
  this.board.options = options.currentValue;
1642
1752
  this.cdr.markForCheck();
@@ -1645,20 +1755,17 @@ class PlaitBoardComponent {
1645
1755
  ngAfterViewInit() {
1646
1756
  this.plaitBoardInitialized.emit(this.board);
1647
1757
  this.initViewportContainer();
1648
- this.calcViewBox(this.viewportState.zoom);
1649
- this.initViewport();
1758
+ initializeViewport(this.board);
1759
+ initializeViewportContainerOffset(this.board);
1650
1760
  }
1651
1761
  initializePlugins() {
1652
- let board = withHandPointer(withHistory(withSelection(withBoard(createBoard(this.plaitValue, this.plaitOptions)))));
1762
+ let board = withHandPointer(withHistory(withSelection(withBoard(withViewport(createBoard(this.plaitValue, this.plaitOptions))))));
1653
1763
  this.plaitPlugins.forEach(plugin => {
1654
1764
  board = plugin(board);
1655
1765
  });
1656
1766
  this.board = board;
1657
1767
  if (this.plaitViewport) {
1658
1768
  this.board.viewport = this.plaitViewport;
1659
- this.updateViewportState({
1660
- zoom: this.plaitViewport?.zoom ?? 1
1661
- });
1662
1769
  }
1663
1770
  }
1664
1771
  initializeEvents() {
@@ -1694,31 +1801,31 @@ class PlaitBoardComponent {
1694
1801
  this.board.dblclick(event);
1695
1802
  });
1696
1803
  fromEvent(document, 'keydown')
1697
- .pipe(takeUntil(this.destroy$), filter(() => {
1698
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1699
- }))
1804
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1700
1805
  .subscribe((event) => {
1806
+ if (isHotkey$1(['mod+=', 'mod++'], { byKey: true })(event)) {
1807
+ event.preventDefault();
1808
+ this.zoomInHandle(false);
1809
+ }
1810
+ if (isHotkey$1('mod+-', { byKey: true })(event)) {
1811
+ this.zoomOutHandle();
1812
+ event.preventDefault();
1813
+ }
1701
1814
  this.board?.keydown(event);
1702
1815
  });
1703
1816
  fromEvent(document, 'keyup')
1704
- .pipe(takeUntil(this.destroy$), filter(() => {
1705
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1706
- }))
1817
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1707
1818
  .subscribe((event) => {
1708
1819
  this.board?.keyup(event);
1709
1820
  });
1710
1821
  fromEvent(document, 'copy')
1711
- .pipe(takeUntil(this.destroy$), filter(() => {
1712
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1713
- }))
1822
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1714
1823
  .subscribe((event) => {
1715
1824
  event.preventDefault();
1716
1825
  this.board?.setFragment(event.clipboardData);
1717
1826
  });
1718
1827
  fromEvent(document, 'paste')
1719
- .pipe(takeUntil(this.destroy$), filter(() => {
1720
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection && !this.readonly;
1721
- }))
1828
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1722
1829
  .subscribe((clipboardEvent) => {
1723
1830
  const mousePoint = BOARD_TO_MOVING_POINT.get(this.board);
1724
1831
  const rect = this.nativeElement.getBoundingClientRect();
@@ -1728,9 +1835,7 @@ class PlaitBoardComponent {
1728
1835
  }
1729
1836
  });
1730
1837
  fromEvent(document, 'cut')
1731
- .pipe(takeUntil(this.destroy$), filter(() => {
1732
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1733
- }))
1838
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1734
1839
  .subscribe((event) => {
1735
1840
  event.preventDefault();
1736
1841
  this.board?.setFragment(event.clipboardData);
@@ -1739,208 +1844,42 @@ class PlaitBoardComponent {
1739
1844
  }
1740
1845
  viewportScrollListener() {
1741
1846
  fromEvent(this.viewportContainer.nativeElement, 'scroll')
1742
- .pipe(takeUntil(this.destroy$), filter(() => {
1743
- return !!this.isFocused;
1744
- }))
1847
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused))
1745
1848
  .subscribe((event) => {
1746
1849
  const { scrollLeft, scrollTop } = event.target;
1747
- const left = this.viewportState.scrollLeft;
1748
- const top = this.viewportState.scrollTop;
1749
- if (Math.abs(left - scrollLeft) >= 1 || Math.abs(top - scrollTop) >= 1) {
1750
- this.setScrollLeft(scrollLeft);
1751
- this.setScrollTop(scrollTop);
1752
- this.setViewport();
1753
- }
1850
+ const zoom = this.board.viewport.zoom;
1851
+ const viewBox = getViewBox(this.board, zoom);
1852
+ const origination = [scrollLeft / zoom + viewBox[0], scrollTop / zoom + viewBox[1]];
1853
+ setViewport(this.board, origination);
1754
1854
  });
1755
1855
  }
1756
1856
  elementResizeListener() {
1757
1857
  this.resizeObserver = new ResizeObserver(() => {
1758
1858
  this.initViewportContainer();
1759
- this.calcViewBox(this.board.viewport.zoom);
1760
- this.updateViewBoxStyles();
1761
- this.updateViewportScrolling();
1762
- this.setViewport();
1763
1859
  });
1764
1860
  this.resizeObserver.observe(this.nativeElement);
1765
1861
  }
1766
- updateViewportState(state) {
1767
- this.viewportState = {
1768
- ...this.viewportState,
1769
- ...state
1770
- };
1771
- }
1772
1862
  initViewportContainer() {
1773
- const { width, height } = getBoardClientBox(this.board);
1863
+ const { width, height } = getViewportContainerRect(this.board);
1774
1864
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'width', `${width}px`);
1775
1865
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'height', `${height}px`);
1776
1866
  }
1777
- initViewport(viewport = this.board.viewport) {
1778
- const originationCoord = viewport?.originationCoord;
1779
- const zoom = viewport?.zoom ?? this.viewportState.zoom;
1780
- if (originationCoord) {
1781
- const matrix = this.getMatrix();
1782
- const [pointX, pointY] = invertViewportCoordinates([0, 0], matrix);
1783
- const scrollLeft = this.viewportState.scrollLeft;
1784
- const scrollTop = this.viewportState.scrollTop;
1785
- const left = scrollLeft + (originationCoord[0] - pointX) * zoom;
1786
- const top = scrollTop + (originationCoord[1] - pointY) * zoom;
1787
- this.setScroll(left, top);
1788
- }
1789
- else {
1790
- this.adaptHandle();
1791
- }
1792
- }
1793
- calcViewBox(zoom = this.viewportState.zoom) {
1794
- zoom = clampZoomLevel(zoom);
1795
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1796
- const viewportContainerBox = getViewportContainerBox(this.board);
1797
- const groupBBox = getRootGroupBBox(this.board, zoom);
1798
- const horizontalPadding = viewportContainerBox.width / 2;
1799
- const verticalPadding = viewportContainerBox.height / 2;
1800
- const viewportWidth = (groupBBox.right - groupBBox.left) * zoom + 2 * horizontalPadding + scrollBarWidth;
1801
- const viewportHeight = (groupBBox.bottom - groupBBox.top) * zoom + 2 * verticalPadding + scrollBarWidth;
1802
- const viewBox = [
1803
- groupBBox.left - horizontalPadding / zoom,
1804
- groupBBox.top - verticalPadding / zoom,
1805
- viewportWidth / zoom,
1806
- viewportHeight / zoom
1807
- ];
1808
- const matrix = this.getMatrix();
1809
- let scrollLeft;
1810
- let scrollTop;
1811
- if (matrix.length > 0) {
1812
- // focusPoint
1813
- const focusX = viewportContainerBox.x + viewportContainerBox.width / 2;
1814
- const focusY = viewportContainerBox.y + viewportContainerBox.height / 2;
1815
- const viewportContainerPoint = [focusX - viewportContainerBox.x, focusY - viewportContainerBox.y, 1];
1816
- const point = invertViewportCoordinates([viewportContainerPoint[0], viewportContainerPoint[1]], matrix);
1817
- const newMatrix = [zoom, 0, 0, 0, zoom, 0, -zoom * viewBox[0], -zoom * viewBox[1], 1];
1818
- const newPoint = transformMat3([], point, newMatrix);
1819
- scrollLeft = newPoint[0] - viewportContainerPoint[0];
1820
- scrollTop = newPoint[1] - viewportContainerPoint[1];
1821
- }
1822
- else {
1823
- scrollLeft = horizontalPadding;
1824
- scrollTop = verticalPadding;
1825
- }
1826
- this.updateViewportState({
1827
- viewportWidth,
1828
- viewportHeight,
1829
- zoom,
1830
- viewBox
1831
- });
1832
- this.setScrollLeft(scrollLeft);
1833
- this.setScrollTop(scrollTop);
1834
- }
1835
- getMatrix() {
1836
- const { viewBox, zoom, scrollLeft, scrollTop } = this.viewportState;
1837
- if (scrollLeft >= 0 && scrollTop >= 0) {
1838
- return [zoom, 0, 0, 0, zoom, 0, -scrollLeft - zoom * viewBox[0], -scrollTop - zoom * viewBox[1], 1];
1839
- }
1840
- return [];
1841
- }
1842
- setScrollLeft(left) {
1843
- const viewportContainerBox = getViewportContainerBox(this.board);
1844
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1845
- const { viewportWidth } = this.viewportState;
1846
- const width = viewportWidth - viewportContainerBox.width + scrollBarWidth;
1847
- this.viewportState.scrollLeft = left < 0 ? 0 : left > width ? width : left;
1848
- }
1849
- setScrollTop(top) {
1850
- const viewportContainerBox = getViewportContainerBox(this.board);
1851
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1852
- const { viewportHeight } = this.viewportState;
1853
- const height = viewportHeight - viewportContainerBox.height + scrollBarWidth;
1854
- this.viewportState.scrollTop = top < 0 ? 0 : top > height ? height : top;
1855
- }
1856
- setScroll(left, top) {
1857
- this.setScrollLeft(left);
1858
- this.setScrollTop(top);
1859
- this.updateViewBoxStyles();
1860
- this.updateViewportScrolling();
1861
- this.setViewport();
1862
- }
1863
- updateViewBoxStyles() {
1864
- const { host, viewportState } = this;
1865
- const { viewportWidth, viewportHeight, viewBox } = viewportState;
1866
- this.renderer2.setStyle(host, 'display', 'block');
1867
- this.renderer2.setStyle(host, 'width', `${viewportWidth}px`);
1868
- this.renderer2.setStyle(host, 'height', `${viewportHeight}px`);
1869
- if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
1870
- this.renderer2.setAttribute(host, 'viewBox', viewBox.join(' '));
1871
- }
1872
- }
1873
- updateViewportScrolling() {
1874
- const { viewportContainer, viewportState } = this;
1875
- const { scrollLeft, scrollTop } = viewportState;
1876
- viewportContainer.nativeElement.scrollLeft = scrollLeft;
1877
- viewportContainer.nativeElement.scrollTop = scrollTop;
1878
- }
1879
- setViewport() {
1880
- const viewport = this.board?.viewport;
1881
- const oldOriginationCoord = viewport?.originationCoord ?? [];
1882
- const matrix = this.getMatrix();
1883
- const originationCoord = invertViewportCoordinates([0, 0], matrix);
1884
- if (!originationCoord.every((item, index) => item === oldOriginationCoord[index])) {
1885
- Transforms.setViewport(this.board, {
1886
- ...viewport,
1887
- zoom: this.viewportState.zoom,
1888
- originationCoord
1889
- });
1890
- }
1891
- }
1892
1867
  adaptHandle() {
1893
- const containerBox = getViewportContainerBox(this.board);
1894
- const rootGroup = this.host.firstChild;
1895
- const matrix = this.getMatrix();
1896
- const rootGroupBox = rootGroup.getBBox();
1897
- const centerPoint = [containerBox.width / 2, containerBox.height / 2];
1898
- const rootGroupCenterX = rootGroupBox.x + rootGroupBox.width / 2;
1899
- const rootGroupCenterY = rootGroupBox.y + rootGroupBox.height / 2;
1900
- const transformedPoint = transformMat3([], [rootGroupCenterX, rootGroupCenterY, 1], matrix);
1901
- const offsetLeft = centerPoint[0] - transformedPoint[0];
1902
- const offsetTop = centerPoint[1] - transformedPoint[1];
1903
- const { autoFitPadding, zoom, scrollLeft, scrollTop } = this.viewportState;
1904
- const viewportWidth = containerBox.width - 2 * autoFitPadding;
1905
- const viewportHeight = containerBox.height - 2 * autoFitPadding;
1906
- let newZoom = zoom;
1907
- if (viewportWidth < rootGroupBox.width || viewportHeight < rootGroupBox.height) {
1908
- newZoom = Math.min(viewportWidth / rootGroupBox.width, viewportHeight / rootGroupBox.height);
1909
- }
1910
- else {
1911
- newZoom = 1;
1912
- }
1913
- this.setScrollLeft(scrollLeft - offsetLeft);
1914
- this.setScrollTop(scrollTop - offsetTop);
1915
- this.calcViewBox(newZoom);
1916
- this.updateViewBoxStyles();
1917
- this.updateViewportScrolling();
1918
- this.setViewport();
1868
+ fitViewport(this.board);
1919
1869
  }
1920
- zoomInHandle() {
1921
- this.calcViewBox(this.viewportState.zoom + 0.1);
1922
- this.updateViewBoxStyles();
1923
- this.updateViewportScrolling();
1924
- this.setViewport();
1870
+ zoomInHandle(isCenter = true) {
1871
+ changeZoom(this.board, this.board.viewport.zoom + 0.1, isCenter);
1925
1872
  }
1926
1873
  zoomOutHandle() {
1927
- this.calcViewBox(this.viewportState.zoom - 0.1);
1928
- this.updateViewBoxStyles();
1929
- this.updateViewportScrolling();
1930
- this.setViewport();
1874
+ changeZoom(this.board, this.board.viewport.zoom - 0.1);
1931
1875
  }
1932
1876
  resetZoomHandel() {
1933
- this.calcViewBox(1);
1934
- this.updateViewBoxStyles();
1935
- this.updateViewportScrolling();
1936
- this.setViewport();
1877
+ changeZoom(this.board, 1);
1937
1878
  }
1938
1879
  ngOnDestroy() {
1939
1880
  this.destroy$.next();
1940
1881
  this.destroy$.complete();
1941
- if (this.resizeObserver) {
1942
- this.resizeObserver.disconnect();
1943
- }
1882
+ this.resizeObserver && this.resizeObserver?.disconnect();
1944
1883
  BOARD_TO_ROUGH_SVG.delete(this.board);
1945
1884
  BOARD_TO_COMPONENT.delete(this.board);
1946
1885
  BOARD_TO_ROUGH_SVG.delete(this.board);
@@ -1951,40 +1890,9 @@ class PlaitBoardComponent {
1951
1890
  markForCheck() {
1952
1891
  this.cdr.markForCheck();
1953
1892
  }
1954
- scrollToRectangle(client) {
1955
- this.calcViewBox();
1956
- this.updateViewBoxStyles();
1957
- this.updateViewportScrolling();
1958
- this.setViewport();
1959
- const svgRect = this.host.getBoundingClientRect();
1960
- const viewportContainerBox = getViewportContainerBox(this.board);
1961
- if (svgRect.width > viewportContainerBox.width || svgRect.height > viewportContainerBox.height) {
1962
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1963
- const matrix = this.getMatrix();
1964
- const [nodePointX, nodePointY] = convertToViewportCoordinates([client.x, client.y], matrix);
1965
- const [fullNodePointX, fullNodePointY] = convertToViewportCoordinates([client.x + client.width, client.y + client.height], matrix);
1966
- let newLeft = this.viewportState.scrollLeft;
1967
- let newTop = this.viewportState.scrollTop;
1968
- if (nodePointX < 0) {
1969
- newLeft -= Math.abs(nodePointX);
1970
- }
1971
- if (nodePointX > 0 && fullNodePointX > viewportContainerBox.width) {
1972
- newLeft += fullNodePointX - viewportContainerBox.width + scrollBarWidth;
1973
- }
1974
- if (nodePointY < 0) {
1975
- newTop -= Math.abs(nodePointY);
1976
- }
1977
- if (nodePointY > 0 && fullNodePointY > viewportContainerBox.height) {
1978
- newTop += fullNodePointY - viewportContainerBox.height + scrollBarWidth;
1979
- }
1980
- if (newLeft !== this.viewportState.scrollLeft || newTop !== this.viewportState.scrollTop) {
1981
- this.setScroll(newLeft, newTop);
1982
- }
1983
- }
1984
- }
1985
1893
  }
1986
1894
  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 });
1987
- 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: `
1895
+ 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: `
1988
1896
  <div class="viewport-container" #viewportContainer>
1989
1897
  <svg #svg width="100%" height="100%" style="position: relative;"><g class="element-host"></g></svg>
1990
1898
  <plait-element
@@ -2050,7 +1958,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2050
1958
  }], readonly: [{
2051
1959
  type: HostBinding,
2052
1960
  args: ['class.readonly']
2053
- }], focused: [{
1961
+ }], isFocused: [{
2054
1962
  type: HostBinding,
2055
1963
  args: ['class.focused']
2056
1964
  }], svg: [{
@@ -2147,6 +2055,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2147
2055
  }]
2148
2056
  }] });
2149
2057
 
2058
+ function withMoving(board) {
2059
+ const { mousedown, mousemove, globalMouseup, globalMousemove } = board;
2060
+ let offsetX = 0;
2061
+ let offsetY = 0;
2062
+ let isPreventDefault = false;
2063
+ let startPoint;
2064
+ let activeElements = [];
2065
+ board.mousedown = event => {
2066
+ const host = BOARD_TO_HOST.get(board);
2067
+ const point = transformPoint(board, toPoint(event.x, event.y, host));
2068
+ const ranges = [{ anchor: point, focus: point }];
2069
+ let movableElements = board.children.filter(item => PlaitElement.isElement(item) && board.isMovable(item));
2070
+ if (movableElements.length) {
2071
+ startPoint = point;
2072
+ const selectedRootElements = getSelectedElements(board).filter(item => movableElements.includes(item));
2073
+ const intersectionSelectedElement = isIntersectionElements(board, selectedRootElements, ranges);
2074
+ if (intersectionSelectedElement) {
2075
+ activeElements = selectedRootElements;
2076
+ }
2077
+ else {
2078
+ activeElements = movableElements.filter(item => ranges.some(range => {
2079
+ return board.isIntersectionSelection(item, range);
2080
+ }));
2081
+ }
2082
+ }
2083
+ mousedown(event);
2084
+ };
2085
+ board.mousemove = event => {
2086
+ if (startPoint && activeElements?.length) {
2087
+ if (!isPreventDefault) {
2088
+ isPreventDefault = true;
2089
+ }
2090
+ const host = BOARD_TO_HOST.get(board);
2091
+ const endPoint = transformPoint(board, toPoint(event.x, event.y, host));
2092
+ offsetX = endPoint[0] - startPoint[0];
2093
+ offsetY = endPoint[1] - startPoint[1];
2094
+ throttleRAF(() => {
2095
+ const currentElements = activeElements.map(activeElement => {
2096
+ const [x, y] = activeElement?.points[0];
2097
+ const index = board.children.findIndex(item => item.id === activeElement.id);
2098
+ Transforms.setNode(board, {
2099
+ points: [[x + offsetX, y + offsetY]]
2100
+ }, [index]);
2101
+ MERGING.set(board, true);
2102
+ return PlaitNode.get(board, [index]);
2103
+ });
2104
+ addMovingElements(board, currentElements);
2105
+ });
2106
+ }
2107
+ if (isPreventDefault) {
2108
+ // 阻止 move 过程中触发画布滚动行为
2109
+ event.preventDefault();
2110
+ }
2111
+ mousemove(event);
2112
+ };
2113
+ board.globalMousemove = event => {
2114
+ if (startPoint) {
2115
+ const inPliatBordElement = isInPlaitBoard(board, event.x, event.y);
2116
+ if (!inPliatBordElement) {
2117
+ cancelMove(board);
2118
+ }
2119
+ }
2120
+ globalMousemove(event);
2121
+ };
2122
+ board.globalMouseup = event => {
2123
+ isPreventDefault = false;
2124
+ if (startPoint) {
2125
+ cancelMove(board);
2126
+ }
2127
+ globalMouseup(event);
2128
+ };
2129
+ function cancelMove(board) {
2130
+ startPoint = null;
2131
+ offsetX = 0;
2132
+ offsetY = 0;
2133
+ activeElements = [];
2134
+ removeMovingElements(board);
2135
+ MERGING.set(board, false);
2136
+ }
2137
+ return board;
2138
+ }
2139
+
2150
2140
  /*
2151
2141
  * Public API Surface of plait
2152
2142
  */
@@ -2155,5 +2145,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2155
2145
  * Generated bundle index. Do not edit.
2156
2146
  */
2157
2147
 
2158
- 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, 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, addSelectedElement, arrowPoints, cacheSelectedElements, calcElementIntersectionSelection, clampZoomLevel, convertToViewportCoordinates, createG, createSVG, createText, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, drawArrow, drawRoundRectangle, getBoardClientBox, getBoardRectangle, getRectangleByElements, getRootGroupBBox, getSelectedElements, getTemporaryElements, getViewportContainerBox, hasBeforeContextChange, hotkeys, idCreator, inverse, invertMatrix, invertViewportCoordinates, isNoSelectionElement, isNullOrUndefined, isSelectedElement, isSetViewportOperation, normalizePoint, removeSelectedElement, rotate, shouldClear, shouldMerge, shouldSave, toPoint, transformMat3, transformPoint, transformPoints, withSelection };
2148
+ 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 };
2159
2149
  //# sourceMappingURL=plait-core.mjs.map