| 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, | |
| } | |
| } | |