Skip to content

Commit 9ee0b85

Browse files
committed
refactor: optimize flowchart's hover to preview and click to commit function
1 parent ec9cd76 commit 9ee0b85

File tree

12 files changed

+168
-199
lines changed

12 files changed

+168
-199
lines changed

.changeset/wicked-lions-yell.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@plait/common': minor
3+
'@plait/core': minor
4+
'@plait/draw': minor
5+
---
6+
7+
extract point-placement to common package
8+
optimize flowchart's hover to preview and click to commit function

packages/common/src/utils/point-placement.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
import { Direction, Point } from '@plait/core';
22

3+
// Based on right
4+
// Right -> Left:
5+
// 1. End point -> starting point/start point -> end point
6+
// 2. Add -> Subtract
7+
8+
// Horizontal -> Vertical:
9+
// 1. Starting point/end point -> vertical axis
10+
// 2. Addition and subtraction -> vertical axis
11+
12+
// Bottom -> Top:
13+
// 1. End point -> starting point/end point -> starting point
14+
// 2. Add -> Subtract
315
export const moveXOfPoint = (point: Point, distance: number, direction: Direction = Direction.right): Point => {
416
if (direction === Direction.left) {
517
return [point[0] - distance, point[1]];

packages/draw/src/constants/line.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export const LINE_TEXT_SPACE = 4;
77

88
export const LINE_AUTO_COMPLETE_DIAMETER = 6;
99

10-
export const LINE_AUTO_COMPLETE_OPACITY = 0.6;
10+
export const LINE_AUTO_COMPLETE_OPACITY = 1;
1111

12-
export const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
12+
export const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 1;
1313

14-
export const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
14+
export const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 12;
1515

1616
export const LINE_ALIGN_TOLERANCE = 3;
1717

packages/draw/src/interfaces/text.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,3 @@ export interface PlaitText extends PlaitGeometry {
44
shape: BasicShapes.text;
55
autoSize: boolean;
66
}
7-
8-
export enum TextColor {
9-
gray = '#828282',
10-
nomal = '#333333'
11-
}

packages/draw/src/plugins/arrow-line/with-arrow-line-auto-complete-reaction.ts

Lines changed: 75 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,166 +2,141 @@ import {
22
CursorClass,
33
PlaitBoard,
44
PlaitElement,
5-
Point,
65
RectangleClient,
76
rgbaToHEX,
87
drawCircle,
98
hasValidAngle,
109
isSelectionMoving,
1110
rotateAntiPointsByElement,
1211
setAngleForG,
13-
addSelectedElement,
14-
clearSelectedElement,
1512
toActivePoint,
16-
Transforms,
17-
PlaitOptionsBoard,
1813
toActiveRectangleFromViewBoxRectangle,
1914
createG,
2015
rotatePointsByElement,
21-
toHostPoint,
22-
toScreenPointFromActivePoint,
23-
toViewBoxPoint,
2416
isHorizontalDirection,
25-
idCreator
17+
idCreator,
18+
Point
2619
} from '@plait/core';
27-
import {
28-
createGeometryElement,
29-
getAutoCompletePoints,
30-
getHitIndexOfAutoCompletePoint,
31-
getSelectedDrawElements,
32-
handleArrowLineCreating
33-
} from '../../utils';
34-
import {
35-
PRIMARY_COLOR,
36-
PlaitCommonElementRef,
37-
getDirectionByIndex,
38-
getXDistanceBetweenPoint,
39-
moveXOfPoint,
40-
moveYOfPoint
41-
} from '@plait/common';
42-
import {
43-
ArrowLineAutoCompleteOptions,
44-
BOARD_TO_PRELOADING_SHAPE,
45-
WithArrowLineAutoCompletePluginKey
46-
} from './with-arrow-line-auto-complete';
20+
import { getAutoCompletePoints, getHitConnection, getHitIndexOfAutoCompletePoint, getSelectedDrawElements, handleArrowLineCreating } from '../../utils';
21+
import { PRIMARY_COLOR, PlaitCommonElementRef, getDirectionByIndex, getXDistanceBetweenPoint, moveXOfPoint } from '@plait/common';
22+
import { BOARD_TO_PRE_COMMIT } from './with-arrow-line-auto-complete';
4723
import { DrawPointerType, LINE_AUTO_COMPLETE_HOVERED_DIAMETER, LINE_AUTO_COMPLETE_HOVERED_OPACITY } from '../../constants';
4824
import { ArrowLineAutoCompleteGenerator } from '../../generators';
4925
import { getGeometryGeneratorByShape } from '../with-geometry-create';
50-
import { ArrowLineShape, PlaitArrowLine, PlaitDrawElement, PlaitGeometry, PlaitShapeElement, TextColor } from '../../interfaces';
26+
import { ArrowLineShape, PlaitArrowLine, PlaitDrawElement, PlaitGeometry } from '../../interfaces';
27+
28+
const PREVIEW_ARROW_LINE_DISTANCE = 100;
5129

5230
export const withArrowLineAutoCompleteReaction = (board: PlaitBoard) => {
53-
const { pointerMove, globalPointerUp } = board;
31+
const { pointerMove, pointerLeave, globalPointerUp } = board;
5432
let reactionG: SVGGElement | null = null;
5533
let temporaryArrowLineElement: PlaitArrowLine | null = null;
5634
let temporaryShapeElement: PlaitGeometry | null = null;
57-
let sourceElement: PlaitShapeElement | null;
58-
let lineShapeG: SVGGElement | null = null;
59-
let geometryShapeG: SVGGElement | null = null;
60-
let selectedElements: ReturnType<typeof getSelectedDrawElements>;
35+
let temporaryArrowLineG: SVGGElement | null = null;
36+
let temporaryShapeG: SVGGElement | null = null;
6137

6238
board.pointerMove = (event: PointerEvent) => {
6339
reactionG?.remove();
6440
PlaitBoard.getBoardContainer(board).classList.remove(CursorClass.crosshair);
65-
selectedElements = getSelectedDrawElements(board);
66-
lineShapeG?.remove();
67-
geometryShapeG?.remove();
68-
const targetElement = selectedElements.length === 1 && selectedElements[0];
41+
const selectedElements = getSelectedDrawElements(board);
42+
temporaryArrowLineG?.remove();
43+
temporaryShapeG?.remove();
44+
const originElement = selectedElements.length === 1 && selectedElements[0];
6945
const activePoint = toActivePoint(board, event.x, event.y);
70-
if (!PlaitBoard.isReadonly(board) && !isSelectionMoving(board) && targetElement && PlaitDrawElement.isShapeElement(targetElement)) {
71-
const points = getAutoCompletePoints(board, targetElement, true);
46+
if (!PlaitBoard.isReadonly(board) && !isSelectionMoving(board) && originElement && PlaitDrawElement.isShapeElement(originElement)) {
47+
const points = getAutoCompletePoints(board, originElement, true);
7248
const hitIndex = getHitIndexOfAutoCompletePoint(
73-
rotateAntiPointsByElement(board, activePoint, targetElement, true) || activePoint,
49+
rotateAntiPointsByElement(board, activePoint, originElement, true) || activePoint,
7450
points
7551
);
76-
// 0上 1右 2下 3左
7752
const hitPoint = points[hitIndex];
78-
const ref = PlaitElement.getElementRef<PlaitCommonElementRef>(targetElement);
53+
const ref = PlaitElement.getElementRef<PlaitCommonElementRef>(originElement);
7954
const lineAutoCompleteGenerator = ref.getGenerator<ArrowLineAutoCompleteGenerator>(ArrowLineAutoCompleteGenerator.key);
8055
lineAutoCompleteGenerator.recoverAutoCompleteG();
8156
if (hitPoint) {
8257
reactionG = drawCircle(PlaitBoard.getRoughSVG(board), hitPoint, LINE_AUTO_COMPLETE_HOVERED_DIAMETER, {
8358
stroke: 'none',
84-
strokeWidth: 2,
8559
fill: rgbaToHEX(PRIMARY_COLOR, LINE_AUTO_COMPLETE_HOVERED_OPACITY),
8660
fillStyle: 'solid'
8761
});
88-
sourceElement = targetElement;
89-
const sourceRect = RectangleClient.getRectangleByPoints(targetElement.points);
90-
let sourcePoint = RectangleClient.getEdgeCenterPoints(sourceRect)[hitIndex];
91-
const direction = getDirectionByIndex(hitIndex);
92-
let targetPoint = moveXOfPoint(sourcePoint, 100, direction);
62+
const originRect = RectangleClient.getRectangleByPoints(originElement.points);
63+
let arrowLineStartPoint = RectangleClient.getEdgeCenterPoints(originRect)[hitIndex];
64+
const arrowLineDirection = getDirectionByIndex(hitIndex);
65+
let arrowLineEndPoint = moveXOfPoint(arrowLineStartPoint, PREVIEW_ARROW_LINE_DISTANCE, arrowLineDirection);
9366
const pointer = PlaitBoard.getPointer(board) as DrawPointerType;
9467
const geometryGenerator = getGeometryGeneratorByShape(board, pointer);
95-
const temporaryShapePoints = targetElement.points.map((point) =>
68+
const temporaryShapePoints = originElement.points.map((point) =>
9669
moveXOfPoint(
9770
point,
98-
100 + getXDistanceBetweenPoint(targetElement.points[0], targetElement.points[1], isHorizontalDirection(direction)),
99-
direction
71+
PREVIEW_ARROW_LINE_DISTANCE +
72+
getXDistanceBetweenPoint(
73+
originElement.points[0],
74+
originElement.points[1],
75+
isHorizontalDirection(arrowLineDirection)
76+
),
77+
arrowLineDirection
10078
)
10179
);
102-
const temporaryShapeElement = {
103-
...targetElement,
104-
points: temporaryShapePoints,
80+
temporaryArrowLineG = createG();
81+
temporaryShapeG = createG();
82+
temporaryArrowLineG.style.opacity = '0.6';
83+
temporaryShapeG.style.opacity = '0.6';
84+
temporaryShapeElement = {
85+
...(originElement as PlaitGeometry),
86+
points: temporaryShapePoints as [Point, Point],
10587
id: idCreator()
10688
};
107-
lineShapeG = createG();
108-
geometryShapeG = createG();
109-
const rotatedSourcePoint = rotatePointsByElement(sourcePoint, sourceElement) || sourcePoint;
89+
const rotatedArrowLineStartPoint = rotatePointsByElement(arrowLineStartPoint, originElement) || arrowLineStartPoint;
90+
const rotatedArrowLineEndPoint = rotatePointsByElement(arrowLineEndPoint, temporaryShapeElement) || arrowLineEndPoint;
11091
temporaryArrowLineElement = handleArrowLineCreating(
11192
board,
11293
ArrowLineShape.elbow,
113-
rotatedSourcePoint,
114-
targetPoint,
115-
sourceElement,
116-
lineShapeG,
117-
{
118-
strokeColor: TextColor.gray
119-
}
94+
rotatedArrowLineStartPoint,
95+
rotatedArrowLineEndPoint,
96+
originElement,
97+
temporaryArrowLineG
12098
);
121-
122-
// arrow bound geometry
123-
const connectionMap: Record<number, [number, number]> = {
124-
0: [0.5, 1],
125-
1: [0, 0.5],
126-
2: [0.5, 0],
127-
3: [1, 0.5]
128-
};
129-
// temporaryArrowLineElement.target.boundId = temporaryShapeElement.id;
130-
// temporaryArrowLineElement.target.connection = connectionMap[hitIndex] || [0.1, 0];
131-
geometryGenerator.processDrawing(temporaryShapeElement as PlaitGeometry, geometryShapeG);
132-
PlaitBoard.getElementTopHost(board).append(geometryShapeG);
99+
BOARD_TO_PRE_COMMIT.set(board, { temporaryArrowLineElement, temporaryShapeElement });
100+
const connectionInfo = getHitConnection(board, rotatedArrowLineEndPoint, temporaryShapeElement);
101+
temporaryArrowLineElement.target.boundId = temporaryShapeElement.id;
102+
temporaryArrowLineElement.target.connection = connectionInfo;
103+
geometryGenerator.processDrawing(temporaryShapeElement as PlaitGeometry, temporaryShapeG);
104+
PlaitBoard.getElementTopHost(board).append(temporaryShapeG);
133105
PlaitBoard.getActiveHost(board).append(reactionG);
134106
PlaitBoard.getBoardContainer(board).classList.add(CursorClass.crosshair);
135-
if (hasValidAngle(targetElement)) {
136-
const rectangle = board.getRectangle(targetElement)!;
107+
if (hasValidAngle(originElement)) {
108+
const rectangle = board.getRectangle(originElement)!;
137109
const activeRectangle = toActiveRectangleFromViewBoxRectangle(board, rectangle);
138-
setAngleForG(reactionG, RectangleClient.getCenterPoint(activeRectangle), targetElement.angle!);
110+
setAngleForG(reactionG, RectangleClient.getCenterPoint(activeRectangle), originElement.angle!);
139111
}
112+
return;
140113
}
141114
}
115+
BOARD_TO_PRE_COMMIT.delete(board);
142116
pointerMove(event);
143117
};
144-
board.globalPointerUp = (event: PointerEvent) => {
145-
if (temporaryArrowLineElement && temporaryShapeElement) {
146-
BOARD_TO_PRELOADING_SHAPE.set(board, { tempArrow: temporaryArrowLineElement, drawElement: temporaryShapeElement });
147118

148-
clearSelectedElement(board);
149-
addSelectedElement(board, temporaryArrowLineElement);
150-
const afterComplete = (board as PlaitOptionsBoard).getPluginOptions<ArrowLineAutoCompleteOptions>(
151-
WithArrowLineAutoCompletePluginKey
152-
)?.afterComplete;
153-
afterComplete && afterComplete(temporaryArrowLineElement);
154-
} else {
155-
BOARD_TO_PRELOADING_SHAPE.delete(board);
119+
board.pointerLeave = (pointer: PointerEvent) => {
120+
clearRef();
121+
pointerLeave(pointer);
122+
};
123+
124+
const clearRef = () => {
125+
if (reactionG) {
126+
reactionG?.remove();
127+
PlaitBoard.getBoardContainer(board).classList.remove(CursorClass.crosshair);
128+
temporaryArrowLineG?.remove();
129+
temporaryShapeG?.remove();
156130
}
157-
lineShapeG?.remove();
158-
lineShapeG = null;
159-
sourceElement = null;
160-
temporaryArrowLineElement = null;
161-
geometryShapeG?.remove();
162-
geometryShapeG = null;
163-
temporaryShapeElement = null;
131+
if (BOARD_TO_PRE_COMMIT.get(board)) {
132+
BOARD_TO_PRE_COMMIT.delete(board);
133+
}
134+
}
135+
136+
board.globalPointerUp = (event: PointerEvent) => {
164137
globalPointerUp(event);
165-
};
138+
clearRef();
139+
}
140+
166141
return board;
167142
};

packages/draw/src/plugins/arrow-line/with-arrow-line-auto-complete.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ export interface ArrowLineAutoCompleteOptions {
3333
afterComplete: (element: PlaitArrowLine) => {};
3434
}
3535

36-
export type PreloadingShapeRef = { tempArrow: PlaitArrowLine; drawElement: PlaitGeometry };
37-
export const BOARD_TO_PRELOADING_SHAPE = new WeakMap<PlaitBoard, PreloadingShapeRef>();
38-
// 改为weakMap存出
36+
export type PreCommitRef = { temporaryArrowLineElement: PlaitArrowLine; temporaryShapeElement: PlaitGeometry };
37+
38+
export const BOARD_TO_PRE_COMMIT = new WeakMap<PlaitBoard, PreCommitRef>();
3939

4040
export const withArrowLineAutoComplete = (board: PlaitBoard) => {
4141
const { pointerDown, pointerMove, globalPointerUp } = board;
@@ -76,7 +76,7 @@ export const withArrowLineAutoComplete = (board: PlaitBoard) => {
7676
...(rotateAntiPointsByElement(board, movingPoint, sourceElement) || movingPoint),
7777
...autoCompletePoint
7878
);
79-
if (distance > PRESS_AND_MOVE_BUFFER) {
79+
if (distance > PRESS_AND_MOVE_BUFFER * 2) {
8080
const rectangle = RectangleClient.getRectangleByPoints(sourceElement.points);
8181
const shape = getElementShape(sourceElement);
8282
const engine = getEngine(shape);
@@ -111,11 +111,11 @@ export const withArrowLineAutoComplete = (board: PlaitBoard) => {
111111
)?.afterComplete;
112112
afterComplete && afterComplete(temporaryElement);
113113
} else {
114-
const preloadingRef = BOARD_TO_PRELOADING_SHAPE.get(board);
115-
if (preloadingRef) {
116-
Transforms.insertNode(board, preloadingRef.tempArrow, [board.children.length]);
117-
insertElement(board, preloadingRef.drawElement);
118-
BOARD_TO_PRELOADING_SHAPE.delete(board);
114+
const preCommitRef = BOARD_TO_PRE_COMMIT.get(board);
115+
if (preCommitRef) {
116+
Transforms.insertNode(board, preCommitRef.temporaryArrowLineElement, [board.children.length]);
117+
insertElement(board, preCommitRef.temporaryShapeElement);
118+
BOARD_TO_PRE_COMMIT.delete(board);
119119
}
120120
}
121121
if (autoCompletePoint) {

packages/mind/src/generators/node-more.generator.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
Direction,
23
PlaitBoard,
34
Point,
45
createG,
@@ -26,9 +27,9 @@ import {
2627
import { MindLayoutType, isHorizontalLayout, isIndentedLayout, isTopLayout } from '@plait/layouts';
2728
import { MindQueries } from '../queries';
2829
import { getBranchColorByMindElement } from '../utils/node-style/branch';
29-
import { getLayoutDirection, getPointByPlacement, moveXOfPoint, transformPlacement } from '../utils/point-placement';
30+
import { getLayoutDirection, getPointByPlacement, transformPlacement } from '../utils/point-placement';
3031
import { HorizontalPlacement, PointPlacement, VerticalPlacement } from '../interfaces/types';
31-
import { buildText, DEFAULT_FONT_FAMILY, Generator, isResizing, measureElement, TRANSPARENT } from '@plait/common';
32+
import { buildText, DEFAULT_FONT_FAMILY, Generator, isResizing, measureElement, moveXOfPoint, TRANSPARENT } from '@plait/common';
3233
import { getChildrenCount } from '../utils/mind';
3334
import { FontSizes } from '@plait/text-plugins';
3435

@@ -65,7 +66,11 @@ export class NodeMoreGenerator extends Generator<MindElement, NodeMoreExtraData>
6566
const stroke = getBranchColorByMindElement(this.board, element);
6667
const layoutDirection = getNodeMoreLayoutDirection(this.board, element);
6768
const moreStartAndEnd = getMoreStartAndEnd(this.board, element, layoutDirection);
68-
const collapseOrExpandCenter = moveXOfPoint(moreStartAndEnd[1], NODE_MORE_ICON_DIAMETER / 2, layoutDirection);
69+
const collapseOrExpandCenter = moveXOfPoint(
70+
moreStartAndEnd[1],
71+
NODE_MORE_ICON_DIAMETER / 2,
72+
layoutDirection as unknown as Direction
73+
);
6974
const hasChildren = element.children.length > 0;
7075
const isShowCollapseOrAdd =
7176
!element.isCollapsed &&
@@ -254,7 +259,7 @@ export class NodeMoreGenerator extends Generator<MindElement, NodeMoreExtraData>
254259
export const getCollapseAndAddCenterPoint = (board: PlaitBoard, element: MindElement) => {
255260
const layoutDirection = getNodeMoreLayoutDirection(board, element);
256261
const [startPoint, endPoint] = getMoreStartAndEnd(board, element, layoutDirection);
257-
const collapseCenter = moveXOfPoint(endPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection);
262+
const collapseCenter = moveXOfPoint(endPoint, NODE_MORE_ICON_DIAMETER / 2, layoutDirection as unknown as Direction);
258263
const addCenter = getAddCenterByCollapseOrExpandCenter(element, collapseCenter, layoutDirection);
259264
return { collapseCenter, addCenter };
260265
};
@@ -266,7 +271,7 @@ export const getAddCenterByCollapseOrExpandCenter = (
266271
) => {
267272
let addCenter = collapseOrExpandCenter;
268273
if (target.children?.length > 0 && !PlaitMind.isMind(target)) {
269-
addCenter = moveXOfPoint(addCenter, NODE_MORE_LINE_DISTANCE + NODE_MORE_ICON_DIAMETER, layoutDirection);
274+
addCenter = moveXOfPoint(addCenter, NODE_MORE_LINE_DISTANCE + NODE_MORE_ICON_DIAMETER, layoutDirection as unknown as Direction);
270275
}
271276
return addCenter;
272277
};
@@ -295,7 +300,7 @@ export const getMoreStartAndEnd = (board: PlaitBoard, element: MindElement, link
295300
placement[1] = VerticalPlacement.bottom;
296301
}
297302
let startPoint = getPointByPlacement(nodeClient, placement);
298-
const endPoint = moveXOfPoint(startPoint, NODE_MORE_LINE_DISTANCE, linkLineDirection);
303+
const endPoint = moveXOfPoint(startPoint, NODE_MORE_LINE_DISTANCE, linkLineDirection as unknown as Direction);
299304
return [startPoint, endPoint] as [Point, Point];
300305
};
301306

0 commit comments

Comments
 (0)