Merge pull request #335 from wbkd/develop

Develop
This commit is contained in:
Moritz
2020-07-15 10:35:24 +02:00
committed by GitHub
22 changed files with 175 additions and 77 deletions
+2
View File
@@ -155,6 +155,7 @@ Node example: `{ id: '1', type: 'input', data: { label: 'Node 1' }, position: {
- `className`: additional class name - `className`: additional class name
- `targetPosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top' - `targetPosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top'
- `sourcePosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'bottom' - `sourcePosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'bottom'
- `isHidden`: if `true` node gets not rendered
## Node Types & Custom Nodes ## Node Types & Custom Nodes
@@ -251,6 +252,7 @@ If you wanted to display this edge, you would need a node with id = 1 (source no
- `labelBgStyle`: css properties for the text background - `labelBgStyle`: css properties for the text background
- `arrowHeadType`: 'arrow' or 'arrowclosed' - defines the arrowhead of the edge - `arrowHeadType`: 'arrow' or 'arrowclosed' - defines the arrowhead of the edge
- `markerEndId`: custom marker end url - if this is used `arrowHeadType` gets ignored - `markerEndId`: custom marker end url - if this is used `arrowHeadType` gets ignored
- `isHidden`: if `true` edge gets not rendered
You can find an example with lots of different edges in the [edges example](https://react-flow.netlify.app/edges). You can find an example with lots of different edges in the [edges example](https://react-flow.netlify.app/edges).
+1 -1
View File
@@ -1,4 +1,4 @@
describe('Basic Graph Rendering', () => { describe('Basic Flow Rendering', () => {
it('renders a flow with three nodes', () => { it('renders a flow with three nodes', () => {
cy.visit('/basic'); cy.visit('/basic');
+2 -2
View File
@@ -1,5 +1,5 @@
describe('Custom Node Graph Rendering', () => { describe('Custom Node Flow Rendering', () => {
it('renders a graph', () => { it('renders a flow', () => {
cy.visit('/custom-node'); cy.visit('/custom-node');
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
+1 -1
View File
@@ -1,5 +1,5 @@
describe('Empty Flow Rendering', () => { describe('Empty Flow Rendering', () => {
it('renders an empty graph', () => { it('renders an empty flow', () => {
cy.visit('/empty'); cy.visit('/empty');
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
+30
View File
@@ -0,0 +1,30 @@
describe('Hidden Flow Rendering', () => {
it('renders the initial flow', () => {
cy.visit('/hidden');
cy.get('.react-flow__renderer');
cy.get('.react-flow__node').should('have.length', 4);
cy.get('.react-flow__edge').should('have.length', 3);
cy.get('.react-flow__minimap-node').should('have.length', 4);
});
it('toggles isHidden mode', () => {
cy.get('.react-flow__ishidden').click();
});
it('renders empty flow', () => {
cy.get('.react-flow__node').should('not.exist');
cy.get('.react-flow__edge').should('not.exist');
cy.get('.react-flow__minimap-node').should('not.exist');
});
it('toggles isHidden mode again', () => {
cy.get('.react-flow__ishidden').click();
});
it('renders initial flow', () => {
cy.get('.react-flow__node').should('have.length', 4);
cy.get('.react-flow__edge').should('have.length', 3);
cy.get('.react-flow__minimap-node').should('have.length', 4);
});
});
@@ -1,5 +1,5 @@
describe('Interaction Graph Rendering', () => { describe('Interaction Flow Rendering', () => {
it('renders a graph', () => { it('renders initial flow', () => {
cy.visit('/interaction'); cy.visit('/interaction');
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
+2 -2
View File
@@ -1,5 +1,5 @@
describe('Overview Graph Rendering', () => { describe('Overview Flow Rendering', () => {
it('renders a graph', () => { it('renders a flow', () => {
cy.visit('/'); cy.visit('/');
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
+2 -2
View File
@@ -1,5 +1,5 @@
describe('Stress Graph Rendering', () => { describe('Stress Flow Rendering', () => {
it('renders a graph', () => { it('renders initial flow', () => {
cy.visit('/stress'); cy.visit('/stress');
cy.get('.react-flow__renderer'); cy.get('.react-flow__renderer');
+11 -15
View File
@@ -2,9 +2,9 @@ import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, isNode, Background } from 'react-flow-renderer'; import ReactFlow, { removeElements, addEdge, isNode, Background } from 'react-flow-renderer';
const onNodeDragStop = node => console.log('drag stop', node); const onNodeDragStop = (node) => console.log('drag stop', node);
const onLoad = reactFlowInstance => console.log('graph loaded:', reactFlowInstance); const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = element => console.log('click', element); const onElementClick = (element) => console.log('click', element);
const initialElements = [ const initialElements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }, { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
@@ -17,20 +17,19 @@ const initialElements = [
const BasicFlow = () => { const BasicFlow = () => {
const [elements, setElements] = useState(initialElements); const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
setElements(els => removeElements(elementsToRemove, els)); const onConnect = (params) => setElements((els) => addEdge(params, els));
const onConnect = (params) => setElements(els => addEdge(params, els));
const updatePos = () => { const updatePos = () => {
setElements(elms => { setElements((elms) => {
return elms.map(el => { return elms.map((el) => {
if (isNode(el)) { if (isNode(el)) {
return { return {
...el, ...el,
position: { position: {
x: Math.random() * 400, x: Math.random() * 400,
y: Math.random() * 400 y: Math.random() * 400,
} },
}; };
} }
@@ -54,14 +53,11 @@ const BasicFlow = () => {
> >
<Background variant="lines" /> <Background variant="lines" />
<button <button onClick={updatePos} style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}>
onClick={updatePos}
style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}
>
change pos change pos
</button> </button>
</ReactFlow> </ReactFlow>
); );
} };
export default BasicFlow; export default BasicFlow;
+1 -1
View File
@@ -6,7 +6,7 @@ import ColorSelectorNode from './ColorSelectorNode';
const onNodeDragStop = (node) => console.log('drag stop', node); const onNodeDragStop = (node) => console.log('drag stop', node);
const onElementClick = (element) => console.log('click', element); const onElementClick = (element) => console.log('click', element);
const onLoad = (reactFlowInstance) => console.log('graph loaded:', reactFlowInstance); const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const initBgColor = '#f0e742'; const initBgColor = '#f0e742';
+10 -15
View File
@@ -2,23 +2,22 @@ import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer'; import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background } from 'react-flow-renderer';
const onNodeDragStop = node => console.log('drag stop', node); const onNodeDragStop = (node) => console.log('drag stop', node);
const onLoad = reactFlowInstance => console.log('graph loaded:', reactFlowInstance); const onLoad = (reactFlowInstance) => console.log('flow loaded:', reactFlowInstance);
const onElementClick = element => console.log('click', element); const onElementClick = (element) => console.log('click', element);
const EmptyFlow = () => { const EmptyFlow = () => {
const [elements, setElements] = useState([]); const [elements, setElements] = useState([]);
const onElementsRemove = (elementsToRemove) => const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
setElements(els => removeElements(elementsToRemove, els)); const onConnect = (params) => setElements((els) => addEdge(params, els));
const onConnect = (params) => setElements(els => addEdge(params, els));
const addRandomNode = () => { const addRandomNode = () => {
const nodeId = (elements.length + 1).toString(); const nodeId = (elements.length + 1).toString();
const newNode = { const newNode = {
id: nodeId, id: nodeId,
data: { label: `Node: ${nodeId}` }, data: { label: `Node: ${nodeId}` },
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight } position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight },
}; };
setElements(els => els.concat(newNode)); setElements((els) => els.concat(newNode));
}; };
return ( return (
@@ -27,22 +26,18 @@ const EmptyFlow = () => {
onLoad={onLoad} onLoad={onLoad}
onElementClick={onElementClick} onElementClick={onElementClick}
onElementsRemove={onElementsRemove} onElementsRemove={onElementsRemove}
onConnect={p => onConnect(p)} onConnect={(p) => onConnect(p)}
onNodeDragStop={onNodeDragStop} onNodeDragStop={onNodeDragStop}
> >
<MiniMap /> <MiniMap />
<Controls /> <Controls />
<Background variant="lines" /> <Background variant="lines" />
<button <button type="button" onClick={addRandomNode} style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
type="button"
onClick={addRandomNode}
style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}
>
add node add node
</button> </button>
</ReactFlow> </ReactFlow>
); );
} };
export default EmptyFlow; export default EmptyFlow;
+55
View File
@@ -0,0 +1,55 @@
import React, { useState } from 'react';
import ReactFlow, { addEdge, MiniMap, Controls } from 'react-flow-renderer';
import { useEffect } from 'react';
const initialElements = [
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
{ 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' },
{ id: 'e1-3', source: '1', target: '3' },
{ id: 'e3-4', source: '3', target: '4' },
];
const HiddenFlow = () => {
const [elements, setElements] = useState(initialElements);
const [isHidden, setIsHidden] = useState(false);
const onConnect = (params) => setElements((els) => addEdge(params, els));
useEffect(() => {
setElements((els) =>
els.map((e) => {
e.isHidden = isHidden;
return e;
})
);
}, [isHidden]);
console.log(elements);
return (
<ReactFlow elements={elements} onConnect={onConnect}>
<MiniMap />
<Controls />
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
<div>
<label htmlFor="ishidden">
isHidden
<input
id="ishidden"
type="checkbox"
checked={isHidden}
onChange={(evt) => setIsHidden(evt.target.checked)}
className="react-flow__ishidden"
/>
</label>
</div>
</div>
</ReactFlow>
);
};
export default HiddenFlow;
+1 -1
View File
@@ -7,7 +7,7 @@ const onNodeDragStop = (node) => console.log('drag stop', node);
const onElementClick = (element) => console.log('click', element); const onElementClick = (element) => console.log('click', element);
const onSelectionChange = (elements) => console.log('selection change', elements); const onSelectionChange = (elements) => console.log('selection change', elements);
const onLoad = (reactFlowInstance) => { const onLoad = (reactFlowInstance) => {
console.log('graph loaded:', reactFlowInstance); console.log('flow loaded:', reactFlowInstance);
reactFlowInstance.fitView(); reactFlowInstance.fitView();
}; };
+13 -22
View File
@@ -3,30 +3,29 @@ import React, { useState } from 'react';
import ReactFlow, { removeElements, addEdge, MiniMap, isNode, Controls, Background } from 'react-flow-renderer'; import ReactFlow, { removeElements, addEdge, MiniMap, isNode, Controls, Background } from 'react-flow-renderer';
import { getElements } from './utils'; import { getElements } from './utils';
const onLoad = reactFlowInstance => { const onLoad = (reactFlowInstance) => {
reactFlowInstance.fitView(); reactFlowInstance.fitView();
console.log(reactFlowInstance.getElements()); console.log(reactFlowInstance.getElements());
} };
const initialElements = getElements(10, 10); const initialElements = getElements(10, 10);
const StressGraph = () => { const StressFlow = () => {
const [elements, setElements] = useState(initialElements); const [elements, setElements] = useState(initialElements);
const onElementsRemove = (elementsToRemove) => const onElementsRemove = (elementsToRemove) => setElements((els) => removeElements(elementsToRemove, els));
setElements(els => removeElements(elementsToRemove, els)); const onConnect = (params) => setElements((els) => addEdge(params, els));
const onConnect = (params) => setElements(els => addEdge(params, els));
const updatePos = () => { const updatePos = () => {
setElements(elms => { setElements((elms) => {
return elms.map(el => { return elms.map((el) => {
if (isNode(el)) { if (isNode(el)) {
return { return {
...el, ...el,
position: { position: {
x: Math.random() * window.innerWidth, x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight y: Math.random() * window.innerHeight,
} },
}; };
} }
@@ -36,24 +35,16 @@ const StressGraph = () => {
}; };
return ( return (
<ReactFlow <ReactFlow elements={elements} onLoad={onLoad} onElementsRemove={onElementsRemove} onConnect={onConnect}>
elements={elements}
onLoad={onLoad}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
>
<MiniMap /> <MiniMap />
<Controls /> <Controls />
<Background /> <Background />
<button <button onClick={updatePos} style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}>
onClick={updatePos}
style={{ position: 'absolute', right: 10, top: 30, zIndex: 4 }}
>
change pos change pos
</button> </button>
</ReactFlow> </ReactFlow>
); );
} };
export default StressGraph; export default StressFlow;
+8 -3
View File
@@ -12,6 +12,7 @@ import Edges from './Edges';
import Validation from './Validation'; import Validation from './Validation';
import Horizontal from './Horizontal'; import Horizontal from './Horizontal';
import Provider from './Provider'; import Provider from './Provider';
import Hidden from './Hidden';
import './index.css'; import './index.css';
@@ -51,6 +52,11 @@ const routes = [
component: Stress, component: Stress,
label: 'Stress', label: 'Stress',
}, },
{
path: '/interaction',
component: Interaction,
label: 'Interaction',
},
{ {
path: '/basic', path: '/basic',
component: Basic, component: Basic,
@@ -60,9 +66,8 @@ const routes = [
component: Empty, component: Empty,
}, },
{ {
path: '/interaction', path: '/hidden',
component: Interaction, component: Hidden,
label: 'Interaction',
}, },
]; ];
+5 -3
View File
@@ -63,9 +63,11 @@ const MiniMap = ({
style={style} style={style}
className={mapClasses} className={mapClasses}
> >
{nodes.map((node) => ( {nodes
<MiniMapNode key={node.id} node={node} color={nodeColorFunc(node)} borderRadius={nodeBorderRadius} /> .filter((node) => !node.isHidden)
))} .map((node) => (
<MiniMapNode key={node.id} node={node} color={nodeColorFunc(node)} borderRadius={nodeBorderRadius} />
))}
<path <path
className="react-flow__minimap-mask" className="react-flow__minimap-mask"
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
+7
View File
@@ -18,6 +18,7 @@ interface EdgeWrapperProps {
animated?: boolean; animated?: boolean;
selected: boolean; selected: boolean;
elementsSelectable: boolean; elementsSelectable: boolean;
isHidden?: boolean;
} }
export default (EdgeComponent: ComponentType<EdgeCompProps>) => { export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
@@ -36,9 +37,15 @@ export default (EdgeComponent: ComponentType<EdgeCompProps>) => {
labelShowBg, labelShowBg,
labelBgStyle, labelBgStyle,
className, className,
isHidden,
...rest ...rest
}: EdgeWrapperProps) => { }: EdgeWrapperProps) => {
const setSelectedElements = useStoreActions((a) => a.setSelectedElements); const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
if (isHidden) {
return null;
}
const edgeClasses = cc(['react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated }]); const edgeClasses = cc(['react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated }]);
const edgeGroupStyle: CSSProperties = { const edgeGroupStyle: CSSProperties = {
pointerEvents: elementsSelectable ? 'all' : 'none', pointerEvents: elementsSelectable ? 'all' : 'none',
+12 -7
View File
@@ -184,6 +184,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
selectNodesOnDrag, selectNodesOnDrag,
sourcePosition, sourcePosition,
targetPosition, targetPosition,
isHidden,
}: WrapNodeProps) => { }: WrapNodeProps) => {
const updateNodeDimensions = useStoreActions((a) => a.updateNodeDimensions); const updateNodeDimensions = useStoreActions((a) => a.updateNodeDimensions);
const setSelectedElements = useStoreActions((a) => a.setSelectedElements); const setSelectedElements = useStoreActions((a) => a.setSelectedElements);
@@ -243,13 +244,6 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
return noop; return noop;
}, [isSelectable, isDraggable, id, type]); }, [isSelectable, isDraggable, id, type]);
const nodeStyle: CSSProperties = {
zIndex: selected ? 10 : 3,
transform: `translate(${xPos}px,${yPos}px)`,
pointerEvents: isSelectable || isDraggable ? 'all' : 'none',
...style,
};
useEffect(() => { useEffect(() => {
if (nodeElement.current) { if (nodeElement.current) {
updateNodeDimensions({ id, nodeElement: nodeElement.current }); updateNodeDimensions({ id, nodeElement: nodeElement.current });
@@ -271,6 +265,17 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
return; return;
}, [id]); }, [id]);
console.log(isHidden);
if (isHidden) {
return null;
}
const nodeStyle: CSSProperties = {
zIndex: selected ? 10 : 3,
transform: `translate(${xPos}px,${yPos}px)`,
pointerEvents: isSelectable || isDraggable ? 'all' : 'none',
...style,
};
return ( return (
<DraggableCore <DraggableCore
+1
View File
@@ -187,6 +187,7 @@ function renderEdge(
targetPosition={targetPosition} targetPosition={targetPosition}
elementsSelectable={elementsSelectable} elementsSelectable={elementsSelectable}
markerEndId={props.markerEndId} markerEndId={props.markerEndId}
isHidden={edge.isHidden}
/> />
); );
} }
+1
View File
@@ -59,6 +59,7 @@ function renderNode(
sourcePosition={node.sourcePosition} sourcePosition={node.sourcePosition}
targetPosition={node.targetPosition} targetPosition={node.targetPosition}
selectNodesOnDrag={props.selectNodesOnDrag} selectNodesOnDrag={props.selectNodesOnDrag}
isHidden={node.isHidden}
/> />
); );
} }
+5
View File
@@ -29,6 +29,7 @@ const useElementUpdater = (elements: Elements): void => {
: existingNode.style; : existingNode.style;
const className = existingNode.className === propNode.className ? existingNode.className : propNode.className; const className = existingNode.className === propNode.className ? existingNode.className : propNode.className;
const isHidden = existingNode.isHidden === propNode.isHidden ? existingNode.isHidden : propNode.isHidden;
const positionChanged = const positionChanged =
existingNode.position.x !== propNode.position.x || existingNode.position.y !== propNode.position.y; existingNode.position.x !== propNode.position.x || existingNode.position.y !== propNode.position.y;
@@ -54,6 +55,10 @@ const useElementUpdater = (elements: Elements): void => {
nodeProps.className = className; nodeProps.className = className;
} }
if (typeof isHidden !== 'undefined') {
nodeProps.isHidden = isHidden;
}
return nodeProps; return nodeProps;
} }
+3
View File
@@ -40,6 +40,7 @@ export interface Node {
className?: string; className?: string;
targetPosition?: Position; targetPosition?: Position;
sourcePosition?: Position; sourcePosition?: Position;
isHidden?: boolean;
} }
export enum ArrowHeadType { export enum ArrowHeadType {
@@ -59,6 +60,7 @@ export interface Edge {
style?: CSSProperties; style?: CSSProperties;
animated?: boolean; animated?: boolean;
arrowHeadType?: ArrowHeadType; arrowHeadType?: ArrowHeadType;
isHidden?: boolean;
} }
export enum BackgroundVariant { export enum BackgroundVariant {
@@ -151,6 +153,7 @@ export interface WrapNodeProps {
className?: string; className?: string;
sourcePosition?: Position; sourcePosition?: Position;
targetPosition?: Position; targetPosition?: Position;
isHidden?: boolean;
} }
export type FitViewParams = { export type FitViewParams = {