@plait/mind 0.2.0-next.3 → 0.2.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/draw/indented-link.d.ts +2 -2
  2. package/draw/link/logic-link.d.ts +2 -2
  3. package/draw/link.d.ts +2 -2
  4. package/esm2020/draw/indented-link.mjs +7 -6
  5. package/esm2020/draw/link/logic-link.mjs +7 -6
  6. package/esm2020/draw/link.mjs +8 -7
  7. package/esm2020/draw/shape.mjs +3 -3
  8. package/esm2020/drawer/quick-insert.drawer.mjs +9 -8
  9. package/esm2020/interfaces/element.mjs +13 -2
  10. package/esm2020/node.component.mjs +6 -6
  11. package/esm2020/plugins/emoji/emoji-base.component.mjs +4 -2
  12. package/esm2020/plugins/emoji/emoji.drawer.mjs +2 -1
  13. package/esm2020/plugins/with-abstract.mjs +2 -2
  14. package/esm2020/plugins/with-dnd.mjs +2 -2
  15. package/esm2020/plugins/with-mind.mjs +3 -2
  16. package/esm2020/transforms/layout.mjs +8 -2
  17. package/esm2020/utils/abstract/common.mjs +49 -15
  18. package/esm2020/utils/abstract/resize.mjs +2 -2
  19. package/esm2020/utils/clipboard.mjs +10 -3
  20. package/esm2020/utils/draw-placeholder.mjs +20 -20
  21. package/esm2020/utils/index.mjs +2 -2
  22. package/esm2020/utils/mind.mjs +30 -21
  23. package/esm2020/utils/node-style/branch.mjs +28 -0
  24. package/esm2020/utils/node-style/index.mjs +3 -0
  25. package/esm2020/utils/node-style/node.mjs +22 -0
  26. package/esm2020/utils/path.mjs +3 -3
  27. package/fesm2015/plait-mind.mjs +1449 -1377
  28. package/fesm2015/plait-mind.mjs.map +1 -1
  29. package/fesm2020/plait-mind.mjs +1451 -1379
  30. package/fesm2020/plait-mind.mjs.map +1 -1
  31. package/interfaces/element.d.ts +3 -2
  32. package/package.json +1 -1
  33. package/plugins/emoji/emoji-base.component.d.ts +3 -1
  34. package/utils/abstract/common.d.ts +6 -1
  35. package/utils/abstract/resize.d.ts +1 -1
  36. package/utils/draw-placeholder.d.ts +8 -8
  37. package/utils/index.d.ts +1 -1
  38. package/utils/mind.d.ts +2 -1
  39. package/utils/node-style/branch.d.ts +7 -0
  40. package/utils/node-style/index.d.ts +2 -0
  41. package/utils/node-style/node.d.ts +3 -0
  42. package/esm2020/utils/colors.mjs +0 -41
  43. package/utils/colors.d.ts +0 -4
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Component, ChangeDetectionStrategy, NgModule, Directive, Input } from '@angular/core';
3
3
  import * as i2 from '@plait/core';
4
- import { distanceBetweenPointAndRectangle, PlaitElement, drawRoundRectangle, PlaitBoard, Path, Transforms, getRectangleByElements, RectangleClient, getSelectedElements, PlaitNode, NODE_TO_PARENT, ELEMENT_TO_COMPONENT, idCreator, isNullOrUndefined, clearSelectedElement, addSelectedElement, createG, drawAbstractRoundRectangle, PlaitPluginElementComponent, PlaitPointerType, NODE_TO_INDEX, createText, IS_TEXT_EDITABLE, MERGING, transformPoint, toPoint, depthFirstRecursion, PlaitModule, distanceBetweenPointAndPoint, CLIP_BOARD_FORMAT_KEY, BOARD_TO_HOST, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
5
- import { isIndentedLayout, MindLayoutType, isStandardLayout, isTopLayout, isLeftLayout, isHorizontalLogicLayout, isVerticalLogicLayout, isBottomLayout, isRightLayout, isHorizontalLayout, AbstractNode, getCorrectStartEnd, isChildOfAbstract, getAbstractLayout, ConnectingPosition, GlobalLayout } from '@plait/layouts';
4
+ import { distanceBetweenPointAndRectangle, PlaitElement, PlaitBoard, PlaitNode, NODE_TO_PARENT, Path, ELEMENT_TO_COMPONENT, Transforms, idCreator, isNullOrUndefined, clearSelectedElement, addSelectedElement, drawRoundRectangle, getRectangleByElements, RectangleClient, getSelectedElements, createG, drawAbstractRoundRectangle, PlaitPluginElementComponent, PlaitPointerType, NODE_TO_INDEX, createText, IS_TEXT_EDITABLE, MERGING, transformPoint, toPoint, depthFirstRecursion, PlaitModule, distanceBetweenPointAndPoint, CLIP_BOARD_FORMAT_KEY, BOARD_TO_HOST, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
5
+ import { AbstractNode, isChildOfAbstract, isIndentedLayout, getAbstractLayout, MindLayoutType, isStandardLayout, isTopLayout, isLeftLayout, isHorizontalLogicLayout, isVerticalLogicLayout, isBottomLayout, isRightLayout, isHorizontalLayout, getCorrectStartEnd, ConnectingPosition, GlobalLayout } from '@plait/layouts';
6
6
  import { getSizeByText, ROOT_DEFAULT_HEIGHT, TEXT_DEFAULT_HEIGHT, drawRichtext, updateForeignObject, createForeignObject, updateRichText, setFullSelectionAndFocus, getRichtextContentSize, hasEditableTarget, RichtextModule } from '@plait/richtext';
7
7
  import { fromEvent, Subject, timer } from 'rxjs';
8
8
  import { take, takeUntil, filter, debounceTime } from 'rxjs/operators';
9
- import { Node, Editor, Operation, Path as Path$1 } from 'slate';
9
+ import { Node, Path as Path$1, Editor, Operation } from 'slate';
10
10
  import { pointsOnBezierCurves } from 'points-on-curve';
11
11
  import { isKeyHotkey } from 'is-hotkey';
12
12
  import * as i1 from '@angular/common';
@@ -68,6 +68,250 @@ function hitMindElement(board, point, element) {
68
68
  }
69
69
  }
70
70
 
71
+ /**
72
+ * get correctly layout:
73
+ * 1. root is standard -> left or right
74
+ * 2. correct layout by incorrect layout direction
75
+ * @param element
76
+ */
77
+ const getCorrectLayoutByElement = (element) => {
78
+ var _a;
79
+ const { root } = findUpElement(element);
80
+ const rootLayout = root.layout || getDefaultLayout();
81
+ let correctRootLayout = rootLayout;
82
+ if (element.isRoot) {
83
+ return correctRootLayout;
84
+ }
85
+ const component = PlaitElement.getComponent(element);
86
+ let layout = element.layout;
87
+ let parentComponent = null;
88
+ let parent = component.parent.origin;
89
+ while (!layout && parent) {
90
+ parentComponent = PlaitElement.getComponent(parent);
91
+ layout = parentComponent.node.origin.layout;
92
+ parent = (_a = parentComponent.parent) === null || _a === void 0 ? void 0 : _a.origin;
93
+ }
94
+ if ((AbstractNode.isAbstract(element) || isChildOfAbstract(MindElement.getNode(element))) &&
95
+ isIndentedLayout(layout)) {
96
+ return getAbstractLayout(layout);
97
+ }
98
+ // handle root standard
99
+ if (rootLayout === MindLayoutType.standard) {
100
+ correctRootLayout = (component === null || component === void 0 ? void 0 : component.node.left) ? MindLayoutType.left : MindLayoutType.right;
101
+ }
102
+ if (parentComponent && parentComponent.node.origin.isRoot) {
103
+ return correctRootLayout;
104
+ }
105
+ if (layout) {
106
+ const incorrectDirection = getInCorrectLayoutDirection(correctRootLayout, layout);
107
+ if (incorrectDirection) {
108
+ return correctLayoutByDirection(layout, incorrectDirection);
109
+ }
110
+ else {
111
+ return layout;
112
+ }
113
+ }
114
+ else {
115
+ return correctRootLayout;
116
+ }
117
+ };
118
+
119
+ const getBranchLayouts = (element) => {
120
+ const layouts = [];
121
+ if (element.layout) {
122
+ //getCorrectLayoutByElement含有递归操作,getBranchMindmapLayouts本身也有递归操作,有待优化
123
+ layouts.unshift(getCorrectLayoutByElement(element));
124
+ }
125
+ let parent = findParentElement(element);
126
+ while (parent) {
127
+ if (parent.layout) {
128
+ layouts.unshift(parent.layout);
129
+ }
130
+ parent = findParentElement(parent);
131
+ }
132
+ return layouts;
133
+ };
134
+
135
+ /**
136
+ * get available sub layouts by element
137
+ * @param element
138
+ * @returns MindLayoutType[]
139
+ */
140
+ const getAvailableSubLayoutsByElement = (element) => {
141
+ const parentElement = findParentElement(element);
142
+ if (parentElement) {
143
+ const branchLayouts = getBranchLayouts(parentElement);
144
+ if (branchLayouts[0] === MindLayoutType.standard) {
145
+ const node = MindElement.getNode(element);
146
+ branchLayouts[0] = node.left ? MindLayoutType.left : MindLayoutType.right;
147
+ }
148
+ const currentLayoutDirections = getBranchDirectionsByLayouts(branchLayouts);
149
+ let availableSubLayouts = getAvailableSubLayoutsByLayoutDirections(currentLayoutDirections);
150
+ const parentLayout = [branchLayouts[branchLayouts.length - 1]];
151
+ const parentDirections = getBranchDirectionsByLayouts(parentLayout);
152
+ const parentAvailableSubLayouts = getAvailableSubLayoutsByLayoutDirections(parentDirections);
153
+ availableSubLayouts = availableSubLayouts.filter(layout => parentAvailableSubLayouts.some(parentAvailableSubLayout => parentAvailableSubLayout === layout));
154
+ return availableSubLayouts;
155
+ }
156
+ return undefined;
157
+ };
158
+
159
+ /**
160
+ * 获取父节点布局类型
161
+ * @param element
162
+ * @returns MindLayoutType
163
+ */
164
+ const getLayoutParentByElement = (element) => {
165
+ let parent = findParentElement(element);
166
+ while (parent) {
167
+ if (parent.layout) {
168
+ return parent.layout;
169
+ }
170
+ parent = findParentElement(parent);
171
+ }
172
+ return getDefaultLayout();
173
+ };
174
+
175
+ const getLayoutByElement = (element) => {
176
+ const layout = element.layout;
177
+ if (layout) {
178
+ return layout;
179
+ }
180
+ if (AbstractNode.isAbstract(element) ||
181
+ (isChildOfAbstract(MindElement.getNode(element)) && isIndentedLayout(layout))) {
182
+ const parentLayout = getLayoutParentByElement(element);
183
+ return getAbstractLayout(parentLayout);
184
+ }
185
+ return getLayoutParentByElement(element);
186
+ };
187
+
188
+ const MindQueries = {
189
+ getAvailableSubLayoutsByElement,
190
+ getLayoutParentByElement,
191
+ getBranchLayouts,
192
+ getLayoutByElement,
193
+ getCorrectLayoutByElement
194
+ };
195
+
196
+ const PlaitMind = {
197
+ isMind: (value) => {
198
+ return value.type === 'mindmap';
199
+ }
200
+ };
201
+ const MindElement = {
202
+ hasLayout(value, layout) {
203
+ const _layout = MindQueries.getLayoutByElement(value);
204
+ return _layout === layout;
205
+ },
206
+ isIndentedLayout(value) {
207
+ const _layout = MindQueries.getLayoutByElement(value);
208
+ return isIndentedLayout(_layout);
209
+ },
210
+ isMindElement(board, element) {
211
+ const path = PlaitBoard.findPath(board, element);
212
+ const rootElement = PlaitNode.get(board, path.slice(0, 1));
213
+ if (PlaitMind.isMind(rootElement)) {
214
+ return true;
215
+ }
216
+ else {
217
+ return false;
218
+ }
219
+ },
220
+ getParent(node) {
221
+ if (PlaitMind.isMind(node)) {
222
+ throw new Error('mind root node can not get parent');
223
+ }
224
+ const parent = NODE_TO_PARENT.get(node);
225
+ return parent;
226
+ },
227
+ getRoot(board, element) {
228
+ const path = PlaitBoard.findPath(board, element);
229
+ return PlaitNode.get(board, path.slice(0, 1));
230
+ },
231
+ getAncestors(board, element) {
232
+ const path = PlaitBoard.findPath(board, element);
233
+ const parents = [];
234
+ for (const p of Path.ancestors(path, { reverse: true })) {
235
+ const n = PlaitNode.get(board, p);
236
+ if (n && !PlaitBoard.isBoard(n)) {
237
+ parents.push(n);
238
+ }
239
+ }
240
+ return parents;
241
+ },
242
+ getNode(element) {
243
+ const node = ELEMENT_TO_NODE.get(element);
244
+ if (!node) {
245
+ throw new Error(`can not get node from ${JSON.stringify(element)}`);
246
+ }
247
+ return node;
248
+ },
249
+ hasEmojis(element) {
250
+ if (element.data.emojis) {
251
+ return true;
252
+ }
253
+ else {
254
+ return false;
255
+ }
256
+ },
257
+ getEmojis(element) {
258
+ return element.data.emojis;
259
+ }
260
+ };
261
+
262
+ const MindNode = {
263
+ get(root, path) {
264
+ let node = root;
265
+ for (let i = 0; i < path.length; i++) {
266
+ const p = path[i];
267
+ if (!node || !node.children || !node.children[p]) {
268
+ throw new Error(`Cannot find a descendant at path [${path}]`);
269
+ }
270
+ node = node.children[p];
271
+ }
272
+ return node;
273
+ },
274
+ isEquals(node, otherNode) {
275
+ const hasSameSize = node.x === otherNode.x && node.y === otherNode.y && node.width === otherNode.width && node.height === otherNode.height;
276
+ const hasSameOrigin = node.origin === otherNode.origin;
277
+ let hasSameParentOriginChildren = false;
278
+ if (node.parent && otherNode.parent) {
279
+ hasSameParentOriginChildren = node.parent.origin.children == otherNode.parent.origin.children;
280
+ }
281
+ return hasSameSize && hasSameOrigin && hasSameParentOriginChildren;
282
+ }
283
+ };
284
+
285
+ var LayoutDirection;
286
+ (function (LayoutDirection) {
287
+ LayoutDirection["top"] = "top";
288
+ LayoutDirection["right"] = "right";
289
+ LayoutDirection["bottom"] = "bottom";
290
+ LayoutDirection["left"] = "left";
291
+ })(LayoutDirection || (LayoutDirection = {}));
292
+ const LayoutDirectionsMap = {
293
+ [MindLayoutType.right]: [LayoutDirection.right],
294
+ [MindLayoutType.left]: [LayoutDirection.left],
295
+ [MindLayoutType.upward]: [LayoutDirection.top],
296
+ [MindLayoutType.downward]: [LayoutDirection.bottom],
297
+ [MindLayoutType.rightBottomIndented]: [LayoutDirection.right, LayoutDirection.bottom],
298
+ [MindLayoutType.rightTopIndented]: [LayoutDirection.right, LayoutDirection.top],
299
+ [MindLayoutType.leftBottomIndented]: [LayoutDirection.left, LayoutDirection.bottom],
300
+ [MindLayoutType.leftTopIndented]: [LayoutDirection.left, LayoutDirection.top]
301
+ };
302
+
303
+ var AbstractHandlePosition;
304
+ (function (AbstractHandlePosition) {
305
+ AbstractHandlePosition["start"] = "start";
306
+ AbstractHandlePosition["end"] = "end";
307
+ })(AbstractHandlePosition || (AbstractHandlePosition = {}));
308
+ var AbstractResizeState;
309
+ (function (AbstractResizeState) {
310
+ AbstractResizeState["start"] = "start";
311
+ AbstractResizeState["resizing"] = "resizing";
312
+ AbstractResizeState["end"] = "end";
313
+ })(AbstractResizeState || (AbstractResizeState = {}));
314
+
71
315
  const getBranchDirectionsByLayouts = (branchLayouts) => {
72
316
  const branchDirections = [];
73
317
  branchLayouts.forEach(l => {
@@ -171,1468 +415,1283 @@ const getRootLayout = (root) => {
171
415
  return root.layout || getDefaultLayout();
172
416
  };
173
417
 
174
- const getNodeShapeByElement = (element) => {
175
- let nodeShape = element.shape;
176
- if (nodeShape) {
177
- return nodeShape;
178
- }
179
- let parent = findParentElement(element);
180
- while (parent) {
181
- if (parent.shape) {
182
- return parent.shape;
183
- }
184
- parent = findParentElement(parent);
185
- }
186
- return MindNodeShape.roundRectangle;
187
- };
418
+ function enterNodeEditing(element) {
419
+ const component = ELEMENT_TO_COMPONENT.get(element);
420
+ component.startEditText(false, false);
421
+ }
188
422
 
189
- function isVirtualKey(e) {
190
- const isMod = e.ctrlKey || e.metaKey;
191
- const isAlt = isKeyHotkey('alt', e);
192
- const isShift = isKeyHotkey('shift', e);
193
- const isCapsLock = e.key.includes('CapsLock');
194
- const isTab = e.key.includes('Tab');
195
- const isEsc = e.key.includes('Escape');
196
- const isF = e.key.startsWith('F');
197
- const isArrow = e.key.includes('Arrow') ? true : false;
198
- return isCapsLock || isMod || isAlt || isArrow || isShift || isTab || isEsc || isF;
199
- }
200
-
201
- function drawLink(roughSVG, node, child, defaultStroke = null, isHorizontal = true, needDrawUnderline = true) {
202
- var _a;
203
- let beginX, beginY, endX, endY, beginNode = node, endNode = child;
204
- const layout = MindQueries.getCorrectLayoutByElement(node.origin);
205
- if (isHorizontal) {
206
- if (!isChildRight(node, child)) {
207
- beginNode = child;
208
- endNode = node;
423
+ const separateChildren = (parentElement) => {
424
+ const rightNodeCount = parentElement.rightNodeCount;
425
+ const children = parentElement.children;
426
+ let rightChildren = [], leftChildren = [];
427
+ for (let i = 0; i < children.length; i++) {
428
+ const child = children[i];
429
+ if (AbstractNode.isAbstract(child) && child.end < rightNodeCount) {
430
+ rightChildren.push(child);
431
+ continue;
209
432
  }
210
- beginX = beginNode.x + beginNode.width - beginNode.hGap;
211
- beginY = beginNode.y + beginNode.height / 2;
212
- endX = endNode.x + endNode.hGap;
213
- endY = endNode.y + endNode.height / 2;
214
- if (node.parent &&
215
- isIndentedLayout(MindQueries.getLayoutByElement((_a = node.parent) === null || _a === void 0 ? void 0 : _a.origin)) &&
216
- getNodeShapeByElement(node.origin) === MindNodeShape.underline) {
217
- if (isChildRight(node, child)) {
218
- beginY = node.y + node.height - node.vGap;
219
- }
220
- else {
221
- endY = node.y + node.height - node.vGap;
222
- }
433
+ if (AbstractNode.isAbstract(child) && child.start >= rightNodeCount) {
434
+ leftChildren.push(child);
435
+ continue;
436
+ }
437
+ if (i < rightNodeCount) {
438
+ rightChildren.push(child);
439
+ }
440
+ else {
441
+ leftChildren.push(child);
223
442
  }
224
443
  }
444
+ return { leftChildren, rightChildren };
445
+ };
446
+ const isSetAbstract = (element) => {
447
+ return !!getCorrespondingAbstract(element);
448
+ };
449
+ const canSetAbstract = (element) => {
450
+ return !PlaitElement.isRootElement(element) && !AbstractNode.isAbstract(element) && !isSetAbstract(element);
451
+ };
452
+ const setAbstract = (board, elements) => {
453
+ let elementGroup = filterChildElement(elements);
454
+ const { parentElements, abstractIncludedGroups } = divideElementByParent(elementGroup);
455
+ abstractIncludedGroups.forEach((group, index) => {
456
+ const groupParent = parentElements[index];
457
+ setAbstractByElements(board, groupParent, group);
458
+ });
459
+ };
460
+ const setAbstractByElements = (board, groupParent, group) => {
461
+ const indexArray = group.map(child => groupParent.children.indexOf(child)).sort((a, b) => a - b);
462
+ const rightNodeCount = groupParent === null || groupParent === void 0 ? void 0 : groupParent.rightNodeCount;
463
+ const start = indexArray[0], end = indexArray[indexArray.length - 1];
464
+ if (isStandardLayout(MindQueries.getLayoutByElement(groupParent)) &&
465
+ rightNodeCount &&
466
+ start < rightNodeCount &&
467
+ end >= rightNodeCount) {
468
+ const childrenLength = groupParent.children.length;
469
+ const path = [...PlaitBoard.findPath(board, groupParent), childrenLength];
470
+ const leftChildren = indexArray.filter(index => index >= rightNodeCount);
471
+ const rightChildren = indexArray.filter(index => index < rightNodeCount);
472
+ insertAbstractNode(board, path, rightChildren[0], rightChildren[rightChildren.length - 1]);
473
+ insertAbstractNode(board, Path.next(path), leftChildren[0], leftChildren[leftChildren.length - 1]);
474
+ }
225
475
  else {
226
- if (node.y > child.y) {
227
- beginNode = child;
228
- endNode = node;
229
- }
230
- beginX = beginNode.x + beginNode.width / 2;
231
- beginY = beginNode.y + beginNode.height - beginNode.vGap;
232
- endX = endNode.x + endNode.width / 2;
233
- endY = endNode.y + endNode.vGap;
476
+ const path = [...PlaitBoard.findPath(board, groupParent), groupParent.children.length];
477
+ insertAbstractNode(board, path, start, end);
234
478
  }
235
- const stroke = defaultStroke || getLinkLineColorByMindElement(child.origin);
236
- const strokeWidth = child.origin.linkLineWidth ? child.origin.linkLineWidth : STROKE_WIDTH;
237
- if (endNode.origin.isRoot) {
238
- if (layout === MindLayoutType.left || isStandardLayout(layout)) {
239
- endX -= strokeWidth;
240
- }
241
- if (layout === MindLayoutType.upward) {
242
- endY -= strokeWidth;
243
- }
479
+ };
480
+ const insertAbstractNode = (board, path, start, end) => {
481
+ const mindElement = createMindElement('概要', 28, 20, {
482
+ strokeColor: GRAY_COLOR,
483
+ branchColor: GRAY_COLOR
484
+ });
485
+ mindElement.start = start;
486
+ mindElement.end = end;
487
+ Transforms.insertNode(board, mindElement, path);
488
+ };
489
+ const handleAbstractIncluded = (board, element) => {
490
+ const rightNodeCount = element.rightNodeCount;
491
+ const abstract = element.children.find(child => {
492
+ return AbstractNode.isAbstract(child) && child.end >= rightNodeCount && child.start < rightNodeCount;
493
+ });
494
+ if (abstract) {
495
+ const path = PlaitBoard.findPath(board, abstract);
496
+ Transforms.setNode(board, { end: rightNodeCount - 1 }, path);
244
497
  }
245
- if (beginNode.origin.isRoot) {
246
- if (layout === MindLayoutType.right || isStandardLayout(layout)) {
247
- beginX += strokeWidth;
248
- }
249
- if (layout === MindLayoutType.downward) {
250
- beginY += strokeWidth;
251
- }
498
+ };
499
+ const getCorrespondingAbstract = (element) => {
500
+ const parent = MindElement.getParent(element);
501
+ if (!parent)
502
+ return undefined;
503
+ const elementIndex = parent.children.indexOf(element);
504
+ return parent.children.find(child => {
505
+ return AbstractNode.isAbstract(child) && elementIndex >= child.start && elementIndex <= child.end;
506
+ });
507
+ };
508
+ const getBehindAbstracts = (element) => {
509
+ const parent = MindElement.getParent(element);
510
+ const index = parent.children.indexOf(element);
511
+ return parent.children.filter(child => AbstractNode.isAbstract(child) && child.start > index);
512
+ };
513
+ const insertSiblingElementHandleAbstract = (board, selectedElement) => {
514
+ const abstract = getCorrespondingAbstract(selectedElement);
515
+ if (abstract) {
516
+ PlaitBoard.findPath(board, abstract);
517
+ Transforms.setNode(board, { end: abstract.end + 1 }, PlaitBoard.findPath(board, abstract));
252
518
  }
253
- if (isHorizontal) {
254
- let curve = [
255
- [beginX, beginY],
256
- [beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
257
- [endX - (beginNode.hGap + endNode.hGap) / 2, endY],
258
- [endX, endY]
259
- ];
260
- const shape = getNodeShapeByElement(child.origin);
261
- if (!node.origin.isRoot) {
262
- if (node.x > child.x) {
263
- curve = [
264
- [beginX, beginY],
265
- [beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
266
- [endX - (beginNode.hGap + endNode.hGap) / 2, endY],
267
- [endX - 12, endY]
268
- ];
269
- const line = [
270
- [endX - 12, endY],
271
- [endX - 12, endY],
272
- [endX, endY]
273
- ];
274
- curve = [...curve, ...line];
275
- }
276
- else {
277
- curve = [
278
- [beginX + 12, beginY],
279
- [beginX + (beginNode.hGap + endNode.hGap) / 2, beginY],
280
- [endX - (beginNode.hGap + endNode.hGap) / 3, endY],
281
- [endX, endY]
282
- ];
283
- const line = [
284
- [beginX, beginY],
285
- [beginX + 12, beginY],
286
- [beginX + 12, beginY]
287
- ];
288
- curve = [...line, ...curve];
289
- }
290
- }
291
- if (needDrawUnderline && shape === MindNodeShape.underline) {
292
- if (child.left) {
293
- const underline = [
294
- [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
295
- [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
296
- [beginX - (beginNode.width - beginNode.hGap * 2), beginY]
297
- ];
298
- curve = [...underline, ...curve];
299
- }
300
- else {
301
- const underline = [
302
- [endX + (endNode.width - endNode.hGap * 2), endY],
303
- [endX + (endNode.width - endNode.hGap * 2), endY],
304
- [endX + (endNode.width - endNode.hGap * 2), endY]
305
- ];
306
- curve = [...curve, ...underline];
307
- }
308
- }
309
- const points = pointsOnBezierCurves(curve);
310
- return roughSVG.curve(points, { stroke, strokeWidth });
519
+ const abstracts = getBehindAbstracts(selectedElement);
520
+ if (abstracts.length) {
521
+ moveAbstractPosition(board, abstracts, 1);
311
522
  }
312
- else {
313
- let curve = [
314
- [beginX, beginY],
315
- [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
316
- [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
317
- [endX, endY]
318
- ];
319
- if (!node.origin.isRoot) {
320
- if (isTopLayout(layout)) {
321
- curve = [
322
- [beginX, beginY],
323
- [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
324
- [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
325
- [endX, endY - 12]
326
- ];
327
- const line = [
328
- [endX, endY - 12],
329
- [endX, endY - 12],
330
- [endX, endY]
331
- ];
332
- curve = [...curve, ...line];
333
- }
334
- else {
335
- curve = [
336
- [beginX, beginY + 12],
337
- [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
338
- [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
339
- [endX, endY]
340
- ];
341
- const line = [
342
- [beginX, beginY],
343
- [beginX, beginY + 12],
344
- [beginX, beginY + 12]
345
- ];
346
- curve = [...line, ...curve];
347
- }
348
- }
349
- const points = pointsOnBezierCurves(curve);
350
- return roughSVG.curve(points, { stroke, strokeWidth });
523
+ };
524
+ const moveAbstractPosition = (board, abstracts, step) => {
525
+ abstracts.forEach(abstract => {
526
+ Transforms.setNode(board, { start: abstract.start + step, end: abstract.end + step }, PlaitBoard.findPath(board, abstract));
527
+ });
528
+ };
529
+
530
+ function findParentElement(element) {
531
+ const component = PlaitElement.getComponent(element);
532
+ if (component && component.parent) {
533
+ return component.parent.origin;
351
534
  }
535
+ return undefined;
352
536
  }
353
-
354
- const drawPlaceholderDropNodeG = (dropTarget, roughSVG, fakeDropNodeG) => {
355
- const targetComponent = PlaitElement.getComponent(dropTarget.target);
356
- const targetRect = getRectangleByNode(targetComponent.node);
357
- if (dropTarget.detectResult && ['right', 'left'].includes(dropTarget.detectResult)) {
358
- drawStraightDropNodeG(targetRect, dropTarget.detectResult, targetComponent, roughSVG, fakeDropNodeG);
537
+ function findUpElement(element) {
538
+ let branch;
539
+ let root = element;
540
+ let parent = findParentElement(element);
541
+ while (parent) {
542
+ branch = root;
543
+ root = parent;
544
+ parent = findParentElement(parent);
359
545
  }
360
- if (targetComponent.parent && dropTarget.detectResult && ['top', 'bottom'].includes(dropTarget.detectResult)) {
361
- const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
362
- const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
363
- drawCurvePlaceholderDropNodeG(targetRect, dropTarget.detectResult, targetIndex, targetComponent, roughSVG, parentComponent, fakeDropNodeG);
546
+ return { root, branch };
547
+ }
548
+ const getChildrenCount = (element) => {
549
+ const count = element.children.reduce((p, c) => {
550
+ return p + getChildrenCount(c);
551
+ }, 0);
552
+ return count + element.children.length;
553
+ };
554
+ const isChildElement = (origin, child) => {
555
+ let parent = findParentElement(child);
556
+ while (parent) {
557
+ if (parent === origin) {
558
+ return true;
559
+ }
560
+ parent = findParentElement(parent);
364
561
  }
562
+ return false;
365
563
  };
366
- const drawCurvePlaceholderDropNodeG = (targetRect, detectResult, targetIndex, targetComponent, roughSVG, parentComponent, fakeDropNodeG) => {
367
- const parentNodeLayout = MindQueries.getCorrectLayoutByElement(parentComponent.node.origin);
368
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.parent.origin);
369
- const strokeWidth = targetComponent.node.origin.linkLineWidth ? targetComponent.node.origin.linkLineWidth : STROKE_WIDTH;
370
- let fakeX = targetComponent.node.x, fakeY = targetRect.y - 30, fakeRectangleStartX = targetRect.x, fakeRectangleEndX = targetRect.x + 30, fakeRectangleStartY = fakeY, fakeRectangleEndY = fakeRectangleStartY + 12, width = 30;
371
- if (isLeftLayout(layout)) {
372
- fakeX = targetComponent.node.x + targetComponent.node.width - 30;
373
- fakeRectangleStartX = targetRect.x + targetRect.width - 30;
374
- fakeRectangleEndX = targetRect.x + targetRect.width;
375
- }
376
- if (isHorizontalLogicLayout(parentNodeLayout)) {
377
- fakeY = getHorizontalFakeY(detectResult, targetIndex, parentComponent.node, targetRect, layout, fakeY);
378
- if (isStandardLayout(parentNodeLayout)) {
379
- const rightNodeCount = parentComponent.node.origin.rightNodeCount || 0;
380
- const idx = parentComponent.node.children.findIndex(x => x === targetComponent.node);
381
- const isLeft = idx >= rightNodeCount;
382
- // 标准布局的左,需要调整 x
383
- if (isLeft) {
384
- fakeX = targetComponent.node.x + targetComponent.node.width - 30;
385
- fakeRectangleStartX = targetRect.x + targetRect.width - 30;
386
- fakeRectangleEndX = targetRect.x + targetRect.width;
387
- }
388
- const isLeftFirst = idx === rightNodeCount;
389
- const isRightLast = idx === rightNodeCount - 1;
390
- // 拖拽至左第一个节点的情况
391
- if (detectResult === 'top' && isLeftFirst) {
392
- fakeY = targetRect.y - targetRect.height;
393
- }
394
- if (detectResult === 'bottom' && isRightLast) {
395
- fakeY = targetRect.y + targetRect.height + 30;
396
- }
397
- }
398
- fakeRectangleStartY = fakeY;
399
- fakeRectangleEndY = fakeRectangleStartY + 12;
400
- }
401
- if (isVerticalLogicLayout(layout)) {
402
- parentComponent = targetComponent;
403
- targetComponent = PlaitElement.getComponent(targetComponent.parent.origin);
404
- fakeX = parentComponent.node.x;
405
- width = parentComponent.node.width;
406
- const vGap = BASE * 6 + strokeWidth;
407
- if (isTopLayout(layout) && detectResult === 'top') {
408
- fakeY = targetRect.y - vGap;
409
- fakeRectangleStartY = fakeY - vGap + strokeWidth;
410
- }
411
- if (isBottomLayout(layout) && detectResult === 'bottom') {
412
- fakeY = targetRect.y + targetRect.height + vGap;
413
- fakeRectangleStartY = fakeY + vGap - strokeWidth;
414
- }
415
- fakeRectangleStartX = fakeX + Math.ceil(parentComponent.node.width / 2) - parentComponent.node.hGap - Math.ceil(strokeWidth / 2);
416
- fakeRectangleEndX = fakeRectangleStartX + 30;
417
- fakeRectangleEndY = fakeRectangleStartY + 12;
418
- }
419
- if (isIndentedLayout(layout)) {
420
- // 偏移一个 Gap
421
- if (isLeftLayout(layout)) {
422
- fakeX -= BASE * 4;
423
- }
424
- if (isRightLayout(layout)) {
425
- fakeX += BASE * 4;
426
- }
427
- if (isTopLayout(layout)) {
428
- if (detectResult === 'top') {
429
- const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
430
- if (isLastNode) {
431
- fakeY = targetRect.y - targetRect.height - BASE;
432
- }
433
- else {
434
- const nextComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
435
- const nextRect = getRectangleByNode(nextComponent.node);
436
- fakeY = targetRect.y - Math.abs((nextRect.y + nextRect.height - targetRect.y) / 2);
437
- }
438
- }
439
- if (detectResult === 'bottom') {
440
- const isFirstNode = targetIndex === 0;
441
- if (isFirstNode) {
442
- const parentRect = getRectangleByNode(parentComponent.node);
443
- fakeY = parentRect.y - parentRect.height / 2 - BASE;
444
- }
445
- else {
446
- const previousComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
447
- const previousRect = getRectangleByNode(previousComponent.node);
448
- fakeY = previousRect.y - Math.abs((targetRect.y + targetRect.height - previousRect.y) / 2);
449
- }
450
- }
451
- }
452
- fakeRectangleStartX = fakeX;
453
- fakeRectangleEndX = fakeRectangleStartX + 30;
454
- fakeRectangleStartY = fakeY;
455
- fakeRectangleEndY = fakeRectangleStartY + 12;
456
- }
457
- // 构造一条曲线
458
- const fakeNode = Object.assign(Object.assign({}, targetComponent.node), { x: fakeX, y: fakeY, width, height: 12 });
459
- const linkSVGG = isIndentedLayout(layout)
460
- ? drawIndentedLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, false)
461
- : drawLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, isHorizontalLayout(layout), false);
462
- // 构造一个矩形框坐标
463
- const fakeRectangleG = drawRoundRectangle(roughSVG, fakeRectangleStartX, fakeRectangleStartY, fakeRectangleEndX, fakeRectangleEndY, {
464
- stroke: PRIMARY_COLOR,
465
- strokeWidth: 2,
466
- fill: PRIMARY_COLOR,
467
- fillStyle: 'solid'
468
- });
469
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
470
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
471
- };
472
- const drawStraightDropNodeG = (targetRect, detectResult, targetComponent, roughSVG, fakeDropNodeG) => {
473
- const { x, y, width, height } = targetRect;
474
- const lineLength = 40;
475
- let startLinePoint = x + width;
476
- let endLinePoint = x + width + lineLength;
477
- let startRectanglePointX = x + width + lineLength;
478
- let endRectanglePointX = x + lineLength + width + 30;
479
- let startRectanglePointY = y + height / 2 - 6;
480
- let endRectanglePointY = y + height / 2 - 6 + 12;
481
- if (detectResult === 'left') {
482
- startLinePoint = x - lineLength;
483
- endLinePoint = x;
484
- startRectanglePointX = x - lineLength - 30;
485
- endRectanglePointX = x - lineLength;
486
- }
487
- let fakeY = targetComponent.node.y;
488
- let fakeX = targetRect.x;
489
- const strokeWidth = targetComponent.node.origin.linkLineWidth ? targetComponent.node.origin.linkLineWidth : STROKE_WIDTH;
490
- const pointOptions = {
491
- fakeX,
492
- fakeY,
493
- x,
494
- y,
495
- width,
496
- height,
497
- strokeWidth
498
- };
499
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
500
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
501
- if (!isMixedLayout(parentLayout, layout)) {
502
- // 构造一条直线
503
- let linePoints = [
504
- [startLinePoint, y + height / 2],
505
- [endLinePoint, y + height / 2]
506
- ];
507
- if (isIndentedLayout(parentLayout)) {
508
- const fakePoint = getIndentedFakePoint(parentLayout, pointOptions);
509
- drawIndentNodeG(fakeDropNodeG, roughSVG, fakePoint, targetComponent.node);
510
- return;
511
- }
512
- else if (isVerticalLogicLayout(parentLayout)) {
513
- if (!targetComponent.node.origin.isRoot) {
514
- /**
515
- * 计算逻辑:
516
- * 1. 移动到左侧:当前节点 startX - 偏移值,偏移值计算如下:
517
- * a. 第一个节点: 固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
518
- * b. 第二个节点到最后一个节点之间:上一个节点到当前节点间距的一半((当前节点 startX - 上一个节点的 endX) / 2),endX = 当前节点的 startX + width;
519
- * 2. 移动到右侧:当前节点 x + width + 偏移值,偏移值计算如下:
520
- * a. 第二个节点到最后一个节点之间的右侧:当前节点到下一个节点间距的一半((下一个节点 startX - 当前节点的 endX) / 2),endX = 当前节点的 startX + width;
521
- * b. 最后一个节点的右侧:固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
522
- */
523
- fakeY = targetComponent.node.y;
524
- const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
525
- const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
526
- if (detectResult === 'left') {
527
- let offsetX = 0;
528
- const isFirstNode = targetIndex === 0;
529
- if (isFirstNode) {
530
- offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
531
- }
532
- else {
533
- const previousComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex - 1]);
534
- const previousRect = getRectangleByNode(previousComponent.node);
535
- const space = targetRect.x - (previousRect.x + previousRect.width);
536
- offsetX = space / 2;
537
- }
538
- fakeX = targetRect.x - offsetX - width / 2 - Math.ceil(strokeWidth / 2);
539
- }
540
- if (detectResult === 'right') {
541
- let offsetX = 0;
542
- const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
543
- if (isLastNode) {
544
- offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
545
- }
546
- else {
547
- const nextComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
548
- const nextRect = getRectangleByNode(nextComponent.node);
549
- const space = nextRect.x - (targetRect.x + targetRect.width);
550
- offsetX = space / 2;
551
- }
552
- fakeX = targetRect.x + width + offsetX - width / 2 - Math.ceil(strokeWidth / 2);
553
- }
554
- startRectanglePointX = fakeX;
555
- if (isTopLayout(parentLayout)) {
556
- // 因为矩形是从左上角为起点向下画的,所以需要向上偏移一个矩形的高度(-12)
557
- startRectanglePointY = fakeY + height + targetComponent.node.vGap - 12;
558
- }
559
- if (isBottomLayout(parentLayout)) {
560
- startRectanglePointY = fakeY + targetComponent.node.vGap;
561
- }
562
- endRectanglePointX = startRectanglePointX + 30;
563
- endRectanglePointY = startRectanglePointY + 12;
564
- const fakeNode = Object.assign(Object.assign({}, targetComponent.node), { x: fakeX, y: fakeY, width: 30 });
565
- const linkSVGG = drawLink(roughSVG, parentComponent.node, fakeNode, PRIMARY_COLOR, false, false);
566
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
567
- }
568
- }
569
- else {
570
- let linkSVGG = roughSVG.linearPath(linePoints, { stroke: PRIMARY_COLOR, strokeWidth });
571
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
572
- }
573
- // 构造一个矩形框坐标
574
- let fakeRectangleG = drawRoundRectangle(roughSVG, startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
575
- stroke: PRIMARY_COLOR,
576
- strokeWidth: 2,
577
- fill: PRIMARY_COLOR,
578
- fillStyle: 'solid'
579
- });
580
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
581
- }
582
- else {
583
- // 混合布局画线逻辑
584
- if (isHorizontalLogicLayout(parentLayout)) {
585
- if (isIndentedLayout(layout)) {
586
- const fakePoint = getIndentedFakePoint(layout, pointOptions);
587
- drawIndentNodeG(fakeDropNodeG, roughSVG, fakePoint, targetComponent.node);
588
- return;
589
- }
590
- }
591
- }
592
- };
593
- const getHorizontalFakeY = (detectResult, targetIndex, parentNode, targetRect, layout, fakeY) => {
594
- if (detectResult === 'top') {
595
- if (targetIndex === 0 && isTopLayout(layout)) {
596
- fakeY = targetRect.y + targetRect.height;
597
- }
598
- if (targetIndex > 0) {
599
- const previousComponent = PlaitElement.getComponent(parentNode.origin.children[targetIndex - 1]);
600
- const previousRect = getRectangleByNode(previousComponent.node);
601
- const topY = previousRect.y + previousRect.height;
602
- fakeY = topY + (targetRect.y - topY) / 5;
603
- }
604
- }
605
- if (detectResult === 'bottom') {
606
- fakeY = targetRect.y + targetRect.height + 30;
607
- if (targetIndex < parentNode.origin.children.length - 1) {
608
- const nextComponent = PlaitElement.getComponent(parentNode.origin.children[targetIndex + 1]);
609
- const nextRect = getRectangleByNode(nextComponent.node);
610
- const topY = targetRect.y + targetRect.height;
611
- fakeY = topY + (nextRect.y - topY) / 5;
612
- }
613
- if (targetIndex === parentNode.origin.children.length - 1) {
614
- fakeY = targetRect.y + targetRect.height + 30;
615
- }
616
- }
617
- return fakeY;
618
- };
619
- const getIndentedFakePoint = (layout, pointOptions) => {
620
- let { fakeX, fakeY, x, y, width, height, strokeWidth } = pointOptions;
621
- const hGap = BASE * 4;
622
- const vGap = BASE * 6;
623
- const offsetX = hGap + width / 2 + strokeWidth;
624
- const offsetY = vGap + height / 2 + strokeWidth;
625
- if (isLeftLayout(layout)) {
626
- fakeX = x - offsetX;
627
- }
628
- if (isRightLayout(layout)) {
629
- fakeX = x + offsetX;
630
- }
631
- if (isTopLayout(layout)) {
632
- fakeY = y - offsetY;
633
- }
634
- if (isBottomLayout(layout)) {
635
- fakeY = y + height + offsetY;
636
- }
637
- return { fakeX, fakeY };
638
- };
639
- const drawIndentNodeG = (fakeDropNodeG, roughSVG, fakePoint, node) => {
640
- const { fakeX, fakeY } = fakePoint;
641
- const fakeNode = Object.assign(Object.assign({}, node), { x: fakeX, y: fakeY, width: 30, height: 12 });
642
- const linkSVGG = drawIndentedLink(roughSVG, node, fakeNode, PRIMARY_COLOR, false);
643
- const startRectanglePointX = fakeX, startRectanglePointY = fakeY, endRectanglePointX = fakeX + 30, endRectanglePointY = fakeY + 12;
644
- const fakeRectangleG = drawRoundRectangle(roughSVG, startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
645
- stroke: PRIMARY_COLOR,
646
- strokeWidth: 2,
647
- fill: PRIMARY_COLOR,
648
- fillStyle: 'solid'
649
- });
650
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
651
- fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
652
- };
653
-
654
- /**
655
- *
656
- * @param targetNode
657
- * @param centerPoint
658
- * @returns DetectResult[] | null
659
- */
660
- const directionDetector = (targetNode, centerPoint) => {
661
- const { x, y, width, height } = getRectangleByNode(targetNode);
662
- const yCenter = y + height / 2;
663
- const xCenter = x + width / 2;
664
- const top = targetNode.y;
665
- const bottom = targetNode.y + targetNode.height;
666
- const left = targetNode.x;
667
- const right = targetNode.x + targetNode.width;
668
- const direction = [];
669
- // x 轴
670
- if (centerPoint[1] > y && centerPoint[1] < y + height) {
671
- if (centerPoint[0] > left && centerPoint[0] < xCenter) {
672
- direction.push('left');
673
- }
674
- if (centerPoint[0] > xCenter && centerPoint[0] < right) {
675
- direction.push('right');
676
- }
677
- // 重合区域,返回两个方向
678
- if ((centerPoint[0] > x && centerPoint[0] < xCenter) || (centerPoint[0] > xCenter && centerPoint[0] < x + width)) {
679
- if (centerPoint[1] < yCenter) {
680
- direction.push('top');
681
- }
682
- else {
683
- direction.push('bottom');
684
- }
685
- }
686
- return direction.length ? direction : null;
687
- }
688
- // y 轴
689
- if (centerPoint[0] > x && centerPoint[0] < x + width) {
690
- if (centerPoint[1] > top && centerPoint[1] < yCenter) {
691
- direction.push('top');
692
- }
693
- if (centerPoint[1] > yCenter && centerPoint[1] < bottom) {
694
- direction.push('bottom');
695
- }
696
- if ((centerPoint[1] > y && centerPoint[1] < y + height) || (centerPoint[1] > yCenter && centerPoint[1] < y + height)) {
697
- if (centerPoint[0] < xCenter) {
698
- direction.push('left');
699
- }
700
- else {
701
- direction.push('right');
702
- }
564
+ const filterChildElement = (elements) => {
565
+ let result = [];
566
+ elements.forEach(element => {
567
+ const isChild = elements.some(node => {
568
+ return isChildElement(node, element);
569
+ });
570
+ if (!isChild) {
571
+ result.push(element);
703
572
  }
704
- return direction.length ? direction : null;
705
- }
706
- return null;
573
+ });
574
+ return result;
707
575
  };
708
-
709
- const directionCorrector = (node, detectResults) => {
710
- if (!node.origin.isRoot) {
711
- const parentlayout = MindQueries.getCorrectLayoutByElement(node === null || node === void 0 ? void 0 : node.parent.origin);
712
- if (isStandardLayout(parentlayout)) {
713
- const idx = node.parent.children.findIndex(x => x === node);
714
- const isLeft = idx >= (node.parent.origin.rightNodeCount || 0);
715
- return getAllowedDirection(detectResults, [isLeft ? 'right' : 'left']);
716
- }
717
- if (isLeftLayout(parentlayout)) {
718
- return getAllowedDirection(detectResults, ['right']);
719
- }
720
- if (isRightLayout(parentlayout)) {
721
- return getAllowedDirection(detectResults, ['left']);
722
- }
723
- if (parentlayout === MindLayoutType.upward) {
724
- return getAllowedDirection(detectResults, ['bottom']);
725
- }
726
- if (parentlayout === MindLayoutType.downward) {
727
- return getAllowedDirection(detectResults, ['top']);
728
- }
576
+ const isChildRight = (node, child) => {
577
+ return node.x < child.x;
578
+ };
579
+ const isChildUp = (node, child) => {
580
+ return node.y > child.y;
581
+ };
582
+ const copyNewNode = (node) => {
583
+ const newNode = Object.assign({}, node);
584
+ newNode.id = idCreator();
585
+ newNode.children = [];
586
+ for (const childNode of node.children) {
587
+ newNode.children.push(copyNewNode(childNode));
729
588
  }
730
- else {
731
- const layout = MindQueries.getCorrectLayoutByElement(node === null || node === void 0 ? void 0 : node.origin);
732
- if (isStandardLayout(layout)) {
733
- return getAllowedDirection(detectResults, ['top', 'bottom']);
734
- }
735
- if (isTopLayout(layout)) {
736
- return getAllowedDirection(detectResults, ['left', 'right', 'bottom']);
737
- }
738
- if (isBottomLayout(layout)) {
739
- return getAllowedDirection(detectResults, ['left', 'right', 'top']);
740
- }
741
- if (layout === MindLayoutType.left) {
742
- return getAllowedDirection(detectResults, ['right', 'top', 'bottom']);
743
- }
744
- if (layout === MindLayoutType.right) {
745
- return getAllowedDirection(detectResults, ['left', 'top', 'bottom']);
746
- }
589
+ return newNode;
590
+ };
591
+ const transformRootToNode = (board, node) => {
592
+ const newNode = Object.assign({}, node);
593
+ delete newNode.isRoot;
594
+ delete newNode.rightNodeCount;
595
+ delete newNode.type;
596
+ const text = Node.string(node.data.topic.children[0]) || ' ';
597
+ const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT);
598
+ newNode.width = Math.max(width, NODE_MIN_WIDTH);
599
+ newNode.height = height;
600
+ if (newNode.layout === MindLayoutType.standard) {
601
+ delete newNode.layout;
747
602
  }
748
- return null;
603
+ return newNode;
749
604
  };
750
- const getAllowedDirection = (detectResults, illegalDirections) => {
751
- const directions = detectResults;
752
- illegalDirections.forEach(item => {
753
- const bottomDirectionIndex = directions.findIndex(direction => direction === item);
754
- if (bottomDirectionIndex !== -1) {
755
- directions.splice(bottomDirectionIndex, 1);
756
- }
757
- });
758
- return directions.length ? directions : null;
605
+ const transformAbstractToNode = (node) => {
606
+ const newNode = Object.assign({}, node);
607
+ delete newNode.start;
608
+ delete newNode.end;
609
+ return newNode;
759
610
  };
760
-
761
- /* 根据布局调整 target 以及 direction */
762
- const readjustmentDropTarget = (dropTarget) => {
763
- const { target, detectResult } = dropTarget;
764
- const newDropTarget = { target, detectResult };
765
- const targetComponent = PlaitElement.getComponent(target);
766
- if (targetComponent.node.children.length > 0 && dropTarget.detectResult) {
767
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
768
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
769
- if (['right', 'left'].includes(dropTarget.detectResult)) {
770
- if (!isMixedLayout(parentLayout, layout)) {
771
- if (targetComponent.node.origin.isRoot) {
772
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
773
- // 标准布局,根节点
774
- if (isStandardLayout(layout)) {
775
- const rightNodeCount = targetComponent.node.origin.rightNodeCount;
776
- if (detectResult === 'left') {
777
- // 作为左的第一个节点
778
- if (targetComponent.node.children.length === rightNodeCount) {
779
- return newDropTarget;
780
- }
781
- }
782
- else {
783
- // 作为右的第一个节点或最后一个节点
784
- if (rightNodeCount === 0) {
785
- newDropTarget.target = target;
786
- }
787
- else {
788
- newDropTarget.target = targetComponent.node.children[rightNodeCount - 1].origin;
789
- newDropTarget.detectResult = 'bottom';
790
- }
791
- return newDropTarget;
792
- }
793
- }
794
- }
795
- // 缩进布局探测到第一个子节点
796
- if (isIndentedLayout(parentLayout)) {
797
- newDropTarget.target = targetComponent.node.children[0].origin;
798
- newDropTarget.detectResult = isTopLayout(parentLayout) ? 'bottom' : 'top';
799
- return newDropTarget;
800
- }
801
- // 上下布局的根节点只可以探测到上或者下,子节点的左右探测不处理,跳过。
802
- if (isVerticalLogicLayout(parentLayout)) {
803
- return newDropTarget;
804
- }
805
- // 剩下是水平布局的默认情况:插入最后一个子节点的下方
806
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
807
- newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
808
- newDropTarget.detectResult = 'bottom';
809
- }
810
- else {
811
- // 处理左右布局下的混合布局
812
- if ([MindLayoutType.left, MindLayoutType.right].includes(parentLayout)) {
813
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
814
- if (isIndentedLayout(layout)) {
815
- newDropTarget.target = targetComponent.node.children[0].origin;
816
- newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
817
- return newDropTarget;
818
- }
819
- }
820
- }
821
- }
822
- if (['top', 'bottom'].includes(dropTarget.detectResult)) {
823
- // 缩进布局移动至第一个节点
824
- if (targetComponent.node.origin.isRoot && isIndentedLayout(layout)) {
825
- newDropTarget.target = targetComponent.node.children[0].origin;
826
- newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
827
- return newDropTarget;
828
- }
829
- // 上下布局,插到右边
830
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
831
- if (isVerticalLogicLayout(parentLayout)) {
832
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
833
- newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
834
- newDropTarget.detectResult = 'right';
835
- return newDropTarget;
836
- }
837
- }
838
- return newDropTarget;
611
+ const transformNodeToRoot = (board, node) => {
612
+ var _a;
613
+ const newElement = Object.assign({}, node);
614
+ let text = Node.string(newElement.data.topic);
615
+ if (!text) {
616
+ text = '思维导图';
617
+ newElement.data.topic = { children: [{ text }] };
839
618
  }
840
- return dropTarget;
619
+ newElement === null || newElement === void 0 ? true : delete newElement.strokeColor;
620
+ newElement === null || newElement === void 0 ? true : delete newElement.fill;
621
+ newElement === null || newElement === void 0 ? true : delete newElement.shape;
622
+ newElement === null || newElement === void 0 ? true : delete newElement.strokeWidth;
623
+ const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT, ROOT_TOPIC_FONT_SIZE);
624
+ newElement.width = Math.max(width, NODE_MIN_WIDTH);
625
+ newElement.height = height;
626
+ return Object.assign(Object.assign({}, newElement), { layout: (_a = newElement.layout) !== null && _a !== void 0 ? _a : MindLayoutType.right, isCollapsed: false, isRoot: true, type: 'mindmap' });
841
627
  };
842
-
843
- const separateChildren = (parentElement) => {
844
- const rightNodeCount = parentElement.rightNodeCount;
845
- const children = parentElement.children;
846
- let rightChildren = [], leftChildren = [];
847
- for (let i = 0; i < children.length; i++) {
848
- const child = children[i];
849
- if (AbstractNode.isAbstract(child) && child.end < rightNodeCount) {
850
- rightChildren.push(child);
851
- continue;
852
- }
853
- if (AbstractNode.isAbstract(child) && child.start >= rightNodeCount) {
854
- leftChildren.push(child);
855
- continue;
856
- }
857
- if (i < rightNodeCount) {
858
- rightChildren.push(child);
859
- }
860
- else {
861
- leftChildren.push(child);
628
+ const extractNodesText = (node) => {
629
+ let str = '';
630
+ if (node) {
631
+ str += Node.string(node.data.topic.children[0]) + ' ';
632
+ for (const childNode of node.children) {
633
+ str += extractNodesText(childNode);
862
634
  }
863
635
  }
864
- return { leftChildren, rightChildren };
636
+ return str;
865
637
  };
866
- const isSetAbstract = (element) => {
867
- const component = PlaitElement.getComponent(element);
868
- const parent = component.parent;
869
- if (!parent)
870
- return false;
871
- const elementIndex = parent.children.indexOf(component.node);
872
- return parent.children.some(child => {
873
- return AbstractNode.isAbstract(child.origin) && elementIndex >= child.origin.start && elementIndex <= child.origin.end;
874
- });
638
+ const changeRightNodeCount = (board, parentPath, changeNumber) => {
639
+ const _rightNodeCount = board.children[parentPath[0]].rightNodeCount;
640
+ Transforms.setNode(board, {
641
+ rightNodeCount: changeNumber >= 0
642
+ ? _rightNodeCount + changeNumber
643
+ : _rightNodeCount + changeNumber < 0
644
+ ? 0
645
+ : _rightNodeCount + changeNumber
646
+ }, parentPath);
875
647
  };
876
- const canSetAbstract = (element) => {
877
- return !PlaitElement.isRootElement(element) && !AbstractNode.isAbstract(element) && !isSetAbstract(element);
648
+ const shouldChangeRightNodeCount = (selectedElement) => {
649
+ const parentElement = findParentElement(selectedElement);
650
+ if (parentElement) {
651
+ const nodeIndex = parentElement.children.findIndex(item => item.id === selectedElement.id);
652
+ if (parentElement.isRoot &&
653
+ getRootLayout(parentElement) === MindLayoutType.standard &&
654
+ parentElement.rightNodeCount &&
655
+ nodeIndex <= parentElement.rightNodeCount - 1) {
656
+ return true;
657
+ }
658
+ }
659
+ return false;
878
660
  };
879
- const setAbstract = (board, elements) => {
880
- let elementGroup = filterChildElement(elements);
881
- const { parentElements, abstractIncludedGroups } = divideElementByParent(elementGroup);
882
- abstractIncludedGroups.forEach((group, index) => {
883
- const groupParent = parentElements[index];
884
- setAbstractByElements(board, groupParent, group);
661
+ const createDefaultMindMapElement = (point, rightNodeCount, layout) => {
662
+ const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle, layout });
663
+ root.rightNodeCount = rightNodeCount;
664
+ root.isRoot = true;
665
+ root.type = 'mindmap';
666
+ root.points = [point];
667
+ const children = [1, 1, 1].map(() => {
668
+ return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle });
885
669
  });
670
+ root.children = children;
671
+ return root;
886
672
  };
887
- const setAbstractByElements = (board, groupParent, group) => {
888
- const indexArray = group.map(child => groupParent.children.indexOf(child)).sort((a, b) => a - b);
889
- const rightNodeCount = groupParent === null || groupParent === void 0 ? void 0 : groupParent.rightNodeCount;
890
- const start = indexArray[0], end = indexArray[indexArray.length - 1];
891
- if (isStandardLayout(MindQueries.getLayoutByElement(groupParent)) &&
892
- rightNodeCount &&
893
- start < rightNodeCount &&
894
- end >= rightNodeCount) {
895
- const childrenLength = groupParent.children.length;
896
- const path = [...PlaitBoard.findPath(board, groupParent), childrenLength];
897
- const leftChildren = indexArray.filter(index => index >= rightNodeCount);
898
- const rightCHildren = indexArray.filter(index => index < rightNodeCount);
899
- insetAbstractNode(board, path, rightCHildren[0], rightCHildren[rightCHildren.length - 1]);
900
- insetAbstractNode(board, Path.next(path), leftChildren[0], leftChildren[leftChildren.length - 1]);
673
+ const createMindElement = (text, width, height, options) => {
674
+ const newElement = {
675
+ id: idCreator(),
676
+ data: {
677
+ topic: { children: [{ text }] }
678
+ },
679
+ children: [],
680
+ width,
681
+ height,
682
+ fill: options.fill,
683
+ strokeColor: options.strokeColor,
684
+ strokeWidth: options.strokeWidth,
685
+ shape: options.shape
686
+ };
687
+ if (options.fill) {
688
+ newElement.fill = options.fill;
901
689
  }
902
- else {
903
- const path = [...PlaitBoard.findPath(board, groupParent), groupParent.children.length];
904
- insetAbstractNode(board, path, start, end);
690
+ if (options.strokeColor) {
691
+ newElement.strokeColor = options.strokeColor;
692
+ }
693
+ if (!isNullOrUndefined(options.strokeWidth)) {
694
+ newElement.strokeWidth = options.strokeWidth;
905
695
  }
696
+ if (options.shape) {
697
+ newElement.shape = options.shape;
698
+ }
699
+ if (options.layout) {
700
+ newElement.layout = options.layout;
701
+ }
702
+ if (options.branchColor) {
703
+ newElement.branchColor = options.branchColor;
704
+ }
705
+ return newElement;
906
706
  };
907
- const insetAbstractNode = (board, path, start, end) => {
908
- const mindElement = createMindElement('概要', 28, 20, {
909
- strokeColor: GRAY_COLOR,
910
- linkLineColor: GRAY_COLOR
707
+ // layoutLevel 用来表示插入兄弟节点还是子节点
708
+ const insertMindElement = (board, inheritNode, path) => {
709
+ let fill, strokeColor, strokeWidth, shape = MindNodeShape.roundRectangle;
710
+ if (!inheritNode.isRoot) {
711
+ fill = inheritNode.fill;
712
+ strokeColor = inheritNode.strokeColor;
713
+ strokeWidth = inheritNode.strokeWidth;
714
+ }
715
+ shape = inheritNode.shape;
716
+ const newElement = createMindElement('', NODE_MIN_WIDTH, TEXT_DEFAULT_HEIGHT, { fill, strokeColor, strokeWidth, shape });
717
+ Transforms.insertNode(board, newElement, path);
718
+ clearSelectedElement(board);
719
+ addSelectedElement(board, newElement);
720
+ setTimeout(() => {
721
+ enterNodeEditing(newElement);
911
722
  });
912
- mindElement.start = start;
913
- mindElement.end = end;
914
- Transforms.insertNode(board, mindElement, path);
915
723
  };
916
-
917
- const getRectangleByResizingLocation = (abstractRectangle, location, activeHandlePosition, isHorizontal) => {
918
- if (isHorizontal) {
919
- if (activeHandlePosition === AbstractHandlePosition.start) {
920
- return Object.assign(Object.assign({}, abstractRectangle), { y: location, height: abstractRectangle.height + abstractRectangle.y - location });
921
- }
922
- else {
923
- return Object.assign(Object.assign({}, abstractRectangle), { height: location - abstractRectangle.y });
924
- }
925
- }
926
- else {
927
- if (activeHandlePosition === AbstractHandlePosition.start) {
928
- return Object.assign(Object.assign({}, abstractRectangle), { x: location, width: abstractRectangle.width + abstractRectangle.x - location });
929
- }
930
- else {
931
- return Object.assign(Object.assign({}, abstractRectangle), { width: location - abstractRectangle.x });
932
- }
724
+ const findLastChild = (child) => {
725
+ let result = child;
726
+ while (result.children.length !== 0) {
727
+ result = result.children[result.children.length - 1];
933
728
  }
729
+ return result;
934
730
  };
935
- const getLocationScope = (board, handlePosition, parentChildren, element, parent, isHorizontal) => {
936
- const node = MindElement.getNode(element);
937
- const { start, end } = getCorrectStartEnd(node.origin, parent);
938
- const startNode = parentChildren[start];
939
- const endNode = parentChildren[end];
940
- if (handlePosition === AbstractHandlePosition.start) {
941
- const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.end < element.start);
942
- let minNode;
943
- if (abstractNode.length) {
944
- const index = abstractNode
945
- .map(node => {
946
- const { end } = getCorrectStartEnd(node, parent);
947
- return end;
948
- })
949
- .sort((a, b) => b - a)[0];
950
- minNode = parentChildren[index + 1];
951
- }
952
- else {
953
- minNode = parentChildren[0];
954
- }
955
- const minNodeRectangle = getRectangleByElements(board, [minNode], true);
956
- const endNodeRectangle = getRectangleByElements(board, [endNode], false);
957
- if (isHorizontal) {
958
- return {
959
- max: endNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
960
- min: minNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET
961
- };
731
+ const deleteSelectedELements = (board, selectedElements) => {
732
+ //翻转,从下到上修改,防止找不到 path
733
+ const deletableElements = filterChildElement(selectedElements).reverse();
734
+ const relativeAbstracts = [];
735
+ const accumulativeProperties = new WeakMap();
736
+ deletableElements.forEach(node => {
737
+ if (!PlaitMind.isMind(node)) {
738
+ const behindAbstracts = getBehindAbstracts(node).filter(abstract => !deletableElements.includes(abstract));
739
+ if (behindAbstracts.length) {
740
+ behindAbstracts.forEach(abstract => {
741
+ let newProperties = accumulativeProperties.get(abstract);
742
+ if (!newProperties) {
743
+ newProperties = { start: abstract.start, end: abstract.end };
744
+ accumulativeProperties.set(abstract, newProperties);
745
+ relativeAbstracts.push(abstract);
746
+ }
747
+ newProperties.start = newProperties.start - 1;
748
+ newProperties.end = newProperties.end - 1;
749
+ });
750
+ }
751
+ const correspondingAbstract = getCorrespondingAbstract(node);
752
+ if (correspondingAbstract && !deletableElements.includes(correspondingAbstract)) {
753
+ let newProperties = accumulativeProperties.get(correspondingAbstract);
754
+ if (!newProperties) {
755
+ newProperties = { start: correspondingAbstract.start, end: correspondingAbstract.end };
756
+ accumulativeProperties.set(correspondingAbstract, newProperties);
757
+ relativeAbstracts.push(correspondingAbstract);
758
+ }
759
+ newProperties.end = newProperties.end - 1;
760
+ }
962
761
  }
963
- else {
964
- return {
965
- max: endNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
966
- min: minNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET
762
+ });
763
+ const abstractHandles = relativeAbstracts.map(value => {
764
+ const newProperties = accumulativeProperties.get(value);
765
+ if (newProperties) {
766
+ const path = PlaitBoard.findPath(board, value);
767
+ return () => {
768
+ if (newProperties.start > newProperties.end) {
769
+ Transforms.removeNode(board, path);
770
+ }
771
+ else {
772
+ Transforms.setNode(board, newProperties, path);
773
+ }
967
774
  };
968
775
  }
969
- }
970
- else {
971
- const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.start > element.end);
972
- let maxNode;
973
- if (abstractNode.length) {
974
- const index = abstractNode
975
- .map(node => {
976
- const { start } = getCorrectStartEnd(node, parent);
977
- return start;
978
- })
979
- .sort((a, b) => a - b)[0];
980
- maxNode = parentChildren[index - 1];
981
- }
982
- else {
983
- const children = parentChildren.filter(child => !AbstractNode.isAbstract(child));
984
- maxNode = parentChildren[children.length - 1];
985
- }
986
- const maxNodeRectangle = getRectangleByElements(board, [maxNode], true);
987
- const startNodeRectangle = getRectangleByElements(board, [startNode], false);
988
- if (isHorizontal) {
989
- return {
990
- max: maxNodeRectangle.y + maxNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
991
- min: startNodeRectangle.y + startNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET
992
- };
776
+ return () => { };
777
+ });
778
+ const deletableHandles = deletableElements.map(node => {
779
+ const path = PlaitBoard.findPath(board, node);
780
+ return () => {
781
+ if (shouldChangeRightNodeCount(node)) {
782
+ changeRightNodeCount(board, path.slice(0, path.length - 1), -1);
783
+ }
784
+ Transforms.removeNode(board, path);
785
+ };
786
+ });
787
+ abstractHandles.forEach(action => action());
788
+ deletableHandles.forEach(action => action());
789
+ };
790
+ const divideElementByParent = (elements) => {
791
+ const abstractIncludedGroups = [];
792
+ const parentElements = [];
793
+ for (let i = 0; i < elements.length; i++) {
794
+ const parent = MindElement.getParent(elements[i]);
795
+ const parentIndex = parentElements.indexOf(parent);
796
+ if (parentIndex === -1) {
797
+ parentElements.push(parent);
798
+ abstractIncludedGroups.push([elements[i]]);
993
799
  }
994
800
  else {
995
- return {
996
- max: maxNodeRectangle.x + maxNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
997
- min: startNodeRectangle.x + startNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET
998
- };
801
+ abstractIncludedGroups[parentIndex].push(elements[i]);
999
802
  }
1000
803
  }
804
+ return { parentElements, abstractIncludedGroups };
1001
805
  };
1002
- const getHitAbstractHandle = (board, element, point) => {
1003
- const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
1004
- const isHorizontal = isHorizontalLayout(nodeLayout);
1005
- const parentElement = MindElement.getParent(element);
1006
- const includedElements = parentElement.children.slice(element.start, element.end + 1);
1007
- let abstractRectangle = getRectangleByElements(board, includedElements, true);
1008
- abstractRectangle = RectangleClient.getOutlineRectangle(abstractRectangle, -ABSTRACT_INCLUDED_OUTLINE_OFFSET);
1009
- const startHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.start);
1010
- const endHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.end);
1011
- const pointRec = RectangleClient.toRectangleClient([point, point]);
1012
- if (RectangleClient.isHit(pointRec, startHandleRec))
1013
- return AbstractHandlePosition.start;
1014
- if (RectangleClient.isHit(pointRec, endHandleRec))
1015
- return AbstractHandlePosition.end;
1016
- return null;
806
+
807
+ const getNodeShapeByElement = (element) => {
808
+ let nodeShape = element.shape;
809
+ if (nodeShape) {
810
+ return nodeShape;
811
+ }
812
+ let parent = findParentElement(element);
813
+ while (parent) {
814
+ if (parent.shape) {
815
+ return parent.shape;
816
+ }
817
+ parent = findParentElement(parent);
818
+ }
819
+ return MindNodeShape.roundRectangle;
1017
820
  };
1018
- const getAbstractHandleRectangle = (rectangle, isHorizontal, position) => {
1019
- let result;
1020
- if (position === AbstractHandlePosition.start) {
1021
- const location = isHorizontal ? rectangle.y : rectangle.x;
1022
- result = getRectangleByResizingLocation(rectangle, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.end, isHorizontal);
1023
- result = getRectangleByResizingLocation(result, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
821
+
822
+ const getBranchColorByMindElement = (board, element) => {
823
+ const ancestors = MindElement.getAncestors(board, element);
824
+ ancestors.unshift(element);
825
+ const ancestor = ancestors.find(value => value.branchColor);
826
+ if (ancestor && ancestor.branchColor) {
827
+ return ancestor.branchColor;
828
+ }
829
+ const root = ancestors[ancestors.length - 1];
830
+ const branch = ancestors[ancestors.length - 2];
831
+ if (branch) {
832
+ const index = root.children.indexOf(branch);
833
+ const length = COLORS.length;
834
+ const remainder = index % length;
835
+ return COLORS[remainder];
1024
836
  }
1025
837
  else {
1026
- const location = isHorizontal ? rectangle.y + rectangle.height : rectangle.x + rectangle.width;
1027
- result = getRectangleByResizingLocation(rectangle, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.start, isHorizontal);
1028
- result = getRectangleByResizingLocation(result, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
838
+ throw new Error('root element should not have branch color');
1029
839
  }
1030
- return result;
1031
840
  };
1032
- function findLocationLeftIndex(board, parentChildren, location, isHorizontal) {
1033
- const children = parentChildren.filter(child => {
1034
- return !AbstractNode.isAbstract(child);
1035
- });
1036
- const recArray = children.map(child => {
1037
- return getRectangleByElements(board, [child], false);
1038
- });
1039
- const firstRec = getRectangleByElements(board, [children[0]], true);
1040
- const fakeLeftRec = {
1041
- x: firstRec.x - firstRec.width,
1042
- y: firstRec.y - firstRec.height,
1043
- width: firstRec.width,
1044
- height: firstRec.height
1045
- };
1046
- const lastRec = getRectangleByElements(board, [children[children.length - 1]], true);
1047
- const fakeRightRec = {
1048
- x: lastRec.x + lastRec.width,
1049
- y: lastRec.y + lastRec.height,
1050
- width: lastRec.width,
1051
- height: lastRec.height
1052
- };
1053
- recArray.push(fakeRightRec);
1054
- recArray.unshift(fakeLeftRec);
1055
- for (let i = 0; i < recArray.length - 1; i++) {
1056
- const recXOrY = isHorizontal ? recArray[i].y : recArray[i].x;
1057
- const recWidthOrHeight = isHorizontal ? recArray[i].height : recArray[i].width;
1058
- if (location >= recXOrY + recWidthOrHeight / 2 &&
1059
- location <= recArray[i + 1][isHorizontal ? 'y' : 'x'] + recArray[i + 1][isHorizontal ? 'height' : 'width'] / 2) {
1060
- return i - 1;
1061
- }
1062
- }
1063
- return 0;
1064
- }
1065
- function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1066
- let touchedHandle;
1067
- const abstract = getSelectedElements(board)
1068
- .filter(element => AbstractNode.isAbstract(element))
1069
- .find(element => {
1070
- touchedHandle = getHitAbstractHandle(board, element, endPoint);
1071
- return touchedHandle;
1072
- });
1073
- if (touchedAbstract === abstract) {
1074
- return touchedAbstract;
1075
- }
1076
- if (touchedAbstract) {
1077
- const component = PlaitElement.getComponent(touchedAbstract);
1078
- component.updateAbstractIncludedOutline();
1079
- touchedAbstract = undefined;
841
+ const getNextBranchColor = (root) => {
842
+ const index = root.children.length;
843
+ const length = COLORS.length;
844
+ const remainder = index % length;
845
+ return COLORS[remainder];
846
+ };
847
+
848
+ const getStrokeByMindElement = (board, element) => {
849
+ const ancestors = MindElement.getAncestors(board, element);
850
+ ancestors.unshift(element);
851
+ const ancestor = ancestors.find(value => value.strokeColor);
852
+ if (ancestor && ancestor.strokeColor) {
853
+ return ancestor.strokeColor;
854
+ }
855
+ const root = ancestors[ancestors.length - 1];
856
+ const branch = ancestors[ancestors.length - 2];
857
+ if (branch) {
858
+ const index = root.children.indexOf(branch);
859
+ const length = COLORS.length;
860
+ const remainder = index % length;
861
+ return COLORS[remainder];
1080
862
  }
1081
- if (abstract) {
1082
- touchedAbstract = abstract;
1083
- const component = PlaitElement.getComponent(touchedAbstract);
1084
- component.updateAbstractIncludedOutline(touchedHandle);
863
+ else {
864
+ return ROOT_NODE_STROKE;
1085
865
  }
1086
- return touchedAbstract;
866
+ };
867
+
868
+ function isVirtualKey(e) {
869
+ const isMod = e.ctrlKey || e.metaKey;
870
+ const isAlt = isKeyHotkey('alt', e);
871
+ const isShift = isKeyHotkey('shift', e);
872
+ const isCapsLock = e.key.includes('CapsLock');
873
+ const isTab = e.key.includes('Tab');
874
+ const isEsc = e.key.includes('Escape');
875
+ const isF = e.key.startsWith('F');
876
+ const isArrow = e.key.includes('Arrow') ? true : false;
877
+ return isCapsLock || isMod || isAlt || isArrow || isShift || isTab || isEsc || isF;
1087
878
  }
1088
879
 
1089
- /**
1090
- * get correctly layout:
1091
- * 1. root is standard -> left or right
1092
- * 2. correct layout by incorrect layout direction
1093
- * @param element
1094
- */
1095
- const getCorrectLayoutByElement = (element) => {
880
+ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true, needDrawUnderline = true) {
1096
881
  var _a;
1097
- const { root } = findUpElement(element);
1098
- const rootLayout = root.layout || getDefaultLayout();
1099
- let correctRootLayout = rootLayout;
1100
- if (element.isRoot) {
1101
- return correctRootLayout;
1102
- }
1103
- const component = PlaitElement.getComponent(element);
1104
- let layout = element.layout;
1105
- let parentComponent = null;
1106
- let parent = component.parent.origin;
1107
- while (!layout && parent) {
1108
- parentComponent = PlaitElement.getComponent(parent);
1109
- layout = parentComponent.node.origin.layout;
1110
- parent = (_a = parentComponent.parent) === null || _a === void 0 ? void 0 : _a.origin;
882
+ let beginX, beginY, endX, endY, beginNode = node, endNode = child;
883
+ const layout = MindQueries.getCorrectLayoutByElement(node.origin);
884
+ if (isHorizontal) {
885
+ if (!isChildRight(node, child)) {
886
+ beginNode = child;
887
+ endNode = node;
888
+ }
889
+ beginX = beginNode.x + beginNode.width - beginNode.hGap;
890
+ beginY = beginNode.y + beginNode.height / 2;
891
+ endX = endNode.x + endNode.hGap;
892
+ endY = endNode.y + endNode.height / 2;
893
+ if (node.parent &&
894
+ isIndentedLayout(MindQueries.getLayoutByElement((_a = node.parent) === null || _a === void 0 ? void 0 : _a.origin)) &&
895
+ getNodeShapeByElement(node.origin) === MindNodeShape.underline) {
896
+ if (isChildRight(node, child)) {
897
+ beginY = node.y + node.height - node.vGap;
898
+ }
899
+ else {
900
+ endY = node.y + node.height - node.vGap;
901
+ }
902
+ }
1111
903
  }
1112
- if ((AbstractNode.isAbstract(element) || isChildOfAbstract(MindElement.getNode(element))) &&
1113
- isIndentedLayout(layout)) {
1114
- return getAbstractLayout(layout);
904
+ else {
905
+ if (node.y > child.y) {
906
+ beginNode = child;
907
+ endNode = node;
908
+ }
909
+ beginX = beginNode.x + beginNode.width / 2;
910
+ beginY = beginNode.y + beginNode.height - beginNode.vGap;
911
+ endX = endNode.x + endNode.width / 2;
912
+ endY = endNode.y + endNode.vGap;
1115
913
  }
1116
- // handle root standard
1117
- if (rootLayout === MindLayoutType.standard) {
1118
- correctRootLayout = (component === null || component === void 0 ? void 0 : component.node.left) ? MindLayoutType.left : MindLayoutType.right;
914
+ const stroke = defaultStroke || getBranchColorByMindElement(board, child.origin);
915
+ const strokeWidth = child.origin.branchWidth ? child.origin.branchWidth : STROKE_WIDTH;
916
+ if (endNode.origin.isRoot) {
917
+ if (layout === MindLayoutType.left || isStandardLayout(layout)) {
918
+ endX -= strokeWidth;
919
+ }
920
+ if (layout === MindLayoutType.upward) {
921
+ endY -= strokeWidth;
922
+ }
1119
923
  }
1120
- if (parentComponent && parentComponent.node.origin.isRoot) {
1121
- return correctRootLayout;
924
+ if (beginNode.origin.isRoot) {
925
+ if (layout === MindLayoutType.right || isStandardLayout(layout)) {
926
+ beginX += strokeWidth;
927
+ }
928
+ if (layout === MindLayoutType.downward) {
929
+ beginY += strokeWidth;
930
+ }
1122
931
  }
1123
- if (layout) {
1124
- const incorrectDirection = getInCorrectLayoutDirection(correctRootLayout, layout);
1125
- if (incorrectDirection) {
1126
- return correctLayoutByDirection(layout, incorrectDirection);
932
+ if (isHorizontal) {
933
+ let curve = [
934
+ [beginX, beginY],
935
+ [beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
936
+ [endX - (beginNode.hGap + endNode.hGap) / 2, endY],
937
+ [endX, endY]
938
+ ];
939
+ const shape = getNodeShapeByElement(child.origin);
940
+ if (!node.origin.isRoot) {
941
+ if (node.x > child.x) {
942
+ curve = [
943
+ [beginX, beginY],
944
+ [beginX + (beginNode.hGap + endNode.hGap) / 3, beginY],
945
+ [endX - (beginNode.hGap + endNode.hGap) / 2, endY],
946
+ [endX - 12, endY]
947
+ ];
948
+ const line = [
949
+ [endX - 12, endY],
950
+ [endX - 12, endY],
951
+ [endX, endY]
952
+ ];
953
+ curve = [...curve, ...line];
954
+ }
955
+ else {
956
+ curve = [
957
+ [beginX + 12, beginY],
958
+ [beginX + (beginNode.hGap + endNode.hGap) / 2, beginY],
959
+ [endX - (beginNode.hGap + endNode.hGap) / 3, endY],
960
+ [endX, endY]
961
+ ];
962
+ const line = [
963
+ [beginX, beginY],
964
+ [beginX + 12, beginY],
965
+ [beginX + 12, beginY]
966
+ ];
967
+ curve = [...line, ...curve];
968
+ }
1127
969
  }
1128
- else {
1129
- return layout;
970
+ if (needDrawUnderline && shape === MindNodeShape.underline) {
971
+ if (child.left) {
972
+ const underline = [
973
+ [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
974
+ [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
975
+ [beginX - (beginNode.width - beginNode.hGap * 2), beginY]
976
+ ];
977
+ curve = [...underline, ...curve];
978
+ }
979
+ else {
980
+ const underline = [
981
+ [endX + (endNode.width - endNode.hGap * 2), endY],
982
+ [endX + (endNode.width - endNode.hGap * 2), endY],
983
+ [endX + (endNode.width - endNode.hGap * 2), endY]
984
+ ];
985
+ curve = [...curve, ...underline];
986
+ }
1130
987
  }
988
+ const points = pointsOnBezierCurves(curve);
989
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke, strokeWidth });
1131
990
  }
1132
991
  else {
1133
- return correctRootLayout;
1134
- }
1135
- };
1136
-
1137
- const getBranchLayouts = (element) => {
1138
- const layouts = [];
1139
- if (element.layout) {
1140
- //getCorrectLayoutByElement含有递归操作,getBranchMindmapLayouts本身也有递归操作,有待优化
1141
- layouts.unshift(getCorrectLayoutByElement(element));
1142
- }
1143
- let parent = findParentElement(element);
1144
- while (parent) {
1145
- if (parent.layout) {
1146
- layouts.unshift(parent.layout);
992
+ let curve = [
993
+ [beginX, beginY],
994
+ [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
995
+ [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
996
+ [endX, endY]
997
+ ];
998
+ if (!node.origin.isRoot) {
999
+ if (isTopLayout(layout)) {
1000
+ curve = [
1001
+ [beginX, beginY],
1002
+ [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
1003
+ [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
1004
+ [endX, endY - 12]
1005
+ ];
1006
+ const line = [
1007
+ [endX, endY - 12],
1008
+ [endX, endY - 12],
1009
+ [endX, endY]
1010
+ ];
1011
+ curve = [...curve, ...line];
1012
+ }
1013
+ else {
1014
+ curve = [
1015
+ [beginX, beginY + 12],
1016
+ [beginX, beginY + (beginNode.vGap + endNode.vGap) / 2],
1017
+ [endX, endY - (beginNode.vGap + endNode.vGap) / 2],
1018
+ [endX, endY]
1019
+ ];
1020
+ const line = [
1021
+ [beginX, beginY],
1022
+ [beginX, beginY + 12],
1023
+ [beginX, beginY + 12]
1024
+ ];
1025
+ curve = [...line, ...curve];
1026
+ }
1147
1027
  }
1148
- parent = findParentElement(parent);
1028
+ const points = pointsOnBezierCurves(curve);
1029
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke, strokeWidth });
1149
1030
  }
1150
- return layouts;
1151
- };
1031
+ }
1152
1032
 
1153
- /**
1154
- * get available sub layouts by element
1155
- * @param element
1156
- * @returns MindLayoutType[]
1157
- */
1158
- const getAvailableSubLayoutsByElement = (element) => {
1159
- const parentElement = findParentElement(element);
1160
- if (parentElement) {
1161
- const branchLayouts = getBranchLayouts(parentElement);
1162
- if (branchLayouts[0] === MindLayoutType.standard) {
1163
- const node = MindElement.getNode(element);
1164
- branchLayouts[0] = node.left ? MindLayoutType.left : MindLayoutType.right;
1165
- }
1166
- const currentLayoutDirections = getBranchDirectionsByLayouts(branchLayouts);
1167
- let availableSubLayouts = getAvailableSubLayoutsByLayoutDirections(currentLayoutDirections);
1168
- const parentLayout = [branchLayouts[branchLayouts.length - 1]];
1169
- const parentDirections = getBranchDirectionsByLayouts(parentLayout);
1170
- const parentAvailableSubLayouts = getAvailableSubLayoutsByLayoutDirections(parentDirections);
1171
- availableSubLayouts = availableSubLayouts.filter(layout => parentAvailableSubLayouts.some(parentAvailableSubLayout => parentAvailableSubLayout === layout));
1172
- return availableSubLayouts;
1033
+ const drawPlaceholderDropNodeG = (board, dropTarget, fakeDropNodeG) => {
1034
+ const targetComponent = PlaitElement.getComponent(dropTarget.target);
1035
+ const targetRect = getRectangleByNode(targetComponent.node);
1036
+ if (dropTarget.detectResult && ['right', 'left'].includes(dropTarget.detectResult)) {
1037
+ drawStraightDropNodeG(board, targetRect, dropTarget.detectResult, targetComponent, fakeDropNodeG);
1173
1038
  }
1174
- return undefined;
1175
- };
1176
-
1177
- /**
1178
- * 获取父节点布局类型
1179
- * @param element
1180
- * @returns MindLayoutType
1181
- */
1182
- const getLayoutParentByElement = (element) => {
1183
- let parent = findParentElement(element);
1184
- while (parent) {
1185
- if (parent.layout) {
1186
- return parent.layout;
1187
- }
1188
- parent = findParentElement(parent);
1039
+ if (targetComponent.parent && dropTarget.detectResult && ['top', 'bottom'].includes(dropTarget.detectResult)) {
1040
+ const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1041
+ const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
1042
+ drawCurvePlaceholderDropNodeG(board, targetRect, dropTarget.detectResult, targetIndex, targetComponent, parentComponent, fakeDropNodeG);
1189
1043
  }
1190
- return getDefaultLayout();
1191
1044
  };
1192
-
1193
- const getLayoutByElement = (element) => {
1194
- const layout = element.layout;
1195
- if (layout) {
1196
- return layout;
1045
+ const drawCurvePlaceholderDropNodeG = (board, targetRect, detectResult, targetIndex, targetComponent, parentComponent, fakeDropNodeG) => {
1046
+ const parentNodeLayout = MindQueries.getCorrectLayoutByElement(parentComponent.node.origin);
1047
+ const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.parent.origin);
1048
+ const strokeWidth = targetComponent.node.origin.branchWidth ? targetComponent.node.origin.branchWidth : STROKE_WIDTH;
1049
+ let fakeX = targetComponent.node.x, fakeY = targetRect.y - 30, fakeRectangleStartX = targetRect.x, fakeRectangleEndX = targetRect.x + 30, fakeRectangleStartY = fakeY, fakeRectangleEndY = fakeRectangleStartY + 12, width = 30;
1050
+ if (isLeftLayout(layout)) {
1051
+ fakeX = targetComponent.node.x + targetComponent.node.width - 30;
1052
+ fakeRectangleStartX = targetRect.x + targetRect.width - 30;
1053
+ fakeRectangleEndX = targetRect.x + targetRect.width;
1197
1054
  }
1198
- if (AbstractNode.isAbstract(element) ||
1199
- (isChildOfAbstract(MindElement.getNode(element)) && isIndentedLayout(layout))) {
1200
- const parentLayout = getLayoutParentByElement(element);
1201
- return getAbstractLayout(parentLayout);
1055
+ if (isHorizontalLogicLayout(parentNodeLayout)) {
1056
+ fakeY = getHorizontalFakeY(detectResult, targetIndex, parentComponent.node, targetRect, layout, fakeY);
1057
+ if (isStandardLayout(parentNodeLayout)) {
1058
+ const rightNodeCount = parentComponent.node.origin.rightNodeCount || 0;
1059
+ const idx = parentComponent.node.children.findIndex(x => x === targetComponent.node);
1060
+ const isLeft = idx >= rightNodeCount;
1061
+ // 标准布局的左,需要调整 x
1062
+ if (isLeft) {
1063
+ fakeX = targetComponent.node.x + targetComponent.node.width - 30;
1064
+ fakeRectangleStartX = targetRect.x + targetRect.width - 30;
1065
+ fakeRectangleEndX = targetRect.x + targetRect.width;
1066
+ }
1067
+ const isLeftFirst = idx === rightNodeCount;
1068
+ const isRightLast = idx === rightNodeCount - 1;
1069
+ // 拖拽至左第一个节点的情况
1070
+ if (detectResult === 'top' && isLeftFirst) {
1071
+ fakeY = targetRect.y - targetRect.height;
1072
+ }
1073
+ if (detectResult === 'bottom' && isRightLast) {
1074
+ fakeY = targetRect.y + targetRect.height + 30;
1075
+ }
1076
+ }
1077
+ fakeRectangleStartY = fakeY;
1078
+ fakeRectangleEndY = fakeRectangleStartY + 12;
1202
1079
  }
1203
- return getLayoutParentByElement(element);
1204
- };
1205
-
1206
- const MindQueries = {
1207
- getAvailableSubLayoutsByElement,
1208
- getLayoutParentByElement,
1209
- getBranchLayouts,
1210
- getLayoutByElement,
1211
- getCorrectLayoutByElement
1212
- };
1213
-
1214
- const PlaitMind = {
1215
- isMind: (value) => {
1216
- return value.type === 'mindmap';
1080
+ if (isVerticalLogicLayout(layout)) {
1081
+ parentComponent = targetComponent;
1082
+ targetComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1083
+ fakeX = parentComponent.node.x;
1084
+ width = parentComponent.node.width;
1085
+ const vGap = BASE * 6 + strokeWidth;
1086
+ if (isTopLayout(layout) && detectResult === 'top') {
1087
+ fakeY = targetRect.y - vGap;
1088
+ fakeRectangleStartY = fakeY - vGap + strokeWidth;
1089
+ }
1090
+ if (isBottomLayout(layout) && detectResult === 'bottom') {
1091
+ fakeY = targetRect.y + targetRect.height + vGap;
1092
+ fakeRectangleStartY = fakeY + vGap - strokeWidth;
1093
+ }
1094
+ fakeRectangleStartX = fakeX + Math.ceil(parentComponent.node.width / 2) - parentComponent.node.hGap - Math.ceil(strokeWidth / 2);
1095
+ fakeRectangleEndX = fakeRectangleStartX + 30;
1096
+ fakeRectangleEndY = fakeRectangleStartY + 12;
1217
1097
  }
1218
- };
1219
- const MindElement = {
1220
- hasLayout(value, layout) {
1221
- const _layout = MindQueries.getLayoutByElement(value);
1222
- return _layout === layout;
1223
- },
1224
- isIndentedLayout(value) {
1225
- const _layout = MindQueries.getLayoutByElement(value);
1226
- return isIndentedLayout(_layout);
1227
- },
1228
- isMindElement(board, element) {
1229
- const path = PlaitBoard.findPath(board, element);
1230
- const rootElement = PlaitNode.get(board, path.slice(0, 1));
1231
- if (PlaitMind.isMind(rootElement)) {
1232
- return true;
1098
+ if (isIndentedLayout(layout)) {
1099
+ // 偏移一个 Gap
1100
+ if (isLeftLayout(layout)) {
1101
+ fakeX -= BASE * 4;
1233
1102
  }
1234
- else {
1235
- return false;
1103
+ if (isRightLayout(layout)) {
1104
+ fakeX += BASE * 4;
1236
1105
  }
1237
- },
1238
- getParent(node) {
1239
- if (PlaitMind.isMind(node)) {
1240
- throw new Error('mind root node can not get parent');
1106
+ if (isTopLayout(layout)) {
1107
+ if (detectResult === 'top') {
1108
+ const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
1109
+ if (isLastNode) {
1110
+ fakeY = targetRect.y - targetRect.height - BASE;
1111
+ }
1112
+ else {
1113
+ const nextComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
1114
+ const nextRect = getRectangleByNode(nextComponent.node);
1115
+ fakeY = targetRect.y - Math.abs((nextRect.y + nextRect.height - targetRect.y) / 2);
1116
+ }
1117
+ }
1118
+ if (detectResult === 'bottom') {
1119
+ const isFirstNode = targetIndex === 0;
1120
+ if (isFirstNode) {
1121
+ const parentRect = getRectangleByNode(parentComponent.node);
1122
+ fakeY = parentRect.y - parentRect.height / 2 - BASE;
1123
+ }
1124
+ else {
1125
+ const previousComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
1126
+ const previousRect = getRectangleByNode(previousComponent.node);
1127
+ fakeY = previousRect.y - Math.abs((targetRect.y + targetRect.height - previousRect.y) / 2);
1128
+ }
1129
+ }
1241
1130
  }
1242
- const parent = NODE_TO_PARENT.get(node);
1243
- return parent;
1244
- },
1245
- getRoot(board, element) {
1246
- const path = PlaitBoard.findPath(board, element);
1247
- return PlaitNode.get(board, path.slice(0, 1));
1248
- },
1249
- getNode(element) {
1250
- const node = ELEMENT_TO_NODE.get(element);
1251
- if (!node) {
1252
- throw new Error(`can not get node from ${JSON.stringify(element)}`);
1131
+ fakeRectangleStartX = fakeX;
1132
+ fakeRectangleEndX = fakeRectangleStartX + 30;
1133
+ fakeRectangleStartY = fakeY;
1134
+ fakeRectangleEndY = fakeRectangleStartY + 12;
1135
+ }
1136
+ // 构造一条曲线
1137
+ const fakeNode = Object.assign(Object.assign({}, targetComponent.node), { x: fakeX, y: fakeY, width, height: 12 });
1138
+ const linkSVGG = isIndentedLayout(layout)
1139
+ ? drawIndentedLink(board, parentComponent.node, fakeNode, PRIMARY_COLOR, false)
1140
+ : drawLink(board, parentComponent.node, fakeNode, PRIMARY_COLOR, isHorizontalLayout(layout), false);
1141
+ // 构造一个矩形框坐标
1142
+ const fakeRectangleG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), fakeRectangleStartX, fakeRectangleStartY, fakeRectangleEndX, fakeRectangleEndY, {
1143
+ stroke: PRIMARY_COLOR,
1144
+ strokeWidth: 2,
1145
+ fill: PRIMARY_COLOR,
1146
+ fillStyle: 'solid'
1147
+ });
1148
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
1149
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
1150
+ };
1151
+ const drawStraightDropNodeG = (board, targetRect, detectResult, targetComponent, fakeDropNodeG) => {
1152
+ const { x, y, width, height } = targetRect;
1153
+ const lineLength = 40;
1154
+ let startLinePoint = x + width;
1155
+ let endLinePoint = x + width + lineLength;
1156
+ let startRectanglePointX = x + width + lineLength;
1157
+ let endRectanglePointX = x + lineLength + width + 30;
1158
+ let startRectanglePointY = y + height / 2 - 6;
1159
+ let endRectanglePointY = y + height / 2 - 6 + 12;
1160
+ if (detectResult === 'left') {
1161
+ startLinePoint = x - lineLength;
1162
+ endLinePoint = x;
1163
+ startRectanglePointX = x - lineLength - 30;
1164
+ endRectanglePointX = x - lineLength;
1165
+ }
1166
+ let fakeY = targetComponent.node.y;
1167
+ let fakeX = targetRect.x;
1168
+ const strokeWidth = targetComponent.node.origin.branchWidth ? targetComponent.node.origin.branchWidth : STROKE_WIDTH;
1169
+ const pointOptions = {
1170
+ fakeX,
1171
+ fakeY,
1172
+ x,
1173
+ y,
1174
+ width,
1175
+ height,
1176
+ strokeWidth
1177
+ };
1178
+ const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1179
+ const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1180
+ if (!isMixedLayout(parentLayout, layout)) {
1181
+ // 构造一条直线
1182
+ let linePoints = [
1183
+ [startLinePoint, y + height / 2],
1184
+ [endLinePoint, y + height / 2]
1185
+ ];
1186
+ if (isIndentedLayout(parentLayout)) {
1187
+ const fakePoint = getIndentedFakePoint(parentLayout, pointOptions);
1188
+ drawIndentNodeG(board, fakeDropNodeG, fakePoint, targetComponent.node);
1189
+ return;
1253
1190
  }
1254
- return node;
1255
- },
1256
- hasEmojis(element) {
1257
- if (element.data.emojis) {
1258
- return true;
1191
+ else if (isVerticalLogicLayout(parentLayout)) {
1192
+ if (!targetComponent.node.origin.isRoot) {
1193
+ /**
1194
+ * 计算逻辑:
1195
+ * 1. 移动到左侧:当前节点 startX - 偏移值,偏移值计算如下:
1196
+ * a. 第一个节点: 固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
1197
+ * b. 第二个节点到最后一个节点之间:上一个节点到当前节点间距的一半((当前节点 startX - 上一个节点的 endX) / 2),endX = 当前节点的 startX + width;
1198
+ * 2. 移动到右侧:当前节点 x + width + 偏移值,偏移值计算如下:
1199
+ * a. 第二个节点到最后一个节点之间的右侧:当前节点到下一个节点间距的一半((下一个节点 startX - 当前节点的 endX) / 2),endX = 当前节点的 startX + width;
1200
+ * b. 最后一个节点的右侧:固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
1201
+ */
1202
+ fakeY = targetComponent.node.y;
1203
+ const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1204
+ const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
1205
+ if (detectResult === 'left') {
1206
+ let offsetX = 0;
1207
+ const isFirstNode = targetIndex === 0;
1208
+ if (isFirstNode) {
1209
+ offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
1210
+ }
1211
+ else {
1212
+ const previousComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex - 1]);
1213
+ const previousRect = getRectangleByNode(previousComponent.node);
1214
+ const space = targetRect.x - (previousRect.x + previousRect.width);
1215
+ offsetX = space / 2;
1216
+ }
1217
+ fakeX = targetRect.x - offsetX - width / 2 - Math.ceil(strokeWidth / 2);
1218
+ }
1219
+ if (detectResult === 'right') {
1220
+ let offsetX = 0;
1221
+ const isLastNode = targetIndex === parentComponent.node.origin.children.length - 1;
1222
+ if (isLastNode) {
1223
+ offsetX = parentComponent.node.origin.isRoot ? BASE * 8 : BASE * 3 + strokeWidth / 2;
1224
+ }
1225
+ else {
1226
+ const nextComponent = PlaitElement.getComponent(parentComponent.node.origin.children[targetIndex + 1]);
1227
+ const nextRect = getRectangleByNode(nextComponent.node);
1228
+ const space = nextRect.x - (targetRect.x + targetRect.width);
1229
+ offsetX = space / 2;
1230
+ }
1231
+ fakeX = targetRect.x + width + offsetX - width / 2 - Math.ceil(strokeWidth / 2);
1232
+ }
1233
+ startRectanglePointX = fakeX;
1234
+ if (isTopLayout(parentLayout)) {
1235
+ // 因为矩形是从左上角为起点向下画的,所以需要向上偏移一个矩形的高度(-12)
1236
+ startRectanglePointY = fakeY + height + targetComponent.node.vGap - 12;
1237
+ }
1238
+ if (isBottomLayout(parentLayout)) {
1239
+ startRectanglePointY = fakeY + targetComponent.node.vGap;
1240
+ }
1241
+ endRectanglePointX = startRectanglePointX + 30;
1242
+ endRectanglePointY = startRectanglePointY + 12;
1243
+ const fakeNode = Object.assign(Object.assign({}, targetComponent.node), { x: fakeX, y: fakeY, width: 30 });
1244
+ const linkSVGG = drawLink(board, parentComponent.node, fakeNode, PRIMARY_COLOR, false, false);
1245
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
1246
+ }
1259
1247
  }
1260
1248
  else {
1261
- return false;
1249
+ let linkSVGG = PlaitBoard.getRoughSVG(board).linearPath(linePoints, { stroke: PRIMARY_COLOR, strokeWidth });
1250
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
1262
1251
  }
1263
- },
1264
- getEmojis(element) {
1265
- return element.data.emojis;
1252
+ // 构造一个矩形框坐标
1253
+ let fakeRectangleG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
1254
+ stroke: PRIMARY_COLOR,
1255
+ strokeWidth: 2,
1256
+ fill: PRIMARY_COLOR,
1257
+ fillStyle: 'solid'
1258
+ });
1259
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
1266
1260
  }
1267
- };
1268
-
1269
- const MindNode = {
1270
- get(root, path) {
1271
- let node = root;
1272
- for (let i = 0; i < path.length; i++) {
1273
- const p = path[i];
1274
- if (!node || !node.children || !node.children[p]) {
1275
- throw new Error(`Cannot find a descendant at path [${path}]`);
1261
+ else {
1262
+ // 混合布局画线逻辑
1263
+ if (isHorizontalLogicLayout(parentLayout)) {
1264
+ if (isIndentedLayout(layout)) {
1265
+ const fakePoint = getIndentedFakePoint(layout, pointOptions);
1266
+ drawIndentNodeG(board, fakeDropNodeG, fakePoint, targetComponent.node);
1267
+ return;
1276
1268
  }
1277
- node = node.children[p];
1278
- }
1279
- return node;
1280
- },
1281
- isEquals(node, otherNode) {
1282
- const hasSameSize = node.x === otherNode.x && node.y === otherNode.y && node.width === otherNode.width && node.height === otherNode.height;
1283
- const hasSameOrigin = node.origin === otherNode.origin;
1284
- let hasSameParentOriginChildren = false;
1285
- if (node.parent && otherNode.parent) {
1286
- hasSameParentOriginChildren = node.parent.origin.children == otherNode.parent.origin.children;
1287
1269
  }
1288
- return hasSameSize && hasSameOrigin && hasSameParentOriginChildren;
1289
- }
1290
- };
1291
-
1292
- var LayoutDirection;
1293
- (function (LayoutDirection) {
1294
- LayoutDirection["top"] = "top";
1295
- LayoutDirection["right"] = "right";
1296
- LayoutDirection["bottom"] = "bottom";
1297
- LayoutDirection["left"] = "left";
1298
- })(LayoutDirection || (LayoutDirection = {}));
1299
- const LayoutDirectionsMap = {
1300
- [MindLayoutType.right]: [LayoutDirection.right],
1301
- [MindLayoutType.left]: [LayoutDirection.left],
1302
- [MindLayoutType.upward]: [LayoutDirection.top],
1303
- [MindLayoutType.downward]: [LayoutDirection.bottom],
1304
- [MindLayoutType.rightBottomIndented]: [LayoutDirection.right, LayoutDirection.bottom],
1305
- [MindLayoutType.rightTopIndented]: [LayoutDirection.right, LayoutDirection.top],
1306
- [MindLayoutType.leftBottomIndented]: [LayoutDirection.left, LayoutDirection.bottom],
1307
- [MindLayoutType.leftTopIndented]: [LayoutDirection.left, LayoutDirection.top]
1308
- };
1309
-
1310
- var AbstractHandlePosition;
1311
- (function (AbstractHandlePosition) {
1312
- AbstractHandlePosition["start"] = "start";
1313
- AbstractHandlePosition["end"] = "end";
1314
- })(AbstractHandlePosition || (AbstractHandlePosition = {}));
1315
- var AbstractResizeState;
1316
- (function (AbstractResizeState) {
1317
- AbstractResizeState["start"] = "start";
1318
- AbstractResizeState["resizing"] = "resizing";
1319
- AbstractResizeState["end"] = "end";
1320
- })(AbstractResizeState || (AbstractResizeState = {}));
1321
-
1322
- function enterNodeEditing(element) {
1323
- const component = ELEMENT_TO_COMPONENT.get(element);
1324
- component.startEditText(false, false);
1325
- }
1326
-
1327
- function findParentElement(element) {
1328
- const component = PlaitElement.getComponent(element);
1329
- if (component && component.parent) {
1330
- return component.parent.origin;
1331
- }
1332
- return undefined;
1333
- }
1334
- function findUpElement(element) {
1335
- let branch;
1336
- let root = element;
1337
- let parent = findParentElement(element);
1338
- while (parent) {
1339
- branch = root;
1340
- root = parent;
1341
- parent = findParentElement(parent);
1342
1270
  }
1343
- return { root, branch };
1344
- }
1345
- const getChildrenCount = (element) => {
1346
- const count = element.children.reduce((p, c) => {
1347
- return p + getChildrenCount(c);
1348
- }, 0);
1349
- return count + element.children.length;
1350
1271
  };
1351
- const isChildElement = (origin, child) => {
1352
- let parent = findParentElement(child);
1353
- while (parent) {
1354
- if (parent === origin) {
1355
- return true;
1272
+ const getHorizontalFakeY = (detectResult, targetIndex, parentNode, targetRect, layout, fakeY) => {
1273
+ if (detectResult === 'top') {
1274
+ if (targetIndex === 0 && isTopLayout(layout)) {
1275
+ fakeY = targetRect.y + targetRect.height;
1356
1276
  }
1357
- parent = findParentElement(parent);
1358
- }
1359
- return false;
1360
- };
1361
- const filterChildElement = (elements) => {
1362
- let result = [];
1363
- elements.forEach(element => {
1364
- const isChild = elements.some(node => {
1365
- return isChildElement(node, element);
1366
- });
1367
- if (!isChild) {
1368
- result.push(element);
1277
+ if (targetIndex > 0) {
1278
+ const previousComponent = PlaitElement.getComponent(parentNode.origin.children[targetIndex - 1]);
1279
+ const previousRect = getRectangleByNode(previousComponent.node);
1280
+ const topY = previousRect.y + previousRect.height;
1281
+ fakeY = topY + (targetRect.y - topY) / 5;
1369
1282
  }
1370
- });
1371
- return result;
1372
- };
1373
- const isChildRight = (node, child) => {
1374
- return node.x < child.x;
1375
- };
1376
- const isChildUp = (node, child) => {
1377
- return node.y > child.y;
1378
- };
1379
- const copyNewNode = (node) => {
1380
- const newNode = Object.assign({}, node);
1381
- newNode.id = idCreator();
1382
- newNode.children = [];
1383
- for (const childNode of node.children) {
1384
- newNode.children.push(copyNewNode(childNode));
1385
- }
1386
- return newNode;
1387
- };
1388
- const transformRootToNode = (board, node) => {
1389
- const newNode = Object.assign({}, node);
1390
- delete newNode.isRoot;
1391
- delete newNode.rightNodeCount;
1392
- delete newNode.type;
1393
- const text = Node.string(node.data.topic.children[0]) || ' ';
1394
- const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT);
1395
- newNode.width = Math.max(width, NODE_MIN_WIDTH);
1396
- newNode.height = height;
1397
- if (newNode.layout === MindLayoutType.standard) {
1398
- delete newNode.layout;
1399
- }
1400
- return newNode;
1401
- };
1402
- const transformNodeToRoot = (board, node) => {
1403
- var _a;
1404
- const newElement = Object.assign({}, node);
1405
- let text = Node.string(newElement.data.topic);
1406
- if (!text) {
1407
- text = '思维导图';
1408
- newElement.data.topic = { children: [{ text }] };
1409
1283
  }
1410
- newElement === null || newElement === void 0 ? true : delete newElement.strokeColor;
1411
- newElement === null || newElement === void 0 ? true : delete newElement.fill;
1412
- newElement === null || newElement === void 0 ? true : delete newElement.shape;
1413
- newElement === null || newElement === void 0 ? true : delete newElement.strokeWidth;
1414
- const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT, ROOT_TOPIC_FONT_SIZE);
1415
- newElement.width = Math.max(width, NODE_MIN_WIDTH);
1416
- newElement.height = height;
1417
- return Object.assign(Object.assign({}, newElement), { layout: (_a = newElement.layout) !== null && _a !== void 0 ? _a : MindLayoutType.right, isCollapsed: false, isRoot: true, type: 'mindmap' });
1418
- };
1419
- const extractNodesText = (node) => {
1420
- let str = '';
1421
- if (node) {
1422
- str += Node.string(node.data.topic.children[0]) + ' ';
1423
- for (const childNode of node.children) {
1424
- str += extractNodesText(childNode);
1284
+ if (detectResult === 'bottom') {
1285
+ fakeY = targetRect.y + targetRect.height + 30;
1286
+ if (targetIndex < parentNode.origin.children.length - 1) {
1287
+ const nextComponent = PlaitElement.getComponent(parentNode.origin.children[targetIndex + 1]);
1288
+ const nextRect = getRectangleByNode(nextComponent.node);
1289
+ const topY = targetRect.y + targetRect.height;
1290
+ fakeY = topY + (nextRect.y - topY) / 5;
1425
1291
  }
1426
- }
1427
- return str;
1428
- };
1429
- const changeRightNodeCount = (board, parentPath, changeNumber) => {
1430
- const _rightNodeCount = board.children[parentPath[0]].rightNodeCount;
1431
- Transforms.setNode(board, {
1432
- rightNodeCount: changeNumber >= 0
1433
- ? _rightNodeCount + changeNumber
1434
- : _rightNodeCount + changeNumber < 0
1435
- ? 0
1436
- : _rightNodeCount + changeNumber
1437
- }, parentPath);
1438
- };
1439
- const shouldChangeRightNodeCount = (selectedElement) => {
1440
- const parentElement = findParentElement(selectedElement);
1441
- if (parentElement) {
1442
- const nodeIndex = parentElement.children.findIndex(item => item.id === selectedElement.id);
1443
- if (parentElement.isRoot &&
1444
- getRootLayout(parentElement) === MindLayoutType.standard &&
1445
- parentElement.rightNodeCount &&
1446
- nodeIndex <= parentElement.rightNodeCount - 1) {
1447
- return true;
1292
+ if (targetIndex === parentNode.origin.children.length - 1) {
1293
+ fakeY = targetRect.y + targetRect.height + 30;
1448
1294
  }
1449
1295
  }
1450
- return false;
1451
- };
1452
- const createDefaultMindMapElement = (point, rightNodeCount, layout) => {
1453
- const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle, layout });
1454
- root.rightNodeCount = rightNodeCount;
1455
- root.isRoot = true;
1456
- root.type = 'mindmap';
1457
- root.points = [point];
1458
- const children = [1, 1, 1].map(() => {
1459
- return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle });
1460
- });
1461
- root.children = children;
1462
- return root;
1296
+ return fakeY;
1463
1297
  };
1464
- const createMindElement = (text, width, height, options) => {
1465
- const newElement = {
1466
- id: idCreator(),
1467
- data: {
1468
- topic: { children: [{ text }] }
1469
- },
1470
- children: [],
1471
- width,
1472
- height,
1473
- fill: options.fill,
1474
- strokeColor: options.strokeColor,
1475
- strokeWidth: options.strokeWidth,
1476
- shape: options.shape
1477
- };
1478
- if (options.fill) {
1479
- newElement.fill = options.fill;
1298
+ const getIndentedFakePoint = (layout, pointOptions) => {
1299
+ let { fakeX, fakeY, x, y, width, height, strokeWidth } = pointOptions;
1300
+ const hGap = BASE * 4;
1301
+ const vGap = BASE * 6;
1302
+ const offsetX = hGap + width / 2 + strokeWidth;
1303
+ const offsetY = vGap + height / 2 + strokeWidth;
1304
+ if (isLeftLayout(layout)) {
1305
+ fakeX = x - offsetX;
1480
1306
  }
1481
- if (options.strokeColor) {
1482
- newElement.strokeColor = options.strokeColor;
1307
+ if (isRightLayout(layout)) {
1308
+ fakeX = x + offsetX;
1483
1309
  }
1484
- if (!isNullOrUndefined(options.strokeWidth)) {
1485
- newElement.strokeWidth = options.strokeWidth;
1310
+ if (isTopLayout(layout)) {
1311
+ fakeY = y - offsetY;
1486
1312
  }
1487
- if (options.shape) {
1488
- newElement.shape = options.shape;
1313
+ if (isBottomLayout(layout)) {
1314
+ fakeY = y + height + offsetY;
1489
1315
  }
1490
- if (options.layout) {
1491
- newElement.layout = options.layout;
1316
+ return { fakeX, fakeY };
1317
+ };
1318
+ const drawIndentNodeG = (board, fakeDropNodeG, fakePoint, node) => {
1319
+ const { fakeX, fakeY } = fakePoint;
1320
+ const fakeNode = Object.assign(Object.assign({}, node), { x: fakeX, y: fakeY, width: 30, height: 12 });
1321
+ const linkSVGG = drawIndentedLink(board, node, fakeNode, PRIMARY_COLOR, false);
1322
+ const startRectanglePointX = fakeX, startRectanglePointY = fakeY, endRectanglePointX = fakeX + 30, endRectanglePointY = fakeY + 12;
1323
+ const fakeRectangleG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), startRectanglePointX, startRectanglePointY, endRectanglePointX, endRectanglePointY, {
1324
+ stroke: PRIMARY_COLOR,
1325
+ strokeWidth: 2,
1326
+ fill: PRIMARY_COLOR,
1327
+ fillStyle: 'solid'
1328
+ });
1329
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(linkSVGG);
1330
+ fakeDropNodeG === null || fakeDropNodeG === void 0 ? void 0 : fakeDropNodeG.appendChild(fakeRectangleG);
1331
+ };
1332
+
1333
+ /**
1334
+ *
1335
+ * @param targetNode
1336
+ * @param centerPoint
1337
+ * @returns DetectResult[] | null
1338
+ */
1339
+ const directionDetector = (targetNode, centerPoint) => {
1340
+ const { x, y, width, height } = getRectangleByNode(targetNode);
1341
+ const yCenter = y + height / 2;
1342
+ const xCenter = x + width / 2;
1343
+ const top = targetNode.y;
1344
+ const bottom = targetNode.y + targetNode.height;
1345
+ const left = targetNode.x;
1346
+ const right = targetNode.x + targetNode.width;
1347
+ const direction = [];
1348
+ // x 轴
1349
+ if (centerPoint[1] > y && centerPoint[1] < y + height) {
1350
+ if (centerPoint[0] > left && centerPoint[0] < xCenter) {
1351
+ direction.push('left');
1352
+ }
1353
+ if (centerPoint[0] > xCenter && centerPoint[0] < right) {
1354
+ direction.push('right');
1355
+ }
1356
+ // 重合区域,返回两个方向
1357
+ if ((centerPoint[0] > x && centerPoint[0] < xCenter) || (centerPoint[0] > xCenter && centerPoint[0] < x + width)) {
1358
+ if (centerPoint[1] < yCenter) {
1359
+ direction.push('top');
1360
+ }
1361
+ else {
1362
+ direction.push('bottom');
1363
+ }
1364
+ }
1365
+ return direction.length ? direction : null;
1492
1366
  }
1493
- if (options.linkLineColor) {
1494
- newElement.linkLineColor = options.linkLineColor;
1367
+ // y
1368
+ if (centerPoint[0] > x && centerPoint[0] < x + width) {
1369
+ if (centerPoint[1] > top && centerPoint[1] < yCenter) {
1370
+ direction.push('top');
1371
+ }
1372
+ if (centerPoint[1] > yCenter && centerPoint[1] < bottom) {
1373
+ direction.push('bottom');
1374
+ }
1375
+ if ((centerPoint[1] > y && centerPoint[1] < y + height) || (centerPoint[1] > yCenter && centerPoint[1] < y + height)) {
1376
+ if (centerPoint[0] < xCenter) {
1377
+ direction.push('left');
1378
+ }
1379
+ else {
1380
+ direction.push('right');
1381
+ }
1382
+ }
1383
+ return direction.length ? direction : null;
1495
1384
  }
1496
- return newElement;
1385
+ return null;
1497
1386
  };
1498
- // layoutLevel 用来表示插入兄弟节点还是子节点
1499
- const insertMindElement = (board, inheritNode, path) => {
1500
- let fill, strokeColor, strokeWidth, shape = MindNodeShape.roundRectangle;
1501
- if (!inheritNode.isRoot) {
1502
- fill = inheritNode.fill;
1503
- strokeColor = inheritNode.strokeColor;
1504
- strokeWidth = inheritNode.strokeWidth;
1387
+
1388
+ const directionCorrector = (node, detectResults) => {
1389
+ if (!node.origin.isRoot) {
1390
+ const parentlayout = MindQueries.getCorrectLayoutByElement(node === null || node === void 0 ? void 0 : node.parent.origin);
1391
+ if (isStandardLayout(parentlayout)) {
1392
+ const idx = node.parent.children.findIndex(x => x === node);
1393
+ const isLeft = idx >= (node.parent.origin.rightNodeCount || 0);
1394
+ return getAllowedDirection(detectResults, [isLeft ? 'right' : 'left']);
1395
+ }
1396
+ if (isLeftLayout(parentlayout)) {
1397
+ return getAllowedDirection(detectResults, ['right']);
1398
+ }
1399
+ if (isRightLayout(parentlayout)) {
1400
+ return getAllowedDirection(detectResults, ['left']);
1401
+ }
1402
+ if (parentlayout === MindLayoutType.upward) {
1403
+ return getAllowedDirection(detectResults, ['bottom']);
1404
+ }
1405
+ if (parentlayout === MindLayoutType.downward) {
1406
+ return getAllowedDirection(detectResults, ['top']);
1407
+ }
1505
1408
  }
1506
- shape = inheritNode.shape;
1507
- const newElement = createMindElement('', NODE_MIN_WIDTH, TEXT_DEFAULT_HEIGHT, { fill, strokeColor, strokeWidth, shape });
1508
- const index = path[path.length - 1];
1509
- const abstractNode = inheritNode.children.find(child => AbstractNode.isAbstract(child) && index > child.start && index <= child.end + 1);
1510
- if (abstractNode) {
1511
- const path = PlaitBoard.findPath(board, abstractNode);
1512
- Transforms.setNode(board, { end: abstractNode.end + 1 }, path);
1409
+ else {
1410
+ const layout = MindQueries.getCorrectLayoutByElement(node === null || node === void 0 ? void 0 : node.origin);
1411
+ if (isStandardLayout(layout)) {
1412
+ return getAllowedDirection(detectResults, ['top', 'bottom']);
1413
+ }
1414
+ if (isTopLayout(layout)) {
1415
+ return getAllowedDirection(detectResults, ['left', 'right', 'bottom']);
1416
+ }
1417
+ if (isBottomLayout(layout)) {
1418
+ return getAllowedDirection(detectResults, ['left', 'right', 'top']);
1419
+ }
1420
+ if (layout === MindLayoutType.left) {
1421
+ return getAllowedDirection(detectResults, ['right', 'top', 'bottom']);
1422
+ }
1423
+ if (layout === MindLayoutType.right) {
1424
+ return getAllowedDirection(detectResults, ['left', 'top', 'bottom']);
1425
+ }
1426
+ }
1427
+ return null;
1428
+ };
1429
+ const getAllowedDirection = (detectResults, illegalDirections) => {
1430
+ const directions = detectResults;
1431
+ illegalDirections.forEach(item => {
1432
+ const bottomDirectionIndex = directions.findIndex(direction => direction === item);
1433
+ if (bottomDirectionIndex !== -1) {
1434
+ directions.splice(bottomDirectionIndex, 1);
1435
+ }
1436
+ });
1437
+ return directions.length ? directions : null;
1438
+ };
1439
+
1440
+ /* 根据布局调整 target 以及 direction */
1441
+ const readjustmentDropTarget = (dropTarget) => {
1442
+ const { target, detectResult } = dropTarget;
1443
+ const newDropTarget = { target, detectResult };
1444
+ const targetComponent = PlaitElement.getComponent(target);
1445
+ if (targetComponent.node.children.length > 0 && dropTarget.detectResult) {
1446
+ const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1447
+ const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1448
+ if (['right', 'left'].includes(dropTarget.detectResult)) {
1449
+ if (!isMixedLayout(parentLayout, layout)) {
1450
+ if (targetComponent.node.origin.isRoot) {
1451
+ const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1452
+ // 标准布局,根节点
1453
+ if (isStandardLayout(layout)) {
1454
+ const rightNodeCount = targetComponent.node.origin.rightNodeCount;
1455
+ if (detectResult === 'left') {
1456
+ // 作为左的第一个节点
1457
+ if (targetComponent.node.children.length === rightNodeCount) {
1458
+ return newDropTarget;
1459
+ }
1460
+ }
1461
+ else {
1462
+ // 作为右的第一个节点或最后一个节点
1463
+ if (rightNodeCount === 0) {
1464
+ newDropTarget.target = target;
1465
+ }
1466
+ else {
1467
+ newDropTarget.target = targetComponent.node.children[rightNodeCount - 1].origin;
1468
+ newDropTarget.detectResult = 'bottom';
1469
+ }
1470
+ return newDropTarget;
1471
+ }
1472
+ }
1473
+ }
1474
+ // 缩进布局探测到第一个子节点
1475
+ if (isIndentedLayout(parentLayout)) {
1476
+ newDropTarget.target = targetComponent.node.children[0].origin;
1477
+ newDropTarget.detectResult = isTopLayout(parentLayout) ? 'bottom' : 'top';
1478
+ return newDropTarget;
1479
+ }
1480
+ // 上下布局的根节点只可以探测到上或者下,子节点的左右探测不处理,跳过。
1481
+ if (isVerticalLogicLayout(parentLayout)) {
1482
+ return newDropTarget;
1483
+ }
1484
+ // 剩下是水平布局的默认情况:插入最后一个子节点的下方
1485
+ const lastChildNodeIndex = targetComponent.node.children.length - 1;
1486
+ newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1487
+ newDropTarget.detectResult = 'bottom';
1488
+ }
1489
+ else {
1490
+ // 处理左右布局下的混合布局
1491
+ if ([MindLayoutType.left, MindLayoutType.right].includes(parentLayout)) {
1492
+ const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1493
+ if (isIndentedLayout(layout)) {
1494
+ newDropTarget.target = targetComponent.node.children[0].origin;
1495
+ newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
1496
+ return newDropTarget;
1497
+ }
1498
+ }
1499
+ }
1500
+ }
1501
+ if (['top', 'bottom'].includes(dropTarget.detectResult)) {
1502
+ // 缩进布局移动至第一个节点
1503
+ if (targetComponent.node.origin.isRoot && isIndentedLayout(layout)) {
1504
+ newDropTarget.target = targetComponent.node.children[0].origin;
1505
+ newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
1506
+ return newDropTarget;
1507
+ }
1508
+ // 上下布局,插到右边
1509
+ const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1510
+ if (isVerticalLogicLayout(parentLayout)) {
1511
+ const lastChildNodeIndex = targetComponent.node.children.length - 1;
1512
+ newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1513
+ newDropTarget.detectResult = 'right';
1514
+ return newDropTarget;
1515
+ }
1516
+ }
1517
+ return newDropTarget;
1513
1518
  }
1514
- Transforms.insertNode(board, newElement, path);
1515
- clearSelectedElement(board);
1516
- addSelectedElement(board, newElement);
1517
- setTimeout(() => {
1518
- enterNodeEditing(newElement);
1519
- });
1519
+ return dropTarget;
1520
1520
  };
1521
- const findLastChild = (child) => {
1522
- let result = child;
1523
- while (result.children.length !== 0) {
1524
- result = result.children[result.children.length - 1];
1521
+
1522
+ const getRectangleByResizingLocation = (abstractRectangle, location, activeHandlePosition, isHorizontal) => {
1523
+ if (isHorizontal) {
1524
+ if (activeHandlePosition === AbstractHandlePosition.start) {
1525
+ return Object.assign(Object.assign({}, abstractRectangle), { y: location, height: abstractRectangle.height + abstractRectangle.y - location });
1526
+ }
1527
+ else {
1528
+ return Object.assign(Object.assign({}, abstractRectangle), { height: location - abstractRectangle.y });
1529
+ }
1530
+ }
1531
+ else {
1532
+ if (activeHandlePosition === AbstractHandlePosition.start) {
1533
+ return Object.assign(Object.assign({}, abstractRectangle), { x: location, width: abstractRectangle.width + abstractRectangle.x - location });
1534
+ }
1535
+ else {
1536
+ return Object.assign(Object.assign({}, abstractRectangle), { width: location - abstractRectangle.x });
1537
+ }
1525
1538
  }
1526
- return result;
1527
1539
  };
1528
- const deleteSelectedELements = (board, selectedElements) => {
1529
- //翻转,从下到上修改,防止找不到 path
1530
- const deletableElements = filterChildElement(selectedElements).reverse();
1531
- const relativeAbstracts = [];
1532
- const accumulativeProperties = new WeakMap();
1533
- deletableElements.forEach(node => {
1534
- if (!PlaitMind.isMind(node)) {
1535
- const parentElement = MindElement.getParent(node);
1536
- const index = parentElement.children.indexOf(node);
1537
- const abstracts = parentElement.children.filter(value => AbstractNode.isAbstract(value));
1538
- abstracts.forEach(abstract => {
1539
- const abstractNode = abstract;
1540
- if (index >= abstractNode.start && index <= abstractNode.end) {
1541
- let newProperties = accumulativeProperties.get(abstractNode);
1542
- if (!newProperties) {
1543
- newProperties = { start: abstractNode.start, end: abstractNode.end };
1544
- accumulativeProperties.set(abstractNode, newProperties);
1545
- relativeAbstracts.push(abstractNode);
1546
- }
1547
- newProperties.end = newProperties.end - 1;
1548
- }
1549
- });
1540
+ const getLocationScope = (board, handlePosition, parentChildren, element, parent, isHorizontal) => {
1541
+ const node = MindElement.getNode(element);
1542
+ const { start, end } = getCorrectStartEnd(node.origin, parent);
1543
+ const startNode = parentChildren[start];
1544
+ const endNode = parentChildren[end];
1545
+ if (handlePosition === AbstractHandlePosition.start) {
1546
+ const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.end < element.start);
1547
+ let minNode;
1548
+ if (abstractNode.length) {
1549
+ const index = abstractNode
1550
+ .map(node => {
1551
+ const { end } = getCorrectStartEnd(node, parent);
1552
+ return end;
1553
+ })
1554
+ .sort((a, b) => b - a)[0];
1555
+ minNode = parentChildren[index + 1];
1550
1556
  }
1551
- });
1552
- const abstractHandles = relativeAbstracts.map(value => {
1553
- const newProperties = accumulativeProperties.get(value);
1554
- if (newProperties) {
1555
- const path = PlaitBoard.findPath(board, value);
1556
- return () => {
1557
- if (newProperties.start > newProperties.end) {
1558
- Transforms.removeNode(board, path);
1559
- }
1560
- else {
1561
- Transforms.setNode(board, newProperties, path);
1562
- }
1557
+ else {
1558
+ minNode = parentChildren[0];
1559
+ }
1560
+ const minNodeRectangle = getRectangleByElements(board, [minNode], true);
1561
+ const endNodeRectangle = getRectangleByElements(board, [endNode], false);
1562
+ if (isHorizontal) {
1563
+ return {
1564
+ max: endNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
1565
+ min: minNodeRectangle.y - ABSTRACT_INCLUDED_OUTLINE_OFFSET
1563
1566
  };
1564
1567
  }
1565
- return () => { };
1566
- });
1567
- const deletableHandles = deletableElements.map(node => {
1568
- const path = PlaitBoard.findPath(board, node);
1569
- return () => {
1570
- if (shouldChangeRightNodeCount(node)) {
1571
- changeRightNodeCount(board, path.slice(0, path.length - 1), -1);
1572
- }
1573
- Transforms.removeNode(board, path);
1574
- };
1575
- });
1576
- abstractHandles.forEach(action => action());
1577
- deletableHandles.forEach(action => action());
1578
- };
1579
- const divideElementByParent = (elements) => {
1580
- const abstractIncludedGroups = [];
1581
- const parentElements = [];
1582
- for (let i = 0; i < elements.length; i++) {
1583
- const parent = MindElement.getParent(elements[i]);
1584
- const parentIndex = parentElements.indexOf(parent);
1585
- if (parentIndex === -1) {
1586
- parentElements.push(parent);
1587
- abstractIncludedGroups.push([elements[i]]);
1568
+ else {
1569
+ return {
1570
+ max: endNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET,
1571
+ min: minNodeRectangle.x - ABSTRACT_INCLUDED_OUTLINE_OFFSET
1572
+ };
1573
+ }
1574
+ }
1575
+ else {
1576
+ const abstractNode = parentChildren.filter(child => AbstractNode.isAbstract(child) && child.start > element.end);
1577
+ let maxNode;
1578
+ if (abstractNode.length) {
1579
+ const index = abstractNode
1580
+ .map(node => {
1581
+ const { start } = getCorrectStartEnd(node, parent);
1582
+ return start;
1583
+ })
1584
+ .sort((a, b) => a - b)[0];
1585
+ maxNode = parentChildren[index - 1];
1588
1586
  }
1589
1587
  else {
1590
- abstractIncludedGroups[parentIndex].push(elements[i]);
1588
+ const children = parentChildren.filter(child => !AbstractNode.isAbstract(child));
1589
+ maxNode = parentChildren[children.length - 1];
1590
+ }
1591
+ const maxNodeRectangle = getRectangleByElements(board, [maxNode], true);
1592
+ const startNodeRectangle = getRectangleByElements(board, [startNode], false);
1593
+ if (isHorizontal) {
1594
+ return {
1595
+ max: maxNodeRectangle.y + maxNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
1596
+ min: startNodeRectangle.y + startNodeRectangle.height + ABSTRACT_INCLUDED_OUTLINE_OFFSET
1597
+ };
1598
+ }
1599
+ else {
1600
+ return {
1601
+ max: maxNodeRectangle.x + maxNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET,
1602
+ min: startNodeRectangle.x + startNodeRectangle.width + ABSTRACT_INCLUDED_OUTLINE_OFFSET
1603
+ };
1591
1604
  }
1592
1605
  }
1593
- return { parentElements, abstractIncludedGroups };
1594
1606
  };
1595
-
1596
- const getStrokeByMindElement = (element) => {
1597
- let stroke = element.strokeColor;
1598
- if (stroke) {
1599
- return stroke;
1600
- }
1601
- const { root, branch } = findUpElement(element);
1602
- if (branch) {
1603
- const index = root.children.indexOf(branch);
1604
- const length = COLORS.length;
1605
- const remainder = index % length;
1606
- return COLORS[remainder];
1607
+ const getHitAbstractHandle = (board, element, point) => {
1608
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
1609
+ const isHorizontal = isHorizontalLayout(nodeLayout);
1610
+ const parentElement = MindElement.getParent(element);
1611
+ const includedElements = parentElement.children.slice(element.start, element.end + 1);
1612
+ let abstractRectangle = getRectangleByElements(board, includedElements, true);
1613
+ abstractRectangle = RectangleClient.getOutlineRectangle(abstractRectangle, -ABSTRACT_INCLUDED_OUTLINE_OFFSET);
1614
+ const startHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.start);
1615
+ const endHandleRec = getAbstractHandleRectangle(abstractRectangle, isHorizontal, AbstractHandlePosition.end);
1616
+ const pointRec = RectangleClient.toRectangleClient([point, point]);
1617
+ if (RectangleClient.isHit(pointRec, startHandleRec))
1618
+ return AbstractHandlePosition.start;
1619
+ if (RectangleClient.isHit(pointRec, endHandleRec))
1620
+ return AbstractHandlePosition.end;
1621
+ return undefined;
1622
+ };
1623
+ const getAbstractHandleRectangle = (rectangle, isHorizontal, position) => {
1624
+ let result;
1625
+ if (position === AbstractHandlePosition.start) {
1626
+ const location = isHorizontal ? rectangle.y : rectangle.x;
1627
+ result = getRectangleByResizingLocation(rectangle, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.end, isHorizontal);
1628
+ result = getRectangleByResizingLocation(result, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
1607
1629
  }
1608
1630
  else {
1609
- return ROOT_NODE_STROKE;
1631
+ const location = isHorizontal ? rectangle.y + rectangle.height : rectangle.x + rectangle.width;
1632
+ result = getRectangleByResizingLocation(rectangle, location - ABSTRACT_HANDLE_MASK_WIDTH / 2, AbstractHandlePosition.start, isHorizontal);
1633
+ result = getRectangleByResizingLocation(result, location + ABSTRACT_HANDLE_MASK_WIDTH / 2, position, isHorizontal);
1610
1634
  }
1635
+ return result;
1611
1636
  };
1612
- const getLinkLineColorByMindElement = (element) => {
1613
- let color = element.linkLineColor;
1614
- if (color) {
1615
- return color;
1637
+ function findLocationLeftIndex(board, parentChildren, location, isHorizontal) {
1638
+ const children = parentChildren.filter(child => {
1639
+ return !AbstractNode.isAbstract(child);
1640
+ });
1641
+ const recArray = children.map(child => {
1642
+ return getRectangleByElements(board, [child], false);
1643
+ });
1644
+ const firstRec = getRectangleByElements(board, [children[0]], true);
1645
+ const fakeLeftRec = {
1646
+ x: firstRec.x - firstRec.width,
1647
+ y: firstRec.y - firstRec.height,
1648
+ width: firstRec.width,
1649
+ height: firstRec.height
1650
+ };
1651
+ const lastRec = getRectangleByElements(board, [children[children.length - 1]], true);
1652
+ const fakeRightRec = {
1653
+ x: lastRec.x + lastRec.width,
1654
+ y: lastRec.y + lastRec.height,
1655
+ width: lastRec.width,
1656
+ height: lastRec.height
1657
+ };
1658
+ recArray.push(fakeRightRec);
1659
+ recArray.unshift(fakeLeftRec);
1660
+ for (let i = 0; i < recArray.length - 1; i++) {
1661
+ const recXOrY = isHorizontal ? recArray[i].y : recArray[i].x;
1662
+ const recWidthOrHeight = isHorizontal ? recArray[i].height : recArray[i].width;
1663
+ if (location >= recXOrY + recWidthOrHeight / 2 &&
1664
+ location <= recArray[i + 1][isHorizontal ? 'y' : 'x'] + recArray[i + 1][isHorizontal ? 'height' : 'width'] / 2) {
1665
+ return i - 1;
1666
+ }
1616
1667
  }
1617
- const { root, branch } = findUpElement(element);
1618
- if (branch) {
1619
- const index = root.children.indexOf(branch);
1620
- const length = COLORS.length;
1621
- const remainder = index % length;
1622
- return COLORS[remainder];
1668
+ return 0;
1669
+ }
1670
+ function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1671
+ let touchedHandle;
1672
+ const abstract = getSelectedElements(board)
1673
+ .filter(element => AbstractNode.isAbstract(element))
1674
+ .find(element => {
1675
+ touchedHandle = getHitAbstractHandle(board, element, endPoint);
1676
+ return touchedHandle;
1677
+ });
1678
+ if (touchedAbstract === abstract) {
1679
+ return touchedAbstract;
1623
1680
  }
1624
- else {
1625
- throw new Error('root element should not have link line');
1681
+ if (touchedAbstract) {
1682
+ const component = PlaitElement.getComponent(touchedAbstract);
1683
+ component.updateAbstractIncludedOutline();
1684
+ touchedAbstract = undefined;
1626
1685
  }
1627
- };
1628
- const getRootLinkLineColorByMindElement = (root) => {
1629
- const index = root.children.length;
1630
- const length = COLORS.length;
1631
- const remainder = index % length;
1632
- return COLORS[remainder];
1633
- };
1686
+ if (abstract) {
1687
+ touchedAbstract = abstract;
1688
+ const component = PlaitElement.getComponent(touchedAbstract);
1689
+ component.updateAbstractIncludedOutline(touchedHandle);
1690
+ }
1691
+ return touchedAbstract;
1692
+ }
1634
1693
 
1635
- function drawIndentedLink(roughSVG, node, child, defaultStroke = null, needDrawUnderline = true) {
1694
+ function drawIndentedLink(board, node, child, defaultStroke = null, needDrawUnderline = true) {
1636
1695
  const isUnderlineShap = getNodeShapeByElement(child.origin) === MindNodeShape.underline;
1637
1696
  let beginX, beginY, endX, endY, beginNode = node, endNode = child;
1638
1697
  const beginRectangle = getRectangleByNode(beginNode);
@@ -1644,7 +1703,7 @@ function drawIndentedLink(roughSVG, node, child, defaultStroke = null, needDrawU
1644
1703
  //根据位置,设置正负参数
1645
1704
  let plusMinus = isChildUp(node, child) ? (node.left ? [-1, -1] : [1, -1]) : node.left ? [-1, 1] : [1, 1];
1646
1705
  const layout = MindQueries.getCorrectLayoutByElement(node.origin);
1647
- const strokeWidth = child.origin.linkLineWidth ? child.origin.linkLineWidth : STROKE_WIDTH;
1706
+ const strokeWidth = child.origin.branchWidth ? child.origin.branchWidth : STROKE_WIDTH;
1648
1707
  if (beginNode.origin.isRoot) {
1649
1708
  if (layout === MindLayoutType.leftBottomIndented || layout === MindLayoutType.rightBottomIndented) {
1650
1709
  beginY += strokeWidth;
@@ -1665,9 +1724,9 @@ function drawIndentedLink(roughSVG, node, child, defaultStroke = null, needDrawU
1665
1724
  isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1666
1725
  isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY]
1667
1726
  ];
1668
- const stroke = defaultStroke || getLinkLineColorByMindElement(child.origin);
1727
+ const stroke = defaultStroke || getBranchColorByMindElement(board, child.origin);
1669
1728
  const points = pointsOnBezierCurves(curve);
1670
- return roughSVG.curve(points, { stroke, strokeWidth });
1729
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke, strokeWidth });
1671
1730
  }
1672
1731
 
1673
1732
  var HorizontalPlacement;
@@ -1780,9 +1839,9 @@ const transformPlacement = (placement, direction) => {
1780
1839
  }
1781
1840
  };
1782
1841
 
1783
- function drawLogicLink(roughSVG, node, parent, isHorizontal) {
1784
- const stroke = getLinkLineColorByMindElement(node.origin);
1785
- const strokeWidth = node.origin.linkLineWidth ? node.origin.linkLineWidth : STROKE_WIDTH;
1842
+ function drawLogicLink(board, node, parent, isHorizontal) {
1843
+ const branchColor = getBranchColorByMindElement(board, node.origin);
1844
+ const strokeWidth = node.origin.branchWidth ? node.origin.branchWidth : STROKE_WIDTH;
1786
1845
  const hasStraightLine = !parent.origin.isRoot;
1787
1846
  const hasUnderlineShape = node.origin.shape === MindNodeShape.underline;
1788
1847
  const hasUnderlineShapeOfParent = parent.origin.shape === MindNodeShape.underline;
@@ -1822,7 +1881,7 @@ function drawLogicLink(roughSVG, node, parent, isHorizontal) {
1822
1881
  const underlineEnd = movePoint(endPoint, nodeClient.width, linkDirection);
1823
1882
  const underline = hasUnderlineShape && isHorizontal ? [underlineEnd, underlineEnd, underlineEnd] : [];
1824
1883
  const points = pointsOnBezierCurves([...straightLine, ...curve, ...underline]);
1825
- return roughSVG.curve(points, { stroke, strokeWidth });
1884
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth });
1826
1885
  }
1827
1886
 
1828
1887
  function getEmojisRectangle(element) {
@@ -1945,7 +2004,7 @@ function getRichtextRectangleByNode(node) {
1945
2004
  function drawRectangleNode(board, node) {
1946
2005
  const { x, y, width, height } = getRectangleByNode(node);
1947
2006
  const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? ROOT_NODE_FILL : NODE_FILL;
1948
- const stroke = getStrokeByMindElement(node.origin);
2007
+ const stroke = getStrokeByMindElement(board, node.origin);
1949
2008
  const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : STROKE_WIDTH;
1950
2009
  const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), x, y, x + width, y + height, {
1951
2010
  stroke,
@@ -2004,6 +2063,7 @@ class EmojiDrawer {
2004
2063
  const componentType = this.board.drawEmoji(emoji, element);
2005
2064
  this.componentRef = this.viewContainerRef.createComponent(componentType);
2006
2065
  this.componentRef.instance.emojiItem = emoji;
2066
+ this.componentRef.instance.board = this.board;
2007
2067
  const fontSize = PlaitMind.isMind(element) ? 18 : 14;
2008
2068
  this.componentRef.instance.fontSize = fontSize;
2009
2069
  }
@@ -2068,6 +2128,10 @@ class EmojisDrawer {
2068
2128
 
2069
2129
  const setLayout = (board, layout, path) => {
2070
2130
  correctLogicLayoutNode(board, layout, path);
2131
+ const element = PlaitNode.get(board, path);
2132
+ if (PlaitMind.isMind(element) && isStandardLayout(layout)) {
2133
+ handleAbstractIncluded(board, element);
2134
+ }
2071
2135
  Transforms.setNode(board, { layout }, path);
2072
2136
  };
2073
2137
  const correctLogicLayoutNode = (board, layout, path) => {
@@ -2236,6 +2300,14 @@ function hasAfterDraw(value) {
2236
2300
  return false;
2237
2301
  }
2238
2302
 
2303
+ function findNewChildNodePath(board, element) {
2304
+ return PlaitBoard.findPath(board, element).concat((element.children || []).filter(child => !AbstractNode.isAbstract(child)).length);
2305
+ }
2306
+ function findNewSiblingNodePath(board, element) {
2307
+ const path = PlaitBoard.findPath(board, element);
2308
+ return Path$1.next(path);
2309
+ }
2310
+
2239
2311
  class QuickInsertDrawer extends BaseDrawer {
2240
2312
  canDraw(element) {
2241
2313
  if (PlaitBoard.isReadonly(this.board) || (element === null || element === void 0 ? void 0 : element.isCollapsed)) {
@@ -2259,7 +2331,7 @@ class QuickInsertDrawer extends BaseDrawer {
2259
2331
  */
2260
2332
  const shape = getNodeShapeByElement(element);
2261
2333
  // 形状是矩形要偏移边框的线宽
2262
- const strokeWidth = element.linkLineWidth ? element.linkLineWidth : STROKE_WIDTH;
2334
+ const strokeWidth = element.branchWidth ? element.branchWidth : STROKE_WIDTH;
2263
2335
  let offsetBorderLineWidth = 0;
2264
2336
  if (shape === MindNodeShape.roundRectangle && offset === 0) {
2265
2337
  offsetBorderLineWidth = strokeWidth;
@@ -2367,7 +2439,7 @@ class QuickInsertDrawer extends BaseDrawer {
2367
2439
  underlineCoordinates[MindLayoutType.right].startY -= height * 0.5;
2368
2440
  underlineCoordinates[MindLayoutType.right].endY -= height * 0.5;
2369
2441
  }
2370
- const stroke = element.isRoot ? getRootLinkLineColorByMindElement(element) : getLinkLineColorByMindElement(element);
2442
+ const branchColor = PlaitMind.isMind(element) ? getNextBranchColor(element) : getBranchColorByMindElement(this.board, element);
2371
2443
  let nodeLayout = MindQueries.getCorrectLayoutByElement(element);
2372
2444
  if (element.isRoot && isStandardLayout(nodeLayout)) {
2373
2445
  const root = element;
@@ -2375,7 +2447,7 @@ class QuickInsertDrawer extends BaseDrawer {
2375
2447
  }
2376
2448
  const underlineCoordinate = underlineCoordinates[nodeLayout];
2377
2449
  if (underlineCoordinate) {
2378
- const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke, strokeWidth });
2450
+ const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth });
2379
2451
  const circleCoordinates = {
2380
2452
  startX: underlineCoordinate.endX,
2381
2453
  startY: underlineCoordinate.endY
@@ -2426,7 +2498,7 @@ class QuickInsertDrawer extends BaseDrawer {
2426
2498
  fromEvent(this.g, 'mouseup')
2427
2499
  .pipe(take(1))
2428
2500
  .subscribe(() => {
2429
- const path = PlaitBoard.findPath(this.board, element).concat(element.children.filter(child => !AbstractNode.isAbstract(child)).length);
2501
+ const path = findNewChildNodePath(this.board, element);
2430
2502
  insertMindElement(this.board, element, path);
2431
2503
  });
2432
2504
  }
@@ -2543,10 +2615,10 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2543
2615
  this.linkG = drawAbstractLink(this.board, this.node, isHorizontalLayout(layout));
2544
2616
  }
2545
2617
  else if (MindElement.isIndentedLayout(this.parent.origin)) {
2546
- this.linkG = drawIndentedLink(this.roughSVG, this.parent, this.node);
2618
+ this.linkG = drawIndentedLink(this.board, this.parent, this.node);
2547
2619
  }
2548
2620
  else {
2549
- this.linkG = drawLogicLink(this.roughSVG, this.node, this.parent, isHorizontalLayout(layout));
2621
+ this.linkG = drawLogicLink(this.board, this.node, this.parent, isHorizontalLayout(layout));
2550
2622
  }
2551
2623
  this.g.append(this.linkG);
2552
2624
  }
@@ -2704,8 +2776,8 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2704
2776
  Transforms.setNode(this.board, newElement, path);
2705
2777
  });
2706
2778
  const { x, y, width, height } = getRectangleByNode(this.node);
2707
- const stroke = getLinkLineColorByMindElement(this.element);
2708
- const strokeWidth = this.node.origin.linkLineWidth ? this.node.origin.linkLineWidth : STROKE_WIDTH;
2779
+ const stroke = getBranchColorByMindElement(this.board, this.element);
2780
+ const strokeWidth = this.node.origin.branchWidth ? this.node.origin.branchWidth : STROKE_WIDTH;
2709
2781
  const extendY = y + height / 2;
2710
2782
  const nodeLayout = MindQueries.getCorrectLayoutByElement(this.element);
2711
2783
  let extendLineXY = [
@@ -3205,7 +3277,7 @@ const withDnd = (board) => {
3205
3277
  });
3206
3278
  if (dropTarget === null || dropTarget === void 0 ? void 0 : dropTarget.target) {
3207
3279
  dropTarget = readjustmentDropTarget(dropTarget);
3208
- drawPlaceholderDropNodeG(dropTarget, roughSVG, fakeDropNodeG);
3280
+ drawPlaceholderDropNodeG(board, dropTarget, fakeDropNodeG);
3209
3281
  }
3210
3282
  }
3211
3283
  mousemove(event);
@@ -3365,7 +3437,7 @@ const setIsDragging = (board, state) => {
3365
3437
 
3366
3438
  const buildClipboardData = (board, selectedElements) => {
3367
3439
  let result = [];
3368
- const selectedMindNodes = selectedElements.map((value) => MindElement.getNode(value));
3440
+ const selectedMindNodes = selectedElements.map(value => MindElement.getNode(value));
3369
3441
  const nodesRectangle = getRectangleByElements(board, selectedElements, true);
3370
3442
  selectedElements.forEach((node, index) => {
3371
3443
  const nodeRectangle = getRectangleByNode(selectedMindNodes[index]);
@@ -3401,12 +3473,18 @@ const insertClipboardData = (board, elements, targetPoint) => {
3401
3473
  if (item.isRoot) {
3402
3474
  newElement = transformRootToNode(board, newElement);
3403
3475
  }
3476
+ if (AbstractNode.isAbstract(item)) {
3477
+ newElement = transformAbstractToNode(newElement);
3478
+ }
3404
3479
  const selectedElementPath = PlaitBoard.findPath(board, selectedElements[0]);
3405
3480
  path = selectedElementPath.concat((selectedElements[0].children || []).length + index);
3406
3481
  }
3407
3482
  else {
3408
3483
  const point = [targetPoint[0] + item.points[0][0], targetPoint[1] + item.points[0][1]];
3409
3484
  newElement.points = [point];
3485
+ if (AbstractNode.isAbstract(item)) {
3486
+ newElement = transformAbstractToNode(newElement);
3487
+ }
3410
3488
  if (!item.isRoot) {
3411
3489
  newElement = transformNodeToRoot(board, newElement);
3412
3490
  }
@@ -3425,15 +3503,6 @@ const insertClipboardText = (board, parentElement, text, width, height) => {
3425
3503
  return;
3426
3504
  };
3427
3505
 
3428
- function findNewChildNodePath(board, element) {
3429
- const path = PlaitBoard.findPath(board, element);
3430
- return path.concat((element.children || []).length);
3431
- }
3432
- function findNewSiblingNodePath(board, element) {
3433
- const path = PlaitBoard.findPath(board, element);
3434
- return Path$1.next(path);
3435
- }
3436
-
3437
3506
  const withEmoji = (board) => {
3438
3507
  const newBoard = board;
3439
3508
  newBoard.drawEmoji = (emoji, element) => {
@@ -3521,7 +3590,7 @@ const withAbstract = (board) => {
3521
3590
  };
3522
3591
  board.mouseup = (event) => {
3523
3592
  startPoint = undefined;
3524
- abstractHandlePosition = null;
3593
+ abstractHandlePosition = undefined;
3525
3594
  if (activeAbstractElement) {
3526
3595
  if (newBoard === null || newBoard === void 0 ? void 0 : newBoard.abstractResize) {
3527
3596
  newBoard.abstractResize(AbstractResizeState.end);
@@ -3605,6 +3674,7 @@ const withMind = (board) => {
3605
3674
  if (shouldChangeRightNodeCount(selectedElement)) {
3606
3675
  changeRightNodeCount(board, selectedElementPath.slice(0, 1), 1);
3607
3676
  }
3677
+ insertSiblingElementHandleAbstract(board, selectedElement);
3608
3678
  insertMindElement(board, selectedElement, findNewSiblingNodePath(board, selectedElement));
3609
3679
  }
3610
3680
  return;
@@ -3721,13 +3791,15 @@ class MindEmojiBaseComponent {
3721
3791
  }
3722
3792
  }
3723
3793
  MindEmojiBaseComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindEmojiBaseComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
3724
- MindEmojiBaseComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: MindEmojiBaseComponent, inputs: { fontSize: "fontSize", emojiItem: "emojiItem" }, ngImport: i0 });
3794
+ MindEmojiBaseComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.5", type: MindEmojiBaseComponent, inputs: { fontSize: "fontSize", emojiItem: "emojiItem", board: "board" }, ngImport: i0 });
3725
3795
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindEmojiBaseComponent, decorators: [{
3726
3796
  type: Directive
3727
3797
  }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { fontSize: [{
3728
3798
  type: Input
3729
3799
  }], emojiItem: [{
3730
3800
  type: Input
3801
+ }], board: [{
3802
+ type: Input
3731
3803
  }] } });
3732
3804
 
3733
3805
  /*
@@ -3738,5 +3810,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3738
3810
  * Generated bundle index. Do not edit.
3739
3811
  */
3740
3812
 
3741
- export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, COLORS, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, LayoutDirection, LayoutDirectionsMap, MAX_RADIUS, MINDMAP_KEY, MindElement, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindNodeShape, MindQueries, MindTransforms, 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, canSetAbstract, changeRightNodeCount, copyNewNode, correctLayoutByDirection, createDefaultMindMapElement, createMindElement, deleteSelectedELements, directionCorrector, directionDetector, divideElementByParent, drawCurvePlaceholderDropNodeG, drawIndentNodeG, drawPlaceholderDropNodeG, drawStraightDropNodeG, extractNodesText, filterChildElement, findLastChild, findLocationLeftIndex, findParentElement, findUpElement, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBranchDirectionsByLayouts, getChildrenCount, getDefaultLayout, getEmojiFontSize, getEmojisRectangle, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLinkLineColorByMindElement, getLocationScope, getNodeShapeByElement, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getRootLinkLineColorByMindElement, getStrokeByMindElement, handleTouchedAbstract, hitMindElement, insertMindElement, insetAbstractNode, isChildElement, isChildRight, isChildUp, isCorrectLayout, isMixedLayout, isSetAbstract, isVirtualKey, readjustmentDropTarget, separateChildren, setAbstract, setAbstractByElements, shouldChangeRightNodeCount, transformNodeToRoot, transformRootToNode, withEmoji, withMind };
3813
+ export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, COLORS, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, LayoutDirection, LayoutDirectionsMap, MAX_RADIUS, MINDMAP_KEY, MindElement, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindNodeShape, MindQueries, MindTransforms, 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, canSetAbstract, changeRightNodeCount, copyNewNode, correctLayoutByDirection, createDefaultMindMapElement, createMindElement, deleteSelectedELements, directionCorrector, directionDetector, divideElementByParent, drawCurvePlaceholderDropNodeG, drawIndentNodeG, drawPlaceholderDropNodeG, drawStraightDropNodeG, extractNodesText, filterChildElement, findLastChild, findLocationLeftIndex, findParentElement, findUpElement, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getChildrenCount, getCorrespondingAbstract, getDefaultLayout, getEmojiFontSize, getEmojisRectangle, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getNextBranchColor, getNodeShapeByElement, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getStrokeByMindElement, handleAbstractIncluded, handleTouchedAbstract, hitMindElement, insertAbstractNode, insertMindElement, insertSiblingElementHandleAbstract, isChildElement, isChildRight, isChildUp, isCorrectLayout, isMixedLayout, isSetAbstract, isVirtualKey, moveAbstractPosition, readjustmentDropTarget, separateChildren, setAbstract, setAbstractByElements, shouldChangeRightNodeCount, transformAbstractToNode, transformNodeToRoot, transformRootToNode, withEmoji, withMind };
3742
3814
  //# sourceMappingURL=plait-mind.mjs.map