feat(edges): add arrow head options
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd } from './utils';
|
||||
import { EdgeBezierProps, Position } from '../../types';
|
||||
|
||||
interface GetBezierPathParams {
|
||||
@@ -52,6 +53,8 @@ export default memo(
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
style,
|
||||
arrowHeadType,
|
||||
markerEndId,
|
||||
}: EdgeBezierProps) => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
@@ -81,9 +84,11 @@ export default memo(
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<path style={style} d={path} className="react-flow__edge-path" />
|
||||
<path style={style} d={path} className="react-flow__edge-path" markerEnd={markerEnd} />
|
||||
{text}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd } from './utils';
|
||||
import { EdgeBezierProps, Position } from '../../types';
|
||||
|
||||
// These are some helper methods for drawing the round corners
|
||||
@@ -138,6 +139,8 @@ export default memo(
|
||||
style,
|
||||
sourcePosition = Position.Bottom,
|
||||
targetPosition = Position.Top,
|
||||
arrowHeadType,
|
||||
markerEndId,
|
||||
}: EdgeBezierProps) => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
@@ -158,6 +161,8 @@ export default memo(
|
||||
targetPosition,
|
||||
});
|
||||
|
||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
||||
|
||||
const text = label ? (
|
||||
<EdgeText
|
||||
x={centerX}
|
||||
@@ -171,7 +176,7 @@ export default memo(
|
||||
|
||||
return (
|
||||
<>
|
||||
<path style={style} className="react-flow__edge-path" d={path} />
|
||||
<path style={style} className="react-flow__edge-path" d={path} markerEnd={markerEnd} />
|
||||
{text}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd } from './utils';
|
||||
import { EdgeProps } from '../../types';
|
||||
|
||||
interface GetStepPathParams {
|
||||
@@ -16,7 +17,19 @@ export function getStepPath({ centerY, sourceX, sourceY, targetX, targetY }: Get
|
||||
}
|
||||
|
||||
export default memo(
|
||||
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style }: EdgeProps) => {
|
||||
({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
style,
|
||||
arrowHeadType,
|
||||
markerEndId,
|
||||
}: EdgeProps) => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
|
||||
@@ -25,6 +38,8 @@ export default memo(
|
||||
|
||||
const path = getStepPath({ centerY, sourceX, sourceY, targetX, targetY });
|
||||
|
||||
const markerEnd = getMarkerEnd(arrowHeadType, markerEndId);
|
||||
|
||||
const text = label ? (
|
||||
<EdgeText
|
||||
x={centerX}
|
||||
@@ -38,7 +53,7 @@ export default memo(
|
||||
|
||||
return (
|
||||
<>
|
||||
<path style={style} className="react-flow__edge-path" d={path} />
|
||||
<path style={style} className="react-flow__edge-path" d={path} markerEnd={markerEnd} />
|
||||
{text}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd } from './utils';
|
||||
import { EdgeProps } from '../../types';
|
||||
|
||||
export default memo(
|
||||
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style }: EdgeProps) => {
|
||||
({
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
style,
|
||||
arrowHeadType,
|
||||
markerEndId,
|
||||
}: 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 ? (
|
||||
<EdgeText
|
||||
@@ -24,7 +38,12 @@ export default memo(
|
||||
|
||||
return (
|
||||
<>
|
||||
<path style={style} className="react-flow__edge-path" d={`M ${sourceX},${sourceY}L ${targetX},${targetY}`} />
|
||||
<path
|
||||
style={style}
|
||||
className="react-flow__edge-path"
|
||||
d={`M ${sourceX},${sourceY}L ${targetX},${targetY}`}
|
||||
markerEnd={markerEnd}
|
||||
/>
|
||||
{text}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ArrowHeadType } from '../../types';
|
||||
|
||||
export const getMarkerEnd = (arrowHeadType?: ArrowHeadType, markerEndId?: string): string => {
|
||||
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
||||
return `url(#${markerEndId})`;
|
||||
}
|
||||
|
||||
return typeof arrowHeadType !== 'undefined' ? `url(#react-flow__${arrowHeadType})` : 'none';
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const Marker = ({ id, children }: MarkerProps) => (
|
||||
<marker
|
||||
className="react-flow__arrowhead"
|
||||
id={id}
|
||||
markerWidth="12.5"
|
||||
markerHeight="12.5"
|
||||
viewBox="-10 -10 20 20"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
>
|
||||
{children}
|
||||
</marker>
|
||||
);
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
const MarkerDefinitions = ({ color }: MarkerDefinitionsProps) => {
|
||||
return (
|
||||
<defs>
|
||||
<Marker id="react-flow__arrowclosed">
|
||||
<polyline
|
||||
stroke={color}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1"
|
||||
fill={color}
|
||||
points="-5,-4 0,0 -5,4 -5,-4"
|
||||
/>
|
||||
</Marker>
|
||||
<Marker id="react-flow__arrow">
|
||||
<polyline
|
||||
stroke={color}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
points="-5,-4 0,0 -5,4"
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
);
|
||||
};
|
||||
|
||||
MarkerDefinitions.displayName = 'MarkerDefinitions';
|
||||
|
||||
export default MarkerDefinitions;
|
||||
@@ -3,6 +3,7 @@ import React, { memo, CSSProperties } from 'react';
|
||||
import { useStoreState } from '../../store/hooks';
|
||||
import ConnectionLine from '../../components/ConnectionLine/index';
|
||||
import { isEdge } from '../../utils/graph';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import { XYPosition, Position, Edge, Node, ElementId, HandleElement, Elements, ConnectionLineType } from '../../types';
|
||||
|
||||
interface EdgeRendererProps {
|
||||
@@ -12,6 +13,8 @@ interface EdgeRendererProps {
|
||||
connectionLineType: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
onElementClick?: (element: Node | Edge) => void;
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
}
|
||||
|
||||
interface EdgePositions {
|
||||
@@ -171,6 +174,7 @@ function renderEdge(
|
||||
labelShowBg={edge.labelShowBg}
|
||||
labelBgStyle={edge.labelBgStyle}
|
||||
style={edge.style}
|
||||
arrowHeadType={edge.arrowHeadType}
|
||||
source={sourceId}
|
||||
target={targetId}
|
||||
sourceHandleId={sourceHandleId}
|
||||
@@ -182,6 +186,7 @@ function renderEdge(
|
||||
sourcePosition={sourcePosition}
|
||||
targetPosition={targetPosition}
|
||||
elementsSelectable={elementsSelectable}
|
||||
markerEndId={props.markerEndId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -197,7 +202,7 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
|
||||
const nodesConnectable = useStoreState((s) => s.nodesConnectable);
|
||||
const elementsSelectable = useStoreState((s) => s.elementsSelectable);
|
||||
|
||||
const { width, height, connectionLineStyle, connectionLineType } = props;
|
||||
const { width, height, connectionLineStyle, connectionLineType, arrowHeadColor } = props;
|
||||
|
||||
if (!width) {
|
||||
return null;
|
||||
@@ -208,6 +213,7 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
|
||||
|
||||
return (
|
||||
<svg width={width} height={height} className="react-flow__edges">
|
||||
<MarkerDefinitions color={arrowHeadColor} />
|
||||
<g transform={transformStyle}>
|
||||
{edges.map((e: Edge) => renderEdge(e, props, nodes, selectedElements, elementsSelectable))}
|
||||
{renderConnectionLine && (
|
||||
|
||||
@@ -52,6 +52,8 @@ export interface GraphViewProps {
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
defaultZoom: number;
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
}
|
||||
|
||||
const GraphView = memo(
|
||||
@@ -84,6 +86,8 @@ const GraphView = memo(
|
||||
minZoom,
|
||||
maxZoom,
|
||||
defaultZoom,
|
||||
arrowHeadColor,
|
||||
markerEndId,
|
||||
}: GraphViewProps) => {
|
||||
const zoomPane = useRef<HTMLDivElement>(null);
|
||||
const rendererNode = useRef<HTMLDivElement>(null);
|
||||
@@ -212,6 +216,8 @@ const GraphView = memo(
|
||||
onElementClick={onElementClick}
|
||||
connectionLineType={connectionLineType}
|
||||
connectionLineStyle={connectionLineStyle}
|
||||
arrowHeadColor={arrowHeadColor}
|
||||
markerEndId={markerEndId}
|
||||
/>
|
||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||
{nodesSelectionActive && <NodesSelection />}
|
||||
|
||||
@@ -60,6 +60,8 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
defaultZoom: number;
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
}
|
||||
|
||||
const ReactFlow = ({
|
||||
@@ -95,6 +97,8 @@ const ReactFlow = ({
|
||||
minZoom,
|
||||
maxZoom,
|
||||
defaultZoom,
|
||||
arrowHeadColor,
|
||||
markerEndId,
|
||||
}: ReactFlowProps) => {
|
||||
const nodeTypesParsed = useMemo(() => createNodeTypes(nodeTypes), []);
|
||||
const edgeTypesParsed = useMemo(() => createEdgeTypes(edgeTypes), []);
|
||||
@@ -132,6 +136,8 @@ const ReactFlow = ({
|
||||
minZoom={minZoom}
|
||||
maxZoom={maxZoom}
|
||||
defaultZoom={defaultZoom}
|
||||
arrowHeadColor={arrowHeadColor}
|
||||
markerEndId={markerEndId}
|
||||
/>
|
||||
{onSelectionChange && <SelectionListener onSelectionChange={onSelectionChange} />}
|
||||
{children}
|
||||
@@ -167,6 +173,7 @@ ReactFlow.defaultProps = {
|
||||
minZoom: 0.5,
|
||||
maxZoom: 2,
|
||||
defaultZoom: 1,
|
||||
arrowHeadColor: '#bbb',
|
||||
};
|
||||
|
||||
export default ReactFlow;
|
||||
|
||||
@@ -42,6 +42,11 @@ export interface Node {
|
||||
sourcePosition?: Position;
|
||||
}
|
||||
|
||||
export enum ArrowHeadType {
|
||||
Arrow = 'arrow',
|
||||
ArrowClosed = 'arrowclosed',
|
||||
}
|
||||
|
||||
export interface Edge {
|
||||
id: ElementId;
|
||||
type?: string;
|
||||
@@ -53,6 +58,7 @@ export interface Edge {
|
||||
labelBgStyle?: CSSProperties;
|
||||
style?: CSSProperties;
|
||||
animated?: boolean;
|
||||
arrowHeadType?: ArrowHeadType;
|
||||
}
|
||||
|
||||
export enum BackgroundVariant {
|
||||
@@ -82,6 +88,8 @@ export interface EdgeProps {
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
style?: CSSProperties;
|
||||
arrowHeadType?: ArrowHeadType;
|
||||
markerEndId?: string;
|
||||
}
|
||||
|
||||
export interface EdgeBezierProps extends EdgeProps {
|
||||
|
||||
Reference in New Issue
Block a user