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

160 lines
5.4 KiB
JavaScript

import { entityCodesToText } from "./utils.js";
import { DEFAULT_FONT_SIZE } from "./constants.js";
export const createArrowSkeletonFromSVG = (arrowNode, opts) => {
const arrow = {};
if (opts?.label) {
arrow.label = { text: entityCodesToText(opts.label), fontSize: 16 };
}
const tagName = arrowNode.tagName;
if (tagName === "line") {
arrow.startX = Number(arrowNode.getAttribute("x1"));
arrow.startY = Number(arrowNode.getAttribute("y1"));
arrow.endX = Number(arrowNode.getAttribute("x2"));
arrow.endY = Number(arrowNode.getAttribute("y2"));
}
else if (tagName === "path") {
const dAttr = arrowNode.getAttribute("d");
if (!dAttr) {
throw new Error('Path element does not contain a "d" attribute');
}
// Split the d attribute based on M (Move To) and C (Curve) commands
const commands = dAttr.split(/(?=[LC])/);
const startPosition = commands[0]
.substring(1)
.split(",")
.map((coord) => parseFloat(coord));
const points = [];
commands.forEach((command) => {
const currPoints = command
.substring(1)
.trim()
.split(" ")
.map((pos) => {
const [x, y] = pos.split(",");
return [
parseFloat(x) - startPosition[0],
parseFloat(y) - startPosition[1],
];
});
points.push(...currPoints);
});
const endPosition = points[points.length - 1];
arrow.startX = startPosition[0];
arrow.startY = startPosition[1];
arrow.endX = endPosition[0];
arrow.endY = endPosition[1];
arrow.points = points;
}
if (opts?.label) {
// In mermaid the text is positioned above arrow but in Excalidraw
// its postioned on the arrow hence the elements below it might look cluttered so shifting the arrow by an offset of 10px
const offset = 10;
arrow.startY = arrow.startY - offset;
arrow.endY = arrow.endY - offset;
}
arrow.strokeColor = arrowNode.getAttribute("stroke");
arrow.strokeWidth = Number(arrowNode.getAttribute("stroke-width"));
arrow.type = "arrow";
arrow.strokeStyle = opts?.strokeStyle || "solid";
arrow.startArrowhead = opts?.startArrowhead || null;
arrow.endArrowhead = opts?.endArrowhead || null;
return arrow;
};
export const createArrowSkeletion = (startX, startY, endX, endY, opts) => {
const arrow = {};
arrow.type = "arrow";
arrow.startX = startX;
arrow.startY = startY;
arrow.endX = endX;
arrow.endY = endY;
Object.assign(arrow, { ...opts });
return arrow;
};
export const createTextSkeleton = (x, y, text, opts) => {
const textElement = {
type: "text",
x,
y,
text,
width: opts?.width || 20,
height: opts?.height || 20,
fontSize: opts?.fontSize || DEFAULT_FONT_SIZE,
id: opts?.id,
groupId: opts?.groupId,
metadata: opts?.metadata,
};
return textElement;
};
export const createTextSkeletonFromSVG = (textNode, text, opts) => {
const node = {};
const x = Number(textNode.getAttribute("x"));
const y = Number(textNode.getAttribute("y"));
node.type = "text";
node.text = entityCodesToText(text);
if (opts?.id) {
node.id = opts.id;
}
if (opts?.groupId) {
node.groupId = opts.groupId;
}
const boundingBox = textNode.getBBox();
node.width = boundingBox.width;
node.height = boundingBox.height;
node.x = x - boundingBox.width / 2;
node.y = y;
const fontSize = parseInt(getComputedStyle(textNode).fontSize);
node.fontSize = fontSize;
return node;
};
export const createContainerSkeletonFromSVG = (node, type, opts = {}) => {
const container = {};
container.type = type;
const { label, subtype, id, groupId } = opts;
container.id = id;
if (groupId) {
container.groupId = groupId;
}
if (label) {
container.label = {
text: entityCodesToText(label.text),
fontSize: 16,
verticalAlign: label?.verticalAlign,
};
}
const boundingBox = node.getBBox();
container.x = boundingBox.x;
container.y = boundingBox.y;
container.width = boundingBox.width;
container.height = boundingBox.height;
container.subtype = subtype;
switch (subtype) {
case "highlight":
const bgColor = node.getAttribute("fill");
if (bgColor) {
container.bgColor = bgColor;
}
break;
case "note":
container.strokeStyle = "dashed";
break;
}
return container;
};
export const createLineSkeletonFromSVG = (lineNode, startX, startY, endX, endY, opts) => {
const line = {};
line.startX = startX;
line.startY = startY;
line.endX = endX;
if (opts?.groupId) {
line.groupId = opts.groupId;
}
if (opts?.id) {
line.id = opts.id;
}
// Make sure lines don't overlap with the nodes, in mermaid it overlaps but isn't visible as its pushed back and containers are non transparent
line.endY = endY;
line.strokeColor = lineNode.getAttribute("stroke");
line.strokeWidth = Number(lineNode.getAttribute("stroke-width"));
line.type = "line";
return line;
};