forked from KaotoIO/kaoto
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(viz): First iteration of the Visualization component
This is the first iteration of the Visualization component, there are still some rough edges but it will serve as an intermediate step to move forward. relates to: KaotoIO#34
- Loading branch information
Showing
6 changed files
with
162 additions
and
509 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
packages/ui/src/components/Visualization/Canvas/Canvas.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { FunctionComponent, PropsWithChildren, useEffect, useState } from 'react'; | ||
import ReactFlow, { Background, Controls, Edge, Node, useReactFlow } from 'reactflow'; | ||
import 'reactflow/dist/style.css'; | ||
import { CamelRoute } from '../../../models/camel-entities'; | ||
import { CanvasService } from './canvas.service'; | ||
|
||
interface CanvasProps { | ||
contextToolbar?: React.ReactNode; | ||
entities: CamelRoute[]; | ||
} | ||
|
||
export const Canvas: FunctionComponent<PropsWithChildren<CanvasProps>> = (props) => { | ||
const { fitView } = useReactFlow(); | ||
const [nodes, setNodes] = useState<Node[]>([]); | ||
const [edges, setEdges] = useState<Edge[]>([]); | ||
|
||
/** Calculate graph */ | ||
useEffect(() => { | ||
if (!Array.isArray(props.entities)) return; | ||
|
||
const localNodes: Node[] = []; | ||
const localEdges: Edge[] = []; | ||
|
||
props.entities.forEach((entity) => { | ||
const { nodes: childNodes, edges: childEdges } = CanvasService.getFlowChart(entity.toVizNode()); | ||
localNodes.push(...childNodes); | ||
localEdges.push(...childEdges); | ||
}); | ||
|
||
setNodes(localNodes); | ||
setEdges(localEdges); | ||
|
||
/** Find a better mechanism to update the canvas */ | ||
setTimeout(() => { | ||
fitView(); | ||
}, 100); | ||
}, []); | ||
|
||
return ( | ||
<ReactFlow nodes={nodes} edges={edges}> | ||
<Background /> | ||
<Controls /> | ||
</ReactFlow> | ||
); | ||
}; |
86 changes: 86 additions & 0 deletions
86
packages/ui/src/components/Visualization/Canvas/canvas.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import Dagre from '@dagrejs/dagre'; | ||
import { Edge, Node } from 'reactflow'; | ||
import { VisualizationNode } from '../../../models/visualization'; | ||
|
||
type NodesAndEdges = { nodes: Node[]; edges: Edge[] }; | ||
|
||
export class CanvasService { | ||
static nodes: Node[] = []; | ||
static edges: Edge[] = []; | ||
static visitedNodes: string[] = []; | ||
|
||
static getFlowChart(vizNode: VisualizationNode): NodesAndEdges { | ||
this.nodes = []; | ||
this.edges = []; | ||
this.visitedNodes = []; | ||
this.getNodesAndEdges(vizNode); | ||
|
||
console.log('this.nodes', this.nodes, vizNode); | ||
console.log('this.edges', this.edges); | ||
const positionedFlowChart = this.getLayoutedElements(this.nodes, this.edges); | ||
return { nodes: positionedFlowChart.nodes, edges: positionedFlowChart.edges }; | ||
} | ||
|
||
/** Method for iterating over all the VisualizationNode and its children using a depth-first algorithm */ | ||
static getNodesAndEdges(vizNodeParam: VisualizationNode) { | ||
if (this.visitedNodes.includes(vizNodeParam.id)) { | ||
return; | ||
} | ||
|
||
console.log('vizNodeParam', vizNodeParam.id, vizNodeParam.label); | ||
const node = vizNodeParam.toNode(); | ||
|
||
/** Add node */ | ||
this.nodes.push(node); | ||
this.visitedNodes.push(node.id); | ||
|
||
/** Add edges */ | ||
this.edges.push(...vizNodeParam.getEdges()); | ||
|
||
/** Traverse the children nodes */ | ||
const children = vizNodeParam.getChildren(); | ||
if (children !== undefined) { | ||
children.forEach((child) => { | ||
this.getNodesAndEdges(child); | ||
}); | ||
} | ||
|
||
/** Traverse the next node */ | ||
const nextNode = vizNodeParam.getNextNode(); | ||
if (nextNode !== undefined) { | ||
this.getNodesAndEdges(nextNode); | ||
} | ||
} | ||
|
||
private static getLayoutedElements(nodes: Node[], edges: Edge[]): NodesAndEdges { | ||
const graph = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); | ||
graph.setGraph({ | ||
rankdir: 'TB', | ||
}); | ||
|
||
edges.forEach((edge) => graph.setEdge(edge.source, edge.target)); | ||
nodes.forEach((node) => { | ||
graph.setNode(node.id, { width: node.style!.width, height: node.style!.height }); | ||
}); | ||
|
||
Dagre.layout(graph); | ||
|
||
return { | ||
nodes: nodes.map((node) => { | ||
let { x, y } = graph.node(node.id); | ||
|
||
/** Position child node relatively to its parent */ | ||
if (node.parentNode) { | ||
const parentNode = graph.node(node.parentNode); | ||
if (parentNode) { | ||
x = x - parentNode.x; | ||
y = y - parentNode.y; | ||
} | ||
} | ||
|
||
return { ...node, position: { x, y } }; | ||
}), | ||
edges, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './Canvas'; |
16 changes: 14 additions & 2 deletions
16
packages/ui/src/components/Visualization/Visualization.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,22 @@ | ||
import { FunctionComponent, PropsWithChildren } from 'react'; | ||
import { FunctionComponent, PropsWithChildren, useState } from 'react'; | ||
import { ReactFlowProvider } from 'reactflow'; | ||
import { CamelRoute } from '../../models/camel-entities'; | ||
import { camelRoute } from '../../stubs/camel-route'; | ||
import { Canvas } from './Canvas'; | ||
import './Visualization.scss'; | ||
|
||
interface CanvasProps { | ||
className?: string; | ||
} | ||
|
||
export const Visualization: FunctionComponent<PropsWithChildren<CanvasProps>> = (props) => { | ||
return <div className={`canvasSurface ${props.className ?? ''}`}>Visualization</div>; | ||
const [entities] = useState<CamelRoute[]>([camelRoute]); | ||
|
||
return ( | ||
<div className={`canvasSurface ${props.className ?? ''}`}> | ||
<ReactFlowProvider> | ||
<Canvas entities={entities} /> | ||
</ReactFlowProvider> | ||
</div> | ||
); | ||
}; |
Oops, something went wrong.