feat(connectionline): custom component option #516
This commit is contained in:
@@ -123,6 +123,7 @@ const BasicFlow = () => <ReactFlow elements={elements} />;
|
|||||||
#### Connection Line Options
|
#### Connection Line Options
|
||||||
- `connectionLineType`: connection line type = `default` (bezier), `straight`, `step`, `smoothstep`
|
- `connectionLineType`: connection line type = `default` (bezier), `straight`, `step`, `smoothstep`
|
||||||
- `connectionLineStyle`: connection style as svg attributes
|
- `connectionLineStyle`: connection style as svg attributes
|
||||||
|
- `connectionLineComponent`: [custom connection line component](/example/src/CustomConnectionLin/index.js)
|
||||||
|
|
||||||
#### Keys
|
#### Keys
|
||||||
- `deleteKeyCode`: default: `8` (delete)
|
- `deleteKeyCode`: default: `8` (delete)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default ({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition,
|
||||||
|
connectionLineType,
|
||||||
|
connectionLineStyle,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="#222"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
className="animated"
|
||||||
|
d={`M${sourceX},${sourceY} C ${sourceX} ${targetY} ${sourceX} ${targetY} ${targetX},${targetY}`}
|
||||||
|
/>
|
||||||
|
<circle cx={targetX} cy={targetY} fill="#fff" r={3} stroke="#222" strokeWidth={1.5} />
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import ReactFlow, { removeElements, addEdge, Background } from 'react-flow-renderer';
|
||||||
|
|
||||||
|
import ConnectionLine from './ConnectionLine';
|
||||||
|
|
||||||
|
const initialElements = [{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
|
||||||
|
|
||||||
|
const ConnectionLineFlow = () => {
|
||||||
|
const [elements, setElements] = useState(initialElements);
|
||||||
|
const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
|
||||||
|
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactFlow
|
||||||
|
elements={elements}
|
||||||
|
connectionLineComponent={ConnectionLine}
|
||||||
|
onElementsRemove={onElementsRemove}
|
||||||
|
onConnect={onConnect}
|
||||||
|
>
|
||||||
|
<Background variant="lines" />
|
||||||
|
</ReactFlow>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConnectionLineFlow;
|
||||||
@@ -14,6 +14,7 @@ import Horizontal from './Horizontal';
|
|||||||
import Provider from './Provider';
|
import Provider from './Provider';
|
||||||
import Hidden from './Hidden';
|
import Hidden from './Hidden';
|
||||||
import EdgeTypes from './EdgeTypes';
|
import EdgeTypes from './EdgeTypes';
|
||||||
|
import CustomConnectionLine from './CustomConnectionLine';
|
||||||
|
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
@@ -74,6 +75,10 @@ const routes = [
|
|||||||
path: '/edge-types',
|
path: '/edge-types',
|
||||||
component: EdgeTypes,
|
component: EdgeTypes,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/custom-connectionline',
|
||||||
|
component: CustomConnectionLine,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const navLinks = routes.filter((route) => route.label);
|
const navLinks = routes.filter((route) => route.label);
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import React, { useEffect, useState, CSSProperties } from 'react';
|
import React, { useEffect, useState, CSSProperties } from 'react';
|
||||||
import cc from 'classcat';
|
|
||||||
|
|
||||||
import { getBezierPath } from '../Edges/BezierEdge';
|
import { getBezierPath } from '../Edges/BezierEdge';
|
||||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||||
import { ElementId, Node, Transform, HandleElement, Position, ConnectionLineType, HandleType } from '../../types';
|
import {
|
||||||
|
ElementId,
|
||||||
|
Node,
|
||||||
|
Transform,
|
||||||
|
HandleElement,
|
||||||
|
Position,
|
||||||
|
ConnectionLineType,
|
||||||
|
ConnectionLineComponent,
|
||||||
|
HandleType,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
interface ConnectionLineProps {
|
interface ConnectionLineProps {
|
||||||
connectionNodeId: ElementId;
|
connectionNodeId: ElementId;
|
||||||
@@ -15,7 +23,7 @@ interface ConnectionLineProps {
|
|||||||
transform: Transform;
|
transform: Transform;
|
||||||
isConnectable: boolean;
|
isConnectable: boolean;
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
className?: string;
|
CustomConnectionLineComponent?: ConnectionLineComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
@@ -26,9 +34,9 @@ export default ({
|
|||||||
connectionPositionY,
|
connectionPositionY,
|
||||||
connectionLineType = ConnectionLineType.Bezier,
|
connectionLineType = ConnectionLineType.Bezier,
|
||||||
nodes = [],
|
nodes = [],
|
||||||
className,
|
|
||||||
transform,
|
transform,
|
||||||
isConnectable,
|
isConnectable,
|
||||||
|
CustomConnectionLineComponent,
|
||||||
}: ConnectionLineProps) => {
|
}: ConnectionLineProps) => {
|
||||||
const [sourceNode, setSourceNode] = useState<Node | null>(null);
|
const [sourceNode, setSourceNode] = useState<Node | null>(null);
|
||||||
const hasHandleId = connectionNodeId.includes('__');
|
const hasHandleId = connectionNodeId.includes('__');
|
||||||
@@ -45,8 +53,6 @@ export default ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectionLineClasses: string = cc(['react-flow__connection', className]);
|
|
||||||
|
|
||||||
const sourceHandle = handleId
|
const sourceHandle = handleId
|
||||||
? sourceNode.__rf.handleBounds[connectionHandleType].find((d: HandleElement) => d.id === handleId)
|
? sourceNode.__rf.handleBounds[connectionHandleType].find((d: HandleElement) => d.id === handleId)
|
||||||
: sourceNode.__rf.handleBounds[connectionHandleType][0];
|
: sourceNode.__rf.handleBounds[connectionHandleType][0];
|
||||||
@@ -55,14 +61,31 @@ export default ({
|
|||||||
const sourceX = sourceNode.__rf.position.x + sourceHandleX;
|
const sourceX = sourceNode.__rf.position.x + sourceHandleX;
|
||||||
const sourceY = sourceNode.__rf.position.y + sourceHandleY;
|
const sourceY = sourceNode.__rf.position.y + sourceHandleY;
|
||||||
|
|
||||||
const targetX = (connectionPositionX - transform[0]) * (1 / transform[2]);
|
const targetX = (connectionPositionX - transform[0]) / transform[2];
|
||||||
const targetY = (connectionPositionY - transform[1]) * (1 / transform[2]);
|
const targetY = (connectionPositionY - transform[1]) / transform[2];
|
||||||
|
|
||||||
let dAttr: string = '';
|
|
||||||
|
|
||||||
const isRightOrLeft = sourceHandle?.position === Position.Left || sourceHandle?.position === Position.Right;
|
const isRightOrLeft = sourceHandle?.position === Position.Left || sourceHandle?.position === Position.Right;
|
||||||
const targetPosition = isRightOrLeft ? Position.Left : Position.Top;
|
const targetPosition = isRightOrLeft ? Position.Left : Position.Top;
|
||||||
|
|
||||||
|
if (CustomConnectionLineComponent) {
|
||||||
|
return (
|
||||||
|
<g className="react-flow__connection">
|
||||||
|
<CustomConnectionLineComponent
|
||||||
|
sourceX={sourceX}
|
||||||
|
sourceY={sourceY}
|
||||||
|
sourcePosition={sourceHandle?.position}
|
||||||
|
targetX={targetX}
|
||||||
|
targetY={targetY}
|
||||||
|
targetPosition={targetPosition}
|
||||||
|
connectionLineType={connectionLineType}
|
||||||
|
connectionLineStyle={connectionLineStyle}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dAttr: string = '';
|
||||||
|
|
||||||
if (connectionLineType === ConnectionLineType.Bezier) {
|
if (connectionLineType === ConnectionLineType.Bezier) {
|
||||||
dAttr = getBezierPath({
|
dAttr = getBezierPath({
|
||||||
sourceX,
|
sourceX,
|
||||||
@@ -96,7 +119,7 @@ export default ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g className={connectionLineClasses}>
|
<g className="react-flow__connection">
|
||||||
<path d={dAttr} className="react-flow__connection-path" style={connectionLineStyle} />
|
<path d={dAttr} className="react-flow__connection-path" style={connectionLineStyle} />
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,17 @@ import { useStoreState } from '../../store/hooks';
|
|||||||
import ConnectionLine from '../../components/ConnectionLine/index';
|
import ConnectionLine from '../../components/ConnectionLine/index';
|
||||||
import { isEdge } from '../../utils/graph';
|
import { isEdge } from '../../utils/graph';
|
||||||
import MarkerDefinitions from './MarkerDefinitions';
|
import MarkerDefinitions from './MarkerDefinitions';
|
||||||
import { XYPosition, Position, Edge, Node, ElementId, HandleElement, Elements, ConnectionLineType } from '../../types';
|
import {
|
||||||
|
XYPosition,
|
||||||
|
Position,
|
||||||
|
Edge,
|
||||||
|
Node,
|
||||||
|
ElementId,
|
||||||
|
HandleElement,
|
||||||
|
Elements,
|
||||||
|
ConnectionLineType,
|
||||||
|
ConnectionLineComponent,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
interface EdgeRendererProps {
|
||||||
edgeTypes: any;
|
edgeTypes: any;
|
||||||
@@ -13,6 +23,7 @@ interface EdgeRendererProps {
|
|||||||
onElementClick?: (event: React.MouseEvent, element: Node | Edge) => void;
|
onElementClick?: (event: React.MouseEvent, element: Node | Edge) => void;
|
||||||
arrowHeadColor: string;
|
arrowHeadColor: string;
|
||||||
markerEndId?: string;
|
markerEndId?: string;
|
||||||
|
connectionLineComponent?: ConnectionLineComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EdgePositions {
|
interface EdgePositions {
|
||||||
@@ -193,19 +204,19 @@ function renderEdge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EdgeRenderer = (props: EdgeRendererProps) => {
|
const EdgeRenderer = (props: EdgeRendererProps) => {
|
||||||
const [tX, tY, tScale] = useStoreState((s) => s.transform);
|
const [tX, tY, tScale] = useStoreState((state) => state.transform);
|
||||||
const edges = useStoreState((s) => s.edges);
|
const edges = useStoreState((state) => state.edges);
|
||||||
const nodes = useStoreState((s) => s.nodes);
|
const nodes = useStoreState((state) => state.nodes);
|
||||||
const connectionNodeId = useStoreState((s) => s.connectionNodeId);
|
const connectionNodeId = useStoreState((state) => state.connectionNodeId);
|
||||||
const connectionHandleType = useStoreState((s) => s.connectionHandleType);
|
const connectionHandleType = useStoreState((state) => state.connectionHandleType);
|
||||||
const connectionPosition = useStoreState((s) => s.connectionPosition);
|
const connectionPosition = useStoreState((state) => state.connectionPosition);
|
||||||
const selectedElements = useStoreState((s) => s.selectedElements);
|
const selectedElements = useStoreState((state) => state.selectedElements);
|
||||||
const nodesConnectable = useStoreState((s) => s.nodesConnectable);
|
const nodesConnectable = useStoreState((state) => state.nodesConnectable);
|
||||||
const elementsSelectable = useStoreState((s) => s.elementsSelectable);
|
const elementsSelectable = useStoreState((state) => state.elementsSelectable);
|
||||||
const width = useStoreState((state) => state.width);
|
const width = useStoreState((state) => state.width);
|
||||||
const height = useStoreState((state) => state.height);
|
const height = useStoreState((state) => state.height);
|
||||||
|
|
||||||
const { connectionLineStyle, connectionLineType, arrowHeadColor } = props;
|
const { connectionLineStyle, connectionLineType, arrowHeadColor, connectionLineComponent } = props;
|
||||||
|
|
||||||
if (!width) {
|
if (!width) {
|
||||||
return null;
|
return null;
|
||||||
@@ -218,7 +229,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
<svg width={width} height={height} className="react-flow__edges">
|
<svg width={width} height={height} className="react-flow__edges">
|
||||||
<MarkerDefinitions color={arrowHeadColor} />
|
<MarkerDefinitions color={arrowHeadColor} />
|
||||||
<g transform={transformStyle}>
|
<g transform={transformStyle}>
|
||||||
{edges.map((edge) => renderEdge(edge, props, nodes, selectedElements, elementsSelectable))}
|
{edges.map((edge: Edge) => renderEdge(edge, props, nodes, selectedElements, elementsSelectable))}
|
||||||
{renderConnectionLine && (
|
{renderConnectionLine && (
|
||||||
<ConnectionLine
|
<ConnectionLine
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
@@ -230,6 +241,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
|
|||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
isConnectable={nodesConnectable}
|
isConnectable={nodesConnectable}
|
||||||
|
CustomConnectionLineComponent={connectionLineComponent}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
Edge,
|
Edge,
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionLineType,
|
ConnectionLineType,
|
||||||
|
ConnectionLineComponent,
|
||||||
FlowTransform,
|
FlowTransform,
|
||||||
OnConnectStartFunc,
|
OnConnectStartFunc,
|
||||||
OnConnectStopFunc,
|
OnConnectStopFunc,
|
||||||
@@ -57,6 +58,7 @@ export interface GraphViewProps {
|
|||||||
edgeTypes: EdgeTypesType;
|
edgeTypes: EdgeTypesType;
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
|
connectionLineComponent?: ConnectionLineComponent;
|
||||||
deleteKeyCode: number;
|
deleteKeyCode: number;
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean;
|
||||||
snapGrid: [number, number];
|
snapGrid: [number, number];
|
||||||
@@ -97,6 +99,7 @@ const GraphView = ({
|
|||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionLineStyle,
|
connectionLineStyle,
|
||||||
|
connectionLineComponent,
|
||||||
selectionKeyCode,
|
selectionKeyCode,
|
||||||
onElementsRemove,
|
onElementsRemove,
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
@@ -289,6 +292,7 @@ const GraphView = ({
|
|||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
arrowHeadColor={arrowHeadColor}
|
arrowHeadColor={arrowHeadColor}
|
||||||
markerEndId={markerEndId}
|
markerEndId={markerEndId}
|
||||||
|
connectionLineComponent={connectionLineComponent}
|
||||||
/>
|
/>
|
||||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||||
{nodesSelectionActive && (
|
{nodesSelectionActive && (
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
Edge,
|
Edge,
|
||||||
Connection,
|
Connection,
|
||||||
ConnectionLineType,
|
ConnectionLineType,
|
||||||
|
ConnectionLineComponent,
|
||||||
FlowTransform,
|
FlowTransform,
|
||||||
OnConnectStartFunc,
|
OnConnectStartFunc,
|
||||||
OnConnectStopFunc,
|
OnConnectStopFunc,
|
||||||
@@ -65,6 +66,7 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
edgeTypes: EdgeTypesType;
|
edgeTypes: EdgeTypesType;
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType;
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties;
|
||||||
|
connectionLineComponent?: ConnectionLineComponent;
|
||||||
deleteKeyCode: number;
|
deleteKeyCode: number;
|
||||||
selectionKeyCode: number;
|
selectionKeyCode: number;
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean;
|
||||||
@@ -116,6 +118,7 @@ const ReactFlow = ({
|
|||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
connectionLineType,
|
connectionLineType,
|
||||||
connectionLineStyle,
|
connectionLineStyle,
|
||||||
|
connectionLineComponent,
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
selectionKeyCode,
|
selectionKeyCode,
|
||||||
snapToGrid,
|
snapToGrid,
|
||||||
@@ -162,6 +165,7 @@ const ReactFlow = ({
|
|||||||
edgeTypes={edgeTypesParsed}
|
edgeTypes={edgeTypesParsed}
|
||||||
connectionLineType={connectionLineType}
|
connectionLineType={connectionLineType}
|
||||||
connectionLineStyle={connectionLineStyle}
|
connectionLineStyle={connectionLineStyle}
|
||||||
|
connectionLineComponent={connectionLineComponent}
|
||||||
selectionKeyCode={selectionKeyCode}
|
selectionKeyCode={selectionKeyCode}
|
||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
deleteKeyCode={deleteKeyCode}
|
deleteKeyCode={deleteKeyCode}
|
||||||
|
|||||||
+7
-1
@@ -78,6 +78,11 @@
|
|||||||
|
|
||||||
.react-flow__connection {
|
.react-flow__connection {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
|
.animated {
|
||||||
|
stroke-dasharray: 5;
|
||||||
|
animation: dashdraw 0.5s linear infinite;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__connection-path {
|
.react-flow__connection-path {
|
||||||
@@ -117,6 +122,7 @@
|
|||||||
.react-flow__node-default.selectable,
|
.react-flow__node-default.selectable,
|
||||||
.react-flow__node-input.selectable,
|
.react-flow__node-input.selectable,
|
||||||
.react-flow__node-output.selectable {
|
.react-flow__node-output.selectable {
|
||||||
|
|
||||||
&.selected,
|
&.selected,
|
||||||
&.selected:hover {
|
&.selected:hover {
|
||||||
box-shadow: 0 0 0 1px #333;
|
box-shadow: 0 0 0 1px #333;
|
||||||
@@ -193,4 +199,4 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(0, -50%);
|
transform: translate(0, -50%);
|
||||||
}
|
}
|
||||||
@@ -248,6 +248,19 @@ export enum ConnectionLineType {
|
|||||||
SmoothStep = 'smoothstep',
|
SmoothStep = 'smoothstep',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ConnectionLineComponentProps = {
|
||||||
|
sourceX: number;
|
||||||
|
sourceY: number;
|
||||||
|
sourcePosition?: Position;
|
||||||
|
targetX: number;
|
||||||
|
targetY: number;
|
||||||
|
targetPosition?: Position;
|
||||||
|
connectionLineStyle?: CSSProperties;
|
||||||
|
connectionLineType: ConnectionLineType;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConnectionLineComponent = React.ComponentType<ConnectionLineComponentProps>;
|
||||||
|
|
||||||
export type OnConnectFunc = (connection: Connection) => void;
|
export type OnConnectFunc = (connection: Connection) => void;
|
||||||
export type OnConnectStartParams = {
|
export type OnConnectStartParams = {
|
||||||
nodeId: ElementId | null;
|
nodeId: ElementId | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user