Merge branch 'develop' into UpdateClassMemberHandling

This commit is contained in:
Justin Greywolf
2023-07-02 17:45:30 -07:00
56 changed files with 2349 additions and 299 deletions

View File

@@ -1,6 +1,15 @@
codecov:
branch: develop
comment:
layout: 'reach, diff, flags, files'
behavior: default
require_changes: false # if true: only post the comment if coverage changes
require_base: no # [yes :: must have a base report to post]
require_head: yes # [yes :: must have a head report to post]
coverage:
status:
project:
default:
threshold: 2%

View File

@@ -2,13 +2,13 @@ name: Build Vitepress docs
on:
pull_request:
merge_group:
permissions:
contents: read
jobs:
# Build job
build:
build-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout

View File

@@ -2,6 +2,7 @@ name: Build
on:
push: {}
merge_group:
pull_request:
types:
- opened
@@ -12,7 +13,7 @@ permissions:
contents: read
jobs:
build:
build-mermaid:
runs-on: ubuntu-latest
strategy:
matrix:

View File

@@ -14,7 +14,7 @@ permissions:
contents: read
jobs:
check:
check-readme:
runs-on: ubuntu-latest
steps:
- name: Checkout repository

View File

@@ -1,15 +1,16 @@
on:
push: {}
push:
merge_group:
pull_request:
types:
- opened
- synchronize
- ready_for_review
name: Static analysis
name: Static analysis on Test files
jobs:
test:
check-tests:
runs-on: ubuntu-latest
name: check tests
if: github.repository_owner == 'mermaid-js'

View File

@@ -19,7 +19,7 @@ env:
USE_APPLI: ${{ secrets.APPLITOOLS_API_KEY && 'true' || '' }}
jobs:
test:
e2e-applitools:
runs-on: ubuntu-latest
strategy:
matrix:

View File

@@ -1,12 +1,15 @@
name: E2E
on: [push, pull_request]
on:
push:
pull_request:
merge_group:
permissions:
contents: read
jobs:
build:
e2e:
runs-on: ubuntu-latest
strategy:
fail-fast: false
@@ -49,7 +52,7 @@ jobs:
files: coverage/cypress/lcov.info
flags: e2e
name: mermaid-codecov
fail_ci_if_error: true
fail_ci_if_error: false
verbose: true
- name: Upload Artifacts
uses: actions/upload-artifact@v3

View File

@@ -1,7 +1,8 @@
name: Lint
on:
push: {}
push:
merge_group:
pull_request:
types:
- opened

View File

@@ -19,7 +19,7 @@ concurrency:
jobs:
# Build job
build:
build-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -48,11 +48,11 @@ jobs:
path: packages/mermaid/src/vitepress/.vitepress/dist
# Deployment job
deploy:
deploy-docs:
environment:
name: github-pages
runs-on: ubuntu-latest
needs: build
needs: build-docs
steps:
- name: Deploy to GitHub Pages
id: deployment

View File

@@ -6,7 +6,7 @@ on:
- 'release/**'
jobs:
publish:
publish-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

View File

@@ -1,12 +1,12 @@
name: Unit Tests
on: [push, pull_request]
on: [push, pull_request, merge_group]
permissions:
contents: read
jobs:
build:
unit-test:
runs-on: ubuntu-latest
strategy:
matrix:
@@ -47,11 +47,5 @@ jobs:
files: ./coverage/vitest/lcov.info
flags: unit
name: mermaid-codecov
fail_ci_if_error: true
fail_ci_if_error: false
verbose: true
# Coveralls is throwing 500. Disabled for now.
# - name: Upload Coverage to Coveralls
# uses: coverallsapp/github-action@v2
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# flag-name: unit

View File

@@ -5,7 +5,7 @@ on:
workflow_dispatch:
jobs:
build:
update-browser-list:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

View File

@@ -0,0 +1,13 @@
/**
* Mocked Sankey diagram renderer
*/
import { vi } from 'vitest';
export const draw = vi.fn().mockImplementation(() => {
return '';
});
export default {
draw,
};

View File

@@ -109,6 +109,7 @@
"rehype",
"roledescription",
"sandboxed",
"sankey",
"setupgraphviewbox",
"shiki",
"sidharth",

View File

@@ -0,0 +1,144 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('Sankey Diagram', () => {
it('should render a simple example', () => {
imgSnapshotTest(
`
sankey-beta
sourceNode,targetNode,10
`,
{}
);
});
describe('when given a linkColor', function () {
this.beforeAll(() => {
cy.wrap(
`sankey-beta
a,b,10
`
).as('graph');
});
it('links should use hex color', function () {
renderGraph(this.graph, { sankey: { linkColor: '#636465' } });
cy.get('.link path').should((link) => {
expect(link.attr('stroke')).to.equal('#636465');
});
});
it('links should be the same color as source node', function () {
renderGraph(this.graph, { sankey: { linkColor: 'source' } });
cy.get('.link path').then((link) => {
cy.get('.node[id="node-1"] rect').should((node) =>
expect(link.attr('stroke')).to.equal(node.attr('fill'))
);
});
});
it('links should be the same color as target node', function () {
renderGraph(this.graph, { sankey: { linkColor: 'target' } });
cy.get('.link path').then((link) => {
cy.get('.node[id="node-2"] rect').should((node) =>
expect(link.attr('stroke')).to.equal(node.attr('fill'))
);
});
});
it('links must be gradient', function () {
renderGraph(this.graph, { sankey: { linkColor: 'gradient' } });
cy.get('.link path').should((link) => {
expect(link.attr('stroke')).to.equal('url(#linearGradient-3)');
});
});
});
describe('when given a nodeAlignment', function () {
this.beforeAll(() => {
cy.wrap(
`
sankey-beta
a,b,8
b,c,8
c,d,8
d,e,8
x,c,4
c,y,4
`
).as('graph');
});
this.afterEach(() => {
cy.get('.node[id="node-1"]').should((node) => {
expect(node.attr('x')).to.equal('0');
});
cy.get('.node[id="node-2"]').should((node) => {
expect(node.attr('x')).to.equal('100');
});
cy.get('.node[id="node-3"]').should((node) => {
expect(node.attr('x')).to.equal('200');
});
cy.get('.node[id="node-4"]').should((node) => {
expect(node.attr('x')).to.equal('300');
});
cy.get('.node[id="node-5"]').should((node) => {
expect(node.attr('x')).to.equal('400');
});
});
it('should justify nodes', function () {
renderGraph(this.graph, {
sankey: { nodeAlignment: 'justify', width: 410, useMaxWidth: false },
});
cy.get('.node[id="node-6"]').should((node) => {
expect(node.attr('x')).to.equal('0');
});
cy.get('.node[id="node-7"]').should((node) => {
expect(node.attr('x')).to.equal('400');
});
});
it('should align nodes left', function () {
renderGraph(this.graph, {
sankey: { nodeAlignment: 'left', width: 410, useMaxWidth: false },
});
cy.get('.node[id="node-6"]').should((node) => {
expect(node.attr('x')).to.equal('0');
});
cy.get('.node[id="node-7"]').should((node) => {
expect(node.attr('x')).to.equal('300');
});
});
it('should align nodes right', function () {
renderGraph(this.graph, {
sankey: { nodeAlignment: 'right', width: 410, useMaxWidth: false },
});
cy.get('.node[id="node-6"]').should((node) => {
expect(node.attr('x')).to.equal('100');
});
cy.get('.node[id="node-7"]').should((node) => {
expect(node.attr('x')).to.equal('400');
});
});
it('should center nodes', function () {
renderGraph(this.graph, {
sankey: { nodeAlignment: 'center', width: 410, useMaxWidth: false },
});
cy.get('.node[id="node-6"]').should((node) => {
expect(node.attr('x')).to.equal('100');
});
cy.get('.node[id="node-7"]').should((node) => {
expect(node.attr('x')).to.equal('300');
});
});
});
});

View File

@@ -156,6 +156,81 @@ context('Sequence diagram', () => {
`
);
});
it('should render a sequence diagram with basic actor creation and destruction', () => {
imgSnapshotTest(
`
sequenceDiagram
Alice ->> Bob: Hello Bob, how are you ?
Bob ->> Alice: Fine, thank you. And you?
create participant Polo
Alice ->> Polo: Hi Polo!
create actor Ola1 as Ola
Polo ->> Ola1: Hiii
Ola1 ->> Alice: Hi too
destroy Ola1
Alice --x Ola1: Bye!
Alice ->> Bob: And now?
create participant Ola2 as Ola
Alice ->> Ola2: Hello again
destroy Alice
Alice --x Ola2: Bye for me!
destroy Bob
Ola2 --> Bob: The end
`
);
});
it('should render a sequence diagram with actor creation and destruction coupled with backgrounds, loops and notes', () => {
imgSnapshotTest(
`
sequenceDiagram
accTitle: test the accTitle
accDescr: Test a description
participant Alice
participant Bob
autonumber 10 10
rect rgb(200, 220, 100)
rect rgb(200, 255, 200)
Alice ->> Bob: Hello Bob, how are you?
create participant John as John<br />Second Line
Bob-->>John: How about you John?
end
Bob--x Alice: 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.
Bob-->Alice: Checking with John...
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
Note over John:nowrap: John's trying hard not to break his train of thought.
destroy John
Bob-x John: John! Cmon!
Note over John: After a few more moments, John<br />finally snaps out of it.
end
autonumber off
alt either this
create actor Lola
Alice->>+Lola: Yes
Lola-->>-Alice: OK
else or this
autonumber
Alice->>Lola: No
else or this will happen
Alice->Lola: Maybe
end
autonumber 200
par this happens in parallel
destroy Bob
Alice -->> Bob: Parallel message 1
and
Alice -->> Lola: Parallel message 2
end
`
);
});
context('font settings', () => {
it('should render different note fonts when configured', () => {
imgSnapshotTest(

View File

@@ -75,6 +75,9 @@
<li>
<h2><a href="./zenuml.html">ZenUML</a></h2>
</li>
<li>
<h2><a href="./sankey.html">Sankey</a></h2>
</li>
</ul>
</body>
</html>

108
demos/sankey.html Normal file
View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>States Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<h1>Sankey diagram demos</h1>
<h2>Energy flow</h2>
<pre class="mermaid">
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({
theme: 'default',
logLevel: 3,
securityLevel: 'loose',
sankey: {
title: 'Hey, this is Sankey-Beta',
width: 1200,
height: 600,
linkColor: 'gradient',
nodeAlignment: 'justify',
},
});
</script>
</body>
</html>

View File

@@ -1,9 +1,36 @@
version: '3.9'
services:
mermaid:
image: node:20.3.1-alpine3.18
image: node:18.16.1-alpine3.18
stdin_open: true
tty: true
working_dir: /mermaid
mem_limit: '2G'
environment:
- NODE_OPTIONS=--max_old_space_size=2048
volumes:
- ./:/mermaid
- root_cache:/root/.cache
- root_local:/root/.local
- root_npm:/root/.npm
ports:
- 9000:9000
- 3333:3333
cypress:
image: cypress/included:12.16.0
stdin_open: true
tty: true
working_dir: /mermaid
mem_limit: '2G'
entrypoint: cypress
environment:
- DISPLAY
volumes:
- ./:/mermaid
- /tmp/.X11-unix:/tmp/.X11-unix
network_mode: host
volumes:
root_cache:
root_local:
root_npm:

View File

@@ -14,7 +14,7 @@
#### Defined in
[defaultConfig.ts:2293](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2293)
[defaultConfig.ts:2300](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2300)
---

View File

@@ -73,9 +73,9 @@ To make a custom theme, modify `themeVariables` via `init`.
You will need to use the [base](#available-themes) theme as it is the only modifiable theme.
| Parameter | Description | Type | Properties |
| -------------- | ------------------------------------ | ------ | --------------------------------------------------------------------------------------------------- |
| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables-reference-table)) |
| Parameter | Description | Type | Properties |
| -------------- | ------------------------------------ | ------ | ----------------------------------------------------------------------------------- |
| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables)) |
Example of modifying `themeVariables` using the `init` directive:

View File

@@ -6,8 +6,8 @@
# Announcements
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
8 June 2023 · 7 mins
15 June 2023 · 12 mins
A quadrant chart is a useful diagram that helps users visualize data and identify patterns in a data set.
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.

View File

@@ -6,6 +6,12 @@
# Blog
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
15 June 2023 · 12 mins
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
8 June 2023 · 7 mins

510
docs/syntax/sankey.md Normal file
View File

@@ -0,0 +1,510 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/sankey.md](../../packages/mermaid/src/docs/syntax/sankey.md).
# Sankey diagrams
> A sankey diagram is a visualization used to depict a flow from one set of values to another.
::: warning
This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future.
:::
The things being connected are called nodes and the connections are called links.
## Example
This example taken from [observable](https://observablehq.com/@d3/sankey/2?collection=@d3/d3-sankey). It may be rendered a little bit differently, though, in terms of size and colors.
```mermaid-example
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
```mermaid
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
::: details
```mermaid-example
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
```mermaid
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
:::
## Syntax
The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result.
It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**:
- CSV must contain **3 columns only**
- It is **allowed** to have **empty lines** without comma separators for visual purposes
### Basic
It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly:
```mermaid-example
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
```mermaid
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
```mermaid-example
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
```mermaid
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
### Empty Lines
CSV does not support empty lines without comma delimeters by default. But you can add them if needed:
```mermaid-example
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
```mermaid
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
```mermaid-example
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
```mermaid
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
### Commas
If you need to have a comma, wrap it in double quotes:
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
### Double Quotes
If you need to have double quote, put a pair of them inside quoted string:
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
## Configuration
You can customize link colors, node alignments and diagram dimensions.
```html
<script>
const config = {
startOnLoad: true,
securityLevel: 'loose',
sankey: {
width: 800,
height: 400,
linkColor: 'source',
nodeAlignment: 'left',
},
};
mermaid.initialize(config);
</script>
```
### Links Coloring
You can adjust links' color by setting `linkColor` to one of those:
- `source` - link will be of a source node color
- `target` - link will be of a target node color
- `gradient` - link color will be smoothly transient between source and target node colors
- hex code of color, like `#a1a1a1`
### Node Alignment
Graph layout can be changed by setting `nodeAlignment` to:
- `justify`
- `center`
- `left`
- `right`

View File

@@ -94,6 +94,43 @@ sequenceDiagram
J->>A: Great!
```
### Actor Creation and Destruction
It is possible to create and destroy actors by messages. To do so, add a create or destroy directive before the message.
create participant B
A --> B: Hello
Create directives support actor/participant distinction and aliases. The sender or the recipient of a message can be destroyed but only the recipient can be created.
```mermaid-example
sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree
```
```mermaid
sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree
```
### Grouping / Box
The actor(s) can be grouped in vertical boxes. You can define a color (if not, it will be transparent) and/or a descriptive label using the following notation:

View File

@@ -487,7 +487,7 @@ where
- the second _property_ is `color` and its _value_ is `white`
- the third _property_ is `font-weight` and its _value_ is `bold`
- the fourth _property_ is `stroke-width` and its _value_ is `2px`
- the fifth _property_ is `stroke` and its _value_ is `yello`
- the fifth _property_ is `stroke` and its _value_ is `yellow`
### Apply classDef styles to states

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "10.2.3",
"version": "10.2.4",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"module": "./dist/mermaid.core.mjs",
@@ -53,13 +53,16 @@
},
"dependencies": {
"@braintree/sanitize-url": "^6.0.2",
"@types/d3-scale": "^4.0.3",
"@types/d3-scale-chromatic": "^3.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.4.0",
"d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.10",
"dayjs": "^1.11.7",
"dompurify": "3.0.3",
"dompurify": "3.0.4",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
@@ -73,6 +76,7 @@
"devDependencies": {
"@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0",
"@types/d3-sankey": "^0.12.1",
"@types/d3-selection": "^3.0.5",
"@types/dompurify": "^3.0.2",
"@types/jsdom": "^21.1.1",

View File

@@ -32,6 +32,7 @@ export interface MermaidConfig {
mindmap?: MindmapDiagramConfig;
gitGraph?: GitGraphDiagramConfig;
c4?: C4DiagramConfig;
sankey?: SankeyDiagramConfig;
dompurifyConfig?: DOMPurify.Config;
wrap?: boolean;
fontSize?: number;
@@ -411,6 +412,26 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
wrappingWidth?: number;
}
export enum SankeyLinkColor {
source = 'source',
target = 'target',
gradient = 'gradient',
}
export enum SankeyNodeAlignment {
left = 'left',
right = 'right',
center = 'center',
justify = 'justify',
}
export interface SankeyDiagramConfig extends BaseDiagramConfig {
width?: number;
height?: number;
linkColor?: SankeyLinkColor | string;
nodeAlignment?: SankeyNodeAlignment;
}
export interface FontConfig {
fontSize?: string | number;
fontFamily?: string;

View File

@@ -1,5 +1,5 @@
import theme from './themes/index.js';
import { MermaidConfig } from './config.type.js';
import { MermaidConfig, SankeyLinkColor, SankeyNodeAlignment } from './config.type.js';
/**
* **Configuration methods in Mermaid version 8.6.0 have been updated, to learn more[[click
* here](8.6.0_docs.md)].**
@@ -2270,6 +2270,13 @@ const config: Partial<MermaidConfig> = {
padding: 10,
maxNodeWidth: 200,
},
sankey: {
width: 800,
height: 400,
linkColor: SankeyLinkColor.gradient,
nodeAlignment: SankeyNodeAlignment.justify,
useMaxWidth: false,
},
fontSize: 16,
};

View File

@@ -18,6 +18,7 @@ import errorDiagram from '../diagrams/error/errorDiagram.js';
import flowchartElk from '../diagrams/flowchart/elk/detector.js';
import timeline from '../diagrams/timeline/detector.js';
import mindmap from '../diagrams/mindmap/detector.js';
import sankey from '../diagrams/sankey/sankeyDetector.js';
import { registerLazyLoadedDiagrams } from './detectType.js';
import { registerDiagram } from './diagramAPI.js';
@@ -79,6 +80,7 @@ export const addDiagrams = () => {
stateV2,
state,
journey,
quadrantChart
quadrantChart,
sankey
);
};

View File

@@ -0,0 +1,99 @@
%% There are leading and trailing spaces, do not crop
Agricultural 'waste',Bio-conversion,124.729
%% line with a comment
%% Normal line
Bio-conversion,Liquid,0.597
%% Line with unquoted sankey keyword
sankey,target,10
%% Quoted sankey keyword
"sankey",target,10
%% Another normal line
Bio-conversion,Losses,26.862
%% Line with integer amount
Bio-conversion,Solid,280
%% Some blank lines in the middle of CSV
%% Another normal line
Bio-conversion,Gas,81.144
%% Quoted line
"Biofuel imports",Liquid,35
%% Quoted line with escaped quotes inside
"""Biomass imports""",Solid,35
%% Lines containing commas inside
%% Quoted and unquoted values should be equal in terms of graph
"District heating","Heating and cooling, commercial",22.505
District heating,"Heating and cooling, homes",46.184
%% A bunch of lines, normal CSV
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
"""Wave""",Electricity grid,19.013
"""Wind""",Electricity grid,289.366
%% lines at the end, do not remove
Can't render this file because it has a wrong number of fields in line 2.

View File

@@ -0,0 +1,69 @@
/** mermaid */
//---------------------------------------------------------
// We support csv format as defined here:
// https://www.ietf.org/rfc/rfc4180.txt
// There are some minor changes for compliance with jison
// We also parse only 3 columns: source,target,value
// And allow blank lines for visual purposes
//---------------------------------------------------------
%lex
%options case-insensitive
%options easy_keword_rules
%x escaped_text
%x csv
// as per section 6.1 of RFC 2234 [2]
COMMA \u002C
CR \u000D
LF \u000A
CRLF \u000D\u000A
ESCAPED_QUOTE \u0022
DQUOTE \u0022
TEXTDATA [\u0020-\u0021\u0023-\u002B\u002D-\u007E]
%%
<INITIAL>"sankey-beta" { this.pushState('csv'); return 'SANKEY'; }
<INITIAL,csv><<EOF>> { return 'EOF' } // match end of file
<INITIAL,csv>({CRLF}|{LF}) { return 'NEWLINE' }
<INITIAL,csv>{COMMA} { return 'COMMA' }
<INITIAL,csv>{DQUOTE} { this.pushState('escaped_text'); return 'DQUOTE'; }
<INITIAL,csv>{TEXTDATA}* { return 'NON_ESCAPED_TEXT' }
<INITIAL,csv,escaped_text>{DQUOTE}(?!{DQUOTE}) {this.popState('escaped_text'); return 'DQUOTE'; } // unescaped DQUOTE closes string
<INITIAL,csv,escaped_text>({TEXTDATA}|{COMMA}|{CR}|{LF}|{DQUOTE}{DQUOTE})* { return 'ESCAPED_TEXT'; }
/lex
%start start
%% // language grammar
start: SANKEY NEWLINE csv opt_eof;
csv: record csv_tail;
csv_tail: NEWLINE csv | ;
opt_eof: EOF | ;
record
: field\[source] COMMA field\[target] COMMA field\[value] {
const source = yy.findOrCreateNode($source.trim().replaceAll('""', '"'));
const target = yy.findOrCreateNode($target.trim().replaceAll('""', '"'));
const value = parseFloat($value.trim());
yy.addLink(source,target,value);
} // parse only 3 fields, this is not part of CSV standard
;
field
: escaped { $$=$escaped; }
| non_escaped { $$=$non_escaped; }
;
escaped: DQUOTE ESCAPED_TEXT DQUOTE { $$=$ESCAPED_TEXT; };
non_escaped: NON_ESCAPED_TEXT { $$=$NON_ESCAPED_TEXT; };

View File

@@ -0,0 +1,24 @@
// @ts-ignore: jison doesn't export types
import sankey from './sankey.jison';
import db from '../sankeyDB.js';
import { cleanupComments } from '../../../diagram-api/comments.js';
import { prepareTextForParsing } from '../sankeyUtils.js';
import * as fs from 'fs';
import * as path from 'path';
describe('Sankey diagram', function () {
describe('when parsing an info graph it', function () {
beforeEach(function () {
sankey.parser.yy = db;
sankey.parser.yy.clear();
});
it('parses csv', async () => {
const csv = path.resolve(__dirname, './energy.csv');
const data = fs.readFileSync(csv, 'utf8');
const graphDefinition = prepareTextForParsing(cleanupComments('sankey-beta\n\n ' + data));
sankey.parser.parse(graphDefinition);
});
});
});

View File

@@ -0,0 +1,81 @@
import * as configApi from '../../config.js';
import common from '../common/common.js';
import {
setAccTitle,
getAccTitle,
getAccDescription,
setAccDescription,
setDiagramTitle,
getDiagramTitle,
clear as commonClear,
} from '../../commonDb.js';
// Sankey diagram represented by nodes and links between those nodes
let links: SankeyLink[] = [];
// Array of nodes guarantees their order
let nodes: SankeyNode[] = [];
// We also have to track nodes uniqueness (by ID)
let nodesMap: Record<string, SankeyNode> = {};
const clear = (): void => {
links = [];
nodes = [];
nodesMap = {};
commonClear();
};
class SankeyLink {
constructor(public source: SankeyNode, public target: SankeyNode, public value: number = 0) {}
}
/**
* @param source - Node where the link starts
* @param target - Node where the link ends
* @param value - number, float or integer, describes the amount to be passed
*/
const addLink = (source: SankeyNode, target: SankeyNode, value: number): void => {
links.push(new SankeyLink(source, target, value));
};
class SankeyNode {
constructor(public ID: string) {}
}
const findOrCreateNode = (ID: string): SankeyNode => {
ID = common.sanitizeText(ID, configApi.getConfig());
if (!nodesMap[ID]) {
nodesMap[ID] = new SankeyNode(ID);
nodes.push(nodesMap[ID]);
}
return nodesMap[ID];
};
const getNodes = () => nodes;
const getLinks = () => links;
const getGraph = () => ({
nodes: nodes.map((node) => ({ id: node.ID })),
links: links.map((link) => ({
source: link.source.ID,
target: link.target.ID,
value: link.value,
})),
});
export default {
nodesMap,
getConfig: () => configApi.getConfig().sankey,
getNodes,
getLinks,
getGraph,
addLink,
findOrCreateNode,
getAccTitle,
setAccTitle,
getAccDescription,
setAccDescription,
getDiagramTitle,
setDiagramTitle,
clear,
};

View File

@@ -0,0 +1,20 @@
import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-api/types.js';
const id = 'sankey';
const detector: DiagramDetector = (txt) => {
return /^\s*sankey-beta/.test(txt);
};
const loader = async () => {
const { diagram } = await import('./sankeyDiagram.js');
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@@ -0,0 +1,15 @@
import { DiagramDefinition } from '../../diagram-api/types.js';
// @ts-ignore: jison doesn't export types
import parser from './parser/sankey.jison';
import db from './sankeyDB.js';
import renderer from './sankeyRenderer.js';
import { prepareTextForParsing } from './sankeyUtils.js';
const originalParse = parser.parse.bind(parser);
parser.parse = (text: string) => originalParse(prepareTextForParsing(text));
export const diagram: DiagramDefinition = {
parser,
db,
renderer,
};

View File

@@ -0,0 +1,205 @@
import { Diagram } from '../../Diagram.js';
import * as configApi from '../../config.js';
import {
select as d3select,
scaleOrdinal as d3scaleOrdinal,
schemeTableau10 as d3schemeTableau10,
} from 'd3';
import {
sankey as d3Sankey,
sankeyLinkHorizontal as d3SankeyLinkHorizontal,
sankeyLeft as d3SankeyLeft,
sankeyRight as d3SankeyRight,
sankeyCenter as d3SankeyCenter,
sankeyJustify as d3SankeyJustify,
SankeyNode as d3SankeyNode,
} from 'd3-sankey';
import { configureSvgSize } from '../../setupGraphViewbox.js';
import { Uid } from '../../rendering-util/uid.js';
import { SankeyLinkColor, SankeyNodeAlignment } from '../../config.type.js';
// Map config options to alignment functions
const alignmentsMap: Record<
SankeyNodeAlignment,
(node: d3SankeyNode<object, object>, n: number) => number
> = {
[SankeyNodeAlignment.left]: d3SankeyLeft,
[SankeyNodeAlignment.right]: d3SankeyRight,
[SankeyNodeAlignment.center]: d3SankeyCenter,
[SankeyNodeAlignment.justify]: d3SankeyJustify,
};
/**
* Draws Sankey diagram.
*
* @param text - The text of the diagram
* @param id - The id of the diagram which will be used as a DOM element id¨
* @param _version - Mermaid version from package.json
* @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): void {
// Get Sankey config
const { securityLevel, sankey: conf } = configApi.getConfig();
const defaultSankeyConfig = configApi!.defaultConfig!.sankey!;
// TODO:
// This code repeats for every diagram
// Figure out what is happening there, probably it should be separated
// The main thing is svg object that is a d3 wrapper for svg operations
//
let sandboxElement: any;
if (securityLevel === 'sandbox') {
sandboxElement = d3select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? d3select(sandboxElement.nodes()[0].contentDocument.body)
: d3select('body');
// @ts-ignore TODO root.select is not callable
const svg = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : d3select(`[id="${id}"]`);
// Establish svg dimensions and get width and height
//
const width = conf?.width || defaultSankeyConfig.width!;
const height = conf?.height || defaultSankeyConfig.width!;
const useMaxWidth = conf?.useMaxWidth || defaultSankeyConfig.useMaxWidth!;
const nodeAlignment = conf?.nodeAlignment || defaultSankeyConfig.nodeAlignment!;
// FIX: using max width prevents height from being set, is it intended?
// to add height directly one can use `svg.attr('height', height)`
//
// @ts-ignore TODO: svg type vs selection mismatch
configureSvgSize(svg, height, width, useMaxWidth);
// Prepare data for construction based on diagObj.db
// This must be a mutable object with `nodes` and `links` properties:
//
// {
// "nodes": [ { "id": "Alice" }, { "id": "Bob" }, { "id": "Carol" } ],
// "links": [ { "source": "Alice", "target": "Bob", "value": 23 }, { "source": "Bob", "target": "Carol", "value": 43 } ]
// }
//
// @ts-ignore TODO: db should be coerced to sankey DB type
const graph = diagObj.db.getGraph();
// Get alignment function
const nodeAlign = alignmentsMap[nodeAlignment];
// Construct and configure a Sankey generator
// That will be a function that calculates nodes and links dimensions
//
const nodeWidth = 10;
const sankey = d3Sankey()
.nodeId((d: any) => d.id) // we use 'id' property to identify node
.nodeWidth(nodeWidth)
.nodePadding(10)
.nodeAlign(nodeAlign)
.extent([
[0, 0],
[width, height],
]);
// Compute the Sankey layout: calculate nodes and links positions
// Our `graph` object will be mutated by this and enriched with other properties
//
sankey(graph);
// Get color scheme for the graph
const colorScheme = d3scaleOrdinal(d3schemeTableau10);
// Create rectangles for nodes
svg
.append('g')
.attr('class', 'nodes')
.selectAll('.node')
.data(graph.nodes)
.join('g')
.attr('class', 'node')
.attr('id', (d: any) => (d.uid = Uid.next('node-')).id)
.attr('transform', function (d: any) {
return 'translate(' + d.x0 + ',' + d.y0 + ')';
})
.attr('x', (d: any) => d.x0)
.attr('y', (d: any) => d.y0)
.append('rect')
.attr('height', (d: any) => {
return d.y1 - d.y0;
})
.attr('width', (d: any) => d.x1 - d.x0)
.attr('fill', (d: any) => colorScheme(d.id));
// Create labels for nodes
svg
.append('g')
.attr('class', 'node-labels')
.attr('font-family', 'sans-serif')
.attr('font-size', 14)
.selectAll('text')
.data(graph.nodes)
.join('text')
.attr('x', (d: any) => (d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6))
.attr('y', (d: any) => (d.y1 + d.y0) / 2)
.attr('dy', '0.35em')
.attr('text-anchor', (d: any) => (d.x0 < width / 2 ? 'start' : 'end'))
.text((d: any) => d.id);
// Creates the paths that represent the links.
const link = svg
.append('g')
.attr('class', 'links')
.attr('fill', 'none')
.attr('stroke-opacity', 0.5)
.selectAll('.link')
.data(graph.links)
.join('g')
.attr('class', 'link')
.style('mix-blend-mode', 'multiply');
const linkColor = conf?.linkColor || SankeyLinkColor.gradient;
if (linkColor === SankeyLinkColor.gradient) {
const gradient = link
.append('linearGradient')
.attr('id', (d: any) => (d.uid = Uid.next('linearGradient-')).id)
.attr('gradientUnits', 'userSpaceOnUse')
.attr('x1', (d: any) => d.source.x1)
.attr('x2', (d: any) => d.target.x0);
gradient
.append('stop')
.attr('offset', '0%')
.attr('stop-color', (d: any) => colorScheme(d.source.id));
gradient
.append('stop')
.attr('offset', '100%')
.attr('stop-color', (d: any) => colorScheme(d.target.id));
}
let coloring: any;
switch (linkColor) {
case SankeyLinkColor.gradient:
coloring = (d: any) => d.uid;
break;
case SankeyLinkColor.source:
coloring = (d: any) => colorScheme(d.source.id);
break;
case SankeyLinkColor.target:
coloring = (d: any) => colorScheme(d.target.id);
break;
default:
coloring = linkColor;
}
link
.append('path')
.attr('d', d3SankeyLinkHorizontal())
.attr('stroke', coloring)
.attr('stroke-width', (d: any) => Math.max(1, d.width));
};
export default {
draw,
};

View File

@@ -0,0 +1,8 @@
export const prepareTextForParsing = (text: string): string => {
const textToParse = text
.replaceAll(/^[^\S\n\r]+|[^\S\n\r]+$/g, '') // remove all trailing spaces for each row
.replaceAll(/([\n\r])+/g, '\n') // remove empty lines duplicated
.trim();
return textToParse;
};

View File

@@ -38,6 +38,8 @@
"box" { this.begin('LINE'); return 'box'; }
"participant" { this.begin('ID'); return 'participant'; }
"actor" { this.begin('ID'); return 'participant_actor'; }
"create" return 'create';
"destroy" { this.begin('ID'); return 'destroy'; }
<ID>[^\->:\n,;]+?([\-]*[^\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
@@ -138,6 +140,7 @@ directive
statement
: participant_statement
| 'create' participant_statement {$2.type='createParticipant'; $$=$2;}
| 'box' restOfLine box_section end
{
$3.unshift({type: 'boxStart', boxData:yy.parseBoxData($2) });
@@ -234,10 +237,11 @@ else_sections
;
participant_statement
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NEWLINE' {$2.draw='participant'; $2.type='addParticipant';$$=$2;}
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant_actor' actor 'NEWLINE' {$2.draw='actor'; $2.type='addParticipant'; $$=$2;}
| 'destroy' actor 'NEWLINE' {$2.type='destroyParticipant'; $$=$2;}
;
note_statement

View File

@@ -14,12 +14,16 @@ import {
let prevActor = undefined;
let actors = {};
let createdActors = {};
let destroyedActors = {};
let boxes = [];
let messages = [];
const notes = [];
let sequenceNumbersEnabled = false;
let wrapEnabled;
let currentBox = undefined;
let lastCreated = undefined;
let lastDestroyed = undefined;
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
@@ -165,6 +169,12 @@ export const getBoxes = function () {
export const getActors = function () {
return actors;
};
export const getCreatedActors = function () {
return createdActors;
};
export const getDestroyedActors = function () {
return destroyedActors;
};
export const getActor = function (id) {
return actors[id];
};
@@ -194,6 +204,8 @@ export const autoWrap = () => {
export const clear = function () {
actors = {};
createdActors = {};
destroyedActors = {};
boxes = [];
messages = [];
sequenceNumbersEnabled = false;
@@ -459,10 +471,21 @@ export const apply = function (param) {
});
break;
case 'addParticipant':
addActor(param.actor, param.actor, param.description, 'participant');
addActor(param.actor, param.actor, param.description, param.draw);
break;
case 'addActor':
addActor(param.actor, param.actor, param.description, 'actor');
case 'createParticipant':
if (actors[param.actor]) {
throw new Error(
"It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior"
);
}
lastCreated = param.actor;
addActor(param.actor, param.actor, param.description, param.draw);
createdActors[param.actor] = messages.length;
break;
case 'destroyParticipant':
lastDestroyed = param.actor;
destroyedActors[param.actor] = messages.length;
break;
case 'activeStart':
addSignal(param.actor, undefined, undefined, param.signalType);
@@ -486,6 +509,27 @@ export const apply = function (param) {
addDetails(param.actor, param.text);
break;
case 'addMessage':
if (lastCreated) {
if (param.to !== lastCreated) {
throw new Error(
'The created participant ' +
lastCreated +
' does not have an associated creating message after its declaration. Please check the sequence diagram.'
);
} else {
lastCreated = undefined;
}
} else if (lastDestroyed) {
if (param.to !== lastDestroyed && param.from !== lastDestroyed) {
throw new Error(
'The destroyed participant ' +
lastDestroyed +
' does not have an associated destroying message after its declaration. Please check the sequence diagram.'
);
} else {
lastDestroyed = undefined;
}
}
addSignal(param.from, param.to, param.msg, param.signalType);
break;
case 'boxStart':
@@ -566,6 +610,8 @@ export default {
showSequenceNumbers,
getMessages,
getActors,
getCreatedActors,
getDestroyedActors,
getActor,
getActorKeys,
getActorProperty,

View File

@@ -1404,6 +1404,62 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com
expect(boxes[0].actorKeys).toEqual(['a', 'b']);
expect(boxes[0].fill).toEqual('Aqua');
});
it('should handle simple actor creation', async () => {
const str = `
sequenceDiagram
participant a as Alice
a ->>b: Hello Bob?
create participant c
b-->>c: Hello c!
c ->> b: Hello b?
create actor d as Donald
a ->> d: Hello Donald?
`;
await mermaidAPI.parse(str);
const actors = diagram.db.getActors();
const createdActors = diagram.db.getCreatedActors();
expect(actors['c'].name).toEqual('c');
expect(actors['c'].description).toEqual('c');
expect(actors['c'].type).toEqual('participant');
expect(createdActors['c']).toEqual(1);
expect(actors['d'].name).toEqual('d');
expect(actors['d'].description).toEqual('Donald');
expect(actors['d'].type).toEqual('actor');
expect(createdActors['d']).toEqual(3);
});
it('should handle simple actor destruction', async () => {
const str = `
sequenceDiagram
participant a as Alice
a ->>b: Hello Bob?
destroy a
b-->>a: Hello Alice!
b ->> c: Where is Alice?
destroy c
b ->> c: Where are you?
`;
await mermaidAPI.parse(str);
const destroyedActors = diagram.db.getDestroyedActors();
expect(destroyedActors['a']).toEqual(1);
expect(destroyedActors['c']).toEqual(3);
});
it('should handle the creation and destruction of the same actor', async () => {
const str = `
sequenceDiagram
a ->>b: Hello Bob?
create participant c
b ->>c: Hello c!
c ->> b: Hello b?
destroy c
b ->> c : Bye c !
`;
await mermaidAPI.parse(str);
const createdActors = diagram.db.getCreatedActors();
const destroyedActors = diagram.db.getDestroyedActors();
expect(createdActors['c']).toEqual(1);
expect(destroyedActors['c']).toEqual(3);
});
});
describe('when checking the bounds in a sequenceDiagram', function () {
beforeAll(() => {
@@ -1973,7 +2029,9 @@ participant Alice`;
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopx).toBe(conf.width);
expect(bounds.stopy).toBe(models.lastActor().y + models.lastActor().height + conf.boxMargin);
expect(bounds.stopy).toBe(
models.lastActor().stopy + models.lastActor().height + conf.boxMargin
);
});
});
});
@@ -2025,7 +2083,7 @@ participant Alice
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(
models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin
models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
);
});
it('should handle one actor, when logLevel is 3 (dfg0)', async () => {
@@ -2045,7 +2103,7 @@ participant Alice
expect(bounds.startx).toBe(0);
expect(bounds.starty).toBe(0);
expect(bounds.stopy).toBe(
models.lastActor().y + models.lastActor().height + mermaid.sequence.boxMargin
models.lastActor().stopy + models.lastActor().height + mermaid.sequence.boxMargin
);
});
it('should hide sequence numbers when autonumber is removed when autonumber is enabled', async () => {

View File

@@ -1,6 +1,6 @@
// @ts-nocheck TODO: fix file
import { select, selectAll } from 'd3';
import svgDraw, { drawText, fixLifeLineHeights } from './svgDraw.js';
import svgDraw, { ACTOR_TYPE_WIDTH, drawText, fixLifeLineHeights } from './svgDraw.js';
import { log } from '../../logger.js';
import common from '../common/common.js';
import * as svgDrawCommon from '../common/svgDrawCommon';
@@ -478,29 +478,19 @@ const drawMessage = function (diagram, msgModel, lineStartY: number, diagObj: Di
}
};
export const drawActors = function (
const addActorRenderingData = function (
diagram,
actors,
createdActors,
actorKeys,
verticalPos,
configuration,
messages,
isFooter
) {
if (configuration.hideUnusedParticipants === true) {
const newActors = new Set();
messages.forEach((message) => {
newActors.add(message.from);
newActors.add(message.to);
});
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
}
// Draw the actors
let prevWidth = 0;
let prevMargin = 0;
let maxHeight = 0;
let prevBox = undefined;
let maxHeight = 0;
for (const actorKey of actorKeys) {
const actor = actors[actorKey];
@@ -528,12 +518,16 @@ export const drawActors = function (
actor.height = common.getMax(actor.height || conf.height, conf.height);
actor.margin = actor.margin || conf.actorMargin;
actor.x = prevWidth + prevMargin;
actor.y = bounds.getVerticalPos();
maxHeight = common.getMax(maxHeight, actor.height);
// if the actor is created by a message, widen margin
if (createdActors[actor.name]) {
prevMargin += actor.width / 2;
}
actor.x = prevWidth + prevMargin;
actor.starty = bounds.getVerticalPos();
// Draw the box with the attached line
const height = svgDraw.drawActor(diagram, actor, conf, isFooter);
maxHeight = common.getMax(maxHeight, height);
bounds.insert(actor.x, verticalPos, actor.x + actor.width, actor.height);
prevWidth += actor.width + prevMargin;
@@ -554,6 +548,28 @@ export const drawActors = function (
bounds.bumpVerticalPos(maxHeight);
};
export const drawActors = function (diagram, actors, actorKeys, isFooter) {
if (!isFooter) {
for (const actorKey of actorKeys) {
const actor = actors[actorKey];
// Draw the box with the attached line
svgDraw.drawActor(diagram, actor, conf, false);
}
} else {
let maxHeight = 0;
bounds.bumpVerticalPos(conf.boxMargin * 2);
for (const actorKey of actorKeys) {
const actor = actors[actorKey];
if (!actor.stopy) {
actor.stopy = bounds.getVerticalPos();
}
const height = svgDraw.drawActor(diagram, actor, conf, true);
maxHeight = common.getMax(maxHeight, height);
}
bounds.bumpVerticalPos(maxHeight + conf.boxMargin);
}
};
export const drawActorsPopup = function (diagram, actors, actorKeys, doc) {
let maxHeight = 0;
let maxWidth = 0;
@@ -633,6 +649,95 @@ function adjustLoopHeightForWrap(loopWidths, msg, preMargin, postMargin, addLoop
bounds.bumpVerticalPos(heightAdjust);
}
/**
* Adjust the msgModel and the actor for the rendering in case the latter is created or destroyed by the msg
* @param msg - the potentially creating or destroying message
* @param msgModel - the model associated with the message
* @param lineStartY - the y position of the message line
* @param index - the index of the current actor under consideration
* @param actors - the array of all actors
* @param createdActors - the array of actors created in the diagram
* @param destroyedActors - the array of actors destroyed in the diagram
*/
function adjustCreatedDestroyedData(
msg,
msgModel,
lineStartY,
index,
actors,
createdActors,
destroyedActors
) {
function receiverAdjustment(actor, adjustment) {
if (actor.x < actors[msg.from].x) {
bounds.insert(
msgModel.stopx - adjustment,
msgModel.starty,
msgModel.startx,
msgModel.stopy + actor.height / 2 + conf.noteMargin
);
msgModel.stopx = msgModel.stopx + adjustment;
} else {
bounds.insert(
msgModel.startx,
msgModel.starty,
msgModel.stopx + adjustment,
msgModel.stopy + actor.height / 2 + conf.noteMargin
);
msgModel.stopx = msgModel.stopx - adjustment;
}
}
function senderAdjustment(actor, adjustment) {
if (actor.x < actors[msg.to].x) {
bounds.insert(
msgModel.startx - adjustment,
msgModel.starty,
msgModel.stopx,
msgModel.stopy + actor.height / 2 + conf.noteMargin
);
msgModel.startx = msgModel.startx + adjustment;
} else {
bounds.insert(
msgModel.stopx,
msgModel.starty,
msgModel.startx + adjustment,
msgModel.stopy + actor.height / 2 + conf.noteMargin
);
msgModel.startx = msgModel.startx - adjustment;
}
}
// if it is a create message
if (createdActors[msg.to] == index) {
const actor = actors[msg.to];
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
receiverAdjustment(actor, adjustment);
actor.starty = lineStartY - actor.height / 2;
bounds.bumpVerticalPos(actor.height / 2);
}
// if it is a destroy sender message
else if (destroyedActors[msg.from] == index) {
const actor = actors[msg.from];
if (conf.mirrorActors) {
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 : actor.width / 2;
senderAdjustment(actor, adjustment);
}
actor.stopy = lineStartY - actor.height / 2;
bounds.bumpVerticalPos(actor.height / 2);
}
// if it is a destroy receiver message
else if (destroyedActors[msg.to] == index) {
const actor = actors[msg.to];
if (conf.mirrorActors) {
const adjustment = actor.type == 'actor' ? ACTOR_TYPE_WIDTH / 2 + 3 : actor.width / 2 + 3;
receiverAdjustment(actor, adjustment);
}
actor.stopy = lineStartY - actor.height / 2;
bounds.bumpVerticalPos(actor.height / 2);
}
}
/**
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
*
@@ -666,8 +771,10 @@ export const draw = function (_text: string, id: string, _version: string, diagO
// Fetch data from the parsing
const actors = diagObj.db.getActors();
const createdActors = diagObj.db.getCreatedActors();
const destroyedActors = diagObj.db.getDestroyedActors();
const boxes = diagObj.db.getBoxes();
const actorKeys = diagObj.db.getActorKeys();
let actorKeys = diagObj.db.getActorKeys();
const messages = diagObj.db.getMessages();
const title = diagObj.db.getDiagramTitle();
const hasBoxes = diagObj.db.hasAtLeastOneBox();
@@ -686,7 +793,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO
}
}
drawActors(diagram, actors, actorKeys, 0, conf, messages, false);
if (conf.hideUnusedParticipants === true) {
const newActors = new Set();
messages.forEach((message) => {
newActors.add(message.from);
newActors.add(message.to);
});
actorKeys = actorKeys.filter((actorKey) => newActors.has(actorKey));
}
addActorRenderingData(diagram, actors, createdActors, actorKeys, 0, messages, false);
const loopWidths = calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj);
// The arrow head definition is attached to the svg once
@@ -720,7 +836,8 @@ export const draw = function (_text: string, id: string, _version: string, diagO
let sequenceIndex = 1;
let sequenceIndexStep = 1;
const messagesToDraw = [];
messages.forEach(function (msg) {
const backgrounds = [];
messages.forEach(function (msg, index) {
let loopModel, noteModel, msgModel;
switch (msg.type) {
@@ -757,7 +874,7 @@ export const draw = function (_text: string, id: string, _version: string, diagO
break;
case diagObj.db.LINETYPE.RECT_END:
loopModel = bounds.endLoop();
svgDraw.drawBackgroundRect(diagram, loopModel);
backgrounds.push(loopModel);
bounds.models.addLoop(loopModel);
bounds.bumpVerticalPos(loopModel.stopy - bounds.getVerticalPos());
break;
@@ -876,13 +993,20 @@ export const draw = function (_text: string, id: string, _version: string, diagO
break;
default:
try {
// lastMsg = msg
bounds.resetVerticalPos();
msgModel = msg.msgModel;
msgModel.starty = bounds.getVerticalPos();
msgModel.sequenceIndex = sequenceIndex;
msgModel.sequenceVisible = diagObj.db.showSequenceNumbers();
const lineStartY = boundMessage(diagram, msgModel);
adjustCreatedDestroyedData(
msg,
msgModel,
lineStartY,
index,
actors,
createdActors,
destroyedActors
);
messagesToDraw.push({ messageModel: msgModel, lineStartY: lineStartY });
bounds.models.addMessage(msgModel);
} catch (e) {
@@ -907,15 +1031,16 @@ export const draw = function (_text: string, id: string, _version: string, diagO
}
});
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
log.debug('createdActors', createdActors);
log.debug('destroyedActors', destroyedActors);
drawActors(diagram, actors, actorKeys, false);
messagesToDraw.forEach((e) => drawMessage(diagram, e.messageModel, e.lineStartY, diagObj));
if (conf.mirrorActors) {
// Draw actors below diagram
bounds.bumpVerticalPos(conf.boxMargin * 2);
drawActors(diagram, actors, actorKeys, bounds.getVerticalPos(), conf, messages, true);
bounds.bumpVerticalPos(conf.boxMargin);
fixLifeLineHeights(diagram, bounds.getVerticalPos());
drawActors(diagram, actors, actorKeys, true);
}
backgrounds.forEach((e) => svgDraw.drawBackgroundRect(diagram, e));
fixLifeLineHeights(diagram, actors, actorKeys, conf);
bounds.models.boxes.forEach(function (box) {
box.height = bounds.getVerticalPos() - box.y;
@@ -937,11 +1062,6 @@ export const draw = function (_text: string, id: string, _version: string, diagO
const { bounds: box } = bounds.getBounds();
// Adjust line height of actor lines now that the height of the diagram is known
log.debug('For line height fix Querying: #' + id + ' .actor-line');
const actorLines = selectAll('#' + id + ' .actor-line');
actorLines.attr('y2', box.stopy);
// Make sure the height of the diagram supports long menus.
let boxHeight = box.stopy - box.starty;
if (boxHeight < requiredBoxSize.maxHeight) {

View File

@@ -4,6 +4,8 @@ import { addFunction } from '../../interactionDb.js';
import { ZERO_WIDTH_SPACE, parseFontSize } from '../../utils.js';
import { sanitizeUrl } from '@braintree/sanitize-url';
export const ACTOR_TYPE_WIDTH = 18 * 2;
export const drawRect = function (elem, rectData) {
return svgDrawCommon.drawRect(elem, rectData);
};
@@ -294,14 +296,19 @@ export const drawLabel = function (elem, txtObject) {
let actorCnt = -1;
export const fixLifeLineHeights = (diagram, bounds) => {
if (!diagram.selectAll) {
export const fixLifeLineHeights = (diagram, actors, actorKeys, conf) => {
if (!diagram.select) {
return;
}
diagram
.selectAll('.actor-line')
.attr('class', '200')
.attr('y2', bounds - 55);
actorKeys.forEach((actorKey) => {
const actor = actors[actorKey];
const actorDOM = diagram.select('#actor' + actor.actorCnt);
if (!conf.mirrorActors && actor.stopy) {
actorDOM.attr('y2', actor.stopy + actor.height / 2);
} else if (conf.mirrorActors) {
actorDOM.attr('y2', actor.stopy);
}
});
};
/**
@@ -313,10 +320,11 @@ export const fixLifeLineHeights = (diagram, bounds) => {
* @param {boolean} isFooter - If the actor is the footer one
*/
const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
const actorY = isFooter ? actor.stopy : actor.starty;
const center = actor.x + actor.width / 2;
const centerY = actor.y + 5;
const centerY = actorY + 5;
const boxpluslineGroup = elem.append('g');
const boxpluslineGroup = elem.append('g').lower();
var g = boxpluslineGroup;
if (!isFooter) {
@@ -328,6 +336,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
.attr('x2', center)
.attr('y2', 2000)
.attr('class', 'actor-line')
.attr('class', '200')
.attr('stroke-width', '0.5px')
.attr('stroke', '#999');
@@ -348,7 +357,7 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
rect.fill = '#eaeaea';
}
rect.x = actor.x;
rect.y = actor.y;
rect.y = actorY;
rect.width = actor.width;
rect.height = actor.height;
rect.class = cssclass;
@@ -388,8 +397,11 @@ const drawActorTypeParticipant = function (elem, actor, conf, isFooter) {
};
const drawActorTypeActor = function (elem, actor, conf, isFooter) {
const actorY = isFooter ? actor.stopy : actor.starty;
const center = actor.x + actor.width / 2;
const centerY = actor.y + 80;
const centerY = actorY + 80;
elem.lower();
if (!isFooter) {
actorCnt++;
@@ -401,15 +413,18 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
.attr('x2', center)
.attr('y2', 2000)
.attr('class', 'actor-line')
.attr('class', '200')
.attr('stroke-width', '0.5px')
.attr('stroke', '#999');
actor.actorCnt = actorCnt;
}
const actElem = elem.append('g');
actElem.attr('class', 'actor-man');
const rect = svgDrawCommon.getNoteRect();
rect.x = actor.x;
rect.y = actor.y;
rect.y = actorY;
rect.fill = '#eaeaea';
rect.width = actor.width;
rect.height = actor.height;
@@ -421,33 +436,33 @@ const drawActorTypeActor = function (elem, actor, conf, isFooter) {
.append('line')
.attr('id', 'actor-man-torso' + actorCnt)
.attr('x1', center)
.attr('y1', actor.y + 25)
.attr('y1', actorY + 25)
.attr('x2', center)
.attr('y2', actor.y + 45);
.attr('y2', actorY + 45);
actElem
.append('line')
.attr('id', 'actor-man-arms' + actorCnt)
.attr('x1', center - 18)
.attr('y1', actor.y + 33)
.attr('x2', center + 18)
.attr('y2', actor.y + 33);
.attr('x1', center - ACTOR_TYPE_WIDTH / 2)
.attr('y1', actorY + 33)
.attr('x2', center + ACTOR_TYPE_WIDTH / 2)
.attr('y2', actorY + 33);
actElem
.append('line')
.attr('x1', center - 18)
.attr('y1', actor.y + 60)
.attr('x1', center - ACTOR_TYPE_WIDTH / 2)
.attr('y1', actorY + 60)
.attr('x2', center)
.attr('y2', actor.y + 45);
.attr('y2', actorY + 45);
actElem
.append('line')
.attr('x1', center)
.attr('y1', actor.y + 45)
.attr('x2', center + 16)
.attr('y2', actor.y + 60);
.attr('y1', actorY + 45)
.attr('x2', center + ACTOR_TYPE_WIDTH / 2 - 2)
.attr('y2', actorY + 60);
const circle = actElem.append('circle');
circle.attr('cx', actor.x + actor.width / 2);
circle.attr('cy', actor.y + 10);
circle.attr('cy', actorY + 10);
circle.attr('r', 15);
circle.attr('width', actor.width);
circle.attr('height', actor.height);

View File

@@ -138,6 +138,7 @@ function sidebarSyntax() {
{ text: 'Mindmaps 🔥', link: '/syntax/mindmap' },
{ text: 'Timeline 🔥', link: '/syntax/timeline' },
{ text: 'Zenuml 🔥', link: '/syntax/zenuml' },
{ text: 'Sankey 🔥', link: '/syntax/sankey' },
{ text: 'Other Examples', link: '/syntax/examples' },
],
},

View File

@@ -18,7 +18,7 @@ async function download(url: string, fileName: URL) {
const image = await fetch(url);
await writeFile(fileName, Buffer.from(await image.arrayBuffer()));
} catch (error) {
console.error(error);
console.error('failed to load', url, error);
}
}
@@ -26,10 +26,13 @@ async function fetchAvatars() {
await mkdir(fileURLToPath(new URL(getAvatarPath('none'))).replace('none.png', ''), {
recursive: true,
});
contributors = JSON.parse(await readFile(pathContributors, { encoding: 'utf-8' }));
for (const name of contributors) {
await download(`https://github.com/${name}.png?size=100`, getAvatarPath(name));
}
let avatars = contributors.map((name) => {
download(`https://github.com/${name}.png?size=100`, getAvatarPath(name));
});
await Promise.all(avatars);
}
fetchAvatars();

View File

@@ -10,23 +10,27 @@ interface Contributor {
async function fetchContributors() {
const collaborators: string[] = [];
let page = 1;
let data: Contributor[] = [];
do {
const response = await fetch(
`https://api.github.com/repos/mermaid-js/mermaid/contributors?per_page=100&page=${page}`,
{
method: 'GET',
headers: {
'content-type': 'application/json',
},
}
);
data = await response.json();
collaborators.push(...data.map((i) => i.login));
console.log(`Fetched page ${page}`);
page++;
} while (data.length === 100);
try {
let page = 1;
let data: Contributor[] = [];
do {
const response = await fetch(
`https://api.github.com/repos/mermaid-js/mermaid/contributors?per_page=100&page=${page}`,
{
method: 'GET',
headers: {
'content-type': 'application/json',
},
}
);
data = await response.json();
collaborators.push(...data.map((i) => i.login));
console.log(`Fetched page ${page}`);
page++;
} while (data.length === 100);
} catch (e) {
/* contributors fetching failure must not hinder docs development */
}
return collaborators.filter((name) => !name.includes('[bot]'));
}

View File

@@ -55,9 +55,9 @@ To make a custom theme, modify `themeVariables` via `init`.
You will need to use the [base](#available-themes) theme as it is the only modifiable theme.
| Parameter | Description | Type | Properties |
| -------------- | ------------------------------------ | ------ | --------------------------------------------------------------------------------------------------- |
| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables-reference-table)) |
| Parameter | Description | Type | Properties |
| -------------- | ------------------------------------ | ------ | ----------------------------------------------------------------------------------- |
| themeVariables | Modifiable with the `init` directive | Object | `primaryColor`, `primaryTextColor`, `lineColor` ([see full list](#theme-variables)) |
Example of modifying `themeVariables` using the `init` directive:

View File

@@ -1,7 +1,7 @@
# Announcements
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
8 June 2023 · 7 mins
15 June 2023 · 12 mins
A quadrant chart is a useful diagram that helps users visualize data and identify patterns in a data set.
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.

View File

@@ -1,5 +1,11 @@
# Blog
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
15 June 2023 · 12 mins
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
8 June 2023 · 7 mins

View File

@@ -0,0 +1,292 @@
# Sankey diagrams
> A sankey diagram is a visualization used to depict a flow from one set of values to another.
::: warning
This is an experimental diagram. Its syntax are very close to plain CSV, but it is to be extended in the nearest future.
:::
The things being connected are called nodes and the connections are called links.
## Example
This example taken from [observable](https://observablehq.com/@d3/sankey/2?collection=@d3/d3-sankey). It may be rendered a little bit differently, though, in terms of size and colors.
```mermaid
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
::: details
```mermaid-example
sankey-beta
Agricultural 'waste',Bio-conversion,124.729
Bio-conversion,Liquid,0.597
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
Biofuel imports,Liquid,35
Biomass imports,Solid,35
Coal imports,Coal,11.606
Coal reserves,Coal,63.965
Coal,Solid,75.571
District heating,Industry,10.639
District heating,Heating and cooling - commercial,22.505
District heating,Heating and cooling - homes,46.184
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
Electricity grid,Industry,342.165
Electricity grid,Road transport,37.797
Electricity grid,Agriculture,4.412
Electricity grid,Heating and cooling - commercial,40.858
Electricity grid,Losses,56.691
Electricity grid,Rail transport,7.863
Electricity grid,Lighting & appliances - commercial,90.008
Electricity grid,Lighting & appliances - homes,93.494
Gas imports,Ngas,40.719
Gas reserves,Ngas,82.233
Gas,Heating and cooling - commercial,0.129
Gas,Losses,1.401
Gas,Thermal generation,151.891
Gas,Agriculture,2.096
Gas,Industry,48.58
Geothermal,Electricity grid,7.013
H2 conversion,H2,20.897
H2 conversion,Losses,6.242
H2,Road transport,20.897
Hydro,Electricity grid,6.995
Liquid,Industry,121.066
Liquid,International shipping,128.69
Liquid,Road transport,135.835
Liquid,Domestic aviation,14.458
Liquid,International aviation,206.267
Liquid,Agriculture,3.64
Liquid,National navigation,33.218
Liquid,Rail transport,4.413
Marine algae,Bio-conversion,4.375
Ngas,Gas,122.952
Nuclear,Thermal generation,839.978
Oil imports,Oil,504.287
Oil reserves,Oil,107.703
Oil,Liquid,611.99
Other waste,Solid,56.587
Other waste,Bio-conversion,77.81
Pumped heat,Heating and cooling - homes,193.026
Pumped heat,Heating and cooling - commercial,70.672
Solar PV,Electricity grid,59.901
Solar Thermal,Heating and cooling - homes,19.263
Solar,Solar Thermal,19.263
Solar,Solar PV,59.901
Solid,Agriculture,0.882
Solid,Thermal generation,400.12
Solid,Industry,46.477
Thermal generation,Electricity grid,525.531
Thermal generation,Losses,787.129
Thermal generation,District heating,79.329
Tidal,Electricity grid,9.452
UK land based bioenergy,Bio-conversion,182.01
Wave,Electricity grid,19.013
Wind,Electricity grid,289.366
```
:::
## Syntax
The idea behind syntax is that a user types `sankey-beta` keyword first, then pastes raw CSV below and get the result.
It implements CSV standard as [described here](https://www.ietf.org/rfc/rfc4180.txt) with subtle **differences**:
- CSV must contain **3 columns only**
- It is **allowed** to have **empty lines** without comma separators for visual purposes
### Basic
It is implied that 3 columns inside CSV should represent `source`, `target` and `value` accordingly:
```mermaid-example
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
```mermaid
sankey-beta
%% source,target,value
Electricity grid,Over generation / exports,104.453
Electricity grid,Heating and cooling - homes,113.726
Electricity grid,H2 conversion,27.14
```
### Empty Lines
CSV does not support empty lines without comma delimeters by default. But you can add them if needed:
```mermaid-example
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
```mermaid
sankey-beta
Bio-conversion,Losses,26.862
Bio-conversion,Solid,280.322
Bio-conversion,Gas,81.144
```
### Commas
If you need to have a comma, wrap it in double quotes:
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, homes",193.026
Pumped heat,"Heating and cooling, commercial",70.672
```
### Double Quotes
If you need to have double quote, put a pair of them inside quoted string:
```mermaid-example
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
```mermaid
sankey-beta
Pumped heat,"Heating and cooling, ""homes""",193.026
Pumped heat,"Heating and cooling, ""commercial""",70.672
```
## Configuration
You can customize link colors, node alignments and diagram dimensions.
```html
<script>
const config = {
startOnLoad: true,
securityLevel: 'loose',
sankey: {
width: 800,
height: 400,
linkColor: 'source',
nodeAlignment: 'left',
},
};
mermaid.initialize(config);
</script>
```
### Links Coloring
You can adjust links' color by setting `linkColor` to one of those:
- `source` - link will be of a source node color
- `target` - link will be of a target node color
- `gradient` - link color will be smoothly transient between source and target node colors
- hex code of color, like `#a1a1a1`
### Node Alignment
Graph layout can be changed by setting `nodeAlignment` to:
- `justify`
- `center`
- `left`
- `right`

View File

@@ -58,6 +58,31 @@ sequenceDiagram
J->>A: Great!
```
### Actor Creation and Destruction
It is possible to create and destroy actors by messages. To do so, add a create or destroy directive before the message.
```
create participant B
A --> B: Hello
```
Create directives support actor/participant distinction and aliases. The sender or the recipient of a message can be destroyed but only the recipient can be created.
```mermaid-example
sequenceDiagram
Alice->>Bob: Hello Bob, how are you ?
Bob->>Alice: Fine, thank you. And you?
create participant Carl
Alice->>Carl: Hi Carl!
create actor D as Donald
Carl->>D: Hi!
destroy Carl
Alice-xCarl: We are too many
destroy Bob
Bob->>Alice: I agree
```
### Grouping / Box
The actor(s) can be grouped in vertical boxes. You can define a color (if not, it will be transparent) and/or a descriptive label using the following notation:

View File

@@ -304,7 +304,7 @@ where
- the second _property_ is `color` and its _value_ is `white`
- the third _property_ is `font-weight` and its _value_ is `bold`
- the fourth _property_ is `stroke-width` and its _value_ is `2px`
- the fifth _property_ is `stroke` and its _value_ is `yello`
- the fifth _property_ is `stroke` and its _value_ is `yellow`
### Apply classDef styles to states

View File

@@ -0,0 +1,18 @@
export class Uid {
private static count = 0;
id: string;
href: string;
public static next(name: string): Uid {
return new Uid(name + ++Uid.count);
}
constructor(id: string) {
this.id = id;
this.href = `#${id}`;
}
toString(): string {
return 'url(' + this.href + ')';
}
}

211
pnpm-lock.yaml generated
View File

@@ -194,6 +194,12 @@ importers:
'@braintree/sanitize-url':
specifier: ^6.0.2
version: 6.0.2
'@types/d3-scale':
specifier: ^4.0.3
version: 4.0.3
'@types/d3-scale-chromatic':
specifier: ^3.0.0
version: 3.0.0
cytoscape:
specifier: ^3.23.0
version: 3.23.0
@@ -206,6 +212,9 @@ importers:
d3:
specifier: ^7.4.0
version: 7.8.2
d3-sankey:
specifier: ^0.12.3
version: 0.12.3
dagre-d3-es:
specifier: 7.0.10
version: 7.0.10
@@ -213,8 +222,8 @@ importers:
specifier: ^1.11.7
version: 1.11.7
dompurify:
specifier: 3.0.3
version: 3.0.3
specifier: 3.0.4
version: 3.0.4
elkjs:
specifier: ^0.8.2
version: 0.8.2
@@ -249,6 +258,9 @@ importers:
'@types/d3':
specifier: ^7.4.0
version: 7.4.0
'@types/d3-sankey':
specifier: ^0.12.1
version: 0.12.1
'@types/d3-selection':
specifier: ^3.0.5
version: 3.0.5
@@ -451,31 +463,6 @@ importers:
specifier: ^7.0.0
version: 7.0.0
packages/mermaid/src/vitepress:
dependencies:
'@vueuse/core':
specifier: ^10.1.0
version: 10.1.0(vue@3.2.47)
vue:
specifier: ^3.2.47
version: 3.2.47
devDependencies:
pathe:
specifier: ^1.1.0
version: 1.1.0
unocss:
specifier: ^0.53.0
version: 0.53.0(postcss@8.4.23)(vite@4.3.9)
unplugin-vue-components:
specifier: ^0.25.0
version: 0.25.0(rollup@2.79.1)(vue@3.2.47)
vite:
specifier: ^4.3.3
version: 4.3.9(@types/node@18.16.0)
vitepress:
specifier: 1.0.0-beta.2
version: 1.0.0-beta.2(@algolia/client-search@4.14.2)(@types/node@18.16.0)(search-insights@2.6.0)
tests/webpack:
dependencies:
'@mermaid-js/mermaid-example-diagram':
@@ -4009,6 +3996,10 @@ packages:
'@types/d3-color': 3.1.0
dev: true
/@types/d3-path@1.0.9:
resolution: {integrity: sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ==}
dev: true
/@types/d3-path@3.0.0:
resolution: {integrity: sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==}
dev: true
@@ -4025,20 +4016,30 @@ packages:
resolution: {integrity: sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==}
dev: true
/@types/d3-scale-chromatic@3.0.0:
resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==}
/@types/d3-sankey@0.12.1:
resolution: {integrity: sha512-10X6l6lXB42udBNX9/fDN+kJuooifSMk7+x4U9815eobavldqis4wDdFQUQjMazh+qlzsUZsGzXKxfWFUVt+3w==}
dependencies:
'@types/d3-shape': 1.3.8
dev: true
/@types/d3-scale@4.0.2:
resolution: {integrity: sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==}
/@types/d3-scale-chromatic@3.0.0:
resolution: {integrity: sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==}
/@types/d3-scale@4.0.3:
resolution: {integrity: sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ==}
dependencies:
'@types/d3-time': 3.0.0
dev: true
/@types/d3-selection@3.0.5:
resolution: {integrity: sha512-xCB0z3Hi8eFIqyja3vW8iV01+OHGYR2di/+e+AiOcXIOrY82lcvWW8Ke1DYE/EUVMsBl4Db9RppSBS3X1U6J0w==}
dev: true
/@types/d3-shape@1.3.8:
resolution: {integrity: sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg==}
dependencies:
'@types/d3-path': 1.0.9
dev: true
/@types/d3-shape@3.1.0:
resolution: {integrity: sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==}
dependencies:
@@ -4051,7 +4052,6 @@ packages:
/@types/d3-time@3.0.0:
resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==}
dev: true
/@types/d3-timer@3.0.0:
resolution: {integrity: sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==}
@@ -4094,7 +4094,7 @@ packages:
'@types/d3-polygon': 3.0.0
'@types/d3-quadtree': 3.0.2
'@types/d3-random': 3.0.1
'@types/d3-scale': 4.0.2
'@types/d3-scale': 4.0.3
'@types/d3-scale-chromatic': 3.0.0
'@types/d3-selection': 3.0.5
'@types/d3-shape': 3.1.0
@@ -4685,17 +4685,6 @@ packages:
- vite
dev: true
/@unocss/astro@0.53.0(vite@4.3.9):
resolution: {integrity: sha512-8bR7ysIMZEOpcjd/cVmogcABSFDYPjUqMnbflv44p1A2/deemo9CIkpRARoq/96NQuzWJsKhKodcQodExZcqiA==}
dependencies:
'@unocss/core': 0.53.0
'@unocss/reset': 0.53.0
'@unocss/vite': 0.53.0(vite@4.3.9)
transitivePeerDependencies:
- rollup
- vite
dev: true
/@unocss/cli@0.53.0(rollup@2.79.1):
resolution: {integrity: sha512-9WNBHy8m8tMqwcp7mUhebRUBvHQfbx01CMe5cAFLmUYtJULM+8IjJxqERkaAZyyoOXf1TNO2v1dFAmCwhMRCLQ==}
engines: {node: '>=14'}
@@ -4874,26 +4863,6 @@ packages:
- rollup
dev: true
/@unocss/vite@0.53.0(vite@4.3.9):
resolution: {integrity: sha512-JoZhKVNruRjfySMVg/zNJbLEn/NTXj29Wf0SN4++xnGKrSapkPzYC46psL5bm5N5v4SHdpepTCoonC3FWCY6Fw==}
peerDependencies:
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
dependencies:
'@ampproject/remapping': 2.2.1
'@rollup/pluginutils': 5.0.2(rollup@2.79.1)
'@unocss/config': 0.53.0
'@unocss/core': 0.53.0
'@unocss/inspector': 0.53.0
'@unocss/scope': 0.53.0
'@unocss/transformer-directives': 0.53.0
chokidar: 3.5.3
fast-glob: 3.2.12
magic-string: 0.30.0
vite: 4.3.9(@types/node@18.16.0)
transitivePeerDependencies:
- rollup
dev: true
/@vite-pwa/vitepress@0.2.0(vite-plugin-pwa@0.16.0):
resolution: {integrity: sha512-dVQVaP6NB9woCFe4UASUqRp7uwBQJOVXlJlqK4krqXcbb3NuXIXIWOnU7HLpJnHqZj5U/81gKtLN6gs5gJBwiQ==}
peerDependencies:
@@ -7200,6 +7169,12 @@ packages:
lodash: 4.17.21
dev: false
/d3-array@2.12.1:
resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
dependencies:
internmap: 1.0.1
dev: false
/d3-array@3.2.0:
resolution: {integrity: sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==}
engines: {node: '>=12'}
@@ -7317,6 +7292,10 @@ packages:
d3-color: 3.1.0
dev: false
/d3-path@1.0.9:
resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
dev: false
/d3-path@3.0.1:
resolution: {integrity: sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==}
engines: {node: '>=12'}
@@ -7337,6 +7316,13 @@ packages:
engines: {node: '>=12'}
dev: false
/d3-sankey@0.12.3:
resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
dependencies:
d3-array: 2.12.1
d3-shape: 1.3.7
dev: false
/d3-scale-chromatic@3.0.0:
resolution: {integrity: sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==}
engines: {node: '>=12'}
@@ -7361,6 +7347,12 @@ packages:
engines: {node: '>=12'}
dev: false
/d3-shape@1.3.7:
resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
dependencies:
d3-path: 1.0.9
dev: false
/d3-shape@3.1.0:
resolution: {integrity: sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==}
engines: {node: '>=12'}
@@ -7765,8 +7757,8 @@ packages:
domelementtype: 2.3.0
dev: true
/dompurify@3.0.3:
resolution: {integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==}
/dompurify@3.0.4:
resolution: {integrity: sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ==}
dev: false
/domutils@3.0.1:
@@ -9044,7 +9036,7 @@ packages:
dependencies:
foreground-child: 3.1.1
jackspeak: 2.1.1
minimatch: 9.0.1
minimatch: 9.0.0
minipass: 5.0.0
path-scurry: 1.7.0
dev: true
@@ -9596,6 +9588,10 @@ packages:
side-channel: 1.0.4
dev: true
/internmap@1.0.1:
resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
dev: false
/internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
@@ -14636,42 +14632,6 @@ packages:
- vite
dev: true
/unocss@0.53.0(postcss@8.4.23)(vite@4.3.9):
resolution: {integrity: sha512-kY4h5ERiDYlSnL2X+hbDfh+uaF7QNouy7j51GOTUr3Q0aaWehaNd05b15SjHrab559dEC0mYfrSEdh/DnCK1cw==}
engines: {node: '>=14'}
peerDependencies:
'@unocss/webpack': 0.53.0
peerDependenciesMeta:
'@unocss/webpack':
optional: true
dependencies:
'@unocss/astro': 0.53.0(vite@4.3.9)
'@unocss/cli': 0.53.0(rollup@2.79.1)
'@unocss/core': 0.53.0
'@unocss/extractor-arbitrary-variants': 0.53.0
'@unocss/postcss': 0.53.0(postcss@8.4.23)
'@unocss/preset-attributify': 0.53.0
'@unocss/preset-icons': 0.53.0
'@unocss/preset-mini': 0.53.0
'@unocss/preset-tagify': 0.53.0
'@unocss/preset-typography': 0.53.0
'@unocss/preset-uno': 0.53.0
'@unocss/preset-web-fonts': 0.53.0
'@unocss/preset-wind': 0.53.0
'@unocss/reset': 0.53.0
'@unocss/transformer-attributify-jsx': 0.53.0
'@unocss/transformer-attributify-jsx-babel': 0.53.0
'@unocss/transformer-compile-class': 0.53.0
'@unocss/transformer-directives': 0.53.0
'@unocss/transformer-variant-group': 0.53.0
'@unocss/vite': 0.53.0(vite@4.3.9)
transitivePeerDependencies:
- postcss
- rollup
- supports-color
- vite
dev: true
/unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
@@ -15041,49 +15001,6 @@ packages:
- terser
dev: true
/vitepress@1.0.0-beta.2(@algolia/client-search@4.14.2)(@types/node@18.16.0)(search-insights@2.6.0):
resolution: {integrity: sha512-DBXYjtYbm3W1IPPJ2TiCaK/XK+o/2XmL2+jslOGKm+txcbmG0kbeB+vadC5tCUZA9NdA+9Ywj3M4548c7t/SDg==}
hasBin: true
dependencies:
'@docsearch/css': 3.5.1
'@docsearch/js': 3.5.1(@algolia/client-search@4.14.2)(search-insights@2.6.0)
'@vitejs/plugin-vue': 4.2.3(vite@4.3.9)(vue@3.3.4)
'@vue/devtools-api': 6.5.0
'@vueuse/core': 10.1.2(vue@3.3.4)
'@vueuse/integrations': 10.1.2(focus-trap@7.4.3)(vue@3.3.4)
body-scroll-lock: 4.0.0-beta.0
focus-trap: 7.4.3
mark.js: 8.11.1
minisearch: 6.1.0
shiki: 0.14.2
vite: 4.3.9(@types/node@18.16.0)
vue: 3.3.4
transitivePeerDependencies:
- '@algolia/client-search'
- '@types/node'
- '@types/react'
- '@vue/composition-api'
- async-validator
- axios
- change-case
- drauu
- fuse.js
- idb-keyval
- jwt-decode
- less
- nprogress
- qrcode
- react
- react-dom
- sass
- search-insights
- sortablejs
- stylus
- sugarss
- terser
- universal-cookie
dev: true
/vitepress@1.0.0-beta.3(@algolia/client-search@4.14.2)(@types/node@18.16.0)(search-insights@2.6.0):
resolution: {integrity: sha512-GR5Pvr/o343NN1M4Na1shhDYZRrQbjmLq7WE0lla0H8iDPAsHE8agTHLWfu3FWx+3q2KA29sv16+0O9RQKGjlA==}
hasBin: true

79
run
View File

@@ -1,40 +1,93 @@
#!/bin/bash
RUN="docker-compose run --rm"
ansi() { echo -e "\e[${1}m${*:2}\e[0m"; }
bold() { ansi 1 "$@"; }
# italic() { ansi 3 "$@"; }
underline() { ansi 4 "$@"; }
# strikethrough() { ansi 9 "$@"; }
# red() { ansi 31 "$@"; }
name=$(basename $0)
command=$1
args=${@:2}
case $command in
sh)
$RUN mermaid sh $args
$RUN mermaid sh -c "npx $args"
;;
install)
$RUN mermaid sh -c "npx pnpm install"
pnpm)
$RUN mermaid sh -c "npx pnpm $args"
;;
test)
$RUN mermaid sh -c "npx pnpm test"
dev)
$RUN --service-ports mermaid sh -c "npx pnpm run dev"
;;
lint)
$RUN mermaid sh -c "npx pnpm -w run lint:fix"
docs:dev)
$RUN --service-ports mermaid sh -c "cd packages/mermaid/src/docs && npx pnpm prefetch && npx vitepress --port 3333 --host"
;;
cypress)
$RUN cypress $args
;;
help)
# Alignment of help message must be as it is, it will be nice looking when printed
usage=$(
cat <<EOF
Run commonly used commands within docker containers
$0 install # Equvalent of pnpm install
$0 lint # Equvalent of pnpm -w run lint:fix
$0 sh # Open sh inside docker container for development
$(bold MERMAID LOCAL DOCKER DEVELOPMENT)
Welcome! Thank you for joining the development.
This is a script for running commands within docker containers at ease.
__________________________________________________________________________________________
Development quick start guide:
$(bold ./$name pnpm install) # Install packages
$(bold ./$name dev) # Run dev server with examples, open http://localhost:9000
$(bold ./$name pnpm vitest) # Run watcher for unit tests
$(bold ./$name cypress) # Run integration tests (after starting dev server)
$(bold ./$name pnpm build) # Prepare it for production
$(bold ./$name docs:dev) # Then add documentation, open http://localhost:3333
__________________________________________________________________________________________
Commands:
$(bold ./$name pnpm) # Run any 'pnpm' command
$(bold ./$name dev) # Run dev server with examples, open http://localhost:9000
$(bold ./$name docs:dev) # For docs contributions, open http://localhost:3333
$(bold ./$name cypress) # Run integration tests
$(bold ./$name sh) # Open 'sh' inside docker container for development
$(bold ./$name help) # Show this help
__________________________________________________________________________________________
Examples of frequiently used commands:
$(bold ./$name pnpm add --filter mermaid) $(underline package)
Add package to mermaid
$(bold git diff --name-only develop \| xargs ./$name pnpm prettier --write)
Prettify everything you added so far
$(bold ./$name cypress open --project .)
Open cypress interactive GUI
$(bold ./$name cypress run --spec cypress/integration/rendering/)$(underline test.spec.ts)
Run specific test in cypress\n
$0 help # Show this help
EOF
)
echo -n -e "$usage"
;;
*)
$0 help
$name help
;;
esac