Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…alidraw into excalidraw-master
  • Loading branch information
zsviczian committed Apr 4, 2024
2 parents 3210d9a + 3251d2f commit 23356d3
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 83 deletions.
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

0 comments on commit 23356d3

Please sign in to comment.