Compare commits

..

12 Commits

Author SHA1 Message Date
autofix-ci[bot]
39b8ad2b09 [autofix.ci] apply automated fixes 2025-10-11 21:20:54 +00:00
Justin Greywolf
709880178e feat: add support for link statements within namespace blocks in class diagrams
Resolves #4700
2025-10-11 14:13:48 -07:00
Justin Greywolf
d80a638e55 Merge pull request #6023 from yari-dewalt/fix_lollipop_stroke
Fix: Delete 'stroke: black' from lollipop marker so it matches themes and edge paths
2025-10-11 20:43:08 +00:00
Justin Greywolf
7a869c08a2 Merge pull request #6026 from yari-dewalt/bug/5669_class-diagram-fix-namespace-themes
Fix: Class diagram namespaces black lines, not responding to theme variables
2025-10-11 20:42:53 +00:00
Justin Greywolf
44e8cbb1de Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-10-11 13:27:47 -07:00
Justin Greywolf
efe38b8425 Merge branch 'develop' into fix_lollipop_stroke 2025-10-11 13:26:59 -07:00
autofix-ci[bot]
47297f7c26 [autofix.ci] apply automated fixes 2025-02-26 17:49:51 +00:00
Yari DeWalt
967aa0629e Merge branch 'develop' into bug/5669_class-diagram-fix-namespace-themes 2025-02-26 09:45:14 -08:00
Yari DeWalt
04b20a79b9 Merge branch 'develop' into fix_lollipop_stroke 2025-02-26 09:20:16 -08:00
yari-dewalt
4ff2ae9f4e Add styles for clusters and remove hard-coded styling for namespace nodes 2024-11-06 09:00:21 -08:00
Yari DeWalt
7a729e8f16 Merge branch 'develop' into fix_lollipop_stroke 2024-11-04 10:21:00 -08:00
yari-dewalt
3c7fd95617 Remove black stroke from lollipops so it matches edge paths 2024-11-04 10:15:51 -08:00
9 changed files with 88 additions and 55 deletions

View File

@@ -603,6 +603,10 @@
</div>
<div class="test">
<pre class="mermaid">
---
config:
theme: dark
---
classDiagram
test ()--() test2
</pre>

View File

@@ -702,6 +702,7 @@ classDiagram
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
You would define these actions on a separate line after all classes have been declared.
If you have classes defined within a namespace, you can also add interaction definitions within the namespace definition, after the class(es) is defined
```
action className "reference" "tooltip"

View File

@@ -627,7 +627,7 @@ export class ClassDB implements DiagramDB {
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'],
cssStyles: [],
look: config.look,
};
nodes.push(node);

View File

@@ -88,6 +88,50 @@ describe('given a basic class diagram, ', function () {
expect(relations[0].title).toBe('generates');
});
it('should handle link statements within namespaces', function () {
spyOn(classDb, 'setLink');
const str = `classDiagram
namespace MyNamespace {
class UserService {
+createUser()
+deleteUser()
}
class PaymentService {
+processPayment()
+refund()
}
link UserService "https://example.com/user-service"
link PaymentService "https://example.com/payment-service" "Payment Service Documentation"
}`;
parser.parse(str);
// Verify setLink was called for both classes
expect(classDb.setLink).toHaveBeenCalledWith(
'UserService',
'https://example.com/user-service'
);
expect(classDb.setLink).toHaveBeenCalledWith(
'PaymentService',
'https://example.com/payment-service'
);
// Verify the classes have the correct links and are in the namespace
const userService = classDb.getClass('UserService');
const paymentService = classDb.getClass('PaymentService');
expect(userService.parent).toBe('MyNamespace');
expect(userService.link).toBe('https://example.com/user-service');
expect(userService.cssClasses).toBe('default clickable');
expect(paymentService.parent).toBe('MyNamespace');
expect(paymentService.link).toBe('https://example.com/payment-service');
expect(paymentService.tooltip).toBe('Payment Service Documentation');
expect(paymentService.cssClasses).toBe('default clickable');
});
it('should handle accTitle and accDescr', function () {
const str = `classDiagram
accTitle: My Title

View File

@@ -275,14 +275,25 @@ statement
;
namespaceStatement
: namespaceIdentifier STRUCT_START classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
| namespaceIdentifier STRUCT_START NEWLINE classStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
: namespaceIdentifier STRUCT_START namespaceBodyStatements STRUCT_STOP { yy.addClassesToNamespace($1, $3); }
| namespaceIdentifier STRUCT_START NEWLINE namespaceBodyStatements STRUCT_STOP { yy.addClassesToNamespace($1, $4); }
;
namespaceIdentifier
: NAMESPACE namespaceName { $$=$2; yy.addNamespace($2); }
;
namespaceBodyStatements
: namespaceBodyStatement { $$=[$1].filter(s => s !== null); }
| namespaceBodyStatement NEWLINE { $$=[$1].filter(s => s !== null); }
| namespaceBodyStatement NEWLINE namespaceBodyStatements { var filtered = [$1].filter(s => s !== null); $3.unshift(...filtered); $$=$3; }
;
namespaceBodyStatement
: classStatement { $$=$1; }
| clickStatement { $$=null; /* clickStatements don't return class names, but are processed for side effects */ }
;
classStatements
: classStatement {$$=[$1]}
| classStatement NEWLINE {$$=[$1]}

View File

@@ -13,6 +13,30 @@ const getStyles = (options) =>
}
.cluster-label text {
fill: ${options.titleColor};
}
.cluster-label span {
color: ${options.titleColor};
}
.cluster-label span p {
background-color: transparent;
}
.cluster rect {
fill: ${options.clusterBkg};
stroke: ${options.clusterBorder};
stroke-width: 1px;
}
.cluster text {
fill: ${options.titleColor};
}
.cluster span {
color: ${options.titleColor};
}
.nodeLabel, .edgeLabel {
color: ${options.classText};
}

View File

@@ -452,6 +452,7 @@ classDiagram
It is possible to bind a click event to a node. The click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
You would define these actions on a separate line after all classes have been declared.
If you have classes defined within a namespace, you can also add interaction definitions within the namespace definition, after the class(es) is defined
```
action className "reference" "tooltip"

View File

@@ -27,53 +27,6 @@ import { log } from '../../../logger.js';
import { getSubGraphTitleMargins } from '../../../utils/subGraphTitleMargins.js';
import { getConfig } from '../../../diagram-api/diagramAPI.js';
/**
* Apply absolute note positioning after dagre layout
* This fixes the issue where TB and LR directions position notes differently
* by making note positioning truly absolute
*/
const positionNotes = (graph) => {
const noteStatePairs = [];
graph.nodes().forEach((nodeId) => {
const node = graph.node(nodeId);
if (node.position && node.shape === 'note') {
const edges = graph.nodeEdges(nodeId);
for (const edge of edges) {
const otherNodeId = edge.v === nodeId ? edge.w : edge.v;
const otherNode = graph.node(otherNodeId);
if (otherNode && otherNode.shape !== 'note' && otherNode.shape !== 'noteGroup') {
noteStatePairs.push({
noteId: nodeId,
noteNode: node,
stateId: otherNodeId,
stateNode: otherNode,
position: node.position,
});
}
}
}
});
noteStatePairs.forEach(({ noteNode, stateNode, position }) => {
const spacing = 60;
let noteX = noteNode.x;
let noteY = stateNode.y;
if (position === 'right of') {
noteX = stateNode.x + stateNode.width / 2 + spacing + noteNode.width / 2;
} else if (position === 'left of') {
noteX = stateNode.x - stateNode.width / 2 - spacing - noteNode.width / 2;
}
noteNode.x = noteX;
noteNode.y = noteY;
});
};
const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, siteConfig) => {
log.warn('Graph in recursive render:XAX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir;
@@ -211,9 +164,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
dagreLayout(graph);
// Apply absolute note positioning after dagre layout
positionNotes(graph);
log.info('Graph after layout:', JSON.stringify(graphlibJson.write(graph)));
// Move the nodes to the correct place
let diff = 0;

View File

@@ -130,7 +130,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent')
.attr('cx', 7)
.attr('cy', 7)
@@ -147,7 +146,6 @@ const lollipop = (elem, type, id) => {
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('circle')
.attr('stroke', 'black')
.attr('fill', 'transparent')
.attr('cx', 7)
.attr('cy', 7)