Skip to content

Commit bea8312

Browse files
authored
Merge pull request #7 from invana/feature/nodetypes-cleanups
Feature/nodetypes cleanups
2 parents 304d6b0 + 01a7abc commit bea8312

28 files changed

+554
-155
lines changed

src/App.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import FlowCanvas from './flow-canvas/canvas';
1+
import BaseFlowCanvas from './flow-canvas/canvasTypes/BaseFlowCanvas';
22
import { initialNodes, initialEdges } from "./example-datasets/raw/er-mock-data";
33

44
export default function App() {
55
return (
66
<div className="App">
77
{/* <h1>ER diagram with Reactflow</h1> */}
8-
<FlowCanvas initialNodes={initialNodes} initialEdges={initialEdges} />
8+
<BaseFlowCanvas initialNodes={initialNodes} initialEdges={initialEdges} />
99
</div>
1010
);
1111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react';
2+
import { MarkerType, Position } from 'reactflow';
3+
4+
export const nodes = [
5+
{
6+
id: '1',
7+
type: 'input',
8+
data: {
9+
label: 'Input Node',
10+
},
11+
position: { x: 250, y: 0 },
12+
},
13+
{
14+
id: '2',
15+
data: {
16+
label: 'Default Node',
17+
},
18+
position: { x: 100, y: 100 },
19+
},
20+
{
21+
id: '3',
22+
type: 'output',
23+
data: {
24+
label: 'Output Node',
25+
},
26+
position: { x: 400, y: 100 },
27+
},
28+
{
29+
id: '4',
30+
type: 'custom',
31+
position: { x: 100, y: 200 },
32+
data: {
33+
label: 'Default Node 4',
34+
35+
},
36+
},
37+
{
38+
id: '5',
39+
type: 'output',
40+
data: {
41+
label: 'custom style',
42+
},
43+
className: 'circle',
44+
style: {
45+
background: '#2B6CB0',
46+
color: 'white',
47+
},
48+
position: { x: 400, y: 200 },
49+
sourcePosition: Position.Right,
50+
targetPosition: Position.Left,
51+
}
52+
53+
];
54+
55+
export const edges = [
56+
{ id: 'e1-2', source: '1', target: '2', label: 'this is an edge label' },
57+
{ id: 'e1-3', source: '1', target: '3', animated: true },
58+
{
59+
id: 'e4-5',
60+
source: '4',
61+
target: '5',
62+
type: 'smoothstep',
63+
sourceHandle: 'handle-0',
64+
data: {
65+
selectIndex: 0,
66+
},
67+
markerEnd: {
68+
type: MarkerType.ArrowClosed,
69+
},
70+
}
71+
];

src/example-datasets/raw/er-mock-data.js

+18
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ export const initialNodes = [
88
// storeType: "MongoDB",
99
label: "MongoDB (1.1)",
1010
fields: [{ label: "crawlerflow", id: "crawlerflow" }]
11+
},
12+
style: {
13+
width: "200px"
1114
}
1215
},
1316
{
@@ -16,6 +19,9 @@ export const initialNodes = [
1619
data: {
1720
label: "FileStorage (1.2)",
1821
fields: [{ label: "myfile.csv", id: "myfile-csv", data_type: "string" }]
22+
},
23+
style: {
24+
width: "200px"
1925
}
2026
},
2127
{
@@ -31,6 +37,9 @@ export const initialNodes = [
3137
// { label: "Model", type: "data" },
3238
// { label: "Error", type: "value" }
3339
// ]
40+
},
41+
style: {
42+
width: "200px"
3443
}
3544
},
3645
{
@@ -44,6 +53,9 @@ export const initialNodes = [
4453
{ label: "description", id: "description", data_type: "string" },
4554
{ label: "is_active", id: "is_active", data_type: "bool" }
4655
]
56+
},
57+
style: {
58+
width: "200px"
4759
}
4860
},
4961
{
@@ -57,6 +69,9 @@ export const initialNodes = [
5769
{ label: "title", id: "title", data_type: "string" },
5870
{ label: "description", id: "description", data_type: "string" }
5971
]
72+
},
73+
style: {
74+
width: "200px"
6075
}
6176
},
6277
{
@@ -68,6 +83,9 @@ export const initialNodes = [
6883
{ label: "identifier", id: "identifier", data_type: "string" },
6984
{ label: "analysed_field", id: "analysed_field", data_type: "integer" }
7085
]
86+
},
87+
style: {
88+
width: "200px"
7189
}
7290
}
7391
];

src/flow-canvas/canvas.tsx renamed to src/flow-canvas/canvasTypes/BaseFlowCanvas.tsx

+78-48
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,39 @@ import ReactFlow, {
1212
applyEdgeChanges, applyNodeChanges, NodeChange, EdgeChange, Edge, Node
1313
// useStore
1414
} from "reactflow";
15-
import "./styles.scss";
15+
import "../styles.scss";
1616
import styled, { ThemeProvider } from 'styled-components';
17-
import { darkTheme, lightTheme } from "./theme";
17+
import { darkTheme, lightTheme } from "../theme";
1818
import React from "react";
19-
import { resetHandlePathHighlight } from "./interactions/EntityRelationHighlight";
19+
import { resetHandlePathHighlight } from "../interactions/EntityRelationHighlight";
2020
import "reactflow/dist/style.css";
21-
import { getLayoutedElements } from "./core/layouts/dagre";
22-
import { CanvasEdge, CanvasNode, FlowCanvasProps } from "./core/types";
23-
import { defaultCanvasSettings, defaultCanvasStyle } from "./settings";
24-
import { CanvasNodeTemplates } from "./nodeTemplates";
25-
import { CanvasEdgeTemplates } from "./edgeTemplates";
26-
import CanvasInteractions from "./interactions/interactions";
27-
import ContextMenu from "./components/ContextMenu";
28-
29-
const canvasInteractions = new CanvasInteractions()
30-
const FlowCanvas = ({ children, initialNodes, initialEdges = [],
21+
// import { getLayoutedElements } from "../layouts/dagre";
22+
import { CanvasEdge, CanvasNode, FlowCanvasProps } from "../core/types";
23+
import { defaultCanvasSettings, defaultCanvasStyle } from "../settings";
24+
import { CanvasNodeTemplates } from "../nodeTemplates";
25+
import { CanvasEdgeTemplates } from "../edgeTemplates";
26+
import CanvasInteractions from "../interactions/interactions";
27+
import GenericNodeContextMenu from "../components/ContextMenu/GenericNodeContextMenu";
28+
import GenericEdgeContextMenu from "../components/ContextMenu/GenericEdgeContextMenu";
29+
import { defaultLayoutChange } from "../layouts/noLayout";
30+
31+
32+
33+
34+
const BaseFlowCanvas = ({
35+
children,
36+
initialNodes,
37+
initialEdges = [],
38+
onLayoutChange,
39+
NodeContextMenu,
40+
EdgeContextMenu,
3141
style = defaultCanvasStyle,
3242
canvasSettings = defaultCanvasSettings,
3343
canvasNodeTemplates = CanvasNodeTemplates,
34-
canvasEdgeTemplates = CanvasEdgeTemplates
44+
canvasEdgeTemplates = CanvasEdgeTemplates,
45+
canvasInteractions = new CanvasInteractions(),
46+
47+
3548
}: FlowCanvasProps) => {
3649

3750

@@ -45,12 +58,16 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
4558
// };
4659
// });
4760
// }
61+
4862
const [flowInstance, setFlowInstance] = useState<ReactFlowInstance | null | undefined>(null);
4963

50-
const { layoutedNodes, layoutedEdges } = getLayoutedElements(
64+
console.log("====onLayoutChange", onLayoutChange);
65+
66+
const { layoutedNodes, layoutedEdges } = onLayoutChange(
5167
initialNodes,
5268
initialEdges,
53-
flowInstance
69+
flowInstance,
70+
"LR"
5471
);
5572

5673
const [mode, setMode] = useState('dark');
@@ -66,7 +83,7 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
6683
const [nodes, setNodes] = useNodesState(layoutedNodes);
6784
const [edges, setEdges] = useEdgesState(layoutedEdges);
6885

69-
const [menu, setMenu] = useState(null);
86+
const [contextMenuItem, setContextMenuItem] = useState(null);
7087

7188
const [direction, setDirection] = useState("LR");
7289

@@ -82,51 +99,53 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
8299

83100
const onNodeContextMenu = useCallback(
84101
(event: React.MouseEvent, node: Node) => {
85-
// Prevent native context menu from showing
102+
// Prevent native context contextMenuItem from showing
86103
event.preventDefault();
87-
console.log("====onNodeContextMenu", node, event, event.clientX, event.clientY)
88-
const pane = ref.current.getBoundingClientRect();
89-
setMenu({
90-
id: node.id,
91-
top: event.clientY < pane.height - 200 && event.clientY,
92-
left: event.clientX < pane.width - 200 && event.clientX,
93-
right: event.clientX >= pane.width - 200 && pane.width - event.clientX,
94-
bottom: event.clientY >= pane.height - 200 && pane.height - event.clientY,
95-
});
96-
104+
const pane = ref.current.getBoundingClientRect();
105+
setContextMenuItem({
106+
id: node.id,
107+
type: "node",
108+
top: event.clientY < pane.height - 200 && event.clientY,
109+
left: event.clientX < pane.width - 200 && event.clientX,
110+
right: event.clientX >= pane.width - 200 && pane.width - event.clientX,
111+
bottom: event.clientY >= pane.height - 200 && pane.height - event.clientY,
112+
});
113+
97114
},
98-
[setMenu]
115+
[setContextMenuItem]
99116
);
100117

101118
const onEdgeContextMenu = useCallback(
102119
(event: React.MouseEvent, edge: Edge) => {
103-
// Prevent native context menu from showing
120+
// Prevent native context contextMenuItem from showing
104121
event.preventDefault();
105-
console.log("====onEdgeContextMenu", edge, event, event.clientX, event.clientY)
106-
const pane = ref.current.getBoundingClientRect();
107-
setMenu({
108-
id: edge.id,
109-
top: event.clientY < pane.height - 200 && event.clientY,
110-
left: event.clientX < pane.width - 200 && event.clientX,
111-
right: event.clientX >= pane.width - 200 && pane.width - event.clientX,
112-
bottom: event.clientY >= pane.height - 200 && pane.height - event.clientY,
113-
});
114-
122+
console.log("====onEdgeContextMenu", edge, event, event.clientX, event.clientY)
123+
const pane = ref.current.getBoundingClientRect();
124+
setContextMenuItem({
125+
id: edge.id,
126+
type: "edge",
127+
top: event.clientY < pane.height - 200 && event.clientY,
128+
left: event.clientX < pane.width - 200 && event.clientX,
129+
right: event.clientX >= pane.width - 200 && pane.width - event.clientX,
130+
bottom: event.clientY >= pane.height - 200 && pane.height - event.clientY,
131+
});
132+
115133
},
116-
[setMenu]
134+
[setContextMenuItem]
117135
);
118136

119137

120138

121-
const onPaneClick = useCallback(() => setMenu(null), [setMenu]);
139+
const onPaneClick = useCallback(() => setContextMenuItem(null), [setContextMenuItem]);
122140

123141

124142
const onInit = (reactFlowInstance: ReactFlowInstance) => {
125143
console.log("flow loaded:", reactFlowInstance);
126144
setFlowInstance(reactFlowInstance);
127-
reactFlowInstance.zoomTo(1);
128-
reactFlowInstance.fitView();
129-
// onLayout(direction)
145+
reactFlowInstance?.zoomTo(1);
146+
reactFlowInstance?.fitView();
147+
148+
onLayout(direction)
130149
}
131150

132151
// const onNodeClick = (event: React.MouseEvent, object: CanvasNode) => {
@@ -161,7 +180,7 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
161180
const {
162181
layoutedNodes,
163182
layoutedEdges
164-
} = getLayoutedElements(nodes, edges, flowInstance, direction);
183+
} = onLayoutChange(nodes, edges, flowInstance, direction);
165184
setNodes([...layoutedNodes]);
166185
setEdges([...layoutedEdges]);
167186
},
@@ -258,7 +277,9 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
258277
// }
259278
>
260279

261-
{menu && <ContextMenu onClick={onPaneClick} {...menu} />}
280+
{contextMenuItem && contextMenuItem?.type === "edge" && <NodeContextMenu onClick={onPaneClick} {...contextMenuItem} />}
281+
{contextMenuItem && contextMenuItem?.type === "node" && <EdgeContextMenu onClick={onPaneClick} {...contextMenuItem} />}
282+
262283
<MiniMapStyled
263284
nodeColor={(node) => {
264285
switch (node.type) {
@@ -292,4 +313,13 @@ const FlowCanvas = ({ children, initialNodes, initialEdges = [],
292313
);
293314
};
294315

295-
export default FlowCanvas;
316+
317+
BaseFlowCanvas.defaultProps = {
318+
onLayoutChange: defaultLayoutChange,
319+
canvasInteractions: new CanvasInteractions(),
320+
NodeContextMenu: GenericNodeContextMenu,
321+
EdgeContextMenu: GenericEdgeContextMenu
322+
323+
};
324+
325+
export default BaseFlowCanvas;
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { FlowCanvasProps } from "../core/types";
2+
import BaseFlowCanvas from "./BaseFlowCanvas";
3+
import { defaultCanvasSettings, defaultCanvasStyle } from "../settings";
4+
import { CanvasNodeTemplates } from "../nodeTemplates";
5+
import { CanvasEdgeTemplates } from "../edgeTemplates";
6+
import CanvasInteractions, { FlowInstanceType } from "../interactions/interactions";
7+
import DagreLayoutEngine from "../layouts/dagre";
8+
import { CanvasEdge , CanvasNode } from "../core/types";
9+
10+
11+
const layoutEngine: DagreLayoutEngine = new DagreLayoutEngine()
12+
const dagreOnLayoutChange = (nodes: CanvasNode[], edges: CanvasEdge[], flowInstance: FlowInstanceType, direction: string) => {
13+
return layoutEngine.getLayoutedElements(nodes, edges, flowInstance, direction)
14+
}
15+
const FlowCanvas = ({
16+
children,
17+
initialNodes,
18+
initialEdges = [],
19+
onLayoutChange,
20+
NodeContextMenu,
21+
EdgeContextMenu,
22+
style = defaultCanvasStyle,
23+
canvasSettings = defaultCanvasSettings,
24+
canvasNodeTemplates = CanvasNodeTemplates,
25+
canvasEdgeTemplates = CanvasEdgeTemplates,
26+
canvasInteractions = null
27+
}: FlowCanvasProps) => {
28+
29+
30+
31+
return <BaseFlowCanvas children={children} initialNodes={initialNodes} initialEdges={initialEdges}
32+
onLayoutChange={dagreOnLayoutChange} style={style} canvasSettings={canvasSettings}
33+
canvasEdgeTemplates={canvasEdgeTemplates} canvasNodeTemplates={canvasNodeTemplates}
34+
NodeContextMenu={NodeContextMenu} EdgeContextMenu={EdgeContextMenu}
35+
36+
canvasInteractions={canvasInteractions} />
37+
}
38+
39+
FlowCanvas.defaultProps = {
40+
onLayoutChange: dagreOnLayoutChange,
41+
canvasInteractions:new CanvasInteractions()
42+
43+
};
44+
45+
export default FlowCanvas

0 commit comments

Comments
 (0)