Skip to content

Commit

Permalink
refactor(floating-edge-example): cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
moklick committed Oct 14, 2021
1 parent a50f9c6 commit ab2391f
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 250 deletions.
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.

36 changes: 36 additions & 0 deletions example/src/FloatingEdges/FloatingConnectionLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { FC } from 'react';
import { getBezierPath, ConnectionLineComponentProps } from 'react-flow-renderer';

import { getArrow } from './utils';

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

const targetNode = { __rf: { width: 1, height: 1, position: { x: targetX, y: targetY } } };
const { sx, sy } = getArrow(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;
35 changes: 35 additions & 0 deletions example/src/FloatingEdges/FloatingEdge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { FC, useMemo, CSSProperties } from 'react';
import { EdgeProps, getMarkerEnd, useStoreState, getBezierPath } from 'react-flow-renderer';

import { getArrow } from './utils';

const FloatingEdge: FC<EdgeProps> = ({ id, source, target, arrowHeadType, markerEndId, style }) => {
const nodes = useStoreState((state) => state.nodes);
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);

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 } = getArrow(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} markerEnd={markerEnd} style={style as CSSProperties} />
</g>
);
};

export default FloatingEdge;
52 changes: 52 additions & 0 deletions example/src/FloatingEdges/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useState } from 'react';

import ReactFlow, {
removeElements,
addEdge,
MiniMap,
Controls,
Background,
OnLoadParams,
EdgeTypesType,
Elements,
Connection,
Edge,
ArrowHeadType,
} from 'react-flow-renderer';

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

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

const initialElements: Elements = createElements();

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

const NodeAsHandleFlow = () => {
const [elements, setElements] = useState<Elements>(initialElements);

const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) =>
setElements((els) => addEdge({ ...params, type: 'floating', arrowHeadType: ArrowHeadType.Arrow }, els));

return (
<ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
onLoad={onLoad}
edgeTypes={edgeTypes}
connectionLineComponent={FloatingConnectionLine}
>
<MiniMap />
<Controls />
<Background />
</ReactFlow>
);
};

export default NodeAsHandleFlow;
94 changes: 94 additions & 0 deletions example/src/FloatingEdges/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Position, ArrowHeadType, Node } from 'react-flow-renderer';

function getNodeIntersection(source: Node, target: Node) {
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
const { width: sourceWidth, height: sourceHeight, position: sourcePosition } = source.__rf;
const targetPosition = target.__rf.position;

const w = sourceWidth / 2;
const h = sourceHeight / 2;

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

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];
}

function getEdgePosition(node: Node, x: number, y: number) {
const n = { ...node.__rf.position, ...node.__rf };
const nx = Math.round(n.x);
const ny = Math.round(n.y);
const px = Math.round(x);
const py = Math.round(y);

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

return Position.Top;
}

export function getArrow(source: any, target: any) {
const [sx, sy] = getNodeIntersection(source, target);
const [tx, ty] = getNodeIntersection(target, source);

const sourcePos = getEdgePosition(source, sx, sy);
const targetPos = getEdgePosition(target, tx, ty);

return {
sx,
sy,
tx,
ty,
label: null,
sourcePos,
targetPos,
};
}

export function createElements() {
const elements = [];
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };

elements.push({ id: 'source', data: { label: 'Source', isTarget: false }, 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;

elements.push({ id: `${i}`, data: { label: 'Target', isTarget: true }, position: { x, y } });

if (i % 2 === 0) {
elements.push({
id: `source-${i}`,
source: 'source',
target: `${i}`,
type: 'floating',
arrowHeadType: ArrowHeadType.Arrow,
});
}
}

return elements;
}
163 changes: 0 additions & 163 deletions example/src/NodeAsHandle/CustomEdge.tsx

This file was deleted.

Loading

0 comments on commit ab2391f

Please sign in to comment.