Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade mermaid to 10.9.0 and support sequence creation and des… #6

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
},
"dependencies": {
"@excalidraw/markdown-to-text": "0.1.2",
"mermaid": "10.2.3",
"mermaid": "10.9.0",
"nanoid": "4.0.2"
},
"devDependencies": {
Expand Down
8 changes: 6 additions & 2 deletions playground/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ <h1>Custom Test</h1>
<a target="_blank" href="https://mermaid.js.org/syntax/flowchart.html"
>Flowchart</a
>
and

<a
target="_blank"
href="https://mermaid.js.org/syntax/sequenceDiagram.html"
>
Sequence </a
Sequence
</a>
and
<a target="_blank" href="https://mermaid.js.org/syntax/classDiagram.html">
class </a
>diagrams.
<br />
<textarea
Expand Down
9 changes: 9 additions & 0 deletions playground/testcases/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,15 @@ export const CLASS_DIAGRAM_TESTCASES = [
Animal <|--|> Zebra
Bird o..\* Peacock`,
},
{
name: "Class with 2 way Relations and direction",
defination: `classDiagram
direction RL
classA <|--|> classB
classC *--* classD
classE o--o classF
classG <--> classH`,
},
{
name: "Class with Namespace",
defination: `classDiagram
Expand Down
14 changes: 14 additions & 0 deletions playground/testcases/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,20 @@ const SEQUENCE_DIAGRAM_TESTCASES = [
Alice->>Bob: I #9829; you!
Bob->>Alice: I #9829; you #infin; times more!`,
},
{
name: "Actor creation and destruction",
defination: `sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree`,
},
];

export { SEQUENCE_DIAGRAM_TESTCASES };
8 changes: 1 addition & 7 deletions src/converter/types/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,13 @@ export const classToExcalidrawSkeletonConvertor = new GraphConverter({
const chartElements = [...chart.lines, ...chart.arrows, ...chart.text];
classIds.forEach((classId) => {
const childIds = chartElements
.filter(
(ele) =>
ele.metadata &&
"class" in ele.metadata &&
ele.metadata.classId === classId
)
.filter((ele) => ele.metadata && ele.metadata.classId === classId)
.map((ele) => ele.id);

if (childIds.length) {
children.push(...(childIds as string[]));
}
});

const frame: ExcalidrawElementSkeleton = {
type: "frame",
id: nanoid(),
Expand Down
151 changes: 96 additions & 55 deletions src/parser/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const LINE_TYPE = {
DOTTED_LINE: 1,
};

// This is the offset to update the arrow head postition for rendering in excalidraw as mermaid calculates the position until the start of arrowhead
const MERMAID_ARROW_HEAD_OFFSET = 16;

export interface Class {
type: "class";
nodes: Array<Node[]>;
Expand Down Expand Up @@ -98,9 +101,9 @@ const parseClasses = (
const text: Text[] = [];

Object.values(classes).forEach((classNode) => {
const { domId, id } = classNode;
const { domId, id: classId } = classNode;
const groupId = nanoid();
const domNode = containerEl.querySelector(`[id*=classId-${id}]`);
const domNode = containerEl.querySelector(`[id*=classId-${classId}]`);
if (!domNode) {
throw Error(`DOM Node with id ${domId} not found`);
}
Expand All @@ -109,11 +112,11 @@ const parseClasses = (
const container = createContainerSkeletonFromSVG(
domNode.firstChild as SVGRectElement,
"rectangle",
{ id, groupId }
{ id: classId, groupId }
);
container.x += transformX;
container.y += transformY;
container.metadata = { classId: id };
container.metadata = { classId };
nodes.push(container);

const lineNodes = Array.from(
Expand All @@ -140,7 +143,7 @@ const parseClasses = (
line.startY += transformY;
line.endX += transformX;
line.endY += transformY;
line.metadata = { classId: id };
line.metadata = { classId };
lines.push(line);
});

Expand Down Expand Up @@ -171,17 +174,59 @@ const parseClasses = (
height: boundingBox.height,
id,
groupId,
metadata: { classId: id },
metadata: { classId },
}
);

text.push(textElement);
});
});

return { nodes, lines, text };
};

// update arrow position by certain offset for triangle and diamond arrow head types
// as mermaid calculates the position until the start of arrowhead
// for reference - https://github.com/mermaid-js/mermaid/issues/5417
const adjustArrowPosition = (direction: string, arrow: Arrow) => {
// The arrowhead shapes where we need to update the position by a 16px offset
const arrowHeadShapes = ["triangle_outline", "diamond", "diamond_outline"];

const shouldUpdateStartArrowhead =
arrow.startArrowhead && arrowHeadShapes.includes(arrow.startArrowhead);

const shouldUpdateEndArrowhead =
arrow.endArrowhead && arrowHeadShapes.includes(arrow.endArrowhead);

if (!shouldUpdateEndArrowhead && !shouldUpdateStartArrowhead) {
return arrow;
}

if (shouldUpdateStartArrowhead) {
if (direction === "LR") {
arrow.startX -= MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "RL") {
arrow.startX += MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "TB") {
arrow.startY -= MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "BT") {
arrow.startY += MERMAID_ARROW_HEAD_OFFSET;
}
}

if (shouldUpdateEndArrowhead) {
if (direction === "LR") {
arrow.endX += MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "RL") {
arrow.endX -= MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "TB") {
arrow.endY += MERMAID_ARROW_HEAD_OFFSET;
} else if (direction === "BT") {
arrow.endY -= MERMAID_ARROW_HEAD_OFFSET;
}
}
return arrow;
};

const parseRelations = (
relations: ClassRelation[],
classNodes: Container[],
Expand All @@ -208,17 +253,22 @@ const parseRelations = (
const edgePositionData = computeEdgePositions(
edges[index] as SVGPathElement
);
const { startX, startY, endX, endY } = edgePositionData;

const arrow = createArrowSkeletion(startX, startY, endX, endY, {
strokeStyle,
startArrowhead,
endArrowhead,
label: relationNode.title ? { text: relationNode.title } : undefined,
start: { type: "rectangle", id: node1.id },
end: { type: "rectangle", id: node2.id },
});
const arrowSkeletion = createArrowSkeletion(
edgePositionData.startX,
edgePositionData.startY,
edgePositionData.endX,
edgePositionData.endY,
{
strokeStyle,
startArrowhead,
endArrowhead,
label: relationNode.title ? { text: relationNode.title } : undefined,
start: { type: "rectangle", id: node1.id },
end: { type: "rectangle", id: node2.id },
}
);

const arrow = adjustArrowPosition(direction, arrowSkeletion);
arrows.push(arrow);

// Add cardianlities and Multiplicities
Expand All @@ -228,39 +278,40 @@ const parseRelations = (
const directionOffset = 15;
let x;
let y;

if (relationTitle1 && relationTitle1 !== "none") {
switch (direction) {
case "TB":
x = startX - offsetX;
if (endX < startX) {
x = arrow.startX - offsetX;
if (arrow.endX < arrow.startX) {
x -= directionOffset;
}
y = startY + offsetY;
y = arrow.startY + offsetY;
break;
case "BT":
x = startX + offsetX;
if (endX > startX) {
x = arrow.startX + offsetX;
if (arrow.endX > arrow.startX) {
x += directionOffset;
}
y = startY - offsetY;
y = arrow.startY - offsetY;
break;
case "LR":
x = startX + offsetX;
y = startY + offsetY;
if (endY > startY) {
x = arrow.startX + offsetX;
y = arrow.startY + offsetY;
if (arrow.endY > arrow.startY) {
y += directionOffset;
}
break;
case "RL":
x = startX - offsetX;
y = startY - offsetY;
if (startY > endY) {
x = arrow.startX - offsetX;
y = arrow.startY - offsetY;
if (arrow.startY > arrow.endY) {
y -= directionOffset;
}
break;
default:
x = startX - offsetX;
y = startY + offsetY;
x = arrow.startX - offsetX;
y = arrow.startY + offsetY;
}

const relationTitleElement = createTextSkeleton(x, y, relationTitle1, {
Expand All @@ -272,36 +323,36 @@ const parseRelations = (
if (relationTitle2 && relationTitle2 !== "none") {
switch (direction) {
case "TB":
x = endX + offsetX;
if (endX < startX) {
x = arrow.endX + offsetX;
if (arrow.endX < arrow.startX) {
x += directionOffset;
}
y = endY - offsetY;
y = arrow.endY - offsetY;
break;
case "BT":
x = endX - offsetX;
if (endX > startX) {
x = arrow.endX - offsetX;
if (arrow.endX > arrow.startX) {
x -= directionOffset;
}
y = endY + offsetY;
y = arrow.endY + offsetY;
break;
case "LR":
x = endX - offsetX;
y = endY - offsetY;
if (endY > startY) {
x = arrow.endX - offsetX;
y = arrow.endY - offsetY;
if (arrow.endY > arrow.startY) {
y -= directionOffset;
}
break;
case "RL":
x = endX + offsetX;
y = endY + offsetY;
if (startY > endY) {
x = arrow.endX + offsetX;
y = arrow.endY + offsetY;
if (arrow.startY > arrow.endY) {
y += directionOffset;
}
break;
default:
x = endX + offsetX;
y = endY - offsetY;
x = arrow.endX + offsetX;
y = arrow.endY - offsetY;
}

const relationTitleElement = createTextSkeleton(x, y, relationTitle2, {
Expand Down Expand Up @@ -376,16 +427,6 @@ export const parseMermaidClassDiagram = (

const namespaces: NamespaceNode[] = mermaidParser.getNamespaces();

if (Object.keys(namespaces).length) {
Object.values(namespaces).forEach((namespace) => {
const namespaceClassData = parseClasses(namespace.classes, containerEl);
nodes.push(namespaceClassData.nodes);
lines.push(...namespaceClassData.lines);
text.push(...namespaceClassData.text);
classNodes.push(...namespaceClassData.nodes);
});
}

const classes = mermaidParser.getClasses();
if (Object.keys(classes).length) {
const classData = parseClasses(classes, containerEl);
Expand Down
Loading