refactor(floating-edges-example): comments and cleanup

This commit is contained in:
moklick
2021-10-14 12:29:05 +02:00
parent ab2391fd51
commit ceb96201de
5 changed files with 74 additions and 55 deletions
@@ -1,7 +1,7 @@
import { FC } from 'react'; import { FC } from 'react';
import { getBezierPath, ConnectionLineComponentProps } from 'react-flow-renderer'; import { getBezierPath, ConnectionLineComponentProps, Node } from 'react-flow-renderer';
import { getArrow } from './utils'; import { getEdgeParams } from './utils';
const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({ const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({
targetX, targetX,
@@ -14,8 +14,12 @@ const FloatingConnectionLine: FC<ConnectionLineComponentProps> = ({
return null; return null;
} }
const targetNode = { __rf: { width: 1, height: 1, position: { x: targetX, y: targetY } } }; const targetNode = {
const { sx, sy } = getArrow(sourceNode, targetNode); id: 'connection-target',
__rf: { width: 1, height: 1, position: { x: targetX, y: targetY } },
} as Node;
const { sx, sy } = getEdgeParams(sourceNode, targetNode);
const d = getBezierPath({ const d = getBezierPath({
sourceX: sx, sourceX: sx,
sourceY: sy, sourceY: sy,
+2 -2
View File
@@ -1,7 +1,7 @@
import { FC, useMemo, CSSProperties } from 'react'; import { FC, useMemo, CSSProperties } from 'react';
import { EdgeProps, getMarkerEnd, useStoreState, getBezierPath } from 'react-flow-renderer'; import { EdgeProps, getMarkerEnd, useStoreState, getBezierPath } from 'react-flow-renderer';
import { getArrow } from './utils'; import { getEdgeParams } from './utils';
const FloatingEdge: FC<EdgeProps> = ({ id, source, target, arrowHeadType, markerEndId, style }) => { const FloatingEdge: FC<EdgeProps> = ({ id, source, target, arrowHeadType, markerEndId, style }) => {
const nodes = useStoreState((state) => state.nodes); const nodes = useStoreState((state) => state.nodes);
@@ -14,7 +14,7 @@ const FloatingEdge: FC<EdgeProps> = ({ id, source, target, arrowHeadType, marker
return null; return null;
} }
const { sx, sy, tx, ty, sourcePos, targetPos } = getArrow(sourceNode, targetNode); const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode);
const d = getBezierPath({ const d = getBezierPath({
sourceX: sx, sourceX: sx,
+15 -14
View File
@@ -3,8 +3,6 @@ import React, { useState } from 'react';
import ReactFlow, { import ReactFlow, {
removeElements, removeElements,
addEdge, addEdge,
MiniMap,
Controls,
Background, Background,
OnLoadParams, OnLoadParams,
EdgeTypesType, EdgeTypesType,
@@ -14,6 +12,8 @@ import ReactFlow, {
ArrowHeadType, ArrowHeadType,
} from 'react-flow-renderer'; } from 'react-flow-renderer';
import './style.css';
import FloatingEdge from './FloatingEdge'; import FloatingEdge from './FloatingEdge';
import FloatingConnectionLine from './FloatingConnectionLine'; import FloatingConnectionLine from './FloatingConnectionLine';
import { createElements } from './utils'; import { createElements } from './utils';
@@ -30,22 +30,23 @@ const NodeAsHandleFlow = () => {
const [elements, setElements] = useState<Elements>(initialElements); const [elements, setElements] = useState<Elements>(initialElements);
const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Connection | Edge) => const onConnect = (params: Connection | Edge) =>
setElements((els) => addEdge({ ...params, type: 'floating', arrowHeadType: ArrowHeadType.Arrow }, els)); setElements((els) => addEdge({ ...params, type: 'floating', arrowHeadType: ArrowHeadType.Arrow }, els));
return ( return (
<ReactFlow <div className="floatingedges">
elements={elements} <ReactFlow
onElementsRemove={onElementsRemove} elements={elements}
onConnect={onConnect} onElementsRemove={onElementsRemove}
onLoad={onLoad} onConnect={onConnect}
edgeTypes={edgeTypes} onLoad={onLoad}
connectionLineComponent={FloatingConnectionLine} edgeTypes={edgeTypes}
> connectionLineComponent={FloatingConnectionLine}
<MiniMap /> >
<Controls /> <Background />
<Background /> </ReactFlow>
</ReactFlow> </div>
); );
}; };
+9
View File
@@ -0,0 +1,9 @@
.floatingedges {
flex-direction: column;
display: flex;
height: 100%;
}
.floatingedges .react-flow__handle {
opacity: 0;
}
+40 -35
View File
@@ -1,17 +1,23 @@
import { Position, ArrowHeadType, Node } from 'react-flow-renderer'; import { Position, ArrowHeadType, Node, XYPosition } from 'react-flow-renderer';
function getNodeIntersection(source: Node, target: Node) { // 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 // 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 {
const targetPosition = target.__rf.position; width: intersectionNodeWidth,
height: intersectionNodeHeight,
position: intersectionNodePosition,
} = intersectionNode.__rf;
const targetPosition = targetNode.__rf.position;
const w = sourceWidth / 2; const w = intersectionNodeWidth / 2;
const h = sourceHeight / 2; const h = intersectionNodeHeight / 2;
const x2 = sourcePosition.x + w; const x2 = intersectionNodePosition.x + w;
const y2 = sourcePosition.y + h; const y2 = intersectionNodePosition.y + h;
const x1 = targetPosition.x; const x1 = targetPosition.x + w;
const y1 = targetPosition.y; const y1 = targetPosition.y + h;
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h); const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h); const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
@@ -21,15 +27,16 @@ function getNodeIntersection(source: Node, target: Node) {
const x = w * (xx3 + yy3) + x2; const x = w * (xx3 + yy3) + x2;
const y = h * (-xx3 + yy3) + y2; const y = h * (-xx3 + yy3) + y2;
return [x, y]; return { x, y };
} }
function getEdgePosition(node: Node, x: number, y: number) { // returns the position (top,right,bottom or right) passed node compared to the intersection point
function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
const n = { ...node.__rf.position, ...node.__rf }; const n = { ...node.__rf.position, ...node.__rf };
const nx = Math.round(n.x); const nx = Math.round(n.x);
const ny = Math.round(n.y); const ny = Math.round(n.y);
const px = Math.round(x); const px = Math.round(intersectionPoint.x);
const py = Math.round(y); const py = Math.round(intersectionPoint.y);
if (px <= nx + 1) { if (px <= nx + 1) {
return Position.Left; return Position.Left;
@@ -47,19 +54,19 @@ function getEdgePosition(node: Node, x: number, y: number) {
return Position.Top; return Position.Top;
} }
export function getArrow(source: any, target: any) { // returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
const [sx, sy] = getNodeIntersection(source, target); export function getEdgeParams(source: Node, target: Node) {
const [tx, ty] = getNodeIntersection(target, source); const sourceIntersectionPoint = getNodeIntersection(source, target);
const targetIntersectionPoint = getNodeIntersection(target, source);
const sourcePos = getEdgePosition(source, sx, sy); const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
const targetPos = getEdgePosition(target, tx, ty); const targetPos = getEdgePosition(target, targetIntersectionPoint);
return { return {
sx, sx: sourceIntersectionPoint.x,
sy, sy: sourceIntersectionPoint.y,
tx, tx: targetIntersectionPoint.x,
ty, ty: targetIntersectionPoint.y,
label: null,
sourcePos, sourcePos,
targetPos, targetPos,
}; };
@@ -69,7 +76,7 @@ export function createElements() {
const elements = []; const elements = [];
const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 }; const center = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
elements.push({ id: 'source', data: { label: 'Source', isTarget: false }, position: center }); elements.push({ id: 'target', data: { label: 'Target' }, position: center });
for (let i = 0; i < 8; i++) { for (let i = 0; i < 8; i++) {
const degrees = i * (360 / 8); const degrees = i * (360 / 8);
@@ -77,17 +84,15 @@ export function createElements() {
const x = 250 * Math.cos(radians) + center.x; const x = 250 * Math.cos(radians) + center.x;
const y = 250 * Math.sin(radians) + center.y; const y = 250 * Math.sin(radians) + center.y;
elements.push({ id: `${i}`, data: { label: 'Target', isTarget: true }, position: { x, y } }); elements.push({ id: `${i}`, data: { label: 'Source' }, position: { x, y } });
if (i % 2 === 0) { elements.push({
elements.push({ id: `edge-${i}`,
id: `source-${i}`, target: 'target',
source: 'source', source: `${i}`,
target: `${i}`, type: 'floating',
type: 'floating', arrowHeadType: ArrowHeadType.Arrow,
arrowHeadType: ArrowHeadType.Arrow, });
});
}
} }
return elements; return elements;