From 2fee4c35ae9259c06fe231c50f917cd0f2167961 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 25 Jul 2020 13:38:55 +0200 Subject: [PATCH 1/5] feat(onLoad): add setTransform function #358 --- src/container/GraphView/index.tsx | 2 ++ src/types/index.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 080d3921..b8503fc7 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -183,6 +183,8 @@ const GraphView = memo( zoomOut: () => zoom(-0.2), project, getElements, + setTransform: (transform: FlowTransform) => + setInitTransform({ x: transform.x, y: transform.y, k: transform.zoom }), }); } diff --git a/src/types/index.ts b/src/types/index.ts index a362abaf..e4ce87ab 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -175,6 +175,7 @@ type OnLoadParams = { fitView: FitViewFunc; project: ProjectFunc; getElements: () => Elements; + setTransform: (transform: FlowTransform) => void; }; export type OnLoadFunc = (params: OnLoadParams) => void; From 3da7efae35c6edc3d11a499857703a4869502b14 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 25 Jul 2020 14:04:08 +0200 Subject: [PATCH 2/5] refactor(elementUpdater): dont create additional nodes object --- src/hooks/useElementUpdater.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/hooks/useElementUpdater.ts b/src/hooks/useElementUpdater.ts index 21c06677..a2a73b65 100644 --- a/src/hooks/useElementUpdater.ts +++ b/src/hooks/useElementUpdater.ts @@ -13,10 +13,8 @@ const useElementUpdater = (elements: Elements): void => { const setEdges = useStoreActions((a) => a.setEdges); useEffect(() => { - const nodes: Node[] = elements.filter(isNode); - const edges: Edge[] = elements.filter(isEdge).map((e) => parseElement(e) as Edge); - - const nextNodes: Node[] = nodes.map((propNode) => { + const nextEdges: Edge[] = elements.filter(isEdge).map((e) => parseElement(e) as Edge); + const nextNodes: Node[] = elements.filter(isNode).map((propNode) => { const existingNode = stateNodes.find((n) => n.id === propNode.id); if (existingNode) { @@ -83,14 +81,14 @@ const useElementUpdater = (elements: Elements): void => { }); const nodesChanged: boolean = !isEqual(stateNodes, nextNodes); - const edgesChanged: boolean = !isEqual(stateEdges, edges); + const edgesChanged: boolean = !isEqual(stateEdges, nextEdges); if (nodesChanged) { setNodes(nextNodes); } if (edgesChanged) { - setEdges(edges); + setEdges(nextEdges); } }, [elements, stateNodes, stateEdges]); }; From 0d2bdf7ce8ffd56dffbf4569f75b27c29fe41339 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 25 Jul 2020 15:06:58 +0200 Subject: [PATCH 3/5] refactor(background): use repeating inline svg as bg pattern #356 --- .../Background/index.tsx | 19 +++++---- .../Background/style.css | 2 + src/additional-components/Background/utils.ts | 40 ++++--------------- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/additional-components/Background/index.tsx b/src/additional-components/Background/index.tsx index 86a24a09..685446e6 100644 --- a/src/additional-components/Background/index.tsx +++ b/src/additional-components/Background/index.tsx @@ -21,8 +21,6 @@ const defaultColors = { const Background = memo( ({ variant = BackgroundVariant.Dots, gap = 24, size = 0.5, color, style, className }: BackgroundProps) => { - const width = useStoreState((s) => s.width); - const height = useStoreState((s) => s.height); const [x, y, scale] = useStoreState((s) => s.transform); const bgClasses = cc(['react-flow__background', className]); @@ -32,15 +30,22 @@ const Background = memo( const yOffset = y % scaledGap; const isLines = variant === BackgroundVariant.Lines; const path = isLines - ? createGridLinesPath(width, height, xOffset, yOffset, scaledGap) - : createGridDotsPath(width, height, xOffset, yOffset, scaledGap, size); + ? createGridLinesPath(xOffset, yOffset, scaledGap) + : createGridDotsPath(xOffset, yOffset, scaledGap, size); const fill = isLines ? 'none' : bgColor; const stroke = isLines ? bgColor : 'none'; + const bg = ``; + return ( - - - +
); } ); diff --git a/src/additional-components/Background/style.css b/src/additional-components/Background/style.css index b96234b9..9343180d 100644 --- a/src/additional-components/Background/style.css +++ b/src/additional-components/Background/style.css @@ -2,4 +2,6 @@ position: absolute; top: 0; left: 0; + width: 100%; + height: 100%; } \ No newline at end of file diff --git a/src/additional-components/Background/utils.ts b/src/additional-components/Background/utils.ts index cf938870..c9b33e3a 100644 --- a/src/additional-components/Background/utils.ts +++ b/src/additional-components/Background/utils.ts @@ -1,37 +1,13 @@ -export const createGridLinesPath = ( - width: number, - height: number, - xOffset: number, - yOffset: number, - gap: number -): string => { - const lineCountX = Math.ceil(width / gap) + 1; - const lineCountY = Math.ceil(height / gap) + 1; +export const createGridLinesPath = (xOffset: number, yOffset: number, scaledGap: number): string => { + const x = xOffset < 0 ? scaledGap + xOffset : xOffset; + const y = yOffset < 0 ? scaledGap + yOffset : yOffset; - const xValues = Array.from({ length: lineCountX }, (_, i) => `M${i * gap + xOffset} 0 V${height}`); - const yValues = Array.from({ length: lineCountY }, (_, i) => `M0 ${i * gap + yOffset} H${width}`); - - return [...xValues, ...yValues].join(' '); + return `M${x} 0 V${scaledGap} M0 ${y} H${scaledGap}`; }; -export const createGridDotsPath = ( - width: number, - height: number, - xOffset: number, - yOffset: number, - gap: number, - size: number -): string => { - const lineCountX = Math.ceil(width / gap) + 1; - const lineCountY = Math.ceil(height / gap) + 1; +export const createGridDotsPath = (xOffset: number, yOffset: number, scaledGap: number, size: number): string => { + const x = xOffset < 0 ? scaledGap + xOffset : xOffset; + const y = yOffset < 0 ? scaledGap + yOffset : yOffset; - const values = Array.from({ length: lineCountX }, (_, col) => { - const x = col * gap + xOffset; - return Array.from({ length: lineCountY }, (_, row) => { - const y = row * gap + yOffset; - return `M${x} ${y - size} l${size} ${size} l${-size} ${size} l${-size} ${-size}z`; - }).join(' '); - }); - - return values.join(' '); + return `M${x} ${y - size} l${size} ${size} l${-size} ${size} l${-size} ${-size}z`; }; From 33a6dab7221731d9b95bc62c706f5b50252c7510 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 25 Jul 2020 15:18:06 +0200 Subject: [PATCH 4/5] docs(readme): add setTransform func --- README.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9fee03ef..ed6f8d43 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ React Flow is a library for building node-based graphs. You can easily implement - [ReactFlowProvider](#reactflowprovider) - [Styling](#styling) - [Helper Functions](#helper-functions) -- [Access Internal State](#access-internal-state) +- [Access Internal State and Actions](#access-internal-state-and-actions) - [Examples](#examples) - [Development](#development) - [Testing](#testing) @@ -156,6 +156,12 @@ Fits view port so that all nodes are visible `getElements = (): Elements` +### setTransform + +Sets position and zoom of the pane + +`setTransform = (transform: FlowTransform): void` + # Nodes There are three different [node types](#node-types--custom-nodes) (`default`, `input`, `output`) you can use. The node types differ in the number and types of handles. An input node has only a source handle, a default node has a source and a target and an output node has only a target handle. You create nodes by adding them to the `elements` array of the `ReactFlow` component. @@ -507,7 +513,7 @@ Returns all direct child nodes of the passed node You can use these function as seen in [this example](/example/src/Overview/index.js#L40-L41) or use your own ones. -# Access Internal State +# Access Internal State and Actions Under the hood React Flow uses [Easy Peasy](https://easy-peasy.now.sh/) for state handling. If you need to access the internal state you can use the `useStoreState` hook inside a child component of the `ReactFlow` component: @@ -530,6 +536,21 @@ const Flow = () => ( ); ``` +You will not need this in most cases but you can also use the internal actions that are defined in the [store](/src/store/index.ts): + +```javascript +import React, { useEffect } from 'react'; +import { useStoreActions } from 'react-flow-renderer' + +const TransformUpdater = ({ x, y, zoom }) => { + const setTransform = useStoreActions(a => a.setInitTransform); + + useEffect(() => { + setTransform({ x, y, k: zoom }) + }, [x, y, zoom]) +}); +``` + If you need more control you can wrap the `ReactFlow` component with the `ReactFlowProvider` component in order to be able to call `useStoreState` outside of the `ReactFlow` component. # Examples From cdfc3119426dff5942d9c68d61330759ffd026ba Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 26 Jul 2020 17:12:50 +0200 Subject: [PATCH 5/5] refactor(background): use background position for positioning dots and lines #356 --- cypress/integration/flow/basic.spec.js | 3 --- example/src/Overview/index.js | 2 +- .../Background/index.tsx | 25 ++++++++++--------- src/additional-components/Background/utils.ts | 14 +++-------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/cypress/integration/flow/basic.spec.js b/cypress/integration/flow/basic.spec.js index 121896d3..39209e42 100644 --- a/cypress/integration/flow/basic.spec.js +++ b/cypress/integration/flow/basic.spec.js @@ -13,9 +13,6 @@ describe('Basic Flow Rendering', () => { it('renders a grid', () => { cy.get('.react-flow__background'); - - const gridStroke = Cypress.$('.react-flow__background path').attr('stroke'); - expect(gridStroke).to.equal('#eee'); }); it('selects a node by click', () => { diff --git a/example/src/Overview/index.js b/example/src/Overview/index.js index d2247c38..c7e3c64f 100644 --- a/example/src/Overview/index.js +++ b/example/src/Overview/index.js @@ -136,7 +136,7 @@ const OverviewFlow = () => { }} /> - + ); }; diff --git a/src/additional-components/Background/index.tsx b/src/additional-components/Background/index.tsx index 685446e6..c655b544 100644 --- a/src/additional-components/Background/index.tsx +++ b/src/additional-components/Background/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, HTMLAttributes } from 'react'; +import React, { memo, useMemo, HTMLAttributes } from 'react'; import cc from 'classcat'; import { useStoreState } from '../../store/hooks'; @@ -24,26 +24,27 @@ const Background = memo( const [x, y, scale] = useStoreState((s) => s.transform); const bgClasses = cc(['react-flow__background', className]); - const bgColor = color ? color : defaultColors[variant]; const scaledGap = gap * scale; const xOffset = x % scaledGap; const yOffset = y % scaledGap; - const isLines = variant === BackgroundVariant.Lines; - const path = isLines - ? createGridLinesPath(xOffset, yOffset, scaledGap) - : createGridDotsPath(xOffset, yOffset, scaledGap, size); - const fill = isLines ? 'none' : bgColor; - const stroke = isLines ? bgColor : 'none'; - const bg = ``; + const bgSvgTile = useMemo(() => { + const isLines = variant === BackgroundVariant.Lines; + const bgColor = color ? color : defaultColors[variant]; + const path = isLines ? createGridLinesPath(scaledGap, size, bgColor) : createGridDotsPath(size, bgColor); + + return encodeURIComponent( + `${path}` + ); + }, [variant, scaledGap, size, color]); return (
); diff --git a/src/additional-components/Background/utils.ts b/src/additional-components/Background/utils.ts index c9b33e3a..c0b6d315 100644 --- a/src/additional-components/Background/utils.ts +++ b/src/additional-components/Background/utils.ts @@ -1,13 +1,7 @@ -export const createGridLinesPath = (xOffset: number, yOffset: number, scaledGap: number): string => { - const x = xOffset < 0 ? scaledGap + xOffset : xOffset; - const y = yOffset < 0 ? scaledGap + yOffset : yOffset; - - return `M${x} 0 V${scaledGap} M0 ${y} H${scaledGap}`; +export const createGridLinesPath = (scaledGap: number, strokeWidth: number, stroke: string): string => { + return ``; }; -export const createGridDotsPath = (xOffset: number, yOffset: number, scaledGap: number, size: number): string => { - const x = xOffset < 0 ? scaledGap + xOffset : xOffset; - const y = yOffset < 0 ? scaledGap + yOffset : yOffset; - - return `M${x} ${y - size} l${size} ${size} l${-size} ${size} l${-size} ${-size}z`; +export const createGridDotsPath = (size: number, fill: string): string => { + return ``; };