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}."
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ React Flow is a library for building node-based graphs. You can easily implement
|
||||
* **Plugin system:** [Mini map and graph controls](#plugins)
|
||||
* **Reliable**: Written in [Typescript](https://www.typescriptlang.org/) and tested with [cypress](https://www.cypress.io/)
|
||||
|
||||
In order to make this library as flexible as possible we don’t do any state updates besides the positions. This means that you need to pass all functions that change the displayed nodes and edges by yourself. You can implement your own ones or use the helper functions that come with the library.
|
||||
In order to make this library as flexible as possible we don’t do any state updates besides the positions. This means that you need to pass all functions that change the displayed nodes and edges by yourself. You can implement your own ones or use the helper functions that come with the library.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -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