@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.
- package/board/board.component.d.ts +4 -17
- package/board/board.component.interface.d.ts +0 -2
- package/constants/selection.d.ts +1 -0
- package/esm2020/board/board.component.interface.mjs +1 -1
- package/esm2020/board/board.component.mjs +49 -248
- package/esm2020/constants/selection.mjs +2 -0
- package/esm2020/core/element/element.component.mjs +2 -2
- package/esm2020/interfaces/board.mjs +11 -2
- package/esm2020/interfaces/viewport.mjs +1 -1
- package/esm2020/plugins/create-board.mjs +2 -1
- package/esm2020/plugins/with-hand.mjs +9 -6
- package/esm2020/plugins/with-moving.mjs +91 -0
- package/esm2020/plugins/with-selection.mjs +43 -12
- package/esm2020/plugins/with-viewport.mjs +11 -0
- package/esm2020/public-api.mjs +2 -1
- package/esm2020/utils/board.mjs +7 -3
- package/esm2020/utils/common.mjs +15 -0
- package/esm2020/utils/element.mjs +3 -3
- package/esm2020/utils/helper.mjs +14 -1
- package/esm2020/utils/index.mjs +4 -2
- package/esm2020/utils/moving-element.mjs +15 -0
- package/esm2020/utils/selected-element.mjs +14 -1
- package/esm2020/utils/viewport.mjs +170 -0
- package/esm2020/utils/weak-maps.mjs +3 -1
- package/fesm2015/plait-core.mjs +548 -561
- package/fesm2015/plait-core.mjs.map +1 -1
- package/fesm2020/plait-core.mjs +548 -558
- package/fesm2020/plait-core.mjs.map +1 -1
- package/interfaces/board.d.ts +4 -10
- package/interfaces/viewport.d.ts +2 -2
- package/package.json +1 -1
- package/plugins/with-moving.d.ts +2 -0
- package/plugins/with-selection.d.ts +4 -1
- package/plugins/with-viewport.d.ts +2 -0
- package/public-api.d.ts +1 -0
- package/utils/board.d.ts +1 -1
- package/utils/common.d.ts +1 -0
- package/utils/helper.d.ts +9 -0
- package/utils/index.d.ts +3 -1
- package/utils/moving-element.d.ts +5 -0
- package/utils/selected-element.d.ts +2 -0
- package/utils/viewport.d.ts +29 -0
- package/utils/weak-maps.d.ts +2 -0
- package/esm2020/utils/matrix.mjs +0 -170
- package/utils/matrix.d.ts +0 -82
package/fesm2020/plait-core.mjs
CHANGED
|
@@ -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
|
-
|
|
12
|
-
const
|
|
13
|
-
const
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
|
433
|
-
|
|
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
|
|
475
|
-
|
|
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
|
|
1307
|
-
const left = event.x - plaitBoardMove.x;
|
|
1308
|
-
const top = event.y - plaitBoardMove.y;
|
|
1309
|
-
const
|
|
1310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1366
|
-
|
|
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
|
|
1603
|
-
return this.
|
|
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
|
-
|
|
1649
|
-
this.
|
|
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
|
|
1748
|
-
const
|
|
1749
|
-
|
|
1750
|
-
|
|
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 } =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
}],
|
|
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,
|
|
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
|