refactor(edges): siplify edge path / label position handling, repair edge labels
This commit is contained in:
@@ -77,10 +77,6 @@ const edges: Edge[] = [
|
|||||||
id: 'e1-2',
|
id: 'e1-2',
|
||||||
source: '1',
|
source: '1',
|
||||||
target: '2',
|
target: '2',
|
||||||
type: 'smoothstep',
|
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
pathOptions: {
|
pathOptions: {
|
||||||
offset: 30,
|
offset: 30,
|
||||||
},
|
},
|
||||||
@@ -90,10 +86,6 @@ const edges: Edge[] = [
|
|||||||
id: 'e3-4',
|
id: 'e3-4',
|
||||||
source: '3',
|
source: '3',
|
||||||
target: '4',
|
target: '4',
|
||||||
type: 'smoothstep',
|
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
pathOptions: {
|
pathOptions: {
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
},
|
},
|
||||||
@@ -104,24 +96,21 @@ const edges: Edge[] = [
|
|||||||
id: 'e4-5',
|
id: 'e4-5',
|
||||||
source: '5',
|
source: '5',
|
||||||
target: '6',
|
target: '6',
|
||||||
type: 'smoothstep',
|
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'e7-8',
|
id: 'e7-8',
|
||||||
source: '7',
|
source: '7',
|
||||||
target: '8',
|
target: '8',
|
||||||
type: 'smoothstep',
|
|
||||||
markerEnd: {
|
|
||||||
type: MarkerType.ArrowClosed,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const defaultEdgeOptions = {
|
const defaultEdgeOptions = {
|
||||||
|
label: 'Edge Label',
|
||||||
|
type: 'default',
|
||||||
|
markerEnd: {
|
||||||
|
type: MarkerType.ArrowClosed,
|
||||||
|
},
|
||||||
style: {
|
style: {
|
||||||
strokeWidth: 1,
|
strokeWidth: 1,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { EdgeProps, getBezierPath } from 'reactflow';
|
import { EdgeProps, getBezierPath } from 'reactflow';
|
||||||
|
|
||||||
const CustomEdge: FC<EdgeProps> = ({
|
const CustomEdge: FC<EdgeProps> = ({
|
||||||
@@ -11,7 +11,7 @@ const CustomEdge: FC<EdgeProps> = ({
|
|||||||
targetPosition,
|
targetPosition,
|
||||||
data,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
const edgePath = getBezierPath({
|
const [edgePath] = getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { EdgeProps, getBezierPath, EdgeText, getBezierEdgeCenter } from 'reactflow';
|
import { EdgeProps, getBezierPath, EdgeText } from 'reactflow';
|
||||||
|
|
||||||
const CustomEdge: FC<EdgeProps> = ({
|
const CustomEdge: FC<EdgeProps> = ({
|
||||||
id,
|
id,
|
||||||
@@ -11,7 +11,7 @@ const CustomEdge: FC<EdgeProps> = ({
|
|||||||
targetPosition,
|
targetPosition,
|
||||||
data,
|
data,
|
||||||
}) => {
|
}) => {
|
||||||
const edgePath = getBezierPath({
|
const [edgePath, labelX, labelY] = getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
@@ -19,19 +19,13 @@ const CustomEdge: FC<EdgeProps> = ({
|
|||||||
targetY,
|
targetY,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
});
|
});
|
||||||
const [centerX, centerY] = getBezierEdgeCenter({
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<path id={id} className="react-flow__edge-path" d={edgePath} />
|
<path id={id} className="react-flow__edge-path" d={edgePath} />
|
||||||
<EdgeText
|
<EdgeText
|
||||||
x={centerX}
|
x={labelX}
|
||||||
y={centerY}
|
y={labelY}
|
||||||
label={data.text}
|
label={data.text}
|
||||||
labelStyle={{ fill: 'white' }}
|
labelStyle={{ fill: 'white' }}
|
||||||
labelShowBg
|
labelShowBg
|
||||||
|
|||||||
@@ -97,16 +97,16 @@ const ConnectionLine = ({
|
|||||||
|
|
||||||
if (connectionLineType === ConnectionLineType.Bezier) {
|
if (connectionLineType === ConnectionLineType.Bezier) {
|
||||||
// we assume the destination position is opposite to the source position
|
// we assume the destination position is opposite to the source position
|
||||||
dAttr = getBezierPath(pathParams);
|
[dAttr] = getBezierPath(pathParams);
|
||||||
} else if (connectionLineType === ConnectionLineType.Step) {
|
} else if (connectionLineType === ConnectionLineType.Step) {
|
||||||
dAttr = getSmoothStepPath({
|
[dAttr] = getSmoothStepPath({
|
||||||
...pathParams,
|
...pathParams,
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
});
|
});
|
||||||
} else if (connectionLineType === ConnectionLineType.SmoothStep) {
|
} else if (connectionLineType === ConnectionLineType.SmoothStep) {
|
||||||
dAttr = getSmoothStepPath(pathParams);
|
[dAttr] = getSmoothStepPath(pathParams);
|
||||||
} else if (connectionLineType === ConnectionLineType.SimpleBezier) {
|
} else if (connectionLineType === ConnectionLineType.SimpleBezier) {
|
||||||
dAttr = getSimpleBezierPath(pathParams);
|
[dAttr] = getSimpleBezierPath(pathParams);
|
||||||
} else {
|
} else {
|
||||||
dAttr = `M${fromX},${fromY} ${toX},${toY}`;
|
dAttr = `M${fromX},${fromY} ${toX},${toY}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { BaseEdgeProps } from '../../types';
|
|||||||
|
|
||||||
const BaseEdge = ({
|
const BaseEdge = ({
|
||||||
path,
|
path,
|
||||||
centerX,
|
labelX,
|
||||||
centerY,
|
labelY,
|
||||||
label,
|
label,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
labelShowBg,
|
labelShowBg,
|
||||||
@@ -29,8 +29,8 @@ const BaseEdge = ({
|
|||||||
{interactionWidth && <path d={path} fill="none" strokeOpacity={0} strokeWidth={interactionWidth} />}
|
{interactionWidth && <path d={path} fill="none" strokeOpacity={0} strokeWidth={interactionWidth} />}
|
||||||
{label ? (
|
{label ? (
|
||||||
<EdgeText
|
<EdgeText
|
||||||
x={centerX}
|
x={labelX}
|
||||||
y={centerY}
|
y={labelY}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
labelShowBg={labelShowBg}
|
labelShowBg={labelShowBg}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { BezierEdgeProps, Position } from '../../types';
|
import { BezierEdgeProps, Position } from '../../types';
|
||||||
import BaseEdge from './BaseEdge';
|
import BaseEdge from './BaseEdge';
|
||||||
|
import { getBezierEdgeCenter } from './utils';
|
||||||
|
|
||||||
export interface GetBezierPathParams {
|
export interface GetBezierPathParams {
|
||||||
sourceX: number;
|
sourceX: number;
|
||||||
@@ -68,7 +69,7 @@ export function getBezierPath({
|
|||||||
targetY,
|
targetY,
|
||||||
targetPosition = Position.Top,
|
targetPosition = Position.Top,
|
||||||
curvature = 0.25,
|
curvature = 0.25,
|
||||||
}: GetBezierPathParams): string {
|
}: GetBezierPathParams): [string, number, number, number, number] {
|
||||||
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
||||||
pos: sourcePosition,
|
pos: sourcePosition,
|
||||||
x1: sourceX,
|
x1: sourceX,
|
||||||
@@ -85,45 +86,24 @@ export function getBezierPath({
|
|||||||
y2: sourceY,
|
y2: sourceY,
|
||||||
c: curvature,
|
c: curvature,
|
||||||
});
|
});
|
||||||
return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`;
|
const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
|
||||||
}
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
sourceControlX,
|
||||||
|
sourceControlY,
|
||||||
|
targetControlX,
|
||||||
|
targetControlY,
|
||||||
|
});
|
||||||
|
|
||||||
// @TODO: this function will recalculate the control points
|
return [
|
||||||
// one option is to let getXXXPath() return center points
|
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
||||||
// but will introduce breaking changes
|
centerX,
|
||||||
// the getCenter() of other types of edges might need to change, too
|
centerY,
|
||||||
export function getBezierCenter({
|
offsetX,
|
||||||
sourceX,
|
offsetY,
|
||||||
sourceY,
|
];
|
||||||
sourcePosition = Position.Bottom,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
targetPosition = Position.Top,
|
|
||||||
curvature = 0.25,
|
|
||||||
}: GetBezierPathParams): [number, number, number, number] {
|
|
||||||
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
|
||||||
pos: sourcePosition,
|
|
||||||
x1: sourceX,
|
|
||||||
y1: sourceY,
|
|
||||||
x2: targetX,
|
|
||||||
y2: targetY,
|
|
||||||
c: curvature,
|
|
||||||
});
|
|
||||||
const [targetControlX, targetControlY] = getControlWithCurvature({
|
|
||||||
pos: targetPosition,
|
|
||||||
x1: targetX,
|
|
||||||
y1: targetY,
|
|
||||||
x2: sourceX,
|
|
||||||
y2: sourceY,
|
|
||||||
c: curvature,
|
|
||||||
});
|
|
||||||
// cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
|
||||||
// https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
|
||||||
const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
|
|
||||||
const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
|
|
||||||
const xOffset = Math.abs(centerX - sourceX);
|
|
||||||
const yOffset = Math.abs(centerY - sourceY);
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BezierEdge = memo(
|
const BezierEdge = memo(
|
||||||
@@ -146,7 +126,7 @@ const BezierEdge = memo(
|
|||||||
pathOptions,
|
pathOptions,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
}: BezierEdgeProps) => {
|
}: BezierEdgeProps) => {
|
||||||
const params = {
|
const [path, labelX, labelY] = getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
@@ -154,15 +134,13 @@ const BezierEdge = memo(
|
|||||||
targetY,
|
targetY,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
curvature: pathOptions?.curvature,
|
curvature: pathOptions?.curvature,
|
||||||
};
|
});
|
||||||
const path = getBezierPath(params);
|
|
||||||
const [centerX, centerY] = getBezierCenter(params);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseEdge
|
<BaseEdge
|
||||||
path={path}
|
path={path}
|
||||||
centerX={centerX}
|
labelX={labelX}
|
||||||
centerY={centerY}
|
labelY={labelY}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
labelShowBg={labelShowBg}
|
labelShowBg={labelShowBg}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { EdgeProps, Position } from '../../types';
|
import { EdgeProps, Position } from '../../types';
|
||||||
import BaseEdge from './BaseEdge';
|
import BaseEdge from './BaseEdge';
|
||||||
|
import { getBezierEdgeCenter } from './utils';
|
||||||
|
|
||||||
export interface GetSimpleBezierPathParams {
|
export interface GetSimpleBezierPathParams {
|
||||||
sourceX: number;
|
sourceX: number;
|
||||||
@@ -47,7 +48,7 @@ export function getSimpleBezierPath({
|
|||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
targetPosition = Position.Top,
|
targetPosition = Position.Top,
|
||||||
}: GetSimpleBezierPathParams): string {
|
}: GetSimpleBezierPathParams): [string, number, number, number, number] {
|
||||||
const [sourceControlX, sourceControlY] = getControl({
|
const [sourceControlX, sourceControlY] = getControl({
|
||||||
pos: sourcePosition,
|
pos: sourcePosition,
|
||||||
x1: sourceX,
|
x1: sourceX,
|
||||||
@@ -62,42 +63,24 @@ export function getSimpleBezierPath({
|
|||||||
x2: sourceX,
|
x2: sourceX,
|
||||||
y2: sourceY,
|
y2: sourceY,
|
||||||
});
|
});
|
||||||
return `M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`;
|
const [centerX, centerY, offsetX, offsetY] = getBezierEdgeCenter({
|
||||||
}
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
sourceControlX,
|
||||||
|
sourceControlY,
|
||||||
|
targetControlX,
|
||||||
|
targetControlY,
|
||||||
|
});
|
||||||
|
|
||||||
// @TODO: this function will recalculate the control points
|
return [
|
||||||
// one option is to let getXXXPath() return center points
|
`M${sourceX},${sourceY} C${sourceControlX},${sourceControlY} ${targetControlX},${targetControlY} ${targetX},${targetY}`,
|
||||||
// but will introduce breaking changes
|
centerX,
|
||||||
// the getCenter() of other types of edges might need to change, too
|
centerY,
|
||||||
export function getSimpleBezierCenter({
|
offsetX,
|
||||||
sourceX,
|
offsetY,
|
||||||
sourceY,
|
];
|
||||||
sourcePosition = Position.Bottom,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
targetPosition = Position.Top,
|
|
||||||
}: GetSimpleBezierPathParams): [number, number, number, number] {
|
|
||||||
const [sourceControlX, sourceControlY] = getControl({
|
|
||||||
pos: sourcePosition,
|
|
||||||
x1: sourceX,
|
|
||||||
y1: sourceY,
|
|
||||||
x2: targetX,
|
|
||||||
y2: targetY,
|
|
||||||
});
|
|
||||||
const [targetControlX, targetControlY] = getControl({
|
|
||||||
pos: targetPosition,
|
|
||||||
x1: targetX,
|
|
||||||
y1: targetY,
|
|
||||||
x2: sourceX,
|
|
||||||
y2: sourceY,
|
|
||||||
});
|
|
||||||
// cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
|
||||||
// https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
|
||||||
const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
|
|
||||||
const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
|
|
||||||
const xOffset = Math.abs(centerX - sourceX);
|
|
||||||
const yOffset = Math.abs(centerY - sourceY);
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SimpleBezierEdge = memo(
|
const SimpleBezierEdge = memo(
|
||||||
@@ -119,22 +102,20 @@ const SimpleBezierEdge = memo(
|
|||||||
markerStart,
|
markerStart,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
}: EdgeProps) => {
|
}: EdgeProps) => {
|
||||||
const params = {
|
const [path, labelX, labelY] = getSimpleBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetX,
|
targetX,
|
||||||
targetY,
|
targetY,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
};
|
});
|
||||||
const path = getSimpleBezierPath(params);
|
|
||||||
const [centerX, centerY] = getSimpleBezierCenter(params);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseEdge
|
<BaseEdge
|
||||||
path={path}
|
path={path}
|
||||||
centerX={centerX}
|
labelX={labelX}
|
||||||
centerY={centerY}
|
labelY={labelY}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
labelShowBg={labelShowBg}
|
labelShowBg={labelShowBg}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
|
|
||||||
import { getCenter } from './utils';
|
|
||||||
import { SmoothStepEdgeProps, Position, XYPosition } from '../../types';
|
import { SmoothStepEdgeProps, Position, XYPosition } from '../../types';
|
||||||
import BaseEdge from './BaseEdge';
|
import BaseEdge from './BaseEdge';
|
||||||
|
import { getSimpleEdgeCenter } from './utils';
|
||||||
|
|
||||||
export interface GetSmoothStepPathParams {
|
export interface GetSmoothStepPathParams {
|
||||||
sourceX: number;
|
sourceX: number;
|
||||||
@@ -55,9 +55,9 @@ function getPoints({
|
|||||||
sourcePosition: Position;
|
sourcePosition: Position;
|
||||||
target: XYPosition;
|
target: XYPosition;
|
||||||
targetPosition: Position;
|
targetPosition: Position;
|
||||||
center: XYPosition;
|
center: Partial<XYPosition>;
|
||||||
offset: number;
|
offset: number;
|
||||||
}): XYPosition[] {
|
}): [XYPosition[], number, number, number, number] {
|
||||||
const sourceDir = handleDirections[sourcePosition];
|
const sourceDir = handleDirections[sourcePosition];
|
||||||
const targetDir = handleDirections[targetPosition];
|
const targetDir = handleDirections[targetPosition];
|
||||||
const sourceGapped: XYPosition = { x: source.x + sourceDir.x * offset, y: source.y + sourceDir.y * offset };
|
const sourceGapped: XYPosition = { x: source.x + sourceDir.x * offset, y: source.y + sourceDir.y * offset };
|
||||||
@@ -71,22 +71,31 @@ function getPoints({
|
|||||||
const currDir = dir[dirAccessor];
|
const currDir = dir[dirAccessor];
|
||||||
|
|
||||||
let points: XYPosition[] = [];
|
let points: XYPosition[] = [];
|
||||||
|
let centerX, centerY;
|
||||||
|
const [defaultCenterX, defaultCenterY, defaultOffsetX, defaultOffsetY] = getSimpleEdgeCenter({
|
||||||
|
sourceX: source.x,
|
||||||
|
sourceY: source.y,
|
||||||
|
targetX: target.x,
|
||||||
|
targetY: target.y,
|
||||||
|
});
|
||||||
|
|
||||||
// opposite handle positions, default case
|
// opposite handle positions, default case
|
||||||
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
||||||
|
centerX = center.x || defaultCenterX;
|
||||||
|
centerY = center.y || defaultCenterY;
|
||||||
// --->
|
// --->
|
||||||
// |
|
// |
|
||||||
// >---
|
// >---
|
||||||
const verticalSplit: XYPosition[] = [
|
const verticalSplit: XYPosition[] = [
|
||||||
{ x: center.x, y: sourceGapped.y },
|
{ x: centerX, y: sourceGapped.y },
|
||||||
{ x: center.x, y: targetGapped.y },
|
{ x: centerX, y: targetGapped.y },
|
||||||
];
|
];
|
||||||
// |
|
// |
|
||||||
// ---
|
// ---
|
||||||
// |
|
// |
|
||||||
const horizontalSplit: XYPosition[] = [
|
const horizontalSplit: XYPosition[] = [
|
||||||
{ x: sourceGapped.x, y: center.y },
|
{ x: sourceGapped.x, y: centerY },
|
||||||
{ x: targetGapped.x, y: center.y },
|
{ x: targetGapped.x, y: centerY },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (sourceDir[dirAccessor] === currDir) {
|
if (sourceDir[dirAccessor] === currDir) {
|
||||||
@@ -119,9 +128,14 @@ function getPoints({
|
|||||||
points = dirAccessor === 'x' ? sourceTarget : targetSource;
|
points = dirAccessor === 'x' ? sourceTarget : targetSource;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
centerX = points[0].x;
|
||||||
|
centerY = points[0].y;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [source, sourceGapped, ...points, targetGapped, target];
|
const pathPoints = [source, sourceGapped, ...points, targetGapped, target];
|
||||||
|
|
||||||
|
return [pathPoints, centerX, centerY, defaultOffsetX, defaultOffsetY];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): string {
|
function getBend(a: XYPosition, b: XYPosition, c: XYPosition, size: number): string {
|
||||||
@@ -156,21 +170,17 @@ export function getSmoothStepPath({
|
|||||||
centerX,
|
centerX,
|
||||||
centerY,
|
centerY,
|
||||||
offset = 20,
|
offset = 20,
|
||||||
}: GetSmoothStepPathParams): string {
|
}: GetSmoothStepPathParams): [string, number, number, number, number] {
|
||||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
const [points, labelX, labelY, offsetX, offsetY] = getPoints({
|
||||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
|
||||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
|
|
||||||
|
|
||||||
const points = getPoints({
|
|
||||||
source: { x: sourceX, y: sourceY },
|
source: { x: sourceX, y: sourceY },
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
target: { x: targetX, y: targetY },
|
target: { x: targetX, y: targetY },
|
||||||
targetPosition,
|
targetPosition,
|
||||||
center: { x: cX, y: cY },
|
center: { x: centerX, y: centerY },
|
||||||
offset,
|
offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
return points.reduce<string>((res, p, i) => {
|
const path = points.reduce<string>((res, p, i) => {
|
||||||
let segment = '';
|
let segment = '';
|
||||||
|
|
||||||
if (i > 0 && i < points.length - 1) {
|
if (i > 0 && i < points.length - 1) {
|
||||||
@@ -183,6 +193,8 @@ export function getSmoothStepPath({
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}, '');
|
}, '');
|
||||||
|
|
||||||
|
return [path, labelX, labelY, offsetX, offsetY];
|
||||||
}
|
}
|
||||||
|
|
||||||
const SmoothStepEdge = memo(
|
const SmoothStepEdge = memo(
|
||||||
@@ -205,9 +217,7 @@ const SmoothStepEdge = memo(
|
|||||||
pathOptions,
|
pathOptions,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
}: SmoothStepEdgeProps) => {
|
}: SmoothStepEdgeProps) => {
|
||||||
const [centerX, centerY] = getCenter({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition });
|
const [path, labelX, labelY] = getSmoothStepPath({
|
||||||
|
|
||||||
const path = getSmoothStepPath({
|
|
||||||
sourceX,
|
sourceX,
|
||||||
sourceY,
|
sourceY,
|
||||||
sourcePosition,
|
sourcePosition,
|
||||||
@@ -221,8 +231,8 @@ const SmoothStepEdge = memo(
|
|||||||
return (
|
return (
|
||||||
<BaseEdge
|
<BaseEdge
|
||||||
path={path}
|
path={path}
|
||||||
centerX={centerX}
|
labelX={labelX}
|
||||||
centerY={centerY}
|
labelY={labelY}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
labelShowBg={labelShowBg}
|
labelShowBg={labelShowBg}
|
||||||
|
|||||||
@@ -2,6 +2,30 @@ import { memo } from 'react';
|
|||||||
|
|
||||||
import BaseEdge from './BaseEdge';
|
import BaseEdge from './BaseEdge';
|
||||||
import { EdgeProps } from '../../types';
|
import { EdgeProps } from '../../types';
|
||||||
|
import { getSimpleEdgeCenter } from './utils';
|
||||||
|
|
||||||
|
export type GetStraightPathParams = {
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getStraightPath({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
}: GetStraightPathParams): [string, number, number, number, number] {
|
||||||
|
const [centerX, centerY, offsetX, offsetY] = getSimpleEdgeCenter({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
});
|
||||||
|
|
||||||
|
return [`M ${sourceX},${sourceY}L ${targetX},${targetY}`, centerX, centerY, offsetX, offsetY];
|
||||||
|
}
|
||||||
|
|
||||||
const StraightEdge = memo(
|
const StraightEdge = memo(
|
||||||
({
|
({
|
||||||
@@ -20,17 +44,13 @@ const StraightEdge = memo(
|
|||||||
markerStart,
|
markerStart,
|
||||||
interactionWidth,
|
interactionWidth,
|
||||||
}: EdgeProps) => {
|
}: EdgeProps) => {
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY });
|
||||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
|
||||||
|
|
||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseEdge
|
<BaseEdge
|
||||||
path={`M ${sourceX},${sourceY}L ${targetX},${targetY}`}
|
path={path}
|
||||||
centerX={centerX}
|
labelX={labelX}
|
||||||
centerY={centerY}
|
labelY={labelY}
|
||||||
label={label}
|
label={label}
|
||||||
labelStyle={labelStyle}
|
labelStyle={labelStyle}
|
||||||
labelShowBg={labelShowBg}
|
labelShowBg={labelShowBg}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||||
import { StoreApi } from 'zustand';
|
import { StoreApi } from 'zustand';
|
||||||
|
|
||||||
import { Edge, MarkerType, Position, ReactFlowState } from '../../types';
|
import { Edge, MarkerType, ReactFlowState } from '../../types';
|
||||||
|
|
||||||
export const getMarkerEnd = (markerType?: MarkerType, markerEndId?: string): string => {
|
export const getMarkerEnd = (markerType?: MarkerType, markerEndId?: string): string => {
|
||||||
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
||||||
@@ -11,51 +11,6 @@ export const getMarkerEnd = (markerType?: MarkerType, markerEndId?: string): str
|
|||||||
return typeof markerType !== 'undefined' ? `url(#react-flow__${markerType})` : 'none';
|
return typeof markerType !== 'undefined' ? `url(#react-flow__${markerType})` : 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GetCenterParams {
|
|
||||||
sourceX: number;
|
|
||||||
sourceY: number;
|
|
||||||
targetX: number;
|
|
||||||
targetY: number;
|
|
||||||
sourcePosition?: Position;
|
|
||||||
targetPosition?: Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
const LeftOrRight = [Position.Left, Position.Right];
|
|
||||||
|
|
||||||
export const getCenter = ({
|
|
||||||
sourceX,
|
|
||||||
sourceY,
|
|
||||||
targetX,
|
|
||||||
targetY,
|
|
||||||
sourcePosition = Position.Bottom,
|
|
||||||
targetPosition = Position.Top,
|
|
||||||
}: GetCenterParams): [number, number, number, number] => {
|
|
||||||
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition);
|
|
||||||
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition);
|
|
||||||
|
|
||||||
// we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
|
|
||||||
// a mixed edge is when one the source is on the left and the target is on the top for example.
|
|
||||||
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight);
|
|
||||||
|
|
||||||
if (mixedEdge) {
|
|
||||||
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0;
|
|
||||||
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset;
|
|
||||||
|
|
||||||
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY);
|
|
||||||
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset;
|
|
||||||
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
|
||||||
}
|
|
||||||
|
|
||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
|
||||||
|
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
|
||||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
|
||||||
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getMouseHandler(
|
export function getMouseHandler(
|
||||||
id: string,
|
id: string,
|
||||||
getState: StoreApi<ReactFlowState>['getState'],
|
getState: StoreApi<ReactFlowState>['getState'],
|
||||||
@@ -71,3 +26,53 @@ export function getMouseHandler(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB)
|
||||||
|
export function getSimpleEdgeCenter({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
}: {
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
}): [number, number, number, number] {
|
||||||
|
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||||
|
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||||
|
|
||||||
|
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||||
|
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||||
|
|
||||||
|
return [centerX, centerY, xOffset, yOffset];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBezierEdgeCenter({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
sourceControlX,
|
||||||
|
sourceControlY,
|
||||||
|
targetControlX,
|
||||||
|
targetControlY,
|
||||||
|
}: {
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
sourceControlX: number;
|
||||||
|
sourceControlY: number;
|
||||||
|
targetControlX: number;
|
||||||
|
targetControlY: number;
|
||||||
|
}): [number, number, number, number] {
|
||||||
|
// cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
||||||
|
// https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
||||||
|
const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
|
||||||
|
const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
|
||||||
|
const offsetX = Math.abs(centerX - sourceX);
|
||||||
|
const offsetY = Math.abs(centerY - sourceY);
|
||||||
|
|
||||||
|
return [centerX, centerY, offsetX, offsetY];
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
export { default as ReactFlow } from './container/ReactFlow';
|
export { default as ReactFlow } from './container/ReactFlow';
|
||||||
export { default as Handle } from './components/Handle';
|
export { default as Handle } from './components/Handle';
|
||||||
export { default as EdgeText } from './components/Edges/EdgeText';
|
export { default as EdgeText } from './components/Edges/EdgeText';
|
||||||
export { default as StraightEdge } from './components/Edges/StraightEdge';
|
export { default as StraightEdge, getStraightPath } from './components/Edges/StraightEdge';
|
||||||
export { default as StepEdge } from './components/Edges/StepEdge';
|
export { default as StepEdge } from './components/Edges/StepEdge';
|
||||||
export {
|
export { default as BezierEdge, getBezierPath } from './components/Edges/BezierEdge';
|
||||||
default as BezierEdge,
|
export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge';
|
||||||
getBezierPath,
|
|
||||||
getBezierCenter as getBezierEdgeCenter,
|
|
||||||
} from './components/Edges/BezierEdge';
|
|
||||||
export {
|
|
||||||
default as SimpleBezierEdge,
|
|
||||||
getSimpleBezierPath,
|
|
||||||
getSimpleBezierCenter as getSimpleBezierEdgeCenter,
|
|
||||||
} from './components/Edges/SimpleBezierEdge';
|
|
||||||
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
||||||
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
||||||
|
|
||||||
@@ -29,7 +21,7 @@ export {
|
|||||||
getRectOfNodes,
|
getRectOfNodes,
|
||||||
} from './utils/graph';
|
} from './utils/graph';
|
||||||
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
|
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
|
||||||
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
|
export { getMarkerEnd } from './components/Edges/utils';
|
||||||
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
|
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
|
||||||
export { default as Panel } from './components/Panel';
|
export { default as Panel } from './components/Panel';
|
||||||
|
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ export type BaseEdgeProps = Pick<
|
|||||||
| 'markerEnd'
|
| 'markerEnd'
|
||||||
| 'interactionWidth'
|
| 'interactionWidth'
|
||||||
> & {
|
> & {
|
||||||
centerX: number;
|
labelX: number;
|
||||||
centerY: number;
|
labelY: number;
|
||||||
path: string;
|
path: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user