mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 22:39:26 +02:00
Compare commits
151 Commits
@mermaid-j
...
4743-timel
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d2c5cbd408 | ||
![]() |
20467bcbe6 | ||
![]() |
fad6676d18 | ||
![]() |
637680d4d9 | ||
![]() |
5f6f5110fd | ||
![]() |
af47269342 | ||
![]() |
d3c0893937 | ||
![]() |
75ef9bc681 | ||
![]() |
45edc91591 | ||
![]() |
f4edd19371 | ||
![]() |
b611a13e04 | ||
![]() |
7a38eb715d | ||
![]() |
2715ddb338 | ||
![]() |
ca2eca58c9 | ||
![]() |
b4fae2d096 | ||
![]() |
6dd9af0dd4 | ||
![]() |
3151241559 | ||
![]() |
dda9c9b46e | ||
![]() |
cdd1a70b67 | ||
![]() |
d8bd4dea93 | ||
![]() |
6deb476182 | ||
![]() |
ed297ee235 | ||
![]() |
03c1201fcb | ||
![]() |
3ca317c5a0 | ||
![]() |
4d83263388 | ||
![]() |
254e5cbd51 | ||
![]() |
a58dd3c6ce | ||
![]() |
10b7bb568f | ||
![]() |
1aa2870224 | ||
![]() |
8fbcbb6dc9 | ||
![]() |
3e545d7925 | ||
![]() |
75d2a259ed | ||
![]() |
746280b3e2 | ||
![]() |
fa4e30bb15 | ||
![]() |
950b107dd4 | ||
![]() |
35b84761a9 | ||
![]() |
0da2922ee7 | ||
![]() |
85eba01663 | ||
![]() |
79ba50216a | ||
![]() |
9655d07adf | ||
![]() |
2b3cb6a362 | ||
![]() |
89ac2932c4 | ||
![]() |
e828609749 | ||
![]() |
91f141f772 | ||
![]() |
c77b968f1e | ||
![]() |
ba13981905 | ||
![]() |
a7a94b95e1 | ||
![]() |
31f141c61a | ||
![]() |
fb017bebfd | ||
![]() |
648698a43a | ||
![]() |
a5e4729c76 | ||
![]() |
c884def5fc | ||
![]() |
592c5bb880 | ||
![]() |
da0c6c6c32 | ||
![]() |
dd213fe86c | ||
![]() |
d9396eedd6 | ||
![]() |
5f9601b6a8 | ||
![]() |
378f8ece0c | ||
![]() |
148a42a31a | ||
![]() |
8980ca4526 | ||
![]() |
caa04aad8b | ||
![]() |
3dc06ea9bd | ||
![]() |
c311c1ba5d | ||
![]() |
11a86d9c06 | ||
![]() |
037bec189c | ||
![]() |
9b164dd185 | ||
![]() |
4ee124bf91 | ||
![]() |
800f23fc01 | ||
![]() |
e7970c66ee | ||
![]() |
c7b96be683 | ||
![]() |
0623a87d70 | ||
![]() |
c5f89eaa9a | ||
![]() |
1fbe550285 | ||
![]() |
b3724f04c4 | ||
![]() |
2f58a0e188 | ||
![]() |
6f1e0e4d17 | ||
![]() |
7dd8ddc3eb | ||
![]() |
222d7170f7 | ||
![]() |
3137cff4a5 | ||
![]() |
b45b1d7795 | ||
![]() |
e1030b186e | ||
![]() |
334c8c2962 | ||
![]() |
bdaa3f693a | ||
![]() |
be13fb0391 | ||
![]() |
c120901744 | ||
![]() |
67aa1a4dc1 | ||
![]() |
c1bcdcfbad | ||
![]() |
f528e2daa4 | ||
![]() |
a867842f32 | ||
![]() |
badb019d38 | ||
![]() |
d85a393bcd | ||
![]() |
dc3ed65c18 | ||
![]() |
43092e6e11 | ||
![]() |
878e77acab | ||
![]() |
63827db60d | ||
![]() |
41108358f6 | ||
![]() |
f970fc8bea | ||
![]() |
db461a4c6c | ||
![]() |
fe33d5fb53 | ||
![]() |
64c4d4197b | ||
![]() |
ed7bab76f2 | ||
![]() |
f338802642 | ||
![]() |
445595f942 | ||
![]() |
66ce617bea | ||
![]() |
3e07a5acff | ||
![]() |
df1e739194 | ||
![]() |
2def5a0768 | ||
![]() |
2746bccef3 | ||
![]() |
f0c3dfe3b3 | ||
![]() |
680d65114c | ||
![]() |
4f8f929340 | ||
![]() |
3629e8e480 | ||
![]() |
ff48c2e1da | ||
![]() |
1bd13b50f1 | ||
![]() |
e0a075ecca | ||
![]() |
40eb0cc240 | ||
![]() |
954f6cc8fc | ||
![]() |
19884294bc | ||
![]() |
e6b63fd70a | ||
![]() |
8a84ede164 | ||
![]() |
d79b7b2d97 | ||
![]() |
844f879f63 | ||
![]() |
cf789d2c91 | ||
![]() |
4936ef5c30 | ||
![]() |
f006718e56 | ||
![]() |
d720776918 | ||
![]() |
f43398dd44 | ||
![]() |
43ad451940 | ||
![]() |
94c099caa1 | ||
![]() |
3e6f680df2 | ||
![]() |
2e5d955e77 | ||
![]() |
7733faf6c4 | ||
![]() |
b053a88993 | ||
![]() |
7c77c46ede | ||
![]() |
da6937f474 | ||
![]() |
cbb496da79 | ||
![]() |
43e66a6089 | ||
![]() |
5acbd7e762 | ||
![]() |
febae345fc | ||
![]() |
28bdbbca1a | ||
![]() |
4b896fa22e | ||
![]() |
a25ee49edd | ||
![]() |
695b5b2fb2 | ||
![]() |
9b77af540b | ||
![]() |
7829138fb2 | ||
![]() |
7e7a4fc665 | ||
![]() |
34e6112fea | ||
![]() |
0c759d0075 | ||
![]() |
5b7c1aad9e | ||
![]() |
865c453547 | ||
![]() |
c0b14021b7 |
@@ -33,4 +33,9 @@ export const packageOptions = {
|
||||
packageName: 'mermaid-layout-elk',
|
||||
file: 'layouts.ts',
|
||||
},
|
||||
examples: {
|
||||
name: 'mermaid-examples',
|
||||
packageName: 'examples',
|
||||
file: 'index.ts',
|
||||
},
|
||||
} as const satisfies Record<string, PackageOptions>;
|
||||
|
@@ -10,13 +10,16 @@ const buildType = (packageName: string) => {
|
||||
console.log(out.toString());
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e.stdout.length > 0) {
|
||||
console.error(e.stdout.toString());
|
||||
}
|
||||
if (e.stderr.length > 0) {
|
||||
console.error(e.stderr.toString());
|
||||
}
|
||||
// Exit the build process if we are in CI
|
||||
if (process.env.CI) {
|
||||
throw new Error(`Failed to build types for ${packageName}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
5
.changeset/gold-olives-rule.md
Normal file
5
.changeset/gold-olives-rule.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': minor
|
||||
---
|
||||
|
||||
feat: Add `getRegisteredDiagramsMetadata` to `mermaid`, which returns all the registered diagram IDs in mermaid
|
5
.changeset/ninety-adults-wink.md
Normal file
5
.changeset/ninety-adults-wink.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'mermaid': major
|
||||
---
|
||||
|
||||
Currently, HTML tags such as <em>, <strong>, <sup>, <a>, <ul>, and <li> are supported in Flowchart and Class diagram labels but not in Timeline diagrams. This change introduces support for basic HTML formatting in Timeline labels, enabling richer text formatting and better usability for multi-line content like descriptions, footnotes, and styled annotations
|
5
.changeset/platinum-olives-rule.md
Normal file
5
.changeset/platinum-olives-rule.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@mermaid-js/examples': minor
|
||||
---
|
||||
|
||||
feat: Add examples for diagrams in the `@mermaid-js/examples` package
|
7
.changeset/slimy-peaches-win.md
Normal file
7
.changeset/slimy-peaches-win.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@mermaid-js/examples': patch
|
||||
'mermaid': patch
|
||||
'@mermaid-js/parser': patch
|
||||
---
|
||||
|
||||
chore: Move packet diagram out of beta
|
7
.changeset/wild-areas-lick.md
Normal file
7
.changeset/wild-areas-lick.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'mermaid': patch
|
||||
---
|
||||
|
||||
fix(timeline): fix loading `leftMargin` from config
|
||||
|
||||
The `timeline.leftMargin` config value should now correctly control the size of the left margin, instead of being ignored.
|
@@ -88,6 +88,7 @@ NODIR
|
||||
NSTR
|
||||
outdir
|
||||
Qcontrolx
|
||||
QSTR
|
||||
reinit
|
||||
rels
|
||||
reqs
|
||||
|
3
.github/lychee.toml
vendored
3
.github/lychee.toml
vendored
@@ -46,6 +46,9 @@ exclude = [
|
||||
# Drupal 403
|
||||
"https://(www.)?drupal.org",
|
||||
|
||||
# Phbpp 403
|
||||
"https://(www.)?phpbb.com",
|
||||
|
||||
# Swimm returns 404, even though the link is valid
|
||||
"https://docs.swimm.io",
|
||||
|
||||
|
26
.github/workflows/pr-labeler.yml
vendored
26
.github/workflows/pr-labeler.yml
vendored
@@ -29,3 +29,29 @@ jobs:
|
||||
disable-releaser: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add "Sponsored by MermaidChart" label
|
||||
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { data: commits } = await github.rest.pulls.listCommits({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
const isSponsored = commits.every(
|
||||
(c) => c.commit.author.email?.endsWith('@mermaidchart.com')
|
||||
);
|
||||
|
||||
if (isSponsored) {
|
||||
console.log('PR is sponsored. Adding label.');
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
labels: ['Sponsored by MermaidChart'],
|
||||
});
|
||||
}
|
||||
|
70
.github/workflows/validate-lockfile.yml
vendored
Normal file
70
.github/workflows/validate-lockfile.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Validate pnpm-lock.yaml
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'pnpm-lock.yaml'
|
||||
- '**/package.json'
|
||||
- '.github/workflows/validate-lockfile.yml'
|
||||
|
||||
jobs:
|
||||
validate-lockfile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||
|
||||
- name: Validate pnpm-lock.yaml entries
|
||||
id: validate # give this step an ID so we can reference its outputs
|
||||
run: |
|
||||
issues=()
|
||||
|
||||
# 1) No tarball references
|
||||
if grep -qF 'tarball:' pnpm-lock.yaml; then
|
||||
issues+=("• Tarball references found (forbidden)")
|
||||
fi
|
||||
|
||||
# 2) No unwanted vitepress paths
|
||||
if grep -qF 'packages/mermaid/src/vitepress' pnpm-lock.yaml; then
|
||||
issues+=("• Disallowed path 'packages/mermaid/src/vitepress' present. Run `rm -rf packages/mermaid/src/vitepress && pnpm install` to regenerate.")
|
||||
fi
|
||||
|
||||
# 3) Lockfile only changes when package.json changes
|
||||
git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }} > changed.txt
|
||||
if grep -q '^pnpm-lock.yaml$' changed.txt && ! grep -q 'package.json' changed.txt; then
|
||||
issues+=("• pnpm-lock.yaml changed without any package.json modification")
|
||||
fi
|
||||
|
||||
# If any issues, output them and fail
|
||||
if [ ${#issues[@]} -gt 0 ]; then
|
||||
# Use the new GITHUB_OUTPUT approach to set a multiline output
|
||||
{
|
||||
echo "errors<<EOF"
|
||||
printf '%s\n' "${issues[@]}"
|
||||
echo "EOF"
|
||||
} >> $GITHUB_OUTPUT
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Comment on PR if validation failed
|
||||
if: failure()
|
||||
uses: peter-evans/create-or-update-comment@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
The following issue(s) were detected:
|
||||
${{ steps.validate.outputs.errors }}
|
||||
|
||||
Please address these and push an update.
|
||||
|
||||
_Posted automatically by GitHub Actions_
|
@@ -95,10 +95,6 @@ In our release process we rely heavily on visual regression tests using [applito
|
||||
|
||||
<!-- </Main description> -->
|
||||
|
||||
## Mermaid AI Bot
|
||||
|
||||
[Mermaid](https://codeparrot.ai/oracle?owner=mermaid-js&repo=mermaid) Bot will help you understand this repository better. You can ask for code examples, installation guide, debugging help and much more.
|
||||
|
||||
## Examples
|
||||
|
||||
**The following are some examples of the diagrams, charts and graphs that can be made using Mermaid. Click here to jump into the [text syntax](https://mermaid.js.org/intro/syntax-reference.html).**
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util';
|
||||
|
||||
describe('packet structure', () => {
|
||||
it('should render a simple packet diagram', () => {
|
||||
it('should render a simple packet-beta diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
title Hello world
|
||||
@@ -10,9 +10,18 @@ describe('packet structure', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a simple packet diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet
|
||||
title Hello world
|
||||
0-10: "hello"
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a simple packet diagram without ranges', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
`packet
|
||||
0: "h"
|
||||
1: "i"
|
||||
`
|
||||
@@ -21,7 +30,7 @@ describe('packet structure', () => {
|
||||
|
||||
it('should render a complex packet diagram', () => {
|
||||
imgSnapshotTest(
|
||||
`packet-beta
|
||||
`packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -52,7 +61,7 @@ describe('packet structure', () => {
|
||||
packet:
|
||||
showBits: false
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
|
@@ -225,4 +225,18 @@ timeline
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('13: should render markdown htmlLabels', () => {
|
||||
const timelineCode = `%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
|
||||
timeline
|
||||
title Timeline of Industrial Revolution
|
||||
section 17th-20th century
|
||||
Industry 1.0 : Machinery, Water power, Steam <br>power
|
||||
Industry 2.0 : Electricity, <strong>Internal combustion engine </strong>, Mass production
|
||||
Industry 3.0 : Electronics, Computers, Automation
|
||||
section 21st century
|
||||
Industry 4.0 : Internet, Robotics, Internet of Things
|
||||
Industry 5.0 : Artificial intelligence, Big data, 3D printing
|
||||
`;
|
||||
imgSnapshotTest(timelineCode);
|
||||
});
|
||||
});
|
||||
|
382
cypress/integration/rendering/treemap.spec.ts
Normal file
382
cypress/integration/rendering/treemap.spec.ts
Normal file
@@ -0,0 +1,382 @@
|
||||
import { imgSnapshotTest } from '../../helpers/util.ts';
|
||||
|
||||
describe('Treemap Diagram', () => {
|
||||
it('1: should render a basic treemap', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('2: should render a hierarchical treemap', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Products"
|
||||
"Electronics"
|
||||
"Phones": 50
|
||||
"Computers": 30
|
||||
"Accessories": 20
|
||||
"Clothing"
|
||||
"Men's"
|
||||
"Shirts": 10
|
||||
"Pants": 15
|
||||
"Women's"
|
||||
"Dresses": 20
|
||||
"Skirts": 10
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('3: should render a treemap with styling using classDef', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('4: should handle long text that wraps', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Main Category"
|
||||
"This is a very long item name that should wrap to the next line when rendered in the treemap diagram": 50
|
||||
"Short item": 20
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('5: should render with a forest theme', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('6: should handle multiple levels of nesting', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Level 1"
|
||||
"Level 2A"
|
||||
"Level 3A": 10
|
||||
"Level 3B": 15
|
||||
"Level 2B"
|
||||
"Level 3C": 20
|
||||
"Level 3D"
|
||||
"Level 4A": 5
|
||||
"Level 4B": 5
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('7: should handle classDef with multiple styles', () => {
|
||||
imgSnapshotTest(
|
||||
`treemap-beta
|
||||
"Main"
|
||||
"A": 20
|
||||
"B":::important
|
||||
"B1": 10
|
||||
"B2": 15
|
||||
"C": 5:::secondary
|
||||
|
||||
classDef important fill:#f96,stroke:#333,stroke-width:2px;
|
||||
classDef secondary fill:#6cf,stroke:#333,stroke-dasharray:5 5;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8: should handle dollar value formatting with thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$0,0"
|
||||
---
|
||||
treemap
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 700000
|
||||
"Equipment": 200000
|
||||
"Supplies": 100000
|
||||
"Marketing"
|
||||
"Advertising": 400000
|
||||
"Events": 100000
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8a: should handle percentage formatting', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".1%"
|
||||
---
|
||||
treemap-beta
|
||||
"Market Share"
|
||||
"Company A": 0.35
|
||||
"Company B": 0.25
|
||||
"Company C": 0.15
|
||||
"Others": 0.25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8b: should handle decimal formatting', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Metrics"
|
||||
"Conversion Rate": 0.0567
|
||||
"Bounce Rate": 0.6723
|
||||
"Click-through Rate": 0.1289
|
||||
"Engagement": 0.4521
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8c: should handle dollar sign with decimal places', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$.2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Product Prices"
|
||||
"Basic": 19.99
|
||||
"Standard": 49.99
|
||||
"Premium": 99.99
|
||||
"Enterprise": 199.99
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8d: should handle dollar sign with thousands separator and decimal places', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$,.2f"
|
||||
---
|
||||
treemap-beta
|
||||
"Revenue"
|
||||
"Q1": 1250345.75
|
||||
"Q2": 1645789.25
|
||||
"Q3": 1845123.50
|
||||
"Q4": 2145678.75
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8e: should handle simple thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ","
|
||||
---
|
||||
treemap-beta
|
||||
"User Counts"
|
||||
"Active Users": 1250345
|
||||
"New Signups": 45789
|
||||
"Churned": 12350
|
||||
"Converted": 78975
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8f: should handle valueFormat set via directive with dollar and thousands separator', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: "$,.0f"
|
||||
---
|
||||
treemap-beta
|
||||
"Sales by Region"
|
||||
"North": 1234567
|
||||
"South": 7654321
|
||||
"East": 4567890
|
||||
"West": 9876543
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('8g: should handle scientific notation format', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: ".2e"
|
||||
---
|
||||
treemap-beta
|
||||
"Scientific Values"
|
||||
"Value 1": 1234567
|
||||
"Value 2": 0.0000123
|
||||
"Value 3": 1000000000
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('9: should handle a complex example with multiple features', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
config:
|
||||
theme: dark
|
||||
treemap:
|
||||
valueFormat: "$0,0"
|
||||
---
|
||||
treemap-beta
|
||||
"Company Budget"
|
||||
"Engineering":::engineering
|
||||
"Frontend": 300000
|
||||
"Backend": 400000
|
||||
"DevOps": 200000
|
||||
"Marketing":::marketing
|
||||
"Digital": 250000
|
||||
"Print": 100000
|
||||
"Events": 150000
|
||||
"Sales":::sales
|
||||
"Direct": 500000
|
||||
"Channel": 300000
|
||||
|
||||
classDef engineering fill:#6b9bc3,stroke:#333;
|
||||
classDef marketing fill:#c36b9b,stroke:#333;
|
||||
classDef sales fill:#c3a66b,stroke:#333;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('10: should render the example from documentation', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('11: should handle comments', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
%% This is a comment
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
%% Another comment
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
/*
|
||||
it.skip('12: should render a treemap with title', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
title Treemap with Title
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('13: should render a treemap with accessibility attributes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap-beta
|
||||
accTitle: Accessible Treemap Title
|
||||
accDescr: This is a description of the treemap for accessibility purposes
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('14: should render a treemap with title and accessibility attributes', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
treemap
|
||||
title Treemap with Title and Accessibility
|
||||
accTitle: Accessible Treemap Title
|
||||
accDescr: This is a description of the treemap for accessibility purposes
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
*/
|
||||
});
|
@@ -32,8 +32,26 @@
|
||||
href="https://fonts.googleapis.com/css2?family=Kalam:wght@300;400;700&family=Rubik+Mono+One&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Recursive:wght@300..1000&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<style>
|
||||
.recursive-mermaid {
|
||||
font-family: 'Recursive', sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
font-variation-settings:
|
||||
'slnt' 0,
|
||||
'CASL' 0,
|
||||
'CRSV' 0.5,
|
||||
'MONO' 0;
|
||||
}
|
||||
|
||||
body {
|
||||
/* background: rgb(221, 208, 208); */
|
||||
/* background: #333; */
|
||||
@@ -45,7 +63,9 @@
|
||||
h1 {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
border: 1px solid red;
|
||||
}
|
||||
.mermaid2 {
|
||||
display: none;
|
||||
}
|
||||
@@ -83,6 +103,11 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.class2 {
|
||||
fill: red;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
/* tspan {
|
||||
font-size: 6px !important;
|
||||
} */
|
||||
@@ -106,19 +131,63 @@
|
||||
|
||||
<body>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
treemap
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
|
||||
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$0,0'
|
||||
---
|
||||
treemap
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 7000
|
||||
"Equipment": 2000
|
||||
"Supplies": 1000
|
||||
"Marketing"
|
||||
"Advertising": 4000
|
||||
"Events": 1000
|
||||
|
||||
</pre
|
||||
>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
treemap
|
||||
title Accessible Treemap Title
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
AB["apa@apa@"] --> B(("`apa@apa`"))
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart
|
||||
D(("for D"))
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
A e1@==> B
|
||||
e1@{ animate: true}
|
||||
</pre>
|
||||
<pre id="diagram4" class="mermaid">
|
||||
<pre id="diagram4" class="mermaid2">
|
||||
flowchart LR
|
||||
A e1@--> B
|
||||
classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round
|
||||
@@ -438,7 +507,7 @@ kanban
|
||||
alert('It worked');
|
||||
}
|
||||
await mermaid.initialize({
|
||||
// theme: 'base',
|
||||
// theme: 'forest',
|
||||
// theme: 'default',
|
||||
// theme: 'forest',
|
||||
// handDrawnSeed: 12,
|
||||
@@ -449,11 +518,7 @@ kanban
|
||||
// layout: 'fixed',
|
||||
// htmlLabels: false,
|
||||
flowchart: { titleTopMargin: 10 },
|
||||
|
||||
// fontFamily: 'Caveat',
|
||||
// fontFamily: 'Kalam',
|
||||
// fontFamily: 'courier',
|
||||
fontFamily: 'arial',
|
||||
fontFamily: "'Recursive', sans-serif",
|
||||
sequence: {
|
||||
actorFontFamily: 'courier',
|
||||
noteFontFamily: 'courier',
|
||||
|
@@ -2,35 +2,35 @@
|
||||
"durations": [
|
||||
{
|
||||
"spec": "cypress/integration/other/configuration.spec.js",
|
||||
"duration": 6130
|
||||
"duration": 5672
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/external-diagrams.spec.js",
|
||||
"duration": 1974
|
||||
"duration": 1990
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/ghsa.spec.js",
|
||||
"duration": 3308
|
||||
"duration": 3186
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/iife.spec.js",
|
||||
"duration": 1877
|
||||
"duration": 1948
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/interaction.spec.js",
|
||||
"duration": 10902
|
||||
"duration": 11938
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/rerender.spec.js",
|
||||
"duration": 1836
|
||||
"duration": 1932
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/other/xss.spec.js",
|
||||
"duration": 26467
|
||||
"duration": 27237
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/appli.spec.js",
|
||||
"duration": 3129
|
||||
"duration": 3170
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/architecture.spec.ts",
|
||||
@@ -38,147 +38,147 @@
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/block.spec.js",
|
||||
"duration": 16230
|
||||
"duration": 17390
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/c4.spec.js",
|
||||
"duration": 5231
|
||||
"duration": 5296
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-elk-v3.spec.js",
|
||||
"duration": 38113
|
||||
"duration": 39004
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-handDrawn-v3.spec.js",
|
||||
"duration": 36423
|
||||
"duration": 37653
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v2.spec.js",
|
||||
"duration": 22509
|
||||
"duration": 23278
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram-v3.spec.js",
|
||||
"duration": 34933
|
||||
"duration": 36645
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/classDiagram.spec.js",
|
||||
"duration": 14681
|
||||
"duration": 15418
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/conf-and-directives.spec.js",
|
||||
"duration": 8877
|
||||
"duration": 9684
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/current.spec.js",
|
||||
"duration": 2517
|
||||
"duration": 2570
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram-unified.spec.js",
|
||||
"duration": 81226
|
||||
"duration": 84687
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/erDiagram.spec.js",
|
||||
"duration": 14211
|
||||
"duration": 14819
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/errorDiagram.spec.js",
|
||||
"duration": 3355
|
||||
"duration": 3371
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-elk.spec.js",
|
||||
"duration": 38857
|
||||
"duration": 39925
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-handDrawn.spec.js",
|
||||
"duration": 28570
|
||||
"duration": 34694
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-icon.spec.js",
|
||||
"duration": 6902
|
||||
"duration": 7137
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-shape-alias.spec.ts",
|
||||
"duration": 23075
|
||||
"duration": 24740
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart-v2.spec.js",
|
||||
"duration": 40514
|
||||
"duration": 42077
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/flowchart.spec.js",
|
||||
"duration": 28611
|
||||
"duration": 30642
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gantt.spec.js",
|
||||
"duration": 16605
|
||||
"duration": 18085
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/gitGraph.spec.js",
|
||||
"duration": 47636
|
||||
"duration": 50107
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/iconShape.spec.ts",
|
||||
"duration": 262219
|
||||
"duration": 276279
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/imageShape.spec.ts",
|
||||
"duration": 54111
|
||||
"duration": 56505
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/info.spec.ts",
|
||||
"duration": 3006
|
||||
"duration": 3036
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/journey.spec.js",
|
||||
"duration": 6858
|
||||
"duration": 6889
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/kanban.spec.ts",
|
||||
"duration": 7281
|
||||
"duration": 7353
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/katex.spec.js",
|
||||
"duration": 3579
|
||||
"duration": 3580
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/marker_unique_id.spec.js",
|
||||
"duration": 2448
|
||||
"duration": 2508
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/mindmap.spec.ts",
|
||||
"duration": 10618
|
||||
"duration": 10939
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/newShapes.spec.ts",
|
||||
"duration": 140874
|
||||
"duration": 149102
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/oldShapes.spec.ts",
|
||||
"duration": 108015
|
||||
"duration": 113987
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/packet.spec.ts",
|
||||
"duration": 4241
|
||||
"duration": 4060
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/pie.spec.ts",
|
||||
"duration": 5645
|
||||
"duration": 5715
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/quadrantChart.spec.js",
|
||||
"duration": 8524
|
||||
"duration": 8945
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/radar.spec.js",
|
||||
"duration": 5203
|
||||
"duration": 5337
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirement.spec.js",
|
||||
"duration": 2635
|
||||
"duration": 2643
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/requirementDiagram-unified.spec.js",
|
||||
"duration": 50512
|
||||
"duration": 52072
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sankey.spec.ts",
|
||||
@@ -186,31 +186,35 @@
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/sequencediagram.spec.js",
|
||||
"duration": 34559
|
||||
"duration": 35721
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram-v2.spec.js",
|
||||
"duration": 24421
|
||||
"duration": 26030
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/stateDiagram.spec.js",
|
||||
"duration": 15316
|
||||
"duration": 16333
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/theme.spec.js",
|
||||
"duration": 28240
|
||||
"duration": 29287
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/timeline.spec.ts",
|
||||
"duration": 6808
|
||||
"duration": 8491
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/treemap.spec.ts",
|
||||
"duration": 12291
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/xyChart.spec.js",
|
||||
"duration": 19359
|
||||
"duration": 20651
|
||||
},
|
||||
{
|
||||
"spec": "cypress/integration/rendering/zenuml.spec.js",
|
||||
"duration": 3164
|
||||
"duration": 3218
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/font-awesome.min.css"
|
||||
/>
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
|
||||
|
@@ -17,7 +17,7 @@
|
||||
|
||||
<div class="diagrams">
|
||||
<pre class="mermaid">
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -44,7 +44,7 @@
|
||||
packet:
|
||||
showBits: false
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -70,7 +70,7 @@
|
||||
config:
|
||||
theme: forest
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
title Forest theme
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
@@ -97,7 +97,7 @@
|
||||
config:
|
||||
theme: dark
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
title Dark theme
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
|
75
demos/treemap.html
Normal file
75
demos/treemap.html
Normal file
@@ -0,0 +1,75 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Mermaid Treemap Diagram Demo</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
margin: 0 auto;
|
||||
max-width: 900px;
|
||||
padding: 20px;
|
||||
}
|
||||
.mermaid {
|
||||
margin: 30px 0;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
color: #333;
|
||||
}
|
||||
pre {
|
||||
background-color: #f5f5f5;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Treemap Diagram Demo</h1>
|
||||
<p>This is a demo of the new treemap diagram type in Mermaid.</p>
|
||||
|
||||
<h2>Basic Treemap Example</h2>
|
||||
<pre class="mermaid">
|
||||
treemap
|
||||
"Root"
|
||||
"Branch 1"
|
||||
"Leaf 1.1": 10
|
||||
"Leaf 1.2": 15
|
||||
"Branch 2"
|
||||
"Branch 2.1"
|
||||
"Leaf 2.1.1": 20
|
||||
"Leaf 2.1.2": 25
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 30
|
||||
</pre>
|
||||
|
||||
<h2>Technology Stack Treemap Example</h2>
|
||||
<pre class="mermaid">
|
||||
treemap
|
||||
"Technology Stack"
|
||||
"Frontend"
|
||||
"React": 35
|
||||
"CSS": 15
|
||||
"HTML": 10
|
||||
"Backend"
|
||||
"Node.js": 25
|
||||
"Express": 10
|
||||
"MongoDB": 15
|
||||
"DevOps"
|
||||
"Docker": 10
|
||||
"Kubernetes": 15
|
||||
"CI/CD": 5
|
||||
</pre>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
mermaid.initialize({
|
||||
theme: 'forest',
|
||||
logLevel: 1,
|
||||
securityLevel: 'loose',
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -111,3 +111,13 @@ const themes = {
|
||||
```
|
||||
|
||||
The actual options and values for the colors are defined in **src/theme/theme-\[xyz].js**. If you provide the options your diagram needs in the existing theme files then the theming will work smoothly without hiccups.
|
||||
|
||||
## Examples
|
||||
|
||||
The `@mermaid-js/examples` package contains a collection of examples that are used by tools like mermaid.live to help users get started with the new diagram.
|
||||
|
||||
You can duplicate an existing diagram example file, eg: `packages/examples/src/examples/flowchart.ts`, and modify it with details specific to your diagram.
|
||||
|
||||
Then you can import the example in the `packages/examples/src/index.ts` file and add it to the `examples` array.
|
||||
|
||||
Each diagram should have at least one example, and that should be marked as default. It is good to add more examples to showcase different features of the diagram.
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> `const` **configKeys**: `Set`<`string`>
|
||||
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:278](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L278)
|
||||
Defined in: [packages/mermaid/src/defaultConfig.ts:290](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L290)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: ExternalDiagramDefinition
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:99](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L99)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:94](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L94)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:99](https://github.com/me
|
||||
|
||||
> **detector**: `DiagramDetector`
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L101)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L96)
|
||||
|
||||
---
|
||||
|
||||
@@ -26,7 +26,7 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:101](https://github.com/m
|
||||
|
||||
> **id**: `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L100)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:95](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L95)
|
||||
|
||||
---
|
||||
|
||||
@@ -34,4 +34,4 @@ Defined in: [packages/mermaid/src/diagram-api/types.ts:100](https://github.com/m
|
||||
|
||||
> **loader**: `DiagramLoader`
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L102)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L97)
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
# Interface: Mermaid
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L418)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:429](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L429)
|
||||
|
||||
## Properties
|
||||
|
||||
@@ -18,7 +18,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:418](https://github.com/mermaid-js/
|
||||
|
||||
> **contentLoaded**: () => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:447](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L447)
|
||||
|
||||
\##contentLoaded Callback function that is called when page is loaded. This functions fetches
|
||||
configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the
|
||||
@@ -34,7 +34,7 @@ page.
|
||||
|
||||
> **detectType**: (`text`, `config`?) => `string`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:449](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L449)
|
||||
|
||||
Detects the type of the graph text.
|
||||
|
||||
@@ -86,11 +86,28 @@ A graph definition key
|
||||
|
||||
---
|
||||
|
||||
### getRegisteredDiagramsMetadata()
|
||||
|
||||
> **getRegisteredDiagramsMetadata**: () => `Pick`<[`ExternalDiagramDefinition`](ExternalDiagramDefinition.md), `"id"`>\[]
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:451](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L451)
|
||||
|
||||
Gets the metadata for all registered diagrams.
|
||||
Currently only the id is returned.
|
||||
|
||||
#### Returns
|
||||
|
||||
`Pick`<[`ExternalDiagramDefinition`](ExternalDiagramDefinition.md), `"id"`>\[]
|
||||
|
||||
An array of objects with the id of the diagram.
|
||||
|
||||
---
|
||||
|
||||
### ~~init()~~
|
||||
|
||||
> **init**: (`config`?, `nodes`?, `callback`?) => `Promise`<`void`>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)
|
||||
|
||||
## init
|
||||
|
||||
@@ -138,7 +155,7 @@ Use [initialize](Mermaid.md#initialize) and [run](Mermaid.md#run) instead.
|
||||
|
||||
> **initialize**: (`config`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:435](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L435)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:446](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L446)
|
||||
|
||||
Used to set configurations for mermaid.
|
||||
This function should be called before the run function.
|
||||
@@ -161,7 +178,7 @@ Configuration object for mermaid.
|
||||
|
||||
> **mermaidAPI**: `Readonly`<{ `defaultConfig`: [`MermaidConfig`](MermaidConfig.md); `getConfig`: () => [`MermaidConfig`](MermaidConfig.md); `getDiagramFromText`: (`text`, `metadata`) => `Promise`<`Diagram`>; `getSiteConfig`: () => [`MermaidConfig`](MermaidConfig.md); `globalReset`: () => `void`; `initialize`: (`userOptions`) => `void`; `parse`: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)>; `render`: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)>; `reset`: () => `void`; `setConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); `updateSiteConfig`: (`conf`) => [`MermaidConfig`](MermaidConfig.md); }>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:425](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L425)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:436](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L436)
|
||||
|
||||
**`Internal`**
|
||||
|
||||
@@ -175,7 +192,7 @@ Use [parse](Mermaid.md#parse) and [render](Mermaid.md#render) instead. Please [o
|
||||
|
||||
> **parse**: (`text`, `parseOptions`) => `Promise`<`false` | [`ParseResult`](ParseResult.md)>(`text`, `parseOptions`?) => `Promise`<[`ParseResult`](ParseResult.md)>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:426](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L426)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
|
||||
|
||||
Parse the text and validate the syntax.
|
||||
|
||||
@@ -243,7 +260,7 @@ Error if the diagram is invalid and parseOptions.suppressErrors is false or not
|
||||
|
||||
> `optional` **parseError**: [`ParseErrorFunction`](../type-aliases/ParseErrorFunction.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L420)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:431](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L431)
|
||||
|
||||
---
|
||||
|
||||
@@ -251,7 +268,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:420](https://github.com/mermaid-js/
|
||||
|
||||
> **registerExternalDiagrams**: (`diagrams`, `opts`) => `Promise`<`void`>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:434](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L434)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:445](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L445)
|
||||
|
||||
Used to register external diagram types.
|
||||
|
||||
@@ -281,7 +298,7 @@ If opts.lazyLoad is false, the diagrams will be loaded immediately.
|
||||
|
||||
> **registerIconPacks**: (`iconLoaders`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L439)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:450](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L450)
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -299,7 +316,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:439](https://github.com/mermaid-js/
|
||||
|
||||
> **registerLayoutLoaders**: (`loaders`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L433)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:444](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L444)
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -317,7 +334,7 @@ Defined in: [packages/mermaid/src/mermaid.ts:433](https://github.com/mermaid-js/
|
||||
|
||||
> **render**: (`id`, `text`, `svgContainingElement`?) => `Promise`<[`RenderResult`](RenderResult.md)>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:427](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L427)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:438](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L438)
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -349,7 +366,7 @@ Deprecated for external use.
|
||||
|
||||
> **run**: (`options`) => `Promise`<`void`>
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:432](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L432)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:443](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L443)
|
||||
|
||||
## run
|
||||
|
||||
@@ -393,7 +410,7 @@ Optional runtime configs
|
||||
|
||||
> **setParseErrorHandler**: (`parseErrorHandler`) => `void`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:437](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L437)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:448](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L448)
|
||||
|
||||
## setParseErrorHandler Alternative to directly setting parseError using:
|
||||
|
||||
@@ -424,4 +441,4 @@ New parseError() callback.
|
||||
|
||||
> **startOnLoad**: `boolean`
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:419](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L419)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:430](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L430)
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> **SVG**: `d3.Selection`<`SVGSVGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:130](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L130)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:126](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L126)
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> **SVGGroup**: `d3.Selection`<`SVGGElement`, `unknown`, `Element` | `null`, `unknown`>
|
||||
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:132](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L132)
|
||||
Defined in: [packages/mermaid/src/diagram-api/types.ts:128](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/diagram-api/types.ts#L128)
|
||||
|
@@ -12,4 +12,4 @@
|
||||
|
||||
> `const` **default**: [`Mermaid`](../interfaces/Mermaid.md)
|
||||
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:442](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L442)
|
||||
Defined in: [packages/mermaid/src/mermaid.ts:454](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaid.ts#L454)
|
||||
|
@@ -245,7 +245,7 @@ Communication tools and platforms
|
||||
| GitHub + Mermaid | - | [🦊🔗](https://addons.mozilla.org/firefox/addon/github-mermaid/) | - | - | [🐙🔗](https://github.com/BackMarket/github-mermaid-extension) |
|
||||
| Asciidoctor Live Preview | [🎡🔗](https://chromewebstore.google.com/detail/asciidoctorjs-live-previe/iaalpfgpbocpdfblpnhhgllgbdbchmia) | - | - | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/asciidoctorjs-live-previ/pefkelkanablhjdekgdahplkccnbdggd?hl=en-US) | - |
|
||||
| Diagram Tab | - | - | - | - | [🐙🔗](https://github.com/khafast/diagramtab) |
|
||||
| Markdown Diagrams | [🎡🔗](https://chromewebstore.google.com/detail/markdown-diagrams/pmoglnmodacnbbofbgcagndelmgaclel) | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-diagrams/) | [🔴🔗](https://addons.opera.com/en/extensions/details/markdown-diagrams/) | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/markdown-diagrams/hceenoomhhdkjjijnmlclkpenkapfihe) | [🐙🔗](https://github.com/marcozaccari/markdown-diagrams-browser-extension/tree/master/doc/examples) |
|
||||
| Markdown Diagrams | [🎡🔗](https://chromewebstore.google.com/detail/markdown-diagrams/pmoglnmodacnbbofbgcagndelmgaclel) | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-diagrams/) | - | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/markdown-diagrams/hceenoomhhdkjjijnmlclkpenkapfihe) | [🐙🔗](https://github.com/marcozaccari/markdown-diagrams-browser-extension/tree/master/doc/examples) |
|
||||
| Markdown Viewer | - | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-viewer-chrome/) | - | - | [🐙🔗](https://github.com/simov/markdown-viewer) |
|
||||
| Extensions for Mermaid | - | - | [🔴🔗](https://addons.opera.com/en/extensions/details/extensions-for-mermaid/) | - | [🐙🔗](https://github.com/Stefan-S/mermaid-extension) |
|
||||
| Chrome Diagrammer | [🎡🔗](https://chromewebstore.google.com/detail/chrome-diagrammer/bkpbgjmkomfoakfklcjeoegkklgjnnpk) | - | - | - | - |
|
||||
@@ -270,5 +270,6 @@ Communication tools and platforms
|
||||
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
|
||||
- [Reveal CK](https://github.com/jedcn/reveal-ck)
|
||||
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
|
||||
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
|
||||
|
||||
<!--- cspell:ignore Blazorade HueHive --->
|
||||
|
@@ -30,7 +30,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
||||
|
||||
Official Mermaid Chart plugins:
|
||||
|
||||
- [Mermaid Chart GPT](https://chat.openai.com/g/g-1IRFKwq4G-mermaid-chart)
|
||||
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
|
||||
- [Confluence](https://marketplace.atlassian.com/apps/1234056/mermaid-chart-for-confluence?hosting=cloud&tab=overview)
|
||||
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
|
||||
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
|
||||
|
@@ -17,7 +17,7 @@ This diagram type is particularly useful for developers, network engineers, educ
|
||||
## Syntax
|
||||
|
||||
```
|
||||
packet-beta
|
||||
packet
|
||||
start: "Block name" %% Single-bit block
|
||||
start-end: "Block name" %% Multi-bit blocks
|
||||
... More Fields ...
|
||||
@@ -28,7 +28,7 @@ start-end: "Block name" %% Multi-bit blocks
|
||||
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
|
||||
|
||||
```
|
||||
packet-beta
|
||||
packet
|
||||
+1: "Block name" %% Single-bit block
|
||||
+8: "Block name" %% 8-bit block
|
||||
9-15: "Manually set start and end, it's fine to mix and match"
|
||||
@@ -41,7 +41,7 @@ packet-beta
|
||||
---
|
||||
title: "TCP Packet"
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -65,7 +65,7 @@ packet-beta
|
||||
---
|
||||
title: "TCP Packet"
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -86,7 +86,7 @@ packet-beta
|
||||
```
|
||||
|
||||
```mermaid-example
|
||||
packet-beta
|
||||
packet
|
||||
title UDP Packet
|
||||
+16: "Source Port"
|
||||
+16: "Destination Port"
|
||||
@@ -96,7 +96,7 @@ title UDP Packet
|
||||
```
|
||||
|
||||
```mermaid
|
||||
packet-beta
|
||||
packet
|
||||
title UDP Packet
|
||||
+16: "Source Port"
|
||||
+16: "Destination Port"
|
||||
@@ -144,7 +144,7 @@ config:
|
||||
packet:
|
||||
startByteColor: red
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
|
353
docs/syntax/treemap.md
Normal file
353
docs/syntax/treemap.md
Normal file
@@ -0,0 +1,353 @@
|
||||
> **Warning**
|
||||
>
|
||||
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
|
||||
>
|
||||
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/treemap.md](../../packages/mermaid/src/docs/syntax/treemap.md).
|
||||
|
||||
# Treemap Diagram
|
||||
|
||||
> A treemap diagram displays hierarchical data as a set of nested rectangles. Each branch of the tree is represented by a rectangle, which is then tiled with smaller rectangles representing sub-branches.
|
||||
|
||||
> **Warning**
|
||||
> This is a new diagram type in Mermaid. Its syntax may evolve in future versions.
|
||||
|
||||
## Introduction
|
||||
|
||||
Treemap diagrams are an effective way to visualize hierarchical data and show proportions between categories and subcategories. The size of each rectangle is proportional to the value it represents, making it easy to compare different parts of a hierarchy.
|
||||
|
||||
Treemap diagrams are particularly useful for:
|
||||
|
||||
- Visualizing hierarchical data structures
|
||||
- Comparing proportions between categories
|
||||
- Displaying large amounts of hierarchical data in a limited space
|
||||
- Identifying patterns and outliers in hierarchical data
|
||||
|
||||
## Syntax
|
||||
|
||||
```
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2"
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20
|
||||
"Leaf 2.2": 25
|
||||
```
|
||||
|
||||
### Node Definition
|
||||
|
||||
Nodes in a treemap are defined using the following syntax:
|
||||
|
||||
- **Section/Parent nodes**: Defined with quoted text `"Section Name"`
|
||||
- **Leaf nodes with values**: Defined with quoted text followed by a colon and value `"Leaf Name": value`
|
||||
- **Hierarchy**: Created using indentation (spaces or tabs)
|
||||
- **Styling**: Nodes can be styled using the `:::class` syntax
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Treemap
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
```mermaid
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
### Hierarchical Treemap
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Products"
|
||||
"Electronics"
|
||||
"Phones": 50
|
||||
"Computers": 30
|
||||
"Accessories": 20
|
||||
"Clothing"
|
||||
"Men's": 40
|
||||
"Women's": 40
|
||||
```
|
||||
|
||||
```mermaid
|
||||
treemap-beta
|
||||
"Products"
|
||||
"Electronics"
|
||||
"Phones": 50
|
||||
"Computers": 30
|
||||
"Accessories": 20
|
||||
"Clothing"
|
||||
"Men's": 40
|
||||
"Women's": 40
|
||||
```
|
||||
|
||||
### Treemap with Styling
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
```
|
||||
|
||||
```mermaid
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
```
|
||||
|
||||
## Styling and Configuration
|
||||
|
||||
Treemap diagrams can be customized using Mermaid's styling and configuration options.
|
||||
|
||||
### Using classDef for Styling
|
||||
|
||||
You can define custom styles for nodes using the `classDef` syntax, which is a standard feature across many Mermaid diagram types:
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Main"
|
||||
"A": 20
|
||||
"B":::important
|
||||
"B1": 10
|
||||
"B2": 15
|
||||
"C": 5
|
||||
|
||||
classDef important fill:#f96,stroke:#333,stroke-width:2px;
|
||||
```
|
||||
|
||||
```mermaid
|
||||
treemap-beta
|
||||
"Main"
|
||||
"A": 20
|
||||
"B":::important
|
||||
"B1": 10
|
||||
"B2": 15
|
||||
"C": 5
|
||||
|
||||
classDef important fill:#f96,stroke:#333,stroke-width:2px;
|
||||
```
|
||||
|
||||
### Theme Configuration
|
||||
|
||||
You can customize the colors of your treemap using the theme configuration:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
theme: 'forest'
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
theme: 'forest'
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
### Diagram Padding
|
||||
|
||||
You can adjust the padding around the treemap diagram using the `diagramPadding` configuration option:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
diagramPadding: 200
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
diagramPadding: 200
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The treemap diagram supports the following configuration options:
|
||||
|
||||
| Option | Description | Default |
|
||||
| -------------- | --------------------------------------------------------------------------- | ------- |
|
||||
| useMaxWidth | When true, the diagram width is set to 100% and scales with available space | true |
|
||||
| padding | Internal padding between nodes | 10 |
|
||||
| diagramPadding | Padding around the entire diagram | 8 |
|
||||
| showValues | Whether to show values in the treemap | true |
|
||||
| nodeWidth | Width of nodes | 100 |
|
||||
| nodeHeight | Height of nodes | 40 |
|
||||
| borderWidth | Width of borders | 1 |
|
||||
| valueFontSize | Font size for values | 12 |
|
||||
| labelFontSize | Font size for labels | 14 |
|
||||
| valueFormat | Format for values (see Value Formatting section) | ',' |
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Value Formatting
|
||||
|
||||
Values in treemap diagrams can be formatted to display in different ways using the `valueFormat` configuration option. This option primarily uses [D3's format specifiers](https://github.com/d3/d3-format#locale_format) to control how numbers are displayed, with some additional special cases for common formats.
|
||||
|
||||
Some common format patterns:
|
||||
|
||||
- `,` - Thousands separator (default)
|
||||
- `$` - Add dollar sign
|
||||
- `.1f` - Show one decimal place
|
||||
- `.1%` - Show as percentage with one decimal place
|
||||
- `$0,0` - Dollar sign with thousands separator
|
||||
- `$.2f` - Dollar sign with 2 decimal places
|
||||
- `$,.2f` - Dollar sign with thousands separator and 2 decimal places
|
||||
|
||||
The treemap diagram supports both standard D3 format specifiers and some common currency formats that combine the dollar sign with other formatting options.
|
||||
|
||||
Example with currency formatting:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$0,0'
|
||||
---
|
||||
treemap-beta
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 700000
|
||||
"Equipment": 200000
|
||||
"Supplies": 100000
|
||||
"Marketing"
|
||||
"Advertising": 400000
|
||||
"Events": 100000
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$0,0'
|
||||
---
|
||||
treemap-beta
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 700000
|
||||
"Equipment": 200000
|
||||
"Supplies": 100000
|
||||
"Marketing"
|
||||
"Advertising": 400000
|
||||
"Events": 100000
|
||||
```
|
||||
|
||||
Example with percentage formatting:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$.1%'
|
||||
---
|
||||
treemap-beta
|
||||
"Market Share"
|
||||
"Company A": 0.35
|
||||
"Company B": 0.25
|
||||
"Company C": 0.15
|
||||
"Others": 0.25
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$.1%'
|
||||
---
|
||||
treemap-beta
|
||||
"Market Share"
|
||||
"Company A": 0.35
|
||||
"Company B": 0.25
|
||||
"Company C": 0.15
|
||||
"Others": 0.25
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
Treemap diagrams are commonly used for:
|
||||
|
||||
1. **Financial Data**: Visualizing budget allocations, market shares, or portfolio compositions
|
||||
2. **File System Analysis**: Showing disk space usage by folders and files
|
||||
3. **Population Demographics**: Displaying population distribution across regions and subregions
|
||||
4. **Product Hierarchies**: Visualizing product categories and their sales volumes
|
||||
5. **Organizational Structures**: Representing departments and team sizes in a company
|
||||
|
||||
## Limitations
|
||||
|
||||
- Treemap diagrams work best when the data has a natural hierarchy
|
||||
- Very small values may be difficult to see or label in a treemap diagram
|
||||
- Deep hierarchies (many levels) can be challenging to represent clearly
|
||||
- Treemap diagrams are not well suited for representing data with negative values
|
||||
|
||||
## Related Diagrams
|
||||
|
||||
If treemap diagrams don't suit your needs, consider these alternatives:
|
||||
|
||||
- [**Pie Charts**](./pie.md): For simple proportion comparisons without hierarchy
|
||||
- **Sunburst Diagrams**: For hierarchical data with a radial layout (yet to be released in Mermaid).
|
||||
- [**Sankey Diagrams**](./sankey.md): For flow-based hierarchical data
|
||||
|
||||
## Notes
|
||||
|
||||
The treemap diagram implementation in Mermaid is designed to be simple to use while providing powerful visualization capabilities. As this is a newer diagram type, feedback and feature requests are welcome through the Mermaid GitHub repository.
|
12
package.json
12
package.json
@@ -83,12 +83,12 @@
|
||||
"@vitest/spy": "^3.0.6",
|
||||
"@vitest/ui": "^3.0.6",
|
||||
"ajv": "^8.17.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"chokidar": "4.0.3",
|
||||
"concurrently": "^9.1.2",
|
||||
"cors": "^2.8.5",
|
||||
"cpy-cli": "^5.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cspell": "^8.6.1",
|
||||
"cspell": "^9.1.3",
|
||||
"cypress": "^14.0.3",
|
||||
"cypress-image-snapshot": "^4.0.1",
|
||||
"cypress-split": "^1.24.14",
|
||||
@@ -109,25 +109,25 @@
|
||||
"globals": "^16.0.0",
|
||||
"globby": "^14.0.2",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest": "^30.0.4",
|
||||
"jison": "^0.4.18",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"langium-cli": "3.3.0",
|
||||
"lint-staged": "^15.2.11",
|
||||
"lint-staged": "^16.1.2",
|
||||
"markdown-table": "^3.0.4",
|
||||
"nyc": "^17.1.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"prettier": "^3.5.2",
|
||||
"prettier-plugin-jsdoc": "^1.3.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"rollup-plugin-visualizer": "^6.0.3",
|
||||
"start-server-and-test": "^2.0.10",
|
||||
"tslib": "^2.8.1",
|
||||
"tsx": "^4.7.3",
|
||||
"typescript": "~5.7.3",
|
||||
"typescript-eslint": "^8.32.0",
|
||||
"vite": "^6.1.1",
|
||||
"vite": "^7.0.3",
|
||||
"vite-plugin-istanbul": "^7.0.0",
|
||||
"vitest": "^3.0.6"
|
||||
},
|
||||
|
32
packages/examples/package.json
Normal file
32
packages/examples/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@mermaid-js/examples",
|
||||
"version": "0.0.1-beta.1",
|
||||
"description": "Mermaid examples package",
|
||||
"author": "Sidharth Vinod",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid-examples.core.mjs",
|
||||
"types": "./dist/mermaid.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/mermaid-examples.core.mjs",
|
||||
"default": "./dist/mermaid-examples.core.mjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rimraf dist"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"mermaid": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"mermaid": "workspace:~"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
34
packages/examples/src/example.spec.ts
Normal file
34
packages/examples/src/example.spec.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import mermaid from 'mermaid';
|
||||
import { diagramData } from './index.js';
|
||||
|
||||
describe('examples', () => {
|
||||
beforeAll(async () => {
|
||||
// To trigger the diagram registration
|
||||
await mermaid.registerExternalDiagrams([]);
|
||||
});
|
||||
|
||||
it('should have examples for each diagrams', () => {
|
||||
const skippedDiagrams = [
|
||||
// These diagrams have no examples
|
||||
'error',
|
||||
'info',
|
||||
'---',
|
||||
// These diagrams have v2 versions, with examples
|
||||
'class',
|
||||
'graph',
|
||||
'flowchart-elk',
|
||||
'flowchart',
|
||||
'state',
|
||||
];
|
||||
const diagrams = mermaid
|
||||
.getRegisteredDiagramsMetadata()
|
||||
.filter((d) => !skippedDiagrams.includes(d.id));
|
||||
expect(diagrams.length).toBeGreaterThan(0);
|
||||
for (const diagram of diagrams) {
|
||||
const data = diagramData.find((d) => d.id === diagram.id)!;
|
||||
expect(data, `Example for ${diagram.id} is not defined`).toBeDefined();
|
||||
expect(data.examples.length).toBeGreaterThan(0);
|
||||
expect(data.examples.filter((e) => e.isDefault).length).toBe(1);
|
||||
}
|
||||
});
|
||||
});
|
24
packages/examples/src/examples/architecture.ts
Normal file
24
packages/examples/src/examples/architecture.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'architecture',
|
||||
name: 'Architecture Diagram',
|
||||
description: 'Visualize system architecture and components',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic System Architecture',
|
||||
isDefault: true,
|
||||
code: `architecture-beta
|
||||
group api(cloud)[API]
|
||||
|
||||
service db(database)[Database] in api
|
||||
service disk1(disk)[Storage] in api
|
||||
service disk2(disk)[Storage] in api
|
||||
service server(server)[Server] in api
|
||||
|
||||
db:L -- R:server
|
||||
disk1:T -- B:server
|
||||
disk2:T -- B:db`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
27
packages/examples/src/examples/block.ts
Normal file
27
packages/examples/src/examples/block.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'block',
|
||||
name: 'Block Diagram',
|
||||
description: 'Create block-based visualizations with beta styling',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Block Layout',
|
||||
isDefault: true,
|
||||
code: `block-beta
|
||||
columns 1
|
||||
db(("DB"))
|
||||
blockArrowId6<[" "]>(down)
|
||||
block:ID
|
||||
A
|
||||
B["A wide one in the middle"]
|
||||
C
|
||||
end
|
||||
space
|
||||
D
|
||||
ID --> D
|
||||
C --> D
|
||||
style B fill:#969,stroke:#333,stroke-width:4px`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
47
packages/examples/src/examples/c4.ts
Normal file
47
packages/examples/src/examples/c4.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'c4',
|
||||
name: 'C4 Diagram',
|
||||
description:
|
||||
'Visualize software architecture using the C4 model (Context, Container, Component, Code)',
|
||||
examples: [
|
||||
{
|
||||
title: 'Internet Banking System Context',
|
||||
isDefault: true,
|
||||
code: `C4Context
|
||||
title System Context diagram for Internet Banking System
|
||||
Enterprise_Boundary(b0, "BankBoundary0") {
|
||||
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
|
||||
Person(customerB, "Banking Customer B")
|
||||
Person_Ext(customerC, "Banking Customer C", "desc")
|
||||
|
||||
Person(customerD, "Banking Customer D", "A customer of the bank, <br/> with personal bank accounts.")
|
||||
|
||||
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
|
||||
|
||||
Enterprise_Boundary(b1, "BankBoundary") {
|
||||
SystemDb_Ext(SystemE, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc.")
|
||||
|
||||
System_Boundary(b2, "BankBoundary2") {
|
||||
System(SystemA, "Banking System A")
|
||||
System(SystemB, "Banking System B", "A system of the bank, with personal bank accounts. next line.")
|
||||
}
|
||||
|
||||
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
|
||||
SystemDb(SystemD, "Banking System D Database", "A system of the bank, with personal bank accounts.")
|
||||
|
||||
Boundary(b3, "BankBoundary3", "boundary") {
|
||||
SystemQueue(SystemF, "Banking System F Queue", "A system of the bank.")
|
||||
SystemQueue_Ext(SystemG, "Banking System G Queue", "A system of the bank, with personal bank accounts.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BiRel(customerA, SystemAA, "Uses")
|
||||
BiRel(SystemAA, SystemE, "Uses")
|
||||
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
|
||||
Rel(SystemC, customerA, "Sends e-mails to")`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
34
packages/examples/src/examples/class.ts
Normal file
34
packages/examples/src/examples/class.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'classDiagram',
|
||||
name: 'Class Diagram',
|
||||
description: 'Visualize class structures and relationships in object-oriented programming',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Class Inheritance',
|
||||
isDefault: true,
|
||||
code: `classDiagram
|
||||
Animal <|-- Duck
|
||||
Animal <|-- Fish
|
||||
Animal <|-- Zebra
|
||||
Animal : +int age
|
||||
Animal : +String gender
|
||||
Animal: +isMammal()
|
||||
Animal: +mate()
|
||||
class Duck{
|
||||
+String beakColor
|
||||
+swim()
|
||||
+quack()
|
||||
}
|
||||
class Fish{
|
||||
-int sizeInFeet
|
||||
-canEat()
|
||||
}
|
||||
class Zebra{
|
||||
+bool is_wild
|
||||
+run()
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
36
packages/examples/src/examples/er.ts
Normal file
36
packages/examples/src/examples/er.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'er',
|
||||
name: 'Entity Relationship Diagram',
|
||||
description: 'Visualize database schemas and relationships between entities',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic ER Schema',
|
||||
isDefault: true,
|
||||
code: `erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ ORDER_ITEM : contains
|
||||
PRODUCT ||--o{ ORDER_ITEM : includes
|
||||
CUSTOMER {
|
||||
string id
|
||||
string name
|
||||
string email
|
||||
}
|
||||
ORDER {
|
||||
string id
|
||||
date orderDate
|
||||
string status
|
||||
}
|
||||
PRODUCT {
|
||||
string id
|
||||
string name
|
||||
float price
|
||||
}
|
||||
ORDER_ITEM {
|
||||
int quantity
|
||||
float price
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
19
packages/examples/src/examples/flowchart.ts
Normal file
19
packages/examples/src/examples/flowchart.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'flowchart-v2',
|
||||
name: 'Flowchart',
|
||||
description: 'Visualize flowcharts and directed graphs',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Flowchart',
|
||||
isDefault: true,
|
||||
code: `flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
22
packages/examples/src/examples/gantt.ts
Normal file
22
packages/examples/src/examples/gantt.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'gantt',
|
||||
name: 'Gantt Chart',
|
||||
description: 'Visualize project schedules and timelines',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Project Timeline',
|
||||
isDefault: true,
|
||||
code: `gantt
|
||||
title A Gantt Diagram
|
||||
dateFormat YYYY-MM-DD
|
||||
section Section
|
||||
A task :a1, 2014-01-01, 30d
|
||||
Another task :after a1 , 20d
|
||||
section Another
|
||||
Task in sec :2014-01-12 , 12d
|
||||
another task : 24d`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
28
packages/examples/src/examples/git.ts
Normal file
28
packages/examples/src/examples/git.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'gitGraph',
|
||||
name: 'Git Graph',
|
||||
description: 'Visualize Git repository history and branch relationships',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Git Flow',
|
||||
isDefault: true,
|
||||
code: `gitGraph
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
branch feature
|
||||
checkout feature
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge feature`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
37
packages/examples/src/examples/kanban.ts
Normal file
37
packages/examples/src/examples/kanban.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'kanban',
|
||||
name: 'Kanban Diagram',
|
||||
description: 'Visualize work items in a Kanban board',
|
||||
examples: [
|
||||
{
|
||||
title: 'Kanban Diagram',
|
||||
isDefault: true,
|
||||
code: `---
|
||||
config:
|
||||
kanban:
|
||||
ticketBaseUrl: 'https://github.com/mermaid-js/mermaid/issues/#TICKET#'
|
||||
---
|
||||
kanban
|
||||
Todo
|
||||
[Create Documentation]
|
||||
docs[Create Blog about the new diagram]
|
||||
[In progress]
|
||||
id6[Create renderer so that it works in all cases. We also add some extra text here for testing purposes. And some more just for the extra flare.]
|
||||
id9[Ready for deploy]
|
||||
id8[Design grammar]@{ assigned: 'knsv' }
|
||||
id10[Ready for test]
|
||||
id4[Create parsing tests]@{ ticket: 2038, assigned: 'K.Sveidqvist', priority: 'High' }
|
||||
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
|
||||
id11[Done]
|
||||
id5[define getData]
|
||||
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: 2036, priority: 'Very High'}
|
||||
id3[Update DB function]@{ ticket: 2037, assigned: knsv, priority: 'High' }
|
||||
|
||||
id12[Can't reproduce]
|
||||
id3[Weird flickering in Firefox]
|
||||
`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
32
packages/examples/src/examples/mindmap.ts
Normal file
32
packages/examples/src/examples/mindmap.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'mindmap',
|
||||
name: 'Mindmap',
|
||||
description: 'Visualize ideas and concepts in a tree-like structure',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Mindmap',
|
||||
isDefault: true,
|
||||
code: `mindmap
|
||||
root((mindmap))
|
||||
Origins
|
||||
Long history
|
||||
::icon(fa fa-book)
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
Strategic planning
|
||||
Argument mapping
|
||||
Tools
|
||||
Pen and paper
|
||||
Mermaid`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
||||
|
||||
// cspell:ignore Buzan
|
34
packages/examples/src/examples/packet.ts
Normal file
34
packages/examples/src/examples/packet.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'packet',
|
||||
name: 'Packet Diagram',
|
||||
description: 'Visualize packet data and network traffic',
|
||||
examples: [
|
||||
{
|
||||
title: 'TCP Packet',
|
||||
isDefault: true,
|
||||
code: `---
|
||||
title: "TCP Packet"
|
||||
---
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
64-95: "Acknowledgment Number"
|
||||
96-99: "Data Offset"
|
||||
100-105: "Reserved"
|
||||
106: "URG"
|
||||
107: "ACK"
|
||||
108: "PSH"
|
||||
109: "RST"
|
||||
110: "SYN"
|
||||
111: "FIN"
|
||||
112-127: "Window"
|
||||
128-143: "Checksum"
|
||||
144-159: "Urgent Pointer"
|
||||
160-191: "(Options and Padding)"
|
||||
192-255: "Data (variable length)"`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
17
packages/examples/src/examples/pie.ts
Normal file
17
packages/examples/src/examples/pie.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'pie',
|
||||
name: 'Pie Chart',
|
||||
description: 'Visualize data as proportional segments of a circle',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Pie Chart',
|
||||
isDefault: true,
|
||||
code: `pie title Pets adopted by volunteers
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
27
packages/examples/src/examples/quadrant-chart.ts
Normal file
27
packages/examples/src/examples/quadrant-chart.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'quadrantChart',
|
||||
name: 'Quadrant Chart',
|
||||
description: 'Visualize items in a 2x2 matrix based on two variables',
|
||||
examples: [
|
||||
{
|
||||
title: 'Product Positioning',
|
||||
isDefault: true,
|
||||
code: `quadrantChart
|
||||
title Reach and engagement of campaigns
|
||||
x-axis Low Reach --> High Reach
|
||||
y-axis Low Engagement --> High Engagement
|
||||
quadrant-1 We should expand
|
||||
quadrant-2 Need to promote
|
||||
quadrant-3 Re-evaluate
|
||||
quadrant-4 May be improved
|
||||
Campaign A: [0.3, 0.6]
|
||||
Campaign B: [0.45, 0.23]
|
||||
Campaign C: [0.57, 0.69]
|
||||
Campaign D: [0.78, 0.34]
|
||||
Campaign E: [0.40, 0.34]
|
||||
Campaign F: [0.35, 0.78]`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
25
packages/examples/src/examples/radar.ts
Normal file
25
packages/examples/src/examples/radar.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'radar',
|
||||
name: 'Radar Diagram',
|
||||
description: 'Visualize data in a radial format',
|
||||
examples: [
|
||||
{
|
||||
title: 'Student Grades',
|
||||
isDefault: true,
|
||||
code: `---
|
||||
title: "Grades"
|
||||
---
|
||||
radar-beta
|
||||
axis m["Math"], s["Science"], e["English"]
|
||||
axis h["History"], g["Geography"], a["Art"]
|
||||
curve a["Alice"]{85, 90, 80, 70, 75, 90}
|
||||
curve b["Bob"]{70, 75, 85, 80, 90, 85}
|
||||
|
||||
max 100
|
||||
min 0
|
||||
`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
27
packages/examples/src/examples/requirement.ts
Normal file
27
packages/examples/src/examples/requirement.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'requirement',
|
||||
name: 'Requirement Diagram',
|
||||
description: 'Visualize system requirements and their relationships',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Requirements',
|
||||
code: `requirementDiagram
|
||||
|
||||
requirement test_req {
|
||||
id: 1
|
||||
text: the test text.
|
||||
risk: high
|
||||
verifymethod: test
|
||||
}
|
||||
|
||||
element test_entity {
|
||||
type: simulation
|
||||
}
|
||||
|
||||
test_entity - satisfies -> test_req`,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
88
packages/examples/src/examples/sankey.ts
Normal file
88
packages/examples/src/examples/sankey.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'sankey',
|
||||
name: 'Sankey Diagram',
|
||||
description: 'Visualize flow quantities between different stages or processes',
|
||||
examples: [
|
||||
{
|
||||
title: 'Energy Flow',
|
||||
isDefault: true,
|
||||
code: `---
|
||||
config:
|
||||
sankey:
|
||||
showValues: false
|
||||
---
|
||||
sankey-beta
|
||||
|
||||
Agricultural 'waste',Bio-conversion,124.729
|
||||
Bio-conversion,Liquid,0.597
|
||||
Bio-conversion,Losses,26.862
|
||||
Bio-conversion,Solid,280.322
|
||||
Bio-conversion,Gas,81.144
|
||||
Biofuel imports,Liquid,35
|
||||
Biomass imports,Solid,35
|
||||
Coal imports,Coal,11.606
|
||||
Coal reserves,Coal,63.965
|
||||
Coal,Solid,75.571
|
||||
District heating,Industry,10.639
|
||||
District heating,Heating and cooling - commercial,22.505
|
||||
District heating,Heating and cooling - homes,46.184
|
||||
Electricity grid,Over generation / exports,104.453
|
||||
Electricity grid,Heating and cooling - homes,113.726
|
||||
Electricity grid,H2 conversion,27.14
|
||||
Electricity grid,Industry,342.165
|
||||
Electricity grid,Road transport,37.797
|
||||
Electricity grid,Agriculture,4.412
|
||||
Electricity grid,Heating and cooling - commercial,40.858
|
||||
Electricity grid,Losses,56.691
|
||||
Electricity grid,Rail transport,7.863
|
||||
Electricity grid,Lighting & appliances - commercial,90.008
|
||||
Electricity grid,Lighting & appliances - homes,93.494
|
||||
Gas imports,NGas,40.719
|
||||
Gas reserves,NGas,82.233
|
||||
Gas,Heating and cooling - commercial,0.129
|
||||
Gas,Losses,1.401
|
||||
Gas,Thermal generation,151.891
|
||||
Gas,Agriculture,2.096
|
||||
Gas,Industry,48.58
|
||||
Geothermal,Electricity grid,7.013
|
||||
H2 conversion,H2,20.897
|
||||
H2 conversion,Losses,6.242
|
||||
H2,Road transport,20.897
|
||||
Hydro,Electricity grid,6.995
|
||||
Liquid,Industry,121.066
|
||||
Liquid,International shipping,128.69
|
||||
Liquid,Road transport,135.835
|
||||
Liquid,Domestic aviation,14.458
|
||||
Liquid,International aviation,206.267
|
||||
Liquid,Agriculture,3.64
|
||||
Liquid,National navigation,33.218
|
||||
Liquid,Rail transport,4.413
|
||||
Marine algae,Bio-conversion,4.375
|
||||
NGas,Gas,122.952
|
||||
Nuclear,Thermal generation,839.978
|
||||
Oil imports,Oil,504.287
|
||||
Oil reserves,Oil,107.703
|
||||
Oil,Liquid,611.99
|
||||
Other waste,Solid,56.587
|
||||
Other waste,Bio-conversion,77.81
|
||||
Pumped heat,Heating and cooling - homes,193.026
|
||||
Pumped heat,Heating and cooling - commercial,70.672
|
||||
Solar PV,Electricity grid,59.901
|
||||
Solar Thermal,Heating and cooling - homes,19.263
|
||||
Solar,Solar Thermal,19.263
|
||||
Solar,Solar PV,59.901
|
||||
Solid,Agriculture,0.882
|
||||
Solid,Thermal generation,400.12
|
||||
Solid,Industry,46.477
|
||||
Thermal generation,Electricity grid,525.531
|
||||
Thermal generation,Losses,787.129
|
||||
Thermal generation,District heating,79.329
|
||||
Tidal,Electricity grid,9.452
|
||||
UK land based bioenergy,Bio-conversion,182.01
|
||||
Wave,Electricity grid,19.013
|
||||
Wind,Electricity grid,289.366`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
18
packages/examples/src/examples/sequence.ts
Normal file
18
packages/examples/src/examples/sequence.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'sequence',
|
||||
name: 'Sequence Diagram',
|
||||
description: 'Visualize interactions between objects over time',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic Sequence',
|
||||
isDefault: true,
|
||||
code: `sequenceDiagram
|
||||
Alice->>+John: Hello John, how are you?
|
||||
Alice->>+John: John, can you hear me?
|
||||
John-->>-Alice: Hi Alice, I can hear you!
|
||||
John-->>-Alice: I feel great!`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
20
packages/examples/src/examples/state.ts
Normal file
20
packages/examples/src/examples/state.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'stateDiagram',
|
||||
name: 'State Diagram',
|
||||
description: 'Visualize the states and transitions of a system',
|
||||
examples: [
|
||||
{
|
||||
title: 'Basic State Diagram',
|
||||
code: `stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]`,
|
||||
isDefault: true,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
20
packages/examples/src/examples/timeline.ts
Normal file
20
packages/examples/src/examples/timeline.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'timeline',
|
||||
name: 'Timeline Diagram',
|
||||
description: 'Visualize events and milestones in chronological order',
|
||||
examples: [
|
||||
{
|
||||
title: 'Project Timeline',
|
||||
isDefault: true,
|
||||
code: `timeline
|
||||
title History of Social Media Platform
|
||||
2002 : LinkedIn
|
||||
2004 : Facebook
|
||||
: Google
|
||||
2005 : YouTube
|
||||
2006 : Twitter`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
21
packages/examples/src/examples/treemap.ts
Normal file
21
packages/examples/src/examples/treemap.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'treemap',
|
||||
name: 'Treemap',
|
||||
description: 'Visualize hierarchical data as nested rectangles',
|
||||
examples: [
|
||||
{
|
||||
title: 'Treemap',
|
||||
isDefault: true,
|
||||
code: `treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2"
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20
|
||||
"Leaf 2.2": 25`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
22
packages/examples/src/examples/user-journey.ts
Normal file
22
packages/examples/src/examples/user-journey.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'journey',
|
||||
name: 'User Journey Diagram',
|
||||
description: 'Visualize user interactions and experiences with a system',
|
||||
examples: [
|
||||
{
|
||||
title: 'My Working Day',
|
||||
isDefault: true,
|
||||
code: `journey
|
||||
title My working day
|
||||
section Go to work
|
||||
Make tea: 5: Me
|
||||
Go upstairs: 3: Me
|
||||
Do work: 1: Me, Cat
|
||||
section Go home
|
||||
Go downstairs: 5: Me
|
||||
Sit down: 5: Me`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
19
packages/examples/src/examples/xychart.ts
Normal file
19
packages/examples/src/examples/xychart.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { DiagramMetadata } from '../types.js';
|
||||
|
||||
export default {
|
||||
id: 'xychart',
|
||||
name: 'XY Chart',
|
||||
description: 'Create scatter plots and line charts with customizable axes',
|
||||
examples: [
|
||||
{
|
||||
title: 'Sales Revenue',
|
||||
isDefault: true,
|
||||
code: `xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]`,
|
||||
},
|
||||
],
|
||||
} satisfies DiagramMetadata;
|
48
packages/examples/src/index.ts
Normal file
48
packages/examples/src/index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { DiagramMetadata } from './types.js';
|
||||
import flowChart from './examples/flowchart.js';
|
||||
import c4 from './examples/c4.js';
|
||||
import kanban from './examples/kanban.js';
|
||||
import classDiagram from './examples/class.js';
|
||||
import sequenceDiagram from './examples/sequence.js';
|
||||
import pieDiagram from './examples/pie.js';
|
||||
import userJourneyDiagram from './examples/user-journey.js';
|
||||
import mindmapDiagram from './examples/mindmap.js';
|
||||
import requirementDiagram from './examples/requirement.js';
|
||||
import radarDiagram from './examples/radar.js';
|
||||
import stateDiagram from './examples/state.js';
|
||||
import erDiagram from './examples/er.js';
|
||||
import gitDiagram from './examples/git.js';
|
||||
import architectureDiagram from './examples/architecture.js';
|
||||
import xychartDiagram from './examples/xychart.js';
|
||||
import sankeyDiagram from './examples/sankey.js';
|
||||
import ganttDiagram from './examples/gantt.js';
|
||||
import timelineDiagram from './examples/timeline.js';
|
||||
import quadrantChart from './examples/quadrant-chart.js';
|
||||
import packetDiagram from './examples/packet.js';
|
||||
import blockDiagram from './examples/block.js';
|
||||
import treemapDiagram from './examples/treemap.js';
|
||||
|
||||
export const diagramData: DiagramMetadata[] = [
|
||||
flowChart,
|
||||
c4,
|
||||
kanban,
|
||||
classDiagram,
|
||||
sequenceDiagram,
|
||||
pieDiagram,
|
||||
userJourneyDiagram,
|
||||
mindmapDiagram,
|
||||
requirementDiagram,
|
||||
radarDiagram,
|
||||
stateDiagram,
|
||||
erDiagram,
|
||||
gitDiagram,
|
||||
architectureDiagram,
|
||||
xychartDiagram,
|
||||
sankeyDiagram,
|
||||
ganttDiagram,
|
||||
timelineDiagram,
|
||||
quadrantChart,
|
||||
packetDiagram,
|
||||
blockDiagram,
|
||||
treemapDiagram,
|
||||
];
|
12
packages/examples/src/types.ts
Normal file
12
packages/examples/src/types.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface Example {
|
||||
title: string;
|
||||
code: string;
|
||||
isDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface DiagramMetadata {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
examples: Example[];
|
||||
}
|
11
packages/examples/tsconfig.json
Normal file
11
packages/examples/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16"
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
@@ -1,5 +1,25 @@
|
||||
# mermaid
|
||||
|
||||
## 11.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`0da2922`](https://github.com/mermaid-js/mermaid/commit/0da2922ee7f47959e324ec10d3d21ee70594f557)]:
|
||||
- @mermaid-js/parser@0.6.1
|
||||
|
||||
## 11.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#6590](https://github.com/mermaid-js/mermaid/pull/6590) [`f338802`](https://github.com/mermaid-js/mermaid/commit/f338802642cdecf5b7ed6c19a20cf2a81effbbee) Thanks [@knsv](https://github.com/knsv)! - Adding support for the new diagram type nested treemap
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6707](https://github.com/mermaid-js/mermaid/pull/6707) [`592c5bb`](https://github.com/mermaid-js/mermaid/commit/592c5bb880c3b942710a2878d386bcb3eb35c137) Thanks [@darshanr0107](https://github.com/darshanr0107)! - fix: Log a warning when duplicate commit IDs are encountered in gitGraph to help identify and debug rendering issues caused by non-unique IDs.
|
||||
|
||||
- Updated dependencies [[`f338802`](https://github.com/mermaid-js/mermaid/commit/f338802642cdecf5b7ed6c19a20cf2a81effbbee)]:
|
||||
- @mermaid-js/parser@0.6.0
|
||||
|
||||
## 11.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "11.7.0",
|
||||
"version": "11.8.1",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, mindmaps, sequence diagrams, class diagrams, gantt charts, git graphs and more.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -82,7 +82,7 @@
|
||||
"katex": "^0.16.9",
|
||||
"khroma": "^2.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^15.0.7",
|
||||
"marked": "^16.0.0",
|
||||
"roughjs": "^4.6.6",
|
||||
"stylis": "^4.3.6",
|
||||
"ts-dedent": "^2.2.0",
|
||||
@@ -105,7 +105,7 @@
|
||||
"@types/stylis": "^4.2.7",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"ajv": "^8.17.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"chokidar": "4.0.3",
|
||||
"concurrently": "^9.1.2",
|
||||
"csstree-validator": "^4.0.1",
|
||||
"globby": "^14.0.2",
|
||||
|
@@ -262,6 +262,18 @@ const config: RequiredDeep<MermaidConfig> = {
|
||||
radar: {
|
||||
...defaultConfigJson.radar,
|
||||
},
|
||||
treemap: {
|
||||
useMaxWidth: true,
|
||||
padding: 10,
|
||||
diagramPadding: 8,
|
||||
showValues: true,
|
||||
nodeWidth: 100,
|
||||
nodeHeight: 40,
|
||||
borderWidth: 1,
|
||||
valueFontSize: 12,
|
||||
labelFontSize: 14,
|
||||
valueFormat: ',',
|
||||
},
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@@ -27,6 +27,7 @@ import block from '../diagrams/block/blockDetector.js';
|
||||
import architecture from '../diagrams/architecture/architectureDetector.js';
|
||||
import { registerLazyLoadedDiagrams } from './detectType.js';
|
||||
import { registerDiagram } from './diagramAPI.js';
|
||||
import { treemap } from '../diagrams/treemap/detector.js';
|
||||
import '../type.d.ts';
|
||||
|
||||
let hasLoadedDiagrams = false;
|
||||
@@ -99,6 +100,7 @@ export const addDiagrams = () => {
|
||||
packet,
|
||||
xychart,
|
||||
block,
|
||||
radar
|
||||
radar,
|
||||
treemap
|
||||
);
|
||||
};
|
||||
|
@@ -7,20 +7,21 @@ export const loadRegisteredDiagrams = async () => {
|
||||
// Load all lazy loaded diagrams in parallel
|
||||
const results = await Promise.allSettled(
|
||||
Object.entries(detectors).map(async ([key, { detector, loader }]) => {
|
||||
if (loader) {
|
||||
if (!loader) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch {
|
||||
try {
|
||||
getDiagram(key);
|
||||
} catch {
|
||||
try {
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
// Register diagram if it is not already registered
|
||||
const { diagram, id } = await loader();
|
||||
registerDiagram(id, diagram, detector);
|
||||
} catch (err) {
|
||||
// Remove failed diagram from detectors
|
||||
log.error(`Failed to load external diagram with key ${key}. Removing from detectors.`);
|
||||
delete detectors[key];
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import type * as d3 from 'd3';
|
||||
import type { SetRequired } from 'type-fest';
|
||||
import type { SetOptional, SetRequired } from 'type-fest';
|
||||
import type { Diagram } from '../Diagram.js';
|
||||
import type { BaseDiagramConfig, MermaidConfig } from '../config.type.js';
|
||||
|
||||
@@ -91,17 +91,13 @@ export interface DiagramDefinition {
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface DetectorRecord {
|
||||
detector: DiagramDetector;
|
||||
loader?: DiagramLoader;
|
||||
}
|
||||
|
||||
export interface ExternalDiagramDefinition {
|
||||
id: string;
|
||||
detector: DiagramDetector;
|
||||
loader: DiagramLoader;
|
||||
}
|
||||
|
||||
export type DetectorRecord = SetOptional<Omit<ExternalDiagramDefinition, 'id'>, 'loader'>;
|
||||
export type DiagramDetector = (text: string, config?: MermaidConfig) => boolean;
|
||||
export type DiagramLoader = () => Promise<{ id: string; diagram: DiagramDefinition }>;
|
||||
|
||||
|
@@ -287,7 +287,7 @@ const setBlock = (block: Block) => {
|
||||
blockDatabase.set(block.id, block);
|
||||
};
|
||||
|
||||
const getLogger = () => console;
|
||||
const getLogger = () => log;
|
||||
|
||||
/**
|
||||
* Return all of the style classes
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { rejects } from 'assert';
|
||||
import { log } from '../../logger.js';
|
||||
import { db } from './gitGraphAst.js';
|
||||
import { parser } from './gitGraphParser.js';
|
||||
|
||||
@@ -1319,4 +1319,42 @@ describe('when parsing a gitGraph', function () {
|
||||
}
|
||||
});
|
||||
});
|
||||
it('should log a warning when two commits have the same ID', async () => {
|
||||
const str = `gitGraph
|
||||
commit id:"initial commit"
|
||||
commit id:"work on first release"
|
||||
commit id:"design freeze from here"
|
||||
branch v1-rc
|
||||
checkout v1-rc
|
||||
commit id:"bugfix 1"
|
||||
commit id:"bigfix 2" tag:"v1.0.1"
|
||||
branch FORK-v1.0-MDR
|
||||
checkout FORK-v1.0-MDR
|
||||
commit id:"working on MDR"
|
||||
checkout v1-rc
|
||||
commit id:"minor design changes for MDR" tag:"v1.0.2"
|
||||
checkout FORK-v1.0-MDR
|
||||
merge v1-rc
|
||||
checkout main
|
||||
commit id:"new feature for v1.1…"
|
||||
checkout FORK-v1.0-MDR
|
||||
commit id:"working on MDR"
|
||||
commit id:"finishing MDR"
|
||||
branch v1.0-MDR
|
||||
checkout v1.0-MDR
|
||||
commit id:"brush up release" tag:"v1.0.2-MDR"
|
||||
checkout v1-rc
|
||||
commit id:"bugfix without MDR"
|
||||
checkout main
|
||||
commit id:"work on v1.1"
|
||||
`;
|
||||
|
||||
const logWarnSpy = vi.spyOn(log, 'warn').mockImplementation(() => undefined);
|
||||
|
||||
await parser.parse(str);
|
||||
|
||||
expect(logWarnSpy).toHaveBeenCalledWith('Commit ID working on MDR already exists');
|
||||
|
||||
logWarnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@@ -125,6 +125,9 @@ export const commit = function (commitDB: CommitDB) {
|
||||
};
|
||||
state.records.head = newCommit;
|
||||
log.info('main branch', config.mainBranchName);
|
||||
if (state.records.commits.has(newCommit.id)) {
|
||||
log.warn(`Commit ID ${newCommit.id} already exists`);
|
||||
}
|
||||
state.records.commits.set(newCommit.id, newCommit);
|
||||
state.records.branches.set(state.records.currBranch, newCommit.id);
|
||||
log.debug('in pushCommit ' + newCommit.id);
|
||||
|
@@ -7,7 +7,7 @@ import type {
|
||||
const id = 'packet';
|
||||
|
||||
const detector: DiagramDetector = (txt) => {
|
||||
return /^\s*packet-beta/.test(txt);
|
||||
return /^\s*packet(-beta)?/.test(txt);
|
||||
};
|
||||
|
||||
const loader: DiagramLoader = async () => {
|
||||
|
@@ -15,8 +15,14 @@ describe('packet diagrams', () => {
|
||||
expect(getPacket()).toMatchInlineSnapshot('[]');
|
||||
});
|
||||
|
||||
it('should handle a packet definition', async () => {
|
||||
const str = `packet`;
|
||||
await expect(parser.parse(str)).resolves.not.toThrow();
|
||||
expect(getPacket()).toMatchInlineSnapshot('[]');
|
||||
});
|
||||
|
||||
it('should handle diagram with data and title', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
title Packet diagram
|
||||
accTitle: Packet accTitle
|
||||
accDescr: Packet accDescription
|
||||
@@ -41,7 +47,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should handle single bits', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-10: "test"
|
||||
11: "single"
|
||||
`;
|
||||
@@ -67,7 +73,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should handle bit counts', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
+8: "byte"
|
||||
+16: "word"
|
||||
`;
|
||||
@@ -93,7 +99,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should handle bit counts with bit or bits', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
+8: "byte"
|
||||
+16: "word"
|
||||
`;
|
||||
@@ -119,7 +125,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should split into multiple rows', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-10: "test"
|
||||
11-90: "multiple"
|
||||
`;
|
||||
@@ -161,7 +167,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should split into multiple rows when cut at exact length', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-16: "test"
|
||||
17-63: "multiple"
|
||||
`;
|
||||
@@ -195,7 +201,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if numbers are not continuous', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-16: "test"
|
||||
18-20: "error"
|
||||
`;
|
||||
@@ -205,7 +211,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if numbers are not continuous with bit counts', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
+16: "test"
|
||||
18-20: "error"
|
||||
`;
|
||||
@@ -215,7 +221,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if numbers are not continuous for single packets', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-16: "test"
|
||||
18: "error"
|
||||
`;
|
||||
@@ -225,7 +231,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if numbers are not continuous for single packets with bit counts', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
+16: "test"
|
||||
18: "error"
|
||||
`;
|
||||
@@ -235,7 +241,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if numbers are not continuous for single packets - 2', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-16: "test"
|
||||
17: "good"
|
||||
19: "error"
|
||||
@@ -246,7 +252,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if end is less than start', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
0-16: "test"
|
||||
25-20: "error"
|
||||
`;
|
||||
@@ -256,7 +262,7 @@ describe('packet diagrams', () => {
|
||||
});
|
||||
|
||||
it('should throw error if bit count is 0', async () => {
|
||||
const str = `packet-beta
|
||||
const str = `packet
|
||||
+0: "test"
|
||||
`;
|
||||
await expect(parser.parse(str)).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
@@ -1,6 +1,91 @@
|
||||
import { arc as d3arc, select } from 'd3';
|
||||
import { createText } from '../../rendering-util/createText.js';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
const MAX_SECTIONS = 12;
|
||||
|
||||
/**
|
||||
* Process HTML content in node descriptions
|
||||
* @param {object} textElem - The SVG element to append text to
|
||||
* @param {object} node - The node object containing description and dimensions
|
||||
* @param {object} conf - Configuration object
|
||||
* @param {boolean} isVirtual - Whether this is for virtual height calculation
|
||||
*/
|
||||
const processHtmlContent = async function (textElem, node, conf, isVirtual = false) {
|
||||
// Create temporary text to get initial dimensions
|
||||
const sanitizedHtml = DOMPurify.sanitize(node.descr, { ALLOWED_TAGS: [] });
|
||||
const tempText = textElem
|
||||
.append('text')
|
||||
.text(sanitizedHtml)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
|
||||
if (!isVirtual) {
|
||||
tempText.attr('visibility', 'hidden');
|
||||
}
|
||||
|
||||
const bbox = tempText.node().getBBox();
|
||||
tempText.remove();
|
||||
|
||||
// Create the actual HTML content
|
||||
const textObj = await createText(
|
||||
textElem,
|
||||
node.descr,
|
||||
{
|
||||
useHtmlLabels: true,
|
||||
width: node.width,
|
||||
classes: 'timeline-node-label',
|
||||
isNode: true,
|
||||
},
|
||||
conf
|
||||
);
|
||||
|
||||
if (!isVirtual) {
|
||||
select(textObj).attr('transform', 'translate(0, 0)');
|
||||
}
|
||||
|
||||
// Process the foreign object
|
||||
const foreignObject = textElem.select('foreignObject');
|
||||
if (foreignObject.node()) {
|
||||
foreignObject.attr('width', `${10 * node.width}px`).attr('height', `${10 * node.width}px`);
|
||||
|
||||
const div = foreignObject.select('div');
|
||||
if (div.node()) {
|
||||
div
|
||||
.style('display', 'table-cell')
|
||||
.style('white-space', 'nowrap')
|
||||
.style('line-height', '1.5')
|
||||
.style('max-width', node.width + 'px')
|
||||
.style('text-align', 'center');
|
||||
|
||||
let divBBox = div.node().getBoundingClientRect();
|
||||
|
||||
if (divBBox.width === node.width) {
|
||||
div
|
||||
.style('display', 'table')
|
||||
.style('white-space', 'break-spaces')
|
||||
.style('width', node.width + 'px');
|
||||
|
||||
divBBox = div.node().getBoundingClientRect();
|
||||
}
|
||||
|
||||
foreignObject.attr('width', node.width).attr('height', divBBox.height);
|
||||
|
||||
if (!isVirtual) {
|
||||
foreignObject.attr('x', -node.width / 2).attr('y', 3);
|
||||
|
||||
div.style('width', node.width + 'px');
|
||||
}
|
||||
bbox.height = divBBox.height;
|
||||
}
|
||||
}
|
||||
|
||||
return bbox;
|
||||
};
|
||||
|
||||
export const drawRect = function (elem, rectData) {
|
||||
const rectElem = elem.append('rect');
|
||||
rectElem.attr('x', rectData.x);
|
||||
@@ -409,6 +494,9 @@ const _drawTextCandidateFunc = (function () {
|
||||
.style('display', 'table-cell')
|
||||
.style('text-align', 'center')
|
||||
.style('vertical-align', 'middle')
|
||||
.style('word-wrap', 'break-word')
|
||||
.style('overflow-wrap', 'break-word')
|
||||
.style('white-space', 'normal')
|
||||
.text(content);
|
||||
|
||||
byTspan(content, body, x, y, width, height, textAttrs, conf);
|
||||
@@ -493,7 +581,7 @@ function wrap(text, width) {
|
||||
});
|
||||
}
|
||||
|
||||
export const drawNode = function (elem, node, fullSection, conf) {
|
||||
export const drawNode = async function (elem, node, fullSection, conf) {
|
||||
const section = (fullSection % MAX_SECTIONS) - 1;
|
||||
const nodeElem = elem.append('g');
|
||||
node.section = section;
|
||||
@@ -506,19 +594,28 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
||||
// Create the wrapped text element
|
||||
const textElem = nodeElem.append('g');
|
||||
|
||||
const txt = textElem
|
||||
.append('text')
|
||||
.text(node.descr)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
const bbox = txt.node().getBBox();
|
||||
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||
node.height = Math.max(node.height, node.maxHeight);
|
||||
node.width = node.width + 2 * node.padding;
|
||||
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
|
||||
|
||||
if (hasHtml) {
|
||||
const bbox = await processHtmlContent(textElem, node, conf, false);
|
||||
node.height = bbox.height + node.padding;
|
||||
node.height = Math.max(node.height, node.maxHeight);
|
||||
node.width = node.width + 2 * node.padding;
|
||||
} else {
|
||||
const txt = textElem
|
||||
.append('text')
|
||||
.text(node.descr)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
const bbox = txt.node().getBBox();
|
||||
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||
node.height = bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||
node.height = Math.max(node.height, node.maxHeight);
|
||||
node.width = node.width + 2 * node.padding;
|
||||
}
|
||||
|
||||
textElem.attr('transform', 'translate(' + node.width / 2 + ', ' + node.padding / 2 + ')');
|
||||
|
||||
@@ -528,17 +625,25 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
||||
return node;
|
||||
};
|
||||
|
||||
export const getVirtualNodeHeight = function (elem, node, conf) {
|
||||
export const getVirtualNodeHeight = async function (elem, node, conf) {
|
||||
const textElem = elem.append('g');
|
||||
const txt = textElem
|
||||
.append('text')
|
||||
.text(node.descr)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
const bbox = txt.node().getBBox();
|
||||
|
||||
const hasHtml = /<[a-z][\S\s]*>/i.test(node.descr);
|
||||
|
||||
let bbox;
|
||||
if (hasHtml) {
|
||||
bbox = await processHtmlContent(textElem, node, conf, true);
|
||||
} else {
|
||||
const txt = textElem
|
||||
.append('text')
|
||||
.text(node.descr)
|
||||
.attr('dy', '1em')
|
||||
.attr('alignment-baseline', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.call(wrap, node.width);
|
||||
bbox = txt.node().getBBox();
|
||||
}
|
||||
const fontSize = conf.fontSize?.replace ? conf.fontSize.replace('px', '') : conf.fontSize;
|
||||
textElem.remove();
|
||||
return bbox.height + fontSize * 1.1 * 0.5 + node.padding;
|
||||
|
@@ -25,11 +25,10 @@ interface TimelineTask {
|
||||
score: number;
|
||||
events: string[];
|
||||
}
|
||||
export const draw = function (text: string, id: string, version: string, diagObj: Diagram) {
|
||||
export const draw = async function (text: string, id: string, version: string, diagObj: Diagram) {
|
||||
//1. Fetch the configuration
|
||||
const conf = getConfig();
|
||||
// @ts-expect-error - wrong config?
|
||||
const LEFT_MARGIN = conf.leftMargin ?? 50;
|
||||
const LEFT_MARGIN = conf.timeline?.leftMargin ?? 50;
|
||||
|
||||
log.debug('timeline', diagObj.db);
|
||||
|
||||
@@ -77,7 +76,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
let hasSections = true;
|
||||
|
||||
//Calculate the max height of the sections
|
||||
sections.forEach(function (section: string) {
|
||||
for (const section of sections) {
|
||||
const sectionNode: Block<string, number> = {
|
||||
number: sectionNumber,
|
||||
descr: section,
|
||||
@@ -86,10 +85,10 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
padding: 20,
|
||||
maxHeight: maxSectionHeight,
|
||||
};
|
||||
const sectionHeight = svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
|
||||
const sectionHeight = await svgDraw.getVirtualNodeHeight(svg, sectionNode, conf);
|
||||
log.debug('sectionHeight before draw', sectionHeight);
|
||||
maxSectionHeight = Math.max(maxSectionHeight, sectionHeight + 20);
|
||||
});
|
||||
}
|
||||
|
||||
//tasks length and maxEventCount
|
||||
let maxEventCount = 0;
|
||||
@@ -107,7 +106,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
padding: 20,
|
||||
maxHeight: maxTaskHeight,
|
||||
};
|
||||
const taskHeight = svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
|
||||
const taskHeight = await svgDraw.getVirtualNodeHeight(svg, taskNode, conf);
|
||||
log.debug('taskHeight before draw', taskHeight);
|
||||
maxTaskHeight = Math.max(maxTaskHeight, taskHeight + 20);
|
||||
|
||||
@@ -124,9 +123,8 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
padding: 20,
|
||||
maxHeight: 50,
|
||||
};
|
||||
maxEventLineLengthTemp += svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
|
||||
maxEventLineLengthTemp += await svgDraw.getVirtualNodeHeight(svg, eventNode, conf);
|
||||
}
|
||||
// Add spacing between events (10px per event except the last one)
|
||||
if (task.events.length > 0) {
|
||||
maxEventLineLengthTemp += (task.events.length - 1) * 10;
|
||||
}
|
||||
@@ -137,7 +135,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
log.debug('maxTaskHeight before draw', maxTaskHeight);
|
||||
|
||||
if (sections && sections.length > 0) {
|
||||
sections.forEach((section) => {
|
||||
for (const section of sections) {
|
||||
//filter task where tasks.section == section
|
||||
const tasksForSection = tasks.filter((task) => task.section === section);
|
||||
|
||||
@@ -151,7 +149,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
};
|
||||
log.debug('sectionNode', sectionNode);
|
||||
const sectionNodeWrapper = svg.append('g');
|
||||
const node = svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
|
||||
const node = await svgDraw.drawNode(sectionNodeWrapper, sectionNode, sectionNumber, conf);
|
||||
log.debug('sectionNode output', node);
|
||||
|
||||
sectionNodeWrapper.attr('transform', `translate(${masterX}, ${sectionBeginY})`);
|
||||
@@ -160,7 +158,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
|
||||
//draw tasks for this section
|
||||
if (tasksForSection.length > 0) {
|
||||
drawTasks(
|
||||
await drawTasks(
|
||||
svg,
|
||||
tasksForSection,
|
||||
sectionNumber,
|
||||
@@ -179,11 +177,11 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
|
||||
masterY = sectionBeginY;
|
||||
sectionNumber++;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
//draw tasks
|
||||
hasSections = false;
|
||||
drawTasks(
|
||||
await drawTasks(
|
||||
svg,
|
||||
tasks,
|
||||
sectionNumber,
|
||||
@@ -237,7 +235,7 @@ export const draw = function (text: string, id: string, version: string, diagObj
|
||||
// addSVGAccessibilityFields(diagObj.db, diagram, id);
|
||||
};
|
||||
|
||||
export const drawTasks = function (
|
||||
export const drawTasks = async function (
|
||||
diagram: Selection<SVGElement, unknown, null, undefined>,
|
||||
tasks: TimelineTask[],
|
||||
sectionColor: number,
|
||||
@@ -266,7 +264,7 @@ export const drawTasks = function (
|
||||
// create task wrapper
|
||||
|
||||
const taskWrapper = diagram.append('g').attr('class', 'taskWrapper');
|
||||
const node = svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
|
||||
const node = await svgDraw.drawNode(taskWrapper, taskNode, sectionColor, conf);
|
||||
const taskHeight = node.height;
|
||||
//log task height
|
||||
log.debug('taskHeight after draw', taskHeight);
|
||||
@@ -283,7 +281,7 @@ export const drawTasks = function (
|
||||
//add margin to task
|
||||
masterY += 100;
|
||||
lineLength =
|
||||
lineLength + drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf);
|
||||
lineLength + (await drawEvents(diagram, task.events, sectionColor, masterX, masterY, conf));
|
||||
masterY -= 100;
|
||||
|
||||
lineWrapper
|
||||
@@ -308,7 +306,7 @@ export const drawTasks = function (
|
||||
masterY = masterY - 10;
|
||||
};
|
||||
|
||||
export const drawEvents = function (
|
||||
export const drawEvents = async function (
|
||||
diagram: Selection<SVGElement, unknown, null, undefined>,
|
||||
events: string[],
|
||||
sectionColor: number,
|
||||
@@ -335,7 +333,7 @@ export const drawEvents = function (
|
||||
log.debug('eventNode', eventNode);
|
||||
// create event wrapper
|
||||
const eventWrapper = diagram.append('g').attr('class', 'eventWrapper');
|
||||
const node = svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
|
||||
const node = await svgDraw.drawNode(eventWrapper, eventNode, sectionColor, conf);
|
||||
const eventHeight = node.height;
|
||||
maxEventHeight = maxEventHeight + eventHeight;
|
||||
eventWrapper.attr('transform', `translate(${masterX}, ${masterY})`);
|
||||
|
112
packages/mermaid/src/diagrams/treemap/db.ts
Normal file
112
packages/mermaid/src/diagrams/treemap/db.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { getConfig as commonGetConfig } from '../../config.js';
|
||||
import DEFAULT_CONFIG from '../../defaultConfig.js';
|
||||
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
|
||||
import { isLabelStyle } from '../../rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
|
||||
|
||||
import { cleanAndMerge } from '../../utils.js';
|
||||
import { ImperativeState } from '../../utils/imperativeState.js';
|
||||
import {
|
||||
clear as commonClear,
|
||||
getAccDescription,
|
||||
getAccTitle,
|
||||
getDiagramTitle,
|
||||
setAccDescription,
|
||||
setAccTitle,
|
||||
setDiagramTitle,
|
||||
} from '../common/commonDb.js';
|
||||
import type { TreemapDB, TreemapData, TreemapDiagramConfig, TreemapNode } from './types.js';
|
||||
|
||||
const defaultTreemapData: TreemapData = {
|
||||
nodes: [],
|
||||
levels: new Map(),
|
||||
outerNodes: [],
|
||||
classes: new Map(),
|
||||
};
|
||||
|
||||
const state = new ImperativeState<TreemapData>(() => structuredClone(defaultTreemapData));
|
||||
|
||||
const getConfig = (): Required<TreemapDiagramConfig> => {
|
||||
// Use type assertion with unknown as intermediate step
|
||||
const defaultConfig = DEFAULT_CONFIG as unknown as { treemap: Required<TreemapDiagramConfig> };
|
||||
const userConfig = commonGetConfig() as unknown as { treemap?: Partial<TreemapDiagramConfig> };
|
||||
|
||||
return cleanAndMerge({
|
||||
...defaultConfig.treemap,
|
||||
...(userConfig.treemap ?? {}),
|
||||
}) as Required<TreemapDiagramConfig>;
|
||||
};
|
||||
|
||||
const getNodes = (): TreemapNode[] => state.records.nodes;
|
||||
|
||||
const addNode = (node: TreemapNode, level: number) => {
|
||||
const data = state.records;
|
||||
data.nodes.push(node);
|
||||
data.levels.set(node, level);
|
||||
|
||||
if (level === 0) {
|
||||
data.outerNodes.push(node);
|
||||
}
|
||||
|
||||
// Set the root node if this is a level 0 node and we don't have a root yet
|
||||
if (level === 0 && !data.root) {
|
||||
data.root = node;
|
||||
}
|
||||
};
|
||||
|
||||
const getRoot = (): TreemapNode | undefined => ({ name: '', children: state.records.outerNodes });
|
||||
|
||||
const addClass = (id: string, _style: string) => {
|
||||
const classes = state.records.classes;
|
||||
const styleClass = classes.get(id) ?? { id, styles: [], textStyles: [] };
|
||||
classes.set(id, styleClass);
|
||||
|
||||
const styles = _style.replace(/\\,/g, '§§§').replace(/,/g, ';').replace(/§§§/g, ',').split(';');
|
||||
|
||||
if (styles) {
|
||||
styles.forEach((s) => {
|
||||
if (isLabelStyle(s)) {
|
||||
if (styleClass?.textStyles) {
|
||||
styleClass.textStyles.push(s);
|
||||
} else {
|
||||
styleClass.textStyles = [s];
|
||||
}
|
||||
}
|
||||
if (styleClass?.styles) {
|
||||
styleClass.styles.push(s);
|
||||
} else {
|
||||
styleClass.styles = [s];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
classes.set(id, styleClass);
|
||||
};
|
||||
const getClasses = (): Map<string, DiagramStyleClassDef> => {
|
||||
return state.records.classes;
|
||||
};
|
||||
|
||||
const getStylesForClass = (classSelector: string): string[] => {
|
||||
return state.records.classes.get(classSelector)?.styles ?? [];
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
commonClear();
|
||||
state.reset();
|
||||
};
|
||||
|
||||
export const db: TreemapDB = {
|
||||
getNodes,
|
||||
addNode,
|
||||
getRoot,
|
||||
getConfig,
|
||||
clear,
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
addClass,
|
||||
getClasses,
|
||||
getStylesForClass,
|
||||
};
|
22
packages/mermaid/src/diagrams/treemap/detector.ts
Normal file
22
packages/mermaid/src/diagrams/treemap/detector.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type {
|
||||
DiagramDetector,
|
||||
DiagramLoader,
|
||||
ExternalDiagramDefinition,
|
||||
} from '../../diagram-api/types.js';
|
||||
|
||||
const id = 'treemap';
|
||||
|
||||
const detector: DiagramDetector = (txt) => {
|
||||
return /^\s*treemap/.test(txt);
|
||||
};
|
||||
|
||||
const loader: DiagramLoader = async () => {
|
||||
const { diagram } = await import('./diagram.js');
|
||||
return { id, diagram };
|
||||
};
|
||||
|
||||
export const treemap: ExternalDiagramDefinition = {
|
||||
id,
|
||||
detector,
|
||||
loader,
|
||||
};
|
12
packages/mermaid/src/diagrams/treemap/diagram.ts
Normal file
12
packages/mermaid/src/diagrams/treemap/diagram.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
import { db } from './db.js';
|
||||
import { parser } from './parser.js';
|
||||
import { renderer } from './renderer.js';
|
||||
import styles from './styles.js';
|
||||
|
||||
export const diagram: DiagramDefinition = {
|
||||
parser,
|
||||
db,
|
||||
renderer,
|
||||
styles,
|
||||
};
|
100
packages/mermaid/src/diagrams/treemap/parser.ts
Normal file
100
packages/mermaid/src/diagrams/treemap/parser.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { parse } from '@mermaid-js/parser';
|
||||
import type { ParserDefinition } from '../../diagram-api/types.js';
|
||||
import { log } from '../../logger.js';
|
||||
import { populateCommonDb } from '../common/populateCommonDb.js';
|
||||
import { db } from './db.js';
|
||||
import type { TreemapNode, TreemapAst } from './types.js';
|
||||
import { buildHierarchy } from './utils.js';
|
||||
|
||||
/**
|
||||
* Populates the database with data from the Treemap AST
|
||||
* @param ast - The Treemap AST
|
||||
*/
|
||||
const populate = (ast: TreemapAst) => {
|
||||
// We need to bypass the type checking for populateCommonDb
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
populateCommonDb(ast as any, db);
|
||||
|
||||
const items: {
|
||||
level: number;
|
||||
name: string;
|
||||
type: string;
|
||||
value?: number;
|
||||
classSelector?: string;
|
||||
cssCompiledStyles?: string;
|
||||
}[] = [];
|
||||
|
||||
// Extract classes and styles from the treemap
|
||||
for (const row of ast.TreemapRows ?? []) {
|
||||
if (row.$type === 'ClassDefStatement') {
|
||||
db.addClass(row.className ?? '', row.styleText ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// Extract data from each row in the treemap
|
||||
for (const row of ast.TreemapRows ?? []) {
|
||||
const item = row.item;
|
||||
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const level = row.indent ? parseInt(row.indent) : 0;
|
||||
const name = getItemName(item);
|
||||
|
||||
// Get styles as a string if they exist
|
||||
const styles = item.classSelector ? db.getStylesForClass(item.classSelector) : [];
|
||||
const cssCompiledStyles = styles.length > 0 ? styles.join(';') : undefined;
|
||||
|
||||
const itemData = {
|
||||
level,
|
||||
name,
|
||||
type: item.$type,
|
||||
value: item.value,
|
||||
classSelector: item.classSelector,
|
||||
cssCompiledStyles,
|
||||
};
|
||||
|
||||
items.push(itemData);
|
||||
}
|
||||
|
||||
// Convert flat structure to hierarchical
|
||||
const hierarchyNodes = buildHierarchy(items);
|
||||
|
||||
// Add all nodes to the database
|
||||
const addNodesRecursively = (nodes: TreemapNode[], level: number) => {
|
||||
for (const node of nodes) {
|
||||
db.addNode(node, level);
|
||||
if (node.children && node.children.length > 0) {
|
||||
addNodesRecursively(node.children, level + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addNodesRecursively(hierarchyNodes, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the name of a treemap item
|
||||
* @param item - The treemap item
|
||||
* @returns The name of the item
|
||||
*/
|
||||
const getItemName = (item: { name?: string | number }): string => {
|
||||
return item.name ? String(item.name) : '';
|
||||
};
|
||||
|
||||
export const parser: ParserDefinition = {
|
||||
parse: async (text: string): Promise<void> => {
|
||||
try {
|
||||
// Use a generic parse that accepts any diagram type
|
||||
|
||||
const parseFunc = parse as (diagramType: string, text: string) => Promise<TreemapAst>;
|
||||
const ast = await parseFunc('treemap', text);
|
||||
log.debug('Treemap AST:', ast);
|
||||
populate(ast);
|
||||
} catch (error) {
|
||||
log.error('Error parsing treemap:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
};
|
526
packages/mermaid/src/diagrams/treemap/renderer.ts
Normal file
526
packages/mermaid/src/diagrams/treemap/renderer.ts
Normal file
@@ -0,0 +1,526 @@
|
||||
import type { Diagram } from '../../Diagram.js';
|
||||
import type {
|
||||
DiagramRenderer,
|
||||
DiagramStyleClassDef,
|
||||
DrawDefinition,
|
||||
} from '../../diagram-api/types.js';
|
||||
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
|
||||
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import type { TreemapDB, TreemapNode } from './types.js';
|
||||
import { scaleOrdinal, treemap, hierarchy, format, select } from 'd3';
|
||||
import { styles2String } from '../../rendering-util/rendering-elements/shapes/handDrawnShapeStyles.js';
|
||||
import { getConfig } from '../../config.js';
|
||||
import { log } from '../../logger.js';
|
||||
import type { Node } from '../../rendering-util/types.js';
|
||||
|
||||
const DEFAULT_INNER_PADDING = 10; // Default for inner padding between cells/sections
|
||||
const SECTION_INNER_PADDING = 10; // Default for inner padding between cells/sections
|
||||
const SECTION_HEADER_HEIGHT = 25;
|
||||
|
||||
/**
|
||||
* Draws the treemap diagram
|
||||
*/
|
||||
const draw: DrawDefinition = (_text, id, _version, diagram: Diagram) => {
|
||||
const treemapDb = diagram.db as TreemapDB;
|
||||
const config = treemapDb.getConfig();
|
||||
const treemapInnerPadding = config.padding ?? DEFAULT_INNER_PADDING;
|
||||
const title = treemapDb.getDiagramTitle();
|
||||
const root = treemapDb.getRoot();
|
||||
const { themeVariables } = getConfig();
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Define dimensions
|
||||
const titleHeight = title ? 30 : 0;
|
||||
|
||||
const svg = selectSvgElement(id);
|
||||
// Use config dimensions or defaults
|
||||
const width = config.nodeWidth ? config.nodeWidth * SECTION_INNER_PADDING : 960;
|
||||
const height = config.nodeHeight ? config.nodeHeight * SECTION_INNER_PADDING : 500;
|
||||
|
||||
const svgWidth = width;
|
||||
const svgHeight = height + titleHeight;
|
||||
|
||||
// Set the SVG size
|
||||
svg.attr('viewBox', `0 0 ${svgWidth} ${svgHeight}`);
|
||||
configureSvgSize(svg, svgHeight, svgWidth, config.useMaxWidth);
|
||||
|
||||
// Format for displaying values
|
||||
let valueFormat;
|
||||
try {
|
||||
// Handle special format patterns
|
||||
const formatStr = config.valueFormat || ',';
|
||||
|
||||
// Handle special cases that aren't directly supported by D3 format
|
||||
if (formatStr === '$0,0') {
|
||||
// Currency with thousands separator
|
||||
valueFormat = (value: number) => '$' + format(',')(value);
|
||||
} else if (formatStr.startsWith('$') && formatStr.includes(',')) {
|
||||
// Other dollar formats with commas
|
||||
const precision = /\.\d+/.exec(formatStr);
|
||||
const precisionStr = precision ? precision[0] : '';
|
||||
valueFormat = (value: number) => '$' + format(',' + precisionStr)(value);
|
||||
} else if (formatStr.startsWith('$')) {
|
||||
// Simple dollar sign prefix
|
||||
const restOfFormat = formatStr.substring(1);
|
||||
valueFormat = (value: number) => '$' + format(restOfFormat || '')(value);
|
||||
} else {
|
||||
// Standard D3 format
|
||||
valueFormat = format(formatStr);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('Error creating format function:', error);
|
||||
// Fallback to default format
|
||||
valueFormat = format(',');
|
||||
}
|
||||
|
||||
// Create color scale
|
||||
const colorScale = scaleOrdinal<string>().range([
|
||||
'transparent',
|
||||
themeVariables.cScale0,
|
||||
themeVariables.cScale1,
|
||||
themeVariables.cScale2,
|
||||
themeVariables.cScale3,
|
||||
themeVariables.cScale4,
|
||||
themeVariables.cScale5,
|
||||
themeVariables.cScale6,
|
||||
themeVariables.cScale7,
|
||||
themeVariables.cScale8,
|
||||
themeVariables.cScale9,
|
||||
themeVariables.cScale10,
|
||||
themeVariables.cScale11,
|
||||
]);
|
||||
const colorScalePeer = scaleOrdinal<string>().range([
|
||||
'transparent',
|
||||
themeVariables.cScalePeer0,
|
||||
themeVariables.cScalePeer1,
|
||||
themeVariables.cScalePeer2,
|
||||
themeVariables.cScalePeer3,
|
||||
themeVariables.cScalePeer4,
|
||||
themeVariables.cScalePeer5,
|
||||
themeVariables.cScalePeer6,
|
||||
themeVariables.cScalePeer7,
|
||||
themeVariables.cScalePeer8,
|
||||
themeVariables.cScalePeer9,
|
||||
themeVariables.cScalePeer10,
|
||||
themeVariables.cScalePeer11,
|
||||
]);
|
||||
const colorScaleLabel = scaleOrdinal<string>().range([
|
||||
themeVariables.cScaleLabel0,
|
||||
themeVariables.cScaleLabel1,
|
||||
themeVariables.cScaleLabel2,
|
||||
themeVariables.cScaleLabel3,
|
||||
themeVariables.cScaleLabel4,
|
||||
themeVariables.cScaleLabel5,
|
||||
themeVariables.cScaleLabel6,
|
||||
themeVariables.cScaleLabel7,
|
||||
themeVariables.cScaleLabel8,
|
||||
themeVariables.cScaleLabel9,
|
||||
themeVariables.cScaleLabel10,
|
||||
themeVariables.cScaleLabel11,
|
||||
]);
|
||||
|
||||
// Draw the title if it exists
|
||||
if (title) {
|
||||
svg
|
||||
.append('text')
|
||||
.attr('x', svgWidth / 2)
|
||||
.attr('y', titleHeight / 2)
|
||||
.attr('class', 'treemapTitle')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.text(title);
|
||||
}
|
||||
|
||||
// Create a main container for the treemap, translated below the title
|
||||
const g = svg
|
||||
.append('g')
|
||||
.attr('transform', `translate(0, ${titleHeight})`)
|
||||
.attr('class', 'treemapContainer');
|
||||
|
||||
// Create the hierarchical structure
|
||||
const hierarchyRoot = hierarchy<TreemapNode>(root)
|
||||
.sum((d) => d.value ?? 0)
|
||||
.sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
|
||||
|
||||
// Create treemap layout
|
||||
const treemapLayout = treemap<TreemapNode>()
|
||||
.size([width, height])
|
||||
.paddingTop((d) =>
|
||||
d.children && d.children.length > 0 ? SECTION_HEADER_HEIGHT + SECTION_INNER_PADDING : 0
|
||||
)
|
||||
.paddingInner(treemapInnerPadding)
|
||||
.paddingLeft((d) => (d.children && d.children.length > 0 ? SECTION_INNER_PADDING : 0))
|
||||
.paddingRight((d) => (d.children && d.children.length > 0 ? SECTION_INNER_PADDING : 0))
|
||||
.paddingBottom((d) => (d.children && d.children.length > 0 ? SECTION_INNER_PADDING : 0))
|
||||
.round(true);
|
||||
|
||||
// Apply the treemap layout to the hierarchy
|
||||
const treemapData = treemapLayout(hierarchyRoot);
|
||||
|
||||
// Draw section nodes (branches - nodes with children)
|
||||
const branchNodes = treemapData.descendants().filter((d) => d.children && d.children.length > 0);
|
||||
const sections = g
|
||||
.selectAll('.treemapSection')
|
||||
.data(branchNodes)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'treemapSection')
|
||||
.attr('transform', (d) => `translate(${d.x0},${d.y0})`);
|
||||
|
||||
// Add section header background
|
||||
sections
|
||||
.append('rect')
|
||||
.attr('width', (d) => d.x1 - d.x0)
|
||||
.attr('height', SECTION_HEADER_HEIGHT)
|
||||
.attr('class', 'treemapSectionHeader')
|
||||
.attr('fill', 'none')
|
||||
.attr('fill-opacity', 0.6)
|
||||
.attr('stroke-width', 0.6)
|
||||
.attr('style', (d) => {
|
||||
// Hide the label for the root section
|
||||
if (d.depth === 0) {
|
||||
return 'display: none;';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
// Add clip paths for section headers to prevent text overflow
|
||||
sections
|
||||
.append('clipPath')
|
||||
.attr('id', (_d, i) => `clip-section-${id}-${i}`)
|
||||
.append('rect')
|
||||
.attr('width', (d) => Math.max(0, d.x1 - d.x0 - 12)) // 6px padding on each side
|
||||
.attr('height', SECTION_HEADER_HEIGHT);
|
||||
|
||||
sections
|
||||
.append('rect')
|
||||
.attr('width', (d) => d.x1 - d.x0)
|
||||
.attr('height', (d) => d.y1 - d.y0)
|
||||
.attr('class', (_d, i) => {
|
||||
return `treemapSection section${i}`;
|
||||
})
|
||||
.attr('fill', (d) => colorScale(d.data.name))
|
||||
.attr('fill-opacity', 0.6)
|
||||
.attr('stroke', (d) => colorScalePeer(d.data.name))
|
||||
.attr('stroke-width', 2.0)
|
||||
.attr('stroke-opacity', 0.4)
|
||||
.attr('style', (d) => {
|
||||
// Hide the label for the root section
|
||||
if (d.depth === 0) {
|
||||
return 'display: none;';
|
||||
}
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return styles.nodeStyles + ';' + styles.borderStyles.join(';');
|
||||
});
|
||||
// Add section labels
|
||||
sections
|
||||
.append('text')
|
||||
.attr('class', 'treemapSectionLabel')
|
||||
.attr('x', 6) // Keep original left padding
|
||||
.attr('y', SECTION_HEADER_HEIGHT / 2)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.text((d) => (d.depth === 0 ? '' : d.data.name)) // Skip label for root section
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('style', (d) => {
|
||||
// Hide the label for the root section
|
||||
if (d.depth === 0) {
|
||||
return 'display: none;';
|
||||
}
|
||||
const labelStyles =
|
||||
'dominant-baseline: middle; font-size: 12px; fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
'; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return labelStyles + styles.labelStyles.replace('color:', 'fill:');
|
||||
})
|
||||
.each(function (d) {
|
||||
// Skip processing for root section
|
||||
if (d.depth === 0) {
|
||||
return;
|
||||
}
|
||||
const self = select(this);
|
||||
const originalText = d.data.name;
|
||||
self.text(originalText);
|
||||
const totalHeaderWidth = d.x1 - d.x0;
|
||||
const labelXPosition = 6;
|
||||
let spaceForTextContent;
|
||||
if (config.showValues !== false && d.value) {
|
||||
const valueEndsAtXRelative = totalHeaderWidth - 10;
|
||||
const estimatedValueTextActualWidth = 30;
|
||||
const gapBetweenLabelAndValue = 10;
|
||||
const labelMustEndBeforeX =
|
||||
valueEndsAtXRelative - estimatedValueTextActualWidth - gapBetweenLabelAndValue;
|
||||
spaceForTextContent = labelMustEndBeforeX - labelXPosition;
|
||||
} else {
|
||||
const labelOwnRightPadding = 6;
|
||||
spaceForTextContent = totalHeaderWidth - labelXPosition - labelOwnRightPadding;
|
||||
}
|
||||
const minimumWidthToDisplay = 15;
|
||||
const actualAvailableWidth = Math.max(minimumWidthToDisplay, spaceForTextContent);
|
||||
const textNode = self.node()!;
|
||||
const currentTextContentLength = textNode.getComputedTextLength();
|
||||
if (currentTextContentLength > actualAvailableWidth) {
|
||||
const ellipsis = '...';
|
||||
let currentTruncatedText = originalText;
|
||||
while (currentTruncatedText.length > 0) {
|
||||
currentTruncatedText = originalText.substring(0, currentTruncatedText.length - 1);
|
||||
if (currentTruncatedText.length === 0) {
|
||||
self.text(ellipsis);
|
||||
if (textNode.getComputedTextLength() > actualAvailableWidth) {
|
||||
self.text('');
|
||||
}
|
||||
break;
|
||||
}
|
||||
self.text(currentTruncatedText + ellipsis);
|
||||
if (textNode.getComputedTextLength() <= actualAvailableWidth) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Add section values if enabled
|
||||
if (config.showValues !== false) {
|
||||
sections
|
||||
.append('text')
|
||||
.attr('class', 'treemapSectionValue')
|
||||
.attr('x', (d) => d.x1 - d.x0 - 10)
|
||||
.attr('y', SECTION_HEADER_HEIGHT / 2)
|
||||
.attr('text-anchor', 'end')
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.text((d) => (d.value ? valueFormat(d.value) : ''))
|
||||
.attr('font-style', 'italic')
|
||||
.attr('style', (d) => {
|
||||
// Hide the value for the root section
|
||||
if (d.depth === 0) {
|
||||
return 'display: none;';
|
||||
}
|
||||
const labelStyles =
|
||||
'text-anchor: end; dominant-baseline: middle; font-size: 10px; fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
'; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return labelStyles + styles.labelStyles.replace('color:', 'fill:');
|
||||
});
|
||||
}
|
||||
|
||||
// Draw the leaf nodes
|
||||
const leafNodes = treemapData.leaves();
|
||||
const cell = g
|
||||
.selectAll('.treemapLeafGroup')
|
||||
.data(leafNodes)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', (d, i) => {
|
||||
return `treemapNode treemapLeafGroup leaf${i}${d.data.classSelector ? ` ${d.data.classSelector}` : ''}x`;
|
||||
})
|
||||
.attr('transform', (d) => `translate(${d.x0},${d.y0})`);
|
||||
|
||||
// Add rectangle for each leaf node
|
||||
cell
|
||||
.append('rect')
|
||||
.attr('width', (d) => d.x1 - d.x0)
|
||||
.attr('height', (d) => d.y1 - d.y0)
|
||||
.attr('class', 'treemapLeaf')
|
||||
.attr('fill', (d) => {
|
||||
// Leaves inherit color from their immediate parent section's name.
|
||||
// If a leaf is the root itself (no parent), it uses its own name.
|
||||
return d.parent ? colorScale(d.parent.data.name) : colorScale(d.data.name);
|
||||
})
|
||||
.attr('style', (d) => {
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return styles.nodeStyles;
|
||||
})
|
||||
.attr('fill-opacity', 0.3)
|
||||
.attr('stroke', (d) => {
|
||||
// Leaves inherit color from their immediate parent section's name.
|
||||
// If a leaf is the root itself (no parent), it uses its own name.
|
||||
return d.parent ? colorScale(d.parent.data.name) : colorScale(d.data.name);
|
||||
})
|
||||
.attr('stroke-width', 3.0);
|
||||
|
||||
// Add clip paths to prevent text from extending outside nodes
|
||||
cell
|
||||
.append('clipPath')
|
||||
.attr('id', (_d, i) => `clip-${id}-${i}`)
|
||||
.append('rect')
|
||||
.attr('width', (d) => Math.max(0, d.x1 - d.x0 - 4))
|
||||
.attr('height', (d) => Math.max(0, d.y1 - d.y0 - 4));
|
||||
|
||||
// Add node labels with clipping
|
||||
const leafLabels = cell
|
||||
.append('text')
|
||||
.attr('class', 'treemapLabel')
|
||||
.attr('x', (d) => (d.x1 - d.x0) / 2)
|
||||
.attr('y', (d) => (d.y1 - d.y0) / 2)
|
||||
// .style('fill', (d) => colorScaleLabel(d.data.name))
|
||||
.attr('style', (d) => {
|
||||
const labelStyles =
|
||||
'text-anchor: middle; dominant-baseline: middle; font-size: 38px;fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
';';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return labelStyles + styles.labelStyles.replace('color:', 'fill:');
|
||||
})
|
||||
.attr('clip-path', (_d, i) => `url(#clip-${id}-${i})`)
|
||||
.text((d) => d.data.name);
|
||||
|
||||
leafLabels.each(function (d) {
|
||||
const self = select(this);
|
||||
const nodeWidth = d.x1 - d.x0;
|
||||
const nodeHeight = d.y1 - d.y0;
|
||||
const textNode = self.node()!;
|
||||
|
||||
const padding = 4;
|
||||
const availableWidth = nodeWidth - 2 * padding;
|
||||
const availableHeight = nodeHeight - 2 * padding;
|
||||
|
||||
if (availableWidth < 10 || availableHeight < 10) {
|
||||
self.style('display', 'none');
|
||||
return;
|
||||
}
|
||||
|
||||
let currentLabelFontSize = parseInt(self.style('font-size'), 10);
|
||||
const minLabelFontSize = 8;
|
||||
const originalValueRelFontSize = 28; // Original font size of value, for max cap
|
||||
const valueScaleFactor = 0.6; // Value font size as a factor of label font size
|
||||
const minValueFontSize = 6;
|
||||
const spacingBetweenLabelAndValue = 2;
|
||||
|
||||
// 1. Adjust label font size to fit width
|
||||
while (
|
||||
textNode.getComputedTextLength() > availableWidth &&
|
||||
currentLabelFontSize > minLabelFontSize
|
||||
) {
|
||||
currentLabelFontSize--;
|
||||
self.style('font-size', `${currentLabelFontSize}px`);
|
||||
}
|
||||
|
||||
// 2. Adjust both label and prospective value font size to fit combined height
|
||||
let prospectiveValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(originalValueRelFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
let combinedHeight =
|
||||
currentLabelFontSize + spacingBetweenLabelAndValue + prospectiveValueFontSize;
|
||||
|
||||
while (combinedHeight > availableHeight && currentLabelFontSize > minLabelFontSize) {
|
||||
currentLabelFontSize--;
|
||||
prospectiveValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(originalValueRelFontSize, Math.round(currentLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
if (
|
||||
prospectiveValueFontSize < minValueFontSize &&
|
||||
currentLabelFontSize === minLabelFontSize
|
||||
) {
|
||||
break;
|
||||
} // Avoid shrinking label if value is already at min
|
||||
self.style('font-size', `${currentLabelFontSize}px`);
|
||||
combinedHeight =
|
||||
currentLabelFontSize + spacingBetweenLabelAndValue + prospectiveValueFontSize;
|
||||
if (prospectiveValueFontSize <= minValueFontSize && combinedHeight > availableHeight) {
|
||||
// If value is at min and still doesn't fit, label might need to shrink more alone
|
||||
// This might lead to label being too small for its own text, checked next
|
||||
}
|
||||
}
|
||||
|
||||
// Update label font size based on height adjustment
|
||||
self.style('font-size', `${currentLabelFontSize}px`);
|
||||
|
||||
// 3. Final visibility check for the label
|
||||
if (
|
||||
textNode.getComputedTextLength() > availableWidth ||
|
||||
currentLabelFontSize < minLabelFontSize ||
|
||||
availableHeight < currentLabelFontSize
|
||||
) {
|
||||
self.style('display', 'none');
|
||||
// If label is hidden, value will be hidden by its own .each() loop
|
||||
}
|
||||
});
|
||||
|
||||
// Add node values with clipping
|
||||
if (config.showValues !== false) {
|
||||
const leafValues = cell
|
||||
.append('text')
|
||||
.attr('class', 'treemapValue')
|
||||
.attr('x', (d) => (d.x1 - d.x0) / 2)
|
||||
.attr('y', function (d) {
|
||||
// Y position calculated dynamically in leafValues.each based on final label metrics
|
||||
return (d.y1 - d.y0) / 2; // Placeholder, will be overwritten
|
||||
})
|
||||
.attr('style', (d) => {
|
||||
const labelStyles =
|
||||
'text-anchor: middle; dominant-baseline: hanging; font-size: 28px;fill:' +
|
||||
colorScaleLabel(d.data.name) +
|
||||
';';
|
||||
const styles = styles2String({ cssCompiledStyles: d.data.cssCompiledStyles } as Node);
|
||||
return labelStyles + styles.labelStyles.replace('color:', 'fill:');
|
||||
})
|
||||
|
||||
.attr('clip-path', (_d, i) => `url(#clip-${id}-${i})`)
|
||||
.text((d) => (d.value ? valueFormat(d.value) : ''));
|
||||
|
||||
leafValues.each(function (d) {
|
||||
const valueTextElement = select(this);
|
||||
const parentCellNode = this.parentNode as SVGGElement | null;
|
||||
|
||||
if (!parentCellNode) {
|
||||
valueTextElement.style('display', 'none');
|
||||
return;
|
||||
}
|
||||
|
||||
const labelElement = select(parentCellNode).select<SVGTextElement>('.treemapLabel');
|
||||
|
||||
if (labelElement.empty() || labelElement.style('display') === 'none') {
|
||||
valueTextElement.style('display', 'none');
|
||||
return;
|
||||
}
|
||||
|
||||
const finalLabelFontSize = parseFloat(labelElement.style('font-size'));
|
||||
const originalValueFontSize = 28; // From initial style setting
|
||||
const valueScaleFactor = 0.6;
|
||||
const minValueFontSize = 6;
|
||||
const spacingBetweenLabelAndValue = 2;
|
||||
|
||||
const actualValueFontSize = Math.max(
|
||||
minValueFontSize,
|
||||
Math.min(originalValueFontSize, Math.round(finalLabelFontSize * valueScaleFactor))
|
||||
);
|
||||
valueTextElement.style('font-size', `${actualValueFontSize}px`);
|
||||
|
||||
const labelCenterY = (d.y1 - d.y0) / 2;
|
||||
const valueTopActualY = labelCenterY + finalLabelFontSize / 2 + spacingBetweenLabelAndValue;
|
||||
valueTextElement.attr('y', valueTopActualY);
|
||||
|
||||
const nodeWidth = d.x1 - d.x0;
|
||||
const nodeTotalHeight = d.y1 - d.y0;
|
||||
const cellBottomPadding = 4;
|
||||
const maxValueBottomY = nodeTotalHeight - cellBottomPadding;
|
||||
const availableWidthForValue = nodeWidth - 2 * 4; // padding for value text
|
||||
|
||||
if (
|
||||
valueTextElement.node()!.getComputedTextLength() > availableWidthForValue ||
|
||||
valueTopActualY + actualValueFontSize > maxValueBottomY ||
|
||||
actualValueFontSize < minValueFontSize
|
||||
) {
|
||||
valueTextElement.style('display', 'none');
|
||||
} else {
|
||||
valueTextElement.style('display', null);
|
||||
}
|
||||
});
|
||||
}
|
||||
const diagramPadding = config.diagramPadding ?? 8;
|
||||
setupViewPortForSVG(svg, diagramPadding, 'flowchart', config?.useMaxWidth || false);
|
||||
};
|
||||
|
||||
const getClasses = function (
|
||||
_text: string,
|
||||
diagramObj: Pick<Diagram, 'db'>
|
||||
): Map<string, DiagramStyleClassDef> {
|
||||
return (diagramObj.db as TreemapDB).getClasses();
|
||||
};
|
||||
export const renderer: DiagramRenderer = { draw, getClasses };
|
51
packages/mermaid/src/diagrams/treemap/styles.ts
Normal file
51
packages/mermaid/src/diagrams/treemap/styles.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
|
||||
import { cleanAndMerge } from '../../utils.js';
|
||||
import type { TreemapStyleOptions } from './types.js';
|
||||
|
||||
const defaultTreemapStyleOptions: TreemapStyleOptions = {
|
||||
sectionStrokeColor: 'black',
|
||||
sectionStrokeWidth: '1',
|
||||
sectionFillColor: '#efefef',
|
||||
leafStrokeColor: 'black',
|
||||
leafStrokeWidth: '1',
|
||||
leafFillColor: '#efefef',
|
||||
labelColor: 'black',
|
||||
labelFontSize: '12px',
|
||||
valueFontSize: '10px',
|
||||
valueColor: 'black',
|
||||
titleColor: 'black',
|
||||
titleFontSize: '14px',
|
||||
};
|
||||
|
||||
export const getStyles: DiagramStylesProvider = ({
|
||||
treemap,
|
||||
}: { treemap?: TreemapStyleOptions } = {}) => {
|
||||
const options = cleanAndMerge(defaultTreemapStyleOptions, treemap);
|
||||
|
||||
return `
|
||||
.treemapNode.section {
|
||||
stroke: ${options.sectionStrokeColor};
|
||||
stroke-width: ${options.sectionStrokeWidth};
|
||||
fill: ${options.sectionFillColor};
|
||||
}
|
||||
.treemapNode.leaf {
|
||||
stroke: ${options.leafStrokeColor};
|
||||
stroke-width: ${options.leafStrokeWidth};
|
||||
fill: ${options.leafFillColor};
|
||||
}
|
||||
.treemapLabel {
|
||||
fill: ${options.labelColor};
|
||||
font-size: ${options.labelFontSize};
|
||||
}
|
||||
.treemapValue {
|
||||
fill: ${options.valueColor};
|
||||
font-size: ${options.valueFontSize};
|
||||
}
|
||||
.treemapTitle {
|
||||
fill: ${options.titleColor};
|
||||
font-size: ${options.titleFontSize};
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
export default getStyles;
|
80
packages/mermaid/src/diagrams/treemap/types.ts
Normal file
80
packages/mermaid/src/diagrams/treemap/types.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import type { DiagramDBBase, DiagramStyleClassDef } from '../../diagram-api/types.js';
|
||||
import type { BaseDiagramConfig } from '../../config.type.js';
|
||||
|
||||
export interface TreemapNode {
|
||||
name: string;
|
||||
children?: TreemapNode[];
|
||||
value?: number;
|
||||
parent?: TreemapNode;
|
||||
classSelector?: string;
|
||||
cssCompiledStyles?: string[];
|
||||
}
|
||||
|
||||
export interface TreemapDB extends DiagramDBBase<TreemapDiagramConfig> {
|
||||
getNodes: () => TreemapNode[];
|
||||
addNode: (node: TreemapNode, level: number) => void;
|
||||
getRoot: () => TreemapNode | undefined;
|
||||
getClasses: () => Map<string, DiagramStyleClassDef>;
|
||||
addClass: (className: string, style: string) => void;
|
||||
getStylesForClass: (classSelector: string) => string[];
|
||||
}
|
||||
|
||||
export interface TreemapStyleOptions {
|
||||
sectionStrokeColor?: string;
|
||||
sectionStrokeWidth?: string;
|
||||
sectionFillColor?: string;
|
||||
leafStrokeColor?: string;
|
||||
leafStrokeWidth?: string;
|
||||
leafFillColor?: string;
|
||||
labelColor?: string;
|
||||
labelFontSize?: string;
|
||||
valueFontSize?: string;
|
||||
valueColor?: string;
|
||||
titleColor?: string;
|
||||
titleFontSize?: string;
|
||||
}
|
||||
|
||||
export interface TreemapData {
|
||||
nodes: TreemapNode[];
|
||||
levels: Map<TreemapNode, number>;
|
||||
root?: TreemapNode;
|
||||
outerNodes: TreemapNode[];
|
||||
classes: Map<string, DiagramStyleClassDef>;
|
||||
}
|
||||
|
||||
export interface TreemapItem {
|
||||
$type: string;
|
||||
name: string;
|
||||
value?: number;
|
||||
classSelector?: string;
|
||||
}
|
||||
|
||||
export interface TreemapRow {
|
||||
$type: string;
|
||||
indent?: string;
|
||||
item?: TreemapItem;
|
||||
className?: string;
|
||||
styleText?: string;
|
||||
}
|
||||
|
||||
export interface TreemapAst {
|
||||
TreemapRows?: TreemapRow[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
accDescription?: string;
|
||||
accTitle?: string;
|
||||
diagramTitle?: string;
|
||||
}
|
||||
|
||||
// Define the TreemapDiagramConfig interface
|
||||
export interface TreemapDiagramConfig extends BaseDiagramConfig {
|
||||
padding?: number;
|
||||
diagramPadding?: number;
|
||||
showValues?: boolean;
|
||||
nodeWidth?: number;
|
||||
nodeHeight?: number;
|
||||
borderWidth?: number;
|
||||
valueFontSize?: number;
|
||||
labelFontSize?: number;
|
||||
valueFormat?: string;
|
||||
}
|
100
packages/mermaid/src/diagrams/treemap/utils.test.ts
Normal file
100
packages/mermaid/src/diagrams/treemap/utils.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildHierarchy } from './utils.js';
|
||||
import type { TreemapNode } from './types.js';
|
||||
|
||||
describe('treemap utilities', () => {
|
||||
describe('buildHierarchy', () => {
|
||||
it('should convert a flat array into a hierarchical structure', () => {
|
||||
// Input flat structure
|
||||
const flatItems = [
|
||||
{ level: 0, name: 'Root', type: 'Section' },
|
||||
{ level: 4, name: 'Branch 1', type: 'Section' },
|
||||
{ level: 8, name: 'Leaf 1.1', type: 'Leaf', value: 10 },
|
||||
{ level: 8, name: 'Leaf 1.2', type: 'Leaf', value: 15 },
|
||||
{ level: 4, name: 'Branch 2', type: 'Section' },
|
||||
{ level: 8, name: 'Leaf 2.1', type: 'Leaf', value: 20 },
|
||||
{ level: 8, name: 'Leaf 2.2', type: 'Leaf', value: 25 },
|
||||
{ level: 8, name: 'Leaf 2.3', type: 'Leaf', value: 30 },
|
||||
];
|
||||
|
||||
// Expected hierarchical structure
|
||||
const expectedHierarchy: TreemapNode[] = [
|
||||
{
|
||||
name: 'Root',
|
||||
children: [
|
||||
{
|
||||
name: 'Branch 1',
|
||||
children: [
|
||||
{ name: 'Leaf 1.1', value: 10 },
|
||||
{ name: 'Leaf 1.2', value: 15 },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Branch 2',
|
||||
children: [
|
||||
{ name: 'Leaf 2.1', value: 20 },
|
||||
{ name: 'Leaf 2.2', value: 25 },
|
||||
{ name: 'Leaf 2.3', value: 30 },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const result = buildHierarchy(flatItems);
|
||||
expect(result).toEqual(expectedHierarchy);
|
||||
});
|
||||
|
||||
it('should handle empty input', () => {
|
||||
expect(buildHierarchy([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle only root nodes', () => {
|
||||
const flatItems = [
|
||||
{ level: 0, name: 'Root 1', type: 'Section' },
|
||||
{ level: 0, name: 'Root 2', type: 'Section' },
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{ name: 'Root 1', children: [] },
|
||||
{ name: 'Root 2', children: [] },
|
||||
];
|
||||
|
||||
expect(buildHierarchy(flatItems)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle complex nesting levels', () => {
|
||||
const flatItems = [
|
||||
{ level: 0, name: 'Root', type: 'Section' },
|
||||
{ level: 2, name: 'Level 1', type: 'Section' },
|
||||
{ level: 4, name: 'Level 2', type: 'Section' },
|
||||
{ level: 6, name: 'Leaf 1', type: 'Leaf', value: 10 },
|
||||
{ level: 4, name: 'Level 2 again', type: 'Section' },
|
||||
{ level: 6, name: 'Leaf 2', type: 'Leaf', value: 20 },
|
||||
];
|
||||
|
||||
const expected = [
|
||||
{
|
||||
name: 'Root',
|
||||
children: [
|
||||
{
|
||||
name: 'Level 1',
|
||||
children: [
|
||||
{
|
||||
name: 'Level 2',
|
||||
children: [{ name: 'Leaf 1', value: 10 }],
|
||||
},
|
||||
{
|
||||
name: 'Level 2 again',
|
||||
children: [{ name: 'Leaf 2', value: 20 }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
expect(buildHierarchy(flatItems)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
64
packages/mermaid/src/diagrams/treemap/utils.ts
Normal file
64
packages/mermaid/src/diagrams/treemap/utils.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { TreemapNode } from './types.js';
|
||||
|
||||
/**
|
||||
* Converts a flat array of treemap items into a hierarchical structure
|
||||
* @param items - Array of flat treemap items with level, name, type, and optional value
|
||||
* @returns A hierarchical tree structure
|
||||
*/
|
||||
export function buildHierarchy(
|
||||
items: {
|
||||
level: number;
|
||||
name: string;
|
||||
type: string;
|
||||
value?: number;
|
||||
classSelector?: string;
|
||||
cssCompiledStyles?: string;
|
||||
}[]
|
||||
): TreemapNode[] {
|
||||
if (!items.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const root: TreemapNode[] = [];
|
||||
const stack: { node: TreemapNode; level: number }[] = [];
|
||||
|
||||
items.forEach((item) => {
|
||||
const node: TreemapNode = {
|
||||
name: item.name,
|
||||
children: item.type === 'Leaf' ? undefined : [],
|
||||
};
|
||||
node.classSelector = item?.classSelector;
|
||||
if (item?.cssCompiledStyles) {
|
||||
node.cssCompiledStyles = [item.cssCompiledStyles];
|
||||
}
|
||||
|
||||
if (item.type === 'Leaf' && item.value !== undefined) {
|
||||
node.value = item.value;
|
||||
}
|
||||
|
||||
// Find the right parent for this node
|
||||
while (stack.length > 0 && stack[stack.length - 1].level >= item.level) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
if (stack.length === 0) {
|
||||
// This is a root node
|
||||
root.push(node);
|
||||
} else {
|
||||
// Add as child to the parent
|
||||
const parent = stack[stack.length - 1].node;
|
||||
if (parent.children) {
|
||||
parent.children.push(node);
|
||||
} else {
|
||||
parent.children = [node];
|
||||
}
|
||||
}
|
||||
|
||||
// Only add to stack if it can have children
|
||||
if (item.type !== 'Leaf') {
|
||||
stack.push({ node, level: item.level });
|
||||
}
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
@@ -179,6 +179,7 @@ function sidebarSyntax() {
|
||||
{ text: 'Kanban 🔥', link: '/syntax/kanban' },
|
||||
{ text: 'Architecture 🔥', link: '/syntax/architecture' },
|
||||
{ text: 'Radar 🔥', link: '/syntax/radar' },
|
||||
{ text: 'Treemap 🔥', link: '/syntax/treemap' },
|
||||
{ text: 'Other Examples', link: '/syntax/examples' },
|
||||
],
|
||||
},
|
||||
|
@@ -106,3 +106,13 @@ const themes = {
|
||||
```
|
||||
|
||||
The actual options and values for the colors are defined in **src/theme/theme-[xyz].js**. If you provide the options your diagram needs in the existing theme files then the theming will work smoothly without hiccups.
|
||||
|
||||
## Examples
|
||||
|
||||
The `@mermaid-js/examples` package contains a collection of examples that are used by tools like mermaid.live to help users get started with the new diagram.
|
||||
|
||||
You can duplicate an existing diagram example file, eg: `packages/examples/src/examples/flowchart.ts`, and modify it with details specific to your diagram.
|
||||
|
||||
Then you can import the example in the `packages/examples/src/index.ts` file and add it to the `examples` array.
|
||||
|
||||
Each diagram should have at least one example, and that should be marked as default. It is good to add more examples to showcase different features of the diagram.
|
||||
|
@@ -240,7 +240,7 @@ Communication tools and platforms
|
||||
| GitHub + Mermaid | - | [🦊🔗](https://addons.mozilla.org/firefox/addon/github-mermaid/) | - | - | [🐙🔗](https://github.com/BackMarket/github-mermaid-extension) |
|
||||
| Asciidoctor Live Preview | [🎡🔗](https://chromewebstore.google.com/detail/asciidoctorjs-live-previe/iaalpfgpbocpdfblpnhhgllgbdbchmia) | - | - | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/asciidoctorjs-live-previ/pefkelkanablhjdekgdahplkccnbdggd?hl=en-US) | - |
|
||||
| Diagram Tab | - | - | - | - | [🐙🔗](https://github.com/khafast/diagramtab) |
|
||||
| Markdown Diagrams | [🎡🔗](https://chromewebstore.google.com/detail/markdown-diagrams/pmoglnmodacnbbofbgcagndelmgaclel) | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-diagrams/) | [🔴🔗](https://addons.opera.com/en/extensions/details/markdown-diagrams/) | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/markdown-diagrams/hceenoomhhdkjjijnmlclkpenkapfihe) | [🐙🔗](https://github.com/marcozaccari/markdown-diagrams-browser-extension/tree/master/doc/examples) |
|
||||
| Markdown Diagrams | [🎡🔗](https://chromewebstore.google.com/detail/markdown-diagrams/pmoglnmodacnbbofbgcagndelmgaclel) | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-diagrams/) | - | [🌀🔗](https://microsoftedge.microsoft.com/addons/detail/markdown-diagrams/hceenoomhhdkjjijnmlclkpenkapfihe) | [🐙🔗](https://github.com/marcozaccari/markdown-diagrams-browser-extension/tree/master/doc/examples) |
|
||||
| Markdown Viewer | - | [🦊🔗](https://addons.mozilla.org/en-US/firefox/addon/markdown-viewer-chrome/) | - | - | [🐙🔗](https://github.com/simov/markdown-viewer) |
|
||||
| Extensions for Mermaid | - | - | [🔴🔗](https://addons.opera.com/en/extensions/details/extensions-for-mermaid/) | - | [🐙🔗](https://github.com/Stefan-S/mermaid-extension) |
|
||||
| Chrome Diagrammer | [🎡🔗](https://chromewebstore.google.com/detail/chrome-diagrammer/bkpbgjmkomfoakfklcjeoegkklgjnnpk) | - | - | - | - |
|
||||
@@ -265,5 +265,6 @@ Communication tools and platforms
|
||||
- [reveal.js-mermaid-plugin](https://github.com/ludwick/reveal.js-mermaid-plugin)
|
||||
- [Reveal CK](https://github.com/jedcn/reveal-ck)
|
||||
- [reveal-ck-mermaid-plugin](https://github.com/tmtm/reveal-ck-mermaid-plugin)
|
||||
- [Vitepress Plugin](https://github.com/sametcn99/vitepress-mermaid-renderer)
|
||||
|
||||
<!--- cspell:ignore Blazorade HueHive --->
|
||||
|
@@ -24,7 +24,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
|
||||
|
||||
Official Mermaid Chart plugins:
|
||||
|
||||
- [Mermaid Chart GPT](https://chat.openai.com/g/g-1IRFKwq4G-mermaid-chart)
|
||||
- [Mermaid Chart GPT](https://chatgpt.com/g/g-684cc36f30208191b21383b88650a45d-mermaid-chart-diagrams-and-charts)
|
||||
- [Confluence](https://marketplace.atlassian.com/apps/1234056/mermaid-chart-for-confluence?hosting=cloud&tab=overview)
|
||||
- [Jira](https://marketplace.atlassian.com/apps/1234810/mermaid-chart-for-jira?tab=overview&hosting=cloud)
|
||||
- [Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=MermaidChart.vscode-mermaid-chart)
|
||||
|
@@ -27,7 +27,7 @@
|
||||
"@iconify-json/carbon": "^1.1.37",
|
||||
"@unocss/reset": "^66.0.0",
|
||||
"@vite-pwa/vitepress": "^1.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"@vitejs/plugin-vue": "^6.0.0",
|
||||
"fast-glob": "^3.3.3",
|
||||
"https-localhost": "^4.7.1",
|
||||
"pathe": "^2.0.3",
|
||||
|
@@ -11,7 +11,7 @@ This diagram type is particularly useful for developers, network engineers, educ
|
||||
## Syntax
|
||||
|
||||
```
|
||||
packet-beta
|
||||
packet
|
||||
start: "Block name" %% Single-bit block
|
||||
start-end: "Block name" %% Multi-bit blocks
|
||||
... More Fields ...
|
||||
@@ -22,7 +22,7 @@ start-end: "Block name" %% Multi-bit blocks
|
||||
Using start and end bit counts can be difficult, especially when modifying a design. For this we add a bit count field, which starts from the end of the previous field automagically. Use `+<count>` to set the number of bits, thus:
|
||||
|
||||
```
|
||||
packet-beta
|
||||
packet
|
||||
+1: "Block name" %% Single-bit block
|
||||
+8: "Block name" %% 8-bit block
|
||||
9-15: "Manually set start and end, it's fine to mix and match"
|
||||
@@ -35,7 +35,7 @@ packet-beta
|
||||
---
|
||||
title: "TCP Packet"
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
@@ -56,7 +56,7 @@ packet-beta
|
||||
```
|
||||
|
||||
```mermaid-example
|
||||
packet-beta
|
||||
packet
|
||||
title UDP Packet
|
||||
+16: "Source Port"
|
||||
+16: "Destination Port"
|
||||
@@ -104,7 +104,7 @@ config:
|
||||
packet:
|
||||
startByteColor: red
|
||||
---
|
||||
packet-beta
|
||||
packet
|
||||
0-15: "Source Port"
|
||||
16-31: "Destination Port"
|
||||
32-63: "Sequence Number"
|
||||
|
245
packages/mermaid/src/docs/syntax/treemap.md
Normal file
245
packages/mermaid/src/docs/syntax/treemap.md
Normal file
@@ -0,0 +1,245 @@
|
||||
---
|
||||
title: Treemap Diagram Syntax
|
||||
outline: 'deep' # shows all h3 headings in outline in Vitepress
|
||||
---
|
||||
|
||||
# Treemap Diagram
|
||||
|
||||
> A treemap diagram displays hierarchical data as a set of nested rectangles. Each branch of the tree is represented by a rectangle, which is then tiled with smaller rectangles representing sub-branches.
|
||||
|
||||
```warning
|
||||
This is a new diagram type in Mermaid. Its syntax may evolve in future versions.
|
||||
```
|
||||
|
||||
## Introduction
|
||||
|
||||
Treemap diagrams are an effective way to visualize hierarchical data and show proportions between categories and subcategories. The size of each rectangle is proportional to the value it represents, making it easy to compare different parts of a hierarchy.
|
||||
|
||||
Treemap diagrams are particularly useful for:
|
||||
|
||||
- Visualizing hierarchical data structures
|
||||
- Comparing proportions between categories
|
||||
- Displaying large amounts of hierarchical data in a limited space
|
||||
- Identifying patterns and outliers in hierarchical data
|
||||
|
||||
## Syntax
|
||||
|
||||
```
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2"
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20
|
||||
"Leaf 2.2": 25
|
||||
```
|
||||
|
||||
### Node Definition
|
||||
|
||||
Nodes in a treemap are defined using the following syntax:
|
||||
|
||||
- **Section/Parent nodes**: Defined with quoted text `"Section Name"`
|
||||
- **Leaf nodes with values**: Defined with quoted text followed by a colon and value `"Leaf Name": value`
|
||||
- **Hierarchy**: Created using indentation (spaces or tabs)
|
||||
- **Styling**: Nodes can be styled using the `:::class` syntax
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Treemap
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
### Hierarchical Treemap
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Products"
|
||||
"Electronics"
|
||||
"Phones": 50
|
||||
"Computers": 30
|
||||
"Accessories": 20
|
||||
"Clothing"
|
||||
"Men's": 40
|
||||
"Women's": 40
|
||||
```
|
||||
|
||||
### Treemap with Styling
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Section 1"
|
||||
"Leaf 1.1": 12
|
||||
"Section 1.2":::class1
|
||||
"Leaf 1.2.1": 12
|
||||
"Section 2"
|
||||
"Leaf 2.1": 20:::class1
|
||||
"Leaf 2.2": 25
|
||||
"Leaf 2.3": 12
|
||||
|
||||
classDef class1 fill:red,color:blue,stroke:#FFD600;
|
||||
```
|
||||
|
||||
## Styling and Configuration
|
||||
|
||||
Treemap diagrams can be customized using Mermaid's styling and configuration options.
|
||||
|
||||
### Using classDef for Styling
|
||||
|
||||
You can define custom styles for nodes using the `classDef` syntax, which is a standard feature across many Mermaid diagram types:
|
||||
|
||||
```mermaid-example
|
||||
treemap-beta
|
||||
"Main"
|
||||
"A": 20
|
||||
"B":::important
|
||||
"B1": 10
|
||||
"B2": 15
|
||||
"C": 5
|
||||
|
||||
classDef important fill:#f96,stroke:#333,stroke-width:2px;
|
||||
```
|
||||
|
||||
### Theme Configuration
|
||||
|
||||
You can customize the colors of your treemap using the theme configuration:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
theme: 'forest'
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
### Diagram Padding
|
||||
|
||||
You can adjust the padding around the treemap diagram using the `diagramPadding` configuration option:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
diagramPadding: 200
|
||||
---
|
||||
treemap-beta
|
||||
"Category A"
|
||||
"Item A1": 10
|
||||
"Item A2": 20
|
||||
"Category B"
|
||||
"Item B1": 15
|
||||
"Item B2": 25
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The treemap diagram supports the following configuration options:
|
||||
|
||||
| Option | Description | Default |
|
||||
| -------------- | --------------------------------------------------------------------------- | ------- |
|
||||
| useMaxWidth | When true, the diagram width is set to 100% and scales with available space | true |
|
||||
| padding | Internal padding between nodes | 10 |
|
||||
| diagramPadding | Padding around the entire diagram | 8 |
|
||||
| showValues | Whether to show values in the treemap | true |
|
||||
| nodeWidth | Width of nodes | 100 |
|
||||
| nodeHeight | Height of nodes | 40 |
|
||||
| borderWidth | Width of borders | 1 |
|
||||
| valueFontSize | Font size for values | 12 |
|
||||
| labelFontSize | Font size for labels | 14 |
|
||||
| valueFormat | Format for values (see Value Formatting section) | ',' |
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### Value Formatting
|
||||
|
||||
Values in treemap diagrams can be formatted to display in different ways using the `valueFormat` configuration option. This option primarily uses [D3's format specifiers](https://github.com/d3/d3-format#locale_format) to control how numbers are displayed, with some additional special cases for common formats.
|
||||
|
||||
Some common format patterns:
|
||||
|
||||
- `,` - Thousands separator (default)
|
||||
- `$` - Add dollar sign
|
||||
- `.1f` - Show one decimal place
|
||||
- `.1%` - Show as percentage with one decimal place
|
||||
- `$0,0` - Dollar sign with thousands separator
|
||||
- `$.2f` - Dollar sign with 2 decimal places
|
||||
- `$,.2f` - Dollar sign with thousands separator and 2 decimal places
|
||||
|
||||
The treemap diagram supports both standard D3 format specifiers and some common currency formats that combine the dollar sign with other formatting options.
|
||||
|
||||
Example with currency formatting:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$0,0'
|
||||
---
|
||||
treemap-beta
|
||||
"Budget"
|
||||
"Operations"
|
||||
"Salaries": 700000
|
||||
"Equipment": 200000
|
||||
"Supplies": 100000
|
||||
"Marketing"
|
||||
"Advertising": 400000
|
||||
"Events": 100000
|
||||
```
|
||||
|
||||
Example with percentage formatting:
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
config:
|
||||
treemap:
|
||||
valueFormat: '$.1%'
|
||||
---
|
||||
treemap-beta
|
||||
"Market Share"
|
||||
"Company A": 0.35
|
||||
"Company B": 0.25
|
||||
"Company C": 0.15
|
||||
"Others": 0.25
|
||||
```
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
Treemap diagrams are commonly used for:
|
||||
|
||||
1. **Financial Data**: Visualizing budget allocations, market shares, or portfolio compositions
|
||||
2. **File System Analysis**: Showing disk space usage by folders and files
|
||||
3. **Population Demographics**: Displaying population distribution across regions and subregions
|
||||
4. **Product Hierarchies**: Visualizing product categories and their sales volumes
|
||||
5. **Organizational Structures**: Representing departments and team sizes in a company
|
||||
|
||||
## Limitations
|
||||
|
||||
- Treemap diagrams work best when the data has a natural hierarchy
|
||||
- Very small values may be difficult to see or label in a treemap diagram
|
||||
- Deep hierarchies (many levels) can be challenging to represent clearly
|
||||
- Treemap diagrams are not well suited for representing data with negative values
|
||||
|
||||
## Related Diagrams
|
||||
|
||||
If treemap diagrams don't suit your needs, consider these alternatives:
|
||||
|
||||
- [**Pie Charts**](./pie.md): For simple proportion comparisons without hierarchy
|
||||
- **Sunburst Diagrams**: For hierarchical data with a radial layout (yet to be released in Mermaid).
|
||||
- [**Sankey Diagrams**](./sankey.md): For flow-based hierarchical data
|
||||
|
||||
## Notes
|
||||
|
||||
The treemap diagram implementation in Mermaid is designed to be simple to use while providing powerful visualization capabilities. As this is a newer diagram type, feedback and feature requests are welcome through the Mermaid GitHub repository.
|
@@ -5,7 +5,7 @@
|
||||
import { registerIconPacks } from './rendering-util/icons.js';
|
||||
import { dedent } from 'ts-dedent';
|
||||
import type { MermaidConfig } from './config.type.js';
|
||||
import { detectType, registerLazyLoadedDiagrams } from './diagram-api/detectType.js';
|
||||
import { detectType, detectors, registerLazyLoadedDiagrams } from './diagram-api/detectType.js';
|
||||
import { addDiagrams } from './diagram-api/diagram-orchestration.js';
|
||||
import { loadRegisteredDiagrams } from './diagram-api/loadDiagram.js';
|
||||
import type { ExternalDiagramDefinition, SVG, SVGGroup } from './diagram-api/types.js';
|
||||
@@ -415,6 +415,17 @@ const render: typeof mermaidAPI.render = (id, text, container) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the metadata for all registered diagrams.
|
||||
* Currently only the id is returned.
|
||||
* @returns An array of objects with the id of the diagram.
|
||||
*/
|
||||
const getRegisteredDiagramsMetadata = (): Pick<ExternalDiagramDefinition, 'id'>[] => {
|
||||
return Object.keys(detectors).map((id) => ({
|
||||
id,
|
||||
}));
|
||||
};
|
||||
|
||||
export interface Mermaid {
|
||||
startOnLoad: boolean;
|
||||
parseError?: ParseErrorFunction;
|
||||
@@ -437,6 +448,7 @@ export interface Mermaid {
|
||||
setParseErrorHandler: typeof setParseErrorHandler;
|
||||
detectType: typeof detectType;
|
||||
registerIconPacks: typeof registerIconPacks;
|
||||
getRegisteredDiagramsMetadata: typeof getRegisteredDiagramsMetadata;
|
||||
}
|
||||
|
||||
const mermaid: Mermaid = {
|
||||
@@ -454,6 +466,7 @@ const mermaid: Mermaid = {
|
||||
setParseErrorHandler,
|
||||
detectType,
|
||||
registerIconPacks,
|
||||
getRegisteredDiagramsMetadata,
|
||||
};
|
||||
|
||||
export default mermaid;
|
||||
|
@@ -794,6 +794,7 @@ graph TD;A--x|text including URL space|B;`)
|
||||
{ textDiagramType: 'gantt', expectedType: 'gantt' },
|
||||
{ textDiagramType: 'journey', expectedType: 'journey' },
|
||||
{ textDiagramType: 'pie', expectedType: 'pie' },
|
||||
{ textDiagramType: 'packet', expectedType: 'packet' },
|
||||
{ textDiagramType: 'packet-beta', expectedType: 'packet' },
|
||||
{ textDiagramType: 'xychart-beta', expectedType: 'xychart' },
|
||||
{ textDiagramType: 'requirementDiagram', expectedType: 'requirement' },
|
||||
|
@@ -17,7 +17,7 @@ export async function note<T extends SVGGraphicsElement>(
|
||||
if (!useHtmlLabels) {
|
||||
node.centerLabel = true;
|
||||
}
|
||||
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
|
||||
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
|
||||
const totalWidth = Math.max(bbox.width + (node.padding ?? 0) * 2, node?.width ?? 0);
|
||||
const totalHeight = Math.max(bbox.height + (node.padding ?? 0) * 2, node?.height ?? 0);
|
||||
const x = -totalWidth / 2;
|
||||
@@ -50,6 +50,11 @@ export async function note<T extends SVGGraphicsElement>(
|
||||
rect.selectAll('path').attr('style', nodeStyles);
|
||||
}
|
||||
|
||||
label.attr(
|
||||
'transform',
|
||||
`translate(${-bbox.width / 2 - (bbox.x - (bbox.left ?? 0))}, ${-(bbox.height / 2) - (bbox.y - (bbox.top ?? 0))})`
|
||||
);
|
||||
|
||||
updateNodeBounds(node, rect);
|
||||
|
||||
node.intersect = function (point) {
|
||||
|
@@ -1,5 +1,17 @@
|
||||
# @mermaid-js/parser
|
||||
|
||||
## 0.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#6725](https://github.com/mermaid-js/mermaid/pull/6725) [`0da2922`](https://github.com/mermaid-js/mermaid/commit/0da2922ee7f47959e324ec10d3d21ee70594f557) Thanks [@shubham-mermaid](https://github.com/shubham-mermaid)! - chore: use Treemap instead of TreemapDoc in parser.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#6590](https://github.com/mermaid-js/mermaid/pull/6590) [`f338802`](https://github.com/mermaid-js/mermaid/commit/f338802642cdecf5b7ed6c19a20cf2a81effbbee) Thanks [@knsv](https://github.com/knsv)! - Adding support for the new diagram type nested treemap
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Minor Changes
|
||||
|
@@ -30,6 +30,11 @@
|
||||
"id": "radar",
|
||||
"grammar": "src/language/radar/radar.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
},
|
||||
{
|
||||
"id": "treemap",
|
||||
"grammar": "src/language/treemap/treemap.langium",
|
||||
"fileExtensions": [".mmd", ".mermaid"]
|
||||
}
|
||||
],
|
||||
"mode": "production",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mermaid-js/parser",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.1",
|
||||
"description": "MermaidJS parser",
|
||||
"author": "Yokozuna59",
|
||||
"contributors": [
|
||||
|
@@ -8,6 +8,7 @@ export {
|
||||
Architecture,
|
||||
GitGraph,
|
||||
Radar,
|
||||
Treemap,
|
||||
Branch,
|
||||
Commit,
|
||||
Merge,
|
||||
@@ -19,6 +20,7 @@ export {
|
||||
isPieSection,
|
||||
isArchitecture,
|
||||
isGitGraph,
|
||||
isTreemap,
|
||||
isBranch,
|
||||
isCommit,
|
||||
isMerge,
|
||||
@@ -32,6 +34,7 @@ export {
|
||||
ArchitectureGeneratedModule,
|
||||
GitGraphGeneratedModule,
|
||||
RadarGeneratedModule,
|
||||
TreemapGeneratedModule,
|
||||
} from './generated/module.js';
|
||||
|
||||
export * from './gitGraph/index.js';
|
||||
@@ -41,3 +44,4 @@ export * from './packet/index.js';
|
||||
export * from './pie/index.js';
|
||||
export * from './architecture/index.js';
|
||||
export * from './radar/index.js';
|
||||
export * from './treemap/index.js';
|
||||
|
@@ -3,7 +3,7 @@ import "../common/common";
|
||||
|
||||
entry Packet:
|
||||
NEWLINE*
|
||||
"packet-beta"
|
||||
("packet"| "packet-beta")
|
||||
(
|
||||
TitleAndAccessibilities
|
||||
| blocks+=PacketBlock
|
||||
|
@@ -2,6 +2,6 @@ import { AbstractMermaidTokenBuilder } from '../common/index.js';
|
||||
|
||||
export class PacketTokenBuilder extends AbstractMermaidTokenBuilder {
|
||||
public constructor() {
|
||||
super(['packet-beta']);
|
||||
super(['packet']);
|
||||
}
|
||||
}
|
||||
|
1
packages/parser/src/language/treemap/index.ts
Normal file
1
packages/parser/src/language/treemap/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './module.js';
|
88
packages/parser/src/language/treemap/module.ts
Normal file
88
packages/parser/src/language/treemap/module.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type {
|
||||
DefaultSharedCoreModuleContext,
|
||||
LangiumCoreServices,
|
||||
LangiumSharedCoreServices,
|
||||
Module,
|
||||
PartialLangiumCoreServices,
|
||||
} from 'langium';
|
||||
import {
|
||||
EmptyFileSystem,
|
||||
createDefaultCoreModule,
|
||||
createDefaultSharedCoreModule,
|
||||
inject,
|
||||
} from 'langium';
|
||||
|
||||
import { MermaidGeneratedSharedModule, TreemapGeneratedModule } from '../generated/module.js';
|
||||
import { TreemapTokenBuilder } from './tokenBuilder.js';
|
||||
import { TreemapValueConverter } from './valueConverter.js';
|
||||
import { TreemapValidator, registerValidationChecks } from './treemap-validator.js';
|
||||
|
||||
/**
|
||||
* Declaration of `Treemap` services.
|
||||
*/
|
||||
interface TreemapAddedServices {
|
||||
parser: {
|
||||
TokenBuilder: TreemapTokenBuilder;
|
||||
ValueConverter: TreemapValueConverter;
|
||||
};
|
||||
validation: {
|
||||
TreemapValidator: TreemapValidator;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Union of Langium default services and `Treemap` services.
|
||||
*/
|
||||
export type TreemapServices = LangiumCoreServices & TreemapAddedServices;
|
||||
|
||||
/**
|
||||
* Dependency injection module that overrides Langium default services and
|
||||
* contributes the declared `Treemap` services.
|
||||
*/
|
||||
export const TreemapModule: Module<
|
||||
TreemapServices,
|
||||
PartialLangiumCoreServices & TreemapAddedServices
|
||||
> = {
|
||||
parser: {
|
||||
TokenBuilder: () => new TreemapTokenBuilder(),
|
||||
ValueConverter: () => new TreemapValueConverter(),
|
||||
},
|
||||
validation: {
|
||||
TreemapValidator: () => new TreemapValidator(),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the full set of services required by Langium.
|
||||
*
|
||||
* First inject the shared services by merging two modules:
|
||||
* - Langium default shared services
|
||||
* - Services generated by langium-cli
|
||||
*
|
||||
* Then inject the language-specific services by merging three modules:
|
||||
* - Langium default language-specific services
|
||||
* - Services generated by langium-cli
|
||||
* - Services specified in this file
|
||||
* @param context - Optional module context with the LSP connection
|
||||
* @returns An object wrapping the shared services and the language-specific services
|
||||
*/
|
||||
export function createTreemapServices(context: DefaultSharedCoreModuleContext = EmptyFileSystem): {
|
||||
shared: LangiumSharedCoreServices;
|
||||
Treemap: TreemapServices;
|
||||
} {
|
||||
const shared: LangiumSharedCoreServices = inject(
|
||||
createDefaultSharedCoreModule(context),
|
||||
MermaidGeneratedSharedModule
|
||||
);
|
||||
const Treemap: TreemapServices = inject(
|
||||
createDefaultCoreModule({ shared }),
|
||||
TreemapGeneratedModule,
|
||||
TreemapModule
|
||||
);
|
||||
shared.ServiceRegistry.register(Treemap);
|
||||
|
||||
// Register validation checks
|
||||
registerValidationChecks(Treemap);
|
||||
|
||||
return { shared, Treemap };
|
||||
}
|
7
packages/parser/src/language/treemap/tokenBuilder.ts
Normal file
7
packages/parser/src/language/treemap/tokenBuilder.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { AbstractMermaidTokenBuilder } from '../common/index.js';
|
||||
|
||||
export class TreemapTokenBuilder extends AbstractMermaidTokenBuilder {
|
||||
public constructor() {
|
||||
super(['treemap']);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user