@plait/mind 0.2.0-next.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/constants/abstract-node.d.ts +4 -0
  2. package/constants/default.d.ts +1 -2
  3. package/constants/index.d.ts +4 -1
  4. package/constants/node-rule.d.ts +1 -0
  5. package/constants/node-style.d.ts +17 -0
  6. package/constants/node-topic-style.d.ts +4 -0
  7. package/draw/richtext.d.ts +1 -1
  8. package/esm2020/constants/abstract-node.mjs +5 -0
  9. package/esm2020/constants/default.mjs +2 -3
  10. package/esm2020/constants/index.mjs +5 -2
  11. package/esm2020/constants/node-rule.mjs +2 -0
  12. package/esm2020/constants/node-style.mjs +19 -0
  13. package/esm2020/constants/node-topic-style.mjs +5 -0
  14. package/esm2020/draw/abstract.mjs +4 -3
  15. package/esm2020/draw/indented-link.mjs +15 -15
  16. package/esm2020/draw/link/abstract-link.mjs +6 -4
  17. package/esm2020/draw/link/logic-link.mjs +10 -8
  18. package/esm2020/draw/link.mjs +8 -7
  19. package/esm2020/draw/richtext.mjs +10 -8
  20. package/esm2020/draw/shape.mjs +5 -5
  21. package/esm2020/drawer/quick-insert.drawer.mjs +13 -14
  22. package/esm2020/interfaces/abstract.mjs +1 -1
  23. package/esm2020/interfaces/element.mjs +20 -1
  24. package/esm2020/interfaces/node.mjs +1 -10
  25. package/esm2020/layout-option.mjs +3 -3
  26. package/esm2020/node.component.mjs +43 -34
  27. package/esm2020/plugins/emoji/emoji.drawer.mjs +4 -8
  28. package/esm2020/plugins/emoji/emoji.mjs +31 -3
  29. package/esm2020/plugins/with-abstract.mjs +2 -2
  30. package/esm2020/plugins/with-dnd.mjs +23 -123
  31. package/esm2020/plugins/with-mind.mjs +37 -29
  32. package/esm2020/queries/get-available-sublayouts-by-element.mjs +5 -10
  33. package/esm2020/queries/get-branch-layouts.mjs +7 -7
  34. package/esm2020/queries/get-correct-layout-by-element.mjs +28 -31
  35. package/esm2020/queries/get-layout-by-element.mjs +11 -9
  36. package/esm2020/queries/index.mjs +1 -3
  37. package/esm2020/transforms/abstract-node.mjs +68 -0
  38. package/esm2020/transforms/index.mjs +6 -2
  39. package/esm2020/transforms/layout.mjs +3 -3
  40. package/esm2020/transforms/node.mjs +2 -2
  41. package/esm2020/utils/abstract/common.mjs +90 -70
  42. package/esm2020/utils/abstract/resize.mjs +3 -3
  43. package/esm2020/utils/clipboard.mjs +54 -14
  44. package/esm2020/utils/direction-corrector.mjs +11 -11
  45. package/esm2020/utils/dnd.mjs +122 -0
  46. package/esm2020/utils/draw-placeholder.mjs +11 -9
  47. package/esm2020/utils/drop-target-corrector.mjs +11 -10
  48. package/esm2020/utils/index.mjs +1 -2
  49. package/esm2020/utils/layout.mjs +1 -1
  50. package/esm2020/utils/mind.mjs +30 -72
  51. package/esm2020/utils/node-space.mjs +23 -18
  52. package/esm2020/utils/node-style/branch.mjs +34 -20
  53. package/esm2020/utils/node-style/common.mjs +13 -0
  54. package/esm2020/utils/node-style/index.mjs +3 -2
  55. package/esm2020/utils/node-style/shape.mjs +21 -0
  56. package/esm2020/utils/path.mjs +4 -3
  57. package/fesm2015/plait-mind.mjs +862 -713
  58. package/fesm2015/plait-mind.mjs.map +1 -1
  59. package/fesm2020/plait-mind.mjs +865 -711
  60. package/fesm2020/plait-mind.mjs.map +1 -1
  61. package/interfaces/abstract.d.ts +3 -0
  62. package/interfaces/element.d.ts +7 -2
  63. package/interfaces/node.d.ts +0 -1
  64. package/node.component.d.ts +0 -1
  65. package/package.json +1 -1
  66. package/plugins/emoji/emoji.d.ts +5 -1
  67. package/plugins/with-dnd.d.ts +0 -9
  68. package/queries/get-available-sublayouts-by-element.d.ts +2 -6
  69. package/queries/get-branch-layouts.d.ts +2 -1
  70. package/queries/get-correct-layout-by-element.d.ts +2 -1
  71. package/queries/index.d.ts +3 -4
  72. package/styles/styles.scss +19 -19
  73. package/transforms/abstract-node.d.ts +6 -0
  74. package/transforms/index.d.ts +3 -0
  75. package/utils/abstract/common.d.ts +6 -8
  76. package/utils/direction-corrector.d.ts +2 -1
  77. package/utils/dnd.d.ts +16 -0
  78. package/utils/drop-target-corrector.d.ts +2 -1
  79. package/utils/index.d.ts +0 -1
  80. package/utils/mind.d.ts +4 -5
  81. package/utils/node-space.d.ts +5 -4
  82. package/utils/node-style/branch.d.ts +6 -1
  83. package/utils/node-style/common.d.ts +3 -0
  84. package/utils/node-style/index.d.ts +2 -1
  85. package/utils/node-style/shape.d.ts +4 -0
  86. package/constants/node.d.ts +0 -17
  87. package/esm2020/constants/node.mjs +0 -19
  88. package/esm2020/queries/get-layout-parent-by-element.mjs +0 -17
  89. package/esm2020/utils/node-style/node.mjs +0 -22
  90. package/esm2020/utils/shape.mjs +0 -17
  91. package/queries/get-layout-parent-by-element.d.ts +0 -8
  92. package/utils/node-style/node.d.ts +0 -3
  93. package/utils/shape.d.ts +0 -3
@@ -1,8 +1,8 @@
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, 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, getNonAbstractChildren, getCorrectStartEnd, ConnectingPosition, GlobalLayout } from '@plait/layouts';
4
+ import { distanceBetweenPointAndRectangle, PlaitBoard, PlaitNode, NODE_TO_PARENT, Path, ELEMENT_TO_COMPONENT, PlaitElement, 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, updateForeignObject as updateForeignObject$1, CLIP_BOARD_FORMAT_KEY, BOARD_TO_HOST, Selection, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
5
+ import { MindLayoutType, AbstractNode, getAbstractLayout, isIndentedLayout, isStandardLayout, isHorizontalLogicLayout, isVerticalLogicLayout, isTopLayout, isLeftLayout, isBottomLayout, isRightLayout, isHorizontalLayout, getNonAbstractChildren, 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';
@@ -16,34 +16,44 @@ const ELEMENT_TO_NODE = new WeakMap();
16
16
 
17
17
  const BASE = 4;
18
18
  const PRIMARY_COLOR = '#6698FF';
19
- const MINDMAP_KEY = 'plait-mindmap';
20
- const MAX_RADIUS = BASE * 4;
21
19
  const TRANSPARENT = 'transparent';
22
20
  const GRAY_COLOR = '#AAAAAA';
23
21
  const STROKE_WIDTH = 3;
22
+ const BRANCH_WIDTH = 3;
24
23
  const EXTEND_OFFSET = 8;
25
24
  const EXTEND_RADIUS = 16;
26
25
  const QUICK_INSERT_CIRCLE_OFFSET = 9;
27
26
  const QUICK_INSERT_CIRCLE_COLOR = '#6698FF';
28
27
  const QUICK_INSERT_INNER_CROSS_COLOR = 'white';
29
28
 
29
+ const DefaultAbstractNodeStyle = {
30
+ strokeColor: GRAY_COLOR,
31
+ strokeWidth: 2,
32
+ branchColor: GRAY_COLOR,
33
+ branchWidth: 2,
34
+ fill: '#FFFFFF'
35
+ };
36
+ const DefaultNodeStyle = {
37
+ strokeWidth: 3,
38
+ branchWidth: 3,
39
+ fill: '#FFFFFF'
40
+ };
41
+ const DefaultRootStyle = {
42
+ fill: '#F5F5F5',
43
+ strokeColor: '#F5F5F5',
44
+ };
45
+ const BRANCH_COLORS = ['#A287E1', '#6F81DB', '#6EC4C4', '#DFB85D', '#B1C774', '#77C386', '#C28976', '#E48484', '#E482D4', '#69B1E4'];
46
+
30
47
  const TOPIC_COLOR = '#333';
31
48
  const TOPIC_FONT_SIZE = 14;
32
- const NODE_FILL = '#FFFFFF';
33
- const ROOT_NODE_FILL = '#F5F5F5';
34
- const ROOT_NODE_STROKE = '#F5F5F5';
35
49
  const ROOT_TOPIC_FONT_SIZE = 18;
50
+ const TOPIC_DEFAULT_MAX_WORD_COUNT = 34;
51
+
36
52
  const NODE_MIN_WIDTH = 18;
37
- const COLORS = ['#A287E1', '#6F81DB', '#6EC4C4', '#DFB85D', '#B1C774', '#77C386', '#C28976', '#E48484', '#E482D4', '#69B1E4'];
38
- var MindNodeShape;
39
- (function (MindNodeShape) {
40
- MindNodeShape["roundRectangle"] = "round-rectangle";
41
- MindNodeShape["underline"] = "underline";
42
- })(MindNodeShape || (MindNodeShape = {}));
43
- const ABSTRACT_HANDLE_COLOR = '#6698FF80'; //PRIMARY_COLOR 50% 透明度
53
+
54
+ const ABSTRACT_HANDLE_COLOR = '#6698FF80'; //primary color 50% opacity
44
55
  const ABSTRACT_INCLUDED_OUTLINE_OFFSET = 3.5;
45
56
  const ABSTRACT_HANDLE_LENGTH = 10;
46
- const TOPIC_DEFAULT_MAX_WORD_COUNT = 34;
47
57
  const ABSTRACT_HANDLE_MASK_WIDTH = 8;
48
58
 
49
59
  function getRectangleByNode(node) {
@@ -74,34 +84,35 @@ function hitMindElement(board, point, element) {
74
84
  * 2. correct layout by incorrect layout direction
75
85
  * @param element
76
86
  */
77
- const getCorrectLayoutByElement = (element) => {
78
- const { root } = findUpElement(element);
79
- const rootLayout = root.layout || getDefaultLayout();
80
- let correctRootLayout = rootLayout;
81
- if (element.isRoot) {
82
- return correctRootLayout;
83
- }
84
- const component = PlaitElement.getComponent(element);
85
- let layout = element.layout;
86
- let parentComponent = null;
87
- let parent = component.parent.origin;
88
- while (!layout && parent) {
89
- parentComponent = PlaitElement.getComponent(parent);
90
- layout = parentComponent.node.origin.layout;
91
- parent = parentComponent.parent?.origin;
92
- }
93
- if ((AbstractNode.isAbstract(element) || isChildOfAbstract(MindElement.getNode(element))) &&
94
- isIndentedLayout(layout)) {
95
- return getAbstractLayout(layout);
87
+ const getCorrectLayoutByElement = (board, element) => {
88
+ const ancestors = MindElement.getAncestors(board, element);
89
+ ancestors.unshift(element);
90
+ const root = ancestors[ancestors.length - 1];
91
+ let rootLayout = getRootLayout(root);
92
+ if (PlaitMind.isMind(element)) {
93
+ return rootLayout;
96
94
  }
97
- // handle root standard
95
+ const node = MindElement.getNode(element);
96
+ let correctRootLayout = rootLayout;
98
97
  if (rootLayout === MindLayoutType.standard) {
99
- correctRootLayout = component?.node.left ? MindLayoutType.left : MindLayoutType.right;
98
+ correctRootLayout = node.left ? MindLayoutType.left : MindLayoutType.right;
100
99
  }
101
- if (parentComponent && parentComponent.node.origin.isRoot) {
100
+ let layout = null;
101
+ const elementWithLayout = ancestors.find(value => value.layout || AbstractNode.isAbstract(value));
102
+ if (elementWithLayout) {
103
+ if (AbstractNode.isAbstract(elementWithLayout)) {
104
+ const parent = MindElement.getParent(elementWithLayout);
105
+ const parentLayout = getCorrectLayoutByElement(board, parent);
106
+ layout = getAbstractLayout(parentLayout);
107
+ }
108
+ else {
109
+ layout = elementWithLayout?.layout;
110
+ }
111
+ }
112
+ if (layout === MindLayoutType.standard || !layout) {
102
113
  return correctRootLayout;
103
114
  }
104
- if (layout) {
115
+ else {
105
116
  const incorrectDirection = getInCorrectLayoutDirection(correctRootLayout, layout);
106
117
  if (incorrectDirection) {
107
118
  return correctLayoutByDirection(layout, incorrectDirection);
@@ -110,36 +121,28 @@ const getCorrectLayoutByElement = (element) => {
110
121
  return layout;
111
122
  }
112
123
  }
113
- else {
114
- return correctRootLayout;
115
- }
116
124
  };
117
125
 
118
- const getBranchLayouts = (element) => {
126
+ const getBranchLayouts = (board, element) => {
119
127
  const layouts = [];
120
128
  if (element.layout) {
121
- //getCorrectLayoutByElement含有递归操作,getBranchMindmapLayouts本身也有递归操作,有待优化
122
- layouts.unshift(getCorrectLayoutByElement(element));
129
+ // TODO: getCorrectLayoutByElement 含有递归操作,getBranchLayouts 本身也有递归操作,有待优化
130
+ layouts.unshift(getCorrectLayoutByElement(board, element));
123
131
  }
124
- let parent = findParentElement(element);
132
+ let parent = MindElement.findParent(element);
125
133
  while (parent) {
126
134
  if (parent.layout) {
127
135
  layouts.unshift(parent.layout);
128
136
  }
129
- parent = findParentElement(parent);
137
+ parent = MindElement.findParent(parent);
130
138
  }
131
139
  return layouts;
132
140
  };
133
141
 
134
- /**
135
- * get available sub layouts by element
136
- * @param element
137
- * @returns MindLayoutType[]
138
- */
139
- const getAvailableSubLayoutsByElement = (element) => {
140
- const parentElement = findParentElement(element);
142
+ const getAvailableSubLayoutsByElement = (board, element) => {
143
+ const parentElement = MindElement.findParent(element);
141
144
  if (parentElement) {
142
- const branchLayouts = getBranchLayouts(parentElement);
145
+ const branchLayouts = getBranchLayouts(board, parentElement);
143
146
  if (branchLayouts[0] === MindLayoutType.standard) {
144
147
  const node = MindElement.getNode(element);
145
148
  branchLayouts[0] = node.left ? MindLayoutType.left : MindLayoutType.right;
@@ -155,20 +158,107 @@ const getAvailableSubLayoutsByElement = (element) => {
155
158
  return undefined;
156
159
  };
157
160
 
158
- /**
159
- * 获取父节点布局类型
160
- * @param element
161
- * @returns MindLayoutType
162
- */
163
- const getLayoutParentByElement = (element) => {
164
- let parent = findParentElement(element);
165
- while (parent) {
166
- if (parent.layout) {
167
- return parent.layout;
161
+ const getBranchDirectionsByLayouts = (branchLayouts) => {
162
+ const branchDirections = [];
163
+ branchLayouts.forEach(l => {
164
+ const directions = LayoutDirectionsMap[l];
165
+ directions.forEach(d => {
166
+ if (!branchDirections.includes(d) && !branchDirections.includes(getLayoutReverseDirection(d))) {
167
+ branchDirections.push(d);
168
+ }
169
+ });
170
+ });
171
+ return branchDirections;
172
+ };
173
+ const isCorrectLayout = (root, layout) => {
174
+ const rootLayout = root.layout || getDefaultLayout();
175
+ return !getInCorrectLayoutDirection(rootLayout, layout);
176
+ };
177
+ const isMixedLayout = (parentLayout, layout) => {
178
+ return (!isIndentedLayout(parentLayout) && isIndentedLayout(layout)) || (isIndentedLayout(parentLayout) && !isIndentedLayout(layout));
179
+ };
180
+ const getInCorrectLayoutDirection = (rootLayout, layout) => {
181
+ const directions = LayoutDirectionsMap[rootLayout];
182
+ const subLayoutDirections = LayoutDirectionsMap[layout];
183
+ if (!subLayoutDirections) {
184
+ throw new Error(`unexpected layout: ${layout} on correct layout`);
185
+ }
186
+ return subLayoutDirections.find(d => directions.includes(getLayoutReverseDirection(d)));
187
+ };
188
+ const correctLayoutByDirection = (layout, direction) => {
189
+ const isHorizontal = direction === LayoutDirection.left || direction === LayoutDirection.right ? true : false;
190
+ let inverseDirectionLayout = MindLayoutType.standard;
191
+ switch (layout) {
192
+ case MindLayoutType.left:
193
+ inverseDirectionLayout = MindLayoutType.right;
194
+ break;
195
+ case MindLayoutType.right:
196
+ inverseDirectionLayout = MindLayoutType.left;
197
+ break;
198
+ case MindLayoutType.downward:
199
+ inverseDirectionLayout = MindLayoutType.upward;
200
+ break;
201
+ case MindLayoutType.upward:
202
+ inverseDirectionLayout = MindLayoutType.downward;
203
+ break;
204
+ case MindLayoutType.rightBottomIndented:
205
+ inverseDirectionLayout = isHorizontal ? MindLayoutType.leftBottomIndented : MindLayoutType.rightTopIndented;
206
+ break;
207
+ case MindLayoutType.leftBottomIndented:
208
+ inverseDirectionLayout = isHorizontal ? MindLayoutType.rightBottomIndented : MindLayoutType.leftTopIndented;
209
+ break;
210
+ case MindLayoutType.rightTopIndented:
211
+ inverseDirectionLayout = isHorizontal ? MindLayoutType.leftTopIndented : MindLayoutType.rightBottomIndented;
212
+ break;
213
+ case MindLayoutType.leftTopIndented:
214
+ inverseDirectionLayout = isHorizontal ? MindLayoutType.rightTopIndented : MindLayoutType.leftBottomIndented;
215
+ break;
216
+ }
217
+ return inverseDirectionLayout;
218
+ };
219
+ const getLayoutDirection$1 = (root) => {
220
+ const layout = root.layout || getDefaultLayout();
221
+ return LayoutDirectionsMap[layout];
222
+ };
223
+ const getDefaultLayout = () => {
224
+ return MindLayoutType.standard;
225
+ };
226
+ const getAvailableSubLayoutsByLayoutDirections = (directions) => {
227
+ const result = [];
228
+ const reverseDirections = directions.map(getLayoutReverseDirection);
229
+ for (const key in MindLayoutType) {
230
+ const layout = MindLayoutType[key];
231
+ const layoutDirections = LayoutDirectionsMap[layout];
232
+ if (layoutDirections) {
233
+ const hasSameDirection = layoutDirections.some(d => directions.includes(d));
234
+ const hasReverseDirection = layoutDirections.some(r => reverseDirections.includes(r));
235
+ if (hasSameDirection && !hasReverseDirection) {
236
+ result.push(layout);
237
+ }
168
238
  }
169
- parent = findParentElement(parent);
170
239
  }
171
- return getDefaultLayout();
240
+ return result;
241
+ };
242
+ const getLayoutReverseDirection = (layoutDirection) => {
243
+ let reverseDirection = LayoutDirection.right;
244
+ switch (layoutDirection) {
245
+ case LayoutDirection.top:
246
+ reverseDirection = LayoutDirection.bottom;
247
+ break;
248
+ case LayoutDirection.bottom:
249
+ reverseDirection = LayoutDirection.top;
250
+ break;
251
+ case LayoutDirection.right:
252
+ reverseDirection = LayoutDirection.left;
253
+ break;
254
+ case LayoutDirection.left:
255
+ reverseDirection = LayoutDirection.right;
256
+ break;
257
+ }
258
+ return reverseDirection;
259
+ };
260
+ const getRootLayout = (root) => {
261
+ return root.layout || getDefaultLayout();
172
262
  };
173
263
 
174
264
  const getLayoutByElement = (element) => {
@@ -176,17 +266,18 @@ const getLayoutByElement = (element) => {
176
266
  if (layout) {
177
267
  return layout;
178
268
  }
179
- if (AbstractNode.isAbstract(element) ||
180
- (isChildOfAbstract(MindElement.getNode(element)) && isIndentedLayout(layout))) {
181
- const parentLayout = getLayoutParentByElement(element);
182
- return getAbstractLayout(parentLayout);
269
+ const parent = !PlaitMind.isMind(element) && MindElement.getParent(element);
270
+ if (AbstractNode.isAbstract(element) && parent) {
271
+ return getAbstractLayout(getLayoutByElement(parent));
272
+ }
273
+ if (parent) {
274
+ return getLayoutByElement(parent);
183
275
  }
184
- return getLayoutParentByElement(element);
276
+ return getDefaultLayout();
185
277
  };
186
278
 
187
279
  const MindQueries = {
188
280
  getAvailableSubLayoutsByElement,
189
- getLayoutParentByElement,
190
281
  getBranchLayouts,
191
282
  getLayoutByElement,
192
283
  getCorrectLayoutByElement
@@ -223,6 +314,13 @@ const MindElement = {
223
314
  const parent = NODE_TO_PARENT.get(node);
224
315
  return parent;
225
316
  },
317
+ findParent(node) {
318
+ if (PlaitMind.isMind(node)) {
319
+ return undefined;
320
+ }
321
+ const parent = NODE_TO_PARENT.get(node);
322
+ return parent;
323
+ },
226
324
  getRoot(board, element) {
227
325
  const path = PlaitBoard.findPath(board, element);
228
326
  return PlaitNode.get(board, path.slice(0, 1));
@@ -245,6 +343,13 @@ const MindElement = {
245
343
  }
246
344
  return node;
247
345
  },
346
+ findParentNode(element) {
347
+ if (PlaitMind.isMind(element)) {
348
+ return undefined;
349
+ }
350
+ const parent = MindElement.getParent(element);
351
+ return MindElement.getNode(parent);
352
+ },
248
353
  hasEmojis(element) {
249
354
  if (element.data.emojis) {
250
355
  return true;
@@ -257,6 +362,11 @@ const MindElement = {
257
362
  return element.data.emojis;
258
363
  }
259
364
  };
365
+ var MindElementShape;
366
+ (function (MindElementShape) {
367
+ MindElementShape["roundRectangle"] = "round-rectangle";
368
+ MindElementShape["underline"] = "underline";
369
+ })(MindElementShape || (MindElementShape = {}));
260
370
 
261
371
  const MindNode = {
262
372
  get(root, path) {
@@ -269,15 +379,6 @@ const MindNode = {
269
379
  node = node.children[p];
270
380
  }
271
381
  return node;
272
- },
273
- isEquals(node, otherNode) {
274
- const hasSameSize = node.x === otherNode.x && node.y === otherNode.y && node.width === otherNode.width && node.height === otherNode.height;
275
- const hasSameOrigin = node.origin === otherNode.origin;
276
- let hasSameParentOriginChildren = false;
277
- if (node.parent && otherNode.parent) {
278
- hasSameParentOriginChildren = node.parent.origin.children == otherNode.parent.origin.children;
279
- }
280
- return hasSameSize && hasSameOrigin && hasSameParentOriginChildren;
281
382
  }
282
383
  };
283
384
 
@@ -311,109 +412,6 @@ var AbstractResizeState;
311
412
  AbstractResizeState["end"] = "end";
312
413
  })(AbstractResizeState || (AbstractResizeState = {}));
313
414
 
314
- const getBranchDirectionsByLayouts = (branchLayouts) => {
315
- const branchDirections = [];
316
- branchLayouts.forEach(l => {
317
- const directions = LayoutDirectionsMap[l];
318
- directions.forEach(d => {
319
- if (!branchDirections.includes(d) && !branchDirections.includes(getLayoutReverseDirection(d))) {
320
- branchDirections.push(d);
321
- }
322
- });
323
- });
324
- return branchDirections;
325
- };
326
- const isCorrectLayout = (root, layout) => {
327
- const rootLayout = root.layout || getDefaultLayout();
328
- return !getInCorrectLayoutDirection(rootLayout, layout);
329
- };
330
- const isMixedLayout = (parentLayout, layout) => {
331
- return (!isIndentedLayout(parentLayout) && isIndentedLayout(layout)) || (isIndentedLayout(parentLayout) && !isIndentedLayout(layout));
332
- };
333
- const getInCorrectLayoutDirection = (rootLayout, layout) => {
334
- const directions = LayoutDirectionsMap[rootLayout];
335
- const subLayoutDirections = LayoutDirectionsMap[layout];
336
- if (!subLayoutDirections) {
337
- throw new Error(`unexpected layout: ${layout} on correct layout`);
338
- }
339
- return subLayoutDirections.find(d => directions.includes(getLayoutReverseDirection(d)));
340
- };
341
- const correctLayoutByDirection = (layout, direction) => {
342
- const isHorizontal = direction === LayoutDirection.left || direction === LayoutDirection.right ? true : false;
343
- let inverseDirectionLayout = MindLayoutType.standard;
344
- switch (layout) {
345
- case MindLayoutType.left:
346
- inverseDirectionLayout = MindLayoutType.right;
347
- break;
348
- case MindLayoutType.right:
349
- inverseDirectionLayout = MindLayoutType.left;
350
- break;
351
- case MindLayoutType.downward:
352
- inverseDirectionLayout = MindLayoutType.upward;
353
- break;
354
- case MindLayoutType.upward:
355
- inverseDirectionLayout = MindLayoutType.downward;
356
- break;
357
- case MindLayoutType.rightBottomIndented:
358
- inverseDirectionLayout = isHorizontal ? MindLayoutType.leftBottomIndented : MindLayoutType.rightTopIndented;
359
- break;
360
- case MindLayoutType.leftBottomIndented:
361
- inverseDirectionLayout = isHorizontal ? MindLayoutType.rightBottomIndented : MindLayoutType.leftTopIndented;
362
- break;
363
- case MindLayoutType.rightTopIndented:
364
- inverseDirectionLayout = isHorizontal ? MindLayoutType.leftTopIndented : MindLayoutType.rightBottomIndented;
365
- break;
366
- case MindLayoutType.leftTopIndented:
367
- inverseDirectionLayout = isHorizontal ? MindLayoutType.rightTopIndented : MindLayoutType.leftBottomIndented;
368
- break;
369
- }
370
- return inverseDirectionLayout;
371
- };
372
- const getLayoutDirection$1 = (root) => {
373
- const layout = root.layout || getDefaultLayout();
374
- return LayoutDirectionsMap[layout];
375
- };
376
- const getDefaultLayout = () => {
377
- return MindLayoutType.standard;
378
- };
379
- const getAvailableSubLayoutsByLayoutDirections = (directions) => {
380
- const result = [];
381
- const reverseDirections = directions.map(getLayoutReverseDirection);
382
- for (const key in MindLayoutType) {
383
- const layout = MindLayoutType[key];
384
- const layoutDirections = LayoutDirectionsMap[layout];
385
- if (layoutDirections) {
386
- const hasSameDirection = layoutDirections.some(d => directions.includes(d));
387
- const hasReverseDirection = layoutDirections.some(r => reverseDirections.includes(r));
388
- if (hasSameDirection && !hasReverseDirection) {
389
- result.push(layout);
390
- }
391
- }
392
- }
393
- return result;
394
- };
395
- const getLayoutReverseDirection = (layoutDirection) => {
396
- let reverseDirection = LayoutDirection.right;
397
- switch (layoutDirection) {
398
- case LayoutDirection.top:
399
- reverseDirection = LayoutDirection.bottom;
400
- break;
401
- case LayoutDirection.bottom:
402
- reverseDirection = LayoutDirection.top;
403
- break;
404
- case LayoutDirection.right:
405
- reverseDirection = LayoutDirection.left;
406
- break;
407
- case LayoutDirection.left:
408
- reverseDirection = LayoutDirection.right;
409
- break;
410
- }
411
- return reverseDirection;
412
- };
413
- const getRootLayout = (root) => {
414
- return root.layout || getDefaultLayout();
415
- };
416
-
417
415
  function enterNodeEditing(element) {
418
416
  const component = ELEMENT_TO_COMPONENT.get(element);
419
417
  component.startEditText(false, false);
@@ -443,13 +441,137 @@ const separateChildren = (parentElement) => {
443
441
  return { leftChildren, rightChildren };
444
442
  };
445
443
  const isSetAbstract = (element) => {
446
- return !!getCorrespondingAbstract(element);
444
+ const parent = MindElement.getParent(element);
445
+ return !!getCorrespondingAbstract(parent, element);
447
446
  };
448
447
  const canSetAbstract = (element) => {
449
448
  return !PlaitElement.isRootElement(element) && !AbstractNode.isAbstract(element) && !isSetAbstract(element);
450
449
  };
451
- const setAbstract = (board, elements) => {
452
- let elementGroup = filterChildElement(elements);
450
+ const getCorrespondingAbstract = (parent, element) => {
451
+ if (!parent)
452
+ return undefined;
453
+ const elementIndex = parent.children.indexOf(element);
454
+ return parent.children.find(child => {
455
+ return AbstractNode.isAbstract(child) && elementIndex >= child.start && elementIndex <= child.end;
456
+ });
457
+ };
458
+ const getBehindAbstracts = (parent, element) => {
459
+ const index = parent.children.indexOf(element);
460
+ return parent.children.filter(child => AbstractNode.isAbstract(child) && child.start > index);
461
+ };
462
+ const getOverallAbstracts = (board, elements) => {
463
+ const overallAbstracts = [];
464
+ elements
465
+ .filter(value => !AbstractNode.isAbstract(value) && !PlaitMind.isMind(value))
466
+ .forEach(value => {
467
+ const parent = MindElement.getParent(value);
468
+ const abstract = getCorrespondingAbstract(parent, value);
469
+ if (abstract && overallAbstracts.indexOf(abstract) === -1) {
470
+ const { start, end } = abstract;
471
+ const parent = MindElement.getParent(value);
472
+ const isOverall = parent.children.slice(start, end + 1).every(includedElement => elements.indexOf(includedElement) > -1);
473
+ if (isOverall) {
474
+ overallAbstracts.push(abstract);
475
+ }
476
+ }
477
+ });
478
+ return overallAbstracts;
479
+ };
480
+ const insertElementHandleAbstract = (board, path,
481
+ //由此区分拖拽和新增到概要概括最后一个节点
482
+ isExtendPreviousNode = true, abstractRefs = new Map()) => {
483
+ const parent = PlaitNode.parent(board, path);
484
+ const hasPreviousNode = path[path.length - 1] !== 0;
485
+ let behindAbstracts;
486
+ if (!hasPreviousNode) {
487
+ behindAbstracts = parent.children.filter(child => AbstractNode.isAbstract(child));
488
+ }
489
+ else {
490
+ const selectedElement = PlaitNode.get(board, Path.previous(path));
491
+ behindAbstracts = getBehindAbstracts(parent, selectedElement);
492
+ }
493
+ if (behindAbstracts.length) {
494
+ behindAbstracts.forEach(abstract => {
495
+ let newProperties = abstractRefs.get(abstract);
496
+ if (!newProperties) {
497
+ newProperties = { start: 0, end: 0 };
498
+ abstractRefs.set(abstract, newProperties);
499
+ }
500
+ newProperties.start = newProperties.start + 1;
501
+ newProperties.end = newProperties.end + 1;
502
+ });
503
+ }
504
+ if (!hasPreviousNode) {
505
+ return abstractRefs;
506
+ }
507
+ const selectedElement = PlaitNode.get(board, Path.previous(path));
508
+ const correspondingAbstract = getCorrespondingAbstract(parent, selectedElement);
509
+ const isDragToLast = !isExtendPreviousNode && correspondingAbstract && correspondingAbstract.end === path[path.length - 1] - 1;
510
+ if (correspondingAbstract && !isDragToLast) {
511
+ let newProperties = abstractRefs.get(correspondingAbstract);
512
+ if (!newProperties) {
513
+ newProperties = { start: 0, end: 0 };
514
+ abstractRefs.set(correspondingAbstract, newProperties);
515
+ }
516
+ newProperties.end = newProperties.end + 1;
517
+ }
518
+ return abstractRefs;
519
+ };
520
+ const deleteElementHandleAbstract = (board, deletableElements, abstractRefs = new Map()) => {
521
+ deletableElements.forEach(node => {
522
+ if (!PlaitMind.isMind(node)) {
523
+ const parent = PlaitNode.parent(board, PlaitBoard.findPath(board, node));
524
+ const behindAbstracts = getBehindAbstracts(parent, node).filter(abstract => !deletableElements.includes(abstract));
525
+ if (behindAbstracts.length) {
526
+ behindAbstracts.forEach(abstract => {
527
+ let newProperties = abstractRefs.get(abstract);
528
+ if (!newProperties) {
529
+ newProperties = { start: 0, end: 0 };
530
+ abstractRefs.set(abstract, newProperties);
531
+ }
532
+ newProperties.start = newProperties.start - 1;
533
+ newProperties.end = newProperties.end - 1;
534
+ });
535
+ }
536
+ const correspondingAbstract = getCorrespondingAbstract(parent, node);
537
+ if (correspondingAbstract && !deletableElements.includes(correspondingAbstract)) {
538
+ let newProperties = abstractRefs.get(correspondingAbstract);
539
+ if (!newProperties) {
540
+ newProperties = { start: 0, end: 0 };
541
+ abstractRefs.set(correspondingAbstract, newProperties);
542
+ }
543
+ newProperties.end = newProperties.end - 1;
544
+ }
545
+ }
546
+ });
547
+ return abstractRefs;
548
+ };
549
+
550
+ const setAbstractsByRefs = (board, abstractRefs) => {
551
+ abstractRefs.forEach((newProperty, element) => {
552
+ const start = element.start + newProperty.start;
553
+ const end = element.end + newProperty.end;
554
+ const path = PlaitBoard.findPath(board, element);
555
+ if (start > end) {
556
+ Transforms.removeNode(board, path);
557
+ }
558
+ else {
559
+ Transforms.setNode(board, { start, end }, path);
560
+ }
561
+ });
562
+ };
563
+ const setAbstractByStandardLayout = (board, element) => {
564
+ const rightNodeCount = element.rightNodeCount;
565
+ const abstract = element.children.find(child => {
566
+ return AbstractNode.isAbstract(child) && child.end >= rightNodeCount && child.start < rightNodeCount;
567
+ });
568
+ if (abstract) {
569
+ const path = PlaitBoard.findPath(board, abstract);
570
+ Transforms.setNode(board, { end: rightNodeCount - 1 }, path);
571
+ }
572
+ };
573
+ const insertAbstract = (board, elements) => {
574
+ let elementGroup = getFirstLevelElement(elements);
453
575
  const { parentElements, abstractIncludedGroups } = divideElementByParent(elementGroup);
454
576
  abstractIncludedGroups.forEach((group, index) => {
455
577
  const groupParent = parentElements[index];
@@ -476,71 +598,119 @@ const setAbstractByElements = (board, groupParent, group) => {
476
598
  insertAbstractNode(board, path, start, end);
477
599
  }
478
600
  };
479
- const insertAbstractNode = (board, path, start, end) => {
480
- const mindElement = createMindElement('概要', 28, 20, {
481
- strokeColor: GRAY_COLOR,
482
- branchColor: GRAY_COLOR
483
- });
484
- mindElement.start = start;
485
- mindElement.end = end;
486
- Transforms.insertNode(board, mindElement, path);
487
- };
488
- const handleAbstractIncluded = (board, element) => {
489
- const rightNodeCount = element.rightNodeCount;
490
- const abstract = element.children.find(child => {
491
- return AbstractNode.isAbstract(child) && child.end >= rightNodeCount && child.start < rightNodeCount;
492
- });
493
- if (abstract) {
494
- const path = PlaitBoard.findPath(board, abstract);
495
- Transforms.setNode(board, { end: rightNodeCount - 1 }, path);
601
+ const insertAbstractNode = (board, path, start, end) => {
602
+ const mindElement = createMindElement('概要', 28, 20, {
603
+ strokeColor: DefaultAbstractNodeStyle.strokeColor,
604
+ strokeWidth: DefaultAbstractNodeStyle.branchWidth,
605
+ branchColor: DefaultAbstractNodeStyle.branchColor,
606
+ branchWidth: DefaultAbstractNodeStyle.branchWidth
607
+ });
608
+ mindElement.start = start;
609
+ mindElement.end = end;
610
+ Transforms.insertNode(board, mindElement, path);
611
+ };
612
+
613
+ const setLayout = (board, layout, path) => {
614
+ correctLogicLayoutNode(board, layout, path);
615
+ const element = PlaitNode.get(board, path);
616
+ if (PlaitMind.isMind(element) && isStandardLayout(layout)) {
617
+ MindTransforms.setAbstractByStandardLayout(board, element);
618
+ }
619
+ Transforms.setNode(board, { layout }, path);
620
+ };
621
+ const correctLogicLayoutNode = (board, layout, path) => {
622
+ const node = PlaitNode.get(board, path);
623
+ if (node && layout) {
624
+ node.children?.forEach((value, index) => {
625
+ if (value.layout) {
626
+ if ((isHorizontalLogicLayout(layout) && isVerticalLogicLayout(value.layout)) ||
627
+ (isVerticalLogicLayout(layout) && isHorizontalLogicLayout(value.layout))) {
628
+ Transforms.setNode(board, { layout: null }, [...path, index]);
629
+ }
630
+ if (value.children?.length) {
631
+ correctLogicLayoutNode(board, layout, [...path, index]);
632
+ }
633
+ }
634
+ });
635
+ }
636
+ };
637
+
638
+ const setTopic = (board, element, topic, width, height) => {
639
+ const newElement = {
640
+ data: { topic },
641
+ width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
642
+ height: height / board.viewport.zoom
643
+ };
644
+ if (MindElement.hasEmojis(element)) {
645
+ newElement.data.emojis = element.data.emojis;
496
646
  }
647
+ const path = PlaitBoard.findPath(board, element);
648
+ Transforms.setNode(board, newElement, path);
497
649
  };
498
- const getCorrespondingAbstract = (element) => {
499
- const parent = MindElement.getParent(element);
500
- if (!parent)
501
- return undefined;
502
- const elementIndex = parent.children.indexOf(element);
503
- return parent.children.find(child => {
504
- return AbstractNode.isAbstract(child) && elementIndex >= child.start && elementIndex <= child.end;
505
- });
650
+ const setTopicSize = (board, element, width, height) => {
651
+ const newElement = {
652
+ width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
653
+ height: height / board.viewport.zoom
654
+ };
655
+ const path = PlaitBoard.findPath(board, element);
656
+ Transforms.setNode(board, newElement, path);
506
657
  };
507
- const getBehindAbstracts = (element) => {
508
- const parent = MindElement.getParent(element);
509
- const index = parent.children.indexOf(element);
510
- return parent.children.filter(child => AbstractNode.isAbstract(child) && child.start > index);
658
+ const addEmoji = (board, element, emojiItem) => {
659
+ const emojis = element.data.emojis || [];
660
+ const newEmojis = [...emojis];
661
+ newEmojis.push(emojiItem);
662
+ const newElement = {
663
+ data: { topic: element.data.topic, emojis: newEmojis }
664
+ };
665
+ const path = PlaitBoard.findPath(board, element);
666
+ Transforms.setNode(board, newElement, path);
511
667
  };
512
- const insertSiblingElementHandleAbstract = (board, selectedElement) => {
513
- const abstract = getCorrespondingAbstract(selectedElement);
514
- if (abstract) {
515
- PlaitBoard.findPath(board, abstract);
516
- Transforms.setNode(board, { end: abstract.end + 1 }, PlaitBoard.findPath(board, abstract));
517
- }
518
- const abstracts = getBehindAbstracts(selectedElement);
519
- if (abstracts.length) {
520
- moveAbstractPosition(board, abstracts, 1);
668
+ const removeEmoji = (board, element, emojiItem) => {
669
+ const emojis = element.data.emojis.filter(value => value !== emojiItem);
670
+ const newElement = {
671
+ data: { topic: element.data.topic }
672
+ };
673
+ if (emojis.length > 0) {
674
+ newElement.data.emojis = emojis;
521
675
  }
676
+ const path = PlaitBoard.findPath(board, element);
677
+ Transforms.setNode(board, newElement, path);
522
678
  };
523
- const moveAbstractPosition = (board, abstracts, step) => {
524
- abstracts.forEach(abstract => {
525
- Transforms.setNode(board, { start: abstract.start + step, end: abstract.end + step }, PlaitBoard.findPath(board, abstract));
679
+ const replaceEmoji = (board, element, oldEmoji, newEmoji) => {
680
+ const newElement = {
681
+ data: { topic: element.data.topic }
682
+ };
683
+ const newEmojis = element.data.emojis.map(value => {
684
+ if (value === oldEmoji) {
685
+ return newEmoji;
686
+ }
687
+ return value;
526
688
  });
689
+ newElement.data.emojis = newEmojis;
690
+ const path = PlaitBoard.findPath(board, element);
691
+ Transforms.setNode(board, newElement, path);
692
+ };
693
+
694
+ const MindTransforms = {
695
+ setLayout,
696
+ setTopic,
697
+ setTopicSize,
698
+ addEmoji,
699
+ removeEmoji,
700
+ replaceEmoji,
701
+ insertAbstract,
702
+ setAbstractsByRefs,
703
+ setAbstractByStandardLayout
527
704
  };
528
705
 
529
- function findParentElement(element) {
530
- const component = PlaitElement.getComponent(element);
531
- if (component && component.parent) {
532
- return component.parent.origin;
533
- }
534
- return undefined;
535
- }
536
706
  function findUpElement(element) {
537
707
  let branch;
538
708
  let root = element;
539
- let parent = findParentElement(element);
709
+ let parent = MindElement.findParent(element);
540
710
  while (parent) {
541
711
  branch = root;
542
712
  root = parent;
543
- parent = findParentElement(parent);
713
+ parent = MindElement.findParent(parent);
544
714
  }
545
715
  return { root, branch };
546
716
  }
@@ -551,16 +721,16 @@ const getChildrenCount = (element) => {
551
721
  return count + element.children.length;
552
722
  };
553
723
  const isChildElement = (origin, child) => {
554
- let parent = findParentElement(child);
724
+ let parent = MindElement.findParent(child);
555
725
  while (parent) {
556
726
  if (parent === origin) {
557
727
  return true;
558
728
  }
559
- parent = findParentElement(parent);
729
+ parent = MindElement.findParent(parent);
560
730
  }
561
731
  return false;
562
732
  };
563
- const filterChildElement = (elements) => {
733
+ const getFirstLevelElement = (elements) => {
564
734
  let result = [];
565
735
  elements.forEach(element => {
566
736
  const isChild = elements.some(node => {
@@ -650,7 +820,7 @@ const changeRightNodeCount = (board, parentPath, changeNumber) => {
650
820
  }, parentPath);
651
821
  };
652
822
  const shouldChangeRightNodeCount = (selectedElement) => {
653
- const parentElement = findParentElement(selectedElement);
823
+ const parentElement = MindElement.findParent(selectedElement);
654
824
  if (parentElement) {
655
825
  const nodeIndex = parentElement.children.findIndex(item => item.id === selectedElement.id);
656
826
  if (parentElement.isRoot &&
@@ -663,13 +833,13 @@ const shouldChangeRightNodeCount = (selectedElement) => {
663
833
  return false;
664
834
  };
665
835
  const createDefaultMindMapElement = (point, rightNodeCount, layout) => {
666
- const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle, layout });
836
+ const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindElementShape.roundRectangle, layout });
667
837
  root.rightNodeCount = rightNodeCount;
668
838
  root.isRoot = true;
669
839
  root.type = 'mindmap';
670
840
  root.points = [point];
671
841
  const children = [1, 1, 1].map(() => {
672
- return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle });
842
+ return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindElementShape.roundRectangle });
673
843
  });
674
844
  root.children = children;
675
845
  return root;
@@ -706,11 +876,14 @@ const createMindElement = (text, width, height, options) => {
706
876
  if (options.branchColor) {
707
877
  newElement.branchColor = options.branchColor;
708
878
  }
879
+ if (!isNullOrUndefined(options.branchWidth)) {
880
+ newElement.branchWidth = options.branchWidth;
881
+ }
709
882
  return newElement;
710
883
  };
711
884
  // layoutLevel 用来表示插入兄弟节点还是子节点
712
885
  const insertMindElement = (board, inheritNode, path) => {
713
- let fill, strokeColor, strokeWidth, shape = MindNodeShape.roundRectangle;
886
+ let fill, strokeColor, strokeWidth, shape = MindElementShape.roundRectangle;
714
887
  if (!inheritNode.isRoot) {
715
888
  fill = inheritNode.fill;
716
889
  strokeColor = inheritNode.strokeColor;
@@ -733,63 +906,23 @@ const findLastChild = (child) => {
733
906
  return result;
734
907
  };
735
908
  const deleteSelectedELements = (board, selectedElements) => {
909
+ const deletableElements = getFirstLevelElement(selectedElements).reverse();
910
+ const abstractRefs = deleteElementHandleAbstract(board, deletableElements);
911
+ MindTransforms.setAbstractsByRefs(board, abstractRefs);
736
912
  //翻转,从下到上修改,防止找不到 path
737
- const deletableElements = filterChildElement(selectedElements).reverse();
738
- const relativeAbstracts = [];
739
- const accumulativeProperties = new WeakMap();
740
- deletableElements.forEach(node => {
741
- if (!PlaitMind.isMind(node)) {
742
- const behindAbstracts = getBehindAbstracts(node).filter(abstract => !deletableElements.includes(abstract));
743
- if (behindAbstracts.length) {
744
- behindAbstracts.forEach(abstract => {
745
- let newProperties = accumulativeProperties.get(abstract);
746
- if (!newProperties) {
747
- newProperties = { start: abstract.start, end: abstract.end };
748
- accumulativeProperties.set(abstract, newProperties);
749
- relativeAbstracts.push(abstract);
750
- }
751
- newProperties.start = newProperties.start - 1;
752
- newProperties.end = newProperties.end - 1;
753
- });
754
- }
755
- const correspondingAbstract = getCorrespondingAbstract(node);
756
- if (correspondingAbstract && !deletableElements.includes(correspondingAbstract)) {
757
- let newProperties = accumulativeProperties.get(correspondingAbstract);
758
- if (!newProperties) {
759
- newProperties = { start: correspondingAbstract.start, end: correspondingAbstract.end };
760
- accumulativeProperties.set(correspondingAbstract, newProperties);
761
- relativeAbstracts.push(correspondingAbstract);
762
- }
763
- newProperties.end = newProperties.end - 1;
764
- }
765
- }
766
- });
767
- const abstractHandles = relativeAbstracts.map(value => {
768
- const newProperties = accumulativeProperties.get(value);
769
- if (newProperties) {
770
- const path = PlaitBoard.findPath(board, value);
771
- return () => {
772
- if (newProperties.start > newProperties.end) {
773
- Transforms.removeNode(board, path);
774
- }
775
- else {
776
- Transforms.setNode(board, newProperties, path);
777
- }
778
- };
779
- }
780
- return () => { };
781
- });
782
- const deletableHandles = deletableElements.map(node => {
783
- const path = PlaitBoard.findPath(board, node);
913
+ deletableElements
914
+ .map(element => {
915
+ const path = PlaitBoard.findPath(board, element);
784
916
  return () => {
785
- if (shouldChangeRightNodeCount(node)) {
786
- changeRightNodeCount(board, path.slice(0, path.length - 1), -1);
917
+ if (shouldChangeRightNodeCount(element)) {
918
+ changeRightNodeCount(board, path.slice(0, 1), -1);
787
919
  }
788
920
  Transforms.removeNode(board, path);
789
921
  };
922
+ })
923
+ .forEach(action => {
924
+ action();
790
925
  });
791
- abstractHandles.forEach(action => action());
792
- deletableHandles.forEach(action => action());
793
926
  };
794
927
  const divideElementByParent = (elements) => {
795
928
  const abstractIncludedGroups = [];
@@ -808,65 +941,70 @@ const divideElementByParent = (elements) => {
808
941
  return { parentElements, abstractIncludedGroups };
809
942
  };
810
943
 
811
- const getNodeShapeByElement = (element) => {
812
- let nodeShape = element.shape;
813
- if (nodeShape) {
814
- return nodeShape;
944
+ const getAvailableProperty = (board, element, propertyKey) => {
945
+ const ancestors = MindElement.getAncestors(board, element);
946
+ ancestors.unshift(element);
947
+ const ancestor = ancestors.find(value => value[propertyKey]);
948
+ if (ancestor) {
949
+ return ancestor[propertyKey];
815
950
  }
816
- let parent = findParentElement(element);
817
- while (parent) {
818
- if (parent.shape) {
819
- return parent.shape;
820
- }
821
- parent = findParentElement(parent);
951
+ else {
952
+ return undefined;
822
953
  }
823
- return MindNodeShape.roundRectangle;
824
954
  };
825
955
 
956
+ /**
957
+ * Processing of branch color, width, style, etc. of the mind node
958
+ */
826
959
  const getBranchColorByMindElement = (board, element) => {
827
- const ancestors = MindElement.getAncestors(board, element);
828
- ancestors.unshift(element);
829
- const ancestor = ancestors.find(value => value.branchColor);
830
- if (ancestor && ancestor.branchColor) {
831
- return ancestor.branchColor;
832
- }
833
- const root = ancestors[ancestors.length - 1];
834
- const branch = ancestors[ancestors.length - 2];
835
- if (branch) {
836
- const index = root.children.indexOf(branch);
837
- const length = COLORS.length;
838
- const remainder = index % length;
839
- return COLORS[remainder];
960
+ const branchColor = getAvailableProperty(board, element, 'branchColor');
961
+ return branchColor || getDefaultBranchColor(board, element);
962
+ };
963
+ const getBranchWidthByMindElement = (board, element) => {
964
+ const branchWidth = getAvailableProperty(board, element, 'branchWidth');
965
+ return branchWidth || BRANCH_WIDTH;
966
+ };
967
+ const getAbstractBranchWidth = (board, element) => {
968
+ if (!isNullOrUndefined(element.branchWidth)) {
969
+ return element.branchWidth;
840
970
  }
841
- else {
842
- throw new Error('root element should not have branch color');
971
+ return DefaultAbstractNodeStyle.branchWidth;
972
+ };
973
+ const getAbstractBranchColor = (board, element) => {
974
+ if (element.branchColor) {
975
+ return element.branchColor;
843
976
  }
977
+ return DefaultAbstractNodeStyle.branchColor;
844
978
  };
845
979
  const getNextBranchColor = (root) => {
846
980
  const index = root.children.length;
847
- const length = COLORS.length;
981
+ return getDefaultBranchColorByIndex(index);
982
+ };
983
+ const getDefaultBranchColor = (board, element) => {
984
+ const path = PlaitBoard.findPath(board, element);
985
+ return getDefaultBranchColorByIndex(path[1]);
986
+ };
987
+ const getDefaultBranchColorByIndex = (index) => {
988
+ const length = BRANCH_COLORS.length;
848
989
  const remainder = index % length;
849
- return COLORS[remainder];
990
+ return BRANCH_COLORS[remainder];
850
991
  };
851
992
 
852
993
  const getStrokeByMindElement = (board, element) => {
994
+ if (PlaitMind.isMind(element)) {
995
+ return element.strokeColor || DefaultRootStyle.strokeColor;
996
+ }
853
997
  const ancestors = MindElement.getAncestors(board, element);
854
998
  ancestors.unshift(element);
855
999
  const ancestor = ancestors.find(value => value.strokeColor);
856
- if (ancestor && ancestor.strokeColor) {
1000
+ if (ancestor && ancestor.strokeColor && !PlaitMind.isMind(ancestor)) {
857
1001
  return ancestor.strokeColor;
858
1002
  }
859
- const root = ancestors[ancestors.length - 1];
860
- const branch = ancestors[ancestors.length - 2];
861
- if (branch) {
862
- const index = root.children.indexOf(branch);
863
- const length = COLORS.length;
864
- const remainder = index % length;
865
- return COLORS[remainder];
866
- }
867
- else {
868
- return ROOT_NODE_STROKE;
869
- }
1003
+ return getDefaultBranchColor(board, element);
1004
+ };
1005
+ const getShapeByElement = (board, element) => {
1006
+ const shape = getAvailableProperty(board, element, 'shape');
1007
+ return shape || MindElementShape.roundRectangle;
870
1008
  };
871
1009
 
872
1010
  function isVirtualKey(e) {
@@ -883,7 +1021,7 @@ function isVirtualKey(e) {
883
1021
 
884
1022
  function drawLink(board, node, child, defaultStroke = null, isHorizontal = true, needDrawUnderline = true) {
885
1023
  let beginX, beginY, endX, endY, beginNode = node, endNode = child;
886
- const layout = MindQueries.getCorrectLayoutByElement(node.origin);
1024
+ const layout = MindQueries.getCorrectLayoutByElement(board, node.origin);
887
1025
  if (isHorizontal) {
888
1026
  if (!isChildRight(node, child)) {
889
1027
  beginNode = child;
@@ -895,7 +1033,7 @@ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true,
895
1033
  endY = endNode.y + endNode.height / 2;
896
1034
  if (node.parent &&
897
1035
  isIndentedLayout(MindQueries.getLayoutByElement(node.parent?.origin)) &&
898
- getNodeShapeByElement(node.origin) === MindNodeShape.underline) {
1036
+ getShapeByElement(board, node.origin) === MindElementShape.underline) {
899
1037
  if (isChildRight(node, child)) {
900
1038
  beginY = node.y + node.height - node.vGap;
901
1039
  }
@@ -939,7 +1077,7 @@ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true,
939
1077
  [endX - (beginNode.hGap + endNode.hGap) / 2, endY],
940
1078
  [endX, endY]
941
1079
  ];
942
- const shape = getNodeShapeByElement(child.origin);
1080
+ const shape = getShapeByElement(board, child.origin);
943
1081
  if (!node.origin.isRoot) {
944
1082
  if (node.x > child.x) {
945
1083
  curve = [
@@ -970,7 +1108,7 @@ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true,
970
1108
  curve = [...line, ...curve];
971
1109
  }
972
1110
  }
973
- if (needDrawUnderline && shape === MindNodeShape.underline) {
1111
+ if (needDrawUnderline && shape === MindElementShape.underline) {
974
1112
  if (child.left) {
975
1113
  const underline = [
976
1114
  [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
@@ -1039,15 +1177,16 @@ const drawPlaceholderDropNodeG = (board, dropTarget, fakeDropNodeG) => {
1039
1177
  if (dropTarget.detectResult && ['right', 'left'].includes(dropTarget.detectResult)) {
1040
1178
  drawStraightDropNodeG(board, targetRect, dropTarget.detectResult, targetComponent, fakeDropNodeG);
1041
1179
  }
1042
- if (targetComponent.parent && dropTarget.detectResult && ['top', 'bottom'].includes(dropTarget.detectResult)) {
1043
- const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1180
+ const targetParent = MindElement.findParent(targetComponent.element);
1181
+ if (targetParent && dropTarget.detectResult && ['top', 'bottom'].includes(dropTarget.detectResult)) {
1182
+ const parentComponent = PlaitElement.getComponent(targetParent);
1044
1183
  const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
1045
1184
  drawCurvePlaceholderDropNodeG(board, targetRect, dropTarget.detectResult, targetIndex, targetComponent, parentComponent, fakeDropNodeG);
1046
1185
  }
1047
1186
  };
1048
1187
  const drawCurvePlaceholderDropNodeG = (board, targetRect, detectResult, targetIndex, targetComponent, parentComponent, fakeDropNodeG) => {
1049
- const parentNodeLayout = MindQueries.getCorrectLayoutByElement(parentComponent.node.origin);
1050
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.parent.origin);
1188
+ const parentNodeLayout = MindQueries.getCorrectLayoutByElement(board, parentComponent.node.origin);
1189
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.parent.origin);
1051
1190
  const strokeWidth = targetComponent.node.origin.branchWidth ? targetComponent.node.origin.branchWidth : STROKE_WIDTH;
1052
1191
  let fakeX = targetComponent.node.x, fakeY = targetRect.y - 30, fakeRectangleStartX = targetRect.x, fakeRectangleEndX = targetRect.x + 30, fakeRectangleStartY = fakeY, fakeRectangleEndY = fakeRectangleStartY + 12, width = 30;
1053
1192
  if (isLeftLayout(layout)) {
@@ -1082,7 +1221,7 @@ const drawCurvePlaceholderDropNodeG = (board, targetRect, detectResult, targetIn
1082
1221
  }
1083
1222
  if (isVerticalLogicLayout(layout)) {
1084
1223
  parentComponent = targetComponent;
1085
- targetComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1224
+ targetComponent = PlaitElement.getComponent(MindElement.getParent(targetComponent.element));
1086
1225
  fakeX = parentComponent.node.x;
1087
1226
  width = parentComponent.node.width;
1088
1227
  const vGap = BASE * 6 + strokeWidth;
@@ -1178,8 +1317,8 @@ const drawStraightDropNodeG = (board, targetRect, detectResult, targetComponent,
1178
1317
  height,
1179
1318
  strokeWidth
1180
1319
  };
1181
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1182
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1320
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1321
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1183
1322
  if (!isMixedLayout(parentLayout, layout)) {
1184
1323
  // 构造一条直线
1185
1324
  let linePoints = [
@@ -1203,7 +1342,7 @@ const drawStraightDropNodeG = (board, targetRect, detectResult, targetComponent,
1203
1342
  * b. 最后一个节点的右侧:固定值(来源于 getMainAxle,第二级节点:BASE * 8,其他 BASE * 3 + strokeWidth / 2);
1204
1343
  */
1205
1344
  fakeY = targetComponent.node.y;
1206
- const parentComponent = PlaitElement.getComponent(targetComponent.parent.origin);
1345
+ const parentComponent = PlaitElement.getComponent(MindElement.getParent(targetComponent.element));
1207
1346
  const targetIndex = parentComponent.node.origin.children.indexOf(targetComponent.node.origin);
1208
1347
  if (detectResult === 'left') {
1209
1348
  let offsetX = 0;
@@ -1389,29 +1528,29 @@ const directionDetector = (targetNode, centerPoint) => {
1389
1528
  return null;
1390
1529
  };
1391
1530
 
1392
- const directionCorrector = (node, detectResults) => {
1393
- if (!node.origin.isRoot) {
1394
- const parentlayout = MindQueries.getCorrectLayoutByElement(node?.parent.origin);
1395
- if (isStandardLayout(parentlayout)) {
1531
+ const directionCorrector = (board, node, detectResults) => {
1532
+ if (!node.origin.isRoot && !AbstractNode.isAbstract(node.origin)) {
1533
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, node?.parent.origin);
1534
+ if (isStandardLayout(parentLayout)) {
1396
1535
  const idx = node.parent.children.findIndex(x => x === node);
1397
1536
  const isLeft = idx >= (node.parent.origin.rightNodeCount || 0);
1398
1537
  return getAllowedDirection(detectResults, [isLeft ? 'right' : 'left']);
1399
1538
  }
1400
- if (isLeftLayout(parentlayout)) {
1539
+ if (isLeftLayout(parentLayout)) {
1401
1540
  return getAllowedDirection(detectResults, ['right']);
1402
1541
  }
1403
- if (isRightLayout(parentlayout)) {
1542
+ if (isRightLayout(parentLayout)) {
1404
1543
  return getAllowedDirection(detectResults, ['left']);
1405
1544
  }
1406
- if (parentlayout === MindLayoutType.upward) {
1545
+ if (parentLayout === MindLayoutType.upward) {
1407
1546
  return getAllowedDirection(detectResults, ['bottom']);
1408
1547
  }
1409
- if (parentlayout === MindLayoutType.downward) {
1548
+ if (parentLayout === MindLayoutType.downward) {
1410
1549
  return getAllowedDirection(detectResults, ['top']);
1411
1550
  }
1412
1551
  }
1413
1552
  else {
1414
- const layout = MindQueries.getCorrectLayoutByElement(node?.origin);
1553
+ const layout = MindQueries.getCorrectLayoutByElement(board, node?.origin);
1415
1554
  if (isStandardLayout(layout)) {
1416
1555
  return getAllowedDirection(detectResults, ['top', 'bottom']);
1417
1556
  }
@@ -1442,17 +1581,18 @@ const getAllowedDirection = (detectResults, illegalDirections) => {
1442
1581
  };
1443
1582
 
1444
1583
  /* 根据布局调整 target 以及 direction */
1445
- const readjustmentDropTarget = (dropTarget) => {
1584
+ const readjustmentDropTarget = (board, dropTarget) => {
1446
1585
  const { target, detectResult } = dropTarget;
1447
1586
  const newDropTarget = { target, detectResult };
1448
1587
  const targetComponent = PlaitElement.getComponent(target);
1449
1588
  if (targetComponent.node.children.length > 0 && dropTarget.detectResult) {
1450
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1451
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1589
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1590
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1591
+ const children = getNonAbstractChildren(targetComponent.node);
1452
1592
  if (['right', 'left'].includes(dropTarget.detectResult)) {
1453
1593
  if (!isMixedLayout(parentLayout, layout)) {
1454
1594
  if (targetComponent.node.origin.isRoot) {
1455
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1595
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1456
1596
  // 标准布局,根节点
1457
1597
  if (isStandardLayout(layout)) {
1458
1598
  const rightNodeCount = targetComponent.node.origin.rightNodeCount;
@@ -1486,14 +1626,14 @@ const readjustmentDropTarget = (dropTarget) => {
1486
1626
  return newDropTarget;
1487
1627
  }
1488
1628
  // 剩下是水平布局的默认情况:插入最后一个子节点的下方
1489
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
1629
+ const lastChildNodeIndex = children.length - 1;
1490
1630
  newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1491
1631
  newDropTarget.detectResult = 'bottom';
1492
1632
  }
1493
1633
  else {
1494
1634
  // 处理左右布局下的混合布局
1495
1635
  if ([MindLayoutType.left, MindLayoutType.right].includes(parentLayout)) {
1496
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1636
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1497
1637
  if (isIndentedLayout(layout)) {
1498
1638
  newDropTarget.target = targetComponent.node.children[0].origin;
1499
1639
  newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
@@ -1510,9 +1650,9 @@ const readjustmentDropTarget = (dropTarget) => {
1510
1650
  return newDropTarget;
1511
1651
  }
1512
1652
  // 上下布局,插到右边
1513
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1653
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1514
1654
  if (isVerticalLogicLayout(parentLayout)) {
1515
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
1655
+ const lastChildNodeIndex = children.length - 1;
1516
1656
  newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1517
1657
  newDropTarget.detectResult = 'right';
1518
1658
  return newDropTarget;
@@ -1623,7 +1763,7 @@ const getLocationScope = (board, handlePosition, parentChildren, element, parent
1623
1763
  }
1624
1764
  };
1625
1765
  const getHitAbstractHandle = (board, element, point) => {
1626
- const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
1766
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, element);
1627
1767
  const isHorizontal = isHorizontalLayout(nodeLayout);
1628
1768
  const parentElement = MindElement.getParent(element);
1629
1769
  const includedElements = parentElement.children.slice(element.start, element.end + 1);
@@ -1710,24 +1850,25 @@ function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1710
1850
  }
1711
1851
 
1712
1852
  function drawIndentedLink(board, node, child, defaultStroke = null, needDrawUnderline = true) {
1713
- const isUnderlineShap = getNodeShapeByElement(child.origin) === MindNodeShape.underline;
1853
+ const branchWidth = getBranchWidthByMindElement(board, child.origin);
1854
+ const branchColor = defaultStroke || getBranchColorByMindElement(board, child.origin);
1855
+ const isUnderlineShape = getShapeByElement(board, child.origin) === MindElementShape.underline;
1714
1856
  let beginX, beginY, endX, endY, beginNode = node, endNode = child;
1715
1857
  const beginRectangle = getRectangleByNode(beginNode);
1716
1858
  const endRectangle = getRectangleByNode(endNode);
1717
1859
  beginX = beginNode.x + beginNode.width / 2;
1718
1860
  beginY = isChildUp(node, child) ? beginRectangle.y : beginRectangle.y + beginRectangle.height;
1719
1861
  endX = node.left ? endNode.x + endNode.hGap + endRectangle.width : endNode.x + endNode.hGap;
1720
- endY = isUnderlineShap ? endNode.y + endNode.height - endNode.vGap : endNode.y + endNode.height / 2;
1862
+ endY = isUnderlineShape ? endNode.y + endNode.height - endNode.vGap : endNode.y + endNode.height / 2;
1721
1863
  //根据位置,设置正负参数
1722
1864
  let plusMinus = isChildUp(node, child) ? (node.left ? [-1, -1] : [1, -1]) : node.left ? [-1, 1] : [1, 1];
1723
- const layout = MindQueries.getCorrectLayoutByElement(node.origin);
1724
- const strokeWidth = child.origin.branchWidth ? child.origin.branchWidth : STROKE_WIDTH;
1865
+ const layout = MindQueries.getCorrectLayoutByElement(board, node.origin);
1725
1866
  if (beginNode.origin.isRoot) {
1726
1867
  if (layout === MindLayoutType.leftBottomIndented || layout === MindLayoutType.rightBottomIndented) {
1727
- beginY += strokeWidth;
1868
+ beginY += branchWidth;
1728
1869
  }
1729
1870
  if (layout === MindLayoutType.leftTopIndented || layout === MindLayoutType.rightTopIndented) {
1730
- beginY -= strokeWidth;
1871
+ beginY -= branchWidth;
1731
1872
  }
1732
1873
  }
1733
1874
  let curve = [
@@ -1738,13 +1879,12 @@ function drawIndentedLink(board, node, child, defaultStroke = null, needDrawUnde
1738
1879
  [beginX, endY - (endNode.hGap * plusMinus[1]) / 5],
1739
1880
  [beginX + (endNode.hGap * plusMinus[0]) / 4, endY],
1740
1881
  [beginX + (endNode.hGap * plusMinus[0] * 3) / 5, endY],
1741
- isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1742
- isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1743
- isUnderlineShap && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY]
1882
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1883
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1884
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY]
1744
1885
  ];
1745
- const stroke = defaultStroke || getBranchColorByMindElement(board, child.origin);
1746
1886
  const points = pointsOnBezierCurves(curve);
1747
- return PlaitBoard.getRoughSVG(board).curve(points, { stroke, strokeWidth });
1887
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth: branchWidth });
1748
1888
  }
1749
1889
 
1750
1890
  var HorizontalPlacement;
@@ -1859,10 +1999,12 @@ const transformPlacement = (placement, direction) => {
1859
1999
 
1860
2000
  function drawLogicLink(board, node, parent, isHorizontal) {
1861
2001
  const branchColor = getBranchColorByMindElement(board, node.origin);
1862
- const strokeWidth = node.origin.branchWidth ? node.origin.branchWidth : STROKE_WIDTH;
2002
+ const branchWidth = getBranchWidthByMindElement(board, node.origin);
1863
2003
  const hasStraightLine = !parent.origin.isRoot;
1864
- const hasUnderlineShape = node.origin.shape === MindNodeShape.underline;
1865
- const hasUnderlineShapeOfParent = parent.origin.shape === MindNodeShape.underline;
2004
+ const parentShape = getShapeByElement(board, parent.origin);
2005
+ const shape = node.origin.shape ? node.origin.shape : parentShape;
2006
+ const hasUnderlineShape = shape === MindElementShape.underline;
2007
+ const hasUnderlineShapeOfParent = parentShape === MindElementShape.underline;
1866
2008
  const nodeClient = getRectangleByNode(node);
1867
2009
  const parentClient = getRectangleByNode(parent);
1868
2010
  const linkDirection = getLayoutDirection(node, isHorizontal);
@@ -1899,10 +2041,10 @@ function drawLogicLink(board, node, parent, isHorizontal) {
1899
2041
  const underlineEnd = movePoint(endPoint, nodeClient.width, linkDirection);
1900
2042
  const underline = hasUnderlineShape && isHorizontal ? [underlineEnd, underlineEnd, underlineEnd] : [];
1901
2043
  const points = pointsOnBezierCurves([...straightLine, ...curve, ...underline]);
1902
- return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth });
2044
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth: branchWidth });
1903
2045
  }
1904
2046
 
1905
- function getEmojisRectangle(board, element) {
2047
+ function getEmojisWidthHeight(board, element) {
1906
2048
  const options = board.getMindOptions();
1907
2049
  const count = element.data.emojis.length;
1908
2050
  const fontSize = getEmojiFontSize(element);
@@ -1919,6 +2061,31 @@ function getEmojiFontSize(element) {
1919
2061
  return 14 + 2;
1920
2062
  }
1921
2063
  }
2064
+ function getEmojiRectangle(board, element) {
2065
+ let { x, y } = getRectangleByNode(MindElement.getNode(element));
2066
+ x = x + NodeSpace.getEmojiLeftSpace(board, element);
2067
+ const { width, height } = getEmojisWidthHeight(board, element);
2068
+ return {
2069
+ x,
2070
+ y,
2071
+ width,
2072
+ height
2073
+ };
2074
+ }
2075
+ function getEmojiForeignRectangle(board, element) {
2076
+ let { x, y } = getRectangleByNode(MindElement.getNode(element));
2077
+ x = x + NodeSpace.getEmojiLeftSpace(board, element);
2078
+ const { width, height } = getEmojisWidthHeight(board, element);
2079
+ return {
2080
+ x,
2081
+ y,
2082
+ width,
2083
+ height: height + NodeSpace.getEmojiTopSpace(element) * 2
2084
+ };
2085
+ }
2086
+ const isHitEmojis = (board, element, point) => {
2087
+ return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), getEmojiRectangle(board, element));
2088
+ };
1922
2089
 
1923
2090
  const NodeDefaultSpace = {
1924
2091
  horizontal: {
@@ -1938,26 +2105,30 @@ const RootDefaultSpace = {
1938
2105
  nodeAndText: BASE * 2
1939
2106
  }
1940
2107
  };
1941
- const getHorizontalSpaceBetweenNodeAndText = (element) => {
2108
+ const getHorizontalSpaceBetweenNodeAndText = (board, element) => {
1942
2109
  const isMind = PlaitMind.isMind(element);
1943
2110
  const nodeAndText = isMind ? RootDefaultSpace.horizontal.nodeAndText : NodeDefaultSpace.horizontal.nodeAndText;
1944
2111
  return nodeAndText;
1945
2112
  };
1946
- const getHorizontalSpaceEmojiAndText = (element) => {
1947
- const isMind = PlaitMind.isMind(element);
1948
- const emojiAndText = isMind ? RootDefaultSpace.horizontal.emojiAndText : NodeDefaultSpace.horizontal.emojiAndText;
1949
- return emojiAndText;
1950
- };
1951
2113
  const getVerticalSpaceBetweenNodeAndText = (element) => {
1952
2114
  const isMind = PlaitMind.isMind(element);
1953
2115
  const nodeAndText = isMind ? RootDefaultSpace.vertical.nodeAndText : NodeDefaultSpace.vertical.nodeAndText;
1954
2116
  return nodeAndText;
1955
2117
  };
2118
+ const getSpaceEmojiAndText = (element) => {
2119
+ const isMind = PlaitMind.isMind(element);
2120
+ const emojiAndText = isMind ? RootDefaultSpace.horizontal.emojiAndText : NodeDefaultSpace.horizontal.emojiAndText;
2121
+ return emojiAndText;
2122
+ };
1956
2123
  const NodeSpace = {
1957
2124
  getNodeWidth(board, element) {
1958
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
2125
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
1959
2126
  if (MindElement.hasEmojis(element)) {
1960
- return nodeAndText + getEmojisRectangle(board, element).width + getHorizontalSpaceEmojiAndText(element) + element.width + nodeAndText;
2127
+ return (NodeSpace.getEmojiLeftSpace(board, element) +
2128
+ getEmojisWidthHeight(board, element).width +
2129
+ getSpaceEmojiAndText(element) +
2130
+ element.width +
2131
+ nodeAndText);
1961
2132
  }
1962
2133
  return nodeAndText + element.width + nodeAndText;
1963
2134
  },
@@ -1965,31 +2136,32 @@ const NodeSpace = {
1965
2136
  const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1966
2137
  return nodeAndText + element.height + nodeAndText;
1967
2138
  },
1968
- getTextHorizontalSpace(board, element) {
1969
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
2139
+ getTextLeftSpace(board, element) {
2140
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
1970
2141
  if (MindElement.hasEmojis(element)) {
1971
- return nodeAndText + getEmojisRectangle(board, element).width + getHorizontalSpaceEmojiAndText(element);
2142
+ return NodeSpace.getEmojiLeftSpace(board, element) + getEmojisWidthHeight(board, element).width + getSpaceEmojiAndText(element);
1972
2143
  }
1973
2144
  else {
1974
2145
  return nodeAndText;
1975
2146
  }
1976
2147
  },
1977
- getTextVerticalSpace(element) {
2148
+ getTextTopSpace(element) {
1978
2149
  const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1979
2150
  return nodeAndText;
1980
2151
  },
1981
- getEmojiHorizontalSpace(element) {
1982
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
1983
- return nodeAndText;
2152
+ getEmojiLeftSpace(board, element) {
2153
+ const options = board.getMindOptions();
2154
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
2155
+ return nodeAndText - options.emojiPadding;
1984
2156
  },
1985
- getEmojiVerticalSpace(element) {
2157
+ getEmojiTopSpace(element) {
1986
2158
  const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1987
2159
  return nodeAndText;
1988
2160
  }
1989
2161
  };
1990
2162
 
1991
2163
  function drawMindNodeRichtext(board, node, viewContainerRef) {
1992
- const { x, y } = getRichtextRectangleByNode(board, node);
2164
+ const { x, y, width, height } = getRichtextRectangleByNode(board, node);
1993
2165
  const classList = [];
1994
2166
  if (node.origin.isRoot) {
1995
2167
  classList.push('root-node');
@@ -2002,7 +2174,7 @@ function drawMindNodeRichtext(board, node, viewContainerRef) {
2002
2174
  classList.push('child-node');
2003
2175
  }
2004
2176
  // COMPAT: last character can not show in safari browser
2005
- return drawRichtext(x, y, Math.ceil(node.origin.width), Math.ceil(node.origin.height), node.origin.data.topic, viewContainerRef, classList);
2177
+ return drawRichtext(x, y, width, height, node.origin.data.topic, viewContainerRef, classList);
2006
2178
  }
2007
2179
  function updateMindNodeTopicSize(board, node, g, isEditable) {
2008
2180
  const { x, y, width, height } = getRichtextRectangleByNode(board, node);
@@ -2012,21 +2184,23 @@ function updateMindNodeTopicSize(board, node, g, isEditable) {
2012
2184
  }
2013
2185
  else {
2014
2186
  // COMPAT: last character can not show in safari browser
2015
- updateForeignObject(g, Math.ceil(node.origin.width), Math.ceil(node.origin.height), x, y);
2187
+ updateForeignObject(g, width, height, x, y);
2016
2188
  }
2017
2189
  }
2018
2190
  function getRichtextRectangleByNode(board, node) {
2019
- let { x, y, width, height } = getRectangleByNode(node);
2020
- x = x + NodeSpace.getTextHorizontalSpace(board, node.origin);
2021
- y = y + NodeSpace.getTextVerticalSpace(node.origin);
2022
- return { width, height, x, y };
2191
+ let { x, y } = getRectangleByNode(node);
2192
+ x = x + NodeSpace.getTextLeftSpace(board, node.origin);
2193
+ y = y + NodeSpace.getTextTopSpace(node.origin);
2194
+ const width = Math.ceil(node.origin.width);
2195
+ const height = Math.ceil(node.origin.height);
2196
+ return { height, width, x, y };
2023
2197
  }
2024
2198
 
2025
2199
  function drawRectangleNode(board, node) {
2026
2200
  const { x, y, width, height } = getRectangleByNode(node);
2027
- const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? ROOT_NODE_FILL : NODE_FILL;
2201
+ const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? DefaultRootStyle.fill : DefaultNodeStyle.fill;
2028
2202
  const stroke = getStrokeByMindElement(board, node.origin);
2029
- const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : STROKE_WIDTH;
2203
+ const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : DefaultNodeStyle.strokeWidth;
2030
2204
  const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), x, y, x + width, y + height, {
2031
2205
  stroke,
2032
2206
  strokeWidth,
@@ -2038,6 +2212,8 @@ function drawRectangleNode(board, node) {
2038
2212
 
2039
2213
  function drawAbstractLink(board, node, isHorizontal) {
2040
2214
  const linkPadding = 15;
2215
+ const branchWidth = getAbstractBranchWidth(board, node.origin);
2216
+ const branchColor = getAbstractBranchColor(board, node.origin);
2041
2217
  const parent = node.parent;
2042
2218
  const abstractRectangle = getRectangleByNode(node);
2043
2219
  let includedElements = parent.children.slice(node.origin.start, node.origin.end + 1).map(node => {
@@ -2067,8 +2243,8 @@ function drawAbstractLink(board, node, isHorizontal) {
2067
2243
  let c2 = movePoint(bezierEndPoint, curveDistance, linkDirection);
2068
2244
  let bezierConnectorPoint = movePoint(abstractConnectorPoint, -linkPadding, linkDirection);
2069
2245
  const link = PlaitBoard.getRoughSVG(board).path(`M${bezierBeginPoint[0]},${bezierBeginPoint[1]} Q${c1[0]},${c1[1]} ${bezierConnectorPoint[0]},${bezierConnectorPoint[1]} Q${c2[0]},${c2[1]} ${bezierEndPoint[0]},${bezierEndPoint[1]} M${abstractConnectorPoint[0]},${abstractConnectorPoint[1]} L${bezierConnectorPoint[0]},${bezierConnectorPoint[1]}`, {
2070
- stroke: GRAY_COLOR,
2071
- strokeWidth: 2
2246
+ stroke: branchColor,
2247
+ strokeWidth: branchWidth
2072
2248
  });
2073
2249
  return link;
2074
2250
  }
@@ -2114,10 +2290,8 @@ class EmojisDrawer {
2114
2290
  if (MindElement.hasEmojis(element)) {
2115
2291
  this.g = createG();
2116
2292
  this.g.classList.add('emojis');
2117
- let { x, y } = getRectangleByNode(MindElement.getNode(element));
2118
- x = x + NodeSpace.getEmojiHorizontalSpace(element);
2119
- const { width, height } = getEmojisRectangle(this.board, element);
2120
- const foreignObject = createForeignObject(x, y, width, height + NodeSpace.getEmojiVerticalSpace(element) * 2);
2293
+ const foreignRectangle = getEmojiForeignRectangle(this.board, element);
2294
+ const foreignObject = createForeignObject(foreignRectangle.x, foreignRectangle.y, foreignRectangle.width, foreignRectangle.height);
2121
2295
  this.g.append(foreignObject);
2122
2296
  const container = document.createElement('div');
2123
2297
  container.classList.add('node-emojis-container');
@@ -2126,117 +2300,27 @@ class EmojisDrawer {
2126
2300
  const drawer = new EmojiDrawer(this.board, this.viewContainerRef);
2127
2301
  drawer.draw(emojiItem, element);
2128
2302
  return drawer;
2129
- });
2130
- this.emojiDrawers.forEach(drawer => {
2131
- container.append(drawer.nativeElement);
2132
- });
2133
- return this.g;
2134
- }
2135
- return undefined;
2136
- }
2137
- destroy() {
2138
- if (this.g) {
2139
- this.g.remove();
2140
- }
2141
- this.emojiDrawers.forEach(drawer => drawer.destroy());
2142
- this.emojiDrawers = [];
2143
- }
2144
- }
2145
-
2146
- const setLayout = (board, layout, path) => {
2147
- correctLogicLayoutNode(board, layout, path);
2148
- const element = PlaitNode.get(board, path);
2149
- if (PlaitMind.isMind(element) && isStandardLayout(layout)) {
2150
- handleAbstractIncluded(board, element);
2151
- }
2152
- Transforms.setNode(board, { layout }, path);
2153
- };
2154
- const correctLogicLayoutNode = (board, layout, path) => {
2155
- const node = PlaitNode.get(board, path);
2156
- if (node && layout) {
2157
- node.children?.forEach((value, index) => {
2158
- if (value.layout) {
2159
- if ((isHorizontalLogicLayout(layout) && isVerticalLogicLayout(value.layout)) ||
2160
- (isVerticalLogicLayout(layout) && isHorizontalLogicLayout(value.layout))) {
2161
- Transforms.setNode(board, { layout: null }, [...path, index]);
2162
- }
2163
- if (value.children?.length) {
2164
- correctLogicLayoutNode(board, layout, [...path, index]);
2165
- }
2166
- }
2167
- });
2168
- }
2169
- };
2170
-
2171
- const setTopic = (board, element, topic, width, height) => {
2172
- const newElement = {
2173
- data: { topic },
2174
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2175
- height: height / board.viewport.zoom
2176
- };
2177
- if (MindElement.hasEmojis(element)) {
2178
- newElement.data.emojis = element.data.emojis;
2179
- }
2180
- const path = PlaitBoard.findPath(board, element);
2181
- Transforms.setNode(board, newElement, path);
2182
- };
2183
- const setTopicSize = (board, element, width, height) => {
2184
- const newElement = {
2185
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2186
- height: height / board.viewport.zoom
2187
- };
2188
- const path = PlaitBoard.findPath(board, element);
2189
- Transforms.setNode(board, newElement, path);
2190
- };
2191
- const addEmoji = (board, element, emojiItem) => {
2192
- const emojis = element.data.emojis || [];
2193
- const newEmojis = [...emojis];
2194
- newEmojis.push(emojiItem);
2195
- const newElement = {
2196
- data: { topic: element.data.topic, emojis: newEmojis }
2197
- };
2198
- const path = PlaitBoard.findPath(board, element);
2199
- Transforms.setNode(board, newElement, path);
2200
- };
2201
- const removeEmoji = (board, element, emojiItem) => {
2202
- const emojis = element.data.emojis.filter(value => value !== emojiItem);
2203
- const newElement = {
2204
- data: { topic: element.data.topic }
2205
- };
2206
- if (emojis.length > 0) {
2207
- newElement.data.emojis = emojis;
2208
- }
2209
- const path = PlaitBoard.findPath(board, element);
2210
- Transforms.setNode(board, newElement, path);
2211
- };
2212
- const replaceEmoji = (board, element, oldEmoji, newEmoji) => {
2213
- const newElement = {
2214
- data: { topic: element.data.topic }
2215
- };
2216
- const newEmojis = element.data.emojis.map(value => {
2217
- if (value === oldEmoji) {
2218
- return newEmoji;
2303
+ });
2304
+ this.emojiDrawers.forEach(drawer => {
2305
+ container.append(drawer.nativeElement);
2306
+ });
2307
+ return this.g;
2219
2308
  }
2220
- return value;
2221
- });
2222
- newElement.data.emojis = newEmojis;
2223
- const path = PlaitBoard.findPath(board, element);
2224
- Transforms.setNode(board, newElement, path);
2225
- };
2226
-
2227
- const MindTransforms = {
2228
- setLayout,
2229
- setTopic,
2230
- setTopicSize,
2231
- addEmoji,
2232
- removeEmoji,
2233
- replaceEmoji
2234
- };
2309
+ return undefined;
2310
+ }
2311
+ destroy() {
2312
+ if (this.g) {
2313
+ this.g.remove();
2314
+ }
2315
+ this.emojiDrawers.forEach(drawer => drawer.destroy());
2316
+ this.emojiDrawers = [];
2317
+ }
2318
+ }
2235
2319
 
2236
2320
  function drawAbstractIncludedOutline(board, roughSVG, element, activeHandlePosition, resizingLocation) {
2237
2321
  const abstractIncludedG = createG();
2238
2322
  const parentElement = MindElement.getParent(element);
2239
- const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
2323
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, element);
2240
2324
  const isHorizontal = isHorizontalLayout(nodeLayout);
2241
2325
  const includedElements = parentElement.children.slice(element.start, element.end + 1);
2242
2326
  let abstractRectangle = getRectangleByElements(board, includedElements, true);
@@ -2316,7 +2400,8 @@ function hasAfterDraw(value) {
2316
2400
  }
2317
2401
 
2318
2402
  function findNewChildNodePath(board, element) {
2319
- return PlaitBoard.findPath(board, element).concat((element.children || []).filter(child => !AbstractNode.isAbstract(child)).length);
2403
+ const children = getNonAbstractChildren(element);
2404
+ return PlaitBoard.findPath(board, element).concat(children.length);
2320
2405
  }
2321
2406
  function findNewSiblingNodePath(board, element) {
2322
2407
  const path = PlaitBoard.findPath(board, element);
@@ -2344,16 +2429,16 @@ class QuickInsertDrawer extends BaseDrawer {
2344
2429
  * 3. 上、上左、上右
2345
2430
  * 4. 下、下左、下右
2346
2431
  */
2347
- const shape = getNodeShapeByElement(element);
2432
+ const shape = getShapeByElement(this.board, element);
2348
2433
  // 形状是矩形要偏移边框的线宽
2349
- const strokeWidth = element.branchWidth ? element.branchWidth : STROKE_WIDTH;
2434
+ const branchWidth = getBranchWidthByMindElement(this.board, element);
2350
2435
  let offsetBorderLineWidth = 0;
2351
- if (shape === MindNodeShape.roundRectangle && offset === 0) {
2352
- offsetBorderLineWidth = strokeWidth;
2436
+ if (shape === MindElementShape.roundRectangle && offset === 0) {
2437
+ offsetBorderLineWidth = branchWidth;
2353
2438
  }
2354
2439
  let offsetRootBorderLineWidth = 0;
2355
2440
  if (element.isRoot) {
2356
- offsetRootBorderLineWidth = strokeWidth;
2441
+ offsetRootBorderLineWidth = branchWidth;
2357
2442
  }
2358
2443
  // 当没有子节点时,需要缩小的偏移量
2359
2444
  const extraOffset = 3;
@@ -2448,21 +2533,21 @@ class QuickInsertDrawer extends BaseDrawer {
2448
2533
  offsetRootBorderLineWidth
2449
2534
  }
2450
2535
  };
2451
- if (shape === MindNodeShape.roundRectangle || element.isRoot) {
2536
+ if (shape === MindElementShape.roundRectangle || element.isRoot) {
2452
2537
  underlineCoordinates[MindLayoutType.left].startY -= height * 0.5;
2453
2538
  underlineCoordinates[MindLayoutType.left].endY -= height * 0.5;
2454
2539
  underlineCoordinates[MindLayoutType.right].startY -= height * 0.5;
2455
2540
  underlineCoordinates[MindLayoutType.right].endY -= height * 0.5;
2456
2541
  }
2457
2542
  const branchColor = PlaitMind.isMind(element) ? getNextBranchColor(element) : getBranchColorByMindElement(this.board, element);
2458
- let nodeLayout = MindQueries.getCorrectLayoutByElement(element);
2543
+ let nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, element);
2459
2544
  if (element.isRoot && isStandardLayout(nodeLayout)) {
2460
2545
  const root = element;
2461
2546
  nodeLayout = root.children.length >= root.rightNodeCount ? MindLayoutType.left : MindLayoutType.right;
2462
2547
  }
2463
2548
  const underlineCoordinate = underlineCoordinates[nodeLayout];
2464
2549
  if (underlineCoordinate) {
2465
- const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth });
2550
+ const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth: branchWidth });
2466
2551
  const circleCoordinates = {
2467
2552
  startX: underlineCoordinate.endX,
2468
2553
  startY: underlineCoordinate.endY
@@ -2540,10 +2625,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2540
2625
  this.emojisDrawer = new EmojisDrawer(this.board, this.viewContainerRef);
2541
2626
  this.quickInsertDrawer = new QuickInsertDrawer(this.board);
2542
2627
  super.ngOnInit();
2543
- this.node = ELEMENT_TO_NODE.get(this.element);
2544
- if (!PlaitMind.isMind(this.element)) {
2545
- this.parent = MindElement.getNode(MindElement.getParent(this.element));
2546
- }
2628
+ this.node = MindElement.getNode(this.element);
2547
2629
  this.index = NODE_TO_INDEX.get(this.element) || 0;
2548
2630
  this.roughSVG = PlaitBoard.getRoughSVG(this.board);
2549
2631
  this.parentG = PlaitElement.getComponent(MindElement.getRoot(this.board, this.element)).rootG;
@@ -2558,21 +2640,18 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2558
2640
  this.drawQuickInsert();
2559
2641
  }
2560
2642
  onContextChanged(value, previous) {
2561
- const newNode = ELEMENT_TO_NODE.get(this.element);
2562
- if (!PlaitMind.isMind(this.element)) {
2563
- this.parent = MindElement.getNode(MindElement.getParent(this.element));
2564
- }
2643
+ const newNode = MindElement.getNode(value.element);
2565
2644
  // resolve move node richtext lose issue
2566
2645
  if (this.node !== newNode) {
2567
2646
  if (this.foreignObject && this.foreignObject.children.length <= 0) {
2568
2647
  this.foreignObject?.appendChild(this.richtextComponentRef?.instance.editable);
2569
2648
  }
2570
2649
  }
2571
- const isEquals = MindNode.isEquals(this.node, newNode);
2650
+ const isEqualNode = RectangleClient.isEqual(this.node, newNode);
2572
2651
  this.node = newNode;
2573
- this.drawActiveG();
2574
- this.updateActiveClass();
2575
- if (!isEquals) {
2652
+ if (!isEqualNode || value.element !== previous.element) {
2653
+ this.drawActiveG();
2654
+ this.updateActiveClass();
2576
2655
  this.drawShape();
2577
2656
  this.drawLink();
2578
2657
  this.updateRichtext();
@@ -2581,6 +2660,20 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2581
2660
  this.drawQuickInsert();
2582
2661
  this.drawEmojis();
2583
2662
  }
2663
+ else {
2664
+ if (value.selected !== previous.selected) {
2665
+ this.drawActiveG();
2666
+ this.updateActiveClass();
2667
+ }
2668
+ if (!PlaitMind.isMind(value.element)) {
2669
+ const parent = MindElement.getParent(previous.element);
2670
+ const newParent = MindElement.getParent(value.element);
2671
+ const hasSameChildren = parent.children.length === newParent.children.length;
2672
+ if (!hasSameChildren) {
2673
+ this.drawLink();
2674
+ }
2675
+ }
2676
+ }
2584
2677
  }
2585
2678
  drawEmojis() {
2586
2679
  const g = this.emojisDrawer.drawEmojis(this.element);
@@ -2600,9 +2693,9 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2600
2693
  }
2601
2694
  drawShape() {
2602
2695
  this.destroyShape();
2603
- const shape = getNodeShapeByElement(this.node.origin);
2696
+ const shape = getShapeByElement(this.board, this.node.origin);
2604
2697
  switch (shape) {
2605
- case MindNodeShape.roundRectangle:
2698
+ case MindElementShape.roundRectangle:
2606
2699
  this.shapeG = drawRectangleNode(this.board, this.node);
2607
2700
  this.g.prepend(this.shapeG);
2608
2701
  break;
@@ -2617,29 +2710,29 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2617
2710
  }
2618
2711
  }
2619
2712
  drawLink() {
2620
- if (!this.parent) {
2713
+ if (PlaitMind.isMind(this.element)) {
2621
2714
  return;
2622
2715
  }
2716
+ const parent = MindElement.getParent(this.element);
2717
+ const parentNode = MindElement.getNode(parent);
2623
2718
  if (this.linkG) {
2624
2719
  this.linkG.remove();
2625
2720
  }
2626
- const layout = MindQueries.getLayoutByElement(this.parent.origin);
2721
+ const layout = MindQueries.getLayoutByElement(parent);
2627
2722
  if (AbstractNode.isAbstract(this.node.origin)) {
2628
2723
  this.linkG = drawAbstractLink(this.board, this.node, isHorizontalLayout(layout));
2629
2724
  }
2630
- else if (MindElement.isIndentedLayout(this.parent.origin)) {
2631
- this.linkG = drawIndentedLink(this.board, this.parent, this.node);
2725
+ else if (MindElement.isIndentedLayout(parent)) {
2726
+ this.linkG = drawIndentedLink(this.board, parentNode, this.node);
2632
2727
  }
2633
2728
  else {
2634
- this.linkG = drawLogicLink(this.board, this.node, this.parent, isHorizontalLayout(layout));
2729
+ this.linkG = drawLogicLink(this.board, this.node, parentNode, isHorizontalLayout(layout));
2635
2730
  }
2636
2731
  this.g.append(this.linkG);
2637
2732
  }
2638
2733
  destroyLine() {
2639
- if (this.parent) {
2640
- if (this.linkG) {
2641
- this.linkG.remove();
2642
- }
2734
+ if (this.linkG) {
2735
+ this.linkG.remove();
2643
2736
  }
2644
2737
  }
2645
2738
  drawMaskG() {
@@ -2789,9 +2882,9 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2789
2882
  });
2790
2883
  const { x, y, width, height } = getRectangleByNode(this.node);
2791
2884
  const stroke = getBranchColorByMindElement(this.board, this.element);
2792
- const strokeWidth = this.node.origin.branchWidth ? this.node.origin.branchWidth : STROKE_WIDTH;
2885
+ const branchWidth = getBranchWidthByMindElement(this.board, this.element);
2793
2886
  const extendY = y + height / 2;
2794
- const nodeLayout = MindQueries.getCorrectLayoutByElement(this.element);
2887
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, this.element);
2795
2888
  let extendLineXY = [
2796
2889
  [x + width, extendY],
2797
2890
  [x + width + EXTEND_OFFSET, extendY]
@@ -2803,7 +2896,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2803
2896
  let circleOffset = [EXTEND_RADIUS / 2, 0];
2804
2897
  if (isHorizontalLayout(nodeLayout) && !isIndentedLayout(nodeLayout)) {
2805
2898
  extendLineYOffset =
2806
- getNodeShapeByElement(this.node.origin) === MindNodeShape.roundRectangle
2899
+ getShapeByElement(this.board, this.node.origin) === MindElementShape.roundRectangle
2807
2900
  ? [0, 0]
2808
2901
  : [height / 2, height / 2];
2809
2902
  if (isLeftLayout(nodeLayout)) {
@@ -2835,7 +2928,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2835
2928
  [extendLineXY[1][0] + extendLineXOffset[1], extendLineXY[1][1] + extendLineYOffset[1]]
2836
2929
  ];
2837
2930
  const extendLine = this.roughSVG.line(extendLineXY[0][0], extendLineXY[0][1], extendLineXY[1][0], extendLineXY[1][1], {
2838
- strokeWidth,
2931
+ strokeWidth: branchWidth,
2839
2932
  stroke
2840
2933
  });
2841
2934
  //绘制箭头
@@ -2872,7 +2965,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2872
2965
  const hideCircleG = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS - 1, {
2873
2966
  fill: '#fff',
2874
2967
  stroke,
2875
- strokeWidth,
2968
+ strokeWidth: branchWidth,
2876
2969
  fillStyle: 'solid'
2877
2970
  });
2878
2971
  collapseG.appendChild(hideCircleG);
@@ -3109,7 +3202,7 @@ const getLayoutOptions = (board) => {
3109
3202
  }
3110
3203
  },
3111
3204
  getVerticalConnectingPosition(element, parent) {
3112
- if (element.shape === MindNodeShape.underline && parent && isHorizontalLogicLayout(parent.layout)) {
3205
+ if (element.shape === MindElementShape.underline && parent && isHorizontalLogicLayout(parent.layout)) {
3113
3206
  return ConnectingPosition.bottom;
3114
3207
  }
3115
3208
  return undefined;
@@ -3178,6 +3271,122 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3178
3271
  }]
3179
3272
  }] });
3180
3273
 
3274
+ const isValidTarget = (origin, target) => {
3275
+ return origin !== target && !isChildElement(origin, target);
3276
+ };
3277
+ const addActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3278
+ const activeComponent = PlaitElement.getComponent(activeElement);
3279
+ if (isOrigin) {
3280
+ activeComponent.g.classList.add('dragging-origin');
3281
+ }
3282
+ else {
3283
+ activeComponent.g.classList.add('dragging-child');
3284
+ }
3285
+ !activeElement.isCollapsed &&
3286
+ activeElement.children.forEach(child => {
3287
+ addActiveOnDragOrigin(child, false);
3288
+ });
3289
+ };
3290
+ const removeActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3291
+ const activeComponent = PlaitElement.getComponent(activeElement);
3292
+ if (isOrigin) {
3293
+ activeComponent.g.classList.remove('dragging-origin');
3294
+ }
3295
+ else {
3296
+ activeComponent.g.classList.remove('dragging-child');
3297
+ }
3298
+ !activeElement.isCollapsed &&
3299
+ activeElement.children.forEach(child => {
3300
+ removeActiveOnDragOrigin(child, false);
3301
+ });
3302
+ };
3303
+ const updatePathByLayoutAndDropTarget = (targetPath, layout, dropTarget) => {
3304
+ // 上下布局:左右是兄弟节点,上下是子节点
3305
+ if (isVerticalLogicLayout(layout)) {
3306
+ if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3307
+ targetPath.push(dropTarget.target.children.length);
3308
+ }
3309
+ if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3310
+ targetPath.push(dropTarget.target.children.length);
3311
+ }
3312
+ // 如果是左,位置不变,右则插入到下一个兄弟节点
3313
+ if (dropTarget.detectResult === 'right') {
3314
+ targetPath = Path.next(targetPath);
3315
+ }
3316
+ }
3317
+ // 水平布局/标准布局:上下是兄弟节点,左右是子节点
3318
+ if (isHorizontalLogicLayout(layout)) {
3319
+ if (dropTarget.detectResult === 'right') {
3320
+ targetPath.push(dropTarget.target.children.length);
3321
+ }
3322
+ if (dropTarget.detectResult === 'left') {
3323
+ targetPath.push(dropTarget.target.children.length);
3324
+ }
3325
+ // 如果是上,位置不变,下插入到下一个兄弟节点
3326
+ if (dropTarget.detectResult === 'bottom') {
3327
+ targetPath = Path.next(targetPath);
3328
+ }
3329
+ }
3330
+ // 缩进布局:上下是兄弟节点,左右是子节点,但上(左上/右上),探测到上是子节点,下则位置不变,反之同理。
3331
+ if (isIndentedLayout(layout)) {
3332
+ if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3333
+ targetPath = Path.next(targetPath);
3334
+ }
3335
+ if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3336
+ targetPath = Path.next(targetPath);
3337
+ }
3338
+ if (isLeftLayout(layout) && dropTarget.detectResult === 'left') {
3339
+ targetPath.push(dropTarget.target.children.length);
3340
+ }
3341
+ if (isRightLayout(layout) && dropTarget.detectResult === 'right') {
3342
+ targetPath.push(dropTarget.target.children.length);
3343
+ }
3344
+ }
3345
+ return targetPath;
3346
+ };
3347
+ const updateRightNodeCount = (board, activeComponent, targetComponent, detectResult) => {
3348
+ let rightNodeCount;
3349
+ const mindElement = findUpElement(targetComponent.node.origin).root;
3350
+ const mindComponent = ELEMENT_TO_COMPONENT.get(mindElement);
3351
+ const activeIndex = mindComponent?.root.children.indexOf(activeComponent.node);
3352
+ const targetIndex = mindComponent?.root.children.indexOf(targetComponent.node);
3353
+ const activeParent = MindElement.getParent(activeComponent.element);
3354
+ const targetParent = MindElement.findParent(targetComponent.element);
3355
+ const isActiveOnRight = activeIndex !== -1 && activeIndex <= activeParent.rightNodeCount - 1;
3356
+ const isTargetOnRight = targetParent && targetIndex !== -1 && targetIndex <= targetParent.rightNodeCount - 1;
3357
+ const isBothOnRight = isActiveOnRight && isTargetOnRight;
3358
+ const rootChildCount = mindComponent.root.children?.length;
3359
+ const rootRightNodeCount = mindComponent?.root.origin.rightNodeCount;
3360
+ if (!isBothOnRight) {
3361
+ if (isActiveOnRight) {
3362
+ rightNodeCount = rootChildCount < rootRightNodeCount ? rootChildCount - 1 : rootRightNodeCount - 1;
3363
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, activeParent));
3364
+ }
3365
+ if (isTargetOnRight && detectResult !== 'right') {
3366
+ rightNodeCount = rootChildCount < rootRightNodeCount ? rootRightNodeCount : rootRightNodeCount + 1;
3367
+ const parent = MindElement.getParent(targetComponent.element);
3368
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, parent));
3369
+ }
3370
+ //二级子节点拖动到根节点左侧
3371
+ if (targetComponent.node.origin.isRoot && detectResult === 'left' && activeIndex === -1) {
3372
+ rightNodeCount = rootChildCount;
3373
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.element));
3374
+ }
3375
+ }
3376
+ };
3377
+ const IS_DRAGGING = new WeakMap();
3378
+ const isDragging = (board) => {
3379
+ return !!IS_DRAGGING.get(board);
3380
+ };
3381
+ const setIsDragging = (board, state) => {
3382
+ IS_DRAGGING.set(board, state);
3383
+ };
3384
+ const updateAbstractInDnd = (board, deletableElements, originPath) => {
3385
+ const refs = insertElementHandleAbstract(board, originPath, false);
3386
+ deleteElementHandleAbstract(board, deletableElements, refs);
3387
+ MindTransforms.setAbstractsByRefs(board, refs);
3388
+ };
3389
+
3181
3390
  const DRAG_MOVE_BUFFER = 5;
3182
3391
  const withDnd = (board) => {
3183
3392
  const { mousedown, mousemove, globalMouseup, keydown } = board;
@@ -3199,8 +3408,8 @@ const withDnd = (board) => {
3199
3408
  return;
3200
3409
  }
3201
3410
  if (PlaitMind.isMind(value)) {
3202
- const mindmapComponent = ELEMENT_TO_COMPONENT.get(value);
3203
- const root = mindmapComponent?.root;
3411
+ const mindComponent = ELEMENT_TO_COMPONENT.get(value);
3412
+ const root = mindComponent.root;
3204
3413
  root.eachNode((node) => {
3205
3414
  if (activeElement) {
3206
3415
  return;
@@ -3249,7 +3458,6 @@ const withDnd = (board) => {
3249
3458
  const offsetX = endPoint[0] - startPoint[0];
3250
3459
  const offsetY = endPoint[1] - startPoint[1];
3251
3460
  const activeComponent = PlaitElement.getComponent(activeElement);
3252
- const roughSVG = PlaitBoard.getRoughSVG(board);
3253
3461
  const fakeDraggingNode = {
3254
3462
  ...activeComponent.node,
3255
3463
  children: [],
@@ -3259,9 +3467,16 @@ const withDnd = (board) => {
3259
3467
  const textRectangle = getRichtextRectangleByNode(board, activeComponent.node);
3260
3468
  const fakeNodeG = drawRectangleNode(board, fakeDraggingNode);
3261
3469
  const richtextG = activeComponent.richtextG?.cloneNode(true);
3262
- updateForeignObject(richtextG, textRectangle.width + BASE * 10, textRectangle.height, textRectangle.x + offsetX, textRectangle.y + offsetY);
3470
+ updateForeignObject$1(richtextG, textRectangle.width, textRectangle.height, textRectangle.x + offsetX, textRectangle.y + offsetY);
3263
3471
  fakeDragNodeG?.append(fakeNodeG);
3264
3472
  fakeDragNodeG?.append(richtextG);
3473
+ // draw emojis
3474
+ if (MindElement.hasEmojis(activeElement)) {
3475
+ const fakeEmojisG = activeComponent.emojisDrawer.g.cloneNode(true);
3476
+ const foreignRectangle = getEmojiForeignRectangle(board, activeElement);
3477
+ updateForeignObject$1(fakeEmojisG, foreignRectangle.width, foreignRectangle.height, foreignRectangle.x + offsetX, foreignRectangle.y + offsetY);
3478
+ fakeDragNodeG?.append(fakeEmojisG);
3479
+ }
3265
3480
  // drop position detect
3266
3481
  const { x, y } = getRectangleByNode(fakeDraggingNode);
3267
3482
  const detectCenterPoint = [x + textRectangle.width / 2, y + textRectangle.height / 2];
@@ -3279,7 +3494,7 @@ const withDnd = (board) => {
3279
3494
  }
3280
3495
  const directions = directionDetector(node, detectCenterPoint);
3281
3496
  if (directions) {
3282
- detectResult = directionCorrector(node, directions);
3497
+ detectResult = directionCorrector(board, node, directions);
3283
3498
  }
3284
3499
  dropTarget = null;
3285
3500
  if (detectResult && isValidTarget(activeComponent.node.origin, node.origin)) {
@@ -3289,7 +3504,7 @@ const withDnd = (board) => {
3289
3504
  }
3290
3505
  });
3291
3506
  if (dropTarget?.target) {
3292
- dropTarget = readjustmentDropTarget(dropTarget);
3507
+ dropTarget = readjustmentDropTarget(board, dropTarget);
3293
3508
  drawPlaceholderDropNodeG(board, dropTarget, fakeDropNodeG);
3294
3509
  }
3295
3510
  }
@@ -3301,12 +3516,13 @@ const withDnd = (board) => {
3301
3516
  const activeComponent = PlaitElement.getComponent(activeElement);
3302
3517
  const targetComponent = PlaitElement.getComponent(dropTarget.target);
3303
3518
  let targetPath = PlaitBoard.findPath(board, targetComponent.element);
3304
- const mindmapElement = findUpElement(dropTarget.target).root;
3305
- const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
3306
- const layout = MindQueries.getCorrectLayoutByElement(mindmapComponent?.root.origin);
3519
+ const mindElement = findUpElement(dropTarget.target).root;
3520
+ const mindComponent = ELEMENT_TO_COMPONENT.get(mindElement);
3521
+ const layout = MindQueries.getCorrectLayoutByElement(board, mindComponent?.root.origin);
3307
3522
  targetPath = updatePathByLayoutAndDropTarget(targetPath, layout, dropTarget);
3308
3523
  const originPath = PlaitBoard.findPath(board, activeComponent.element);
3309
3524
  let newElement = { isCollapsed: false }, rightTargetPath = PlaitBoard.findPath(board, targetComponent.element);
3525
+ updateAbstractInDnd(board, [activeElement], targetPath);
3310
3526
  if (isStandardLayout(layout)) {
3311
3527
  updateRightNodeCount(board, activeComponent, targetComponent, dropTarget.detectResult);
3312
3528
  }
@@ -3339,124 +3555,51 @@ const withDnd = (board) => {
3339
3555
  };
3340
3556
  return board;
3341
3557
  };
3342
- const isValidTarget = (origin, target) => {
3343
- return origin !== target && !isChildElement(origin, target);
3344
- };
3345
- const addActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3346
- const activeComponent = PlaitElement.getComponent(activeElement);
3347
- if (isOrigin) {
3348
- activeComponent.g.classList.add('dragging-origin');
3349
- }
3350
- else {
3351
- activeComponent.g.classList.add('dragging-child');
3352
- }
3353
- !activeElement.isCollapsed &&
3354
- activeElement.children.forEach(child => {
3355
- addActiveOnDragOrigin(child, false);
3356
- });
3357
- };
3358
- const removeActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3359
- const activeComponent = PlaitElement.getComponent(activeElement);
3360
- if (isOrigin) {
3361
- activeComponent.g.classList.remove('dragging-origin');
3362
- }
3363
- else {
3364
- activeComponent.g.classList.remove('dragging-child');
3365
- }
3366
- !activeElement.isCollapsed &&
3367
- activeElement.children.forEach(child => {
3368
- removeActiveOnDragOrigin(child, false);
3369
- });
3370
- };
3371
- const updatePathByLayoutAndDropTarget = (targetPath, layout, dropTarget) => {
3372
- // 上下布局:左右是兄弟节点,上下是子节点
3373
- if (isVerticalLogicLayout(layout)) {
3374
- if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3375
- targetPath.push(dropTarget.target.children.length);
3376
- }
3377
- if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3378
- targetPath.push(dropTarget.target.children.length);
3379
- }
3380
- // 如果是左,位置不变,右则插入到下一个兄弟节点
3381
- if (dropTarget.detectResult === 'right') {
3382
- targetPath = Path.next(targetPath);
3383
- }
3384
- }
3385
- // 水平布局/标准布局:上下是兄弟节点,左右是子节点
3386
- if (isHorizontalLogicLayout(layout)) {
3387
- if (dropTarget.detectResult === 'right') {
3388
- targetPath.push(dropTarget.target.children.length);
3389
- }
3390
- if (dropTarget.detectResult === 'left') {
3391
- targetPath.push(dropTarget.target.children.length);
3392
- }
3393
- // 如果是上,位置不变,下插入到下一个兄弟节点
3394
- if (dropTarget.detectResult === 'bottom') {
3395
- targetPath = Path.next(targetPath);
3396
- }
3397
- }
3398
- // 缩进布局:上下是兄弟节点,左右是子节点,但上(左上/右上),探测到上是子节点,下则位置不变,反之同理。
3399
- if (isIndentedLayout(layout)) {
3400
- if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3401
- targetPath = Path.next(targetPath);
3402
- }
3403
- if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3404
- targetPath = Path.next(targetPath);
3405
- }
3406
- if (isLeftLayout(layout) && dropTarget.detectResult === 'left') {
3407
- targetPath.push(dropTarget.target.children.length);
3408
- }
3409
- if (isRightLayout(layout) && dropTarget.detectResult === 'right') {
3410
- targetPath.push(dropTarget.target.children.length);
3411
- }
3412
- }
3413
- return targetPath;
3414
- };
3415
- const updateRightNodeCount = (board, activeComponent, targetComponent, detectResult) => {
3416
- let rightNodeCount;
3417
- const mindmapElement = findUpElement(targetComponent.node.origin).root;
3418
- const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
3419
- const activeIndex = mindmapComponent?.root.children.indexOf(activeComponent.node);
3420
- const targetIndex = mindmapComponent?.root.children.indexOf(targetComponent.node);
3421
- const isActiveOnRight = activeIndex !== -1 && activeIndex <= activeComponent.parent.origin.rightNodeCount - 1;
3422
- const isTargetOnRight = targetComponent.parent && targetIndex !== -1 && targetIndex <= targetComponent.parent.origin.rightNodeCount - 1;
3423
- const isBothOnRight = isActiveOnRight && isTargetOnRight;
3424
- const rootChildCount = mindmapComponent.root.children?.length;
3425
- const rootRightNodeCount = mindmapComponent?.root.origin.rightNodeCount;
3426
- if (!isBothOnRight) {
3427
- if (isActiveOnRight) {
3428
- rightNodeCount = rootChildCount < rootRightNodeCount ? rootChildCount - 1 : rootRightNodeCount - 1;
3429
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, activeComponent.parent.origin));
3430
- }
3431
- if (isTargetOnRight && detectResult !== 'right') {
3432
- rightNodeCount = rootChildCount < rootRightNodeCount ? rootRightNodeCount : rootRightNodeCount + 1;
3433
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.parent.origin));
3434
- }
3435
- //二级子节点拖动到根节点左侧
3436
- if (targetComponent.node.origin.isRoot && detectResult === 'left' && activeIndex === -1) {
3437
- rightNodeCount = rootChildCount;
3438
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.element));
3439
- }
3440
- }
3441
- };
3442
- const IS_DRAGGING = new WeakMap();
3443
- const isDragging = (board) => {
3444
- return !!IS_DRAGGING.get(board);
3445
- };
3446
- const setIsDragging = (board, state) => {
3447
- IS_DRAGGING.set(board, state);
3448
- };
3449
3558
 
3450
3559
  const buildClipboardData = (board, selectedElements) => {
3451
3560
  let result = [];
3452
- const selectedMindNodes = selectedElements.map(value => MindElement.getNode(value));
3453
- const nodesRectangle = getRectangleByElements(board, selectedElements, true);
3454
- selectedElements.forEach((node, index) => {
3561
+ // get overall abstract
3562
+ const overallAbstracts = getOverallAbstracts(board, selectedElements);
3563
+ // keep correct order
3564
+ const newSelectedElements = selectedElements.filter((value) => !overallAbstracts.includes(value));
3565
+ newSelectedElements.push(...overallAbstracts);
3566
+ // get correct start and end in selected elements
3567
+ function getCorrectStartEnd(abstract) {
3568
+ const parent = MindElement.getParent(abstract);
3569
+ const startElement = parent.children[abstract.start];
3570
+ const index = selectedElements.indexOf(startElement);
3571
+ return { start: index, end: index + (abstract.end - abstract.start) };
3572
+ }
3573
+ const selectedMindNodes = newSelectedElements.map(value => MindElement.getNode(value));
3574
+ const nodesRectangle = getRectangleByElements(board, newSelectedElements, true);
3575
+ newSelectedElements.forEach((element, index) => {
3576
+ // handle relative location
3455
3577
  const nodeRectangle = getRectangleByNode(selectedMindNodes[index]);
3456
- result.push({
3457
- ...node,
3458
- points: [[nodeRectangle.x - nodesRectangle.x, nodeRectangle.y - nodesRectangle.y]]
3459
- });
3578
+ const points = [[nodeRectangle.x - nodesRectangle.x, nodeRectangle.y - nodesRectangle.y]];
3579
+ // handle invalid abstract
3580
+ if (AbstractNode.isAbstract(element) && overallAbstracts.includes(element)) {
3581
+ const { start, end } = getCorrectStartEnd(element);
3582
+ result.push({
3583
+ ...element,
3584
+ points,
3585
+ start,
3586
+ end
3587
+ });
3588
+ }
3589
+ else {
3590
+ if (AbstractNode.isAbstract(element)) {
3591
+ let newElement = { ...element, points };
3592
+ delete newElement.start;
3593
+ delete newElement.end;
3594
+ result.push(newElement);
3595
+ }
3596
+ else {
3597
+ result.push({
3598
+ ...element,
3599
+ points: points
3600
+ });
3601
+ }
3602
+ }
3460
3603
  });
3461
3604
  return result;
3462
3605
  };
@@ -3482,17 +3625,22 @@ const insertClipboardData = (board, elements, targetPoint) => {
3482
3625
  let newElement, path;
3483
3626
  const selectedElements = getSelectedElements(board);
3484
3627
  let newELements = [];
3628
+ const hasTargetParent = selectedElements.length === 1;
3629
+ const targetParent = selectedElements[0];
3630
+ const targetParentPath = targetParent && PlaitBoard.findPath(board, targetParent);
3631
+ const nonAbstractChildrenLength = targetParent && getNonAbstractChildren(targetParent).length;
3485
3632
  elements.forEach((item, index) => {
3486
3633
  newElement = copyNewNode(item);
3487
- if (selectedElements.length === 1) {
3634
+ if (hasTargetParent) {
3488
3635
  if (item.isRoot) {
3489
3636
  newElement = transformRootToNode(board, newElement);
3490
3637
  }
3491
- if (AbstractNode.isAbstract(item)) {
3492
- newElement = transformAbstractToNode(newElement);
3638
+ // handle abstract start and end
3639
+ if (AbstractNode.isAbstract(newElement)) {
3640
+ newElement.start = newElement.start + nonAbstractChildrenLength;
3641
+ newElement.end = newElement.end + nonAbstractChildrenLength;
3493
3642
  }
3494
- const selectedElementPath = PlaitBoard.findPath(board, selectedElements[0]);
3495
- path = selectedElementPath.concat((selectedElements[0].children || []).length + index);
3643
+ path = [...targetParentPath, nonAbstractChildrenLength + index];
3496
3644
  }
3497
3645
  else {
3498
3646
  const point = [targetPoint[0] + item.points[0][0], targetPoint[1] + item.points[0][1]];
@@ -3552,7 +3700,7 @@ const withAbstract = (board) => {
3552
3700
  event.preventDefault();
3553
3701
  const abstractComponent = PlaitElement.getComponent(activeAbstractElement);
3554
3702
  const element = abstractComponent.element;
3555
- const nodeLayout = MindQueries.getCorrectLayoutByElement(activeAbstractElement);
3703
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, activeAbstractElement);
3556
3704
  const isHorizontal = isHorizontalLayout(nodeLayout);
3557
3705
  const parentElement = MindElement.getParent(element);
3558
3706
  let children = parentElement.children;
@@ -3655,7 +3803,11 @@ const withMind = (board) => {
3655
3803
  board.isHitSelection = (element, range) => {
3656
3804
  if (MindElement.isMindElement(board, element) && board.selection) {
3657
3805
  const client = getRectangleByNode(MindElement.getNode(element));
3658
- return RectangleClient.isHit(RectangleClient.toRectangleClient([range.anchor, range.focus]), client);
3806
+ const isHit = RectangleClient.isHit(RectangleClient.toRectangleClient([range.anchor, range.focus]), client);
3807
+ if (isHit && MindElement.hasEmojis(element) && Selection.isCollapsed(range) && isHitEmojis(board, element, range.anchor)) {
3808
+ return false;
3809
+ }
3810
+ return isHit;
3659
3811
  }
3660
3812
  return isHitSelection(element, range);
3661
3813
  };
@@ -3692,7 +3844,8 @@ const withMind = (board) => {
3692
3844
  if (shouldChangeRightNodeCount(selectedElement)) {
3693
3845
  changeRightNodeCount(board, selectedElementPath.slice(0, 1), 1);
3694
3846
  }
3695
- insertSiblingElementHandleAbstract(board, selectedElement);
3847
+ const abstractRefs = insertElementHandleAbstract(board, Path.next(selectedElementPath));
3848
+ MindTransforms.setAbstractsByRefs(board, abstractRefs);
3696
3849
  insertMindElement(board, selectedElement, findNewSiblingNodePath(board, selectedElement));
3697
3850
  }
3698
3851
  return;
@@ -3700,31 +3853,32 @@ const withMind = (board) => {
3700
3853
  if (hotkeys.isDeleteBackward(event) || hotkeys.isDeleteForward(event)) {
3701
3854
  event.preventDefault();
3702
3855
  deleteSelectedELements(board, selectedElements);
3703
- let lastNode = null;
3704
- const firstLevelElements = filterChildElement(selectedElements);
3856
+ let activeElement;
3857
+ const firstLevelElements = getFirstLevelElement(selectedElements);
3858
+ if (AbstractNode.isAbstract(firstLevelElements[0])) {
3859
+ const parent = MindElement.getParent(firstLevelElements[0]);
3860
+ activeElement = parent.children[firstLevelElements[0].start];
3861
+ }
3705
3862
  const firstElement = firstLevelElements[0];
3706
- const firstComponent = PlaitElement.getComponent(firstElement);
3707
- const nodeIndex = firstComponent?.parent?.children.findIndex(item => item.origin.id === firstElement.id);
3708
- const isSameParent = firstLevelElements.every(element => {
3709
- return findParentElement(element) && findParentElement(firstLevelElements[0]) === findParentElement(element);
3863
+ const firstElementParent = MindElement.findParent(firstElement);
3864
+ const hasSameParent = firstLevelElements.every(element => {
3865
+ return MindElement.findParent(element) === firstElementParent;
3710
3866
  });
3711
- if (isSameParent) {
3712
- const childCount = firstComponent.parent?.children.length - firstLevelElements.length;
3713
- if (childCount === 0) {
3714
- lastNode = firstComponent?.parent;
3867
+ if (firstElementParent && hasSameParent && !activeElement) {
3868
+ const firstElementIndex = firstElementParent.children.indexOf(firstElement);
3869
+ const childrenCount = firstElementParent.children.length;
3870
+ // active parent element
3871
+ if (childrenCount === firstLevelElements.length) {
3872
+ activeElement = firstElementParent;
3715
3873
  }
3716
- else if (nodeIndex === 0) {
3717
- lastNode = firstComponent?.parent.children[firstLevelElements.length];
3718
- }
3719
- else if (nodeIndex > 0) {
3720
- lastNode = firstComponent?.parent.children[nodeIndex - 1];
3874
+ else {
3875
+ if (firstElementIndex > 0) {
3876
+ activeElement = firstElementParent.children[firstElementIndex - 1];
3877
+ }
3721
3878
  }
3722
3879
  }
3723
- if (firstLevelElements.length === 1 && AbstractNode.isAbstract(firstElement)) {
3724
- lastNode = firstComponent.parent.children[firstElement.start];
3725
- }
3726
- if (lastNode) {
3727
- addSelectedElement(board, lastNode.origin);
3880
+ if (activeElement) {
3881
+ addSelectedElement(board, activeElement);
3728
3882
  }
3729
3883
  return;
3730
3884
  }
@@ -3751,8 +3905,8 @@ const withMind = (board) => {
3751
3905
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
3752
3906
  board.children
3753
3907
  .filter(value => PlaitMind.isMind(value))
3754
- .forEach(mindmap => {
3755
- depthFirstRecursion(mindmap, node => {
3908
+ .forEach(mindMap => {
3909
+ depthFirstRecursion(mindMap, node => {
3756
3910
  if (!PlaitBoard.hasBeenTextEditing(board) && hitMindElement(board, point, node)) {
3757
3911
  enterNodeEditing(node);
3758
3912
  }
@@ -3764,7 +3918,7 @@ const withMind = (board) => {
3764
3918
  dblclick(event);
3765
3919
  };
3766
3920
  board.setFragment = (data) => {
3767
- const selectedElements = filterChildElement(getSelectedElements(board));
3921
+ const selectedElements = getFirstLevelElement(getSelectedElements(board));
3768
3922
  if (selectedElements.length) {
3769
3923
  const elements = buildClipboardData(board, selectedElements);
3770
3924
  setClipboardData(data, elements);
@@ -3838,5 +3992,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3838
3992
  * Generated bundle index. Do not edit.
3839
3993
  */
3840
3994
 
3841
- 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, withExtendMind, withMind };
3995
+ export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, BRANCH_COLORS, BRANCH_WIDTH, DefaultAbstractNodeStyle, DefaultNodeStyle, DefaultRootStyle, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, LayoutDirection, LayoutDirectionsMap, MindElement, MindElementShape, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindQueries, MindTransforms, NODE_MIN_WIDTH, PRIMARY_COLOR, PlaitMind, PlaitMindComponent, QUICK_INSERT_CIRCLE_COLOR, QUICK_INSERT_CIRCLE_OFFSET, QUICK_INSERT_INNER_CROSS_COLOR, ROOT_TOPIC_FONT_SIZE, STROKE_WIDTH, TOPIC_COLOR, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, TRANSPARENT, canSetAbstract, changeRightNodeCount, copyNewNode, correctLayoutByDirection, createDefaultMindMapElement, createMindElement, deleteElementHandleAbstract, deleteSelectedELements, directionCorrector, directionDetector, divideElementByParent, drawCurvePlaceholderDropNodeG, drawIndentNodeG, drawPlaceholderDropNodeG, drawStraightDropNodeG, extractNodesText, findLastChild, findLocationLeftIndex, findUpElement, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultBranchColor, getDefaultBranchColorByIndex, getDefaultLayout, getEmojiFontSize, getEmojiForeignRectangle, getEmojiRectangle, getEmojisWidthHeight, getFirstLevelElement, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getNextBranchColor, getOverallAbstracts, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getShapeByElement, getStrokeByMindElement, handleTouchedAbstract, hitMindElement, insertElementHandleAbstract, insertMindElement, isChildElement, isChildRight, isChildUp, isCorrectLayout, isHitEmojis, isMixedLayout, isSetAbstract, isVirtualKey, readjustmentDropTarget, separateChildren, shouldChangeRightNodeCount, transformAbstractToNode, transformNodeToRoot, transformRootToNode, withExtendMind, withMind };
3842
3996
  //# sourceMappingURL=plait-mind.mjs.map