176 lines
6 KiB
JavaScript
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,
|
|
};
|
|
};
|