Skip to Content
ExamplesLayout

Elkjs Multiple Handles

import type { Edge } from '@xyflow/vue'; import type { ElkNode as ElkLayoutNode } from 'elkjs/lib/elk-api'; import type { ElkNode } from './nodes'; import { watch } from 'vue'; import { useNodesInitialized, useVueFlow } from '@xyflow/vue'; import ELK from 'elkjs/lib/elk.bundled.js'; // elk layouting options: https://www.eclipse.org/elk/reference/algorithms/org-eclipse-elk-layered.html const layoutOptions = { 'elk.algorithm': 'layered', 'elk.direction': 'RIGHT', 'elk.layered.spacing.edgeNodeBetweenLayers': '40', 'elk.spacing.nodeNode': '40', 'elk.layered.nodePlacement.strategy': 'SIMPLE', }; const elk = new ELK(); // uses elkjs to give each node a layouted position export async function getLayoutedNodes(nodes: ElkNode[], edges: readonly Edge[]): Promise<ElkNode[]> { const graph: ElkLayoutNode = { id: 'root', layoutOptions, children: nodes.map((n) => { const targetPorts = n.data.targetHandles.map((t) => ({ id: t.id, // ⚠️ it's important to tell elk which side the port is on — targets are on the // left (WEST) and sources on the right (EAST) properties: { side: 'WEST' }, })); const sourcePorts = n.data.sourceHandles.map((s) => ({ id: s.id, properties: { side: 'EAST' }, })); return { id: n.id, width: n.width ?? 150, height: n.height ?? 50, // ⚠️ tell elk the ports are fixed, to reduce edge crossings properties: { 'org.eclipse.elk.portConstraints': 'FIXED_ORDER' }, // pass the node id too, so we can handle edges without a source/target handle ports: [{ id: n.id }, ...targetPorts, ...sourcePorts], }; }), edges: edges.map((e) => ({ id: e.id, sources: [e.sourceHandle || e.source], targets: [e.targetHandle || e.target], })), }; const layoutedGraph = await elk.layout(graph); return nodes.map((node) => { const layoutedNode = layoutedGraph.children?.find((lgNode) => lgNode.id === node.id); return { ...node, position: { x: layoutedNode?.x ?? 0, y: layoutedNode?.y ?? 0, }, }; }); } export default function useLayoutNodes() { const nodesInitialized = useNodesInitialized(); const { getNodes, getEdges, setNodes, fitView } = useVueFlow(); watch( nodesInitialized, async (initialized) => { if (!initialized) { return; } const layoutedNodes = await getLayoutedNodes(getNodes.value as unknown as ElkNode[], getEdges.value); setNodes(layoutedNodes); fitView(); }, { immediate: true }, ); }
Last updated on