diff --git a/README.md b/README.md index d7b513e9..b8008b27 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ Node example: `{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { - `className`: additional class name - `targetPosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'top' - `sourcePosition`: 'left' | 'right' | 'top' | 'bottom' handle position - default: 'bottom' +- `isHidden`: if `true` node gets not rendered ## 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 - `arrowHeadType`: 'arrow' or 'arrowclosed' - defines the arrowhead of the edge - `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). diff --git a/cypress/integration/flow/hidden.spec.js b/cypress/integration/flow/hidden.spec.js new file mode 100644 index 00000000..ff534735 --- /dev/null +++ b/cypress/integration/flow/hidden.spec.js @@ -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); + }); +}); diff --git a/cypress/integration/flow/inactive.spec.js b/cypress/integration/flow/interaction.spec.js similarity index 100% rename from cypress/integration/flow/inactive.spec.js rename to cypress/integration/flow/interaction.spec.js diff --git a/example/src/Hidden/index.js b/example/src/Hidden/index.js new file mode 100644 index 00000000..404567f6 --- /dev/null +++ b/example/src/Hidden/index.js @@ -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 ( + + + + + + + + isHidden + setIsHidden(evt.target.checked)} + className="react-flow__ishidden" + /> + + + + + ); +}; + +export default HiddenFlow; diff --git a/example/src/index.js b/example/src/index.js index a0bcd254..5eb71cfa 100644 --- a/example/src/index.js +++ b/example/src/index.js @@ -12,6 +12,7 @@ import Edges from './Edges'; import Validation from './Validation'; import Horizontal from './Horizontal'; import Provider from './Provider'; +import Hidden from './Hidden'; import './index.css'; @@ -51,6 +52,11 @@ const routes = [ component: Stress, label: 'Stress', }, + { + path: '/interaction', + component: Interaction, + label: 'Interaction', + }, { path: '/basic', component: Basic, @@ -60,9 +66,8 @@ const routes = [ component: Empty, }, { - path: '/interaction', - component: Interaction, - label: 'Interaction', + path: '/hidden', + component: Hidden, }, ]; diff --git a/src/additional-components/MiniMap/index.tsx b/src/additional-components/MiniMap/index.tsx index 6c109d7f..50d2ae0e 100644 --- a/src/additional-components/MiniMap/index.tsx +++ b/src/additional-components/MiniMap/index.tsx @@ -63,9 +63,11 @@ const MiniMap = ({ style={style} className={mapClasses} > - {nodes.map((node) => ( - - ))} + {nodes + .filter((node) => !node.isHidden) + .map((node) => ( + + ))} ) => { @@ -36,9 +37,15 @@ export default (EdgeComponent: ComponentType) => { labelShowBg, labelBgStyle, className, + isHidden, ...rest }: EdgeWrapperProps) => { const setSelectedElements = useStoreActions((a) => a.setSelectedElements); + + if (isHidden) { + return null; + } + const edgeClasses = cc(['react-flow__edge', `react-flow__edge-${type}`, className, { selected, animated }]); const edgeGroupStyle: CSSProperties = { pointerEvents: elementsSelectable ? 'all' : 'none', diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 389b5de1..b15f9605 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -184,6 +184,7 @@ export default (NodeComponent: ComponentType) => { selectNodesOnDrag, sourcePosition, targetPosition, + isHidden, }: WrapNodeProps) => { const updateNodeDimensions = useStoreActions((a) => a.updateNodeDimensions); const setSelectedElements = useStoreActions((a) => a.setSelectedElements); @@ -243,13 +244,6 @@ export default (NodeComponent: ComponentType) => { return noop; }, [isSelectable, isDraggable, id, type]); - const nodeStyle: CSSProperties = { - zIndex: selected ? 10 : 3, - transform: `translate(${xPos}px,${yPos}px)`, - pointerEvents: isSelectable || isDraggable ? 'all' : 'none', - ...style, - }; - useEffect(() => { if (nodeElement.current) { updateNodeDimensions({ id, nodeElement: nodeElement.current }); @@ -271,6 +265,17 @@ export default (NodeComponent: ComponentType) => { return; }, [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 ( ); } diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 079d6d23..34921f93 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -59,6 +59,7 @@ function renderNode( sourcePosition={node.sourcePosition} targetPosition={node.targetPosition} selectNodesOnDrag={props.selectNodesOnDrag} + isHidden={node.isHidden} /> ); } diff --git a/src/hooks/useElementUpdater.ts b/src/hooks/useElementUpdater.ts index 51877d11..bf0ca511 100644 --- a/src/hooks/useElementUpdater.ts +++ b/src/hooks/useElementUpdater.ts @@ -29,6 +29,7 @@ const useElementUpdater = (elements: Elements): void => { : existingNode.style; const className = existingNode.className === propNode.className ? existingNode.className : propNode.className; + const isHidden = existingNode.isHidden === propNode.isHidden ? existingNode.isHidden : propNode.isHidden; const positionChanged = existingNode.position.x !== propNode.position.x || existingNode.position.y !== propNode.position.y; @@ -54,6 +55,10 @@ const useElementUpdater = (elements: Elements): void => { nodeProps.className = className; } + if (typeof isHidden !== 'undefined') { + nodeProps.isHidden = isHidden; + } + return nodeProps; } diff --git a/src/types/index.ts b/src/types/index.ts index 269f83fa..6bbf076d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -40,6 +40,7 @@ export interface Node { className?: string; targetPosition?: Position; sourcePosition?: Position; + isHidden?: boolean; } export enum ArrowHeadType { @@ -59,6 +60,7 @@ export interface Edge { style?: CSSProperties; animated?: boolean; arrowHeadType?: ArrowHeadType; + isHidden?: boolean; } export enum BackgroundVariant { @@ -151,6 +153,7 @@ export interface WrapNodeProps { className?: string; sourcePosition?: Position; targetPosition?: Position; + isHidden?: boolean; } export type FitViewParams = {