Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions .changeset/old-eagles-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': patch
---

fix: Added support for styling class diagram elements based on stereotype annotations
84 changes: 84 additions & 0 deletions cypress/integration/rendering/classDiagram-v3.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1028,4 +1028,88 @@ class C13["With Città foreign language"]
{ logLevel: 1, htmlLabels: true }
);
});
it('should render a full class diagram using interface annotation', () => {
imgSnapshotTest(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07 .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
size()
}

`,
{ logLevel: 1, htmlLabels: true }
);
});
it('should render a full class diagram using abstract annotation', () => {
imgSnapshotTest(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
&lt;&lt;abstract&gt;&gt; Class01
Class03 "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07 .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
size()
}

`,
{ logLevel: 1, htmlLabels: true }
);
});
it('should render a full class diagram using enumeration annotation', () => {
imgSnapshotTest(
`
classDiagram
Class01 <|-- AveryLongClass : Cool
&lt;&lt;enumeration&gt;&gt; Class01
Class03 "0" *-- "0..n" Class04
Class05 "1" o-- "many" Class06
Class07 .. Class08
Class09 "many" --> "1" C2 : Where am i?
Class09 "0" --* "1..n" C3
Class09 --|> Class07
Class07 : equals()
Class07 : Object[] elementData
Class01 : #size()
Class01 : -int chimp
Class01 : +int gorilla
Class08 <--> C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
size()
}

`,
{ logLevel: 1, htmlLabels: true }
);
});
});
36 changes: 34 additions & 2 deletions packages/mermaid/src/diagrams/class/shapeUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,46 @@ export async function textHelper<T extends SVGGraphicsElement>(
annotationGroup = shapeSvg.insert('g').attr('class', 'annotation-group text');
if (node.annotations.length > 0) {
const annotation = node.annotations[0];

await addText(annotationGroup, { text: `«${annotation}»` } as unknown as ClassMember, 0);

annotationGroup.style('opacity', '0');

const annotationGroupBBox = annotationGroup.node()!.getBBox();
annotationGroupHeight = annotationGroupBBox.height;
} else {
annotationGroupHeight = 0;
}

labelGroup = shapeSvg.insert('g').attr('class', 'label-group text');
await addText(labelGroup, node, 0, ['font-weight: bolder']);

// Determine styling based on annotations
let labelStyles = ['font-weight: bolder']; // Default bold style
let labelClass = '';
if (node.annotations && node.annotations.length > 0) {
const annotation = node.annotations[0].toLowerCase();
switch (annotation) {
case 'abstract':
labelClass = 'abstract';
labelStyles = [];
break;
case 'enumeration':
labelClass = 'enumeration';
labelStyles = [];
break;
case 'interface':
labelClass = 'interface';
labelStyles = [];
break;
default:
labelStyles = ['font-weight: bolder'];
break;
}
}
// Apply the CSS class to the label group
labelGroup.attr('class', `label-group text classTitle ${labelClass}`);

await addText(labelGroup, node, 0, labelStyles);
const labelGroupBBox = labelGroup.node()!.getBBox();
labelGroupHeight = labelGroupBBox.height;

Expand All @@ -71,7 +103,7 @@ export async function textHelper<T extends SVGGraphicsElement>(
// Center annotation
if (annotationGroup !== null) {
const annotationGroupBBox = annotationGroup.node()!.getBBox();
annotationGroup.attr('transform', `translate(${-annotationGroupBBox.width / 2})`);
annotationGroup.attr('transform', `translate(${-annotationGroupBBox.width / 2}, 0)`);
}

// Adjust label
Expand Down
18 changes: 18 additions & 0 deletions packages/mermaid/src/diagrams/class/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ g.clickable {
cursor: pointer;
}

g.annotation-group[style*="opacity: 0"] {
pointer-events: none;
}

g.classGroup rect {
fill: ${options.mainBkg};
stroke: ${options.nodeBorder};
Expand Down Expand Up @@ -148,6 +152,20 @@ g.classGroup line {
stroke: ${options.lineColor} !important;
stroke-width: 1;
}

.classTitle.abstract {
font-style: italic;
font-weight: normal;
}

.classTitle.enumeration {
text-decoration: underline;
font-weight: normal;
}

.classTitle.interface {
font-weight: bold;
}

.edgeTerminals {
font-size: 11px;
Expand Down
Loading