@plait/core 0.24.0-next.1 → 0.24.0-next.11
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 +1 -1
- package/board/board.component.interface.d.ts +3 -1
- package/constants/selection.d.ts +2 -0
- package/core/children/children.component.d.ts +1 -1
- package/core/element/element.component.d.ts +1 -1
- package/core/element/plugin-element.d.ts +1 -1
- package/core/island/island-base.component.d.ts +4 -2
- package/esm2022/board/board.component.interface.mjs +2 -0
- package/esm2022/board/board.component.mjs +405 -0
- package/esm2022/constants/selection.mjs +4 -0
- package/{esm2020 → esm2022}/core/children/children.component.mjs +6 -6
- package/{esm2020 → esm2022}/core/element/element.component.mjs +4 -4
- package/{esm2020 → esm2022}/core/element/plugin-element.mjs +4 -4
- package/esm2022/core/island/island-base.component.mjs +72 -0
- package/{esm2020 → esm2022}/interfaces/board.mjs +1 -1
- package/esm2022/interfaces/direction.mjs +8 -0
- package/{esm2020 → esm2022}/interfaces/index.mjs +2 -1
- package/esm2022/interfaces/rectangle-client.mjs +68 -0
- package/{esm2020 → esm2022}/plait.module.mjs +5 -5
- package/esm2022/plugins/create-board.mjs +106 -0
- package/esm2022/plugins/with-hotkey.mjs +62 -0
- package/esm2022/plugins/with-moving.mjs +116 -0
- package/esm2022/plugins/with-selection.mjs +193 -0
- package/{esm2020 → esm2022}/services/image-context.service.mjs +4 -4
- package/esm2022/transforms/element.mjs +22 -0
- package/esm2022/transforms/index.mjs +13 -0
- package/esm2022/transforms/selection.mjs +26 -0
- package/esm2022/utils/dom/common.mjs +76 -0
- package/esm2022/utils/dom/foreign.mjs +25 -0
- package/esm2022/utils/draw/line.mjs +47 -0
- package/esm2022/utils/draw/rectangle.mjs +34 -0
- package/esm2022/utils/element.mjs +71 -0
- package/esm2022/utils/math.mjs +176 -0
- package/esm2022/utils/reaction-manager.mjs +340 -0
- package/esm2022/utils/touch.mjs +35 -0
- package/esm2022/utils/weak-maps.mjs +21 -0
- package/{fesm2020 → fesm2022}/plait-core.mjs +2587 -1933
- package/fesm2022/plait-core.mjs.map +1 -0
- package/interfaces/board.d.ts +2 -1
- package/interfaces/direction.d.ts +7 -0
- package/interfaces/index.d.ts +1 -0
- package/interfaces/rectangle-client.d.ts +14 -0
- package/package.json +15 -14
- package/plugins/with-selection.d.ts +5 -1
- package/styles/styles.scss +1 -1
- package/transforms/element.d.ts +6 -0
- package/transforms/index.d.ts +1 -0
- package/transforms/selection.d.ts +2 -2
- package/utils/dom/common.d.ts +6 -0
- package/utils/dom/foreign.d.ts +2 -1
- package/utils/draw/line.d.ts +1 -1
- package/utils/draw/rectangle.d.ts +3 -0
- package/utils/element.d.ts +5 -0
- package/utils/math.d.ts +9 -0
- package/utils/reaction-manager.d.ts +41 -0
- package/utils/touch.d.ts +14 -1
- package/utils/weak-maps.d.ts +4 -2
- package/esm2020/board/board.component.interface.mjs +0 -2
- package/esm2020/board/board.component.mjs +0 -407
- package/esm2020/constants/selection.mjs +0 -2
- package/esm2020/core/island/island-base.component.mjs +0 -69
- package/esm2020/interfaces/rectangle-client.mjs +0 -40
- package/esm2020/plugins/create-board.mjs +0 -101
- package/esm2020/plugins/with-hotkey.mjs +0 -57
- package/esm2020/plugins/with-moving.mjs +0 -97
- package/esm2020/plugins/with-selection.mjs +0 -183
- package/esm2020/transforms/index.mjs +0 -12
- package/esm2020/transforms/selection.mjs +0 -16
- package/esm2020/utils/dom/common.mjs +0 -53
- package/esm2020/utils/dom/foreign.mjs +0 -19
- package/esm2020/utils/draw/line.mjs +0 -43
- package/esm2020/utils/draw/rectangle.mjs +0 -26
- package/esm2020/utils/element.mjs +0 -44
- package/esm2020/utils/math.mjs +0 -48
- package/esm2020/utils/touch.mjs +0 -8
- package/esm2020/utils/weak-maps.mjs +0 -22
- package/fesm2015/plait-core.mjs +0 -3454
- package/fesm2015/plait-core.mjs.map +0 -1
- package/fesm2020/plait-core.mjs.map +0 -1
- /package/{esm2020 → esm2022}/constants/index.mjs +0 -0
- /package/{esm2020 → esm2022}/constants/keycodes.mjs +0 -0
- /package/{esm2020 → esm2022}/constants/resize.mjs +0 -0
- /package/{esm2020 → esm2022}/core/children/effect.mjs +0 -0
- /package/{esm2020 → esm2022}/core/element/context-change.mjs +0 -0
- /package/{esm2020 → esm2022}/core/element/context.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/custom-types.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/element.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/history.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/node.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/operation.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/path-ref.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/path.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/plugin-key.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/plugin.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/point.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/pointer.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/selection.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/theme.mjs +0 -0
- /package/{esm2020 → esm2022}/interfaces/viewport.mjs +0 -0
- /package/{esm2020 → esm2022}/plait-core.mjs +0 -0
- /package/{esm2020 → esm2022}/plugins/with-board.mjs +0 -0
- /package/{esm2020 → esm2022}/plugins/with-hand.mjs +0 -0
- /package/{esm2020 → esm2022}/plugins/with-history.mjs +0 -0
- /package/{esm2020 → esm2022}/plugins/with-options.mjs +0 -0
- /package/{esm2020 → esm2022}/plugins/with-viewport.mjs +0 -0
- /package/{esm2020 → esm2022}/public-api.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/core/create-board.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/core/fake-weak-map.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/core/index.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/fake-events/event-objects.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/fake-events/index.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/index.mjs +0 -0
- /package/{esm2020 → esm2022}/testing/test-element.mjs +0 -0
- /package/{esm2020 → esm2022}/transforms/board.mjs +0 -0
- /package/{esm2020 → esm2022}/transforms/general.mjs +0 -0
- /package/{esm2020 → esm2022}/transforms/node.mjs +0 -0
- /package/{esm2020 → esm2022}/transforms/theme.mjs +0 -0
- /package/{esm2020 → esm2022}/transforms/viewport.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/board.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/clipboard.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/common.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/dom/environment.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/dom/index.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/draw/arrow.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/draw/circle.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/environment.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/helper.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/history.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/hotkeys.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/id-creator.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/index.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/moving-element.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/selected-element.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/to-image.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/tree.mjs +0 -0
- /package/{esm2020 → esm2022}/utils/viewport.mjs +0 -0
|
@@ -3,8 +3,8 @@ import { Directive, Input, Injectable, Component, ChangeDetectionStrategy, Event
|
|
|
3
3
|
import rough from 'roughjs/bin/rough';
|
|
4
4
|
import { timer, Subject, fromEvent } from 'rxjs';
|
|
5
5
|
import { takeUntil, filter, tap } from 'rxjs/operators';
|
|
6
|
-
import produce, { createDraft, finishDraft, isDraft } from 'immer';
|
|
7
6
|
import { isKeyHotkey, isHotkey } from 'is-hotkey';
|
|
7
|
+
import produce, { createDraft, finishDraft, isDraft } from 'immer';
|
|
8
8
|
import * as i1 from '@angular/common';
|
|
9
9
|
import { CommonModule } from '@angular/common';
|
|
10
10
|
|
|
@@ -27,9 +27,18 @@ const BOARD_TO_IS_SELECTION_MOVING = new WeakMap();
|
|
|
27
27
|
// save no standard selected elements
|
|
28
28
|
const BOARD_TO_TEMPORARY_ELEMENTS = new WeakMap();
|
|
29
29
|
const BOARD_TO_MOVING_ELEMENT = new WeakMap();
|
|
30
|
-
const IS_PREVENT_TOUCH_MOVE = new WeakMap();
|
|
31
30
|
const PATH_REFS = new WeakMap();
|
|
32
31
|
|
|
32
|
+
var PlaitPointerType;
|
|
33
|
+
(function (PlaitPointerType) {
|
|
34
|
+
PlaitPointerType["hand"] = "hand";
|
|
35
|
+
PlaitPointerType["selection"] = "selection";
|
|
36
|
+
})(PlaitPointerType || (PlaitPointerType = {}));
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Extendable Custom Types Interface
|
|
40
|
+
*/
|
|
41
|
+
|
|
33
42
|
function depthFirstRecursion(node, callback, recursion, isReverse) {
|
|
34
43
|
if (!recursion || recursion(node)) {
|
|
35
44
|
let children = [];
|
|
@@ -54,1886 +63,2322 @@ const getIsRecursionFunc = (board) => {
|
|
|
54
63
|
};
|
|
55
64
|
};
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
const calcRectangleClient = (node) => {
|
|
65
|
-
const nodeRectangle = board.getRectangle(node);
|
|
66
|
-
if (nodeRectangle) {
|
|
67
|
-
boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
|
|
68
|
-
boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
|
|
69
|
-
boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
|
|
70
|
-
boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
elements.forEach(element => {
|
|
74
|
-
if (recursion) {
|
|
75
|
-
depthFirstRecursion(element, node => calcRectangleClient(node), node => board.isRecursion(node));
|
|
66
|
+
const SELECTION_BORDER_COLOR = '#6698FF';
|
|
67
|
+
const SELECTION_FILL_COLOR = '#6698FF19'; // 主色 0.1 透明度
|
|
68
|
+
const Selection = {
|
|
69
|
+
isCollapsed(selection) {
|
|
70
|
+
if (selection.anchor[0] == selection.focus[0] && selection.anchor[1] === selection.focus[1]) {
|
|
71
|
+
return true;
|
|
76
72
|
}
|
|
77
73
|
else {
|
|
78
|
-
|
|
74
|
+
return false;
|
|
79
75
|
}
|
|
80
|
-
});
|
|
81
|
-
if (boundaryBox.left === Number.MAX_VALUE) {
|
|
82
|
-
return {
|
|
83
|
-
x: 0,
|
|
84
|
-
y: 0,
|
|
85
|
-
width: 0,
|
|
86
|
-
height: 0
|
|
87
|
-
};
|
|
88
76
|
}
|
|
89
|
-
|
|
90
|
-
x: boundaryBox.left,
|
|
91
|
-
y: boundaryBox.top,
|
|
92
|
-
width: boundaryBox.right - boundaryBox.left,
|
|
93
|
-
height: boundaryBox.bottom - boundaryBox.top
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
function getBoardRectangle(board) {
|
|
97
|
-
return getRectangleByElements(board, board.children, true);
|
|
98
|
-
}
|
|
77
|
+
};
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
79
|
+
const getHitElements = (board, selection, match = () => true) => {
|
|
80
|
+
const realSelection = selection || board.selection;
|
|
81
|
+
const selectedElements = [];
|
|
82
|
+
const isCollapsed = realSelection && realSelection.ranges.length === 1 && Selection.isCollapsed(realSelection.ranges[0]);
|
|
83
|
+
depthFirstRecursion(board, node => {
|
|
84
|
+
if (selectedElements.length > 0 && isCollapsed) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!PlaitBoard.isBoard(node) &&
|
|
88
|
+
match(node) &&
|
|
89
|
+
realSelection &&
|
|
90
|
+
realSelection.ranges.some(range => {
|
|
91
|
+
return board.isHitSelection(node, range);
|
|
92
|
+
})) {
|
|
93
|
+
selectedElements.push(node);
|
|
94
|
+
}
|
|
95
|
+
}, getIsRecursionFunc(board), true);
|
|
96
|
+
return selectedElements;
|
|
113
97
|
};
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
const getHitElementOfRoot = (board, rootElements, range) => {
|
|
99
|
+
const newRootElements = [...rootElements].reverse();
|
|
100
|
+
return newRootElements.find(item => {
|
|
101
|
+
return board.isHitSelection(item, range);
|
|
102
|
+
});
|
|
118
103
|
};
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
104
|
+
const isHitElements = (board, elements, ranges) => {
|
|
105
|
+
let isIntersectionElements = false;
|
|
106
|
+
if (elements.length) {
|
|
107
|
+
elements.map(item => {
|
|
108
|
+
if (!isIntersectionElements) {
|
|
109
|
+
isIntersectionElements = ranges.some(range => {
|
|
110
|
+
return board.isHitSelection(item, range);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return isIntersectionElements;
|
|
123
116
|
};
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
boardBackground: '#f9f8ed',
|
|
127
|
-
textColor: '#333333'
|
|
117
|
+
const cacheSelectedElements = (board, selectedElements) => {
|
|
118
|
+
BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
|
|
128
119
|
};
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
boardBackground: '#141414',
|
|
132
|
-
textColor: '#FFFFFF'
|
|
120
|
+
const getSelectedElements = (board) => {
|
|
121
|
+
return BOARD_TO_SELECTED_ELEMENT.get(board) || [];
|
|
133
122
|
};
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
123
|
+
const addSelectedElement = (board, element) => {
|
|
124
|
+
const selectedElements = getSelectedElements(board);
|
|
125
|
+
cacheSelectedElements(board, [...selectedElements, element]);
|
|
126
|
+
};
|
|
127
|
+
const removeSelectedElement = (board, element) => {
|
|
128
|
+
const selectedElements = getSelectedElements(board);
|
|
129
|
+
const newSelectedElements = selectedElements.filter(value => value !== element);
|
|
130
|
+
cacheSelectedElements(board, newSelectedElements);
|
|
131
|
+
};
|
|
132
|
+
const clearSelectedElement = (board) => {
|
|
133
|
+
cacheSelectedElements(board, []);
|
|
134
|
+
};
|
|
135
|
+
const isSelectedElement = (board, element) => {
|
|
136
|
+
const selectedElements = getSelectedElements(board);
|
|
137
|
+
return !!selectedElements.find(value => value === element);
|
|
138
138
|
};
|
|
139
|
-
const ThemeColors = [
|
|
140
|
-
DefaultThemeColor,
|
|
141
|
-
ColorfulThemeColor,
|
|
142
|
-
SoftThemeColor,
|
|
143
|
-
RetroThemeColor,
|
|
144
|
-
DarkThemeColor,
|
|
145
|
-
StarryThemeColor
|
|
146
|
-
];
|
|
147
|
-
|
|
148
|
-
// https://stackoverflow.com/a/6853926/232122
|
|
149
|
-
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
150
|
-
const A = x - x1;
|
|
151
|
-
const B = y - y1;
|
|
152
|
-
const C = x2 - x1;
|
|
153
|
-
const D = y2 - y1;
|
|
154
|
-
const dot = A * C + B * D;
|
|
155
|
-
const lenSquare = C * C + D * D;
|
|
156
|
-
let param = -1;
|
|
157
|
-
if (lenSquare !== 0) {
|
|
158
|
-
// in case of 0 length line
|
|
159
|
-
param = dot / lenSquare;
|
|
160
|
-
}
|
|
161
|
-
let xx, yy;
|
|
162
|
-
if (param < 0) {
|
|
163
|
-
xx = x1;
|
|
164
|
-
yy = y1;
|
|
165
|
-
}
|
|
166
|
-
else if (param > 1) {
|
|
167
|
-
xx = x2;
|
|
168
|
-
yy = y2;
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
xx = x1 + param * C;
|
|
172
|
-
yy = y1 + param * D;
|
|
173
|
-
}
|
|
174
|
-
const dx = x - xx;
|
|
175
|
-
const dy = y - yy;
|
|
176
|
-
return Math.hypot(dx, dy);
|
|
177
|
-
}
|
|
178
|
-
function rotate(x1, y1, x2, y2, angle) {
|
|
179
|
-
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
180
|
-
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
181
|
-
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
182
|
-
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
183
|
-
}
|
|
184
|
-
function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
|
|
185
|
-
const dx = x1 - x2;
|
|
186
|
-
const dy = y1 - y2;
|
|
187
|
-
return Math.hypot(dx, dy);
|
|
188
|
-
}
|
|
189
|
-
// https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
|
|
190
|
-
function distanceBetweenPointAndRectangle(x, y, rect) {
|
|
191
|
-
var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
|
|
192
|
-
var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
|
|
193
|
-
return Math.sqrt(dx * dx + dy * dy);
|
|
194
|
-
}
|
|
195
139
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
140
|
+
/**
|
|
141
|
+
* @license
|
|
142
|
+
* Copyright Google LLC All Rights Reserved.
|
|
143
|
+
*
|
|
144
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
145
|
+
* found in the LICENSE file at https://angular.io/license
|
|
146
|
+
*/
|
|
147
|
+
const MAC_ENTER = 3;
|
|
148
|
+
const BACKSPACE = 8;
|
|
149
|
+
const TAB = 9;
|
|
150
|
+
const NUM_CENTER = 12;
|
|
151
|
+
const ENTER = 13;
|
|
152
|
+
const SHIFT = 16;
|
|
153
|
+
const CONTROL = 17;
|
|
154
|
+
const ALT = 18;
|
|
155
|
+
const PAUSE = 19;
|
|
156
|
+
const CAPS_LOCK = 20;
|
|
157
|
+
const ESCAPE = 27;
|
|
158
|
+
const SPACE = 32;
|
|
159
|
+
const PAGE_UP = 33;
|
|
160
|
+
const PAGE_DOWN = 34;
|
|
161
|
+
const END = 35;
|
|
162
|
+
const HOME = 36;
|
|
163
|
+
const LEFT_ARROW = 37;
|
|
164
|
+
const UP_ARROW = 38;
|
|
165
|
+
const RIGHT_ARROW = 39;
|
|
166
|
+
const DOWN_ARROW = 40;
|
|
167
|
+
const PLUS_SIGN = 43;
|
|
168
|
+
const PRINT_SCREEN = 44;
|
|
169
|
+
const INSERT = 45;
|
|
170
|
+
const DELETE = 46;
|
|
171
|
+
const ZERO = 48;
|
|
172
|
+
const ONE = 49;
|
|
173
|
+
const TWO = 50;
|
|
174
|
+
const THREE = 51;
|
|
175
|
+
const FOUR = 52;
|
|
176
|
+
const FIVE = 53;
|
|
177
|
+
const SIX = 54;
|
|
178
|
+
const SEVEN = 55;
|
|
179
|
+
const EIGHT = 56;
|
|
180
|
+
const NINE = 57;
|
|
181
|
+
const FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
182
|
+
const FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
183
|
+
const QUESTION_MARK = 63;
|
|
184
|
+
const AT_SIGN = 64;
|
|
185
|
+
const A = 65;
|
|
186
|
+
const B = 66;
|
|
187
|
+
const C = 67;
|
|
188
|
+
const D = 68;
|
|
189
|
+
const E = 69;
|
|
190
|
+
const F = 70;
|
|
191
|
+
const G = 71;
|
|
192
|
+
const H = 72;
|
|
193
|
+
const I = 73;
|
|
194
|
+
const J = 74;
|
|
195
|
+
const K = 75;
|
|
196
|
+
const L = 76;
|
|
197
|
+
const M = 77;
|
|
198
|
+
const N = 78;
|
|
199
|
+
const O = 79;
|
|
200
|
+
const P = 80;
|
|
201
|
+
const Q = 81;
|
|
202
|
+
const R = 82;
|
|
203
|
+
const S = 83;
|
|
204
|
+
const T = 84;
|
|
205
|
+
const U = 85;
|
|
206
|
+
const V = 86;
|
|
207
|
+
const W = 87;
|
|
208
|
+
const X = 88;
|
|
209
|
+
const Y = 89;
|
|
210
|
+
const Z = 90;
|
|
211
|
+
const META = 91; // WIN_KEY_LEFT
|
|
212
|
+
const MAC_WK_CMD_LEFT = 91;
|
|
213
|
+
const MAC_WK_CMD_RIGHT = 93;
|
|
214
|
+
const CONTEXT_MENU = 93;
|
|
215
|
+
const NUMPAD_ZERO = 96;
|
|
216
|
+
const NUMPAD_ONE = 97;
|
|
217
|
+
const NUMPAD_TWO = 98;
|
|
218
|
+
const NUMPAD_THREE = 99;
|
|
219
|
+
const NUMPAD_FOUR = 100;
|
|
220
|
+
const NUMPAD_FIVE = 101;
|
|
221
|
+
const NUMPAD_SIX = 102;
|
|
222
|
+
const NUMPAD_SEVEN = 103;
|
|
223
|
+
const NUMPAD_EIGHT = 104;
|
|
224
|
+
const NUMPAD_NINE = 105;
|
|
225
|
+
const NUMPAD_MULTIPLY = 106;
|
|
226
|
+
const NUMPAD_PLUS = 107;
|
|
227
|
+
const NUMPAD_MINUS = 109;
|
|
228
|
+
const NUMPAD_PERIOD = 110;
|
|
229
|
+
const NUMPAD_DIVIDE = 111;
|
|
230
|
+
const F1 = 112;
|
|
231
|
+
const F2 = 113;
|
|
232
|
+
const F3 = 114;
|
|
233
|
+
const F4 = 115;
|
|
234
|
+
const F5 = 116;
|
|
235
|
+
const F6 = 117;
|
|
236
|
+
const F7 = 118;
|
|
237
|
+
const F8 = 119;
|
|
238
|
+
const F9 = 120;
|
|
239
|
+
const F10 = 121;
|
|
240
|
+
const F11 = 122;
|
|
241
|
+
const F12 = 123;
|
|
242
|
+
const NUM_LOCK = 144;
|
|
243
|
+
const SCROLL_LOCK = 145;
|
|
244
|
+
const FIRST_MEDIA = 166;
|
|
245
|
+
const FF_MINUS = 173;
|
|
246
|
+
const MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
247
|
+
const VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
248
|
+
const VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
249
|
+
const FF_MUTE = 181;
|
|
250
|
+
const FF_VOLUME_DOWN = 182;
|
|
251
|
+
const LAST_MEDIA = 183;
|
|
252
|
+
const FF_VOLUME_UP = 183;
|
|
253
|
+
const SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
254
|
+
const EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
255
|
+
const COMMA = 188;
|
|
256
|
+
const DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
257
|
+
const PERIOD = 190;
|
|
258
|
+
const SLASH = 191;
|
|
259
|
+
const APOSTROPHE = 192;
|
|
260
|
+
const TILDE = 192;
|
|
261
|
+
const OPEN_SQUARE_BRACKET = 219;
|
|
262
|
+
const BACKSLASH = 220;
|
|
263
|
+
const CLOSE_SQUARE_BRACKET = 221;
|
|
264
|
+
const SINGLE_QUOTE = 222;
|
|
265
|
+
const MAC_META = 224;
|
|
289
266
|
|
|
290
|
-
var
|
|
291
|
-
(function (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
})(PlaitPointerType || (PlaitPointerType = {}));
|
|
267
|
+
var ResizeCursorClass;
|
|
268
|
+
(function (ResizeCursorClass) {
|
|
269
|
+
ResizeCursorClass["ew-resize"] = "ew-resize";
|
|
270
|
+
})(ResizeCursorClass || (ResizeCursorClass = {}));
|
|
295
271
|
|
|
296
|
-
|
|
297
|
-
|
|
272
|
+
const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
|
|
273
|
+
const ACTIVE_STROKE_WIDTH = 1;
|
|
274
|
+
const SELECTION_RECTANGLE_CLASS_NAME = 'selection-rectangle';
|
|
275
|
+
|
|
276
|
+
const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
|
|
277
|
+
const HOST_CLASS_NAME = 'plait-board-container';
|
|
278
|
+
const SCROLL_BAR_WIDTH = 20;
|
|
279
|
+
const MAX_RADIUS = 16;
|
|
280
|
+
const POINTER_BUTTON = {
|
|
281
|
+
MAIN: 0,
|
|
282
|
+
WHEEL: 1,
|
|
283
|
+
SECONDARY: 2,
|
|
284
|
+
TOUCH: -1
|
|
285
|
+
};
|
|
286
|
+
const PRESS_AND_MOVE_BUFFER = 5;
|
|
287
|
+
|
|
288
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
289
|
+
function toPoint(x, y, container) {
|
|
290
|
+
const rect = container.getBoundingClientRect();
|
|
291
|
+
return [x - rect.x, y - rect.y];
|
|
292
|
+
}
|
|
293
|
+
function createG() {
|
|
294
|
+
const newG = document.createElementNS(NS, 'g');
|
|
295
|
+
return newG;
|
|
296
|
+
}
|
|
297
|
+
function createPath() {
|
|
298
|
+
const newG = document.createElementNS(NS, 'path');
|
|
299
|
+
return newG;
|
|
300
|
+
}
|
|
301
|
+
function createRect(rectangle, options) {
|
|
302
|
+
const rect = document.createElementNS(NS, 'rect');
|
|
303
|
+
rect.setAttribute('x', `${rectangle.x}`);
|
|
304
|
+
rect.setAttribute('y', `${rectangle.y}`);
|
|
305
|
+
rect.setAttribute('width', `${rectangle.width}`);
|
|
306
|
+
rect.setAttribute('height', `${rectangle.height}`);
|
|
307
|
+
for (let key in options) {
|
|
308
|
+
const optionKey = key;
|
|
309
|
+
rect.setAttribute(key, `${options[optionKey]}`);
|
|
310
|
+
}
|
|
311
|
+
return rect;
|
|
312
|
+
}
|
|
313
|
+
const setStrokeLinecap = (g, value) => {
|
|
314
|
+
g.setAttribute('stroke-linecap', value);
|
|
315
|
+
};
|
|
316
|
+
const setPathStrokeLinecap = (g, value) => {
|
|
317
|
+
g.querySelectorAll('path').forEach(path => {
|
|
318
|
+
path.setAttribute('stroke-linecap', value);
|
|
319
|
+
});
|
|
320
|
+
};
|
|
321
|
+
function createMask() {
|
|
322
|
+
return document.createElementNS(NS, 'mask');
|
|
323
|
+
}
|
|
324
|
+
function createSVG() {
|
|
325
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
326
|
+
return svg;
|
|
327
|
+
}
|
|
328
|
+
function createText(x, y, fill, textContent) {
|
|
329
|
+
var text = document.createElementNS(NS, 'text');
|
|
330
|
+
text.setAttribute('x', `${x}`);
|
|
331
|
+
text.setAttribute('y', `${y}`);
|
|
332
|
+
text.setAttribute('fill', fill);
|
|
333
|
+
text.textContent = textContent;
|
|
334
|
+
return text;
|
|
298
335
|
}
|
|
299
336
|
/**
|
|
300
|
-
*
|
|
301
|
-
* @param point
|
|
302
|
-
* @returns point
|
|
337
|
+
* Check if a DOM node is an element node.
|
|
303
338
|
*/
|
|
304
|
-
|
|
305
|
-
return
|
|
306
|
-
? {
|
|
307
|
-
x: point[0],
|
|
308
|
-
y: point[1]
|
|
309
|
-
}
|
|
310
|
-
: point;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const Viewport = {
|
|
314
|
-
isViewport: (value) => {
|
|
315
|
-
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
316
|
-
},
|
|
339
|
+
const isDOMElement = (value) => {
|
|
340
|
+
return isDOMNode(value) && value.nodeType === 1;
|
|
317
341
|
};
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
let paths = Path.levels(path, options);
|
|
329
|
-
if (reverse) {
|
|
330
|
-
paths = paths.slice(1);
|
|
331
|
-
}
|
|
332
|
-
else {
|
|
333
|
-
paths = paths.slice(0, -1);
|
|
334
|
-
}
|
|
335
|
-
return paths;
|
|
336
|
-
},
|
|
337
|
-
/**
|
|
338
|
-
* Get a list of paths at every level down to a path. Note: this is the same
|
|
339
|
-
* as `Path.ancestors`, but including the path itself.
|
|
340
|
-
*
|
|
341
|
-
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
342
|
-
* true` option is passed, they are reversed.
|
|
343
|
-
*/
|
|
344
|
-
levels(path, options = {}) {
|
|
345
|
-
const { reverse = false } = options;
|
|
346
|
-
const list = [];
|
|
347
|
-
for (let i = 0; i <= path.length; i++) {
|
|
348
|
-
list.push(path.slice(0, i));
|
|
342
|
+
/**
|
|
343
|
+
* Check if a value is a DOM node.
|
|
344
|
+
*/
|
|
345
|
+
const isDOMNode = (value) => {
|
|
346
|
+
return value instanceof window.Node;
|
|
347
|
+
};
|
|
348
|
+
const hasInputOrTextareaTarget = (target) => {
|
|
349
|
+
if (isDOMElement(target)) {
|
|
350
|
+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
|
|
351
|
+
return true;
|
|
349
352
|
}
|
|
350
|
-
|
|
351
|
-
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
};
|
|
356
|
+
const isSecondaryPointer = (event) => {
|
|
357
|
+
return event.button === POINTER_BUTTON.SECONDARY;
|
|
358
|
+
};
|
|
359
|
+
const isMainPointer = (event) => {
|
|
360
|
+
return event.button === POINTER_BUTTON.MAIN;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
function hasBeforeContextChange(value) {
|
|
364
|
+
if (value.beforeContextChange) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
function hasOnContextChanged(value) {
|
|
370
|
+
if (value.onContextChanged) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
class PlaitPluginElementComponent {
|
|
377
|
+
set context(value) {
|
|
378
|
+
if (hasBeforeContextChange(this)) {
|
|
379
|
+
this.beforeContextChange(value);
|
|
352
380
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
381
|
+
const previousContext = this._context;
|
|
382
|
+
this._context = value;
|
|
383
|
+
if (this.element) {
|
|
384
|
+
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
358
385
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
386
|
+
if (this.initialized) {
|
|
387
|
+
this.cdr.markForCheck();
|
|
388
|
+
if (hasOnContextChanged(this)) {
|
|
389
|
+
this.onContextChanged(value, previousContext);
|
|
390
|
+
}
|
|
364
391
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
392
|
+
else {
|
|
393
|
+
if (PlaitElement.isRootElement(this.element) && this.element.children) {
|
|
394
|
+
this.g = createG();
|
|
395
|
+
this.rootG = createG();
|
|
396
|
+
this.rootG.append(this.g);
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
this.g = createG();
|
|
400
|
+
}
|
|
374
401
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return 1;
|
|
402
|
+
}
|
|
403
|
+
get context() {
|
|
404
|
+
return this._context;
|
|
405
|
+
}
|
|
406
|
+
get element() {
|
|
407
|
+
return this.context && this.context.element;
|
|
408
|
+
}
|
|
409
|
+
get board() {
|
|
410
|
+
return this.context && this.context.board;
|
|
411
|
+
}
|
|
412
|
+
get selected() {
|
|
413
|
+
return this.context && this.context.selected;
|
|
414
|
+
}
|
|
415
|
+
get effect() {
|
|
416
|
+
return this.context && this.context.effect;
|
|
417
|
+
}
|
|
418
|
+
constructor(cdr) {
|
|
419
|
+
this.cdr = cdr;
|
|
420
|
+
this.initialized = false;
|
|
421
|
+
}
|
|
422
|
+
ngOnInit() {
|
|
423
|
+
if (this.element.type) {
|
|
424
|
+
(this.rootG || this.g).setAttribute(`plait-${this.element.type}`, 'true');
|
|
399
425
|
}
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
equals(path, another) {
|
|
406
|
-
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
407
|
-
},
|
|
408
|
-
/**
|
|
409
|
-
* Check if a path ends before one of the indexes in another.
|
|
410
|
-
*/
|
|
411
|
-
endsBefore(path, another) {
|
|
412
|
-
const i = path.length - 1;
|
|
413
|
-
const as = path.slice(0, i);
|
|
414
|
-
const bs = another.slice(0, i);
|
|
415
|
-
const av = path[i];
|
|
416
|
-
const bv = another[i];
|
|
417
|
-
return Path.equals(as, bs) && av < bv;
|
|
418
|
-
},
|
|
419
|
-
/**
|
|
420
|
-
* Check if a path is a sibling of another.
|
|
421
|
-
*/
|
|
422
|
-
isSibling(path, another) {
|
|
423
|
-
if (path.length !== another.length) {
|
|
424
|
-
return false;
|
|
426
|
+
this.initialized = true;
|
|
427
|
+
}
|
|
428
|
+
ngOnDestroy() {
|
|
429
|
+
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
430
|
+
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
425
431
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const al = path[path.length - 1];
|
|
429
|
-
const bl = another[another.length - 1];
|
|
430
|
-
return al !== bl && Path.equals(as, bs);
|
|
431
|
-
},
|
|
432
|
-
transform(path, operation) {
|
|
433
|
-
return produce(path, p => {
|
|
434
|
-
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
435
|
-
if (!path || path?.length === 0) {
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
|
-
if (p === null) {
|
|
439
|
-
return null;
|
|
440
|
-
}
|
|
441
|
-
switch (operation.type) {
|
|
442
|
-
case 'insert_node': {
|
|
443
|
-
const { path: op } = operation;
|
|
444
|
-
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
445
|
-
p[op.length - 1] += 1;
|
|
446
|
-
}
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
case 'remove_node': {
|
|
450
|
-
const { path: op } = operation;
|
|
451
|
-
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
452
|
-
return null;
|
|
453
|
-
}
|
|
454
|
-
else if (Path.endsBefore(op, p)) {
|
|
455
|
-
p[op.length - 1] -= 1;
|
|
456
|
-
}
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
case 'move_node': {
|
|
460
|
-
const { path: op, newPath: onp } = operation;
|
|
461
|
-
// If the old and new path are the same, it's a no-op.
|
|
462
|
-
if (Path.equals(op, onp)) {
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
466
|
-
const copy = onp.slice();
|
|
467
|
-
// op.length <= onp.length is different for slate
|
|
468
|
-
// resolve drag from [0, 0] to [0, 3] issue
|
|
469
|
-
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
470
|
-
copy[op.length - 1] -= 1;
|
|
471
|
-
}
|
|
472
|
-
return copy.concat(p.slice(op.length));
|
|
473
|
-
}
|
|
474
|
-
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
475
|
-
if (Path.endsBefore(op, p)) {
|
|
476
|
-
p[op.length - 1] -= 1;
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
p[op.length - 1] += 1;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
483
|
-
if (Path.endsBefore(op, p)) {
|
|
484
|
-
p[op.length - 1] -= 1;
|
|
485
|
-
}
|
|
486
|
-
p[onp.length - 1] += 1;
|
|
487
|
-
}
|
|
488
|
-
else if (Path.endsBefore(op, p)) {
|
|
489
|
-
if (Path.equals(onp, p)) {
|
|
490
|
-
p[onp.length - 1] += 1;
|
|
491
|
-
}
|
|
492
|
-
p[op.length - 1] -= 1;
|
|
493
|
-
}
|
|
494
|
-
break;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
return p;
|
|
498
|
-
});
|
|
432
|
+
removeSelectedElement(this.board, this.element);
|
|
433
|
+
(this.rootG || this.g).remove();
|
|
499
434
|
}
|
|
500
|
-
};
|
|
435
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
436
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
|
|
437
|
+
}
|
|
438
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
|
|
439
|
+
type: Directive
|
|
440
|
+
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
|
|
441
|
+
type: Input
|
|
442
|
+
}] } });
|
|
443
|
+
const ELEMENT_TO_COMPONENT = new WeakMap();
|
|
501
444
|
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
*parents(root, path, options = {}) {
|
|
515
|
-
for (const p of Path.ancestors(path, options)) {
|
|
516
|
-
const n = PlaitNode.get(root, p);
|
|
517
|
-
yield n;
|
|
445
|
+
const RectangleClient = {
|
|
446
|
+
isHit: (origin, target) => {
|
|
447
|
+
const minX = origin.x < target.x ? origin.x : target.x;
|
|
448
|
+
const maxX = origin.x + origin.width > target.x + target.width ? origin.x + origin.width : target.x + target.width;
|
|
449
|
+
const minY = origin.y < target.y ? origin.y : target.y;
|
|
450
|
+
const maxY = origin.y + origin.height > target.y + target.height ? origin.y + origin.height : target.y + target.height;
|
|
451
|
+
// float calculate error( eg: 1.4210854715202004e-14 > 0)
|
|
452
|
+
if (Math.floor(maxX - minX - origin.width - target.width) <= 0 && Math.floor(maxY - minY - origin.height - target.height) <= 0) {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
return false;
|
|
518
457
|
}
|
|
519
458
|
},
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
return node;
|
|
459
|
+
toRectangleClient: (points) => {
|
|
460
|
+
const xArray = points.map(ele => ele[0]);
|
|
461
|
+
const yArray = points.map(ele => ele[1]);
|
|
462
|
+
const xMin = Math.min(...xArray);
|
|
463
|
+
const xMax = Math.max(...xArray);
|
|
464
|
+
const yMin = Math.min(...yArray);
|
|
465
|
+
const yMax = Math.max(...yArray);
|
|
466
|
+
const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
467
|
+
return rect;
|
|
530
468
|
},
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
469
|
+
getOutlineRectangle: (rectangle, offset) => {
|
|
470
|
+
return {
|
|
471
|
+
x: rectangle.x + offset,
|
|
472
|
+
y: rectangle.y + offset,
|
|
473
|
+
width: rectangle.width - offset * 2,
|
|
474
|
+
height: rectangle.height - offset * 2
|
|
475
|
+
};
|
|
476
|
+
},
|
|
477
|
+
inflate: (rectangle, delta) => {
|
|
478
|
+
const half = delta / 2;
|
|
479
|
+
return {
|
|
480
|
+
x: rectangle.x - half,
|
|
481
|
+
y: rectangle.y - half,
|
|
482
|
+
width: rectangle.width + half * 2,
|
|
483
|
+
height: rectangle.height + half * 2
|
|
484
|
+
};
|
|
485
|
+
},
|
|
486
|
+
isEqual: (rectangle, otherRectangle) => {
|
|
487
|
+
return (rectangle.x === otherRectangle.x &&
|
|
488
|
+
rectangle.y === otherRectangle.y &&
|
|
489
|
+
rectangle.width === otherRectangle.width &&
|
|
490
|
+
rectangle.height === otherRectangle.height);
|
|
491
|
+
},
|
|
492
|
+
getCornerPoints: (rectangle) => {
|
|
493
|
+
return [
|
|
494
|
+
[rectangle.x, rectangle.y],
|
|
495
|
+
[rectangle.x + rectangle.width, rectangle.y],
|
|
496
|
+
[rectangle.x + rectangle.width, rectangle.y + rectangle.height],
|
|
497
|
+
[rectangle.x, rectangle.y + rectangle.height]
|
|
498
|
+
];
|
|
499
|
+
},
|
|
500
|
+
getEdgeCenterPoints: (rectangle) => {
|
|
501
|
+
return [
|
|
502
|
+
[rectangle.x + rectangle.width / 2, rectangle.y],
|
|
503
|
+
[rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
|
|
504
|
+
[rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height],
|
|
505
|
+
[rectangle.x, rectangle.y + rectangle.height / 2]
|
|
506
|
+
];
|
|
507
|
+
},
|
|
508
|
+
getConnectionPoint: (rectangle, point) => {
|
|
509
|
+
return [rectangle.x + rectangle.width * point[0], rectangle.y + rectangle.height * point[1]];
|
|
538
510
|
}
|
|
539
511
|
};
|
|
540
512
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
513
|
+
// https://stackoverflow.com/a/6853926/232122
|
|
514
|
+
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
515
|
+
const A = x - x1;
|
|
516
|
+
const B = y - y1;
|
|
517
|
+
const C = x2 - x1;
|
|
518
|
+
const D = y2 - y1;
|
|
519
|
+
const dot = A * C + B * D;
|
|
520
|
+
const lenSquare = C * C + D * D;
|
|
521
|
+
let param = -1;
|
|
522
|
+
if (lenSquare !== 0) {
|
|
523
|
+
// in case of 0 length line
|
|
524
|
+
param = dot / lenSquare;
|
|
525
|
+
}
|
|
526
|
+
let xx, yy;
|
|
527
|
+
if (param < 0) {
|
|
528
|
+
xx = x1;
|
|
529
|
+
yy = y1;
|
|
530
|
+
}
|
|
531
|
+
else if (param > 1) {
|
|
532
|
+
xx = x2;
|
|
533
|
+
yy = y2;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
xx = x1 + param * C;
|
|
537
|
+
yy = y1 + param * D;
|
|
538
|
+
}
|
|
539
|
+
const dx = x - xx;
|
|
540
|
+
const dy = y - yy;
|
|
541
|
+
return Math.hypot(dx, dy);
|
|
542
|
+
}
|
|
543
|
+
function getNearestPointBetweenPointAndSegment(point, linePoints) {
|
|
544
|
+
const x = point[0], y = point[1], x1 = linePoints[0][0], y1 = linePoints[0][1], x2 = linePoints[1][0], y2 = linePoints[1][1];
|
|
545
|
+
const A = x - x1;
|
|
546
|
+
const B = y - y1;
|
|
547
|
+
const C = x2 - x1;
|
|
548
|
+
const D = y2 - y1;
|
|
549
|
+
const dot = A * C + B * D;
|
|
550
|
+
const lenSquare = C * C + D * D;
|
|
551
|
+
let param = -1;
|
|
552
|
+
if (lenSquare !== 0) {
|
|
553
|
+
// in case of 0 length line
|
|
554
|
+
param = dot / lenSquare;
|
|
555
|
+
}
|
|
556
|
+
let xx, yy;
|
|
557
|
+
if (param < 0) {
|
|
558
|
+
xx = x1;
|
|
559
|
+
yy = y1;
|
|
560
|
+
}
|
|
561
|
+
else if (param > 1) {
|
|
562
|
+
xx = x2;
|
|
563
|
+
yy = y2;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
xx = x1 + param * C;
|
|
567
|
+
yy = y1 + param * D;
|
|
568
|
+
}
|
|
569
|
+
return [xx, yy];
|
|
570
|
+
}
|
|
571
|
+
function distanceBetweenPointAndSegments(points, point) {
|
|
572
|
+
const len = points.length;
|
|
573
|
+
let distance = Infinity;
|
|
574
|
+
for (let i = 0; i < len - 1; i++) {
|
|
575
|
+
const p = points[i];
|
|
576
|
+
const p2 = points[i + 1];
|
|
577
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
578
|
+
if (currentDistance < distance) {
|
|
579
|
+
distance = currentDistance;
|
|
606
580
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
if (value == null) {
|
|
622
|
-
delete viewport[key];
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
viewport[key] = value;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
break;
|
|
581
|
+
}
|
|
582
|
+
return distance;
|
|
583
|
+
}
|
|
584
|
+
function getNearestPointBetweenPointAndSegments(point, points) {
|
|
585
|
+
const len = points.length;
|
|
586
|
+
let distance = Infinity;
|
|
587
|
+
let result = point;
|
|
588
|
+
for (let i = 0; i < len; i++) {
|
|
589
|
+
const p = points[i];
|
|
590
|
+
const p2 = i === len - 1 ? points[0] : points[i + 1];
|
|
591
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
592
|
+
if (currentDistance < distance) {
|
|
593
|
+
distance = currentDistance;
|
|
594
|
+
result = getNearestPointBetweenPointAndSegment(point, [p, p2]);
|
|
630
595
|
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
596
|
+
}
|
|
597
|
+
return result;
|
|
598
|
+
}
|
|
599
|
+
function rotate(x1, y1, x2, y2, angle) {
|
|
600
|
+
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
601
|
+
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
602
|
+
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
603
|
+
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
604
|
+
}
|
|
605
|
+
function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
|
|
606
|
+
const dx = x1 - x2;
|
|
607
|
+
const dy = y1 - y2;
|
|
608
|
+
return Math.hypot(dx, dy);
|
|
609
|
+
}
|
|
610
|
+
// https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
|
|
611
|
+
function distanceBetweenPointAndRectangle(x, y, rect) {
|
|
612
|
+
var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
|
|
613
|
+
var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
|
|
614
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
615
|
+
}
|
|
616
|
+
const isLineHitLine = (a, b, c, d) => {
|
|
617
|
+
const crossProduct = (v1, v2) => v1[0] * v2[1] - v1[1] * v2[0];
|
|
618
|
+
const ab = [b[0] - a[0], b[1] - a[1]];
|
|
619
|
+
const ac = [c[0] - a[0], c[1] - a[1]];
|
|
620
|
+
const ad = [d[0] - a[0], d[1] - a[1]];
|
|
621
|
+
const ca = [a[0] - c[0], a[1] - c[1]];
|
|
622
|
+
const cb = [b[0] - c[0], b[1] - c[1]];
|
|
623
|
+
const cd = [d[0] - c[0], d[1] - c[1]];
|
|
624
|
+
return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
|
|
625
|
+
};
|
|
626
|
+
const isPolylineHitRectangle = (points, rectangle) => {
|
|
627
|
+
const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
|
|
628
|
+
for (let i = 1; i < points.length; i++) {
|
|
629
|
+
const isIntersect = isLineHitLine(points[i], points[i - 1], rectanglePoints[0], rectanglePoints[1]) ||
|
|
630
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[1], rectanglePoints[2]) ||
|
|
631
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[2], rectanglePoints[3]) ||
|
|
632
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[3], rectanglePoints[0]);
|
|
633
|
+
if (isIntersect) {
|
|
634
|
+
return true;
|
|
645
635
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
636
|
+
}
|
|
637
|
+
return false;
|
|
638
|
+
};
|
|
639
|
+
//https://stackoverflow.com/questions/22521982/check-if-point-is-inside-a-polygon
|
|
640
|
+
const isPointInPolygon = (point, points) => {
|
|
641
|
+
// ray-casting algorithm based on
|
|
642
|
+
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
|
|
643
|
+
const x = point[0], y = point[1];
|
|
644
|
+
let inside = false;
|
|
645
|
+
for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
646
|
+
let xi = points[i][0], yi = points[i][1];
|
|
647
|
+
let xj = points[j][0], yj = points[j][1];
|
|
648
|
+
let intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
|
|
649
|
+
if (intersect)
|
|
650
|
+
inside = !inside;
|
|
651
|
+
}
|
|
652
|
+
return inside;
|
|
653
|
+
};
|
|
654
|
+
const isPointInEllipse = (point, center, rx, ry, rotation = 0) => {
|
|
655
|
+
const cosAngle = Math.cos(rotation);
|
|
656
|
+
const sinAngle = Math.sin(rotation);
|
|
657
|
+
const x1 = (point[0] - center[0]) * cosAngle + (point[1] - center[1]) * sinAngle;
|
|
658
|
+
const y1 = (point[1] - center[1]) * cosAngle - (point[0] - center[0]) * sinAngle;
|
|
659
|
+
return (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry) <= 1;
|
|
660
|
+
};
|
|
661
|
+
const isPointInRoundRectangle = (point, rectangle, radius) => {
|
|
662
|
+
const { x: rectX, y: rectY, width, height } = rectangle;
|
|
663
|
+
const isInRectangle = point[0] >= rectX && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + height;
|
|
664
|
+
const handleLeftTop = point[0] >= rectX &&
|
|
665
|
+
point[0] <= rectX + radius &&
|
|
666
|
+
point[1] >= rectY &&
|
|
667
|
+
point[1] <= rectY + radius &&
|
|
668
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + radius)) > radius;
|
|
669
|
+
const handleLeftBottom = point[0] >= rectX &&
|
|
670
|
+
point[0] <= rectX + radius &&
|
|
671
|
+
point[1] >= rectY + height &&
|
|
672
|
+
point[1] <= rectY + height - radius &&
|
|
673
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + height - radius)) > radius;
|
|
674
|
+
const handleRightTop = point[0] >= rectX + width - radius &&
|
|
675
|
+
point[0] <= rectX + width &&
|
|
676
|
+
point[1] >= rectY &&
|
|
677
|
+
point[1] <= rectY + radius &&
|
|
678
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + radius)) > radius;
|
|
679
|
+
const handleRightBottom = point[0] >= rectX + width - radius &&
|
|
680
|
+
point[0] <= rectX + width &&
|
|
681
|
+
point[1] >= rectY + height - radius &&
|
|
682
|
+
point[1] <= rectY + height &&
|
|
683
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + height - radius)) > radius;
|
|
684
|
+
const isInCorner = handleLeftTop || handleLeftBottom || handleRightTop || handleRightBottom;
|
|
685
|
+
return isInRectangle && !isInCorner;
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
function transformPoints(board, points) {
|
|
689
|
+
const newPoints = points.map(point => {
|
|
690
|
+
return transformPoint(board, point);
|
|
691
|
+
});
|
|
692
|
+
return newPoints;
|
|
693
|
+
}
|
|
694
|
+
function transformPoint(board, point) {
|
|
695
|
+
const { width, height } = PlaitBoard.getHost(board).getBoundingClientRect();
|
|
696
|
+
const viewBox = PlaitBoard.getHost(board).viewBox.baseVal;
|
|
697
|
+
const x = (point[0] / width) * viewBox.width + viewBox.x;
|
|
698
|
+
const y = (point[1] / height) * viewBox.height + viewBox.y;
|
|
699
|
+
const newPoint = [x, y];
|
|
700
|
+
return newPoint;
|
|
701
|
+
}
|
|
702
|
+
function isInPlaitBoard(board, x, y) {
|
|
703
|
+
const plaitBoardElement = PlaitBoard.getBoardContainer(board);
|
|
704
|
+
const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
|
|
705
|
+
const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
|
|
706
|
+
return distances === 0;
|
|
707
|
+
}
|
|
708
|
+
function getRealScrollBarWidth(board) {
|
|
709
|
+
const { hideScrollbar } = board.options;
|
|
710
|
+
let scrollBarWidth = 0;
|
|
711
|
+
if (!hideScrollbar) {
|
|
712
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
713
|
+
scrollBarWidth = viewportContainer.offsetWidth - viewportContainer.clientWidth;
|
|
714
|
+
}
|
|
715
|
+
return scrollBarWidth;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function createForeignObject(x, y, width, height) {
|
|
719
|
+
var newForeignObject = document.createElementNS(NS, 'foreignObject');
|
|
720
|
+
newForeignObject.setAttribute('x', `${x}`);
|
|
721
|
+
newForeignObject.setAttribute('y', `${y}`);
|
|
722
|
+
newForeignObject.setAttribute('width', `${width}`);
|
|
723
|
+
newForeignObject.setAttribute('height', `${height}`);
|
|
724
|
+
return newForeignObject;
|
|
725
|
+
}
|
|
726
|
+
function updateForeignObject(target, width, height, x, y) {
|
|
727
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
728
|
+
if (foreignObject) {
|
|
729
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
730
|
+
foreignObject.setAttribute('height', `${height}`);
|
|
731
|
+
foreignObject.setAttribute('x', `${x}`);
|
|
732
|
+
foreignObject.setAttribute('y', `${y}`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function updateForeignObjectWidth(target, width) {
|
|
736
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
737
|
+
if (foreignObject) {
|
|
738
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const IS_MAC = typeof window != 'undefined' && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
|
743
|
+
|
|
744
|
+
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
745
|
+
typeof window !== 'undefined' &&
|
|
746
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
747
|
+
!window.MSStream;
|
|
748
|
+
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
749
|
+
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
750
|
+
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
751
|
+
// "modern" Edge was released at 79.x
|
|
752
|
+
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
753
|
+
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
754
|
+
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
755
|
+
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
756
|
+
|
|
757
|
+
function isNullOrUndefined(value) {
|
|
758
|
+
return value === null || value === undefined;
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* 规范 point
|
|
762
|
+
* @param point
|
|
763
|
+
* @returns point
|
|
764
|
+
*/
|
|
765
|
+
function normalizePoint(point) {
|
|
766
|
+
return Array.isArray(point)
|
|
767
|
+
? {
|
|
768
|
+
x: point[0],
|
|
769
|
+
y: point[1]
|
|
650
770
|
}
|
|
771
|
+
: point;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Check whether to merge an operation into the previous operation.
|
|
776
|
+
*/
|
|
777
|
+
const shouldMerge = (op, prev) => {
|
|
778
|
+
if (op.type === 'set_viewport' && op.type === prev?.type) {
|
|
779
|
+
return true;
|
|
651
780
|
}
|
|
652
|
-
return
|
|
781
|
+
return false;
|
|
653
782
|
};
|
|
654
|
-
|
|
783
|
+
/**
|
|
784
|
+
* Check whether an operation needs to be saved to the history.
|
|
785
|
+
*/
|
|
786
|
+
const shouldSave = (op, prev) => {
|
|
787
|
+
if (op.type === 'set_selection' || op.type === 'set_viewport') {
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
return true;
|
|
791
|
+
};
|
|
792
|
+
/**
|
|
793
|
+
* Check whether an operation should clear the redos stack.
|
|
794
|
+
*/
|
|
795
|
+
const shouldClear = (op) => {
|
|
796
|
+
if (op.type === 'set_selection') {
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
return true;
|
|
800
|
+
};
|
|
801
|
+
const PlaitHistoryBoard = {
|
|
655
802
|
/**
|
|
656
|
-
*
|
|
803
|
+
* Get the saving flag's current value.
|
|
657
804
|
*/
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
805
|
+
isSaving(board) {
|
|
806
|
+
return SAVING.get(board);
|
|
807
|
+
},
|
|
808
|
+
/**
|
|
809
|
+
* Get the merge flag's current value.
|
|
810
|
+
*/
|
|
811
|
+
isMerging(board) {
|
|
812
|
+
return MERGING.get(board);
|
|
813
|
+
},
|
|
814
|
+
/**
|
|
815
|
+
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
816
|
+
* the new operations into previous save point in the history.
|
|
817
|
+
*/
|
|
818
|
+
withoutMerging(board, fn) {
|
|
819
|
+
const prev = PlaitHistoryBoard.isMerging(board);
|
|
820
|
+
MERGING.set(board, false);
|
|
821
|
+
fn();
|
|
822
|
+
MERGING.set(board, prev);
|
|
823
|
+
},
|
|
824
|
+
/**
|
|
825
|
+
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
826
|
+
* their operations into the history.
|
|
827
|
+
*/
|
|
828
|
+
withoutSaving(board, fn) {
|
|
829
|
+
const prev = PlaitHistoryBoard.isSaving(board);
|
|
830
|
+
SAVING.set(board, false);
|
|
831
|
+
fn();
|
|
832
|
+
SAVING.set(board, prev);
|
|
680
833
|
}
|
|
681
834
|
};
|
|
682
835
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
836
|
+
/**
|
|
837
|
+
* Hotkey mappings for each platform.
|
|
838
|
+
*/
|
|
839
|
+
const HOTKEYS = {
|
|
840
|
+
bold: 'mod+b',
|
|
841
|
+
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
842
|
+
moveBackward: 'left',
|
|
843
|
+
moveForward: 'right',
|
|
844
|
+
moveUp: 'up',
|
|
845
|
+
moveDown: 'down',
|
|
846
|
+
moveWordBackward: 'ctrl+left',
|
|
847
|
+
moveWordForward: 'ctrl+right',
|
|
848
|
+
deleteBackward: 'shift?+backspace',
|
|
849
|
+
deleteForward: 'shift?+delete',
|
|
850
|
+
extendBackward: 'shift+left',
|
|
851
|
+
extendForward: 'shift+right',
|
|
852
|
+
italic: 'mod+i',
|
|
853
|
+
splitBlock: 'shift?+enter',
|
|
854
|
+
undo: 'mod+z'
|
|
855
|
+
};
|
|
856
|
+
const APPLE_HOTKEYS = {
|
|
857
|
+
moveLineBackward: 'opt+up',
|
|
858
|
+
moveLineForward: 'opt+down',
|
|
859
|
+
moveWordBackward: 'opt+left',
|
|
860
|
+
moveWordForward: 'opt+right',
|
|
861
|
+
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
862
|
+
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
863
|
+
deleteLineBackward: 'cmd+shift?+backspace',
|
|
864
|
+
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
865
|
+
deleteWordBackward: 'opt+shift?+backspace',
|
|
866
|
+
deleteWordForward: 'opt+shift?+delete',
|
|
867
|
+
extendLineBackward: 'opt+shift+up',
|
|
868
|
+
extendLineForward: 'opt+shift+down',
|
|
869
|
+
redo: 'cmd+shift+z',
|
|
870
|
+
transposeCharacter: 'ctrl+t'
|
|
871
|
+
};
|
|
872
|
+
const WINDOWS_HOTKEYS = {
|
|
873
|
+
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
874
|
+
deleteWordForward: 'ctrl+shift?+delete',
|
|
875
|
+
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
876
|
+
};
|
|
877
|
+
/**
|
|
878
|
+
* Create a platform-aware hotkey checker.
|
|
879
|
+
*/
|
|
880
|
+
const create = (key) => {
|
|
881
|
+
const generic = HOTKEYS[key];
|
|
882
|
+
const apple = APPLE_HOTKEYS[key];
|
|
883
|
+
const windows = WINDOWS_HOTKEYS[key];
|
|
884
|
+
const isGeneric = generic && isKeyHotkey(generic);
|
|
885
|
+
const isApple = apple && isKeyHotkey(apple);
|
|
886
|
+
const isWindows = windows && isKeyHotkey(windows);
|
|
887
|
+
return (event) => {
|
|
888
|
+
if (isGeneric && isGeneric(event)) {
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
if (IS_APPLE && isApple && isApple(event)) {
|
|
892
|
+
return true;
|
|
698
893
|
}
|
|
894
|
+
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
return false;
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
/**
|
|
901
|
+
* Hotkeys.
|
|
902
|
+
*/
|
|
903
|
+
const hotkeys = {
|
|
904
|
+
isBold: create('bold'),
|
|
905
|
+
isCompose: create('compose'),
|
|
906
|
+
isMoveBackward: create('moveBackward'),
|
|
907
|
+
isMoveForward: create('moveForward'),
|
|
908
|
+
isMoveUp: create('moveUp'),
|
|
909
|
+
isMoveDown: create('moveDown'),
|
|
910
|
+
isDeleteBackward: create('deleteBackward'),
|
|
911
|
+
isDeleteForward: create('deleteForward'),
|
|
912
|
+
isDeleteLineBackward: create('deleteLineBackward'),
|
|
913
|
+
isDeleteLineForward: create('deleteLineForward'),
|
|
914
|
+
isDeleteWordBackward: create('deleteWordBackward'),
|
|
915
|
+
isDeleteWordForward: create('deleteWordForward'),
|
|
916
|
+
isExtendBackward: create('extendBackward'),
|
|
917
|
+
isExtendForward: create('extendForward'),
|
|
918
|
+
isExtendLineBackward: create('extendLineBackward'),
|
|
919
|
+
isExtendLineForward: create('extendLineForward'),
|
|
920
|
+
isItalic: create('italic'),
|
|
921
|
+
isMoveLineBackward: create('moveLineBackward'),
|
|
922
|
+
isMoveLineForward: create('moveLineForward'),
|
|
923
|
+
isMoveWordBackward: create('moveWordBackward'),
|
|
924
|
+
isMoveWordForward: create('moveWordForward'),
|
|
925
|
+
isRedo: create('redo'),
|
|
926
|
+
isSplitBlock: create('splitBlock'),
|
|
927
|
+
isTransposeCharacter: create('transposeCharacter'),
|
|
928
|
+
isUndo: create('undo')
|
|
929
|
+
};
|
|
930
|
+
|
|
931
|
+
function idCreator(length = 5) {
|
|
932
|
+
// remove numeral
|
|
933
|
+
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
934
|
+
const maxPosition = $chars.length;
|
|
935
|
+
let key = '';
|
|
936
|
+
for (let i = 0; i < length; i++) {
|
|
937
|
+
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
699
938
|
}
|
|
700
|
-
|
|
701
|
-
board.apply(operation);
|
|
702
|
-
}
|
|
703
|
-
function removeNode(board, path) {
|
|
704
|
-
const node = PlaitNode.get(board, path);
|
|
705
|
-
const operation = { type: 'remove_node', path, node };
|
|
706
|
-
board.apply(operation);
|
|
939
|
+
return key;
|
|
707
940
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* drawRoundRectangle
|
|
944
|
+
*/
|
|
945
|
+
function drawRoundRectangle(rs, x1, y1, x2, y2, options, outline = false, borderRadius) {
|
|
946
|
+
const width = Math.abs(x1 - x2);
|
|
947
|
+
const height = Math.abs(y1 - y2);
|
|
948
|
+
let radius = borderRadius || 0;
|
|
949
|
+
if (radius === 0) {
|
|
950
|
+
const defaultRadius = Math.min(width, height) / 8;
|
|
951
|
+
let radius = defaultRadius;
|
|
952
|
+
if (defaultRadius > MAX_RADIUS) {
|
|
953
|
+
radius = outline ? MAX_RADIUS + 2 : MAX_RADIUS;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const point1 = [x1 + radius, y1];
|
|
957
|
+
const point2 = [x2 - radius, y1];
|
|
958
|
+
const point3 = [x2, y1 + radius];
|
|
959
|
+
const point4 = [x2, y2 - radius];
|
|
960
|
+
const point5 = [x2 - radius, y2];
|
|
961
|
+
const point6 = [x1 + radius, y2];
|
|
962
|
+
const point7 = [x1, y2 - radius];
|
|
963
|
+
const point8 = [x1, y1 + radius];
|
|
964
|
+
return rs.path(`M${point2[0]} ${point2[1]} A ${radius} ${radius}, 0, 0, 1, ${point3[0]} ${point3[1]} L ${point4[0]} ${point4[1]} A ${radius} ${radius}, 0, 0, 1, ${point5[0]} ${point5[1]} L ${point6[0]} ${point6[1]} A ${radius} ${radius}, 0, 0, 1, ${point7[0]} ${point7[1]} L ${point8[0]} ${point8[1]} A ${radius} ${radius}, 0, 0, 1, ${point1[0]} ${point1[1]} Z`, options);
|
|
711
965
|
}
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
966
|
+
const drawRectangle = (board, rectangle, options) => {
|
|
967
|
+
const roughSVG = PlaitBoard.getRoughSVG(board);
|
|
968
|
+
const rectangleG = roughSVG.rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height, options);
|
|
969
|
+
setStrokeLinecap(rectangleG, 'round');
|
|
970
|
+
return rectangleG;
|
|
717
971
|
};
|
|
718
972
|
|
|
719
|
-
function
|
|
720
|
-
const
|
|
721
|
-
|
|
973
|
+
function arrowPoints(start, end, maxHypotenuseLength = 10, degree = 40) {
|
|
974
|
+
const width = Math.abs(start[0] - end[0]);
|
|
975
|
+
const height = Math.abs(start[1] - end[1]);
|
|
976
|
+
let hypotenuse = Math.hypot(width, height); // 斜边
|
|
977
|
+
const realRotateLine = hypotenuse > maxHypotenuseLength * 2 ? maxHypotenuseLength : hypotenuse / 2;
|
|
978
|
+
const rotateWidth = (realRotateLine / hypotenuse) * width;
|
|
979
|
+
const rotateHeight = (realRotateLine / hypotenuse) * height;
|
|
980
|
+
const rotatePoint = [
|
|
981
|
+
end[0] > start[0] ? end[0] - rotateWidth : end[0] + rotateWidth,
|
|
982
|
+
end[1] > start[1] ? end[1] - rotateHeight : end[1] + rotateHeight
|
|
983
|
+
];
|
|
984
|
+
const pointRight = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (degree * Math.PI) / 180);
|
|
985
|
+
const pointLeft = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (-degree * Math.PI) / 180);
|
|
986
|
+
return { pointLeft, pointRight };
|
|
722
987
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
setTimeout(() => {
|
|
729
|
-
BOARD_TO_TEMPORARY_ELEMENTS.set(board, elements);
|
|
730
|
-
setSelection(board, { ranges: [] });
|
|
731
|
-
});
|
|
988
|
+
function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 40) {
|
|
989
|
+
const { pointLeft, pointRight } = arrowPoints(start, end, maxHypotenuseLength, degree);
|
|
990
|
+
const arrowLineLeft = rs.linearPath([pointLeft, end], options);
|
|
991
|
+
const arrowLineRight = rs.linearPath([pointRight, end], options);
|
|
992
|
+
return [arrowLineLeft, arrowLineRight];
|
|
732
993
|
}
|
|
733
994
|
|
|
734
|
-
function
|
|
735
|
-
|
|
736
|
-
board.apply(operation);
|
|
995
|
+
function drawCircle(roughSVG, point, diameter, options) {
|
|
996
|
+
return roughSVG.circle(point[0], point[1], diameter, options);
|
|
737
997
|
}
|
|
738
|
-
const ViewportTransforms$1 = {
|
|
739
|
-
setViewport
|
|
740
|
-
};
|
|
741
|
-
|
|
742
|
-
/**
|
|
743
|
-
* @license
|
|
744
|
-
* Copyright Google LLC All Rights Reserved.
|
|
745
|
-
*
|
|
746
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
747
|
-
* found in the LICENSE file at https://angular.io/license
|
|
748
|
-
*/
|
|
749
|
-
const MAC_ENTER = 3;
|
|
750
|
-
const BACKSPACE = 8;
|
|
751
|
-
const TAB = 9;
|
|
752
|
-
const NUM_CENTER = 12;
|
|
753
|
-
const ENTER = 13;
|
|
754
|
-
const SHIFT = 16;
|
|
755
|
-
const CONTROL = 17;
|
|
756
|
-
const ALT = 18;
|
|
757
|
-
const PAUSE = 19;
|
|
758
|
-
const CAPS_LOCK = 20;
|
|
759
|
-
const ESCAPE = 27;
|
|
760
|
-
const SPACE = 32;
|
|
761
|
-
const PAGE_UP = 33;
|
|
762
|
-
const PAGE_DOWN = 34;
|
|
763
|
-
const END = 35;
|
|
764
|
-
const HOME = 36;
|
|
765
|
-
const LEFT_ARROW = 37;
|
|
766
|
-
const UP_ARROW = 38;
|
|
767
|
-
const RIGHT_ARROW = 39;
|
|
768
|
-
const DOWN_ARROW = 40;
|
|
769
|
-
const PLUS_SIGN = 43;
|
|
770
|
-
const PRINT_SCREEN = 44;
|
|
771
|
-
const INSERT = 45;
|
|
772
|
-
const DELETE = 46;
|
|
773
|
-
const ZERO = 48;
|
|
774
|
-
const ONE = 49;
|
|
775
|
-
const TWO = 50;
|
|
776
|
-
const THREE = 51;
|
|
777
|
-
const FOUR = 52;
|
|
778
|
-
const FIVE = 53;
|
|
779
|
-
const SIX = 54;
|
|
780
|
-
const SEVEN = 55;
|
|
781
|
-
const EIGHT = 56;
|
|
782
|
-
const NINE = 57;
|
|
783
|
-
const FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
784
|
-
const FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
785
|
-
const QUESTION_MARK = 63;
|
|
786
|
-
const AT_SIGN = 64;
|
|
787
|
-
const A = 65;
|
|
788
|
-
const B = 66;
|
|
789
|
-
const C = 67;
|
|
790
|
-
const D = 68;
|
|
791
|
-
const E = 69;
|
|
792
|
-
const F = 70;
|
|
793
|
-
const G = 71;
|
|
794
|
-
const H = 72;
|
|
795
|
-
const I = 73;
|
|
796
|
-
const J = 74;
|
|
797
|
-
const K = 75;
|
|
798
|
-
const L = 76;
|
|
799
|
-
const M = 77;
|
|
800
|
-
const N = 78;
|
|
801
|
-
const O = 79;
|
|
802
|
-
const P = 80;
|
|
803
|
-
const Q = 81;
|
|
804
|
-
const R = 82;
|
|
805
|
-
const S = 83;
|
|
806
|
-
const T = 84;
|
|
807
|
-
const U = 85;
|
|
808
|
-
const V = 86;
|
|
809
|
-
const W = 87;
|
|
810
|
-
const X = 88;
|
|
811
|
-
const Y = 89;
|
|
812
|
-
const Z = 90;
|
|
813
|
-
const META = 91; // WIN_KEY_LEFT
|
|
814
|
-
const MAC_WK_CMD_LEFT = 91;
|
|
815
|
-
const MAC_WK_CMD_RIGHT = 93;
|
|
816
|
-
const CONTEXT_MENU = 93;
|
|
817
|
-
const NUMPAD_ZERO = 96;
|
|
818
|
-
const NUMPAD_ONE = 97;
|
|
819
|
-
const NUMPAD_TWO = 98;
|
|
820
|
-
const NUMPAD_THREE = 99;
|
|
821
|
-
const NUMPAD_FOUR = 100;
|
|
822
|
-
const NUMPAD_FIVE = 101;
|
|
823
|
-
const NUMPAD_SIX = 102;
|
|
824
|
-
const NUMPAD_SEVEN = 103;
|
|
825
|
-
const NUMPAD_EIGHT = 104;
|
|
826
|
-
const NUMPAD_NINE = 105;
|
|
827
|
-
const NUMPAD_MULTIPLY = 106;
|
|
828
|
-
const NUMPAD_PLUS = 107;
|
|
829
|
-
const NUMPAD_MINUS = 109;
|
|
830
|
-
const NUMPAD_PERIOD = 110;
|
|
831
|
-
const NUMPAD_DIVIDE = 111;
|
|
832
|
-
const F1 = 112;
|
|
833
|
-
const F2 = 113;
|
|
834
|
-
const F3 = 114;
|
|
835
|
-
const F4 = 115;
|
|
836
|
-
const F5 = 116;
|
|
837
|
-
const F6 = 117;
|
|
838
|
-
const F7 = 118;
|
|
839
|
-
const F8 = 119;
|
|
840
|
-
const F9 = 120;
|
|
841
|
-
const F10 = 121;
|
|
842
|
-
const F11 = 122;
|
|
843
|
-
const F12 = 123;
|
|
844
|
-
const NUM_LOCK = 144;
|
|
845
|
-
const SCROLL_LOCK = 145;
|
|
846
|
-
const FIRST_MEDIA = 166;
|
|
847
|
-
const FF_MINUS = 173;
|
|
848
|
-
const MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
849
|
-
const VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
850
|
-
const VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
851
|
-
const FF_MUTE = 181;
|
|
852
|
-
const FF_VOLUME_DOWN = 182;
|
|
853
|
-
const LAST_MEDIA = 183;
|
|
854
|
-
const FF_VOLUME_UP = 183;
|
|
855
|
-
const SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
856
|
-
const EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
857
|
-
const COMMA = 188;
|
|
858
|
-
const DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
859
|
-
const PERIOD = 190;
|
|
860
|
-
const SLASH = 191;
|
|
861
|
-
const APOSTROPHE = 192;
|
|
862
|
-
const TILDE = 192;
|
|
863
|
-
const OPEN_SQUARE_BRACKET = 219;
|
|
864
|
-
const BACKSLASH = 220;
|
|
865
|
-
const CLOSE_SQUARE_BRACKET = 221;
|
|
866
|
-
const SINGLE_QUOTE = 222;
|
|
867
|
-
const MAC_META = 224;
|
|
868
|
-
|
|
869
|
-
var ResizeCursorClass;
|
|
870
|
-
(function (ResizeCursorClass) {
|
|
871
|
-
ResizeCursorClass["ew-resize"] = "ew-resize";
|
|
872
|
-
})(ResizeCursorClass || (ResizeCursorClass = {}));
|
|
873
|
-
|
|
874
|
-
const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
|
|
875
998
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
}
|
|
886
|
-
|
|
999
|
+
function drawLine(rs, start, end, options) {
|
|
1000
|
+
return rs.linearPath([start, end], options);
|
|
1001
|
+
}
|
|
1002
|
+
function drawLinearPath(points, options, closePath) {
|
|
1003
|
+
const g = createG();
|
|
1004
|
+
const path = createPath();
|
|
1005
|
+
let polylinePath = '';
|
|
1006
|
+
points.forEach((point, index) => {
|
|
1007
|
+
if (index === 0) {
|
|
1008
|
+
polylinePath += `M ${point[0]} ${point[1]} `;
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
polylinePath += `L ${point[0]} ${point[1]} `;
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
if (closePath) {
|
|
1015
|
+
polylinePath += 'Z';
|
|
1016
|
+
}
|
|
1017
|
+
path.setAttribute('d', polylinePath);
|
|
1018
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1019
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1020
|
+
path.setAttribute('fill', `${options?.fill || 'none'}`);
|
|
1021
|
+
options?.strokeLineDash && path.setAttribute('stroke-dasharray', `${options.strokeLineDash}`);
|
|
1022
|
+
g.appendChild(path);
|
|
1023
|
+
return g;
|
|
1024
|
+
}
|
|
1025
|
+
function drawBezierPath(points, options) {
|
|
1026
|
+
const g = createG();
|
|
1027
|
+
const path = createPath();
|
|
1028
|
+
let polylinePath = '';
|
|
1029
|
+
for (let i = 0; i < points.length - 3; i += 3) {
|
|
1030
|
+
if (i === 0) {
|
|
1031
|
+
polylinePath += `M ${points[0][0]} ${points[0][1]} `;
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
polylinePath += `C ${points[i + 1][0]} ${points[i + 1][1]}, ${points[i + 2][0]} ${points[i + 2][1]}, ${points[i + 3][0]} ${points[i + 3][1]}`;
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
path.setAttribute('d', polylinePath);
|
|
1038
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1039
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1040
|
+
path.setAttribute('fill', `none`);
|
|
1041
|
+
g.appendChild(path);
|
|
1042
|
+
return g;
|
|
1043
|
+
}
|
|
887
1044
|
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1045
|
+
const IS_FROM_SCROLLING = new WeakMap();
|
|
1046
|
+
const IS_FROM_VIEWPORT_CHANGE = new WeakMap();
|
|
1047
|
+
function getViewportContainerRect(board) {
|
|
1048
|
+
const { hideScrollbar } = board.options;
|
|
1049
|
+
const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
|
|
1050
|
+
const viewportRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1051
|
+
return {
|
|
1052
|
+
width: viewportRect.width + scrollBarWidth,
|
|
1053
|
+
height: viewportRect.height + scrollBarWidth
|
|
1054
|
+
};
|
|
892
1055
|
}
|
|
893
|
-
function
|
|
894
|
-
const
|
|
895
|
-
|
|
1056
|
+
function getElementHostBBox(board, zoom) {
|
|
1057
|
+
const childrenRect = getRectangleByElements(board, board.children, true);
|
|
1058
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1059
|
+
const containerWidth = viewportContainerRect.width / zoom;
|
|
1060
|
+
const containerHeight = viewportContainerRect.height / zoom;
|
|
1061
|
+
let left;
|
|
1062
|
+
let right;
|
|
1063
|
+
let top;
|
|
1064
|
+
let bottom;
|
|
1065
|
+
if (childrenRect.width < containerWidth) {
|
|
1066
|
+
const centerX = childrenRect.x + childrenRect.width / 2;
|
|
1067
|
+
const halfContainerWidth = containerWidth / 2;
|
|
1068
|
+
left = centerX - halfContainerWidth;
|
|
1069
|
+
right = centerX + halfContainerWidth;
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
left = childrenRect.x;
|
|
1073
|
+
right = childrenRect.x + childrenRect.width;
|
|
1074
|
+
}
|
|
1075
|
+
if (childrenRect.height < containerHeight) {
|
|
1076
|
+
const centerY = childrenRect.y + childrenRect.height / 2;
|
|
1077
|
+
const halfContainerHeight = containerHeight / 2;
|
|
1078
|
+
top = centerY - halfContainerHeight;
|
|
1079
|
+
bottom = centerY + halfContainerHeight;
|
|
1080
|
+
}
|
|
1081
|
+
else {
|
|
1082
|
+
top = childrenRect.y;
|
|
1083
|
+
bottom = childrenRect.y + childrenRect.height;
|
|
1084
|
+
}
|
|
1085
|
+
return {
|
|
1086
|
+
left,
|
|
1087
|
+
right,
|
|
1088
|
+
top,
|
|
1089
|
+
bottom
|
|
1090
|
+
};
|
|
896
1091
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1092
|
+
/**
|
|
1093
|
+
* 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
|
|
1094
|
+
* @param zoom 缩放比
|
|
1095
|
+
* @param minZoom 最小缩放比
|
|
1096
|
+
* @param maxZoom 最大缩放比
|
|
1097
|
+
* @returns 正确的缩放比
|
|
1098
|
+
*/
|
|
1099
|
+
function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
|
|
1100
|
+
return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
|
|
900
1101
|
}
|
|
901
|
-
function
|
|
902
|
-
const
|
|
903
|
-
|
|
1102
|
+
function getViewBox(board, zoom) {
|
|
1103
|
+
const boardContainerRectangle = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1104
|
+
const elementHostBBox = getElementHostBBox(board, zoom);
|
|
1105
|
+
const horizontalPadding = boardContainerRectangle.width / 2;
|
|
1106
|
+
const verticalPadding = boardContainerRectangle.height / 2;
|
|
1107
|
+
const viewBox = [
|
|
1108
|
+
elementHostBBox.left - horizontalPadding / zoom,
|
|
1109
|
+
elementHostBBox.top - verticalPadding / zoom,
|
|
1110
|
+
elementHostBBox.right - elementHostBBox.left + (horizontalPadding * 2) / zoom,
|
|
1111
|
+
elementHostBBox.bottom - elementHostBBox.top + (verticalPadding * 2) / zoom
|
|
1112
|
+
];
|
|
1113
|
+
return viewBox;
|
|
904
1114
|
}
|
|
905
|
-
function
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
text.setAttribute('y', `${y}`);
|
|
909
|
-
text.setAttribute('fill', fill);
|
|
910
|
-
text.textContent = textContent;
|
|
911
|
-
return text;
|
|
1115
|
+
function getViewBoxCenterPoint(board) {
|
|
1116
|
+
const childrenRectangle = getRectangleByElements(board, board.children, true);
|
|
1117
|
+
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
912
1118
|
}
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1119
|
+
function setSVGViewBox(board, viewBox) {
|
|
1120
|
+
const zoom = board.viewport.zoom;
|
|
1121
|
+
const hostElement = PlaitBoard.getHost(board);
|
|
1122
|
+
hostElement.style.display = 'block';
|
|
1123
|
+
hostElement.style.width = `${viewBox[2] * zoom}px`;
|
|
1124
|
+
hostElement.style.height = `${viewBox[3] * zoom}px`;
|
|
1125
|
+
if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
|
|
1126
|
+
hostElement.setAttribute('viewBox', viewBox.join(' '));
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function updateViewportOffset(board) {
|
|
1130
|
+
const origination = getViewportOrigination(board);
|
|
1131
|
+
if (!origination)
|
|
1132
|
+
return;
|
|
1133
|
+
const { zoom } = board.viewport;
|
|
1134
|
+
const viewBox = getViewBox(board, zoom);
|
|
1135
|
+
const scrollLeft = (origination[0] - viewBox[0]) * zoom;
|
|
1136
|
+
const scrollTop = (origination[1] - viewBox[1]) * zoom;
|
|
1137
|
+
updateViewportContainerScroll(board, scrollLeft, scrollTop);
|
|
1138
|
+
}
|
|
1139
|
+
function updateViewportContainerScroll(board, left, top, isFromViewportChange = true) {
|
|
1140
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1141
|
+
if (viewportContainer.scrollLeft !== left || viewportContainer.scrollTop !== top) {
|
|
1142
|
+
viewportContainer.scrollLeft = left;
|
|
1143
|
+
viewportContainer.scrollTop = top;
|
|
1144
|
+
isFromViewportChange && setIsFromViewportChange(board, true);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
function initializeViewportContainer(board) {
|
|
1148
|
+
const { width, height } = getViewportContainerRect(board);
|
|
1149
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1150
|
+
viewportContainer.style.width = `${width}px`;
|
|
1151
|
+
viewportContainer.style.height = `${height}px`;
|
|
1152
|
+
}
|
|
1153
|
+
function initializeViewBox(board) {
|
|
1154
|
+
const zoom = board.viewport.zoom;
|
|
1155
|
+
const viewBox = getViewBox(board, zoom);
|
|
1156
|
+
setSVGViewBox(board, viewBox);
|
|
1157
|
+
}
|
|
1158
|
+
function initializeViewportOffset(board) {
|
|
1159
|
+
if (!board.viewport?.origination) {
|
|
1160
|
+
const zoom = board.viewport.zoom;
|
|
1161
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1162
|
+
const viewBox = getViewBox(board, zoom);
|
|
1163
|
+
const centerX = viewBox[0] + viewBox[2] / 2;
|
|
1164
|
+
const centerY = viewBox[1] + viewBox[3] / 2;
|
|
1165
|
+
const origination = [centerX - viewportContainerRect.width / 2 / zoom, centerY - viewportContainerRect.height / 2 / zoom];
|
|
1166
|
+
updateViewportOrigination(board, origination);
|
|
1167
|
+
updateViewportOffset(board);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
updateViewportOffset(board);
|
|
1171
|
+
}
|
|
1172
|
+
const updateViewportOrigination = (board, origination) => {
|
|
1173
|
+
BOARD_TO_VIEWPORT_ORIGINATION.set(board, origination);
|
|
918
1174
|
};
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
*/
|
|
922
|
-
const isDOMNode = (value) => {
|
|
923
|
-
return value instanceof window.Node;
|
|
1175
|
+
const clearViewportOrigination = (board) => {
|
|
1176
|
+
BOARD_TO_VIEWPORT_ORIGINATION.delete(board);
|
|
924
1177
|
};
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1178
|
+
const getViewportOrigination = (board) => {
|
|
1179
|
+
const origination = BOARD_TO_VIEWPORT_ORIGINATION.get(board);
|
|
1180
|
+
if (origination) {
|
|
1181
|
+
return origination;
|
|
1182
|
+
}
|
|
1183
|
+
else {
|
|
1184
|
+
return board.viewport.origination;
|
|
930
1185
|
}
|
|
931
|
-
return false;
|
|
932
1186
|
};
|
|
933
|
-
const
|
|
934
|
-
return
|
|
1187
|
+
const isFromScrolling = (board) => {
|
|
1188
|
+
return !!IS_FROM_SCROLLING.get(board);
|
|
935
1189
|
};
|
|
936
|
-
const
|
|
937
|
-
|
|
1190
|
+
const setIsFromScrolling = (board, state) => {
|
|
1191
|
+
IS_FROM_SCROLLING.set(board, state);
|
|
938
1192
|
};
|
|
1193
|
+
const isFromViewportChange = (board) => {
|
|
1194
|
+
return !!IS_FROM_VIEWPORT_CHANGE.get(board);
|
|
1195
|
+
};
|
|
1196
|
+
const setIsFromViewportChange = (board, state) => {
|
|
1197
|
+
IS_FROM_VIEWPORT_CHANGE.set(board, state);
|
|
1198
|
+
};
|
|
1199
|
+
function scrollToRectangle(board, client) { }
|
|
939
1200
|
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
else {
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
1201
|
+
let timerId = null;
|
|
1202
|
+
const throttleRAF = (fn) => {
|
|
1203
|
+
const scheduleFunc = () => {
|
|
1204
|
+
timerId = requestAnimationFrame(() => {
|
|
1205
|
+
timerId = null;
|
|
1206
|
+
fn();
|
|
1207
|
+
});
|
|
1208
|
+
};
|
|
1209
|
+
if (timerId !== null) {
|
|
1210
|
+
cancelAnimationFrame(timerId);
|
|
1211
|
+
timerId = null;
|
|
954
1212
|
}
|
|
1213
|
+
scheduleFunc();
|
|
955
1214
|
};
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
}
|
|
965
|
-
if (!PlaitBoard.isBoard(node) &&
|
|
966
|
-
match(node) &&
|
|
967
|
-
realSelection &&
|
|
968
|
-
realSelection.ranges.some(range => {
|
|
969
|
-
return board.isHitSelection(node, range);
|
|
970
|
-
})) {
|
|
971
|
-
selectedElements.push(node);
|
|
1215
|
+
const debounce = (func, wait, options) => {
|
|
1216
|
+
let timerSubscription = null;
|
|
1217
|
+
return () => {
|
|
1218
|
+
if (timerSubscription && !timerSubscription.closed) {
|
|
1219
|
+
timerSubscription.unsubscribe();
|
|
1220
|
+
timerSubscription = timer(wait).subscribe(() => {
|
|
1221
|
+
func();
|
|
1222
|
+
});
|
|
972
1223
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const getHitElementOfRoot = (board, rootElements, range) => {
|
|
977
|
-
const newRootElements = [...rootElements].reverse();
|
|
978
|
-
return newRootElements.find(item => {
|
|
979
|
-
return board.isHitSelection(item, range);
|
|
980
|
-
});
|
|
981
|
-
};
|
|
982
|
-
const isHitElements = (board, elements, ranges) => {
|
|
983
|
-
let isIntersectionElements = false;
|
|
984
|
-
if (elements.length) {
|
|
985
|
-
elements.map(item => {
|
|
986
|
-
if (!isIntersectionElements) {
|
|
987
|
-
isIntersectionElements = ranges.some(range => {
|
|
988
|
-
return board.isHitSelection(item, range);
|
|
989
|
-
});
|
|
1224
|
+
else {
|
|
1225
|
+
if (options?.leading) {
|
|
1226
|
+
func();
|
|
990
1227
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
};
|
|
995
|
-
const cacheSelectedElements = (board, selectedElements) => {
|
|
996
|
-
BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
|
|
997
|
-
};
|
|
998
|
-
const getSelectedElements = (board) => {
|
|
999
|
-
return BOARD_TO_SELECTED_ELEMENT.get(board) || [];
|
|
1228
|
+
timerSubscription = timer(wait).subscribe();
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1000
1231
|
};
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1232
|
+
|
|
1233
|
+
const getMovingElements = (board) => {
|
|
1234
|
+
return BOARD_TO_MOVING_ELEMENT.get(board) || [];
|
|
1004
1235
|
};
|
|
1005
|
-
const
|
|
1006
|
-
const
|
|
1007
|
-
const
|
|
1008
|
-
|
|
1236
|
+
const addMovingElements = (board, elements) => {
|
|
1237
|
+
const movingElements = getMovingElements(board);
|
|
1238
|
+
const newElements = elements.filter(item => !movingElements.find(movingElement => movingElement.key === item.key));
|
|
1239
|
+
cacheMovingElements(board, [...movingElements, ...newElements]);
|
|
1009
1240
|
};
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1241
|
+
const removeMovingElements = (board) => {
|
|
1242
|
+
BOARD_TO_MOVING_ELEMENT.delete(board);
|
|
1012
1243
|
};
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
return !!selectedElements.find(value => value === element);
|
|
1244
|
+
const cacheMovingElements = (board, elements) => {
|
|
1245
|
+
BOARD_TO_MOVING_ELEMENT.set(board, elements);
|
|
1016
1246
|
};
|
|
1017
1247
|
|
|
1018
|
-
function
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
return false;
|
|
1023
|
-
}
|
|
1024
|
-
function hasOnContextChanged(value) {
|
|
1025
|
-
if (value.onContextChanged) {
|
|
1026
|
-
return true;
|
|
1027
|
-
}
|
|
1028
|
-
return false;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
class PlaitPluginElementComponent {
|
|
1032
|
-
set context(value) {
|
|
1033
|
-
if (hasBeforeContextChange(this)) {
|
|
1034
|
-
this.beforeContextChange(value);
|
|
1035
|
-
}
|
|
1036
|
-
const previousContext = this._context;
|
|
1037
|
-
this._context = value;
|
|
1038
|
-
if (this.element) {
|
|
1039
|
-
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
1040
|
-
}
|
|
1041
|
-
if (this.initialized) {
|
|
1042
|
-
this.cdr.markForCheck();
|
|
1043
|
-
if (hasOnContextChanged(this)) {
|
|
1044
|
-
this.onContextChanged(value, previousContext);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
else {
|
|
1048
|
-
if (PlaitElement.isRootElement(this.element) && this.element.children) {
|
|
1049
|
-
this.g = createG();
|
|
1050
|
-
this.rootG = createG();
|
|
1051
|
-
this.rootG.append(this.g);
|
|
1052
|
-
}
|
|
1053
|
-
else {
|
|
1054
|
-
this.g = createG();
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
get context() {
|
|
1059
|
-
return this._context;
|
|
1060
|
-
}
|
|
1061
|
-
get element() {
|
|
1062
|
-
return this.context && this.context.element;
|
|
1063
|
-
}
|
|
1064
|
-
get board() {
|
|
1065
|
-
return this.context && this.context.board;
|
|
1066
|
-
}
|
|
1067
|
-
get selected() {
|
|
1068
|
-
return this.context && this.context.selected;
|
|
1069
|
-
}
|
|
1070
|
-
get effect() {
|
|
1071
|
-
return this.context && this.context.effect;
|
|
1248
|
+
function cloneCSSStyle(nativeNode, clonedNode) {
|
|
1249
|
+
const targetStyle = clonedNode.style;
|
|
1250
|
+
if (!targetStyle) {
|
|
1251
|
+
return;
|
|
1072
1252
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1253
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
1254
|
+
if (sourceStyle.cssText) {
|
|
1255
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
1256
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
1076
1257
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1258
|
+
else {
|
|
1259
|
+
Array.from(sourceStyle).forEach(name => {
|
|
1260
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
1261
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
1262
|
+
});
|
|
1082
1263
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1264
|
+
}
|
|
1265
|
+
function createCanvas(width, height, fillStyle) {
|
|
1266
|
+
const canvas = document.createElement('canvas');
|
|
1267
|
+
const ctx = canvas.getContext('2d');
|
|
1268
|
+
canvas.width = width;
|
|
1269
|
+
canvas.height = height;
|
|
1270
|
+
canvas.style.width = `${width}px`;
|
|
1271
|
+
canvas.style.height = `${height}px`;
|
|
1272
|
+
ctx.strokeStyle = '#ffffff';
|
|
1273
|
+
ctx.fillStyle = fillStyle;
|
|
1274
|
+
ctx.fillRect(0, 0, width, height);
|
|
1275
|
+
return {
|
|
1276
|
+
canvas,
|
|
1277
|
+
ctx
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function isElementNode(node) {
|
|
1281
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
1282
|
+
}
|
|
1283
|
+
function cloneSvg(board, options) {
|
|
1284
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1285
|
+
const { width, height, x, y } = elementHostBox;
|
|
1286
|
+
const { padding = 4, inlineStyleClassNames } = options;
|
|
1287
|
+
const sourceSvg = PlaitBoard.getHost(board);
|
|
1288
|
+
const cloneSvgElement = sourceSvg.cloneNode(true);
|
|
1289
|
+
cloneSvgElement.style.width = `${width}px`;
|
|
1290
|
+
cloneSvgElement.style.height = `${height}px`;
|
|
1291
|
+
cloneSvgElement.style.backgroundColor = '';
|
|
1292
|
+
cloneSvgElement.setAttribute('width', `${width}`);
|
|
1293
|
+
cloneSvgElement.setAttribute('height', `${height}`);
|
|
1294
|
+
cloneSvgElement.setAttribute('viewBox', [x - padding, y - padding, width + 2 * padding, height + 2 * padding].join(','));
|
|
1295
|
+
if (inlineStyleClassNames) {
|
|
1296
|
+
const sourceNodes = Array.from(sourceSvg.querySelectorAll(inlineStyleClassNames));
|
|
1297
|
+
const cloneNodes = Array.from(cloneSvgElement.querySelectorAll(inlineStyleClassNames));
|
|
1298
|
+
sourceNodes.forEach((node, index) => {
|
|
1299
|
+
const cloneNode = cloneNodes[index];
|
|
1300
|
+
const childElements = Array.from(node.querySelectorAll('*')).filter(isElementNode);
|
|
1301
|
+
const cloneChildElements = Array.from(cloneNode.querySelectorAll('*')).filter(isElementNode);
|
|
1302
|
+
sourceNodes.push(...childElements);
|
|
1303
|
+
cloneNodes.push(...cloneChildElements);
|
|
1304
|
+
});
|
|
1305
|
+
sourceNodes.forEach((node, index) => {
|
|
1306
|
+
const cloneNode = cloneNodes[index];
|
|
1307
|
+
cloneCSSStyle(node, cloneNode);
|
|
1308
|
+
});
|
|
1089
1309
|
}
|
|
1310
|
+
return cloneSvgElement;
|
|
1090
1311
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}] } });
|
|
1098
|
-
const ELEMENT_TO_COMPONENT = new WeakMap();
|
|
1099
|
-
|
|
1100
|
-
function transformPoints(board, points) {
|
|
1101
|
-
const newPoints = points.map(point => {
|
|
1102
|
-
return transformPoint(board, point);
|
|
1312
|
+
function loadImage(src) {
|
|
1313
|
+
return new Promise((resolve, reject) => {
|
|
1314
|
+
const img = new Image();
|
|
1315
|
+
img.onload = () => resolve(img);
|
|
1316
|
+
img.onerror = () => reject(new Error('Failed to load image'));
|
|
1317
|
+
img.src = src;
|
|
1103
1318
|
});
|
|
1104
|
-
return newPoints;
|
|
1105
1319
|
}
|
|
1106
|
-
function
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
const
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1320
|
+
async function toImage(board, options) {
|
|
1321
|
+
if (!board) {
|
|
1322
|
+
return undefined;
|
|
1323
|
+
}
|
|
1324
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1325
|
+
const { ratio = 2, fillStyle = 'transparent' } = options;
|
|
1326
|
+
const { width, height } = elementHostBox;
|
|
1327
|
+
const ratioWidth = width * ratio;
|
|
1328
|
+
const ratioHeight = height * ratio;
|
|
1329
|
+
const cloneSvgElement = cloneSvg(board, options);
|
|
1330
|
+
const { canvas, ctx } = createCanvas(ratioWidth, ratioHeight, fillStyle);
|
|
1331
|
+
const svgStr = new XMLSerializer().serializeToString(cloneSvgElement);
|
|
1332
|
+
const imgSrc = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;
|
|
1333
|
+
try {
|
|
1334
|
+
const img = await loadImage(imgSrc);
|
|
1335
|
+
ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight);
|
|
1336
|
+
const url = canvas.toDataURL('image/png');
|
|
1337
|
+
return url;
|
|
1338
|
+
}
|
|
1339
|
+
catch (error) {
|
|
1340
|
+
console.error('Error converting SVG to image:', error);
|
|
1341
|
+
return undefined;
|
|
1342
|
+
}
|
|
1113
1343
|
}
|
|
1114
|
-
function
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1344
|
+
function downloadImage(url, name) {
|
|
1345
|
+
const a = document.createElement('a');
|
|
1346
|
+
a.href = url;
|
|
1347
|
+
a.download = name;
|
|
1348
|
+
a.click();
|
|
1349
|
+
a.remove();
|
|
1119
1350
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1351
|
+
|
|
1352
|
+
const getClipboardByKey = (key) => {
|
|
1353
|
+
return `application/x-plait-${key}-fragment`;
|
|
1354
|
+
};
|
|
1355
|
+
const setClipboardData = (data, elements) => {
|
|
1356
|
+
const result = [...elements];
|
|
1357
|
+
const pluginContextResult = getDataFromClipboard(data);
|
|
1358
|
+
if (pluginContextResult) {
|
|
1359
|
+
result.push(...pluginContextResult);
|
|
1360
|
+
}
|
|
1361
|
+
const stringObj = JSON.stringify(result);
|
|
1362
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1363
|
+
data?.setData(`application/${CLIP_BOARD_FORMAT_KEY}`, encoded);
|
|
1364
|
+
};
|
|
1365
|
+
const setClipboardDataByText = (data, text) => {
|
|
1366
|
+
const pluginContextResult = getTextFromClipboard(data);
|
|
1367
|
+
data?.setData(`text/plain`, text + '\n' + pluginContextResult);
|
|
1368
|
+
};
|
|
1369
|
+
const setClipboardDataByMedia = (data, media, key) => {
|
|
1370
|
+
const stringObj = JSON.stringify(media);
|
|
1371
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1372
|
+
data?.setData(getClipboardByKey(key), encoded);
|
|
1373
|
+
};
|
|
1374
|
+
const getDataFromClipboard = (data) => {
|
|
1375
|
+
const encoded = data?.getData(`application/${CLIP_BOARD_FORMAT_KEY}`);
|
|
1376
|
+
let nodesData = [];
|
|
1377
|
+
if (encoded) {
|
|
1378
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1379
|
+
nodesData = JSON.parse(decoded);
|
|
1126
1380
|
}
|
|
1127
|
-
return
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
function updateForeignObject(g, width, height, x, y) {
|
|
1139
|
-
const foreignObject = g.querySelector('foreignObject');
|
|
1140
|
-
if (foreignObject) {
|
|
1141
|
-
foreignObject.setAttribute('width', `${width}`);
|
|
1142
|
-
foreignObject.setAttribute('height', `${height}`);
|
|
1143
|
-
foreignObject.setAttribute('x', `${x}`);
|
|
1144
|
-
foreignObject.setAttribute('y', `${y}`);
|
|
1381
|
+
return nodesData;
|
|
1382
|
+
};
|
|
1383
|
+
const getTextFromClipboard = (data) => {
|
|
1384
|
+
return (data ? data.getData(`text/plain`) : '');
|
|
1385
|
+
};
|
|
1386
|
+
const getClipboardDataByMedia = (data, key) => {
|
|
1387
|
+
const encoded = data?.getData(getClipboardByKey(key));
|
|
1388
|
+
let imageItem = null;
|
|
1389
|
+
if (encoded) {
|
|
1390
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1391
|
+
imageItem = JSON.parse(decoded);
|
|
1145
1392
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
const IS_MAC = typeof window != 'undefined' && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
|
1149
|
-
|
|
1150
|
-
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
1151
|
-
typeof window !== 'undefined' &&
|
|
1152
|
-
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
1153
|
-
!window.MSStream;
|
|
1154
|
-
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
1155
|
-
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
1156
|
-
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
1157
|
-
// "modern" Edge was released at 79.x
|
|
1158
|
-
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
1159
|
-
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
1160
|
-
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
1161
|
-
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
1393
|
+
return imageItem;
|
|
1394
|
+
};
|
|
1162
1395
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1396
|
+
const BOARD_TO_TOUCH_REF = new WeakMap();
|
|
1397
|
+
const isPreventTouchMove = (board) => {
|
|
1398
|
+
return !!BOARD_TO_TOUCH_REF.get(board);
|
|
1399
|
+
};
|
|
1400
|
+
const preventTouchMove = (board, event, state) => {
|
|
1401
|
+
if (state && (event.target instanceof HTMLElement || event.target instanceof SVGElement)) {
|
|
1402
|
+
BOARD_TO_TOUCH_REF.set(board, { state, target: event.target instanceof SVGElement ? event.target : undefined });
|
|
1403
|
+
}
|
|
1404
|
+
else {
|
|
1405
|
+
const ref = BOARD_TO_TOUCH_REF.get(board);
|
|
1406
|
+
if (ref) {
|
|
1407
|
+
BOARD_TO_TOUCH_REF.delete(board);
|
|
1408
|
+
ref.host?.remove();
|
|
1409
|
+
}
|
|
1169
1410
|
}
|
|
1170
|
-
return false;
|
|
1171
1411
|
};
|
|
1172
1412
|
/**
|
|
1173
|
-
*
|
|
1413
|
+
* some intersection maybe cause target is removed from current browser window,
|
|
1414
|
+
* after it was removed touch move event will not be fired
|
|
1415
|
+
* so scroll behavior will can not be prevented in mobile browser device
|
|
1416
|
+
* this function will prevent target element being remove.
|
|
1174
1417
|
*/
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1418
|
+
const handleTouchTarget = (board) => {
|
|
1419
|
+
const touchRef = BOARD_TO_TOUCH_REF.get(board);
|
|
1420
|
+
if (touchRef && touchRef.target && !touchRef.target.contains(PlaitBoard.getElementActiveHost(board))) {
|
|
1421
|
+
touchRef.target.style.opacity = '0';
|
|
1422
|
+
const host = createG();
|
|
1423
|
+
host.appendChild(touchRef.target);
|
|
1424
|
+
touchRef.host = host;
|
|
1425
|
+
PlaitBoard.getElementActiveHost(board).append(host);
|
|
1178
1426
|
}
|
|
1179
|
-
return true;
|
|
1180
1427
|
};
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1186
|
-
|
|
1428
|
+
|
|
1429
|
+
const PlaitElement = {
|
|
1430
|
+
isRootElement(value) {
|
|
1431
|
+
const parent = NODE_TO_PARENT.get(value);
|
|
1432
|
+
if (parent && PlaitBoard.isBoard(parent)) {
|
|
1433
|
+
return true;
|
|
1434
|
+
}
|
|
1435
|
+
else {
|
|
1436
|
+
return false;
|
|
1437
|
+
}
|
|
1438
|
+
},
|
|
1439
|
+
getComponent(value) {
|
|
1440
|
+
return ELEMENT_TO_COMPONENT.get(value);
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
const Path = {
|
|
1445
|
+
/**
|
|
1446
|
+
* Get a list of ancestor paths for a given path.
|
|
1447
|
+
*
|
|
1448
|
+
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
1449
|
+
* `reverse: true` option is passed, they are reversed.
|
|
1450
|
+
*/
|
|
1451
|
+
ancestors(path, options = {}) {
|
|
1452
|
+
const { reverse = false } = options;
|
|
1453
|
+
let paths = Path.levels(path, options);
|
|
1454
|
+
if (reverse) {
|
|
1455
|
+
paths = paths.slice(1);
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
paths = paths.slice(0, -1);
|
|
1459
|
+
}
|
|
1460
|
+
return paths;
|
|
1461
|
+
},
|
|
1462
|
+
/**
|
|
1463
|
+
* Get a list of paths at every level down to a path. Note: this is the same
|
|
1464
|
+
* as `Path.ancestors`, but including the path itself.
|
|
1465
|
+
*
|
|
1466
|
+
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
1467
|
+
* true` option is passed, they are reversed.
|
|
1468
|
+
*/
|
|
1469
|
+
levels(path, options = {}) {
|
|
1470
|
+
const { reverse = false } = options;
|
|
1471
|
+
const list = [];
|
|
1472
|
+
for (let i = 0; i <= path.length; i++) {
|
|
1473
|
+
list.push(path.slice(0, i));
|
|
1474
|
+
}
|
|
1475
|
+
if (reverse) {
|
|
1476
|
+
list.reverse();
|
|
1477
|
+
}
|
|
1478
|
+
return list;
|
|
1479
|
+
},
|
|
1480
|
+
parent(path) {
|
|
1481
|
+
if (path.length === 0) {
|
|
1482
|
+
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
1483
|
+
}
|
|
1484
|
+
return path.slice(0, -1);
|
|
1485
|
+
},
|
|
1486
|
+
next(path) {
|
|
1487
|
+
if (path.length === 0) {
|
|
1488
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
1489
|
+
}
|
|
1490
|
+
const last = path[path.length - 1];
|
|
1491
|
+
return path.slice(0, -1).concat(last + 1);
|
|
1492
|
+
},
|
|
1493
|
+
hasPrevious(path) {
|
|
1494
|
+
return path[path.length - 1] > 0;
|
|
1495
|
+
},
|
|
1496
|
+
previous(path) {
|
|
1497
|
+
if (path.length === 0) {
|
|
1498
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
|
|
1499
|
+
}
|
|
1500
|
+
const last = path[path.length - 1];
|
|
1501
|
+
return path.slice(0, -1).concat(last - 1);
|
|
1502
|
+
},
|
|
1503
|
+
/**
|
|
1504
|
+
* Check if a path is an ancestor of another.
|
|
1505
|
+
*/
|
|
1506
|
+
isAncestor(path, another) {
|
|
1507
|
+
return path.length < another.length && Path.compare(path, another) === 0;
|
|
1508
|
+
},
|
|
1509
|
+
/**
|
|
1510
|
+
* Compare a path to another, returning an integer indicating whether the path
|
|
1511
|
+
* was before, at, or after the other.
|
|
1512
|
+
*
|
|
1513
|
+
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
1514
|
+
* directly above or below the other. If you want exact matching, use
|
|
1515
|
+
* [[Path.equals]] instead.
|
|
1516
|
+
*/
|
|
1517
|
+
compare(path, another) {
|
|
1518
|
+
const min = Math.min(path.length, another.length);
|
|
1519
|
+
for (let i = 0; i < min; i++) {
|
|
1520
|
+
if (path[i] < another[i])
|
|
1521
|
+
return -1;
|
|
1522
|
+
if (path[i] > another[i])
|
|
1523
|
+
return 1;
|
|
1524
|
+
}
|
|
1525
|
+
return 0;
|
|
1526
|
+
},
|
|
1527
|
+
/**
|
|
1528
|
+
* Check if a path is exactly equal to another.
|
|
1529
|
+
*/
|
|
1530
|
+
equals(path, another) {
|
|
1531
|
+
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
1532
|
+
},
|
|
1533
|
+
/**
|
|
1534
|
+
* Check if a path ends before one of the indexes in another.
|
|
1535
|
+
*/
|
|
1536
|
+
endsBefore(path, another) {
|
|
1537
|
+
const i = path.length - 1;
|
|
1538
|
+
const as = path.slice(0, i);
|
|
1539
|
+
const bs = another.slice(0, i);
|
|
1540
|
+
const av = path[i];
|
|
1541
|
+
const bv = another[i];
|
|
1542
|
+
return Path.equals(as, bs) && av < bv;
|
|
1543
|
+
},
|
|
1544
|
+
/**
|
|
1545
|
+
* Check if a path is a sibling of another.
|
|
1546
|
+
*/
|
|
1547
|
+
isSibling(path, another) {
|
|
1548
|
+
if (path.length !== another.length) {
|
|
1549
|
+
return false;
|
|
1550
|
+
}
|
|
1551
|
+
const as = path.slice(0, -1);
|
|
1552
|
+
const bs = another.slice(0, -1);
|
|
1553
|
+
const al = path[path.length - 1];
|
|
1554
|
+
const bl = another[another.length - 1];
|
|
1555
|
+
return al !== bl && Path.equals(as, bs);
|
|
1556
|
+
},
|
|
1557
|
+
transform(path, operation) {
|
|
1558
|
+
return produce(path, p => {
|
|
1559
|
+
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
1560
|
+
if (!path || path?.length === 0) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
if (p === null) {
|
|
1564
|
+
return null;
|
|
1565
|
+
}
|
|
1566
|
+
switch (operation.type) {
|
|
1567
|
+
case 'insert_node': {
|
|
1568
|
+
const { path: op } = operation;
|
|
1569
|
+
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
1570
|
+
p[op.length - 1] += 1;
|
|
1571
|
+
}
|
|
1572
|
+
break;
|
|
1573
|
+
}
|
|
1574
|
+
case 'remove_node': {
|
|
1575
|
+
const { path: op } = operation;
|
|
1576
|
+
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
1577
|
+
return null;
|
|
1578
|
+
}
|
|
1579
|
+
else if (Path.endsBefore(op, p)) {
|
|
1580
|
+
p[op.length - 1] -= 1;
|
|
1581
|
+
}
|
|
1582
|
+
break;
|
|
1583
|
+
}
|
|
1584
|
+
case 'move_node': {
|
|
1585
|
+
const { path: op, newPath: onp } = operation;
|
|
1586
|
+
// If the old and new path are the same, it's a no-op.
|
|
1587
|
+
if (Path.equals(op, onp)) {
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
1591
|
+
const copy = onp.slice();
|
|
1592
|
+
// op.length <= onp.length is different for slate
|
|
1593
|
+
// resolve drag from [0, 0] to [0, 3] issue
|
|
1594
|
+
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
1595
|
+
copy[op.length - 1] -= 1;
|
|
1596
|
+
}
|
|
1597
|
+
return copy.concat(p.slice(op.length));
|
|
1598
|
+
}
|
|
1599
|
+
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
1600
|
+
if (Path.endsBefore(op, p)) {
|
|
1601
|
+
p[op.length - 1] -= 1;
|
|
1602
|
+
}
|
|
1603
|
+
else {
|
|
1604
|
+
p[op.length - 1] += 1;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
1608
|
+
if (Path.endsBefore(op, p)) {
|
|
1609
|
+
p[op.length - 1] -= 1;
|
|
1610
|
+
}
|
|
1611
|
+
p[onp.length - 1] += 1;
|
|
1612
|
+
}
|
|
1613
|
+
else if (Path.endsBefore(op, p)) {
|
|
1614
|
+
if (Path.equals(onp, p)) {
|
|
1615
|
+
p[onp.length - 1] += 1;
|
|
1616
|
+
}
|
|
1617
|
+
p[op.length - 1] -= 1;
|
|
1618
|
+
}
|
|
1619
|
+
break;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
return p;
|
|
1623
|
+
});
|
|
1187
1624
|
}
|
|
1188
|
-
return true;
|
|
1189
1625
|
};
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
return
|
|
1626
|
+
|
|
1627
|
+
const PlaitNode = {
|
|
1628
|
+
parent: (board, path) => {
|
|
1629
|
+
const parentPath = Path.parent(path);
|
|
1630
|
+
const p = PlaitNode.get(board, parentPath);
|
|
1631
|
+
return p;
|
|
1196
1632
|
},
|
|
1197
1633
|
/**
|
|
1198
|
-
*
|
|
1634
|
+
* Return a generator of all the ancestor nodes above a specific path.
|
|
1635
|
+
*
|
|
1636
|
+
* By default the order is top-down, from highest to lowest ancestor in
|
|
1637
|
+
* the tree, but you can pass the `reverse: true` option to go bottom-up.
|
|
1199
1638
|
*/
|
|
1200
|
-
|
|
1201
|
-
|
|
1639
|
+
*parents(root, path, options = {}) {
|
|
1640
|
+
for (const p of Path.ancestors(path, options)) {
|
|
1641
|
+
const n = PlaitNode.get(root, p);
|
|
1642
|
+
yield n;
|
|
1643
|
+
}
|
|
1202
1644
|
},
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1645
|
+
get(root, path) {
|
|
1646
|
+
let node = root;
|
|
1647
|
+
for (let i = 0; i < path.length; i++) {
|
|
1648
|
+
const p = path[i];
|
|
1649
|
+
if (!node || !node.children || !node.children[p]) {
|
|
1650
|
+
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
1651
|
+
}
|
|
1652
|
+
node = node.children[p];
|
|
1653
|
+
}
|
|
1654
|
+
return node;
|
|
1212
1655
|
},
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
fn();
|
|
1221
|
-
SAVING.set(board, prev);
|
|
1656
|
+
last(board, path) {
|
|
1657
|
+
let n = PlaitNode.get(board, path);
|
|
1658
|
+
while (n && n.children && n.children.length > 0) {
|
|
1659
|
+
const i = n.children.length - 1;
|
|
1660
|
+
n = n.children[i];
|
|
1661
|
+
}
|
|
1662
|
+
return n;
|
|
1222
1663
|
}
|
|
1223
1664
|
};
|
|
1224
1665
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
*/
|
|
1228
|
-
const HOTKEYS = {
|
|
1229
|
-
bold: 'mod+b',
|
|
1230
|
-
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
1231
|
-
moveBackward: 'left',
|
|
1232
|
-
moveForward: 'right',
|
|
1233
|
-
moveUp: 'up',
|
|
1234
|
-
moveDown: 'down',
|
|
1235
|
-
moveWordBackward: 'ctrl+left',
|
|
1236
|
-
moveWordForward: 'ctrl+right',
|
|
1237
|
-
deleteBackward: 'shift?+backspace',
|
|
1238
|
-
deleteForward: 'shift?+delete',
|
|
1239
|
-
extendBackward: 'shift+left',
|
|
1240
|
-
extendForward: 'shift+right',
|
|
1241
|
-
italic: 'mod+i',
|
|
1242
|
-
splitBlock: 'shift?+enter',
|
|
1243
|
-
undo: 'mod+z'
|
|
1244
|
-
};
|
|
1245
|
-
const APPLE_HOTKEYS = {
|
|
1246
|
-
moveLineBackward: 'opt+up',
|
|
1247
|
-
moveLineForward: 'opt+down',
|
|
1248
|
-
moveWordBackward: 'opt+left',
|
|
1249
|
-
moveWordForward: 'opt+right',
|
|
1250
|
-
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
1251
|
-
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
1252
|
-
deleteLineBackward: 'cmd+shift?+backspace',
|
|
1253
|
-
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
1254
|
-
deleteWordBackward: 'opt+shift?+backspace',
|
|
1255
|
-
deleteWordForward: 'opt+shift?+delete',
|
|
1256
|
-
extendLineBackward: 'opt+shift+up',
|
|
1257
|
-
extendLineForward: 'opt+shift+down',
|
|
1258
|
-
redo: 'cmd+shift+z',
|
|
1259
|
-
transposeCharacter: 'ctrl+t'
|
|
1260
|
-
};
|
|
1261
|
-
const WINDOWS_HOTKEYS = {
|
|
1262
|
-
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
1263
|
-
deleteWordForward: 'ctrl+shift?+delete',
|
|
1264
|
-
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
1666
|
+
const isSetViewportOperation = (value) => {
|
|
1667
|
+
return value.type === 'set_viewport';
|
|
1265
1668
|
};
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
const generic = HOTKEYS[key];
|
|
1271
|
-
const apple = APPLE_HOTKEYS[key];
|
|
1272
|
-
const windows = WINDOWS_HOTKEYS[key];
|
|
1273
|
-
const isGeneric = generic && isKeyHotkey(generic);
|
|
1274
|
-
const isApple = apple && isKeyHotkey(apple);
|
|
1275
|
-
const isWindows = windows && isKeyHotkey(windows);
|
|
1276
|
-
return (event) => {
|
|
1277
|
-
if (isGeneric && isGeneric(event)) {
|
|
1278
|
-
return true;
|
|
1279
|
-
}
|
|
1280
|
-
if (IS_APPLE && isApple && isApple(event)) {
|
|
1281
|
-
return true;
|
|
1282
|
-
}
|
|
1283
|
-
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
1284
|
-
return true;
|
|
1669
|
+
const inverse = (op) => {
|
|
1670
|
+
switch (op.type) {
|
|
1671
|
+
case 'insert_node': {
|
|
1672
|
+
return { ...op, type: 'remove_node' };
|
|
1285
1673
|
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
};
|
|
1289
|
-
/**
|
|
1290
|
-
* Hotkeys.
|
|
1291
|
-
*/
|
|
1292
|
-
const hotkeys = {
|
|
1293
|
-
isBold: create('bold'),
|
|
1294
|
-
isCompose: create('compose'),
|
|
1295
|
-
isMoveBackward: create('moveBackward'),
|
|
1296
|
-
isMoveForward: create('moveForward'),
|
|
1297
|
-
isMoveUp: create('moveUp'),
|
|
1298
|
-
isMoveDown: create('moveDown'),
|
|
1299
|
-
isDeleteBackward: create('deleteBackward'),
|
|
1300
|
-
isDeleteForward: create('deleteForward'),
|
|
1301
|
-
isDeleteLineBackward: create('deleteLineBackward'),
|
|
1302
|
-
isDeleteLineForward: create('deleteLineForward'),
|
|
1303
|
-
isDeleteWordBackward: create('deleteWordBackward'),
|
|
1304
|
-
isDeleteWordForward: create('deleteWordForward'),
|
|
1305
|
-
isExtendBackward: create('extendBackward'),
|
|
1306
|
-
isExtendForward: create('extendForward'),
|
|
1307
|
-
isExtendLineBackward: create('extendLineBackward'),
|
|
1308
|
-
isExtendLineForward: create('extendLineForward'),
|
|
1309
|
-
isItalic: create('italic'),
|
|
1310
|
-
isMoveLineBackward: create('moveLineBackward'),
|
|
1311
|
-
isMoveLineForward: create('moveLineForward'),
|
|
1312
|
-
isMoveWordBackward: create('moveWordBackward'),
|
|
1313
|
-
isMoveWordForward: create('moveWordForward'),
|
|
1314
|
-
isRedo: create('redo'),
|
|
1315
|
-
isSplitBlock: create('splitBlock'),
|
|
1316
|
-
isTransposeCharacter: create('transposeCharacter'),
|
|
1317
|
-
isUndo: create('undo')
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
|
-
function idCreator(length = 5) {
|
|
1321
|
-
// remove numeral
|
|
1322
|
-
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
1323
|
-
const maxPosition = $chars.length;
|
|
1324
|
-
let key = '';
|
|
1325
|
-
for (let i = 0; i < length; i++) {
|
|
1326
|
-
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
1327
|
-
}
|
|
1328
|
-
return key;
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
/**
|
|
1332
|
-
* drawRoundRectangle
|
|
1333
|
-
*/
|
|
1334
|
-
function drawRoundRectangle(rs, x1, y1, x2, y2, options, outline = false, borderRadius) {
|
|
1335
|
-
const width = Math.abs(x1 - x2);
|
|
1336
|
-
const height = Math.abs(y1 - y2);
|
|
1337
|
-
let radius = borderRadius || 0;
|
|
1338
|
-
if (radius === 0) {
|
|
1339
|
-
const defaultRadius = Math.min(width, height) / 8;
|
|
1340
|
-
let radius = defaultRadius;
|
|
1341
|
-
if (defaultRadius > MAX_RADIUS) {
|
|
1342
|
-
radius = outline ? MAX_RADIUS + 2 : MAX_RADIUS;
|
|
1674
|
+
case 'remove_node': {
|
|
1675
|
+
return { ...op, type: 'insert_node' };
|
|
1343
1676
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
end[0] > start[0] ? end[0] - rotateWidth : end[0] + rotateWidth,
|
|
1365
|
-
end[1] > start[1] ? end[1] - rotateHeight : end[1] + rotateHeight
|
|
1366
|
-
];
|
|
1367
|
-
const pointRight = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (degree * Math.PI) / 180);
|
|
1368
|
-
const pointLeft = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (-degree * Math.PI) / 180);
|
|
1369
|
-
return { pointLeft, pointRight };
|
|
1370
|
-
}
|
|
1371
|
-
function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 40) {
|
|
1372
|
-
const { pointLeft, pointRight } = arrowPoints(start, end, maxHypotenuseLength, degree);
|
|
1373
|
-
const arrowLineLeft = rs.linearPath([pointLeft, end], options);
|
|
1374
|
-
const arrowLineRight = rs.linearPath([pointRight, end], options);
|
|
1375
|
-
return [arrowLineLeft, arrowLineRight];
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
function drawCircle(roughSVG, point, diameter, options) {
|
|
1379
|
-
return roughSVG.circle(point[0], point[1], diameter, options);
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
function drawLine(rs, start, end, options) {
|
|
1383
|
-
return rs.linearPath([start, end], options);
|
|
1384
|
-
}
|
|
1385
|
-
function drawLinearPath(points, options) {
|
|
1386
|
-
const g = createG();
|
|
1387
|
-
const path = createPath();
|
|
1388
|
-
let polylinePath = '';
|
|
1389
|
-
points.forEach((point, index) => {
|
|
1390
|
-
if (index === 0) {
|
|
1391
|
-
polylinePath += `M ${point[0]} ${point[1]} `;
|
|
1677
|
+
case 'move_node': {
|
|
1678
|
+
const { newPath, path } = op;
|
|
1679
|
+
// PERF: in this case the move operation is a no-op anyways.
|
|
1680
|
+
if (Path.equals(newPath, path)) {
|
|
1681
|
+
return op;
|
|
1682
|
+
}
|
|
1683
|
+
// when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
|
|
1684
|
+
// shoud not return [0,2] -> [0,0] #WIK-8981
|
|
1685
|
+
// if (Path.isSibling(path, newPath)) {
|
|
1686
|
+
// return { ...op, path: newPath, newPath: path };
|
|
1687
|
+
// }
|
|
1688
|
+
// If the move does not happen within a single parent it is possible
|
|
1689
|
+
// for the move to impact the true path to the location where the node
|
|
1690
|
+
// was removed from and where it was inserted. We have to adjust for this
|
|
1691
|
+
// and find the original path. We can accomplish this (only in non-sibling)
|
|
1692
|
+
// moves by looking at the impact of the move operation on the node
|
|
1693
|
+
// after the original move path.
|
|
1694
|
+
const inversePath = Path.transform(path, op);
|
|
1695
|
+
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
1696
|
+
return { ...op, path: inversePath, newPath: inverseNewPath };
|
|
1392
1697
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1698
|
+
case 'set_node': {
|
|
1699
|
+
const { properties, newProperties } = op;
|
|
1700
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1395
1701
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1702
|
+
case 'set_selection': {
|
|
1703
|
+
const { properties, newProperties } = op;
|
|
1704
|
+
if (properties == null) {
|
|
1705
|
+
return {
|
|
1706
|
+
...op,
|
|
1707
|
+
properties: newProperties,
|
|
1708
|
+
newProperties: null
|
|
1709
|
+
};
|
|
1710
|
+
}
|
|
1711
|
+
else if (newProperties == null) {
|
|
1712
|
+
return {
|
|
1713
|
+
...op,
|
|
1714
|
+
properties: null,
|
|
1715
|
+
newProperties: properties
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
else {
|
|
1719
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1720
|
+
}
|
|
1411
1721
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1722
|
+
case 'set_viewport': {
|
|
1723
|
+
const { properties, newProperties } = op;
|
|
1724
|
+
if (properties == null) {
|
|
1725
|
+
return {
|
|
1726
|
+
...op,
|
|
1727
|
+
properties: newProperties,
|
|
1728
|
+
newProperties: newProperties
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
else if (newProperties == null) {
|
|
1732
|
+
return {
|
|
1733
|
+
...op,
|
|
1734
|
+
properties: properties,
|
|
1735
|
+
newProperties: properties
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
else {
|
|
1739
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
case 'set_theme': {
|
|
1743
|
+
const { properties, newProperties } = op;
|
|
1744
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1414
1745
|
}
|
|
1415
1746
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
return g;
|
|
1422
|
-
}
|
|
1747
|
+
};
|
|
1748
|
+
const PlaitOperation = {
|
|
1749
|
+
isSetViewportOperation,
|
|
1750
|
+
inverse
|
|
1751
|
+
};
|
|
1423
1752
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
timerId = requestAnimationFrame(() => {
|
|
1428
|
-
timerId = null;
|
|
1429
|
-
fn();
|
|
1430
|
-
});
|
|
1431
|
-
};
|
|
1432
|
-
if (timerId !== null) {
|
|
1433
|
-
cancelAnimationFrame(timerId);
|
|
1434
|
-
timerId = null;
|
|
1753
|
+
const Point = {
|
|
1754
|
+
isEquals(point, otherPoint) {
|
|
1755
|
+
return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
|
|
1435
1756
|
}
|
|
1436
|
-
scheduleFunc();
|
|
1437
1757
|
};
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
timerSubscription = timer(wait).subscribe(() => {
|
|
1444
|
-
func();
|
|
1445
|
-
});
|
|
1446
|
-
}
|
|
1447
|
-
else {
|
|
1448
|
-
if (options?.leading) {
|
|
1449
|
-
func();
|
|
1450
|
-
}
|
|
1451
|
-
timerSubscription = timer(wait).subscribe();
|
|
1452
|
-
}
|
|
1453
|
-
};
|
|
1758
|
+
|
|
1759
|
+
const Viewport = {
|
|
1760
|
+
isViewport: (value) => {
|
|
1761
|
+
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
1762
|
+
},
|
|
1454
1763
|
};
|
|
1455
1764
|
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1765
|
+
const SAVING = new WeakMap();
|
|
1766
|
+
const MERGING = new WeakMap();
|
|
1767
|
+
|
|
1768
|
+
var PlaitPluginKey;
|
|
1769
|
+
(function (PlaitPluginKey) {
|
|
1770
|
+
PlaitPluginKey["withSelection"] = "withSelection";
|
|
1771
|
+
})(PlaitPluginKey || (PlaitPluginKey = {}));
|
|
1772
|
+
|
|
1773
|
+
var ThemeColorMode;
|
|
1774
|
+
(function (ThemeColorMode) {
|
|
1775
|
+
ThemeColorMode["default"] = "default";
|
|
1776
|
+
ThemeColorMode["colorful"] = "colorful";
|
|
1777
|
+
ThemeColorMode["soft"] = "soft";
|
|
1778
|
+
ThemeColorMode["retro"] = "retro";
|
|
1779
|
+
ThemeColorMode["dark"] = "dark";
|
|
1780
|
+
ThemeColorMode["starry"] = "starry";
|
|
1781
|
+
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
1782
|
+
const DefaultThemeColor = {
|
|
1783
|
+
mode: ThemeColorMode.default,
|
|
1784
|
+
boardBackground: '#ffffff',
|
|
1785
|
+
textColor: '#333333'
|
|
1458
1786
|
};
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1787
|
+
const ColorfulThemeColor = {
|
|
1788
|
+
mode: ThemeColorMode.colorful,
|
|
1789
|
+
boardBackground: '#ffffff',
|
|
1790
|
+
textColor: '#333333'
|
|
1463
1791
|
};
|
|
1464
|
-
const
|
|
1465
|
-
|
|
1792
|
+
const SoftThemeColor = {
|
|
1793
|
+
mode: ThemeColorMode.soft,
|
|
1794
|
+
boardBackground: '#f5f5f5',
|
|
1795
|
+
textColor: '#333333'
|
|
1466
1796
|
};
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1797
|
+
const RetroThemeColor = {
|
|
1798
|
+
mode: ThemeColorMode.retro,
|
|
1799
|
+
boardBackground: '#f9f8ed',
|
|
1800
|
+
textColor: '#333333'
|
|
1801
|
+
};
|
|
1802
|
+
const DarkThemeColor = {
|
|
1803
|
+
mode: ThemeColorMode.dark,
|
|
1804
|
+
boardBackground: '#141414',
|
|
1805
|
+
textColor: '#FFFFFF'
|
|
1806
|
+
};
|
|
1807
|
+
const StarryThemeColor = {
|
|
1808
|
+
mode: ThemeColorMode.starry,
|
|
1809
|
+
boardBackground: '#0d2537',
|
|
1810
|
+
textColor: '#FFFFFF'
|
|
1469
1811
|
};
|
|
1812
|
+
const ThemeColors = [
|
|
1813
|
+
DefaultThemeColor,
|
|
1814
|
+
ColorfulThemeColor,
|
|
1815
|
+
SoftThemeColor,
|
|
1816
|
+
RetroThemeColor,
|
|
1817
|
+
DarkThemeColor,
|
|
1818
|
+
StarryThemeColor
|
|
1819
|
+
];
|
|
1470
1820
|
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
});
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
function createCanvas(width, height, fillStyle) {
|
|
1489
|
-
const canvas = document.createElement('canvas');
|
|
1490
|
-
const ctx = canvas.getContext('2d');
|
|
1491
|
-
canvas.width = width;
|
|
1492
|
-
canvas.height = height;
|
|
1493
|
-
canvas.style.width = `${width}px`;
|
|
1494
|
-
canvas.style.height = `${height}px`;
|
|
1495
|
-
ctx.strokeStyle = '#ffffff';
|
|
1496
|
-
ctx.fillStyle = fillStyle;
|
|
1497
|
-
ctx.fillRect(0, 0, width, height);
|
|
1498
|
-
return {
|
|
1499
|
-
canvas,
|
|
1500
|
-
ctx
|
|
1821
|
+
var Direction;
|
|
1822
|
+
(function (Direction) {
|
|
1823
|
+
Direction["left"] = "left";
|
|
1824
|
+
Direction["top"] = "top";
|
|
1825
|
+
Direction["right"] = "right";
|
|
1826
|
+
Direction["bottom"] = "bottom";
|
|
1827
|
+
})(Direction || (Direction = {}));
|
|
1828
|
+
|
|
1829
|
+
function getRectangleByElements(board, elements, recursion) {
|
|
1830
|
+
const boundaryBox = {
|
|
1831
|
+
left: Number.MAX_VALUE,
|
|
1832
|
+
top: Number.MAX_VALUE,
|
|
1833
|
+
right: Number.NEGATIVE_INFINITY,
|
|
1834
|
+
bottom: Number.NEGATIVE_INFINITY
|
|
1501
1835
|
};
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
const cloneNode = cloneNodes[index];
|
|
1530
|
-
cloneCSSStyle(node, cloneNode);
|
|
1531
|
-
});
|
|
1836
|
+
const calcRectangleClient = (node) => {
|
|
1837
|
+
const nodeRectangle = board.getRectangle(node);
|
|
1838
|
+
if (nodeRectangle) {
|
|
1839
|
+
boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
|
|
1840
|
+
boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
|
|
1841
|
+
boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
|
|
1842
|
+
boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
|
|
1843
|
+
}
|
|
1844
|
+
else {
|
|
1845
|
+
console.error(`can not get rectangle of element:`, node);
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
elements.forEach(element => {
|
|
1849
|
+
if (recursion) {
|
|
1850
|
+
depthFirstRecursion(element, node => calcRectangleClient(node), node => board.isRecursion(node));
|
|
1851
|
+
}
|
|
1852
|
+
else {
|
|
1853
|
+
calcRectangleClient(element);
|
|
1854
|
+
}
|
|
1855
|
+
});
|
|
1856
|
+
if (boundaryBox.left === Number.MAX_VALUE) {
|
|
1857
|
+
return {
|
|
1858
|
+
x: 0,
|
|
1859
|
+
y: 0,
|
|
1860
|
+
width: 0,
|
|
1861
|
+
height: 0
|
|
1862
|
+
};
|
|
1532
1863
|
}
|
|
1533
|
-
return
|
|
1864
|
+
return {
|
|
1865
|
+
x: boundaryBox.left,
|
|
1866
|
+
y: boundaryBox.top,
|
|
1867
|
+
width: boundaryBox.right - boundaryBox.left,
|
|
1868
|
+
height: boundaryBox.bottom - boundaryBox.top
|
|
1869
|
+
};
|
|
1534
1870
|
}
|
|
1535
|
-
function
|
|
1536
|
-
return
|
|
1537
|
-
const img = new Image();
|
|
1538
|
-
img.onload = () => resolve(img);
|
|
1539
|
-
img.onerror = () => reject(new Error('Failed to load image'));
|
|
1540
|
-
img.src = src;
|
|
1541
|
-
});
|
|
1871
|
+
function getBoardRectangle(board) {
|
|
1872
|
+
return getRectangleByElements(board, board.children, true);
|
|
1542
1873
|
}
|
|
1543
|
-
|
|
1544
|
-
if (!
|
|
1545
|
-
|
|
1546
|
-
}
|
|
1547
|
-
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1548
|
-
const { ratio = 2, fillStyle = 'transparent' } = options;
|
|
1549
|
-
const { width, height } = elementHostBox;
|
|
1550
|
-
const ratioWidth = width * ratio;
|
|
1551
|
-
const ratioHeight = height * ratio;
|
|
1552
|
-
const cloneSvgElement = cloneSvg(board, options);
|
|
1553
|
-
const { canvas, ctx } = createCanvas(ratioWidth, ratioHeight, fillStyle);
|
|
1554
|
-
const svgStr = new XMLSerializer().serializeToString(cloneSvgElement);
|
|
1555
|
-
const imgSrc = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;
|
|
1556
|
-
try {
|
|
1557
|
-
const img = await loadImage(imgSrc);
|
|
1558
|
-
ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight);
|
|
1559
|
-
const url = canvas.toDataURL('image/png');
|
|
1560
|
-
return url;
|
|
1561
|
-
}
|
|
1562
|
-
catch (error) {
|
|
1563
|
-
console.error('Error converting SVG to image:', error);
|
|
1564
|
-
return undefined;
|
|
1874
|
+
function getElementById(board, id, dataSource) {
|
|
1875
|
+
if (!dataSource) {
|
|
1876
|
+
dataSource = findElements(board, { match: (element) => true, recursion: (element) => true });
|
|
1565
1877
|
}
|
|
1878
|
+
let element = dataSource.find((element) => element.id === id);
|
|
1879
|
+
return element;
|
|
1566
1880
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1881
|
+
function findElements(board, options) {
|
|
1882
|
+
let elements = [];
|
|
1883
|
+
depthFirstRecursion(board, node => {
|
|
1884
|
+
if (!PlaitBoard.isBoard(node) && options.match(node)) {
|
|
1885
|
+
elements.push(node);
|
|
1886
|
+
}
|
|
1887
|
+
}, (value) => {
|
|
1888
|
+
if (PlaitBoard.isBoard(value)) {
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
else {
|
|
1892
|
+
return getIsRecursionFunc(board)(value) && options.recursion(value);
|
|
1893
|
+
}
|
|
1894
|
+
}, true);
|
|
1895
|
+
return elements;
|
|
1573
1896
|
}
|
|
1574
1897
|
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1898
|
+
const PlaitBoard = {
|
|
1899
|
+
isBoard(value) {
|
|
1900
|
+
const cachedIsBoard = IS_BOARD_CACHE.get(value);
|
|
1901
|
+
if (cachedIsBoard !== undefined) {
|
|
1902
|
+
return cachedIsBoard;
|
|
1903
|
+
}
|
|
1904
|
+
const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
|
|
1905
|
+
IS_BOARD_CACHE.set(value, isBoard);
|
|
1906
|
+
return isBoard;
|
|
1907
|
+
},
|
|
1908
|
+
findPath(board, node) {
|
|
1909
|
+
const path = [];
|
|
1910
|
+
let child = node;
|
|
1911
|
+
while (true) {
|
|
1912
|
+
const parent = NODE_TO_PARENT.get(child);
|
|
1913
|
+
if (parent == null) {
|
|
1914
|
+
if (PlaitBoard.isBoard(child)) {
|
|
1915
|
+
return path;
|
|
1916
|
+
}
|
|
1917
|
+
else {
|
|
1918
|
+
break;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
const i = NODE_TO_INDEX.get(child);
|
|
1922
|
+
if (i == null) {
|
|
1923
|
+
break;
|
|
1924
|
+
}
|
|
1925
|
+
path.unshift(i);
|
|
1926
|
+
child = parent;
|
|
1927
|
+
}
|
|
1928
|
+
throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
|
|
1929
|
+
},
|
|
1930
|
+
getHost(board) {
|
|
1931
|
+
return BOARD_TO_HOST.get(board);
|
|
1932
|
+
},
|
|
1933
|
+
getElementHost(board) {
|
|
1934
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.host;
|
|
1935
|
+
},
|
|
1936
|
+
getElementUpperHost(board) {
|
|
1937
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
|
|
1938
|
+
},
|
|
1939
|
+
getElementActiveHost(board) {
|
|
1940
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
|
|
1941
|
+
},
|
|
1942
|
+
getRoughSVG(board) {
|
|
1943
|
+
return BOARD_TO_ROUGH_SVG.get(board);
|
|
1944
|
+
},
|
|
1945
|
+
getComponent(board) {
|
|
1946
|
+
return BOARD_TO_COMPONENT.get(board);
|
|
1947
|
+
},
|
|
1948
|
+
getBoardContainer(board) {
|
|
1949
|
+
return PlaitBoard.getComponent(board).nativeElement;
|
|
1950
|
+
},
|
|
1951
|
+
getRectangle(board) {
|
|
1952
|
+
return getRectangleByElements(board, board.children, true);
|
|
1953
|
+
},
|
|
1954
|
+
getViewportContainer(board) {
|
|
1955
|
+
return PlaitBoard.getHost(board).parentElement;
|
|
1956
|
+
},
|
|
1957
|
+
isFocus(board) {
|
|
1958
|
+
return !!board.selection;
|
|
1959
|
+
},
|
|
1960
|
+
isReadonly(board) {
|
|
1961
|
+
return board.options.readonly;
|
|
1962
|
+
},
|
|
1963
|
+
hasBeenTextEditing(board) {
|
|
1964
|
+
return !!IS_TEXT_EDITABLE.get(board);
|
|
1965
|
+
},
|
|
1966
|
+
getPointer(board) {
|
|
1967
|
+
return board.pointer;
|
|
1968
|
+
},
|
|
1969
|
+
isPointer(board, pointer) {
|
|
1970
|
+
return board.pointer === pointer;
|
|
1971
|
+
},
|
|
1972
|
+
isInPointer(board, pointers) {
|
|
1973
|
+
const point = board.pointer;
|
|
1974
|
+
return pointers.includes(point);
|
|
1975
|
+
},
|
|
1976
|
+
getMovingPointInBoard(board) {
|
|
1977
|
+
return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
|
|
1978
|
+
},
|
|
1979
|
+
isMovingPointInBoard(board) {
|
|
1980
|
+
const point = BOARD_TO_MOVING_POINT.get(board);
|
|
1981
|
+
const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1982
|
+
if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
|
|
1983
|
+
return true;
|
|
1984
|
+
}
|
|
1985
|
+
return false;
|
|
1986
|
+
},
|
|
1987
|
+
getThemeColors(board) {
|
|
1988
|
+
return (board.options.themeColors || ThemeColors);
|
|
1603
1989
|
}
|
|
1604
|
-
return nodesData;
|
|
1605
|
-
};
|
|
1606
|
-
const getTextFromClipboard = (data) => {
|
|
1607
|
-
return (data ? data.getData(`text/plain`) : '');
|
|
1608
1990
|
};
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1991
|
+
|
|
1992
|
+
const applyToDraft = (board, selection, viewport, theme, op) => {
|
|
1993
|
+
switch (op.type) {
|
|
1994
|
+
case 'insert_node': {
|
|
1995
|
+
const { path, node } = op;
|
|
1996
|
+
const parent = PlaitNode.parent(board, path);
|
|
1997
|
+
const index = path[path.length - 1];
|
|
1998
|
+
if (!parent.children || index > parent.children.length) {
|
|
1999
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2000
|
+
}
|
|
2001
|
+
parent.children.splice(index, 0, node);
|
|
2002
|
+
break;
|
|
2003
|
+
}
|
|
2004
|
+
case 'remove_node': {
|
|
2005
|
+
const { path } = op;
|
|
2006
|
+
const parent = PlaitNode.parent(board, path);
|
|
2007
|
+
const index = path[path.length - 1];
|
|
2008
|
+
if (!parent.children || index > parent.children.length) {
|
|
2009
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2010
|
+
}
|
|
2011
|
+
parent.children.splice(index, 1);
|
|
2012
|
+
break;
|
|
2013
|
+
}
|
|
2014
|
+
case 'move_node': {
|
|
2015
|
+
const { path, newPath } = op;
|
|
2016
|
+
if (Path.isAncestor(path, newPath)) {
|
|
2017
|
+
throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
|
|
2018
|
+
}
|
|
2019
|
+
const node = PlaitNode.get(board, path);
|
|
2020
|
+
const parent = PlaitNode.parent(board, path);
|
|
2021
|
+
const index = path[path.length - 1];
|
|
2022
|
+
// This is tricky, but since the `path` and `newPath` both refer to
|
|
2023
|
+
// the same snapshot in time, there's a mismatch. After either
|
|
2024
|
+
// removing the original position, the second step's path can be out
|
|
2025
|
+
// of date. So instead of using the `op.newPath` directly, we
|
|
2026
|
+
// transform `op.path` to ascertain what the `newPath` would be after
|
|
2027
|
+
// the operation was applied.
|
|
2028
|
+
parent.children?.splice(index, 1);
|
|
2029
|
+
const truePath = Path.transform(path, op);
|
|
2030
|
+
const newParent = PlaitNode.get(board, Path.parent(truePath));
|
|
2031
|
+
const newIndex = truePath[truePath.length - 1];
|
|
2032
|
+
newParent.children?.splice(newIndex, 0, node);
|
|
2033
|
+
break;
|
|
2034
|
+
}
|
|
2035
|
+
case 'set_node': {
|
|
2036
|
+
const { path, properties, newProperties } = op;
|
|
2037
|
+
if (path.length === 0) {
|
|
2038
|
+
throw new Error(`Cannot set properties on the root node!`);
|
|
2039
|
+
}
|
|
2040
|
+
const node = PlaitNode.get(board, path);
|
|
2041
|
+
for (const key in newProperties) {
|
|
2042
|
+
const value = newProperties[key];
|
|
2043
|
+
if (value == null) {
|
|
2044
|
+
delete node[key];
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
node[key] = value;
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
// properties that were previously defined, but are now missing, must be deleted
|
|
2051
|
+
for (const key in properties) {
|
|
2052
|
+
if (!newProperties.hasOwnProperty(key)) {
|
|
2053
|
+
delete node[key];
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
break;
|
|
2057
|
+
}
|
|
2058
|
+
case 'set_viewport': {
|
|
2059
|
+
const { newProperties } = op;
|
|
2060
|
+
if (newProperties == null) {
|
|
2061
|
+
viewport = newProperties;
|
|
2062
|
+
}
|
|
2063
|
+
else {
|
|
2064
|
+
if (viewport == null) {
|
|
2065
|
+
if (!Viewport.isViewport(newProperties)) {
|
|
2066
|
+
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
2067
|
+
}
|
|
2068
|
+
viewport = { ...newProperties };
|
|
2069
|
+
}
|
|
2070
|
+
for (const key in newProperties) {
|
|
2071
|
+
const value = newProperties[key];
|
|
2072
|
+
if (value == null) {
|
|
2073
|
+
delete viewport[key];
|
|
2074
|
+
}
|
|
2075
|
+
else {
|
|
2076
|
+
viewport[key] = value;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
break;
|
|
2081
|
+
}
|
|
2082
|
+
case 'set_selection': {
|
|
2083
|
+
const { newProperties } = op;
|
|
2084
|
+
if (newProperties == null) {
|
|
2085
|
+
selection = newProperties;
|
|
2086
|
+
}
|
|
2087
|
+
else {
|
|
2088
|
+
if (selection === null) {
|
|
2089
|
+
selection = op.newProperties;
|
|
2090
|
+
}
|
|
2091
|
+
else {
|
|
2092
|
+
selection.ranges = newProperties.ranges;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
break;
|
|
2096
|
+
}
|
|
2097
|
+
case 'set_theme': {
|
|
2098
|
+
const { newProperties } = op;
|
|
2099
|
+
theme = newProperties;
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
1615
2102
|
}
|
|
1616
|
-
return
|
|
1617
|
-
};
|
|
1618
|
-
|
|
1619
|
-
const isPreventTouchMove = (board) => {
|
|
1620
|
-
return !!IS_PREVENT_TOUCH_MOVE.get(board);
|
|
1621
|
-
};
|
|
1622
|
-
const preventTouchMove = (board, state) => {
|
|
1623
|
-
IS_PREVENT_TOUCH_MOVE.set(board, state);
|
|
2103
|
+
return { selection, viewport, theme };
|
|
1624
2104
|
};
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
2105
|
+
const GeneralTransforms = {
|
|
2106
|
+
/**
|
|
2107
|
+
* Transform the board by an operation.
|
|
2108
|
+
*/
|
|
2109
|
+
transform(board, op) {
|
|
2110
|
+
board.children = createDraft(board.children);
|
|
2111
|
+
let viewport = board.viewport && createDraft(board.viewport);
|
|
2112
|
+
let selection = board.selection && createDraft(board.selection);
|
|
2113
|
+
let theme = board.theme && createDraft(board.theme);
|
|
2114
|
+
try {
|
|
2115
|
+
const state = applyToDraft(board, selection, viewport, theme, op);
|
|
2116
|
+
viewport = state.viewport;
|
|
2117
|
+
selection = state.selection;
|
|
2118
|
+
theme = state.theme;
|
|
1631
2119
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
2120
|
+
finally {
|
|
2121
|
+
board.children = finishDraft(board.children);
|
|
2122
|
+
if (selection) {
|
|
2123
|
+
board.selection = isDraft(selection) ? finishDraft(selection) : selection;
|
|
2124
|
+
}
|
|
2125
|
+
else {
|
|
2126
|
+
board.selection = null;
|
|
2127
|
+
}
|
|
2128
|
+
board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
|
|
2129
|
+
board.theme = isDraft(theme) ? finishDraft(theme) : theme;
|
|
1634
2130
|
}
|
|
1635
|
-
},
|
|
1636
|
-
getComponent(value) {
|
|
1637
|
-
return ELEMENT_TO_COMPONENT.get(value);
|
|
1638
2131
|
}
|
|
1639
2132
|
};
|
|
1640
2133
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2134
|
+
function insertNode(board, node, path) {
|
|
2135
|
+
const operation = { type: 'insert_node', node, path };
|
|
2136
|
+
board.apply(operation);
|
|
2137
|
+
}
|
|
2138
|
+
function setNode(board, props, path) {
|
|
2139
|
+
const properties = {};
|
|
2140
|
+
const newProperties = {};
|
|
2141
|
+
const node = PlaitNode.get(board, path);
|
|
2142
|
+
for (const k in props) {
|
|
2143
|
+
if (node[k] !== props[k]) {
|
|
2144
|
+
if (node.hasOwnProperty(k)) {
|
|
2145
|
+
properties[k] = node[k];
|
|
2146
|
+
}
|
|
2147
|
+
if (props[k] != null)
|
|
2148
|
+
newProperties[k] = props[k];
|
|
1653
2149
|
}
|
|
1654
|
-
},
|
|
1655
|
-
toRectangleClient: (points) => {
|
|
1656
|
-
const xArray = points.map(ele => ele[0]);
|
|
1657
|
-
const yArray = points.map(ele => ele[1]);
|
|
1658
|
-
const xMin = Math.min(...xArray);
|
|
1659
|
-
const xMax = Math.max(...xArray);
|
|
1660
|
-
const yMin = Math.min(...yArray);
|
|
1661
|
-
const yMax = Math.max(...yArray);
|
|
1662
|
-
const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
1663
|
-
return rect;
|
|
1664
|
-
},
|
|
1665
|
-
getOutlineRectangle: (rectangle, offset) => {
|
|
1666
|
-
return {
|
|
1667
|
-
x: rectangle.x + offset,
|
|
1668
|
-
y: rectangle.y + offset,
|
|
1669
|
-
width: rectangle.width + Math.abs(offset) * 2,
|
|
1670
|
-
height: rectangle.height + Math.abs(offset) * 2
|
|
1671
|
-
};
|
|
1672
|
-
},
|
|
1673
|
-
isEqual: (rectangle, otherRectangle) => {
|
|
1674
|
-
return (rectangle.x === otherRectangle.x &&
|
|
1675
|
-
rectangle.y === otherRectangle.y &&
|
|
1676
|
-
rectangle.width === otherRectangle.width &&
|
|
1677
|
-
rectangle.height === otherRectangle.height);
|
|
1678
2150
|
}
|
|
2151
|
+
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2152
|
+
board.apply(operation);
|
|
2153
|
+
}
|
|
2154
|
+
function removeNode(board, path) {
|
|
2155
|
+
const node = PlaitNode.get(board, path);
|
|
2156
|
+
const operation = { type: 'remove_node', path, node };
|
|
2157
|
+
board.apply(operation);
|
|
2158
|
+
}
|
|
2159
|
+
function moveNode(board, path, newPath) {
|
|
2160
|
+
const operation = { type: 'move_node', path, newPath };
|
|
2161
|
+
board.apply(operation);
|
|
2162
|
+
}
|
|
2163
|
+
const NodeTransforms = {
|
|
2164
|
+
insertNode,
|
|
2165
|
+
setNode,
|
|
2166
|
+
removeNode,
|
|
2167
|
+
moveNode
|
|
1679
2168
|
};
|
|
1680
2169
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
2170
|
+
function withSelection(board) {
|
|
2171
|
+
const { pointerDown, globalPointerMove, globalPointerUp, onChange } = board;
|
|
2172
|
+
let start = null;
|
|
2173
|
+
let end = null;
|
|
2174
|
+
let selectionMovingG;
|
|
2175
|
+
let selectionRectangleG;
|
|
2176
|
+
let previousSelectedElements;
|
|
2177
|
+
// prevent text from being selected when user pressed main pointer and is moving
|
|
2178
|
+
let needPreventNativeSelectionWhenMoving = false;
|
|
2179
|
+
board.pointerDown = (event) => {
|
|
2180
|
+
if (event.target instanceof Element && !event.target.closest('.plait-richtext-container')) {
|
|
2181
|
+
needPreventNativeSelectionWhenMoving = true;
|
|
1688
2182
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
2183
|
+
if (!isMainPointer(event)) {
|
|
2184
|
+
pointerDown(event);
|
|
2185
|
+
return;
|
|
1691
2186
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
// if (Path.isSibling(path, newPath)) {
|
|
1701
|
-
// return { ...op, path: newPath, newPath: path };
|
|
1702
|
-
// }
|
|
1703
|
-
// If the move does not happen within a single parent it is possible
|
|
1704
|
-
// for the move to impact the true path to the location where the node
|
|
1705
|
-
// was removed from and where it was inserted. We have to adjust for this
|
|
1706
|
-
// and find the original path. We can accomplish this (only in non-sibling)
|
|
1707
|
-
// moves by looking at the impact of the move operation on the node
|
|
1708
|
-
// after the original move path.
|
|
1709
|
-
const inversePath = Path.transform(path, op);
|
|
1710
|
-
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
1711
|
-
return { ...op, path: inversePath, newPath: inverseNewPath };
|
|
2187
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2188
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2189
|
+
const range = { anchor: point, focus: point };
|
|
2190
|
+
const hitElements = getHitElements(board, { ranges: [range] });
|
|
2191
|
+
const selectedElements = getSelectedElements(board);
|
|
2192
|
+
if (hitElements.length === 1 && selectedElements.includes(hitElements[0]) && !options.isDisabledSelect) {
|
|
2193
|
+
pointerDown(event);
|
|
2194
|
+
return;
|
|
1712
2195
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
2196
|
+
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) &&
|
|
2197
|
+
hitElements.length === 0 &&
|
|
2198
|
+
options.isMultiple &&
|
|
2199
|
+
!options.isDisabledSelect) {
|
|
2200
|
+
selectionRectangleG?.remove();
|
|
2201
|
+
start = point;
|
|
2202
|
+
preventTouchMove(board, event, true);
|
|
1716
2203
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
2204
|
+
Transforms.setSelection(board, { ranges: [range] });
|
|
2205
|
+
pointerDown(event);
|
|
2206
|
+
};
|
|
2207
|
+
board.globalPointerMove = (event) => {
|
|
2208
|
+
if (needPreventNativeSelectionWhenMoving) {
|
|
2209
|
+
// prevent text from being selected
|
|
2210
|
+
event.preventDefault();
|
|
2211
|
+
}
|
|
2212
|
+
if (start) {
|
|
2213
|
+
const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2214
|
+
const rectangle = RectangleClient.toRectangleClient([start, movedTarget]);
|
|
2215
|
+
selectionMovingG?.remove();
|
|
2216
|
+
if (Math.hypot(rectangle.width, rectangle.height) > 5) {
|
|
2217
|
+
end = movedTarget;
|
|
2218
|
+
throttleRAF(() => {
|
|
2219
|
+
if (start && end) {
|
|
2220
|
+
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
2221
|
+
}
|
|
2222
|
+
});
|
|
2223
|
+
setSelectionMoving(board);
|
|
2224
|
+
selectionMovingG = drawRectangle(board, rectangle, {
|
|
2225
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2226
|
+
strokeWidth: 1,
|
|
2227
|
+
fill: SELECTION_FILL_COLOR,
|
|
2228
|
+
fillStyle: 'solid'
|
|
2229
|
+
});
|
|
2230
|
+
PlaitBoard.getHost(board).append(selectionMovingG);
|
|
1735
2231
|
}
|
|
1736
2232
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
2233
|
+
globalPointerMove(event);
|
|
2234
|
+
};
|
|
2235
|
+
board.globalPointerUp = (event) => {
|
|
2236
|
+
if (start && end) {
|
|
2237
|
+
selectionMovingG?.remove();
|
|
2238
|
+
clearSelectionMoving(board);
|
|
2239
|
+
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
2240
|
+
}
|
|
2241
|
+
if (PlaitBoard.isFocus(board)) {
|
|
2242
|
+
const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
|
|
2243
|
+
const isInDocument = event.target instanceof Node && document.contains(event.target);
|
|
2244
|
+
const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
|
|
2245
|
+
// Clear selection when mouse board outside area
|
|
2246
|
+
// The framework needs to determine whether the board is focused through selection
|
|
2247
|
+
if (!isInBoard && !start && !isAttachedElement && isInDocument) {
|
|
2248
|
+
Transforms.setSelection(board, null);
|
|
1745
2249
|
}
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
2250
|
+
}
|
|
2251
|
+
start = null;
|
|
2252
|
+
end = null;
|
|
2253
|
+
needPreventNativeSelectionWhenMoving = false;
|
|
2254
|
+
preventTouchMove(board, event, false);
|
|
2255
|
+
globalPointerUp(event);
|
|
2256
|
+
};
|
|
2257
|
+
board.onChange = () => {
|
|
2258
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2259
|
+
if (options.isDisabledSelect) {
|
|
2260
|
+
clearSelectedElement(board);
|
|
2261
|
+
}
|
|
2262
|
+
// calc selected elements entry
|
|
2263
|
+
if (board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect) {
|
|
2264
|
+
try {
|
|
2265
|
+
if (board.operations.find(value => value.type === 'set_selection')) {
|
|
2266
|
+
selectionRectangleG?.remove();
|
|
2267
|
+
const temporaryElements = getTemporaryElements(board);
|
|
2268
|
+
let elements = temporaryElements ? temporaryElements : getHitElements(board);
|
|
2269
|
+
if (!options.isMultiple && elements.length > 1) {
|
|
2270
|
+
elements = [elements[0]];
|
|
2271
|
+
}
|
|
2272
|
+
cacheSelectedElements(board, elements);
|
|
2273
|
+
previousSelectedElements = elements;
|
|
2274
|
+
deleteTemporaryElements(board);
|
|
2275
|
+
if (!isSelectionMoving(board) && elements.length > 1) {
|
|
2276
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
else {
|
|
2280
|
+
// wait node destroy and remove selected element state
|
|
2281
|
+
setTimeout(() => {
|
|
2282
|
+
const currentSelectedElements = getSelectedElements(board);
|
|
2283
|
+
if (currentSelectedElements.length && currentSelectedElements.length > 1) {
|
|
2284
|
+
if (currentSelectedElements.length !== previousSelectedElements.length ||
|
|
2285
|
+
currentSelectedElements.some((c, index) => c !== previousSelectedElements[index])) {
|
|
2286
|
+
selectionRectangleG?.remove();
|
|
2287
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
2288
|
+
previousSelectedElements = currentSelectedElements;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
else {
|
|
2292
|
+
selectionRectangleG?.remove();
|
|
2293
|
+
}
|
|
2294
|
+
});
|
|
2295
|
+
}
|
|
1752
2296
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
2297
|
+
catch (error) {
|
|
2298
|
+
console.error(error);
|
|
1755
2299
|
}
|
|
1756
2300
|
}
|
|
1757
|
-
|
|
1758
|
-
const { properties, newProperties } = op;
|
|
1759
|
-
return { ...op, properties: newProperties, newProperties: properties };
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
};
|
|
1763
|
-
const PlaitOperation = {
|
|
1764
|
-
isSetViewportOperation,
|
|
1765
|
-
inverse
|
|
1766
|
-
};
|
|
1767
|
-
|
|
1768
|
-
const Point = {
|
|
1769
|
-
isEquals(point, otherPoint) {
|
|
1770
|
-
return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
|
|
1771
|
-
}
|
|
1772
|
-
};
|
|
1773
|
-
|
|
1774
|
-
const SAVING = new WeakMap();
|
|
1775
|
-
const MERGING = new WeakMap();
|
|
1776
|
-
|
|
1777
|
-
var PlaitPluginKey;
|
|
1778
|
-
(function (PlaitPluginKey) {
|
|
1779
|
-
PlaitPluginKey["withSelection"] = "withSelection";
|
|
1780
|
-
})(PlaitPluginKey || (PlaitPluginKey = {}));
|
|
1781
|
-
|
|
1782
|
-
const IS_FROM_SCROLLING = new WeakMap();
|
|
1783
|
-
const IS_FROM_VIEWPORT_CHANGE = new WeakMap();
|
|
1784
|
-
function getViewportContainerRect(board) {
|
|
1785
|
-
const { hideScrollbar } = board.options;
|
|
1786
|
-
const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
|
|
1787
|
-
const viewportRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1788
|
-
return {
|
|
1789
|
-
width: viewportRect.width + scrollBarWidth,
|
|
1790
|
-
height: viewportRect.height + scrollBarWidth
|
|
2301
|
+
onChange();
|
|
1791
2302
|
};
|
|
2303
|
+
board.setPluginOptions(PlaitPluginKey.withSelection, {
|
|
2304
|
+
isMultiple: true,
|
|
2305
|
+
isDisabledSelect: false
|
|
2306
|
+
});
|
|
2307
|
+
return board;
|
|
1792
2308
|
}
|
|
1793
|
-
function
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
const containerHeight = viewportContainerRect.height / zoom;
|
|
1798
|
-
let left;
|
|
1799
|
-
let right;
|
|
1800
|
-
let top;
|
|
1801
|
-
let bottom;
|
|
1802
|
-
if (childrenRect.width < containerWidth) {
|
|
1803
|
-
const centerX = childrenRect.x + childrenRect.width / 2;
|
|
1804
|
-
const halfContainerWidth = containerWidth / 2;
|
|
1805
|
-
left = centerX - halfContainerWidth;
|
|
1806
|
-
right = centerX + halfContainerWidth;
|
|
1807
|
-
}
|
|
1808
|
-
else {
|
|
1809
|
-
left = childrenRect.x;
|
|
1810
|
-
right = childrenRect.x + childrenRect.width;
|
|
1811
|
-
}
|
|
1812
|
-
if (childrenRect.height < containerHeight) {
|
|
1813
|
-
const centerY = childrenRect.y + childrenRect.height / 2;
|
|
1814
|
-
const halfContainerHeight = containerHeight / 2;
|
|
1815
|
-
top = centerY - halfContainerHeight;
|
|
1816
|
-
bottom = centerY + halfContainerHeight;
|
|
2309
|
+
function getTemporaryElements(board) {
|
|
2310
|
+
const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
2311
|
+
if (ref) {
|
|
2312
|
+
return ref.elements;
|
|
1817
2313
|
}
|
|
1818
2314
|
else {
|
|
1819
|
-
|
|
1820
|
-
bottom = childrenRect.y + childrenRect.height;
|
|
2315
|
+
return undefined;
|
|
1821
2316
|
}
|
|
1822
|
-
return {
|
|
1823
|
-
left,
|
|
1824
|
-
right,
|
|
1825
|
-
top,
|
|
1826
|
-
bottom
|
|
1827
|
-
};
|
|
1828
2317
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
* @param zoom 缩放比
|
|
1832
|
-
* @param minZoom 最小缩放比
|
|
1833
|
-
* @param maxZoom 最大缩放比
|
|
1834
|
-
* @returns 正确的缩放比
|
|
1835
|
-
*/
|
|
1836
|
-
function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
|
|
1837
|
-
return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
|
|
2318
|
+
function getTemporaryRef(board) {
|
|
2319
|
+
return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
1838
2320
|
}
|
|
1839
|
-
function
|
|
1840
|
-
|
|
1841
|
-
const elementHostBBox = getElementHostBBox(board, zoom);
|
|
1842
|
-
const horizontalPadding = boardContainerRectangle.width / 2;
|
|
1843
|
-
const verticalPadding = boardContainerRectangle.height / 2;
|
|
1844
|
-
const viewBox = [
|
|
1845
|
-
elementHostBBox.left - horizontalPadding / zoom,
|
|
1846
|
-
elementHostBBox.top - verticalPadding / zoom,
|
|
1847
|
-
elementHostBBox.right - elementHostBBox.left + (horizontalPadding * 2) / zoom,
|
|
1848
|
-
elementHostBBox.bottom - elementHostBBox.top + (verticalPadding * 2) / zoom
|
|
1849
|
-
];
|
|
1850
|
-
return viewBox;
|
|
2321
|
+
function deleteTemporaryElements(board) {
|
|
2322
|
+
BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
|
|
1851
2323
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
1854
|
-
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
2324
|
+
function isSelectionMoving(board) {
|
|
2325
|
+
return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
|
|
1855
2326
|
}
|
|
1856
|
-
function
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
hostElement.style.display = 'block';
|
|
1860
|
-
hostElement.style.width = `${viewBox[2] * zoom}px`;
|
|
1861
|
-
hostElement.style.height = `${viewBox[3] * zoom}px`;
|
|
1862
|
-
if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
|
|
1863
|
-
hostElement.setAttribute('viewBox', viewBox.join(' '));
|
|
1864
|
-
}
|
|
2327
|
+
function setSelectionMoving(board) {
|
|
2328
|
+
PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
|
|
2329
|
+
BOARD_TO_IS_SELECTION_MOVING.set(board, true);
|
|
1865
2330
|
}
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
return;
|
|
1870
|
-
const { zoom } = board.viewport;
|
|
1871
|
-
const viewBox = getViewBox(board, zoom);
|
|
1872
|
-
const scrollLeft = (origination[0] - viewBox[0]) * zoom;
|
|
1873
|
-
const scrollTop = (origination[1] - viewBox[1]) * zoom;
|
|
1874
|
-
updateViewportContainerScroll(board, scrollLeft, scrollTop);
|
|
2331
|
+
function clearSelectionMoving(board) {
|
|
2332
|
+
PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
|
|
2333
|
+
BOARD_TO_IS_SELECTION_MOVING.delete(board);
|
|
1875
2334
|
}
|
|
1876
|
-
function
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2335
|
+
function createSelectionRectangleG(board) {
|
|
2336
|
+
const elements = getSelectedElements(board);
|
|
2337
|
+
const rectangle = getRectangleByElements(board, elements, false);
|
|
2338
|
+
if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
|
|
2339
|
+
const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
|
|
2340
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2341
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
2342
|
+
fillStyle: 'solid'
|
|
2343
|
+
});
|
|
2344
|
+
selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
|
|
2345
|
+
PlaitBoard.getHost(board).append(selectionRectangleG);
|
|
2346
|
+
return selectionRectangleG;
|
|
1882
2347
|
}
|
|
2348
|
+
return null;
|
|
1883
2349
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
const
|
|
1887
|
-
|
|
1888
|
-
viewportContainer.style.height = `${height}px`;
|
|
1889
|
-
}
|
|
1890
|
-
function initializeViewBox(board) {
|
|
1891
|
-
const zoom = board.viewport.zoom;
|
|
1892
|
-
const viewBox = getViewBox(board, zoom);
|
|
1893
|
-
setSVGViewBox(board, viewBox);
|
|
1894
|
-
}
|
|
1895
|
-
function initializeViewportOffset(board) {
|
|
1896
|
-
if (!board.viewport?.origination) {
|
|
1897
|
-
const zoom = board.viewport.zoom;
|
|
1898
|
-
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1899
|
-
const viewBox = getViewBox(board, zoom);
|
|
1900
|
-
const centerX = viewBox[0] + viewBox[2] / 2;
|
|
1901
|
-
const centerY = viewBox[1] + viewBox[3] / 2;
|
|
1902
|
-
const origination = [centerX - viewportContainerRect.width / 2 / zoom, centerY - viewportContainerRect.height / 2 / zoom];
|
|
1903
|
-
updateViewportOrigination(board, origination);
|
|
1904
|
-
updateViewportOffset(board);
|
|
1905
|
-
return;
|
|
1906
|
-
}
|
|
1907
|
-
updateViewportOffset(board);
|
|
2350
|
+
|
|
2351
|
+
function setSelection(board, selection) {
|
|
2352
|
+
const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
|
|
2353
|
+
board.apply(operation);
|
|
1908
2354
|
}
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
const clearViewportOrigination = (board) => {
|
|
1913
|
-
BOARD_TO_VIEWPORT_ORIGINATION.delete(board);
|
|
2355
|
+
const SelectionTransforms = {
|
|
2356
|
+
setSelection,
|
|
2357
|
+
addSelectionWithTemporaryElements
|
|
1914
2358
|
};
|
|
1915
|
-
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
2359
|
+
function addSelectionWithTemporaryElements(board, elements) {
|
|
2360
|
+
const timeoutId = setTimeout(() => {
|
|
2361
|
+
setSelection(board, { ranges: [] });
|
|
2362
|
+
}, 0);
|
|
2363
|
+
let ref = getTemporaryRef(board);
|
|
2364
|
+
if (ref) {
|
|
2365
|
+
clearTimeout(ref.timeoutId);
|
|
2366
|
+
const currentElements = ref.elements;
|
|
2367
|
+
ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
|
|
2368
|
+
ref.timeoutId = timeoutId;
|
|
1919
2369
|
}
|
|
1920
2370
|
else {
|
|
1921
|
-
|
|
2371
|
+
BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
|
|
1922
2372
|
}
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
function setViewport(board, viewport) {
|
|
2376
|
+
const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
|
|
2377
|
+
board.apply(operation);
|
|
2378
|
+
}
|
|
2379
|
+
const ViewportTransforms$1 = {
|
|
2380
|
+
setViewport
|
|
1923
2381
|
};
|
|
1924
|
-
const isFromScrolling = (board) => {
|
|
1925
|
-
return !!IS_FROM_SCROLLING.get(board);
|
|
1926
|
-
};
|
|
1927
|
-
const setIsFromScrolling = (board, state) => {
|
|
1928
|
-
IS_FROM_SCROLLING.set(board, state);
|
|
1929
|
-
};
|
|
1930
|
-
const isFromViewportChange = (board) => {
|
|
1931
|
-
return !!IS_FROM_VIEWPORT_CHANGE.get(board);
|
|
1932
|
-
};
|
|
1933
|
-
const setIsFromViewportChange = (board, state) => {
|
|
1934
|
-
IS_FROM_VIEWPORT_CHANGE.set(board, state);
|
|
1935
|
-
};
|
|
1936
|
-
function scrollToRectangle(board, client) { }
|
|
1937
2382
|
|
|
1938
2383
|
function setTheme(board, themeColorMode) {
|
|
1939
2384
|
const operation = { type: 'set_theme', properties: board.theme, newProperties: themeColorMode };
|
|
@@ -2053,6 +2498,25 @@ const BoardTransforms = {
|
|
|
2053
2498
|
fitViewportWidth
|
|
2054
2499
|
};
|
|
2055
2500
|
|
|
2501
|
+
const removeElements = (board, elements) => {
|
|
2502
|
+
elements
|
|
2503
|
+
.map(element => {
|
|
2504
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2505
|
+
const ref = board.pathRef(path);
|
|
2506
|
+
return () => {
|
|
2507
|
+
removeNode(board, ref.current);
|
|
2508
|
+
ref.unref();
|
|
2509
|
+
removeSelectedElement(board, element);
|
|
2510
|
+
};
|
|
2511
|
+
})
|
|
2512
|
+
.forEach(action => {
|
|
2513
|
+
action();
|
|
2514
|
+
});
|
|
2515
|
+
};
|
|
2516
|
+
const CoreTransforms = {
|
|
2517
|
+
removeElements
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2056
2520
|
const Transforms = {
|
|
2057
2521
|
...GeneralTransforms,
|
|
2058
2522
|
...ViewportTransforms$1,
|
|
@@ -2148,7 +2612,11 @@ function createBoard(children, options) {
|
|
|
2148
2612
|
dblclick: (event) => { },
|
|
2149
2613
|
setFragment: (data) => { },
|
|
2150
2614
|
insertFragment: (data) => { },
|
|
2151
|
-
deleteFragment: (data) => {
|
|
2615
|
+
deleteFragment: (data) => {
|
|
2616
|
+
const elements = board.getDeletedFragment([]);
|
|
2617
|
+
CoreTransforms.removeElements(board, elements);
|
|
2618
|
+
},
|
|
2619
|
+
getDeletedFragment: (data) => data,
|
|
2152
2620
|
drawElement: (context) => [],
|
|
2153
2621
|
redrawElement: (context, previousContext) => { },
|
|
2154
2622
|
destroyElement: (context) => { },
|
|
@@ -2320,177 +2788,6 @@ function withHandPointer(board) {
|
|
|
2320
2788
|
return board;
|
|
2321
2789
|
}
|
|
2322
2790
|
|
|
2323
|
-
function withSelection(board) {
|
|
2324
|
-
const { pointerDown, globalPointerMove, globalPointerUp, onChange } = board;
|
|
2325
|
-
let start = null;
|
|
2326
|
-
let end = null;
|
|
2327
|
-
let selectionMovingG;
|
|
2328
|
-
let selectionOuterG;
|
|
2329
|
-
let previousSelectedElements;
|
|
2330
|
-
// prevent text from being selected when user pressed main pointer and is moving
|
|
2331
|
-
let needPreventNativeSelectionWhenMoving = false;
|
|
2332
|
-
board.pointerDown = (event) => {
|
|
2333
|
-
if (event.target instanceof Element && !event.target.closest('.plait-richtext-container')) {
|
|
2334
|
-
needPreventNativeSelectionWhenMoving = true;
|
|
2335
|
-
}
|
|
2336
|
-
if (!isMainPointer(event)) {
|
|
2337
|
-
pointerDown(event);
|
|
2338
|
-
return;
|
|
2339
|
-
}
|
|
2340
|
-
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2341
|
-
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2342
|
-
const range = { anchor: point, focus: point };
|
|
2343
|
-
const hitElements = getHitElements(board, { ranges: [range] });
|
|
2344
|
-
const selectedElements = getSelectedElements(board);
|
|
2345
|
-
if (hitElements.length === 1 && selectedElements.includes(hitElements[0]) && !options.isDisabledSelect) {
|
|
2346
|
-
pointerDown(event);
|
|
2347
|
-
return;
|
|
2348
|
-
}
|
|
2349
|
-
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) &&
|
|
2350
|
-
hitElements.length === 0 &&
|
|
2351
|
-
options.isMultiple &&
|
|
2352
|
-
!options.isDisabledSelect) {
|
|
2353
|
-
start = point;
|
|
2354
|
-
preventTouchMove(board, true);
|
|
2355
|
-
}
|
|
2356
|
-
Transforms.setSelection(board, { ranges: [range] });
|
|
2357
|
-
pointerDown(event);
|
|
2358
|
-
};
|
|
2359
|
-
board.globalPointerMove = (event) => {
|
|
2360
|
-
if (needPreventNativeSelectionWhenMoving) {
|
|
2361
|
-
// prevent text from being selected
|
|
2362
|
-
event.preventDefault();
|
|
2363
|
-
}
|
|
2364
|
-
if (start) {
|
|
2365
|
-
const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2366
|
-
const { x, y, width, height } = RectangleClient.toRectangleClient([start, movedTarget]);
|
|
2367
|
-
selectionMovingG?.remove();
|
|
2368
|
-
if (Math.hypot(width, height) > 5) {
|
|
2369
|
-
end = movedTarget;
|
|
2370
|
-
throttleRAF(() => {
|
|
2371
|
-
if (start && end) {
|
|
2372
|
-
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
2373
|
-
}
|
|
2374
|
-
});
|
|
2375
|
-
setSelectionMoving(board);
|
|
2376
|
-
const rough = PlaitBoard.getRoughSVG(board);
|
|
2377
|
-
selectionMovingG = rough.rectangle(x, y, width, height, {
|
|
2378
|
-
stroke: SELECTION_BORDER_COLOR,
|
|
2379
|
-
strokeWidth: 1,
|
|
2380
|
-
fill: SELECTION_FILL_COLOR,
|
|
2381
|
-
fillStyle: 'solid'
|
|
2382
|
-
});
|
|
2383
|
-
PlaitBoard.getHost(board).append(selectionMovingG);
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
globalPointerMove(event);
|
|
2387
|
-
};
|
|
2388
|
-
board.globalPointerUp = (event) => {
|
|
2389
|
-
if (start && end) {
|
|
2390
|
-
selectionMovingG?.remove();
|
|
2391
|
-
clearSelectionMoving(board);
|
|
2392
|
-
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
2393
|
-
}
|
|
2394
|
-
if (PlaitBoard.isFocus(board)) {
|
|
2395
|
-
const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
|
|
2396
|
-
const isInDocument = event.target instanceof Node && document.contains(event.target);
|
|
2397
|
-
const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
|
|
2398
|
-
// Clear selection when mouse board outside area
|
|
2399
|
-
// The framework needs to determine whether the board is focused through selection
|
|
2400
|
-
if (!isInBoard && !start && !isAttachedElement && isInDocument) {
|
|
2401
|
-
Transforms.setSelection(board, null);
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
start = null;
|
|
2405
|
-
end = null;
|
|
2406
|
-
needPreventNativeSelectionWhenMoving = false;
|
|
2407
|
-
preventTouchMove(board, false);
|
|
2408
|
-
globalPointerUp(event);
|
|
2409
|
-
};
|
|
2410
|
-
board.onChange = () => {
|
|
2411
|
-
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2412
|
-
if (options.isDisabledSelect) {
|
|
2413
|
-
selectionOuterG?.remove();
|
|
2414
|
-
clearSelectedElement(board);
|
|
2415
|
-
}
|
|
2416
|
-
// calc selected elements entry
|
|
2417
|
-
if (board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect) {
|
|
2418
|
-
try {
|
|
2419
|
-
if (board.operations.find(value => value.type === 'set_selection')) {
|
|
2420
|
-
selectionOuterG?.remove();
|
|
2421
|
-
const temporaryElements = getTemporaryElements(board);
|
|
2422
|
-
let elements = temporaryElements ? temporaryElements : getHitElements(board);
|
|
2423
|
-
if (!options.isMultiple && elements.length > 1) {
|
|
2424
|
-
elements = [elements[0]];
|
|
2425
|
-
}
|
|
2426
|
-
cacheSelectedElements(board, elements);
|
|
2427
|
-
previousSelectedElements = elements;
|
|
2428
|
-
const { width, height } = getRectangleByElements(board, elements, false);
|
|
2429
|
-
if (width > 0 && height > 0 && elements.length > 1) {
|
|
2430
|
-
selectionOuterG = createSelectionOuterG(board, elements);
|
|
2431
|
-
selectionOuterG.classList.add('selection-outer');
|
|
2432
|
-
PlaitBoard.getHost(board).append(selectionOuterG);
|
|
2433
|
-
}
|
|
2434
|
-
deleteTemporaryElements(board);
|
|
2435
|
-
}
|
|
2436
|
-
else {
|
|
2437
|
-
// wait node destroy and remove selected element state
|
|
2438
|
-
setTimeout(() => {
|
|
2439
|
-
const currentSelectedElements = getSelectedElements(board);
|
|
2440
|
-
if (currentSelectedElements.length && currentSelectedElements.length > 1) {
|
|
2441
|
-
const selectedElementChange = currentSelectedElements.some(item => !previousSelectedElements.includes(item));
|
|
2442
|
-
if (selectedElementChange) {
|
|
2443
|
-
selectionOuterG?.remove();
|
|
2444
|
-
selectionOuterG = createSelectionOuterG(board, currentSelectedElements);
|
|
2445
|
-
selectionOuterG.classList.add('selection-outer');
|
|
2446
|
-
PlaitBoard.getHost(board).append(selectionOuterG);
|
|
2447
|
-
}
|
|
2448
|
-
}
|
|
2449
|
-
else {
|
|
2450
|
-
selectionOuterG?.remove();
|
|
2451
|
-
}
|
|
2452
|
-
});
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
catch (error) {
|
|
2456
|
-
console.error(error);
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
onChange();
|
|
2460
|
-
};
|
|
2461
|
-
board.setPluginOptions(PlaitPluginKey.withSelection, {
|
|
2462
|
-
isMultiple: true,
|
|
2463
|
-
isDisabledSelect: false
|
|
2464
|
-
});
|
|
2465
|
-
return board;
|
|
2466
|
-
}
|
|
2467
|
-
function getTemporaryElements(board) {
|
|
2468
|
-
return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
2469
|
-
}
|
|
2470
|
-
function deleteTemporaryElements(board) {
|
|
2471
|
-
BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
|
|
2472
|
-
}
|
|
2473
|
-
function isSelectionMoving(board) {
|
|
2474
|
-
return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
|
|
2475
|
-
}
|
|
2476
|
-
function setSelectionMoving(board) {
|
|
2477
|
-
PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
|
|
2478
|
-
BOARD_TO_IS_SELECTION_MOVING.set(board, true);
|
|
2479
|
-
}
|
|
2480
|
-
function clearSelectionMoving(board) {
|
|
2481
|
-
PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
|
|
2482
|
-
BOARD_TO_IS_SELECTION_MOVING.delete(board);
|
|
2483
|
-
}
|
|
2484
|
-
function createSelectionOuterG(board, selectElements) {
|
|
2485
|
-
const { x, y, width, height } = getRectangleByElements(board, selectElements, false);
|
|
2486
|
-
const rough = PlaitBoard.getRoughSVG(board);
|
|
2487
|
-
return rough.rectangle(x - 2.5, y - 2.5, width + 5, height + 5, {
|
|
2488
|
-
stroke: SELECTION_BORDER_COLOR,
|
|
2489
|
-
strokeWidth: 1,
|
|
2490
|
-
fillStyle: 'solid'
|
|
2491
|
-
});
|
|
2492
|
-
}
|
|
2493
|
-
|
|
2494
2791
|
function withViewport(board) {
|
|
2495
2792
|
const { onChange } = board;
|
|
2496
2793
|
const throttleUpdate = debounce(() => {
|
|
@@ -2519,19 +2816,355 @@ function withViewport(board) {
|
|
|
2519
2816
|
return board;
|
|
2520
2817
|
}
|
|
2521
2818
|
|
|
2819
|
+
const ALIGN_TOLERANCE = 2;
|
|
2820
|
+
class ReactionManager {
|
|
2821
|
+
constructor(board, activeElements, activeRectangle) {
|
|
2822
|
+
this.board = board;
|
|
2823
|
+
this.activeElements = activeElements;
|
|
2824
|
+
this.activeRectangle = activeRectangle;
|
|
2825
|
+
this.alignRectangles = this.getAlignRectangle();
|
|
2826
|
+
}
|
|
2827
|
+
getAlignRectangle() {
|
|
2828
|
+
const result = [];
|
|
2829
|
+
depthFirstRecursion(this.board, node => {
|
|
2830
|
+
if (PlaitBoard.isBoard(node) || this.activeElements.some(element => node.id === element.id) || node.type !== 'geometry') {
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
const rectangle = this.board.getRectangle(node);
|
|
2834
|
+
rectangle && result.push(rectangle);
|
|
2835
|
+
}, node => {
|
|
2836
|
+
if (node && (PlaitBoard.isBoard(node) || this.board.isRecursion(node))) {
|
|
2837
|
+
return true;
|
|
2838
|
+
}
|
|
2839
|
+
else {
|
|
2840
|
+
return false;
|
|
2841
|
+
}
|
|
2842
|
+
}, true);
|
|
2843
|
+
return result;
|
|
2844
|
+
}
|
|
2845
|
+
handleAlign() {
|
|
2846
|
+
const alignRectangles = this.getAlignRectangle();
|
|
2847
|
+
const g = createG();
|
|
2848
|
+
let alignLines = [];
|
|
2849
|
+
const offset = 12;
|
|
2850
|
+
let deltaX = 0;
|
|
2851
|
+
let deltaY = 0;
|
|
2852
|
+
let isCorrectX = false;
|
|
2853
|
+
let isCorrectY = false;
|
|
2854
|
+
for (let alignRectangle of alignRectangles) {
|
|
2855
|
+
const closestDistances = this.calculateClosestDistances(this.activeRectangle, alignRectangle);
|
|
2856
|
+
let canDrawHorizontal = false;
|
|
2857
|
+
if (!isCorrectX && closestDistances.absXDistance < ALIGN_TOLERANCE) {
|
|
2858
|
+
deltaX = closestDistances.xDistance;
|
|
2859
|
+
this.activeRectangle.x -= deltaX;
|
|
2860
|
+
isCorrectX = true;
|
|
2861
|
+
canDrawHorizontal = true;
|
|
2862
|
+
}
|
|
2863
|
+
if (closestDistances.absXDistance === 0) {
|
|
2864
|
+
canDrawHorizontal = true;
|
|
2865
|
+
}
|
|
2866
|
+
if (canDrawHorizontal) {
|
|
2867
|
+
const verticalY = [
|
|
2868
|
+
alignRectangle.y,
|
|
2869
|
+
alignRectangle.y + alignRectangle.height,
|
|
2870
|
+
this.activeRectangle.y,
|
|
2871
|
+
this.activeRectangle.y + this.activeRectangle.height
|
|
2872
|
+
];
|
|
2873
|
+
const lineTopY = Math.min(...verticalY) - offset;
|
|
2874
|
+
const lineBottomY = Math.max(...verticalY) + offset;
|
|
2875
|
+
const leftLine = [this.activeRectangle.x, lineTopY, this.activeRectangle.x, lineBottomY];
|
|
2876
|
+
const middleLine = [
|
|
2877
|
+
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
2878
|
+
lineTopY,
|
|
2879
|
+
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
2880
|
+
lineBottomY
|
|
2881
|
+
];
|
|
2882
|
+
const rightLine = [
|
|
2883
|
+
this.activeRectangle.x + this.activeRectangle.width,
|
|
2884
|
+
lineTopY,
|
|
2885
|
+
this.activeRectangle.x + this.activeRectangle.width,
|
|
2886
|
+
lineBottomY
|
|
2887
|
+
];
|
|
2888
|
+
const shouldDrawLeftLine = closestDistances.indexX === 0 ||
|
|
2889
|
+
closestDistances.indexX === 1 ||
|
|
2890
|
+
(closestDistances.indexX === 2 && this.activeRectangle.width === alignRectangle.width);
|
|
2891
|
+
if (shouldDrawLeftLine && !alignLines[0]) {
|
|
2892
|
+
alignLines[0] = leftLine;
|
|
2893
|
+
}
|
|
2894
|
+
const shouldDrawRightLine = closestDistances.indexX === 2 ||
|
|
2895
|
+
closestDistances.indexX === 3 ||
|
|
2896
|
+
(closestDistances.indexX === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
2897
|
+
if (shouldDrawRightLine && !alignLines[2]) {
|
|
2898
|
+
alignLines[2] = rightLine;
|
|
2899
|
+
}
|
|
2900
|
+
const shouldDrawMiddleLine = closestDistances.indexX === 4 || (!shouldDrawLeftLine && !shouldDrawRightLine);
|
|
2901
|
+
if (shouldDrawMiddleLine && !alignLines[1]) {
|
|
2902
|
+
alignLines[1] = middleLine;
|
|
2903
|
+
}
|
|
2904
|
+
isCorrectX = true;
|
|
2905
|
+
}
|
|
2906
|
+
let canDrawVertical = false;
|
|
2907
|
+
if (!isCorrectY && closestDistances.absYDistance < ALIGN_TOLERANCE) {
|
|
2908
|
+
deltaY = closestDistances.yDistance;
|
|
2909
|
+
this.activeRectangle.y -= deltaY;
|
|
2910
|
+
isCorrectY = true;
|
|
2911
|
+
canDrawVertical = true;
|
|
2912
|
+
}
|
|
2913
|
+
if (closestDistances.absYDistance === 0) {
|
|
2914
|
+
canDrawVertical = true;
|
|
2915
|
+
}
|
|
2916
|
+
if (canDrawVertical) {
|
|
2917
|
+
const horizontalX = [
|
|
2918
|
+
alignRectangle.x,
|
|
2919
|
+
alignRectangle.x + alignRectangle.width,
|
|
2920
|
+
this.activeRectangle.x,
|
|
2921
|
+
this.activeRectangle.x + this.activeRectangle.width
|
|
2922
|
+
];
|
|
2923
|
+
const lineLeftX = Math.min(...horizontalX) - offset;
|
|
2924
|
+
const lineRightX = Math.max(...horizontalX) + offset;
|
|
2925
|
+
const topLine = [lineLeftX, this.activeRectangle.y, lineRightX, this.activeRectangle.y];
|
|
2926
|
+
const horizontalMiddleLine = [
|
|
2927
|
+
lineLeftX,
|
|
2928
|
+
this.activeRectangle.y + this.activeRectangle.height / 2,
|
|
2929
|
+
lineRightX,
|
|
2930
|
+
this.activeRectangle.y + this.activeRectangle.height / 2
|
|
2931
|
+
];
|
|
2932
|
+
const bottomLine = [
|
|
2933
|
+
lineLeftX,
|
|
2934
|
+
this.activeRectangle.y + this.activeRectangle.height,
|
|
2935
|
+
lineRightX,
|
|
2936
|
+
this.activeRectangle.y + this.activeRectangle.height
|
|
2937
|
+
];
|
|
2938
|
+
const shouldDrawTopLine = closestDistances.indexY === 0 ||
|
|
2939
|
+
closestDistances.indexY === 1 ||
|
|
2940
|
+
(closestDistances.indexY === 2 && this.activeRectangle.height === alignRectangle.height);
|
|
2941
|
+
if (shouldDrawTopLine && !alignLines[3]) {
|
|
2942
|
+
alignLines[3] = topLine;
|
|
2943
|
+
}
|
|
2944
|
+
const shouldDrawBottomLine = closestDistances.indexY === 2 ||
|
|
2945
|
+
closestDistances.indexY === 3 ||
|
|
2946
|
+
(closestDistances.indexY === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
2947
|
+
if (shouldDrawBottomLine && !alignLines[5]) {
|
|
2948
|
+
alignLines[5] = bottomLine;
|
|
2949
|
+
}
|
|
2950
|
+
const shouldDrawMiddleLine = closestDistances.indexY === 4 || (!shouldDrawTopLine && !shouldDrawBottomLine);
|
|
2951
|
+
if (shouldDrawMiddleLine && !alignLines[4]) {
|
|
2952
|
+
alignLines[4] = horizontalMiddleLine;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2956
|
+
const alignDeltaX = deltaX;
|
|
2957
|
+
this.activeRectangle.x += deltaX;
|
|
2958
|
+
const distributeHorizontalResult = this.alignDistributeHorizontal(alignRectangles);
|
|
2959
|
+
const distributeLines = distributeHorizontalResult.distributeLines;
|
|
2960
|
+
if (distributeHorizontalResult.deltaX) {
|
|
2961
|
+
deltaX = distributeHorizontalResult.deltaX;
|
|
2962
|
+
if (alignDeltaX !== deltaX) {
|
|
2963
|
+
alignLines[0] = [];
|
|
2964
|
+
alignLines[1] = [];
|
|
2965
|
+
alignLines[2] = [];
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
if (alignLines.length) {
|
|
2969
|
+
this.drawAlignLines(alignLines, g);
|
|
2970
|
+
}
|
|
2971
|
+
if (distributeLines.length) {
|
|
2972
|
+
this.drawDistributeLines(distributeLines, g);
|
|
2973
|
+
}
|
|
2974
|
+
return { deltaX, deltaY, g };
|
|
2975
|
+
}
|
|
2976
|
+
calculateClosestDistances(activeRectangle, alignRectangle) {
|
|
2977
|
+
const activeRectangleCenter = [activeRectangle.x + activeRectangle.width / 2, activeRectangle.y + activeRectangle.height / 2];
|
|
2978
|
+
const alignRectangleCenter = [alignRectangle.x + alignRectangle.width / 2, alignRectangle.y + alignRectangle.height / 2];
|
|
2979
|
+
const centerXDistance = activeRectangleCenter[0] - alignRectangleCenter[0];
|
|
2980
|
+
const centerYDistance = activeRectangleCenter[1] - alignRectangleCenter[1];
|
|
2981
|
+
const leftToLeft = activeRectangle.x - alignRectangle.x;
|
|
2982
|
+
const leftToRight = activeRectangle.x - (alignRectangle.x + alignRectangle.width);
|
|
2983
|
+
const rightToRight = activeRectangle.x + activeRectangle.width - (alignRectangle.x + alignRectangle.width);
|
|
2984
|
+
const rightToLeft = activeRectangle.x + activeRectangle.width - alignRectangle.x;
|
|
2985
|
+
const topToTop = activeRectangle.y - alignRectangle.y;
|
|
2986
|
+
const topToBottom = activeRectangle.y - (alignRectangle.y + alignRectangle.height);
|
|
2987
|
+
const bottomToTop = activeRectangle.y + activeRectangle.height - alignRectangle.y;
|
|
2988
|
+
const bottomToBottom = activeRectangle.y + activeRectangle.height - (alignRectangle.y + alignRectangle.height);
|
|
2989
|
+
const xDistances = [leftToLeft, leftToRight, rightToRight, rightToLeft, centerXDistance];
|
|
2990
|
+
const yDistances = [topToTop, topToBottom, bottomToBottom, bottomToTop, centerYDistance];
|
|
2991
|
+
const xDistancesAbs = xDistances.map(distance => Math.abs(distance));
|
|
2992
|
+
const yDistancesAbs = yDistances.map(distance => Math.abs(distance));
|
|
2993
|
+
const indexX = xDistancesAbs.indexOf(Math.min(...xDistancesAbs));
|
|
2994
|
+
const indexY = yDistancesAbs.indexOf(Math.min(...yDistancesAbs));
|
|
2995
|
+
return {
|
|
2996
|
+
absXDistance: xDistancesAbs[indexX],
|
|
2997
|
+
xDistance: xDistances[indexX],
|
|
2998
|
+
absYDistance: yDistancesAbs[indexY],
|
|
2999
|
+
yDistance: yDistances[indexY],
|
|
3000
|
+
indexX,
|
|
3001
|
+
indexY
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
alignDistributeHorizontal(alignRectangles) {
|
|
3005
|
+
let distributeLines = [];
|
|
3006
|
+
let deltaX = 0;
|
|
3007
|
+
const activeRectangleCenterX = this.activeRectangle.x + this.activeRectangle.width / 2;
|
|
3008
|
+
let rectangles = [];
|
|
3009
|
+
alignRectangles.forEach(rec => {
|
|
3010
|
+
if (isHorizontalCross(rec, this.activeRectangle) && !RectangleClient.isHit(rec, this.activeRectangle)) {
|
|
3011
|
+
rectangles.push(rec);
|
|
3012
|
+
}
|
|
3013
|
+
});
|
|
3014
|
+
rectangles = [...rectangles, this.activeRectangle].sort((a, b) => a.x - b.x);
|
|
3015
|
+
const refArray = [];
|
|
3016
|
+
let distributeDistance = 0;
|
|
3017
|
+
let leftIndex = undefined;
|
|
3018
|
+
let rightIndex = undefined;
|
|
3019
|
+
for (let i = 0; i < rectangles.length; i++) {
|
|
3020
|
+
for (let j = i + 1; j < rectangles.length; j++) {
|
|
3021
|
+
const left = rectangles[i];
|
|
3022
|
+
const right = rectangles[j];
|
|
3023
|
+
const distance = right.x - (left.x + left.width);
|
|
3024
|
+
let dif = Infinity;
|
|
3025
|
+
if (refArray[i]?.right) {
|
|
3026
|
+
refArray[i].right.push({ distance, index: j });
|
|
3027
|
+
}
|
|
3028
|
+
else {
|
|
3029
|
+
refArray[i] = { ...refArray[i], right: [{ distance, index: j }] };
|
|
3030
|
+
}
|
|
3031
|
+
if (refArray[j]?.left) {
|
|
3032
|
+
refArray[j].left.push({ distance, index: i });
|
|
3033
|
+
}
|
|
3034
|
+
else {
|
|
3035
|
+
refArray[j] = { ...refArray[j], left: [{ distance, index: i }] };
|
|
3036
|
+
}
|
|
3037
|
+
//middle
|
|
3038
|
+
let _centerX = (left.x + left.width + right.x) / 2;
|
|
3039
|
+
dif = Math.abs(activeRectangleCenterX - _centerX);
|
|
3040
|
+
if (dif < ALIGN_TOLERANCE) {
|
|
3041
|
+
distributeDistance = (right.x - (left.x + left.width) - this.activeRectangle.width) / 2;
|
|
3042
|
+
deltaX = activeRectangleCenterX - _centerX;
|
|
3043
|
+
leftIndex = i;
|
|
3044
|
+
rightIndex = j;
|
|
3045
|
+
}
|
|
3046
|
+
//right
|
|
3047
|
+
const distanceRight = right.x - (left.x + left.width);
|
|
3048
|
+
_centerX = right.x + right.width + distanceRight + this.activeRectangle.width / 2;
|
|
3049
|
+
dif = Math.abs(activeRectangleCenterX - _centerX);
|
|
3050
|
+
if (!distributeDistance && dif < ALIGN_TOLERANCE) {
|
|
3051
|
+
distributeDistance = distanceRight;
|
|
3052
|
+
leftIndex = j;
|
|
3053
|
+
deltaX = activeRectangleCenterX - _centerX;
|
|
3054
|
+
}
|
|
3055
|
+
//left
|
|
3056
|
+
const distanceLeft = right.x - (left.x + left.width);
|
|
3057
|
+
_centerX = left.x - distanceLeft - this.activeRectangle.width / 2;
|
|
3058
|
+
dif = Math.abs(activeRectangleCenterX - _centerX);
|
|
3059
|
+
if (!distributeDistance && dif < ALIGN_TOLERANCE) {
|
|
3060
|
+
distributeDistance = distanceLeft;
|
|
3061
|
+
rightIndex = i;
|
|
3062
|
+
deltaX = activeRectangleCenterX - _centerX;
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
const activeIndex = rectangles.indexOf(this.activeRectangle);
|
|
3067
|
+
let leftIndexes = [];
|
|
3068
|
+
let rightIndexes = [];
|
|
3069
|
+
if (leftIndex !== undefined) {
|
|
3070
|
+
leftIndexes.push(leftIndex);
|
|
3071
|
+
findRectangle(distributeDistance, refArray[leftIndex], Direction.left, leftIndexes);
|
|
3072
|
+
}
|
|
3073
|
+
if (rightIndex !== undefined) {
|
|
3074
|
+
rightIndexes.push(rightIndex);
|
|
3075
|
+
findRectangle(distributeDistance, refArray[rightIndex], Direction.right, rightIndexes);
|
|
3076
|
+
}
|
|
3077
|
+
if (leftIndexes.length || rightIndexes.length) {
|
|
3078
|
+
const indexArr = [...leftIndexes.reverse(), activeIndex, ...rightIndexes];
|
|
3079
|
+
this.activeRectangle.x -= deltaX;
|
|
3080
|
+
for (let i = 1; i < indexArr.length; i++) {
|
|
3081
|
+
distributeLines.push(getLinePoints(rectangles[indexArr[i - 1]], rectangles[indexArr[i]]));
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
function findRectangle(distance, ref, direction, rectangleIndexes) {
|
|
3085
|
+
const arr = ref[direction];
|
|
3086
|
+
const index = refArray.indexOf(ref);
|
|
3087
|
+
if ((index === 0 && direction === Direction.left) || (index === refArray.length - 1 && direction === Direction.right))
|
|
3088
|
+
return;
|
|
3089
|
+
for (let i = 0; i < arr.length; i++) {
|
|
3090
|
+
if (Math.abs(arr[i].distance - distance) < 0.1) {
|
|
3091
|
+
rectangleIndexes.push(arr[i].index);
|
|
3092
|
+
findRectangle(distance, refArray[arr[i].index], direction, rectangleIndexes);
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
function getLinePoints(leftRectangle, rightRectangle) {
|
|
3098
|
+
const verticalY = [
|
|
3099
|
+
leftRectangle.y,
|
|
3100
|
+
leftRectangle.y + leftRectangle.height,
|
|
3101
|
+
rightRectangle.y,
|
|
3102
|
+
rightRectangle.y + rightRectangle.height
|
|
3103
|
+
];
|
|
3104
|
+
const sortArr = verticalY.sort((a, b) => a - b);
|
|
3105
|
+
const y = (sortArr[1] + sortArr[2]) / 2;
|
|
3106
|
+
const line = [
|
|
3107
|
+
[leftRectangle.x + leftRectangle.width + 2, y],
|
|
3108
|
+
[rightRectangle.x - 2, y]
|
|
3109
|
+
];
|
|
3110
|
+
return line;
|
|
3111
|
+
}
|
|
3112
|
+
return { deltaX, distributeLines };
|
|
3113
|
+
}
|
|
3114
|
+
drawAlignLines(lines, g) {
|
|
3115
|
+
lines.forEach(points => {
|
|
3116
|
+
if (!points.length)
|
|
3117
|
+
return;
|
|
3118
|
+
const xAlign = PlaitBoard.getRoughSVG(this.board).line(points[0], points[1], points[2], points[3], {
|
|
3119
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3120
|
+
strokeWidth: 1,
|
|
3121
|
+
strokeLineDash: [4, 4]
|
|
3122
|
+
});
|
|
3123
|
+
g.appendChild(xAlign);
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
drawDistributeLines(lines, g) {
|
|
3127
|
+
lines.forEach(points => {
|
|
3128
|
+
if (!points.length)
|
|
3129
|
+
return;
|
|
3130
|
+
if (points[0][1] === points[1][1]) {
|
|
3131
|
+
const yAlign = PlaitBoard.getRoughSVG(this.board).line(points[0][0], points[0][1], points[1][0], points[1][1], {
|
|
3132
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3133
|
+
strokeWidth: 1
|
|
3134
|
+
});
|
|
3135
|
+
const bar1 = PlaitBoard.getRoughSVG(this.board).line(points[0][0], points[0][1] - 4, points[0][0], points[1][1] + 4, {
|
|
3136
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3137
|
+
strokeWidth: 1
|
|
3138
|
+
});
|
|
3139
|
+
const bar2 = PlaitBoard.getRoughSVG(this.board).line(points[1][0], points[0][1] - 4, points[1][0], points[1][1] + 4, {
|
|
3140
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3141
|
+
strokeWidth: 1
|
|
3142
|
+
});
|
|
3143
|
+
g.appendChild(yAlign);
|
|
3144
|
+
g.appendChild(bar1);
|
|
3145
|
+
g.appendChild(bar2);
|
|
3146
|
+
}
|
|
3147
|
+
});
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
function isHorizontalCross(rectangle, other) {
|
|
3151
|
+
return !(rectangle.y + rectangle.height < other.y || rectangle.y > other.y + other.height);
|
|
3152
|
+
}
|
|
3153
|
+
|
|
2522
3154
|
function withMoving(board) {
|
|
2523
|
-
const {
|
|
3155
|
+
const { pointerDown, pointerMove, globalPointerUp, globalPointerMove } = board;
|
|
2524
3156
|
let offsetX = 0;
|
|
2525
3157
|
let offsetY = 0;
|
|
2526
3158
|
let isPreventDefault = false;
|
|
2527
3159
|
let startPoint;
|
|
2528
3160
|
let activeElements = [];
|
|
2529
|
-
|
|
3161
|
+
let alignG = null;
|
|
3162
|
+
board.pointerDown = (event) => {
|
|
2530
3163
|
const host = BOARD_TO_HOST.get(board);
|
|
2531
3164
|
const point = transformPoint(board, toPoint(event.x, event.y, host));
|
|
2532
3165
|
const range = { anchor: point, focus: point };
|
|
2533
3166
|
let movableElements = board.children.filter(item => board.isMovable(item));
|
|
2534
|
-
if (movableElements.length) {
|
|
3167
|
+
if (movableElements.length && !isPreventTouchMove(board)) {
|
|
2535
3168
|
startPoint = point;
|
|
2536
3169
|
const selectedRootElements = getSelectedElements(board).filter(item => movableElements.includes(item));
|
|
2537
3170
|
const hitElement = getHitElementOfRoot(board, movableElements, range);
|
|
@@ -2541,21 +3174,35 @@ function withMoving(board) {
|
|
|
2541
3174
|
else if (hitElement) {
|
|
2542
3175
|
activeElements = [hitElement];
|
|
2543
3176
|
}
|
|
3177
|
+
if (activeElements.length > 0) {
|
|
3178
|
+
preventTouchMove(board, event, true);
|
|
3179
|
+
}
|
|
2544
3180
|
}
|
|
2545
|
-
|
|
3181
|
+
pointerDown(event);
|
|
2546
3182
|
};
|
|
2547
|
-
board.
|
|
3183
|
+
board.pointerMove = (event) => {
|
|
2548
3184
|
if (startPoint && activeElements.length && !PlaitBoard.hasBeenTextEditing(board)) {
|
|
2549
3185
|
if (!isPreventDefault) {
|
|
2550
3186
|
isPreventDefault = true;
|
|
2551
3187
|
}
|
|
3188
|
+
alignG?.remove();
|
|
2552
3189
|
const host = BOARD_TO_HOST.get(board);
|
|
2553
3190
|
const endPoint = transformPoint(board, toPoint(event.x, event.y, host));
|
|
2554
3191
|
offsetX = endPoint[0] - startPoint[0];
|
|
2555
3192
|
offsetY = endPoint[1] - startPoint[1];
|
|
2556
3193
|
const offsetBuffer = 5;
|
|
2557
|
-
if (Math.abs(offsetX) > offsetBuffer || Math.abs(offsetY) > offsetBuffer) {
|
|
3194
|
+
if (Math.abs(offsetX) > offsetBuffer || Math.abs(offsetY) > offsetBuffer || getMovingElements(board).length > 0) {
|
|
2558
3195
|
throttleRAF(() => {
|
|
3196
|
+
const activeElementsRectangle = getRectangleByElements(board, activeElements, true);
|
|
3197
|
+
activeElementsRectangle.x += offsetX;
|
|
3198
|
+
activeElementsRectangle.y += offsetY;
|
|
3199
|
+
const reactionManager = new ReactionManager(board, activeElements, activeElementsRectangle);
|
|
3200
|
+
const ref = reactionManager.handleAlign();
|
|
3201
|
+
offsetX -= ref.deltaX;
|
|
3202
|
+
offsetY -= ref.deltaY;
|
|
3203
|
+
alignG = ref.g;
|
|
3204
|
+
PlaitBoard.getElementActiveHost(board).append(alignG);
|
|
3205
|
+
handleTouchTarget(board);
|
|
2559
3206
|
const currentElements = activeElements.map(activeElement => {
|
|
2560
3207
|
const points = activeElement.points || [];
|
|
2561
3208
|
const [x, y] = activeElement.points[0];
|
|
@@ -2576,25 +3223,27 @@ function withMoving(board) {
|
|
|
2576
3223
|
// 阻止 move 过程中触发画布滚动行为
|
|
2577
3224
|
event.preventDefault();
|
|
2578
3225
|
}
|
|
2579
|
-
|
|
3226
|
+
pointerMove(event);
|
|
2580
3227
|
};
|
|
2581
|
-
board.
|
|
3228
|
+
board.globalPointerMove = (event) => {
|
|
2582
3229
|
if (startPoint) {
|
|
2583
3230
|
const inPlaitBoardElement = isInPlaitBoard(board, event.x, event.y);
|
|
2584
3231
|
if (!inPlaitBoardElement) {
|
|
2585
3232
|
cancelMove(board);
|
|
2586
3233
|
}
|
|
2587
3234
|
}
|
|
2588
|
-
|
|
3235
|
+
globalPointerMove(event);
|
|
2589
3236
|
};
|
|
2590
|
-
board.
|
|
3237
|
+
board.globalPointerUp = event => {
|
|
2591
3238
|
isPreventDefault = false;
|
|
2592
3239
|
if (startPoint) {
|
|
2593
3240
|
cancelMove(board);
|
|
2594
3241
|
}
|
|
2595
|
-
|
|
3242
|
+
preventTouchMove(board, event, false);
|
|
3243
|
+
globalPointerUp(event);
|
|
2596
3244
|
};
|
|
2597
3245
|
function cancelMove(board) {
|
|
3246
|
+
alignG?.remove();
|
|
2598
3247
|
startPoint = null;
|
|
2599
3248
|
offsetX = 0;
|
|
2600
3249
|
offsetY = 0;
|
|
@@ -2630,10 +3279,10 @@ class PlaitIslandBaseComponent {
|
|
|
2630
3279
|
markForCheck() {
|
|
2631
3280
|
this.cdr.markForCheck();
|
|
2632
3281
|
}
|
|
3282
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3283
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
|
|
2633
3284
|
}
|
|
2634
|
-
|
|
2635
|
-
PlaitIslandBaseComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 });
|
|
2636
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
|
|
3285
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
|
|
2637
3286
|
type: Directive,
|
|
2638
3287
|
args: [{
|
|
2639
3288
|
host: {
|
|
@@ -2642,16 +3291,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
|
|
|
2642
3291
|
}]
|
|
2643
3292
|
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; } });
|
|
2644
3293
|
class PlaitIslandPopoverBaseComponent {
|
|
3294
|
+
constructor(cdr) {
|
|
3295
|
+
this.cdr = cdr;
|
|
3296
|
+
}
|
|
2645
3297
|
initialize(board) {
|
|
2646
3298
|
this.board = board;
|
|
2647
|
-
const
|
|
2648
|
-
|
|
2649
|
-
onChange();
|
|
3299
|
+
const boardComponent = PlaitBoard.getComponent(board);
|
|
3300
|
+
this.subscription = boardComponent.plaitChange.subscribe(() => {
|
|
2650
3301
|
if (hasOnBoardChange(this)) {
|
|
2651
3302
|
this.onBoardChange();
|
|
2652
3303
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
3304
|
+
this.cdr.markForCheck();
|
|
3305
|
+
});
|
|
2655
3306
|
}
|
|
2656
3307
|
ngOnInit() {
|
|
2657
3308
|
if (!this.board) {
|
|
@@ -2661,20 +3312,20 @@ class PlaitIslandPopoverBaseComponent {
|
|
|
2661
3312
|
this.islandOnInit();
|
|
2662
3313
|
}
|
|
2663
3314
|
ngOnDestroy() {
|
|
2664
|
-
this.
|
|
3315
|
+
this.subscription?.unsubscribe();
|
|
2665
3316
|
this.islandOnDestroy();
|
|
2666
3317
|
}
|
|
3318
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandPopoverBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
3319
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 }); }
|
|
2667
3320
|
}
|
|
2668
|
-
|
|
2669
|
-
PlaitIslandPopoverBaseComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 });
|
|
2670
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
|
|
3321
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
|
|
2671
3322
|
type: Directive,
|
|
2672
3323
|
args: [{
|
|
2673
3324
|
host: {
|
|
2674
3325
|
class: 'plait-island-popover-container'
|
|
2675
3326
|
}
|
|
2676
3327
|
}]
|
|
2677
|
-
}], propDecorators: { board: [{
|
|
3328
|
+
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { board: [{
|
|
2678
3329
|
type: Input
|
|
2679
3330
|
}] } });
|
|
2680
3331
|
const hasOnBoardChange = (value) => {
|
|
@@ -2706,9 +3357,14 @@ const withHotkey = (board) => {
|
|
|
2706
3357
|
return false;
|
|
2707
3358
|
}
|
|
2708
3359
|
}, true);
|
|
2709
|
-
Transforms.
|
|
3360
|
+
Transforms.addSelectionWithTemporaryElements(board, elements);
|
|
2710
3361
|
return;
|
|
2711
3362
|
}
|
|
3363
|
+
const selectedElements = getSelectedElements(board);
|
|
3364
|
+
if (!PlaitBoard.isReadonly(board) && selectedElements.length > 0 && (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
3365
|
+
event.preventDefault();
|
|
3366
|
+
board.deleteFragment(null);
|
|
3367
|
+
}
|
|
2712
3368
|
keydown(event);
|
|
2713
3369
|
};
|
|
2714
3370
|
board.globalKeydown = (event) => {
|
|
@@ -2752,10 +3408,10 @@ class PlaitContextService {
|
|
|
2752
3408
|
removeUploadingFile(fileEntry) {
|
|
2753
3409
|
this.uploadingFiles = this.uploadingFiles.filter(file => file.url !== fileEntry.url);
|
|
2754
3410
|
}
|
|
3411
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3412
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService }); }
|
|
2755
3413
|
}
|
|
2756
|
-
|
|
2757
|
-
PlaitContextService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitContextService });
|
|
2758
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitContextService, decorators: [{
|
|
3414
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService, decorators: [{
|
|
2759
3415
|
type: Injectable
|
|
2760
3416
|
}] });
|
|
2761
3417
|
|
|
@@ -2832,10 +3488,10 @@ class PlaitElementComponent {
|
|
|
2832
3488
|
ngOnDestroy() {
|
|
2833
3489
|
this.board.destroyElement(this.getContext());
|
|
2834
3490
|
}
|
|
3491
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3492
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: PlaitElementComponent, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2835
3493
|
}
|
|
2836
|
-
|
|
2837
|
-
PlaitElementComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: PlaitElementComponent, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2838
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitElementComponent, decorators: [{
|
|
3494
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitElementComponent, decorators: [{
|
|
2839
3495
|
type: Component,
|
|
2840
3496
|
args: [{
|
|
2841
3497
|
selector: 'plait-element',
|
|
@@ -2870,9 +3526,8 @@ class PlaitChildrenElement {
|
|
|
2870
3526
|
this.parentG = PlaitBoard.getElementHost(this.board);
|
|
2871
3527
|
}
|
|
2872
3528
|
}
|
|
2873
|
-
}
|
|
2874
|
-
|
|
2875
|
-
PlaitChildrenElement.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: PlaitChildrenElement, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
|
|
3529
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitChildrenElement, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3530
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: PlaitChildrenElement, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
|
|
2876
3531
|
<plait-element
|
|
2877
3532
|
*ngFor="let item of parent.children; let index = index; trackBy: trackBy"
|
|
2878
3533
|
[index]="index"
|
|
@@ -2882,8 +3537,9 @@ PlaitChildrenElement.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
|
|
|
2882
3537
|
[effect]="effect"
|
|
2883
3538
|
[parentG]="parentG"
|
|
2884
3539
|
></plait-element>
|
|
2885
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: PlaitElementComponent, selector: "plait-element", inputs: ["index", "element", "parent", "board", "effect", "parentG"] }] });
|
|
2886
|
-
|
|
3540
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: PlaitElementComponent, selector: "plait-element", inputs: ["index", "element", "parent", "board", "effect", "parentG"] }] }); }
|
|
3541
|
+
}
|
|
3542
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitChildrenElement, decorators: [{
|
|
2887
3543
|
type: Component,
|
|
2888
3544
|
args: [{
|
|
2889
3545
|
selector: 'plait-children',
|
|
@@ -3116,10 +3772,6 @@ class PlaitBoardComponent {
|
|
|
3116
3772
|
this.board.globalKeydown(event);
|
|
3117
3773
|
}), filter(event => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board) && !hasInputOrTextareaTarget(event.target)))
|
|
3118
3774
|
.subscribe((event) => {
|
|
3119
|
-
const selectedElements = getSelectedElements(this.board);
|
|
3120
|
-
if (selectedElements.length > 0 && (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
3121
|
-
this.board.deleteFragment(null);
|
|
3122
|
-
}
|
|
3123
3775
|
this.board.keydown(event);
|
|
3124
3776
|
});
|
|
3125
3777
|
fromEvent(document, 'keyup')
|
|
@@ -3133,7 +3785,7 @@ class PlaitBoardComponent {
|
|
|
3133
3785
|
const selectedElements = getSelectedElements(this.board);
|
|
3134
3786
|
event.preventDefault();
|
|
3135
3787
|
const rectangle = getRectangleByElements(this.board, selectedElements, false);
|
|
3136
|
-
this.board.setFragment(event.clipboardData, rectangle);
|
|
3788
|
+
this.board.setFragment(event.clipboardData, rectangle, 'copy');
|
|
3137
3789
|
});
|
|
3138
3790
|
fromEvent(document, 'paste')
|
|
3139
3791
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
@@ -3150,7 +3802,7 @@ class PlaitBoardComponent {
|
|
|
3150
3802
|
const selectedElements = getSelectedElements(this.board);
|
|
3151
3803
|
event.preventDefault();
|
|
3152
3804
|
const rectangle = getRectangleByElements(this.board, selectedElements, false);
|
|
3153
|
-
this.board.setFragment(event.clipboardData, rectangle);
|
|
3805
|
+
this.board.setFragment(event.clipboardData, rectangle, 'cut');
|
|
3154
3806
|
this.board.deleteFragment(event.clipboardData);
|
|
3155
3807
|
});
|
|
3156
3808
|
}
|
|
@@ -3177,7 +3829,9 @@ class PlaitBoardComponent {
|
|
|
3177
3829
|
});
|
|
3178
3830
|
});
|
|
3179
3831
|
this.ngZone.runOutsideAngular(() => {
|
|
3180
|
-
fromEvent(this.viewportContainer.nativeElement, 'touchmove', { passive: false })
|
|
3832
|
+
fromEvent(this.viewportContainer.nativeElement, 'touchmove', { passive: false })
|
|
3833
|
+
.pipe(takeUntil(this.destroy$))
|
|
3834
|
+
.subscribe((event) => {
|
|
3181
3835
|
if (isPreventTouchMove(this.board)) {
|
|
3182
3836
|
event.preventDefault();
|
|
3183
3837
|
}
|
|
@@ -3222,9 +3876,8 @@ class PlaitBoardComponent {
|
|
|
3222
3876
|
this.updateIslands();
|
|
3223
3877
|
});
|
|
3224
3878
|
}
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, 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: `
|
|
3879
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3880
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: PlaitBoardComponent, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, 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: `
|
|
3228
3881
|
<div class="viewport-container" #viewportContainer>
|
|
3229
3882
|
<svg #svg width="100%" height="100%" style="position: relative;" class="board-host-svg">
|
|
3230
3883
|
<g class="element-host"></g>
|
|
@@ -3234,8 +3887,9 @@ PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ve
|
|
|
3234
3887
|
<plait-children [board]="board" [effect]="effect"></plait-children>
|
|
3235
3888
|
</div>
|
|
3236
3889
|
<ng-content></ng-content>
|
|
3237
|
-
`, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3238
|
-
|
|
3890
|
+
`, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3891
|
+
}
|
|
3892
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitBoardComponent, decorators: [{
|
|
3239
3893
|
type: Component,
|
|
3240
3894
|
args: [{
|
|
3241
3895
|
selector: 'plait-board',
|
|
@@ -3292,11 +3946,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
|
|
|
3292
3946
|
|
|
3293
3947
|
const COMPONENTS = [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent];
|
|
3294
3948
|
class PlaitModule {
|
|
3949
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3950
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, declarations: [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent], imports: [CommonModule], exports: [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent] }); }
|
|
3951
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, imports: [CommonModule] }); }
|
|
3295
3952
|
}
|
|
3296
|
-
|
|
3297
|
-
PlaitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.5", ngImport: i0, type: PlaitModule, declarations: [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent], imports: [CommonModule], exports: [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent] });
|
|
3298
|
-
PlaitModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitModule, imports: [CommonModule] });
|
|
3299
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitModule, decorators: [{
|
|
3953
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, decorators: [{
|
|
3300
3954
|
type: NgModule,
|
|
3301
3955
|
args: [{
|
|
3302
3956
|
declarations: [...COMPONENTS],
|
|
@@ -3473,5 +4127,5 @@ function createModModifierKeys() {
|
|
|
3473
4127
|
* Generated bundle index. Do not edit.
|
|
3474
4128
|
*/
|
|
3475
4129
|
|
|
3476
|
-
export { A, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, 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_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLIP_BOARD_FORMAT_KEY, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC,
|
|
4130
|
+
export { A, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, 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_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLIP_BOARD_FORMAT_KEY, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_INDEX, NODE_TO_PARENT, NS, NUMPAD_DIVIDE, NUMPAD_EIGHT, NUMPAD_FIVE, NUMPAD_FOUR, NUMPAD_MINUS, NUMPAD_MULTIPLY, NUMPAD_NINE, NUMPAD_ONE, NUMPAD_PERIOD, NUMPAD_PLUS, NUMPAD_SEVEN, NUMPAD_SIX, NUMPAD_THREE, NUMPAD_TWO, NUMPAD_ZERO, NUM_CENTER, NUM_LOCK, O, ONE, OPEN_SQUARE_BRACKET, P, PAGE_DOWN, PAGE_UP, PATH_REFS, PAUSE, PERIOD, PLUS_SIGN, POINTER_BUTTON, PRESS_AND_MOVE_BUFFER, PRINT_SCREEN, Path, PlaitBoard, PlaitBoardComponent, PlaitChildrenElement, PlaitContextService, PlaitElement, PlaitElementComponent, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitModule, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RIGHT_ARROW, RectangleClient, ResizeCursorClass, RetroThemeColor, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, X, Y, Z, ZERO, addMovingElements, addSelectedElement, arrowPoints, cacheMovingElements, cacheSelectedElements, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createFakeEvent, createForeignObject, createG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createSelectionRectangleG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, findElements, getBoardRectangle, getClipboardByKey, getClipboardDataByMedia, getDataFromClipboard, getElementById, getElementHostBBox, getHitElementOfRoot, getHitElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getRealScrollBarWidth, getRectangleByElements, getSelectedElements, getTemporaryElements, getTemporaryRef, getTextFromClipboard, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isDOMElement, isDOMNode, isFromScrolling, isFromViewportChange, isHitElements, isInPlaitBoard, isLineHitLine, isMainPointer, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedElement, isSelectionMoving, isSetViewportOperation, normalizePoint, preventTouchMove, removeMovingElements, removeSelectedElement, rotate, scrollToRectangle, setClipboardData, setClipboardDataByMedia, setClipboardDataByText, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, throttleRAF, toImage, toPoint, transformPoint, transformPoints, updateForeignObject, updateForeignObjectWidth, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withMoving, withOptions, withSelection };
|
|
3477
4131
|
//# sourceMappingURL=plait-core.mjs.map
|