Elkjs Tree
Like our dagre example, this example shows how you can integrate elkjs with Svelte Flow for more advanced tree layouts. The code for this example builds a similar tree to the dagre example, but you can look at the reference here to see what you can configure (hint: it’s a lot).
<script lang="ts">
import { writable } from 'svelte/store';
import ELK from 'elkjs/lib/elk.bundled.js';
import {
SvelteFlow,
Background,
Position,
ConnectionLineType,
Panel,
useSvelteFlow,
type Node,
type Edge
} from '@xyflow/svelte';
import '@xyflow/svelte/dist/style.css';
import { initialNodes, initialEdges } from './nodes-and-edges';
import { onMount } from 'svelte';
const nodes = writable<Node[]>([]);
const edges = writable<Edge[]>([]);
const { fitView } = useSvelteFlow();
const elk = new ELK();
// Elk has a *huge* amount of options to configure. To see everything you can
// tweak check out:
//
// - https://www.eclipse.org/elk/reference/algorithms.html
// - https://www.eclipse.org/elk/reference/options.html
const elkOptions = {
'elk.algorithm': 'layered',
'elk.layered.spacing.nodeNodeBetweenLayers': '100',
'elk.spacing.nodeNode': '80'
};
function getLayoutedElements(nodes: Node[], edges: Edge[], options = {}) {
const isHorizontal = options?.['elk.direction'] === 'RIGHT';
const graph = {
id: 'root',
layoutOptions: options,
children: nodes.map((node) => ({
...node,
// Adjust the target and source handle positions based on the layout
// direction.
targetPosition: isHorizontal ? Position.Left : Position.Top,
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
// Hardcode a width and height for elk to use when layouting.
width: 150,
height: 50
})),
edges: edges
};
return elk
.layout(graph)
.then((layoutedGraph) => ({
nodes: layoutedGraph.children.map((node) => ({
...node,
// React Flow expects a position property on the node instead of `x`
// and `y` fields.
position: { x: node.x, y: node.y }
})),
edges: layoutedGraph.edges
}))
.catch(console.error);
}
function onLayout(direction: string, useInitialNodes = false) {
const opts = { 'elk.direction': direction, ...elkOptions };
const ns = useInitialNodes ? initialNodes : $nodes;
const es = useInitialNodes ? initialEdges : $edges;
getLayoutedElements(ns, es, opts).then(({ nodes: layoutedNodes, edges: layoutedEdges }) => {
$nodes = layoutedNodes;
$edges = layoutedEdges;
fitView();
window.requestAnimationFrame(() => fitView());
});
}
onMount(() => {
onLayout('DOWN', true);
});
</script>
<div style="height:100vh;">
<SvelteFlow
{nodes}
{edges}
fitView
connectionLineType={ConnectionLineType.SmoothStep}
defaultEdgeOptions={{ type: 'smoothstep', animated: true }}
>
<Panel position="top-right">
<button on:click={() => onLayout('DOWN')}>vertical layout</button>
<button on:click={() => onLayout('RIGHT')}>horizontal layout</button>
</Panel>
<Background />
</SvelteFlow>
</div>