Skip to Content

Elkjs 树

与我们的 dagre 示例 一样,此示例展示了如何将 elkjs 与 React Flow 集成以实现更高级的树布局。此示例的代码构建了一个与 dagre 示例类似的树,但你可以查看参考 此处 以了解你可以配置的内容(提示:有很多)。

¥Like our dagre example, this example shows how you can integrate elkjs with React 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).

import { initialNodes, initialEdges } from './initialElements.js'; import ELK from 'elkjs/lib/elk.bundled.js'; import React, { useCallback, useLayoutEffect } from 'react'; import { Background, ReactFlow, ReactFlowProvider, addEdge, Panel, useNodesState, useEdgesState, useReactFlow, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; 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', }; const getLayoutedElements = (nodes, edges, 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 ? 'left' : 'top', sourcePosition: isHorizontal ? 'right' : '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 LayoutFlow() { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { fitView } = useReactFlow(); const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []); const onLayout = useCallback( ({ direction, 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 }) => { setNodes(layoutedNodes); setEdges(layoutedEdges); fitView(); }, ); }, [nodes, edges], ); // Calculate the initial layout on mount. useLayoutEffect(() => { onLayout({ direction: 'DOWN', useInitialNodes: true }); }, []); return ( <ReactFlow nodes={nodes} edges={edges} onConnect={onConnect} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} fitView style={{ backgroundColor: '#F7F9FB' }} > <Panel position="top-right"> <button onClick={() => onLayout({ direction: 'DOWN' })}>vertical layout</button> <button onClick={() => onLayout({ direction: 'RIGHT' })}> horizontal layout </button> </Panel> <Background /> </ReactFlow> ); } export default () => ( <ReactFlowProvider> <LayoutFlow /> </ReactFlowProvider> );
Last updated on