From e01249a87c1e8f23864c2ccac05f16820a49f952 Mon Sep 17 00:00:00 2001 From: moklick Date: Sat, 9 Oct 2021 16:49:41 +0200 Subject: [PATCH] refactor(examples): hide old api examples --- example/.gitignore | 2 + example/src/Basic/index.tsx | 71 +++--- .../CustomConnectionLine/ConnectionLine.tsx | 19 -- example/src/CustomConnectionLine/index.tsx | 33 --- example/src/CustomNode/ColorSelectorNode.tsx | 25 -- example/src/CustomNode/index.tsx | 135 ----------- example/src/DragHandle/DragHandleNode.tsx | 33 --- example/src/DragHandle/index.tsx | 21 -- example/src/DragNDrop/Sidebar.tsx | 25 -- example/src/DragNDrop/dnd.css | 37 --- example/src/DragNDrop/index.tsx | 75 ------ example/src/EdgeTypes/index.tsx | 54 ----- example/src/EdgeTypes/utils.ts | 106 -------- example/src/Edges/CustomEdge.tsx | 31 --- example/src/Edges/CustomEdge2.tsx | 44 ---- example/src/Edges/index.tsx | 115 --------- example/src/Empty/index.tsx | 60 ----- example/src/Hidden/index.tsx | 53 ---- example/src/Interaction/index.tsx | 226 ------------------ example/src/Layouting/index.tsx | 86 ------- example/src/Layouting/initial-elements.ts | 71 ------ example/src/Layouting/layouting.css | 11 - example/src/MultiFlows/index.tsx | 45 ---- example/src/MultiFlows/multiflows.css | 13 - example/src/NodeTypeChange/index.tsx | 55 ----- example/src/NodeTypesObjectChange/index.tsx | 76 ------ example/src/Overview/index.tsx | 202 ---------------- example/src/Provider/Sidebar.tsx | 36 --- example/src/Provider/index.tsx | 57 ----- example/src/Provider/provider.css | 45 ---- example/src/SaveRestore/Controls.tsx | 61 ----- example/src/SaveRestore/index.tsx | 37 --- example/src/SaveRestore/save.css | 11 - example/src/Stress/index.tsx | 51 ++-- example/src/Stress/utils.ts | 21 +- example/src/Switch/index.tsx | 53 ---- example/src/Undirectional/CustomNode.tsx | 19 -- example/src/Undirectional/index.tsx | 221 ----------------- example/src/UpdatableEdge/index.tsx | 79 ------ example/src/UpdateNode/index.tsx | 57 +++-- .../src/UseUpdateNodeInternals/CustomNode.tsx | 34 --- example/src/UseUpdateNodeInternals/index.tsx | 101 -------- example/src/UseZoomPanHelper/index.tsx | 65 ----- example/src/Validation/index.tsx | 73 ------ example/src/Validation/validation.css | 31 --- example/src/index.tsx | 116 +-------- package-lock.json | 38 ++- package.json | 4 +- 48 files changed, 161 insertions(+), 2773 deletions(-) delete mode 100644 example/src/CustomConnectionLine/ConnectionLine.tsx delete mode 100644 example/src/CustomConnectionLine/index.tsx delete mode 100644 example/src/CustomNode/ColorSelectorNode.tsx delete mode 100644 example/src/CustomNode/index.tsx delete mode 100644 example/src/DragHandle/DragHandleNode.tsx delete mode 100644 example/src/DragHandle/index.tsx delete mode 100644 example/src/DragNDrop/Sidebar.tsx delete mode 100644 example/src/DragNDrop/dnd.css delete mode 100644 example/src/DragNDrop/index.tsx delete mode 100644 example/src/EdgeTypes/index.tsx delete mode 100644 example/src/EdgeTypes/utils.ts delete mode 100644 example/src/Edges/CustomEdge.tsx delete mode 100644 example/src/Edges/CustomEdge2.tsx delete mode 100644 example/src/Edges/index.tsx delete mode 100644 example/src/Empty/index.tsx delete mode 100644 example/src/Hidden/index.tsx delete mode 100644 example/src/Interaction/index.tsx delete mode 100644 example/src/Layouting/index.tsx delete mode 100644 example/src/Layouting/initial-elements.ts delete mode 100644 example/src/Layouting/layouting.css delete mode 100644 example/src/MultiFlows/index.tsx delete mode 100644 example/src/MultiFlows/multiflows.css delete mode 100644 example/src/NodeTypeChange/index.tsx delete mode 100644 example/src/NodeTypesObjectChange/index.tsx delete mode 100644 example/src/Overview/index.tsx delete mode 100644 example/src/Provider/Sidebar.tsx delete mode 100644 example/src/Provider/index.tsx delete mode 100644 example/src/Provider/provider.css delete mode 100644 example/src/SaveRestore/Controls.tsx delete mode 100644 example/src/SaveRestore/index.tsx delete mode 100644 example/src/SaveRestore/save.css delete mode 100644 example/src/Switch/index.tsx delete mode 100644 example/src/Undirectional/CustomNode.tsx delete mode 100644 example/src/Undirectional/index.tsx delete mode 100644 example/src/UpdatableEdge/index.tsx delete mode 100644 example/src/UseUpdateNodeInternals/CustomNode.tsx delete mode 100644 example/src/UseUpdateNodeInternals/index.tsx delete mode 100644 example/src/UseZoomPanHelper/index.tsx delete mode 100644 example/src/Validation/index.tsx delete mode 100644 example/src/Validation/validation.css diff --git a/example/.gitignore b/example/.gitignore index 4d29575d..703c7975 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -21,3 +21,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +src_oldapi \ No newline at end of file diff --git a/example/src/Basic/index.tsx b/example/src/Basic/index.tsx index ae003545..4872d56b 100644 --- a/example/src/Basic/index.tsx +++ b/example/src/Basic/index.tsx @@ -1,49 +1,54 @@ -import React, { useState, MouseEvent } from 'react'; +import { useState, MouseEvent, useCallback } from 'react'; import ReactFlow, { - removeElements, addEdge, - isNode, Background, - Elements, - BackgroundVariant, + applyNodeChanges, + applyEdgeChanges, + MiniMap, + Controls, FlowElement, Node, Edge, - Connection, + ElementChange, OnLoadParams, + Connection, } from 'react-flow-renderer'; const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); -const initialElements: Elements = [ +const initialNodes: Node[] = [ { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' }, { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' }, { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' }, +]; + +const initialEdges: Edge[] = [ { id: 'e1-2', source: '1', target: '2', animated: true }, { id: 'e1-3', source: '1', target: '3' }, ]; const BasicFlow = () => { const [rfInstance, setRfInstance] = useState(null); - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els)); + const [nodes, setNodes] = useState(initialNodes); + const [edges, setEdges] = useState(initialEdges); + + const onConnect = useCallback((params: Edge | Connection, nds: Node[]) => { + setEdges((eds) => addEdge(params, nds, eds)); + }, []); const onLoad = (reactFlowInstance: OnLoadParams) => setRfInstance(reactFlowInstance); const updatePos = () => { - setElements((elms) => { - return elms.map((el) => { - if (isNode(el)) { - el.position = { - x: Math.random() * 400, - y: Math.random() * 400, - }; - } + setNodes((nds) => { + return nds.map((n) => { + n.position = { + x: Math.random() * 400, + y: Math.random() * 400, + }; - return el; + return n; }); }); }; @@ -52,23 +57,31 @@ const BasicFlow = () => { const resetTransform = () => rfInstance?.setTransform({ x: 0, y: 0, zoom: 1 }); const toggleClassnames = () => { - setElements((elms) => { - return elms.map((el) => { - if (isNode(el)) { - el.className = el.className === 'light' ? 'dark' : 'light'; - } + setNodes((nds) => { + return nds.map((n) => { + n.className = n.className === 'light' ? 'dark' : 'light'; - return el; + return n; }); }); }; + const onNodesChange = useCallback((changes: ElementChange[]) => { + setNodes((ns) => applyNodeChanges(changes, ns)); + }, []); + + const onEdgesChange = useCallback((changes: ElementChange[]) => { + setEdges((es) => applyEdgeChanges(changes, es)); + }, []); + return ( { minZoom={0.2} maxZoom={4} > - + + +
- - ); -}; - -export default EmptyFlow; diff --git a/example/src/Hidden/index.tsx b/example/src/Hidden/index.tsx deleted file mode 100644 index 9869b2bc..00000000 --- a/example/src/Hidden/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from 'react'; - -import { useEffect } from 'react'; -import ReactFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from 'react-flow-renderer'; - -const initialElements: Elements = [ - { 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: Connection | Edge) => setElements((els) => addEdge(params, els)); - - useEffect(() => { - setElements((els) => - els.map((e) => { - e.isHidden = isHidden; - return e; - }) - ); - }, [isHidden]); - - return ( - - - - -
-
- -
-
-
- ); -}; - -export default HiddenFlow; diff --git a/example/src/Interaction/index.tsx b/example/src/Interaction/index.tsx deleted file mode 100644 index 2ca7900c..00000000 --- a/example/src/Interaction/index.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import React, { useState, MouseEvent, WheelEvent } from 'react'; -import ReactFlow, { - addEdge, - MiniMap, - Controls, - Elements, - Node, - FlowElement, - Connection, - Edge, - PanOnScrollMode, - FlowTransform, -} from 'react-flow-renderer'; - -const initialElements: Elements = [ - { 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', animated: true }, - { id: 'e1-3', source: '1', target: '3' }, -]; - -const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node); -const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); -const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); -const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event); -const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event); -const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event); -const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform); - -const InteractionFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - - const [isSelectable, setIsSelectable] = useState(false); - const [isDraggable, setIsDraggable] = useState(false); - const [isConnectable, setIsConnectable] = useState(false); - const [zoomOnScroll, setZoomOnScroll] = useState(false); - const [zoomOnPinch, setZoomOnPinch] = useState(false); - const [panOnScroll, setPanOnScroll] = useState(false); - const [panOnScrollMode, setPanOnScrollMode] = useState(PanOnScrollMode.Free); - const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false); - const [paneMoveable, setPaneMoveable] = useState(true); - const [captureZoomClick, setCaptureZoomClick] = useState(false); - const [captureZoomScroll, setCaptureZoomScroll] = useState(false); - const [captureElementClick, setCaptureElementClick] = useState(false); - - return ( - - - - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- ); -}; - -export default InteractionFlow; diff --git a/example/src/Layouting/index.tsx b/example/src/Layouting/index.tsx deleted file mode 100644 index 34ee23e6..00000000 --- a/example/src/Layouting/index.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useState } from 'react'; -import ReactFlow, { - ReactFlowProvider, - addEdge, - removeElements, - Controls, - isNode, - Elements, - Connection, - Edge, - NodeExtent, - Position, -} from 'react-flow-renderer'; -import dagre from 'dagre'; - -import initialElements from './initial-elements'; - -import './layouting.css'; - -const dagreGraph = new dagre.graphlib.Graph(); -dagreGraph.setDefaultEdgeLabel(() => ({})); - -const nodeExtent: NodeExtent = [ - [0, 0], - [1000, 1000], -]; - -const LayoutFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - - const onLayout = (direction: string) => { - const isHorizontal = direction === 'LR'; - dagreGraph.setGraph({ rankdir: direction }); - - elements.forEach((el) => { - if (isNode(el)) { - dagreGraph.setNode(el.id, { width: 150, height: 50 }); - } else { - dagreGraph.setEdge(el.source, el.target); - } - }); - - dagre.layout(dagreGraph); - - const layoutedElements = elements.map((el) => { - if (isNode(el)) { - const nodeWithPosition = dagreGraph.node(el.id); - el.targetPosition = isHorizontal ? Position.Left : Position.Top; - el.sourcePosition = isHorizontal ? Position.Right : Position.Bottom; - // we need to pass a slightly different position in order to notify react flow about the change - // @TODO how can we change the position handling so that we dont need this hack? - el.position = { x: nodeWithPosition.x + Math.random() / 1000, y: nodeWithPosition.y }; - } - - return el; - }); - - setElements(layoutedElements); - }; - - return ( -
- - onLayout('TB')} - > - - -
- - -
-
-
- ); -}; - -export default LayoutFlow; diff --git a/example/src/Layouting/initial-elements.ts b/example/src/Layouting/initial-elements.ts deleted file mode 100644 index 404b1b8b..00000000 --- a/example/src/Layouting/initial-elements.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Elements, XYPosition } from 'react-flow-renderer'; - -const position: XYPosition = { x: 0, y: 0 }; - -const elements: Elements = [ - { - id: '1', - type: 'input', - data: { label: 'input' }, - position, - }, - { - id: '2', - data: { label: 'node 2' }, - position, - }, - { - id: '2a', - data: { label: 'node 2a' }, - position, - }, - { - id: '2b', - data: { label: 'node 2b' }, - position, - }, - { - id: '2c', - data: { label: 'node 2c' }, - position, - }, - { - id: '2d', - data: { label: 'node 2d' }, - position, - }, - { - id: '3', - data: { label: 'node 3' }, - position, - }, - { - id: '4', - data: { label: 'node 4' }, - position, - }, - { - id: '5', - data: { label: 'node 5' }, - position, - }, - { - id: '6', - type: 'output', - data: { label: 'output' }, - position, - }, - { id: '7', type: 'output', data: { label: 'output' }, position: { x: 400, y: 450 } }, - { id: 'e12', source: '1', target: '2', type: 'smoothstep' }, - { id: 'e13', source: '1', target: '3', type: 'smoothstep' }, - { id: 'e22a', source: '2', target: '2a', type: 'smoothstep' }, - { id: 'e22b', source: '2', target: '2b', type: 'smoothstep' }, - { id: 'e22c', source: '2', target: '2c', type: 'smoothstep' }, - { id: 'e2c2d', source: '2c', target: '2d', type: 'smoothstep' }, - - { id: 'e45', source: '4', target: '5', type: 'smoothstep' }, - { id: 'e56', source: '5', target: '6', type: 'smoothstep' }, - { id: 'e57', source: '5', target: '7', type: 'smoothstep' }, -]; - -export default elements; diff --git a/example/src/Layouting/layouting.css b/example/src/Layouting/layouting.css deleted file mode 100644 index 6d248b22..00000000 --- a/example/src/Layouting/layouting.css +++ /dev/null @@ -1,11 +0,0 @@ -.layoutflow { - flex-grow: 1; - position: relative; -} - -.layoutflow .controls { - position: absolute; - right: 10px; - top: 10px; - z-index: 10; -} diff --git a/example/src/MultiFlows/index.tsx b/example/src/MultiFlows/index.tsx deleted file mode 100644 index 8e185992..00000000 --- a/example/src/MultiFlows/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useState, FC } from 'react'; - -import ReactFlow, { - removeElements, - addEdge, - Background, - Elements, - Edge, - Connection, - ReactFlowProvider, -} from 'react-flow-renderer'; - -import './multiflows.css'; - -const initialElements: Elements = [ - { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' }, - { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' }, - { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, - { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' }, - { id: 'e1-2', source: '1', target: '2', animated: true }, - { id: 'e1-3', source: '1', target: '3' }, -]; - -const Flow: FC = () => { - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Edge | Connection) => setElements((els) => addEdge(params, els)); - - return ( - - - - - - ); -}; - -const MultiFlows: FC = () => ( -
- - -
-); - -export default MultiFlows; diff --git a/example/src/MultiFlows/multiflows.css b/example/src/MultiFlows/multiflows.css deleted file mode 100644 index b7017390..00000000 --- a/example/src/MultiFlows/multiflows.css +++ /dev/null @@ -1,13 +0,0 @@ -.react-flow__example-multiflows { - display: flex; - height: 100%; -} - -.react-flow__example-multiflows .react-flow { - width: 100%; - height: 100%; -} - -.react-flow__example-multiflows .react-flow:first-child { - border-right: 2px solid #333; -} diff --git a/example/src/NodeTypeChange/index.tsx b/example/src/NodeTypeChange/index.tsx deleted file mode 100644 index c5e4ff90..00000000 --- a/example/src/NodeTypeChange/index.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { useState, CSSProperties } from 'react'; - -import ReactFlow, { addEdge, isEdge, OnLoadParams, Elements, Position, Connection, Edge } from 'react-flow-renderer'; - -const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView(); - -const initialElements: Elements = [ - { - id: '1', - sourcePosition: Position.Right, - type: 'input', - data: { label: 'Input' }, - position: { x: 0, y: 80 }, - }, - { - id: '2', - type: 'output', - sourcePosition: Position.Right, - targetPosition: Position.Left, - data: { label: 'A Node' }, - position: { x: 250, y: 0 }, - }, - { id: 'e1-2', source: '1', type: 'smoothstep', target: '2', animated: true }, -]; - -const buttonStyle: CSSProperties = { position: 'absolute', right: 10, top: 30, zIndex: 4 }; - -const NodeTypeChangeFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const changeType = () => { - setElements((elms) => - elms.map((el) => { - if (isEdge(el) || el.type === 'input') { - return el; - } - - return { - ...el, - type: el.type === 'default' ? 'output' : 'default', - }; - }) - ); - }; - - return ( - - - - ); -}; - -export default NodeTypeChangeFlow; diff --git a/example/src/NodeTypesObjectChange/index.tsx b/example/src/NodeTypesObjectChange/index.tsx deleted file mode 100644 index 54b34fcc..00000000 --- a/example/src/NodeTypesObjectChange/index.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useState, CSSProperties, FC } from 'react'; - -import ReactFlow, { - addEdge, - Elements, - Position, - Connection, - Edge, - NodeProps, - NodeTypesType, -} from 'react-flow-renderer'; - -const initialElements: Elements = [ - { - id: '1', - sourcePosition: Position.Right, - type: 'input', - data: { label: 'Input' }, - position: { x: 0, y: 80 }, - }, - { - id: '2', - type: 'a', - sourcePosition: Position.Right, - targetPosition: Position.Left, - data: { label: 'A Node' }, - position: { x: 250, y: 0 }, - }, -]; - -const buttonStyle: CSSProperties = { position: 'absolute', right: 10, top: 30, zIndex: 4 }; - -const nodeStyles: CSSProperties = { padding: '10px 15px', border: '1px solid #ddd' }; - -const NodeA: FC = () => { - return
A
; -}; - -const NodeB: FC = () => { - return
B
; -}; - -type NodeTypesObject = { - [key: string]: NodeTypesType; -}; - -const nodeTypesObjects: NodeTypesObject = { - a: { - a: NodeA, - }, - b: { - b: NodeB, - }, -}; - -const NodeTypeChangeFlow = () => { - const [nodeTypesId, setNodeTypesId] = useState('a'); - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const changeType = () => setNodeTypesId((nt) => (nt === 'a' ? 'b' : 'a')); - - return ( - - - - ); -}; - -export default NodeTypeChangeFlow; diff --git a/example/src/Overview/index.tsx b/example/src/Overview/index.tsx deleted file mode 100644 index ee13f916..00000000 --- a/example/src/Overview/index.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import React, { useState, MouseEvent, CSSProperties } from 'react'; - -import ReactFlow, { - removeElements, - addEdge, - MiniMap, - Controls, - Background, - isNode, - Node, - Elements, - FlowElement, - OnLoadParams, - FlowTransform, - SnapGrid, - ArrowHeadType, - Connection, - Edge, -} from 'react-flow-renderer'; - -const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node); -const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); -const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); -const onNodeDoubleClick = (_: MouseEvent, node: Node) => console.log('node double click', node); -const onPaneClick = (event: MouseEvent) => console.log('pane click', event); -const onPaneScroll = (event?: MouseEvent) => console.log('pane scroll', event); -const onPaneContextMenu = (event: MouseEvent) => console.log('pane context menu', event); -const onSelectionDrag = (_: MouseEvent, nodes: Node[]) => console.log('selection drag', nodes); -const onSelectionDragStart = (_: MouseEvent, nodes: Node[]) => console.log('selection drag start', nodes); -const onSelectionDragStop = (_: MouseEvent, nodes: Node[]) => console.log('selection drag stop', nodes); -const onSelectionContextMenu = (event: MouseEvent, nodes: Node[]) => { - event.preventDefault(); - console.log('selection context menu', nodes); -}; -const onElementClick = (_: MouseEvent, element: FlowElement) => - console.log(`${isNode(element) ? 'node' : 'edge'} click:`, element); -const onSelectionChange = (elements: Elements | null) => console.log('selection change', elements); -const onLoad = (reactFlowInstance: OnLoadParams) => { - console.log('flow loaded:', reactFlowInstance); - reactFlowInstance.fitView(); -}; - -const onMoveStart = (transform?: FlowTransform) => console.log('zoom/move start', transform); -const onMoveEnd = (transform?: FlowTransform) => console.log('zoom/move end', transform); -const onEdgeContextMenu = (_: MouseEvent, edge: Edge) => console.log('edge context menu', edge); -const onEdgeMouseEnter = (_: MouseEvent, edge: Edge) => console.log('edge mouse enter', edge); -const onEdgeMouseMove = (_: MouseEvent, edge: Edge) => console.log('edge mouse move', edge); -const onEdgeMouseLeave = (_: MouseEvent, edge: Edge) => console.log('edge mouse leave', edge); -const onEdgeDoubleClick = (_: MouseEvent, edge: Edge) => console.log('edge double click', edge); - -const initialElements: Elements = [ - { - id: '1', - type: 'input', - data: { - label: ( - <> - Welcome to React Flow! - - ), - }, - position: { x: 250, y: 0 }, - }, - { - id: '2', - data: { - label: ( - <> - This is a default node - - ), - }, - position: { x: 100, y: 100 }, - }, - { - id: '3', - data: { - label: ( - <> - This one has a custom style - - ), - }, - position: { x: 400, y: 100 }, - style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 }, - }, - { - id: '4', - position: { x: 250, y: 200 }, - data: { - label: ( - <> - You can find the docs on{' '} - - Github - - - ), - }, - }, - { - id: '5', - data: { - label: ( - <> - Or check out the other examples - - ), - }, - position: { x: 250, y: 325 }, - }, - { - id: '6', - type: 'output', - data: { - label: ( - <> - An output node - - ), - }, - position: { x: 100, y: 480 }, - }, - { id: '7', type: 'output', data: { label: 'Another output node' }, position: { x: 400, y: 450 } }, - { id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' }, - { id: 'e1-3', source: '1', target: '3' }, - { id: 'e3-4', source: '3', target: '4', animated: true, label: 'animated edge' }, - { id: 'e4-5', source: '4', target: '5', arrowHeadType: ArrowHeadType.Arrow, label: 'edge with arrow head' }, - { id: 'e5-6', source: '5', target: '6', type: 'smoothstep', label: 'smooth step edge' }, - { - id: 'e5-7', - source: '5', - target: '7', - type: 'step', - style: { stroke: '#f6ab6c' }, - label: 'a step edge', - animated: true, - labelStyle: { fill: '#f6ab6c', fontWeight: 700 }, - }, -]; - -const connectionLineStyle: CSSProperties = { stroke: '#ddd' }; -const snapGrid: SnapGrid = [16, 16]; - -const nodeStrokeColor = (n: Node): string => { - if (n.style?.background) return n.style.background as string; - if (n.type === 'input') return '#0041d0'; - if (n.type === 'output') return '#ff0072'; - if (n.type === 'default') return '#1a192b'; - - return '#eee'; -}; - -const nodeColor = (n: Node): string => { - if (n.style?.background) return n.style.background as string; - - return '#fff'; -}; - -const OverviewFlow = () => { - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - - return ( - - - - - - ); -}; - -export default OverviewFlow; diff --git a/example/src/Provider/Sidebar.tsx b/example/src/Provider/Sidebar.tsx deleted file mode 100644 index 0b2c5f24..00000000 --- a/example/src/Provider/Sidebar.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import { useStoreState, useStoreActions } from 'react-flow-renderer'; - -const Sidebar = () => { - const nodes = useStoreState((store) => store.nodes); - const transform = useStoreState((store) => store.transform); - const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements); - - const selectAll = () => { - setSelectedElements(nodes.map((node) => ({ id: node.id, type: node.type }))); - }; - - return ( - - ); -}; - -export default Sidebar; diff --git a/example/src/Provider/index.tsx b/example/src/Provider/index.tsx deleted file mode 100644 index a7a78b7c..00000000 --- a/example/src/Provider/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { useState, MouseEvent } from 'react'; -import ReactFlow, { - ReactFlowProvider, - addEdge, - removeElements, - Controls, - OnLoadParams, - FlowElement, - Connection, - Edge, - Elements, - ConnectionMode, -} from 'react-flow-renderer'; - -import Sidebar from './Sidebar'; - -import './provider.css'; - -const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); -const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance); - -const initialElements: Elements = [ - { 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', animated: true }, - { id: 'e1-3', source: '1', target: '3' }, -]; - -const ProviderFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - - return ( -
- - -
- - - -
-
-
- ); -}; - -export default ProviderFlow; diff --git a/example/src/Provider/provider.css b/example/src/Provider/provider.css deleted file mode 100644 index aa719acc..00000000 --- a/example/src/Provider/provider.css +++ /dev/null @@ -1,45 +0,0 @@ -.providerflow { - flex-direction: column; - display: flex; - height: 100%; -} - -.providerflow aside { - border-right: 1px solid #eee; - padding: 15px 10px; - font-size: 12px; - background: #fcfcfc; -} - -.providerflow aside .description { - margin-bottom: 10px; -} - -.providerflow aside .title { - font-weight: 700; - margin-bottom: 5px; -} - -.providerflow aside .transform { - margin-bottom: 20px; -} - -.providerflow .reactflow-wrapper { - flex-grow: 1; - height: 100%; -} - -.providerflow .selectall { - margin-top: 10px; -} - -@media screen and (min-width: 768px) { - .providerflow { - flex-direction: row; - } - - .providerflow aside { - width: 20%; - max-width: 250px; - } -} \ No newline at end of file diff --git a/example/src/SaveRestore/Controls.tsx b/example/src/SaveRestore/Controls.tsx deleted file mode 100644 index f79f5e47..00000000 --- a/example/src/SaveRestore/Controls.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { memo, useCallback, Dispatch, FC } from 'react'; -import { useZoomPanHelper, OnLoadParams, Elements, FlowExportObject } from 'react-flow-renderer'; -import localforage from 'localforage'; - -localforage.config({ - name: 'react-flow', - storeName: 'flows', -}); - -const flowKey = 'example-flow'; - -const getNodeId = () => `randomnode_${+new Date()}`; - -type ControlsProps = { - rfInstance?: OnLoadParams; - setElements: Dispatch>>; -}; - -const Controls: FC = ({ rfInstance, setElements }) => { - const { transform } = useZoomPanHelper(); - - const onSave = useCallback(() => { - if (rfInstance) { - const flow = rfInstance.toObject(); - localforage.setItem(flowKey, flow); - } - }, [rfInstance]); - - const onRestore = useCallback(() => { - const restoreFlow = async () => { - const flow: FlowExportObject | null = await localforage.getItem(flowKey); - - if (flow) { - const [x = 0, y = 0] = flow.position; - setElements(flow.elements || []); - transform({ x, y, zoom: flow.zoom || 0 }); - } - }; - - restoreFlow(); - }, [setElements, transform]); - - const onAdd = useCallback(() => { - const newNode = { - id: `random_node-${getNodeId()}`, - data: { label: 'Added node' }, - position: { x: Math.random() * window.innerWidth - 100, y: Math.random() * window.innerHeight }, - }; - setElements((els) => els.concat(newNode)); - }, [setElements]); - - return ( -
- - - -
- ); -}; - -export default memo(Controls); diff --git a/example/src/SaveRestore/index.tsx b/example/src/SaveRestore/index.tsx deleted file mode 100644 index 9457248d..00000000 --- a/example/src/SaveRestore/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React, { useState } from 'react'; -import ReactFlow, { - ReactFlowProvider, - removeElements, - addEdge, - Elements, - Connection, - Edge, - OnLoadParams, -} from 'react-flow-renderer'; - -import Controls from './Controls'; - -import './save.css'; - -const initialElements: Elements = [ - { id: '1', data: { label: 'Node 1' }, position: { x: 100, y: 100 } }, - { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } }, - { id: 'e1-2', source: '1', target: '2' }, -]; - -const SaveRestore = () => { - const [rfInstance, setRfInstance] = useState(); - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - - return ( - - - - - - ); -}; - -export default SaveRestore; diff --git a/example/src/SaveRestore/save.css b/example/src/SaveRestore/save.css deleted file mode 100644 index ffd028dd..00000000 --- a/example/src/SaveRestore/save.css +++ /dev/null @@ -1,11 +0,0 @@ -.save__controls { - position: absolute; - right: 10px; - top: 10px; - z-index: 4; - font-size: 12px; -} - -.save__controls button { - margin-left: 5px; -} diff --git a/example/src/Stress/index.tsx b/example/src/Stress/index.tsx index b734c6b6..2b27158b 100644 --- a/example/src/Stress/index.tsx +++ b/example/src/Stress/index.tsx @@ -1,15 +1,13 @@ -import React, { useState, CSSProperties } from 'react'; +import { useState, CSSProperties, useCallback } from 'react'; import ReactFlow, { - removeElements, - addEdge, MiniMap, - isNode, Controls, Background, OnLoadParams, - Elements, - Connection, Edge, + Node, + ElementChange, + applyNodeChanges, } from 'react-flow-renderer'; import { getElements } from './utils'; @@ -21,38 +19,41 @@ const onLoad = (reactFlowInstance: OnLoadParams) => { console.log(reactFlowInstance.getElements()); }; -const initialElements: Elements = getElements(30, 30); +const initialElements = getElements(30, 30); const StressFlow = () => { - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); + const [nodes, setNodes] = useState(initialElements.nodes); + const [edges, setEdges] = useState(initialElements.edges); + // const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); + // const onConnect = (params: Connection | Edge, nds: Node[]) => setElements((els) => addEdge(params, els)); const updatePos = () => { - setElements((elms) => { - return elms.map((el) => { - if (isNode(el)) { - return { - ...el, - position: { - x: Math.random() * window.innerWidth, - y: Math.random() * window.innerHeight, - }, - }; - } - - return el; + setNodes((nds) => { + return nds.map((n) => { + return { + ...n, + position: { + x: Math.random() * window.innerWidth, + y: Math.random() * window.innerHeight, + }, + }; }); }); }; const updateElements = () => { const grid = Math.ceil(Math.random() * 10); - setElements(getElements(grid, grid)); + const initialElements = getElements(grid, grid); + setNodes(initialElements.nodes); + setEdges(initialElements.edges); }; + const onNodesChange = useCallback((changes: ElementChange[]) => { + setNodes((ns) => applyNodeChanges(changes, ns)); + }, []); + return ( - + diff --git a/example/src/Stress/utils.ts b/example/src/Stress/utils.ts index a37eb8de..0d2abc90 100644 --- a/example/src/Stress/utils.ts +++ b/example/src/Stress/utils.ts @@ -1,7 +1,13 @@ -import { Elements } from 'react-flow-renderer'; +import { Node, Edge } from 'react-flow-renderer'; -export function getElements(xElements: number = 10, yElements: number = 10): Elements { - const initialElements = []; +type ElementsCollection = { + nodes: Node[]; + edges: Edge[]; +}; + +export function getElements(xElements: number = 10, yElements: number = 10): ElementsCollection { + const initialNodes = []; + const initialEdges = []; let nodeId = 1; let recentNodeId = null; @@ -15,10 +21,10 @@ export function getElements(xElements: number = 10, yElements: number = 10): Ele data, position, }; - initialElements.push(node); + initialNodes.push(node); if (recentNodeId && nodeId <= xElements * yElements) { - initialElements.push({ id: `${x}-${y}`, source: recentNodeId.toString(), target: nodeId.toString() }); + initialEdges.push({ id: `${x}-${y}`, source: recentNodeId.toString(), target: nodeId.toString() }); } recentNodeId = nodeId; @@ -26,5 +32,8 @@ export function getElements(xElements: number = 10, yElements: number = 10): Ele } } - return initialElements; + return { + nodes: initialNodes, + edges: initialEdges, + }; } diff --git a/example/src/Switch/index.tsx b/example/src/Switch/index.tsx deleted file mode 100644 index 03dacf99..00000000 --- a/example/src/Switch/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState, MouseEvent } from 'react'; - -import ReactFlow, { removeElements, addEdge, Node, FlowElement, Elements, Connection, Edge } from 'react-flow-renderer'; - -const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); -const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element); - -const elementsA: Elements = [ - { id: '1a', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' }, - { id: '2a', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' }, - { id: '3a', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, - { id: '4a', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' }, - { id: 'e1-2', source: '1a', target: '2a' }, - { id: 'e1-3', source: '1a', target: '3a' }, -]; - -const elementsB: Elements = [ - { id: 'inputb', type: 'input', data: { label: 'Input' }, position: { x: 300, y: 5 }, className: 'light' }, - { id: '1b', data: { label: 'Node 1' }, position: { x: 0, y: 100 }, className: 'light' }, - { id: '2b', data: { label: 'Node 2' }, position: { x: 200, y: 100 }, className: 'light' }, - { id: '3b', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, - { id: '4b', data: { label: 'Node 4' }, position: { x: 600, y: 100 }, className: 'light' }, - - { id: 'e1b', source: 'inputb', target: '1b' }, - { id: 'e2b', source: 'inputb', target: '2b' }, - { id: 'e3b', source: 'inputb', target: '3b' }, - { id: 'e4b', source: 'inputb', target: '4b' }, -]; - -const BasicFlow = () => { - const [elements, setElements] = useState(elementsA); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - - return ( - -
- - -
-
- ); -}; - -export default BasicFlow; diff --git a/example/src/Undirectional/CustomNode.tsx b/example/src/Undirectional/CustomNode.tsx deleted file mode 100644 index ab5853e8..00000000 --- a/example/src/Undirectional/CustomNode.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React, { memo, FC, CSSProperties } from 'react'; - -import { Handle, Position, NodeProps } from 'react-flow-renderer'; - -const nodeStyles: CSSProperties = { padding: '10px 15px', border: '1px solid #ddd' }; - -const CustomNode: FC = ({ id }) => { - return ( -
-
node {id}
- - - - -
- ); -}; - -export default memo(CustomNode); diff --git a/example/src/Undirectional/index.tsx b/example/src/Undirectional/index.tsx deleted file mode 100644 index f05ad21e..00000000 --- a/example/src/Undirectional/index.tsx +++ /dev/null @@ -1,221 +0,0 @@ -import React, { useState, useCallback } from 'react'; - -import ReactFlow, { - NodeTypesType, - addEdge, - useZoomPanHelper, - ReactFlowProvider, - Elements, - Connection, - Edge, - ElementId, - ConnectionLineType, - ConnectionMode, - updateEdge, - ArrowHeadType, -} from 'react-flow-renderer'; -import CustomNode from './CustomNode'; - -const initialElements: Elements = [ - { - id: '00', - type: 'custom', - position: { x: 300, y: 250 }, - }, - { - id: '01', - type: 'custom', - position: { x: 100, y: 50 }, - }, - { - id: '02', - type: 'custom', - position: { x: 500, y: 50 }, - }, - { - id: '03', - type: 'custom', - position: { x: 500, y: 500 }, - }, - { - id: '04', - type: 'custom', - position: { x: 100, y: 500 }, - }, - { - id: '10', - type: 'custom', - position: { x: 300, y: 5 }, - }, - { - id: '20', - type: 'custom', - position: { x: 600, y: 250 }, - }, - { - id: '30', - type: 'custom', - position: { x: 300, y: 600 }, - }, - { - id: '40', - type: 'custom', - position: { x: 5, y: 250 }, - }, - { - id: 'e0-1a', - source: '00', - target: '01', - sourceHandle: 'left', - targetHandle: 'bottom', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-1b', - source: '00', - target: '01', - sourceHandle: 'top', - targetHandle: 'right', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-2a', - source: '00', - target: '02', - sourceHandle: 'top', - targetHandle: 'left', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-2b', - source: '00', - target: '02', - sourceHandle: 'right', - targetHandle: 'bottom', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-3a', - source: '00', - target: '03', - sourceHandle: 'right', - targetHandle: 'top', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-3b', - source: '00', - target: '03', - sourceHandle: 'bottom', - targetHandle: 'left', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-4a', - source: '00', - target: '04', - sourceHandle: 'bottom', - targetHandle: 'right', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-4b', - source: '00', - target: '04', - sourceHandle: 'left', - targetHandle: 'top', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-10', - source: '00', - target: '10', - sourceHandle: 'top', - targetHandle: 'bottom', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-20', - source: '00', - target: '20', - sourceHandle: 'right', - targetHandle: 'left', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-30', - source: '00', - target: '30', - sourceHandle: 'bottom', - targetHandle: 'top', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, - { - id: 'e0-40', - source: '00', - target: '40', - sourceHandle: 'left', - targetHandle: 'right', - type: 'smoothstep', - arrowHeadType: ArrowHeadType.Arrow, - }, -]; - -const nodeTypes: NodeTypesType = { - custom: CustomNode, -}; - -let id = 4; -const getId = (): ElementId => `${id++}`; - -const UpdateNodeInternalsFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => - setElements((els) => addEdge({ ...params, type: 'smoothstep' }, els)); - const { project } = useZoomPanHelper(); - const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) => - setElements((els) => updateEdge(oldEdge, newConnection, els)); - - const onPaneClick = useCallback( - (evt) => - setElements((els) => - els.concat({ - id: getId(), - position: project({ x: evt.clientX, y: evt.clientY - 40 }), - type: 'custom', - }) - ), - [project] - ); - - return ( - - ); -}; - -const WrappedFlow = () => ( - - - -); - -export default WrappedFlow; diff --git a/example/src/UpdatableEdge/index.tsx b/example/src/UpdatableEdge/index.tsx deleted file mode 100644 index 4ffdab04..00000000 --- a/example/src/UpdatableEdge/index.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { useState } from 'react'; -import ReactFlow, { - Controls, - updateEdge, - addEdge, - Elements, - OnLoadParams, - Connection, - Edge, - removeElements, -} from 'react-flow-renderer'; - -const initialElements: Elements = [ - { - id: '1', - type: 'input', - data: { - label: ( - <> - Node A - - ), - }, - position: { x: 250, y: 0 }, - }, - { - id: '2', - data: { - label: ( - <> - Node B - - ), - }, - position: { x: 100, y: 100 }, - }, - { - id: '3', - data: { - label: ( - <> - Node C - - ), - }, - position: { x: 400, y: 100 }, - style: { background: '#D6D5E6', color: '#333', border: '1px solid #222138', width: 180 }, - }, - { id: 'e1-2', source: '1', target: '2', label: 'This is a draggable edge' }, -]; - -const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView(); -const onEdgeUpdateStart = (_: React.MouseEvent, edge: Edge) => console.log('start update', edge); -const onEdgeUpdateEnd = (_: MouseEvent, edge: Edge) => console.log('end update', edge); - -const UpdatableEdge = () => { - const [elements, setElements] = useState(initialElements); - const onEdgeUpdate = (oldEdge: Edge, newConnection: Connection) => - setElements((els) => updateEdge(oldEdge, newConnection, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - - return ( - - - - ); -}; - -export default UpdatableEdge; diff --git a/example/src/UpdateNode/index.tsx b/example/src/UpdateNode/index.tsx index a7cce8f2..a4c10abe 100644 --- a/example/src/UpdateNode/index.tsx +++ b/example/src/UpdateNode/index.tsx @@ -1,64 +1,71 @@ -import React, { useEffect, useState } from 'react'; -import ReactFlow, { Elements } from 'react-flow-renderer'; +import { useEffect, useState, useCallback } from 'react'; +import ReactFlow, { Node, Edge, applyNodeChanges, ElementChange } from 'react-flow-renderer'; import './updatenode.css'; -const initialElements: Elements = [ +const initialNodes: Node[] = [ { id: '1', data: { label: '-' }, position: { x: 100, y: 100 } }, { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } }, - { id: 'e1-2', source: '1', target: '2' }, ]; +const initialEdges: Edge[] = [{ id: 'e1-2', source: '1', target: '2' }]; + const UpdateNode = () => { - const [elements, setElements] = useState(initialElements); + const [nodes, setNodes] = useState(initialNodes); + const [edges, setEdges] = useState(initialEdges); + const [nodeName, setNodeName] = useState('Node 1'); const [nodeBg, setNodeBg] = useState('#eee'); const [nodeHidden, setNodeHidden] = useState(false); useEffect(() => { - setElements((els) => - els.map((el) => { - if (el.id === '1') { + setNodes((nds) => + nds.map((n) => { + if (n.id === '1') { // it's important that you create a new object here in order to notify react flow about the change - el.data = { - ...el.data, + n.data = { + ...n.data, label: nodeName, }; } - return el; + return n; }) ); - }, [nodeName, setElements]); + }, [nodeName]); useEffect(() => { - setElements((els) => - els.map((el) => { - if (el.id === '1') { + setNodes((nds) => + nds.map((n) => { + if (n.id === '1') { // it's important that you create a new object here in order to notify react flow about the change - el.style = { ...el.style, backgroundColor: nodeBg }; + n.style = { ...n.style, backgroundColor: nodeBg }; } - return el; + return n; }) ); - }, [nodeBg, setElements]); + }, [nodeBg]); useEffect(() => { - setElements((els) => - els.map((el) => { - if (el.id === '1' || el.id === 'e1-2') { + setNodes((nds) => + nds.map((n) => { + if (n.id === '1' || n.id === 'e1-2') { // when you update a simple type you can just update the value - el.isHidden = nodeHidden; + n.isHidden = nodeHidden; } - return el; + return n; }) ); - }, [nodeHidden, setElements]); + }, [nodeHidden]); + + const onNodesChange = useCallback((changes: ElementChange[]) => { + setNodes((ns) => applyNodeChanges(changes, ns)); + }, []); return ( - +
setNodeName(evt.target.value)} /> diff --git a/example/src/UseUpdateNodeInternals/CustomNode.tsx b/example/src/UseUpdateNodeInternals/CustomNode.tsx deleted file mode 100644 index 969a4c3c..00000000 --- a/example/src/UseUpdateNodeInternals/CustomNode.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { memo, FC, useMemo, CSSProperties } from 'react'; - -import { Handle, Position, NodeProps } from 'react-flow-renderer'; - -const nodeStyles: CSSProperties = { padding: 10, border: '1px solid #ddd' }; - -const CustomNode: FC = ({ data }) => { - const handles = useMemo( - () => - Array.from({ length: data.handleCount }, (x, i) => { - const handleId = `handle-${i}`; - return ( - - ); - }), - [data.handleCount, data.handlePosition] - ); - - return ( -
- -
output handle count: {data.handleCount}
- {handles} -
- ); -}; - -export default memo(CustomNode); diff --git a/example/src/UseUpdateNodeInternals/index.tsx b/example/src/UseUpdateNodeInternals/index.tsx deleted file mode 100644 index 2b254c5a..00000000 --- a/example/src/UseUpdateNodeInternals/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState, useCallback, CSSProperties } from 'react'; - -import ReactFlow, { - NodeTypesType, - addEdge, - useZoomPanHelper, - ReactFlowProvider, - Elements, - Connection, - Edge, - ElementId, - useUpdateNodeInternals, - Position, - isEdge, -} from 'react-flow-renderer'; -import CustomNode from './CustomNode'; - -const initialHandleCount = 1; - -const initialElements: Elements = [ - { - id: '1', - type: 'custom', - data: { label: 'Node 1', handleCount: initialHandleCount, handlePosition: 0 }, - position: { x: 250, y: 5 }, - }, -]; - -const buttonWrapperStyles: CSSProperties = { position: 'absolute', right: 10, top: 10, zIndex: 10 }; - -const nodeTypes: NodeTypesType = { - custom: CustomNode, -}; - -let id = 5; -const getId = (): ElementId => `${id++}`; - -const UpdateNodeInternalsFlow = () => { - const [elements, setElements] = useState(initialElements); - const updateNodeInternals = useUpdateNodeInternals(); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const { project } = useZoomPanHelper(); - - const onPaneClick = useCallback( - (evt) => - setElements((els) => - els.concat({ - id: getId(), - position: project({ x: evt.clientX, y: evt.clientY - 40 }), - data: { label: 'new node' }, - targetPosition: Position.Left, - sourcePosition: Position.Right, - }) - ), - [project] - ); - - const toggleHandleCount = useCallback(() => { - setElements((els) => - els.map((el) => { - if (isEdge(el)) { - return el; - } - - return { ...el, data: { ...el.data, handleCount: el.data?.handleCount === 1 ? 2 : 1 } }; - }) - ); - }, []); - - const toggleHandlePosition = useCallback(() => { - setElements((els) => - els.map((el) => { - if (isEdge(el)) { - return el; - } - - return { ...el, data: { ...el.data, handlePosition: el.data?.handlePosition === 0 ? 1 : 0 } }; - }) - ); - }, []); - - const updateNode = useCallback(() => updateNodeInternals('1'), [updateNodeInternals]); - - return ( - -
- - - -
-
- ); -}; - -const WrappedFlow = () => ( - - - -); - -export default WrappedFlow; diff --git a/example/src/UseZoomPanHelper/index.tsx b/example/src/UseZoomPanHelper/index.tsx deleted file mode 100644 index 94ff30f8..00000000 --- a/example/src/UseZoomPanHelper/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useState, useCallback } from 'react'; - -import ReactFlow, { - removeElements, - addEdge, - Background, - MiniMap, - useZoomPanHelper, - ReactFlowProvider, - Elements, - ElementId, - Connection, - Edge, -} from 'react-flow-renderer'; - -const initialElements: Elements = [ - { id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, className: 'light' }, - { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, className: 'light' }, - { id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, className: 'light' }, - { id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light' }, - { id: 'e1-2', source: '1', target: '2', animated: true }, - { id: 'e1-3', source: '1', target: '3' }, -]; - -let id = 5; -const getId = (): ElementId => `${id++}`; - -const UseZoomPanHelperFlow = () => { - const [elements, setElements] = useState(initialElements); - const onElementsRemove = (elementsToRemove: Elements) => setElements((els) => removeElements(elementsToRemove, els)); - const onConnect = (params: Connection | Edge) => setElements((els) => addEdge(params, els)); - const { project } = useZoomPanHelper(); - - const onPaneClick = useCallback( - (evt) => { - const projectedPosition = project({ x: evt.clientX, y: evt.clientY - 40 }); - - setElements((els) => - els.concat({ - id: getId(), - position: projectedPosition, - data: { - label: `${projectedPosition.x}-${projectedPosition.y}`, - }, - }) - ); - }, - [project] - ); - - return ( - - - - - ); -}; - -const WrappedFlow = () => ( - - - -); - -export default WrappedFlow; diff --git a/example/src/Validation/index.tsx b/example/src/Validation/index.tsx deleted file mode 100644 index d5209ed4..00000000 --- a/example/src/Validation/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useState, MouseEvent as ReactMouseEvent, FC } from 'react'; -import ReactFlow, { - addEdge, - Handle, - OnLoadParams, - Connection, - Position, - Elements, - Edge, - OnConnectStartParams, - NodeProps, - NodeTypesType, -} from 'react-flow-renderer'; - -import './validation.css'; - -const initialElements: Elements = [ - { id: '0', type: 'custominput', position: { x: 0, y: 150 } }, - { id: 'A', type: 'customnode', position: { x: 250, y: 0 } }, - { id: 'B', type: 'customnode', position: { x: 250, y: 150 } }, - { id: 'C', type: 'customnode', position: { x: 250, y: 300 } }, -]; - -const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView(); -const isValidConnection = (connection: Connection) => connection.target === 'B'; -const onConnectStart = (_: ReactMouseEvent, { nodeId, handleType }: OnConnectStartParams) => - console.log('on connect start', { nodeId, handleType }); -const onConnectStop = (event: MouseEvent) => console.log('on connect stop', event); -const onConnectEnd = (event: MouseEvent) => console.log('on connect end', event); - -const CustomInput: FC = () => ( - <> -
Only connectable with B
- - -); - -const CustomNode: FC = ({ id }) => ( - <> - -
{id}
- - -); - -const nodeTypes: NodeTypesType = { - custominput: CustomInput, - customnode: CustomNode, -}; - -const HorizontalFlow = () => { - const [elements, setElements] = useState(initialElements); - const onConnect = (params: Connection | Edge) => { - console.log('on connect', params); - setElements((els) => addEdge(params, els)); - }; - - return ( - - ); -}; - -export default HorizontalFlow; diff --git a/example/src/Validation/validation.css b/example/src/Validation/validation.css deleted file mode 100644 index a777e6c5..00000000 --- a/example/src/Validation/validation.css +++ /dev/null @@ -1,31 +0,0 @@ -.validationflow .react-flow__node { - width: 150px; - border-radius: 5px; - padding: 10px; - color: #555; - border: 1px solid #ddd; - text-align: center; - font-size: 12px; -} - -.validationflow .react-flow__node-customnode { - background: #e6e6e9; - border: 1px solid #ddd; -} - -.react-flow__node-custominput .react-flow__handle { - background: #e6e6e9; -} - -.validationflow .react-flow__node-custominput { - background: #fff; - -} - -.validationflow .react-flow__handle-connecting { - background: #ff6060; -} - -.validationflow .react-flow__handle-valid { - background: #55dd99; -} \ No newline at end of file diff --git a/example/src/index.tsx b/example/src/index.tsx index f81278ba..d6acbeba 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -2,134 +2,24 @@ import React, { ChangeEvent } from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom'; -import Overview from './Overview'; import Basic from './Basic'; -import CustomNode from './CustomNode'; -import Stress from './Stress'; -import Interaction from './Interaction'; -import Empty from './Empty'; -import Edges from './Edges'; -import Validation from './Validation'; -import Provider from './Provider'; -import Hidden from './Hidden'; -import EdgeTypes from './EdgeTypes'; -import CustomConnectionLine from './CustomConnectionLine'; -import NodeTypeChange from './NodeTypeChange'; -import NodeTypesObjectChange from './NodeTypesObjectChange'; -import UpdatableEdge from './UpdatableEdge'; import UpdateNode from './UpdateNode'; -import SaveRestore from './SaveRestore'; -import DragNDrop from './DragNDrop'; -import Layout from './Layouting'; -import SwitchFlows from './Switch'; -import UseZoomPanHelper from './UseZoomPanHelper'; -import UseUpdateNodeInternals from './UseUpdateNodeInternals'; -import Undirectional from './Undirectional'; -import MultiFlows from './MultiFlows'; -import DragHandle from './DragHandle'; +import Stress from './Stress'; import './index.css'; const routes = [ - { - path: '/', - component: Overview, - }, - { - path: '/edges', - component: Edges, - }, - { - path: '/custom-node', - component: CustomNode, - }, - { - path: '/validation', - component: Validation, - }, - { - path: '/provider', - component: Provider, - }, - { - path: '/stress', - component: Stress, - }, - { - path: '/interaction', - component: Interaction, - }, { path: '/basic', component: Basic, }, - { - path: '/empty', - component: Empty, - }, - { - path: '/hidden', - component: Hidden, - }, - { - path: '/edge-types', - component: EdgeTypes, - }, - { - path: '/custom-connectionline', - component: CustomConnectionLine, - }, - { - path: '/nodetype-change', - component: NodeTypeChange, - }, - { - path: '/nodetypesobject-change', - component: NodeTypesObjectChange, - }, - { - path: '/updatable-edge', - component: UpdatableEdge, - }, { path: '/update-node', component: UpdateNode, }, { - path: '/save-restore', - component: SaveRestore, - }, - { - path: '/drag-and-drop', - component: DragNDrop, - }, - { - path: '/layouting', - component: Layout, - }, - { - path: '/switch', - component: SwitchFlows, - }, - { - path: '/usezoompanhelper', - component: UseZoomPanHelper, - }, - { - path: '/useupdatenodeinternals', - component: UseUpdateNodeInternals, - }, - { - path: '/undirectional', - component: Undirectional, - }, - { - path: '/multiflows', - component: MultiFlows, - }, - { - path: '/draghandle', - component: DragHandle, + path: '/stress', + component: Stress, }, ]; diff --git a/package-lock.json b/package-lock.json index ace876fc..9121c531 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,8 @@ "fast-deep-equal": "^3.1.3", "react-draggable": "^4.4.4", "react-redux": "^7.2.5", - "redux": "^4.1.1" + "redux": "^4.1.1", + "redux-thunk": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.15.5", @@ -32,6 +33,7 @@ "@svgr/rollup": "^5.5.0", "@types/redux": "^3.6.31", "@types/resize-observer-browser": "^0.1.6", + "@welldone-software/why-did-you-render": "^6.2.1", "autoprefixer": "^10.3.6", "babel-preset-react-app": "^10.0.0", "cypress": "^8.5.0", @@ -52,6 +54,9 @@ "start-server-and-test": "^1.14.0", "typescript": "^4.4.3" }, + "engines": { + "node": ">=12" + }, "peerDependencies": { "react": "16 || 17", "react-dom": "16 || 17" @@ -2833,6 +2838,18 @@ "@types/node": "*" } }, + "node_modules/@welldone-software/why-did-you-render": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz", + "integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==", + "dev": true, + "dependencies": { + "lodash": "^4" + }, + "peerDependencies": { + "react": "^16 || ^17" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -9261,6 +9278,11 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -13296,6 +13318,15 @@ "@types/node": "*" } }, + "@welldone-software/why-did-you-render": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@welldone-software/why-did-you-render/-/why-did-you-render-6.2.1.tgz", + "integrity": "sha512-eIVKeK6ueS3tuzCqMVTaaNrPYvb9cA8NHiNgLA7Op8SD4TiT31zqNjxmhzLEK+y3sBxcwr6YhsiQGX9EThrvaw==", + "dev": true, + "requires": { + "lodash": "^4" + } + }, "aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -18047,6 +18078,11 @@ "@babel/runtime": "^7.9.2" } }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/package.json b/package.json index 3e5250e4..6f81fe7b 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "fast-deep-equal": "^3.1.3", "react-draggable": "^4.4.4", "react-redux": "^7.2.5", - "redux": "^4.1.1" + "redux": "^4.1.1", + "redux-thunk": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.15.5", @@ -54,6 +55,7 @@ "@svgr/rollup": "^5.5.0", "@types/redux": "^3.6.31", "@types/resize-observer-browser": "^0.1.6", + "@welldone-software/why-did-you-render": "^6.2.1", "autoprefixer": "^10.3.6", "babel-preset-react-app": "^10.0.0", "cypress": "^8.5.0",