@plait/core 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/board/board.component.d.ts +6 -3
- package/constants/index.d.ts +2 -0
- package/core/element/context.d.ts +6 -2
- package/core/element/plugin-element.d.ts +13 -4
- package/core/list-render.d.ts +16 -0
- package/esm2022/board/board.component.mjs +24 -21
- package/esm2022/constants/index.mjs +3 -1
- package/esm2022/core/element/context.mjs +1 -1
- package/esm2022/core/element/plugin-element.mjs +79 -12
- package/esm2022/core/list-render.mjs +209 -0
- package/esm2022/interfaces/board.mjs +3 -3
- package/esm2022/interfaces/element.mjs +28 -2
- package/esm2022/interfaces/node.mjs +18 -1
- package/esm2022/interfaces/path.mjs +56 -57
- package/esm2022/plugins/create-board.mjs +10 -10
- package/esm2022/plugins/with-hotkey.mjs +32 -3
- package/esm2022/plugins/with-moving.mjs +12 -12
- package/esm2022/plugins/with-related-fragment.mjs +5 -5
- package/esm2022/public-api.mjs +1 -3
- package/esm2022/transforms/group.mjs +23 -6
- package/esm2022/transforms/index.mjs +6 -3
- package/esm2022/transforms/z-index.mjs +20 -0
- package/esm2022/utils/angle.mjs +17 -3
- package/esm2022/utils/clipboard/clipboard.mjs +5 -5
- package/esm2022/utils/clipboard/common.mjs +5 -5
- package/esm2022/utils/clipboard/types.mjs +1 -1
- package/esm2022/utils/common.mjs +27 -1
- package/esm2022/utils/fragment.mjs +20 -1
- package/esm2022/utils/group.mjs +27 -1
- package/esm2022/utils/helper.mjs +37 -1
- package/esm2022/utils/index.mjs +4 -1
- package/esm2022/utils/math.mjs +37 -1
- package/esm2022/utils/position.mjs +3 -3
- package/esm2022/utils/snap/snap-moving.mjs +199 -0
- package/esm2022/utils/snap/snap.mjs +208 -0
- package/esm2022/utils/to-image.mjs +2 -2
- package/esm2022/utils/weak-maps.mjs +3 -1
- package/esm2022/utils/z-index.mjs +166 -0
- package/fesm2022/plait-core.mjs +1655 -1080
- package/fesm2022/plait-core.mjs.map +1 -1
- package/interfaces/board.d.ts +5 -5
- package/interfaces/element.d.ts +5 -0
- package/interfaces/node.d.ts +1 -0
- package/package.json +1 -1
- package/public-api.d.ts +0 -2
- package/styles/styles.scss +9 -0
- package/transforms/group.d.ts +4 -0
- package/transforms/index.d.ts +3 -2
- package/transforms/z-index.d.ts +13 -0
- package/utils/angle.d.ts +2 -0
- package/utils/clipboard/common.d.ts +1 -1
- package/utils/clipboard/types.d.ts +1 -1
- package/utils/common.d.ts +8 -0
- package/utils/fragment.d.ts +3 -1
- package/utils/group.d.ts +3 -1
- package/utils/helper.d.ts +4 -1
- package/utils/index.d.ts +3 -0
- package/utils/math.d.ts +1 -0
- package/utils/position.d.ts +1 -1
- package/utils/snap/snap-moving.d.ts +5 -0
- package/utils/snap/snap.d.ts +31 -0
- package/utils/weak-maps.d.ts +2 -0
- package/utils/z-index.d.ts +5 -0
- package/core/children/children.component.d.ts +0 -17
- package/core/children/effect.d.ts +0 -2
- package/core/element/element.component.d.ts +0 -30
- package/esm2022/core/children/children.component.mjs +0 -60
- package/esm2022/core/children/effect.mjs +0 -2
- package/esm2022/core/element/element.component.mjs +0 -105
- package/esm2022/utils/moving-snap.mjs +0 -372
- package/utils/moving-snap.d.ts +0 -41
package/fesm2022/plait-core.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Directive, Input, Injectable,
|
|
2
|
+
import { IterableDiffers, inject, ViewContainerRef, Directive, Input, Injectable, EventEmitter, ElementRef, Component, ChangeDetectionStrategy, Output, HostBinding, ViewChild, ContentChildren } from '@angular/core';
|
|
3
3
|
import rough from 'roughjs/bin/rough';
|
|
4
4
|
import { timer, Subject, fromEvent } from 'rxjs';
|
|
5
5
|
import { takeUntil, filter, tap } from 'rxjs/operators';
|
|
6
6
|
import { isKeyHotkey, isHotkey } from 'is-hotkey';
|
|
7
|
-
import {
|
|
8
|
-
import { NgFor } from '@angular/common';
|
|
7
|
+
import { createDraft, finishDraft, isDraft } from 'immer';
|
|
9
8
|
|
|
10
9
|
// record richtext type status
|
|
11
10
|
const IS_BOARD_CACHE = new WeakMap();
|
|
12
11
|
const FLUSHING = new WeakMap();
|
|
13
12
|
const NODE_TO_INDEX = new WeakMap();
|
|
14
13
|
const NODE_TO_PARENT = new WeakMap();
|
|
14
|
+
const NODE_TO_G = new WeakMap();
|
|
15
|
+
const NODE_TO_CONTAINER_G = new WeakMap();
|
|
15
16
|
const IS_TEXT_EDITABLE = new WeakMap();
|
|
16
17
|
const BOARD_TO_ON_CHANGE = new WeakMap();
|
|
17
18
|
const BOARD_TO_AFTER_CHANGE = new WeakMap();
|
|
@@ -77,11 +78,11 @@ const Selection = {
|
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
|
|
80
|
-
const sortElements = (board, elements) => {
|
|
81
|
+
const sortElements = (board, elements, ascendingOrder = true) => {
|
|
81
82
|
return [...elements].sort((a, b) => {
|
|
82
83
|
const pathA = PlaitBoard.findPath(board, a);
|
|
83
84
|
const pathB = PlaitBoard.findPath(board, b);
|
|
84
|
-
return pathA[0] - pathB[0];
|
|
85
|
+
return ascendingOrder ? pathA[0] - pathB[0] : pathB[0] - pathA[0];
|
|
85
86
|
});
|
|
86
87
|
};
|
|
87
88
|
|
|
@@ -506,6 +507,8 @@ const SELECTION_RECTANGLE_CLASS_NAME = 'selection-rectangle';
|
|
|
506
507
|
|
|
507
508
|
const HOST_CLASS_NAME = 'plait-board-container';
|
|
508
509
|
const ACTIVE_MOVING_CLASS_NAME = 'active-with-moving';
|
|
510
|
+
const ROTATE_HANDLE_CLASS_NAME = 'rotate-handle';
|
|
511
|
+
const RESIZE_HANDLE_CLASS_NAME = 'resize-handle';
|
|
509
512
|
const SCROLL_BAR_WIDTH = 20;
|
|
510
513
|
const MAX_RADIUS = 16;
|
|
511
514
|
const POINTER_BUTTON = {
|
|
@@ -621,7 +624,215 @@ function hasOnContextChanged(value) {
|
|
|
621
624
|
return false;
|
|
622
625
|
}
|
|
623
626
|
|
|
627
|
+
class ListRender {
|
|
628
|
+
constructor(board, viewContainerRef) {
|
|
629
|
+
this.board = board;
|
|
630
|
+
this.viewContainerRef = viewContainerRef;
|
|
631
|
+
this.children = [];
|
|
632
|
+
this.componentRefs = [];
|
|
633
|
+
this.contexts = [];
|
|
634
|
+
this.differ = null;
|
|
635
|
+
this.initialized = false;
|
|
636
|
+
}
|
|
637
|
+
initialize(children, childrenContext) {
|
|
638
|
+
this.initialized = true;
|
|
639
|
+
this.children = children;
|
|
640
|
+
children.forEach((descendant, index) => {
|
|
641
|
+
NODE_TO_INDEX.set(descendant, index);
|
|
642
|
+
NODE_TO_PARENT.set(descendant, childrenContext.parent);
|
|
643
|
+
const context = getContext(this.board, descendant, index, childrenContext.parent);
|
|
644
|
+
const componentType = getComponentType(this.board, context);
|
|
645
|
+
const componentRef = createPluginComponent(componentType, context, this.viewContainerRef, childrenContext);
|
|
646
|
+
this.componentRefs.push(componentRef);
|
|
647
|
+
this.contexts.push(context);
|
|
648
|
+
});
|
|
649
|
+
const newDiffers = this.viewContainerRef.injector.get(IterableDiffers);
|
|
650
|
+
this.differ = newDiffers.find(children).create(trackBy);
|
|
651
|
+
this.differ.diff(children);
|
|
652
|
+
}
|
|
653
|
+
update(children, childrenContext) {
|
|
654
|
+
if (!this.initialized) {
|
|
655
|
+
this.initialize(children, childrenContext);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
if (!this.differ) {
|
|
659
|
+
throw new Error('Exception: Can not find differ ');
|
|
660
|
+
}
|
|
661
|
+
const { board, parent } = childrenContext;
|
|
662
|
+
const diffResult = this.differ.diff(children);
|
|
663
|
+
if (diffResult) {
|
|
664
|
+
const newContexts = [];
|
|
665
|
+
const newComponentRefs = [];
|
|
666
|
+
let currentIndexForFirstElement = null;
|
|
667
|
+
diffResult.forEachItem((record) => {
|
|
668
|
+
NODE_TO_INDEX.set(record.item, record.currentIndex);
|
|
669
|
+
NODE_TO_PARENT.set(record.item, childrenContext.parent);
|
|
670
|
+
const previousContext = record.previousIndex === null ? undefined : this.contexts[record.previousIndex];
|
|
671
|
+
const context = getContext(board, record.item, record.currentIndex, parent, previousContext);
|
|
672
|
+
if (record.previousIndex === null) {
|
|
673
|
+
const componentType = getComponentType(board, context);
|
|
674
|
+
const componentRef = createPluginComponent(componentType, context, this.viewContainerRef, childrenContext);
|
|
675
|
+
newContexts.push(context);
|
|
676
|
+
newComponentRefs.push(componentRef);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
const componentRef = this.componentRefs[record.previousIndex];
|
|
680
|
+
componentRef.instance.context = context;
|
|
681
|
+
newComponentRefs.push(componentRef);
|
|
682
|
+
newContexts.push(context);
|
|
683
|
+
}
|
|
684
|
+
if (record.item === this.children[0]) {
|
|
685
|
+
currentIndexForFirstElement = record.currentIndex;
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
diffResult.forEachOperation(record => {
|
|
689
|
+
// removed
|
|
690
|
+
if (record.currentIndex === null) {
|
|
691
|
+
const componentRef = this.componentRefs[record.previousIndex];
|
|
692
|
+
componentRef?.destroy();
|
|
693
|
+
}
|
|
694
|
+
// moved
|
|
695
|
+
if (record.previousIndex !== null && record.currentIndex !== null) {
|
|
696
|
+
mountOnItemMove(record.item, record.currentIndex, childrenContext, currentIndexForFirstElement);
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
this.componentRefs = newComponentRefs;
|
|
700
|
+
this.contexts = newContexts;
|
|
701
|
+
this.children = children;
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
const newContexts = [];
|
|
705
|
+
this.children.forEach((element, index) => {
|
|
706
|
+
NODE_TO_INDEX.set(element, index);
|
|
707
|
+
NODE_TO_PARENT.set(element, childrenContext.parent);
|
|
708
|
+
const previousContext = this.contexts[index];
|
|
709
|
+
const previousComponentRef = this.componentRefs[index];
|
|
710
|
+
const context = getContext(board, element, index, parent, previousContext);
|
|
711
|
+
previousComponentRef.instance.context = context;
|
|
712
|
+
newContexts.push(context);
|
|
713
|
+
});
|
|
714
|
+
this.contexts = newContexts;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
destroy() {
|
|
718
|
+
this.children.forEach((element, index) => {
|
|
719
|
+
if (this.componentRefs[index]) {
|
|
720
|
+
this.componentRefs[index].destroy();
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
this.componentRefs = [];
|
|
724
|
+
this.children = [];
|
|
725
|
+
this.contexts = [];
|
|
726
|
+
this.initialized = false;
|
|
727
|
+
this.differ = null;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const trackBy = (index, element) => {
|
|
731
|
+
return element.id;
|
|
732
|
+
};
|
|
733
|
+
const createPluginComponent = (componentType, context, viewContainerRef, childrenContext) => {
|
|
734
|
+
const componentRef = viewContainerRef.createComponent(componentType);
|
|
735
|
+
const instance = componentRef.instance;
|
|
736
|
+
instance.context = context;
|
|
737
|
+
componentRef.changeDetectorRef.detectChanges();
|
|
738
|
+
const g = componentRef.instance.getContainerG();
|
|
739
|
+
mountElementG(context.index, g, childrenContext);
|
|
740
|
+
componentRef.instance.initializeListRender();
|
|
741
|
+
return componentRef;
|
|
742
|
+
};
|
|
743
|
+
const getComponentType = (board, context) => {
|
|
744
|
+
const result = board.drawElement(context);
|
|
745
|
+
return result;
|
|
746
|
+
};
|
|
747
|
+
const getContext = (board, element, index, parent, previousContext) => {
|
|
748
|
+
let isSelected = isSelectedElement(board, element);
|
|
749
|
+
const previousElement = previousContext && previousContext.element;
|
|
750
|
+
if (previousElement && previousElement !== element && isSelectedElement(board, previousElement)) {
|
|
751
|
+
isSelected = true;
|
|
752
|
+
removeSelectedElement(board, previousElement);
|
|
753
|
+
addSelectedElement(board, element);
|
|
754
|
+
}
|
|
755
|
+
const context = {
|
|
756
|
+
element: element,
|
|
757
|
+
parent: parent,
|
|
758
|
+
board: board,
|
|
759
|
+
selected: isSelected,
|
|
760
|
+
index
|
|
761
|
+
};
|
|
762
|
+
return context;
|
|
763
|
+
};
|
|
764
|
+
// the g depth of root element:[1]-[2]-[3]-[4]
|
|
765
|
+
// the g depth of root element and children element(the [2] element has children):
|
|
766
|
+
// [1]-
|
|
767
|
+
// [2]([2-1-1][2-1-2][2-1][2-2][2-3-1][2-3-2][2-3][2])-
|
|
768
|
+
// [3]-
|
|
769
|
+
// [4]
|
|
770
|
+
const mountElementG = (index, g, childrenContext,
|
|
771
|
+
// for moving scene: the current index for first element before moving
|
|
772
|
+
currentIndexForFirstElement = null) => {
|
|
773
|
+
const { parent, parentG } = childrenContext;
|
|
774
|
+
if (PlaitBoard.isBoard(parent)) {
|
|
775
|
+
if (index > 0) {
|
|
776
|
+
const previousElement = parent.children[index - 1];
|
|
777
|
+
const previousContainerG = PlaitElement.getContainerG(previousElement, { suppressThrow: false });
|
|
778
|
+
previousContainerG.insertAdjacentElement('afterend', g);
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
if (currentIndexForFirstElement !== null) {
|
|
782
|
+
const firstElement = parent.children[currentIndexForFirstElement];
|
|
783
|
+
const firstContainerG = firstElement && PlaitElement.getContainerG(firstElement, { suppressThrow: true });
|
|
784
|
+
if (firstElement && firstContainerG) {
|
|
785
|
+
parentG.insertBefore(g, firstContainerG);
|
|
786
|
+
}
|
|
787
|
+
else {
|
|
788
|
+
throw new Error('fail to mount container on moving');
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
parentG.append(g);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
if (index > 0) {
|
|
798
|
+
const previousElement = parent.children[index - 1];
|
|
799
|
+
const previousElementG = PlaitElement.getElementG(previousElement);
|
|
800
|
+
previousElementG.insertAdjacentElement('afterend', g);
|
|
801
|
+
}
|
|
802
|
+
else {
|
|
803
|
+
if (currentIndexForFirstElement) {
|
|
804
|
+
const nextElement = parent.children[currentIndexForFirstElement];
|
|
805
|
+
const nextPath = nextElement && PlaitBoard.findPath(childrenContext.board, nextElement);
|
|
806
|
+
const first = nextPath && PlaitNode.first(childrenContext.board, nextPath);
|
|
807
|
+
const firstContainerG = first && PlaitElement.getContainerG(first, { suppressThrow: false });
|
|
808
|
+
if (firstContainerG) {
|
|
809
|
+
parentG.insertBefore(g, firstContainerG);
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
throw new Error('fail to mount container on moving');
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
let parentElementG = PlaitElement.getElementG(parent);
|
|
817
|
+
parentG.insertBefore(g, parentElementG);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
const mountOnItemMove = (element, index, childrenContext, currentIndexForFirstElement) => {
|
|
823
|
+
const containerG = PlaitElement.getContainerG(element, { suppressThrow: false });
|
|
824
|
+
mountElementG(index, containerG, childrenContext, currentIndexForFirstElement);
|
|
825
|
+
if (element.children && !PlaitElement.isRootElement(element)) {
|
|
826
|
+
element.children.forEach((child, index) => {
|
|
827
|
+
mountOnItemMove(child, index, { ...childrenContext, parent: element }, null);
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
|
|
624
832
|
class PlaitPluginElementComponent {
|
|
833
|
+
get hasChildren() {
|
|
834
|
+
return !!this.element.children;
|
|
835
|
+
}
|
|
625
836
|
set context(value) {
|
|
626
837
|
if (hasBeforeContextChange(this)) {
|
|
627
838
|
this.beforeContextChange(value);
|
|
@@ -632,20 +843,28 @@ class PlaitPluginElementComponent {
|
|
|
632
843
|
ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
633
844
|
}
|
|
634
845
|
if (this.initialized) {
|
|
846
|
+
const elementG = this.getElementG();
|
|
847
|
+
const containerG = this.getContainerG();
|
|
848
|
+
NODE_TO_G.set(this.element, elementG);
|
|
849
|
+
NODE_TO_CONTAINER_G.set(this.element, containerG);
|
|
850
|
+
this.updateListRender();
|
|
635
851
|
this.cdr.markForCheck();
|
|
636
852
|
if (hasOnContextChanged(this)) {
|
|
637
853
|
this.onContextChanged(value, previousContext);
|
|
638
854
|
}
|
|
639
855
|
}
|
|
640
856
|
else {
|
|
641
|
-
if (PlaitElement.isRootElement(this.element) && this.
|
|
642
|
-
this.
|
|
643
|
-
this.
|
|
644
|
-
this.
|
|
857
|
+
if (PlaitElement.isRootElement(this.element) && this.hasChildren) {
|
|
858
|
+
this._g = createG();
|
|
859
|
+
this._containerG = createG();
|
|
860
|
+
this._containerG.append(this._g);
|
|
645
861
|
}
|
|
646
862
|
else {
|
|
647
|
-
this.
|
|
863
|
+
this._g = createG();
|
|
864
|
+
this._containerG = this._g;
|
|
648
865
|
}
|
|
866
|
+
NODE_TO_G.set(this.element, this._g);
|
|
867
|
+
NODE_TO_CONTAINER_G.set(this.element, this._containerG);
|
|
649
868
|
}
|
|
650
869
|
}
|
|
651
870
|
get context() {
|
|
@@ -660,25 +879,79 @@ class PlaitPluginElementComponent {
|
|
|
660
879
|
get selected() {
|
|
661
880
|
return this.context && this.context.selected;
|
|
662
881
|
}
|
|
663
|
-
|
|
664
|
-
return this.
|
|
882
|
+
getContainerG() {
|
|
883
|
+
return this._containerG;
|
|
884
|
+
}
|
|
885
|
+
getElementG() {
|
|
886
|
+
return this._g;
|
|
665
887
|
}
|
|
666
888
|
constructor(cdr) {
|
|
667
889
|
this.cdr = cdr;
|
|
890
|
+
this.viewContainerRef = inject(ViewContainerRef);
|
|
668
891
|
this.initialized = false;
|
|
669
892
|
}
|
|
670
893
|
ngOnInit() {
|
|
671
894
|
if (this.element.type) {
|
|
672
|
-
|
|
895
|
+
this.getContainerG().setAttribute(`plait-${this.element.type}`, 'true');
|
|
896
|
+
}
|
|
897
|
+
if (this.hasChildren) {
|
|
898
|
+
if (PlaitElement.isRootElement(this.element)) {
|
|
899
|
+
this._rootContainerG = this._containerG;
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
const path = PlaitBoard.findPath(this.board, this.element);
|
|
903
|
+
const rootNode = PlaitNode.get(this.board, path.slice(0, 1));
|
|
904
|
+
this._rootContainerG = PlaitElement.getContainerG(rootNode, { suppressThrow: false });
|
|
905
|
+
}
|
|
673
906
|
}
|
|
907
|
+
this.getContainerG().setAttribute('plait-data-id', this.element.id);
|
|
674
908
|
this.initialized = true;
|
|
675
909
|
}
|
|
910
|
+
initializeListRender() {
|
|
911
|
+
if (this.hasChildren) {
|
|
912
|
+
this.listRender = new ListRender(this.board, this.viewContainerRef);
|
|
913
|
+
if (this.board.isExpanded(this.element)) {
|
|
914
|
+
this.listRender.initialize(this.element.children, this.initializeChildrenContext());
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
updateListRender() {
|
|
919
|
+
if (this.hasChildren) {
|
|
920
|
+
if (!this.listRender) {
|
|
921
|
+
throw new Error('incorrectly initialize list render');
|
|
922
|
+
}
|
|
923
|
+
if (this.board.isExpanded(this.element)) {
|
|
924
|
+
this.listRender.update(this.element.children, this.initializeChildrenContext());
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
if (this.listRender.initialized) {
|
|
928
|
+
this.listRender.destroy();
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
initializeChildrenContext() {
|
|
934
|
+
if (!this._rootContainerG) {
|
|
935
|
+
throw new Error('can not resolve root container g');
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
board: this.board,
|
|
939
|
+
parent: this.element,
|
|
940
|
+
parentG: this._rootContainerG
|
|
941
|
+
};
|
|
942
|
+
}
|
|
676
943
|
ngOnDestroy() {
|
|
677
944
|
if (ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
678
945
|
ELEMENT_TO_COMPONENT.delete(this.element);
|
|
679
946
|
}
|
|
947
|
+
if (NODE_TO_G.get(this.element) === this._g) {
|
|
948
|
+
NODE_TO_G.delete(this.element);
|
|
949
|
+
}
|
|
950
|
+
if (NODE_TO_CONTAINER_G.get(this.element) === this._containerG) {
|
|
951
|
+
NODE_TO_CONTAINER_G.delete(this.element);
|
|
952
|
+
}
|
|
680
953
|
removeSelectedElement(this.board, this.element);
|
|
681
|
-
|
|
954
|
+
this.getContainerG().remove();
|
|
682
955
|
}
|
|
683
956
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitPluginElementComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
684
957
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.2.4", type: PlaitPluginElementComponent, inputs: { context: "context" }, ngImport: i0 }); }
|
|
@@ -968,6 +1241,42 @@ function toFixed(v) {
|
|
|
968
1241
|
function approximately(a, b, precision = 0.000001) {
|
|
969
1242
|
return Math.abs(a - b) <= precision;
|
|
970
1243
|
}
|
|
1244
|
+
// https://medium.com/@steveruiz/find-the-points-where-a-line-segment-intercepts-an-angled-ellipse-in-javascript-typescript-e451524beece
|
|
1245
|
+
function getCrossingPointsBetweenEllipseAndSegment(startPoint, endPoint, cx, cy, rx, ry, segment_only = true) {
|
|
1246
|
+
// If the ellipse or line segment are empty, return no tValues.
|
|
1247
|
+
if (rx === 0 || ry === 0 || (startPoint[0] === endPoint[0] && startPoint[1] === endPoint[1])) {
|
|
1248
|
+
return [];
|
|
1249
|
+
}
|
|
1250
|
+
rx = rx < 0 ? rx : -rx;
|
|
1251
|
+
ry = ry < 0 ? ry : -ry;
|
|
1252
|
+
startPoint[0] -= cx;
|
|
1253
|
+
startPoint[1] -= cy;
|
|
1254
|
+
endPoint[0] -= cx;
|
|
1255
|
+
endPoint[1] -= cy;
|
|
1256
|
+
// Calculate the quadratic parameters.
|
|
1257
|
+
var A = ((endPoint[0] - startPoint[0]) * (endPoint[0] - startPoint[0])) / rx / rx +
|
|
1258
|
+
((endPoint[1] - startPoint[1]) * (endPoint[1] - startPoint[1])) / ry / ry;
|
|
1259
|
+
var B = (2 * startPoint[0] * (endPoint[0] - startPoint[0])) / rx / rx + (2 * startPoint[1] * (endPoint[1] - startPoint[1])) / ry / ry;
|
|
1260
|
+
var C = (startPoint[0] * startPoint[0]) / rx / rx + (startPoint[1] * startPoint[1]) / ry / ry - 1;
|
|
1261
|
+
// Make a list of t values (normalized points on the line where intersections occur).
|
|
1262
|
+
var tValues = [];
|
|
1263
|
+
// Calculate the discriminant.
|
|
1264
|
+
var discriminant = B * B - 4 * A * C;
|
|
1265
|
+
if (discriminant === 0) {
|
|
1266
|
+
// One real solution.
|
|
1267
|
+
tValues.push(-B / 2 / A);
|
|
1268
|
+
}
|
|
1269
|
+
else if (discriminant > 0) {
|
|
1270
|
+
// Two real solutions.
|
|
1271
|
+
tValues.push((-B + Math.sqrt(discriminant)) / 2 / A);
|
|
1272
|
+
tValues.push((-B - Math.sqrt(discriminant)) / 2 / A);
|
|
1273
|
+
}
|
|
1274
|
+
return (tValues
|
|
1275
|
+
// Filter to only points that are on the segment.
|
|
1276
|
+
.filter(t => !segment_only || (t >= 0 && t <= 1))
|
|
1277
|
+
// Solve for points.
|
|
1278
|
+
.map(t => [startPoint[0] + (endPoint[0] - startPoint[0]) * t + cx, startPoint[1] + (endPoint[1] - startPoint[1]) * t + cy]));
|
|
1279
|
+
}
|
|
971
1280
|
|
|
972
1281
|
function isInPlaitBoard(board, x, y) {
|
|
973
1282
|
const plaitBoardElement = PlaitBoard.getBoardContainer(board);
|
|
@@ -1055,6 +1364,42 @@ function uniqueById(elements) {
|
|
|
1055
1364
|
});
|
|
1056
1365
|
return Array.from(uniqueMap.values());
|
|
1057
1366
|
}
|
|
1367
|
+
const findLastIndex = (array, cb, fromIndex = array.length - 1) => {
|
|
1368
|
+
if (fromIndex < 0) {
|
|
1369
|
+
fromIndex = array.length + fromIndex;
|
|
1370
|
+
}
|
|
1371
|
+
fromIndex = Math.min(array.length - 1, Math.max(fromIndex, 0));
|
|
1372
|
+
let index = fromIndex + 1;
|
|
1373
|
+
while (--index > -1) {
|
|
1374
|
+
if (cb(array[index], index, array)) {
|
|
1375
|
+
return index;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
return -1;
|
|
1379
|
+
};
|
|
1380
|
+
const findIndex = (array, cb, fromIndex = 0) => {
|
|
1381
|
+
// fromIndex = 2
|
|
1382
|
+
if (fromIndex < 0) {
|
|
1383
|
+
fromIndex = array.length + fromIndex;
|
|
1384
|
+
}
|
|
1385
|
+
fromIndex = Math.min(array.length, Math.max(fromIndex, 0));
|
|
1386
|
+
let index = fromIndex - 1;
|
|
1387
|
+
while (++index < array.length) {
|
|
1388
|
+
if (cb(array[index], index, array)) {
|
|
1389
|
+
return index;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return -1;
|
|
1393
|
+
};
|
|
1394
|
+
const isIndicesContinuous = (indexes) => {
|
|
1395
|
+
indexes.sort((a, b) => a - b);
|
|
1396
|
+
for (let i = 1; i < indexes.length; i++) {
|
|
1397
|
+
if (indexes[i] !== indexes[i - 1] + 1) {
|
|
1398
|
+
return false;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
return true;
|
|
1402
|
+
};
|
|
1058
1403
|
|
|
1059
1404
|
/**
|
|
1060
1405
|
* Check whether to merge an operation into the previous operation.
|
|
@@ -1694,157 +2039,456 @@ const setIsFromViewportChange = (board, state) => {
|
|
|
1694
2039
|
};
|
|
1695
2040
|
function scrollToRectangle(board, client) { }
|
|
1696
2041
|
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
}
|
|
1705
|
-
const
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
value[key] = null;
|
|
1710
|
-
BOARD_TO_RAF.set(board, value);
|
|
1711
|
-
PlaitBoard.isAlive(board) && fn();
|
|
1712
|
-
});
|
|
1713
|
-
const state = getRAFState(board);
|
|
1714
|
-
state[key] = timerId;
|
|
1715
|
-
BOARD_TO_RAF.set(board, state);
|
|
1716
|
-
};
|
|
1717
|
-
let timerId = getTimerId(board, key);
|
|
1718
|
-
if (timerId !== null) {
|
|
1719
|
-
cancelAnimationFrame(timerId);
|
|
1720
|
-
}
|
|
1721
|
-
scheduleFunc();
|
|
1722
|
-
};
|
|
1723
|
-
const debounce = (func, wait, options) => {
|
|
1724
|
-
let timerSubscription = null;
|
|
1725
|
-
return () => {
|
|
1726
|
-
if (timerSubscription && !timerSubscription.closed) {
|
|
1727
|
-
timerSubscription.unsubscribe();
|
|
1728
|
-
timerSubscription = timer(wait).subscribe(() => {
|
|
1729
|
-
func();
|
|
1730
|
-
});
|
|
2042
|
+
const Path = {
|
|
2043
|
+
/**
|
|
2044
|
+
* Get a list of ancestor paths for a given path.
|
|
2045
|
+
*
|
|
2046
|
+
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
2047
|
+
* `reverse: true` option is passed, they are reversed.
|
|
2048
|
+
*/
|
|
2049
|
+
ancestors(path, options = {}) {
|
|
2050
|
+
const { reverse = false } = options;
|
|
2051
|
+
let paths = Path.levels(path, options);
|
|
2052
|
+
if (reverse) {
|
|
2053
|
+
paths = paths.slice(1);
|
|
1731
2054
|
}
|
|
1732
2055
|
else {
|
|
1733
|
-
|
|
1734
|
-
timer(0).subscribe(() => {
|
|
1735
|
-
func();
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
|
-
timerSubscription = timer(wait).subscribe();
|
|
2056
|
+
paths = paths.slice(0, -1);
|
|
1739
2057
|
}
|
|
1740
|
-
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
2058
|
+
return paths;
|
|
2059
|
+
},
|
|
2060
|
+
/**
|
|
2061
|
+
* Get a list of paths at every level down to a path. Note: this is the same
|
|
2062
|
+
* as `Path.ancestors`, but including the path itself.
|
|
2063
|
+
*
|
|
2064
|
+
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
2065
|
+
* true` option is passed, they are reversed.
|
|
2066
|
+
*/
|
|
2067
|
+
levels(path, options = {}) {
|
|
2068
|
+
const { reverse = false } = options;
|
|
2069
|
+
const list = [];
|
|
2070
|
+
for (let i = 0; i <= path.length; i++) {
|
|
2071
|
+
list.push(path.slice(0, i));
|
|
2072
|
+
}
|
|
2073
|
+
if (reverse) {
|
|
2074
|
+
list.reverse();
|
|
2075
|
+
}
|
|
2076
|
+
return list;
|
|
2077
|
+
},
|
|
2078
|
+
parent(path) {
|
|
2079
|
+
if (path.length === 0) {
|
|
2080
|
+
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
2081
|
+
}
|
|
2082
|
+
return path.slice(0, -1);
|
|
2083
|
+
},
|
|
2084
|
+
next(path) {
|
|
2085
|
+
if (path.length === 0) {
|
|
2086
|
+
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
2087
|
+
}
|
|
2088
|
+
const last = path[path.length - 1];
|
|
2089
|
+
return path.slice(0, -1).concat(last + 1);
|
|
2090
|
+
},
|
|
2091
|
+
hasPrevious(path) {
|
|
2092
|
+
return path[path.length - 1] > 0;
|
|
2093
|
+
},
|
|
2094
|
+
previous(path) {
|
|
2095
|
+
if (path.length === 0) {
|
|
2096
|
+
throw new Error(`Cannot get the previous path of a root path [${path}], because it has no previous index.`);
|
|
2097
|
+
}
|
|
2098
|
+
const last = path[path.length - 1];
|
|
2099
|
+
if (last <= 0) {
|
|
2100
|
+
throw new Error(`Cannot get the previous path of a first child path [${path}] because it would result in a negative index.`);
|
|
2101
|
+
}
|
|
2102
|
+
return path.slice(0, -1).concat(last - 1);
|
|
2103
|
+
},
|
|
2104
|
+
/**
|
|
2105
|
+
* Check if a path is an ancestor of another.
|
|
2106
|
+
*/
|
|
2107
|
+
isAncestor(path, another) {
|
|
2108
|
+
return path.length < another.length && Path.compare(path, another) === 0;
|
|
2109
|
+
},
|
|
2110
|
+
/**
|
|
2111
|
+
* Compare a path to another, returning an integer indicating whether the path
|
|
2112
|
+
* was before, at, or after the other.
|
|
2113
|
+
*
|
|
2114
|
+
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
2115
|
+
* directly above or below the other. If you want exact matching, use
|
|
2116
|
+
* [[Path.equals]] instead.
|
|
2117
|
+
*/
|
|
2118
|
+
compare(path, another) {
|
|
2119
|
+
const min = Math.min(path.length, another.length);
|
|
2120
|
+
for (let i = 0; i < min; i++) {
|
|
2121
|
+
if (path[i] < another[i])
|
|
2122
|
+
return -1;
|
|
2123
|
+
if (path[i] > another[i])
|
|
2124
|
+
return 1;
|
|
2125
|
+
}
|
|
2126
|
+
return 0;
|
|
2127
|
+
},
|
|
2128
|
+
/**
|
|
2129
|
+
* Check if a path is exactly equal to another.
|
|
2130
|
+
*/
|
|
2131
|
+
equals(path, another) {
|
|
2132
|
+
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
2133
|
+
},
|
|
2134
|
+
/**
|
|
2135
|
+
* Check if a path ends before one of the indexes in another.
|
|
2136
|
+
*/
|
|
2137
|
+
endsBefore(path, another) {
|
|
2138
|
+
const i = path.length - 1;
|
|
2139
|
+
const as = path.slice(0, i);
|
|
2140
|
+
const bs = another.slice(0, i);
|
|
2141
|
+
const av = path[i];
|
|
2142
|
+
const bv = another[i];
|
|
2143
|
+
return Path.equals(as, bs) && av < bv;
|
|
2144
|
+
},
|
|
2145
|
+
/**
|
|
2146
|
+
* Check if a path is a sibling of another.
|
|
2147
|
+
*/
|
|
2148
|
+
isSibling(path, another) {
|
|
2149
|
+
if (path.length !== another.length) {
|
|
2150
|
+
return false;
|
|
2151
|
+
}
|
|
2152
|
+
const as = path.slice(0, -1);
|
|
2153
|
+
const bs = another.slice(0, -1);
|
|
2154
|
+
const al = path[path.length - 1];
|
|
2155
|
+
const bl = another[another.length - 1];
|
|
2156
|
+
return al !== bl && Path.equals(as, bs);
|
|
2157
|
+
},
|
|
2158
|
+
transform(path, operation) {
|
|
2159
|
+
if (!path)
|
|
2160
|
+
return null;
|
|
2161
|
+
// PERF: use destructing instead of immer
|
|
2162
|
+
const p = [...path];
|
|
2163
|
+
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
2164
|
+
if (path.length === 0) {
|
|
2165
|
+
return p;
|
|
2166
|
+
}
|
|
2167
|
+
switch (operation.type) {
|
|
2168
|
+
case 'insert_node': {
|
|
2169
|
+
const { path: op } = operation;
|
|
2170
|
+
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
2171
|
+
p[op.length - 1] += 1;
|
|
2172
|
+
}
|
|
2173
|
+
break;
|
|
2174
|
+
}
|
|
2175
|
+
case 'remove_node': {
|
|
2176
|
+
const { path: op } = operation;
|
|
2177
|
+
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
2178
|
+
return null;
|
|
2179
|
+
}
|
|
2180
|
+
else if (Path.endsBefore(op, p)) {
|
|
2181
|
+
p[op.length - 1] -= 1;
|
|
2182
|
+
}
|
|
2183
|
+
break;
|
|
2184
|
+
}
|
|
2185
|
+
case 'move_node': {
|
|
2186
|
+
const { path: op, newPath: onp } = operation;
|
|
2187
|
+
// If the old and new path are the same, it's a no-op.
|
|
2188
|
+
if (Path.equals(op, onp)) {
|
|
2189
|
+
return p;
|
|
2190
|
+
}
|
|
2191
|
+
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
2192
|
+
const copy = onp.slice();
|
|
2193
|
+
if (Path.endsBefore(op, onp) && op.length < onp.length) {
|
|
2194
|
+
copy[op.length - 1] -= 1;
|
|
2195
|
+
}
|
|
2196
|
+
return copy.concat(p.slice(op.length));
|
|
2197
|
+
}
|
|
2198
|
+
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
2199
|
+
if (Path.endsBefore(op, p)) {
|
|
2200
|
+
p[op.length - 1] -= 1;
|
|
2201
|
+
}
|
|
2202
|
+
else {
|
|
2203
|
+
p[op.length - 1] += 1;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
2207
|
+
if (Path.endsBefore(op, p)) {
|
|
2208
|
+
p[op.length - 1] -= 1;
|
|
2209
|
+
}
|
|
2210
|
+
p[onp.length - 1] += 1;
|
|
2211
|
+
}
|
|
2212
|
+
else if (Path.endsBefore(op, p)) {
|
|
2213
|
+
if (Path.equals(onp, p)) {
|
|
2214
|
+
p[onp.length - 1] += 1;
|
|
2215
|
+
}
|
|
2216
|
+
p[op.length - 1] -= 1;
|
|
2217
|
+
}
|
|
2218
|
+
break;
|
|
2219
|
+
}
|
|
2220
|
+
}
|
|
2221
|
+
return p;
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
|
|
2225
|
+
const PlaitNode = {
|
|
2226
|
+
parent: (board, path) => {
|
|
2227
|
+
const parentPath = Path.parent(path);
|
|
2228
|
+
const p = PlaitNode.get(board, parentPath);
|
|
2229
|
+
return p;
|
|
2230
|
+
},
|
|
2231
|
+
/**
|
|
2232
|
+
* Return a generator of all the ancestor nodes above a specific path.
|
|
2233
|
+
*
|
|
2234
|
+
* By default the order is top-down, from highest to lowest ancestor in
|
|
2235
|
+
* the tree, but you can pass the `reverse: true` option to go bottom-up.
|
|
2236
|
+
*/
|
|
2237
|
+
*parents(root, path, options = {}) {
|
|
2238
|
+
for (const p of Path.ancestors(path, options)) {
|
|
2239
|
+
const n = PlaitNode.get(root, p);
|
|
2240
|
+
yield n;
|
|
2241
|
+
}
|
|
2242
|
+
},
|
|
2243
|
+
get(root, path) {
|
|
2244
|
+
let node = root;
|
|
2245
|
+
for (let i = 0; i < path.length; i++) {
|
|
2246
|
+
const p = path[i];
|
|
2247
|
+
if (!node || !node.children || !node.children[p]) {
|
|
2248
|
+
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
2249
|
+
}
|
|
2250
|
+
node = node.children[p];
|
|
2251
|
+
}
|
|
2252
|
+
return node;
|
|
2253
|
+
},
|
|
2254
|
+
last(board, path) {
|
|
2255
|
+
let n = PlaitNode.get(board, path);
|
|
2256
|
+
while (n && n.children && n.children.length > 0) {
|
|
2257
|
+
const i = n.children.length - 1;
|
|
2258
|
+
n = n.children[i];
|
|
2259
|
+
}
|
|
2260
|
+
return n;
|
|
2261
|
+
},
|
|
2262
|
+
first(board, path) {
|
|
2263
|
+
const p = path.slice();
|
|
2264
|
+
let n = PlaitNode.get(board, p);
|
|
2265
|
+
if (!n.children) {
|
|
2266
|
+
return n;
|
|
2267
|
+
}
|
|
2268
|
+
while (n) {
|
|
2269
|
+
if (n.children.length === 0) {
|
|
2270
|
+
break;
|
|
2271
|
+
}
|
|
2272
|
+
else {
|
|
2273
|
+
n = n.children[0];
|
|
2274
|
+
p.push(0);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
return n;
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
|
|
2281
|
+
function insertNode(board, node, path) {
|
|
2282
|
+
const operation = { type: 'insert_node', node, path };
|
|
2283
|
+
board.apply(operation);
|
|
2284
|
+
}
|
|
2285
|
+
function setNode(board, props, path) {
|
|
2286
|
+
const properties = {};
|
|
2287
|
+
const newProperties = {};
|
|
2288
|
+
const node = PlaitNode.get(board, path);
|
|
2289
|
+
for (const k in props) {
|
|
2290
|
+
if (node[k] !== props[k]) {
|
|
2291
|
+
if (node.hasOwnProperty(k)) {
|
|
2292
|
+
properties[k] = node[k];
|
|
2293
|
+
}
|
|
2294
|
+
if (props[k] != null)
|
|
2295
|
+
newProperties[k] = props[k];
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
const operation = { type: 'set_node', properties, newProperties, path };
|
|
2299
|
+
board.apply(operation);
|
|
2300
|
+
}
|
|
2301
|
+
function removeNode(board, path) {
|
|
2302
|
+
const node = PlaitNode.get(board, path);
|
|
2303
|
+
const operation = { type: 'remove_node', path, node };
|
|
2304
|
+
board.apply(operation);
|
|
2305
|
+
}
|
|
2306
|
+
function moveNode(board, path, newPath) {
|
|
2307
|
+
const operation = { type: 'move_node', path, newPath };
|
|
2308
|
+
board.apply(operation);
|
|
2309
|
+
}
|
|
2310
|
+
const NodeTransforms = {
|
|
2311
|
+
insertNode,
|
|
2312
|
+
setNode,
|
|
2313
|
+
removeNode,
|
|
2314
|
+
moveNode
|
|
2315
|
+
};
|
|
2316
|
+
|
|
2317
|
+
const BOARD_TO_RAF = new WeakMap();
|
|
2318
|
+
const getTimerId = (board, key) => {
|
|
2319
|
+
const state = getRAFState(board);
|
|
2320
|
+
return state[key] || null;
|
|
2321
|
+
};
|
|
2322
|
+
const getRAFState = (board) => {
|
|
2323
|
+
return BOARD_TO_RAF.get(board) || {};
|
|
2324
|
+
};
|
|
2325
|
+
const throttleRAF = (board, key, fn) => {
|
|
2326
|
+
const scheduleFunc = () => {
|
|
2327
|
+
let timerId = requestAnimationFrame(() => {
|
|
2328
|
+
const value = BOARD_TO_RAF.get(board) || {};
|
|
2329
|
+
value[key] = null;
|
|
2330
|
+
BOARD_TO_RAF.set(board, value);
|
|
2331
|
+
PlaitBoard.isAlive(board) && fn();
|
|
2332
|
+
});
|
|
2333
|
+
const state = getRAFState(board);
|
|
2334
|
+
state[key] = timerId;
|
|
2335
|
+
BOARD_TO_RAF.set(board, state);
|
|
2336
|
+
};
|
|
2337
|
+
let timerId = getTimerId(board, key);
|
|
2338
|
+
if (timerId !== null) {
|
|
2339
|
+
cancelAnimationFrame(timerId);
|
|
2340
|
+
}
|
|
2341
|
+
scheduleFunc();
|
|
2342
|
+
};
|
|
2343
|
+
const debounce = (func, wait, options) => {
|
|
2344
|
+
let timerSubscription = null;
|
|
2345
|
+
return () => {
|
|
2346
|
+
if (timerSubscription && !timerSubscription.closed) {
|
|
2347
|
+
timerSubscription.unsubscribe();
|
|
2348
|
+
timerSubscription = timer(wait).subscribe(() => {
|
|
2349
|
+
func();
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
else {
|
|
2353
|
+
if (options?.leading) {
|
|
2354
|
+
timer(0).subscribe(() => {
|
|
2355
|
+
func();
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
timerSubscription = timer(wait).subscribe();
|
|
2359
|
+
}
|
|
2360
|
+
};
|
|
2361
|
+
};
|
|
2362
|
+
const getElementsIndices = (board, elements) => {
|
|
2363
|
+
sortElements(board, elements);
|
|
2364
|
+
return elements.map(item => {
|
|
2365
|
+
return board.children.map(item => item.id).indexOf(item.id);
|
|
2366
|
+
});
|
|
2367
|
+
};
|
|
2368
|
+
const getHighestIndexOfElement = (board, elements) => {
|
|
2369
|
+
const indices = getElementsIndices(board, elements);
|
|
2370
|
+
return indices[indices.length - 1];
|
|
2371
|
+
};
|
|
2372
|
+
const moveElementsToNewPath = (board, moveOptions) => {
|
|
2373
|
+
moveOptions
|
|
2374
|
+
.map(item => {
|
|
2375
|
+
const path = PlaitBoard.findPath(board, item.element);
|
|
2376
|
+
const ref = board.pathRef(path);
|
|
2377
|
+
return () => {
|
|
2378
|
+
ref.current && NodeTransforms.moveNode(board, ref.current, item.newPath);
|
|
2379
|
+
ref.unref();
|
|
2380
|
+
};
|
|
2381
|
+
})
|
|
2382
|
+
.forEach(action => {
|
|
2383
|
+
action();
|
|
2384
|
+
});
|
|
2385
|
+
};
|
|
2386
|
+
|
|
2387
|
+
const IS_DRAGGING = new WeakMap();
|
|
2388
|
+
const isDragging = (board) => {
|
|
2389
|
+
return !!IS_DRAGGING.get(board);
|
|
2390
|
+
};
|
|
2391
|
+
const setDragging = (board, state) => {
|
|
2392
|
+
IS_DRAGGING.set(board, state);
|
|
2393
|
+
};
|
|
2394
|
+
|
|
2395
|
+
const getMovingElements = (board) => {
|
|
2396
|
+
return BOARD_TO_MOVING_ELEMENT.get(board) || [];
|
|
2397
|
+
};
|
|
2398
|
+
const isMovingElements = (board) => {
|
|
2399
|
+
return (BOARD_TO_MOVING_ELEMENT.get(board) || []).length > 0;
|
|
2400
|
+
};
|
|
2401
|
+
const removeMovingElements = (board) => {
|
|
2402
|
+
BOARD_TO_MOVING_ELEMENT.delete(board);
|
|
2403
|
+
setDragging(board, false);
|
|
2404
|
+
};
|
|
2405
|
+
const cacheMovingElements = (board, elements) => {
|
|
2406
|
+
BOARD_TO_MOVING_ELEMENT.set(board, elements);
|
|
2407
|
+
setDragging(board, true);
|
|
2408
|
+
};
|
|
2409
|
+
|
|
2410
|
+
const IMAGE_CONTAINER = 'plait-image-container';
|
|
2411
|
+
/**
|
|
2412
|
+
* Is element node
|
|
2413
|
+
* @param node
|
|
2414
|
+
* @returns
|
|
2415
|
+
*/
|
|
2416
|
+
function isElementNode(node) {
|
|
2417
|
+
return node.nodeType === Node.ELEMENT_NODE;
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* load image resources
|
|
2421
|
+
* @param url image url
|
|
2422
|
+
* @returns image element
|
|
2423
|
+
*/
|
|
2424
|
+
function loadImage(src) {
|
|
2425
|
+
return new Promise((resolve, reject) => {
|
|
2426
|
+
const img = new Image();
|
|
2427
|
+
img.crossOrigin = 'Anonymous';
|
|
2428
|
+
img.onload = () => resolve(img);
|
|
2429
|
+
img.onerror = () => reject(new Error('Failed to load image'));
|
|
2430
|
+
img.src = src;
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
/**
|
|
2434
|
+
* create and return canvas and context
|
|
2435
|
+
* @param width canvas width
|
|
2436
|
+
* @param height canvas height
|
|
2437
|
+
* @param fillStyle fill style
|
|
2438
|
+
* @returns canvas and context
|
|
2439
|
+
*/
|
|
2440
|
+
function createCanvas(width, height, fillStyle = 'transparent') {
|
|
2441
|
+
const canvas = document.createElement('canvas');
|
|
2442
|
+
const ctx = canvas.getContext('2d');
|
|
2443
|
+
canvas.width = width;
|
|
2444
|
+
canvas.height = height;
|
|
2445
|
+
canvas.style.width = `${width}px`;
|
|
2446
|
+
canvas.style.height = `${height}px`;
|
|
2447
|
+
ctx.strokeStyle = '#ffffff';
|
|
2448
|
+
ctx.fillStyle = fillStyle;
|
|
2449
|
+
ctx.fillRect(0, 0, width, height);
|
|
2450
|
+
return {
|
|
2451
|
+
canvas,
|
|
2452
|
+
ctx
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2456
|
+
* convert image to base64
|
|
2457
|
+
* @param url image url
|
|
2458
|
+
* @returns image base64
|
|
2459
|
+
*/
|
|
2460
|
+
function convertImageToBase64(url) {
|
|
2461
|
+
return loadImage(url).then(img => {
|
|
2462
|
+
const { canvas, ctx } = createCanvas(img.width, img.height);
|
|
2463
|
+
ctx?.drawImage(img, 0, 0);
|
|
2464
|
+
return canvas.toDataURL('image/png');
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* clone node style
|
|
2469
|
+
* @param nativeNode source node
|
|
2470
|
+
* @param clonedNode clone node
|
|
2471
|
+
*/
|
|
2472
|
+
function cloneCSSStyle(nativeNode, clonedNode) {
|
|
2473
|
+
const targetStyle = clonedNode?.style;
|
|
2474
|
+
if (!targetStyle) {
|
|
2475
|
+
return;
|
|
2476
|
+
}
|
|
2477
|
+
const sourceStyle = window.getComputedStyle(nativeNode);
|
|
2478
|
+
if (sourceStyle.cssText) {
|
|
2479
|
+
targetStyle.cssText = sourceStyle.cssText;
|
|
2480
|
+
targetStyle.transformOrigin = sourceStyle.transformOrigin;
|
|
2481
|
+
}
|
|
2482
|
+
else {
|
|
2483
|
+
Array.from(sourceStyle).forEach(name => {
|
|
2484
|
+
let value = sourceStyle.getPropertyValue(name);
|
|
2485
|
+
targetStyle.setProperty(name, value, sourceStyle.getPropertyPriority(name));
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* batch clone target styles
|
|
2491
|
+
* @param sourceNode
|
|
1848
2492
|
* @param cloneNode
|
|
1849
2493
|
* @param inlineStyleClassNames
|
|
1850
2494
|
*/
|
|
@@ -1899,7 +2543,7 @@ async function cloneSvg(board, elements, rectangle, options) {
|
|
|
1899
2543
|
const { width, height, x, y } = rectangle;
|
|
1900
2544
|
const { padding = 4, inlineStyleClassNames } = options;
|
|
1901
2545
|
const sourceSvg = PlaitBoard.getHost(board);
|
|
1902
|
-
const selectedGElements = elements.map(value => PlaitElement.
|
|
2546
|
+
const selectedGElements = elements.map(value => PlaitElement.getElementG(value));
|
|
1903
2547
|
const cloneSvgElement = sourceSvg.cloneNode();
|
|
1904
2548
|
const newHostElement = PlaitBoard.getElementHost(board).cloneNode();
|
|
1905
2549
|
cloneSvgElement.style.width = `${width}px`;
|
|
@@ -2013,19 +2657,19 @@ const getProbablySupportsClipboardWriteText = () => {
|
|
|
2013
2657
|
const getProbablySupportsClipboardRead = () => {
|
|
2014
2658
|
return 'clipboard' in navigator && 'read' in navigator.clipboard;
|
|
2015
2659
|
};
|
|
2016
|
-
const createClipboardContext = (type,
|
|
2660
|
+
const createClipboardContext = (type, elements, text) => {
|
|
2017
2661
|
return {
|
|
2018
2662
|
type,
|
|
2019
|
-
|
|
2663
|
+
elements,
|
|
2020
2664
|
text
|
|
2021
2665
|
};
|
|
2022
2666
|
};
|
|
2023
2667
|
const addClipboardContext = (clipboardContext, addition) => {
|
|
2024
|
-
const { type,
|
|
2668
|
+
const { type, elements, text } = clipboardContext;
|
|
2025
2669
|
if (type === addition.type) {
|
|
2026
2670
|
return {
|
|
2027
2671
|
type,
|
|
2028
|
-
|
|
2672
|
+
elements: elements.concat(addition.elements),
|
|
2029
2673
|
text: text + ' ' + addition.text
|
|
2030
2674
|
};
|
|
2031
2675
|
}
|
|
@@ -2138,309 +2782,87 @@ const getClipboardData = async (dataTransfer) => {
|
|
|
2138
2782
|
let clipboardData = {};
|
|
2139
2783
|
if (dataTransfer) {
|
|
2140
2784
|
if (dataTransfer.files.length) {
|
|
2141
|
-
return { files: Array.from(dataTransfer.files) };
|
|
2142
|
-
}
|
|
2143
|
-
clipboardData = getDataTransferClipboard(dataTransfer);
|
|
2144
|
-
if (Object.keys(clipboardData).length === 0) {
|
|
2145
|
-
clipboardData = getDataTransferClipboardText(dataTransfer);
|
|
2146
|
-
}
|
|
2147
|
-
return clipboardData;
|
|
2148
|
-
}
|
|
2149
|
-
if (getProbablySupportsClipboardRead()) {
|
|
2150
|
-
return await getNavigatorClipboard();
|
|
2151
|
-
}
|
|
2152
|
-
return clipboardData;
|
|
2153
|
-
};
|
|
2154
|
-
const setClipboardData = async (dataTransfer, clipboardContext) => {
|
|
2155
|
-
if (!clipboardContext) {
|
|
2156
|
-
return;
|
|
2157
|
-
}
|
|
2158
|
-
const { type,
|
|
2159
|
-
if (getProbablySupportsClipboardWrite()) {
|
|
2160
|
-
return await setNavigatorClipboard(type,
|
|
2161
|
-
}
|
|
2162
|
-
if (dataTransfer) {
|
|
2163
|
-
setDataTransferClipboard(dataTransfer, type,
|
|
2164
|
-
setDataTransferClipboardText(dataTransfer, text);
|
|
2165
|
-
return;
|
|
2166
|
-
}
|
|
2167
|
-
// Compatible with situations where navigator.clipboard.write is not supported and dataTransfer is empty
|
|
2168
|
-
// Such as contextmenu copy in Firefox.
|
|
2169
|
-
if (getProbablySupportsClipboardWriteText()) {
|
|
2170
|
-
return await navigator.clipboard.writeText(buildPlaitHtml(type,
|
|
2171
|
-
}
|
|
2172
|
-
};
|
|
2173
|
-
|
|
2174
|
-
const BOARD_TO_TOUCH_REF = new WeakMap();
|
|
2175
|
-
const isPreventTouchMove = (board) => {
|
|
2176
|
-
return !!BOARD_TO_TOUCH_REF.get(board);
|
|
2177
|
-
};
|
|
2178
|
-
const preventTouchMove = (board, event, state) => {
|
|
2179
|
-
const hostElement = PlaitBoard.getElementHost(board);
|
|
2180
|
-
const activeHostElement = PlaitBoard.getElementActiveHost(board);
|
|
2181
|
-
if (state) {
|
|
2182
|
-
if ((event.target instanceof HTMLElement || event.target instanceof SVGElement) &&
|
|
2183
|
-
(hostElement.contains(event.target) || activeHostElement.contains(event.target))) {
|
|
2184
|
-
BOARD_TO_TOUCH_REF.set(board, { state, target: event.target instanceof SVGElement ? event.target : undefined });
|
|
2185
|
-
}
|
|
2186
|
-
else {
|
|
2187
|
-
BOARD_TO_TOUCH_REF.set(board, { state, target: undefined });
|
|
2188
|
-
}
|
|
2189
|
-
}
|
|
2190
|
-
else {
|
|
2191
|
-
const ref = BOARD_TO_TOUCH_REF.get(board);
|
|
2192
|
-
if (ref) {
|
|
2193
|
-
BOARD_TO_TOUCH_REF.delete(board);
|
|
2194
|
-
ref.host?.remove();
|
|
2195
|
-
}
|
|
2196
|
-
}
|
|
2197
|
-
};
|
|
2198
|
-
/**
|
|
2199
|
-
* some intersection maybe cause target is removed from current browser window,
|
|
2200
|
-
* after it was removed touch move event will not be fired
|
|
2201
|
-
* so scroll behavior will can not be prevented in mobile browser device
|
|
2202
|
-
* this function will prevent target element being remove.
|
|
2203
|
-
*/
|
|
2204
|
-
const handleTouchTarget = (board) => {
|
|
2205
|
-
const touchRef = BOARD_TO_TOUCH_REF.get(board);
|
|
2206
|
-
if (touchRef &&
|
|
2207
|
-
touchRef.target &&
|
|
2208
|
-
!PlaitBoard.getElementHost(board).contains(touchRef.target) &&
|
|
2209
|
-
!PlaitBoard.getElementActiveHost(board).contains(touchRef.target)) {
|
|
2210
|
-
touchRef.target.style.opacity = '0';
|
|
2211
|
-
const host = createG();
|
|
2212
|
-
host.appendChild(touchRef.target);
|
|
2213
|
-
touchRef.host = host;
|
|
2214
|
-
host.classList.add('touch-target');
|
|
2215
|
-
PlaitBoard.getElementActiveHost(board).append(host);
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2218
|
-
|
|
2219
|
-
const Viewport = {
|
|
2220
|
-
isViewport: (value) => {
|
|
2221
|
-
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
2222
|
-
}
|
|
2223
|
-
};
|
|
2224
|
-
|
|
2225
|
-
const Path = {
|
|
2226
|
-
/**
|
|
2227
|
-
* Get a list of ancestor paths for a given path.
|
|
2228
|
-
*
|
|
2229
|
-
* The paths are sorted from shallowest to deepest ancestor. However, if the
|
|
2230
|
-
* `reverse: true` option is passed, they are reversed.
|
|
2231
|
-
*/
|
|
2232
|
-
ancestors(path, options = {}) {
|
|
2233
|
-
const { reverse = false } = options;
|
|
2234
|
-
let paths = Path.levels(path, options);
|
|
2235
|
-
if (reverse) {
|
|
2236
|
-
paths = paths.slice(1);
|
|
2237
|
-
}
|
|
2238
|
-
else {
|
|
2239
|
-
paths = paths.slice(0, -1);
|
|
2240
|
-
}
|
|
2241
|
-
return paths;
|
|
2242
|
-
},
|
|
2243
|
-
/**
|
|
2244
|
-
* Get a list of paths at every level down to a path. Note: this is the same
|
|
2245
|
-
* as `Path.ancestors`, but including the path itself.
|
|
2246
|
-
*
|
|
2247
|
-
* The paths are sorted from shallowest to deepest. However, if the `reverse:
|
|
2248
|
-
* true` option is passed, they are reversed.
|
|
2249
|
-
*/
|
|
2250
|
-
levels(path, options = {}) {
|
|
2251
|
-
const { reverse = false } = options;
|
|
2252
|
-
const list = [];
|
|
2253
|
-
for (let i = 0; i <= path.length; i++) {
|
|
2254
|
-
list.push(path.slice(0, i));
|
|
2255
|
-
}
|
|
2256
|
-
if (reverse) {
|
|
2257
|
-
list.reverse();
|
|
2258
|
-
}
|
|
2259
|
-
return list;
|
|
2260
|
-
},
|
|
2261
|
-
parent(path) {
|
|
2262
|
-
if (path.length === 0) {
|
|
2263
|
-
throw new Error(`Cannot get the parent path of the root path [${path}].`);
|
|
2264
|
-
}
|
|
2265
|
-
return path.slice(0, -1);
|
|
2266
|
-
},
|
|
2267
|
-
next(path) {
|
|
2268
|
-
if (path.length === 0) {
|
|
2269
|
-
throw new Error(`Cannot get the next path of a root path [${path}], because it has no next index.`);
|
|
2270
|
-
}
|
|
2271
|
-
const last = path[path.length - 1];
|
|
2272
|
-
return path.slice(0, -1).concat(last + 1);
|
|
2273
|
-
},
|
|
2274
|
-
hasPrevious(path) {
|
|
2275
|
-
return path[path.length - 1] > 0;
|
|
2276
|
-
},
|
|
2277
|
-
previous(path) {
|
|
2278
|
-
if (path.length === 0) {
|
|
2279
|
-
throw new Error(`Cannot get the next path of a root path [${path}], because it has no previous index.`);
|
|
2280
|
-
}
|
|
2281
|
-
const last = path[path.length - 1];
|
|
2282
|
-
return path.slice(0, -1).concat(last - 1);
|
|
2283
|
-
},
|
|
2284
|
-
/**
|
|
2285
|
-
* Check if a path is an ancestor of another.
|
|
2286
|
-
*/
|
|
2287
|
-
isAncestor(path, another) {
|
|
2288
|
-
return path.length < another.length && Path.compare(path, another) === 0;
|
|
2289
|
-
},
|
|
2290
|
-
/**
|
|
2291
|
-
* Compare a path to another, returning an integer indicating whether the path
|
|
2292
|
-
* was before, at, or after the other.
|
|
2293
|
-
*
|
|
2294
|
-
* Note: Two paths of unequal length can still receive a `0` result if one is
|
|
2295
|
-
* directly above or below the other. If you want exact matching, use
|
|
2296
|
-
* [[Path.equals]] instead.
|
|
2297
|
-
*/
|
|
2298
|
-
compare(path, another) {
|
|
2299
|
-
const min = Math.min(path.length, another.length);
|
|
2300
|
-
for (let i = 0; i < min; i++) {
|
|
2301
|
-
if (path[i] < another[i])
|
|
2302
|
-
return -1;
|
|
2303
|
-
if (path[i] > another[i])
|
|
2304
|
-
return 1;
|
|
2305
|
-
}
|
|
2306
|
-
return 0;
|
|
2307
|
-
},
|
|
2308
|
-
/**
|
|
2309
|
-
* Check if a path is exactly equal to another.
|
|
2310
|
-
*/
|
|
2311
|
-
equals(path, another) {
|
|
2312
|
-
return path.length === another.length && path.every((n, i) => n === another[i]);
|
|
2313
|
-
},
|
|
2314
|
-
/**
|
|
2315
|
-
* Check if a path ends before one of the indexes in another.
|
|
2316
|
-
*/
|
|
2317
|
-
endsBefore(path, another) {
|
|
2318
|
-
const i = path.length - 1;
|
|
2319
|
-
const as = path.slice(0, i);
|
|
2320
|
-
const bs = another.slice(0, i);
|
|
2321
|
-
const av = path[i];
|
|
2322
|
-
const bv = another[i];
|
|
2323
|
-
return Path.equals(as, bs) && av < bv;
|
|
2324
|
-
},
|
|
2325
|
-
/**
|
|
2326
|
-
* Check if a path is a sibling of another.
|
|
2327
|
-
*/
|
|
2328
|
-
isSibling(path, another) {
|
|
2329
|
-
if (path.length !== another.length) {
|
|
2330
|
-
return false;
|
|
2331
|
-
}
|
|
2332
|
-
const as = path.slice(0, -1);
|
|
2333
|
-
const bs = another.slice(0, -1);
|
|
2334
|
-
const al = path[path.length - 1];
|
|
2335
|
-
const bl = another[another.length - 1];
|
|
2336
|
-
return al !== bl && Path.equals(as, bs);
|
|
2337
|
-
},
|
|
2338
|
-
transform(path, operation) {
|
|
2339
|
-
return produce(path, p => {
|
|
2340
|
-
// PERF: Exit early if the operation is guaranteed not to have an effect.
|
|
2341
|
-
if (!path || path?.length === 0) {
|
|
2342
|
-
return;
|
|
2343
|
-
}
|
|
2344
|
-
if (p === null) {
|
|
2345
|
-
return null;
|
|
2346
|
-
}
|
|
2347
|
-
switch (operation.type) {
|
|
2348
|
-
case 'insert_node': {
|
|
2349
|
-
const { path: op } = operation;
|
|
2350
|
-
if (Path.equals(op, p) || Path.endsBefore(op, p) || Path.isAncestor(op, p)) {
|
|
2351
|
-
p[op.length - 1] += 1;
|
|
2352
|
-
}
|
|
2353
|
-
break;
|
|
2354
|
-
}
|
|
2355
|
-
case 'remove_node': {
|
|
2356
|
-
const { path: op } = operation;
|
|
2357
|
-
if (Path.equals(op, p) || Path.isAncestor(op, p)) {
|
|
2358
|
-
return null;
|
|
2359
|
-
}
|
|
2360
|
-
else if (Path.endsBefore(op, p)) {
|
|
2361
|
-
p[op.length - 1] -= 1;
|
|
2362
|
-
}
|
|
2363
|
-
break;
|
|
2364
|
-
}
|
|
2365
|
-
case 'move_node': {
|
|
2366
|
-
const { path: op, newPath: onp } = operation;
|
|
2367
|
-
// If the old and new path are the same, it's a no-op.
|
|
2368
|
-
if (Path.equals(op, onp)) {
|
|
2369
|
-
return;
|
|
2370
|
-
}
|
|
2371
|
-
if (Path.isAncestor(op, p) || Path.equals(op, p)) {
|
|
2372
|
-
const copy = onp.slice();
|
|
2373
|
-
// op.length <= onp.length is different for slate
|
|
2374
|
-
// resolve drag from [0, 0] to [0, 3] issue
|
|
2375
|
-
if (Path.endsBefore(op, onp) && op.length <= onp.length) {
|
|
2376
|
-
copy[op.length - 1] -= 1;
|
|
2377
|
-
}
|
|
2378
|
-
return copy.concat(p.slice(op.length));
|
|
2379
|
-
}
|
|
2380
|
-
else if (Path.isSibling(op, onp) && (Path.isAncestor(onp, p) || Path.equals(onp, p))) {
|
|
2381
|
-
if (Path.endsBefore(op, p)) {
|
|
2382
|
-
p[op.length - 1] -= 1;
|
|
2383
|
-
}
|
|
2384
|
-
else {
|
|
2385
|
-
p[op.length - 1] += 1;
|
|
2386
|
-
}
|
|
2387
|
-
}
|
|
2388
|
-
else if (Path.endsBefore(onp, p) || Path.equals(onp, p) || Path.isAncestor(onp, p)) {
|
|
2389
|
-
if (Path.endsBefore(op, p)) {
|
|
2390
|
-
p[op.length - 1] -= 1;
|
|
2391
|
-
}
|
|
2392
|
-
p[onp.length - 1] += 1;
|
|
2393
|
-
}
|
|
2394
|
-
else if (Path.endsBefore(op, p)) {
|
|
2395
|
-
if (Path.equals(onp, p)) {
|
|
2396
|
-
p[onp.length - 1] += 1;
|
|
2397
|
-
}
|
|
2398
|
-
p[op.length - 1] -= 1;
|
|
2399
|
-
}
|
|
2400
|
-
break;
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
return p;
|
|
2404
|
-
});
|
|
2785
|
+
return { files: Array.from(dataTransfer.files) };
|
|
2786
|
+
}
|
|
2787
|
+
clipboardData = getDataTransferClipboard(dataTransfer);
|
|
2788
|
+
if (Object.keys(clipboardData).length === 0) {
|
|
2789
|
+
clipboardData = getDataTransferClipboardText(dataTransfer);
|
|
2790
|
+
}
|
|
2791
|
+
return clipboardData;
|
|
2792
|
+
}
|
|
2793
|
+
if (getProbablySupportsClipboardRead()) {
|
|
2794
|
+
return await getNavigatorClipboard();
|
|
2795
|
+
}
|
|
2796
|
+
return clipboardData;
|
|
2797
|
+
};
|
|
2798
|
+
const setClipboardData = async (dataTransfer, clipboardContext) => {
|
|
2799
|
+
if (!clipboardContext) {
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
const { type, elements, text } = clipboardContext;
|
|
2803
|
+
if (getProbablySupportsClipboardWrite()) {
|
|
2804
|
+
return await setNavigatorClipboard(type, elements, text);
|
|
2805
|
+
}
|
|
2806
|
+
if (dataTransfer) {
|
|
2807
|
+
setDataTransferClipboard(dataTransfer, type, elements);
|
|
2808
|
+
setDataTransferClipboardText(dataTransfer, text);
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
// Compatible with situations where navigator.clipboard.write is not supported and dataTransfer is empty
|
|
2812
|
+
// Such as contextmenu copy in Firefox.
|
|
2813
|
+
if (getProbablySupportsClipboardWriteText()) {
|
|
2814
|
+
return await navigator.clipboard.writeText(buildPlaitHtml(type, elements));
|
|
2405
2815
|
}
|
|
2406
2816
|
};
|
|
2407
2817
|
|
|
2408
|
-
const
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
*/
|
|
2420
|
-
*parents(root, path, options = {}) {
|
|
2421
|
-
for (const p of Path.ancestors(path, options)) {
|
|
2422
|
-
const n = PlaitNode.get(root, p);
|
|
2423
|
-
yield n;
|
|
2818
|
+
const BOARD_TO_TOUCH_REF = new WeakMap();
|
|
2819
|
+
const isPreventTouchMove = (board) => {
|
|
2820
|
+
return !!BOARD_TO_TOUCH_REF.get(board);
|
|
2821
|
+
};
|
|
2822
|
+
const preventTouchMove = (board, event, state) => {
|
|
2823
|
+
const hostElement = PlaitBoard.getElementHost(board);
|
|
2824
|
+
const activeHostElement = PlaitBoard.getElementActiveHost(board);
|
|
2825
|
+
if (state) {
|
|
2826
|
+
if ((event.target instanceof HTMLElement || event.target instanceof SVGElement) &&
|
|
2827
|
+
(hostElement.contains(event.target) || activeHostElement.contains(event.target))) {
|
|
2828
|
+
BOARD_TO_TOUCH_REF.set(board, { state, target: event.target instanceof SVGElement ? event.target : undefined });
|
|
2424
2829
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
let node = root;
|
|
2428
|
-
for (let i = 0; i < path.length; i++) {
|
|
2429
|
-
const p = path[i];
|
|
2430
|
-
if (!node || !node.children || !node.children[p]) {
|
|
2431
|
-
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
2432
|
-
}
|
|
2433
|
-
node = node.children[p];
|
|
2830
|
+
else {
|
|
2831
|
+
BOARD_TO_TOUCH_REF.set(board, { state, target: undefined });
|
|
2434
2832
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
n = n.children[i];
|
|
2833
|
+
}
|
|
2834
|
+
else {
|
|
2835
|
+
const ref = BOARD_TO_TOUCH_REF.get(board);
|
|
2836
|
+
if (ref) {
|
|
2837
|
+
BOARD_TO_TOUCH_REF.delete(board);
|
|
2838
|
+
ref.host?.remove();
|
|
2442
2839
|
}
|
|
2443
|
-
|
|
2840
|
+
}
|
|
2841
|
+
};
|
|
2842
|
+
/**
|
|
2843
|
+
* some intersection maybe cause target is removed from current browser window,
|
|
2844
|
+
* after it was removed touch move event will not be fired
|
|
2845
|
+
* so scroll behavior will can not be prevented in mobile browser device
|
|
2846
|
+
* this function will prevent target element being remove.
|
|
2847
|
+
*/
|
|
2848
|
+
const handleTouchTarget = (board) => {
|
|
2849
|
+
const touchRef = BOARD_TO_TOUCH_REF.get(board);
|
|
2850
|
+
if (touchRef &&
|
|
2851
|
+
touchRef.target &&
|
|
2852
|
+
!PlaitBoard.getElementHost(board).contains(touchRef.target) &&
|
|
2853
|
+
!PlaitBoard.getElementActiveHost(board).contains(touchRef.target)) {
|
|
2854
|
+
touchRef.target.style.opacity = '0';
|
|
2855
|
+
const host = createG();
|
|
2856
|
+
host.appendChild(touchRef.target);
|
|
2857
|
+
touchRef.host = host;
|
|
2858
|
+
host.classList.add('touch-target');
|
|
2859
|
+
PlaitBoard.getElementActiveHost(board).append(host);
|
|
2860
|
+
}
|
|
2861
|
+
};
|
|
2862
|
+
|
|
2863
|
+
const Viewport = {
|
|
2864
|
+
isViewport: (value) => {
|
|
2865
|
+
return !isNullOrUndefined(value.zoom) && !isNullOrUndefined(value.viewBackgroundColor);
|
|
2444
2866
|
}
|
|
2445
2867
|
};
|
|
2446
2868
|
|
|
@@ -2586,40 +3008,65 @@ const GeneralTransforms = {
|
|
|
2586
3008
|
}
|
|
2587
3009
|
};
|
|
2588
3010
|
|
|
2589
|
-
|
|
2590
|
-
const
|
|
2591
|
-
board
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
3011
|
+
const addGroup = (board, elements) => {
|
|
3012
|
+
const selectedGroups = getHighestSelectedGroups(board, elements);
|
|
3013
|
+
const selectedIsolatedElements = getSelectedIsolatedElementsCanAddToGroup(board);
|
|
3014
|
+
const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
|
|
3015
|
+
const group = createGroup();
|
|
3016
|
+
if (canAddGroup(board)) {
|
|
3017
|
+
highestSelectedElements.forEach(item => {
|
|
3018
|
+
const path = PlaitBoard.findPath(board, item);
|
|
3019
|
+
NodeTransforms.setNode(board, { groupId: group.id }, path);
|
|
3020
|
+
});
|
|
3021
|
+
const selectedElements = getSelectedElements(board);
|
|
3022
|
+
const highestIndexOfSelectedElement = getHighestIndexOfElement(board, selectedElements);
|
|
3023
|
+
const indices = getElementsIndices(board, highestSelectedElements);
|
|
3024
|
+
const isContinuous = isIndicesContinuous(indices);
|
|
3025
|
+
if (!isContinuous) {
|
|
3026
|
+
moveElementsToNewPathAfterAddGroup(board, selectedElements, [highestIndexOfSelectedElement - 1]);
|
|
3027
|
+
}
|
|
3028
|
+
if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
|
|
3029
|
+
const newGroupId = selectedIsolatedElements[0].groupId;
|
|
3030
|
+
NodeTransforms.insertNode(board, {
|
|
3031
|
+
...group,
|
|
3032
|
+
groupId: newGroupId
|
|
3033
|
+
}, [board.children.length]);
|
|
3034
|
+
}
|
|
3035
|
+
else {
|
|
3036
|
+
NodeTransforms.insertNode(board, group, [board.children.length]);
|
|
2604
3037
|
}
|
|
2605
3038
|
}
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
3039
|
+
};
|
|
3040
|
+
const removeGroup = (board, elements) => {
|
|
3041
|
+
const selectedGroups = getHighestSelectedGroups(board, elements);
|
|
3042
|
+
if (canRemoveGroup(board)) {
|
|
3043
|
+
selectedGroups.forEach(group => {
|
|
3044
|
+
const elementsInGroup = findElements(board, {
|
|
3045
|
+
match: item => item.groupId === group.id,
|
|
3046
|
+
recursion: () => false
|
|
3047
|
+
});
|
|
3048
|
+
elementsInGroup.forEach(element => {
|
|
3049
|
+
const path = PlaitBoard.findPath(board, element);
|
|
3050
|
+
NodeTransforms.setNode(board, { groupId: group.groupId || undefined }, path);
|
|
3051
|
+
});
|
|
3052
|
+
});
|
|
3053
|
+
selectedGroups
|
|
3054
|
+
.map(group => {
|
|
3055
|
+
const groupPath = PlaitBoard.findPath(board, group);
|
|
3056
|
+
const groupRef = board.pathRef(groupPath);
|
|
3057
|
+
return () => {
|
|
3058
|
+
groupRef.current && NodeTransforms.removeNode(board, groupRef.current);
|
|
3059
|
+
groupRef.unref();
|
|
3060
|
+
};
|
|
3061
|
+
})
|
|
3062
|
+
.forEach(action => {
|
|
3063
|
+
action();
|
|
3064
|
+
});
|
|
3065
|
+
}
|
|
3066
|
+
};
|
|
3067
|
+
const GroupTransforms = {
|
|
3068
|
+
addGroup,
|
|
3069
|
+
removeGroup
|
|
2623
3070
|
};
|
|
2624
3071
|
|
|
2625
3072
|
function setSelection(board, selection) {
|
|
@@ -2646,6 +3093,184 @@ function addSelectionWithTemporaryElements(board, elements) {
|
|
|
2646
3093
|
}
|
|
2647
3094
|
}
|
|
2648
3095
|
|
|
3096
|
+
const getOneMoveOptions = (board, direction) => {
|
|
3097
|
+
const indicesToMove = getElementsIndices(board, getSelectedElements(board));
|
|
3098
|
+
let groupedIndices = toContiguousGroups(board, indicesToMove);
|
|
3099
|
+
if (direction === 'up') {
|
|
3100
|
+
groupedIndices = groupedIndices.reverse();
|
|
3101
|
+
}
|
|
3102
|
+
let moveContents = [];
|
|
3103
|
+
groupedIndices.forEach((indices, i) => {
|
|
3104
|
+
const leadingIndex = indices[0];
|
|
3105
|
+
const trailingIndex = indices[indices.length - 1];
|
|
3106
|
+
const boundaryIndex = direction === 'down' ? leadingIndex : trailingIndex;
|
|
3107
|
+
const targetIndex = getTargetIndex(board, boundaryIndex, direction);
|
|
3108
|
+
if (targetIndex === -1 || boundaryIndex === targetIndex) {
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
if (direction === 'down') {
|
|
3112
|
+
indices = indices.reverse();
|
|
3113
|
+
}
|
|
3114
|
+
moveContents.push(...indices.map(path => {
|
|
3115
|
+
return {
|
|
3116
|
+
element: board.children[path],
|
|
3117
|
+
newPath: [targetIndex]
|
|
3118
|
+
};
|
|
3119
|
+
}));
|
|
3120
|
+
});
|
|
3121
|
+
return moveContents;
|
|
3122
|
+
};
|
|
3123
|
+
const getAllMoveOptions = (board, direction) => {
|
|
3124
|
+
const indicesToMove = getElementsIndices(board, getSelectedElements(board));
|
|
3125
|
+
let groupedIndices = toContiguousGroups(board, indicesToMove);
|
|
3126
|
+
let moveContents = [];
|
|
3127
|
+
if (direction === 'down') {
|
|
3128
|
+
groupedIndices = groupedIndices.reverse();
|
|
3129
|
+
}
|
|
3130
|
+
groupedIndices.forEach(indices => {
|
|
3131
|
+
const leadingIndex = indices[0];
|
|
3132
|
+
const trailingIndex = indices[indices.length - 1];
|
|
3133
|
+
const boundaryIndex = direction === 'down' ? leadingIndex : trailingIndex;
|
|
3134
|
+
const sourceElement = board.children[boundaryIndex];
|
|
3135
|
+
const editingGroup = getEditingGroup(board, sourceElement);
|
|
3136
|
+
let targetIndex = direction === 'down' ? 0 : board.children.length - 1;
|
|
3137
|
+
if (editingGroup) {
|
|
3138
|
+
const elementsInGroup = sortElements(board, getElementsInGroup(board, editingGroup, true, true));
|
|
3139
|
+
targetIndex =
|
|
3140
|
+
direction === 'down'
|
|
3141
|
+
? board.children.indexOf(elementsInGroup[0])
|
|
3142
|
+
: board.children.indexOf(elementsInGroup[elementsInGroup.length - 1]);
|
|
3143
|
+
}
|
|
3144
|
+
if (direction === 'down') {
|
|
3145
|
+
indices = indices.reverse();
|
|
3146
|
+
}
|
|
3147
|
+
moveContents.push(...indices.map(path => {
|
|
3148
|
+
return {
|
|
3149
|
+
element: board.children[path],
|
|
3150
|
+
newPath: [targetIndex]
|
|
3151
|
+
};
|
|
3152
|
+
}));
|
|
3153
|
+
});
|
|
3154
|
+
return moveContents;
|
|
3155
|
+
};
|
|
3156
|
+
const canSetZIndex = (board) => {
|
|
3157
|
+
const selectedElements = getSelectedElements(board).filter(item => board.canSetZIndex(item));
|
|
3158
|
+
return selectedElements.length > 0;
|
|
3159
|
+
};
|
|
3160
|
+
const toContiguousGroups = (board, array) => {
|
|
3161
|
+
let cursor = 0;
|
|
3162
|
+
return array.reduce((acc, value, index) => {
|
|
3163
|
+
if (index > 0) {
|
|
3164
|
+
const currentElement = board.children[value];
|
|
3165
|
+
const previousElement = board.children[array[index - 1]];
|
|
3166
|
+
const isContiguous = value - 1 === array[index - 1]
|
|
3167
|
+
? true
|
|
3168
|
+
: board.children.every((item, childIndex) => {
|
|
3169
|
+
if (childIndex > array[index - 1] && childIndex <= value - 1) {
|
|
3170
|
+
return PlaitGroupElement.isGroup(item);
|
|
3171
|
+
}
|
|
3172
|
+
return true;
|
|
3173
|
+
});
|
|
3174
|
+
let isPartialSelectGroupElement = false;
|
|
3175
|
+
if (previousElement.groupId || (currentElement.groupId && previousElement.groupId !== currentElement.groupId)) {
|
|
3176
|
+
let isPartialSelectPreviousGroup = false;
|
|
3177
|
+
let isPartialSelectCurrentElement = false;
|
|
3178
|
+
if (previousElement.groupId) {
|
|
3179
|
+
const highestGroup = getHighestGroup(board, previousElement);
|
|
3180
|
+
isPartialSelectPreviousGroup = !isSelectedAllElementsInGroup(board, highestGroup);
|
|
3181
|
+
}
|
|
3182
|
+
if (currentElement.groupId) {
|
|
3183
|
+
const highestGroup = getHighestGroup(board, currentElement);
|
|
3184
|
+
isPartialSelectCurrentElement = !isSelectedAllElementsInGroup(board, highestGroup);
|
|
3185
|
+
}
|
|
3186
|
+
isPartialSelectGroupElement = isPartialSelectPreviousGroup || isPartialSelectCurrentElement;
|
|
3187
|
+
}
|
|
3188
|
+
if (!isContiguous || isPartialSelectGroupElement) {
|
|
3189
|
+
cursor = ++cursor;
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
(acc[cursor] || (acc[cursor] = [])).push(value);
|
|
3193
|
+
return acc;
|
|
3194
|
+
}, []);
|
|
3195
|
+
};
|
|
3196
|
+
/**
|
|
3197
|
+
* Returns next candidate index that's available to be moved to. Currently that
|
|
3198
|
+
* is a non-deleted element, and not inside a group (unless we're editing it).
|
|
3199
|
+
*/
|
|
3200
|
+
const getTargetIndex = (board, boundaryIndex, direction) => {
|
|
3201
|
+
if ((boundaryIndex === 0 && direction === 'down') || (boundaryIndex === board.children.length - 1 && direction === 'up')) {
|
|
3202
|
+
return -1;
|
|
3203
|
+
}
|
|
3204
|
+
const indexFilter = (element) => {
|
|
3205
|
+
if (element.isDeleted || PlaitGroupElement.isGroup(element)) {
|
|
3206
|
+
return false;
|
|
3207
|
+
}
|
|
3208
|
+
return true;
|
|
3209
|
+
};
|
|
3210
|
+
const candidateIndex = direction === 'down'
|
|
3211
|
+
? findLastIndex(board.children, el => indexFilter(el), Math.max(0, boundaryIndex - 1))
|
|
3212
|
+
: findIndex(board.children, el => indexFilter(el), boundaryIndex + 1);
|
|
3213
|
+
const nextElement = board.children[candidateIndex];
|
|
3214
|
+
if (!nextElement) {
|
|
3215
|
+
return -1;
|
|
3216
|
+
}
|
|
3217
|
+
const elements = [...board.children];
|
|
3218
|
+
const sourceElement = elements[boundaryIndex];
|
|
3219
|
+
const editingGroup = getEditingGroup(board, sourceElement);
|
|
3220
|
+
const nextElementGroups = (getGroupByElement(board, nextElement, true) || []);
|
|
3221
|
+
// candidate element is a sibling in current editing group → return
|
|
3222
|
+
if (editingGroup && sourceElement?.groupId !== nextElement?.groupId) {
|
|
3223
|
+
// candidate element is outside current editing group → prevent
|
|
3224
|
+
if (!nextElementGroups.find(item => item.id === editingGroup.id)) {
|
|
3225
|
+
return -1;
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
if (!nextElement.groupId) {
|
|
3229
|
+
return candidateIndex;
|
|
3230
|
+
}
|
|
3231
|
+
let siblingGroup;
|
|
3232
|
+
if (editingGroup) {
|
|
3233
|
+
siblingGroup = nextElementGroups[nextElementGroups.indexOf(editingGroup) - 1];
|
|
3234
|
+
}
|
|
3235
|
+
else {
|
|
3236
|
+
siblingGroup = nextElementGroups[nextElementGroups.length - 1];
|
|
3237
|
+
}
|
|
3238
|
+
if (siblingGroup) {
|
|
3239
|
+
let elementsInSiblingGroup = getElementsInGroup(board, siblingGroup, true, false);
|
|
3240
|
+
if (elementsInSiblingGroup.length) {
|
|
3241
|
+
elementsInSiblingGroup.sort((a, b) => {
|
|
3242
|
+
const indexA = board.children.findIndex(child => child.id === a.id);
|
|
3243
|
+
const indexB = board.children.findIndex(child => child.id === b.id);
|
|
3244
|
+
return indexA - indexB;
|
|
3245
|
+
});
|
|
3246
|
+
// assumes getElementsInGroup() returned elements are sorted
|
|
3247
|
+
// by zIndex (ascending)
|
|
3248
|
+
return direction === 'down'
|
|
3249
|
+
? elements.indexOf(elementsInSiblingGroup[0])
|
|
3250
|
+
: elements.indexOf(elementsInSiblingGroup[elementsInSiblingGroup.length - 1]);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
return candidateIndex;
|
|
3254
|
+
};
|
|
3255
|
+
|
|
3256
|
+
const moveToTop = (board) => {
|
|
3257
|
+
const moveOptions = getAllMoveOptions(board, 'up');
|
|
3258
|
+
moveElementsToNewPath(board, moveOptions);
|
|
3259
|
+
};
|
|
3260
|
+
const moveToBottom = (board) => {
|
|
3261
|
+
const moveOptions = getAllMoveOptions(board, 'down');
|
|
3262
|
+
moveElementsToNewPath(board, moveOptions);
|
|
3263
|
+
};
|
|
3264
|
+
const moveUp = (board) => {
|
|
3265
|
+
const moveOptions = getOneMoveOptions(board, 'up');
|
|
3266
|
+
moveElementsToNewPath(board, moveOptions);
|
|
3267
|
+
};
|
|
3268
|
+
const moveDown = (board) => {
|
|
3269
|
+
const moveOptions = getOneMoveOptions(board, 'down');
|
|
3270
|
+
moveElementsToNewPath(board, moveOptions);
|
|
3271
|
+
};
|
|
3272
|
+
const ZIndexTransforms = { moveUp, moveDown, moveToTop, moveToBottom };
|
|
3273
|
+
|
|
2649
3274
|
const removeElements = (board, elements) => {
|
|
2650
3275
|
elements
|
|
2651
3276
|
.map(element => {
|
|
@@ -2665,55 +3290,13 @@ const CoreTransforms = {
|
|
|
2665
3290
|
removeElements
|
|
2666
3291
|
};
|
|
2667
3292
|
|
|
2668
|
-
const addGroup = (board, elements) => {
|
|
2669
|
-
const selectedGroups = getHighestSelectedGroups(board, elements);
|
|
2670
|
-
const selectedIsolatedElements = getSelectedIsolatedElementsCanAddToGroup(board);
|
|
2671
|
-
const highestSelectedElements = [...selectedGroups, ...selectedIsolatedElements];
|
|
2672
|
-
const group = createGroup();
|
|
2673
|
-
if (canAddGroup(board)) {
|
|
2674
|
-
highestSelectedElements.forEach(item => {
|
|
2675
|
-
const path = PlaitBoard.findPath(board, item);
|
|
2676
|
-
NodeTransforms.setNode(board, { groupId: group.id }, path);
|
|
2677
|
-
});
|
|
2678
|
-
if (hasSelectedElementsInSameGroup(highestSelectedElements)) {
|
|
2679
|
-
const newGroupId = selectedIsolatedElements[0].groupId;
|
|
2680
|
-
NodeTransforms.insertNode(board, {
|
|
2681
|
-
...group,
|
|
2682
|
-
groupId: newGroupId
|
|
2683
|
-
}, [board.children.length]);
|
|
2684
|
-
}
|
|
2685
|
-
else {
|
|
2686
|
-
NodeTransforms.insertNode(board, group, [board.children.length]);
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
};
|
|
2690
|
-
const removeGroup = (board, elements) => {
|
|
2691
|
-
const selectedGroups = getHighestSelectedGroups(board, elements);
|
|
2692
|
-
if (canRemoveGroup(board)) {
|
|
2693
|
-
selectedGroups.map(group => {
|
|
2694
|
-
const elementsInGroup = findElements(board, {
|
|
2695
|
-
match: item => item.groupId === group.id,
|
|
2696
|
-
recursion: () => false
|
|
2697
|
-
});
|
|
2698
|
-
elementsInGroup.forEach(item => {
|
|
2699
|
-
const path = PlaitBoard.findPath(board, item);
|
|
2700
|
-
NodeTransforms.setNode(board, { groupId: group.groupId || undefined }, path);
|
|
2701
|
-
});
|
|
2702
|
-
const groupPath = PlaitBoard.findPath(board, group);
|
|
2703
|
-
NodeTransforms.removeNode(board, groupPath);
|
|
2704
|
-
});
|
|
2705
|
-
}
|
|
2706
|
-
};
|
|
2707
|
-
const GroupTransforms = {
|
|
2708
|
-
addGroup,
|
|
2709
|
-
removeGroup
|
|
2710
|
-
};
|
|
2711
|
-
|
|
2712
3293
|
const Transforms = {
|
|
2713
3294
|
...GeneralTransforms,
|
|
2714
3295
|
...ViewportTransforms$1,
|
|
2715
3296
|
...SelectionTransforms,
|
|
2716
|
-
...NodeTransforms
|
|
3297
|
+
...NodeTransforms,
|
|
3298
|
+
...GroupTransforms,
|
|
3299
|
+
...ZIndexTransforms
|
|
2717
3300
|
};
|
|
2718
3301
|
|
|
2719
3302
|
const rotatePoints = (points, centerPoint, angle) => {
|
|
@@ -2820,11 +3403,25 @@ function rotateElements(board, elements, angle) {
|
|
|
2820
3403
|
const selectionCenterPoint = RectangleClient.getCenterPoint(selectionRectangle);
|
|
2821
3404
|
elements.forEach(item => {
|
|
2822
3405
|
const originAngle = item.angle;
|
|
2823
|
-
const points = rotatedDataPoints(item.points, selectionCenterPoint, angle);
|
|
3406
|
+
const points = rotatedDataPoints(item.points, selectionCenterPoint, normalizeAngle(angle));
|
|
2824
3407
|
const path = PlaitBoard.findPath(board, item);
|
|
2825
|
-
Transforms.setNode(board, { points, angle: originAngle + angle }, path);
|
|
3408
|
+
Transforms.setNode(board, { points, angle: normalizeAngle(originAngle + angle) }, path);
|
|
2826
3409
|
});
|
|
2827
3410
|
}
|
|
3411
|
+
const normalizeAngle = (angle) => {
|
|
3412
|
+
if (angle < 0) {
|
|
3413
|
+
return angle + 2 * Math.PI;
|
|
3414
|
+
}
|
|
3415
|
+
if (angle >= 2 * Math.PI) {
|
|
3416
|
+
return angle - 2 * Math.PI;
|
|
3417
|
+
}
|
|
3418
|
+
return angle;
|
|
3419
|
+
};
|
|
3420
|
+
const getAngleBetweenPoints = (startPoint, endPoint, centerPoint) => {
|
|
3421
|
+
const startAngle = (5 * Math.PI) / 2 + Math.atan2(startPoint[1] - centerPoint[1], startPoint[0] - centerPoint[0]);
|
|
3422
|
+
const endAngle = (5 * Math.PI) / 2 + Math.atan2(endPoint[1] - centerPoint[1], endPoint[0] - centerPoint[0]);
|
|
3423
|
+
return normalizeAngle(endAngle - startAngle);
|
|
3424
|
+
};
|
|
2828
3425
|
|
|
2829
3426
|
function isSelectionMoving(board) {
|
|
2830
3427
|
return !!BOARD_TO_IS_SELECTION_MOVING.get(board);
|
|
@@ -3156,11 +3753,255 @@ const canRemoveGroup = (board, elements) => {
|
|
|
3156
3753
|
const selectedElements = elements || getSelectedElements(board);
|
|
3157
3754
|
return selectedElements.length > 0 && selectedGroups.length > 0;
|
|
3158
3755
|
};
|
|
3756
|
+
const getEditingGroup = (board, element) => {
|
|
3757
|
+
const groups = getGroupByElement(board, element, true);
|
|
3758
|
+
let editingGroup = null;
|
|
3759
|
+
if (groups?.length) {
|
|
3760
|
+
for (let i = 0; i < groups?.length; i++) {
|
|
3761
|
+
if (!isSelectedAllElementsInGroup(board, groups[i])) {
|
|
3762
|
+
editingGroup = groups[i];
|
|
3763
|
+
break;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
return editingGroup;
|
|
3768
|
+
};
|
|
3769
|
+
const moveElementsToNewPathAfterAddGroup = (board, selectedElements, newPath) => {
|
|
3770
|
+
const moveElements = [...selectedElements];
|
|
3771
|
+
sortElements(board, moveElements);
|
|
3772
|
+
moveElements.pop();
|
|
3773
|
+
moveElementsToNewPath(board, moveElements.map(element => {
|
|
3774
|
+
return {
|
|
3775
|
+
element,
|
|
3776
|
+
newPath
|
|
3777
|
+
};
|
|
3778
|
+
}));
|
|
3779
|
+
};
|
|
3159
3780
|
|
|
3160
3781
|
const deleteFragment = (board) => {
|
|
3161
3782
|
const elements = board.getDeletedFragment([]);
|
|
3162
3783
|
board.deleteFragment(elements);
|
|
3163
3784
|
};
|
|
3785
|
+
const setFragment = (board, type, clipboardData) => {
|
|
3786
|
+
const selectedElements = getSelectedElements(board);
|
|
3787
|
+
const rectangle = getRectangleByElements(board, selectedElements, false);
|
|
3788
|
+
const clipboardContext = board.buildFragment(null, rectangle, type);
|
|
3789
|
+
clipboardContext && setClipboardData(clipboardData, clipboardContext);
|
|
3790
|
+
};
|
|
3791
|
+
const duplicateElements = (board, elements) => {
|
|
3792
|
+
const selectedElements = elements || getSelectedElements(board);
|
|
3793
|
+
const rectangle = getRectangleByElements(board, selectedElements, false);
|
|
3794
|
+
const clipboardContext = board.buildFragment(null, rectangle, 'copy');
|
|
3795
|
+
clipboardContext &&
|
|
3796
|
+
board.insertFragment({
|
|
3797
|
+
...clipboardContext,
|
|
3798
|
+
text: undefined
|
|
3799
|
+
}, [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2]);
|
|
3800
|
+
};
|
|
3801
|
+
|
|
3802
|
+
const SNAP_TOLERANCE = 2;
|
|
3803
|
+
const SNAP_SPACING = 24;
|
|
3804
|
+
function getSnapRectangles(board, activeElements) {
|
|
3805
|
+
const elements = findElements(board, {
|
|
3806
|
+
match: element => board.isAlign(element) && !activeElements.some(item => item.id === element.id),
|
|
3807
|
+
recursion: () => true,
|
|
3808
|
+
isReverse: false
|
|
3809
|
+
});
|
|
3810
|
+
return elements.map(item => getRectangleByAngle(board.getRectangle(item), item.angle) || board.getRectangle(item));
|
|
3811
|
+
}
|
|
3812
|
+
function getBarPoint(point, isHorizontal) {
|
|
3813
|
+
return isHorizontal
|
|
3814
|
+
? [
|
|
3815
|
+
[point[0], point[1] - 4],
|
|
3816
|
+
[point[0], point[1] + 4]
|
|
3817
|
+
]
|
|
3818
|
+
: [
|
|
3819
|
+
[point[0] - 4, point[1]],
|
|
3820
|
+
[point[0] + 4, point[1]]
|
|
3821
|
+
];
|
|
3822
|
+
}
|
|
3823
|
+
function getMinPointDelta(pointRectangles, axis, isHorizontal) {
|
|
3824
|
+
let delta = SNAP_TOLERANCE;
|
|
3825
|
+
pointRectangles.forEach(item => {
|
|
3826
|
+
const distance = getNearestDelta(axis, item, isHorizontal);
|
|
3827
|
+
if (Math.abs(distance) < Math.abs(delta)) {
|
|
3828
|
+
delta = distance;
|
|
3829
|
+
}
|
|
3830
|
+
});
|
|
3831
|
+
return delta;
|
|
3832
|
+
}
|
|
3833
|
+
const getNearestDelta = (axis, rectangle, isHorizontal) => {
|
|
3834
|
+
const pointAxis = getTripleAxis(rectangle, isHorizontal);
|
|
3835
|
+
const deltas = pointAxis.map(item => item - axis);
|
|
3836
|
+
const absDeltas = deltas.map(item => Math.abs(item));
|
|
3837
|
+
const index = absDeltas.indexOf(Math.min(...absDeltas));
|
|
3838
|
+
return deltas[index];
|
|
3839
|
+
};
|
|
3840
|
+
const getTripleAxis = (rectangle, isHorizontal) => {
|
|
3841
|
+
const axis = isHorizontal ? 'x' : 'y';
|
|
3842
|
+
const side = isHorizontal ? 'width' : 'height';
|
|
3843
|
+
return [rectangle[axis], rectangle[axis] + rectangle[side] / 2, rectangle[axis] + rectangle[side]];
|
|
3844
|
+
};
|
|
3845
|
+
function getNearestPointRectangle(snapRectangles, activeRectangle) {
|
|
3846
|
+
let minDistance = Infinity;
|
|
3847
|
+
let nearestRectangle = snapRectangles[0];
|
|
3848
|
+
snapRectangles.forEach(item => {
|
|
3849
|
+
const distance = Math.sqrt(Math.pow(activeRectangle.x - item.x, 2) + Math.pow(activeRectangle.y - item.y, 2));
|
|
3850
|
+
if (distance < minDistance) {
|
|
3851
|
+
minDistance = distance;
|
|
3852
|
+
nearestRectangle = item;
|
|
3853
|
+
}
|
|
3854
|
+
});
|
|
3855
|
+
return nearestRectangle;
|
|
3856
|
+
}
|
|
3857
|
+
const isSnapPoint = (axis, rectangle, isHorizontal) => {
|
|
3858
|
+
const pointAxis = getTripleAxis(rectangle, isHorizontal);
|
|
3859
|
+
return pointAxis.includes(axis);
|
|
3860
|
+
};
|
|
3861
|
+
function drawPointSnapLines(board, activeRectangle, snapRectangles, drawHorizontal = true, drawVertical = true, snapMiddle = false) {
|
|
3862
|
+
let pointLinePoints = [];
|
|
3863
|
+
const pointAxisX = getTripleAxis(activeRectangle, true);
|
|
3864
|
+
const pointAxisY = getTripleAxis(activeRectangle, false);
|
|
3865
|
+
const pointLineRefs = [
|
|
3866
|
+
{
|
|
3867
|
+
axis: pointAxisX[0],
|
|
3868
|
+
isHorizontal: true,
|
|
3869
|
+
pointRectangles: []
|
|
3870
|
+
},
|
|
3871
|
+
{
|
|
3872
|
+
axis: pointAxisX[1],
|
|
3873
|
+
isHorizontal: true,
|
|
3874
|
+
pointRectangles: []
|
|
3875
|
+
},
|
|
3876
|
+
{
|
|
3877
|
+
axis: pointAxisX[2],
|
|
3878
|
+
isHorizontal: true,
|
|
3879
|
+
pointRectangles: []
|
|
3880
|
+
},
|
|
3881
|
+
{
|
|
3882
|
+
axis: pointAxisY[0],
|
|
3883
|
+
isHorizontal: false,
|
|
3884
|
+
pointRectangles: []
|
|
3885
|
+
},
|
|
3886
|
+
{
|
|
3887
|
+
axis: pointAxisY[1],
|
|
3888
|
+
isHorizontal: false,
|
|
3889
|
+
pointRectangles: []
|
|
3890
|
+
},
|
|
3891
|
+
{
|
|
3892
|
+
axis: pointAxisY[2],
|
|
3893
|
+
isHorizontal: false,
|
|
3894
|
+
pointRectangles: []
|
|
3895
|
+
}
|
|
3896
|
+
];
|
|
3897
|
+
for (let index = 0; index < snapRectangles.length; index++) {
|
|
3898
|
+
const element = snapRectangles[index];
|
|
3899
|
+
if (isSnapPoint(pointLineRefs[0].axis, element, pointLineRefs[0].isHorizontal)) {
|
|
3900
|
+
pointLineRefs[0].pointRectangles.push(element);
|
|
3901
|
+
}
|
|
3902
|
+
if (isSnapPoint(pointLineRefs[1].axis, element, pointLineRefs[1].isHorizontal)) {
|
|
3903
|
+
pointLineRefs[1].pointRectangles.push(element);
|
|
3904
|
+
}
|
|
3905
|
+
if (isSnapPoint(pointLineRefs[2].axis, element, pointLineRefs[2].isHorizontal)) {
|
|
3906
|
+
pointLineRefs[2].pointRectangles.push(element);
|
|
3907
|
+
}
|
|
3908
|
+
if (isSnapPoint(pointLineRefs[3].axis, element, pointLineRefs[3].isHorizontal)) {
|
|
3909
|
+
pointLineRefs[3].pointRectangles.push(element);
|
|
3910
|
+
}
|
|
3911
|
+
if (isSnapPoint(pointLineRefs[4].axis, element, pointLineRefs[4].isHorizontal)) {
|
|
3912
|
+
pointLineRefs[4].pointRectangles.push(element);
|
|
3913
|
+
}
|
|
3914
|
+
if (isSnapPoint(pointLineRefs[5].axis, element, pointLineRefs[5].isHorizontal)) {
|
|
3915
|
+
pointLineRefs[5].pointRectangles.push(element);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
const setResizePointSnapLine = (axis, pointRectangle, isHorizontal) => {
|
|
3919
|
+
const boundingRectangle = RectangleClient.inflate(RectangleClient.getBoundingRectangle([activeRectangle, pointRectangle]), SNAP_SPACING);
|
|
3920
|
+
if (isHorizontal) {
|
|
3921
|
+
const pointStart = [axis, boundingRectangle.y];
|
|
3922
|
+
const pointEnd = [axis, boundingRectangle.y + boundingRectangle.height];
|
|
3923
|
+
pointLinePoints.push([pointStart, pointEnd]);
|
|
3924
|
+
}
|
|
3925
|
+
else {
|
|
3926
|
+
const pointStart = [boundingRectangle.x, axis];
|
|
3927
|
+
const pointEnd = [boundingRectangle.x + boundingRectangle.width, axis];
|
|
3928
|
+
pointLinePoints.push([pointStart, pointEnd]);
|
|
3929
|
+
}
|
|
3930
|
+
};
|
|
3931
|
+
if (drawHorizontal && pointLineRefs[0].pointRectangles.length) {
|
|
3932
|
+
const leftRectangle = pointLineRefs[0].pointRectangles.length === 1
|
|
3933
|
+
? pointLineRefs[0].pointRectangles[0]
|
|
3934
|
+
: getNearestPointRectangle(pointLineRefs[0].pointRectangles, activeRectangle);
|
|
3935
|
+
setResizePointSnapLine(pointLineRefs[0].axis, leftRectangle, pointLineRefs[0].isHorizontal);
|
|
3936
|
+
}
|
|
3937
|
+
if (drawHorizontal && snapMiddle && pointLineRefs[1].pointRectangles.length) {
|
|
3938
|
+
const middleRectangle = pointLineRefs[1].pointRectangles.length === 1
|
|
3939
|
+
? pointLineRefs[1].pointRectangles[0]
|
|
3940
|
+
: getNearestPointRectangle(pointLineRefs[1].pointRectangles, activeRectangle);
|
|
3941
|
+
setResizePointSnapLine(pointLineRefs[1].axis, middleRectangle, pointLineRefs[1].isHorizontal);
|
|
3942
|
+
}
|
|
3943
|
+
if (drawHorizontal && pointLineRefs[2].pointRectangles.length) {
|
|
3944
|
+
const rightRectangle = pointLineRefs[2].pointRectangles.length === 1
|
|
3945
|
+
? pointLineRefs[2].pointRectangles[0]
|
|
3946
|
+
: getNearestPointRectangle(pointLineRefs[2].pointRectangles, activeRectangle);
|
|
3947
|
+
setResizePointSnapLine(pointLineRefs[2].axis, rightRectangle, pointLineRefs[2].isHorizontal);
|
|
3948
|
+
}
|
|
3949
|
+
if (drawVertical && pointLineRefs[3].pointRectangles.length) {
|
|
3950
|
+
const topRectangle = pointLineRefs[3].pointRectangles.length === 1
|
|
3951
|
+
? pointLineRefs[3].pointRectangles[0]
|
|
3952
|
+
: getNearestPointRectangle(pointLineRefs[3].pointRectangles, activeRectangle);
|
|
3953
|
+
setResizePointSnapLine(pointLineRefs[3].axis, topRectangle, pointLineRefs[3].isHorizontal);
|
|
3954
|
+
}
|
|
3955
|
+
if (drawVertical && snapMiddle && pointLineRefs[4].pointRectangles.length) {
|
|
3956
|
+
const middleRectangle = pointLineRefs[4].pointRectangles.length === 1
|
|
3957
|
+
? pointLineRefs[4].pointRectangles[0]
|
|
3958
|
+
: getNearestPointRectangle(pointLineRefs[4].pointRectangles, activeRectangle);
|
|
3959
|
+
setResizePointSnapLine(pointLineRefs[4].axis, middleRectangle, pointLineRefs[4].isHorizontal);
|
|
3960
|
+
}
|
|
3961
|
+
if (drawVertical && pointLineRefs[5].pointRectangles.length) {
|
|
3962
|
+
const rightRectangle = pointLineRefs[5].pointRectangles.length === 1
|
|
3963
|
+
? pointLineRefs[5].pointRectangles[0]
|
|
3964
|
+
: getNearestPointRectangle(pointLineRefs[5].pointRectangles, activeRectangle);
|
|
3965
|
+
setResizePointSnapLine(pointLineRefs[5].axis, rightRectangle, pointLineRefs[5].isHorizontal);
|
|
3966
|
+
}
|
|
3967
|
+
return drawDashedLines(board, pointLinePoints);
|
|
3968
|
+
}
|
|
3969
|
+
function drawDashedLines(board, lines) {
|
|
3970
|
+
const g = createG();
|
|
3971
|
+
lines.forEach(points => {
|
|
3972
|
+
if (!points.length)
|
|
3973
|
+
return;
|
|
3974
|
+
const line = PlaitBoard.getRoughSVG(board).line(points[0][0], points[0][1], points[1][0], points[1][1], {
|
|
3975
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3976
|
+
strokeWidth: 1,
|
|
3977
|
+
strokeLineDash: [4, 4]
|
|
3978
|
+
});
|
|
3979
|
+
g.appendChild(line);
|
|
3980
|
+
});
|
|
3981
|
+
return g;
|
|
3982
|
+
}
|
|
3983
|
+
function drawSolidLines(board, lines) {
|
|
3984
|
+
const g = createG();
|
|
3985
|
+
lines.forEach(points => {
|
|
3986
|
+
if (!points.length)
|
|
3987
|
+
return;
|
|
3988
|
+
let isHorizontal = points[0][1] === points[1][1];
|
|
3989
|
+
const line = PlaitBoard.getRoughSVG(board).line(points[0][0], points[0][1], points[1][0], points[1][1], {
|
|
3990
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3991
|
+
strokeWidth: 1
|
|
3992
|
+
});
|
|
3993
|
+
g.appendChild(line);
|
|
3994
|
+
points.forEach(point => {
|
|
3995
|
+
const barPoint = getBarPoint(point, isHorizontal);
|
|
3996
|
+
const bar = PlaitBoard.getRoughSVG(board).line(barPoint[0][0], barPoint[0][1], barPoint[1][0], barPoint[1][1], {
|
|
3997
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3998
|
+
strokeWidth: 1
|
|
3999
|
+
});
|
|
4000
|
+
g.appendChild(bar);
|
|
4001
|
+
});
|
|
4002
|
+
});
|
|
4003
|
+
return g;
|
|
4004
|
+
}
|
|
3164
4005
|
|
|
3165
4006
|
const PlaitElement = {
|
|
3166
4007
|
isRootElement(value) {
|
|
@@ -3174,6 +4015,32 @@ const PlaitElement = {
|
|
|
3174
4015
|
},
|
|
3175
4016
|
getComponent(value) {
|
|
3176
4017
|
return ELEMENT_TO_COMPONENT.get(value);
|
|
4018
|
+
},
|
|
4019
|
+
getElementG(value) {
|
|
4020
|
+
const g = NODE_TO_G.get(value);
|
|
4021
|
+
if (!g) {
|
|
4022
|
+
throw new Error(`can not resolve element g: ${JSON.stringify(value)}`);
|
|
4023
|
+
}
|
|
4024
|
+
return g;
|
|
4025
|
+
},
|
|
4026
|
+
hasMounted(element) {
|
|
4027
|
+
const containerG = PlaitElement.getContainerG(element, { suppressThrow: true });
|
|
4028
|
+
if (containerG) {
|
|
4029
|
+
return true;
|
|
4030
|
+
}
|
|
4031
|
+
else {
|
|
4032
|
+
return false;
|
|
4033
|
+
}
|
|
4034
|
+
},
|
|
4035
|
+
getContainerG(value, options) {
|
|
4036
|
+
const containerG = NODE_TO_CONTAINER_G.get(value) || null;
|
|
4037
|
+
if (!containerG) {
|
|
4038
|
+
if (options.suppressThrow) {
|
|
4039
|
+
return null;
|
|
4040
|
+
}
|
|
4041
|
+
throw new Error('can not resolve container g');
|
|
4042
|
+
}
|
|
4043
|
+
return containerG;
|
|
3177
4044
|
}
|
|
3178
4045
|
};
|
|
3179
4046
|
|
|
@@ -3437,7 +4304,7 @@ const PlaitBoard = {
|
|
|
3437
4304
|
return isBoard;
|
|
3438
4305
|
},
|
|
3439
4306
|
isAlive(board) {
|
|
3440
|
-
const isAlive =
|
|
4307
|
+
const isAlive = IS_BOARD_ALIVE.get(board);
|
|
3441
4308
|
return !!isAlive;
|
|
3442
4309
|
},
|
|
3443
4310
|
findPath(board, node) {
|
|
@@ -3615,18 +4482,16 @@ function createBoard(children, options) {
|
|
|
3615
4482
|
globalKeyDown: (event) => { },
|
|
3616
4483
|
keyUp: (event) => { },
|
|
3617
4484
|
dblClick: (event) => { },
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
},
|
|
3621
|
-
insertFragment: (data) => { },
|
|
4485
|
+
buildFragment: (clipboardContext) => clipboardContext,
|
|
4486
|
+
insertFragment: () => { },
|
|
3622
4487
|
deleteFragment: (elements) => {
|
|
3623
4488
|
CoreTransforms.removeElements(board, elements);
|
|
3624
4489
|
},
|
|
3625
4490
|
getDeletedFragment: (data) => data,
|
|
3626
4491
|
getRelatedFragment: (data, originData) => data,
|
|
3627
|
-
drawElement: (context) =>
|
|
3628
|
-
|
|
3629
|
-
|
|
4492
|
+
drawElement: (context) => {
|
|
4493
|
+
throw new Error(`can not resolve plugin element component type: ${context.element.type}`);
|
|
4494
|
+
},
|
|
3630
4495
|
isWithinSelection: element => false,
|
|
3631
4496
|
isRectangleHit: element => false,
|
|
3632
4497
|
isHit: element => false,
|
|
@@ -3645,7 +4510,9 @@ function createBoard(children, options) {
|
|
|
3645
4510
|
globalPointerMove: pointer => { },
|
|
3646
4511
|
globalPointerUp: pointer => { },
|
|
3647
4512
|
isImageBindingAllowed: (element) => false,
|
|
3648
|
-
canAddToGroup: (element) => true
|
|
4513
|
+
canAddToGroup: (element) => true,
|
|
4514
|
+
canSetZIndex: (element) => true,
|
|
4515
|
+
isExpanded: (element) => true
|
|
3649
4516
|
};
|
|
3650
4517
|
return board;
|
|
3651
4518
|
}
|
|
@@ -4039,354 +4906,194 @@ function withViewport(board) {
|
|
|
4039
4906
|
return board;
|
|
4040
4907
|
}
|
|
4041
4908
|
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4909
|
+
function getSnapMovingRef(board, activeRectangle, activeElements) {
|
|
4910
|
+
const snapRectangles = getSnapRectangles(board, activeElements);
|
|
4911
|
+
const snapG = createG();
|
|
4912
|
+
let snapDelta = getPointLineDelta(activeRectangle, snapRectangles);
|
|
4913
|
+
const pointLinesG = drawMovingPointSnapLines(board, snapDelta, activeRectangle, snapRectangles);
|
|
4914
|
+
snapG.append(pointLinesG);
|
|
4915
|
+
const result = getGapSnapLinesAndDelta(board, snapDelta, activeRectangle, snapRectangles);
|
|
4916
|
+
snapDelta = result.snapDelta;
|
|
4917
|
+
snapG.append(result.snapG);
|
|
4918
|
+
return { ...snapDelta, snapG };
|
|
4919
|
+
}
|
|
4920
|
+
function getPointLineDeltas(activeRectangle, snapRectangles, isHorizontal) {
|
|
4921
|
+
const axis = getTripleAxis(activeRectangle, isHorizontal);
|
|
4922
|
+
const deltaStart = getMinPointDelta(snapRectangles, axis[0], isHorizontal);
|
|
4923
|
+
const deltaMiddle = getMinPointDelta(snapRectangles, axis[1], isHorizontal);
|
|
4924
|
+
const deltaEnd = getMinPointDelta(snapRectangles, axis[2], isHorizontal);
|
|
4925
|
+
return [deltaStart, deltaMiddle, deltaEnd];
|
|
4926
|
+
}
|
|
4927
|
+
function getPointLineDelta(activeRectangle, snapRectangles) {
|
|
4928
|
+
let snapDelta = {
|
|
4929
|
+
deltaX: 0,
|
|
4930
|
+
deltaY: 0
|
|
4931
|
+
};
|
|
4932
|
+
function getDelta(isHorizontal) {
|
|
4933
|
+
let delta = 0;
|
|
4934
|
+
const deltas = getPointLineDeltas(activeRectangle, snapRectangles, isHorizontal);
|
|
4935
|
+
for (let i = 0; i < deltas.length; i++) {
|
|
4936
|
+
if (Math.abs(deltas[i]) < SNAP_TOLERANCE) {
|
|
4937
|
+
delta = deltas[i];
|
|
4938
|
+
break;
|
|
4064
4939
|
}
|
|
4065
|
-
}
|
|
4066
|
-
return
|
|
4940
|
+
}
|
|
4941
|
+
return delta;
|
|
4067
4942
|
}
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4943
|
+
snapDelta.deltaX = getDelta(true);
|
|
4944
|
+
snapDelta.deltaY = getDelta(false);
|
|
4945
|
+
return snapDelta;
|
|
4946
|
+
}
|
|
4947
|
+
function updateActiveRectangle(snapDelta, activeRectangle) {
|
|
4948
|
+
const { deltaX, deltaY } = snapDelta;
|
|
4949
|
+
const { x, y, width, height } = activeRectangle;
|
|
4950
|
+
return {
|
|
4951
|
+
x: x + deltaX,
|
|
4952
|
+
y: y + deltaY,
|
|
4953
|
+
width,
|
|
4954
|
+
height
|
|
4955
|
+
};
|
|
4956
|
+
}
|
|
4957
|
+
function drawMovingPointSnapLines(board, snapDelta, activeRectangle, snapRectangles) {
|
|
4958
|
+
const newActiveRectangle = updateActiveRectangle(snapDelta, activeRectangle);
|
|
4959
|
+
return drawPointSnapLines(board, newActiveRectangle, snapRectangles, true, true, true);
|
|
4960
|
+
}
|
|
4961
|
+
function getGapSnapLinesAndDelta(board, snapDelta, activeRectangle, snapRectangles) {
|
|
4962
|
+
let deltaX = snapDelta.deltaX;
|
|
4963
|
+
let deltaY = snapDelta.deltaY;
|
|
4964
|
+
const gapHorizontalResult = getGapLinesAndDelta(activeRectangle, snapRectangles, true);
|
|
4965
|
+
const gapVerticalResult = getGapLinesAndDelta(activeRectangle, snapRectangles, false);
|
|
4966
|
+
const gapSnapLines = [...gapHorizontalResult.lines, ...gapVerticalResult.lines];
|
|
4967
|
+
if (gapHorizontalResult.delta) {
|
|
4968
|
+
deltaX = gapHorizontalResult.delta;
|
|
4969
|
+
}
|
|
4970
|
+
if (gapVerticalResult.delta) {
|
|
4971
|
+
deltaY = gapVerticalResult.delta;
|
|
4972
|
+
}
|
|
4973
|
+
return {
|
|
4974
|
+
snapDelta: { deltaX, deltaY },
|
|
4975
|
+
snapG: drawSolidLines(board, gapSnapLines)
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
function getGapLinesAndDelta(activeRectangle, snapRectangles, isHorizontal) {
|
|
4979
|
+
let lines = [];
|
|
4980
|
+
let delta = 0;
|
|
4981
|
+
let rectangles = [];
|
|
4982
|
+
const axis = isHorizontal ? 'x' : 'y';
|
|
4983
|
+
const side = isHorizontal ? 'width' : 'height';
|
|
4984
|
+
const activeRectangleCenter = activeRectangle[axis] + activeRectangle[side] / 2;
|
|
4985
|
+
snapRectangles.forEach(rec => {
|
|
4986
|
+
const isCross = isHorizontal ? isHorizontalCross(rec, activeRectangle) : isVerticalCross(rec, activeRectangle);
|
|
4987
|
+
if (isCross && !RectangleClient.isHit(rec, activeRectangle)) {
|
|
4988
|
+
rectangles.push(rec);
|
|
4989
|
+
}
|
|
4990
|
+
});
|
|
4991
|
+
rectangles = [...rectangles, activeRectangle].sort((a, b) => a[axis] - b[axis]);
|
|
4992
|
+
const refArray = [];
|
|
4993
|
+
let gapDistance = 0;
|
|
4994
|
+
let beforeIndex = undefined;
|
|
4995
|
+
let afterIndex = undefined;
|
|
4996
|
+
for (let i = 0; i < rectangles.length; i++) {
|
|
4997
|
+
for (let j = i + 1; j < rectangles.length; j++) {
|
|
4998
|
+
const before = rectangles[i];
|
|
4999
|
+
const after = rectangles[j];
|
|
5000
|
+
const distance = after[axis] - (before[axis] + before[side]);
|
|
5001
|
+
let dif = Infinity;
|
|
5002
|
+
if (refArray[i]?.after) {
|
|
5003
|
+
refArray[i].after.push({ distance, index: j });
|
|
4088
5004
|
}
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
alignRectangle.y,
|
|
4092
|
-
alignRectangle.y + alignRectangle.height,
|
|
4093
|
-
this.activeRectangle.y,
|
|
4094
|
-
this.activeRectangle.y + this.activeRectangle.height
|
|
4095
|
-
];
|
|
4096
|
-
const lineTopY = Math.min(...verticalY) - offset;
|
|
4097
|
-
const lineBottomY = Math.max(...verticalY) + offset;
|
|
4098
|
-
const leftLine = [this.activeRectangle.x, lineTopY, this.activeRectangle.x, lineBottomY];
|
|
4099
|
-
const middleLine = [
|
|
4100
|
-
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
4101
|
-
lineTopY,
|
|
4102
|
-
this.activeRectangle.x + this.activeRectangle.width / 2,
|
|
4103
|
-
lineBottomY
|
|
4104
|
-
];
|
|
4105
|
-
const rightLine = [
|
|
4106
|
-
this.activeRectangle.x + this.activeRectangle.width,
|
|
4107
|
-
lineTopY,
|
|
4108
|
-
this.activeRectangle.x + this.activeRectangle.width,
|
|
4109
|
-
lineBottomY
|
|
4110
|
-
];
|
|
4111
|
-
const shouldDrawLeftLine = closestDistances.indexX === 0 ||
|
|
4112
|
-
closestDistances.indexX === 1 ||
|
|
4113
|
-
(closestDistances.indexX === 2 && this.activeRectangle.width === alignRectangle.width);
|
|
4114
|
-
if (shouldDrawLeftLine && !alignLines[0]) {
|
|
4115
|
-
alignLines[0] = leftLine;
|
|
4116
|
-
}
|
|
4117
|
-
const shouldDrawRightLine = closestDistances.indexX === 2 ||
|
|
4118
|
-
closestDistances.indexX === 3 ||
|
|
4119
|
-
(closestDistances.indexX === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
4120
|
-
if (shouldDrawRightLine && !alignLines[2]) {
|
|
4121
|
-
alignLines[2] = rightLine;
|
|
4122
|
-
}
|
|
4123
|
-
const shouldDrawMiddleLine = closestDistances.indexX === 4 || (!shouldDrawLeftLine && !shouldDrawRightLine);
|
|
4124
|
-
if (shouldDrawMiddleLine && !alignLines[1]) {
|
|
4125
|
-
alignLines[1] = middleLine;
|
|
4126
|
-
}
|
|
4127
|
-
isCorrectX = true;
|
|
5005
|
+
else {
|
|
5006
|
+
refArray[i] = { ...refArray[i], after: [{ distance, index: j }] };
|
|
4128
5007
|
}
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
deltaY = closestDistances.yDistance;
|
|
4132
|
-
this.activeRectangle.y -= deltaY;
|
|
4133
|
-
isCorrectY = true;
|
|
4134
|
-
canDrawVertical = true;
|
|
5008
|
+
if (refArray[j]?.before) {
|
|
5009
|
+
refArray[j].before.push({ distance, index: i });
|
|
4135
5010
|
}
|
|
4136
|
-
|
|
4137
|
-
|
|
5011
|
+
else {
|
|
5012
|
+
refArray[j] = { ...refArray[j], before: [{ distance, index: i }] };
|
|
4138
5013
|
}
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
const lineRightX = Math.max(...horizontalX) + offset;
|
|
4148
|
-
const topLine = [lineLeftX, this.activeRectangle.y, lineRightX, this.activeRectangle.y];
|
|
4149
|
-
const horizontalMiddleLine = [
|
|
4150
|
-
lineLeftX,
|
|
4151
|
-
this.activeRectangle.y + this.activeRectangle.height / 2,
|
|
4152
|
-
lineRightX,
|
|
4153
|
-
this.activeRectangle.y + this.activeRectangle.height / 2
|
|
4154
|
-
];
|
|
4155
|
-
const bottomLine = [
|
|
4156
|
-
lineLeftX,
|
|
4157
|
-
this.activeRectangle.y + this.activeRectangle.height,
|
|
4158
|
-
lineRightX,
|
|
4159
|
-
this.activeRectangle.y + this.activeRectangle.height
|
|
4160
|
-
];
|
|
4161
|
-
const shouldDrawTopLine = closestDistances.indexY === 0 ||
|
|
4162
|
-
closestDistances.indexY === 1 ||
|
|
4163
|
-
(closestDistances.indexY === 2 && this.activeRectangle.height === alignRectangle.height);
|
|
4164
|
-
if (shouldDrawTopLine && !alignLines[3]) {
|
|
4165
|
-
alignLines[3] = topLine;
|
|
4166
|
-
}
|
|
4167
|
-
const shouldDrawBottomLine = closestDistances.indexY === 2 ||
|
|
4168
|
-
closestDistances.indexY === 3 ||
|
|
4169
|
-
(closestDistances.indexY === 0 && this.activeRectangle.width === alignRectangle.width);
|
|
4170
|
-
if (shouldDrawBottomLine && !alignLines[5]) {
|
|
4171
|
-
alignLines[5] = bottomLine;
|
|
4172
|
-
}
|
|
4173
|
-
const shouldDrawMiddleLine = closestDistances.indexY === 4 || (!shouldDrawTopLine && !shouldDrawBottomLine);
|
|
4174
|
-
if (shouldDrawMiddleLine && !alignLines[4]) {
|
|
4175
|
-
alignLines[4] = horizontalMiddleLine;
|
|
4176
|
-
}
|
|
5014
|
+
//middle
|
|
5015
|
+
let _center = (before[axis] + before[side] + after[axis]) / 2;
|
|
5016
|
+
dif = Math.abs(_center - activeRectangleCenter);
|
|
5017
|
+
if (dif < SNAP_TOLERANCE) {
|
|
5018
|
+
gapDistance = (after[axis] - (before[axis] + before[side]) - activeRectangle[side]) / 2;
|
|
5019
|
+
delta = _center - activeRectangleCenter;
|
|
5020
|
+
beforeIndex = i;
|
|
5021
|
+
afterIndex = j;
|
|
4177
5022
|
}
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
if (distributeHorizontalResult.delta) {
|
|
4187
|
-
deltaX = distributeHorizontalResult.delta;
|
|
4188
|
-
if (alignDeltaX !== deltaX) {
|
|
4189
|
-
alignLines[0] = [];
|
|
4190
|
-
alignLines[1] = [];
|
|
4191
|
-
alignLines[2] = [];
|
|
5023
|
+
//after
|
|
5024
|
+
const distanceRight = after[axis] - (before[axis] + before[side]);
|
|
5025
|
+
_center = after[axis] + after[side] + distanceRight + activeRectangle[side] / 2;
|
|
5026
|
+
dif = Math.abs(_center - activeRectangleCenter);
|
|
5027
|
+
if (!gapDistance && dif < SNAP_TOLERANCE) {
|
|
5028
|
+
gapDistance = distanceRight;
|
|
5029
|
+
beforeIndex = j;
|
|
5030
|
+
delta = _center - activeRectangleCenter;
|
|
4192
5031
|
}
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
5032
|
+
//before
|
|
5033
|
+
const distanceBefore = after[axis] - (before[axis] + before[side]);
|
|
5034
|
+
_center = before[axis] - distanceBefore - activeRectangle[side] / 2;
|
|
5035
|
+
dif = Math.abs(_center - activeRectangleCenter);
|
|
5036
|
+
if (!gapDistance && dif < SNAP_TOLERANCE) {
|
|
5037
|
+
gapDistance = distanceBefore;
|
|
5038
|
+
afterIndex = i;
|
|
5039
|
+
delta = _center - activeRectangleCenter;
|
|
4200
5040
|
}
|
|
4201
5041
|
}
|
|
4202
|
-
if (alignLines.length) {
|
|
4203
|
-
this.drawAlignLines(alignLines, g);
|
|
4204
|
-
}
|
|
4205
|
-
if (distributeLines.length) {
|
|
4206
|
-
this.drawDistributeLines(distributeLines, g);
|
|
4207
|
-
}
|
|
4208
|
-
return { deltaX, deltaY, g };
|
|
4209
|
-
}
|
|
4210
|
-
calculateClosestDistances(activeRectangle, alignRectangle) {
|
|
4211
|
-
const activeRectangleCenter = [activeRectangle.x + activeRectangle.width / 2, activeRectangle.y + activeRectangle.height / 2];
|
|
4212
|
-
const alignRectangleCenter = [alignRectangle.x + alignRectangle.width / 2, alignRectangle.y + alignRectangle.height / 2];
|
|
4213
|
-
const centerXDistance = activeRectangleCenter[0] - alignRectangleCenter[0];
|
|
4214
|
-
const centerYDistance = activeRectangleCenter[1] - alignRectangleCenter[1];
|
|
4215
|
-
const leftToLeft = activeRectangle.x - alignRectangle.x;
|
|
4216
|
-
const leftToRight = activeRectangle.x - (alignRectangle.x + alignRectangle.width);
|
|
4217
|
-
const rightToRight = activeRectangle.x + activeRectangle.width - (alignRectangle.x + alignRectangle.width);
|
|
4218
|
-
const rightToLeft = activeRectangle.x + activeRectangle.width - alignRectangle.x;
|
|
4219
|
-
const topToTop = activeRectangle.y - alignRectangle.y;
|
|
4220
|
-
const topToBottom = activeRectangle.y - (alignRectangle.y + alignRectangle.height);
|
|
4221
|
-
const bottomToTop = activeRectangle.y + activeRectangle.height - alignRectangle.y;
|
|
4222
|
-
const bottomToBottom = activeRectangle.y + activeRectangle.height - (alignRectangle.y + alignRectangle.height);
|
|
4223
|
-
const xDistances = [leftToLeft, leftToRight, rightToRight, rightToLeft, centerXDistance];
|
|
4224
|
-
const yDistances = [topToTop, topToBottom, bottomToBottom, bottomToTop, centerYDistance];
|
|
4225
|
-
const xDistancesAbs = xDistances.map(distance => Math.abs(distance));
|
|
4226
|
-
const yDistancesAbs = yDistances.map(distance => Math.abs(distance));
|
|
4227
|
-
const indexX = xDistancesAbs.indexOf(Math.min(...xDistancesAbs));
|
|
4228
|
-
const indexY = yDistancesAbs.indexOf(Math.min(...yDistancesAbs));
|
|
4229
|
-
return {
|
|
4230
|
-
absXDistance: xDistancesAbs[indexX],
|
|
4231
|
-
xDistance: xDistances[indexX],
|
|
4232
|
-
absYDistance: yDistancesAbs[indexY],
|
|
4233
|
-
yDistance: yDistances[indexY],
|
|
4234
|
-
indexX,
|
|
4235
|
-
indexY
|
|
4236
|
-
};
|
|
4237
5042
|
}
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
let beforeIndex = undefined;
|
|
4255
|
-
let afterIndex = undefined;
|
|
4256
|
-
for (let i = 0; i < rectangles.length; i++) {
|
|
4257
|
-
for (let j = i + 1; j < rectangles.length; j++) {
|
|
4258
|
-
const before = rectangles[i];
|
|
4259
|
-
const after = rectangles[j];
|
|
4260
|
-
const distance = after[axis] - (before[axis] + before[side]);
|
|
4261
|
-
let dif = Infinity;
|
|
4262
|
-
if (refArray[i]?.after) {
|
|
4263
|
-
refArray[i].after.push({ distance, index: j });
|
|
4264
|
-
}
|
|
4265
|
-
else {
|
|
4266
|
-
refArray[i] = { ...refArray[i], after: [{ distance, index: j }] };
|
|
4267
|
-
}
|
|
4268
|
-
if (refArray[j]?.before) {
|
|
4269
|
-
refArray[j].before.push({ distance, index: i });
|
|
4270
|
-
}
|
|
4271
|
-
else {
|
|
4272
|
-
refArray[j] = { ...refArray[j], before: [{ distance, index: i }] };
|
|
4273
|
-
}
|
|
4274
|
-
//middle
|
|
4275
|
-
let _center = (before[axis] + before[side] + after[axis]) / 2;
|
|
4276
|
-
dif = Math.abs(activeRectangleCenter - _center);
|
|
4277
|
-
if (dif < SNAP_TOLERANCE) {
|
|
4278
|
-
distributeDistance = (after[axis] - (before[axis] + before[side]) - this.activeRectangle[side]) / 2;
|
|
4279
|
-
delta = activeRectangleCenter - _center;
|
|
4280
|
-
beforeIndex = i;
|
|
4281
|
-
afterIndex = j;
|
|
4282
|
-
}
|
|
4283
|
-
//after
|
|
4284
|
-
const distanceRight = after[axis] - (before[axis] + before[side]);
|
|
4285
|
-
_center = after[axis] + after[side] + distanceRight + this.activeRectangle[side] / 2;
|
|
4286
|
-
dif = Math.abs(activeRectangleCenter - _center);
|
|
4287
|
-
if (!distributeDistance && dif < SNAP_TOLERANCE) {
|
|
4288
|
-
distributeDistance = distanceRight;
|
|
4289
|
-
beforeIndex = j;
|
|
4290
|
-
delta = activeRectangleCenter - _center;
|
|
4291
|
-
}
|
|
4292
|
-
//before
|
|
4293
|
-
const distanceBefore = after[axis] - (before[axis] + before[side]);
|
|
4294
|
-
_center = before[axis] - distanceBefore - this.activeRectangle[side] / 2;
|
|
4295
|
-
dif = Math.abs(activeRectangleCenter - _center);
|
|
4296
|
-
if (!distributeDistance && dif < SNAP_TOLERANCE) {
|
|
4297
|
-
distributeDistance = distanceBefore;
|
|
4298
|
-
afterIndex = i;
|
|
4299
|
-
delta = activeRectangleCenter - _center;
|
|
4300
|
-
}
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
const activeIndex = rectangles.indexOf(this.activeRectangle);
|
|
4304
|
-
let beforeIndexes = [];
|
|
4305
|
-
let afterIndexes = [];
|
|
4306
|
-
if (beforeIndex !== undefined) {
|
|
4307
|
-
beforeIndexes.push(beforeIndex);
|
|
4308
|
-
findRectangle(distributeDistance, refArray[beforeIndex], 'before', beforeIndexes);
|
|
4309
|
-
}
|
|
4310
|
-
if (afterIndex !== undefined) {
|
|
4311
|
-
afterIndexes.push(afterIndex);
|
|
4312
|
-
findRectangle(distributeDistance, refArray[afterIndex], 'after', afterIndexes);
|
|
4313
|
-
}
|
|
4314
|
-
if (beforeIndexes.length || afterIndexes.length) {
|
|
4315
|
-
const indexArr = [...beforeIndexes.reverse(), activeIndex, ...afterIndexes];
|
|
4316
|
-
this.activeRectangle[axis] -= delta;
|
|
4317
|
-
for (let i = 1; i < indexArr.length; i++) {
|
|
4318
|
-
distributeLines.push(getLinePoints(rectangles[indexArr[i - 1]], rectangles[indexArr[i]]));
|
|
4319
|
-
}
|
|
5043
|
+
const activeIndex = rectangles.indexOf(activeRectangle);
|
|
5044
|
+
let beforeIndexes = [];
|
|
5045
|
+
let afterIndexes = [];
|
|
5046
|
+
if (beforeIndex !== undefined) {
|
|
5047
|
+
beforeIndexes.push(beforeIndex);
|
|
5048
|
+
findRectangle(gapDistance, refArray[beforeIndex], 'before', beforeIndexes);
|
|
5049
|
+
}
|
|
5050
|
+
if (afterIndex !== undefined) {
|
|
5051
|
+
afterIndexes.push(afterIndex);
|
|
5052
|
+
findRectangle(gapDistance, refArray[afterIndex], 'after', afterIndexes);
|
|
5053
|
+
}
|
|
5054
|
+
if (beforeIndexes.length || afterIndexes.length) {
|
|
5055
|
+
const indexArr = [...beforeIndexes.reverse(), activeIndex, ...afterIndexes];
|
|
5056
|
+
activeRectangle[axis] += delta;
|
|
5057
|
+
for (let i = 1; i < indexArr.length; i++) {
|
|
5058
|
+
lines.push(getLinePoints(rectangles[indexArr[i - 1]], rectangles[indexArr[i]]));
|
|
4320
5059
|
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
5060
|
+
}
|
|
5061
|
+
function findRectangle(distance, ref, direction, rectangleIndexes) {
|
|
5062
|
+
const arr = ref[direction];
|
|
5063
|
+
const index = refArray.indexOf(ref);
|
|
5064
|
+
if ((index === 0 && direction === 'before') || (index === refArray.length - 1 && direction === 'after'))
|
|
5065
|
+
return;
|
|
5066
|
+
for (let i = 0; i < arr.length; i++) {
|
|
5067
|
+
if (Math.abs(arr[i].distance - distance) < 0.1) {
|
|
5068
|
+
rectangleIndexes.push(arr[i].index);
|
|
5069
|
+
findRectangle(distance, refArray[arr[i].index], direction, rectangleIndexes);
|
|
4325
5070
|
return;
|
|
4326
|
-
for (let i = 0; i < arr.length; i++) {
|
|
4327
|
-
if (Math.abs(arr[i].distance - distance) < 0.1) {
|
|
4328
|
-
rectangleIndexes.push(arr[i].index);
|
|
4329
|
-
findRectangle(distance, refArray[arr[i].index], direction, rectangleIndexes);
|
|
4330
|
-
return;
|
|
4331
|
-
}
|
|
4332
5071
|
}
|
|
4333
5072
|
}
|
|
4334
|
-
function getLinePoints(beforeRectangle, afterRectangle) {
|
|
4335
|
-
const oppositeAxis = axis === 'x' ? 'y' : 'x';
|
|
4336
|
-
const oppositeSide = side === 'width' ? 'height' : 'width';
|
|
4337
|
-
const align = [
|
|
4338
|
-
beforeRectangle[oppositeAxis],
|
|
4339
|
-
beforeRectangle[oppositeAxis] + beforeRectangle[oppositeSide],
|
|
4340
|
-
afterRectangle[oppositeAxis],
|
|
4341
|
-
afterRectangle[oppositeAxis] + afterRectangle[oppositeSide]
|
|
4342
|
-
];
|
|
4343
|
-
const sortArr = align.sort((a, b) => a - b);
|
|
4344
|
-
const average = (sortArr[1] + sortArr[2]) / 2;
|
|
4345
|
-
const offset = 3;
|
|
4346
|
-
return isHorizontal
|
|
4347
|
-
? [
|
|
4348
|
-
[beforeRectangle.x + beforeRectangle.width + offset, average],
|
|
4349
|
-
[afterRectangle.x - offset, average]
|
|
4350
|
-
]
|
|
4351
|
-
: [
|
|
4352
|
-
[average, beforeRectangle.y + beforeRectangle.height + offset],
|
|
4353
|
-
[average, afterRectangle.y - offset]
|
|
4354
|
-
];
|
|
4355
|
-
}
|
|
4356
|
-
return { delta, distributeLines };
|
|
4357
|
-
}
|
|
4358
|
-
drawAlignLines(lines, g) {
|
|
4359
|
-
lines.forEach(points => {
|
|
4360
|
-
if (!points.length)
|
|
4361
|
-
return;
|
|
4362
|
-
const xAlign = PlaitBoard.getRoughSVG(this.board).line(points[0], points[1], points[2], points[3], {
|
|
4363
|
-
stroke: SELECTION_BORDER_COLOR,
|
|
4364
|
-
strokeWidth: 1,
|
|
4365
|
-
strokeLineDash: [4, 4]
|
|
4366
|
-
});
|
|
4367
|
-
g.appendChild(xAlign);
|
|
4368
|
-
});
|
|
4369
5073
|
}
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
5074
|
+
function getLinePoints(beforeRectangle, afterRectangle) {
|
|
5075
|
+
const oppositeAxis = axis === 'x' ? 'y' : 'x';
|
|
5076
|
+
const oppositeSide = side === 'width' ? 'height' : 'width';
|
|
5077
|
+
const snap = [
|
|
5078
|
+
beforeRectangle[oppositeAxis],
|
|
5079
|
+
beforeRectangle[oppositeAxis] + beforeRectangle[oppositeSide],
|
|
5080
|
+
afterRectangle[oppositeAxis],
|
|
5081
|
+
afterRectangle[oppositeAxis] + afterRectangle[oppositeSide]
|
|
5082
|
+
];
|
|
5083
|
+
const sortArr = snap.sort((a, b) => a - b);
|
|
5084
|
+
const average = (sortArr[1] + sortArr[2]) / 2;
|
|
5085
|
+
const offset = 3;
|
|
5086
|
+
return isHorizontal
|
|
5087
|
+
? [
|
|
5088
|
+
[beforeRectangle.x + beforeRectangle.width + offset, average],
|
|
5089
|
+
[afterRectangle.x - offset, average]
|
|
5090
|
+
]
|
|
5091
|
+
: [
|
|
5092
|
+
[average, beforeRectangle.y + beforeRectangle.height + offset],
|
|
5093
|
+
[average, afterRectangle.y - offset]
|
|
5094
|
+
];
|
|
4389
5095
|
}
|
|
5096
|
+
return { delta, lines };
|
|
4390
5097
|
}
|
|
4391
5098
|
function isHorizontalCross(rectangle, other) {
|
|
4392
5099
|
return !(rectangle.y + rectangle.height < other.y || rectangle.y > other.y + other.height);
|
|
@@ -4394,17 +5101,6 @@ function isHorizontalCross(rectangle, other) {
|
|
|
4394
5101
|
function isVerticalCross(rectangle, other) {
|
|
4395
5102
|
return !(rectangle.x + rectangle.width < other.x || rectangle.x > other.x + other.width);
|
|
4396
5103
|
}
|
|
4397
|
-
function getBarPoint(point, isHorizontal) {
|
|
4398
|
-
return isHorizontal
|
|
4399
|
-
? [
|
|
4400
|
-
[point[0], point[1] - 4],
|
|
4401
|
-
[point[0], point[1] + 4]
|
|
4402
|
-
]
|
|
4403
|
-
: [
|
|
4404
|
-
[point[0] - 4, point[1]],
|
|
4405
|
-
[point[0] + 4, point[1]]
|
|
4406
|
-
];
|
|
4407
|
-
}
|
|
4408
5104
|
|
|
4409
5105
|
function withMoving(board) {
|
|
4410
5106
|
const { pointerDown, pointerMove, globalPointerUp, globalPointerMove } = board;
|
|
@@ -4413,7 +5109,7 @@ function withMoving(board) {
|
|
|
4413
5109
|
let isPreventDefault = false;
|
|
4414
5110
|
let startPoint;
|
|
4415
5111
|
let activeElements = [];
|
|
4416
|
-
let
|
|
5112
|
+
let snapG = null;
|
|
4417
5113
|
let activeElementsRectangle = null;
|
|
4418
5114
|
let selectedTargetElements = null;
|
|
4419
5115
|
let hitTargetElement = undefined;
|
|
@@ -4462,7 +5158,7 @@ function withMoving(board) {
|
|
|
4462
5158
|
if (!isPreventDefault) {
|
|
4463
5159
|
isPreventDefault = true;
|
|
4464
5160
|
}
|
|
4465
|
-
|
|
5161
|
+
snapG?.remove();
|
|
4466
5162
|
const endPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
4467
5163
|
offsetX = endPoint[0] - startPoint[0];
|
|
4468
5164
|
offsetY = endPoint[1] - startPoint[1];
|
|
@@ -4483,13 +5179,13 @@ function withMoving(board) {
|
|
|
4483
5179
|
x: activeElementsRectangle.x + offsetX,
|
|
4484
5180
|
y: activeElementsRectangle.y + offsetY
|
|
4485
5181
|
};
|
|
4486
|
-
const
|
|
4487
|
-
const ref =
|
|
4488
|
-
offsetX
|
|
4489
|
-
offsetY
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
PlaitBoard.getElementActiveHost(board).append(
|
|
5182
|
+
const activeRectangle = getRectangleByAngle(newRectangle, getSelectionAngle(activeElements)) || newRectangle;
|
|
5183
|
+
const ref = getSnapMovingRef(board, activeRectangle, activeElements);
|
|
5184
|
+
offsetX += ref.deltaX;
|
|
5185
|
+
offsetY += ref.deltaY;
|
|
5186
|
+
snapG = ref.snapG;
|
|
5187
|
+
snapG.classList.add(ACTIVE_MOVING_CLASS_NAME);
|
|
5188
|
+
PlaitBoard.getElementActiveHost(board).append(snapG);
|
|
4493
5189
|
handleTouchTarget(board);
|
|
4494
5190
|
const currentElements = updatePoints(board, activeElements, offsetX, offsetY);
|
|
4495
5191
|
PlaitBoard.getBoardContainer(board).classList.add('element-moving');
|
|
@@ -4524,7 +5220,7 @@ function withMoving(board) {
|
|
|
4524
5220
|
globalPointerUp(event);
|
|
4525
5221
|
};
|
|
4526
5222
|
function cancelMove(board) {
|
|
4527
|
-
|
|
5223
|
+
snapG?.remove();
|
|
4528
5224
|
startPoint = null;
|
|
4529
5225
|
activeElementsRectangle = null;
|
|
4530
5226
|
offsetX = 0;
|
|
@@ -4708,7 +5404,36 @@ const withHotkey = (board) => {
|
|
|
4708
5404
|
Transforms.addSelectionWithTemporaryElements(board, elements);
|
|
4709
5405
|
return;
|
|
4710
5406
|
}
|
|
5407
|
+
if (!PlaitBoard.isReadonly(board)) {
|
|
5408
|
+
if (isKeyHotkey('mod+]', event)) {
|
|
5409
|
+
event.preventDefault();
|
|
5410
|
+
Transforms.moveUp(board);
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
if (isKeyHotkey('mod+[', event)) {
|
|
5414
|
+
event.preventDefault();
|
|
5415
|
+
Transforms.moveDown(board);
|
|
5416
|
+
return;
|
|
5417
|
+
}
|
|
5418
|
+
if (isKeyHotkey('mod+option+‘', event)) {
|
|
5419
|
+
event.preventDefault();
|
|
5420
|
+
Transforms.moveToTop(board);
|
|
5421
|
+
return;
|
|
5422
|
+
}
|
|
5423
|
+
if (isKeyHotkey('mod+option+“', event)) {
|
|
5424
|
+
event.preventDefault();
|
|
5425
|
+
Transforms.moveToBottom(board);
|
|
5426
|
+
return;
|
|
5427
|
+
}
|
|
5428
|
+
}
|
|
4711
5429
|
const selectedElements = getSelectedElements(board);
|
|
5430
|
+
if (!PlaitBoard.isReadonly(board) && selectedElements.length > 0) {
|
|
5431
|
+
if (isKeyHotkey('mod+d', event)) {
|
|
5432
|
+
event.preventDefault();
|
|
5433
|
+
duplicateElements(board, selectedElements);
|
|
5434
|
+
return;
|
|
5435
|
+
}
|
|
5436
|
+
}
|
|
4712
5437
|
if (!PlaitBoard.isReadonly(board) &&
|
|
4713
5438
|
selectedElements.length > 0 &&
|
|
4714
5439
|
(hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event))) {
|
|
@@ -4768,162 +5493,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImpor
|
|
|
4768
5493
|
type: Injectable
|
|
4769
5494
|
}] });
|
|
4770
5495
|
|
|
4771
|
-
class PlaitElementComponent {
|
|
4772
|
-
constructor(renderer2, viewContainerRef) {
|
|
4773
|
-
this.renderer2 = renderer2;
|
|
4774
|
-
this.viewContainerRef = viewContainerRef;
|
|
4775
|
-
this.initialized = false;
|
|
4776
|
-
}
|
|
4777
|
-
ngOnInit() {
|
|
4778
|
-
this.initialize();
|
|
4779
|
-
this.drawElement();
|
|
4780
|
-
}
|
|
4781
|
-
initialize() {
|
|
4782
|
-
NODE_TO_INDEX.set(this.element, this.index);
|
|
4783
|
-
NODE_TO_PARENT.set(this.element, this.parent);
|
|
4784
|
-
this.initialized = true;
|
|
4785
|
-
}
|
|
4786
|
-
drawElement() {
|
|
4787
|
-
const context = this.getContext();
|
|
4788
|
-
const result = this.board.drawElement(context);
|
|
4789
|
-
if (Array.isArray(result)) {
|
|
4790
|
-
}
|
|
4791
|
-
else {
|
|
4792
|
-
const componentRef = this.viewContainerRef.createComponent(result);
|
|
4793
|
-
const instance = componentRef.instance;
|
|
4794
|
-
instance.context = context;
|
|
4795
|
-
this.insertG(instance.rootG ? instance.rootG : instance.g);
|
|
4796
|
-
this.instance = instance;
|
|
4797
|
-
}
|
|
4798
|
-
}
|
|
4799
|
-
insertG(g) {
|
|
4800
|
-
if (PlaitBoard.isBoard(this.parent)) {
|
|
4801
|
-
this.parentG.append(g);
|
|
4802
|
-
}
|
|
4803
|
-
else {
|
|
4804
|
-
let siblingG = PlaitElement.getComponent(this.parent).g;
|
|
4805
|
-
if (this.index > 0) {
|
|
4806
|
-
const brotherElement = this.parent.children[this.index - 1];
|
|
4807
|
-
const lastElement = PlaitNode.last(this.board, PlaitBoard.findPath(this.board, brotherElement));
|
|
4808
|
-
let component = PlaitElement.getComponent(lastElement) || PlaitElement.getComponent(brotherElement);
|
|
4809
|
-
siblingG = component.g;
|
|
4810
|
-
}
|
|
4811
|
-
this.parentG.insertBefore(g, siblingG);
|
|
4812
|
-
}
|
|
4813
|
-
}
|
|
4814
|
-
ngOnChanges(simpleChanges) {
|
|
4815
|
-
if (this.initialized) {
|
|
4816
|
-
NODE_TO_INDEX.set(this.element, this.index);
|
|
4817
|
-
NODE_TO_PARENT.set(this.element, this.parent);
|
|
4818
|
-
const elementChanged = simpleChanges['element'];
|
|
4819
|
-
const context = this.getContext();
|
|
4820
|
-
if (elementChanged && isSelectedElement(this.board, elementChanged.previousValue)) {
|
|
4821
|
-
context.selected = true;
|
|
4822
|
-
removeSelectedElement(this.board, elementChanged.previousValue);
|
|
4823
|
-
addSelectedElement(this.board, this.element);
|
|
4824
|
-
}
|
|
4825
|
-
if (this.instance) {
|
|
4826
|
-
this.instance.context = context;
|
|
4827
|
-
}
|
|
4828
|
-
}
|
|
4829
|
-
}
|
|
4830
|
-
getContext() {
|
|
4831
|
-
const isSelected = isSelectedElement(this.board, this.element);
|
|
4832
|
-
const context = {
|
|
4833
|
-
element: this.element,
|
|
4834
|
-
parent: this.parent,
|
|
4835
|
-
board: this.board,
|
|
4836
|
-
selected: isSelected,
|
|
4837
|
-
effect: this.effect
|
|
4838
|
-
};
|
|
4839
|
-
return context;
|
|
4840
|
-
}
|
|
4841
|
-
ngOnDestroy() {
|
|
4842
|
-
this.board.destroyElement(this.getContext());
|
|
4843
|
-
}
|
|
4844
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitElementComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4845
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.2.4", 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 }); }
|
|
4846
|
-
}
|
|
4847
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitElementComponent, decorators: [{
|
|
4848
|
-
type: Component,
|
|
4849
|
-
args: [{
|
|
4850
|
-
selector: 'plait-element',
|
|
4851
|
-
template: '',
|
|
4852
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4853
|
-
standalone: true
|
|
4854
|
-
}]
|
|
4855
|
-
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ViewContainerRef }], propDecorators: { index: [{
|
|
4856
|
-
type: Input
|
|
4857
|
-
}], element: [{
|
|
4858
|
-
type: Input
|
|
4859
|
-
}], parent: [{
|
|
4860
|
-
type: Input
|
|
4861
|
-
}], board: [{
|
|
4862
|
-
type: Input
|
|
4863
|
-
}], effect: [{
|
|
4864
|
-
type: Input
|
|
4865
|
-
}], parentG: [{
|
|
4866
|
-
type: Input
|
|
4867
|
-
}] } });
|
|
4868
|
-
|
|
4869
|
-
class PlaitChildrenElementComponent {
|
|
4870
|
-
constructor() {
|
|
4871
|
-
this.trackBy = (index, element) => {
|
|
4872
|
-
return element.id;
|
|
4873
|
-
};
|
|
4874
|
-
}
|
|
4875
|
-
ngOnInit() {
|
|
4876
|
-
if (!this.parent) {
|
|
4877
|
-
this.parent = this.board;
|
|
4878
|
-
}
|
|
4879
|
-
if (!this.parentG) {
|
|
4880
|
-
this.parentG = PlaitBoard.getElementHost(this.board);
|
|
4881
|
-
}
|
|
4882
|
-
}
|
|
4883
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitChildrenElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4884
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.2.4", type: PlaitChildrenElementComponent, isStandalone: true, selector: "plait-children", inputs: { board: "board", parent: "parent", effect: "effect", parentG: "parentG" }, ngImport: i0, template: `
|
|
4885
|
-
<plait-element
|
|
4886
|
-
*ngFor="let item of parent.children; let index = index; trackBy: trackBy"
|
|
4887
|
-
[index]="index"
|
|
4888
|
-
[element]="item"
|
|
4889
|
-
[parent]="parent"
|
|
4890
|
-
[board]="board"
|
|
4891
|
-
[effect]="effect"
|
|
4892
|
-
[parentG]="parentG"
|
|
4893
|
-
></plait-element>
|
|
4894
|
-
`, 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"] }] }); }
|
|
4895
|
-
}
|
|
4896
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitChildrenElementComponent, decorators: [{
|
|
4897
|
-
type: Component,
|
|
4898
|
-
args: [{
|
|
4899
|
-
selector: 'plait-children',
|
|
4900
|
-
template: `
|
|
4901
|
-
<plait-element
|
|
4902
|
-
*ngFor="let item of parent.children; let index = index; trackBy: trackBy"
|
|
4903
|
-
[index]="index"
|
|
4904
|
-
[element]="item"
|
|
4905
|
-
[parent]="parent"
|
|
4906
|
-
[board]="board"
|
|
4907
|
-
[effect]="effect"
|
|
4908
|
-
[parentG]="parentG"
|
|
4909
|
-
></plait-element>
|
|
4910
|
-
`,
|
|
4911
|
-
standalone: true,
|
|
4912
|
-
imports: [NgFor, PlaitElementComponent]
|
|
4913
|
-
}]
|
|
4914
|
-
}], ctorParameters: () => [], propDecorators: { board: [{
|
|
4915
|
-
type: Input
|
|
4916
|
-
}], parent: [{
|
|
4917
|
-
type: Input
|
|
4918
|
-
}], effect: [{
|
|
4919
|
-
type: Input
|
|
4920
|
-
}], parentG: [{
|
|
4921
|
-
type: Input
|
|
4922
|
-
}] } });
|
|
4923
|
-
|
|
4924
5496
|
function withRelatedFragment(board) {
|
|
4925
|
-
const {
|
|
4926
|
-
board.
|
|
5497
|
+
const { buildFragment } = board;
|
|
5498
|
+
board.buildFragment = (clipboardContext, rectangle, type) => {
|
|
4927
5499
|
const relatedFragment = board.getRelatedFragment([]);
|
|
4928
5500
|
if (!clipboardContext) {
|
|
4929
5501
|
clipboardContext = createClipboardContext(WritableClipboardType.elements, relatedFragment, '');
|
|
@@ -4932,10 +5504,10 @@ function withRelatedFragment(board) {
|
|
|
4932
5504
|
clipboardContext = addClipboardContext(clipboardContext, {
|
|
4933
5505
|
text: '',
|
|
4934
5506
|
type: WritableClipboardType.elements,
|
|
4935
|
-
|
|
5507
|
+
elements: relatedFragment
|
|
4936
5508
|
});
|
|
4937
5509
|
}
|
|
4938
|
-
|
|
5510
|
+
return buildFragment(clipboardContext, rectangle, type);
|
|
4939
5511
|
};
|
|
4940
5512
|
return board;
|
|
4941
5513
|
}
|
|
@@ -4980,7 +5552,6 @@ class PlaitBoardComponent {
|
|
|
4980
5552
|
this.elementRef = elementRef;
|
|
4981
5553
|
this.ngZone = ngZone;
|
|
4982
5554
|
this.hasInitialized = false;
|
|
4983
|
-
this.effect = {};
|
|
4984
5555
|
this.destroy$ = new Subject();
|
|
4985
5556
|
this.plaitValue = [];
|
|
4986
5557
|
this.plaitPlugins = [];
|
|
@@ -5023,7 +5594,7 @@ class PlaitBoardComponent {
|
|
|
5023
5594
|
});
|
|
5024
5595
|
BOARD_TO_ON_CHANGE.set(this.board, () => {
|
|
5025
5596
|
this.ngZone.run(() => {
|
|
5026
|
-
this.
|
|
5597
|
+
this.updateListRender();
|
|
5027
5598
|
});
|
|
5028
5599
|
});
|
|
5029
5600
|
BOARD_TO_AFTER_CHANGE.set(this.board, () => {
|
|
@@ -5039,15 +5610,12 @@ class PlaitBoardComponent {
|
|
|
5039
5610
|
this.plaitChange.emit(changeEvent);
|
|
5040
5611
|
});
|
|
5041
5612
|
});
|
|
5613
|
+
this.initializeListRender();
|
|
5042
5614
|
this.hasInitialized = true;
|
|
5043
5615
|
}
|
|
5044
5616
|
ngAfterContentInit() {
|
|
5045
5617
|
this.initializeIslands();
|
|
5046
5618
|
}
|
|
5047
|
-
update() {
|
|
5048
|
-
this.effect = {};
|
|
5049
|
-
this.cdr.detectChanges();
|
|
5050
|
-
}
|
|
5051
5619
|
ngOnChanges(changes) {
|
|
5052
5620
|
if (this.hasInitialized) {
|
|
5053
5621
|
const valueChange = changes['plaitValue'];
|
|
@@ -5165,10 +5733,8 @@ class PlaitBoardComponent {
|
|
|
5165
5733
|
fromEvent(document, 'copy')
|
|
5166
5734
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
5167
5735
|
.subscribe((event) => {
|
|
5168
|
-
const selectedElements = getSelectedElements(this.board);
|
|
5169
5736
|
event.preventDefault();
|
|
5170
|
-
|
|
5171
|
-
this.board.setFragment(event.clipboardData, null, rectangle, 'copy');
|
|
5737
|
+
setFragment(this.board, 'copy', event.clipboardData);
|
|
5172
5738
|
});
|
|
5173
5739
|
fromEvent(document, 'paste')
|
|
5174
5740
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
@@ -5177,19 +5743,31 @@ class PlaitBoardComponent {
|
|
|
5177
5743
|
if (mousePoint) {
|
|
5178
5744
|
const targetPoint = toViewBoxPoint(this.board, toHostPoint(this.board, mousePoint[0], mousePoint[1]));
|
|
5179
5745
|
const clipboardData = await getClipboardData(clipboardEvent.clipboardData);
|
|
5180
|
-
this.board.insertFragment(
|
|
5746
|
+
this.board.insertFragment(clipboardData, targetPoint);
|
|
5181
5747
|
}
|
|
5182
5748
|
});
|
|
5183
5749
|
fromEvent(document, 'cut')
|
|
5184
5750
|
.pipe(takeUntil(this.destroy$), filter(() => this.isFocused && !PlaitBoard.isReadonly(this.board) && !PlaitBoard.hasBeenTextEditing(this.board)))
|
|
5185
5751
|
.subscribe((event) => {
|
|
5186
|
-
const selectedElements = getSelectedElements(this.board);
|
|
5187
5752
|
event.preventDefault();
|
|
5188
|
-
|
|
5189
|
-
this.board.setFragment(event.clipboardData, null, rectangle, 'cut');
|
|
5753
|
+
setFragment(this.board, 'cut', event.clipboardData);
|
|
5190
5754
|
deleteFragment(this.board);
|
|
5191
5755
|
});
|
|
5192
5756
|
}
|
|
5757
|
+
initializeListRender() {
|
|
5758
|
+
this.listRender = new ListRender(this.board, this.viewContainerRef);
|
|
5759
|
+
this.listRender.initialize(this.board.children, this.initializeChildrenContext());
|
|
5760
|
+
}
|
|
5761
|
+
updateListRender() {
|
|
5762
|
+
this.listRender.update(this.board.children, this.initializeChildrenContext());
|
|
5763
|
+
}
|
|
5764
|
+
initializeChildrenContext() {
|
|
5765
|
+
return {
|
|
5766
|
+
board: this.board,
|
|
5767
|
+
parent: this.board,
|
|
5768
|
+
parentG: PlaitBoard.getElementHost(this.board)
|
|
5769
|
+
};
|
|
5770
|
+
}
|
|
5193
5771
|
viewportScrollListener() {
|
|
5194
5772
|
fromEvent(this.viewportContainer.nativeElement, 'scroll')
|
|
5195
5773
|
.pipe(takeUntil(this.destroy$), filter(() => {
|
|
@@ -5287,10 +5865,9 @@ class PlaitBoardComponent {
|
|
|
5287
5865
|
<g class="element-upper-host"></g>
|
|
5288
5866
|
<g class="element-active-host"></g>
|
|
5289
5867
|
</svg>
|
|
5290
|
-
<plait-children [board]="board" [effect]="effect"></plait-children>
|
|
5291
5868
|
</div>
|
|
5292
5869
|
<ng-content></ng-content>
|
|
5293
|
-
`, isInline: true,
|
|
5870
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5294
5871
|
}
|
|
5295
5872
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImport: i0, type: PlaitBoardComponent, decorators: [{
|
|
5296
5873
|
type: Component,
|
|
@@ -5303,14 +5880,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.2.4", ngImpor
|
|
|
5303
5880
|
<g class="element-upper-host"></g>
|
|
5304
5881
|
<g class="element-active-host"></g>
|
|
5305
5882
|
</svg>
|
|
5306
|
-
<plait-children [board]="board" [effect]="effect"></plait-children>
|
|
5307
5883
|
</div>
|
|
5308
5884
|
<ng-content></ng-content>
|
|
5309
5885
|
`,
|
|
5310
5886
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
5311
5887
|
providers: [PlaitContextService],
|
|
5312
|
-
standalone: true
|
|
5313
|
-
imports: [PlaitChildrenElementComponent]
|
|
5888
|
+
standalone: true
|
|
5314
5889
|
}]
|
|
5315
5890
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }, { type: i0.ElementRef }, { type: i0.NgZone }], propDecorators: { plaitValue: [{
|
|
5316
5891
|
type: Input
|
|
@@ -5607,5 +6182,5 @@ const isDebug = (key) => {
|
|
|
5607
6182
|
* Generated bundle index. Do not edit.
|
|
5608
6183
|
*/
|
|
5609
6184
|
|
|
5610
|
-
export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DebugGenerator, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G,
|
|
6185
|
+
export { A, ACTIVE_MOVING_CLASS_NAME, ACTIVE_STROKE_WIDTH, ALT, APOSTROPHE, ATTACHED_ELEMENT_CLASS_NAME, AT_SIGN, B, BACKSLASH, BACKSPACE, BOARD_TO_AFTER_CHANGE, BOARD_TO_COMPONENT, BOARD_TO_ELEMENT_HOST, BOARD_TO_HOST, BOARD_TO_IS_SELECTION_MOVING, BOARD_TO_MOVING_ELEMENT, BOARD_TO_MOVING_POINT, BOARD_TO_MOVING_POINT_IN_BOARD, BOARD_TO_ON_CHANGE, BOARD_TO_ROUGH_SVG, BOARD_TO_SELECTED_ELEMENT, BOARD_TO_TEMPORARY_ELEMENTS, BOARD_TO_TOUCH_REF, BOARD_TO_VIEWPORT_ORIGINATION, BoardTransforms, C, CAPS_LOCK, CLOSE_SQUARE_BRACKET, COMMA, CONTEXT_MENU, CONTROL, ColorfulThemeColor, CoreTransforms, CursorClass, D, DASH, DELETE, DOWN_ARROW, DarkThemeColor, DebugGenerator, DefaultThemeColor, Direction, E, EIGHT, ELEMENT_TO_COMPONENT, END, ENTER, EQUALS, ESCAPE, F, F1, F10, F11, F12, F2, F3, F4, F5, F6, F7, F8, F9, FF_EQUALS, FF_MINUS, FF_MUTE, FF_SEMICOLON, FF_VOLUME_DOWN, FF_VOLUME_UP, FIRST_MEDIA, FIVE, FLUSHING, FOUR, G, H, HIT_DISTANCE_BUFFER, HOME, HOST_CLASS_NAME, I, INSERT, IS_APPLE, IS_BOARD_ALIVE, IS_BOARD_CACHE, IS_CHROME, IS_CHROME_LEGACY, IS_DRAGGING, IS_EDGE_LEGACY, IS_FIREFOX, IS_IOS, IS_MAC, IS_SAFARI, IS_TEXT_EDITABLE, J, K, L, LAST_MEDIA, LEFT_ARROW, M, MAC_ENTER, MAC_META, MAC_WK_CMD_LEFT, MAC_WK_CMD_RIGHT, MAX_RADIUS, MERGING, META, MUTE, N, NINE, NODE_TO_CONTAINER_G, NODE_TO_G, 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, PlaitContextService, PlaitElement, PlaitGroupElement, PlaitHistoryBoard, PlaitIslandBaseComponent, PlaitIslandPopoverBaseComponent, PlaitNode, PlaitOperation, PlaitPluginElementComponent, PlaitPluginKey, PlaitPointerType, Point, Q, QUESTION_MARK, R, RESIZE_CURSORS, RESIZE_HANDLE_CLASS_NAME, RIGHT_ARROW, ROTATE_HANDLE_CLASS_NAME, RectangleClient, ResizeCursorClass, RetroThemeColor, RgbaToHEX, S, SAVING, SCROLL_BAR_WIDTH, SCROLL_LOCK, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, SELECTION_RECTANGLE_CLASS_NAME, SEMICOLON, SEVEN, SHIFT, SINGLE_QUOTE, SIX, SLASH, SNAPPING_STROKE_WIDTH, SNAP_TOLERANCE, SPACE, Selection, SoftThemeColor, StarryThemeColor, T, TAB, THREE, TILDE, TWO, ThemeColorMode, ThemeColors, Transforms, U, UP_ARROW, V, VOLUME_DOWN, VOLUME_UP, Viewport, W, WritableClipboardType, X, Y, Z, ZERO, addClipboardContext, addSelectedElement, approximately, arrowPoints, buildPlaitHtml, cacheMovingElements, cacheSelectedElements, cacheSelectedElementsWithGroup, cacheSelectedElementsWithGroupOnShift, calcNewViewBox, canAddGroup, canRemoveGroup, canSetZIndex, catmullRomFitting, clampZoomLevel, clearNodeWeakMap, clearSelectedElement, clearSelectionMoving, clearViewportOrigination, createClipboardContext, createDebugGenerator, createFakeEvent, createForeignObject, createG, createGroup, createGroupRectangleG, createKeyboardEvent, createMask, createModModifierKeys, createMouseEvent, createPath, createPointerEvent, createRect, createSVG, createTestingBoard, createText, createTouchEvent, debounce, degreesToRadians, deleteFragment, deleteTemporaryElements, depthFirstRecursion, distanceBetweenPointAndPoint, distanceBetweenPointAndRectangle, distanceBetweenPointAndSegment, distanceBetweenPointAndSegments, downloadImage, drawArrow, drawBezierPath, drawCircle, drawDashedLines, drawEntireActiveRectangleG, drawLine, drawLinearPath, drawPointSnapLines, drawRectangle, drawRoundRectangle, drawSolidLines, duplicateElements, fakeNodeWeakMap, filterSelectedGroups, findElements, findIndex, findLastIndex, getAllElementsInGroup, getAllMoveOptions, getAngleBetweenPoints, getBarPoint, getBoardRectangle, getClipboardData, getClipboardFromHtml, getCrossingPointsBetweenEllipseAndSegment, getDataTransferClipboard, getDataTransferClipboardText, getEditingGroup, getElementById, getElementHostBBox, getElementsInGroup, getElementsInGroupByElement, getElementsIndices, getEllipseTangentSlope, getGroupByElement, getHighestGroup, getHighestIndexOfElement, getHighestSelectedElements, getHighestSelectedGroup, getHighestSelectedGroups, getHitElementByPoint, getHitElementsBySelection, getHitSelectedElements, getIsRecursionFunc, getMinPointDelta, getMovingElements, getNearestDelta, getNearestPointBetweenPointAndSegment, getNearestPointBetweenPointAndSegments, getNearestPointRectangle, getOffsetAfterRotate, getOneMoveOptions, getProbablySupportsClipboardRead, getProbablySupportsClipboardWrite, getProbablySupportsClipboardWriteText, getRealScrollBarWidth, getRectangleByAngle, getRectangleByElements, getRectangleByGroup, getRotatedBoundingRectangle, getSelectedElements, getSelectedGroups, getSelectedIsolatedElements, getSelectedIsolatedElementsCanAddToGroup, getSelectedTargetElements, getSelectionAngle, getSnapRectangles, getTemporaryElements, getTemporaryRef, getTripleAxis, getVectorFromPointAndSlope, getViewBox, getViewBoxCenterPoint, getViewportContainerRect, getViewportOrigination, handleTouchTarget, hasBeforeContextChange, hasInputOrTextareaTarget, hasOnBoardChange, hasOnContextChanged, hasSameAngle, hasSelectedElementsInSameGroup, hasValidAngle, hotkeys, idCreator, initializeViewBox, initializeViewportContainer, initializeViewportOffset, inverse, isAxisChangedByAngle, isContextmenu, isDOMElement, isDOMNode, isDebug, isDragging, isFromScrolling, isFromViewportChange, isHandleSelection, isInPlaitBoard, isIndicesContinuous, isLineHitLine, isMainPointer, isMovingElements, isNullOrUndefined, isPointInEllipse, isPointInPolygon, isPointInRoundRectangle, isPolylineHitRectangle, isPreventTouchMove, isSecondaryPointer, isSelectedAllElementsInGroup, isSelectedElement, isSelectedElementOrGroup, isSelectionMoving, isSetSelectionOperation, isSetViewportOperation, isSnapPoint, moveElementsToNewPath, moveElementsToNewPathAfterAddGroup, nonGroupInHighestSelectedElements, normalizeAngle, normalizePoint, preventTouchMove, radiansToDegrees, removeMovingElements, removeSelectedElement, rotate, rotateAntiPointsByElement, rotateElements, rotatePoints, rotatePointsByElement, rotatedDataPoints, scrollToRectangle, setAngleForG, setClipboardData, setDataTransferClipboard, setDataTransferClipboardText, setDragging, setFragment, setIsFromScrolling, setIsFromViewportChange, setPathStrokeLinecap, setSVGViewBox, setSelectedElementsWithGroup, setSelectionMoving, setStrokeLinecap, shouldClear, shouldMerge, shouldSave, sortElements, stripHtml, temporaryDisableSelection, throttleRAF, toDomPrecision, toFixed, toHostPoint, toHostPointFromViewBoxPoint, toImage, toScreenPointFromHostPoint, toViewBoxPoint, toViewBoxPoints, uniqueById, updateForeignObject, updateForeignObjectWidth, updatePoints, updateViewportByScrolling, updateViewportContainerScroll, updateViewportOffset, updateViewportOrigination, withArrowMoving, withMoving, withOptions, withSelection };
|
|
5611
6186
|
//# sourceMappingURL=plait-core.mjs.map
|