Merge pull request #208 from wbkd/develop
Text option for edges, release handling
This commit is contained in:
+1
-1
@@ -4,4 +4,4 @@ node_modules
|
||||
examples/build
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
stats.html
|
||||
dist
|
||||
@@ -0,0 +1,8 @@
|
||||
.DS_Store
|
||||
.cache
|
||||
.vscode
|
||||
.dependabot
|
||||
node_modules
|
||||
examples
|
||||
cypress
|
||||
dist
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"git": {
|
||||
"commitMessage": "chore: release v${version}"
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["npm test"],
|
||||
"after:bump": "npm run build",
|
||||
"after:release": "echo Successfully released ${name} v${version} to ${repo.repository}."
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ You can find an example of how to implement a custom node in the [custom node ex
|
||||
|
||||
There are three [edge types](#edge-types--custom-edges) (`straight`, `default`, `step`) you can use. The default type is `default`. You can also create [custom edges](#edge-types--custom-edges).
|
||||
|
||||
Edge example: `{ id: 'e1-2', type: 'straight', source: '1', target: '2', animated: true }`
|
||||
Edge example: `{ id: 'e1-2', type: 'straight', source: '1', target: '2', animated: true, label: 'edge label' }`
|
||||
|
||||
### Props
|
||||
|
||||
@@ -139,7 +139,13 @@ Edge example: `{ id: 'e1-2', type: 'straight', source: '1', target: '2', animate
|
||||
- `target`: string (an id of a node) *(required)*
|
||||
- `type`: 'input' | 'output' | 'default' or a custom one you implemented
|
||||
- `animated`: boolean
|
||||
- `style`: css properties
|
||||
- `style`: css properties for the edge line path
|
||||
- `label`: string
|
||||
- `labelStyle`: css properties for the text
|
||||
- `labelShowBg`: boolean - default: `true`
|
||||
- `labelBgStyle`: css properties for the text background
|
||||
|
||||
You can find an example with lots of different edges in the [edges example](https://react-flow.netlify.app/edges).
|
||||
|
||||
### Edge Types / Custom Edges
|
||||
|
||||
|
||||
Vendored
-9518
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-9532
File diff suppressed because it is too large
Load Diff
Vendored
-1
File diff suppressed because one or more lines are too long
-10
@@ -1,10 +0,0 @@
|
||||
import React, { HTMLAttributes } from 'react';
|
||||
import { GridType } from '../../types';
|
||||
interface GridProps extends HTMLAttributes<SVGElement> {
|
||||
backgroundType?: GridType;
|
||||
gap?: number;
|
||||
color?: string;
|
||||
size?: number;
|
||||
}
|
||||
declare const Grid: React.MemoExoticComponent<({ gap, color, size, style, className, backgroundType, }: GridProps) => JSX.Element>;
|
||||
export default Grid;
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
import { SVGAttributes } from 'react';
|
||||
import { ElementId, Node, Transform } from '../../types';
|
||||
interface ConnectionLineProps {
|
||||
connectionSourceId: ElementId;
|
||||
connectionPositionX: number;
|
||||
connectionPositionY: number;
|
||||
connectionLineType?: string | null;
|
||||
nodes: Node[];
|
||||
transform: Transform;
|
||||
isInteractive: boolean;
|
||||
connectionLineStyle?: SVGAttributes<{}>;
|
||||
className?: string;
|
||||
}
|
||||
declare const _default: ({ connectionSourceId, connectionLineStyle, connectionPositionX, connectionPositionY, connectionLineType, nodes, className, transform, isInteractive, }: ConnectionLineProps) => JSX.Element | null;
|
||||
export default _default;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import { EdgeBezierProps } from '../../types';
|
||||
declare const _default: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style, }: EdgeBezierProps) => JSX.Element>;
|
||||
export default _default;
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import { EdgeProps } from '../../types';
|
||||
declare const _default: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, style }: EdgeProps) => JSX.Element>;
|
||||
export default _default;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import { EdgeProps } from '../../types';
|
||||
declare const _default: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, style }: EdgeProps) => JSX.Element>;
|
||||
export default _default;
|
||||
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ElementId, Edge, EdgeCompProps } from '../../types';
|
||||
interface EdgeWrapperProps {
|
||||
id: ElementId;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
type: any;
|
||||
onClick: (edge: Edge) => void;
|
||||
animated: boolean;
|
||||
selected: boolean;
|
||||
isInteractive: boolean;
|
||||
}
|
||||
declare const _default: (EdgeComponent: React.ComponentType<EdgeCompProps>) => React.MemoExoticComponent<({ id, source, target, type, animated, selected, onClick, isInteractive, ...rest }: EdgeWrapperProps) => JSX.Element>;
|
||||
export default _default;
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
import React from 'react';
|
||||
import { HandleType, ElementId, Position, XYPosition, OnConnectFunc, Connection } from '../../types';
|
||||
declare type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
declare type SetSourceIdFunc = (nodeId: ElementId | null) => void;
|
||||
interface BaseHandleProps {
|
||||
type: HandleType;
|
||||
nodeId: ElementId;
|
||||
onConnect: OnConnectFunc;
|
||||
position: Position;
|
||||
setSourceId: SetSourceIdFunc;
|
||||
setPosition: (pos: XYPosition) => void;
|
||||
isValidConnection: ValidConnectionFunc;
|
||||
id?: ElementId | boolean;
|
||||
className?: string;
|
||||
}
|
||||
declare const BaseHandle: React.MemoExoticComponent<({ type, nodeId, onConnect, position, setSourceId, setPosition, className, id, isValidConnection, ...rest }: BaseHandleProps) => JSX.Element>;
|
||||
export default BaseHandle;
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import { HandleType, Position, OnConnectFunc } from '../../types';
|
||||
interface HandleProps {
|
||||
type: HandleType;
|
||||
position: Position;
|
||||
onConnect?: OnConnectFunc;
|
||||
isValidConnection?: () => boolean;
|
||||
}
|
||||
declare const Handle: React.MemoExoticComponent<({ onConnect, type, position, isValidConnection, ...rest }: HandleProps) => JSX.Element>;
|
||||
export default Handle;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
/// <reference types="react" />
|
||||
import { NodeProps } from '../../types';
|
||||
declare const _default: ({ data, targetPosition, sourcePosition, style }: NodeProps) => JSX.Element;
|
||||
export default _default;
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
/// <reference types="react" />
|
||||
import { NodeProps } from '../../types';
|
||||
declare const _default: ({ data, style, sourcePosition }: NodeProps) => JSX.Element;
|
||||
export default _default;
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
/// <reference types="react" />
|
||||
import { NodeProps } from '../../types';
|
||||
declare const _default: ({ data, style, targetPosition }: NodeProps) => JSX.Element;
|
||||
export default _default;
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
import { HandleElement } from '../../types';
|
||||
export declare const getHandleBounds: (selector: string, nodeElement: HTMLDivElement, parentBounds: ClientRect | DOMRect, k: number) => HandleElement[] | null;
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import { NodeComponentProps, WrapNodeProps } from '../../types';
|
||||
declare const _default: (NodeComponent: React.ComponentType<NodeComponentProps>) => React.MemoExoticComponent<({ id, type, data, transform, xPos, yPos, selected, onClick, onNodeDragStop, style, isInteractive, sourcePosition, targetPosition, }: WrapNodeProps) => JSX.Element>;
|
||||
export default _default;
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* The nodes selection rectangle gets displayed when a user
|
||||
* made a selectio with on or several nodes
|
||||
*/
|
||||
import React from 'react';
|
||||
declare const _default: React.MemoExoticComponent<() => JSX.Element>;
|
||||
export default _default;
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
|
||||
*/
|
||||
import React from 'react';
|
||||
declare type UserSelectionProps = {
|
||||
isInteractive: boolean;
|
||||
};
|
||||
declare const _default: React.MemoExoticComponent<({ isInteractive }: UserSelectionProps) => JSX.Element | null>;
|
||||
export default _default;
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import React, { SVGAttributes } from 'react';
|
||||
interface EdgeRendererProps {
|
||||
width: number;
|
||||
height: number;
|
||||
edgeTypes: any;
|
||||
connectionLineStyle?: SVGAttributes<{}>;
|
||||
connectionLineType?: string;
|
||||
onElementClick?: () => void;
|
||||
}
|
||||
declare const EdgeRenderer: React.MemoExoticComponent<(props: EdgeRendererProps) => JSX.Element | null>;
|
||||
export default EdgeRenderer;
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
import { EdgeTypesType } from '../../types';
|
||||
export declare function createEdgeTypes(edgeTypes: EdgeTypesType): EdgeTypesType;
|
||||
Vendored
-27
@@ -1,27 +0,0 @@
|
||||
import React, { SVGAttributes } from 'react';
|
||||
import { Elements, NodeTypesType, EdgeTypesType, GridType, OnLoadFunc } from '../../types';
|
||||
export interface GraphViewProps {
|
||||
elements: Elements;
|
||||
onElementClick: () => void;
|
||||
onElementsRemove: (elements: Elements) => void;
|
||||
onNodeDragStop: () => void;
|
||||
onConnect: () => void;
|
||||
onLoad: OnLoadFunc;
|
||||
onMove: () => void;
|
||||
selectionKeyCode: number;
|
||||
nodeTypes: NodeTypesType;
|
||||
edgeTypes: EdgeTypesType;
|
||||
connectionLineType: string;
|
||||
connectionLineStyle: SVGAttributes<{}>;
|
||||
deleteKeyCode: number;
|
||||
showBackground: boolean;
|
||||
backgroundGap: number;
|
||||
backgroundColor: string;
|
||||
backgroundType: GridType;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
onlyRenderVisibleNodes: boolean;
|
||||
isInteractive: boolean;
|
||||
}
|
||||
declare const GraphView: React.MemoExoticComponent<({ nodeTypes, edgeTypes, onMove, onLoad, onElementClick, onNodeDragStop, connectionLineType, connectionLineStyle, selectionKeyCode, onElementsRemove, deleteKeyCode, elements, showBackground, backgroundGap, backgroundColor, backgroundType, onConnect, snapToGrid, snapGrid, onlyRenderVisibleNodes, isInteractive, }: GraphViewProps) => JSX.Element>;
|
||||
export default GraphView;
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import { NodeTypesType } from '../../types';
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: NodeTypesType;
|
||||
onElementClick: () => void;
|
||||
onNodeDragStop: () => void;
|
||||
onlyRenderVisibleNodes?: boolean;
|
||||
}
|
||||
declare const NodeRenderer: React.MemoExoticComponent<({ onlyRenderVisibleNodes, ...props }: NodeRendererProps) => JSX.Element>;
|
||||
export default NodeRenderer;
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
import { NodeTypesType } from '../../types';
|
||||
export declare function createNodeTypes(nodeTypes: NodeTypesType): NodeTypesType;
|
||||
Vendored
-61
@@ -1,61 +0,0 @@
|
||||
import React, { SVGAttributes, HTMLAttributes } from 'react';
|
||||
import { Elements, NodeTypesType, EdgeTypesType, GridType, OnLoadFunc } from '../../types';
|
||||
import '../../style.css';
|
||||
export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad'> {
|
||||
elements: Elements;
|
||||
onElementClick: () => void;
|
||||
onElementsRemove: (elements: Elements) => void;
|
||||
onNodeDragStop: () => void;
|
||||
onConnect: () => void;
|
||||
onLoad: OnLoadFunc;
|
||||
onMove: () => void;
|
||||
nodeTypes: NodeTypesType;
|
||||
edgeTypes: EdgeTypesType;
|
||||
connectionLineType: string;
|
||||
connectionLineStyle: SVGAttributes<{}>;
|
||||
deleteKeyCode: number;
|
||||
selectionKeyCode: number;
|
||||
showBackground: boolean;
|
||||
backgroundGap: number;
|
||||
backgroundColor: string;
|
||||
backgroundType: GridType;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
onlyRenderVisibleNodes: boolean;
|
||||
isInteractive: boolean;
|
||||
}
|
||||
declare const ReactFlow: {
|
||||
({ style, onElementClick, elements, children, nodeTypes, edgeTypes, onLoad, onMove, onElementsRemove, onConnect, onNodeDragStop, connectionLineType, connectionLineStyle, deleteKeyCode, selectionKeyCode, showBackground, backgroundGap, backgroundType, backgroundColor, snapToGrid, snapGrid, onlyRenderVisibleNodes, isInteractive, }: ReactFlowProps): JSX.Element;
|
||||
displayName: string;
|
||||
defaultProps: {
|
||||
onElementClick: () => void;
|
||||
onElementsRemove: () => void;
|
||||
onNodeDragStop: () => void;
|
||||
onConnect: () => void;
|
||||
onLoad: () => void;
|
||||
onMove: () => void;
|
||||
nodeTypes: {
|
||||
input: ({ data, style, sourcePosition }: import("../../types").NodeProps) => JSX.Element;
|
||||
default: ({ data, targetPosition, sourcePosition, style }: import("../../types").NodeProps) => JSX.Element;
|
||||
output: ({ data, style, targetPosition }: import("../../types").NodeProps) => JSX.Element;
|
||||
};
|
||||
edgeTypes: {
|
||||
default: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style, }: import("../../types").EdgeBezierProps) => JSX.Element>;
|
||||
straight: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, style }: import("../../types").EdgeProps) => JSX.Element>;
|
||||
step: React.MemoExoticComponent<({ sourceX, sourceY, targetX, targetY, style }: import("../../types").EdgeProps) => JSX.Element>;
|
||||
};
|
||||
connectionLineType: string;
|
||||
connectionLineStyle: {};
|
||||
deleteKeyCode: number;
|
||||
selectionKeyCode: number;
|
||||
backgroundColor: string;
|
||||
backgroundGap: number;
|
||||
showBackground: boolean;
|
||||
backgroundType: GridType;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: number[];
|
||||
onlyRenderVisibleNodes: boolean;
|
||||
isInteractive: boolean;
|
||||
};
|
||||
};
|
||||
export default ReactFlow;
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
/// <reference types="react" />
|
||||
import { ElementId } from '../types';
|
||||
declare type ContextProps = ElementId | null;
|
||||
export declare const NodeIdContext: import("react").Context<ContextProps>;
|
||||
export declare const Provider: import("react").Provider<ContextProps>;
|
||||
export declare const Consumer: import("react").Consumer<ContextProps>;
|
||||
export default NodeIdContext;
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
import { MutableRefObject } from 'react';
|
||||
declare const _default: (zoomPane: MutableRefObject<Element | null>, onMove: () => void, shiftPressed: boolean) => void;
|
||||
export default _default;
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
import { Elements } from '../types';
|
||||
declare const useElementUpdater: (elements: Elements) => void;
|
||||
export default useElementUpdater;
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
import { Elements } from '../types';
|
||||
interface HookParams {
|
||||
deleteKeyCode: number;
|
||||
onElementsRemove: (elements: Elements) => void;
|
||||
}
|
||||
declare const _default: ({ deleteKeyCode, onElementsRemove }: HookParams) => void;
|
||||
export default _default;
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
declare const _default: (keyCode: number) => boolean;
|
||||
export default _default;
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
import ReactFlow from './container/ReactFlow';
|
||||
export default ReactFlow;
|
||||
export { default as Handle } from './components/Handle';
|
||||
export { MiniMap, Controls } from './plugins';
|
||||
export { isNode, isEdge, removeElements, addEdge, getOutgoers, } from './utils/graph';
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
interface ControlProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
}
|
||||
declare const _default: ({ style, className }: ControlProps) => JSX.Element;
|
||||
export default _default;
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Node } from '../../types';
|
||||
declare type StringFunc = (node: Node) => string;
|
||||
interface MiniMapProps extends React.HTMLAttributes<SVGSVGElement> {
|
||||
nodeColor: string | StringFunc;
|
||||
nodeBorderRadius: number;
|
||||
maskColor: string;
|
||||
}
|
||||
declare const _default: ({ style, className, nodeColor, nodeBorderRadius, maskColor, }: MiniMapProps) => JSX.Element;
|
||||
export default _default;
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
export { default as MiniMap } from './MiniMap';
|
||||
export { default as Controls } from './Controls';
|
||||
Vendored
-71
@@ -1,71 +0,0 @@
|
||||
import { StoreModel } from './index';
|
||||
export declare const useStoreActions: <Result>(mapActions: (actions: import("easy-peasy").ActionMapper<{
|
||||
selectedNodesBbox: import("../types").Rect;
|
||||
d3Zoom: import("d3-zoom").ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: import("d3-selection").Selection<Element, unknown, null, undefined> | null;
|
||||
selection: import("../types").SelectionRect | null;
|
||||
userSelectionRect: import("../types").SelectionRect;
|
||||
connectionPosition: import("../types").XYPosition;
|
||||
onConnect: import("../types").OnConnectFunc;
|
||||
setOnConnect: import("easy-peasy").Action<StoreModel, import("../types").OnConnectFunc>;
|
||||
setNodes: import("easy-peasy").Action<StoreModel, import("../types").Node[]>;
|
||||
setEdges: import("easy-peasy").Action<StoreModel, import("../types").Edge[]>;
|
||||
updateNodeDimensions: import("easy-peasy").Action<StoreModel, {
|
||||
id: string;
|
||||
nodeElement: HTMLDivElement;
|
||||
}>;
|
||||
updateNodePos: import("easy-peasy").Action<StoreModel, {
|
||||
id: string;
|
||||
pos: import("../types").XYPosition;
|
||||
}>;
|
||||
setSelection: import("easy-peasy").Action<StoreModel, boolean>;
|
||||
setNodesSelection: import("easy-peasy").Action<StoreModel, {
|
||||
isActive: boolean;
|
||||
selection?: import("../types").SelectionRect | undefined;
|
||||
}>;
|
||||
setSelectedElements: import("easy-peasy").Action<StoreModel, import("../types").Node | import("../types").Edge | import("../types").Elements>;
|
||||
updateSelection: import("easy-peasy").Action<StoreModel, import("../types").SelectionRect>;
|
||||
updateTransform: import("easy-peasy").Action<StoreModel, {
|
||||
x: number;
|
||||
y: number;
|
||||
k: number;
|
||||
}>;
|
||||
updateSize: import("easy-peasy").Action<StoreModel, import("../types").Dimensions>;
|
||||
initD3: import("easy-peasy").Action<StoreModel, {
|
||||
zoom: import("d3-zoom").ZoomBehavior<Element, unknown>;
|
||||
selection: import("d3-selection").Selection<Element, unknown, null, undefined>;
|
||||
}>;
|
||||
setSnapGrid: import("easy-peasy").Action<StoreModel, {
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
}>;
|
||||
setConnectionPosition: import("easy-peasy").Action<StoreModel, import("../types").XYPosition>;
|
||||
setConnectionSourceId: import("easy-peasy").Action<StoreModel, string | null>;
|
||||
setInteractive: import("easy-peasy").Action<StoreModel, boolean>;
|
||||
setUserSelection: import("easy-peasy").Action<StoreModel, import("../types").XYPosition>;
|
||||
updateUserSelection: import("easy-peasy").Action<StoreModel, import("../types").XYPosition>;
|
||||
unsetUserSelection: import("easy-peasy").Action<StoreModel, void>;
|
||||
}, "1">) => Result) => Result;
|
||||
export declare const useStoreDispatch: () => import("easy-peasy").Dispatch<StoreModel, import("redux").Action<any>>;
|
||||
export declare const useStoreState: <Result>(mapState: (state: import("easy-peasy").StateMapper<{
|
||||
width: number;
|
||||
height: number;
|
||||
transform: import("../types").Transform;
|
||||
nodes: import("../types").Node[];
|
||||
edges: import("../types").Edge[];
|
||||
selectedElements: import("../types").Elements;
|
||||
selectedNodesBbox: import("../types").Rect;
|
||||
d3Zoom: import("d3-zoom").ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: import("d3-selection").Selection<Element, unknown, null, undefined> | null;
|
||||
d3Initialised: boolean;
|
||||
nodesSelectionActive: boolean;
|
||||
selectionActive: boolean;
|
||||
selection: import("../types").SelectionRect | null;
|
||||
userSelectionRect: import("../types").SelectionRect;
|
||||
connectionSourceId: string | null;
|
||||
connectionPosition: import("../types").XYPosition;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
isInteractive: boolean;
|
||||
onConnect: import("../types").OnConnectFunc;
|
||||
}, "1">) => Result, equalityFn?: ((prev: Result, next: Result) => boolean) | undefined) => Result;
|
||||
Vendored
-71
@@ -1,71 +0,0 @@
|
||||
import { Action } from 'easy-peasy';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
import { ElementId, Elements, Transform, Node, Edge, Rect, Dimensions, XYPosition, OnConnectFunc, SelectionRect } from '../types';
|
||||
declare type TransformXYK = {
|
||||
x: number;
|
||||
y: number;
|
||||
k: number;
|
||||
};
|
||||
declare type NodePosUpdate = {
|
||||
id: ElementId;
|
||||
pos: XYPosition;
|
||||
};
|
||||
declare type NodeDimensionUpdate = {
|
||||
id: ElementId;
|
||||
nodeElement: HTMLDivElement;
|
||||
};
|
||||
declare type SelectionUpdate = {
|
||||
isActive: boolean;
|
||||
selection?: SelectionRect;
|
||||
};
|
||||
declare type D3Init = {
|
||||
zoom: ZoomBehavior<Element, unknown>;
|
||||
selection: D3Selection<Element, unknown, null, undefined>;
|
||||
};
|
||||
declare type SetSnapGrid = {
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
};
|
||||
export interface StoreModel {
|
||||
width: number;
|
||||
height: number;
|
||||
transform: Transform;
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
selectedElements: Elements;
|
||||
selectedNodesBbox: Rect;
|
||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
||||
d3Initialised: boolean;
|
||||
nodesSelectionActive: boolean;
|
||||
selectionActive: boolean;
|
||||
selection: SelectionRect | null;
|
||||
userSelectionRect: SelectionRect;
|
||||
connectionSourceId: ElementId | null;
|
||||
connectionPosition: XYPosition;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
isInteractive: boolean;
|
||||
onConnect: OnConnectFunc;
|
||||
setOnConnect: Action<StoreModel, OnConnectFunc>;
|
||||
setNodes: Action<StoreModel, Node[]>;
|
||||
setEdges: Action<StoreModel, Edge[]>;
|
||||
updateNodeDimensions: Action<StoreModel, NodeDimensionUpdate>;
|
||||
updateNodePos: Action<StoreModel, NodePosUpdate>;
|
||||
setSelection: Action<StoreModel, boolean>;
|
||||
setNodesSelection: Action<StoreModel, SelectionUpdate>;
|
||||
setSelectedElements: Action<StoreModel, Elements | Node | Edge>;
|
||||
updateSelection: Action<StoreModel, SelectionRect>;
|
||||
updateTransform: Action<StoreModel, TransformXYK>;
|
||||
updateSize: Action<StoreModel, Dimensions>;
|
||||
initD3: Action<StoreModel, D3Init>;
|
||||
setSnapGrid: Action<StoreModel, SetSnapGrid>;
|
||||
setConnectionPosition: Action<StoreModel, XYPosition>;
|
||||
setConnectionSourceId: Action<StoreModel, ElementId | null>;
|
||||
setInteractive: Action<StoreModel, boolean>;
|
||||
setUserSelection: Action<StoreModel, XYPosition>;
|
||||
updateUserSelection: Action<StoreModel, XYPosition>;
|
||||
unsetUserSelection: Action<StoreModel>;
|
||||
}
|
||||
declare const store: import("easy-peasy").Store<StoreModel, import("easy-peasy").EasyPeasyConfig<{}, any>>;
|
||||
export default store;
|
||||
Vendored
-134
@@ -1,134 +0,0 @@
|
||||
import { CSSProperties, SVGAttributes } from 'react';
|
||||
export declare type ElementId = string;
|
||||
export declare type Elements = Array<Node | Edge>;
|
||||
export declare type Transform = [number, number, number];
|
||||
export declare enum Position {
|
||||
Left = "left",
|
||||
Top = "top",
|
||||
Right = "right",
|
||||
Bottom = "bottom"
|
||||
}
|
||||
export declare type XYPosition = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
export declare enum GridType {
|
||||
Lines = "lines",
|
||||
Dots = "dots"
|
||||
}
|
||||
export declare type HandleType = 'source' | 'target';
|
||||
export declare type NodeTypesType = {
|
||||
[key: string]: React.ReactNode;
|
||||
};
|
||||
export declare type EdgeTypesType = NodeTypesType;
|
||||
export interface Dimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
export interface Rect extends Dimensions, XYPosition {
|
||||
}
|
||||
export interface Box extends XYPosition {
|
||||
x2: number;
|
||||
y2: number;
|
||||
}
|
||||
export interface SelectionRect extends Rect {
|
||||
startX: number;
|
||||
startY: number;
|
||||
draw: boolean;
|
||||
}
|
||||
export interface Node {
|
||||
id: ElementId;
|
||||
position: XYPosition;
|
||||
type?: string;
|
||||
__rg?: any;
|
||||
data?: any;
|
||||
style?: CSSProperties;
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
}
|
||||
export interface Edge {
|
||||
id: ElementId;
|
||||
type?: string;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
style?: SVGAttributes<{}>;
|
||||
animated?: boolean;
|
||||
}
|
||||
export interface EdgeProps {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
style?: SVGAttributes<{}>;
|
||||
}
|
||||
export interface EdgeBezierProps extends EdgeProps {
|
||||
sourcePosition: Position;
|
||||
targetPosition: Position;
|
||||
}
|
||||
export interface NodeProps {
|
||||
id: ElementId;
|
||||
type: string;
|
||||
data: any;
|
||||
selected: boolean;
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
export interface NodeComponentProps {
|
||||
id: ElementId;
|
||||
type: string;
|
||||
data: any;
|
||||
selected?: boolean;
|
||||
transform?: Transform;
|
||||
xPos?: number;
|
||||
yPos?: number;
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
onClick?: (node: Node) => void | undefined;
|
||||
onNodeDragStop?: () => any;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
export interface WrapNodeProps {
|
||||
id: ElementId;
|
||||
type: string;
|
||||
data: any;
|
||||
selected: boolean;
|
||||
transform: Transform;
|
||||
xPos: number;
|
||||
yPos: number;
|
||||
isInteractive: boolean;
|
||||
onClick: (node: Node) => void | undefined;
|
||||
onNodeDragStop: (node: Node) => void;
|
||||
style?: CSSProperties;
|
||||
sourcePosition?: Position;
|
||||
targetPosition?: Position;
|
||||
}
|
||||
export declare type FitViewParams = {
|
||||
padding: number;
|
||||
};
|
||||
export declare type FitViewFunc = (fitViewOptions: FitViewParams) => void;
|
||||
declare type OnLoadParams = {
|
||||
zoomIn: () => void;
|
||||
zoomOut: () => void;
|
||||
fitView: FitViewFunc;
|
||||
};
|
||||
export declare type OnLoadFunc = (params: OnLoadParams) => void;
|
||||
export declare type Connection = {
|
||||
source: ElementId | null;
|
||||
target: ElementId | null;
|
||||
};
|
||||
export declare type OnConnectFunc = (params: Connection) => void;
|
||||
export interface HandleElement extends XYPosition, Dimensions {
|
||||
id?: ElementId | null;
|
||||
position: Position;
|
||||
}
|
||||
export interface EdgeCompProps {
|
||||
id: ElementId;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
type: any;
|
||||
onClick?: (edge: Edge) => void;
|
||||
animated?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
export {};
|
||||
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
import { Node, Edge, Elements, Transform, XYPosition, Rect, FitViewParams } from '../types';
|
||||
export declare const isEdge: (element: Node | Edge) => boolean;
|
||||
export declare const isNode: (element: Node | Edge) => boolean;
|
||||
export declare const getOutgoers: (node: Node, elements: Elements) => Elements;
|
||||
export declare const removeElements: (elementsToRemove: Elements, elements: Elements) => Elements;
|
||||
export declare const addEdge: (edgeParams: Edge, elements: Elements) => Elements;
|
||||
export declare const parseElement: (element: Node | Edge, transform: Transform, snapToGrid: boolean, snapGrid: [number, number]) => Node | Edge;
|
||||
export declare const getBoundsofRects: (rect1: Rect, rect2: Rect) => Rect;
|
||||
export declare const getRectOfNodes: (nodes: Node[]) => Rect;
|
||||
export declare const graphPosToZoomedPos: ({ x, y }: XYPosition, [tx, ty, tScale]: Transform) => XYPosition;
|
||||
export declare const getNodesInside: (nodes: Node[], rect: Rect, [tx, ty, tScale]?: Transform, partially?: boolean) => Node[];
|
||||
export declare const getConnectedEdges: (nodes: Node[], edges: Edge[]) => Edge[];
|
||||
export declare const fitView: ({ padding }?: FitViewParams) => void;
|
||||
export declare const zoomIn: () => void;
|
||||
export declare const zoomOut: () => void;
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||
export declare const isInputDOMNode: (e: MouseEvent | ReactMouseEvent<Element, MouseEvent> | ReactMouseEvent<HTMLElement | SVGElement, MouseEvent> | import("react").TouchEvent<HTMLElement | SVGElement> | TouchEvent | KeyboardEvent) => boolean;
|
||||
export declare const getDimensions: (node: HTMLDivElement) => {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
@@ -11,7 +11,7 @@ const initialElements = [
|
||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 } },
|
||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 } },
|
||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true, label: 'edge text' },
|
||||
{ id: 'e1-3', source: '1', target: '3' },
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import ReactFlow, { removeElements, addEdge, MiniMap, Controls } from 'react-flow-renderer';
|
||||
|
||||
const onNodeDragStop = node => console.log('drag stop', node);
|
||||
const onElementClick = element => console.log('click', element);
|
||||
const onLoad = (graph) => {
|
||||
graph.fitView();
|
||||
};
|
||||
|
||||
const initialElements = [
|
||||
{ id: '1', type: 'input', data: { label: 'Input Node 1' }, position: { x: 250, y: 0 } },
|
||||
{ id: '2', data: { label: 'Node 2' }, position: { x: 150, y: 100 } },
|
||||
{ id: '3', data: { label: 'Node 3' }, position: { x: 250, y: 200 } },
|
||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 300 } },
|
||||
{ id: '5', data: { label: 'Node 5' }, position: { x: 450, y: 400 } },
|
||||
{ id: '6', type: 'output', data: { label: 'Output Node 5' }, position: { x: 250, y: 550 } },
|
||||
{ id: '7', type: 'output', data: { label: 'Output Node 6' }, position: { x: 550, y: 550 } },
|
||||
{ id: 'e1-2', source: '1', target: '2', label: 'bezier edge', labelBgStyle: { fillOpacity: 0.75 } },
|
||||
{ id: 'e2-3', source: '2', target: '3', type: 'step', label: 'step edge' },
|
||||
{ id: 'e3-4', source: '3', target: '4', type: 'straight', label: 'straight edge' },
|
||||
{ id: 'e3-5', source: '4', target: '5', animated: true, label: 'animated styled edge', style: { stroke: 'red' } },
|
||||
{ id: 'e5-6', source: '5', target: '6', label: 'styled label', labelStyle: { fill: 'red', fontWeight: 700 } },
|
||||
{ id: 'e5-7', source: '5', target: '7', label: 'label with styled bg', labelBgStyle: { fill: '#eee', fillOpacity: 0.7 } },
|
||||
|
||||
]
|
||||
|
||||
const EdgesFlow = () => {
|
||||
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}
|
||||
onElementClick={onElementClick}
|
||||
onElementsRemove={onElementsRemove}
|
||||
onConnect={onConnect}
|
||||
onNodeDragStop={onNodeDragStop}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
onLoad={onLoad}
|
||||
snapToGrid={true}
|
||||
>
|
||||
<MiniMap />
|
||||
<Controls />
|
||||
</ReactFlow>
|
||||
);
|
||||
}
|
||||
|
||||
export default EdgesFlow;
|
||||
@@ -18,13 +18,13 @@ const initialElements = [
|
||||
sourcePosition: 'right'
|
||||
},
|
||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 500, y: 200 }, targetPosition: 'left' },
|
||||
{ id: '5', type: 'output', data: { label: 'Output Node 5'}, position: { x: 300, y: 300 } },
|
||||
{ id: '5', type: 'output', data: { label: 'Output Node 5' }, position: { x: 300, y: 300 } },
|
||||
{ id: '6', type: 'output', data: { label: 'Output Node 6' }, position: { x: 600, y: 400 } },
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true, label: 'edge text', labelBgStyle: { fillOpacity: 0.75 } },
|
||||
{ id: 'e2-3', source: '2', target: '3', animated: true },
|
||||
{ id: 'e3-4', source: '3', target: '4', animated: true },
|
||||
{ id: 'e3-5', source: '4', target: '5', animated: true, type: 'step' },
|
||||
{ id: 'e5-6b', source: '4', target: '6', type: 'step' },
|
||||
{ id: 'e5-6b', source: '4', target: '6', type: 'step', label: 'styled label', labelStyle: { fill: 'red', fontWeight: 700 } },
|
||||
]
|
||||
|
||||
const RichGraph = () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import CustomNode from './CustomNode';
|
||||
import Stress from './Stress';
|
||||
import Inactive from './Inactive';
|
||||
import Empty from './Empty';
|
||||
import Edges from './Edges';
|
||||
|
||||
import './index.css';
|
||||
|
||||
@@ -27,10 +28,14 @@ const routes = [{
|
||||
path: '/stress',
|
||||
component: Stress,
|
||||
label: 'Stress'
|
||||
}, {
|
||||
path: '/edges',
|
||||
component: Edges,
|
||||
label: 'Edges'
|
||||
}, {
|
||||
path: '/empty',
|
||||
component: Empty
|
||||
},{
|
||||
}, {
|
||||
path: '/inactive',
|
||||
component: Inactive
|
||||
}];
|
||||
|
||||
Generated
+2366
-2063
File diff suppressed because it is too large
Load Diff
+14
-12
@@ -10,7 +10,7 @@
|
||||
"url": "https://github.com/wbkd/react-flow.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@welldone-software/why-did-you-render": "^4.1.3",
|
||||
"@welldone-software/why-did-you-render": "^4.2.0",
|
||||
"classnames": "^2.2.6",
|
||||
"d3-selection": "^1.4.1",
|
||||
"d3-zoom": "^1.8.3",
|
||||
@@ -20,14 +20,14 @@
|
||||
"resize-observer": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.0",
|
||||
"@babel/core": "^7.9.6",
|
||||
"@babel/preset-env": "^7.9.6",
|
||||
"@babel/preset-react": "^7.9.4",
|
||||
"@svgr/rollup": "^5.4.0",
|
||||
"@types/classnames": "^2.2.10",
|
||||
"@types/d3": "^5.7.2",
|
||||
"@types/react": "^16.9.34",
|
||||
"@types/react-dom": "^16.9.6",
|
||||
"@types/react": "^16.9.35",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"autoprefixer": "^9.7.6",
|
||||
"babel-loader": "^8.1.0",
|
||||
"babel-preset-react-app": "^9.1.2",
|
||||
@@ -36,11 +36,12 @@
|
||||
"prettier": "2.0.5",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.1",
|
||||
"rollup": "^2.7.6",
|
||||
"release-it": "^13.5.8",
|
||||
"rollup": "^2.9.1",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-bundle-size": "^1.0.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-livereload": "^1.1.0",
|
||||
"rollup-plugin-livereload": "^1.3.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.2",
|
||||
"rollup-plugin-postcss": "^3.1.1",
|
||||
@@ -49,10 +50,9 @@
|
||||
"rollup-plugin-terser": "^5.3.0",
|
||||
"rollup-plugin-typescript2": "^0.27.0",
|
||||
"rollup-plugin-uglify": "^6.0.4",
|
||||
"rollup-plugin-visualizer": "^4.0.4",
|
||||
"start-server-and-test": "^1.11.0",
|
||||
"typescript": "^3.8.3",
|
||||
"wait-on": "^4.0.2"
|
||||
"wait-on": "^5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1"
|
||||
@@ -60,13 +60,15 @@
|
||||
"scripts": {
|
||||
"build": "rollup -c --environment NODE_ENV:production",
|
||||
"start": "rollup -w -c",
|
||||
"dev": "npm start & cd example && npm start",
|
||||
"build:example": "npm install && cd example && npm install && npm run build",
|
||||
"waiton:dist": "wait-on dist/ReactFlow.js",
|
||||
"dev": "npm start & npm run waiton:dist && cd example && npm start",
|
||||
"dev:wait": "start-server-and-test dev http-get://localhost:3000",
|
||||
"build:example": "npm install && npm run build && cd example && npm install && npm run build",
|
||||
"cy:run": "cypress run",
|
||||
"cy:open": "cypress open",
|
||||
"cypress": "npm run dev:wait cy:open",
|
||||
"test": "npm run dev:wait cy:run"
|
||||
"test": "npm run dev:wait cy:run",
|
||||
"release": "release-it"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
+44
-41
@@ -3,52 +3,55 @@ import commonjs from 'rollup-plugin-commonjs';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import bundleSize from 'rollup-plugin-bundle-size';
|
||||
import visualizer from 'rollup-plugin-visualizer';
|
||||
import replace from 'rollup-plugin-replace';
|
||||
import svgr from '@svgr/rollup';
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
|
||||
import pkg from './package.json';
|
||||
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const processEnv = isProd ? 'production' : 'development';
|
||||
|
||||
export default [{
|
||||
input: 'src/index.ts',
|
||||
external: ['react', 'react-dom'],
|
||||
onwarn(warning, rollupWarn) {
|
||||
if (warning.code !== 'CIRCULAR_DEPENDENCY') {
|
||||
rollupWarn(warning);
|
||||
}
|
||||
},
|
||||
output: [{
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
exports: 'named'
|
||||
}, {
|
||||
file: pkg.module,
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
exports: 'named'
|
||||
}],
|
||||
plugins: [
|
||||
bundleSize(),
|
||||
postcss(),
|
||||
babel({
|
||||
exclude: 'node_modules/**'
|
||||
}),
|
||||
visualizer(),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify(processEnv)
|
||||
}),
|
||||
svgr(),
|
||||
typescript({
|
||||
clean: true
|
||||
}),
|
||||
resolve(),
|
||||
commonjs({
|
||||
include: 'node_modules/**'
|
||||
}),
|
||||
]
|
||||
}];
|
||||
export default [
|
||||
{
|
||||
input: 'src/index.ts',
|
||||
external: ['react', 'react-dom'],
|
||||
onwarn(warning, rollupWarn) {
|
||||
if (warning.code !== 'CIRCULAR_DEPENDENCY') {
|
||||
rollupWarn(warning);
|
||||
}
|
||||
},
|
||||
output: [
|
||||
{
|
||||
file: pkg.main,
|
||||
format: 'cjs',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
},
|
||||
{
|
||||
file: pkg.module,
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
exports: 'named',
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
bundleSize(),
|
||||
postcss(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify(processEnv),
|
||||
}),
|
||||
svgr(),
|
||||
typescript({
|
||||
clean: true,
|
||||
}),
|
||||
resolve(),
|
||||
commonjs({
|
||||
include: 'node_modules/**',
|
||||
}),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { EdgeBezierProps, Position } from '../../types';
|
||||
|
||||
export default memo(
|
||||
@@ -10,23 +11,43 @@ export default memo(
|
||||
targetY,
|
||||
sourcePosition = Position.Bottom,
|
||||
targetPosition = Position.Top,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
style = {},
|
||||
}: EdgeBezierProps) => {
|
||||
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;
|
||||
|
||||
let dAttr = `M${sourceX},${sourceY} C${sourceX},${centerY} ${targetX},${centerY} ${targetX},${targetY}`;
|
||||
|
||||
const leftAndRight = [Position.Left, Position.Right];
|
||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||
|
||||
dAttr = `M${sourceX},${sourceY} C${centerX},${sourceY} ${centerX},${targetY} ${targetX},${targetY}`;
|
||||
} else if (leftAndRight.includes(sourcePosition) || leftAndRight.includes(targetPosition)) {
|
||||
dAttr = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
|
||||
}
|
||||
|
||||
return <path {...style} d={dAttr} />;
|
||||
const text = label ? (
|
||||
<EdgeText
|
||||
x={centerX}
|
||||
y={centerY}
|
||||
label={label}
|
||||
labelStyle={labelStyle}
|
||||
labelShowBg={labelShowBg}
|
||||
labelBgStyle={labelBgStyle}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<g>
|
||||
<path style={style} d={dAttr} className="react-flow__edge-path" />
|
||||
{text}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { memo, useRef, useState, useEffect } from 'react';
|
||||
|
||||
import { EdgeTextProps, Rect } from '../../types';
|
||||
|
||||
const textBgPadding = 2;
|
||||
|
||||
export default memo(({ x, y, label, labelStyle = {}, labelShowBg = true, labelBgStyle = {} }: EdgeTextProps) => {
|
||||
const edgeRef = useRef<SVGTextElement>(null);
|
||||
const [edgeTextBbox, setEdgeTextBbox] = useState<Rect>({ x: 0, y: 0, width: 0, height: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
if (edgeRef.current) {
|
||||
const textBbox = edgeRef.current.getBBox();
|
||||
setEdgeTextBbox({
|
||||
x: textBbox.x,
|
||||
y: textBbox.y,
|
||||
width: textBbox.width,
|
||||
height: textBbox.height,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (typeof label === 'undefined' || !label) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<g transform={`translate(${x - edgeTextBbox.width / 2} ${y - edgeTextBbox.height / 2})`}>
|
||||
{labelShowBg && (
|
||||
<rect
|
||||
width={edgeTextBbox.width + 2 * textBgPadding}
|
||||
x={-textBgPadding}
|
||||
y={-textBgPadding}
|
||||
height={edgeTextBbox.height + 2 * textBgPadding}
|
||||
className="react-flow__edge-textbg"
|
||||
style={labelBgStyle}
|
||||
/>
|
||||
)}
|
||||
<text className="react-flow__edge-text" y={edgeTextBbox.height / 2} dy="0.3em" ref={edgeRef} style={labelStyle}>
|
||||
{label}
|
||||
</text>
|
||||
</g>
|
||||
);
|
||||
});
|
||||
@@ -1,17 +1,36 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { EdgeProps } from '../../types';
|
||||
|
||||
export default memo(
|
||||
({ sourceX, sourceY, targetX, targetY, style = {} }: EdgeProps) => {
|
||||
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style = {} }: EdgeProps) => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
|
||||
return (
|
||||
<path
|
||||
{...style}
|
||||
d={`M ${sourceX},${sourceY}L ${sourceX},${centerY}L ${targetX},${centerY}L ${targetX},${targetY}`}
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||
|
||||
const text = label ? (
|
||||
<EdgeText
|
||||
x={centerX}
|
||||
y={centerY}
|
||||
label={label}
|
||||
labelStyle={labelStyle}
|
||||
labelShowBg={labelShowBg}
|
||||
labelBgStyle={labelBgStyle}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<g>
|
||||
<path
|
||||
style={style}
|
||||
className="react-flow__edge-path"
|
||||
d={`M ${sourceX},${sourceY}L ${sourceX},${centerY}L ${targetX},${centerY}L ${targetX},${targetY}`}
|
||||
/>
|
||||
{text}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
import React, { memo } from 'react';
|
||||
|
||||
import EdgeText from './EdgeText';
|
||||
import { EdgeProps } from '../../types';
|
||||
|
||||
export default memo(
|
||||
({ sourceX, sourceY, targetX, targetY, style = {} }: EdgeProps) => {
|
||||
({ sourceX, sourceY, targetX, targetY, label, labelStyle, labelShowBg, labelBgStyle, style = {} }: 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 text = label ? (
|
||||
<EdgeText
|
||||
x={centerX}
|
||||
y={centerY}
|
||||
label={label}
|
||||
labelStyle={labelStyle}
|
||||
labelShowBg={labelShowBg}
|
||||
labelBgStyle={labelBgStyle}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<path {...style} d={`M ${sourceX},${sourceY}L ${targetX},${targetY}`} />
|
||||
<g>
|
||||
<path style={style} className="react-flow__edge-path" d={`M ${sourceX},${sourceY}L ${targetX},${targetY}`} />
|
||||
{text}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { memo, ComponentType } from 'react';
|
||||
import React, { memo, ComponentType, CSSProperties } from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import store from '../../store';
|
||||
@@ -9,6 +9,10 @@ interface EdgeWrapperProps {
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
type: any;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle: CSSProperties;
|
||||
onClick: (edge: Edge) => void;
|
||||
animated: boolean;
|
||||
selected: boolean;
|
||||
@@ -17,7 +21,21 @@ interface EdgeWrapperProps {
|
||||
|
||||
export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
||||
const EdgeWrapper = memo(
|
||||
({ id, source, target, type, animated, selected, onClick, isInteractive, ...rest }: EdgeWrapperProps) => {
|
||||
({
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
type,
|
||||
animated,
|
||||
selected,
|
||||
onClick,
|
||||
isInteractive,
|
||||
label,
|
||||
labelStyle,
|
||||
labelShowBg,
|
||||
labelBgStyle,
|
||||
...rest
|
||||
}: EdgeWrapperProps) => {
|
||||
const edgeClasses = cx('react-flow__edge', { selected, animated });
|
||||
const onEdgeClick = (): void => {
|
||||
if (!isInteractive) {
|
||||
@@ -38,6 +56,10 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
|
||||
animated={animated}
|
||||
selected={selected}
|
||||
onClick={onClick}
|
||||
label={label}
|
||||
labelStyle={labelStyle}
|
||||
labelShowBg={labelShowBg}
|
||||
labelBgStyle={labelBgStyle}
|
||||
{...rest}
|
||||
/>
|
||||
</g>
|
||||
|
||||
@@ -83,7 +83,7 @@ function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleE
|
||||
if (bounds.length === 1 || !handleId) {
|
||||
handle = bounds[0];
|
||||
} else if (handleId) {
|
||||
handle = bounds.find(d => d.id === handleId);
|
||||
handle = bounds.find((d) => d.id === handleId);
|
||||
}
|
||||
|
||||
return handle;
|
||||
@@ -123,8 +123,8 @@ function renderEdge(
|
||||
const [sourceId, sourceHandleId] = edge.source.split('__');
|
||||
const [targetId, targetHandleId] = edge.target.split('__');
|
||||
|
||||
const sourceNode = nodes.find(n => n.id === sourceId);
|
||||
const targetNode = nodes.find(n => n.id === targetId);
|
||||
const sourceNode = nodes.find((n) => n.id === sourceId);
|
||||
const targetNode = nodes.find((n) => n.id === targetId);
|
||||
|
||||
if (!sourceNode) {
|
||||
throw new Error(`couldn't create edge for source id: ${sourceId}`);
|
||||
@@ -155,7 +155,7 @@ function renderEdge(
|
||||
);
|
||||
|
||||
const isSelected = (selectedElements as Edge[]).some(
|
||||
elm => isEdge(elm) && elm.source === sourceId && elm.target === targetId
|
||||
(elm) => isEdge(elm) && elm.source === sourceId && elm.target === targetId
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -166,6 +166,10 @@ function renderEdge(
|
||||
onClick={props.onElementClick}
|
||||
selected={isSelected}
|
||||
animated={edge.animated}
|
||||
label={edge.label}
|
||||
labelStyle={edge.labelStyle}
|
||||
labelShowBg={edge.labelShowBg}
|
||||
labelBgStyle={edge.labelBgStyle}
|
||||
style={edge.style}
|
||||
source={sourceId}
|
||||
target={targetId}
|
||||
@@ -191,7 +195,7 @@ const EdgeRenderer = memo((props: EdgeRendererProps) => {
|
||||
connectionPosition: { x, y },
|
||||
selectedElements,
|
||||
isInteractive,
|
||||
} = useStoreState(s => ({
|
||||
} = useStoreState((s) => ({
|
||||
transform: s.transform,
|
||||
edges: s.edges,
|
||||
nodes: s.nodes,
|
||||
|
||||
+20
-8
@@ -46,16 +46,9 @@
|
||||
}
|
||||
|
||||
.react-flow__edge {
|
||||
fill: none;
|
||||
stroke: #bbb;
|
||||
stroke-width: 2;
|
||||
pointer-events: all;
|
||||
|
||||
&.selected {
|
||||
stroke: #555;
|
||||
}
|
||||
|
||||
&.animated {
|
||||
&.animated path {
|
||||
stroke-dasharray: 5;
|
||||
animation: dashdraw 0.5s linear infinite;
|
||||
}
|
||||
@@ -66,6 +59,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow__edge-path {
|
||||
fill: none;
|
||||
stroke: #bbb;
|
||||
stroke-width: 2;
|
||||
|
||||
&.selected {
|
||||
stroke: #555;
|
||||
}
|
||||
}
|
||||
|
||||
.react-flow__edge-text {
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.react-flow__edge-textbg {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
@keyframes dashdraw {
|
||||
from {
|
||||
stroke-dashoffset: 10;
|
||||
|
||||
+24
-3
@@ -1,4 +1,4 @@
|
||||
import { CSSProperties, SVGAttributes } from 'react';
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
export type ElementId = string;
|
||||
|
||||
@@ -63,7 +63,11 @@ export interface Edge {
|
||||
type?: string;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
style?: SVGAttributes<{}>;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
style?: CSSProperties;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
@@ -72,7 +76,11 @@ export interface EdgeProps {
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
style?: SVGAttributes<{}>;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
export interface EdgeBezierProps extends EdgeProps {
|
||||
@@ -151,7 +159,20 @@ export interface EdgeCompProps {
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
type: any;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
onClick?: (edge: Edge) => void;
|
||||
animated?: boolean;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
export interface EdgeTextProps {
|
||||
x: number;
|
||||
y: number;
|
||||
label?: string;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user