Skip to content

Commit

Permalink
Merge branch 'v10' into refactor/nodes-edges-state
Browse files Browse the repository at this point in the history
  • Loading branch information
moklick committed Oct 20, 2021
2 parents f957462 + 11ff5ec commit a0b8aa6
Show file tree
Hide file tree
Showing 27 changed files with 691 additions and 356 deletions.
2 changes: 1 addition & 1 deletion .release-it.json
Expand Up @@ -7,6 +7,6 @@
},
"hooks": {
"after:bump": "npm run build",
"after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
"after:release": "echo Successfully released ${name} v${version}."
}
}
2 changes: 2 additions & 0 deletions example/.gitignore
Expand Up @@ -21,3 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*

/src_oldapi
2 changes: 1 addition & 1 deletion example/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions example/src/FloatingEdges/FloatingConnectionLine.tsx
@@ -0,0 +1,42 @@
import { FC } from 'react';
import { getBezierPath, ConnectionLineComponentProps, Node } from 'react-flow-renderer';

import { getEdgeParams } from './utils';

const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({
targetX,
targetY,
sourcePosition,
targetPosition,
sourceNode,
}) => {
if (!sourceNode) {
return null;
}

const targetNode = {
id: 'connection-target',
width: 1,
height: 1,
position: { x: targetX, y: targetY },
} as Node;

const { sx, sy } = getEdgeParams(sourceNode, targetNode);
const d = getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition,
targetPosition,
targetX,
targetY,
});

return (
<g>
<path fill="none" stroke="#222" strokeWidth={1.5} className="animated" d={d} />
<circle cx={targetX} cy={targetY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
</g>
);
};

export default FloatingConnectionLine;
36 changes: 36 additions & 0 deletions example/src/FloatingEdges/FloatingEdge.tsx
@@ -0,0 +1,36 @@
import { FC, useMemo, CSSProperties } from 'react';
import { EdgeProps, useStore, getBezierPath, ReactFlowState } from 'react-flow-renderer';

import { getEdgeParams } from './utils';

const nodeSelector = (s: ReactFlowState) => s.nodes;

const FloatingEdge: FC<EdgeProps> = ({ id, source, target, style }) => {
const nodes = useStore(nodeSelector);

const sourceNode = useMemo(() => nodes.find((n) => n.id === source), [source, nodes]);
const targetNode = useMemo(() => nodes.find((n) => n.id === target), [target, nodes]);

if (!sourceNode || !targetNode) {
return null;
}

const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);

const d = getBezierPath({
sourceX: sx,
sourceY: sy,
sourcePosition: sourcePos,
targetPosition: targetPos,
targetX: tx,
targetY: ty,
});

return (
<g className="react-flow__connection">
<path id={id} className="react-flow__edge-path" d={d} style={style as CSSProperties} />
</g>
);
};

export default FloatingEdge;
65 changes: 65 additions & 0 deletions example/src/FloatingEdges/index.tsx
@@ -0,0 +1,65 @@
import { useState, useCallback } from 'react';

import ReactFlow, {
addEdge,
Background,
OnLoadParams,
EdgeTypesType,
Node,
Connection,
Edge,
applyNodeChanges,
applyEdgeChanges,
NodeChange,
EdgeChange,
} from 'react-flow-renderer';

import './style.css';

import FloatingEdge from './FloatingEdge';
import FloatingConnectionLine from './FloatingConnectionLine';
import { createElements } from './utils';

const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();

const { nodes: initialNodes, edges: initialEdges } = createElements();

const edgeTypes: EdgeTypesType = {
floating: FloatingEdge,
};

const FloatingEdges = () => {
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState<Edge[]>(initialEdges);

const onConnect = useCallback((params: Edge | Connection) => {
setEdges((eds) => addEdge(params, eds));
}, []);

const onNodesChange = useCallback((changes: NodeChange[]) => {
setNodes((ns) => applyNodeChanges(changes, ns));
}, []);

const onEdgesChange = useCallback((changes: EdgeChange[]) => {
setEdges((es) => applyEdgeChanges(changes, es));
}, []);

return (
<div className="floatingedges">
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
onLoad={onLoad}
edgeTypes={edgeTypes}
connectionLineComponent={FloatingConnectionLine}
>
<Background />
</ReactFlow>
</div>
);
};

export default FloatingEdges;
9 changes: 9 additions & 0 deletions example/src/FloatingEdges/style.css
@@ -0,0 +1,9 @@
.floatingedges {
flex-direction: column;
display: flex;
height: 100%;
}

.floatingedges .react-flow__handle {
opacity: 0;
}
106 changes: 106 additions & 0 deletions example/src/FloatingEdges/utils.ts
@@ -0,0 +1,106 @@
import { Position, ArrowHeadType, XYPosition, Node, Edge } from 'react-flow-renderer';

// this helper function returns the intersection point
// of the line between the center of the intersectionNode and the target node
function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a

const {
width: intersectionNodeWidth,
height: intersectionNodeHeight,
position: intersectionNodePosition,
} = intersectionNode;
const targetPosition = targetNode.position;

const w = (intersectionNodeWidth ?? 0) / 2;
const h = (intersectionNodeHeight ?? 0) / 2;

const x2 = intersectionNodePosition.x + w;
const y2 = intersectionNodePosition.y + h;
const x1 = targetPosition.x + w;
const y1 = targetPosition.y + h;

const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
const a = 1 / (Math.abs(xx1) + Math.abs(yy1));
const xx3 = a * xx1;
const yy3 = a * yy1;
const x = w * (xx3 + yy3) + x2;
const y = h * (-xx3 + yy3) + y2;

return { x, y };
}

// returns the position (top,right,bottom or right) passed node compared to the intersection point
function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
const n = { ...node.position, ...node };
const nx = Math.round(n.x);
const ny = Math.round(n.y);
const px = Math.round(intersectionPoint.x);
const py = Math.round(intersectionPoint.y);

if (px <= nx + 1) {
return Position.Left;
}
if (px >= nx + (n.width ?? 0) - 1) {
return Position.Right;
}
if (py <= ny + 1) {
return Position.Top;
}
if (py >= n.y + (n.height ?? 0) - 1) {
return Position.Bottom;
}

return Position.Top;
}

// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
export function getEdgeParams(source: Node, target: Node) {
const sourceIntersectionPoint = getNodeIntersection(source, target);
const targetIntersectionPoint = getNodeIntersection(target, source);

const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
const targetPos = getEdgePosition(target, targetIntersectionPoint);

return {
sx: sourceIntersectionPoint.x,
sy: sourceIntersectionPoint.y,
tx: targetIntersectionPoint.x,
ty: targetIntersectionPoint.y,
sourcePos,
targetPos,
};
}

type NodesAndEdges = {
nodes: Node[];
edges: Edge[];
};

export function createElements(): NodesAndEdges {
const nodes: Node[] = [];
const edges: Edge[] = [];

const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };

nodes.push({ id: 'target', data: { label: 'Target' }, position: center });

for (let i = 0; i < 8; i++) {
const degrees = i * (360 / 8);
const radians = degrees * (Math.PI / 180);
const x = 250 * Math.cos(radians) + center.x;
const y = 250 * Math.sin(radians) + center.y;

nodes.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });

edges.push({
id: `edge-${i}`,
target: 'target',
source: `${i}`,
type: 'floating',
});
}

return { nodes, edges };
}
5 changes: 5 additions & 0 deletions example/src/index.tsx
Expand Up @@ -6,6 +6,7 @@ import Basic from './Basic';
import UpdateNode from './UpdateNode';
import Stress from './Stress';
import CustomNode from './CustomNode';
import FloatingEdges from './FloatingEdges';

import './index.css';

Expand All @@ -26,6 +27,10 @@ const routes = [
path: '/custom-node',
component: CustomNode,
},
{
path: '/floating-edges',
component: FloatingEdges,
},
];

const Header = withRouter(({ history, location }) => {
Expand Down
7 changes: 2 additions & 5 deletions example/src_oldapi/Edges/CustomEdge.tsx
@@ -1,5 +1,5 @@
import { FC } from 'react';
import { EdgeProps, getBezierPath, getMarkerEnd } from 'react-flow-renderer';
import { EdgeProps, getBezierPath } from 'react-flow-renderer';

const CustomEdge: FC<EdgeProps> = ({
id,
Expand All @@ -10,15 +10,12 @@ const CustomEdge: FC<EdgeProps> = ({
sourcePosition,
targetPosition,
data,
arrowHeadType,
markerEndId,
}) => {
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);

return (
<>
<path id={id} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} />
<path id={id} className="react-flow__edge-path" d={edgePath} />
<text>
<textPath href={`#${id}`} style={{ fontSize: '12px' }} startOffset="50%" textAnchor="middle">
{data.text}
Expand Down
7 changes: 2 additions & 5 deletions example/src_oldapi/Edges/CustomEdge2.tsx
@@ -1,5 +1,5 @@
import { FC } from 'react';
import { EdgeProps, getBezierPath, getMarkerEnd, EdgeText, getEdgeCenter } from 'react-flow-renderer';
import { EdgeProps, getBezierPath, EdgeText, getEdgeCenter } from 'react-flow-renderer';

const CustomEdge: FC<EdgeProps> = ({
id,
Expand All @@ -10,11 +10,8 @@ const CustomEdge: FC<EdgeProps> = ({
sourcePosition,
targetPosition,
data,
arrowHeadType,
markerEndId,
}) => {
const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition });
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
const [centerX, centerY] = getEdgeCenter({
sourceX,
sourceY,
Expand All @@ -24,7 +21,7 @@ const CustomEdge: FC<EdgeProps> = ({

return (
<>
<path id={id} className="react-flow__edge-path" d={edgePath} markerEnd={markerEnd} />
<path id={id} className="react-flow__edge-path" d={edgePath} />
<EdgeText
x={centerX}
y={centerY}
Expand Down

0 comments on commit a0b8aa6

Please sign in to comment.