in src/state/layout/workflowLayout.ts [25:79]
export default function workflowLayout<NodeType, EdgeType>(
state: DiagramMakerData<NodeType, EdgeType>,
workflowConfig: WorkflowLayoutConfig
): DiagramMakerData<NodeType, EdgeType> {
if (!Dagre) {
throw new Error(
'Could not find "dagre" library. It must be included in your application in order to use "Workflow" layout.'
);
}
const dagreGraph = new Dagre.graphlib.Graph() as dagre.graphlib.Graph;
dagreGraph.setGraph({
rankdir: WORKFLOW_DIRECTION_TO_DAGRE_MAP[workflowConfig.direction],
nodesep: workflowConfig.distanceMin,
ranksep: workflowConfig.distanceMin
});
dagreGraph.setDefaultEdgeLabel(() => ({}));
keys(state.nodes).forEach((nodeId) => {
const nodeData = state.nodes[nodeId].diagramMakerData;
dagreGraph.setNode(nodeId, {
height: nodeData.size.height,
width: nodeData.size.width
});
});
values(state.edges).forEach((edge) => {
dagreGraph.setEdge(edge.src, edge.dest);
});
Dagre.layout(dagreGraph);
// Dagre lays out graph neatly near the top-left corner.
// Move whole graph so that `fixedNodeId` position will remain the same.
// (That is, when `fixedNodeId` is provided.)
let offset: Position = { x: 0, y: 0 };
if (workflowConfig.fixedNodeId) {
const initialFixedNodePosition = state.nodes[workflowConfig.fixedNodeId].diagramMakerData.position;
const dagreFixedNode = dagreGraph.node(workflowConfig.fixedNodeId);
offset = {
x: initialFixedNodePosition.x - dagreFixedNode.x,
y: initialFixedNodePosition.y - dagreFixedNode.y
};
}
return produce(state, (draftState) => {
dagreGraph.nodes().forEach((nodeId: any) => {
const dagreNode = dagreGraph.node(nodeId);
draftState.nodes[nodeId].diagramMakerData.position = {
x: dagreNode.x + offset.x,
y: dagreNode.y + offset.y
};
});
});
}