Add Vue Flow graph visualizer
This commit is contained in:
77
graphfx-ui/src/lib/layout.ts
Normal file
77
graphfx-ui/src/lib/layout.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type { Edge, Node, Position } from '@vue-flow/core'
|
||||
import dagre from '@dagrejs/dagre'
|
||||
|
||||
const DEFAULT_NODE_WIDTH = 280
|
||||
const DEFAULT_ROW_HEIGHT = 34
|
||||
const DEFAULT_HEADER_HEIGHT = 88
|
||||
const MIN_NODE_HEIGHT = 150
|
||||
|
||||
type LayoutDirection = 'LR' | 'TB'
|
||||
|
||||
type LayoutOptions = {
|
||||
direction?: LayoutDirection
|
||||
useSavedPositions?: boolean
|
||||
}
|
||||
|
||||
function estimateNodeHeight(node: Node) {
|
||||
const inputs = Array.isArray(node.data?.inputs) ? node.data.inputs.length : 0
|
||||
const outputs = Array.isArray(node.data?.outputs) ? node.data.outputs.length : 0
|
||||
return Math.max(MIN_NODE_HEIGHT, DEFAULT_HEADER_HEIGHT + Math.max(inputs, outputs, 1) * DEFAULT_ROW_HEIGHT)
|
||||
}
|
||||
|
||||
function targetPosition(direction: LayoutDirection): Position {
|
||||
return direction === 'LR' ? 'left' as Position : 'top' as Position
|
||||
}
|
||||
|
||||
function sourcePosition(direction: LayoutDirection): Position {
|
||||
return direction === 'LR' ? 'right' as Position : 'bottom' as Position
|
||||
}
|
||||
|
||||
export function layoutGraph(nodes: Node[], edges: Edge[], options: LayoutOptions = {}) {
|
||||
const direction = options.direction || 'LR'
|
||||
|
||||
if (options.useSavedPositions && nodes.every((node) => node.data?.hasSavedPosition)) {
|
||||
return nodes.map((node) => ({
|
||||
...node,
|
||||
targetPosition: targetPosition(direction),
|
||||
sourcePosition: sourcePosition(direction),
|
||||
}))
|
||||
}
|
||||
|
||||
const graph = new dagre.graphlib.Graph()
|
||||
graph.setDefaultEdgeLabel(() => ({}))
|
||||
graph.setGraph({
|
||||
rankdir: direction,
|
||||
nodesep: 90,
|
||||
ranksep: 130,
|
||||
marginx: 40,
|
||||
marginy: 40,
|
||||
})
|
||||
|
||||
for (const node of nodes) {
|
||||
graph.setNode(node.id, {
|
||||
width: DEFAULT_NODE_WIDTH,
|
||||
height: estimateNodeHeight(node),
|
||||
})
|
||||
}
|
||||
|
||||
for (const edge of edges) {
|
||||
graph.setEdge(edge.source, edge.target)
|
||||
}
|
||||
|
||||
dagre.layout(graph)
|
||||
|
||||
return nodes.map((node) => {
|
||||
const graphNode = graph.node(node.id)
|
||||
const height = estimateNodeHeight(node)
|
||||
|
||||
return {
|
||||
...node,
|
||||
targetPosition: targetPosition(direction),
|
||||
sourcePosition: sourcePosition(direction),
|
||||
position: graphNode
|
||||
? { x: graphNode.x - DEFAULT_NODE_WIDTH / 2, y: graphNode.y - height / 2 }
|
||||
: node.position,
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user