Test/node_modules/@excalidraw/mermaid-to-excalidraw/dist/parser/flowchart.js
2026-04-09 22:54:00 +07:00

176 lines
6 KiB
JavaScript

import { computeEdgePositions, entityCodesToText, getTransformAttr, } from "../utils.js";
const parseSubGraph = (data, containerEl) => {
// Extract only node id for better reference
// e.g. full element id = "flowchart-c1-205" will map to "c1"
const nodeIds = data.nodes.map((n) => {
if (n.startsWith("flowchart-")) {
return n.split("-")[1];
}
return n;
});
// Get position
const el = containerEl.querySelector(`[id='${data.id}']`);
if (!el) {
throw new Error("SubGraph element not found");
}
const position = computeElementPosition(el, containerEl);
// Get dimension
const boundingBox = el.getBBox();
const dimension = {
width: boundingBox.width,
height: boundingBox.height,
};
// Remove irrelevant properties
data.classes = undefined;
data.dir = undefined;
return {
...data,
nodeIds,
...position,
...dimension,
text: entityCodesToText(data.title),
};
};
const parseVertex = (data, containerEl) => {
// Find Vertex element
const el = containerEl.querySelector(`[id*="flowchart-${data.id}-"]`);
if (!el) {
return undefined;
}
// Check if Vertex attached with link
let link;
if (el.parentElement?.tagName.toLowerCase() === "a") {
link = el.parentElement.getAttribute("xlink:href");
}
// Get position
const position = computeElementPosition(link ? el.parentElement : el, containerEl);
// Get dimension
const boundingBox = el.getBBox();
const dimension = {
width: boundingBox.width,
height: boundingBox.height,
};
// Extract style
const labelContainerStyleText = el
.querySelector(".label-container")
?.getAttribute("style");
const labelStyleText = el.querySelector(".label")?.getAttribute("style");
const containerStyle = {};
labelContainerStyleText?.split(";").forEach((property) => {
if (!property) {
return;
}
const key = property.split(":")[0].trim();
const value = property.split(":")[1].trim();
containerStyle[key] = value;
});
const labelStyle = {};
labelStyleText?.split(";").forEach((property) => {
if (!property) {
return;
}
const key = property.split(":")[0].trim();
const value = property.split(":")[1].trim();
labelStyle[key] = value;
});
return {
id: data.id,
labelType: data.labelType,
text: entityCodesToText(data.text),
type: data.type,
link: link || undefined,
...position,
...dimension,
containerStyle,
labelStyle,
};
};
const parseEdge = (data, edgeIndex, containerEl) => {
// Find edge element
const edge = containerEl.querySelector(`[id*="L-${data.start}-${data.end}-${edgeIndex}"]`);
if (!edge) {
throw new Error("Edge element not found");
}
// Compute edge position data
const position = computeElementPosition(edge, containerEl);
const edgePositionData = computeEdgePositions(edge, position);
// Remove irrelevant properties
data.length = undefined;
return {
...data,
...edgePositionData,
text: entityCodesToText(data.text),
};
};
// Compute element position
const computeElementPosition = (el, containerEl) => {
if (!el) {
throw new Error("Element not found");
}
let root = el.parentElement?.parentElement;
const childElement = el.childNodes[0];
let childPosition = { x: 0, y: 0 };
if (childElement) {
const { transformX, transformY } = getTransformAttr(childElement);
const boundingBox = childElement.getBBox();
childPosition = {
x: Number(childElement.getAttribute("x")) ||
transformX + boundingBox.x ||
0,
y: Number(childElement.getAttribute("y")) ||
transformY + boundingBox.y ||
0,
};
}
const { transformX, transformY } = getTransformAttr(el);
const position = {
x: transformX + childPosition.x,
y: transformY + childPosition.y,
};
while (root && root.id !== containerEl.id) {
if (root.classList.value === "root" && root.hasAttribute("transform")) {
const { transformX, transformY } = getTransformAttr(root);
position.x += transformX;
position.y += transformY;
}
root = root.parentElement;
}
return position;
};
export const parseMermaidFlowChartDiagram = (diagram, containerEl) => {
// This does some cleanup and initialization making sure
// diagram is parsed correctly. Useful when multiple diagrams are
// parsed together one after another, eg in playground
// https://github.com/mermaid-js/mermaid/blob/e561cbd3be2a93b8bedfa4839484966faad92ccf/packages/mermaid/src/Diagram.ts#L43
diagram.parse();
// Get mermaid parsed data from parser shared variable `yy`
//@ts-ignore
const mermaidParser = diagram.parser.yy;
const vertices = mermaidParser.getVertices();
Object.keys(vertices).forEach((id) => {
vertices[id] = parseVertex(vertices[id], containerEl);
});
// Track the count of edges based on the edge id
const edgeCountMap = new Map();
const edges = mermaidParser
.getEdges()
.filter((edge) => {
// Sometimes mermaid parser returns edges which are not present in the DOM hence this is a safety check to only consider edges present in the DOM, issue - https://github.com/mermaid-js/mermaid/issues/5516
return containerEl.querySelector(`[id*="L-${edge.start}-${edge.end}"]`);
})
.map((data) => {
const edgeId = `${data.start}-${data.end}`;
const count = edgeCountMap.get(edgeId) || 0;
edgeCountMap.set(edgeId, count + 1);
return parseEdge(data, count, containerEl);
});
const subGraphs = mermaidParser
.getSubGraphs()
.map((data) => parseSubGraph(data, containerEl));
return {
type: "flowchart",
subGraphs,
vertices,
edges,
};
};