@plait/core 0.53.0 → 0.54.0

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.
Files changed (38) hide show
  1. package/board/board.component.d.ts +1 -1
  2. package/constants/selection.d.ts +1 -0
  3. package/esm2022/board/board.component.mjs +6 -4
  4. package/esm2022/constants/selection.mjs +2 -1
  5. package/esm2022/interfaces/board.mjs +5 -1
  6. package/esm2022/interfaces/path.mjs +2 -2
  7. package/esm2022/interfaces/rectangle-client.mjs +4 -1
  8. package/esm2022/interfaces/selection.mjs +2 -2
  9. package/esm2022/plugins/create-board.mjs +7 -5
  10. package/esm2022/plugins/with-hotkey.mjs +3 -3
  11. package/esm2022/plugins/with-moving.mjs +7 -6
  12. package/esm2022/plugins/with-selection.mjs +7 -5
  13. package/esm2022/transforms/element.mjs +2 -2
  14. package/esm2022/utils/angle.mjs +42 -3
  15. package/esm2022/utils/common.mjs +3 -2
  16. package/esm2022/utils/debug.mjs +16 -1
  17. package/esm2022/utils/element.mjs +3 -3
  18. package/esm2022/utils/fragment.mjs +5 -0
  19. package/esm2022/utils/group.mjs +11 -5
  20. package/esm2022/utils/index.mjs +2 -1
  21. package/esm2022/utils/moving-snap.mjs +372 -0
  22. package/esm2022/utils/selection.mjs +2 -3
  23. package/esm2022/utils/weak-maps.mjs +2 -1
  24. package/fesm2022/plait-core.mjs +1092 -1015
  25. package/fesm2022/plait-core.mjs.map +1 -1
  26. package/interfaces/board.d.ts +4 -2
  27. package/interfaces/rectangle-client.d.ts +1 -0
  28. package/interfaces/selection.d.ts +1 -1
  29. package/package.json +1 -1
  30. package/utils/angle.d.ts +6 -1
  31. package/utils/debug.d.ts +1 -0
  32. package/utils/fragment.d.ts +2 -0
  33. package/utils/group.d.ts +2 -2
  34. package/utils/index.d.ts +1 -0
  35. package/utils/{reaction-manager.d.ts → moving-snap.d.ts} +3 -3
  36. package/utils/selection.d.ts +1 -1
  37. package/utils/weak-maps.d.ts +1 -0
  38. package/esm2022/utils/reaction-manager.mjs +0 -371
@@ -4,7 +4,7 @@ import rough from 'roughjs/bin/rough';
4
4
  import { timer, Subject, fromEvent } from 'rxjs';
5
5
  import { takeUntil, filter, tap } from 'rxjs/operators';
6
6
  import { isKeyHotkey, isHotkey } from 'is-hotkey';
7
- import produce, { createDraft, finishDraft, isDraft } from 'immer';
7
+ import { produce, createDraft, finishDraft, isDraft } from 'immer';
8
8
  import { NgFor } from '@angular/common';
9
9
 
10
10
  // record richtext type status
@@ -18,6 +18,7 @@ const BOARD_TO_AFTER_CHANGE = new WeakMap();
18
18
  const BOARD_TO_COMPONENT = new WeakMap();
19
19
  const BOARD_TO_ROUGH_SVG = new WeakMap();
20
20
  const BOARD_TO_HOST = new WeakMap();
21
+ const IS_BOARD_ALIVE = new WeakMap();
21
22
  const BOARD_TO_ELEMENT_HOST = new WeakMap();
22
23
  const BOARD_TO_SELECTED_ELEMENT = new WeakMap();
23
24
  const BOARD_TO_MOVING_POINT_IN_BOARD = new WeakMap();
@@ -64,7 +65,7 @@ const getIsRecursionFunc = (board) => {
64
65
  };
65
66
 
66
67
  const SELECTION_BORDER_COLOR = '#6698FF';
67
- const SELECTION_FILL_COLOR = '#6698FF19'; // 主色 0.1 透明度
68
+ const SELECTION_FILL_COLOR = '#6698FF25'; // opacity 0.25
68
69
  const Selection = {
69
70
  isCollapsed(selection) {
70
71
  if (selection.anchor[0] == selection.focus[0] && selection.anchor[1] === selection.focus[1]) {
@@ -185,6 +186,9 @@ const RectangleClient = {
185
186
  getCenterPoint: (rectangle) => {
186
187
  return [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
187
188
  },
189
+ getCenterPointByPoints: (points) => {
190
+ return RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(points));
191
+ },
188
192
  getEdgeCenterPoints: (rectangle) => {
189
193
  return [
190
194
  [rectangle.x + rectangle.width / 2, rectangle.y],
@@ -497,6 +501,7 @@ const RESIZE_CURSORS = [ResizeCursorClass.ns, ResizeCursorClass.nesw, ResizeCurs
497
501
 
498
502
  const ATTACHED_ELEMENT_CLASS_NAME = 'plait-board-attached';
499
503
  const ACTIVE_STROKE_WIDTH = 1;
504
+ const SNAPPING_STROKE_WIDTH = 2;
500
505
  const SELECTION_RECTANGLE_CLASS_NAME = 'selection-rectangle';
501
506
 
502
507
  const HOST_CLASS_NAME = 'plait-board-container';
@@ -1703,7 +1708,7 @@ const throttleRAF = (board, key, fn) => {
1703
1708
  const value = BOARD_TO_RAF.get(board) || {};
1704
1709
  value[key] = null;
1705
1710
  BOARD_TO_RAF.set(board, value);
1706
- fn();
1711
+ PlaitBoard.isAlive(board) && fn();
1707
1712
  });
1708
1713
  const state = getRAFState(board);
1709
1714
  state[key] = timerId;
@@ -2211,476 +2216,67 @@ const handleTouchTarget = (board) => {
2211
2216
  }
2212
2217
  };
2213
2218
 
2214
- const rotatePoints = (points, centerPoint, angle) => {
2215
- if (!angle) {
2216
- angle = 0;
2217
- }
2218
- if (Array.isArray(points) && typeof points[0] === 'number') {
2219
- return rotate(points[0], points[1], centerPoint[0], centerPoint[1], angle);
2220
- }
2221
- else {
2222
- return points.map(point => {
2223
- return rotate(point[0], point[1], centerPoint[0], centerPoint[1], angle);
2224
- });
2225
- }
2226
- };
2227
- const getSelectionAngle = (elements) => {
2228
- let angle = elements[0]?.angle || 0;
2229
- elements.forEach(item => {
2230
- if (item.angle !== angle && !approximately((item.angle % (Math.PI / 2)) - (angle % (Math.PI / 2)), 0)) {
2231
- angle = 0;
2232
- }
2233
- });
2234
- return angle;
2235
- };
2236
- const hasSameAngle = (elements) => {
2237
- return !!getSelectionAngle(elements);
2238
- };
2239
- const getRotatedBoundingRectangle = (rectanglesCornerPoints, angle) => {
2240
- let rectanglesFromOrigin = [];
2241
- for (let i = 0; i < rectanglesCornerPoints.length; i++) {
2242
- const cornerPoints = rectanglesCornerPoints[i];
2243
- const invertCornerPointsFromOrigin = rotatePoints(cornerPoints, [0, 0], -angle);
2244
- rectanglesFromOrigin.push(RectangleClient.getRectangleByPoints(invertCornerPointsFromOrigin));
2245
- }
2246
- const selectionRectangleFromOrigin = RectangleClient.getBoundingRectangle(rectanglesFromOrigin);
2247
- const selectionCornerPoints = RectangleClient.getCornerPoints(selectionRectangleFromOrigin);
2248
- const cornerPointsFromOrigin = rotatePoints(selectionCornerPoints, [0, 0], angle);
2249
- const centerPoint = RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(cornerPointsFromOrigin));
2250
- return RectangleClient.getRectangleByPoints(rotatePoints(cornerPointsFromOrigin, centerPoint, -angle));
2251
- };
2252
- const getOffsetAfterRotate = (rectangle, rotateCenterPoint, angle) => {
2253
- const targetCenterPoint = RectangleClient.getCenterPoint(rectangle);
2254
- const [rotatedCenterPoint] = rotatePoints([targetCenterPoint], rotateCenterPoint, angle);
2255
- const offsetX = rotatedCenterPoint[0] - targetCenterPoint[0];
2256
- const offsetY = rotatedCenterPoint[1] - targetCenterPoint[1];
2257
- return { offsetX, offsetY };
2258
- };
2259
- const rotatedDataPoints = (points, rotateCenterPoint, angle) => {
2260
- const { offsetX, offsetY } = getOffsetAfterRotate(RectangleClient.getRectangleByPoints(points), rotateCenterPoint, angle);
2261
- return points.map(p => [p[0] + offsetX, p[1] + offsetY]);
2262
- };
2263
- const hasValidAngle = (node) => {
2264
- return node.angle && node.angle !== 0;
2265
- };
2266
- const rotatePointsByElement = (points, element) => {
2267
- if (hasValidAngle(element)) {
2268
- let rectangle = RectangleClient.getRectangleByPoints(element.points);
2269
- const centerPoint = RectangleClient.getCenterPoint(rectangle);
2270
- return rotatePoints(points, centerPoint, element.angle);
2271
- }
2272
- else {
2273
- return null;
2274
- }
2275
- };
2276
- const rotateAntiPointsByElement = (points, element) => {
2277
- if (hasValidAngle(element)) {
2278
- let rectangle = RectangleClient.getRectangleByPoints(element.points);
2279
- const centerPoint = RectangleClient.getCenterPoint(rectangle);
2280
- return rotatePoints(points, centerPoint, -element.angle);
2281
- }
2282
- else {
2283
- return null;
2219
+ const Viewport = {
2220
+ isViewport: (value) => {
2221
+ return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
2284
2222
  }
2285
2223
  };
2286
2224
 
2287
- function isSelectionMoving(board) {
2288
- return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
2289
- }
2290
- function setSelectionMoving(board) {
2291
- PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
2292
- BOARD_TO_IS_SELECTION_MOVING.set(board, true);
2293
- setDragging(board, true);
2294
- }
2295
- function clearSelectionMoving(board) {
2296
- PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
2297
- BOARD_TO_IS_SELECTION_MOVING.delete(board);
2298
- setDragging(board, false);
2299
- }
2300
- function isHandleSelection(board) {
2301
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
2302
- return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
2303
- }
2304
- function isSetSelectionOperation(board) {
2305
- return !!board.operations.find(value => value.type === 'set_selection');
2306
- }
2307
- function getTemporaryElements(board) {
2308
- const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2309
- if (ref) {
2310
- return ref.elements;
2311
- }
2312
- else {
2313
- return undefined;
2314
- }
2315
- }
2316
- function getTemporaryRef(board) {
2317
- return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2318
- }
2319
- function deleteTemporaryElements(board) {
2320
- BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
2321
- }
2322
- function createSelectionRectangleG(board) {
2323
- const elements = getSelectedElements(board);
2324
- const rectangle = getRectangleByElements(board, elements, false);
2325
- if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
2326
- const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
2327
- stroke: SELECTION_BORDER_COLOR,
2328
- strokeWidth: ACTIVE_STROKE_WIDTH,
2329
- fillStyle: 'solid'
2330
- });
2331
- selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
2332
- PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
2333
- const angle = getSelectionAngle(elements);
2334
- if (angle) {
2335
- setAngleForG(selectionRectangleG, RectangleClient.getCenterPoint(rectangle), angle);
2225
+ const Path = {
2226
+ /**
2227
+ * Get a list of ancestor paths for a given path.
2228
+ *
2229
+ * The paths are sorted from shallowest to deepest ancestor. However, if the
2230
+ * `reverse: true` option is passed, they are reversed.
2231
+ */
2232
+ ancestors(path, options = {}) {
2233
+ const { reverse = false } = options;
2234
+ let paths = Path.levels(path, options);
2235
+ if (reverse) {
2236
+ paths = paths.slice(1);
2336
2237
  }
2337
- return selectionRectangleG;
2338
- }
2339
- return null;
2340
- }
2341
- function setSelectedElementsWithGroup(board, elements, isShift) {
2342
- if (!board.selection) {
2343
- return;
2344
- }
2345
- const selectedElements = getSelectedElements(board);
2346
- if (!Selection.isCollapsed(board.selection)) {
2347
- let newElements = [...selectedElements];
2348
- elements.forEach(item => {
2349
- if (!item.groupId) {
2350
- newElements.push(item);
2351
- }
2352
- else {
2353
- newElements.push(...getElementsInGroupByElement(board, item));
2354
- }
2355
- });
2356
- cacheSelectedElements(board, uniqueById(newElements));
2357
- return;
2358
- }
2359
- if (Selection.isCollapsed(board.selection)) {
2360
- const hitElement = elements[0];
2361
- const hitElementGroups = getGroupByElement(board, hitElement, true);
2362
- if (hitElementGroups.length) {
2363
- const elementsInHighestGroup = getElementsInGroup(board, hitElementGroups[hitElementGroups.length - 1], true) || [];
2364
- const isSelectGroupElement = selectedElements.some(element => elementsInHighestGroup.map(item => item.id).includes(element.id));
2365
- if (isShift) {
2366
- cacheSelectedElementsWithGroupOnShift(board, elements, isSelectGroupElement, elementsInHighestGroup);
2367
- }
2368
- else {
2369
- cacheSelectedElementsWithGroup(board, elements, isSelectGroupElement, hitElementGroups);
2370
- }
2238
+ else {
2239
+ paths = paths.slice(0, -1);
2371
2240
  }
2372
- }
2373
- }
2374
- function cacheSelectedElementsWithGroupOnShift(board, elements, isSelectGroupElement, elementsInHighestGroup) {
2375
- const selectedElements = getSelectedElements(board);
2376
- let newElements = [...selectedElements];
2377
- const hitElement = elements[0];
2378
- let pendingElements = [];
2379
- if (!isSelectGroupElement) {
2380
- pendingElements = elementsInHighestGroup;
2381
- }
2382
- else {
2383
- const isHitSelectedElement = selectedElements.some(item => item.id === hitElement.id);
2384
- const selectedElementsInGroup = elementsInHighestGroup.filter(item => selectedElements.includes(item));
2385
- if (isHitSelectedElement) {
2386
- pendingElements = selectedElementsInGroup.filter(item => item.id !== hitElement.id);
2241
+ return paths;
2242
+ },
2243
+ /**
2244
+ * Get a list of paths at every level down to a path. Note: this is the same
2245
+ * as `Path.ancestors`, but including the path itself.
2246
+ *
2247
+ * The paths are sorted from shallowest to deepest. However, if the `reverse:
2248
+ * true` option is passed, they are reversed.
2249
+ */
2250
+ levels(path, options = {}) {
2251
+ const { reverse = false } = options;
2252
+ const list = [];
2253
+ for (let i = 0; i <= path.length; i++) {
2254
+ list.push(path.slice(0, i));
2387
2255
  }
2388
- else {
2389
- pendingElements.push(...selectedElementsInGroup, ...elements);
2256
+ if (reverse) {
2257
+ list.reverse();
2390
2258
  }
2391
- }
2392
- elementsInHighestGroup.forEach(element => {
2393
- if (newElements.includes(element)) {
2394
- newElements.splice(newElements.indexOf(element), 1);
2259
+ return list;
2260
+ },
2261
+ parent(path) {
2262
+ if (path.length === 0) {
2263
+ throw new Error(`Cannot get the parent path of the root path [${path}].`);
2395
2264
  }
2396
- });
2397
- if (pendingElements.length) {
2398
- newElements.push(...pendingElements);
2399
- }
2400
- cacheSelectedElements(board, uniqueById(newElements));
2401
- }
2402
- function cacheSelectedElementsWithGroup(board, elements, isSelectGroupElement, hitElementGroups) {
2403
- let newElements = [...elements];
2404
- const selectedGroups = filterSelectedGroups(board, hitElementGroups);
2405
- if (selectedGroups.length > 0) {
2406
- if (selectedGroups.length > 1) {
2407
- newElements = getAllElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
2265
+ return path.slice(0, -1);
2266
+ },
2267
+ next(path) {
2268
+ if (path.length === 0) {
2269
+ throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
2408
2270
  }
2409
- }
2410
- else {
2411
- const elementsInGroup = getAllElementsInGroup(board, hitElementGroups[hitElementGroups.length - 1], true);
2412
- if (!isSelectGroupElement) {
2413
- newElements = elementsInGroup;
2414
- }
2415
- }
2416
- cacheSelectedElements(board, uniqueById(newElements));
2417
- }
2418
-
2419
- const getElementsInGroup = (board, group, recursion, includeGroup) => {
2420
- let result = [];
2421
- const elements = board.children.filter(value => value.groupId === group.id);
2422
- if (recursion) {
2423
- elements.forEach(item => {
2424
- if (PlaitGroupElement.isGroup(item)) {
2425
- if (includeGroup) {
2426
- result.push(item);
2427
- }
2428
- result.push(...getElementsInGroup(board, item, recursion, includeGroup));
2429
- }
2430
- else {
2431
- result.push(item);
2432
- }
2433
- });
2434
- }
2435
- else {
2436
- result = includeGroup ? elements : elements.filter(item => !PlaitGroupElement.isGroup(item));
2437
- }
2438
- return result;
2439
- };
2440
- const getAllElementsInGroup = (board, group, recursion, includeGroup) => {
2441
- const elementsInGroup = getElementsInGroup(board, group, recursion, includeGroup);
2442
- const result = [];
2443
- elementsInGroup.forEach(element => {
2444
- depthFirstRecursion(element, node => {
2445
- result.push(node);
2446
- }, () => true);
2447
- });
2448
- return result;
2449
- };
2450
- const getRectangleByGroup = (board, group, recursion) => {
2451
- const elementsInGroup = getAllElementsInGroup(board, group, recursion);
2452
- return getRectangleByElements(board, elementsInGroup, false);
2453
- };
2454
- const getGroupByElement = (board, element, recursion, source) => {
2455
- const group = (source || board.children).find(item => item.id === element?.groupId);
2456
- if (!group) {
2457
- return recursion ? [] : null;
2458
- }
2459
- if (recursion) {
2460
- const groups = [group];
2461
- const grandGroups = getGroupByElement(board, group, recursion, source);
2462
- if (grandGroups.length) {
2463
- groups.push(...grandGroups);
2464
- }
2465
- return groups;
2466
- }
2467
- else {
2468
- return group;
2469
- }
2470
- };
2471
- const getHighestGroup = (board, element) => {
2472
- const hitElementGroups = getGroupByElement(board, element, true);
2473
- if (hitElementGroups.length) {
2474
- return hitElementGroups[hitElementGroups.length - 1];
2475
- }
2476
- return null;
2477
- };
2478
- const getElementsInGroupByElement = (board, element) => {
2479
- const highestGroup = getHighestGroup(board, element);
2480
- if (highestGroup) {
2481
- return getAllElementsInGroup(board, highestGroup, true);
2482
- }
2483
- else {
2484
- return [element];
2485
- }
2486
- };
2487
- const isSelectedElementOrGroup = (board, element, elements) => {
2488
- const selectedElements = elements || getSelectedElements(board);
2489
- if (PlaitGroupElement.isGroup(element)) {
2490
- return isSelectedAllElementsInGroup(board, element, elements);
2491
- }
2492
- return selectedElements.map(item => item.id).includes(element.id);
2493
- };
2494
- const isSelectedAllElementsInGroup = (board, group, elements) => {
2495
- const selectedElements = elements || getSelectedElements(board);
2496
- const elementsInGroup = getElementsInGroup(board, group, true);
2497
- return elementsInGroup.every(item => selectedElements.map(element => element.id).includes(item.id));
2498
- };
2499
- const filterSelectedGroups = (board, groups, elements) => {
2500
- const selectedGroups = [];
2501
- groups.forEach(item => {
2502
- if (isSelectedElementOrGroup(board, item, elements)) {
2503
- selectedGroups.push(item);
2504
- }
2505
- });
2506
- return selectedGroups;
2507
- };
2508
- const getSelectedGroups = (board, elements) => {
2509
- const highestSelectedGroups = getHighestSelectedGroups(board, elements);
2510
- const groups = [];
2511
- highestSelectedGroups.forEach(item => {
2512
- groups.push(item);
2513
- const elementsInGroup = getElementsInGroup(board, item, true, true);
2514
- groups.push(...elementsInGroup.filter(item => PlaitGroupElement.isGroup(item)));
2515
- });
2516
- return groups;
2517
- };
2518
- const getHighestSelectedGroup = (board, element, elements) => {
2519
- const hitElementGroups = getGroupByElement(board, element, true, elements);
2520
- const selectedGroups = filterSelectedGroups(board, hitElementGroups, elements);
2521
- if (selectedGroups.length) {
2522
- return selectedGroups[selectedGroups.length - 1];
2523
- }
2524
- return null;
2525
- };
2526
- const getHighestSelectedGroups = (board, elements) => {
2527
- let result = [];
2528
- const selectedElements = elements || getSelectedElements(board);
2529
- selectedElements.forEach(item => {
2530
- if (item.groupId) {
2531
- const group = getHighestSelectedGroup(board, item, elements);
2532
- if (group && !result.includes(group)) {
2533
- result.push(group);
2534
- }
2535
- }
2536
- });
2537
- return result;
2538
- };
2539
- const getSelectedIsolatedElements = (board, elements) => {
2540
- let result = [];
2541
- const selectedElements = elements || getSelectedElements(board);
2542
- selectedElements
2543
- .filter(item => !PlaitGroupElement.isGroup(item))
2544
- .forEach(item => {
2545
- if (!item.groupId) {
2546
- result.push(item);
2547
- }
2548
- else {
2549
- const group = getHighestSelectedGroup(board, item, elements);
2550
- if (!group) {
2551
- result.push(item);
2552
- }
2553
- }
2554
- });
2555
- return result;
2556
- };
2557
- const getSelectedIsolatedElementsCanAddToGroup = (board, elements) => {
2558
- const selectedIsolatedElements = getSelectedIsolatedElements(board, elements);
2559
- return selectedIsolatedElements.filter(item => board.canAddToGroup(item));
2560
- };
2561
- const getHighestSelectedElements = (board, elements) => {
2562
- return [...getHighestSelectedGroups(board, elements), ...getSelectedIsolatedElements(board, elements)];
2563
- };
2564
- const createGroupRectangleG = (board, elements) => {
2565
- const selectedElements = getSelectedElements(board);
2566
- const groupRectangleG = createG();
2567
- const isMoving = isSelectionMoving(board);
2568
- elements.forEach(item => {
2569
- const isRender = (!selectedElements.includes(item) && !isMoving) || isMoving;
2570
- if (item.groupId && isRender) {
2571
- const elements = getElementsInGroupByElement(board, item);
2572
- const rectangle = getRectangleByElements(board, elements, false);
2573
- groupRectangleG.append(drawRectangle(board, rectangle, {
2574
- stroke: SELECTION_BORDER_COLOR,
2575
- strokeWidth: ACTIVE_STROKE_WIDTH,
2576
- strokeLineDash: [5]
2577
- }));
2578
- }
2579
- });
2580
- return groupRectangleG;
2581
- };
2582
- const createGroup = (groupId) => {
2583
- return groupId
2584
- ? {
2585
- id: idCreator(),
2586
- type: 'group',
2587
- groupId
2588
- }
2589
- : {
2590
- id: idCreator(),
2591
- type: 'group'
2592
- };
2593
- };
2594
- const nonGroupInHighestSelectedElements = (elements) => {
2595
- return elements.every(item => !item.groupId);
2596
- };
2597
- const hasSelectedElementsInSameGroup = (elements) => {
2598
- return elements.every(item => item.groupId && item.groupId === elements[0].groupId);
2599
- };
2600
- const canAddGroup = (board, elements) => {
2601
- const highestSelectedElements = getHighestSelectedElements(board, elements);
2602
- const rootElements = highestSelectedElements.filter(item => board.canAddToGroup(item));
2603
- if (rootElements.length > 1) {
2604
- return nonGroupInHighestSelectedElements(rootElements) || hasSelectedElementsInSameGroup(rootElements);
2605
- }
2606
- return false;
2607
- };
2608
- const canRemoveGroup = (board, elements) => {
2609
- const selectedGroups = getHighestSelectedGroups(board, elements);
2610
- const selectedElements = elements || getSelectedElements(board);
2611
- return selectedElements.length > 0 && selectedGroups.length > 0;
2612
- };
2613
-
2614
- const PlaitElement = {
2615
- isRootElement(value) {
2616
- const parent = NODE_TO_PARENT.get(value);
2617
- if (parent && PlaitBoard.isBoard(parent)) {
2618
- return true;
2619
- }
2620
- else {
2621
- return false;
2622
- }
2623
- },
2624
- getComponent(value) {
2625
- return ELEMENT_TO_COMPONENT.get(value);
2626
- }
2627
- };
2628
-
2629
- const Path = {
2630
- /**
2631
- * Get a list of ancestor paths for a given path.
2632
- *
2633
- * The paths are sorted from shallowest to deepest ancestor. However, if the
2634
- * `reverse: true` option is passed, they are reversed.
2635
- */
2636
- ancestors(path, options = {}) {
2637
- const { reverse = false } = options;
2638
- let paths = Path.levels(path, options);
2639
- if (reverse) {
2640
- paths = paths.slice(1);
2641
- }
2642
- else {
2643
- paths = paths.slice(0, -1);
2644
- }
2645
- return paths;
2646
- },
2647
- /**
2648
- * Get a list of paths at every level down to a path. Note: this is the same
2649
- * as `Path.ancestors`, but including the path itself.
2650
- *
2651
- * The paths are sorted from shallowest to deepest. However, if the `reverse:
2652
- * true` option is passed, they are reversed.
2653
- */
2654
- levels(path, options = {}) {
2655
- const { reverse = false } = options;
2656
- const list = [];
2657
- for (let i = 0; i <= path.length; i++) {
2658
- list.push(path.slice(0, i));
2659
- }
2660
- if (reverse) {
2661
- list.reverse();
2662
- }
2663
- return list;
2664
- },
2665
- parent(path) {
2666
- if (path.length === 0) {
2667
- throw new Error(`Cannot get the parent path of the root path [${path}].`);
2668
- }
2669
- return path.slice(0, -1);
2670
- },
2671
- next(path) {
2672
- if (path.length === 0) {
2673
- throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
2674
- }
2675
- const last = path[path.length - 1];
2676
- return path.slice(0, -1).concat(last + 1);
2677
- },
2678
- hasPrevious(path) {
2679
- return path[path.length - 1] > 0;
2680
- },
2681
- previous(path) {
2682
- if (path.length === 0) {
2683
- throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
2271
+ const last = path[path.length - 1];
2272
+ return path.slice(0, -1).concat(last + 1);
2273
+ },
2274
+ hasPrevious(path) {
2275
+ return path[path.length - 1] > 0;
2276
+ },
2277
+ previous(path) {
2278
+ if (path.length === 0) {
2279
+ throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
2684
2280
  }
2685
2281
  const last = path[path.length - 1];
2686
2282
  return path.slice(0, -1).concat(last - 1);
@@ -2848,625 +2444,1084 @@ const PlaitNode = {
2848
2444
  }
2849
2445
  };
2850
2446
 
2851
- const isSetViewportOperation = (value) => {
2852
- return value.type === 'set_viewport';
2853
- };
2854
- const inverse = (op) => {
2447
+ const applyToDraft = (board, selection, viewport, theme, op) => {
2855
2448
  switch (op.type) {
2856
2449
  case 'insert_node': {
2857
- return { ...op, type: 'remove_node' };
2450
+ const { path, node } = op;
2451
+ const parent = PlaitNode.parent(board, path);
2452
+ const index = path[path.length - 1];
2453
+ if (!parent.children || index > parent.children.length) {
2454
+ throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2455
+ }
2456
+ parent.children.splice(index, 0, node);
2457
+ break;
2858
2458
  }
2859
2459
  case 'remove_node': {
2860
- return { ...op, type: 'insert_node' };
2460
+ const { path } = op;
2461
+ const parent = PlaitNode.parent(board, path);
2462
+ const index = path[path.length - 1];
2463
+ if (!parent.children || index > parent.children.length) {
2464
+ throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2465
+ }
2466
+ parent.children.splice(index, 1);
2467
+ break;
2861
2468
  }
2862
2469
  case 'move_node': {
2863
- const { newPath, path } = op;
2864
- // PERF: in this case the move operation is a no-op anyways.
2865
- if (Path.equals(newPath, path)) {
2866
- return op;
2470
+ const { path, newPath } = op;
2471
+ if (Path.isAncestor(path, newPath)) {
2472
+ throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
2867
2473
  }
2868
- // when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
2869
- // shoud not return [0,2] -> [0,0] #WIK-8981
2870
- // if (Path.isSibling(path, newPath)) {
2871
- // return { ...op, path: newPath, newPath: path };
2872
- // }
2873
- // If the move does not happen within a single parent it is possible
2874
- // for the move to impact the true path to the location where the node
2875
- // was removed from and where it was inserted. We have to adjust for this
2876
- // and find the original path. We can accomplish this (only in non-sibling)
2877
- // moves by looking at the impact of the move operation on the node
2878
- // after the original move path.
2879
- const inversePath = Path.transform(path, op);
2880
- const inverseNewPath = Path.transform(Path.next(path), op);
2881
- return { ...op, path: inversePath, newPath: inverseNewPath };
2474
+ const node = PlaitNode.get(board, path);
2475
+ const parent = PlaitNode.parent(board, path);
2476
+ const index = path[path.length - 1];
2477
+ // This is tricky, but since the `path` and `newPath` both refer to
2478
+ // the same snapshot in time, there's a mismatch. After either
2479
+ // removing the original position, the second step's path can be out
2480
+ // of date. So instead of using the `op.newPath` directly, we
2481
+ // transform `op.path` to ascertain what the `newPath` would be after
2482
+ // the operation was applied.
2483
+ parent.children?.splice(index, 1);
2484
+ const truePath = Path.transform(path, op);
2485
+ const newParent = PlaitNode.get(board, Path.parent(truePath));
2486
+ const newIndex = truePath[truePath.length - 1];
2487
+ newParent.children?.splice(newIndex, 0, node);
2488
+ break;
2882
2489
  }
2883
2490
  case 'set_node': {
2884
- const { properties, newProperties } = op;
2885
- return { ...op, properties: newProperties, newProperties: properties };
2886
- }
2887
- case 'set_selection': {
2888
- const { properties, newProperties } = op;
2889
- if (properties == null) {
2890
- return {
2891
- ...op,
2892
- properties: newProperties,
2893
- newProperties: null
2894
- };
2491
+ const { path, properties, newProperties } = op;
2492
+ if (path.length === 0) {
2493
+ throw new Error(`Cannot set properties on the root node!`);
2895
2494
  }
2896
- else if (newProperties == null) {
2897
- return {
2898
- ...op,
2899
- properties: null,
2900
- newProperties: properties
2901
- };
2495
+ const node = PlaitNode.get(board, path);
2496
+ for (const key in newProperties) {
2497
+ const value = newProperties[key];
2498
+ if (value == null) {
2499
+ delete node[key];
2500
+ }
2501
+ else {
2502
+ node[key] = value;
2503
+ }
2902
2504
  }
2903
- else {
2904
- return { ...op, properties: newProperties, newProperties: properties };
2505
+ // properties that were previously defined, but are now missing, must be deleted
2506
+ for (const key in properties) {
2507
+ if (!newProperties.hasOwnProperty(key)) {
2508
+ delete node[key];
2509
+ }
2905
2510
  }
2511
+ break;
2906
2512
  }
2907
2513
  case 'set_viewport': {
2908
- const { properties, newProperties } = op;
2909
- if (properties == null) {
2910
- return {
2911
- ...op,
2912
- properties: newProperties,
2913
- newProperties: newProperties
2914
- };
2514
+ const { newProperties } = op;
2515
+ if (newProperties == null) {
2516
+ viewport = newProperties;
2915
2517
  }
2916
- else if (newProperties == null) {
2917
- return {
2918
- ...op,
2919
- properties: properties,
2920
- newProperties: properties
2921
- };
2518
+ else {
2519
+ if (viewport == null) {
2520
+ if (!Viewport.isViewport(newProperties)) {
2521
+ throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
2522
+ }
2523
+ viewport = { ...newProperties };
2524
+ }
2525
+ for (const key in newProperties) {
2526
+ const value = newProperties[key];
2527
+ if (value == null) {
2528
+ delete viewport[key];
2529
+ }
2530
+ else {
2531
+ viewport[key] = value;
2532
+ }
2533
+ }
2534
+ }
2535
+ break;
2536
+ }
2537
+ case 'set_selection': {
2538
+ const { newProperties } = op;
2539
+ if (newProperties == null) {
2540
+ selection = newProperties;
2922
2541
  }
2923
2542
  else {
2924
- return { ...op, properties: newProperties, newProperties: properties };
2543
+ if (selection === null) {
2544
+ selection = op.newProperties;
2545
+ }
2546
+ else {
2547
+ selection = newProperties;
2548
+ }
2925
2549
  }
2550
+ break;
2926
2551
  }
2927
2552
  case 'set_theme': {
2928
- const { properties, newProperties } = op;
2929
- return { ...op, properties: newProperties, newProperties: properties };
2553
+ const { newProperties } = op;
2554
+ theme = newProperties;
2555
+ break;
2930
2556
  }
2931
2557
  }
2558
+ return { selection, viewport, theme };
2932
2559
  };
2933
- const PlaitOperation = {
2934
- isSetViewportOperation,
2935
- inverse
2936
- };
2937
-
2938
- const Point = {
2939
- isEquals(point, otherPoint) {
2940
- return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
2941
- },
2942
- isHorizontal(point, otherPoint, tolerance = 0) {
2943
- return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
2944
- },
2945
- isOverHorizontal(points, tolerance = 0) {
2946
- return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
2947
- },
2948
- isVertical(point, otherPoint, tolerance = 0) {
2949
- return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
2950
- },
2951
- isOverVertical(points, tolerance = 0) {
2952
- return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
2953
- },
2954
- isAlign(points, tolerance = 0) {
2955
- return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
2956
- },
2957
- getOffsetX(point1, point2) {
2958
- return point2[0] - point1[0];
2959
- },
2960
- getOffsetY(point1, point2) {
2961
- return point2[1] - point1[1];
2560
+ const GeneralTransforms = {
2561
+ /**
2562
+ * Transform the board by an operation.
2563
+ */
2564
+ transform(board, op) {
2565
+ board.children = createDraft(board.children);
2566
+ let viewport = board.viewport && createDraft(board.viewport);
2567
+ let selection = board.selection && createDraft(board.selection);
2568
+ let theme = board.theme && createDraft(board.theme);
2569
+ try {
2570
+ const state = applyToDraft(board, selection, viewport, theme, op);
2571
+ viewport = state.viewport;
2572
+ selection = state.selection;
2573
+ theme = state.theme;
2574
+ }
2575
+ finally {
2576
+ board.children = finishDraft(board.children);
2577
+ if (selection) {
2578
+ board.selection = isDraft(selection) ? finishDraft(selection) : selection;
2579
+ }
2580
+ else {
2581
+ board.selection = null;
2582
+ }
2583
+ board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
2584
+ board.theme = isDraft(theme) ? finishDraft(theme) : theme;
2585
+ }
2962
2586
  }
2963
2587
  };
2964
2588
 
2965
- const Viewport = {
2966
- isViewport: (value) => {
2967
- return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
2589
+ function insertNode(board, node, path) {
2590
+ const operation = { type: 'insert_node', node, path };
2591
+ board.apply(operation);
2592
+ }
2593
+ function setNode(board, props, path) {
2594
+ const properties = {};
2595
+ const newProperties = {};
2596
+ const node = PlaitNode.get(board, path);
2597
+ for (const k in props) {
2598
+ if (node[k] !== props[k]) {
2599
+ if (node.hasOwnProperty(k)) {
2600
+ properties[k] = node[k];
2601
+ }
2602
+ if (props[k] != null)
2603
+ newProperties[k] = props[k];
2604
+ }
2968
2605
  }
2606
+ const operation = { type: 'set_node', properties, newProperties, path };
2607
+ board.apply(operation);
2608
+ }
2609
+ function removeNode(board, path) {
2610
+ const node = PlaitNode.get(board, path);
2611
+ const operation = { type: 'remove_node', path, node };
2612
+ board.apply(operation);
2613
+ }
2614
+ function moveNode(board, path, newPath) {
2615
+ const operation = { type: 'move_node', path, newPath };
2616
+ board.apply(operation);
2617
+ }
2618
+ const NodeTransforms = {
2619
+ insertNode,
2620
+ setNode,
2621
+ removeNode,
2622
+ moveNode
2969
2623
  };
2970
2624
 
2971
- const SAVING = new WeakMap();
2972
- const MERGING = new WeakMap();
2973
-
2974
- var ThemeColorMode;
2975
- (function (ThemeColorMode) {
2976
- ThemeColorMode["default"] = "default";
2977
- ThemeColorMode["colorful"] = "colorful";
2978
- ThemeColorMode["soft"] = "soft";
2979
- ThemeColorMode["retro"] = "retro";
2980
- ThemeColorMode["dark"] = "dark";
2981
- ThemeColorMode["starry"] = "starry";
2982
- })(ThemeColorMode || (ThemeColorMode = {}));
2983
- const DefaultThemeColor = {
2984
- mode: ThemeColorMode.default,
2985
- boardBackground: '#ffffff',
2986
- textColor: '#333333'
2987
- };
2988
- const ColorfulThemeColor = {
2989
- mode: ThemeColorMode.colorful,
2990
- boardBackground: '#ffffff',
2991
- textColor: '#333333'
2625
+ function setSelection(board, selection) {
2626
+ const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
2627
+ board.apply(operation);
2628
+ }
2629
+ const SelectionTransforms = {
2630
+ setSelection,
2631
+ addSelectionWithTemporaryElements
2992
2632
  };
2993
- const SoftThemeColor = {
2994
- mode: ThemeColorMode.soft,
2995
- boardBackground: '#f5f5f5',
2996
- textColor: '#333333'
2633
+ function addSelectionWithTemporaryElements(board, elements) {
2634
+ const timeoutId = setTimeout(() => {
2635
+ setSelection(board, { anchor: [0, 0], focus: [0, 0] });
2636
+ }, 0);
2637
+ let ref = getTemporaryRef(board);
2638
+ if (ref) {
2639
+ clearTimeout(ref.timeoutId);
2640
+ const currentElements = ref.elements;
2641
+ ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
2642
+ ref.timeoutId = timeoutId;
2643
+ }
2644
+ else {
2645
+ BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
2646
+ }
2647
+ }
2648
+
2649
+ const removeElements = (board, elements) => {
2650
+ elements
2651
+ .map(element => {
2652
+ const path = PlaitBoard.findPath(board, element);
2653
+ const ref = board.pathRef(path);
2654
+ return () => {
2655
+ ref.current && removeNode(board, ref.current);
2656
+ ref.unref();
2657
+ removeSelectedElement(board, element, true);
2658
+ };
2659
+ })
2660
+ .forEach(action => {
2661
+ action();
2662
+ });
2997
2663
  };
2998
- const RetroThemeColor = {
2999
- mode: ThemeColorMode.retro,
3000
- boardBackground: '#f9f8ed',
3001
- textColor: '#333333'
2664
+ const CoreTransforms = {
2665
+ removeElements
3002
2666
  };
3003
- const DarkThemeColor = {
3004
- mode: ThemeColorMode.dark,
3005
- boardBackground: '#141414',
3006
- textColor: '#FFFFFF'
2667
+
2668
+ const addGroup = (board, elements) => {
2669
+ const selectedGroups = getHighestSelectedGroups(board, elements);
2670
+ const selectedIsolatedElements = getSelectedIsolatedElementsCanAddToGroup(board);
2671
+ const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
2672
+ const group = createGroup();
2673
+ if (canAddGroup(board)) {
2674
+ highestSelectedElements.forEach(item => {
2675
+ const path = PlaitBoard.findPath(board, item);
2676
+ NodeTransforms.setNode(board, { groupId: group.id }, path);
2677
+ });
2678
+ if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
2679
+ const newGroupId = selectedIsolatedElements[0].groupId;
2680
+ NodeTransforms.insertNode(board, {
2681
+ ...group,
2682
+ groupId: newGroupId
2683
+ }, [board.children.length]);
2684
+ }
2685
+ else {
2686
+ NodeTransforms.insertNode(board, group, [board.children.length]);
2687
+ }
2688
+ }
3007
2689
  };
3008
- const StarryThemeColor = {
3009
- mode: ThemeColorMode.starry,
3010
- boardBackground: '#0d2537',
3011
- textColor: '#FFFFFF'
2690
+ const removeGroup = (board, elements) => {
2691
+ const selectedGroups = getHighestSelectedGroups(board, elements);
2692
+ if (canRemoveGroup(board)) {
2693
+ selectedGroups.map(group => {
2694
+ const elementsInGroup = findElements(board, {
2695
+ match: item => item.groupId === group.id,
2696
+ recursion: () => false
2697
+ });
2698
+ elementsInGroup.forEach(item => {
2699
+ const path = PlaitBoard.findPath(board, item);
2700
+ NodeTransforms.setNode(board, { groupId: group.groupId || undefined }, path);
2701
+ });
2702
+ const groupPath = PlaitBoard.findPath(board, group);
2703
+ NodeTransforms.removeNode(board, groupPath);
2704
+ });
2705
+ }
2706
+ };
2707
+ const GroupTransforms = {
2708
+ addGroup,
2709
+ removeGroup
3012
2710
  };
3013
- const ThemeColors = [
3014
- DefaultThemeColor,
3015
- ColorfulThemeColor,
3016
- SoftThemeColor,
3017
- RetroThemeColor,
3018
- DarkThemeColor,
3019
- StarryThemeColor
3020
- ];
3021
2711
 
3022
- var Direction;
3023
- (function (Direction) {
3024
- Direction["left"] = "left";
3025
- Direction["top"] = "top";
3026
- Direction["right"] = "right";
3027
- Direction["bottom"] = "bottom";
3028
- })(Direction || (Direction = {}));
2712
+ const Transforms = {
2713
+ ...GeneralTransforms,
2714
+ ...ViewportTransforms$1,
2715
+ ...SelectionTransforms,
2716
+ ...NodeTransforms
2717
+ };
3029
2718
 
3030
- const PlaitGroupElement = {
3031
- isGroup: (value) => {
3032
- return value.type === 'group';
2719
+ const rotatePoints = (points, centerPoint, angle) => {
2720
+ if (!angle) {
2721
+ angle = 0;
2722
+ }
2723
+ if (Array.isArray(points) && typeof points[0] === 'number') {
2724
+ return rotate(points[0], points[1], centerPoint[0], centerPoint[1], angle);
2725
+ }
2726
+ else {
2727
+ return points.map(point => {
2728
+ return rotate(point[0], point[1], centerPoint[0], centerPoint[1], angle);
2729
+ });
2730
+ }
2731
+ };
2732
+ const getSelectionAngle = (elements) => {
2733
+ let angle = elements[0]?.angle || 0;
2734
+ elements.forEach(item => {
2735
+ if (item.angle !== angle && !approximately((item.angle % (Math.PI / 2)) - (angle % (Math.PI / 2)), 0)) {
2736
+ angle = 0;
2737
+ }
2738
+ });
2739
+ return angle;
2740
+ };
2741
+ const hasSameAngle = (elements) => {
2742
+ if (!elements.length) {
2743
+ return false;
2744
+ }
2745
+ const angle = elements[0].angle;
2746
+ if (angle === undefined) {
2747
+ return false;
3033
2748
  }
2749
+ return !elements.some(item => item.angle !== angle);
3034
2750
  };
2751
+ const getRotatedBoundingRectangle = (rectanglesCornerPoints, angle) => {
2752
+ let rectanglesFromOrigin = [];
2753
+ for (let i = 0; i < rectanglesCornerPoints.length; i++) {
2754
+ const cornerPoints = rectanglesCornerPoints[i];
2755
+ const invertCornerPointsFromOrigin = rotatePoints(cornerPoints, [0, 0], -angle);
2756
+ rectanglesFromOrigin.push(RectangleClient.getRectangleByPoints(invertCornerPointsFromOrigin));
2757
+ }
2758
+ const selectionRectangleFromOrigin = RectangleClient.getBoundingRectangle(rectanglesFromOrigin);
2759
+ const selectionCornerPoints = RectangleClient.getCornerPoints(selectionRectangleFromOrigin);
2760
+ const cornerPointsFromOrigin = rotatePoints(selectionCornerPoints, [0, 0], angle);
2761
+ const centerPoint = RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(cornerPointsFromOrigin));
2762
+ return RectangleClient.getRectangleByPoints(rotatePoints(cornerPointsFromOrigin, centerPoint, -angle));
2763
+ };
2764
+ const getOffsetAfterRotate = (rectangle, rotateCenterPoint, angle) => {
2765
+ const targetCenterPoint = RectangleClient.getCenterPoint(rectangle);
2766
+ const [rotatedCenterPoint] = rotatePoints([targetCenterPoint], rotateCenterPoint, angle);
2767
+ const offsetX = rotatedCenterPoint[0] - targetCenterPoint[0];
2768
+ const offsetY = rotatedCenterPoint[1] - targetCenterPoint[1];
2769
+ return { offsetX, offsetY };
2770
+ };
2771
+ const rotatedDataPoints = (points, rotateCenterPoint, angle) => {
2772
+ const { offsetX, offsetY } = getOffsetAfterRotate(RectangleClient.getRectangleByPoints(points), rotateCenterPoint, angle);
2773
+ return points.map(p => [p[0] + offsetX, p[1] + offsetY]);
2774
+ };
2775
+ const hasValidAngle = (node) => {
2776
+ return node.angle && node.angle !== 0;
2777
+ };
2778
+ const rotatePointsByElement = (points, element) => {
2779
+ if (hasValidAngle(element)) {
2780
+ let rectangle = RectangleClient.getRectangleByPoints(element.points);
2781
+ const centerPoint = RectangleClient.getCenterPoint(rectangle);
2782
+ return rotatePoints(points, centerPoint, element.angle);
2783
+ }
2784
+ else {
2785
+ return null;
2786
+ }
2787
+ };
2788
+ const rotateAntiPointsByElement = (points, element) => {
2789
+ if (hasValidAngle(element)) {
2790
+ let rectangle = RectangleClient.getRectangleByPoints(element.points);
2791
+ const centerPoint = RectangleClient.getCenterPoint(rectangle);
2792
+ return rotatePoints(points, centerPoint, -element.angle);
2793
+ }
2794
+ else {
2795
+ return null;
2796
+ }
2797
+ };
2798
+ const getRectangleByAngle = (rectangle, angle) => {
2799
+ if (angle) {
2800
+ const cornerPoints = RectangleClient.getCornerPoints(rectangle);
2801
+ const centerPoint = RectangleClient.getCenterPoint(rectangle);
2802
+ return RectangleClient.getRectangleByPoints(rotatePoints(cornerPoints, centerPoint, angle));
2803
+ }
2804
+ else {
2805
+ return null;
2806
+ }
2807
+ };
2808
+ const isAxisChangedByAngle = (angle) => {
2809
+ const unitAngle = Math.abs(angle) % Math.PI;
2810
+ return unitAngle >= (1 / 4) * Math.PI && unitAngle <= (3 / 4) * Math.PI;
2811
+ };
2812
+ function degreesToRadians(d) {
2813
+ return (d / 180) * Math.PI;
2814
+ }
2815
+ function radiansToDegrees(r) {
2816
+ return (r / Math.PI) * 180;
2817
+ }
2818
+ function rotateElements(board, elements, angle) {
2819
+ const selectionRectangle = getRectangleByElements(board, elements, false);
2820
+ const selectionCenterPoint = RectangleClient.getCenterPoint(selectionRectangle);
2821
+ elements.forEach(item => {
2822
+ const originAngle = item.angle;
2823
+ const points = rotatedDataPoints(item.points, selectionCenterPoint, angle);
2824
+ const path = PlaitBoard.findPath(board, item);
2825
+ Transforms.setNode(board, { points, angle: originAngle + angle }, path);
2826
+ });
2827
+ }
3035
2828
 
3036
- function getRectangleByElements(board, elements, recursion) {
3037
- const rectanglesCornerPoints = [];
3038
- const callback = (node) => {
3039
- const nodeRectangle = board.getRectangle(node);
3040
- if (nodeRectangle) {
3041
- const cornerPoints = RectangleClient.getCornerPoints(nodeRectangle);
3042
- const rotatedCornerPoints = rotatePoints(cornerPoints, RectangleClient.getCenterPoint(nodeRectangle), node.angle || 0);
3043
- rectanglesCornerPoints.push(rotatedCornerPoints);
2829
+ function isSelectionMoving(board) {
2830
+ return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
2831
+ }
2832
+ function setSelectionMoving(board) {
2833
+ PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
2834
+ BOARD_TO_IS_SELECTION_MOVING.set(board, true);
2835
+ setDragging(board, true);
2836
+ }
2837
+ function clearSelectionMoving(board) {
2838
+ PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
2839
+ BOARD_TO_IS_SELECTION_MOVING.delete(board);
2840
+ setDragging(board, false);
2841
+ }
2842
+ function isHandleSelection(board) {
2843
+ const options = board.getPluginOptions(PlaitPluginKey.withSelection);
2844
+ return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
2845
+ }
2846
+ function isSetSelectionOperation(board) {
2847
+ return !!board.operations.find(value => value.type === 'set_selection');
2848
+ }
2849
+ function getTemporaryElements(board) {
2850
+ const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2851
+ if (ref) {
2852
+ return ref.elements;
2853
+ }
2854
+ else {
2855
+ return undefined;
2856
+ }
2857
+ }
2858
+ function getTemporaryRef(board) {
2859
+ return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2860
+ }
2861
+ function deleteTemporaryElements(board) {
2862
+ BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
2863
+ }
2864
+ function drawEntireActiveRectangleG(board) {
2865
+ const elements = getSelectedElements(board);
2866
+ const rectangle = getRectangleByElements(board, elements, false);
2867
+ if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
2868
+ const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
2869
+ stroke: SELECTION_BORDER_COLOR,
2870
+ strokeWidth: ACTIVE_STROKE_WIDTH,
2871
+ fillStyle: 'solid'
2872
+ });
2873
+ selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
2874
+ const angle = getSelectionAngle(elements);
2875
+ if (angle) {
2876
+ setAngleForG(selectionRectangleG, RectangleClient.getCenterPoint(rectangle), angle);
2877
+ }
2878
+ return selectionRectangleG;
2879
+ }
2880
+ return null;
2881
+ }
2882
+ function setSelectedElementsWithGroup(board, elements, isShift) {
2883
+ if (!board.selection) {
2884
+ return;
2885
+ }
2886
+ const selectedElements = getSelectedElements(board);
2887
+ if (!Selection.isCollapsed(board.selection)) {
2888
+ let newElements = [...selectedElements];
2889
+ elements.forEach(item => {
2890
+ if (!item.groupId) {
2891
+ newElements.push(item);
2892
+ }
2893
+ else {
2894
+ newElements.push(...getElementsInGroupByElement(board, item));
2895
+ }
2896
+ });
2897
+ cacheSelectedElements(board, uniqueById(newElements));
2898
+ return;
2899
+ }
2900
+ if (Selection.isCollapsed(board.selection)) {
2901
+ const hitElement = elements[0];
2902
+ const hitElementGroups = getGroupByElement(board, hitElement, true);
2903
+ if (hitElementGroups.length) {
2904
+ const elementsInHighestGroup = getElementsInGroup(board, hitElementGroups[hitElementGroups.length - 1], true) || [];
2905
+ const isSelectGroupElement = selectedElements.some(element => elementsInHighestGroup.map(item => item.id).includes(element.id));
2906
+ if (isShift) {
2907
+ cacheSelectedElementsWithGroupOnShift(board, elements, isSelectGroupElement, elementsInHighestGroup);
2908
+ }
2909
+ else {
2910
+ cacheSelectedElementsWithGroup(board, elements, isSelectGroupElement, hitElementGroups);
2911
+ }
2912
+ }
2913
+ }
2914
+ }
2915
+ function cacheSelectedElementsWithGroupOnShift(board, elements, isSelectGroupElement, elementsInHighestGroup) {
2916
+ const selectedElements = getSelectedElements(board);
2917
+ let newElements = [...selectedElements];
2918
+ const hitElement = elements[0];
2919
+ let pendingElements = [];
2920
+ if (!isSelectGroupElement) {
2921
+ pendingElements = elementsInHighestGroup;
2922
+ }
2923
+ else {
2924
+ const isHitSelectedElement = selectedElements.some(item => item.id === hitElement.id);
2925
+ const selectedElementsInGroup = elementsInHighestGroup.filter(item => selectedElements.includes(item));
2926
+ if (isHitSelectedElement) {
2927
+ pendingElements = selectedElementsInGroup.filter(item => item.id !== hitElement.id);
3044
2928
  }
3045
2929
  else {
3046
- console.error(`can not get rectangle of element:`, node);
2930
+ pendingElements.push(...selectedElementsInGroup, ...elements);
3047
2931
  }
3048
- };
3049
- elements.forEach(element => {
3050
- if (recursion) {
3051
- depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
2932
+ }
2933
+ elementsInHighestGroup.forEach(element => {
2934
+ if (newElements.includes(element)) {
2935
+ newElements.splice(newElements.indexOf(element), 1);
2936
+ }
2937
+ });
2938
+ if (pendingElements.length) {
2939
+ newElements.push(...pendingElements);
2940
+ }
2941
+ cacheSelectedElements(board, uniqueById(newElements));
2942
+ }
2943
+ function cacheSelectedElementsWithGroup(board, elements, isSelectGroupElement, hitElementGroups) {
2944
+ let newElements = [...elements];
2945
+ const selectedGroups = filterSelectedGroups(board, hitElementGroups);
2946
+ if (selectedGroups.length > 0) {
2947
+ if (selectedGroups.length > 1) {
2948
+ newElements = getAllElementsInGroup(board, selectedGroups[selectedGroups.length - 2], true);
2949
+ }
2950
+ }
2951
+ else {
2952
+ const elementsInGroup = getAllElementsInGroup(board, hitElementGroups[hitElementGroups.length - 1], true);
2953
+ if (!isSelectGroupElement) {
2954
+ newElements = elementsInGroup;
2955
+ }
2956
+ }
2957
+ cacheSelectedElements(board, uniqueById(newElements));
2958
+ }
2959
+
2960
+ const getElementsInGroup = (board, group, recursion, includeGroup) => {
2961
+ let result = [];
2962
+ const elements = board.children.filter(value => value.groupId === group.id);
2963
+ if (recursion) {
2964
+ elements.forEach(item => {
2965
+ if (PlaitGroupElement.isGroup(item)) {
2966
+ if (includeGroup) {
2967
+ result.push(item);
2968
+ }
2969
+ result.push(...getElementsInGroup(board, item, recursion, includeGroup));
2970
+ }
2971
+ else {
2972
+ result.push(item);
2973
+ }
2974
+ });
2975
+ }
2976
+ else {
2977
+ result = includeGroup ? elements : elements.filter(item => !PlaitGroupElement.isGroup(item));
2978
+ }
2979
+ return result;
2980
+ };
2981
+ const getAllElementsInGroup = (board, group, recursion, includeGroup) => {
2982
+ const elementsInGroup = getElementsInGroup(board, group, recursion, includeGroup);
2983
+ const result = [];
2984
+ elementsInGroup.forEach(element => {
2985
+ depthFirstRecursion(element, node => {
2986
+ result.push(node);
2987
+ }, () => true);
2988
+ });
2989
+ return result;
2990
+ };
2991
+ const getRectangleByGroup = (board, group, recursion) => {
2992
+ const elementsInGroup = getAllElementsInGroup(board, group, recursion);
2993
+ return getRectangleByElements(board, elementsInGroup, false);
2994
+ };
2995
+ const getGroupByElement = (board, element, recursion, source) => {
2996
+ const group = (source || board.children).find(item => item.id === element?.groupId);
2997
+ if (!group) {
2998
+ return recursion ? [] : null;
2999
+ }
3000
+ if (recursion) {
3001
+ const groups = [group];
3002
+ const grandGroups = getGroupByElement(board, group, recursion, source);
3003
+ if (grandGroups.length) {
3004
+ groups.push(...grandGroups);
3005
+ }
3006
+ return groups;
3007
+ }
3008
+ else {
3009
+ return group;
3010
+ }
3011
+ };
3012
+ const getHighestGroup = (board, element) => {
3013
+ const hitElementGroups = getGroupByElement(board, element, true);
3014
+ if (hitElementGroups.length) {
3015
+ return hitElementGroups[hitElementGroups.length - 1];
3016
+ }
3017
+ return null;
3018
+ };
3019
+ const getElementsInGroupByElement = (board, element) => {
3020
+ const highestGroup = getHighestGroup(board, element);
3021
+ if (highestGroup) {
3022
+ return getAllElementsInGroup(board, highestGroup, true);
3023
+ }
3024
+ else {
3025
+ return [element];
3026
+ }
3027
+ };
3028
+ const isSelectedElementOrGroup = (board, element, elements) => {
3029
+ const selectedElements = elements || getSelectedElements(board);
3030
+ if (PlaitGroupElement.isGroup(element)) {
3031
+ return isSelectedAllElementsInGroup(board, element, elements);
3032
+ }
3033
+ return selectedElements.map(item => item.id).includes(element.id);
3034
+ };
3035
+ const isSelectedAllElementsInGroup = (board, group, elements) => {
3036
+ const selectedElements = elements || getSelectedElements(board);
3037
+ const elementsInGroup = getElementsInGroup(board, group, true);
3038
+ return elementsInGroup.every(item => selectedElements.map(element => element.id).includes(item.id));
3039
+ };
3040
+ const filterSelectedGroups = (board, groups, elements) => {
3041
+ const selectedGroups = [];
3042
+ groups.forEach(item => {
3043
+ if (isSelectedElementOrGroup(board, item, elements)) {
3044
+ selectedGroups.push(item);
3045
+ }
3046
+ });
3047
+ return selectedGroups;
3048
+ };
3049
+ const getSelectedGroups = (board, elements) => {
3050
+ const highestSelectedGroups = getHighestSelectedGroups(board, elements);
3051
+ const groups = [];
3052
+ highestSelectedGroups.forEach(item => {
3053
+ groups.push(item);
3054
+ const elementsInGroup = getElementsInGroup(board, item, true, true);
3055
+ groups.push(...elementsInGroup.filter(item => PlaitGroupElement.isGroup(item)));
3056
+ });
3057
+ return groups;
3058
+ };
3059
+ const getHighestSelectedGroup = (board, element, elements) => {
3060
+ const hitElementGroups = getGroupByElement(board, element, true, elements);
3061
+ const selectedGroups = filterSelectedGroups(board, hitElementGroups, elements);
3062
+ if (selectedGroups.length) {
3063
+ return selectedGroups[selectedGroups.length - 1];
3064
+ }
3065
+ return null;
3066
+ };
3067
+ const getHighestSelectedGroups = (board, elements) => {
3068
+ let result = [];
3069
+ const selectedElements = elements || getSelectedElements(board);
3070
+ selectedElements.forEach(item => {
3071
+ if (item.groupId) {
3072
+ const group = getHighestSelectedGroup(board, item, elements);
3073
+ if (group && !result.includes(group)) {
3074
+ result.push(group);
3075
+ }
3076
+ }
3077
+ });
3078
+ return result;
3079
+ };
3080
+ const getSelectedIsolatedElements = (board, elements) => {
3081
+ let result = [];
3082
+ const selectedElements = elements || getSelectedElements(board);
3083
+ selectedElements
3084
+ .filter(item => !PlaitGroupElement.isGroup(item))
3085
+ .forEach(item => {
3086
+ if (!item.groupId) {
3087
+ result.push(item);
3052
3088
  }
3053
3089
  else {
3054
- callback(element);
3090
+ const group = getHighestSelectedGroup(board, item, elements);
3091
+ if (!group) {
3092
+ result.push(item);
3093
+ }
3055
3094
  }
3056
3095
  });
3057
- if (rectanglesCornerPoints.length > 0) {
3058
- if (hasSameAngle(elements)) {
3096
+ return result;
3097
+ };
3098
+ const getSelectedIsolatedElementsCanAddToGroup = (board, elements) => {
3099
+ const selectedIsolatedElements = getSelectedIsolatedElements(board, elements);
3100
+ return selectedIsolatedElements.filter(item => board.canAddToGroup(item));
3101
+ };
3102
+ const getHighestSelectedElements = (board, elements) => {
3103
+ return [...getHighestSelectedGroups(board, elements), ...getSelectedIsolatedElements(board, elements)];
3104
+ };
3105
+ const createGroupRectangleG = (board, elements) => {
3106
+ const selectedElements = getSelectedElements(board);
3107
+ const groupRectangleG = createG();
3108
+ const isMoving = isSelectionMoving(board);
3109
+ elements.forEach(item => {
3110
+ const isRender = (!selectedElements.includes(item) && !isMoving) || isMoving;
3111
+ if (item.groupId && isRender) {
3112
+ const elements = getElementsInGroupByElement(board, item);
3113
+ const rectangle = getRectangleByElements(board, elements, false);
3114
+ const rectangleG = drawRectangle(board, rectangle, {
3115
+ stroke: SELECTION_BORDER_COLOR,
3116
+ strokeWidth: ACTIVE_STROKE_WIDTH,
3117
+ strokeLineDash: [5]
3118
+ });
3059
3119
  const angle = getSelectionAngle(elements);
3060
- return getRotatedBoundingRectangle(rectanglesCornerPoints, angle);
3120
+ if (angle) {
3121
+ setAngleForG(rectangleG, RectangleClient.getCenterPoint(rectangle), angle);
3122
+ }
3123
+ groupRectangleG.append(rectangleG);
3061
3124
  }
3062
- else {
3063
- const flatCornerPoints = rectanglesCornerPoints.reduce((acc, val) => {
3064
- return acc.concat(val);
3065
- }, []);
3066
- return RectangleClient.getRectangleByPoints(flatCornerPoints);
3125
+ });
3126
+ return groupRectangleG;
3127
+ };
3128
+ const createGroup = (groupId) => {
3129
+ return groupId
3130
+ ? {
3131
+ id: idCreator(),
3132
+ type: 'group',
3133
+ groupId
3067
3134
  }
3068
- }
3069
- else {
3070
- return {
3071
- x: 0,
3072
- y: 0,
3073
- width: 0,
3074
- height: 0
3135
+ : {
3136
+ id: idCreator(),
3137
+ type: 'group'
3075
3138
  };
3139
+ };
3140
+ const nonGroupInHighestSelectedElements = (elements) => {
3141
+ return elements.every(item => !item.groupId);
3142
+ };
3143
+ const hasSelectedElementsInSameGroup = (elements) => {
3144
+ return elements.every(item => item.groupId && item.groupId === elements[0].groupId);
3145
+ };
3146
+ const canAddGroup = (board, elements) => {
3147
+ const highestSelectedElements = getHighestSelectedElements(board, elements);
3148
+ const rootElements = highestSelectedElements.filter(item => board.canAddToGroup(item));
3149
+ if (rootElements.length > 1) {
3150
+ return nonGroupInHighestSelectedElements(rootElements) || hasSelectedElementsInSameGroup(rootElements);
3076
3151
  }
3077
- }
3078
- function getBoardRectangle(board) {
3079
- return getRectangleByElements(board, board.children, true);
3080
- }
3081
- function getElementById(board, id, dataSource) {
3082
- if (!dataSource) {
3083
- dataSource = findElements(board, { match: element => true, recursion: element => true });
3084
- }
3085
- let element = dataSource.find(element => element.id === id);
3086
- return element;
3087
- }
3088
- function findElements(board, options) {
3089
- let elements = [];
3090
- const isReverse = options.isReverse ?? true;
3091
- depthFirstRecursion(board, node => {
3092
- if (!PlaitBoard.isBoard(node) && options.match(node)) {
3093
- elements.push(node);
3094
- }
3095
- }, (value) => {
3096
- if (PlaitBoard.isBoard(value)) {
3152
+ return false;
3153
+ };
3154
+ const canRemoveGroup = (board, elements) => {
3155
+ const selectedGroups = getHighestSelectedGroups(board, elements);
3156
+ const selectedElements = elements || getSelectedElements(board);
3157
+ return selectedElements.length > 0 && selectedGroups.length > 0;
3158
+ };
3159
+
3160
+ const deleteFragment = (board) => {
3161
+ const elements = board.getDeletedFragment([]);
3162
+ board.deleteFragment(elements);
3163
+ };
3164
+
3165
+ const PlaitElement = {
3166
+ isRootElement(value) {
3167
+ const parent = NODE_TO_PARENT.get(value);
3168
+ if (parent && PlaitBoard.isBoard(parent)) {
3097
3169
  return true;
3098
3170
  }
3099
3171
  else {
3100
- return getIsRecursionFunc(board)(value) && options.recursion(value);
3101
- }
3102
- }, isReverse);
3103
- return elements;
3104
- }
3105
-
3106
- const PlaitBoard = {
3107
- isBoard(value) {
3108
- const cachedIsBoard = IS_BOARD_CACHE.get(value);
3109
- if (cachedIsBoard !== undefined) {
3110
- return cachedIsBoard;
3111
- }
3112
- const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
3113
- IS_BOARD_CACHE.set(value, isBoard);
3114
- return isBoard;
3115
- },
3116
- findPath(board, node) {
3117
- const path = [];
3118
- let child = node;
3119
- while (true) {
3120
- const parent = NODE_TO_PARENT.get(child);
3121
- if (parent == null) {
3122
- if (PlaitBoard.isBoard(child)) {
3123
- return path;
3124
- }
3125
- else {
3126
- break;
3127
- }
3128
- }
3129
- const i = NODE_TO_INDEX.get(child);
3130
- if (i == null) {
3131
- break;
3132
- }
3133
- path.unshift(i);
3134
- child = parent;
3135
- }
3136
- throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
3137
- },
3138
- getHost(board) {
3139
- return BOARD_TO_HOST.get(board);
3140
- },
3141
- getElementHost(board) {
3142
- return BOARD_TO_ELEMENT_HOST.get(board)?.host;
3143
- },
3144
- getElementUpperHost(board) {
3145
- return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
3146
- },
3147
- getElementActiveHost(board) {
3148
- return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
3149
- },
3150
- getRoughSVG(board) {
3151
- return BOARD_TO_ROUGH_SVG.get(board);
3152
- },
3153
- getComponent(board) {
3154
- return BOARD_TO_COMPONENT.get(board);
3155
- },
3156
- getBoardContainer(board) {
3157
- return BOARD_TO_ELEMENT_HOST.get(board)?.container;
3158
- },
3159
- getRectangle(board) {
3160
- return getRectangleByElements(board, board.children, true);
3161
- },
3162
- getViewportContainer(board) {
3163
- return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
3164
- },
3165
- isFocus(board) {
3166
- return !!board.selection;
3167
- },
3168
- isReadonly(board) {
3169
- return board.options.readonly;
3170
- },
3171
- hasBeenTextEditing(board) {
3172
- return !!IS_TEXT_EDITABLE.get(board);
3173
- },
3174
- getPointer(board) {
3175
- return board.pointer;
3176
- },
3177
- isPointer(board, pointer) {
3178
- return board.pointer === pointer;
3179
- },
3180
- isInPointer(board, pointers) {
3181
- const point = board.pointer;
3182
- return pointers.includes(point);
3183
- },
3184
- getMovingPointInBoard(board) {
3185
- return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
3186
- },
3187
- isMovingPointInBoard(board) {
3188
- const point = BOARD_TO_MOVING_POINT.get(board);
3189
- const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
3190
- if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
3191
- return true;
3172
+ return false;
3192
3173
  }
3193
- return false;
3194
3174
  },
3195
- getThemeColors(board) {
3196
- return (board.options.themeColors || ThemeColors);
3175
+ getComponent(value) {
3176
+ return ELEMENT_TO_COMPONENT.get(value);
3197
3177
  }
3198
3178
  };
3199
3179
 
3200
- const applyToDraft = (board, selection, viewport, theme, op) => {
3180
+ const isSetViewportOperation = (value) => {
3181
+ return value.type === 'set_viewport';
3182
+ };
3183
+ const inverse = (op) => {
3201
3184
  switch (op.type) {
3202
3185
  case 'insert_node': {
3203
- const { path, node } = op;
3204
- const parent = PlaitNode.parent(board, path);
3205
- const index = path[path.length - 1];
3206
- if (!parent.children || index > parent.children.length) {
3207
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
3208
- }
3209
- parent.children.splice(index, 0, node);
3210
- break;
3211
- }
3212
- case 'remove_node': {
3213
- const { path } = op;
3214
- const parent = PlaitNode.parent(board, path);
3215
- const index = path[path.length - 1];
3216
- if (!parent.children || index > parent.children.length) {
3217
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
3218
- }
3219
- parent.children.splice(index, 1);
3220
- break;
3221
- }
3222
- case 'move_node': {
3223
- const { path, newPath } = op;
3224
- if (Path.isAncestor(path, newPath)) {
3225
- throw new Error(`Cannot move a path [${path}] to new path [${newPath}] because the destination is inside itself.`);
3226
- }
3227
- const node = PlaitNode.get(board, path);
3228
- const parent = PlaitNode.parent(board, path);
3229
- const index = path[path.length - 1];
3230
- // This is tricky, but since the `path` and `newPath` both refer to
3231
- // the same snapshot in time, there's a mismatch. After either
3232
- // removing the original position, the second step's path can be out
3233
- // of date. So instead of using the `op.newPath` directly, we
3234
- // transform `op.path` to ascertain what the `newPath` would be after
3235
- // the operation was applied.
3236
- parent.children?.splice(index, 1);
3237
- const truePath = Path.transform(path, op);
3238
- const newParent = PlaitNode.get(board, Path.parent(truePath));
3239
- const newIndex = truePath[truePath.length - 1];
3240
- newParent.children?.splice(newIndex, 0, node);
3241
- break;
3186
+ return { ...op, type: 'remove_node' };
3187
+ }
3188
+ case 'remove_node': {
3189
+ return { ...op, type: 'insert_node' };
3190
+ }
3191
+ case 'move_node': {
3192
+ const { newPath, path } = op;
3193
+ // PERF: in this case the move operation is a no-op anyways.
3194
+ if (Path.equals(newPath, path)) {
3195
+ return op;
3196
+ }
3197
+ // when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
3198
+ // shoud not return [0,2] -> [0,0] #WIK-8981
3199
+ // if (Path.isSibling(path, newPath)) {
3200
+ // return { ...op, path: newPath, newPath: path };
3201
+ // }
3202
+ // If the move does not happen within a single parent it is possible
3203
+ // for the move to impact the true path to the location where the node
3204
+ // was removed from and where it was inserted. We have to adjust for this
3205
+ // and find the original path. We can accomplish this (only in non-sibling)
3206
+ // moves by looking at the impact of the move operation on the node
3207
+ // after the original move path.
3208
+ const inversePath = Path.transform(path, op);
3209
+ const inverseNewPath = Path.transform(Path.next(path), op);
3210
+ return { ...op, path: inversePath, newPath: inverseNewPath };
3242
3211
  }
3243
3212
  case 'set_node': {
3244
- const { path, properties, newProperties } = op;
3245
- if (path.length === 0) {
3246
- throw new Error(`Cannot set properties on the root node!`);
3213
+ const { properties, newProperties } = op;
3214
+ return { ...op, properties: newProperties, newProperties: properties };
3215
+ }
3216
+ case 'set_selection': {
3217
+ const { properties, newProperties } = op;
3218
+ if (properties == null) {
3219
+ return {
3220
+ ...op,
3221
+ properties: newProperties,
3222
+ newProperties: null
3223
+ };
3247
3224
  }
3248
- const node = PlaitNode.get(board, path);
3249
- for (const key in newProperties) {
3250
- const value = newProperties[key];
3251
- if (value == null) {
3252
- delete node[key];
3253
- }
3254
- else {
3255
- node[key] = value;
3256
- }
3225
+ else if (newProperties == null) {
3226
+ return {
3227
+ ...op,
3228
+ properties: null,
3229
+ newProperties: properties
3230
+ };
3257
3231
  }
3258
- // properties that were previously defined, but are now missing, must be deleted
3259
- for (const key in properties) {
3260
- if (!newProperties.hasOwnProperty(key)) {
3261
- delete node[key];
3262
- }
3232
+ else {
3233
+ return { ...op, properties: newProperties, newProperties: properties };
3263
3234
  }
3264
- break;
3265
3235
  }
3266
3236
  case 'set_viewport': {
3267
- const { newProperties } = op;
3268
- if (newProperties == null) {
3269
- viewport = newProperties;
3270
- }
3271
- else {
3272
- if (viewport == null) {
3273
- if (!Viewport.isViewport(newProperties)) {
3274
- throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
3275
- }
3276
- viewport = { ...newProperties };
3277
- }
3278
- for (const key in newProperties) {
3279
- const value = newProperties[key];
3280
- if (value == null) {
3281
- delete viewport[key];
3282
- }
3283
- else {
3284
- viewport[key] = value;
3285
- }
3286
- }
3237
+ const { properties, newProperties } = op;
3238
+ if (properties == null) {
3239
+ return {
3240
+ ...op,
3241
+ properties: newProperties,
3242
+ newProperties: newProperties
3243
+ };
3287
3244
  }
3288
- break;
3289
- }
3290
- case 'set_selection': {
3291
- const { newProperties } = op;
3292
- if (newProperties == null) {
3293
- selection = newProperties;
3245
+ else if (newProperties == null) {
3246
+ return {
3247
+ ...op,
3248
+ properties: properties,
3249
+ newProperties: properties
3250
+ };
3294
3251
  }
3295
3252
  else {
3296
- if (selection === null) {
3297
- selection = op.newProperties;
3298
- }
3299
- else {
3300
- selection = newProperties;
3301
- }
3253
+ return { ...op, properties: newProperties, newProperties: properties };
3302
3254
  }
3303
- break;
3304
3255
  }
3305
3256
  case 'set_theme': {
3306
- const { newProperties } = op;
3307
- theme = newProperties;
3308
- break;
3257
+ const { properties, newProperties } = op;
3258
+ return { ...op, properties: newProperties, newProperties: properties };
3309
3259
  }
3310
3260
  }
3311
- return { selection, viewport, theme };
3312
3261
  };
3313
- const GeneralTransforms = {
3314
- /**
3315
- * Transform the board by an operation.
3316
- */
3317
- transform(board, op) {
3318
- board.children = createDraft(board.children);
3319
- let viewport = board.viewport && createDraft(board.viewport);
3320
- let selection = board.selection && createDraft(board.selection);
3321
- let theme = board.theme && createDraft(board.theme);
3322
- try {
3323
- const state = applyToDraft(board, selection, viewport, theme, op);
3324
- viewport = state.viewport;
3325
- selection = state.selection;
3326
- theme = state.theme;
3262
+ const PlaitOperation = {
3263
+ isSetViewportOperation,
3264
+ inverse
3265
+ };
3266
+
3267
+ const Point = {
3268
+ isEquals(point, otherPoint) {
3269
+ return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
3270
+ },
3271
+ isHorizontal(point, otherPoint, tolerance = 0) {
3272
+ return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
3273
+ },
3274
+ isOverHorizontal(points, tolerance = 0) {
3275
+ return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
3276
+ },
3277
+ isVertical(point, otherPoint, tolerance = 0) {
3278
+ return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
3279
+ },
3280
+ isOverVertical(points, tolerance = 0) {
3281
+ return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
3282
+ },
3283
+ isAlign(points, tolerance = 0) {
3284
+ return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
3285
+ },
3286
+ getOffsetX(point1, point2) {
3287
+ return point2[0] - point1[0];
3288
+ },
3289
+ getOffsetY(point1, point2) {
3290
+ return point2[1] - point1[1];
3291
+ }
3292
+ };
3293
+
3294
+ const SAVING = new WeakMap();
3295
+ const MERGING = new WeakMap();
3296
+
3297
+ var ThemeColorMode;
3298
+ (function (ThemeColorMode) {
3299
+ ThemeColorMode["default"] = "default";
3300
+ ThemeColorMode["colorful"] = "colorful";
3301
+ ThemeColorMode["soft"] = "soft";
3302
+ ThemeColorMode["retro"] = "retro";
3303
+ ThemeColorMode["dark"] = "dark";
3304
+ ThemeColorMode["starry"] = "starry";
3305
+ })(ThemeColorMode || (ThemeColorMode = {}));
3306
+ const DefaultThemeColor = {
3307
+ mode: ThemeColorMode.default,
3308
+ boardBackground: '#ffffff',
3309
+ textColor: '#333333'
3310
+ };
3311
+ const ColorfulThemeColor = {
3312
+ mode: ThemeColorMode.colorful,
3313
+ boardBackground: '#ffffff',
3314
+ textColor: '#333333'
3315
+ };
3316
+ const SoftThemeColor = {
3317
+ mode: ThemeColorMode.soft,
3318
+ boardBackground: '#f5f5f5',
3319
+ textColor: '#333333'
3320
+ };
3321
+ const RetroThemeColor = {
3322
+ mode: ThemeColorMode.retro,
3323
+ boardBackground: '#f9f8ed',
3324
+ textColor: '#333333'
3325
+ };
3326
+ const DarkThemeColor = {
3327
+ mode: ThemeColorMode.dark,
3328
+ boardBackground: '#141414',
3329
+ textColor: '#FFFFFF'
3330
+ };
3331
+ const StarryThemeColor = {
3332
+ mode: ThemeColorMode.starry,
3333
+ boardBackground: '#0d2537',
3334
+ textColor: '#FFFFFF'
3335
+ };
3336
+ const ThemeColors = [
3337
+ DefaultThemeColor,
3338
+ ColorfulThemeColor,
3339
+ SoftThemeColor,
3340
+ RetroThemeColor,
3341
+ DarkThemeColor,
3342
+ StarryThemeColor
3343
+ ];
3344
+
3345
+ var Direction;
3346
+ (function (Direction) {
3347
+ Direction["left"] = "left";
3348
+ Direction["top"] = "top";
3349
+ Direction["right"] = "right";
3350
+ Direction["bottom"] = "bottom";
3351
+ })(Direction || (Direction = {}));
3352
+
3353
+ const PlaitGroupElement = {
3354
+ isGroup: (value) => {
3355
+ return value.type === 'group';
3356
+ }
3357
+ };
3358
+
3359
+ function getRectangleByElements(board, elements, recursion) {
3360
+ const rectanglesCornerPoints = [];
3361
+ const callback = (node) => {
3362
+ const nodeRectangle = board.getRectangle(node);
3363
+ if (nodeRectangle) {
3364
+ const cornerPoints = RectangleClient.getCornerPoints(nodeRectangle);
3365
+ const rotatedCornerPoints = rotatePointsByElement(cornerPoints, node) || cornerPoints;
3366
+ rectanglesCornerPoints.push(rotatedCornerPoints);
3327
3367
  }
3328
- finally {
3329
- board.children = finishDraft(board.children);
3330
- if (selection) {
3331
- board.selection = isDraft(selection) ? finishDraft(selection) : selection;
3332
- }
3333
- else {
3334
- board.selection = null;
3335
- }
3336
- board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
3337
- board.theme = isDraft(theme) ? finishDraft(theme) : theme;
3368
+ else {
3369
+ console.error(`can not get rectangle of element:`, node);
3370
+ }
3371
+ };
3372
+ elements.forEach(element => {
3373
+ if (recursion) {
3374
+ depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
3375
+ }
3376
+ else {
3377
+ callback(element);
3378
+ }
3379
+ });
3380
+ if (rectanglesCornerPoints.length > 0) {
3381
+ if (hasSameAngle(elements)) {
3382
+ const angle = getSelectionAngle(elements);
3383
+ return getRotatedBoundingRectangle(rectanglesCornerPoints, angle);
3338
3384
  }
3339
- }
3340
- };
3341
-
3342
- function insertNode(board, node, path) {
3343
- const operation = { type: 'insert_node', node, path };
3344
- board.apply(operation);
3345
- }
3346
- function setNode(board, props, path) {
3347
- const properties = {};
3348
- const newProperties = {};
3349
- const node = PlaitNode.get(board, path);
3350
- for (const k in props) {
3351
- if (node[k] !== props[k]) {
3352
- if (node.hasOwnProperty(k)) {
3353
- properties[k] = node[k];
3354
- }
3355
- if (props[k] != null)
3356
- newProperties[k] = props[k];
3385
+ else {
3386
+ const flatCornerPoints = rectanglesCornerPoints.reduce((acc, val) => {
3387
+ return acc.concat(val);
3388
+ }, []);
3389
+ return RectangleClient.getRectangleByPoints(flatCornerPoints);
3357
3390
  }
3358
3391
  }
3359
- const operation = { type: 'set_node', properties, newProperties, path };
3360
- board.apply(operation);
3361
- }
3362
- function removeNode(board, path) {
3363
- const node = PlaitNode.get(board, path);
3364
- const operation = { type: 'remove_node', path, node };
3365
- board.apply(operation);
3366
- }
3367
- function moveNode(board, path, newPath) {
3368
- const operation = { type: 'move_node', path, newPath };
3369
- board.apply(operation);
3392
+ else {
3393
+ return {
3394
+ x: 0,
3395
+ y: 0,
3396
+ width: 0,
3397
+ height: 0
3398
+ };
3399
+ }
3370
3400
  }
3371
- const NodeTransforms = {
3372
- insertNode,
3373
- setNode,
3374
- removeNode,
3375
- moveNode
3376
- };
3377
-
3378
- function setSelection(board, selection) {
3379
- const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
3380
- board.apply(operation);
3401
+ function getBoardRectangle(board) {
3402
+ return getRectangleByElements(board, board.children, true);
3381
3403
  }
3382
- const SelectionTransforms = {
3383
- setSelection,
3384
- addSelectionWithTemporaryElements
3385
- };
3386
- function addSelectionWithTemporaryElements(board, elements) {
3387
- const timeoutId = setTimeout(() => {
3388
- setSelection(board, { anchor: [0, 0], focus: [0, 0] });
3389
- }, 0);
3390
- let ref = getTemporaryRef(board);
3391
- if (ref) {
3392
- clearTimeout(ref.timeoutId);
3393
- const currentElements = ref.elements;
3394
- ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
3395
- ref.timeoutId = timeoutId;
3396
- }
3397
- else {
3398
- BOARD_TO_TEMPORARY_ELEMENTS.set(board, { timeoutId, elements });
3404
+ function getElementById(board, id, dataSource) {
3405
+ if (!dataSource) {
3406
+ dataSource = findElements(board, { match: element => true, recursion: element => true });
3399
3407
  }
3408
+ let element = dataSource.find(element => element.id === id);
3409
+ return element;
3400
3410
  }
3401
-
3402
- const removeElements = (board, elements) => {
3403
- elements
3404
- .map(element => {
3405
- const path = PlaitBoard.findPath(board, element);
3406
- const ref = board.pathRef(path);
3407
- return () => {
3408
- removeNode(board, ref.current);
3409
- ref.unref();
3410
- removeSelectedElement(board, element, true);
3411
- };
3412
- })
3413
- .forEach(action => {
3414
- action();
3415
- });
3416
- };
3417
- const CoreTransforms = {
3418
- removeElements
3419
- };
3420
-
3421
- const addGroup = (board, elements) => {
3422
- const selectedGroups = getHighestSelectedGroups(board, elements);
3423
- const selectedIsolatedElements = getSelectedIsolatedElementsCanAddToGroup(board);
3424
- const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
3425
- const group = createGroup();
3426
- if (canAddGroup(board)) {
3427
- highestSelectedElements.forEach(item => {
3428
- const path = PlaitBoard.findPath(board, item);
3429
- NodeTransforms.setNode(board, { groupId: group.id }, path);
3430
- });
3431
- if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
3432
- const newGroupId = selectedIsolatedElements[0].groupId;
3433
- NodeTransforms.insertNode(board, {
3434
- ...group,
3435
- groupId: newGroupId
3436
- }, [board.children.length]);
3411
+ function findElements(board, options) {
3412
+ let elements = [];
3413
+ const isReverse = options.isReverse ?? true;
3414
+ depthFirstRecursion(board, node => {
3415
+ if (!PlaitBoard.isBoard(node) && options.match(node)) {
3416
+ elements.push(node);
3417
+ }
3418
+ }, (value) => {
3419
+ if (PlaitBoard.isBoard(value)) {
3420
+ return true;
3437
3421
  }
3438
3422
  else {
3439
- NodeTransforms.insertNode(board, group, [board.children.length]);
3423
+ return getIsRecursionFunc(board)(value) && options.recursion(value);
3440
3424
  }
3441
- }
3442
- };
3443
- const removeGroup = (board, elements) => {
3444
- const selectedGroups = getHighestSelectedGroups(board, elements);
3445
- if (canRemoveGroup(board)) {
3446
- selectedGroups.map(group => {
3447
- const elementsInGroup = findElements(board, {
3448
- match: item => item.groupId === group.id,
3449
- recursion: () => false
3450
- });
3451
- elementsInGroup.forEach(item => {
3452
- const path = PlaitBoard.findPath(board, item);
3453
- NodeTransforms.setNode(board, { groupId: group.groupId || undefined }, path);
3454
- });
3455
- const groupPath = PlaitBoard.findPath(board, group);
3456
- NodeTransforms.removeNode(board, groupPath);
3457
- });
3458
- }
3459
- };
3460
- const GroupTransforms = {
3461
- addGroup,
3462
- removeGroup
3463
- };
3425
+ }, isReverse);
3426
+ return elements;
3427
+ }
3464
3428
 
3465
- const Transforms = {
3466
- ...GeneralTransforms,
3467
- ...ViewportTransforms$1,
3468
- ...SelectionTransforms,
3469
- ...NodeTransforms
3429
+ const PlaitBoard = {
3430
+ isBoard(value) {
3431
+ const cachedIsBoard = IS_BOARD_CACHE.get(value);
3432
+ if (cachedIsBoard !== undefined) {
3433
+ return cachedIsBoard;
3434
+ }
3435
+ const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
3436
+ IS_BOARD_CACHE.set(value, isBoard);
3437
+ return isBoard;
3438
+ },
3439
+ isAlive(board) {
3440
+ const isAlive = IS_BOARD_CACHE.get(board);
3441
+ return !!isAlive;
3442
+ },
3443
+ findPath(board, node) {
3444
+ const path = [];
3445
+ let child = node;
3446
+ while (true) {
3447
+ const parent = NODE_TO_PARENT.get(child);
3448
+ if (parent == null) {
3449
+ if (PlaitBoard.isBoard(child)) {
3450
+ return path;
3451
+ }
3452
+ else {
3453
+ break;
3454
+ }
3455
+ }
3456
+ const i = NODE_TO_INDEX.get(child);
3457
+ if (i == null) {
3458
+ break;
3459
+ }
3460
+ path.unshift(i);
3461
+ child = parent;
3462
+ }
3463
+ throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
3464
+ },
3465
+ getHost(board) {
3466
+ return BOARD_TO_HOST.get(board);
3467
+ },
3468
+ getElementHost(board) {
3469
+ return BOARD_TO_ELEMENT_HOST.get(board)?.host;
3470
+ },
3471
+ getElementUpperHost(board) {
3472
+ return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
3473
+ },
3474
+ getElementActiveHost(board) {
3475
+ return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
3476
+ },
3477
+ getRoughSVG(board) {
3478
+ return BOARD_TO_ROUGH_SVG.get(board);
3479
+ },
3480
+ getComponent(board) {
3481
+ return BOARD_TO_COMPONENT.get(board);
3482
+ },
3483
+ getBoardContainer(board) {
3484
+ return BOARD_TO_ELEMENT_HOST.get(board)?.container;
3485
+ },
3486
+ getRectangle(board) {
3487
+ return getRectangleByElements(board, board.children, true);
3488
+ },
3489
+ getViewportContainer(board) {
3490
+ return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
3491
+ },
3492
+ isFocus(board) {
3493
+ return !!board.selection;
3494
+ },
3495
+ isReadonly(board) {
3496
+ return board.options.readonly;
3497
+ },
3498
+ hasBeenTextEditing(board) {
3499
+ return !!IS_TEXT_EDITABLE.get(board);
3500
+ },
3501
+ getPointer(board) {
3502
+ return board.pointer;
3503
+ },
3504
+ isPointer(board, pointer) {
3505
+ return board.pointer === pointer;
3506
+ },
3507
+ isInPointer(board, pointers) {
3508
+ const point = board.pointer;
3509
+ return pointers.includes(point);
3510
+ },
3511
+ getMovingPointInBoard(board) {
3512
+ return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
3513
+ },
3514
+ isMovingPointInBoard(board) {
3515
+ const point = BOARD_TO_MOVING_POINT.get(board);
3516
+ const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
3517
+ if (point && distanceBetweenPointAndRectangle(point[0], point[1], rect) === 0) {
3518
+ return true;
3519
+ }
3520
+ return false;
3521
+ },
3522
+ getThemeColors(board) {
3523
+ return (board.options.themeColors || ThemeColors);
3524
+ }
3470
3525
  };
3471
3526
 
3472
3527
  const PathRef = {
@@ -3547,6 +3602,9 @@ function createBoard(children, options) {
3547
3602
  },
3548
3603
  onChange: () => { },
3549
3604
  afterChange: () => { },
3605
+ drawActiveRectangle: () => {
3606
+ return drawEntireActiveRectangleG(board);
3607
+ },
3550
3608
  mousedown: (event) => { },
3551
3609
  mousemove: (event) => { },
3552
3610
  mouseleave: (event) => { },
@@ -3561,12 +3619,11 @@ function createBoard(children, options) {
3561
3619
  setClipboardData(data, clipboardContext);
3562
3620
  },
3563
3621
  insertFragment: (data) => { },
3564
- deleteFragment: (data) => {
3565
- const elements = board.getDeletedFragment([]);
3622
+ deleteFragment: (elements) => {
3566
3623
  CoreTransforms.removeElements(board, elements);
3567
3624
  },
3568
3625
  getDeletedFragment: (data) => data,
3569
- getRelatedFragment: (data) => data,
3626
+ getRelatedFragment: (data, originData) => data,
3570
3627
  drawElement: (context) => [],
3571
3628
  redrawElement: (context, previousContext) => { },
3572
3629
  destroyElement: (context) => { },
@@ -3751,7 +3808,7 @@ function withHandPointer(board) {
3751
3808
  }
3752
3809
 
3753
3810
  function withSelection(board) {
3754
- const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange } = board;
3811
+ const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange, drawActiveRectangle } = board;
3755
3812
  let start = null;
3756
3813
  let end = null;
3757
3814
  let selectionMovingG;
@@ -3912,7 +3969,8 @@ function withSelection(board) {
3912
3969
  if (!isSelectionMoving(board)) {
3913
3970
  selectionRectangleG?.remove();
3914
3971
  if (newElements.length > 1) {
3915
- selectionRectangleG = createSelectionRectangleG(board);
3972
+ selectionRectangleG = board.drawActiveRectangle();
3973
+ PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
3916
3974
  }
3917
3975
  }
3918
3976
  }
@@ -3931,7 +3989,8 @@ function withSelection(board) {
3931
3989
  (currentSelectedElements.length !== previousSelectedElements.length ||
3932
3990
  currentSelectedElements.some((c, index) => c !== previousSelectedElements[index]))) {
3933
3991
  selectionRectangleG?.remove();
3934
- selectionRectangleG = createSelectionRectangleG(board);
3992
+ selectionRectangleG = board.drawActiveRectangle();
3993
+ PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
3935
3994
  previousSelectedElements = currentSelectedElements;
3936
3995
  }
3937
3996
  }
@@ -3980,8 +4039,8 @@ function withViewport(board) {
3980
4039
  return board;
3981
4040
  }
3982
4041
 
3983
- const ALIGN_TOLERANCE = 2;
3984
- class AlignReaction {
4042
+ const SNAP_TOLERANCE = 2;
4043
+ class MovingSnapReaction {
3985
4044
  constructor(board, activeElements, activeRectangle) {
3986
4045
  this.board = board;
3987
4046
  this.activeElements = activeElements;
@@ -3995,7 +4054,7 @@ class AlignReaction {
3995
4054
  return;
3996
4055
  }
3997
4056
  const rectangle = this.board.getRectangle(node);
3998
- rectangle && result.push(rectangle);
4057
+ rectangle && result.push(getRectangleByAngle(rectangle, node.angle) || rectangle);
3999
4058
  }, node => {
4000
4059
  if (node && (PlaitBoard.isBoard(node) || this.board.isRecursion(node))) {
4001
4060
  return true;
@@ -4006,7 +4065,7 @@ class AlignReaction {
4006
4065
  }, true);
4007
4066
  return result;
4008
4067
  }
4009
- handleAlign() {
4068
+ handleSnapping() {
4010
4069
  const alignRectangles = this.getAlignRectangle();
4011
4070
  const g = createG();
4012
4071
  let alignLines = [];
@@ -4018,7 +4077,7 @@ class AlignReaction {
4018
4077
  for (let alignRectangle of alignRectangles) {
4019
4078
  const closestDistances = this.calculateClosestDistances(this.activeRectangle, alignRectangle);
4020
4079
  let canDrawHorizontal = false;
4021
- if (!isCorrectX && closestDistances.absXDistance < ALIGN_TOLERANCE) {
4080
+ if (!isCorrectX && closestDistances.absXDistance < SNAP_TOLERANCE) {
4022
4081
  deltaX = closestDistances.xDistance;
4023
4082
  this.activeRectangle.x -= deltaX;
4024
4083
  isCorrectX = true;
@@ -4068,7 +4127,7 @@ class AlignReaction {
4068
4127
  isCorrectX = true;
4069
4128
  }
4070
4129
  let canDrawVertical = false;
4071
- if (!isCorrectY && closestDistances.absYDistance < ALIGN_TOLERANCE) {
4130
+ if (!isCorrectY && closestDistances.absYDistance < SNAP_TOLERANCE) {
4072
4131
  deltaY = closestDistances.yDistance;
4073
4132
  this.activeRectangle.y -= deltaY;
4074
4133
  isCorrectY = true;
@@ -4215,7 +4274,7 @@ class AlignReaction {
4215
4274
  //middle
4216
4275
  let _center = (before[axis] + before[side] + after[axis]) / 2;
4217
4276
  dif = Math.abs(activeRectangleCenter - _center);
4218
- if (dif < ALIGN_TOLERANCE) {
4277
+ if (dif < SNAP_TOLERANCE) {
4219
4278
  distributeDistance = (after[axis] - (before[axis] + before[side]) - this.activeRectangle[side]) / 2;
4220
4279
  delta = activeRectangleCenter - _center;
4221
4280
  beforeIndex = i;
@@ -4225,7 +4284,7 @@ class AlignReaction {
4225
4284
  const distanceRight = after[axis] - (before[axis] + before[side]);
4226
4285
  _center = after[axis] + after[side] + distanceRight + this.activeRectangle[side] / 2;
4227
4286
  dif = Math.abs(activeRectangleCenter - _center);
4228
- if (!distributeDistance && dif < ALIGN_TOLERANCE) {
4287
+ if (!distributeDistance && dif < SNAP_TOLERANCE) {
4229
4288
  distributeDistance = distanceRight;
4230
4289
  beforeIndex = j;
4231
4290
  delta = activeRectangleCenter - _center;
@@ -4234,7 +4293,7 @@ class AlignReaction {
4234
4293
  const distanceBefore = after[axis] - (before[axis] + before[side]);
4235
4294
  _center = before[axis] - distanceBefore - this.activeRectangle[side] / 2;
4236
4295
  dif = Math.abs(activeRectangleCenter - _center);
4237
- if (!distributeDistance && dif < ALIGN_TOLERANCE) {
4296
+ if (!distributeDistance && dif < SNAP_TOLERANCE) {
4238
4297
  distributeDistance = distanceBefore;
4239
4298
  afterIndex = i;
4240
4299
  delta = activeRectangleCenter - _center;
@@ -4379,7 +4438,8 @@ function withMoving(board) {
4379
4438
  }
4380
4439
  else if (hitTargetElement) {
4381
4440
  startPoint = point;
4382
- activeElements = getElementsInGroupByElement(board, hitTargetElement);
4441
+ const relatedElements = board.getRelatedFragment([], [hitTargetElement]);
4442
+ activeElements = [...getElementsInGroupByElement(board, hitTargetElement), ...relatedElements];
4383
4443
  activeElementsRectangle = getRectangleByElements(board, activeElements, true);
4384
4444
  preventTouchMove(board, event, true);
4385
4445
  }
@@ -4423,8 +4483,8 @@ function withMoving(board) {
4423
4483
  x: activeElementsRectangle.x + offsetX,
4424
4484
  y: activeElementsRectangle.y + offsetY
4425
4485
  };
4426
- const reactionManager = new AlignReaction(board, activeElements, newRectangle);
4427
- const ref = reactionManager.handleAlign();
4486
+ const movingSnapReaction = new MovingSnapReaction(board, activeElements, getRectangleByAngle(newRectangle, getSelectionAngle(activeElements)) || newRectangle);
4487
+ const ref = movingSnapReaction.handleSnapping();
4428
4488
  offsetX -= ref.deltaX;
4429
4489
  offsetY -= ref.deltaY;
4430
4490
  alignG = ref.g;
@@ -4653,7 +4713,7 @@ const withHotkey = (board) => {
4653
4713
  selectedElements.length > 0 &&
4654
4714
  (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
4655
4715
  event.preventDefault();
4656
- board.deleteFragment(null);
4716
+ deleteFragment(board);
4657
4717
  }
4658
4718
  keyDown(event);
4659
4719
  };
@@ -4953,6 +5013,7 @@ class PlaitBoardComponent {
4953
5013
  BOARD_TO_COMPONENT.set(this.board, this);
4954
5014
  BOARD_TO_ROUGH_SVG.set(this.board, roughSVG);
4955
5015
  BOARD_TO_HOST.set(this.board, this.host);
5016
+ IS_BOARD_ALIVE.set(this.board, true);
4956
5017
  BOARD_TO_ELEMENT_HOST.set(this.board, {
4957
5018
  host: elementHost,
4958
5019
  upperHost: elementUpperHost,
@@ -5126,7 +5187,7 @@ class PlaitBoardComponent {
5126
5187
  event.preventDefault();
5127
5188
  const rectangle = getRectangleByElements(this.board, selectedElements, false);
5128
5189
  this.board.setFragment(event.clipboardData, null, rectangle, 'cut');
5129
- this.board.deleteFragment(event.clipboardData);
5190
+ deleteFragment(this.board);
5130
5191
  });
5131
5192
  }
5132
5193
  viewportScrollListener() {
@@ -5208,6 +5269,7 @@ class PlaitBoardComponent {
5208
5269
  BOARD_TO_ROUGH_SVG.delete(this.board);
5209
5270
  BOARD_TO_HOST.delete(this.board);
5210
5271
  BOARD_TO_ELEMENT_HOST.delete(this.board);
5272
+ IS_BOARD_ALIVE.set(this.board, false);
5211
5273
  BOARD_TO_ON_CHANGE.delete(this.board);
5212
5274
  BOARD_TO_AFTER_CHANGE.set(this.board, () => { });
5213
5275
  }
@@ -5474,12 +5536,25 @@ class DebugGenerator {
5474
5536
  return;
5475
5537
  }
5476
5538
  const polygonG = PlaitBoard.getRoughSVG(board).polygon(points, options || { stroke: 'red' });
5539
+ polygonG.classList.add(this.debugKey);
5477
5540
  PlaitBoard.getElementActiveHost(board).append(polygonG);
5478
5541
  const gArray = getTemporaryGArray(this.debugKey);
5479
5542
  gArray.push(polygonG);
5480
5543
  setTemporaryGArray(this.debugKey, gArray);
5481
5544
  return polygonG;
5482
5545
  }
5546
+ drawLine(board, points, options) {
5547
+ if (!isDebug(this.debugKey)) {
5548
+ return;
5549
+ }
5550
+ const lineG = PlaitBoard.getRoughSVG(board).linearPath(points, options || { stroke: 'red' });
5551
+ lineG.classList.add(this.debugKey);
5552
+ PlaitBoard.getElementActiveHost(board).append(lineG);
5553
+ const gArray = getTemporaryGArray(this.debugKey);
5554
+ gArray.push(lineG);
5555
+ setTemporaryGArray(this.debugKey, gArray);
5556
+ return lineG;
5557
+ }
5483
5558
  drawRectangle(board, data, options) {
5484
5559
  if (!isDebug(this.debugKey)) {
5485
5560
  return;
@@ -5492,6 +5567,7 @@ class DebugGenerator {
5492
5567
  rectangle = data;
5493
5568
  }
5494
5569
  const rectangleG = PlaitBoard.getRoughSVG(board).rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height, options || { stroke: 'red' });
5570
+ rectangleG.classList.add(this.debugKey);
5495
5571
  PlaitBoard.getElementActiveHost(board).append(rectangleG);
5496
5572
  const gArray = getTemporaryGArray(this.debugKey);
5497
5573
  gArray.push(rectangleG);
@@ -5505,6 +5581,7 @@ class DebugGenerator {
5505
5581
  const result = [];
5506
5582
  points.forEach((p, i) => {
5507
5583
  const circle = PlaitBoard.getRoughSVG(board).circle(p[0], p[1], isCumulativeDiameter ? diameter * (i + 1) : diameter, Object.assign({}, { stroke: 'red', fill: 'red', fillStyle: 'solid' }, options || {}));
5584
+ circle.classList.add(this.debugKey);
5508
5585
  PlaitBoard.getElementActiveHost(board).append(circle);
5509
5586
  const gArray = getTemporaryGArray(this.debugKey);
5510
5587
  gArray.push(circle);
@@ -5530,5 +5607,5 @@ const isDebug = (key) => {
5530
5607
  * Generated bundle index. Do not edit.
5531
5608
  */
5532
5609
 
5533
- 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, DebugGenerator, 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, GroupTransforms, 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, RESIZE_CURSORS, 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, approximately, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, cacheSelectedElementsWithGroup, cacheSelectedElementsWithGroupOnShift, calcNewViewBox, canAddGroup, canRemoveGroup, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createDebugGenerator, 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, filterSelectedGroups, findElements, getAllElementsInGroup, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getElementsInGroup, getElementsInGroupByElement, getEllipseTangentSlope, getGroupByElement, getHighestGroup, getHighestSelectedElements, getHighestSelectedGroup, getHighestSelectedGroups, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getOffsetAfterRotate, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByElements, getRectangleByGroup, getRotatedBoundingRectangle, getSelectedElements, getSelectedGroups, getSelectedIsolatedElements, getSelectedIsolatedElementsCanAddToGroup, getSelectedTargetElements, getSelectionAngle, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hasSameAngle, hasSelectedElementsInSameGroup, hasValidAngle, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isContextmenu, isDOMElement, isDOMNode, isDebug, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedAllElementsInGroup, isSelectedElement, isSelectedElementOrGroup, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, nonGroupInHighestSelectedElements, normalizePoint, preventTouchMove, removeMovingElements, removeSelectedElement, rotate, rotateAntiPointsByElement, rotatePoints, rotatePointsByElement, rotatedDataPoints, scrollToRectangle, setAngleForG, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectedElementsWithGroup, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, uniqueById, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
5610
+ 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, DebugGenerator, 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, GroupTransforms, H, HIT_DISTANCE_BUFFER, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_ALIVE, 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, RESIZE_CURSORS, 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, SNAPPING_STROKE_WIDTH, 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, approximately, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, cacheSelectedElementsWithGroup, cacheSelectedElementsWithGroupOnShift, calcNewViewBox, canAddGroup, canRemoveGroup, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createDebugGenerator, createFakeEvent, createForeignObject, createG, createGroup, createGroupRectangleG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createTestingBoard, createText, createTouchEvent, debounce, degreesToRadians, deleteFragment, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawEntireActiveRectangleG, drawLine, drawLinearPath, drawRectangle, drawRoundRectangle, fakeNodeWeakMap, filterSelectedGroups, findElements, getAllElementsInGroup, getBoardRectangle, getClipboardData, getClipboardFromHtml, getDataTransferClipboard, getDataTransferClipboardText, getElementById, getElementHostBBox, getElementsInGroup, getElementsInGroupByElement, getEllipseTangentSlope, getGroupByElement, getHighestGroup, getHighestSelectedElements, getHighestSelectedGroup, getHighestSelectedGroups, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMovingElements, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getOffsetAfterRotate, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByAngle, getRectangleByElements, getRectangleByGroup, getRotatedBoundingRectangle, getSelectedElements, getSelectedGroups, getSelectedIsolatedElements, getSelectedIsolatedElementsCanAddToGroup, getSelectedTargetElements, getSelectionAngle, getTemporaryElements, getTemporaryRef, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hasSameAngle, hasSelectedElementsInSameGroup, hasValidAngle, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isAxisChangedByAngle, isContextmenu, isDOMElement, isDOMNode, isDebug, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedAllElementsInGroup, isSelectedElement, isSelectedElementOrGroup, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, nonGroupInHighestSelectedElements, normalizePoint, preventTouchMove, radiansToDegrees, removeMovingElements, removeSelectedElement, rotate, rotateAntiPointsByElement, rotateElements, rotatePoints, rotatePointsByElement, rotatedDataPoints, scrollToRectangle, setAngleForG, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectedElementsWithGroup, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, uniqueById, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
5534
5611
  //# sourceMappingURL=plait-core.mjs.map