@plait/core 0.24.0-next.0 → 0.24.0-next.10
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/rectangle-client.mjs +65 -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 +185 -0
- package/esm2022/utils/touch.mjs +35 -0
- package/esm2022/utils/weak-maps.mjs +21 -0
- package/{fesm2020 → fesm2022}/plait-core.mjs +2430 -1945
- package/fesm2022/plait-core.mjs.map +1 -0
- package/interfaces/board.d.ts +2 -1
- package/interfaces/rectangle-client.d.ts +8 -0
- package/package.json +14 -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 +25 -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 -411
- 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 -3458
- 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/index.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,2311 @@ 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
|
-
|
|
308
|
-
|
|
339
|
+
const isDOMElement = (value) => {
|
|
340
|
+
return isDOMNode(value) && value.nodeType === 1;
|
|
341
|
+
};
|
|
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;
|
|
309
352
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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;
|
|
317
361
|
};
|
|
318
362
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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);
|
|
331
380
|
}
|
|
332
|
-
|
|
333
|
-
|
|
381
|
+
const previousContext = this._context;
|
|
382
|
+
this._context = value;
|
|
383
|
+
if (this.element) {
|
|
384
|
+
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
334
385
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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));
|
|
386
|
+
if (this.initialized) {
|
|
387
|
+
this.cdr.markForCheck();
|
|
388
|
+
if (hasOnContextChanged(this)) {
|
|
389
|
+
this.onContextChanged(value, previousContext);
|
|
390
|
+
}
|
|
349
391
|
}
|
|
350
|
-
|
|
351
|
-
|
|
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
|
+
}
|
|
352
401
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
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');
|
|
358
425
|
}
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
426
|
+
this.initialized = true;
|
|
427
|
+
}
|
|
428
|
+
ngOnDestroy() {
|
|
429
|
+
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
430
|
+
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
364
431
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
432
|
+
removeSelectedElement(this.board, this.element);
|
|
433
|
+
(this.rootG || this.g).remove();
|
|
434
|
+
}
|
|
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();
|
|
444
|
+
|
|
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;
|
|
374
457
|
}
|
|
375
|
-
const last = path[path.length - 1];
|
|
376
|
-
return path.slice(0, -1).concat(last - 1);
|
|
377
458
|
},
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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;
|
|
383
468
|
},
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
*/
|
|
392
|
-
compare(path, another) {
|
|
393
|
-
const min = Math.min(path.length, another.length);
|
|
394
|
-
for (let i = 0; i < min; i++) {
|
|
395
|
-
if (path[i] < another[i])
|
|
396
|
-
return -1;
|
|
397
|
-
if (path[i] > another[i])
|
|
398
|
-
return 1;
|
|
399
|
-
}
|
|
400
|
-
return 0;
|
|
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
|
+
};
|
|
401
476
|
},
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
+
};
|
|
407
485
|
},
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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;
|
|
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);
|
|
418
491
|
},
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const as = path.slice(0, -1);
|
|
427
|
-
const bs = another.slice(0, -1);
|
|
428
|
-
const al = path[path.length - 1];
|
|
429
|
-
const bl = another[another.length - 1];
|
|
430
|
-
return al !== bl && Path.equals(as, bs);
|
|
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
|
+
];
|
|
431
499
|
},
|
|
432
|
-
|
|
433
|
-
return
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
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
|
-
});
|
|
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
|
+
];
|
|
499
507
|
}
|
|
500
508
|
};
|
|
501
509
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
510
|
+
// https://stackoverflow.com/a/6853926/232122
|
|
511
|
+
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
512
|
+
const A = x - x1;
|
|
513
|
+
const B = y - y1;
|
|
514
|
+
const C = x2 - x1;
|
|
515
|
+
const D = y2 - y1;
|
|
516
|
+
const dot = A * C + B * D;
|
|
517
|
+
const lenSquare = C * C + D * D;
|
|
518
|
+
let param = -1;
|
|
519
|
+
if (lenSquare !== 0) {
|
|
520
|
+
// in case of 0 length line
|
|
521
|
+
param = dot / lenSquare;
|
|
522
|
+
}
|
|
523
|
+
let xx, yy;
|
|
524
|
+
if (param < 0) {
|
|
525
|
+
xx = x1;
|
|
526
|
+
yy = y1;
|
|
527
|
+
}
|
|
528
|
+
else if (param > 1) {
|
|
529
|
+
xx = x2;
|
|
530
|
+
yy = y2;
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
xx = x1 + param * C;
|
|
534
|
+
yy = y1 + param * D;
|
|
535
|
+
}
|
|
536
|
+
const dx = x - xx;
|
|
537
|
+
const dy = y - yy;
|
|
538
|
+
return Math.hypot(dx, dy);
|
|
539
|
+
}
|
|
540
|
+
function getNearestPointBetweenPointAndSegment(point, linePoints) {
|
|
541
|
+
const x = point[0], y = point[1], x1 = linePoints[0][0], y1 = linePoints[0][1], x2 = linePoints[1][0], y2 = linePoints[1][1];
|
|
542
|
+
const A = x - x1;
|
|
543
|
+
const B = y - y1;
|
|
544
|
+
const C = x2 - x1;
|
|
545
|
+
const D = y2 - y1;
|
|
546
|
+
const dot = A * C + B * D;
|
|
547
|
+
const lenSquare = C * C + D * D;
|
|
548
|
+
let param = -1;
|
|
549
|
+
if (lenSquare !== 0) {
|
|
550
|
+
// in case of 0 length line
|
|
551
|
+
param = dot / lenSquare;
|
|
552
|
+
}
|
|
553
|
+
let xx, yy;
|
|
554
|
+
if (param < 0) {
|
|
555
|
+
xx = x1;
|
|
556
|
+
yy = y1;
|
|
557
|
+
}
|
|
558
|
+
else if (param > 1) {
|
|
559
|
+
xx = x2;
|
|
560
|
+
yy = y2;
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
xx = x1 + param * C;
|
|
564
|
+
yy = y1 + param * D;
|
|
565
|
+
}
|
|
566
|
+
return [xx, yy];
|
|
567
|
+
}
|
|
568
|
+
function distanceBetweenPointAndSegments(points, point) {
|
|
569
|
+
const len = points.length;
|
|
570
|
+
let distance = Infinity;
|
|
571
|
+
for (let i = 0; i < len - 1; i++) {
|
|
572
|
+
const p = points[i];
|
|
573
|
+
const p2 = points[i + 1];
|
|
574
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
575
|
+
if (currentDistance < distance) {
|
|
576
|
+
distance = currentDistance;
|
|
518
577
|
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
578
|
+
}
|
|
579
|
+
return distance;
|
|
580
|
+
}
|
|
581
|
+
function getNearestPointBetweenPointAndSegments(point, points) {
|
|
582
|
+
const len = points.length;
|
|
583
|
+
let distance = Infinity;
|
|
584
|
+
let result = point;
|
|
585
|
+
for (let i = 0; i < len; i++) {
|
|
586
|
+
const p = points[i];
|
|
587
|
+
const p2 = i === len - 1 ? points[0] : points[i + 1];
|
|
588
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
589
|
+
if (currentDistance < distance) {
|
|
590
|
+
distance = currentDistance;
|
|
591
|
+
result = getNearestPointBetweenPointAndSegment(point, [p, p2]);
|
|
528
592
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
593
|
+
}
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
function rotate(x1, y1, x2, y2, angle) {
|
|
597
|
+
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
598
|
+
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
599
|
+
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
600
|
+
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
601
|
+
}
|
|
602
|
+
function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
|
|
603
|
+
const dx = x1 - x2;
|
|
604
|
+
const dy = y1 - y2;
|
|
605
|
+
return Math.hypot(dx, dy);
|
|
606
|
+
}
|
|
607
|
+
// https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
|
|
608
|
+
function distanceBetweenPointAndRectangle(x, y, rect) {
|
|
609
|
+
var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
|
|
610
|
+
var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
|
|
611
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
612
|
+
}
|
|
613
|
+
const isLineHitLine = (a, b, c, d) => {
|
|
614
|
+
const crossProduct = (v1, v2) => v1[0] * v2[1] - v1[1] * v2[0];
|
|
615
|
+
const ab = [b[0] - a[0], b[1] - a[1]];
|
|
616
|
+
const ac = [c[0] - a[0], c[1] - a[1]];
|
|
617
|
+
const ad = [d[0] - a[0], d[1] - a[1]];
|
|
618
|
+
const ca = [a[0] - c[0], a[1] - c[1]];
|
|
619
|
+
const cb = [b[0] - c[0], b[1] - c[1]];
|
|
620
|
+
const cd = [d[0] - c[0], d[1] - c[1]];
|
|
621
|
+
return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
|
|
622
|
+
};
|
|
623
|
+
const isPolylineHitRectangle = (points, rectangle) => {
|
|
624
|
+
const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
|
|
625
|
+
for (let i = 1; i < points.length; i++) {
|
|
626
|
+
const isIntersect = isLineHitLine(points[i], points[i - 1], rectanglePoints[0], rectanglePoints[1]) ||
|
|
627
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[1], rectanglePoints[2]) ||
|
|
628
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[2], rectanglePoints[3]) ||
|
|
629
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[3], rectanglePoints[0]);
|
|
630
|
+
if (isIntersect) {
|
|
631
|
+
return true;
|
|
536
632
|
}
|
|
537
|
-
return n;
|
|
538
633
|
}
|
|
634
|
+
return false;
|
|
635
|
+
};
|
|
636
|
+
//https://stackoverflow.com/questions/22521982/check-if-point-is-inside-a-polygon
|
|
637
|
+
const isPointInPolygon = (point, points) => {
|
|
638
|
+
// ray-casting algorithm based on
|
|
639
|
+
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
|
|
640
|
+
const x = point[0], y = point[1];
|
|
641
|
+
let inside = false;
|
|
642
|
+
for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
643
|
+
let xi = points[i][0], yi = points[i][1];
|
|
644
|
+
let xj = points[j][0], yj = points[j][1];
|
|
645
|
+
let intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
|
|
646
|
+
if (intersect)
|
|
647
|
+
inside = !inside;
|
|
648
|
+
}
|
|
649
|
+
return inside;
|
|
650
|
+
};
|
|
651
|
+
const isPointInEllipse = (point, center, rx, ry, rotation = 0) => {
|
|
652
|
+
const cosAngle = Math.cos(rotation);
|
|
653
|
+
const sinAngle = Math.sin(rotation);
|
|
654
|
+
const x1 = (point[0] - center[0]) * cosAngle + (point[1] - center[1]) * sinAngle;
|
|
655
|
+
const y1 = (point[1] - center[1]) * cosAngle - (point[0] - center[0]) * sinAngle;
|
|
656
|
+
return (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry) <= 1;
|
|
657
|
+
};
|
|
658
|
+
const isPointInRoundRectangle = (point, rectangle, radius) => {
|
|
659
|
+
const { x: rectX, y: rectY, width, height } = rectangle;
|
|
660
|
+
const isInRectangle = point[0] >= rectX && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + height;
|
|
661
|
+
const handleLeftTop = point[0] >= rectX &&
|
|
662
|
+
point[0] <= rectX + radius &&
|
|
663
|
+
point[1] >= rectY &&
|
|
664
|
+
point[1] <= rectY + radius &&
|
|
665
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + radius)) > radius;
|
|
666
|
+
const handleLeftBottom = point[0] >= rectX &&
|
|
667
|
+
point[0] <= rectX + radius &&
|
|
668
|
+
point[1] >= rectY + height &&
|
|
669
|
+
point[1] <= rectY + height - radius &&
|
|
670
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + height - radius)) > radius;
|
|
671
|
+
const handleRightTop = point[0] >= rectX + width - radius &&
|
|
672
|
+
point[0] <= rectX + width &&
|
|
673
|
+
point[1] >= rectY &&
|
|
674
|
+
point[1] <= rectY + radius &&
|
|
675
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + radius)) > radius;
|
|
676
|
+
const handleRightBottom = point[0] >= rectX + width - radius &&
|
|
677
|
+
point[0] <= rectX + width &&
|
|
678
|
+
point[1] >= rectY + height - radius &&
|
|
679
|
+
point[1] <= rectY + height &&
|
|
680
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + height - radius)) > radius;
|
|
681
|
+
const isInCorner = handleLeftTop || handleLeftBottom || handleRightTop || handleRightBottom;
|
|
682
|
+
return isInRectangle && !isInCorner;
|
|
539
683
|
};
|
|
540
684
|
|
|
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
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
viewport[key] = value;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
case 'set_selection': {
|
|
632
|
-
const { newProperties } = op;
|
|
633
|
-
if (newProperties == null) {
|
|
634
|
-
selection = newProperties;
|
|
635
|
-
}
|
|
636
|
-
else {
|
|
637
|
-
if (selection === null) {
|
|
638
|
-
selection = op.newProperties;
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
selection.ranges = newProperties.ranges;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
case 'set_theme': {
|
|
647
|
-
const { newProperties } = op;
|
|
648
|
-
theme = newProperties;
|
|
649
|
-
break;
|
|
685
|
+
function transformPoints(board, points) {
|
|
686
|
+
const newPoints = points.map(point => {
|
|
687
|
+
return transformPoint(board, point);
|
|
688
|
+
});
|
|
689
|
+
return newPoints;
|
|
690
|
+
}
|
|
691
|
+
function transformPoint(board, point) {
|
|
692
|
+
const { width, height } = PlaitBoard.getHost(board).getBoundingClientRect();
|
|
693
|
+
const viewBox = PlaitBoard.getHost(board).viewBox.baseVal;
|
|
694
|
+
const x = (point[0] / width) * viewBox.width + viewBox.x;
|
|
695
|
+
const y = (point[1] / height) * viewBox.height + viewBox.y;
|
|
696
|
+
const newPoint = [x, y];
|
|
697
|
+
return newPoint;
|
|
698
|
+
}
|
|
699
|
+
function isInPlaitBoard(board, x, y) {
|
|
700
|
+
const plaitBoardElement = PlaitBoard.getBoardContainer(board);
|
|
701
|
+
const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
|
|
702
|
+
const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
|
|
703
|
+
return distances === 0;
|
|
704
|
+
}
|
|
705
|
+
function getRealScrollBarWidth(board) {
|
|
706
|
+
const { hideScrollbar } = board.options;
|
|
707
|
+
let scrollBarWidth = 0;
|
|
708
|
+
if (!hideScrollbar) {
|
|
709
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
710
|
+
scrollBarWidth = viewportContainer.offsetWidth - viewportContainer.clientWidth;
|
|
711
|
+
}
|
|
712
|
+
return scrollBarWidth;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function createForeignObject(x, y, width, height) {
|
|
716
|
+
var newForeignObject = document.createElementNS(NS, 'foreignObject');
|
|
717
|
+
newForeignObject.setAttribute('x', `${x}`);
|
|
718
|
+
newForeignObject.setAttribute('y', `${y}`);
|
|
719
|
+
newForeignObject.setAttribute('width', `${width}`);
|
|
720
|
+
newForeignObject.setAttribute('height', `${height}`);
|
|
721
|
+
return newForeignObject;
|
|
722
|
+
}
|
|
723
|
+
function updateForeignObject(target, width, height, x, y) {
|
|
724
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
725
|
+
if (foreignObject) {
|
|
726
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
727
|
+
foreignObject.setAttribute('height', `${height}`);
|
|
728
|
+
foreignObject.setAttribute('x', `${x}`);
|
|
729
|
+
foreignObject.setAttribute('y', `${y}`);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
function updateForeignObjectWidth(target, width) {
|
|
733
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
734
|
+
if (foreignObject) {
|
|
735
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const IS_MAC = typeof window != 'undefined' && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
|
740
|
+
|
|
741
|
+
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
742
|
+
typeof window !== 'undefined' &&
|
|
743
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
744
|
+
!window.MSStream;
|
|
745
|
+
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
746
|
+
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
747
|
+
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
748
|
+
// "modern" Edge was released at 79.x
|
|
749
|
+
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
750
|
+
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
751
|
+
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
752
|
+
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
753
|
+
|
|
754
|
+
function isNullOrUndefined(value) {
|
|
755
|
+
return value === null || value === undefined;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* 规范 point
|
|
759
|
+
* @param point
|
|
760
|
+
* @returns point
|
|
761
|
+
*/
|
|
762
|
+
function normalizePoint(point) {
|
|
763
|
+
return Array.isArray(point)
|
|
764
|
+
? {
|
|
765
|
+
x: point[0],
|
|
766
|
+
y: point[1]
|
|
650
767
|
}
|
|
768
|
+
: point;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Check whether to merge an operation into the previous operation.
|
|
773
|
+
*/
|
|
774
|
+
const shouldMerge = (op, prev) => {
|
|
775
|
+
if (op.type === 'set_viewport' && op.type === prev?.type) {
|
|
776
|
+
return true;
|
|
651
777
|
}
|
|
652
|
-
return
|
|
778
|
+
return false;
|
|
653
779
|
};
|
|
654
|
-
|
|
780
|
+
/**
|
|
781
|
+
* Check whether an operation needs to be saved to the history.
|
|
782
|
+
*/
|
|
783
|
+
const shouldSave = (op, prev) => {
|
|
784
|
+
if (op.type === 'set_selection' || op.type === 'set_viewport') {
|
|
785
|
+
return false;
|
|
786
|
+
}
|
|
787
|
+
return true;
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Check whether an operation should clear the redos stack.
|
|
791
|
+
*/
|
|
792
|
+
const shouldClear = (op) => {
|
|
793
|
+
if (op.type === 'set_selection') {
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
return true;
|
|
797
|
+
};
|
|
798
|
+
const PlaitHistoryBoard = {
|
|
655
799
|
/**
|
|
656
|
-
*
|
|
800
|
+
* Get the saving flag's current value.
|
|
657
801
|
*/
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
802
|
+
isSaving(board) {
|
|
803
|
+
return SAVING.get(board);
|
|
804
|
+
},
|
|
805
|
+
/**
|
|
806
|
+
* Get the merge flag's current value.
|
|
807
|
+
*/
|
|
808
|
+
isMerging(board) {
|
|
809
|
+
return MERGING.get(board);
|
|
810
|
+
},
|
|
811
|
+
/**
|
|
812
|
+
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
813
|
+
* the new operations into previous save point in the history.
|
|
814
|
+
*/
|
|
815
|
+
withoutMerging(board, fn) {
|
|
816
|
+
const prev = PlaitHistoryBoard.isMerging(board);
|
|
817
|
+
MERGING.set(board, false);
|
|
818
|
+
fn();
|
|
819
|
+
MERGING.set(board, prev);
|
|
820
|
+
},
|
|
821
|
+
/**
|
|
822
|
+
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
823
|
+
* their operations into the history.
|
|
824
|
+
*/
|
|
825
|
+
withoutSaving(board, fn) {
|
|
826
|
+
const prev = PlaitHistoryBoard.isSaving(board);
|
|
827
|
+
SAVING.set(board, false);
|
|
828
|
+
fn();
|
|
829
|
+
SAVING.set(board, prev);
|
|
680
830
|
}
|
|
681
831
|
};
|
|
682
832
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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);
|
|
707
|
-
}
|
|
708
|
-
function moveNode(board, path, newPath) {
|
|
709
|
-
const operation = { type: 'move_node', path, newPath };
|
|
710
|
-
board.apply(operation);
|
|
711
|
-
}
|
|
712
|
-
const NodeTransforms = {
|
|
713
|
-
insertNode,
|
|
714
|
-
setNode,
|
|
715
|
-
removeNode,
|
|
716
|
-
moveNode
|
|
833
|
+
/**
|
|
834
|
+
* Hotkey mappings for each platform.
|
|
835
|
+
*/
|
|
836
|
+
const HOTKEYS = {
|
|
837
|
+
bold: 'mod+b',
|
|
838
|
+
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
839
|
+
moveBackward: 'left',
|
|
840
|
+
moveForward: 'right',
|
|
841
|
+
moveUp: 'up',
|
|
842
|
+
moveDown: 'down',
|
|
843
|
+
moveWordBackward: 'ctrl+left',
|
|
844
|
+
moveWordForward: 'ctrl+right',
|
|
845
|
+
deleteBackward: 'shift?+backspace',
|
|
846
|
+
deleteForward: 'shift?+delete',
|
|
847
|
+
extendBackward: 'shift+left',
|
|
848
|
+
extendForward: 'shift+right',
|
|
849
|
+
italic: 'mod+i',
|
|
850
|
+
splitBlock: 'shift?+enter',
|
|
851
|
+
undo: 'mod+z'
|
|
717
852
|
};
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
853
|
+
const APPLE_HOTKEYS = {
|
|
854
|
+
moveLineBackward: 'opt+up',
|
|
855
|
+
moveLineForward: 'opt+down',
|
|
856
|
+
moveWordBackward: 'opt+left',
|
|
857
|
+
moveWordForward: 'opt+right',
|
|
858
|
+
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
859
|
+
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
860
|
+
deleteLineBackward: 'cmd+shift?+backspace',
|
|
861
|
+
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
862
|
+
deleteWordBackward: 'opt+shift?+backspace',
|
|
863
|
+
deleteWordForward: 'opt+shift?+delete',
|
|
864
|
+
extendLineBackward: 'opt+shift+up',
|
|
865
|
+
extendLineForward: 'opt+shift+down',
|
|
866
|
+
redo: 'cmd+shift+z',
|
|
867
|
+
transposeCharacter: 'ctrl+t'
|
|
726
868
|
};
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function setViewport(board, viewport) {
|
|
735
|
-
const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
|
|
736
|
-
board.apply(operation);
|
|
737
|
-
}
|
|
738
|
-
const ViewportTransforms$1 = {
|
|
739
|
-
setViewport
|
|
869
|
+
const WINDOWS_HOTKEYS = {
|
|
870
|
+
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
871
|
+
deleteWordForward: 'ctrl+shift?+delete',
|
|
872
|
+
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
740
873
|
};
|
|
741
|
-
|
|
742
874
|
/**
|
|
743
|
-
*
|
|
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
|
|
875
|
+
* Create a platform-aware hotkey checker.
|
|
748
876
|
*/
|
|
749
|
-
const
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
const
|
|
753
|
-
const
|
|
754
|
-
const
|
|
755
|
-
const
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
-
|
|
876
|
-
const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
|
|
877
|
-
const HOST_CLASS_NAME = 'plait-board-container';
|
|
878
|
-
const SCROLL_BAR_WIDTH = 20;
|
|
879
|
-
const MAX_RADIUS = 16;
|
|
880
|
-
const POINTER_BUTTON = {
|
|
881
|
-
MAIN: 0,
|
|
882
|
-
WHEEL: 1,
|
|
883
|
-
SECONDARY: 2,
|
|
884
|
-
TOUCH: -1
|
|
885
|
-
};
|
|
886
|
-
const PRESS_AND_MOVE_BUFFER = 5;
|
|
887
|
-
|
|
888
|
-
const NS = 'http://www.w3.org/2000/svg';
|
|
889
|
-
function toPoint(x, y, container) {
|
|
890
|
-
const rect = container.getBoundingClientRect();
|
|
891
|
-
return [x - rect.x, y - rect.y];
|
|
892
|
-
}
|
|
893
|
-
function createG() {
|
|
894
|
-
const newG = document.createElementNS(NS, 'g');
|
|
895
|
-
return newG;
|
|
896
|
-
}
|
|
897
|
-
function createPath() {
|
|
898
|
-
const newG = document.createElementNS(NS, 'path');
|
|
899
|
-
return newG;
|
|
900
|
-
}
|
|
901
|
-
function createSVG() {
|
|
902
|
-
const svg = document.createElementNS(NS, 'svg');
|
|
903
|
-
return svg;
|
|
904
|
-
}
|
|
905
|
-
function createText(x, y, fill, textContent) {
|
|
906
|
-
var text = document.createElementNS(NS, 'text');
|
|
907
|
-
text.setAttribute('x', `${x}`);
|
|
908
|
-
text.setAttribute('y', `${y}`);
|
|
909
|
-
text.setAttribute('fill', fill);
|
|
910
|
-
text.textContent = textContent;
|
|
911
|
-
return text;
|
|
912
|
-
}
|
|
913
|
-
/**
|
|
914
|
-
* Check if a DOM node is an element node.
|
|
915
|
-
*/
|
|
916
|
-
const isDOMElement = (value) => {
|
|
917
|
-
return isDOMNode(value) && value.nodeType === 1;
|
|
877
|
+
const create = (key) => {
|
|
878
|
+
const generic = HOTKEYS[key];
|
|
879
|
+
const apple = APPLE_HOTKEYS[key];
|
|
880
|
+
const windows = WINDOWS_HOTKEYS[key];
|
|
881
|
+
const isGeneric = generic && isKeyHotkey(generic);
|
|
882
|
+
const isApple = apple && isKeyHotkey(apple);
|
|
883
|
+
const isWindows = windows && isKeyHotkey(windows);
|
|
884
|
+
return (event) => {
|
|
885
|
+
if (isGeneric && isGeneric(event)) {
|
|
886
|
+
return true;
|
|
887
|
+
}
|
|
888
|
+
if (IS_APPLE && isApple && isApple(event)) {
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
return false;
|
|
895
|
+
};
|
|
918
896
|
};
|
|
919
897
|
/**
|
|
920
|
-
*
|
|
898
|
+
* Hotkeys.
|
|
921
899
|
*/
|
|
922
|
-
const
|
|
923
|
-
|
|
900
|
+
const hotkeys = {
|
|
901
|
+
isBold: create('bold'),
|
|
902
|
+
isCompose: create('compose'),
|
|
903
|
+
isMoveBackward: create('moveBackward'),
|
|
904
|
+
isMoveForward: create('moveForward'),
|
|
905
|
+
isMoveUp: create('moveUp'),
|
|
906
|
+
isMoveDown: create('moveDown'),
|
|
907
|
+
isDeleteBackward: create('deleteBackward'),
|
|
908
|
+
isDeleteForward: create('deleteForward'),
|
|
909
|
+
isDeleteLineBackward: create('deleteLineBackward'),
|
|
910
|
+
isDeleteLineForward: create('deleteLineForward'),
|
|
911
|
+
isDeleteWordBackward: create('deleteWordBackward'),
|
|
912
|
+
isDeleteWordForward: create('deleteWordForward'),
|
|
913
|
+
isExtendBackward: create('extendBackward'),
|
|
914
|
+
isExtendForward: create('extendForward'),
|
|
915
|
+
isExtendLineBackward: create('extendLineBackward'),
|
|
916
|
+
isExtendLineForward: create('extendLineForward'),
|
|
917
|
+
isItalic: create('italic'),
|
|
918
|
+
isMoveLineBackward: create('moveLineBackward'),
|
|
919
|
+
isMoveLineForward: create('moveLineForward'),
|
|
920
|
+
isMoveWordBackward: create('moveWordBackward'),
|
|
921
|
+
isMoveWordForward: create('moveWordForward'),
|
|
922
|
+
isRedo: create('redo'),
|
|
923
|
+
isSplitBlock: create('splitBlock'),
|
|
924
|
+
isTransposeCharacter: create('transposeCharacter'),
|
|
925
|
+
isUndo: create('undo')
|
|
924
926
|
};
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
927
|
+
|
|
928
|
+
function idCreator(length = 5) {
|
|
929
|
+
// remove numeral
|
|
930
|
+
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
931
|
+
const maxPosition = $chars.length;
|
|
932
|
+
let key = '';
|
|
933
|
+
for (let i = 0; i < length; i++) {
|
|
934
|
+
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
930
935
|
}
|
|
931
|
-
return
|
|
932
|
-
}
|
|
933
|
-
const isSecondaryPointer = (event) => {
|
|
934
|
-
return event.button === POINTER_BUTTON.SECONDARY;
|
|
935
|
-
};
|
|
936
|
-
const isMainPointer = (event) => {
|
|
937
|
-
return event.button === POINTER_BUTTON.MAIN;
|
|
938
|
-
};
|
|
936
|
+
return key;
|
|
937
|
+
}
|
|
939
938
|
|
|
940
939
|
/**
|
|
941
|
-
*
|
|
940
|
+
* drawRoundRectangle
|
|
942
941
|
*/
|
|
943
|
-
|
|
944
|
-
const
|
|
945
|
-
const
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
return false;
|
|
942
|
+
function drawRoundRectangle(rs, x1, y1, x2, y2, options, outline = false, borderRadius) {
|
|
943
|
+
const width = Math.abs(x1 - x2);
|
|
944
|
+
const height = Math.abs(y1 - y2);
|
|
945
|
+
let radius = borderRadius || 0;
|
|
946
|
+
if (radius === 0) {
|
|
947
|
+
const defaultRadius = Math.min(width, height) / 8;
|
|
948
|
+
let radius = defaultRadius;
|
|
949
|
+
if (defaultRadius > MAX_RADIUS) {
|
|
950
|
+
radius = outline ? MAX_RADIUS + 2 : MAX_RADIUS;
|
|
953
951
|
}
|
|
954
952
|
}
|
|
953
|
+
const point1 = [x1 + radius, y1];
|
|
954
|
+
const point2 = [x2 - radius, y1];
|
|
955
|
+
const point3 = [x2, y1 + radius];
|
|
956
|
+
const point4 = [x2, y2 - radius];
|
|
957
|
+
const point5 = [x2 - radius, y2];
|
|
958
|
+
const point6 = [x1 + radius, y2];
|
|
959
|
+
const point7 = [x1, y2 - radius];
|
|
960
|
+
const point8 = [x1, y1 + radius];
|
|
961
|
+
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);
|
|
962
|
+
}
|
|
963
|
+
const drawRectangle = (board, rectangle, options) => {
|
|
964
|
+
const roughSVG = PlaitBoard.getRoughSVG(board);
|
|
965
|
+
const rectangleG = roughSVG.rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height, options);
|
|
966
|
+
setStrokeLinecap(rectangleG, 'round');
|
|
967
|
+
return rectangleG;
|
|
955
968
|
};
|
|
956
969
|
|
|
957
|
-
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
selectedElements.push(node);
|
|
972
|
-
}
|
|
973
|
-
}, getIsRecursionFunc(board), true);
|
|
974
|
-
return selectedElements;
|
|
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
|
-
});
|
|
990
|
-
}
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
return isIntersectionElements;
|
|
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) || [];
|
|
1000
|
-
};
|
|
1001
|
-
const addSelectedElement = (board, element) => {
|
|
1002
|
-
const selectedElements = getSelectedElements(board);
|
|
1003
|
-
cacheSelectedElements(board, [...selectedElements, element]);
|
|
1004
|
-
};
|
|
1005
|
-
const removeSelectedElement = (board, element) => {
|
|
1006
|
-
const selectedElements = getSelectedElements(board);
|
|
1007
|
-
const newSelectedElements = selectedElements.filter(value => value !== element);
|
|
1008
|
-
cacheSelectedElements(board, newSelectedElements);
|
|
1009
|
-
};
|
|
1010
|
-
const clearSelectedElement = (board) => {
|
|
1011
|
-
cacheSelectedElements(board, []);
|
|
1012
|
-
};
|
|
1013
|
-
const isSelectedElement = (board, element) => {
|
|
1014
|
-
const selectedElements = getSelectedElements(board);
|
|
1015
|
-
return !!selectedElements.find(value => value === element);
|
|
1016
|
-
};
|
|
1017
|
-
|
|
1018
|
-
function hasBeforeContextChange(value) {
|
|
1019
|
-
if (value.beforeContextChange) {
|
|
1020
|
-
return true;
|
|
1021
|
-
}
|
|
1022
|
-
return false;
|
|
970
|
+
function arrowPoints(start, end, maxHypotenuseLength = 10, degree = 40) {
|
|
971
|
+
const width = Math.abs(start[0] - end[0]);
|
|
972
|
+
const height = Math.abs(start[1] - end[1]);
|
|
973
|
+
let hypotenuse = Math.hypot(width, height); // 斜边
|
|
974
|
+
const realRotateLine = hypotenuse > maxHypotenuseLength * 2 ? maxHypotenuseLength : hypotenuse / 2;
|
|
975
|
+
const rotateWidth = (realRotateLine / hypotenuse) * width;
|
|
976
|
+
const rotateHeight = (realRotateLine / hypotenuse) * height;
|
|
977
|
+
const rotatePoint = [
|
|
978
|
+
end[0] > start[0] ? end[0] - rotateWidth : end[0] + rotateWidth,
|
|
979
|
+
end[1] > start[1] ? end[1] - rotateHeight : end[1] + rotateHeight
|
|
980
|
+
];
|
|
981
|
+
const pointRight = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (degree * Math.PI) / 180);
|
|
982
|
+
const pointLeft = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (-degree * Math.PI) / 180);
|
|
983
|
+
return { pointLeft, pointRight };
|
|
1023
984
|
}
|
|
1024
|
-
function
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
return
|
|
985
|
+
function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 40) {
|
|
986
|
+
const { pointLeft, pointRight } = arrowPoints(start, end, maxHypotenuseLength, degree);
|
|
987
|
+
const arrowLineLeft = rs.linearPath([pointLeft, end], options);
|
|
988
|
+
const arrowLineRight = rs.linearPath([pointRight, end], options);
|
|
989
|
+
return [arrowLineLeft, arrowLineRight];
|
|
1029
990
|
}
|
|
1030
991
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
992
|
+
function drawCircle(roughSVG, point, diameter, options) {
|
|
993
|
+
return roughSVG.circle(point[0], point[1], diameter, options);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function drawLine(rs, start, end, options) {
|
|
997
|
+
return rs.linearPath([start, end], options);
|
|
998
|
+
}
|
|
999
|
+
function drawLinearPath(points, options, closePath) {
|
|
1000
|
+
const g = createG();
|
|
1001
|
+
const path = createPath();
|
|
1002
|
+
let polylinePath = '';
|
|
1003
|
+
points.forEach((point, index) => {
|
|
1004
|
+
if (index === 0) {
|
|
1005
|
+
polylinePath += `M ${point[0]} ${point[1]} `;
|
|
1046
1006
|
}
|
|
1047
1007
|
else {
|
|
1048
|
-
|
|
1049
|
-
this.g = createG();
|
|
1050
|
-
this.rootG = createG();
|
|
1051
|
-
this.rootG.append(this.g);
|
|
1052
|
-
}
|
|
1053
|
-
else {
|
|
1054
|
-
this.g = createG();
|
|
1055
|
-
}
|
|
1008
|
+
polylinePath += `L ${point[0]} ${point[1]} `;
|
|
1056
1009
|
}
|
|
1010
|
+
});
|
|
1011
|
+
if (closePath) {
|
|
1012
|
+
polylinePath += 'Z';
|
|
1057
1013
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
constructor(cdr) {
|
|
1074
|
-
this.cdr = cdr;
|
|
1075
|
-
this.initialized = false;
|
|
1076
|
-
}
|
|
1077
|
-
ngOnInit() {
|
|
1078
|
-
if (this.element.type) {
|
|
1079
|
-
(this.rootG || this.g).setAttribute(`plait-${this.element.type}`, 'true');
|
|
1014
|
+
path.setAttribute('d', polylinePath);
|
|
1015
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1016
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1017
|
+
path.setAttribute('fill', `${options?.fill || 'none'}`);
|
|
1018
|
+
options?.strokeLineDash && path.setAttribute('stroke-dasharray', `${options.strokeLineDash}`);
|
|
1019
|
+
g.appendChild(path);
|
|
1020
|
+
return g;
|
|
1021
|
+
}
|
|
1022
|
+
function drawBezierPath(points, options) {
|
|
1023
|
+
const g = createG();
|
|
1024
|
+
const path = createPath();
|
|
1025
|
+
let polylinePath = '';
|
|
1026
|
+
for (let i = 0; i < points.length - 3; i += 3) {
|
|
1027
|
+
if (i === 0) {
|
|
1028
|
+
polylinePath += `M ${points[0][0]} ${points[0][1]} `;
|
|
1080
1029
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
ngOnDestroy() {
|
|
1084
|
-
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
1085
|
-
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
1030
|
+
else {
|
|
1031
|
+
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]}`;
|
|
1086
1032
|
}
|
|
1087
|
-
removeSelectedElement(this.board, this.element);
|
|
1088
|
-
(this.rootG || this.g).remove();
|
|
1089
1033
|
}
|
|
1034
|
+
path.setAttribute('d', polylinePath);
|
|
1035
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1036
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1037
|
+
path.setAttribute('fill', `none`);
|
|
1038
|
+
g.appendChild(path);
|
|
1039
|
+
return g;
|
|
1090
1040
|
}
|
|
1091
|
-
PlaitPluginElementComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
1092
|
-
PlaitPluginElementComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 });
|
|
1093
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
|
|
1094
|
-
type: Directive
|
|
1095
|
-
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
|
|
1096
|
-
type: Input
|
|
1097
|
-
}] } });
|
|
1098
|
-
const ELEMENT_TO_COMPONENT = new WeakMap();
|
|
1099
1041
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
});
|
|
1104
|
-
return newPoints;
|
|
1105
|
-
}
|
|
1106
|
-
function transformPoint(board, point) {
|
|
1107
|
-
const { width, height } = PlaitBoard.getHost(board).getBoundingClientRect();
|
|
1108
|
-
const viewBox = PlaitBoard.getHost(board).viewBox.baseVal;
|
|
1109
|
-
const x = (point[0] / width) * viewBox.width + viewBox.x;
|
|
1110
|
-
const y = (point[1] / height) * viewBox.height + viewBox.y;
|
|
1111
|
-
const newPoint = [x, y];
|
|
1112
|
-
return newPoint;
|
|
1113
|
-
}
|
|
1114
|
-
function isInPlaitBoard(board, x, y) {
|
|
1115
|
-
const plaitBoardElement = PlaitBoard.getBoardContainer(board);
|
|
1116
|
-
const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
|
|
1117
|
-
const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
|
|
1118
|
-
return distances === 0;
|
|
1119
|
-
}
|
|
1120
|
-
function getRealScrollBarWidth(board) {
|
|
1042
|
+
const IS_FROM_SCROLLING = new WeakMap();
|
|
1043
|
+
const IS_FROM_VIEWPORT_CHANGE = new WeakMap();
|
|
1044
|
+
function getViewportContainerRect(board) {
|
|
1121
1045
|
const { hideScrollbar } = board.options;
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
function createForeignObject(x, y, width, height) {
|
|
1131
|
-
var newForeignObject = document.createElementNS(NS, 'foreignObject');
|
|
1132
|
-
newForeignObject.setAttribute('x', `${x}`);
|
|
1133
|
-
newForeignObject.setAttribute('y', `${y}`);
|
|
1134
|
-
newForeignObject.setAttribute('width', `${width}`);
|
|
1135
|
-
newForeignObject.setAttribute('height', `${height}`);
|
|
1136
|
-
return newForeignObject;
|
|
1046
|
+
const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
|
|
1047
|
+
const viewportRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1048
|
+
return {
|
|
1049
|
+
width: viewportRect.width + scrollBarWidth,
|
|
1050
|
+
height: viewportRect.height + scrollBarWidth
|
|
1051
|
+
};
|
|
1137
1052
|
}
|
|
1138
|
-
function
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1053
|
+
function getElementHostBBox(board, zoom) {
|
|
1054
|
+
const childrenRect = getRectangleByElements(board, board.children, true);
|
|
1055
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1056
|
+
const containerWidth = viewportContainerRect.width / zoom;
|
|
1057
|
+
const containerHeight = viewportContainerRect.height / zoom;
|
|
1058
|
+
let left;
|
|
1059
|
+
let right;
|
|
1060
|
+
let top;
|
|
1061
|
+
let bottom;
|
|
1062
|
+
if (childrenRect.width < containerWidth) {
|
|
1063
|
+
const centerX = childrenRect.x + childrenRect.width / 2;
|
|
1064
|
+
const halfContainerWidth = containerWidth / 2;
|
|
1065
|
+
left = centerX - halfContainerWidth;
|
|
1066
|
+
right = centerX + halfContainerWidth;
|
|
1145
1067
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
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);
|
|
1162
|
-
|
|
1163
|
-
/**
|
|
1164
|
-
* Check whether to merge an operation into the previous operation.
|
|
1165
|
-
*/
|
|
1166
|
-
const shouldMerge = (op, prev) => {
|
|
1167
|
-
if (op.type === 'set_viewport' && op.type === prev?.type) {
|
|
1168
|
-
return true;
|
|
1068
|
+
else {
|
|
1069
|
+
left = childrenRect.x;
|
|
1070
|
+
right = childrenRect.x + childrenRect.width;
|
|
1169
1071
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
const shouldSave = (op, prev) => {
|
|
1176
|
-
if (op.type === 'set_selection' || op.type === 'set_viewport') {
|
|
1177
|
-
return false;
|
|
1072
|
+
if (childrenRect.height < containerHeight) {
|
|
1073
|
+
const centerY = childrenRect.y + childrenRect.height / 2;
|
|
1074
|
+
const halfContainerHeight = containerHeight / 2;
|
|
1075
|
+
top = centerY - halfContainerHeight;
|
|
1076
|
+
bottom = centerY + halfContainerHeight;
|
|
1178
1077
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1078
|
+
else {
|
|
1079
|
+
top = childrenRect.y;
|
|
1080
|
+
bottom = childrenRect.y + childrenRect.height;
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
left,
|
|
1084
|
+
right,
|
|
1085
|
+
top,
|
|
1086
|
+
bottom
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1181
1089
|
/**
|
|
1182
|
-
*
|
|
1090
|
+
* 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
|
|
1091
|
+
* @param zoom 缩放比
|
|
1092
|
+
* @param minZoom 最小缩放比
|
|
1093
|
+
* @param maxZoom 最大缩放比
|
|
1094
|
+
* @returns 正确的缩放比
|
|
1183
1095
|
*/
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1096
|
+
function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
|
|
1097
|
+
return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
|
|
1098
|
+
}
|
|
1099
|
+
function getViewBox(board, zoom) {
|
|
1100
|
+
const boardContainerRectangle = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1101
|
+
const elementHostBBox = getElementHostBBox(board, zoom);
|
|
1102
|
+
const horizontalPadding = boardContainerRectangle.width / 2;
|
|
1103
|
+
const verticalPadding = boardContainerRectangle.height / 2;
|
|
1104
|
+
const viewBox = [
|
|
1105
|
+
elementHostBBox.left - horizontalPadding / zoom,
|
|
1106
|
+
elementHostBBox.top - verticalPadding / zoom,
|
|
1107
|
+
elementHostBBox.right - elementHostBBox.left + (horizontalPadding * 2) / zoom,
|
|
1108
|
+
elementHostBBox.bottom - elementHostBBox.top + (verticalPadding * 2) / zoom
|
|
1109
|
+
];
|
|
1110
|
+
return viewBox;
|
|
1111
|
+
}
|
|
1112
|
+
function getViewBoxCenterPoint(board) {
|
|
1113
|
+
const childrenRectangle = getRectangleByElements(board, board.children, true);
|
|
1114
|
+
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
1115
|
+
}
|
|
1116
|
+
function setSVGViewBox(board, viewBox) {
|
|
1117
|
+
const zoom = board.viewport.zoom;
|
|
1118
|
+
const hostElement = PlaitBoard.getHost(board);
|
|
1119
|
+
hostElement.style.display = 'block';
|
|
1120
|
+
hostElement.style.width = `${viewBox[2] * zoom}px`;
|
|
1121
|
+
hostElement.style.height = `${viewBox[3] * zoom}px`;
|
|
1122
|
+
if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
|
|
1123
|
+
hostElement.setAttribute('viewBox', viewBox.join(' '));
|
|
1187
1124
|
}
|
|
1188
|
-
|
|
1125
|
+
}
|
|
1126
|
+
function updateViewportOffset(board) {
|
|
1127
|
+
const origination = getViewportOrigination(board);
|
|
1128
|
+
if (!origination)
|
|
1129
|
+
return;
|
|
1130
|
+
const { zoom } = board.viewport;
|
|
1131
|
+
const viewBox = getViewBox(board, zoom);
|
|
1132
|
+
const scrollLeft = (origination[0] - viewBox[0]) * zoom;
|
|
1133
|
+
const scrollTop = (origination[1] - viewBox[1]) * zoom;
|
|
1134
|
+
updateViewportContainerScroll(board, scrollLeft, scrollTop);
|
|
1135
|
+
}
|
|
1136
|
+
function updateViewportContainerScroll(board, left, top, isFromViewportChange = true) {
|
|
1137
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1138
|
+
if (viewportContainer.scrollLeft !== left || viewportContainer.scrollTop !== top) {
|
|
1139
|
+
viewportContainer.scrollLeft = left;
|
|
1140
|
+
viewportContainer.scrollTop = top;
|
|
1141
|
+
isFromViewportChange && setIsFromViewportChange(board, true);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
function initializeViewportContainer(board) {
|
|
1145
|
+
const { width, height } = getViewportContainerRect(board);
|
|
1146
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1147
|
+
viewportContainer.style.width = `${width}px`;
|
|
1148
|
+
viewportContainer.style.height = `${height}px`;
|
|
1149
|
+
}
|
|
1150
|
+
function initializeViewBox(board) {
|
|
1151
|
+
const zoom = board.viewport.zoom;
|
|
1152
|
+
const viewBox = getViewBox(board, zoom);
|
|
1153
|
+
setSVGViewBox(board, viewBox);
|
|
1154
|
+
}
|
|
1155
|
+
function initializeViewportOffset(board) {
|
|
1156
|
+
if (!board.viewport?.origination) {
|
|
1157
|
+
const zoom = board.viewport.zoom;
|
|
1158
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1159
|
+
const viewBox = getViewBox(board, zoom);
|
|
1160
|
+
const centerX = viewBox[0] + viewBox[2] / 2;
|
|
1161
|
+
const centerY = viewBox[1] + viewBox[3] / 2;
|
|
1162
|
+
const origination = [centerX - viewportContainerRect.width / 2 / zoom, centerY - viewportContainerRect.height / 2 / zoom];
|
|
1163
|
+
updateViewportOrigination(board, origination);
|
|
1164
|
+
updateViewportOffset(board);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
updateViewportOffset(board);
|
|
1168
|
+
}
|
|
1169
|
+
const updateViewportOrigination = (board, origination) => {
|
|
1170
|
+
BOARD_TO_VIEWPORT_ORIGINATION.set(board, origination);
|
|
1189
1171
|
};
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
isMerging(board) {
|
|
1201
|
-
return MERGING.get(board);
|
|
1202
|
-
},
|
|
1203
|
-
/**
|
|
1204
|
-
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
1205
|
-
* the new operations into previous save point in the history.
|
|
1206
|
-
*/
|
|
1207
|
-
withoutMerging(board, fn) {
|
|
1208
|
-
const prev = PlaitHistoryBoard.isMerging(board);
|
|
1209
|
-
MERGING.set(board, false);
|
|
1210
|
-
fn();
|
|
1211
|
-
MERGING.set(board, prev);
|
|
1212
|
-
},
|
|
1213
|
-
/**
|
|
1214
|
-
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
1215
|
-
* their operations into the history.
|
|
1216
|
-
*/
|
|
1217
|
-
withoutSaving(board, fn) {
|
|
1218
|
-
const prev = PlaitHistoryBoard.isSaving(board);
|
|
1219
|
-
SAVING.set(board, false);
|
|
1220
|
-
fn();
|
|
1221
|
-
SAVING.set(board, prev);
|
|
1172
|
+
const clearViewportOrigination = (board) => {
|
|
1173
|
+
BOARD_TO_VIEWPORT_ORIGINATION.delete(board);
|
|
1174
|
+
};
|
|
1175
|
+
const getViewportOrigination = (board) => {
|
|
1176
|
+
const origination = BOARD_TO_VIEWPORT_ORIGINATION.get(board);
|
|
1177
|
+
if (origination) {
|
|
1178
|
+
return origination;
|
|
1179
|
+
}
|
|
1180
|
+
else {
|
|
1181
|
+
return board.viewport.origination;
|
|
1222
1182
|
}
|
|
1223
1183
|
};
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
* Hotkey mappings for each platform.
|
|
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'
|
|
1184
|
+
const isFromScrolling = (board) => {
|
|
1185
|
+
return !!IS_FROM_SCROLLING.get(board);
|
|
1244
1186
|
};
|
|
1245
|
-
const
|
|
1246
|
-
|
|
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'
|
|
1187
|
+
const setIsFromScrolling = (board, state) => {
|
|
1188
|
+
IS_FROM_SCROLLING.set(board, state);
|
|
1260
1189
|
};
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
deleteWordForward: 'ctrl+shift?+delete',
|
|
1264
|
-
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
1190
|
+
const isFromViewportChange = (board) => {
|
|
1191
|
+
return !!IS_FROM_VIEWPORT_CHANGE.get(board);
|
|
1265
1192
|
};
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
const
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
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;
|
|
1285
|
-
}
|
|
1286
|
-
return false;
|
|
1193
|
+
const setIsFromViewportChange = (board, state) => {
|
|
1194
|
+
IS_FROM_VIEWPORT_CHANGE.set(board, state);
|
|
1195
|
+
};
|
|
1196
|
+
function scrollToRectangle(board, client) { }
|
|
1197
|
+
|
|
1198
|
+
let timerId = null;
|
|
1199
|
+
const throttleRAF = (fn) => {
|
|
1200
|
+
const scheduleFunc = () => {
|
|
1201
|
+
timerId = requestAnimationFrame(() => {
|
|
1202
|
+
timerId = null;
|
|
1203
|
+
fn();
|
|
1204
|
+
});
|
|
1287
1205
|
};
|
|
1206
|
+
if (timerId !== null) {
|
|
1207
|
+
cancelAnimationFrame(timerId);
|
|
1208
|
+
timerId = null;
|
|
1209
|
+
}
|
|
1210
|
+
scheduleFunc();
|
|
1288
1211
|
};
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
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')
|
|
1212
|
+
const debounce = (func, wait, options) => {
|
|
1213
|
+
let timerSubscription = null;
|
|
1214
|
+
return () => {
|
|
1215
|
+
if (timerSubscription && !timerSubscription.closed) {
|
|
1216
|
+
timerSubscription.unsubscribe();
|
|
1217
|
+
timerSubscription = timer(wait).subscribe(() => {
|
|
1218
|
+
func();
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
else {
|
|
1222
|
+
if (options?.leading) {
|
|
1223
|
+
func();
|
|
1224
|
+
}
|
|
1225
|
+
timerSubscription = timer(wait).subscribe();
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1318
1228
|
};
|
|
1319
1229
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1230
|
+
const getMovingElements = (board) => {
|
|
1231
|
+
return BOARD_TO_MOVING_ELEMENT.get(board) || [];
|
|
1232
|
+
};
|
|
1233
|
+
const addMovingElements = (board, elements) => {
|
|
1234
|
+
const movingElements = getMovingElements(board);
|
|
1235
|
+
const newElements = elements.filter(item => !movingElements.find(movingElement => movingElement.key === item.key));
|
|
1236
|
+
cacheMovingElements(board, [...movingElements, ...newElements]);
|
|
1237
|
+
};
|
|
1238
|
+
const removeMovingElements = (board) => {
|
|
1239
|
+
BOARD_TO_MOVING_ELEMENT.delete(board);
|
|
1240
|
+
};
|
|
1241
|
+
const cacheMovingElements = (board, elements) => {
|
|
1242
|
+
BOARD_TO_MOVING_ELEMENT.set(board, elements);
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
function cloneCSSStyle(nativeNode, clonedNode) {
|
|
1246
|
+
const targetStyle = clonedNode.style;
|
|
1247
|
+
if (!targetStyle) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
1251
|
+
if (sourceStyle.cssText) {
|
|
1252
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
1253
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
1254
|
+
}
|
|
1255
|
+
else {
|
|
1256
|
+
Array.from(sourceStyle).forEach(name => {
|
|
1257
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
1258
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
1259
|
+
});
|
|
1327
1260
|
}
|
|
1328
|
-
return key;
|
|
1329
1261
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1262
|
+
function createCanvas(width, height, fillStyle) {
|
|
1263
|
+
const canvas = document.createElement('canvas');
|
|
1264
|
+
const ctx = canvas.getContext('2d');
|
|
1265
|
+
canvas.width = width;
|
|
1266
|
+
canvas.height = height;
|
|
1267
|
+
canvas.style.width = `${width}px`;
|
|
1268
|
+
canvas.style.height = `${height}px`;
|
|
1269
|
+
ctx.strokeStyle = '#ffffff';
|
|
1270
|
+
ctx.fillStyle = fillStyle;
|
|
1271
|
+
ctx.fillRect(0, 0, width, height);
|
|
1272
|
+
return {
|
|
1273
|
+
canvas,
|
|
1274
|
+
ctx
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
function isElementNode(node) {
|
|
1278
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
1279
|
+
}
|
|
1280
|
+
function cloneSvg(board, options) {
|
|
1281
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1282
|
+
const { width, height, x, y } = elementHostBox;
|
|
1283
|
+
const { padding = 4, inlineStyleClassNames } = options;
|
|
1284
|
+
const sourceSvg = PlaitBoard.getHost(board);
|
|
1285
|
+
const cloneSvgElement = sourceSvg.cloneNode(true);
|
|
1286
|
+
cloneSvgElement.style.width = `${width}px`;
|
|
1287
|
+
cloneSvgElement.style.height = `${height}px`;
|
|
1288
|
+
cloneSvgElement.style.backgroundColor = '';
|
|
1289
|
+
cloneSvgElement.setAttribute('width', `${width}`);
|
|
1290
|
+
cloneSvgElement.setAttribute('height', `${height}`);
|
|
1291
|
+
cloneSvgElement.setAttribute('viewBox', [x - padding, y - padding, width + 2 * padding, height + 2 * padding].join(','));
|
|
1292
|
+
if (inlineStyleClassNames) {
|
|
1293
|
+
const sourceNodes = Array.from(sourceSvg.querySelectorAll(inlineStyleClassNames));
|
|
1294
|
+
const cloneNodes = Array.from(cloneSvgElement.querySelectorAll(inlineStyleClassNames));
|
|
1295
|
+
sourceNodes.forEach((node, index) => {
|
|
1296
|
+
const cloneNode = cloneNodes[index];
|
|
1297
|
+
const childElements = Array.from(node.querySelectorAll('*')).filter(isElementNode);
|
|
1298
|
+
const cloneChildElements = Array.from(cloneNode.querySelectorAll('*')).filter(isElementNode);
|
|
1299
|
+
sourceNodes.push(...childElements);
|
|
1300
|
+
cloneNodes.push(...cloneChildElements);
|
|
1301
|
+
});
|
|
1302
|
+
sourceNodes.forEach((node, index) => {
|
|
1303
|
+
const cloneNode = cloneNodes[index];
|
|
1304
|
+
cloneCSSStyle(node, cloneNode);
|
|
1305
|
+
});
|
|
1344
1306
|
}
|
|
1345
|
-
|
|
1346
|
-
const point2 = [x2 - radius, y1];
|
|
1347
|
-
const point3 = [x2, y1 + radius];
|
|
1348
|
-
const point4 = [x2, y2 - radius];
|
|
1349
|
-
const point5 = [x2 - radius, y2];
|
|
1350
|
-
const point6 = [x1 + radius, y2];
|
|
1351
|
-
const point7 = [x1, y2 - radius];
|
|
1352
|
-
const point8 = [x1, y1 + radius];
|
|
1353
|
-
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);
|
|
1307
|
+
return cloneSvgElement;
|
|
1354
1308
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
const rotateHeight = (realRotateLine / hypotenuse) * height;
|
|
1363
|
-
const rotatePoint = [
|
|
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 };
|
|
1309
|
+
function loadImage(src) {
|
|
1310
|
+
return new Promise((resolve, reject) => {
|
|
1311
|
+
const img = new Image();
|
|
1312
|
+
img.onload = () => resolve(img);
|
|
1313
|
+
img.onerror = () => reject(new Error('Failed to load image'));
|
|
1314
|
+
img.src = src;
|
|
1315
|
+
});
|
|
1370
1316
|
}
|
|
1371
|
-
function
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1317
|
+
async function toImage(board, options) {
|
|
1318
|
+
if (!board) {
|
|
1319
|
+
return undefined;
|
|
1320
|
+
}
|
|
1321
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1322
|
+
const { ratio = 2, fillStyle = 'transparent' } = options;
|
|
1323
|
+
const { width, height } = elementHostBox;
|
|
1324
|
+
const ratioWidth = width * ratio;
|
|
1325
|
+
const ratioHeight = height * ratio;
|
|
1326
|
+
const cloneSvgElement = cloneSvg(board, options);
|
|
1327
|
+
const { canvas, ctx } = createCanvas(ratioWidth, ratioHeight, fillStyle);
|
|
1328
|
+
const svgStr = new XMLSerializer().serializeToString(cloneSvgElement);
|
|
1329
|
+
const imgSrc = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;
|
|
1330
|
+
try {
|
|
1331
|
+
const img = await loadImage(imgSrc);
|
|
1332
|
+
ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight);
|
|
1333
|
+
const url = canvas.toDataURL('image/png');
|
|
1334
|
+
return url;
|
|
1335
|
+
}
|
|
1336
|
+
catch (error) {
|
|
1337
|
+
console.error('Error converting SVG to image:', error);
|
|
1338
|
+
return undefined;
|
|
1339
|
+
}
|
|
1376
1340
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1341
|
+
function downloadImage(url, name) {
|
|
1342
|
+
const a = document.createElement('a');
|
|
1343
|
+
a.href = url;
|
|
1344
|
+
a.download = name;
|
|
1345
|
+
a.click();
|
|
1346
|
+
a.remove();
|
|
1380
1347
|
}
|
|
1381
1348
|
|
|
1382
|
-
|
|
1383
|
-
return
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
const
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1349
|
+
const getClipboardByKey = (key) => {
|
|
1350
|
+
return `application/x-plait-${key}-fragment`;
|
|
1351
|
+
};
|
|
1352
|
+
const setClipboardData = (data, elements) => {
|
|
1353
|
+
const result = [...elements];
|
|
1354
|
+
const pluginContextResult = getDataFromClipboard(data);
|
|
1355
|
+
if (pluginContextResult) {
|
|
1356
|
+
result.push(...pluginContextResult);
|
|
1357
|
+
}
|
|
1358
|
+
const stringObj = JSON.stringify(result);
|
|
1359
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1360
|
+
data?.setData(`application/${CLIP_BOARD_FORMAT_KEY}`, encoded);
|
|
1361
|
+
};
|
|
1362
|
+
const setClipboardDataByText = (data, text) => {
|
|
1363
|
+
const pluginContextResult = getTextFromClipboard(data);
|
|
1364
|
+
data?.setData(`text/plain`, text + '\n' + pluginContextResult);
|
|
1365
|
+
};
|
|
1366
|
+
const setClipboardDataByMedia = (data, media, key) => {
|
|
1367
|
+
const stringObj = JSON.stringify(media);
|
|
1368
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1369
|
+
data?.setData(getClipboardByKey(key), encoded);
|
|
1370
|
+
};
|
|
1371
|
+
const getDataFromClipboard = (data) => {
|
|
1372
|
+
const encoded = data?.getData(`application/${CLIP_BOARD_FORMAT_KEY}`);
|
|
1373
|
+
let nodesData = [];
|
|
1374
|
+
if (encoded) {
|
|
1375
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1376
|
+
nodesData = JSON.parse(decoded);
|
|
1377
|
+
}
|
|
1378
|
+
return nodesData;
|
|
1379
|
+
};
|
|
1380
|
+
const getTextFromClipboard = (data) => {
|
|
1381
|
+
return (data ? data.getData(`text/plain`) : '');
|
|
1382
|
+
};
|
|
1383
|
+
const getClipboardDataByMedia = (data, key) => {
|
|
1384
|
+
const encoded = data?.getData(getClipboardByKey(key));
|
|
1385
|
+
let imageItem = null;
|
|
1386
|
+
if (encoded) {
|
|
1387
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1388
|
+
imageItem = JSON.parse(decoded);
|
|
1389
|
+
}
|
|
1390
|
+
return imageItem;
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
const BOARD_TO_TOUCH_REF = new WeakMap();
|
|
1394
|
+
const isPreventTouchMove = (board) => {
|
|
1395
|
+
return !!BOARD_TO_TOUCH_REF.get(board);
|
|
1396
|
+
};
|
|
1397
|
+
const preventTouchMove = (board, event, state) => {
|
|
1398
|
+
if (state && (event.target instanceof HTMLElement || event.target instanceof SVGElement)) {
|
|
1399
|
+
BOARD_TO_TOUCH_REF.set(board, { state, target: event.target instanceof SVGElement ? event.target : undefined });
|
|
1400
|
+
}
|
|
1401
|
+
else {
|
|
1402
|
+
const ref = BOARD_TO_TOUCH_REF.get(board);
|
|
1403
|
+
if (ref) {
|
|
1404
|
+
BOARD_TO_TOUCH_REF.delete(board);
|
|
1405
|
+
ref.host?.remove();
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
/**
|
|
1410
|
+
* some intersection maybe cause target is removed from current browser window,
|
|
1411
|
+
* after it was removed touch move event will not be fired
|
|
1412
|
+
* so scroll behavior will can not be prevented in mobile browser device
|
|
1413
|
+
* this function will prevent target element being remove.
|
|
1414
|
+
*/
|
|
1415
|
+
const handleTouchTarget = (board) => {
|
|
1416
|
+
const touchRef = BOARD_TO_TOUCH_REF.get(board);
|
|
1417
|
+
if (touchRef && touchRef.target && !touchRef.target.contains(PlaitBoard.getElementActiveHost(board))) {
|
|
1418
|
+
touchRef.target.style.opacity = '0';
|
|
1419
|
+
const host = createG();
|
|
1420
|
+
host.appendChild(touchRef.target);
|
|
1421
|
+
touchRef.host = host;
|
|
1422
|
+
PlaitBoard.getElementActiveHost(board).append(host);
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
|
|
1426
|
+
const PlaitElement = {
|
|
1427
|
+
isRootElement(value) {
|
|
1428
|
+
const parent = NODE_TO_PARENT.get(value);
|
|
1429
|
+
if (parent && PlaitBoard.isBoard(parent)) {
|
|
1430
|
+
return true;
|
|
1392
1431
|
}
|
|
1393
1432
|
else {
|
|
1394
|
-
|
|
1433
|
+
return false;
|
|
1395
1434
|
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1435
|
+
},
|
|
1436
|
+
getComponent(value) {
|
|
1437
|
+
return ELEMENT_TO_COMPONENT.get(value);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
const Path = {
|
|
1442
|
+
/**
|
|
1443
|
+
* Get a list of ancestor paths for a given path.
|
|
1444
|
+
*
|
|
1445
|
+
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
1446
|
+
* `reverse: true` option is passed, they are reversed.
|
|
1447
|
+
*/
|
|
1448
|
+
ancestors(path, options = {}) {
|
|
1449
|
+
const { reverse = false } = options;
|
|
1450
|
+
let paths = Path.levels(path, options);
|
|
1451
|
+
if (reverse) {
|
|
1452
|
+
paths = paths.slice(1);
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
paths = paths.slice(0, -1);
|
|
1456
|
+
}
|
|
1457
|
+
return paths;
|
|
1458
|
+
},
|
|
1459
|
+
/**
|
|
1460
|
+
* Get a list of paths at every level down to a path. Note: this is the same
|
|
1461
|
+
* as `Path.ancestors`, but including the path itself.
|
|
1462
|
+
*
|
|
1463
|
+
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
1464
|
+
* true` option is passed, they are reversed.
|
|
1465
|
+
*/
|
|
1466
|
+
levels(path, options = {}) {
|
|
1467
|
+
const { reverse = false } = options;
|
|
1468
|
+
const list = [];
|
|
1469
|
+
for (let i = 0; i <= path.length; i++) {
|
|
1470
|
+
list.push(path.slice(0, i));
|
|
1471
|
+
}
|
|
1472
|
+
if (reverse) {
|
|
1473
|
+
list.reverse();
|
|
1474
|
+
}
|
|
1475
|
+
return list;
|
|
1476
|
+
},
|
|
1477
|
+
parent(path) {
|
|
1478
|
+
if (path.length === 0) {
|
|
1479
|
+
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
1480
|
+
}
|
|
1481
|
+
return path.slice(0, -1);
|
|
1482
|
+
},
|
|
1483
|
+
next(path) {
|
|
1484
|
+
if (path.length === 0) {
|
|
1485
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
1486
|
+
}
|
|
1487
|
+
const last = path[path.length - 1];
|
|
1488
|
+
return path.slice(0, -1).concat(last + 1);
|
|
1489
|
+
},
|
|
1490
|
+
hasPrevious(path) {
|
|
1491
|
+
return path[path.length - 1] > 0;
|
|
1492
|
+
},
|
|
1493
|
+
previous(path) {
|
|
1494
|
+
if (path.length === 0) {
|
|
1495
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
|
|
1496
|
+
}
|
|
1497
|
+
const last = path[path.length - 1];
|
|
1498
|
+
return path.slice(0, -1).concat(last - 1);
|
|
1499
|
+
},
|
|
1500
|
+
/**
|
|
1501
|
+
* Check if a path is an ancestor of another.
|
|
1502
|
+
*/
|
|
1503
|
+
isAncestor(path, another) {
|
|
1504
|
+
return path.length < another.length && Path.compare(path, another) === 0;
|
|
1505
|
+
},
|
|
1506
|
+
/**
|
|
1507
|
+
* Compare a path to another, returning an integer indicating whether the path
|
|
1508
|
+
* was before, at, or after the other.
|
|
1509
|
+
*
|
|
1510
|
+
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
1511
|
+
* directly above or below the other. If you want exact matching, use
|
|
1512
|
+
* [[Path.equals]] instead.
|
|
1513
|
+
*/
|
|
1514
|
+
compare(path, another) {
|
|
1515
|
+
const min = Math.min(path.length, another.length);
|
|
1516
|
+
for (let i = 0; i < min; i++) {
|
|
1517
|
+
if (path[i] < another[i])
|
|
1518
|
+
return -1;
|
|
1519
|
+
if (path[i] > another[i])
|
|
1520
|
+
return 1;
|
|
1521
|
+
}
|
|
1522
|
+
return 0;
|
|
1523
|
+
},
|
|
1524
|
+
/**
|
|
1525
|
+
* Check if a path is exactly equal to another.
|
|
1526
|
+
*/
|
|
1527
|
+
equals(path, another) {
|
|
1528
|
+
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
1529
|
+
},
|
|
1530
|
+
/**
|
|
1531
|
+
* Check if a path ends before one of the indexes in another.
|
|
1532
|
+
*/
|
|
1533
|
+
endsBefore(path, another) {
|
|
1534
|
+
const i = path.length - 1;
|
|
1535
|
+
const as = path.slice(0, i);
|
|
1536
|
+
const bs = another.slice(0, i);
|
|
1537
|
+
const av = path[i];
|
|
1538
|
+
const bv = another[i];
|
|
1539
|
+
return Path.equals(as, bs) && av < bv;
|
|
1540
|
+
},
|
|
1541
|
+
/**
|
|
1542
|
+
* Check if a path is a sibling of another.
|
|
1543
|
+
*/
|
|
1544
|
+
isSibling(path, another) {
|
|
1545
|
+
if (path.length !== another.length) {
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
const as = path.slice(0, -1);
|
|
1549
|
+
const bs = another.slice(0, -1);
|
|
1550
|
+
const al = path[path.length - 1];
|
|
1551
|
+
const bl = another[another.length - 1];
|
|
1552
|
+
return al !== bl && Path.equals(as, bs);
|
|
1553
|
+
},
|
|
1554
|
+
transform(path, operation) {
|
|
1555
|
+
return produce(path, p => {
|
|
1556
|
+
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
1557
|
+
if (!path || path?.length === 0) {
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
if (p === null) {
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
switch (operation.type) {
|
|
1564
|
+
case 'insert_node': {
|
|
1565
|
+
const { path: op } = operation;
|
|
1566
|
+
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
1567
|
+
p[op.length - 1] += 1;
|
|
1568
|
+
}
|
|
1569
|
+
break;
|
|
1570
|
+
}
|
|
1571
|
+
case 'remove_node': {
|
|
1572
|
+
const { path: op } = operation;
|
|
1573
|
+
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
1574
|
+
return null;
|
|
1575
|
+
}
|
|
1576
|
+
else if (Path.endsBefore(op, p)) {
|
|
1577
|
+
p[op.length - 1] -= 1;
|
|
1578
|
+
}
|
|
1579
|
+
break;
|
|
1580
|
+
}
|
|
1581
|
+
case 'move_node': {
|
|
1582
|
+
const { path: op, newPath: onp } = operation;
|
|
1583
|
+
// If the old and new path are the same, it's a no-op.
|
|
1584
|
+
if (Path.equals(op, onp)) {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
1588
|
+
const copy = onp.slice();
|
|
1589
|
+
// op.length <= onp.length is different for slate
|
|
1590
|
+
// resolve drag from [0, 0] to [0, 3] issue
|
|
1591
|
+
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
1592
|
+
copy[op.length - 1] -= 1;
|
|
1593
|
+
}
|
|
1594
|
+
return copy.concat(p.slice(op.length));
|
|
1595
|
+
}
|
|
1596
|
+
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
1597
|
+
if (Path.endsBefore(op, p)) {
|
|
1598
|
+
p[op.length - 1] -= 1;
|
|
1599
|
+
}
|
|
1600
|
+
else {
|
|
1601
|
+
p[op.length - 1] += 1;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
1605
|
+
if (Path.endsBefore(op, p)) {
|
|
1606
|
+
p[op.length - 1] -= 1;
|
|
1607
|
+
}
|
|
1608
|
+
p[onp.length - 1] += 1;
|
|
1609
|
+
}
|
|
1610
|
+
else if (Path.endsBefore(op, p)) {
|
|
1611
|
+
if (Path.equals(onp, p)) {
|
|
1612
|
+
p[onp.length - 1] += 1;
|
|
1613
|
+
}
|
|
1614
|
+
p[op.length - 1] -= 1;
|
|
1615
|
+
}
|
|
1616
|
+
break;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
return p;
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
const PlaitNode = {
|
|
1625
|
+
parent: (board, path) => {
|
|
1626
|
+
const parentPath = Path.parent(path);
|
|
1627
|
+
const p = PlaitNode.get(board, parentPath);
|
|
1628
|
+
return p;
|
|
1629
|
+
},
|
|
1630
|
+
/**
|
|
1631
|
+
* Return a generator of all the ancestor nodes above a specific path.
|
|
1632
|
+
*
|
|
1633
|
+
* By default the order is top-down, from highest to lowest ancestor in
|
|
1634
|
+
* the tree, but you can pass the `reverse: true` option to go bottom-up.
|
|
1635
|
+
*/
|
|
1636
|
+
*parents(root, path, options = {}) {
|
|
1637
|
+
for (const p of Path.ancestors(path, options)) {
|
|
1638
|
+
const n = PlaitNode.get(root, p);
|
|
1639
|
+
yield n;
|
|
1411
1640
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1641
|
+
},
|
|
1642
|
+
get(root, path) {
|
|
1643
|
+
let node = root;
|
|
1644
|
+
for (let i = 0; i < path.length; i++) {
|
|
1645
|
+
const p = path[i];
|
|
1646
|
+
if (!node || !node.children || !node.children[p]) {
|
|
1647
|
+
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
1648
|
+
}
|
|
1649
|
+
node = node.children[p];
|
|
1650
|
+
}
|
|
1651
|
+
return node;
|
|
1652
|
+
},
|
|
1653
|
+
last(board, path) {
|
|
1654
|
+
let n = PlaitNode.get(board, path);
|
|
1655
|
+
while (n && n.children && n.children.length > 0) {
|
|
1656
|
+
const i = n.children.length - 1;
|
|
1657
|
+
n = n.children[i];
|
|
1414
1658
|
}
|
|
1659
|
+
return n;
|
|
1415
1660
|
}
|
|
1416
|
-
|
|
1417
|
-
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1418
|
-
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1419
|
-
path.setAttribute('fill', `none`);
|
|
1420
|
-
g.appendChild(path);
|
|
1421
|
-
return g;
|
|
1422
|
-
}
|
|
1661
|
+
};
|
|
1423
1662
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
const scheduleFunc = () => {
|
|
1427
|
-
timerId = requestAnimationFrame(() => {
|
|
1428
|
-
timerId = null;
|
|
1429
|
-
fn();
|
|
1430
|
-
});
|
|
1431
|
-
};
|
|
1432
|
-
if (timerId !== null) {
|
|
1433
|
-
cancelAnimationFrame(timerId);
|
|
1434
|
-
timerId = null;
|
|
1435
|
-
}
|
|
1436
|
-
scheduleFunc();
|
|
1663
|
+
const isSetViewportOperation = (value) => {
|
|
1664
|
+
return value.type === 'set_viewport';
|
|
1437
1665
|
};
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
timerSubscription.unsubscribe();
|
|
1443
|
-
timerSubscription = timer(wait).subscribe(() => {
|
|
1444
|
-
func();
|
|
1445
|
-
});
|
|
1666
|
+
const inverse = (op) => {
|
|
1667
|
+
switch (op.type) {
|
|
1668
|
+
case 'insert_node': {
|
|
1669
|
+
return { ...op, type: 'remove_node' };
|
|
1446
1670
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1671
|
+
case 'remove_node': {
|
|
1672
|
+
return { ...op, type: 'insert_node' };
|
|
1673
|
+
}
|
|
1674
|
+
case 'move_node': {
|
|
1675
|
+
const { newPath, path } = op;
|
|
1676
|
+
// PERF: in this case the move operation is a no-op anyways.
|
|
1677
|
+
if (Path.equals(newPath, path)) {
|
|
1678
|
+
return op;
|
|
1450
1679
|
}
|
|
1451
|
-
|
|
1680
|
+
// when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
|
|
1681
|
+
// shoud not return [0,2] -> [0,0] #WIK-8981
|
|
1682
|
+
// if (Path.isSibling(path, newPath)) {
|
|
1683
|
+
// return { ...op, path: newPath, newPath: path };
|
|
1684
|
+
// }
|
|
1685
|
+
// If the move does not happen within a single parent it is possible
|
|
1686
|
+
// for the move to impact the true path to the location where the node
|
|
1687
|
+
// was removed from and where it was inserted. We have to adjust for this
|
|
1688
|
+
// and find the original path. We can accomplish this (only in non-sibling)
|
|
1689
|
+
// moves by looking at the impact of the move operation on the node
|
|
1690
|
+
// after the original move path.
|
|
1691
|
+
const inversePath = Path.transform(path, op);
|
|
1692
|
+
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
1693
|
+
return { ...op, path: inversePath, newPath: inverseNewPath };
|
|
1452
1694
|
}
|
|
1453
|
-
|
|
1695
|
+
case 'set_node': {
|
|
1696
|
+
const { properties, newProperties } = op;
|
|
1697
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1698
|
+
}
|
|
1699
|
+
case 'set_selection': {
|
|
1700
|
+
const { properties, newProperties } = op;
|
|
1701
|
+
if (properties == null) {
|
|
1702
|
+
return {
|
|
1703
|
+
...op,
|
|
1704
|
+
properties: newProperties,
|
|
1705
|
+
newProperties: null
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
else if (newProperties == null) {
|
|
1709
|
+
return {
|
|
1710
|
+
...op,
|
|
1711
|
+
properties: null,
|
|
1712
|
+
newProperties: properties
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
else {
|
|
1716
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
case 'set_viewport': {
|
|
1720
|
+
const { properties, newProperties } = op;
|
|
1721
|
+
if (properties == null) {
|
|
1722
|
+
return {
|
|
1723
|
+
...op,
|
|
1724
|
+
properties: newProperties,
|
|
1725
|
+
newProperties: newProperties
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
else if (newProperties == null) {
|
|
1729
|
+
return {
|
|
1730
|
+
...op,
|
|
1731
|
+
properties: properties,
|
|
1732
|
+
newProperties: properties
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
else {
|
|
1736
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
case 'set_theme': {
|
|
1740
|
+
const { properties, newProperties } = op;
|
|
1741
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
const PlaitOperation = {
|
|
1746
|
+
isSetViewportOperation,
|
|
1747
|
+
inverse
|
|
1454
1748
|
};
|
|
1455
1749
|
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1750
|
+
const Point = {
|
|
1751
|
+
isEquals(point, otherPoint) {
|
|
1752
|
+
return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
|
|
1753
|
+
}
|
|
1458
1754
|
};
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1755
|
+
|
|
1756
|
+
const Viewport = {
|
|
1757
|
+
isViewport: (value) => {
|
|
1758
|
+
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
1759
|
+
},
|
|
1463
1760
|
};
|
|
1464
|
-
|
|
1465
|
-
|
|
1761
|
+
|
|
1762
|
+
const SAVING = new WeakMap();
|
|
1763
|
+
const MERGING = new WeakMap();
|
|
1764
|
+
|
|
1765
|
+
var PlaitPluginKey;
|
|
1766
|
+
(function (PlaitPluginKey) {
|
|
1767
|
+
PlaitPluginKey["withSelection"] = "withSelection";
|
|
1768
|
+
})(PlaitPluginKey || (PlaitPluginKey = {}));
|
|
1769
|
+
|
|
1770
|
+
var ThemeColorMode;
|
|
1771
|
+
(function (ThemeColorMode) {
|
|
1772
|
+
ThemeColorMode["default"] = "default";
|
|
1773
|
+
ThemeColorMode["colorful"] = "colorful";
|
|
1774
|
+
ThemeColorMode["soft"] = "soft";
|
|
1775
|
+
ThemeColorMode["retro"] = "retro";
|
|
1776
|
+
ThemeColorMode["dark"] = "dark";
|
|
1777
|
+
ThemeColorMode["starry"] = "starry";
|
|
1778
|
+
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
1779
|
+
const DefaultThemeColor = {
|
|
1780
|
+
mode: ThemeColorMode.default,
|
|
1781
|
+
boardBackground: '#ffffff',
|
|
1782
|
+
textColor: '#333333'
|
|
1466
1783
|
};
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1784
|
+
const ColorfulThemeColor = {
|
|
1785
|
+
mode: ThemeColorMode.colorful,
|
|
1786
|
+
boardBackground: '#ffffff',
|
|
1787
|
+
textColor: '#333333'
|
|
1788
|
+
};
|
|
1789
|
+
const SoftThemeColor = {
|
|
1790
|
+
mode: ThemeColorMode.soft,
|
|
1791
|
+
boardBackground: '#f5f5f5',
|
|
1792
|
+
textColor: '#333333'
|
|
1793
|
+
};
|
|
1794
|
+
const RetroThemeColor = {
|
|
1795
|
+
mode: ThemeColorMode.retro,
|
|
1796
|
+
boardBackground: '#f9f8ed',
|
|
1797
|
+
textColor: '#333333'
|
|
1798
|
+
};
|
|
1799
|
+
const DarkThemeColor = {
|
|
1800
|
+
mode: ThemeColorMode.dark,
|
|
1801
|
+
boardBackground: '#141414',
|
|
1802
|
+
textColor: '#FFFFFF'
|
|
1803
|
+
};
|
|
1804
|
+
const StarryThemeColor = {
|
|
1805
|
+
mode: ThemeColorMode.starry,
|
|
1806
|
+
boardBackground: '#0d2537',
|
|
1807
|
+
textColor: '#FFFFFF'
|
|
1469
1808
|
};
|
|
1809
|
+
const ThemeColors = [
|
|
1810
|
+
DefaultThemeColor,
|
|
1811
|
+
ColorfulThemeColor,
|
|
1812
|
+
SoftThemeColor,
|
|
1813
|
+
RetroThemeColor,
|
|
1814
|
+
DarkThemeColor,
|
|
1815
|
+
StarryThemeColor
|
|
1816
|
+
];
|
|
1470
1817
|
|
|
1471
|
-
function
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
}
|
|
1818
|
+
function getRectangleByElements(board, elements, recursion) {
|
|
1819
|
+
const boundaryBox = {
|
|
1820
|
+
left: Number.MAX_VALUE,
|
|
1821
|
+
top: Number.MAX_VALUE,
|
|
1822
|
+
right: Number.NEGATIVE_INFINITY,
|
|
1823
|
+
bottom: Number.NEGATIVE_INFINITY
|
|
1824
|
+
};
|
|
1825
|
+
const calcRectangleClient = (node) => {
|
|
1826
|
+
const nodeRectangle = board.getRectangle(node);
|
|
1827
|
+
if (nodeRectangle) {
|
|
1828
|
+
boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
|
|
1829
|
+
boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
|
|
1830
|
+
boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
|
|
1831
|
+
boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
|
|
1832
|
+
}
|
|
1833
|
+
else {
|
|
1834
|
+
console.error(`can not get rectangle of element:`, node);
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1837
|
+
elements.forEach(element => {
|
|
1838
|
+
if (recursion) {
|
|
1839
|
+
depthFirstRecursion(element, node => calcRectangleClient(node), node => board.isRecursion(node));
|
|
1840
|
+
}
|
|
1841
|
+
else {
|
|
1842
|
+
calcRectangleClient(element);
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
if (boundaryBox.left === Number.MAX_VALUE) {
|
|
1846
|
+
return {
|
|
1847
|
+
x: 0,
|
|
1848
|
+
y: 0,
|
|
1849
|
+
width: 0,
|
|
1850
|
+
height: 0
|
|
1851
|
+
};
|
|
1486
1852
|
}
|
|
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
1853
|
return {
|
|
1499
|
-
|
|
1500
|
-
|
|
1854
|
+
x: boundaryBox.left,
|
|
1855
|
+
y: boundaryBox.top,
|
|
1856
|
+
width: boundaryBox.right - boundaryBox.left,
|
|
1857
|
+
height: boundaryBox.bottom - boundaryBox.top
|
|
1501
1858
|
};
|
|
1502
1859
|
}
|
|
1503
|
-
function
|
|
1504
|
-
return
|
|
1505
|
-
}
|
|
1506
|
-
function cloneSvg(board, options) {
|
|
1507
|
-
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1508
|
-
const { width, height, x, y } = elementHostBox;
|
|
1509
|
-
const { padding = 4, inlineStyleClassNames } = options;
|
|
1510
|
-
const sourceSvg = PlaitBoard.getHost(board);
|
|
1511
|
-
const cloneSvgElement = sourceSvg.cloneNode(true);
|
|
1512
|
-
cloneSvgElement.style.width = `${width}px`;
|
|
1513
|
-
cloneSvgElement.style.height = `${height}px`;
|
|
1514
|
-
cloneSvgElement.style.backgroundColor = '';
|
|
1515
|
-
cloneSvgElement.setAttribute('width', `${width}`);
|
|
1516
|
-
cloneSvgElement.setAttribute('height', `${height}`);
|
|
1517
|
-
cloneSvgElement.setAttribute('viewBox', [x - padding, y - padding, width + 2 * padding, height + 2 * padding].join(','));
|
|
1518
|
-
if (inlineStyleClassNames) {
|
|
1519
|
-
const sourceNodes = Array.from(sourceSvg.querySelectorAll(inlineStyleClassNames));
|
|
1520
|
-
const cloneNodes = Array.from(cloneSvgElement.querySelectorAll(inlineStyleClassNames));
|
|
1521
|
-
sourceNodes.forEach((node, index) => {
|
|
1522
|
-
const cloneNode = cloneNodes[index];
|
|
1523
|
-
const childElements = Array.from(node.querySelectorAll('*')).filter(isElementNode);
|
|
1524
|
-
const cloneChildElements = Array.from(cloneNode.querySelectorAll('*')).filter(isElementNode);
|
|
1525
|
-
sourceNodes.push(...childElements);
|
|
1526
|
-
cloneNodes.push(...cloneChildElements);
|
|
1527
|
-
});
|
|
1528
|
-
sourceNodes.forEach((node, index) => {
|
|
1529
|
-
const cloneNode = cloneNodes[index];
|
|
1530
|
-
cloneCSSStyle(node, cloneNode);
|
|
1531
|
-
});
|
|
1532
|
-
}
|
|
1533
|
-
return cloneSvgElement;
|
|
1534
|
-
}
|
|
1535
|
-
function loadImage(src) {
|
|
1536
|
-
return new Promise((resolve, reject) => {
|
|
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
|
-
});
|
|
1860
|
+
function getBoardRectangle(board) {
|
|
1861
|
+
return getRectangleByElements(board, board.children, true);
|
|
1542
1862
|
}
|
|
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;
|
|
1863
|
+
function getElementById(board, id, dataSource) {
|
|
1864
|
+
if (!dataSource) {
|
|
1865
|
+
dataSource = findElements(board, { match: (element) => true, recursion: (element) => true });
|
|
1565
1866
|
}
|
|
1867
|
+
let element = dataSource.find((element) => element.id === id);
|
|
1868
|
+
return element;
|
|
1566
1869
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1870
|
+
function findElements(board, options) {
|
|
1871
|
+
let elements = [];
|
|
1872
|
+
depthFirstRecursion(board, node => {
|
|
1873
|
+
if (!PlaitBoard.isBoard(node) && options.match(node)) {
|
|
1874
|
+
elements.push(node);
|
|
1875
|
+
}
|
|
1876
|
+
}, (value) => {
|
|
1877
|
+
if (PlaitBoard.isBoard(value)) {
|
|
1878
|
+
return true;
|
|
1879
|
+
}
|
|
1880
|
+
else {
|
|
1881
|
+
return getIsRecursionFunc(board)(value) && options.recursion(value);
|
|
1882
|
+
}
|
|
1883
|
+
}, true);
|
|
1884
|
+
return elements;
|
|
1573
1885
|
}
|
|
1574
1886
|
|
|
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
|
-
|
|
1887
|
+
const PlaitBoard = {
|
|
1888
|
+
isBoard(value) {
|
|
1889
|
+
const cachedIsBoard = IS_BOARD_CACHE.get(value);
|
|
1890
|
+
if (cachedIsBoard !== undefined) {
|
|
1891
|
+
return cachedIsBoard;
|
|
1892
|
+
}
|
|
1893
|
+
const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
|
|
1894
|
+
IS_BOARD_CACHE.set(value, isBoard);
|
|
1895
|
+
return isBoard;
|
|
1896
|
+
},
|
|
1897
|
+
findPath(board, node) {
|
|
1898
|
+
const path = [];
|
|
1899
|
+
let child = node;
|
|
1900
|
+
while (true) {
|
|
1901
|
+
const parent = NODE_TO_PARENT.get(child);
|
|
1902
|
+
if (parent == null) {
|
|
1903
|
+
if (PlaitBoard.isBoard(child)) {
|
|
1904
|
+
return path;
|
|
1905
|
+
}
|
|
1906
|
+
else {
|
|
1907
|
+
break;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
const i = NODE_TO_INDEX.get(child);
|
|
1911
|
+
if (i == null) {
|
|
1912
|
+
break;
|
|
1913
|
+
}
|
|
1914
|
+
path.unshift(i);
|
|
1915
|
+
child = parent;
|
|
1916
|
+
}
|
|
1917
|
+
throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
|
|
1918
|
+
},
|
|
1919
|
+
getHost(board) {
|
|
1920
|
+
return BOARD_TO_HOST.get(board);
|
|
1921
|
+
},
|
|
1922
|
+
getElementHost(board) {
|
|
1923
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.host;
|
|
1924
|
+
},
|
|
1925
|
+
getElementUpperHost(board) {
|
|
1926
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
|
|
1927
|
+
},
|
|
1928
|
+
getElementActiveHost(board) {
|
|
1929
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
|
|
1930
|
+
},
|
|
1931
|
+
getRoughSVG(board) {
|
|
1932
|
+
return BOARD_TO_ROUGH_SVG.get(board);
|
|
1933
|
+
},
|
|
1934
|
+
getComponent(board) {
|
|
1935
|
+
return BOARD_TO_COMPONENT.get(board);
|
|
1936
|
+
},
|
|
1937
|
+
getBoardContainer(board) {
|
|
1938
|
+
return PlaitBoard.getComponent(board).nativeElement;
|
|
1939
|
+
},
|
|
1940
|
+
getRectangle(board) {
|
|
1941
|
+
return getRectangleByElements(board, board.children, true);
|
|
1942
|
+
},
|
|
1943
|
+
getViewportContainer(board) {
|
|
1944
|
+
return PlaitBoard.getHost(board).parentElement;
|
|
1945
|
+
},
|
|
1946
|
+
isFocus(board) {
|
|
1947
|
+
return !!board.selection;
|
|
1948
|
+
},
|
|
1949
|
+
isReadonly(board) {
|
|
1950
|
+
return board.options.readonly;
|
|
1951
|
+
},
|
|
1952
|
+
hasBeenTextEditing(board) {
|
|
1953
|
+
return !!IS_TEXT_EDITABLE.get(board);
|
|
1954
|
+
},
|
|
1955
|
+
getPointer(board) {
|
|
1956
|
+
return board.pointer;
|
|
1957
|
+
},
|
|
1958
|
+
isPointer(board, pointer) {
|
|
1959
|
+
return board.pointer === pointer;
|
|
1960
|
+
},
|
|
1961
|
+
isInPointer(board, pointers) {
|
|
1962
|
+
const point = board.pointer;
|
|
1963
|
+
return pointers.includes(point);
|
|
1964
|
+
},
|
|
1965
|
+
getMovingPointInBoard(board) {
|
|
1966
|
+
return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
|
|
1967
|
+
},
|
|
1968
|
+
isMovingPointInBoard(board) {
|
|
1969
|
+
const point = BOARD_TO_MOVING_POINT.get(board);
|
|
1970
|
+
const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1971
|
+
if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
|
|
1972
|
+
return true;
|
|
1973
|
+
}
|
|
1974
|
+
return false;
|
|
1975
|
+
},
|
|
1976
|
+
getThemeColors(board) {
|
|
1977
|
+
return (board.options.themeColors || ThemeColors);
|
|
1603
1978
|
}
|
|
1604
|
-
return nodesData;
|
|
1605
|
-
};
|
|
1606
|
-
const getTextFromClipboard = (data) => {
|
|
1607
|
-
return (data ? data.getData(`text/plain`) : '');
|
|
1608
1979
|
};
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1980
|
+
|
|
1981
|
+
const applyToDraft = (board, selection, viewport, theme, op) => {
|
|
1982
|
+
switch (op.type) {
|
|
1983
|
+
case 'insert_node': {
|
|
1984
|
+
const { path, node } = op;
|
|
1985
|
+
const parent = PlaitNode.parent(board, path);
|
|
1986
|
+
const index = path[path.length - 1];
|
|
1987
|
+
if (!parent.children || index > parent.children.length) {
|
|
1988
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
1989
|
+
}
|
|
1990
|
+
parent.children.splice(index, 0, node);
|
|
1991
|
+
break;
|
|
1992
|
+
}
|
|
1993
|
+
case 'remove_node': {
|
|
1994
|
+
const { path } = op;
|
|
1995
|
+
const parent = PlaitNode.parent(board, path);
|
|
1996
|
+
const index = path[path.length - 1];
|
|
1997
|
+
if (!parent.children || index > parent.children.length) {
|
|
1998
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
1999
|
+
}
|
|
2000
|
+
parent.children.splice(index, 1);
|
|
2001
|
+
break;
|
|
2002
|
+
}
|
|
2003
|
+
case 'move_node': {
|
|
2004
|
+
const { path, newPath } = op;
|
|
2005
|
+
if (Path.isAncestor(path, newPath)) {
|
|
2006
|
+
throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
|
|
2007
|
+
}
|
|
2008
|
+
const node = PlaitNode.get(board, path);
|
|
2009
|
+
const parent = PlaitNode.parent(board, path);
|
|
2010
|
+
const index = path[path.length - 1];
|
|
2011
|
+
// This is tricky, but since the `path` and `newPath` both refer to
|
|
2012
|
+
// the same snapshot in time, there's a mismatch. After either
|
|
2013
|
+
// removing the original position, the second step's path can be out
|
|
2014
|
+
// of date. So instead of using the `op.newPath` directly, we
|
|
2015
|
+
// transform `op.path` to ascertain what the `newPath` would be after
|
|
2016
|
+
// the operation was applied.
|
|
2017
|
+
parent.children?.splice(index, 1);
|
|
2018
|
+
const truePath = Path.transform(path, op);
|
|
2019
|
+
const newParent = PlaitNode.get(board, Path.parent(truePath));
|
|
2020
|
+
const newIndex = truePath[truePath.length - 1];
|
|
2021
|
+
newParent.children?.splice(newIndex, 0, node);
|
|
2022
|
+
break;
|
|
2023
|
+
}
|
|
2024
|
+
case 'set_node': {
|
|
2025
|
+
const { path, properties, newProperties } = op;
|
|
2026
|
+
if (path.length === 0) {
|
|
2027
|
+
throw new Error(`Cannot set properties on the root node!`);
|
|
2028
|
+
}
|
|
2029
|
+
const node = PlaitNode.get(board, path);
|
|
2030
|
+
for (const key in newProperties) {
|
|
2031
|
+
const value = newProperties[key];
|
|
2032
|
+
if (value == null) {
|
|
2033
|
+
delete node[key];
|
|
2034
|
+
}
|
|
2035
|
+
else {
|
|
2036
|
+
node[key] = value;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
// properties that were previously defined, but are now missing, must be deleted
|
|
2040
|
+
for (const key in properties) {
|
|
2041
|
+
if (!newProperties.hasOwnProperty(key)) {
|
|
2042
|
+
delete node[key];
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
break;
|
|
2046
|
+
}
|
|
2047
|
+
case 'set_viewport': {
|
|
2048
|
+
const { newProperties } = op;
|
|
2049
|
+
if (newProperties == null) {
|
|
2050
|
+
viewport = newProperties;
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
if (viewport == null) {
|
|
2054
|
+
if (!Viewport.isViewport(newProperties)) {
|
|
2055
|
+
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
2056
|
+
}
|
|
2057
|
+
viewport = { ...newProperties };
|
|
2058
|
+
}
|
|
2059
|
+
for (const key in newProperties) {
|
|
2060
|
+
const value = newProperties[key];
|
|
2061
|
+
if (value == null) {
|
|
2062
|
+
delete viewport[key];
|
|
2063
|
+
}
|
|
2064
|
+
else {
|
|
2065
|
+
viewport[key] = value;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
break;
|
|
2070
|
+
}
|
|
2071
|
+
case 'set_selection': {
|
|
2072
|
+
const { newProperties } = op;
|
|
2073
|
+
if (newProperties == null) {
|
|
2074
|
+
selection = newProperties;
|
|
2075
|
+
}
|
|
2076
|
+
else {
|
|
2077
|
+
if (selection === null) {
|
|
2078
|
+
selection = op.newProperties;
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
selection.ranges = newProperties.ranges;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
break;
|
|
2085
|
+
}
|
|
2086
|
+
case 'set_theme': {
|
|
2087
|
+
const { newProperties } = op;
|
|
2088
|
+
theme = newProperties;
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
1615
2091
|
}
|
|
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);
|
|
2092
|
+
return { selection, viewport, theme };
|
|
1624
2093
|
};
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
2094
|
+
const GeneralTransforms = {
|
|
2095
|
+
/**
|
|
2096
|
+
* Transform the board by an operation.
|
|
2097
|
+
*/
|
|
2098
|
+
transform(board, op) {
|
|
2099
|
+
board.children = createDraft(board.children);
|
|
2100
|
+
let viewport = board.viewport && createDraft(board.viewport);
|
|
2101
|
+
let selection = board.selection && createDraft(board.selection);
|
|
2102
|
+
let theme = board.theme && createDraft(board.theme);
|
|
2103
|
+
try {
|
|
2104
|
+
const state = applyToDraft(board, selection, viewport, theme, op);
|
|
2105
|
+
viewport = state.viewport;
|
|
2106
|
+
selection = state.selection;
|
|
2107
|
+
theme = state.theme;
|
|
1631
2108
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
2109
|
+
finally {
|
|
2110
|
+
board.children = finishDraft(board.children);
|
|
2111
|
+
if (selection) {
|
|
2112
|
+
board.selection = isDraft(selection) ? finishDraft(selection) : selection;
|
|
2113
|
+
}
|
|
2114
|
+
else {
|
|
2115
|
+
board.selection = null;
|
|
2116
|
+
}
|
|
2117
|
+
board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
|
|
2118
|
+
board.theme = isDraft(theme) ? finishDraft(theme) : theme;
|
|
1634
2119
|
}
|
|
1635
|
-
},
|
|
1636
|
-
getComponent(value) {
|
|
1637
|
-
return ELEMENT_TO_COMPONENT.get(value);
|
|
1638
2120
|
}
|
|
1639
2121
|
};
|
|
1640
2122
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
2123
|
+
function insertNode(board, node, path) {
|
|
2124
|
+
const operation = { type: 'insert_node', node, path };
|
|
2125
|
+
board.apply(operation);
|
|
2126
|
+
}
|
|
2127
|
+
function setNode(board, props, path) {
|
|
2128
|
+
const properties = {};
|
|
2129
|
+
const newProperties = {};
|
|
2130
|
+
const node = PlaitNode.get(board, path);
|
|
2131
|
+
for (const k in props) {
|
|
2132
|
+
if (node[k] !== props[k]) {
|
|
2133
|
+
if (node.hasOwnProperty(k)) {
|
|
2134
|
+
properties[k] = node[k];
|
|
2135
|
+
}
|
|
2136
|
+
if (props[k] != null)
|
|
2137
|
+
newProperties[k] = props[k];
|
|
1653
2138
|
}
|
|
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
2139
|
}
|
|
2140
|
+
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2141
|
+
board.apply(operation);
|
|
2142
|
+
}
|
|
2143
|
+
function removeNode(board, path) {
|
|
2144
|
+
const node = PlaitNode.get(board, path);
|
|
2145
|
+
const operation = { type: 'remove_node', path, node };
|
|
2146
|
+
board.apply(operation);
|
|
2147
|
+
}
|
|
2148
|
+
function moveNode(board, path, newPath) {
|
|
2149
|
+
const operation = { type: 'move_node', path, newPath };
|
|
2150
|
+
board.apply(operation);
|
|
2151
|
+
}
|
|
2152
|
+
const NodeTransforms = {
|
|
2153
|
+
insertNode,
|
|
2154
|
+
setNode,
|
|
2155
|
+
removeNode,
|
|
2156
|
+
moveNode
|
|
1679
2157
|
};
|
|
1680
2158
|
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
2159
|
+
function withSelection(board) {
|
|
2160
|
+
const { pointerDown, globalPointerMove, globalPointerUp, onChange } = board;
|
|
2161
|
+
let start = null;
|
|
2162
|
+
let end = null;
|
|
2163
|
+
let selectionMovingG;
|
|
2164
|
+
let selectionRectangleG;
|
|
2165
|
+
let previousSelectedElements;
|
|
2166
|
+
// prevent text from being selected when user pressed main pointer and is moving
|
|
2167
|
+
let needPreventNativeSelectionWhenMoving = false;
|
|
2168
|
+
board.pointerDown = (event) => {
|
|
2169
|
+
if (event.target instanceof Element && !event.target.closest('.plait-richtext-container')) {
|
|
2170
|
+
needPreventNativeSelectionWhenMoving = true;
|
|
1688
2171
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
2172
|
+
if (!isMainPointer(event)) {
|
|
2173
|
+
pointerDown(event);
|
|
2174
|
+
return;
|
|
1691
2175
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
2176
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2177
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2178
|
+
const range = { anchor: point, focus: point };
|
|
2179
|
+
const hitElements = getHitElements(board, { ranges: [range] });
|
|
2180
|
+
const selectedElements = getSelectedElements(board);
|
|
2181
|
+
if (hitElements.length === 1 && selectedElements.includes(hitElements[0]) && !options.isDisabledSelect) {
|
|
2182
|
+
pointerDown(event);
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) &&
|
|
2186
|
+
hitElements.length === 0 &&
|
|
2187
|
+
options.isMultiple &&
|
|
2188
|
+
!options.isDisabledSelect) {
|
|
2189
|
+
selectionRectangleG?.remove();
|
|
2190
|
+
start = point;
|
|
2191
|
+
preventTouchMove(board, event, true);
|
|
2192
|
+
}
|
|
2193
|
+
Transforms.setSelection(board, { ranges: [range] });
|
|
2194
|
+
pointerDown(event);
|
|
2195
|
+
};
|
|
2196
|
+
board.globalPointerMove = (event) => {
|
|
2197
|
+
if (needPreventNativeSelectionWhenMoving) {
|
|
2198
|
+
// prevent text from being selected
|
|
2199
|
+
event.preventDefault();
|
|
2200
|
+
}
|
|
2201
|
+
if (start) {
|
|
2202
|
+
const movedTarget = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2203
|
+
const rectangle = RectangleClient.toRectangleClient([start, movedTarget]);
|
|
2204
|
+
selectionMovingG?.remove();
|
|
2205
|
+
if (Math.hypot(rectangle.width, rectangle.height) > 5) {
|
|
2206
|
+
end = movedTarget;
|
|
2207
|
+
throttleRAF(() => {
|
|
2208
|
+
if (start && end) {
|
|
2209
|
+
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
setSelectionMoving(board);
|
|
2213
|
+
selectionMovingG = drawRectangle(board, rectangle, {
|
|
2214
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2215
|
+
strokeWidth: 1,
|
|
2216
|
+
fill: SELECTION_FILL_COLOR,
|
|
2217
|
+
fillStyle: 'solid'
|
|
2218
|
+
});
|
|
2219
|
+
PlaitBoard.getHost(board).append(selectionMovingG);
|
|
2220
|
+
}
|
|
1712
2221
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
2222
|
+
globalPointerMove(event);
|
|
2223
|
+
};
|
|
2224
|
+
board.globalPointerUp = (event) => {
|
|
2225
|
+
if (start && end) {
|
|
2226
|
+
selectionMovingG?.remove();
|
|
2227
|
+
clearSelectionMoving(board);
|
|
2228
|
+
Transforms.setSelection(board, { ranges: [{ anchor: start, focus: end }] });
|
|
1716
2229
|
}
|
|
1717
|
-
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
}
|
|
1726
|
-
else if (newProperties == null) {
|
|
1727
|
-
return {
|
|
1728
|
-
...op,
|
|
1729
|
-
properties: null,
|
|
1730
|
-
newProperties: properties
|
|
1731
|
-
};
|
|
1732
|
-
}
|
|
1733
|
-
else {
|
|
1734
|
-
return { ...op, properties: newProperties, newProperties: properties };
|
|
2230
|
+
if (PlaitBoard.isFocus(board)) {
|
|
2231
|
+
const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
|
|
2232
|
+
const isInDocument = event.target instanceof Node && document.contains(event.target);
|
|
2233
|
+
const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
|
|
2234
|
+
// Clear selection when mouse board outside area
|
|
2235
|
+
// The framework needs to determine whether the board is focused through selection
|
|
2236
|
+
if (!isInBoard && !start && !isAttachedElement && isInDocument) {
|
|
2237
|
+
Transforms.setSelection(board, null);
|
|
1735
2238
|
}
|
|
1736
2239
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
2240
|
+
start = null;
|
|
2241
|
+
end = null;
|
|
2242
|
+
needPreventNativeSelectionWhenMoving = false;
|
|
2243
|
+
preventTouchMove(board, event, false);
|
|
2244
|
+
globalPointerUp(event);
|
|
2245
|
+
};
|
|
2246
|
+
board.onChange = () => {
|
|
2247
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2248
|
+
if (options.isDisabledSelect) {
|
|
2249
|
+
clearSelectedElement(board);
|
|
2250
|
+
}
|
|
2251
|
+
// calc selected elements entry
|
|
2252
|
+
if (board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect) {
|
|
2253
|
+
try {
|
|
2254
|
+
if (board.operations.find(value => value.type === 'set_selection')) {
|
|
2255
|
+
selectionRectangleG?.remove();
|
|
2256
|
+
const temporaryElements = getTemporaryElements(board);
|
|
2257
|
+
let elements = temporaryElements ? temporaryElements : getHitElements(board);
|
|
2258
|
+
if (!options.isMultiple && elements.length > 1) {
|
|
2259
|
+
elements = [elements[0]];
|
|
2260
|
+
}
|
|
2261
|
+
cacheSelectedElements(board, elements);
|
|
2262
|
+
previousSelectedElements = elements;
|
|
2263
|
+
deleteTemporaryElements(board);
|
|
2264
|
+
if (!isSelectionMoving(board) && elements.length > 1) {
|
|
2265
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
else {
|
|
2269
|
+
// wait node destroy and remove selected element state
|
|
2270
|
+
setTimeout(() => {
|
|
2271
|
+
const currentSelectedElements = getSelectedElements(board);
|
|
2272
|
+
if (currentSelectedElements.length && currentSelectedElements.length > 1) {
|
|
2273
|
+
if (currentSelectedElements.length !== previousSelectedElements.length ||
|
|
2274
|
+
currentSelectedElements.some((c, index) => c !== previousSelectedElements[index])) {
|
|
2275
|
+
selectionRectangleG?.remove();
|
|
2276
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
2277
|
+
previousSelectedElements = currentSelectedElements;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
else {
|
|
2281
|
+
selectionRectangleG?.remove();
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
1752
2285
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
2286
|
+
catch (error) {
|
|
2287
|
+
console.error(error);
|
|
1755
2288
|
}
|
|
1756
2289
|
}
|
|
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
|
|
2290
|
+
onChange();
|
|
1791
2291
|
};
|
|
2292
|
+
board.setPluginOptions(PlaitPluginKey.withSelection, {
|
|
2293
|
+
isMultiple: true,
|
|
2294
|
+
isDisabledSelect: false
|
|
2295
|
+
});
|
|
2296
|
+
return board;
|
|
1792
2297
|
}
|
|
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;
|
|
2298
|
+
function getTemporaryElements(board) {
|
|
2299
|
+
const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
2300
|
+
if (ref) {
|
|
2301
|
+
return ref.elements;
|
|
1817
2302
|
}
|
|
1818
2303
|
else {
|
|
1819
|
-
|
|
1820
|
-
bottom = childrenRect.y + childrenRect.height;
|
|
1821
|
-
}
|
|
1822
|
-
return {
|
|
1823
|
-
left,
|
|
1824
|
-
right,
|
|
1825
|
-
top,
|
|
1826
|
-
bottom
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
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;
|
|
1838
|
-
}
|
|
1839
|
-
function getViewBox(board, zoom) {
|
|
1840
|
-
const boardContainerRectangle = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
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;
|
|
1851
|
-
}
|
|
1852
|
-
function getViewBoxCenterPoint(board) {
|
|
1853
|
-
const childrenRectangle = getRectangleByElements(board, board.children, true);
|
|
1854
|
-
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
1855
|
-
}
|
|
1856
|
-
function setSVGViewBox(board, viewBox) {
|
|
1857
|
-
const zoom = board.viewport.zoom;
|
|
1858
|
-
const hostElement = PlaitBoard.getHost(board);
|
|
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(' '));
|
|
2304
|
+
return undefined;
|
|
1864
2305
|
}
|
|
1865
2306
|
}
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
if (!origination)
|
|
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);
|
|
2307
|
+
function getTemporaryRef(board) {
|
|
2308
|
+
return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
1875
2309
|
}
|
|
1876
|
-
function
|
|
1877
|
-
|
|
1878
|
-
if (viewportContainer.scrollLeft !== left || viewportContainer.scrollTop !== top) {
|
|
1879
|
-
viewportContainer.scrollLeft = left;
|
|
1880
|
-
viewportContainer.scrollTop = top;
|
|
1881
|
-
isFromViewportChange && setIsFromViewportChange(board, true);
|
|
1882
|
-
}
|
|
2310
|
+
function deleteTemporaryElements(board) {
|
|
2311
|
+
BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
|
|
1883
2312
|
}
|
|
1884
|
-
function
|
|
1885
|
-
|
|
1886
|
-
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1887
|
-
viewportContainer.style.width = `${width}px`;
|
|
1888
|
-
viewportContainer.style.height = `${height}px`;
|
|
2313
|
+
function isSelectionMoving(board) {
|
|
2314
|
+
return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
|
|
1889
2315
|
}
|
|
1890
|
-
function
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
setSVGViewBox(board, viewBox);
|
|
2316
|
+
function setSelectionMoving(board) {
|
|
2317
|
+
PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
|
|
2318
|
+
BOARD_TO_IS_SELECTION_MOVING.set(board, true);
|
|
1894
2319
|
}
|
|
1895
|
-
function
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
2320
|
+
function clearSelectionMoving(board) {
|
|
2321
|
+
PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
|
|
2322
|
+
BOARD_TO_IS_SELECTION_MOVING.delete(board);
|
|
2323
|
+
}
|
|
2324
|
+
function createSelectionRectangleG(board) {
|
|
2325
|
+
const elements = getSelectedElements(board);
|
|
2326
|
+
const rectangle = getRectangleByElements(board, elements, false);
|
|
2327
|
+
if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
|
|
2328
|
+
const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
|
|
2329
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2330
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
2331
|
+
fillStyle: 'solid'
|
|
2332
|
+
});
|
|
2333
|
+
selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
|
|
2334
|
+
PlaitBoard.getHost(board).append(selectionRectangleG);
|
|
2335
|
+
return selectionRectangleG;
|
|
1906
2336
|
}
|
|
1907
|
-
|
|
2337
|
+
return null;
|
|
1908
2338
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
};
|
|
1912
|
-
|
|
1913
|
-
|
|
2339
|
+
|
|
2340
|
+
function setSelection(board, selection) {
|
|
2341
|
+
const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
|
|
2342
|
+
board.apply(operation);
|
|
2343
|
+
}
|
|
2344
|
+
const SelectionTransforms = {
|
|
2345
|
+
setSelection,
|
|
2346
|
+
addSelectionWithTemporaryElements
|
|
1914
2347
|
};
|
|
1915
|
-
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
2348
|
+
function addSelectionWithTemporaryElements(board, elements) {
|
|
2349
|
+
const timeoutId = setTimeout(() => {
|
|
2350
|
+
setSelection(board, { ranges: [] });
|
|
2351
|
+
}, 0);
|
|
2352
|
+
let ref = getTemporaryRef(board);
|
|
2353
|
+
if (ref) {
|
|
2354
|
+
clearTimeout(ref.timeoutId);
|
|
2355
|
+
const currentElements = ref.elements;
|
|
2356
|
+
ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
|
|
2357
|
+
ref.timeoutId = timeoutId;
|
|
1919
2358
|
}
|
|
1920
2359
|
else {
|
|
1921
|
-
|
|
2360
|
+
BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
|
|
1922
2361
|
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
function setViewport(board, viewport) {
|
|
2365
|
+
const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
|
|
2366
|
+
board.apply(operation);
|
|
2367
|
+
}
|
|
2368
|
+
const ViewportTransforms$1 = {
|
|
2369
|
+
setViewport
|
|
1923
2370
|
};
|
|
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
2371
|
|
|
1938
2372
|
function setTheme(board, themeColorMode) {
|
|
1939
2373
|
const operation = { type: 'set_theme', properties: board.theme, newProperties: themeColorMode };
|
|
@@ -2053,6 +2487,25 @@ const BoardTransforms = {
|
|
|
2053
2487
|
fitViewportWidth
|
|
2054
2488
|
};
|
|
2055
2489
|
|
|
2490
|
+
const removeElements = (board, elements) => {
|
|
2491
|
+
elements
|
|
2492
|
+
.map(element => {
|
|
2493
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2494
|
+
const ref = board.pathRef(path);
|
|
2495
|
+
return () => {
|
|
2496
|
+
removeNode(board, ref.current);
|
|
2497
|
+
ref.unref();
|
|
2498
|
+
removeSelectedElement(board, element);
|
|
2499
|
+
};
|
|
2500
|
+
})
|
|
2501
|
+
.forEach(action => {
|
|
2502
|
+
action();
|
|
2503
|
+
});
|
|
2504
|
+
};
|
|
2505
|
+
const CoreTransforms = {
|
|
2506
|
+
removeElements
|
|
2507
|
+
};
|
|
2508
|
+
|
|
2056
2509
|
const Transforms = {
|
|
2057
2510
|
...GeneralTransforms,
|
|
2058
2511
|
...ViewportTransforms$1,
|
|
@@ -2148,7 +2601,11 @@ function createBoard(children, options) {
|
|
|
2148
2601
|
dblclick: (event) => { },
|
|
2149
2602
|
setFragment: (data) => { },
|
|
2150
2603
|
insertFragment: (data) => { },
|
|
2151
|
-
deleteFragment: (data) => {
|
|
2604
|
+
deleteFragment: (data) => {
|
|
2605
|
+
const elements = board.getDeletedFragment([]);
|
|
2606
|
+
CoreTransforms.removeElements(board, elements);
|
|
2607
|
+
},
|
|
2608
|
+
getDeletedFragment: (data) => data,
|
|
2152
2609
|
drawElement: (context) => [],
|
|
2153
2610
|
redrawElement: (context, previousContext) => { },
|
|
2154
2611
|
destroyElement: (context) => { },
|
|
@@ -2320,177 +2777,6 @@ function withHandPointer(board) {
|
|
|
2320
2777
|
return board;
|
|
2321
2778
|
}
|
|
2322
2779
|
|
|
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
2780
|
function withViewport(board) {
|
|
2495
2781
|
const { onChange } = board;
|
|
2496
2782
|
const throttleUpdate = debounce(() => {
|
|
@@ -2519,19 +2805,201 @@ function withViewport(board) {
|
|
|
2519
2805
|
return board;
|
|
2520
2806
|
}
|
|
2521
2807
|
|
|
2808
|
+
class ReactionManager {
|
|
2809
|
+
constructor(board, activeElements, activeRectangle) {
|
|
2810
|
+
this.board = board;
|
|
2811
|
+
this.activeElements = activeElements;
|
|
2812
|
+
this.activeRectangle = activeRectangle;
|
|
2813
|
+
this.alignRectangles = this.getAlignRectangle();
|
|
2814
|
+
}
|
|
2815
|
+
getAlignRectangle() {
|
|
2816
|
+
const result = [];
|
|
2817
|
+
depthFirstRecursion(this.board, node => {
|
|
2818
|
+
if (PlaitBoard.isBoard(node) || this.activeElements.some(element => node.id === element.id) || node.type !== 'geometry') {
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
const rectangle = this.board.getRectangle(node);
|
|
2822
|
+
rectangle && result.push(rectangle);
|
|
2823
|
+
}, node => {
|
|
2824
|
+
if (node && (PlaitBoard.isBoard(node) || this.board.isRecursion(node))) {
|
|
2825
|
+
return true;
|
|
2826
|
+
}
|
|
2827
|
+
else {
|
|
2828
|
+
return false;
|
|
2829
|
+
}
|
|
2830
|
+
}, true);
|
|
2831
|
+
return result;
|
|
2832
|
+
}
|
|
2833
|
+
handleAlign() {
|
|
2834
|
+
const alignRectangles = this.getAlignRectangle();
|
|
2835
|
+
const g = createG();
|
|
2836
|
+
const alignLines = [];
|
|
2837
|
+
const offset = 12;
|
|
2838
|
+
const options = {
|
|
2839
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2840
|
+
strokeWidth: 1,
|
|
2841
|
+
strokeLineDash: [4, 4]
|
|
2842
|
+
};
|
|
2843
|
+
let deltaX = 0;
|
|
2844
|
+
let deltaY = 0;
|
|
2845
|
+
let isCorrectX = false;
|
|
2846
|
+
let isCorrectY = false;
|
|
2847
|
+
for (let alignRectangle of alignRectangles) {
|
|
2848
|
+
const closestDistances = this.calculateClosestDistances(this.activeRectangle, alignRectangle);
|
|
2849
|
+
let canDrawHorizontal = false;
|
|
2850
|
+
if (!isCorrectX && closestDistances.absXDistance < 5) {
|
|
2851
|
+
deltaX = closestDistances.xDistance;
|
|
2852
|
+
this.activeRectangle.x -= deltaX;
|
|
2853
|
+
isCorrectX = true;
|
|
2854
|
+
canDrawHorizontal = true;
|
|
2855
|
+
}
|
|
2856
|
+
if (closestDistances.absXDistance === 0) {
|
|
2857
|
+
canDrawHorizontal = true;
|
|
2858
|
+
}
|
|
2859
|
+
if (canDrawHorizontal) {
|
|
2860
|
+
const verticalY = [
|
|
2861
|
+
alignRectangle.y,
|
|
2862
|
+
alignRectangle.y + alignRectangle.height,
|
|
2863
|
+
this.activeRectangle.y,
|
|
2864
|
+
this.activeRectangle.y + this.activeRectangle.height
|
|
2865
|
+
];
|
|
2866
|
+
const lineTopY = Math.min(...verticalY) - offset;
|
|
2867
|
+
const lineBottomY = Math.max(...verticalY) + offset;
|
|
2868
|
+
const leftLine = [this.activeRectangle.x, lineTopY, this.activeRectangle.x, lineBottomY];
|
|
2869
|
+
const middleLine = [
|
|
2870
|
+
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
2871
|
+
lineTopY,
|
|
2872
|
+
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
2873
|
+
lineBottomY
|
|
2874
|
+
];
|
|
2875
|
+
const rightLine = [
|
|
2876
|
+
this.activeRectangle.x + this.activeRectangle.width,
|
|
2877
|
+
lineTopY,
|
|
2878
|
+
this.activeRectangle.x + this.activeRectangle.width,
|
|
2879
|
+
lineBottomY
|
|
2880
|
+
];
|
|
2881
|
+
const shouldDrawLeftLine = closestDistances.indexX === 0 ||
|
|
2882
|
+
closestDistances.indexX === 1 ||
|
|
2883
|
+
(closestDistances.indexX === 2 && this.activeRectangle.width === alignRectangle.width);
|
|
2884
|
+
if (shouldDrawLeftLine && !alignLines[0]) {
|
|
2885
|
+
alignLines[0] = leftLine;
|
|
2886
|
+
}
|
|
2887
|
+
const shouldDrawRightLine = closestDistances.indexX === 2 ||
|
|
2888
|
+
closestDistances.indexX === 3 ||
|
|
2889
|
+
(closestDistances.indexX === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
2890
|
+
if (shouldDrawRightLine && !alignLines[2]) {
|
|
2891
|
+
alignLines[2] = rightLine;
|
|
2892
|
+
}
|
|
2893
|
+
const shouldDrawMiddleLine = closestDistances.indexX === 4 || (!shouldDrawLeftLine && !shouldDrawRightLine);
|
|
2894
|
+
if (shouldDrawMiddleLine && !alignLines[1]) {
|
|
2895
|
+
alignLines[1] = middleLine;
|
|
2896
|
+
}
|
|
2897
|
+
isCorrectX = true;
|
|
2898
|
+
}
|
|
2899
|
+
let canDrawVertical = false;
|
|
2900
|
+
if (!isCorrectY && closestDistances.absYDistance < 5) {
|
|
2901
|
+
deltaY = closestDistances.yDistance;
|
|
2902
|
+
this.activeRectangle.y -= deltaY;
|
|
2903
|
+
isCorrectY = true;
|
|
2904
|
+
canDrawVertical = true;
|
|
2905
|
+
}
|
|
2906
|
+
if (closestDistances.absYDistance === 0) {
|
|
2907
|
+
canDrawVertical = true;
|
|
2908
|
+
}
|
|
2909
|
+
if (canDrawVertical) {
|
|
2910
|
+
const horizontalX = [
|
|
2911
|
+
alignRectangle.x,
|
|
2912
|
+
alignRectangle.x + alignRectangle.width,
|
|
2913
|
+
this.activeRectangle.x,
|
|
2914
|
+
this.activeRectangle.x + this.activeRectangle.width
|
|
2915
|
+
];
|
|
2916
|
+
const lineLeftX = Math.min(...horizontalX) - offset;
|
|
2917
|
+
const lineRightX = Math.max(...horizontalX) + offset;
|
|
2918
|
+
const topLine = [lineLeftX, this.activeRectangle.y, lineRightX, this.activeRectangle.y];
|
|
2919
|
+
const horizontalMiddleLine = [
|
|
2920
|
+
lineLeftX,
|
|
2921
|
+
this.activeRectangle.y + this.activeRectangle.height / 2,
|
|
2922
|
+
lineRightX,
|
|
2923
|
+
this.activeRectangle.y + this.activeRectangle.height / 2
|
|
2924
|
+
];
|
|
2925
|
+
const bottomLine = [
|
|
2926
|
+
lineLeftX,
|
|
2927
|
+
this.activeRectangle.y + this.activeRectangle.height,
|
|
2928
|
+
lineRightX,
|
|
2929
|
+
this.activeRectangle.y + this.activeRectangle.height
|
|
2930
|
+
];
|
|
2931
|
+
const shouldDrawTopLine = closestDistances.indexY === 0 ||
|
|
2932
|
+
closestDistances.indexY === 1 ||
|
|
2933
|
+
(closestDistances.indexY === 2 && this.activeRectangle.height === alignRectangle.height);
|
|
2934
|
+
if (shouldDrawTopLine && !alignLines[3]) {
|
|
2935
|
+
alignLines[3] = topLine;
|
|
2936
|
+
}
|
|
2937
|
+
const shouldDrawBottomLine = closestDistances.indexY === 2 ||
|
|
2938
|
+
closestDistances.indexY === 3 ||
|
|
2939
|
+
(closestDistances.indexY === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
2940
|
+
if (shouldDrawBottomLine && !alignLines[5]) {
|
|
2941
|
+
alignLines[5] = bottomLine;
|
|
2942
|
+
}
|
|
2943
|
+
const shouldDrawMiddleLine = closestDistances.indexY === 4 || (!shouldDrawTopLine && !shouldDrawBottomLine);
|
|
2944
|
+
if (shouldDrawMiddleLine && !alignLines[4]) {
|
|
2945
|
+
alignLines[4] = horizontalMiddleLine;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
if (alignLines.length) {
|
|
2950
|
+
alignLines.forEach(points => {
|
|
2951
|
+
if (!points.length)
|
|
2952
|
+
return;
|
|
2953
|
+
const xAlign = PlaitBoard.getRoughSVG(this.board).line(points[0], points[1], points[2], points[3], options);
|
|
2954
|
+
g.appendChild(xAlign);
|
|
2955
|
+
});
|
|
2956
|
+
}
|
|
2957
|
+
return { deltaX, deltaY, g };
|
|
2958
|
+
}
|
|
2959
|
+
calculateClosestDistances(activeRectangle, alignRectangle) {
|
|
2960
|
+
const activeRectangleCenter = [activeRectangle.x + activeRectangle.width / 2, activeRectangle.y + activeRectangle.height / 2];
|
|
2961
|
+
const alignRectangleCenter = [alignRectangle.x + alignRectangle.width / 2, alignRectangle.y + alignRectangle.height / 2];
|
|
2962
|
+
const centerXDistance = activeRectangleCenter[0] - alignRectangleCenter[0];
|
|
2963
|
+
const centerYDistance = activeRectangleCenter[1] - alignRectangleCenter[1];
|
|
2964
|
+
const leftToLeft = activeRectangle.x - alignRectangle.x;
|
|
2965
|
+
const leftToRight = activeRectangle.x - (alignRectangle.x + alignRectangle.width);
|
|
2966
|
+
const rightToRight = activeRectangle.x + activeRectangle.width - (alignRectangle.x + alignRectangle.width);
|
|
2967
|
+
const rightToLeft = activeRectangle.x + activeRectangle.width - alignRectangle.x;
|
|
2968
|
+
const topToTop = activeRectangle.y - alignRectangle.y;
|
|
2969
|
+
const topToBottom = activeRectangle.y - (alignRectangle.y + alignRectangle.height);
|
|
2970
|
+
const bottomToTop = activeRectangle.y + activeRectangle.height - alignRectangle.y;
|
|
2971
|
+
const bottomToBottom = activeRectangle.y + activeRectangle.height - (alignRectangle.y + alignRectangle.height);
|
|
2972
|
+
const xDistances = [leftToLeft, leftToRight, rightToRight, rightToLeft, centerXDistance];
|
|
2973
|
+
const yDistances = [topToTop, topToBottom, bottomToBottom, bottomToTop, centerYDistance];
|
|
2974
|
+
const xDistancesAbs = xDistances.map(distance => Math.abs(distance));
|
|
2975
|
+
const yDistancesAbs = yDistances.map(distance => Math.abs(distance));
|
|
2976
|
+
const indexX = xDistancesAbs.indexOf(Math.min(...xDistancesAbs));
|
|
2977
|
+
const indexY = yDistancesAbs.indexOf(Math.min(...yDistancesAbs));
|
|
2978
|
+
return {
|
|
2979
|
+
absXDistance: xDistancesAbs[indexX],
|
|
2980
|
+
xDistance: xDistances[indexX],
|
|
2981
|
+
absYDistance: yDistancesAbs[indexY],
|
|
2982
|
+
yDistance: yDistances[indexY],
|
|
2983
|
+
indexX,
|
|
2984
|
+
indexY
|
|
2985
|
+
};
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2522
2989
|
function withMoving(board) {
|
|
2523
|
-
const {
|
|
2990
|
+
const { pointerDown, pointerMove, globalPointerUp, globalPointerMove } = board;
|
|
2524
2991
|
let offsetX = 0;
|
|
2525
2992
|
let offsetY = 0;
|
|
2526
2993
|
let isPreventDefault = false;
|
|
2527
2994
|
let startPoint;
|
|
2528
2995
|
let activeElements = [];
|
|
2529
|
-
|
|
2996
|
+
let alignG = null;
|
|
2997
|
+
board.pointerDown = (event) => {
|
|
2530
2998
|
const host = BOARD_TO_HOST.get(board);
|
|
2531
2999
|
const point = transformPoint(board, toPoint(event.x, event.y, host));
|
|
2532
3000
|
const range = { anchor: point, focus: point };
|
|
2533
3001
|
let movableElements = board.children.filter(item => board.isMovable(item));
|
|
2534
|
-
if (movableElements.length) {
|
|
3002
|
+
if (movableElements.length && !isPreventTouchMove(board)) {
|
|
2535
3003
|
startPoint = point;
|
|
2536
3004
|
const selectedRootElements = getSelectedElements(board).filter(item => movableElements.includes(item));
|
|
2537
3005
|
const hitElement = getHitElementOfRoot(board, movableElements, range);
|
|
@@ -2541,21 +3009,35 @@ function withMoving(board) {
|
|
|
2541
3009
|
else if (hitElement) {
|
|
2542
3010
|
activeElements = [hitElement];
|
|
2543
3011
|
}
|
|
3012
|
+
if (activeElements.length > 0) {
|
|
3013
|
+
preventTouchMove(board, event, true);
|
|
3014
|
+
}
|
|
2544
3015
|
}
|
|
2545
|
-
|
|
3016
|
+
pointerDown(event);
|
|
2546
3017
|
};
|
|
2547
|
-
board.
|
|
3018
|
+
board.pointerMove = (event) => {
|
|
2548
3019
|
if (startPoint && activeElements.length && !PlaitBoard.hasBeenTextEditing(board)) {
|
|
2549
3020
|
if (!isPreventDefault) {
|
|
2550
3021
|
isPreventDefault = true;
|
|
2551
3022
|
}
|
|
3023
|
+
alignG?.remove();
|
|
2552
3024
|
const host = BOARD_TO_HOST.get(board);
|
|
2553
3025
|
const endPoint = transformPoint(board, toPoint(event.x, event.y, host));
|
|
2554
3026
|
offsetX = endPoint[0] - startPoint[0];
|
|
2555
3027
|
offsetY = endPoint[1] - startPoint[1];
|
|
3028
|
+
const activeElementsRectangle = getRectangleByElements(board, activeElements, true);
|
|
3029
|
+
activeElementsRectangle.x += offsetX;
|
|
3030
|
+
activeElementsRectangle.y += offsetY;
|
|
3031
|
+
const reactionManager = new ReactionManager(board, activeElements, activeElementsRectangle);
|
|
3032
|
+
const ref = reactionManager.handleAlign();
|
|
3033
|
+
offsetX -= ref.deltaX;
|
|
3034
|
+
offsetY -= ref.deltaY;
|
|
3035
|
+
alignG = ref.g;
|
|
3036
|
+
PlaitBoard.getElementActiveHost(board).append(alignG);
|
|
2556
3037
|
const offsetBuffer = 5;
|
|
2557
|
-
if (Math.abs(offsetX) > offsetBuffer || Math.abs(offsetY) > offsetBuffer) {
|
|
3038
|
+
if (Math.abs(offsetX) > offsetBuffer || Math.abs(offsetY) > offsetBuffer || getMovingElements(board).length > 0 || ref.deltaX) {
|
|
2558
3039
|
throttleRAF(() => {
|
|
3040
|
+
handleTouchTarget(board);
|
|
2559
3041
|
const currentElements = activeElements.map(activeElement => {
|
|
2560
3042
|
const points = activeElement.points || [];
|
|
2561
3043
|
const [x, y] = activeElement.points[0];
|
|
@@ -2576,25 +3058,27 @@ function withMoving(board) {
|
|
|
2576
3058
|
// 阻止 move 过程中触发画布滚动行为
|
|
2577
3059
|
event.preventDefault();
|
|
2578
3060
|
}
|
|
2579
|
-
|
|
3061
|
+
pointerMove(event);
|
|
2580
3062
|
};
|
|
2581
|
-
board.
|
|
3063
|
+
board.globalPointerMove = (event) => {
|
|
2582
3064
|
if (startPoint) {
|
|
2583
3065
|
const inPlaitBoardElement = isInPlaitBoard(board, event.x, event.y);
|
|
2584
3066
|
if (!inPlaitBoardElement) {
|
|
2585
3067
|
cancelMove(board);
|
|
2586
3068
|
}
|
|
2587
3069
|
}
|
|
2588
|
-
|
|
3070
|
+
globalPointerMove(event);
|
|
2589
3071
|
};
|
|
2590
|
-
board.
|
|
3072
|
+
board.globalPointerUp = event => {
|
|
2591
3073
|
isPreventDefault = false;
|
|
2592
3074
|
if (startPoint) {
|
|
2593
3075
|
cancelMove(board);
|
|
2594
3076
|
}
|
|
2595
|
-
|
|
3077
|
+
preventTouchMove(board, event, false);
|
|
3078
|
+
globalPointerUp(event);
|
|
2596
3079
|
};
|
|
2597
3080
|
function cancelMove(board) {
|
|
3081
|
+
alignG?.remove();
|
|
2598
3082
|
startPoint = null;
|
|
2599
3083
|
offsetX = 0;
|
|
2600
3084
|
offsetY = 0;
|
|
@@ -2630,10 +3114,10 @@ class PlaitIslandBaseComponent {
|
|
|
2630
3114
|
markForCheck() {
|
|
2631
3115
|
this.cdr.markForCheck();
|
|
2632
3116
|
}
|
|
3117
|
+
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 }); }
|
|
3118
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
|
|
2633
3119
|
}
|
|
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: [{
|
|
3120
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
|
|
2637
3121
|
type: Directive,
|
|
2638
3122
|
args: [{
|
|
2639
3123
|
host: {
|
|
@@ -2642,16 +3126,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
|
|
|
2642
3126
|
}]
|
|
2643
3127
|
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; } });
|
|
2644
3128
|
class PlaitIslandPopoverBaseComponent {
|
|
3129
|
+
constructor(cdr) {
|
|
3130
|
+
this.cdr = cdr;
|
|
3131
|
+
}
|
|
2645
3132
|
initialize(board) {
|
|
2646
3133
|
this.board = board;
|
|
2647
|
-
const
|
|
2648
|
-
|
|
2649
|
-
onChange();
|
|
3134
|
+
const boardComponent = PlaitBoard.getComponent(board);
|
|
3135
|
+
this.subscription = boardComponent.plaitChange.subscribe(() => {
|
|
2650
3136
|
if (hasOnBoardChange(this)) {
|
|
2651
3137
|
this.onBoardChange();
|
|
2652
3138
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
3139
|
+
this.cdr.markForCheck();
|
|
3140
|
+
});
|
|
2655
3141
|
}
|
|
2656
3142
|
ngOnInit() {
|
|
2657
3143
|
if (!this.board) {
|
|
@@ -2661,20 +3147,20 @@ class PlaitIslandPopoverBaseComponent {
|
|
|
2661
3147
|
this.islandOnInit();
|
|
2662
3148
|
}
|
|
2663
3149
|
ngOnDestroy() {
|
|
2664
|
-
this.
|
|
3150
|
+
this.subscription?.unsubscribe();
|
|
2665
3151
|
this.islandOnDestroy();
|
|
2666
3152
|
}
|
|
3153
|
+
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 }); }
|
|
3154
|
+
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
3155
|
}
|
|
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: [{
|
|
3156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
|
|
2671
3157
|
type: Directive,
|
|
2672
3158
|
args: [{
|
|
2673
3159
|
host: {
|
|
2674
3160
|
class: 'plait-island-popover-container'
|
|
2675
3161
|
}
|
|
2676
3162
|
}]
|
|
2677
|
-
}], propDecorators: { board: [{
|
|
3163
|
+
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { board: [{
|
|
2678
3164
|
type: Input
|
|
2679
3165
|
}] } });
|
|
2680
3166
|
const hasOnBoardChange = (value) => {
|
|
@@ -2706,9 +3192,14 @@ const withHotkey = (board) => {
|
|
|
2706
3192
|
return false;
|
|
2707
3193
|
}
|
|
2708
3194
|
}, true);
|
|
2709
|
-
Transforms.
|
|
3195
|
+
Transforms.addSelectionWithTemporaryElements(board, elements);
|
|
2710
3196
|
return;
|
|
2711
3197
|
}
|
|
3198
|
+
const selectedElements = getSelectedElements(board);
|
|
3199
|
+
if (!PlaitBoard.isReadonly(board) && selectedElements.length > 0 && (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
3200
|
+
event.preventDefault();
|
|
3201
|
+
board.deleteFragment(null);
|
|
3202
|
+
}
|
|
2712
3203
|
keydown(event);
|
|
2713
3204
|
};
|
|
2714
3205
|
board.globalKeydown = (event) => {
|
|
@@ -2752,10 +3243,10 @@ class PlaitContextService {
|
|
|
2752
3243
|
removeUploadingFile(fileEntry) {
|
|
2753
3244
|
this.uploadingFiles = this.uploadingFiles.filter(file => file.url !== fileEntry.url);
|
|
2754
3245
|
}
|
|
3246
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3247
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService }); }
|
|
2755
3248
|
}
|
|
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: [{
|
|
3249
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitContextService, decorators: [{
|
|
2759
3250
|
type: Injectable
|
|
2760
3251
|
}] });
|
|
2761
3252
|
|
|
@@ -2832,10 +3323,10 @@ class PlaitElementComponent {
|
|
|
2832
3323
|
ngOnDestroy() {
|
|
2833
3324
|
this.board.destroyElement(this.getContext());
|
|
2834
3325
|
}
|
|
3326
|
+
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 }); }
|
|
3327
|
+
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
3328
|
}
|
|
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: [{
|
|
3329
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitElementComponent, decorators: [{
|
|
2839
3330
|
type: Component,
|
|
2840
3331
|
args: [{
|
|
2841
3332
|
selector: 'plait-element',
|
|
@@ -2870,9 +3361,8 @@ class PlaitChildrenElement {
|
|
|
2870
3361
|
this.parentG = PlaitBoard.getElementHost(this.board);
|
|
2871
3362
|
}
|
|
2872
3363
|
}
|
|
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: `
|
|
3364
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitChildrenElement, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3365
|
+
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
3366
|
<plait-element
|
|
2877
3367
|
*ngFor="let item of parent.children; let index = index; trackBy: trackBy"
|
|
2878
3368
|
[index]="index"
|
|
@@ -2882,8 +3372,9 @@ PlaitChildrenElement.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", v
|
|
|
2882
3372
|
[effect]="effect"
|
|
2883
3373
|
[parentG]="parentG"
|
|
2884
3374
|
></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
|
-
|
|
3375
|
+
`, 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"] }] }); }
|
|
3376
|
+
}
|
|
3377
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitChildrenElement, decorators: [{
|
|
2887
3378
|
type: Component,
|
|
2888
3379
|
args: [{
|
|
2889
3380
|
selector: 'plait-children',
|
|
@@ -3116,10 +3607,6 @@ class PlaitBoardComponent {
|
|
|
3116
3607
|
this.board.globalKeydown(event);
|
|
3117
3608
|
}), filter(event => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board) && !hasInputOrTextareaTarget(event.target)))
|
|
3118
3609
|
.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
3610
|
this.board.keydown(event);
|
|
3124
3611
|
});
|
|
3125
3612
|
fromEvent(document, 'keyup')
|
|
@@ -3131,11 +3618,9 @@ class PlaitBoardComponent {
|
|
|
3131
3618
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
3132
3619
|
.subscribe((event) => {
|
|
3133
3620
|
const selectedElements = getSelectedElements(this.board);
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
this.board.setFragment(event.clipboardData, rectangle);
|
|
3138
|
-
}
|
|
3621
|
+
event.preventDefault();
|
|
3622
|
+
const rectangle = getRectangleByElements(this.board, selectedElements, false);
|
|
3623
|
+
this.board.setFragment(event.clipboardData, rectangle, 'copy');
|
|
3139
3624
|
});
|
|
3140
3625
|
fromEvent(document, 'paste')
|
|
3141
3626
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
@@ -3150,12 +3635,10 @@ class PlaitBoardComponent {
|
|
|
3150
3635
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
3151
3636
|
.subscribe((event) => {
|
|
3152
3637
|
const selectedElements = getSelectedElements(this.board);
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
this.board.deleteFragment(event.clipboardData);
|
|
3158
|
-
}
|
|
3638
|
+
event.preventDefault();
|
|
3639
|
+
const rectangle = getRectangleByElements(this.board, selectedElements, false);
|
|
3640
|
+
this.board.setFragment(event.clipboardData, rectangle, 'cut');
|
|
3641
|
+
this.board.deleteFragment(event.clipboardData);
|
|
3159
3642
|
});
|
|
3160
3643
|
}
|
|
3161
3644
|
viewportScrollListener() {
|
|
@@ -3181,7 +3664,9 @@ class PlaitBoardComponent {
|
|
|
3181
3664
|
});
|
|
3182
3665
|
});
|
|
3183
3666
|
this.ngZone.runOutsideAngular(() => {
|
|
3184
|
-
fromEvent(this.viewportContainer.nativeElement, 'touchmove', { passive: false })
|
|
3667
|
+
fromEvent(this.viewportContainer.nativeElement, 'touchmove', { passive: false })
|
|
3668
|
+
.pipe(takeUntil(this.destroy$))
|
|
3669
|
+
.subscribe((event) => {
|
|
3185
3670
|
if (isPreventTouchMove(this.board)) {
|
|
3186
3671
|
event.preventDefault();
|
|
3187
3672
|
}
|
|
@@ -3226,9 +3711,8 @@ class PlaitBoardComponent {
|
|
|
3226
3711
|
this.updateIslands();
|
|
3227
3712
|
});
|
|
3228
3713
|
}
|
|
3229
|
-
}
|
|
3230
|
-
|
|
3231
|
-
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: `
|
|
3714
|
+
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 }); }
|
|
3715
|
+
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: `
|
|
3232
3716
|
<div class="viewport-container" #viewportContainer>
|
|
3233
3717
|
<svg #svg width="100%" height="100%" style="position: relative;" class="board-host-svg">
|
|
3234
3718
|
<g class="element-host"></g>
|
|
@@ -3238,8 +3722,9 @@ PlaitBoardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", ve
|
|
|
3238
3722
|
<plait-children [board]="board" [effect]="effect"></plait-children>
|
|
3239
3723
|
</div>
|
|
3240
3724
|
<ng-content></ng-content>
|
|
3241
|
-
`, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3242
|
-
|
|
3725
|
+
`, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3726
|
+
}
|
|
3727
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitBoardComponent, decorators: [{
|
|
3243
3728
|
type: Component,
|
|
3244
3729
|
args: [{
|
|
3245
3730
|
selector: 'plait-board',
|
|
@@ -3296,11 +3781,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
|
|
|
3296
3781
|
|
|
3297
3782
|
const COMPONENTS = [PlaitBoardComponent, PlaitChildrenElement, PlaitElementComponent];
|
|
3298
3783
|
class PlaitModule {
|
|
3784
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
3785
|
+
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] }); }
|
|
3786
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, imports: [CommonModule] }); }
|
|
3299
3787
|
}
|
|
3300
|
-
|
|
3301
|
-
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] });
|
|
3302
|
-
PlaitModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitModule, imports: [CommonModule] });
|
|
3303
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitModule, decorators: [{
|
|
3788
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitModule, decorators: [{
|
|
3304
3789
|
type: NgModule,
|
|
3305
3790
|
args: [{
|
|
3306
3791
|
declarations: [...COMPONENTS],
|
|
@@ -3477,5 +3962,5 @@ function createModModifierKeys() {
|
|
|
3477
3962
|
* Generated bundle index. Do not edit.
|
|
3478
3963
|
*/
|
|
3479
3964
|
|
|
3480
|
-
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,
|
|
3965
|
+
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, 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 };
|
|
3481
3966
|
//# sourceMappingURL=plait-core.mjs.map
|