mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-26 19:56:45 +02:00
Compare commits
179 Commits
v10.0.1
...
contributi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f510f28a22 | ||
![]() |
89a1b67ad8 | ||
![]() |
f08778d0c7 | ||
![]() |
37eb100bb2 | ||
![]() |
4643bb1c00 | ||
![]() |
d132d26246 | ||
![]() |
777a781052 | ||
![]() |
d1e369e68d | ||
![]() |
3b861ab91f | ||
![]() |
721208e981 | ||
![]() |
ee455c1edb | ||
![]() |
43137fbdb2 | ||
![]() |
7ac84fb9af | ||
![]() |
efc4062721 | ||
![]() |
8e6d16e601 | ||
![]() |
e96c8306d1 | ||
![]() |
756fc06c5d | ||
![]() |
80df10067d | ||
![]() |
e7c237dcf3 | ||
![]() |
75adb8ae90 | ||
![]() |
fc1962c795 | ||
![]() |
1a56a18f9b | ||
![]() |
1841346ff6 | ||
![]() |
e8799ad515 | ||
![]() |
2b9872d656 | ||
![]() |
9ffd4d2344 | ||
![]() |
815f4cab73 | ||
![]() |
5d536b9973 | ||
![]() |
6807c48f9a | ||
![]() |
c777f9193d | ||
![]() |
917a54f3cd | ||
![]() |
471c842a58 | ||
![]() |
99f65813a1 | ||
![]() |
7e610d13dc | ||
![]() |
64baf2d365 | ||
![]() |
f9c0f1d46f | ||
![]() |
3823ecafb1 | ||
![]() |
057c9e4b81 | ||
![]() |
e6db9e8fc3 | ||
![]() |
c4e4efd4b8 | ||
![]() |
da066553bd | ||
![]() |
a58d014a13 | ||
![]() |
006da82470 | ||
![]() |
1945a62990 | ||
![]() |
d16894daf4 | ||
![]() |
4d933f6b72 | ||
![]() |
7739302ee8 | ||
![]() |
46ab6f46f2 | ||
![]() |
48d267c6dc | ||
![]() |
15af3ea585 | ||
![]() |
35366f79ac | ||
![]() |
4c0980629c | ||
![]() |
8bd2c0f272 | ||
![]() |
2896865163 | ||
![]() |
a49cdabd6c | ||
![]() |
2dd6329872 | ||
![]() |
507a518a91 | ||
![]() |
4caf7d7c7b | ||
![]() |
89193d7360 | ||
![]() |
89eec225ce | ||
![]() |
cbc2df1ff6 | ||
![]() |
e4a2c74b1b | ||
![]() |
63160293c7 | ||
![]() |
9c0cb3f320 | ||
![]() |
fbeb016398 | ||
![]() |
093f1697e1 | ||
![]() |
f56e0bd530 | ||
![]() |
022e6670d0 | ||
![]() |
e3760d1709 | ||
![]() |
0475591fb6 | ||
![]() |
ba1c5dc6c7 | ||
![]() |
2f8c571a5c | ||
![]() |
a70b3a881d | ||
![]() |
950f560d81 | ||
![]() |
fd9680a050 | ||
![]() |
2d815e9626 | ||
![]() |
8c0550b2b7 | ||
![]() |
f054609e02 | ||
![]() |
45c0c5fee0 | ||
![]() |
a535fe1679 | ||
![]() |
e4d2118d4b | ||
![]() |
1184fce148 | ||
![]() |
a79f118323 | ||
![]() |
6cba2ea02d | ||
![]() |
3add711c55 | ||
![]() |
1481a8ccb1 | ||
![]() |
b3b7108d59 | ||
![]() |
fd9ad95346 | ||
![]() |
a1c50b8079 | ||
![]() |
8b37ceffe1 | ||
![]() |
7647ae317a | ||
![]() |
8c69ecd5ac | ||
![]() |
dc98fe6a3b | ||
![]() |
b56c8a2a7a | ||
![]() |
d18dff65e1 | ||
![]() |
2dc71377dc | ||
![]() |
89b9868870 | ||
![]() |
b232f20169 | ||
![]() |
2dd906d809 | ||
![]() |
fb70091046 | ||
![]() |
ddd245de71 | ||
![]() |
853d9b7f98 | ||
![]() |
f3a9f81bfb | ||
![]() |
4b462d717c | ||
![]() |
273a9e7ad6 | ||
![]() |
f75bd397f8 | ||
![]() |
f70d52510a | ||
![]() |
f0c24d9ec5 | ||
![]() |
708633f639 | ||
![]() |
2e174bb3b6 | ||
![]() |
160fe0f971 | ||
![]() |
58d4ba0d8f | ||
![]() |
155e729722 | ||
![]() |
a17463307b | ||
![]() |
4e4f2fcfc5 | ||
![]() |
759ab0c0f9 | ||
![]() |
1a7b8d3897 | ||
![]() |
f5e7abb71f | ||
![]() |
1412bb4e94 | ||
![]() |
328f3968d1 | ||
![]() |
c965e4c456 | ||
![]() |
86aa7ab91e | ||
![]() |
141d38b4e7 | ||
![]() |
98af37f09f | ||
![]() |
a57e392ed4 | ||
![]() |
993a19e15b | ||
![]() |
7bea44e752 | ||
![]() |
f0a73696f5 | ||
![]() |
878c9f1d9d | ||
![]() |
8ebd550e0b | ||
![]() |
6b5221e465 | ||
![]() |
8f0cb695e7 | ||
![]() |
1913aad03f | ||
![]() |
b80da0daa1 | ||
![]() |
c2035c3709 | ||
![]() |
d77be9546d | ||
![]() |
e9d49e6b98 | ||
![]() |
6c2c28940b | ||
![]() |
72c94b6e6e | ||
![]() |
44d806e7f5 | ||
![]() |
727c56dbb1 | ||
![]() |
526e8fa1ad | ||
![]() |
969088187c | ||
![]() |
649e6820cc | ||
![]() |
8027a0c55d | ||
![]() |
d65d4fc39f | ||
![]() |
f7f6cc73f5 | ||
![]() |
55ebfadb32 | ||
![]() |
c5a5a22b72 | ||
![]() |
ad52d7d823 | ||
![]() |
b9576b4bbe | ||
![]() |
f57fed0eb4 | ||
![]() |
4e344df204 | ||
![]() |
43762c4d7f | ||
![]() |
4d1d1c36de | ||
![]() |
e603840395 | ||
![]() |
c7bcd74d56 | ||
![]() |
20298d243a | ||
![]() |
51c6462f1d | ||
![]() |
6f3077c856 | ||
![]() |
8810b378b3 | ||
![]() |
c3064f396c | ||
![]() |
82f5b4ca39 | ||
![]() |
73ce499863 | ||
![]() |
1ecf15669a | ||
![]() |
d24ddca03f | ||
![]() |
a7847038a5 | ||
![]() |
b6db75fe3e | ||
![]() |
2a838e645c | ||
![]() |
61bf7c577c | ||
![]() |
704506835f | ||
![]() |
3bed70a0c5 | ||
![]() |
b079fb4710 | ||
![]() |
a2855931d2 | ||
![]() |
6e4e529af2 | ||
![]() |
067b6adc20 | ||
![]() |
a8162634cd | ||
![]() |
6ded32880d | ||
![]() |
03419c691c |
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -51,18 +51,10 @@ body:
|
|||||||
label: Setup
|
label: Setup
|
||||||
description: |-
|
description: |-
|
||||||
Please fill out the below info.
|
Please fill out the below info.
|
||||||
Note that you only need to fill out one and not both sections.
|
Note that you only need to fill out the relevant section
|
||||||
value: |-
|
value: |-
|
||||||
**Desktop**
|
- Mermaid version:
|
||||||
|
|
||||||
- OS and Version: [Windows, Linux, Mac, ...]
|
|
||||||
- Browser and Version: [Chrome, Edge, Firefox]
|
- Browser and Version: [Chrome, Edge, Firefox]
|
||||||
|
|
||||||
**Smartphone**
|
|
||||||
|
|
||||||
- Device: [Samsung, iPhone, ...]
|
|
||||||
- OS and Version: [Android, iOS, ...]
|
|
||||||
- Browser and Version: [Chrome, Safari, ...]
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional Context
|
label: Additional Context
|
||||||
|
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
@@ -21,11 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
# Need to skip setup if Cypress run is skipped, otherwise an error
|
|
||||||
# is thrown since the pnpm cache step fails
|
|
||||||
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
|
|
||||||
with:
|
with:
|
||||||
cache: pnpm
|
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
# Install NPM dependencies, cache them correctly
|
# Install NPM dependencies, cache them correctly
|
||||||
|
12
.github/workflows/link-checker.yml
vendored
12
.github/workflows/link-checker.yml
vendored
@@ -14,6 +14,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
# * is a special character in YAML so you have to quote this string
|
# * is a special character in YAML so you have to quote this string
|
||||||
- cron: '30 8 * * *'
|
- cron: '30 8 * * *'
|
||||||
@@ -35,9 +36,16 @@ jobs:
|
|||||||
restore-keys: cache-lychee-
|
restore-keys: cache-lychee-
|
||||||
|
|
||||||
- name: Link Checker
|
- name: Link Checker
|
||||||
uses: lycheeverse/lychee-action@v1.5.4
|
uses: lycheeverse/lychee-action@v1.6.1
|
||||||
with:
|
with:
|
||||||
args: --verbose --no-progress --cache --max-cache-age 1d packages/mermaid/src/docs/**/*.md README.md README.zh-CN.md
|
args: >-
|
||||||
|
--verbose
|
||||||
|
--no-progress
|
||||||
|
--cache
|
||||||
|
--max-cache-age 1d
|
||||||
|
packages/mermaid/src/docs/**/*.md
|
||||||
|
README.md
|
||||||
|
README.zh-CN.md
|
||||||
fail: true
|
fail: true
|
||||||
jobSummary: true
|
jobSummary: true
|
||||||
env:
|
env:
|
||||||
|
@@ -4,16 +4,8 @@
|
|||||||
# Network error: Forbidden
|
# Network error: Forbidden
|
||||||
https://codepen.io
|
https://codepen.io
|
||||||
|
|
||||||
# Network error: The certificate was not trusted
|
|
||||||
https://mkdocs.org/
|
|
||||||
https://osawards.com/javascript/#nominees
|
|
||||||
https://osawards.com/javascript/2019
|
|
||||||
|
|
||||||
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
# Timeout error, maybe Twitter has anti-bot defenses against GitHub's CI servers?
|
||||||
https://twitter.com/mermaidjs_
|
https://twitter.com/mermaidjs_
|
||||||
|
|
||||||
# Don't check files that are generated during the build via `pnpm docs:code`
|
# Don't check files that are generated during the build via `pnpm docs:code`
|
||||||
packages/mermaid/src/docs/config/setup/*
|
packages/mermaid/src/docs/config/setup/*
|
||||||
|
|
||||||
# Network error: 502, since few days
|
|
||||||
https://bundlephobia.com/
|
|
||||||
|
1
.npmrc
1
.npmrc
@@ -1,3 +1,2 @@
|
|||||||
auto-install-peers=true
|
auto-install-peers=true
|
||||||
strict-peer-dependencies=false
|
strict-peer-dependencies=false
|
||||||
use-inline-specifiers-lockfile-format=true
|
|
||||||
|
@@ -10,7 +10,7 @@ async function createServer() {
|
|||||||
configFile: './vite.config.ts',
|
configFile: './vite.config.ts',
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
server: { middlewareMode: true },
|
server: { middlewareMode: true },
|
||||||
appType: 'custom', // don't include Vite's default HTML handling middlewares
|
appType: 'custom', // don't include Vite's default HTML handling middleware
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
|
@@ -55,6 +55,8 @@ The documentation is written in **Markdown**. For more information about Markdow
|
|||||||
The source files for the project documentation are located in the [`/packages/mermaid/src/docs`](packages/mermaid/src/docs) directory. This is where you should make changes.
|
The source files for the project documentation are located in the [`/packages/mermaid/src/docs`](packages/mermaid/src/docs) directory. This is where you should make changes.
|
||||||
The files under `/packages/mermaid/src/docs` are processed to generate the published documentation, and the resulting files are put into the `/docs` directory.
|
The files under `/packages/mermaid/src/docs` are processed to generate the published documentation, and the resulting files are put into the `/docs` directory.
|
||||||
|
|
||||||
|
After editing files in the [`/packages/mermaid/src/docs`](packages/mermaid/src/docs) directory, be sure to run `pnpm install` and `pnpm run --filter mermaid docs:build` locally to build the `/docs` directory.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
classDef default fill:#fff,color:black,stroke:black
|
classDef default fill:#fff,color:black,stroke:black
|
||||||
|
38
README.md
38
README.md
@@ -226,6 +226,44 @@ pie
|
|||||||
|
|
||||||
### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>]
|
### Git graph [experimental - <a href="https://mermaid.live/edit#pako:eNqNkMFugzAMhl8F-VyVAR1tOW_aA-zKxSSGRCMJCk6lCvHuNZPKZdM0n-zf3_8r8QIqaIIGMqnB8kfEybQ--y4VnLP8-9RF9Mpkmm40hmlnDKmvkPiH_kfS7nFo_VN0FAf6XwocQGgxa_nGsm1bYEOOWmik1dRjGrmF1q-Cpkkj07u2HCI0PY4zHQATh8-7V9BwTPSE3iwOEd1OjQE1iWkBvk_bzQY7s0Sq4Hs7bHqKo8iGeZqbPN_WR7mpSd1RHpvPVhuMbG7XOq_L-oJlRfW5wteq0qorrpe-PBW9Pr8UJcK6rg-BLYPQ">live editor</a>]
|
||||||
|
|
||||||
|
### Bar chart (using gantt chart) [<a href="https://mermaid-js.github.io/mermaid/#/gantt">docs</a> - <a href="https://mermaid.live/edit#pako:eNptkU1vhCAQhv8KIenNugiI4rkf6bmXpvEyFVxJFDYyNt1u9r8X63Z7WQ9m5pknLzieaBeMpQ3dg0dsPUkPOhwteXZIXmJcbCT3xMAxkuh8Z8kIEclyMIB209fqKcwTICFvG4IvFy_oLrZ-g9F26ILfQgvNFN94VaRXQ1iWqpumZBcu1J8p1E1TXDx59eQNr5LyEqjJn6hv5QnGNlxevZJmdLLpy5xJSzut45biYCfb0iaVxvawjNjS1p-TCguG16PvaIPzYjO67e3BwX6GiTY9jPFKH43DMF_hGMDY1J4oHg-_f8hFTJFd8L3br3yZx4QHxENsdrt1nO8dDstH3oVpF50ZYMbhU6ud4qoGLqyqBJRCmO6j0HXPZdGbihUc6Pmc0QP49xD-b5X69ZQv2gjO81IwzWqhC1lKrjJ6pA3nVS7SMiVjrKirWlYp5fs3osgrWeo00lorLWvOzz8JVbXm">live editor</a>]
|
||||||
|
|
||||||
|
```
|
||||||
|
gantt
|
||||||
|
title Git Issues - days since last update
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %s
|
||||||
|
|
||||||
|
section Issue19062
|
||||||
|
71 : 0, 71
|
||||||
|
section Issue19401
|
||||||
|
36 : 0, 36
|
||||||
|
section Issue193
|
||||||
|
34 : 0, 34
|
||||||
|
section Issue7441
|
||||||
|
9 : 0, 9
|
||||||
|
section Issue1300
|
||||||
|
5 : 0, 5
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Git Issues - days since last update
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %s
|
||||||
|
|
||||||
|
section Issue19062
|
||||||
|
71 : 0, 71
|
||||||
|
section Issue19401
|
||||||
|
36 : 0, 36
|
||||||
|
section Issue193
|
||||||
|
34 : 0, 34
|
||||||
|
section Issue7441
|
||||||
|
9 : 0, 9
|
||||||
|
section Issue1300
|
||||||
|
5 : 0, 5
|
||||||
|
```
|
||||||
|
|
||||||
### User Journey diagram [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>]
|
### User Journey diagram [<a href="https://mermaid-js.github.io/mermaid/#/user-journey">docs</a> - <a href="https://mermaid.live/edit#pako:eNplkMFuwjAQRH9l5TMiTVIC-FqqnjhxzWWJN4khsSN7XRSh_HsdKBVt97R6Mzsj-yoqq0hIAXCywRkaSwNxWHNHsB_hYt1ZmwYUfiueKtbWwIcFtjf5zgH2eCZgQgkrCXt64GgMg2fUzkvIn5Xd_V5COtMFvCH_62ht_5yk7MU8sn61HDTfxD8VYiF6cj1qFd94nWkpuKWYKWRcFdUYOi5FaaZoDYNCpnel2Toha-w8LQQGtofRVEKyC_Qw7TQ2DvsfV2dRUTy6Ch6H-UMb7TlGVtbUupl5cF3ELfPgZZLM8rLR3IbjsrJ94rVq0XH7uS2SIis2mOVUrHNc5bmqjul2U2evaa3WL2mGYpqmL2BGiho">live editor</a>]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@@ -47,6 +47,7 @@
|
|||||||
"graphviz",
|
"graphviz",
|
||||||
"grav",
|
"grav",
|
||||||
"greywolf",
|
"greywolf",
|
||||||
|
"huynh",
|
||||||
"inkdrop",
|
"inkdrop",
|
||||||
"jaoude",
|
"jaoude",
|
||||||
"jison",
|
"jison",
|
||||||
@@ -90,6 +91,7 @@
|
|||||||
"sidharthv",
|
"sidharthv",
|
||||||
"sphinxcontrib",
|
"sphinxcontrib",
|
||||||
"statediagram",
|
"statediagram",
|
||||||
|
"steph",
|
||||||
"stylis",
|
"stylis",
|
||||||
"substate",
|
"substate",
|
||||||
"sveidqvist",
|
"sveidqvist",
|
||||||
|
@@ -10,7 +10,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render an ER diagram with a recursive relationship', () => {
|
it('should render an ER diagram with a recursive relationship', () => {
|
||||||
@@ -23,7 +22,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render an ER diagram with multiple relationships between the same two entities', () => {
|
it('should render an ER diagram with multiple relationships between the same two entities', () => {
|
||||||
@@ -35,7 +33,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a cyclical ER diagram', () => {
|
it('should render a cyclical ER diagram', () => {
|
||||||
@@ -48,7 +45,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a not-so-simple ER diagram', () => {
|
it('should render a not-so-simple ER diagram', () => {
|
||||||
@@ -66,7 +62,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render multiple ER diagrams', () => {
|
it('should render multiple ER diagrams', () => {
|
||||||
@@ -85,7 +80,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
],
|
],
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render an ER diagram with blank or empty labels', () => {
|
it('should render an ER diagram with blank or empty labels', () => {
|
||||||
@@ -98,7 +92,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render an ER diagrams when useMaxWidth is true (default)', () => {
|
it('should render an ER diagrams when useMaxWidth is true (default)', () => {
|
||||||
@@ -151,7 +144,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ er: { useMaxWidth: false } }
|
{ er: { useMaxWidth: false } }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with and without attributes', () => {
|
it('should render entities with and without attributes', () => {
|
||||||
@@ -164,7 +156,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with generic and array attributes', () => {
|
it('should render entities with generic and array attributes', () => {
|
||||||
@@ -179,7 +170,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with length in attributes type', () => {
|
it('should render entities with length in attributes type', () => {
|
||||||
@@ -193,7 +183,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities and attributes with big and small entity names', () => {
|
it('should render entities and attributes with big and small entity names', () => {
|
||||||
@@ -209,7 +198,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with keys', () => {
|
it('should render entities with keys', () => {
|
||||||
@@ -228,7 +216,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with comments', () => {
|
it('should render entities with comments', () => {
|
||||||
@@ -247,7 +234,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with keys and comments', () => {
|
it('should render entities with keys and comments', () => {
|
||||||
@@ -267,7 +253,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render entities with aliases', () => {
|
it('should render entities with aliases', () => {
|
||||||
@@ -285,7 +270,6 @@ describe('Entity Relationship Diagram', () => {
|
|||||||
`,
|
`,
|
||||||
{ logLevel: 1 }
|
{ logLevel: 1 }
|
||||||
);
|
);
|
||||||
cy.get('svg');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('1433: should render a simple ER diagram with a title', () => {
|
it('1433: should render a simple ER diagram with a title', () => {
|
||||||
|
45
cypress/integration/rendering/errorDiagram.spec.js
Normal file
45
cypress/integration/rendering/errorDiagram.spec.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { imgSnapshotTest } from '../../helpers/util';
|
||||||
|
|
||||||
|
describe('Error Diagrams', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.on('uncaught:exception', (err) => {
|
||||||
|
expect(err.message).to.include('Parse error');
|
||||||
|
// return false to prevent the error from
|
||||||
|
// failing this test
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a simple ER diagram', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
error
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render error diagram for actual errors', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
flowchart TD
|
||||||
|
A[Christmas] --|Get money| B(Go shopping)
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render error for wrong ER diagram', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
erDiagram
|
||||||
|
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
|
||||||
|
MONGODB-CLUSTERS ||..|{
|
||||||
|
ATLAS-TEAMS ||..|{
|
||||||
|
`,
|
||||||
|
{ logLevel: 1 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
@@ -684,4 +684,149 @@ A --> B
|
|||||||
{ titleTopMargin: 0 }
|
{ titleTopMargin: 0 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
describe('Markdown strings flowchart-elk (#4220)', () => {
|
||||||
|
describe('html labels', () => {
|
||||||
|
it('With styling and classes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
|
||||||
|
id1(Start)-->id2(Stop)
|
||||||
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||||
|
classDef someclass fill:#f96
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('With formatting in a node', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
a{"\`The **cat** in the hat\`"} -- 1o --> b
|
||||||
|
a -- 2o --> c
|
||||||
|
a -- 3o --> d
|
||||||
|
g --2i--> a
|
||||||
|
d --1i--> a
|
||||||
|
h --3i -->a
|
||||||
|
b --> d(The dog in the hog)
|
||||||
|
c --> d
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('New line in node and formatted edge label', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
b("\`The dog in **the** hog.(1)
|
||||||
|
NL\`") --"\`1o **bold**\`"--> c
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Wrapping long text with a new line', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
b(\`The dog in **the** hog.(1).. a a a a *very long text* about it
|
||||||
|
Word!
|
||||||
|
|
||||||
|
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`) --> c
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Sub graphs and markdown strings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
subgraph "One"
|
||||||
|
a("\`The **cat**
|
||||||
|
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
|
||||||
|
end
|
||||||
|
subgraph "\`**Two**\`"
|
||||||
|
c("\`The **cat**
|
||||||
|
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('svg text labels', () => {
|
||||||
|
it('With styling and classes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
|
||||||
|
id1(Start)-->id2(Stop)
|
||||||
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||||
|
classDef someclass fill:#f96
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('With formatting in a node', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
a{"\`The **cat** in the hat\`"} -- 1o --> b
|
||||||
|
a -- 2o --> c
|
||||||
|
a -- 3o --> d
|
||||||
|
g --2i--> a
|
||||||
|
d --1i--> a
|
||||||
|
h --3i -->a
|
||||||
|
b --> d(The dog in the hog)
|
||||||
|
c --> d
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('New line in node and formatted edge label', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
b("\`The dog in **the** hog.(1)
|
||||||
|
NL\`") --"\`1o **bold**\`"--> c
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Wrapping long text with a new line', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
|
||||||
|
Word!
|
||||||
|
|
||||||
|
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Sub graphs and markdown strings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart-elk LR
|
||||||
|
subgraph "One"
|
||||||
|
a("\`The **cat**
|
||||||
|
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
|
||||||
|
end
|
||||||
|
subgraph "\`**Two**\`"
|
||||||
|
c("\`The **cat**
|
||||||
|
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -685,4 +685,149 @@ A ~~~ B
|
|||||||
{ titleTopMargin: 0 }
|
{ titleTopMargin: 0 }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
describe('Markdown strings flowchart (#4220)', () => {
|
||||||
|
describe('html labels', () => {
|
||||||
|
it('With styling and classes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart LR
|
||||||
|
A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
|
||||||
|
id1(Start)-->id2(Stop)
|
||||||
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||||
|
classDef someclass fill:#f96
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('With formatting in a node', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart LR
|
||||||
|
a{"\`The **cat** in the hat\`"} -- 1o --> b
|
||||||
|
a -- 2o --> c
|
||||||
|
a -- 3o --> d
|
||||||
|
g --2i--> a
|
||||||
|
d --1i--> a
|
||||||
|
h --3i -->a
|
||||||
|
b --> d(The dog in the hog)
|
||||||
|
c --> d
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('New line in node and formatted edge label', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart LR
|
||||||
|
b("\`The dog in **the** hog.(1)
|
||||||
|
NL\`") --"\`1o **bold**\`"--> c
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Wrapping long text with a new line', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart LR
|
||||||
|
b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
|
||||||
|
Word!
|
||||||
|
|
||||||
|
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Sub graphs and markdown strings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": true}} }%%
|
||||||
|
flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("\`The **cat**
|
||||||
|
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
|
||||||
|
end
|
||||||
|
subgraph "\`**Two**\`"
|
||||||
|
c("\`The **cat**
|
||||||
|
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('svg text labels', () => {
|
||||||
|
it('With styling and classes', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
A:::someclass --> B["\`The **cat** in the hat\`"]:::someclass
|
||||||
|
id1(Start)-->id2(Stop)
|
||||||
|
style id1 fill:#f9f,stroke:#333,stroke-width:4px
|
||||||
|
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
|
||||||
|
classDef someclass fill:#f96
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('With formatting in a node', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
a{"\`The **cat** in the hat\`"} -- 1o --> b
|
||||||
|
a -- 2o --> c
|
||||||
|
a -- 3o --> d
|
||||||
|
g --2i--> a
|
||||||
|
d --1i--> a
|
||||||
|
h --3i -->a
|
||||||
|
b --> d(The dog in the hog)
|
||||||
|
c --> d
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('New line in node and formatted edge label', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
b("\`The dog in **the** hog.(1)
|
||||||
|
NL\`") --"\`1o **bold**\`"--> c
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Wrapping long text with a new line', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
b("\`The dog in **the** hog.(1).. a a a a *very long text* about it
|
||||||
|
Word!
|
||||||
|
|
||||||
|
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. \`") --> c
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('Sub graphs and markdown strings', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("\`The **cat**
|
||||||
|
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
|
||||||
|
end
|
||||||
|
subgraph "\`**Two**\`"
|
||||||
|
c("\`The **cat**
|
||||||
|
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -133,6 +133,24 @@ describe('Gantt diagram', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should default to showing today marker', () => {
|
||||||
|
// This test only works if the environment thinks today is 1010-10-10
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
gantt
|
||||||
|
title Show today marker (vertical line should be visible)
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
axisFormat %d
|
||||||
|
%% Should default to being on
|
||||||
|
%% todayMarker on
|
||||||
|
section Section1
|
||||||
|
Yesterday: 1010-10-09, 1d
|
||||||
|
Today: 1010-10-10, 1d
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should hide today marker', () => {
|
it('should hide today marker', () => {
|
||||||
imgSnapshotTest(
|
imgSnapshotTest(
|
||||||
`
|
`
|
||||||
@@ -142,7 +160,8 @@ describe('Gantt diagram', () => {
|
|||||||
axisFormat %d
|
axisFormat %d
|
||||||
todayMarker off
|
todayMarker off
|
||||||
section Section1
|
section Section1
|
||||||
Today: 1, -1h
|
Yesterday: 1010-10-09, 1d
|
||||||
|
Today: 1010-10-10, 1d
|
||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -157,7 +176,8 @@ describe('Gantt diagram', () => {
|
|||||||
axisFormat %d
|
axisFormat %d
|
||||||
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
|
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
|
||||||
section Section1
|
section Section1
|
||||||
Today: 1, -1h
|
Yesterday: 1010-10-09, 1d
|
||||||
|
Today: 1010-10-10, 1d
|
||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
@@ -435,4 +455,39 @@ describe('Gantt diagram', () => {
|
|||||||
{ gantt: { topAxis: true } }
|
{ gantt: { topAxis: true } }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render when compact is true', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
---
|
||||||
|
displayMode: compact
|
||||||
|
---
|
||||||
|
gantt
|
||||||
|
title GANTT compact
|
||||||
|
dateFormat HH:mm:ss
|
||||||
|
axisFormat %Hh%M
|
||||||
|
|
||||||
|
section DB Clean
|
||||||
|
Clean: 12:00:00, 10m
|
||||||
|
Clean: 12:30:00, 12m
|
||||||
|
Clean: 13:00:00, 8m
|
||||||
|
Clean: 13:30:00, 9m
|
||||||
|
Clean: 14:00:00, 13m
|
||||||
|
Clean: 14:30:00, 10m
|
||||||
|
Clean: 15:00:00, 11m
|
||||||
|
|
||||||
|
section Sessions
|
||||||
|
A: 12:00:00, 63m
|
||||||
|
B: 12:30:00, 12m
|
||||||
|
C: 13:05:00, 12m
|
||||||
|
D: 13:06:00, 33m
|
||||||
|
E: 13:15:00, 55m
|
||||||
|
F: 13:20:00, 12m
|
||||||
|
G: 13:32:00, 18m
|
||||||
|
H: 13:50:00, 20m
|
||||||
|
I: 14:10:00, 10m
|
||||||
|
`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -223,5 +223,18 @@ mindmap
|
|||||||
shouldHaveRoot
|
shouldHaveRoot
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
describe('Markdown strings mindmaps (#4220)', () => {
|
||||||
|
it('Formatted label with linebreak and a wrapping label and emojis', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`mindmap
|
||||||
|
id1[\`**Start** with
|
||||||
|
a second line 😎\`]
|
||||||
|
id2[\`The dog in **the** hog... a *very long text* about it
|
||||||
|
Word!\`]
|
||||||
|
`,
|
||||||
|
{ titleTopMargin: 0 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
/* The end */
|
/* The end */
|
||||||
});
|
});
|
||||||
|
@@ -75,4 +75,15 @@ describe('Pie Chart', () => {
|
|||||||
expect(svg).to.not.have.attr('style');
|
expect(svg).to.not.have.attr('style');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('should render a pie diagram when textPosition is set', () => {
|
||||||
|
imgSnapshotTest(
|
||||||
|
`
|
||||||
|
pie
|
||||||
|
"Dogs": 50
|
||||||
|
"Cats": 25
|
||||||
|
`,
|
||||||
|
{ logLevel: 1, pie: { textPosition: 0.9 } }
|
||||||
|
);
|
||||||
|
cy.get('svg');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -29,9 +29,9 @@
|
|||||||
}
|
}
|
||||||
.mermaid svg {
|
.mermaid svg {
|
||||||
/* font-size: 18px !important; */
|
/* font-size: 18px !important; */
|
||||||
background-color: #eee;
|
background-color: #efefef;
|
||||||
background-image: radial-gradient(#fff 1%, transparent 11%),
|
background-image: radial-gradient(#fff 51%, transparent 91%),
|
||||||
radial-gradient(#fff 1%, transparent 11%);
|
radial-gradient(#fff 51%, transparent 91%);
|
||||||
background-size: 20px 20px;
|
background-size: 20px 20px;
|
||||||
background-position: 0 0, 10px 10px;
|
background-position: 0 0, 10px 10px;
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
@@ -51,29 +51,103 @@
|
|||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 72px;
|
font-size: 72px;
|
||||||
}
|
}
|
||||||
|
/* tspan {
|
||||||
|
font-size: 6px !important;
|
||||||
|
} */
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<pre id="diagram" class="mermaid">
|
<pre id="diagram" class="mermaid">
|
||||||
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
stateDiagram-v2
|
||||||
graph BT
|
[*] --> Still
|
||||||
a{The cat in the hat} -- 1o --> b
|
Still --> [*]
|
||||||
a -- 2o --> c
|
Still --> Moving
|
||||||
a -- 3o --> d
|
Moving --> Still
|
||||||
g --2i--> a
|
Moving --> Crash
|
||||||
d --1i--> a
|
Crash --> [*] </pre
|
||||||
h --3i -->a
|
>
|
||||||
b --> d(The dog in the hog)
|
<pre id="diagram" class="mermaid2">
|
||||||
c --> d
|
flowchart RL
|
||||||
|
subgraph "`one`"
|
||||||
|
a1 -- l1 --> a2
|
||||||
|
a1 -- l2 --> a2
|
||||||
|
end
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid">
|
||||||
|
flowchart RL
|
||||||
|
subgraph "`one`"
|
||||||
|
a1 -- l1 --> a2
|
||||||
|
a1 -- l2 --> a2
|
||||||
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart-elk TB
|
flowchart
|
||||||
a --> b
|
id["`A root with a long text that wraps to keep the node size in check. A root with a long text that wraps to keep the node size in check`"]</pre
|
||||||
a --> c
|
>
|
||||||
b --> d
|
<pre id="diagram" class="mermaid2">
|
||||||
c --> d
|
flowchart LR
|
||||||
|
A[A text that needs to be wrapped wraps to another line]
|
||||||
|
B[A text that needs to be<br/>wrapped wraps to another line]
|
||||||
|
C["`A text that needs to be wrapped to another line`"]</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
flowchart LR
|
||||||
|
C["`A text
|
||||||
|
that needs
|
||||||
|
to be wrapped
|
||||||
|
in another
|
||||||
|
way`"]
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram" class="mermaid">
|
||||||
|
classDiagram-v2
|
||||||
|
note "I love this diagram!\nDo you love it?"
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid">
|
||||||
|
stateDiagram-v2
|
||||||
|
State1: The state with a note with minus - and plus + in it
|
||||||
|
note left of State1
|
||||||
|
Important information! You can write
|
||||||
|
notes with . and in them.
|
||||||
|
end note </pre
|
||||||
|
>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
mindmap
|
||||||
|
root
|
||||||
|
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
%%{init: {"theme": "forest"} }%%
|
||||||
|
mindmap
|
||||||
|
id1[**Start2**<br/>end]
|
||||||
|
id2[**Start2**<br />end]
|
||||||
|
%% Another comment
|
||||||
|
id3[**Start2**<br>end] %% Comment
|
||||||
|
id4[**Start2**<br >end<br >the very end]
|
||||||
</pre>
|
</pre>
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
|
mindmap
|
||||||
|
id1["`**Start2**
|
||||||
|
second line 😎 with long text that is wrapping to the next line`"]
|
||||||
|
id2["`Child **with bold** text`"]
|
||||||
|
id3["`Children of which some
|
||||||
|
is using *italic type of* text`"]
|
||||||
|
id4[Child]
|
||||||
|
id5["`Child
|
||||||
|
Row
|
||||||
|
and another
|
||||||
|
`"]
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
mindmap
|
||||||
|
id1("`**Root**`"]
|
||||||
|
id2["`A formatted text... with **bold** and *italics*`"]
|
||||||
|
id3[Regular labels works as usual]
|
||||||
|
id4["`Emojis and unicode works too: 🤓
|
||||||
|
शान्तिः سلام 和平 `"]
|
||||||
|
|
||||||
|
</pre>
|
||||||
|
<pre id="diagram" class="mermaid">
|
||||||
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
|
||||||
flowchart TB
|
flowchart TB
|
||||||
%% I could not figure out how to use double quotes in labels in Mermaid
|
%% I could not figure out how to use double quotes in labels in Mermaid
|
||||||
@@ -89,7 +163,7 @@ flowchart TB
|
|||||||
rom --> core2
|
rom --> core2
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph amd[AMD Latte GPU]
|
subgraph amd["`**AMD** Latte GPU`"]
|
||||||
mem[Memory & I/O Bridge]
|
mem[Memory & I/O Bridge]
|
||||||
dram[DRAM Controller]
|
dram[DRAM Controller]
|
||||||
edram[32 MB EDRAM MEM1]
|
edram[32 MB EDRAM MEM1]
|
||||||
@@ -128,6 +202,62 @@ flowchart TB
|
|||||||
rtc{{rtc}}
|
rtc{{rtc}}
|
||||||
</pre
|
</pre
|
||||||
>
|
>
|
||||||
|
<pre id="diagram" class="mermaid2">
|
||||||
|
%%{init: {"flowchart": {"defaultRenderer": "elk", "htmlLabels": false}} }%%
|
||||||
|
flowchart TB
|
||||||
|
%% I could not figure out how to use double quotes in labels in Mermaid
|
||||||
|
subgraph ibm[IBM Espresso CPU]
|
||||||
|
core0[IBM PowerPC Broadway Core 0]
|
||||||
|
core1[IBM PowerPC Broadway Core 1]
|
||||||
|
core2[IBM PowerPC Broadway Core 2]
|
||||||
|
|
||||||
|
rom[16 KB ROM]
|
||||||
|
|
||||||
|
core0 --- core2
|
||||||
|
|
||||||
|
rom --> core2
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph amd["`**AMD** Latte GPU`"]
|
||||||
|
mem[Memory & I/O Bridge]
|
||||||
|
dram[DRAM Controller]
|
||||||
|
edram[32 MB EDRAM MEM1]
|
||||||
|
rom[512 B SEEPROM]
|
||||||
|
|
||||||
|
sata[SATA IF]
|
||||||
|
exi[EXI]
|
||||||
|
|
||||||
|
subgraph gx[GX]
|
||||||
|
sram[3 MB 1T-SRAM]
|
||||||
|
end
|
||||||
|
|
||||||
|
radeon[AMD Radeon R7xx GX2]
|
||||||
|
|
||||||
|
mem --- gx
|
||||||
|
mem --- radeon
|
||||||
|
|
||||||
|
rom --- mem
|
||||||
|
|
||||||
|
mem --- sata
|
||||||
|
mem --- exi
|
||||||
|
|
||||||
|
dram --- sata
|
||||||
|
dram --- exi
|
||||||
|
end
|
||||||
|
|
||||||
|
ddr3[2 GB DDR3 RAM MEM2]
|
||||||
|
|
||||||
|
mem --- ddr3
|
||||||
|
dram --- ddr3
|
||||||
|
edram --- ddr3
|
||||||
|
|
||||||
|
core1 --- mem
|
||||||
|
|
||||||
|
exi --- rtc
|
||||||
|
rtc{{rtc}}
|
||||||
|
</pre
|
||||||
|
>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<pre id="diagram" class="mermaid2">
|
<pre id="diagram" class="mermaid2">
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -270,14 +400,16 @@ mindmap
|
|||||||
// console.error('Mermaid error: ', err);
|
// console.error('Mermaid error: ', err);
|
||||||
};
|
};
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
theme: 'forest',
|
// theme: 'forest',
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
logLevel: 5,
|
logLevel: 0,
|
||||||
flowchart: {
|
flowchart: {
|
||||||
// defaultRenderer: 'elk',
|
// defaultRenderer: 'elk',
|
||||||
useMaxWidth: false,
|
useMaxWidth: false,
|
||||||
|
// htmlLabels: false,
|
||||||
htmlLabels: true,
|
htmlLabels: true,
|
||||||
},
|
},
|
||||||
|
// htmlLabels: false,
|
||||||
gantt: {
|
gantt: {
|
||||||
useMaxWidth: false,
|
useMaxWidth: false,
|
||||||
},
|
},
|
||||||
|
@@ -47,7 +47,6 @@ const contentLoaded = async function () {
|
|||||||
await mermaid2.registerExternalDiagrams([externalExample]);
|
await mermaid2.registerExternalDiagrams([externalExample]);
|
||||||
mermaid2.initialize(graphObj.mermaid);
|
mermaid2.initialize(graphObj.mermaid);
|
||||||
await mermaid2.run();
|
await mermaid2.run();
|
||||||
markRendered();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,7 +122,6 @@ const contentLoadedApi = async function () {
|
|||||||
bindFunctions(div);
|
bindFunctions(div);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
markRendered();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
@@ -135,10 +133,10 @@ if (typeof document !== 'undefined') {
|
|||||||
function () {
|
function () {
|
||||||
if (this.location.href.match('xss.html')) {
|
if (this.location.href.match('xss.html')) {
|
||||||
this.console.log('Using api');
|
this.console.log('Using api');
|
||||||
void contentLoadedApi();
|
void contentLoadedApi().finally(markRendered);
|
||||||
} else {
|
} else {
|
||||||
this.console.log('Not using api');
|
this.console.log('Not using api');
|
||||||
void contentLoaded();
|
void contentLoaded().finally(markRendered);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
38
demos/error.html
Normal file
38
demos/error.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>Error | Mermaid Quick Test Page</title>
|
||||||
|
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<pre class="mermaid">
|
||||||
|
erDiagram
|
||||||
|
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
|
||||||
|
</pre>
|
||||||
|
<pre class="mermaid">
|
||||||
|
erDiagram
|
||||||
|
ATLAS-ORGANIZATION ||--|{ ATLAS-PROJECTS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ MONGODB-CLUSTERS : "has many"
|
||||||
|
ATLAS-PROJECTS ||--|{ ATLAS-TEAMS : "has many"
|
||||||
|
MONGODB-CLUSTERS ||..|{
|
||||||
|
ATLAS-TEAMS ||..|{
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
<pre class="mermaid">
|
||||||
|
flowchart TD
|
||||||
|
A[Christmas] -->|Get money| B(Go shopping)
|
||||||
|
</pre>
|
||||||
|
<pre class="mermaid">
|
||||||
|
flowchart TD
|
||||||
|
A[Christmas] --|Get money| B(Go shopping)
|
||||||
|
</pre>
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -78,7 +78,7 @@
|
|||||||
axisFormat %d/%m
|
axisFormat %d/%m
|
||||||
todayMarker off
|
todayMarker off
|
||||||
section Section1
|
section Section1
|
||||||
Today: 1, -01:00, 5min
|
Today: 1, 08-08-09-01:00, 5min
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
axisFormat %d/%m
|
axisFormat %d/%m
|
||||||
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
|
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
|
||||||
section Section1
|
section Section1
|
||||||
Today: 1, -01:00, 5min
|
Today: 1, 08-08-09-01:00, 5min
|
||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
@@ -166,6 +166,37 @@
|
|||||||
</pre>
|
</pre>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
<pre class="mermaid">
|
||||||
|
---
|
||||||
|
displayMode: compact
|
||||||
|
---
|
||||||
|
gantt
|
||||||
|
title GANTT compact
|
||||||
|
dateFormat HH:mm:ss
|
||||||
|
axisFormat %Hh%M
|
||||||
|
|
||||||
|
section DB Clean
|
||||||
|
Clean: 12:00:00, 10m
|
||||||
|
Clean: 12:30:00, 12m
|
||||||
|
Clean: 13:00:00, 8m
|
||||||
|
Clean: 13:30:00, 9m
|
||||||
|
Clean: 14:00:00, 13m
|
||||||
|
Clean: 14:30:00, 10m
|
||||||
|
Clean: 15:00:00, 11m
|
||||||
|
|
||||||
|
section Sessions
|
||||||
|
A: 12:00:00, 63m
|
||||||
|
B: 12:30:00, 12m
|
||||||
|
C: 13:05:00, 12m
|
||||||
|
D: 13:06:00, 33m
|
||||||
|
E: 13:15:00, 55m
|
||||||
|
F: 13:20:00, 12m
|
||||||
|
G: 13:32:00, 18m
|
||||||
|
H: 13:50:00, 20m
|
||||||
|
I: 14:10:00, 10m
|
||||||
|
</pre>
|
||||||
|
<hr />
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function ganttTestClick(a, b, c) {
|
function ganttTestClick(a, b, c) {
|
||||||
console.log('a:', a);
|
console.log('a:', a);
|
||||||
|
@@ -81,7 +81,6 @@
|
|||||||
mermaid.parseError = function (err, hash) {
|
mermaid.parseError = function (err, hash) {
|
||||||
// console.error('Mermaid error: ', err);
|
// console.error('Mermaid error: ', err);
|
||||||
};
|
};
|
||||||
await mermaid.registerExternalDiagrams([mermaidMindmap]);
|
|
||||||
mermaid.initialize({
|
mermaid.initialize({
|
||||||
theme: 'base',
|
theme: 'base',
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
<pre class="mermaid">
|
<pre class="mermaid">
|
||||||
|
%%{init: {"pie": {"textPosition": 0.9}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
|
||||||
pie
|
pie
|
||||||
title Key elements in Product X
|
title Key elements in Product X
|
||||||
accTitle: Key elements in Product X
|
accTitle: Key elements in Product X
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
# Tutorials
|
# Tutorials
|
||||||
|
|
||||||
This is list of publicly available Tutorials for using Mermaid.JS . This is intended as a basic introduction for the use of the Live Editor for generating diagrams, and deploying Mermaid.JS through HTML.
|
This is a list of publicly available Tutorials for using Mermaid.JS and is intended as a basic introduction for the use of the Live Editor for generating diagrams, and deploying Mermaid.JS through HTML.
|
||||||
|
|
||||||
**Note that these tutorials might display an older interface, but the usage of the live-editor will largely be the same.**
|
**Note that these tutorials might display an older interface, but the usage of the live-editor will largely be the same.**
|
||||||
|
|
||||||
|
@@ -16,4 +16,4 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L70)
|
[mermaidAPI.ts:77](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L77)
|
||||||
|
@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:91](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L91)
|
[mermaidAPI.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L98)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -51,4 +51,4 @@ The svg code for the rendered graph.
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:81](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L81)
|
[mermaidAPI.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L88)
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[defaultConfig.ts:2084](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2084)
|
[defaultConfig.ts:2115](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2115)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -25,13 +25,13 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L75)
|
[mermaidAPI.ts:82](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L82)
|
||||||
|
|
||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
### mermaidAPI
|
### mermaidAPI
|
||||||
|
|
||||||
• `Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean` | `void`> ; `parseDirective`: (`p`: `any`, `statement`: `string`, `context`: `string`, `type`: `string`) => `void` ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
|
• `Const` **mermaidAPI**: `Readonly`<{ `defaultConfig`: `MermaidConfig` = configApi.defaultConfig; `getConfig`: () => `MermaidConfig` = configApi.getConfig; `getDiagramFromText`: (`text`: `string`) => `Promise`<`Diagram`> ; `getSiteConfig`: () => `MermaidConfig` = configApi.getSiteConfig; `globalReset`: () => `void` ; `initialize`: (`options`: `MermaidConfig`) => `void` ; `parse`: (`text`: `string`, `parseOptions?`: [`ParseOptions`](../interfaces/mermaidAPI.ParseOptions.md)) => `Promise`<`boolean`> ; `parseDirective`: (`p`: `any`, `statement`: `string`, `context`: `string`, `type`: `string`) => `void` ; `render`: (`id`: `string`, `text`: `string`, `svgContainingElement?`: `Element`) => `Promise`<[`RenderResult`](../interfaces/mermaidAPI.RenderResult.md)> ; `reset`: () => `void` ; `setConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.setConfig; `updateSiteConfig`: (`conf`: `MermaidConfig`) => `MermaidConfig` = configApi.updateSiteConfig }>
|
||||||
|
|
||||||
## mermaidAPI configuration defaults
|
## mermaidAPI configuration defaults
|
||||||
|
|
||||||
@@ -88,6 +88,7 @@ const config = {
|
|||||||
numberSectionStyles: 4,
|
numberSectionStyles: 4,
|
||||||
axisFormat: '%Y-%m-%d',
|
axisFormat: '%Y-%m-%d',
|
||||||
topAxis: false,
|
topAxis: false,
|
||||||
|
displayMode: '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
mermaid.initialize(config);
|
mermaid.initialize(config);
|
||||||
@@ -95,7 +96,7 @@ mermaid.initialize(config);
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:662](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L662)
|
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667)
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ Return the last node appended
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:308](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L308)
|
[mermaidAPI.ts:312](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L312)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ the cleaned up svgCode
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:259](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L259)
|
[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@ the string with all the user styles
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:188](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L188)
|
[mermaidAPI.ts:192](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L192)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -201,7 +202,7 @@ the string with all the user styles
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:236](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L236)
|
[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -228,7 +229,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L172)
|
[mermaidAPI.ts:176](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L176)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -248,7 +249,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L152)
|
[mermaidAPI.ts:156](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L156)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -268,7 +269,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L123)
|
[mermaidAPI.ts:127](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L127)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -294,7 +295,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
|
[mermaidAPI.ts:291](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L291)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -319,4 +320,4 @@ Remove any existing elements from the given document
|
|||||||
|
|
||||||
#### Defined in
|
#### Defined in
|
||||||
|
|
||||||
[mermaidAPI.ts:358](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L358)
|
[mermaidAPI.ts:362](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L362)
|
||||||
|
@@ -261,6 +261,34 @@ The theming engine will only recognize hex colors and not color names. So, the v
|
|||||||
| activationBkgColor | secondaryColor | Activation Background Color |
|
| activationBkgColor | secondaryColor | Activation Background Color |
|
||||||
| sequenceNumberColor | calculated from lineColor | Sequence Number Color |
|
| sequenceNumberColor | calculated from lineColor | Sequence Number Color |
|
||||||
|
|
||||||
|
## Pie Diagram Variables
|
||||||
|
|
||||||
|
| Variable | Default value | Description |
|
||||||
|
| ------------------- | ------------------------------ | ------------------------------------------ |
|
||||||
|
| pie1 | primaryColor | Fill for 1st section in pie diagram |
|
||||||
|
| pie2 | secondaryColor | Fill for 2nd section in pie diagram |
|
||||||
|
| pie3 | calculated from tertiary | Fill for 3rd section in pie diagram |
|
||||||
|
| pie4 | calculated from primaryColor | Fill for 4th section in pie diagram |
|
||||||
|
| pie5 | calculated from secondaryColor | Fill for 5th section in pie diagram |
|
||||||
|
| pie6 | calculated from tertiaryColor | Fill for 6th section in pie diagram |
|
||||||
|
| pie7 | calculated from primaryColor | Fill for 7th section in pie diagram |
|
||||||
|
| pie8 | calculated from primaryColor | Fill for 8th section in pie diagram |
|
||||||
|
| pie9 | calculated from primaryColor | Fill for 9th section in pie diagram |
|
||||||
|
| pie10 | calculated from primaryColor | Fill for 10th section in pie diagram |
|
||||||
|
| pie11 | calculated from primaryColor | Fill for 11th section in pie diagram |
|
||||||
|
| pie12 | calculated from primaryColor | Fill for 12th section in pie diagram |
|
||||||
|
| pieTitleTextSize | 25px | Title text size |
|
||||||
|
| pieTitleTextColor | taskTextDarkColor | Title text color |
|
||||||
|
| pieSectionTextSize | 17px | Text size of individual section labels |
|
||||||
|
| pieSectionTextColor | textColor | Text color of individual section labels |
|
||||||
|
| pieLegendTextSize | 17px | Text size of labels in diagram legend |
|
||||||
|
| pieLegendTextColor | taskTextDarkColor | Text color of labels in diagram legend |
|
||||||
|
| pieStrokeColor | black | Border color of individual pie sections |
|
||||||
|
| pieStrokeWidth | 2px | Border width of individual pie sections |
|
||||||
|
| pieOuterStrokeWidth | 2px | Border width of pie diagram's outer circle |
|
||||||
|
| pieOuterStrokeColor | black | Border color of pie diagram's outer circle |
|
||||||
|
| pieOpacity | 0.7 | Opacity of individual pie sections |
|
||||||
|
|
||||||
## State Colors
|
## State Colors
|
||||||
|
|
||||||
| Variable | Default value | Description |
|
| Variable | Default value | Description |
|
||||||
|
@@ -247,6 +247,23 @@ The example below show an outline of how this could be used. The example just lo
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To determine the type of diagram present in a given text, you can utilize the `mermaid.detectType` function, as demonstrated in the example below.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
const graphDefinition = `sequenceDiagram
|
||||||
|
Pumbaa->>Timon:I ate like a pig.
|
||||||
|
Timon->>Pumbaa:Pumbaa, you ARE a pig.`;
|
||||||
|
try {
|
||||||
|
const type = mermaid.detectType(graphDefinition);
|
||||||
|
console.log(type); // 'sequence'
|
||||||
|
} catch (error) {
|
||||||
|
// UnknownDiagramError
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
### Binding events
|
### Binding events
|
||||||
|
|
||||||
Sometimes the generated graph also has defined interactions like tooltip and click events. When using the API one must
|
Sometimes the generated graph also has defined interactions like tooltip and click events. When using the API one must
|
||||||
|
@@ -20,6 +20,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Gitea](https://gitea.io) (**Native support**)
|
- [Gitea](https://gitea.io) (**Native support**)
|
||||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||||
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
||||||
|
- [Mermaid Flow Visual Editor](https://www.mermaidflow.app) (**Native support**)
|
||||||
- [Deepdwn](https://billiam.itch.io/deepdwn) (**Native support**)
|
- [Deepdwn](https://billiam.itch.io/deepdwn) (**Native support**)
|
||||||
- [Joplin](https://joplinapp.org) (**Native support**)
|
- [Joplin](https://joplinapp.org) (**Native support**)
|
||||||
- [Swimm](https://swimm.io) (**Native support**)
|
- [Swimm](https://swimm.io) (**Native support**)
|
||||||
@@ -88,7 +89,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [FosWiki](https://foswiki.org)
|
- [FosWiki](https://foswiki.org)
|
||||||
- [Mermaid Plugin](https://foswiki.org/Extensions/MermaidPlugin)
|
- [Mermaid Plugin](https://foswiki.org/Extensions/MermaidPlugin)
|
||||||
- [DokuWiki](https://dokuwiki.org)
|
- [DokuWiki](https://dokuwiki.org)
|
||||||
- [Flowcharts](https://www.dokuwiki.org/plugin:flowcharts?s[]=mermaid)
|
- [Mermaid Plugin](https://www.dokuwiki.org/plugin:mermaid)
|
||||||
- [ComboStrap](https://combostrap.com/mermaid)
|
- [ComboStrap](https://combostrap.com/mermaid)
|
||||||
- [TiddlyWiki](https://tiddlywiki.com/)
|
- [TiddlyWiki](https://tiddlywiki.com/)
|
||||||
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
|
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
|
||||||
@@ -149,7 +150,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [remark-mermaid](https://github.com/temando/remark-mermaid)
|
- [remark-mermaid](https://github.com/temando/remark-mermaid)
|
||||||
- [jSDoc](https://jsdoc.app/)
|
- [jSDoc](https://jsdoc.app/)
|
||||||
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
|
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
|
||||||
- [MkDocs](https://mkdocs.org)
|
- [MkDocs](https://www.mkdocs.org)
|
||||||
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
|
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
|
||||||
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
|
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
|
||||||
- [Type Doc](https://typedoc.org/)
|
- [Type Doc](https://typedoc.org/)
|
||||||
|
13
docs/news/announcements.md
Normal file
13
docs/news/announcements.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
> **Warning**
|
||||||
|
>
|
||||||
|
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||||
|
>
|
||||||
|
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/news/announcements.md](../../packages/mermaid/src/docs/news/announcements.md).
|
||||||
|
|
||||||
|
# Announcements
|
||||||
|
|
||||||
|
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
|
||||||
|
|
||||||
|
3 April 2023 · 3 mins
|
||||||
|
|
||||||
|
Markdown Strings reduce the hassle # Starting from v10.
|
31
docs/news/blog.md
Normal file
31
docs/news/blog.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
> **Warning**
|
||||||
|
>
|
||||||
|
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||||
|
>
|
||||||
|
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/news/blog.md](../../packages/mermaid/src/docs/news/blog.md).
|
||||||
|
|
||||||
|
# Blog
|
||||||
|
|
||||||
|
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
|
||||||
|
|
||||||
|
27 March 2023 · 2 mins
|
||||||
|
|
||||||
|
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
|
||||||
|
|
||||||
|
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
|
||||||
|
|
||||||
|
8 March 2023 · 9 mins
|
||||||
|
|
||||||
|
The hype around AI in general and ChatGPT, in particular, is so intense that it’s very understandable to assume the hype train is driving straight toward the trough of disillusionment.
|
||||||
|
|
||||||
|
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
|
||||||
|
|
||||||
|
1 March 2023 · 12 mins
|
||||||
|
|
||||||
|
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
|
||||||
|
|
||||||
|
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
|
||||||
|
|
||||||
|
10 February 2023 · 10 mins
|
||||||
|
|
||||||
|
Busting the myth that developers can’t write # It’s an annoying stereotype that developers don’t know how to write, speak, and otherwise communicate.
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 12 KiB |
@@ -183,20 +183,6 @@ flowchart LR
|
|||||||
|
|
||||||
### A hexagon node
|
### A hexagon node
|
||||||
|
|
||||||
Code:
|
|
||||||
|
|
||||||
```mermaid-example
|
|
||||||
flowchart LR
|
|
||||||
id1{{This is the text in the box}}
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
id1{{This is the text in the box}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Render:
|
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
flowchart LR
|
flowchart LR
|
||||||
id1{{This is the text in the box}}
|
id1{{This is the text in the box}}
|
||||||
@@ -724,6 +710,44 @@ flowchart LR
|
|||||||
B1 --> B2
|
B1 --> B2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Markdown Strings
|
||||||
|
|
||||||
|
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("`The **cat**
|
||||||
|
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
|
||||||
|
end
|
||||||
|
subgraph "`**Two**`"
|
||||||
|
c("`The **cat**
|
||||||
|
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("`The **cat**
|
||||||
|
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
|
||||||
|
end
|
||||||
|
subgraph "`**Two**`"
|
||||||
|
c("`The **cat**
|
||||||
|
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Formatting:
|
||||||
|
|
||||||
|
- For bold text, use double asterisks \*\* before and after the text.
|
||||||
|
- For italics, use single asterisks \* before and after the text.
|
||||||
|
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
|
||||||
|
|
||||||
|
This feature is applicable to node labels, edge labels, and subgraph labels.
|
||||||
|
|
||||||
## Interaction
|
## Interaction
|
||||||
|
|
||||||
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||||
|
@@ -257,9 +257,41 @@ The pattern is:
|
|||||||
|
|
||||||
More info in: <https://github.com/d3/d3-time#interval_every>
|
More info in: <https://github.com/d3/d3-time#interval_every>
|
||||||
|
|
||||||
|
## Output in compact mode
|
||||||
|
|
||||||
|
The compact mode allows you to display multiple tasks in the same row. Compact mode can be enabled for a gantt chart by setting the display mode of the graph via preceeding YAML settings.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
---
|
||||||
|
displayMode: compact
|
||||||
|
---
|
||||||
|
gantt
|
||||||
|
title A Gantt Diagram
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
|
||||||
|
section Section
|
||||||
|
A task :a1, 2014-01-01, 30d
|
||||||
|
Another task :a2, 2014-01-20, 25d
|
||||||
|
Another one :a3, 2014-02-10, 20d
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
---
|
||||||
|
displayMode: compact
|
||||||
|
---
|
||||||
|
gantt
|
||||||
|
title A Gantt Diagram
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
|
||||||
|
section Section
|
||||||
|
A task :a1, 2014-01-01, 30d
|
||||||
|
Another task :a2, 2014-01-20, 25d
|
||||||
|
Another one :a3, 2014-02-10, 20d
|
||||||
|
```
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
|
|
||||||
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
|
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax.
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
gantt
|
gantt
|
||||||
@@ -432,3 +464,41 @@ Beginner's tip—a full example using interactive links in an html context:
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Bar chart (using gantt chart)
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
gantt
|
||||||
|
title Git Issues - days since last update
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %s
|
||||||
|
section Issue19062
|
||||||
|
71 : 0, 71
|
||||||
|
section Issue19401
|
||||||
|
36 : 0, 36
|
||||||
|
section Issue193
|
||||||
|
34 : 0, 34
|
||||||
|
section Issue7441
|
||||||
|
9 : 0, 9
|
||||||
|
section Issue1300
|
||||||
|
5 : 0, 5
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
gantt
|
||||||
|
title Git Issues - days since last update
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %s
|
||||||
|
section Issue19062
|
||||||
|
71 : 0, 71
|
||||||
|
section Issue19401
|
||||||
|
36 : 0, 36
|
||||||
|
section Issue193
|
||||||
|
34 : 0, 34
|
||||||
|
section Issue7441
|
||||||
|
9 : 0, 9
|
||||||
|
section Issue1300
|
||||||
|
5 : 0, 5
|
||||||
|
```
|
||||||
|
@@ -224,7 +224,7 @@ mindmap
|
|||||||
C
|
C
|
||||||
```
|
```
|
||||||
|
|
||||||
_These classes needs top be supplied by the site administrator._
|
_These classes need to be supplied by the site administrator._
|
||||||
|
|
||||||
## Unclear indentation
|
## Unclear indentation
|
||||||
|
|
||||||
@@ -254,6 +254,34 @@ Root
|
|||||||
C
|
C
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Markdown Strings
|
||||||
|
|
||||||
|
The "Markdown Strings" feature enhances mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
mindmap
|
||||||
|
id1["`**Root** with
|
||||||
|
a second line
|
||||||
|
Unicode works too: 🤓`"]
|
||||||
|
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
|
||||||
|
id3[Regular labels still works]
|
||||||
|
```
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
mindmap
|
||||||
|
id1["`**Root** with
|
||||||
|
a second line
|
||||||
|
Unicode works too: 🤓`"]
|
||||||
|
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
|
||||||
|
id3[Regular labels still works]
|
||||||
|
```
|
||||||
|
|
||||||
|
Formatting:
|
||||||
|
|
||||||
|
- For bold text, use double asterisks \*\* before and after the text.
|
||||||
|
- For italics, use single asterisks \* before and after the text.
|
||||||
|
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
|
||||||
|
|
||||||
## Integrating with your library/website.
|
## Integrating with your library/website.
|
||||||
|
|
||||||
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.
|
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.
|
||||||
|
@@ -48,6 +48,7 @@ Drawing a pie chart is really simple in mermaid.
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
|
%%{init: {"pie": {"textPosition": 0.5}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
|
||||||
pie showData
|
pie showData
|
||||||
title Key elements in Product X
|
title Key elements in Product X
|
||||||
"Calcium" : 42.96
|
"Calcium" : 42.96
|
||||||
@@ -57,6 +58,7 @@ pie showData
|
|||||||
```
|
```
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
|
%%{init: {"pie": {"textPosition": 0.5}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
|
||||||
pie showData
|
pie showData
|
||||||
title Key elements in Product X
|
title Key elements in Product X
|
||||||
"Calcium" : 42.96
|
"Calcium" : 42.96
|
||||||
@@ -64,3 +66,11 @@ pie showData
|
|||||||
"Magnesium" : 10.01
|
"Magnesium" : 10.01
|
||||||
"Iron" : 5
|
"Iron" : 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Possible pie diagram configuration parameters:
|
||||||
|
|
||||||
|
| Parameter | Description | Default value |
|
||||||
|
| -------------- | ------------------------------------------------------------------------------------------------------------ | ------------- |
|
||||||
|
| `textPosition` | The axial position of the pie slice labels, from 0.0 at the center to 1.0 at the outside edge of the circle. | `0.75` |
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
|
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
|
||||||
|
|
||||||
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
|
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
|
||||||
|
|
||||||
### An example of a timeline.
|
### An example of a timeline.
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@ However, if there is no section defined, then we have two possibilities:
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this is no, section defined, and each time period and its corresponding events will have its own color scheme.
|
Note that there are no sections defined, and each time period and its corresponding events will have its own color scheme.
|
||||||
|
|
||||||
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
|
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ let us look at same example, where we have disabled the multiColor option.
|
|||||||
|
|
||||||
### Customizing Color scheme
|
### Customizing Color scheme
|
||||||
|
|
||||||
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
|
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12 sections, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
|
||||||
In case you have more than 12 sections, the color scheme will start to repeat.
|
In case you have more than 12 sections, the color scheme will start to repeat.
|
||||||
|
|
||||||
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.
|
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.
|
||||||
|
14
package.json
14
package.json
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid-monorepo",
|
"name": "mermaid-monorepo",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "9.4.0",
|
"version": "10.1.0",
|
||||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@7.27.0",
|
"packageManager": "pnpm@7.30.1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"diagram",
|
"diagram",
|
||||||
"markdown",
|
"markdown",
|
||||||
@@ -70,9 +70,9 @@
|
|||||||
"@types/rollup-plugin-visualizer": "^4.2.1",
|
"@types/rollup-plugin-visualizer": "^4.2.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
||||||
"@typescript-eslint/parser": "^5.48.2",
|
"@typescript-eslint/parser": "^5.48.2",
|
||||||
"@vitest/coverage-c8": "^0.28.4",
|
"@vitest/coverage-c8": "^0.29.0",
|
||||||
"@vitest/spy": "^0.28.4",
|
"@vitest/spy": "^0.29.0",
|
||||||
"@vitest/ui": "^0.28.4",
|
"@vitest/ui": "^0.29.0",
|
||||||
"concurrently": "^7.5.0",
|
"concurrently": "^7.5.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"coveralls": "^3.1.1",
|
"coveralls": "^3.1.1",
|
||||||
@@ -109,9 +109,9 @@
|
|||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.4",
|
"typescript": "^4.8.4",
|
||||||
"vite": "^4.1.1",
|
"vite": "^4.1.1",
|
||||||
"vitest": "^0.28.5"
|
"vitest": "^0.29.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "18.14.0"
|
"node": "18.15.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mermaid",
|
"name": "mermaid",
|
||||||
"version": "10.0.1",
|
"version": "10.1.0",
|
||||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/mermaid.core.mjs",
|
"module": "./dist/mermaid.core.mjs",
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/mermaid.d.ts",
|
"types": "./dist/mermaid.d.ts",
|
||||||
"import": "./dist/mermaid.core.mjs"
|
"import": "./dist/mermaid.core.mjs",
|
||||||
|
"default": "./dist/mermaid.core.mjs"
|
||||||
},
|
},
|
||||||
"./*": "./*"
|
"./*": "./*"
|
||||||
},
|
},
|
||||||
@@ -52,13 +53,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@braintree/sanitize-url": "^6.0.0",
|
"@braintree/sanitize-url": "^6.0.0",
|
||||||
|
"@khanacademy/simple-markdown": "^0.8.6",
|
||||||
"cytoscape": "^3.23.0",
|
"cytoscape": "^3.23.0",
|
||||||
"cytoscape-cose-bilkent": "^4.1.0",
|
"cytoscape-cose-bilkent": "^4.1.0",
|
||||||
"cytoscape-fcose": "^2.1.0",
|
"cytoscape-fcose": "^2.1.0",
|
||||||
"d3": "^7.4.0",
|
"d3": "^7.4.0",
|
||||||
"dagre-d3-es": "7.0.9",
|
"dagre-d3-es": "7.0.10",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"dompurify": "2.4.3",
|
"dompurify": "2.4.5",
|
||||||
"elkjs": "^0.8.2",
|
"elkjs": "^0.8.2",
|
||||||
"khroma": "^2.0.0",
|
"khroma": "^2.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
@@ -73,7 +75,7 @@
|
|||||||
"@types/d3": "^7.4.0",
|
"@types/d3": "^7.4.0",
|
||||||
"@types/dompurify": "^2.4.0",
|
"@types/dompurify": "^2.4.0",
|
||||||
"@types/jsdom": "^21.0.0",
|
"@types/jsdom": "^21.0.0",
|
||||||
"@types/lodash-es": "^4.17.6",
|
"@types/lodash-es": "^4.17.7",
|
||||||
"@types/micromatch": "^4.0.2",
|
"@types/micromatch": "^4.0.2",
|
||||||
"@types/prettier": "^2.7.1",
|
"@types/prettier": "^2.7.1",
|
||||||
"@types/stylis": "^4.0.2",
|
"@types/stylis": "^4.0.2",
|
||||||
|
@@ -5,8 +5,14 @@ import { detectType, getDiagramLoader } from './diagram-api/detectType';
|
|||||||
import { extractFrontMatter } from './diagram-api/frontmatter';
|
import { extractFrontMatter } from './diagram-api/frontmatter';
|
||||||
import { UnknownDiagramError } from './errors';
|
import { UnknownDiagramError } from './errors';
|
||||||
import { DetailedError } from './utils';
|
import { DetailedError } from './utils';
|
||||||
|
import { cleanupComments } from './diagram-api/comments';
|
||||||
|
|
||||||
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
|
export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object representing a parsed mermaid diagram definition.
|
||||||
|
* @privateRemarks This is exported as part of the public mermaidAPI.
|
||||||
|
*/
|
||||||
export class Diagram {
|
export class Diagram {
|
||||||
type = 'graph';
|
type = 'graph';
|
||||||
parser;
|
parser;
|
||||||
@@ -38,7 +44,10 @@ export class Diagram {
|
|||||||
// Similarly, we can't do this in getDiagramFromText() because some code
|
// Similarly, we can't do this in getDiagramFromText() because some code
|
||||||
// calls diagram.db.clear(), which would reset anything set by
|
// calls diagram.db.clear(), which would reset anything set by
|
||||||
// extractFrontMatter().
|
// extractFrontMatter().
|
||||||
this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db));
|
|
||||||
|
this.parser.parse = (text: string) =>
|
||||||
|
originalParse(cleanupComments(extractFrontMatter(text, this.db)));
|
||||||
|
|
||||||
this.parser.parser.yy = this.db;
|
this.parser.parser.yy = this.db;
|
||||||
if (diagram.init) {
|
if (diagram.init) {
|
||||||
diagram.init(cnf);
|
diagram.init(cnf);
|
||||||
@@ -68,6 +77,15 @@ export class Diagram {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the text asynchronously and generate a Diagram object asynchronously.
|
||||||
|
* **Warning:** This function may be changed in the future.
|
||||||
|
* @alpha
|
||||||
|
* @param text - The mermaid diagram definition.
|
||||||
|
* @returns A the Promise of a Diagram object.
|
||||||
|
* @throws {@link UnknownDiagramError} if the diagram type can not be found.
|
||||||
|
* @privateRemarks This is exported as part of the public mermaidAPI.
|
||||||
|
*/
|
||||||
export const getDiagramFromText = async (text: string): Promise<Diagram> => {
|
export const getDiagramFromText = async (text: string): Promise<Diagram> => {
|
||||||
const type = detectType(text, configApi.getConfig());
|
const type = detectType(text, configApi.getConfig());
|
||||||
try {
|
try {
|
||||||
|
@@ -3,6 +3,7 @@ import { getConfig } from './config';
|
|||||||
let title = '';
|
let title = '';
|
||||||
let diagramTitle = '';
|
let diagramTitle = '';
|
||||||
let description = '';
|
let description = '';
|
||||||
|
|
||||||
const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig());
|
const sanitizeText = (txt: string): string => _sanitizeText(txt, getConfig());
|
||||||
|
|
||||||
export const clear = function (): void {
|
export const clear = function (): void {
|
||||||
@@ -36,10 +37,10 @@ export const getDiagramTitle = function (): string {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setAccTitle,
|
|
||||||
getAccTitle,
|
getAccTitle,
|
||||||
|
setAccTitle,
|
||||||
|
getDiagramTitle,
|
||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
getDiagramTitle: getDiagramTitle,
|
|
||||||
getAccDescription,
|
getAccDescription,
|
||||||
setAccDescription,
|
setAccDescription,
|
||||||
clear,
|
clear,
|
||||||
|
@@ -222,7 +222,9 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig {
|
|||||||
maxNodeWidth: number;
|
maxNodeWidth: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PieDiagramConfig = BaseDiagramConfig;
|
export interface PieDiagramConfig extends BaseDiagramConfig {
|
||||||
|
textPosition?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ErDiagramConfig extends BaseDiagramConfig {
|
export interface ErDiagramConfig extends BaseDiagramConfig {
|
||||||
titleTopMargin?: number;
|
titleTopMargin?: number;
|
||||||
@@ -333,6 +335,7 @@ export interface GanttDiagramConfig extends BaseDiagramConfig {
|
|||||||
axisFormat?: string;
|
axisFormat?: string;
|
||||||
tickInterval?: string;
|
tickInterval?: string;
|
||||||
topAxis?: boolean;
|
topAxis?: boolean;
|
||||||
|
displayMode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
||||||
@@ -383,6 +386,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
|||||||
curve?: string;
|
curve?: string;
|
||||||
padding?: number;
|
padding?: number;
|
||||||
defaultRenderer?: string;
|
defaultRenderer?: string;
|
||||||
|
wrappingWidth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FontConfig {
|
export interface FontConfig {
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import intersectRect from './intersect/intersect-rect';
|
import intersectRect from './intersect/intersect-rect';
|
||||||
import { log } from '../logger';
|
import { log } from '../logger';
|
||||||
import createLabel from './createLabel';
|
import createLabel from './createLabel';
|
||||||
|
import { createText } from '../rendering-util/createText';
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import { evaluate } from '../diagrams/common/common';
|
import { evaluate } from '../diagrams/common/common';
|
||||||
|
|
||||||
const rect = (parent, node) => {
|
const rect = (parent, node) => {
|
||||||
log.trace('Creating subgraph rect for ', node.id, node);
|
log.info('Creating subgraph rect for ', node.id, node);
|
||||||
|
|
||||||
// Add outer g element
|
// Add outer g element
|
||||||
const shapeSvg = parent
|
const shapeSvg = parent
|
||||||
@@ -17,12 +18,18 @@ const rect = (parent, node) => {
|
|||||||
// add the rect
|
// add the rect
|
||||||
const rect = shapeSvg.insert('rect', ':first-child');
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
|
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||||
|
|
||||||
// Create the label and insert it after the rect
|
// Create the label and insert it after the rect
|
||||||
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
|
||||||
|
|
||||||
const text = label
|
// const text = label
|
||||||
.node()
|
// .node()
|
||||||
.appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
// .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
||||||
|
const text =
|
||||||
|
node.labelType === 'markdown'
|
||||||
|
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
|
||||||
|
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
@@ -56,13 +63,20 @@ const rect = (parent, node) => {
|
|||||||
.attr('width', width)
|
.attr('width', width)
|
||||||
.attr('height', node.height + padding);
|
.attr('height', node.height + padding);
|
||||||
|
|
||||||
|
if (useHtmlLabels) {
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
// This puts the labal on top of the box instead of inside it
|
||||||
|
'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2) + ')'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
label.attr(
|
||||||
|
'transform',
|
||||||
|
// This puts the labal on top of the box instead of inside it
|
||||||
|
'translate(' + node.x + ', ' + (node.y - node.height / 2) + ')'
|
||||||
|
);
|
||||||
|
}
|
||||||
// Center the label
|
// Center the label
|
||||||
label.attr(
|
|
||||||
'transform',
|
|
||||||
// This puts the labal on top of the box instead of inside it
|
|
||||||
// 'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2 - bbox.height) + ')'
|
|
||||||
'translate(' + (node.x - bbox.width / 2) + ', ' + (node.y - node.height / 2) + ')'
|
|
||||||
);
|
|
||||||
|
|
||||||
const rectBox = rect.node().getBBox();
|
const rectBox = rect.node().getBBox();
|
||||||
node.width = rectBox.width;
|
node.width = rectBox.width;
|
||||||
|
@@ -41,7 +41,13 @@ function addHtmlLabel(node) {
|
|||||||
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||||
return fo.node();
|
return fo.node();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @param _vertexText
|
||||||
|
* @param style
|
||||||
|
* @param isTitle
|
||||||
|
* @param isNode
|
||||||
|
* @deprecated svg-util/createText instead
|
||||||
|
*/
|
||||||
const createLabel = (_vertexText, style, isTitle, isNode) => {
|
const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||||
let vertexText = _vertexText || '';
|
let vertexText = _vertexText || '';
|
||||||
if (typeof vertexText === 'object') {
|
if (typeof vertexText === 'object') {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { log } from '../logger';
|
import { log } from '../logger';
|
||||||
import createLabel from './createLabel';
|
import createLabel from './createLabel';
|
||||||
|
import { createText } from '../rendering-util/createText';
|
||||||
import { line, curveBasis, select } from 'd3';
|
import { line, curveBasis, select } from 'd3';
|
||||||
import { getConfig } from '../config';
|
import { getConfig } from '../config';
|
||||||
import utils from '../utils';
|
import utils from '../utils';
|
||||||
@@ -14,8 +15,17 @@ export const clear = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const insertEdgeLabel = (elem, edge) => {
|
export const insertEdgeLabel = (elem, edge) => {
|
||||||
|
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
|
||||||
// Create the actual text element
|
// Create the actual text element
|
||||||
const labelElement = createLabel(edge.label, edge.labelStyle);
|
const labelElement =
|
||||||
|
edge.labelType === 'markdown'
|
||||||
|
? createText(elem, edge.label, {
|
||||||
|
style: edge.labelStyle,
|
||||||
|
useHtmlLabels,
|
||||||
|
addSvgBackground: true,
|
||||||
|
})
|
||||||
|
: createLabel(edge.label, edge.labelStyle);
|
||||||
|
log.info('abc82', edge, edge.labelType);
|
||||||
|
|
||||||
// Create outer g, edgeLabel, this will be positioned after graph layout
|
// Create outer g, edgeLabel, this will be positioned after graph layout
|
||||||
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
const edgeLabel = elem.insert('g').attr('class', 'edgeLabel');
|
||||||
@@ -26,7 +36,7 @@ export const insertEdgeLabel = (elem, edge) => {
|
|||||||
|
|
||||||
// Center the label
|
// Center the label
|
||||||
let bbox = labelElement.getBBox();
|
let bbox = labelElement.getBBox();
|
||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
if (useHtmlLabels) {
|
||||||
const div = labelElement.children[0];
|
const div = labelElement.children[0];
|
||||||
const dv = select(labelElement);
|
const dv = select(labelElement);
|
||||||
bbox = div.getBoundingClientRect();
|
bbox = div.getBoundingClientRect();
|
||||||
|
@@ -313,10 +313,11 @@ const cylinder = (parent, node) => {
|
|||||||
const rect = (parent, node) => {
|
const rect = (parent, node) => {
|
||||||
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
|
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
|
||||||
|
|
||||||
log.trace('Classes = ', node.classes);
|
|
||||||
// add the rect
|
// add the rect
|
||||||
const rect = shapeSvg.insert('rect', ':first-child');
|
const rect = shapeSvg.insert('rect', ':first-child');
|
||||||
|
|
||||||
|
// const totalWidth = bbox.width + node.padding * 2;
|
||||||
|
// const totalHeight = bbox.height + node.padding * 2;
|
||||||
const totalWidth = bbox.width + node.padding;
|
const totalWidth = bbox.width + node.padding;
|
||||||
const totalHeight = bbox.height + node.padding;
|
const totalHeight = bbox.height + node.padding;
|
||||||
rect
|
rect
|
||||||
@@ -324,6 +325,8 @@ const rect = (parent, node) => {
|
|||||||
.attr('style', node.style)
|
.attr('style', node.style)
|
||||||
.attr('rx', node.rx)
|
.attr('rx', node.rx)
|
||||||
.attr('ry', node.ry)
|
.attr('ry', node.ry)
|
||||||
|
// .attr('x', -bbox.width / 2 - node.padding)
|
||||||
|
// .attr('y', -bbox.height / 2 - node.padding)
|
||||||
.attr('x', -bbox.width / 2 - halfPadding)
|
.attr('x', -bbox.width / 2 - halfPadding)
|
||||||
.attr('y', -bbox.height / 2 - halfPadding)
|
.attr('y', -bbox.height / 2 - halfPadding)
|
||||||
.attr('width', totalWidth)
|
.attr('width', totalWidth)
|
||||||
|
@@ -1,8 +1,13 @@
|
|||||||
import { updateNodeBounds, labelHelper } from './util';
|
import { updateNodeBounds, labelHelper } from './util';
|
||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
|
import { getConfig } from '../../config';
|
||||||
import intersect from '../intersect/index.js';
|
import intersect from '../intersect/index.js';
|
||||||
|
|
||||||
const note = (parent, node) => {
|
const note = (parent, node) => {
|
||||||
|
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels;
|
||||||
|
if (!useHtmlLabels) {
|
||||||
|
node.centerLabel = true;
|
||||||
|
}
|
||||||
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
|
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true);
|
||||||
|
|
||||||
log.info('Classes = ', node.classes);
|
log.info('Classes = ', node.classes);
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
import createLabel from '../createLabel';
|
import createLabel from '../createLabel';
|
||||||
|
import { createText } from '../../rendering-util/createText';
|
||||||
import { getConfig } from '../../config';
|
import { getConfig } from '../../config';
|
||||||
import { decodeEntities } from '../../mermaidAPI';
|
import { decodeEntities } from '../../mermaidAPI';
|
||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import { evaluate, sanitizeText } from '../../diagrams/common/common';
|
import { evaluate, sanitizeText } from '../../diagrams/common/common';
|
||||||
export const labelHelper = (parent, node, _classes, isNode) => {
|
export const labelHelper = (parent, node, _classes, isNode) => {
|
||||||
let classes;
|
let classes;
|
||||||
|
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
|
||||||
if (!_classes) {
|
if (!_classes) {
|
||||||
classes = 'node default';
|
classes = 'node default';
|
||||||
} else {
|
} else {
|
||||||
@@ -27,9 +29,17 @@ export const labelHelper = (parent, node, _classes, isNode) => {
|
|||||||
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = label
|
const textNode = label.node();
|
||||||
.node()
|
let text;
|
||||||
.appendChild(
|
if (node.labelType === 'markdown') {
|
||||||
|
// text = textNode;
|
||||||
|
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
|
||||||
|
useHtmlLabels,
|
||||||
|
width: node.width || getConfig().flowchart.wrappingWidth,
|
||||||
|
classes: 'markdown-node-label',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
text = textNode.appendChild(
|
||||||
createLabel(
|
createLabel(
|
||||||
sanitizeText(decodeEntities(labelText), getConfig()),
|
sanitizeText(decodeEntities(labelText), getConfig()),
|
||||||
node.labelStyle,
|
node.labelStyle,
|
||||||
@@ -37,6 +47,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
|
|||||||
isNode
|
isNode
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the size of the label
|
// Get the size of the label
|
||||||
let bbox = text.getBBox();
|
let bbox = text.getBBox();
|
||||||
@@ -52,8 +63,15 @@ export const labelHelper = (parent, node, _classes, isNode) => {
|
|||||||
const halfPadding = node.padding / 2;
|
const halfPadding = node.padding / 2;
|
||||||
|
|
||||||
// Center the label
|
// Center the label
|
||||||
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
if (useHtmlLabels) {
|
||||||
|
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
||||||
|
} else {
|
||||||
|
label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
|
||||||
|
}
|
||||||
|
if (node.centerLabel) {
|
||||||
|
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');
|
||||||
|
}
|
||||||
|
label.insert('rect', ':first-child');
|
||||||
return { shapeSvg, bbox, halfPadding, label };
|
return { shapeSvg, bbox, halfPadding, label };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -258,6 +258,18 @@ const config: Partial<MermaidConfig> = {
|
|||||||
* Default value: 'dagre-wrapper'
|
* Default value: 'dagre-wrapper'
|
||||||
*/
|
*/
|
||||||
defaultRenderer: 'dagre-wrapper',
|
defaultRenderer: 'dagre-wrapper',
|
||||||
|
/**
|
||||||
|
* | Parameter | Description | Type | Required | Values |
|
||||||
|
* | --------------- | ----------- | ------- | -------- | ----------------------- |
|
||||||
|
* | wrappingWidth | See notes | number | 4 | width of nodes where text is wrapped |
|
||||||
|
*
|
||||||
|
* **Notes:**
|
||||||
|
*
|
||||||
|
* When using markdown strings the text ius wrapped automatically, this
|
||||||
|
* value sets the max width of a text before it continues on a new line.
|
||||||
|
* Default value: 'dagre-wrapper'
|
||||||
|
*/
|
||||||
|
wrappingWidth: 200,
|
||||||
},
|
},
|
||||||
|
|
||||||
/** The object containing configurations specific for sequence diagrams */
|
/** The object containing configurations specific for sequence diagrams */
|
||||||
@@ -659,6 +671,17 @@ const config: Partial<MermaidConfig> = {
|
|||||||
*/
|
*/
|
||||||
numberSectionStyles: 4,
|
numberSectionStyles: 4,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* | Parameter | Description | Type | Required | Values |
|
||||||
|
* | ----------- | ------------------------- | ------ | -------- | --------- |
|
||||||
|
* | displayMode | Controls the display mode | string | 4 | 'compact' |
|
||||||
|
*
|
||||||
|
* **Notes**:
|
||||||
|
*
|
||||||
|
* - **compact**: Enables displaying multiple tasks on the same row.
|
||||||
|
*/
|
||||||
|
displayMode: '',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* | Parameter | Description | Type | Required | Values |
|
* | Parameter | Description | Type | Required | Values |
|
||||||
* | ---------- | ---------------------------- | ---- | -------- | ---------------- |
|
* | ---------- | ---------------------------- | ---- | -------- | ---------------- |
|
||||||
@@ -684,7 +707,6 @@ const config: Partial<MermaidConfig> = {
|
|||||||
* Default value: undefined
|
* Default value: undefined
|
||||||
*/
|
*/
|
||||||
tickInterval: undefined,
|
tickInterval: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* | Parameter | Description | Type | Required | Values |
|
* | Parameter | Description | Type | Required | Values |
|
||||||
* | ----------- | ----------- | ------- | -------- | ----------- |
|
* | ----------- | ----------- | ------- | -------- | ----------- |
|
||||||
@@ -1247,6 +1269,15 @@ const config: Partial<MermaidConfig> = {
|
|||||||
* Default value: true
|
* Default value: true
|
||||||
*/
|
*/
|
||||||
useMaxWidth: true,
|
useMaxWidth: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* | Parameter | Description | Type | Required | Values |
|
||||||
|
* | ------------ | -------------------------------------------------------------------------------- | ------- | -------- | ------------------- |
|
||||||
|
* | textPosition | Axial position of slice's label from zero at the center to 1 at the outside edge | Number | Optional | Decimal from 0 to 1 |
|
||||||
|
*
|
||||||
|
* **Notes:** Default value: 0.75
|
||||||
|
*/
|
||||||
|
textPosition: 0.75,
|
||||||
},
|
},
|
||||||
|
|
||||||
/** The object containing configurations specific for req diagrams */
|
/** The object containing configurations specific for req diagrams */
|
||||||
|
94
packages/mermaid/src/diagram-api/comments.spec.ts
Normal file
94
packages/mermaid/src/diagram-api/comments.spec.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// tests to check that comments are removed
|
||||||
|
|
||||||
|
import { cleanupComments } from './comments';
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
|
describe('comments', () => {
|
||||||
|
it('should remove comments', () => {
|
||||||
|
const text = `
|
||||||
|
|
||||||
|
%% This is a comment
|
||||||
|
%% This is another comment
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
%% This is a comment
|
||||||
|
`;
|
||||||
|
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||||
|
"graph TD
|
||||||
|
A-->B
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should keep init statements when removing comments', () => {
|
||||||
|
const text = `
|
||||||
|
%% This is a comment
|
||||||
|
|
||||||
|
%% This is another comment
|
||||||
|
%%{init: {'theme': 'forest'}}%%
|
||||||
|
%%{ init: {'theme': 'space before init'}}%%
|
||||||
|
%%{init: {'theme': 'space after ending'}}%%
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
|
||||||
|
B-->C
|
||||||
|
%% This is a comment
|
||||||
|
`;
|
||||||
|
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||||
|
"%%{init: {'theme': 'forest'}}%%
|
||||||
|
%%{ init: {'theme': 'space before init'}}%%
|
||||||
|
%%{init: {'theme': 'space after ending'}}%%
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
|
||||||
|
B-->C
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove indented comments', () => {
|
||||||
|
const text = `
|
||||||
|
%% This is a comment
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
%% This is a comment
|
||||||
|
C-->D
|
||||||
|
`;
|
||||||
|
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||||
|
"graph TD
|
||||||
|
A-->B
|
||||||
|
C-->D
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove empty newlines from start', () => {
|
||||||
|
const text = `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%% This is a comment
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
`;
|
||||||
|
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||||
|
"graph TD
|
||||||
|
A-->B
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove comments at end of text with no newline', () => {
|
||||||
|
const text = `
|
||||||
|
graph TD
|
||||||
|
A-->B
|
||||||
|
%% This is a comment`;
|
||||||
|
|
||||||
|
expect(cleanupComments(text)).toMatchInlineSnapshot(`
|
||||||
|
"graph TD
|
||||||
|
A-->B
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
8
packages/mermaid/src/diagram-api/comments.ts
Normal file
8
packages/mermaid/src/diagram-api/comments.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Remove all lines starting with `%%` from the text that don't contain a `%%{`
|
||||||
|
* @param text - The text to remove comments from
|
||||||
|
* @returns cleaned text
|
||||||
|
*/
|
||||||
|
export const cleanupComments = (text: string): string => {
|
||||||
|
return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
|
||||||
|
};
|
@@ -13,7 +13,7 @@ import classDiagramV2 from '../diagrams/class/classDetector-V2';
|
|||||||
import state from '../diagrams/state/stateDetector';
|
import state from '../diagrams/state/stateDetector';
|
||||||
import stateV2 from '../diagrams/state/stateDetector-V2';
|
import stateV2 from '../diagrams/state/stateDetector-V2';
|
||||||
import journey from '../diagrams/user-journey/journeyDetector';
|
import journey from '../diagrams/user-journey/journeyDetector';
|
||||||
import error from '../diagrams/error/errorDetector';
|
import errorDiagram from '../diagrams/error/errorDiagram';
|
||||||
import flowchartElk from '../diagrams/flowchart/elk/detector';
|
import flowchartElk from '../diagrams/flowchart/elk/detector';
|
||||||
import timeline from '../diagrams/timeline/detector';
|
import timeline from '../diagrams/timeline/detector';
|
||||||
import mindmap from '../diagrams/mindmap/detector';
|
import mindmap from '../diagrams/mindmap/detector';
|
||||||
@@ -28,6 +28,9 @@ export const addDiagrams = () => {
|
|||||||
// This is added here to avoid race-conditions.
|
// This is added here to avoid race-conditions.
|
||||||
// We could optimize the loading logic somehow.
|
// We could optimize the loading logic somehow.
|
||||||
hasLoadedDiagrams = true;
|
hasLoadedDiagrams = true;
|
||||||
|
registerDiagram('error', errorDiagram, (text) => {
|
||||||
|
return text.toLowerCase().trim() === 'error';
|
||||||
|
});
|
||||||
registerDiagram(
|
registerDiagram(
|
||||||
'---',
|
'---',
|
||||||
// --- diagram type may appear if YAML front-matter is not parsed correctly
|
// --- diagram type may appear if YAML front-matter is not parsed correctly
|
||||||
@@ -57,7 +60,6 @@ export const addDiagrams = () => {
|
|||||||
);
|
);
|
||||||
// Ordering of detectors is important. The first one to return true will be used.
|
// Ordering of detectors is important. The first one to return true will be used.
|
||||||
registerLazyLoadedDiagrams(
|
registerLazyLoadedDiagrams(
|
||||||
error,
|
|
||||||
c4,
|
c4,
|
||||||
classDiagramV2,
|
classDiagramV2,
|
||||||
classDiagram,
|
classDiagram,
|
||||||
|
@@ -11,6 +11,8 @@ export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
|||||||
|
|
||||||
type FrontMatterMetadata = {
|
type FrontMatterMetadata = {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
// Allows custom display modes. Currently used for compact mode in gantt charts.
|
||||||
|
displayMode?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +35,10 @@ export function extractFrontMatter(text: string, db: DiagramDb): string {
|
|||||||
db.setDiagramTitle?.(parsed.title);
|
db.setDiagramTitle?.(parsed.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsed?.displayMode) {
|
||||||
|
db.setDisplayMode?.(parsed.displayMode);
|
||||||
|
}
|
||||||
|
|
||||||
return text.slice(matches[0].length);
|
return text.slice(matches[0].length);
|
||||||
} else {
|
} else {
|
||||||
return text;
|
return text;
|
||||||
|
@@ -16,6 +16,7 @@ export interface InjectUtils {
|
|||||||
export interface DiagramDb {
|
export interface DiagramDb {
|
||||||
clear?: () => void;
|
clear?: () => void;
|
||||||
setDiagramTitle?: (title: string) => void;
|
setDiagramTitle?: (title: string) => void;
|
||||||
|
setDisplayMode?: (title: string) => void;
|
||||||
getAccTitle?: () => string;
|
getAccTitle?: () => string;
|
||||||
getAccDescription?: () => string;
|
getAccDescription?: () => string;
|
||||||
bindFunctions?: (element: Element) => void;
|
bindFunctions?: (element: Element) => void;
|
||||||
|
@@ -19,8 +19,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||||
\%%(?!\{)[^\n]* /* skip comments */
|
|
||||||
[^\}]\%\%[^\n]* /* skip comments */
|
|
||||||
[\n]+ return 'NEWLINE';
|
[\n]+ return 'NEWLINE';
|
||||||
\s+ /* skip whitespace */
|
\s+ /* skip whitespace */
|
||||||
[\s]+ return 'SPACE';
|
[\s]+ return 'SPACE';
|
||||||
@@ -35,8 +33,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
<block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
|
<block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
|
||||||
<block>\"[^"]*\" return 'COMMENT';
|
<block>\"[^"]*\" return 'COMMENT';
|
||||||
<block>[\n]+ /* nothing */
|
<block>[\n]+ /* nothing */
|
||||||
<block>\%%(?!\{)[^\n]* /* skip comments in attribute block */
|
|
||||||
<block>[^\}]\%\%[^\n]* /* skip comments in attribute block */
|
|
||||||
<block>"}" { this.popState(); return 'BLOCK_STOP'; }
|
<block>"}" { this.popState(); return 'BLOCK_STOP'; }
|
||||||
<block>. return yytext[0];
|
<block>. return yytext[0];
|
||||||
|
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
import type { DiagramDetector, ExternalDiagramDefinition } from '../../diagram-api/types';
|
|
||||||
|
|
||||||
const id = 'error';
|
|
||||||
|
|
||||||
const detector: DiagramDetector = (text) => {
|
|
||||||
return text.toLowerCase().trim() === 'error';
|
|
||||||
};
|
|
||||||
|
|
||||||
const loader = async () => {
|
|
||||||
const { diagram } = await import('./errorDiagram');
|
|
||||||
return { id, diagram };
|
|
||||||
};
|
|
||||||
|
|
||||||
const plugin: ExternalDiagramDefinition = {
|
|
||||||
id,
|
|
||||||
detector,
|
|
||||||
loader,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default plugin;
|
|
@@ -19,3 +19,5 @@ export const diagram: DiagramDefinition = {
|
|||||||
// no op
|
// no op
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default diagram;
|
||||||
|
@@ -4,15 +4,13 @@ import { select } from 'd3';
|
|||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import { getErrorMessage } from '../../utils';
|
import { getErrorMessage } from '../../utils';
|
||||||
|
|
||||||
let conf = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the value of `conf` with the passed `cnf`
|
* Merges the value of `conf` with the passed `cnf`
|
||||||
*
|
*
|
||||||
* @param cnf - Config to merge
|
* @param cnf - Config to merge
|
||||||
*/
|
*/
|
||||||
export const setConf = function (cnf: any) {
|
export const setConf = function () {
|
||||||
conf = { ...conf, ...cnf };
|
// no-op
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +76,7 @@ export const draw = (_text: string, id: string, mermaidVersion: string) => {
|
|||||||
.attr('y', 250)
|
.attr('y', 250)
|
||||||
.attr('font-size', '150px')
|
.attr('font-size', '150px')
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.text('Syntax error in graph');
|
.text('Syntax error in text');
|
||||||
g.append('text') // text label for the x axis
|
g.append('text') // text label for the x axis
|
||||||
.attr('class', 'error-text')
|
.attr('class', 'error-text')
|
||||||
.attr('x', 1250)
|
.attr('x', 1250)
|
||||||
|
@@ -3,6 +3,7 @@ import { insertNode } from '../../../dagre-wrapper/nodes.js';
|
|||||||
import insertMarkers from '../../../dagre-wrapper/markers.js';
|
import insertMarkers from '../../../dagre-wrapper/markers.js';
|
||||||
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
|
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
|
||||||
import { findCommonAncestor } from './render-utils';
|
import { findCommonAncestor } from './render-utils';
|
||||||
|
import { labelHelper } from '../../../dagre-wrapper/shapes/util';
|
||||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||||
import { getConfig } from '../../../config';
|
import { getConfig } from '../../../config';
|
||||||
import { log } from '../../../logger';
|
import { log } from '../../../logger';
|
||||||
@@ -12,7 +13,7 @@ import { interpolateToCurve, getStylesFromArray } from '../../../utils';
|
|||||||
import ELK from 'elkjs/lib/elk.bundled.js';
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
||||||
const elk = new ELK();
|
const elk = new ELK();
|
||||||
|
|
||||||
const portPos = {};
|
let portPos = {};
|
||||||
|
|
||||||
const conf = {};
|
const conf = {};
|
||||||
export const setConf = function (cnf) {
|
export const setConf = function (cnf) {
|
||||||
@@ -52,7 +53,7 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
|
|||||||
if (vertex.classes.length > 0) {
|
if (vertex.classes.length > 0) {
|
||||||
classStr = vertex.classes.join(' ');
|
classStr = vertex.classes.join(' ');
|
||||||
}
|
}
|
||||||
|
classStr = classStr + ' flowchart-label';
|
||||||
const styles = getStylesFromArray(vertex.styles);
|
const styles = getStylesFromArray(vertex.styles);
|
||||||
|
|
||||||
// Use vertex id as text in the box if no text is provided by the graph definition
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
@@ -61,40 +62,6 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
|
|||||||
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
let vertexNode;
|
let vertexNode;
|
||||||
const labelData = { width: 0, height: 0 };
|
const labelData = { width: 0, height: 0 };
|
||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
|
||||||
const node = {
|
|
||||||
label: vertexText.replace(
|
|
||||||
/fa[blrs]?:fa-[\w-]+/g,
|
|
||||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
|
||||||
),
|
|
||||||
};
|
|
||||||
vertexNode = addHtmlLabel(svg, node).node();
|
|
||||||
const bbox = vertexNode.getBBox();
|
|
||||||
labelData.width = bbox.width;
|
|
||||||
labelData.height = bbox.height;
|
|
||||||
labelData.labelNode = vertexNode;
|
|
||||||
vertexNode.parentNode.removeChild(vertexNode);
|
|
||||||
} else {
|
|
||||||
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
||||||
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
|
|
||||||
|
|
||||||
const rows = vertexText.split(common.lineBreakRegex);
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
|
||||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
|
||||||
tspan.setAttribute('dy', '1em');
|
|
||||||
tspan.setAttribute('x', '1');
|
|
||||||
tspan.textContent = row;
|
|
||||||
svgLabel.appendChild(tspan);
|
|
||||||
}
|
|
||||||
vertexNode = svgLabel;
|
|
||||||
const bbox = vertexNode.getBBox();
|
|
||||||
labelData.width = bbox.width;
|
|
||||||
labelData.height = bbox.height;
|
|
||||||
labelData.labelNode = vertexNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ports = [
|
const ports = [
|
||||||
{
|
{
|
||||||
@@ -186,11 +153,13 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
|
|||||||
default:
|
default:
|
||||||
_shape = 'rect';
|
_shape = 'rect';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the node
|
// Add the node
|
||||||
const node = {
|
const node = {
|
||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText: vertexText,
|
||||||
|
labelType: vertex.labelType,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
ry: radious,
|
ry: radious,
|
||||||
class: classStr,
|
class: classStr,
|
||||||
@@ -209,10 +178,33 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
|
|||||||
};
|
};
|
||||||
let boundingBox;
|
let boundingBox;
|
||||||
let nodeEl;
|
let nodeEl;
|
||||||
|
|
||||||
|
// Add the element to the DOM
|
||||||
if (node.type !== 'group') {
|
if (node.type !== 'group') {
|
||||||
nodeEl = insertNode(nodes, node, vertex.dir);
|
nodeEl = insertNode(nodes, node, vertex.dir);
|
||||||
boundingBox = nodeEl.node().getBBox();
|
boundingBox = nodeEl.node().getBBox();
|
||||||
|
} else {
|
||||||
|
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
|
||||||
|
// const rows = vertexText.split(common.lineBreakRegex);
|
||||||
|
// for (const row of rows) {
|
||||||
|
// const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||||
|
// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||||
|
// tspan.setAttribute('dy', '1em');
|
||||||
|
// tspan.setAttribute('x', '1');
|
||||||
|
// tspan.textContent = row;
|
||||||
|
// svgLabel.appendChild(tspan);
|
||||||
|
// }
|
||||||
|
// vertexNode = svgLabel;
|
||||||
|
// const bbox = vertexNode.getBBox();
|
||||||
|
const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true);
|
||||||
|
labelData.width = bbox.width;
|
||||||
|
labelData.wrappingWidth = getConfig().flowchart.wrappingWidth;
|
||||||
|
labelData.height = bbox.height;
|
||||||
|
labelData.labelNode = shapeSvg.node();
|
||||||
|
node.labelData = labelData;
|
||||||
}
|
}
|
||||||
|
// const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true);
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
id: vertex.id,
|
id: vertex.id,
|
||||||
@@ -520,7 +512,7 @@ export const addEdges = function (edges, diagObj, graph, svg) {
|
|||||||
edgeData.labelpos = 'c';
|
edgeData.labelpos = 'c';
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeData.labelType = 'text';
|
edgeData.labelType = edge.labelType;
|
||||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||||
|
|
||||||
if (edge.style === undefined) {
|
if (edge.style === undefined) {
|
||||||
@@ -775,6 +767,7 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
// Add temporary render element
|
// Add temporary render element
|
||||||
diagObj.db.clear();
|
diagObj.db.clear();
|
||||||
nodeDb = {};
|
nodeDb = {};
|
||||||
|
portPos = {};
|
||||||
diagObj.db.setGen('gen-2');
|
diagObj.db.setGen('gen-2');
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
diagObj.parser.parse(text);
|
diagObj.parser.parse(text);
|
||||||
@@ -845,9 +838,17 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
log.info('Subgraphs - ', subGraphs);
|
log.info('Subgraphs - ', subGraphs);
|
||||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
subG = subGraphs[i];
|
subG = subGraphs[i];
|
||||||
diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
|
diagObj.db.addVertex(
|
||||||
|
subG.id,
|
||||||
|
{ text: subG.title, type: subG.labelType },
|
||||||
|
'group',
|
||||||
|
undefined,
|
||||||
|
subG.classes,
|
||||||
|
subG.dir
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// debugger;
|
||||||
// Add an element in the svg to be used to hold the subgraphs container
|
// Add an element in the svg to be used to hold the subgraphs container
|
||||||
// elements
|
// elements
|
||||||
const subGraphsEl = svg.insert('g').attr('class', 'subgraphs');
|
const subGraphsEl = svg.insert('g').attr('class', 'subgraphs');
|
||||||
@@ -860,7 +861,7 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
// in order to get the size of the node. You can't get the size of a node
|
// in order to get the size of the node. You can't get the size of a node
|
||||||
// that is not in the dom so we need to add it to the dom, get the size
|
// that is not in the dom so we need to add it to the dom, get the size
|
||||||
// we will position the nodes when we get the layout from elkjs
|
// we will position the nodes when we get the layout from elkjs
|
||||||
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
|
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg);
|
||||||
|
|
||||||
// Time for the edges, we start with adding an element in the node to hold the edges
|
// Time for the edges, we start with adding an element in the node to hold the edges
|
||||||
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
|
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
|
||||||
@@ -887,6 +888,8 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
},
|
},
|
||||||
width: node.labelData.width,
|
width: node.labelData.width,
|
||||||
height: node.labelData.height,
|
height: node.labelData.height,
|
||||||
|
// width: 100,
|
||||||
|
// height: 100,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
delete node.x;
|
delete node.x;
|
||||||
@@ -895,6 +898,7 @@ export const draw = async function (text, id, _version, diagObj) {
|
|||||||
delete node.height;
|
delete node.height;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
insertChildren(graph.children, parentLookupDb);
|
insertChildren(graph.children, parentLookupDb);
|
||||||
log.info('after layout', JSON.stringify(graph, null, 2));
|
log.info('after layout', JSON.stringify(graph, null, 2));
|
||||||
const g = await elk.layout(graph);
|
const g = await elk.layout(graph);
|
||||||
@@ -930,9 +934,12 @@ const drawNodes = (relX, relY, nodeArray, svg, subgraphsEl, diagObj, depth) => {
|
|||||||
.attr('width', node.width)
|
.attr('width', node.width)
|
||||||
.attr('height', node.height);
|
.attr('height', node.height);
|
||||||
const label = subgraphEl.insert('g').attr('class', 'label');
|
const label = subgraphEl.insert('g').attr('class', 'label');
|
||||||
|
const labelCentering = getConfig().flowchart.htmlLabels ? node.labelData.width / 2 : 0;
|
||||||
label.attr(
|
label.attr(
|
||||||
'transform',
|
'transform',
|
||||||
`translate(${node.labels[0].x + relX + node.x}, ${node.labels[0].y + relY + node.y})`
|
`translate(${node.labels[0].x + relX + node.x + labelCentering}, ${
|
||||||
|
node.labels[0].y + relY + node.y + 3
|
||||||
|
})`
|
||||||
);
|
);
|
||||||
label.node().appendChild(node.labelData.labelNode);
|
label.node().appendChild(node.labelData.labelNode);
|
||||||
|
|
||||||
|
@@ -81,7 +81,7 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
.edgeLabel {
|
.edgeLabel {
|
||||||
background-color: ${options.edgeLabelBackground};
|
background-color: ${options.edgeLabelBackground};
|
||||||
rect {
|
rect {
|
||||||
opacity: 0.5;
|
opacity: 0.85;
|
||||||
background-color: ${options.edgeLabelBackground};
|
background-color: ${options.edgeLabelBackground};
|
||||||
fill: ${options.edgeLabelBackground};
|
fill: ${options.edgeLabelBackground};
|
||||||
}
|
}
|
||||||
@@ -132,6 +132,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
// fill:#ccc;
|
// fill:#ccc;
|
||||||
// // stroke:black;
|
// // stroke:black;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
.flowchart-label text {
|
||||||
|
text-anchor: middle;
|
||||||
|
}
|
||||||
|
|
||||||
${genSections(options)}
|
${genSections(options)}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@@ -59,13 +59,14 @@ export const lookUpDomId = function (id) {
|
|||||||
*
|
*
|
||||||
* @param _id
|
* @param _id
|
||||||
* @param text
|
* @param text
|
||||||
|
* @param textObj
|
||||||
* @param type
|
* @param type
|
||||||
* @param style
|
* @param style
|
||||||
* @param classes
|
* @param classes
|
||||||
* @param dir
|
* @param dir
|
||||||
* @param props
|
* @param props
|
||||||
*/
|
*/
|
||||||
export const addVertex = function (_id, text, type, style, classes, dir, props = {}) {
|
export const addVertex = function (_id, textObj, type, style, classes, dir, props = {}) {
|
||||||
let txt;
|
let txt;
|
||||||
let id = _id;
|
let id = _id;
|
||||||
if (id === undefined) {
|
if (id === undefined) {
|
||||||
@@ -80,16 +81,17 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
|||||||
if (vertices[id] === undefined) {
|
if (vertices[id] === undefined) {
|
||||||
vertices[id] = {
|
vertices[id] = {
|
||||||
id: id,
|
id: id,
|
||||||
|
labelType: 'text',
|
||||||
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
|
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
|
||||||
styles: [],
|
styles: [],
|
||||||
classes: [],
|
classes: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
vertexCounter++;
|
vertexCounter++;
|
||||||
if (text !== undefined) {
|
if (textObj !== undefined) {
|
||||||
config = configApi.getConfig();
|
config = configApi.getConfig();
|
||||||
txt = sanitizeText(text.trim());
|
txt = sanitizeText(textObj.text.trim());
|
||||||
|
vertices[id].labelType = textObj.type;
|
||||||
// strip quotes if string starts and ends with a quote
|
// strip quotes if string starts and ends with a quote
|
||||||
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
|
if (txt[0] === '"' && txt[txt.length - 1] === '"') {
|
||||||
txt = txt.substring(1, txt.length - 1);
|
txt = txt.substring(1, txt.length - 1);
|
||||||
@@ -131,24 +133,27 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
|||||||
* @param _end
|
* @param _end
|
||||||
* @param type
|
* @param type
|
||||||
* @param linkText
|
* @param linkText
|
||||||
|
* @param linkTextObj
|
||||||
*/
|
*/
|
||||||
export const addSingleLink = function (_start, _end, type, linkText) {
|
export const addSingleLink = function (_start, _end, type) {
|
||||||
let start = _start;
|
let start = _start;
|
||||||
let end = _end;
|
let end = _end;
|
||||||
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
||||||
// if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
|
// if (end[0].match(/\d/)) end = MERMAID_DOM_ID_PREFIX + end;
|
||||||
// log.info('Got edge...', start, end);
|
// log.info('Got edge...', start, end);
|
||||||
|
|
||||||
const edge = { start: start, end: end, type: undefined, text: '' };
|
const edge = { start: start, end: end, type: undefined, text: '', labelType: 'text' };
|
||||||
linkText = type.text;
|
log.info('abc78 Got edge...', edge);
|
||||||
|
const linkTextObj = type.text;
|
||||||
|
|
||||||
if (linkText !== undefined) {
|
if (linkTextObj !== undefined) {
|
||||||
edge.text = sanitizeText(linkText.trim());
|
edge.text = sanitizeText(linkTextObj.text.trim());
|
||||||
|
|
||||||
// strip quotes if string starts and ends with a quote
|
// strip quotes if string starts and ends with a quote
|
||||||
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
||||||
edge.text = edge.text.substring(1, edge.text.length - 1);
|
edge.text = edge.text.substring(1, edge.text.length - 1);
|
||||||
}
|
}
|
||||||
|
edge.labelType = linkTextObj.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type !== undefined) {
|
if (type !== undefined) {
|
||||||
@@ -158,11 +163,12 @@ export const addSingleLink = function (_start, _end, type, linkText) {
|
|||||||
}
|
}
|
||||||
edges.push(edge);
|
edges.push(edge);
|
||||||
};
|
};
|
||||||
export const addLink = function (_start, _end, type, linktext) {
|
export const addLink = function (_start, _end, type) {
|
||||||
|
log.info('addLink (abc78)', _start, _end, type);
|
||||||
let i, j;
|
let i, j;
|
||||||
for (i = 0; i < _start.length; i++) {
|
for (i = 0; i < _start.length; i++) {
|
||||||
for (j = 0; j < _end.length; j++) {
|
for (j = 0; j < _end.length; j++) {
|
||||||
addSingleLink(_start[i], _end[j], type, linktext);
|
addSingleLink(_start[i], _end[j], type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -457,10 +463,9 @@ export const defaultStyle = function () {
|
|||||||
* @param _title
|
* @param _title
|
||||||
*/
|
*/
|
||||||
export const addSubGraph = function (_id, list, _title) {
|
export const addSubGraph = function (_id, list, _title) {
|
||||||
// console.log('addSubGraph', _id, list, _title);
|
let id = _id.text.trim();
|
||||||
let id = _id.trim();
|
let title = _title.text;
|
||||||
let title = _title;
|
if (_id === _title && _title.text.match(/\s/)) {
|
||||||
if (_id === _title && _title.match(/\s/)) {
|
|
||||||
id = undefined;
|
id = undefined;
|
||||||
}
|
}
|
||||||
/** @param a */
|
/** @param a */
|
||||||
@@ -502,7 +507,14 @@ export const addSubGraph = function (_id, list, _title) {
|
|||||||
title = title || '';
|
title = title || '';
|
||||||
title = sanitizeText(title);
|
title = sanitizeText(title);
|
||||||
subCount = subCount + 1;
|
subCount = subCount + 1;
|
||||||
const subGraph = { id: id, nodes: nodeList, title: title.trim(), classes: [], dir };
|
const subGraph = {
|
||||||
|
id: id,
|
||||||
|
nodes: nodeList,
|
||||||
|
title: title.trim(),
|
||||||
|
classes: [],
|
||||||
|
dir,
|
||||||
|
labelType: _title.type,
|
||||||
|
};
|
||||||
|
|
||||||
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
|
log.info('Adding', subGraph.id, subGraph.nodes, subGraph.dir);
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
if (vertex.classes.length > 0) {
|
if (vertex.classes.length > 0) {
|
||||||
classStr = vertex.classes.join(' ');
|
classStr = vertex.classes.join(' ');
|
||||||
}
|
}
|
||||||
|
classStr = classStr + ' flowchart-label';
|
||||||
const styles = getStylesFromArray(vertex.styles);
|
const styles = getStylesFromArray(vertex.styles);
|
||||||
|
|
||||||
// Use vertex id as text in the box if no text is provided by the graph definition
|
// Use vertex id as text in the box if no text is provided by the graph definition
|
||||||
@@ -55,31 +55,36 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
|
|
||||||
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
// We create a SVG label, either by delegating to addHtmlLabel or manually
|
||||||
let vertexNode;
|
let vertexNode;
|
||||||
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
log.info('vertex', vertex, vertex.labelType);
|
||||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
if (vertex.labelType === 'markdown') {
|
||||||
const node = {
|
log.info('vertex', vertex, vertex.labelType);
|
||||||
label: vertexText.replace(
|
|
||||||
/fa[blrs]?:fa-[\w-]+/g,
|
|
||||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
|
||||||
),
|
|
||||||
};
|
|
||||||
vertexNode = addHtmlLabel(svg, node).node();
|
|
||||||
vertexNode.parentNode.removeChild(vertexNode);
|
|
||||||
} else {
|
} else {
|
||||||
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
if (evaluate(getConfig().flowchart.htmlLabels)) {
|
||||||
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
|
const node = {
|
||||||
|
label: vertexText.replace(
|
||||||
|
/fa[blrs]?:fa-[\w-]+/g,
|
||||||
|
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
),
|
||||||
|
};
|
||||||
|
vertexNode = addHtmlLabel(svg, node).node();
|
||||||
|
vertexNode.parentNode.removeChild(vertexNode);
|
||||||
|
} else {
|
||||||
|
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||||
|
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
|
||||||
|
|
||||||
const rows = vertexText.split(common.lineBreakRegex);
|
const rows = vertexText.split(common.lineBreakRegex);
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||||
tspan.setAttribute('dy', '1em');
|
tspan.setAttribute('dy', '1em');
|
||||||
tspan.setAttribute('x', '1');
|
tspan.setAttribute('x', '1');
|
||||||
tspan.textContent = row;
|
tspan.textContent = row;
|
||||||
svgLabel.appendChild(tspan);
|
svgLabel.appendChild(tspan);
|
||||||
|
}
|
||||||
|
vertexNode = svgLabel;
|
||||||
}
|
}
|
||||||
vertexNode = svgLabel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let radious = 0;
|
let radious = 0;
|
||||||
@@ -146,6 +151,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText: vertexText,
|
||||||
|
labelType: vertex.labelType,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
ry: radious,
|
ry: radious,
|
||||||
class: classStr,
|
class: classStr,
|
||||||
@@ -165,6 +171,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
|||||||
|
|
||||||
log.info('setNode', {
|
log.info('setNode', {
|
||||||
labelStyle: styles.labelStyle,
|
labelStyle: styles.labelStyle,
|
||||||
|
labelType: vertex.labelType,
|
||||||
shape: _shape,
|
shape: _shape,
|
||||||
labelText: vertexText,
|
labelText: vertexText,
|
||||||
rx: radious,
|
rx: radious,
|
||||||
@@ -312,7 +319,7 @@ export const addEdges = function (edges, g, diagObj) {
|
|||||||
edgeData.labelpos = 'c';
|
edgeData.labelpos = 'c';
|
||||||
}
|
}
|
||||||
|
|
||||||
edgeData.labelType = 'text';
|
edgeData.labelType = edge.labelType;
|
||||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||||
|
|
||||||
if (edge.style === undefined) {
|
if (edge.style === undefined) {
|
||||||
@@ -405,7 +412,14 @@ export const draw = function (text, id, _version, diagObj) {
|
|||||||
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
for (let i = subGraphs.length - 1; i >= 0; i--) {
|
||||||
subG = subGraphs[i];
|
subG = subGraphs[i];
|
||||||
log.info('Subgraph - ', subG);
|
log.info('Subgraph - ', subG);
|
||||||
diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
|
diagObj.db.addVertex(
|
||||||
|
subG.id,
|
||||||
|
{ text: subG.title, type: subG.labelType },
|
||||||
|
'group',
|
||||||
|
undefined,
|
||||||
|
subG.classes,
|
||||||
|
subG.dir
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the vertices/nodes and edges/links from the parsed graph definition
|
// Fetch the vertices/nodes and edges/links from the parsed graph definition
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import flowDb from '../flowDb';
|
import flowDb from '../flowDb';
|
||||||
import flow from './flow';
|
import flow from './flow';
|
||||||
import { setConfig } from '../../../config';
|
import { setConfig } from '../../../config';
|
||||||
|
import { cleanupComments } from '../../../diagram-api/comments';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
securityLevel: 'strict',
|
securityLevel: 'strict',
|
||||||
@@ -13,7 +14,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle comments', function () {
|
it('should handle comments', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n%% Comment\n A-->B;');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n%% Comment\n A-->B;'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -28,7 +29,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle comments at the start', function () {
|
it('should handle comments at the start', function () {
|
||||||
const res = flow.parser.parse('%% Comment\ngraph TD;\n A-->B;');
|
const res = flow.parser.parse(cleanupComments('%% Comment\ngraph TD;\n A-->B;'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -43,7 +44,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle comments at the end', function () {
|
it('should handle comments at the end', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n A-->B\n %% Comment at the end\n');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n %% Comment at the end\n'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -58,7 +59,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle comments at the end no trailing newline', function () {
|
it('should handle comments at the end no trailing newline', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -73,7 +74,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle comments at the end many trailing newlines', function () {
|
it('should handle comments at the end many trailing newlines', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n A-->B\n%% Comment\n\n\n');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n%% Comment\n\n\n'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -88,7 +89,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle no trailing newlines', function () {
|
it('should handle no trailing newlines', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n A-->B');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -103,7 +104,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle many trailing newlines', function () {
|
it('should handle many trailing newlines', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n A-->B\n\n');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n A-->B\n\n'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -118,7 +119,7 @@ describe('[Comments] when parsing', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle a comment with blank rows in-between', function () {
|
it('should handle a comment with blank rows in-between', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B;');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B;'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
@@ -134,7 +135,9 @@ describe('[Comments] when parsing', () => {
|
|||||||
|
|
||||||
it('should handle a comment with mermaid flowchart code in them', function () {
|
it('should handle a comment with mermaid flowchart code in them', function () {
|
||||||
const res = flow.parser.parse(
|
const res = flow.parser.parse(
|
||||||
'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;'
|
cleanupComments(
|
||||||
|
'graph TD;\n\n\n %% Test od>Odd shape]-->|Two line<br>edge comment|ro;\n A-->B;'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
@@ -0,0 +1,64 @@
|
|||||||
|
import flowDb from '../flowDb';
|
||||||
|
import flow from './flow';
|
||||||
|
import { setConfig } from '../../../config';
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
securityLevel: 'strict',
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parsing a flow chart with markdown strings', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
flow.parser.yy = flowDb;
|
||||||
|
flow.parser.yy.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mardown formatting in nodes and labels', function () {
|
||||||
|
const res = flow.parser.parse(`flowchart
|
||||||
|
A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in the hog"] -- "The rat in the mat" -->C;`);
|
||||||
|
|
||||||
|
const vert = flow.parser.yy.getVertices();
|
||||||
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
|
||||||
|
expect(vert['A'].id).toBe('A');
|
||||||
|
expect(vert['A'].text).toBe('The cat in **the** hat');
|
||||||
|
expect(vert['A'].labelType).toBe('markdown');
|
||||||
|
expect(vert['B'].id).toBe('B');
|
||||||
|
expect(vert['B'].text).toBe('The dog in the hog');
|
||||||
|
expect(vert['B'].labelType).toBe('text');
|
||||||
|
expect(edges.length).toBe(2);
|
||||||
|
expect(edges[0].start).toBe('A');
|
||||||
|
expect(edges[0].end).toBe('B');
|
||||||
|
expect(edges[0].type).toBe('arrow_point');
|
||||||
|
expect(edges[0].text).toBe('The *bat* in the chat');
|
||||||
|
expect(edges[0].labelType).toBe('markdown');
|
||||||
|
expect(edges[1].start).toBe('B');
|
||||||
|
expect(edges[1].end).toBe('C');
|
||||||
|
expect(edges[1].type).toBe('arrow_point');
|
||||||
|
expect(edges[1].text).toBe('The rat in the mat');
|
||||||
|
expect(edges[1].labelType).toBe('text');
|
||||||
|
});
|
||||||
|
it('mardown formatting in subgraphs', function () {
|
||||||
|
const res = flow.parser.parse(`flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("\`The **cat**
|
||||||
|
in the hat\`") -- "1o" --> b{{"\`The **dog** in the hog\`"}}
|
||||||
|
end
|
||||||
|
subgraph "\`**Two**\`"
|
||||||
|
c("\`The **cat**
|
||||||
|
in the hat\`") -- "\`1o **ipa**\`" --> d("The dog in the hog")
|
||||||
|
end`);
|
||||||
|
|
||||||
|
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||||
|
expect(subgraphs.length).toBe(2);
|
||||||
|
const subgraph = subgraphs[0];
|
||||||
|
|
||||||
|
expect(subgraph.nodes.length).toBe(2);
|
||||||
|
expect(subgraph.title).toBe('One');
|
||||||
|
expect(subgraph.labelType).toBe('text');
|
||||||
|
|
||||||
|
const subgraph2 = subgraphs[1];
|
||||||
|
expect(subgraph2.nodes.length).toBe(2);
|
||||||
|
expect(subgraph2.title).toBe('**Two**');
|
||||||
|
expect(subgraph2.labelType).toBe('markdown');
|
||||||
|
});
|
||||||
|
});
|
@@ -7,6 +7,7 @@
|
|||||||
/* lexical grammar */
|
/* lexical grammar */
|
||||||
%lex
|
%lex
|
||||||
%x string
|
%x string
|
||||||
|
%x md_string
|
||||||
%x acc_title
|
%x acc_title
|
||||||
%x acc_descr
|
%x acc_descr
|
||||||
%x acc_descr_multiline
|
%x acc_descr_multiline
|
||||||
@@ -27,8 +28,6 @@
|
|||||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||||
\%\%(?!\{)[^\n]* /* skip comments */
|
|
||||||
[^\}]\%\%[^\n]* /* skip comments */
|
|
||||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
||||||
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
|
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
|
||||||
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
|
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
|
||||||
@@ -37,6 +36,9 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||||||
<acc_descr_multiline>[\}] { this.popState(); }
|
<acc_descr_multiline>[\}] { this.popState(); }
|
||||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||||
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
|
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
|
||||||
|
["][`] { this.begin("md_string");}
|
||||||
|
<md_string>[^`"]+ { return "MD_STR";}
|
||||||
|
<md_string>[`]["] { this.popState();}
|
||||||
["] this.begin("string");
|
["] this.begin("string");
|
||||||
<string>["] this.popState();
|
<string>["] this.popState();
|
||||||
<string>[^"]* return "STR";
|
<string>[^"]* return "STR";
|
||||||
@@ -434,11 +436,13 @@ arrowText:
|
|||||||
;
|
;
|
||||||
|
|
||||||
text: textToken
|
text: textToken
|
||||||
{$$=$1;}
|
{ $$={text:$1, type: 'text'};}
|
||||||
| text textToken
|
| text textToken
|
||||||
{$$=$1+''+$2;}
|
{ $$={text:$1.text+''+$2, type: $1.type};}
|
||||||
| STR
|
| STR
|
||||||
{$$=$1;}
|
{ $$={text: $1, type: 'text'};}
|
||||||
|
| MD_STR
|
||||||
|
{ $$={text: $1, type: 'markdown'};}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import flowDb from '../flowDb';
|
import flowDb from '../flowDb';
|
||||||
import flow from './flow';
|
import flow from './flow';
|
||||||
import { setConfig } from '../../../config';
|
import { setConfig } from '../../../config';
|
||||||
|
import { cleanupComments } from '../../../diagram-api/comments';
|
||||||
|
|
||||||
setConfig({
|
setConfig({
|
||||||
securityLevel: 'strict',
|
securityLevel: 'strict',
|
||||||
@@ -13,7 +14,7 @@ describe('parsing a flow chart', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle a trailing whitespaces after statements', function () {
|
it('should handle a trailing whitespaces after statements', function () {
|
||||||
const res = flow.parser.parse('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;');
|
const res = flow.parser.parse(cleanupComments('graph TD;\n\n\n %% Comment\n A-->B; \n B-->C;'));
|
||||||
|
|
||||||
const vert = flow.parser.yy.getVertices();
|
const vert = flow.parser.yy.getVertices();
|
||||||
const edges = flow.parser.yy.getEdges();
|
const edges = flow.parser.yy.getEdges();
|
||||||
|
@@ -23,11 +23,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
.cluster-label text {
|
.cluster-label text {
|
||||||
fill: ${options.titleColor};
|
fill: ${options.titleColor};
|
||||||
}
|
}
|
||||||
.cluster-label span {
|
.cluster-label span,p {
|
||||||
color: ${options.titleColor};
|
color: ${options.titleColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
.label text,span {
|
.label text,span,p {
|
||||||
fill: ${options.nodeTextColor || options.textColor};
|
fill: ${options.nodeTextColor || options.textColor};
|
||||||
color: ${options.nodeTextColor || options.textColor};
|
color: ${options.nodeTextColor || options.textColor};
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,15 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
stroke: ${options.nodeBorder};
|
stroke: ${options.nodeBorder};
|
||||||
stroke-width: 1px;
|
stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
.flowchart-label text {
|
||||||
|
text-anchor: middle;
|
||||||
|
}
|
||||||
|
// .flowchart-label .text-outer-tspan {
|
||||||
|
// text-anchor: middle;
|
||||||
|
// }
|
||||||
|
// .flowchart-label .text-inner-tspan {
|
||||||
|
// text-anchor: start;
|
||||||
|
// }
|
||||||
|
|
||||||
.node .label {
|
.node .label {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -83,7 +92,7 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
|||||||
fill: ${options.titleColor};
|
fill: ${options.titleColor};
|
||||||
}
|
}
|
||||||
|
|
||||||
.cluster span {
|
.cluster span,p {
|
||||||
color: ${options.titleColor};
|
color: ${options.titleColor};
|
||||||
}
|
}
|
||||||
/* .cluster div {
|
/* .cluster div {
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { sanitizeUrl } from '@braintree/sanitize-url';
|
import { sanitizeUrl } from '@braintree/sanitize-url';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs/esm/index.js';
|
||||||
import dayjsIsoWeek from 'dayjs/plugin/isoWeek';
|
import dayjsIsoWeek from 'dayjs/esm/plugin/isoWeek/index.js';
|
||||||
import dayjsCustomParseFormat from 'dayjs/plugin/customParseFormat';
|
import dayjsCustomParseFormat from 'dayjs/esm/plugin/customParseFormat/index.js';
|
||||||
import dayjsAdvancedFormat from 'dayjs/plugin/advancedFormat';
|
import dayjsAdvancedFormat from 'dayjs/esm/plugin/advancedFormat/index.js';
|
||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import * as configApi from '../../config';
|
import * as configApi from '../../config';
|
||||||
import utils from '../../utils';
|
import utils from '../../utils';
|
||||||
@@ -32,6 +32,7 @@ let links = {};
|
|||||||
let sections = [];
|
let sections = [];
|
||||||
let tasks = [];
|
let tasks = [];
|
||||||
let currentSection = '';
|
let currentSection = '';
|
||||||
|
let displayMode = '';
|
||||||
const tags = ['active', 'done', 'crit', 'milestone'];
|
const tags = ['active', 'done', 'crit', 'milestone'];
|
||||||
let funs = [];
|
let funs = [];
|
||||||
let inclusiveEndDates = false;
|
let inclusiveEndDates = false;
|
||||||
@@ -55,6 +56,7 @@ export const clear = function () {
|
|||||||
rawTasks = [];
|
rawTasks = [];
|
||||||
dateFormat = '';
|
dateFormat = '';
|
||||||
axisFormat = '';
|
axisFormat = '';
|
||||||
|
displayMode = '';
|
||||||
tickInterval = undefined;
|
tickInterval = undefined;
|
||||||
todayMarker = '';
|
todayMarker = '';
|
||||||
includes = [];
|
includes = [];
|
||||||
@@ -110,6 +112,14 @@ export const topAxisEnabled = function () {
|
|||||||
return topAxis;
|
return topAxis;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setDisplayMode = function (txt) {
|
||||||
|
displayMode = txt;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDisplayMode = function () {
|
||||||
|
return displayMode;
|
||||||
|
};
|
||||||
|
|
||||||
export const getDateFormat = function () {
|
export const getDateFormat = function () {
|
||||||
return dateFormat;
|
return dateFormat;
|
||||||
};
|
};
|
||||||
@@ -143,11 +153,11 @@ export const getSections = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getTasks = function () {
|
export const getTasks = function () {
|
||||||
let allItemsPricessed = compileTasks();
|
let allItemsProcessed = compileTasks();
|
||||||
const maxDepth = 10;
|
const maxDepth = 10;
|
||||||
let iterationCount = 0;
|
let iterationCount = 0;
|
||||||
while (!allItemsPricessed && iterationCount < maxDepth) {
|
while (!allItemsProcessed && iterationCount < maxDepth) {
|
||||||
allItemsPricessed = compileTasks();
|
allItemsProcessed = compileTasks();
|
||||||
iterationCount++;
|
iterationCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,6 +729,8 @@ export default {
|
|||||||
getAccTitle,
|
getAccTitle,
|
||||||
setDiagramTitle,
|
setDiagramTitle,
|
||||||
getDiagramTitle,
|
getDiagramTitle,
|
||||||
|
setDisplayMode,
|
||||||
|
getDisplayMode,
|
||||||
setAccDescription,
|
setAccDescription,
|
||||||
getAccDescription,
|
getAccDescription,
|
||||||
addSection,
|
addSection,
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck TODO: Fix TS
|
// @ts-nocheck TODO: Fix TS
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs/esm/index.js';
|
||||||
import ganttDb from './ganttDb';
|
import ganttDb from './ganttDb';
|
||||||
import { convert } from '../../tests/util';
|
import { convert } from '../../tests/util';
|
||||||
|
|
||||||
@@ -34,6 +34,7 @@ describe('when using the ganttDb', function () {
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
ganttDb.setDateFormat('YYYY-MM-DD');
|
ganttDb.setDateFormat('YYYY-MM-DD');
|
||||||
ganttDb.enableInclusiveEndDates();
|
ganttDb.enableInclusiveEndDates();
|
||||||
|
ganttDb.setDisplayMode('compact');
|
||||||
ganttDb.setTodayMarker('off');
|
ganttDb.setTodayMarker('off');
|
||||||
ganttDb.setExcludes('weekends 2019-02-06,friday');
|
ganttDb.setExcludes('weekends 2019-02-06,friday');
|
||||||
ganttDb.addSection('weekends skip test');
|
ganttDb.addSection('weekends skip test');
|
||||||
@@ -53,6 +54,7 @@ describe('when using the ganttDb', function () {
|
|||||||
${'getExcludes'} | ${[]}
|
${'getExcludes'} | ${[]}
|
||||||
${'getSections'} | ${[]}
|
${'getSections'} | ${[]}
|
||||||
${'endDatesAreInclusive'} | ${false}
|
${'endDatesAreInclusive'} | ${false}
|
||||||
|
${'getDisplayMode'} | ${''}
|
||||||
`)('should clear $fn', ({ fn, expected }) => {
|
`)('should clear $fn', ({ fn, expected }) => {
|
||||||
expect(ganttDb[fn]()).toEqual(expected);
|
expect(ganttDb[fn]()).toEqual(expected);
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs/esm/index.js';
|
||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import {
|
import {
|
||||||
select,
|
select,
|
||||||
@@ -24,12 +24,43 @@ export const setConf = function () {
|
|||||||
log.debug('Something is calling, setConf, remove the call');
|
log.debug('Something is calling, setConf, remove the call');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For this issue:
|
||||||
|
* https://github.com/mermaid-js/mermaid/issues/1618
|
||||||
|
*
|
||||||
|
* Finds the number of intersections between tasks that happen at any point in time.
|
||||||
|
* Used to figure out how many rows are needed to display the tasks when the display
|
||||||
|
* mode is set to 'compact'.
|
||||||
|
*
|
||||||
|
* @param tasks
|
||||||
|
* @param orderOffset
|
||||||
|
*/
|
||||||
|
const getMaxIntersections = (tasks, orderOffset) => {
|
||||||
|
let timeline = [...tasks].map(() => -Infinity);
|
||||||
|
let sorted = [...tasks].sort((a, b) => a.startTime - b.startTime || a.order - b.order);
|
||||||
|
let maxIntersections = 0;
|
||||||
|
for (const element of sorted) {
|
||||||
|
for (let j = 0; j < timeline.length; j++) {
|
||||||
|
if (element.startTime >= timeline[j]) {
|
||||||
|
timeline[j] = element.endTime;
|
||||||
|
element.order = j + orderOffset;
|
||||||
|
if (j > maxIntersections) {
|
||||||
|
maxIntersections = j;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIntersections;
|
||||||
|
};
|
||||||
|
|
||||||
let w;
|
let w;
|
||||||
export const draw = function (text, id, version, diagObj) {
|
export const draw = function (text, id, version, diagObj) {
|
||||||
const conf = getConfig().gantt;
|
const conf = getConfig().gantt;
|
||||||
|
|
||||||
// diagObj.db.clear();
|
// diagObj.db.clear();
|
||||||
// parser.parse(text);
|
// parser.parse(text);
|
||||||
|
|
||||||
const securityLevel = getConfig().securityLevel;
|
const securityLevel = getConfig().securityLevel;
|
||||||
// Handle root and Document for when rendering in sandbox mode
|
// Handle root and Document for when rendering in sandbox mode
|
||||||
let sandboxElement;
|
let sandboxElement;
|
||||||
@@ -56,7 +87,40 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
const taskArray = diagObj.db.getTasks();
|
const taskArray = diagObj.db.getTasks();
|
||||||
|
|
||||||
// Set height based on number of tasks
|
// Set height based on number of tasks
|
||||||
const h = taskArray.length * (conf.barHeight + conf.barGap) + 2 * conf.topPadding;
|
|
||||||
|
let categories = [];
|
||||||
|
|
||||||
|
for (const element of taskArray) {
|
||||||
|
categories.push(element.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
categories = checkUnique(categories);
|
||||||
|
const categoryHeights = {};
|
||||||
|
|
||||||
|
let h = 2 * conf.topPadding;
|
||||||
|
if (diagObj.db.getDisplayMode() === 'compact' || conf.displayMode === 'compact') {
|
||||||
|
const categoryElements = {};
|
||||||
|
for (const element of taskArray) {
|
||||||
|
if (categoryElements[element.section] === undefined) {
|
||||||
|
categoryElements[element.section] = [element];
|
||||||
|
} else {
|
||||||
|
categoryElements[element.section].push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let intersections = 0;
|
||||||
|
for (const category of Object.keys(categoryElements)) {
|
||||||
|
const categoryHeight = getMaxIntersections(categoryElements[category], intersections) + 1;
|
||||||
|
intersections += categoryHeight;
|
||||||
|
h += categoryHeight * (conf.barHeight + conf.barGap);
|
||||||
|
categoryHeights[category] = categoryHeight;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h += taskArray.length * (conf.barHeight + conf.barGap);
|
||||||
|
for (const category of categories) {
|
||||||
|
categoryHeights[category] = taskArray.filter((task) => task.type === category).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set viewBox
|
// Set viewBox
|
||||||
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h);
|
||||||
@@ -74,16 +138,6 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
])
|
])
|
||||||
.rangeRound([0, w - conf.leftPadding - conf.rightPadding]);
|
.rangeRound([0, w - conf.leftPadding - conf.rightPadding]);
|
||||||
|
|
||||||
let categories = [];
|
|
||||||
|
|
||||||
for (const element of taskArray) {
|
|
||||||
categories.push(element.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
const catsUnfiltered = categories; // for vert labels
|
|
||||||
|
|
||||||
categories = checkUnique(categories);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param a
|
* @param a
|
||||||
* @param b
|
* @param b
|
||||||
@@ -157,11 +211,15 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
* @param w
|
* @param w
|
||||||
*/
|
*/
|
||||||
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
|
function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
|
||||||
|
// Get unique task orders. Required to draw the background rects when display mode is compact.
|
||||||
|
const uniqueTaskOrderIds = [...new Set(theArray.map((item) => item.order))];
|
||||||
|
const uniqueTasks = uniqueTaskOrderIds.map((id) => theArray.find((item) => item.order === id));
|
||||||
|
|
||||||
// Draw background rects covering the entire width of the graph, these form the section rows.
|
// Draw background rects covering the entire width of the graph, these form the section rows.
|
||||||
svg
|
svg
|
||||||
.append('g')
|
.append('g')
|
||||||
.selectAll('rect')
|
.selectAll('rect')
|
||||||
.data(theArray)
|
.data(uniqueTasks)
|
||||||
.enter()
|
.enter()
|
||||||
.append('rect')
|
.append('rect')
|
||||||
.attr('x', 0)
|
.attr('x', 0)
|
||||||
@@ -582,12 +640,9 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
* @param theTopPad
|
* @param theTopPad
|
||||||
*/
|
*/
|
||||||
function vertLabels(theGap, theTopPad) {
|
function vertLabels(theGap, theTopPad) {
|
||||||
const numOccurances = [];
|
|
||||||
let prevGap = 0;
|
let prevGap = 0;
|
||||||
|
|
||||||
for (const [i, category] of categories.entries()) {
|
const numOccurances = Object.keys(categoryHeights).map((d) => [d, categoryHeights[d]]);
|
||||||
numOccurances[i] = [category, getCount(category, catsUnfiltered)];
|
|
||||||
}
|
|
||||||
|
|
||||||
svg
|
svg
|
||||||
.append('g') // without doing this, impossible to put grid lines behind text
|
.append('g') // without doing this, impossible to put grid lines behind text
|
||||||
@@ -625,7 +680,6 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.attr('font-size', conf.sectionFontSize)
|
.attr('font-size', conf.sectionFontSize)
|
||||||
.attr('font-size', conf.sectionFontSize)
|
|
||||||
.attr('class', function (d) {
|
.attr('class', function (d) {
|
||||||
for (const [i, category] of categories.entries()) {
|
for (const [i, category] of categories.entries()) {
|
||||||
if (d[0] === category) {
|
if (d[0] === category) {
|
||||||
@@ -682,31 +736,6 @@ export const draw = function (text, id, version, diagObj) {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* From this stack exchange question:
|
|
||||||
* http://stackoverflow.com/questions/14227981/count-how-many-strings-in-an-array-have-duplicates-in-the-same-array
|
|
||||||
*
|
|
||||||
* @param arr
|
|
||||||
*/
|
|
||||||
function getCounts(arr) {
|
|
||||||
let i = arr.length; // const to loop over
|
|
||||||
const obj = {}; // obj to store results
|
|
||||||
while (i) {
|
|
||||||
obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get specific from everything
|
|
||||||
*
|
|
||||||
* @param word
|
|
||||||
* @param arr
|
|
||||||
*/
|
|
||||||
function getCount(word, arr) {
|
|
||||||
return getCounts(arr)[word] || 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@@ -131,9 +131,10 @@ statement
|
|||||||
| includes {yy.setIncludes($1.substr(9));$$=$1.substr(9);}
|
| includes {yy.setIncludes($1.substr(9));$$=$1.substr(9);}
|
||||||
| todayMarker {yy.setTodayMarker($1.substr(12));$$=$1.substr(12);}
|
| todayMarker {yy.setTodayMarker($1.substr(12));$$=$1.substr(12);}
|
||||||
| title {yy.setDiagramTitle($1.substr(6));$$=$1.substr(6);}
|
| title {yy.setDiagramTitle($1.substr(6));$$=$1.substr(6);}
|
||||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); }
|
||||||
|
| section { yy.addSection($1.substr(8));$$=$1.substr(8); }
|
||||||
| clickStatement
|
| clickStatement
|
||||||
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
| taskTxt taskData {yy.addTask($1,$2);$$='task';}
|
||||||
| directive
|
| directive
|
||||||
|
@@ -167,12 +167,15 @@ function positionNodes(cy) {
|
|||||||
export const draw = async (text, id, version, diagObj) => {
|
export const draw = async (text, id, version, diagObj) => {
|
||||||
const conf = getConfig();
|
const conf = getConfig();
|
||||||
|
|
||||||
|
// console.log('Config: ', conf);
|
||||||
|
conf.htmlLabels = false;
|
||||||
|
|
||||||
// This is done only for throwing the error if the text is not valid.
|
// This is done only for throwing the error if the text is not valid.
|
||||||
diagObj.db.clear();
|
diagObj.db.clear();
|
||||||
// Parse the graph definition
|
// Parse the graph definition
|
||||||
diagObj.parser.parse(text);
|
diagObj.parser.parse(text);
|
||||||
|
|
||||||
log.debug('Renering info diagram\n' + text);
|
log.debug('Rendering mindmap diagram\n' + text, diagObj.parser);
|
||||||
|
|
||||||
const securityLevel = getConfig().securityLevel;
|
const securityLevel = getConfig().securityLevel;
|
||||||
// Handle root and Document for when rendering in sandbox mode
|
// Handle root and Document for when rendering in sandbox mode
|
||||||
|
@@ -12,12 +12,13 @@
|
|||||||
%}
|
%}
|
||||||
%x NODE
|
%x NODE
|
||||||
%x NSTR
|
%x NSTR
|
||||||
|
%x NSTR2
|
||||||
%x ICON
|
%x ICON
|
||||||
%x CLASS
|
%x CLASS
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
\s*\%\%.* {yy.getLogger().trace('Found comment',yytext);}
|
\s*\%\%.* {yy.getLogger().trace('Found comment',yytext); return 'SPACELINE';}
|
||||||
// \%\%[^\n]*\n /* skip comments */
|
// \%\%[^\n]*\n /* skip comments */
|
||||||
"mindmap" return 'MINDMAP';
|
"mindmap" return 'MINDMAP';
|
||||||
":::" { this.begin('CLASS'); }
|
":::" { this.begin('CLASS'); }
|
||||||
@@ -41,6 +42,9 @@
|
|||||||
// !(-\() return 'NODE_ID';
|
// !(-\() return 'NODE_ID';
|
||||||
[^\(\[\n\-\)\{\}]+ return 'NODE_ID';
|
[^\(\[\n\-\)\{\}]+ return 'NODE_ID';
|
||||||
<<EOF>> return 'EOF';
|
<<EOF>> return 'EOF';
|
||||||
|
<NODE>["][`] { this.begin("NSTR2");}
|
||||||
|
<NSTR2>[^`"]+ { return "NODE_DESCR";}
|
||||||
|
<NSTR2>[`]["] { this.popState();}
|
||||||
<NODE>["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
|
<NODE>["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
|
||||||
<NSTR>[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
|
<NSTR>[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
|
||||||
<NSTR>["] {this.popState();}
|
<NSTR>["] {this.popState();}
|
||||||
|
@@ -70,5 +70,12 @@ const getStyles = (options) =>
|
|||||||
.edge {
|
.edge {
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
|
.mindmap-node-label {
|
||||||
|
dy: 1em;
|
||||||
|
alignment-baseline: middle;
|
||||||
|
text-anchor: middle;
|
||||||
|
dominant-baseline: middle;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
export default getStyles;
|
export default getStyles;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { select } from 'd3';
|
import { select } from 'd3';
|
||||||
import * as db from './mindmapDb';
|
import * as db from './mindmapDb';
|
||||||
|
import { createText } from '../../rendering-util/createText';
|
||||||
const MAX_SECTIONS = 12;
|
const MAX_SECTIONS = 12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,7 +12,7 @@ function wrap(text, width) {
|
|||||||
var text = select(this),
|
var text = select(this),
|
||||||
words = text
|
words = text
|
||||||
.text()
|
.text()
|
||||||
.split(/(\s+|<br>)/)
|
.split(/(\s+|<br\/>)/)
|
||||||
.reverse(),
|
.reverse(),
|
||||||
word,
|
word,
|
||||||
line = [],
|
line = [],
|
||||||
@@ -28,10 +29,10 @@ function wrap(text, width) {
|
|||||||
word = words[words.length - 1 - j];
|
word = words[words.length - 1 - j];
|
||||||
line.push(word);
|
line.push(word);
|
||||||
tspan.text(line.join(' ').trim());
|
tspan.text(line.join(' ').trim());
|
||||||
if (tspan.node().getComputedTextLength() > width || word === '<br>') {
|
if (tspan.node().getComputedTextLength() > width || word === '<br/>') {
|
||||||
line.pop();
|
line.pop();
|
||||||
tspan.text(line.join(' ').trim());
|
tspan.text(line.join(' ').trim());
|
||||||
if (word === '<br>') {
|
if (word === '<br/>') {
|
||||||
line = [''];
|
line = [''];
|
||||||
} else {
|
} else {
|
||||||
line = [word];
|
line = [word];
|
||||||
@@ -203,6 +204,7 @@ const roundedRectBkg = function (elem, node) {
|
|||||||
* @returns {number} The height nodes dom element
|
* @returns {number} The height nodes dom element
|
||||||
*/
|
*/
|
||||||
export const drawNode = function (elem, node, fullSection, conf) {
|
export const drawNode = function (elem, node, fullSection, conf) {
|
||||||
|
const htmlLabels = conf.htmlLabels;
|
||||||
const section = fullSection % (MAX_SECTIONS - 1);
|
const section = fullSection % (MAX_SECTIONS - 1);
|
||||||
const nodeElem = elem.append('g');
|
const nodeElem = elem.append('g');
|
||||||
node.section = section;
|
node.section = section;
|
||||||
@@ -215,15 +217,22 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
|||||||
|
|
||||||
// Create the wrapped text element
|
// Create the wrapped text element
|
||||||
const textElem = nodeElem.append('g');
|
const textElem = nodeElem.append('g');
|
||||||
const txt = textElem
|
const description = node.descr.replace(/(<br\/*>)/g, '\n');
|
||||||
.append('text')
|
const newEl = createText(textElem, description, {
|
||||||
.text(node.descr)
|
useHtmlLabels: htmlLabels,
|
||||||
.attr('dy', '1em')
|
width: node.width,
|
||||||
.attr('alignment-baseline', 'middle')
|
classes: 'mindmap-node-label',
|
||||||
.attr('dominant-baseline', 'middle')
|
});
|
||||||
.attr('text-anchor', 'middle')
|
|
||||||
.call(wrap, node.width);
|
if (!htmlLabels) {
|
||||||
const bbox = txt.node().getBBox();
|
textElem
|
||||||
|
.attr('dy', '1em')
|
||||||
|
.attr('alignment-baseline', 'middle')
|
||||||
|
.attr('dominant-baseline', 'middle')
|
||||||
|
.attr('text-anchor', 'middle');
|
||||||
|
}
|
||||||
|
// .call(wrap, node.width);
|
||||||
|
const bbox = textElem.node().getBBox();
|
||||||
const fontSize = conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
const fontSize = conf.fontSize.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||||
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||||
node.width = bbox.width + 2 * node.padding;
|
node.width = bbox.width + 2 * node.padding;
|
||||||
@@ -267,7 +276,16 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
|
if (!htmlLabels) {
|
||||||
|
const dx = node.width / 2;
|
||||||
|
const dy = node.padding / 2;
|
||||||
|
textElem.attr('transform', 'translate(' + dx + ', ' + dy + ')');
|
||||||
|
// textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
|
||||||
|
} else {
|
||||||
|
const dx = (node.width - bbox.width) / 2;
|
||||||
|
const dy = (node.height - bbox.height) / 2;
|
||||||
|
textElem.attr('transform', 'translate(' + dx + ', ' + dy + ')');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
|
@@ -3,6 +3,7 @@ import { select, scaleOrdinal, pie as d3pie, arc } from 'd3';
|
|||||||
import { log } from '../../logger';
|
import { log } from '../../logger';
|
||||||
import { configureSvgSize } from '../../setupGraphViewbox';
|
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||||
import * as configApi from '../../config';
|
import * as configApi from '../../config';
|
||||||
|
import { parseFontSize } from '../../utils';
|
||||||
|
|
||||||
let conf = configApi.getConfig();
|
let conf = configApi.getConfig();
|
||||||
|
|
||||||
@@ -88,6 +89,10 @@ export const draw = (txt, id, _version, diagObj) => {
|
|||||||
themeVariables.pie12,
|
themeVariables.pie12,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const textPosition = conf.pie?.textPosition ?? 0.75;
|
||||||
|
let [outerStrokeWidth] = parseFontSize(themeVariables.pieOuterStrokeWidth);
|
||||||
|
outerStrokeWidth ??= 2;
|
||||||
|
|
||||||
// Set the color scale
|
// Set the color scale
|
||||||
var color = scaleOrdinal().range(myGeneratedColors);
|
var color = scaleOrdinal().range(myGeneratedColors);
|
||||||
|
|
||||||
@@ -111,6 +116,16 @@ export const draw = (txt, id, _version, diagObj) => {
|
|||||||
|
|
||||||
// Shape helper to build arcs:
|
// Shape helper to build arcs:
|
||||||
var arcGenerator = arc().innerRadius(0).outerRadius(radius);
|
var arcGenerator = arc().innerRadius(0).outerRadius(radius);
|
||||||
|
var labelArcGenerator = arc()
|
||||||
|
.innerRadius(radius * textPosition)
|
||||||
|
.outerRadius(radius * textPosition);
|
||||||
|
|
||||||
|
svg
|
||||||
|
.append('circle')
|
||||||
|
.attr('cx', 0)
|
||||||
|
.attr('cy', 0)
|
||||||
|
.attr('r', radius + outerStrokeWidth / 2)
|
||||||
|
.attr('class', 'pieOuterCircle');
|
||||||
|
|
||||||
// Build the pie chart: each part of the pie is a path that we build using the arc function.
|
// Build the pie chart: each part of the pie is a path that we build using the arc function.
|
||||||
svg
|
svg
|
||||||
@@ -135,7 +150,7 @@ export const draw = (txt, id, _version, diagObj) => {
|
|||||||
return ((d.data.value / sum) * 100).toFixed(0) + '%';
|
return ((d.data.value / sum) * 100).toFixed(0) + '%';
|
||||||
})
|
})
|
||||||
.attr('transform', function (d) {
|
.attr('transform', function (d) {
|
||||||
return 'translate(' + arcGenerator.centroid(d) + ')';
|
return 'translate(' + labelArcGenerator.centroid(d) + ')';
|
||||||
})
|
})
|
||||||
.style('text-anchor', 'middle')
|
.style('text-anchor', 'middle')
|
||||||
.attr('class', 'slice');
|
.attr('class', 'slice');
|
||||||
|
@@ -5,6 +5,11 @@ const getStyles = (options) =>
|
|||||||
stroke-width : ${options.pieStrokeWidth};
|
stroke-width : ${options.pieStrokeWidth};
|
||||||
opacity : ${options.pieOpacity};
|
opacity : ${options.pieOpacity};
|
||||||
}
|
}
|
||||||
|
.pieOuterCircle{
|
||||||
|
stroke: ${options.pieOuterStrokeColor};
|
||||||
|
stroke-width: ${options.pieOuterStrokeWidth};
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
.pieTitleText {
|
.pieTitleText {
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
font-size: ${options.pieTitleTextSize};
|
font-size: ${options.pieTitleTextSize};
|
||||||
|
@@ -232,6 +232,9 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
|
|||||||
type: newNode.type,
|
type: newNode.type,
|
||||||
padding: 15, //getConfig().flowchart.padding
|
padding: 15, //getConfig().flowchart.padding
|
||||||
};
|
};
|
||||||
|
// if (useHtmlLabels) {
|
||||||
|
nodeData.centerLabel = true;
|
||||||
|
// }
|
||||||
|
|
||||||
if (parsedItem.note) {
|
if (parsedItem.note) {
|
||||||
// Todo: set random id
|
// Todo: set random id
|
||||||
@@ -240,6 +243,7 @@ const setupNode = (g, parent, parsedItem, diagramStates, diagramDb, altFlag) =>
|
|||||||
shape: SHAPE_NOTE,
|
shape: SHAPE_NOTE,
|
||||||
labelText: parsedItem.note.text,
|
labelText: parsedItem.note.text,
|
||||||
classes: CSS_DIAGRAM_NOTE,
|
classes: CSS_DIAGRAM_NOTE,
|
||||||
|
// useHtmlLabels: false,
|
||||||
style: '', // styles.style,
|
style: '', // styles.style,
|
||||||
id: itemId + NOTE_ID + '-' + graphItemCount,
|
id: itemId + NOTE_ID + '-' + graphItemCount,
|
||||||
domId: stateDomId(itemId, graphItemCount, NOTE),
|
domId: stateDomId(itemId, graphItemCount, NOTE),
|
||||||
|
@@ -28,7 +28,16 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
socialLinks: [
|
socialLinks: [
|
||||||
{ icon: 'github', link: 'https://github.com/mermaid-js/mermaid' },
|
{ icon: 'github', link: 'https://github.com/mermaid-js/mermaid' },
|
||||||
{ icon: 'slack', link: 'https://mermaid-talk.slack.com' },
|
{
|
||||||
|
icon: 'slack',
|
||||||
|
link: 'https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: {
|
||||||
|
svg: '<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 490.16 490.16"><defs><mask id="Mask"><rect x="0" y="0" width="490.16" height="490.16" fill="white" /><path fill="black" d="M407.48,111.18A165.2,165.2,0,0,0,245.08,220,165.2,165.2,0,0,0,82.68,111.18a165.5,165.5,0,0,0,72.06,143.64,88.81,88.81,0,0,1,38.53,73.45v50.86H296.9V328.27a88.8,88.8,0,0,1,38.52-73.45,165.41,165.41,0,0,0,72.06-143.64Z"/><path fill="black" d="M160.63,328.27a56.09,56.09,0,0,0-24.27-46.49,198.74,198.74,0,0,1-28.54-23.66A196.87,196.87,0,0,1,82.53,227V379.13h78.1Z"/><path fill="black" d="M329.53,328.27a56.09,56.09,0,0,1,24.27-46.49,198.74,198.74,0,0,0,28.54-23.66A196.87,196.87,0,0,0,407.63,227V379.13h-78.1Z"/></mask><style>.cls-1{fill:#76767B;}.cls-1:hover{fill:#FF3570}</style></defs><rect class="cls-1" width="490.16" height="490.16" rx="84.61" mask="url(#Mask)" /></svg>',
|
||||||
|
},
|
||||||
|
link: 'https://www.mermaidchart.com/',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -42,6 +51,11 @@ function nav() {
|
|||||||
activeMatch: '/config/',
|
activeMatch: '/config/',
|
||||||
},
|
},
|
||||||
{ text: 'Integrations', link: '/ecosystem/integrations', activeMatch: '/ecosystem/' },
|
{ text: 'Integrations', link: '/ecosystem/integrations', activeMatch: '/ecosystem/' },
|
||||||
|
{
|
||||||
|
text: 'Latest News',
|
||||||
|
link: '/news/announcements',
|
||||||
|
activeMatch: '/announcements',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: version,
|
text: version,
|
||||||
items: [
|
items: [
|
||||||
@@ -80,6 +94,7 @@ function sidebarAll() {
|
|||||||
...sidebarEcosystem(),
|
...sidebarEcosystem(),
|
||||||
...sidebarConfig(),
|
...sidebarConfig(),
|
||||||
...sidebarCommunity(),
|
...sidebarCommunity(),
|
||||||
|
...sidebarNews(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,3 +177,16 @@ function sidebarCommunity() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sidebarNews() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: '📰 Latest News',
|
||||||
|
collapsible: true,
|
||||||
|
items: [
|
||||||
|
{ text: 'Announcements', link: '/news/announcements' },
|
||||||
|
{ text: 'Blog', link: '/news/blog' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Tutorials
|
# Tutorials
|
||||||
|
|
||||||
This is list of publicly available Tutorials for using Mermaid.JS . This is intended as a basic introduction for the use of the Live Editor for generating diagrams, and deploying Mermaid.JS through HTML.
|
This is a list of publicly available Tutorials for using Mermaid.JS and is intended as a basic introduction for the use of the Live Editor for generating diagrams, and deploying Mermaid.JS through HTML.
|
||||||
|
|
||||||
**Note that these tutorials might display an older interface, but the usage of the live-editor will largely be the same.**
|
**Note that these tutorials might display an older interface, but the usage of the live-editor will largely be the same.**
|
||||||
|
|
||||||
|
@@ -183,6 +183,34 @@ The theming engine will only recognize hex colors and not color names. So, the v
|
|||||||
| activationBkgColor | secondaryColor | Activation Background Color |
|
| activationBkgColor | secondaryColor | Activation Background Color |
|
||||||
| sequenceNumberColor | calculated from lineColor | Sequence Number Color |
|
| sequenceNumberColor | calculated from lineColor | Sequence Number Color |
|
||||||
|
|
||||||
|
## Pie Diagram Variables
|
||||||
|
|
||||||
|
| Variable | Default value | Description |
|
||||||
|
| ------------------- | ------------------------------ | ------------------------------------------ |
|
||||||
|
| pie1 | primaryColor | Fill for 1st section in pie diagram |
|
||||||
|
| pie2 | secondaryColor | Fill for 2nd section in pie diagram |
|
||||||
|
| pie3 | calculated from tertiary | Fill for 3rd section in pie diagram |
|
||||||
|
| pie4 | calculated from primaryColor | Fill for 4th section in pie diagram |
|
||||||
|
| pie5 | calculated from secondaryColor | Fill for 5th section in pie diagram |
|
||||||
|
| pie6 | calculated from tertiaryColor | Fill for 6th section in pie diagram |
|
||||||
|
| pie7 | calculated from primaryColor | Fill for 7th section in pie diagram |
|
||||||
|
| pie8 | calculated from primaryColor | Fill for 8th section in pie diagram |
|
||||||
|
| pie9 | calculated from primaryColor | Fill for 9th section in pie diagram |
|
||||||
|
| pie10 | calculated from primaryColor | Fill for 10th section in pie diagram |
|
||||||
|
| pie11 | calculated from primaryColor | Fill for 11th section in pie diagram |
|
||||||
|
| pie12 | calculated from primaryColor | Fill for 12th section in pie diagram |
|
||||||
|
| pieTitleTextSize | 25px | Title text size |
|
||||||
|
| pieTitleTextColor | taskTextDarkColor | Title text color |
|
||||||
|
| pieSectionTextSize | 17px | Text size of individual section labels |
|
||||||
|
| pieSectionTextColor | textColor | Text color of individual section labels |
|
||||||
|
| pieLegendTextSize | 17px | Text size of labels in diagram legend |
|
||||||
|
| pieLegendTextColor | taskTextDarkColor | Text color of labels in diagram legend |
|
||||||
|
| pieStrokeColor | black | Border color of individual pie sections |
|
||||||
|
| pieStrokeWidth | 2px | Border width of individual pie sections |
|
||||||
|
| pieOuterStrokeWidth | 2px | Border width of pie diagram's outer circle |
|
||||||
|
| pieOuterStrokeColor | black | Border color of pie diagram's outer circle |
|
||||||
|
| pieOpacity | 0.7 | Opacity of individual pie sections |
|
||||||
|
|
||||||
## State Colors
|
## State Colors
|
||||||
|
|
||||||
| Variable | Default value | Description |
|
| Variable | Default value | Description |
|
||||||
|
@@ -244,6 +244,23 @@ The example below show an outline of how this could be used. The example just lo
|
|||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To determine the type of diagram present in a given text, you can utilize the `mermaid.detectType` function, as demonstrated in the example below.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from './mermaid.esm.mjs';
|
||||||
|
const graphDefinition = `sequenceDiagram
|
||||||
|
Pumbaa->>Timon:I ate like a pig.
|
||||||
|
Timon->>Pumbaa:Pumbaa, you ARE a pig.`;
|
||||||
|
try {
|
||||||
|
const type = mermaid.detectType(graphDefinition);
|
||||||
|
console.log(type); // 'sequence'
|
||||||
|
} catch (error) {
|
||||||
|
// UnknownDiagramError
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
### Binding events
|
### Binding events
|
||||||
|
|
||||||
Sometimes the generated graph also has defined interactions like tooltip and click events. When using the API one must
|
Sometimes the generated graph also has defined interactions like tooltip and click events. When using the API one must
|
||||||
|
@@ -14,6 +14,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [Gitea](https://gitea.io) (**Native support**)
|
- [Gitea](https://gitea.io) (**Native support**)
|
||||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||||
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
|
||||||
|
- [Mermaid Flow Visual Editor](https://www.mermaidflow.app) (**Native support**)
|
||||||
- [Deepdwn](https://billiam.itch.io/deepdwn) (**Native support**)
|
- [Deepdwn](https://billiam.itch.io/deepdwn) (**Native support**)
|
||||||
- [Joplin](https://joplinapp.org) (**Native support**)
|
- [Joplin](https://joplinapp.org) (**Native support**)
|
||||||
- [Swimm](https://swimm.io) (**Native support**)
|
- [Swimm](https://swimm.io) (**Native support**)
|
||||||
@@ -82,7 +83,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [FosWiki](https://foswiki.org)
|
- [FosWiki](https://foswiki.org)
|
||||||
- [Mermaid Plugin](https://foswiki.org/Extensions/MermaidPlugin)
|
- [Mermaid Plugin](https://foswiki.org/Extensions/MermaidPlugin)
|
||||||
- [DokuWiki](https://dokuwiki.org)
|
- [DokuWiki](https://dokuwiki.org)
|
||||||
- [Flowcharts](https://www.dokuwiki.org/plugin:flowcharts?s[]=mermaid)
|
- [Mermaid Plugin](https://www.dokuwiki.org/plugin:mermaid)
|
||||||
- [ComboStrap](https://combostrap.com/mermaid)
|
- [ComboStrap](https://combostrap.com/mermaid)
|
||||||
- [TiddlyWiki](https://tiddlywiki.com/)
|
- [TiddlyWiki](https://tiddlywiki.com/)
|
||||||
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
|
- [mermaid-tw5: full js library](https://github.com/efurlanm/mermaid-tw5)
|
||||||
@@ -143,7 +144,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
|||||||
- [remark-mermaid](https://github.com/temando/remark-mermaid)
|
- [remark-mermaid](https://github.com/temando/remark-mermaid)
|
||||||
- [jSDoc](https://jsdoc.app/)
|
- [jSDoc](https://jsdoc.app/)
|
||||||
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
|
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
|
||||||
- [MkDocs](https://mkdocs.org)
|
- [MkDocs](https://www.mkdocs.org)
|
||||||
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
|
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
|
||||||
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
|
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
|
||||||
- [Type Doc](https://typedoc.org/)
|
- [Type Doc](https://typedoc.org/)
|
||||||
|
@@ -23,15 +23,15 @@ features:
|
|||||||
- title: ➕ Easy to use!
|
- title: ➕ Easy to use!
|
||||||
details: Easily create and render detailed diagrams and charts with the Mermaid Live Editor.
|
details: Easily create and render detailed diagrams and charts with the Mermaid Live Editor.
|
||||||
link: https://mermaid.live/
|
link: https://mermaid.live/
|
||||||
- title: 🎥 Video Tutorials!
|
|
||||||
details: Curated list of video tutorials and examples created by the community.
|
|
||||||
link: ../../config/Tutorials.html
|
|
||||||
- title: 🧩 Integrations available!
|
- title: 🧩 Integrations available!
|
||||||
details: Use Mermaid with your favorite applications, check out the integrations list.
|
details: Use Mermaid with your favorite applications, check out the integrations list.
|
||||||
link: ../../ecosystem/integrations.md
|
link: ../../ecosystem/integrations.md
|
||||||
- title: 🏆 Award winning!
|
- title: 🏆 Award winning!
|
||||||
details: 2019 JavaScript Open Source Award winner for "The Most Exciting Use of Technology".
|
details: 2019 JavaScript Open Source Award winner for "The Most Exciting Use of Technology".
|
||||||
link: https://osawards.com/javascript/2019
|
link: https://osawards.com/javascript/2019
|
||||||
|
- title: 🥰 Mermaid + Mermaid Chart
|
||||||
|
details: Mermaid Chart is a major supporter of the Mermaid project.
|
||||||
|
link: https://www.mermaidchart.com/
|
||||||
---
|
---
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -149,6 +149,12 @@ const members = [
|
|||||||
title: "Developer",
|
title: "Developer",
|
||||||
links: [{ icon: "github", link: "https://github.com/spopida" }],
|
links: [{ icon: "github", link: "https://github.com/spopida" }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
avatar: "https://avatars.githubusercontent.com/u/35910788?v=4",
|
||||||
|
name: "Steph Huynh",
|
||||||
|
title: "Developer",
|
||||||
|
links: [{ icon: "github", link: "https://github.com/huynhicode" }],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
7
packages/mermaid/src/docs/news/announcements.md
Normal file
7
packages/mermaid/src/docs/news/announcements.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Announcements
|
||||||
|
|
||||||
|
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
|
||||||
|
|
||||||
|
3 April 2023 · 3 mins
|
||||||
|
|
||||||
|
Markdown Strings reduce the hassle # Starting from v10.
|
25
packages/mermaid/src/docs/news/blog.md
Normal file
25
packages/mermaid/src/docs/news/blog.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Blog
|
||||||
|
|
||||||
|
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
|
||||||
|
|
||||||
|
27 March 2023 · 2 mins
|
||||||
|
|
||||||
|
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
|
||||||
|
|
||||||
|
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
|
||||||
|
|
||||||
|
8 March 2023 · 9 mins
|
||||||
|
|
||||||
|
The hype around AI in general and ChatGPT, in particular, is so intense that it’s very understandable to assume the hype train is driving straight toward the trough of disillusionment.
|
||||||
|
|
||||||
|
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
|
||||||
|
|
||||||
|
1 March 2023 · 12 mins
|
||||||
|
|
||||||
|
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
|
||||||
|
|
||||||
|
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
|
||||||
|
|
||||||
|
10 February 2023 · 10 mins
|
||||||
|
|
||||||
|
Busting the myth that developers can’t write # It’s an annoying stereotype that developers don’t know how to write, speak, and otherwise communicate.
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 12 KiB |
@@ -122,16 +122,7 @@ flowchart LR
|
|||||||
|
|
||||||
### A hexagon node
|
### A hexagon node
|
||||||
|
|
||||||
Code:
|
```mermaid-example
|
||||||
|
|
||||||
```mmd
|
|
||||||
flowchart LR
|
|
||||||
id1{{This is the text in the box}}
|
|
||||||
```
|
|
||||||
|
|
||||||
Render:
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
flowchart LR
|
||||||
id1{{This is the text in the box}}
|
id1{{This is the text in the box}}
|
||||||
```
|
```
|
||||||
@@ -455,6 +446,31 @@ flowchart LR
|
|||||||
B1 --> B2
|
B1 --> B2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Markdown Strings
|
||||||
|
|
||||||
|
The "Markdown Strings" feature enhances flowcharts and mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||||
|
flowchart LR
|
||||||
|
subgraph "One"
|
||||||
|
a("`The **cat**
|
||||||
|
in the hat`") -- "edge label" --> b{{"`The **dog** in the hog`"}}
|
||||||
|
end
|
||||||
|
subgraph "`**Two**`"
|
||||||
|
c("`The **cat**
|
||||||
|
in the hat`") -- "`Bold **edge label**`" --> d("The dog in the hog")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Formatting:
|
||||||
|
|
||||||
|
- For bold text, use double asterisks \*\* before and after the text.
|
||||||
|
- For italics, use single asterisks \* before and after the text.
|
||||||
|
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
|
||||||
|
|
||||||
|
This feature is applicable to node labels, edge labels, and subgraph labels.
|
||||||
|
|
||||||
## Interaction
|
## Interaction
|
||||||
|
|
||||||
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab. **Note**: This functionality is disabled when using `securityLevel='strict'` and enabled when using `securityLevel='loose'`.
|
||||||
|
@@ -189,9 +189,27 @@ The pattern is:
|
|||||||
|
|
||||||
More info in: [https://github.com/d3/d3-time#interval_every](https://github.com/d3/d3-time#interval_every)
|
More info in: [https://github.com/d3/d3-time#interval_every](https://github.com/d3/d3-time#interval_every)
|
||||||
|
|
||||||
|
## Output in compact mode
|
||||||
|
|
||||||
|
The compact mode allows you to display multiple tasks in the same row. Compact mode can be enabled for a gantt chart by setting the display mode of the graph via preceeding YAML settings.
|
||||||
|
|
||||||
|
```mmd
|
||||||
|
---
|
||||||
|
displayMode: compact
|
||||||
|
---
|
||||||
|
gantt
|
||||||
|
title A Gantt Diagram
|
||||||
|
dateFormat YYYY-MM-DD
|
||||||
|
|
||||||
|
section Section
|
||||||
|
A task :a1, 2014-01-01, 30d
|
||||||
|
Another task :a2, 2014-01-20, 25d
|
||||||
|
Another one :a3, 2014-02-10, 20d
|
||||||
|
```
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
|
|
||||||
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
|
Comments can be entered within a gantt chart, which will be ignored by the parser. Comments need to be on their own line and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax.
|
||||||
|
|
||||||
```mmd
|
```mmd
|
||||||
gantt
|
gantt
|
||||||
@@ -356,3 +374,24 @@ Beginner's tip—a full example using interactive links in an html context:
|
|||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Bar chart (using gantt chart)
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
gantt
|
||||||
|
title Git Issues - days since last update
|
||||||
|
dateFormat X
|
||||||
|
axisFormat %s
|
||||||
|
section Issue19062
|
||||||
|
71 : 0, 71
|
||||||
|
section Issue19401
|
||||||
|
36 : 0, 36
|
||||||
|
section Issue193
|
||||||
|
34 : 0, 34
|
||||||
|
section Issue7441
|
||||||
|
9 : 0, 9
|
||||||
|
section Issue1300
|
||||||
|
5 : 0, 5
|
||||||
|
```
|
||||||
|
@@ -138,7 +138,7 @@ mindmap
|
|||||||
C
|
C
|
||||||
```
|
```
|
||||||
|
|
||||||
_These classes needs top be supplied by the site administrator._
|
_These classes need to be supplied by the site administrator._
|
||||||
|
|
||||||
## Unclear indentation
|
## Unclear indentation
|
||||||
|
|
||||||
@@ -162,6 +162,25 @@ Root
|
|||||||
C
|
C
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Markdown Strings
|
||||||
|
|
||||||
|
The "Markdown Strings" feature enhances mind maps by offering a more versatile string type, which supports text formatting options such as bold and italics, and automatically wraps text within labels.
|
||||||
|
|
||||||
|
```mermaid-example
|
||||||
|
mindmap
|
||||||
|
id1["`**Root** with
|
||||||
|
a second line
|
||||||
|
Unicode works too: 🤓`"]
|
||||||
|
id2["`The dog in **the** hog... a *very long text* that wraps to a new line`"]
|
||||||
|
id3[Regular labels still works]
|
||||||
|
```
|
||||||
|
|
||||||
|
Formatting:
|
||||||
|
|
||||||
|
- For bold text, use double asterisks \*\* before and after the text.
|
||||||
|
- For italics, use single asterisks \* before and after the text.
|
||||||
|
- With traditional strings, you needed to add <br> tags for text to wrap in nodes. However, markdown strings automatically wrap text when it becomes too long and allows you to start a new line by simply using a newline character instead of a <br> tag.
|
||||||
|
|
||||||
## Integrating with your library/website.
|
## Integrating with your library/website.
|
||||||
|
|
||||||
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.
|
Mindmap uses the experimental lazy loading & async rendering features which could change in the future. From version 9.4.0 this diagram is included in mermaid but use lazy loading in order to keep the size of mermaid down. This is important in order to be able to add additional diagrams going forward.
|
||||||
|
@@ -35,6 +35,7 @@ Drawing a pie chart is really simple in mermaid.
|
|||||||
## Example
|
## Example
|
||||||
|
|
||||||
```mermaid-example
|
```mermaid-example
|
||||||
|
%%{init: {"pie": {"textPosition": 0.5}, "themeVariables": {"pieOuterStrokeWidth": "5px"}} }%%
|
||||||
pie showData
|
pie showData
|
||||||
title Key elements in Product X
|
title Key elements in Product X
|
||||||
"Calcium" : 42.96
|
"Calcium" : 42.96
|
||||||
@@ -42,3 +43,11 @@ pie showData
|
|||||||
"Magnesium" : 10.01
|
"Magnesium" : 10.01
|
||||||
"Iron" : 5
|
"Iron" : 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Possible pie diagram configuration parameters:
|
||||||
|
|
||||||
|
| Parameter | Description | Default value |
|
||||||
|
| -------------- | ------------------------------------------------------------------------------------------------------------ | ------------- |
|
||||||
|
| `textPosition` | The axial position of the pie slice labels, from 0.0 at the center to 1.0 at the outside edge of the circle. | `0.75` |
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
|
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
|
||||||
|
|
||||||
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
|
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
|
||||||
|
|
||||||
### An example of a timeline.
|
### An example of a timeline.
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ However, if there is no section defined, then we have two possibilities:
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that this is no, section defined, and each time period and its corresponding events will have its own color scheme.
|
Note that there are no sections defined, and each time period and its corresponding events will have its own color scheme.
|
||||||
|
|
||||||
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
|
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ let us look at same example, where we have disabled the multiColor option.
|
|||||||
|
|
||||||
### Customizing Color scheme
|
### Customizing Color scheme
|
||||||
|
|
||||||
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
|
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12 sections, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
|
||||||
In case you have more than 12 sections, the color scheme will start to repeat.
|
In case you have more than 12 sections, the color scheme will start to repeat.
|
||||||
|
|
||||||
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.
|
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs/esm/index.js';
|
||||||
|
|
||||||
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||||
|
|
||||||
|
@@ -7,7 +7,11 @@ import { MermaidConfig } from './config.type';
|
|||||||
import { log } from './logger';
|
import { log } from './logger';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import { mermaidAPI, ParseOptions, RenderResult } from './mermaidAPI';
|
import { mermaidAPI, ParseOptions, RenderResult } from './mermaidAPI';
|
||||||
import { registerLazyLoadedDiagrams, loadRegisteredDiagrams } from './diagram-api/detectType';
|
import {
|
||||||
|
registerLazyLoadedDiagrams,
|
||||||
|
loadRegisteredDiagrams,
|
||||||
|
detectType,
|
||||||
|
} from './diagram-api/detectType';
|
||||||
import type { ParseErrorFunction } from './Diagram';
|
import type { ParseErrorFunction } from './Diagram';
|
||||||
import { isDetailedError } from './utils';
|
import { isDetailedError } from './utils';
|
||||||
import type { DetailedError } from './utils';
|
import type { DetailedError } from './utils';
|
||||||
@@ -388,7 +392,7 @@ const render = (id: string, text: string, container?: Element): Promise<RenderRe
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const mermaid: {
|
export interface Mermaid {
|
||||||
startOnLoad: boolean;
|
startOnLoad: boolean;
|
||||||
parseError?: ParseErrorFunction;
|
parseError?: ParseErrorFunction;
|
||||||
mermaidAPI: typeof mermaidAPI;
|
mermaidAPI: typeof mermaidAPI;
|
||||||
@@ -400,7 +404,10 @@ const mermaid: {
|
|||||||
initialize: typeof initialize;
|
initialize: typeof initialize;
|
||||||
contentLoaded: typeof contentLoaded;
|
contentLoaded: typeof contentLoaded;
|
||||||
setParseErrorHandler: typeof setParseErrorHandler;
|
setParseErrorHandler: typeof setParseErrorHandler;
|
||||||
} = {
|
detectType: typeof detectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mermaid: Mermaid = {
|
||||||
startOnLoad: true,
|
startOnLoad: true,
|
||||||
mermaidAPI,
|
mermaidAPI,
|
||||||
parse,
|
parse,
|
||||||
@@ -412,6 +419,7 @@ const mermaid: {
|
|||||||
parseError: undefined,
|
parseError: undefined,
|
||||||
contentLoaded,
|
contentLoaded,
|
||||||
setParseErrorHandler,
|
setParseErrorHandler,
|
||||||
|
detectType,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default mermaid;
|
export default mermaid;
|
||||||
|
@@ -689,7 +689,7 @@ describe('mermaidAPI', () => {
|
|||||||
it('resolves for valid definition', async () => {
|
it('resolves for valid definition', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
mermaidAPI.parse('graph TD;A--x|text including URL space|B;')
|
mermaidAPI.parse('graph TD;A--x|text including URL space|B;')
|
||||||
).resolves.not.toThrow();
|
).resolves.toBeTruthy();
|
||||||
});
|
});
|
||||||
it('returns true for valid definition with silent option', async () => {
|
it('returns true for valid definition with silent option', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
|
@@ -32,7 +32,14 @@ import { setA11yDiagramInfo, addSVGa11yTitleDescription } from './accessibility'
|
|||||||
import { parseDirective } from './directiveUtils';
|
import { parseDirective } from './directiveUtils';
|
||||||
|
|
||||||
// diagram names that support classDef statements
|
// diagram names that support classDef statements
|
||||||
const CLASSDEF_DIAGRAMS = ['graph', 'flowchart', 'flowchart-v2', 'stateDiagram', 'stateDiagram-v2'];
|
const CLASSDEF_DIAGRAMS = [
|
||||||
|
'graph',
|
||||||
|
'flowchart',
|
||||||
|
'flowchart-v2',
|
||||||
|
'flowchart-elk',
|
||||||
|
'stateDiagram',
|
||||||
|
'stateDiagram-v2',
|
||||||
|
];
|
||||||
const MAX_TEXTLENGTH = 50_000;
|
const MAX_TEXTLENGTH = 50_000;
|
||||||
const MAX_TEXTLENGTH_EXCEEDED_MSG =
|
const MAX_TEXTLENGTH_EXCEEDED_MSG =
|
||||||
'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
'graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa';
|
||||||
@@ -99,21 +106,18 @@ export interface RenderResult {
|
|||||||
* @throws Error if the diagram is invalid and parseOptions.suppressErrors is false.
|
* @throws Error if the diagram is invalid and parseOptions.suppressErrors is false.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async function parse(text: string, parseOptions?: ParseOptions): Promise<boolean | void> {
|
async function parse(text: string, parseOptions?: ParseOptions): Promise<boolean> {
|
||||||
addDiagrams();
|
addDiagrams();
|
||||||
let error;
|
|
||||||
try {
|
try {
|
||||||
const diagram = await getDiagramFromText(text);
|
const diagram = await getDiagramFromText(text);
|
||||||
diagram.parse();
|
diagram.parse();
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
error = err;
|
if (parseOptions?.suppressErrors) {
|
||||||
}
|
return false;
|
||||||
if (parseOptions?.suppressErrors) {
|
}
|
||||||
return error === undefined;
|
|
||||||
}
|
|
||||||
if (error) {
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -534,6 +538,10 @@ const render = async function (
|
|||||||
|
|
||||||
attachFunctions();
|
attachFunctions();
|
||||||
|
|
||||||
|
if (parseEncounteredException) {
|
||||||
|
throw parseEncounteredException;
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------
|
||||||
// Remove the temporary HTML element if appropriate
|
// Remove the temporary HTML element if appropriate
|
||||||
const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector;
|
const tmpElementSelector = isSandboxed ? iFrameID_selector : enclosingDivID_selector;
|
||||||
@@ -542,10 +550,6 @@ const render = async function (
|
|||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parseEncounteredException) {
|
|
||||||
throw parseEncounteredException;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
svg: svgCode,
|
svg: svgCode,
|
||||||
bindFunctions: diag.db.bindFunctions,
|
bindFunctions: diag.db.bindFunctions,
|
||||||
@@ -653,6 +657,7 @@ function addA11yInfo(
|
|||||||
* numberSectionStyles: 4,
|
* numberSectionStyles: 4,
|
||||||
* axisFormat: '%Y-%m-%d',
|
* axisFormat: '%Y-%m-%d',
|
||||||
* topAxis: false,
|
* topAxis: false,
|
||||||
|
* displayMode: '',
|
||||||
* },
|
* },
|
||||||
* };
|
* };
|
||||||
* mermaid.initialize(config);
|
* mermaid.initialize(config);
|
||||||
@@ -663,6 +668,7 @@ export const mermaidAPI = Object.freeze({
|
|||||||
render,
|
render,
|
||||||
parse,
|
parse,
|
||||||
parseDirective,
|
parseDirective,
|
||||||
|
getDiagramFromText,
|
||||||
initialize,
|
initialize,
|
||||||
getConfig: configApi.getConfig,
|
getConfig: configApi.getConfig,
|
||||||
setConfig: configApi.setConfig,
|
setConfig: configApi.setConfig,
|
||||||
|
224
packages/mermaid/src/rendering-util/createText.js
Normal file
224
packages/mermaid/src/rendering-util/createText.js
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
import { select } from 'd3';
|
||||||
|
import { log } from '../logger';
|
||||||
|
import { getConfig } from '../config';
|
||||||
|
import { evaluate } from '../diagrams/common/common';
|
||||||
|
import { decodeEntities } from '../mermaidAPI';
|
||||||
|
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text';
|
||||||
|
/**
|
||||||
|
* @param dom
|
||||||
|
* @param styleFn
|
||||||
|
*/
|
||||||
|
function applyStyle(dom, styleFn) {
|
||||||
|
if (styleFn) {
|
||||||
|
dom.attr('style', styleFn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param element
|
||||||
|
* @param {any} node
|
||||||
|
* @param width
|
||||||
|
* @param classes
|
||||||
|
* @returns {SVGForeignObjectElement} Node
|
||||||
|
*/
|
||||||
|
function addHtmlSpan(element, node, width, classes) {
|
||||||
|
const fo = element.append('foreignObject');
|
||||||
|
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||||
|
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||||
|
const div = fo.append('xhtml:div');
|
||||||
|
// const div = body.append('div');
|
||||||
|
// const div = fo.append('div');
|
||||||
|
|
||||||
|
const label = node.label;
|
||||||
|
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||||
|
div.html(
|
||||||
|
`<span class="${labelClass} ${classes}" ` +
|
||||||
|
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
|
||||||
|
'>' +
|
||||||
|
label +
|
||||||
|
'</span>'
|
||||||
|
);
|
||||||
|
|
||||||
|
applyStyle(div, node.labelStyle);
|
||||||
|
div.style('display', 'table-cell');
|
||||||
|
div.style('white-space', 'nowrap');
|
||||||
|
div.style('max-width', width + 'px');
|
||||||
|
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||||
|
|
||||||
|
let bbox = div.node().getBoundingClientRect();
|
||||||
|
if (bbox.width === width) {
|
||||||
|
div.style('display', 'table');
|
||||||
|
div.style('white-space', 'break-spaces');
|
||||||
|
div.style('width', width + 'px');
|
||||||
|
bbox = div.node().getBoundingClientRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fo.style('width', bbox.width);
|
||||||
|
fo.style('height', bbox.height);
|
||||||
|
|
||||||
|
return fo.node();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a tspan element with the specified attributes for text positioning.
|
||||||
|
*
|
||||||
|
* @param {object} textElement - The parent text element to append the tspan element.
|
||||||
|
* @param {number} lineIndex - The index of the current line in the structuredText array.
|
||||||
|
* @param {number} lineHeight - The line height value for the text.
|
||||||
|
* @returns {object} The created tspan element.
|
||||||
|
*/
|
||||||
|
function createTspan(textElement, lineIndex, lineHeight) {
|
||||||
|
return textElement
|
||||||
|
.append('tspan')
|
||||||
|
.attr('class', 'text-outer-tspan')
|
||||||
|
.attr('x', 0)
|
||||||
|
.attr('y', lineIndex * lineHeight - 0.1 + 'em')
|
||||||
|
.attr('dy', lineHeight + 'em');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a formatted text element by breaking lines and applying styles based on
|
||||||
|
* the given structuredText.
|
||||||
|
*
|
||||||
|
* @param {number} width - The maximum allowed width of the text.
|
||||||
|
* @param {object} g - The parent group element to append the formatted text.
|
||||||
|
* @param {Array} structuredText - The structured text data to format.
|
||||||
|
* @param addBackground
|
||||||
|
*/
|
||||||
|
function createFormattedText(width, g, structuredText, addBackground = false) {
|
||||||
|
const lineHeight = 1.1;
|
||||||
|
const labelGroup = g.append('g');
|
||||||
|
let bkg = labelGroup.insert('rect').attr('class', 'background');
|
||||||
|
const textElement = labelGroup.append('text').attr('y', '-10.1');
|
||||||
|
// .attr('dominant-baseline', 'middle')
|
||||||
|
// .attr('text-anchor', 'middle');
|
||||||
|
// .attr('text-anchor', 'middle');
|
||||||
|
let lineIndex = -1;
|
||||||
|
structuredText.forEach((line) => {
|
||||||
|
lineIndex++;
|
||||||
|
let tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||||
|
|
||||||
|
let words = [...line].reverse();
|
||||||
|
let currentWord;
|
||||||
|
let wrappedLine = [];
|
||||||
|
|
||||||
|
while (words.length) {
|
||||||
|
currentWord = words.pop();
|
||||||
|
wrappedLine.push(currentWord);
|
||||||
|
|
||||||
|
updateTextContentAndStyles(tspan, wrappedLine);
|
||||||
|
|
||||||
|
if (tspan.node().getComputedTextLength() > width) {
|
||||||
|
wrappedLine.pop();
|
||||||
|
words.push(currentWord);
|
||||||
|
|
||||||
|
updateTextContentAndStyles(tspan, wrappedLine);
|
||||||
|
|
||||||
|
wrappedLine = [];
|
||||||
|
lineIndex++;
|
||||||
|
tspan = createTspan(textElement, lineIndex, lineHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (addBackground) {
|
||||||
|
const bbox = textElement.node().getBBox();
|
||||||
|
const padding = 2;
|
||||||
|
bkg
|
||||||
|
.attr('x', -padding)
|
||||||
|
.attr('y', -padding)
|
||||||
|
.attr('width', bbox.width + 2 * padding)
|
||||||
|
.attr('height', bbox.height + 2 * padding);
|
||||||
|
// .style('fill', 'red');
|
||||||
|
|
||||||
|
return labelGroup.node();
|
||||||
|
} else {
|
||||||
|
return textElement.node();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the text content and styles of the given tspan element based on the
|
||||||
|
* provided wrappedLine data.
|
||||||
|
*
|
||||||
|
* @param {object} tspan - The tspan element to update.
|
||||||
|
* @param {Array} wrappedLine - The line data to apply to the tspan element.
|
||||||
|
*/
|
||||||
|
function updateTextContentAndStyles(tspan, wrappedLine) {
|
||||||
|
tspan.text('');
|
||||||
|
|
||||||
|
wrappedLine.forEach((word, index) => {
|
||||||
|
const innerTspan = tspan
|
||||||
|
.append('tspan')
|
||||||
|
.attr('font-style', word.type === 'em' ? 'italic' : 'normal')
|
||||||
|
.attr('class', 'text-inner-tspan')
|
||||||
|
.attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
|
||||||
|
const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
|
||||||
|
if (index === 0) {
|
||||||
|
innerTspan.text(word.content);
|
||||||
|
} else {
|
||||||
|
innerTspan.text(' ' + word.content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param el
|
||||||
|
* @param {*} text
|
||||||
|
* @param {*} param1
|
||||||
|
* @param root0
|
||||||
|
* @param root0.style
|
||||||
|
* @param root0.isTitle
|
||||||
|
* @param root0.classes
|
||||||
|
* @param root0.useHtmlLabels
|
||||||
|
* @param root0.isNode
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
// Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel'
|
||||||
|
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
|
||||||
|
export const createText = (
|
||||||
|
el,
|
||||||
|
text = '',
|
||||||
|
{
|
||||||
|
style = '',
|
||||||
|
isTitle = false,
|
||||||
|
classes = '',
|
||||||
|
useHtmlLabels = true,
|
||||||
|
isNode = true,
|
||||||
|
width,
|
||||||
|
addSvgBackground = false,
|
||||||
|
} = {}
|
||||||
|
) => {
|
||||||
|
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
|
||||||
|
if (useHtmlLabels) {
|
||||||
|
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||||
|
// text = text.replace(/\\n|\n/g, '<br />');
|
||||||
|
const htmlText = markdownToHTML(text);
|
||||||
|
// log.info('markdo wnToHTML' + text, markdownToHTML(text));
|
||||||
|
const node = {
|
||||||
|
isNode,
|
||||||
|
label: decodeEntities(htmlText).replace(
|
||||||
|
/fa[blrs]?:fa-[\w-]+/g,
|
||||||
|
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||||
|
),
|
||||||
|
labelStyle: style.replace('fill:', 'color:'),
|
||||||
|
};
|
||||||
|
let vertexNode = addHtmlSpan(el, node, width, classes);
|
||||||
|
return vertexNode;
|
||||||
|
} else {
|
||||||
|
const structuredText = markdownToLines(text);
|
||||||
|
const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
|
||||||
|
let lastWord;
|
||||||
|
structuredText.forEach((line) => {
|
||||||
|
line.forEach((word) => {
|
||||||
|
if (special.includes(word.content) && lastWord) {
|
||||||
|
lastWord.content += word.content;
|
||||||
|
word.content = '';
|
||||||
|
}
|
||||||
|
lastWord = word;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
|
||||||
|
return svgLabel;
|
||||||
|
}
|
||||||
|
};
|
94
packages/mermaid/src/rendering-util/handle-markdown-text.js
Normal file
94
packages/mermaid/src/rendering-util/handle-markdown-text.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param markdown
|
||||||
|
*/
|
||||||
|
function preprocessMarkdown(markdown) {
|
||||||
|
// Replace multiple newlines with a single newline
|
||||||
|
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
|
||||||
|
// Remove extra spaces at the beginning of each line
|
||||||
|
const withoutExtraSpaces = withoutMultipleNewlines.replace(/^\s+/gm, '');
|
||||||
|
return withoutExtraSpaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param markdown
|
||||||
|
*/
|
||||||
|
export function markdownToLines(markdown) {
|
||||||
|
const preprocessedMarkdown = preprocessMarkdown(markdown);
|
||||||
|
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||||
|
const syntaxTree = mdParse(preprocessedMarkdown);
|
||||||
|
|
||||||
|
let lines = [[]];
|
||||||
|
let currentLine = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
* @param parentType
|
||||||
|
*/
|
||||||
|
function processNode(node, parentType) {
|
||||||
|
if (node.type === 'text') {
|
||||||
|
const textLines = node.content.split('\n');
|
||||||
|
|
||||||
|
textLines.forEach((textLine, index) => {
|
||||||
|
if (index !== 0) {
|
||||||
|
currentLine++;
|
||||||
|
lines.push([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// textLine.split(/ (?=[^!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]+)/).forEach((word) => {
|
||||||
|
textLine.split(' ').forEach((word) => {
|
||||||
|
if (word) {
|
||||||
|
lines[currentLine].push({ content: word, type: parentType || 'normal' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (node.type === 'strong' || node.type === 'em') {
|
||||||
|
node.content.forEach((contentNode) => {
|
||||||
|
processNode(contentNode, node.type);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syntaxTree.forEach((treeNode) => {
|
||||||
|
if (treeNode.type === 'paragraph') {
|
||||||
|
treeNode.content.forEach((contentNode) => {
|
||||||
|
processNode(contentNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param markdown
|
||||||
|
*/
|
||||||
|
export function markdownToHTML(markdown) {
|
||||||
|
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||||
|
const syntaxTree = mdParse(markdown);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
function output(node) {
|
||||||
|
if (node.type === 'text') {
|
||||||
|
return node.content.replace(/\n/g, '<br/>');
|
||||||
|
} else if (node.type === 'strong') {
|
||||||
|
return `<strong>${node.content.map(output).join('')}</strong>`;
|
||||||
|
} else if (node.type === 'em') {
|
||||||
|
return `<em>${node.content.map(output).join('')}</em>`;
|
||||||
|
} else if (node.type === 'paragraph') {
|
||||||
|
return `<p>${node.content.map(output).join('')}</p>`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return syntaxTree.map(output).join('');
|
||||||
|
}
|
253
packages/mermaid/src/rendering-util/handle-markdown-text.spec.js
Normal file
253
packages/mermaid/src/rendering-util/handle-markdown-text.spec.js
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
// import { test } from 'vitest';
|
||||||
|
import { markdownToLines, markdownToHTML } from './handle-markdown-text';
|
||||||
|
import { test } from 'vitest';
|
||||||
|
|
||||||
|
test('markdownToLines - Basic test', () => {
|
||||||
|
const input = `This is regular text
|
||||||
|
Here is a new line
|
||||||
|
There is some words **with a bold** section
|
||||||
|
Here is a line *with an italic* section`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'This', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'regular', type: 'normal' },
|
||||||
|
{ content: 'text', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'Here', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'new', type: 'normal' },
|
||||||
|
{ content: 'line', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'There', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'some', type: 'normal' },
|
||||||
|
{ content: 'words', type: 'normal' },
|
||||||
|
{ content: 'with', type: 'strong' },
|
||||||
|
{ content: 'a', type: 'strong' },
|
||||||
|
{ content: 'bold', type: 'strong' },
|
||||||
|
{ content: 'section', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'Here', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'line', type: 'normal' },
|
||||||
|
{ content: 'with', type: 'em' },
|
||||||
|
{ content: 'an', type: 'em' },
|
||||||
|
{ content: 'italic', type: 'em' },
|
||||||
|
{ content: 'section', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - Empty input', () => {
|
||||||
|
const input = '';
|
||||||
|
const expectedOutput = [[]];
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - No formatting', () => {
|
||||||
|
const input = `This is a simple test
|
||||||
|
with no formatting`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'This', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'simple', type: 'normal' },
|
||||||
|
{ content: 'test', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'with', type: 'normal' },
|
||||||
|
{ content: 'no', type: 'normal' },
|
||||||
|
{ content: 'formatting', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - Only bold formatting', () => {
|
||||||
|
const input = `This is a **bold** test`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'This', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'bold', type: 'strong' },
|
||||||
|
{ content: 'test', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - paragraph 1', () => {
|
||||||
|
const input = `**Start** with
|
||||||
|
a second line`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'Start', type: 'strong' },
|
||||||
|
{ content: 'with', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'second', type: 'normal' },
|
||||||
|
{ content: 'line', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - paragraph', () => {
|
||||||
|
const input = `**Start** with
|
||||||
|
|
||||||
|
a second line`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'Start', type: 'strong' },
|
||||||
|
{ content: 'with', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'second', type: 'normal' },
|
||||||
|
{ content: 'line', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToLines - Only italic formatting', () => {
|
||||||
|
const input = `This is an *italic* test`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'This', type: 'normal' },
|
||||||
|
{ content: 'is', type: 'normal' },
|
||||||
|
{ content: 'an', type: 'normal' },
|
||||||
|
{ content: 'italic', type: 'em' },
|
||||||
|
{ content: 'test', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('markdownToLines - Mixed formatting', () => {
|
||||||
|
const input = `*Italic* and **bold** formatting`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'Italic', type: 'em' },
|
||||||
|
{ content: 'and', type: 'normal' },
|
||||||
|
{ content: 'bold', type: 'strong' },
|
||||||
|
{ content: 'formatting', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('markdownToLines - Mixed formatting', () => {
|
||||||
|
const input = `The dog in **the** hog... a *very long text* about it
|
||||||
|
Word!`;
|
||||||
|
|
||||||
|
const expectedOutput = [
|
||||||
|
[
|
||||||
|
{ content: 'The', type: 'normal' },
|
||||||
|
{ content: 'dog', type: 'normal' },
|
||||||
|
{ content: 'in', type: 'normal' },
|
||||||
|
{ content: 'the', type: 'strong' },
|
||||||
|
{ content: 'hog', type: 'normal' },
|
||||||
|
{ content: '.', type: 'normal' },
|
||||||
|
{ content: '.', type: 'normal' },
|
||||||
|
{ content: '.', type: 'normal' },
|
||||||
|
{ content: 'a', type: 'normal' },
|
||||||
|
{ content: 'very', type: 'em' },
|
||||||
|
{ content: 'long', type: 'em' },
|
||||||
|
{ content: 'text', type: 'em' },
|
||||||
|
{ content: 'about', type: 'normal' },
|
||||||
|
{ content: 'it', type: 'normal' },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ content: 'Word', type: 'normal' },
|
||||||
|
{ content: '!', type: 'normal' },
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const output = markdownToLines(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - Basic test', () => {
|
||||||
|
const input = `This is regular text
|
||||||
|
Here is a new line
|
||||||
|
There is some words **with a bold** section
|
||||||
|
Here is a line *with an italic* section`;
|
||||||
|
|
||||||
|
const expectedOutput = `<p>This is regular text<br/>Here is a new line<br/>There is some words <strong>with a bold</strong> section<br/>Here is a line <em>with an italic</em> section</p>`;
|
||||||
|
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - Empty input', () => {
|
||||||
|
const input = '';
|
||||||
|
const expectedOutput = '';
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - No formatting', () => {
|
||||||
|
const input = `This is a simple test
|
||||||
|
with no formatting`;
|
||||||
|
|
||||||
|
const expectedOutput = `<p>This is a simple test<br/>with no formatting</p>`;
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - Only bold formatting', () => {
|
||||||
|
const input = `This is a **bold** test`;
|
||||||
|
|
||||||
|
const expectedOutput = `<p>This is a <strong>bold</strong> test</p>`;
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - Only italic formatting', () => {
|
||||||
|
const input = `This is an *italic* test`;
|
||||||
|
|
||||||
|
const expectedOutput = `<p>This is an <em>italic</em> test</p>`;
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('markdownToHTML - Mixed formatting', () => {
|
||||||
|
const input = `*Italic* and **bold** formatting`;
|
||||||
|
|
||||||
|
const expectedOutput = `<p><em>Italic</em> and <strong>bold</strong> formatting</p>`;
|
||||||
|
const output = markdownToHTML(input);
|
||||||
|
expect(output).toEqual(expectedOutput);
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user