@plait/mind 0.2.0-next.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/README.md +24 -0
- package/constants/default.d.ts +12 -0
- package/constants/index.d.ts +2 -0
- package/constants/node.d.ts +17 -0
- package/draw/abstract.d.ts +4 -0
- package/draw/indented-link.d.ts +3 -0
- package/draw/link/abstract-link.d.ts +3 -0
- package/draw/link/logic-link.d.ts +3 -0
- package/draw/link.d.ts +3 -0
- package/draw/richtext.d.ts +14 -0
- package/draw/shape.d.ts +3 -0
- package/esm2020/constants/default.mjs +13 -0
- package/esm2020/constants/index.mjs +3 -0
- package/esm2020/constants/node.mjs +19 -0
- package/esm2020/draw/abstract.mjs +48 -0
- package/esm2020/draw/indented-link.mjs +44 -0
- package/esm2020/draw/link/abstract-link.mjs +42 -0
- package/esm2020/draw/link/logic-link.mjs +51 -0
- package/esm2020/draw/link.mjs +158 -0
- package/esm2020/draw/richtext.mjs +35 -0
- package/esm2020/draw/shape.mjs +18 -0
- package/esm2020/interfaces/abstract.mjs +6 -0
- package/esm2020/interfaces/element-data.mjs +2 -0
- package/esm2020/interfaces/element.mjs +59 -0
- package/esm2020/interfaces/index.mjs +6 -0
- package/esm2020/interfaces/layout.mjs +19 -0
- package/esm2020/interfaces/node.mjs +23 -0
- package/esm2020/interfaces/types.mjs +13 -0
- package/esm2020/layout-option.mjs +72 -0
- package/esm2020/mind.component.mjs +50 -0
- package/esm2020/mind.module.mjs +21 -0
- package/esm2020/node.component.mjs +742 -0
- package/esm2020/plait-mind.mjs +5 -0
- package/esm2020/plugins/emoji/emoji-base.component.mjs +21 -0
- package/esm2020/plugins/emoji/emoji.drawer.mjs +79 -0
- package/esm2020/plugins/emoji/emoji.mjs +15 -0
- package/esm2020/plugins/emoji/index.mjs +4 -0
- package/esm2020/plugins/emoji/with-mind-emoji.mjs +8 -0
- package/esm2020/plugins/with-abstract.mjs +92 -0
- package/esm2020/plugins/with-dnd.mjs +276 -0
- package/esm2020/plugins/with-mind.mjs +183 -0
- package/esm2020/public-api.mjs +14 -0
- package/esm2020/queries/get-available-sublayouts-by-element.mjs +29 -0
- package/esm2020/queries/get-branch-mindmap-layouts-by-element.mjs +18 -0
- package/esm2020/queries/get-correct-layout-by-element.mjs +50 -0
- package/esm2020/queries/get-layout-by-element.mjs +16 -0
- package/esm2020/queries/get-layout-parent-by-element.mjs +17 -0
- package/esm2020/queries/index.mjs +13 -0
- package/esm2020/transforms/index.mjs +10 -0
- package/esm2020/transforms/layout.mjs +23 -0
- package/esm2020/transforms/node.mjs +44 -0
- package/esm2020/utils/abstract/common.mjs +25 -0
- package/esm2020/utils/abstract/resize.mjs +169 -0
- package/esm2020/utils/clipboard.mjs +71 -0
- package/esm2020/utils/colors.mjs +41 -0
- package/esm2020/utils/direction-corrector.mjs +54 -0
- package/esm2020/utils/direction-detector.mjs +56 -0
- package/esm2020/utils/draw-placeholder.mjs +311 -0
- package/esm2020/utils/drop-target-corrector.mjs +86 -0
- package/esm2020/utils/graph.mjs +24 -0
- package/esm2020/utils/index.mjs +14 -0
- package/esm2020/utils/is-virtual-key.mjs +13 -0
- package/esm2020/utils/layout.mjs +105 -0
- package/esm2020/utils/mindmap.mjs +295 -0
- package/esm2020/utils/node-space.mjs +72 -0
- package/esm2020/utils/node.mjs +6 -0
- package/esm2020/utils/path.mjs +11 -0
- package/esm2020/utils/point-placement.mjs +99 -0
- package/esm2020/utils/shape.mjs +17 -0
- package/esm2020/utils/weak-maps.mjs +3 -0
- package/fesm2015/plait-mind.mjs +3583 -0
- package/fesm2015/plait-mind.mjs.map +1 -0
- package/fesm2020/plait-mind.mjs +3595 -0
- package/fesm2020/plait-mind.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/interfaces/abstract.d.ts +4 -0
- package/interfaces/element-data.d.ts +11 -0
- package/interfaces/element.d.ts +39 -0
- package/interfaces/index.d.ts +5 -0
- package/interfaces/layout.d.ts +10 -0
- package/interfaces/node.d.ts +33 -0
- package/interfaces/types.d.ts +11 -0
- package/layout-option.d.ts +2 -0
- package/mind.component.d.ts +16 -0
- package/mind.module.d.ts +11 -0
- package/node.component.d.ts +60 -0
- package/package.json +31 -0
- package/plugins/emoji/emoji-base.component.d.ts +12 -0
- package/plugins/emoji/emoji.d.ts +6 -0
- package/plugins/emoji/emoji.drawer.d.ts +22 -0
- package/plugins/emoji/index.d.ts +3 -0
- package/plugins/emoji/with-mind-emoji.d.ts +8 -0
- package/plugins/with-abstract.d.ts +2 -0
- package/plugins/with-dnd.d.ts +11 -0
- package/plugins/with-mind.d.ts +2 -0
- package/public-api.d.ts +10 -0
- package/queries/get-available-sublayouts-by-element.d.ts +8 -0
- package/queries/get-branch-mindmap-layouts-by-element.d.ts +3 -0
- package/queries/get-correct-layout-by-element.d.ts +9 -0
- package/queries/get-layout-by-element.d.ts +3 -0
- package/queries/get-layout-parent-by-element.d.ts +8 -0
- package/queries/index.d.ts +7 -0
- package/styles/styles.scss +99 -0
- package/transforms/index.d.ts +7 -0
- package/transforms/layout.d.ts +3 -0
- package/transforms/node.d.ts +8 -0
- package/utils/abstract/common.d.ts +5 -0
- package/utils/abstract/resize.d.ts +21 -0
- package/utils/clipboard.d.ts +7 -0
- package/utils/colors.d.ts +4 -0
- package/utils/direction-corrector.d.ts +3 -0
- package/utils/direction-detector.d.ts +9 -0
- package/utils/draw-placeholder.d.ts +42 -0
- package/utils/drop-target-corrector.d.ts +8 -0
- package/utils/graph.d.ts +5 -0
- package/utils/index.d.ts +13 -0
- package/utils/is-virtual-key.d.ts +1 -0
- package/utils/layout.d.ts +12 -0
- package/utils/mindmap.d.ts +33 -0
- package/utils/node-space.d.ts +9 -0
- package/utils/node.d.ts +2 -0
- package/utils/path.d.ts +4 -0
- package/utils/point-placement.d.ts +8 -0
- package/utils/shape.d.ts +3 -0
- package/utils/weak-maps.d.ts +5 -0
|
@@ -0,0 +1,3595 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Component, ChangeDetectionStrategy, NgModule, Directive, Input } from '@angular/core';
|
|
3
|
+
import * as i2 from '@plait/core';
|
|
4
|
+
import { distanceBetweenPointAndRectangle, drawRoundRectangle, getRectangleByElements, RectangleClient, PlaitBoard, PlaitNode, NODE_TO_PARENT, ELEMENT_TO_COMPONENT, idCreator, Transforms, isNullOrUndefined, addSelectedElement, createG, drawAbstractRoundRectangle, PlaitPluginElementComponent, PlaitPointerType, NODE_TO_INDEX, PlaitElement, createText, IS_TEXT_EDITABLE, MERGING, transformPoint, toPoint, depthFirstRecursion, PlaitModule, getSelectedElements, distanceBetweenPointAndPoint, Path, CLIP_BOARD_FORMAT_KEY, BOARD_TO_HOST, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
|
|
5
|
+
import { isIndentedLayout, MindmapLayoutType, isStandardLayout, isTopLayout, isLeftLayout, isHorizontalLogicLayout, isVerticalLogicLayout, isBottomLayout, isRightLayout, isHorizontalLayout, AbstractNode, getCorrectStartEnd, isChildOfAbstract, getAbstractLayout, ConnectingPosition, GlobalLayout } from '@plait/layouts';
|
|
6
|
+
import { getSizeByText, ROOT_DEFAULT_HEIGHT, TEXT_DEFAULT_HEIGHT, drawRichtext, updateForeignObject, createForeignObject, updateRichText, setFullSelectionAndFocus, getRichtextContentSize, hasEditableTarget, RichtextModule } from '@plait/richtext';
|
|
7
|
+
import { Subject, fromEvent, timer } from 'rxjs';
|
|
8
|
+
import { takeUntil, filter, take, debounceTime } from 'rxjs/operators';
|
|
9
|
+
import { Node, Editor, Operation, Path as Path$1 } from 'slate';
|
|
10
|
+
import { pointsOnBezierCurves } from 'points-on-curve';
|
|
11
|
+
import { isKeyHotkey } from 'is-hotkey';
|
|
12
|
+
import * as i1 from '@angular/common';
|
|
13
|
+
import { BrowserModule } from '@angular/platform-browser';
|
|
14
|
+
|
|
15
|
+
const MINDMAP_ELEMENT_TO_COMPONENT = new WeakMap();
|
|
16
|
+
const ELEMENT_TO_NODE = new WeakMap();
|
|
17
|
+
|
|
18
|
+
const BASE = 4;
|
|
19
|
+
const PRIMARY_COLOR = '#6698FF';
|
|
20
|
+
const MINDMAP_KEY = 'plait-mindmap';
|
|
21
|
+
const MAX_RADIUS = BASE * 4;
|
|
22
|
+
const TRANSPARENT = 'transparent';
|
|
23
|
+
const GRAY_COLOR = '#AAAAAA';
|
|
24
|
+
const STROKE_WIDTH = 3;
|
|
25
|
+
const EXTEND_OFFSET = 8;
|
|
26
|
+
const EXTEND_RADIUS = 16;
|
|
27
|
+
const QUICK_INSERT_CIRCLE_OFFSET = 9;
|
|
28
|
+
const QUICK_INSERT_CIRCLE_COLOR = '#6698FF';
|
|
29
|
+
const QUICK_INSERT_INNER_CROSS_COLOR = 'white';
|
|
30
|
+
|
|
31
|
+
const TOPIC_COLOR = '#333';
|
|
32
|
+
const TOPIC_FONT_SIZE = 14;
|
|
33
|
+
const NODE_FILL = '#FFFFFF';
|
|
34
|
+
const ROOT_NODE_FILL = '#F5F5F5';
|
|
35
|
+
const ROOT_NODE_STROKE = '#F5F5F5';
|
|
36
|
+
const ROOT_TOPIC_FONT_SIZE = 18;
|
|
37
|
+
const NODE_MIN_WIDTH = 18;
|
|
38
|
+
const COLORS = ['#A287E1', '#6F81DB', '#6EC4C4', '#DFB85D', '#B1C774', '#77C386', '#C28976', '#E48484', '#E482D4', '#69B1E4'];
|
|
39
|
+
var MindmapNodeShape;
|
|
40
|
+
(function (MindmapNodeShape) {
|
|
41
|
+
MindmapNodeShape["roundRectangle"] = "round-rectangle";
|
|
42
|
+
MindmapNodeShape["underline"] = "underline";
|
|
43
|
+
})(MindmapNodeShape || (MindmapNodeShape = {}));
|
|
44
|
+
const ABSTRACT_HANDLE_COLOR = '#6698FF80'; //PRIMARY_COLOR 50% 透明度
|
|
45
|
+
const ABSTRACT_INCLUDED_OUTLINE_OFFSET = 3.5;
|
|
46
|
+
const ABSTRACT_HANDLE_LENGTH = 12;
|
|
47
|
+
const TOPIC_DEFAULT_MAX_WORD_COUNT = 34;
|
|
48
|
+
const ABSTRACT_HANDLE_MASK_WIDTH = 8;
|
|
49
|
+
|
|
50
|
+
function getRectangleByNode(node) {
|
|
51
|
+
const x = node.x + node.hGap;
|
|
52
|
+
let y = node.y + node.vGap;
|
|
53
|
+
const width = node.width - node.hGap * 2;
|
|
54
|
+
const height = node.height - node.vGap * 2;
|
|
55
|
+
return {
|
|
56
|
+
x,
|
|
57
|
+
y,
|
|
58
|
+
width,
|
|
59
|
+
height
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function hitMindmapElement(board, point, element) {
|
|
63
|
+
const node = ELEMENT_TO_NODE.get(element);
|
|
64
|
+
if (node && distanceBetweenPointAndRectangle(point[0], point[1], getRectangleByNode(node)) === 0) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const getNodeShapeByElement = (element) => {
|
|
73
|
+
let nodeShape = element.shape;
|
|
74
|
+
if (nodeShape) {
|
|
75
|
+
return nodeShape;
|
|
76
|
+
}
|
|
77
|
+
let parent = findParentElement(element);
|
|
78
|
+
while (parent) {
|
|
79
|
+
if (parent.shape) {
|
|
80
|
+
return parent.shape;
|
|
81
|
+
}
|
|
82
|
+
parent = findParentElement(parent);
|
|
83
|
+
}
|
|
84
|
+
return MindmapNodeShape.roundRectangle;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function isVirtualKey(e) {
|
|
88
|
+
const isMod = e.ctrlKey || e.metaKey;
|
|
89
|
+
const isAlt = isKeyHotkey('alt', e);
|
|
90
|
+
const isShift = isKeyHotkey('shift', e);
|
|
91
|
+
const isCapsLock = e.key.includes('CapsLock');
|
|
92
|
+
const isTab = e.key.includes('Tab');
|
|
93
|
+
const isEsc = e.key.includes('Escape');
|
|
94
|
+
const isF = e.key.startsWith('F');
|
|
95
|
+
const isArrow = e.key.includes('Arrow') ? true : false;
|
|
96
|
+
return isCapsLock || isMod || isAlt || isArrow || isShift || isTab || isEsc || isF;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function drawLink(roughSVG, node, child, defaultStroke = null, isHorizontal = true, needDrawUnderline = true) {
|
|
100
|
+
let beginX, beginY, endX, endY, beginNode = node, endNode = child;
|
|
101
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(node.origin);
|
|
102
|
+
if (isHorizontal) {
|
|
103
|
+
if (!isChildRight(node, child)) {
|
|
104
|
+
beginNode = child;
|
|
105
|
+
endNode = node;
|
|
106
|
+
}
|
|
107
|
+
beginX = beginNode.x + beginNode.width - beginNode.hGap;
|
|
108
|
+
beginY = beginNode.y + beginNode.height / 2;
|
|
109
|
+
endX = endNode.x + endNode.hGap;
|
|
110
|
+
endY = endNode.y + endNode.height / 2;
|
|
111
|
+
if (node.parent &&
|
|
112
|
+
isIndentedLayout(MindmapQueries.getLayoutByElement(node.parent?.origin)) &&
|
|
113
|
+
getNodeShapeByElement(node.origin) === MindmapNodeShape.underline) {
|
|
114
|
+
if (isChildRight(node, child)) {
|
|
115
|
+
beginY = node.y + node.height - node.vGap;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
endY = node.y + node.height - node.vGap;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
if (node.y > child.y) {
|
|
124
|
+
beginNode = child;
|
|
125
|
+
endNode = node;
|
|
126
|
+
}
|
|
127
|
+
beginX = beginNode.x + beginNode.width / 2;
|
|
128
|
+
beginY = beginNode.y + beginNode.height - beginNode.vGap;
|
|
129
|
+
endX = endNode.x + endNode.width / 2;
|
|
130
|
+
endY = endNode.y + endNode.vGap;
|
|
131
|
+
}
|
|
132
|
+
const stroke = defaultStroke || getLinkLineColorByMindmapElement(child.origin);
|
|
133
|
+
const strokeWidth = child.origin.linkLineWidth ? child.origin.linkLineWidth : STROKE_WIDTH;
|
|
134
|
+
if (endNode.origin.isRoot) {
|
|
135
|
+
if (layout === MindmapLayoutType.left || isStandardLayout(layout)) {
|
|
136
|
+
endX -= strokeWidth;
|
|
137
|
+
}
|
|
138
|
+
if (layout === MindmapLayoutType.upward) {
|
|
139
|
+
endY -= strokeWidth;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (beginNode.origin.isRoot) {
|
|
143
|
+
if (layout === MindmapLayoutType.right || isStandardLayout(layout)) {
|
|
144
|
+
beginX += strokeWidth;
|
|
145
|
+
}
|
|
146
|
+
if (layout === MindmapLayoutType.downward) {
|
|
147
|
+
beginY += strokeWidth;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (isHorizontal) {
|
|
151
|
+
let curve = [
|
|
152
|
+
[beginX, beginY],
|
|
153
|
+
[beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
|
|
154
|
+
[endX - (beginNode.hGap + endNode.hGap) / 2, endY],
|
|
155
|
+
[endX, endY]
|
|
156
|
+
];
|
|
157
|
+
const shape = getNodeShapeByElement(child.origin);
|
|
158
|
+
if (!node.origin.isRoot) {
|
|
159
|
+
if (node.x > child.x) {
|
|
160
|
+
curve = [
|
|
161
|
+
[beginX, beginY],
|
|
162
|
+
[beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
|
|
163
|
+
[endX - (beginNode.hGap + endNode.hGap) / 2, endY],
|
|
164
|
+
[endX - 12, endY]
|
|
165
|
+
];
|
|
166
|
+
const line = [
|
|
167
|
+
[endX - 12, endY],
|
|
168
|
+
[endX - 12, endY],
|
|
169
|
+
[endX, endY]
|
|
170
|
+
];
|
|
171
|
+
curve = [...curve, ...line];
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
curve = [
|
|
175
|
+
[beginX + 12, beginY],
|
|
176
|
+
[beginX + (beginNode.hGap + endNode.hGap) / 2, beginY],
|
|
177
|
+
[endX - (beginNode.hGap + endNode.hGap) / 3, endY],
|
|
178
|
+
[endX, endY]
|
|
179
|
+
];
|
|
180
|
+
const line = [
|
|
181
|
+
[beginX, beginY],
|
|
182
|
+
[beginX + 12, beginY],
|
|
183
|
+
[beginX + 12, beginY]
|
|
184
|
+
];
|
|
185
|
+
curve = [...line, ...curve];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (needDrawUnderline && shape === MindmapNodeShape.underline) {
|
|
189
|
+
if (child.left) {
|
|
190
|
+
const underline = [
|
|
191
|
+
[beginX - (beginNode.width - beginNode.hGap * 2), beginY],
|
|
192
|
+
[beginX - (beginNode.width - beginNode.hGap * 2), beginY],
|
|
193
|
+
[beginX - (beginNode.width - beginNode.hGap * 2), beginY]
|
|
194
|
+
];
|
|
195
|
+
curve = [...underline, ...curve];
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const underline = [
|
|
199
|
+
[endX + (endNode.width - endNode.hGap * 2), endY],
|
|
200
|
+
[endX + (endNode.width - endNode.hGap * 2), endY],
|
|
201
|
+
[endX + (endNode.width - endNode.hGap * 2), endY]
|
|
202
|
+
];
|
|
203
|
+
curve = [...curve, ...underline];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const points = pointsOnBezierCurves(curve);
|
|
207
|
+
return roughSVG.curve(points, { stroke, strokeWidth });
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
let curve = [
|
|
211
|
+
[beginX, beginY],
|
|
212
|
+
[beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
|
|
213
|
+
[endX, endY - (beginNode.vGap + endNode.vGap) / 2],
|
|
214
|
+
[endX, endY]
|
|
215
|
+
];
|
|
216
|
+
if (!node.origin.isRoot) {
|
|
217
|
+
if (isTopLayout(layout)) {
|
|
218
|
+
curve = [
|
|
219
|
+
[beginX, beginY],
|
|
220
|
+
[beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
|
|
221
|
+
[endX, endY - (beginNode.vGap + endNode.vGap) / 2],
|
|
222
|
+
[endX, endY - 12]
|
|
223
|
+
];
|
|
224
|
+
const line = [
|
|
225
|
+
[endX, endY - 12],
|
|
226
|
+
[endX, endY - 12],
|
|
227
|
+
[endX, endY]
|
|
228
|
+
];
|
|
229
|
+
curve = [...curve, ...line];
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
curve = [
|
|
233
|
+
[beginX, beginY + 12],
|
|
234
|
+
[beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
|
|
235
|
+
[endX, endY - (beginNode.vGap + endNode.vGap) / 2],
|
|
236
|
+
[endX, endY]
|
|
237
|
+
];
|
|
238
|
+
const line = [
|
|
239
|
+
[beginX, beginY],
|
|
240
|
+
[beginX, beginY + 12],
|
|
241
|
+
[beginX, beginY + 12]
|
|
242
|
+
];
|
|
243
|
+
curve = [...line, ...curve];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const points = pointsOnBezierCurves(curve);
|
|
247
|
+
return roughSVG.curve(points, { stroke, strokeWidth });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const drawPlaceholderDropNodeG = (dropTarget, roughSVG, fakeDropNodeG) => {
|
|
252
|
+
const targetComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(dropTarget.target);
|
|
253
|
+
const targetRect = getRectangleByNode(targetComponent.node);
|
|
254
|
+
if (dropTarget.detectResult && ['right', 'left'].includes(dropTarget.detectResult)) {
|
|
255
|
+
drawStraightDropNodeG(targetRect, dropTarget.detectResult, targetComponent, roughSVG, fakeDropNodeG);
|
|
256
|
+
}
|
|
257
|
+
if (targetComponent.parent && dropTarget.detectResult && ['top', 'bottom'].includes(dropTarget.detectResult)) {
|
|
258
|
+
const parentComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(targetComponent.parent.origin);
|
|
259
|
+
const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
|
|
260
|
+
drawCurvePlaceholderDropNodeG(targetRect, dropTarget.detectResult, targetIndex, targetComponent, roughSVG, parentComponent, fakeDropNodeG);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const drawCurvePlaceholderDropNodeG = (targetRect, detectResult, targetIndex, targetComponent, roughSVG, parentComponent, fakeDropNodeG) => {
|
|
264
|
+
const parentNodeLayout = MindmapQueries.getCorrectLayoutByElement(parentComponent.node.origin);
|
|
265
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.parent.origin);
|
|
266
|
+
const strokeWidth = targetComponent.node.origin.linkLineWidth ? targetComponent.node.origin.linkLineWidth : STROKE_WIDTH;
|
|
267
|
+
let fakeX = targetComponent.node.x, fakeY = targetRect.y - 30, fakeRectangleStartX = targetRect.x, fakeRectangleEndX = targetRect.x + 30, fakeRectangleStartY = fakeY, fakeRectangleEndY = fakeRectangleStartY + 12, width = 30;
|
|
268
|
+
if (isLeftLayout(layout)) {
|
|
269
|
+
fakeX = targetComponent.node.x + targetComponent.node.width - 30;
|
|
270
|
+
fakeRectangleStartX = targetRect.x + targetRect.width - 30;
|
|
271
|
+
fakeRectangleEndX = targetRect.x + targetRect.width;
|
|
272
|
+
}
|
|
273
|
+
if (isHorizontalLogicLayout(parentNodeLayout)) {
|
|
274
|
+
fakeY = getHorizontalFakeY(detectResult, targetIndex, parentComponent.node, targetRect, layout, fakeY);
|
|
275
|
+
if (isStandardLayout(parentNodeLayout)) {
|
|
276
|
+
const rightNodeCount = parentComponent.node.origin.rightNodeCount || 0;
|
|
277
|
+
const idx = parentComponent.node.children.findIndex(x => x === targetComponent.node);
|
|
278
|
+
const isLeft = idx >= rightNodeCount;
|
|
279
|
+
// 标准布局的左,需要调整 x
|
|
280
|
+
if (isLeft) {
|
|
281
|
+
fakeX = targetComponent.node.x + targetComponent.node.width - 30;
|
|
282
|
+
fakeRectangleStartX = targetRect.x + targetRect.width - 30;
|
|
283
|
+
fakeRectangleEndX = targetRect.x + targetRect.width;
|
|
284
|
+
}
|
|
285
|
+
const isLeftFirst = idx === rightNodeCount;
|
|
286
|
+
const isRightLast = idx === rightNodeCount - 1;
|
|
287
|
+
// 拖拽至左第一个节点的情况
|
|
288
|
+
if (detectResult === 'top' && isLeftFirst) {
|
|
289
|
+
fakeY = targetRect.y - targetRect.height;
|
|
290
|
+
}
|
|
291
|
+
if (detectResult === 'bottom' && isRightLast) {
|
|
292
|
+
fakeY = targetRect.y + targetRect.height + 30;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
fakeRectangleStartY = fakeY;
|
|
296
|
+
fakeRectangleEndY = fakeRectangleStartY + 12;
|
|
297
|
+
}
|
|
298
|
+
if (isVerticalLogicLayout(layout)) {
|
|
299
|
+
parentComponent = targetComponent;
|
|
300
|
+
targetComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(targetComponent.parent.origin);
|
|
301
|
+
fakeX = parentComponent.node.x;
|
|
302
|
+
width = parentComponent.node.width;
|
|
303
|
+
const vGap = BASE * 6 + strokeWidth;
|
|
304
|
+
if (isTopLayout(layout) && detectResult === 'top') {
|
|
305
|
+
fakeY = targetRect.y - vGap;
|
|
306
|
+
fakeRectangleStartY = fakeY - vGap + strokeWidth;
|
|
307
|
+
}
|
|
308
|
+
if (isBottomLayout(layout) && detectResult === 'bottom') {
|
|
309
|
+
fakeY = targetRect.y + targetRect.height + vGap;
|
|
310
|
+
fakeRectangleStartY = fakeY + vGap - strokeWidth;
|
|
311
|
+
}
|
|
312
|
+
fakeRectangleStartX = fakeX + Math.ceil(parentComponent.node.width / 2) - parentComponent.node.hGap - Math.ceil(strokeWidth / 2);
|
|
313
|
+
fakeRectangleEndX = fakeRectangleStartX + 30;
|
|
314
|
+
fakeRectangleEndY = fakeRectangleStartY + 12;
|
|
315
|
+
}
|
|
316
|
+
if (isIndentedLayout(layout)) {
|
|
317
|
+
// 偏移一个 Gap
|
|
318
|
+
if (isLeftLayout(layout)) {
|
|
319
|
+
fakeX -= BASE * 4;
|
|
320
|
+
}
|
|
321
|
+
if (isRightLayout(layout)) {
|
|
322
|
+
fakeX += BASE * 4;
|
|
323
|
+
}
|
|
324
|
+
if (isTopLayout(layout)) {
|
|
325
|
+
if (detectResult === 'top') {
|
|
326
|
+
const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
|
|
327
|
+
if (isLastNode) {
|
|
328
|
+
fakeY = targetRect.y - targetRect.height - BASE;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
const nextComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentComponent.node.origin.children[targetIndex + 1]);
|
|
332
|
+
const nextRect = getRectangleByNode(nextComponent.node);
|
|
333
|
+
fakeY = targetRect.y - Math.abs((nextRect.y + nextRect.height - targetRect.y) / 2);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (detectResult === 'bottom') {
|
|
337
|
+
const isFirstNode = targetIndex === 0;
|
|
338
|
+
if (isFirstNode) {
|
|
339
|
+
const parentRect = getRectangleByNode(parentComponent.node);
|
|
340
|
+
fakeY = parentRect.y - parentRect.height / 2 - BASE;
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
const previousComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentComponent.node.origin.children[targetIndex - 1]);
|
|
344
|
+
const previousRect = getRectangleByNode(previousComponent.node);
|
|
345
|
+
fakeY = previousRect.y - Math.abs((targetRect.y + targetRect.height - previousRect.y) / 2);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
fakeRectangleStartX = fakeX;
|
|
350
|
+
fakeRectangleEndX = fakeRectangleStartX + 30;
|
|
351
|
+
fakeRectangleStartY = fakeY;
|
|
352
|
+
fakeRectangleEndY = fakeRectangleStartY + 12;
|
|
353
|
+
}
|
|
354
|
+
// 构造一条曲线
|
|
355
|
+
const fakeNode = { ...targetComponent.node, x: fakeX, y: fakeY, width, height: 12 };
|
|
356
|
+
const linkSVGG = isIndentedLayout(layout)
|
|
357
|
+
? drawIndentedLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, false)
|
|
358
|
+
: drawLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, isHorizontalLayout(layout), false);
|
|
359
|
+
// 构造一个矩形框坐标
|
|
360
|
+
const fakeRectangleG = drawRoundRectangle(roughSVG, fakeRectangleStartX, fakeRectangleStartY, fakeRectangleEndX, fakeRectangleEndY, {
|
|
361
|
+
stroke: PRIMARY_COLOR,
|
|
362
|
+
strokeWidth: 2,
|
|
363
|
+
fill: PRIMARY_COLOR,
|
|
364
|
+
fillStyle: 'solid'
|
|
365
|
+
});
|
|
366
|
+
fakeDropNodeG?.appendChild(linkSVGG);
|
|
367
|
+
fakeDropNodeG?.appendChild(fakeRectangleG);
|
|
368
|
+
};
|
|
369
|
+
const drawStraightDropNodeG = (targetRect, detectResult, targetComponent, roughSVG, fakeDropNodeG) => {
|
|
370
|
+
const { x, y, width, height } = targetRect;
|
|
371
|
+
const lineLength = 40;
|
|
372
|
+
let startLinePoint = x + width;
|
|
373
|
+
let endLinePoint = x + width + lineLength;
|
|
374
|
+
let startRectanglePointX = x + width + lineLength;
|
|
375
|
+
let endRectanglePointX = x + lineLength + width + 30;
|
|
376
|
+
let startRectanglePointY = y + height / 2 - 6;
|
|
377
|
+
let endRectanglePointY = y + height / 2 - 6 + 12;
|
|
378
|
+
if (detectResult === 'left') {
|
|
379
|
+
startLinePoint = x - lineLength;
|
|
380
|
+
endLinePoint = x;
|
|
381
|
+
startRectanglePointX = x - lineLength - 30;
|
|
382
|
+
endRectanglePointX = x - lineLength;
|
|
383
|
+
}
|
|
384
|
+
let fakeY = targetComponent.node.y;
|
|
385
|
+
let fakeX = targetRect.x;
|
|
386
|
+
const strokeWidth = targetComponent.node.origin.linkLineWidth ? targetComponent.node.origin.linkLineWidth : STROKE_WIDTH;
|
|
387
|
+
const pointOptions = {
|
|
388
|
+
fakeX,
|
|
389
|
+
fakeY,
|
|
390
|
+
x,
|
|
391
|
+
y,
|
|
392
|
+
width,
|
|
393
|
+
height,
|
|
394
|
+
strokeWidth
|
|
395
|
+
};
|
|
396
|
+
const parentLayout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
|
|
397
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin);
|
|
398
|
+
if (!isMixedLayout(parentLayout, layout)) {
|
|
399
|
+
// 构造一条直线
|
|
400
|
+
let linePoints = [
|
|
401
|
+
[startLinePoint, y + height / 2],
|
|
402
|
+
[endLinePoint, y + height / 2]
|
|
403
|
+
];
|
|
404
|
+
if (isIndentedLayout(parentLayout)) {
|
|
405
|
+
const fakePoint = getIndentedFakePoint(parentLayout, pointOptions);
|
|
406
|
+
drawIndentNodeG(fakeDropNodeG, roughSVG, fakePoint, targetComponent.node);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
else if (isVerticalLogicLayout(parentLayout)) {
|
|
410
|
+
if (!targetComponent.node.origin.isRoot) {
|
|
411
|
+
/**
|
|
412
|
+
* 计算逻辑:
|
|
413
|
+
* 1. 移动到左侧:当前节点 startX - 偏移值,偏移值计算如下:
|
|
414
|
+
* a. 第一个节点: 固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
|
|
415
|
+
* b. 第二个节点到最后一个节点之间:上一个节点到当前节点间距的一半((当前节点 startX - 上一个节点的 endX) / 2),endX = 当前节点的 startX + width;
|
|
416
|
+
* 2. 移动到右侧:当前节点 x + width + 偏移值,偏移值计算如下:
|
|
417
|
+
* a. 第二个节点到最后一个节点之间的右侧:当前节点到下一个节点间距的一半((下一个节点 startX - 当前节点的 endX) / 2),endX = 当前节点的 startX + width;
|
|
418
|
+
* b. 最后一个节点的右侧:固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
|
|
419
|
+
*/
|
|
420
|
+
fakeY = targetComponent.node.y;
|
|
421
|
+
const parentComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(targetComponent.parent.origin);
|
|
422
|
+
const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
|
|
423
|
+
if (detectResult === 'left') {
|
|
424
|
+
let offsetX = 0;
|
|
425
|
+
const isFirstNode = targetIndex === 0;
|
|
426
|
+
if (isFirstNode) {
|
|
427
|
+
offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
const previousComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentComponent.node.origin.children[targetIndex - 1]);
|
|
431
|
+
const previousRect = getRectangleByNode(previousComponent.node);
|
|
432
|
+
const space = targetRect.x - (previousRect.x + previousRect.width);
|
|
433
|
+
offsetX = space / 2;
|
|
434
|
+
}
|
|
435
|
+
fakeX = targetRect.x - offsetX - width / 2 - Math.ceil(strokeWidth / 2);
|
|
436
|
+
}
|
|
437
|
+
if (detectResult === 'right') {
|
|
438
|
+
let offsetX = 0;
|
|
439
|
+
const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
|
|
440
|
+
if (isLastNode) {
|
|
441
|
+
offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
const nextComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentComponent.node.origin.children[targetIndex + 1]);
|
|
445
|
+
const nextRect = getRectangleByNode(nextComponent.node);
|
|
446
|
+
const space = nextRect.x - (targetRect.x + targetRect.width);
|
|
447
|
+
offsetX = space / 2;
|
|
448
|
+
}
|
|
449
|
+
fakeX = targetRect.x + width + offsetX - width / 2 - Math.ceil(strokeWidth / 2);
|
|
450
|
+
}
|
|
451
|
+
startRectanglePointX = fakeX;
|
|
452
|
+
if (isTopLayout(parentLayout)) {
|
|
453
|
+
// 因为矩形是从左上角为起点向下画的,所以需要向上偏移一个矩形的高度(-12)
|
|
454
|
+
startRectanglePointY = fakeY + height + targetComponent.node.vGap - 12;
|
|
455
|
+
}
|
|
456
|
+
if (isBottomLayout(parentLayout)) {
|
|
457
|
+
startRectanglePointY = fakeY + targetComponent.node.vGap;
|
|
458
|
+
}
|
|
459
|
+
endRectanglePointX = startRectanglePointX + 30;
|
|
460
|
+
endRectanglePointY = startRectanglePointY + 12;
|
|
461
|
+
const fakeNode = { ...targetComponent.node, x: fakeX, y: fakeY, width: 30 };
|
|
462
|
+
const linkSVGG = drawLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, false, false);
|
|
463
|
+
fakeDropNodeG?.appendChild(linkSVGG);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
let linkSVGG = roughSVG.linearPath(linePoints, { stroke: PRIMARY_COLOR, strokeWidth });
|
|
468
|
+
fakeDropNodeG?.appendChild(linkSVGG);
|
|
469
|
+
}
|
|
470
|
+
// 构造一个矩形框坐标
|
|
471
|
+
let fakeRectangleG = drawRoundRectangle(roughSVG, startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
|
|
472
|
+
stroke: PRIMARY_COLOR,
|
|
473
|
+
strokeWidth: 2,
|
|
474
|
+
fill: PRIMARY_COLOR,
|
|
475
|
+
fillStyle: 'solid'
|
|
476
|
+
});
|
|
477
|
+
fakeDropNodeG?.appendChild(fakeRectangleG);
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
// 混合布局画线逻辑
|
|
481
|
+
if (isHorizontalLogicLayout(parentLayout)) {
|
|
482
|
+
if (isIndentedLayout(layout)) {
|
|
483
|
+
const fakePoint = getIndentedFakePoint(layout, pointOptions);
|
|
484
|
+
drawIndentNodeG(fakeDropNodeG, roughSVG, fakePoint, targetComponent.node);
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
const getHorizontalFakeY = (detectResult, targetIndex, parentNode, targetRect, layout, fakeY) => {
|
|
491
|
+
if (detectResult === 'top') {
|
|
492
|
+
if (targetIndex === 0 && isTopLayout(layout)) {
|
|
493
|
+
fakeY = targetRect.y + targetRect.height;
|
|
494
|
+
}
|
|
495
|
+
if (targetIndex > 0) {
|
|
496
|
+
const previousComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentNode.origin.children[targetIndex - 1]);
|
|
497
|
+
const previousRect = getRectangleByNode(previousComponent.node);
|
|
498
|
+
const topY = previousRect.y + previousRect.height;
|
|
499
|
+
fakeY = topY + (targetRect.y - topY) / 5;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (detectResult === 'bottom') {
|
|
503
|
+
fakeY = targetRect.y + targetRect.height + 30;
|
|
504
|
+
if (targetIndex < parentNode.origin.children.length - 1) {
|
|
505
|
+
const nextComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parentNode.origin.children[targetIndex + 1]);
|
|
506
|
+
const nextRect = getRectangleByNode(nextComponent.node);
|
|
507
|
+
const topY = targetRect.y + targetRect.height;
|
|
508
|
+
fakeY = topY + (nextRect.y - topY) / 5;
|
|
509
|
+
}
|
|
510
|
+
if (targetIndex === parentNode.origin.children.length - 1) {
|
|
511
|
+
fakeY = targetRect.y + targetRect.height + 30;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return fakeY;
|
|
515
|
+
};
|
|
516
|
+
const getIndentedFakePoint = (layout, pointOptions) => {
|
|
517
|
+
let { fakeX, fakeY, x, y, width, height, strokeWidth } = pointOptions;
|
|
518
|
+
const hGap = BASE * 4;
|
|
519
|
+
const vGap = BASE * 6;
|
|
520
|
+
const offsetX = hGap + width / 2 + strokeWidth;
|
|
521
|
+
const offsetY = vGap + height / 2 + strokeWidth;
|
|
522
|
+
if (isLeftLayout(layout)) {
|
|
523
|
+
fakeX = x - offsetX;
|
|
524
|
+
}
|
|
525
|
+
if (isRightLayout(layout)) {
|
|
526
|
+
fakeX = x + offsetX;
|
|
527
|
+
}
|
|
528
|
+
if (isTopLayout(layout)) {
|
|
529
|
+
fakeY = y - offsetY;
|
|
530
|
+
}
|
|
531
|
+
if (isBottomLayout(layout)) {
|
|
532
|
+
fakeY = y + height + offsetY;
|
|
533
|
+
}
|
|
534
|
+
return { fakeX, fakeY };
|
|
535
|
+
};
|
|
536
|
+
const drawIndentNodeG = (fakeDropNodeG, roughSVG, fakePoint, node) => {
|
|
537
|
+
const { fakeX, fakeY } = fakePoint;
|
|
538
|
+
const fakeNode = { ...node, x: fakeX, y: fakeY, width: 30, height: 12 };
|
|
539
|
+
const linkSVGG = drawIndentedLink(roughSVG, node, fakeNode, PRIMARY_COLOR, false);
|
|
540
|
+
const startRectanglePointX = fakeX, startRectanglePointY = fakeY, endRectanglePointX = fakeX + 30, endRectanglePointY = fakeY + 12;
|
|
541
|
+
const fakeRectangleG = drawRoundRectangle(roughSVG, startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
|
|
542
|
+
stroke: PRIMARY_COLOR,
|
|
543
|
+
strokeWidth: 2,
|
|
544
|
+
fill: PRIMARY_COLOR,
|
|
545
|
+
fillStyle: 'solid'
|
|
546
|
+
});
|
|
547
|
+
fakeDropNodeG?.appendChild(linkSVGG);
|
|
548
|
+
fakeDropNodeG?.appendChild(fakeRectangleG);
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
*
|
|
553
|
+
* @param targetNode
|
|
554
|
+
* @param centerPoint
|
|
555
|
+
* @returns DetectResult[] | null
|
|
556
|
+
*/
|
|
557
|
+
const directionDetector = (targetNode, centerPoint) => {
|
|
558
|
+
const { x, y, width, height } = getRectangleByNode(targetNode);
|
|
559
|
+
const yCenter = y + height / 2;
|
|
560
|
+
const xCenter = x + width / 2;
|
|
561
|
+
const top = targetNode.y;
|
|
562
|
+
const bottom = targetNode.y + targetNode.height;
|
|
563
|
+
const left = targetNode.x;
|
|
564
|
+
const right = targetNode.x + targetNode.width;
|
|
565
|
+
const direction = [];
|
|
566
|
+
// x 轴
|
|
567
|
+
if (centerPoint[1] > y && centerPoint[1] < y + height) {
|
|
568
|
+
if (centerPoint[0] > left && centerPoint[0] < xCenter) {
|
|
569
|
+
direction.push('left');
|
|
570
|
+
}
|
|
571
|
+
if (centerPoint[0] > xCenter && centerPoint[0] < right) {
|
|
572
|
+
direction.push('right');
|
|
573
|
+
}
|
|
574
|
+
// 重合区域,返回两个方向
|
|
575
|
+
if ((centerPoint[0] > x && centerPoint[0] < xCenter) || (centerPoint[0] > xCenter && centerPoint[0] < x + width)) {
|
|
576
|
+
if (centerPoint[1] < yCenter) {
|
|
577
|
+
direction.push('top');
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
direction.push('bottom');
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
return direction.length ? direction : null;
|
|
584
|
+
}
|
|
585
|
+
// y 轴
|
|
586
|
+
if (centerPoint[0] > x && centerPoint[0] < x + width) {
|
|
587
|
+
if (centerPoint[1] > top && centerPoint[1] < yCenter) {
|
|
588
|
+
direction.push('top');
|
|
589
|
+
}
|
|
590
|
+
if (centerPoint[1] > yCenter && centerPoint[1] < bottom) {
|
|
591
|
+
direction.push('bottom');
|
|
592
|
+
}
|
|
593
|
+
if ((centerPoint[1] > y && centerPoint[1] < y + height) || (centerPoint[1] > yCenter && centerPoint[1] < y + height)) {
|
|
594
|
+
if (centerPoint[0] < xCenter) {
|
|
595
|
+
direction.push('left');
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
direction.push('right');
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return direction.length ? direction : null;
|
|
602
|
+
}
|
|
603
|
+
return null;
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
const directionCorrector = (node, detectResults) => {
|
|
607
|
+
if (!node.origin.isRoot) {
|
|
608
|
+
const parentlayout = MindmapQueries.getCorrectLayoutByElement(node?.parent.origin);
|
|
609
|
+
if (isStandardLayout(parentlayout)) {
|
|
610
|
+
const idx = node.parent.children.findIndex(x => x === node);
|
|
611
|
+
const isLeft = idx >= (node.parent.origin.rightNodeCount || 0);
|
|
612
|
+
return getAllowedDirection(detectResults, [isLeft ? 'right' : 'left']);
|
|
613
|
+
}
|
|
614
|
+
if (isLeftLayout(parentlayout)) {
|
|
615
|
+
return getAllowedDirection(detectResults, ['right']);
|
|
616
|
+
}
|
|
617
|
+
if (isRightLayout(parentlayout)) {
|
|
618
|
+
return getAllowedDirection(detectResults, ['left']);
|
|
619
|
+
}
|
|
620
|
+
if (parentlayout === MindmapLayoutType.upward) {
|
|
621
|
+
return getAllowedDirection(detectResults, ['bottom']);
|
|
622
|
+
}
|
|
623
|
+
if (parentlayout === MindmapLayoutType.downward) {
|
|
624
|
+
return getAllowedDirection(detectResults, ['top']);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(node?.origin);
|
|
629
|
+
if (isStandardLayout(layout)) {
|
|
630
|
+
return getAllowedDirection(detectResults, ['top', 'bottom']);
|
|
631
|
+
}
|
|
632
|
+
if (isTopLayout(layout)) {
|
|
633
|
+
return getAllowedDirection(detectResults, ['left', 'right', 'bottom']);
|
|
634
|
+
}
|
|
635
|
+
if (isBottomLayout(layout)) {
|
|
636
|
+
return getAllowedDirection(detectResults, ['left', 'right', 'top']);
|
|
637
|
+
}
|
|
638
|
+
if (layout === MindmapLayoutType.left) {
|
|
639
|
+
return getAllowedDirection(detectResults, ['right', 'top', 'bottom']);
|
|
640
|
+
}
|
|
641
|
+
if (layout === MindmapLayoutType.right) {
|
|
642
|
+
return getAllowedDirection(detectResults, ['left', 'top', 'bottom']);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return null;
|
|
646
|
+
};
|
|
647
|
+
const getAllowedDirection = (detectResults, illegalDirections) => {
|
|
648
|
+
const directions = detectResults;
|
|
649
|
+
illegalDirections.forEach(item => {
|
|
650
|
+
const bottomDirectionIndex = directions.findIndex(direction => direction === item);
|
|
651
|
+
if (bottomDirectionIndex !== -1) {
|
|
652
|
+
directions.splice(bottomDirectionIndex, 1);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
return directions.length ? directions : null;
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
/* 根据布局调整 target 以及 direction */
|
|
659
|
+
const readjustmentDropTarget = (dropTarget) => {
|
|
660
|
+
const { target, detectResult } = dropTarget;
|
|
661
|
+
const newDropTarget = { target, detectResult };
|
|
662
|
+
const targetComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(target);
|
|
663
|
+
if (targetComponent.node.children.length > 0 && dropTarget.detectResult) {
|
|
664
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin);
|
|
665
|
+
const parentLayout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
|
|
666
|
+
if (['right', 'left'].includes(dropTarget.detectResult)) {
|
|
667
|
+
if (!isMixedLayout(parentLayout, layout)) {
|
|
668
|
+
if (targetComponent.node.origin.isRoot) {
|
|
669
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin);
|
|
670
|
+
// 标准布局,根节点
|
|
671
|
+
if (isStandardLayout(layout)) {
|
|
672
|
+
const rightNodeCount = targetComponent.node.origin.rightNodeCount;
|
|
673
|
+
if (detectResult === 'left') {
|
|
674
|
+
// 作为左的第一个节点
|
|
675
|
+
if (targetComponent.node.children.length === rightNodeCount) {
|
|
676
|
+
return newDropTarget;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
// 作为右的第一个节点或最后一个节点
|
|
681
|
+
if (rightNodeCount === 0) {
|
|
682
|
+
newDropTarget.target = target;
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
newDropTarget.target = targetComponent.node.children[rightNodeCount - 1].origin;
|
|
686
|
+
newDropTarget.detectResult = 'bottom';
|
|
687
|
+
}
|
|
688
|
+
return newDropTarget;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
// 缩进布局探测到第一个子节点
|
|
693
|
+
if (isIndentedLayout(parentLayout)) {
|
|
694
|
+
newDropTarget.target = targetComponent.node.children[0].origin;
|
|
695
|
+
newDropTarget.detectResult = isTopLayout(parentLayout) ? 'bottom' : 'top';
|
|
696
|
+
return newDropTarget;
|
|
697
|
+
}
|
|
698
|
+
// 上下布局的根节点只可以探测到上或者下,子节点的左右探测不处理,跳过。
|
|
699
|
+
if (isVerticalLogicLayout(parentLayout)) {
|
|
700
|
+
return newDropTarget;
|
|
701
|
+
}
|
|
702
|
+
// 剩下是水平布局的默认情况:插入最后一个子节点的下方
|
|
703
|
+
const lastChildNodeIndex = targetComponent.node.children.length - 1;
|
|
704
|
+
newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
|
|
705
|
+
newDropTarget.detectResult = 'bottom';
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
// 处理左右布局下的混合布局
|
|
709
|
+
if ([MindmapLayoutType.left, MindmapLayoutType.right].includes(parentLayout)) {
|
|
710
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin);
|
|
711
|
+
if (isIndentedLayout(layout)) {
|
|
712
|
+
newDropTarget.target = targetComponent.node.children[0].origin;
|
|
713
|
+
newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
|
|
714
|
+
return newDropTarget;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (['top', 'bottom'].includes(dropTarget.detectResult)) {
|
|
720
|
+
// 缩进布局移动至第一个节点
|
|
721
|
+
if (targetComponent.node.origin.isRoot && isIndentedLayout(layout)) {
|
|
722
|
+
newDropTarget.target = targetComponent.node.children[0].origin;
|
|
723
|
+
newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
|
|
724
|
+
return newDropTarget;
|
|
725
|
+
}
|
|
726
|
+
// 上下布局,插到右边
|
|
727
|
+
const parentLayout = MindmapQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
|
|
728
|
+
if (isVerticalLogicLayout(parentLayout)) {
|
|
729
|
+
const lastChildNodeIndex = targetComponent.node.children.length - 1;
|
|
730
|
+
newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
|
|
731
|
+
newDropTarget.detectResult = 'right';
|
|
732
|
+
return newDropTarget;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return newDropTarget;
|
|
736
|
+
}
|
|
737
|
+
return dropTarget;
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
const separateChildren = (parentElement) => {
|
|
741
|
+
const rightNodeCount = parentElement.rightNodeCount;
|
|
742
|
+
const children = parentElement.children;
|
|
743
|
+
let rightChildren = [], leftChildren = [];
|
|
744
|
+
for (let i = 0; i < children.length; i++) {
|
|
745
|
+
const child = children[i];
|
|
746
|
+
if (AbstractNode.isAbstract(child) && child.end < rightNodeCount) {
|
|
747
|
+
rightChildren.push(child);
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if (AbstractNode.isAbstract(child) && child.start >= rightNodeCount) {
|
|
751
|
+
leftChildren.push(child);
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
if (i < rightNodeCount) {
|
|
755
|
+
rightChildren.push(child);
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
leftChildren.push(child);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return { leftChildren, rightChildren };
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
const getRectangleByResizingLocation = (abstractRectangle, location, handlePosition, isHorizontal) => {
|
|
765
|
+
if (isHorizontal) {
|
|
766
|
+
if (handlePosition === AbstractHandlePosition.start) {
|
|
767
|
+
return {
|
|
768
|
+
...abstractRectangle,
|
|
769
|
+
y: location,
|
|
770
|
+
height: abstractRectangle.height + abstractRectangle.y - location
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
return {
|
|
775
|
+
...abstractRectangle,
|
|
776
|
+
height: location - abstractRectangle.y
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
else {
|
|
781
|
+
if (handlePosition === AbstractHandlePosition.start) {
|
|
782
|
+
return {
|
|
783
|
+
...abstractRectangle,
|
|
784
|
+
x: location,
|
|
785
|
+
width: abstractRectangle.width + abstractRectangle.x - location
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
return {
|
|
790
|
+
...abstractRectangle,
|
|
791
|
+
width: location - abstractRectangle.x
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
const getLocationScope = (board, handlePosition, parentChildren, element, parent, isHorizontal) => {
|
|
797
|
+
const node = MindElement.getNode(element);
|
|
798
|
+
const { start, end } = getCorrectStartEnd(node.origin, parent);
|
|
799
|
+
const startNode = parentChildren[start];
|
|
800
|
+
const endNode = parentChildren[end];
|
|
801
|
+
if (handlePosition === AbstractHandlePosition.start) {
|
|
802
|
+
const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.end < element.start);
|
|
803
|
+
let minNode;
|
|
804
|
+
if (abstractNode.length) {
|
|
805
|
+
const index = abstractNode
|
|
806
|
+
.map(node => {
|
|
807
|
+
const { end } = getCorrectStartEnd(node, parent);
|
|
808
|
+
return end;
|
|
809
|
+
})
|
|
810
|
+
.sort((a, b) => b - a)[0];
|
|
811
|
+
minNode = parentChildren[index + 1];
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
minNode = parentChildren[0];
|
|
815
|
+
}
|
|
816
|
+
const minNodeRectangle = getRectangleByElements(board, [minNode], true);
|
|
817
|
+
const endNodeRectangle = getRectangleByElements(board, [endNode], false);
|
|
818
|
+
if (isHorizontal) {
|
|
819
|
+
return {
|
|
820
|
+
max: endNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
|
|
821
|
+
min: minNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
return {
|
|
826
|
+
max: endNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
|
|
827
|
+
min: minNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.start > element.end);
|
|
833
|
+
let maxNode;
|
|
834
|
+
if (abstractNode.length) {
|
|
835
|
+
const index = abstractNode
|
|
836
|
+
.map(node => {
|
|
837
|
+
const { start } = getCorrectStartEnd(node, parent);
|
|
838
|
+
return start;
|
|
839
|
+
})
|
|
840
|
+
.sort((a, b) => a - b)[0];
|
|
841
|
+
maxNode = parentChildren[index - 1];
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
const children = parentChildren.filter(child => !AbstractNode.isAbstract(child));
|
|
845
|
+
maxNode = parentChildren[children.length - 1];
|
|
846
|
+
}
|
|
847
|
+
const maxNodeRectangle = getRectangleByElements(board, [maxNode], true);
|
|
848
|
+
const startNodeRectangle = getRectangleByElements(board, [startNode], false);
|
|
849
|
+
if (isHorizontal) {
|
|
850
|
+
return {
|
|
851
|
+
max: maxNodeRectangle.y + maxNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
|
|
852
|
+
min: startNodeRectangle.y + startNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
return {
|
|
857
|
+
max: maxNodeRectangle.x + maxNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
|
|
858
|
+
min: startNodeRectangle.x + startNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
const getHitAbstractHandle = (board, element, point) => {
|
|
864
|
+
const nodeLayout = MindmapQueries.getCorrectLayoutByElement(element);
|
|
865
|
+
const isHorizontal = isHorizontalLayout(nodeLayout);
|
|
866
|
+
const parentElement = MindElement.getParent(element);
|
|
867
|
+
const includedElements = parentElement.children.slice(element.start, element.end + 1);
|
|
868
|
+
let abstractRectangle = getRectangleByElements(board, includedElements, true);
|
|
869
|
+
abstractRectangle = RectangleClient.getOutlineRectangle(abstractRectangle, -ABSTRACT_INCLUDED_OUTLINE_OFFSET);
|
|
870
|
+
const startHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.start);
|
|
871
|
+
const endHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.end);
|
|
872
|
+
const pointRec = RectangleClient.toRectangleClient([point, point]);
|
|
873
|
+
if (RectangleClient.isIntersect(pointRec, startHandleRec))
|
|
874
|
+
return AbstractHandlePosition.start;
|
|
875
|
+
if (RectangleClient.isIntersect(pointRec, endHandleRec))
|
|
876
|
+
return AbstractHandlePosition.end;
|
|
877
|
+
return null;
|
|
878
|
+
};
|
|
879
|
+
const getAbstractHandleRectangle = (rectangle, isHorizontal, position) => {
|
|
880
|
+
let result;
|
|
881
|
+
if (position === AbstractHandlePosition.start) {
|
|
882
|
+
const location = isHorizontal ? rectangle.y : rectangle.x;
|
|
883
|
+
result = getRectangleByResizingLocation(rectangle, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.end, isHorizontal);
|
|
884
|
+
result = getRectangleByResizingLocation(result, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
const location = isHorizontal ? rectangle.y + rectangle.height : rectangle.x + rectangle.width;
|
|
888
|
+
result = getRectangleByResizingLocation(rectangle, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.start, isHorizontal);
|
|
889
|
+
result = getRectangleByResizingLocation(result, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
|
|
890
|
+
}
|
|
891
|
+
return result;
|
|
892
|
+
};
|
|
893
|
+
function findLocationLeftIndex(board, parentChildren, location, isHorizontal) {
|
|
894
|
+
const children = parentChildren.filter(child => {
|
|
895
|
+
return !AbstractNode.isAbstract(child);
|
|
896
|
+
});
|
|
897
|
+
const recArray = children.map(child => {
|
|
898
|
+
return getRectangleByElements(board, [child], false);
|
|
899
|
+
});
|
|
900
|
+
const firstRec = getRectangleByElements(board, [children[0]], true);
|
|
901
|
+
const fakeLeftRec = {
|
|
902
|
+
x: firstRec.x - firstRec.width,
|
|
903
|
+
y: firstRec.y - firstRec.height,
|
|
904
|
+
width: firstRec.width,
|
|
905
|
+
height: firstRec.height
|
|
906
|
+
};
|
|
907
|
+
const lastRec = getRectangleByElements(board, [children[children.length - 1]], true);
|
|
908
|
+
const fakeRightRec = {
|
|
909
|
+
x: lastRec.x + lastRec.width,
|
|
910
|
+
y: lastRec.y + lastRec.height,
|
|
911
|
+
width: lastRec.width,
|
|
912
|
+
height: lastRec.height
|
|
913
|
+
};
|
|
914
|
+
recArray.push(fakeRightRec);
|
|
915
|
+
recArray.unshift(fakeLeftRec);
|
|
916
|
+
for (let i = 0; i < recArray.length - 1; i++) {
|
|
917
|
+
const recXOrY = isHorizontal ? recArray[i].y : recArray[i].x;
|
|
918
|
+
const recWidthOrHeight = isHorizontal ? recArray[i].height : recArray[i].width;
|
|
919
|
+
if (location >= recXOrY + recWidthOrHeight / 2 &&
|
|
920
|
+
location <= recArray[i + 1][isHorizontal ? 'y' : 'x'] + recArray[i + 1][isHorizontal ? 'height' : 'width'] / 2) {
|
|
921
|
+
return i - 1;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* get correctly layout:
|
|
929
|
+
* 1. root is standard -> left or right
|
|
930
|
+
* 2. correct layout by incorrect layout direction
|
|
931
|
+
* @param element
|
|
932
|
+
*/
|
|
933
|
+
const getCorrectLayoutByElement = (element) => {
|
|
934
|
+
const { root } = findUpElement(element);
|
|
935
|
+
const rootLayout = root.layout || getDefaultMindmapLayout();
|
|
936
|
+
let correctRootLayout = rootLayout;
|
|
937
|
+
if (element.isRoot) {
|
|
938
|
+
return correctRootLayout;
|
|
939
|
+
}
|
|
940
|
+
const component = MINDMAP_ELEMENT_TO_COMPONENT.get(element);
|
|
941
|
+
let layout = component?.node.origin.layout;
|
|
942
|
+
let parentComponent;
|
|
943
|
+
let parent = component?.parent?.origin;
|
|
944
|
+
while (!layout && parent) {
|
|
945
|
+
parentComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(parent);
|
|
946
|
+
layout = parentComponent?.node.origin.layout;
|
|
947
|
+
parent = parentComponent?.parent?.origin;
|
|
948
|
+
}
|
|
949
|
+
if ((AbstractNode.isAbstract(element) || isChildOfAbstract(MindElement.getNode(element))) &&
|
|
950
|
+
isIndentedLayout(layout)) {
|
|
951
|
+
return getAbstractLayout(layout);
|
|
952
|
+
}
|
|
953
|
+
// handle root standard
|
|
954
|
+
if (rootLayout === MindmapLayoutType.standard) {
|
|
955
|
+
correctRootLayout = component?.node.left ? MindmapLayoutType.left : MindmapLayoutType.right;
|
|
956
|
+
}
|
|
957
|
+
if (parentComponent?.node.origin.isRoot) {
|
|
958
|
+
return correctRootLayout;
|
|
959
|
+
}
|
|
960
|
+
if (layout) {
|
|
961
|
+
const incorrectDirection = getInCorrectLayoutDirection(correctRootLayout, layout);
|
|
962
|
+
if (incorrectDirection) {
|
|
963
|
+
return correctLayoutByDirection(layout, incorrectDirection);
|
|
964
|
+
}
|
|
965
|
+
else {
|
|
966
|
+
return layout;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
return correctRootLayout;
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
|
|
974
|
+
const getBranchMindmapLayouts = (element) => {
|
|
975
|
+
const layouts = [];
|
|
976
|
+
if (element.layout) {
|
|
977
|
+
//getCorrectLayoutByElement含有递归操作,getBranchMindmapLayouts本身也有递归操作,有待优化
|
|
978
|
+
layouts.unshift(getCorrectLayoutByElement(element));
|
|
979
|
+
}
|
|
980
|
+
let parent = findParentElement(element);
|
|
981
|
+
while (parent) {
|
|
982
|
+
if (parent.layout) {
|
|
983
|
+
layouts.unshift(parent.layout);
|
|
984
|
+
}
|
|
985
|
+
parent = findParentElement(parent);
|
|
986
|
+
}
|
|
987
|
+
return layouts;
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* get available sub layouts by element
|
|
992
|
+
* @param element
|
|
993
|
+
* @returns MindmapLayoutType[]
|
|
994
|
+
*/
|
|
995
|
+
const getAvailableSubLayoutsByElement = (element) => {
|
|
996
|
+
const parentElement = findParentElement(element);
|
|
997
|
+
if (parentElement) {
|
|
998
|
+
const branchLayouts = getBranchMindmapLayouts(parentElement);
|
|
999
|
+
if (branchLayouts[0] === MindmapLayoutType.standard) {
|
|
1000
|
+
const component = MINDMAP_ELEMENT_TO_COMPONENT.get(element);
|
|
1001
|
+
if (component) {
|
|
1002
|
+
branchLayouts[0] = component.node.left ? MindmapLayoutType.left : MindmapLayoutType.right;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
const currentLayoutDirections = getBranchDirectionsByLayouts(branchLayouts);
|
|
1006
|
+
let availableSubLayouts = getAvailableSubLayoutsByLayoutDirections(currentLayoutDirections);
|
|
1007
|
+
const parentLayout = [branchLayouts[branchLayouts.length - 1]];
|
|
1008
|
+
const parentDirections = getBranchDirectionsByLayouts(parentLayout);
|
|
1009
|
+
const parentAvailableSubLayouts = getAvailableSubLayoutsByLayoutDirections(parentDirections);
|
|
1010
|
+
availableSubLayouts = availableSubLayouts.filter(layout => parentAvailableSubLayouts.some(parentAvailableSubLayout => parentAvailableSubLayout === layout));
|
|
1011
|
+
return availableSubLayouts;
|
|
1012
|
+
}
|
|
1013
|
+
return undefined;
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* 获取父节点布局类型
|
|
1018
|
+
* @param element
|
|
1019
|
+
* @returns MindmapLayoutType
|
|
1020
|
+
*/
|
|
1021
|
+
const getLayoutParentByElement = (element) => {
|
|
1022
|
+
let parent = findParentElement(element);
|
|
1023
|
+
while (parent) {
|
|
1024
|
+
if (parent.layout) {
|
|
1025
|
+
return parent.layout;
|
|
1026
|
+
}
|
|
1027
|
+
parent = findParentElement(parent);
|
|
1028
|
+
}
|
|
1029
|
+
return getDefaultMindmapLayout();
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
const getLayoutByElement = (element) => {
|
|
1033
|
+
const layout = element.layout;
|
|
1034
|
+
if (layout) {
|
|
1035
|
+
return layout;
|
|
1036
|
+
}
|
|
1037
|
+
if (AbstractNode.isAbstract(element) ||
|
|
1038
|
+
(isChildOfAbstract(MindElement.getNode(element)) && isIndentedLayout(layout))) {
|
|
1039
|
+
const parentLayout = getLayoutParentByElement(element);
|
|
1040
|
+
return getAbstractLayout(parentLayout);
|
|
1041
|
+
}
|
|
1042
|
+
return getLayoutParentByElement(element);
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
const MindmapQueries = {
|
|
1046
|
+
getAvailableSubLayoutsByElement,
|
|
1047
|
+
getLayoutParentByElement,
|
|
1048
|
+
getBranchMindmapLayouts,
|
|
1049
|
+
getLayoutByElement,
|
|
1050
|
+
getCorrectLayoutByElement
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
const PlaitMind = {
|
|
1054
|
+
isMind: (value) => {
|
|
1055
|
+
return value.type === 'mindmap';
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
const MindElement = {
|
|
1059
|
+
hasLayout(value, layout) {
|
|
1060
|
+
const _layout = MindmapQueries.getLayoutByElement(value);
|
|
1061
|
+
return _layout === layout;
|
|
1062
|
+
},
|
|
1063
|
+
isIndentedLayout(value) {
|
|
1064
|
+
const _layout = MindmapQueries.getLayoutByElement(value);
|
|
1065
|
+
return isIndentedLayout(_layout);
|
|
1066
|
+
},
|
|
1067
|
+
isMindElement(board, element) {
|
|
1068
|
+
const path = PlaitBoard.findPath(board, element);
|
|
1069
|
+
const rootElement = PlaitNode.get(board, path.slice(0, 1));
|
|
1070
|
+
if (PlaitMind.isMind(rootElement)) {
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
return false;
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
getParent(node) {
|
|
1078
|
+
if (PlaitMind.isMind(node)) {
|
|
1079
|
+
throw new Error('mind root node can not get parent');
|
|
1080
|
+
}
|
|
1081
|
+
const parent = NODE_TO_PARENT.get(node);
|
|
1082
|
+
return parent;
|
|
1083
|
+
},
|
|
1084
|
+
getRoot(board, element) {
|
|
1085
|
+
const path = PlaitBoard.findPath(board, element);
|
|
1086
|
+
return PlaitNode.get(board, path.slice(0, 1));
|
|
1087
|
+
},
|
|
1088
|
+
getNode(element) {
|
|
1089
|
+
const node = ELEMENT_TO_NODE.get(element);
|
|
1090
|
+
if (!node) {
|
|
1091
|
+
throw new Error(`can not get node from ${JSON.stringify(element)}`);
|
|
1092
|
+
}
|
|
1093
|
+
return node;
|
|
1094
|
+
},
|
|
1095
|
+
hasEmojis(element) {
|
|
1096
|
+
if (element.data.emojis) {
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
return false;
|
|
1101
|
+
}
|
|
1102
|
+
},
|
|
1103
|
+
getEmojis(element) {
|
|
1104
|
+
return element.data.emojis;
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
const MindmapNode = {
|
|
1109
|
+
get(root, path) {
|
|
1110
|
+
let node = root;
|
|
1111
|
+
for (let i = 0; i < path.length; i++) {
|
|
1112
|
+
const p = path[i];
|
|
1113
|
+
if (!node || !node.children || !node.children[p]) {
|
|
1114
|
+
throw new Error(`Cannot find a descendant at path [${path}]`);
|
|
1115
|
+
}
|
|
1116
|
+
node = node.children[p];
|
|
1117
|
+
}
|
|
1118
|
+
return node;
|
|
1119
|
+
},
|
|
1120
|
+
isEquals(node, otherNode) {
|
|
1121
|
+
const hasSameSize = node.x === otherNode.x && node.y === otherNode.y && node.width === otherNode.width && node.height === otherNode.height;
|
|
1122
|
+
const hasSameOrigin = node.origin === otherNode.origin;
|
|
1123
|
+
let hasSameParentOriginChildren = false;
|
|
1124
|
+
if (node.parent && otherNode.parent) {
|
|
1125
|
+
hasSameParentOriginChildren = node.parent.origin.children == otherNode.parent.origin.children;
|
|
1126
|
+
}
|
|
1127
|
+
return hasSameSize && hasSameOrigin && hasSameParentOriginChildren;
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
var LayoutDirection;
|
|
1132
|
+
(function (LayoutDirection) {
|
|
1133
|
+
LayoutDirection["top"] = "top";
|
|
1134
|
+
LayoutDirection["right"] = "right";
|
|
1135
|
+
LayoutDirection["bottom"] = "bottom";
|
|
1136
|
+
LayoutDirection["left"] = "left";
|
|
1137
|
+
})(LayoutDirection || (LayoutDirection = {}));
|
|
1138
|
+
const LayoutDirectionsMap = {
|
|
1139
|
+
[MindmapLayoutType.right]: [LayoutDirection.right],
|
|
1140
|
+
[MindmapLayoutType.left]: [LayoutDirection.left],
|
|
1141
|
+
[MindmapLayoutType.upward]: [LayoutDirection.top],
|
|
1142
|
+
[MindmapLayoutType.downward]: [LayoutDirection.bottom],
|
|
1143
|
+
[MindmapLayoutType.rightBottomIndented]: [LayoutDirection.right, LayoutDirection.bottom],
|
|
1144
|
+
[MindmapLayoutType.rightTopIndented]: [LayoutDirection.right, LayoutDirection.top],
|
|
1145
|
+
[MindmapLayoutType.leftBottomIndented]: [LayoutDirection.left, LayoutDirection.bottom],
|
|
1146
|
+
[MindmapLayoutType.leftTopIndented]: [LayoutDirection.left, LayoutDirection.top]
|
|
1147
|
+
};
|
|
1148
|
+
|
|
1149
|
+
var AbstractHandlePosition;
|
|
1150
|
+
(function (AbstractHandlePosition) {
|
|
1151
|
+
AbstractHandlePosition["start"] = "start";
|
|
1152
|
+
AbstractHandlePosition["end"] = "end";
|
|
1153
|
+
})(AbstractHandlePosition || (AbstractHandlePosition = {}));
|
|
1154
|
+
|
|
1155
|
+
const getBranchDirectionsByLayouts = (branchLayouts) => {
|
|
1156
|
+
const branchDirections = [];
|
|
1157
|
+
branchLayouts.forEach(l => {
|
|
1158
|
+
const directions = LayoutDirectionsMap[l];
|
|
1159
|
+
directions.forEach(d => {
|
|
1160
|
+
if (!branchDirections.includes(d) && !branchDirections.includes(getLayoutReverseDirection(d))) {
|
|
1161
|
+
branchDirections.push(d);
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
});
|
|
1165
|
+
return branchDirections;
|
|
1166
|
+
};
|
|
1167
|
+
const isCorrectLayout = (root, layout) => {
|
|
1168
|
+
const rootLayout = root.layout || getDefaultMindmapLayout();
|
|
1169
|
+
return !getInCorrectLayoutDirection(rootLayout, layout);
|
|
1170
|
+
};
|
|
1171
|
+
const isMixedLayout = (parentLayout, layout) => {
|
|
1172
|
+
return (!isIndentedLayout(parentLayout) && isIndentedLayout(layout)) || (isIndentedLayout(parentLayout) && !isIndentedLayout(layout));
|
|
1173
|
+
};
|
|
1174
|
+
const getInCorrectLayoutDirection = (rootLayout, layout) => {
|
|
1175
|
+
const mindmapDirections = LayoutDirectionsMap[rootLayout];
|
|
1176
|
+
const subLayoutDirections = LayoutDirectionsMap[layout];
|
|
1177
|
+
if (!subLayoutDirections) {
|
|
1178
|
+
throw new Error(`unexpection layout: ${layout} on correct layout`);
|
|
1179
|
+
}
|
|
1180
|
+
return subLayoutDirections.find(d => mindmapDirections.includes(getLayoutReverseDirection(d)));
|
|
1181
|
+
};
|
|
1182
|
+
const correctLayoutByDirection = (layout, direction) => {
|
|
1183
|
+
const isHorizontal = direction === LayoutDirection.left || direction === LayoutDirection.right ? true : false;
|
|
1184
|
+
let inverseDirectionLayout = MindmapLayoutType.standard;
|
|
1185
|
+
switch (layout) {
|
|
1186
|
+
case MindmapLayoutType.left:
|
|
1187
|
+
inverseDirectionLayout = MindmapLayoutType.right;
|
|
1188
|
+
break;
|
|
1189
|
+
case MindmapLayoutType.right:
|
|
1190
|
+
inverseDirectionLayout = MindmapLayoutType.left;
|
|
1191
|
+
break;
|
|
1192
|
+
case MindmapLayoutType.downward:
|
|
1193
|
+
inverseDirectionLayout = MindmapLayoutType.upward;
|
|
1194
|
+
break;
|
|
1195
|
+
case MindmapLayoutType.upward:
|
|
1196
|
+
inverseDirectionLayout = MindmapLayoutType.downward;
|
|
1197
|
+
break;
|
|
1198
|
+
case MindmapLayoutType.rightBottomIndented:
|
|
1199
|
+
inverseDirectionLayout = isHorizontal ? MindmapLayoutType.leftBottomIndented : MindmapLayoutType.rightTopIndented;
|
|
1200
|
+
break;
|
|
1201
|
+
case MindmapLayoutType.leftBottomIndented:
|
|
1202
|
+
inverseDirectionLayout = isHorizontal ? MindmapLayoutType.rightBottomIndented : MindmapLayoutType.leftTopIndented;
|
|
1203
|
+
break;
|
|
1204
|
+
case MindmapLayoutType.rightTopIndented:
|
|
1205
|
+
inverseDirectionLayout = isHorizontal ? MindmapLayoutType.leftTopIndented : MindmapLayoutType.rightBottomIndented;
|
|
1206
|
+
break;
|
|
1207
|
+
case MindmapLayoutType.leftTopIndented:
|
|
1208
|
+
inverseDirectionLayout = isHorizontal ? MindmapLayoutType.rightTopIndented : MindmapLayoutType.leftBottomIndented;
|
|
1209
|
+
break;
|
|
1210
|
+
}
|
|
1211
|
+
return inverseDirectionLayout;
|
|
1212
|
+
};
|
|
1213
|
+
const getMindmapDirection = (root) => {
|
|
1214
|
+
const layout = root.layout || getDefaultMindmapLayout();
|
|
1215
|
+
return LayoutDirectionsMap[layout];
|
|
1216
|
+
};
|
|
1217
|
+
const getDefaultMindmapLayout = () => {
|
|
1218
|
+
return MindmapLayoutType.standard;
|
|
1219
|
+
};
|
|
1220
|
+
const getAvailableSubLayoutsByLayoutDirections = (directions) => {
|
|
1221
|
+
const result = [];
|
|
1222
|
+
const reverseDirections = directions.map(getLayoutReverseDirection);
|
|
1223
|
+
for (const key in MindmapLayoutType) {
|
|
1224
|
+
const layout = MindmapLayoutType[key];
|
|
1225
|
+
const layoutDirections = LayoutDirectionsMap[layout];
|
|
1226
|
+
if (layoutDirections) {
|
|
1227
|
+
const hasSameDirection = layoutDirections.some(d => directions.includes(d));
|
|
1228
|
+
const hasReverseDirection = layoutDirections.some(r => reverseDirections.includes(r));
|
|
1229
|
+
if (hasSameDirection && !hasReverseDirection) {
|
|
1230
|
+
result.push(layout);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
return result;
|
|
1235
|
+
};
|
|
1236
|
+
const getLayoutReverseDirection = (layoutDirection) => {
|
|
1237
|
+
let reverseDirection = LayoutDirection.right;
|
|
1238
|
+
switch (layoutDirection) {
|
|
1239
|
+
case LayoutDirection.top:
|
|
1240
|
+
reverseDirection = LayoutDirection.bottom;
|
|
1241
|
+
break;
|
|
1242
|
+
case LayoutDirection.bottom:
|
|
1243
|
+
reverseDirection = LayoutDirection.top;
|
|
1244
|
+
break;
|
|
1245
|
+
case LayoutDirection.right:
|
|
1246
|
+
reverseDirection = LayoutDirection.left;
|
|
1247
|
+
break;
|
|
1248
|
+
case LayoutDirection.left:
|
|
1249
|
+
reverseDirection = LayoutDirection.right;
|
|
1250
|
+
break;
|
|
1251
|
+
}
|
|
1252
|
+
return reverseDirection;
|
|
1253
|
+
};
|
|
1254
|
+
const getRootLayout = (root) => {
|
|
1255
|
+
return root.layout || getDefaultMindmapLayout();
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
function enterNodeEditing(element) {
|
|
1259
|
+
const component = ELEMENT_TO_COMPONENT.get(element);
|
|
1260
|
+
component.startEditText(false, false);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
function findParentElement(element) {
|
|
1264
|
+
const component = MINDMAP_ELEMENT_TO_COMPONENT.get(element);
|
|
1265
|
+
if (component && component.parent) {
|
|
1266
|
+
return component.parent.origin;
|
|
1267
|
+
}
|
|
1268
|
+
return undefined;
|
|
1269
|
+
}
|
|
1270
|
+
function findUpElement(element) {
|
|
1271
|
+
let branch;
|
|
1272
|
+
let root = element;
|
|
1273
|
+
let parent = findParentElement(element);
|
|
1274
|
+
while (parent) {
|
|
1275
|
+
branch = root;
|
|
1276
|
+
root = parent;
|
|
1277
|
+
parent = findParentElement(parent);
|
|
1278
|
+
}
|
|
1279
|
+
return { root, branch };
|
|
1280
|
+
}
|
|
1281
|
+
const getChildrenCount = (element) => {
|
|
1282
|
+
const count = element.children.reduce((p, c) => {
|
|
1283
|
+
return p + getChildrenCount(c);
|
|
1284
|
+
}, 0);
|
|
1285
|
+
return count + element.children.length;
|
|
1286
|
+
};
|
|
1287
|
+
const isChildElement = (origin, child) => {
|
|
1288
|
+
let parent = findParentElement(child);
|
|
1289
|
+
while (parent) {
|
|
1290
|
+
if (parent === origin) {
|
|
1291
|
+
return true;
|
|
1292
|
+
}
|
|
1293
|
+
parent = findParentElement(parent);
|
|
1294
|
+
}
|
|
1295
|
+
return false;
|
|
1296
|
+
};
|
|
1297
|
+
const filterChildElement = (elements) => {
|
|
1298
|
+
let result = [];
|
|
1299
|
+
elements.forEach(element => {
|
|
1300
|
+
const isChild = elements.some(node => {
|
|
1301
|
+
return isChildElement(node, element);
|
|
1302
|
+
});
|
|
1303
|
+
if (!isChild) {
|
|
1304
|
+
result.push(element);
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
return result;
|
|
1308
|
+
};
|
|
1309
|
+
const isChildRight = (node, child) => {
|
|
1310
|
+
return node.x < child.x;
|
|
1311
|
+
};
|
|
1312
|
+
const isChildUp = (node, child) => {
|
|
1313
|
+
return node.y > child.y;
|
|
1314
|
+
};
|
|
1315
|
+
const copyNewNode = (node) => {
|
|
1316
|
+
const newNode = { ...node };
|
|
1317
|
+
newNode.id = idCreator();
|
|
1318
|
+
newNode.children = [];
|
|
1319
|
+
for (const childNode of node.children) {
|
|
1320
|
+
newNode.children.push(copyNewNode(childNode));
|
|
1321
|
+
}
|
|
1322
|
+
return newNode;
|
|
1323
|
+
};
|
|
1324
|
+
const transformRootToNode = (board, node) => {
|
|
1325
|
+
const newNode = { ...node };
|
|
1326
|
+
delete newNode.isRoot;
|
|
1327
|
+
delete newNode.rightNodeCount;
|
|
1328
|
+
delete newNode.type;
|
|
1329
|
+
const text = Node.string(node.data.topic.children[0]) || ' ';
|
|
1330
|
+
const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT);
|
|
1331
|
+
newNode.width = Math.max(width, NODE_MIN_WIDTH);
|
|
1332
|
+
newNode.height = height;
|
|
1333
|
+
if (newNode.layout === MindmapLayoutType.standard) {
|
|
1334
|
+
delete newNode.layout;
|
|
1335
|
+
}
|
|
1336
|
+
return newNode;
|
|
1337
|
+
};
|
|
1338
|
+
const transformNodeToRoot = (board, node) => {
|
|
1339
|
+
const newElement = { ...node };
|
|
1340
|
+
let text = Node.string(newElement.data.topic);
|
|
1341
|
+
if (!text) {
|
|
1342
|
+
text = '思维导图';
|
|
1343
|
+
newElement.data.topic = { children: [{ text }] };
|
|
1344
|
+
}
|
|
1345
|
+
delete newElement?.strokeColor;
|
|
1346
|
+
delete newElement?.fill;
|
|
1347
|
+
delete newElement?.shape;
|
|
1348
|
+
delete newElement?.strokeWidth;
|
|
1349
|
+
const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT, ROOT_TOPIC_FONT_SIZE);
|
|
1350
|
+
newElement.width = Math.max(width, NODE_MIN_WIDTH);
|
|
1351
|
+
newElement.height = height;
|
|
1352
|
+
return {
|
|
1353
|
+
...newElement,
|
|
1354
|
+
layout: newElement.layout ?? MindmapLayoutType.right,
|
|
1355
|
+
isCollapsed: false,
|
|
1356
|
+
isRoot: true,
|
|
1357
|
+
type: 'mindmap'
|
|
1358
|
+
};
|
|
1359
|
+
};
|
|
1360
|
+
const extractNodesText = (node) => {
|
|
1361
|
+
let str = '';
|
|
1362
|
+
if (node) {
|
|
1363
|
+
str += Node.string(node.data.topic.children[0]) + ' ';
|
|
1364
|
+
for (const childNode of node.children) {
|
|
1365
|
+
str += extractNodesText(childNode);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return str;
|
|
1369
|
+
};
|
|
1370
|
+
const changeRightNodeCount = (board, parentPath, changeNumber) => {
|
|
1371
|
+
const _rightNodeCount = board.children[parentPath[0]].rightNodeCount;
|
|
1372
|
+
Transforms.setNode(board, {
|
|
1373
|
+
rightNodeCount: changeNumber >= 0
|
|
1374
|
+
? _rightNodeCount + changeNumber
|
|
1375
|
+
: _rightNodeCount + changeNumber < 0
|
|
1376
|
+
? 0
|
|
1377
|
+
: _rightNodeCount + changeNumber
|
|
1378
|
+
}, parentPath);
|
|
1379
|
+
};
|
|
1380
|
+
const shouldChangeRightNodeCount = (selectedElement) => {
|
|
1381
|
+
const parentElement = findParentElement(selectedElement);
|
|
1382
|
+
const MindNodeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(selectedElement);
|
|
1383
|
+
if (parentElement && MindNodeComponent) {
|
|
1384
|
+
const nodeIndex = MindNodeComponent.parent.children.findIndex(item => item.origin.id === selectedElement.id);
|
|
1385
|
+
if (parentElement.isRoot &&
|
|
1386
|
+
getRootLayout(parentElement) === MindmapLayoutType.standard &&
|
|
1387
|
+
parentElement.rightNodeCount &&
|
|
1388
|
+
nodeIndex <= parentElement.rightNodeCount - 1) {
|
|
1389
|
+
return true;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return false;
|
|
1393
|
+
};
|
|
1394
|
+
const createMindmapData = (rightNodeCount, layout) => {
|
|
1395
|
+
const mindmapData = {
|
|
1396
|
+
type: 'mindmap',
|
|
1397
|
+
id: idCreator(),
|
|
1398
|
+
isRoot: true,
|
|
1399
|
+
rightNodeCount,
|
|
1400
|
+
layout,
|
|
1401
|
+
width: 72,
|
|
1402
|
+
height: ROOT_DEFAULT_HEIGHT,
|
|
1403
|
+
points: [[230, 208]],
|
|
1404
|
+
value: { children: [{ text: '思维导图' }] },
|
|
1405
|
+
shape: MindmapNodeShape.roundRectangle,
|
|
1406
|
+
children: [
|
|
1407
|
+
{
|
|
1408
|
+
id: idCreator(),
|
|
1409
|
+
value: { children: [{ text: '新建节点' }] },
|
|
1410
|
+
children: [],
|
|
1411
|
+
width: 56,
|
|
1412
|
+
height: TEXT_DEFAULT_HEIGHT,
|
|
1413
|
+
shape: MindmapNodeShape.roundRectangle
|
|
1414
|
+
},
|
|
1415
|
+
{
|
|
1416
|
+
id: idCreator(),
|
|
1417
|
+
value: { children: [{ text: '新建节点' }] },
|
|
1418
|
+
children: [],
|
|
1419
|
+
width: 56,
|
|
1420
|
+
height: TEXT_DEFAULT_HEIGHT,
|
|
1421
|
+
shape: MindmapNodeShape.roundRectangle
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
id: idCreator(),
|
|
1425
|
+
value: { children: [{ text: '新建节点' }] },
|
|
1426
|
+
children: [],
|
|
1427
|
+
width: 56,
|
|
1428
|
+
height: TEXT_DEFAULT_HEIGHT,
|
|
1429
|
+
shape: MindmapNodeShape.roundRectangle
|
|
1430
|
+
}
|
|
1431
|
+
]
|
|
1432
|
+
};
|
|
1433
|
+
return [mindmapData];
|
|
1434
|
+
};
|
|
1435
|
+
const createMindElement = (text, width, height, options) => {
|
|
1436
|
+
const newElement = {
|
|
1437
|
+
id: idCreator(),
|
|
1438
|
+
data: {
|
|
1439
|
+
topic: { children: [{ text }] }
|
|
1440
|
+
},
|
|
1441
|
+
children: [],
|
|
1442
|
+
width,
|
|
1443
|
+
height,
|
|
1444
|
+
fill: options.fill,
|
|
1445
|
+
strokeColor: options.strokeColor,
|
|
1446
|
+
strokeWidth: options.strokeWidth,
|
|
1447
|
+
shape: options.shape
|
|
1448
|
+
};
|
|
1449
|
+
if (options.fill) {
|
|
1450
|
+
newElement.fill = options.fill;
|
|
1451
|
+
}
|
|
1452
|
+
if (options.strokeColor) {
|
|
1453
|
+
newElement.strokeColor = options.strokeColor;
|
|
1454
|
+
}
|
|
1455
|
+
if (!isNullOrUndefined(options.strokeWidth)) {
|
|
1456
|
+
newElement.strokeWidth = options.strokeWidth;
|
|
1457
|
+
}
|
|
1458
|
+
if (options.shape) {
|
|
1459
|
+
newElement.shape = options.shape;
|
|
1460
|
+
}
|
|
1461
|
+
if (options.layout) {
|
|
1462
|
+
newElement.layout = options.layout;
|
|
1463
|
+
}
|
|
1464
|
+
if (options.linkLineColor) {
|
|
1465
|
+
newElement.linkLineColor = options.linkLineColor;
|
|
1466
|
+
}
|
|
1467
|
+
return newElement;
|
|
1468
|
+
};
|
|
1469
|
+
// layoutLevel 用来表示插入兄弟节点还是子节点
|
|
1470
|
+
const insertMindElement = (board, inheritNode, path) => {
|
|
1471
|
+
let fill, strokeColor, strokeWidth, shape = MindmapNodeShape.roundRectangle;
|
|
1472
|
+
if (!inheritNode.isRoot) {
|
|
1473
|
+
fill = inheritNode.fill;
|
|
1474
|
+
strokeColor = inheritNode.strokeColor;
|
|
1475
|
+
strokeWidth = inheritNode.strokeWidth;
|
|
1476
|
+
}
|
|
1477
|
+
shape = inheritNode.shape;
|
|
1478
|
+
const newElement = createMindElement('', NODE_MIN_WIDTH, TEXT_DEFAULT_HEIGHT, { fill, strokeColor, strokeWidth, shape });
|
|
1479
|
+
const index = path[path.length - 1];
|
|
1480
|
+
const abstractNode = inheritNode.children.find(child => AbstractNode.isAbstract(child) && index > child.start && index <= child.end + 1);
|
|
1481
|
+
if (abstractNode) {
|
|
1482
|
+
const path = PlaitBoard.findPath(board, abstractNode);
|
|
1483
|
+
Transforms.setNode(board, { end: abstractNode.end + 1 }, path);
|
|
1484
|
+
}
|
|
1485
|
+
Transforms.insertNode(board, newElement, path);
|
|
1486
|
+
addSelectedElement(board, newElement);
|
|
1487
|
+
setTimeout(() => {
|
|
1488
|
+
enterNodeEditing(newElement);
|
|
1489
|
+
});
|
|
1490
|
+
};
|
|
1491
|
+
const findLastChild = (child) => {
|
|
1492
|
+
let result = child;
|
|
1493
|
+
while (result.children.length !== 0) {
|
|
1494
|
+
result = result.children[result.children.length - 1];
|
|
1495
|
+
}
|
|
1496
|
+
return result;
|
|
1497
|
+
};
|
|
1498
|
+
const deleteSelectedELements = (board, selectedElements) => {
|
|
1499
|
+
//翻转,从下到上修改,防止找不到 path
|
|
1500
|
+
const deletableElements = filterChildElement(selectedElements).reverse();
|
|
1501
|
+
const relativeAbstracts = [];
|
|
1502
|
+
const accumulativeProperties = new WeakMap();
|
|
1503
|
+
deletableElements.forEach(node => {
|
|
1504
|
+
const MindNodeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(node);
|
|
1505
|
+
if (MindNodeComponent && MindNodeComponent.parent) {
|
|
1506
|
+
const index = MindNodeComponent.parent.origin.children.indexOf(node);
|
|
1507
|
+
const abstracts = MindNodeComponent.parent.children.filter(value => AbstractNode.isAbstract(value.origin));
|
|
1508
|
+
abstracts.forEach(abstract => {
|
|
1509
|
+
const abstractNode = abstract.origin;
|
|
1510
|
+
if (index >= abstractNode.start && index <= abstractNode.end) {
|
|
1511
|
+
let newProperties = accumulativeProperties.get(abstractNode);
|
|
1512
|
+
if (!newProperties) {
|
|
1513
|
+
newProperties = { start: abstractNode.start, end: abstractNode.end };
|
|
1514
|
+
accumulativeProperties.set(abstractNode, newProperties);
|
|
1515
|
+
relativeAbstracts.push(abstractNode);
|
|
1516
|
+
}
|
|
1517
|
+
newProperties.end = newProperties.end - 1;
|
|
1518
|
+
}
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1522
|
+
const abstractHandles = relativeAbstracts.map(value => {
|
|
1523
|
+
const newProperties = accumulativeProperties.get(value);
|
|
1524
|
+
if (newProperties) {
|
|
1525
|
+
const path = PlaitBoard.findPath(board, value);
|
|
1526
|
+
return () => {
|
|
1527
|
+
if (newProperties.start > newProperties.end) {
|
|
1528
|
+
Transforms.removeNode(board, path);
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
Transforms.setNode(board, newProperties, path);
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
return () => { };
|
|
1536
|
+
});
|
|
1537
|
+
const deletableHandles = deletableElements.map(node => {
|
|
1538
|
+
const path = PlaitBoard.findPath(board, node);
|
|
1539
|
+
return () => {
|
|
1540
|
+
if (shouldChangeRightNodeCount(node)) {
|
|
1541
|
+
changeRightNodeCount(board, path.slice(0, path.length - 1), -1);
|
|
1542
|
+
}
|
|
1543
|
+
Transforms.removeNode(board, path);
|
|
1544
|
+
};
|
|
1545
|
+
});
|
|
1546
|
+
abstractHandles.forEach(action => action());
|
|
1547
|
+
deletableHandles.forEach(action => action());
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
const getStrokeByMindmapElement = (element) => {
|
|
1551
|
+
let stroke = element.strokeColor;
|
|
1552
|
+
if (stroke) {
|
|
1553
|
+
return stroke;
|
|
1554
|
+
}
|
|
1555
|
+
const { root, branch } = findUpElement(element);
|
|
1556
|
+
if (branch) {
|
|
1557
|
+
const index = root.children.indexOf(branch);
|
|
1558
|
+
const length = COLORS.length;
|
|
1559
|
+
const remainder = index % length;
|
|
1560
|
+
return COLORS[remainder];
|
|
1561
|
+
}
|
|
1562
|
+
else {
|
|
1563
|
+
return ROOT_NODE_STROKE;
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
const getLinkLineColorByMindmapElement = (element) => {
|
|
1567
|
+
let color = element.linkLineColor;
|
|
1568
|
+
if (color) {
|
|
1569
|
+
return color;
|
|
1570
|
+
}
|
|
1571
|
+
const { root, branch } = findUpElement(element);
|
|
1572
|
+
if (branch) {
|
|
1573
|
+
const index = root.children.indexOf(branch);
|
|
1574
|
+
const length = COLORS.length;
|
|
1575
|
+
const remainder = index % length;
|
|
1576
|
+
return COLORS[remainder];
|
|
1577
|
+
}
|
|
1578
|
+
else {
|
|
1579
|
+
throw new Error('root element should not have link line');
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
const getRootLinkLineColorByMindmapElement = (root) => {
|
|
1583
|
+
const index = root.children.length;
|
|
1584
|
+
const length = COLORS.length;
|
|
1585
|
+
const remainder = index % length;
|
|
1586
|
+
return COLORS[remainder];
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
function drawIndentedLink(roughSVG, node, child, defaultStroke = null, needDrawUnderline = true) {
|
|
1590
|
+
const isUnderlineShap = getNodeShapeByElement(child.origin) === MindmapNodeShape.underline;
|
|
1591
|
+
let beginX, beginY, endX, endY, beginNode = node, endNode = child;
|
|
1592
|
+
const beginRectangle = getRectangleByNode(beginNode);
|
|
1593
|
+
const endRectangle = getRectangleByNode(endNode);
|
|
1594
|
+
beginX = beginNode.x + beginNode.width / 2;
|
|
1595
|
+
beginY = isChildUp(node, child) ? beginRectangle.y : beginRectangle.y + beginRectangle.height;
|
|
1596
|
+
endX = node.left ? endNode.x + endNode.hGap + endRectangle.width : endNode.x + endNode.hGap;
|
|
1597
|
+
endY = isUnderlineShap ? endNode.y + endNode.height - endNode.vGap : endNode.y + endNode.height / 2;
|
|
1598
|
+
//根据位置,设置正负参数
|
|
1599
|
+
let plusMinus = isChildUp(node, child) ? (node.left ? [-1, -1] : [1, -1]) : node.left ? [-1, 1] : [1, 1];
|
|
1600
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(node.origin);
|
|
1601
|
+
const strokeWidth = child.origin.linkLineWidth ? child.origin.linkLineWidth : STROKE_WIDTH;
|
|
1602
|
+
if (beginNode.origin.isRoot) {
|
|
1603
|
+
if (layout === MindmapLayoutType.leftBottomIndented || layout === MindmapLayoutType.rightBottomIndented) {
|
|
1604
|
+
beginY += strokeWidth;
|
|
1605
|
+
}
|
|
1606
|
+
if (layout === MindmapLayoutType.leftTopIndented || layout === MindmapLayoutType.rightTopIndented) {
|
|
1607
|
+
beginY -= strokeWidth;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
let curve = [
|
|
1611
|
+
[beginX, beginY],
|
|
1612
|
+
[beginX, beginY],
|
|
1613
|
+
[beginX, beginY],
|
|
1614
|
+
[beginX, endY - (endNode.hGap * 3 * plusMinus[1]) / 5],
|
|
1615
|
+
[beginX, endY - (endNode.hGap * plusMinus[1]) / 5],
|
|
1616
|
+
[beginX + (endNode.hGap * plusMinus[0]) / 4, endY],
|
|
1617
|
+
[beginX + (endNode.hGap * plusMinus[0] * 3) / 5, endY],
|
|
1618
|
+
isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
|
|
1619
|
+
isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
|
|
1620
|
+
isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY]
|
|
1621
|
+
];
|
|
1622
|
+
const stroke = defaultStroke || getLinkLineColorByMindmapElement(child.origin);
|
|
1623
|
+
const points = pointsOnBezierCurves(curve);
|
|
1624
|
+
return roughSVG.curve(points, { stroke, strokeWidth });
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
var HorizontalPlacement;
|
|
1628
|
+
(function (HorizontalPlacement) {
|
|
1629
|
+
HorizontalPlacement["left"] = "left";
|
|
1630
|
+
HorizontalPlacement["center"] = "center";
|
|
1631
|
+
HorizontalPlacement["right"] = "right";
|
|
1632
|
+
})(HorizontalPlacement || (HorizontalPlacement = {}));
|
|
1633
|
+
var VerticalPlacement;
|
|
1634
|
+
(function (VerticalPlacement) {
|
|
1635
|
+
VerticalPlacement["top"] = "top";
|
|
1636
|
+
VerticalPlacement["middle"] = "middle";
|
|
1637
|
+
VerticalPlacement["bottom"] = "bottom";
|
|
1638
|
+
})(VerticalPlacement || (VerticalPlacement = {}));
|
|
1639
|
+
|
|
1640
|
+
const getPointByPlacement = (client, placement) => {
|
|
1641
|
+
let x = client.x;
|
|
1642
|
+
let y = client.y;
|
|
1643
|
+
if (placement[0] === HorizontalPlacement.center) {
|
|
1644
|
+
x = client.x + client.width / 2;
|
|
1645
|
+
}
|
|
1646
|
+
if (placement[0] === HorizontalPlacement.right) {
|
|
1647
|
+
x = client.x + client.width;
|
|
1648
|
+
}
|
|
1649
|
+
if (placement[1] === VerticalPlacement.middle) {
|
|
1650
|
+
y = client.y + client.height / 2;
|
|
1651
|
+
}
|
|
1652
|
+
if (placement[1] === VerticalPlacement.bottom) {
|
|
1653
|
+
y = client.y + client.height;
|
|
1654
|
+
}
|
|
1655
|
+
return [x, y];
|
|
1656
|
+
};
|
|
1657
|
+
const getLayoutDirection = (node, isHorizontal) => {
|
|
1658
|
+
if (isHorizontal) {
|
|
1659
|
+
if (node.left) {
|
|
1660
|
+
return LayoutDirection.left;
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
return LayoutDirection.right;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
if (node.up) {
|
|
1668
|
+
return LayoutDirection.top;
|
|
1669
|
+
}
|
|
1670
|
+
else {
|
|
1671
|
+
return LayoutDirection.bottom;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
// 以右为基准
|
|
1676
|
+
// 右 -> 左:
|
|
1677
|
+
// 1. 终点 -> 起点/起点 -> 终点
|
|
1678
|
+
// 2. 加 -> 减
|
|
1679
|
+
// 水平 -> 垂直:
|
|
1680
|
+
// 1. 起点/终点 -> 纵轴
|
|
1681
|
+
// 2. 加减 -> 纵轴
|
|
1682
|
+
// 下 -> 上:
|
|
1683
|
+
// 1. 终点 -> 起点/终点 -> 起点
|
|
1684
|
+
// 2. 加 -> 减
|
|
1685
|
+
const movePoint = (point, distance, direction = LayoutDirection.right) => {
|
|
1686
|
+
if (direction === LayoutDirection.left) {
|
|
1687
|
+
return [point[0] - distance, point[1]];
|
|
1688
|
+
}
|
|
1689
|
+
if (direction === LayoutDirection.bottom) {
|
|
1690
|
+
return [point[0], point[1] + distance];
|
|
1691
|
+
}
|
|
1692
|
+
if (direction === LayoutDirection.top) {
|
|
1693
|
+
return [point[0], point[1] - distance];
|
|
1694
|
+
}
|
|
1695
|
+
return [point[0] + distance, point[1]];
|
|
1696
|
+
};
|
|
1697
|
+
const transformPlacement = (placement, direction) => {
|
|
1698
|
+
// to left
|
|
1699
|
+
if (direction === LayoutDirection.left) {
|
|
1700
|
+
if (placement[0] === HorizontalPlacement.right) {
|
|
1701
|
+
placement[0] = HorizontalPlacement.left;
|
|
1702
|
+
}
|
|
1703
|
+
else if (placement[0] === HorizontalPlacement.left) {
|
|
1704
|
+
placement[0] = HorizontalPlacement.right;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
// to bottom
|
|
1708
|
+
if (direction === LayoutDirection.bottom || direction === LayoutDirection.top) {
|
|
1709
|
+
let horizontal = HorizontalPlacement.center;
|
|
1710
|
+
let vertical = VerticalPlacement.middle;
|
|
1711
|
+
if (placement[1] === VerticalPlacement.top) {
|
|
1712
|
+
horizontal = HorizontalPlacement.left;
|
|
1713
|
+
}
|
|
1714
|
+
if (placement[1] === VerticalPlacement.bottom) {
|
|
1715
|
+
horizontal = HorizontalPlacement.right;
|
|
1716
|
+
}
|
|
1717
|
+
if (placement[0] === HorizontalPlacement.left) {
|
|
1718
|
+
vertical = VerticalPlacement.top;
|
|
1719
|
+
}
|
|
1720
|
+
if (placement[0] === HorizontalPlacement.right) {
|
|
1721
|
+
vertical = VerticalPlacement.bottom;
|
|
1722
|
+
}
|
|
1723
|
+
placement[0] = horizontal;
|
|
1724
|
+
placement[1] = vertical;
|
|
1725
|
+
}
|
|
1726
|
+
// to up
|
|
1727
|
+
if (direction === LayoutDirection.top) {
|
|
1728
|
+
if (placement[1] === VerticalPlacement.bottom) {
|
|
1729
|
+
placement[1] = VerticalPlacement.top;
|
|
1730
|
+
}
|
|
1731
|
+
else if (placement[1] === VerticalPlacement.top) {
|
|
1732
|
+
placement[1] = VerticalPlacement.bottom;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
|
|
1737
|
+
function drawLogicLink(roughSVG, node, parent, isHorizontal) {
|
|
1738
|
+
const stroke = getLinkLineColorByMindmapElement(node.origin);
|
|
1739
|
+
const strokeWidth = node.origin.linkLineWidth ? node.origin.linkLineWidth : STROKE_WIDTH;
|
|
1740
|
+
const hasStraightLine = !parent.origin.isRoot;
|
|
1741
|
+
const hasUnderlineShape = node.origin.shape === MindmapNodeShape.underline;
|
|
1742
|
+
const hasUnderlineShapeOfParent = parent.origin.shape === MindmapNodeShape.underline;
|
|
1743
|
+
const nodeClient = getRectangleByNode(node);
|
|
1744
|
+
const parentClient = getRectangleByNode(parent);
|
|
1745
|
+
const linkDirection = getLayoutDirection(node, isHorizontal);
|
|
1746
|
+
// ① ensure begin placement and end placement
|
|
1747
|
+
// begin placement represent parent connector position and end placement represent child connector
|
|
1748
|
+
const beginPlacement = [HorizontalPlacement.right, VerticalPlacement.middle];
|
|
1749
|
+
const endPlacement = [HorizontalPlacement.left, VerticalPlacement.middle];
|
|
1750
|
+
transformPlacement(beginPlacement, linkDirection);
|
|
1751
|
+
transformPlacement(endPlacement, linkDirection);
|
|
1752
|
+
// underline shape and horizontal
|
|
1753
|
+
if (isHorizontal && hasUnderlineShapeOfParent && !parent.origin.isRoot) {
|
|
1754
|
+
beginPlacement[1] = VerticalPlacement.bottom;
|
|
1755
|
+
}
|
|
1756
|
+
if (isHorizontal && hasUnderlineShape) {
|
|
1757
|
+
endPlacement[1] = VerticalPlacement.bottom;
|
|
1758
|
+
}
|
|
1759
|
+
let beginPoint = getPointByPlacement(parentClient, beginPlacement);
|
|
1760
|
+
let endPoint = getPointByPlacement(nodeClient, endPlacement);
|
|
1761
|
+
// ② 确定凸出直线,从起始点开始画一条直线,从直线的结束位置绘制曲线,保证收起图标可以完美覆盖起始连线,根节点不需要这条直线
|
|
1762
|
+
// 绘制贝塞尔曲线要求,需要增加三个点,正常两个点就可以确定这条直线
|
|
1763
|
+
const straightLineDistance = 8;
|
|
1764
|
+
const beginPoint2 = hasStraightLine ? movePoint(beginPoint, straightLineDistance, linkDirection) : beginPoint;
|
|
1765
|
+
let straightLine = hasStraightLine ? [beginPoint, beginPoint2, beginPoint2] : [];
|
|
1766
|
+
// ③ 确定曲线
|
|
1767
|
+
const beginBufferDistance = (parent.hGap + node.hGap) / 3;
|
|
1768
|
+
const endBufferDistance = -(parent.hGap + node.hGap) / 2.4;
|
|
1769
|
+
let curve = [
|
|
1770
|
+
beginPoint2,
|
|
1771
|
+
movePoint(beginPoint2, beginBufferDistance, linkDirection),
|
|
1772
|
+
movePoint(endPoint, endBufferDistance, linkDirection),
|
|
1773
|
+
endPoint
|
|
1774
|
+
];
|
|
1775
|
+
// ④ 下划线绘制,underline shape and horizontal
|
|
1776
|
+
const underlineEnd = movePoint(endPoint, nodeClient.width, linkDirection);
|
|
1777
|
+
const underline = hasUnderlineShape && isHorizontal ? [underlineEnd, underlineEnd, underlineEnd] : [];
|
|
1778
|
+
const points = pointsOnBezierCurves([...straightLine, ...curve, ...underline]);
|
|
1779
|
+
return roughSVG.curve(points, { stroke, strokeWidth });
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
function getEmojisRectangle(element) {
|
|
1783
|
+
const count = element.data.emojis.length;
|
|
1784
|
+
const fontSize = getEmojiFontSize(element);
|
|
1785
|
+
return { width: fontSize * count, height: fontSize * 1.5 };
|
|
1786
|
+
}
|
|
1787
|
+
function getEmojiFontSize(element) {
|
|
1788
|
+
if (PlaitMind.isMind(element)) {
|
|
1789
|
+
return 18;
|
|
1790
|
+
}
|
|
1791
|
+
else {
|
|
1792
|
+
return 14;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
const NodeDefaultSpace = {
|
|
1797
|
+
horizontal: {
|
|
1798
|
+
nodeAndText: BASE * 3,
|
|
1799
|
+
emojiAndText: BASE * 1.5
|
|
1800
|
+
},
|
|
1801
|
+
vertical: {
|
|
1802
|
+
nodeAndText: BASE * 1.5
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1805
|
+
const RootDefaultSpace = {
|
|
1806
|
+
horizontal: {
|
|
1807
|
+
nodeAndText: BASE * 4,
|
|
1808
|
+
emojiAndText: BASE * 2
|
|
1809
|
+
},
|
|
1810
|
+
vertical: {
|
|
1811
|
+
nodeAndText: BASE * 2
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
const getHorizontalSpaceBetweenNodeAndText = (element) => {
|
|
1815
|
+
const isMind = PlaitMind.isMind(element);
|
|
1816
|
+
const nodeAndText = isMind ? RootDefaultSpace.horizontal.nodeAndText : NodeDefaultSpace.horizontal.nodeAndText;
|
|
1817
|
+
return nodeAndText;
|
|
1818
|
+
};
|
|
1819
|
+
const getHorizontalSpaceEmojiAndText = (element) => {
|
|
1820
|
+
const isMind = PlaitMind.isMind(element);
|
|
1821
|
+
const emojiAndText = isMind ? RootDefaultSpace.horizontal.emojiAndText : NodeDefaultSpace.horizontal.emojiAndText;
|
|
1822
|
+
return emojiAndText;
|
|
1823
|
+
};
|
|
1824
|
+
const getVerticalSpaceBetweenNodeAndText = (element) => {
|
|
1825
|
+
const isMind = PlaitMind.isMind(element);
|
|
1826
|
+
const nodeAndText = isMind ? RootDefaultSpace.vertical.nodeAndText : NodeDefaultSpace.vertical.nodeAndText;
|
|
1827
|
+
return nodeAndText;
|
|
1828
|
+
};
|
|
1829
|
+
const NodeSpace = {
|
|
1830
|
+
getNodeWidth(element) {
|
|
1831
|
+
const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
|
|
1832
|
+
if (MindElement.hasEmojis(element)) {
|
|
1833
|
+
return nodeAndText + getEmojisRectangle(element).width + getHorizontalSpaceEmojiAndText(element) + element.width + nodeAndText;
|
|
1834
|
+
}
|
|
1835
|
+
return nodeAndText + element.width + nodeAndText;
|
|
1836
|
+
},
|
|
1837
|
+
getNodeHeight(element) {
|
|
1838
|
+
const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
|
|
1839
|
+
return nodeAndText + element.height + nodeAndText;
|
|
1840
|
+
},
|
|
1841
|
+
getTextHorizontalSpace(element) {
|
|
1842
|
+
const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
|
|
1843
|
+
if (MindElement.hasEmojis(element)) {
|
|
1844
|
+
return nodeAndText + getEmojisRectangle(element).width + getHorizontalSpaceEmojiAndText(element);
|
|
1845
|
+
}
|
|
1846
|
+
else {
|
|
1847
|
+
return nodeAndText;
|
|
1848
|
+
}
|
|
1849
|
+
},
|
|
1850
|
+
getTextVerticalSpace(element) {
|
|
1851
|
+
const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
|
|
1852
|
+
return nodeAndText;
|
|
1853
|
+
},
|
|
1854
|
+
getEmojiHorizontalSpace(element) {
|
|
1855
|
+
const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
|
|
1856
|
+
return nodeAndText;
|
|
1857
|
+
},
|
|
1858
|
+
getEmojiVerticalSpace(element) {
|
|
1859
|
+
const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
|
|
1860
|
+
return nodeAndText;
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
|
|
1864
|
+
function drawMindmapNodeRichtext(node, viewContainerRef) {
|
|
1865
|
+
const { x, y } = getRichtextRectangleByNode(node);
|
|
1866
|
+
const classList = [];
|
|
1867
|
+
if (node.origin.isRoot) {
|
|
1868
|
+
classList.push('root-node');
|
|
1869
|
+
classList.push('font-size-18');
|
|
1870
|
+
}
|
|
1871
|
+
else if (node.parent?.origin?.isRoot) {
|
|
1872
|
+
classList.push('root-child-node');
|
|
1873
|
+
}
|
|
1874
|
+
else {
|
|
1875
|
+
classList.push('child-node');
|
|
1876
|
+
}
|
|
1877
|
+
return drawRichtext(x, y, node.origin.width, node.origin.height, node.origin.data.topic, viewContainerRef, classList);
|
|
1878
|
+
}
|
|
1879
|
+
function updateMindNodeTopicSize(node, g, isEditable) {
|
|
1880
|
+
const { x, y, width, height } = getRichtextRectangleByNode(node);
|
|
1881
|
+
if (isEditable) {
|
|
1882
|
+
// add 999, avoid changing lines when paste more text
|
|
1883
|
+
updateForeignObject(g, width + 999, height + 999, x, y);
|
|
1884
|
+
}
|
|
1885
|
+
else {
|
|
1886
|
+
updateForeignObject(g, node.origin.width, node.origin.height, x, y);
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
function getRichtextRectangleByNode(node) {
|
|
1890
|
+
let { x, y, width, height } = getRectangleByNode(node);
|
|
1891
|
+
x = x + NodeSpace.getTextHorizontalSpace(node.origin);
|
|
1892
|
+
y = y + NodeSpace.getTextVerticalSpace(node.origin);
|
|
1893
|
+
return { width, height, x, y };
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
function drawRectangleNode(board, node) {
|
|
1897
|
+
const { x, y, width, height } = getRectangleByNode(node);
|
|
1898
|
+
const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? ROOT_NODE_FILL : NODE_FILL;
|
|
1899
|
+
const stroke = getStrokeByMindmapElement(node.origin);
|
|
1900
|
+
const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : STROKE_WIDTH;
|
|
1901
|
+
const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), x, y, x + width, y + height, {
|
|
1902
|
+
stroke,
|
|
1903
|
+
strokeWidth,
|
|
1904
|
+
fill,
|
|
1905
|
+
fillStyle: 'solid'
|
|
1906
|
+
});
|
|
1907
|
+
return nodeG;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
function drawAbstractLink(board, node, isHorizontal) {
|
|
1911
|
+
const linkPadding = 15;
|
|
1912
|
+
const parent = node.parent;
|
|
1913
|
+
const abstractRectangle = getRectangleByNode(node);
|
|
1914
|
+
let includedElements = parent.children.slice(node.origin.start, node.origin.end + 1).map(node => {
|
|
1915
|
+
return node.origin;
|
|
1916
|
+
});
|
|
1917
|
+
const includedElementsRectangle = getRectangleByElements(board, includedElements, true);
|
|
1918
|
+
const linkDirection = getLayoutDirection(node, isHorizontal);
|
|
1919
|
+
const bezierBeginPlacement = [HorizontalPlacement.right, VerticalPlacement.top];
|
|
1920
|
+
const bezierEndPlacement = [HorizontalPlacement.right, VerticalPlacement.bottom];
|
|
1921
|
+
const abstractConnectorPlacement = [HorizontalPlacement.left, VerticalPlacement.middle];
|
|
1922
|
+
transformPlacement(bezierBeginPlacement, linkDirection);
|
|
1923
|
+
transformPlacement(bezierEndPlacement, linkDirection);
|
|
1924
|
+
transformPlacement(abstractConnectorPlacement, linkDirection);
|
|
1925
|
+
let bezierBeginPoint = getPointByPlacement(includedElementsRectangle, bezierBeginPlacement);
|
|
1926
|
+
let bezierEndPoint = getPointByPlacement(includedElementsRectangle, bezierEndPlacement);
|
|
1927
|
+
let abstractConnectorPoint = getPointByPlacement(abstractRectangle, abstractConnectorPlacement);
|
|
1928
|
+
let curveDistance = 0;
|
|
1929
|
+
if (isHorizontal) {
|
|
1930
|
+
curveDistance = Math.abs(abstractConnectorPoint[0] - bezierBeginPoint[0]) - linkPadding * 2;
|
|
1931
|
+
}
|
|
1932
|
+
else {
|
|
1933
|
+
curveDistance = Math.abs(abstractConnectorPoint[1] - bezierBeginPoint[1]) - linkPadding * 2;
|
|
1934
|
+
}
|
|
1935
|
+
bezierBeginPoint = movePoint(bezierBeginPoint, linkPadding, linkDirection);
|
|
1936
|
+
let c1 = movePoint(bezierBeginPoint, curveDistance, linkDirection);
|
|
1937
|
+
bezierEndPoint = movePoint(bezierEndPoint, linkPadding, linkDirection);
|
|
1938
|
+
let c2 = movePoint(bezierEndPoint, curveDistance, linkDirection);
|
|
1939
|
+
let bezierConnectorPoint = movePoint(abstractConnectorPoint, -linkPadding, linkDirection);
|
|
1940
|
+
const link = PlaitBoard.getRoughSVG(board).path(`M${bezierBeginPoint[0]},${bezierBeginPoint[1]} Q${c1[0]},${c1[1]} ${bezierConnectorPoint[0]},${bezierConnectorPoint[1]} Q${c2[0]},${c2[1]} ${bezierEndPoint[0]},${bezierEndPoint[1]} M${abstractConnectorPoint[0]},${abstractConnectorPoint[1]} L${bezierConnectorPoint[0]},${bezierConnectorPoint[1]}`, {
|
|
1941
|
+
stroke: GRAY_COLOR,
|
|
1942
|
+
strokeWidth: 2
|
|
1943
|
+
});
|
|
1944
|
+
return link;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
class EmojiDrawer {
|
|
1948
|
+
constructor(board, viewContainerRef) {
|
|
1949
|
+
this.board = board;
|
|
1950
|
+
this.viewContainerRef = viewContainerRef;
|
|
1951
|
+
this.componentRef = null;
|
|
1952
|
+
}
|
|
1953
|
+
draw(emoji, element) {
|
|
1954
|
+
this.destroy();
|
|
1955
|
+
const componentType = this.board.drawEmoji(emoji, element);
|
|
1956
|
+
this.componentRef = this.viewContainerRef.createComponent(componentType);
|
|
1957
|
+
this.componentRef.instance.emojiItem = emoji;
|
|
1958
|
+
const fontSize = PlaitMind.isMind(element) ? 18 : 14;
|
|
1959
|
+
this.componentRef.instance.fontSize = fontSize;
|
|
1960
|
+
}
|
|
1961
|
+
get nativeElement() {
|
|
1962
|
+
if (this.componentRef) {
|
|
1963
|
+
return this.componentRef.instance.nativeElement;
|
|
1964
|
+
}
|
|
1965
|
+
else {
|
|
1966
|
+
return null;
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
destroy() {
|
|
1970
|
+
if (this.componentRef) {
|
|
1971
|
+
this.componentRef.destroy();
|
|
1972
|
+
this.componentRef = null;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
class EmojisDrawer {
|
|
1977
|
+
constructor(board, viewContainerRef) {
|
|
1978
|
+
this.board = board;
|
|
1979
|
+
this.viewContainerRef = viewContainerRef;
|
|
1980
|
+
this.emojiDrawers = [];
|
|
1981
|
+
}
|
|
1982
|
+
drawEmojis(element) {
|
|
1983
|
+
this.destroy();
|
|
1984
|
+
if (MindElement.hasEmojis(element)) {
|
|
1985
|
+
const node = MindElement.getNode(element);
|
|
1986
|
+
this.g = createG();
|
|
1987
|
+
this.g.classList.add('emojis');
|
|
1988
|
+
let { x, y } = getRectangleByNode(MindElement.getNode(element));
|
|
1989
|
+
x = x + NodeSpace.getEmojiHorizontalSpace(element);
|
|
1990
|
+
y = y + NodeSpace.getEmojiVerticalSpace(element);
|
|
1991
|
+
const { width, height } = getEmojisRectangle(element);
|
|
1992
|
+
const fontSize = getEmojiFontSize(element);
|
|
1993
|
+
const foreignObject = createForeignObject(x, y, width, height);
|
|
1994
|
+
this.g.append(foreignObject);
|
|
1995
|
+
const container = document.createElement('div');
|
|
1996
|
+
container.classList.add('node-emojis-container');
|
|
1997
|
+
container.classList.add(`emoji-font-size-${fontSize}`);
|
|
1998
|
+
foreignObject.append(container);
|
|
1999
|
+
this.emojiDrawers = element.data.emojis.map(emojiItem => {
|
|
2000
|
+
const drawer = new EmojiDrawer(this.board, this.viewContainerRef);
|
|
2001
|
+
drawer.draw(emojiItem, element);
|
|
2002
|
+
return drawer;
|
|
2003
|
+
});
|
|
2004
|
+
this.emojiDrawers.forEach(drawer => {
|
|
2005
|
+
container.append(drawer.nativeElement);
|
|
2006
|
+
});
|
|
2007
|
+
return this.g;
|
|
2008
|
+
}
|
|
2009
|
+
return undefined;
|
|
2010
|
+
}
|
|
2011
|
+
destroy() {
|
|
2012
|
+
if (this.g) {
|
|
2013
|
+
this.g.remove();
|
|
2014
|
+
}
|
|
2015
|
+
this.emojiDrawers.forEach(drawer => drawer.destroy());
|
|
2016
|
+
this.emojiDrawers = [];
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
const setLayout = (board, layout, path) => {
|
|
2021
|
+
correctLogicLayoutNode(board, layout, path);
|
|
2022
|
+
Transforms.setNode(board, { layout }, path);
|
|
2023
|
+
};
|
|
2024
|
+
const correctLogicLayoutNode = (board, layout, path) => {
|
|
2025
|
+
const node = PlaitNode.get(board, path);
|
|
2026
|
+
if (node && layout) {
|
|
2027
|
+
node.children?.forEach((value, index) => {
|
|
2028
|
+
if (value.layout) {
|
|
2029
|
+
if ((isHorizontalLogicLayout(layout) && isVerticalLogicLayout(value.layout)) ||
|
|
2030
|
+
(isVerticalLogicLayout(layout) && isHorizontalLogicLayout(value.layout))) {
|
|
2031
|
+
Transforms.setNode(board, { layout: null }, [...path, index]);
|
|
2032
|
+
}
|
|
2033
|
+
if (value.children?.length) {
|
|
2034
|
+
correctLogicLayoutNode(board, layout, [...path, index]);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
|
|
2041
|
+
const setTopic = (board, element, topic, width, height) => {
|
|
2042
|
+
const newElement = {
|
|
2043
|
+
data: { topic },
|
|
2044
|
+
width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
|
|
2045
|
+
height: height / board.viewport.zoom
|
|
2046
|
+
};
|
|
2047
|
+
if (MindElement.hasEmojis(element)) {
|
|
2048
|
+
newElement.data.emojis = element.data.emojis;
|
|
2049
|
+
}
|
|
2050
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2051
|
+
Transforms.setNode(board, newElement, path);
|
|
2052
|
+
};
|
|
2053
|
+
const setTopicSize = (board, element, width, height) => {
|
|
2054
|
+
const newElement = {
|
|
2055
|
+
width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
|
|
2056
|
+
height: height / board.viewport.zoom
|
|
2057
|
+
};
|
|
2058
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2059
|
+
Transforms.setNode(board, newElement, path);
|
|
2060
|
+
};
|
|
2061
|
+
const addEmoji = (board, element, emojiItem) => {
|
|
2062
|
+
const emojis = element.data.emojis || [];
|
|
2063
|
+
emojis.push(emojiItem);
|
|
2064
|
+
const newElement = {
|
|
2065
|
+
data: { topic: element.data.topic, emojis }
|
|
2066
|
+
};
|
|
2067
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2068
|
+
Transforms.setNode(board, newElement, path);
|
|
2069
|
+
};
|
|
2070
|
+
const removeEmoji = (board, element, emojiItem) => {
|
|
2071
|
+
const emojis = element.data.emojis.filter(value => value !== emojiItem);
|
|
2072
|
+
const newElement = {
|
|
2073
|
+
data: { topic: element.data.topic }
|
|
2074
|
+
};
|
|
2075
|
+
if (emojis.length > 0) {
|
|
2076
|
+
newElement.data.emojis = emojis;
|
|
2077
|
+
}
|
|
2078
|
+
const path = PlaitBoard.findPath(board, element);
|
|
2079
|
+
Transforms.setNode(board, newElement, path);
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2082
|
+
const MindTransforms = {
|
|
2083
|
+
setLayout,
|
|
2084
|
+
setTopic,
|
|
2085
|
+
setTopicSize,
|
|
2086
|
+
addEmoji,
|
|
2087
|
+
removeEmoji
|
|
2088
|
+
};
|
|
2089
|
+
|
|
2090
|
+
function drawAbstractIncludedOutline(board, roughSVG, element, handlePosition = AbstractHandlePosition.start, resizingLocation) {
|
|
2091
|
+
const abstractIncludedG = createG();
|
|
2092
|
+
const parentElement = MindElement.getParent(element);
|
|
2093
|
+
const nodeLayout = MindmapQueries.getCorrectLayoutByElement(element);
|
|
2094
|
+
const isHorizontal = isHorizontalLayout(nodeLayout);
|
|
2095
|
+
const includedElements = parentElement.children.slice(element.start, element.end + 1);
|
|
2096
|
+
let abstractRectangle = getRectangleByElements(board, includedElements, true);
|
|
2097
|
+
abstractRectangle = RectangleClient.getOutlineRectangle(abstractRectangle, -ABSTRACT_INCLUDED_OUTLINE_OFFSET);
|
|
2098
|
+
if (resizingLocation) {
|
|
2099
|
+
abstractRectangle = getRectangleByResizingLocation(abstractRectangle, resizingLocation, handlePosition, isHorizontal);
|
|
2100
|
+
}
|
|
2101
|
+
const rectangle = drawAbstractRoundRectangle(roughSVG, abstractRectangle.x, abstractRectangle.y, abstractRectangle.x + abstractRectangle.width, abstractRectangle.y + abstractRectangle.height, isHorizontal, {
|
|
2102
|
+
stroke: PRIMARY_COLOR,
|
|
2103
|
+
strokeWidth: 1,
|
|
2104
|
+
fillStyle: 'solid'
|
|
2105
|
+
});
|
|
2106
|
+
const handleOptions = {
|
|
2107
|
+
stroke: ABSTRACT_HANDLE_COLOR,
|
|
2108
|
+
strokeWidth: 3,
|
|
2109
|
+
fillStyle: 'solid'
|
|
2110
|
+
};
|
|
2111
|
+
const startPlacement = [HorizontalPlacement.center, VerticalPlacement.top];
|
|
2112
|
+
const endPlacement = [HorizontalPlacement.center, VerticalPlacement.bottom];
|
|
2113
|
+
const linkDirection = getLayoutDirection(MindElement.getNode(element), isHorizontal);
|
|
2114
|
+
transformPlacement(startPlacement, linkDirection);
|
|
2115
|
+
transformPlacement(endPlacement, linkDirection);
|
|
2116
|
+
let startCenterPoint = getPointByPlacement(abstractRectangle, startPlacement);
|
|
2117
|
+
let endCenterPoint = getPointByPlacement(abstractRectangle, endPlacement);
|
|
2118
|
+
const startPoint1 = movePoint(startCenterPoint, -ABSTRACT_HANDLE_LENGTH / 2, linkDirection);
|
|
2119
|
+
const startPoint2 = movePoint(startCenterPoint, ABSTRACT_HANDLE_LENGTH / 2, linkDirection);
|
|
2120
|
+
const endPoint1 = movePoint(endCenterPoint, -ABSTRACT_HANDLE_LENGTH / 2, linkDirection);
|
|
2121
|
+
const endPoint2 = movePoint(endCenterPoint, ABSTRACT_HANDLE_LENGTH / 2, linkDirection);
|
|
2122
|
+
const startHandle = roughSVG.line(startPoint1[0], startPoint1[1], startPoint2[0], startPoint2[1], handleOptions);
|
|
2123
|
+
const endHandle = roughSVG.line(endPoint1[0], endPoint1[1], endPoint2[0], endPoint2[1], handleOptions);
|
|
2124
|
+
abstractIncludedG.append(startHandle);
|
|
2125
|
+
abstractIncludedG.append(endHandle);
|
|
2126
|
+
abstractIncludedG.append(rectangle);
|
|
2127
|
+
return abstractIncludedG;
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
class MindNodeComponent extends PlaitPluginElementComponent {
|
|
2131
|
+
get handActive() {
|
|
2132
|
+
return this.board.pointer === PlaitPointerType.hand;
|
|
2133
|
+
}
|
|
2134
|
+
constructor(viewContainerRef, cdr, render2) {
|
|
2135
|
+
super(cdr);
|
|
2136
|
+
this.viewContainerRef = viewContainerRef;
|
|
2137
|
+
this.cdr = cdr;
|
|
2138
|
+
this.render2 = render2;
|
|
2139
|
+
this.isEditable = false;
|
|
2140
|
+
this.activeG = [];
|
|
2141
|
+
this.shapeG = null;
|
|
2142
|
+
this.destroy$ = new Subject();
|
|
2143
|
+
this.trackBy = (index, node) => {
|
|
2144
|
+
return node.origin.id;
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
ngOnInit() {
|
|
2148
|
+
this.emojisDrawer = new EmojisDrawer(this.board, this.viewContainerRef);
|
|
2149
|
+
MINDMAP_ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
2150
|
+
super.ngOnInit();
|
|
2151
|
+
this.node = ELEMENT_TO_NODE.get(this.element);
|
|
2152
|
+
if (!PlaitMind.isMind(this.element)) {
|
|
2153
|
+
this.parent = MindElement.getNode(MindElement.getParent(this.element));
|
|
2154
|
+
}
|
|
2155
|
+
this.index = NODE_TO_INDEX.get(this.element) || 0;
|
|
2156
|
+
this.roughSVG = PlaitBoard.getRoughSVG(this.board);
|
|
2157
|
+
this.parentG = PlaitElement.getComponent(MindElement.getRoot(this.board, this.element)).rootG;
|
|
2158
|
+
this.drawShape();
|
|
2159
|
+
this.drawLink();
|
|
2160
|
+
this.drawRichtext();
|
|
2161
|
+
this.drawEmojis();
|
|
2162
|
+
this.drawActiveG();
|
|
2163
|
+
this.updateActiveClass();
|
|
2164
|
+
this.drawMaskG();
|
|
2165
|
+
this.drawExtend();
|
|
2166
|
+
}
|
|
2167
|
+
onContextChanged(value, previous) {
|
|
2168
|
+
const newNode = ELEMENT_TO_NODE.get(this.element);
|
|
2169
|
+
if (!PlaitMind.isMind(this.element)) {
|
|
2170
|
+
this.parent = MindElement.getNode(MindElement.getParent(this.element));
|
|
2171
|
+
}
|
|
2172
|
+
MINDMAP_ELEMENT_TO_COMPONENT.set(this.element, this);
|
|
2173
|
+
// resolve move node richtext lose issue
|
|
2174
|
+
if (this.node !== newNode) {
|
|
2175
|
+
if (this.foreignObject && this.foreignObject.children.length <= 0) {
|
|
2176
|
+
this.foreignObject?.appendChild(this.richtextComponentRef?.instance.editable);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
const isEquals = MindmapNode.isEquals(this.node, newNode);
|
|
2180
|
+
this.node = newNode;
|
|
2181
|
+
this.drawActiveG();
|
|
2182
|
+
this.updateActiveClass();
|
|
2183
|
+
if (!isEquals) {
|
|
2184
|
+
this.drawShape();
|
|
2185
|
+
this.drawLink();
|
|
2186
|
+
this.updateRichtext();
|
|
2187
|
+
this.drawMaskG();
|
|
2188
|
+
this.drawExtend();
|
|
2189
|
+
this.drawEmojis();
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
drawShape() {
|
|
2193
|
+
this.destroyShape();
|
|
2194
|
+
const shape = getNodeShapeByElement(this.node.origin);
|
|
2195
|
+
switch (shape) {
|
|
2196
|
+
case MindmapNodeShape.roundRectangle:
|
|
2197
|
+
this.shapeG = drawRectangleNode(this.board, this.node);
|
|
2198
|
+
this.g.prepend(this.shapeG);
|
|
2199
|
+
break;
|
|
2200
|
+
default:
|
|
2201
|
+
break;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
drawEmojis() {
|
|
2205
|
+
const g = this.emojisDrawer.drawEmojis(this.element);
|
|
2206
|
+
if (g) {
|
|
2207
|
+
this.g.append(g);
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
destroyShape() {
|
|
2211
|
+
if (this.shapeG) {
|
|
2212
|
+
this.shapeG.remove();
|
|
2213
|
+
this.shapeG = null;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
drawLink() {
|
|
2217
|
+
if (!this.parent) {
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
if (this.linkG) {
|
|
2221
|
+
this.linkG.remove();
|
|
2222
|
+
}
|
|
2223
|
+
const layout = MindmapQueries.getLayoutByElement(this.parent.origin);
|
|
2224
|
+
if (AbstractNode.isAbstract(this.node.origin)) {
|
|
2225
|
+
this.linkG = drawAbstractLink(this.board, this.node, isHorizontalLayout(layout));
|
|
2226
|
+
}
|
|
2227
|
+
else if (MindElement.isIndentedLayout(this.parent.origin)) {
|
|
2228
|
+
this.linkG = drawIndentedLink(this.roughSVG, this.parent, this.node);
|
|
2229
|
+
}
|
|
2230
|
+
else {
|
|
2231
|
+
this.linkG = drawLogicLink(this.roughSVG, this.node, this.parent, isHorizontalLayout(layout));
|
|
2232
|
+
}
|
|
2233
|
+
this.g.append(this.linkG);
|
|
2234
|
+
}
|
|
2235
|
+
destroyLine() {
|
|
2236
|
+
if (this.parent) {
|
|
2237
|
+
if (this.linkG) {
|
|
2238
|
+
this.linkG.remove();
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
drawMaskG() {
|
|
2243
|
+
this.destroyMaskG();
|
|
2244
|
+
const lineWidthOffset = 2;
|
|
2245
|
+
const extendOffset = 15;
|
|
2246
|
+
const nodeLayout = MindmapQueries.getLayoutByElement(this.node.origin);
|
|
2247
|
+
const isTop = isTopLayout(nodeLayout);
|
|
2248
|
+
const isRight = isRightLayout(nodeLayout);
|
|
2249
|
+
const isBottom = isBottomLayout(nodeLayout);
|
|
2250
|
+
const isLeft = isLeftLayout(nodeLayout);
|
|
2251
|
+
const { x, y, width, height } = getRectangleByNode(this.node);
|
|
2252
|
+
let drawX = x;
|
|
2253
|
+
let drawY = y;
|
|
2254
|
+
let drawWidth = x + width;
|
|
2255
|
+
let drawHeight = y + height;
|
|
2256
|
+
switch (true) {
|
|
2257
|
+
case isTop:
|
|
2258
|
+
drawX = x - lineWidthOffset;
|
|
2259
|
+
drawY = y - extendOffset;
|
|
2260
|
+
drawWidth = x + width + lineWidthOffset;
|
|
2261
|
+
drawHeight = y + height + lineWidthOffset;
|
|
2262
|
+
break;
|
|
2263
|
+
case isBottom:
|
|
2264
|
+
drawX = x - lineWidthOffset;
|
|
2265
|
+
drawY = y - lineWidthOffset;
|
|
2266
|
+
drawWidth = x + width + lineWidthOffset;
|
|
2267
|
+
drawHeight = y + height + extendOffset;
|
|
2268
|
+
break;
|
|
2269
|
+
case isLeft:
|
|
2270
|
+
drawX = x - extendOffset;
|
|
2271
|
+
drawY = y - lineWidthOffset;
|
|
2272
|
+
drawWidth = x + width + lineWidthOffset;
|
|
2273
|
+
drawHeight = y + height + lineWidthOffset;
|
|
2274
|
+
break;
|
|
2275
|
+
case isRight:
|
|
2276
|
+
drawX = x - lineWidthOffset;
|
|
2277
|
+
drawY = y - lineWidthOffset;
|
|
2278
|
+
drawWidth = x + width + extendOffset;
|
|
2279
|
+
drawHeight = y + height + lineWidthOffset;
|
|
2280
|
+
break;
|
|
2281
|
+
}
|
|
2282
|
+
this.maskG = drawRoundRectangle(this.roughSVG, drawX, drawY, drawWidth, drawHeight, { stroke: 'none', fill: 'rgba(255,255,255,0)', fillStyle: 'solid' }, true);
|
|
2283
|
+
this.maskG.classList.add('mask');
|
|
2284
|
+
this.maskG.setAttribute('visibility', 'visible');
|
|
2285
|
+
this.g.append(this.maskG);
|
|
2286
|
+
if (this.isEditable) {
|
|
2287
|
+
this.disabledMaskG();
|
|
2288
|
+
}
|
|
2289
|
+
fromEvent(this.maskG, 'mouseenter')
|
|
2290
|
+
.pipe(takeUntil(this.destroy$), filter(() => {
|
|
2291
|
+
return PlaitBoard.isFocus(this.board) && !this.element.isCollapsed && !this.handActive;
|
|
2292
|
+
}))
|
|
2293
|
+
.subscribe(() => {
|
|
2294
|
+
this.g.classList.add('hovered');
|
|
2295
|
+
});
|
|
2296
|
+
fromEvent(this.maskG, 'mouseleave')
|
|
2297
|
+
.pipe(takeUntil(this.destroy$), filter(() => {
|
|
2298
|
+
return PlaitBoard.isFocus(this.board) && !this.element.isCollapsed;
|
|
2299
|
+
}))
|
|
2300
|
+
.subscribe(() => {
|
|
2301
|
+
this.g.classList.remove('hovered');
|
|
2302
|
+
});
|
|
2303
|
+
}
|
|
2304
|
+
destroyMaskG() {
|
|
2305
|
+
if (this.maskG) {
|
|
2306
|
+
this.maskG.remove();
|
|
2307
|
+
this.g.classList.remove('hovered');
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
enableMaskG() {
|
|
2311
|
+
if (this.maskG) {
|
|
2312
|
+
this.maskG.setAttribute('visibility', 'visible');
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
disabledMaskG() {
|
|
2316
|
+
if (this.maskG) {
|
|
2317
|
+
this.maskG.setAttribute('visibility', 'hidden');
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
drawActiveG() {
|
|
2321
|
+
this.destroyActiveG();
|
|
2322
|
+
this.abstractIncludedOutlineG?.remove();
|
|
2323
|
+
if (this.selected) {
|
|
2324
|
+
if (AbstractNode.isAbstract(this.element)) {
|
|
2325
|
+
this.updateAbstractIncludedOutline();
|
|
2326
|
+
}
|
|
2327
|
+
let { x, y, width, height } = getRectangleByNode(this.node);
|
|
2328
|
+
const selectedStrokeG = drawRoundRectangle(this.roughSVG, x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, strokeWidth: 2, fill: '' }, true);
|
|
2329
|
+
// 影响 mask 移入移出事件
|
|
2330
|
+
selectedStrokeG.style.pointerEvents = 'none';
|
|
2331
|
+
this.g.appendChild(selectedStrokeG);
|
|
2332
|
+
this.activeG.push(selectedStrokeG);
|
|
2333
|
+
if (this.richtextComponentRef?.instance.plaitReadonly === true) {
|
|
2334
|
+
const selectedBackgroundG = drawRoundRectangle(this.roughSVG, x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, fill: PRIMARY_COLOR, fillStyle: 'solid' }, true);
|
|
2335
|
+
selectedBackgroundG.style.opacity = '0.15';
|
|
2336
|
+
// 影响双击事件
|
|
2337
|
+
selectedBackgroundG.style.pointerEvents = 'none';
|
|
2338
|
+
this.g.appendChild(selectedBackgroundG);
|
|
2339
|
+
this.activeG.push(selectedBackgroundG, selectedStrokeG);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
destroyActiveG() {
|
|
2344
|
+
this.activeG.forEach(g => g.remove());
|
|
2345
|
+
this.activeG = [];
|
|
2346
|
+
}
|
|
2347
|
+
updateActiveClass() {
|
|
2348
|
+
if (!this.g) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
if (this.selected) {
|
|
2352
|
+
this.render2.addClass(this.g, 'active');
|
|
2353
|
+
}
|
|
2354
|
+
else {
|
|
2355
|
+
this.render2.removeClass(this.g, 'active');
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
drawRichtext() {
|
|
2359
|
+
const { richtextG, richtextComponentRef, foreignObject } = drawMindmapNodeRichtext(this.node, this.viewContainerRef);
|
|
2360
|
+
this.richtextComponentRef = richtextComponentRef;
|
|
2361
|
+
this.richtextG = richtextG;
|
|
2362
|
+
this.foreignObject = foreignObject;
|
|
2363
|
+
this.render2.addClass(richtextG, 'richtext');
|
|
2364
|
+
this.g.append(richtextG);
|
|
2365
|
+
}
|
|
2366
|
+
drawQuickInsert(offset = 0) {
|
|
2367
|
+
if (this.board.options.readonly) {
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
const quickInsertG = createG();
|
|
2371
|
+
quickInsertG.classList.add('quick-insert');
|
|
2372
|
+
this.extendG?.append(quickInsertG);
|
|
2373
|
+
const { x, y, width, height } = getRectangleByNode(this.node);
|
|
2374
|
+
/**
|
|
2375
|
+
* 方位:
|
|
2376
|
+
* 1. 左、左上、左下
|
|
2377
|
+
* 2. 右、右上、右下
|
|
2378
|
+
* 3. 上、上左、上右
|
|
2379
|
+
* 4. 下、下左、下右
|
|
2380
|
+
*/
|
|
2381
|
+
const shape = getNodeShapeByElement(this.node.origin);
|
|
2382
|
+
// 形状是矩形要偏移边框的线宽
|
|
2383
|
+
const strokeWidth = this.node.origin.linkLineWidth ? this.node.origin.linkLineWidth : STROKE_WIDTH;
|
|
2384
|
+
let offsetBorderLineWidth = 0;
|
|
2385
|
+
if (shape === MindmapNodeShape.roundRectangle && offset === 0) {
|
|
2386
|
+
offsetBorderLineWidth = strokeWidth;
|
|
2387
|
+
}
|
|
2388
|
+
let offsetRootBorderLineWidth = 0;
|
|
2389
|
+
if (this.node.origin.isRoot) {
|
|
2390
|
+
offsetRootBorderLineWidth = strokeWidth;
|
|
2391
|
+
}
|
|
2392
|
+
// 当没有子节点时,需要缩小的偏移量
|
|
2393
|
+
const extraOffset = 3;
|
|
2394
|
+
const underlineCoordinates = {
|
|
2395
|
+
// 画线方向:右向左 <--
|
|
2396
|
+
[MindmapLayoutType.left]: {
|
|
2397
|
+
// EXTEND_RADIUS * 0.5 是 左方向,折叠/收起的偏移量
|
|
2398
|
+
startX: x - (offset > 0 ? offset + EXTEND_RADIUS * 0.5 : 0) - offsetRootBorderLineWidth,
|
|
2399
|
+
startY: y + height,
|
|
2400
|
+
endX: x -
|
|
2401
|
+
offsetBorderLineWidth -
|
|
2402
|
+
offsetRootBorderLineWidth -
|
|
2403
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) -
|
|
2404
|
+
EXTEND_RADIUS,
|
|
2405
|
+
endY: y + height
|
|
2406
|
+
},
|
|
2407
|
+
// 画线方向:左向右 -->
|
|
2408
|
+
[MindmapLayoutType.right]: {
|
|
2409
|
+
startX: x + width + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
|
|
2410
|
+
startY: y + height,
|
|
2411
|
+
endX: x +
|
|
2412
|
+
width +
|
|
2413
|
+
offsetBorderLineWidth +
|
|
2414
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
|
|
2415
|
+
EXTEND_RADIUS +
|
|
2416
|
+
offsetRootBorderLineWidth,
|
|
2417
|
+
endY: y + height
|
|
2418
|
+
},
|
|
2419
|
+
// 画线方向:下向上 -->
|
|
2420
|
+
[MindmapLayoutType.upward]: {
|
|
2421
|
+
startX: x + width * 0.5,
|
|
2422
|
+
startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
|
|
2423
|
+
endX: x + width * 0.5,
|
|
2424
|
+
endY: y -
|
|
2425
|
+
offsetBorderLineWidth -
|
|
2426
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) -
|
|
2427
|
+
EXTEND_RADIUS -
|
|
2428
|
+
offsetRootBorderLineWidth
|
|
2429
|
+
},
|
|
2430
|
+
// 画线方向:上向下 -->
|
|
2431
|
+
[MindmapLayoutType.downward]: {
|
|
2432
|
+
startX: x + width * 0.5,
|
|
2433
|
+
startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
|
|
2434
|
+
endX: x + width * 0.5,
|
|
2435
|
+
endY: y +
|
|
2436
|
+
height +
|
|
2437
|
+
offsetBorderLineWidth +
|
|
2438
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
|
|
2439
|
+
EXTEND_RADIUS +
|
|
2440
|
+
offsetRootBorderLineWidth
|
|
2441
|
+
},
|
|
2442
|
+
[MindmapLayoutType.leftBottomIndented]: {
|
|
2443
|
+
startX: x + width * 0.5,
|
|
2444
|
+
startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
|
|
2445
|
+
endX: x + width * 0.5,
|
|
2446
|
+
endY: y +
|
|
2447
|
+
height +
|
|
2448
|
+
offsetBorderLineWidth +
|
|
2449
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
|
|
2450
|
+
EXTEND_RADIUS +
|
|
2451
|
+
offsetRootBorderLineWidth
|
|
2452
|
+
},
|
|
2453
|
+
[MindmapLayoutType.leftTopIndented]: {
|
|
2454
|
+
startX: x + width * 0.5,
|
|
2455
|
+
startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
|
|
2456
|
+
endX: x + width * 0.5,
|
|
2457
|
+
endY: y -
|
|
2458
|
+
offsetBorderLineWidth -
|
|
2459
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) -
|
|
2460
|
+
EXTEND_RADIUS -
|
|
2461
|
+
offsetRootBorderLineWidth
|
|
2462
|
+
},
|
|
2463
|
+
[MindmapLayoutType.rightBottomIndented]: {
|
|
2464
|
+
startX: x + width * 0.5,
|
|
2465
|
+
startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
|
|
2466
|
+
endX: x + width * 0.5,
|
|
2467
|
+
endY: y +
|
|
2468
|
+
height +
|
|
2469
|
+
offsetBorderLineWidth +
|
|
2470
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
|
|
2471
|
+
EXTEND_RADIUS +
|
|
2472
|
+
offsetRootBorderLineWidth
|
|
2473
|
+
},
|
|
2474
|
+
[MindmapLayoutType.rightTopIndented]: {
|
|
2475
|
+
startX: x + width * 0.5,
|
|
2476
|
+
startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
|
|
2477
|
+
endX: x + width * 0.5,
|
|
2478
|
+
endY: y -
|
|
2479
|
+
offsetBorderLineWidth -
|
|
2480
|
+
(offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) -
|
|
2481
|
+
EXTEND_RADIUS -
|
|
2482
|
+
offsetRootBorderLineWidth
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2485
|
+
if (shape === MindmapNodeShape.roundRectangle || this.node.origin.isRoot) {
|
|
2486
|
+
underlineCoordinates[MindmapLayoutType.left].startY -= height * 0.5;
|
|
2487
|
+
underlineCoordinates[MindmapLayoutType.left].endY -= height * 0.5;
|
|
2488
|
+
underlineCoordinates[MindmapLayoutType.right].startY -= height * 0.5;
|
|
2489
|
+
underlineCoordinates[MindmapLayoutType.right].endY -= height * 0.5;
|
|
2490
|
+
}
|
|
2491
|
+
const stroke = this.node.origin.isRoot
|
|
2492
|
+
? getRootLinkLineColorByMindmapElement(this.element)
|
|
2493
|
+
: getLinkLineColorByMindmapElement(this.element);
|
|
2494
|
+
let nodeLayout = MindmapQueries.getCorrectLayoutByElement(this.node.origin);
|
|
2495
|
+
if (this.node.origin.isRoot && isStandardLayout(nodeLayout)) {
|
|
2496
|
+
const root = this.node.origin;
|
|
2497
|
+
nodeLayout = root.children.length >= root.rightNodeCount ? MindmapLayoutType.left : MindmapLayoutType.right;
|
|
2498
|
+
}
|
|
2499
|
+
const underlineCoordinate = underlineCoordinates[nodeLayout];
|
|
2500
|
+
if (underlineCoordinate) {
|
|
2501
|
+
const underline = this.roughSVG.line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke, strokeWidth });
|
|
2502
|
+
const circleCoordinates = {
|
|
2503
|
+
startX: underlineCoordinate.endX,
|
|
2504
|
+
startY: underlineCoordinate.endY
|
|
2505
|
+
};
|
|
2506
|
+
const circle = this.roughSVG.circle(circleCoordinates.startX, circleCoordinates.startY, EXTEND_RADIUS, {
|
|
2507
|
+
fill: QUICK_INSERT_CIRCLE_COLOR,
|
|
2508
|
+
stroke: QUICK_INSERT_CIRCLE_COLOR,
|
|
2509
|
+
fillStyle: 'solid'
|
|
2510
|
+
});
|
|
2511
|
+
const innerCrossCoordinates = {
|
|
2512
|
+
horizontal: {
|
|
2513
|
+
startX: circleCoordinates.startX - EXTEND_RADIUS * 0.5 + 3,
|
|
2514
|
+
startY: circleCoordinates.startY,
|
|
2515
|
+
endX: circleCoordinates.startX + EXTEND_RADIUS * 0.5 - 3,
|
|
2516
|
+
endY: circleCoordinates.startY
|
|
2517
|
+
},
|
|
2518
|
+
vertical: {
|
|
2519
|
+
startX: circleCoordinates.startX,
|
|
2520
|
+
startY: circleCoordinates.startY - EXTEND_RADIUS * 0.5 + 3,
|
|
2521
|
+
endX: circleCoordinates.startX,
|
|
2522
|
+
endY: circleCoordinates.startY + EXTEND_RADIUS * 0.5 - 3
|
|
2523
|
+
}
|
|
2524
|
+
};
|
|
2525
|
+
const innerCrossHLine = this.roughSVG.line(innerCrossCoordinates.horizontal.startX, innerCrossCoordinates.horizontal.startY, innerCrossCoordinates.horizontal.endX, innerCrossCoordinates.horizontal.endY, {
|
|
2526
|
+
stroke: QUICK_INSERT_INNER_CROSS_COLOR,
|
|
2527
|
+
strokeWidth: 2
|
|
2528
|
+
});
|
|
2529
|
+
const innerRingVLine = this.roughSVG.line(innerCrossCoordinates.vertical.startX, innerCrossCoordinates.vertical.startY, innerCrossCoordinates.vertical.endX, innerCrossCoordinates.vertical.endY, {
|
|
2530
|
+
stroke: QUICK_INSERT_INNER_CROSS_COLOR,
|
|
2531
|
+
strokeWidth: 2
|
|
2532
|
+
});
|
|
2533
|
+
quickInsertG.appendChild(underline);
|
|
2534
|
+
quickInsertG.appendChild(circle);
|
|
2535
|
+
quickInsertG.appendChild(innerCrossHLine);
|
|
2536
|
+
quickInsertG.appendChild(innerRingVLine);
|
|
2537
|
+
}
|
|
2538
|
+
fromEvent(quickInsertG, 'mouseup')
|
|
2539
|
+
.pipe(take(1))
|
|
2540
|
+
.subscribe(() => {
|
|
2541
|
+
const path = PlaitBoard.findPath(this.board, this.element).concat(this.element.children.filter(child => !AbstractNode.isAbstract(child)).length);
|
|
2542
|
+
insertMindElement(this.board, this.node.origin, path);
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
drawExtend() {
|
|
2546
|
+
// destroy
|
|
2547
|
+
this.destroyExtend();
|
|
2548
|
+
// create extend
|
|
2549
|
+
this.extendG = createG();
|
|
2550
|
+
const collapseG = createG();
|
|
2551
|
+
this.extendG.classList.add('extend');
|
|
2552
|
+
collapseG.classList.add('collapse-container');
|
|
2553
|
+
this.g.append(this.extendG);
|
|
2554
|
+
this.extendG.append(collapseG);
|
|
2555
|
+
if (this.node.origin.isRoot) {
|
|
2556
|
+
this.drawQuickInsert();
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
// interactive
|
|
2560
|
+
fromEvent(collapseG, 'mouseup')
|
|
2561
|
+
.pipe(filter(() => !this.handActive || this.board.options.readonly), take(1))
|
|
2562
|
+
.subscribe(() => {
|
|
2563
|
+
const isCollapsed = !this.node.origin.isCollapsed;
|
|
2564
|
+
const newElement = { isCollapsed };
|
|
2565
|
+
const path = PlaitBoard.findPath(this.board, this.element);
|
|
2566
|
+
Transforms.setNode(this.board, newElement, path);
|
|
2567
|
+
});
|
|
2568
|
+
const { x, y, width, height } = getRectangleByNode(this.node);
|
|
2569
|
+
const stroke = getLinkLineColorByMindmapElement(this.element);
|
|
2570
|
+
const strokeWidth = this.node.origin.linkLineWidth ? this.node.origin.linkLineWidth : STROKE_WIDTH;
|
|
2571
|
+
const extendY = y + height / 2;
|
|
2572
|
+
const nodeLayout = MindmapQueries.getCorrectLayoutByElement(this.element);
|
|
2573
|
+
let extendLineXY = [
|
|
2574
|
+
[x + width, extendY],
|
|
2575
|
+
[x + width + EXTEND_OFFSET, extendY]
|
|
2576
|
+
];
|
|
2577
|
+
let arrowYOffset = [-4, 1, -0.6, 4];
|
|
2578
|
+
let arrowXOffset = [10, 5.5, 5.5, 10];
|
|
2579
|
+
let extendLineXOffset = [0, 0];
|
|
2580
|
+
let extendLineYOffset = [0, 0];
|
|
2581
|
+
let circleOffset = [EXTEND_RADIUS / 2, 0];
|
|
2582
|
+
if (isHorizontalLayout(nodeLayout) && !isIndentedLayout(nodeLayout)) {
|
|
2583
|
+
extendLineYOffset =
|
|
2584
|
+
getNodeShapeByElement(this.node.origin) === MindmapNodeShape.roundRectangle
|
|
2585
|
+
? [0, 0]
|
|
2586
|
+
: [height / 2, height / 2];
|
|
2587
|
+
if (isLeftLayout(nodeLayout)) {
|
|
2588
|
+
//左
|
|
2589
|
+
extendLineXOffset = [-width, -width - EXTEND_OFFSET * 2];
|
|
2590
|
+
circleOffset = [-EXTEND_RADIUS / 2, 0];
|
|
2591
|
+
arrowXOffset = [-10, -5.5, -5.5, -10];
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
else {
|
|
2595
|
+
arrowXOffset = [-4, 0.6, -1, 4];
|
|
2596
|
+
if (isTopLayout(nodeLayout)) {
|
|
2597
|
+
//上
|
|
2598
|
+
extendLineXOffset = [-width / 2, -width / 2 - EXTEND_OFFSET];
|
|
2599
|
+
extendLineYOffset = [-height / 2, -height / 2 - EXTEND_OFFSET];
|
|
2600
|
+
arrowYOffset = [-10, -5.5, -5.5, -10];
|
|
2601
|
+
circleOffset = [0, -EXTEND_RADIUS / 2];
|
|
2602
|
+
}
|
|
2603
|
+
else {
|
|
2604
|
+
//下
|
|
2605
|
+
extendLineXOffset = [-width / 2, -width / 2 - EXTEND_OFFSET];
|
|
2606
|
+
extendLineYOffset = [height / 2, height / 2 + EXTEND_OFFSET];
|
|
2607
|
+
arrowYOffset = [10, 5.5, 5.5, 10];
|
|
2608
|
+
circleOffset = [0, EXTEND_RADIUS / 2];
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
extendLineXY = [
|
|
2612
|
+
[extendLineXY[0][0] + extendLineXOffset[0], extendLineXY[0][1] + extendLineYOffset[0]],
|
|
2613
|
+
[extendLineXY[1][0] + extendLineXOffset[1], extendLineXY[1][1] + extendLineYOffset[1]]
|
|
2614
|
+
];
|
|
2615
|
+
const extendLine = this.roughSVG.line(extendLineXY[0][0], extendLineXY[0][1], extendLineXY[1][0], extendLineXY[1][1], {
|
|
2616
|
+
strokeWidth,
|
|
2617
|
+
stroke
|
|
2618
|
+
});
|
|
2619
|
+
//绘制箭头
|
|
2620
|
+
const hideArrowTopLine = this.roughSVG.line(extendLineXY[1][0] + arrowXOffset[0], extendLineXY[1][1] + arrowYOffset[0], extendLineXY[1][0] + arrowXOffset[1], extendLineXY[1][1] + arrowYOffset[1], {
|
|
2621
|
+
stroke,
|
|
2622
|
+
strokeWidth: 2
|
|
2623
|
+
});
|
|
2624
|
+
const hideArrowBottomLine = this.roughSVG.line(extendLineXY[1][0] + arrowXOffset[2], extendLineXY[1][1] + arrowYOffset[2], extendLineXY[1][0] + arrowXOffset[3], extendLineXY[1][1] + arrowYOffset[3], {
|
|
2625
|
+
stroke,
|
|
2626
|
+
strokeWidth: 2
|
|
2627
|
+
});
|
|
2628
|
+
if (this.node.origin.isCollapsed) {
|
|
2629
|
+
const badge = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS, {
|
|
2630
|
+
fill: stroke,
|
|
2631
|
+
stroke,
|
|
2632
|
+
fillStyle: 'solid'
|
|
2633
|
+
});
|
|
2634
|
+
let numberOffset = 0;
|
|
2635
|
+
if (getChildrenCount(this.node.origin) >= 10)
|
|
2636
|
+
numberOffset = -2;
|
|
2637
|
+
if (getChildrenCount(this.node.origin) === 1)
|
|
2638
|
+
numberOffset = 1;
|
|
2639
|
+
const badgeText = createText(extendLineXY[1][0] + circleOffset[0] - 4 + numberOffset, extendLineXY[1][1] + circleOffset[1] + 4, stroke, `${getChildrenCount(this.node.origin)}`);
|
|
2640
|
+
this.g.classList.add('collapsed');
|
|
2641
|
+
badge.setAttribute('style', 'opacity: 0.15');
|
|
2642
|
+
badgeText.setAttribute('style', 'font-size: 12px');
|
|
2643
|
+
collapseG.appendChild(badge);
|
|
2644
|
+
collapseG.appendChild(badgeText);
|
|
2645
|
+
collapseG.appendChild(extendLine);
|
|
2646
|
+
}
|
|
2647
|
+
else {
|
|
2648
|
+
this.g.classList.remove('collapsed');
|
|
2649
|
+
if (this.node.origin.children.length > 0) {
|
|
2650
|
+
const hideCircleG = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS - 1, {
|
|
2651
|
+
fill: '#fff',
|
|
2652
|
+
stroke,
|
|
2653
|
+
strokeWidth,
|
|
2654
|
+
fillStyle: 'solid'
|
|
2655
|
+
});
|
|
2656
|
+
collapseG.appendChild(hideCircleG);
|
|
2657
|
+
collapseG.appendChild(hideArrowTopLine);
|
|
2658
|
+
collapseG.appendChild(hideArrowBottomLine);
|
|
2659
|
+
this.drawQuickInsert(EXTEND_RADIUS);
|
|
2660
|
+
}
|
|
2661
|
+
else {
|
|
2662
|
+
this.drawQuickInsert();
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
destroyExtend() {
|
|
2667
|
+
if (this.extendG) {
|
|
2668
|
+
this.extendG.remove();
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
destroyRichtext() {
|
|
2672
|
+
if (this.richtextG) {
|
|
2673
|
+
this.richtextG.remove();
|
|
2674
|
+
}
|
|
2675
|
+
if (this.richtextComponentRef) {
|
|
2676
|
+
this.richtextComponentRef.destroy();
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
updateAbstractIncludedOutline(resizingLocation, handlePosition) {
|
|
2680
|
+
this.abstractIncludedOutlineG?.remove();
|
|
2681
|
+
this.abstractIncludedOutlineG = drawAbstractIncludedOutline(this.board, this.roughSVG, this.element, handlePosition, resizingLocation);
|
|
2682
|
+
PlaitBoard.getHost(this.board).append(this.abstractIncludedOutlineG);
|
|
2683
|
+
}
|
|
2684
|
+
updateRichtext() {
|
|
2685
|
+
updateRichText(this.node.origin.data.topic, this.richtextComponentRef);
|
|
2686
|
+
updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
|
|
2687
|
+
}
|
|
2688
|
+
startEditText(isEnd, isClear) {
|
|
2689
|
+
if (!this.richtextComponentRef) {
|
|
2690
|
+
throw new Error('undefined richtextComponentRef');
|
|
2691
|
+
}
|
|
2692
|
+
const richtextInstance = this.richtextComponentRef.instance;
|
|
2693
|
+
this.isEditable = true;
|
|
2694
|
+
IS_TEXT_EDITABLE.set(this.board, true);
|
|
2695
|
+
this.disabledMaskG();
|
|
2696
|
+
updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
|
|
2697
|
+
if (richtextInstance.plaitReadonly) {
|
|
2698
|
+
richtextInstance.plaitReadonly = false;
|
|
2699
|
+
this.richtextComponentRef.changeDetectorRef.detectChanges();
|
|
2700
|
+
this.drawActiveG();
|
|
2701
|
+
const location = isEnd ? Editor.end(richtextInstance.editor, [0]) : [0];
|
|
2702
|
+
setFullSelectionAndFocus(richtextInstance.editor, location);
|
|
2703
|
+
if (isClear) {
|
|
2704
|
+
Editor.deleteBackward(richtextInstance.editor);
|
|
2705
|
+
}
|
|
2706
|
+
// handle invalid width and height (old data)
|
|
2707
|
+
let { width, height } = getRichtextContentSize(richtextInstance.editable);
|
|
2708
|
+
if (width !== this.element.width || height !== this.element.height) {
|
|
2709
|
+
MindTransforms.setTopicSize(this.board, this.element, width, height);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
let richtext = richtextInstance.plaitValue;
|
|
2713
|
+
// use debounceTime to wait DOM render complete
|
|
2714
|
+
const valueChange$ = richtextInstance.plaitChange
|
|
2715
|
+
.pipe(debounceTime(0), filter(event => {
|
|
2716
|
+
// 过滤掉 operations 中全是 set_selection 的操作
|
|
2717
|
+
return !event.operations.every(op => Operation.isSelectionOperation(op));
|
|
2718
|
+
}))
|
|
2719
|
+
.subscribe(event => {
|
|
2720
|
+
if (richtext === event.value) {
|
|
2721
|
+
return;
|
|
2722
|
+
}
|
|
2723
|
+
this.updateRichtext();
|
|
2724
|
+
// 更新富文本、更新宽高
|
|
2725
|
+
let { width, height } = getRichtextContentSize(richtextInstance.editable);
|
|
2726
|
+
MindTransforms.setTopic(this.board, this.element, event.value, width, height);
|
|
2727
|
+
MERGING.set(this.board, true);
|
|
2728
|
+
});
|
|
2729
|
+
const composition$ = richtextInstance.plaitComposition.pipe(debounceTime(0)).subscribe(event => {
|
|
2730
|
+
let { width, height } = getRichtextContentSize(richtextInstance.editable);
|
|
2731
|
+
if (width < NODE_MIN_WIDTH) {
|
|
2732
|
+
width = NODE_MIN_WIDTH;
|
|
2733
|
+
}
|
|
2734
|
+
if (event.isComposing && (width !== this.node.origin.width || height !== this.node.origin.height)) {
|
|
2735
|
+
const newElement = {
|
|
2736
|
+
width: width / this.board.viewport.zoom,
|
|
2737
|
+
height: height / this.board.viewport.zoom
|
|
2738
|
+
};
|
|
2739
|
+
const path = PlaitBoard.findPath(this.board, this.element);
|
|
2740
|
+
Transforms.setNode(this.board, newElement, path);
|
|
2741
|
+
MERGING.set(this.board, true);
|
|
2742
|
+
}
|
|
2743
|
+
});
|
|
2744
|
+
const mousedown$ = fromEvent(document, 'mousedown').subscribe((event) => {
|
|
2745
|
+
const point = transformPoint(this.board, toPoint(event.x, event.y, PlaitBoard.getHost(this.board)));
|
|
2746
|
+
const clickInNode = hitMindmapElement(this.board, point, this.element);
|
|
2747
|
+
if (clickInNode && !hasEditableTarget(richtextInstance.editor, event.target)) {
|
|
2748
|
+
event.preventDefault();
|
|
2749
|
+
}
|
|
2750
|
+
else if (!clickInNode) {
|
|
2751
|
+
// handle composition input state, like: Chinese IME Composition Input
|
|
2752
|
+
timer(0).subscribe(() => {
|
|
2753
|
+
exitHandle();
|
|
2754
|
+
this.enableMaskG();
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
});
|
|
2758
|
+
const editor = richtextInstance.editor;
|
|
2759
|
+
const { keydown } = editor;
|
|
2760
|
+
editor.keydown = (event) => {
|
|
2761
|
+
if (event.isComposing) {
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
if (event.key === 'Escape') {
|
|
2765
|
+
event.preventDefault();
|
|
2766
|
+
event.stopPropagation();
|
|
2767
|
+
exitHandle();
|
|
2768
|
+
this.drawActiveG();
|
|
2769
|
+
this.enableMaskG();
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2772
|
+
if (event.key === 'Enter' && !event.shiftKey) {
|
|
2773
|
+
event.preventDefault();
|
|
2774
|
+
event.stopPropagation();
|
|
2775
|
+
exitHandle();
|
|
2776
|
+
this.drawActiveG();
|
|
2777
|
+
this.enableMaskG();
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
if (event.key === 'Tab') {
|
|
2781
|
+
event.preventDefault();
|
|
2782
|
+
event.stopPropagation();
|
|
2783
|
+
exitHandle();
|
|
2784
|
+
this.drawActiveG();
|
|
2785
|
+
this.drawMaskG();
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
const exitHandle = () => {
|
|
2789
|
+
// unsubscribe
|
|
2790
|
+
valueChange$.unsubscribe();
|
|
2791
|
+
composition$.unsubscribe();
|
|
2792
|
+
mousedown$.unsubscribe();
|
|
2793
|
+
editor.keydown = keydown; // reset keydown
|
|
2794
|
+
// editable status
|
|
2795
|
+
MERGING.set(this.board, false);
|
|
2796
|
+
richtextInstance.plaitReadonly = true;
|
|
2797
|
+
this.richtextComponentRef?.changeDetectorRef.markForCheck();
|
|
2798
|
+
this.isEditable = false;
|
|
2799
|
+
updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
|
|
2800
|
+
IS_TEXT_EDITABLE.set(this.board, false);
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2803
|
+
ngOnDestroy() {
|
|
2804
|
+
super.ngOnDestroy();
|
|
2805
|
+
this.abstractIncludedOutlineG?.remove();
|
|
2806
|
+
this.destroyRichtext();
|
|
2807
|
+
this.destroy$.next();
|
|
2808
|
+
this.destroy$.complete();
|
|
2809
|
+
if (ELEMENT_TO_NODE.get(this.element) === this.node) {
|
|
2810
|
+
ELEMENT_TO_NODE.delete(this.element);
|
|
2811
|
+
}
|
|
2812
|
+
if (MINDMAP_ELEMENT_TO_COMPONENT.get(this.element) === this) {
|
|
2813
|
+
MINDMAP_ELEMENT_TO_COMPONENT.delete(this.element);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
MindNodeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindNodeComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
|
|
2818
|
+
MindNodeComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: MindNodeComponent, selector: "plait-mindmap-node", usesInheritance: true, ngImport: i0, template: `
|
|
2819
|
+
<plait-children
|
|
2820
|
+
*ngIf="!element.isCollapsed"
|
|
2821
|
+
[board]="board"
|
|
2822
|
+
[parent]="element"
|
|
2823
|
+
[effect]="effect"
|
|
2824
|
+
[parentG]="parentG"
|
|
2825
|
+
></plait-children>
|
|
2826
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2827
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindNodeComponent, decorators: [{
|
|
2828
|
+
type: Component,
|
|
2829
|
+
args: [{
|
|
2830
|
+
selector: 'plait-mindmap-node',
|
|
2831
|
+
template: `
|
|
2832
|
+
<plait-children
|
|
2833
|
+
*ngIf="!element.isCollapsed"
|
|
2834
|
+
[board]="board"
|
|
2835
|
+
[parent]="element"
|
|
2836
|
+
[effect]="effect"
|
|
2837
|
+
[parentG]="parentG"
|
|
2838
|
+
></plait-children>
|
|
2839
|
+
`,
|
|
2840
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
2841
|
+
}]
|
|
2842
|
+
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }]; } });
|
|
2843
|
+
|
|
2844
|
+
const getLayoutOptions = () => {
|
|
2845
|
+
function getMainAxle(element, parent) {
|
|
2846
|
+
const strokeWidth = element.strokeWidth || STROKE_WIDTH;
|
|
2847
|
+
if (element.isRoot) {
|
|
2848
|
+
return BASE * 12;
|
|
2849
|
+
}
|
|
2850
|
+
if (parent && parent.isRoot()) {
|
|
2851
|
+
return BASE * 3 + strokeWidth / 2;
|
|
2852
|
+
}
|
|
2853
|
+
return BASE * 3 + strokeWidth / 2;
|
|
2854
|
+
}
|
|
2855
|
+
function getSecondAxle(element, parent) {
|
|
2856
|
+
const strokeWidth = element.strokeWidth || STROKE_WIDTH;
|
|
2857
|
+
if (element.isRoot) {
|
|
2858
|
+
return BASE * 10 + strokeWidth / 2;
|
|
2859
|
+
}
|
|
2860
|
+
return BASE * 6 + strokeWidth / 2;
|
|
2861
|
+
}
|
|
2862
|
+
return {
|
|
2863
|
+
getHeight(element) {
|
|
2864
|
+
return NodeSpace.getNodeHeight(element);
|
|
2865
|
+
},
|
|
2866
|
+
getWidth(element) {
|
|
2867
|
+
return NodeSpace.getNodeWidth(element);
|
|
2868
|
+
},
|
|
2869
|
+
getHorizontalGap(element, parent) {
|
|
2870
|
+
const _layout = (parent && parent.layout) || getRootLayout(element);
|
|
2871
|
+
const isHorizontal = isHorizontalLayout(_layout);
|
|
2872
|
+
const strokeWidth = element.strokeWidth || STROKE_WIDTH;
|
|
2873
|
+
if (isIndentedLayout(_layout)) {
|
|
2874
|
+
return BASE * 4 + strokeWidth;
|
|
2875
|
+
}
|
|
2876
|
+
if (!isHorizontal) {
|
|
2877
|
+
return getMainAxle(element, parent);
|
|
2878
|
+
}
|
|
2879
|
+
else {
|
|
2880
|
+
return getSecondAxle(element, parent);
|
|
2881
|
+
}
|
|
2882
|
+
},
|
|
2883
|
+
getVerticalGap(element, parent) {
|
|
2884
|
+
const _layout = (parent && parent.layout) || getRootLayout(element);
|
|
2885
|
+
if (isIndentedLayout(_layout)) {
|
|
2886
|
+
return BASE;
|
|
2887
|
+
}
|
|
2888
|
+
const isHorizontal = isHorizontalLayout(_layout);
|
|
2889
|
+
if (isHorizontal) {
|
|
2890
|
+
return getMainAxle(element, parent);
|
|
2891
|
+
}
|
|
2892
|
+
else {
|
|
2893
|
+
return getSecondAxle(element, parent);
|
|
2894
|
+
}
|
|
2895
|
+
},
|
|
2896
|
+
getVerticalConnectingPosition(element, parent) {
|
|
2897
|
+
if (element.shape === MindmapNodeShape.underline && parent && isHorizontalLogicLayout(parent.layout)) {
|
|
2898
|
+
return ConnectingPosition.bottom;
|
|
2899
|
+
}
|
|
2900
|
+
return undefined;
|
|
2901
|
+
},
|
|
2902
|
+
getExtendHeight(node) {
|
|
2903
|
+
return BASE * 6;
|
|
2904
|
+
},
|
|
2905
|
+
getIndentedCrossLevelGap() {
|
|
2906
|
+
return BASE * 2;
|
|
2907
|
+
}
|
|
2908
|
+
};
|
|
2909
|
+
};
|
|
2910
|
+
|
|
2911
|
+
class PlaitMindComponent extends MindNodeComponent {
|
|
2912
|
+
ngOnInit() {
|
|
2913
|
+
this.updateMindmap();
|
|
2914
|
+
super.ngOnInit();
|
|
2915
|
+
}
|
|
2916
|
+
beforeContextChange(value) {
|
|
2917
|
+
if (value.element !== this.element && this.initialized) {
|
|
2918
|
+
this.updateMindmap(value.element);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
updateMindmap(element = this.element) {
|
|
2922
|
+
const mindLayoutType = element.layout || getDefaultMindmapLayout();
|
|
2923
|
+
this.root = GlobalLayout.layout(element, getLayoutOptions(), mindLayoutType);
|
|
2924
|
+
this.updateMindmapLocation(element);
|
|
2925
|
+
}
|
|
2926
|
+
updateMindmapLocation(element) {
|
|
2927
|
+
const { x, y, hGap, vGap } = this.root;
|
|
2928
|
+
const offsetX = x + hGap;
|
|
2929
|
+
const offsetY = y + vGap;
|
|
2930
|
+
depthFirstRecursion(this.root, node => {
|
|
2931
|
+
node.x = node.x - offsetX + element.points[0][0];
|
|
2932
|
+
node.y = node.y - offsetY + element.points[0][1];
|
|
2933
|
+
ELEMENT_TO_NODE.set(node.origin, node);
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
PlaitMindComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitMindComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
2938
|
+
PlaitMindComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: PlaitMindComponent, selector: "plait-mind", usesInheritance: true, ngImport: i0, template: `
|
|
2939
|
+
<plait-children [board]="board" [parent]="element" [effect]="effect" [parentG]="rootG"></plait-children>
|
|
2940
|
+
`, isInline: true, dependencies: [{ kind: "component", type: i2.PlaitChildrenElement, selector: "plait-children", inputs: ["board", "parent", "effect", "parentG"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2941
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: PlaitMindComponent, decorators: [{
|
|
2942
|
+
type: Component,
|
|
2943
|
+
args: [{
|
|
2944
|
+
selector: 'plait-mind',
|
|
2945
|
+
template: `
|
|
2946
|
+
<plait-children [board]="board" [parent]="element" [effect]="effect" [parentG]="rootG"></plait-children>
|
|
2947
|
+
`,
|
|
2948
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
2949
|
+
}]
|
|
2950
|
+
}] });
|
|
2951
|
+
|
|
2952
|
+
class MindModule {
|
|
2953
|
+
}
|
|
2954
|
+
MindModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2955
|
+
MindModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.5", ngImport: i0, type: MindModule, declarations: [PlaitMindComponent, MindNodeComponent], imports: [BrowserModule, RichtextModule, PlaitModule], exports: [PlaitMindComponent, MindNodeComponent] });
|
|
2956
|
+
MindModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, imports: [BrowserModule, RichtextModule, PlaitModule] });
|
|
2957
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, decorators: [{
|
|
2958
|
+
type: NgModule,
|
|
2959
|
+
args: [{
|
|
2960
|
+
declarations: [PlaitMindComponent, MindNodeComponent],
|
|
2961
|
+
imports: [BrowserModule, RichtextModule, PlaitModule],
|
|
2962
|
+
exports: [PlaitMindComponent, MindNodeComponent]
|
|
2963
|
+
}]
|
|
2964
|
+
}] });
|
|
2965
|
+
|
|
2966
|
+
const DRAG_MOVE_BUFFER = 5;
|
|
2967
|
+
const withDnd = (board) => {
|
|
2968
|
+
const { mousedown, mousemove, globalMouseup, keydown } = board;
|
|
2969
|
+
let activeElement;
|
|
2970
|
+
let startPoint;
|
|
2971
|
+
let fakeDragNodeG;
|
|
2972
|
+
let fakeDropNodeG;
|
|
2973
|
+
let dropTarget = null;
|
|
2974
|
+
board.mousedown = (event) => {
|
|
2975
|
+
if (board.options.readonly || IS_TEXT_EDITABLE.get(board) || event.button === 2) {
|
|
2976
|
+
mousedown(event);
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
// 确认是否 hit 节点
|
|
2980
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
2981
|
+
const selectedElements = getSelectedElements(board);
|
|
2982
|
+
board.children.forEach((value) => {
|
|
2983
|
+
if (activeElement) {
|
|
2984
|
+
return;
|
|
2985
|
+
}
|
|
2986
|
+
if (PlaitMind.isMind(value)) {
|
|
2987
|
+
const mindmapComponent = ELEMENT_TO_COMPONENT.get(value);
|
|
2988
|
+
const root = mindmapComponent?.root;
|
|
2989
|
+
root.eachNode((node) => {
|
|
2990
|
+
if (activeElement) {
|
|
2991
|
+
return;
|
|
2992
|
+
}
|
|
2993
|
+
if (hitMindmapElement(board, point, node.origin) && !node.origin.isRoot && selectedElements.length <= 1) {
|
|
2994
|
+
activeElement = node.origin;
|
|
2995
|
+
startPoint = point;
|
|
2996
|
+
}
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2999
|
+
});
|
|
3000
|
+
if (activeElement) {
|
|
3001
|
+
event.preventDefault();
|
|
3002
|
+
}
|
|
3003
|
+
mousedown(event);
|
|
3004
|
+
};
|
|
3005
|
+
board.mousemove = (event) => {
|
|
3006
|
+
if (!board.options.readonly && activeElement && startPoint) {
|
|
3007
|
+
const endPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
3008
|
+
const distance = distanceBetweenPointAndPoint(startPoint[0], startPoint[1], endPoint[0], endPoint[1]);
|
|
3009
|
+
if (distance < DRAG_MOVE_BUFFER) {
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
if (!isDragging(board)) {
|
|
3013
|
+
setIsDragging(board, true);
|
|
3014
|
+
fakeDragNodeG = createG();
|
|
3015
|
+
fakeDragNodeG.classList.add('dragging', 'fake-node', 'plait-board-attached');
|
|
3016
|
+
fakeDropNodeG = createG();
|
|
3017
|
+
addActiveOnDragOrigin(activeElement);
|
|
3018
|
+
PlaitBoard.getHost(board).appendChild(fakeDropNodeG);
|
|
3019
|
+
PlaitBoard.getHost(board).appendChild(fakeDragNodeG);
|
|
3020
|
+
}
|
|
3021
|
+
else {
|
|
3022
|
+
fakeDragNodeG?.childNodes.forEach(node => {
|
|
3023
|
+
node.remove();
|
|
3024
|
+
});
|
|
3025
|
+
fakeDropNodeG?.childNodes.forEach(node => {
|
|
3026
|
+
node.remove();
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
// fake dragging origin node
|
|
3030
|
+
const offsetX = endPoint[0] - startPoint[0];
|
|
3031
|
+
const offsetY = endPoint[1] - startPoint[1];
|
|
3032
|
+
const activeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(activeElement);
|
|
3033
|
+
const roughSVG = PlaitBoard.getRoughSVG(board);
|
|
3034
|
+
const fakeDraggingNode = {
|
|
3035
|
+
...activeComponent.node,
|
|
3036
|
+
children: [],
|
|
3037
|
+
x: activeComponent.node.x + offsetX,
|
|
3038
|
+
y: activeComponent.node.y + offsetY
|
|
3039
|
+
};
|
|
3040
|
+
const textRectangle = getRichtextRectangleByNode(activeComponent.node);
|
|
3041
|
+
const fakeNodeG = drawRectangleNode(board, fakeDraggingNode);
|
|
3042
|
+
const richtextG = activeComponent.richtextG?.cloneNode(true);
|
|
3043
|
+
updateForeignObject(richtextG, textRectangle.width + BASE * 10, textRectangle.height, textRectangle.x + offsetX, textRectangle.y + offsetY);
|
|
3044
|
+
fakeDragNodeG?.append(fakeNodeG);
|
|
3045
|
+
fakeDragNodeG?.append(richtextG);
|
|
3046
|
+
// drop position detect
|
|
3047
|
+
const { x, y } = getRectangleByNode(fakeDraggingNode);
|
|
3048
|
+
const detectCenterPoint = [x + textRectangle.width / 2, y + textRectangle.height / 2];
|
|
3049
|
+
let detectResult = null;
|
|
3050
|
+
board.children.forEach((value) => {
|
|
3051
|
+
if (detectResult) {
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
if (PlaitMind.isMind(value)) {
|
|
3055
|
+
const mindmapComponent = ELEMENT_TO_COMPONENT.get(value);
|
|
3056
|
+
const root = mindmapComponent?.root;
|
|
3057
|
+
root.eachNode((node) => {
|
|
3058
|
+
if (detectResult) {
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
const directions = directionDetector(node, detectCenterPoint);
|
|
3062
|
+
if (directions) {
|
|
3063
|
+
detectResult = directionCorrector(node, directions);
|
|
3064
|
+
}
|
|
3065
|
+
dropTarget = null;
|
|
3066
|
+
if (detectResult && isValidTarget(activeComponent.node.origin, node.origin)) {
|
|
3067
|
+
dropTarget = { target: node.origin, detectResult: detectResult[0] };
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
3070
|
+
}
|
|
3071
|
+
});
|
|
3072
|
+
if (dropTarget?.target) {
|
|
3073
|
+
dropTarget = readjustmentDropTarget(dropTarget);
|
|
3074
|
+
drawPlaceholderDropNodeG(dropTarget, roughSVG, fakeDropNodeG);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
mousemove(event);
|
|
3078
|
+
};
|
|
3079
|
+
board.globalMouseup = (event) => {
|
|
3080
|
+
if (!board.options.readonly && activeElement) {
|
|
3081
|
+
if (dropTarget?.target) {
|
|
3082
|
+
const activeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(activeElement);
|
|
3083
|
+
const targetComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(dropTarget.target);
|
|
3084
|
+
let targetPath = PlaitBoard.findPath(board, targetComponent.element);
|
|
3085
|
+
const mindmapElement = findUpElement(dropTarget.target).root;
|
|
3086
|
+
const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
|
|
3087
|
+
const layout = MindmapQueries.getCorrectLayoutByElement(mindmapComponent?.root.origin);
|
|
3088
|
+
targetPath = updatePathByLayoutAndDropTarget(targetPath, layout, dropTarget);
|
|
3089
|
+
const originPath = PlaitBoard.findPath(board, activeComponent.element);
|
|
3090
|
+
let newElement = { isCollapsed: false }, rightTargetPath = PlaitBoard.findPath(board, targetComponent.element);
|
|
3091
|
+
if (isStandardLayout(layout)) {
|
|
3092
|
+
updateRightNodeCount(board, activeComponent, targetComponent, dropTarget.detectResult);
|
|
3093
|
+
}
|
|
3094
|
+
if (dropTarget.detectResult === 'right') {
|
|
3095
|
+
if (targetComponent.node.origin.isRoot) {
|
|
3096
|
+
targetPath = PlaitBoard.findPath(board, targetComponent.element);
|
|
3097
|
+
targetPath.push(0);
|
|
3098
|
+
const rightNodeCount = targetComponent.node.origin.rightNodeCount + 1;
|
|
3099
|
+
newElement = { isCollapsed: false, rightNodeCount };
|
|
3100
|
+
}
|
|
3101
|
+
Transforms.setNode(board, newElement, rightTargetPath);
|
|
3102
|
+
}
|
|
3103
|
+
Transforms.moveNode(board, originPath, targetPath);
|
|
3104
|
+
}
|
|
3105
|
+
if (isDragging(board)) {
|
|
3106
|
+
removeActiveOnDragOrigin(activeElement);
|
|
3107
|
+
}
|
|
3108
|
+
setIsDragging(board, false);
|
|
3109
|
+
activeElement = null;
|
|
3110
|
+
fakeDragNodeG?.remove();
|
|
3111
|
+
fakeDragNodeG = undefined;
|
|
3112
|
+
fakeDropNodeG?.remove();
|
|
3113
|
+
fakeDropNodeG = undefined;
|
|
3114
|
+
dropTarget = null;
|
|
3115
|
+
}
|
|
3116
|
+
globalMouseup(event);
|
|
3117
|
+
};
|
|
3118
|
+
board.keydown = (event) => {
|
|
3119
|
+
keydown(event);
|
|
3120
|
+
};
|
|
3121
|
+
return board;
|
|
3122
|
+
};
|
|
3123
|
+
const isValidTarget = (origin, target) => {
|
|
3124
|
+
return origin !== target && !isChildElement(origin, target);
|
|
3125
|
+
};
|
|
3126
|
+
const addActiveOnDragOrigin = (activeElement, isOrigin = true) => {
|
|
3127
|
+
const activeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(activeElement);
|
|
3128
|
+
if (isOrigin) {
|
|
3129
|
+
activeComponent.g.classList.add('dragging-origin');
|
|
3130
|
+
}
|
|
3131
|
+
else {
|
|
3132
|
+
activeComponent.g.classList.add('dragging-child');
|
|
3133
|
+
}
|
|
3134
|
+
!activeElement.isCollapsed &&
|
|
3135
|
+
activeElement.children.forEach(child => {
|
|
3136
|
+
addActiveOnDragOrigin(child, false);
|
|
3137
|
+
});
|
|
3138
|
+
};
|
|
3139
|
+
const removeActiveOnDragOrigin = (activeElement, isOrigin = true) => {
|
|
3140
|
+
const activeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(activeElement);
|
|
3141
|
+
if (isOrigin) {
|
|
3142
|
+
activeComponent.g.classList.remove('dragging-origin');
|
|
3143
|
+
}
|
|
3144
|
+
else {
|
|
3145
|
+
activeComponent.g.classList.remove('dragging-child');
|
|
3146
|
+
}
|
|
3147
|
+
!activeElement.isCollapsed &&
|
|
3148
|
+
activeElement.children.forEach(child => {
|
|
3149
|
+
removeActiveOnDragOrigin(child, false);
|
|
3150
|
+
});
|
|
3151
|
+
};
|
|
3152
|
+
const updatePathByLayoutAndDropTarget = (targetPath, layout, dropTarget) => {
|
|
3153
|
+
// 上下布局:左右是兄弟节点,上下是子节点
|
|
3154
|
+
if (isVerticalLogicLayout(layout)) {
|
|
3155
|
+
if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
|
|
3156
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3157
|
+
}
|
|
3158
|
+
if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
|
|
3159
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3160
|
+
}
|
|
3161
|
+
// 如果是左,位置不变,右则插入到下一个兄弟节点
|
|
3162
|
+
if (dropTarget.detectResult === 'right') {
|
|
3163
|
+
targetPath = Path.next(targetPath);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
// 水平布局/标准布局:上下是兄弟节点,左右是子节点
|
|
3167
|
+
if (isHorizontalLogicLayout(layout)) {
|
|
3168
|
+
if (dropTarget.detectResult === 'right') {
|
|
3169
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3170
|
+
}
|
|
3171
|
+
if (dropTarget.detectResult === 'left') {
|
|
3172
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3173
|
+
}
|
|
3174
|
+
// 如果是上,位置不变,下插入到下一个兄弟节点
|
|
3175
|
+
if (dropTarget.detectResult === 'bottom') {
|
|
3176
|
+
targetPath = Path.next(targetPath);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
// 缩进布局:上下是兄弟节点,左右是子节点,但上(左上/右上),探测到上是子节点,下则位置不变,反之同理。
|
|
3180
|
+
if (isIndentedLayout(layout)) {
|
|
3181
|
+
if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
|
|
3182
|
+
targetPath = Path.next(targetPath);
|
|
3183
|
+
}
|
|
3184
|
+
if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
|
|
3185
|
+
targetPath = Path.next(targetPath);
|
|
3186
|
+
}
|
|
3187
|
+
if (isLeftLayout(layout) && dropTarget.detectResult === 'left') {
|
|
3188
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3189
|
+
}
|
|
3190
|
+
if (isRightLayout(layout) && dropTarget.detectResult === 'right') {
|
|
3191
|
+
targetPath.push(dropTarget.target.children.length);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
return targetPath;
|
|
3195
|
+
};
|
|
3196
|
+
const updateRightNodeCount = (board, activeComponent, targetComponent, detectResult) => {
|
|
3197
|
+
let rightNodeCount;
|
|
3198
|
+
const mindmapElement = findUpElement(targetComponent.node.origin).root;
|
|
3199
|
+
const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
|
|
3200
|
+
const activeIndex = mindmapComponent?.root.children.indexOf(activeComponent.node);
|
|
3201
|
+
const targetIndex = mindmapComponent?.root.children.indexOf(targetComponent.node);
|
|
3202
|
+
const isActiveOnRight = activeIndex !== -1 && activeIndex <= activeComponent.parent.origin.rightNodeCount - 1;
|
|
3203
|
+
const isTargetOnRight = targetComponent.parent && targetIndex !== -1 && targetIndex <= targetComponent.parent.origin.rightNodeCount - 1;
|
|
3204
|
+
const isBothOnRight = isActiveOnRight && isTargetOnRight;
|
|
3205
|
+
const rootChildCount = mindmapComponent.root.children?.length;
|
|
3206
|
+
const rootRightNodeCount = mindmapComponent?.root.origin.rightNodeCount;
|
|
3207
|
+
if (!isBothOnRight) {
|
|
3208
|
+
if (isActiveOnRight) {
|
|
3209
|
+
rightNodeCount = rootChildCount < rootRightNodeCount ? rootChildCount - 1 : rootRightNodeCount - 1;
|
|
3210
|
+
Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, activeComponent.parent.origin));
|
|
3211
|
+
}
|
|
3212
|
+
if (isTargetOnRight && detectResult !== 'right') {
|
|
3213
|
+
rightNodeCount = rootChildCount < rootRightNodeCount ? rootRightNodeCount : rootRightNodeCount + 1;
|
|
3214
|
+
Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.parent.origin));
|
|
3215
|
+
}
|
|
3216
|
+
//二级子节点拖动到根节点左侧
|
|
3217
|
+
if (targetComponent.node.origin.isRoot && detectResult === 'left' && activeIndex === -1) {
|
|
3218
|
+
rightNodeCount = rootChildCount;
|
|
3219
|
+
Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.element));
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
};
|
|
3223
|
+
const IS_DRAGGING = new WeakMap();
|
|
3224
|
+
const isDragging = (board) => {
|
|
3225
|
+
return !!IS_DRAGGING.get(board);
|
|
3226
|
+
};
|
|
3227
|
+
const setIsDragging = (board, state) => {
|
|
3228
|
+
IS_DRAGGING.set(board, state);
|
|
3229
|
+
};
|
|
3230
|
+
|
|
3231
|
+
const buildClipboardData = (board, selectedElements) => {
|
|
3232
|
+
let result = [];
|
|
3233
|
+
const selectedMindmapNodes = Array.from(selectedElements, node => {
|
|
3234
|
+
return MINDMAP_ELEMENT_TO_COMPONENT.get(node)?.node;
|
|
3235
|
+
});
|
|
3236
|
+
const nodesRectangle = getRectangleByElements(board, selectedElements, true);
|
|
3237
|
+
selectedElements.forEach((node, index) => {
|
|
3238
|
+
const nodeRectangle = getRectangleByNode(selectedMindmapNodes[index]);
|
|
3239
|
+
result.push({
|
|
3240
|
+
...node,
|
|
3241
|
+
points: [[nodeRectangle.x - nodesRectangle.x, nodeRectangle.y - nodesRectangle.y]]
|
|
3242
|
+
});
|
|
3243
|
+
});
|
|
3244
|
+
return result;
|
|
3245
|
+
};
|
|
3246
|
+
const setClipboardData = (data, elements) => {
|
|
3247
|
+
const stringObj = JSON.stringify(elements);
|
|
3248
|
+
const encoded = window.btoa(encodeURIComponent(stringObj));
|
|
3249
|
+
const text = elements.reduce((string, currentNode) => {
|
|
3250
|
+
return string + extractNodesText(currentNode);
|
|
3251
|
+
}, '');
|
|
3252
|
+
data?.setData(`application/${CLIP_BOARD_FORMAT_KEY}`, encoded);
|
|
3253
|
+
data?.setData(`text/plain`, text);
|
|
3254
|
+
};
|
|
3255
|
+
const getDataFromClipboard = (data) => {
|
|
3256
|
+
const encoded = data?.getData(`application/${CLIP_BOARD_FORMAT_KEY}`);
|
|
3257
|
+
let nodesData = [];
|
|
3258
|
+
if (encoded) {
|
|
3259
|
+
const decoded = decodeURIComponent(window.atob(encoded));
|
|
3260
|
+
nodesData = JSON.parse(decoded);
|
|
3261
|
+
}
|
|
3262
|
+
return nodesData;
|
|
3263
|
+
};
|
|
3264
|
+
const insertClipboardData = (board, elements, targetPoint) => {
|
|
3265
|
+
let newElement, path;
|
|
3266
|
+
const selectedElements = getSelectedElements(board);
|
|
3267
|
+
let newELements = [];
|
|
3268
|
+
elements.forEach((item, index) => {
|
|
3269
|
+
newElement = copyNewNode(item);
|
|
3270
|
+
if (selectedElements.length === 1) {
|
|
3271
|
+
if (item.isRoot) {
|
|
3272
|
+
newElement = transformRootToNode(board, newElement);
|
|
3273
|
+
}
|
|
3274
|
+
const selectedElementPath = PlaitBoard.findPath(board, selectedElements[0]);
|
|
3275
|
+
path = selectedElementPath.concat((selectedElements[0].children || []).length + index);
|
|
3276
|
+
}
|
|
3277
|
+
else {
|
|
3278
|
+
const point = [targetPoint[0] + item.points[0][0], targetPoint[1] + item.points[0][1]];
|
|
3279
|
+
newElement.points = [point];
|
|
3280
|
+
if (!item.isRoot) {
|
|
3281
|
+
newElement = transformNodeToRoot(board, newElement);
|
|
3282
|
+
}
|
|
3283
|
+
path = [board.children.length];
|
|
3284
|
+
}
|
|
3285
|
+
newELements.push(newElement);
|
|
3286
|
+
Transforms.insertNode(board, newElement, path);
|
|
3287
|
+
return;
|
|
3288
|
+
});
|
|
3289
|
+
Transforms.setSelectionWithTemporaryElements(board, newELements);
|
|
3290
|
+
};
|
|
3291
|
+
const insertClipboardText = (board, parentElement, text, width, height) => {
|
|
3292
|
+
const newElement = createMindElement(text, width, height, {});
|
|
3293
|
+
const path = PlaitBoard.findPath(board, parentElement).concat((parentElement.children || []).length);
|
|
3294
|
+
Transforms.insertNode(board, newElement, path);
|
|
3295
|
+
return;
|
|
3296
|
+
};
|
|
3297
|
+
|
|
3298
|
+
function findNewChildNodePath(board, element) {
|
|
3299
|
+
const path = PlaitBoard.findPath(board, element);
|
|
3300
|
+
return path.concat((element.children || []).length);
|
|
3301
|
+
}
|
|
3302
|
+
function findNewSiblingNodePath(board, element) {
|
|
3303
|
+
const path = PlaitBoard.findPath(board, element);
|
|
3304
|
+
return Path$1.next(path);
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
const withEmoji = (board) => {
|
|
3308
|
+
const newBoard = board;
|
|
3309
|
+
newBoard.drawEmoji = (emoji, element) => {
|
|
3310
|
+
throw new Error('Not implement drawEmoji method error.');
|
|
3311
|
+
};
|
|
3312
|
+
return newBoard;
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3315
|
+
const withAbstract = (board) => {
|
|
3316
|
+
const { mousedown, mousemove, mouseup } = board;
|
|
3317
|
+
let activeAbstractElement;
|
|
3318
|
+
let abstractHandlePosition;
|
|
3319
|
+
let startPoint;
|
|
3320
|
+
let newProperty;
|
|
3321
|
+
board.mousedown = (event) => {
|
|
3322
|
+
const activeAbstractElements = getSelectedElements(board).filter(element => AbstractNode.isAbstract(element));
|
|
3323
|
+
const host = BOARD_TO_HOST.get(board);
|
|
3324
|
+
const point = transformPoint(board, toPoint(event.x, event.y, host));
|
|
3325
|
+
activeAbstractElement = activeAbstractElements.find(element => {
|
|
3326
|
+
abstractHandlePosition = getHitAbstractHandle(board, element, point);
|
|
3327
|
+
return abstractHandlePosition;
|
|
3328
|
+
});
|
|
3329
|
+
if (activeAbstractElement) {
|
|
3330
|
+
startPoint = point;
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
mousedown(event);
|
|
3334
|
+
};
|
|
3335
|
+
board.mousemove = (event) => {
|
|
3336
|
+
getSelectedElements(board);
|
|
3337
|
+
const host = BOARD_TO_HOST.get(board);
|
|
3338
|
+
const endPoint = transformPoint(board, toPoint(event.x, event.y, host));
|
|
3339
|
+
if (abstractHandlePosition && activeAbstractElement) {
|
|
3340
|
+
const abstractComponent = PlaitElement.getComponent(activeAbstractElement);
|
|
3341
|
+
const element = abstractComponent.element;
|
|
3342
|
+
const nodeLayout = MindmapQueries.getCorrectLayoutByElement(activeAbstractElement);
|
|
3343
|
+
const isHorizontal = isHorizontalLayout(nodeLayout);
|
|
3344
|
+
const parentElement = MindElement.getParent(element);
|
|
3345
|
+
let children = parentElement.children;
|
|
3346
|
+
const parentLayout = MindmapQueries.getLayoutByElement(parentElement);
|
|
3347
|
+
if (isStandardLayout(parentLayout)) {
|
|
3348
|
+
const rightNodeCount = parentElement.rightNodeCount;
|
|
3349
|
+
const { leftChildren, rightChildren } = separateChildren(parentElement);
|
|
3350
|
+
if (activeAbstractElement.end < rightNodeCount) {
|
|
3351
|
+
children = rightChildren;
|
|
3352
|
+
}
|
|
3353
|
+
if (activeAbstractElement.start >= rightNodeCount) {
|
|
3354
|
+
children = leftChildren;
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
const resizingLocation = isHorizontal ? endPoint[1] : endPoint[0];
|
|
3358
|
+
const parent = MindElement.getNode(parentElement);
|
|
3359
|
+
const scope = getLocationScope(board, abstractHandlePosition, children, element, parent, isHorizontal);
|
|
3360
|
+
const location = Math.min(scope.max, Math.max(scope.min, resizingLocation));
|
|
3361
|
+
let locationIndex = findLocationLeftIndex(board, children, location, isHorizontal);
|
|
3362
|
+
const isPropertyUnchanged = (abstractHandlePosition === AbstractHandlePosition.start &&
|
|
3363
|
+
locationIndex + 1 === activeAbstractElement.start) ||
|
|
3364
|
+
(abstractHandlePosition === AbstractHandlePosition.end && locationIndex === activeAbstractElement.end);
|
|
3365
|
+
if (isPropertyUnchanged) {
|
|
3366
|
+
newProperty = undefined;
|
|
3367
|
+
}
|
|
3368
|
+
else {
|
|
3369
|
+
if (isStandardLayout(parent.layout)) {
|
|
3370
|
+
const rightNodeCount = parent.origin.rightNodeCount;
|
|
3371
|
+
let start = element.start;
|
|
3372
|
+
if (start >= rightNodeCount) {
|
|
3373
|
+
locationIndex += rightNodeCount;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
newProperty =
|
|
3377
|
+
abstractHandlePosition === AbstractHandlePosition.start ? { start: locationIndex + 1 } : { end: locationIndex };
|
|
3378
|
+
}
|
|
3379
|
+
abstractComponent.updateAbstractIncludedOutline(location, abstractHandlePosition);
|
|
3380
|
+
}
|
|
3381
|
+
mousemove(event);
|
|
3382
|
+
};
|
|
3383
|
+
board.mouseup = (event) => {
|
|
3384
|
+
startPoint = undefined;
|
|
3385
|
+
abstractHandlePosition = null;
|
|
3386
|
+
if (activeAbstractElement) {
|
|
3387
|
+
if (newProperty) {
|
|
3388
|
+
const path = PlaitBoard.findPath(board, activeAbstractElement);
|
|
3389
|
+
Transforms.setNode(board, newProperty, path);
|
|
3390
|
+
}
|
|
3391
|
+
else {
|
|
3392
|
+
const abstractComponent = PlaitElement.getComponent(activeAbstractElement);
|
|
3393
|
+
abstractComponent.updateAbstractIncludedOutline();
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
mouseup(event);
|
|
3397
|
+
};
|
|
3398
|
+
return board;
|
|
3399
|
+
};
|
|
3400
|
+
|
|
3401
|
+
const withMind = (board) => {
|
|
3402
|
+
const { drawElement, dblclick, keydown, insertFragment, setFragment, deleteFragment, isHitSelection, getRectangle, isMovable, isRecursion } = board;
|
|
3403
|
+
board.drawElement = (context) => {
|
|
3404
|
+
if (PlaitMind.isMind(context.element)) {
|
|
3405
|
+
return PlaitMindComponent;
|
|
3406
|
+
}
|
|
3407
|
+
else if (MindElement.isMindElement(board, context.element)) {
|
|
3408
|
+
return MindNodeComponent;
|
|
3409
|
+
}
|
|
3410
|
+
return drawElement(context);
|
|
3411
|
+
};
|
|
3412
|
+
board.getRectangle = element => {
|
|
3413
|
+
if (MindElement.isMindElement(board, element)) {
|
|
3414
|
+
return getRectangleByNode(MindElement.getNode(element));
|
|
3415
|
+
}
|
|
3416
|
+
return getRectangle(element);
|
|
3417
|
+
};
|
|
3418
|
+
board.isRecursion = element => {
|
|
3419
|
+
if (MindElement.isMindElement(board, element) && element.isCollapsed) {
|
|
3420
|
+
return false;
|
|
3421
|
+
}
|
|
3422
|
+
return isRecursion(element);
|
|
3423
|
+
};
|
|
3424
|
+
board.isHitSelection = (element, range) => {
|
|
3425
|
+
if (MindElement.isMindElement(board, element) && board.selection) {
|
|
3426
|
+
const client = getRectangleByNode(MindElement.getNode(element));
|
|
3427
|
+
return RectangleClient.isIntersect(RectangleClient.toRectangleClient([range.anchor, range.focus]), client);
|
|
3428
|
+
}
|
|
3429
|
+
return isHitSelection(element, range);
|
|
3430
|
+
};
|
|
3431
|
+
board.isMovable = element => {
|
|
3432
|
+
if (PlaitMind.isMind(element) && element.isRoot) {
|
|
3433
|
+
return true;
|
|
3434
|
+
}
|
|
3435
|
+
return isMovable(element);
|
|
3436
|
+
};
|
|
3437
|
+
board.keydown = (event) => {
|
|
3438
|
+
if (board.options.readonly || IS_TEXT_EDITABLE.get(board)) {
|
|
3439
|
+
keydown(event);
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
const selectedElements = getSelectedElements(board);
|
|
3443
|
+
if (selectedElements.length) {
|
|
3444
|
+
if (selectedElements.length === 1 &&
|
|
3445
|
+
(event.key === 'Tab' ||
|
|
3446
|
+
(event.key === 'Enter' && !selectedElements[0].isRoot && !AbstractNode.isAbstract(selectedElements[0])))) {
|
|
3447
|
+
event.preventDefault();
|
|
3448
|
+
const selectedElement = selectedElements[0];
|
|
3449
|
+
removeSelectedElement(board, selectedElement);
|
|
3450
|
+
const selectedElementPath = PlaitBoard.findPath(board, selectedElement);
|
|
3451
|
+
if (event.key === 'Tab') {
|
|
3452
|
+
if (selectedElement.isCollapsed) {
|
|
3453
|
+
const newElement = { isCollapsed: false };
|
|
3454
|
+
PlaitHistoryBoard.withoutSaving(board, () => {
|
|
3455
|
+
Transforms.setNode(board, newElement, selectedElementPath);
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
insertMindElement(board, selectedElement, findNewChildNodePath(board, selectedElement));
|
|
3459
|
+
}
|
|
3460
|
+
else {
|
|
3461
|
+
if (shouldChangeRightNodeCount(selectedElement)) {
|
|
3462
|
+
changeRightNodeCount(board, selectedElementPath.slice(0, 1), 1);
|
|
3463
|
+
}
|
|
3464
|
+
insertMindElement(board, selectedElement, findNewSiblingNodePath(board, selectedElement));
|
|
3465
|
+
}
|
|
3466
|
+
return;
|
|
3467
|
+
}
|
|
3468
|
+
if (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event)) {
|
|
3469
|
+
event.preventDefault();
|
|
3470
|
+
deleteSelectedELements(board, selectedElements);
|
|
3471
|
+
let lastNode = null;
|
|
3472
|
+
const elementGroup = filterChildElement(selectedElements);
|
|
3473
|
+
const selectNode = elementGroup[0];
|
|
3474
|
+
const MindNodeComponent = MINDMAP_ELEMENT_TO_COMPONENT.get(selectNode);
|
|
3475
|
+
const nodeIndex = MindNodeComponent?.parent?.children.findIndex(item => item.origin.id === selectNode.id);
|
|
3476
|
+
const isSameParent = elementGroup.every(element => {
|
|
3477
|
+
return findParentElement(element) && findParentElement(elementGroup[0]) === findParentElement(element);
|
|
3478
|
+
});
|
|
3479
|
+
if (isSameParent) {
|
|
3480
|
+
const childCount = MindNodeComponent.parent?.children.length - elementGroup.length;
|
|
3481
|
+
if (childCount === 0) {
|
|
3482
|
+
lastNode = MindNodeComponent?.parent;
|
|
3483
|
+
}
|
|
3484
|
+
else if (nodeIndex === 0) {
|
|
3485
|
+
lastNode = MindNodeComponent?.parent.children[elementGroup.length];
|
|
3486
|
+
}
|
|
3487
|
+
else if (nodeIndex > 0) {
|
|
3488
|
+
lastNode = MindNodeComponent?.parent.children[nodeIndex - 1];
|
|
3489
|
+
}
|
|
3490
|
+
}
|
|
3491
|
+
if (lastNode) {
|
|
3492
|
+
addSelectedElement(board, lastNode.origin);
|
|
3493
|
+
}
|
|
3494
|
+
return;
|
|
3495
|
+
}
|
|
3496
|
+
// auto enter edit status
|
|
3497
|
+
if (!isVirtualKey(event)) {
|
|
3498
|
+
event.preventDefault();
|
|
3499
|
+
const selectedElement = selectedElements[0];
|
|
3500
|
+
enterNodeEditing(selectedElement);
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
if (board.selection && event.code === 'Space') {
|
|
3505
|
+
if (selectedElements?.length) {
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
keydown(event);
|
|
3510
|
+
};
|
|
3511
|
+
board.dblclick = (event) => {
|
|
3512
|
+
if (board.options.readonly || IS_TEXT_EDITABLE.get(board)) {
|
|
3513
|
+
dblclick(event);
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
3517
|
+
board.children
|
|
3518
|
+
.filter(value => PlaitMind.isMind(value))
|
|
3519
|
+
.forEach(mindmap => {
|
|
3520
|
+
depthFirstRecursion(mindmap, node => {
|
|
3521
|
+
if (!PlaitBoard.hasBeenTextEditing(board) && hitMindmapElement(board, point, node)) {
|
|
3522
|
+
enterNodeEditing(node);
|
|
3523
|
+
}
|
|
3524
|
+
});
|
|
3525
|
+
});
|
|
3526
|
+
if (PlaitBoard.hasBeenTextEditing(board)) {
|
|
3527
|
+
return;
|
|
3528
|
+
}
|
|
3529
|
+
dblclick(event);
|
|
3530
|
+
};
|
|
3531
|
+
board.setFragment = (data) => {
|
|
3532
|
+
const selectedElements = filterChildElement(getSelectedElements(board));
|
|
3533
|
+
if (selectedElements.length) {
|
|
3534
|
+
const elements = buildClipboardData(board, selectedElements);
|
|
3535
|
+
setClipboardData(data, elements);
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
setFragment(data);
|
|
3539
|
+
};
|
|
3540
|
+
board.insertFragment = (data, targetPoint) => {
|
|
3541
|
+
if (board.options.readonly) {
|
|
3542
|
+
insertFragment(data, targetPoint);
|
|
3543
|
+
return;
|
|
3544
|
+
}
|
|
3545
|
+
const elements = getDataFromClipboard(data);
|
|
3546
|
+
if (elements.length) {
|
|
3547
|
+
insertClipboardData(board, elements, targetPoint || [0, 0]);
|
|
3548
|
+
}
|
|
3549
|
+
else {
|
|
3550
|
+
const text = data?.getData(`text/plain`);
|
|
3551
|
+
const { width, height } = getSizeByText(text, PlaitBoard.getHost(board).parentElement, TOPIC_DEFAULT_MAX_WORD_COUNT);
|
|
3552
|
+
const selectedElements = getSelectedElements(board);
|
|
3553
|
+
if (text && selectedElements.length === 1) {
|
|
3554
|
+
insertClipboardText(board, selectedElements[0], text, width, height);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
insertFragment(data, targetPoint);
|
|
3558
|
+
};
|
|
3559
|
+
board.deleteFragment = (data) => {
|
|
3560
|
+
const selectedElements = getSelectedElements(board);
|
|
3561
|
+
deleteSelectedELements(board, selectedElements);
|
|
3562
|
+
deleteFragment(data);
|
|
3563
|
+
};
|
|
3564
|
+
return withEmoji(withAbstract(withDnd(board)));
|
|
3565
|
+
};
|
|
3566
|
+
|
|
3567
|
+
class MindEmojiBaseComponent {
|
|
3568
|
+
get nativeElement() {
|
|
3569
|
+
return this.elementRef.nativeElement;
|
|
3570
|
+
}
|
|
3571
|
+
constructor(elementRef) {
|
|
3572
|
+
this.elementRef = elementRef;
|
|
3573
|
+
this.fontSize = 14;
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
MindEmojiBaseComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindEmojiBaseComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
3577
|
+
MindEmojiBaseComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: MindEmojiBaseComponent, inputs: { fontSize: "fontSize", emojiItem: "emojiItem" }, ngImport: i0 });
|
|
3578
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindEmojiBaseComponent, decorators: [{
|
|
3579
|
+
type: Directive
|
|
3580
|
+
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { fontSize: [{
|
|
3581
|
+
type: Input
|
|
3582
|
+
}], emojiItem: [{
|
|
3583
|
+
type: Input
|
|
3584
|
+
}] } });
|
|
3585
|
+
|
|
3586
|
+
/*
|
|
3587
|
+
* Public API Surface of mind
|
|
3588
|
+
*/
|
|
3589
|
+
|
|
3590
|
+
/**
|
|
3591
|
+
* Generated bundle index. Do not edit.
|
|
3592
|
+
*/
|
|
3593
|
+
|
|
3594
|
+
export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, BASE, COLORS, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, LayoutDirection, LayoutDirectionsMap, MAX_RADIUS, MINDMAP_ELEMENT_TO_COMPONENT, MINDMAP_KEY, MindElement, MindEmojiBaseComponent, MindModule, MindNodeComponent, MindTransforms, MindmapNode, MindmapNodeShape, MindmapQueries, NODE_FILL, NODE_MIN_WIDTH, PRIMARY_COLOR, PlaitMind, PlaitMindComponent, QUICK_INSERT_CIRCLE_COLOR, QUICK_INSERT_CIRCLE_OFFSET, QUICK_INSERT_INNER_CROSS_COLOR, ROOT_NODE_FILL, ROOT_NODE_STROKE, ROOT_TOPIC_FONT_SIZE, STROKE_WIDTH, TOPIC_COLOR, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, TRANSPARENT, changeRightNodeCount, copyNewNode, correctLayoutByDirection, createMindElement, createMindmapData, deleteSelectedELements, directionCorrector, directionDetector, drawCurvePlaceholderDropNodeG, drawIndentNodeG, drawPlaceholderDropNodeG, drawStraightDropNodeG, extractNodesText, filterChildElement, findLastChild, findLocationLeftIndex, findParentElement, findUpElement, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBranchDirectionsByLayouts, getChildrenCount, getDefaultMindmapLayout, getEmojiFontSize, getEmojisRectangle, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutReverseDirection, getLinkLineColorByMindmapElement, getLocationScope, getMindmapDirection, getNodeShapeByElement, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getRootLinkLineColorByMindmapElement, getStrokeByMindmapElement, hitMindmapElement, insertMindElement, isChildElement, isChildRight, isChildUp, isCorrectLayout, isMixedLayout, isVirtualKey, readjustmentDropTarget, separateChildren, shouldChangeRightNodeCount, transformNodeToRoot, transformRootToNode, withEmoji, withMind };
|
|
3595
|
+
//# sourceMappingURL=plait-mind.mjs.map
|