From d3b1148bcab61d7bf925fa9b82b5a1b73d43b264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20M=C3=B6ller?= Date: Thu, 14 Oct 2021 17:53:50 +0200 Subject: [PATCH] feat(markers): implement more generic api for markerEnd and markerStart handling --- example/src/Edges/CustomEdge.tsx | 7 +- example/src/Edges/CustomEdge2.tsx | 7 +- example/src/Edges/index.tsx | 15 ++- example/src/FloatingEdges/FloatingEdge.tsx | 7 +- example/src/FloatingEdges/index.tsx | 4 +- example/src/Overview/index.tsx | 3 +- example/src/Undirectional/index.tsx | 13 -- src/components/Edges/BezierEdge.tsx | 16 ++- src/components/Edges/SmoothStepEdge.tsx | 20 +-- src/components/Edges/StraightEdge.tsx | 7 +- src/components/Edges/wrapEdge.tsx | 24 +++- .../EdgeRenderer/MarkerDefinitions.tsx | 126 ++++++++++++------ src/container/EdgeRenderer/index.tsx | 11 +- src/container/FlowRenderer/index.tsx | 1 + src/container/GraphView/index.tsx | 8 +- src/container/ReactFlow/index.tsx | 9 +- src/types/index.ts | 18 ++- src/utils/graph.ts | 16 +++ 18 files changed, 192 insertions(+), 120 deletions(-) diff --git a/example/src/Edges/CustomEdge.tsx b/example/src/Edges/CustomEdge.tsx index f72ff831..9a3c4230 100644 --- a/example/src/Edges/CustomEdge.tsx +++ b/example/src/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 = ({ id, @@ -10,15 +10,12 @@ const CustomEdge: FC = ({ sourcePosition, targetPosition, data, - arrowHeadType, - markerEndId, }) => { const edgePath = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }); - const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); return ( <> - + {data.text} diff --git a/example/src/Edges/CustomEdge2.tsx b/example/src/Edges/CustomEdge2.tsx index 361b7f92..63b010a9 100644 --- a/example/src/Edges/CustomEdge2.tsx +++ b/example/src/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 = ({ id, @@ -10,11 +10,8 @@ const CustomEdge: FC = ({ 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, @@ -24,7 +21,7 @@ const CustomEdge: FC = ({ return ( <> - + ), labelStyle: { fill: 'red', fontWeight: 700 }, - arrowHeadType: ArrowHeadType.Arrow, + style: { stroke: '#ffcc00' }, + markerEnd: { + type: ArrowHeadType.ArrowClosed, + }, + markerStart: { + type: ArrowHeadType.Arrow, + color: '#FFCC00', + }, }, { id: 'e5-7', @@ -64,7 +71,10 @@ const initialElements: Elements = [ labelBgPadding: [8, 4], labelBgBorderRadius: 4, labelBgStyle: { fill: '#FFCC00', color: '#fff', fillOpacity: 0.7 }, - arrowHeadType: ArrowHeadType.ArrowClosed, + markerEnd: { + type: ArrowHeadType.ArrowClosed, + color: '#FFCC00', + }, }, { id: 'e5-8', @@ -72,7 +82,6 @@ const initialElements: Elements = [ target: '8', type: 'custom', data: { text: 'custom edge' }, - arrowHeadType: ArrowHeadType.ArrowClosed, }, { id: 'e5-9', diff --git a/example/src/FloatingEdges/FloatingEdge.tsx b/example/src/FloatingEdges/FloatingEdge.tsx index 60ee4b93..b03241d1 100644 --- a/example/src/FloatingEdges/FloatingEdge.tsx +++ b/example/src/FloatingEdges/FloatingEdge.tsx @@ -1,11 +1,10 @@ import { FC, useMemo, CSSProperties } from 'react'; -import { EdgeProps, getMarkerEnd, useStoreState, getBezierPath } from 'react-flow-renderer'; +import { EdgeProps, useStoreState, getBezierPath } from 'react-flow-renderer'; import { getEdgeParams } from './utils'; -const FloatingEdge: FC = ({ id, source, target, arrowHeadType, markerEndId, style }) => { +const FloatingEdge: FC = ({ id, source, target, 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]); @@ -27,7 +26,7 @@ const FloatingEdge: FC = ({ id, source, target, arrowHeadType, marker return ( - + ); }; diff --git a/example/src/FloatingEdges/index.tsx b/example/src/FloatingEdges/index.tsx index 7bdfc7a2..f1902515 100644 --- a/example/src/FloatingEdges/index.tsx +++ b/example/src/FloatingEdges/index.tsx @@ -9,7 +9,6 @@ import ReactFlow, { Elements, Connection, Edge, - ArrowHeadType, } from 'react-flow-renderer'; import './style.css'; @@ -31,8 +30,7 @@ const NodeAsHandleFlow = () => { const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => - setElements((els) => addEdge({ ...params, type: 'floating', arrowHeadType: ArrowHeadType.Arrow }, els)); + const onConnect = (params: Connection | Edge) => setElements((els) => addEdge({ ...params, type: 'floating' }, els)); return (
diff --git a/example/src/Overview/index.tsx b/example/src/Overview/index.tsx index ee13f916..17e0c9b5 100644 --- a/example/src/Overview/index.tsx +++ b/example/src/Overview/index.tsx @@ -13,7 +13,6 @@ import ReactFlow, { OnLoadParams, FlowTransform, SnapGrid, - ArrowHeadType, Connection, Edge, } from 'react-flow-renderer'; @@ -125,7 +124,7 @@ const initialElements: Elements = [ { id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' }, { id: 'e1-3', source: '1', target: '3' }, { id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' }, - { id: 'e4-5', source: '4', target: '5', arrowHeadType: ArrowHeadType.Arrow, label: 'edge with arrow head' }, + { id: 'e4-5', source: '4', target: '5', label: 'edge with arrow head' }, { id: 'e5-6', source: '5', target: '6', type: 'smoothstep', label: 'smooth step edge' }, { id: 'e5-7', diff --git a/example/src/Undirectional/index.tsx b/example/src/Undirectional/index.tsx index 9f51296a..7687f4b0 100644 --- a/example/src/Undirectional/index.tsx +++ b/example/src/Undirectional/index.tsx @@ -12,7 +12,6 @@ import ReactFlow, { ConnectionLineType, ConnectionMode, updateEdge, - ArrowHeadType, } from 'react-flow-renderer'; import CustomNode from './CustomNode'; @@ -69,7 +68,6 @@ const initialElements: Elements = [ sourceHandle: 'left', targetHandle: 'bottom', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-1b', @@ -78,7 +76,6 @@ const initialElements: Elements = [ sourceHandle: 'top', targetHandle: 'right', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-2a', @@ -87,7 +84,6 @@ const initialElements: Elements = [ sourceHandle: 'top', targetHandle: 'left', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-2b', @@ -96,7 +92,6 @@ const initialElements: Elements = [ sourceHandle: 'right', targetHandle: 'bottom', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-3a', @@ -105,7 +100,6 @@ const initialElements: Elements = [ sourceHandle: 'right', targetHandle: 'top', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-3b', @@ -114,7 +108,6 @@ const initialElements: Elements = [ sourceHandle: 'bottom', targetHandle: 'left', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-4a', @@ -123,7 +116,6 @@ const initialElements: Elements = [ sourceHandle: 'bottom', targetHandle: 'right', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-4b', @@ -132,7 +124,6 @@ const initialElements: Elements = [ sourceHandle: 'left', targetHandle: 'top', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-10', @@ -141,7 +132,6 @@ const initialElements: Elements = [ sourceHandle: 'top', targetHandle: 'bottom', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-20', @@ -150,7 +140,6 @@ const initialElements: Elements = [ sourceHandle: 'right', targetHandle: 'left', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-30', @@ -159,7 +148,6 @@ const initialElements: Elements = [ sourceHandle: 'bottom', targetHandle: 'top', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, { id: 'e0-40', @@ -168,7 +156,6 @@ const initialElements: Elements = [ sourceHandle: 'left', targetHandle: 'right', type: 'default', - arrowHeadType: ArrowHeadType.Arrow, }, ]; diff --git a/src/components/Edges/BezierEdge.tsx b/src/components/Edges/BezierEdge.tsx index b4c94197..2313767f 100644 --- a/src/components/Edges/BezierEdge.tsx +++ b/src/components/Edges/BezierEdge.tsx @@ -2,7 +2,7 @@ import React, { memo } from 'react'; import EdgeText from './EdgeText'; -import { getMarkerEnd, getCenter } from './utils'; +import { getCenter } from './utils'; import { EdgeProps, Position } from '../../types'; interface GetBezierPathParams { @@ -60,8 +60,8 @@ export default memo( labelBgPadding, labelBgBorderRadius, style, - arrowHeadType, - markerEndId, + markerEnd, + markerStart, }: EdgeProps) => { const [centerX, centerY] = getCenter({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }); const path = getBezierPath({ @@ -86,11 +86,15 @@ export default memo( /> ) : null; - const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); - return ( <> - + {text} ); diff --git a/src/components/Edges/SmoothStepEdge.tsx b/src/components/Edges/SmoothStepEdge.tsx index 0c42410a..3bb087dd 100644 --- a/src/components/Edges/SmoothStepEdge.tsx +++ b/src/components/Edges/SmoothStepEdge.tsx @@ -1,7 +1,7 @@ import React, { memo } from 'react'; import EdgeText from './EdgeText'; -import { getMarkerEnd, getCenter } from './utils'; +import { getCenter } from './utils'; import { EdgeSmoothStepProps, Position } from '../../types'; // These are some helper methods for drawing the round corners @@ -73,8 +73,8 @@ export function getSmoothStepPath({ sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize); secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize); - } else if (sourcePosition === Position.Right && targetPosition === Position.Left){ - // and sourceX > targetX + } else if (sourcePosition === Position.Right && targetPosition === Position.Left) { + // and sourceX > targetX firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize); secondCornerPath = @@ -126,8 +126,8 @@ export default memo( style, sourcePosition = Position.Bottom, targetPosition = Position.Top, - arrowHeadType, - markerEndId, + markerEnd, + markerStart, borderRadius = 5, }: EdgeSmoothStepProps) => { const [centerX, centerY] = getCenter({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }); @@ -142,8 +142,6 @@ export default memo( borderRadius, }); - const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); - const text = label ? ( - + {text} ); diff --git a/src/components/Edges/StraightEdge.tsx b/src/components/Edges/StraightEdge.tsx index 1bd3dedf..b7f0fac1 100644 --- a/src/components/Edges/StraightEdge.tsx +++ b/src/components/Edges/StraightEdge.tsx @@ -1,7 +1,6 @@ import React, { memo } from 'react'; import EdgeText from './EdgeText'; -import { getMarkerEnd } from './utils'; import { EdgeProps } from '../../types'; export default memo( @@ -17,15 +16,14 @@ export default memo( labelBgPadding, labelBgBorderRadius, style, - arrowHeadType, - markerEndId, + markerEnd, + markerStart, }: EdgeProps) => { const yOffset = Math.abs(targetY - sourceY) / 2; const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset; const xOffset = Math.abs(targetX - sourceX) / 2; const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset; - const markerEnd = getMarkerEnd(arrowHeadType, markerEndId); const text = label ? ( {text} diff --git a/src/components/Edges/wrapEdge.tsx b/src/components/Edges/wrapEdge.tsx index 60384e74..4e191cf7 100644 --- a/src/components/Edges/wrapEdge.tsx +++ b/src/components/Edges/wrapEdge.tsx @@ -5,6 +5,7 @@ import { useStoreActions, useStoreState } from '../../store/hooks'; import { Edge, EdgeProps, WrapEdgeProps } from '../../types'; import { onMouseDown } from '../../components/Handle/handler'; import { EdgeAnchor } from './EdgeAnchor'; +import { getMarkerId } from '../../utils/graph'; export default (EdgeComponent: ComponentType) => { const EdgeWrapper = ({ @@ -23,7 +24,6 @@ export default (EdgeComponent: ComponentType) => { labelBgPadding, labelBgBorderRadius, style, - arrowHeadType, source, target, sourceX, @@ -33,7 +33,6 @@ export default (EdgeComponent: ComponentType) => { sourcePosition, targetPosition, elementsSelectable, - markerEndId, isHidden, sourceHandleId, targetHandleId, @@ -46,6 +45,8 @@ export default (EdgeComponent: ComponentType) => { edgeUpdaterRadius, onEdgeUpdateStart, onEdgeUpdateEnd, + markerEnd, + markerStart, }: WrapEdgeProps): JSX.Element | null => { const addSelectedElements = useStoreActions((actions) => actions.addSelectedElements); const setConnectionNodeId = useStoreActions((actions) => actions.setConnectionNodeId); @@ -160,7 +161,18 @@ export default (EdgeComponent: ComponentType) => { _onEdgeUpdate ); }, - [id, source, target, type, sourceHandleId, targetHandleId, setConnectionNodeId, setPosition, edgeElement, onConnectEdge] + [ + id, + source, + target, + type, + sourceHandleId, + targetHandleId, + setConnectionNodeId, + setPosition, + edgeElement, + onConnectEdge, + ] ); const onEdgeUpdaterSourceMouseDown = useCallback( @@ -179,6 +191,8 @@ export default (EdgeComponent: ComponentType) => { const onEdgeUpdaterMouseEnter = useCallback(() => setUpdating(true), [setUpdating]); const onEdgeUpdaterMouseOut = useCallback(() => setUpdating(false), [setUpdating]); + const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart)})`, [markerStart]); + const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd)})`, [markerEnd]); if (isHidden) { return null; @@ -208,16 +222,16 @@ export default (EdgeComponent: ComponentType) => { labelBgBorderRadius={labelBgBorderRadius} data={data} style={style} - arrowHeadType={arrowHeadType} sourceX={sourceX} sourceY={sourceY} targetX={targetX} targetY={targetY} sourcePosition={sourcePosition} targetPosition={targetPosition} - markerEndId={markerEndId} sourceHandleId={sourceHandleId} targetHandleId={targetHandleId} + markerStart={markerStartUrl} + markerEnd={markerEndUrl} /> {handleEdgeUpdate && ( ( - - {children} - -); - -interface MarkerDefinitionsProps { +interface SymbolProps { color: string; } -const MarkerDefinitions = ({ color }: MarkerDefinitionsProps) => { +interface EdgeMarkerExtended extends EdgeMarker { + id: string; +} + +const ArrowSymbol = ({ color }: SymbolProps) => { + return ( + + ); +}; + +const ArrowClosedSymbol = ({ color }: SymbolProps) => { + return ( + + ); +}; + +const markerSymbols = { + [ArrowHeadType.Arrow]: ArrowSymbol, + [ArrowHeadType.ArrowClosed]: ArrowClosedSymbol, +}; + +const Marker = ({ id, type, color }: MarkerProps) => { + const Symbol = markerSymbols[type]; + + return ( + + + + ); +}; + +const MarkerDefinitions = ({ defaultColor }: MarkerDefinitionsProps) => { + const edges = useStoreState((state) => state.edges); + const markers = useMemo(() => { + const ids: string[] = []; + + return edges.reduce((markers, edge) => { + [edge.markerStart, edge.markerEnd].forEach((marker) => { + if (marker && typeof marker === 'object') { + const markerId = getMarkerId(marker); + if (!ids.includes(markerId)) { + markers.push({ id: markerId, ...marker }); + ids.push(markerId); + } + } + }); + return markers.sort((a, b) => a.id.localeCompare(b.id)); + }, []); + }, [edges]); + return ( - - - - - - + {markers.map((marker: EdgeMarkerExtended) => ( + + ))} ); }; diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 1fd7f766..eaea06ad 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -26,8 +26,7 @@ interface EdgeRendererProps { connectionMode?: ConnectionMode; onElementClick?: (event: React.MouseEvent, element: Node | Edge) => void; onEdgeDoubleClick?: (event: React.MouseEvent, edge: Edge) => void; - arrowHeadColor: string; - markerEndId?: string; + defaultMarkerColor: string; onlyRenderVisibleElements: boolean; onEdgeUpdate?: OnEdgeUpdateFunc; onEdgeContextMenu?: (event: React.MouseEvent, edge: Edge) => void; @@ -155,7 +154,8 @@ const Edge = ({ labelBgPadding={edge.labelBgPadding} labelBgBorderRadius={edge.labelBgBorderRadius} style={edge.style} - arrowHeadType={edge.arrowHeadType} + markerEnd={edge.markerEnd} + markerStart={edge.markerStart} source={edge.source} target={edge.target} sourceHandleId={sourceHandleId} @@ -167,7 +167,6 @@ const Edge = ({ sourcePosition={sourcePosition} targetPosition={targetPosition} elementsSelectable={elementsSelectable} - markerEndId={props.markerEndId} isHidden={edge.isHidden} onConnectEdge={onConnectEdge} handleEdgeUpdate={typeof props.onEdgeUpdate !== 'undefined'} @@ -203,7 +202,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => { const { connectionLineType, - arrowHeadColor, + defaultMarkerColor, connectionLineStyle, connectionLineComponent, onlyRenderVisibleElements, @@ -213,7 +212,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => { return ( - + {edges.map((edge: Edge) => ( { children: ReactNode; } diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index e002f366..51663eb5 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -23,7 +23,7 @@ export interface GraphViewProps extends Omit, 'on translateExtent?: TranslateExtent; preventScrolling?: boolean; nodeExtent?: NodeExtent; - arrowHeadColor?: string; - markerEndId?: string; + defaultMarkerColor?: string; zoomOnScroll?: boolean; zoomOnPinch?: boolean; panOnScroll?: boolean; @@ -184,8 +183,7 @@ const ReactFlow = forwardRef( translateExtent, preventScrolling = true, nodeExtent, - arrowHeadColor = '#b1b1b7', - markerEndId, + defaultMarkerColor = '#b1b1b7', zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, @@ -262,8 +260,7 @@ const ReactFlow = forwardRef( translateExtent={translateExtent} preventScrolling={preventScrolling} nodeExtent={nodeExtent} - arrowHeadColor={arrowHeadColor} - markerEndId={markerEndId} + defaultMarkerColor={defaultMarkerColor} zoomOnScroll={zoomOnScroll} zoomOnPinch={zoomOnPinch} zoomOnDoubleClick={zoomOnDoubleClick} diff --git a/src/types/index.ts b/src/types/index.ts index 754e10e2..97938453 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -57,6 +57,13 @@ export enum ArrowHeadType { ArrowClosed = 'arrowclosed', } +export interface EdgeMarker { + type: ArrowHeadType; + color?: string; +} + +export type EdgeMarkerType = string | EdgeMarker; + export interface Edge { id: ElementId; type?: string; @@ -72,10 +79,11 @@ export interface Edge { labelBgBorderRadius?: number; style?: CSSProperties; animated?: boolean; - arrowHeadType?: ArrowHeadType; isHidden?: boolean; data?: T; className?: string; + markerStart?: EdgeMarkerType; + markerEnd?: EdgeMarkerType; } export enum BackgroundVariant { @@ -111,7 +119,6 @@ export interface WrapEdgeProps { labelBgPadding?: [number, number]; labelBgBorderRadius?: number; style?: CSSProperties; - arrowHeadType?: ArrowHeadType; source: ElementId; target: ElementId; sourceHandleId: ElementId | null; @@ -123,7 +130,6 @@ export interface WrapEdgeProps { sourcePosition: Position; targetPosition: Position; elementsSelectable?: boolean; - markerEndId?: string; isHidden?: boolean; handleEdgeUpdate: boolean; onConnectEdge: OnConnectFunc; @@ -134,6 +140,8 @@ export interface WrapEdgeProps { edgeUpdaterRadius?: number; onEdgeUpdateStart?: (event: React.MouseEvent, edge: Edge) => void; onEdgeUpdateEnd?: (event: MouseEvent, edge: Edge) => void; + markerStart?: EdgeMarkerType; + markerEnd?: EdgeMarkerType; } export interface EdgeProps { @@ -155,11 +163,11 @@ export interface EdgeProps { labelBgPadding?: [number, number]; labelBgBorderRadius?: number; style?: CSSProperties; - arrowHeadType?: ArrowHeadType; - markerEndId?: string; data?: T; sourceHandleId?: ElementId | null; targetHandleId?: ElementId | null; + markerStart?: string; + markerEnd?: string; } export interface EdgeSmoothStepProps extends EdgeProps { borderRadius?: number; diff --git a/src/utils/graph.ts b/src/utils/graph.ts index 73b6c68b..2c6f4309 100644 --- a/src/utils/graph.ts +++ b/src/utils/graph.ts @@ -15,6 +15,7 @@ import { FlowExportObject, ReactFlowState, NodeExtent, + EdgeMarkerType, } from '../types'; export const isEdge = (element: Node | Connection | Edge): element is Edge => @@ -57,6 +58,21 @@ export const removeElements = (elementsToRemove: Elements, elements: Elements): const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): ElementId => `reactflow__edge-${source}${sourceHandle}-${target}${targetHandle}`; +export const getMarkerId = (marker: EdgeMarkerType | undefined): string => { + if (typeof marker === 'undefined') { + return ''; + } + + if (typeof marker === 'string') { + return marker; + } + + return Object.keys(marker) + .sort() + .map((key: string) => `${key}=${(marker as any)[key]}`) + .join('&'); +}; + const connectionExists = (edge: Edge, elements: Elements) => { return elements.some( (el) =>