@plait/core 0.51.2 → 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.
@@ -535,9 +535,13 @@ function createRect(rectangle, options) {
535
535
  const setStrokeLinecap = (g, value) => {
536
536
  g.setAttribute('stroke-linecap', value);
537
537
  };
538
- const setTransformRotate = (g, rectangle, angle) => {
539
- var centerX = rectangle.x + rectangle.width / 2;
540
- 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];
541
545
  let cosTheta = Math.cos(angle);
542
546
  let sinTheta = Math.sin(angle);
543
547
  let transformMatrix = [
@@ -670,10 +674,10 @@ class PlaitPluginElementComponent {
670
674
  removeSelectedElement(this.board, this.element);
671
675
  (this.rootG || this.g).remove();
672
676
  }
673
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
674
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
677
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
678
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.8", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
675
679
  }
676
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
680
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitPluginElementComponent, decorators: [{
677
681
  type: Directive
678
682
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }]; }, propDecorators: { context: [{
679
683
  type: Input
@@ -795,14 +799,19 @@ const isLineHitLine = (a, b, c, d) => {
795
799
  const cd = [d[0] - c[0], d[1] - c[1]];
796
800
  return crossProduct(ab, ac) * crossProduct(ab, ad) <= 0 && crossProduct(cd, ca) * crossProduct(cd, cb) <= 0;
797
801
  };
798
- const isPolylineHitRectangle = (points, rectangle) => {
802
+ const isPolylineHitRectangle = (points, rectangle, isClose = true) => {
799
803
  const rectanglePoints = RectangleClient.getCornerPoints(rectangle);
800
- for (let i = 1; i < points.length; i++) {
801
- const isIntersect = isLineHitLine(points[i], points[i - 1], rectanglePoints[0], rectanglePoints[1]) ||
802
- isLineHitLine(points[i], points[i - 1], rectanglePoints[1], rectanglePoints[2]) ||
803
- isLineHitLine(points[i], points[i - 1], rectanglePoints[2], rectanglePoints[3]) ||
804
- isLineHitLine(points[i], points[i - 1], rectanglePoints[3], rectanglePoints[0]);
805
- 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)) {
806
815
  return true;
807
816
  }
808
817
  }
@@ -2171,18 +2180,15 @@ const handleTouchTarget = (board) => {
2171
2180
  }
2172
2181
  };
2173
2182
 
2174
- const PlaitElement = {
2175
- isRootElement(value) {
2176
- const parent = NODE_TO_PARENT.get(value);
2177
- if (parent && PlaitBoard.isBoard(parent)) {
2178
- return true;
2179
- }
2180
- else {
2181
- return false;
2182
- }
2183
- },
2184
- getComponent(value) {
2185
- 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);
2186
2192
  }
2187
2193
  };
2188
2194
 
@@ -2408,803 +2414,823 @@ const PlaitNode = {
2408
2414
  }
2409
2415
  };
2410
2416
 
2411
- const isSetViewportOperation = (value) => {
2412
- return value.type === 'set_viewport';
2413
- };
2414
- const inverse = (op) => {
2417
+ const applyToDraft = (board, selection, viewport, theme, op) => {
2415
2418
  switch (op.type) {
2416
2419
  case 'insert_node': {
2417
- 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;
2418
2428
  }
2419
2429
  case 'remove_node': {
2420
- 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;
2421
2438
  }
2422
2439
  case 'move_node': {
2423
- const { newPath, path } = op;
2424
- // PERF: in this case the move operation is a no-op anyways.
2425
- if (Path.equals(newPath, path)) {
2426
- 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.`);
2427
2443
  }
2428
- // when operation path is [0,0] -> [0,2], should exec Path.transform to get [0,1] -> [0,0]
2429
- // shoud not return [0,2] -> [0,0] #WIK-8981
2430
- // if (Path.isSibling(path, newPath)) {
2431
- // return { ...op, path: newPath, newPath: path };
2432
- // }
2433
- // If the move does not happen within a single parent it is possible
2434
- // for the move to impact the true path to the location where the node
2435
- // was removed from and where it was inserted. We have to adjust for this
2436
- // and find the original path. We can accomplish this (only in non-sibling)
2437
- // moves by looking at the impact of the move operation on the node
2438
- // after the original move path.
2439
- const inversePath = Path.transform(path, op);
2440
- const inverseNewPath = Path.transform(Path.next(path), op);
2441
- 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;
2442
2459
  }
2443
2460
  case 'set_node': {
2444
- const { properties, newProperties } = op;
2445
- return { ...op, properties: newProperties, newProperties: properties };
2446
- }
2447
- case 'set_selection': {
2448
- const { properties, newProperties } = op;
2449
- if (properties == null) {
2450
- return {
2451
- ...op,
2452
- properties: newProperties,
2453
- newProperties: null
2454
- };
2461
+ const { path, properties, newProperties } = op;
2462
+ if (path.length === 0) {
2463
+ throw new Error(`Cannot set properties on the root node!`);
2455
2464
  }
2456
- else if (newProperties == null) {
2457
- return {
2458
- ...op,
2459
- properties: null,
2460
- newProperties: properties
2461
- };
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
+ }
2462
2474
  }
2463
- else {
2464
- 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
+ }
2465
2480
  }
2481
+ break;
2466
2482
  }
2467
2483
  case 'set_viewport': {
2468
- const { properties, newProperties } = op;
2469
- if (properties == null) {
2470
- return {
2471
- ...op,
2472
- properties: newProperties,
2473
- newProperties: newProperties
2474
- };
2484
+ const { newProperties } = op;
2485
+ if (newProperties == null) {
2486
+ viewport = newProperties;
2475
2487
  }
2476
- else if (newProperties == null) {
2477
- return {
2478
- ...op,
2479
- properties: properties,
2480
- newProperties: properties
2481
- };
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;
2482
2511
  }
2483
2512
  else {
2484
- return { ...op, properties: newProperties, newProperties: properties };
2513
+ if (selection === null) {
2514
+ selection = op.newProperties;
2515
+ }
2516
+ else {
2517
+ selection = newProperties;
2518
+ }
2485
2519
  }
2520
+ break;
2486
2521
  }
2487
2522
  case 'set_theme': {
2488
- const { properties, newProperties } = op;
2489
- return { ...op, properties: newProperties, newProperties: properties };
2523
+ const { newProperties } = op;
2524
+ theme = newProperties;
2525
+ break;
2490
2526
  }
2491
2527
  }
2528
+ return { selection, viewport, theme };
2492
2529
  };
2493
- const PlaitOperation = {
2494
- isSetViewportOperation,
2495
- inverse
2496
- };
2497
-
2498
- const Point = {
2499
- isEquals(point, otherPoint) {
2500
- return point && otherPoint && point[0] === otherPoint[0] && point[1] === otherPoint[1];
2501
- },
2502
- isHorizontal(point, otherPoint, tolerance = 0) {
2503
- return point && otherPoint && Point.isOverHorizontal([point, otherPoint], tolerance);
2504
- },
2505
- isOverHorizontal(points, tolerance = 0) {
2506
- return points.every(point => Math.abs(point[1] - points[0][1]) <= tolerance);
2507
- },
2508
- isVertical(point, otherPoint, tolerance = 0) {
2509
- return point && otherPoint && Point.isOverVertical([point, otherPoint], tolerance);
2510
- },
2511
- isOverVertical(points, tolerance = 0) {
2512
- return points.every(point => Math.abs(point[0] - points[0][0]) <= tolerance);
2513
- },
2514
- isAlign(points, tolerance = 0) {
2515
- return Point.isOverHorizontal(points, tolerance) || Point.isOverVertical(points, tolerance);
2516
- },
2517
- getOffsetX(point1, point2) {
2518
- return point2[0] - point1[0];
2519
- },
2520
- getOffsetY(point1, point2) {
2521
- 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
+ }
2522
2556
  }
2523
2557
  };
2524
2558
 
2525
- const Viewport = {
2526
- isViewport: (value) => {
2527
- 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
+ }
2528
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
2529
2593
  };
2530
-
2531
- const SAVING = new WeakMap();
2532
- const MERGING = new WeakMap();
2533
-
2534
- var ThemeColorMode;
2535
- (function (ThemeColorMode) {
2536
- ThemeColorMode["default"] = "default";
2537
- ThemeColorMode["colorful"] = "colorful";
2538
- ThemeColorMode["soft"] = "soft";
2539
- ThemeColorMode["retro"] = "retro";
2540
- ThemeColorMode["dark"] = "dark";
2541
- ThemeColorMode["starry"] = "starry";
2542
- })(ThemeColorMode || (ThemeColorMode = {}));
2543
- const DefaultThemeColor = {
2544
- mode: ThemeColorMode.default,
2545
- boardBackground: '#ffffff',
2546
- textColor: '#333333'
2547
- };
2548
- const ColorfulThemeColor = {
2549
- mode: ThemeColorMode.colorful,
2550
- boardBackground: '#ffffff',
2551
- textColor: '#333333'
2552
- };
2553
- const SoftThemeColor = {
2554
- mode: ThemeColorMode.soft,
2555
- boardBackground: '#f5f5f5',
2556
- textColor: '#333333'
2557
- };
2558
- const RetroThemeColor = {
2559
- mode: ThemeColorMode.retro,
2560
- boardBackground: '#f9f8ed',
2561
- 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
2562
2602
  };
2563
- const DarkThemeColor = {
2564
- mode: ThemeColorMode.dark,
2565
- boardBackground: '#141414',
2566
- 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
+ });
2567
2633
  };
2568
- const StarryThemeColor = {
2569
- mode: ThemeColorMode.starry,
2570
- boardBackground: '#0d2537',
2571
- textColor: '#FFFFFF'
2634
+ const CoreTransforms = {
2635
+ removeElements
2572
2636
  };
2573
- const ThemeColors = [
2574
- DefaultThemeColor,
2575
- ColorfulThemeColor,
2576
- SoftThemeColor,
2577
- RetroThemeColor,
2578
- DarkThemeColor,
2579
- StarryThemeColor
2580
- ];
2581
2637
 
2582
- var Direction;
2583
- (function (Direction) {
2584
- Direction["left"] = "left";
2585
- Direction["top"] = "top";
2586
- Direction["right"] = "right";
2587
- Direction["bottom"] = "bottom";
2588
- })(Direction || (Direction = {}));
2638
+ const Transforms = {
2639
+ ...GeneralTransforms,
2640
+ ...ViewportTransforms$1,
2641
+ ...SelectionTransforms,
2642
+ ...NodeTransforms
2643
+ };
2589
2644
 
2590
- function getRectangleByElements(board, elements, recursion) {
2591
- const rectangles = [];
2592
- const callback = (node) => {
2593
- const nodeRectangle = board.getRectangle(node);
2594
- if (nodeRectangle) {
2595
- rectangles.push(nodeRectangle);
2596
- }
2597
- else {
2598
- console.error(`can not get rectangle of element:`, node);
2599
- }
2600
- };
2601
- elements.forEach(element => {
2602
- if (recursion) {
2603
- depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
2604
- }
2605
- else {
2606
- callback(element);
2607
- }
2608
- });
2609
- if (rectangles.length > 0) {
2610
- 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;
2611
2669
  }
2612
2670
  else {
2613
- return {
2614
- x: 0,
2615
- y: 0,
2616
- width: 0,
2617
- height: 0
2618
- };
2671
+ return undefined;
2619
2672
  }
2620
2673
  }
2621
- function getBoardRectangle(board) {
2622
- return getRectangleByElements(board, board.children, true);
2674
+ function getTemporaryRef(board) {
2675
+ return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
2623
2676
  }
2624
- function getElementById(board, id, dataSource) {
2625
- if (!dataSource) {
2626
- dataSource = findElements(board, { match: element => true, recursion: element => true });
2627
- }
2628
- let element = dataSource.find(element => element.id === id);
2629
- return element;
2677
+ function deleteTemporaryElements(board) {
2678
+ BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
2630
2679
  }
2631
- function findElements(board, options) {
2632
- let elements = [];
2633
- const isReverse = options.isReverse ?? true;
2634
- depthFirstRecursion(board, node => {
2635
- if (!PlaitBoard.isBoard(node) && options.match(node)) {
2636
- elements.push(node);
2637
- }
2638
- }, (value) => {
2639
- if (PlaitBoard.isBoard(value)) {
2640
- return true;
2641
- }
2642
- else {
2643
- return getIsRecursionFunc(board)(value) && options.recursion(value);
2644
- }
2645
- }, isReverse);
2646
- 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;
2647
2694
  }
2648
2695
 
2649
- const PlaitBoard = {
2650
- isBoard(value) {
2651
- const cachedIsBoard = IS_BOARD_CACHE.get(value);
2652
- if (cachedIsBoard !== undefined) {
2653
- return cachedIsBoard;
2654
- }
2655
- const isBoard = typeof value.onChange === 'function' && typeof value.apply === 'function';
2656
- IS_BOARD_CACHE.set(value, isBoard);
2657
- return isBoard;
2658
- },
2659
- findPath(board, node) {
2660
- const path = [];
2661
- let child = node;
2662
- while (true) {
2663
- const parent = NODE_TO_PARENT.get(child);
2664
- if (parent == null) {
2665
- if (PlaitBoard.isBoard(child)) {
2666
- return path;
2667
- }
2668
- else {
2669
- 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);
2670
2704
  }
2705
+ result.push(...getElementsInGroup(board, item, recursion));
2671
2706
  }
2672
- const i = NODE_TO_INDEX.get(child);
2673
- if (i == null) {
2674
- break;
2707
+ else {
2708
+ result.push(item);
2675
2709
  }
2676
- path.unshift(i);
2677
- 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);
2678
2731
  }
2679
- throw new Error(`Unable to find the path for Plait node: ${JSON.stringify(node)}`);
2680
- },
2681
- getHost(board) {
2682
- return BOARD_TO_HOST.get(board);
2683
- },
2684
- getElementHost(board) {
2685
- return BOARD_TO_ELEMENT_HOST.get(board)?.host;
2686
- },
2687
- getElementUpperHost(board) {
2688
- return BOARD_TO_ELEMENT_HOST.get(board)?.upperHost;
2689
- },
2690
- getElementActiveHost(board) {
2691
- return BOARD_TO_ELEMENT_HOST.get(board)?.activeHost;
2692
- },
2693
- getRoughSVG(board) {
2694
- return BOARD_TO_ROUGH_SVG.get(board);
2695
- },
2696
- getComponent(board) {
2697
- return BOARD_TO_COMPONENT.get(board);
2698
- },
2699
- getBoardContainer(board) {
2700
- return BOARD_TO_ELEMENT_HOST.get(board)?.container;
2701
- },
2702
- getRectangle(board) {
2703
- return getRectangleByElements(board, board.children, true);
2704
- },
2705
- getViewportContainer(board) {
2706
- return BOARD_TO_ELEMENT_HOST.get(board)?.viewportContainer;
2707
- },
2708
- isFocus(board) {
2709
- return !!board.selection;
2710
- },
2711
- isReadonly(board) {
2712
- return board.options.readonly;
2713
- },
2714
- hasBeenTextEditing(board) {
2715
- return !!IS_TEXT_EDITABLE.get(board);
2716
- },
2717
- getPointer(board) {
2718
- return board.pointer;
2719
- },
2720
- isPointer(board, pointer) {
2721
- return board.pointer === pointer;
2722
- },
2723
- isInPointer(board, pointers) {
2724
- const point = board.pointer;
2725
- return pointers.includes(point);
2726
- },
2727
- getMovingPointInBoard(board) {
2728
- return BOARD_TO_MOVING_POINT_IN_BOARD.get(board);
2729
- },
2730
- isMovingPointInBoard(board) {
2731
- const point = BOARD_TO_MOVING_POINT.get(board);
2732
- const rect = PlaitBoard.getBoardContainer(board).getBoundingClientRect();
2733
- 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)) {
2734
2899
  return true;
2735
2900
  }
2736
- return false;
2901
+ else {
2902
+ return false;
2903
+ }
2737
2904
  },
2738
- getThemeColors(board) {
2739
- return (board.options.themeColors || ThemeColors);
2905
+ getComponent(value) {
2906
+ return ELEMENT_TO_COMPONENT.get(value);
2740
2907
  }
2741
2908
  };
2742
2909
 
2743
- const applyToDraft = (board, selection, viewport, theme, op) => {
2910
+ const isSetViewportOperation = (value) => {
2911
+ return value.type === 'set_viewport';
2912
+ };
2913
+ const inverse = (op) => {
2744
2914
  switch (op.type) {
2745
2915
  case 'insert_node': {
2746
- const { path, node } = op;
2747
- const parent = PlaitNode.parent(board, path);
2748
- const index = path[path.length - 1];
2749
- if (!parent.children || index > parent.children.length) {
2750
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2751
- }
2752
- parent.children.splice(index, 0, node);
2753
- break;
2916
+ return { ...op, type: 'remove_node' };
2754
2917
  }
2755
2918
  case 'remove_node': {
2756
- const { path } = op;
2757
- const parent = PlaitNode.parent(board, path);
2758
- const index = path[path.length - 1];
2759
- if (!parent.children || index > parent.children.length) {
2760
- throw new Error(`Cannot apply an "insert_node" operation at path [${path}] because the destination is past the end of the node.`);
2761
- }
2762
- parent.children.splice(index, 1);
2763
- break;
2919
+ return { ...op, type: 'insert_node' };
2764
2920
  }
2765
2921
  case 'move_node': {
2766
- const { path, newPath } = op;
2767
- if (Path.isAncestor(path, newPath)) {
2768
- 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;
2769
2926
  }
2770
- const node = PlaitNode.get(board, path);
2771
- const parent = PlaitNode.parent(board, path);
2772
- const index = path[path.length - 1];
2773
- // This is tricky, but since the `path` and `newPath` both refer to
2774
- // the same snapshot in time, there's a mismatch. After either
2775
- // removing the original position, the second step's path can be out
2776
- // of date. So instead of using the `op.newPath` directly, we
2777
- // transform `op.path` to ascertain what the `newPath` would be after
2778
- // the operation was applied.
2779
- parent.children?.splice(index, 1);
2780
- const truePath = Path.transform(path, op);
2781
- const newParent = PlaitNode.get(board, Path.parent(truePath));
2782
- const newIndex = truePath[truePath.length - 1];
2783
- newParent.children?.splice(newIndex, 0, node);
2784
- 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 };
2785
2941
  }
2786
2942
  case 'set_node': {
2787
- const { path, properties, newProperties } = op;
2788
- if (path.length === 0) {
2789
- 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
+ };
2790
2954
  }
2791
- const node = PlaitNode.get(board, path);
2792
- for (const key in newProperties) {
2793
- const value = newProperties[key];
2794
- if (value == null) {
2795
- delete node[key];
2796
- }
2797
- else {
2798
- node[key] = value;
2799
- }
2955
+ else if (newProperties == null) {
2956
+ return {
2957
+ ...op,
2958
+ properties: null,
2959
+ newProperties: properties
2960
+ };
2800
2961
  }
2801
- // properties that were previously defined, but are now missing, must be deleted
2802
- for (const key in properties) {
2803
- if (!newProperties.hasOwnProperty(key)) {
2804
- delete node[key];
2805
- }
2962
+ else {
2963
+ return { ...op, properties: newProperties, newProperties: properties };
2806
2964
  }
2807
- break;
2808
2965
  }
2809
2966
  case 'set_viewport': {
2810
- const { newProperties } = op;
2811
- if (newProperties == null) {
2812
- viewport = newProperties;
2813
- }
2814
- else {
2815
- if (viewport == null) {
2816
- if (!Viewport.isViewport(newProperties)) {
2817
- throw new Error(`Cannot apply an incomplete "set_viewport" operation properties ${JSON.stringify(newProperties)} when there is no current viewport.`);
2818
- }
2819
- viewport = { ...newProperties };
2820
- }
2821
- for (const key in newProperties) {
2822
- const value = newProperties[key];
2823
- if (value == null) {
2824
- delete viewport[key];
2825
- }
2826
- else {
2827
- viewport[key] = value;
2828
- }
2829
- }
2967
+ const { properties, newProperties } = op;
2968
+ if (properties == null) {
2969
+ return {
2970
+ ...op,
2971
+ properties: newProperties,
2972
+ newProperties: newProperties
2973
+ };
2830
2974
  }
2831
- break;
2832
- }
2833
- case 'set_selection': {
2834
- const { newProperties } = op;
2835
- if (newProperties == null) {
2836
- selection = newProperties;
2975
+ else if (newProperties == null) {
2976
+ return {
2977
+ ...op,
2978
+ properties: properties,
2979
+ newProperties: properties
2980
+ };
2837
2981
  }
2838
2982
  else {
2839
- if (selection === null) {
2840
- selection = op.newProperties;
2841
- }
2842
- else {
2843
- selection = newProperties;
2844
- }
2983
+ return { ...op, properties: newProperties, newProperties: properties };
2845
2984
  }
2846
- break;
2847
2985
  }
2848
2986
  case 'set_theme': {
2849
- const { newProperties } = op;
2850
- theme = newProperties;
2851
- break;
2987
+ const { properties, newProperties } = op;
2988
+ return { ...op, properties: newProperties, newProperties: properties };
2852
2989
  }
2853
2990
  }
2854
- return { selection, viewport, theme };
2855
2991
  };
2856
- const GeneralTransforms = {
2857
- /**
2858
- * Transform the board by an operation.
2859
- */
2860
- transform(board, op) {
2861
- board.children = createDraft(board.children);
2862
- let viewport = board.viewport && createDraft(board.viewport);
2863
- let selection = board.selection && createDraft(board.selection);
2864
- let theme = board.theme && createDraft(board.theme);
2865
- try {
2866
- const state = applyToDraft(board, selection, viewport, theme, op);
2867
- viewport = state.viewport;
2868
- selection = state.selection;
2869
- theme = state.theme;
2870
- }
2871
- finally {
2872
- board.children = finishDraft(board.children);
2873
- if (selection) {
2874
- board.selection = isDraft(selection) ? finishDraft(selection) : selection;
2875
- }
2876
- else {
2877
- board.selection = null;
2878
- }
2879
- board.viewport = isDraft(viewport) ? finishDraft(viewport) : viewport;
2880
- board.theme = isDraft(theme) ? finishDraft(theme) : theme;
2881
- }
2882
- }
2992
+ const PlaitOperation = {
2993
+ isSetViewportOperation,
2994
+ inverse
2883
2995
  };
2884
2996
 
2885
- function insertNode(board, node, path) {
2886
- const operation = { type: 'insert_node', node, path };
2887
- board.apply(operation);
2888
- }
2889
- function setNode(board, props, path) {
2890
- const properties = {};
2891
- const newProperties = {};
2892
- const node = PlaitNode.get(board, path);
2893
- for (const k in props) {
2894
- if (node[k] !== props[k]) {
2895
- if (node.hasOwnProperty(k)) {
2896
- properties[k] = node[k];
2897
- }
2898
- if (props[k] != null)
2899
- newProperties[k] = props[k];
2900
- }
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];
2901
3021
  }
2902
- const operation = { type: 'set_node', properties, newProperties, path };
2903
- board.apply(operation);
2904
- }
2905
- function removeNode(board, path) {
2906
- const node = PlaitNode.get(board, path);
2907
- const operation = { type: 'remove_node', path, node };
2908
- board.apply(operation);
2909
- }
2910
- function moveNode(board, path, newPath) {
2911
- const operation = { type: 'move_node', path, newPath };
2912
- board.apply(operation);
2913
- }
2914
- const NodeTransforms = {
2915
- insertNode,
2916
- setNode,
2917
- removeNode,
2918
- moveNode
2919
3022
  };
2920
3023
 
2921
- function withSelection(board) {
2922
- const { pointerDown, pointerUp, pointerMove, globalPointerUp, onChange, afterChange } = board;
2923
- let start = null;
2924
- let end = null;
2925
- let selectionMovingG;
2926
- let selectionRectangleG;
2927
- let previousSelectedElements;
2928
- let isShift = false;
2929
- let isTextSelection = false;
2930
- board.pointerDown = (event) => {
2931
- if (!isShift && event.shiftKey) {
2932
- isShift = true;
2933
- }
2934
- if (isShift && !event.shiftKey) {
2935
- isShift = false;
2936
- }
2937
- const isHitText = !!(event.target instanceof Element && event.target.closest('.plait-richtext-container'));
2938
- isTextSelection = isHitText && PlaitBoard.hasBeenTextEditing(board);
2939
- // prevent text from being selected
2940
- if (event.shiftKey && !isTextSelection) {
2941
- 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);
2942
3089
  }
2943
- const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2944
- const selectedElements = getSelectedElements(board);
2945
- const hitElement = getHitElementByPoint(board, point);
2946
- const hitSelectedElements = selectedElements.length > 1 ? getHitSelectedElements(board, point) : [];
2947
- const isHitTarget = hitElement || hitSelectedElements.length > 0;
2948
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
2949
- if (PlaitBoard.isPointer(board, PlaitPointerType.selection) && !isHitTarget && options.isMultiple && !options.isDisabledSelect) {
2950
- preventTouchMove(board, event, true);
2951
- // start rectangle selection
2952
- start = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3090
+ else {
3091
+ console.error(`can not get rectangle of element:`, node);
2953
3092
  }
2954
- pointerDown(event);
2955
3093
  };
2956
- board.pointerMove = (event) => {
2957
- if (!isTextSelection) {
2958
- // prevent text from being selected
2959
- event.preventDefault();
2960
- }
2961
- if (start && PlaitBoard.isPointer(board, PlaitPointerType.selection)) {
2962
- const movedTarget = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2963
- const rectangle = RectangleClient.getRectangleByPoints([start, movedTarget]);
2964
- selectionMovingG?.remove();
2965
- if (Math.hypot(rectangle.width, rectangle.height) > PRESS_AND_MOVE_BUFFER || isSelectionMoving(board)) {
2966
- end = movedTarget;
2967
- throttleRAF(board, 'with-selection', () => {
2968
- if (start && end) {
2969
- Transforms.setSelection(board, { anchor: start, focus: end });
2970
- }
2971
- });
2972
- setSelectionMoving(board);
2973
- selectionMovingG = drawRectangle(board, rectangle, {
2974
- stroke: SELECTION_BORDER_COLOR,
2975
- strokeWidth: 1,
2976
- fill: SELECTION_FILL_COLOR,
2977
- fillStyle: 'solid'
2978
- });
2979
- PlaitBoard.getElementActiveHost(board).append(selectionMovingG);
2980
- }
3094
+ elements.forEach(element => {
3095
+ if (recursion) {
3096
+ depthFirstRecursion(element, node => callback(node), node => board.isRecursion(node));
2981
3097
  }
2982
- pointerMove(event);
2983
- };
2984
- // handle the end of click select
2985
- board.pointerUp = (event) => {
2986
- const isSetSelectionPointer = PlaitBoard.isPointer(board, PlaitPointerType.selection) || PlaitBoard.isPointer(board, PlaitPointerType.hand);
2987
- const isSkip = !isMainPointer(event) || isDragging(board) || !isSetSelectionPointer;
2988
- if (isSkip) {
2989
- pointerUp(event);
2990
- return;
3098
+ else {
3099
+ callback(element);
2991
3100
  }
2992
- const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
2993
- const selection = { anchor: point, focus: point };
2994
- Transforms.setSelection(board, selection);
2995
- pointerUp(event);
2996
- };
2997
- board.globalPointerUp = (event) => {
2998
- if (start && end) {
2999
- selectionMovingG?.remove();
3000
- clearSelectionMoving(board);
3001
- 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);
3002
3130
  }
3003
- if (PlaitBoard.isFocus(board)) {
3004
- const isInBoard = event.target instanceof Node && PlaitBoard.getBoardContainer(board).contains(event.target);
3005
- const isInDocument = event.target instanceof Node && document.contains(event.target);
3006
- const isAttachedElement = event.target instanceof Element && event.target.closest(`.${ATTACHED_ELEMENT_CLASS_NAME}`);
3007
- // Clear selection when mouse board outside area
3008
- // The framework needs to determine whether the board is focused through selection
3009
- if (!isInBoard && !start && !isAttachedElement && isInDocument) {
3010
- Transforms.setSelection(board, null);
3011
- }
3131
+ }, (value) => {
3132
+ if (PlaitBoard.isBoard(value)) {
3133
+ return true;
3012
3134
  }
3013
- start = null;
3014
- end = null;
3015
- isTextSelection = false;
3016
- preventTouchMove(board, event, false);
3017
- globalPointerUp(event);
3018
- };
3019
- board.onChange = () => {
3020
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3021
- if (options.isDisabledSelect) {
3022
- clearSelectedElement(board);
3135
+ else {
3136
+ return getIsRecursionFunc(board)(value) && options.recursion(value);
3023
3137
  }
3024
- // remove selected element if include
3025
- board.operations.forEach(op => {
3026
- if (op.type === 'remove_node') {
3027
- removeSelectedElement(board, op.node, true);
3028
- }
3029
- });
3030
- if (isHandleSelection(board) && isSetSelectionOperation(board)) {
3031
- try {
3032
- if (!isShift) {
3033
- selectionRectangleG?.remove();
3034
- }
3035
- const temporaryElements = getTemporaryElements(board);
3036
- let elements = temporaryElements ? temporaryElements : getHitElementsBySelection(board);
3037
- if (!options.isMultiple && elements.length > 1) {
3038
- elements = [elements[0]];
3039
- }
3040
- if (isShift) {
3041
- if (board.selection && Selection.isCollapsed(board.selection)) {
3042
- const newSelectedElements = [...getSelectedElements(board)];
3043
- elements.forEach(element => {
3044
- if (newSelectedElements.includes(element)) {
3045
- newSelectedElements.splice(newSelectedElements.indexOf(element), 1);
3046
- }
3047
- else {
3048
- newSelectedElements.push(element);
3049
- }
3050
- });
3051
- cacheSelectedElements(board, newSelectedElements);
3052
- }
3053
- else {
3054
- const newSelectedElements = [...getSelectedElements(board)];
3055
- elements.forEach(element => {
3056
- if (!newSelectedElements.includes(element)) {
3057
- newSelectedElements.push(element);
3058
- }
3059
- });
3060
- cacheSelectedElements(board, newSelectedElements);
3061
- }
3062
- }
3063
- else {
3064
- const newSelectedElements = [...elements];
3065
- cacheSelectedElements(board, newSelectedElements);
3066
- }
3067
- const newElements = getSelectedElements(board);
3068
- previousSelectedElements = newElements;
3069
- deleteTemporaryElements(board);
3070
- if (!isSelectionMoving(board) && newElements.length > 1) {
3071
- selectionRectangleG?.remove();
3072
- selectionRectangleG = createSelectionRectangleG(board);
3073
- }
3074
- }
3075
- catch (error) {
3076
- console.error(error);
3077
- }
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;
3078
3147
  }
3079
- onChange();
3080
- };
3081
- board.afterChange = () => {
3082
- if (isHandleSelection(board) && !isSetSelectionOperation(board)) {
3083
- try {
3084
- const currentSelectedElements = getSelectedElements(board);
3085
- if (currentSelectedElements.length && currentSelectedElements.length > 1) {
3086
- if (currentSelectedElements.length !== previousSelectedElements.length ||
3087
- currentSelectedElements.some((c, index) => c !== previousSelectedElements[index])) {
3088
- selectionRectangleG?.remove();
3089
- selectionRectangleG = createSelectionRectangleG(board);
3090
- previousSelectedElements = currentSelectedElements;
3091
- }
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;
3092
3160
  }
3093
3161
  else {
3094
- selectionRectangleG?.remove();
3162
+ break;
3095
3163
  }
3096
3164
  }
3097
- catch (error) {
3098
- console.error(error);
3165
+ const i = NODE_TO_INDEX.get(child);
3166
+ if (i == null) {
3167
+ break;
3099
3168
  }
3169
+ path.unshift(i);
3170
+ child = parent;
3100
3171
  }
3101
- afterChange();
3102
- };
3103
- board.setPluginOptions(PlaitPluginKey.withSelection, {
3104
- isMultiple: true,
3105
- isDisabledSelect: false
3106
- });
3107
- return board;
3108
- }
3109
- function isHandleSelection(board) {
3110
- const options = board.getPluginOptions(PlaitPluginKey.withSelection);
3111
- return board.pointer !== PlaitPointerType.hand && !options.isDisabledSelect && !PlaitBoard.isReadonly(board);
3112
- }
3113
- function isSetSelectionOperation(board) {
3114
- return !!board.operations.find(value => value.type === 'set_selection');
3115
- }
3116
- function getTemporaryElements(board) {
3117
- const ref = BOARD_TO_TEMPORARY_ELEMENTS.get(board);
3118
- if (ref) {
3119
- return ref.elements;
3120
- }
3121
- else {
3122
- return undefined;
3123
- }
3124
- }
3125
- function getTemporaryRef(board) {
3126
- return BOARD_TO_TEMPORARY_ELEMENTS.get(board);
3127
- }
3128
- function deleteTemporaryElements(board) {
3129
- BOARD_TO_TEMPORARY_ELEMENTS.delete(board);
3130
- }
3131
- function isSelectionMoving(board) {
3132
- return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
3133
- }
3134
- function setSelectionMoving(board) {
3135
- PlaitBoard.getBoardContainer(board).classList.add('selection-moving');
3136
- BOARD_TO_IS_SELECTION_MOVING.set(board, true);
3137
- setDragging(board, true);
3138
- }
3139
- function clearSelectionMoving(board) {
3140
- PlaitBoard.getBoardContainer(board).classList.remove('selection-moving');
3141
- BOARD_TO_IS_SELECTION_MOVING.delete(board);
3142
- setDragging(board, false);
3143
- }
3144
- function createSelectionRectangleG(board) {
3145
- const elements = getSelectedElements(board);
3146
- const rectangle = getRectangleByElements(board, elements, false);
3147
- if (rectangle.width > 0 && rectangle.height > 0 && elements.length > 1) {
3148
- const selectionRectangleG = drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
3149
- stroke: SELECTION_BORDER_COLOR,
3150
- strokeWidth: ACTIVE_STROKE_WIDTH,
3151
- fillStyle: 'solid'
3152
- });
3153
- selectionRectangleG.classList.add(SELECTION_RECTANGLE_CLASS_NAME);
3154
- PlaitBoard.getElementActiveHost(board).append(selectionRectangleG);
3155
- return selectionRectangleG;
3156
- }
3157
- return null;
3158
- }
3159
-
3160
- function setSelection(board, selection) {
3161
- const operation = { type: 'set_selection', properties: board.selection, newProperties: selection };
3162
- board.apply(operation);
3163
- }
3164
- const SelectionTransforms = {
3165
- setSelection,
3166
- addSelectionWithTemporaryElements
3167
- };
3168
- function addSelectionWithTemporaryElements(board, elements) {
3169
- const timeoutId = setTimeout(() => {
3170
- setSelection(board, { anchor: [0, 0], focus: [0, 0] });
3171
- }, 0);
3172
- let ref = getTemporaryRef(board);
3173
- if (ref) {
3174
- clearTimeout(ref.timeoutId);
3175
- const currentElements = ref.elements;
3176
- ref.elements.push(...elements.filter(element => !currentElements.includes(element)));
3177
- ref.timeoutId = timeoutId;
3178
- }
3179
- else {
3180
- 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);
3181
3233
  }
3182
- }
3183
-
3184
- const removeElements = (board, elements) => {
3185
- elements
3186
- .map(element => {
3187
- const path = PlaitBoard.findPath(board, element);
3188
- const ref = board.pathRef(path);
3189
- return () => {
3190
- removeNode(board, ref.current);
3191
- ref.unref();
3192
- removeSelectedElement(board, element, true);
3193
- };
3194
- })
3195
- .forEach(action => {
3196
- action();
3197
- });
3198
- };
3199
- const CoreTransforms = {
3200
- removeElements
3201
- };
3202
-
3203
- const Transforms = {
3204
- ...GeneralTransforms,
3205
- ...ViewportTransforms$1,
3206
- ...SelectionTransforms,
3207
- ...NodeTransforms
3208
3234
  };
3209
3235
 
3210
3236
  const PathRef = {
@@ -3487,6 +3513,274 @@ function withHandPointer(board) {
3487
3513
  return board;
3488
3514
  }
3489
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
+
3490
3784
  function withViewport(board) {
3491
3785
  const { onChange } = board;
3492
3786
  const throttleUpdate = debounce(() => {
@@ -3913,7 +4207,7 @@ function withMoving(board) {
3913
4207
  const targetElement = getHitElementByPoint(board, point, el => board.isMovable(el));
3914
4208
  if (targetElement) {
3915
4209
  startPoint = point;
3916
- activeElements = [targetElement];
4210
+ activeElements = getElementsInGroupByElement(board, targetElement);
3917
4211
  if (targetElements.length > 0) {
3918
4212
  addSelectionWithTemporaryElements(board, []);
3919
4213
  }
@@ -4083,10 +4377,10 @@ class PlaitIslandBaseComponent {
4083
4377
  markForCheck() {
4084
4378
  this.cdr.markForCheck();
4085
4379
  }
4086
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4087
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
4380
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitIslandBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4381
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.8", type: PlaitIslandBaseComponent, host: { classAttribute: "plait-island-container" }, ngImport: i0 }); }
4088
4382
  }
4089
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
4383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitIslandBaseComponent, decorators: [{
4090
4384
  type: Directive,
4091
4385
  args: [{
4092
4386
  host: {
@@ -4119,10 +4413,10 @@ class PlaitIslandPopoverBaseComponent {
4119
4413
  this.subscription?.unsubscribe();
4120
4414
  this.islandOnDestroy();
4121
4415
  }
4122
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandPopoverBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4123
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 }); }
4416
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitIslandPopoverBaseComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
4417
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.8", type: PlaitIslandPopoverBaseComponent, inputs: { board: "board" }, host: { classAttribute: "plait-island-popover-container" }, ngImport: i0 }); }
4124
4418
  }
4125
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
4419
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitIslandPopoverBaseComponent, decorators: [{
4126
4420
  type: Directive,
4127
4421
  args: [{
4128
4422
  host: {
@@ -4217,10 +4511,10 @@ class PlaitContextService {
4217
4511
  removeUploadingFile(fileEntry) {
4218
4512
  this.uploadingFiles = this.uploadingFiles.filter(file => file.url !== fileEntry.url);
4219
4513
  }
4220
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4221
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService }); }
4514
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitContextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4515
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitContextService }); }
4222
4516
  }
4223
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitContextService, decorators: [{
4517
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitContextService, decorators: [{
4224
4518
  type: Injectable
4225
4519
  }] });
4226
4520
 
@@ -4297,10 +4591,10 @@ class PlaitElementComponent {
4297
4591
  ngOnDestroy() {
4298
4592
  this.board.destroyElement(this.getContext());
4299
4593
  }
4300
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
4301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitElementComponent, isStandalone: true, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4594
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
4595
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.8", type: PlaitElementComponent, isStandalone: true, selector: "plait-element", inputs: { index: "index", element: "element", parent: "parent", board: "board", effect: "effect", parentG: "parentG" }, usesOnChanges: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4302
4596
  }
4303
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitElementComponent, decorators: [{
4597
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitElementComponent, decorators: [{
4304
4598
  type: Component,
4305
4599
  args: [{
4306
4600
  selector: 'plait-element',
@@ -4336,8 +4630,8 @@ class PlaitChildrenElementComponent {
4336
4630
  this.parentG = PlaitBoard.getElementHost(this.board);
4337
4631
  }
4338
4632
  }
4339
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitChildrenElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4340
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitChildrenElementComponent, isStandalone: true, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
4633
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitChildrenElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4634
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.8", type: PlaitChildrenElementComponent, isStandalone: true, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
4341
4635
  <plait-element
4342
4636
  *ngFor="let item of parent.children; let index = index; trackBy: trackBy"
4343
4637
  [index]="index"
@@ -4349,7 +4643,7 @@ class PlaitChildrenElementComponent {
4349
4643
  ></plait-element>
4350
4644
  `, isInline: true, dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: PlaitElementComponent, selector: "plait-element", inputs: ["index", "element", "parent", "board", "effect", "parentG"] }] }); }
4351
4645
  }
4352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitChildrenElementComponent, decorators: [{
4646
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitChildrenElementComponent, decorators: [{
4353
4647
  type: Component,
4354
4648
  args: [{
4355
4649
  selector: 'plait-children',
@@ -4377,6 +4671,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
4377
4671
  type: Input
4378
4672
  }] } });
4379
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
+
4380
4699
  const ElementHostClass = 'element-host';
4381
4700
  const ElementUpperHostClass = 'element-upper-host';
4382
4701
  const ElementActiveHostClass = 'element-active-host';
@@ -4502,7 +4821,7 @@ class PlaitBoardComponent {
4502
4821
  initializeViewportOffset(this.board);
4503
4822
  }
4504
4823
  initializePlugins() {
4505
- 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))))))))));
4506
4825
  this.plaitPlugins.forEach(plugin => {
4507
4826
  board = plugin(board);
4508
4827
  });
@@ -4714,8 +5033,8 @@ class PlaitBoardComponent {
4714
5033
  this.updateIslands();
4715
5034
  });
4716
5035
  }
4717
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
4718
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: PlaitBoardComponent, isStandalone: true, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, descendants: true }], viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }, { propertyName: "viewportContainer", first: true, predicate: ["viewportContainer"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: `
5036
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitBoardComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ViewContainerRef }, { token: i0.ElementRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component }); }
5037
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.8", type: PlaitBoardComponent, isStandalone: true, selector: "plait-board", inputs: { plaitValue: "plaitValue", plaitViewport: "plaitViewport", plaitPlugins: "plaitPlugins", plaitOptions: "plaitOptions", plaitTheme: "plaitTheme" }, outputs: { plaitChange: "plaitChange", plaitBoardInitialized: "plaitBoardInitialized" }, host: { properties: { "class": "this.hostClass", "class.readonly": "this.readonly", "class.focused": "this.isFocused", "class.disabled-scroll": "this.disabledScrollOnNonFocus" } }, providers: [PlaitContextService], queries: [{ propertyName: "islands", predicate: PlaitIslandBaseComponent, descendants: true }], viewQueries: [{ propertyName: "svg", first: true, predicate: ["svg"], descendants: true, static: true }, { propertyName: "viewportContainer", first: true, predicate: ["viewportContainer"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: `
4719
5038
  <div class="viewport-container" #viewportContainer>
4720
5039
  <svg #svg width="100%" height="100%" style="position: relative;" class="board-host-svg">
4721
5040
  <g class="element-host"></g>
@@ -4727,7 +5046,7 @@ class PlaitBoardComponent {
4727
5046
  <ng-content></ng-content>
4728
5047
  `, isInline: true, dependencies: [{ kind: "component", type: PlaitChildrenElementComponent, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4729
5048
  }
4730
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: PlaitBoardComponent, decorators: [{
5049
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.8", ngImport: i0, type: PlaitBoardComponent, decorators: [{
4731
5050
  type: Component,
4732
5051
  args: [{
4733
5052
  selector: 'plait-board',
@@ -4952,5 +5271,5 @@ function createModModifierKeys() {
4952
5271
  * Generated bundle index. Do not edit.
4953
5272
  */
4954
5273
 
4955
- 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, 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 };
4956
5275
  //# sourceMappingURL=plait-core.mjs.map