@plait/mind 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +132 -13
  2. package/base/base.drawer.d.ts +4 -3
  3. package/constants/default.d.ts +1 -1
  4. package/constants/node-topic-style.d.ts +1 -0
  5. package/drawer/node-active.drawer.d.ts +13 -0
  6. package/drawer/node-collapse.drawer.d.ts +8 -0
  7. package/drawer/{emojis.drawer.d.ts → node-emojis.drawer.d.ts} +1 -1
  8. package/drawer/{quick-insert.drawer.d.ts → node-insert.drawer.d.ts} +2 -2
  9. package/esm2020/base/base.drawer.mjs +13 -1
  10. package/esm2020/constants/default.mjs +2 -2
  11. package/esm2020/constants/node-rule.mjs +1 -1
  12. package/esm2020/constants/node-topic-style.mjs +2 -1
  13. package/esm2020/drawer/node-active.drawer.mjs +43 -0
  14. package/esm2020/drawer/node-collapse.drawer.mjs +108 -0
  15. package/esm2020/drawer/node-emojis.drawer.mjs +72 -0
  16. package/esm2020/drawer/node-insert.drawer.mjs +98 -0
  17. package/esm2020/interfaces/element.mjs +5 -2
  18. package/esm2020/mind.component.mjs +2 -1
  19. package/esm2020/mind.module.mjs +5 -5
  20. package/esm2020/node.component.mjs +57 -424
  21. package/esm2020/plugins/with-abstract-resize.mjs +3 -3
  22. package/esm2020/plugins/with-mind-create.mjs +23 -15
  23. package/esm2020/plugins/with-mind.mjs +9 -8
  24. package/esm2020/plugins/with-node-dnd.mjs +7 -4
  25. package/esm2020/plugins/with-node-hover.mjs +65 -0
  26. package/esm2020/transforms/node.mjs +10 -7
  27. package/esm2020/utils/abstract/resize.mjs +4 -6
  28. package/esm2020/utils/dnd/common.mjs +8 -2
  29. package/esm2020/utils/draw/node-dnd.mjs +3 -3
  30. package/esm2020/utils/mind.mjs +4 -4
  31. package/esm2020/utils/node/adjust-node.mjs +4 -5
  32. package/esm2020/utils/node/common.mjs +3 -3
  33. package/esm2020/utils/node/create-node.mjs +4 -3
  34. package/fesm2015/plait-mind.mjs +567 -857
  35. package/fesm2015/plait-mind.mjs.map +1 -1
  36. package/fesm2020/plait-mind.mjs +567 -845
  37. package/fesm2020/plait-mind.mjs.map +1 -1
  38. package/interfaces/element.d.ts +1 -0
  39. package/mind.module.d.ts +2 -2
  40. package/node.component.d.ts +15 -31
  41. package/package.json +1 -1
  42. package/plugins/with-mind-create.d.ts +3 -5
  43. package/plugins/with-node-dnd.d.ts +1 -1
  44. package/plugins/with-node-hover.d.ts +5 -0
  45. package/styles/mixins.scss +13 -15
  46. package/styles/styles.scss +12 -16
  47. package/utils/abstract/resize.d.ts +2 -2
  48. package/utils/node/common.d.ts +1 -1
  49. package/esm2020/drawer/emojis.drawer.mjs +0 -72
  50. package/esm2020/drawer/quick-insert.drawer.mjs +0 -211
  51. package/esm2020/utils/draw/node-topic.mjs +0 -32
  52. package/utils/draw/node-topic.d.ts +0 -16
@@ -1,55 +1,19 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, ChangeDetectionStrategy, NgModule, Directive, Input, HostListener } from '@angular/core';
2
+ import { Component, ChangeDetectionStrategy, NgModule, NgZone, Directive, Input, HostListener } from '@angular/core';
3
3
  import * as i2 from '@plait/core';
4
- import { DefaultThemeColor, ColorfulThemeColor, SoftThemeColor, RetroThemeColor, DarkThemeColor, StarryThemeColor, distanceBetweenPointAndRectangle, RectangleClient, PlaitElement, PlaitBoard, idCreator, isNullOrUndefined, Transforms, clearSelectedElement, addSelectedElement, depthFirstRecursion, Path, drawRoundRectangle, drawLinearPath, createG, updateForeignObject, PlaitNode, getRectangleByElements, getSelectedElements, NODE_TO_PARENT, createForeignObject, drawAbstractRoundRectangle, PlaitPluginElementComponent, PlaitPointerType, NODE_TO_INDEX, createText, IS_TEXT_EDITABLE, MERGING, transformPoint, toPoint, PlaitModule, distanceBetweenPointAndPoint, CLIP_BOARD_FORMAT_KEY, isMainPointer, BOARD_TO_HOST, PlaitPluginKey, throttleRAF, BoardTransforms, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
4
+ import { DefaultThemeColor, ColorfulThemeColor, SoftThemeColor, RetroThemeColor, DarkThemeColor, StarryThemeColor, RectangleClient, PlaitElement, idCreator, isNullOrUndefined, Transforms, clearSelectedElement, addSelectedElement, PlaitBoard, depthFirstRecursion, Path, drawLinearPath, createG, updateForeignObject, PlaitNode, drawRoundRectangle, getRectangleByElements, getSelectedElements, NODE_TO_PARENT, distanceBetweenPointAndRectangle, createForeignObject, drawAbstractRoundRectangle, createText, PlaitPointerType, PlaitPluginElementComponent, NODE_TO_INDEX, PlaitModule, transformPoint, toPoint, distanceBetweenPointAndPoint, CLIP_BOARD_FORMAT_KEY, isMainPointer, BOARD_TO_HOST, PlaitPluginKey, throttleRAF, BoardTransforms, removeSelectedElement, PlaitHistoryBoard, hotkeys } from '@plait/core';
5
5
  import { MindLayoutType, isIndentedLayout, getNonAbstractChildren, isStandardLayout, AbstractNode, isLeftLayout, isRightLayout, isVerticalLogicLayout, isHorizontalLogicLayout, isTopLayout, isBottomLayout, isHorizontalLayout, getCorrectStartEnd, getAbstractLayout, ConnectingPosition, GlobalLayout } from '@plait/layouts';
6
- import { getSizeByText, ROOT_DEFAULT_HEIGHT, TEXT_DEFAULT_HEIGHT, drawRichtext, updateRichText, setFullSelectionAndFocus, getRichtextContentSize, hasEditableTarget, RichtextModule } from '@plait/richtext';
7
- import { fromEvent, Subject, timer } from 'rxjs';
8
- import { take, takeUntil, filter, debounceTime } from 'rxjs/operators';
9
- import { Node, Path as Path$1, Editor, Operation } from 'slate';
6
+ import { getTextSize, TEXT_DEFAULT_HEIGHT, TextManage, TextModule } from '@plait/text';
7
+ import { fromEvent, Subject } from 'rxjs';
8
+ import { Node, Path as Path$1 } from 'slate';
10
9
  import { isKeyHotkey } from 'is-hotkey';
11
10
  import { pointsOnBezierCurves } from 'points-on-curve';
11
+ import { take, filter } from 'rxjs/operators';
12
12
  import * as i1 from '@angular/common';
13
13
  import { CommonModule } from '@angular/common';
14
14
 
15
15
  const ELEMENT_TO_NODE = new WeakMap();
16
16
 
17
- const BASE = 4;
18
- const PRIMARY_COLOR = '#6698FF';
19
- const TRANSPARENT = 'transparent';
20
- const GRAY_COLOR = '#AAAAAA';
21
- const STROKE_WIDTH = 3;
22
- const BRANCH_WIDTH = 3;
23
- const EXTEND_OFFSET = 8;
24
- const EXTEND_RADIUS = 16;
25
- const QUICK_INSERT_CIRCLE_OFFSET = 9;
26
- const QUICK_INSERT_CIRCLE_COLOR = '#6698FF';
27
- const QUICK_INSERT_INNER_CROSS_COLOR = 'white';
28
-
29
- const DefaultAbstractNodeStyle = {
30
- strokeColor: GRAY_COLOR,
31
- strokeWidth: 2,
32
- branchColor: GRAY_COLOR,
33
- branchWidth: 2
34
- };
35
- const DefaultNodeStyle = {
36
- strokeWidth: 3,
37
- branchWidth: 3,
38
- fill: 'none'
39
- };
40
-
41
- const TOPIC_COLOR = '#333';
42
- const TOPIC_FONT_SIZE = 14;
43
- const ROOT_TOPIC_FONT_SIZE = 18;
44
- const TOPIC_DEFAULT_MAX_WORD_COUNT = 34;
45
-
46
- const NODE_MIN_WIDTH = 18;
47
-
48
- const ABSTRACT_HANDLE_COLOR = '#6698FF80'; //primary color 50% opacity
49
- const ABSTRACT_INCLUDED_OUTLINE_OFFSET = 3.5;
50
- const ABSTRACT_HANDLE_LENGTH = 10;
51
- const ABSTRACT_HANDLE_MASK_WIDTH = 8;
52
-
53
17
  const MindNode = {
54
18
  get(root, path) {
55
19
  let node = root;
@@ -215,36 +179,17 @@ const MindThemeColor = {
215
179
  }
216
180
  };
217
181
 
218
- function getRectangleByNode(node) {
219
- const x = node.x + node.hGap;
220
- let y = node.y + node.vGap;
221
- const width = node.width - node.hGap * 2;
222
- const height = node.height - node.vGap * 2;
223
- return {
224
- x,
225
- y,
226
- width,
227
- height
228
- };
229
- }
230
- function getRectangleByElement(board, originPoint, element) {
231
- const nodeRectangle = {
232
- x: originPoint[0],
233
- y: originPoint[1],
234
- width: NodeSpace.getNodeWidth(board, element),
235
- height: NodeSpace.getNodeHeight(board, element)
236
- };
237
- return nodeRectangle;
238
- }
239
- function isHitMindElement(board, point, element) {
240
- const node = MindElement.getNode(element);
241
- if (node && distanceBetweenPointAndRectangle(point[0], point[1], getRectangleByNode(node)) === 0) {
242
- return true;
243
- }
244
- else {
245
- return false;
246
- }
247
- }
182
+ const BASE = 4;
183
+ const PRIMARY_COLOR = '#6698FF';
184
+ const TRANSPARENT = 'transparent';
185
+ const GRAY_COLOR = '#AAAAAA';
186
+ const STROKE_WIDTH = 3;
187
+ const BRANCH_WIDTH = 3;
188
+ const EXTEND_OFFSET = 8;
189
+ const EXTEND_DIAMETER = 16;
190
+ const QUICK_INSERT_CIRCLE_OFFSET = 9;
191
+ const QUICK_INSERT_CIRCLE_COLOR = '#6698FF';
192
+ const QUICK_INSERT_INNER_CROSS_COLOR = 'white';
248
193
 
249
194
  function getEmojisWidthHeight(board, element) {
250
195
  const options = board.getMindOptions();
@@ -264,6 +209,79 @@ function getEmojiFontSize(element) {
264
209
  }
265
210
  }
266
211
 
212
+ const NodeDefaultSpace = {
213
+ horizontal: {
214
+ nodeAndText: BASE * 3,
215
+ emojiAndText: BASE * 1.5
216
+ },
217
+ vertical: {
218
+ nodeAndText: BASE * 1.5
219
+ }
220
+ };
221
+ const RootDefaultSpace = {
222
+ horizontal: {
223
+ nodeAndText: BASE * 4,
224
+ emojiAndText: BASE * 2
225
+ },
226
+ vertical: {
227
+ nodeAndText: BASE * 2
228
+ }
229
+ };
230
+ const getHorizontalSpaceBetweenNodeAndText = (board, element) => {
231
+ const isMind = PlaitMind.isMind(element);
232
+ const nodeAndText = isMind ? RootDefaultSpace.horizontal.nodeAndText : NodeDefaultSpace.horizontal.nodeAndText;
233
+ return nodeAndText;
234
+ };
235
+ const getVerticalSpaceBetweenNodeAndText = (element) => {
236
+ const isMind = PlaitMind.isMind(element);
237
+ const nodeAndText = isMind ? RootDefaultSpace.vertical.nodeAndText : NodeDefaultSpace.vertical.nodeAndText;
238
+ return nodeAndText;
239
+ };
240
+ const getSpaceEmojiAndText = (element) => {
241
+ const isMind = PlaitMind.isMind(element);
242
+ const emojiAndText = isMind ? RootDefaultSpace.horizontal.emojiAndText : NodeDefaultSpace.horizontal.emojiAndText;
243
+ return emojiAndText;
244
+ };
245
+ const NodeSpace = {
246
+ getNodeWidth(board, element) {
247
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
248
+ if (MindElement.hasEmojis(element)) {
249
+ return (NodeSpace.getEmojiLeftSpace(board, element) +
250
+ getEmojisWidthHeight(board, element).width +
251
+ getSpaceEmojiAndText(element) +
252
+ element.width +
253
+ nodeAndText);
254
+ }
255
+ return nodeAndText + element.width + nodeAndText;
256
+ },
257
+ getNodeHeight(board, element) {
258
+ const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
259
+ return nodeAndText + element.height + nodeAndText;
260
+ },
261
+ getTextLeftSpace(board, element) {
262
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
263
+ if (MindElement.hasEmojis(element)) {
264
+ return NodeSpace.getEmojiLeftSpace(board, element) + getEmojisWidthHeight(board, element).width + getSpaceEmojiAndText(element);
265
+ }
266
+ else {
267
+ return nodeAndText;
268
+ }
269
+ },
270
+ getTextTopSpace(element) {
271
+ const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
272
+ return nodeAndText;
273
+ },
274
+ getEmojiLeftSpace(board, element) {
275
+ const options = board.getMindOptions();
276
+ const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
277
+ return nodeAndText - options.emojiPadding;
278
+ },
279
+ getEmojiTopSpace(element) {
280
+ const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
281
+ return nodeAndText;
282
+ }
283
+ };
284
+
267
285
  function getEmojiRectangle(board, element) {
268
286
  let { x, y } = getRectangleByNode(MindElement.getNode(element));
269
287
  x = x + NodeSpace.getEmojiLeftSpace(board, element);
@@ -290,18 +308,38 @@ const isHitEmojis = (board, element, point) => {
290
308
  return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), getEmojiRectangle(board, element));
291
309
  };
292
310
 
293
- function enterNodeEditing(element) {
311
+ function getTopicRectangleByNode(board, node) {
312
+ let nodeRectangle = getRectangleByNode(node);
313
+ return getTopicRectangleByElement(board, nodeRectangle, node.origin);
314
+ }
315
+ function getTopicRectangleByElement(board, nodeRectangle, element) {
316
+ const x = nodeRectangle.x + NodeSpace.getTextLeftSpace(board, element);
317
+ const y = nodeRectangle.y + NodeSpace.getTextTopSpace(element);
318
+ const width = Math.ceil(element.width);
319
+ const height = Math.ceil(element.height);
320
+ return { height, width, x, y };
321
+ }
322
+
323
+ const NODE_MIN_WIDTH = 18;
324
+
325
+ function editTopic(element) {
294
326
  const component = PlaitElement.getComponent(element);
295
- component.startEditText(false, false);
327
+ component.editTopic();
296
328
  }
297
329
 
330
+ const TOPIC_COLOR = '#333';
331
+ const TOPIC_FONT_SIZE = 14;
332
+ const ROOT_TOPIC_FONT_SIZE = 18;
333
+ const ROOT_TOPIC_HEIGHT = 25;
334
+ const TOPIC_DEFAULT_MAX_WORD_COUNT = 34;
335
+
298
336
  const adjustRootToNode = (board, node) => {
299
337
  const newNode = { ...node };
300
338
  delete newNode.isRoot;
301
339
  delete newNode.rightNodeCount;
302
340
  delete newNode.type;
303
341
  const text = Node.string(node.data.topic.children[0]) || ' ';
304
- const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT);
342
+ const { width, height } = getTextSize(board, text, TOPIC_DEFAULT_MAX_WORD_COUNT);
305
343
  newNode.width = Math.max(width, NODE_MIN_WIDTH);
306
344
  newNode.height = height;
307
345
  if (newNode.layout === MindLayoutType.standard) {
@@ -326,7 +364,7 @@ const adjustNodeToRoot = (board, node) => {
326
364
  delete newElement?.fill;
327
365
  delete newElement?.shape;
328
366
  delete newElement?.strokeWidth;
329
- const { width, height } = getSizeByText(text, PlaitBoard.getViewportContainer(board), TOPIC_DEFAULT_MAX_WORD_COUNT, ROOT_TOPIC_FONT_SIZE);
367
+ const { width, height } = getTextSize(board, text, TOPIC_DEFAULT_MAX_WORD_COUNT, ROOT_TOPIC_FONT_SIZE);
330
368
  newElement.width = Math.max(width, NODE_MIN_WIDTH);
331
369
  newElement.height = height;
332
370
  return {
@@ -345,7 +383,7 @@ const createEmptyMind = (board, point) => {
345
383
  return rootElement;
346
384
  };
347
385
  const createDefaultMind = (point, rightNodeCount, layout) => {
348
- const root = createMindElement('思维导图', 72, ROOT_DEFAULT_HEIGHT, { layout });
386
+ const root = createMindElement('思维导图', 72, ROOT_TOPIC_HEIGHT, { layout });
349
387
  root.rightNodeCount = rightNodeCount;
350
388
  root.isRoot = true;
351
389
  root.type = 'mindmap';
@@ -452,7 +490,7 @@ const insertMindElement = (board, inheritNode, path) => {
452
490
  clearSelectedElement(board);
453
491
  addSelectedElement(board, newElement);
454
492
  setTimeout(() => {
455
- enterNodeEditing(newElement);
493
+ editTopic(newElement);
456
494
  });
457
495
  };
458
496
  const findLastChild = (child) => {
@@ -582,6 +620,18 @@ const getRootLayout = (root) => {
582
620
  return root.layout || getDefaultLayout();
583
621
  };
584
622
 
623
+ const DefaultAbstractNodeStyle = {
624
+ strokeColor: GRAY_COLOR,
625
+ strokeWidth: 2,
626
+ branchColor: GRAY_COLOR,
627
+ branchWidth: 2
628
+ };
629
+ const DefaultNodeStyle = {
630
+ strokeWidth: 3,
631
+ branchWidth: 3,
632
+ fill: 'none'
633
+ };
634
+
585
635
  const getAvailableProperty = (board, element, propertyKey) => {
586
636
  return element[propertyKey];
587
637
  };
@@ -684,6 +734,12 @@ const isDragging = (board) => {
684
734
  };
685
735
  const setIsDragging = (board, state) => {
686
736
  IS_DRAGGING.set(board, state);
737
+ if (state) {
738
+ PlaitBoard.getBoardNativeElement(board).classList.add('mind-node-dragging');
739
+ }
740
+ else {
741
+ PlaitBoard.getBoardNativeElement(board).classList.remove('mind-node-dragging');
742
+ }
687
743
  };
688
744
  const hasPreviousOrNextOfDropPath = (parent, dropTarget, dropPath) => {
689
745
  let children = getNonAbstractChildren(parent);
@@ -897,23 +953,10 @@ const getPathByDropTarget = (board, dropTarget) => {
897
953
  return targetPath;
898
954
  };
899
955
 
900
- function drawRoundRectangleByNode(board, node) {
901
- const rectangle = getRectangleByNode(node);
902
- return drawRoundRectangleByElement(board, rectangle, node.origin);
903
- }
904
- function drawRoundRectangleByElement(board, nodeRectangle, element) {
905
- const defaultRootFill = getMindThemeColor(board).rootFill;
906
- const fill = element.fill ? element.fill : element.isRoot ? defaultRootFill : DefaultNodeStyle.fill;
907
- const stroke = getStrokeByMindElement(board, element);
908
- const strokeWidth = element.strokeWidth ? element.strokeWidth : DefaultNodeStyle.strokeWidth;
909
- const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), nodeRectangle.x, nodeRectangle.y, nodeRectangle.x + nodeRectangle.width, nodeRectangle.y + nodeRectangle.height, {
910
- stroke,
911
- strokeWidth,
912
- fill,
913
- fillStyle: 'solid'
914
- });
915
- return nodeG;
916
- }
956
+ const ABSTRACT_HANDLE_COLOR = '#6698FF80'; //primary color 50% opacity
957
+ const ABSTRACT_INCLUDED_OUTLINE_OFFSET = 3.5;
958
+ const ABSTRACT_HANDLE_LENGTH = 10;
959
+ const ABSTRACT_HANDLE_MASK_WIDTH = 8;
917
960
 
918
961
  var HorizontalPlacement;
919
962
  (function (HorizontalPlacement) {
@@ -1171,13 +1214,13 @@ const drawFakeDragNode = (board, element, offsetX, offsetY) => {
1171
1214
  };
1172
1215
  const textRectangle = getTopicRectangleByNode(board, activeComponent.node);
1173
1216
  const fakeNodeG = drawRoundRectangleByNode(board, fakeDraggingNode);
1174
- const richtextG = activeComponent.richtextG?.cloneNode(true);
1217
+ const richtextG = activeComponent.textManage.g.cloneNode(true);
1175
1218
  updateForeignObject(richtextG, textRectangle.width, textRectangle.height, textRectangle.x + offsetX, textRectangle.y + offsetY);
1176
1219
  dragFakeNodeG?.append(fakeNodeG);
1177
1220
  dragFakeNodeG?.append(richtextG);
1178
1221
  // draw emojis
1179
1222
  if (MindElement.hasEmojis(element)) {
1180
- const fakeEmojisG = activeComponent.emojisDrawer.g.cloneNode(true);
1223
+ const fakeEmojisG = activeComponent.nodeEmojisDrawer.g.cloneNode(true);
1181
1224
  const foreignRectangle = getEmojiForeignRectangle(board, element);
1182
1225
  updateForeignObject(fakeEmojisG, foreignRectangle.width, foreignRectangle.height, foreignRectangle.x + offsetX, foreignRectangle.y + offsetY);
1183
1226
  dragFakeNodeG?.append(fakeEmojisG);
@@ -1637,9 +1680,7 @@ function findLocationLeftIndex(board, parentChildren, location, isHorizontal) {
1637
1680
  }
1638
1681
  function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1639
1682
  let touchedHandle;
1640
- const abstract = getSelectedElements(board)
1641
- .filter(element => AbstractNode.isAbstract(element))
1642
- .find(element => {
1683
+ const abstract = getSelectedElements(board).filter(element => AbstractNode.isAbstract(element)).find(element => {
1643
1684
  touchedHandle = getHitAbstractHandle(board, element, endPoint);
1644
1685
  return touchedHandle;
1645
1686
  });
@@ -1648,13 +1689,13 @@ function handleTouchedAbstract(board, touchedAbstract, endPoint) {
1648
1689
  }
1649
1690
  if (touchedAbstract) {
1650
1691
  const component = PlaitElement.getComponent(touchedAbstract);
1651
- component.updateAbstractIncludedOutline();
1692
+ component.activeDrawer.updateAbstractOutline(touchedAbstract);
1652
1693
  touchedAbstract = undefined;
1653
1694
  }
1654
1695
  if (abstract) {
1655
1696
  touchedAbstract = abstract;
1656
1697
  const component = PlaitElement.getComponent(touchedAbstract);
1657
- component.updateAbstractIncludedOutline(touchedHandle);
1698
+ component.activeDrawer.updateAbstractOutline(touchedAbstract, touchedHandle);
1658
1699
  }
1659
1700
  return touchedAbstract;
1660
1701
  }
@@ -1879,6 +1920,9 @@ const MindElement = {
1879
1920
  },
1880
1921
  getEmojis(element) {
1881
1922
  return element.data.emojis;
1923
+ },
1924
+ getEditor(element) {
1925
+ return PlaitElement.getComponent(element).textManage.componentRef.instance.editor;
1882
1926
  }
1883
1927
  };
1884
1928
  var MindElementShape;
@@ -1892,118 +1936,53 @@ var BranchShape;
1892
1936
  BranchShape["polyline"] = "polyline";
1893
1937
  })(BranchShape || (BranchShape = {}));
1894
1938
 
1895
- const NodeDefaultSpace = {
1896
- horizontal: {
1897
- nodeAndText: BASE * 3,
1898
- emojiAndText: BASE * 1.5
1899
- },
1900
- vertical: {
1901
- nodeAndText: BASE * 1.5
1939
+ function getRectangleByNode(node) {
1940
+ const x = node.x + node.hGap;
1941
+ let y = node.y + node.vGap;
1942
+ const width = node.width - node.hGap * 2;
1943
+ const height = node.height - node.vGap * 2;
1944
+ return {
1945
+ x,
1946
+ y,
1947
+ width,
1948
+ height
1949
+ };
1950
+ }
1951
+ function getRectangleByElement(board, originPoint, element) {
1952
+ const nodeRectangle = {
1953
+ x: originPoint[0],
1954
+ y: originPoint[1],
1955
+ width: NodeSpace.getNodeWidth(board, element),
1956
+ height: NodeSpace.getNodeHeight(board, element)
1957
+ };
1958
+ return nodeRectangle;
1959
+ }
1960
+ function isHitMindElement(board, point, element) {
1961
+ const node = MindElement.getNode(element);
1962
+ if (node && distanceBetweenPointAndRectangle(point[0], point[1], getRectangleByNode(node)) === 0) {
1963
+ return true;
1902
1964
  }
1903
- };
1904
- const RootDefaultSpace = {
1905
- horizontal: {
1906
- nodeAndText: BASE * 4,
1907
- emojiAndText: BASE * 2
1908
- },
1909
- vertical: {
1910
- nodeAndText: BASE * 2
1965
+ else {
1966
+ return false;
1911
1967
  }
1912
- };
1913
- const getHorizontalSpaceBetweenNodeAndText = (board, element) => {
1914
- const isMind = PlaitMind.isMind(element);
1915
- const nodeAndText = isMind ? RootDefaultSpace.horizontal.nodeAndText : NodeDefaultSpace.horizontal.nodeAndText;
1916
- return nodeAndText;
1917
- };
1918
- const getVerticalSpaceBetweenNodeAndText = (element) => {
1919
- const isMind = PlaitMind.isMind(element);
1920
- const nodeAndText = isMind ? RootDefaultSpace.vertical.nodeAndText : NodeDefaultSpace.vertical.nodeAndText;
1921
- return nodeAndText;
1922
- };
1923
- const getSpaceEmojiAndText = (element) => {
1924
- const isMind = PlaitMind.isMind(element);
1925
- const emojiAndText = isMind ? RootDefaultSpace.horizontal.emojiAndText : NodeDefaultSpace.horizontal.emojiAndText;
1926
- return emojiAndText;
1927
- };
1928
- const NodeSpace = {
1929
- getNodeWidth(board, element) {
1930
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
1931
- if (MindElement.hasEmojis(element)) {
1932
- return (NodeSpace.getEmojiLeftSpace(board, element) +
1933
- getEmojisWidthHeight(board, element).width +
1934
- getSpaceEmojiAndText(element) +
1935
- element.width +
1936
- nodeAndText);
1937
- }
1938
- return nodeAndText + element.width + nodeAndText;
1939
- },
1940
- getNodeHeight(board, element) {
1941
- const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1942
- return nodeAndText + element.height + nodeAndText;
1943
- },
1944
- getTextLeftSpace(board, element) {
1945
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
1946
- if (MindElement.hasEmojis(element)) {
1947
- return NodeSpace.getEmojiLeftSpace(board, element) + getEmojisWidthHeight(board, element).width + getSpaceEmojiAndText(element);
1948
- }
1949
- else {
1950
- return nodeAndText;
1951
- }
1952
- },
1953
- getTextTopSpace(element) {
1954
- const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1955
- return nodeAndText;
1956
- },
1957
- getEmojiLeftSpace(board, element) {
1958
- const options = board.getMindOptions();
1959
- const nodeAndText = getHorizontalSpaceBetweenNodeAndText(board, element);
1960
- return nodeAndText - options.emojiPadding;
1961
- },
1962
- getEmojiTopSpace(element) {
1963
- const nodeAndText = getVerticalSpaceBetweenNodeAndText(element);
1964
- return nodeAndText;
1965
- }
1966
- };
1967
-
1968
- function getTopicRectangleByNode(board, node) {
1969
- let nodeRectangle = getRectangleByNode(node);
1970
- return getTopicRectangleByElement(board, nodeRectangle, node.origin);
1971
- }
1972
- function getTopicRectangleByElement(board, nodeRectangle, element) {
1973
- const x = nodeRectangle.x + NodeSpace.getTextLeftSpace(board, element);
1974
- const y = nodeRectangle.y + NodeSpace.getTextTopSpace(element);
1975
- const width = Math.ceil(element.width);
1976
- const height = Math.ceil(element.height);
1977
- return { height, width, x, y };
1978
1968
  }
1979
1969
 
1980
- function drawTopicByNode(board, node, viewContainerRef) {
1981
- const rectangle = getTopicRectangleByNode(board, node);
1982
- return drawTopicByElement(board, rectangle, node.origin, viewContainerRef);
1983
- }
1984
- function drawTopicByElement(board, rectangle, element, viewContainerRef) {
1985
- const containerRef = viewContainerRef || PlaitBoard.getComponent(board).viewContainerRef;
1986
- const classList = [];
1987
- if (element.isRoot) {
1988
- classList.push('root-node');
1989
- classList.push('font-size-18');
1990
- }
1991
- else {
1992
- classList.push('child-node');
1993
- }
1994
- // COMPAT: last character can not show in safari browser
1995
- return drawRichtext(rectangle.x, rectangle.y, rectangle.width, rectangle.height, element.data.topic, containerRef, classList);
1970
+ function drawRoundRectangleByNode(board, node) {
1971
+ const rectangle = getRectangleByNode(node);
1972
+ return drawRoundRectangleByElement(board, rectangle, node.origin);
1996
1973
  }
1997
- function updateMindNodeTopicSize(board, node, g, isEditable) {
1998
- const { x, y, width, height } = getTopicRectangleByNode(board, node);
1999
- if (isEditable) {
2000
- // add 999, avoid changing lines when paste more text
2001
- updateForeignObject(g, width + 999, height + 999, x, y);
2002
- }
2003
- else {
2004
- // COMPAT: last character can not show in safari browser
2005
- updateForeignObject(g, width, height, x, y);
2006
- }
1974
+ function drawRoundRectangleByElement(board, nodeRectangle, element) {
1975
+ const defaultRootFill = getMindThemeColor(board).rootFill;
1976
+ const fill = element.fill ? element.fill : element.isRoot ? defaultRootFill : DefaultNodeStyle.fill;
1977
+ const stroke = getStrokeByMindElement(board, element);
1978
+ const strokeWidth = element.strokeWidth ? element.strokeWidth : DefaultNodeStyle.strokeWidth;
1979
+ const nodeG = drawRoundRectangle(PlaitBoard.getRoughSVG(board), nodeRectangle.x, nodeRectangle.y, nodeRectangle.x + nodeRectangle.width, nodeRectangle.y + nodeRectangle.height, {
1980
+ stroke,
1981
+ strokeWidth,
1982
+ fill,
1983
+ fillStyle: 'solid'
1984
+ });
1985
+ return nodeG;
2007
1986
  }
2008
1987
 
2009
1988
  function drawAbstractLink(board, node, isHorizontal) {
@@ -2084,7 +2063,7 @@ class EmojiDrawer {
2084
2063
  }
2085
2064
  }
2086
2065
  }
2087
- class EmojisDrawer {
2066
+ class NodeEmojisDrawer {
2088
2067
  constructor(board, viewContainerRef) {
2089
2068
  this.board = board;
2090
2069
  this.viewContainerRef = viewContainerRef;
@@ -2210,11 +2189,15 @@ const correctLogicLayoutNode = (board, layout, path) => {
2210
2189
  }
2211
2190
  };
2212
2191
 
2192
+ const normalizeWidthAndHeight = (board, width, height) => {
2193
+ const newWidth = width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom;
2194
+ const newHeight = height / board.viewport.zoom;
2195
+ return { width: newWidth, height: newHeight };
2196
+ };
2213
2197
  const setTopic = (board, element, topic, width, height) => {
2214
2198
  const newElement = {
2215
2199
  data: { topic },
2216
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2217
- height: height / board.viewport.zoom
2200
+ ...normalizeWidthAndHeight(board, width, height)
2218
2201
  };
2219
2202
  if (MindElement.hasEmojis(element)) {
2220
2203
  newElement.data.emojis = element.data.emojis;
@@ -2224,11 +2207,10 @@ const setTopic = (board, element, topic, width, height) => {
2224
2207
  };
2225
2208
  const setTopicSize = (board, element, width, height) => {
2226
2209
  const newElement = {
2227
- width: width < NODE_MIN_WIDTH * board.viewport.zoom ? NODE_MIN_WIDTH : width / board.viewport.zoom,
2228
- height: height / board.viewport.zoom
2210
+ ...normalizeWidthAndHeight(board, width, height)
2229
2211
  };
2230
- const path = PlaitBoard.findPath(board, element);
2231
- if (newElement.width !== element.width || newElement.height !== element.height) {
2212
+ if (element.width !== newElement.width || element.height !== newElement.height) {
2213
+ const path = PlaitBoard.findPath(board, element);
2232
2214
  Transforms.setNode(board, newElement, path);
2233
2215
  }
2234
2216
  };
@@ -2327,6 +2309,127 @@ const MindTransforms = {
2327
2309
  setRightNodeCountByRefs
2328
2310
  };
2329
2311
 
2312
+ class BaseDrawer {
2313
+ constructor(board) {
2314
+ this.board = board;
2315
+ }
2316
+ draw(element, parentG, data) {
2317
+ this.destroy();
2318
+ if (this.canDraw && this.canDraw(element, data)) {
2319
+ const g = this.baseDraw(element, data);
2320
+ if (g) {
2321
+ parentG.append(g);
2322
+ }
2323
+ if (hasAfterDraw(this)) {
2324
+ this.afterDraw(element);
2325
+ }
2326
+ }
2327
+ }
2328
+ destroy() {
2329
+ if (this.g) {
2330
+ this.g.remove();
2331
+ }
2332
+ }
2333
+ }
2334
+ function hasAfterDraw(value) {
2335
+ if (value.afterDraw) {
2336
+ return true;
2337
+ }
2338
+ return false;
2339
+ }
2340
+
2341
+ function findNewChildNodePath(board, element) {
2342
+ const children = getNonAbstractChildren(element);
2343
+ return PlaitBoard.findPath(board, element).concat(children.length);
2344
+ }
2345
+ function findNewSiblingNodePath(board, element) {
2346
+ const path = PlaitBoard.findPath(board, element);
2347
+ return Path$1.next(path);
2348
+ }
2349
+
2350
+ class NodeInsertDrawer extends BaseDrawer {
2351
+ canDraw(element) {
2352
+ if (PlaitBoard.isReadonly(this.board) || element?.isCollapsed) {
2353
+ return false;
2354
+ }
2355
+ return true;
2356
+ }
2357
+ baseDraw(element) {
2358
+ const quickInsertG = createG();
2359
+ this.g = quickInsertG;
2360
+ quickInsertG.classList.add('quick-insert');
2361
+ const node = MindElement.getNode(element);
2362
+ const layout = MindQueries.getLayoutByElement(element);
2363
+ const isHorizontal = isHorizontalLayout(layout);
2364
+ let linkDirection = getLayoutDirection(node, isHorizontal);
2365
+ if (isIndentedLayout(layout)) {
2366
+ linkDirection = isTopLayout(layout) ? LayoutDirection.top : LayoutDirection.bottom;
2367
+ }
2368
+ const isUnderlineShape = getShapeByElement(this.board, element) === MindElementShape.underline;
2369
+ const nodeClient = getRectangleByNode(node);
2370
+ const branchWidth = getBranchWidthByMindElement(this.board, element);
2371
+ const branchColor = PlaitMind.isMind(element)
2372
+ ? getNextBranchColor(this.board, element)
2373
+ : getBranchColorByMindElement(this.board, element);
2374
+ let distance = 8;
2375
+ let placement = [HorizontalPlacement.right, VerticalPlacement.middle];
2376
+ transformPlacement(placement, linkDirection);
2377
+ // underline shape and horizontal
2378
+ if (isHorizontal && isUnderlineShape && !element.isRoot) {
2379
+ placement[1] = VerticalPlacement.bottom;
2380
+ }
2381
+ let beginPoint = getPointByPlacement(nodeClient, placement);
2382
+ if (element.children.length > 0 && !element.isRoot) {
2383
+ beginPoint = moveXOfPoint(beginPoint, EXTEND_DIAMETER + 8, linkDirection);
2384
+ distance = 5;
2385
+ }
2386
+ const endPoint = moveXOfPoint(beginPoint, distance, linkDirection);
2387
+ const circleCenter = moveXOfPoint(endPoint, 8, linkDirection);
2388
+ const line = PlaitBoard.getRoughSVG(this.board).line(beginPoint[0], beginPoint[1], endPoint[0], endPoint[1], {
2389
+ stroke: branchColor,
2390
+ strokeWidth: branchWidth
2391
+ });
2392
+ const circle = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2393
+ fill: QUICK_INSERT_CIRCLE_COLOR,
2394
+ stroke: QUICK_INSERT_CIRCLE_COLOR,
2395
+ fillStyle: 'solid'
2396
+ });
2397
+ const HLineBeginPoint = [circleCenter[0] - 5, circleCenter[1]];
2398
+ const HLineEndPoint = [circleCenter[0] + 5, circleCenter[1]];
2399
+ const VLineBeginPoint = [circleCenter[0], circleCenter[1] - 5];
2400
+ const VLineEndPoint = [circleCenter[0], circleCenter[1] + 5];
2401
+ const innerCrossHLine = PlaitBoard.getRoughSVG(this.board).line(HLineBeginPoint[0], HLineBeginPoint[1], HLineEndPoint[0], HLineEndPoint[1], {
2402
+ stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2403
+ strokeWidth: 2
2404
+ });
2405
+ const innerCrossVLine = PlaitBoard.getRoughSVG(this.board).line(VLineBeginPoint[0], VLineBeginPoint[1], VLineEndPoint[0], VLineEndPoint[1], {
2406
+ stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2407
+ strokeWidth: 2
2408
+ });
2409
+ quickInsertG.appendChild(line);
2410
+ quickInsertG.appendChild(circle);
2411
+ quickInsertG.appendChild(innerCrossHLine);
2412
+ quickInsertG.appendChild(innerCrossVLine);
2413
+ return quickInsertG;
2414
+ }
2415
+ afterDraw(element) {
2416
+ if (!this.g) {
2417
+ throw new Error(`can not find quick insert g`);
2418
+ }
2419
+ fromEvent(this.g, 'mousedown')
2420
+ .pipe(take(1))
2421
+ .subscribe(e => {
2422
+ e.stopPropagation();
2423
+ });
2424
+ fromEvent(this.g, 'mouseup')
2425
+ .pipe(take(1))
2426
+ .subscribe(() => {
2427
+ const path = findNewChildNodePath(this.board, element);
2428
+ insertMindElement(this.board, element, path);
2429
+ });
2430
+ }
2431
+ }
2432
+
2330
2433
  function drawAbstractIncludedOutline(board, roughSVG, element, activeHandlePosition, resizingLocation) {
2331
2434
  const abstractIncludedG = createG();
2332
2435
  const parentElement = MindElement.getParent(element);
@@ -2392,294 +2495,206 @@ function changeBoardClass(board, activeHandlePosition, isHorizontal) {
2392
2495
  }
2393
2496
  }
2394
2497
 
2395
- class BaseDrawer {
2396
- constructor(board) {
2397
- this.board = board;
2498
+ class NodeActiveDrawer extends BaseDrawer {
2499
+ canDraw(element, data) {
2500
+ if (data.selected) {
2501
+ return true;
2502
+ }
2503
+ else {
2504
+ return false;
2505
+ }
2398
2506
  }
2399
- destroy() {
2400
- if (this.g) {
2401
- this.g.remove();
2507
+ baseDraw(element, data) {
2508
+ const activeG = createG();
2509
+ this.g = activeG;
2510
+ if (AbstractNode.isAbstract(element)) {
2511
+ this.abstractOutlineG = drawAbstractIncludedOutline(this.board, PlaitBoard.getRoughSVG(this.board), element);
2512
+ activeG.append(this.abstractOutlineG);
2513
+ }
2514
+ const node = MindElement.getNode(element);
2515
+ let { x, y, width, height } = getRectangleByNode(node);
2516
+ const strokeG = drawRoundRectangle(PlaitBoard.getRoughSVG(this.board), x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, strokeWidth: 2, fill: '' }, true);
2517
+ this.g.appendChild(strokeG);
2518
+ if (!data.isEditing) {
2519
+ const fillG = drawRoundRectangle(PlaitBoard.getRoughSVG(this.board), x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, fill: PRIMARY_COLOR, fillStyle: 'solid' }, true);
2520
+ fillG.style.opacity = '0.15';
2521
+ this.g.appendChild(fillG);
2402
2522
  }
2523
+ return activeG;
2403
2524
  }
2404
- }
2405
- function hasAfterDraw(value) {
2406
- if (value.afterDraw) {
2407
- return true;
2525
+ updateAbstractOutline(element, activeHandlePosition, resizingLocation) {
2526
+ if (this.abstractOutlineG) {
2527
+ this.abstractOutlineG.remove();
2528
+ }
2529
+ this.abstractOutlineG = drawAbstractIncludedOutline(this.board, PlaitBoard.getRoughSVG(this.board), element, activeHandlePosition, resizingLocation);
2530
+ this.g.append(this.abstractOutlineG);
2408
2531
  }
2409
- return false;
2410
- }
2411
-
2412
- function findNewChildNodePath(board, element) {
2413
- const children = getNonAbstractChildren(element);
2414
- return PlaitBoard.findPath(board, element).concat(children.length);
2415
- }
2416
- function findNewSiblingNodePath(board, element) {
2417
- const path = PlaitBoard.findPath(board, element);
2418
- return Path$1.next(path);
2419
2532
  }
2420
2533
 
2421
- class QuickInsertDrawer extends BaseDrawer {
2534
+ class CollapseDrawer extends BaseDrawer {
2422
2535
  canDraw(element) {
2423
- if (PlaitBoard.isReadonly(this.board) || element?.isCollapsed) {
2424
- return false;
2536
+ if (element.children.length && !PlaitMind.isMind(element)) {
2537
+ return true;
2425
2538
  }
2426
- return true;
2539
+ return false;
2427
2540
  }
2428
- draw(element) {
2429
- let offset = element.children.length > 0 && !element.isRoot ? EXTEND_RADIUS : 0;
2430
- const quickInsertG = createG();
2431
- this.g = quickInsertG;
2432
- quickInsertG.classList.add('quick-insert');
2541
+ baseDraw(element) {
2542
+ const collapseG = createG();
2543
+ this.g = collapseG;
2544
+ collapseG.classList.add('collapse-container');
2433
2545
  const node = MindElement.getNode(element);
2434
- const { x, y, width, height } = getRectangleByNode(node);
2435
- /**
2436
- * 方位:
2437
- * 1. 左、左上、左下
2438
- * 2. 右、右上、右下
2439
- * 3. 上、上左、上右
2440
- * 4. 下、下左、下右
2441
- */
2442
- const shape = getShapeByElement(this.board, element);
2443
- // 形状是矩形要偏移边框的线宽
2546
+ const stroke = getBranchColorByMindElement(this.board, element);
2444
2547
  const branchWidth = getBranchWidthByMindElement(this.board, element);
2445
- let offsetBorderLineWidth = 0;
2446
- if (shape === MindElementShape.roundRectangle && offset === 0) {
2447
- offsetBorderLineWidth = branchWidth;
2548
+ const layout = MindQueries.getLayoutByElement(element);
2549
+ const isUnderlineShape = getShapeByElement(this.board, element) === MindElementShape.underline;
2550
+ const isHorizontal = isHorizontalLayout(layout);
2551
+ const nodeClient = getRectangleByNode(node);
2552
+ let linkDirection = getLayoutDirection(node, isHorizontal);
2553
+ if (isIndentedLayout(layout)) {
2554
+ linkDirection = isTopLayout(layout) ? LayoutDirection.top : LayoutDirection.bottom;
2448
2555
  }
2449
- let offsetRootBorderLineWidth = 0;
2450
- if (element.isRoot) {
2451
- offsetRootBorderLineWidth = branchWidth;
2452
- }
2453
- // 当没有子节点时,需要缩小的偏移量
2454
- const extraOffset = 3;
2455
- const underlineCoordinates = {
2456
- // 画线方向:右向左 <--
2457
- [MindLayoutType.left]: {
2458
- // EXTEND_RADIUS * 0.5 是 左方向,折叠/收起的偏移量
2459
- startX: x - (offset > 0 ? offset + EXTEND_RADIUS * 0.5 : 0) - offsetRootBorderLineWidth,
2460
- startY: y + height,
2461
- endX: x -
2462
- offsetBorderLineWidth -
2463
- offsetRootBorderLineWidth -
2464
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) -
2465
- EXTEND_RADIUS,
2466
- endY: y + height
2467
- },
2468
- // 画线方向:左向右 -->
2469
- [MindLayoutType.right]: {
2470
- startX: x + width + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
2471
- startY: y + height,
2472
- endX: x +
2473
- width +
2474
- offsetBorderLineWidth +
2475
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
2476
- EXTEND_RADIUS +
2477
- offsetRootBorderLineWidth,
2478
- endY: y + height
2479
- },
2480
- // 画线方向:下向上 -->
2481
- [MindLayoutType.upward]: {
2482
- startX: x + width * 0.5,
2483
- startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
2484
- endX: x + width * 0.5,
2485
- endY: y -
2486
- offsetBorderLineWidth -
2487
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) -
2488
- EXTEND_RADIUS -
2489
- offsetRootBorderLineWidth
2490
- },
2491
- // 画线方向:上向下 -->
2492
- [MindLayoutType.downward]: {
2493
- startX: x + width * 0.5,
2494
- startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
2495
- endX: x + width * 0.5,
2496
- endY: y +
2497
- height +
2498
- offsetBorderLineWidth +
2499
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
2500
- EXTEND_RADIUS +
2501
- offsetRootBorderLineWidth
2502
- },
2503
- [MindLayoutType.leftBottomIndented]: {
2504
- startX: x + width * 0.5,
2505
- startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
2506
- endX: x + width * 0.5,
2507
- endY: y +
2508
- height +
2509
- offsetBorderLineWidth +
2510
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
2511
- EXTEND_RADIUS +
2512
- offsetRootBorderLineWidth
2513
- },
2514
- [MindLayoutType.leftTopIndented]: {
2515
- startX: x + width * 0.5,
2516
- startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
2517
- endX: x + width * 0.5,
2518
- endY: y -
2519
- offsetBorderLineWidth -
2520
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) -
2521
- EXTEND_RADIUS -
2522
- offsetRootBorderLineWidth
2523
- },
2524
- [MindLayoutType.rightBottomIndented]: {
2525
- startX: x + width * 0.5,
2526
- startY: y + height + offsetBorderLineWidth + (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) + offsetRootBorderLineWidth,
2527
- endX: x + width * 0.5,
2528
- endY: y +
2529
- height +
2530
- offsetBorderLineWidth +
2531
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET - extraOffset : 0) +
2532
- EXTEND_RADIUS +
2533
- offsetRootBorderLineWidth
2534
- },
2535
- [MindLayoutType.rightTopIndented]: {
2536
- startX: x + width * 0.5,
2537
- startY: y - offsetBorderLineWidth - (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) - offsetRootBorderLineWidth,
2538
- endX: x + width * 0.5,
2539
- endY: y -
2540
- offsetBorderLineWidth -
2541
- (offset > 0 ? offset + QUICK_INSERT_CIRCLE_OFFSET : 0) -
2542
- EXTEND_RADIUS -
2543
- offsetRootBorderLineWidth
2544
- }
2545
- };
2546
- if (shape === MindElementShape.roundRectangle || element.isRoot) {
2547
- underlineCoordinates[MindLayoutType.left].startY -= height * 0.5;
2548
- underlineCoordinates[MindLayoutType.left].endY -= height * 0.5;
2549
- underlineCoordinates[MindLayoutType.right].startY -= height * 0.5;
2550
- underlineCoordinates[MindLayoutType.right].endY -= height * 0.5;
2556
+ let placement = [HorizontalPlacement.right, VerticalPlacement.middle];
2557
+ transformPlacement(placement, linkDirection);
2558
+ // underline shape and horizontal
2559
+ if (isHorizontal && isUnderlineShape && !element.isRoot) {
2560
+ placement[1] = VerticalPlacement.bottom;
2551
2561
  }
2552
- const branchColor = PlaitMind.isMind(element)
2553
- ? getNextBranchColor(this.board, element)
2554
- : getBranchColorByMindElement(this.board, element);
2555
- let nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, element);
2556
- if (element.isRoot && isStandardLayout(nodeLayout)) {
2557
- const root = element;
2558
- nodeLayout = root.children.length >= root.rightNodeCount ? MindLayoutType.left : MindLayoutType.right;
2559
- }
2560
- const underlineCoordinate = underlineCoordinates[nodeLayout];
2561
- if (underlineCoordinate) {
2562
- const underline = PlaitBoard.getRoughSVG(this.board).line(underlineCoordinate.startX, underlineCoordinate.startY, underlineCoordinate.endX, underlineCoordinate.endY, { stroke: branchColor, strokeWidth: branchWidth });
2563
- const circleCoordinates = {
2564
- startX: underlineCoordinate.endX,
2565
- startY: underlineCoordinate.endY
2566
- };
2567
- const circle = PlaitBoard.getRoughSVG(this.board).circle(circleCoordinates.startX, circleCoordinates.startY, EXTEND_RADIUS, {
2568
- fill: QUICK_INSERT_CIRCLE_COLOR,
2569
- stroke: QUICK_INSERT_CIRCLE_COLOR,
2570
- fillStyle: 'solid'
2571
- });
2572
- const innerCrossCoordinates = {
2573
- horizontal: {
2574
- startX: circleCoordinates.startX - EXTEND_RADIUS * 0.5 + 3,
2575
- startY: circleCoordinates.startY,
2576
- endX: circleCoordinates.startX + EXTEND_RADIUS * 0.5 - 3,
2577
- endY: circleCoordinates.startY
2578
- },
2579
- vertical: {
2580
- startX: circleCoordinates.startX,
2581
- startY: circleCoordinates.startY - EXTEND_RADIUS * 0.5 + 3,
2582
- endX: circleCoordinates.startX,
2583
- endY: circleCoordinates.startY + EXTEND_RADIUS * 0.5 - 3
2584
- }
2585
- };
2586
- const innerCrossHLine = PlaitBoard.getRoughSVG(this.board).line(innerCrossCoordinates.horizontal.startX, innerCrossCoordinates.horizontal.startY, innerCrossCoordinates.horizontal.endX, innerCrossCoordinates.horizontal.endY, {
2587
- stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2588
- strokeWidth: 2
2589
- });
2590
- const innerRingVLine = PlaitBoard.getRoughSVG(this.board).line(innerCrossCoordinates.vertical.startX, innerCrossCoordinates.vertical.startY, innerCrossCoordinates.vertical.endX, innerCrossCoordinates.vertical.endY, {
2591
- stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2592
- strokeWidth: 2
2593
- });
2594
- quickInsertG.appendChild(underline);
2595
- quickInsertG.appendChild(circle);
2596
- quickInsertG.appendChild(innerCrossHLine);
2597
- quickInsertG.appendChild(innerRingVLine);
2562
+ let startPoint = getPointByPlacement(nodeClient, placement);
2563
+ const endPoint = moveXOfPoint(startPoint, EXTEND_OFFSET, linkDirection);
2564
+ const circleCenter = moveXOfPoint(endPoint, EXTEND_DIAMETER / 2, linkDirection);
2565
+ const arrowPoints = this.getArrowPoints(circleCenter, linkDirection);
2566
+ const arrowLine = drawLinearPath(arrowPoints, {
2567
+ stroke,
2568
+ strokeWidth: 2
2569
+ });
2570
+ const extendLine = PlaitBoard.getRoughSVG(this.board).line(startPoint[0], startPoint[1], endPoint[0], endPoint[1], {
2571
+ strokeWidth: branchWidth,
2572
+ stroke
2573
+ });
2574
+ const badge = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2575
+ fill: stroke,
2576
+ stroke,
2577
+ fillStyle: 'solid'
2578
+ });
2579
+ const hideCircleG = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2580
+ fill: '#fff',
2581
+ stroke,
2582
+ strokeWidth: branchWidth > 3 ? 3 : branchWidth,
2583
+ fillStyle: 'solid'
2584
+ });
2585
+ if (element.isCollapsed) {
2586
+ let numberOffset = 0;
2587
+ if (getChildrenCount(element) >= 10)
2588
+ numberOffset = -2;
2589
+ if (getChildrenCount(element) === 1)
2590
+ numberOffset = 1;
2591
+ const badgeText = createText(circleCenter[0] - 4 + numberOffset, circleCenter[1] + 4, stroke, `${getChildrenCount(element)}`);
2592
+ badge.setAttribute('style', 'opacity: 0.15');
2593
+ badgeText.setAttribute('style', 'font-size: 12px');
2594
+ collapseG.appendChild(badge);
2595
+ collapseG.appendChild(badgeText);
2596
+ collapseG.appendChild(extendLine);
2598
2597
  }
2599
- return quickInsertG;
2598
+ else {
2599
+ collapseG.appendChild(hideCircleG);
2600
+ collapseG.appendChild(arrowLine);
2601
+ }
2602
+ collapseG.appendChild(extendLine);
2603
+ return collapseG;
2600
2604
  }
2601
2605
  afterDraw(element) {
2602
2606
  if (!this.g) {
2603
2607
  throw new Error(`can not find quick insert g`);
2604
2608
  }
2605
- fromEvent(this.g, 'mousedown')
2606
- .pipe(take(1))
2607
- .subscribe(e => {
2608
- e.stopPropagation();
2609
- });
2610
2609
  fromEvent(this.g, 'mouseup')
2611
- .pipe(take(1))
2610
+ .pipe(filter(() => !PlaitBoard.isPointer(this.board, PlaitPointerType.hand) || !!PlaitBoard.isReadonly(this.board)), take(1))
2612
2611
  .subscribe(() => {
2613
- const path = findNewChildNodePath(this.board, element);
2614
- insertMindElement(this.board, element, path);
2612
+ const isCollapsed = !element.isCollapsed;
2613
+ const newElement = { isCollapsed };
2614
+ const path = PlaitBoard.findPath(this.board, element);
2615
+ Transforms.setNode(this.board, newElement, path);
2615
2616
  });
2616
2617
  }
2618
+ getArrowPoints(circleCenter, linkDirection) {
2619
+ let arrowTopPoint = moveXOfPoint(circleCenter, 2, linkDirection);
2620
+ arrowTopPoint = moveYOfPoint(arrowTopPoint, 4, linkDirection);
2621
+ const arrowMiddlePoint = moveXOfPoint(circleCenter, -2, linkDirection);
2622
+ let arrowBottomPoint = moveXOfPoint(circleCenter, 2, linkDirection);
2623
+ arrowBottomPoint = moveYOfPoint(arrowBottomPoint, -4, linkDirection);
2624
+ return [arrowTopPoint, arrowMiddlePoint, arrowBottomPoint];
2625
+ }
2617
2626
  }
2618
2627
 
2619
2628
  class MindNodeComponent extends PlaitPluginElementComponent {
2620
- get handActive() {
2621
- return this.board.pointer === PlaitPointerType.hand;
2622
- }
2623
- constructor(viewContainerRef, cdr, render2, ngZone) {
2629
+ constructor(viewContainerRef, cdr) {
2624
2630
  super(cdr);
2625
2631
  this.viewContainerRef = viewContainerRef;
2626
2632
  this.cdr = cdr;
2627
- this.render2 = render2;
2628
- this.ngZone = ngZone;
2629
- this.isEditable = false;
2630
- this.activeG = [];
2631
2633
  this.shapeG = null;
2632
2634
  this.destroy$ = new Subject();
2633
2635
  this.trackBy = (index, node) => {
2634
2636
  return node.origin.id;
2635
2637
  };
2636
2638
  }
2639
+ initializeDrawer() {
2640
+ this.nodeEmojisDrawer = new NodeEmojisDrawer(this.board, this.viewContainerRef);
2641
+ this.nodeInsertDrawer = new NodeInsertDrawer(this.board);
2642
+ this.activeDrawer = new NodeActiveDrawer(this.board);
2643
+ this.collapseDrawer = new CollapseDrawer(this.board);
2644
+ this.textManage = new TextManage(this.board, this.viewContainerRef, () => {
2645
+ return getTopicRectangleByNode(this.board, this.node);
2646
+ }, (point) => {
2647
+ return isHitMindElement(this.board, point, this.element);
2648
+ }, (textManageRef) => {
2649
+ const width = textManageRef.width;
2650
+ const height = textManageRef.height;
2651
+ if (textManageRef.newValue) {
2652
+ MindTransforms.setTopic(this.board, this.element, textManageRef.newValue, width, height);
2653
+ }
2654
+ else {
2655
+ MindTransforms.setTopicSize(this.board, this.element, width, height);
2656
+ }
2657
+ });
2658
+ }
2637
2659
  ngOnInit() {
2638
- this.emojisDrawer = new EmojisDrawer(this.board, this.viewContainerRef);
2639
- this.quickInsertDrawer = new QuickInsertDrawer(this.board);
2640
2660
  super.ngOnInit();
2661
+ this.initializeDrawer();
2641
2662
  this.node = MindElement.getNode(this.element);
2642
2663
  this.index = NODE_TO_INDEX.get(this.element) || 0;
2643
2664
  this.roughSVG = PlaitBoard.getRoughSVG(this.board);
2644
2665
  this.parentG = PlaitElement.getComponent(MindElement.getRoot(this.board, this.element)).rootG;
2645
2666
  this.drawShape();
2646
2667
  this.drawLink();
2647
- this.drawRichtext();
2648
- this.drawActiveG();
2649
- this.updateActiveClass();
2650
- this.drawMaskG();
2668
+ this.drawText();
2669
+ this.activeDrawer.draw(this.element, this.g, { selected: this.selected, isEditing: this.textManage.isEditing });
2651
2670
  this.drawEmojis();
2652
2671
  this.drawExtend();
2653
- this.drawQuickInsert();
2672
+ }
2673
+ editTopic() {
2674
+ this.activeDrawer.draw(this.element, this.g, { selected: this.selected, isEditing: true });
2675
+ this.textManage.edit(() => {
2676
+ this.activeDrawer.draw(this.element, this.g, { selected: this.selected, isEditing: false });
2677
+ });
2654
2678
  }
2655
2679
  onContextChanged(value, previous) {
2656
2680
  const newNode = MindElement.getNode(value.element);
2657
- // resolve move node richtext lose issue
2658
- if (this.node !== newNode) {
2659
- if (this.foreignObject && this.foreignObject.children.length <= 0) {
2660
- this.foreignObject?.appendChild(this.richtextComponentRef?.instance.editable);
2661
- }
2662
- }
2663
2681
  const isEqualNode = RectangleClient.isEqual(this.node, newNode);
2664
2682
  this.node = newNode;
2665
2683
  const isChangeTheme = this.board.operations.find(op => op.type === 'set_theme');
2666
2684
  if (!isEqualNode || value.element !== previous.element || isChangeTheme) {
2667
- this.drawActiveG();
2668
- this.updateActiveClass();
2685
+ this.activeDrawer.draw(this.element, this.g, { selected: this.selected, isEditing: this.textManage.isEditing });
2669
2686
  this.drawShape();
2670
2687
  this.drawLink();
2671
- this.updateRichtext();
2672
- this.drawMaskG();
2673
- this.drawExtend();
2674
- this.drawQuickInsert();
2675
2688
  this.drawEmojis();
2689
+ this.drawExtend();
2690
+ this.textManage.updateText(this.element.data.topic);
2691
+ this.textManage.updateRectangle();
2676
2692
  }
2677
2693
  else {
2678
2694
  const hasSameSelected = value.selected === previous.selected;
2679
2695
  const hasSameParent = value.parent === previous.parent;
2680
2696
  if (!hasSameSelected) {
2681
- this.drawActiveG();
2682
- this.updateActiveClass();
2697
+ this.activeDrawer.draw(this.element, this.g, { selected: this.selected, isEditing: this.textManage.isEditing });
2683
2698
  }
2684
2699
  if (!hasSameParent) {
2685
2700
  this.drawLink();
@@ -2687,21 +2702,11 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2687
2702
  }
2688
2703
  }
2689
2704
  drawEmojis() {
2690
- const g = this.emojisDrawer.drawEmojis(this.element);
2705
+ const g = this.nodeEmojisDrawer.drawEmojis(this.element);
2691
2706
  if (g) {
2692
2707
  this.g.append(g);
2693
2708
  }
2694
2709
  }
2695
- drawQuickInsert() {
2696
- this.quickInsertDrawer.destroy();
2697
- if (this.quickInsertDrawer.canDraw(this.element)) {
2698
- const g = this.quickInsertDrawer.draw(this.element);
2699
- if (hasAfterDraw(this.quickInsertDrawer)) {
2700
- this.quickInsertDrawer.afterDraw(this.element);
2701
- }
2702
- this.extendG?.appendChild(g);
2703
- }
2704
- }
2705
2710
  drawShape() {
2706
2711
  this.destroyShape();
2707
2712
  const shape = getShapeByElement(this.board, this.node.origin);
@@ -2743,389 +2748,33 @@ class MindNodeComponent extends PlaitPluginElementComponent {
2743
2748
  this.linkG.remove();
2744
2749
  }
2745
2750
  }
2746
- drawMaskG() {
2747
- this.destroyMaskG();
2748
- const lineWidthOffset = 2;
2749
- const extendOffset = 15;
2750
- const nodeLayout = MindQueries.getLayoutByElement(this.node.origin);
2751
- const isTop = isTopLayout(nodeLayout);
2752
- const isRight = isRightLayout(nodeLayout);
2753
- const isBottom = isBottomLayout(nodeLayout);
2754
- const isLeft = isLeftLayout(nodeLayout);
2755
- const { x, y, width, height } = getRectangleByNode(this.node);
2756
- let drawX = x;
2757
- let drawY = y;
2758
- let drawWidth = x + width;
2759
- let drawHeight = y + height;
2760
- switch (true) {
2761
- case isTop:
2762
- drawX = x - lineWidthOffset;
2763
- drawY = y - extendOffset;
2764
- drawWidth = x + width + lineWidthOffset;
2765
- drawHeight = y + height + lineWidthOffset;
2766
- break;
2767
- case isBottom:
2768
- drawX = x - lineWidthOffset;
2769
- drawY = y - lineWidthOffset;
2770
- drawWidth = x + width + lineWidthOffset;
2771
- drawHeight = y + height + extendOffset;
2772
- break;
2773
- case isLeft:
2774
- drawX = x - extendOffset;
2775
- drawY = y - lineWidthOffset;
2776
- drawWidth = x + width + lineWidthOffset;
2777
- drawHeight = y + height + lineWidthOffset;
2778
- break;
2779
- case isRight:
2780
- drawX = x - lineWidthOffset;
2781
- drawY = y - lineWidthOffset;
2782
- drawWidth = x + width + extendOffset;
2783
- drawHeight = y + height + lineWidthOffset;
2784
- break;
2785
- }
2786
- this.maskG = drawRoundRectangle(this.roughSVG, drawX, drawY, drawWidth, drawHeight, { stroke: 'none', fill: 'rgba(255,255,255,0)', fillStyle: 'solid' }, true);
2787
- this.maskG.classList.add('mask');
2788
- this.maskG.setAttribute('visibility', 'visible');
2789
- this.g.append(this.maskG);
2790
- if (this.isEditable) {
2791
- this.disabledMaskG();
2792
- }
2793
- fromEvent(this.maskG, 'mouseenter')
2794
- .pipe(takeUntil(this.destroy$), filter(() => {
2795
- return PlaitBoard.isFocus(this.board) && !this.element.isCollapsed && !this.handActive;
2796
- }))
2797
- .subscribe(() => {
2798
- this.g.classList.add('hovered');
2799
- });
2800
- fromEvent(this.maskG, 'mouseleave')
2801
- .pipe(takeUntil(this.destroy$), filter(() => {
2802
- return PlaitBoard.isFocus(this.board) && !this.element.isCollapsed;
2803
- }))
2804
- .subscribe(() => {
2805
- this.g.classList.remove('hovered');
2806
- });
2807
- }
2808
- destroyMaskG() {
2809
- if (this.maskG) {
2810
- this.maskG.remove();
2811
- this.g.classList.remove('hovered');
2812
- }
2813
- }
2814
- enableMaskG() {
2815
- if (this.maskG) {
2816
- this.maskG.setAttribute('visibility', 'visible');
2817
- }
2818
- }
2819
- disabledMaskG() {
2820
- if (this.maskG) {
2821
- this.maskG.setAttribute('visibility', 'hidden');
2822
- }
2823
- }
2824
- drawActiveG() {
2825
- this.destroyActiveG();
2826
- this.abstractIncludedOutlineG?.remove();
2827
- if (this.selected) {
2828
- if (AbstractNode.isAbstract(this.element)) {
2829
- this.updateAbstractIncludedOutline();
2830
- }
2831
- let { x, y, width, height } = getRectangleByNode(this.node);
2832
- const selectedStrokeG = drawRoundRectangle(this.roughSVG, x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, strokeWidth: 2, fill: '' }, true);
2833
- // 影响 mask 移入移出事件
2834
- selectedStrokeG.style.pointerEvents = 'none';
2835
- this.g.appendChild(selectedStrokeG);
2836
- this.activeG.push(selectedStrokeG);
2837
- if (this.richtextComponentRef?.instance.plaitReadonly === true) {
2838
- const selectedBackgroundG = drawRoundRectangle(this.roughSVG, x - 2, y - 2, x + width + 2, y + height + 2, { stroke: PRIMARY_COLOR, fill: PRIMARY_COLOR, fillStyle: 'solid' }, true);
2839
- selectedBackgroundG.style.opacity = '0.15';
2840
- // 影响双击事件
2841
- selectedBackgroundG.style.pointerEvents = 'none';
2842
- this.g.appendChild(selectedBackgroundG);
2843
- this.activeG.push(selectedBackgroundG, selectedStrokeG);
2844
- }
2845
- }
2846
- }
2847
- destroyActiveG() {
2848
- this.activeG.forEach(g => g.remove());
2849
- this.activeG = [];
2850
- }
2851
- updateActiveClass() {
2852
- if (!this.g) {
2853
- return;
2854
- }
2855
- if (this.selected) {
2856
- this.render2.addClass(this.g, 'active');
2857
- }
2858
- else {
2859
- this.render2.removeClass(this.g, 'active');
2860
- }
2861
- }
2862
- drawRichtext() {
2863
- const { richtextG, richtextComponentRef, foreignObject } = drawTopicByNode(this.board, this.node, this.viewContainerRef);
2864
- this.richtextComponentRef = richtextComponentRef;
2865
- this.richtextG = richtextG;
2866
- this.foreignObject = foreignObject;
2867
- this.render2.addClass(richtextG, 'richtext');
2868
- this.g.append(richtextG);
2869
- }
2870
2751
  drawExtend() {
2871
2752
  this.destroyExtend();
2872
- // create extend
2873
2753
  this.extendG = createG();
2874
- const collapseG = createG();
2875
2754
  this.extendG.classList.add('extend');
2876
- collapseG.classList.add('collapse-container');
2877
2755
  this.g.append(this.extendG);
2878
- this.extendG.append(collapseG);
2879
- if (this.node.origin.isRoot) {
2880
- return;
2881
- }
2882
- // interactive
2883
- fromEvent(collapseG, 'mouseup')
2884
- .pipe(filter(() => !this.handActive || !!PlaitBoard.isReadonly(this.board)), take(1))
2885
- .subscribe(() => {
2886
- const isCollapsed = !this.node.origin.isCollapsed;
2887
- const newElement = { isCollapsed };
2888
- const path = PlaitBoard.findPath(this.board, this.element);
2889
- Transforms.setNode(this.board, newElement, path);
2890
- });
2891
- const { x, y, width, height } = getRectangleByNode(this.node);
2892
- const stroke = getBranchColorByMindElement(this.board, this.element);
2893
- const branchWidth = getBranchWidthByMindElement(this.board, this.element);
2894
- const extendY = y + height / 2;
2895
- const nodeLayout = MindQueries.getCorrectLayoutByElement(this.board, this.element);
2896
- let extendLineXY = [
2897
- [x + width, extendY],
2898
- [x + width + EXTEND_OFFSET, extendY]
2899
- ];
2900
- let arrowYOffset = [-4, 1, -0.6, 4];
2901
- let arrowXOffset = [10, 5.5, 5.5, 10];
2902
- let extendLineXOffset = [0, 0];
2903
- let extendLineYOffset = [0, 0];
2904
- let circleOffset = [EXTEND_RADIUS / 2, 0];
2905
- if (isHorizontalLayout(nodeLayout) && !isIndentedLayout(nodeLayout)) {
2906
- extendLineYOffset =
2907
- getShapeByElement(this.board, this.node.origin) === MindElementShape.roundRectangle
2908
- ? [0, 0]
2909
- : [height / 2, height / 2];
2910
- if (isLeftLayout(nodeLayout)) {
2911
- //左
2912
- extendLineXOffset = [-width, -width - EXTEND_OFFSET * 2];
2913
- circleOffset = [-EXTEND_RADIUS / 2, 0];
2914
- arrowXOffset = [-10, -5.5, -5.5, -10];
2915
- }
2916
- }
2917
- else {
2918
- arrowXOffset = [-4, 0.6, -1, 4];
2919
- if (isTopLayout(nodeLayout)) {
2920
- //上
2921
- extendLineXOffset = [-width / 2, -width / 2 - EXTEND_OFFSET];
2922
- extendLineYOffset = [-height / 2, -height / 2 - EXTEND_OFFSET];
2923
- arrowYOffset = [-10, -5.5, -5.5, -10];
2924
- circleOffset = [0, -EXTEND_RADIUS / 2];
2925
- }
2926
- else {
2927
- //下
2928
- extendLineXOffset = [-width / 2, -width / 2 - EXTEND_OFFSET];
2929
- extendLineYOffset = [height / 2, height / 2 + EXTEND_OFFSET];
2930
- arrowYOffset = [10, 5.5, 5.5, 10];
2931
- circleOffset = [0, EXTEND_RADIUS / 2];
2932
- }
2933
- }
2934
- extendLineXY = [
2935
- [extendLineXY[0][0] + extendLineXOffset[0], extendLineXY[0][1] + extendLineYOffset[0]],
2936
- [extendLineXY[1][0] + extendLineXOffset[1], extendLineXY[1][1] + extendLineYOffset[1]]
2937
- ];
2938
- const extendLine = this.roughSVG.line(extendLineXY[0][0], extendLineXY[0][1], extendLineXY[1][0], extendLineXY[1][1], {
2939
- strokeWidth: branchWidth,
2940
- stroke
2941
- });
2942
- //绘制箭头
2943
- const hideArrowTopLine = this.roughSVG.line(extendLineXY[1][0] + arrowXOffset[0], extendLineXY[1][1] + arrowYOffset[0], extendLineXY[1][0] + arrowXOffset[1], extendLineXY[1][1] + arrowYOffset[1], {
2944
- stroke,
2945
- strokeWidth: 2
2946
- });
2947
- const hideArrowBottomLine = this.roughSVG.line(extendLineXY[1][0] + arrowXOffset[2], extendLineXY[1][1] + arrowYOffset[2], extendLineXY[1][0] + arrowXOffset[3], extendLineXY[1][1] + arrowYOffset[3], {
2948
- stroke,
2949
- strokeWidth: 2
2950
- });
2951
- if (this.node.origin.isCollapsed) {
2952
- const badge = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS, {
2953
- fill: stroke,
2954
- stroke,
2955
- fillStyle: 'solid'
2956
- });
2957
- let numberOffset = 0;
2958
- if (getChildrenCount(this.node.origin) >= 10)
2959
- numberOffset = -2;
2960
- if (getChildrenCount(this.node.origin) === 1)
2961
- numberOffset = 1;
2962
- const badgeText = createText(extendLineXY[1][0] + circleOffset[0] - 4 + numberOffset, extendLineXY[1][1] + circleOffset[1] + 4, stroke, `${getChildrenCount(this.node.origin)}`);
2756
+ if (this.element.isCollapsed) {
2963
2757
  this.g.classList.add('collapsed');
2964
- badge.setAttribute('style', 'opacity: 0.15');
2965
- badgeText.setAttribute('style', 'font-size: 12px');
2966
- collapseG.appendChild(badge);
2967
- collapseG.appendChild(badgeText);
2968
- collapseG.appendChild(extendLine);
2969
2758
  }
2970
2759
  else {
2971
2760
  this.g.classList.remove('collapsed');
2972
- if (this.node.origin.children.length > 0) {
2973
- const hideCircleG = this.roughSVG.circle(extendLineXY[1][0] + circleOffset[0], extendLineXY[1][1] + circleOffset[1], EXTEND_RADIUS - 1, {
2974
- fill: '#fff',
2975
- stroke,
2976
- strokeWidth: branchWidth > 3 ? 3 : branchWidth,
2977
- fillStyle: 'solid'
2978
- });
2979
- collapseG.appendChild(hideCircleG);
2980
- collapseG.appendChild(hideArrowTopLine);
2981
- collapseG.appendChild(hideArrowBottomLine);
2982
- }
2983
2761
  }
2762
+ this.nodeInsertDrawer.draw(this.element, this.extendG);
2763
+ this.collapseDrawer.draw(this.element, this.extendG);
2984
2764
  }
2985
2765
  destroyExtend() {
2986
2766
  if (this.extendG) {
2987
2767
  this.extendG.remove();
2988
2768
  }
2989
2769
  }
2990
- destroyRichtext() {
2991
- if (this.richtextG) {
2992
- this.richtextG.remove();
2993
- }
2994
- if (this.richtextComponentRef) {
2995
- this.richtextComponentRef.destroy();
2996
- }
2997
- }
2998
- updateAbstractIncludedOutline(activeHandlePosition, resizingLocation) {
2999
- this.abstractIncludedOutlineG?.remove();
3000
- this.abstractIncludedOutlineG = drawAbstractIncludedOutline(this.board, this.roughSVG, this.element, activeHandlePosition, resizingLocation);
3001
- PlaitBoard.getHost(this.board).append(this.abstractIncludedOutlineG);
3002
- }
3003
- updateRichtext() {
3004
- updateRichText(this.node.origin.data.topic, this.richtextComponentRef);
3005
- updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
3006
- }
3007
- startEditText(isEnd, isClear) {
3008
- if (!this.richtextComponentRef) {
3009
- throw new Error('undefined richtextComponentRef');
3010
- }
3011
- const richtextInstance = this.richtextComponentRef.instance;
3012
- this.isEditable = true;
3013
- IS_TEXT_EDITABLE.set(this.board, true);
3014
- this.disabledMaskG();
3015
- updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
3016
- if (richtextInstance.plaitReadonly) {
3017
- richtextInstance.plaitReadonly = false;
3018
- this.richtextComponentRef.changeDetectorRef.detectChanges();
3019
- this.drawActiveG();
3020
- const location = isEnd ? Editor.end(richtextInstance.editor, [0]) : [0];
3021
- setFullSelectionAndFocus(richtextInstance.editor, location);
3022
- if (isClear) {
3023
- Editor.deleteBackward(richtextInstance.editor);
3024
- }
3025
- // handle invalid width and height (old data)
3026
- let { width, height } = getRichtextContentSize(richtextInstance.editable);
3027
- if (width !== this.element.width || height !== this.element.height) {
3028
- MindTransforms.setTopicSize(this.board, this.element, width, height);
3029
- }
3030
- }
3031
- let richtext = richtextInstance.plaitValue;
3032
- // use debounceTime to wait DOM render complete
3033
- const valueChange$ = richtextInstance.plaitChange
3034
- .pipe(debounceTime(0), filter(event => {
3035
- // 过滤掉 operations 中全是 set_selection 的操作
3036
- return !event.operations.every(op => Operation.isSelectionOperation(op));
3037
- }))
3038
- .subscribe(event => {
3039
- if (richtext === event.value) {
3040
- return;
3041
- }
3042
- this.updateRichtext();
3043
- // 更新富文本、更新宽高
3044
- let { width, height } = getRichtextContentSize(richtextInstance.editable);
3045
- MindTransforms.setTopic(this.board, this.element, event.value, width, height);
3046
- MERGING.set(this.board, true);
3047
- });
3048
- const composition$ = richtextInstance.plaitComposition.pipe(debounceTime(0)).subscribe(event => {
3049
- let { width, height } = getRichtextContentSize(richtextInstance.editable);
3050
- if (width < NODE_MIN_WIDTH) {
3051
- width = NODE_MIN_WIDTH;
3052
- }
3053
- if (event.isComposing && (width !== this.node.origin.width || height !== this.node.origin.height)) {
3054
- const newElement = {
3055
- width: width / this.board.viewport.zoom,
3056
- height: height / this.board.viewport.zoom
3057
- };
3058
- const path = PlaitBoard.findPath(this.board, this.element);
3059
- Transforms.setNode(this.board, newElement, path);
3060
- MERGING.set(this.board, true);
3061
- }
3062
- });
3063
- const mousedown$ = fromEvent(document, 'mousedown').subscribe((event) => {
3064
- const point = transformPoint(this.board, toPoint(event.x, event.y, PlaitBoard.getHost(this.board)));
3065
- const clickInNode = isHitMindElement(this.board, point, this.element);
3066
- if (clickInNode && !hasEditableTarget(richtextInstance.editor, event.target)) {
3067
- event.preventDefault();
3068
- }
3069
- else if (!clickInNode) {
3070
- // handle composition input state, like: Chinese IME Composition Input
3071
- timer(0).subscribe(() => {
3072
- exitHandle();
3073
- this.enableMaskG();
3074
- });
3075
- }
3076
- });
3077
- const editor = richtextInstance.editor;
3078
- const { keydown } = editor;
3079
- editor.keydown = (event) => {
3080
- if (event.isComposing) {
3081
- return;
3082
- }
3083
- if (event.key === 'Escape') {
3084
- event.preventDefault();
3085
- event.stopPropagation();
3086
- exitHandle();
3087
- this.drawActiveG();
3088
- this.enableMaskG();
3089
- return;
3090
- }
3091
- if (event.key === 'Enter' && !event.shiftKey) {
3092
- event.preventDefault();
3093
- event.stopPropagation();
3094
- exitHandle();
3095
- this.drawActiveG();
3096
- this.enableMaskG();
3097
- return;
3098
- }
3099
- if (event.key === 'Tab') {
3100
- event.preventDefault();
3101
- event.stopPropagation();
3102
- exitHandle();
3103
- this.drawActiveG();
3104
- this.drawMaskG();
3105
- }
3106
- };
3107
- const exitHandle = () => {
3108
- this.ngZone.run(() => {
3109
- // unsubscribe
3110
- valueChange$.unsubscribe();
3111
- composition$.unsubscribe();
3112
- mousedown$.unsubscribe();
3113
- editor.keydown = keydown; // reset keydown
3114
- // editable status
3115
- MERGING.set(this.board, false);
3116
- richtextInstance.plaitReadonly = true;
3117
- this.richtextComponentRef?.changeDetectorRef.markForCheck();
3118
- this.isEditable = false;
3119
- updateMindNodeTopicSize(this.board, this.node, this.richtextG, this.isEditable);
3120
- IS_TEXT_EDITABLE.set(this.board, false);
3121
- });
3122
- };
2770
+ drawText() {
2771
+ this.textManage.draw(this.element.data.topic);
2772
+ this.g.append(this.textManage.g);
3123
2773
  }
3124
2774
  ngOnDestroy() {
3125
2775
  super.ngOnDestroy();
3126
- this.abstractIncludedOutlineG?.remove();
3127
- this.destroyRichtext();
3128
- this.emojisDrawer.destroy();
2776
+ this.textManage.destroy();
2777
+ this.nodeEmojisDrawer.destroy();
3129
2778
  this.destroy$.next();
3130
2779
  this.destroy$.complete();
3131
2780
  if (ELEMENT_TO_NODE.get(this.element) === this.node) {
@@ -3133,7 +2782,7 @@ class MindNodeComponent extends PlaitPluginElementComponent {
3133
2782
  }
3134
2783
  }
3135
2784
  }
3136
- MindNodeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindNodeComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }, { token: i0.Renderer2 }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
2785
+ MindNodeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindNodeComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
3137
2786
  MindNodeComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.5", type: MindNodeComponent, selector: "plait-mind-node", usesInheritance: true, ngImport: i0, template: `
3138
2787
  <plait-children
3139
2788
  *ngIf="!element.isCollapsed"
@@ -3158,7 +2807,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3158
2807
  `,
3159
2808
  changeDetection: ChangeDetectionStrategy.OnPush
3160
2809
  }]
3161
- }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }, { type: i0.Renderer2 }, { type: i0.NgZone }]; } });
2810
+ }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
3162
2811
 
3163
2812
  const getLayoutOptions = (board) => {
3164
2813
  function getMainAxle(element, parent) {
@@ -3231,6 +2880,7 @@ class PlaitMindComponent extends MindNodeComponent {
3231
2880
  ngOnInit() {
3232
2881
  this.updateMindLayout();
3233
2882
  super.ngOnInit();
2883
+ this.g.classList.add('root');
3234
2884
  }
3235
2885
  beforeContextChange(value) {
3236
2886
  if (value.element !== this.element && this.initialized) {
@@ -3271,19 +2921,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
3271
2921
  class MindModule {
3272
2922
  }
3273
2923
  MindModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
3274
- MindModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.5", ngImport: i0, type: MindModule, declarations: [PlaitMindComponent, MindNodeComponent], imports: [CommonModule, RichtextModule, PlaitModule], exports: [PlaitMindComponent, MindNodeComponent] });
3275
- MindModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, imports: [CommonModule, RichtextModule, PlaitModule] });
2924
+ MindModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.2.5", ngImport: i0, type: MindModule, declarations: [PlaitMindComponent, MindNodeComponent], imports: [CommonModule, TextModule, PlaitModule], exports: [PlaitMindComponent, MindNodeComponent] });
2925
+ MindModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, imports: [CommonModule, TextModule, PlaitModule] });
3276
2926
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImport: i0, type: MindModule, decorators: [{
3277
2927
  type: NgModule,
3278
2928
  args: [{
3279
2929
  declarations: [PlaitMindComponent, MindNodeComponent],
3280
- imports: [CommonModule, RichtextModule, PlaitModule],
2930
+ imports: [CommonModule, TextModule, PlaitModule],
3281
2931
  exports: [PlaitMindComponent, MindNodeComponent]
3282
2932
  }]
3283
2933
  }] });
3284
2934
 
3285
2935
  const DRAG_MOVE_BUFFER = 5;
3286
- const withDnd = (board) => {
2936
+ const withNodeDnd = (board) => {
3287
2937
  const { mousedown, mousemove, globalMouseup } = board;
3288
2938
  let activeElements = [];
3289
2939
  let correspondingElements = [];
@@ -3293,7 +2943,10 @@ const withDnd = (board) => {
3293
2943
  let dropTarget = null;
3294
2944
  let targetPath;
3295
2945
  board.mousedown = (event) => {
3296
- if (board.options.readonly || IS_TEXT_EDITABLE.get(board) || event.button === 2) {
2946
+ if (PlaitBoard.isReadonly(board) ||
2947
+ PlaitBoard.hasBeenTextEditing(board) ||
2948
+ !PlaitBoard.isPointer(board, PlaitPointerType.selection) ||
2949
+ event.button === 2) {
3297
2950
  mousedown(event);
3298
2951
  return;
3299
2952
  }
@@ -3634,7 +3287,7 @@ const withAbstract = (board) => {
3634
3287
  newProperty =
3635
3288
  abstractHandlePosition === AbstractHandlePosition.start ? { start: locationIndex + 1 } : { end: locationIndex };
3636
3289
  }
3637
- abstractComponent.updateAbstractIncludedOutline(abstractHandlePosition, location);
3290
+ abstractComponent.activeDrawer.updateAbstractOutline(activeAbstractElement, abstractHandlePosition, location);
3638
3291
  }
3639
3292
  mousemove(event);
3640
3293
  };
@@ -3651,7 +3304,7 @@ const withAbstract = (board) => {
3651
3304
  }
3652
3305
  else {
3653
3306
  const abstractComponent = PlaitElement.getComponent(activeAbstractElement);
3654
- abstractComponent.updateAbstractIncludedOutline();
3307
+ abstractComponent.activeDrawer.updateAbstractOutline(activeAbstractElement);
3655
3308
  }
3656
3309
  activeAbstractElement = undefined;
3657
3310
  }
@@ -3703,22 +3356,29 @@ const withCreateMind = (board) => {
3703
3356
  const nodeG = drawRoundRectangleByElement(board, nodeRectangle, emptyMind);
3704
3357
  const topicRectangle = getTopicRectangleByElement(newBoard, nodeRectangle, emptyMind);
3705
3358
  if (!fakeCreateNodeRef) {
3706
- const { richtextComponentRef, richtextG, foreignObject } = drawTopicByElement(newBoard, topicRectangle, emptyMind);
3359
+ const textManage = new TextManage(board, PlaitBoard.getComponent(board).viewContainerRef, () => {
3360
+ return topicRectangle;
3361
+ });
3362
+ PlaitBoard.getComponent(board)
3363
+ .viewContainerRef.injector.get(NgZone)
3364
+ .run(() => {
3365
+ textManage.draw(emptyMind.data.topic);
3366
+ });
3707
3367
  fakeCreateNodeRef = {
3708
- instanceRef: richtextComponentRef,
3368
+ g: createG(),
3709
3369
  nodeG,
3710
- foreignObject,
3711
- topicG: richtextG
3370
+ textManage
3712
3371
  };
3713
- richtextComponentRef.changeDetectorRef.detectChanges();
3714
- PlaitBoard.getHost(board).append(...[fakeCreateNodeRef.nodeG, fakeCreateNodeRef.topicG]);
3372
+ fakeCreateNodeRef.g.classList.add('root');
3373
+ PlaitBoard.getHost(board).append(fakeCreateNodeRef.g);
3374
+ fakeCreateNodeRef.g.append(...[fakeCreateNodeRef.nodeG, textManage.g]);
3715
3375
  }
3716
3376
  else {
3377
+ fakeCreateNodeRef.textManage.updateRectangle(topicRectangle);
3717
3378
  fakeCreateNodeRef.nodeG.remove();
3718
3379
  fakeCreateNodeRef.nodeG = nodeG;
3719
- PlaitBoard.getHost(board).append(nodeG);
3720
- PlaitBoard.getHost(board).append(fakeCreateNodeRef.topicG);
3721
- updateForeignObject(fakeCreateNodeRef.topicG, topicRectangle.width, topicRectangle.height, topicRectangle.x, topicRectangle.y);
3380
+ fakeCreateNodeRef.g.append(nodeG);
3381
+ fakeCreateNodeRef.g.append(fakeCreateNodeRef.textManage.g);
3722
3382
  }
3723
3383
  }
3724
3384
  });
@@ -3734,6 +3394,7 @@ const withCreateMind = (board) => {
3734
3394
  const targetPoint = transformPoint(board, toPoint(movingPoint[0], movingPoint[1], PlaitBoard.getHost(board)));
3735
3395
  const emptyMind = createEmptyMind(board, targetPoint);
3736
3396
  Transforms.insertNode(board, emptyMind, [board.children.length]);
3397
+ clearSelectedElement(board);
3737
3398
  addSelectedElement(board, emptyMind);
3738
3399
  BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
3739
3400
  }
@@ -3754,9 +3415,8 @@ const withCreateMind = (board) => {
3754
3415
  };
3755
3416
  function destroy() {
3756
3417
  if (fakeCreateNodeRef) {
3757
- fakeCreateNodeRef.instanceRef.destroy();
3758
- fakeCreateNodeRef.nodeG.remove();
3759
- fakeCreateNodeRef.topicG.remove();
3418
+ fakeCreateNodeRef.textManage.destroy();
3419
+ fakeCreateNodeRef.g.remove();
3760
3420
  fakeCreateNodeRef = null;
3761
3421
  }
3762
3422
  }
@@ -3788,6 +3448,68 @@ const isExpandHotkey = (keyboardEvent) => {
3788
3448
  return isKeyHotkey('mod+/', keyboardEvent);
3789
3449
  };
3790
3450
 
3451
+ const withNodeHover = (board) => {
3452
+ const { mousemove, mouseleave } = board;
3453
+ let hoveredMindElement = null;
3454
+ board.mousemove = (event) => {
3455
+ throttleRAF(() => {
3456
+ let target = null;
3457
+ const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
3458
+ depthFirstRecursion(board, element => {
3459
+ if (target) {
3460
+ return;
3461
+ }
3462
+ if (!MindElement.isMindElement(board, element)) {
3463
+ return;
3464
+ }
3465
+ const isHitElement = isHitMindElement(board, point, element);
3466
+ if (isHitElement) {
3467
+ target = element;
3468
+ }
3469
+ }, node => {
3470
+ if (PlaitBoard.isBoard(node) || board.isRecursion(node)) {
3471
+ return true;
3472
+ }
3473
+ else {
3474
+ return false;
3475
+ }
3476
+ });
3477
+ if (hoveredMindElement && target && hoveredMindElement === target) {
3478
+ return;
3479
+ }
3480
+ if (hoveredMindElement) {
3481
+ removeHovered(hoveredMindElement);
3482
+ }
3483
+ if (target) {
3484
+ addHovered(target);
3485
+ hoveredMindElement = target;
3486
+ }
3487
+ else {
3488
+ hoveredMindElement = null;
3489
+ }
3490
+ });
3491
+ mousemove(event);
3492
+ };
3493
+ board.mouseleave = (event) => {
3494
+ if (hoveredMindElement) {
3495
+ removeHovered(hoveredMindElement);
3496
+ hoveredMindElement = null;
3497
+ }
3498
+ mouseleave(event);
3499
+ };
3500
+ return board;
3501
+ };
3502
+ const addHovered = (element) => {
3503
+ const component = PlaitElement.getComponent(element);
3504
+ component.g.classList.add('hovered');
3505
+ };
3506
+ const removeHovered = (element) => {
3507
+ const component = PlaitElement.getComponent(element);
3508
+ if (component && component.g) {
3509
+ component.g.classList.remove('hovered');
3510
+ }
3511
+ };
3512
+
3791
3513
  const withMind = (board) => {
3792
3514
  const { drawElement, dblclick, keydown, insertFragment, setFragment, deleteFragment, isHitSelection, getRectangle, isMovable, isRecursion } = board;
3793
3515
  board.drawElement = (context) => {
@@ -3914,7 +3636,7 @@ const withMind = (board) => {
3914
3636
  if (!isVirtualKey(event)) {
3915
3637
  event.preventDefault();
3916
3638
  const selectedElement = selectedElements[0];
3917
- enterNodeEditing(selectedElement);
3639
+ editTopic(selectedElement);
3918
3640
  return;
3919
3641
  }
3920
3642
  }
@@ -3936,7 +3658,7 @@ const withMind = (board) => {
3936
3658
  .forEach(mindMap => {
3937
3659
  depthFirstRecursion(mindMap, node => {
3938
3660
  if (!PlaitBoard.hasBeenTextEditing(board) && isHitMindElement(board, point, node)) {
3939
- enterNodeEditing(node);
3661
+ editTopic(node);
3940
3662
  }
3941
3663
  }, node => {
3942
3664
  if (PlaitBoard.isBoard(node) || board.isRecursion(node)) {
@@ -3972,7 +3694,7 @@ const withMind = (board) => {
3972
3694
  }
3973
3695
  else {
3974
3696
  const text = data?.getData(`text/plain`);
3975
- const { width, height } = getSizeByText(text, PlaitBoard.getHost(board).parentElement, TOPIC_DEFAULT_MAX_WORD_COUNT);
3697
+ const { width, height } = getTextSize(board, text, TOPIC_DEFAULT_MAX_WORD_COUNT);
3976
3698
  const selectedElements = getSelectedElements(board);
3977
3699
  if (text && selectedElements.length === 1) {
3978
3700
  insertClipboardText(board, selectedElements[0], text, width, height);
@@ -3990,7 +3712,7 @@ const withMind = (board) => {
3990
3712
  MindTransforms.removeElements(board, selectedElements);
3991
3713
  deleteFragment(data);
3992
3714
  };
3993
- return withMindHotkey(withMindExtend(withCreateMind(withAbstract(withDnd(board)))));
3715
+ return withNodeHover(withMindHotkey(withMindExtend(withCreateMind(withAbstract(withNodeDnd(board))))));
3994
3716
  };
3995
3717
 
3996
3718
  class MindEmojiBaseComponent {
@@ -4044,5 +3766,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.5", ngImpor
4044
3766
  * Generated bundle index. Do not edit.
4045
3767
  */
4046
3768
 
4047
- export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, BRANCH_WIDTH, BaseDrawer, BranchShape, DefaultAbstractNodeStyle, DefaultNodeStyle, ELEMENT_TO_NODE, EXTEND_OFFSET, EXTEND_RADIUS, GRAY_COLOR, INHERIT_ATTRIBUTE_KEYS, IS_DRAGGING, LayoutDirection, LayoutDirectionsMap, MindColorfulThemeColor, MindDarkThemeColor, MindDefaultThemeColor, MindElement, MindElementShape, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindPointerType, MindQueries, MindRetroThemeColor, MindSoftThemeColor, MindStarryThemeColor, MindThemeColor, MindThemeColors, 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, addActiveOnDragOrigin, adjustAbstractToNode, adjustNodeToRoot, adjustRootToNode, canSetAbstract, copyNewNode, correctLayoutByDirection, createDefaultMind, createEmptyMind, createMindElement, deleteElementHandleAbstract, deleteElementsHandleRightNodeCount, detectDropTarget, directionCorrector, directionDetector, divideElementByParent, drawFakeDragNode, drawFakeDropNode, enterNodeEditing, extractNodesText, findLastChild, findLocationLeftIndex, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchShapeByMindElement, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultBranchColor, getDefaultBranchColorByIndex, getDefaultLayout, getEmojiForeignRectangle, getEmojiRectangle, getFirstLevelElement, getHitAbstractHandle, getInCorrectLayoutDirection, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getMindThemeColor, getNextBranchColor, getOverallAbstracts, getPathByDropTarget, getRectangleByElement, getRectangleByNode, getRectangleByResizingLocation, getRelativeStartEndByAbstractRef, getRootLayout, getShapeByElement, getStrokeByMindElement, getTopicRectangleByElement, getTopicRectangleByNode, getValidAbstractRefs, handleTouchedAbstract, hasAfterDraw, hasPreviousOrNextOfDropPath, insertElementHandleAbstract, insertElementHandleRightNodeCount, insertMindElement, isChildElement, isChildRight, isChildUp, isCorrectLayout, isDragging, isDropStandardRight, isHitEmojis, isHitMindElement, isInRightBranchOfStandardLayout, isMixedLayout, isSetAbstract, isValidTarget, isVirtualKey, removeActiveOnDragOrigin, separateChildren, setIsDragging, withMind, withMindExtend };
3769
+ export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, AbstractHandlePosition, AbstractResizeState, BASE, BRANCH_WIDTH, BaseDrawer, BranchShape, DefaultAbstractNodeStyle, DefaultNodeStyle, ELEMENT_TO_NODE, EXTEND_DIAMETER, EXTEND_OFFSET, GRAY_COLOR, INHERIT_ATTRIBUTE_KEYS, IS_DRAGGING, LayoutDirection, LayoutDirectionsMap, MindColorfulThemeColor, MindDarkThemeColor, MindDefaultThemeColor, MindElement, MindElementShape, MindEmojiBaseComponent, MindModule, MindNode, MindNodeComponent, MindPointerType, MindQueries, MindRetroThemeColor, MindSoftThemeColor, MindStarryThemeColor, MindThemeColor, MindThemeColors, 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, ROOT_TOPIC_HEIGHT, STROKE_WIDTH, TOPIC_COLOR, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, TRANSPARENT, addActiveOnDragOrigin, adjustAbstractToNode, adjustNodeToRoot, adjustRootToNode, canSetAbstract, copyNewNode, correctLayoutByDirection, createDefaultMind, createEmptyMind, createMindElement, deleteElementHandleAbstract, deleteElementsHandleRightNodeCount, detectDropTarget, directionCorrector, directionDetector, divideElementByParent, drawFakeDragNode, drawFakeDropNode, editTopic, extractNodesText, findLastChild, findLocationLeftIndex, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchShapeByMindElement, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultBranchColor, getDefaultBranchColorByIndex, getDefaultLayout, getEmojiForeignRectangle, getEmojiRectangle, getFirstLevelElement, getHitAbstractHandle, getInCorrectLayoutDirection, getLayoutDirection$1 as getLayoutDirection, getLayoutReverseDirection, getLocationScope, getMindThemeColor, getNextBranchColor, getOverallAbstracts, getPathByDropTarget, getRectangleByElement, getRectangleByNode, getRectangleByResizingLocation, getRelativeStartEndByAbstractRef, getRootLayout, getShapeByElement, getStrokeByMindElement, getTopicRectangleByElement, getTopicRectangleByNode, getValidAbstractRefs, handleTouchedAbstract, hasAfterDraw, hasPreviousOrNextOfDropPath, insertElementHandleAbstract, insertElementHandleRightNodeCount, insertMindElement, isChildElement, isChildRight, isChildUp, isCorrectLayout, isDragging, isDropStandardRight, isHitEmojis, isHitMindElement, isInRightBranchOfStandardLayout, isMixedLayout, isSetAbstract, isValidTarget, isVirtualKey, removeActiveOnDragOrigin, separateChildren, setIsDragging, withMind, withMindExtend };
4048
3770
  //# sourceMappingURL=plait-mind.mjs.map