@plait/mind 0.82.0-next.0 → 0.83.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.
@@ -1,11 +1,9 @@
1
- import { DEFAULT_COLOR, DefaultThemeColor, ColorfulThemeColor, SoftThemeColor, RetroThemeColor, DarkThemeColor, StarryThemeColor, PlaitElement, PlaitNode, Path, isNullOrUndefined, PlaitBoard, getSelectedElements, getI18nValue, idCreator, Transforms, clearSelectedElement, addSelectedElement, distanceBetweenPointAndRectangle, RectangleClient, setDragging, depthFirstRecursion, getIsRecursionFunc, drawRoundRectangle, drawLinearPath, drawBezierPath, setStrokeLinecap, createG, createForeignObject, updateForeignObject, getRectangleByElements, toActiveRectangleFromViewBoxRectangle, ACTIVE_STROKE_WIDTH, SELECTION_RECTANGLE_CLASS_NAME, NODE_TO_PARENT, removeSelectedElement, PlaitHistoryBoard, createText, PlaitPointerType, NODE_TO_INDEX, isMainPointer, toViewBoxPoint, toHostPoint, getHitElementByPoint, distanceBetweenPointAndPoint, isDragging, CoreTransforms, toActivePointFromViewBoxPoint, BoardTransforms, throttleRAF, isContextmenu, temporaryDisableSelection, hotkeys, createClipboardContext, WritableClipboardType, Point, ResizeCursorClass, WritableClipboardOperationType, addOrCreateClipboardContext } from '@plait/core';
1
+ import { DEFAULT_COLOR, DefaultThemeColor, ColorfulThemeColor, SoftThemeColor, RetroThemeColor, DarkThemeColor, StarryThemeColor, rgbaToHEX, PlaitElement, PlaitNode, Path, isNullOrUndefined, PlaitBoard, getSelectedElements, getI18nValue, idCreator, Transforms, clearSelectedElement, addSelectedElement, distanceBetweenPointAndRectangle, RectangleClient, setDragging, depthFirstRecursion, getIsRecursionFunc, drawRoundRectangle, drawLinearPath, drawBezierPath, setStrokeLinecap, createG, createForeignObject, updateForeignObject, getRectangleByElements, toActiveRectangleFromViewBoxRectangle, ACTIVE_STROKE_WIDTH, SELECTION_RECTANGLE_CLASS_NAME, NODE_TO_PARENT, removeSelectedElement, PlaitHistoryBoard, isSelectedElement, createText, isSelectionMoving, isDragging, isMovingElements, NODE_TO_INDEX, PlaitPointerType, isMainPointer, toViewBoxPoint, toHostPoint, getHitElementByPoint, distanceBetweenPointAndPoint, CoreTransforms, toActivePointFromViewBoxPoint, BoardTransforms, throttleRAF, isContextmenu, temporaryDisableSelection, hotkeys, createClipboardContext, WritableClipboardType, Point, ResizeCursorClass, WritableClipboardOperationType, addOrCreateClipboardContext } from '@plait/core';
2
2
  import { MindLayoutType, AbstractNode, isIndentedLayout, isHorizontalLogicLayout, ConnectingPosition, isHorizontalLayout, getNonAbstractChildren, isStandardLayout, isLeftLayout, isRightLayout, isVerticalLogicLayout, isTopLayout, isBottomLayout, getCorrectStartEnd, getAbstractLayout, GlobalLayout } from '@plait/layouts';
3
- import { StrokeStyle, getFirstTextManage, DEFAULT_FONT_FAMILY, measureElement, buildText, getFirstTextEditor, RESIZE_HANDLE_DIAMETER, getRectangleResizeHandleRefs, addElementOfFocusedImage, ImageGenerator, removeElementOfFocusedImage, getStrokeLineDash, Generator, PropertyTransforms, CommonElementFlavour, WithTextPluginKey, TextManage, isDrawingMode, isDndMode, setCreationMode, BoardCreationMode, isExpandHotkey, isTabHotkey, isEnterHotkey, isVirtualKey, isDelete, isSpaceHotkey, getElementOfFocusedImage, acceptImageTypes, buildImage, withResize, getElementsText } from '@plait/common';
4
- import { TEXT_DEFAULT_HEIGHT, PlaitMarkEditor, MarkTypes, DEFAULT_FONT_SIZE } from '@plait/text-plugins';
3
+ import { StrokeStyle, getFirstTextManage, DEFAULT_FONT_FAMILY, measureElement, buildText, getFirstTextEditor, RESIZE_HANDLE_DIAMETER, getRectangleResizeHandleRefs, addElementOfFocusedImage, ImageGenerator, removeElementOfFocusedImage, getStrokeLineDash, getXDistanceBetweenPoint, moveXOfPoint, moveYOfPoint, Generator, PropertyTransforms, TRANSPARENT, isResizing, CommonElementFlavour, WithTextPluginKey, TextManage, isDrawingMode, isDndMode, setCreationMode, BoardCreationMode, isExpandHotkey, isTabHotkey, isEnterHotkey, isVirtualKey, isDelete, isSpaceHotkey, getElementOfFocusedImage, acceptImageTypes, buildImage, withResize, getElementsText } from '@plait/common';
4
+ import { TEXT_DEFAULT_HEIGHT, PlaitMarkEditor, MarkTypes, DEFAULT_FONT_SIZE, FontSizes } from '@plait/text-plugins';
5
5
  import { Node as Node$1, Path as Path$1 } from 'slate';
6
6
  import { pointsOnBezierCurves } from 'points-on-curve';
7
- import { fromEvent } from 'rxjs';
8
- import { take, filter } from 'rxjs/operators';
9
7
  import { isHotkey } from 'is-hotkey';
10
8
 
11
9
  const ELEMENT_TO_NODE = new WeakMap();
@@ -180,11 +178,14 @@ const BASE = 4;
180
178
  const PRIMARY_COLOR = '#6698FF';
181
179
  const GRAY_COLOR = '#AAAAAA';
182
180
  const STROKE_WIDTH = 3;
183
- const EXTEND_OFFSET = 8;
184
- const EXTEND_DIAMETER = 16;
185
- const QUICK_INSERT_CIRCLE_OFFSET = 9;
186
- const QUICK_INSERT_CIRCLE_COLOR = '#6698FF';
187
- const QUICK_INSERT_INNER_CROSS_COLOR = 'white';
181
+ const RESIZE_HANDLE_BUFFER_DISTANCE = 8;
182
+ const NODE_MORE_LINE_DISTANCE = 10;
183
+ const NODE_MORE_STROKE_WIDTH = 2;
184
+ const NODE_MORE_ICON_DIAMETER = 20;
185
+ const NODE_MORE_BRIDGE_DISTANCE = 10;
186
+ const NODE_ADD_CIRCLE_COLOR = rgbaToHEX('#000000', 0.2);
187
+ const NODE_ADD_HOVER_COLOR = '#6698FF';
188
+ const NODE_ADD_INNER_CROSS_COLOR = 'white';
188
189
  const DEFAULT_MIND_IMAGE_WIDTH = 240;
189
190
  var MindI18nKey;
190
191
  (function (MindI18nKey) {
@@ -611,9 +612,9 @@ const getLayoutOptions = (board) => {
611
612
  }
612
613
  function getSecondAxle(element, parent) {
613
614
  if (element.isRoot) {
614
- return BASE * 10;
615
+ return BASE * 12;
615
616
  }
616
- return BASE * 6;
617
+ return BASE * 8.5;
617
618
  }
618
619
  return {
619
620
  getHeight(element) {
@@ -626,7 +627,7 @@ const getLayoutOptions = (board) => {
626
627
  const _layout = (parent && parent.layout) || getRootLayout(element);
627
628
  const isHorizontal = isHorizontalLayout(_layout);
628
629
  if (isIndentedLayout(_layout)) {
629
- return BASE * 4;
630
+ return BASE * 6;
630
631
  }
631
632
  if (!isHorizontal) {
632
633
  return getMainAxle(element, parent);
@@ -638,7 +639,7 @@ const getLayoutOptions = (board) => {
638
639
  getVerticalGap(element, parent) {
639
640
  const _layout = (parent && parent.layout) || getRootLayout(element);
640
641
  if (isIndentedLayout(_layout)) {
641
- return BASE;
642
+ return BASE * 3.5;
642
643
  }
643
644
  const isHorizontal = isHorizontalLayout(_layout);
644
645
  if (isHorizontal) {
@@ -655,10 +656,10 @@ const getLayoutOptions = (board) => {
655
656
  return undefined;
656
657
  },
657
658
  getExtendHeight(node) {
658
- return BASE * 6;
659
+ return 0;
659
660
  },
660
661
  getIndentedCrossLevelGap() {
661
- return BASE * 2;
662
+ return BASE * 1;
662
663
  }
663
664
  };
664
665
  };
@@ -683,7 +684,7 @@ const getDefaultMindNameText = (board) => {
683
684
  const getAbstractNodeText = (board) => {
684
685
  return getI18nValue(board, MindI18nKey.abstractNodeText, ABSTRACT_NODE_TEXT);
685
686
  };
686
- const getTopicSize = (isRoot, isBranch, topic, manualWidth) => {
687
+ const getTopicSize = (board, isRoot, isBranch, topic, manualWidth) => {
687
688
  let fontFamily = DEFAULT_FONT_FAMILY;
688
689
  let fontSize = TOPIC_FONT_SIZE;
689
690
  if (isRoot) {
@@ -694,12 +695,12 @@ const getTopicSize = (isRoot, isBranch, topic, manualWidth) => {
694
695
  fontFamily = DEFAULT_FONT_FAMILY;
695
696
  }
696
697
  const maxWidth = fontSize * TOPIC_DEFAULT_MAX_WORD_COUNT;
697
- return measureElement(topic, { fontSize, fontFamily }, manualWidth ? manualWidth : maxWidth);
698
+ return measureElement(board, topic, { fontSize, fontFamily }, manualWidth ? manualWidth : maxWidth);
698
699
  };
699
700
 
700
701
  const createEmptyMind = (board, point) => {
701
702
  const text = getDefaultMindNameText(board);
702
- const topicSize = getTopicSize(true, false, buildText(text));
703
+ const topicSize = getTopicSize(board, true, false, buildText(text));
703
704
  const element = createMindElement(text, topicSize.width, topicSize.height, { layout: MindLayoutType.right });
704
705
  element.isRoot = true;
705
706
  element.type = 'mindmap';
@@ -1394,14 +1395,6 @@ const getPointByPlacement = (client, placement) => {
1394
1395
  }
1395
1396
  return [x, y];
1396
1397
  };
1397
- const getXDistanceBetweenPoint = (point1, point2, isHorizontalLayout) => {
1398
- if (isHorizontalLayout) {
1399
- return Math.abs(point1[0] - point2[0]);
1400
- }
1401
- else {
1402
- return Math.abs(point1[1] - point2[1]);
1403
- }
1404
- };
1405
1398
  const getYDistanceBetweenPoint = (point1, point2, isHorizontalLayout) => {
1406
1399
  getXDistanceBetweenPoint(point1, point2, !isHorizontalLayout);
1407
1400
  };
@@ -1423,37 +1416,6 @@ const getLayoutDirection = (node, isHorizontal) => {
1423
1416
  }
1424
1417
  }
1425
1418
  };
1426
- // Based on right
1427
- // Right -> Left:
1428
- // 1. End point -> starting point/start point -> end point
1429
- // 2. Add -> Subtract
1430
- // Horizontal -> Vertical:
1431
- // 1. Starting point/end point -> vertical axis
1432
- // 2. Addition and subtraction -> vertical axis
1433
- // Bottom -> Top:
1434
- // 1. End point -> starting point/end point -> starting point
1435
- // 2. Add -> Subtract
1436
- const moveXOfPoint = (point, distance, direction = LayoutDirection.right) => {
1437
- if (direction === LayoutDirection.left) {
1438
- return [point[0] - distance, point[1]];
1439
- }
1440
- if (direction === LayoutDirection.bottom) {
1441
- return [point[0], point[1] + distance];
1442
- }
1443
- if (direction === LayoutDirection.top) {
1444
- return [point[0], point[1] - distance];
1445
- }
1446
- return [point[0] + distance, point[1]];
1447
- };
1448
- const moveYOfPoint = (point, distance, direction = LayoutDirection.right) => {
1449
- if (direction === LayoutDirection.bottom) {
1450
- return [point[0] + distance, point[1]];
1451
- }
1452
- if (direction === LayoutDirection.top) {
1453
- return [point[0] + distance, point[1]];
1454
- }
1455
- return [point[0], point[1] + distance];
1456
- };
1457
1419
  const transformPlacement = (placement, direction) => {
1458
1420
  // to left
1459
1421
  if (direction === LayoutDirection.left) {
@@ -1564,7 +1526,9 @@ function drawLogicLink(board, parent, node, isHorizontal, defaultStrokeColor = n
1564
1526
  let endPoint = getPointByPlacement(nodeClient, endPlacement);
1565
1527
  // ② Determine the convex straight line
1566
1528
  const straightLineDistance = 8;
1567
- const beginPoint2 = hasStraightLine ? moveXOfPoint(beginPoint, straightLineDistance, linkDirection) : beginPoint;
1529
+ const beginPoint2 = hasStraightLine
1530
+ ? moveXOfPoint(beginPoint, straightLineDistance, linkDirection)
1531
+ : beginPoint;
1568
1532
  let straightLine = hasStraightLine ? [beginPoint, beginPoint2, beginPoint2] : [];
1569
1533
  // ③ Determine the curve
1570
1534
  const beginBufferDistance = (parent.hGap + node.hGap) / 3;
@@ -2514,7 +2478,7 @@ const setAbstractByElements = (board, groupParent, group) => {
2514
2478
  };
2515
2479
  const insertAbstractNode = (board, path, start, end) => {
2516
2480
  const abstractNodeText = getAbstractNodeText(board);
2517
- const { width, height } = getTopicSize(false, false, buildText(abstractNodeText));
2481
+ const { width, height } = getTopicSize(board, false, false, buildText(abstractNodeText));
2518
2482
  const mindElement = createMindElement(abstractNodeText, width, height, {
2519
2483
  strokeWidth: DefaultAbstractNodeStyle.branch.width,
2520
2484
  branchWidth: DefaultAbstractNodeStyle.branch.width
@@ -2752,201 +2716,202 @@ const MindTransforms = {
2752
2716
  insertMind
2753
2717
  };
2754
2718
 
2755
- class NodePlusGenerator extends Generator {
2756
- canDraw(element) {
2757
- if (PlaitBoard.isReadonly(this.board) || element?.isCollapsed) {
2758
- return false;
2759
- }
2760
- return true;
2719
+ class NodeShapeGenerator extends Generator {
2720
+ constructor(board) {
2721
+ super(board, { prepend: true });
2761
2722
  }
2762
- draw(element) {
2763
- const plusG = createG();
2764
- plusG.classList.add('plus');
2765
- const node = MindElement.getNode(element);
2766
- const layout = MindQueries.getLayoutByElement(element);
2767
- const isHorizontal = isHorizontalLayout(layout);
2768
- let linkDirection = getLayoutDirection(node, isHorizontal);
2769
- if (isIndentedLayout(layout)) {
2770
- linkDirection = isTopLayout(layout) ? LayoutDirection.top : LayoutDirection.bottom;
2771
- }
2772
- const isUnderlineShape = getShapeByElement(this.board, element) === MindElementShape.underline;
2773
- const nodeClient = getRectangleByNode(node);
2774
- const branchWidth = getBranchWidthByMindElement(this.board, element);
2775
- const branchColor = PlaitMind.isMind(element)
2776
- ? getNextBranchColor(this.board, element)
2777
- : getBranchColorByMindElement(this.board, element);
2778
- let distance = 8;
2779
- let placement = [HorizontalPlacement.right, VerticalPlacement.middle];
2780
- transformPlacement(placement, linkDirection);
2781
- // underline shape and horizontal
2782
- if (isHorizontal && isUnderlineShape && !element.isRoot) {
2783
- placement[1] = VerticalPlacement.bottom;
2784
- }
2785
- let beginPoint = getPointByPlacement(nodeClient, placement);
2786
- if (element.children.length > 0 && !element.isRoot) {
2787
- beginPoint = moveXOfPoint(beginPoint, EXTEND_DIAMETER + 8, linkDirection);
2788
- distance = 5;
2723
+ canDraw(element, data) {
2724
+ const shape = getShapeByElement(this.board, element);
2725
+ if (shape === MindElementShape.roundRectangle) {
2726
+ return true;
2789
2727
  }
2790
- const endPoint = moveXOfPoint(beginPoint, distance, linkDirection);
2791
- const circleCenter = moveXOfPoint(endPoint, 8, linkDirection);
2792
- const line = PlaitBoard.getRoughSVG(this.board).line(beginPoint[0], beginPoint[1], endPoint[0], endPoint[1], {
2793
- stroke: branchColor,
2794
- strokeWidth: branchWidth
2795
- });
2796
- const circle = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2797
- fill: QUICK_INSERT_CIRCLE_COLOR,
2798
- stroke: QUICK_INSERT_CIRCLE_COLOR,
2799
- fillStyle: 'solid'
2800
- });
2801
- const HLineBeginPoint = [circleCenter[0] - 5, circleCenter[1]];
2802
- const HLineEndPoint = [circleCenter[0] + 5, circleCenter[1]];
2803
- const VLineBeginPoint = [circleCenter[0], circleCenter[1] - 5];
2804
- const VLineEndPoint = [circleCenter[0], circleCenter[1] + 5];
2805
- const innerCrossHLine = PlaitBoard.getRoughSVG(this.board).line(HLineBeginPoint[0], HLineBeginPoint[1], HLineEndPoint[0], HLineEndPoint[1], {
2806
- stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2807
- strokeWidth: 2
2808
- });
2809
- const innerCrossVLine = PlaitBoard.getRoughSVG(this.board).line(VLineBeginPoint[0], VLineBeginPoint[1], VLineEndPoint[0], VLineEndPoint[1], {
2810
- stroke: QUICK_INSERT_INNER_CROSS_COLOR,
2811
- strokeWidth: 2
2812
- });
2813
- plusG.appendChild(line);
2814
- plusG.appendChild(circle);
2815
- plusG.appendChild(innerCrossHLine);
2816
- plusG.appendChild(innerCrossVLine);
2817
- return plusG;
2818
- }
2819
- afterDraw(element) {
2820
- if (!this.g) {
2821
- throw new Error(`can not find quick insert g`);
2822
- }
2823
- fromEvent(this.g, 'pointerdown')
2824
- .pipe(take(1))
2825
- .subscribe(e => {
2826
- e.preventDefault();
2827
- });
2828
- fromEvent(this.g, 'pointerup')
2829
- .pipe(take(1))
2830
- .subscribe((event) => {
2831
- // wait the event period end of pointerup to otherwise the pointerup will cause new element lose selected state
2832
- setTimeout(() => {
2833
- const path = findNewChildNodePath(this.board, element);
2834
- insertMindElement(this.board, element, path);
2835
- }, 0);
2836
- });
2728
+ return false;
2729
+ }
2730
+ draw(element, data) {
2731
+ const rectangle = getRectangleByNode(data.node);
2732
+ return drawRoundRectangleByElement(this.board, rectangle, data.node.origin);
2837
2733
  }
2838
2734
  }
2839
2735
 
2840
- class CollapseGenerator extends Generator {
2841
- canDraw(element) {
2842
- if (element.children.length && !PlaitMind.isMind(element)) {
2736
+ class NodeMoreGenerator extends Generator {
2737
+ static { this.key = 'mind-node-more'; }
2738
+ canDraw(element, extraData) {
2739
+ if (((extraData?.isHovered || extraData?.isHoveredCollapseArea || extraData?.isHoveredAddArea) && canHandleNodeMore(this.board)) ||
2740
+ (extraData?.isSelected && isLastSelectedMindElement(this.board, element) && canHandleNodeMore(this.board)) ||
2741
+ element.isCollapsed) {
2843
2742
  return true;
2844
2743
  }
2845
2744
  return false;
2846
2745
  }
2847
- draw(element) {
2848
- const collapseG = createG();
2849
- collapseG.classList.add('collapse');
2850
- const node = MindElement.getNode(element);
2746
+ draw(element, extraData) {
2747
+ const moreGContainer = createG();
2851
2748
  const stroke = getBranchColorByMindElement(this.board, element);
2852
- const branchWidth = getBranchWidthByMindElement(this.board, element);
2853
- const layout = MindQueries.getLayoutByElement(element);
2854
- const isUnderlineShape = getShapeByElement(this.board, element) === MindElementShape.underline;
2855
- const isHorizontal = isHorizontalLayout(layout);
2856
- const nodeClient = getRectangleByNode(node);
2857
- let linkDirection = getLayoutDirection(node, isHorizontal);
2858
- if (isIndentedLayout(layout)) {
2859
- linkDirection = isTopLayout(layout) ? LayoutDirection.top : LayoutDirection.bottom;
2749
+ const layoutDirection = getNodeMoreLayoutDirection(this.board, element);
2750
+ const moreStartAndEnd = getMoreStartAndEnd(this.board, element, layoutDirection);
2751
+ const collapseOrExpandCenter = moveXOfPoint(moreStartAndEnd[1], NODE_MORE_ICON_DIAMETER / 2, layoutDirection);
2752
+ const hasChildren = element.children.length > 0;
2753
+ const isShowCollapseOrAdd = !element.isCollapsed &&
2754
+ (isSelectedElement(this.board, element) ||
2755
+ !!extraData?.isHovered ||
2756
+ !!extraData?.isHoveredCollapseArea ||
2757
+ !!extraData?.isHoveredAddArea);
2758
+ const isShowCollapse = isShowCollapseOrAdd && hasChildren && !PlaitMind.isMind(element);
2759
+ const isShowAdd = isShowCollapseOrAdd && !PlaitBoard.isReadonly(this.board);
2760
+ const addCenter = (isShowCollapseOrAdd && getAddCenterByCollapseOrExpandCenter(element, collapseOrExpandCenter, layoutDirection)) || null;
2761
+ this.toggleCollapseOrAdd(collapseOrExpandCenter, addCenter, stroke, moreGContainer, isShowCollapse, isShowAdd, !!extraData?.isHoveredAddArea, !!extraData?.isShowCollapseAnimation, !!extraData?.isShowAddAnimation);
2762
+ this.toggleExpandBadge(element, moreStartAndEnd, collapseOrExpandCenter, stroke, moreGContainer, !!element.isCollapsed, !!extraData?.isHoveredExpandArea);
2763
+ return moreGContainer;
2764
+ }
2765
+ toggleCollapseOrAdd(center, addCenter, stroke, parentG, isShowCollapse, isShowAdd, isHoveredAddArea, isShowCollapseAnimation, isShowAddAnimation) {
2766
+ this.collapseOrAddG?.remove();
2767
+ if (!isShowCollapse && !isShowAdd) {
2768
+ return;
2860
2769
  }
2861
- let placement = [HorizontalPlacement.right, VerticalPlacement.middle];
2862
- transformPlacement(placement, linkDirection);
2863
- // underline shape and horizontal
2864
- if (isHorizontal && isUnderlineShape && !element.isRoot) {
2865
- placement[1] = VerticalPlacement.bottom;
2770
+ this.collapseOrAddG = createG();
2771
+ if (isShowCollapse) {
2772
+ const collapseG = createG();
2773
+ this.collapseOrAddG.appendChild(collapseG);
2774
+ collapseG.classList.add('collapse-button');
2775
+ if (isShowCollapseAnimation) {
2776
+ collapseG.classList.add('animated');
2777
+ }
2778
+ const collapseCircle = PlaitBoard.getRoughSVG(this.board).circle(center[0], center[1], NODE_MORE_ICON_DIAMETER, {
2779
+ fill: '#fff',
2780
+ stroke,
2781
+ strokeWidth: NODE_MORE_STROKE_WIDTH,
2782
+ fillStyle: 'solid'
2783
+ });
2784
+ const start = moveXOfPoint(center, -NODE_MORE_BRIDGE_DISTANCE / 2);
2785
+ const end = moveXOfPoint(center, NODE_MORE_BRIDGE_DISTANCE / 2);
2786
+ const collapseLine = PlaitBoard.getRoughSVG(this.board).line(start[0], start[1], end[0], end[1], {
2787
+ fill: '#fff',
2788
+ stroke,
2789
+ strokeWidth: NODE_MORE_STROKE_WIDTH,
2790
+ fillStyle: 'solid'
2791
+ });
2792
+ collapseG.appendChild(collapseCircle);
2793
+ collapseG.appendChild(collapseLine);
2794
+ setStrokeLinecap(collapseLine, 'round');
2795
+ }
2796
+ if (isShowAdd && addCenter) {
2797
+ const addG = createG();
2798
+ this.collapseOrAddG.appendChild(addG);
2799
+ addG.classList.add('add-button');
2800
+ if (isShowAddAnimation) {
2801
+ addG.classList.add('animated');
2802
+ }
2803
+ const circle = PlaitBoard.getRoughSVG(this.board).circle(addCenter[0], addCenter[1], NODE_MORE_ICON_DIAMETER + NODE_MORE_STROKE_WIDTH, {
2804
+ fill: isHoveredAddArea ? NODE_ADD_HOVER_COLOR : NODE_ADD_CIRCLE_COLOR,
2805
+ stroke: TRANSPARENT,
2806
+ fillStyle: 'solid'
2807
+ });
2808
+ const hLineBeginPoint = [addCenter[0] - NODE_MORE_BRIDGE_DISTANCE / 2, addCenter[1]];
2809
+ const hLineEndPoint = [addCenter[0] + NODE_MORE_BRIDGE_DISTANCE / 2, addCenter[1]];
2810
+ const vLineBeginPoint = [addCenter[0], addCenter[1] - NODE_MORE_BRIDGE_DISTANCE / 2];
2811
+ const vLineEndPoint = [addCenter[0], addCenter[1] + NODE_MORE_BRIDGE_DISTANCE / 2];
2812
+ const innerCrossHLine = PlaitBoard.getRoughSVG(this.board).line(hLineBeginPoint[0], hLineBeginPoint[1], hLineEndPoint[0], hLineEndPoint[1], {
2813
+ stroke: NODE_ADD_INNER_CROSS_COLOR,
2814
+ strokeWidth: NODE_MORE_STROKE_WIDTH
2815
+ });
2816
+ setStrokeLinecap(innerCrossHLine, 'round');
2817
+ const innerCrossVLine = PlaitBoard.getRoughSVG(this.board).line(vLineBeginPoint[0], vLineBeginPoint[1], vLineEndPoint[0], vLineEndPoint[1], {
2818
+ stroke: NODE_ADD_INNER_CROSS_COLOR,
2819
+ strokeWidth: NODE_MORE_STROKE_WIDTH
2820
+ });
2821
+ setStrokeLinecap(innerCrossVLine, 'round');
2822
+ addG.appendChild(circle);
2823
+ addG.appendChild(innerCrossHLine);
2824
+ addG.appendChild(innerCrossVLine);
2866
2825
  }
2867
- let startPoint = getPointByPlacement(nodeClient, placement);
2868
- const endPoint = moveXOfPoint(startPoint, EXTEND_OFFSET, linkDirection);
2869
- const circleCenter = moveXOfPoint(endPoint, EXTEND_DIAMETER / 2, linkDirection);
2870
- const arrowPoints = this.getArrowPoints(circleCenter, linkDirection);
2871
- const arrowLine = drawLinearPath(arrowPoints, {
2872
- stroke,
2873
- strokeWidth: 2
2874
- });
2875
- const extendLine = PlaitBoard.getRoughSVG(this.board).line(startPoint[0], startPoint[1], endPoint[0], endPoint[1], {
2876
- strokeWidth: branchWidth,
2877
- stroke
2878
- });
2879
- const badge = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2826
+ parentG.appendChild(this.collapseOrAddG);
2827
+ }
2828
+ toggleExpandBadge(element, moreStartAndEnd, center, stroke, parentG, isCollapsed, isHoveredExpandIcon) {
2829
+ this.expandG?.remove();
2830
+ if (!isCollapsed) {
2831
+ return;
2832
+ }
2833
+ this.expandG = createG();
2834
+ this.expandG.classList.add('expanded-button');
2835
+ const endWithWidth = moreStartAndEnd[1];
2836
+ const moreLine = PlaitBoard.getRoughSVG(this.board).line(moreStartAndEnd[0][0], moreStartAndEnd[0][1], endWithWidth[0], endWithWidth[1], {
2880
2837
  fill: stroke,
2881
2838
  stroke,
2882
- fillStyle: 'solid'
2839
+ fillStyle: 'solid',
2840
+ strokeWidth: NODE_MORE_STROKE_WIDTH
2883
2841
  });
2884
- const hideCircleG = PlaitBoard.getRoughSVG(this.board).circle(circleCenter[0], circleCenter[1], EXTEND_DIAMETER, {
2885
- fill: '#fff',
2886
- stroke,
2887
- strokeWidth: branchWidth > 3 ? 3 : branchWidth,
2842
+ const backgroundColor = isHoveredExpandIcon ? rgbaToHEX(stroke, 0.4) : rgbaToHEX(stroke, 0.2);
2843
+ const badgeBackground = PlaitBoard.getRoughSVG(this.board).circle(center[0], center[1], NODE_MORE_ICON_DIAMETER + NODE_MORE_STROKE_WIDTH, {
2844
+ fill: backgroundColor,
2845
+ stroke: TRANSPARENT,
2888
2846
  fillStyle: 'solid'
2889
2847
  });
2890
- if (element.isCollapsed) {
2891
- let numberOffset = 0;
2892
- if (getChildrenCount(element) >= 10)
2893
- numberOffset = -2;
2894
- if (getChildrenCount(element) === 1)
2895
- numberOffset = 1;
2896
- const badgeText = createText(circleCenter[0] - 4 + numberOffset, circleCenter[1] + 4, stroke, `${getChildrenCount(element)}`);
2897
- badge.setAttribute('style', 'opacity: 0.15');
2898
- badgeText.setAttribute('style', 'font-size: 12px');
2899
- collapseG.appendChild(badge);
2900
- collapseG.appendChild(badgeText);
2901
- collapseG.appendChild(extendLine);
2902
- }
2903
- else {
2904
- collapseG.appendChild(hideCircleG);
2905
- collapseG.appendChild(arrowLine);
2906
- }
2907
- collapseG.appendChild(extendLine);
2908
- return collapseG;
2909
- }
2910
- afterDraw(element) {
2911
- if (!this.g) {
2912
- throw new Error(`can not find quick insert g`);
2913
- }
2914
- fromEvent(this.g, 'pointerdown')
2915
- .pipe(filter(() => !PlaitBoard.isPointer(this.board, PlaitPointerType.hand) || !!PlaitBoard.isReadonly(this.board)), take(1))
2916
- .subscribe((event) => {
2917
- event.preventDefault();
2918
- const isCollapsed = !element.isCollapsed;
2919
- const newElement = { isCollapsed };
2920
- const path = PlaitBoard.findPath(this.board, element);
2921
- Transforms.setNode(this.board, newElement, path);
2848
+ const childrenCount = getChildrenCount(element);
2849
+ let text = `${childrenCount}`;
2850
+ let y = center[1] + 4.5;
2851
+ if (childrenCount >= 99) {
2852
+ text = '...';
2853
+ y = center[1] + 1;
2854
+ }
2855
+ const { width, height } = measureElement(this.board, buildText(text), {
2856
+ fontSize: Number(FontSizes.fontSize12),
2857
+ fontFamily: DEFAULT_FONT_FAMILY
2922
2858
  });
2923
- }
2924
- getArrowPoints(circleCenter, linkDirection) {
2925
- let arrowTopPoint = moveXOfPoint(circleCenter, 2, linkDirection);
2926
- arrowTopPoint = moveYOfPoint(arrowTopPoint, 4, linkDirection);
2927
- const arrowMiddlePoint = moveXOfPoint(circleCenter, -2, linkDirection);
2928
- let arrowBottomPoint = moveXOfPoint(circleCenter, 2, linkDirection);
2929
- arrowBottomPoint = moveYOfPoint(arrowBottomPoint, -4, linkDirection);
2930
- return [arrowTopPoint, arrowMiddlePoint, arrowBottomPoint];
2859
+ const badgeText = createText(center[0] - width / 2 + 0.5, y, stroke, `${text}`);
2860
+ badgeText.setAttribute('style', `font-size: ${Number(FontSizes.fontSize12)}px;`);
2861
+ this.expandG.appendChild(moreLine);
2862
+ this.expandG.appendChild(badgeBackground);
2863
+ this.expandG.appendChild(badgeText);
2864
+ parentG.appendChild(this.expandG);
2931
2865
  }
2932
2866
  }
2933
-
2934
- class NodeShapeGenerator extends Generator {
2935
- constructor(board) {
2936
- super(board, { prepend: true });
2867
+ const getCollapseAndAddCenterPoint = (board, element) => {
2868
+ const layoutDirection = getNodeMoreLayoutDirection(board, element);
2869
+ const [startPoint, endPoint] = getMoreStartAndEnd(board, element, layoutDirection);
2870
+ const collapseCenter = moveXOfPoint(endPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection);
2871
+ const addCenter = getAddCenterByCollapseOrExpandCenter(element, collapseCenter, layoutDirection);
2872
+ return { collapseCenter, addCenter };
2873
+ };
2874
+ const getAddCenterByCollapseOrExpandCenter = (target, collapseOrExpandCenter, layoutDirection) => {
2875
+ let addCenter = collapseOrExpandCenter;
2876
+ if (target.children?.length > 0 && !PlaitMind.isMind(target)) {
2877
+ addCenter = moveXOfPoint(addCenter, NODE_MORE_LINE_DISTANCE + NODE_MORE_ICON_DIAMETER, layoutDirection);
2937
2878
  }
2938
- canDraw(element, data) {
2939
- const shape = getShapeByElement(this.board, element);
2940
- if (shape === MindElementShape.roundRectangle) {
2941
- return true;
2942
- }
2943
- return false;
2879
+ return addCenter;
2880
+ };
2881
+ const getNodeMoreLayoutDirection = (board, element) => {
2882
+ const node = MindElement.getNode(element);
2883
+ const layout = MindQueries.getLayoutByElement(element);
2884
+ const isHorizontal = isHorizontalLayout(layout);
2885
+ let layoutDirection = getLayoutDirection(node, isHorizontal);
2886
+ if (isIndentedLayout(layout)) {
2887
+ layoutDirection = isTopLayout(layout) ? LayoutDirection.top : LayoutDirection.bottom;
2944
2888
  }
2945
- draw(element, data) {
2946
- const rectangle = getRectangleByNode(data.node);
2947
- return drawRoundRectangleByElement(this.board, rectangle, data.node.origin);
2889
+ return layoutDirection;
2890
+ };
2891
+ const getMoreStartAndEnd = (board, element, linkLineDirection) => {
2892
+ const node = MindElement.getNode(element);
2893
+ const isUnderlineShape = getShapeByElement(board, element) === MindElementShape.underline;
2894
+ const nodeClient = getRectangleByNode(node);
2895
+ let placement = [HorizontalPlacement.right, VerticalPlacement.middle];
2896
+ transformPlacement(placement, linkLineDirection);
2897
+ // underline shape and horizontal
2898
+ const layout = MindQueries.getLayoutByElement(element);
2899
+ const isHorizontal = isHorizontalLayout(layout);
2900
+ if (isHorizontal && isUnderlineShape && !element.isRoot) {
2901
+ placement[1] = VerticalPlacement.bottom;
2948
2902
  }
2949
- }
2903
+ let startPoint = getPointByPlacement(nodeClient, placement);
2904
+ const endPoint = moveXOfPoint(startPoint, NODE_MORE_LINE_DISTANCE, linkLineDirection);
2905
+ return [startPoint, endPoint];
2906
+ };
2907
+ const isLastSelectedMindElement = (board, element) => {
2908
+ const selectedElements = getSelectedElements(board);
2909
+ const selectedMindElements = selectedElements.filter((element) => MindElement.isMindElement(board, element)).reverse();
2910
+ return selectedMindElements[selectedMindElements.length - 1] === element;
2911
+ };
2912
+ const canHandleNodeMore = (board) => {
2913
+ return !isResizing(board) && !isSelectionMoving(board) && !isDragging(board) && !isMovingElements(board);
2914
+ };
2950
2915
 
2951
2916
  class MindNodeComponent extends CommonElementFlavour {
2952
2917
  get textManage() {
@@ -2963,8 +2928,7 @@ class MindNodeComponent extends CommonElementFlavour {
2963
2928
  this.nodeShapeGenerator = new NodeShapeGenerator(this.board);
2964
2929
  this.nodeEmojisGenerator = new NodeEmojisGenerator(this.board);
2965
2930
  this.activeGenerator = new NodeActiveGenerator(this.board);
2966
- this.nodePlusGenerator = new NodePlusGenerator(this.board);
2967
- this.collapseGenerator = new CollapseGenerator(this.board);
2931
+ this.nodeMoreGenerator = new NodeMoreGenerator(this.board);
2968
2932
  this.imageGenerator = new ImageGenerator(this.board, {
2969
2933
  getRectangle: (element) => {
2970
2934
  return getImageForeignRectangle(this.board, element);
@@ -3002,6 +2966,7 @@ class MindNodeComponent extends CommonElementFlavour {
3002
2966
  this.getRef().addGenerator(NodeActiveGenerator.key, this.activeGenerator);
3003
2967
  this.getRef().addGenerator(NodeEmojisGenerator.key, this.nodeEmojisGenerator);
3004
2968
  this.getRef().addGenerator(ImageGenerator.key, this.imageGenerator);
2969
+ this.getRef().addGenerator(NodeMoreGenerator.key, this.nodeMoreGenerator);
3005
2970
  this.getRef().initializeTextManage(textManage);
3006
2971
  this.getRef().updateActiveSection = () => {
3007
2972
  this.activeGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
@@ -3021,8 +2986,8 @@ class MindNodeComponent extends CommonElementFlavour {
3021
2986
  this.activeGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
3022
2987
  selected: this.selected
3023
2988
  });
2989
+ this.nodeMoreGenerator.processDrawing(this.element, this.getElementG());
3024
2990
  this.drawEmojis();
3025
- this.drawExtend();
3026
2991
  this.imageGenerator.processDrawing(this.element, this.getElementG());
3027
2992
  if (PlaitMind.isMind(this.context.parent)) {
3028
2993
  this.getElementG().classList.add('branch');
@@ -3036,10 +3001,10 @@ class MindNodeComponent extends CommonElementFlavour {
3036
3001
  this.activeGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
3037
3002
  selected: this.selected
3038
3003
  });
3004
+ this.nodeMoreGenerator.processDrawing(this.element, this.getElementG(), { isSelected: this.selected });
3039
3005
  this.nodeShapeGenerator.processDrawing(this.element, this.getElementG(), { node: this.node });
3040
3006
  this.drawLink();
3041
3007
  this.drawEmojis();
3042
- this.drawExtend();
3043
3008
  if (!MindElement.hasImage(previous.element) && MindElement.hasImage(this.element)) {
3044
3009
  this.imageGenerator.processDrawing(this.element, this.getElementG());
3045
3010
  }
@@ -3058,6 +3023,9 @@ class MindNodeComponent extends CommonElementFlavour {
3058
3023
  this.activeGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
3059
3024
  selected: this.selected
3060
3025
  });
3026
+ this.nodeMoreGenerator.processDrawing(this.element, this.getElementG(), {
3027
+ isSelected: this.selected
3028
+ });
3061
3029
  }
3062
3030
  if (!hasSameParent) {
3063
3031
  this.drawLink();
@@ -3076,32 +3044,17 @@ class MindNodeComponent extends CommonElementFlavour {
3076
3044
  }
3077
3045
  const parent = MindElement.getParent(this.element);
3078
3046
  const parentNode = MindElement.getNode(parent);
3079
- if (this.linkG) {
3080
- this.linkG.remove();
3047
+ if (this.linkLineG) {
3048
+ this.linkLineG.remove();
3081
3049
  }
3082
3050
  const layout = MindQueries.getLayoutByElement(parent);
3083
3051
  if (AbstractNode.isAbstract(this.node.origin)) {
3084
- this.linkG = drawAbstractLink(this.board, this.node, isHorizontalLayout(layout));
3052
+ this.linkLineG = drawAbstractLink(this.board, this.node, isHorizontalLayout(layout));
3085
3053
  }
3086
3054
  else {
3087
- this.linkG = drawLink(this.board, parentNode, this.node, isHorizontalLayout(layout));
3055
+ this.linkLineG = drawLink(this.board, parentNode, this.node, isHorizontalLayout(layout));
3088
3056
  }
3089
- this.getElementG().append(this.linkG);
3090
- }
3091
- drawExtend() {
3092
- if (!this.extendG) {
3093
- this.extendG = createG();
3094
- this.extendG.classList.add('extend');
3095
- this.getElementG().append(this.extendG);
3096
- }
3097
- if (this.element.isCollapsed) {
3098
- this.getElementG().classList.add('collapsed');
3099
- }
3100
- else {
3101
- this.getElementG().classList.remove('collapsed');
3102
- }
3103
- this.nodePlusGenerator.processDrawing(this.element, this.extendG);
3104
- this.collapseGenerator.processDrawing(this.element, this.extendG);
3057
+ this.getElementG().append(this.linkLineG);
3105
3058
  }
3106
3059
  drawTopic() {
3107
3060
  this.textManage.draw(this.element.data.topic);
@@ -3580,72 +3533,152 @@ const withMindHotkey = (baseBoard) => {
3580
3533
  return board;
3581
3534
  };
3582
3535
 
3583
- const pointerMoveHandle = (board, event, nodeExtendHoveredRef) => {
3536
+ const withNodeMore = (board) => {
3537
+ const { pointerMove, pointerLeave, pointerUp } = board;
3538
+ let nodeMoreRef = null;
3539
+ board.pointerMove = (event) => {
3540
+ if (canHandleNodeMore(board)) {
3541
+ throttleRAF(board, 'with-mind-node-hover-hit-test', () => {
3542
+ // target has been deleted
3543
+ if (nodeMoreRef && !PlaitElement.hasMounted(nodeMoreRef.target)) {
3544
+ nodeMoreRef = null;
3545
+ }
3546
+ const newNodeMoreRef = getNodeMoreRef(board, event.x, event.y);
3547
+ if (nodeMoreRef && newNodeMoreRef && nodeMoreRef.target === newNodeMoreRef.target) {
3548
+ return;
3549
+ }
3550
+ if (nodeMoreRef) {
3551
+ toggleHoveredNodeCallback({
3552
+ target: nodeMoreRef.target,
3553
+ isHovered: false,
3554
+ isHoveredCollapseArea: false,
3555
+ isHoveredExpandArea: false,
3556
+ isHoveredAddArea: false
3557
+ });
3558
+ }
3559
+ if (newNodeMoreRef) {
3560
+ toggleHoveredNodeCallback(newNodeMoreRef);
3561
+ if (nodeMoreRef) {
3562
+ nodeMoreRef.target = newNodeMoreRef.target;
3563
+ }
3564
+ else {
3565
+ nodeMoreRef = newNodeMoreRef;
3566
+ }
3567
+ }
3568
+ else {
3569
+ nodeMoreRef = null;
3570
+ }
3571
+ });
3572
+ }
3573
+ pointerMove(event);
3574
+ };
3575
+ board.pointerUp = (event) => {
3576
+ if (nodeMoreRef && (nodeMoreRef.isHoveredCollapseArea || nodeMoreRef.isHoveredExpandArea)) {
3577
+ const isCollapsed = !nodeMoreRef.target.isCollapsed;
3578
+ const newElement = { isCollapsed };
3579
+ const path = PlaitBoard.findPath(board, nodeMoreRef.target);
3580
+ Transforms.setNode(board, newElement, path);
3581
+ setTimeout(() => {
3582
+ const newNodeMoreRef = getNodeMoreRef(board, event.x, event.y);
3583
+ if (newNodeMoreRef) {
3584
+ toggleHoveredNodeCallback(newNodeMoreRef);
3585
+ nodeMoreRef = newNodeMoreRef;
3586
+ }
3587
+ else {
3588
+ nodeMoreRef = null;
3589
+ }
3590
+ }, 0);
3591
+ return;
3592
+ }
3593
+ if (nodeMoreRef && nodeMoreRef.isHoveredAddArea && !PlaitBoard.isReadonly(board)) {
3594
+ if (nodeMoreRef) {
3595
+ const path = findNewChildNodePath(board, nodeMoreRef.target);
3596
+ insertMindElement(board, nodeMoreRef.target, path);
3597
+ }
3598
+ return;
3599
+ }
3600
+ pointerUp(event);
3601
+ };
3602
+ const toggleHoveredNodeCallback = (ref) => {
3603
+ const elementRef = PlaitElement.getElementRef(ref.target);
3604
+ const nodeMoreGenerator = elementRef?.getGenerator(NodeMoreGenerator.key);
3605
+ if (nodeMoreGenerator) {
3606
+ const g = PlaitElement.getElementG(ref.target);
3607
+ nodeMoreGenerator.processDrawing(ref.target, g, {
3608
+ isHovered: ref.isHovered,
3609
+ isHoveredCollapseArea: ref.isHoveredCollapseArea,
3610
+ isHoveredExpandArea: ref.isHoveredExpandArea,
3611
+ isSelected: isSelectedElement(board, ref.target),
3612
+ isHoveredAddArea: ref.isHoveredAddArea,
3613
+ isShowCollapseAnimation: (ref.isHovered || ref.isHoveredCollapseArea) && !isSelectedElement(board, ref.target),
3614
+ isShowAddAnimation: (ref.isHovered || ref.isHoveredAddArea) && !isSelectedElement(board, ref.target)
3615
+ });
3616
+ }
3617
+ };
3618
+ board.pointerLeave = (event) => {
3619
+ if (nodeMoreRef) {
3620
+ toggleHoveredNodeCallback({
3621
+ target: nodeMoreRef.target,
3622
+ isHovered: false,
3623
+ isHoveredCollapseArea: false,
3624
+ isHoveredExpandArea: false,
3625
+ isHoveredAddArea: false
3626
+ });
3627
+ }
3628
+ nodeMoreRef = null;
3629
+ pointerLeave(event);
3630
+ };
3631
+ return board;
3632
+ };
3633
+ const getNodeMoreRef = (board, x, y) => {
3584
3634
  let target = null;
3585
- const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3586
- depthFirstRecursion(board, element => {
3635
+ let isHovered = false;
3636
+ let isHoveredCollapseArea = false;
3637
+ let isHoveredExpandArea = false;
3638
+ let isHoveredAddArea = false;
3639
+ const point = toViewBoxPoint(board, toHostPoint(board, x, y));
3640
+ depthFirstRecursion(board, (element) => {
3587
3641
  if (target) {
3588
3642
  return;
3589
3643
  }
3590
3644
  if (!MindElement.isMindElement(board, element)) {
3591
3645
  return;
3592
3646
  }
3647
+ const isMind = PlaitMind.isMind(element);
3593
3648
  const isHitElement = isHitMindElement(board, point, element);
3594
- if (isHitElement) {
3649
+ let isHitCollapseOrExpand = false;
3650
+ let isHitAdd = false;
3651
+ const { collapseCenter, addCenter } = getCollapseAndAddCenterPoint(board, element);
3652
+ const collapseOrExpandIconRectangle = !isMind && RectangleClient.getRectangleByCenterPoint(collapseCenter, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER);
3653
+ isHitCollapseOrExpand =
3654
+ collapseOrExpandIconRectangle &&
3655
+ RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), collapseOrExpandIconRectangle);
3656
+ const addIconRectangle = RectangleClient.getRectangleByCenterPoint(addCenter, NODE_MORE_ICON_DIAMETER, NODE_MORE_ICON_DIAMETER);
3657
+ isHitAdd = RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), addIconRectangle);
3658
+ if (isHitElement || isHitCollapseOrExpand || isHitAdd) {
3659
+ isHovered = isHitElement;
3660
+ if (element.children.length > 0) {
3661
+ if (element.isCollapsed) {
3662
+ isHoveredExpandArea = isHitCollapseOrExpand;
3663
+ }
3664
+ else {
3665
+ isHoveredCollapseArea = isHitCollapseOrExpand;
3666
+ }
3667
+ }
3668
+ isHoveredAddArea = isHitAdd;
3595
3669
  target = element;
3596
3670
  }
3597
3671
  }, getIsRecursionFunc(board), true);
3598
- if (nodeExtendHoveredRef && target && nodeExtendHoveredRef.element === target) {
3599
- return nodeExtendHoveredRef;
3600
- }
3601
- if (nodeExtendHoveredRef) {
3602
- removeHovered(nodeExtendHoveredRef.element);
3603
- }
3604
- if (target) {
3605
- addHovered(target);
3606
- if (nodeExtendHoveredRef) {
3607
- nodeExtendHoveredRef.element = target;
3608
- }
3609
- else {
3610
- nodeExtendHoveredRef = { element: target };
3611
- }
3672
+ if (!target) {
3673
+ return null;
3612
3674
  }
3613
- else {
3614
- nodeExtendHoveredRef = null;
3615
- }
3616
- return nodeExtendHoveredRef;
3617
- };
3618
- const pointerLeaveHandle = (board, event, nodeExtendHoveredRef) => {
3619
- if (nodeExtendHoveredRef) {
3620
- removeHovered(nodeExtendHoveredRef.element);
3621
- }
3622
- };
3623
- const addHovered = (element) => {
3624
- PlaitElement.getElementG(element).classList.add('hovered');
3625
- };
3626
- const removeHovered = (element) => {
3627
- PlaitElement.getElementG(element)?.classList?.remove('hovered');
3628
- };
3629
-
3630
- const withNodeHoverHitTest = (board) => {
3631
- const { pointerMove, pointerLeave } = board;
3632
- let nodeExtendHoveredRef = null;
3633
- board.pointerMove = (event) => {
3634
- throttleRAF(board, 'with-mind-node-hover-hit-test', () => {
3635
- // element has been deleted
3636
- if (nodeExtendHoveredRef && !PlaitElement.hasMounted(nodeExtendHoveredRef.element)) {
3637
- nodeExtendHoveredRef = null;
3638
- }
3639
- nodeExtendHoveredRef = pointerMoveHandle(board, event, nodeExtendHoveredRef);
3640
- });
3641
- pointerMove(event);
3642
- };
3643
- board.pointerLeave = (event) => {
3644
- pointerLeaveHandle(board, event, nodeExtendHoveredRef);
3645
- nodeExtendHoveredRef = null;
3646
- pointerLeave(event);
3675
+ return {
3676
+ target,
3677
+ isHovered,
3678
+ isHoveredCollapseArea,
3679
+ isHoveredExpandArea,
3680
+ isHoveredAddArea
3647
3681
  };
3648
- return board;
3649
3682
  };
3650
3683
 
3651
3684
  const withNodeImage = (board) => {
@@ -3800,9 +3833,9 @@ const withNodeResize = (board) => {
3800
3833
  return board;
3801
3834
  };
3802
3835
  const getSelectedTarget = (board, point) => {
3803
- const selectedElements = getSelectedElements(board).filter(value => MindElement.isMindElement(board, value));
3836
+ const selectedElements = getSelectedElements(board).filter((value) => MindElement.isMindElement(board, value));
3804
3837
  if (selectedElements.length > 0) {
3805
- const target = selectedElements.find(value => {
3838
+ const target = selectedElements.find((value) => {
3806
3839
  const rectangle = getResizeActiveRectangle(board, value);
3807
3840
  return distanceBetweenPointAndRectangle(point[0], point[1], rectangle) <= 0;
3808
3841
  });
@@ -3813,7 +3846,12 @@ const getSelectedTarget = (board, point) => {
3813
3846
  const getResizeActiveRectangle = (board, element) => {
3814
3847
  const node = MindElement.getNode(element);
3815
3848
  const rectangle = getRectangleByNode(node);
3816
- return { x: rectangle.x + rectangle.width - EXTEND_OFFSET, y: rectangle.y, width: EXTEND_OFFSET * 2, height: rectangle.height };
3849
+ return {
3850
+ x: rectangle.x + rectangle.width - RESIZE_HANDLE_BUFFER_DISTANCE,
3851
+ y: rectangle.y,
3852
+ width: RESIZE_HANDLE_BUFFER_DISTANCE * 2,
3853
+ height: rectangle.height
3854
+ };
3817
3855
  };
3818
3856
 
3819
3857
  const withNodeImageResize = (board) => {
@@ -3912,7 +3950,7 @@ const insertClipboardData = (board, elements, targetPoint, operationType) => {
3912
3950
  if (hasTargetParent && operationType !== WritableClipboardOperationType.duplicate) {
3913
3951
  if (item.isRoot) {
3914
3952
  newElement = adjustRootToNode(board, newElement);
3915
- const { width, height } = getTopicSizeByElement(newElement, targetParent);
3953
+ const { width, height } = getTopicSizeByElement(board, newElement, targetParent);
3916
3954
  newElement.width = width;
3917
3955
  newElement.height = height;
3918
3956
  }
@@ -3931,7 +3969,7 @@ const insertClipboardData = (board, elements, targetPoint, operationType) => {
3931
3969
  }
3932
3970
  if (!item.isRoot) {
3933
3971
  newElement = adjustNodeToRoot(board, newElement);
3934
- const { width, height } = getTopicSizeByElement(newElement);
3972
+ const { width, height } = getTopicSizeByElement(board, newElement);
3935
3973
  newElement.width = width;
3936
3974
  newElement.height = height;
3937
3975
  }
@@ -3944,13 +3982,13 @@ const insertClipboardData = (board, elements, targetPoint, operationType) => {
3944
3982
  Transforms.addSelectionWithTemporaryElements(board, newELements);
3945
3983
  };
3946
3984
  const insertClipboardText = (board, targetParent, text) => {
3947
- const { width, height } = getTopicSize(false, PlaitMind.isMind(targetParent), buildText(text));
3985
+ const { width, height } = getTopicSize(board, false, PlaitMind.isMind(targetParent), buildText(text));
3948
3986
  const newElement = createMindElement(text, Math.max(width, getFontSizeBySlateElement(text)), height, {});
3949
3987
  Transforms.insertNode(board, newElement, findNewChildNodePath(board, targetParent));
3950
3988
  Transforms.addSelectionWithTemporaryElements(board, [newElement]);
3951
3989
  };
3952
- const getTopicSizeByElement = (element, parentElement) => {
3953
- return getTopicSize(PlaitMind.isMind(element), (parentElement && PlaitMind.isMind(parentElement)) || false, element.data.topic, element.manualWidth);
3990
+ const getTopicSizeByElement = (board, element, parentElement) => {
3991
+ return getTopicSize(board, PlaitMind.isMind(element), (parentElement && PlaitMind.isMind(parentElement)) || false, element.data.topic, element.manualWidth);
3954
3992
  };
3955
3993
 
3956
3994
  const withMindFragment = (baseBoard) => {
@@ -4218,7 +4256,7 @@ const withMind = (baseBoard) => {
4218
4256
  }
4219
4257
  dblClick(event);
4220
4258
  };
4221
- return withEmoji(withNodeResize(withNodeImageResize(withNodeImage(withNodeHoverHitTest(withMindFragment(withMindHotkey(withMindExtend(withCreateMind(withAbstract(withNodeDnd(board)))))))))));
4259
+ return withEmoji(withNodeResize(withNodeImageResize(withNodeImage(withNodeMore(withMindFragment(withMindHotkey(withMindExtend(withCreateMind(withAbstract(withNodeDnd(board)))))))))));
4222
4260
  };
4223
4261
 
4224
4262
  class MindEmojiBaseComponent {
@@ -4235,5 +4273,5 @@ class MindEmojiBaseComponent {
4235
4273
  * Generated bundle index. Do not edit.
4236
4274
  */
4237
4275
 
4238
- export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, ABSTRACT_NODE_TEXT, AbstractHandlePosition, AbstractResizeState, BASE, BranchShape, DEFAULT_MIND_IMAGE_WIDTH, DefaultAbstractNodeStyle, DefaultNodeStyle, ELEMENT_TO_NODE, EXTEND_DIAMETER, EXTEND_OFFSET, GRAY_COLOR, INHERIT_ATTRIBUTE_KEYS, LayoutDirection, LayoutDirectionsMap, MIND_CENTRAL_TEXT, MindColorfulThemeColor, MindDarkThemeColor, MindDefaultThemeColor, MindElement, MindElementShape, MindEmojiBaseComponent, MindI18nKey, MindNode, MindNodeComponent, MindPointerType, MindQueries, MindRetroThemeColor, MindSoftThemeColor, MindStarryThemeColor, MindThemeColor, MindThemeColors, MindTransforms, NodeSpace, NodeTopicThreshold, PRIMARY_COLOR, PlaitMind, PlaitMindComponent, QUICK_INSERT_CIRCLE_COLOR, QUICK_INSERT_CIRCLE_OFFSET, QUICK_INSERT_INNER_CROSS_COLOR, ROOT_TOPIC_FONT_SIZE, STROKE_WIDTH, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, WithMindPluginKey, addActiveOnDragOrigin, addImageFocus, adjustAbstractToNode, adjustNodeToRoot, adjustRootToNode, canSetAbstract, copyNewNode, correctLayoutByDirection, createEmptyMind, createMindElement, deleteElementHandleAbstract, deleteElementsHandleRightNodeCount, detectDropTarget, directionCorrector, directionDetector, divideElementByParent, drawFakeDragNode, drawFakeDropNode, editTopic, findLastChild, findLocationLeftIndex, findNewChildNodePath, findNewSiblingNodePath, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAbstractNodeText, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchShapeByMindElement, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultBranchColor, getDefaultBranchColorByIndex, getDefaultLayout, getDefaultMindElementFontSize, getDefaultMindNameText, getEmojiFontSize, getEmojiForeignRectangle, getEmojiRectangle, getEmojisWidthHeight, getFillByElement, getFirstLevelElement, getFontSizeBySlateElement, getHitAbstractHandle, getHitImageResizeHandleDirection, getImageForeignRectangle, getInCorrectLayoutDirection, getLayoutDirection$1 as getLayoutDirection, getLayoutOptions, getLayoutReverseDirection, getLocationScope, getMindThemeColor, getNewNodeHeight, getNextBranchColor, getOverallAbstracts, getPathByDropTarget, getRectangleByElement, getRectangleByNode, getRectangleByResizingLocation, getRelativeStartEndByAbstractRef, getRootLayout, getSelectedMindElements, getShapeByElement, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTopicRectangleByElement, getTopicRectangleByNode, getTopicSize, getValidAbstractRefs, handleTouchedAbstract, hasPreviousOrNextOfDropPath, insertElementHandleAbstract, insertElementHandleRightNodeCount, insertMindElement, isChildElement, isChildOfAbstract, isChildRight, isChildUp, isCorrectLayout, isDropStandardRight, isHitEmojis, isHitImage, isHitMindElement, isInRightBranchOfStandardLayout, isMixedLayout, isSetAbstract, isValidTarget, removeActiveOnDragOrigin, removeImageFocus, separateChildren, setMindDragging, withEmoji, withMind, withMindExtend };
4276
+ export { ABSTRACT_HANDLE_COLOR, ABSTRACT_HANDLE_LENGTH, ABSTRACT_HANDLE_MASK_WIDTH, ABSTRACT_INCLUDED_OUTLINE_OFFSET, ABSTRACT_NODE_TEXT, AbstractHandlePosition, AbstractResizeState, BASE, BranchShape, DEFAULT_MIND_IMAGE_WIDTH, DefaultAbstractNodeStyle, DefaultNodeStyle, ELEMENT_TO_NODE, GRAY_COLOR, INHERIT_ATTRIBUTE_KEYS, LayoutDirection, LayoutDirectionsMap, MIND_CENTRAL_TEXT, MindColorfulThemeColor, MindDarkThemeColor, MindDefaultThemeColor, MindElement, MindElementShape, MindEmojiBaseComponent, MindI18nKey, MindNode, MindNodeComponent, MindPointerType, MindQueries, MindRetroThemeColor, MindSoftThemeColor, MindStarryThemeColor, MindThemeColor, MindThemeColors, MindTransforms, NODE_ADD_CIRCLE_COLOR, NODE_ADD_HOVER_COLOR, NODE_ADD_INNER_CROSS_COLOR, NODE_MORE_BRIDGE_DISTANCE, NODE_MORE_ICON_DIAMETER, NODE_MORE_LINE_DISTANCE, NODE_MORE_STROKE_WIDTH, NodeSpace, NodeTopicThreshold, PRIMARY_COLOR, PlaitMind, PlaitMindComponent, RESIZE_HANDLE_BUFFER_DISTANCE, ROOT_TOPIC_FONT_SIZE, STROKE_WIDTH, TOPIC_DEFAULT_MAX_WORD_COUNT, TOPIC_FONT_SIZE, WithMindPluginKey, addActiveOnDragOrigin, addImageFocus, adjustAbstractToNode, adjustNodeToRoot, adjustRootToNode, canSetAbstract, copyNewNode, correctLayoutByDirection, createEmptyMind, createMindElement, deleteElementHandleAbstract, deleteElementsHandleRightNodeCount, detectDropTarget, directionCorrector, directionDetector, divideElementByParent, drawFakeDragNode, drawFakeDropNode, editTopic, findLastChild, findLocationLeftIndex, findNewChildNodePath, findNewSiblingNodePath, getAbstractBranchColor, getAbstractBranchWidth, getAbstractHandleRectangle, getAbstractNodeText, getAllowedDirection, getAvailableSubLayoutsByLayoutDirections, getBehindAbstracts, getBranchColorByMindElement, getBranchDirectionsByLayouts, getBranchShapeByMindElement, getBranchWidthByMindElement, getChildrenCount, getCorrespondingAbstract, getDefaultBranchColor, getDefaultBranchColorByIndex, getDefaultLayout, getDefaultMindElementFontSize, getDefaultMindNameText, getEmojiFontSize, getEmojiForeignRectangle, getEmojiRectangle, getEmojisWidthHeight, getFillByElement, getFirstLevelElement, getFontSizeBySlateElement, getHitAbstractHandle, getHitImageResizeHandleDirection, getImageForeignRectangle, getInCorrectLayoutDirection, getLayoutDirection$1 as getLayoutDirection, getLayoutOptions, getLayoutReverseDirection, getLocationScope, getMindThemeColor, getNewNodeHeight, getNextBranchColor, getOverallAbstracts, getPathByDropTarget, getRectangleByElement, getRectangleByNode, getRectangleByResizingLocation, getRelativeStartEndByAbstractRef, getRootLayout, getSelectedMindElements, getShapeByElement, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTopicRectangleByElement, getTopicRectangleByNode, getTopicSize, getValidAbstractRefs, handleTouchedAbstract, hasPreviousOrNextOfDropPath, insertElementHandleAbstract, insertElementHandleRightNodeCount, insertMindElement, isChildElement, isChildOfAbstract, isChildRight, isChildUp, isCorrectLayout, isDropStandardRight, isHitEmojis, isHitImage, isHitMindElement, isInRightBranchOfStandardLayout, isMixedLayout, isSetAbstract, isValidTarget, removeActiveOnDragOrigin, removeImageFocus, separateChildren, setMindDragging, withEmoji, withMind, withMindExtend };
4239
4277
  //# sourceMappingURL=plait-mind.mjs.map