@plait/core 0.51.1 → 0.51.3

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