160 lines
5.4 KiB
JavaScript
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;
|
|
};
|