UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

81 lines (80 loc) 3.09 kB
import { useCallback, useEffect, useMemo, useState } from "react"; import { layeredConverter } from "../converters/layeredConverter"; import { layoutGraph } from "../layout"; import { computeLevels } from "../utils/computeLevels"; export function useLayeredLayout(params) { const { blocks, connections, layoutOptions, onError } = params; const [result, setResult] = useState(null); const [isLoading, setIsLoading] = useState(true); const layoutNodes = useMemo(() => { const levels = computeLevels(blocks.map((b) => b.id), connections.map((c) => ({ sourceBlockId: c.sourceBlockId, targetBlockId: c.targetBlockId }))); return blocks.map((b) => ({ id: String(b.id), level: b.level ?? levels.get(b.id) ?? 0, width: b.width, height: b.height, })); }, [blocks, connections]); const layoutEdges = useMemo(() => connections.map((c) => ({ from: String(c.sourceBlockId), to: String(c.targetBlockId), })), [connections]); const connectionIdBySourceTarget = useMemo(() => { const map = new Map(); for (const c of connections) { const key = `${String(c.sourceBlockId)}/${String(c.targetBlockId)}`; const list = map.get(key) ?? []; list.push(c.id ?? key); map.set(key, list); } return map; }, [connections]); const blockSizes = useMemo(() => { const map = new Map(); for (const b of blocks) { map.set(String(b.id), { width: b.width, height: b.height }); } return map; }, [blocks]); const runLayout = useCallback(() => { return layoutGraph({ nodes: layoutNodes, edges: layoutEdges, options: layoutOptions, }); }, [layoutNodes, layoutEdges, layoutOptions]); useEffect(() => { let isCancelled = false; setIsLoading(true); runLayout() .then((layoutResult) => { if (isCancelled) return; const converted = layeredConverter({ layoutResult: { nodes: layoutResult.nodes.map((n) => ({ id: String(n.id), x: n.x, y: n.y, shape: n.shape, })), edges: layoutResult.edges.map((e) => ({ from: String(e.from), to: String(e.to), arrows: e.arrows })), }, connectionIdBySourceTarget: new Map(Array.from(connectionIdBySourceTarget.entries()).map(([k, v]) => [k, [...v]])), blockSizes, }); setResult(converted); setIsLoading(false); }) .catch((error) => { if (!isCancelled) { onError?.(error); setIsLoading(false); } }); return () => { isCancelled = true; }; }, [runLayout, connectionIdBySourceTarget, blockSizes, onError]); return { result, isLoading }; }