mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-01-28 07:03:17 +08:00
Merge branch 'develop' into aakansha/actor-name
This commit is contained in:
commit
1a8361d5c9
5
.github/lychee.toml
vendored
5
.github/lychee.toml
vendored
@ -35,7 +35,10 @@ exclude = [
|
|||||||
'packages/mermaid/src/docs/config/setup/*',
|
'packages/mermaid/src/docs/config/setup/*',
|
||||||
|
|
||||||
# Ignore Discord invite
|
# Ignore Discord invite
|
||||||
"https://discord.gg"
|
"https://discord.gg",
|
||||||
|
|
||||||
|
# BundlePhobia has frequent downtime
|
||||||
|
"https://bundlephobia.com"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Exclude all private IPs from checking.
|
# Exclude all private IPs from checking.
|
||||||
|
@ -68,6 +68,7 @@
|
|||||||
"jgreywolf",
|
"jgreywolf",
|
||||||
"jison",
|
"jison",
|
||||||
"jiti",
|
"jiti",
|
||||||
|
"katex",
|
||||||
"kaufmann",
|
"kaufmann",
|
||||||
"khroma",
|
"khroma",
|
||||||
"klemm",
|
"klemm",
|
||||||
@ -81,6 +82,7 @@
|
|||||||
"logmsg",
|
"logmsg",
|
||||||
"lucida",
|
"lucida",
|
||||||
"markdownish",
|
"markdownish",
|
||||||
|
"mathml",
|
||||||
"matthieu",
|
"matthieu",
|
||||||
"matthieumorel",
|
"matthieumorel",
|
||||||
"mdast",
|
"mdast",
|
||||||
|
36
cypress/integration/rendering/katex.spec.js
Normal file
36
cypress/integration/rendering/katex.spec.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { imgSnapshotTest } from '../../helpers/util';
|
||||||
|
|
||||||
|
describe('Katex', () => {
|
||||||
|
it('1: should render a complex Katex flowchart no htmlLabels', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A["$$f(\\relax{x}) = \\int_{-\\infty}^\\infty \\hat{f}(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi$$"] -->|"$$\\Bigg(\\bigg(\\Big(\\big((\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a})\\big)\\Big)\\bigg)\\Bigg)$$"| B("$$1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}} {1+\\frac{e^{-8\\pi}} {1+\\cdots}}}}$$")
|
||||||
|
A -->|"$$\\overbrace{a+b+c}^{\\text{note}}$$"| C("$$\\phase{-78^\\circ}$$")
|
||||||
|
B --> D("$$x = \\begin{cases} a &\\text{if } b \\\\ c &\\text{if } d \\end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\\begin{bmatrix}-\\cos{t}+\\sin{t}\\\\ 2\\cos{t} \\end{bmatrix}e^{2t}$$")`,
|
||||||
|
{ fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('2: should render a Katex flowchart containing the Greek alphabet', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A["$$\\alpha\\beta\\gamma\\delta\\epsilon\\zeta\\eta\\theta\\iota\\kappa\\lambda\\mu\\nu\\xi\\omicron\\pi\\rho\\sigma\\tau\\upsilon\\phi\\chi\\psi\\omega$$"] --> B["$$\\Alpha\\Beta\\Gamma\\Delta\\Epsilon\\Zeta\\Eta\\Theta\\Iota\\Kappa\\Lambda\\Mu\\Nu\\Xi\\Omicron\\Pi\\Rho\\Sigma\\Tau\\Upsilon\\Phi\\Chi\\Psi\\Omega$$"]`,
|
||||||
|
{ fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('3: should render a Katex flowchart containing set theory symbols', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`graph LR
|
||||||
|
A["$$\\forall\\complement\\therefore\\emptyset\\exists\\subset\\because\\empty\\exist\\supset\\mapsto\\varnothing\\nexists\\mid\\to\\implies\\in\\land\\gets\\impliedby\\isin\\lor\\leftrightarrow\\iff\\notin\\ni\\notni\\lnot$$"] --> B["$$\\nabla\\Im\\Reals\\jmath\\partial\\image\\wp\\aleph\\Game\\weierp\\alef\\Finv\\N\\Z\\alefsym\\cnums\\natnums\\beth\\Complex\\R\\gimel\\ell\\Re\\daleth\\hbar\\real\\eth\\hslash\\reals$$"]`,
|
||||||
|
{ fontFamily: 'courier' }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// TODO: changes made to develop between Feb 13 - Feb 23 cause this test to no longer function
|
||||||
|
// it.skip('4: should render an error box originating from Katex', () => {
|
||||||
|
// imgSnapshotTest(
|
||||||
|
// `graph LR
|
||||||
|
// A["$$\\shouldBeError$$"]`,
|
||||||
|
// { fontFamily: 'courier' }
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
});
|
@ -1102,6 +1102,57 @@
|
|||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
<h2>Sample 20</h2>
|
||||||
|
<h3>graph</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A["$$f(\relax{x}) = \int_{-\infty}^\infty \hat{f}(\xi)\,e^{2 \pi i \xi x}\,d\xi$$"] -->|"$$\Bigg(\bigg(\Big(\big((\frac{-b\pm\sqrt{b^2-4ac}}{2a})\big)\Big)\bigg)\Bigg)$$"| B("$$1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots}}}}$$")
|
||||||
|
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\phase{-78^\circ}$$")
|
||||||
|
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h3>flowchart</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
flowchart LR
|
||||||
|
A["$$f(\relax{x}) = \int_{-\infty}^\infty \hat{f}(\xi)\,e^{2 \pi i \xi x}\,d\xi$$"] -->|"$$\Bigg(\bigg(\Big(\big((\frac{-b\pm\sqrt{b^2-4ac}}{2a})\big)\Big)\bigg)\Bigg)$$"| B("$$1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots}}}}$$")
|
||||||
|
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\phase{-78^\circ}$$")
|
||||||
|
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h2>Sample 21</h2>
|
||||||
|
<h3>graph</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A["$$\alpha\beta\gamma\delta\epsilon\zeta\eta\theta\iota\kappa\lambda\mu\nu\xi\omicron\pi\rho\sigma\tau\upsilon\phi\chi\psi\omega$$"] --> B["$$\Alpha\Beta\Gamma\Delta\Epsilon\Zeta\Eta\Theta\Iota\Kappa\Lambda\Mu\Nu\Xi\Omicron\Pi\Rho\Sigma\Tau\Upsilon\Phi\Chi\Psi\Omega$$"]
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h3>flowchart</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A["$$\alpha\beta\gamma\delta\epsilon\zeta\eta\theta\iota\kappa\lambda\mu\nu\xi\omicron\pi\rho\sigma\tau\upsilon\phi\chi\psi\omega$$"] --> B["$$\Alpha\Beta\Gamma\Delta\Epsilon\Zeta\Eta\Theta\Iota\Kappa\Lambda\Mu\Nu\Xi\Omicron\Pi\Rho\Sigma\Tau\Upsilon\Phi\Chi\Psi\Omega$$"]
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h2>Sample 22</h2>
|
||||||
|
<h3>graph</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A["$$\forall\complement\therefore\emptyset\exists\subset\because\empty\exist\supset\mapsto\varnothing\nexists\mid\to\implies\in\land\gets\impliedby\isin\lor\leftrightarrow\iff\notin\ni\notni\lnot$$"] --> B["$$\nabla\Im\Reals\jmath\partial\image\wp\aleph\Game\weierp\alef\Finv\N\Z\alefsym\cnums\natnums\beth\Complex\R\gimel\ell\Re\daleth\hbar\real\eth\hslash\reals$$"]
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<h3>flowchart</h3>
|
||||||
|
<pre class="mermaid">
|
||||||
|
graph LR
|
||||||
|
A["$$\forall\complement\therefore\emptyset\exists\subset\because\empty\exist\supset\mapsto\varnothing\nexists\mid\to\implies\in\land\gets\impliedby\isin\lor\leftrightarrow\iff\notin\ni\notni\lnot$$"] --> B["$$\nabla\Im\Reals\jmath\partial\image\wp\aleph\Game\weierp\alef\Finv\N\Z\alefsym\cnums\natnums\beth\Complex\R\gimel\ell\Re\daleth\hbar\real\eth\hslash\reals$$"]
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
<body>
|
<body>
|
||||||
<h1>Sequence diagram demos</h1>
|
<h1>Sequence diagram demos</h1>
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
accTitle: test the accTitle
|
accTitle: test the accTitle
|
||||||
accDescr: Test a description
|
accDescr: Test a description
|
||||||
|
|
||||||
participant Alice
|
participant Alice
|
||||||
participant Bob
|
participant Bob
|
||||||
@ -31,39 +31,39 @@
|
|||||||
rect rgb(200, 220, 100)
|
rect rgb(200, 220, 100)
|
||||||
rect rgb(200, 255, 200)
|
rect rgb(200, 255, 200)
|
||||||
|
|
||||||
Alice ->> Bob: Hello Bob, how are you?
|
Alice ->> Bob: Hello Bob, how are you?
|
||||||
Bob-->>John: How about you John?
|
Bob-->>John: How about you John?
|
||||||
end
|
end
|
||||||
|
|
||||||
Bob--x Alice: I am good thanks!
|
Bob--x Alice: I am good thanks!
|
||||||
Bob-x John: I am good thanks!
|
Bob-x John: I am good thanks!
|
||||||
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
|
Note right of John: John thinks a long<br />long time, so long<br />that the text does<br />not fit on a row.
|
||||||
|
|
||||||
Bob-->Alice: Checking with John...
|
Bob-->Alice: Checking with John...
|
||||||
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
|
Note over John:wrap: John looks like he's still thinking, so Bob prods him a bit.
|
||||||
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
|
Bob-x John: Hey John - we're still waiting to know<br />how you're doing
|
||||||
Note over John:nowrap: John's trying hard not to break his train of thought.
|
Note over John:nowrap: John's trying hard not to break his train of thought.
|
||||||
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
|
Bob-x John:wrap: John! Are you still debating about how you're doing? How long does it take??
|
||||||
Note over John: After a few more moments, John<br />finally snaps out of it.
|
Note over John: After a few more moments, John<br />finally snaps out of it.
|
||||||
end
|
end
|
||||||
|
|
||||||
autonumber off
|
autonumber off
|
||||||
alt either this
|
alt either this
|
||||||
Alice->>+John: Yes
|
Alice->>+John: Yes
|
||||||
John-->>-Alice: OK
|
John-->>-Alice: OK
|
||||||
else or this
|
else or this
|
||||||
autonumber
|
autonumber
|
||||||
Alice->>John: No
|
Alice->>John: No
|
||||||
else or this will happen
|
else or this will happen
|
||||||
Alice->John: Maybe
|
Alice->John: Maybe
|
||||||
end
|
end
|
||||||
autonumber 200
|
autonumber 200
|
||||||
par this happens in parallel
|
par this happens in parallel
|
||||||
Alice -->> Bob: Parallel message 1
|
Alice -->> Bob: Parallel message 1
|
||||||
and
|
and
|
||||||
Alice -->> John: Parallel message 2
|
Alice -->> John: Parallel message 2
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
---
|
---
|
||||||
@ -153,18 +153,18 @@
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
box lightgreen Alice & John
|
box lightgreen Alice & John
|
||||||
participant A
|
participant A
|
||||||
participant J
|
participant J
|
||||||
end
|
end
|
||||||
box Another Group very very long description not wrapped
|
box Another Group very very long description not wrapped
|
||||||
participant B
|
participant B
|
||||||
end
|
end
|
||||||
A->>J: Hello John, how are you?
|
A->>J: Hello John, how are you?
|
||||||
J->>A: Great!
|
J->>A: Great!
|
||||||
A->>B: Hello Bob, how are you ?
|
A->>B: Hello Bob, how are you ?
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
@ -195,6 +195,49 @@
|
|||||||
John--xAlice: Great!
|
John--xAlice: Great!
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<pre class="mermaid">
|
||||||
|
sequenceDiagram
|
||||||
|
participant 1 as $$\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}$$
|
||||||
|
participant 2 as $$\beta$$
|
||||||
|
participant 3 as $$\delta$$
|
||||||
|
participant 4 as $$\frac{\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}}{\frac{\text{d}}{\text{d}x}{x^2}}$$
|
||||||
|
1->>2: $$\sqrt{2}$$
|
||||||
|
note right of 2: $$\frac{1+\frac{1+\frac{1+\frac{1}{2}}{2}}{2}}{2}+\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$
|
||||||
|
2->>3: $$\frac{\lim_{x\rightarrow0}{\frac{1}{x}}}{\frac{-b\pm\sqrt{b^2-4ac}}{2a}}$$
|
||||||
|
note right of 3: $$\frac{-b\pm\sqrt{b^2-4ac}}{2a}$$
|
||||||
|
3->>4: $$\lim_{x\rightarrow0}{\frac{1}{x}}$$;
|
||||||
|
note right of 4: multiline
|
||||||
|
4->>1: multiline<br />using #lt;br /#gt;
|
||||||
|
note right of 1: multiline<br />$$\frac{1}{2}$$<br />3rd line
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
<pre class="mermaid">
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant 1 as $$\alpha$$lex
|
||||||
|
participant 2 as $$\beta$$ob
|
||||||
|
participant 3 as $$\theta$$iffany
|
||||||
|
1->>2: Hello John, does $$\frac{1}{2}+1=2$$?
|
||||||
|
loop $$\frac{1}{2}+1=2$$
|
||||||
|
2->>2: $$\frac{1}{2}+1=\frac{3}{2}$$
|
||||||
|
end
|
||||||
|
Note right of 2: $$x = \begin{cases} 1 &\text{if } \frac{1}{2}+1=2 \\ 0 &\text{if } \frac{1}{2}+1\ne2 \end{cases}$$
|
||||||
|
2-->>1: $$\frac{1}{2}+1\ne2\implies 1$$
|
||||||
|
2->>3: $$\frac{\text{d}}{\text{d}x}{3x^2+2x+1}$$
|
||||||
|
3-->>2: $$6x+2$$
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<pre class="mermaid">
|
||||||
|
sequenceDiagram
|
||||||
|
actor Alice
|
||||||
|
actor John
|
||||||
|
Alice-xJohn: Hello John, how are you?
|
||||||
|
John--xAlice: Great!
|
||||||
|
</pre>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import mermaid from './mermaid.esm.mjs';
|
import mermaid from './mermaid.esm.mjs';
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
|
86
docs/config/math.md
Normal file
86
docs/config/math.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
> **Warning**
|
||||||
|
>
|
||||||
|
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||||
|
>
|
||||||
|
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/math.md](../../packages/mermaid/src/docs/config/math.md).
|
||||||
|
|
||||||
|
# Math Configuration (v\<MERMAID_RELEASE_VERSION>+)
|
||||||
|
|
||||||
|
Mermaid supports rendering mathematical expressions through the [KaTeX](https://katex.org/) typesetter.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To render math within a diagram, surround the mathematical expression with the `$$` delimiter.
|
||||||
|
|
||||||
|
Note that at the moment, the only supported diagrams are below:
|
||||||
|
|
||||||
|
### Flowcharts
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
graph LR
|
||||||
|
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||||
|
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||||
|
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||||
|
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||||
|
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sequence
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant 1 as $$\alpha$$
|
||||||
|
participant 2 as $$\beta$$
|
||||||
|
1->>2: Solve: $$\sqrt{2+2}$$
|
||||||
|
2-->>1: Answer: $$2$$
|
||||||
|
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant 1 as $$\alpha$$
|
||||||
|
participant 2 as $$\beta$$
|
||||||
|
1->>2: Solve: $$\sqrt{2+2}$$
|
||||||
|
2-->>1: Answer: $$2$$
|
||||||
|
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||||
|
```
|
||||||
|
|
||||||
|
## Legacy Support
|
||||||
|
|
||||||
|
By default, MathML is used for rendering mathematical expressions. If you have users on [unsupported browsers](https://caniuse.com/?search=mathml), `legacyMathML` can be set in the config to fall back to CSS rendering. Note that **you must provide KaTeX's stylesheets on your own** as they do not come bundled with Mermaid.
|
||||||
|
|
||||||
|
Example with legacy mode enabled (the latest version of KaTeX's stylesheet can be found on their [docs](https://katex.org/docs/browser.html)):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- Please ensure the stylesheet's version matches with the KaTeX version in your package-lock -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/katex@{version_number}/dist/katex.min.css"
|
||||||
|
integrity="sha384-{hash}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
mermaid.initialize({
|
||||||
|
legacyMathML: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
@ -70,6 +70,7 @@
|
|||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"dompurify": "^3.0.5",
|
"dompurify": "^3.0.5",
|
||||||
"elkjs": "^0.9.0",
|
"elkjs": "^0.9.0",
|
||||||
|
"katex": "^0.16.9",
|
||||||
"khroma": "^2.0.0",
|
"khroma": "^2.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mdast-util-from-markdown": "^1.3.0",
|
"mdast-util-from-markdown": "^1.3.0",
|
||||||
@ -89,6 +90,7 @@
|
|||||||
"@types/d3-shape": "^3.1.1",
|
"@types/d3-shape": "^3.1.1",
|
||||||
"@types/dompurify": "^3.0.2",
|
"@types/dompurify": "^3.0.2",
|
||||||
"@types/jsdom": "^21.1.1",
|
"@types/jsdom": "^21.1.1",
|
||||||
|
"@types/katex": "^0.16.7",
|
||||||
"@types/lodash-es": "^4.17.7",
|
"@types/lodash-es": "^4.17.7",
|
||||||
"@types/micromatch": "^4.0.2",
|
"@types/micromatch": "^4.0.2",
|
||||||
"@types/prettier": "^2.7.2",
|
"@types/prettier": "^2.7.2",
|
||||||
|
@ -127,6 +127,14 @@ export interface MermaidConfig {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
secure?: string[];
|
secure?: string[];
|
||||||
|
/**
|
||||||
|
* This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||||
|
* without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||||
|
* equations are replaced with a warning. If this option is enabled and MathML is not supported, Mermaid will
|
||||||
|
* fall back to legacy rendering for KaTeX.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
legacyMathML?: boolean;
|
||||||
/**
|
/**
|
||||||
* This option controls if the generated ids of nodes in the SVG are
|
* This option controls if the generated ids of nodes in the SVG are
|
||||||
* generated randomly or based on a seed.
|
* generated randomly or based on a seed.
|
||||||
|
@ -289,6 +289,83 @@ const processSet = (input: string): string => {
|
|||||||
return chars.join('');
|
return chars.join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: find a better method for detecting support. This interface was added in the MathML 4 spec.
|
||||||
|
// Firefox versions between [4,71] (0.47%) and Safari versions between [5,13.4] (0.17%) don't have this interface implemented but MathML is supported
|
||||||
|
export const isMathMLSupported = () => window.MathMLElement !== undefined;
|
||||||
|
|
||||||
|
export const katexRegex = /\$\$(.*)\$\$/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not a text has KaTeX delimiters
|
||||||
|
*
|
||||||
|
* @param text - The text to test
|
||||||
|
* @returns Whether or not the text has KaTeX delimiters
|
||||||
|
*/
|
||||||
|
export const hasKatex = (text: string): boolean => (text.match(katexRegex)?.length ?? 0) > 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the minimum dimensions needed to display a div containing MathML
|
||||||
|
*
|
||||||
|
* @param text - The text to test
|
||||||
|
* @param config - Configuration for Mermaid
|
||||||
|
* @returns Object containing \{width, height\}
|
||||||
|
*/
|
||||||
|
export const calculateMathMLDimensions = async (text: string, config: MermaidConfig) => {
|
||||||
|
text = await renderKatex(text, config);
|
||||||
|
const divElem = document.createElement('div');
|
||||||
|
divElem.innerHTML = text;
|
||||||
|
divElem.id = 'katex-temp';
|
||||||
|
divElem.style.visibility = 'hidden';
|
||||||
|
divElem.style.position = 'absolute';
|
||||||
|
divElem.style.top = '0';
|
||||||
|
const body = document.querySelector('body');
|
||||||
|
body?.insertAdjacentElement('beforeend', divElem);
|
||||||
|
const dim = { width: divElem.clientWidth, height: divElem.clientHeight };
|
||||||
|
divElem.remove();
|
||||||
|
return dim;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to render and return the KaTeX portion of a string with MathML
|
||||||
|
*
|
||||||
|
* @param text - The text to test
|
||||||
|
* @param config - Configuration for Mermaid
|
||||||
|
* @returns String containing MathML if KaTeX is supported, or an error message if it is not and stylesheets aren't present
|
||||||
|
*/
|
||||||
|
export const renderKatex = async (text: string, config: MermaidConfig): Promise<string> => {
|
||||||
|
if (!hasKatex(text)) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isMathMLSupported() && !config.legacyMathML) {
|
||||||
|
return text.replace(katexRegex, 'MathML is unsupported in this environment.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { default: katex } = await import('katex');
|
||||||
|
return text
|
||||||
|
.split(lineBreakRegex)
|
||||||
|
.map((line) =>
|
||||||
|
hasKatex(line)
|
||||||
|
? `
|
||||||
|
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
|
||||||
|
${line}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: `<div>${line}</div>`
|
||||||
|
)
|
||||||
|
.join('')
|
||||||
|
.replace(katexRegex, (_, c) =>
|
||||||
|
katex
|
||||||
|
.renderToString(c, {
|
||||||
|
throwOnError: true,
|
||||||
|
displayMode: true,
|
||||||
|
output: isMathMLSupported() ? 'mathml' : 'htmlAndMathml',
|
||||||
|
})
|
||||||
|
.replace(/\n/g, ' ')
|
||||||
|
.replace(/<annotation.*<\/annotation>/g, '')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getRows,
|
getRows,
|
||||||
sanitizeText,
|
sanitizeText,
|
||||||
|
@ -4,7 +4,7 @@ import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
|||||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a an info picture in the tag with id: id based on the graph definition in text.
|
* Draws an info picture in the tag with id: id based on the graph definition in text.
|
||||||
*
|
*
|
||||||
* @param _text - Mermaid graph definition.
|
* @param _text - Mermaid graph definition.
|
||||||
* @param id - The text for the error
|
* @param id - The text for the error
|
||||||
@ -12,12 +12,12 @@ import { configureSvgSize } from '../../setupGraphViewbox.js';
|
|||||||
*/
|
*/
|
||||||
export const draw = (_text: string, id: string, version: string) => {
|
export const draw = (_text: string, id: string, version: string) => {
|
||||||
log.debug('rendering svg for syntax error\n');
|
log.debug('rendering svg for syntax error\n');
|
||||||
|
|
||||||
const svg: SVG = selectSvgElement(id);
|
const svg: SVG = selectSvgElement(id);
|
||||||
|
const g: Group = svg.append('g');
|
||||||
|
|
||||||
svg.attr('viewBox', '0 0 2412 512');
|
svg.attr('viewBox', '0 0 2412 512');
|
||||||
configureSvgSize(svg, 100, 512, true);
|
configureSvgSize(svg, 100, 512, true);
|
||||||
|
|
||||||
const g: Group = svg.append('g');
|
|
||||||
g.append('path')
|
g.append('path')
|
||||||
.attr('class', 'error-icon')
|
.attr('class', 'error-icon')
|
||||||
.attr(
|
.attr(
|
||||||
|
@ -5,7 +5,7 @@ import utils from '../../utils.js';
|
|||||||
import { render } from '../../dagre-wrapper/index.js';
|
import { render } from '../../dagre-wrapper/index.js';
|
||||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common, { evaluate } from '../common/common.js';
|
import common, { evaluate, renderKatex } from '../common/common.js';
|
||||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||||
|
|
||||||
@ -27,12 +27,12 @@ export const setConf = function (cnf) {
|
|||||||
* @param doc
|
* @param doc
|
||||||
* @param diagObj
|
* @param diagObj
|
||||||
*/
|
*/
|
||||||
export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
export const addVertices = async function (vert, g, svgId, root, doc, diagObj) {
|
||||||
const svg = root.select(`[id="${svgId}"]`);
|
const svg = root.select(`[id="${svgId}"]`);
|
||||||
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) {
|
for (const id of keys) {
|
||||||
const vertex = vert[id];
|
const vertex = vert[id];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,10 +59,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
const node = {
|
const node = {
|
||||||
label: vertexText.replace(
|
label: vertexText,
|
||||||
/fa[blrs]?:fa-[\w-]+/g,
|
|
||||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
vertexNode = addHtmlLabel(svg, node).node();
|
vertexNode = addHtmlLabel(svg, node).node();
|
||||||
vertexNode.parentNode.removeChild(vertexNode);
|
vertexNode.parentNode.removeChild(vertexNode);
|
||||||
@ -143,11 +140,13 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
default:
|
default:
|
||||||
_shape = 'rect';
|
_shape = 'rect';
|
||||||
}
|
}
|
||||||
|
const labelText = await renderKatex(vertexText, getConfig());
|
||||||
|
|
||||||
// Add the node
|
// Add the node
|
||||||
g.setNode(vertex.id, {
|
g.setNode(vertex.id, {
|
||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText,
|
||||||
labelType: vertex.labelType,
|
labelType: vertex.labelType,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
ry: radious,
|
ry: radious,
|
||||||
@ -170,7 +169,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
labelType: vertex.labelType,
|
labelType: vertex.labelType,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
ry: radious,
|
ry: radious,
|
||||||
class: classStr,
|
class: classStr,
|
||||||
@ -183,7 +182,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
props: vertex.props,
|
props: vertex.props,
|
||||||
padding: getConfig().flowchart.padding,
|
padding: getConfig().flowchart.padding,
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,7 +192,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
* @param {object} g The graph object
|
* @param {object} g The graph object
|
||||||
* @param diagObj
|
* @param diagObj
|
||||||
*/
|
*/
|
||||||
export const addEdges = function (edges, g, diagObj) {
|
export const addEdges = async function (edges, g, diagObj) {
|
||||||
log.info('abc78 edges = ', edges);
|
log.info('abc78 edges = ', edges);
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
let linkIdCnt = {};
|
let linkIdCnt = {};
|
||||||
@ -207,7 +206,7 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
defaultLabelStyle = defaultStyles.labelStyle;
|
defaultLabelStyle = defaultStyles.labelStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
edges.forEach(function (edge) {
|
for (const edge of edges) {
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
// Identify Link
|
// Identify Link
|
||||||
@ -315,9 +314,8 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
edgeData.arrowheadStyle = 'fill: #333';
|
edgeData.arrowheadStyle = 'fill: #333';
|
||||||
edgeData.labelpos = 'c';
|
edgeData.labelpos = 'c';
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeData.labelType = edge.labelType;
|
edgeData.labelType = edge.labelType;
|
||||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
edgeData.label = await renderKatex(edge.text.replace(common.lineBreakRegex, '\n'), getConfig());
|
||||||
|
|
||||||
if (edge.style === undefined) {
|
if (edge.style === undefined) {
|
||||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
||||||
@ -330,7 +328,7 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
|
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
g.setEdge(edge.start, edge.end, edgeData, cnt);
|
g.setEdge(edge.start, edge.end, edgeData, cnt);
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -427,8 +425,8 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
g.setParent(subG.nodes[j], subG.id);
|
g.setParent(subG.nodes[j], subG.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addVertices(vert, g, id, root, doc, diagObj);
|
await addVertices(vert, g, id, root, doc, diagObj);
|
||||||
addEdges(edges, g, diagObj);
|
await addEdges(edges, g, diagObj);
|
||||||
|
|
||||||
// Add custom shapes
|
// Add custom shapes
|
||||||
// flowChartShapes.addToRenderV2(addShape);
|
// flowChartShapes.addToRenderV2(addShape);
|
||||||
|
@ -15,7 +15,7 @@ describe('when using mermaid and ', function () {
|
|||||||
flowDb.clear();
|
flowDb.clear();
|
||||||
flowDb.setGen('gen-2');
|
flowDb.setGen('gen-2');
|
||||||
});
|
});
|
||||||
it('should handle edges with text', () => {
|
it('should handle edges with text', async () => {
|
||||||
parser.parse('graph TD;A-->|text ex|B;');
|
parser.parse('graph TD;A-->|text ex|B;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -29,7 +29,7 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle edges without text', async function () {
|
it('should handle edges without text', async function () {
|
||||||
@ -45,10 +45,10 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle open-ended edges', () => {
|
it('should handle open-ended edges', async () => {
|
||||||
parser.parse('graph TD;A---B;');
|
parser.parse('graph TD;A---B;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -61,10 +61,10 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle edges with styles defined', () => {
|
it('should handle edges with styles defined', async () => {
|
||||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -78,9 +78,9 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
it('should handle edges with interpolation defined', () => {
|
it('should handle edges with interpolation defined', async () => {
|
||||||
parser.parse('graph TD;A---B; linkStyle 0 interpolate basis');
|
parser.parse('graph TD;A---B; linkStyle 0 interpolate basis');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -94,9 +94,9 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
it('should handle edges with text and styles defined', () => {
|
it('should handle edges with text and styles defined', async () => {
|
||||||
parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
parser.parse('graph TD;A---|the text|B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -111,10 +111,10 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set fill to "none" by default when handling edges', () => {
|
it('should set fill to "none" by default when handling edges', async () => {
|
||||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -128,10 +128,10 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set fill to none if fill is set in linkStyle', () => {
|
it('should not set fill to none if fill is set in linkStyle', async () => {
|
||||||
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;');
|
parser.parse('graph TD;A---B; linkStyle 0 stroke:val1,stroke-width:val2,fill:blue;');
|
||||||
flowDb.getVertices();
|
flowDb.getVertices();
|
||||||
const edges = flowDb.getEdges();
|
const edges = flowDb.getEdges();
|
||||||
@ -144,7 +144,7 @@ describe('when using mermaid and ', function () {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
flowRenderer.addEdges(edges, mockG, diag);
|
await flowRenderer.addEdges(edges, mockG, diag);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,7 @@ import { render as Render } from 'dagre-d3-es';
|
|||||||
import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
|
import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
|
||||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common, { evaluate } from '../common/common.js';
|
import common, { evaluate, renderKatex } from '../common/common.js';
|
||||||
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
import { interpolateToCurve, getStylesFromArray } from '../../utils.js';
|
||||||
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
|
||||||
import flowChartShapes from './flowChartShapes.js';
|
import flowChartShapes from './flowChartShapes.js';
|
||||||
@ -28,13 +28,13 @@ export const setConf = function (cnf) {
|
|||||||
* @param _doc
|
* @param _doc
|
||||||
* @param diagObj
|
* @param diagObj
|
||||||
*/
|
*/
|
||||||
export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
export const addVertices = async function (vert, g, svgId, root, _doc, diagObj) {
|
||||||
const svg = !root ? select(`[id="${svgId}"]`) : root.select(`[id="${svgId}"]`);
|
const svg = !root ? select(`[id="${svgId}"]`) : root.select(`[id="${svgId}"]`);
|
||||||
const doc = !_doc ? document : _doc;
|
const doc = !_doc ? document : _doc;
|
||||||
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) {
|
for (const id of keys) {
|
||||||
const vertex = vert[id];
|
const vertex = vert[id];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,9 +57,12 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
|||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
const node = {
|
const node = {
|
||||||
label: vertexText.replace(
|
label: await renderKatex(
|
||||||
/fa[blrs]?:fa-[\w-]+/g,
|
vertexText.replace(
|
||||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
/fa[blrs]?:fa-[\w-]+/g,
|
||||||
|
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
),
|
||||||
|
getConfig()
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
vertexNode = addHtmlLabel(svg, node).node();
|
vertexNode = addHtmlLabel(svg, node).node();
|
||||||
@ -150,7 +153,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
|||||||
style: styles.style,
|
style: styles.style,
|
||||||
id: diagObj.db.lookUpDomId(vertex.id),
|
id: diagObj.db.lookUpDomId(vertex.id),
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -160,7 +163,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
|||||||
* @param {object} g The graph object
|
* @param {object} g The graph object
|
||||||
* @param diagObj
|
* @param diagObj
|
||||||
*/
|
*/
|
||||||
export const addEdges = function (edges, g, diagObj) {
|
export const addEdges = async function (edges, g, diagObj) {
|
||||||
let cnt = 0;
|
let cnt = 0;
|
||||||
|
|
||||||
let defaultStyle;
|
let defaultStyle;
|
||||||
@ -172,7 +175,7 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
defaultLabelStyle = defaultStyles.labelStyle;
|
defaultLabelStyle = defaultStyles.labelStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
edges.forEach(function (edge) {
|
for (const edge of edges) {
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
// Identify Link
|
// Identify Link
|
||||||
@ -239,9 +242,12 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
edgeData.labelType = 'html';
|
edgeData.labelType = 'html';
|
||||||
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
|
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
|
||||||
edgeData.labelStyle
|
edgeData.labelStyle
|
||||||
}">${edge.text.replace(
|
}">${await renderKatex(
|
||||||
/fa[blrs]?:fa-[\w-]+/g,
|
edge.text.replace(
|
||||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
/fa[blrs]?:fa-[\w-]+/g,
|
||||||
|
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
),
|
||||||
|
getConfig()
|
||||||
)}</span>`;
|
)}</span>`;
|
||||||
} else {
|
} else {
|
||||||
edgeData.labelType = 'text';
|
edgeData.labelType = 'text';
|
||||||
@ -261,7 +267,7 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
|
|
||||||
// Add the edge to the graph
|
// Add the edge to the graph
|
||||||
g.setEdge(diagObj.db.lookUpDomId(edge.start), diagObj.db.lookUpDomId(edge.end), edgeData, cnt);
|
g.setEdge(diagObj.db.lookUpDomId(edge.start), diagObj.db.lookUpDomId(edge.end), edgeData, cnt);
|
||||||
});
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -284,7 +290,7 @@ export const getClasses = function (text, diagObj) {
|
|||||||
* @param _version
|
* @param _version
|
||||||
* @param diagObj
|
* @param diagObj
|
||||||
*/
|
*/
|
||||||
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');
|
||||||
const { securityLevel, flowchart: conf } = getConfig();
|
const { securityLevel, flowchart: conf } = getConfig();
|
||||||
let sandboxElement;
|
let sandboxElement;
|
||||||
@ -350,8 +356,8 @@ export const draw = function (text, id, _version, diagObj) {
|
|||||||
g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id));
|
g.setParent(diagObj.db.lookUpDomId(subG.nodes[j]), diagObj.db.lookUpDomId(subG.id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addVertices(vert, g, id, root, doc, diagObj);
|
await addVertices(vert, g, id, root, doc, diagObj);
|
||||||
addEdges(edges, g, diagObj);
|
await addEdges(edges, g, diagObj);
|
||||||
|
|
||||||
// Create the renderer
|
// Create the renderer
|
||||||
const render = new Render();
|
const render = new Render();
|
||||||
|
@ -27,7 +27,7 @@ describe('the flowchart renderer', function () {
|
|||||||
['cylinder', 'cylinder'],
|
['cylinder', 'cylinder'],
|
||||||
['group', 'rect'],
|
['group', 'rect'],
|
||||||
].forEach(function ([type, expectedShape, expectedRadios = 0]) {
|
].forEach(function ([type, expectedShape, expectedRadios = 0]) {
|
||||||
it(`should add the correct shaped node to the graph for vertex type ${type}`, function () {
|
it(`should add the correct shaped node to the graph for vertex type ${type}`, async function () {
|
||||||
const fakeDiag = {
|
const fakeDiag = {
|
||||||
db: {
|
db: {
|
||||||
lookUpDomId: () => {
|
lookUpDomId: () => {
|
||||||
@ -41,7 +41,7 @@ describe('the flowchart renderer', function () {
|
|||||||
addedNodes.push([id, object]);
|
addedNodes.push([id, object]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addVertices(
|
await addVertices(
|
||||||
{
|
{
|
||||||
v1: {
|
v1: {
|
||||||
type,
|
type,
|
||||||
@ -70,7 +70,7 @@ describe('the flowchart renderer', function () {
|
|||||||
['Multi<br>Line', 'Multi<br/>Line', 'Multi<br />Line', 'Multi<br\t/>Line'].forEach(function (
|
['Multi<br>Line', 'Multi<br/>Line', 'Multi<br />Line', 'Multi<br\t/>Line'].forEach(function (
|
||||||
labelText
|
labelText
|
||||||
) {
|
) {
|
||||||
it('should handle multiline texts with different line breaks', function () {
|
it('should handle multiline texts with different line breaks', async function () {
|
||||||
const addedNodes = [];
|
const addedNodes = [];
|
||||||
const fakeDiag = {
|
const fakeDiag = {
|
||||||
db: {
|
db: {
|
||||||
@ -84,7 +84,7 @@ describe('the flowchart renderer', function () {
|
|||||||
addedNodes.push([id, object]);
|
addedNodes.push([id, object]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addVertices(
|
await addVertices(
|
||||||
{
|
{
|
||||||
v1: {
|
v1: {
|
||||||
type: 'rect',
|
type: 'rect',
|
||||||
@ -121,7 +121,7 @@ describe('the flowchart renderer', function () {
|
|||||||
'color:#ccc;text-align:center;',
|
'color:#ccc;text-align:center;',
|
||||||
],
|
],
|
||||||
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
||||||
it(`should add the styles to style and/or labelStyle for style ${style}`, function () {
|
it(`should add the styles to style and/or labelStyle for style ${style}`, async function () {
|
||||||
const addedNodes = [];
|
const addedNodes = [];
|
||||||
const fakeDiag = {
|
const fakeDiag = {
|
||||||
db: {
|
db: {
|
||||||
@ -135,7 +135,7 @@ describe('the flowchart renderer', function () {
|
|||||||
addedNodes.push([id, object]);
|
addedNodes.push([id, object]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addVertices(
|
await addVertices(
|
||||||
{
|
{
|
||||||
v1: {
|
v1: {
|
||||||
type: 'rect',
|
type: 'rect',
|
||||||
@ -160,7 +160,7 @@ describe('the flowchart renderer', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should add default class to all nodes which do not have another class assigned`, function () {
|
it(`should add default class to all nodes which do not have another class assigned`, async function () {
|
||||||
const addedNodes = [];
|
const addedNodes = [];
|
||||||
const mockG = {
|
const mockG = {
|
||||||
setNode: function (id, object) {
|
setNode: function (id, object) {
|
||||||
@ -174,7 +174,7 @@ describe('the flowchart renderer', function () {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addVertices(
|
await addVertices(
|
||||||
{
|
{
|
||||||
v1: {
|
v1: {
|
||||||
type: 'rect',
|
type: 'rect',
|
||||||
@ -206,7 +206,7 @@ describe('the flowchart renderer', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when adding edges to a graph', function () {
|
describe('when adding edges to a graph', function () {
|
||||||
it('should handle multiline texts and set centered label position', function () {
|
it('should handle multiline texts and set centered label position', async function () {
|
||||||
const addedEdges = [];
|
const addedEdges = [];
|
||||||
const fakeDiag = {
|
const fakeDiag = {
|
||||||
db: {
|
db: {
|
||||||
@ -220,7 +220,7 @@ describe('the flowchart renderer', function () {
|
|||||||
addedEdges.push(data);
|
addedEdges.push(data);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addEdges(
|
await addEdges(
|
||||||
[
|
[
|
||||||
{ text: 'Multi<br>Line' },
|
{ text: 'Multi<br>Line' },
|
||||||
{ text: 'Multi<br/>Line' },
|
{ text: 'Multi<br/>Line' },
|
||||||
@ -251,7 +251,7 @@ describe('the flowchart renderer', function () {
|
|||||||
'fill:red;',
|
'fill:red;',
|
||||||
],
|
],
|
||||||
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
].forEach(function ([style, expectedStyle, expectedLabelStyle]) {
|
||||||
it(`should add the styles to style and/or labelStyle for style ${style}`, function () {
|
it(`should add the styles to style and/or labelStyle for style ${style}`, async function () {
|
||||||
const addedEdges = [];
|
const addedEdges = [];
|
||||||
const fakeDiag = {
|
const fakeDiag = {
|
||||||
db: {
|
db: {
|
||||||
@ -265,7 +265,7 @@ describe('the flowchart renderer', function () {
|
|||||||
addedEdges.push(data);
|
addedEdges.push(data);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
addEdges([{ style: style, text: 'styling' }], mockG, fakeDiag);
|
await addEdges([{ style: style, text: 'styling' }], mockG, fakeDiag);
|
||||||
|
|
||||||
expect(addedEdges).toHaveLength(1);
|
expect(addedEdges).toHaveLength(1);
|
||||||
expect(addedEdges[0]).toHaveProperty('style', expectedStyle);
|
expect(addedEdges[0]).toHaveProperty('style', expectedStyle);
|
||||||
|
@ -66,6 +66,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
// text-anchor: start;
|
// text-anchor: start;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
.node .katex path {
|
||||||
|
fill: #000;
|
||||||
|
stroke: #000;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
.node .label {
|
.node .label {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ Note right of Bob: Bob thinks
|
|||||||
Bob-->Alice: I am good thanks!`;
|
Bob-->Alice: I am good thanks!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||||
expect(diagram.db.showSequenceNumbers()).toBe(false);
|
expect(diagram.db.showSequenceNumbers()).toBe(false);
|
||||||
});
|
});
|
||||||
it('should show sequence numbers when autonumber is enabled', async () => {
|
it('should show sequence numbers when autonumber is enabled', async () => {
|
||||||
@ -221,7 +221,7 @@ Note right of Bob: Bob thinks
|
|||||||
Bob-->Alice: I am good thanks!`;
|
Bob-->Alice: I am good thanks!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
|
||||||
expect(diagram.db.showSequenceNumbers()).toBe(true);
|
expect(diagram.db.showSequenceNumbers()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1648,7 +1648,7 @@ participant Alice`;
|
|||||||
// mermaidAPI.reinitialize({ sequence: { textPlacement: textPlacement } });
|
// mermaidAPI.reinitialize({ sequence: { textPlacement: textPlacement } });
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
// diagram.renderer.setConf(mermaidAPI.getConfig().sequence);
|
// diagram.renderer.setConf(mermaidAPI.getConfig().sequence);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds } = diagram.renderer.bounds.getBounds();
|
const { bounds } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1679,7 +1679,7 @@ Note over Alice: Alice thinks
|
|||||||
|
|
||||||
expect(mermaidAPI.getConfig().sequence.mirrorActors).toBeFalsy();
|
expect(mermaidAPI.getConfig().sequence.mirrorActors).toBeFalsy();
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1695,7 +1695,7 @@ participant Alice
|
|||||||
Note left of Alice: Alice thinks`;
|
Note left of Alice: Alice thinks`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
||||||
@ -1711,7 +1711,7 @@ participant Alice
|
|||||||
Note right of Alice: Alice thinks`;
|
Note right of Alice: Alice thinks`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1726,7 +1726,7 @@ sequenceDiagram
|
|||||||
Alice->Bob: Hello Bob, how are you?`;
|
Alice->Bob: Hello Bob, how are you?`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1744,7 +1744,7 @@ end
|
|||||||
Alice->Bob: Hello Bob, how are you?`;
|
Alice->Bob: Hello Bob, how are you?`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1759,7 +1759,7 @@ sequenceDiagram
|
|||||||
Alice->Bob: Hello Bob, how are you?`;
|
Alice->Bob: Hello Bob, how are you?`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
const mermaid = mermaidAPI.getConfig();
|
const mermaid = mermaidAPI.getConfig();
|
||||||
@ -1779,7 +1779,7 @@ wrap
|
|||||||
Alice->Bob: Hello Bob, how are you?`;
|
Alice->Bob: Hello Bob, how are you?`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const msgs = diagram.db.getMessages();
|
const msgs = diagram.db.getMessages();
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
@ -1800,7 +1800,7 @@ Note over Bob,Alice: Looks back
|
|||||||
`;
|
`;
|
||||||
// mermaidAPI.initialize({logLevel:0})
|
// mermaidAPI.initialize({logLevel:0})
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1815,7 +1815,7 @@ Alice->Bob: Hello Bob, how are you?
|
|||||||
Bob->Alice: Fine!`;
|
Bob->Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1831,7 +1831,7 @@ Note right of Bob: Bob thinks
|
|||||||
Bob->Alice: Fine!`;
|
Bob->Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1850,7 +1850,7 @@ Note left of Alice: Bob thinks
|
|||||||
Bob->Alice: Fine!`;
|
Bob->Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2);
|
||||||
@ -1867,7 +1867,7 @@ Note left of Alice: Bob thinks
|
|||||||
Bob->>Alice: Fine!`;
|
Bob->>Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
const msgs = diagram.db.getMessages();
|
const msgs = diagram.db.getMessages();
|
||||||
@ -1888,7 +1888,7 @@ Note left of Alice: Bob thinks
|
|||||||
Bob->>Alice: Fine!`;
|
Bob->>Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
const msgs = diagram.db.getMessages();
|
const msgs = diagram.db.getMessages();
|
||||||
@ -1911,7 +1911,7 @@ Note left of Alice: Bob thinks
|
|||||||
Bob->>Alice: Fine!`;
|
Bob->>Alice: Fine!`;
|
||||||
|
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
const msgs = diagram.db.getMessages();
|
const msgs = diagram.db.getMessages();
|
||||||
@ -1933,7 +1933,7 @@ Note left of Alice: Bob thinks
|
|||||||
Bob->>Alice: Fine!`;
|
Bob->>Alice: Fine!`;
|
||||||
// mermaidAPI.initialize({ logLevel: 0 });
|
// mermaidAPI.initialize({ logLevel: 0 });
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
const msgs = diagram.db.getMessages();
|
const msgs = diagram.db.getMessages();
|
||||||
@ -1957,7 +1957,7 @@ loop Cheers
|
|||||||
Bob->Alice: Fine!
|
Bob->Alice: Fine!
|
||||||
end`;
|
end`;
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
@ -1975,7 +1975,7 @@ end`;
|
|||||||
end
|
end
|
||||||
`;
|
`;
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
expect(bounds.starty).toBe(0);
|
expect(bounds.starty).toBe(0);
|
||||||
@ -2022,7 +2022,7 @@ sequenceDiagram
|
|||||||
participant Alice`;
|
participant Alice`;
|
||||||
diagram.renderer.bounds.init();
|
diagram.renderer.bounds.init();
|
||||||
await mermaidAPI.parse(str);
|
await mermaidAPI.parse(str);
|
||||||
diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
await diagram.renderer.draw(str, 'tst', '1.2.3', diagram);
|
||||||
|
|
||||||
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
const { bounds, models } = diagram.renderer.bounds.getBounds();
|
||||||
expect(bounds.startx).toBe(0);
|
expect(bounds.startx).toBe(0);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// @ts-nocheck TODO: fix file
|
// @ts-nocheck TODO: fix file
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
import svgDraw, { drawKatex, ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
|
||||||
import { log } from '../../logger.js';
|
import { log } from '../../logger.js';
|
||||||
import common from '../common/common.js';
|
import common, { calculateMathMLDimensions, hasKatex } from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
import { getConfig } from '../../diagram-api/diagramAPI.js';
|
||||||
import assignWithDepth from '../../assignWithDepth.js';
|
import assignWithDepth from '../../assignWithDepth.js';
|
||||||
@ -237,7 +237,7 @@ interface NoteModel {
|
|||||||
* @param elem - The diagram to draw to.
|
* @param elem - The diagram to draw to.
|
||||||
* @param noteModel - Note model options.
|
* @param noteModel - Note model options.
|
||||||
*/
|
*/
|
||||||
const drawNote = function (elem: any, noteModel: NoteModel) {
|
const drawNote = async function (elem: any, noteModel: NoteModel) {
|
||||||
bounds.bumpVerticalPos(conf.boxMargin);
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
noteModel.height = conf.boxMargin;
|
noteModel.height = conf.boxMargin;
|
||||||
noteModel.starty = bounds.getVerticalPos();
|
noteModel.starty = bounds.getVerticalPos();
|
||||||
@ -263,7 +263,7 @@ const drawNote = function (elem: any, noteModel: NoteModel) {
|
|||||||
textObj.textMargin = conf.noteMargin;
|
textObj.textMargin = conf.noteMargin;
|
||||||
textObj.valign = 'center';
|
textObj.valign = 'center';
|
||||||
|
|
||||||
const textElem = drawText(g, textObj);
|
const textElem = hasKatex(textObj.text) ? await drawKatex(g, textObj) : drawText(g, textObj);
|
||||||
|
|
||||||
const textHeight = Math.round(
|
const textHeight = Math.round(
|
||||||
textElem
|
textElem
|
||||||
@ -311,15 +311,20 @@ const actorFont = (cnf) => {
|
|||||||
* @param msgModel - The model containing fields describing a message
|
* @param msgModel - The model containing fields describing a message
|
||||||
* @returns `lineStartY` - The Y coordinate at which the message line starts
|
* @returns `lineStartY` - The Y coordinate at which the message line starts
|
||||||
*/
|
*/
|
||||||
function boundMessage(_diagram, msgModel): number {
|
async function boundMessage(_diagram, msgModel): Promise<number> {
|
||||||
bounds.bumpVerticalPos(10);
|
bounds.bumpVerticalPos(10);
|
||||||
const { startx, stopx, message } = msgModel;
|
const { startx, stopx, message } = msgModel;
|
||||||
const lines = common.splitBreaks(message).length;
|
const lines = common.splitBreaks(message).length;
|
||||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
const isKatexMsg = hasKatex(message);
|
||||||
const lineHeight = textDims.height / lines;
|
const textDims = isKatexMsg
|
||||||
msgModel.height += lineHeight;
|
? await calculateMathMLDimensions(message, getConfig())
|
||||||
|
: utils.calculateTextDimensions(message, messageFont(conf));
|
||||||
|
|
||||||
bounds.bumpVerticalPos(lineHeight);
|
if (!isKatexMsg) {
|
||||||
|
const lineHeight = textDims.height / lines;
|
||||||
|
msgModel.height += lineHeight;
|
||||||
|
bounds.bumpVerticalPos(lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
let lineStartY;
|
let lineStartY;
|
||||||
let totalOffset = textDims.height - 10;
|
let totalOffset = textDims.height - 10;
|
||||||
@ -360,7 +365,7 @@ function boundMessage(_diagram, msgModel): number {
|
|||||||
* @param lineStartY - The Y coordinate at which the message line starts
|
* @param lineStartY - The Y coordinate at which the message line starts
|
||||||
* @param diagObj - The diagram object.
|
* @param diagObj - The diagram object.
|
||||||
*/
|
*/
|
||||||
const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) {
|
||||||
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel;
|
||||||
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
const textDims = utils.calculateTextDimensions(message, messageFont(conf));
|
||||||
const textObj = svgDrawCommon.getTextObj();
|
const textObj = svgDrawCommon.getTextObj();
|
||||||
@ -378,7 +383,9 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
|
|||||||
textObj.textMargin = conf.wrapPadding;
|
textObj.textMargin = conf.wrapPadding;
|
||||||
textObj.tspan = false;
|
textObj.tspan = false;
|
||||||
|
|
||||||
drawText(diagram, textObj);
|
hasKatex(textObj.text)
|
||||||
|
? await drawKatex(diagram, textObj, { startx, stopx, starty: lineStartY })
|
||||||
|
: drawText(diagram, textObj);
|
||||||
|
|
||||||
const textWidth = textDims.width;
|
const textWidth = textDims.width;
|
||||||
|
|
||||||
@ -478,7 +485,7 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addActorRenderingData = function (
|
const addActorRenderingData = async function (
|
||||||
diagram,
|
diagram,
|
||||||
actors,
|
actors,
|
||||||
createdActors,
|
createdActors,
|
||||||
@ -548,12 +555,12 @@ const addActorRenderingData = function (
|
|||||||
bounds.bumpVerticalPos(maxHeight);
|
bounds.bumpVerticalPos(maxHeight);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawActors = function (diagram, actors, actorKeys, isFooter) {
|
export const drawActors = async function (diagram, actors, actorKeys, isFooter) {
|
||||||
if (!isFooter) {
|
if (!isFooter) {
|
||||||
for (const actorKey of actorKeys) {
|
for (const actorKey of actorKeys) {
|
||||||
const actor = actors[actorKey];
|
const actor = actors[actorKey];
|
||||||
// Draw the box with the attached line
|
// Draw the box with the attached line
|
||||||
svgDraw.drawActor(diagram, actor, conf, false);
|
await svgDraw.drawActor(diagram, actor, conf, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
@ -563,7 +570,7 @@ export const drawActors = function (diagram, actors, actorKeys, isFooter) {
|
|||||||
if (!actor.stopy) {
|
if (!actor.stopy) {
|
||||||
actor.stopy = bounds.getVerticalPos();
|
actor.stopy = bounds.getVerticalPos();
|
||||||
}
|
}
|
||||||
const height = svgDraw.drawActor(diagram, actor, conf, true);
|
const height = await svgDraw.drawActor(diagram, actor, conf, true);
|
||||||
maxHeight = common.getMax(maxHeight, height);
|
maxHeight = common.getMax(maxHeight, height);
|
||||||
}
|
}
|
||||||
bounds.bumpVerticalPos(maxHeight + conf.boxMargin);
|
bounds.bumpVerticalPos(maxHeight + conf.boxMargin);
|
||||||
@ -746,7 +753,7 @@ function adjustCreatedDestroyedData(
|
|||||||
* @param _version - Mermaid version from package.json
|
* @param _version - Mermaid version from package.json
|
||||||
* @param diagObj - A standard diagram containing the db and the text and type etc of the diagram
|
* @param diagObj - A standard diagram containing the db and the text and type etc of the diagram
|
||||||
*/
|
*/
|
||||||
export const draw = function (_text: string, id: string, _version: string, diagObj: Diagram) {
|
export const draw = async function (_text: string, id: string, _version: string, diagObj: Diagram) {
|
||||||
const { securityLevel, sequence } = getConfig();
|
const { securityLevel, sequence } = getConfig();
|
||||||
conf = sequence;
|
conf = sequence;
|
||||||
// Handle root and Document for when rendering in sandbox mode
|
// Handle root and Document for when rendering in sandbox mode
|
||||||
@ -776,8 +783,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
const title = diagObj.db.getDiagramTitle();
|
const title = diagObj.db.getDiagramTitle();
|
||||||
const hasBoxes = diagObj.db.hasAtLeastOneBox();
|
const hasBoxes = diagObj.db.hasAtLeastOneBox();
|
||||||
const hasBoxTitles = diagObj.db.hasAtLeastOneBoxWithTitle();
|
const hasBoxTitles = diagObj.db.hasAtLeastOneBoxWithTitle();
|
||||||
const maxMessageWidthPerActor = getMaxMessageWidthPerActor(actors, messages, diagObj);
|
const maxMessageWidthPerActor = await getMaxMessageWidthPerActor(actors, messages, diagObj);
|
||||||
conf.height = calculateActorMargins(actors, maxMessageWidthPerActor, boxes);
|
conf.height = await calculateActorMargins(actors, maxMessageWidthPerActor, boxes);
|
||||||
|
|
||||||
svgDraw.insertComputerIcon(diagram);
|
svgDraw.insertComputerIcon(diagram);
|
||||||
svgDraw.insertDatabaseIcon(diagram);
|
svgDraw.insertDatabaseIcon(diagram);
|
||||||
@ -799,8 +806,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
|
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
|
await addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
|
||||||
const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
const loopWidths = await calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
|
||||||
|
|
||||||
// The arrow head definition is attached to the svg once
|
// The arrow head definition is attached to the svg once
|
||||||
svgDraw.insertArrowHead(diagram);
|
svgDraw.insertArrowHead(diagram);
|
||||||
@ -834,14 +841,15 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
let sequenceIndexStep = 1;
|
let sequenceIndexStep = 1;
|
||||||
const messagesToDraw = [];
|
const messagesToDraw = [];
|
||||||
const backgrounds = [];
|
const backgrounds = [];
|
||||||
messages.forEach(function (msg, index) {
|
let index = 0;
|
||||||
|
for (const msg of messages) {
|
||||||
let loopModel, noteModel, msgModel;
|
let loopModel, noteModel, msgModel;
|
||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case diagObj.db.LINETYPE.NOTE:
|
case diagObj.db.LINETYPE.NOTE:
|
||||||
bounds.resetVerticalPos();
|
bounds.resetVerticalPos();
|
||||||
noteModel = msg.noteModel;
|
noteModel = msg.noteModel;
|
||||||
drawNote(diagram, noteModel);
|
await drawNote(diagram, noteModel);
|
||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.ACTIVE_START:
|
case diagObj.db.LINETYPE.ACTIVE_START:
|
||||||
bounds.newActivation(msg, diagram, actors);
|
bounds.newActivation(msg, diagram, actors);
|
||||||
@ -860,7 +868,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.LOOP_END:
|
case diagObj.db.LINETYPE.LOOP_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'loop', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -886,7 +894,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.OPT_END:
|
case diagObj.db.LINETYPE.OPT_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'opt', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'opt', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -910,7 +918,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.ALT_END:
|
case diagObj.db.LINETYPE.ALT_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'alt', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'alt', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -936,7 +944,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.PAR_END:
|
case diagObj.db.LINETYPE.PAR_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'par', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'par', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -969,7 +977,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.CRITICAL_END:
|
case diagObj.db.LINETYPE.CRITICAL_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'critical', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'critical', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -984,7 +992,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
break;
|
break;
|
||||||
case diagObj.db.LINETYPE.BREAK_END:
|
case diagObj.db.LINETYPE.BREAK_END:
|
||||||
loopModel = bounds.endLoop();
|
loopModel = bounds.endLoop();
|
||||||
svgDraw.drawLoop(diagram, loopModel, 'break', conf);
|
await svgDraw.drawLoop(diagram, loopModel, 'break', conf);
|
||||||
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
|
||||||
bounds.models.addLoop(loopModel);
|
bounds.models.addLoop(loopModel);
|
||||||
break;
|
break;
|
||||||
@ -994,7 +1002,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
msgModel.starty = bounds.getVerticalPos();
|
msgModel.starty = bounds.getVerticalPos();
|
||||||
msgModel.sequenceIndex = sequenceIndex;
|
msgModel.sequenceIndex = sequenceIndex;
|
||||||
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
|
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
|
||||||
const lineStartY = boundMessage(diagram, msgModel);
|
const lineStartY = await boundMessage(diagram, msgModel);
|
||||||
adjustCreatedDestroyedData(
|
adjustCreatedDestroyedData(
|
||||||
msg,
|
msg,
|
||||||
msgModel,
|
msgModel,
|
||||||
@ -1026,20 +1034,23 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
) {
|
) {
|
||||||
sequenceIndex = sequenceIndex + sequenceIndexStep;
|
sequenceIndex = sequenceIndex + sequenceIndexStep;
|
||||||
}
|
}
|
||||||
});
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
log.debug('createdActors', createdActors);
|
log.debug('createdActors', createdActors);
|
||||||
log.debug('destroyedActors', destroyedActors);
|
log.debug('destroyedActors', destroyedActors);
|
||||||
|
await drawActors(diagram, actors, actorKeys, false);
|
||||||
|
|
||||||
drawActors(diagram, actors, actorKeys, false);
|
for (const e of messagesToDraw) {
|
||||||
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
|
await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj);
|
||||||
|
}
|
||||||
if (conf.mirrorActors) {
|
if (conf.mirrorActors) {
|
||||||
drawActors(diagram, actors, actorKeys, true);
|
await drawActors(diagram, actors, actorKeys, true);
|
||||||
}
|
}
|
||||||
backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e));
|
backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e));
|
||||||
fixLifeLineHeights(diagram, actors, actorKeys, conf);
|
fixLifeLineHeights(diagram, actors, actorKeys, conf);
|
||||||
|
|
||||||
bounds.models.boxes.forEach(function (box) {
|
for (const box of bounds.models.boxes) {
|
||||||
box.height = bounds.getVerticalPos() - box.y;
|
box.height = bounds.getVerticalPos() - box.y;
|
||||||
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
bounds.insert(box.x, box.y, box.x + box.width, box.height);
|
||||||
box.startx = box.x;
|
box.startx = box.x;
|
||||||
@ -1047,8 +1058,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
box.stopx = box.startx + box.width;
|
box.stopx = box.startx + box.width;
|
||||||
box.stopy = box.starty + box.height;
|
box.stopy = box.starty + box.height;
|
||||||
box.stroke = 'rgb(0,0,0, 0.5)';
|
box.stroke = 'rgb(0,0,0, 0.5)';
|
||||||
svgDraw.drawBox(diagram, box, conf);
|
await svgDraw.drawBox(diagram, box, conf);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (hasBoxes) {
|
if (hasBoxes) {
|
||||||
bounds.bumpVerticalPos(conf.boxMargin);
|
bounds.bumpVerticalPos(conf.boxMargin);
|
||||||
@ -1114,25 +1125,25 @@ export const draw = function (_text: string, id: string, _version: string, diagO
|
|||||||
* @param diagObj - The diagram object.
|
* @param diagObj - The diagram object.
|
||||||
* @returns The max message width of each actor.
|
* @returns The max message width of each actor.
|
||||||
*/
|
*/
|
||||||
function getMaxMessageWidthPerActor(
|
async function getMaxMessageWidthPerActor(
|
||||||
actors: { [id: string]: any },
|
actors: { [id: string]: any },
|
||||||
messages: any[],
|
messages: any[],
|
||||||
diagObj: Diagram
|
diagObj: Diagram
|
||||||
): { [id: string]: number } {
|
): Promise<{ [id: string]: number }> {
|
||||||
const maxMessageWidthPerActor = {};
|
const maxMessageWidthPerActor = {};
|
||||||
|
|
||||||
messages.forEach(function (msg) {
|
for (const msg of messages) {
|
||||||
if (actors[msg.to] && actors[msg.from]) {
|
if (actors[msg.to] && actors[msg.from]) {
|
||||||
const actor = actors[msg.to];
|
const actor = actors[msg.to];
|
||||||
|
|
||||||
// If this is the first actor, and the message is left of it, no need to calculate the margin
|
// If this is the first actor, and the message is left of it, no need to calculate the margin
|
||||||
if (msg.placement === diagObj.db.PLACEMENT.LEFTOF && !actor.prevActor) {
|
if (msg.placement === diagObj.db.PLACEMENT.LEFTOF && !actor.prevActor) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is the last actor, and the message is right of it, no need to calculate the margin
|
// If this is the last actor, and the message is right of it, no need to calculate the margin
|
||||||
if (msg.placement === diagObj.db.PLACEMENT.RIGHTOF && !actor.nextActor) {
|
if (msg.placement === diagObj.db.PLACEMENT.RIGHTOF && !actor.nextActor) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isNote = msg.placement !== undefined;
|
const isNote = msg.placement !== undefined;
|
||||||
@ -1142,7 +1153,9 @@ function getMaxMessageWidthPerActor(
|
|||||||
const wrappedMessage = msg.wrap
|
const wrappedMessage = msg.wrap
|
||||||
? utils.wrapLabel(msg.message, conf.width - 2 * conf.wrapPadding, textFont)
|
? utils.wrapLabel(msg.message, conf.width - 2 * conf.wrapPadding, textFont)
|
||||||
: msg.message;
|
: msg.message;
|
||||||
const messageDimensions = utils.calculateTextDimensions(wrappedMessage, textFont);
|
const messageDimensions = hasKatex(wrappedMessage)
|
||||||
|
? await calculateMathMLDimensions(msg.message, getConfig())
|
||||||
|
: utils.calculateTextDimensions(wrappedMessage, textFont);
|
||||||
const messageWidth = messageDimensions.width + 2 * conf.wrapPadding;
|
const messageWidth = messageDimensions.width + 2 * conf.wrapPadding;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1207,7 +1220,7 @@ function getMaxMessageWidthPerActor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
log.debug('maxMessageWidthPerActor:', maxMessageWidthPerActor);
|
log.debug('maxMessageWidthPerActor:', maxMessageWidthPerActor);
|
||||||
return maxMessageWidthPerActor;
|
return maxMessageWidthPerActor;
|
||||||
@ -1238,13 +1251,13 @@ const getRequiredPopupWidth = function (actor) {
|
|||||||
* @param actorToMessageWidth - A map of actor key → max message width it holds
|
* @param actorToMessageWidth - A map of actor key → max message width it holds
|
||||||
* @param boxes - The boxes around the actors if any
|
* @param boxes - The boxes around the actors if any
|
||||||
*/
|
*/
|
||||||
function calculateActorMargins(
|
async function calculateActorMargins(
|
||||||
actors: { [id: string]: any },
|
actors: { [id: string]: any },
|
||||||
actorToMessageWidth: ReturnType<typeof getMaxMessageWidthPerActor>,
|
actorToMessageWidth: Awaited<ReturnType<typeof getMaxMessageWidthPerActor>>,
|
||||||
boxes
|
boxes
|
||||||
) {
|
) {
|
||||||
let maxHeight = 0;
|
let maxHeight = 0;
|
||||||
Object.keys(actors).forEach((prop) => {
|
for (const prop of Object.keys(actors)) {
|
||||||
const actor = actors[prop];
|
const actor = actors[prop];
|
||||||
if (actor.wrap) {
|
if (actor.wrap) {
|
||||||
actor.description = utils.wrapLabel(
|
actor.description = utils.wrapLabel(
|
||||||
@ -1253,14 +1266,17 @@ function calculateActorMargins(
|
|||||||
actorFont(conf)
|
actorFont(conf)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const actDims = utils.calculateTextDimensions(actor.description, actorFont(conf));
|
const actDims = hasKatex(actor.description)
|
||||||
|
? await calculateMathMLDimensions(actor.description, getConfig())
|
||||||
|
: utils.calculateTextDimensions(actor.description, actorFont(conf));
|
||||||
|
|
||||||
actor.width = actor.wrap
|
actor.width = actor.wrap
|
||||||
? conf.width
|
? conf.width
|
||||||
: common.getMax(conf.width, actDims.width + 2 * conf.wrapPadding);
|
: common.getMax(conf.width, actDims.width + 2 * conf.wrapPadding);
|
||||||
|
|
||||||
actor.height = actor.wrap ? common.getMax(actDims.height, conf.height) : conf.height;
|
actor.height = actor.wrap ? common.getMax(actDims.height, conf.height) : conf.height;
|
||||||
maxHeight = common.getMax(maxHeight, actor.height);
|
maxHeight = common.getMax(maxHeight, actor.height);
|
||||||
});
|
}
|
||||||
|
|
||||||
for (const actorKey in actorToMessageWidth) {
|
for (const actorKey in actorToMessageWidth) {
|
||||||
const actor = actors[actorKey];
|
const actor = actors[actorKey];
|
||||||
@ -1311,15 +1327,17 @@ function calculateActorMargins(
|
|||||||
return common.getMax(maxHeight, conf.height);
|
return common.getMax(maxHeight, conf.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
const buildNoteModel = function (msg, actors, diagObj) {
|
const buildNoteModel = async function (msg, actors, diagObj) {
|
||||||
const startx = actors[msg.from].x;
|
const startx = actors[msg.from].x;
|
||||||
const stopx = actors[msg.to].x;
|
const stopx = actors[msg.to].x;
|
||||||
const shouldWrap = msg.wrap && msg.message;
|
const shouldWrap = msg.wrap && msg.message;
|
||||||
|
|
||||||
let textDimensions = utils.calculateTextDimensions(
|
let textDimensions: { width: number; height: number; lineHeight?: number } = hasKatex(msg.message)
|
||||||
shouldWrap ? utils.wrapLabel(msg.message, conf.width, noteFont(conf)) : msg.message,
|
? await calculateMathMLDimensions(msg.message, getConfig())
|
||||||
noteFont(conf)
|
: utils.calculateTextDimensions(
|
||||||
);
|
shouldWrap ? utils.wrapLabel(msg.message, conf.width, noteFont(conf)) : msg.message,
|
||||||
|
noteFont(conf)
|
||||||
|
);
|
||||||
const noteModel = {
|
const noteModel = {
|
||||||
width: shouldWrap
|
width: shouldWrap
|
||||||
? conf.width
|
? conf.width
|
||||||
@ -1477,12 +1495,12 @@ const buildMessageModel = function (msg, actors, diagObj) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagObj) {
|
const calculateLoopBounds = async function (messages, actors, _maxWidthPerActor, diagObj) {
|
||||||
const loops = {};
|
const loops = {};
|
||||||
const stack = [];
|
const stack = [];
|
||||||
let current, noteModel, msgModel;
|
let current, noteModel, msgModel;
|
||||||
|
|
||||||
messages.forEach(function (msg) {
|
for (const msg of messages) {
|
||||||
msg.id = utils.random({ length: 10 });
|
msg.id = utils.random({ length: 10 });
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case diagObj.db.LINETYPE.LOOP_START:
|
case diagObj.db.LINETYPE.LOOP_START:
|
||||||
@ -1545,7 +1563,7 @@ const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagO
|
|||||||
}
|
}
|
||||||
const isNote = msg.placement !== undefined;
|
const isNote = msg.placement !== undefined;
|
||||||
if (isNote) {
|
if (isNote) {
|
||||||
noteModel = buildNoteModel(msg, actors, diagObj);
|
noteModel = await buildNoteModel(msg, actors, diagObj);
|
||||||
msg.noteModel = noteModel;
|
msg.noteModel = noteModel;
|
||||||
stack.forEach((stk) => {
|
stack.forEach((stk) => {
|
||||||
current = stk;
|
current = stk;
|
||||||
@ -1584,7 +1602,7 @@ const calculateLoopBounds = function (messages, actors, _maxWidthPerActor, diagO
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
bounds.activations = [];
|
bounds.activations = [];
|
||||||
log.debug('Loop type widths:', loops);
|
log.debug('Loop type widths:', loops);
|
||||||
return loops;
|
return loops;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import common from '../common/common.js';
|
import common, { calculateMathMLDimensions, hasKatex, renderKatex } from '../common/common.js';
|
||||||
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
import * as svgDrawCommon from '../common/svgDrawCommon.js';
|
||||||
import { addFunction } from '../../interactionDb.js';
|
import { addFunction } from '../../interactionDb.js';
|
||||||
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
|
||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
|
import * as configApi from '../../config.js';
|
||||||
|
|
||||||
export const ACTOR_TYPE_WIDTH = 18 * 2;
|
export const ACTOR_TYPE_WIDTH = 18 * 2;
|
||||||
const TOP_ACTOR_CLASS = 'actor-top';
|
const TOP_ACTOR_CLASS = 'actor-top';
|
||||||
@ -83,6 +84,47 @@ const popupMenuToggle = function (popid) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const drawKatex = async function (elem, textData, msgModel = null) {
|
||||||
|
let textElem = elem.append('foreignObject');
|
||||||
|
const lines = await renderKatex(textData.text, configApi.getConfig());
|
||||||
|
|
||||||
|
const divElem = textElem
|
||||||
|
.append('xhtml:div')
|
||||||
|
.attr('style', 'width: fit-content;')
|
||||||
|
.attr('xmlns', 'http://www.w3.org/1999/xhtml')
|
||||||
|
.html(lines);
|
||||||
|
const dim = divElem.node().getBoundingClientRect();
|
||||||
|
|
||||||
|
textElem.attr('height', Math.round(dim.height)).attr('width', Math.round(dim.width));
|
||||||
|
|
||||||
|
if (textData.class === 'noteText') {
|
||||||
|
const rectElem = elem.node().firstChild;
|
||||||
|
|
||||||
|
rectElem.setAttribute('height', dim.height + 2 * textData.textMargin);
|
||||||
|
const rectDim = rectElem.getBBox();
|
||||||
|
|
||||||
|
textElem
|
||||||
|
.attr('x', Math.round(rectDim.x + rectDim.width / 2 - dim.width / 2))
|
||||||
|
.attr('y', Math.round(rectDim.y + rectDim.height / 2 - dim.height / 2));
|
||||||
|
} else if (msgModel) {
|
||||||
|
let { startx, stopx, starty } = msgModel;
|
||||||
|
if (startx > stopx) {
|
||||||
|
const temp = startx;
|
||||||
|
startx = stopx;
|
||||||
|
stopx = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
textElem.attr('x', Math.round(startx + Math.abs(startx - stopx) / 2 - dim.width / 2));
|
||||||
|
if (textData.class === 'loopText') {
|
||||||
|
textElem.attr('y', Math.round(starty));
|
||||||
|
} else {
|
||||||
|
textElem.attr('y', Math.round(starty - dim.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [textElem];
|
||||||
|
};
|
||||||
|
|
||||||
export const drawText = function (elem, textData) {
|
export const drawText = function (elem, textData) {
|
||||||
let prevTextHeight = 0;
|
let prevTextHeight = 0;
|
||||||
let textHeight = 0;
|
let textHeight = 0;
|
||||||
@ -282,7 +324,7 @@ export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => {
|
|||||||
* @param {any} conf - DrawText implementation discriminator object
|
* @param {any} conf - DrawText implementation discriminator object
|
||||||
* @param {boolean} isFooter - If the actor is the footer one
|
* @param {boolean} isFooter - If the actor is the footer one
|
||||||
*/
|
*/
|
||||||
const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
const drawActorTypeParticipant = async function (elem, actor, conf, isFooter) {
|
||||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
const center = actor.x + actor.width / 2;
|
const center = actor.x + actor.width / 2;
|
||||||
const centerY = actorY + 5;
|
const centerY = actorY + 5;
|
||||||
@ -346,7 +388,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_drawTextCandidateFunc(conf)(
|
await _drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
actor.description,
|
actor.description,
|
||||||
g,
|
g,
|
||||||
rect.x,
|
rect.x,
|
||||||
@ -367,7 +409,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
|
|||||||
return height;
|
return height;
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
const drawActorTypeActor = async function (elem, actor, conf, isFooter) {
|
||||||
const actorY = isFooter ? actor.stopy : actor.starty;
|
const actorY = isFooter ? actor.stopy : actor.starty;
|
||||||
const center = actor.x + actor.width / 2;
|
const center = actor.x + actor.width / 2;
|
||||||
const centerY = actorY + 80;
|
const centerY = actorY + 80;
|
||||||
@ -448,7 +490,7 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
|||||||
const bounds = actElem.node().getBBox();
|
const bounds = actElem.node().getBBox();
|
||||||
actor.height = bounds.height;
|
actor.height = bounds.height;
|
||||||
|
|
||||||
_drawTextCandidateFunc(conf)(
|
await _drawTextCandidateFunc(conf, hasKatex(actor.description))(
|
||||||
actor.description,
|
actor.description,
|
||||||
actElem,
|
actElem,
|
||||||
rect.x,
|
rect.x,
|
||||||
@ -462,21 +504,21 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
|
|||||||
return actor.height;
|
return actor.height;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawActor = function (elem, actor, conf, isFooter) {
|
export const drawActor = async function (elem, actor, conf, isFooter) {
|
||||||
switch (actor.type) {
|
switch (actor.type) {
|
||||||
case 'actor':
|
case 'actor':
|
||||||
return drawActorTypeActor(elem, actor, conf, isFooter);
|
return await drawActorTypeActor(elem, actor, conf, isFooter);
|
||||||
case 'participant':
|
case 'participant':
|
||||||
return drawActorTypeParticipant(elem, actor, conf, isFooter);
|
return await drawActorTypeParticipant(elem, actor, conf, isFooter);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const drawBox = function (elem, box, conf) {
|
export const drawBox = async function (elem, box, conf) {
|
||||||
const boxplustextGroup = elem.append('g');
|
const boxplustextGroup = elem.append('g');
|
||||||
const g = boxplustextGroup;
|
const g = boxplustextGroup;
|
||||||
drawBackgroundRect(g, box);
|
drawBackgroundRect(g, box);
|
||||||
if (box.name) {
|
if (box.name) {
|
||||||
_drawTextCandidateFunc(conf)(
|
await _drawTextCandidateFunc(conf)(
|
||||||
box.name,
|
box.name,
|
||||||
g,
|
g,
|
||||||
box.x,
|
box.x,
|
||||||
@ -523,7 +565,7 @@ export const drawActivation = function (elem, bounds, verticalPos, conf, actorAc
|
|||||||
* @param {any} conf - Diagram configuration
|
* @param {any} conf - Diagram configuration
|
||||||
* @returns {any}
|
* @returns {any}
|
||||||
*/
|
*/
|
||||||
export const drawLoop = function (elem, loopModel, labelText, conf) {
|
export const drawLoop = async function (elem, loopModel, labelText, conf) {
|
||||||
const {
|
const {
|
||||||
boxMargin,
|
boxMargin,
|
||||||
boxTextMargin,
|
boxTextMargin,
|
||||||
@ -585,10 +627,10 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
|||||||
txt.fontWeight = fontWeight;
|
txt.fontWeight = fontWeight;
|
||||||
txt.wrap = true;
|
txt.wrap = true;
|
||||||
|
|
||||||
let textElem = drawText(g, txt);
|
let textElem = hasKatex(txt.text) ? await drawKatex(g, txt, loopModel) : drawText(g, txt);
|
||||||
|
|
||||||
if (loopModel.sectionTitles !== undefined) {
|
if (loopModel.sectionTitles !== undefined) {
|
||||||
loopModel.sectionTitles.forEach(function (item, idx) {
|
for (const [idx, item] of Object.entries(loopModel.sectionTitles)) {
|
||||||
if (item.message) {
|
if (item.message) {
|
||||||
txt.text = item.message;
|
txt.text = item.message;
|
||||||
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
|
txt.x = loopModel.startx + (loopModel.stopx - loopModel.startx) / 2;
|
||||||
@ -601,7 +643,13 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
|||||||
txt.fontSize = fontSize;
|
txt.fontSize = fontSize;
|
||||||
txt.fontWeight = fontWeight;
|
txt.fontWeight = fontWeight;
|
||||||
txt.wrap = loopModel.wrap;
|
txt.wrap = loopModel.wrap;
|
||||||
textElem = drawText(g, txt);
|
|
||||||
|
if (hasKatex(txt.text)) {
|
||||||
|
loopModel.starty = loopModel.sections[idx].y;
|
||||||
|
await drawKatex(g, txt, loopModel);
|
||||||
|
} else {
|
||||||
|
drawText(g, txt);
|
||||||
|
}
|
||||||
let sectionHeight = Math.round(
|
let sectionHeight = Math.round(
|
||||||
textElem
|
textElem
|
||||||
.map((te) => (te._groups || te)[0][0].getBBox().height)
|
.map((te) => (te._groups || te)[0][0].getBBox().height)
|
||||||
@ -609,7 +657,7 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
|||||||
);
|
);
|
||||||
loopModel.sections[idx].height += sectionHeight - (boxMargin + boxTextMargin);
|
loopModel.sections[idx].height += sectionHeight - (boxMargin + boxTextMargin);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loopModel.height = Math.round(loopModel.stopy - loopModel.starty);
|
loopModel.height = Math.round(loopModel.stopy - loopModel.starty);
|
||||||
@ -886,6 +934,41 @@ const _drawTextCandidateFunc = (function () {
|
|||||||
_setTextAttrs(text, textAttrs);
|
_setTextAttrs(text, textAttrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param content
|
||||||
|
* @param g
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
* @param textAttrs
|
||||||
|
* @param conf
|
||||||
|
*/
|
||||||
|
async function byKatex(content, g, x, y, width, height, textAttrs, conf) {
|
||||||
|
// TODO duplicate render calls, optimize
|
||||||
|
|
||||||
|
const dim = await calculateMathMLDimensions(content, configApi.getConfig());
|
||||||
|
const s = g.append('switch');
|
||||||
|
const f = s
|
||||||
|
.append('foreignObject')
|
||||||
|
.attr('x', x + width / 2 - dim.width / 2)
|
||||||
|
.attr('y', y + height / 2 - dim.height / 2)
|
||||||
|
.attr('width', dim.width)
|
||||||
|
.attr('height', dim.height);
|
||||||
|
|
||||||
|
const text = f.append('xhtml:div').style('height', '100%').style('width', '100%');
|
||||||
|
|
||||||
|
text
|
||||||
|
.append('div')
|
||||||
|
.style('text-align', 'center')
|
||||||
|
.style('vertical-align', 'middle')
|
||||||
|
.html(await renderKatex(content, configApi.getConfig()));
|
||||||
|
|
||||||
|
byTspan(content, s, x, y, width, height, textAttrs, conf);
|
||||||
|
_setTextAttrs(text, textAttrs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {any} toText
|
* @param {any} toText
|
||||||
* @param {any} fromTextAttrsDict
|
* @param {any} fromTextAttrsDict
|
||||||
@ -898,7 +981,10 @@ const _drawTextCandidateFunc = (function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (conf) {
|
return function (conf, hasKatex = false) {
|
||||||
|
if (hasKatex) {
|
||||||
|
return byKatex;
|
||||||
|
}
|
||||||
return conf.textPlacement === 'fo' ? byFo : conf.textPlacement === 'old' ? byText : byTspan;
|
return conf.textPlacement === 'fo' ? byFo : conf.textPlacement === 'old' ? byText : byTspan;
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
@ -172,6 +172,7 @@ function sidebarConfig() {
|
|||||||
{ text: 'Mermaid Configuration Options', link: '/config/schema-docs/config' },
|
{ text: 'Mermaid Configuration Options', link: '/config/schema-docs/config' },
|
||||||
{ text: 'Directives', link: '/config/directives' },
|
{ text: 'Directives', link: '/config/directives' },
|
||||||
{ text: 'Theming', link: '/config/theming' },
|
{ text: 'Theming', link: '/config/theming' },
|
||||||
|
{ text: 'Math', link: '/config/math' },
|
||||||
{ text: 'Accessibility', link: '/config/accessibility' },
|
{ text: 'Accessibility', link: '/config/accessibility' },
|
||||||
{ text: 'Mermaid CLI', link: '/config/mermaidCLI' },
|
{ text: 'Mermaid CLI', link: '/config/mermaidCLI' },
|
||||||
{ text: 'FAQ', link: '/config/faq' },
|
{ text: 'FAQ', link: '/config/faq' },
|
||||||
|
62
packages/mermaid/src/docs/config/math.md
Normal file
62
packages/mermaid/src/docs/config/math.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Math Configuration (v<MERMAID_RELEASE_VERSION>+)
|
||||||
|
|
||||||
|
Mermaid supports rendering mathematical expressions through the [KaTeX](https://katex.org/) typesetter.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To render math within a diagram, surround the mathematical expression with the `$$` delimiter.
|
||||||
|
|
||||||
|
Note that at the moment, the only supported diagrams are below:
|
||||||
|
|
||||||
|
### Flowcharts
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph LR
|
||||||
|
A["$$x^2$$"] -->|"$$\sqrt{x+3}$$"| B("$$\frac{1}{2}$$")
|
||||||
|
A -->|"$$\overbrace{a+b+c}^{\text{note}}$$"| C("$$\pi r^2$$")
|
||||||
|
B --> D("$$x = \begin{cases} a &\text{if } b \\ c &\text{if } d \end{cases}$$")
|
||||||
|
C --> E("$$x(t)=c_1\begin{bmatrix}-\cos{t}+\sin{t}\\ 2\cos{t} \end{bmatrix}e^{2t}$$")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sequence
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
autonumber
|
||||||
|
participant 1 as $$\alpha$$
|
||||||
|
participant 2 as $$\beta$$
|
||||||
|
1->>2: Solve: $$\sqrt{2+2}$$
|
||||||
|
2-->>1: Answer: $$2$$
|
||||||
|
Note right of 2: $$\sqrt{2+2}=\sqrt{4}=2$$
|
||||||
|
```
|
||||||
|
|
||||||
|
## Legacy Support
|
||||||
|
|
||||||
|
By default, MathML is used for rendering mathematical expressions. If you have users on [unsupported browsers](https://caniuse.com/?search=mathml), `legacyMathML` can be set in the config to fall back to CSS rendering. Note that **you must provide KaTeX's stylesheets on your own** as they do not come bundled with Mermaid.
|
||||||
|
|
||||||
|
Example with legacy mode enabled (the latest version of KaTeX's stylesheet can be found on their [docs](https://katex.org/docs/browser.html)):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- Please ensure the stylesheet's version matches with the KaTeX version in your package-lock -->
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/katex@{version_number}/dist/katex.min.css"
|
||||||
|
integrity="sha384-{hash}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
mermaid.initialize({
|
||||||
|
legacyMathML: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
@ -168,6 +168,14 @@ properties:
|
|||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
uniqueItems: false # Should be enabled, but it may be a breaking change from the old config
|
uniqueItems: false # Should be enabled, but it may be a breaking change from the old config
|
||||||
|
legacyMathML:
|
||||||
|
description: |
|
||||||
|
This option specifies if Mermaid can expect the dependent to include KaTeX stylesheets for browsers
|
||||||
|
without their own MathML implementation. If this option is disabled and MathML is not supported, the math
|
||||||
|
equations are replaced with a warning. If this option is enabled and MathML is not supported, Mermaid will
|
||||||
|
fall back to legacy rendering for KaTeX.
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
deterministicIds:
|
deterministicIds:
|
||||||
description: |
|
description: |
|
||||||
This option controls if the generated ids of nodes in the SVG are
|
This option controls if the generated ids of nodes in the SVG are
|
||||||
|
28
pnpm-lock.yaml
generated
28
pnpm-lock.yaml
generated
@ -229,6 +229,9 @@ importers:
|
|||||||
elkjs:
|
elkjs:
|
||||||
specifier: ^0.9.0
|
specifier: ^0.9.0
|
||||||
version: 0.9.1
|
version: 0.9.1
|
||||||
|
katex:
|
||||||
|
specifier: ^0.16.9
|
||||||
|
version: 0.16.9
|
||||||
khroma:
|
khroma:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
@ -252,7 +255,7 @@ importers:
|
|||||||
version: 9.0.0
|
version: 9.0.0
|
||||||
web-worker:
|
web-worker:
|
||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.3.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@adobe/jsonschema2md':
|
'@adobe/jsonschema2md':
|
||||||
specifier: ^7.1.4
|
specifier: ^7.1.4
|
||||||
@ -278,6 +281,9 @@ importers:
|
|||||||
'@types/jsdom':
|
'@types/jsdom':
|
||||||
specifier: ^21.1.1
|
specifier: ^21.1.1
|
||||||
version: 21.1.1
|
version: 21.1.1
|
||||||
|
'@types/katex':
|
||||||
|
specifier: ^0.16.7
|
||||||
|
version: 0.16.7
|
||||||
'@types/lodash-es':
|
'@types/lodash-es':
|
||||||
specifier: ^4.17.7
|
specifier: ^4.17.7
|
||||||
version: 4.17.7
|
version: 4.17.7
|
||||||
@ -4897,6 +4903,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/katex@0.16.7:
|
||||||
|
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/keyv@3.1.4:
|
/@types/keyv@3.1.4:
|
||||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -7436,6 +7446,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
|
/commander@8.3.0:
|
||||||
|
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/comment-json@4.2.3:
|
/comment-json@4.2.3:
|
||||||
resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==}
|
resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@ -11843,6 +11858,13 @@ packages:
|
|||||||
engines: {node: '>=12.20'}
|
engines: {node: '>=12.20'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/katex@0.16.9:
|
||||||
|
resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
commander: 8.3.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/keyv@4.5.3:
|
/keyv@4.5.3:
|
||||||
resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==}
|
resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -16522,8 +16544,8 @@ packages:
|
|||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/web-worker@1.2.0:
|
/web-worker@1.3.0:
|
||||||
resolution: {integrity: sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==}
|
resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/webdriver@7.31.1(typescript@5.1.6):
|
/webdriver@7.31.1(typescript@5.1.6):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user