@plait/core 0.24.0-next.5 → 0.24.0-next.7
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/esm2022/board/board.component.mjs +2 -6
- package/esm2022/interfaces/board.mjs +1 -1
- package/esm2022/plugins/create-board.mjs +7 -2
- package/esm2022/plugins/with-hotkey.mjs +7 -2
- package/esm2022/transforms/element.mjs +22 -0
- package/esm2022/transforms/index.mjs +2 -1
- package/esm2022/utils/element.mjs +25 -7
- package/fesm2022/plait-core.mjs +1914 -1873
- package/fesm2022/plait-core.mjs.map +1 -1
- package/interfaces/board.d.ts +1 -0
- package/package.json +1 -1
- package/transforms/element.d.ts +6 -0
- package/transforms/index.d.ts +1 -0
- package/utils/element.d.ts +5 -1
package/fesm2022/plait-core.mjs
CHANGED
|
@@ -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
|
|
|
@@ -30,6 +30,16 @@ const BOARD_TO_MOVING_ELEMENT = new WeakMap();
|
|
|
30
30
|
const IS_PREVENT_TOUCH_MOVE = new WeakMap();
|
|
31
31
|
const PATH_REFS = new WeakMap();
|
|
32
32
|
|
|
33
|
+
var PlaitPointerType;
|
|
34
|
+
(function (PlaitPointerType) {
|
|
35
|
+
PlaitPointerType["hand"] = "hand";
|
|
36
|
+
PlaitPointerType["selection"] = "selection";
|
|
37
|
+
})(PlaitPointerType || (PlaitPointerType = {}));
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extendable Custom Types Interface
|
|
41
|
+
*/
|
|
42
|
+
|
|
33
43
|
function depthFirstRecursion(node, callback, recursion, isReverse) {
|
|
34
44
|
if (!recursion || recursion(node)) {
|
|
35
45
|
let children = [];
|
|
@@ -54,2073 +64,2080 @@ const getIsRecursionFunc = (board) => {
|
|
|
54
64
|
};
|
|
55
65
|
};
|
|
56
66
|
|
|
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));
|
|
67
|
+
const SELECTION_BORDER_COLOR = '#6698FF';
|
|
68
|
+
const SELECTION_FILL_COLOR = '#6698FF19'; // 主色 0.1 透明度
|
|
69
|
+
const Selection = {
|
|
70
|
+
isCollapsed(selection) {
|
|
71
|
+
if (selection.anchor[0] == selection.focus[0] && selection.anchor[1] === selection.focus[1]) {
|
|
72
|
+
return true;
|
|
76
73
|
}
|
|
77
74
|
else {
|
|
78
|
-
|
|
75
|
+
return false;
|
|
79
76
|
}
|
|
80
|
-
});
|
|
81
|
-
if (boundaryBox.left === Number.MAX_VALUE) {
|
|
82
|
-
return {
|
|
83
|
-
x: 0,
|
|
84
|
-
y: 0,
|
|
85
|
-
width: 0,
|
|
86
|
-
height: 0
|
|
87
|
-
};
|
|
88
77
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
function getBoardRectangle(board) {
|
|
97
|
-
return getRectangleByElements(board, board.children, true);
|
|
98
|
-
}
|
|
99
|
-
function getElementById(board, id) {
|
|
100
|
-
let element = null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const getHitElements = (board, selection, match = () => true) => {
|
|
81
|
+
const realSelection = selection || board.selection;
|
|
82
|
+
const selectedElements = [];
|
|
83
|
+
const isCollapsed = realSelection && realSelection.ranges.length === 1 && Selection.isCollapsed(realSelection.ranges[0]);
|
|
101
84
|
depthFirstRecursion(board, node => {
|
|
102
|
-
if (
|
|
103
|
-
|
|
85
|
+
if (selectedElements.length > 0 && isCollapsed) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!PlaitBoard.isBoard(node) &&
|
|
89
|
+
match(node) &&
|
|
90
|
+
realSelection &&
|
|
91
|
+
realSelection.ranges.some(range => {
|
|
92
|
+
return board.isHitSelection(node, range);
|
|
93
|
+
})) {
|
|
94
|
+
selectedElements.push(node);
|
|
104
95
|
}
|
|
105
96
|
}, getIsRecursionFunc(board), true);
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
var ThemeColorMode;
|
|
110
|
-
(function (ThemeColorMode) {
|
|
111
|
-
ThemeColorMode["default"] = "default";
|
|
112
|
-
ThemeColorMode["colorful"] = "colorful";
|
|
113
|
-
ThemeColorMode["soft"] = "soft";
|
|
114
|
-
ThemeColorMode["retro"] = "retro";
|
|
115
|
-
ThemeColorMode["dark"] = "dark";
|
|
116
|
-
ThemeColorMode["starry"] = "starry";
|
|
117
|
-
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
118
|
-
const DefaultThemeColor = {
|
|
119
|
-
mode: ThemeColorMode.default,
|
|
120
|
-
boardBackground: '#ffffff',
|
|
121
|
-
textColor: '#333333'
|
|
97
|
+
return selectedElements;
|
|
122
98
|
};
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
99
|
+
const getHitElementOfRoot = (board, rootElements, range) => {
|
|
100
|
+
const newRootElements = [...rootElements].reverse();
|
|
101
|
+
return newRootElements.find(item => {
|
|
102
|
+
return board.isHitSelection(item, range);
|
|
103
|
+
});
|
|
127
104
|
};
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
105
|
+
const isHitElements = (board, elements, ranges) => {
|
|
106
|
+
let isIntersectionElements = false;
|
|
107
|
+
if (elements.length) {
|
|
108
|
+
elements.map(item => {
|
|
109
|
+
if (!isIntersectionElements) {
|
|
110
|
+
isIntersectionElements = ranges.some(range => {
|
|
111
|
+
return board.isHitSelection(item, range);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return isIntersectionElements;
|
|
132
117
|
};
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
boardBackground: '#f9f8ed',
|
|
136
|
-
textColor: '#333333'
|
|
118
|
+
const cacheSelectedElements = (board, selectedElements) => {
|
|
119
|
+
BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
|
|
137
120
|
};
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
boardBackground: '#141414',
|
|
141
|
-
textColor: '#FFFFFF'
|
|
121
|
+
const getSelectedElements = (board) => {
|
|
122
|
+
return BOARD_TO_SELECTED_ELEMENT.get(board) || [];
|
|
142
123
|
};
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
textColor: '#FFFFFF'
|
|
124
|
+
const addSelectedElement = (board, element) => {
|
|
125
|
+
const selectedElements = getSelectedElements(board);
|
|
126
|
+
cacheSelectedElements(board, [...selectedElements, element]);
|
|
147
127
|
};
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const minX = origin.x < target.x ? origin.x : target.x;
|
|
160
|
-
const maxX = origin.x + origin.width > target.x + target.width ? origin.x + origin.width : target.x + target.width;
|
|
161
|
-
const minY = origin.y < target.y ? origin.y : target.y;
|
|
162
|
-
const maxY = origin.y + origin.height > target.y + target.height ? origin.y + origin.height : target.y + target.height;
|
|
163
|
-
// float calculate error( eg: 1.4210854715202004e-14 > 0)
|
|
164
|
-
if (Math.floor(maxX - minX - origin.width - target.width) <= 0 && Math.floor(maxY - minY - origin.height - target.height) <= 0) {
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
toRectangleClient: (points) => {
|
|
172
|
-
const xArray = points.map(ele => ele[0]);
|
|
173
|
-
const yArray = points.map(ele => ele[1]);
|
|
174
|
-
const xMin = Math.min(...xArray);
|
|
175
|
-
const xMax = Math.max(...xArray);
|
|
176
|
-
const yMin = Math.min(...yArray);
|
|
177
|
-
const yMax = Math.max(...yArray);
|
|
178
|
-
const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
179
|
-
return rect;
|
|
180
|
-
},
|
|
181
|
-
getOutlineRectangle: (rectangle, offset) => {
|
|
182
|
-
return {
|
|
183
|
-
x: rectangle.x + offset,
|
|
184
|
-
y: rectangle.y + offset,
|
|
185
|
-
width: rectangle.width - offset * 2,
|
|
186
|
-
height: rectangle.height - offset * 2
|
|
187
|
-
};
|
|
188
|
-
},
|
|
189
|
-
isEqual: (rectangle, otherRectangle) => {
|
|
190
|
-
return (rectangle.x === otherRectangle.x &&
|
|
191
|
-
rectangle.y === otherRectangle.y &&
|
|
192
|
-
rectangle.width === otherRectangle.width &&
|
|
193
|
-
rectangle.height === otherRectangle.height);
|
|
194
|
-
},
|
|
195
|
-
getCornerPoints: (rectangle) => {
|
|
196
|
-
return [
|
|
197
|
-
[rectangle.x, rectangle.y],
|
|
198
|
-
[rectangle.x + rectangle.width, rectangle.y],
|
|
199
|
-
[rectangle.x + rectangle.width, rectangle.y + rectangle.height],
|
|
200
|
-
[rectangle.x, rectangle.y + rectangle.height]
|
|
201
|
-
];
|
|
202
|
-
},
|
|
203
|
-
getEdgeCenterPoints: (rectangle) => {
|
|
204
|
-
return [
|
|
205
|
-
[rectangle.x + rectangle.width / 2, rectangle.y],
|
|
206
|
-
[rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
|
|
207
|
-
[rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height],
|
|
208
|
-
[rectangle.x, rectangle.y + rectangle.height / 2]
|
|
209
|
-
];
|
|
210
|
-
}
|
|
128
|
+
const removeSelectedElement = (board, element) => {
|
|
129
|
+
const selectedElements = getSelectedElements(board);
|
|
130
|
+
const newSelectedElements = selectedElements.filter(value => value !== element);
|
|
131
|
+
cacheSelectedElements(board, newSelectedElements);
|
|
132
|
+
};
|
|
133
|
+
const clearSelectedElement = (board) => {
|
|
134
|
+
cacheSelectedElements(board, []);
|
|
135
|
+
};
|
|
136
|
+
const isSelectedElement = (board, element) => {
|
|
137
|
+
const selectedElements = getSelectedElements(board);
|
|
138
|
+
return !!selectedElements.find(value => value === element);
|
|
211
139
|
};
|
|
212
140
|
|
|
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
|
-
|
|
141
|
+
/**
|
|
142
|
+
* @license
|
|
143
|
+
* Copyright Google LLC All Rights Reserved.
|
|
144
|
+
*
|
|
145
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
146
|
+
* found in the LICENSE file at https://angular.io/license
|
|
147
|
+
*/
|
|
148
|
+
const MAC_ENTER = 3;
|
|
149
|
+
const BACKSPACE = 8;
|
|
150
|
+
const TAB = 9;
|
|
151
|
+
const NUM_CENTER = 12;
|
|
152
|
+
const ENTER = 13;
|
|
153
|
+
const SHIFT = 16;
|
|
154
|
+
const CONTROL = 17;
|
|
155
|
+
const ALT = 18;
|
|
156
|
+
const PAUSE = 19;
|
|
157
|
+
const CAPS_LOCK = 20;
|
|
158
|
+
const ESCAPE = 27;
|
|
159
|
+
const SPACE = 32;
|
|
160
|
+
const PAGE_UP = 33;
|
|
161
|
+
const PAGE_DOWN = 34;
|
|
162
|
+
const END = 35;
|
|
163
|
+
const HOME = 36;
|
|
164
|
+
const LEFT_ARROW = 37;
|
|
165
|
+
const UP_ARROW = 38;
|
|
166
|
+
const RIGHT_ARROW = 39;
|
|
167
|
+
const DOWN_ARROW = 40;
|
|
168
|
+
const PLUS_SIGN = 43;
|
|
169
|
+
const PRINT_SCREEN = 44;
|
|
170
|
+
const INSERT = 45;
|
|
171
|
+
const DELETE = 46;
|
|
172
|
+
const ZERO = 48;
|
|
173
|
+
const ONE = 49;
|
|
174
|
+
const TWO = 50;
|
|
175
|
+
const THREE = 51;
|
|
176
|
+
const FOUR = 52;
|
|
177
|
+
const FIVE = 53;
|
|
178
|
+
const SIX = 54;
|
|
179
|
+
const SEVEN = 55;
|
|
180
|
+
const EIGHT = 56;
|
|
181
|
+
const NINE = 57;
|
|
182
|
+
const FF_SEMICOLON = 59; // Firefox (Gecko) fires this for semicolon instead of 186
|
|
183
|
+
const FF_EQUALS = 61; // Firefox (Gecko) fires this for equals instead of 187
|
|
184
|
+
const QUESTION_MARK = 63;
|
|
185
|
+
const AT_SIGN = 64;
|
|
186
|
+
const A = 65;
|
|
187
|
+
const B = 66;
|
|
188
|
+
const C = 67;
|
|
189
|
+
const D = 68;
|
|
190
|
+
const E = 69;
|
|
191
|
+
const F = 70;
|
|
192
|
+
const G = 71;
|
|
193
|
+
const H = 72;
|
|
194
|
+
const I = 73;
|
|
195
|
+
const J = 74;
|
|
196
|
+
const K = 75;
|
|
197
|
+
const L = 76;
|
|
198
|
+
const M = 77;
|
|
199
|
+
const N = 78;
|
|
200
|
+
const O = 79;
|
|
201
|
+
const P = 80;
|
|
202
|
+
const Q = 81;
|
|
203
|
+
const R = 82;
|
|
204
|
+
const S = 83;
|
|
205
|
+
const T = 84;
|
|
206
|
+
const U = 85;
|
|
207
|
+
const V = 86;
|
|
208
|
+
const W = 87;
|
|
209
|
+
const X = 88;
|
|
210
|
+
const Y = 89;
|
|
211
|
+
const Z = 90;
|
|
212
|
+
const META = 91; // WIN_KEY_LEFT
|
|
213
|
+
const MAC_WK_CMD_LEFT = 91;
|
|
214
|
+
const MAC_WK_CMD_RIGHT = 93;
|
|
215
|
+
const CONTEXT_MENU = 93;
|
|
216
|
+
const NUMPAD_ZERO = 96;
|
|
217
|
+
const NUMPAD_ONE = 97;
|
|
218
|
+
const NUMPAD_TWO = 98;
|
|
219
|
+
const NUMPAD_THREE = 99;
|
|
220
|
+
const NUMPAD_FOUR = 100;
|
|
221
|
+
const NUMPAD_FIVE = 101;
|
|
222
|
+
const NUMPAD_SIX = 102;
|
|
223
|
+
const NUMPAD_SEVEN = 103;
|
|
224
|
+
const NUMPAD_EIGHT = 104;
|
|
225
|
+
const NUMPAD_NINE = 105;
|
|
226
|
+
const NUMPAD_MULTIPLY = 106;
|
|
227
|
+
const NUMPAD_PLUS = 107;
|
|
228
|
+
const NUMPAD_MINUS = 109;
|
|
229
|
+
const NUMPAD_PERIOD = 110;
|
|
230
|
+
const NUMPAD_DIVIDE = 111;
|
|
231
|
+
const F1 = 112;
|
|
232
|
+
const F2 = 113;
|
|
233
|
+
const F3 = 114;
|
|
234
|
+
const F4 = 115;
|
|
235
|
+
const F5 = 116;
|
|
236
|
+
const F6 = 117;
|
|
237
|
+
const F7 = 118;
|
|
238
|
+
const F8 = 119;
|
|
239
|
+
const F9 = 120;
|
|
240
|
+
const F10 = 121;
|
|
241
|
+
const F11 = 122;
|
|
242
|
+
const F12 = 123;
|
|
243
|
+
const NUM_LOCK = 144;
|
|
244
|
+
const SCROLL_LOCK = 145;
|
|
245
|
+
const FIRST_MEDIA = 166;
|
|
246
|
+
const FF_MINUS = 173;
|
|
247
|
+
const MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
248
|
+
const VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
249
|
+
const VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
250
|
+
const FF_MUTE = 181;
|
|
251
|
+
const FF_VOLUME_DOWN = 182;
|
|
252
|
+
const LAST_MEDIA = 183;
|
|
253
|
+
const FF_VOLUME_UP = 183;
|
|
254
|
+
const SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
255
|
+
const EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
256
|
+
const COMMA = 188;
|
|
257
|
+
const DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
258
|
+
const PERIOD = 190;
|
|
259
|
+
const SLASH = 191;
|
|
260
|
+
const APOSTROPHE = 192;
|
|
261
|
+
const TILDE = 192;
|
|
262
|
+
const OPEN_SQUARE_BRACKET = 219;
|
|
263
|
+
const BACKSLASH = 220;
|
|
264
|
+
const CLOSE_SQUARE_BRACKET = 221;
|
|
265
|
+
const SINGLE_QUOTE = 222;
|
|
266
|
+
const MAC_META = 224;
|
|
267
|
+
|
|
268
|
+
var ResizeCursorClass;
|
|
269
|
+
(function (ResizeCursorClass) {
|
|
270
|
+
ResizeCursorClass["ew-resize"] = "ew-resize";
|
|
271
|
+
})(ResizeCursorClass || (ResizeCursorClass = {}));
|
|
272
|
+
|
|
273
|
+
const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
|
|
274
|
+
|
|
275
|
+
const CLIP_BOARD_FORMAT_KEY = 'x-plait-fragment';
|
|
276
|
+
const HOST_CLASS_NAME = 'plait-board-container';
|
|
277
|
+
const SCROLL_BAR_WIDTH = 20;
|
|
278
|
+
const MAX_RADIUS = 16;
|
|
279
|
+
const POINTER_BUTTON = {
|
|
280
|
+
MAIN: 0,
|
|
281
|
+
WHEEL: 1,
|
|
282
|
+
SECONDARY: 2,
|
|
283
|
+
TOUCH: -1
|
|
284
|
+
};
|
|
285
|
+
const PRESS_AND_MOVE_BUFFER = 5;
|
|
286
|
+
|
|
287
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
288
|
+
function toPoint(x, y, container) {
|
|
289
|
+
const rect = container.getBoundingClientRect();
|
|
290
|
+
return [x - rect.x, y - rect.y];
|
|
270
291
|
}
|
|
271
|
-
function
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
for (let i = 0; i < len - 1; i++) {
|
|
275
|
-
const p = points[i];
|
|
276
|
-
const p2 = points[i + 1];
|
|
277
|
-
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
278
|
-
if (currentDistance < distance) {
|
|
279
|
-
distance = currentDistance;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return distance;
|
|
292
|
+
function createG() {
|
|
293
|
+
const newG = document.createElementNS(NS, 'g');
|
|
294
|
+
return newG;
|
|
283
295
|
}
|
|
284
|
-
function
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
+
function createPath() {
|
|
297
|
+
const newG = document.createElementNS(NS, 'path');
|
|
298
|
+
return newG;
|
|
299
|
+
}
|
|
300
|
+
function createRect(rectangle, options) {
|
|
301
|
+
const rect = document.createElementNS(NS, 'rect');
|
|
302
|
+
rect.setAttribute('x', `${rectangle.x}`);
|
|
303
|
+
rect.setAttribute('y', `${rectangle.y}`);
|
|
304
|
+
rect.setAttribute('width', `${rectangle.width}`);
|
|
305
|
+
rect.setAttribute('height', `${rectangle.height}`);
|
|
306
|
+
for (let key in options) {
|
|
307
|
+
const optionKey = key;
|
|
308
|
+
rect.setAttribute(key, `${options[optionKey]}`);
|
|
296
309
|
}
|
|
297
|
-
return
|
|
310
|
+
return rect;
|
|
298
311
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
312
|
+
const setStrokeLinecap = (g, value) => {
|
|
313
|
+
g.setAttribute('stroke-linecap', value);
|
|
314
|
+
};
|
|
315
|
+
const setPathStrokeLinecap = (g, value) => {
|
|
316
|
+
g.querySelectorAll('path').forEach(path => {
|
|
317
|
+
path.setAttribute('stroke-linecap', value);
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
function createMask() {
|
|
321
|
+
return document.createElementNS(NS, 'mask');
|
|
304
322
|
}
|
|
305
|
-
function
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
return Math.hypot(dx, dy);
|
|
323
|
+
function createSVG() {
|
|
324
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
325
|
+
return svg;
|
|
309
326
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
327
|
+
function createText(x, y, fill, textContent) {
|
|
328
|
+
var text = document.createElementNS(NS, 'text');
|
|
329
|
+
text.setAttribute('x', `${x}`);
|
|
330
|
+
text.setAttribute('y', `${y}`);
|
|
331
|
+
text.setAttribute('fill', fill);
|
|
332
|
+
text.textContent = textContent;
|
|
333
|
+
return text;
|
|
315
334
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const ca = [a[0] - c[0], a[1] - c[1]];
|
|
322
|
-
const cb = [b[0] - c[0], b[1] - c[1]];
|
|
323
|
-
const cd = [d[0] - c[0], d[1] - c[1]];
|
|
324
|
-
return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
|
|
335
|
+
/**
|
|
336
|
+
* Check if a DOM node is an element node.
|
|
337
|
+
*/
|
|
338
|
+
const isDOMElement = (value) => {
|
|
339
|
+
return isDOMNode(value) && value.nodeType === 1;
|
|
325
340
|
};
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
341
|
+
/**
|
|
342
|
+
* Check if a value is a DOM node.
|
|
343
|
+
*/
|
|
344
|
+
const isDOMNode = (value) => {
|
|
345
|
+
return value instanceof window.Node;
|
|
346
|
+
};
|
|
347
|
+
const hasInputOrTextareaTarget = (target) => {
|
|
348
|
+
if (isDOMElement(target)) {
|
|
349
|
+
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {
|
|
334
350
|
return true;
|
|
335
351
|
}
|
|
336
352
|
}
|
|
337
353
|
return false;
|
|
338
354
|
};
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
// ray-casting algorithm based on
|
|
342
|
-
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
|
|
343
|
-
const x = point[0], y = point[1];
|
|
344
|
-
let inside = false;
|
|
345
|
-
for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
346
|
-
let xi = points[i][0], yi = points[i][1];
|
|
347
|
-
let xj = points[j][0], yj = points[j][1];
|
|
348
|
-
let intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
|
|
349
|
-
if (intersect)
|
|
350
|
-
inside = !inside;
|
|
351
|
-
}
|
|
352
|
-
return inside;
|
|
353
|
-
};
|
|
354
|
-
const isPointInEllipse = (point, center, rx, ry, rotation = 0) => {
|
|
355
|
-
const cosAngle = Math.cos(rotation);
|
|
356
|
-
const sinAngle = Math.sin(rotation);
|
|
357
|
-
const x1 = (point[0] - center[0]) * cosAngle + (point[1] - center[1]) * sinAngle;
|
|
358
|
-
const y1 = (point[1] - center[1]) * cosAngle - (point[0] - center[0]) * sinAngle;
|
|
359
|
-
return (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry) <= 1;
|
|
355
|
+
const isSecondaryPointer = (event) => {
|
|
356
|
+
return event.button === POINTER_BUTTON.SECONDARY;
|
|
360
357
|
};
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
const isInRectangle = point[0] >= rectX && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + height;
|
|
364
|
-
const handleLeftTop = point[0] >= rectX &&
|
|
365
|
-
point[0] <= rectX + radius &&
|
|
366
|
-
point[1] >= rectY &&
|
|
367
|
-
point[1] <= rectY + radius &&
|
|
368
|
-
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + radius)) > radius;
|
|
369
|
-
const handleLeftBottom = point[0] >= rectX &&
|
|
370
|
-
point[0] <= rectX + radius &&
|
|
371
|
-
point[1] >= rectY + height &&
|
|
372
|
-
point[1] <= rectY + height - radius &&
|
|
373
|
-
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + height - radius)) > radius;
|
|
374
|
-
const handleRightTop = point[0] >= rectX + width - radius &&
|
|
375
|
-
point[0] <= rectX + width &&
|
|
376
|
-
point[1] >= rectY &&
|
|
377
|
-
point[1] <= rectY + radius &&
|
|
378
|
-
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + radius)) > radius;
|
|
379
|
-
const handleRightBottom = point[0] >= rectX + width - radius &&
|
|
380
|
-
point[0] <= rectX + width &&
|
|
381
|
-
point[1] >= rectY + height - radius &&
|
|
382
|
-
point[1] <= rectY + height &&
|
|
383
|
-
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + height - radius)) > radius;
|
|
384
|
-
const isInCorner = handleLeftTop || handleLeftBottom || handleRightTop || handleRightBottom;
|
|
385
|
-
return isInRectangle && !isInCorner;
|
|
358
|
+
const isMainPointer = (event) => {
|
|
359
|
+
return event.button === POINTER_BUTTON.MAIN;
|
|
386
360
|
};
|
|
387
361
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
362
|
+
function hasBeforeContextChange(value) {
|
|
363
|
+
if (value.beforeContextChange) {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
function hasOnContextChanged(value) {
|
|
369
|
+
if (value.onContextChanged) {
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
class PlaitPluginElementComponent {
|
|
376
|
+
set context(value) {
|
|
377
|
+
if (hasBeforeContextChange(this)) {
|
|
378
|
+
this.beforeContextChange(value);
|
|
393
379
|
}
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if (parent == null) {
|
|
404
|
-
if (PlaitBoard.isBoard(child)) {
|
|
405
|
-
return path;
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
380
|
+
const previousContext = this._context;
|
|
381
|
+
this._context = value;
|
|
382
|
+
if (this.element) {
|
|
383
|
+
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
384
|
+
}
|
|
385
|
+
if (this.initialized) {
|
|
386
|
+
this.cdr.markForCheck();
|
|
387
|
+
if (hasOnContextChanged(this)) {
|
|
388
|
+
this.onContextChanged(value, previousContext);
|
|
410
389
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
if (PlaitElement.isRootElement(this.element) && this.element.children) {
|
|
393
|
+
this.g = createG();
|
|
394
|
+
this.rootG = createG();
|
|
395
|
+
this.rootG.append(this.g);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
this.g = createG();
|
|
414
399
|
}
|
|
415
|
-
path.unshift(i);
|
|
416
|
-
child = parent;
|
|
417
400
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
getRectangle(board) {
|
|
442
|
-
return getRectangleByElements(board, board.children, true);
|
|
443
|
-
},
|
|
444
|
-
getViewportContainer(board) {
|
|
445
|
-
return PlaitBoard.getHost(board).parentElement;
|
|
446
|
-
},
|
|
447
|
-
isFocus(board) {
|
|
448
|
-
return !!board.selection;
|
|
449
|
-
},
|
|
450
|
-
isReadonly(board) {
|
|
451
|
-
return board.options.readonly;
|
|
452
|
-
},
|
|
453
|
-
hasBeenTextEditing(board) {
|
|
454
|
-
return !!IS_TEXT_EDITABLE.get(board);
|
|
455
|
-
},
|
|
456
|
-
getPointer(board) {
|
|
457
|
-
return board.pointer;
|
|
458
|
-
},
|
|
459
|
-
isPointer(board, pointer) {
|
|
460
|
-
return board.pointer === pointer;
|
|
461
|
-
},
|
|
462
|
-
isInPointer(board, pointers) {
|
|
463
|
-
const point = board.pointer;
|
|
464
|
-
return pointers.includes(point);
|
|
465
|
-
},
|
|
466
|
-
getMovingPointInBoard(board) {
|
|
467
|
-
return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
|
|
468
|
-
},
|
|
469
|
-
isMovingPointInBoard(board) {
|
|
470
|
-
const point = BOARD_TO_MOVING_POINT.get(board);
|
|
471
|
-
const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
472
|
-
if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
|
|
473
|
-
return true;
|
|
401
|
+
}
|
|
402
|
+
get context() {
|
|
403
|
+
return this._context;
|
|
404
|
+
}
|
|
405
|
+
get element() {
|
|
406
|
+
return this.context && this.context.element;
|
|
407
|
+
}
|
|
408
|
+
get board() {
|
|
409
|
+
return this.context && this.context.board;
|
|
410
|
+
}
|
|
411
|
+
get selected() {
|
|
412
|
+
return this.context && this.context.selected;
|
|
413
|
+
}
|
|
414
|
+
get effect() {
|
|
415
|
+
return this.context && this.context.effect;
|
|
416
|
+
}
|
|
417
|
+
constructor(cdr) {
|
|
418
|
+
this.cdr = cdr;
|
|
419
|
+
this.initialized = false;
|
|
420
|
+
}
|
|
421
|
+
ngOnInit() {
|
|
422
|
+
if (this.element.type) {
|
|
423
|
+
(this.rootG || this.g).setAttribute(`plait-${this.element.type}`, 'true');
|
|
474
424
|
}
|
|
475
|
-
|
|
476
|
-
},
|
|
477
|
-
getThemeColors(board) {
|
|
478
|
-
return (board.options.themeColors || ThemeColors);
|
|
425
|
+
this.initialized = true;
|
|
479
426
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
(function (PlaitPointerType) {
|
|
484
|
-
PlaitPointerType["hand"] = "hand";
|
|
485
|
-
PlaitPointerType["selection"] = "selection";
|
|
486
|
-
})(PlaitPointerType || (PlaitPointerType = {}));
|
|
487
|
-
|
|
488
|
-
function isNullOrUndefined(value) {
|
|
489
|
-
return value === null || value === undefined;
|
|
490
|
-
}
|
|
491
|
-
/**
|
|
492
|
-
* 规范 point
|
|
493
|
-
* @param point
|
|
494
|
-
* @returns point
|
|
495
|
-
*/
|
|
496
|
-
function normalizePoint(point) {
|
|
497
|
-
return Array.isArray(point)
|
|
498
|
-
? {
|
|
499
|
-
x: point[0],
|
|
500
|
-
y: point[1]
|
|
427
|
+
ngOnDestroy() {
|
|
428
|
+
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
429
|
+
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
501
430
|
}
|
|
502
|
-
|
|
431
|
+
removeSelectedElement(this.board, this.element);
|
|
432
|
+
(this.rootG || this.g).remove();
|
|
433
|
+
}
|
|
434
|
+
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 }); }
|
|
435
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
|
|
503
436
|
}
|
|
437
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
|
|
438
|
+
type: Directive
|
|
439
|
+
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
|
|
440
|
+
type: Input
|
|
441
|
+
}] } });
|
|
442
|
+
const ELEMENT_TO_COMPONENT = new WeakMap();
|
|
504
443
|
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
*
|
|
515
|
-
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
516
|
-
* `reverse: true` option is passed, they are reversed.
|
|
517
|
-
*/
|
|
518
|
-
ancestors(path, options = {}) {
|
|
519
|
-
const { reverse = false } = options;
|
|
520
|
-
let paths = Path.levels(path, options);
|
|
521
|
-
if (reverse) {
|
|
522
|
-
paths = paths.slice(1);
|
|
444
|
+
const RectangleClient = {
|
|
445
|
+
isHit: (origin, target) => {
|
|
446
|
+
const minX = origin.x < target.x ? origin.x : target.x;
|
|
447
|
+
const maxX = origin.x + origin.width > target.x + target.width ? origin.x + origin.width : target.x + target.width;
|
|
448
|
+
const minY = origin.y < target.y ? origin.y : target.y;
|
|
449
|
+
const maxY = origin.y + origin.height > target.y + target.height ? origin.y + origin.height : target.y + target.height;
|
|
450
|
+
// float calculate error( eg: 1.4210854715202004e-14 > 0)
|
|
451
|
+
if (Math.floor(maxX - minX - origin.width - target.width) <= 0 && Math.floor(maxY - minY - origin.height - target.height) <= 0) {
|
|
452
|
+
return true;
|
|
523
453
|
}
|
|
524
454
|
else {
|
|
525
|
-
|
|
526
|
-
}
|
|
527
|
-
return paths;
|
|
528
|
-
},
|
|
529
|
-
/**
|
|
530
|
-
* Get a list of paths at every level down to a path. Note: this is the same
|
|
531
|
-
* as `Path.ancestors`, but including the path itself.
|
|
532
|
-
*
|
|
533
|
-
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
534
|
-
* true` option is passed, they are reversed.
|
|
535
|
-
*/
|
|
536
|
-
levels(path, options = {}) {
|
|
537
|
-
const { reverse = false } = options;
|
|
538
|
-
const list = [];
|
|
539
|
-
for (let i = 0; i <= path.length; i++) {
|
|
540
|
-
list.push(path.slice(0, i));
|
|
541
|
-
}
|
|
542
|
-
if (reverse) {
|
|
543
|
-
list.reverse();
|
|
544
|
-
}
|
|
545
|
-
return list;
|
|
546
|
-
},
|
|
547
|
-
parent(path) {
|
|
548
|
-
if (path.length === 0) {
|
|
549
|
-
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
550
|
-
}
|
|
551
|
-
return path.slice(0, -1);
|
|
552
|
-
},
|
|
553
|
-
next(path) {
|
|
554
|
-
if (path.length === 0) {
|
|
555
|
-
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
556
|
-
}
|
|
557
|
-
const last = path[path.length - 1];
|
|
558
|
-
return path.slice(0, -1).concat(last + 1);
|
|
559
|
-
},
|
|
560
|
-
hasPrevious(path) {
|
|
561
|
-
return path[path.length - 1] > 0;
|
|
562
|
-
},
|
|
563
|
-
previous(path) {
|
|
564
|
-
if (path.length === 0) {
|
|
565
|
-
throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
|
|
455
|
+
return false;
|
|
566
456
|
}
|
|
567
|
-
const last = path[path.length - 1];
|
|
568
|
-
return path.slice(0, -1).concat(last - 1);
|
|
569
457
|
},
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
*
|
|
580
|
-
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
581
|
-
* directly above or below the other. If you want exact matching, use
|
|
582
|
-
* [[Path.equals]] instead.
|
|
583
|
-
*/
|
|
584
|
-
compare(path, another) {
|
|
585
|
-
const min = Math.min(path.length, another.length);
|
|
586
|
-
for (let i = 0; i < min; i++) {
|
|
587
|
-
if (path[i] < another[i])
|
|
588
|
-
return -1;
|
|
589
|
-
if (path[i] > another[i])
|
|
590
|
-
return 1;
|
|
591
|
-
}
|
|
592
|
-
return 0;
|
|
458
|
+
toRectangleClient: (points) => {
|
|
459
|
+
const xArray = points.map(ele => ele[0]);
|
|
460
|
+
const yArray = points.map(ele => ele[1]);
|
|
461
|
+
const xMin = Math.min(...xArray);
|
|
462
|
+
const xMax = Math.max(...xArray);
|
|
463
|
+
const yMin = Math.min(...yArray);
|
|
464
|
+
const yMax = Math.max(...yArray);
|
|
465
|
+
const rect = { x: xMin, y: yMin, width: xMax - xMin, height: yMax - yMin };
|
|
466
|
+
return rect;
|
|
593
467
|
},
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
468
|
+
getOutlineRectangle: (rectangle, offset) => {
|
|
469
|
+
return {
|
|
470
|
+
x: rectangle.x + offset,
|
|
471
|
+
y: rectangle.y + offset,
|
|
472
|
+
width: rectangle.width - offset * 2,
|
|
473
|
+
height: rectangle.height - offset * 2
|
|
474
|
+
};
|
|
599
475
|
},
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const as = path.slice(0, i);
|
|
606
|
-
const bs = another.slice(0, i);
|
|
607
|
-
const av = path[i];
|
|
608
|
-
const bv = another[i];
|
|
609
|
-
return Path.equals(as, bs) && av < bv;
|
|
476
|
+
isEqual: (rectangle, otherRectangle) => {
|
|
477
|
+
return (rectangle.x === otherRectangle.x &&
|
|
478
|
+
rectangle.y === otherRectangle.y &&
|
|
479
|
+
rectangle.width === otherRectangle.width &&
|
|
480
|
+
rectangle.height === otherRectangle.height);
|
|
610
481
|
},
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const as = path.slice(0, -1);
|
|
619
|
-
const bs = another.slice(0, -1);
|
|
620
|
-
const al = path[path.length - 1];
|
|
621
|
-
const bl = another[another.length - 1];
|
|
622
|
-
return al !== bl && Path.equals(as, bs);
|
|
482
|
+
getCornerPoints: (rectangle) => {
|
|
483
|
+
return [
|
|
484
|
+
[rectangle.x, rectangle.y],
|
|
485
|
+
[rectangle.x + rectangle.width, rectangle.y],
|
|
486
|
+
[rectangle.x + rectangle.width, rectangle.y + rectangle.height],
|
|
487
|
+
[rectangle.x, rectangle.y + rectangle.height]
|
|
488
|
+
];
|
|
623
489
|
},
|
|
624
|
-
|
|
625
|
-
return
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
return null;
|
|
632
|
-
}
|
|
633
|
-
switch (operation.type) {
|
|
634
|
-
case 'insert_node': {
|
|
635
|
-
const { path: op } = operation;
|
|
636
|
-
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
637
|
-
p[op.length - 1] += 1;
|
|
638
|
-
}
|
|
639
|
-
break;
|
|
640
|
-
}
|
|
641
|
-
case 'remove_node': {
|
|
642
|
-
const { path: op } = operation;
|
|
643
|
-
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
else if (Path.endsBefore(op, p)) {
|
|
647
|
-
p[op.length - 1] -= 1;
|
|
648
|
-
}
|
|
649
|
-
break;
|
|
650
|
-
}
|
|
651
|
-
case 'move_node': {
|
|
652
|
-
const { path: op, newPath: onp } = operation;
|
|
653
|
-
// If the old and new path are the same, it's a no-op.
|
|
654
|
-
if (Path.equals(op, onp)) {
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
658
|
-
const copy = onp.slice();
|
|
659
|
-
// op.length <= onp.length is different for slate
|
|
660
|
-
// resolve drag from [0, 0] to [0, 3] issue
|
|
661
|
-
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
662
|
-
copy[op.length - 1] -= 1;
|
|
663
|
-
}
|
|
664
|
-
return copy.concat(p.slice(op.length));
|
|
665
|
-
}
|
|
666
|
-
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
667
|
-
if (Path.endsBefore(op, p)) {
|
|
668
|
-
p[op.length - 1] -= 1;
|
|
669
|
-
}
|
|
670
|
-
else {
|
|
671
|
-
p[op.length - 1] += 1;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
675
|
-
if (Path.endsBefore(op, p)) {
|
|
676
|
-
p[op.length - 1] -= 1;
|
|
677
|
-
}
|
|
678
|
-
p[onp.length - 1] += 1;
|
|
679
|
-
}
|
|
680
|
-
else if (Path.endsBefore(op, p)) {
|
|
681
|
-
if (Path.equals(onp, p)) {
|
|
682
|
-
p[onp.length - 1] += 1;
|
|
683
|
-
}
|
|
684
|
-
p[op.length - 1] -= 1;
|
|
685
|
-
}
|
|
686
|
-
break;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
return p;
|
|
690
|
-
});
|
|
490
|
+
getEdgeCenterPoints: (rectangle) => {
|
|
491
|
+
return [
|
|
492
|
+
[rectangle.x + rectangle.width / 2, rectangle.y],
|
|
493
|
+
[rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
|
|
494
|
+
[rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height],
|
|
495
|
+
[rectangle.x, rectangle.y + rectangle.height / 2]
|
|
496
|
+
];
|
|
691
497
|
}
|
|
692
498
|
};
|
|
693
499
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
*parents(root, path, options = {}) {
|
|
707
|
-
for (const p of Path.ancestors(path, options)) {
|
|
708
|
-
const n = PlaitNode.get(root, p);
|
|
709
|
-
yield n;
|
|
710
|
-
}
|
|
711
|
-
},
|
|
712
|
-
get(root, path) {
|
|
713
|
-
let node = root;
|
|
714
|
-
for (let i = 0; i < path.length; i++) {
|
|
715
|
-
const p = path[i];
|
|
716
|
-
if (!node || !node.children || !node.children[p]) {
|
|
717
|
-
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
718
|
-
}
|
|
719
|
-
node = node.children[p];
|
|
720
|
-
}
|
|
721
|
-
return node;
|
|
722
|
-
},
|
|
723
|
-
last(board, path) {
|
|
724
|
-
let n = PlaitNode.get(board, path);
|
|
725
|
-
while (n && n.children && n.children.length > 0) {
|
|
726
|
-
const i = n.children.length - 1;
|
|
727
|
-
n = n.children[i];
|
|
728
|
-
}
|
|
729
|
-
return n;
|
|
500
|
+
// https://stackoverflow.com/a/6853926/232122
|
|
501
|
+
function distanceBetweenPointAndSegment(x, y, x1, y1, x2, y2) {
|
|
502
|
+
const A = x - x1;
|
|
503
|
+
const B = y - y1;
|
|
504
|
+
const C = x2 - x1;
|
|
505
|
+
const D = y2 - y1;
|
|
506
|
+
const dot = A * C + B * D;
|
|
507
|
+
const lenSquare = C * C + D * D;
|
|
508
|
+
let param = -1;
|
|
509
|
+
if (lenSquare !== 0) {
|
|
510
|
+
// in case of 0 length line
|
|
511
|
+
param = dot / lenSquare;
|
|
730
512
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
case 'insert_node': {
|
|
736
|
-
const { path, node } = op;
|
|
737
|
-
const parent = PlaitNode.parent(board, path);
|
|
738
|
-
const index = path[path.length - 1];
|
|
739
|
-
if (!parent.children || index > parent.children.length) {
|
|
740
|
-
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
741
|
-
}
|
|
742
|
-
parent.children.splice(index, 0, node);
|
|
743
|
-
break;
|
|
744
|
-
}
|
|
745
|
-
case 'remove_node': {
|
|
746
|
-
const { path } = op;
|
|
747
|
-
const parent = PlaitNode.parent(board, path);
|
|
748
|
-
const index = path[path.length - 1];
|
|
749
|
-
if (!parent.children || index > parent.children.length) {
|
|
750
|
-
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
751
|
-
}
|
|
752
|
-
parent.children.splice(index, 1);
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
case 'move_node': {
|
|
756
|
-
const { path, newPath } = op;
|
|
757
|
-
if (Path.isAncestor(path, newPath)) {
|
|
758
|
-
throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
|
|
759
|
-
}
|
|
760
|
-
const node = PlaitNode.get(board, path);
|
|
761
|
-
const parent = PlaitNode.parent(board, path);
|
|
762
|
-
const index = path[path.length - 1];
|
|
763
|
-
// This is tricky, but since the `path` and `newPath` both refer to
|
|
764
|
-
// the same snapshot in time, there's a mismatch. After either
|
|
765
|
-
// removing the original position, the second step's path can be out
|
|
766
|
-
// of date. So instead of using the `op.newPath` directly, we
|
|
767
|
-
// transform `op.path` to ascertain what the `newPath` would be after
|
|
768
|
-
// the operation was applied.
|
|
769
|
-
parent.children?.splice(index, 1);
|
|
770
|
-
const truePath = Path.transform(path, op);
|
|
771
|
-
const newParent = PlaitNode.get(board, Path.parent(truePath));
|
|
772
|
-
const newIndex = truePath[truePath.length - 1];
|
|
773
|
-
newParent.children?.splice(newIndex, 0, node);
|
|
774
|
-
break;
|
|
775
|
-
}
|
|
776
|
-
case 'set_node': {
|
|
777
|
-
const { path, properties, newProperties } = op;
|
|
778
|
-
if (path.length === 0) {
|
|
779
|
-
throw new Error(`Cannot set properties on the root node!`);
|
|
780
|
-
}
|
|
781
|
-
const node = PlaitNode.get(board, path);
|
|
782
|
-
for (const key in newProperties) {
|
|
783
|
-
const value = newProperties[key];
|
|
784
|
-
if (value == null) {
|
|
785
|
-
delete node[key];
|
|
786
|
-
}
|
|
787
|
-
else {
|
|
788
|
-
node[key] = value;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
// properties that were previously defined, but are now missing, must be deleted
|
|
792
|
-
for (const key in properties) {
|
|
793
|
-
if (!newProperties.hasOwnProperty(key)) {
|
|
794
|
-
delete node[key];
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
break;
|
|
798
|
-
}
|
|
799
|
-
case 'set_viewport': {
|
|
800
|
-
const { newProperties } = op;
|
|
801
|
-
if (newProperties == null) {
|
|
802
|
-
viewport = newProperties;
|
|
803
|
-
}
|
|
804
|
-
else {
|
|
805
|
-
if (viewport == null) {
|
|
806
|
-
if (!Viewport.isViewport(newProperties)) {
|
|
807
|
-
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
808
|
-
}
|
|
809
|
-
viewport = { ...newProperties };
|
|
810
|
-
}
|
|
811
|
-
for (const key in newProperties) {
|
|
812
|
-
const value = newProperties[key];
|
|
813
|
-
if (value == null) {
|
|
814
|
-
delete viewport[key];
|
|
815
|
-
}
|
|
816
|
-
else {
|
|
817
|
-
viewport[key] = value;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
break;
|
|
822
|
-
}
|
|
823
|
-
case 'set_selection': {
|
|
824
|
-
const { newProperties } = op;
|
|
825
|
-
if (newProperties == null) {
|
|
826
|
-
selection = newProperties;
|
|
827
|
-
}
|
|
828
|
-
else {
|
|
829
|
-
if (selection === null) {
|
|
830
|
-
selection = op.newProperties;
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
selection.ranges = newProperties.ranges;
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
break;
|
|
837
|
-
}
|
|
838
|
-
case 'set_theme': {
|
|
839
|
-
const { newProperties } = op;
|
|
840
|
-
theme = newProperties;
|
|
841
|
-
break;
|
|
842
|
-
}
|
|
513
|
+
let xx, yy;
|
|
514
|
+
if (param < 0) {
|
|
515
|
+
xx = x1;
|
|
516
|
+
yy = y1;
|
|
843
517
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
518
|
+
else if (param > 1) {
|
|
519
|
+
xx = x2;
|
|
520
|
+
yy = y2;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
xx = x1 + param * C;
|
|
524
|
+
yy = y1 + param * D;
|
|
525
|
+
}
|
|
526
|
+
const dx = x - xx;
|
|
527
|
+
const dy = y - yy;
|
|
528
|
+
return Math.hypot(dx, dy);
|
|
529
|
+
}
|
|
530
|
+
function getNearestPointBetweenPointAndSegment(point, linePoints) {
|
|
531
|
+
const x = point[0], y = point[1], x1 = linePoints[0][0], y1 = linePoints[0][1], x2 = linePoints[1][0], y2 = linePoints[1][1];
|
|
532
|
+
const A = x - x1;
|
|
533
|
+
const B = y - y1;
|
|
534
|
+
const C = x2 - x1;
|
|
535
|
+
const D = y2 - y1;
|
|
536
|
+
const dot = A * C + B * D;
|
|
537
|
+
const lenSquare = C * C + D * D;
|
|
538
|
+
let param = -1;
|
|
539
|
+
if (lenSquare !== 0) {
|
|
540
|
+
// in case of 0 length line
|
|
541
|
+
param = dot / lenSquare;
|
|
542
|
+
}
|
|
543
|
+
let xx, yy;
|
|
544
|
+
if (param < 0) {
|
|
545
|
+
xx = x1;
|
|
546
|
+
yy = y1;
|
|
547
|
+
}
|
|
548
|
+
else if (param > 1) {
|
|
549
|
+
xx = x2;
|
|
550
|
+
yy = y2;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
xx = x1 + param * C;
|
|
554
|
+
yy = y1 + param * D;
|
|
555
|
+
}
|
|
556
|
+
return [xx, yy];
|
|
557
|
+
}
|
|
558
|
+
function distanceBetweenPointAndSegments(points, point) {
|
|
559
|
+
const len = points.length;
|
|
560
|
+
let distance = Infinity;
|
|
561
|
+
for (let i = 0; i < len - 1; i++) {
|
|
562
|
+
const p = points[i];
|
|
563
|
+
const p2 = points[i + 1];
|
|
564
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
565
|
+
if (currentDistance < distance) {
|
|
566
|
+
distance = currentDistance;
|
|
871
567
|
}
|
|
872
568
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
function insertNode(board, node, path) {
|
|
876
|
-
const operation = { type: 'insert_node', node, path };
|
|
877
|
-
board.apply(operation);
|
|
569
|
+
return distance;
|
|
878
570
|
}
|
|
879
|
-
function
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
for (
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
571
|
+
function getNearestPointBetweenPointAndSegments(point, points) {
|
|
572
|
+
const len = points.length;
|
|
573
|
+
let distance = Infinity;
|
|
574
|
+
let result = point;
|
|
575
|
+
for (let i = 0; i < len; i++) {
|
|
576
|
+
const p = points[i];
|
|
577
|
+
const p2 = i === len - 1 ? points[0] : points[i + 1];
|
|
578
|
+
const currentDistance = distanceBetweenPointAndSegment(point[0], point[1], p[0], p[1], p2[0], p2[1]);
|
|
579
|
+
if (currentDistance < distance) {
|
|
580
|
+
distance = currentDistance;
|
|
581
|
+
result = getNearestPointBetweenPointAndSegment(point, [p, p2]);
|
|
890
582
|
}
|
|
891
583
|
}
|
|
892
|
-
|
|
893
|
-
board.apply(operation);
|
|
584
|
+
return result;
|
|
894
585
|
}
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
586
|
+
function rotate(x1, y1, x2, y2, angle) {
|
|
587
|
+
// 𝑎′𝑥=(𝑎𝑥−𝑐𝑥)cos𝜃−(𝑎𝑦−𝑐𝑦)sin𝜃+𝑐𝑥
|
|
588
|
+
// 𝑎′𝑦=(𝑎𝑥−𝑐𝑥)sin𝜃+(𝑎𝑦−𝑐𝑦)cos𝜃+𝑐𝑦.
|
|
589
|
+
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
|
|
590
|
+
return [(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2, (x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2];
|
|
899
591
|
}
|
|
900
|
-
function
|
|
901
|
-
const
|
|
902
|
-
|
|
592
|
+
function distanceBetweenPointAndPoint(x1, y1, x2, y2) {
|
|
593
|
+
const dx = x1 - x2;
|
|
594
|
+
const dy = y1 - y2;
|
|
595
|
+
return Math.hypot(dx, dy);
|
|
903
596
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
};
|
|
910
|
-
|
|
911
|
-
function setSelection(board, selection) {
|
|
912
|
-
const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
|
|
913
|
-
board.apply(operation);
|
|
597
|
+
// https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
|
|
598
|
+
function distanceBetweenPointAndRectangle(x, y, rect) {
|
|
599
|
+
var dx = Math.max(rect.x - x, 0, x - (rect.x + rect.width));
|
|
600
|
+
var dy = Math.max(rect.y - y, 0, y - (rect.y + rect.height));
|
|
601
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
914
602
|
}
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
603
|
+
const isLineHitLine = (a, b, c, d) => {
|
|
604
|
+
const crossProduct = (v1, v2) => v1[0] * v2[1] - v1[1] * v2[0];
|
|
605
|
+
const ab = [b[0] - a[0], b[1] - a[1]];
|
|
606
|
+
const ac = [c[0] - a[0], c[1] - a[1]];
|
|
607
|
+
const ad = [d[0] - a[0], d[1] - a[1]];
|
|
608
|
+
const ca = [a[0] - c[0], a[1] - c[1]];
|
|
609
|
+
const cb = [b[0] - c[0], b[1] - c[1]];
|
|
610
|
+
const cd = [d[0] - c[0], d[1] - c[1]];
|
|
611
|
+
return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
|
|
918
612
|
};
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
613
|
+
const isPolylineHitRectangle = (points, rectangle) => {
|
|
614
|
+
const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
|
|
615
|
+
for (let i = 1; i < points.length; i++) {
|
|
616
|
+
const isIntersect = isLineHitLine(points[i], points[i - 1], rectanglePoints[0], rectanglePoints[1]) ||
|
|
617
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[1], rectanglePoints[2]) ||
|
|
618
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[2], rectanglePoints[3]) ||
|
|
619
|
+
isLineHitLine(points[i], points[i - 1], rectanglePoints[3], rectanglePoints[0]);
|
|
620
|
+
if (isIntersect) {
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return false;
|
|
625
|
+
};
|
|
626
|
+
//https://stackoverflow.com/questions/22521982/check-if-point-is-inside-a-polygon
|
|
627
|
+
const isPointInPolygon = (point, points) => {
|
|
628
|
+
// ray-casting algorithm based on
|
|
629
|
+
// https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
|
|
630
|
+
const x = point[0], y = point[1];
|
|
631
|
+
let inside = false;
|
|
632
|
+
for (var i = 0, j = points.length - 1; i < points.length; j = i++) {
|
|
633
|
+
let xi = points[i][0], yi = points[i][1];
|
|
634
|
+
let xj = points[j][0], yj = points[j][1];
|
|
635
|
+
let intersect = yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
|
|
636
|
+
if (intersect)
|
|
637
|
+
inside = !inside;
|
|
638
|
+
}
|
|
639
|
+
return inside;
|
|
640
|
+
};
|
|
641
|
+
const isPointInEllipse = (point, center, rx, ry, rotation = 0) => {
|
|
642
|
+
const cosAngle = Math.cos(rotation);
|
|
643
|
+
const sinAngle = Math.sin(rotation);
|
|
644
|
+
const x1 = (point[0] - center[0]) * cosAngle + (point[1] - center[1]) * sinAngle;
|
|
645
|
+
const y1 = (point[1] - center[1]) * cosAngle - (point[0] - center[0]) * sinAngle;
|
|
646
|
+
return (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry) <= 1;
|
|
647
|
+
};
|
|
648
|
+
const isPointInRoundRectangle = (point, rectangle, radius) => {
|
|
649
|
+
const { x: rectX, y: rectY, width, height } = rectangle;
|
|
650
|
+
const isInRectangle = point[0] >= rectX && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + height;
|
|
651
|
+
const handleLeftTop = point[0] >= rectX &&
|
|
652
|
+
point[0] <= rectX + radius &&
|
|
653
|
+
point[1] >= rectY &&
|
|
654
|
+
point[1] <= rectY + radius &&
|
|
655
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + radius)) > radius;
|
|
656
|
+
const handleLeftBottom = point[0] >= rectX &&
|
|
657
|
+
point[0] <= rectX + radius &&
|
|
658
|
+
point[1] >= rectY + height &&
|
|
659
|
+
point[1] <= rectY + height - radius &&
|
|
660
|
+
Math.hypot(point[0] - (rectX + radius), point[1] - (rectY + height - radius)) > radius;
|
|
661
|
+
const handleRightTop = point[0] >= rectX + width - radius &&
|
|
662
|
+
point[0] <= rectX + width &&
|
|
663
|
+
point[1] >= rectY &&
|
|
664
|
+
point[1] <= rectY + radius &&
|
|
665
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + radius)) > radius;
|
|
666
|
+
const handleRightBottom = point[0] >= rectX + width - radius &&
|
|
667
|
+
point[0] <= rectX + width &&
|
|
668
|
+
point[1] >= rectY + height - radius &&
|
|
669
|
+
point[1] <= rectY + height &&
|
|
670
|
+
Math.hypot(point[0] - (rectX + width - radius), point[1] - (rectY + height - radius)) > radius;
|
|
671
|
+
const isInCorner = handleLeftTop || handleLeftBottom || handleRightTop || handleRightBottom;
|
|
672
|
+
return isInRectangle && !isInCorner;
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
function transformPoints(board, points) {
|
|
676
|
+
const newPoints = points.map(point => {
|
|
677
|
+
return transformPoint(board, point);
|
|
923
678
|
});
|
|
679
|
+
return newPoints;
|
|
680
|
+
}
|
|
681
|
+
function transformPoint(board, point) {
|
|
682
|
+
const { width, height } = PlaitBoard.getHost(board).getBoundingClientRect();
|
|
683
|
+
const viewBox = PlaitBoard.getHost(board).viewBox.baseVal;
|
|
684
|
+
const x = (point[0] / width) * viewBox.width + viewBox.x;
|
|
685
|
+
const y = (point[1] / height) * viewBox.height + viewBox.y;
|
|
686
|
+
const newPoint = [x, y];
|
|
687
|
+
return newPoint;
|
|
688
|
+
}
|
|
689
|
+
function isInPlaitBoard(board, x, y) {
|
|
690
|
+
const plaitBoardElement = PlaitBoard.getBoardContainer(board);
|
|
691
|
+
const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
|
|
692
|
+
const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
|
|
693
|
+
return distances === 0;
|
|
694
|
+
}
|
|
695
|
+
function getRealScrollBarWidth(board) {
|
|
696
|
+
const { hideScrollbar } = board.options;
|
|
697
|
+
let scrollBarWidth = 0;
|
|
698
|
+
if (!hideScrollbar) {
|
|
699
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
700
|
+
scrollBarWidth = viewportContainer.offsetWidth - viewportContainer.clientWidth;
|
|
701
|
+
}
|
|
702
|
+
return scrollBarWidth;
|
|
924
703
|
}
|
|
925
704
|
|
|
926
|
-
function
|
|
927
|
-
|
|
928
|
-
|
|
705
|
+
function createForeignObject(x, y, width, height) {
|
|
706
|
+
var newForeignObject = document.createElementNS(NS, 'foreignObject');
|
|
707
|
+
newForeignObject.setAttribute('x', `${x}`);
|
|
708
|
+
newForeignObject.setAttribute('y', `${y}`);
|
|
709
|
+
newForeignObject.setAttribute('width', `${width}`);
|
|
710
|
+
newForeignObject.setAttribute('height', `${height}`);
|
|
711
|
+
return newForeignObject;
|
|
929
712
|
}
|
|
930
|
-
|
|
931
|
-
|
|
713
|
+
function updateForeignObject(target, width, height, x, y) {
|
|
714
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
715
|
+
if (foreignObject) {
|
|
716
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
717
|
+
foreignObject.setAttribute('height', `${height}`);
|
|
718
|
+
foreignObject.setAttribute('x', `${x}`);
|
|
719
|
+
foreignObject.setAttribute('y', `${y}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function updateForeignObjectWidth(target, width) {
|
|
723
|
+
const foreignObject = target instanceof SVGForeignObjectElement ? target : target.querySelector('foreignObject');
|
|
724
|
+
if (foreignObject) {
|
|
725
|
+
foreignObject.setAttribute('width', `${width}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const IS_MAC = typeof window != 'undefined' && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
|
730
|
+
|
|
731
|
+
const IS_IOS = typeof navigator !== 'undefined' &&
|
|
732
|
+
typeof window !== 'undefined' &&
|
|
733
|
+
/iPad|iPhone|iPod/.test(navigator.userAgent) &&
|
|
734
|
+
!window.MSStream;
|
|
735
|
+
const IS_APPLE = typeof navigator !== 'undefined' && /Mac OS X/.test(navigator.userAgent);
|
|
736
|
+
const IS_FIREFOX = typeof navigator !== 'undefined' && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
|
|
737
|
+
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
738
|
+
// "modern" Edge was released at 79.x
|
|
739
|
+
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
740
|
+
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
741
|
+
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
742
|
+
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
743
|
+
|
|
744
|
+
function isNullOrUndefined(value) {
|
|
745
|
+
return value === null || value === undefined;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 规范 point
|
|
749
|
+
* @param point
|
|
750
|
+
* @returns point
|
|
751
|
+
*/
|
|
752
|
+
function normalizePoint(point) {
|
|
753
|
+
return Array.isArray(point)
|
|
754
|
+
? {
|
|
755
|
+
x: point[0],
|
|
756
|
+
y: point[1]
|
|
757
|
+
}
|
|
758
|
+
: point;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Check whether to merge an operation into the previous operation.
|
|
763
|
+
*/
|
|
764
|
+
const shouldMerge = (op, prev) => {
|
|
765
|
+
if (op.type === 'set_viewport' && op.type === prev?.type) {
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
return false;
|
|
769
|
+
};
|
|
770
|
+
/**
|
|
771
|
+
* Check whether an operation needs to be saved to the history.
|
|
772
|
+
*/
|
|
773
|
+
const shouldSave = (op, prev) => {
|
|
774
|
+
if (op.type === 'set_selection' || op.type === 'set_viewport') {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
return true;
|
|
778
|
+
};
|
|
779
|
+
/**
|
|
780
|
+
* Check whether an operation should clear the redos stack.
|
|
781
|
+
*/
|
|
782
|
+
const shouldClear = (op) => {
|
|
783
|
+
if (op.type === 'set_selection') {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
return true;
|
|
787
|
+
};
|
|
788
|
+
const PlaitHistoryBoard = {
|
|
789
|
+
/**
|
|
790
|
+
* Get the saving flag's current value.
|
|
791
|
+
*/
|
|
792
|
+
isSaving(board) {
|
|
793
|
+
return SAVING.get(board);
|
|
794
|
+
},
|
|
795
|
+
/**
|
|
796
|
+
* Get the merge flag's current value.
|
|
797
|
+
*/
|
|
798
|
+
isMerging(board) {
|
|
799
|
+
return MERGING.get(board);
|
|
800
|
+
},
|
|
801
|
+
/**
|
|
802
|
+
* Apply a series of changes inside a synchronous `fn`, without merging any of
|
|
803
|
+
* the new operations into previous save point in the history.
|
|
804
|
+
*/
|
|
805
|
+
withoutMerging(board, fn) {
|
|
806
|
+
const prev = PlaitHistoryBoard.isMerging(board);
|
|
807
|
+
MERGING.set(board, false);
|
|
808
|
+
fn();
|
|
809
|
+
MERGING.set(board, prev);
|
|
810
|
+
},
|
|
811
|
+
/**
|
|
812
|
+
* Apply a series of changes inside a synchronous `fn`, without saving any of
|
|
813
|
+
* their operations into the history.
|
|
814
|
+
*/
|
|
815
|
+
withoutSaving(board, fn) {
|
|
816
|
+
const prev = PlaitHistoryBoard.isSaving(board);
|
|
817
|
+
SAVING.set(board, false);
|
|
818
|
+
fn();
|
|
819
|
+
SAVING.set(board, prev);
|
|
820
|
+
}
|
|
932
821
|
};
|
|
933
822
|
|
|
934
823
|
/**
|
|
935
|
-
*
|
|
936
|
-
* Copyright Google LLC All Rights Reserved.
|
|
937
|
-
*
|
|
938
|
-
* Use of this source code is governed by an MIT-style license that can be
|
|
939
|
-
* found in the LICENSE file at https://angular.io/license
|
|
824
|
+
* Hotkey mappings for each platform.
|
|
940
825
|
*/
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
const
|
|
983
|
-
const
|
|
984
|
-
const
|
|
985
|
-
const
|
|
986
|
-
const
|
|
987
|
-
const
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const F9 = 120;
|
|
1033
|
-
const F10 = 121;
|
|
1034
|
-
const F11 = 122;
|
|
1035
|
-
const F12 = 123;
|
|
1036
|
-
const NUM_LOCK = 144;
|
|
1037
|
-
const SCROLL_LOCK = 145;
|
|
1038
|
-
const FIRST_MEDIA = 166;
|
|
1039
|
-
const FF_MINUS = 173;
|
|
1040
|
-
const MUTE = 173; // Firefox (Gecko) fires 181 for MUTE
|
|
1041
|
-
const VOLUME_DOWN = 174; // Firefox (Gecko) fires 182 for VOLUME_DOWN
|
|
1042
|
-
const VOLUME_UP = 175; // Firefox (Gecko) fires 183 for VOLUME_UP
|
|
1043
|
-
const FF_MUTE = 181;
|
|
1044
|
-
const FF_VOLUME_DOWN = 182;
|
|
1045
|
-
const LAST_MEDIA = 183;
|
|
1046
|
-
const FF_VOLUME_UP = 183;
|
|
1047
|
-
const SEMICOLON = 186; // Firefox (Gecko) fires 59 for SEMICOLON
|
|
1048
|
-
const EQUALS = 187; // Firefox (Gecko) fires 61 for EQUALS
|
|
1049
|
-
const COMMA = 188;
|
|
1050
|
-
const DASH = 189; // Firefox (Gecko) fires 173 for DASH/MINUS
|
|
1051
|
-
const PERIOD = 190;
|
|
1052
|
-
const SLASH = 191;
|
|
1053
|
-
const APOSTROPHE = 192;
|
|
1054
|
-
const TILDE = 192;
|
|
1055
|
-
const OPEN_SQUARE_BRACKET = 219;
|
|
1056
|
-
const BACKSLASH = 220;
|
|
1057
|
-
const CLOSE_SQUARE_BRACKET = 221;
|
|
1058
|
-
const SINGLE_QUOTE = 222;
|
|
1059
|
-
const MAC_META = 224;
|
|
1060
|
-
|
|
1061
|
-
var ResizeCursorClass;
|
|
1062
|
-
(function (ResizeCursorClass) {
|
|
1063
|
-
ResizeCursorClass["ew-resize"] = "ew-resize";
|
|
1064
|
-
})(ResizeCursorClass || (ResizeCursorClass = {}));
|
|
826
|
+
const HOTKEYS = {
|
|
827
|
+
bold: 'mod+b',
|
|
828
|
+
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
829
|
+
moveBackward: 'left',
|
|
830
|
+
moveForward: 'right',
|
|
831
|
+
moveUp: 'up',
|
|
832
|
+
moveDown: 'down',
|
|
833
|
+
moveWordBackward: 'ctrl+left',
|
|
834
|
+
moveWordForward: 'ctrl+right',
|
|
835
|
+
deleteBackward: 'shift?+backspace',
|
|
836
|
+
deleteForward: 'shift?+delete',
|
|
837
|
+
extendBackward: 'shift+left',
|
|
838
|
+
extendForward: 'shift+right',
|
|
839
|
+
italic: 'mod+i',
|
|
840
|
+
splitBlock: 'shift?+enter',
|
|
841
|
+
undo: 'mod+z'
|
|
842
|
+
};
|
|
843
|
+
const APPLE_HOTKEYS = {
|
|
844
|
+
moveLineBackward: 'opt+up',
|
|
845
|
+
moveLineForward: 'opt+down',
|
|
846
|
+
moveWordBackward: 'opt+left',
|
|
847
|
+
moveWordForward: 'opt+right',
|
|
848
|
+
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
849
|
+
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
850
|
+
deleteLineBackward: 'cmd+shift?+backspace',
|
|
851
|
+
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
852
|
+
deleteWordBackward: 'opt+shift?+backspace',
|
|
853
|
+
deleteWordForward: 'opt+shift?+delete',
|
|
854
|
+
extendLineBackward: 'opt+shift+up',
|
|
855
|
+
extendLineForward: 'opt+shift+down',
|
|
856
|
+
redo: 'cmd+shift+z',
|
|
857
|
+
transposeCharacter: 'ctrl+t'
|
|
858
|
+
};
|
|
859
|
+
const WINDOWS_HOTKEYS = {
|
|
860
|
+
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
861
|
+
deleteWordForward: 'ctrl+shift?+delete',
|
|
862
|
+
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
863
|
+
};
|
|
864
|
+
/**
|
|
865
|
+
* Create a platform-aware hotkey checker.
|
|
866
|
+
*/
|
|
867
|
+
const create = (key) => {
|
|
868
|
+
const generic = HOTKEYS[key];
|
|
869
|
+
const apple = APPLE_HOTKEYS[key];
|
|
870
|
+
const windows = WINDOWS_HOTKEYS[key];
|
|
871
|
+
const isGeneric = generic && isKeyHotkey(generic);
|
|
872
|
+
const isApple = apple && isKeyHotkey(apple);
|
|
873
|
+
const isWindows = windows && isKeyHotkey(windows);
|
|
874
|
+
return (event) => {
|
|
875
|
+
if (isGeneric && isGeneric(event)) {
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
if (IS_APPLE && isApple && isApple(event)) {
|
|
879
|
+
return true;
|
|
880
|
+
}
|
|
881
|
+
if (!IS_APPLE && isWindows && isWindows(event)) {
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
return false;
|
|
885
|
+
};
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Hotkeys.
|
|
889
|
+
*/
|
|
890
|
+
const hotkeys = {
|
|
891
|
+
isBold: create('bold'),
|
|
892
|
+
isCompose: create('compose'),
|
|
893
|
+
isMoveBackward: create('moveBackward'),
|
|
894
|
+
isMoveForward: create('moveForward'),
|
|
895
|
+
isMoveUp: create('moveUp'),
|
|
896
|
+
isMoveDown: create('moveDown'),
|
|
897
|
+
isDeleteBackward: create('deleteBackward'),
|
|
898
|
+
isDeleteForward: create('deleteForward'),
|
|
899
|
+
isDeleteLineBackward: create('deleteLineBackward'),
|
|
900
|
+
isDeleteLineForward: create('deleteLineForward'),
|
|
901
|
+
isDeleteWordBackward: create('deleteWordBackward'),
|
|
902
|
+
isDeleteWordForward: create('deleteWordForward'),
|
|
903
|
+
isExtendBackward: create('extendBackward'),
|
|
904
|
+
isExtendForward: create('extendForward'),
|
|
905
|
+
isExtendLineBackward: create('extendLineBackward'),
|
|
906
|
+
isExtendLineForward: create('extendLineForward'),
|
|
907
|
+
isItalic: create('italic'),
|
|
908
|
+
isMoveLineBackward: create('moveLineBackward'),
|
|
909
|
+
isMoveLineForward: create('moveLineForward'),
|
|
910
|
+
isMoveWordBackward: create('moveWordBackward'),
|
|
911
|
+
isMoveWordForward: create('moveWordForward'),
|
|
912
|
+
isRedo: create('redo'),
|
|
913
|
+
isSplitBlock: create('splitBlock'),
|
|
914
|
+
isTransposeCharacter: create('transposeCharacter'),
|
|
915
|
+
isUndo: create('undo')
|
|
916
|
+
};
|
|
1065
917
|
|
|
1066
|
-
|
|
918
|
+
function idCreator(length = 5) {
|
|
919
|
+
// remove numeral
|
|
920
|
+
const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
|
921
|
+
const maxPosition = $chars.length;
|
|
922
|
+
let key = '';
|
|
923
|
+
for (let i = 0; i < length; i++) {
|
|
924
|
+
key += $chars.charAt(Math.floor(Math.random() * maxPosition));
|
|
925
|
+
}
|
|
926
|
+
return key;
|
|
927
|
+
}
|
|
1067
928
|
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
929
|
+
/**
|
|
930
|
+
* drawRoundRectangle
|
|
931
|
+
*/
|
|
932
|
+
function drawRoundRectangle(rs, x1, y1, x2, y2, options, outline = false, borderRadius) {
|
|
933
|
+
const width = Math.abs(x1 - x2);
|
|
934
|
+
const height = Math.abs(y1 - y2);
|
|
935
|
+
let radius = borderRadius || 0;
|
|
936
|
+
if (radius === 0) {
|
|
937
|
+
const defaultRadius = Math.min(width, height) / 8;
|
|
938
|
+
let radius = defaultRadius;
|
|
939
|
+
if (defaultRadius > MAX_RADIUS) {
|
|
940
|
+
radius = outline ? MAX_RADIUS + 2 : MAX_RADIUS;
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const point1 = [x1 + radius, y1];
|
|
944
|
+
const point2 = [x2 - radius, y1];
|
|
945
|
+
const point3 = [x2, y1 + radius];
|
|
946
|
+
const point4 = [x2, y2 - radius];
|
|
947
|
+
const point5 = [x2 - radius, y2];
|
|
948
|
+
const point6 = [x1 + radius, y2];
|
|
949
|
+
const point7 = [x1, y2 - radius];
|
|
950
|
+
const point8 = [x1, y1 + radius];
|
|
951
|
+
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);
|
|
952
|
+
}
|
|
953
|
+
const drawRectangle = (board, rectangle, options) => {
|
|
954
|
+
const roughSVG = PlaitBoard.getRoughSVG(board);
|
|
955
|
+
const rectangleG = roughSVG.rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height, options);
|
|
956
|
+
setStrokeLinecap(rectangleG, 'round');
|
|
957
|
+
return rectangleG;
|
|
1077
958
|
};
|
|
1078
|
-
const PRESS_AND_MOVE_BUFFER = 5;
|
|
1079
959
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
const
|
|
1083
|
-
|
|
960
|
+
function arrowPoints(start, end, maxHypotenuseLength = 10, degree = 40) {
|
|
961
|
+
const width = Math.abs(start[0] - end[0]);
|
|
962
|
+
const height = Math.abs(start[1] - end[1]);
|
|
963
|
+
let hypotenuse = Math.hypot(width, height); // 斜边
|
|
964
|
+
const realRotateLine = hypotenuse > maxHypotenuseLength * 2 ? maxHypotenuseLength : hypotenuse / 2;
|
|
965
|
+
const rotateWidth = (realRotateLine / hypotenuse) * width;
|
|
966
|
+
const rotateHeight = (realRotateLine / hypotenuse) * height;
|
|
967
|
+
const rotatePoint = [
|
|
968
|
+
end[0] > start[0] ? end[0] - rotateWidth : end[0] + rotateWidth,
|
|
969
|
+
end[1] > start[1] ? end[1] - rotateHeight : end[1] + rotateHeight
|
|
970
|
+
];
|
|
971
|
+
const pointRight = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (degree * Math.PI) / 180);
|
|
972
|
+
const pointLeft = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (-degree * Math.PI) / 180);
|
|
973
|
+
return { pointLeft, pointRight };
|
|
1084
974
|
}
|
|
1085
|
-
function
|
|
1086
|
-
const
|
|
1087
|
-
|
|
975
|
+
function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 40) {
|
|
976
|
+
const { pointLeft, pointRight } = arrowPoints(start, end, maxHypotenuseLength, degree);
|
|
977
|
+
const arrowLineLeft = rs.linearPath([pointLeft, end], options);
|
|
978
|
+
const arrowLineRight = rs.linearPath([pointRight, end], options);
|
|
979
|
+
return [arrowLineLeft, arrowLineRight];
|
|
1088
980
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
return
|
|
981
|
+
|
|
982
|
+
function drawCircle(roughSVG, point, diameter, options) {
|
|
983
|
+
return roughSVG.circle(point[0], point[1], diameter, options);
|
|
1092
984
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
985
|
+
|
|
986
|
+
function drawLine(rs, start, end, options) {
|
|
987
|
+
return rs.linearPath([start, end], options);
|
|
988
|
+
}
|
|
989
|
+
function drawLinearPath(points, options) {
|
|
990
|
+
const g = createG();
|
|
991
|
+
const path = createPath();
|
|
992
|
+
let polylinePath = '';
|
|
993
|
+
points.forEach((point, index) => {
|
|
994
|
+
if (index === 0) {
|
|
995
|
+
polylinePath += `M ${point[0]} ${point[1]} `;
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
polylinePath += `L ${point[0]} ${point[1]} `;
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
path.setAttribute('d', polylinePath);
|
|
1002
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1003
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1004
|
+
path.setAttribute('fill', `none`);
|
|
1005
|
+
g.appendChild(path);
|
|
1006
|
+
return g;
|
|
1007
|
+
}
|
|
1008
|
+
function drawBezierPath(points, options) {
|
|
1009
|
+
const g = createG();
|
|
1010
|
+
const path = createPath();
|
|
1011
|
+
let polylinePath = '';
|
|
1012
|
+
for (let i = 0; i < points.length - 3; i += 3) {
|
|
1013
|
+
if (i === 0) {
|
|
1014
|
+
polylinePath += `M ${points[0][0]} ${points[0][1]} `;
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
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]}`;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
path.setAttribute('d', polylinePath);
|
|
1021
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1022
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1023
|
+
path.setAttribute('fill', `none`);
|
|
1024
|
+
g.appendChild(path);
|
|
1025
|
+
return g;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
const IS_FROM_SCROLLING = new WeakMap();
|
|
1029
|
+
const IS_FROM_VIEWPORT_CHANGE = new WeakMap();
|
|
1030
|
+
function getViewportContainerRect(board) {
|
|
1031
|
+
const { hideScrollbar } = board.options;
|
|
1032
|
+
const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
|
|
1033
|
+
const viewportRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1034
|
+
return {
|
|
1035
|
+
width: viewportRect.width + scrollBarWidth,
|
|
1036
|
+
height: viewportRect.height + scrollBarWidth
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function getElementHostBBox(board, zoom) {
|
|
1040
|
+
const childrenRect = getRectangleByElements(board, board.children, true);
|
|
1041
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1042
|
+
const containerWidth = viewportContainerRect.width / zoom;
|
|
1043
|
+
const containerHeight = viewportContainerRect.height / zoom;
|
|
1044
|
+
let left;
|
|
1045
|
+
let right;
|
|
1046
|
+
let top;
|
|
1047
|
+
let bottom;
|
|
1048
|
+
if (childrenRect.width < containerWidth) {
|
|
1049
|
+
const centerX = childrenRect.x + childrenRect.width / 2;
|
|
1050
|
+
const halfContainerWidth = containerWidth / 2;
|
|
1051
|
+
left = centerX - halfContainerWidth;
|
|
1052
|
+
right = centerX + halfContainerWidth;
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
left = childrenRect.x;
|
|
1056
|
+
right = childrenRect.x + childrenRect.width;
|
|
1057
|
+
}
|
|
1058
|
+
if (childrenRect.height < containerHeight) {
|
|
1059
|
+
const centerY = childrenRect.y + childrenRect.height / 2;
|
|
1060
|
+
const halfContainerHeight = containerHeight / 2;
|
|
1061
|
+
top = centerY - halfContainerHeight;
|
|
1062
|
+
bottom = centerY + halfContainerHeight;
|
|
1063
|
+
}
|
|
1064
|
+
else {
|
|
1065
|
+
top = childrenRect.y;
|
|
1066
|
+
bottom = childrenRect.y + childrenRect.height;
|
|
1067
|
+
}
|
|
1068
|
+
return {
|
|
1069
|
+
left,
|
|
1070
|
+
right,
|
|
1071
|
+
top,
|
|
1072
|
+
bottom
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
* 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
|
|
1077
|
+
* @param zoom 缩放比
|
|
1078
|
+
* @param minZoom 最小缩放比
|
|
1079
|
+
* @param maxZoom 最大缩放比
|
|
1080
|
+
* @returns 正确的缩放比
|
|
1081
|
+
*/
|
|
1082
|
+
function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
|
|
1083
|
+
return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
|
|
1084
|
+
}
|
|
1085
|
+
function getViewBox(board, zoom) {
|
|
1086
|
+
const boardContainerRectangle = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1087
|
+
const elementHostBBox = getElementHostBBox(board, zoom);
|
|
1088
|
+
const horizontalPadding = boardContainerRectangle.width / 2;
|
|
1089
|
+
const verticalPadding = boardContainerRectangle.height / 2;
|
|
1090
|
+
const viewBox = [
|
|
1091
|
+
elementHostBBox.left - horizontalPadding / zoom,
|
|
1092
|
+
elementHostBBox.top - verticalPadding / zoom,
|
|
1093
|
+
elementHostBBox.right - elementHostBBox.left + (horizontalPadding * 2) / zoom,
|
|
1094
|
+
elementHostBBox.bottom - elementHostBBox.top + (verticalPadding * 2) / zoom
|
|
1095
|
+
];
|
|
1096
|
+
return viewBox;
|
|
1097
|
+
}
|
|
1098
|
+
function getViewBoxCenterPoint(board) {
|
|
1099
|
+
const childrenRectangle = getRectangleByElements(board, board.children, true);
|
|
1100
|
+
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
1101
|
+
}
|
|
1102
|
+
function setSVGViewBox(board, viewBox) {
|
|
1103
|
+
const zoom = board.viewport.zoom;
|
|
1104
|
+
const hostElement = PlaitBoard.getHost(board);
|
|
1105
|
+
hostElement.style.display = 'block';
|
|
1106
|
+
hostElement.style.width = `${viewBox[2] * zoom}px`;
|
|
1107
|
+
hostElement.style.height = `${viewBox[3] * zoom}px`;
|
|
1108
|
+
if (viewBox && viewBox[2] > 0 && viewBox[3] > 0) {
|
|
1109
|
+
hostElement.setAttribute('viewBox', viewBox.join(' '));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function updateViewportOffset(board) {
|
|
1113
|
+
const origination = getViewportOrigination(board);
|
|
1114
|
+
if (!origination)
|
|
1115
|
+
return;
|
|
1116
|
+
const { zoom } = board.viewport;
|
|
1117
|
+
const viewBox = getViewBox(board, zoom);
|
|
1118
|
+
const scrollLeft = (origination[0] - viewBox[0]) * zoom;
|
|
1119
|
+
const scrollTop = (origination[1] - viewBox[1]) * zoom;
|
|
1120
|
+
updateViewportContainerScroll(board, scrollLeft, scrollTop);
|
|
1121
|
+
}
|
|
1122
|
+
function updateViewportContainerScroll(board, left, top, isFromViewportChange = true) {
|
|
1123
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1124
|
+
if (viewportContainer.scrollLeft !== left || viewportContainer.scrollTop !== top) {
|
|
1125
|
+
viewportContainer.scrollLeft = left;
|
|
1126
|
+
viewportContainer.scrollTop = top;
|
|
1127
|
+
isFromViewportChange && setIsFromViewportChange(board, true);
|
|
1102
1128
|
}
|
|
1103
|
-
return rect;
|
|
1104
1129
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
path.setAttribute('stroke-linecap', value);
|
|
1111
|
-
});
|
|
1112
|
-
};
|
|
1113
|
-
function createMask() {
|
|
1114
|
-
return document.createElementNS(NS, 'mask');
|
|
1130
|
+
function initializeViewportContainer(board) {
|
|
1131
|
+
const { width, height } = getViewportContainerRect(board);
|
|
1132
|
+
const viewportContainer = PlaitBoard.getViewportContainer(board);
|
|
1133
|
+
viewportContainer.style.width = `${width}px`;
|
|
1134
|
+
viewportContainer.style.height = `${height}px`;
|
|
1115
1135
|
}
|
|
1116
|
-
function
|
|
1117
|
-
const
|
|
1118
|
-
|
|
1136
|
+
function initializeViewBox(board) {
|
|
1137
|
+
const zoom = board.viewport.zoom;
|
|
1138
|
+
const viewBox = getViewBox(board, zoom);
|
|
1139
|
+
setSVGViewBox(board, viewBox);
|
|
1119
1140
|
}
|
|
1120
|
-
function
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1141
|
+
function initializeViewportOffset(board) {
|
|
1142
|
+
if (!board.viewport?.origination) {
|
|
1143
|
+
const zoom = board.viewport.zoom;
|
|
1144
|
+
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1145
|
+
const viewBox = getViewBox(board, zoom);
|
|
1146
|
+
const centerX = viewBox[0] + viewBox[2] / 2;
|
|
1147
|
+
const centerY = viewBox[1] + viewBox[3] / 2;
|
|
1148
|
+
const origination = [centerX - viewportContainerRect.width / 2 / zoom, centerY - viewportContainerRect.height / 2 / zoom];
|
|
1149
|
+
updateViewportOrigination(board, origination);
|
|
1150
|
+
updateViewportOffset(board);
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
updateViewportOffset(board);
|
|
1127
1154
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
*/
|
|
1131
|
-
const isDOMElement = (value) => {
|
|
1132
|
-
return isDOMNode(value) && value.nodeType === 1;
|
|
1155
|
+
const updateViewportOrigination = (board, origination) => {
|
|
1156
|
+
BOARD_TO_VIEWPORT_ORIGINATION.set(board, origination);
|
|
1133
1157
|
};
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
*/
|
|
1137
|
-
const isDOMNode = (value) => {
|
|
1138
|
-
return value instanceof window.Node;
|
|
1158
|
+
const clearViewportOrigination = (board) => {
|
|
1159
|
+
BOARD_TO_VIEWPORT_ORIGINATION.delete(board);
|
|
1139
1160
|
};
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1161
|
+
const getViewportOrigination = (board) => {
|
|
1162
|
+
const origination = BOARD_TO_VIEWPORT_ORIGINATION.get(board);
|
|
1163
|
+
if (origination) {
|
|
1164
|
+
return origination;
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
return board.viewport.origination;
|
|
1145
1168
|
}
|
|
1146
|
-
return false;
|
|
1147
|
-
};
|
|
1148
|
-
const isSecondaryPointer = (event) => {
|
|
1149
|
-
return event.button === POINTER_BUTTON.SECONDARY;
|
|
1150
1169
|
};
|
|
1151
|
-
const
|
|
1152
|
-
return
|
|
1170
|
+
const isFromScrolling = (board) => {
|
|
1171
|
+
return !!IS_FROM_SCROLLING.get(board);
|
|
1153
1172
|
};
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
* Extendable Custom Types Interface
|
|
1157
|
-
*/
|
|
1158
|
-
|
|
1159
|
-
const SELECTION_BORDER_COLOR = '#6698FF';
|
|
1160
|
-
const SELECTION_FILL_COLOR = '#6698FF19'; // 主色 0.1 透明度
|
|
1161
|
-
const Selection = {
|
|
1162
|
-
isCollapsed(selection) {
|
|
1163
|
-
if (selection.anchor[0] == selection.focus[0] && selection.anchor[1] === selection.focus[1]) {
|
|
1164
|
-
return true;
|
|
1165
|
-
}
|
|
1166
|
-
else {
|
|
1167
|
-
return false;
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1173
|
+
const setIsFromScrolling = (board, state) => {
|
|
1174
|
+
IS_FROM_SCROLLING.set(board, state);
|
|
1170
1175
|
};
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const realSelection = selection || board.selection;
|
|
1174
|
-
const selectedElements = [];
|
|
1175
|
-
const isCollapsed = realSelection && realSelection.ranges.length === 1 && Selection.isCollapsed(realSelection.ranges[0]);
|
|
1176
|
-
depthFirstRecursion(board, node => {
|
|
1177
|
-
if (selectedElements.length > 0 && isCollapsed) {
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
if (!PlaitBoard.isBoard(node) &&
|
|
1181
|
-
match(node) &&
|
|
1182
|
-
realSelection &&
|
|
1183
|
-
realSelection.ranges.some(range => {
|
|
1184
|
-
return board.isHitSelection(node, range);
|
|
1185
|
-
})) {
|
|
1186
|
-
selectedElements.push(node);
|
|
1187
|
-
}
|
|
1188
|
-
}, getIsRecursionFunc(board), true);
|
|
1189
|
-
return selectedElements;
|
|
1176
|
+
const isFromViewportChange = (board) => {
|
|
1177
|
+
return !!IS_FROM_VIEWPORT_CHANGE.get(board);
|
|
1190
1178
|
};
|
|
1191
|
-
const
|
|
1192
|
-
|
|
1193
|
-
return newRootElements.find(item => {
|
|
1194
|
-
return board.isHitSelection(item, range);
|
|
1195
|
-
});
|
|
1179
|
+
const setIsFromViewportChange = (board, state) => {
|
|
1180
|
+
IS_FROM_VIEWPORT_CHANGE.set(board, state);
|
|
1196
1181
|
};
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
}
|
|
1182
|
+
function scrollToRectangle(board, client) { }
|
|
1183
|
+
|
|
1184
|
+
let timerId = null;
|
|
1185
|
+
const throttleRAF = (fn) => {
|
|
1186
|
+
const scheduleFunc = () => {
|
|
1187
|
+
timerId = requestAnimationFrame(() => {
|
|
1188
|
+
timerId = null;
|
|
1189
|
+
fn();
|
|
1206
1190
|
});
|
|
1191
|
+
};
|
|
1192
|
+
if (timerId !== null) {
|
|
1193
|
+
cancelAnimationFrame(timerId);
|
|
1194
|
+
timerId = null;
|
|
1207
1195
|
}
|
|
1208
|
-
|
|
1209
|
-
};
|
|
1210
|
-
const cacheSelectedElements = (board, selectedElements) => {
|
|
1211
|
-
BOARD_TO_SELECTED_ELEMENT.set(board, selectedElements);
|
|
1196
|
+
scheduleFunc();
|
|
1212
1197
|
};
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1198
|
+
const debounce = (func, wait, options) => {
|
|
1199
|
+
let timerSubscription = null;
|
|
1200
|
+
return () => {
|
|
1201
|
+
if (timerSubscription && !timerSubscription.closed) {
|
|
1202
|
+
timerSubscription.unsubscribe();
|
|
1203
|
+
timerSubscription = timer(wait).subscribe(() => {
|
|
1204
|
+
func();
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
else {
|
|
1208
|
+
if (options?.leading) {
|
|
1209
|
+
func();
|
|
1210
|
+
}
|
|
1211
|
+
timerSubscription = timer(wait).subscribe();
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1215
1214
|
};
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1215
|
+
|
|
1216
|
+
const getMovingElements = (board) => {
|
|
1217
|
+
return BOARD_TO_MOVING_ELEMENT.get(board) || [];
|
|
1219
1218
|
};
|
|
1220
|
-
const
|
|
1221
|
-
const
|
|
1222
|
-
const
|
|
1223
|
-
|
|
1219
|
+
const addMovingElements = (board, elements) => {
|
|
1220
|
+
const movingElements = getMovingElements(board);
|
|
1221
|
+
const newElements = elements.filter(item => !movingElements.find(movingElement => movingElement.key === item.key));
|
|
1222
|
+
cacheMovingElements(board, [...movingElements, ...newElements]);
|
|
1224
1223
|
};
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1224
|
+
const removeMovingElements = (board) => {
|
|
1225
|
+
BOARD_TO_MOVING_ELEMENT.delete(board);
|
|
1227
1226
|
};
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
return !!selectedElements.find(value => value === element);
|
|
1227
|
+
const cacheMovingElements = (board, elements) => {
|
|
1228
|
+
BOARD_TO_MOVING_ELEMENT.set(board, elements);
|
|
1231
1229
|
};
|
|
1232
1230
|
|
|
1233
|
-
function
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
return false;
|
|
1238
|
-
}
|
|
1239
|
-
function hasOnContextChanged(value) {
|
|
1240
|
-
if (value.onContextChanged) {
|
|
1241
|
-
return true;
|
|
1242
|
-
}
|
|
1243
|
-
return false;
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
class PlaitPluginElementComponent {
|
|
1247
|
-
set context(value) {
|
|
1248
|
-
if (hasBeforeContextChange(this)) {
|
|
1249
|
-
this.beforeContextChange(value);
|
|
1250
|
-
}
|
|
1251
|
-
const previousContext = this._context;
|
|
1252
|
-
this._context = value;
|
|
1253
|
-
if (this.element) {
|
|
1254
|
-
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
1255
|
-
}
|
|
1256
|
-
if (this.initialized) {
|
|
1257
|
-
this.cdr.markForCheck();
|
|
1258
|
-
if (hasOnContextChanged(this)) {
|
|
1259
|
-
this.onContextChanged(value, previousContext);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
else {
|
|
1263
|
-
if (PlaitElement.isRootElement(this.element) && this.element.children) {
|
|
1264
|
-
this.g = createG();
|
|
1265
|
-
this.rootG = createG();
|
|
1266
|
-
this.rootG.append(this.g);
|
|
1267
|
-
}
|
|
1268
|
-
else {
|
|
1269
|
-
this.g = createG();
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
get context() {
|
|
1274
|
-
return this._context;
|
|
1275
|
-
}
|
|
1276
|
-
get element() {
|
|
1277
|
-
return this.context && this.context.element;
|
|
1278
|
-
}
|
|
1279
|
-
get board() {
|
|
1280
|
-
return this.context && this.context.board;
|
|
1281
|
-
}
|
|
1282
|
-
get selected() {
|
|
1283
|
-
return this.context && this.context.selected;
|
|
1284
|
-
}
|
|
1285
|
-
get effect() {
|
|
1286
|
-
return this.context && this.context.effect;
|
|
1287
|
-
}
|
|
1288
|
-
constructor(cdr) {
|
|
1289
|
-
this.cdr = cdr;
|
|
1290
|
-
this.initialized = false;
|
|
1231
|
+
function cloneCSSStyle(nativeNode, clonedNode) {
|
|
1232
|
+
const targetStyle = clonedNode.style;
|
|
1233
|
+
if (!targetStyle) {
|
|
1234
|
+
return;
|
|
1291
1235
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
this.initialized = true;
|
|
1236
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
1237
|
+
if (sourceStyle.cssText) {
|
|
1238
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
1239
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
1297
1240
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
(this.rootG || this.g).remove();
|
|
1241
|
+
else {
|
|
1242
|
+
Array.from(sourceStyle).forEach(name => {
|
|
1243
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
1244
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
1245
|
+
});
|
|
1304
1246
|
}
|
|
1305
|
-
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 }); }
|
|
1306
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.3", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
|
|
1307
|
-
}
|
|
1308
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
|
|
1309
|
-
type: Directive
|
|
1310
|
-
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
|
|
1311
|
-
type: Input
|
|
1312
|
-
}] } });
|
|
1313
|
-
const ELEMENT_TO_COMPONENT = new WeakMap();
|
|
1314
|
-
|
|
1315
|
-
function transformPoints(board, points) {
|
|
1316
|
-
const newPoints = points.map(point => {
|
|
1317
|
-
return transformPoint(board, point);
|
|
1318
|
-
});
|
|
1319
|
-
return newPoints;
|
|
1320
1247
|
}
|
|
1321
|
-
function
|
|
1322
|
-
const
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1248
|
+
function createCanvas(width, height, fillStyle) {
|
|
1249
|
+
const canvas = document.createElement('canvas');
|
|
1250
|
+
const ctx = canvas.getContext('2d');
|
|
1251
|
+
canvas.width = width;
|
|
1252
|
+
canvas.height = height;
|
|
1253
|
+
canvas.style.width = `${width}px`;
|
|
1254
|
+
canvas.style.height = `${height}px`;
|
|
1255
|
+
ctx.strokeStyle = '#ffffff';
|
|
1256
|
+
ctx.fillStyle = fillStyle;
|
|
1257
|
+
ctx.fillRect(0, 0, width, height);
|
|
1258
|
+
return {
|
|
1259
|
+
canvas,
|
|
1260
|
+
ctx
|
|
1261
|
+
};
|
|
1328
1262
|
}
|
|
1329
|
-
function
|
|
1330
|
-
|
|
1331
|
-
const plaitBoardRect = plaitBoardElement.getBoundingClientRect();
|
|
1332
|
-
const distances = distanceBetweenPointAndRectangle(x, y, plaitBoardRect);
|
|
1333
|
-
return distances === 0;
|
|
1263
|
+
function isElementNode(node) {
|
|
1264
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
1334
1265
|
}
|
|
1335
|
-
function
|
|
1336
|
-
const
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1266
|
+
function cloneSvg(board, options) {
|
|
1267
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1268
|
+
const { width, height, x, y } = elementHostBox;
|
|
1269
|
+
const { padding = 4, inlineStyleClassNames } = options;
|
|
1270
|
+
const sourceSvg = PlaitBoard.getHost(board);
|
|
1271
|
+
const cloneSvgElement = sourceSvg.cloneNode(true);
|
|
1272
|
+
cloneSvgElement.style.width = `${width}px`;
|
|
1273
|
+
cloneSvgElement.style.height = `${height}px`;
|
|
1274
|
+
cloneSvgElement.style.backgroundColor = '';
|
|
1275
|
+
cloneSvgElement.setAttribute('width', `${width}`);
|
|
1276
|
+
cloneSvgElement.setAttribute('height', `${height}`);
|
|
1277
|
+
cloneSvgElement.setAttribute('viewBox', [x - padding, y - padding, width + 2 * padding, height + 2 * padding].join(','));
|
|
1278
|
+
if (inlineStyleClassNames) {
|
|
1279
|
+
const sourceNodes = Array.from(sourceSvg.querySelectorAll(inlineStyleClassNames));
|
|
1280
|
+
const cloneNodes = Array.from(cloneSvgElement.querySelectorAll(inlineStyleClassNames));
|
|
1281
|
+
sourceNodes.forEach((node, index) => {
|
|
1282
|
+
const cloneNode = cloneNodes[index];
|
|
1283
|
+
const childElements = Array.from(node.querySelectorAll('*')).filter(isElementNode);
|
|
1284
|
+
const cloneChildElements = Array.from(cloneNode.querySelectorAll('*')).filter(isElementNode);
|
|
1285
|
+
sourceNodes.push(...childElements);
|
|
1286
|
+
cloneNodes.push(...cloneChildElements);
|
|
1287
|
+
});
|
|
1288
|
+
sourceNodes.forEach((node, index) => {
|
|
1289
|
+
const cloneNode = cloneNodes[index];
|
|
1290
|
+
cloneCSSStyle(node, cloneNode);
|
|
1291
|
+
});
|
|
1341
1292
|
}
|
|
1342
|
-
return
|
|
1293
|
+
return cloneSvgElement;
|
|
1343
1294
|
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
return newForeignObject;
|
|
1295
|
+
function loadImage(src) {
|
|
1296
|
+
return new Promise((resolve, reject) => {
|
|
1297
|
+
const img = new Image();
|
|
1298
|
+
img.onload = () => resolve(img);
|
|
1299
|
+
img.onerror = () => reject(new Error('Failed to load image'));
|
|
1300
|
+
img.src = src;
|
|
1301
|
+
});
|
|
1352
1302
|
}
|
|
1353
|
-
function
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
foreignObject.setAttribute('width', `${width}`);
|
|
1357
|
-
foreignObject.setAttribute('height', `${height}`);
|
|
1358
|
-
foreignObject.setAttribute('x', `${x}`);
|
|
1359
|
-
foreignObject.setAttribute('y', `${y}`);
|
|
1303
|
+
async function toImage(board, options) {
|
|
1304
|
+
if (!board) {
|
|
1305
|
+
return undefined;
|
|
1360
1306
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1307
|
+
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1308
|
+
const { ratio = 2, fillStyle = 'transparent' } = options;
|
|
1309
|
+
const { width, height } = elementHostBox;
|
|
1310
|
+
const ratioWidth = width * ratio;
|
|
1311
|
+
const ratioHeight = height * ratio;
|
|
1312
|
+
const cloneSvgElement = cloneSvg(board, options);
|
|
1313
|
+
const { canvas, ctx } = createCanvas(ratioWidth, ratioHeight, fillStyle);
|
|
1314
|
+
const svgStr = new XMLSerializer().serializeToString(cloneSvgElement);
|
|
1315
|
+
const imgSrc = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;
|
|
1316
|
+
try {
|
|
1317
|
+
const img = await loadImage(imgSrc);
|
|
1318
|
+
ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight);
|
|
1319
|
+
const url = canvas.toDataURL('image/png');
|
|
1320
|
+
return url;
|
|
1321
|
+
}
|
|
1322
|
+
catch (error) {
|
|
1323
|
+
console.error('Error converting SVG to image:', error);
|
|
1324
|
+
return undefined;
|
|
1366
1325
|
}
|
|
1367
1326
|
}
|
|
1327
|
+
function downloadImage(url, name) {
|
|
1328
|
+
const a = document.createElement('a');
|
|
1329
|
+
a.href = url;
|
|
1330
|
+
a.download = name;
|
|
1331
|
+
a.click();
|
|
1332
|
+
a.remove();
|
|
1333
|
+
}
|
|
1368
1334
|
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
const IS_SAFARI = typeof navigator !== 'undefined' && /Version\/[\d\.]+.*Safari/.test(navigator.userAgent);
|
|
1378
|
-
// "modern" Edge was released at 79.x
|
|
1379
|
-
const IS_EDGE_LEGACY = typeof navigator !== 'undefined' && /Edge?\/(?:[0-6][0-9]|[0-7][0-8])/i.test(navigator.userAgent);
|
|
1380
|
-
const IS_CHROME = typeof navigator !== 'undefined' && /Chrome/i.test(navigator.userAgent);
|
|
1381
|
-
// Native beforeInput events don't work well with react on Chrome 75 and older, Chrome 76+ can use beforeInput
|
|
1382
|
-
const IS_CHROME_LEGACY = typeof navigator !== 'undefined' && /Chrome?\/(?:[0-7][0-5]|[0-6][0-9])/i.test(navigator.userAgent);
|
|
1383
|
-
|
|
1384
|
-
/**
|
|
1385
|
-
* Check whether to merge an operation into the previous operation.
|
|
1386
|
-
*/
|
|
1387
|
-
const shouldMerge = (op, prev) => {
|
|
1388
|
-
if (op.type === 'set_viewport' && op.type === prev?.type) {
|
|
1389
|
-
return true;
|
|
1335
|
+
const getClipboardByKey = (key) => {
|
|
1336
|
+
return `application/x-plait-${key}-fragment`;
|
|
1337
|
+
};
|
|
1338
|
+
const setClipboardData = (data, elements) => {
|
|
1339
|
+
const result = [...elements];
|
|
1340
|
+
const pluginContextResult = getDataFromClipboard(data);
|
|
1341
|
+
if (pluginContextResult) {
|
|
1342
|
+
result.push(...pluginContextResult);
|
|
1390
1343
|
}
|
|
1391
|
-
|
|
1344
|
+
const stringObj = JSON.stringify(result);
|
|
1345
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1346
|
+
data?.setData(`application/${CLIP_BOARD_FORMAT_KEY}`, encoded);
|
|
1392
1347
|
};
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1348
|
+
const setClipboardDataByText = (data, text) => {
|
|
1349
|
+
const pluginContextResult = getTextFromClipboard(data);
|
|
1350
|
+
data?.setData(`text/plain`, text + '\n' + pluginContextResult);
|
|
1351
|
+
};
|
|
1352
|
+
const setClipboardDataByMedia = (data, media, key) => {
|
|
1353
|
+
const stringObj = JSON.stringify(media);
|
|
1354
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1355
|
+
data?.setData(getClipboardByKey(key), encoded);
|
|
1356
|
+
};
|
|
1357
|
+
const getDataFromClipboard = (data) => {
|
|
1358
|
+
const encoded = data?.getData(`application/${CLIP_BOARD_FORMAT_KEY}`);
|
|
1359
|
+
let nodesData = [];
|
|
1360
|
+
if (encoded) {
|
|
1361
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1362
|
+
nodesData = JSON.parse(decoded);
|
|
1399
1363
|
}
|
|
1400
|
-
return
|
|
1364
|
+
return nodesData;
|
|
1401
1365
|
};
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1366
|
+
const getTextFromClipboard = (data) => {
|
|
1367
|
+
return (data ? data.getData(`text/plain`) : '');
|
|
1368
|
+
};
|
|
1369
|
+
const getClipboardDataByMedia = (data, key) => {
|
|
1370
|
+
const encoded = data?.getData(getClipboardByKey(key));
|
|
1371
|
+
let imageItem = null;
|
|
1372
|
+
if (encoded) {
|
|
1373
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1374
|
+
imageItem = JSON.parse(decoded);
|
|
1408
1375
|
}
|
|
1409
|
-
return
|
|
1376
|
+
return imageItem;
|
|
1410
1377
|
};
|
|
1411
|
-
|
|
1378
|
+
|
|
1379
|
+
const isPreventTouchMove = (board) => {
|
|
1380
|
+
return !!IS_PREVENT_TOUCH_MOVE.get(board);
|
|
1381
|
+
};
|
|
1382
|
+
const preventTouchMove = (board, state) => {
|
|
1383
|
+
IS_PREVENT_TOUCH_MOVE.set(board, state);
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
const PlaitElement = {
|
|
1387
|
+
isRootElement(value) {
|
|
1388
|
+
const parent = NODE_TO_PARENT.get(value);
|
|
1389
|
+
if (parent && PlaitBoard.isBoard(parent)) {
|
|
1390
|
+
return true;
|
|
1391
|
+
}
|
|
1392
|
+
else {
|
|
1393
|
+
return false;
|
|
1394
|
+
}
|
|
1395
|
+
},
|
|
1396
|
+
getComponent(value) {
|
|
1397
|
+
return ELEMENT_TO_COMPONENT.get(value);
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
const Path = {
|
|
1412
1402
|
/**
|
|
1413
|
-
* Get
|
|
1403
|
+
* Get a list of ancestor paths for a given path.
|
|
1404
|
+
*
|
|
1405
|
+
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
1406
|
+
* `reverse: true` option is passed, they are reversed.
|
|
1414
1407
|
*/
|
|
1415
|
-
|
|
1416
|
-
|
|
1408
|
+
ancestors(path, options = {}) {
|
|
1409
|
+
const { reverse = false } = options;
|
|
1410
|
+
let paths = Path.levels(path, options);
|
|
1411
|
+
if (reverse) {
|
|
1412
|
+
paths = paths.slice(1);
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
paths = paths.slice(0, -1);
|
|
1416
|
+
}
|
|
1417
|
+
return paths;
|
|
1417
1418
|
},
|
|
1418
1419
|
/**
|
|
1419
|
-
* Get
|
|
1420
|
+
* Get a list of paths at every level down to a path. Note: this is the same
|
|
1421
|
+
* as `Path.ancestors`, but including the path itself.
|
|
1422
|
+
*
|
|
1423
|
+
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
1424
|
+
* true` option is passed, they are reversed.
|
|
1420
1425
|
*/
|
|
1421
|
-
|
|
1422
|
-
|
|
1426
|
+
levels(path, options = {}) {
|
|
1427
|
+
const { reverse = false } = options;
|
|
1428
|
+
const list = [];
|
|
1429
|
+
for (let i = 0; i <= path.length; i++) {
|
|
1430
|
+
list.push(path.slice(0, i));
|
|
1431
|
+
}
|
|
1432
|
+
if (reverse) {
|
|
1433
|
+
list.reverse();
|
|
1434
|
+
}
|
|
1435
|
+
return list;
|
|
1436
|
+
},
|
|
1437
|
+
parent(path) {
|
|
1438
|
+
if (path.length === 0) {
|
|
1439
|
+
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
1440
|
+
}
|
|
1441
|
+
return path.slice(0, -1);
|
|
1442
|
+
},
|
|
1443
|
+
next(path) {
|
|
1444
|
+
if (path.length === 0) {
|
|
1445
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
1446
|
+
}
|
|
1447
|
+
const last = path[path.length - 1];
|
|
1448
|
+
return path.slice(0, -1).concat(last + 1);
|
|
1449
|
+
},
|
|
1450
|
+
hasPrevious(path) {
|
|
1451
|
+
return path[path.length - 1] > 0;
|
|
1452
|
+
},
|
|
1453
|
+
previous(path) {
|
|
1454
|
+
if (path.length === 0) {
|
|
1455
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
|
|
1456
|
+
}
|
|
1457
|
+
const last = path[path.length - 1];
|
|
1458
|
+
return path.slice(0, -1).concat(last - 1);
|
|
1423
1459
|
},
|
|
1424
1460
|
/**
|
|
1425
|
-
*
|
|
1426
|
-
* the new operations into previous save point in the history.
|
|
1461
|
+
* Check if a path is an ancestor of another.
|
|
1427
1462
|
*/
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
MERGING.set(board, false);
|
|
1431
|
-
fn();
|
|
1432
|
-
MERGING.set(board, prev);
|
|
1463
|
+
isAncestor(path, another) {
|
|
1464
|
+
return path.length < another.length && Path.compare(path, another) === 0;
|
|
1433
1465
|
},
|
|
1434
1466
|
/**
|
|
1435
|
-
*
|
|
1436
|
-
*
|
|
1467
|
+
* Compare a path to another, returning an integer indicating whether the path
|
|
1468
|
+
* was before, at, or after the other.
|
|
1469
|
+
*
|
|
1470
|
+
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
1471
|
+
* directly above or below the other. If you want exact matching, use
|
|
1472
|
+
* [[Path.equals]] instead.
|
|
1437
1473
|
*/
|
|
1438
|
-
|
|
1439
|
-
const
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
/**
|
|
1447
|
-
* Hotkey mappings for each platform.
|
|
1448
|
-
*/
|
|
1449
|
-
const HOTKEYS = {
|
|
1450
|
-
bold: 'mod+b',
|
|
1451
|
-
compose: ['down', 'left', 'right', 'up', 'backspace', 'enter'],
|
|
1452
|
-
moveBackward: 'left',
|
|
1453
|
-
moveForward: 'right',
|
|
1454
|
-
moveUp: 'up',
|
|
1455
|
-
moveDown: 'down',
|
|
1456
|
-
moveWordBackward: 'ctrl+left',
|
|
1457
|
-
moveWordForward: 'ctrl+right',
|
|
1458
|
-
deleteBackward: 'shift?+backspace',
|
|
1459
|
-
deleteForward: 'shift?+delete',
|
|
1460
|
-
extendBackward: 'shift+left',
|
|
1461
|
-
extendForward: 'shift+right',
|
|
1462
|
-
italic: 'mod+i',
|
|
1463
|
-
splitBlock: 'shift?+enter',
|
|
1464
|
-
undo: 'mod+z'
|
|
1465
|
-
};
|
|
1466
|
-
const APPLE_HOTKEYS = {
|
|
1467
|
-
moveLineBackward: 'opt+up',
|
|
1468
|
-
moveLineForward: 'opt+down',
|
|
1469
|
-
moveWordBackward: 'opt+left',
|
|
1470
|
-
moveWordForward: 'opt+right',
|
|
1471
|
-
deleteBackward: ['ctrl+backspace', 'ctrl+h'],
|
|
1472
|
-
deleteForward: ['ctrl+delete', 'ctrl+d'],
|
|
1473
|
-
deleteLineBackward: 'cmd+shift?+backspace',
|
|
1474
|
-
deleteLineForward: ['cmd+shift?+delete', 'ctrl+k'],
|
|
1475
|
-
deleteWordBackward: 'opt+shift?+backspace',
|
|
1476
|
-
deleteWordForward: 'opt+shift?+delete',
|
|
1477
|
-
extendLineBackward: 'opt+shift+up',
|
|
1478
|
-
extendLineForward: 'opt+shift+down',
|
|
1479
|
-
redo: 'cmd+shift+z',
|
|
1480
|
-
transposeCharacter: 'ctrl+t'
|
|
1481
|
-
};
|
|
1482
|
-
const WINDOWS_HOTKEYS = {
|
|
1483
|
-
deleteWordBackward: 'ctrl+shift?+backspace',
|
|
1484
|
-
deleteWordForward: 'ctrl+shift?+delete',
|
|
1485
|
-
redo: ['ctrl+y', 'ctrl+shift+z']
|
|
1486
|
-
};
|
|
1487
|
-
/**
|
|
1488
|
-
* Create a platform-aware hotkey checker.
|
|
1489
|
-
*/
|
|
1490
|
-
const create = (key) => {
|
|
1491
|
-
const generic = HOTKEYS[key];
|
|
1492
|
-
const apple = APPLE_HOTKEYS[key];
|
|
1493
|
-
const windows = WINDOWS_HOTKEYS[key];
|
|
1494
|
-
const isGeneric = generic && isKeyHotkey(generic);
|
|
1495
|
-
const isApple = apple && isKeyHotkey(apple);
|
|
1496
|
-
const isWindows = windows && isKeyHotkey(windows);
|
|
1497
|
-
return (event) => {
|
|
1498
|
-
if (isGeneric && isGeneric(event)) {
|
|
1499
|
-
return true;
|
|
1500
|
-
}
|
|
1501
|
-
if (IS_APPLE && isApple && isApple(event)) {
|
|
1502
|
-
return true;
|
|
1474
|
+
compare(path, another) {
|
|
1475
|
+
const min = Math.min(path.length, another.length);
|
|
1476
|
+
for (let i = 0; i < min; i++) {
|
|
1477
|
+
if (path[i] < another[i])
|
|
1478
|
+
return -1;
|
|
1479
|
+
if (path[i] > another[i])
|
|
1480
|
+
return 1;
|
|
1503
1481
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1482
|
+
return 0;
|
|
1483
|
+
},
|
|
1484
|
+
/**
|
|
1485
|
+
* Check if a path is exactly equal to another.
|
|
1486
|
+
*/
|
|
1487
|
+
equals(path, another) {
|
|
1488
|
+
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
1489
|
+
},
|
|
1490
|
+
/**
|
|
1491
|
+
* Check if a path ends before one of the indexes in another.
|
|
1492
|
+
*/
|
|
1493
|
+
endsBefore(path, another) {
|
|
1494
|
+
const i = path.length - 1;
|
|
1495
|
+
const as = path.slice(0, i);
|
|
1496
|
+
const bs = another.slice(0, i);
|
|
1497
|
+
const av = path[i];
|
|
1498
|
+
const bv = another[i];
|
|
1499
|
+
return Path.equals(as, bs) && av < bv;
|
|
1500
|
+
},
|
|
1501
|
+
/**
|
|
1502
|
+
* Check if a path is a sibling of another.
|
|
1503
|
+
*/
|
|
1504
|
+
isSibling(path, another) {
|
|
1505
|
+
if (path.length !== another.length) {
|
|
1506
|
+
return false;
|
|
1506
1507
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1508
|
+
const as = path.slice(0, -1);
|
|
1509
|
+
const bs = another.slice(0, -1);
|
|
1510
|
+
const al = path[path.length - 1];
|
|
1511
|
+
const bl = another[another.length - 1];
|
|
1512
|
+
return al !== bl && Path.equals(as, bs);
|
|
1513
|
+
},
|
|
1514
|
+
transform(path, operation) {
|
|
1515
|
+
return produce(path, p => {
|
|
1516
|
+
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
1517
|
+
if (!path || path?.length === 0) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
if (p === null) {
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
switch (operation.type) {
|
|
1524
|
+
case 'insert_node': {
|
|
1525
|
+
const { path: op } = operation;
|
|
1526
|
+
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
1527
|
+
p[op.length - 1] += 1;
|
|
1528
|
+
}
|
|
1529
|
+
break;
|
|
1530
|
+
}
|
|
1531
|
+
case 'remove_node': {
|
|
1532
|
+
const { path: op } = operation;
|
|
1533
|
+
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
else if (Path.endsBefore(op, p)) {
|
|
1537
|
+
p[op.length - 1] -= 1;
|
|
1538
|
+
}
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
case 'move_node': {
|
|
1542
|
+
const { path: op, newPath: onp } = operation;
|
|
1543
|
+
// If the old and new path are the same, it's a no-op.
|
|
1544
|
+
if (Path.equals(op, onp)) {
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
1548
|
+
const copy = onp.slice();
|
|
1549
|
+
// op.length <= onp.length is different for slate
|
|
1550
|
+
// resolve drag from [0, 0] to [0, 3] issue
|
|
1551
|
+
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
1552
|
+
copy[op.length - 1] -= 1;
|
|
1553
|
+
}
|
|
1554
|
+
return copy.concat(p.slice(op.length));
|
|
1555
|
+
}
|
|
1556
|
+
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
1557
|
+
if (Path.endsBefore(op, p)) {
|
|
1558
|
+
p[op.length - 1] -= 1;
|
|
1559
|
+
}
|
|
1560
|
+
else {
|
|
1561
|
+
p[op.length - 1] += 1;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
1565
|
+
if (Path.endsBefore(op, p)) {
|
|
1566
|
+
p[op.length - 1] -= 1;
|
|
1567
|
+
}
|
|
1568
|
+
p[onp.length - 1] += 1;
|
|
1569
|
+
}
|
|
1570
|
+
else if (Path.endsBefore(op, p)) {
|
|
1571
|
+
if (Path.equals(onp, p)) {
|
|
1572
|
+
p[onp.length - 1] += 1;
|
|
1573
|
+
}
|
|
1574
|
+
p[op.length - 1] -= 1;
|
|
1575
|
+
}
|
|
1576
|
+
break;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return p;
|
|
1580
|
+
});
|
|
1548
1581
|
}
|
|
1549
|
-
|
|
1550
|
-
}
|
|
1582
|
+
};
|
|
1551
1583
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1584
|
+
const PlaitNode = {
|
|
1585
|
+
parent: (board, path) => {
|
|
1586
|
+
const parentPath = Path.parent(path);
|
|
1587
|
+
const p = PlaitNode.get(board, parentPath);
|
|
1588
|
+
return p;
|
|
1589
|
+
},
|
|
1590
|
+
/**
|
|
1591
|
+
* Return a generator of all the ancestor nodes above a specific path.
|
|
1592
|
+
*
|
|
1593
|
+
* By default the order is top-down, from highest to lowest ancestor in
|
|
1594
|
+
* the tree, but you can pass the `reverse: true` option to go bottom-up.
|
|
1595
|
+
*/
|
|
1596
|
+
*parents(root, path, options = {}) {
|
|
1597
|
+
for (const p of Path.ancestors(path, options)) {
|
|
1598
|
+
const n = PlaitNode.get(root, p);
|
|
1599
|
+
yield n;
|
|
1600
|
+
}
|
|
1601
|
+
},
|
|
1602
|
+
get(root, path) {
|
|
1603
|
+
let node = root;
|
|
1604
|
+
for (let i = 0; i < path.length; i++) {
|
|
1605
|
+
const p = path[i];
|
|
1606
|
+
if (!node || !node.children || !node.children[p]) {
|
|
1607
|
+
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
1608
|
+
}
|
|
1609
|
+
node = node.children[p];
|
|
1610
|
+
}
|
|
1611
|
+
return node;
|
|
1612
|
+
},
|
|
1613
|
+
last(board, path) {
|
|
1614
|
+
let n = PlaitNode.get(board, path);
|
|
1615
|
+
while (n && n.children && n.children.length > 0) {
|
|
1616
|
+
const i = n.children.length - 1;
|
|
1617
|
+
n = n.children[i];
|
|
1564
1618
|
}
|
|
1619
|
+
return n;
|
|
1565
1620
|
}
|
|
1566
|
-
const point1 = [x1 + radius, y1];
|
|
1567
|
-
const point2 = [x2 - radius, y1];
|
|
1568
|
-
const point3 = [x2, y1 + radius];
|
|
1569
|
-
const point4 = [x2, y2 - radius];
|
|
1570
|
-
const point5 = [x2 - radius, y2];
|
|
1571
|
-
const point6 = [x1 + radius, y2];
|
|
1572
|
-
const point7 = [x1, y2 - radius];
|
|
1573
|
-
const point8 = [x1, y1 + radius];
|
|
1574
|
-
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);
|
|
1575
|
-
}
|
|
1576
|
-
const drawRectangle = (board, rectangle, options) => {
|
|
1577
|
-
const roughSVG = PlaitBoard.getRoughSVG(board);
|
|
1578
|
-
const rectangleG = roughSVG.rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height, options);
|
|
1579
|
-
setStrokeLinecap(rectangleG, 'round');
|
|
1580
|
-
return rectangleG;
|
|
1581
1621
|
};
|
|
1582
1622
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
const rotatePoint = [
|
|
1591
|
-
end[0] > start[0] ? end[0] - rotateWidth : end[0] + rotateWidth,
|
|
1592
|
-
end[1] > start[1] ? end[1] - rotateHeight : end[1] + rotateHeight
|
|
1593
|
-
];
|
|
1594
|
-
const pointRight = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (degree * Math.PI) / 180);
|
|
1595
|
-
const pointLeft = rotate(rotatePoint[0], rotatePoint[1], end[0], end[1], (-degree * Math.PI) / 180);
|
|
1596
|
-
return { pointLeft, pointRight };
|
|
1597
|
-
}
|
|
1598
|
-
function drawArrow(rs, start, end, options, maxHypotenuseLength = 10, degree = 40) {
|
|
1599
|
-
const { pointLeft, pointRight } = arrowPoints(start, end, maxHypotenuseLength, degree);
|
|
1600
|
-
const arrowLineLeft = rs.linearPath([pointLeft, end], options);
|
|
1601
|
-
const arrowLineRight = rs.linearPath([pointRight, end], options);
|
|
1602
|
-
return [arrowLineLeft, arrowLineRight];
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
function drawCircle(roughSVG, point, diameter, options) {
|
|
1606
|
-
return roughSVG.circle(point[0], point[1], diameter, options);
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
function drawLine(rs, start, end, options) {
|
|
1610
|
-
return rs.linearPath([start, end], options);
|
|
1611
|
-
}
|
|
1612
|
-
function drawLinearPath(points, options) {
|
|
1613
|
-
const g = createG();
|
|
1614
|
-
const path = createPath();
|
|
1615
|
-
let polylinePath = '';
|
|
1616
|
-
points.forEach((point, index) => {
|
|
1617
|
-
if (index === 0) {
|
|
1618
|
-
polylinePath += `M ${point[0]} ${point[1]} `;
|
|
1623
|
+
const isSetViewportOperation = (value) => {
|
|
1624
|
+
return value.type === 'set_viewport';
|
|
1625
|
+
};
|
|
1626
|
+
const inverse = (op) => {
|
|
1627
|
+
switch (op.type) {
|
|
1628
|
+
case 'insert_node': {
|
|
1629
|
+
return { ...op, type: 'remove_node' };
|
|
1619
1630
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1631
|
+
case 'remove_node': {
|
|
1632
|
+
return { ...op, type: 'insert_node' };
|
|
1622
1633
|
}
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1634
|
+
case 'move_node': {
|
|
1635
|
+
const { newPath, path } = op;
|
|
1636
|
+
// PERF: in this case the move operation is a no-op anyways.
|
|
1637
|
+
if (Path.equals(newPath, path)) {
|
|
1638
|
+
return op;
|
|
1639
|
+
}
|
|
1640
|
+
// when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
|
|
1641
|
+
// shoud not return [0,2] -> [0,0] #WIK-8981
|
|
1642
|
+
// if (Path.isSibling(path, newPath)) {
|
|
1643
|
+
// return { ...op, path: newPath, newPath: path };
|
|
1644
|
+
// }
|
|
1645
|
+
// If the move does not happen within a single parent it is possible
|
|
1646
|
+
// for the move to impact the true path to the location where the node
|
|
1647
|
+
// was removed from and where it was inserted. We have to adjust for this
|
|
1648
|
+
// and find the original path. We can accomplish this (only in non-sibling)
|
|
1649
|
+
// moves by looking at the impact of the move operation on the node
|
|
1650
|
+
// after the original move path.
|
|
1651
|
+
const inversePath = Path.transform(path, op);
|
|
1652
|
+
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
1653
|
+
return { ...op, path: inversePath, newPath: inverseNewPath };
|
|
1638
1654
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1655
|
+
case 'set_node': {
|
|
1656
|
+
const { properties, newProperties } = op;
|
|
1657
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1641
1658
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
timerId = null;
|
|
1662
|
-
}
|
|
1663
|
-
scheduleFunc();
|
|
1664
|
-
};
|
|
1665
|
-
const debounce = (func, wait, options) => {
|
|
1666
|
-
let timerSubscription = null;
|
|
1667
|
-
return () => {
|
|
1668
|
-
if (timerSubscription && !timerSubscription.closed) {
|
|
1669
|
-
timerSubscription.unsubscribe();
|
|
1670
|
-
timerSubscription = timer(wait).subscribe(() => {
|
|
1671
|
-
func();
|
|
1672
|
-
});
|
|
1659
|
+
case 'set_selection': {
|
|
1660
|
+
const { properties, newProperties } = op;
|
|
1661
|
+
if (properties == null) {
|
|
1662
|
+
return {
|
|
1663
|
+
...op,
|
|
1664
|
+
properties: newProperties,
|
|
1665
|
+
newProperties: null
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
else if (newProperties == null) {
|
|
1669
|
+
return {
|
|
1670
|
+
...op,
|
|
1671
|
+
properties: null,
|
|
1672
|
+
newProperties: properties
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
else {
|
|
1676
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1677
|
+
}
|
|
1673
1678
|
}
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1679
|
+
case 'set_viewport': {
|
|
1680
|
+
const { properties, newProperties } = op;
|
|
1681
|
+
if (properties == null) {
|
|
1682
|
+
return {
|
|
1683
|
+
...op,
|
|
1684
|
+
properties: newProperties,
|
|
1685
|
+
newProperties: newProperties
|
|
1686
|
+
};
|
|
1677
1687
|
}
|
|
1678
|
-
|
|
1688
|
+
else if (newProperties == null) {
|
|
1689
|
+
return {
|
|
1690
|
+
...op,
|
|
1691
|
+
properties: properties,
|
|
1692
|
+
newProperties: properties
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
else {
|
|
1696
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
case 'set_theme': {
|
|
1700
|
+
const { properties, newProperties } = op;
|
|
1701
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
1679
1702
|
}
|
|
1680
|
-
}
|
|
1681
|
-
};
|
|
1682
|
-
|
|
1683
|
-
const getMovingElements = (board) => {
|
|
1684
|
-
return BOARD_TO_MOVING_ELEMENT.get(board) || [];
|
|
1703
|
+
}
|
|
1685
1704
|
};
|
|
1686
|
-
const
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
cacheMovingElements(board, [...movingElements, ...newElements]);
|
|
1705
|
+
const PlaitOperation = {
|
|
1706
|
+
isSetViewportOperation,
|
|
1707
|
+
inverse
|
|
1690
1708
|
};
|
|
1691
|
-
|
|
1692
|
-
|
|
1709
|
+
|
|
1710
|
+
const Point = {
|
|
1711
|
+
isEquals(point, otherPoint) {
|
|
1712
|
+
return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
|
|
1713
|
+
}
|
|
1693
1714
|
};
|
|
1694
|
-
|
|
1695
|
-
|
|
1715
|
+
|
|
1716
|
+
const Viewport = {
|
|
1717
|
+
isViewport: (value) => {
|
|
1718
|
+
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
1719
|
+
},
|
|
1696
1720
|
};
|
|
1697
1721
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
if (!targetStyle) {
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
1704
|
-
if (sourceStyle.cssText) {
|
|
1705
|
-
targetStyle.cssText = sourceStyle.cssText;
|
|
1706
|
-
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
1707
|
-
}
|
|
1708
|
-
else {
|
|
1709
|
-
Array.from(sourceStyle).forEach(name => {
|
|
1710
|
-
let value = sourceStyle.getPropertyValue(name);
|
|
1711
|
-
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
}
|
|
1715
|
-
function createCanvas(width, height, fillStyle) {
|
|
1716
|
-
const canvas = document.createElement('canvas');
|
|
1717
|
-
const ctx = canvas.getContext('2d');
|
|
1718
|
-
canvas.width = width;
|
|
1719
|
-
canvas.height = height;
|
|
1720
|
-
canvas.style.width = `${width}px`;
|
|
1721
|
-
canvas.style.height = `${height}px`;
|
|
1722
|
-
ctx.strokeStyle = '#ffffff';
|
|
1723
|
-
ctx.fillStyle = fillStyle;
|
|
1724
|
-
ctx.fillRect(0, 0, width, height);
|
|
1725
|
-
return {
|
|
1726
|
-
canvas,
|
|
1727
|
-
ctx
|
|
1728
|
-
};
|
|
1729
|
-
}
|
|
1730
|
-
function isElementNode(node) {
|
|
1731
|
-
return node.nodeType === Node.ELEMENT_NODE;
|
|
1732
|
-
}
|
|
1733
|
-
function cloneSvg(board, options) {
|
|
1734
|
-
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1735
|
-
const { width, height, x, y } = elementHostBox;
|
|
1736
|
-
const { padding = 4, inlineStyleClassNames } = options;
|
|
1737
|
-
const sourceSvg = PlaitBoard.getHost(board);
|
|
1738
|
-
const cloneSvgElement = sourceSvg.cloneNode(true);
|
|
1739
|
-
cloneSvgElement.style.width = `${width}px`;
|
|
1740
|
-
cloneSvgElement.style.height = `${height}px`;
|
|
1741
|
-
cloneSvgElement.style.backgroundColor = '';
|
|
1742
|
-
cloneSvgElement.setAttribute('width', `${width}`);
|
|
1743
|
-
cloneSvgElement.setAttribute('height', `${height}`);
|
|
1744
|
-
cloneSvgElement.setAttribute('viewBox', [x - padding, y - padding, width + 2 * padding, height + 2 * padding].join(','));
|
|
1745
|
-
if (inlineStyleClassNames) {
|
|
1746
|
-
const sourceNodes = Array.from(sourceSvg.querySelectorAll(inlineStyleClassNames));
|
|
1747
|
-
const cloneNodes = Array.from(cloneSvgElement.querySelectorAll(inlineStyleClassNames));
|
|
1748
|
-
sourceNodes.forEach((node, index) => {
|
|
1749
|
-
const cloneNode = cloneNodes[index];
|
|
1750
|
-
const childElements = Array.from(node.querySelectorAll('*')).filter(isElementNode);
|
|
1751
|
-
const cloneChildElements = Array.from(cloneNode.querySelectorAll('*')).filter(isElementNode);
|
|
1752
|
-
sourceNodes.push(...childElements);
|
|
1753
|
-
cloneNodes.push(...cloneChildElements);
|
|
1754
|
-
});
|
|
1755
|
-
sourceNodes.forEach((node, index) => {
|
|
1756
|
-
const cloneNode = cloneNodes[index];
|
|
1757
|
-
cloneCSSStyle(node, cloneNode);
|
|
1758
|
-
});
|
|
1759
|
-
}
|
|
1760
|
-
return cloneSvgElement;
|
|
1761
|
-
}
|
|
1762
|
-
function loadImage(src) {
|
|
1763
|
-
return new Promise((resolve, reject) => {
|
|
1764
|
-
const img = new Image();
|
|
1765
|
-
img.onload = () => resolve(img);
|
|
1766
|
-
img.onerror = () => reject(new Error('Failed to load image'));
|
|
1767
|
-
img.src = src;
|
|
1768
|
-
});
|
|
1769
|
-
}
|
|
1770
|
-
async function toImage(board, options) {
|
|
1771
|
-
if (!board) {
|
|
1772
|
-
return undefined;
|
|
1773
|
-
}
|
|
1774
|
-
const elementHostBox = getRectangleByElements(board, board.children, true);
|
|
1775
|
-
const { ratio = 2, fillStyle = 'transparent' } = options;
|
|
1776
|
-
const { width, height } = elementHostBox;
|
|
1777
|
-
const ratioWidth = width * ratio;
|
|
1778
|
-
const ratioHeight = height * ratio;
|
|
1779
|
-
const cloneSvgElement = cloneSvg(board, options);
|
|
1780
|
-
const { canvas, ctx } = createCanvas(ratioWidth, ratioHeight, fillStyle);
|
|
1781
|
-
const svgStr = new XMLSerializer().serializeToString(cloneSvgElement);
|
|
1782
|
-
const imgSrc = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;
|
|
1783
|
-
try {
|
|
1784
|
-
const img = await loadImage(imgSrc);
|
|
1785
|
-
ctx.drawImage(img, 0, 0, ratioWidth, ratioHeight);
|
|
1786
|
-
const url = canvas.toDataURL('image/png');
|
|
1787
|
-
return url;
|
|
1788
|
-
}
|
|
1789
|
-
catch (error) {
|
|
1790
|
-
console.error('Error converting SVG to image:', error);
|
|
1791
|
-
return undefined;
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
function downloadImage(url, name) {
|
|
1795
|
-
const a = document.createElement('a');
|
|
1796
|
-
a.href = url;
|
|
1797
|
-
a.download = name;
|
|
1798
|
-
a.click();
|
|
1799
|
-
a.remove();
|
|
1800
|
-
}
|
|
1722
|
+
const SAVING = new WeakMap();
|
|
1723
|
+
const MERGING = new WeakMap();
|
|
1801
1724
|
|
|
1802
|
-
|
|
1803
|
-
|
|
1725
|
+
var PlaitPluginKey;
|
|
1726
|
+
(function (PlaitPluginKey) {
|
|
1727
|
+
PlaitPluginKey["withSelection"] = "withSelection";
|
|
1728
|
+
})(PlaitPluginKey || (PlaitPluginKey = {}));
|
|
1729
|
+
|
|
1730
|
+
var ThemeColorMode;
|
|
1731
|
+
(function (ThemeColorMode) {
|
|
1732
|
+
ThemeColorMode["default"] = "default";
|
|
1733
|
+
ThemeColorMode["colorful"] = "colorful";
|
|
1734
|
+
ThemeColorMode["soft"] = "soft";
|
|
1735
|
+
ThemeColorMode["retro"] = "retro";
|
|
1736
|
+
ThemeColorMode["dark"] = "dark";
|
|
1737
|
+
ThemeColorMode["starry"] = "starry";
|
|
1738
|
+
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
1739
|
+
const DefaultThemeColor = {
|
|
1740
|
+
mode: ThemeColorMode.default,
|
|
1741
|
+
boardBackground: '#ffffff',
|
|
1742
|
+
textColor: '#333333'
|
|
1804
1743
|
};
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
result.push(...pluginContextResult);
|
|
1810
|
-
}
|
|
1811
|
-
const stringObj = JSON.stringify(result);
|
|
1812
|
-
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
1813
|
-
data?.setData(`application/${CLIP_BOARD_FORMAT_KEY}`, encoded);
|
|
1744
|
+
const ColorfulThemeColor = {
|
|
1745
|
+
mode: ThemeColorMode.colorful,
|
|
1746
|
+
boardBackground: '#ffffff',
|
|
1747
|
+
textColor: '#333333'
|
|
1814
1748
|
};
|
|
1815
|
-
const
|
|
1816
|
-
|
|
1817
|
-
|
|
1749
|
+
const SoftThemeColor = {
|
|
1750
|
+
mode: ThemeColorMode.soft,
|
|
1751
|
+
boardBackground: '#f5f5f5',
|
|
1752
|
+
textColor: '#333333'
|
|
1818
1753
|
};
|
|
1819
|
-
const
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1754
|
+
const RetroThemeColor = {
|
|
1755
|
+
mode: ThemeColorMode.retro,
|
|
1756
|
+
boardBackground: '#f9f8ed',
|
|
1757
|
+
textColor: '#333333'
|
|
1823
1758
|
};
|
|
1824
|
-
const
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const decoded = decodeURIComponent(window.atob(encoded));
|
|
1829
|
-
nodesData = JSON.parse(decoded);
|
|
1830
|
-
}
|
|
1831
|
-
return nodesData;
|
|
1759
|
+
const DarkThemeColor = {
|
|
1760
|
+
mode: ThemeColorMode.dark,
|
|
1761
|
+
boardBackground: '#141414',
|
|
1762
|
+
textColor: '#FFFFFF'
|
|
1832
1763
|
};
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1764
|
+
const StarryThemeColor = {
|
|
1765
|
+
mode: ThemeColorMode.starry,
|
|
1766
|
+
boardBackground: '#0d2537',
|
|
1767
|
+
textColor: '#FFFFFF'
|
|
1835
1768
|
};
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1769
|
+
const ThemeColors = [
|
|
1770
|
+
DefaultThemeColor,
|
|
1771
|
+
ColorfulThemeColor,
|
|
1772
|
+
SoftThemeColor,
|
|
1773
|
+
RetroThemeColor,
|
|
1774
|
+
DarkThemeColor,
|
|
1775
|
+
StarryThemeColor
|
|
1776
|
+
];
|
|
1777
|
+
|
|
1778
|
+
function getRectangleByElements(board, elements, recursion) {
|
|
1779
|
+
const boundaryBox = {
|
|
1780
|
+
left: Number.MAX_VALUE,
|
|
1781
|
+
top: Number.MAX_VALUE,
|
|
1782
|
+
right: Number.NEGATIVE_INFINITY,
|
|
1783
|
+
bottom: Number.NEGATIVE_INFINITY
|
|
1784
|
+
};
|
|
1785
|
+
const calcRectangleClient = (node) => {
|
|
1786
|
+
const nodeRectangle = board.getRectangle(node);
|
|
1787
|
+
if (nodeRectangle) {
|
|
1788
|
+
boundaryBox.left = Math.min(boundaryBox.left, nodeRectangle.x);
|
|
1789
|
+
boundaryBox.top = Math.min(boundaryBox.top, nodeRectangle.y);
|
|
1790
|
+
boundaryBox.right = Math.max(boundaryBox.right, nodeRectangle.x + nodeRectangle.width);
|
|
1791
|
+
boundaryBox.bottom = Math.max(boundaryBox.bottom, nodeRectangle.y + nodeRectangle.height);
|
|
1792
|
+
}
|
|
1793
|
+
else {
|
|
1794
|
+
console.error(`can not get rectangle of element:`, node);
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
elements.forEach(element => {
|
|
1798
|
+
if (recursion) {
|
|
1799
|
+
depthFirstRecursion(element, node => calcRectangleClient(node), node => board.isRecursion(node));
|
|
1800
|
+
}
|
|
1801
|
+
else {
|
|
1802
|
+
calcRectangleClient(element);
|
|
1803
|
+
}
|
|
1804
|
+
});
|
|
1805
|
+
if (boundaryBox.left === Number.MAX_VALUE) {
|
|
1806
|
+
return {
|
|
1807
|
+
x: 0,
|
|
1808
|
+
y: 0,
|
|
1809
|
+
width: 0,
|
|
1810
|
+
height: 0
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
return {
|
|
1814
|
+
x: boundaryBox.left,
|
|
1815
|
+
y: boundaryBox.top,
|
|
1816
|
+
width: boundaryBox.right - boundaryBox.left,
|
|
1817
|
+
height: boundaryBox.bottom - boundaryBox.top
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
function getBoardRectangle(board) {
|
|
1821
|
+
return getRectangleByElements(board, board.children, true);
|
|
1822
|
+
}
|
|
1823
|
+
function getElementById(board, id, dataSource) {
|
|
1824
|
+
if (!dataSource) {
|
|
1825
|
+
dataSource = findElements(board, { match: (element) => true, recursion: (element) => true });
|
|
1842
1826
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
}
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
isRootElement(value) {
|
|
1855
|
-
const parent = NODE_TO_PARENT.get(value);
|
|
1856
|
-
if (parent && PlaitBoard.isBoard(parent)) {
|
|
1827
|
+
let element = dataSource.find((element) => element.id === id);
|
|
1828
|
+
return element;
|
|
1829
|
+
}
|
|
1830
|
+
function findElements(board, options) {
|
|
1831
|
+
let elements = [];
|
|
1832
|
+
depthFirstRecursion(board, node => {
|
|
1833
|
+
if (!PlaitBoard.isBoard(node) && options.match(node)) {
|
|
1834
|
+
elements.push(node);
|
|
1835
|
+
}
|
|
1836
|
+
}, (value) => {
|
|
1837
|
+
if (PlaitBoard.isBoard(value)) {
|
|
1857
1838
|
return true;
|
|
1858
1839
|
}
|
|
1859
1840
|
else {
|
|
1860
|
-
return
|
|
1841
|
+
return getIsRecursionFunc(board)(value) && options.recursion(value);
|
|
1842
|
+
}
|
|
1843
|
+
}, true);
|
|
1844
|
+
return elements;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
const PlaitBoard = {
|
|
1848
|
+
isBoard(value) {
|
|
1849
|
+
const cachedIsBoard = IS_BOARD_CACHE.get(value);
|
|
1850
|
+
if (cachedIsBoard !== undefined) {
|
|
1851
|
+
return cachedIsBoard;
|
|
1861
1852
|
}
|
|
1853
|
+
const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
|
|
1854
|
+
IS_BOARD_CACHE.set(value, isBoard);
|
|
1855
|
+
return isBoard;
|
|
1862
1856
|
},
|
|
1863
|
-
|
|
1864
|
-
|
|
1857
|
+
findPath(board, node) {
|
|
1858
|
+
const path = [];
|
|
1859
|
+
let child = node;
|
|
1860
|
+
while (true) {
|
|
1861
|
+
const parent = NODE_TO_PARENT.get(child);
|
|
1862
|
+
if (parent == null) {
|
|
1863
|
+
if (PlaitBoard.isBoard(child)) {
|
|
1864
|
+
return path;
|
|
1865
|
+
}
|
|
1866
|
+
else {
|
|
1867
|
+
break;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
const i = NODE_TO_INDEX.get(child);
|
|
1871
|
+
if (i == null) {
|
|
1872
|
+
break;
|
|
1873
|
+
}
|
|
1874
|
+
path.unshift(i);
|
|
1875
|
+
child = parent;
|
|
1876
|
+
}
|
|
1877
|
+
throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
|
|
1878
|
+
},
|
|
1879
|
+
getHost(board) {
|
|
1880
|
+
return BOARD_TO_HOST.get(board);
|
|
1881
|
+
},
|
|
1882
|
+
getElementHost(board) {
|
|
1883
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.host;
|
|
1884
|
+
},
|
|
1885
|
+
getElementUpperHost(board) {
|
|
1886
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
|
|
1887
|
+
},
|
|
1888
|
+
getElementActiveHost(board) {
|
|
1889
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
|
|
1890
|
+
},
|
|
1891
|
+
getRoughSVG(board) {
|
|
1892
|
+
return BOARD_TO_ROUGH_SVG.get(board);
|
|
1893
|
+
},
|
|
1894
|
+
getComponent(board) {
|
|
1895
|
+
return BOARD_TO_COMPONENT.get(board);
|
|
1896
|
+
},
|
|
1897
|
+
getBoardContainer(board) {
|
|
1898
|
+
return PlaitBoard.getComponent(board).nativeElement;
|
|
1899
|
+
},
|
|
1900
|
+
getRectangle(board) {
|
|
1901
|
+
return getRectangleByElements(board, board.children, true);
|
|
1902
|
+
},
|
|
1903
|
+
getViewportContainer(board) {
|
|
1904
|
+
return PlaitBoard.getHost(board).parentElement;
|
|
1905
|
+
},
|
|
1906
|
+
isFocus(board) {
|
|
1907
|
+
return !!board.selection;
|
|
1908
|
+
},
|
|
1909
|
+
isReadonly(board) {
|
|
1910
|
+
return board.options.readonly;
|
|
1911
|
+
},
|
|
1912
|
+
hasBeenTextEditing(board) {
|
|
1913
|
+
return !!IS_TEXT_EDITABLE.get(board);
|
|
1914
|
+
},
|
|
1915
|
+
getPointer(board) {
|
|
1916
|
+
return board.pointer;
|
|
1917
|
+
},
|
|
1918
|
+
isPointer(board, pointer) {
|
|
1919
|
+
return board.pointer === pointer;
|
|
1920
|
+
},
|
|
1921
|
+
isInPointer(board, pointers) {
|
|
1922
|
+
const point = board.pointer;
|
|
1923
|
+
return pointers.includes(point);
|
|
1924
|
+
},
|
|
1925
|
+
getMovingPointInBoard(board) {
|
|
1926
|
+
return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
|
|
1927
|
+
},
|
|
1928
|
+
isMovingPointInBoard(board) {
|
|
1929
|
+
const point = BOARD_TO_MOVING_POINT.get(board);
|
|
1930
|
+
const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1931
|
+
if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
|
|
1932
|
+
return true;
|
|
1933
|
+
}
|
|
1934
|
+
return false;
|
|
1935
|
+
},
|
|
1936
|
+
getThemeColors(board) {
|
|
1937
|
+
return (board.options.themeColors || ThemeColors);
|
|
1865
1938
|
}
|
|
1866
1939
|
};
|
|
1867
1940
|
|
|
1868
|
-
const
|
|
1869
|
-
return value.type === 'set_viewport';
|
|
1870
|
-
};
|
|
1871
|
-
const inverse = (op) => {
|
|
1941
|
+
const applyToDraft = (board, selection, viewport, theme, op) => {
|
|
1872
1942
|
switch (op.type) {
|
|
1873
1943
|
case 'insert_node': {
|
|
1874
|
-
|
|
1944
|
+
const { path, node } = op;
|
|
1945
|
+
const parent = PlaitNode.parent(board, path);
|
|
1946
|
+
const index = path[path.length - 1];
|
|
1947
|
+
if (!parent.children || index > parent.children.length) {
|
|
1948
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
1949
|
+
}
|
|
1950
|
+
parent.children.splice(index, 0, node);
|
|
1951
|
+
break;
|
|
1875
1952
|
}
|
|
1876
1953
|
case 'remove_node': {
|
|
1877
|
-
|
|
1954
|
+
const { path } = op;
|
|
1955
|
+
const parent = PlaitNode.parent(board, path);
|
|
1956
|
+
const index = path[path.length - 1];
|
|
1957
|
+
if (!parent.children || index > parent.children.length) {
|
|
1958
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
1959
|
+
}
|
|
1960
|
+
parent.children.splice(index, 1);
|
|
1961
|
+
break;
|
|
1878
1962
|
}
|
|
1879
1963
|
case 'move_node': {
|
|
1880
|
-
const {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
return op;
|
|
1964
|
+
const { path, newPath } = op;
|
|
1965
|
+
if (Path.isAncestor(path, newPath)) {
|
|
1966
|
+
throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
|
|
1884
1967
|
}
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
//
|
|
1889
|
-
//
|
|
1890
|
-
//
|
|
1891
|
-
//
|
|
1892
|
-
//
|
|
1893
|
-
//
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
const
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1968
|
+
const node = PlaitNode.get(board, path);
|
|
1969
|
+
const parent = PlaitNode.parent(board, path);
|
|
1970
|
+
const index = path[path.length - 1];
|
|
1971
|
+
// This is tricky, but since the `path` and `newPath` both refer to
|
|
1972
|
+
// the same snapshot in time, there's a mismatch. After either
|
|
1973
|
+
// removing the original position, the second step's path can be out
|
|
1974
|
+
// of date. So instead of using the `op.newPath` directly, we
|
|
1975
|
+
// transform `op.path` to ascertain what the `newPath` would be after
|
|
1976
|
+
// the operation was applied.
|
|
1977
|
+
parent.children?.splice(index, 1);
|
|
1978
|
+
const truePath = Path.transform(path, op);
|
|
1979
|
+
const newParent = PlaitNode.get(board, Path.parent(truePath));
|
|
1980
|
+
const newIndex = truePath[truePath.length - 1];
|
|
1981
|
+
newParent.children?.splice(newIndex, 0, node);
|
|
1982
|
+
break;
|
|
1899
1983
|
}
|
|
1900
1984
|
case 'set_node': {
|
|
1901
|
-
const { properties, newProperties } = op;
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
case 'set_selection': {
|
|
1905
|
-
const { properties, newProperties } = op;
|
|
1906
|
-
if (properties == null) {
|
|
1907
|
-
return {
|
|
1908
|
-
...op,
|
|
1909
|
-
properties: newProperties,
|
|
1910
|
-
newProperties: null
|
|
1911
|
-
};
|
|
1985
|
+
const { path, properties, newProperties } = op;
|
|
1986
|
+
if (path.length === 0) {
|
|
1987
|
+
throw new Error(`Cannot set properties on the root node!`);
|
|
1912
1988
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1989
|
+
const node = PlaitNode.get(board, path);
|
|
1990
|
+
for (const key in newProperties) {
|
|
1991
|
+
const value = newProperties[key];
|
|
1992
|
+
if (value == null) {
|
|
1993
|
+
delete node[key];
|
|
1994
|
+
}
|
|
1995
|
+
else {
|
|
1996
|
+
node[key] = value;
|
|
1997
|
+
}
|
|
1919
1998
|
}
|
|
1920
|
-
|
|
1921
|
-
|
|
1999
|
+
// properties that were previously defined, but are now missing, must be deleted
|
|
2000
|
+
for (const key in properties) {
|
|
2001
|
+
if (!newProperties.hasOwnProperty(key)) {
|
|
2002
|
+
delete node[key];
|
|
2003
|
+
}
|
|
1922
2004
|
}
|
|
2005
|
+
break;
|
|
1923
2006
|
}
|
|
1924
2007
|
case 'set_viewport': {
|
|
1925
|
-
const {
|
|
1926
|
-
if (
|
|
1927
|
-
|
|
1928
|
-
...op,
|
|
1929
|
-
properties: newProperties,
|
|
1930
|
-
newProperties: newProperties
|
|
1931
|
-
};
|
|
2008
|
+
const { newProperties } = op;
|
|
2009
|
+
if (newProperties == null) {
|
|
2010
|
+
viewport = newProperties;
|
|
1932
2011
|
}
|
|
1933
|
-
else
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
2012
|
+
else {
|
|
2013
|
+
if (viewport == null) {
|
|
2014
|
+
if (!Viewport.isViewport(newProperties)) {
|
|
2015
|
+
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
2016
|
+
}
|
|
2017
|
+
viewport = { ...newProperties };
|
|
2018
|
+
}
|
|
2019
|
+
for (const key in newProperties) {
|
|
2020
|
+
const value = newProperties[key];
|
|
2021
|
+
if (value == null) {
|
|
2022
|
+
delete viewport[key];
|
|
2023
|
+
}
|
|
2024
|
+
else {
|
|
2025
|
+
viewport[key] = value;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
break;
|
|
2030
|
+
}
|
|
2031
|
+
case 'set_selection': {
|
|
2032
|
+
const { newProperties } = op;
|
|
2033
|
+
if (newProperties == null) {
|
|
2034
|
+
selection = newProperties;
|
|
1939
2035
|
}
|
|
1940
2036
|
else {
|
|
1941
|
-
|
|
2037
|
+
if (selection === null) {
|
|
2038
|
+
selection = op.newProperties;
|
|
2039
|
+
}
|
|
2040
|
+
else {
|
|
2041
|
+
selection.ranges = newProperties.ranges;
|
|
2042
|
+
}
|
|
1942
2043
|
}
|
|
2044
|
+
break;
|
|
1943
2045
|
}
|
|
1944
2046
|
case 'set_theme': {
|
|
1945
|
-
const {
|
|
1946
|
-
|
|
2047
|
+
const { newProperties } = op;
|
|
2048
|
+
theme = newProperties;
|
|
2049
|
+
break;
|
|
1947
2050
|
}
|
|
1948
2051
|
}
|
|
2052
|
+
return { selection, viewport, theme };
|
|
1949
2053
|
};
|
|
1950
|
-
const
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2054
|
+
const GeneralTransforms = {
|
|
2055
|
+
/**
|
|
2056
|
+
* Transform the board by an operation.
|
|
2057
|
+
*/
|
|
2058
|
+
transform(board, op) {
|
|
2059
|
+
board.children = createDraft(board.children);
|
|
2060
|
+
let viewport = board.viewport && createDraft(board.viewport);
|
|
2061
|
+
let selection = board.selection && createDraft(board.selection);
|
|
2062
|
+
let theme = board.theme && createDraft(board.theme);
|
|
2063
|
+
try {
|
|
2064
|
+
const state = applyToDraft(board, selection, viewport, theme, op);
|
|
2065
|
+
viewport = state.viewport;
|
|
2066
|
+
selection = state.selection;
|
|
2067
|
+
theme = state.theme;
|
|
2068
|
+
}
|
|
2069
|
+
finally {
|
|
2070
|
+
board.children = finishDraft(board.children);
|
|
2071
|
+
if (selection) {
|
|
2072
|
+
board.selection = isDraft(selection) ? finishDraft(selection) : selection;
|
|
2073
|
+
}
|
|
2074
|
+
else {
|
|
2075
|
+
board.selection = null;
|
|
2076
|
+
}
|
|
2077
|
+
board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
|
|
2078
|
+
board.theme = isDraft(theme) ? finishDraft(theme) : theme;
|
|
2079
|
+
}
|
|
1958
2080
|
}
|
|
1959
2081
|
};
|
|
1960
2082
|
|
|
1961
|
-
|
|
1962
|
-
const
|
|
1963
|
-
|
|
1964
|
-
var PlaitPluginKey;
|
|
1965
|
-
(function (PlaitPluginKey) {
|
|
1966
|
-
PlaitPluginKey["withSelection"] = "withSelection";
|
|
1967
|
-
})(PlaitPluginKey || (PlaitPluginKey = {}));
|
|
1968
|
-
|
|
1969
|
-
const IS_FROM_SCROLLING = new WeakMap();
|
|
1970
|
-
const IS_FROM_VIEWPORT_CHANGE = new WeakMap();
|
|
1971
|
-
function getViewportContainerRect(board) {
|
|
1972
|
-
const { hideScrollbar } = board.options;
|
|
1973
|
-
const scrollBarWidth = hideScrollbar ? SCROLL_BAR_WIDTH : 0;
|
|
1974
|
-
const viewportRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1975
|
-
return {
|
|
1976
|
-
width: viewportRect.width + scrollBarWidth,
|
|
1977
|
-
height: viewportRect.height + scrollBarWidth
|
|
1978
|
-
};
|
|
1979
|
-
}
|
|
1980
|
-
function getElementHostBBox(board, zoom) {
|
|
1981
|
-
const childrenRect = getRectangleByElements(board, board.children, true);
|
|
1982
|
-
const viewportContainerRect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
1983
|
-
const containerWidth = viewportContainerRect.width / zoom;
|
|
1984
|
-
const containerHeight = viewportContainerRect.height / zoom;
|
|
1985
|
-
let left;
|
|
1986
|
-
let right;
|
|
1987
|
-
let top;
|
|
1988
|
-
let bottom;
|
|
1989
|
-
if (childrenRect.width < containerWidth) {
|
|
1990
|
-
const centerX = childrenRect.x + childrenRect.width / 2;
|
|
1991
|
-
const halfContainerWidth = containerWidth / 2;
|
|
1992
|
-
left = centerX - halfContainerWidth;
|
|
1993
|
-
right = centerX + halfContainerWidth;
|
|
1994
|
-
}
|
|
1995
|
-
else {
|
|
1996
|
-
left = childrenRect.x;
|
|
1997
|
-
right = childrenRect.x + childrenRect.width;
|
|
1998
|
-
}
|
|
1999
|
-
if (childrenRect.height < containerHeight) {
|
|
2000
|
-
const centerY = childrenRect.y + childrenRect.height / 2;
|
|
2001
|
-
const halfContainerHeight = containerHeight / 2;
|
|
2002
|
-
top = centerY - halfContainerHeight;
|
|
2003
|
-
bottom = centerY + halfContainerHeight;
|
|
2004
|
-
}
|
|
2005
|
-
else {
|
|
2006
|
-
top = childrenRect.y;
|
|
2007
|
-
bottom = childrenRect.y + childrenRect.height;
|
|
2008
|
-
}
|
|
2009
|
-
return {
|
|
2010
|
-
left,
|
|
2011
|
-
right,
|
|
2012
|
-
top,
|
|
2013
|
-
bottom
|
|
2014
|
-
};
|
|
2015
|
-
}
|
|
2016
|
-
/**
|
|
2017
|
-
* 验证缩放比是否符合限制,如果超出限制,则返回合适的缩放比
|
|
2018
|
-
* @param zoom 缩放比
|
|
2019
|
-
* @param minZoom 最小缩放比
|
|
2020
|
-
* @param maxZoom 最大缩放比
|
|
2021
|
-
* @returns 正确的缩放比
|
|
2022
|
-
*/
|
|
2023
|
-
function clampZoomLevel(zoom, minZoom = 0.2, maxZoom = 4) {
|
|
2024
|
-
return zoom < minZoom ? minZoom : zoom > maxZoom ? maxZoom : zoom;
|
|
2025
|
-
}
|
|
2026
|
-
function getViewBox(board, zoom) {
|
|
2027
|
-
const boardContainerRectangle = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
2028
|
-
const elementHostBBox = getElementHostBBox(board, zoom);
|
|
2029
|
-
const horizontalPadding = boardContainerRectangle.width / 2;
|
|
2030
|
-
const verticalPadding = boardContainerRectangle.height / 2;
|
|
2031
|
-
const viewBox = [
|
|
2032
|
-
elementHostBBox.left - horizontalPadding / zoom,
|
|
2033
|
-
elementHostBBox.top - verticalPadding / zoom,
|
|
2034
|
-
elementHostBBox.right - elementHostBBox.left + (horizontalPadding * 2) / zoom,
|
|
2035
|
-
elementHostBBox.bottom - elementHostBBox.top + (verticalPadding * 2) / zoom
|
|
2036
|
-
];
|
|
2037
|
-
return viewBox;
|
|
2038
|
-
}
|
|
2039
|
-
function getViewBoxCenterPoint(board) {
|
|
2040
|
-
const childrenRectangle = getRectangleByElements(board, board.children, true);
|
|
2041
|
-
return [childrenRectangle.x + childrenRectangle.width / 2, childrenRectangle.y + childrenRectangle.height / 2];
|
|
2083
|
+
function insertNode(board, node, path) {
|
|
2084
|
+
const operation = { type: 'insert_node', node, path };
|
|
2085
|
+
board.apply(operation);
|
|
2042
2086
|
}
|
|
2043
|
-
function
|
|
2044
|
-
const
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2087
|
+
function setNode(board, props, path) {
|
|
2088
|
+
const properties = {};
|
|
2089
|
+
const newProperties = {};
|
|
2090
|
+
const node = PlaitNode.get(board, path);
|
|
2091
|
+
for (const k in props) {
|
|
2092
|
+
if (node[k] !== props[k]) {
|
|
2093
|
+
if (node.hasOwnProperty(k)) {
|
|
2094
|
+
properties[k] = node[k];
|
|
2095
|
+
}
|
|
2096
|
+
if (props[k] != null)
|
|
2097
|
+
newProperties[k] = props[k];
|
|
2098
|
+
}
|
|
2051
2099
|
}
|
|
2100
|
+
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2101
|
+
board.apply(operation);
|
|
2052
2102
|
}
|
|
2053
|
-
function
|
|
2054
|
-
const
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
const { zoom } = board.viewport;
|
|
2058
|
-
const viewBox = getViewBox(board, zoom);
|
|
2059
|
-
const scrollLeft = (origination[0] - viewBox[0]) * zoom;
|
|
2060
|
-
const scrollTop = (origination[1] - viewBox[1]) * zoom;
|
|
2061
|
-
updateViewportContainerScroll(board, scrollLeft, scrollTop);
|
|
2103
|
+
function removeNode(board, path) {
|
|
2104
|
+
const node = PlaitNode.get(board, path);
|
|
2105
|
+
const operation = { type: 'remove_node', path, node };
|
|
2106
|
+
board.apply(operation);
|
|
2062
2107
|
}
|
|
2063
|
-
function
|
|
2064
|
-
const
|
|
2065
|
-
|
|
2066
|
-
viewportContainer.scrollLeft = left;
|
|
2067
|
-
viewportContainer.scrollTop = top;
|
|
2068
|
-
isFromViewportChange && setIsFromViewportChange(board, true);
|
|
2069
|
-
}
|
|
2108
|
+
function moveNode(board, path, newPath) {
|
|
2109
|
+
const operation = { type: 'move_node', path, newPath };
|
|
2110
|
+
board.apply(operation);
|
|
2070
2111
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2112
|
+
const NodeTransforms = {
|
|
2113
|
+
insertNode,
|
|
2114
|
+
setNode,
|
|
2115
|
+
removeNode,
|
|
2116
|
+
moveNode
|
|
2117
|
+
};
|
|
2118
|
+
|
|
2119
|
+
function setSelection(board, selection) {
|
|
2120
|
+
const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
|
|
2121
|
+
board.apply(operation);
|
|
2076
2122
|
}
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2123
|
+
const SelectionTransforms = {
|
|
2124
|
+
setSelection,
|
|
2125
|
+
setSelectionWithTemporaryElements
|
|
2126
|
+
};
|
|
2127
|
+
function setSelectionWithTemporaryElements(board, elements) {
|
|
2128
|
+
setTimeout(() => {
|
|
2129
|
+
BOARD_TO_TEMPORARY_ELEMENTS.set(board, elements);
|
|
2130
|
+
setSelection(board, { ranges: [] });
|
|
2131
|
+
});
|
|
2081
2132
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
const viewBox = getViewBox(board, zoom);
|
|
2087
|
-
const centerX = viewBox[0] + viewBox[2] / 2;
|
|
2088
|
-
const centerY = viewBox[1] + viewBox[3] / 2;
|
|
2089
|
-
const origination = [centerX - viewportContainerRect.width / 2 / zoom, centerY - viewportContainerRect.height / 2 / zoom];
|
|
2090
|
-
updateViewportOrigination(board, origination);
|
|
2091
|
-
updateViewportOffset(board);
|
|
2092
|
-
return;
|
|
2093
|
-
}
|
|
2094
|
-
updateViewportOffset(board);
|
|
2133
|
+
|
|
2134
|
+
function setViewport(board, viewport) {
|
|
2135
|
+
const operation = { type: 'set_viewport', properties: board.viewport, newProperties: viewport };
|
|
2136
|
+
board.apply(operation);
|
|
2095
2137
|
}
|
|
2096
|
-
const
|
|
2097
|
-
|
|
2098
|
-
};
|
|
2099
|
-
const clearViewportOrigination = (board) => {
|
|
2100
|
-
BOARD_TO_VIEWPORT_ORIGINATION.delete(board);
|
|
2101
|
-
};
|
|
2102
|
-
const getViewportOrigination = (board) => {
|
|
2103
|
-
const origination = BOARD_TO_VIEWPORT_ORIGINATION.get(board);
|
|
2104
|
-
if (origination) {
|
|
2105
|
-
return origination;
|
|
2106
|
-
}
|
|
2107
|
-
else {
|
|
2108
|
-
return board.viewport.origination;
|
|
2109
|
-
}
|
|
2110
|
-
};
|
|
2111
|
-
const isFromScrolling = (board) => {
|
|
2112
|
-
return !!IS_FROM_SCROLLING.get(board);
|
|
2113
|
-
};
|
|
2114
|
-
const setIsFromScrolling = (board, state) => {
|
|
2115
|
-
IS_FROM_SCROLLING.set(board, state);
|
|
2116
|
-
};
|
|
2117
|
-
const isFromViewportChange = (board) => {
|
|
2118
|
-
return !!IS_FROM_VIEWPORT_CHANGE.get(board);
|
|
2119
|
-
};
|
|
2120
|
-
const setIsFromViewportChange = (board, state) => {
|
|
2121
|
-
IS_FROM_VIEWPORT_CHANGE.set(board, state);
|
|
2138
|
+
const ViewportTransforms$1 = {
|
|
2139
|
+
setViewport
|
|
2122
2140
|
};
|
|
2123
|
-
function scrollToRectangle(board, client) { }
|
|
2124
2141
|
|
|
2125
2142
|
function setTheme(board, themeColorMode) {
|
|
2126
2143
|
const operation = { type: 'set_theme', properties: board.theme, newProperties: themeColorMode };
|
|
@@ -2240,6 +2257,25 @@ const BoardTransforms = {
|
|
|
2240
2257
|
fitViewportWidth
|
|
2241
2258
|
};
|
|
2242
2259
|
|
|
2260
|
+
const removeElements = (board, elements) => {
|
|
2261
|
+
elements
|
|
2262
|
+
.map(element => {
|
|
2263
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2264
|
+
const ref = board.pathRef(path);
|
|
2265
|
+
return () => {
|
|
2266
|
+
removeNode(board, ref.current);
|
|
2267
|
+
ref.unref();
|
|
2268
|
+
removeSelectedElement(board, element);
|
|
2269
|
+
};
|
|
2270
|
+
})
|
|
2271
|
+
.forEach(action => {
|
|
2272
|
+
action();
|
|
2273
|
+
});
|
|
2274
|
+
};
|
|
2275
|
+
const CoreTransforms = {
|
|
2276
|
+
removeElements
|
|
2277
|
+
};
|
|
2278
|
+
|
|
2243
2279
|
const Transforms = {
|
|
2244
2280
|
...GeneralTransforms,
|
|
2245
2281
|
...ViewportTransforms$1,
|
|
@@ -2335,7 +2371,11 @@ function createBoard(children, options) {
|
|
|
2335
2371
|
dblclick: (event) => { },
|
|
2336
2372
|
setFragment: (data) => { },
|
|
2337
2373
|
insertFragment: (data) => { },
|
|
2338
|
-
deleteFragment: (data) => {
|
|
2374
|
+
deleteFragment: (data) => {
|
|
2375
|
+
const elements = board.getDeletedFragment([]);
|
|
2376
|
+
CoreTransforms.removeElements(board, elements);
|
|
2377
|
+
},
|
|
2378
|
+
getDeletedFragment: (data) => data,
|
|
2339
2379
|
drawElement: (context) => [],
|
|
2340
2380
|
redrawElement: (context, previousContext) => { },
|
|
2341
2381
|
destroyElement: (context) => { },
|
|
@@ -2894,6 +2934,11 @@ const withHotkey = (board) => {
|
|
|
2894
2934
|
Transforms.setSelectionWithTemporaryElements(board, elements);
|
|
2895
2935
|
return;
|
|
2896
2936
|
}
|
|
2937
|
+
const selectedElements = getSelectedElements(board);
|
|
2938
|
+
if (!PlaitBoard.isReadonly(board) && selectedElements.length > 0 && (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
2939
|
+
event.preventDefault();
|
|
2940
|
+
board.deleteFragment(null);
|
|
2941
|
+
}
|
|
2897
2942
|
keydown(event);
|
|
2898
2943
|
};
|
|
2899
2944
|
board.globalKeydown = (event) => {
|
|
@@ -3301,10 +3346,6 @@ class PlaitBoardComponent {
|
|
|
3301
3346
|
this.board.globalKeydown(event);
|
|
3302
3347
|
}), filter(event => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board) && !hasInputOrTextareaTarget(event.target)))
|
|
3303
3348
|
.subscribe((event) => {
|
|
3304
|
-
const selectedElements = getSelectedElements(this.board);
|
|
3305
|
-
if (selectedElements.length > 0 && (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
3306
|
-
this.board.deleteFragment(null);
|
|
3307
|
-
}
|
|
3308
3349
|
this.board.keydown(event);
|
|
3309
3350
|
});
|
|
3310
3351
|
fromEvent(document, 'keyup')
|
|
@@ -3658,5 +3699,5 @@ function createModModifierKeys() {
|
|
|
3658
3699
|
* Generated bundle index. Do not edit.
|
|
3659
3700
|
*/
|
|
3660
3701
|
|
|
3661
|
-
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, IS_PREVENT_TOUCH_MOVE, 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, 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, createSelectionOuterG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, getBoardRectangle, getClipboardByKey, getClipboardDataByMedia, getDataFromClipboard, getElementById, getElementHostBBox, getHitElementOfRoot, getHitElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getRealScrollBarWidth, getRectangleByElements, getSelectedElements, getTemporaryElements, getTextFromClipboard, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, 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 };
|
|
3702
|
+
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, 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_PREVENT_TOUCH_MOVE, 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, 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, createSelectionOuterG, 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, getTextFromClipboard, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, 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 };
|
|
3662
3703
|
//# sourceMappingURL=plait-core.mjs.map
|