@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,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) => {
@@ -360,93 +459,71 @@ function setSelection(board, selection) {
360
459
  board.apply(operation);
361
460
  }
362
461
  const SelectionTransforms = {
363
- setSelection
462
+ setSelection,
463
+ setSelectionWithTemporaryElements
364
464
  };
465
+ function setSelectionWithTemporaryElements(board, elements) {
466
+ setTimeout(() => {
467
+ BOARD_TO_TEMPORARY_ELEMENTS.set(board, elements);
468
+ setSelection(board, { ranges: [] });
469
+ });
470
+ }
365
471
 
366
- function setViewport(board, viewport) {
472
+ function setViewport$1(board, viewport) {
367
473
  const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
368
474
  board.apply(operation);
369
475
  }
370
476
  const ViewportTransforms = {
371
- setViewport
477
+ setViewport: setViewport$1
372
478
  };
373
479
 
374
- // record richtext type status
375
- const FLUSHING = new WeakMap();
376
- const IS_TEXT_EDITABLE = new WeakMap();
377
- const BOARD_TO_ON_CHANGE = new WeakMap();
378
- const BOARD_TO_COMPONENT = new WeakMap();
379
- const BOARD_TO_ROUGH_SVG = new WeakMap();
380
- const BOARD_TO_HOST = new WeakMap();
381
- const BOARD_TO_ELEMENT_HOST = new WeakMap();
382
- const BOARD_TO_SELECTED_ELEMENT = new WeakMap();
383
- const BOARD_TO_MOVING_POINT = new WeakMap();
384
-
385
- function depthFirstRecursion(node, callback) {
386
- node.children?.forEach(child => {
387
- depthFirstRecursion(child, callback);
388
- });
389
- 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);
390
509
  }
391
-
392
- function getRectangleByElements(board, elements, recursion) {
393
- const boundaryBox = {
394
- left: Number.MAX_VALUE,
395
- top: Number.MAX_VALUE,
396
- right: Number.MIN_VALUE,
397
- bottom: Number.MIN_VALUE
398
- };
399
- const calcRectangleClient = (node) => {
400
- const nodeRectangle = board.getRectangle(node);
401
- if (nodeRectangle) {
402
- boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
403
- boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
404
- boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
405
- boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
406
- }
407
- };
408
- elements.forEach(element => {
409
- if (recursion) {
410
- depthFirstRecursion(element, node => calcRectangleClient(node));
411
- }
412
- else {
413
- calcRectangleClient(element);
414
- }
415
- });
416
- return {
417
- x: boundaryBox.left,
418
- y: boundaryBox.top,
419
- width: boundaryBox.right - boundaryBox.left,
420
- height: boundaryBox.bottom - boundaryBox.top
421
- };
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];
422
515
  }
423
- function getBoardRectangle(board) {
424
- 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);
425
526
  }
426
-
427
- const PlaitBoard = {
428
- getHost(board) {
429
- return BOARD_TO_HOST.get(board);
430
- },
431
- getElementHost(board) {
432
- return BOARD_TO_ELEMENT_HOST.get(board);
433
- },
434
- getRoughSVG(board) {
435
- return BOARD_TO_ROUGH_SVG.get(board);
436
- },
437
- getComponent(board) {
438
- return BOARD_TO_COMPONENT.get(board);
439
- },
440
- getBoardNativeElement(board) {
441
- return PlaitBoard.getComponent(board).nativeElement;
442
- },
443
- getRectangle(board) {
444
- return getRectangleByElements(board, board.children, true);
445
- },
446
- getViewportContainer(board) {
447
- return PlaitBoard.getHost(board).parentElement;
448
- }
449
- };
450
527
 
451
528
  function transformPoints(board, points) {
452
529
  const newPoints = points.map(point => {
@@ -462,8 +539,11 @@ function transformPoint(board, point) {
462
539
  const newPoint = [x, y];
463
540
  return newPoint;
464
541
  }
465
- function isNoSelectionElement(e) {
466
- 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;
467
547
  }
468
548
 
469
549
  const NS = 'http://www.w3.org/2000/svg';
@@ -804,222 +884,6 @@ function idCreator(length = 5) {
804
884
  return key;
805
885
  }
806
886
 
807
- // https://stackoverflow.com/a/6853926/232122
808
- function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
809
- const A = x - x1;
810
- const B = y - y1;
811
- const C = x2 - x1;
812
- const D = y2 - y1;
813
- const dot = A * C + B * D;
814
- const lenSquare = C * C + D * D;
815
- let param = -1;
816
- if (lenSquare !== 0) {
817
- // in case of 0 length line
818
- param = dot / lenSquare;
819
- }
820
- let xx, yy;
821
- if (param < 0) {
822
- xx = x1;
823
- yy = y1;
824
- }
825
- else if (param > 1) {
826
- xx = x2;
827
- yy = y2;
828
- }
829
- else {
830
- xx = x1 + param * C;
831
- yy = y1 + param * D;
832
- }
833
- const dx = x - xx;
834
- const dy = y - yy;
835
- return Math.hypot(dx, dy);
836
- }
837
- function rotate(x1, y1, x2, y2, angle) {
838
- // 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
839
- // 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
840
- // https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
841
- return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
842
- }
843
- function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
844
- const dx = x1 - x2;
845
- const dy = y1 - y2;
846
- return Math.hypot(dx, dy);
847
- }
848
- // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
849
- function distanceBetweenPointAndRectangle(x, y, rect) {
850
- var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
851
- var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
852
- return Math.sqrt(dx * dx + dy * dy);
853
- }
854
-
855
- /**
856
- * 逆矩阵
857
- * [a c e]
858
- * [b d f]
859
- * [0 0 1]
860
- * @param newMatrix 输出返回矩阵
861
- * @param matrix 新矩阵
862
- * @returns 逆矩阵
863
- */
864
- function invertMatrix(newMatrix, matrix) {
865
- const [n, r, a, i, o, c, l, s, u] = matrix;
866
- const determinant = u * o - c * s;
867
- const h = -u * i + c * l;
868
- const f = s * i - o * l;
869
- const product = n * determinant + r * h + a * f;
870
- if (!product) {
871
- return null;
872
- }
873
- const reciprocal = 1 / product;
874
- newMatrix[0] = determinant * reciprocal;
875
- newMatrix[1] = (-u * r + a * s) * reciprocal;
876
- newMatrix[2] = (c * r - a * o) * reciprocal;
877
- newMatrix[3] = h * reciprocal;
878
- newMatrix[4] = (u * n - a * l) * reciprocal;
879
- newMatrix[5] = (-c * n + a * i) * reciprocal;
880
- newMatrix[6] = f * reciprocal;
881
- newMatrix[7] = (-s * n + r * l) * reciprocal;
882
- newMatrix[8] = (o * n - r * i) * reciprocal;
883
- return newMatrix;
884
- }
885
- /**
886
- * 将视图坐标与反转矩阵相乘,以得到原始坐标
887
- * 使用给定的矩阵进行转换
888
- * 矩阵与向量乘法,3 维向量与3x3矩阵的乘积
889
- * [m11 m12 m13][v1]
890
- * [m21 m22 m23][v2]
891
- * [m31 m32 m33][v3]
892
- * @param out 输出结果向量
893
- * @param t 要转换的向量
894
- * @param n 矩阵转换
895
- * @returns [v1 * m11 + v2 * m12 + v3 * m13, v1 * m21 + v2 * m22 + v3 * m23, v1 * m31 + v2 * m32 + v3 * m33];
896
- */
897
- function transformMat3(out, vector, matrix) {
898
- out = [
899
- vector[0] * matrix[0] + vector[1] * matrix[3] + vector[2] * matrix[6],
900
- vector[0] * matrix[1] + vector[1] * matrix[4] + vector[2] * matrix[7],
901
- vector[0] * matrix[2] + vector[1] * matrix[5] + vector[2] * matrix[8]
902
- ];
903
- return out;
904
- }
905
- /**
906
- * 规范 point
907
- * @param point
908
- * @returns point
909
- */
910
- function normalizePoint(point) {
911
- return Array.isArray(point)
912
- ? {
913
- x: point[0],
914
- y: point[1]
915
- }
916
- : point;
917
- }
918
- /**
919
- * 将一个点坐标反转回它的原始坐标
920
- * @param point 表示要反转的点的视图坐标,它是一个长度为 2 的数组,存储点的 x 和 y 坐标
921
- * @param matrix 表示视图矩阵,是在视图中对图形进行缩放和平移时使用的矩阵
922
- * @returns 最终结果是一个长度为 3 的数组,存储点的 x,y 和 w 坐标(w 坐标是点的齐次坐标)
923
- */
924
- function invertViewportCoordinates(point, matrix) {
925
- const { x, y } = normalizePoint(point);
926
- const invertedMatrix = invertMatrix([], matrix);
927
- return transformMat3([], [x, y, 1], invertedMatrix);
928
- }
929
- function convertToViewportCoordinates(point, matrix) {
930
- const { x, y } = normalizePoint(point);
931
- return transformMat3([], [x, y, 1], matrix);
932
- }
933
- /**
934
- * 获取 contentContainer 的 clientBox
935
- * @param board
936
- * @returns
937
- */
938
- function getViewportContainerBox(board) {
939
- const { hideScrollbar } = board.options;
940
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
941
- const container = PlaitBoard.getViewportContainer(board);
942
- const containerRect = container.getBoundingClientRect();
943
- const x = containerRect.x || containerRect.left;
944
- const y = containerRect.y || containerRect.top;
945
- const width = containerRect.width - scrollBarWidth;
946
- const height = containerRect.height - scrollBarWidth;
947
- return {
948
- minX: x,
949
- minY: y,
950
- maxX: x + width,
951
- maxY: y + height,
952
- x,
953
- y,
954
- width,
955
- height
956
- };
957
- }
958
- /**
959
- * 获取 board.plait-board 的 clientBox
960
- * @param board
961
- * @returns
962
- */
963
- function getBoardClientBox(board) {
964
- const { hideScrollbar } = board.options;
965
- const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
966
- const viewportRect = PlaitBoard.getViewportContainer(board).getBoundingClientRect();
967
- return {
968
- width: viewportRect.width + scrollBarWidth,
969
- height: viewportRect.height + scrollBarWidth
970
- };
971
- }
972
- /**
973
- * 获取 rootGroup 相对于当前 svg 空间的最小矩阵坐标
974
- */
975
- function getRootGroupBBox(board, zoom) {
976
- const elementHost = PlaitBoard.getElementHost(board);
977
- const rootGroupBox = elementHost.getBBox();
978
- const viewportContainerBox = getViewportContainerBox(board);
979
- const containerWidth = viewportContainerBox.width / zoom;
980
- const containerHeight = viewportContainerBox.height / zoom;
981
- let left;
982
- let right;
983
- let top;
984
- let bottom;
985
- if (rootGroupBox.width < containerWidth) {
986
- const offsetX = rootGroupBox.x + rootGroupBox.width / 2;
987
- const containerX = containerWidth / 2;
988
- left = offsetX - containerX;
989
- right = offsetX + containerX;
990
- }
991
- else {
992
- left = rootGroupBox.x;
993
- right = rootGroupBox.x + rootGroupBox.width;
994
- }
995
- if (rootGroupBox.height < containerHeight) {
996
- const offsetY = rootGroupBox.y + rootGroupBox.height / 2;
997
- const containerY = containerHeight / 2;
998
- top = offsetY - containerY;
999
- bottom = offsetY + containerY;
1000
- }
1001
- else {
1002
- top = rootGroupBox.y;
1003
- bottom = rootGroupBox.y + rootGroupBox.height;
1004
- }
1005
- return {
1006
- left,
1007
- right,
1008
- top,
1009
- bottom
1010
- };
1011
- }
1012
- /**
1013
- * 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
1014
- * @param zoom 缩放比
1015
- * @param minZoom 最小缩放比
1016
- * @param maxZoom 最大缩放比
1017
- * @returns 正确的缩放比
1018
- */
1019
- function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
1020
- return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
1021
- }
1022
-
1023
887
  const calcElementIntersectionSelection = (board) => {
1024
888
  const selectedElements = [];
1025
889
  depthFirstRecursion(board, node => {
@@ -1032,6 +896,19 @@ const calcElementIntersectionSelection = (board) => {
1032
896
  });
1033
897
  return selectedElements;
1034
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
+ };
1035
912
  const cacheSelectedElements = (board, selectedElements) => {
1036
913
  BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
1037
914
  };
@@ -1052,6 +929,10 @@ const isSelectedElement = (board, element) => {
1052
929
  return !!selectedElements.find(value => value === element);
1053
930
  };
1054
931
 
932
+ const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
933
+ const SCROLL_BAR_WIDTH = 20;
934
+ const MAX_RADIUS = 16;
935
+
1055
936
  /**
1056
937
  * drawRoundRectangle
1057
938
  * @param rs RoughSVG
@@ -1104,6 +985,198 @@ function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 4
1104
985
  return [arrowLineLeft, arrowLineRight];
1105
986
  }
1106
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
+
1107
1180
  const updatePointerType = (board, pointer) => {
1108
1181
  board.pointer = pointer;
1109
1182
  const boardComponent = BOARD_TO_COMPONENT.get(board);
@@ -1169,6 +1242,7 @@ function createBoard(children, options) {
1169
1242
  destroyElement: (context) => { },
1170
1243
  isWithinSelection: element => false,
1171
1244
  isIntersectionSelection: element => false,
1245
+ isMovable: element => false,
1172
1246
  getRectangle: element => null
1173
1247
  };
1174
1248
  return board;
@@ -1294,11 +1368,13 @@ function withHandPointer(board) {
1294
1368
  };
1295
1369
  board.mousemove = (event) => {
1296
1370
  if (board.pointer === PlaitPointerType.hand && board.selection && isMoving) {
1297
- const boardComponent = PlaitBoard.getComponent(board);
1298
- const left = event.x - plaitBoardMove.x;
1299
- const top = event.y - plaitBoardMove.y;
1300
- const { scrollLeft, scrollTop } = boardComponent.viewportState;
1301
- 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);
1302
1378
  plaitBoardMove.x = event.x;
1303
1379
  plaitBoardMove.y = event.y;
1304
1380
  }
@@ -1331,6 +1407,8 @@ function withHandPointer(board) {
1331
1407
  return board;
1332
1408
  }
1333
1409
 
1410
+ const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
1411
+
1334
1412
  function withSelection(board) {
1335
1413
  const { mousedown, globalMousemove, globalMouseup, onChange } = board;
1336
1414
  let start = null;
@@ -1338,12 +1416,22 @@ function withSelection(board) {
1338
1416
  let selectionMovingG;
1339
1417
  let selectionOuterG;
1340
1418
  board.mousedown = (event) => {
1341
- selectionOuterG?.remove();
1342
1419
  if (event.button === 0) {
1343
1420
  start = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1344
1421
  }
1345
1422
  if (start) {
1346
- 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
+ }
1347
1435
  }
1348
1436
  mousedown(event);
1349
1437
  };
@@ -1351,13 +1439,11 @@ function withSelection(board) {
1351
1439
  if (start) {
1352
1440
  const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1353
1441
  const { x, y, width, height } = RectangleClient.toRectangleClient([start, movedTarget]);
1442
+ selectionMovingG?.remove();
1354
1443
  if (Math.hypot(width, height) > 5) {
1355
1444
  end = movedTarget;
1356
- if (movedTarget) {
1357
- Transforms.setSelection(board, { ranges: [{ anchor: start, focus: start }] });
1358
- }
1359
- PlaitBoard.getBoardNativeElement(board).classList.add('selection-moving');
1360
- selectionMovingG?.remove();
1445
+ Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
1446
+ setSelectionMoving(board);
1361
1447
  const rough = PlaitBoard.getRoughSVG(board);
1362
1448
  selectionMovingG = rough.rectangle(x, y, width, height, {
1363
1449
  stroke: SELECTION_BORDER_COLOR,
@@ -1372,8 +1458,18 @@ function withSelection(board) {
1372
1458
  };
1373
1459
  board.globalMouseup = (event) => {
1374
1460
  if (start && end) {
1375
- PlaitBoard.getBoardNativeElement(board).classList.remove('selection-moving');
1376
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
+ }
1377
1473
  }
1378
1474
  start = null;
1379
1475
  end = null;
@@ -1382,10 +1478,12 @@ function withSelection(board) {
1382
1478
  board.onChange = () => {
1383
1479
  // calc selected elements entry
1384
1480
  try {
1481
+ selectionOuterG?.remove();
1385
1482
  if (board.operations.find(value => value.type === 'set_selection')) {
1386
- const elementIds = calcElementIntersectionSelection(board);
1387
- cacheSelectedElements(board, elementIds);
1388
- const { x, y, width, height } = getRectangleByElements(board, elementIds, false);
1483
+ const temporaryElements = getTemporaryElements(board);
1484
+ const elements = temporaryElements ? temporaryElements : calcElementIntersectionSelection(board);
1485
+ cacheSelectedElements(board, elements);
1486
+ const { x, y, width, height } = getRectangleByElements(board, elements, false);
1389
1487
  if (width > 0 && height > 0) {
1390
1488
  const rough = PlaitBoard.getRoughSVG(board);
1391
1489
  selectionOuterG = rough.rectangle(x - 2, y - 2, width + 4, height + 4, {
@@ -1393,9 +1491,11 @@ function withSelection(board) {
1393
1491
  strokeWidth: 1,
1394
1492
  fillStyle: 'solid'
1395
1493
  });
1494
+ selectionOuterG.classList.add('selection-outer');
1396
1495
  PlaitBoard.getHost(board).append(selectionOuterG);
1397
1496
  }
1398
1497
  }
1498
+ deleteTemporaryElements(board);
1399
1499
  }
1400
1500
  catch (error) {
1401
1501
  console.error(error);
@@ -1404,6 +1504,33 @@ function withSelection(board) {
1404
1504
  };
1405
1505
  return board;
1406
1506
  }
1507
+ function getTemporaryElements(board) {
1508
+ return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
1509
+ }
1510
+ function deleteTemporaryElements(board) {
1511
+ BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
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
+ }
1407
1534
 
1408
1535
  class PlaitElementComponent {
1409
1536
  constructor(renderer2, viewContainerRef) {
@@ -1457,7 +1584,7 @@ class PlaitElementComponent {
1457
1584
  const current = {
1458
1585
  element: this.element,
1459
1586
  selection: this.selection,
1460
- board: this.board,
1587
+ board: this.board
1461
1588
  };
1462
1589
  if (this.context) {
1463
1590
  const previous = { ...this.context };
@@ -1557,10 +1684,6 @@ class PlaitBoardComponent {
1557
1684
  this.elementRef = elementRef;
1558
1685
  this.hasInitialized = false;
1559
1686
  this.destroy$ = new Subject();
1560
- this.viewportState = {
1561
- zoom: 1,
1562
- autoFitPadding: 8
1563
- };
1564
1687
  this.plaitValue = [];
1565
1688
  this.plaitPlugins = [];
1566
1689
  this.plaitChange = new EventEmitter();
@@ -1569,9 +1692,6 @@ class PlaitBoardComponent {
1569
1692
  return index;
1570
1693
  };
1571
1694
  }
1572
- get isFocused() {
1573
- return this.board?.selection;
1574
- }
1575
1695
  get host() {
1576
1696
  return this.svg.nativeElement;
1577
1697
  }
@@ -1581,8 +1701,8 @@ class PlaitBoardComponent {
1581
1701
  get readonly() {
1582
1702
  return this.board.options.readonly;
1583
1703
  }
1584
- get focused() {
1585
- return this.isFocused;
1704
+ get isFocused() {
1705
+ return PlaitBoard.isFocus(this.board);
1586
1706
  }
1587
1707
  get nativeElement() {
1588
1708
  return this.elementRef.nativeElement;
@@ -1592,10 +1712,12 @@ class PlaitBoardComponent {
1592
1712
  const roughSVG = rough.svg(this.host, {
1593
1713
  options: { roughness: 0, strokeWidth: 1 }
1594
1714
  });
1715
+ this.roughSVG = roughSVG;
1595
1716
  this.initializePlugins();
1596
1717
  this.initializeEvents();
1597
1718
  this.viewportScrollListener();
1598
1719
  this.elementResizeListener();
1720
+ this.mouseLeaveListener();
1599
1721
  BOARD_TO_COMPONENT.set(this.board, this);
1600
1722
  BOARD_TO_ROUGH_SVG.set(this.board, roughSVG);
1601
1723
  BOARD_TO_HOST.set(this.board, this.host);
@@ -1612,13 +1734,19 @@ class PlaitBoardComponent {
1612
1734
  });
1613
1735
  this.hasInitialized = true;
1614
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
+ }
1615
1744
  ngOnChanges(changes) {
1616
1745
  if (this.hasInitialized) {
1617
1746
  const valueChange = changes['plaitValue'];
1618
1747
  const options = changes['plaitOptions'];
1619
- if (valueChange) {
1748
+ if (valueChange)
1620
1749
  this.board.children = valueChange.currentValue;
1621
- }
1622
1750
  if (options)
1623
1751
  this.board.options = options.currentValue;
1624
1752
  this.cdr.markForCheck();
@@ -1627,20 +1755,17 @@ class PlaitBoardComponent {
1627
1755
  ngAfterViewInit() {
1628
1756
  this.plaitBoardInitialized.emit(this.board);
1629
1757
  this.initViewportContainer();
1630
- this.calcViewBox(this.viewportState.zoom);
1631
- this.initViewport();
1758
+ initializeViewport(this.board);
1759
+ initializeViewportContainerOffset(this.board);
1632
1760
  }
1633
1761
  initializePlugins() {
1634
- let board = withHandPointer(withHistory(withSelection(withBoard(createBoard(this.plaitValue, this.plaitOptions)))));
1762
+ let board = withHandPointer(withHistory(withSelection(withBoard(withViewport(createBoard(this.plaitValue, this.plaitOptions))))));
1635
1763
  this.plaitPlugins.forEach(plugin => {
1636
1764
  board = plugin(board);
1637
1765
  });
1638
1766
  this.board = board;
1639
1767
  if (this.plaitViewport) {
1640
1768
  this.board.viewport = this.plaitViewport;
1641
- this.updateViewportState({
1642
- zoom: this.plaitViewport?.zoom ?? 1
1643
- });
1644
1769
  }
1645
1770
  }
1646
1771
  initializeEvents() {
@@ -1676,31 +1801,31 @@ class PlaitBoardComponent {
1676
1801
  this.board.dblclick(event);
1677
1802
  });
1678
1803
  fromEvent(document, 'keydown')
1679
- .pipe(takeUntil(this.destroy$), filter(() => {
1680
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1681
- }))
1804
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1682
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
+ }
1683
1814
  this.board?.keydown(event);
1684
1815
  });
1685
1816
  fromEvent(document, 'keyup')
1686
- .pipe(takeUntil(this.destroy$), filter(() => {
1687
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1688
- }))
1817
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1689
1818
  .subscribe((event) => {
1690
1819
  this.board?.keyup(event);
1691
1820
  });
1692
1821
  fromEvent(document, 'copy')
1693
- .pipe(takeUntil(this.destroy$), filter(() => {
1694
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1695
- }))
1822
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
1696
1823
  .subscribe((event) => {
1697
1824
  event.preventDefault();
1698
1825
  this.board?.setFragment(event.clipboardData);
1699
1826
  });
1700
1827
  fromEvent(document, 'paste')
1701
- .pipe(takeUntil(this.destroy$), filter(() => {
1702
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection && !this.readonly;
1703
- }))
1828
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1704
1829
  .subscribe((clipboardEvent) => {
1705
1830
  const mousePoint = BOARD_TO_MOVING_POINT.get(this.board);
1706
1831
  const rect = this.nativeElement.getBoundingClientRect();
@@ -1710,9 +1835,7 @@ class PlaitBoardComponent {
1710
1835
  }
1711
1836
  });
1712
1837
  fromEvent(document, 'cut')
1713
- .pipe(takeUntil(this.destroy$), filter(() => {
1714
- return !IS_TEXT_EDITABLE.get(this.board) && !!this.board.selection;
1715
- }))
1838
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
1716
1839
  .subscribe((event) => {
1717
1840
  event.preventDefault();
1718
1841
  this.board?.setFragment(event.clipboardData);
@@ -1721,208 +1844,42 @@ class PlaitBoardComponent {
1721
1844
  }
1722
1845
  viewportScrollListener() {
1723
1846
  fromEvent(this.viewportContainer.nativeElement, 'scroll')
1724
- .pipe(takeUntil(this.destroy$), filter(() => {
1725
- return !!this.isFocused;
1726
- }))
1847
+ .pipe(takeUntil(this.destroy$), filter(() => this.isFocused))
1727
1848
  .subscribe((event) => {
1728
1849
  const { scrollLeft, scrollTop } = event.target;
1729
- const left = this.viewportState.scrollLeft;
1730
- const top = this.viewportState.scrollTop;
1731
- if (Math.abs(left - scrollLeft) >= 1 || Math.abs(top - scrollTop) >= 1) {
1732
- this.setScrollLeft(scrollLeft);
1733
- this.setScrollTop(scrollTop);
1734
- this.setViewport();
1735
- }
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);
1736
1854
  });
1737
1855
  }
1738
1856
  elementResizeListener() {
1739
1857
  this.resizeObserver = new ResizeObserver(() => {
1740
1858
  this.initViewportContainer();
1741
- this.calcViewBox(this.board.viewport.zoom);
1742
- this.updateViewBoxStyles();
1743
- this.updateViewportScrolling();
1744
- this.setViewport();
1745
1859
  });
1746
1860
  this.resizeObserver.observe(this.nativeElement);
1747
1861
  }
1748
- updateViewportState(state) {
1749
- this.viewportState = {
1750
- ...this.viewportState,
1751
- ...state
1752
- };
1753
- }
1754
1862
  initViewportContainer() {
1755
- const { width, height } = getBoardClientBox(this.board);
1863
+ const { width, height } = getViewportContainerRect(this.board);
1756
1864
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'width', `${width}px`);
1757
1865
  this.renderer2.setStyle(this.viewportContainer.nativeElement, 'height', `${height}px`);
1758
1866
  }
1759
- initViewport(viewport = this.board.viewport) {
1760
- const originationCoord = viewport?.originationCoord;
1761
- const zoom = viewport?.zoom ?? this.viewportState.zoom;
1762
- if (originationCoord) {
1763
- const matrix = this.getMatrix();
1764
- const [pointX, pointY] = invertViewportCoordinates([0, 0], matrix);
1765
- const scrollLeft = this.viewportState.scrollLeft;
1766
- const scrollTop = this.viewportState.scrollTop;
1767
- const left = scrollLeft + (originationCoord[0] - pointX) * zoom;
1768
- const top = scrollTop + (originationCoord[1] - pointY) * zoom;
1769
- this.setScroll(left, top);
1770
- }
1771
- else {
1772
- this.adaptHandle();
1773
- }
1774
- }
1775
- calcViewBox(zoom = this.viewportState.zoom) {
1776
- zoom = clampZoomLevel(zoom);
1777
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1778
- const viewportContainerBox = getViewportContainerBox(this.board);
1779
- const groupBBox = getRootGroupBBox(this.board, zoom);
1780
- const horizontalPadding = viewportContainerBox.width / 2;
1781
- const verticalPadding = viewportContainerBox.height / 2;
1782
- const viewportWidth = (groupBBox.right - groupBBox.left) * zoom + 2 * horizontalPadding + scrollBarWidth;
1783
- const viewportHeight = (groupBBox.bottom - groupBBox.top) * zoom + 2 * verticalPadding + scrollBarWidth;
1784
- const viewBox = [
1785
- groupBBox.left - horizontalPadding / zoom,
1786
- groupBBox.top - verticalPadding / zoom,
1787
- viewportWidth / zoom,
1788
- viewportHeight / zoom
1789
- ];
1790
- const matrix = this.getMatrix();
1791
- let scrollLeft;
1792
- let scrollTop;
1793
- if (matrix.length > 0) {
1794
- // focusPoint
1795
- const focusX = viewportContainerBox.x + viewportContainerBox.width / 2;
1796
- const focusY = viewportContainerBox.y + viewportContainerBox.height / 2;
1797
- const viewportContainerPoint = [focusX - viewportContainerBox.x, focusY - viewportContainerBox.y, 1];
1798
- const point = invertViewportCoordinates([viewportContainerPoint[0], viewportContainerPoint[1]], matrix);
1799
- const newMatrix = [zoom, 0, 0, 0, zoom, 0, -zoom * viewBox[0], -zoom * viewBox[1], 1];
1800
- const newPoint = transformMat3([], point, newMatrix);
1801
- scrollLeft = newPoint[0] - viewportContainerPoint[0];
1802
- scrollTop = newPoint[1] - viewportContainerPoint[1];
1803
- }
1804
- else {
1805
- scrollLeft = horizontalPadding;
1806
- scrollTop = verticalPadding;
1807
- }
1808
- this.updateViewportState({
1809
- viewportWidth,
1810
- viewportHeight,
1811
- zoom,
1812
- viewBox
1813
- });
1814
- this.setScrollLeft(scrollLeft);
1815
- this.setScrollTop(scrollTop);
1816
- }
1817
- getMatrix() {
1818
- const { viewBox, zoom, scrollLeft, scrollTop } = this.viewportState;
1819
- if (scrollLeft >= 0 && scrollTop >= 0) {
1820
- return [zoom, 0, 0, 0, zoom, 0, -scrollLeft - zoom * viewBox[0], -scrollTop - zoom * viewBox[1], 1];
1821
- }
1822
- return [];
1823
- }
1824
- setScrollLeft(left) {
1825
- const viewportContainerBox = getViewportContainerBox(this.board);
1826
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1827
- const { viewportWidth } = this.viewportState;
1828
- const width = viewportWidth - viewportContainerBox.width + scrollBarWidth;
1829
- this.viewportState.scrollLeft = left < 0 ? 0 : left > width ? width : left;
1830
- }
1831
- setScrollTop(top) {
1832
- const viewportContainerBox = getViewportContainerBox(this.board);
1833
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1834
- const { viewportHeight } = this.viewportState;
1835
- const height = viewportHeight - viewportContainerBox.height + scrollBarWidth;
1836
- this.viewportState.scrollTop = top < 0 ? 0 : top > height ? height : top;
1837
- }
1838
- setScroll(left, top) {
1839
- this.setScrollLeft(left);
1840
- this.setScrollTop(top);
1841
- this.updateViewBoxStyles();
1842
- this.updateViewportScrolling();
1843
- this.setViewport();
1844
- }
1845
- updateViewBoxStyles() {
1846
- const { host, viewportState } = this;
1847
- const { viewportWidth, viewportHeight, viewBox } = viewportState;
1848
- this.renderer2.setStyle(host, 'display', 'block');
1849
- this.renderer2.setStyle(host, 'width', `${viewportWidth}px`);
1850
- this.renderer2.setStyle(host, 'height', `${viewportHeight}px`);
1851
- if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
1852
- this.renderer2.setAttribute(host, 'viewBox', viewBox.join(' '));
1853
- }
1854
- }
1855
- updateViewportScrolling() {
1856
- const { viewportContainer, viewportState } = this;
1857
- const { scrollLeft, scrollTop } = viewportState;
1858
- viewportContainer.nativeElement.scrollLeft = scrollLeft;
1859
- viewportContainer.nativeElement.scrollTop = scrollTop;
1860
- }
1861
- setViewport() {
1862
- const viewport = this.board?.viewport;
1863
- const oldOriginationCoord = viewport?.originationCoord ?? [];
1864
- const matrix = this.getMatrix();
1865
- const originationCoord = invertViewportCoordinates([0, 0], matrix);
1866
- if (!originationCoord.every((item, index) => item === oldOriginationCoord[index])) {
1867
- Transforms.setViewport(this.board, {
1868
- ...viewport,
1869
- zoom: this.viewportState.zoom,
1870
- originationCoord
1871
- });
1872
- }
1873
- }
1874
1867
  adaptHandle() {
1875
- const containerBox = getViewportContainerBox(this.board);
1876
- const rootGroup = this.host.firstChild;
1877
- const matrix = this.getMatrix();
1878
- const rootGroupBox = rootGroup.getBBox();
1879
- const centerPoint = [containerBox.width / 2, containerBox.height / 2];
1880
- const rootGroupCenterX = rootGroupBox.x + rootGroupBox.width / 2;
1881
- const rootGroupCenterY = rootGroupBox.y + rootGroupBox.height / 2;
1882
- const transformedPoint = transformMat3([], [rootGroupCenterX, rootGroupCenterY, 1], matrix);
1883
- const offsetLeft = centerPoint[0] - transformedPoint[0];
1884
- const offsetTop = centerPoint[1] - transformedPoint[1];
1885
- const { autoFitPadding, zoom, scrollLeft, scrollTop } = this.viewportState;
1886
- const viewportWidth = containerBox.width - 2 * autoFitPadding;
1887
- const viewportHeight = containerBox.height - 2 * autoFitPadding;
1888
- let newZoom = zoom;
1889
- if (viewportWidth < rootGroupBox.width || viewportHeight < rootGroupBox.height) {
1890
- newZoom = Math.min(viewportWidth / rootGroupBox.width, viewportHeight / rootGroupBox.height);
1891
- }
1892
- else {
1893
- newZoom = 1;
1894
- }
1895
- this.setScrollLeft(scrollLeft - offsetLeft);
1896
- this.setScrollTop(scrollTop - offsetTop);
1897
- this.calcViewBox(newZoom);
1898
- this.updateViewBoxStyles();
1899
- this.updateViewportScrolling();
1900
- this.setViewport();
1868
+ fitViewport(this.board);
1901
1869
  }
1902
- zoomInHandle() {
1903
- this.calcViewBox(this.viewportState.zoom + 0.1);
1904
- this.updateViewBoxStyles();
1905
- this.updateViewportScrolling();
1906
- this.setViewport();
1870
+ zoomInHandle(isCenter = true) {
1871
+ changeZoom(this.board, this.board.viewport.zoom + 0.1, isCenter);
1907
1872
  }
1908
1873
  zoomOutHandle() {
1909
- this.calcViewBox(this.viewportState.zoom - 0.1);
1910
- this.updateViewBoxStyles();
1911
- this.updateViewportScrolling();
1912
- this.setViewport();
1874
+ changeZoom(this.board, this.board.viewport.zoom - 0.1);
1913
1875
  }
1914
1876
  resetZoomHandel() {
1915
- this.calcViewBox(1);
1916
- this.updateViewBoxStyles();
1917
- this.updateViewportScrolling();
1918
- this.setViewport();
1877
+ changeZoom(this.board, 1);
1919
1878
  }
1920
1879
  ngOnDestroy() {
1921
1880
  this.destroy$.next();
1922
1881
  this.destroy$.complete();
1923
- if (this.resizeObserver) {
1924
- this.resizeObserver.disconnect();
1925
- }
1882
+ this.resizeObserver && this.resizeObserver?.disconnect();
1926
1883
  BOARD_TO_ROUGH_SVG.delete(this.board);
1927
1884
  BOARD_TO_COMPONENT.delete(this.board);
1928
1885
  BOARD_TO_ROUGH_SVG.delete(this.board);
@@ -1933,40 +1890,9 @@ class PlaitBoardComponent {
1933
1890
  markForCheck() {
1934
1891
  this.cdr.markForCheck();
1935
1892
  }
1936
- scrollToRectangle(client) {
1937
- this.calcViewBox();
1938
- this.updateViewBoxStyles();
1939
- this.updateViewportScrolling();
1940
- this.setViewport();
1941
- const svgRect = this.host.getBoundingClientRect();
1942
- const viewportContainerBox = getViewportContainerBox(this.board);
1943
- if (svgRect.width > viewportContainerBox.width || svgRect.height > viewportContainerBox.height) {
1944
- const scrollBarWidth = this.plaitOptions?.hideScrollbar ? SCROLL_BAR_WIDTH : 0;
1945
- const matrix = this.getMatrix();
1946
- const [nodePointX, nodePointY] = convertToViewportCoordinates([client.x, client.y], matrix);
1947
- const [fullNodePointX, fullNodePointY] = convertToViewportCoordinates([client.x + client.width, client.y + client.height], matrix);
1948
- let newLeft = this.viewportState.scrollLeft;
1949
- let newTop = this.viewportState.scrollTop;
1950
- if (nodePointX < 0) {
1951
- newLeft -= Math.abs(nodePointX);
1952
- }
1953
- if (nodePointX > 0 && fullNodePointX > viewportContainerBox.width) {
1954
- newLeft += fullNodePointX - viewportContainerBox.width + scrollBarWidth;
1955
- }
1956
- if (nodePointY < 0) {
1957
- newTop -= Math.abs(nodePointY);
1958
- }
1959
- if (nodePointY > 0 && fullNodePointY > viewportContainerBox.height) {
1960
- newTop += fullNodePointY - viewportContainerBox.height + scrollBarWidth;
1961
- }
1962
- if (newLeft !== this.viewportState.scrollLeft || newTop !== this.viewportState.scrollTop) {
1963
- this.setScroll(newLeft, newTop);
1964
- }
1965
- }
1966
- }
1967
1893
  }
1968
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 });
1969
- 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: `
1970
1896
  <div class="viewport-container" #viewportContainer>
1971
1897
  <svg #svg width="100%" height="100%" style="position: relative;"><g class="element-host"></g></svg>
1972
1898
  <plait-element
@@ -2032,7 +1958,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2032
1958
  }], readonly: [{
2033
1959
  type: HostBinding,
2034
1960
  args: ['class.readonly']
2035
- }], focused: [{
1961
+ }], isFocused: [{
2036
1962
  type: HostBinding,
2037
1963
  args: ['class.focused']
2038
1964
  }], svg: [{
@@ -2129,6 +2055,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2129
2055
  }]
2130
2056
  }] });
2131
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
+
2132
2140
  /*
2133
2141
  * Public API Surface of plait
2134
2142
  */
@@ -2137,5 +2145,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.11", ngImpo
2137
2145
  * Generated bundle index. Do not edit.
2138
2146
  */
2139
2147
 
2140
- 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 };
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 };
2141
2149
  //# sourceMappingURL=plait-core.mjs.map