Merge branch 'mermaid-js:develop' into develop

This commit is contained in:
Zihua Li
2023-04-13 16:00:01 +08:00
committed by GitHub
42 changed files with 909 additions and 388 deletions

View File

@@ -226,6 +226,44 @@ pie
### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>] ### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>]
### Bar chart (using gantt chart) [<a href="https://mermaid-js.github.io/mermaid/#/gantt">docs</a> - <a href="https://mermaid.live/edit#pako:eNptkU1vhCAQhv8KIenNugiI4rkf6bmXpvEyFVxJFDYyNt1u9r8X63Z7WQ9m5pknLzieaBeMpQ3dg0dsPUkPOhwteXZIXmJcbCT3xMAxkuh8Z8kIEclyMIB209fqKcwTICFvG4IvFy_oLrZ-g9F26ILfQgvNFN94VaRXQ1iWqpumZBcu1J8p1E1TXDx59eQNr5LyEqjJn6hv5QnGNlxevZJmdLLpy5xJSzut45biYCfb0iaVxvawjNjS1p-TCguG16PvaIPzYjO67e3BwX6GiTY9jPFKH43DMF_hGMDY1J4oHg-_f8hFTJFd8L3br3yZx4QHxENsdrt1nO8dDstH3oVpF50ZYMbhU6ud4qoGLqyqBJRCmO6j0HXPZdGbihUc6Pmc0QP49xD-b5X69ZQv2gjO81IwzWqhC1lKrjJ6pA3nVS7SMiVjrKirWlYp5fs3osgrWeo00lorLWvOzz8JVbXm">live editor</a>]
```
gantt
title Git Issues - days since last update
dateFormat X
axisFormat %s
section Issue19062
71 : 0, 71
section Issue19401
36 : 0, 36
section Issue193
34 : 0, 34
section Issue7441
9 : 0, 9
section Issue1300
5 : 0, 5
```
```mermaid
gantt
title Git Issues - days since last update
dateFormat X
axisFormat %s
section Issue19062
71 : 0, 71
section Issue19401
36 : 0, 36
section Issue193
34 : 0, 34
section Issue7441
9 : 0, 9
section Issue1300
5 : 0, 5
```
### User Journey diagram [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>] ### User Journey diagram [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>]
``` ```

View File

@@ -47,6 +47,7 @@
"graphviz", "graphviz",
"grav", "grav",
"greywolf", "greywolf",
"huynh",
"inkdrop", "inkdrop",
"jaoude", "jaoude",
"jison", "jison",
@@ -90,6 +91,7 @@
"sidharthv", "sidharthv",
"sphinxcontrib", "sphinxcontrib",
"statediagram", "statediagram",
"steph",
"stylis", "stylis",
"substate", "substate",
"sveidqvist", "sveidqvist",

View File

@@ -685,6 +685,16 @@ A ~~~ B
{ titleTopMargin: 0 } { titleTopMargin: 0 }
); );
}); });
it('4023: Should render html labels with images and-or text correctly', () => {
imgSnapshotTest(
`flowchart TD
B[<img src='https://mermaid.js.org/mermaid-logo.svg'>]
B-->C[<img src="https://mermaid.js.org/mermaid-logo.svg"> more text <img src='https://mermaid.js.org/mermaid-logo.svg'>]
B-->D(<img src='https://mermaid.js.org/mermaid-logo.svg'> some text)
B-->E(plain)`,
{}
);
});
describe('Markdown strings flowchart (#4220)', () => { describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => { describe('html labels', () => {
it('With styling and classes', () => { it('With styling and classes', () => {

View File

@@ -29,9 +29,9 @@
} }
.mermaid svg { .mermaid svg {
/* font-size: 18px !important; */ /* font-size: 18px !important; */
background-color: #eee; background-color: #efefef;
background-image: radial-gradient(#fff 1%, transparent 11%), background-image: radial-gradient(#fff 51%, transparent 91%),
radial-gradient(#fff 1%, transparent 11%); radial-gradient(#fff 51%, transparent 91%);
background-size: 20px 20px; background-size: 20px 20px;
background-position: 0 0, 10px 10px; background-position: 0 0, 10px 10px;
background-repeat: repeat; background-repeat: repeat;
@@ -58,43 +58,96 @@
</head> </head>
<body> <body>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"htmlLabels": false}} }%% stateDiagram-v2
flowchart-elk LR [*] --> Still
b(`The dog in **the** hog(2)... a a a a *very long text* about it Still --> [*]
Word! Still --> Moving
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `) Moving --> Still
Moving --> Crash
Crash --> [*] </pre
>
<pre id="diagram" class="mermaid2">
flowchart RL
subgraph "`one`"
a1 -- l1 --> a2
a1 -- l2 --> a2
end
</pre>
<pre id="diagram" class="mermaid">
flowchart RL
subgraph "`one`"
a1 -- l1 --> a2
a1 -- l2 --> a2
end
</pre>
<pre id="diagram" class="mermaid2">
flowchart
id["`A root with a long text that wraps to keep the node size in check. A root with a long text that wraps to keep the node size in check`"]</pre
>
<pre id="diagram" class="mermaid2">
flowchart LR
A[A text that needs to be wrapped wraps to another line]
B[A text that needs to be<br/>wrapped wraps to another line]
C["`A text that needs to be wrapped to another line`"]</pre>
<pre id="diagram" class="mermaid2">
flowchart LR
C["`A text
that needs
to be wrapped
in another
way`"]
</pre
>
<pre id="diagram" class="mermaid">
classDiagram-v2
note "I love this diagram!\nDo you love it?"
</pre>
<pre id="diagram" class="mermaid">
stateDiagram-v2
State1: The state with a note with minus - and plus + in it
note left of State1
Important information! You can write
notes with . and in them.
end note </pre
>
<pre id="diagram" class="mermaid2">
mindmap
root
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
</pre </pre
> >
<pre id="diagram" class="mermaid2"> <pre id="diagram" class="mermaid2">
flowchart-elk LR %%{init: {"theme": "forest"} }%%
b("The dog in the hog... a very<br/>long text about it<br/>Word!") mindmap
</pre> id1[**Start2**<br/>end]
<pre id="diagram" class="mermaid2"> id2[**Start2**<br />end]
flowchart-elk LR %% Another comment
b("The dog in the hog... a very<br/>long text about it<br/>Word!") id3[**Start2**<br>end] %% Comment
</pre> id4[**Start2**<br >end<br >the very end]
> </pre>
<pre id="diagram" class="mermaid2"> <pre id="diagram" class="mermaid2">
mindmap mindmap
id1[`**Start2** id1["`**Start2**
second line 😎 with long text that is wrapping to the next line`] second line 😎 with long text that is wrapping to the next line`"]
id2[`Child **with bold** text`] id2["`Child **with bold** text`"]
id3[`Children of which some id3["`Children of which some
is using *italic type of* text`] is using *italic type of* text`"]
id4[Child] id4[Child]
id5[`Child id5["`Child
Row Row
and another and another
`] `"]
</pre>
<pre id="diagram" class="mermaid">
mindmap
id1["`**Start** with
a second line 😎`"]
id2["`The dog in **the** hog... a *very long text* about it
Word!`"]
</pre> </pre>
<pre id="diagram" class="mermaid2"> <pre id="diagram" class="mermaid2">
mindmap
id1("`**Root**`"]
id2["`A formatted text... with **bold** and *italics*`"]
id3[Regular labels works as usual]
id4["`Emojis and unicode works too: 🤓
शान्तिः سلام 和平 `"]
</pre>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%% %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid %% I could not figure out how to use double quotes in labels in Mermaid
@@ -110,7 +163,7 @@ flowchart TB
rom --> core2 rom --> core2
end end
subgraph amd[AMD Latte GPU] subgraph amd["`**AMD** Latte GPU`"]
mem[Memory & I/O Bridge] mem[Memory & I/O Bridge]
dram[DRAM Controller] dram[DRAM Controller]
edram[32 MB EDRAM MEM1] edram[32 MB EDRAM MEM1]
@@ -149,6 +202,62 @@ flowchart TB
rtc{{rtc}} rtc{{rtc}}
</pre </pre
> >
<pre id="diagram" class="mermaid2">
%%{init: {"flowchart": {"defaultRenderer": "elk", "htmlLabels": false}} }%%
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd["`**AMD** Latte GPU`"]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br /> <br />
<pre id="diagram" class="mermaid2"> <pre id="diagram" class="mermaid2">
flowchart TB flowchart TB
@@ -291,16 +400,16 @@ mindmap
// console.error('Mermaid error: ', err); // console.error('Mermaid error: ', err);
}; };
mermaid.initialize({ mermaid.initialize({
theme: 'forest', // theme: 'forest',
startOnLoad: true, startOnLoad: true,
logLevel: 0, logLevel: 0,
flowchart: { flowchart: {
// defaultRenderer: 'elk', // defaultRenderer: 'elk',
useMaxWidth: false, useMaxWidth: false,
htmlLabels: false, // htmlLabels: false,
// htmlLabels: true, htmlLabels: true,
}, },
// htmlLabels: true, // htmlLabels: false,
gantt: { gantt: {
useMaxWidth: false, useMaxWidth: false,
}, },

View File

@@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667) [mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
## Functions ## Functions

View File

@@ -0,0 +1,13 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/news/announcements.md](../../packages/mermaid/src/docs/news/announcements.md).
# Announcements
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
3 April 2023 · 3 mins
Markdown Strings reduce the hassle # Starting from v10.

31
docs/news/blog.md Normal file
View File

@@ -0,0 +1,31 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/news/blog.md](../../packages/mermaid/src/docs/news/blog.md).
# Blog
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
27 March 2023 · 2 mins
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
8 March 2023 · 9 mins
The hype around AI in general and ChatGPT, in particular, is so intense that its very understandable to assume the hype train is driving straight toward the trough of disillusionment.
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
1 March 2023 · 12 mins
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
10 February 2023 · 10 mins
Busting the myth that developers cant write # Its an annoying stereotype that developers dont know how to write, speak, and otherwise communicate.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -710,6 +710,44 @@ flowchart LR
B1 --> B2 B1 --> B2
``` ```
## Markdown Strings
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
```mermaid-example
%%{init: {"flowchart": {"htmlLabels": false}} }%%
flowchart LR
subgraph "One"
a("`The **cat**
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
end
subgraph "`**Two**`"
c("`The **cat**
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
end
```
```mermaid
%%{init: {"flowchart": {"htmlLabels": false}} }%%
flowchart LR
subgraph "One"
a("`The **cat**
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
end
subgraph "`**Two**`"
c("`The **cat**
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
end
```
Formatting:
- For bold text, use double asterisks \*\* before and after the text.
- For italics, use single asterisks \* before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
This feature is applicable to node labels, edge labels, and subgraph labels.
## Interaction ## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`. It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.

View File

@@ -464,3 +464,41 @@ Beginner's tip—a full example using interactive links in an html context:
</script> </script>
</body> </body>
``` ```
## Examples
### Bar chart (using gantt chart)
```mermaid-example
gantt
title Git Issues - days since last update
dateFormat X
axisFormat %s
section Issue19062
71 : 0, 71
section Issue19401
36 : 0, 36
section Issue193
34 : 0, 34
section Issue7441
9 : 0, 9
section Issue1300
5 : 0, 5
```
```mermaid
gantt
title Git Issues - days since last update
dateFormat X
axisFormat %s
section Issue19062
71 : 0, 71
section Issue19401
36 : 0, 36
section Issue193
34 : 0, 34
section Issue7441
9 : 0, 9
section Issue1300
5 : 0, 5
```

View File

@@ -254,6 +254,34 @@ Root
C C
``` ```
## Markdown Strings
The "Markdown Strings" feature enhances mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
```mermaid-example
mindmap
id1["`**Root** with
a second line
Unicode works too: 🤓`"]
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
id3[Regular labels still works]
```
```mermaid
mindmap
id1["`**Root** with
a second line
Unicode works too: 🤓`"]
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
id3[Regular labels still works]
```
Formatting:
- For bold text, use double asterisks \*\* before and after the text.
- For italics, use single asterisks \* before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
## Integrating with your library/website. ## Integrating with your library/website.
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward. Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.

View File

@@ -1,7 +1,7 @@
{ {
"name": "mermaid-monorepo", "name": "mermaid-monorepo",
"private": true, "private": true,
"version": "10.0.2", "version": "10.1.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"packageManager": "pnpm@7.30.1", "packageManager": "pnpm@7.30.1",

View File

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

View File

@@ -63,13 +63,20 @@ const rect = (parent, node) => {
.attr('width', width) .attr('width', width)
.attr('height', node.height + padding); .attr('height', node.height + padding);
if (useHtmlLabels) {
label.attr(
'transform',
// This puts the labal on top of the box instead of inside it
'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2) + ')'
);
} else {
label.attr(
'transform',
// This puts the labal on top of the box instead of inside it
'translate(' + node.x + ', ' + (node.y - node.height / 2) + ')'
);
}
// Center the label // Center the label
label.attr(
'transform',
// This puts the labal on top of the box instead of inside it
// 'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2 - bbox.height) + ')'
'translate(' + node.x + ', ' + (node.y - node.height / 2) + ')'
);
const rectBox = rect.node().getBBox(); const rectBox = rect.node().getBBox();
node.width = rectBox.width; node.width = rectBox.width;

View File

@@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges'; import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges';
import { log } from '../logger'; import { log } from '../logger';
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => { const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster); log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir; const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir); log.trace('Dir in recursive render - dir:', dir);
@@ -35,44 +35,46 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
// Insert nodes, this will insert them into the dom and each node will get a size. The size is updated // Insert nodes, this will insert them into the dom and each node will get a size. The size is updated
// to the abstract node and is later used by dagre for the layout // to the abstract node and is later used by dagre for the layout
graph.nodes().forEach(function (v) { await Promise.all(
const node = graph.node(v); graph.nodes().map(async function (v) {
if (parentCluster !== undefined) { const node = graph.node(v);
const data = JSON.parse(JSON.stringify(parentCluster.clusterData)); if (parentCluster !== undefined) {
// data.clusterPositioning = true; const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster); // data.clusterPositioning = true;
graph.setNode(parentCluster.id, data); log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
if (!graph.parent(v)) { graph.setNode(parentCluster.id, data);
log.trace('Setting parent', v, parentCluster.id); if (!graph.parent(v)) {
graph.setParent(v, parentCluster.id, data); log.trace('Setting parent', v, parentCluster.id);
graph.setParent(v, parentCluster.id, data);
}
} }
} log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v))); if (node && node.clusterNode) {
if (node && node.clusterNode) { // const children = graph.children(v);
// const children = graph.children(v); log.info('Cluster identified', v, node.width, graph.node(v));
log.info('Cluster identified', v, node.width, graph.node(v)); const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
const o = recursiveRender(nodes, node.graph, diagramtype, graph.node(v)); const newEl = o.elem;
const newEl = o.elem; updateNodeBounds(node, newEl);
updateNodeBounds(node, newEl); node.diff = o.diff || 0;
node.diff = o.diff || 0; log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y);
log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y); setNodeElem(newEl, node);
setNodeElem(newEl, node);
log.warn('Recursive render complete ', newEl, node); log.warn('Recursive render complete ', newEl, node);
} else {
if (graph.children(v).length > 0) {
// This is a cluster but not to be rendered recursively
// Render as before
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
// insertCluster(clusters, graph.node(v));
} else { } else {
log.info('Node - the non recursive path', v, node.id, node); if (graph.children(v).length > 0) {
insertNode(nodes, graph.node(v), dir); // This is a cluster but not to be rendered recursively
// Render as before
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
// insertCluster(clusters, graph.node(v));
} else {
log.info('Node - the non recursive path', v, node.id, node);
await insertNode(nodes, graph.node(v), dir);
}
} }
} })
}); );
// Insert labels, this will insert them into the dom so that the width can be calculated // Insert labels, this will insert them into the dom so that the width can be calculated
// Also figure out which edges point to/from clusters and adjust them accordingly // Also figure out which edges point to/from clusters and adjust them accordingly
@@ -146,7 +148,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
return { elem, diff }; return { elem, diff };
}; };
export const render = (elem, graph, markers, diagramtype, id) => { export const render = async (elem, graph, markers, diagramtype, id) => {
insertMarkers(elem, markers, diagramtype, id); insertMarkers(elem, markers, diagramtype, id);
clearNodes(); clearNodes();
clearEdges(); clearEdges();
@@ -157,7 +159,7 @@ export const render = (elem, graph, markers, diagramtype, id) => {
adjustClustersAndEdges(graph); adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlibJson.write(graph)); log.warn('Graph after:', graphlibJson.write(graph));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph)); // log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
recursiveRender(elem, graph, diagramtype); await recursiveRender(elem, graph, diagramtype);
}; };
// const shapeDefinitions = {}; // const shapeDefinitions = {};

View File

@@ -8,8 +8,8 @@ import note from './shapes/note';
import { parseMember } from '../diagrams/class/svgDraw'; import { parseMember } from '../diagrams/class/svgDraw';
import { evaluate } from '../diagrams/common/common'; import { evaluate } from '../diagrams/common/common';
const question = (parent, node) => { const question = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -69,8 +69,8 @@ const choice = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const hexagon = (parent, node) => { const hexagon = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 4; const f = 4;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -96,8 +96,8 @@ const hexagon = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_left_inv_arrow = (parent, node) => { const rect_left_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -122,8 +122,8 @@ const rect_left_inv_arrow = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_right = (parent, node) => { const lean_right = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -145,8 +145,8 @@ const lean_right = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_left = (parent, node) => { const lean_left = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -168,8 +168,8 @@ const lean_left = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const trapezoid = (parent, node) => { const trapezoid = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -191,8 +191,8 @@ const trapezoid = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const inv_trapezoid = (parent, node) => { const inv_trapezoid = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -214,8 +214,8 @@ const inv_trapezoid = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_right_inv_arrow = (parent, node) => { const rect_right_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -238,8 +238,8 @@ const rect_right_inv_arrow = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const cylinder = (parent, node) => { const cylinder = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const rx = w / 2; const rx = w / 2;
@@ -310,21 +310,30 @@ const cylinder = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect = (parent, node) => { const rect = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(
parent,
node,
'node ' + node.classes,
true
);
// add the rect // add the rect
const rect = shapeSvg.insert('rect', ':first-child'); const rect = shapeSvg.insert('rect', ':first-child');
const totalWidth = bbox.width + node.padding * 2; // const totalWidth = bbox.width + node.padding * 2;
const totalHeight = bbox.height + node.padding * 2; // const totalHeight = bbox.height + node.padding * 2;
const totalWidth = bbox.width + node.padding;
const totalHeight = bbox.height + node.padding;
rect rect
.attr('class', 'basic label-container') .attr('class', 'basic label-container')
.attr('style', node.style) .attr('style', node.style)
.attr('rx', node.rx) .attr('rx', node.rx)
.attr('ry', node.ry) .attr('ry', node.ry)
.attr('x', -bbox.width / 2 - node.padding) // .attr('x', -bbox.width / 2 - node.padding)
.attr('y', -bbox.height / 2 - node.padding) // .attr('y', -bbox.height / 2 - node.padding)
.attr('x', -bbox.width / 2 - halfPadding)
.attr('y', -bbox.height / 2 - halfPadding)
.attr('width', totalWidth) .attr('width', totalWidth)
.attr('height', totalHeight); .attr('height', totalHeight);
@@ -348,10 +357,10 @@ const rect = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const labelRect = (parent, node) => { const labelRect = async (parent, node) => {
const { shapeSvg } = labelHelper(parent, node, 'label', true); const { shapeSvg } = await labelHelper(parent, node, 'label', true);
log.info('Classes = ', node.classes); log.trace('Classes = ', node.classes);
// add the rect // add the rect
const rect = shapeSvg.insert('rect', ':first-child'); const rect = shapeSvg.insert('rect', ':first-child');
@@ -535,8 +544,8 @@ const rectWithTitle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const stadium = (parent, node) => { const stadium = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
const w = bbox.width + h / 4 + node.padding; const w = bbox.width + h / 4 + node.padding;
@@ -561,8 +570,8 @@ const stadium = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const circle = (parent, node) => { const circle = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const circle = shapeSvg.insert('circle', ':first-child'); const circle = shapeSvg.insert('circle', ':first-child');
// center the circle around its coordinate // center the circle around its coordinate
@@ -586,8 +595,8 @@ const circle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const doublecircle = (parent, node) => { const doublecircle = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const gap = 5; const gap = 5;
const circleGroup = shapeSvg.insert('g', ':first-child'); const circleGroup = shapeSvg.insert('g', ':first-child');
const outerCircle = circleGroup.insert('circle'); const outerCircle = circleGroup.insert('circle');
@@ -622,8 +631,8 @@ const doublecircle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const subroutine = (parent, node) => { const subroutine = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@@ -972,7 +981,7 @@ const shapes = {
let nodeElems = {}; let nodeElems = {};
export const insertNode = (elem, node, dir) => { export const insertNode = async (elem, node, dir) => {
let newEl; let newEl;
let el; let el;
@@ -985,9 +994,9 @@ export const insertNode = (elem, node, dir) => {
target = node.linkTarget || '_blank'; target = node.linkTarget || '_blank';
} }
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target); newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
el = shapes[node.shape](newEl, node, dir); el = await shapes[node.shape](newEl, node, dir);
} else { } else {
el = shapes[node.shape](elem, node, dir); el = await shapes[node.shape](elem, node, dir);
newEl = el; newEl = el;
} }
if (node.tooltip) { if (node.tooltip) {
@@ -1013,6 +1022,7 @@ export const clear = () => {
export const positionNode = (node) => { export const positionNode = (node) => {
const el = nodeElems[node.id]; const el = nodeElems[node.id];
log.trace( log.trace(
'Transforming node', 'Transforming node',
node.diff, node.diff,

View File

@@ -1,9 +1,19 @@
import { updateNodeBounds, labelHelper } from './util'; import { updateNodeBounds, labelHelper } from './util';
import { log } from '../../logger'; import { log } from '../../logger';
import { getConfig } from '../../config';
import intersect from '../intersect/index.js'; import intersect from '../intersect/index.js';
const note = (parent, node) => { const note = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels;
if (!useHtmlLabels) {
node.centerLabel = true;
}
const { shapeSvg, bbox, halfPadding } = await labelHelper(
parent,
node,
'node ' + node.classes,
true
);
log.info('Classes = ', node.classes); log.info('Classes = ', node.classes);
// add the rect // add the rect

View File

@@ -4,8 +4,10 @@ import { getConfig } from '../../config';
import { decodeEntities } from '../../mermaidAPI'; import { decodeEntities } from '../../mermaidAPI';
import { select } from 'd3'; import { select } from 'd3';
import { evaluate, sanitizeText } from '../../diagrams/common/common'; import { evaluate, sanitizeText } from '../../diagrams/common/common';
export const labelHelper = (parent, node, _classes, isNode) => {
export const labelHelper = async (parent, node, _classes, isNode) => {
let classes; let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
if (!_classes) { if (!_classes) {
classes = 'node default'; classes = 'node default';
} else { } else {
@@ -33,7 +35,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
if (node.labelType === 'markdown') { if (node.labelType === 'markdown') {
// text = textNode; // text = textNode;
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), { text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
useHtmlLabels: getConfig().flowchart.htmlLabels, useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth, width: node.width || getConfig().flowchart.wrappingWidth,
classes: 'markdown-node-label', classes: 'markdown-node-label',
}); });
@@ -50,23 +52,56 @@ export const labelHelper = (parent, node, _classes, isNode) => {
// Get the size of the label // Get the size of the label
let bbox = text.getBBox(); let bbox = text.getBBox();
const halfPadding = node.padding / 2;
if (evaluate(getConfig().flowchart.htmlLabels)) { if (evaluate(getConfig().flowchart.htmlLabels)) {
const div = text.children[0]; const div = text.children[0];
const dv = select(text); const dv = select(text);
// if there are images, need to wait for them to load before getting the bounding box
const images = div.getElementsByTagName('img');
if (images) {
const noImgText = labelText.replace(/<img[^>]*>/g, '').trim() === '';
await Promise.all(
[...images].map(
(img) =>
new Promise((res) =>
img.addEventListener('load', function () {
img.style.display = 'flex';
img.style.flexDirection = 'column';
if (noImgText) {
// default size if no text
const bodyFontSize = getConfig().fontSize
? getConfig().fontSize
: window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
img.style.width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
} else {
img.style.width = '100%';
}
res(img);
})
)
)
);
}
bbox = div.getBoundingClientRect(); bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width); dv.attr('width', bbox.width);
dv.attr('height', bbox.height); dv.attr('height', bbox.height);
} }
const halfPadding = node.padding / 2;
// Center the label // Center the label
if (getConfig().flowchart.htmlLabels) { if (useHtmlLabels) {
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
} else { } else {
label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')'); label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
} }
if (node.centerLabel) {
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
}
label.insert('rect', ':first-child'); label.insert('rect', ':first-child');
return { shapeSvg, bbox, halfPadding, label }; return { shapeSvg, bbox, halfPadding, label };
}; };

View File

@@ -248,7 +248,7 @@ export const setConf = function (cnf: any) {
* @param _version - * @param _version -
* @param diagObj - * @param diagObj -
*/ */
export const draw = function (text: string, id: string, _version: string, diagObj: any) { export const draw = async function (text: string, id: string, _version: string, diagObj: any) {
log.info('Drawing class - ', id); log.info('Drawing class - ', id);
// TODO V10: Why flowchart? Might be a mistake when copying. // TODO V10: Why flowchart? Might be a mistake when copying.
@@ -300,7 +300,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
// @ts-ignore Ignore type error for now // @ts-ignore Ignore type error for now
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render( await render(
element, element,
g, g,
['aggregation', 'extension', 'composition', 'dependency', 'lollipop'], ['aggregation', 'extension', 'composition', 'dependency', 'lollipop'],

View File

@@ -35,229 +35,231 @@ let nodeDb = {};
// * @param doc // * @param doc
// * @param diagObj // * @param diagObj
// */ // */
export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) { export const addVertices = async function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
const svg = root.select(`[id="${svgId}"]`); const svg = root.select(`[id="${svgId}"]`);
const nodes = svg.insert('g').attr('class', 'nodes'); const nodes = svg.insert('g').attr('class', 'nodes');
const keys = Object.keys(vert); const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) { await Promise.all(
const vertex = vert[id]; keys.map(async function (id) {
const vertex = vert[id];
/** /**
* Variable for storing the classes for the vertex * Variable for storing the classes for the vertex
* *
* @type {string} * @type {string}
*/ */
let classStr = 'default'; let classStr = 'default';
if (vertex.classes.length > 0) { if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' '); classStr = vertex.classes.join(' ');
} }
classStr = classStr + ' flowchart-label'; classStr = classStr + ' flowchart-label';
const styles = getStylesFromArray(vertex.styles); const styles = getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition // Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id; let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
// We create a SVG label, either by delegating to addHtmlLabel or manually // We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode; let vertexNode;
const labelData = { width: 0, height: 0 }; const labelData = { width: 0, height: 0 };
const ports = [ const ports = [
{ {
id: vertex.id + '-west', id: vertex.id + '-west',
layoutOptions: { layoutOptions: {
'port.side': 'WEST', 'port.side': 'WEST',
},
}, },
}, {
{ id: vertex.id + '-east',
id: vertex.id + '-east', layoutOptions: {
layoutOptions: { 'port.side': 'EAST',
'port.side': 'EAST', },
}, },
}, {
{ id: vertex.id + '-south',
id: vertex.id + '-south', layoutOptions: {
layoutOptions: { 'port.side': 'SOUTH',
'port.side': 'SOUTH', },
}, },
}, {
{ id: vertex.id + '-north',
id: vertex.id + '-north', layoutOptions: {
layoutOptions: { 'port.side': 'NORTH',
'port.side': 'NORTH', },
}, },
}, ];
];
let radious = 0; let radious = 0;
let _shape = ''; let _shape = '';
let layoutOptions = {}; let layoutOptions = {};
// Set the shape based parameters // Set the shape based parameters
switch (vertex.type) { switch (vertex.type) {
case 'round': case 'round':
radious = 5; radious = 5;
_shape = 'rect'; _shape = 'rect';
break; break;
case 'square': case 'square':
_shape = 'rect'; _shape = 'rect';
break; break;
case 'diamond': case 'diamond':
_shape = 'question'; _shape = 'question';
layoutOptions = { layoutOptions = {
portConstraints: 'FIXED_SIDE', portConstraints: 'FIXED_SIDE',
}; };
break; break;
case 'hexagon': case 'hexagon':
_shape = 'hexagon'; _shape = 'hexagon';
break; break;
case 'odd': case 'odd':
_shape = 'rect_left_inv_arrow'; _shape = 'rect_left_inv_arrow';
break; break;
case 'lean_right': case 'lean_right':
_shape = 'lean_right'; _shape = 'lean_right';
break; break;
case 'lean_left': case 'lean_left':
_shape = 'lean_left'; _shape = 'lean_left';
break; break;
case 'trapezoid': case 'trapezoid':
_shape = 'trapezoid'; _shape = 'trapezoid';
break; break;
case 'inv_trapezoid': case 'inv_trapezoid':
_shape = 'inv_trapezoid'; _shape = 'inv_trapezoid';
break; break;
case 'odd_right': case 'odd_right':
_shape = 'rect_left_inv_arrow'; _shape = 'rect_left_inv_arrow';
break; break;
case 'circle': case 'circle':
_shape = 'circle'; _shape = 'circle';
break; break;
case 'ellipse': case 'ellipse':
_shape = 'ellipse'; _shape = 'ellipse';
break; break;
case 'stadium': case 'stadium':
_shape = 'stadium'; _shape = 'stadium';
break; break;
case 'subroutine': case 'subroutine':
_shape = 'subroutine'; _shape = 'subroutine';
break; break;
case 'cylinder': case 'cylinder':
_shape = 'cylinder'; _shape = 'cylinder';
break; break;
case 'group': case 'group':
_shape = 'rect'; _shape = 'rect';
break; break;
case 'doublecircle': case 'doublecircle':
_shape = 'doublecircle'; _shape = 'doublecircle';
break; break;
default: default:
_shape = 'rect'; _shape = 'rect';
} }
// Add the node // Add the node
const node = { const node = {
labelStyle: styles.labelStyle, labelStyle: styles.labelStyle,
shape: _shape, shape: _shape,
labelText: vertexText, labelText: vertexText,
labelType: vertex.labelType, labelType: vertex.labelType,
rx: radious, rx: radious,
ry: radious, ry: radious,
class: classStr, class: classStr,
style: styles.style, style: styles.style,
id: vertex.id, id: vertex.id,
link: vertex.link, link: vertex.link,
linkTarget: vertex.linkTarget, linkTarget: vertex.linkTarget,
tooltip: diagObj.db.getTooltip(vertex.id) || '', tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id), domId: diagObj.db.lookUpDomId(vertex.id),
haveCallback: vertex.haveCallback, haveCallback: vertex.haveCallback,
width: vertex.type === 'group' ? 500 : undefined, width: vertex.type === 'group' ? 500 : undefined,
dir: vertex.dir, dir: vertex.dir,
type: vertex.type, type: vertex.type,
props: vertex.props, props: vertex.props,
padding: getConfig().flowchart.padding, padding: getConfig().flowchart.padding,
}; };
let boundingBox; let boundingBox;
let nodeEl; let nodeEl;
// Add the element to the DOM // Add the element to the DOM
if (node.type !== 'group') { if (node.type !== 'group') {
nodeEl = insertNode(nodes, node, vertex.dir); nodeEl = insertNode(nodes, node, vertex.dir);
boundingBox = nodeEl.node().getBBox(); boundingBox = nodeEl.node().getBBox();
} else { } else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); // svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
// const rows = vertexText.split(common.lineBreakRegex); // const rows = vertexText.split(common.lineBreakRegex);
// for (const row of rows) { // for (const row of rows) {
// const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan'); // const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); // tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
// tspan.setAttribute('dy', '1em'); // tspan.setAttribute('dy', '1em');
// tspan.setAttribute('x', '1'); // tspan.setAttribute('x', '1');
// tspan.textContent = row; // tspan.textContent = row;
// svgLabel.appendChild(tspan); // svgLabel.appendChild(tspan);
// }
// vertexNode = svgLabel;
// const bbox = vertexNode.getBBox();
const { shapeSvg, bbox } = await labelHelper(nodes, node, undefined, true);
labelData.width = bbox.width;
labelData.wrappingWidth = getConfig().flowchart.wrappingWidth;
labelData.height = bbox.height;
labelData.labelNode = shapeSvg.node();
node.labelData = labelData;
}
// const { shapeSvg, bbox } = await labelHelper(svg, node, undefined, true);
const data = {
id: vertex.id,
ports: vertex.type === 'diamond' ? ports : [],
// labelStyle: styles.labelStyle,
// shape: _shape,
layoutOptions,
labelText: vertexText,
labelData,
// labels: [{ text: vertexText }],
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// link: vertex.link,
// linkTarget: vertex.linkTarget,
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
// haveCallback: vertex.haveCallback,
width: boundingBox?.width,
height: boundingBox?.height,
// dir: vertex.dir,
type: vertex.type,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// boundingBox,
el: nodeEl,
parent: parentLookupDb.parentById[vertex.id],
};
// if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
// graph.children.push({
// ...data,
// });
// } // }
// vertexNode = svgLabel; nodeDb[node.id] = data;
// const bbox = vertexNode.getBBox(); // log.trace('setNode', {
const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true); // labelStyle: styles.labelStyle,
labelData.width = bbox.width; // shape: _shape,
labelData.wrappingWidth = getConfig().flowchart.wrappingWidth; // labelText: vertexText,
labelData.height = bbox.height; // rx: radius,
labelData.labelNode = shapeSvg.node(); // ry: radius,
node.labelData = labelData; // class: classStr,
} // style: styles.style,
// const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true); // id: vertex.id,
// domId: diagObj.db.lookUpDomId(vertex.id),
const data = { // width: vertex.type === 'group' ? 500 : undefined,
id: vertex.id, // type: vertex.type,
ports: vertex.type === 'diamond' ? ports : [], // dir: vertex.dir,
// labelStyle: styles.labelStyle, // props: vertex.props,
// shape: _shape, // padding: getConfig().flowchart.padding,
layoutOptions, // parent: parentLookupDb.parentById[vertex.id],
labelText: vertexText, // });
labelData, })
// labels: [{ text: vertexText }], );
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// link: vertex.link,
// linkTarget: vertex.linkTarget,
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
// haveCallback: vertex.haveCallback,
width: boundingBox?.width,
height: boundingBox?.height,
// dir: vertex.dir,
type: vertex.type,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// boundingBox,
el: nodeEl,
parent: parentLookupDb.parentById[vertex.id],
};
// if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
// graph.children.push({
// ...data,
// });
// }
nodeDb[node.id] = data;
// log.trace('setNode', {
// labelStyle: styles.labelStyle,
// shape: _shape,
// labelText: vertexText,
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// id: vertex.id,
// domId: diagObj.db.lookUpDomId(vertex.id),
// width: vertex.type === 'group' ? 500 : undefined,
// type: vertex.type,
// dir: vertex.dir,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// parent: parentLookupDb.parentById[vertex.id],
// });
});
return graph; return graph;
}; };
@@ -861,7 +863,7 @@ export const draw = async function (text, id, _version, diagObj) {
// in order to get the size of the node. You can't get the size of a node // in order to get the size of the node. You can't get the size of a node
// that is not in the dom so we need to add it to the dom, get the size // that is not in the dom so we need to add it to the dom, get the size
// we will position the nodes when we get the layout from elkjs // we will position the nodes when we get the layout from elkjs
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg); graph = await addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
// Time for the edges, we start with adding an element in the node to hold the edges // Time for the edges, we start with adding an element in the node to hold the edges
const edgesEl = svg.insert('g').attr('class', 'edges edgePath'); const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
@@ -934,9 +936,12 @@ const drawNodes = (relX, relY, nodeArray, svg, subgraphsEl, diagObj, depth) => {
.attr('width', node.width) .attr('width', node.width)
.attr('height', node.height); .attr('height', node.height);
const label = subgraphEl.insert('g').attr('class', 'label'); const label = subgraphEl.insert('g').attr('class', 'label');
const labelCentering = getConfig().flowchart.htmlLabels ? node.labelData.width / 2 : 0;
label.attr( label.attr(
'transform', 'transform',
`translate(${node.labels[0].x + relX + node.x}, ${node.labels[0].y + relY + node.y})` `translate(${node.labels[0].x + relX + node.x + labelCentering}, ${
node.labels[0].y + relY + node.y + 3
})`
); );
label.node().appendChild(node.labelData.labelNode); label.node().appendChild(node.labelData.labelNode);

View File

@@ -362,7 +362,7 @@ export const getClasses = function (text, diagObj) {
* @param id * @param id
*/ */
export const draw = function (text, id, _version, diagObj) { export const draw = async function (text, id, _version, diagObj) {
log.info('Drawing flowchart'); log.info('Drawing flowchart');
diagObj.db.clear(); diagObj.db.clear();
flowDb.setGen('gen-2'); flowDb.setGen('gen-2');
@@ -451,7 +451,7 @@ export const draw = function (text, id, _version, diagObj) {
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id); await render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());

View File

@@ -23,11 +23,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
.cluster-label text { .cluster-label text {
fill: ${options.titleColor}; fill: ${options.titleColor};
} }
.cluster-label span { .cluster-label span,p {
color: ${options.titleColor}; color: ${options.titleColor};
} }
.label text,span { .label text,span,p {
fill: ${options.nodeTextColor || options.textColor}; fill: ${options.nodeTextColor || options.textColor};
color: ${options.nodeTextColor || options.textColor}; color: ${options.nodeTextColor || options.textColor};
} }
@@ -92,7 +92,7 @@ const getStyles = (options: FlowChartStyleOptions) =>
fill: ${options.titleColor}; fill: ${options.titleColor};
} }
.cluster span { .cluster span,p {
color: ${options.titleColor}; color: ${options.titleColor};
} }
/* .cluster div { /* .cluster div {

View File

@@ -1,8 +1,8 @@
import { sanitizeUrl } from '@braintree/sanitize-url'; import { sanitizeUrl } from '@braintree/sanitize-url';
import dayjs from 'dayjs'; import dayjs from 'dayjs/esm/index.js';
import dayjsIsoWeek from 'dayjs/plugin/isoWeek.js'; import dayjsIsoWeek from 'dayjs/esm/plugin/isoWeek/index.js';
import dayjsCustomParseFormat from 'dayjs/plugin/customParseFormat.js'; import dayjsCustomParseFormat from 'dayjs/esm/plugin/customParseFormat/index.js';
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat.js'; import dayjsAdvancedFormat from 'dayjs/esm/plugin/advancedFormat/index.js';
import { log } from '../../logger'; import { log } from '../../logger';
import * as configApi from '../../config'; import * as configApi from '../../config';
import utils from '../../utils'; import utils from '../../utils';

View File

@@ -1,5 +1,5 @@
// @ts-nocheck TODO: Fix TS // @ts-nocheck TODO: Fix TS
import dayjs from 'dayjs'; import dayjs from 'dayjs/esm/index.js';
import ganttDb from './ganttDb'; import ganttDb from './ganttDb';
import { convert } from '../../tests/util'; import { convert } from '../../tests/util';

View File

@@ -1,4 +1,4 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs/esm/index.js';
import { log } from '../../logger'; import { log } from '../../logger';
import { import {
select, select,

View File

@@ -175,7 +175,7 @@ export const draw = async (text, id, version, diagObj) => {
// Parse the graph definition // Parse the graph definition
diagObj.parser.parse(text); diagObj.parser.parse(text);
log.debug('Renering mindmap diagram\n' + text, diagObj); log.debug('Rendering mindmap diagram\n' + text, diagObj.parser);
const securityLevel = getConfig().securityLevel; const securityLevel = getConfig().securityLevel;
// Handle root and Document for when rendering in sandbox mode // Handle root and Document for when rendering in sandbox mode

View File

@@ -18,7 +18,7 @@
%% %%
\s*\%\%.* {yy.getLogger().trace('Found comment',yytext);} \s*\%\%.* {yy.getLogger().trace('Found comment',yytext); return 'SPACELINE';}
// \%\%[^\n]*\n /* skip comments */ // \%\%[^\n]*\n /* skip comments */
"mindmap" return 'MINDMAP'; "mindmap" return 'MINDMAP';
":::" { this.begin('CLASS'); } ":::" { this.begin('CLASS'); }

View File

@@ -217,7 +217,8 @@ export const drawNode = function (elem, node, fullSection, conf) {
// Create the wrapped text element // Create the wrapped text element
const textElem = nodeElem.append('g'); const textElem = nodeElem.append('g');
const newEl = createText(textElem, node.descr, { const description = node.descr.replace(/(<br\/*>)/g, '\n');
const newEl = createText(textElem, description, {
useHtmlLabels: htmlLabels, useHtmlLabels: htmlLabels,
width: node.width, width: node.width,
classes: 'mindmap-node-label', classes: 'mindmap-node-label',

View File

@@ -232,6 +232,9 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
type: newNode.type, type: newNode.type,
padding: 15, //getConfig().flowchart.padding padding: 15, //getConfig().flowchart.padding
}; };
// if (useHtmlLabels) {
nodeData.centerLabel = true;
// }
if (parsedItem.note) { if (parsedItem.note) {
// Todo: set random id // Todo: set random id
@@ -240,6 +243,7 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
shape: SHAPE_NOTE, shape: SHAPE_NOTE,
labelText: parsedItem.note.text, labelText: parsedItem.note.text,
classes: CSS_DIAGRAM_NOTE, classes: CSS_DIAGRAM_NOTE,
// useHtmlLabels: false,
style: '', // styles.style, style: '', // styles.style,
id: itemId + NOTE_ID + '-' + graphItemCount, id: itemId + NOTE_ID + '-' + graphItemCount,
domId: stateDomId(itemId, graphItemCount, NOTE), domId: stateDomId(itemId, graphItemCount, NOTE),
@@ -378,7 +382,7 @@ const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
* @param _version * @param _version
* @param diag * @param diag
*/ */
export const draw = function (text, id, _version, diag) { export const draw = async function (text, id, _version, diag) {
log.info('Drawing state diagram (v2)', id); log.info('Drawing state diagram (v2)', id);
// diag.sb.clear(); // diag.sb.clear();
nodeDb = {}; nodeDb = {};
@@ -432,7 +436,7 @@ export const draw = function (text, id, _version, diag) {
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render(element, g, ['barb'], CSS_DIAGRAM, id); await render(element, g, ['barb'], CSS_DIAGRAM, id);
const padding = 8; const padding = 8;

View File

@@ -28,7 +28,16 @@ export default defineConfig({
}, },
socialLinks: [ socialLinks: [
{ icon: 'github', link: 'https://github.com/mermaid-js/mermaid' }, { icon: 'github', link: 'https://github.com/mermaid-js/mermaid' },
{ icon: 'slack', link: 'https://mermaid-talk.slack.com' }, {
icon: 'slack',
link: 'https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE',
},
{
icon: {
svg: '<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490.16 490.16"><defs><mask id="Mask"><rect x="0" y="0" width="490.16" height="490.16" fill="white" /><path fill="black" d="M407.48,111.18A165.2,165.2,0,0,0,245.08,220,165.2,165.2,0,0,0,82.68,111.18a165.5,165.5,0,0,0,72.06,143.64,88.81,88.81,0,0,1,38.53,73.45v50.86H296.9V328.27a88.8,88.8,0,0,1,38.52-73.45,165.41,165.41,0,0,0,72.06-143.64Z"/><path fill="black" d="M160.63,328.27a56.09,56.09,0,0,0-24.27-46.49,198.74,198.74,0,0,1-28.54-23.66A196.87,196.87,0,0,1,82.53,227V379.13h78.1Z"/><path fill="black" d="M329.53,328.27a56.09,56.09,0,0,1,24.27-46.49,198.74,198.74,0,0,0,28.54-23.66A196.87,196.87,0,0,0,407.63,227V379.13h-78.1Z"/></mask><style>.cls-1{fill:#76767B;}.cls-1:hover{fill:#FF3570}</style></defs><rect class="cls-1" width="490.16" height="490.16" rx="84.61" mask="url(#Mask)" /></svg>',
},
link: 'https://www.mermaidchart.com/',
},
], ],
}, },
}); });
@@ -42,6 +51,11 @@ function nav() {
activeMatch: '/config/', activeMatch: '/config/',
}, },
{ text: 'Integrations', link: '/ecosystem/integrations', activeMatch: '/ecosystem/' }, { text: 'Integrations', link: '/ecosystem/integrations', activeMatch: '/ecosystem/' },
{
text: 'Latest News',
link: '/news/announcements',
activeMatch: '/announcements',
},
{ {
text: version, text: version,
items: [ items: [
@@ -80,6 +94,7 @@ function sidebarAll() {
...sidebarEcosystem(), ...sidebarEcosystem(),
...sidebarConfig(), ...sidebarConfig(),
...sidebarCommunity(), ...sidebarCommunity(),
...sidebarNews(),
]; ];
} }
@@ -162,3 +177,16 @@ function sidebarCommunity() {
}, },
]; ];
} }
function sidebarNews() {
return [
{
text: '📰 Latest News',
collapsible: true,
items: [
{ text: 'Announcements', link: '/news/announcements' },
{ text: 'Blog', link: '/news/blog' },
],
},
];
}

View File

@@ -23,15 +23,15 @@ features:
- title: Easy to use! - title: Easy to use!
details: Easily create and render detailed diagrams and charts with the Mermaid Live Editor. details: Easily create and render detailed diagrams and charts with the Mermaid Live Editor.
link: https://mermaid.live/ link: https://mermaid.live/
- title: 🎥 Video Tutorials!
details: Curated list of video tutorials and examples created by the community.
link: ../../config/Tutorials.html
- title: 🧩 Integrations available! - title: 🧩 Integrations available!
details: Use Mermaid with your favorite applications, check out the integrations list. details: Use Mermaid with your favorite applications, check out the integrations list.
link: ../../ecosystem/integrations.md link: ../../ecosystem/integrations.md
- title: 🏆 Award winning! - title: 🏆 Award winning!
details: 2019 JavaScript Open Source Award winner for "The Most Exciting Use of Technology". details: 2019 JavaScript Open Source Award winner for "The Most Exciting Use of Technology".
link: https://osawards.com/javascript/2019 link: https://osawards.com/javascript/2019
- title: 🥰 Mermaid + Mermaid Chart
details: Mermaid Chart is a major supporter of the Mermaid project.
link: https://www.mermaidchart.com/
--- ---
<script setup> <script setup>
@@ -149,6 +149,12 @@ const members = [
title: "Developer", title: "Developer",
links: [{ icon: "github", link: "https://github.com/spopida" }], links: [{ icon: "github", link: "https://github.com/spopida" }],
}, },
{
avatar: "https://avatars.githubusercontent.com/u/35910788?v=4",
name: "Steph Huynh",
title: "Developer",
links: [{ icon: "github", link: "https://github.com/huynhicode" }],
},
]; ];
</script> </script>

View File

@@ -0,0 +1,7 @@
# Announcements
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
3 April 2023 · 3 mins
Markdown Strings reduce the hassle # Starting from v10.

View File

@@ -0,0 +1,25 @@
# Blog
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
27 March 2023 · 2 mins
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
8 March 2023 · 9 mins
The hype around AI in general and ChatGPT, in particular, is so intense that its very understandable to assume the hype train is driving straight toward the trough of disillusionment.
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
1 March 2023 · 12 mins
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
10 February 2023 · 10 mins
Busting the myth that developers cant write # Its an annoying stereotype that developers dont know how to write, speak, and otherwise communicate.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -446,6 +446,31 @@ flowchart LR
B1 --> B2 B1 --> B2
``` ```
## Markdown Strings
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
```mermaid-example
%%{init: {"flowchart": {"htmlLabels": false}} }%%
flowchart LR
subgraph "One"
a("`The **cat**
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
end
subgraph "`**Two**`"
c("`The **cat**
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
end
```
Formatting:
- For bold text, use double asterisks \*\* before and after the text.
- For italics, use single asterisks \* before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
This feature is applicable to node labels, edge labels, and subgraph labels.
## Interaction ## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`. It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.

View File

@@ -374,3 +374,24 @@ Beginner's tip—a full example using interactive links in an html context:
</script> </script>
</body> </body>
``` ```
## Examples
### Bar chart (using gantt chart)
```mermaid-example
gantt
title Git Issues - days since last update
dateFormat X
axisFormat %s
section Issue19062
71 : 0, 71
section Issue19401
36 : 0, 36
section Issue193
34 : 0, 34
section Issue7441
9 : 0, 9
section Issue1300
5 : 0, 5
```

View File

@@ -162,6 +162,25 @@ Root
C C
``` ```
## Markdown Strings
The "Markdown Strings" feature enhances mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
```mermaid-example
mindmap
id1["`**Root** with
a second line
Unicode works too: 🤓`"]
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
id3[Regular labels still works]
```
Formatting:
- For bold text, use double asterisks \*\* before and after the text.
- For italics, use single asterisks \* before and after the text.
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
## Integrating with your library/website. ## Integrating with your library/website.
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward. Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.

View File

@@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-empty-function */ /* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable no-console */ /* eslint-disable no-console */
import dayjs from 'dayjs'; import dayjs from 'dayjs/esm/index.js';
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';

View File

@@ -406,6 +406,12 @@ const render = async function (
// clean up text CRLFs // clean up text CRLFs
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;; text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
// clean up html tags so that all attributes use single quotes, parser throws error on double quotes
text = text.replace(
/<(\w+)([^>]*)>/g,
(match, tag, attributes) => '<' + tag + attributes.replace(/="([^"]*)"/g, "='$1'") + '>'
);
const idSelector = '#' + id; const idSelector = '#' + id;
const iFrameID = 'i' + id; const iFrameID = 'i' + id;
const iFrameID_selector = '#' + iFrameID; const iFrameID_selector = '#' + iFrameID;

View File

@@ -152,26 +152,8 @@ function updateTextContentAndStyles(tspan, wrappedLine) {
.attr('font-style', word.type === 'em' ? 'italic' : 'normal') .attr('font-style', word.type === 'em' ? 'italic' : 'normal')
.attr('class', 'text-inner-tspan') .attr('class', 'text-inner-tspan')
.attr('font-weight', word.type === 'strong' ? 'bold' : 'normal'); .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
const special = [ const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
'<', if (index === 0) {
'>',
'&',
'"',
"'",
'.',
',',
':',
';',
'!',
'?',
'(',
')',
'[',
']',
'{',
'}',
];
if (index !== 0 && special.includes(word.content)) {
innerTspan.text(word.content); innerTspan.text(word.content);
} else { } else {
innerTspan.text(' ' + word.content); innerTspan.text(' ' + word.content);
@@ -225,7 +207,17 @@ export const createText = (
return vertexNode; return vertexNode;
} else { } else {
const structuredText = markdownToLines(text); const structuredText = markdownToLines(text);
const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
let lastWord;
structuredText.forEach((line) => {
line.forEach((word) => {
if (special.includes(word.content) && lastWord) {
lastWord.content += word.content;
word.content = '';
}
lastWord = word;
});
});
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground); const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
return svgLabel; return svgLabel;
} }

View File

@@ -38,6 +38,8 @@ export function markdownToLines(markdown) {
currentLine++; currentLine++;
lines.push([]); lines.push([]);
} }
// textLine.split(/ (?=[^!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]+)/).forEach((word) => {
textLine.split(' ').forEach((word) => { textLine.split(' ').forEach((word) => {
if (word) { if (word) {
lines[currentLine].push({ content: word, type: parentType || 'normal' }); lines[currentLine].push({ content: word, type: parentType || 'normal' });

29
pnpm-lock.yaml generated
View File

@@ -2356,7 +2356,7 @@ packages:
react: 16.14.0 react: 16.14.0
react-dom: 16.14.0 react-dom: 16.14.0
dependencies: dependencies:
'@types/react': 18.0.28 '@types/react': 18.0.33
react: 16.14.0 react: 16.14.0
react-dom: 16.14.0_react@16.14.0 react-dom: 16.14.0_react@16.14.0
dev: false dev: false
@@ -2956,12 +2956,12 @@ packages:
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
dev: true dev: true
/@types/react/18.0.28: /@types/react/18.0.33:
resolution: {integrity: sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==} resolution: {integrity: sha512-sHxzVxeanvQyQ1lr8NSHaj0kDzcNiGpILEVt69g9S31/7PfMvNCKLKcsHw4lYKjs3cGNJjXSP4mYzX43QlnjNA==}
dependencies: dependencies:
'@types/prop-types': 15.7.5 '@types/prop-types': 15.7.5
'@types/scheduler': 0.16.2 '@types/scheduler': 0.16.3
csstype: 3.1.1 csstype: 3.1.2
dev: false dev: false
/@types/responselike/1.0.0: /@types/responselike/1.0.0:
@@ -2981,8 +2981,8 @@ packages:
rollup: 2.79.1 rollup: 2.79.1
dev: true dev: true
/@types/scheduler/0.16.2: /@types/scheduler/0.16.3:
resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
dev: false dev: false
/@types/semver/7.3.12: /@types/semver/7.3.12:
@@ -3417,7 +3417,7 @@ packages:
'@vue/shared': 3.2.45 '@vue/shared': 3.2.45
estree-walker: 2.0.2 estree-walker: 2.0.2
magic-string: 0.25.9 magic-string: 0.25.9
postcss: 8.4.21 postcss: 8.4.20
source-map: 0.6.1 source-map: 0.6.1
dev: true dev: true
@@ -5161,8 +5161,8 @@ packages:
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
dev: true dev: true
/csstype/3.1.1: /csstype/3.1.2:
resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
dev: false dev: false
/cypress-image-snapshot/4.0.1_cypress@12.5.1+jest@29.3.1: /cypress-image-snapshot/4.0.1_cypress@12.5.1+jest@29.3.1:
@@ -9755,6 +9755,15 @@ packages:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: true dev: true
/postcss/8.4.20:
resolution: {integrity: sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==}
engines: {node: ^10 || ^12 || >=14}
dependencies:
nanoid: 3.3.4
picocolors: 1.0.0
source-map-js: 1.0.2
dev: true
/postcss/8.4.21: /postcss/8.4.21:
resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}