mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-23 17:29:54 +02:00
Merge pull request #20 from Mermaid-Chart/yari/classBox-neofication
"classBox" Shape Neofication and Resize Support
This commit is contained in:
@@ -113,6 +113,402 @@
|
||||
<th>handdrawn-default</th>
|
||||
<th>classic-default</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="vertical-header">
|
||||
<button class="collapsible">Simple classNode</button>
|
||||
<div class="content">
|
||||
<div class="pre-scrollable">
|
||||
<pre>
|
||||
classNode
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "neo", "fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "dark","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "forest","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "handDrawn", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "classic", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="vertical-header">
|
||||
<button class="collapsible">Filled classNode</button>
|
||||
<div class="content">
|
||||
<div class="pre-scrollable">
|
||||
<pre>
|
||||
Filled classNode
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "neo", "fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "dark","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "forest","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "handDrawn", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "classic", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classNode {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="vertical-header">
|
||||
<button class="collapsible">Class with relation</button>
|
||||
<div class="content">
|
||||
<div class="pre-scrollable">
|
||||
<pre>
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "neo", "fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "dark","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "forest","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "handDrawn", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "classic", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
classA --> classB
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="vertical-header">
|
||||
<button class="collapsible">Full class diagram</button>
|
||||
<div class="content">
|
||||
<div class="pre-scrollable">
|
||||
<pre>
|
||||
nameSpace { classA --> classB } note
|
||||
</pre
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "neo", "fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram1" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "dark","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram2" class="mermaid">
|
||||
%%{init: {"look": "neo", "theme": "forest","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "handDrawn", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
<td>
|
||||
<pre id="diagram3" class="mermaid">
|
||||
%%{init: {"look": "classic", "theme": "default","fontFamily": "Arial"} }%%
|
||||
classDiagram
|
||||
namespace myNamespace {
|
||||
class classA {
|
||||
+int number
|
||||
method()
|
||||
}
|
||||
class classB {
|
||||
+int number
|
||||
-string text
|
||||
method()
|
||||
another_method()
|
||||
}
|
||||
}
|
||||
classA "1" o--> "*" classB : label
|
||||
note for classB "This is a note for classB"
|
||||
</pre
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="vertical-header">
|
||||
<button class="collapsible">Simple State (only id)</button>
|
||||
|
@@ -54,6 +54,9 @@
|
||||
//layout: 'elk',
|
||||
fontFamily: 'Kalam',
|
||||
logLevel: 1,
|
||||
class: {
|
||||
hideEmptyMembersBox: true,
|
||||
},
|
||||
});
|
||||
|
||||
let shape = 'circle';
|
||||
@@ -70,6 +73,41 @@
|
||||
n84@{ shape: '${shape}'}
|
||||
`;
|
||||
|
||||
let code2 = `
|
||||
classDiagram
|
||||
class class1 {
|
||||
int num
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
method()
|
||||
}
|
||||
class class2 {
|
||||
int num
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
string test
|
||||
method()
|
||||
method()
|
||||
}
|
||||
class class3 {
|
||||
int test
|
||||
}
|
||||
<<interface>> class3
|
||||
class class4 {
|
||||
int[] id
|
||||
method()
|
||||
method()
|
||||
method()
|
||||
method()
|
||||
}
|
||||
<<interface>> class4
|
||||
`;
|
||||
|
||||
let positions = {
|
||||
edges: {},
|
||||
nodes: {
|
||||
@@ -104,7 +142,37 @@
|
||||
},
|
||||
};
|
||||
|
||||
const { svg } = await mermaid.render('the-id-of-the-svg', code, undefined, positions);
|
||||
let positions2 = {
|
||||
edges: {},
|
||||
nodes: {
|
||||
class1: {
|
||||
x: 0,
|
||||
y: 10,
|
||||
width: 100,
|
||||
height: 400,
|
||||
},
|
||||
class2: {
|
||||
x: -300,
|
||||
y: 100,
|
||||
width: 100,
|
||||
height: 0,
|
||||
},
|
||||
class3: {
|
||||
x: 400,
|
||||
y: 10,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
class4: {
|
||||
x: 800,
|
||||
y: 10,
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { svg } = await mermaid.render('the-id-of-the-svg', code2, undefined, positions2);
|
||||
const elem = document.querySelector('#diagram');
|
||||
elem.innerHTML = svg;
|
||||
</script>
|
||||
|
@@ -38,7 +38,13 @@ export const getClasses = function (
|
||||
return diagramObj.db.getClasses();
|
||||
};
|
||||
|
||||
export const draw = async function (text: string, id: string, _version: string, diag: any) {
|
||||
export const draw = async function (
|
||||
text: string,
|
||||
id: string,
|
||||
_version: string,
|
||||
diag: any,
|
||||
positions: any
|
||||
) {
|
||||
log.info('REF0:');
|
||||
log.info('Drawing class diagram (v3)', id);
|
||||
const { securityLevel, state: conf, layout } = getConfig();
|
||||
@@ -60,7 +66,7 @@ export const draw = async function (text: string, id: string, _version: string,
|
||||
data4Layout.rankSpacing = conf?.rankSpacing || 50;
|
||||
data4Layout.markers = ['aggregation', 'extension', 'composition', 'dependency', 'lollipop'];
|
||||
data4Layout.diagramId = id;
|
||||
await render(data4Layout, svg);
|
||||
await render(data4Layout, svg, positions);
|
||||
const padding = 8;
|
||||
utils.insertTitle(
|
||||
svg,
|
||||
|
@@ -15,6 +15,7 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
const PADDING = config.class!.padding ?? 12;
|
||||
const GAP = PADDING;
|
||||
const useHtmlLabels = node.useHtmlLabels ?? evaluate(config.htmlLabels) ?? true;
|
||||
|
||||
// Treat node as classNode
|
||||
const classNode = node as unknown as ClassNode;
|
||||
classNode.annotations = classNode.annotations ?? [];
|
||||
@@ -49,15 +50,25 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
options.fillStyle = 'solid';
|
||||
}
|
||||
|
||||
const w = bbox.width;
|
||||
let h = bbox.height;
|
||||
const w = Math.max(node.width ?? 0, bbox.width);
|
||||
let h = Math.max(node.height ?? 0, bbox.height);
|
||||
const nodeHeightGreater = (node.height ?? 0) > bbox.height;
|
||||
if (classNode.members.length === 0 && classNode.methods.length === 0) {
|
||||
h += GAP;
|
||||
} else if (classNode.members.length > 0 && classNode.methods.length === 0) {
|
||||
h += GAP * 2;
|
||||
}
|
||||
|
||||
const x = -w / 2;
|
||||
const y = -h / 2;
|
||||
let extraHeight = renderExtraBox
|
||||
? PADDING * 2
|
||||
: classNode.members.length === 0 && classNode.methods.length === 0
|
||||
? -PADDING
|
||||
: 0;
|
||||
if (nodeHeightGreater) {
|
||||
extraHeight = PADDING * 2;
|
||||
}
|
||||
|
||||
// Create and center rectangle
|
||||
const roughRect = rc.rectangle(
|
||||
@@ -70,13 +81,7 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
? -PADDING / 2
|
||||
: 0),
|
||||
w + 2 * PADDING,
|
||||
h +
|
||||
2 * PADDING +
|
||||
(renderExtraBox
|
||||
? PADDING * 2
|
||||
: classNode.members.length === 0 && classNode.methods.length === 0
|
||||
? -PADDING
|
||||
: 0),
|
||||
h + 2 * PADDING + extraHeight,
|
||||
options
|
||||
);
|
||||
|
||||
@@ -85,6 +90,31 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
const rectBBox = rect.node()!.getBBox();
|
||||
|
||||
// Rect is centered so now adjust labels.
|
||||
const annotationGroupHeight =
|
||||
(shapeSvg.select('.annotation-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
const labelGroupHeight =
|
||||
(shapeSvg.select('.label-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
const membersGroupHeight =
|
||||
(shapeSvg.select('.members-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
|
||||
// Y value in the middle of the first line and remaining space.
|
||||
const methodsAreaPlacement =
|
||||
(annotationGroupHeight +
|
||||
labelGroupHeight +
|
||||
y +
|
||||
PADDING -
|
||||
(y -
|
||||
PADDING -
|
||||
(renderExtraBox
|
||||
? PADDING
|
||||
: classNode.members.length === 0 && classNode.methods.length === 0
|
||||
? -PADDING / 2
|
||||
: 0))) /
|
||||
2;
|
||||
|
||||
// TODO: Fix types
|
||||
shapeSvg.selectAll('.text').each((_: any, i: number, nodes: any) => {
|
||||
const text = select<any, unknown>(nodes[i]);
|
||||
@@ -110,6 +140,30 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
: classNode.members.length === 0 && classNode.methods.length === 0
|
||||
? -PADDING / 2
|
||||
: 0);
|
||||
if (text.attr('class').includes('methods-group')) {
|
||||
if (nodeHeightGreater) {
|
||||
newTranslateY =
|
||||
Math.max(
|
||||
methodsAreaPlacement,
|
||||
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 2 + PADDING
|
||||
) +
|
||||
GAP * 2;
|
||||
} else {
|
||||
newTranslateY =
|
||||
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 4 + PADDING;
|
||||
}
|
||||
}
|
||||
if (
|
||||
classNode.members.length === 0 &&
|
||||
classNode.methods.length === 0 &&
|
||||
config.class?.hideEmptyMembersBox
|
||||
) {
|
||||
if (classNode.annotations.length > 0) {
|
||||
newTranslateY = translateY - GAP;
|
||||
} else {
|
||||
newTranslateY = translateY;
|
||||
}
|
||||
}
|
||||
if (!useHtmlLabels) {
|
||||
// Fix so non html labels are better centered.
|
||||
// BBox of text seems to be slightly different when calculated so we offset
|
||||
@@ -132,22 +186,16 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
});
|
||||
|
||||
// Render divider lines.
|
||||
const annotationGroupHeight =
|
||||
(shapeSvg.select('.annotation-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
const labelGroupHeight =
|
||||
(shapeSvg.select('.label-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
const membersGroupHeight =
|
||||
(shapeSvg.select('.members-group').node() as SVGGraphicsElement).getBBox().height -
|
||||
(renderExtraBox ? PADDING / 2 : 0) || 0;
|
||||
// Line y-values are offset by 0.001 so gradient stroke can apply.
|
||||
// If y-values are the same then the height of the bounding box is zero and it doesn't work.
|
||||
// First line (under label)
|
||||
if (classNode.members.length > 0 || classNode.methods.length > 0 || renderExtraBox) {
|
||||
const firstLineY = annotationGroupHeight + labelGroupHeight + y + PADDING;
|
||||
const roughLine = rc.line(
|
||||
rectBBox.x,
|
||||
annotationGroupHeight + labelGroupHeight + y + PADDING,
|
||||
firstLineY,
|
||||
rectBBox.x + rectBBox.width,
|
||||
annotationGroupHeight + labelGroupHeight + y + PADDING,
|
||||
firstLineY + 0.001,
|
||||
options
|
||||
);
|
||||
const line = shapeSvg.insert(() => roughLine);
|
||||
@@ -156,11 +204,13 @@ export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection
|
||||
|
||||
// Second line (under members)
|
||||
if (renderExtraBox || classNode.members.length > 0 || classNode.methods.length > 0) {
|
||||
const secondLineY =
|
||||
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 2 + PADDING;
|
||||
const roughLine = rc.line(
|
||||
rectBBox.x,
|
||||
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 2 + PADDING,
|
||||
nodeHeightGreater ? Math.max(methodsAreaPlacement, secondLineY) : secondLineY,
|
||||
rectBBox.x + rectBBox.width,
|
||||
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + PADDING + GAP * 2,
|
||||
(nodeHeightGreater ? Math.max(methodsAreaPlacement, secondLineY) : secondLineY) + 0.001,
|
||||
options
|
||||
);
|
||||
const line = shapeSvg.insert(() => roughLine);
|
||||
|
Reference in New Issue
Block a user