Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
58fd5dd
Add: demo for notes in namespace on classDialog
kairi003 Sep 2, 2024
2d2add5
Change: use Map for notes
kairi003 Sep 2, 2024
fc0c793
feat: Add support for adding notes to namespaces in class diagrams
kairi003 Sep 2, 2024
a101ce8
Merge branch 'develop' into feature/4706_allow_notes_in_namespace
kairi003 Sep 4, 2024
1963064
Merge branch 'develop' into feature/4706_allow_notes_in_namespace
kairi003 Sep 7, 2024
259a508
add e2e test
kairi003 Sep 7, 2024
2dd29be
add changelogs
kairi003 Sep 7, 2024
4ee1fe2
Merge branch 'master' into feature/4706_allow_notes_in_namespace
kairi003 Nov 8, 2024
02d368d
chore: relax git version constraint in Dockerfile to allow patch updates
kairi003 Nov 9, 2024
309ff6b
fix: Reimplement notes in namespaces for classRenderer-v3 (#5880)
kairi003 Nov 9, 2024
ec1c632
refactor: Add type annotations and optimize redundant set loop
kairi003 Nov 9, 2024
25c43fa
add: horizontal rules to demos/classchart.html
kairi003 Nov 9, 2024
70f679d
add: e2e test for classDiagram-v3
kairi003 Nov 10, 2024
c4e0826
Merge branch 'develop' into feature/4706_allow_notes_in_namespace
kairi003 Nov 10, 2024
875827f
Merge branch 'develop' into feature/4706_allow_notes_in_namespace
kairi003 Nov 20, 2024
d60b09c
Merge branch 'develop' into feature/4706_allow_notes_in_namespace
kairi003 Dec 1, 2024
3f46c94
Merge branch 'develop' into tmp/feature/4706_allow_notes_in_namespace
kairi003 Oct 25, 2025
49103ea
fix: update ClassDB and ClassTypes for improved type safety and consi…
kairi003 Oct 25, 2025
700aa10
fix: style broken caused by a mistake in merge 3f46c94a
kairi003 Oct 25, 2025
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/short-seals-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'mermaid': minor
---

feat: allow to put notes in namespaces on classDiagram
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ USER 0:0
RUN corepack enable \
&& corepack enable pnpm

RUN apk add --no-cache git~=2.43.4 \
RUN apk add --no-cache git~=2.43 \
&& git config --add --system safe.directory /mermaid

ENV NODE_OPTIONS="--max_old_space_size=8192"
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/rendering/classDiagram-v2.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
Expand Down
14 changes: 14 additions & 0 deletions cypress/integration/rendering/classDiagram-v3.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,20 @@ class C13["With Città foreign language"]
`
);
});
it('should add notes in namespaces', function () {
imgSnapshotTest(
`
classDiagram
note "This is a outer note"
note for C1 "This is a outer note for C1"
namespace Namespace1 {
note "This is a inner note"
note for C1 "This is a inner note for C1"
class C1
}
`
);
});
it('should render a simple class diagram with no members', () => {
imgSnapshotTest(
`
Expand Down
15 changes: 15 additions & 0 deletions demos/classchart.html
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ <h1>Class diagram demos</h1>
}
Admin --> Report : generates
</pre>
<hr />
<pre class="mermaid">
classDiagram
namespace Company.Project.Module {
Expand Down Expand Up @@ -240,6 +241,20 @@ <h1>Class diagram demos</h1>
Bike --> Square : "Logo Shape"

</pre>
<hr />
<pre class="mermaid">
classDiagram
note "This is a outer note"
note for Class1 "This is a outer note for Class1"
namespace ns {
note "This is a inner note"
note for Class1 "This is a inner note for Class1"
class Class1
class Class2
}
</pre>
<hr />

<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
Expand Down
111 changes: 63 additions & 48 deletions packages/mermaid/src/diagrams/class/classDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
ClassRelation,
ClassNode,
ClassNote,
ClassNoteMap,
ClassMap,
NamespaceMap,
NamespaceNode,
Expand All @@ -33,15 +34,16 @@ const sanitizeText = (txt: string) => common.sanitizeText(txt, getConfig());

export class ClassDB implements DiagramDB {
private relations: ClassRelation[] = [];
private classes = new Map<string, ClassNode>();
private classes: ClassMap = new Map<string, ClassNode>();
private readonly styleClasses = new Map<string, StyleClass>();
private notes: ClassNote[] = [];
private notes: ClassNoteMap = new Map<string, ClassNote>();
private interfaces: Interface[] = [];
// private static classCounter = 0;
private namespaces = new Map<string, NamespaceNode>();
private namespaceCounter = 0;

private functions: any[] = [];
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
private functions: Function[] = [];

constructor() {
this.functions.push(this.setupToolTips.bind(this));
Expand Down Expand Up @@ -124,7 +126,7 @@ export class ClassDB implements DiagramDB {
annotations: [],
styles: [],
domId: MERMAID_DOM_ID_PREFIX + name + '-' + classCounter,
} as ClassNode);
});

classCounter++;
}
Expand Down Expand Up @@ -155,12 +157,12 @@ export class ClassDB implements DiagramDB {

public clear() {
this.relations = [];
this.classes = new Map();
this.notes = [];
this.classes = new Map<string, ClassNode>();
this.notes = new Map<string, ClassNote>();
this.interfaces = [];
this.functions = [];
this.functions.push(this.setupToolTips.bind(this));
this.namespaces = new Map();
this.namespaces = new Map<string, NamespaceNode>();
this.namespaceCounter = 0;
this.direction = 'TB';
commonClear();
Expand All @@ -178,7 +180,12 @@ export class ClassDB implements DiagramDB {
return this.relations;
}

public getNotes() {
public getNote(id: string | number): ClassNote {
const key = typeof id === 'number' ? `note${id}` : id;
return this.notes.get(key)!;
}

public getNotes(): ClassNoteMap {
return this.notes;
}

Expand Down Expand Up @@ -279,16 +286,19 @@ export class ClassDB implements DiagramDB {
}
}

public addNote(text: string, className: string) {
public addNote(text: string, className: string): string {
const index = this.notes.size;
const note = {
id: `note${this.notes.length}`,
id: `note${index}`,
class: className,
text: text,
index: index,
};
this.notes.push(note);
this.notes.set(note.id, note);
return note.id;
}

public cleanupLabel(label: string) {
public cleanupLabel(label: string): string {
if (label.startsWith(':')) {
label = label.substring(1);
}
Expand Down Expand Up @@ -354,7 +364,7 @@ export class ClassDB implements DiagramDB {
});
}

public getTooltip(id: string, namespace?: string) {
public getTooltip(id: string, namespace?: string): string | undefined {
if (namespace && this.namespaces.has(namespace)) {
return this.namespaces.get(namespace)!.classes.get(id)!.tooltip;
}
Expand Down Expand Up @@ -534,10 +544,11 @@ export class ClassDB implements DiagramDB {

this.namespaces.set(id, {
id: id,
classes: new Map(),
children: {},
classes: new Map<string, ClassNode>(),
notes: new Map<string, ClassNote>(),
children: new Map<string, NamespaceNode>(),
domId: MERMAID_DOM_ID_PREFIX + id + '-' + this.namespaceCounter,
} as NamespaceNode);
});

this.namespaceCounter++;
}
Expand All @@ -555,16 +566,23 @@ export class ClassDB implements DiagramDB {
*
* @param id - ID of the namespace to add
* @param classNames - IDs of the class to add
* @param noteNames - IDs of the notes to add
* @public
*/
public addClassesToNamespace(id: string, classNames: string[]) {
public addClassesToNamespace(id: string, classNames: string[], noteNames: string[]) {
if (!this.namespaces.has(id)) {
return;
}
for (const name of classNames) {
const { className } = this.splitClassNameAndType(name);
this.classes.get(className)!.parent = id;
this.namespaces.get(id)!.classes.set(className, this.classes.get(className)!);
const classNode = this.getClass(className);
classNode.parent = id;
this.namespaces.get(id)!.classes.set(className, classNode);
}
for (const noteName of noteNames) {
const noteNode = this.getNote(noteName);
noteNode.parent = id;
this.namespaces.get(id)!.notes.set(noteName, noteNode);
}
}

Expand Down Expand Up @@ -617,36 +635,32 @@ export class ClassDB implements DiagramDB {
const edges: Edge[] = [];
const config = getConfig();

for (const namespaceKey of this.namespaces.keys()) {
const namespace = this.namespaces.get(namespaceKey);
if (namespace) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: [],
look: config.look,
};
nodes.push(node);
}
for (const namespace of this.namespaces.values()) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: [],
look: config.look,
};
nodes.push(node);
}

for (const classKey of this.classes.keys()) {
const classNode = this.classes.get(classKey);
if (classNode) {
const node = classNode as unknown as Node;
node.parentId = classNode.parent;
node.look = config.look;
nodes.push(node);
}
for (const classNode of this.classes.values()) {
const node: Node = {
...classNode,
type: undefined,
isGroup: false,
parentId: classNode.parent,
look: config.look,
};
nodes.push(node);
}

let cnt = 0;
for (const note of this.notes) {
cnt++;
for (const note of this.notes.values()) {
const noteNode: Node = {
id: note.id,
label: note.text,
Expand All @@ -660,14 +674,15 @@ export class ClassDB implements DiagramDB {
`stroke: ${config.themeVariables.noteBorderColor}`,
],
look: config.look,
parentId: note.parent,
};
nodes.push(noteNode);

const noteClassId = this.classes.get(note.class)?.id ?? '';
const noteClassId = this.classes.get(note.class)?.id;

if (noteClassId) {
const edge: Edge = {
id: `edgeNote${cnt}`,
id: `edgeNote${note.index}`,
start: note.id,
end: noteClassId,
type: 'normal',
Expand Down Expand Up @@ -697,7 +712,7 @@ export class ClassDB implements DiagramDB {
nodes.push(interfaceNode);
}

cnt = 0;
let cnt = 0;
for (const classRelation of this.relations) {
cnt++;
const edge: Edge = {
Expand Down
8 changes: 4 additions & 4 deletions packages/mermaid/src/diagrams/class/classDiagram.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ class C13["With Città foreign language"]
note "This is a keyword: ${keyword}. It truly is."
`;
parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});

it.each(keywords)(
Expand All @@ -427,7 +427,7 @@ class C13["With Città foreign language"]
note "${keyword}"`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
}
);

Expand All @@ -441,7 +441,7 @@ class C13["With Città foreign language"]
`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
expect(classDb.getNote(0).text).toEqual(`This is a keyword: ${keyword}. It truly is.`);
});

it.each(keywords)(
Expand All @@ -456,7 +456,7 @@ class C13["With Città foreign language"]
`;

parser.parse(str);
expect(classDb.getNotes()[0].text).toEqual(`${keyword}`);
expect(classDb.getNote(0).text).toEqual(`${keyword}`);
}
);

Expand Down
Loading
Loading