@plait/core 0.51.1 → 0.51.3
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/constants/index.d.ts +1 -0
- package/esm2022/board/board.component.mjs +3 -2
- package/esm2022/constants/index.mjs +2 -1
- package/esm2022/interfaces/board.mjs +1 -1
- package/esm2022/interfaces/element.mjs +1 -1
- package/esm2022/interfaces/group.mjs +6 -0
- package/esm2022/interfaces/index.mjs +2 -1
- package/esm2022/interfaces/rectangle-client.mjs +1 -1
- package/esm2022/plugins/create-board.mjs +2 -1
- package/esm2022/plugins/with-group.mjs +27 -0
- package/esm2022/plugins/with-moving.mjs +4 -3
- package/esm2022/plugins/with-selection.mjs +96 -69
- package/esm2022/transforms/selection.mjs +2 -2
- package/esm2022/utils/dom/common.mjs +8 -4
- package/esm2022/utils/group.mjs +209 -0
- package/esm2022/utils/index.mjs +3 -1
- package/esm2022/utils/math.mjs +17 -12
- package/esm2022/utils/selected-element.mjs +12 -5
- package/esm2022/utils/selection.mjs +58 -0
- package/fesm2022/plait-core.mjs +1064 -735
- package/fesm2022/plait-core.mjs.map +1 -1
- package/interfaces/board.d.ts +1 -0
- package/interfaces/element.d.ts +1 -0
- package/interfaces/group.d.ts +7 -0
- package/interfaces/index.d.ts +1 -0
- package/package.json +1 -1
- package/plugins/with-group.d.ts +2 -0
- package/plugins/with-selection.d.ts +0 -13
- package/utils/dom/common.d.ts +2 -2
- package/utils/group.d.ts +22 -0
- package/utils/index.d.ts +2 -0
- package/utils/math.d.ts +3 -3
- package/utils/selection.d.ts +13 -0
package/fesm2022/plait-core.mjs
CHANGED
|
@@ -267,8 +267,8 @@ const getHitElementsBySelection = (board, selection, match = () => true) => {
|
|
|
267
267
|
return rectangleHitElements;
|
|
268
268
|
};
|
|
269
269
|
const getHitElementByPoint = (board, point, match = () => true) => {
|
|
270
|
-
let rectangleHitElement = undefined;
|
|
271
270
|
let hitElement = undefined;
|
|
271
|
+
let hitInsideElement = undefined;
|
|
272
272
|
depthFirstRecursion(board, node => {
|
|
273
273
|
if (hitElement) {
|
|
274
274
|
return;
|
|
@@ -280,11 +280,18 @@ const getHitElementByPoint = (board, point, match = () => true) => {
|
|
|
280
280
|
hitElement = node;
|
|
281
281
|
return;
|
|
282
282
|
}
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
/**
|
|
284
|
+
* 需要增加场景测试
|
|
285
|
+
* hitInsideElement 存的是第一个符合 isInsidePoint 的元素
|
|
286
|
+
* 当有元素符合 isHit 时结束遍历,并返回 hitElement
|
|
287
|
+
* 当所有元素都不符合 isHit ,则返回第一个符合 isInsidePoint 的元素
|
|
288
|
+
* 这样保证最上面的元素优先被探测到;
|
|
289
|
+
*/
|
|
290
|
+
if (!hitInsideElement && board.isInsidePoint(node, point)) {
|
|
291
|
+
hitInsideElement = node;
|
|
285
292
|
}
|
|
286
293
|
}, getIsRecursionFunc(board), true);
|
|
287
|
-
return hitElement ||
|
|
294
|
+
return hitElement || hitInsideElement;
|
|
288
295
|
};
|
|
289
296
|
const getHitSelectedElements = (board, point) => {
|
|
290
297
|
const selectedElements = getSelectedElements(board);
|
|
@@ -502,6 +509,7 @@ const POINTER_BUTTON = {
|
|
|
502
509
|
TOUCH: -1
|
|
503
510
|
};
|
|
504
511
|
const PRESS_AND_MOVE_BUFFER = 3;
|
|
512
|
+
const HIT_DISTANCE_BUFFER = 5;
|
|
505
513
|
|
|
506
514
|
const NS = 'http://www.w3.org/2000/svg';
|
|
507
515
|
function createG() {
|
|
@@ -527,9 +535,13 @@ function createRect(rectangle, options) {
|
|
|
527
535
|
const setStrokeLinecap = (g, value) => {
|
|
528
536
|
g.setAttribute('stroke-linecap', value);
|
|
529
537
|
};
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
538
|
+
const setAngleForG = (g, centerPoint, angle) => {
|
|
539
|
+
if (angle === 0) {
|
|
540
|
+
g.removeAttribute('transform');
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
var centerX = centerPoint[0];
|
|
544
|
+
var centerY = centerPoint[1];
|
|
533
545
|
let cosTheta = Math.cos(angle);
|
|
534
546
|
let sinTheta = Math.sin(angle);
|
|
535
547
|
let transformMatrix = [
|
|
@@ -787,14 +799,19 @@ const isLineHitLine = (a, b, c, d) => {
|
|
|
787
799
|
const cd = [d[0] - c[0], d[1] - c[1]];
|
|
788
800
|
return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
|
|
789
801
|
};
|
|
790
|
-
const isPolylineHitRectangle = (points, rectangle) => {
|
|
802
|
+
const isPolylineHitRectangle = (points, rectangle, isClose = true) => {
|
|
791
803
|
const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
804
|
+
const len = points.length;
|
|
805
|
+
for (let i = 0; i < len; i++) {
|
|
806
|
+
if (i === len - 1 && !isClose)
|
|
807
|
+
continue;
|
|
808
|
+
const p1 = points[i];
|
|
809
|
+
const p2 = points[(i + 1) % len];
|
|
810
|
+
const isHit = isLineHitLine(p1, p2, rectanglePoints[0], rectanglePoints[1]) ||
|
|
811
|
+
isLineHitLine(p1, p2, rectanglePoints[1], rectanglePoints[2]) ||
|
|
812
|
+
isLineHitLine(p1, p2, rectanglePoints[2], rectanglePoints[3]) ||
|
|
813
|
+
isLineHitLine(p1, p2, rectanglePoints[3], rectanglePoints[0]);
|
|
814
|
+
if (isHit || isPointInPolygon(p1, rectanglePoints) || isPointInPolygon(p2, rectanglePoints)) {
|
|
798
815
|
return true;
|
|
799
816
|
}
|
|
800
817
|
}
|
|
@@ -815,14 +832,14 @@ const isPointInPolygon = (point, points) => {
|
|
|
815
832
|
}
|
|
816
833
|
return inside;
|
|
817
834
|
};
|
|
818
|
-
const isPointInEllipse = (point, center, rx, ry,
|
|
819
|
-
const cosAngle = Math.cos(
|
|
820
|
-
const sinAngle = Math.sin(
|
|
835
|
+
const isPointInEllipse = (point, center, rx, ry, angle = 0) => {
|
|
836
|
+
const cosAngle = Math.cos(angle);
|
|
837
|
+
const sinAngle = Math.sin(angle);
|
|
821
838
|
const x1 = (point[0] - center[0]) * cosAngle + (point[1] - center[1]) * sinAngle;
|
|
822
839
|
const y1 = (point[1] - center[1]) * cosAngle - (point[0] - center[0]) * sinAngle;
|
|
823
840
|
return (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry) <= 1;
|
|
824
841
|
};
|
|
825
|
-
const isPointInRoundRectangle = (point, rectangle, radius) => {
|
|
842
|
+
const isPointInRoundRectangle = (point, rectangle, radius, angle = 0) => {
|
|
826
843
|
const { x: rectX, y: rectY, width, height } = rectangle;
|
|
827
844
|
const isInRectangle = point[0] >= rectX && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + height;
|
|
828
845
|
const handleLeftTop = point[0] >= rectX &&
|
|
@@ -2163,18 +2180,15 @@ const handleTouchTarget = (board) => {
|
|
|
2163
2180
|
}
|
|
2164
2181
|
};
|
|
2165
2182
|
|
|
2166
|
-
const
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
},
|
|
2176
|
-
getComponent(value) {
|
|
2177
|
-
return ELEMENT_TO_COMPONENT.get(value);
|
|
2183
|
+
const PlaitGroupElement = {
|
|
2184
|
+
isGroup: (value) => {
|
|
2185
|
+
return value.type === 'group';
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
const Viewport = {
|
|
2190
|
+
isViewport: (value) => {
|
|
2191
|
+
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
2178
2192
|
}
|
|
2179
2193
|
};
|
|
2180
2194
|
|
|
@@ -2400,803 +2414,823 @@ const PlaitNode = {
|
|
|
2400
2414
|
}
|
|
2401
2415
|
};
|
|
2402
2416
|
|
|
2403
|
-
const
|
|
2404
|
-
return value.type === 'set_viewport';
|
|
2405
|
-
};
|
|
2406
|
-
const inverse = (op) => {
|
|
2417
|
+
const applyToDraft = (board, selection, viewport, theme, op) => {
|
|
2407
2418
|
switch (op.type) {
|
|
2408
2419
|
case 'insert_node': {
|
|
2409
|
-
|
|
2420
|
+
const { path, node } = op;
|
|
2421
|
+
const parent = PlaitNode.parent(board, path);
|
|
2422
|
+
const index = path[path.length - 1];
|
|
2423
|
+
if (!parent.children || index > parent.children.length) {
|
|
2424
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2425
|
+
}
|
|
2426
|
+
parent.children.splice(index, 0, node);
|
|
2427
|
+
break;
|
|
2410
2428
|
}
|
|
2411
2429
|
case 'remove_node': {
|
|
2412
|
-
|
|
2430
|
+
const { path } = op;
|
|
2431
|
+
const parent = PlaitNode.parent(board, path);
|
|
2432
|
+
const index = path[path.length - 1];
|
|
2433
|
+
if (!parent.children || index > parent.children.length) {
|
|
2434
|
+
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2435
|
+
}
|
|
2436
|
+
parent.children.splice(index, 1);
|
|
2437
|
+
break;
|
|
2413
2438
|
}
|
|
2414
2439
|
case 'move_node': {
|
|
2415
|
-
const {
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
return op;
|
|
2440
|
+
const { path, newPath } = op;
|
|
2441
|
+
if (Path.isAncestor(path, newPath)) {
|
|
2442
|
+
throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
|
|
2419
2443
|
}
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
//
|
|
2424
|
-
//
|
|
2425
|
-
//
|
|
2426
|
-
//
|
|
2427
|
-
//
|
|
2428
|
-
//
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
const
|
|
2432
|
-
const
|
|
2433
|
-
|
|
2444
|
+
const node = PlaitNode.get(board, path);
|
|
2445
|
+
const parent = PlaitNode.parent(board, path);
|
|
2446
|
+
const index = path[path.length - 1];
|
|
2447
|
+
// This is tricky, but since the `path` and `newPath` both refer to
|
|
2448
|
+
// the same snapshot in time, there's a mismatch. After either
|
|
2449
|
+
// removing the original position, the second step's path can be out
|
|
2450
|
+
// of date. So instead of using the `op.newPath` directly, we
|
|
2451
|
+
// transform `op.path` to ascertain what the `newPath` would be after
|
|
2452
|
+
// the operation was applied.
|
|
2453
|
+
parent.children?.splice(index, 1);
|
|
2454
|
+
const truePath = Path.transform(path, op);
|
|
2455
|
+
const newParent = PlaitNode.get(board, Path.parent(truePath));
|
|
2456
|
+
const newIndex = truePath[truePath.length - 1];
|
|
2457
|
+
newParent.children?.splice(newIndex, 0, node);
|
|
2458
|
+
break;
|
|
2434
2459
|
}
|
|
2435
2460
|
case 'set_node': {
|
|
2436
|
-
const { properties, newProperties } = op;
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
case 'set_selection': {
|
|
2440
|
-
const { properties, newProperties } = op;
|
|
2441
|
-
if (properties == null) {
|
|
2442
|
-
return {
|
|
2443
|
-
...op,
|
|
2444
|
-
properties: newProperties,
|
|
2445
|
-
newProperties: null
|
|
2446
|
-
};
|
|
2461
|
+
const { path, properties, newProperties } = op;
|
|
2462
|
+
if (path.length === 0) {
|
|
2463
|
+
throw new Error(`Cannot set properties on the root node!`);
|
|
2447
2464
|
}
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
}
|
|
2465
|
+
const node = PlaitNode.get(board, path);
|
|
2466
|
+
for (const key in newProperties) {
|
|
2467
|
+
const value = newProperties[key];
|
|
2468
|
+
if (value == null) {
|
|
2469
|
+
delete node[key];
|
|
2470
|
+
}
|
|
2471
|
+
else {
|
|
2472
|
+
node[key] = value;
|
|
2473
|
+
}
|
|
2454
2474
|
}
|
|
2455
|
-
|
|
2456
|
-
|
|
2475
|
+
// properties that were previously defined, but are now missing, must be deleted
|
|
2476
|
+
for (const key in properties) {
|
|
2477
|
+
if (!newProperties.hasOwnProperty(key)) {
|
|
2478
|
+
delete node[key];
|
|
2479
|
+
}
|
|
2457
2480
|
}
|
|
2481
|
+
break;
|
|
2458
2482
|
}
|
|
2459
2483
|
case 'set_viewport': {
|
|
2460
|
-
const {
|
|
2461
|
-
if (
|
|
2462
|
-
|
|
2463
|
-
...op,
|
|
2464
|
-
properties: newProperties,
|
|
2465
|
-
newProperties: newProperties
|
|
2466
|
-
};
|
|
2484
|
+
const { newProperties } = op;
|
|
2485
|
+
if (newProperties == null) {
|
|
2486
|
+
viewport = newProperties;
|
|
2467
2487
|
}
|
|
2468
|
-
else
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2488
|
+
else {
|
|
2489
|
+
if (viewport == null) {
|
|
2490
|
+
if (!Viewport.isViewport(newProperties)) {
|
|
2491
|
+
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
2492
|
+
}
|
|
2493
|
+
viewport = { ...newProperties };
|
|
2494
|
+
}
|
|
2495
|
+
for (const key in newProperties) {
|
|
2496
|
+
const value = newProperties[key];
|
|
2497
|
+
if (value == null) {
|
|
2498
|
+
delete viewport[key];
|
|
2499
|
+
}
|
|
2500
|
+
else {
|
|
2501
|
+
viewport[key] = value;
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
break;
|
|
2506
|
+
}
|
|
2507
|
+
case 'set_selection': {
|
|
2508
|
+
const { newProperties } = op;
|
|
2509
|
+
if (newProperties == null) {
|
|
2510
|
+
selection = newProperties;
|
|
2474
2511
|
}
|
|
2475
2512
|
else {
|
|
2476
|
-
|
|
2513
|
+
if (selection === null) {
|
|
2514
|
+
selection = op.newProperties;
|
|
2515
|
+
}
|
|
2516
|
+
else {
|
|
2517
|
+
selection = newProperties;
|
|
2518
|
+
}
|
|
2477
2519
|
}
|
|
2520
|
+
break;
|
|
2478
2521
|
}
|
|
2479
2522
|
case 'set_theme': {
|
|
2480
|
-
const {
|
|
2481
|
-
|
|
2523
|
+
const { newProperties } = op;
|
|
2524
|
+
theme = newProperties;
|
|
2525
|
+
break;
|
|
2482
2526
|
}
|
|
2483
2527
|
}
|
|
2528
|
+
return { selection, viewport, theme };
|
|
2484
2529
|
};
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
},
|
|
2512
|
-
getOffsetY(point1, point2) {
|
|
2513
|
-
return point2[1] - point1[1];
|
|
2530
|
+
const GeneralTransforms = {
|
|
2531
|
+
/**
|
|
2532
|
+
* Transform the board by an operation.
|
|
2533
|
+
*/
|
|
2534
|
+
transform(board, op) {
|
|
2535
|
+
board.children = createDraft(board.children);
|
|
2536
|
+
let viewport = board.viewport && createDraft(board.viewport);
|
|
2537
|
+
let selection = board.selection && createDraft(board.selection);
|
|
2538
|
+
let theme = board.theme && createDraft(board.theme);
|
|
2539
|
+
try {
|
|
2540
|
+
const state = applyToDraft(board, selection, viewport, theme, op);
|
|
2541
|
+
viewport = state.viewport;
|
|
2542
|
+
selection = state.selection;
|
|
2543
|
+
theme = state.theme;
|
|
2544
|
+
}
|
|
2545
|
+
finally {
|
|
2546
|
+
board.children = finishDraft(board.children);
|
|
2547
|
+
if (selection) {
|
|
2548
|
+
board.selection = isDraft(selection) ? finishDraft(selection) : selection;
|
|
2549
|
+
}
|
|
2550
|
+
else {
|
|
2551
|
+
board.selection = null;
|
|
2552
|
+
}
|
|
2553
|
+
board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
|
|
2554
|
+
board.theme = isDraft(theme) ? finishDraft(theme) : theme;
|
|
2555
|
+
}
|
|
2514
2556
|
}
|
|
2515
2557
|
};
|
|
2516
2558
|
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2559
|
+
function insertNode(board, node, path) {
|
|
2560
|
+
const operation = { type: 'insert_node', node, path };
|
|
2561
|
+
board.apply(operation);
|
|
2562
|
+
}
|
|
2563
|
+
function setNode(board, props, path) {
|
|
2564
|
+
const properties = {};
|
|
2565
|
+
const newProperties = {};
|
|
2566
|
+
const node = PlaitNode.get(board, path);
|
|
2567
|
+
for (const k in props) {
|
|
2568
|
+
if (node[k] !== props[k]) {
|
|
2569
|
+
if (node.hasOwnProperty(k)) {
|
|
2570
|
+
properties[k] = node[k];
|
|
2571
|
+
}
|
|
2572
|
+
if (props[k] != null)
|
|
2573
|
+
newProperties[k] = props[k];
|
|
2574
|
+
}
|
|
2520
2575
|
}
|
|
2576
|
+
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2577
|
+
board.apply(operation);
|
|
2578
|
+
}
|
|
2579
|
+
function removeNode(board, path) {
|
|
2580
|
+
const node = PlaitNode.get(board, path);
|
|
2581
|
+
const operation = { type: 'remove_node', path, node };
|
|
2582
|
+
board.apply(operation);
|
|
2583
|
+
}
|
|
2584
|
+
function moveNode(board, path, newPath) {
|
|
2585
|
+
const operation = { type: 'move_node', path, newPath };
|
|
2586
|
+
board.apply(operation);
|
|
2587
|
+
}
|
|
2588
|
+
const NodeTransforms = {
|
|
2589
|
+
insertNode,
|
|
2590
|
+
setNode,
|
|
2591
|
+
removeNode,
|
|
2592
|
+
moveNode
|
|
2521
2593
|
};
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
ThemeColorMode["soft"] = "soft";
|
|
2531
|
-
ThemeColorMode["retro"] = "retro";
|
|
2532
|
-
ThemeColorMode["dark"] = "dark";
|
|
2533
|
-
ThemeColorMode["starry"] = "starry";
|
|
2534
|
-
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
2535
|
-
const DefaultThemeColor = {
|
|
2536
|
-
mode: ThemeColorMode.default,
|
|
2537
|
-
boardBackground: '#ffffff',
|
|
2538
|
-
textColor: '#333333'
|
|
2539
|
-
};
|
|
2540
|
-
const ColorfulThemeColor = {
|
|
2541
|
-
mode: ThemeColorMode.colorful,
|
|
2542
|
-
boardBackground: '#ffffff',
|
|
2543
|
-
textColor: '#333333'
|
|
2544
|
-
};
|
|
2545
|
-
const SoftThemeColor = {
|
|
2546
|
-
mode: ThemeColorMode.soft,
|
|
2547
|
-
boardBackground: '#f5f5f5',
|
|
2548
|
-
textColor: '#333333'
|
|
2549
|
-
};
|
|
2550
|
-
const RetroThemeColor = {
|
|
2551
|
-
mode: ThemeColorMode.retro,
|
|
2552
|
-
boardBackground: '#f9f8ed',
|
|
2553
|
-
textColor: '#333333'
|
|
2594
|
+
|
|
2595
|
+
function setSelection(board, selection) {
|
|
2596
|
+
const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
|
|
2597
|
+
board.apply(operation);
|
|
2598
|
+
}
|
|
2599
|
+
const SelectionTransforms = {
|
|
2600
|
+
setSelection,
|
|
2601
|
+
addSelectionWithTemporaryElements
|
|
2554
2602
|
};
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2603
|
+
function addSelectionWithTemporaryElements(board, elements) {
|
|
2604
|
+
const timeoutId = setTimeout(() => {
|
|
2605
|
+
setSelection(board, { anchor: [0, 0], focus: [0, 0] });
|
|
2606
|
+
}, 0);
|
|
2607
|
+
let ref = getTemporaryRef(board);
|
|
2608
|
+
if (ref) {
|
|
2609
|
+
clearTimeout(ref.timeoutId);
|
|
2610
|
+
const currentElements = ref.elements;
|
|
2611
|
+
ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
|
|
2612
|
+
ref.timeoutId = timeoutId;
|
|
2613
|
+
}
|
|
2614
|
+
else {
|
|
2615
|
+
BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
const removeElements = (board, elements) => {
|
|
2620
|
+
elements
|
|
2621
|
+
.map(element => {
|
|
2622
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2623
|
+
const ref = board.pathRef(path);
|
|
2624
|
+
return () => {
|
|
2625
|
+
removeNode(board, ref.current);
|
|
2626
|
+
ref.unref();
|
|
2627
|
+
removeSelectedElement(board, element, true);
|
|
2628
|
+
};
|
|
2629
|
+
})
|
|
2630
|
+
.forEach(action => {
|
|
2631
|
+
action();
|
|
2632
|
+
});
|
|
2559
2633
|
};
|
|
2560
|
-
const
|
|
2561
|
-
|
|
2562
|
-
boardBackground: '#0d2537',
|
|
2563
|
-
textColor: '#FFFFFF'
|
|
2634
|
+
const CoreTransforms = {
|
|
2635
|
+
removeElements
|
|
2564
2636
|
};
|
|
2565
|
-
const ThemeColors = [
|
|
2566
|
-
DefaultThemeColor,
|
|
2567
|
-
ColorfulThemeColor,
|
|
2568
|
-
SoftThemeColor,
|
|
2569
|
-
RetroThemeColor,
|
|
2570
|
-
DarkThemeColor,
|
|
2571
|
-
StarryThemeColor
|
|
2572
|
-
];
|
|
2573
2637
|
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
})(Direction || (Direction = {}));
|
|
2638
|
+
const Transforms = {
|
|
2639
|
+
...GeneralTransforms,
|
|
2640
|
+
...ViewportTransforms$1,
|
|
2641
|
+
...SelectionTransforms,
|
|
2642
|
+
...NodeTransforms
|
|
2643
|
+
};
|
|
2581
2644
|
|
|
2582
|
-
function
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2645
|
+
function isSelectionMoving(board) {
|
|
2646
|
+
return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
|
|
2647
|
+
}
|
|
2648
|
+
function setSelectionMoving(board) {
|
|
2649
|
+
PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
|
|
2650
|
+
BOARD_TO_IS_SELECTION_MOVING.set(board, true);
|
|
2651
|
+
setDragging(board, true);
|
|
2652
|
+
}
|
|
2653
|
+
function clearSelectionMoving(board) {
|
|
2654
|
+
PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
|
|
2655
|
+
BOARD_TO_IS_SELECTION_MOVING.delete(board);
|
|
2656
|
+
setDragging(board, false);
|
|
2657
|
+
}
|
|
2658
|
+
function isHandleSelection(board) {
|
|
2659
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2660
|
+
return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
|
|
2661
|
+
}
|
|
2662
|
+
function isSetSelectionOperation(board) {
|
|
2663
|
+
return !!board.operations.find(value => value.type === 'set_selection');
|
|
2664
|
+
}
|
|
2665
|
+
function getTemporaryElements(board) {
|
|
2666
|
+
const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
2667
|
+
if (ref) {
|
|
2668
|
+
return ref.elements;
|
|
2603
2669
|
}
|
|
2604
2670
|
else {
|
|
2605
|
-
return
|
|
2606
|
-
x: 0,
|
|
2607
|
-
y: 0,
|
|
2608
|
-
width: 0,
|
|
2609
|
-
height: 0
|
|
2610
|
-
};
|
|
2671
|
+
return undefined;
|
|
2611
2672
|
}
|
|
2612
2673
|
}
|
|
2613
|
-
function
|
|
2614
|
-
return
|
|
2674
|
+
function getTemporaryRef(board) {
|
|
2675
|
+
return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
|
|
2615
2676
|
}
|
|
2616
|
-
function
|
|
2617
|
-
|
|
2618
|
-
dataSource = findElements(board, { match: element => true, recursion: element => true });
|
|
2619
|
-
}
|
|
2620
|
-
let element = dataSource.find(element => element.id === id);
|
|
2621
|
-
return element;
|
|
2677
|
+
function deleteTemporaryElements(board) {
|
|
2678
|
+
BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
|
|
2622
2679
|
}
|
|
2623
|
-
function
|
|
2624
|
-
|
|
2625
|
-
const
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
}, isReverse);
|
|
2638
|
-
return elements;
|
|
2680
|
+
function createSelectionRectangleG(board) {
|
|
2681
|
+
const elements = getSelectedElements(board);
|
|
2682
|
+
const rectangle = getRectangleByElements(board, elements, false);
|
|
2683
|
+
if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
|
|
2684
|
+
const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
|
|
2685
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2686
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
2687
|
+
fillStyle: 'solid'
|
|
2688
|
+
});
|
|
2689
|
+
selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
|
|
2690
|
+
PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
|
|
2691
|
+
return selectionRectangleG;
|
|
2692
|
+
}
|
|
2693
|
+
return null;
|
|
2639
2694
|
}
|
|
2640
2695
|
|
|
2641
|
-
const
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
return isBoard;
|
|
2650
|
-
},
|
|
2651
|
-
findPath(board, node) {
|
|
2652
|
-
const path = [];
|
|
2653
|
-
let child = node;
|
|
2654
|
-
while (true) {
|
|
2655
|
-
const parent = NODE_TO_PARENT.get(child);
|
|
2656
|
-
if (parent == null) {
|
|
2657
|
-
if (PlaitBoard.isBoard(child)) {
|
|
2658
|
-
return path;
|
|
2659
|
-
}
|
|
2660
|
-
else {
|
|
2661
|
-
break;
|
|
2696
|
+
const getElementsInGroup = (board, group, recursion, includeGroup) => {
|
|
2697
|
+
let result = [];
|
|
2698
|
+
const elements = board.children.filter(value => value.groupId === group.id);
|
|
2699
|
+
if (recursion) {
|
|
2700
|
+
elements.forEach(item => {
|
|
2701
|
+
if (PlaitGroupElement.isGroup(item)) {
|
|
2702
|
+
if (includeGroup) {
|
|
2703
|
+
result.push(item);
|
|
2662
2704
|
}
|
|
2705
|
+
result.push(...getElementsInGroup(board, item, recursion));
|
|
2663
2706
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
break;
|
|
2707
|
+
else {
|
|
2708
|
+
result.push(item);
|
|
2667
2709
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
else {
|
|
2713
|
+
result = includeGroup ? elements : elements.filter(item => !PlaitGroupElement.isGroup(item));
|
|
2714
|
+
}
|
|
2715
|
+
return result;
|
|
2716
|
+
};
|
|
2717
|
+
const getRectangleByGroup = (board, group, recursion) => {
|
|
2718
|
+
const elementsInGroup = getElementsInGroup(board, group, recursion);
|
|
2719
|
+
return getRectangleByElements(board, elementsInGroup, false);
|
|
2720
|
+
};
|
|
2721
|
+
const getGroupByElement = (board, element, recursion) => {
|
|
2722
|
+
const group = board.children.find(item => item.id === element?.groupId);
|
|
2723
|
+
if (!group) {
|
|
2724
|
+
return recursion ? [] : null;
|
|
2725
|
+
}
|
|
2726
|
+
if (recursion) {
|
|
2727
|
+
const groups = [group];
|
|
2728
|
+
const grandGroups = getGroupByElement(board, group, recursion);
|
|
2729
|
+
if (grandGroups.length) {
|
|
2730
|
+
groups.push(...grandGroups);
|
|
2670
2731
|
}
|
|
2671
|
-
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
|
-
return
|
|
2675
|
-
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
return
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2732
|
+
return groups;
|
|
2733
|
+
}
|
|
2734
|
+
else {
|
|
2735
|
+
return group;
|
|
2736
|
+
}
|
|
2737
|
+
};
|
|
2738
|
+
const getHighestGroup = (board, element) => {
|
|
2739
|
+
const groups = getGroupByElement(board, element, true);
|
|
2740
|
+
if (groups.length) {
|
|
2741
|
+
return groups[groups.length - 1];
|
|
2742
|
+
}
|
|
2743
|
+
return null;
|
|
2744
|
+
};
|
|
2745
|
+
const getElementsInGroupByElement = (board, element) => {
|
|
2746
|
+
const highestGroup = getHighestGroup(board, element);
|
|
2747
|
+
if (highestGroup) {
|
|
2748
|
+
return getElementsInGroup(board, highestGroup, true);
|
|
2749
|
+
}
|
|
2750
|
+
else {
|
|
2751
|
+
return [element];
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
const isSelectedElementOrGroup = (board, element) => {
|
|
2755
|
+
const selectedElements = getSelectedElements(board);
|
|
2756
|
+
if (PlaitGroupElement.isGroup(element)) {
|
|
2757
|
+
return isSelectedAllElementsInGroup(board, element);
|
|
2758
|
+
}
|
|
2759
|
+
return selectedElements.includes(element);
|
|
2760
|
+
};
|
|
2761
|
+
const isSelectedAllElementsInGroup = (board, group) => {
|
|
2762
|
+
const selectedElements = getSelectedElements(board);
|
|
2763
|
+
const elementsInGroup = getElementsInGroup(board, group, true);
|
|
2764
|
+
return elementsInGroup.every(item => selectedElements.includes(item));
|
|
2765
|
+
};
|
|
2766
|
+
const getSelectedGroups = (board, groups) => {
|
|
2767
|
+
const selectedGroups = [];
|
|
2768
|
+
groups.forEach(item => {
|
|
2769
|
+
if (isSelectedElementOrGroup(board, item)) {
|
|
2770
|
+
selectedGroups.push(item);
|
|
2771
|
+
}
|
|
2772
|
+
});
|
|
2773
|
+
return selectedGroups;
|
|
2774
|
+
};
|
|
2775
|
+
const getHighestSelectedGroup = (board, element) => {
|
|
2776
|
+
const groups = getGroupByElement(board, element, true);
|
|
2777
|
+
const selectedGroups = getSelectedGroups(board, groups);
|
|
2778
|
+
if (selectedGroups.length) {
|
|
2779
|
+
return selectedGroups[selectedGroups.length - 1];
|
|
2780
|
+
}
|
|
2781
|
+
return null;
|
|
2782
|
+
};
|
|
2783
|
+
const getHighestSelectedGroups = (board) => {
|
|
2784
|
+
let result = [];
|
|
2785
|
+
const selectedElements = getSelectedElements(board);
|
|
2786
|
+
selectedElements.forEach(item => {
|
|
2787
|
+
if (item.groupId) {
|
|
2788
|
+
const group = getHighestSelectedGroup(board, item);
|
|
2789
|
+
if (group && !result.includes(group)) {
|
|
2790
|
+
result.push(group);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
});
|
|
2794
|
+
return result;
|
|
2795
|
+
};
|
|
2796
|
+
const getSelectedIsolatedElements = (board) => {
|
|
2797
|
+
let result = [];
|
|
2798
|
+
const selectedElements = getSelectedElements(board);
|
|
2799
|
+
selectedElements.forEach(item => {
|
|
2800
|
+
if (!item.groupId) {
|
|
2801
|
+
result.push(item);
|
|
2802
|
+
}
|
|
2803
|
+
else {
|
|
2804
|
+
const group = getHighestSelectedGroup(board, item);
|
|
2805
|
+
if (!group) {
|
|
2806
|
+
result.push(item);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
});
|
|
2810
|
+
return result;
|
|
2811
|
+
};
|
|
2812
|
+
const getHighestSelectedElements = (board) => {
|
|
2813
|
+
return [...getHighestSelectedGroups(board), ...getSelectedIsolatedElements(board)];
|
|
2814
|
+
};
|
|
2815
|
+
const createGroupRectangleG = (board, elements) => {
|
|
2816
|
+
const selectedElements = getSelectedElements(board);
|
|
2817
|
+
const groupRectangleG = createG();
|
|
2818
|
+
const isMoving = isSelectionMoving(board);
|
|
2819
|
+
elements.forEach(item => {
|
|
2820
|
+
const isRender = (!selectedElements.includes(item) && !isMoving) || isMoving;
|
|
2821
|
+
if (item.groupId && isRender) {
|
|
2822
|
+
const elements = getElementsInGroupByElement(board, item);
|
|
2823
|
+
const rectangle = getRectangleByElements(board, elements, false);
|
|
2824
|
+
groupRectangleG.append(drawRectangle(board, rectangle, {
|
|
2825
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
2826
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
2827
|
+
strokeLineDash: [5]
|
|
2828
|
+
}));
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
return groupRectangleG;
|
|
2832
|
+
};
|
|
2833
|
+
const createGroup = () => {
|
|
2834
|
+
return {
|
|
2835
|
+
id: idCreator(),
|
|
2836
|
+
type: 'group'
|
|
2837
|
+
};
|
|
2838
|
+
};
|
|
2839
|
+
const nonGroupInHighestSelectedElements = (elements) => {
|
|
2840
|
+
return elements.every(item => !item.groupId);
|
|
2841
|
+
};
|
|
2842
|
+
const hasSelectedElementsInSameGroup = (elements) => {
|
|
2843
|
+
return elements.every(item => item.groupId && item.groupId === elements[0].groupId);
|
|
2844
|
+
};
|
|
2845
|
+
const canAddGroup = (highestSelectedElements) => {
|
|
2846
|
+
if (highestSelectedElements.length > 1) {
|
|
2847
|
+
return nonGroupInHighestSelectedElements(highestSelectedElements) || hasSelectedElementsInSameGroup(highestSelectedElements);
|
|
2848
|
+
}
|
|
2849
|
+
return false;
|
|
2850
|
+
};
|
|
2851
|
+
const addGroup = (board) => {
|
|
2852
|
+
const selectedGroups = getHighestSelectedGroups(board);
|
|
2853
|
+
const selectedIsolatedElements = getSelectedIsolatedElements(board);
|
|
2854
|
+
const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
|
|
2855
|
+
const group = createGroup();
|
|
2856
|
+
if (canAddGroup(highestSelectedElements)) {
|
|
2857
|
+
highestSelectedElements.forEach(item => {
|
|
2858
|
+
const path = PlaitBoard.findPath(board, item);
|
|
2859
|
+
Transforms.setNode(board, { groupId: group.id }, path);
|
|
2860
|
+
});
|
|
2861
|
+
if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
|
|
2862
|
+
const newGroupId = selectedIsolatedElements[0].groupId;
|
|
2863
|
+
Transforms.insertNode(board, {
|
|
2864
|
+
...group,
|
|
2865
|
+
groupId: newGroupId
|
|
2866
|
+
}, [board.children.length]);
|
|
2867
|
+
}
|
|
2868
|
+
else {
|
|
2869
|
+
Transforms.insertNode(board, group, [board.children.length]);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
};
|
|
2873
|
+
const canRemoveGroup = (board, selectedGroups) => {
|
|
2874
|
+
const selectedElements = getSelectedElements(board);
|
|
2875
|
+
return selectedElements.length > 0 && selectedGroups.length > 0;
|
|
2876
|
+
};
|
|
2877
|
+
const removeGroup = (board) => {
|
|
2878
|
+
const selectedGroups = getHighestSelectedGroups(board);
|
|
2879
|
+
if (canRemoveGroup(board, selectedGroups)) {
|
|
2880
|
+
selectedGroups.map(group => {
|
|
2881
|
+
const elementsInGroup = findElements(board, {
|
|
2882
|
+
match: item => item.groupId === group.id,
|
|
2883
|
+
recursion: () => false
|
|
2884
|
+
});
|
|
2885
|
+
elementsInGroup.forEach(item => {
|
|
2886
|
+
const path = PlaitBoard.findPath(board, item);
|
|
2887
|
+
Transforms.setNode(board, { groupId: group.groupId || undefined }, path);
|
|
2888
|
+
});
|
|
2889
|
+
const groupPath = PlaitBoard.findPath(board, group);
|
|
2890
|
+
Transforms.removeNode(board, groupPath);
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
|
|
2895
|
+
const PlaitElement = {
|
|
2896
|
+
isRootElement(value) {
|
|
2897
|
+
const parent = NODE_TO_PARENT.get(value);
|
|
2898
|
+
if (parent && PlaitBoard.isBoard(parent)) {
|
|
2726
2899
|
return true;
|
|
2727
2900
|
}
|
|
2728
|
-
|
|
2901
|
+
else {
|
|
2902
|
+
return false;
|
|
2903
|
+
}
|
|
2729
2904
|
},
|
|
2730
|
-
|
|
2731
|
-
return (
|
|
2905
|
+
getComponent(value) {
|
|
2906
|
+
return ELEMENT_TO_COMPONENT.get(value);
|
|
2732
2907
|
}
|
|
2733
2908
|
};
|
|
2734
2909
|
|
|
2735
|
-
const
|
|
2910
|
+
const isSetViewportOperation = (value) => {
|
|
2911
|
+
return value.type === 'set_viewport';
|
|
2912
|
+
};
|
|
2913
|
+
const inverse = (op) => {
|
|
2736
2914
|
switch (op.type) {
|
|
2737
2915
|
case 'insert_node': {
|
|
2738
|
-
|
|
2739
|
-
const parent = PlaitNode.parent(board, path);
|
|
2740
|
-
const index = path[path.length - 1];
|
|
2741
|
-
if (!parent.children || index > parent.children.length) {
|
|
2742
|
-
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2743
|
-
}
|
|
2744
|
-
parent.children.splice(index, 0, node);
|
|
2745
|
-
break;
|
|
2916
|
+
return { ...op, type: 'remove_node' };
|
|
2746
2917
|
}
|
|
2747
2918
|
case 'remove_node': {
|
|
2748
|
-
|
|
2749
|
-
const parent = PlaitNode.parent(board, path);
|
|
2750
|
-
const index = path[path.length - 1];
|
|
2751
|
-
if (!parent.children || index > parent.children.length) {
|
|
2752
|
-
throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
|
|
2753
|
-
}
|
|
2754
|
-
parent.children.splice(index, 1);
|
|
2755
|
-
break;
|
|
2919
|
+
return { ...op, type: 'insert_node' };
|
|
2756
2920
|
}
|
|
2757
2921
|
case 'move_node': {
|
|
2758
|
-
const {
|
|
2759
|
-
|
|
2760
|
-
|
|
2922
|
+
const { newPath, path } = op;
|
|
2923
|
+
// PERF: in this case the move operation is a no-op anyways.
|
|
2924
|
+
if (Path.equals(newPath, path)) {
|
|
2925
|
+
return op;
|
|
2761
2926
|
}
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
//
|
|
2766
|
-
//
|
|
2767
|
-
//
|
|
2768
|
-
//
|
|
2769
|
-
//
|
|
2770
|
-
// the
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
const
|
|
2774
|
-
const
|
|
2775
|
-
|
|
2776
|
-
break;
|
|
2927
|
+
// when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
|
|
2928
|
+
// shoud not return [0,2] -> [0,0] #WIK-8981
|
|
2929
|
+
// if (Path.isSibling(path, newPath)) {
|
|
2930
|
+
// return { ...op, path: newPath, newPath: path };
|
|
2931
|
+
// }
|
|
2932
|
+
// If the move does not happen within a single parent it is possible
|
|
2933
|
+
// for the move to impact the true path to the location where the node
|
|
2934
|
+
// was removed from and where it was inserted. We have to adjust for this
|
|
2935
|
+
// and find the original path. We can accomplish this (only in non-sibling)
|
|
2936
|
+
// moves by looking at the impact of the move operation on the node
|
|
2937
|
+
// after the original move path.
|
|
2938
|
+
const inversePath = Path.transform(path, op);
|
|
2939
|
+
const inverseNewPath = Path.transform(Path.next(path), op);
|
|
2940
|
+
return { ...op, path: inversePath, newPath: inverseNewPath };
|
|
2777
2941
|
}
|
|
2778
2942
|
case 'set_node': {
|
|
2779
|
-
const {
|
|
2780
|
-
|
|
2781
|
-
|
|
2943
|
+
const { properties, newProperties } = op;
|
|
2944
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
2945
|
+
}
|
|
2946
|
+
case 'set_selection': {
|
|
2947
|
+
const { properties, newProperties } = op;
|
|
2948
|
+
if (properties == null) {
|
|
2949
|
+
return {
|
|
2950
|
+
...op,
|
|
2951
|
+
properties: newProperties,
|
|
2952
|
+
newProperties: null
|
|
2953
|
+
};
|
|
2782
2954
|
}
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
}
|
|
2789
|
-
else {
|
|
2790
|
-
node[key] = value;
|
|
2791
|
-
}
|
|
2955
|
+
else if (newProperties == null) {
|
|
2956
|
+
return {
|
|
2957
|
+
...op,
|
|
2958
|
+
properties: null,
|
|
2959
|
+
newProperties: properties
|
|
2960
|
+
};
|
|
2792
2961
|
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
if (!newProperties.hasOwnProperty(key)) {
|
|
2796
|
-
delete node[key];
|
|
2797
|
-
}
|
|
2962
|
+
else {
|
|
2963
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
2798
2964
|
}
|
|
2799
|
-
break;
|
|
2800
2965
|
}
|
|
2801
2966
|
case 'set_viewport': {
|
|
2802
|
-
const { newProperties } = op;
|
|
2803
|
-
if (
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
|
|
2810
|
-
}
|
|
2811
|
-
viewport = { ...newProperties };
|
|
2812
|
-
}
|
|
2813
|
-
for (const key in newProperties) {
|
|
2814
|
-
const value = newProperties[key];
|
|
2815
|
-
if (value == null) {
|
|
2816
|
-
delete viewport[key];
|
|
2817
|
-
}
|
|
2818
|
-
else {
|
|
2819
|
-
viewport[key] = value;
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2967
|
+
const { properties, newProperties } = op;
|
|
2968
|
+
if (properties == null) {
|
|
2969
|
+
return {
|
|
2970
|
+
...op,
|
|
2971
|
+
properties: newProperties,
|
|
2972
|
+
newProperties: newProperties
|
|
2973
|
+
};
|
|
2822
2974
|
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2975
|
+
else if (newProperties == null) {
|
|
2976
|
+
return {
|
|
2977
|
+
...op,
|
|
2978
|
+
properties: properties,
|
|
2979
|
+
newProperties: properties
|
|
2980
|
+
};
|
|
2829
2981
|
}
|
|
2830
2982
|
else {
|
|
2831
|
-
|
|
2832
|
-
selection = op.newProperties;
|
|
2833
|
-
}
|
|
2834
|
-
else {
|
|
2835
|
-
selection = newProperties;
|
|
2836
|
-
}
|
|
2983
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
2837
2984
|
}
|
|
2838
|
-
break;
|
|
2839
2985
|
}
|
|
2840
2986
|
case 'set_theme': {
|
|
2841
|
-
const { newProperties } = op;
|
|
2842
|
-
|
|
2843
|
-
break;
|
|
2987
|
+
const { properties, newProperties } = op;
|
|
2988
|
+
return { ...op, properties: newProperties, newProperties: properties };
|
|
2844
2989
|
}
|
|
2845
2990
|
}
|
|
2846
|
-
return { selection, viewport, theme };
|
|
2847
2991
|
};
|
|
2848
|
-
const
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
*/
|
|
2852
|
-
transform(board, op) {
|
|
2853
|
-
board.children = createDraft(board.children);
|
|
2854
|
-
let viewport = board.viewport && createDraft(board.viewport);
|
|
2855
|
-
let selection = board.selection && createDraft(board.selection);
|
|
2856
|
-
let theme = board.theme && createDraft(board.theme);
|
|
2857
|
-
try {
|
|
2858
|
-
const state = applyToDraft(board, selection, viewport, theme, op);
|
|
2859
|
-
viewport = state.viewport;
|
|
2860
|
-
selection = state.selection;
|
|
2861
|
-
theme = state.theme;
|
|
2862
|
-
}
|
|
2863
|
-
finally {
|
|
2864
|
-
board.children = finishDraft(board.children);
|
|
2865
|
-
if (selection) {
|
|
2866
|
-
board.selection = isDraft(selection) ? finishDraft(selection) : selection;
|
|
2867
|
-
}
|
|
2868
|
-
else {
|
|
2869
|
-
board.selection = null;
|
|
2870
|
-
}
|
|
2871
|
-
board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
|
|
2872
|
-
board.theme = isDraft(theme) ? finishDraft(theme) : theme;
|
|
2873
|
-
}
|
|
2874
|
-
}
|
|
2992
|
+
const PlaitOperation = {
|
|
2993
|
+
isSetViewportOperation,
|
|
2994
|
+
inverse
|
|
2875
2995
|
};
|
|
2876
2996
|
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
}
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2997
|
+
const Point = {
|
|
2998
|
+
isEquals(point, otherPoint) {
|
|
2999
|
+
return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
|
|
3000
|
+
},
|
|
3001
|
+
isHorizontal(point, otherPoint, tolerance = 0) {
|
|
3002
|
+
return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
|
|
3003
|
+
},
|
|
3004
|
+
isOverHorizontal(points, tolerance = 0) {
|
|
3005
|
+
return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
|
|
3006
|
+
},
|
|
3007
|
+
isVertical(point, otherPoint, tolerance = 0) {
|
|
3008
|
+
return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
|
|
3009
|
+
},
|
|
3010
|
+
isOverVertical(points, tolerance = 0) {
|
|
3011
|
+
return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
|
|
3012
|
+
},
|
|
3013
|
+
isAlign(points, tolerance = 0) {
|
|
3014
|
+
return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
|
|
3015
|
+
},
|
|
3016
|
+
getOffsetX(point1, point2) {
|
|
3017
|
+
return point2[0] - point1[0];
|
|
3018
|
+
},
|
|
3019
|
+
getOffsetY(point1, point2) {
|
|
3020
|
+
return point2[1] - point1[1];
|
|
2893
3021
|
}
|
|
2894
|
-
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2895
|
-
board.apply(operation);
|
|
2896
|
-
}
|
|
2897
|
-
function removeNode(board, path) {
|
|
2898
|
-
const node = PlaitNode.get(board, path);
|
|
2899
|
-
const operation = { type: 'remove_node', path, node };
|
|
2900
|
-
board.apply(operation);
|
|
2901
|
-
}
|
|
2902
|
-
function moveNode(board, path, newPath) {
|
|
2903
|
-
const operation = { type: 'move_node', path, newPath };
|
|
2904
|
-
board.apply(operation);
|
|
2905
|
-
}
|
|
2906
|
-
const NodeTransforms = {
|
|
2907
|
-
insertNode,
|
|
2908
|
-
setNode,
|
|
2909
|
-
removeNode,
|
|
2910
|
-
moveNode
|
|
2911
3022
|
};
|
|
2912
3023
|
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
3024
|
+
const SAVING = new WeakMap();
|
|
3025
|
+
const MERGING = new WeakMap();
|
|
3026
|
+
|
|
3027
|
+
var ThemeColorMode;
|
|
3028
|
+
(function (ThemeColorMode) {
|
|
3029
|
+
ThemeColorMode["default"] = "default";
|
|
3030
|
+
ThemeColorMode["colorful"] = "colorful";
|
|
3031
|
+
ThemeColorMode["soft"] = "soft";
|
|
3032
|
+
ThemeColorMode["retro"] = "retro";
|
|
3033
|
+
ThemeColorMode["dark"] = "dark";
|
|
3034
|
+
ThemeColorMode["starry"] = "starry";
|
|
3035
|
+
})(ThemeColorMode || (ThemeColorMode = {}));
|
|
3036
|
+
const DefaultThemeColor = {
|
|
3037
|
+
mode: ThemeColorMode.default,
|
|
3038
|
+
boardBackground: '#ffffff',
|
|
3039
|
+
textColor: '#333333'
|
|
3040
|
+
};
|
|
3041
|
+
const ColorfulThemeColor = {
|
|
3042
|
+
mode: ThemeColorMode.colorful,
|
|
3043
|
+
boardBackground: '#ffffff',
|
|
3044
|
+
textColor: '#333333'
|
|
3045
|
+
};
|
|
3046
|
+
const SoftThemeColor = {
|
|
3047
|
+
mode: ThemeColorMode.soft,
|
|
3048
|
+
boardBackground: '#f5f5f5',
|
|
3049
|
+
textColor: '#333333'
|
|
3050
|
+
};
|
|
3051
|
+
const RetroThemeColor = {
|
|
3052
|
+
mode: ThemeColorMode.retro,
|
|
3053
|
+
boardBackground: '#f9f8ed',
|
|
3054
|
+
textColor: '#333333'
|
|
3055
|
+
};
|
|
3056
|
+
const DarkThemeColor = {
|
|
3057
|
+
mode: ThemeColorMode.dark,
|
|
3058
|
+
boardBackground: '#141414',
|
|
3059
|
+
textColor: '#FFFFFF'
|
|
3060
|
+
};
|
|
3061
|
+
const StarryThemeColor = {
|
|
3062
|
+
mode: ThemeColorMode.starry,
|
|
3063
|
+
boardBackground: '#0d2537',
|
|
3064
|
+
textColor: '#FFFFFF'
|
|
3065
|
+
};
|
|
3066
|
+
const ThemeColors = [
|
|
3067
|
+
DefaultThemeColor,
|
|
3068
|
+
ColorfulThemeColor,
|
|
3069
|
+
SoftThemeColor,
|
|
3070
|
+
RetroThemeColor,
|
|
3071
|
+
DarkThemeColor,
|
|
3072
|
+
StarryThemeColor
|
|
3073
|
+
];
|
|
3074
|
+
|
|
3075
|
+
var Direction;
|
|
3076
|
+
(function (Direction) {
|
|
3077
|
+
Direction["left"] = "left";
|
|
3078
|
+
Direction["top"] = "top";
|
|
3079
|
+
Direction["right"] = "right";
|
|
3080
|
+
Direction["bottom"] = "bottom";
|
|
3081
|
+
})(Direction || (Direction = {}));
|
|
3082
|
+
|
|
3083
|
+
function getRectangleByElements(board, elements, recursion) {
|
|
3084
|
+
const rectangles = [];
|
|
3085
|
+
const callback = (node) => {
|
|
3086
|
+
const nodeRectangle = board.getRectangle(node);
|
|
3087
|
+
if (nodeRectangle) {
|
|
3088
|
+
rectangles.push(nodeRectangle);
|
|
2934
3089
|
}
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
const hitElement = getHitElementByPoint(board, point);
|
|
2938
|
-
const hitSelectedElements = selectedElements.length > 1 ? getHitSelectedElements(board, point) : [];
|
|
2939
|
-
const isHitTarget = hitElement || hitSelectedElements.length > 0;
|
|
2940
|
-
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
2941
|
-
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && !isHitTarget && options.isMultiple && !options.isDisabledSelect) {
|
|
2942
|
-
preventTouchMove(board, event, true);
|
|
2943
|
-
// start rectangle selection
|
|
2944
|
-
start = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3090
|
+
else {
|
|
3091
|
+
console.error(`can not get rectangle of element:`, node);
|
|
2945
3092
|
}
|
|
2946
|
-
pointerDown(event);
|
|
2947
3093
|
};
|
|
2948
|
-
|
|
2949
|
-
if (
|
|
2950
|
-
|
|
2951
|
-
event.preventDefault();
|
|
2952
|
-
}
|
|
2953
|
-
if (start && PlaitBoard.isPointer(board, PlaitPointerType.selection)) {
|
|
2954
|
-
const movedTarget = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
2955
|
-
const rectangle = RectangleClient.getRectangleByPoints([start, movedTarget]);
|
|
2956
|
-
selectionMovingG?.remove();
|
|
2957
|
-
if (Math.hypot(rectangle.width, rectangle.height) > PRESS_AND_MOVE_BUFFER || isSelectionMoving(board)) {
|
|
2958
|
-
end = movedTarget;
|
|
2959
|
-
throttleRAF(board, 'with-selection', () => {
|
|
2960
|
-
if (start && end) {
|
|
2961
|
-
Transforms.setSelection(board, { anchor: start, focus: end });
|
|
2962
|
-
}
|
|
2963
|
-
});
|
|
2964
|
-
setSelectionMoving(board);
|
|
2965
|
-
selectionMovingG = drawRectangle(board, rectangle, {
|
|
2966
|
-
stroke: SELECTION_BORDER_COLOR,
|
|
2967
|
-
strokeWidth: 1,
|
|
2968
|
-
fill: SELECTION_FILL_COLOR,
|
|
2969
|
-
fillStyle: 'solid'
|
|
2970
|
-
});
|
|
2971
|
-
PlaitBoard.getElementActiveHost(board).append(selectionMovingG);
|
|
2972
|
-
}
|
|
3094
|
+
elements.forEach(element => {
|
|
3095
|
+
if (recursion) {
|
|
3096
|
+
depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
|
|
2973
3097
|
}
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
// handle the end of click select
|
|
2977
|
-
board.pointerUp = (event) => {
|
|
2978
|
-
const isSetSelectionPointer = PlaitBoard.isPointer(board, PlaitPointerType.selection) || PlaitBoard.isPointer(board, PlaitPointerType.hand);
|
|
2979
|
-
const isSkip = !isMainPointer(event) || isDragging(board) || !isSetSelectionPointer;
|
|
2980
|
-
if (isSkip) {
|
|
2981
|
-
pointerUp(event);
|
|
2982
|
-
return;
|
|
3098
|
+
else {
|
|
3099
|
+
callback(element);
|
|
2983
3100
|
}
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3101
|
+
});
|
|
3102
|
+
if (rectangles.length > 0) {
|
|
3103
|
+
return RectangleClient.getBoundingRectangle(rectangles);
|
|
3104
|
+
}
|
|
3105
|
+
else {
|
|
3106
|
+
return {
|
|
3107
|
+
x: 0,
|
|
3108
|
+
y: 0,
|
|
3109
|
+
width: 0,
|
|
3110
|
+
height: 0
|
|
3111
|
+
};
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
function getBoardRectangle(board) {
|
|
3115
|
+
return getRectangleByElements(board, board.children, true);
|
|
3116
|
+
}
|
|
3117
|
+
function getElementById(board, id, dataSource) {
|
|
3118
|
+
if (!dataSource) {
|
|
3119
|
+
dataSource = findElements(board, { match: element => true, recursion: element => true });
|
|
3120
|
+
}
|
|
3121
|
+
let element = dataSource.find(element => element.id === id);
|
|
3122
|
+
return element;
|
|
3123
|
+
}
|
|
3124
|
+
function findElements(board, options) {
|
|
3125
|
+
let elements = [];
|
|
3126
|
+
const isReverse = options.isReverse ?? true;
|
|
3127
|
+
depthFirstRecursion(board, node => {
|
|
3128
|
+
if (!PlaitBoard.isBoard(node) && options.match(node)) {
|
|
3129
|
+
elements.push(node);
|
|
2994
3130
|
}
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
|
|
2999
|
-
// Clear selection when mouse board outside area
|
|
3000
|
-
// The framework needs to determine whether the board is focused through selection
|
|
3001
|
-
if (!isInBoard && !start && !isAttachedElement && isInDocument) {
|
|
3002
|
-
Transforms.setSelection(board, null);
|
|
3003
|
-
}
|
|
3131
|
+
}, (value) => {
|
|
3132
|
+
if (PlaitBoard.isBoard(value)) {
|
|
3133
|
+
return true;
|
|
3004
3134
|
}
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
isTextSelection = false;
|
|
3008
|
-
preventTouchMove(board, event, false);
|
|
3009
|
-
globalPointerUp(event);
|
|
3010
|
-
};
|
|
3011
|
-
board.onChange = () => {
|
|
3012
|
-
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
3013
|
-
if (options.isDisabledSelect) {
|
|
3014
|
-
clearSelectedElement(board);
|
|
3135
|
+
else {
|
|
3136
|
+
return getIsRecursionFunc(board)(value) && options.recursion(value);
|
|
3015
3137
|
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
selectionRectangleG?.remove();
|
|
3026
|
-
}
|
|
3027
|
-
const temporaryElements = getTemporaryElements(board);
|
|
3028
|
-
let elements = temporaryElements ? temporaryElements : getHitElementsBySelection(board);
|
|
3029
|
-
if (!options.isMultiple && elements.length > 1) {
|
|
3030
|
-
elements = [elements[0]];
|
|
3031
|
-
}
|
|
3032
|
-
if (isShift) {
|
|
3033
|
-
if (board.selection && Selection.isCollapsed(board.selection)) {
|
|
3034
|
-
const newSelectedElements = [...getSelectedElements(board)];
|
|
3035
|
-
elements.forEach(element => {
|
|
3036
|
-
if (newSelectedElements.includes(element)) {
|
|
3037
|
-
newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
|
|
3038
|
-
}
|
|
3039
|
-
else {
|
|
3040
|
-
newSelectedElements.push(element);
|
|
3041
|
-
}
|
|
3042
|
-
});
|
|
3043
|
-
cacheSelectedElements(board, newSelectedElements);
|
|
3044
|
-
}
|
|
3045
|
-
else {
|
|
3046
|
-
const newSelectedElements = [...getSelectedElements(board)];
|
|
3047
|
-
elements.forEach(element => {
|
|
3048
|
-
if (!newSelectedElements.includes(element)) {
|
|
3049
|
-
newSelectedElements.push(element);
|
|
3050
|
-
}
|
|
3051
|
-
});
|
|
3052
|
-
cacheSelectedElements(board, newSelectedElements);
|
|
3053
|
-
}
|
|
3054
|
-
}
|
|
3055
|
-
else {
|
|
3056
|
-
const newSelectedElements = [...elements];
|
|
3057
|
-
cacheSelectedElements(board, newSelectedElements);
|
|
3058
|
-
}
|
|
3059
|
-
const newElements = getSelectedElements(board);
|
|
3060
|
-
previousSelectedElements = newElements;
|
|
3061
|
-
deleteTemporaryElements(board);
|
|
3062
|
-
if (!isSelectionMoving(board) && newElements.length > 1) {
|
|
3063
|
-
selectionRectangleG?.remove();
|
|
3064
|
-
selectionRectangleG = createSelectionRectangleG(board);
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
catch (error) {
|
|
3068
|
-
console.error(error);
|
|
3069
|
-
}
|
|
3138
|
+
}, isReverse);
|
|
3139
|
+
return elements;
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
const PlaitBoard = {
|
|
3143
|
+
isBoard(value) {
|
|
3144
|
+
const cachedIsBoard = IS_BOARD_CACHE.get(value);
|
|
3145
|
+
if (cachedIsBoard !== undefined) {
|
|
3146
|
+
return cachedIsBoard;
|
|
3070
3147
|
}
|
|
3071
|
-
onChange
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
}
|
|
3148
|
+
const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
|
|
3149
|
+
IS_BOARD_CACHE.set(value, isBoard);
|
|
3150
|
+
return isBoard;
|
|
3151
|
+
},
|
|
3152
|
+
findPath(board, node) {
|
|
3153
|
+
const path = [];
|
|
3154
|
+
let child = node;
|
|
3155
|
+
while (true) {
|
|
3156
|
+
const parent = NODE_TO_PARENT.get(child);
|
|
3157
|
+
if (parent == null) {
|
|
3158
|
+
if (PlaitBoard.isBoard(child)) {
|
|
3159
|
+
return path;
|
|
3084
3160
|
}
|
|
3085
3161
|
else {
|
|
3086
|
-
|
|
3162
|
+
break;
|
|
3087
3163
|
}
|
|
3088
3164
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3165
|
+
const i = NODE_TO_INDEX.get(child);
|
|
3166
|
+
if (i == null) {
|
|
3167
|
+
break;
|
|
3091
3168
|
}
|
|
3169
|
+
path.unshift(i);
|
|
3170
|
+
child = parent;
|
|
3092
3171
|
}
|
|
3093
|
-
|
|
3094
|
-
}
|
|
3095
|
-
board
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
}
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
return
|
|
3112
|
-
}
|
|
3113
|
-
|
|
3114
|
-
return
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
}
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
PlaitBoard.
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
board.apply(operation);
|
|
3155
|
-
}
|
|
3156
|
-
const SelectionTransforms = {
|
|
3157
|
-
setSelection,
|
|
3158
|
-
addSelectionWithTemporaryElements
|
|
3159
|
-
};
|
|
3160
|
-
function addSelectionWithTemporaryElements(board, elements) {
|
|
3161
|
-
const timeoutId = setTimeout(() => {
|
|
3162
|
-
setSelection(board, { anchor: [0, 0], focus: [0, 0] });
|
|
3163
|
-
}, 0);
|
|
3164
|
-
let ref = getTemporaryRef(board);
|
|
3165
|
-
if (ref) {
|
|
3166
|
-
clearTimeout(ref.timeoutId);
|
|
3167
|
-
const currentElements = ref.elements;
|
|
3168
|
-
ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
|
|
3169
|
-
ref.timeoutId = timeoutId;
|
|
3170
|
-
}
|
|
3171
|
-
else {
|
|
3172
|
-
BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
|
|
3172
|
+
throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
|
|
3173
|
+
},
|
|
3174
|
+
getHost(board) {
|
|
3175
|
+
return BOARD_TO_HOST.get(board);
|
|
3176
|
+
},
|
|
3177
|
+
getElementHost(board) {
|
|
3178
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.host;
|
|
3179
|
+
},
|
|
3180
|
+
getElementUpperHost(board) {
|
|
3181
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
|
|
3182
|
+
},
|
|
3183
|
+
getElementActiveHost(board) {
|
|
3184
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
|
|
3185
|
+
},
|
|
3186
|
+
getRoughSVG(board) {
|
|
3187
|
+
return BOARD_TO_ROUGH_SVG.get(board);
|
|
3188
|
+
},
|
|
3189
|
+
getComponent(board) {
|
|
3190
|
+
return BOARD_TO_COMPONENT.get(board);
|
|
3191
|
+
},
|
|
3192
|
+
getBoardContainer(board) {
|
|
3193
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.container;
|
|
3194
|
+
},
|
|
3195
|
+
getRectangle(board) {
|
|
3196
|
+
return getRectangleByElements(board, board.children, true);
|
|
3197
|
+
},
|
|
3198
|
+
getViewportContainer(board) {
|
|
3199
|
+
return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
|
|
3200
|
+
},
|
|
3201
|
+
isFocus(board) {
|
|
3202
|
+
return !!board.selection;
|
|
3203
|
+
},
|
|
3204
|
+
isReadonly(board) {
|
|
3205
|
+
return board.options.readonly;
|
|
3206
|
+
},
|
|
3207
|
+
hasBeenTextEditing(board) {
|
|
3208
|
+
return !!IS_TEXT_EDITABLE.get(board);
|
|
3209
|
+
},
|
|
3210
|
+
getPointer(board) {
|
|
3211
|
+
return board.pointer;
|
|
3212
|
+
},
|
|
3213
|
+
isPointer(board, pointer) {
|
|
3214
|
+
return board.pointer === pointer;
|
|
3215
|
+
},
|
|
3216
|
+
isInPointer(board, pointers) {
|
|
3217
|
+
const point = board.pointer;
|
|
3218
|
+
return pointers.includes(point);
|
|
3219
|
+
},
|
|
3220
|
+
getMovingPointInBoard(board) {
|
|
3221
|
+
return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
|
|
3222
|
+
},
|
|
3223
|
+
isMovingPointInBoard(board) {
|
|
3224
|
+
const point = BOARD_TO_MOVING_POINT.get(board);
|
|
3225
|
+
const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
|
|
3226
|
+
if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
|
|
3227
|
+
return true;
|
|
3228
|
+
}
|
|
3229
|
+
return false;
|
|
3230
|
+
},
|
|
3231
|
+
getThemeColors(board) {
|
|
3232
|
+
return (board.options.themeColors || ThemeColors);
|
|
3173
3233
|
}
|
|
3174
|
-
}
|
|
3175
|
-
|
|
3176
|
-
const removeElements = (board, elements) => {
|
|
3177
|
-
elements
|
|
3178
|
-
.map(element => {
|
|
3179
|
-
const path = PlaitBoard.findPath(board, element);
|
|
3180
|
-
const ref = board.pathRef(path);
|
|
3181
|
-
return () => {
|
|
3182
|
-
removeNode(board, ref.current);
|
|
3183
|
-
ref.unref();
|
|
3184
|
-
removeSelectedElement(board, element, true);
|
|
3185
|
-
};
|
|
3186
|
-
})
|
|
3187
|
-
.forEach(action => {
|
|
3188
|
-
action();
|
|
3189
|
-
});
|
|
3190
|
-
};
|
|
3191
|
-
const CoreTransforms = {
|
|
3192
|
-
removeElements
|
|
3193
|
-
};
|
|
3194
|
-
|
|
3195
|
-
const Transforms = {
|
|
3196
|
-
...GeneralTransforms,
|
|
3197
|
-
...ViewportTransforms$1,
|
|
3198
|
-
...SelectionTransforms,
|
|
3199
|
-
...NodeTransforms
|
|
3200
3234
|
};
|
|
3201
3235
|
|
|
3202
3236
|
const PathRef = {
|
|
@@ -3303,6 +3337,7 @@ function createBoard(children, options) {
|
|
|
3303
3337
|
isWithinSelection: element => false,
|
|
3304
3338
|
isRectangleHit: element => false,
|
|
3305
3339
|
isHit: element => false,
|
|
3340
|
+
isInsidePoint: element => false,
|
|
3306
3341
|
isRecursion: element => true,
|
|
3307
3342
|
isMovable: element => false,
|
|
3308
3343
|
getRectangle: element => null,
|
|
@@ -3478,6 +3513,274 @@ function withHandPointer(board) {
|
|
|
3478
3513
|
return board;
|
|
3479
3514
|
}
|
|
3480
3515
|
|
|
3516
|
+
function withSelection(board) {
|
|
3517
|
+
const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange } = board;
|
|
3518
|
+
let start = null;
|
|
3519
|
+
let end = null;
|
|
3520
|
+
let selectionMovingG;
|
|
3521
|
+
let selectionRectangleG;
|
|
3522
|
+
let previousSelectedElements;
|
|
3523
|
+
let isShift = false;
|
|
3524
|
+
let isTextSelection = false;
|
|
3525
|
+
board.pointerDown = (event) => {
|
|
3526
|
+
if (!isShift && event.shiftKey) {
|
|
3527
|
+
isShift = true;
|
|
3528
|
+
}
|
|
3529
|
+
if (isShift && !event.shiftKey) {
|
|
3530
|
+
isShift = false;
|
|
3531
|
+
}
|
|
3532
|
+
const isHitText = !!(event.target instanceof Element && event.target.closest('.plait-richtext-container'));
|
|
3533
|
+
isTextSelection = isHitText && PlaitBoard.hasBeenTextEditing(board);
|
|
3534
|
+
// prevent text from being selected
|
|
3535
|
+
if (event.shiftKey && !isTextSelection) {
|
|
3536
|
+
event.preventDefault();
|
|
3537
|
+
}
|
|
3538
|
+
const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3539
|
+
const selectedElements = getSelectedElements(board);
|
|
3540
|
+
const hitElement = getHitElementByPoint(board, point);
|
|
3541
|
+
const hitSelectedElements = selectedElements.length > 1 ? getHitSelectedElements(board, point) : [];
|
|
3542
|
+
const isHitTarget = hitElement || hitSelectedElements.length > 0;
|
|
3543
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
3544
|
+
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && !isHitTarget && options.isMultiple && !options.isDisabledSelect) {
|
|
3545
|
+
preventTouchMove(board, event, true);
|
|
3546
|
+
// start rectangle selection
|
|
3547
|
+
start = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3548
|
+
}
|
|
3549
|
+
pointerDown(event);
|
|
3550
|
+
};
|
|
3551
|
+
board.pointerMove = (event) => {
|
|
3552
|
+
if (!isTextSelection) {
|
|
3553
|
+
// prevent text from being selected
|
|
3554
|
+
event.preventDefault();
|
|
3555
|
+
}
|
|
3556
|
+
if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && start) {
|
|
3557
|
+
const movedTarget = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3558
|
+
const rectangle = RectangleClient.getRectangleByPoints([start, movedTarget]);
|
|
3559
|
+
selectionMovingG?.remove();
|
|
3560
|
+
if (Math.hypot(rectangle.width, rectangle.height) > PRESS_AND_MOVE_BUFFER || isSelectionMoving(board)) {
|
|
3561
|
+
end = movedTarget;
|
|
3562
|
+
throttleRAF(board, 'with-selection', () => {
|
|
3563
|
+
if (start && end) {
|
|
3564
|
+
Transforms.setSelection(board, { anchor: start, focus: end });
|
|
3565
|
+
}
|
|
3566
|
+
});
|
|
3567
|
+
setSelectionMoving(board);
|
|
3568
|
+
selectionMovingG = drawRectangle(board, rectangle, {
|
|
3569
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3570
|
+
strokeWidth: 1,
|
|
3571
|
+
fill: SELECTION_FILL_COLOR,
|
|
3572
|
+
fillStyle: 'solid'
|
|
3573
|
+
});
|
|
3574
|
+
PlaitBoard.getElementActiveHost(board).append(selectionMovingG);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
pointerMove(event);
|
|
3578
|
+
};
|
|
3579
|
+
// handle the end of click select
|
|
3580
|
+
board.pointerUp = (event) => {
|
|
3581
|
+
const isSetSelectionPointer = PlaitBoard.isPointer(board, PlaitPointerType.selection) || PlaitBoard.isPointer(board, PlaitPointerType.hand);
|
|
3582
|
+
const isSkip = !isMainPointer(event) || isDragging(board) || !isSetSelectionPointer;
|
|
3583
|
+
if (isSkip) {
|
|
3584
|
+
pointerUp(event);
|
|
3585
|
+
return;
|
|
3586
|
+
}
|
|
3587
|
+
const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3588
|
+
const selection = { anchor: point, focus: point };
|
|
3589
|
+
Transforms.setSelection(board, selection);
|
|
3590
|
+
pointerUp(event);
|
|
3591
|
+
};
|
|
3592
|
+
board.globalPointerUp = (event) => {
|
|
3593
|
+
if (start && end) {
|
|
3594
|
+
selectionMovingG?.remove();
|
|
3595
|
+
clearSelectionMoving(board);
|
|
3596
|
+
Transforms.setSelection(board, { anchor: start, focus: end });
|
|
3597
|
+
}
|
|
3598
|
+
if (PlaitBoard.isFocus(board)) {
|
|
3599
|
+
const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
|
|
3600
|
+
const isInDocument = event.target instanceof Node && document.contains(event.target);
|
|
3601
|
+
const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
|
|
3602
|
+
// Clear selection when mouse board outside area
|
|
3603
|
+
// The framework needs to determine whether the board is focused through selection
|
|
3604
|
+
if (!isInBoard && !start && !isAttachedElement && isInDocument) {
|
|
3605
|
+
Transforms.setSelection(board, null);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
start = null;
|
|
3609
|
+
end = null;
|
|
3610
|
+
isTextSelection = false;
|
|
3611
|
+
preventTouchMove(board, event, false);
|
|
3612
|
+
globalPointerUp(event);
|
|
3613
|
+
};
|
|
3614
|
+
board.onChange = () => {
|
|
3615
|
+
const options = board.getPluginOptions(PlaitPluginKey.withSelection);
|
|
3616
|
+
if (options.isDisabledSelect) {
|
|
3617
|
+
clearSelectedElement(board);
|
|
3618
|
+
}
|
|
3619
|
+
// remove selected element if include
|
|
3620
|
+
board.operations.forEach(op => {
|
|
3621
|
+
if (op.type === 'remove_node') {
|
|
3622
|
+
removeSelectedElement(board, op.node, true);
|
|
3623
|
+
}
|
|
3624
|
+
});
|
|
3625
|
+
if (isHandleSelection(board) && isSetSelectionOperation(board)) {
|
|
3626
|
+
try {
|
|
3627
|
+
if (!isShift) {
|
|
3628
|
+
selectionRectangleG?.remove();
|
|
3629
|
+
}
|
|
3630
|
+
const temporaryElements = getTemporaryElements(board);
|
|
3631
|
+
let elements = temporaryElements ? temporaryElements : getHitElementsBySelection(board);
|
|
3632
|
+
if (!options.isMultiple && elements.length > 1) {
|
|
3633
|
+
elements = [elements[0]];
|
|
3634
|
+
}
|
|
3635
|
+
const isHitElementWithGroup = elements.some(item => item.groupId);
|
|
3636
|
+
if (isShift) {
|
|
3637
|
+
const newSelectedElements = [...getSelectedElements(board)];
|
|
3638
|
+
if (board.selection && Selection.isCollapsed(board.selection)) {
|
|
3639
|
+
if (isHitElementWithGroup) {
|
|
3640
|
+
let pendingElements = [...elements];
|
|
3641
|
+
const hitElement = elements[0];
|
|
3642
|
+
const groups = getGroupByElement(board, hitElement, true);
|
|
3643
|
+
const selectedGroups = getSelectedGroups(board, groups);
|
|
3644
|
+
const elementsInHighestGroup = getElementsInGroup(board, groups[groups.length - 1], true);
|
|
3645
|
+
if (selectedGroups.length > 0) {
|
|
3646
|
+
if (selectedGroups.length > 1) {
|
|
3647
|
+
pendingElements = getElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
else {
|
|
3651
|
+
if (!newSelectedElements.includes(hitElement)) {
|
|
3652
|
+
const selectedElementsInGroup = elementsInHighestGroup.filter(item => newSelectedElements.includes(item));
|
|
3653
|
+
// When partially selected elements belong to a group,
|
|
3654
|
+
// only select those elements along with the hit elements.
|
|
3655
|
+
if (selectedElementsInGroup.length) {
|
|
3656
|
+
pendingElements.push(...selectedElementsInGroup);
|
|
3657
|
+
}
|
|
3658
|
+
else {
|
|
3659
|
+
pendingElements = elementsInHighestGroup;
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
else {
|
|
3663
|
+
pendingElements = [];
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
elementsInHighestGroup.forEach(element => {
|
|
3667
|
+
if (newSelectedElements.includes(element)) {
|
|
3668
|
+
newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
|
|
3669
|
+
}
|
|
3670
|
+
});
|
|
3671
|
+
if (pendingElements.length) {
|
|
3672
|
+
newSelectedElements.push(...pendingElements);
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
else {
|
|
3676
|
+
elements.forEach(element => {
|
|
3677
|
+
if (newSelectedElements.includes(element)) {
|
|
3678
|
+
newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
|
|
3679
|
+
}
|
|
3680
|
+
else {
|
|
3681
|
+
newSelectedElements.push(element);
|
|
3682
|
+
}
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
cacheSelectedElements(board, newSelectedElements);
|
|
3686
|
+
}
|
|
3687
|
+
else {
|
|
3688
|
+
let newElements = [...elements];
|
|
3689
|
+
if (isHitElementWithGroup) {
|
|
3690
|
+
elements.forEach(item => {
|
|
3691
|
+
if (!item.groupId) {
|
|
3692
|
+
newElements.push(item);
|
|
3693
|
+
}
|
|
3694
|
+
else {
|
|
3695
|
+
newElements.push(...getElementsInGroupByElement(board, item));
|
|
3696
|
+
}
|
|
3697
|
+
});
|
|
3698
|
+
}
|
|
3699
|
+
newElements.forEach(element => {
|
|
3700
|
+
if (!newSelectedElements.includes(element)) {
|
|
3701
|
+
newSelectedElements.push(element);
|
|
3702
|
+
}
|
|
3703
|
+
});
|
|
3704
|
+
cacheSelectedElements(board, newSelectedElements);
|
|
3705
|
+
}
|
|
3706
|
+
}
|
|
3707
|
+
else {
|
|
3708
|
+
let newSelectedElements = [...elements];
|
|
3709
|
+
if (isHitElementWithGroup) {
|
|
3710
|
+
const isCollapsed = Selection.isCollapsed(board.selection);
|
|
3711
|
+
if (!isCollapsed) {
|
|
3712
|
+
newSelectedElements = [];
|
|
3713
|
+
elements.forEach(item => {
|
|
3714
|
+
if (!item.groupId) {
|
|
3715
|
+
newSelectedElements.push(item);
|
|
3716
|
+
}
|
|
3717
|
+
else {
|
|
3718
|
+
newSelectedElements.push(...getElementsInGroupByElement(board, item));
|
|
3719
|
+
}
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
else {
|
|
3723
|
+
const hitElement = elements[0];
|
|
3724
|
+
const groups = getGroupByElement(board, hitElement, true);
|
|
3725
|
+
const selectedGroups = getSelectedGroups(board, groups);
|
|
3726
|
+
if (selectedGroups.length > 0) {
|
|
3727
|
+
if (selectedGroups.length > 1) {
|
|
3728
|
+
newSelectedElements = getElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
else {
|
|
3732
|
+
newSelectedElements = getElementsInGroup(board, groups[groups.length - 1], true);
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
cacheSelectedElements(board, newSelectedElements);
|
|
3737
|
+
}
|
|
3738
|
+
const newElements = getSelectedElements(board);
|
|
3739
|
+
previousSelectedElements = newElements;
|
|
3740
|
+
deleteTemporaryElements(board);
|
|
3741
|
+
if (!isSelectionMoving(board)) {
|
|
3742
|
+
selectionRectangleG?.remove();
|
|
3743
|
+
if (newElements.length > 1) {
|
|
3744
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
catch (error) {
|
|
3749
|
+
console.error(error);
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
onChange();
|
|
3753
|
+
};
|
|
3754
|
+
board.afterChange = () => {
|
|
3755
|
+
if (isHandleSelection(board) && !isSetSelectionOperation(board)) {
|
|
3756
|
+
try {
|
|
3757
|
+
const currentSelectedElements = getSelectedElements(board);
|
|
3758
|
+
if (currentSelectedElements.length && currentSelectedElements.length > 1) {
|
|
3759
|
+
if (previousSelectedElements &&
|
|
3760
|
+
(currentSelectedElements.length !== previousSelectedElements.length ||
|
|
3761
|
+
currentSelectedElements.some((c, index) => c !== previousSelectedElements[index]))) {
|
|
3762
|
+
selectionRectangleG?.remove();
|
|
3763
|
+
selectionRectangleG = createSelectionRectangleG(board);
|
|
3764
|
+
previousSelectedElements = currentSelectedElements;
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
else {
|
|
3768
|
+
selectionRectangleG?.remove();
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
catch (error) {
|
|
3772
|
+
console.error(error);
|
|
3773
|
+
}
|
|
3774
|
+
}
|
|
3775
|
+
afterChange();
|
|
3776
|
+
};
|
|
3777
|
+
board.setPluginOptions(PlaitPluginKey.withSelection, {
|
|
3778
|
+
isMultiple: true,
|
|
3779
|
+
isDisabledSelect: false
|
|
3780
|
+
});
|
|
3781
|
+
return board;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3481
3784
|
function withViewport(board) {
|
|
3482
3785
|
const { onChange } = board;
|
|
3483
3786
|
const throttleUpdate = debounce(() => {
|
|
@@ -3888,6 +4191,7 @@ function withMoving(board) {
|
|
|
3888
4191
|
isPreventTouchMove(board) ||
|
|
3889
4192
|
!isMainPointer(event)) {
|
|
3890
4193
|
pointerDown(event);
|
|
4194
|
+
return;
|
|
3891
4195
|
}
|
|
3892
4196
|
const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3893
4197
|
const targetElements = getTargetElements(board);
|
|
@@ -3903,7 +4207,7 @@ function withMoving(board) {
|
|
|
3903
4207
|
const targetElement = getHitElementByPoint(board, point, el => board.isMovable(el));
|
|
3904
4208
|
if (targetElement) {
|
|
3905
4209
|
startPoint = point;
|
|
3906
|
-
activeElements =
|
|
4210
|
+
activeElements = getElementsInGroupByElement(board, targetElement);
|
|
3907
4211
|
if (targetElements.length > 0) {
|
|
3908
4212
|
addSelectionWithTemporaryElements(board, []);
|
|
3909
4213
|
}
|
|
@@ -4367,6 +4671,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImpor
|
|
|
4367
4671
|
type: Input
|
|
4368
4672
|
}] } });
|
|
4369
4673
|
|
|
4674
|
+
function withGroup(board) {
|
|
4675
|
+
const { pointerMove, globalPointerUp } = board;
|
|
4676
|
+
let groupRectangleG;
|
|
4677
|
+
board.pointerMove = (event) => {
|
|
4678
|
+
groupRectangleG?.remove();
|
|
4679
|
+
const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
4680
|
+
let selection = { anchor: point, focus: point };
|
|
4681
|
+
if (board.selection && !Selection.isCollapsed(board.selection)) {
|
|
4682
|
+
selection = board.selection;
|
|
4683
|
+
}
|
|
4684
|
+
const hitElements = getHitElementsBySelection(board, selection);
|
|
4685
|
+
if (hitElements.length) {
|
|
4686
|
+
groupRectangleG = createGroupRectangleG(board, hitElements);
|
|
4687
|
+
groupRectangleG && PlaitBoard.getElementActiveHost(board).append(groupRectangleG);
|
|
4688
|
+
}
|
|
4689
|
+
pointerMove(event);
|
|
4690
|
+
};
|
|
4691
|
+
board.globalPointerUp = (event) => {
|
|
4692
|
+
groupRectangleG?.remove();
|
|
4693
|
+
groupRectangleG = null;
|
|
4694
|
+
globalPointerUp(event);
|
|
4695
|
+
};
|
|
4696
|
+
return board;
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4370
4699
|
const ElementHostClass = 'element-host';
|
|
4371
4700
|
const ElementUpperHostClass = 'element-upper-host';
|
|
4372
4701
|
const ElementActiveHostClass = 'element-active-host';
|
|
@@ -4492,7 +4821,7 @@ class PlaitBoardComponent {
|
|
|
4492
4821
|
initializeViewportOffset(this.board);
|
|
4493
4822
|
}
|
|
4494
4823
|
initializePlugins() {
|
|
4495
|
-
let board = withHotkey(withHandPointer(withHistory(withSelection(withMoving(withBoard(withViewport(withOptions(createBoard(this.plaitValue, this.plaitOptions)))))))));
|
|
4824
|
+
let board = withHotkey(withHandPointer(withHistory(withSelection(withGroup(withMoving(withBoard(withViewport(withOptions(createBoard(this.plaitValue, this.plaitOptions))))))))));
|
|
4496
4825
|
this.plaitPlugins.forEach(plugin => {
|
|
4497
4826
|
board = plugin(board);
|
|
4498
4827
|
});
|
|
@@ -4942,5 +5271,5 @@ function createModModifierKeys() {
|
|
|
4942
5271
|
* Generated bundle index. Do not edit.
|
|
4943
5272
|
*/
|
|
4944
5273
|
|
|
4945
|
-
export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_DRAGGING, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_INDEX, NODE_TO_PARENT, NS, NUMPAD_DIVIDE, NUMPAD_EIGHT, NUMPAD_FIVE, NUMPAD_FOUR, NUMPAD_MINUS, NUMPAD_MULTIPLY, NUMPAD_NINE, NUMPAD_ONE, NUMPAD_PERIOD, NUMPAD_PLUS, NUMPAD_SEVEN, NUMPAD_SIX, NUMPAD_THREE, NUMPAD_TWO, NUMPAD_ZERO, NUM_CENTER, NUM_LOCK, O, ONE, OPEN_SQUARE_BRACKET, P, PAGE_DOWN, PAGE_UP, PATH_REFS, PAUSE, PERIOD, PLUS_SIGN, POINTER_BUTTON, PRESS_AND_MOVE_BUFFER, PRINT_SCREEN, Path, PlaitBoard, PlaitBoardComponent, PlaitChildrenElementComponent, PlaitContextService, PlaitElement, PlaitElementComponent, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RIGHT_ARROW, RectangleClient, ResizeCursorClass, RetroThemeColor, RgbaToHEX, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, WritableClipboardType, X, Y, Z, ZERO, addClipboardContext, addSelectedElement, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, calcNewViewBox, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createFakeEvent, createForeignObject, createG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createSelectionRectangleG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, findElements, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getEllipseTangentSlope, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByElements, getSelectedElements, getTargetElements, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isContextmenu, isDOMElement, isDOMNode, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedElement, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, normalizePoint, preventTouchMove, removeMovingElements, removeSelectedElement, rotate, scrollToRectangle, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectionMoving, setStrokeLinecap,
|
|
5274
|
+
export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HIT_DISTANCE_BUFFER, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_DRAGGING, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_INDEX, NODE_TO_PARENT, NS, NUMPAD_DIVIDE, NUMPAD_EIGHT, NUMPAD_FIVE, NUMPAD_FOUR, NUMPAD_MINUS, NUMPAD_MULTIPLY, NUMPAD_NINE, NUMPAD_ONE, NUMPAD_PERIOD, NUMPAD_PLUS, NUMPAD_SEVEN, NUMPAD_SIX, NUMPAD_THREE, NUMPAD_TWO, NUMPAD_ZERO, NUM_CENTER, NUM_LOCK, O, ONE, OPEN_SQUARE_BRACKET, P, PAGE_DOWN, PAGE_UP, PATH_REFS, PAUSE, PERIOD, PLUS_SIGN, POINTER_BUTTON, PRESS_AND_MOVE_BUFFER, PRINT_SCREEN, Path, PlaitBoard, PlaitBoardComponent, PlaitChildrenElementComponent, PlaitContextService, PlaitElement, PlaitElementComponent, PlaitGroupElement, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RIGHT_ARROW, RectangleClient, ResizeCursorClass, RetroThemeColor, RgbaToHEX, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, WritableClipboardType, X, Y, Z, ZERO, addClipboardContext, addGroup, addSelectedElement, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, calcNewViewBox, canAddGroup, canRemoveGroup, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createFakeEvent, createForeignObject, createG, createGroup, createGroupRectangleG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createSelectionRectangleG, createTestingBoard, createText, createTouchEvent, debounce, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, findElements, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getElementsInGroup, getElementsInGroupByElement, getEllipseTangentSlope, getGroupByElement, getHighestGroup, getHighestSelectedElements, getHighestSelectedGroup, getHighestSelectedGroups, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByElements, getRectangleByGroup, getSelectedElements, getSelectedGroups, getSelectedIsolatedElements, getTargetElements, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hasSelectedElementsInSameGroup, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isContextmenu, isDOMElement, isDOMNode, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedAllElementsInGroup, isSelectedElement, isSelectedElementOrGroup, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, nonGroupInHighestSelectedElements, normalizePoint, preventTouchMove, removeGroup, removeMovingElements, removeSelectedElement, rotate, scrollToRectangle, setAngleForG, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
|
|
4946
5275
|
//# sourceMappingURL=plait-core.mjs.map
|