Merge branch 'neo-new-shapes' of https://github.com/Mermaid-Chart/alana-mermaid into redux-theme

This commit is contained in:
omkarht
2025-01-23 14:58:48 +05:30
58 changed files with 2103 additions and 1222 deletions

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: architecture diagrams no longer grow to extreme heights due to conflicting alignments

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
Fixes for consistent edge id creation & handling edge cases for animate edge feature

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
Fix for issue #6195 - allowing @ signs inside node labels

View File

@@ -0,0 +1,8 @@
---
'mermaid': minor
---
Flowchart new syntax for node metadata bugs
- Incorrect label mapping for nodes when using `&`
- Syntax error when `}` with trailing spaces before new line

View File

@@ -0,0 +1,5 @@
---
'mermaid': minor
---
Adding support for animation of flowchart edges

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each flowchart

View File

@@ -0,0 +1,5 @@
---
'mermaid': patch
---
fix: Gantt, Sankey and User Journey diagram are now able to pick font-family from mermaid config.

View File

@@ -171,6 +171,58 @@ describe.skip('architecture diagram', () => {
`
);
});
it('should render an architecture diagram with a resonable height', () => {
imgSnapshotTest(
`architecture-beta
group federated(cloud)[Federated Environment]
service server1(server)[System] in federated
service edge(server)[Edge Device] in federated
server1:R -- L:edge
group on_prem(cloud)[Hub]
service firewall(server)[Firewall Device] in on_prem
service server(server)[Server] in on_prem
firewall:R -- L:server
service db1(database)[db1] in on_prem
service db2(database)[db2] in on_prem
service db3(database)[db3] in on_prem
service db4(database)[db4] in on_prem
service db5(database)[db5] in on_prem
service db6(database)[db6] in on_prem
junction mid in on_prem
server:B -- T:mid
junction 1Leftofmid in on_prem
1Leftofmid:R -- L:mid
1Leftofmid:B -- T:db1
junction 2Leftofmid in on_prem
2Leftofmid:R -- L:1Leftofmid
2Leftofmid:B -- T:db2
junction 3Leftofmid in on_prem
3Leftofmid:R -- L:2Leftofmid
3Leftofmid:B -- T:db3
junction 1RightOfMid in on_prem
mid:R -- L:1RightOfMid
1RightOfMid:B -- T:db4
junction 2RightOfMid in on_prem
1RightOfMid:R -- L:2RightOfMid
2RightOfMid:B -- T:db5
junction 3RightOfMid in on_prem
2RightOfMid:R -- L:3RightOfMid
3RightOfMid:B -- T:db6
edge:R -- L:firewall
`
);
});
});
// Skipped as the layout is not deterministic, and causes issues in E2E tests.

View File

@@ -1076,4 +1076,41 @@ end
);
});
});
describe('New @ sytax for node metadata edge cases', () => {
it('should be possible to use @ syntax to add labels on multi nodes', () => {
imgSnapshotTest(
`flowchart TB
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
`,
{}
);
});
it('should be possible to use @ syntax to add labels with trail spaces and &', () => {
imgSnapshotTest(
`flowchart TB
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
`,
{}
);
});
it('should be possible to use @ syntax to add labels with trail spaces', () => {
imgSnapshotTest(
`flowchart TB
n2["label for n2"]
n4@{ label: "labe for n4"}
n5@{ label: "labe for n5"}
`,
{}
);
});
it('should be possible to use @ syntax to add labels with trail spaces and edge/link', () => {
imgSnapshotTest(
`flowchart TD
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
D@{label: "for D"}
`,
{}
);
});
});
});

View File

@@ -86,29 +86,70 @@
/* tspan {
font-size: 6px !important;
} */
/* .flowchart-link {
stroke-dasharray: 4, 4 !important;
animation: flow 1s linear infinite;
animation: dashdraw 4.93282s linear infinite;
stroke-width: 2px !important;
} */
@keyframes dashdraw {
from {
stroke-dashoffset: 0;
}
}
/*stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: 4.932820s linear infinite;*/
/* stroke-width:2;stroke-dasharray:10.000000,9.865639;stroke-dashoffset:-198.656393;animation: dashdraw 4.932820s linear infinite;*/
</style>
</head>
<body>
<pre id="diagram4" class="mermaid">
---
config:
layout: elk
---
flowchart LR
subgraph S2
subgraph s1["APA"]
D{"Use the editor"}
end
D -- Mermaid js --> I{"fa:fa-code Text"}
D --> I
D --> I
end
AB["apa@apa@"] --> B(("`apa@apa`"))
</pre>
<pre id="diagram4" class="mermaid">
flowchart
D(("for D"))
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
A e1@==> B
e1@{ animate: true}
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
A e1@--> B
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
class e1 animate
</pre>
<h2>infinite</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class e1 animate
</pre>
<h2>Mermaid - edge-animation-slow</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
e1@{ animation: fast}
</pre>
<h2>Mermaid - edge-animation-fast</h2>
<pre id="diagram4" class="mermaid2">
flowchart LR
A e1@--> B
classDef animate stroke-dasharray: 1000,stroke-dashoffset: 1000,animation: dash 10s linear;
class e1 edge-animation-fast
</pre>
<pre id="diagram4" class="mermaid2">
info </pre
>
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -133,7 +174,7 @@ config:
end
end
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -146,7 +187,7 @@ config:
D-->I
D-->I
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -185,7 +226,7 @@ flowchart LR
n8@{ shape: rect}
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -201,7 +242,7 @@ flowchart LR
</pre>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -210,7 +251,7 @@ flowchart LR
A{A} --> B & C
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk
@@ -222,7 +263,7 @@ flowchart LR
end
</pre
>
<pre id="diagram4" class="mermaid">
<pre id="diagram4" class="mermaid2">
---
config:
layout: elk

View File

@@ -62,54 +62,23 @@
<body style="display: flex; gap: 2rem; flex-direction: row">
<pre id="diagram4" class="mermaid">
flowchart TD
B2@{ icon: "fa:bell", form: "square", label: "B2 agsyua duadu", pos: "b" }
flowchart
A --> A
subgraph B
B1 --> B1
end
subgraph C
subgraph C1
C2 --> C2
subgraph D
D1 --> D1
end
D --> D
end
C1 --> C1
end
</pre>
<pre id="diagram4" class="mermaid">
flowchart TD
B2@{ icon: "logos:aws", form: "rounded", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }
</pre
>
<pre id="diagram4" class="mermaid">
flowchart TD
B2@{ icon: "fa:bell", form: "circle", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }
</pre
>
<pre id="diagram4" class="mermaid">
flowchart TD
B2@{ icon: "fa:bell", label: "B2 aiduaid uyawduad uaduabd uyduadb", pos: "b" }
</pre
>
<pre id="diagram43" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", form: "square", label: "B2", pos: "t", h: 40, w: 30 }@
B2 --test--> C
D --> B2 --> E
</pre
>
<pre id="diagram4" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", label: "B2 awiugdawu uydgayuiwd wuydguy", pos: "b", h: 40, w: 30 }@
B2 --test--> C
</pre
>
<pre id="diagram43" class="mermaid2">
flowchart BT
A --test2--> B2@{ icon: "fa:bell", label: "B2 dawuygd ayuwgd uy", pos: "t", h: 40, w: 30 }@
B2 --test--> C
</pre
>
<pre id="diagram6" class="mermaid2">
flowchart TB
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "t", w: 200, h: 100 }@ --> C
</pre
>
<pre id="diagram6" class="mermaid2">
flowchart TB
A --> B2@{ icon: "fa:bell", form: "circle", label: "test augfuyfavf ydvaubfuac", pos: "b", w: 200, h: 100 }@ --> C
D --> B2 --> E
</pre
>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
@@ -123,9 +92,7 @@
{
name: 'fa',
loader: () =>
fetch('https://unpkg.com/@iconify-json/fa6-regular/icons.json').then((res) =>
res.json()
),
fetch('https://unpkg.com/@iconify-json/fa6-solid/icons.json').then((res) => res.json()),
},
]);
mermaid.parseError = function (err, hash) {
@@ -137,7 +104,7 @@
mermaid.initialize({
// theme: 'base',
// handdrawnSeed: 12,
// look: 'classic',
look: 'classic',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
// 'elk.nodePlacement.strategy': 'SIMPLE',
// 'elk.nodePlacement.strategy': 'LAYERED',
@@ -146,16 +113,19 @@
// layout: 'elk',
// layout: 'fixed',
// htmlLabels: false,
flowchart: { titleTopMargin: 10, htmlLabels: true },
flowchart: { titleTopMargin: 10, padding: 0, htmlLabels: true },
// fontFamily: 'Caveat',
fontFamily: 'Kalam',
// fontFamily: 'courier',
// fontFamily: 'Kalam',
fontFamily: 'courier',
sequence: {
actorFontFamily: 'courier',
noteFontFamily: 'courier',
messageFontFamily: 'courier',
},
fontSize: 12,
themeVariables: {
fontSize: 50,
fontFamily: 'courier',
},
logLevel: 0,
securityLevel: 'loose',
});

View File

@@ -20,7 +20,7 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:145](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L145)
[packages/mermaid/src/rendering-util/types.ts:149](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L149)
---
@@ -30,7 +30,7 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:144](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L144)
[packages/mermaid/src/rendering-util/types.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L148)
---
@@ -40,4 +40,4 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:143](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L143)
[packages/mermaid/src/rendering-util/types.ts:147](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L147)

View File

@@ -16,7 +16,7 @@
#### Defined in
[packages/mermaid/src/config.type.ts:122](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L122)
[packages/mermaid/src/config.type.ts:133](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L133)
---
@@ -26,7 +26,7 @@
#### Defined in
[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
[packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
---
@@ -39,7 +39,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:141](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L141)
[packages/mermaid/src/config.type.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L152)
---
@@ -49,7 +49,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
[packages/mermaid/src/config.type.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L212)
---
@@ -59,7 +59,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
[packages/mermaid/src/config.type.ts:209](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L209)
---
@@ -69,7 +69,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:187](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L187)
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
---
@@ -79,7 +79,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L113)
[packages/mermaid/src/config.type.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L124)
---
@@ -93,7 +93,7 @@ You can set this attribute to base the seed on a static string.
#### Defined in
[packages/mermaid/src/config.type.ts:181](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L181)
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
---
@@ -111,7 +111,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
---
@@ -121,7 +121,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
[packages/mermaid/src/config.type.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L213)
---
@@ -139,7 +139,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L91)
[packages/mermaid/src/config.type.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L102)
---
@@ -149,7 +149,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:189](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L189)
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
---
@@ -159,7 +159,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:182](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L182)
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
---
@@ -173,7 +173,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:121](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L121)
[packages/mermaid/src/config.type.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L132)
---
@@ -183,7 +183,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
[packages/mermaid/src/config.type.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L215)
---
@@ -197,7 +197,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163)
[packages/mermaid/src/config.type.ts:174](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L174)
---
@@ -207,7 +207,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:184](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L184)
[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
---
@@ -217,7 +217,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
[packages/mermaid/src/config.type.ts:208](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L208)
---
@@ -229,7 +229,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L76)
[packages/mermaid/src/config.type.ts:87](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L87)
---
@@ -239,7 +239,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L114)
[packages/mermaid/src/config.type.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L125)
---
@@ -249,7 +249,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:185](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L185)
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
---
@@ -259,7 +259,7 @@ Defines the seed to be used when using handDrawn look. This is important for the
#### Defined in
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
[packages/mermaid/src/config.type.ts:207](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L207)
---
@@ -271,7 +271,7 @@ Defines which layout algorithm to use for rendering the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L81)
[packages/mermaid/src/config.type.ts:92](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L92)
---
@@ -286,7 +286,7 @@ fall back to legacy rendering for KaTeX.
#### Defined in
[packages/mermaid/src/config.type.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L156)
[packages/mermaid/src/config.type.ts:167](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L167)
---
@@ -298,7 +298,7 @@ This option decides the amount of logging to be used by mermaid.
#### Defined in
[packages/mermaid/src/config.type.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L127)
[packages/mermaid/src/config.type.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L138)
---
@@ -310,7 +310,7 @@ Defines which main look to use for the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:71](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L71)
[packages/mermaid/src/config.type.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L82)
---
@@ -320,7 +320,7 @@ Defines which main look to use for the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
[packages/mermaid/src/config.type.ts:216](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L216)
---
@@ -332,7 +332,7 @@ Defines the maximum number of edges that can be drawn in a graph.
#### Defined in
[packages/mermaid/src/config.type.ts:90](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L90)
[packages/mermaid/src/config.type.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L101)
---
@@ -344,7 +344,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:85](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L85)
[packages/mermaid/src/config.type.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L96)
---
@@ -354,7 +354,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:195](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L195)
[packages/mermaid/src/config.type.ts:206](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L206)
---
@@ -364,7 +364,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
[packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
---
@@ -374,7 +374,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:190](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L190)
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
---
@@ -384,7 +384,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L191)
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
---
@@ -394,7 +394,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L193)
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
---
@@ -404,7 +404,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
[packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
---
@@ -418,7 +418,7 @@ This prevents malicious graph directives from overriding a site's default securi
#### Defined in
[packages/mermaid/src/config.type.ts:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L148)
[packages/mermaid/src/config.type.ts:159](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L159)
---
@@ -430,7 +430,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L131)
[packages/mermaid/src/config.type.ts:142](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L142)
---
@@ -440,7 +440,7 @@ Level of trust for parsed diagram
#### Defined in
[packages/mermaid/src/config.type.ts:183](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L183)
[packages/mermaid/src/config.type.ts:194](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L194)
---
@@ -452,7 +452,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:135](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L135)
[packages/mermaid/src/config.type.ts:146](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L146)
---
@@ -462,7 +462,7 @@ Dictates whether mermaid starts on Page load
#### Defined in
[packages/mermaid/src/config.type.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L188)
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
---
@@ -475,13 +475,13 @@ This is useful when you want to control how to handle syntax errors in your appl
#### Defined in
[packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
[packages/mermaid/src/config.type.ts:222](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L222)
---
### theme
`Optional` **theme**: `"default"` | `"base"` | `"dark"` | `"forest"` | `"neutral"` | `"neo"` | `"neo-dark"` | `"mc"` | `"null"`
`Optional` **theme**: `"default"` | `"base"` | `"dark"` | `"forest"` | `"neutral"` | `"neo"` | `"neo-dark"` | `"redux"` | `"redux-dark"` | `"mc"` | `"null"`
Theme, the CSS style sheet.
You may also use `themeCSS` to override this value.
@@ -498,7 +498,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L66)
[packages/mermaid/src/config.type.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L77)
---
@@ -508,7 +508,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:65](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L65)
[packages/mermaid/src/config.type.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L76)
---
@@ -518,7 +518,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:186](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L186)
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
---
@@ -528,7 +528,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
[packages/mermaid/src/config.type.ts:214](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L214)
---
@@ -538,4 +538,4 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L192)
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)

View File

@@ -19,4 +19,4 @@ The `parseError` function will not be called.
#### Defined in
[packages/mermaid/src/types.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L61)
[packages/mermaid/src/types.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L66)

View File

@@ -18,7 +18,7 @@ The mermaid code after extracting the config.
#### Defined in
[packages/mermaid/src/types.ts:69](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L69)
[packages/mermaid/src/types.ts:74](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L74)
---
@@ -30,7 +30,7 @@ The config passed as YAML frontmatter or directives
#### Defined in
[packages/mermaid/src/types.ts:73](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L73)
[packages/mermaid/src/types.ts:78](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L78)
---
@@ -40,7 +40,7 @@ The config passed as YAML frontmatter or directives
#### Defined in
[packages/mermaid/src/types.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L75)
[packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
---
@@ -52,7 +52,7 @@ The error that occurred during parsing, if any.
#### Defined in
[packages/mermaid/src/types.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L79)
[packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)
---
@@ -62,7 +62,7 @@ The error that occurred during parsing, if any.
#### Defined in
[packages/mermaid/src/types.ts:65](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L65)
[packages/mermaid/src/types.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L70)
---
@@ -72,4 +72,4 @@ The error that occurred during parsing, if any.
#### Defined in
[packages/mermaid/src/types.ts:74](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L74)
[packages/mermaid/src/types.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L79)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in
[packages/mermaid/src/types.ts:108](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L108)
[packages/mermaid/src/types.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L113)
---
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
[packages/mermaid/src/types.ts:103](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L103)
---
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
#### Defined in
[packages/mermaid/src/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L94)
[packages/mermaid/src/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L99)

View File

@@ -1183,6 +1183,91 @@ flowchart TB
B --> D
```
### Attaching an ID to Edges
Mermaid now supports assigning IDs to edges, similar to how IDs and metadata can be attached to nodes. This feature lays the groundwork for more advanced styling, classes, and animation capabilities on edges.
**Syntax:**
To give an edge an ID, prepend the edge syntax with the ID followed by an `@` character. For example:
```mermaid-example
flowchart LR
A e1@> B
```
```mermaid
flowchart LR
A e1@> B
```
In this example, `e1` is the ID of the edge connecting `A` to `B`. You can then use this ID in later definitions or style statements, just like with nodes.
### Turning an Animation On
Once you have assigned an ID to an edge, you can turn on animations for that edge by defining the edges properties:
```mermaid-example
flowchart LR
A e1@==> B
e1@{ animate: true }
```
```mermaid
flowchart LR
A e1@==> B
e1@{ animate: true }
```
This tells Mermaid that the edge `e1` should be animated.
### Selecting Type of Animation
In the initial version, two animation speeds are supported: `fast` and `slow`. Selecting a specific animation type is a shorthand for enabling animation and setting the animation speed in one go.
**Examples:**
```mermaid-example
flowchart LR
A e1@> B
e1@{ animation: fast }
```
```mermaid
flowchart LR
A e1@> B
e1@{ animation: fast }
```
This is equivalent to `{ animate: true, animation: fast }`.
### Using classDef Statements for Animations
You can also animate edges by assigning a class to them and then defining animation properties in a `classDef` statement. For example:
```mermaid-example
flowchart LR
A e1@> B
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class e1 animate
```
```mermaid
flowchart LR
A e1@> B
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class e1 animate
```
In this snippet:
- `e1@-->` creates an edge with ID `e1`.
- `classDef animate` defines a class named `animate` with styling and animation properties.
- `class e1 animate` applies the `animate` class to the edge `e1`.
**Note on Escaping Commas:**
When setting the `stroke-dasharray` property, remember to escape commas as `\,` since commas are used as delimiters in Mermaids style definitions.
## New arrow types
There are new types of arrows supported:

View File

@@ -500,7 +500,7 @@ mermaid.ganttConfig = {
sectionFontSize: 24, // Font size for sections
numberSectionStyles: 1, // The number of alternating section styles
axisFormat: '%d/%m', // Date/time format of the axis
tickInterval: '1 week', // Axis ticks
tickInterval: '1week', // Axis ticks
topAxis: true, // When this flag is set, date labels will be added to the top of the chart
displayMode: 'compact', // Turns compact mode on
weekday: 'sunday', // On which day a week-based interval should start

View File

@@ -1,6 +1,6 @@
{
"name": "@mermaid-chart/mermaid",
"version": "11.4.1-b.1",
"version": "11.4.1-b.7",
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
"type": "module",
"module": "./dist/mermaid.core.mjs",

View File

@@ -13,6 +13,7 @@ import {
setDiagramTitle,
} from '../common/commonDb.js';
import type {
ArchitectureAlignment,
ArchitectureDB,
ArchitectureDirectionPair,
ArchitectureDirectionPairMap,
@@ -25,6 +26,7 @@ import type {
ArchitectureState,
} from './architectureTypes.js';
import {
getArchitectureDirectionAlignment,
getArchitectureDirectionPair,
isArchitectureDirection,
isArchitectureJunction,
@@ -211,12 +213,18 @@ const addEdge = function ({
const getEdges = (): ArchitectureEdge[] => state.records.edges;
/**
* Returns the current diagram's adjacency list & spatial map.
* Returns the current diagram's adjacency list, spatial map, & group alignments.
* If they have not been created, run the algorithms to generate them.
* @returns
*/
const getDataStructures = () => {
if (state.records.dataStructures === undefined) {
// Tracks how groups are aligned with one another. Generated while creating the adj list
const groupAlignments: Record<
string,
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
> = {};
// Create an adjacency list of the diagram to perform BFS on
// Outer reduce applied on all services
// Inner reduce applied on the edges for a service
@@ -224,6 +232,19 @@ const getDataStructures = () => {
Record<string, ArchitectureDirectionPairMap>
>((prevOuter, [id, service]) => {
prevOuter[id] = service.edges.reduce<ArchitectureDirectionPairMap>((prevInner, edge) => {
// track the direction groups connect to one another
const lhsGroupId = getNode(edge.lhsId)?.in;
const rhsGroupId = getNode(edge.rhsId)?.in;
if (lhsGroupId && rhsGroupId && lhsGroupId !== rhsGroupId) {
const alignment = getArchitectureDirectionAlignment(edge.lhsDir, edge.rhsDir);
if (alignment !== 'bend') {
groupAlignments[lhsGroupId] ??= {};
groupAlignments[lhsGroupId][rhsGroupId] = alignment;
groupAlignments[rhsGroupId] ??= {};
groupAlignments[rhsGroupId][lhsGroupId] = alignment;
}
}
if (edge.lhsId === id) {
// source is LHS
const pair = getArchitectureDirectionPair(edge.lhsDir, edge.rhsDir);
@@ -245,6 +266,7 @@ const getDataStructures = () => {
// Configuration for the initial pass of BFS
const firstId = Object.keys(adjList)[0];
const visited = { [firstId]: 1 };
// If a key is present in this object, it has not been visited
const notVisited = Object.keys(adjList).reduce(
(prev, id) => (id === firstId ? prev : { ...prev, [id]: 1 }),
{} as Record<string, number>
@@ -283,6 +305,7 @@ const getDataStructures = () => {
state.records.dataStructures = {
adjList,
spatialMaps,
groupAlignments,
};
}
return state.records.dataStructures;

View File

@@ -12,7 +12,9 @@ import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import { getConfigField } from './architectureDb.js';
import { architectureIcons } from './architectureIcons.js';
import type {
ArchitectureAlignment,
ArchitectureDataStructures,
ArchitectureGroupAlignments,
ArchitectureJunction,
ArchitectureSpatialMap,
EdgeSingular,
@@ -149,25 +151,91 @@ function addEdges(edges: ArchitectureEdge[], cy: cytoscape.Core) {
});
}
function getAlignments(spatialMaps: ArchitectureSpatialMap[]): fcose.FcoseAlignmentConstraint {
function getAlignments(
db: ArchitectureDB,
spatialMaps: ArchitectureSpatialMap[],
groupAlignments: ArchitectureGroupAlignments
): fcose.FcoseAlignmentConstraint {
/**
* Flattens the alignment object so nodes in different groups will be in the same alignment array IFF their groups don't connect in a conflicting alignment
*
* i.e., two groups which connect horizontally should not have nodes with vertical alignments to one another
*
* See: #5952
*
* @param alignmentObj - alignment object with the outer key being the row/col # and the inner key being the group name mapped to the nodes on that axis in the group
* @param alignmentDir - alignment direction
* @returns flattened alignment object with an arbitrary key mapping to nodes in the same row/col
*/
const flattenAlignments = (
alignmentObj: Record<number, Record<string, string[]>>,
alignmentDir: ArchitectureAlignment
): Record<string, string[]> => {
return Object.entries(alignmentObj).reduce(
(prev, [dir, alignments]) => {
// prev is the mapping of x/y coordinate to an array of the nodes in that row/column
let cnt = 0;
const arr = Object.entries(alignments); // [group name, array of nodes within the group on axis dir]
if (arr.length === 1) {
// If only one group exists in the row/column, we don't need to do anything else
prev[dir] = arr[0][1];
return prev;
}
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
const [aGroupId, aNodeIds] = arr[i];
const [bGroupId, bNodeIds] = arr[j];
const alignment = groupAlignments[aGroupId]?.[bGroupId]; // Get how the two groups are intended to align (undefined if they aren't)
if (alignment === alignmentDir) {
// If the intended alignment between the two groups is the same as the alignment we are parsing
prev[dir] ??= [];
prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds]; // add the node ids of both groups to the axis array in prev
} else if (aGroupId === 'default' || bGroupId === 'default') {
// If either of the groups are in the default space (not in a group), use the same behavior as above
prev[dir] ??= [];
prev[dir] = [...prev[dir], ...aNodeIds, ...bNodeIds];
} else {
// Otherwise, the nodes in the two groups are not intended to align
const keyA = `${dir}-${cnt++}`;
prev[keyA] = aNodeIds;
const keyB = `${dir}-${cnt++}`;
prev[keyB] = bNodeIds;
}
}
}
return prev;
},
{} as Record<string, string[]>
);
};
const alignments = spatialMaps.map((spatialMap) => {
const horizontalAlignments: Record<number, string[]> = {};
const verticalAlignments: Record<number, string[]> = {};
const horizontalAlignments: Record<number, Record<string, string[]>> = {};
const verticalAlignments: Record<number, Record<string, string[]>> = {};
// Group service ids in an object with their x and y coordinate as the key
Object.entries(spatialMap).forEach(([id, [x, y]]) => {
if (!horizontalAlignments[y]) {
horizontalAlignments[y] = [];
}
if (!verticalAlignments[x]) {
verticalAlignments[x] = [];
}
horizontalAlignments[y].push(id);
verticalAlignments[x].push(id);
const nodeGroup = db.getNode(id)?.in ?? 'default';
horizontalAlignments[y] ??= {};
horizontalAlignments[y][nodeGroup] ??= [];
horizontalAlignments[y][nodeGroup].push(id);
verticalAlignments[x] ??= {};
verticalAlignments[x][nodeGroup] ??= [];
verticalAlignments[x][nodeGroup].push(id);
});
// Merge the values of each object into a list if the inner list has at least 2 elements
return {
horiz: Object.values(horizontalAlignments).filter((arr) => arr.length > 1),
vert: Object.values(verticalAlignments).filter((arr) => arr.length > 1),
horiz: Object.values(flattenAlignments(horizontalAlignments, 'horizontal')).filter(
(arr) => arr.length > 1
),
vert: Object.values(flattenAlignments(verticalAlignments, 'vertical')).filter(
(arr) => arr.length > 1
),
};
});
@@ -244,7 +312,8 @@ function layoutArchitecture(
junctions: ArchitectureJunction[],
groups: ArchitectureGroup[],
edges: ArchitectureEdge[],
{ spatialMaps }: ArchitectureDataStructures
db: ArchitectureDB,
{ spatialMaps, groupAlignments }: ArchitectureDataStructures
): Promise<cytoscape.Core> {
return new Promise((resolve) => {
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
@@ -318,9 +387,8 @@ function layoutArchitecture(
addServices(services, cy);
addJunctions(junctions, cy);
addEdges(edges, cy);
// Use the spatial map to create alignment arrays for fcose
const alignmentConstraint = getAlignments(spatialMaps);
const alignmentConstraint = getAlignments(db, spatialMaps, groupAlignments);
// Create the relative constraints for fcose by using an inverse of the spatial map and performing BFS on it
const relativePlacementConstraint = getRelativeConstraints(spatialMaps);
@@ -454,7 +522,7 @@ export const draw: DrawDefinition = async (text, id, _version, diagObj: Diagram)
await drawServices(db, servicesElem, services);
drawJunctions(db, servicesElem, junctions);
const cy = await layoutArchitecture(services, junctions, groups, edges, ds);
const cy = await layoutArchitecture(services, junctions, groups, edges, db, ds);
await drawEdges(edgesElem, cy);
await drawGroups(groupElem, cy);

View File

@@ -7,6 +7,8 @@ import type cytoscape from 'cytoscape';
| Architecture Diagram Types |
\*=======================================*/
export type ArchitectureAlignment = 'vertical' | 'horizontal' | 'bend';
export type ArchitectureDirection = 'L' | 'R' | 'T' | 'B';
export type ArchitectureDirectionX = Extract<ArchitectureDirection, 'L' | 'R'>;
export type ArchitectureDirectionY = Extract<ArchitectureDirection, 'T' | 'B'>;
@@ -170,6 +172,18 @@ export const getArchitectureDirectionXYFactors = function (
}
};
export const getArchitectureDirectionAlignment = function (
a: ArchitectureDirection,
b: ArchitectureDirection
): ArchitectureAlignment {
if (isArchitectureDirectionXY(a, b)) {
return 'bend';
} else if (isArchitectureDirectionX(a)) {
return 'horizontal';
}
return 'vertical';
};
export interface ArchitectureStyleOptions {
archEdgeColor: string;
archEdgeArrowColor: string;
@@ -249,9 +263,27 @@ export interface ArchitectureDB extends DiagramDB {
export type ArchitectureAdjacencyList = Record<string, ArchitectureDirectionPairMap>;
export type ArchitectureSpatialMap = Record<string, number[]>;
/**
* Maps the direction that groups connect from.
*
* **Outer key**: ID of group A
*
* **Inner key**: ID of group B
*
* **Value**: 'vertical' or 'horizontal'
*
* Note: tmp[groupA][groupB] == tmp[groupB][groupA]
*/
export type ArchitectureGroupAlignments = Record<
string,
Record<string, Exclude<ArchitectureAlignment, 'bend'>>
>;
export interface ArchitectureDataStructures {
adjList: ArchitectureAdjacencyList;
spatialMaps: ArchitectureSpatialMap[];
groupAlignments: ArchitectureGroupAlignments;
}
export interface ArchitectureState extends Record<string, unknown> {

View File

@@ -1,9 +1,11 @@
import flowDb from './flowDb.js';
import { FlowDB } from './flowDb.js';
import type { FlowSubGraph } from './types.js';
describe('flow db subgraphs', () => {
let flowDb: FlowDB;
let subgraphs: FlowSubGraph[];
beforeEach(() => {
flowDb = new FlowDB();
subgraphs = [
{ nodes: ['a', 'b', 'c', 'e'] },
{ nodes: ['f', 'g', 'h'] },
@@ -44,8 +46,9 @@ describe('flow db subgraphs', () => {
});
describe('flow db addClass', () => {
let flowDb: FlowDB;
beforeEach(() => {
flowDb.clear();
flowDb = new FlowDB();
});
it('should detect many classes', () => {
flowDb.addClass('a,b', ['stroke-width: 8px']);
@@ -65,3 +68,33 @@ describe('flow db addClass', () => {
expect(classes.get('a')?.styles).toEqual(['stroke-width: 8px']);
});
});
describe('flow db class', () => {
let flowDb: FlowDB;
beforeEach(() => {
flowDb = new FlowDB();
});
// This is to ensure that functions used in flow JISON are exposed as function from FlowDB
it('should have functions used in flow JISON as own property', () => {
const functionsUsedInParser = [
'setDirection',
'addSubGraph',
'setAccTitle',
'setAccDescription',
'addVertex',
'addLink',
'setClass',
'destructLink',
'addClass',
'setClickEvent',
'setTooltip',
'setLink',
'updateLink',
'updateLinkInterpolate',
] as const satisfies (keyof FlowDB)[];
for (const fun of functionsUsedInParser) {
expect(Object.hasOwn(flowDb, fun)).toBe(true);
}
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,17 @@
import type { MermaidConfig } from '../../config.type.js';
import { setConfig } from '../../diagram-api/diagramAPI.js';
import flowDb from './flowDb.js';
import { FlowDB } from './flowDb.js';
import renderer from './flowRenderer-v3-unified.js';
// @ts-ignore: JISON doesn't support types
import flowParser from './parser/flow.jison';
//import flowParser from './parser/flow.jison';
import flowParser from './parser/flowParser.ts';
import flowStyles from './styles.js';
export const diagram = {
parser: flowParser,
db: flowDb,
get db() {
return new FlowDB();
},
renderer,
styles: flowStyles,
init: (cnf: MermaidConfig) => {
@@ -20,7 +23,5 @@ export const diagram = {
}
cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
flowDb.clear();
flowDb.setGen('gen-2');
},
};

View File

@@ -7,7 +7,6 @@ import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/rende
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
import type { LayoutData } from '../../rendering-util/types.js';
import utils from '../../utils.js';
import { getDirection } from './flowDb.js';
export const getClasses = function (
text: string,
@@ -43,7 +42,7 @@ export const draw = async function (
log.debug('Data: ', data4Layout);
// Create the root SVG
const svg = getDiagramElement(id, securityLevel);
const direction = getDirection();
const direction = diag.db.getDirection();
data4Layout.type = diag.type;
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('[Arrows] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
@@ -9,7 +9,7 @@ setConfig({
describe('[Comments] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -39,10 +39,31 @@ const doubleEndedEdges = [
{ edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' },
{ edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' },
];
const regularEdges = [
{ edgeStart: '--', edgeEnd: '--x', stroke: 'normal', type: 'arrow_cross' },
{ edgeStart: '==', edgeEnd: '==x', stroke: 'thick', type: 'arrow_cross' },
{ edgeStart: '-.', edgeEnd: '.-x', stroke: 'dotted', type: 'arrow_cross' },
{ edgeStart: '--', edgeEnd: '--o', stroke: 'normal', type: 'arrow_circle' },
{ edgeStart: '==', edgeEnd: '==o', stroke: 'thick', type: 'arrow_circle' },
{ edgeStart: '-.', edgeEnd: '.-o', stroke: 'dotted', type: 'arrow_circle' },
{ edgeStart: '--', edgeEnd: '-->', stroke: 'normal', type: 'arrow_point' },
{ edgeStart: '==', edgeEnd: '==>', stroke: 'thick', type: 'arrow_point' },
{ edgeStart: '-.', edgeEnd: '.->', stroke: 'dotted', type: 'arrow_point' },
{ edgeStart: '--', edgeEnd: '----x', stroke: 'normal', type: 'arrow_cross' },
{ edgeStart: '==', edgeEnd: '====x', stroke: 'thick', type: 'arrow_cross' },
{ edgeStart: '-.', edgeEnd: '...-x', stroke: 'dotted', type: 'arrow_cross' },
{ edgeStart: '--', edgeEnd: '----o', stroke: 'normal', type: 'arrow_circle' },
{ edgeStart: '==', edgeEnd: '====o', stroke: 'thick', type: 'arrow_circle' },
{ edgeStart: '-.', edgeEnd: '...-o', stroke: 'dotted', type: 'arrow_circle' },
{ edgeStart: '--', edgeEnd: '---->', stroke: 'normal', type: 'arrow_point' },
{ edgeStart: '==', edgeEnd: '====>', stroke: 'thick', type: 'arrow_point' },
{ edgeStart: '-.', edgeEnd: '...->', stroke: 'dotted', type: 'arrow_point' },
];
describe('[Edges] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});
@@ -67,6 +88,74 @@ describe('[Edges] when parsing', () => {
expect(edges[0].type).toBe('arrow_circle');
});
describe('edges with ids', function () {
describe('open ended edges with ids and labels', function () {
regularEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].id).toBe('e1');
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
});
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart}${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].id).toBe('e1');
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
});
});
it('should handle normal edges where you also have a node with metadata', function () {
const res = flow.parser.parse(`flowchart LR
A id1@-->B
A@{ shape: 'rect' }
`);
const edges = flow.parser.yy.getEdges();
expect(edges[0].id).toBe('id1');
});
});
describe('double ended edges with ids and labels', function () {
doubleEndedEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parser.parse(
`flowchart TD;\nA e1@${edgeType.edgeStart} label ${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert.get('A').id).toBe('A');
expect(vert.get('B').id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].id).toBe('e1');
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe('label');
expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
});
});
});
});
describe('edges', function () {
doubleEndedEdges.forEach((edgeType) => {
it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('[Text] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
import { vi } from 'vitest';
const spyOn = vi.spyOn;
@@ -9,7 +9,9 @@ setConfig({
});
describe('[Interactions] when parsing', () => {
let flowDb;
beforeEach(function () {
flowDb = new FlowDB();
flow.parser.yy = flowDb;
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('[Lines] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('parsing a flow chart with markdown strings', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('when parsing directions', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});
@@ -251,7 +251,7 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a<br/>multiline string');
});
it(' should be possible to use } in strings', function () {
it('should be possible to use } in strings', function () {
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with }"
@@ -264,7 +264,7 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with }');
});
it(' should be possible to use @ in strings', function () {
it('should be possible to use @ in strings', function () {
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with @"
@@ -277,7 +277,7 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with @');
});
it(' should be possible to use @ in strings', function () {
it('should be possible to use @ in strings', function () {
const res = flow.parser.parse(`flowchart TB
A@{
label: "This is a string with}"
@@ -290,4 +290,126 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is a string with}');
});
it('should be possible to use @ syntax to add labels on multi nodes', function () {
const res = flow.parser.parse(`flowchart TB
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"}
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('labe for n4');
expect(data4Layout.nodes[2].label).toEqual('labe for n5');
});
it('should be possible to use @ syntax to add labels on multi nodes with edge/link', function () {
const res = flow.parser.parse(`flowchart TD
A["A"] --> B["for B"] & C@{ label: "for c"} & E@{label : "for E"}
D@{label: "for D"}
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(5);
expect(data4Layout.nodes[0].label).toEqual('A');
expect(data4Layout.nodes[1].label).toEqual('for B');
expect(data4Layout.nodes[2].label).toEqual('for c');
expect(data4Layout.nodes[3].label).toEqual('for E');
expect(data4Layout.nodes[4].label).toEqual('for D');
});
it('should be possible to use @ syntax in labels', function () {
const res = flow.parser.parse(`flowchart TD
A["@A@"] --> B["@for@ B@"] & C@{ label: "@for@ c@"} & E{"\`@for@ E@\`"} & D(("@for@ D@"))
H1{{"@for@ H@"}}
H2{{"\`@for@ H@\`"}}
Q1{"@for@ Q@"}
Q2{"\`@for@ Q@\`"}
AS1>"@for@ AS@"]
AS2>"\`@for@ AS@\`"]
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(11);
expect(data4Layout.nodes[0].label).toEqual('@A@');
expect(data4Layout.nodes[1].label).toEqual('@for@ B@');
expect(data4Layout.nodes[2].label).toEqual('@for@ c@');
expect(data4Layout.nodes[3].label).toEqual('@for@ E@');
expect(data4Layout.nodes[4].label).toEqual('@for@ D@');
expect(data4Layout.nodes[5].label).toEqual('@for@ H@');
expect(data4Layout.nodes[6].label).toEqual('@for@ H@');
expect(data4Layout.nodes[7].label).toEqual('@for@ Q@');
expect(data4Layout.nodes[8].label).toEqual('@for@ Q@');
expect(data4Layout.nodes[9].label).toEqual('@for@ AS@');
expect(data4Layout.nodes[10].label).toEqual('@for@ AS@');
});
it('should handle unique edge creation with using @ and &', function () {
const res = flow.parser.parse(`flowchart TD
A & B e1@--> C & D
A1 e2@--> C1 & D1
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
expect(data4Layout.edges[2].id).toEqual('e1');
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
expect(data4Layout.edges[4].id).toEqual('e2');
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
});
it('should handle redefine same edge ids again', function () {
const res = flow.parser.parse(`flowchart TD
A & B e1@--> C & D
A1 e1@--> C1 & D1
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(7);
expect(data4Layout.edges.length).toBe(6);
expect(data4Layout.edges[0].id).toEqual('L_A_C_0');
expect(data4Layout.edges[1].id).toEqual('L_A_D_0');
expect(data4Layout.edges[2].id).toEqual('e1');
expect(data4Layout.edges[3].id).toEqual('L_B_D_0');
expect(data4Layout.edges[4].id).toEqual('L_A1_C1_0');
expect(data4Layout.edges[5].id).toEqual('L_A1_D1_0');
});
it('should handle overriding edge animate again', function () {
const res = flow.parser.parse(`flowchart TD
A e1@--> B
C e2@--> D
E e3@--> F
e1@{ animate: true }
e2@{ animate: false }
e3@{ animate: true }
e3@{ animate: false }
`);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(6);
expect(data4Layout.edges.length).toBe(3);
expect(data4Layout.edges[0].id).toEqual('e1');
expect(data4Layout.edges[0].animate).toEqual(true);
expect(data4Layout.edges[1].id).toEqual('e2');
expect(data4Layout.edges[1].animate).toEqual(false);
expect(data4Layout.edges[2].id).toEqual('e3');
expect(data4Layout.edges[2].animate).toEqual(false);
});
it.skip('should be possible to use @ syntax to add labels with trail spaces', function () {
const res = flow.parser.parse(
`flowchart TB
n2["label for n2"] & n4@{ label: "labe for n4"} & n5@{ label: "labe for n5"} `
);
const data4Layout = flow.parser.yy.getData();
expect(data4Layout.nodes.length).toBe(3);
expect(data4Layout.nodes[0].label).toEqual('label for n2');
expect(data4Layout.nodes[1].label).toEqual('labe for n4');
expect(data4Layout.nodes[2].label).toEqual('labe for n5');
});
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -31,7 +31,7 @@ const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_
describe('[Singlenodes] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('[Style] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('[Text] when parsing', () => {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('when parsing flowcharts', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@@ -141,6 +141,7 @@ that id.
.*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr';
[^\s\"]+\@(?=[^\{\"]) { return 'LINK_ID'; }
[0-9]+ return 'NUM';
\# return 'BRKT';
":::" return 'STYLE_SEPARATOR';
@@ -201,7 +202,9 @@ that id.
"*" return 'MULT';
"#" return 'BRKT';
"&" return 'AMP';
([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING';
([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ {
return 'NODE_STRING';
}
"-" return 'MINUS'
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
@@ -361,7 +364,7 @@ spaceList
statement
: vertexStatement separator
{ /* console.warn('finat vs', $vertexStatement.nodes); */ $$=$vertexStatement.nodes}
{ $$=$vertexStatement.nodes}
| styleStatement separator
{$$=[];}
| linkStyleStatement separator
@@ -396,7 +399,7 @@ shapeData:
;
vertexStatement: vertexStatement link node shapeData
{ /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
{ /* console.warn('vs shapeData',$vertexStatement.stmt,$node, $shapeData);*/ yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
| vertexStatement link node
{ /*console.warn('vs',$vertexStatement.stmt,$node);*/ yy.addLink($vertexStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($vertexStatement.nodes) } }
| vertexStatement link node spaceList
@@ -404,7 +407,7 @@ vertexStatement: vertexStatement link node shapeData
|node spaceList { /*console.warn('vertexStatement: node spaceList', $node);*/ $$ = {stmt: $node, nodes:$node }}
|node shapeData {
/*console.warn('vertexStatement: node shapeData', $node[0], $shapeData);*/
yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData);
yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData);
$$ = {stmt: $node, nodes:$node, shapeData: $shapeData}
}
|node { /* console.warn('vertexStatement: single node', $node); */ $$ = {stmt: $node, nodes:$node }}
@@ -413,7 +416,7 @@ vertexStatement: vertexStatement link node shapeData
node: styledVertex
{ /*console.warn('nod', $styledVertex);*/ $$ = [$styledVertex];}
| node shapeData spaceList AMP spaceList styledVertex
{ yy.addVertex($node[0],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); $$ = $node.concat($styledVertex); /*console.warn('pip2', $node[0], $styledVertex, $$);*/ }
{ yy.addVertex($node[$node.length-1],undefined,undefined,undefined, undefined,undefined, undefined,$shapeData); $$ = $node.concat($styledVertex); /*console.warn('pip2', $node[0], $styledVertex, $$);*/ }
| node spaceList AMP spaceList styledVertex
{ $$ = $node.concat($styledVertex); /*console.warn('pip', $node[0], $styledVertex, $$);*/ }
;
@@ -472,6 +475,8 @@ link: linkStatement arrowText
{$$ = $linkStatement;}
| START_LINK edgeText LINK
{var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};}
| LINK_ID START_LINK edgeText LINK
{var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText, "id": $LINK_ID};}
;
edgeText: edgeTextToken
@@ -487,6 +492,8 @@ edgeText: edgeTextToken
linkStatement: LINK
{var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
| LINK_ID LINK
{var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length, "id": $LINK_ID};}
;
arrowText:

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { cleanupComments } from '../../../diagram-api/comments.js';
import { setConfig } from '../../../config.js';
@@ -9,7 +9,7 @@ setConfig({
describe('parsing a flow chart', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
});

View File

@@ -0,0 +1,12 @@
// @ts-ignore: JISON doesn't support types
import flowJisonParser from './flow.jison';
const newParser = Object.assign({}, flowJisonParser);
newParser.parse = (src: string): unknown => {
// remove the trailing whitespace after closing curly braces when ending a line break
const newSrc = src.replace(/}\s*\n/g, '}\n');
return flowJisonParser.parse(newSrc);
};
export default newParser;

View File

@@ -1,5 +1,5 @@
import flowDb from '../flowDb.js';
import flow from './flow.jison';
import { FlowDB } from '../flowDb.js';
import flow from './flowParser.ts';
import { setConfig } from '../../../config.js';
setConfig({
@@ -8,7 +8,7 @@ setConfig({
describe('when parsing subgraphs', function () {
beforeEach(function () {
flow.parser.yy = flowDb;
flow.parser.yy = new FlowDB();
flow.parser.yy.clear();
flow.parser.yy.setGen('gen-2');
});

View File

@@ -53,6 +53,7 @@ export interface FlowText {
}
export interface FlowEdge {
isUserDefinedId: boolean;
start: string;
end: string;
interpolate?: string;
@@ -62,6 +63,10 @@ export interface FlowEdge {
length?: number;
text: string;
labelType: 'text';
classes: string[];
id?: string;
animation?: 'fast' | 'slow';
animate?: boolean;
}
export interface FlowClass {

View File

@@ -1,7 +1,7 @@
const getStyles = (options) =>
`
.mermaid-main-font {
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
font-family: ${options.fontFamily};
}
.exclude-range {
@@ -45,7 +45,7 @@ const getStyles = (options) =>
.sectionTitle {
text-anchor: start;
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
font-family: ${options.fontFamily};
}
@@ -86,13 +86,13 @@ const getStyles = (options) =>
.taskText {
text-anchor: middle;
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
font-family: ${options.fontFamily};
}
.taskTextOutsideRight {
fill: ${options.taskTextDarkColor};
text-anchor: start;
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
font-family: ${options.fontFamily};
}
.taskTextOutsideLeft {
@@ -248,7 +248,7 @@ const getStyles = (options) =>
text-anchor: middle;
font-size: 18px;
fill: ${options.titleColor || options.textColor};
font-family: var(--mermaid-font-family, "trebuchet ms", verdana, arial, sans-serif);
font-family: ${options.fontFamily};
}
`;

View File

@@ -4,11 +4,13 @@ import parser from './parser/sankey.jison';
import db from './sankeyDB.js';
import renderer from './sankeyRenderer.js';
import { prepareTextForParsing } from './sankeyUtils.js';
import sankeyStyles from './styles.js';
const originalParse = parser.parse.bind(parser);
parser.parse = (text: string) => originalParse(prepareTextForParsing(text));
export const diagram: DiagramDefinition = {
styles: sankeyStyles,
parser,
db,
renderer,

View File

@@ -136,7 +136,6 @@ export const draw = function (text: string, id: string, _version: string, diagOb
svg
.append('g')
.attr('class', 'node-labels')
.attr('font-family', 'sans-serif')
.attr('font-size', 14)
.selectAll('text')
.data(graph.nodes)

View File

@@ -0,0 +1,6 @@
const getStyles = (options) =>
`.label {
font-family: ${options.fontFamily};
}`;
export default getStyles;

View File

@@ -1,7 +1,6 @@
const getStyles = (options) =>
`.label {
font-family: 'trebuchet ms', verdana, arial, sans-serif;
font-family: var(--mermaid-font-family);
font-family: ${options.fontFamily};
color: ${options.textColor};
}
.mouth {
@@ -79,8 +78,7 @@ const getStyles = (options) =>
text-align: center;
max-width: 200px;
padding: 2px;
font-family: 'trebuchet ms', verdana, arial, sans-serif;
font-family: var(--mermaid-font-family);
font-family: ${options.fontFamily};
font-size: 12px;
background: ${options.tertiaryColor};
border: 1px solid ${options.border2};

View File

@@ -711,6 +711,67 @@ flowchart TB
B --> D
```
### Attaching an ID to Edges
Mermaid now supports assigning IDs to edges, similar to how IDs and metadata can be attached to nodes. This feature lays the groundwork for more advanced styling, classes, and animation capabilities on edges.
**Syntax:**
To give an edge an ID, prepend the edge syntax with the ID followed by an `@` character. For example:
```mermaid
flowchart LR
A e1@> B
```
In this example, `e1` is the ID of the edge connecting `A` to `B`. You can then use this ID in later definitions or style statements, just like with nodes.
### Turning an Animation On
Once you have assigned an ID to an edge, you can turn on animations for that edge by defining the edges properties:
```mermaid
flowchart LR
A e1@==> B
e1@{ animate: true }
```
This tells Mermaid that the edge `e1` should be animated.
### Selecting Type of Animation
In the initial version, two animation speeds are supported: `fast` and `slow`. Selecting a specific animation type is a shorthand for enabling animation and setting the animation speed in one go.
**Examples:**
```mermaid
flowchart LR
A e1@> B
e1@{ animation: fast }
```
This is equivalent to `{ animate: true, animation: fast }`.
### Using classDef Statements for Animations
You can also animate edges by assigning a class to them and then defining animation properties in a `classDef` statement. For example:
```mermaid
flowchart LR
A e1@> B
classDef animate stroke-dasharray: 9,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
class e1 animate
```
In this snippet:
- `e1@-->` creates an edge with ID `e1`.
- `classDef animate` defines a class named `animate` with styling and animation properties.
- `class e1 animate` applies the `animate` class to the edge `e1`.
**Note on Escaping Commas:**
When setting the `stroke-dasharray` property, remember to escape commas as `\,` since commas are used as delimiters in Mermaids style definitions.
## New arrow types
There are new types of arrows supported:

View File

@@ -390,7 +390,7 @@ mermaid.ganttConfig = {
sectionFontSize: 24, // Font size for sections
numberSectionStyles: 1, // The number of alternating section styles
axisFormat: '%d/%m', // Date/time format of the axis
tickInterval: '1 week', // Axis ticks
tickInterval: '1week', // Axis ticks
topAxis: true, // When this flag is set, date labels will be added to the top of the chart
displayMode: 'compact', // Turns compact mode on
weekday: 'sunday', // On which day a week-based interval should start

View File

@@ -69,6 +69,7 @@ import { compile, serialize } from 'stylis';
import { Diagram } from './Diagram.js';
import { decodeEntities, encodeEntities } from './utils.js';
import { toBase64 } from './utils/base64.js';
import { FlowDB } from './diagrams/flowchart/flowDb.js';
/**
* @see https://vitest.dev/guide/mocking.html Mock part of a module
@@ -850,5 +851,57 @@ graph TD;A--x|text including URL space|B;`
expect(diagram).toBeInstanceOf(Diagram);
expect(diagram.type).toBe('flowchart-v2');
});
it('should not modify db when rendering different diagrams', async () => {
const flowDiagram1 = await mermaidAPI.getDiagramFromText(
`flowchart LR
A -- text --> B -- text2 --> C`
);
const flwoDiagram2 = await mermaidAPI.getDiagramFromText(
`flowchart TD
A -- text --> B -- text2 --> C`
);
// Since flowDiagram will return new Db object each time, we can compare the db to be different.
expect(flowDiagram1.db).not.toBe(flwoDiagram2.db);
assert(flowDiagram1.db instanceof FlowDB);
assert(flwoDiagram2.db instanceof FlowDB);
expect(flowDiagram1.db.getDirection()).not.toEqual(flwoDiagram2.db.getDirection());
const classDiagram1 = await mermaidAPI.getDiagramFromText(
`stateDiagram
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`
);
const classDiagram2 = await mermaidAPI.getDiagramFromText(
`stateDiagram
[*] --> Still
Still --> [*]
Still --> Moving
Moving --> Still
Moving --> Crash
Crash --> [*]`
);
// Since sequenceDiagram will return same Db object each time, we can compare the db to be same.
expect(classDiagram1.db).toBe(classDiagram2.db);
});
});
// Sequence Diagram currently uses a singleton DB, so this test will fail
it.fails('should not modify db when rendering different sequence diagrams', async () => {
const sequenceDiagram1 = await mermaidAPI.getDiagramFromText(
`sequenceDiagram
Alice->>Bob: Hello Bob, how are you?
Bob-->>John: How about you John?`
);
const sequenceDiagram2 = await mermaidAPI.getDiagramFromText(
`sequenceDiagram
Alice->>Bob: Hello Bob, how are you?
Bob-->>John: How about you John?`
);
expect(sequenceDiagram1.db).not.toBe(sequenceDiagram2.db);
});
});

View File

@@ -13,6 +13,7 @@ import { curveBasis, curveLinear, curveCardinal, line, select } from 'd3';
import rough from 'roughjs';
import createLabel from './createLabel.js';
import { addEdgeMarkers } from './edgeMarker.ts';
import { isLabelStyle } from './shapes/handDrawnShapeStyles.js';
const edgeLabels = new Map();
const terminalLabels = new Map();
@@ -434,6 +435,13 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
let pointsHasChanged = false;
const tail = startNode;
var head = endNode;
const edgeClassStyles = [];
for (const key in edge.cssCompiledStyles) {
if (isLabelStyle(key)) {
continue;
}
edgeClassStyles.push(edge.cssCompiledStyles[key]);
}
if (head.intersect && tail.intersect) {
points = points.slice(1, edge.points.length - 1);
@@ -536,6 +544,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5)
: lineFunction(lineData);
const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style];
if (edge.look === 'handDrawn') {
const rc = rough.svg(elem);
Object.assign([], lineData);
@@ -556,12 +565,33 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
svgPath.attr('d', d);
elem.node().appendChild(svgPath.node());
} else {
const stylesFromClasses = edgeClassStyles.join(';');
const styles = edgeStyles ? edgeStyles.reduce((acc, style) => acc + style + ';', '') : '';
let animationClass = '';
if (edge.animate) {
animationClass = ' edge-animation-fast';
}
if (edge.animation) {
animationClass = ' edge-animation-' + edge.animation;
}
svgPath = elem
.append('path')
.attr('d', linePath)
.attr('id', edge.id)
.attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : ''))
.attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '');
.attr(
'class',
' ' +
strokeClasses +
(edge.classes ? ' ' + edge.classes : '') +
(animationClass ? animationClass : '')
)
.attr(
'style',
(stylesFromClasses ? stylesFromClasses + ';' + styles + ';' : styles) +
';' +
(edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '')
);
const len = svgPath.node().getTotalLength();
const oValueS = markerOffsets2[edge.arrowTypeStart] || 0;
const oValueE = markerOffsets2[edge.arrowTypeEnd] || 0;

View File

@@ -32,7 +32,28 @@ export const styles2Map = (styles: string[]) => {
});
return styleMap;
};
export const isLabelStyle = (key: string) => {
return (
key === 'color' ||
key === 'font-size' ||
key === 'font-family' ||
key === 'font-weight' ||
key === 'font-style' ||
key === 'text-decoration' ||
key === 'text-align' ||
key === 'text-transform' ||
key === 'line-height' ||
key === 'letter-spacing' ||
key === 'word-spacing' ||
key === 'text-shadow' ||
key === 'text-overflow' ||
key === 'white-space' ||
key === 'word-wrap' ||
key === 'word-break' ||
key === 'overflow-wrap' ||
key === 'hyphens'
);
};
export const styles2String = (node: Node) => {
const { stylesArray } = compileStyles(node);
const labelStyles: string[] = [];
@@ -42,26 +63,7 @@ export const styles2String = (node: Node) => {
stylesArray.forEach((style) => {
const key = style[0];
if (
key === 'color' ||
key === 'font-size' ||
key === 'font-family' ||
key === 'font-weight' ||
key === 'font-style' ||
key === 'text-decoration' ||
key === 'text-align' ||
key === 'text-transform' ||
key === 'line-height' ||
key === 'letter-spacing' ||
key === 'word-spacing' ||
key === 'text-shadow' ||
key === 'text-overflow' ||
key === 'white-space' ||
key === 'word-wrap' ||
key === 'word-break' ||
key === 'overflow-wrap' ||
key === 'hyphens'
) {
if (isLabelStyle(key)) {
labelStyles.push(style.join(':') + ' !important');
} else {
nodeStyles.push(style.join(':') + ' !important');

View File

@@ -96,11 +96,14 @@ export interface Edge {
label?: string;
classes?: string;
style?: string[];
animate?: boolean;
animation?: 'fast' | 'slow';
// Properties common to both Flowchart and State Diagram edges
arrowhead?: string;
arrowheadStyle?: string;
arrowTypeEnd?: string;
arrowTypeStart?: string;
cssCompiledStyles?: string[];
// Flowchart specific properties
defaultInterpolate?: string;
end?: string;
@@ -122,6 +125,7 @@ export interface Edge {
pattern?: string;
thickness?: 'normal' | 'thick' | 'invisible' | 'dotted';
look?: string;
isUserDefinedId?: boolean;
showPoints?: boolean;
}

View File

@@ -32,7 +32,28 @@ const getStyles = (
font-size: ${options.fontSize};
fill: ${options.textColor}
}
@keyframes edge-animation-frame {
from {
stroke-dashoffset: 0;
}
}
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
& .edge-animation-slow {
stroke-dasharray: 9,5 !important;
stroke-dashoffset: 900;
animation: dash 50s linear infinite;
stroke-linecap: round;
}
& .edge-animation-fast {
stroke-dasharray: 9,5 !important;
stroke-dashoffset: 900;
animation: dash 20s linear infinite;
stroke-linecap: round;
}
/* Classes common for multiple diagrams */
& .error-icon {

View File

@@ -16,6 +16,11 @@ export interface NodeMetaData {
ticket?: string;
}
export interface EdgeMetaData {
animation?: 'fast' | 'slow';
animate?: boolean;
}
export interface Point {
x: number;
y: number;

View File

@@ -937,8 +937,13 @@ export const getEdgeId = (
counter?: number;
prefix?: string;
suffix?: string;
}
},
id?: string
) => {
if (id) {
return id;
}
log.debug('Disabled addition of id:', id);
return `${prefix ? `${prefix}_` : ''}${from}_${to}_${counter}${suffix ? `_${suffix}` : ''}`;
};