https://github.com/tisoap/react-flow-smart-edge
Custom Edge for React Flow that never intersects with other nodes
https://github.com/tisoap/react-flow-smart-edge
edge flow flowchart graph pathfinding react smart typescript
Last synced: 3 months ago
JSON representation
Custom Edge for React Flow that never intersects with other nodes
- Host: GitHub
- URL: https://github.com/tisoap/react-flow-smart-edge
- Owner: tisoap
- License: mit
- Created: 2021-11-11T13:39:39.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2023-09-16T18:21:13.000Z (almost 2 years ago)
- Last Synced: 2025-04-01T11:51:09.630Z (3 months ago)
- Topics: edge, flow, flowchart, graph, pathfinding, react, smart, typescript
- Language: TypeScript
- Homepage: https://tisoap.github.io/react-flow-smart-edge/
- Size: 12.8 MB
- Stars: 287
- Watchers: 3
- Forks: 49
- Open Issues: 16
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# React Flow Smart Edge
Custom Edges for React Flow that never intersect with other nodes, using pathfinding.






## Install
With `npm`:
```bash
npm install @tisoap/react-flow-smart-edge
```With `yarn`:
```bash
yarn add @tisoap/react-flow-smart-edge
```This package is only compatible with [**version 11 or newer** of React Flow Edge](https://reactflow.dev/docs/guides/migrate-to-v11/).
## Support
Like this project and want to show your support? Buy me a coffee:
[](https://ko-fi.com/J3J472RAJ)
_Really_ like this project? Sponsor me on GitHub:
[](https://github.com/sponsors/tisoap)
## Usage
This package ships with the following Smart Edges components:
- `SmartBezierEdge`: A smart equivalent to React Flow's [BezierEdge](https://reactflow.dev/docs/api/edges/edge-types/)
- `SmartStraightEdge`: A smart equivalent to React Flow's [StraightEdge](https://reactflow.dev/docs/api/edges/edge-types/)
- `SmartStepEdge`: A smart equivalent to React Flow's [StepEdge](https://reactflow.dev/docs/api/edges/edge-types/)Each one can be imported individually as a named export.
### Example
```jsx
import React from 'react'
import { ReactFlow } from 'reactflow'
import { SmartBezierEdge } from '@tisoap/react-flow-smart-edge'
import 'reactflow/dist/style.css'const nodes = [
{
id: '1',
data: { label: 'Node 1' },
position: { x: 300, y: 100 }
},
{
id: '2',
data: { label: 'Node 2' },
position: { x: 300, y: 200 }
}
]const edges = [
{
id: 'e21',
source: '2',
target: '1',
type: 'smart'
}
]// You can give any name to your edge types
// https://reactflow.dev/docs/api/edges/custom-edges/
const edgeTypes = {
smart: SmartBezierEdge
}export const Graph = (props) => {
const { children, ...rest } = propsreturn (
{children}
)
}
```## Edge Options
All smart edges will take the exact same options as a [React Flow Edge](https://reactflow.dev/docs/api/edges/edge-options/).
## Custom Smart Edges
You can have more control over how the edge is rerendered by creating a [custom edge](https://reactflow.dev/docs/api/edges/custom-edges/) and using the provided `getSmartEdge` function. It takes an object with the following keys:
- `sourcePosition`, `targetPosition`, `sourceX`, `sourceY`, `targetX` and `targetY`: The same values your [custom edge](https://reactflow.dev/docs/examples/edges/custom-edge/) will take as props
- `nodes`: An array containing all graph nodes, you can get it from the [`useNodes` hook](https://reactflow.dev/docs/api/hooks/use-nodes/)### Example
Just like you can use `getBezierPath` from `reactflow` to create a [custom edge with a button](https://reactflow.dev/docs/examples/edges/edge-with-button/), you can do the same with `getSmartEdge`:
```jsx
import React from 'react'
import { useNodes, BezierEdge } from 'reactflow'
import { getSmartEdge } from '@tisoap/react-flow-smart-edge'const foreignObjectSize = 200
export function SmartEdgeWithButtonLabel(props) {
const {
id,
sourcePosition,
targetPosition,
sourceX,
sourceY,
targetX,
targetY,
style,
markerStart,
markerEnd
} = propsconst nodes = useNodes()
const getSmartEdgeResponse = getSmartEdge({
sourcePosition,
targetPosition,
sourceX,
sourceY,
targetX,
targetY,
nodes
})// If the value returned is null, it means "getSmartEdge" was unable to find
// a valid path, and you should do something else instead
if (getSmartEdgeResponse === null) {
return
}const { edgeCenterX, edgeCenterY, svgPathString } = getSmartEdgeResponse
return (
<>
{
event.stopPropagation()
alert(`remove ${id}`)
}}
>
X
>
)
}
```## Advanced Custom Smart Edges
The `getSmartEdge` function also accepts an optional object `options`, which allows you to configure aspects of the path-finding algorithm. You may use it like so:
```js
const myOptions = {
// your configuration goes here
nodePadding: 20,
gridRatio: 15
}// ...
const getSmartEdgeResponse = getSmartEdge({
sourcePosition,
targetPosition,
sourceX,
sourceY,
targetX,
targetY,
nodes,
// Pass down options in the getSmartEdge object
options: myOptions
})
```The `options` object accepts the following keys (they're all optional):
- `nodePadding`: How many pixels of padding are added around nodes, or by how much should the edge avoid the walls of a node. Default `10`, minimum `2`.
- `gridRatio`: The size in pixels of each square grid cell used for path-finding. Smaller values for a more accurate path, bigger for faster path-finding. Default `10`, minimum `2`.
- `drawEdge`: Allows you to change the function responsible to draw the SVG line, by default it's the same used by `SmartBezierEdge` ([more below](#drawedge))
- `generatePath`: Allows you to change the function for the path-finding, by default it's the same used by `SmartBezierEdge` ([more below](#generatepath))### `drawEdge`
With the `drawEdge` option, you can change the function used to generate the final [SVG path string](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths), used to draw the line. By default it's the `svgDrawSmoothLinePath` function (same as used by the `SmartBezierEdge`), but the package also includes `svgDrawStraightLinePath` (same as used by the `SmartStraightEdge` and `SmartStepEdge`), or you can provide your own.
```jsx
import {
getSmartEdge,
// Available built-in SVG draw functions
svgDrawSmoothLinePath,
svgDrawStraightLinePath
} from '@tisoap/react-flow-smart-edge'// Using provided SVG draw functions:
const result = getSmartEdge({
// ...
options: {
drawEdge: svgDrawSmoothLinePath
}
})// ...or using your own custom function
const result = getSmartEdge({
// ...
options: {
drawEdge: (source, target, path) => {
// your code goes here
// ...
return svgPath
}
}
})
```The function you provided must comply with this signature:
```ts
type SVGDrawFunction = (
source: XYPosition, // The starting {x, y} point
target: XYPosition, // The ending {x, y} point
path: number[][] // The sequence of points [x, y] the line must follow
) => string // A string to be used in the "d" property of the SVG line
```For inspiration on how to implement your own, you can check the [`drawSvgPath.ts` source code](https://github.com/tisoap/react-flow-smart-edge/blob/main/src/functions/drawSvgPath.ts).
### `generatePath`
With the `generatePath` option, you can change the function used to do [Pathfinding](https://en.wikipedia.org/wiki/Pathfinding). By default, it's the `pathfindingAStarDiagonal` function (same as used by the `SmartBezierEdge`), but the package also includes `pathfindingAStarNoDiagonal` (used by `SmartStraightEdge`) and `pathfindingJumpPointNoDiagonal` (used by `SmartStepEdge`), or your can provide your own. The built-in functions use the [`pathfinding` dependency](https://www.npmjs.com/package/pathfinding#advanced-usage) behind the scenes.
```jsx
import {
getSmartEdge,
// Available built-in pathfinding functions
pathfindingAStarDiagonal,
pathfindingAStarNoDiagonal,
pathfindingJumpPointNoDiagonal
} from '@tisoap/react-flow-smart-edge'// Using provided pathfinding functions:
const result = getSmartEdge({
// ...
options: {
generatePath: pathfindingJumpPointNoDiagonal
}
})// ...or using your own custom function
const result = getSmartEdge({
// ...
options: {
generatePath: (grid, start, end) => {
// your code goes here
// ...
return { fullPath, smoothedPath }
}
}
})
```The function you provide must comply with this signature:
```ts
type PathFindingFunction = (
grid: Grid, // Grid representation of the graph
start: XYPosition, // The starting {x, y} point
end: XYPosition // The ending {x, y} point
) => {
fullPath: number[][] // Array of points [x, y] representing the full path with all points
smoothedPath: number[][] // Array of points [x, y] representing a smaller, compressed path
} | null // The function should return null if it was unable to do pathfinding
```For inspiration on how to implement your own, you can check the [`generatePath.ts` source code](https://github.com/tisoap/react-flow-smart-edge/blob/main/src/functions/generatePath.ts) and the [`pathfinding` dependency](https://www.npmjs.com/package/pathfinding#advanced-usage) documentation.
### Advanced Examples
```jsx
import {
getSmartEdge,
svgDrawSmoothLinePath,
svgDrawStraightLinePath
pathfindingAStarDiagonal,
pathfindingAStarNoDiagonal,
pathfindingJumpPointNoDiagonal
} from '@tisoap/react-flow-smart-edge'// ...
// Same as importing "SmartBezierEdge" directly
const bezierResult = getSmartEdge({
// ...
options: {
drawEdge: svgDrawSmoothLinePath,
generatePath: pathfindingAStarDiagonal,
}
})// Same as importing "SmartStepEdge" directly
const stepResult = getSmartEdge({
// ...
options: {
drawEdge: svgDrawStraightLinePath,
generatePath: pathfindingJumpPointNoDiagonal,
}
})// Same as importing "SmartStraightEdge" directly
const straightResult = getSmartEdge({
// ...
options: {
drawEdge: svgDrawStraightLinePath,
generatePath: pathfindingAStarNoDiagonal,
}
})
```## Storybook
You can see live Storybook examples by visiting [this page](https://tisoap.github.io/react-flow-smart-edge/), and see their source code [here](https://github.com/tisoap/react-flow-smart-edge/blob/main/src/stories/SmartEdge.stories.tsx).
## License
This project is [MIT](https://github.com/tisoap/react-flow-smart-edge/blob/main/LICENSE) licensed.