@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.
- 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 +54 -13
- package/esm2020/plugins/with-viewport.mjs +11 -0
- package/esm2020/public-api.mjs +3 -1
- package/esm2020/transforms/selection.mjs +10 -2
- 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 +5 -1
- package/fesm2015/plait-core.mjs +567 -562
- package/fesm2015/plait-core.mjs.map +1 -1
- package/fesm2020/plait-core.mjs +567 -559
- 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 +6 -1
- package/plugins/with-viewport.d.ts +2 -0
- package/public-api.d.ts +2 -0
- package/transforms/selection.d.ts +3 -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 +3 -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) => {
|
|
@@ -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
|
-
//
|
|
375
|
-
|
|
376
|
-
const
|
|
377
|
-
const
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
|
424
|
-
|
|
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
|
|
466
|
-
|
|
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
|
|
1298
|
-
const left = event.x - plaitBoardMove.x;
|
|
1299
|
-
const top = event.y - plaitBoardMove.y;
|
|
1300
|
-
const
|
|
1301
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1357
|
-
|
|
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
|
|
1387
|
-
|
|
1388
|
-
|
|
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
|
|
1585
|
-
return this.
|
|
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
|
-
|
|
1631
|
-
this.
|
|
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
|
|
1730
|
-
const
|
|
1731
|
-
|
|
1732
|
-
|
|
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 } =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
}],
|
|
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,
|
|
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
|