@plait/mind 0.2.0-next.7 → 0.2.0-next.9

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 (98) 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 +4 -3
  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 +14 -14
  16. package/esm2020/draw/link/abstract-link.mjs +6 -4
  17. package/esm2020/draw/link/logic-link.mjs +7 -7
  18. package/esm2020/draw/link.mjs +6 -5
  19. package/esm2020/draw/richtext.mjs +7 -7
  20. package/esm2020/draw/shape.mjs +4 -4
  21. package/esm2020/drawer/quick-insert.drawer.mjs +11 -12
  22. package/esm2020/interfaces/abstract.mjs +1 -1
  23. package/esm2020/interfaces/element.mjs +6 -1
  24. package/esm2020/interfaces/node.mjs +1 -1
  25. package/esm2020/interfaces/options.mjs +2 -0
  26. package/esm2020/layout-option.mjs +5 -5
  27. package/esm2020/mind.component.mjs +2 -2
  28. package/esm2020/node.component.mjs +15 -13
  29. package/esm2020/plugins/emoji/emoji.drawer.mjs +3 -4
  30. package/esm2020/plugins/emoji/emoji.mjs +7 -3
  31. package/esm2020/plugins/emoji/index.mjs +1 -2
  32. package/esm2020/plugins/with-abstract.mjs +2 -2
  33. package/esm2020/plugins/with-dnd.mjs +12 -117
  34. package/esm2020/plugins/with-extend-mind.mjs +11 -0
  35. package/esm2020/plugins/with-mind.mjs +9 -7
  36. package/esm2020/public-api.mjs +2 -1
  37. package/esm2020/queries/get-available-sublayouts-by-element.mjs +4 -9
  38. package/esm2020/queries/get-branch-layouts.mjs +4 -4
  39. package/esm2020/queries/get-correct-layout-by-element.mjs +28 -31
  40. package/esm2020/queries/get-layout-by-element.mjs +10 -8
  41. package/esm2020/queries/index.mjs +1 -3
  42. package/esm2020/transforms/abstract-node.mjs +68 -0
  43. package/esm2020/transforms/index.mjs +6 -2
  44. package/esm2020/transforms/layout.mjs +3 -3
  45. package/esm2020/transforms/node.mjs +2 -2
  46. package/esm2020/utils/abstract/common.mjs +90 -70
  47. package/esm2020/utils/abstract/resize.mjs +3 -3
  48. package/esm2020/utils/clipboard.mjs +54 -14
  49. package/esm2020/utils/direction-corrector.mjs +11 -11
  50. package/esm2020/utils/dnd.mjs +118 -0
  51. package/esm2020/utils/draw-placeholder.mjs +5 -5
  52. package/esm2020/utils/drop-target-corrector.mjs +11 -10
  53. package/esm2020/utils/layout.mjs +1 -1
  54. package/esm2020/utils/mind.mjs +23 -58
  55. package/esm2020/utils/node-space.mjs +5 -5
  56. package/esm2020/utils/node-style/branch.mjs +33 -6
  57. package/esm2020/utils/node-style/node.mjs +6 -5
  58. package/esm2020/utils/path.mjs +4 -3
  59. package/esm2020/utils/shape.mjs +3 -3
  60. package/fesm2015/plait-mind.mjs +724 -602
  61. package/fesm2015/plait-mind.mjs.map +1 -1
  62. package/fesm2020/plait-mind.mjs +726 -600
  63. package/fesm2020/plait-mind.mjs.map +1 -1
  64. package/interfaces/abstract.d.ts +3 -0
  65. package/interfaces/element.d.ts +5 -2
  66. package/interfaces/options.d.ts +4 -0
  67. package/layout-option.d.ts +2 -1
  68. package/mind.component.d.ts +2 -2
  69. package/node.component.d.ts +5 -4
  70. package/package.json +1 -1
  71. package/plugins/emoji/emoji.d.ts +2 -1
  72. package/plugins/emoji/emoji.drawer.d.ts +3 -3
  73. package/plugins/emoji/index.d.ts +0 -1
  74. package/plugins/with-dnd.d.ts +0 -9
  75. package/plugins/with-extend-mind.d.ts +10 -0
  76. package/plugins/with-mind.d.ts +1 -1
  77. package/public-api.d.ts +1 -0
  78. package/queries/get-available-sublayouts-by-element.d.ts +2 -6
  79. package/queries/get-branch-layouts.d.ts +2 -1
  80. package/queries/get-correct-layout-by-element.d.ts +2 -1
  81. package/queries/index.d.ts +3 -4
  82. package/styles/styles.scss +0 -3
  83. package/transforms/abstract-node.d.ts +6 -0
  84. package/transforms/index.d.ts +3 -0
  85. package/utils/abstract/common.d.ts +6 -8
  86. package/utils/direction-corrector.d.ts +2 -1
  87. package/utils/dnd.d.ts +16 -0
  88. package/utils/drop-target-corrector.d.ts +2 -1
  89. package/utils/mind.d.ts +3 -3
  90. package/utils/node-space.d.ts +3 -2
  91. package/utils/node-style/branch.d.ts +3 -0
  92. package/utils/shape.d.ts +2 -3
  93. package/constants/node.d.ts +0 -17
  94. package/esm2020/constants/node.mjs +0 -19
  95. package/esm2020/plugins/emoji/with-mind-emoji.mjs +0 -8
  96. package/esm2020/queries/get-layout-parent-by-element.mjs +0 -17
  97. package/plugins/emoji/with-mind-emoji.d.ts +0 -8
  98. package/queries/get-layout-parent-by-element.d.ts +0 -8
@@ -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, CLIP_BOARD_FORMAT_KEY, BOARD_TO_HOST, 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;
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;
83
94
  }
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);
96
- }
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;
99
+ }
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
+ }
100
111
  }
101
- if (parentComponent && parentComponent.node.origin.isRoot) {
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,16 +121,13 @@ 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
132
  let parent = findParentElement(element);
125
133
  while (parent) {
@@ -131,15 +139,10 @@ const getBranchLayouts = (element) => {
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) => {
142
+ const getAvailableSubLayoutsByElement = (board, element) => {
140
143
  const parentElement = findParentElement(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 = MindElement.getParent(element);
270
+ if (AbstractNode.isAbstract(element)) {
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
@@ -257,6 +348,11 @@ const MindElement = {
257
348
  return element.data.emojis;
258
349
  }
259
350
  };
351
+ var MindElementShape;
352
+ (function (MindElementShape) {
353
+ MindElementShape["roundRectangle"] = "round-rectangle";
354
+ MindElementShape["underline"] = "underline";
355
+ })(MindElementShape || (MindElementShape = {}));
260
356
 
261
357
  const MindNode = {
262
358
  get(root, path) {
@@ -311,109 +407,6 @@ var AbstractResizeState;
311
407
  AbstractResizeState["end"] = "end";
312
408
  })(AbstractResizeState || (AbstractResizeState = {}));
313
409
 
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
410
  function enterNodeEditing(element) {
418
411
  const component = ELEMENT_TO_COMPONENT.get(element);
419
412
  component.startEditText(false, false);
@@ -433,22 +426,146 @@ const separateChildren = (parentElement) => {
433
426
  leftChildren.push(child);
434
427
  continue;
435
428
  }
436
- if (i < rightNodeCount) {
437
- rightChildren.push(child);
429
+ if (i < rightNodeCount) {
430
+ rightChildren.push(child);
431
+ }
432
+ else {
433
+ leftChildren.push(child);
434
+ }
435
+ }
436
+ return { leftChildren, rightChildren };
437
+ };
438
+ const isSetAbstract = (element) => {
439
+ const parent = MindElement.getParent(element);
440
+ return !!getCorrespondingAbstract(parent, element);
441
+ };
442
+ const canSetAbstract = (element) => {
443
+ return !PlaitElement.isRootElement(element) && !AbstractNode.isAbstract(element) && !isSetAbstract(element);
444
+ };
445
+ const getCorrespondingAbstract = (parent, element) => {
446
+ if (!parent)
447
+ return undefined;
448
+ const elementIndex = parent.children.indexOf(element);
449
+ return parent.children.find(child => {
450
+ return AbstractNode.isAbstract(child) && elementIndex >= child.start && elementIndex <= child.end;
451
+ });
452
+ };
453
+ const getBehindAbstracts = (parent, element) => {
454
+ const index = parent.children.indexOf(element);
455
+ return parent.children.filter(child => AbstractNode.isAbstract(child) && child.start > index);
456
+ };
457
+ const getOverallAbstracts = (board, elements) => {
458
+ const overallAbstracts = [];
459
+ elements
460
+ .filter(value => !AbstractNode.isAbstract(value) && !PlaitMind.isMind(value))
461
+ .forEach(value => {
462
+ const parent = MindElement.getParent(value);
463
+ const abstract = getCorrespondingAbstract(parent, value);
464
+ if (abstract && overallAbstracts.indexOf(abstract) === -1) {
465
+ const { start, end } = abstract;
466
+ const parent = MindElement.getParent(value);
467
+ const isOverall = parent.children.slice(start, end + 1).every(includedElement => elements.indexOf(includedElement) > -1);
468
+ if (isOverall) {
469
+ overallAbstracts.push(abstract);
470
+ }
471
+ }
472
+ });
473
+ return overallAbstracts;
474
+ };
475
+ const insertElementHandleAbstract = (board, path,
476
+ //由此区分拖拽和新增到概要概括最后一个节点
477
+ isExtendPreviousNode = true, abstractRefs = new Map()) => {
478
+ const parent = PlaitNode.parent(board, path);
479
+ const hasPreviousNode = path[path.length - 1] !== 0;
480
+ let behindAbstracts;
481
+ if (!hasPreviousNode) {
482
+ behindAbstracts = parent.children.filter(child => AbstractNode.isAbstract(child));
483
+ }
484
+ else {
485
+ const selectedElement = PlaitNode.get(board, Path.previous(path));
486
+ behindAbstracts = getBehindAbstracts(parent, selectedElement);
487
+ }
488
+ if (behindAbstracts.length) {
489
+ behindAbstracts.forEach(abstract => {
490
+ let newProperties = abstractRefs.get(abstract);
491
+ if (!newProperties) {
492
+ newProperties = { start: 0, end: 0 };
493
+ abstractRefs.set(abstract, newProperties);
494
+ }
495
+ newProperties.start = newProperties.start + 1;
496
+ newProperties.end = newProperties.end + 1;
497
+ });
498
+ }
499
+ if (!hasPreviousNode) {
500
+ return abstractRefs;
501
+ }
502
+ const selectedElement = PlaitNode.get(board, Path.previous(path));
503
+ const correspondingAbstract = getCorrespondingAbstract(parent, selectedElement);
504
+ const isDragToLast = !isExtendPreviousNode && correspondingAbstract && correspondingAbstract.end === path[path.length - 1] - 1;
505
+ if (correspondingAbstract && !isDragToLast) {
506
+ let newProperties = abstractRefs.get(correspondingAbstract);
507
+ if (!newProperties) {
508
+ newProperties = { start: 0, end: 0 };
509
+ abstractRefs.set(correspondingAbstract, newProperties);
510
+ }
511
+ newProperties.end = newProperties.end + 1;
512
+ }
513
+ return abstractRefs;
514
+ };
515
+ const deleteElementHandleAbstract = (board, deletableElements, abstractRefs = new Map()) => {
516
+ deletableElements.forEach(node => {
517
+ if (!PlaitMind.isMind(node)) {
518
+ const parent = PlaitNode.parent(board, PlaitBoard.findPath(board, node));
519
+ const behindAbstracts = getBehindAbstracts(parent, node).filter(abstract => !deletableElements.includes(abstract));
520
+ if (behindAbstracts.length) {
521
+ behindAbstracts.forEach(abstract => {
522
+ let newProperties = abstractRefs.get(abstract);
523
+ if (!newProperties) {
524
+ newProperties = { start: 0, end: 0 };
525
+ abstractRefs.set(abstract, newProperties);
526
+ }
527
+ newProperties.start = newProperties.start - 1;
528
+ newProperties.end = newProperties.end - 1;
529
+ });
530
+ }
531
+ const correspondingAbstract = getCorrespondingAbstract(parent, node);
532
+ if (correspondingAbstract && !deletableElements.includes(correspondingAbstract)) {
533
+ let newProperties = abstractRefs.get(correspondingAbstract);
534
+ if (!newProperties) {
535
+ newProperties = { start: 0, end: 0 };
536
+ abstractRefs.set(correspondingAbstract, newProperties);
537
+ }
538
+ newProperties.end = newProperties.end - 1;
539
+ }
540
+ }
541
+ });
542
+ return abstractRefs;
543
+ };
544
+
545
+ const setAbstractsByRefs = (board, abstractRefs) => {
546
+ abstractRefs.forEach((newProperty, element) => {
547
+ const start = element.start + newProperty.start;
548
+ const end = element.end + newProperty.end;
549
+ const path = PlaitBoard.findPath(board, element);
550
+ if (start > end) {
551
+ Transforms.removeNode(board, path);
438
552
  }
439
553
  else {
440
- leftChildren.push(child);
554
+ Transforms.setNode(board, { start, end }, path);
441
555
  }
442
- }
443
- return { leftChildren, rightChildren };
444
- };
445
- const isSetAbstract = (element) => {
446
- return !!getCorrespondingAbstract(element);
556
+ });
447
557
  };
448
- const canSetAbstract = (element) => {
449
- return !PlaitElement.isRootElement(element) && !AbstractNode.isAbstract(element) && !isSetAbstract(element);
558
+ const setAbstractByStandardLayout = (board, element) => {
559
+ const rightNodeCount = element.rightNodeCount;
560
+ const abstract = element.children.find(child => {
561
+ return AbstractNode.isAbstract(child) && child.end >= rightNodeCount && child.start < rightNodeCount;
562
+ });
563
+ if (abstract) {
564
+ const path = PlaitBoard.findPath(board, abstract);
565
+ Transforms.setNode(board, { end: rightNodeCount - 1 }, path);
566
+ }
450
567
  };
451
- const setAbstract = (board, elements) => {
568
+ const insertAbstract = (board, elements) => {
452
569
  let elementGroup = filterChildElement(elements);
453
570
  const { parentElements, abstractIncludedGroups } = divideElementByParent(elementGroup);
454
571
  abstractIncludedGroups.forEach((group, index) => {
@@ -478,52 +595,107 @@ const setAbstractByElements = (board, groupParent, group) => {
478
595
  };
479
596
  const insertAbstractNode = (board, path, start, end) => {
480
597
  const mindElement = createMindElement('概要', 28, 20, {
481
- strokeColor: GRAY_COLOR,
482
- branchColor: GRAY_COLOR
598
+ strokeColor: DefaultAbstractNodeStyle.strokeColor,
599
+ strokeWidth: DefaultAbstractNodeStyle.branchWidth,
600
+ branchColor: DefaultAbstractNodeStyle.branchColor,
601
+ branchWidth: DefaultAbstractNodeStyle.branchWidth
483
602
  });
484
603
  mindElement.start = start;
485
604
  mindElement.end = end;
486
605
  Transforms.insertNode(board, mindElement, path);
487
606
  };
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);
607
+
608
+ const setLayout = (board, layout, path) => {
609
+ correctLogicLayoutNode(board, layout, path);
610
+ const element = PlaitNode.get(board, path);
611
+ if (PlaitMind.isMind(element) && isStandardLayout(layout)) {
612
+ MindTransforms.setAbstractByStandardLayout(board, element);
496
613
  }
614
+ Transforms.setNode(board, { layout }, path);
497
615
  };
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
- });
506
- };
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);
616
+ const correctLogicLayoutNode = (board, layout, path) => {
617
+ const node = PlaitNode.get(board, path);
618
+ if (node && layout) {
619
+ node.children?.forEach((value, index) => {
620
+ if (value.layout) {
621
+ if ((isHorizontalLogicLayout(layout) && isVerticalLogicLayout(value.layout)) ||
622
+ (isVerticalLogicLayout(layout) && isHorizontalLogicLayout(value.layout))) {
623
+ Transforms.setNode(board, { layout: null }, [...path, index]);
624
+ }
625
+ if (value.children?.length) {
626
+ correctLogicLayoutNode(board, layout, [...path, index]);
627
+ }
628
+ }
629
+ });
630
+ }
511
631
  };
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));
632
+
633
+ const setTopic = (board, element, topic, width, height) => {
634
+ const newElement = {
635
+ data: { topic },
636
+ width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
637
+ height: height / board.viewport.zoom
638
+ };
639
+ if (MindElement.hasEmojis(element)) {
640
+ newElement.data.emojis = element.data.emojis;
517
641
  }
518
- const abstracts = getBehindAbstracts(selectedElement);
519
- if (abstracts.length) {
520
- moveAbstractPosition(board, abstracts, 1);
642
+ const path = PlaitBoard.findPath(board, element);
643
+ Transforms.setNode(board, newElement, path);
644
+ };
645
+ const setTopicSize = (board, element, width, height) => {
646
+ const newElement = {
647
+ width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
648
+ height: height / board.viewport.zoom
649
+ };
650
+ const path = PlaitBoard.findPath(board, element);
651
+ Transforms.setNode(board, newElement, path);
652
+ };
653
+ const addEmoji = (board, element, emojiItem) => {
654
+ const emojis = element.data.emojis || [];
655
+ const newEmojis = [...emojis];
656
+ newEmojis.push(emojiItem);
657
+ const newElement = {
658
+ data: { topic: element.data.topic, emojis: newEmojis }
659
+ };
660
+ const path = PlaitBoard.findPath(board, element);
661
+ Transforms.setNode(board, newElement, path);
662
+ };
663
+ const removeEmoji = (board, element, emojiItem) => {
664
+ const emojis = element.data.emojis.filter(value => value !== emojiItem);
665
+ const newElement = {
666
+ data: { topic: element.data.topic }
667
+ };
668
+ if (emojis.length > 0) {
669
+ newElement.data.emojis = emojis;
521
670
  }
671
+ const path = PlaitBoard.findPath(board, element);
672
+ Transforms.setNode(board, newElement, path);
522
673
  };
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));
674
+ const replaceEmoji = (board, element, oldEmoji, newEmoji) => {
675
+ const newElement = {
676
+ data: { topic: element.data.topic }
677
+ };
678
+ const newEmojis = element.data.emojis.map(value => {
679
+ if (value === oldEmoji) {
680
+ return newEmoji;
681
+ }
682
+ return value;
526
683
  });
684
+ newElement.data.emojis = newEmojis;
685
+ const path = PlaitBoard.findPath(board, element);
686
+ Transforms.setNode(board, newElement, path);
687
+ };
688
+
689
+ const MindTransforms = {
690
+ setLayout,
691
+ setTopic,
692
+ setTopicSize,
693
+ addEmoji,
694
+ removeEmoji,
695
+ replaceEmoji,
696
+ insertAbstract,
697
+ setAbstractsByRefs,
698
+ setAbstractByStandardLayout
527
699
  };
528
700
 
529
701
  function findParentElement(element) {
@@ -663,13 +835,13 @@ const shouldChangeRightNodeCount = (selectedElement) => {
663
835
  return false;
664
836
  };
665
837
  const createDefaultMindMapElement = (point, rightNodeCount, layout) => {
666
- const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle, layout });
838
+ const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { shape: MindElementShape.roundRectangle, layout });
667
839
  root.rightNodeCount = rightNodeCount;
668
840
  root.isRoot = true;
669
841
  root.type = 'mindmap';
670
842
  root.points = [point];
671
843
  const children = [1, 1, 1].map(() => {
672
- return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindNodeShape.roundRectangle });
844
+ return createMindElement('新建节点', 56, TEXT_DEFAULT_HEIGHT, { shape: MindElementShape.roundRectangle });
673
845
  });
674
846
  root.children = children;
675
847
  return root;
@@ -706,11 +878,14 @@ const createMindElement = (text, width, height, options) => {
706
878
  if (options.branchColor) {
707
879
  newElement.branchColor = options.branchColor;
708
880
  }
881
+ if (!isNullOrUndefined(options.branchWidth)) {
882
+ newElement.branchWidth = options.branchWidth;
883
+ }
709
884
  return newElement;
710
885
  };
711
886
  // layoutLevel 用来表示插入兄弟节点还是子节点
712
887
  const insertMindElement = (board, inheritNode, path) => {
713
- let fill, strokeColor, strokeWidth, shape = MindNodeShape.roundRectangle;
888
+ let fill, strokeColor, strokeWidth, shape = MindElementShape.roundRectangle;
714
889
  if (!inheritNode.isRoot) {
715
890
  fill = inheritNode.fill;
716
891
  strokeColor = inheritNode.strokeColor;
@@ -733,63 +908,23 @@ const findLastChild = (child) => {
733
908
  return result;
734
909
  };
735
910
  const deleteSelectedELements = (board, selectedElements) => {
736
- //翻转,从下到上修改,防止找不到 path
737
911
  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);
912
+ const abstractRefs = deleteElementHandleAbstract(board, deletableElements);
913
+ MindTransforms.setAbstractsByRefs(board, abstractRefs);
914
+ //翻转,从下到上修改,防止找不到 path
915
+ deletableElements
916
+ .map(element => {
917
+ const path = PlaitBoard.findPath(board, element);
784
918
  return () => {
785
- if (shouldChangeRightNodeCount(node)) {
786
- changeRightNodeCount(board, path.slice(0, path.length - 1), -1);
919
+ if (shouldChangeRightNodeCount(element)) {
920
+ changeRightNodeCount(board, path.slice(0, 1), -1);
787
921
  }
788
922
  Transforms.removeNode(board, path);
789
923
  };
924
+ })
925
+ .forEach(action => {
926
+ action();
790
927
  });
791
- abstractHandles.forEach(action => action());
792
- deletableHandles.forEach(action => action());
793
928
  };
794
929
  const divideElementByParent = (elements) => {
795
930
  const abstractIncludedGroups = [];
@@ -820,9 +955,12 @@ const getNodeShapeByElement = (element) => {
820
955
  }
821
956
  parent = findParentElement(parent);
822
957
  }
823
- return MindNodeShape.roundRectangle;
958
+ return MindElementShape.roundRectangle;
824
959
  };
825
960
 
961
+ /**
962
+ * Processing of branch color, width, style, etc. of the mind node
963
+ */
826
964
  const getBranchColorByMindElement = (board, element) => {
827
965
  const ancestors = MindElement.getAncestors(board, element);
828
966
  ancestors.unshift(element);
@@ -834,19 +972,40 @@ const getBranchColorByMindElement = (board, element) => {
834
972
  const branch = ancestors[ancestors.length - 2];
835
973
  if (branch) {
836
974
  const index = root.children.indexOf(branch);
837
- const length = COLORS.length;
975
+ const length = BRANCH_COLORS.length;
838
976
  const remainder = index % length;
839
- return COLORS[remainder];
977
+ return BRANCH_COLORS[remainder];
840
978
  }
841
979
  else {
842
980
  throw new Error('root element should not have branch color');
843
981
  }
844
982
  };
983
+ const getBranchWidthByMindElement = (board, element) => {
984
+ const ancestors = MindElement.getAncestors(board, element);
985
+ ancestors.unshift(element);
986
+ const ancestor = ancestors.find(value => value.branchColor);
987
+ if (ancestor && ancestor.branchWidth) {
988
+ return ancestor.branchWidth;
989
+ }
990
+ return BRANCH_WIDTH;
991
+ };
992
+ const getAbstractBranchWidth = (board, element) => {
993
+ if (!isNullOrUndefined(element.branchWidth)) {
994
+ return element.branchWidth;
995
+ }
996
+ return DefaultAbstractNodeStyle.branchWidth;
997
+ };
998
+ const getAbstractBranchColor = (board, element) => {
999
+ if (element.branchColor) {
1000
+ return element.branchColor;
1001
+ }
1002
+ return DefaultAbstractNodeStyle.branchColor;
1003
+ };
845
1004
  const getNextBranchColor = (root) => {
846
1005
  const index = root.children.length;
847
- const length = COLORS.length;
1006
+ const length = BRANCH_COLORS.length;
848
1007
  const remainder = index % length;
849
- return COLORS[remainder];
1008
+ return BRANCH_COLORS[remainder];
850
1009
  };
851
1010
 
852
1011
  const getStrokeByMindElement = (board, element) => {
@@ -860,12 +1019,12 @@ const getStrokeByMindElement = (board, element) => {
860
1019
  const branch = ancestors[ancestors.length - 2];
861
1020
  if (branch) {
862
1021
  const index = root.children.indexOf(branch);
863
- const length = COLORS.length;
1022
+ const length = BRANCH_COLORS.length;
864
1023
  const remainder = index % length;
865
- return COLORS[remainder];
1024
+ return BRANCH_COLORS[remainder];
866
1025
  }
867
1026
  else {
868
- return ROOT_NODE_STROKE;
1027
+ return DefaultRootStyle.strokeColor;
869
1028
  }
870
1029
  };
871
1030
 
@@ -883,7 +1042,7 @@ function isVirtualKey(e) {
883
1042
 
884
1043
  function drawLink(board, node, child, defaultStroke = null, isHorizontal = true, needDrawUnderline = true) {
885
1044
  let beginX, beginY, endX, endY, beginNode = node, endNode = child;
886
- const layout = MindQueries.getCorrectLayoutByElement(node.origin);
1045
+ const layout = MindQueries.getCorrectLayoutByElement(board, node.origin);
887
1046
  if (isHorizontal) {
888
1047
  if (!isChildRight(node, child)) {
889
1048
  beginNode = child;
@@ -895,7 +1054,7 @@ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true,
895
1054
  endY = endNode.y + endNode.height / 2;
896
1055
  if (node.parent &&
897
1056
  isIndentedLayout(MindQueries.getLayoutByElement(node.parent?.origin)) &&
898
- getNodeShapeByElement(node.origin) === MindNodeShape.underline) {
1057
+ getNodeShapeByElement(node.origin) === MindElementShape.underline) {
899
1058
  if (isChildRight(node, child)) {
900
1059
  beginY = node.y + node.height - node.vGap;
901
1060
  }
@@ -970,7 +1129,7 @@ function drawLink(board, node, child, defaultStroke = null, isHorizontal = true,
970
1129
  curve = [...line, ...curve];
971
1130
  }
972
1131
  }
973
- if (needDrawUnderline && shape === MindNodeShape.underline) {
1132
+ if (needDrawUnderline && shape === MindElementShape.underline) {
974
1133
  if (child.left) {
975
1134
  const underline = [
976
1135
  [beginX - (beginNode.width - beginNode.hGap * 2), beginY],
@@ -1046,8 +1205,8 @@ const drawPlaceholderDropNodeG = (board, dropTarget, fakeDropNodeG) => {
1046
1205
  }
1047
1206
  };
1048
1207
  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);
1208
+ const parentNodeLayout = MindQueries.getCorrectLayoutByElement(board, parentComponent.node.origin);
1209
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.parent.origin);
1051
1210
  const strokeWidth = targetComponent.node.origin.branchWidth ? targetComponent.node.origin.branchWidth : STROKE_WIDTH;
1052
1211
  let fakeX = targetComponent.node.x, fakeY = targetRect.y - 30, fakeRectangleStartX = targetRect.x, fakeRectangleEndX = targetRect.x + 30, fakeRectangleStartY = fakeY, fakeRectangleEndY = fakeRectangleStartY + 12, width = 30;
1053
1212
  if (isLeftLayout(layout)) {
@@ -1178,8 +1337,8 @@ const drawStraightDropNodeG = (board, targetRect, detectResult, targetComponent,
1178
1337
  height,
1179
1338
  strokeWidth
1180
1339
  };
1181
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1182
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1340
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1341
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1183
1342
  if (!isMixedLayout(parentLayout, layout)) {
1184
1343
  // 构造一条直线
1185
1344
  let linePoints = [
@@ -1389,29 +1548,29 @@ const directionDetector = (targetNode, centerPoint) => {
1389
1548
  return null;
1390
1549
  };
1391
1550
 
1392
- const directionCorrector = (node, detectResults) => {
1393
- if (!node.origin.isRoot) {
1394
- const parentlayout = MindQueries.getCorrectLayoutByElement(node?.parent.origin);
1395
- if (isStandardLayout(parentlayout)) {
1551
+ const directionCorrector = (board, node, detectResults) => {
1552
+ if (!node.origin.isRoot && !AbstractNode.isAbstract(node.origin)) {
1553
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, node?.parent.origin);
1554
+ if (isStandardLayout(parentLayout)) {
1396
1555
  const idx = node.parent.children.findIndex(x => x === node);
1397
1556
  const isLeft = idx >= (node.parent.origin.rightNodeCount || 0);
1398
1557
  return getAllowedDirection(detectResults, [isLeft ? 'right' : 'left']);
1399
1558
  }
1400
- if (isLeftLayout(parentlayout)) {
1559
+ if (isLeftLayout(parentLayout)) {
1401
1560
  return getAllowedDirection(detectResults, ['right']);
1402
1561
  }
1403
- if (isRightLayout(parentlayout)) {
1562
+ if (isRightLayout(parentLayout)) {
1404
1563
  return getAllowedDirection(detectResults, ['left']);
1405
1564
  }
1406
- if (parentlayout === MindLayoutType.upward) {
1565
+ if (parentLayout === MindLayoutType.upward) {
1407
1566
  return getAllowedDirection(detectResults, ['bottom']);
1408
1567
  }
1409
- if (parentlayout === MindLayoutType.downward) {
1568
+ if (parentLayout === MindLayoutType.downward) {
1410
1569
  return getAllowedDirection(detectResults, ['top']);
1411
1570
  }
1412
1571
  }
1413
1572
  else {
1414
- const layout = MindQueries.getCorrectLayoutByElement(node?.origin);
1573
+ const layout = MindQueries.getCorrectLayoutByElement(board, node?.origin);
1415
1574
  if (isStandardLayout(layout)) {
1416
1575
  return getAllowedDirection(detectResults, ['top', 'bottom']);
1417
1576
  }
@@ -1442,17 +1601,18 @@ const getAllowedDirection = (detectResults, illegalDirections) => {
1442
1601
  };
1443
1602
 
1444
1603
  /* 根据布局调整 target 以及 direction */
1445
- const readjustmentDropTarget = (dropTarget) => {
1604
+ const readjustmentDropTarget = (board, dropTarget) => {
1446
1605
  const { target, detectResult } = dropTarget;
1447
1606
  const newDropTarget = { target, detectResult };
1448
1607
  const targetComponent = PlaitElement.getComponent(target);
1449
1608
  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);
1609
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1610
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1611
+ const children = getNonAbstractChildren(targetComponent.node);
1452
1612
  if (['right', 'left'].includes(dropTarget.detectResult)) {
1453
1613
  if (!isMixedLayout(parentLayout, layout)) {
1454
1614
  if (targetComponent.node.origin.isRoot) {
1455
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1615
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1456
1616
  // 标准布局,根节点
1457
1617
  if (isStandardLayout(layout)) {
1458
1618
  const rightNodeCount = targetComponent.node.origin.rightNodeCount;
@@ -1486,14 +1646,14 @@ const readjustmentDropTarget = (dropTarget) => {
1486
1646
  return newDropTarget;
1487
1647
  }
1488
1648
  // 剩下是水平布局的默认情况:插入最后一个子节点的下方
1489
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
1649
+ const lastChildNodeIndex = children.length - 1;
1490
1650
  newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1491
1651
  newDropTarget.detectResult = 'bottom';
1492
1652
  }
1493
1653
  else {
1494
1654
  // 处理左右布局下的混合布局
1495
1655
  if ([MindLayoutType.left, MindLayoutType.right].includes(parentLayout)) {
1496
- const layout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin);
1656
+ const layout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin);
1497
1657
  if (isIndentedLayout(layout)) {
1498
1658
  newDropTarget.target = targetComponent.node.children[0].origin;
1499
1659
  newDropTarget.detectResult = isTopLayout(layout) ? 'bottom' : 'top';
@@ -1510,9 +1670,9 @@ const readjustmentDropTarget = (dropTarget) => {
1510
1670
  return newDropTarget;
1511
1671
  }
1512
1672
  // 上下布局,插到右边
1513
- const parentLayout = MindQueries.getCorrectLayoutByElement(targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1673
+ const parentLayout = MindQueries.getCorrectLayoutByElement(board, targetComponent.node.origin.isRoot ? targetComponent.node.origin : targetComponent.node.parent.origin);
1514
1674
  if (isVerticalLogicLayout(parentLayout)) {
1515
- const lastChildNodeIndex = targetComponent.node.children.length - 1;
1675
+ const lastChildNodeIndex = children.length - 1;
1516
1676
  newDropTarget.target = targetComponent.node.children[lastChildNodeIndex].origin;
1517
1677
  newDropTarget.detectResult = 'right';
1518
1678
  return newDropTarget;
@@ -1623,7 +1783,7 @@ const getLocationScope = (board, handlePosition, parentChildren, element, parent
1623
1783
  }
1624
1784
  };
1625
1785
  const getHitAbstractHandle = (board, element, point) => {
1626
- const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
1786
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, element);
1627
1787
  const isHorizontal = isHorizontalLayout(nodeLayout);
1628
1788
  const parentElement = MindElement.getParent(element);
1629
1789
  const includedElements = parentElement.children.slice(element.start, element.end + 1);
@@ -1710,24 +1870,25 @@ function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1710
1870
  }
1711
1871
 
1712
1872
  function drawIndentedLink(board, node, child, defaultStroke = null, needDrawUnderline = true) {
1713
- const isUnderlineShap = getNodeShapeByElement(child.origin) === MindNodeShape.underline;
1873
+ const branchWidth = getBranchWidthByMindElement(board, child.origin);
1874
+ const branchColor = defaultStroke || getBranchColorByMindElement(board, child.origin);
1875
+ const isUnderlineShape = getNodeShapeByElement(child.origin) === MindElementShape.underline;
1714
1876
  let beginX, beginY, endX, endY, beginNode = node, endNode = child;
1715
1877
  const beginRectangle = getRectangleByNode(beginNode);
1716
1878
  const endRectangle = getRectangleByNode(endNode);
1717
1879
  beginX = beginNode.x + beginNode.width / 2;
1718
1880
  beginY = isChildUp(node, child) ? beginRectangle.y : beginRectangle.y + beginRectangle.height;
1719
1881
  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;
1882
+ endY = isUnderlineShape ? endNode.y + endNode.height - endNode.vGap : endNode.y + endNode.height / 2;
1721
1883
  //根据位置,设置正负参数
1722
1884
  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;
1885
+ const layout = MindQueries.getCorrectLayoutByElement(board, node.origin);
1725
1886
  if (beginNode.origin.isRoot) {
1726
1887
  if (layout === MindLayoutType.leftBottomIndented || layout === MindLayoutType.rightBottomIndented) {
1727
- beginY += strokeWidth;
1888
+ beginY += branchWidth;
1728
1889
  }
1729
1890
  if (layout === MindLayoutType.leftTopIndented || layout === MindLayoutType.rightTopIndented) {
1730
- beginY -= strokeWidth;
1891
+ beginY -= branchWidth;
1731
1892
  }
1732
1893
  }
1733
1894
  let curve = [
@@ -1738,13 +1899,12 @@ function drawIndentedLink(board, node, child, defaultStroke = null, needDrawUnde
1738
1899
  [beginX, endY - (endNode.hGap * plusMinus[1]) / 5],
1739
1900
  [beginX + (endNode.hGap * plusMinus[0]) / 4, endY],
1740
1901
  [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]
1902
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1903
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY],
1904
+ isUnderlineShape && needDrawUnderline ? [endX + (endNode.width - endNode.hGap * 2) * plusMinus[0], endY] : [endX, endY]
1744
1905
  ];
1745
- const stroke = defaultStroke || getBranchColorByMindElement(board, child.origin);
1746
1906
  const points = pointsOnBezierCurves(curve);
1747
- return PlaitBoard.getRoughSVG(board).curve(points, { stroke, strokeWidth });
1907
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth: branchWidth });
1748
1908
  }
1749
1909
 
1750
1910
  var HorizontalPlacement;
@@ -1859,10 +2019,10 @@ const transformPlacement = (placement, direction) => {
1859
2019
 
1860
2020
  function drawLogicLink(board, node, parent, isHorizontal) {
1861
2021
  const branchColor = getBranchColorByMindElement(board, node.origin);
1862
- const strokeWidth = node.origin.branchWidth ? node.origin.branchWidth : STROKE_WIDTH;
2022
+ const branchWidth = getBranchWidthByMindElement(board, node.origin);
1863
2023
  const hasStraightLine = !parent.origin.isRoot;
1864
- const hasUnderlineShape = node.origin.shape === MindNodeShape.underline;
1865
- const hasUnderlineShapeOfParent = parent.origin.shape === MindNodeShape.underline;
2024
+ const hasUnderlineShape = node.origin.shape === MindElementShape.underline;
2025
+ const hasUnderlineShapeOfParent = parent.origin.shape === MindElementShape.underline;
1866
2026
  const nodeClient = getRectangleByNode(node);
1867
2027
  const parentClient = getRectangleByNode(parent);
1868
2028
  const linkDirection = getLayoutDirection(node, isHorizontal);
@@ -1899,13 +2059,17 @@ function drawLogicLink(board, node, parent, isHorizontal) {
1899
2059
  const underlineEnd = movePoint(endPoint, nodeClient.width, linkDirection);
1900
2060
  const underline = hasUnderlineShape && isHorizontal ? [underlineEnd, underlineEnd, underlineEnd] : [];
1901
2061
  const points = pointsOnBezierCurves([...straightLine, ...curve, ...underline]);
1902
- return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth });
2062
+ return PlaitBoard.getRoughSVG(board).curve(points, { stroke: branchColor, strokeWidth: branchWidth });
1903
2063
  }
1904
2064
 
1905
- function getEmojisRectangle(element) {
2065
+ function getEmojisRectangle(board, element) {
2066
+ const options = board.getMindOptions();
1906
2067
  const count = element.data.emojis.length;
1907
2068
  const fontSize = getEmojiFontSize(element);
1908
- return { width: fontSize * count + (count - 1) * 4, height: element.height };
2069
+ return {
2070
+ width: fontSize * count + count * 2 * options.emojiPadding + (count - 1) * options.spaceBetweenEmojis,
2071
+ height: element.height
2072
+ };
1909
2073
  }
1910
2074
  function getEmojiFontSize(element) {
1911
2075
  if (PlaitMind.isMind(element)) {
@@ -1950,10 +2114,10 @@ const getVerticalSpaceBetweenNodeAndText = (element) => {
1950
2114
  return nodeAndText;
1951
2115
  };
1952
2116
  const NodeSpace = {
1953
- getNodeWidth(element) {
2117
+ getNodeWidth(board, element) {
1954
2118
  const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
1955
2119
  if (MindElement.hasEmojis(element)) {
1956
- return nodeAndText + getEmojisRectangle(element).width + getHorizontalSpaceEmojiAndText(element) + element.width + nodeAndText;
2120
+ return nodeAndText + getEmojisRectangle(board, element).width + getHorizontalSpaceEmojiAndText(element) + element.width + nodeAndText;
1957
2121
  }
1958
2122
  return nodeAndText + element.width + nodeAndText;
1959
2123
  },
@@ -1961,10 +2125,10 @@ const NodeSpace = {
1961
2125
  const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1962
2126
  return nodeAndText + element.height + nodeAndText;
1963
2127
  },
1964
- getTextHorizontalSpace(element) {
2128
+ getTextHorizontalSpace(board, element) {
1965
2129
  const nodeAndText = getHorizontalSpaceBetweenNodeAndText(element);
1966
2130
  if (MindElement.hasEmojis(element)) {
1967
- return nodeAndText + getEmojisRectangle(element).width + getHorizontalSpaceEmojiAndText(element);
2131
+ return nodeAndText + getEmojisRectangle(board, element).width + getHorizontalSpaceEmojiAndText(element);
1968
2132
  }
1969
2133
  else {
1970
2134
  return nodeAndText;
@@ -1984,8 +2148,8 @@ const NodeSpace = {
1984
2148
  }
1985
2149
  };
1986
2150
 
1987
- function drawMindNodeRichtext(node, viewContainerRef) {
1988
- const { x, y } = getRichtextRectangleByNode(node);
2151
+ function drawMindNodeRichtext(board, node, viewContainerRef) {
2152
+ const { x, y } = getRichtextRectangleByNode(board, node);
1989
2153
  const classList = [];
1990
2154
  if (node.origin.isRoot) {
1991
2155
  classList.push('root-node');
@@ -2000,8 +2164,8 @@ function drawMindNodeRichtext(node, viewContainerRef) {
2000
2164
  // COMPAT: last character can not show in safari browser
2001
2165
  return drawRichtext(x, y, Math.ceil(node.origin.width), Math.ceil(node.origin.height), node.origin.data.topic, viewContainerRef, classList);
2002
2166
  }
2003
- function updateMindNodeTopicSize(node, g, isEditable) {
2004
- const { x, y, width, height } = getRichtextRectangleByNode(node);
2167
+ function updateMindNodeTopicSize(board, node, g, isEditable) {
2168
+ const { x, y, width, height } = getRichtextRectangleByNode(board, node);
2005
2169
  if (isEditable) {
2006
2170
  // add 999, avoid changing lines when paste more text
2007
2171
  updateForeignObject(g, width + 999, height + 999, x, y);
@@ -2011,18 +2175,18 @@ function updateMindNodeTopicSize(node, g, isEditable) {
2011
2175
  updateForeignObject(g, Math.ceil(node.origin.width), Math.ceil(node.origin.height), x, y);
2012
2176
  }
2013
2177
  }
2014
- function getRichtextRectangleByNode(node) {
2178
+ function getRichtextRectangleByNode(board, node) {
2015
2179
  let { x, y, width, height } = getRectangleByNode(node);
2016
- x = x + NodeSpace.getTextHorizontalSpace(node.origin);
2180
+ x = x + NodeSpace.getTextHorizontalSpace(board, node.origin);
2017
2181
  y = y + NodeSpace.getTextVerticalSpace(node.origin);
2018
2182
  return { width, height, x, y };
2019
2183
  }
2020
2184
 
2021
2185
  function drawRectangleNode(board, node) {
2022
2186
  const { x, y, width, height } = getRectangleByNode(node);
2023
- const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? ROOT_NODE_FILL : NODE_FILL;
2187
+ const fill = node.origin.fill ? node.origin.fill : node.origin.isRoot ? DefaultRootStyle.fill : DefaultNodeStyle.fill;
2024
2188
  const stroke = getStrokeByMindElement(board, node.origin);
2025
- const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : STROKE_WIDTH;
2189
+ const strokeWidth = node.origin.strokeWidth ? node.origin.strokeWidth : DefaultNodeStyle.strokeWidth;
2026
2190
  const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), x, y, x + width, y + height, {
2027
2191
  stroke,
2028
2192
  strokeWidth,
@@ -2034,6 +2198,8 @@ function drawRectangleNode(board, node) {
2034
2198
 
2035
2199
  function drawAbstractLink(board, node, isHorizontal) {
2036
2200
  const linkPadding = 15;
2201
+ const branchWidth = getAbstractBranchWidth(board, node.origin);
2202
+ const branchColor = getAbstractBranchColor(board, node.origin);
2037
2203
  const parent = node.parent;
2038
2204
  const abstractRectangle = getRectangleByNode(node);
2039
2205
  let includedElements = parent.children.slice(node.origin.start, node.origin.end + 1).map(node => {
@@ -2063,8 +2229,8 @@ function drawAbstractLink(board, node, isHorizontal) {
2063
2229
  let c2 = movePoint(bezierEndPoint, curveDistance, linkDirection);
2064
2230
  let bezierConnectorPoint = movePoint(abstractConnectorPoint, -linkPadding, linkDirection);
2065
2231
  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]}`, {
2066
- stroke: GRAY_COLOR,
2067
- strokeWidth: 2
2232
+ stroke: branchColor,
2233
+ strokeWidth: branchWidth
2068
2234
  });
2069
2235
  return link;
2070
2236
  }
@@ -2112,9 +2278,8 @@ class EmojisDrawer {
2112
2278
  this.g.classList.add('emojis');
2113
2279
  let { x, y } = getRectangleByNode(MindElement.getNode(element));
2114
2280
  x = x + NodeSpace.getEmojiHorizontalSpace(element);
2115
- y = y + NodeSpace.getEmojiVerticalSpace(element);
2116
- const { width, height } = getEmojisRectangle(element);
2117
- const foreignObject = createForeignObject(x, y, width, height);
2281
+ const { width, height } = getEmojisRectangle(this.board, element);
2282
+ const foreignObject = createForeignObject(x, y, width, height + NodeSpace.getEmojiVerticalSpace(element) * 2);
2118
2283
  this.g.append(foreignObject);
2119
2284
  const container = document.createElement('div');
2120
2285
  container.classList.add('node-emojis-container');
@@ -2126,114 +2291,24 @@ class EmojisDrawer {
2126
2291
  });
2127
2292
  this.emojiDrawers.forEach(drawer => {
2128
2293
  container.append(drawer.nativeElement);
2129
- });
2130
- return this.g;
2131
- }
2132
- return undefined;
2133
- }
2134
- destroy() {
2135
- if (this.g) {
2136
- this.g.remove();
2137
- }
2138
- this.emojiDrawers.forEach(drawer => drawer.destroy());
2139
- this.emojiDrawers = [];
2140
- }
2141
- }
2142
-
2143
- const setLayout = (board, layout, path) => {
2144
- correctLogicLayoutNode(board, layout, path);
2145
- const element = PlaitNode.get(board, path);
2146
- if (PlaitMind.isMind(element) && isStandardLayout(layout)) {
2147
- handleAbstractIncluded(board, element);
2148
- }
2149
- Transforms.setNode(board, { layout }, path);
2150
- };
2151
- const correctLogicLayoutNode = (board, layout, path) => {
2152
- const node = PlaitNode.get(board, path);
2153
- if (node && layout) {
2154
- node.children?.forEach((value, index) => {
2155
- if (value.layout) {
2156
- if ((isHorizontalLogicLayout(layout) && isVerticalLogicLayout(value.layout)) ||
2157
- (isVerticalLogicLayout(layout) && isHorizontalLogicLayout(value.layout))) {
2158
- Transforms.setNode(board, { layout: null }, [...path, index]);
2159
- }
2160
- if (value.children?.length) {
2161
- correctLogicLayoutNode(board, layout, [...path, index]);
2162
- }
2163
- }
2164
- });
2165
- }
2166
- };
2167
-
2168
- const setTopic = (board, element, topic, width, height) => {
2169
- const newElement = {
2170
- data: { topic },
2171
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2172
- height: height / board.viewport.zoom
2173
- };
2174
- if (MindElement.hasEmojis(element)) {
2175
- newElement.data.emojis = element.data.emojis;
2176
- }
2177
- const path = PlaitBoard.findPath(board, element);
2178
- Transforms.setNode(board, newElement, path);
2179
- };
2180
- const setTopicSize = (board, element, width, height) => {
2181
- const newElement = {
2182
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2183
- height: height / board.viewport.zoom
2184
- };
2185
- const path = PlaitBoard.findPath(board, element);
2186
- Transforms.setNode(board, newElement, path);
2187
- };
2188
- const addEmoji = (board, element, emojiItem) => {
2189
- const emojis = element.data.emojis || [];
2190
- const newEmojis = [...emojis];
2191
- newEmojis.push(emojiItem);
2192
- const newElement = {
2193
- data: { topic: element.data.topic, emojis: newEmojis }
2194
- };
2195
- const path = PlaitBoard.findPath(board, element);
2196
- Transforms.setNode(board, newElement, path);
2197
- };
2198
- const removeEmoji = (board, element, emojiItem) => {
2199
- const emojis = element.data.emojis.filter(value => value !== emojiItem);
2200
- const newElement = {
2201
- data: { topic: element.data.topic }
2202
- };
2203
- if (emojis.length > 0) {
2204
- newElement.data.emojis = emojis;
2205
- }
2206
- const path = PlaitBoard.findPath(board, element);
2207
- Transforms.setNode(board, newElement, path);
2208
- };
2209
- const replaceEmoji = (board, element, oldEmoji, newEmoji) => {
2210
- const newElement = {
2211
- data: { topic: element.data.topic }
2212
- };
2213
- const newEmojis = element.data.emojis.map(value => {
2214
- if (value === oldEmoji) {
2215
- return newEmoji;
2294
+ });
2295
+ return this.g;
2216
2296
  }
2217
- return value;
2218
- });
2219
- newElement.data.emojis = newEmojis;
2220
- const path = PlaitBoard.findPath(board, element);
2221
- Transforms.setNode(board, newElement, path);
2222
- };
2223
-
2224
- const MindTransforms = {
2225
- setLayout,
2226
- setTopic,
2227
- setTopicSize,
2228
- addEmoji,
2229
- removeEmoji,
2230
- replaceEmoji
2231
- };
2297
+ return undefined;
2298
+ }
2299
+ destroy() {
2300
+ if (this.g) {
2301
+ this.g.remove();
2302
+ }
2303
+ this.emojiDrawers.forEach(drawer => drawer.destroy());
2304
+ this.emojiDrawers = [];
2305
+ }
2306
+ }
2232
2307
 
2233
2308
  function drawAbstractIncludedOutline(board, roughSVG, element, activeHandlePosition, resizingLocation) {
2234
2309
  const abstractIncludedG = createG();
2235
2310
  const parentElement = MindElement.getParent(element);
2236
- const nodeLayout = MindQueries.getCorrectLayoutByElement(element);
2311
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, element);
2237
2312
  const isHorizontal = isHorizontalLayout(nodeLayout);
2238
2313
  const includedElements = parentElement.children.slice(element.start, element.end + 1);
2239
2314
  let abstractRectangle = getRectangleByElements(board, includedElements, true);
@@ -2313,7 +2388,8 @@ function hasAfterDraw(value) {
2313
2388
  }
2314
2389
 
2315
2390
  function findNewChildNodePath(board, element) {
2316
- return PlaitBoard.findPath(board, element).concat((element.children || []).filter(child => !AbstractNode.isAbstract(child)).length);
2391
+ const children = getNonAbstractChildren(element);
2392
+ return PlaitBoard.findPath(board, element).concat(children.length);
2317
2393
  }
2318
2394
  function findNewSiblingNodePath(board, element) {
2319
2395
  const path = PlaitBoard.findPath(board, element);
@@ -2343,14 +2419,14 @@ class QuickInsertDrawer extends BaseDrawer {
2343
2419
  */
2344
2420
  const shape = getNodeShapeByElement(element);
2345
2421
  // 形状是矩形要偏移边框的线宽
2346
- const strokeWidth = element.branchWidth ? element.branchWidth : STROKE_WIDTH;
2422
+ const branchWidth = getBranchWidthByMindElement(this.board, element);
2347
2423
  let offsetBorderLineWidth = 0;
2348
- if (shape === MindNodeShape.roundRectangle && offset === 0) {
2349
- offsetBorderLineWidth = strokeWidth;
2424
+ if (shape === MindElementShape.roundRectangle && offset === 0) {
2425
+ offsetBorderLineWidth = branchWidth;
2350
2426
  }
2351
2427
  let offsetRootBorderLineWidth = 0;
2352
2428
  if (element.isRoot) {
2353
- offsetRootBorderLineWidth = strokeWidth;
2429
+ offsetRootBorderLineWidth = branchWidth;
2354
2430
  }
2355
2431
  // 当没有子节点时,需要缩小的偏移量
2356
2432
  const extraOffset = 3;
@@ -2445,21 +2521,21 @@ class QuickInsertDrawer extends BaseDrawer {
2445
2521
  offsetRootBorderLineWidth
2446
2522
  }
2447
2523
  };
2448
- if (shape === MindNodeShape.roundRectangle || element.isRoot) {
2524
+ if (shape === MindElementShape.roundRectangle || element.isRoot) {
2449
2525
  underlineCoordinates[MindLayoutType.left].startY -= height * 0.5;
2450
2526
  underlineCoordinates[MindLayoutType.left].endY -= height * 0.5;
2451
2527
  underlineCoordinates[MindLayoutType.right].startY -= height * 0.5;
2452
2528
  underlineCoordinates[MindLayoutType.right].endY -= height * 0.5;
2453
2529
  }
2454
2530
  const branchColor = PlaitMind.isMind(element) ? getNextBranchColor(element) : getBranchColorByMindElement(this.board, element);
2455
- let nodeLayout = MindQueries.getCorrectLayoutByElement(element);
2531
+ let nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, element);
2456
2532
  if (element.isRoot && isStandardLayout(nodeLayout)) {
2457
2533
  const root = element;
2458
2534
  nodeLayout = root.children.length >= root.rightNodeCount ? MindLayoutType.left : MindLayoutType.right;
2459
2535
  }
2460
2536
  const underlineCoordinate = underlineCoordinates[nodeLayout];
2461
2537
  if (underlineCoordinate) {
2462
- const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth });
2538
+ const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth: branchWidth });
2463
2539
  const circleCoordinates = {
2464
2540
  startX: underlineCoordinate.endX,
2465
2541
  startY: underlineCoordinate.endY
@@ -2599,7 +2675,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2599
2675
  this.destroyShape();
2600
2676
  const shape = getNodeShapeByElement(this.node.origin);
2601
2677
  switch (shape) {
2602
- case MindNodeShape.roundRectangle:
2678
+ case MindElementShape.roundRectangle:
2603
2679
  this.shapeG = drawRectangleNode(this.board, this.node);
2604
2680
  this.g.prepend(this.shapeG);
2605
2681
  break;
@@ -2756,7 +2832,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2756
2832
  }
2757
2833
  }
2758
2834
  drawRichtext() {
2759
- const { richtextG, richtextComponentRef, foreignObject } = drawMindNodeRichtext(this.node, this.viewContainerRef);
2835
+ const { richtextG, richtextComponentRef, foreignObject } = drawMindNodeRichtext(this.board, this.node, this.viewContainerRef);
2760
2836
  this.richtextComponentRef = richtextComponentRef;
2761
2837
  this.richtextG = richtextG;
2762
2838
  this.foreignObject = foreignObject;
@@ -2786,9 +2862,9 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2786
2862
  });
2787
2863
  const { x, y, width, height } = getRectangleByNode(this.node);
2788
2864
  const stroke = getBranchColorByMindElement(this.board, this.element);
2789
- const strokeWidth = this.node.origin.branchWidth ? this.node.origin.branchWidth : STROKE_WIDTH;
2865
+ const branchWidth = getBranchWidthByMindElement(this.board, this.element);
2790
2866
  const extendY = y + height / 2;
2791
- const nodeLayout = MindQueries.getCorrectLayoutByElement(this.element);
2867
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, this.element);
2792
2868
  let extendLineXY = [
2793
2869
  [x + width, extendY],
2794
2870
  [x + width + EXTEND_OFFSET, extendY]
@@ -2800,7 +2876,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2800
2876
  let circleOffset = [EXTEND_RADIUS / 2, 0];
2801
2877
  if (isHorizontalLayout(nodeLayout) && !isIndentedLayout(nodeLayout)) {
2802
2878
  extendLineYOffset =
2803
- getNodeShapeByElement(this.node.origin) === MindNodeShape.roundRectangle
2879
+ getNodeShapeByElement(this.node.origin) === MindElementShape.roundRectangle
2804
2880
  ? [0, 0]
2805
2881
  : [height / 2, height / 2];
2806
2882
  if (isLeftLayout(nodeLayout)) {
@@ -2832,7 +2908,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2832
2908
  [extendLineXY[1][0] + extendLineXOffset[1], extendLineXY[1][1] + extendLineYOffset[1]]
2833
2909
  ];
2834
2910
  const extendLine = this.roughSVG.line(extendLineXY[0][0], extendLineXY[0][1], extendLineXY[1][0], extendLineXY[1][1], {
2835
- strokeWidth,
2911
+ strokeWidth: branchWidth,
2836
2912
  stroke
2837
2913
  });
2838
2914
  //绘制箭头
@@ -2869,7 +2945,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2869
2945
  const hideCircleG = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS - 1, {
2870
2946
  fill: '#fff',
2871
2947
  stroke,
2872
- strokeWidth,
2948
+ strokeWidth: branchWidth,
2873
2949
  fillStyle: 'solid'
2874
2950
  });
2875
2951
  collapseG.appendChild(hideCircleG);
@@ -2898,7 +2974,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2898
2974
  }
2899
2975
  updateRichtext() {
2900
2976
  updateRichText(this.node.origin.data.topic, this.richtextComponentRef);
2901
- updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
2977
+ updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
2902
2978
  }
2903
2979
  startEditText(isEnd, isClear) {
2904
2980
  if (!this.richtextComponentRef) {
@@ -2908,7 +2984,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2908
2984
  this.isEditable = true;
2909
2985
  IS_TEXT_EDITABLE.set(this.board, true);
2910
2986
  this.disabledMaskG();
2911
- updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
2987
+ updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
2912
2988
  if (richtextInstance.plaitReadonly) {
2913
2989
  richtextInstance.plaitReadonly = false;
2914
2990
  this.richtextComponentRef.changeDetectorRef.detectChanges();
@@ -3011,7 +3087,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
3011
3087
  richtextInstance.plaitReadonly = true;
3012
3088
  this.richtextComponentRef?.changeDetectorRef.markForCheck();
3013
3089
  this.isEditable = false;
3014
- updateMindNodeTopicSize(this.node, this.richtextG, this.isEditable);
3090
+ updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
3015
3091
  IS_TEXT_EDITABLE.set(this.board, false);
3016
3092
  };
3017
3093
  }
@@ -3053,7 +3129,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3053
3129
  }]
3054
3130
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }]; } });
3055
3131
 
3056
- const getLayoutOptions = () => {
3132
+ const getLayoutOptions = (board) => {
3057
3133
  function getMainAxle(element, parent) {
3058
3134
  const strokeWidth = element.strokeWidth || STROKE_WIDTH;
3059
3135
  if (element.isRoot) {
@@ -3076,7 +3152,7 @@ const getLayoutOptions = () => {
3076
3152
  return NodeSpace.getNodeHeight(element);
3077
3153
  },
3078
3154
  getWidth(element) {
3079
- return NodeSpace.getNodeWidth(element);
3155
+ return NodeSpace.getNodeWidth(board, element);
3080
3156
  },
3081
3157
  getHorizontalGap(element, parent) {
3082
3158
  const _layout = (parent && parent.layout) || getRootLayout(element);
@@ -3106,7 +3182,7 @@ const getLayoutOptions = () => {
3106
3182
  }
3107
3183
  },
3108
3184
  getVerticalConnectingPosition(element, parent) {
3109
- if (element.shape === MindNodeShape.underline && parent && isHorizontalLogicLayout(parent.layout)) {
3185
+ if (element.shape === MindElementShape.underline && parent && isHorizontalLogicLayout(parent.layout)) {
3110
3186
  return ConnectingPosition.bottom;
3111
3187
  }
3112
3188
  return undefined;
@@ -3132,7 +3208,7 @@ class PlaitMindComponent extends MindNodeComponent {
3132
3208
  }
3133
3209
  updateMindLayout(element = this.element) {
3134
3210
  const mindLayoutType = element.layout || getDefaultLayout();
3135
- this.root = GlobalLayout.layout(element, getLayoutOptions(), mindLayoutType);
3211
+ this.root = GlobalLayout.layout(element, getLayoutOptions(this.board), mindLayoutType);
3136
3212
  this.updateMindNodeLocation(element);
3137
3213
  }
3138
3214
  updateMindNodeLocation(element) {
@@ -3175,6 +3251,119 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3175
3251
  }]
3176
3252
  }] });
3177
3253
 
3254
+ const isValidTarget = (origin, target) => {
3255
+ return origin !== target && !isChildElement(origin, target);
3256
+ };
3257
+ const addActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3258
+ const activeComponent = PlaitElement.getComponent(activeElement);
3259
+ if (isOrigin) {
3260
+ activeComponent.g.classList.add('dragging-origin');
3261
+ }
3262
+ else {
3263
+ activeComponent.g.classList.add('dragging-child');
3264
+ }
3265
+ !activeElement.isCollapsed &&
3266
+ activeElement.children.forEach(child => {
3267
+ addActiveOnDragOrigin(child, false);
3268
+ });
3269
+ };
3270
+ const removeActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3271
+ const activeComponent = PlaitElement.getComponent(activeElement);
3272
+ if (isOrigin) {
3273
+ activeComponent.g.classList.remove('dragging-origin');
3274
+ }
3275
+ else {
3276
+ activeComponent.g.classList.remove('dragging-child');
3277
+ }
3278
+ !activeElement.isCollapsed &&
3279
+ activeElement.children.forEach(child => {
3280
+ removeActiveOnDragOrigin(child, false);
3281
+ });
3282
+ };
3283
+ const updatePathByLayoutAndDropTarget = (targetPath, layout, dropTarget) => {
3284
+ // 上下布局:左右是兄弟节点,上下是子节点
3285
+ if (isVerticalLogicLayout(layout)) {
3286
+ if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3287
+ targetPath.push(dropTarget.target.children.length);
3288
+ }
3289
+ if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3290
+ targetPath.push(dropTarget.target.children.length);
3291
+ }
3292
+ // 如果是左,位置不变,右则插入到下一个兄弟节点
3293
+ if (dropTarget.detectResult === 'right') {
3294
+ targetPath = Path.next(targetPath);
3295
+ }
3296
+ }
3297
+ // 水平布局/标准布局:上下是兄弟节点,左右是子节点
3298
+ if (isHorizontalLogicLayout(layout)) {
3299
+ if (dropTarget.detectResult === 'right') {
3300
+ targetPath.push(dropTarget.target.children.length);
3301
+ }
3302
+ if (dropTarget.detectResult === 'left') {
3303
+ targetPath.push(dropTarget.target.children.length);
3304
+ }
3305
+ // 如果是上,位置不变,下插入到下一个兄弟节点
3306
+ if (dropTarget.detectResult === 'bottom') {
3307
+ targetPath = Path.next(targetPath);
3308
+ }
3309
+ }
3310
+ // 缩进布局:上下是兄弟节点,左右是子节点,但上(左上/右上),探测到上是子节点,下则位置不变,反之同理。
3311
+ if (isIndentedLayout(layout)) {
3312
+ if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3313
+ targetPath = Path.next(targetPath);
3314
+ }
3315
+ if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3316
+ targetPath = Path.next(targetPath);
3317
+ }
3318
+ if (isLeftLayout(layout) && dropTarget.detectResult === 'left') {
3319
+ targetPath.push(dropTarget.target.children.length);
3320
+ }
3321
+ if (isRightLayout(layout) && dropTarget.detectResult === 'right') {
3322
+ targetPath.push(dropTarget.target.children.length);
3323
+ }
3324
+ }
3325
+ return targetPath;
3326
+ };
3327
+ const updateRightNodeCount = (board, activeComponent, targetComponent, detectResult) => {
3328
+ let rightNodeCount;
3329
+ const mindElement = findUpElement(targetComponent.node.origin).root;
3330
+ const mindComponent = ELEMENT_TO_COMPONENT.get(mindElement);
3331
+ const activeIndex = mindComponent?.root.children.indexOf(activeComponent.node);
3332
+ const targetIndex = mindComponent?.root.children.indexOf(targetComponent.node);
3333
+ const isActiveOnRight = activeIndex !== -1 && activeIndex <= activeComponent.parent.origin.rightNodeCount - 1;
3334
+ const isTargetOnRight = targetComponent.parent && targetIndex !== -1 && targetIndex <= targetComponent.parent.origin.rightNodeCount - 1;
3335
+ const isBothOnRight = isActiveOnRight && isTargetOnRight;
3336
+ const rootChildCount = mindComponent.root.children?.length;
3337
+ const rootRightNodeCount = mindComponent?.root.origin.rightNodeCount;
3338
+ if (!isBothOnRight) {
3339
+ if (isActiveOnRight) {
3340
+ rightNodeCount = rootChildCount < rootRightNodeCount ? rootChildCount - 1 : rootRightNodeCount - 1;
3341
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, activeComponent.parent.origin));
3342
+ }
3343
+ if (isTargetOnRight && detectResult !== 'right') {
3344
+ rightNodeCount = rootChildCount < rootRightNodeCount ? rootRightNodeCount : rootRightNodeCount + 1;
3345
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.parent.origin));
3346
+ }
3347
+ //二级子节点拖动到根节点左侧
3348
+ if (targetComponent.node.origin.isRoot && detectResult === 'left' && activeIndex === -1) {
3349
+ rightNodeCount = rootChildCount;
3350
+ Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.element));
3351
+ }
3352
+ }
3353
+ };
3354
+ const IS_DRAGGING = new WeakMap();
3355
+ const isDragging = (board) => {
3356
+ return !!IS_DRAGGING.get(board);
3357
+ };
3358
+ const setIsDragging = (board, state) => {
3359
+ IS_DRAGGING.set(board, state);
3360
+ };
3361
+ const updateAbstractInDnd = (board, deletableElements, originPath) => {
3362
+ const refs = insertElementHandleAbstract(board, originPath, false);
3363
+ deleteElementHandleAbstract(board, deletableElements, refs);
3364
+ MindTransforms.setAbstractsByRefs(board, refs);
3365
+ };
3366
+
3178
3367
  const DRAG_MOVE_BUFFER = 5;
3179
3368
  const withDnd = (board) => {
3180
3369
  const { mousedown, mousemove, globalMouseup, keydown } = board;
@@ -3253,7 +3442,7 @@ const withDnd = (board) => {
3253
3442
  x: activeComponent.node.x + offsetX,
3254
3443
  y: activeComponent.node.y + offsetY
3255
3444
  };
3256
- const textRectangle = getRichtextRectangleByNode(activeComponent.node);
3445
+ const textRectangle = getRichtextRectangleByNode(board, activeComponent.node);
3257
3446
  const fakeNodeG = drawRectangleNode(board, fakeDraggingNode);
3258
3447
  const richtextG = activeComponent.richtextG?.cloneNode(true);
3259
3448
  updateForeignObject(richtextG, textRectangle.width + BASE * 10, textRectangle.height, textRectangle.x + offsetX, textRectangle.y + offsetY);
@@ -3276,7 +3465,7 @@ const withDnd = (board) => {
3276
3465
  }
3277
3466
  const directions = directionDetector(node, detectCenterPoint);
3278
3467
  if (directions) {
3279
- detectResult = directionCorrector(node, directions);
3468
+ detectResult = directionCorrector(board, node, directions);
3280
3469
  }
3281
3470
  dropTarget = null;
3282
3471
  if (detectResult && isValidTarget(activeComponent.node.origin, node.origin)) {
@@ -3286,7 +3475,7 @@ const withDnd = (board) => {
3286
3475
  }
3287
3476
  });
3288
3477
  if (dropTarget?.target) {
3289
- dropTarget = readjustmentDropTarget(dropTarget);
3478
+ dropTarget = readjustmentDropTarget(board, dropTarget);
3290
3479
  drawPlaceholderDropNodeG(board, dropTarget, fakeDropNodeG);
3291
3480
  }
3292
3481
  }
@@ -3298,12 +3487,13 @@ const withDnd = (board) => {
3298
3487
  const activeComponent = PlaitElement.getComponent(activeElement);
3299
3488
  const targetComponent = PlaitElement.getComponent(dropTarget.target);
3300
3489
  let targetPath = PlaitBoard.findPath(board, targetComponent.element);
3301
- const mindmapElement = findUpElement(dropTarget.target).root;
3302
- const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
3303
- const layout = MindQueries.getCorrectLayoutByElement(mindmapComponent?.root.origin);
3490
+ const mindElement = findUpElement(dropTarget.target).root;
3491
+ const mindComponent = ELEMENT_TO_COMPONENT.get(mindElement);
3492
+ const layout = MindQueries.getCorrectLayoutByElement(board, mindComponent?.root.origin);
3304
3493
  targetPath = updatePathByLayoutAndDropTarget(targetPath, layout, dropTarget);
3305
3494
  const originPath = PlaitBoard.findPath(board, activeComponent.element);
3306
3495
  let newElement = { isCollapsed: false }, rightTargetPath = PlaitBoard.findPath(board, targetComponent.element);
3496
+ updateAbstractInDnd(board, [activeElement], targetPath);
3307
3497
  if (isStandardLayout(layout)) {
3308
3498
  updateRightNodeCount(board, activeComponent, targetComponent, dropTarget.detectResult);
3309
3499
  }
@@ -3336,124 +3526,51 @@ const withDnd = (board) => {
3336
3526
  };
3337
3527
  return board;
3338
3528
  };
3339
- const isValidTarget = (origin, target) => {
3340
- return origin !== target && !isChildElement(origin, target);
3341
- };
3342
- const addActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3343
- const activeComponent = PlaitElement.getComponent(activeElement);
3344
- if (isOrigin) {
3345
- activeComponent.g.classList.add('dragging-origin');
3346
- }
3347
- else {
3348
- activeComponent.g.classList.add('dragging-child');
3349
- }
3350
- !activeElement.isCollapsed &&
3351
- activeElement.children.forEach(child => {
3352
- addActiveOnDragOrigin(child, false);
3353
- });
3354
- };
3355
- const removeActiveOnDragOrigin = (activeElement, isOrigin = true) => {
3356
- const activeComponent = PlaitElement.getComponent(activeElement);
3357
- if (isOrigin) {
3358
- activeComponent.g.classList.remove('dragging-origin');
3359
- }
3360
- else {
3361
- activeComponent.g.classList.remove('dragging-child');
3362
- }
3363
- !activeElement.isCollapsed &&
3364
- activeElement.children.forEach(child => {
3365
- removeActiveOnDragOrigin(child, false);
3366
- });
3367
- };
3368
- const updatePathByLayoutAndDropTarget = (targetPath, layout, dropTarget) => {
3369
- // 上下布局:左右是兄弟节点,上下是子节点
3370
- if (isVerticalLogicLayout(layout)) {
3371
- if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3372
- targetPath.push(dropTarget.target.children.length);
3373
- }
3374
- if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3375
- targetPath.push(dropTarget.target.children.length);
3376
- }
3377
- // 如果是左,位置不变,右则插入到下一个兄弟节点
3378
- if (dropTarget.detectResult === 'right') {
3379
- targetPath = Path.next(targetPath);
3380
- }
3381
- }
3382
- // 水平布局/标准布局:上下是兄弟节点,左右是子节点
3383
- if (isHorizontalLogicLayout(layout)) {
3384
- if (dropTarget.detectResult === 'right') {
3385
- targetPath.push(dropTarget.target.children.length);
3386
- }
3387
- if (dropTarget.detectResult === 'left') {
3388
- targetPath.push(dropTarget.target.children.length);
3389
- }
3390
- // 如果是上,位置不变,下插入到下一个兄弟节点
3391
- if (dropTarget.detectResult === 'bottom') {
3392
- targetPath = Path.next(targetPath);
3393
- }
3394
- }
3395
- // 缩进布局:上下是兄弟节点,左右是子节点,但上(左上/右上),探测到上是子节点,下则位置不变,反之同理。
3396
- if (isIndentedLayout(layout)) {
3397
- if (isTopLayout(layout) && dropTarget.detectResult === 'top') {
3398
- targetPath = Path.next(targetPath);
3399
- }
3400
- if (isBottomLayout(layout) && dropTarget.detectResult === 'bottom') {
3401
- targetPath = Path.next(targetPath);
3402
- }
3403
- if (isLeftLayout(layout) && dropTarget.detectResult === 'left') {
3404
- targetPath.push(dropTarget.target.children.length);
3405
- }
3406
- if (isRightLayout(layout) && dropTarget.detectResult === 'right') {
3407
- targetPath.push(dropTarget.target.children.length);
3408
- }
3409
- }
3410
- return targetPath;
3411
- };
3412
- const updateRightNodeCount = (board, activeComponent, targetComponent, detectResult) => {
3413
- let rightNodeCount;
3414
- const mindmapElement = findUpElement(targetComponent.node.origin).root;
3415
- const mindmapComponent = ELEMENT_TO_COMPONENT.get(mindmapElement);
3416
- const activeIndex = mindmapComponent?.root.children.indexOf(activeComponent.node);
3417
- const targetIndex = mindmapComponent?.root.children.indexOf(targetComponent.node);
3418
- const isActiveOnRight = activeIndex !== -1 && activeIndex <= activeComponent.parent.origin.rightNodeCount - 1;
3419
- const isTargetOnRight = targetComponent.parent && targetIndex !== -1 && targetIndex <= targetComponent.parent.origin.rightNodeCount - 1;
3420
- const isBothOnRight = isActiveOnRight && isTargetOnRight;
3421
- const rootChildCount = mindmapComponent.root.children?.length;
3422
- const rootRightNodeCount = mindmapComponent?.root.origin.rightNodeCount;
3423
- if (!isBothOnRight) {
3424
- if (isActiveOnRight) {
3425
- rightNodeCount = rootChildCount < rootRightNodeCount ? rootChildCount - 1 : rootRightNodeCount - 1;
3426
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, activeComponent.parent.origin));
3427
- }
3428
- if (isTargetOnRight && detectResult !== 'right') {
3429
- rightNodeCount = rootChildCount < rootRightNodeCount ? rootRightNodeCount : rootRightNodeCount + 1;
3430
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.parent.origin));
3431
- }
3432
- //二级子节点拖动到根节点左侧
3433
- if (targetComponent.node.origin.isRoot && detectResult === 'left' && activeIndex === -1) {
3434
- rightNodeCount = rootChildCount;
3435
- Transforms.setNode(board, { rightNodeCount }, PlaitBoard.findPath(board, targetComponent.element));
3436
- }
3437
- }
3438
- };
3439
- const IS_DRAGGING = new WeakMap();
3440
- const isDragging = (board) => {
3441
- return !!IS_DRAGGING.get(board);
3442
- };
3443
- const setIsDragging = (board, state) => {
3444
- IS_DRAGGING.set(board, state);
3445
- };
3446
3529
 
3447
3530
  const buildClipboardData = (board, selectedElements) => {
3448
3531
  let result = [];
3449
- const selectedMindNodes = selectedElements.map(value => MindElement.getNode(value));
3450
- const nodesRectangle = getRectangleByElements(board, selectedElements, true);
3451
- selectedElements.forEach((node, index) => {
3532
+ // get overall abstract
3533
+ const overallAbstracts = getOverallAbstracts(board, selectedElements);
3534
+ // keep correct order
3535
+ const newSelectedElements = selectedElements.filter((value) => !overallAbstracts.includes(value));
3536
+ newSelectedElements.push(...overallAbstracts);
3537
+ // get correct start and end in selected elements
3538
+ function getCorrectStartEnd(abstract) {
3539
+ const parent = MindElement.getParent(abstract);
3540
+ const startElement = parent.children[abstract.start];
3541
+ const index = selectedElements.indexOf(startElement);
3542
+ return { start: index, end: index + (abstract.end - abstract.start) };
3543
+ }
3544
+ const selectedMindNodes = newSelectedElements.map(value => MindElement.getNode(value));
3545
+ const nodesRectangle = getRectangleByElements(board, newSelectedElements, true);
3546
+ newSelectedElements.forEach((element, index) => {
3547
+ // handle relative location
3452
3548
  const nodeRectangle = getRectangleByNode(selectedMindNodes[index]);
3453
- result.push({
3454
- ...node,
3455
- points: [[nodeRectangle.x - nodesRectangle.x, nodeRectangle.y - nodesRectangle.y]]
3456
- });
3549
+ const points = [[nodeRectangle.x - nodesRectangle.x, nodeRectangle.y - nodesRectangle.y]];
3550
+ // handle invalid abstract
3551
+ if (AbstractNode.isAbstract(element) && overallAbstracts.includes(element)) {
3552
+ const { start, end } = getCorrectStartEnd(element);
3553
+ result.push({
3554
+ ...element,
3555
+ points,
3556
+ start,
3557
+ end
3558
+ });
3559
+ }
3560
+ else {
3561
+ if (AbstractNode.isAbstract(element)) {
3562
+ let newElement = { ...element, points };
3563
+ delete newElement.start;
3564
+ delete newElement.end;
3565
+ result.push(newElement);
3566
+ }
3567
+ else {
3568
+ result.push({
3569
+ ...element,
3570
+ points: points
3571
+ });
3572
+ }
3573
+ }
3457
3574
  });
3458
3575
  return result;
3459
3576
  };
@@ -3479,17 +3596,22 @@ const insertClipboardData = (board, elements, targetPoint) => {
3479
3596
  let newElement, path;
3480
3597
  const selectedElements = getSelectedElements(board);
3481
3598
  let newELements = [];
3599
+ const hasTargetParent = selectedElements.length === 1;
3600
+ const targetParent = selectedElements[0];
3601
+ const targetParentPath = targetParent && PlaitBoard.findPath(board, targetParent);
3602
+ const nonAbstractChildrenLength = targetParent && getNonAbstractChildren(targetParent).length;
3482
3603
  elements.forEach((item, index) => {
3483
3604
  newElement = copyNewNode(item);
3484
- if (selectedElements.length === 1) {
3605
+ if (hasTargetParent) {
3485
3606
  if (item.isRoot) {
3486
3607
  newElement = transformRootToNode(board, newElement);
3487
3608
  }
3488
- if (AbstractNode.isAbstract(item)) {
3489
- newElement = transformAbstractToNode(newElement);
3609
+ // handle abstract start and end
3610
+ if (AbstractNode.isAbstract(newElement)) {
3611
+ newElement.start = newElement.start + nonAbstractChildrenLength;
3612
+ newElement.end = newElement.end + nonAbstractChildrenLength;
3490
3613
  }
3491
- const selectedElementPath = PlaitBoard.findPath(board, selectedElements[0]);
3492
- path = selectedElementPath.concat((selectedElements[0].children || []).length + index);
3614
+ path = [...targetParentPath, nonAbstractChildrenLength + index];
3493
3615
  }
3494
3616
  else {
3495
3617
  const point = [targetPoint[0] + item.points[0][0], targetPoint[1] + item.points[0][1]];
@@ -3515,14 +3637,6 @@ const insertClipboardText = (board, parentElement, text, width, height) => {
3515
3637
  return;
3516
3638
  };
3517
3639
 
3518
- const withEmoji = (board) => {
3519
- const newBoard = board;
3520
- newBoard.drawEmoji = (emoji, element) => {
3521
- throw new Error('Not implement drawEmoji method error.');
3522
- };
3523
- return newBoard;
3524
- };
3525
-
3526
3640
  const withAbstract = (board) => {
3527
3641
  const newBoard = board;
3528
3642
  const { mousedown, mousemove, mouseup } = board;
@@ -3557,7 +3671,7 @@ const withAbstract = (board) => {
3557
3671
  event.preventDefault();
3558
3672
  const abstractComponent = PlaitElement.getComponent(activeAbstractElement);
3559
3673
  const element = abstractComponent.element;
3560
- const nodeLayout = MindQueries.getCorrectLayoutByElement(activeAbstractElement);
3674
+ const nodeLayout = MindQueries.getCorrectLayoutByElement(board, activeAbstractElement);
3561
3675
  const isHorizontal = isHorizontalLayout(nodeLayout);
3562
3676
  const parentElement = MindElement.getParent(element);
3563
3677
  let children = parentElement.children;
@@ -3623,6 +3737,17 @@ const withAbstract = (board) => {
3623
3737
  return board;
3624
3738
  };
3625
3739
 
3740
+ const withExtendMind = (board) => {
3741
+ const newBoard = board;
3742
+ newBoard.drawEmoji = (emoji, element) => {
3743
+ throw new Error('Not implement drawEmoji method error.');
3744
+ };
3745
+ newBoard.getMindOptions = () => {
3746
+ return { spaceBetweenEmojis: 4, emojiPadding: 0 };
3747
+ };
3748
+ return newBoard;
3749
+ };
3750
+
3626
3751
  const withMind = (board) => {
3627
3752
  const { drawElement, dblclick, keydown, insertFragment, setFragment, deleteFragment, isHitSelection, getRectangle, isMovable, isRecursion } = board;
3628
3753
  board.drawElement = (context) => {
@@ -3686,7 +3811,8 @@ const withMind = (board) => {
3686
3811
  if (shouldChangeRightNodeCount(selectedElement)) {
3687
3812
  changeRightNodeCount(board, selectedElementPath.slice(0, 1), 1);
3688
3813
  }
3689
- insertSiblingElementHandleAbstract(board, selectedElement);
3814
+ const abstractRefs = insertElementHandleAbstract(board, Path.next(selectedElementPath));
3815
+ MindTransforms.setAbstractsByRefs(board, abstractRefs);
3690
3816
  insertMindElement(board, selectedElement, findNewSiblingNodePath(board, selectedElement));
3691
3817
  }
3692
3818
  return;
@@ -3790,7 +3916,7 @@ const withMind = (board) => {
3790
3916
  deleteSelectedELements(board, selectedElements);
3791
3917
  deleteFragment(data);
3792
3918
  };
3793
- return withEmoji(withAbstract(withDnd(board)));
3919
+ return withExtendMind(withAbstract(withDnd(board)));
3794
3920
  };
3795
3921
 
3796
3922
  class MindEmojiBaseComponent {
@@ -3832,5 +3958,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3832
3958
  * Generated bundle index. Do not edit.
3833
3959
  */
3834
3960
 
3835
- export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, COLORS, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, LayoutDirection, LayoutDirectionsMap, MAX_RADIUS, MINDMAP_KEY, MindElement, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindNodeShape, MindQueries, MindTransforms, NODE_FILL, NODE_MIN_WIDTH, PRIMARY_COLOR, PlaitMind, PlaitMindComponent, QUICK_INSERT_CIRCLE_COLOR, QUICK_INSERT_CIRCLE_OFFSET, QUICK_INSERT_INNER_CROSS_COLOR, ROOT_NODE_FILL, ROOT_NODE_STROKE, ROOT_TOPIC_FONT_SIZE, STROKE_WIDTH, TOPIC_COLOR, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, TRANSPARENT, canSetAbstract, changeRightNodeCount, copyNewNode, correctLayoutByDirection, createDefaultMindMapElement, createMindElement, deleteSelectedELements, directionCorrector, directionDetector, divideElementByParent, drawCurvePlaceholderDropNodeG, drawIndentNodeG, drawPlaceholderDropNodeG, drawStraightDropNodeG, extractNodesText, filterChildElement, findLastChild, findLocationLeftIndex, findParentElement, findUpElement, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getChildrenCount, getCorrespondingAbstract, getDefaultLayout, getEmojiFontSize, getEmojisRectangle, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getNextBranchColor, getNodeShapeByElement, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getStrokeByMindElement, handleAbstractIncluded, handleTouchedAbstract, hitMindElement, insertAbstractNode, insertMindElement, insertSiblingElementHandleAbstract, isChildElement, isChildRight, isChildUp, isCorrectLayout, isMixedLayout, isSetAbstract, isVirtualKey, moveAbstractPosition, readjustmentDropTarget, separateChildren, setAbstract, setAbstractByElements, shouldChangeRightNodeCount, transformAbstractToNode, transformNodeToRoot, transformRootToNode, withEmoji, withMind };
3961
+ 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, filterChildElement, findLastChild, findLocationLeftIndex, findParentElement, findUpElement, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultLayout, getEmojiFontSize, getEmojisRectangle, getHitAbstractHandle, getHorizontalFakeY, getInCorrectLayoutDirection, getIndentedFakePoint, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getNextBranchColor, getNodeShapeByElement, getOverallAbstracts, getRectangleByNode, getRectangleByResizingLocation, getRootLayout, getStrokeByMindElement, handleTouchedAbstract, hitMindElement, insertElementHandleAbstract, insertMindElement, isChildElement, isChildRight, isChildUp, isCorrectLayout, isMixedLayout, isSetAbstract, isVirtualKey, readjustmentDropTarget, separateChildren, shouldChangeRightNodeCount, transformAbstractToNode, transformNodeToRoot, transformRootToNode, withExtendMind, withMind };
3836
3962
  //# sourceMappingURL=plait-mind.mjs.map