mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-31 10:53:02 +00:00 
			
		
		
		
	
		
			
	
	
		
			97 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			97 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|   | import { useCallback } from 'react' | ||
|  | import ELK from 'elkjs/lib/elk.bundled.js' | ||
|  | import { | ||
|  |   useReactFlow, | ||
|  |   useStoreApi, | ||
|  | } from 'reactflow' | ||
|  | import { cloneDeep } from 'lodash-es' | ||
|  | import type { | ||
|  |   Edge, | ||
|  |   Node, | ||
|  | } from '../types' | ||
|  | import { useWorkflowStore } from '../store' | ||
|  | import { AUTO_LAYOUT_OFFSET } from '../constants' | ||
|  | import { useNodesSyncDraft } from './use-nodes-sync-draft' | ||
|  | 
 | ||
|  | const layoutOptions = { | ||
|  |   'elk.algorithm': 'layered', | ||
|  |   'elk.direction': 'RIGHT', | ||
|  |   'elk.layered.spacing.nodeNodeBetweenLayers': '60', | ||
|  |   'elk.spacing.nodeNode': '40', | ||
|  |   'elk.layered.nodePlacement.strategy': 'SIMPLE', | ||
|  | } | ||
|  | 
 | ||
|  | const elk = new ELK() | ||
|  | 
 | ||
|  | export const getLayoutedNodes = async (nodes: Node[], edges: Edge[]) => { | ||
|  |   const graph = { | ||
|  |     id: 'root', | ||
|  |     layoutOptions, | ||
|  |     children: nodes.map((n) => { | ||
|  |       return { | ||
|  |         ...n, | ||
|  |         width: n.width ?? 150, | ||
|  |         height: n.height ?? 50, | ||
|  |         targetPosition: 'left', | ||
|  |         sourcePosition: 'right', | ||
|  |       } | ||
|  |     }), | ||
|  |     edges: cloneDeep(edges), | ||
|  |   } | ||
|  | 
 | ||
|  |   const layoutedGraph = await elk.layout(graph as any) | ||
|  |   const layoutedNodes = nodes.map((node) => { | ||
|  |     const layoutedNode = layoutedGraph.children?.find( | ||
|  |       lgNode => lgNode.id === node.id, | ||
|  |     ) | ||
|  | 
 | ||
|  |     return { | ||
|  |       ...node, | ||
|  |       position: { | ||
|  |         x: (layoutedNode?.x ?? 0) + AUTO_LAYOUT_OFFSET.x, | ||
|  |         y: (layoutedNode?.y ?? 0) + AUTO_LAYOUT_OFFSET.y, | ||
|  |       }, | ||
|  |     } | ||
|  |   }) | ||
|  | 
 | ||
|  |   return { | ||
|  |     layoutedNodes, | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | export const useNodesLayout = () => { | ||
|  |   const store = useStoreApi() | ||
|  |   const reactflow = useReactFlow() | ||
|  |   const workflowStore = useWorkflowStore() | ||
|  |   const { handleSyncWorkflowDraft } = useNodesSyncDraft() | ||
|  | 
 | ||
|  |   const handleNodesLayout = useCallback(async () => { | ||
|  |     workflowStore.setState({ nodeAnimation: true }) | ||
|  |     const { | ||
|  |       getNodes, | ||
|  |       edges, | ||
|  |       setNodes, | ||
|  |     } = store.getState() | ||
|  |     const { setViewport } = reactflow | ||
|  |     const nodes = getNodes() | ||
|  |     const { | ||
|  |       layoutedNodes, | ||
|  |     } = await getLayoutedNodes(nodes, edges) | ||
|  | 
 | ||
|  |     setNodes(layoutedNodes) | ||
|  |     const zoom = 0.7 | ||
|  |     setViewport({ | ||
|  |       x: 0, | ||
|  |       y: 0, | ||
|  |       zoom, | ||
|  |     }) | ||
|  |     setTimeout(() => { | ||
|  |       handleSyncWorkflowDraft() | ||
|  |     }) | ||
|  |   }, [store, reactflow, handleSyncWorkflowDraft, workflowStore]) | ||
|  | 
 | ||
|  |   return { | ||
|  |     handleNodesLayout, | ||
|  |   } | ||
|  | } |