feat(connectionline): add step and smoothstep types
This commit is contained in:
@@ -81,7 +81,7 @@ const BasicFlow = () => <ReactFlow elements={elements} />;
|
|||||||
- `edgeTypes`: object with [edge types](#edge-types--custom-edges)
|
- `edgeTypes`: object with [edge types](#edge-types--custom-edges)
|
||||||
- `style`: css properties
|
- `style`: css properties
|
||||||
- `className`: additional class name
|
- `className`: additional class name
|
||||||
- `connectionLineType`: connection line type = `straight` or `bezier`
|
- `connectionLineType`: connection line type = `default` (bezier), `straight`, `step`, `smoothstep`
|
||||||
- `connectionLineStyle`: connection style as svg attributes
|
- `connectionLineStyle`: connection style as svg attributes
|
||||||
- `deleteKeyCode`: default: `8` (delete)
|
- `deleteKeyCode`: default: `8` (delete)
|
||||||
- `selectionKeyCode`: default: `16` (shift)
|
- `selectionKeyCode`: default: `16` (shift)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import ReactFlow, { isEdge, removeElements, addEdge, MiniMap, Controls } from 'r
|
|||||||
|
|
||||||
import ColorSelectorNode from './ColorSelectorNode';
|
import ColorSelectorNode from './ColorSelectorNode';
|
||||||
|
|
||||||
const onNodeDragStop = node => console.log('drag stop', node);
|
const onNodeDragStop = (node) => console.log('drag stop', node);
|
||||||
const onElementClick = element => console.log('click', element);
|
const onElementClick = (element) => console.log('click', element);
|
||||||
const onLoad = reactFlowInstance => console.log('graph loaded:', reactFlowInstance);
|
const onLoad = (reactFlowInstance) => console.log('graph loaded:', reactFlowInstance);
|
||||||
|
|
||||||
const initBgColor = '#f0e742';
|
const initBgColor = '#f0e742';
|
||||||
|
|
||||||
@@ -16,39 +16,47 @@ const CustomNodeFlow = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onChange = (evt) => {
|
const onChange = (evt) => {
|
||||||
setElements(els => els.map(e => {
|
setElements((els) =>
|
||||||
if (isEdge(e) || e.id !== '2') {
|
els.map((e) => {
|
||||||
return e;
|
if (isEdge(e) || e.id !== '2') {
|
||||||
}
|
return e;
|
||||||
|
|
||||||
const color = evt.target.value;
|
|
||||||
|
|
||||||
setBgColor(color);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...e,
|
|
||||||
data: {
|
|
||||||
...e.data,
|
|
||||||
color
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}));
|
const color = evt.target.value;
|
||||||
|
|
||||||
|
setBgColor(color);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...e,
|
||||||
|
data: {
|
||||||
|
...e.data,
|
||||||
|
color,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
setElements([
|
setElements([
|
||||||
{ id: '1', type: 'input', data: { label: 'An input node' }, position: { x: 0, y: 50 }, sourcePosition: 'right' },
|
{ id: '1', type: 'input', data: { label: 'An input node' }, position: { x: 0, y: 50 }, sourcePosition: 'right' },
|
||||||
{ id: '2', type: 'selectorNode', data: { onChange: onChange, color: initBgColor }, style: { border: '1px solid #777', padding: 10 }, position: { x: 250, y: 50 } },
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'selectorNode',
|
||||||
|
data: { onChange: onChange, color: initBgColor },
|
||||||
|
style: { border: '1px solid #777', padding: 10 },
|
||||||
|
position: { x: 250, y: 50 },
|
||||||
|
},
|
||||||
{ id: '3', type: 'output', data: { label: 'Output A' }, position: { x: 550, y: 25 }, targetPosition: 'left' },
|
{ id: '3', type: 'output', data: { label: 'Output A' }, position: { x: 550, y: 25 }, targetPosition: 'left' },
|
||||||
{ id: '4', type: 'output', data: { label: 'Output B' }, position: { x: 550, y: 100 }, targetPosition: 'left' },
|
{ id: '4', type: 'output', data: { label: 'Output B' }, position: { x: 550, y: 100 }, targetPosition: 'left' },
|
||||||
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
||||||
{ id: 'e2a-3', source: '2__a', target: '3', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e2a-3', source: '2__a', target: '3', animated: true, style: { stroke: '#fff' } },
|
||||||
{ id: 'e2b-4', source: '2__b', target: '4', animated: true, style: { stroke: '#fff' } },
|
{ id: 'e2b-4', source: '2__b', target: '4', animated: true, style: { stroke: '#fff' } },
|
||||||
])
|
]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onElementsRemove = (elementsToRemove) => setElements(els => removeElements(elementsToRemove, els));
|
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
const onConnect = (params) => setElements(els => addEdge(params, els));
|
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -63,12 +71,11 @@ const CustomNodeFlow = () => {
|
|||||||
selectorNode: ColorSelectorNode,
|
selectorNode: ColorSelectorNode,
|
||||||
}}
|
}}
|
||||||
connectionLineStyle={{ stroke: '#ddd', strokeWidth: 2 }}
|
connectionLineStyle={{ stroke: '#ddd', strokeWidth: 2 }}
|
||||||
connectionLineType="bezier"
|
|
||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={[16, 16]}
|
snapGrid={[16, 16]}
|
||||||
>
|
>
|
||||||
<MiniMap
|
<MiniMap
|
||||||
nodeColor={n => {
|
nodeColor={(n) => {
|
||||||
if (n.type === 'input') return 'blue';
|
if (n.type === 'input') return 'blue';
|
||||||
if (n.type === 'selectorNode') return bgColor;
|
if (n.type === 'selectorNode') return bgColor;
|
||||||
if (n.type === 'output') return 'green';
|
if (n.type === 'output') return 'green';
|
||||||
@@ -77,6 +84,6 @@ const CustomNodeFlow = () => {
|
|||||||
<Controls />
|
<Controls />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default CustomNodeFlow;
|
export default CustomNodeFlow;
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ const OverviewFlow = () => {
|
|||||||
style={{ width: '100%', height: '100%' }}
|
style={{ width: '100%', height: '100%' }}
|
||||||
onLoad={onLoad}
|
onLoad={onLoad}
|
||||||
connectionLineStyle={{ stroke: '#ddd', strokeWidth: 2 }}
|
connectionLineStyle={{ stroke: '#ddd', strokeWidth: 2 }}
|
||||||
connectionLineType="bezier"
|
|
||||||
snapToGrid={true}
|
snapToGrid={true}
|
||||||
snapGrid={[16, 16]}
|
snapGrid={[16, 16]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import React, { useEffect, useState, CSSProperties } from 'react';
|
import React, { useEffect, useState, CSSProperties } from 'react';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
|
|
||||||
|
import { getBezierPath } from '../Edges/BezierEdge';
|
||||||
|
import { getStepPath } from '../Edges/StepEdge';
|
||||||
|
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||||
import { ElementId, Node, Transform, HandleElement, Position, ConnectionLineType, HandleType } from '../../types';
|
import { ElementId, Node, Transform, HandleElement, Position, ConnectionLineType, HandleType } from '../../types';
|
||||||
|
|
||||||
interface ConnectionLineProps {
|
interface ConnectionLineProps {
|
||||||
@@ -57,17 +60,46 @@ export default ({
|
|||||||
const targetY = (connectionPositionY - transform[1]) * (1 / transform[2]);
|
const targetY = (connectionPositionY - transform[1]) * (1 / transform[2]);
|
||||||
|
|
||||||
let dAttr: string = '';
|
let dAttr: string = '';
|
||||||
|
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;
|
||||||
|
const isRightOrLeft = sourceHandle?.position === Position.Left || sourceHandle?.position === Position.Right;
|
||||||
|
const targetPosition = isRightOrLeft ? Position.Left : Position.Top;
|
||||||
|
|
||||||
if (connectionLineType === ConnectionLineType.Bezier) {
|
if (connectionLineType === ConnectionLineType.Bezier) {
|
||||||
if (sourceHandle?.position === Position.Left || sourceHandle?.position === Position.Right) {
|
dAttr = getBezierPath({
|
||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
centerX,
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
centerY,
|
||||||
dAttr = `M${sourceX},${sourceY} C${centerX},${sourceY} ${centerX},${targetY} ${targetX},${targetY}`;
|
sourceX,
|
||||||
} else {
|
sourceY,
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
sourcePosition: sourceHandle?.position,
|
||||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
targetX,
|
||||||
dAttr = `M${sourceX},${sourceY} C${sourceX},${centerY} ${targetX},${centerY} ${targetX},${targetY}`;
|
targetY,
|
||||||
}
|
targetPosition,
|
||||||
|
});
|
||||||
|
} else if (connectionLineType === ConnectionLineType.Step) {
|
||||||
|
dAttr = getStepPath({
|
||||||
|
centerY,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
});
|
||||||
|
} else if (connectionLineType === ConnectionLineType.SmoothStep) {
|
||||||
|
dAttr = getSmoothStepPath({
|
||||||
|
xOffset,
|
||||||
|
yOffset,
|
||||||
|
centerX,
|
||||||
|
centerY,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition: sourceHandle?.position,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
dAttr = `M${sourceX},${sourceY} ${targetX},${targetY}`;
|
dAttr = `M${sourceX},${sourceY} ${targetX},${targetY}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,42 @@ import React, { memo } from 'react';
|
|||||||
import EdgeText from './EdgeText';
|
import EdgeText from './EdgeText';
|
||||||
import { EdgeBezierProps, Position } from '../../types';
|
import { EdgeBezierProps, Position } from '../../types';
|
||||||
|
|
||||||
|
interface GetBezierPathParams {
|
||||||
|
centerX: number;
|
||||||
|
centerY: number;
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
sourcePosition?: Position;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
targetPosition?: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBezierPath({
|
||||||
|
centerX,
|
||||||
|
centerY,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition = Position.Bottom,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition = Position.Top,
|
||||||
|
}: GetBezierPathParams): string {
|
||||||
|
let path = `M${sourceX},${sourceY} C${sourceX},${centerY} ${targetX},${centerY} ${targetX},${targetY}`;
|
||||||
|
|
||||||
|
const leftAndRight = [Position.Left, Position.Right];
|
||||||
|
|
||||||
|
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
|
path = `M${sourceX},${sourceY} C${centerX},${sourceY} ${centerX},${targetY} ${targetX},${targetY}`;
|
||||||
|
} else if (leftAndRight.includes(targetPosition)) {
|
||||||
|
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
|
||||||
|
} else if (leftAndRight.includes(sourcePosition)) {
|
||||||
|
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
export default memo(
|
export default memo(
|
||||||
({
|
({
|
||||||
sourceX,
|
sourceX,
|
||||||
@@ -23,17 +59,16 @@ export default memo(
|
|||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||||
|
|
||||||
let dAttr = `M${sourceX},${sourceY} C${sourceX},${centerY} ${targetX},${centerY} ${targetX},${targetY}`;
|
const path = getBezierPath({
|
||||||
|
centerX,
|
||||||
const leftAndRight = [Position.Left, Position.Right];
|
centerY,
|
||||||
|
sourceX,
|
||||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
sourceY,
|
||||||
dAttr = `M${sourceX},${sourceY} C${centerX},${sourceY} ${centerX},${targetY} ${targetX},${targetY}`;
|
sourcePosition,
|
||||||
} else if (leftAndRight.includes(targetPosition)) {
|
targetX,
|
||||||
dAttr = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
|
targetY,
|
||||||
} else if (leftAndRight.includes(sourcePosition)) {
|
targetPosition,
|
||||||
dAttr = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const text = label ? (
|
const text = label ? (
|
||||||
<EdgeText
|
<EdgeText
|
||||||
@@ -48,7 +83,7 @@ export default memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<path style={style} d={dAttr} className="react-flow__edge-path" />
|
<path style={style} d={path} className="react-flow__edge-path" />
|
||||||
{text}
|
{text}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,6 +32,99 @@ const topRightCorner = (cornerX: number, cornerY: number, cornerSize: number): s
|
|||||||
const rightTopCorner = (cornerX: number, cornerY: number, cornerSize: number): string =>
|
const rightTopCorner = (cornerX: number, cornerY: number, cornerSize: number): string =>
|
||||||
`L ${cornerX - cornerSize},${cornerY}Q ${cornerX},${cornerY} ${cornerX},${cornerY + cornerSize}`;
|
`L ${cornerX - cornerSize},${cornerY}Q ${cornerX},${cornerY} ${cornerX},${cornerY + cornerSize}`;
|
||||||
|
|
||||||
|
interface GetSmoothStepPathParams {
|
||||||
|
xOffset: number;
|
||||||
|
yOffset: number;
|
||||||
|
centerX: number;
|
||||||
|
centerY: number;
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
sourcePosition?: Position;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
targetPosition?: Position;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSmoothStepPath({
|
||||||
|
xOffset,
|
||||||
|
yOffset,
|
||||||
|
centerX,
|
||||||
|
centerY,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition = Position.Bottom,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition = Position.Top,
|
||||||
|
}: GetSmoothStepPathParams): string {
|
||||||
|
const cornerWidth = Math.min(5, Math.abs(targetX - sourceX));
|
||||||
|
const cornerHeight = Math.min(5, Math.abs(targetY - sourceY));
|
||||||
|
const cornerSize = Math.min(cornerWidth, cornerHeight, xOffset, yOffset);
|
||||||
|
|
||||||
|
const leftAndRight = [Position.Left, Position.Right];
|
||||||
|
|
||||||
|
let firstCornerPath = null;
|
||||||
|
let secondCornerPath = null;
|
||||||
|
|
||||||
|
// default case: source and target positions are top or bottom
|
||||||
|
if (sourceX < targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY ? bottomLeftCorner(sourceX, centerY, cornerSize) : topLeftCorner(sourceX, centerY, cornerSize);
|
||||||
|
secondCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? rightTopCorner(targetX, centerY, cornerSize)
|
||||||
|
: rightBottomCorner(targetX, centerY, cornerSize);
|
||||||
|
} else if (sourceX > targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? bottomRightCorner(sourceX, centerY, cornerSize)
|
||||||
|
: topRightCorner(sourceX, centerY, cornerSize);
|
||||||
|
secondCornerPath =
|
||||||
|
sourceY < targetY ? leftTopCorner(targetX, centerY, cornerSize) : leftBottomCorner(targetX, centerY, cornerSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
|
if (sourceX < targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? rightTopCorner(centerX, sourceY, cornerSize)
|
||||||
|
: rightBottomCorner(centerX, sourceY, cornerSize);
|
||||||
|
secondCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? bottomLeftCorner(centerX, targetY, cornerSize)
|
||||||
|
: topLeftCorner(centerX, targetY, cornerSize);
|
||||||
|
}
|
||||||
|
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
|
||||||
|
if (sourceX < targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? rightTopCorner(targetX, sourceY, cornerSize)
|
||||||
|
: rightBottomCorner(targetX, sourceY, cornerSize);
|
||||||
|
} else if (sourceX > targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? bottomRightCorner(sourceX, targetY, cornerSize)
|
||||||
|
: topRightCorner(sourceX, targetY, cornerSize);
|
||||||
|
}
|
||||||
|
secondCornerPath = '';
|
||||||
|
} else if (!leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
|
if (sourceX < targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? bottomLeftCorner(sourceX, targetY, cornerSize)
|
||||||
|
: topLeftCorner(sourceX, targetY, cornerSize);
|
||||||
|
} else if (sourceX > targetX) {
|
||||||
|
firstCornerPath =
|
||||||
|
sourceY < targetY
|
||||||
|
? bottomRightCorner(sourceX, targetY, cornerSize)
|
||||||
|
: topRightCorner(sourceX, targetY, cornerSize);
|
||||||
|
}
|
||||||
|
secondCornerPath = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`;
|
||||||
|
}
|
||||||
|
|
||||||
export default memo(
|
export default memo(
|
||||||
({
|
({
|
||||||
sourceX,
|
sourceX,
|
||||||
@@ -52,77 +145,18 @@ export default memo(
|
|||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||||
|
|
||||||
const cornerWidth = Math.min(5, Math.abs(targetX - sourceX));
|
const path = getSmoothStepPath({
|
||||||
const cornerHeight = Math.min(5, Math.abs(targetY - sourceY));
|
xOffset,
|
||||||
const cornerSize = Math.min(cornerWidth, cornerHeight, xOffset, yOffset);
|
yOffset,
|
||||||
|
centerX,
|
||||||
const leftAndRight = [Position.Left, Position.Right];
|
centerY,
|
||||||
|
sourceX,
|
||||||
let path;
|
sourceY,
|
||||||
let firstCornerPath = null;
|
sourcePosition,
|
||||||
let secondCornerPath = null;
|
targetX,
|
||||||
|
targetY,
|
||||||
// default case: source and target positions are top or bottom
|
targetPosition,
|
||||||
if (sourceX < targetX) {
|
});
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomLeftCorner(sourceX, centerY, cornerSize)
|
|
||||||
: topLeftCorner(sourceX, centerY, cornerSize);
|
|
||||||
secondCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? rightTopCorner(targetX, centerY, cornerSize)
|
|
||||||
: rightBottomCorner(targetX, centerY, cornerSize);
|
|
||||||
} else if (sourceX > targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomRightCorner(sourceX, centerY, cornerSize)
|
|
||||||
: topRightCorner(sourceX, centerY, cornerSize);
|
|
||||||
secondCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? leftTopCorner(targetX, centerY, cornerSize)
|
|
||||||
: leftBottomCorner(targetX, centerY, cornerSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
|
||||||
if (sourceX < targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? rightTopCorner(centerX, sourceY, cornerSize)
|
|
||||||
: rightBottomCorner(centerX, sourceY, cornerSize);
|
|
||||||
secondCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomLeftCorner(centerX, targetY, cornerSize)
|
|
||||||
: topLeftCorner(centerX, targetY, cornerSize);
|
|
||||||
}
|
|
||||||
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
|
|
||||||
if (sourceX < targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? rightTopCorner(targetX, sourceY, cornerSize)
|
|
||||||
: rightBottomCorner(targetX, sourceY, cornerSize);
|
|
||||||
} else if (sourceX > targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomRightCorner(sourceX, targetY, cornerSize)
|
|
||||||
: topRightCorner(sourceX, targetY, cornerSize);
|
|
||||||
}
|
|
||||||
secondCornerPath = '';
|
|
||||||
} else if (!leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
|
||||||
if (sourceX < targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomLeftCorner(sourceX, targetY, cornerSize)
|
|
||||||
: topLeftCorner(sourceX, targetY, cornerSize);
|
|
||||||
} else if (sourceX > targetX) {
|
|
||||||
firstCornerPath =
|
|
||||||
sourceY < targetY
|
|
||||||
? bottomRightCorner(sourceX, targetY, cornerSize)
|
|
||||||
: topRightCorner(sourceX, targetY, cornerSize);
|
|
||||||
}
|
|
||||||
secondCornerPath = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
path = `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`;
|
|
||||||
|
|
||||||
const text = label ? (
|
const text = label ? (
|
||||||
<EdgeText
|
<EdgeText
|
||||||
|
|||||||
@@ -3,6 +3,18 @@ import React, { memo } from 'react';
|
|||||||
import EdgeText from './EdgeText';
|
import EdgeText from './EdgeText';
|
||||||
import { EdgeProps } from '../../types';
|
import { EdgeProps } from '../../types';
|
||||||
|
|
||||||
|
interface GetStepPathParams {
|
||||||
|
centerY: number;
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStepPath({ centerY, sourceX, sourceY, targetX, targetY }: GetStepPathParams): string {
|
||||||
|
return `M ${sourceX},${sourceY}L ${sourceX},${centerY}L ${targetX},${centerY}L ${targetX},${targetY}`;
|
||||||
|
}
|
||||||
|
|
||||||
export default memo(
|
export default memo(
|
||||||
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style }: EdgeProps) => {
|
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style }: EdgeProps) => {
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||||
@@ -11,6 +23,8 @@ export default memo(
|
|||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||||
|
|
||||||
|
const path = getStepPath({ centerY, sourceX, sourceY, targetX, targetY });
|
||||||
|
|
||||||
const text = label ? (
|
const text = label ? (
|
||||||
<EdgeText
|
<EdgeText
|
||||||
x={centerX}
|
x={centerX}
|
||||||
@@ -24,11 +38,7 @@ export default memo(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<path
|
<path style={style} className="react-flow__edge-path" d={path} />
|
||||||
style={style}
|
|
||||||
className="react-flow__edge-path"
|
|
||||||
d={`M ${sourceX},${sourceY}L ${sourceX},${centerY}L ${targetX},${centerY}L ${targetX},${targetY}`}
|
|
||||||
/>
|
|
||||||
{text}
|
{text}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -167,8 +167,10 @@ export interface Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum ConnectionLineType {
|
export enum ConnectionLineType {
|
||||||
Bezier = 'bezier',
|
Bezier = 'default',
|
||||||
Straight = 'straight',
|
Straight = 'straight',
|
||||||
|
Step = 'step',
|
||||||
|
SmoothStep = 'smoothstep',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OnConnectFunc = (connection: Connection) => void;
|
export type OnConnectFunc = (connection: Connection) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user