Compare commits

..

188 Commits

Author SHA1 Message Date
Per Brolin
4d88bdc0cc Updated links due to changes on the MC site 2024-11-06 11:29:56 +01:00
Sidharth Vinod
f507dbbe00 Merge pull request #6010 from mermaid-js/update-whiteboard-tagline
TopBar: update Whiteboard tagline
2024-11-06 11:42:22 +05:30
Sidharth Vinod
9966beb99b Merge pull request #6016 from mermaid-js/update-blog
DOCS: add latest blog posts
2024-11-06 11:42:08 +05:30
Steph
f4713332c0 minor fix 2024-11-05 07:58:56 -08:00
Steph
63ff5b1d98 update utm 2024-11-05 07:57:55 -08:00
Steph
7e71b85668 update blog page 2024-11-01 06:43:26 -07:00
Steph
1dd7bcb0a6 minor update 2024-10-30 09:20:35 -07:00
Steph
bbf6ab1206 update whiteboard tagline 2024-10-30 08:58:52 -07:00
Knut Sveidqvist
56f522ec50 Merge pull request #6008 from mermaid-js/changeset-release/master
Version Packages
2024-10-30 16:42:15 +01:00
github-actions[bot]
d4e5acc530 Version Packages 2024-10-30 15:19:33 +00:00
Knut Sveidqvist
ef9cbb61e5 Merge pull request #6009 from mermaid-js/chore/fix-changeset-for-#5880
chore: update changeset for #5880
2024-10-30 16:17:20 +01:00
Alois Klink
8a0ecc1fe9 chore: update changeset for #5880
This isn't documented, but apparently changesets supports this in 91d1ef2ef7

This is needed to give @yari-dewalt credit for PR #5880
2024-10-31 00:09:10 +09:00
Ashish Jain
c6e1271ef7 Merge pull request #6007 from mermaid-js/release/11.4.0
Release v11.4.0
2024-10-30 15:19:37 +01:00
autofix-ci[bot]
b02294376a [autofix.ci] apply automated fixes 2024-10-30 14:05:05 +00:00
Ashish Jain
97c61d818c added changeset for class diagram 2024-10-30 14:11:41 +01:00
Alois Klink
1c66237fa8 test(unit): update docs unit test snapshot
This snapshot should have been modified in
c1ca3511c (Fix (tmp) for handling of config of htmlLabels ion note. MOvint class box out of flowchart docs, 2024-10-30

Fixes: c1ca3511c4
2024-10-30 21:41:38 +09:00
Knut Sveidqvist
8ef5d324fd Typescript fixes 2024-10-30 13:32:03 +01:00
Knut Sveidqvist
c1ca3511c4 Fix (tmp) for handling of config of htmlLabels ion note. MOvint class box out of flowchart docs 2024-10-30 11:54:31 +01:00
Alois Klink
953d288957 refactor(types): fix kanbanRenderer types
Add the appropriate type checks to ensure that we're only ever calling:

- `insertCluster()` with `ClusterNode`
- `insertNode()` with a `NonClusterNode`

Fixes: 7401cb8f6a
2024-10-30 19:00:35 +09:00
Alois Klink
143806b360 refactor(types): assert insertNode returns non-empty
Fixes: 7401cb8f6a
2024-10-30 19:00:35 +09:00
Alois Klink
a381ab6c41 refactor(types): separate ClusterNodes
Fixes: 7401cb8f6a
2024-10-30 19:00:34 +09:00
Ashish Jain
93e20fdaa3 Merge from develop 2024-10-30 10:38:51 +01:00
Knut Sveidqvist
ece3b254db Merge pull request #6006 from yari-dewalt/fix-note-non-html-text
Fix note non html text
2024-10-30 10:37:35 +01:00
Yari DeWalt
dd42305e8c Merge branch 'develop' into fix-note-non-html-text 2024-10-29 12:21:52 -07:00
yari-dewalt
c34fd152d5 Add handling for ' characters in non-html text so they don't get sanitized into html code values 2024-10-29 11:57:22 -07:00
yari-dewalt
8f0e0b9c39 Change useHtmlLabels to use general config's htmlLabels attribute not flowchart's 2024-10-29 11:55:34 -07:00
yari-dewalt
fe8932e906 Set node.centerLabel before using labelHelper() 2024-10-29 11:54:25 -07:00
Knut Sveidqvist
73bbde8476 Update of the background-fix 2024-10-29 16:21:56 +01:00
Knut Sveidqvist
01a41ef530 Merge branch 'release/11.4.0' of github.com:mermaid-js/mermaid into release/11.4.0 2024-10-29 14:52:25 +01:00
Knut Sveidqvist
124d2f72dd Fix for icon borders 2024-10-29 14:51:55 +01:00
Alois Klink
34e8946c58 refactor(types): fix kanbanItem circular types
Exclude the `node.shape` type from `kanbanItem()`, as otherwise it
causes a circular dependency in the types.
2024-10-29 22:18:38 +09:00
Ashish Jain
0197c08916 revert adding kanban section to shape 2024-10-29 14:04:57 +01:00
Alois Klink
ef30283fe9 refactor: fix more type errors in kanbanItem
Fixes: 7401cb8f6a
2024-10-29 21:44:34 +09:00
Ashish Jain
dffaf9fa8f fix for kanban section shape 2024-10-29 13:41:20 +01:00
Alois Klink
9168d5d1f4 refactor(types): fix some type errors in kanbanItem
Fixes: 7401cb8f6a
2024-10-29 21:14:51 +09:00
Alois Klink
f33ebfaf7a refactor: fix broken generic on insertLabel()
Fixes: 7401cb8f6a
2024-10-29 21:14:48 +09:00
Alois Klink
054f929150 refactor: remove unused param from labelHelper()
Fixes: 7401cb8f6a
2024-10-29 21:14:40 +09:00
Knut Sveidqvist
8d0902de4c Merge pull request #6002 from aloisklink/fix/make-parse-error-on-invalid-shape
fix: make `mermaid.parse` throw an error on invalid shapes
2024-10-29 10:46:02 +01:00
Knut Sveidqvist
f6c32b6da4 Merge branch 'develop' into fix/make-parse-error-on-invalid-shape 2024-10-29 10:45:50 +01:00
Knut Sveidqvist
bdf145ffe3 Merge pull request #5880 from yari-dewalt/update-class-diagram
Update class diagram to v3 using new renderer
2024-10-29 10:44:07 +01:00
Knut Sveidqvist
75ca802b62 Merge branch 'develop' into update-class-diagram 2024-10-29 10:40:39 +01:00
Ashish Jain
3f85b6179d Merge pull request #5999 from mermaid-js/knsv/5342-kanban
Knsv/5342 kanban
2024-10-29 10:38:18 +01:00
yari-dewalt
b315d70af5 Merge branch 'develop' into update-class-diagram and fix conflicts 2024-10-28 10:32:19 -07:00
autofix-ci[bot]
848be3d129 [autofix.ci] apply automated fixes 2024-10-28 14:33:47 +00:00
Alois Klink
5fabd414fb fix: error mermaid.parse on invalid shapes
Currently, invalid shapes cause an error when rendering, but not when
parsing. This confuses the Mermaid Live Editor, e.g. by not showing the
error message.

Instead, I've added an `isValidShape()` that validates if the shape is
valid at parse time. This only checks shapes using the new extended
shapes syntax. Currenlty, using `A(-this is an ellipse node-)` is broken
(see #5976) and also causes an invalid shape error at render time, but
I've ignored it in this PR, so it will continue pass at parse-time
(we have unit tests checking ellipse parsing).

See: https://github.com/mermaid-js/mermaid/issues/5976
2024-10-28 23:17:13 +09:00
Alois Klink
17e2f9e0d4 refactor: fix addVertex type parameter
I went through
`packages/mermaid/src/diagrams/flowchart/parser/flow.jison` and found
every possible value this `type` parameter could be.
2024-10-28 23:17:13 +09:00
Alois Klink
7fa8b35bdc refactor: convert if-statements to switch..case 2024-10-28 23:17:13 +09:00
Knut Sveidqvist
e6ea4eae50 Fix for missing function 2024-10-28 14:29:06 +01:00
Knut Sveidqvist
7401cb8f6a Merge branch 'develop' into knsv/5342-kanban 2024-10-28 14:22:10 +01:00
Sidharth Vinod
e765007b21 Merge pull request #5974 from aloisklink/refactor/improving-rendering-shape-types
refactor: TypeScript improvements to `packages/mermaid/src/rendering-util/rendering-elements`
2024-10-28 18:22:23 +05:30
Sidharth Vinod
b5cd101d86 Merge branch 'develop' into refactor/improving-rendering-shape-types 2024-10-28 18:11:41 +05:30
autofix-ci[bot]
da12ace593 [autofix.ci] apply automated fixes 2024-10-28 12:08:48 +00:00
Knut Sveidqvist
2c676b6222 Moving kanbanItem out of the shapes doc 2024-10-28 13:03:18 +01:00
Knut Sveidqvist
0af08276d2 #5232 Enabling handdrawn mode 2024-10-28 12:53:03 +01:00
Knut Sveidqvist
d790e353b0 #5432 Theme based text color for labels 2024-10-28 11:51:44 +01:00
Knut Sveidqvist
e07e9b9daf Merge branch 'knsv/5342-kanban' of github.com:mermaid-js/mermaid into knsv/5342-kanban 2024-10-28 11:47:17 +01:00
Knut Sveidqvist
948ec4d1ce #5342 Handing of wide section texts 2024-10-28 11:43:56 +01:00
autofix-ci[bot]
e62d268c89 [autofix.ci] apply automated fixes 2024-10-28 09:19:37 +00:00
Knut Sveidqvist
742ad7c130 Adding changeset and kanban in the docs sidenav 2024-10-28 10:03:42 +01:00
Knut Sveidqvist
56cc3728c5 End to end tests and docs 2024-10-25 15:40:58 +02:00
Knut Sveidqvist
fb86e5c689 Merge branch 'develop' into knsv/5342-kanban 2024-10-25 14:56:46 +02:00
Alois Klink
f8746bee04 refactor: move MaybePromise type to src/types.ts
Suggested-by: https://github.com/mermaid-js/mermaid/pull/5974#discussion_r1810562136
Co-authored-by: Sidharth Vinod <sidharthv96@gmail.com>
2024-10-22 21:49:23 +09:00
Alois Klink
bb97fdbd1e refactor: use parseFontSize()
Handle `NaN` values by using `parseFontSize()`.

Suggested-by: https://github.com/mermaid-js/mermaid/pull/5974#discussion_r1810570967
2024-10-22 21:49:02 +09:00
Sidharth Vinod
e3ec6ed993 Merge pull request #5987 from mermaid-js/update-promos
Remove Product Hunt promos & Update Promo bar
2024-10-22 12:03:05 +05:30
Steph
6df1fa1a03 update taglines 2024-10-21 23:26:17 -07:00
yari-dewalt
fd6b875f87 Fix subgraph / cluster node width based off of label 2024-10-21 16:09:55 -07:00
yari-dewalt
be3da0b39d Update testing file 2024-10-21 15:37:41 -07:00
yari-dewalt
64bc7f4bd9 Add fix for flipped edge markers 2024-10-21 15:37:24 -07:00
Yari DeWalt
75e796f183 Merge branch 'develop' into update-class-diagram 2024-10-21 15:36:29 -07:00
Alois Klink
6cc0132e0a refactor(elk): add LabelData type for layout-elk
I don't know what exactly the type does or is for, but I've tried to
type it to what it seems to be.
2024-10-21 22:23:36 +09:00
Alois Klink
4bc70b7325 refactor: use more standard types
See: https://github.com/mermaid-js/mermaid/pull/5974#discussion_r1806473749
See: https://github.com/mermaid-js/mermaid/pull/5974#discussion_r1808511738

Co-authored-by: saurabhg772244 <saurabh@mermaidchart.com>
2024-10-21 22:23:36 +09:00
yari-dewalt
bfdd0f244d Fix tests to work with new cssClasses attribute 2024-10-18 16:36:06 -07:00
yari-dewalt
0e819ae050 Change cssClasses to use concatenated string instead of array to pick up css styles 2024-10-18 10:33:56 -07:00
yari-dewalt
10f11e016f Merge branch 'update-class-diagram' of github.com:yari-dewalt/mermaid into update-class-diagram 2024-10-18 10:12:48 -07:00
yari-dewalt
b7d66bb975 Add elk and handDrawn specific test files 2024-10-18 10:12:09 -07:00
yari-dewalt
74695d2e88 Fix undesired wrapping on notes 2024-10-18 10:11:36 -07:00
Steph
38eed7c988 product hunt promo material updates 2024-10-18 09:54:46 -07:00
Alois Klink
d16e46a386 Merge pull request #5972 from gabeidx/zed-mermaid
docs(ecosystem): Add Zed extension zed-mermaid
2024-10-18 08:45:30 +00:00
Alois Klink
97820a50c2 Merge pull request #5918 from vorburger/patch-2
docs: Fix inconsistency in Block Diagram doc example
2024-10-18 08:42:34 +00:00
Alois Klink
df948bcf2c Merge pull request #5968 from eitsupi/fix-ssg
docs(integrations): fix listing order
2024-10-18 08:39:31 +00:00
Alois Klink
cfd097b029 Merge pull request #5967 from yihui/patch-1
docs: bullet lists should not be code blocks
2024-10-18 08:37:12 +00:00
Alois Klink
ffc1fef599 Merge pull request #5957 from TomasHubelbauer/patch-1
Remove extraneous period
2024-10-18 08:36:31 +00:00
Alois Klink
1e8e4e4b73 Merge branch 'develop' into patch-2 2024-10-18 08:34:07 +00:00
autofix-ci[bot]
606ecf8990 [autofix.ci] apply automated fixes 2024-10-17 17:42:10 +00:00
Alois Klink
16a5fc05d6 refactor: remove some any types from render()
This let's us confirm that the types we're passing to `insertNode()` are
valid.
2024-10-18 01:51:53 +09:00
Alois Klink
62fd359c0e refactor: convert shapes/util.js to TypeScript
Convert the
`packages/mermaid/src/rendering-util/rendering-elements/shapes/util.js`
file to TypeScript at:
`packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts`.
2024-10-18 01:51:53 +09:00
Alois Klink
b0f4ace3a1 refactor: convert rendering-elements/nodes to TS
Convert the
`packages/mermaid/src/rendering-util/rendering-elements/nodes.js`
file to TypeScript at
`packages/mermaid/src/rendering-util/rendering-elements/nodes.ts`
2024-10-18 01:51:53 +09:00
autofix-ci[bot]
6f506c30eb [autofix.ci] apply automated fixes 2024-10-17 16:04:56 +00:00
yari-dewalt
cf2c7f7f8c Update tests 2024-10-17 08:58:49 -07:00
yari-dewalt
6041729ae2 Add suport for start and end labels (cardinality) in elk renderer 2024-10-17 08:58:06 -07:00
yari-dewalt
b7ef8b2b5a Refactor to make code more clear 2024-10-17 08:57:03 -07:00
yari-dewalt
bfea287b44 Register and update classBox shape 2024-10-17 08:55:42 -07:00
Alois Klink
9afb181d06 refactor: correct shapes types
All of the shapes in
`packages/mermaid/src/rendering-util/rendering-elements/shapes/` use
`D3.Selection`s as input parameters and as return parameters.

Although, for some reason, passing them to roughjs seems to work?
2024-10-17 22:29:38 +09:00
Alois Klink
cfe7cce41d refactor: improve types of shapes
Instead of being a `Record<string, ShapeHandler>`, the type now knows
every key in the variable.
2024-10-17 18:59:43 +09:00
autofix-ci[bot]
33029c8bd2 [autofix.ci] apply automated fixes 2024-10-17 08:31:48 +00:00
Gabriel Silva
e54d10c706 docs(ecosystem): Add Zed extension zed-mermaid to community integrations 2024-10-17 10:21:15 +02:00
Yari DeWalt
86c7532f46 Merge branch 'develop' into update-class-diagram 2024-10-16 09:43:58 -07:00
eitsupi
8aa87c7091 docs(integrations): fix listing order 2024-10-16 21:36:26 +09:00
autofix-ci[bot]
e1024838e8 [autofix.ci] apply automated fixes 2024-10-16 03:52:48 +00:00
Yihui Xie
4cce43f0a1 Bullet lists should not be code blocks 2024-10-15 22:45:38 -05:00
autofix-ci[bot]
c69ab34eac [autofix.ci] apply automated fixes 2024-10-11 20:27:05 +00:00
Tomáš Hübelbauer
7353be3b16 Remove extraneous period
Other cells in the table do not end with one.
2024-10-11 22:21:23 +02:00
Knut Sveidqvist
02be63ed72 Refactoring and cleanup including updated unit tests 2024-10-11 08:48:04 -07:00
Sidharth Vinod
742531a2c2 Merge pull request #5951 from gbremond/docs/5949_update-icon-name-for-database
docs(architecture): update icon name for database in exemple
2024-10-11 06:12:42 +00:00
Knut Sveidqvist
991d403d7f Typescript fixes 2024-10-10 09:47:52 -07:00
Knut Sveidqvist
290c678dc7 Adding ticket handling 2024-10-10 08:23:58 -07:00
g.bremont-ext
2c31a599af docs(architecture): use right icon for database in exemple 2024-10-10 17:00:13 +02:00
autofix-ci[bot]
2655be0bad [autofix.ci] apply automated fixes 2024-10-10 14:21:46 +00:00
g.bremont-ext
4f97c8c70e docs(architecture): update icon name for database in exemple 2024-10-10 16:15:23 +02:00
Sidharth Vinod
f85977246c Merge pull request #5945 from mermaid-js/saurabh/doc-update
Updated doc for image and icon shapes
2024-10-10 16:46:02 +05:30
saurabhg772244
a4fd42214b Use relative URL in linking docs 2024-10-10 15:17:26 +05:30
saurabhg772244
ca885027d4 Moved position for icon config and added major version in icon docs 2024-10-10 14:15:16 +05:30
saurabhg772244
9f6f1e96e5 Added new doc for registering icon pack in mermaid 2024-10-10 13:11:54 +05:30
saurabhg772244
351ce30d8a Merge branch 'develop' of https://github.com/mermaid-js/mermaid into saurabh/doc-update 2024-10-10 12:26:14 +05:30
Sidharth Vinod
2933eb5c28 fix: Build options 2024-10-09 16:44:51 +05:30
saurabhg772244
09ecf76034 Updated doc for image and icon shapes 2024-10-09 15:34:12 +05:30
Sidharth Vinod
2b86750cb3 Merge pull request #5924 from mermaid-js/sidv/autoGenerateShapeDocs
Auto generate shape docs
2024-10-09 14:11:56 +05:30
Sidharth Vinod
b45224755e Merge branch 'develop' into sidv/autoGenerateShapeDocs 2024-10-09 13:41:43 +05:30
Sidharth Vinod
14524e1b0e docs: Optimise document generation
Co-authored-by: Alois Klink <alois@aloisklink.com>
2024-10-09 13:39:06 +05:30
Sidharth Vinod
efa47e3176 fix: Docs vitepress build 2024-10-09 13:18:01 +05:30
Sidharth Vinod
1bc45405e0 chore: Generate shape doc before verification 2024-10-09 13:18:01 +05:30
Sidharth Vinod
3720e1a894 docs: Auto generate shape docs
Not keen on how it's currently run, open to suggestions on better ways.
2024-10-09 13:18:00 +05:30
Sidharth Vinod
76138cf3b9 chore: Decouple build logic 2024-10-09 13:17:59 +05:30
Alois Klink
2506c29f6b Merge pull request #5937 from mermaid-js/saurabh/icon-shape-style-fix
Fix icon shape color for colored icons.
2024-10-09 07:47:34 +00:00
Sidharth Vinod
463ba6ed6d Merge pull request #5923 from mermaid-js/sidv/refactorShapes
Refactor shapes definition
2024-10-09 13:17:06 +05:30
saurabhg772244
17b783135f Added pnpm changeset 2024-10-09 12:55:37 +05:30
saurabhg772244
8df2729bd4 Merge branch 'saurabh/icon-shape-style-fix' of https://github.com/mermaid-js/mermaid into saurabh/icon-shape-style-fix 2024-10-09 12:16:05 +05:30
saurabhg772244
a001619464 Move MIT license just before icon definition 2024-10-09 12:15:23 +05:30
Saurabh Gore
3c5a19d297 Merge branch 'develop' into saurabh/icon-shape-style-fix 2024-10-09 11:12:56 +05:30
saurabhg772244
a98290a6e2 remove doc for icon shape 2024-10-09 11:08:27 +05:30
saurabhg772244
9bac464442 Use tropical-fish icon with MIT license in e2e tests 2024-10-09 10:41:37 +05:30
autofix-ci[bot]
11fb806992 [autofix.ci] apply automated fixes 2024-10-08 15:03:03 +00:00
Yari DeWalt
d424a4473a Merge branch 'develop' into update-class-diagram 2024-10-08 07:58:15 -07:00
saurabhg772244
4062813d44 Added docs for icon-shape 2024-10-08 17:45:53 +05:30
Sidharth Vinod
29b9d3069b Merge branch 'develop' into sidv/refactorShapes 2024-10-08 16:15:34 +05:30
Sidharth Vinod
f99e2c9366 fix: Add internalAliases
Co-authored-by: Alois Klink <alois@aloisklink.com>
2024-10-08 13:25:00 +05:30
Sidharth Vinod
51be99c9de fix: Remove duplicates, add documentation, rename legacyAlias
Co-authored-by: Alois Klink <alois@aloisklink.com>
2024-10-08 13:01:31 +05:30
saurabhg772244
d3308232e8 updated test case for icon shape 2024-10-07 16:11:31 +05:30
saurabhg772244
0b8f27e1d2 Updated icon shape to use css color style instead of stroke 2024-10-07 15:52:12 +05:30
Knut Sveidqvist
93f2c241b8 5432 WIP, rendering items 2024-10-06 19:22:07 +02:00
Knut Sveidqvist
fb44e769f2 5432 WIP, rendering sections 2024-10-06 18:34:24 +02:00
Knut Sveidqvist
fabdfd9ae8 5432 WIP, parsing works 2024-10-06 16:55:57 +02:00
Knut Sveidqvist
2d7686eb65 #5342 Initial commit 2024-10-04 16:03:55 +02:00
Sidharth Vinod
d9a26ff193 Merge branch 'develop' into sidv/refactorShapes 2024-10-04 14:44:31 +05:30
Sidharth Vinod
5a3e0ccc3d chore: rebuild 2024-10-04 02:33:45 +05:30
Sidharth Vinod
e2474049d8 Merge branch 'develop' into sidv/refactorShapes
* develop:
  chore: move handDrawnSeed to mermaidUrl function
2024-10-04 01:31:54 +05:30
Sidharth Vinod
cda48a7143 Merge branch 'develop' into sidv/refactorShapes
* develop:
  fix: Hard code handDrawnSeed
2024-10-04 00:46:01 +05:30
Sidharth Vinod
0ae4f97036 test: Update nodes test 2024-10-04 00:05:38 +05:30
Sidharth Vinod
218bfe1603 chore: Update import path 2024-10-03 22:13:00 +05:30
Sidharth Vinod
339c670839 docs: Update new shape registration doc 2024-10-03 22:12:41 +05:30
Sidharth Vinod
c5a19f6e85 chore: Organize shape handling 2024-10-03 22:08:30 +05:30
autofix-ci[bot]
719685b930 [autofix.ci] apply automated fixes 2024-10-03 08:54:12 +00:00
Michael Vorburger
0cbc000bfa docs: Fix inconsistency in Block Diagram doc example 2024-10-03 10:47:31 +02:00
yari-dewalt
91e3da1293 Update tests to take into account 'default' class 2024-09-24 10:47:26 -07:00
yari-dewalt
fa46267c14 Update documentation 2024-09-24 10:22:46 -07:00
yari-dewalt
10679297f9 Fix conditional logic 2024-09-24 09:35:34 -07:00
yari-dewalt
3e697e2ea4 Update and fix logic for lollipop interfaces 2024-09-24 09:28:17 -07:00
yari-dewalt
f031abcec2 Support lollipop interface feature 2024-09-23 12:19:22 -07:00
yari-dewalt
dd62322ae5 Add default class support 2024-09-23 10:09:05 -07:00
yari-dewalt
48e5dee963 Fix edge markers being flipped 2024-09-23 09:51:36 -07:00
yari-dewalt
bd0a00d373 Update config yaml so ClassDiagramConfig can be properly updated 2024-09-20 09:56:17 -07:00
yari-dewalt
3b8bd6b979 Include classRenderer.js to fix lint error 2024-09-20 09:13:21 -07:00
yari-dewalt
41f22864b4 Add adjustments for text and useHtmlLabels 2024-09-19 12:33:34 -07:00
yari-dewalt
6a99a1a40a Update HTML file and add rendering tests 2024-09-19 12:30:38 -07:00
yari-dewalt
1fcebab9ed Change cssDiagram parameter to 'classDiagram' 2024-09-18 13:22:21 -07:00
yari-dewalt
bed301a485 Update tests 2024-09-18 13:21:27 -07:00
yari-dewalt
31546b08ff Fix note text 2024-09-18 13:20:47 -07:00
yari-dewalt
25337dc453 Update text logic and change workaround for markdown 2024-09-18 12:54:32 -07:00
yari-dewalt
745495c2d5 Refactor and cleanup logic for classBox shape 2024-09-18 09:12:15 -07:00
yari-dewalt
94d2a0ee89 Cleanup 2024-09-18 09:11:25 -07:00
yari-dewalt
b105c7b35e Add node look and fix text attribute on class 2024-09-18 09:10:35 -07:00
yari-dewalt
149f991c1d Fix typo 2024-09-18 08:53:54 -07:00
yari-dewalt
a43ee60942 Add fixes to utils 2024-09-18 08:53:54 -07:00
yari-dewalt
5f11cd417f Add classBox shape 2024-09-18 08:53:54 -07:00
yari-dewalt
3782b69744 Add support for classDef statement 2024-09-18 08:53:54 -07:00
yari-dewalt
ca93280b17 Change types 2024-09-18 08:53:54 -07:00
yari-dewalt
2ec9f0af2e Add support for new renderer 2024-09-18 08:53:54 -07:00
yari-dewalt
3eb338df04 Add default option for hideEmptyBox in config 2024-09-18 08:53:54 -07:00
yari-dewalt
b7790ba6b5 Add diagrams for testing 2024-09-18 08:53:54 -07:00
yari-dewalt
b67c931f18 Update snapshot to include new data 2024-09-18 08:53:54 -07:00
yari-dewalt
97186504b5 Add classifier test diagram 2024-09-18 08:53:54 -07:00
yari-dewalt
bd4ca9af1b Add support for classifiers in text attribute 2024-09-18 08:53:54 -07:00
yari-dewalt
9ac20a5cb8 Add styling compatibility to shape 2024-09-18 08:53:54 -07:00
yari-dewalt
20a58d6dbb Add special handling to avoid markdown to HTML issues 2024-09-18 08:53:54 -07:00
yari-dewalt
191e4217fc Adjust classNode text when label is set 2024-09-18 08:53:54 -07:00
yari-dewalt
8745daa701 Add handling for escape type 2024-09-18 08:53:54 -07:00
yari-dewalt
27f7b615cc Add more tests / samples 2024-09-18 08:53:54 -07:00
yari-dewalt
e3c426de86 Initial work on updating to new renderer 2024-09-18 08:53:54 -07:00
162 changed files with 9229 additions and 1865 deletions

View File

@@ -1,3 +1,9 @@
export interface PackageOptions {
name: string;
packageName: string;
file: string;
}
/**
* Shared common options for both ESBuild and Vite
*/
@@ -27,4 +33,4 @@ export const packageOptions = {
packageName: 'mermaid-layout-elk',
file: 'layouts.ts',
},
} as const;
} as const satisfies Record<string, PackageOptions>;

View File

@@ -19,6 +19,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [
'xyChart',
'requirement',
'mindmap',
'kanban',
'timeline',
'gitGraph',
'c4',

View File

@@ -1,5 +0,0 @@
---
'mermaid': patch
---
Add missing TypeScript dependencies

View File

@@ -12,6 +12,7 @@ gantt
gitgraph
gzipped
handDrawn
kanban
knsv
Knut
marginx

View File

@@ -8,7 +8,10 @@ import { defaultOptions, getBuildConfig } from './util.js';
const shouldVisualize = process.argv.includes('--visualize');
const buildPackage = async (entryName: keyof typeof packageOptions) => {
const commonOptions: MermaidBuildOptions = { ...defaultOptions, entryName } as const;
const commonOptions: MermaidBuildOptions = {
...defaultOptions,
options: packageOptions[entryName],
} as const;
const buildConfigs: MermaidBuildOptions[] = [
// package.mjs
{ ...commonOptions },
@@ -40,7 +43,7 @@ const buildPackage = async (entryName: keyof typeof packageOptions) => {
continue;
}
const fileName = Object.keys(metafile.outputs)
.find((file) => !file.includes('chunks') && file.endsWith('js'))
.find((file) => !file.includes('chunks') && file.endsWith('js'))!
.replace('dist/', '');
// Upload metafile into https://esbuild.github.io/analyze/
await writeFile(`stats/${fileName}.meta.json`, JSON.stringify(metafile));

View File

@@ -9,13 +9,18 @@ import { generateLangium } from '../.build/generateLangium.js';
import { defaultOptions, getBuildConfig } from './util.js';
const configs = Object.values(packageOptions).map(({ packageName }) =>
getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: packageName })
getBuildConfig({
...defaultOptions,
minify: false,
core: false,
options: packageOptions[packageName],
})
);
const mermaidIIFEConfig = getBuildConfig({
...defaultOptions,
minify: false,
core: false,
entryName: 'mermaid',
options: packageOptions.mermaid,
format: 'iife',
});
configs.push(mermaidIIFEConfig);

View File

@@ -3,7 +3,7 @@ import { fileURLToPath } from 'url';
import type { BuildOptions } from 'esbuild';
import { readFileSync } from 'fs';
import jsonSchemaPlugin from './jsonSchemaPlugin.js';
import { packageOptions } from '../.build/common.js';
import type { PackageOptions } from '../.build/common.js';
import { jisonPlugin } from './jisonPlugin.js';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
@@ -13,10 +13,10 @@ export interface MermaidBuildOptions extends BuildOptions {
core: boolean;
metafile: boolean;
format: 'esm' | 'iife';
entryName: keyof typeof packageOptions;
options: PackageOptions;
}
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName'> = {
export const defaultOptions: Omit<MermaidBuildOptions, 'entryName' | 'options'> = {
minify: false,
metafile: false,
core: false,
@@ -52,9 +52,14 @@ const getFileName = (fileName: string, { core, format, minify }: MermaidBuildOpt
};
export const getBuildConfig = (options: MermaidBuildOptions): BuildOptions => {
const { core, entryName, metafile, format, minify } = options;
const {
core,
metafile,
format,
minify,
options: { name, file, packageName },
} = options;
const external: string[] = ['require', 'fs', 'path'];
const { name, file, packageName } = packageOptions[entryName];
const outFileName = getFileName(name, options);
const output: BuildOptions = buildOptions({
absWorkingDir: resolve(__dirname, `../packages/${packageName}`),

View File

@@ -133,7 +133,7 @@ jobs:
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_PARALLEL: true
ARGOS_PARALLEL_TOTAL: ${{ strategy.job-total }}
ARGOS_PARALLEL_INDEX: ${{ strategy.job-index }}
ARGOS_PARALLEL_INDEX: ${{ matrix.containers }}
SPLIT: ${{ strategy.job-total }}
SPLIT_INDEX: ${{ strategy.job-index }}
SPLIT_FILE: 'cypress/timings.json'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -124,3 +124,20 @@ describe('Test iconShape with different h', () => {
imgSnapshotTest(flowchartCode);
});
});
describe('Test colored iconShape', () => {
it('with no styles', () => {
let flowchartCode = `flowchart TB\n`;
const icon = 'fluent-emoji:tropical-fish';
flowchartCode += ` nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`;
imgSnapshotTest(flowchartCode);
});
it('with styles', () => {
let flowchartCode = `flowchart TB\n`;
const icon = 'fluent-emoji:tropical-fish';
flowchartCode += ` nA --> nAA@{ icon: '${icon}', form: 'square', label: 'icon with color' }\n`;
flowchartCode += ` style nAA fill:#f9f,stroke:#333,stroke-width:4px \n`;
imgSnapshotTest(flowchartCode);
});
});

View File

@@ -0,0 +1,136 @@
import { imgSnapshotTest } from '../../helpers/util.ts';
describe('Kanban diagram', () => {
it('1: should render a kanban with a single section', () => {
imgSnapshotTest(
`kanban
id1[Todo]
docs[Create Documentation]
docs[Create Blog about the new diagram]
`,
{}
);
});
it('2: should render a kanban with multiple sections', () => {
imgSnapshotTest(
`kanban
id1[Todo]
docs[Create Documentation]
id2
docs[Create Blog about the new diagram]
`,
{}
);
});
it('3: should render a kanban with a single wrapping node', () => {
imgSnapshotTest(
`kanban
id1[Todo]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping]
`,
{}
);
});
it('4: should handle the height of a section with a wrapping node at the end', () => {
imgSnapshotTest(
`kanban
id1[Todo]
id2[One line]
id3[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping]
`,
{}
);
});
it('5: should handle the height of a section with a wrapping node at the top', () => {
imgSnapshotTest(
`kanban
id1[Todo]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping]
id3[One line]
`,
{}
);
});
it('6: should handle the height of a section with a wrapping node in the middle', () => {
imgSnapshotTest(
`kanban
id1[Todo]
id2[One line]
id3[Title of diagram is more than 100 chars when user duplicates diagram with 100 char, wrapping]
id4[One line]
`,
{}
);
});
it('6: should handle assigments', () => {
imgSnapshotTest(
`kanban
id1[Todo]
docs[Create Documentation]
id2[In progress]
docs[Create Blog about the new diagram]@{ assigned: 'knsv' }
`,
{}
);
});
it('7: should handle prioritization', () => {
imgSnapshotTest(
`kanban
id2[In progress]
vh[Very High]@{ priority: 'Very High' }
h[High]@{ priority: 'High' }
m[Default priority]
l[Low]@{ priority: 'Low' }
vl[Very Low]@{ priority: 'Very Low' }
`,
{}
);
});
it('7: should handle external tickets', () => {
imgSnapshotTest(
`kanban
id1[Todo]
docs[Create Documentation]
id2[In progress]
docs[Create Blog about the new diagram]@{ ticket: MC-2037 }
`,
{}
);
});
it('8: should handle assignments, prioritization and tickets ids in the same item', () => {
imgSnapshotTest(
`kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
`,
{}
);
});
it('10: Full example', () => {
imgSnapshotTest(
`---
config:
kanban:
ticketBaseUrl: 'https://abc123.atlassian.net/browse/#TICKET#'
---
kanban
id1[Todo]
docs[Create Documentation]
docs[Create Blog about the new diagram]
id7[In progress]
id6[Create renderer so that it works in all cases. We also add som extra text here for testing purposes. And some more just for the extra flare.]
id8[Design grammar]@{ assigned: 'knsv' }
id9[Ready for deploy]
id10[Ready for test]
id11[Done]
id5[define getData]
id2[Title of diagram is more than 100 chars when user duplicates diagram with 100 char]@{ ticket: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id4[Create parsing tests]@{ ticket: MC-2038, assigned: 'K.Sveidqvist', priority: 'High' }
id66[last item]@{ priority: 'Very Low', assigned: 'knsv' }
id12[Can't reproduce]
`,
{}
);
});
});

View File

@@ -10,6 +10,10 @@
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
@@ -34,6 +38,7 @@
/* background: rgb(221, 208, 208); */
/* background: #333; */
font-family: 'Arial';
/* color: white; */
/* font-size: 18px !important; */
}
@@ -83,353 +88,133 @@
</head>
<body>
<div class="flex">
<pre id="diagram" class="mermaid2">
---
title: hello2
config:
look: handDrawn
layout: elk
elk:
<!-- nodePlacementStrategy: INTERACTIVE -->
<!-- mergeEdges: true -->
---
stateDiagram-v2
direction LR
accTitle: An idealized Open Source supply-chain graph
%%
state "🟦 Importer" as author_importer
state "🟥 Supplier, Owner" as author_owner
state "🟨🟥 Maintainer, Author\n🟨 Custodian" as author
state "🟩 Distributor" as repository_distributor
state "🟦 Importer" as language_importer
state "🟦🟨 Packager" as language_packager
state "🟦🟨 OSS Steward" as language_steward
state "🟨 Curator" as language_curator
state "🟩 Distributor" as language_distributor
state "🟦 Contributor" as contributor
state "🟦 Importer" as package_importer
state "🟨 Patcher" as package_patcher
state "🟨🟦 Builder\n🟨🟦 Packager\n🟨🟦 Containerizer" as package_packager
state "🟨 Curator" as package_curator
state "🟩 Distributor" as package_distributor
state "🟦 Importer" as integrator_importer
state "🟥 Supplier, Manufacturer, Owner" as integrator_owner
state "🟦🟨🟥 Integrator, Developer" as integrator_developer
state "🟩🟨 SBOM Redactor\n🟩 Publisher" as integrator_publisher
state "🟦🟨 Builder" as integrator_builder
state "🟨 Deployer" as deployer
state "🟦 Vuln. Checker" as integrator_checker
state "🟩🟨 SBOM Redactor" as redactor
state "🟦 Consumer\n🟦 User" as consumer
state "🟦 Auditor" as auditor_internal
state "🟦 Auditor" as auditor_external
%%
classDef createsSBOM stroke:red,stroke-width:3px;
classDef updatesSBOM stroke:yellow,stroke-width:3px;
classDef assemblesSBOM stroke:yellow,stroke-width:3px;
classDef distributesSBOM stroke:green,stroke-width:3px;
classDef verifiesSBOM stroke:#07f,stroke-width:3px;
%%
class author_importer verifiesSBOM
class author_owner createsSBOM
class manufacturer_owner createsSBOM
class author assemblesSBOM
class package_importer verifiesSBOM
class package_patcher updatesSBOM
class package_packager assemblesSBOM
class package_curator distributesSBOM
class package_distributor distributesSBOM
class language_importer verifiesSBOM
class language_packager assemblesSBOM
class language_steward updatesSBOM
class language_curator distributesSBOM
class language_distributor distributesSBOM
class repository_distributor distributesSBOM
class integrator_importer verifiesSBOM
class integrator_owner createsSBOM
class integrator_developer assemblesSBOM
class integrator_publisher distributesSBOM
class integrator_builder assemblesSBOM
class integrator_checker verifiesSBOM
class deployer assemblesSBOM
class redactor distributesSBOM
class auditor_internal verifiesSBOM
class auditor_external verifiesSBOM
state "Maintainer Environment" as environment_maintainer {
[*] --> author_importer
[*] --> author
author_importer --> author
author_owner --> author
author --> language_packager
}
[*] --> environment_maintainer
state "Language Ecosystem" as ecosystem_lang {
[*] --> language_importer
[*] --> language_steward
[*] --> language_curator
[*] --> language_distributor
language_importer --> language_distributor
language_importer --> language_curator
language_steward --> language_curator
language_curator --> language_distributor
}
language_packager --> ecosystem_lang
ecosystem_lang --> ecosystem_lang
state "Public Collaboration Ecosystem" as ecosystem_repo {
[*] --> repository_distributor
}
author --> ecosystem_repo
ecosystem_repo --> author
repository_distributor --> contributor
contributor --> repository_distributor
state "Package Ecosystem" as ecosystem_package {
[*] --> package_importer
[*] --> package_packager
[*] --> package_patcher
package_importer --> package_patcher
package_importer --> package_packager
package_patcher --> package_packager
package_packager --> package_curator
package_packager --> package_distributor
package_curator --> package_distributor
}
repository_distributor --> ecosystem_package
language_distributor --> ecosystem_package
ecosystem_package --> ecosystem_package
state "Integrator Environment" as environment_integrator {
[*] --> integrator_developer
[*] --> integrator_importer
integrator_importer --> integrator_developer
integrator_owner --> integrator_developer
integrator_builder --> integrator_publisher
integrator_developer --> integrator_checker
integrator_checker --> integrator_developer
auditor_internal --> integrator_developer
integrator_developer --> integrator_builder
integrator_developer --> auditor_internal
}
repository_distributor --> environment_integrator
language_distributor --> environment_integrator
package_distributor --> environment_integrator
state "Production Environment" as environment_prod {
[*] --> deployer
deployer --> redactor
}
integrator_publisher --> [*]
integrator_developer --> environment_prod
integrator_builder --> environment_prod
integrator_publisher --> environment_prod
deployer --> auditor_external
deployer --> consumer
redactor --> consumer
</pre>
<pre id="diagram" class="mermaid2">
---
config:
look: neo
---
flowchart RL
subgraph " "
A5@{ shape: manual-file, label: "a label"}
B5@{ shape: manual-input, label: "a label" }
C5@{ shape: mul-doc, label: "a label" }
D5@{ shape: mul-proc, label: "a label" }
E5@{ shape: paper-tape, label: "a label" }
B3@{ shape: das, label: "a label" }
C3@{ shape: disk, label: "a label" }
D4@{ shape: lin-doc, label: "a label" }
E4@{ shape: loop-limit, label: "a label" }
end
subgraph " "
B6@{ shape: summary, label: "a label" }
C6@{ shape: tag-we-rect, label: "a label" }
D6@{ shape: tag-rect, label: "a label" }
A2@{ shape: fork}
B2@{ shape: hourglass }
C2@{ shape: comment, label: "I am a comment" }
D2@{ shape: bolt }
D3@{ shape: disp, label: "a label" }
C4@{ shape: junction, label: "a label" }
A4@{ shape: extract, label: "a label"}
B52[a fr]@{ shape: fr }
end
subgraph " "
A1@{ shape: text, label: This is a textblock}
B1@{ shape: card, label: "a label" }
C1@{ shape: lined-proc, label: "a label" }
D1@{ shape: start, label: "a label" }
E1@{ shape: stop, label: "a label" }
E2@{ shape: doc, label: "a label" }
A6@{ shape: stored-data, label: "a label"}
A3@{ shape: delay, label: "a label" }
E3@{ shape: div-proc, label: "a label" }
B4[a label]@{ shape: win-pane }
end
</pre>
<pre id="diagram" class="mermaid2">
---
title: hello2
config:
look: handDrawn
elk:
<!-- nodePlacementStrategy: SIMPLE -->
---
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TD
A([Start]) -->|go to booking page| B("select
ISBS booking no")
A --> QQ{cancel booking}
A --> RR{no show}
A --> SS{change booking}
B -->C(wmpay_request_payment.request_type= 'partial',
wmpay_request_payment.status= 'paid',
pos_booking.booking_status= partial and 'full_deposit')
style C text-align:left
C -->D{manage booking}
D -->|cancel|E[ระบบแสดงช่องให้กรอกเหตุผล]
E -->F{กดปุ่ม 'cancel' หรือไม่}
F -->|Yes|G[ระบบบันทึกค่าใหม่
และไม่สามารถแก้ไขข้อมูลได้]
F -->|No|H[กดปุ่ม 'close']
H -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
G -->|ระบบส่งข้อมูล|I[(POS_database)]
I -->|pos_booking.booking_status='cancel'|Z([End])
D -->|no show|J[ระบบแสดงช่องให้กรอกเหตุผล]
J -->K{กดปุ่ม 'noshow' หรือไม่}
K -->|Yes|L[ระบบสร้างใบเสร็จอัตโนมัติ
Product_id: 439,
ItemName: no show]
style L text-align:left
K -->|No|O[กดปุ่ม 'close']
O -->|ระบบไม่เปลี่ยนแปลงข้อมูล|Z
L -->M[ระบบบันทึกค่าใหม่]
M -->|ระบบส่งข้อมูล|N[(POS_database)]
N -->|pos_booking.booking_status=noshow|Z
</pre>
<pre id="diagram" class="mermaid2">
---
title: hello2
config:
look: handDrawn
layout: dagre
elk:
nodePlacementStrategy: BRANDES_KOEPF
---
flowchart
A --> A
subgraph A
B --> B
subgraph B
C
end
end
</pre
>
<pre id="diagram" class="mermaid2">
---
config:
look: handdrawn
flowchart:
htmlLabels: true
---
flowchart
A[I am a long text, where do I go??? handdrawn - true]
</pre
>
</div>
<div class="flex">
<pre id="diagram" class="mermaid2">
---
config:
flowchart:
htmlLabels: false
---
flowchart
A[I am a long text, where do I go??? classic - false]
</pre
>
<pre id="diagram" class="mermaid2">
---
config:
flowchart:
htmlLabels: true
---
flowchart
A[I am a long text, where do I go??? classic - true]
</pre
>
</div>
<pre id="diagram2" class="mermaid2">
flowchart LR
id1(Start)-->id2(Stop)
style id1 fill:#f9f,stroke:#333,stroke-width:4px
style id2 fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5
</pre>
<pre id="diagram3" class="mermaid2">
flowchart LR
A:::foo & B:::bar --> C:::foobar
classDef foo stroke:#f00
classDef bar stroke:#0f0
classDef ash color:red
class C ash
style C stroke:#00f, fill:black
</pre>
<pre id="diagram4" class="mermaid2">
stateDiagram
A:::foo
B:::bar --> C:::foobar
classDef foo stroke:#f00
classDef bar stroke:#0f0
style C stroke:#00f, fill:black, color:white
</pre>
<pre id="diagram4" class="mermaid">
flowchart TB
A@{
label: "aksljhf kasjdh"
}
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
A:::AClass
classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid">
flowchart LR
nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
style A fill:#f9f,stroke:#333,stroke-width:4px
</pre>
<pre id="diagram4" class="mermaid2">
kanban
id2[In progress]
docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
</pre>
<pre id="diagram4" class="mermaid2">
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#TICKET#'
# sectionWidth: 300
---
kanban
Todo
[Create Documentation]
docs[Create Blog about the new diagram]
id7[In progress]
id6[Create renderer so that it works in all cases. We also add som 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: MC-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: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
const staticBellIconPack = {
prefix: 'fa6-regular',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',
width: 448,
},
},
width: 512,
height: 512,
};
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
},
{
name: 'fa',
loader: () => staticBellIconPack,
},
]);
mermaid.registerLayoutLoaders(layouts);
mermaid.parseError = function (err, hash) {
console.error('Mermaid error: ', err);
@@ -439,6 +224,8 @@ flowchart TB
};
mermaid.initialize({
// theme: 'base',
// theme: 'default',
// theme: 'forest',
// handDrawnSeed: 12,
// look: 'handDrawn',
// 'elk.nodePlacement.strategy': 'NETWORK_SIMPLEX',
@@ -455,8 +242,11 @@ flowchart TB
noteFontFamily: 'courier',
messageFontFamily: 'courier',
},
kanban: {
htmlLabels: false,
},
fontSize: 12,
logLevel: 3,
logLevel: 0,
securityLevel: 'loose',
});
function callback() {

File diff suppressed because one or more lines are too long

663
cypress/platform/yari.html Normal file
View File

@@ -0,0 +1,663 @@
<html>
<body>
<h1 class="header">Class Nodes</h1>
<div class="node-showcase">
<div class="test">
<h2>Basic Class</h2>
<pre class="mermaid">
---
config:
htmlLabels: false
---
classDiagram
class _Duck_ {
+String beakColor
_+_swim_()a_
__+quack() test__
}
</pre>
</div>
<div class="test">
<h2>Basic Class</h2>
<pre class="mermaid">
---
config:
htmlLabels: false
---
classDiagram
class Class10:::exClass2 {
int[] id
List~int~ ids
test(List~int~ ids) List~bool~
testArray() bool[]
}
</pre>
</div>
<div class="test">
<h2>Basic Class</h2>
<pre class="mermaid">
flowchart TD
Start --> Stop
</pre>
</div>
<div class="test">
<h2>Complex Class</h2>
<pre class="mermaid">
classDiagram
class Square~Shape~{
int id
List~int~ position
setPoints(List~int~ points)
getPoints() List~int~
}
Square : -List~string~ messages
Square : +setMessages(List~string~ messages)
Square : +getMessages() List~string~
Square : +getDistanceMatrix() List~List~int~~
</pre
>
</div>
<div class="test">
<h2>No Attributes</h2>
<pre class="mermaid">
classDiagram
class Duck {
+swim()
+quack()
}
</pre>
</div>
<div class="test">
<h2>No Methods</h2>
<pre class="mermaid">
classDiagram
class Duck {
+String beakColor
}
</pre>
</div>
<div class="test">
<h2>Only Class Name</h2>
<p>Empty line as attribute</p>
<pre class="mermaid">
---
config:
class:
hideEmptyMembersBox: false
---
classDiagram
class Duck {
}
</pre>
</div>
<div class="test">
<h2>Visibility and Types</h2>
<p>(Further tilde testing)</p>
<div class="mermaid">
classDiagram class Duck { ~interface~~~ +String beakColor #swim() ~quack()~~~
-test()~~~~~~~ +deposit(amount) bool }
</div>
</div>
<div class="test">
<h2>Additional Classifiers</h2>
<p>(* Abstract | $ Static)</p>
<div class="mermaid">
classDiagram class Square~Shape~ { int id* List~int~ position* setPoints(List~int~points)*
getPoints()* List~int~ } Square : -List~string~ messages$ Square :
+setMessages(List~string~ messages)* Square : +getMessages()$ List~string~ Square :
+getDistanceMatrix() List~List~int~~$
</div>
</div>
<div class="test">
<h2>Label</h2>
<pre class="mermaid">
classDiagram
class Animal~test~["Animal with a label"]
</pre>
</div>
<div class="test">
<h2>Spacing</h2>
<p>(Fix ensures consistent spacing rules)</p>
<p>(No space or single space?)</p>
<pre class="mermaid">
classDiagram
class ClassName {
-attribute:type
- attribute : type
test
+ GetAttribute() type
+ GetAttribute() type
}
</pre>
</div>
<div class="test">
<h2>Annotation</h2>
<pre class="mermaid">
classDiagram
class Shape
&lt;&lt;interface&gt;&gt; Shape
Shape : noOfVertices
Shape : draw()
</pre>
</div>
<div class="test">
<h2>Long Class Name Text</h2>
<pre class="mermaid">
classDiagram
class ThisIsATestForALongClassName {
&lt;&lt;interface&gt;&gt;
noOfLetters
delete()
}
</pre>
</div>
<div class="test">
<h2>Long Annotation Text</h2>
<pre class="mermaid">
classDiagram
class Shape
&lt;&lt;superlongannotationtext&gt;&gt; Shape
Shape : noOfVertices
Shape : draw()
</pre>
</div>
<div class="test">
<h2>Long Member Text</h2>
<pre class="mermaid">
classDiagram
class Shape
&lt;&lt;interface&gt;&gt; Shape
Shape : noOfVertices
Shape : longtexttestkeepgoingandgoing
Shape : draw()
</pre>
</div>
<div class="test">
<h2>Link</h2>
<pre class="mermaid">
classDiagram
class Shape
link Shape "https://www.github.com" "This is a tooltip for a link"
</pre>
</div>
<div class="test">
<h2>Click</h2>
<pre class="mermaid">
classDiagram
class Shape
click Shape href "https://www.github.com" "This is a tooltip for a link"
</pre>
</div>
<div class="test">
<h2>Hand Drawn</h2>
<pre class="mermaid">
---
config:
look: handDrawn
htmlLabels: true
---
classDiagram
class Hand {
+String beakColor
+swim()
+quack()
}
style Hand fill:#f9f,stroke:#29f,stroke-width:2px
</pre>
</div>
<div class="test">
<h2>Neutral Theme</h2>
<pre class="mermaid">
---
config:
theme: neutral
---
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
</pre>
</div>
<div class="test">
<h2>Dark Theme</h2>
<pre class="mermaid">
---
config:
theme: dark
---
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
</pre>
</div>
<div class="test">
<h2>Forest Theme</h2>
<pre class="mermaid">
---
config:
theme: forest
---
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
</pre>
</div>
<div class="test">
<h2>Base Theme</h2>
<pre class="mermaid">
---
config:
theme: base
---
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
</pre>
</div>
<div class="test">
<h2>Custom Theme</h2>
<pre class="mermaid">
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#BB2528',
'primaryTextColor': '#fff',
'primaryBorderColor': '#7C0000',
'lineColor': '#F83d29',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}
}
}%%
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
Duck--Dog
</pre>
</div>
<div class="test">
<h2>Styling within Diagram</h2>
<pre class="mermaid">
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
style Duck fill:#f9f,stroke:#333,stroke-width:8px
</pre>
</div>
<div class="test">
<h2>Styling with classDef Statement</h2>
<pre class="mermaid">
classDiagram
class Duck:::bold {
+String beakColor
+swim()
+quack()
}
class Dog {
+int numTeeth
+bark()
}
cssClass "Duck,Dog" pink
classDef pink fill:#f9f
classDef default color:#f1e
classDef bold stroke:#333,stroke-width:6px,color:#fff
</pre>
</div>
<div class="test">
<h2>Styling with Class in Stylesheet</h2>
<pre class="mermaid">
classDiagram
class Duck {
+String beakColor
+swim()
+quack()
}
class Duck:::styleClass
</pre>
</div>
</div>
<h1 class="header">Diagram Testing</h1>
<div class="diagram-showcase">
<div class="test">
<h2>Class Nodes Only</h2>
<pre class="mermaid">
---
title: Animal example
---
classDiagram
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()
}
</pre>
</div>
<div class="test">
<h2>Class Nodes LR</h2>
<pre class="mermaid">
---
title: Animal example
---
classDiagram
direction LR
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()
}
</pre>
</div>
<div class="test">
<h2>Relations</h2>
<pre class="mermaid">
classDiagram
classA <|-- classB
classC *-- classD
classE o-- classF
classG <-- classH
classI -- classJ
classK <.. classL
classM <|.. classN
classO .. classP
</pre>
</div>
<div class="test">
<h2>Two Way Relation</h2>
<pre class="mermaid">
classDiagram
class Animal {
int size
walk()
}
class Zebra {
int size
walk()
}
Animal o--|> Zebra
</pre>
</div>
<div class="test">
<h2>Relations with Labels</h2>
<pre class="mermaid">
classDiagram
classA <|-- classB : implements
classC *-- classD : composition
classE o-- classF : aggregation
</pre>
</div>
<div class="test">
<h2>Cardinality / Multiplicity</h2>
<pre class="mermaid">
classDiagram
Customer "1" --> "*" Ticket
Student "1" --> "1..*" Course
Galaxy --> "many" Star : Contains
</pre>
</div>
<div class="test">
<h2>Complex Relations with Theme</h2>
<pre class="mermaid">
---
config:
theme: forest
look: handDrawns
layout: elk
---
classDiagram
direction RL
class Student {
-idCard : IdCard
}
class IdCard{
-id : int
-name : string
}
class Bike{
-id : int
-name : string
}
Student "1" o--o "1" IdCard : carries
Student "1" o--o "1" Bike : rides
</pre>
</div>
<div class="test">
<h2>Notes</h2>
<pre class="mermaid">
classDiagram
note "This is a general note"
note for MyClass "This is a note for a class"
class MyClass
</pre>
</div>
<div class="test">
<h2>Namespaces</h2>
<pre class="mermaid">
classDiagram
namespace BaseShapes {
class Triangle
class Rectangle {
double width
double height
}
}
</pre>
</div>
<div class="test">
<h2>Namespaces</h2>
<pre class="mermaid">
---
config:
layout: elk
---
classDiagram
namespace Namespace1 {
class C1
class C2
}
C1 --> C2
class C3
class C4
</pre>
</div>
<div class="test">
<h2>Full Example</h2>
<pre class="mermaid">
---
title: Animal example
config:
layout: dagre
---
classDiagram
note "From Duck till Zebra"
Animal <|--|> Duck
note for Duck "can fly<br>can swim<br>can dive<br>can help in debugging"
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()
}
cssClass "Duck" test
classDef test fill:#f71
%%classDef default fill:#f93
</pre>
</div>
<div class="test">
<h2>Full Example</h2>
<pre class="mermaid">
---
config:
theme: forest
look: handDrawn
---
classDiagram
note for Outside "Note testing"
namespace Test {
class Outside
}
namespace BaseShapes {
class Triangle
class Rectangle {
double width
double height
}
}
Outside <|--|> Rectangle
style Triangle fill:#f9f,stroke:#333,stroke-width:4px
</pre>
</div>
<div class="test">
<pre class="mermaid">
---
config:
look: handDrawn
layout: elk
---
classDiagram
Class01 "1" <|--|> "*" AveryLongClass : Cool
&lt;&lt;interface&gt;&gt; Class01
Class03 "1" *-- "*" Class04
Class05 "1" o-- "many" Class06
Class07 "1" .. "*" Class08
Class09 "1" --> "*" C2 : Where am i?
Class09 "*" --* "*" C3
Class09 "1" --|> "1" Class07
NewClass ()--() Class04
Class09 <|--|> AveryLongClass
Class07 : equals()
Class07 : Object[] elementData
Class01 : size()
Class01 : int chimp
Class01 : int gorilla
Class08 "1" <--> "*" C2: Cool label
class Class10 {
&lt;&lt;service&gt;&gt;
int id
test()
}
Class10 o--o AveryLongClass
Class10 <--> Class07
</pre>
</div>
<div class="test">
<pre class="mermaid">
classDiagram
test ()--() test2
</pre>
</div>
</div>
<script type="module">
import mermaid from './mermaid.esm.mjs';
import layouts from './mermaid-layout-elk.esm.mjs';
mermaid.registerLayoutLoaders(layouts);
mermaid.parseError = function (err, hash) {
console.error('Mermaid error: ', err);
};
mermaid.initialize();
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
</script>
</body>
<style>
.header {
text-decoration: underline;
text-align: center;
}
.node-showcase {
display: grid;
grid-template-columns: 1fr 1fr;
}
.test {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
}
.test > h2 {
margin: 0;
text-align: center;
}
.test > p {
margin-top: -6px;
color: gray;
}
.diagram-showcase {
display: grid;
grid-template-columns: 1fr;
}
.styleClass > * > path {
fill: #ff0000 !important;
stroke: #ffff00 !important;
stroke-width: 4px !important;
stroke-dasharray: 2 !important;
}
</style>
</html>

View File

@@ -101,7 +101,7 @@ To add a new shape:
- **Example**:
```typescript
import { Node, RenderOptions } from '../../types.d.ts';
import { Node, RenderOptions } from '../../types.ts';
export const myNewShape = async (
parent: SVGAElement,
@@ -117,7 +117,7 @@ To add a new shape:
### 2. Register the Shape
- **Register the shape**: Add your shape to the `shapes` object in the main shapes module. This allows your shape to be recognized and used within the system.
- **Register the shape**: Add your shape to the `shapes` object in the [main shapes module](../rendering-util/rendering-elements/shapes.ts). This allows your shape to be recognized and used within the system.
- **Example**:
@@ -126,9 +126,14 @@ To add a new shape:
const shapes = {
...,
'my-new-shape': myNewShape,
// Shortened alias (if any).
'm-nsh': myNewShape
{
semanticName: 'My Shape',
name: 'Shape Name',
shortName: '<short-name>',
description: '<Description for the shape>',
aliases: ['<alias-one>', '<al-on>', '<alias-two>', '<al-two>'],
handler: myNewShape,
},
};
```

55
docs/config/icons.md Normal file
View File

@@ -0,0 +1,55 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/icons.md](../../packages/mermaid/src/docs/config/icons.md).
# Registering icon pack in mermaid
The icon packs available can be found at [icones.js.org](https://icones.js.org/).
We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram.
Using JSON file directly from CDN:
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
},
]);
```
Using packages and a bundler:
```bash
npm install @iconify-json/logos@1
```
With lazy loading
```js
import mermaid from 'mermaid';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () => import('@iconify-json/logos').then((module) => module.icons),
},
]);
```
Without lazy loading
```js
import mermaid from 'mermaid';
import { icons } from '@iconify-json/logos';
mermaid.registerIconPacks([
{
name: icons.prefix, // To use the prefix defined in the icon pack
icons,
},
]);
```

View File

@@ -20,7 +20,7 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:125](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L125)
[packages/mermaid/src/rendering-util/types.ts:144](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L144)
---
@@ -30,7 +30,7 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:124](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L124)
[packages/mermaid/src/rendering-util/types.ts:143](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L143)
---
@@ -40,4 +40,4 @@
#### Defined in
[packages/mermaid/src/rendering-util/types.ts:123](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L123)
[packages/mermaid/src/rendering-util/types.ts:142](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/rendering-util/types.ts#L142)

View File

@@ -49,7 +49,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
---
@@ -59,7 +59,7 @@ This matters if you are using base tag settings.
#### Defined in
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
---
@@ -121,7 +121,7 @@ should not change unless content is changed.
#### Defined in
[packages/mermaid/src/config.type.ts:201](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L201)
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
---
@@ -183,7 +183,7 @@ See <https://developer.mozilla.org/en-US/docs/Web/CSS/font-family>
#### Defined in
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
---
@@ -217,7 +217,7 @@ If set to true, ignores legacyMathML.
#### Defined in
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
[packages/mermaid/src/config.type.ts:197](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L197)
---
@@ -253,6 +253,16 @@ Defines the seed to be used when using handDrawn look. This is important for the
---
### kanban
`Optional` **kanban**: `KanbanDiagramConfig`
#### Defined in
[packages/mermaid/src/config.type.ts:196](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L196)
---
### layout
`Optional` **layout**: `string`
@@ -310,7 +320,7 @@ Defines which main look to use for the diagram.
#### Defined in
[packages/mermaid/src/config.type.ts:204](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L204)
[packages/mermaid/src/config.type.ts:205](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L205)
---
@@ -354,7 +364,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
[packages/mermaid/src/config.type.ts:200](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L200)
---
@@ -394,7 +404,7 @@ The maximum allowed size of the users text diagram
#### Defined in
[packages/mermaid/src/config.type.ts:198](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L198)
[packages/mermaid/src/config.type.ts:199](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L199)
---
@@ -465,7 +475,7 @@ This is useful when you want to control how to handle syntax errors in your appl
#### Defined in
[packages/mermaid/src/config.type.ts:210](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L210)
[packages/mermaid/src/config.type.ts:211](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L211)
---
@@ -518,7 +528,7 @@ You may also use `themeCSS` to override this value.
#### Defined in
[packages/mermaid/src/config.type.ts:202](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L202)
[packages/mermaid/src/config.type.ts:203](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L203)
---

View File

@@ -19,4 +19,4 @@ The `parseError` function will not be called.
#### Defined in
[packages/mermaid/src/types.ts:56](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L56)
[packages/mermaid/src/types.ts:59](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L59)

View File

@@ -18,7 +18,7 @@ The config passed as YAML frontmatter or directives
#### Defined in
[packages/mermaid/src/types.ts:67](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L67)
[packages/mermaid/src/types.ts:70](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L70)
---
@@ -30,4 +30,4 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[packages/mermaid/src/types.ts:63](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L63)
[packages/mermaid/src/types.ts:66](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L66)

View File

@@ -39,7 +39,7 @@ bindFunctions?.(div); // To call bindFunctions only if it's present.
#### Defined in
[packages/mermaid/src/types.ts:90](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L90)
[packages/mermaid/src/types.ts:98](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L98)
---
@@ -51,7 +51,7 @@ The diagram type, e.g. 'flowchart', 'sequence', etc.
#### Defined in
[packages/mermaid/src/types.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L80)
[packages/mermaid/src/types.ts:88](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L88)
---
@@ -63,4 +63,4 @@ The svg code for the rendered graph.
#### Defined in
[packages/mermaid/src/types.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L76)
[packages/mermaid/src/types.ts:84](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/types.ts#L84)

View File

@@ -14,7 +14,7 @@
#### Defined in
[packages/mermaid/src/defaultConfig.ts:267](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L267)
[packages/mermaid/src/defaultConfig.ts:270](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L270)
---

View File

@@ -200,15 +200,22 @@ Communication tools and platforms
- [Vim](https://www.vim.org)
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
- [Official Vim Syntax and ft plugin](https://github.com/craigmac/vim-mermaid)
- [Zed](https://zed.dev)
- [zed-mermaid](https://github.com/gabeidx/zed-mermaid)
### Document Generation
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Codedoc](https://codedoc.cc/)
- [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin)
- [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) ✅
- [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅
- [Gatsby](https://www.gatsbyjs.com/)
- [gatsby-remark-mermaid](https://github.com/remcohaszing/gatsby-remark-mermaid)
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [JSDoc](https://jsdoc.app/)
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
- [Madness](https://madness.dannyb.co/)
@@ -217,7 +224,7 @@ Communication tools and platforms
- [MkDocs](https://www.mkdocs.org)
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
- [Quarto](https://quarto.org/)
- [Quarto](https://quarto.org/)
- [rehype](https://github.com/rehypejs/rehype)
- [rehype-mermaid](https://github.com/remcohaszing/rehype-mermaid)
- [remark](https://remark.js.org/)
@@ -246,17 +253,12 @@ Communication tools and platforms
### Other
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Bisheng](https://www.npmjs.com/package/bisheng)
- [bisheng-plugin-mermaid](https://github.com/yct21/bisheng-plugin-mermaid)
- [Blazorade Mermaid: Render Mermaid diagrams in Blazor applications](https://github.com/Blazorade/Blazorade-Mermaid/wiki)
- [Codemia: A tool to practice system design problems](https://codemia.io) ✅
- [ExDoc](https://github.com/elixir-lang/ex_doc)
- [Rendering Mermaid graphs](https://github.com/elixir-lang/ex_doc#rendering-mermaid-graphs)
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)

View File

@@ -12,7 +12,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
<br />
<a href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;whiteboard" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light" alt="Mermaid&#0032;Whiteboard - Drag&#0032;&#0038;&#0032;Drop&#0032;your&#0032;Nodes&#0032;with&#0032;Mermaid&#0039;s&#0032;new&#0032;Whiteboard&#0033; | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://www.producthunt.com/products/mermaid-chart?utm_source=badge-follow&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/follow.svg?product_id=552855&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
## About

View File

@@ -94,10 +94,8 @@ Mermaid offers a variety of styles or “looks” for your diagrams, allowing yo
**Available Looks:**
```
• Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams.
• Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. Its great for consistency across projects or when you want to keep the familiar aesthetic.
```
- Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams.
- Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. Its great for consistency across projects or when you want to keep the familiar aesthetic.
**How to Select a Look:**
@@ -133,10 +131,8 @@ In addition to customizing the look of your diagrams, Mermaid Chart now allows y
#### Supported Layout Algorithms:
```
• Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams.
• ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support.
```
- Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams.
- ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support.
#### How to Select a Layout Algorithm:

View File

@@ -6,169 +6,181 @@
# Blog
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
## [Mermaid 11.4 is out: New Features and Kanban Diagramming](https://www.mermaidchart.com/blog/posts/mermaid-11-4-is-out-new-features-and-kanban-diagramming)
Mermaid 11.4 brings enhanced functionality with the introduction of Kanban diagrams, allowing users to create visual workflows with status columns and task details.
October 31, 2024 · 2 mins
## [How To Build an ER Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-build-an-er-diagram-with-mermaid-chart)
An entity relationship (ER) diagram acts like a blueprint for your database. This makes ER diagrams effective tools for anyone dealing with complex databases, data modeling, and AI model training.
October 24, 2024 · 4 mins
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/expanding-the-horizons-of-mermaid-flowcharts-introducing-30-new-shapes)
24 September 2024 · 5 mins
Discover 30 new shapes in Mermaid flowcharts, offering enhanced clarity, customization, and versatility for more dynamic and expressive visualizations.
## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/mermaid-supports-architecture-diagrams/)
## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/introducing-architecture-diagrams-in-mermaid)
2 September 2024 · 2 mins
Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love.
## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11/)
## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11-is-out)
23 August 2024 · 2 mins
Mermaid v11 introduces advanced layout options, new diagram types, and enhanced customization features, thanks to the incredible contributions from our community.
## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams/)
## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams)
6 August 2024 ·3 mins
Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love.
## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira-a-how-to-user-guide/)
## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira)
31 July 2024 · 5 mins
The Mermaid Chart plugin for Jira has arrived!
## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation/)
## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation)
22 July 2024 · 5 mins
The Mermaid AI chat interface
## [How to Make a Sequence Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-sequence-diagram-in-mermaid-chart-step-by-step-guide/)
## [How to Make a Sequence Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-sequence-diagram-with-mermaid-chart)
8 July 2024 · 6 mins
Sequence diagrams are important for communicating complex systems in a clear and concise manner.
## [How to Use the New “Comments” Feature in Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-use-the-new-comments-feature-in-mermaid-chart/)
## [How to Use the New “Comments” Feature in Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-use-the-new-comments-feature-in-mermaid-chart)
2 July 2024 · 3 mins
How to Use the New Comments Feature in Mermaid Chart
## [How to Use the official Mermaid Chart for Confluence app](https://www.mermaidchart.com/blog/posts/how-to-use-the-official-mermaid-chart-for-confluence-app/)
## [How to Use the official Mermaid Chart for Confluence app](https://www.mermaidchart.com/blog/posts/how-to-use-the-official-mermaid-chart-for-confluence-app)
21 May 2024 · 4 mins
It doesnt matter if youre a data enthusiast, software engineer, or visual storyteller; our Confluence app can allow you to embed Mermaid Chart diagrams — and dynamically edit them — within your Confluence pages.
## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software/)
## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software)
7 May 2024 · 5 mins
How to Choose the Right Documentation Software. Reliable and efficient documentation software is crucial in the fast-paced world of software development.
## [AI in software diagramming: What trends will define the future?](https://www.mermaidchart.com/blog/posts/ai-in-software-diagramming/)
## [AI in software diagramming: What trends will define the future?](https://www.mermaidchart.com/blog/posts/ai-in-software-diagramming)
24 April 2024 · 5 mins
Artificial intelligence (AI) tools are changing the way developers work.
## [Mermaid Chart Unveils Visual Editor for Sequence Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams/)
## [Mermaid Chart Unveils Visual Editor for Sequence Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams)
8 April 2024 · 5 mins
Sequence diagrams are excellent tools for communication and documentation.
## [Modeling system states: It starts with a Turing machine](https://www.mermaidchart.com/blog/posts/modeling-system-states/)
## [Modeling system states: It starts with a Turing machine](https://www.mermaidchart.com/blog/posts/modeling-system-states-it-starts-with-a-turing-machine)
27 March 2024 · 12 mins
In computer science, there are a few fundamental papers that, without exaggeration, changed everything.
## [Mermaid Chart Raises $7.5M to Reinvent Visual Collaboration for Enterprises](https://www.mermaidchart.com/blog/posts/mermaid-chart-raises-7.5m-to-reinvent-visual-collaoration-for-enterprises/)
## [Mermaid Chart Raises $7.5M to Reinvent Visual Collaboration for Enterprises](https://www.mermaidchart.com/blog/posts/mermaid-chart-raises-7-5m-to-reinvent-visual-collaboration-for-enterprises)
20 March 2024 · 4 mins
Mermaid Chart, the company offering text-based diagramming and workflow management tools, today announced it has raised $7.5 million in Seed funding.
## [Mermaid Chart GPT Is Now Available In the GPT Store!](https://www.mermaidchart.com/blog/posts/mermaid-chart-gpt-is-now-available-in-the-gpt-store/)
## [Mermaid Chart GPT Is Now Available In the GPT Store!](https://www.mermaidchart.com/blog/posts/mermaid-chart-gpt-is-now-available-in-the-gpt-store)
7 March 2024 · 3 mins
Mermaid Chart GPT is Now Available In the GPT Store!
## [How to Make a Flowchart with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-flowcharts-with-mermaid-chart/)
## [How to Make a Flowchart with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-flowchart-with-mermaid-chart)
30 January 2024 · 6 mins
Learn how to make a flowchart with Mermaid Chart, the leading text-to-diagram platform for both developers and non-developers.
## [How one data scientist uses Mermaid Chart to quickly and easily build flowcharts](https://www.mermaidchart.com/blog/posts/customer-spotlight-ari-tal/)
## [How one data scientist uses Mermaid Chart to quickly and easily build flowcharts](https://www.mermaidchart.com/blog/posts/how-one-data-scientist-uses-mermaid-chart-to-quickly-and-easily-build-flowcharts)
23 January 2024 · 4 mins
Read about how Ari Tal, a data scientist and founder of Leveling Up with XAI, utilizes Mermaid Chart for its easy-to-use flowchart creation capabilities to enhance his work in explainable AI (XAI).
## [Introducing Mermaid Charts JetBrains IDE Extension](https://www.mermaidchart.com/blog/posts/introducing-mermaid-charts-jetbrains-ide-extension/)
## [Introducing Mermaid Charts JetBrains IDE Extension](https://www.mermaidchart.com/blog/posts/introducing-mermaid-charts-jetbrains-ide-extension)
20 December 2023 · 5 mins
Diagrams are essential for documenting your code.
## [Mermaid Chart Releases New Visual Editor For Flowcharts](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts/)
## [Mermaid Chart Releases New Visual Editor For Flowcharts](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts)
14 December 2023 · 5 mins
Mermaid Chart introduces a new Visual Editor for flowcharts, enabling users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options.
## [7 best practices (+ examples) for good developer documentation](https://www.mermaidchart.com/blog/posts/7-best-practices-for-good-documentation/)
## [7 best practices (+ examples) for good developer documentation](https://www.mermaidchart.com/blog/posts/7-best-practices-examples-for-good-developer-documentation)
4 December 2023 · 11 min
Essential strategies for crafting grate developer documentation, with practical examples and insights from leading tech companies.
## [5 Reasons You Should Be Using Mermaid Chart As Your Diagram Generator](https://www.mermaidchart.com/blog/posts/5-reasons-you-should-be-using-mermaid-chart-as-your-diagram-generator/)
## [5 Reasons You Should Be Using Mermaid Chart As Your Diagram Generator](https://www.mermaidchart.com/blog/posts/5-reasons-you-should-be-using-mermaid-chart-as-your-diagram-generator)
14 November 2023 · 5 mins
Mermaid Chart, a user-friendly, code-based diagram generator with AI integrations, templates, collaborative tools, and plugins for developers, streamlines the process of creating and sharing diagrams, enhancing both creativity and collaboration.
## [How to Use Mermaid Chart as an AI Diagram Generator](https://www.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator/)
## [How to Use Mermaid Chart as an AI Diagram Generator](https://www.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator)
1 November 2023 · 5 mins
Would an AI diagram generator make your life easier?
## [Diagrams, Made Even Easier: Introducing “Code Snippets” in the Mermaid Chart Editor](https://www.mermaidchart.com/blog/posts/easier-diagram-editing-with-code-snippets/)
## [Diagrams, Made Even Easier: Introducing “Code Snippets” in the Mermaid Chart Editor](https://www.mermaidchart.com/blog/posts/diagrams-made-even-easier-introducing-code-snippets-in-the-mermaid-chart-editor)
12 October 2023 · 4 mins
Mermaid Chart introduces Code Snippets in its editor, streamlining the diagramming process for developers and professionals.
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart/)
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart)
22 September 2023 · 7 mins
A git graph is one of the more useful forms of diagrams for developers and DevOps professionals.
## [Present flow data using Sankey diagrams in Mermaid, thanks to Nikolay Rozhkov](https://www.mermaidchart.com/blog/posts/present-flow-data-using-sankey-diagrams/)
## [Present flow data using Sankey diagrams in Mermaid, thanks to Nikolay Rozhkov](https://www.mermaidchart.com/blog/posts/present-flow-data-using-sankey-diagrams-in-mermaid-thanks-to-nikolay-rozhkov)
8 September 2023 · 4 mins
Sankey diagrams are a powerful tool for visualizing flow data.
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too/)
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too)
23 August 2023 · 15 mins
Read about the pitfalls of special cases in programming, illustrating how they can lead to complexity, diminish readability, and create maintenance challenges.
## [New AI chatbot now available on Mermaid Chart to simplify text-based diagram creation](https://www.mermaidchart.com/blog/posts/ai-chatbot-now-available-on-mermaid-chart-to-simplify-text-based-diagram-creation/)
## [New AI chatbot now available on Mermaid Chart to simplify text-based diagram creation](https://www.mermaidchart.com/blog/posts/new-ai-chatbot-now-available-on-mermaid-chart-to-simplify-text-based-diagram-creation)
14 August 2023 · 4 mins
Introducing Mermaid Charts new AI chatbot, a diagramming assistant that simplifies text-based diagram creation for everyone, from developers to educators, offering features to start, edit, and fix diagrams, and embodying our vision to make diagramming accessible, user-friendly, and fun.
## [Believe It or Not, You Still Need an Online UML Diagram Tool](https://www.mermaidchart.com/blog/posts/uml-diagram-tool/)
## [Believe It or Not, You Still Need an Online UML Diagram Tool](https://www.mermaidchart.com/blog/posts/believe-it-or-not-you-still-need-an-online-uml-diagram-tool)
14 August 2023 · 8 mins
@@ -180,25 +192,25 @@ A UML diagram tool helps developers and other professionals quickly create and s
Introducing the concept of mind mapping as a tool for organizing complex information, and highlights Mermaid as a user-friendly software that simplifies the creation and editing of mind maps for applications in IT solution design, business decision-making, and knowledge organization.
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin)
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin-to-simplify-development-workflows)
17 July 2023 · 3 mins
New Integration Enhances Workflows By Enabling Developers To Reference And Edit Diagrams Within Visual Studio Code.
## [Mermaid Charts ChatGPT Plugin Combines Generative AI and Smart Diagramming For Users](https://www.mermaidchart.com/blog/posts/mermaid-chart-chatgpt-plugin-combines-generative-ai-and-smart-diagramming)
## [Mermaid Charts ChatGPT Plugin Combines Generative AI and Smart Diagramming For Users](https://www.mermaidchart.com/blog/posts/mermaid-charts-chatgpt-plugin-combines-generative-ai-and-smart-diagramming-for-users)
29 June 2023 · 4 mins
Mermaid Charts new ChatGPT plugin integrates AI-powered text prompts with Mermaids intuitive diagramming editor, enabling users to generate, edit, and share complex diagrams with ease and efficiency.
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-only-good-thing-uml-brought-to-software-development)
15 June 2023 · 12 mins
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do)
8 June 2023 · 7 mins
@@ -210,31 +222,31 @@ A quadrant chart is a useful diagram that helps users visualize data and identif
Documentation tends to be bad because companies and projects dont fully realize the costs of bad documentation.
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here/)
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
3 April 2023 · 3 mins
Markdown Strings reduce the hassle # Starting from v10.
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode)
27 March 2023 · 2 mins
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative)
8 March 2023 · 9 mins
The hype around AI in general and ChatGPT, in particular, is so intense that its very understandable to assume the hype train is driving straight toward the trough of disillusionment.
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections)
1 March 2023 · 12 mins
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write)
10 February 2023 · 10 mins

View File

@@ -79,15 +79,15 @@ service {service id}({icon name})[{title}] (in {parent id})?
Put together:
```
service database(db)[Database]
service database1(database)[My Database]
```
creates the service identified as `database`, using the icon `db`, with the label `Database`.
creates the service identified as `database1`, using the icon `database`, with the label `My Database`.
If the service belongs to a group, it can be placed inside it through the optional `in` keyword
```
service database(db)[Database] in private_api
service database1(database)[My Database] in private_api
```
### Edges
@@ -194,55 +194,7 @@ architecture-beta
## Icons
By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`.
Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below.
The icon packs available can be found at [icones.js.org](https://icones.js.org/).
We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram.
Using JSON file directly from CDN:
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
]);
```
Using packages and a bundler:
```bash
npm install @iconify-json/logos
```
With lazy loading
```js
import mermaid from 'mermaid';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () => import('@iconify-json/logos').then((module) => module.icons),
},
]);
```
Without lazy loading
```js
import mermaid from 'mermaid';
import { icons } from '@iconify-json/logos';
mermaid.registerIconPacks([
{
name: icons.prefix, // To use the prefix defined in the icon pack
icons,
},
]);
```
Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps [here](../config/icons.md).
After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack.

View File

@@ -141,7 +141,7 @@ block-beta
a["A label"] b:2 c:2 d
```
In this example, the block labeled "A wide one" spans two columns, while blocks 'b', 'c', and 'd' are allocated their own columns. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size.
In this example, the block labeled "A labels" spans one column, while blocks 'b', 'c' span 2 columns, and 'd' is again allocated its own column. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size.
### Creating Composite Blocks

View File

@@ -427,6 +427,51 @@ And `Link` can be one of:
| -- | Solid |
| .. | Dashed |
### Lollipop Interfaces
Classes can also be given a special relation type that defines a lollipop interface on the class. A lollipop interface is defined using the following syntax:
- `bar ()-- foo`
- `foo --() bar`
The interface (bar) with the lollipop connects to the class (foo).
Note: Each interface that is defined is unique and is meant to not be shared between classes / have multiple edges connecting to it.
```mermaid-example
classDiagram
bar ()-- foo
```
```mermaid
classDiagram
bar ()-- foo
```
```mermaid-example
classDiagram
class Class01 {
int amount
draw()
}
Class01 --() bar
Class02 --() bar
foo ()-- Class01
```
```mermaid
classDiagram
class Class01 {
int amount
draw()
}
Class01 --() bar
Class02 --() bar
foo ()-- Class01
```
## Define Namespace
A namespace groups classes.
@@ -776,10 +821,12 @@ Beginner's tip—a full example using interactive links in an HTML page:
## Styling
### Styling a node (v10.7.0+)
### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to an individual node using the `style` keyword.
Note that notes and namespaces cannot be styled individually but do support themes.
```mermaid-example
classDiagram
class Animal
@@ -799,11 +846,102 @@ classDiagram
#### Classes
More convenient than defining the style every time is to define a class of styles and attach this class to the nodes that
should have a different look. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
should have a different look.
A class definition looks like the example below:
```
classDef className fill:#f9f,stroke:#333,stroke-width:4px;
```
Also, it is possible to define style to multiple classes in one statement:
```
classDef firstClassName,secondClassName font-size:12pt;
```
Attachment of a class to a node is done as per below:
```
cssClass "nodeId1" className;
```
It is also possible to attach a class to a list of nodes in one statement:
```
cssClass "nodeId1,nodeId2" className;
```
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
```mermaid-example
classDiagram
class Animal:::someclass
classDef someclass fill:#f96
```
```mermaid
classDiagram
class Animal:::someclass
classDef someclass fill:#f96
```
Or:
```mermaid-example
classDiagram
class Animal:::someclass {
-int sizeInFeet
-canEat()
}
classDef someclass fill:#f96
```
```mermaid
classDiagram
class Animal:::someclass {
-int sizeInFeet
-canEat()
}
classDef someclass fill:#f96
```
### Default class
If a class is named default it will be applied to all nodes. Specific styles and classes should be defined afterwards to override the applied default styling.
```
classDef default fill:#f9f,stroke:#333,stroke-width:4px;
```
```mermaid-example
classDiagram
class Animal:::pink
class Mineral
classDef default fill:#f96,color:red
classDef pink color:#f9f
```
```mermaid
classDiagram
class Animal:::pink
class Mineral
classDef default fill:#f96,color:red
classDef pink color:#f9f
```
### CSS Classes
It is also possible to predefine classes in CSS styles that can be applied from the graph definition as in the example
below:
**Example style**
```html
<style>
.styleClass > rect {
.styleClass > * > g {
fill: #ff0000;
stroke: #ffff00;
stroke-width: 4px;
@@ -811,19 +949,7 @@ should have a different look. This is done by predefining classes in css styles
</style>
```
Then attaching that class to a specific node:
```
cssClass "nodeId1" styleClass;
```
It is also possible to attach a class to a list of nodes in one statement:
```
cssClass "nodeId1,nodeId2" styleClass;
```
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
**Example definition**
```mermaid-example
classDiagram
@@ -835,136 +961,32 @@ classDiagram
class Animal:::styleClass
```
Or:
```mermaid-example
classDiagram
class Animal:::styleClass {
-int sizeInFeet
-canEat()
}
```
```mermaid
classDiagram
class Animal:::styleClass {
-int sizeInFeet
-canEat()
}
```
?> cssClasses cannot be added using this shorthand method at the same time as a relation statement.
?> Due to limitations with existing markup for class diagrams, it is not currently possible to define css classes within the diagram itself. **_Coming soon!_**
### Default Styles
The main styling of the class diagram is done with a preset number of css classes. During rendering these classes are extracted from the file located at src/themes/class.scss. The classes used here are described below:
| Class | Description |
| ------------------ | ----------------------------------------------------------------- |
| g.classGroup text | Styles for general class text |
| classGroup .title | Styles for general class title |
| g.classGroup rect | Styles for class diagram rectangle |
| g.classGroup line | Styles for class diagram line |
| .classLabel .box | Styles for class label box |
| .classLabel .label | Styles for class label text |
| composition | Styles for composition arrow head and arrow line |
| aggregation | Styles for aggregation arrow head and arrow line(dashed or solid) |
| dependency | Styles for dependency arrow head and arrow line |
#### Sample stylesheet
```scss
body {
background: white;
}
g.classGroup text {
fill: $nodeBorder;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-family: var(--mermaid-font-family);
font-size: 10px;
.title {
font-weight: bolder;
}
}
g.classGroup rect {
fill: $nodeBkg;
stroke: $nodeBorder;
}
g.classGroup line {
stroke: $nodeBorder;
stroke-width: 1;
}
.classLabel .box {
stroke: none;
stroke-width: 0;
fill: $nodeBkg;
opacity: 0.5;
}
.classLabel .label {
fill: $nodeBorder;
font-size: 10px;
}
.relation {
stroke: $nodeBorder;
stroke-width: 1;
fill: none;
}
@mixin composition {
fill: $nodeBorder;
stroke: $nodeBorder;
stroke-width: 1;
}
#compositionStart {
@include composition;
}
#compositionEnd {
@include composition;
}
@mixin aggregation {
fill: $nodeBkg;
stroke: $nodeBorder;
stroke-width: 1;
}
#aggregationStart {
@include aggregation;
}
#aggregationEnd {
@include aggregation;
}
#dependencyStart {
@include composition;
}
#dependencyEnd {
@include composition;
}
#extensionStart {
@include composition;
}
#extensionEnd {
@include composition;
}
```
> cssClasses cannot be added using this shorthand method at the same time as a relation statement.
## Configuration
`Coming soon!`
### Members Box
It is possible to hide the empty members box of a class node.
This is done by changing the **hideEmptyMembersBox** value of the class diagram configuration. For more information on how to edit the Mermaid configuration see the [configuration page.](https://mermaid.js.org/config/configuration.html)
```mermaid-example
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class Duck
```
```mermaid
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class Duck
```

View File

@@ -316,53 +316,53 @@ This syntax creates a node A as a rectangle. It renders in the same way as `A["A
Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases:
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
| ------------------------------------- | ---------------------- | -------------- | ------------------------------ | -------------------------------------------------------------- |
| **Process** | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
| **Event** | Rounded Rectangle | `rounded` | Represents an event | `event` |
| **Terminal Point** | Stadium | `stadium` | Terminal point | `terminal`, `pill` |
| **Subprocess** | Framed Rectangle | `fr-rect` | Subprocess | `subprocess`,`subproc`, `framed-rectangle`, `subroutine` |
| **Database** | Cylinder | `cyl` | Database storage | `db`, `database`, `cylinder` |
| **Start** | Circle | `circle` | Starting point | `circ` |
| **Odd** | Odd | `odd` | Odd shape | |
| **Decision** | Diamond | `diam` | Decision-making step | `decision`, `diamond` |
| **Prepare Conditional** | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
| **Data Input/Output** | Lean Right | `lean-r` | Represents input or output | `lean-right`, `in-out` |
| **Data Input/Output** | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
| **Priority Action** | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid-bottom` |
| **Manual Operation** | Trapezoid Base Top | `trap-t` | Represents a manual task | `manual`, `trapezoid-top` |
| **Stop** | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
| **Text Block** | Text Block | `text` | Text block | - |
| **Card** | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
| **Lined/Shaded Process** | Lined Rectangle | `lin-rect` | Lined process shape | `lined-rectangle`,`lined-process`, `lin-proc`,`shaded-process` |
| **Start** | Small Circle | `sm-circ` | Small starting point | `start`, `small-circle` |
| **Stop** | Framed Circle | `fr-circ` | Stop point | `stop`, `framed-circle` |
| **Fork/Join** | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
| **Collate** | Hourglass | `hourglass` | Represents a collate operation | `hourglass` |
| **Comment** | Curly Brace | `brace` | Adds a comment | `comment`, `brace-l` |
| **Comment Right** | Curly Brace | `brace-r` | Adds a comment | - |
| **Comment with braces on both sides** | Curly Braces | `braces` | Adds a comment | - |
| **Com Link** | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
| **Document** | Document | `doc` | Represents a document | `doc`, `document` |
| **Delay** | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
| **Direct Access Storage** | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
| **Disk Storage** | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
| **Display** | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
| **Divided Process** | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-rectangle`, `divided-process` |
| **Extract** | Triangle | `tri` | Extraction process | `extract`, `triangle` |
| **Internal Storage** | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
| **Junction** | Filled Circle | `f-circ` | Junction point | `junction`, `filled-circle` |
| **Lined Document** | Lined Document | `lin-doc` | Lined document | `lined-document` |
| **Loop Limit** | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
| **Manual File** | Flipped Triangle | `flip-tri` | Manual file operation | `manual-file`, `flipped-triangle` |
| **Manual Input** | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
| **Multi-Document** | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
| **Multi-Process** | Stacked Rectangle | `st-rect` | Multiple processes | `procs`, `processes`, `stacked-rectangle` |
| **Paper Tape** | Flag | `flag` | Paper tape | `paper-tape` |
| **Stored Data** | Bow Tie Rectangle | `bow-rect` | Stored data | `stored-data`, `bow-tie-rectangle` |
| **Summary** | Crossed Circle | `cross-circ` | Summary | `summary`, `crossed-circle` |
| **Tagged Document** | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
| **Tagged Process** | Tagged Rectangle | `tag-rect` | Tagged process | `tagged-rectangle`,`tag-proc`, `tagged-process` |
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
| Card | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
| Collate | Hourglass | `hourglass` | Represents a collate operation | `collate`, `hourglass` |
| Com Link | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
| Comment | Curly Brace | `brace` | Adds a comment | `brace-l`, `comment` |
| Comment Right | Curly Brace | `brace-r` | Adds a comment | |
| Comment with braces on both sides | Curly Braces | `braces` | Adds a comment | |
| Data Input/Output | Lean Right | `lean-r` | Represents input or output | `in-out`, `lean-right` |
| Data Input/Output | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
| Database | Cylinder | `cyl` | Database storage | `cylinder`, `database`, `db` |
| Decision | Diamond | `diam` | Decision-making step | `decision`, `diamond`, `question` |
| Delay | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
| Direct Access Storage | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
| Disk Storage | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
| Display | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
| Divided Process | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-process`, `divided-rectangle` |
| Document | Document | `doc` | Represents a document | `doc`, `document` |
| Event | Rounded Rectangle | `rounded` | Represents an event | `event` |
| Extract | Triangle | `tri` | Extraction process | `extract`, `triangle` |
| Fork/Join | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
| Internal Storage | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
| Junction | Filled Circle | `f-circ` | Junction point | `filled-circle`, `junction` |
| Lined Document | Lined Document | `lin-doc` | Lined document | `lined-document` |
| Lined/Shaded Process | Lined Rectangle | `lin-rect` | Lined process shape | `lin-proc`, `lined-process`, `lined-rectangle`, `shaded-process` |
| Loop Limit | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
| Manual File | Flipped Triangle | `flip-tri` | Manual file operation | `flipped-triangle`, `manual-file` |
| Manual Input | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
| Manual Operation | Trapezoid Base Top | `trap-t` | Represents a manual task | `inv-trapezoid`, `manual`, `trapezoid-top` |
| Multi-Document | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
| Multi-Process | Stacked Rectangle | `st-rect` | Multiple processes | `processes`, `procs`, `stacked-rectangle` |
| Odd | Odd | `odd` | Odd shape | |
| Paper Tape | Flag | `flag` | Paper tape | `paper-tape` |
| Prepare Conditional | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
| Priority Action | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid`, `trapezoid-bottom` |
| Process | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
| Start | Circle | `circle` | Starting point | `circ` |
| Start | Small Circle | `sm-circ` | Small starting point | `small-circle`, `start` |
| Stop | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
| Stop | Framed Circle | `fr-circ` | Stop point | `framed-circle`, `stop` |
| Stored Data | Bow Tie Rectangle | `bow-rect` | Stored data | `bow-tie-rectangle`, `stored-data` |
| Subprocess | Framed Rectangle | `fr-rect` | Subprocess | `framed-rectangle`, `subproc`, `subprocess`, `subroutine` |
| Summary | Crossed Circle | `cross-circ` | Summary | `crossed-circle`, `summary` |
| Tagged Document | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
| Tagged Process | Tagged Rectangle | `tag-rect` | Tagged process | `tag-proc`, `tagged-process`, `tagged-rectangle` |
| Terminal Point | Stadium | `stadium` | Terminal point | `pill`, `terminal` |
| Text Block | Text Block | `text` | Text block | |
### Example Flowchart with New Shapes
@@ -926,6 +926,66 @@ flowchart TD
A@{ shape: tag-rect, label: "Tagged process" }
```
## Special shapes in Mermaid Flowcharts (v11.3.0+)
Mermaid also introduces 2 special shapes to enhance your flowcharts: **icon** and **image**. These shapes allow you to include icons and images directly within your flowcharts, providing more visual context and clarity.
### Icon Shape
You can use the `icon` shape to include an icon in your flowchart. To use icons, you need to register the icon pack first. Follow the instructions provided [here](../config/icons.md). The syntax for defining an icon shape is as follows:
```mermaid-example
flowchart TD
A@{ icon: "fa:user", form: "square", label: "User Icon", pos: "t", h: 60 }
```
```mermaid
flowchart TD
A@{ icon: "fa:user", form: "square", label: "User Icon", pos: "t", h: 60 }
```
### Parameters
- **icon**: The name of the icon from the registered icon pack.
- **form**: Specifies the background shape of the icon. If not defined there will be no background to icon. Options include:
- `square`
- `circle`
- `rounded`
- **label**: The text label associated with the icon. This can be any string. If not defined, no label will be displayed.
- **pos**: The position of the label. If not defined label will default to bottom of icon. Possible values are:
- `t`
- `b`
- **h**: The height of the icon. If not defined this will default to 48 which is minimum.
### Image Shape
You can use the `image` shape to include an image in your flowchart. The syntax for defining an image shape is as follows:
```mermaid-example
flowchart TD
A@{ img: "https://example.com/image.png", label: "Image Label", pos: "t", w: 60, h: 60, constraint: "off" }
```
```mermaid
flowchart TD
A@{ img: "https://example.com/image.png", label: "Image Label", pos: "t", w: 60, h: 60, constraint: "off" }
```
### Parameters
- **img**: The URL of the image to be displayed.
- **label**: The text label associated with the image. This can be any string. If not defined, no label will be displayed.
- **pos**: The position of the label. If not defined, the label will default to the bottom of the image. Possible values are:
- `t`
- `b`
- **w**: The width of the image. If not defined, this will default to the natural width of the image.
- **h**: The height of the image. If not defined, this will default to the natural height of the image.
- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are:
- `on`
- `off`
These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging.
## Links between nodes
Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link.

161
docs/syntax/kanban.md Normal file
View File

@@ -0,0 +1,161 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/kanban.md](../../packages/mermaid/src/docs/syntax/kanban.md).
# Mermaid Kanban Diagram Documentation
Mermaids Kanban diagram allows you to create visual representations of tasks moving through different stages of a workflow. This guide explains how to use the Kanban diagram syntax, based on the provided example.
## Overview
A Kanban diagram in Mermaid starts with the kanban keyword, followed by the definition of columns (stages) and tasks within those columns.
```mermaid-example
kanban
column1[Column Title]
task1[Task Description]
```
```mermaid
kanban
column1[Column Title]
task1[Task Description]
```
## Defining Columns
Columns represent the different stages in your workflow, such as “Todo,” “In Progress,” “Done,” etc. Each column is defined using a unique identifier and a title enclosed in square brackets.
**Syntax:**
```
columnId[Column Title]
```
- columnId: A unique identifier for the column.
- \[Column Title]: The title displayed on the column header.
Like this `id1[Todo]`
## Adding Tasks to Columns
Tasks are listed under their respective columns with an indentation. Each task also has a unique identifier and a description enclosed in square brackets.
**Syntax:**
```
taskId[Task Description]
```
```
• taskId: A unique identifier for the task.
• [Task Description]: The description of the task.
```
**Example:**
```
docs[Create Documentation]
```
## Adding Metadata to Tasks
You can include additional metadata for each task using the @{ ... } syntax. Metadata can contain key-value pairs like assigned, ticket, priority, etc. This will be rendered added to the rendering of the node.
## Supported Metadata Keys
```
• assigned: Specifies who is responsible for the task.
• ticket: Links the task to a ticket or issue number.
• priority: Indicates the urgency of the task. Allowed values: 'Very High', 'High', 'Low' and 'Very Low'
```
```mermaid-example
kanban
todo[Todo]
id3[Update Database Function]@{ ticket: MC-2037, assigned: 'knsv', priority: 'High' }
```
```mermaid
kanban
todo[Todo]
id3[Update Database Function]@{ ticket: MC-2037, assigned: 'knsv', priority: 'High' }
```
## Configuration Options
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams tacketBaseUrl. This can be set as in the the following example:
```yaml
---
config:
kanban:
ticketBaseUrl: 'https://yourproject.atlassian.net/browse/#TICKET#'
---
```
When the kanban item has an assigned ticket number the ticket number in the diagram will have a link to an external system where the ticket is defined. The `ticketBaseUrl` sets the base URL to the external system and #TICKET# is replaced with the ticket value from task metadata to create a valid link.
## Full Example
Below is the full Kanban diagram based on the provided example:
```mermaid-example
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#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 som 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: MC-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: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
```
```mermaid
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#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 som 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: MC-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: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
```
In conclusion, creating a Kanban diagram in Mermaid is a straightforward process that effectively visualizes your workflow. Start by using the kanban keyword to initiate the diagram. Define your columns with unique identifiers and titles to represent different stages of your project. Under each column, list your tasks—also with unique identifiers—and provide detailed descriptions as needed. Remember that proper indentation is crucial; tasks must be indented under their parent columns to maintain the correct structure.
You can enhance your diagram by adding optional metadata to tasks using the @{ ... } syntax, which allows you to include additional context such as assignee, ticket numbers, and priority levels. For further customization, utilize the configuration block at the top of your file to set global options like ticketBaseUrl for linking tickets directly from your diagram.
By adhering to these guidelines—ensuring unique identifiers, proper indentation, and utilizing metadata and configuration options—you can create a comprehensive and customized Kanban board that effectively maps out your projects workflow using Mermaid.

View File

@@ -220,7 +220,7 @@ There are ten types of arrows currently supported:
| `<<->>` | Solid line with bidirectional arrowheads (v11.0.0+) |
| `<<-->>` | Dotted line with bidirectional arrowheads (v11.0.0+) |
| `-x` | Solid line with a cross at the end |
| `--x` | Dotted line with a cross at the end. |
| `--x` | Dotted line with a cross at the end |
| `-)` | Solid line with an open arrow at the end (async) |
| `--)` | Dotted line with a open arrow at the end (async) |

View File

@@ -64,7 +64,7 @@
},
"devDependencies": {
"@applitools/eyes-cypress": "^3.44.4",
"@argos-ci/cypress": "^2.2.3",
"@argos-ci/cypress": "^2.2.2",
"@changesets/changelog-github": "^0.5.0",
"@changesets/cli": "^2.27.7",
"@cspell/eslint-plugin": "^8.8.4",

View File

@@ -3,6 +3,21 @@ import ELK from 'elkjs/lib/elk.bundled.js';
import type { InternalHelpers, LayoutData, RenderOptions, SVG, SVGGroup } from 'mermaid';
import { type TreeData, findCommonAncestor } from './find-common-ancestor.js';
type Node = LayoutData['nodes'][number];
interface LabelData {
width: number;
height: number;
wrappingWidth?: number;
labelNode?: SVGGElement | null;
}
interface NodeWithVertex extends Omit<Node, 'domId'> {
children?: unknown[];
labelData?: LabelData;
domId?: Node['domId'] | SVGGroup | d3.Selection<SVGAElement, unknown, Element | null, unknown>;
}
export const render = async (
data4Layout: LayoutData,
svg: SVG,
@@ -24,27 +39,37 @@ export const render = async (
const nodeDb: Record<string, any> = {};
const clusterDb: Record<string, any> = {};
const addVertex = async (nodeEl: any, graph: { children: any[] }, nodeArr: any, node: any) => {
const labelData: any = { width: 0, height: 0 };
const addVertex = async (
nodeEl: SVGGroup,
graph: { children: NodeWithVertex[] },
nodeArr: Node[],
node: Node
) => {
const labelData: LabelData = { width: 0, height: 0 };
let boundingBox;
const child = {
...node,
};
graph.children.push(child);
nodeDb[node.id] = child;
const config = getConfig();
// Add the element to the DOM
if (!node.isGroup) {
const child: NodeWithVertex = {
...node,
};
graph.children.push(child);
nodeDb[node.id] = child;
const childNodeEl = await insertNode(nodeEl, node, { config, dir: node.dir });
boundingBox = childNodeEl.node().getBBox();
const boundingBox = childNodeEl.node()!.getBBox();
child.domId = childNodeEl;
child.width = boundingBox.width;
child.height = boundingBox.height;
} else {
// A subgraph
child.children = [];
const child: NodeWithVertex & { children: NodeWithVertex[] } = {
...node,
children: [],
};
graph.children.push(child);
nodeDb[node.id] = child;
await addVertices(nodeEl, nodeArr, child, node.id);
if (node.label) {
@@ -68,28 +93,16 @@ export const render = async (
};
const addVertices = async function (
nodeEl: any,
nodeArr: any[],
graph: {
id: string;
layoutOptions: {
'elk.hierarchyHandling': string;
'elk.algorithm': any;
'nodePlacement.strategy': any;
'elk.layered.mergeEdges': any;
'elk.direction': string;
'spacing.baseValue': number;
};
children: never[];
edges: never[];
},
parentId?: undefined
nodeEl: SVGGroup,
nodeArr: Node[],
graph: { children: NodeWithVertex[] },
parentId?: string
) {
const siblings = nodeArr.filter((node: { parentId: any }) => node.parentId === parentId);
const siblings = nodeArr.filter((node) => node?.parentId === parentId);
log.info('addVertices APA12', siblings, parentId);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
await Promise.all(
siblings.map(async (node: any) => {
siblings.map(async (node) => {
await addVertex(nodeEl, graph, nodeArr, node);
})
);
@@ -136,6 +149,7 @@ export const render = async (
const clusterNode = JSON.parse(JSON.stringify(node));
clusterNode.x = node.offset.posX + node.width / 2;
clusterNode.y = node.offset.posY + node.height / 2;
clusterNode.width = Math.max(clusterNode.width, node.labelData.width);
await insertCluster(subgraphEl, clusterNode);
log.debug('Id (UIO)= ', node.id, node.width, node.shape, node.labels);
@@ -262,6 +276,8 @@ export const render = async (
interpolate: undefined;
style: undefined;
labelType: any;
startLabelRight?: string;
endLabelLeft?: string;
}) {
// Identify Link
const linkIdBase = edge.id; // 'L-' + edge.start + '-' + edge.end;
@@ -315,6 +331,9 @@ export const render = async (
let style = '';
let labelStyle = '';
edgeData.startLabelRight = edge.startLabelRight;
edgeData.endLabelLeft = edge.endLabelLeft;
switch (edge.stroke) {
case 'normal':
style = 'fill:none;';

View File

@@ -1,5 +1,33 @@
# mermaid
## 11.4.0
### Minor Changes
- [#5999](https://github.com/mermaid-js/mermaid/pull/5999) [`742ad7c`](https://github.com/mermaid-js/mermaid/commit/742ad7c130964df1fb5544e909d9556081285f68) Thanks [@knsv](https://github.com/knsv)! - Adding Kanban board, a new diagram type
- [#5880](https://github.com/mermaid-js/mermaid/pull/5880) [`bdf145f`](https://github.com/mermaid-js/mermaid/commit/bdf145ffe362462176d9c1e68d5f3ff5c9d962b0) Thanks [@yari-dewalt](https://github.com/yari-dewalt)! - Class diagram changes:
- Updates the class diagram to the new unified way of rendering.
- Includes a new "classBox" shape to be used in diagrams
- Other updates such as:
- the option to hide the empty members box in class diagrams,
- support for handDrawn look,
- the introduction of the classDef statement into class diagrams,
- support for styling the default class,
- support lollipop interfaces.
- Includes fixes / additions for #5562 #3139 and #4037
### Patch Changes
- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Jagged edge fix for icon shape
- [#5933](https://github.com/mermaid-js/mermaid/pull/5933) [`72d60d2`](https://github.com/mermaid-js/mermaid/commit/72d60d2633584eb59bccdb6cf30b9522db645db2) Thanks [@remcohaszing](https://github.com/remcohaszing)! - Add missing TypeScript dependencies
- [#5937](https://github.com/mermaid-js/mermaid/pull/5937) [`17b7831`](https://github.com/mermaid-js/mermaid/commit/17b783135f9b2b7748b620dbf81d0f56ab4755f1) Thanks [@saurabhg772244](https://github.com/saurabhg772244)! - fix: Icon color fix for colored icons.
- [#6002](https://github.com/mermaid-js/mermaid/pull/6002) [`5fabd41`](https://github.com/mermaid-js/mermaid/commit/5fabd414fbee01e43bf6c900907ffc1511ca7440) Thanks [@aloisklink](https://github.com/aloisklink)! - fix: error `mermaid.parse` on an invalid shape, so that it matches the errors thrown by `mermaid.render`
## 11.3.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "11.3.0",
"version": "11.4.0",
"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",
@@ -35,8 +35,8 @@
"clean": "rimraf dist",
"dev": "pnpm -w dev",
"docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaid.ts && prettier --write ./src/docs/config/setup",
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts",
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && tsx scripts/docs.cli.mts --verify",
"docs:build": "rimraf ../../docs && pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts",
"docs:verify": "pnpm docs:code && pnpm docs:spellcheck && tsx scripts/docs.cli.mts --verify",
"docs:pre:vitepress": "pnpm --filter ./src/docs prefetch && rimraf src/vitepress && pnpm docs:code && tsx scripts/docs.cli.mts --vitepress && pnpm --filter ./src/vitepress install --no-frozen-lockfile --ignore-scripts",
"docs:build:vitepress": "pnpm docs:pre:vitepress && (cd src/vitepress && pnpm run build) && cpy --flat src/docs/landing/ ./src/vitepress/.vitepress/dist/landing",
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"pnpm --filter ./src/vitepress dev\" \"tsx scripts/docs.cli.mts --watch --vitepress\"",

View File

@@ -41,7 +41,8 @@ import { exec } from 'child_process';
import { globby } from 'globby';
import { JSDOM } from 'jsdom';
import { dump, load, JSON_SCHEMA } from 'js-yaml';
import type { Code, ListItem, Root, Text, YAML } from 'mdast';
import type { Code, ListItem, PhrasingContent, Root, Text, YAML } from 'mdast';
import { register } from 'node:module';
import { posix, dirname, relative, join } from 'path';
import prettier from 'prettier';
import { remark } from 'remark';
@@ -53,6 +54,10 @@ import mm from 'micromatch';
import flatmap from 'unist-util-flatmap';
import { visit } from 'unist-util-visit';
// short-circuit `.schema.yaml` imports, so that we can safely import `shapes.js`
register('./loadHook.mjs', import.meta.url);
const { shapesDefs } = await import('../src/rendering-util/rendering-elements/shapes.js');
export const MERMAID_RELEASE_VERSION = JSON.parse(readFileSync('../mermaid/package.json', 'utf8'))
.version as string;
const MERMAID_MAJOR_VERSION = MERMAID_RELEASE_VERSION.split('.')[0];
@@ -103,6 +108,60 @@ const generateHeader = (file: string): string => {
> ## Please edit the corresponding file in [${filePathFromRoot}](${sourcePathRelativeToGenerated}).`;
};
/**
* Builds a markdown list of shapes supported in flowcharts.
*/
export function buildShapeDoc() {
const data = shapesDefs
.sort((a, b) => a.semanticName.localeCompare(b.semanticName))
.map((shape): PhrasingContent[][] => {
const { name, semanticName, description, shortName, aliases = [] } = shape;
return [
[{ type: 'text', value: semanticName }],
[{ type: 'text', value: name }],
[{ type: 'inlineCode', value: shortName }],
[{ type: 'text', value: description }],
aliases.sort().flatMap((alias, index) => [
...(index !== 0 ? ([{ type: 'text', value: ', ' }] as const) : []),
{
type: 'inlineCode',
value: alias,
},
]),
];
});
// don't prettify this table, since we'd do it later
return remark()
.use(remarkGfm)
.stringify({
type: 'root',
children: [
{
type: 'table',
children: [
['Semantic Name', 'Shape Name', 'Short Name', 'Description', 'Alias Supported'].map(
(s): PhrasingContent[] => [
{
type: 'strong',
children: [{ type: 'text', value: s }],
},
]
),
...data,
].map((row) => ({
type: 'tableRow',
children: row.map((cell) => ({
type: 'tableCell',
children: cell,
})),
})),
},
],
})
.toString();
}
/**
* Given a source file name and path, return the documentation destination full path and file name
* Create the destination path if it does not already exist.
@@ -192,10 +251,22 @@ export const transformToBlockQuote = (
const injectPlaceholders = (text: string): string =>
text.replace(/<MERMAID_VERSION>/g, MERMAID_MAJOR_VERSION).replace(/<CDN_URL>/g, CDN_URL);
const virtualGenerators: Record<string, () => string> = {
shapesTable: buildShapeDoc,
};
const transformIncludeStatements = (file: string, text: string): string => {
// resolve includes - src https://github.com/vuejs/vitepress/blob/428eec3750d6b5648a77ac52d88128df0554d4d1/src/node/markdownToVue.ts#L65-L76
return text.replace(includesRE, (m, m1) => {
return text.replace(includesRE, (m, m1: string) => {
try {
if (m1.startsWith('virtual:')) {
const key = m1.replace('virtual:', '');
const generator = virtualGenerators[key];
if (!generator) {
throw new Error(`Unknown virtual generator: ${key} in "${file}"`);
}
return generator();
}
const includePath = join(dirname(file), m1).replaceAll('\\', '/');
const content = readSyncedUTF8file(includePath);
includedFiles.add(changeToFinalDocDir(includePath));

View File

@@ -1,4 +1,4 @@
import { transformMarkdownAst, transformToBlockQuote } from './docs.mjs';
import { buildShapeDoc, transformMarkdownAst, transformToBlockQuote } from './docs.mjs';
import { remark } from 'remark'; // import it this way so we can mock it
import remarkFrontmatter from 'remark-frontmatter';
@@ -165,4 +165,59 @@ This Markdown should be kept.
});
});
});
describe('buildShapeDoc', () => {
it('should build shapesTable based on the shapeDefs', () => {
expect(buildShapeDoc()).toMatchInlineSnapshot(`
"| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
| --------------------------------- | ---------------------- | -------------- | ------------------------------ | ---------------------------------------------------------------- |
| Card | Notched Rectangle | \`notch-rect\` | Represents a card | \`card\`, \`notched-rectangle\` |
| Collate | Hourglass | \`hourglass\` | Represents a collate operation | \`collate\`, \`hourglass\` |
| Com Link | Lightning Bolt | \`bolt\` | Communication link | \`com-link\`, \`lightning-bolt\` |
| Comment | Curly Brace | \`brace\` | Adds a comment | \`brace-l\`, \`comment\` |
| Comment Right | Curly Brace | \`brace-r\` | Adds a comment | |
| Comment with braces on both sides | Curly Braces | \`braces\` | Adds a comment | |
| Data Input/Output | Lean Right | \`lean-r\` | Represents input or output | \`in-out\`, \`lean-right\` |
| Data Input/Output | Lean Left | \`lean-l\` | Represents output or input | \`lean-left\`, \`out-in\` |
| Database | Cylinder | \`cyl\` | Database storage | \`cylinder\`, \`database\`, \`db\` |
| Decision | Diamond | \`diam\` | Decision-making step | \`decision\`, \`diamond\`, \`question\` |
| Delay | Half-Rounded Rectangle | \`delay\` | Represents a delay | \`half-rounded-rectangle\` |
| Direct Access Storage | Horizontal Cylinder | \`h-cyl\` | Direct access storage | \`das\`, \`horizontal-cylinder\` |
| Disk Storage | Lined Cylinder | \`lin-cyl\` | Disk storage | \`disk\`, \`lined-cylinder\` |
| Display | Curved Trapezoid | \`curv-trap\` | Represents a display | \`curved-trapezoid\`, \`display\` |
| Divided Process | Divided Rectangle | \`div-rect\` | Divided process shape | \`div-proc\`, \`divided-process\`, \`divided-rectangle\` |
| Document | Document | \`doc\` | Represents a document | \`doc\`, \`document\` |
| Event | Rounded Rectangle | \`rounded\` | Represents an event | \`event\` |
| Extract | Triangle | \`tri\` | Extraction process | \`extract\`, \`triangle\` |
| Fork/Join | Filled Rectangle | \`fork\` | Fork or join in process flow | \`join\` |
| Internal Storage | Window Pane | \`win-pane\` | Internal storage | \`internal-storage\`, \`window-pane\` |
| Junction | Filled Circle | \`f-circ\` | Junction point | \`filled-circle\`, \`junction\` |
| Lined Document | Lined Document | \`lin-doc\` | Lined document | \`lined-document\` |
| Lined/Shaded Process | Lined Rectangle | \`lin-rect\` | Lined process shape | \`lin-proc\`, \`lined-process\`, \`lined-rectangle\`, \`shaded-process\` |
| Loop Limit | Trapezoidal Pentagon | \`notch-pent\` | Loop limit step | \`loop-limit\`, \`notched-pentagon\` |
| Manual File | Flipped Triangle | \`flip-tri\` | Manual file operation | \`flipped-triangle\`, \`manual-file\` |
| Manual Input | Sloped Rectangle | \`sl-rect\` | Manual input step | \`manual-input\`, \`sloped-rectangle\` |
| Manual Operation | Trapezoid Base Top | \`trap-t\` | Represents a manual task | \`inv-trapezoid\`, \`manual\`, \`trapezoid-top\` |
| Multi-Document | Stacked Document | \`docs\` | Multiple documents | \`documents\`, \`st-doc\`, \`stacked-document\` |
| Multi-Process | Stacked Rectangle | \`st-rect\` | Multiple processes | \`processes\`, \`procs\`, \`stacked-rectangle\` |
| Odd | Odd | \`odd\` | Odd shape | |
| Paper Tape | Flag | \`flag\` | Paper tape | \`paper-tape\` |
| Prepare Conditional | Hexagon | \`hex\` | Preparation or condition step | \`hexagon\`, \`prepare\` |
| Priority Action | Trapezoid Base Bottom | \`trap-b\` | Priority action | \`priority\`, \`trapezoid\`, \`trapezoid-bottom\` |
| Process | Rectangle | \`rect\` | Standard process shape | \`proc\`, \`process\`, \`rectangle\` |
| Start | Circle | \`circle\` | Starting point | \`circ\` |
| Start | Small Circle | \`sm-circ\` | Small starting point | \`small-circle\`, \`start\` |
| Stop | Double Circle | \`dbl-circ\` | Represents a stop point | \`double-circle\` |
| Stop | Framed Circle | \`fr-circ\` | Stop point | \`framed-circle\`, \`stop\` |
| Stored Data | Bow Tie Rectangle | \`bow-rect\` | Stored data | \`bow-tie-rectangle\`, \`stored-data\` |
| Subprocess | Framed Rectangle | \`fr-rect\` | Subprocess | \`framed-rectangle\`, \`subproc\`, \`subprocess\`, \`subroutine\` |
| Summary | Crossed Circle | \`cross-circ\` | Summary | \`crossed-circle\`, \`summary\` |
| Tagged Document | Tagged Document | \`tag-doc\` | Tagged document | \`tag-doc\`, \`tagged-document\` |
| Tagged Process | Tagged Rectangle | \`tag-rect\` | Tagged process | \`tag-proc\`, \`tagged-process\`, \`tagged-rectangle\` |
| Terminal Point | Stadium | \`stadium\` | Terminal point | \`pill\`, \`terminal\` |
| Text Block | Text Block | \`text\` | Text block | |
"
`);
});
});
});

View File

@@ -0,0 +1,22 @@
import { fileURLToPath } from 'node:url';
/** @import import { LoadHook } from "node:module"; */
/**
* @type {LoadHook}
*
* Load hook that short circuits the loading of `.schema.yaml` files with `export default {}`.
* These would normally be loaded using ESBuild, but that doesn't work for these local scripts.
*
* @see https://nodejs.org/api/module.html#loadurl-context-nextload
*/
export const load = async (url, context, nextLoad) => {
const filePath = url.startsWith('file://') ? fileURLToPath(url) : url;
if (filePath.endsWith('.schema.yaml')) {
return {
format: 'module',
shortCircuit: true,
source: `export default {}`,
};
} else {
return await nextLoad(url, context);
}
};

View File

@@ -193,6 +193,7 @@ export interface MermaidConfig {
requirement?: RequirementDiagramConfig;
architecture?: ArchitectureDiagramConfig;
mindmap?: MindmapDiagramConfig;
kanban?: KanbanDiagramConfig;
gitGraph?: GitGraphDiagramConfig;
c4?: C4DiagramConfig;
sankey?: SankeyDiagramConfig;
@@ -716,6 +717,7 @@ export interface ClassDiagramConfig extends BaseDiagramConfig {
*/
diagramPadding?: number;
htmlLabels?: boolean;
hideEmptyMembersBox?: boolean;
}
/**
* The object containing configurations specific for entity relationship diagrams
@@ -1023,6 +1025,17 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig {
padding?: number;
maxNodeWidth?: number;
}
/**
* The object containing configurations specific for kanban diagrams
*
* This interface was referenced by `MermaidConfig`'s JSON-Schema
* via the `definition` "KanbanDiagramConfig".
*/
export interface KanbanDiagramConfig extends BaseDiagramConfig {
padding?: number;
sectionWidth?: number;
ticketBaseUrl?: string;
}
/**
* This interface was referenced by `MermaidConfig`'s JSON-Schema
* via the `definition` "GitGraphDiagramConfig".

View File

@@ -53,6 +53,9 @@ const config: RequiredDeep<MermaidConfig> = {
};
},
},
class: {
hideEmptyMembersBox: false,
},
gantt: {
...defaultConfigJson.gantt,
tickInterval: undefined,

View File

@@ -19,6 +19,7 @@ import errorDiagram from '../diagrams/error/errorDiagram.js';
import flowchartElk from '../diagrams/flowchart/elk/detector.js';
import timeline from '../diagrams/timeline/detector.js';
import mindmap from '../diagrams/mindmap/detector.js';
import kanban from '../diagrams/kanban/detector.js';
import sankey from '../diagrams/sankey/sankeyDetector.js';
import { packet } from '../diagrams/packet/detector.js';
import block from '../diagrams/block/blockDetector.js';
@@ -70,6 +71,7 @@ export const addDiagrams = () => {
// Ordering of detectors is important. The first one to return true will be used.
registerLazyLoadedDiagrams(
c4,
kanban,
classDiagramV2,
classDiagram,
er,

View File

@@ -1,9 +1,8 @@
import type { Selection } from 'd3';
import { select } from 'd3';
import { select, type Selection } from 'd3';
import { log } from '../../logger.js';
import { getConfig } from '../../diagram-api/diagramAPI.js';
import common from '../common/common.js';
import utils from '../../utils.js';
import utils, { getEdgeId } from '../../utils.js';
import {
setAccTitle,
getAccTitle,
@@ -21,13 +20,18 @@ import type {
ClassMap,
NamespaceMap,
NamespaceNode,
StyleClass,
Interface,
} from './classTypes.js';
import type { Node, Edge } from '../../rendering-util/types.js';
const MERMAID_DOM_ID_PREFIX = 'classId-';
let relations: ClassRelation[] = [];
let classes = new Map<string, ClassNode>();
const styleClasses = new Map<string, StyleClass>();
let notes: ClassNote[] = [];
let interfaces: Interface[] = [];
let classCounter = 0;
let namespaces = new Map<string, NamespaceNode>();
let namespaceCounter = 0;
@@ -58,6 +62,8 @@ export const setClassLabel = function (_id: string, label: string) {
const { className } = splitClassNameAndType(id);
classes.get(className)!.label = label;
classes.get(className)!.text =
`${label}${classes.get(className)!.type ? `<${classes.get(className)!.type}>` : ''}`;
};
/**
@@ -80,7 +86,9 @@ export const addClass = function (_id: string) {
id: name,
type: type,
label: name,
cssClasses: [],
text: `${name}${type ? `&lt;${type}&gt;` : ''}`,
shape: 'classBox',
cssClasses: 'default',
methods: [],
members: [],
annotations: [],
@@ -91,6 +99,16 @@ export const addClass = function (_id: string) {
classCounter++;
};
const addInterface = function (label: string, classId: string) {
const classInterface: Interface = {
id: `interface${interfaces.length}`,
label,
classId,
};
interfaces.push(classInterface);
};
/**
* Function to lookup domId from id in the graph definition.
*
@@ -109,6 +127,7 @@ export const clear = function () {
relations = [];
classes = new Map();
notes = [];
interfaces = [];
functions = [];
functions.push(setupToolTips);
namespaces = new Map();
@@ -133,19 +152,50 @@ export const getNotes = function () {
return notes;
};
export const addRelation = function (relation: ClassRelation) {
log.debug('Adding relation: ' + JSON.stringify(relation));
addClass(relation.id1);
addClass(relation.id2);
export const addRelation = function (classRelation: ClassRelation) {
log.debug('Adding relation: ' + JSON.stringify(classRelation));
// Due to relationType cannot just check if it is equal to 'none' or it complains, can fix this later
const invalidTypes = [
relationType.LOLLIPOP,
relationType.AGGREGATION,
relationType.COMPOSITION,
relationType.DEPENDENCY,
relationType.EXTENSION,
];
relation.id1 = splitClassNameAndType(relation.id1).className;
relation.id2 = splitClassNameAndType(relation.id2).className;
if (
classRelation.relation.type1 === relationType.LOLLIPOP &&
!invalidTypes.includes(classRelation.relation.type2)
) {
addClass(classRelation.id2);
addInterface(classRelation.id1, classRelation.id2);
classRelation.id1 = `interface${interfaces.length - 1}`;
} else if (
classRelation.relation.type2 === relationType.LOLLIPOP &&
!invalidTypes.includes(classRelation.relation.type1)
) {
addClass(classRelation.id1);
addInterface(classRelation.id2, classRelation.id1);
classRelation.id2 = `interface${interfaces.length - 1}`;
} else {
addClass(classRelation.id1);
addClass(classRelation.id2);
}
relation.relationTitle1 = common.sanitizeText(relation.relationTitle1.trim(), getConfig());
classRelation.id1 = splitClassNameAndType(classRelation.id1).className;
classRelation.id2 = splitClassNameAndType(classRelation.id2).className;
relation.relationTitle2 = common.sanitizeText(relation.relationTitle2.trim(), getConfig());
classRelation.relationTitle1 = common.sanitizeText(
classRelation.relationTitle1.trim(),
getConfig()
);
relations.push(relation);
classRelation.relationTitle2 = common.sanitizeText(
classRelation.relationTitle2.trim(),
getConfig()
);
relations.push(classRelation);
};
/**
@@ -229,11 +279,37 @@ export const setCssClass = function (ids: string, className: string) {
}
const classNode = classes.get(id);
if (classNode) {
classNode.cssClasses.push(className);
classNode.cssClasses += ' ' + className;
}
});
};
export const defineClass = function (ids: string[], style: string[]) {
for (const id of ids) {
let styleClass = styleClasses.get(id);
if (styleClass === undefined) {
styleClass = { id, styles: [], textStyles: [] };
styleClasses.set(id, styleClass);
}
if (style) {
style.forEach(function (s) {
if (/color/.exec(s)) {
const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
styleClass.textStyles.push(newStyle);
}
styleClass.styles.push(s);
});
}
classes.forEach((value) => {
if (value.cssClasses.includes(id)) {
value.styles.push(...style.flatMap((s) => s.split(',')));
}
});
}
};
/**
* Called by parser when a tooltip is found, e.g. a clickable element.
*
@@ -472,6 +548,152 @@ export const setCssStyle = function (id: string, styles: string[]) {
}
};
/**
* Gets the arrow marker for a type index
*
* @param type - The type to look for
* @returns The arrow marker
*/
function getArrowMarker(type: number) {
let marker;
switch (type) {
case 0:
marker = 'aggregation';
break;
case 1:
marker = 'extension';
break;
case 2:
marker = 'composition';
break;
case 3:
marker = 'dependency';
break;
case 4:
marker = 'lollipop';
break;
default:
marker = 'none';
}
return marker;
}
export const getData = () => {
const nodes: Node[] = [];
const edges: Edge[] = [];
const config = getConfig();
for (const namespaceKey of namespaces.keys()) {
const namespace = namespaces.get(namespaceKey);
if (namespace) {
const node: Node = {
id: namespace.id,
label: namespace.id,
isGroup: true,
padding: config.class!.padding ?? 16,
// parent node must be one of [rect, roundedWithTitle, noteGroup, divider]
shape: 'rect',
cssStyles: ['fill: none', 'stroke: black'],
look: config.look,
};
nodes.push(node);
}
}
for (const classKey of classes.keys()) {
const classNode = classes.get(classKey);
if (classNode) {
const node = classNode as unknown as Node;
node.parentId = classNode.parent;
node.look = config.look;
nodes.push(node);
}
}
let cnt = 0;
for (const note of notes) {
cnt++;
const noteNode: Node = {
id: note.id,
label: note.text,
isGroup: false,
shape: 'note',
padding: config.class!.padding ?? 6,
cssStyles: [
'text-align: left',
'white-space: nowrap',
`fill: ${config.themeVariables.noteBkgColor}`,
`stroke: ${config.themeVariables.noteBorderColor}`,
],
look: config.look,
};
nodes.push(noteNode);
const noteClassId = classes.get(note.class)?.id ?? '';
if (noteClassId) {
const edge: Edge = {
id: `edgeNote${cnt}`,
start: note.id,
end: noteClassId,
type: 'normal',
thickness: 'normal',
classes: 'relation',
arrowTypeStart: 'none',
arrowTypeEnd: 'none',
arrowheadStyle: '',
labelStyle: [''],
style: ['fill: none'],
pattern: 'dotted',
look: config.look,
};
edges.push(edge);
}
}
for (const _interface of interfaces) {
const interfaceNode: Node = {
id: _interface.id,
label: _interface.label,
isGroup: false,
shape: 'rect',
cssStyles: ['opacity: 0;'],
look: config.look,
};
nodes.push(interfaceNode);
}
cnt = 0;
for (const classRelation of relations) {
cnt++;
const edge: Edge = {
id: getEdgeId(classRelation.id1, classRelation.id2, {
prefix: 'id',
counter: cnt,
}),
start: classRelation.id1,
end: classRelation.id2,
type: 'normal',
label: classRelation.title,
labelpos: 'c',
thickness: 'normal',
classes: 'relation',
arrowTypeStart: getArrowMarker(classRelation.relation.type1),
arrowTypeEnd: getArrowMarker(classRelation.relation.type2),
startLabelRight: classRelation.relationTitle1 === 'none' ? '' : classRelation.relationTitle1,
endLabelLeft: classRelation.relationTitle2 === 'none' ? '' : classRelation.relationTitle2,
arrowheadStyle: '',
labelStyle: ['display: inline-block'],
style: classRelation.style || '',
pattern: classRelation.relation.lineType == 1 ? 'dashed' : 'solid',
look: config.look,
};
edges.push(edge);
}
return { nodes, edges, other: {}, config, direction: getDirection() };
};
export default {
setAccTitle,
getAccTitle,
@@ -497,6 +719,7 @@ export default {
relationType,
setClickEvent,
setCssClass,
defineClass,
setLink,
getTooltip,
setTooltip,
@@ -509,4 +732,5 @@ export default {
getNamespace,
getNamespaces,
setCssStyle,
getData,
};

View File

@@ -13,7 +13,7 @@ describe('class diagram, ', function () {
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass');
});
it('should be possible to apply a css class to a class directly with struct', function () {
@@ -28,7 +28,7 @@ describe('class diagram, ', function () {
parser.parse(str);
const testClass = parser.yy.getClass('Class1');
expect(testClass.cssClasses[0]).toBe('exClass');
expect(testClass.cssClasses).toBe('default exClass');
});
it('should be possible to apply a css class to a class with relations', function () {
@@ -36,7 +36,7 @@ describe('class diagram, ', function () {
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass');
});
it('should be possible to apply a cssClass to a class', function () {
@@ -44,7 +44,7 @@ describe('class diagram, ', function () {
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass');
});
it('should be possible to apply a cssClass to a comma separated list of classes', function () {
@@ -53,8 +53,8 @@ describe('class diagram, ', function () {
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class02').cssClasses[0]).toBe('exClass');
expect(parser.yy.getClass('Class01').cssClasses).toBe('default exClass');
expect(parser.yy.getClass('Class02').cssClasses).toBe('default exClass');
});
it('should be possible to apply a style to an individual node', function () {
const str =
@@ -69,5 +69,47 @@ describe('class diagram, ', function () {
expect(styleElements[1]).toBe('stroke:#333');
expect(styleElements[2]).toBe('stroke-width:4px');
});
it('should be possible to define and assign a class inside the diagram', function () {
const str =
'classDiagram\n' + 'class Class01\n cssClass "Class01" pink\n classDef pink fill:#f9f';
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink');
});
it('should be possible to define and assign a class using shorthand inside the diagram', function () {
const str = 'classDiagram\n' + 'class Class01:::pink\n classDef pink fill:#f9f';
parser.parse(str);
expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink');
});
it('should properly assign styles from a class defined inside the diagram', function () {
const str =
'classDiagram\n' +
'class Class01:::pink\n classDef pink fill:#f9f,stroke:#333,stroke-width:6px';
parser.parse(str);
expect(parser.yy.getClass('Class01').styles).toStrictEqual([
'fill:#f9f',
'stroke:#333',
'stroke-width:6px',
]);
});
it('should properly assign multiple classes and styles from classes defined inside the diagram', function () {
const str =
'classDiagram\n' +
'class Class01:::pink\n cssClass "Class01" bold\n classDef pink fill:#f9f\n classDef bold stroke:#333,stroke-width:6px';
parser.parse(str);
expect(parser.yy.getClass('Class01').styles).toStrictEqual([
'fill:#f9f',
'stroke:#333',
'stroke-width:6px',
]);
expect(parser.yy.getClass('Class01').cssClasses).toBe('default pink bold');
});
});
});

View File

@@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
import parser from './parser/classDiagram.jison';
import db from './classDb.js';
import styles from './styles.js';
import renderer from './classRenderer-v2.js';
import renderer from './classRenderer-v3-unified.js';
export const diagram: DiagramDefinition = {
parser,

View File

@@ -246,7 +246,7 @@ describe('given a basic class diagram, ', function () {
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
});
it('should parse a class with text label and css class', () => {
@@ -261,7 +261,7 @@ describe('given a basic class diagram, ', function () {
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.members[0].getDisplayDetails().displayText).toBe('int member1');
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
});
it('should parse two classes with text labels and css classes', () => {
@@ -276,11 +276,11 @@ describe('given a basic class diagram, ', function () {
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Long long long long long long long long long long label');
expect(c2.cssClasses[0]).toBe('styleClass');
expect(c2.cssClasses).toBe('default styleClass');
});
it('should parse two classes with text labels and css class shorthands', () => {
@@ -293,11 +293,11 @@ describe('given a basic class diagram, ', function () {
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses[0]).toBe('styleClass1');
expect(c1.cssClasses).toBe('default styleClass1');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Class 2 !@#$%^&*() label');
expect(c2.cssClasses[0]).toBe('styleClass2');
expect(c2.cssClasses).toBe('default styleClass2');
});
it('should parse multiple classes with same text labels', () => {
@@ -494,10 +494,32 @@ class C13["With Città foreign language"]
],
methods: [],
annotations: [],
cssClasses: [],
cssClasses: 'default',
});
expect(classDb.getClasses().size).toBe(3);
expect(classDb.getClasses().get('Student')).toMatchInlineSnapshot(`
{
"annotations": [],
"cssClasses": "default",
"domId": "classId-Student-141",
"id": "Student",
"label": "Student",
"members": [
ClassMember {
"classifier": "",
"id": "idCard : IdCard",
"memberType": "attribute",
"text": "\\-idCard : IdCard",
"visibility": "-",
},
],
"methods": [],
"shape": "classBox",
"styles": [],
"text": "Student",
"type": "",
}
`);
expect(classDb.getRelations().length).toBe(2);
expect(classDb.getRelations()).toMatchInlineSnapshot(`
[
@@ -738,7 +760,7 @@ foo()
const actual = parser.yy.getClass('Class1');
expect(actual.link).toBe('google.com');
expect(actual.cssClasses[0]).toBe('clickable');
expect(actual.cssClasses).toBe('default clickable');
});
it('should handle href link with tooltip', function () {
@@ -754,7 +776,7 @@ foo()
const actual = parser.yy.getClass('Class1');
expect(actual.link).toBe('google.com');
expect(actual.tooltip).toBe('A Tooltip');
expect(actual.cssClasses[0]).toBe('clickable');
expect(actual.cssClasses).toBe('default clickable');
});
it('should handle href link with tooltip and target', function () {
@@ -773,7 +795,7 @@ foo()
const actual = parser.yy.getClass('Class1');
expect(actual.link).toBe('google.com');
expect(actual.tooltip).toBe('A tooltip');
expect(actual.cssClasses[0]).toBe('clickable');
expect(actual.cssClasses).toBe('default clickable');
});
it('should handle function call', function () {
@@ -1468,8 +1490,7 @@ describe('given a class diagram with relationships, ', function () {
const testClass = parser.yy.getClass('Class1');
expect(testClass.link).toBe('google.com');
expect(testClass.cssClasses.length).toBe(1);
expect(testClass.cssClasses[0]).toBe('clickable');
expect(testClass.cssClasses).toBe('default clickable');
});
it('should associate click and href link and css appropriately', function () {
@@ -1482,8 +1503,7 @@ describe('given a class diagram with relationships, ', function () {
const testClass = parser.yy.getClass('Class1');
expect(testClass.link).toBe('google.com');
expect(testClass.cssClasses.length).toBe(1);
expect(testClass.cssClasses[0]).toBe('clickable');
expect(testClass.cssClasses).toBe('default clickable');
});
it('should associate link with tooltip', function () {
@@ -1497,8 +1517,7 @@ describe('given a class diagram with relationships, ', function () {
const testClass = parser.yy.getClass('Class1');
expect(testClass.link).toBe('google.com');
expect(testClass.tooltip).toBe('A tooltip');
expect(testClass.cssClasses.length).toBe(1);
expect(testClass.cssClasses[0]).toBe('clickable');
expect(testClass.cssClasses).toBe('default clickable');
});
it('should associate click and href link with tooltip', function () {
@@ -1512,8 +1531,7 @@ describe('given a class diagram with relationships, ', function () {
const testClass = parser.yy.getClass('Class1');
expect(testClass.link).toBe('google.com');
expect(testClass.tooltip).toBe('A tooltip');
expect(testClass.cssClasses.length).toBe(1);
expect(testClass.cssClasses[0]).toBe('clickable');
expect(testClass.cssClasses).toBe('default clickable');
});
it('should associate click and href link with tooltip and target appropriately', function () {
@@ -1770,8 +1788,7 @@ C1 --> C2
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
const member = c1.members[0];
expect(member.getDisplayDetails().displayText).toBe('+member1');
});
@@ -1787,8 +1804,7 @@ cssClass "C1" styleClass
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
const member = c1.members[0];
expect(member.getDisplayDetails().displayText).toBe('+member1');
});
@@ -1805,13 +1821,11 @@ cssClass "C1,C2" styleClass
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass');
expect(c1.cssClasses).toBe('default styleClass');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Long long long long long long long long long long label');
expect(c2.cssClasses.length).toBe(1);
expect(c2.cssClasses[0]).toBe('styleClass');
expect(c2.cssClasses).toBe('default styleClass');
});
it('should parse two classes with text labels and css class shorthands', () => {
@@ -1825,13 +1839,11 @@ C1 --> C2
const c1 = classDb.getClass('C1');
expect(c1.label).toBe('Class 1 with text label');
expect(c1.cssClasses.length).toBe(1);
expect(c1.cssClasses[0]).toBe('styleClass1');
expect(c1.cssClasses).toBe('default styleClass1');
const c2 = classDb.getClass('C2');
expect(c2.label).toBe('Class 2 !@#$%^&*() label');
expect(c2.cssClasses.length).toBe(1);
expect(c2.cssClasses[0]).toBe('styleClass2');
expect(c2.cssClasses).toBe('default styleClass2');
});
it('should parse multiple classes with same text labels', () => {

View File

@@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
import parser from './parser/classDiagram.jison';
import db from './classDb.js';
import styles from './styles.js';
import renderer from './classRenderer.js';
import renderer from './classRenderer-v3-unified.js';
export const diagram: DiagramDefinition = {
parser,

View File

@@ -0,0 +1,79 @@
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import { getDiagramElement } from '../../rendering-util/insertElementsForSize.js';
import { getRegisteredLayoutAlgorithm, render } from '../../rendering-util/render.js';
import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
import type { LayoutData } from '../../rendering-util/types.js';
import utils from '../../utils.js';
/**
* Get the direction from the statement items.
* Look through all of the documents (docs) in the parsedItems
* Because is a _document_ direction, the default direction is not necessarily the same as the overall default _diagram_ direction.
* @param parsedItem - the parsed statement item to look through
* @param defaultDir - the direction to use if none is found
* @returns The direction to use
*/
export const getDir = (parsedItem: any, defaultDir = 'TB') => {
if (!parsedItem.doc) {
return defaultDir;
}
let dir = defaultDir;
for (const parsedItemDoc of parsedItem.doc) {
if (parsedItemDoc.stmt === 'dir') {
dir = parsedItemDoc.value;
}
}
return dir;
};
export const getClasses = function (
text: string,
diagramObj: any
): Map<string, DiagramStyleClassDef> {
return diagramObj.db.getClasses();
};
export const draw = async function (text: string, id: string, _version: string, diag: any) {
log.info('REF0:');
log.info('Drawing class diagram (v3)', id);
const { securityLevel, state: conf, layout } = getConfig();
// Extracting the data from the parsed structure into a more usable form
// Not related to the refactoring, but this is the first step in the rendering process
// diag.db.extract(diag.db.getRootDocV2());
// The getData method provided in all supported diagrams is used to extract the data from the parsed structure
// into the Layout data format
const data4Layout = diag.db.getData() as LayoutData;
// Create the root SVG - the element is the div containing the SVG element
const svg = getDiagramElement(id, securityLevel);
data4Layout.type = diag.type;
data4Layout.layoutAlgorithm = getRegisteredLayoutAlgorithm(layout);
data4Layout.nodeSpacing = conf?.nodeSpacing || 50;
data4Layout.rankSpacing = conf?.rankSpacing || 50;
data4Layout.markers = ['aggregation', 'extension', 'composition', 'dependency', 'lollipop'];
data4Layout.diagramId = id;
await render(data4Layout, svg);
const padding = 8;
utils.insertTitle(
svg,
'classDiagramTitleText',
conf?.titleTopMargin ?? 25,
diag.db.getDiagramTitle()
);
setupViewPortForSVG(svg, padding, 'classDiagram', conf?.useMaxWidth ?? true);
};
export default {
getClasses,
draw,
getDir,
};

View File

@@ -5,7 +5,9 @@ export interface ClassNode {
id: string;
type: string;
label: string;
cssClasses: string[];
shape: string;
text: string;
cssClasses: string;
methods: ClassMember[];
members: ClassMember[];
annotations: string[];
@@ -16,6 +18,7 @@ export interface ClassNode {
linkTarget?: string;
haveCallback?: boolean;
tooltip?: string;
look?: string;
}
export type Visibility = '#' | '+' | '~' | '-' | '';
@@ -30,6 +33,7 @@ export class ClassMember {
cssStyle!: string;
memberType!: 'method' | 'attribute';
visibility!: Visibility;
text: string;
/**
* denote if static or to determine which css class to apply to the node
* @defaultValue ''
@@ -50,6 +54,7 @@ export class ClassMember {
this.memberType = memberType;
this.visibility = '';
this.classifier = '';
this.text = '';
const sanitizedInput = sanitizeText(input, getConfig());
this.parseMember(sanitizedInput);
}
@@ -85,7 +90,7 @@ export class ClassMember {
this.visibility = detectedVisibility as Visibility;
}
this.id = match[2].trim();
this.id = match[2];
this.parameters = match[3] ? match[3].trim() : '';
potentialClassifier = match[4] ? match[4].trim() : '';
this.returnType = match[5] ? match[5].trim() : '';
@@ -118,6 +123,14 @@ export class ClassMember {
}
this.classifier = potentialClassifier;
// Preserve one space only
this.id = this.id.startsWith(' ') ? ' ' + this.id.trim() : this.id.trim();
const combinedText = `${this.visibility ? '\\' + this.visibility : ''}${parseGenericTypes(this.id)}${this.memberType === 'method' ? `(${parseGenericTypes(this.parameters)})${this.returnType ? ' : ' + parseGenericTypes(this.returnType) : ''}` : ''}`;
this.text = combinedText.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
if (this.text.startsWith('\\&lt;')) {
this.text = this.text.replace('\\&lt;', '~');
}
}
parseClassifier() {
@@ -154,6 +167,12 @@ export interface ClassRelation {
};
}
export interface Interface {
id: string;
label: string;
classId: string;
}
export interface NamespaceNode {
id: string;
domId: string;
@@ -161,5 +180,11 @@ export interface NamespaceNode {
children: NamespaceMap;
}
export interface StyleClass {
id: string;
styles: string[];
textStyles: string[];
}
export type ClassMap = Map<string, ClassNode>;
export type NamespaceMap = Map<string, NamespaceNode>;

View File

@@ -61,6 +61,7 @@ Function arguments are optional: 'call <callback_name>()' simply executes 'callb
<string>[^"]* return "STR";
<*>["] this.begin("string");
"style" return 'STYLE';
"classDef" return 'CLASSDEF';
<INITIAL,namespace>"namespace" { this.begin('namespace'); return 'NAMESPACE'; }
<namespace>\s*(\r?\n)+ { this.popState(); return 'NEWLINE'; }
@@ -265,6 +266,7 @@ statement
| styleStatement
| cssClassStatement
| noteStatement
| classDefStatement
| direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
@@ -326,6 +328,15 @@ noteStatement
| NOTE noteText { yy.addNote($2); }
;
classDefStatement
: CLASSDEF classList stylesOpt {$$ = $CLASSDEF;yy.defineClass($classList,$stylesOpt);}
;
classList
: ALPHA { $$ = [$ALPHA]; }
| classList COMMA ALPHA = { $$ = $classList.concat([$ALPHA]); }
;
direction
: direction_tb
{ yy.setDirection('TB');}

View File

@@ -0,0 +1,223 @@
import { select } from 'd3';
import { getConfig } from '../../config.js';
import { getNodeClasses } from '../../rendering-util/rendering-elements/shapes/util.js';
import { calculateTextWidth, decodeEntities } from '../../utils.js';
import type { ClassMember, ClassNode } from './classTypes.js';
import { sanitizeText } from '../../diagram-api/diagramAPI.js';
import { createText } from '../../rendering-util/createText.js';
import { evaluate, hasKatex } from '../common/common.js';
import type { Node } from '../../rendering-util/types.js';
import type { MermaidConfig } from '../../config.type.js';
import type { D3Selection } from '../../types.js';
// Creates the shapeSvg and inserts text
export async function textHelper<T extends SVGGraphicsElement>(
parent: D3Selection<T>,
node: any,
config: MermaidConfig,
useHtmlLabels: boolean,
GAP = config.class!.padding ?? 12
) {
const TEXT_PADDING = !useHtmlLabels ? 3 : 0;
const shapeSvg = parent
// @ts-ignore: Ignore error for using .insert on SVGAElement
.insert('g')
.attr('class', getNodeClasses(node))
.attr('id', node.domId || node.id);
let annotationGroup = null;
let labelGroup = null;
let membersGroup = null;
let methodsGroup = null;
let annotationGroupHeight = 0;
let labelGroupHeight = 0;
let membersGroupHeight = 0;
annotationGroup = shapeSvg.insert('g').attr('class', 'annotation-group text');
if (node.annotations.length > 0) {
const annotation = node.annotations[0];
await addText(annotationGroup, { text: `«${annotation}»` } as unknown as ClassMember, 0);
const annotationGroupBBox = annotationGroup.node()!.getBBox();
annotationGroupHeight = annotationGroupBBox.height;
}
labelGroup = shapeSvg.insert('g').attr('class', 'label-group text');
await addText(labelGroup, node, 0, ['font-weight: bolder']);
const labelGroupBBox = labelGroup.node()!.getBBox();
labelGroupHeight = labelGroupBBox.height;
membersGroup = shapeSvg.insert('g').attr('class', 'members-group text');
let yOffset = 0;
for (const member of node.members) {
const height = await addText(membersGroup, member, yOffset, [member.parseClassifier()]);
yOffset += height + TEXT_PADDING;
}
membersGroupHeight = membersGroup.node()!.getBBox().height;
if (membersGroupHeight <= 0) {
membersGroupHeight = GAP / 2;
}
methodsGroup = shapeSvg.insert('g').attr('class', 'methods-group text');
let methodsYOffset = 0;
for (const method of node.methods) {
const height = await addText(methodsGroup, method, methodsYOffset, [method.parseClassifier()]);
methodsYOffset += height + TEXT_PADDING;
}
let bbox = shapeSvg.node()!.getBBox();
// Center annotation
if (annotationGroup !== null) {
const annotationGroupBBox = annotationGroup.node()!.getBBox();
annotationGroup.attr('transform', `translate(${-annotationGroupBBox.width / 2})`);
}
// Adjust label
labelGroup.attr('transform', `translate(${-labelGroupBBox.width / 2}, ${annotationGroupHeight})`);
bbox = shapeSvg.node()!.getBBox();
membersGroup.attr(
'transform',
`translate(${0}, ${annotationGroupHeight + labelGroupHeight + GAP * 2})`
);
bbox = shapeSvg.node()!.getBBox();
methodsGroup.attr(
'transform',
`translate(${0}, ${annotationGroupHeight + labelGroupHeight + (membersGroupHeight ? membersGroupHeight + GAP * 4 : GAP * 2)})`
);
bbox = shapeSvg.node()!.getBBox();
return { shapeSvg, bbox };
}
// Modified version of labelHelper() to help create and place text for classes
async function addText<T extends SVGGraphicsElement>(
parentGroup: D3Selection<T>,
node: Node | ClassNode | ClassMember,
yOffset: number,
styles: string[] = []
) {
const textEl = parentGroup.insert('g').attr('class', 'label').attr('style', styles.join('; '));
const config = getConfig();
let useHtmlLabels =
'useHtmlLabels' in node ? node.useHtmlLabels : (evaluate(config.htmlLabels) ?? true);
let textContent = '';
// Support regular node type (.label) and classNodes (.text)
if ('text' in node) {
textContent = node.text;
} else {
textContent = node.label!;
}
// createText() will cause unwanted behavior because of classDiagram syntax so workarounds are needed
if (!useHtmlLabels && textContent.startsWith('\\')) {
textContent = textContent.substring(1);
}
if (hasKatex(textContent)) {
useHtmlLabels = true;
}
const text = await createText(
textEl,
sanitizeText(decodeEntities(textContent)),
{
width: calculateTextWidth(textContent, config) + 50, // Add room for error when splitting text into multiple lines
classes: 'markdown-node-label',
useHtmlLabels,
},
config
);
let bbox;
let numberOfLines = 1;
if (!useHtmlLabels) {
// Undo font-weight normal
if (styles.includes('font-weight: bolder')) {
select(text).selectAll('tspan').attr('font-weight', '');
}
numberOfLines = text.children.length;
const textChild = text.children[0];
if (text.textContent === '' || text.textContent.includes('&gt')) {
textChild.textContent =
textContent[0] +
textContent.substring(1).replaceAll('&gt;', '>').replaceAll('&lt;', '<').trim();
// Text was improperly removed due to spaces (preserve one space if present)
const preserveSpace = textContent[1] === ' ';
if (preserveSpace) {
textChild.textContent = textChild.textContent[0] + ' ' + textChild.textContent.substring(1);
}
}
// To support empty boxes
if (textChild.textContent === 'undefined') {
textChild.textContent = '';
}
// Get the bounding box after the text update
bbox = text.getBBox();
} else {
const div = text.children[0];
const dv = select(text);
numberOfLines = div.innerHTML.split('<br>').length;
// Katex math support
if (div.innerHTML.includes('</math>')) {
numberOfLines += div.innerHTML.split('<mrow>').length - 1;
}
// Support images
const images = div.getElementsByTagName('img');
if (images) {
const noImgText = textContent.replace(/<img[^>]*>/g, '').trim() === '';
await Promise.all(
[...images].map(
(img) =>
new Promise((res) => {
function setupImage() {
img.style.display = 'flex';
img.style.flexDirection = 'column';
if (noImgText) {
// default size if no text
const bodyFontSize =
config.fontSize?.toString() ?? window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
img.style.minWidth = width;
img.style.maxWidth = width;
} else {
img.style.width = '100%';
}
res(img);
}
setTimeout(() => {
if (img.complete) {
setupImage();
}
});
img.addEventListener('error', setupImage);
img.addEventListener('load', setupImage);
})
)
);
}
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
// Center text and offset by yOffset
textEl.attr('transform', 'translate(0,' + (-bbox.height / (2 * numberOfLines) + yOffset) + ')');
return bbox.height;
}

View File

@@ -20,6 +20,10 @@ const getStyles = (options) =>
.label text {
fill: ${options.classText};
}
.labelBkg {
background: ${options.mainBkg};
}
.edgeLabel .label span {
background: ${options.mainBkg};
}

View File

@@ -2,6 +2,7 @@ import { select } from 'd3';
import utils, { getEdgeId } from '../../utils.js';
import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js';
import common from '../common/common.js';
import { isValidShape, type ShapeID } from '../../rendering-util/rendering-elements/shapes.js';
import type { Node, Edge } from '../../rendering-util/types.js';
import { log } from '../../logger.js';
import * as yaml from 'js-yaml';
@@ -14,7 +15,15 @@ import {
setDiagramTitle,
getDiagramTitle,
} from '../common/commonDb.js';
import type { FlowVertex, FlowClass, FlowSubGraph, FlowText, FlowEdge, FlowLink } from './types.js';
import type {
FlowVertex,
FlowClass,
FlowSubGraph,
FlowText,
FlowEdge,
FlowLink,
FlowVertexTypeParam,
} from './types.js';
import type { NodeMetaData } from '../../types.js';
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
@@ -53,12 +62,11 @@ export const lookUpDomId = function (id: string) {
/**
* Function called by parser when a node definition has been found
*
*/
export const addVertex = function (
id: string,
textObj: FlowText,
type: 'group',
type: FlowVertexTypeParam,
style: string[],
classes: string[],
dir: string,
@@ -133,14 +141,15 @@ export const addVertex = function (
}
// console.log('yamlData', yamlData);
const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData;
if (doc.shape && (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_'))) {
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
}
// console.log('yamlData doc', doc);
if (doc?.shape) {
if (doc.shape) {
if (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_')) {
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
} else if (!isValidShape(doc.shape)) {
throw new Error(`No such shape: ${doc.shape}.`);
}
vertex.type = doc?.shape;
}
if (doc?.label) {
vertex.text = doc?.label;
}
@@ -816,7 +825,7 @@ export const lex = {
firstGraph,
};
const getTypeFromVertex = (vertex: FlowVertex) => {
const getTypeFromVertex = (vertex: FlowVertex): ShapeID => {
if (vertex.img) {
return 'imageSquare';
}
@@ -832,14 +841,18 @@ const getTypeFromVertex = (vertex: FlowVertex) => {
}
return 'icon';
}
if (vertex.type === 'square') {
return 'squareRect';
switch (vertex.type) {
case 'square':
case undefined:
return 'squareRect';
case 'round':
return 'roundedRect';
case 'ellipse':
// @ts-expect-error -- Ellipses are broken, see https://github.com/mermaid-js/mermaid/issues/5976
return 'ellipse';
default:
return vertex.type;
}
if (vertex.type === 'round') {
return 'roundedRect';
}
return vertex.type ?? 'squareRect';
};
const findNode = (nodes: Node[], id: string) => nodes.find((node) => node.id === id);
@@ -880,7 +893,7 @@ const addNodeFromVertex = (
node.cssCompiledStyles = getCompiledStyles(vertex.classes);
node.cssClasses = vertex.classes.join(' ');
} else {
nodes.push({
const baseNode = {
id: vertex.id,
label: vertex.text,
labelStyle: '',
@@ -889,10 +902,8 @@ const addNodeFromVertex = (
cssStyles: vertex.styles,
cssCompiledStyles: getCompiledStyles(['default', 'node', ...vertex.classes]),
cssClasses: 'default ' + vertex.classes.join(' '),
shape: getTypeFromVertex(vertex),
dir: vertex.dir,
domId: vertex.domId,
isGroup,
look,
link: vertex.link,
linkTarget: vertex.linkTarget,
@@ -903,7 +914,20 @@ const addNodeFromVertex = (
assetWidth: vertex.assetWidth,
assetHeight: vertex.assetHeight,
constraint: vertex.constraint,
});
};
if (isGroup) {
nodes.push({
...baseNode,
isGroup: true,
shape: 'rect',
});
} else {
nodes.push({
...baseNode,
isGroup: false,
shape: getTypeFromVertex(vertex),
});
}
}
};

View File

@@ -197,6 +197,21 @@ describe('when parsing directions', function () {
expect(data4Layout.nodes[0].shape).toEqual('squareRect');
expect(data4Layout.nodes[0].label).toEqual('This is }');
});
it('should error on non-existent shape', function () {
expect(() => {
flow.parser.parse(`flowchart TB
A@{ shape: this-shape-does-not-exist }
`);
}).toThrow('No such shape: this-shape-does-not-exist.');
});
it('should error on internal-only shape', function () {
expect(() => {
// this shape does exist, but it's only supposed to be for internal/backwards compatibility use
flow.parser.parse(`flowchart TB
A@{ shape: rect_left_inv_arrow }
`);
}).toThrow('No such shape: rect_left_inv_arrow. Shape names should be lowercase.');
});
it('Diamond shapes should work as usual', function () {
const res = flow.parser.parse(`flowchart TB
A{This is a label}

View File

@@ -1,3 +1,28 @@
import type { ShapeID } from '../../rendering-util/rendering-elements/shapes.js';
/**
* Valid `type` args to `yy.addVertex` taken from
* `packages/mermaid/src/diagrams/flowchart/parser/flow.jison`
*/
export type FlowVertexTypeParam =
| undefined
| 'square'
| 'doublecircle'
| 'circle'
| 'ellipse'
| 'stadium'
| 'subroutine'
| 'rect'
| 'cylinder'
| 'round'
| 'diamond'
| 'hexagon'
| 'odd'
| 'trapezoid'
| 'inv_trapezoid'
| 'lean_right'
| 'lean_left';
export interface FlowVertex {
classes: string[];
dir?: string;
@@ -10,7 +35,7 @@ export interface FlowVertex {
props?: any;
styles: string[];
text?: string;
type?: string;
type?: ShapeID | FlowVertexTypeParam;
icon?: string;
form?: string;
pos?: 't' | 'b';

View File

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

View File

@@ -0,0 +1,13 @@
// @ts-ignore: JISON doesn't support types
import parser from './parser/kanban.jison';
import db from './kanbanDb.js';
import renderer from './kanbanRenderer.js';
import styles from './styles.js';
import type { DiagramDefinition } from '../../diagram-api/types.js';
export const diagram: DiagramDefinition = {
db,
renderer,
parser,
styles,
};

View File

@@ -0,0 +1,494 @@
// @ts-expect-error No types available for JISON
import { parser as kanban } from './parser/kanban.jison';
import kanbanDB from './kanbanDb.js';
import type { KanbanNode } from '../../rendering-util/types.js';
// Todo fix utils functions for tests
import { setLogLevel } from '../../diagram-api/diagramAPI.js';
describe('when parsing a kanban ', function () {
beforeEach(function () {
kanban.yy = kanbanDB;
kanban.yy.clear();
setLogLevel('trace');
});
describe('hiearchy', function () {
it('KNBN-1 should handle a simple root definition abc122', function () {
const str = `kanban
root`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections.length).toEqual(1);
expect(sections[0].label).toEqual('root');
});
it('KNBN-2 should handle a hierachial kanban definition', function () {
const str = `kanban
root
child1
child2
`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections.length).toEqual(1);
expect(sections[0].label).toEqual('root');
expect(children.length).toEqual(2);
expect(children[0].label).toEqual('child1');
expect(children[1].label).toEqual('child2');
});
/** CATCH case when a lower level comes later, should throw
* a
* b
* c
*/
it('3 should handle a simple root definition with a shape and without an id abc123', function () {
const str = `kanban
(root)`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].label).toEqual('root');
});
it('KNBN-4 should not dsitinguis between deeper hierachial levels in thr kanban definition', function () {
const str = `kanban
root
child1
leaf1
child2`;
// less picky is better
// expect(() => kanban.parse(str)).toThrow(
// 'There can be only one root. No parent could be found for ("fakeRoot")'
// );
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections.length).toBe(1);
expect(children.length).toBe(3);
});
it('5 Multiple sections are ok', function () {
const str = `kanban
section1
section2`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections.length).toBe(2);
expect(sections[0].label).toBe('section1');
expect(sections[1].label).toBe('section2');
// expect(() => kanban.parse(str)).toThrow(
// 'There can be only one root. No parent could be found for ("fakeRoot")'
// );
});
it('KNBN-6 real root in wrong place', function () {
const str = `kanban
root
fakeRoot
realRootWrongPlace`;
expect(() => kanban.parse(str)).toThrow(
'Items without section detected, found section ("fakeRoot")'
);
});
});
describe('nodes', function () {
it('KNBN-7 should handle an id and type for a node definition', function () {
const str = `kanban
root[The root]
`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('The root');
});
it('KNBN-8 should handle an id and type for a node definition', function () {
const str = `kanban
root
theId(child1)`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].label).toEqual('root');
expect(children.length).toEqual(1);
const child = children[0];
expect(child.label).toEqual('child1');
expect(child.id).toEqual('theId');
});
it('KNBN-9 should handle an id and type for a node definition', function () {
const str = `kanban
root
theId(child1)`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].label).toEqual('root');
expect(children.length).toEqual(1);
const child = children[0];
expect(child.label).toEqual('child1');
expect(child.id).toEqual('theId');
});
});
describe('decorations', function () {
it('KNBN-13 should be possible to set an icon for the node', function () {
const str = `kanban
root[The root]
::icon(bomb)
`;
// ::class1 class2
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('The root');
expect(sections[0].icon).toEqual('bomb');
});
it('KNBN-14 should be possible to set classes for the node', function () {
const str = `kanban
root[The root]
:::m-4 p-8
`;
// ::class1 class2
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('The root');
expect(sections[0].cssClasses).toEqual('m-4 p-8');
});
it('KNBN-15 should be possible to set both classes and icon for the node', function () {
const str = `kanban
root[The root]
:::m-4 p-8
::icon(bomb)
`;
// ::class1 class2
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('The root');
expect(sections[0].cssClasses).toEqual('m-4 p-8');
expect(sections[0].icon).toEqual('bomb');
});
it('KNBN-16 should be possible to set both classes and icon for the node', function () {
const str = `kanban
root[The root]
::icon(bomb)
:::m-4 p-8
`;
// ::class1 class2
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('The root');
// expect(sections[0].type).toEqual('rect');
expect(sections[0].cssClasses).toEqual('m-4 p-8');
expect(sections[0].icon).toEqual('bomb');
});
});
describe('descriptions', function () {
it('KNBN-17 should be possible to use node syntax in the descriptions', function () {
const str = `kanban
root["String containing []"]
`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('String containing []');
});
it('KNBN-18 should be possible to use node syntax in the descriptions in children', function () {
const str = `kanban
root["String containing []"]
child1["String containing ()"]
`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('String containing []');
expect(children.length).toEqual(1);
expect(children[0].label).toEqual('String containing ()');
});
it('KNBN-19 should be possible to have a child after a class assignment', function () {
const str = `kanban
root(Root)
Child(Child)
:::hot
a(a)
b[New Stuff]`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('Root');
expect(children.length).toEqual(3);
const item1 = children[0];
const item2 = children[1];
const item3 = children[2];
expect(item1.id).toEqual('Child');
expect(item2.id).toEqual('a');
expect(item3.id).toEqual('b');
});
});
it('KNBN-20 should be possible to have meaningless empty rows in a kanban abc124', function () {
const str = `kanban
root(Root)
Child(Child)
a(a)
b[New Stuff]`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('Root');
expect(children.length).toEqual(3);
const item1 = children[0];
const item2 = children[1];
const item3 = children[2];
expect(item1.id).toEqual('Child');
expect(item2.id).toEqual('a');
expect(item3.id).toEqual('b');
});
it('KNBN-21 should be possible to have comments in a kanban', function () {
const str = `kanban
root(Root)
Child(Child)
a(a)
%% This is a comment
b[New Stuff]`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('Root');
const child = children[0];
expect(child.id).toEqual('Child');
expect(children[1].id).toEqual('a');
expect(children[2].id).toEqual('b');
expect(children.length).toEqual(3);
});
it('KNBN-22 should be possible to have comments at the end of a line', function () {
const str = `kanban
root(Root)
Child(Child)
a(a) %% This is a comment
b[New Stuff]`;
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(sections[0].label).toEqual('Root');
expect(children.length).toEqual(3);
const child1 = children[0];
expect(child1.id).toEqual('Child');
const child2 = children[1];
expect(child2.id).toEqual('a');
const child3 = children[2];
expect(child3.id).toEqual('b');
});
it('KNBN-23 Rows with only spaces should not interfere', function () {
const str = 'kanban\nroot\n A\n \n\n B';
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(children.length).toEqual(2);
const child = children[0];
expect(child.id).toEqual('A');
const child2 = children[1];
expect(child2.id).toEqual('B');
});
it('KNBN-24 Handle rows above the kanban declarations', function () {
const str = '\n \nkanban\nroot\n A\n \n\n B';
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(children.length).toEqual(2);
const child = children[0];
expect(child.id).toEqual('A');
const child2 = children[1];
expect(child2.id).toEqual('B');
});
it('KNBN-25 Handle rows above the kanban declarations, no space', function () {
const str = '\n\n\nkanban\nroot\n A\n \n\n B';
kanban.parse(str);
const data = kanban.yy.getData();
const sections = kanban.yy.getSections();
const children = data.nodes.filter((n: KanbanNode) => n.parentId === sections[0].id);
expect(sections[0].id).toEqual('root');
expect(children.length).toEqual(2);
const child = children[0];
expect(child.id).toEqual('A');
const child2 = children[1];
expect(child2.id).toEqual('B');
});
});
describe('item data data', function () {
beforeEach(function () {
kanban.yy = kanbanDB;
kanban.yy.clear();
setLogLevel('trace');
});
it('KNBN-30 should be possible to set the priority', function () {
let str = `kanban
root
`;
str = `kanban
root@{ priority: high }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].priority).toEqual('high');
});
it('KNBN-31 should be possible to set the assignment', function () {
const str = `kanban
root@{ assigned: knsv }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].assigned).toEqual('knsv');
});
it('KNBN-32 should be possible to set the icon', function () {
const str = `kanban
root@{ icon: star }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].icon).toEqual('star');
});
it('KNBN-33 should be possible to set the icon', function () {
const str = `kanban
root@{ icon: star }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].icon).toEqual('star');
});
it('KNBN-34 should be possible to set the metadata using multiple lines', function () {
const str = `kanban
root@{
icon: star
assigned: knsv
}
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].icon).toEqual('star');
expect(sections[0].assigned).toEqual('knsv');
});
it('KNBN-35 should be possible to set the metadata using one line', function () {
const str = `kanban
root@{ icon: star, assigned: knsv }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].id).toEqual('root');
expect(sections[0].icon).toEqual('star');
expect(sections[0].assigned).toEqual('knsv');
});
it('KNBN-36 should be possible to set the label using the new syntax', function () {
const str = `kanban
root@{ icon: star, label: 'fix things' }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
expect(sections[0].label).toEqual('fix things');
});
it('KNBN-37 should be possible to set the external id', function () {
const str = `kanban
root@{ ticket: MC-1234 }
`;
kanban.parse(str);
const sections = kanban.yy.getSections();
const data = kanban.yy.getData();
expect(sections[0].id).toEqual('root');
expect(sections[0].ticket).toEqual('MC-1234');
});
});

View File

@@ -0,0 +1,253 @@
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { D3Element } from '../../types.js';
import { sanitizeText } from '../../diagrams/common/common.js';
import { log } from '../../logger.js';
import type { Edge, KanbanNode } from '../../rendering-util/types.js';
import defaultConfig from '../../defaultConfig.js';
import type { NodeMetaData } from '../../types.js';
import * as yaml from 'js-yaml';
let nodes: KanbanNode[] = [];
let sections: KanbanNode[] = [];
let cnt = 0;
let elements: Record<number, D3Element> = {};
const clear = () => {
nodes = [];
sections = [];
cnt = 0;
elements = {};
};
/*
* if your level is the section level return null - then you do not belong to a level
* otherwise return the current section
*/
const getSection = (level: number) => {
if (nodes.length === 0) {
// console.log('No nodes');
return null;
}
const sectionLevel = nodes[0].level;
let lastSection = null;
for (let i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].level === sectionLevel && !lastSection) {
lastSection = nodes[i];
// console.log('lastSection found', lastSection);
}
// console.log('HERE', nodes[i].id, level, nodes[i].level, sectionLevel);
if (nodes[i].level < sectionLevel) {
throw new Error('Items without section detected, found section ("' + nodes[i].label + '")');
}
}
if (level === lastSection?.level) {
return null;
}
// No found
return lastSection;
};
const getSections = function () {
return sections;
};
const getData = function () {
const edges = [] as Edge[];
const _nodes: KanbanNode[] = [];
const sections = getSections();
const conf = getConfig();
for (const section of sections) {
const node = {
id: section.id,
label: sanitizeText(section.label ?? '', conf),
isGroup: true,
ticket: section.ticket,
shape: 'kanbanSection',
level: section.level,
look: conf.look,
} satisfies KanbanNode;
_nodes.push(node);
const children = nodes.filter((n) => n.parentId === section.id);
for (const item of children) {
const childNode = {
id: item.id,
parentId: section.id,
label: sanitizeText(item.label ?? '', conf),
isGroup: false,
ticket: item?.ticket,
priority: item?.priority,
assigned: item?.assigned,
icon: item?.icon,
shape: 'kanbanItem',
level: item.level,
rx: 5,
ry: 5,
cssStyles: ['text-align: left'],
} satisfies KanbanNode;
_nodes.push(childNode);
}
}
return { nodes: _nodes, edges, other: {}, config: getConfig() };
};
const addNode = (level: number, id: string, descr: string, type: number, shapeData: string) => {
const conf = getConfig();
let padding: number = conf.mindmap?.padding ?? defaultConfig.mindmap.padding;
switch (type) {
case nodeType.ROUNDED_RECT:
case nodeType.RECT:
case nodeType.HEXAGON:
padding *= 2;
}
const node: KanbanNode = {
id: sanitizeText(id, conf) || 'kbn' + cnt++,
level,
label: sanitizeText(descr, conf),
width: conf.mindmap?.maxNodeWidth ?? defaultConfig.mindmap.maxNodeWidth,
padding,
isGroup: false,
} satisfies KanbanNode;
if (shapeData !== undefined) {
let yamlData;
// detect if shapeData contains a newline character
// console.log('shapeData', shapeData);
if (!shapeData.includes('\n')) {
// console.log('yamlData shapeData has no new lines', shapeData);
yamlData = '{\n' + shapeData + '\n}';
} else {
// console.log('yamlData shapeData has new lines', shapeData);
yamlData = shapeData + '\n';
}
const doc = yaml.load(yamlData, { schema: yaml.JSON_SCHEMA }) as NodeMetaData;
// console.log('yamlData', doc);
if (doc.shape && (doc.shape !== doc.shape.toLowerCase() || doc.shape.includes('_'))) {
throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`);
}
// if shape is defined in the yaml data, use it if it is a valid shape kanbanItem
if (doc?.shape && doc.shape === 'kanbanItem') {
node.shape = doc?.shape;
}
if (doc?.label) {
node.label = doc?.label;
}
if (doc?.icon) {
node.icon = doc?.icon;
}
if (doc?.assigned) {
node.assigned = doc?.assigned;
}
if (doc?.ticket) {
node.ticket = doc?.ticket;
}
if (doc?.priority) {
node.priority = doc?.priority;
}
}
const section = getSection(level);
if (section) {
// @ts-ignore false positive for section.id
node.parentId = section.id || 'kbn' + cnt++;
} else {
sections.push(node);
}
nodes.push(node);
};
const nodeType = {
DEFAULT: 0,
NO_BORDER: 0,
ROUNDED_RECT: 1,
RECT: 2,
CIRCLE: 3,
CLOUD: 4,
BANG: 5,
HEXAGON: 6,
};
const getType = (startStr: string, endStr: string): number => {
log.debug('In get type', startStr, endStr);
switch (startStr) {
case '[':
return nodeType.RECT;
case '(':
return endStr === ')' ? nodeType.ROUNDED_RECT : nodeType.CLOUD;
case '((':
return nodeType.CIRCLE;
case ')':
return nodeType.CLOUD;
case '))':
return nodeType.BANG;
case '{{':
return nodeType.HEXAGON;
default:
return nodeType.DEFAULT;
}
};
const setElementForId = (id: number, element: D3Element) => {
elements[id] = element;
};
const decorateNode = (decoration?: { class?: string; icon?: string }) => {
if (!decoration) {
return;
}
const config = getConfig();
const node = nodes[nodes.length - 1];
if (decoration.icon) {
node.icon = sanitizeText(decoration.icon, config);
}
if (decoration.class) {
node.cssClasses = sanitizeText(decoration.class, config);
}
};
const type2Str = (type: number) => {
switch (type) {
case nodeType.DEFAULT:
return 'no-border';
case nodeType.RECT:
return 'rect';
case nodeType.ROUNDED_RECT:
return 'rounded-rect';
case nodeType.CIRCLE:
return 'circle';
case nodeType.CLOUD:
return 'cloud';
case nodeType.BANG:
return 'bang';
case nodeType.HEXAGON:
return 'hexgon'; // cspell: disable-line
default:
return 'no-border';
}
};
// Expose logger to grammar
const getLogger = () => log;
const getElementById = (id: number) => elements[id];
const db = {
clear,
addNode,
getSections,
getData,
nodeType,
getType,
setElementForId,
decorateNode,
type2Str,
getLogger,
getElementById,
} as const;
export default db;

View File

@@ -0,0 +1,96 @@
import { getConfig } from '../../diagram-api/diagramAPI.js';
import type { DrawDefinition } from '../../diagram-api/types.js';
import { log } from '../../logger.js';
import { selectSvgElement } from '../../rendering-util/selectSvgElement.js';
import { setupGraphViewbox } from '../../setupGraphViewbox.js';
import type { KanbanDB } from './kanbanTypes.js';
import defaultConfig from '../../defaultConfig.js';
import { insertCluster } from '../../rendering-util/rendering-elements/clusters.js';
import { insertNode, positionNode } from '../../rendering-util/rendering-elements/nodes.js';
import type { ClusterNode } from '../../rendering-util/types.js';
export const draw: DrawDefinition = async (text, id, _version, diagObj) => {
log.debug('Rendering kanban diagram\n' + text);
const db = diagObj.db as KanbanDB;
const data4Layout = db.getData();
const conf = getConfig();
conf.htmlLabels = false;
const svg = selectSvgElement(id);
// Draw the graph and start with drawing the nodes without proper position
// this gives us the size of the nodes and we can set the positions later
const sectionsElem = svg.append('g');
sectionsElem.attr('class', 'sections');
const nodesElem = svg.append('g');
nodesElem.attr('class', 'items');
const sections = data4Layout.nodes.filter(
// TODO: TypeScript 5.5 will infer this predicate automatically
(node): node is typeof node & ClusterNode => node.isGroup
);
let cnt = 0;
// TODO set padding
const padding = 10;
const sectionObjects = [];
let maxLabelHeight = 25;
for (const section of sections) {
const WIDTH = conf?.kanban?.sectionWidth || 200;
// const top = (-WIDTH * 3) / 2 + 25;
// let y = top;
cnt = cnt + 1;
section.x = WIDTH * cnt + ((cnt - 1) * padding) / 2;
section.width = WIDTH;
section.y = 0;
section.height = WIDTH * 3;
section.rx = 5;
section.ry = 5;
// Todo, use theme variable THEME_COLOR_LIMIT instead of 10
section.cssClasses = section.cssClasses + ' section-' + cnt;
const sectionObj = await insertCluster(sectionsElem, section);
maxLabelHeight = Math.max(maxLabelHeight, sectionObj?.labelBBox?.height);
sectionObjects.push(sectionObj);
}
let i = 0;
for (const section of sections) {
const sectionObj = sectionObjects[i];
i = i + 1;
const WIDTH = conf?.kanban?.sectionWidth || 200;
const top = (-WIDTH * 3) / 2 + maxLabelHeight;
let y = top;
const sectionItems = data4Layout.nodes.filter((node) => node.parentId === section.id);
for (const item of sectionItems) {
if (item.isGroup) {
// Kanban diagrams should not have groups within groups
// this should never happen
throw new Error('Groups within groups are not allowed in Kanban diagrams');
}
item.x = section.x;
item.width = WIDTH - 1.5 * padding;
const nodeEl = await insertNode(nodesElem, item, { config: conf });
const bbox = nodeEl.node()!.getBBox();
item.y = y + bbox.height / 2;
await positionNode(item);
y = item.y + bbox.height / 2 + padding / 2;
}
const rect = sectionObj.cluster.select('rect');
const height = Math.max(y - top + 3 * padding, 50) + (maxLabelHeight - 25);
rect.attr('height', height);
}
// Setup the view box and size of the svg element
setupGraphViewbox(
undefined,
svg,
conf.mindmap?.padding ?? defaultConfig.kanban.padding,
conf.mindmap?.useMaxWidth ?? defaultConfig.kanban.useMaxWidth
);
};
export default {
draw,
};

View File

@@ -0,0 +1,3 @@
import type kanbanDb from './kanbanDb.js';
export type KanbanDB = typeof kanbanDb;

View File

@@ -0,0 +1,166 @@
/** mermaid
* https://knsv.github.io/mermaid
* (c) 2015 Knut Sveidqvist
* MIT license.
*/
%lex
%options case-insensitive
%{
// Pre-lexer code can go here
%}
%x NODE
%x NSTR
%x NSTR2
%x ICON
%x CLASS
%x shapeData
%x shapeDataStr
%x shapeDataEndBracket
%%
\@\{ {
// console.log('=> shapeData', yytext);
this.pushState("shapeData"); yytext=""; return 'SHAPE_DATA' }
<shapeData>["] {
// console.log('=> shapeDataStr', yytext);
this.pushState("shapeDataStr");
return 'SHAPE_DATA';
}
<shapeDataStr>["] {
// console.log('shapeData <==', yytext);
this.popState(); return 'SHAPE_DATA'}
<shapeDataStr>[^\"]+ {
// console.log('shapeData', yytext);
const re = /\n\s*/g;
yytext = yytext.replace(re,"<br/>");
return 'SHAPE_DATA'}
<shapeData>[^}^"]+ {
// console.log('shapeData', yytext);
return 'SHAPE_DATA';
}
<shapeData>"}" {
// console.log('<== root', yytext)
this.popState();
}
\s*\%\%.* {yy.getLogger().trace('Found comment',yytext); return 'SPACELINE';}
// \%\%[^\n]*\n /* skip comments */
"kanban" {return 'KANBAN';}
":::" { this.begin('CLASS'); }
<CLASS>.+ { this.popState();return 'CLASS'; }
<CLASS>\n { this.popState();}
// [\s]*"::icon(" { this.begin('ICON'); }
"::icon(" { yy.getLogger().trace('Begin icon');this.begin('ICON'); }
[\s]+[\n] {yy.getLogger().trace('SPACELINE');return 'SPACELINE' /* skip all whitespace */ ;}
[\n]+ return 'NL';
<ICON>[^\)]+ { return 'ICON'; }
<ICON>\) {yy.getLogger().trace('end icon');this.popState();}
"-)" { yy.getLogger().trace('Exploding node'); this.begin('NODE');return 'NODE_DSTART'; }
"(-" { yy.getLogger().trace('Cloud'); this.begin('NODE');return 'NODE_DSTART'; }
"))" { yy.getLogger().trace('Explosion Bang'); this.begin('NODE');return 'NODE_DSTART'; }
")" { yy.getLogger().trace('Cloud Bang'); this.begin('NODE');return 'NODE_DSTART'; }
"((" { this.begin('NODE');return 'NODE_DSTART'; }
"{{" { this.begin('NODE');return 'NODE_DSTART'; }
"(" { this.begin('NODE');return 'NODE_DSTART'; }
"[" { this.begin('NODE');return 'NODE_DSTART'; }
[\s]+ return 'SPACELIST' /* skip all whitespace */ ;
// !(-\() return 'NODE_ID';
[^\(\[\n\)\{\}@]+ {return 'NODE_ID';}
<<EOF>> return 'EOF';
<NODE>["][`] { this.begin("NSTR2");}
<NSTR2>[^`"]+ { return "NODE_DESCR";}
<NSTR2>[`]["] { this.popState();}
<NODE>["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
<NSTR>[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
<NSTR>["] {this.popState();}
<NODE>[\)]\) {this.popState();yy.getLogger().trace('node end ))');return "NODE_DEND";}
<NODE>[\)] {this.popState();yy.getLogger().trace('node end )');return "NODE_DEND";}
<NODE>[\]] {this.popState();yy.getLogger().trace('node end ...',yytext);return "NODE_DEND";}
<NODE>"}}" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>"(-" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
<NODE>"-)" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
<NODE>"((" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
<NODE>[^\)\]\(\}]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
<NODE>.+(?!\(\() { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
// [\[] return 'NODE_START';
// .+ return 'TXT' ;
/lex
%start start
%% /* language grammar */
start
// %{ : info document 'EOF' { return yy; } }
: mindMap
| spaceLines mindMap
;
spaceLines
: SPACELINE
| spaceLines SPACELINE
| spaceLines NL
;
mindMap
: KANBAN document { return yy; }
| KANBAN NL document { return yy; }
;
stop
: NL {yy.getLogger().trace('Stop NL ');}
| EOF {yy.getLogger().trace('Stop EOF ');}
| SPACELINE
| stop NL {yy.getLogger().trace('Stop NL2 ');}
| stop EOF {yy.getLogger().trace('Stop EOF2 ');}
;
document
: document statement stop
| statement stop
;
statement
: SPACELIST node shapeData { yy.getLogger().info('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type, $3); }
| SPACELIST node { yy.getLogger().info('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type); }
| SPACELIST ICON { yy.getLogger().trace('Icon: ',$2);yy.decorateNode({icon: $2}); }
| SPACELIST CLASS { yy.decorateNode({class: $2}); }
| SPACELINE { yy.getLogger().trace('SPACELIST');}
| node shapeData { yy.getLogger().trace('Node: ',$1.id);yy.addNode(0, $1.id, $1.descr, $1.type, $2); }
| node { yy.getLogger().trace('Node: ',$1.id);yy.addNode(0, $1.id, $1.descr, $1.type); }
| ICON { yy.decorateNode({icon: $1}); }
| CLASS { yy.decorateNode({class: $1}); }
| SPACELIST
;
node
:nodeWithId
|nodeWithoutId
;
nodeWithoutId
: NODE_DSTART NODE_DESCR NODE_DEND
{ yy.getLogger().trace("node found ..", $1); $$ = { id: $2, descr: $2, type: yy.getType($1, $3) }; }
;
nodeWithId
: NODE_ID { $$ = { id: $1, descr: $1, type: 0 }; }
| NODE_ID NODE_DSTART NODE_DESCR NODE_DEND
{ yy.getLogger().trace("node found ..", $1); $$ = { id: $1, descr: $3, type: yy.getType($2, $4) }; }
;
shapeData:
shapeData SHAPE_DATA
{ $$ = $1 + $2; }
| SHAPE_DATA
{ $$ = $1; }
;
%%

View File

@@ -0,0 +1,105 @@
```mermaid
kanban
New
Sometimes wrong Shape type is highlighted
In progress
```
```mermaid
kanban
Todo
Create JISON
Update DB function
Create parsing tests
define getData
Create renderer
In progress
Design grammar
```
Adding ID
```mermaid
kanban
id1[Todo]
id2[Create JISON]
id3[Update DB function]
id4[Create parsing tests]
id5[define getData]
id6[Create renderer]
id7[In progress]
id8[Design grammar]
```
Background color for section
```mermaid
kanban
id1[Todo]
id2[Create JISON]
id3[Update DB function]
id4[Create parsing tests]
id5[define getData]
id6[Create renderer]
id7[In progress]
id8[Design grammar]
style n2 stroke:#AA00FF,fill:#E1BEE7
```
Background color for section
```mermaid
kanban
id1[Todo]
id2[Create JISON]
id3[Update DB function]
id4[Create parsing tests]
id5[define getData]
id6[Create renderer]
id7[In progress]
id8[Design grammar]
id2@{
assigned: knsv
icon: heart
priority: high
descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
}
style n1 stroke:#AA00FF,fill:#E1BEE7
```
Background color for section
```mermaid
---
config:
kanban:
showIds: true
fields: [[title],[description][id, assigned]]
---
kanban
id1[Todo]
id2[Create JISON]
id3[Update DB function]
id4[Create parsing tests]
id5[define getData]
id6[Create renderer]
id7[In progress]
id8[Design grammar]
id2@{
assigned: knsv
icon: heart
priority: high
descr: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
}
style n1 stroke:#AA00FF,fill:#E1BEE7
```
priority - dedicated
link - dedicated

View File

@@ -0,0 +1,109 @@
// @ts-expect-error Incorrect khroma types
import { darken, lighten, isDark } from 'khroma';
import type { DiagramStylesProvider } from '../../diagram-api/types.js';
const genSections: DiagramStylesProvider = (options) => {
let sections = '';
for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
options['lineColor' + i] = options['lineColor' + i] || options['cScaleInv' + i];
if (isDark(options['lineColor' + i])) {
options['lineColor' + i] = lighten(options['lineColor' + i], 20);
} else {
options['lineColor' + i] = darken(options['lineColor' + i], 20);
}
}
const adjuster = (color: string, level: number) =>
options.darkMode ? darken(color, level) : lighten(color, level);
for (let i = 0; i < options.THEME_COLOR_LIMIT; i++) {
const sw = '' + (17 - 3 * i);
sections += `
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${
i - 1
} polygon, .section-${i - 1} path {
fill: ${adjuster(options['cScale' + i], 10)};
stroke: ${adjuster(options['cScale' + i], 10)};
}
.section-${i - 1} text {
fill: ${options['cScaleLabel' + i]};
}
.node-icon-${i - 1} {
font-size: 40px;
color: ${options['cScaleLabel' + i]};
}
.section-edge-${i - 1}{
stroke: ${options['cScale' + i]};
}
.edge-depth-${i - 1}{
stroke-width: ${sw};
}
.section-${i - 1} line {
stroke: ${options['cScaleInv' + i]} ;
stroke-width: 3;
}
.disabled, .disabled circle, .disabled text {
fill: lightgray;
}
.disabled text {
fill: #efefef;
}
.node rect,
.node circle,
.node ellipse,
.node polygon,
.node path {
fill: ${options.background};
stroke: ${options.nodeBorder};
stroke-width: 1px;
}
.kanban-ticket-link {
fill: ${options.background};
stroke: ${options.nodeBorder};
text-decoration: underline;
}
`;
}
return sections;
};
// TODO: These options seem incorrect.
const getStyles: DiagramStylesProvider = (options) =>
`
.edge {
stroke-width: 3;
}
${genSections(options)}
.section-root rect, .section-root path, .section-root circle, .section-root polygon {
fill: ${options.git0};
}
.section-root text {
fill: ${options.gitBranchLabel0};
}
.icon-container {
height:100%;
display: flex;
justify-content: center;
align-items: center;
}
.edge {
fill: none;
}
.cluster-label, .label {
color: ${options.textColor};
fill: ${options.textColor};
}
.kanban-label {
dy: 1em;
alignment-baseline: middle;
text-anchor: middle;
dominant-baseline: middle;
text-align: center;
}
`;
export default getStyles;

View File

@@ -1,14 +0,0 @@
<template>
<div class="-mt-6 mb-8">
<a
href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;whiteboard"
target="_blank"
><img
src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light"
alt="Mermaid&#0032;Whiteboard - Drag&#0032;&#0038;&#0032;Drop&#0032;your&#0032;Nodes&#0032;with&#0032;Mermaid&#0039;s&#0032;new&#0032;Whiteboard&#0033; | Product Hunt"
style="width: 250px; height: 54px"
width="250"
height="54"
/></a>
</div>
</template>

View File

@@ -7,17 +7,21 @@ interface Taglines {
}
const taglines: Taglines[] = [
{
label: 'Explore the Mermaid Whiteboard from the creators of Mermaid',
url: 'https://www.mermaidchart.com/whiteboard?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=whiteboard',
},
{
label: 'Use the Visual Editor in Mermaid Chart to design and build diagrams',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=visual_editor',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=visual_editor',
},
{
label: 'Diagram live with teammates in Mermaid Chart',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=teams',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=teams',
},
{
label: 'Skip the rough draft with Mermaid AI in Mermaid Chart',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=mermaid_ai',
label: 'Replace ChatGPT Pro, Mermaid.live, and LucidChart with Mermaid Pro',
url: 'https://www.mermaidchart.com/play?utm_source=mermaid_js&utm_medium=banner_ad&utm_campaign=AIbundle',
},
];
@@ -25,7 +29,7 @@ let index = ref(Math.floor(Math.random() * taglines.length));
onMounted(() => {
setInterval(() => {
index.value = (index.value + 1) % taglines.length;
}, 60_000);
}, 40_000);
});
</script>
@@ -41,7 +45,7 @@ onMounted(() => {
>
<span class="font-semibold">{{ taglines[index].label }}</span>
<button class="rounded bg-[#111113] p-1 px-2 text-sm font-semibold tracking-wide">
Try it now
Try now
</button>
</a>
</transition>

View File

@@ -157,6 +157,7 @@ function sidebarSyntax() {
{ text: 'XY Chart 🔥', link: '/syntax/xyChart' },
{ text: 'Block Diagram 🔥', link: '/syntax/block' },
{ text: 'Packet 🔥', link: '/syntax/packet' },
{ text: 'Kanban 🔥', link: '/syntax/kanban' },
{ text: 'Architecture 🔥', link: '/syntax/architecture' },
{ text: 'Other Examples', link: '/syntax/examples' },
],
@@ -174,6 +175,7 @@ function sidebarConfig() {
{ text: 'API-Usage', link: '/config/usage' },
{ text: 'Mermaid API Configuration', link: '/config/setup/README' },
{ text: 'Mermaid Configuration Options', link: '/config/schema-docs/config' },
{ text: 'Registering icons', link: '/config/icons' },
{ text: 'Directives', link: '/config/directives' },
{ text: 'Theming', link: '/config/theming' },
{ text: 'Math', link: '/config/math' },

View File

@@ -9,8 +9,6 @@ import Contributors from '../components/Contributors.vue';
import HomePage from '../components/HomePage.vue';
// @ts-ignore Type not available
import TopBar from '../components/TopBar.vue';
// @ts-ignore Type not available
import ProductHuntBadge from '../components/ProductHuntBadge.vue';
import { getRedirect } from './redirect.js';
// @ts-ignore Type not available
import 'uno.css';
@@ -25,7 +23,6 @@ export default {
return h(Theme.Layout, null, {
// Keeping this as comment as it took a lot of time to figure out how to add a component to the top bar.
'home-hero-before': () => h(TopBar),
'home-hero-info-before': () => h(ProductHuntBadge),
'home-features-after': () => h(HomePage),
'doc-before': () => h(TopBar),
});

View File

@@ -95,7 +95,7 @@ To add a new shape:
- **Example**:
```typescript
import { Node, RenderOptions } from '../../types.d.ts';
import { Node, RenderOptions } from '../../types.ts';
export const myNewShape = async (
parent: SVGAElement,
@@ -111,7 +111,7 @@ To add a new shape:
### 2. Register the Shape
- **Register the shape**: Add your shape to the `shapes` object in the main shapes module. This allows your shape to be recognized and used within the system.
- **Register the shape**: Add your shape to the `shapes` object in the [main shapes module](../rendering-util/rendering-elements/shapes.ts). This allows your shape to be recognized and used within the system.
- **Example**:
@@ -120,9 +120,14 @@ To add a new shape:
const shapes = {
...,
'my-new-shape': myNewShape,
// Shortened alias (if any).
'm-nsh': myNewShape
{
semanticName: 'My Shape',
name: 'Shape Name',
shortName: '<short-name>',
description: '<Description for the shape>',
aliases: ['<alias-one>', '<al-on>', '<alias-two>', '<al-two>'],
handler: myNewShape,
},
};
```

View File

@@ -0,0 +1,49 @@
# Registering icon pack in mermaid
The icon packs available can be found at [icones.js.org](https://icones.js.org/).
We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram.
Using JSON file directly from CDN:
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos@1/icons.json').then((res) => res.json()),
},
]);
```
Using packages and a bundler:
```bash
npm install @iconify-json/logos@1
```
With lazy loading
```js
import mermaid from 'mermaid';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () => import('@iconify-json/logos').then((module) => module.icons),
},
]);
```
Without lazy loading
```js
import mermaid from 'mermaid';
import { icons } from '@iconify-json/logos';
mermaid.registerIconPacks([
{
name: icons.prefix, // To use the prefix defined in the icon pack
icons,
},
]);
```

View File

@@ -195,15 +195,22 @@ Communication tools and platforms
- [Vim](https://www.vim.org)
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
- [Official Vim Syntax and ft plugin](https://github.com/craigmac/vim-mermaid)
- [Zed](https://zed.dev)
- [zed-mermaid](https://github.com/gabeidx/zed-mermaid)
### Document Generation
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Codedoc](https://codedoc.cc/)
- [codedoc-mermaid-plugin](https://www.npmjs.com/package/codedoc-mermaid-plugin)
- [Docsy Hugo Theme](https://www.docsy.dev/docs/adding-content/lookandfeel/#diagrams-with-mermaid) ✅
- [Docusaurus](https://docusaurus.io/docs/markdown-features/diagrams) ✅
- [Gatsby](https://www.gatsbyjs.com/)
- [gatsby-remark-mermaid](https://github.com/remcohaszing/gatsby-remark-mermaid)
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [JSDoc](https://jsdoc.app/)
- [jsdoc-mermaid](https://github.com/Jellyvision/jsdoc-mermaid)
- [Madness](https://madness.dannyb.co/)
@@ -212,7 +219,7 @@ Communication tools and platforms
- [MkDocs](https://www.mkdocs.org)
- [mkdocs-mermaid2-plugin](https://github.com/fralau/mkdocs-mermaid2-plugin)
- [mkdocs-material](https://github.com/squidfunk/mkdocs-material), check the [docs](https://squidfunk.github.io/mkdocs-material/reference/diagrams/)
- [Quarto](https://quarto.org/)
- [Quarto](https://quarto.org/)
- [rehype](https://github.com/rehypejs/rehype)
- [rehype-mermaid](https://github.com/remcohaszing/rehype-mermaid)
- [remark](https://remark.js.org/)
@@ -241,17 +248,12 @@ Communication tools and platforms
### Other
- [Astro](https://astro.build/)
- [Adding diagrams to your Astro site with MermaidJS and Playwright](https://agramont.net/blog/diagraming-with-mermaidjs-astro/)
- [Bisheng](https://www.npmjs.com/package/bisheng)
- [bisheng-plugin-mermaid](https://github.com/yct21/bisheng-plugin-mermaid)
- [Blazorade Mermaid: Render Mermaid diagrams in Blazor applications](https://github.com/Blazorade/Blazorade-Mermaid/wiki)
- [Codemia: A tool to practice system design problems](https://codemia.io) ✅
- [ExDoc](https://github.com/elixir-lang/ex_doc)
- [Rendering Mermaid graphs](https://github.com/elixir-lang/ex_doc#rendering-mermaid-graphs)
- [Jekyll](https://jekyllrb.com/)
- [jekyll-mermaid](https://rubygems.org/gems/jekyll-mermaid)
- [jekyll-mermaid-diagrams](https://github.com/fuzhibo/jekyll-mermaid-diagrams)
- [MarkChart: Preview Mermaid diagrams on macOS](https://markchart.app/)
- [mermaid-isomorphic](https://github.com/remcohaszing/mermaid-isomorphic)
- [mermaid-server: Generate diagrams using a HTTP request](https://github.com/TomWright/mermaid-server)

View File

@@ -6,7 +6,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
<br />
<a href="https://www.producthunt.com/posts/mermaid-whiteboard?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-mermaid&#0045;whiteboard" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=486720&theme=light" alt="Mermaid&#0032;Whiteboard - Drag&#0032;&#0038;&#0032;Drop&#0032;your&#0032;Nodes&#0032;with&#0032;Mermaid&#0039;s&#0032;new&#0032;Whiteboard&#0033; | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<a href="https://www.producthunt.com/products/mermaid-chart?utm_source=badge-follow&utm_medium=badge&utm_souce=badge-mermaid&#0045;chart" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/follow.svg?product_id=552855&theme=light" alt="Mermaid&#0032;Chart - A&#0032;smarter&#0032;way&#0032;to&#0032;create&#0032;diagrams | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
## About

View File

@@ -76,8 +76,8 @@ Mermaid offers a variety of styles or “looks” for your diagrams, allowing yo
**Available Looks:**
Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams.
Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. Its great for consistency across projects or when you want to keep the familiar aesthetic.
- Hand-Drawn Look: For a more personal, creative touch, the hand-drawn look brings a sketch-like quality to your diagrams. This style is perfect for informal settings or when you want to add a bit of personality to your diagrams.
- Classic Look: If you prefer the traditional Mermaid style, the classic look maintains the original appearance that many users are familiar with. Its great for consistency across projects or when you want to keep the familiar aesthetic.
**How to Select a Look:**
@@ -101,8 +101,8 @@ In addition to customizing the look of your diagrams, Mermaid Chart now allows y
#### Supported Layout Algorithms:
Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams.
ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support.
- Dagre (default): This is the classic layout algorithm that has been used in Mermaid for a long time. It provides a good balance of simplicity and visual clarity, making it ideal for most diagrams.
- ELK: For those who need more sophisticated layout capabilities, especially when working with large or intricate diagrams, the ELK (Eclipse Layout Kernel) layout offers advanced options. It provides a more optimized arrangement, potentially reducing overlapping and improving readability. This is not included out the box but needs to be added when integrating mermaid for sites/applications that want to have elk support.
#### How to Select a Layout Algorithm:

View File

@@ -1,168 +1,180 @@
# Blog
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/new-mermaid-flowchart-shapes/)
## [Mermaid 11.4 is out: New Features and Kanban Diagramming](https://www.mermaidchart.com/blog/posts/mermaid-11-4-is-out-new-features-and-kanban-diagramming)
Mermaid 11.4 brings enhanced functionality with the introduction of Kanban diagrams, allowing users to create visual workflows with status columns and task details.
October 31, 2024 · 2 mins
## [How To Build an ER Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-build-an-er-diagram-with-mermaid-chart)
An entity relationship (ER) diagram acts like a blueprint for your database. This makes ER diagrams effective tools for anyone dealing with complex databases, data modeling, and AI model training.
October 24, 2024 · 4 mins
## [Expanding the Horizons of Mermaid Flowcharts: Introducing 30 New Shapes!](https://www.mermaidchart.com/blog/posts/expanding-the-horizons-of-mermaid-flowcharts-introducing-30-new-shapes)
24 September 2024 · 5 mins
Discover 30 new shapes in Mermaid flowcharts, offering enhanced clarity, customization, and versatility for more dynamic and expressive visualizations.
## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/mermaid-supports-architecture-diagrams/)
## [Introducing Architecture Diagrams in Mermaid](https://www.mermaidchart.com/blog/posts/introducing-architecture-diagrams-in-mermaid)
2 September 2024 · 2 mins
Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love.
## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11/)
## [Mermaid v11 is out!](https://www.mermaidchart.com/blog/posts/mermaid-v11-is-out)
23 August 2024 · 2 mins
Mermaid v11 introduces advanced layout options, new diagram types, and enhanced customization features, thanks to the incredible contributions from our community.
## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams/)
## [Mermaid Innovation - Introducing New Looks for Mermaid Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-innovation-introducing-new-looks-for-mermaid-diagrams)
6 August 2024 ·3 mins
Discover the fresh new and unique Neo and Hand-Drawn looks for Mermaid Diagrams, while still offering the classic look you love.
## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira-a-how-to-user-guide/)
## [The Mermaid Chart Plugin for Jira: A How-To User Guide](https://www.mermaidchart.com/blog/posts/the-mermaid-chart-plugin-for-jira)
31 July 2024 · 5 mins
The Mermaid Chart plugin for Jira has arrived!
## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation/)
## [Mermaid AI Is Here to Change the Game For Diagram Creation](https://www.mermaidchart.com/blog/posts/mermaid-ai-is-here-to-change-the-game-for-diagram-creation)
22 July 2024 · 5 mins
The Mermaid AI chat interface
## [How to Make a Sequence Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-sequence-diagram-in-mermaid-chart-step-by-step-guide/)
## [How to Make a Sequence Diagram with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-sequence-diagram-with-mermaid-chart)
8 July 2024 · 6 mins
Sequence diagrams are important for communicating complex systems in a clear and concise manner.
## [How to Use the New “Comments” Feature in Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-use-the-new-comments-feature-in-mermaid-chart/)
## [How to Use the New “Comments” Feature in Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-use-the-new-comments-feature-in-mermaid-chart)
2 July 2024 · 3 mins
How to Use the New Comments Feature in Mermaid Chart
## [How to Use the official Mermaid Chart for Confluence app](https://www.mermaidchart.com/blog/posts/how-to-use-the-official-mermaid-chart-for-confluence-app/)
## [How to Use the official Mermaid Chart for Confluence app](https://www.mermaidchart.com/blog/posts/how-to-use-the-official-mermaid-chart-for-confluence-app)
21 May 2024 · 4 mins
It doesnt matter if youre a data enthusiast, software engineer, or visual storyteller; our Confluence app can allow you to embed Mermaid Chart diagrams — and dynamically edit them — within your Confluence pages.
## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software/)
## [How to Choose the Right Documentation Software](https://www.mermaidchart.com/blog/posts/how-to-choose-the-right-documentation-software)
7 May 2024 · 5 mins
How to Choose the Right Documentation Software. Reliable and efficient documentation software is crucial in the fast-paced world of software development.
## [AI in software diagramming: What trends will define the future?](https://www.mermaidchart.com/blog/posts/ai-in-software-diagramming/)
## [AI in software diagramming: What trends will define the future?](https://www.mermaidchart.com/blog/posts/ai-in-software-diagramming)
24 April 2024 · 5 mins
Artificial intelligence (AI) tools are changing the way developers work.
## [Mermaid Chart Unveils Visual Editor for Sequence Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams/)
## [Mermaid Chart Unveils Visual Editor for Sequence Diagrams](https://www.mermaidchart.com/blog/posts/mermaid-chart-unveils-visual-editor-for-sequence-diagrams)
8 April 2024 · 5 mins
Sequence diagrams are excellent tools for communication and documentation.
## [Modeling system states: It starts with a Turing machine](https://www.mermaidchart.com/blog/posts/modeling-system-states/)
## [Modeling system states: It starts with a Turing machine](https://www.mermaidchart.com/blog/posts/modeling-system-states-it-starts-with-a-turing-machine)
27 March 2024 · 12 mins
In computer science, there are a few fundamental papers that, without exaggeration, changed everything.
## [Mermaid Chart Raises $7.5M to Reinvent Visual Collaboration for Enterprises](https://www.mermaidchart.com/blog/posts/mermaid-chart-raises-7.5m-to-reinvent-visual-collaoration-for-enterprises/)
## [Mermaid Chart Raises $7.5M to Reinvent Visual Collaboration for Enterprises](https://www.mermaidchart.com/blog/posts/mermaid-chart-raises-7-5m-to-reinvent-visual-collaboration-for-enterprises)
20 March 2024 · 4 mins
Mermaid Chart, the company offering text-based diagramming and workflow management tools, today announced it has raised $7.5 million in Seed funding.
## [Mermaid Chart GPT Is Now Available In the GPT Store!](https://www.mermaidchart.com/blog/posts/mermaid-chart-gpt-is-now-available-in-the-gpt-store/)
## [Mermaid Chart GPT Is Now Available In the GPT Store!](https://www.mermaidchart.com/blog/posts/mermaid-chart-gpt-is-now-available-in-the-gpt-store)
7 March 2024 · 3 mins
Mermaid Chart GPT is Now Available In the GPT Store!
## [How to Make a Flowchart with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-flowcharts-with-mermaid-chart/)
## [How to Make a Flowchart with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-flowchart-with-mermaid-chart)
30 January 2024 · 6 mins
Learn how to make a flowchart with Mermaid Chart, the leading text-to-diagram platform for both developers and non-developers.
## [How one data scientist uses Mermaid Chart to quickly and easily build flowcharts](https://www.mermaidchart.com/blog/posts/customer-spotlight-ari-tal/)
## [How one data scientist uses Mermaid Chart to quickly and easily build flowcharts](https://www.mermaidchart.com/blog/posts/how-one-data-scientist-uses-mermaid-chart-to-quickly-and-easily-build-flowcharts)
23 January 2024 · 4 mins
Read about how Ari Tal, a data scientist and founder of Leveling Up with XAI, utilizes Mermaid Chart for its easy-to-use flowchart creation capabilities to enhance his work in explainable AI (XAI).
## [Introducing Mermaid Charts JetBrains IDE Extension](https://www.mermaidchart.com/blog/posts/introducing-mermaid-charts-jetbrains-ide-extension/)
## [Introducing Mermaid Charts JetBrains IDE Extension](https://www.mermaidchart.com/blog/posts/introducing-mermaid-charts-jetbrains-ide-extension)
20 December 2023 · 5 mins
Diagrams are essential for documenting your code.
## [Mermaid Chart Releases New Visual Editor For Flowcharts](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts/)
## [Mermaid Chart Releases New Visual Editor For Flowcharts](https://www.mermaidchart.com/blog/posts/mermaid-chart-releases-new-visual-editor-for-flowcharts)
14 December 2023 · 5 mins
Mermaid Chart introduces a new Visual Editor for flowcharts, enabling users of all skill levels to create diagrams easily and efficiently, with both GUI and code-based editing options.
## [7 best practices (+ examples) for good developer documentation](https://www.mermaidchart.com/blog/posts/7-best-practices-for-good-documentation/)
## [7 best practices (+ examples) for good developer documentation](https://www.mermaidchart.com/blog/posts/7-best-practices-examples-for-good-developer-documentation)
4 December 2023 · 11 min
Essential strategies for crafting grate developer documentation, with practical examples and insights from leading tech companies.
## [5 Reasons You Should Be Using Mermaid Chart As Your Diagram Generator](https://www.mermaidchart.com/blog/posts/5-reasons-you-should-be-using-mermaid-chart-as-your-diagram-generator/)
## [5 Reasons You Should Be Using Mermaid Chart As Your Diagram Generator](https://www.mermaidchart.com/blog/posts/5-reasons-you-should-be-using-mermaid-chart-as-your-diagram-generator)
14 November 2023 · 5 mins
Mermaid Chart, a user-friendly, code-based diagram generator with AI integrations, templates, collaborative tools, and plugins for developers, streamlines the process of creating and sharing diagrams, enhancing both creativity and collaboration.
## [How to Use Mermaid Chart as an AI Diagram Generator](https://www.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator/)
## [How to Use Mermaid Chart as an AI Diagram Generator](https://www.mermaidchart.com/blog/posts/how-to-use-mermaid-chart-as-an-ai-diagram-generator)
1 November 2023 · 5 mins
Would an AI diagram generator make your life easier?
## [Diagrams, Made Even Easier: Introducing “Code Snippets” in the Mermaid Chart Editor](https://www.mermaidchart.com/blog/posts/easier-diagram-editing-with-code-snippets/)
## [Diagrams, Made Even Easier: Introducing “Code Snippets” in the Mermaid Chart Editor](https://www.mermaidchart.com/blog/posts/diagrams-made-even-easier-introducing-code-snippets-in-the-mermaid-chart-editor)
12 October 2023 · 4 mins
Mermaid Chart introduces Code Snippets in its editor, streamlining the diagramming process for developers and professionals.
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart/)
## [How to Make a Git Graph with Mermaid Chart](https://www.mermaidchart.com/blog/posts/how-to-make-a-git-graph-with-mermaid-chart)
22 September 2023 · 7 mins
A git graph is one of the more useful forms of diagrams for developers and DevOps professionals.
## [Present flow data using Sankey diagrams in Mermaid, thanks to Nikolay Rozhkov](https://www.mermaidchart.com/blog/posts/present-flow-data-using-sankey-diagrams/)
## [Present flow data using Sankey diagrams in Mermaid, thanks to Nikolay Rozhkov](https://www.mermaidchart.com/blog/posts/present-flow-data-using-sankey-diagrams-in-mermaid-thanks-to-nikolay-rozhkov)
8 September 2023 · 4 mins
Sankey diagrams are a powerful tool for visualizing flow data.
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too/)
## [Special cases broke Microsoft Zune and can ruin your code base too](https://www.mermaidchart.com/blog/posts/special-cases-broke-microsoft-zune-and-can-ruin-your-code-base-too)
23 August 2023 · 15 mins
Read about the pitfalls of special cases in programming, illustrating how they can lead to complexity, diminish readability, and create maintenance challenges.
## [New AI chatbot now available on Mermaid Chart to simplify text-based diagram creation](https://www.mermaidchart.com/blog/posts/ai-chatbot-now-available-on-mermaid-chart-to-simplify-text-based-diagram-creation/)
## [New AI chatbot now available on Mermaid Chart to simplify text-based diagram creation](https://www.mermaidchart.com/blog/posts/new-ai-chatbot-now-available-on-mermaid-chart-to-simplify-text-based-diagram-creation)
14 August 2023 · 4 mins
Introducing Mermaid Charts new AI chatbot, a diagramming assistant that simplifies text-based diagram creation for everyone, from developers to educators, offering features to start, edit, and fix diagrams, and embodying our vision to make diagramming accessible, user-friendly, and fun.
## [Believe It or Not, You Still Need an Online UML Diagram Tool](https://www.mermaidchart.com/blog/posts/uml-diagram-tool/)
## [Believe It or Not, You Still Need an Online UML Diagram Tool](https://www.mermaidchart.com/blog/posts/believe-it-or-not-you-still-need-an-online-uml-diagram-tool)
14 August 2023 · 8 mins
@@ -174,25 +186,25 @@ A UML diagram tool helps developers and other professionals quickly create and s
Introducing the concept of mind mapping as a tool for organizing complex information, and highlights Mermaid as a user-friendly software that simplifies the creation and editing of mind maps for applications in IT solution design, business decision-making, and knowledge organization.
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin)
## [Mermaid Chart Announces Visual Studio Code Plugin to Simplify Development Workflows](https://www.mermaidchart.com/blog/posts/mermaid-chart-announces-visual-studio-code-plugin-to-simplify-development-workflows)
17 July 2023 · 3 mins
New Integration Enhances Workflows By Enabling Developers To Reference And Edit Diagrams Within Visual Studio Code.
## [Mermaid Charts ChatGPT Plugin Combines Generative AI and Smart Diagramming For Users](https://www.mermaidchart.com/blog/posts/mermaid-chart-chatgpt-plugin-combines-generative-ai-and-smart-diagramming)
## [Mermaid Charts ChatGPT Plugin Combines Generative AI and Smart Diagramming For Users](https://www.mermaidchart.com/blog/posts/mermaid-charts-chatgpt-plugin-combines-generative-ai-and-smart-diagramming-for-users)
29 June 2023 · 4 mins
Mermaid Charts new ChatGPT plugin integrates AI-powered text prompts with Mermaids intuitive diagramming editor, enabling users to generate, edit, and share complex diagrams with ease and efficiency.
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-good-thing-uml-brought-to-software-development/)
## [Sequence diagrams, the only good thing UML brought to software development](https://www.mermaidchart.com/blog/posts/sequence-diagrams-the-only-good-thing-uml-brought-to-software-development)
15 June 2023 · 12 mins
Sequence diagrams really shine when youre documenting different parts of a system and the various ways these parts interact with each other.
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do/)
## [subhash-halder contributed quadrant charts so you can show your manager what to select - just like the strategy consultants at BCG do](https://www.mermaidchart.com/blog/posts/subhash-halder-contributed-quadrant-charts-so-you-can-show-your-manager-what-to-select-just-like-the-strategy-consultants-at-bcg-do)
8 June 2023 · 7 mins
@@ -204,31 +216,31 @@ A quadrant chart is a useful diagram that helps users visualize data and identif
Documentation tends to be bad because companies and projects dont fully realize the costs of bad documentation.
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here/)
## [Automatic text wrapping in flowcharts is here!](https://www.mermaidchart.com/blog/posts/automatic-text-wrapping-in-flowcharts-is-here)
3 April 2023 · 3 mins
Markdown Strings reduce the hassle # Starting from v10.
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode/)
## [Mermaid Chart officially launched with sharable diagram links and presentation mode](https://www.mermaidchart.com/blog/posts/mermaid-chart-officially-launched-with-sharable-diagram-links-and-presentation-mode)
27 March 2023 · 2 mins
Exciting news for all Mermaid OSS fans: Mermaid Chart has officially launched with Mermaid Chart!
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative-enough/)
## [If you're not excited about ChatGPT, then you're not being creative](https://www.mermaidchart.com/blog/posts/if-youre-not-excited-about-chatgpt-then-youre-not-being-creative)
8 March 2023 · 9 mins
The hype around AI in general and ChatGPT, in particular, is so intense that its very understandable to assume the hype train is driving straight toward the trough of disillusionment.
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections/)
## [Flow charts are O(n)2 complex, so don't go over 100 connections](https://www.mermaidchart.com/blog/posts/flow-charts-are-on2-complex-so-dont-go-over-100-connections)
1 March 2023 · 12 mins
Flowchart design is a game of balance: Read about the importance of dialling in the right level of detail and how to manage complexity in large flowcharts.
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write/)
## [Busting the myth that developers can't write](https://www.mermaidchart.com/blog/posts/busting-the-myth-that-developers-cant-write)
10 February 2023 · 10 mins

View File

@@ -59,15 +59,15 @@ service {service id}({icon name})[{title}] (in {parent id})?
Put together:
```
service database(db)[Database]
service database1(database)[My Database]
```
creates the service identified as `database`, using the icon `db`, with the label `Database`.
creates the service identified as `database1`, using the icon `database`, with the label `My Database`.
If the service belongs to a group, it can be placed inside it through the optional `in` keyword
```
service database(db)[Database] in private_api
service database1(database)[My Database] in private_api
```
### Edges
@@ -156,55 +156,7 @@ architecture-beta
## Icons
By default, architecture diagram supports the following icons: `cloud`, `database`, `disk`, `internet`, `server`.
Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps below.
The icon packs available can be found at [icones.js.org](https://icones.js.org/).
We use the name defined when registering the icon pack, to override the prefix field of the iconify pack. This allows the user to use shorter names for the icons. It also allows us to load a particular pack only when it is used in a diagram.
Using JSON file directly from CDN:
```js
import mermaid from 'CDN/mermaid.esm.mjs';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () =>
fetch('https://unpkg.com/@iconify-json/logos/icons.json').then((res) => res.json()),
},
]);
```
Using packages and a bundler:
```bash
npm install @iconify-json/logos
```
With lazy loading
```js
import mermaid from 'mermaid';
mermaid.registerIconPacks([
{
name: 'logos',
loader: () => import('@iconify-json/logos').then((module) => module.icons),
},
]);
```
Without lazy loading
```js
import mermaid from 'mermaid';
import { icons } from '@iconify-json/logos';
mermaid.registerIconPacks([
{
name: icons.prefix, // To use the prefix defined in the icon pack
icons,
},
]);
```
Users can use any of the 200,000+ icons available in iconify.design, or add their own custom icons, by following the steps [here](../config/icons.md).
After the icons are installed, they can be used in the architecture diagram by using the format "name:icon-name", where name is the value used when registering the icon pack.

View File

@@ -106,7 +106,7 @@ block-beta
a["A label"] b:2 c:2 d
```
In this example, the block labeled "A wide one" spans two columns, while blocks 'b', 'c', and 'd' are allocated their own columns. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size.
In this example, the block labeled "A labels" spans one column, while blocks 'b', 'c' span 2 columns, and 'd' is again allocated its own column. This flexibility in block sizing is crucial for accurately representing systems with components of varying significance or size.
### Creating Composite Blocks

View File

@@ -277,6 +277,34 @@ And `Link` can be one of:
| -- | Solid |
| .. | Dashed |
### Lollipop Interfaces
Classes can also be given a special relation type that defines a lollipop interface on the class. A lollipop interface is defined using the following syntax:
- `bar ()-- foo`
- `foo --() bar`
The interface (bar) with the lollipop connects to the class (foo).
Note: Each interface that is defined is unique and is meant to not be shared between classes / have multiple edges connecting to it.
```mermaid-example
classDiagram
bar ()-- foo
```
```mermaid-example
classDiagram
class Class01 {
int amount
draw()
}
Class01 --() bar
Class02 --() bar
foo ()-- Class01
```
## Define Namespace
A namespace groups classes.
@@ -518,10 +546,12 @@ Beginner's tip—a full example using interactive links in an HTML page:
## Styling
### Styling a node (v10.7.0+)
### Styling a node
It is possible to apply specific styles such as a thicker border or a different background color to an individual node using the `style` keyword.
Note that notes and namespaces cannot be styled individually but do support themes.
```mermaid-example
classDiagram
class Animal
@@ -533,11 +563,78 @@ classDiagram
#### Classes
More convenient than defining the style every time is to define a class of styles and attach this class to the nodes that
should have a different look. This is done by predefining classes in css styles that can be applied from the graph definition using the `cssClass` statement or the `:::` short hand.
should have a different look.
A class definition looks like the example below:
```
classDef className fill:#f9f,stroke:#333,stroke-width:4px;
```
Also, it is possible to define style to multiple classes in one statement:
```
classDef firstClassName,secondClassName font-size:12pt;
```
Attachment of a class to a node is done as per below:
```
cssClass "nodeId1" className;
```
It is also possible to attach a class to a list of nodes in one statement:
```
cssClass "nodeId1,nodeId2" className;
```
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
```mermaid-example
classDiagram
class Animal:::someclass
classDef someclass fill:#f96
```
Or:
```mermaid-example
classDiagram
class Animal:::someclass {
-int sizeInFeet
-canEat()
}
classDef someclass fill:#f96
```
### Default class
If a class is named default it will be applied to all nodes. Specific styles and classes should be defined afterwards to override the applied default styling.
```
classDef default fill:#f9f,stroke:#333,stroke-width:4px;
```
```mermaid-example
classDiagram
class Animal:::pink
class Mineral
classDef default fill:#f96,color:red
classDef pink color:#f9f
```
### CSS Classes
It is also possible to predefine classes in CSS styles that can be applied from the graph definition as in the example
below:
**Example style**
```html
<style>
.styleClass > rect {
.styleClass > * > g {
fill: #ff0000;
stroke: #ffff00;
stroke-width: 4px;
@@ -545,147 +642,29 @@ should have a different look. This is done by predefining classes in css styles
</style>
```
Then attaching that class to a specific node:
```
cssClass "nodeId1" styleClass;
```
It is also possible to attach a class to a list of nodes in one statement:
```
cssClass "nodeId1,nodeId2" styleClass;
```
A shorter form of adding a class is to attach the classname to the node using the `:::` operator:
**Example definition**
```mermaid-example
classDiagram
class Animal:::styleClass
```
Or:
```mermaid-example
classDiagram
class Animal:::styleClass {
-int sizeInFeet
-canEat()
}
```
?> cssClasses cannot be added using this shorthand method at the same time as a relation statement.
?> Due to limitations with existing markup for class diagrams, it is not currently possible to define css classes within the diagram itself. **_Coming soon!_**
### Default Styles
The main styling of the class diagram is done with a preset number of css classes. During rendering these classes are extracted from the file located at src/themes/class.scss. The classes used here are described below:
| Class | Description |
| ------------------ | ----------------------------------------------------------------- |
| g.classGroup text | Styles for general class text |
| classGroup .title | Styles for general class title |
| g.classGroup rect | Styles for class diagram rectangle |
| g.classGroup line | Styles for class diagram line |
| .classLabel .box | Styles for class label box |
| .classLabel .label | Styles for class label text |
| composition | Styles for composition arrow head and arrow line |
| aggregation | Styles for aggregation arrow head and arrow line(dashed or solid) |
| dependency | Styles for dependency arrow head and arrow line |
#### Sample stylesheet
```scss
body {
background: white;
}
g.classGroup text {
fill: $nodeBorder;
stroke: none;
font-family: 'trebuchet ms', verdana, arial;
font-family: var(--mermaid-font-family);
font-size: 10px;
.title {
font-weight: bolder;
}
}
g.classGroup rect {
fill: $nodeBkg;
stroke: $nodeBorder;
}
g.classGroup line {
stroke: $nodeBorder;
stroke-width: 1;
}
.classLabel .box {
stroke: none;
stroke-width: 0;
fill: $nodeBkg;
opacity: 0.5;
}
.classLabel .label {
fill: $nodeBorder;
font-size: 10px;
}
.relation {
stroke: $nodeBorder;
stroke-width: 1;
fill: none;
}
@mixin composition {
fill: $nodeBorder;
stroke: $nodeBorder;
stroke-width: 1;
}
#compositionStart {
@include composition;
}
#compositionEnd {
@include composition;
}
@mixin aggregation {
fill: $nodeBkg;
stroke: $nodeBorder;
stroke-width: 1;
}
#aggregationStart {
@include aggregation;
}
#aggregationEnd {
@include aggregation;
}
#dependencyStart {
@include composition;
}
#dependencyEnd {
@include composition;
}
#extensionStart {
@include composition;
}
#extensionEnd {
@include composition;
}
```
> cssClasses cannot be added using this shorthand method at the same time as a relation statement.
## Configuration
`Coming soon!`
### Members Box
It is possible to hide the empty members box of a class node.
This is done by changing the **hideEmptyMembersBox** value of the class diagram configuration. For more information on how to edit the Mermaid configuration see the [configuration page.](https://mermaid.js.org/config/configuration.html)
```mermaid-example
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
class Duck
```

View File

@@ -212,53 +212,7 @@ This syntax creates a node A as a rectangle. It renders in the same way as `A["A
Below is a comprehensive list of the newly introduced shapes and their corresponding semantic meanings, short names, and aliases:
| **Semantic Name** | **Shape Name** | **Short Name** | **Description** | **Alias Supported** |
| ------------------------------------- | ---------------------- | -------------- | ------------------------------ | -------------------------------------------------------------- |
| **Process** | Rectangle | `rect` | Standard process shape | `proc`, `process`, `rectangle` |
| **Event** | Rounded Rectangle | `rounded` | Represents an event | `event` |
| **Terminal Point** | Stadium | `stadium` | Terminal point | `terminal`, `pill` |
| **Subprocess** | Framed Rectangle | `fr-rect` | Subprocess | `subprocess`,`subproc`, `framed-rectangle`, `subroutine` |
| **Database** | Cylinder | `cyl` | Database storage | `db`, `database`, `cylinder` |
| **Start** | Circle | `circle` | Starting point | `circ` |
| **Odd** | Odd | `odd` | Odd shape | |
| **Decision** | Diamond | `diam` | Decision-making step | `decision`, `diamond` |
| **Prepare Conditional** | Hexagon | `hex` | Preparation or condition step | `hexagon`, `prepare` |
| **Data Input/Output** | Lean Right | `lean-r` | Represents input or output | `lean-right`, `in-out` |
| **Data Input/Output** | Lean Left | `lean-l` | Represents output or input | `lean-left`, `out-in` |
| **Priority Action** | Trapezoid Base Bottom | `trap-b` | Priority action | `priority`, `trapezoid-bottom` |
| **Manual Operation** | Trapezoid Base Top | `trap-t` | Represents a manual task | `manual`, `trapezoid-top` |
| **Stop** | Double Circle | `dbl-circ` | Represents a stop point | `double-circle` |
| **Text Block** | Text Block | `text` | Text block | - |
| **Card** | Notched Rectangle | `notch-rect` | Represents a card | `card`, `notched-rectangle` |
| **Lined/Shaded Process** | Lined Rectangle | `lin-rect` | Lined process shape | `lined-rectangle`,`lined-process`, `lin-proc`,`shaded-process` |
| **Start** | Small Circle | `sm-circ` | Small starting point | `start`, `small-circle` |
| **Stop** | Framed Circle | `fr-circ` | Stop point | `stop`, `framed-circle` |
| **Fork/Join** | Filled Rectangle | `fork` | Fork or join in process flow | `join` |
| **Collate** | Hourglass | `hourglass` | Represents a collate operation | `hourglass` |
| **Comment** | Curly Brace | `brace` | Adds a comment | `comment`, `brace-l` |
| **Comment Right** | Curly Brace | `brace-r` | Adds a comment | - |
| **Comment with braces on both sides** | Curly Braces | `braces` | Adds a comment | - |
| **Com Link** | Lightning Bolt | `bolt` | Communication link | `com-link`, `lightning-bolt` |
| **Document** | Document | `doc` | Represents a document | `doc`, `document` |
| **Delay** | Half-Rounded Rectangle | `delay` | Represents a delay | `half-rounded-rectangle` |
| **Direct Access Storage** | Horizontal Cylinder | `h-cyl` | Direct access storage | `das`, `horizontal-cylinder` |
| **Disk Storage** | Lined Cylinder | `lin-cyl` | Disk storage | `disk`, `lined-cylinder` |
| **Display** | Curved Trapezoid | `curv-trap` | Represents a display | `curved-trapezoid`, `display` |
| **Divided Process** | Divided Rectangle | `div-rect` | Divided process shape | `div-proc`, `divided-rectangle`, `divided-process` |
| **Extract** | Triangle | `tri` | Extraction process | `extract`, `triangle` |
| **Internal Storage** | Window Pane | `win-pane` | Internal storage | `internal-storage`, `window-pane` |
| **Junction** | Filled Circle | `f-circ` | Junction point | `junction`, `filled-circle` |
| **Lined Document** | Lined Document | `lin-doc` | Lined document | `lined-document` |
| **Loop Limit** | Trapezoidal Pentagon | `notch-pent` | Loop limit step | `loop-limit`, `notched-pentagon` |
| **Manual File** | Flipped Triangle | `flip-tri` | Manual file operation | `manual-file`, `flipped-triangle` |
| **Manual Input** | Sloped Rectangle | `sl-rect` | Manual input step | `manual-input`, `sloped-rectangle` |
| **Multi-Document** | Stacked Document | `docs` | Multiple documents | `documents`, `st-doc`, `stacked-document` |
| **Multi-Process** | Stacked Rectangle | `st-rect` | Multiple processes | `procs`, `processes`, `stacked-rectangle` |
| **Paper Tape** | Flag | `flag` | Paper tape | `paper-tape` |
| **Stored Data** | Bow Tie Rectangle | `bow-rect` | Stored data | `stored-data`, `bow-tie-rectangle` |
| **Summary** | Crossed Circle | `cross-circ` | Summary | `summary`, `crossed-circle` |
| **Tagged Document** | Tagged Document | `tag-doc` | Tagged document | `tag-doc`, `tagged-document` |
| **Tagged Process** | Tagged Rectangle | `tag-rect` | Tagged process | `tagged-rectangle`,`tag-proc`, `tagged-process` |
<!--@include: virtual:shapesTable -->
### Example Flowchart with New Shapes
@@ -588,6 +542,56 @@ flowchart TD
A@{ shape: tag-rect, label: "Tagged process" }
```
## Special shapes in Mermaid Flowcharts (v11.3.0+)
Mermaid also introduces 2 special shapes to enhance your flowcharts: **icon** and **image**. These shapes allow you to include icons and images directly within your flowcharts, providing more visual context and clarity.
### Icon Shape
You can use the `icon` shape to include an icon in your flowchart. To use icons, you need to register the icon pack first. Follow the instructions provided [here](../config/icons.md). The syntax for defining an icon shape is as follows:
```mermaid-example
flowchart TD
A@{ icon: "fa:user", form: "square", label: "User Icon", pos: "t", h: 60 }
```
### Parameters
- **icon**: The name of the icon from the registered icon pack.
- **form**: Specifies the background shape of the icon. If not defined there will be no background to icon. Options include:
- `square`
- `circle`
- `rounded`
- **label**: The text label associated with the icon. This can be any string. If not defined, no label will be displayed.
- **pos**: The position of the label. If not defined label will default to bottom of icon. Possible values are:
- `t`
- `b`
- **h**: The height of the icon. If not defined this will default to 48 which is minimum.
### Image Shape
You can use the `image` shape to include an image in your flowchart. The syntax for defining an image shape is as follows:
```mermaid-example
flowchart TD
A@{ img: "https://example.com/image.png", label: "Image Label", pos: "t", w: 60, h: 60, constraint: "off" }
```
### Parameters
- **img**: The URL of the image to be displayed.
- **label**: The text label associated with the image. This can be any string. If not defined, no label will be displayed.
- **pos**: The position of the label. If not defined, the label will default to the bottom of the image. Possible values are:
- `t`
- `b`
- **w**: The width of the image. If not defined, this will default to the natural width of the image.
- **h**: The height of the image. If not defined, this will default to the natural height of the image.
- **constraint**: Determines if the image should constrain the node size. This setting also ensures the image maintains its original aspect ratio, adjusting the height (`h`) accordingly to the width (`w`). If not defined, this will default to `off` Possible values are:
- `on`
- `off`
These new shapes provide additional flexibility and visual appeal to your flowcharts, making them more informative and engaging.
## Links between nodes
Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link.

View File

@@ -0,0 +1,113 @@
# Mermaid Kanban Diagram Documentation
Mermaids Kanban diagram allows you to create visual representations of tasks moving through different stages of a workflow. This guide explains how to use the Kanban diagram syntax, based on the provided example.
## Overview
A Kanban diagram in Mermaid starts with the kanban keyword, followed by the definition of columns (stages) and tasks within those columns.
```mermaid-example
kanban
column1[Column Title]
task1[Task Description]
```
## Defining Columns
Columns represent the different stages in your workflow, such as “Todo,” “In Progress,” “Done,” etc. Each column is defined using a unique identifier and a title enclosed in square brackets.
**Syntax:**
```
columnId[Column Title]
```
- columnId: A unique identifier for the column.
- [Column Title]: The title displayed on the column header.
Like this `id1[Todo]`
## Adding Tasks to Columns
Tasks are listed under their respective columns with an indentation. Each task also has a unique identifier and a description enclosed in square brackets.
**Syntax:**
```
taskId[Task Description]
```
• taskId: A unique identifier for the task.
• [Task Description]: The description of the task.
**Example:**
```
docs[Create Documentation]
```
## Adding Metadata to Tasks
You can include additional metadata for each task using the @{ ... } syntax. Metadata can contain key-value pairs like assigned, ticket, priority, etc. This will be rendered added to the rendering of the node.
## Supported Metadata Keys
• assigned: Specifies who is responsible for the task.
• ticket: Links the task to a ticket or issue number.
• priority: Indicates the urgency of the task. Allowed values: 'Very High', 'High', 'Low' and 'Very Low'
```mermaid-example
kanban
todo[Todo]
id3[Update Database Function]@{ ticket: MC-2037, assigned: 'knsv', priority: 'High' }
```
## Configuration Options
You can customize the Kanban diagram using a configuration block at the beginning of your markdown file. This is useful for setting global settings like a base URL for tickets. Currently there is one configuration option for kanban diagrams tacketBaseUrl. This can be set as in the the following example:
```yaml
---
config:
kanban:
ticketBaseUrl: 'https://yourproject.atlassian.net/browse/#TICKET#'
---
```
When the kanban item has an assigned ticket number the ticket number in the diagram will have a link to an external system where the ticket is defined. The `ticketBaseUrl` sets the base URL to the external system and #TICKET# is replaced with the ticket value from task metadata to create a valid link.
## Full Example
Below is the full Kanban diagram based on the provided example:
```mermaid-example
---
config:
kanban:
ticketBaseUrl: 'https://mermaidchart.atlassian.net/browse/#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 som 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: MC-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: MC-2036, priority: 'Very High'}
id3[Update DB function]@{ ticket: MC-2037, assigned: knsv, priority: 'High' }
id12[Can't reproduce]
id3[Weird flickering in Firefox]
```
In conclusion, creating a Kanban diagram in Mermaid is a straightforward process that effectively visualizes your workflow. Start by using the kanban keyword to initiate the diagram. Define your columns with unique identifiers and titles to represent different stages of your project. Under each column, list your tasks—also with unique identifiers—and provide detailed descriptions as needed. Remember that proper indentation is crucial; tasks must be indented under their parent columns to maintain the correct structure.
You can enhance your diagram by adding optional metadata to tasks using the @{ ... } syntax, which allows you to include additional context such as assignee, ticket numbers, and priority levels. For further customization, utilize the configuration block at the top of your file to set global options like ticketBaseUrl for linking tickets directly from your diagram.
By adhering to these guidelines—ensuring unique identifiers, proper indentation, and utilizing metadata and configuration options—you can create a comprehensive and customized Kanban board that effectively maps out your projects workflow using Mermaid.

View File

@@ -155,7 +155,7 @@ There are ten types of arrows currently supported:
| `<<->>` | Solid line with bidirectional arrowheads (v11.0.0+) |
| `<<-->>` | Dotted line with bidirectional arrowheads (v11.0.0+) |
| `-x` | Solid line with a cross at the end |
| `--x` | Dotted line with a cross at the end. |
| `--x` | Dotted line with a cross at the end |
| `-)` | Solid line with an open arrow at the end (async) |
| `--)` | Dotted line with a open arrow at the end (async) |

View File

@@ -779,7 +779,7 @@ graph TD;A--x|text including URL space|B;`)
// We have to have both the specific textDiagramType and the expected type name because the expected type may be slightly different than was is put in the diagram text (ex: in -v2 diagrams)
const diagramTypesAndExpectations = [
{ textDiagramType: 'C4Context', expectedType: 'c4' },
{ textDiagramType: 'classDiagram', expectedType: 'classDiagram' },
{ textDiagramType: 'classDiagram', expectedType: 'class' },
{ textDiagramType: 'classDiagram-v2', expectedType: 'classDiagram' },
{ textDiagramType: 'erDiagram', expectedType: 'er' },
{ textDiagramType: 'graph', expectedType: 'flowchart-v2' },

View File

@@ -204,7 +204,7 @@ export const createText = async (
width = 200,
addSvgBackground = false,
} = {},
config: MermaidConfig
config?: MermaidConfig
) => {
log.debug(
'XYZ createText',

View File

@@ -39,6 +39,7 @@ export function markdownToLines(markdown: string, config: MermaidConfig = {}): M
lines.push([]);
}
textLine.split(' ').forEach((word) => {
word = word.replace(/&#39;/g, `'`);
if (word) {
lines[currentLine].push({ content: word, type: parentType });
}
@@ -85,6 +86,8 @@ export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidCo
return '';
} else if (node.type === 'html') {
return `${node.text}`;
} else if (node.type === 'escape') {
return node.text;
}
return `Unsupported markdown: ${node.type}`;
}

View File

@@ -280,6 +280,117 @@ const roundedWithTitle = async (parent, node) => {
return { cluster: shapeSvg, labelBBox: bbox };
};
const kanbanSection = async (parent, node) => {
log.info('Creating subgraph rect for ', node.id, node);
const siteConfig = getConfig();
const { themeVariables, handDrawnSeed } = siteConfig;
const { clusterBkg, clusterBorder } = themeVariables;
const { labelStyles, nodeStyles, borderStyles, backgroundStyles } = styles2String(node);
// Add outer g element
const shapeSvg = parent
.insert('g')
.attr('class', 'cluster ' + node.cssClasses)
.attr('id', node.id)
.attr('data-look', node.look);
const useHtmlLabels = evaluate(siteConfig.flowchart.htmlLabels);
// Create the label and insert it after the rect
const labelEl = shapeSvg.insert('g').attr('class', 'cluster-label ');
const text = await createText(labelEl, node.label, {
style: node.labelStyle,
useHtmlLabels,
isNode: true,
width: node.width,
});
// Get the size of the label
let bbox = text.getBBox();
if (evaluate(siteConfig.flowchart.htmlLabels)) {
const div = text.children[0];
const dv = select(text);
bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width);
dv.attr('height', bbox.height);
}
const width = node.width <= bbox.width + node.padding ? bbox.width + node.padding : node.width;
if (node.width <= bbox.width + node.padding) {
node.diff = (width - node.width) / 2 - node.padding;
} else {
node.diff = -node.padding;
}
const height = node.height;
const x = node.x - width / 2;
const y = node.y - height / 2;
log.trace('Data ', node, JSON.stringify(node));
let rect;
if (node.look === 'handDrawn') {
// @ts-ignore TODO: Fix rough typings
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {
roughness: 0.7,
fill: clusterBkg,
// fill: 'red',
stroke: clusterBorder,
fillWeight: 4,
seed: handDrawnSeed,
});
const roughNode = rc.path(createRoundedRectPathD(x, y, width, height, node.rx), options);
rect = shapeSvg.insert(() => {
log.debug('Rough node insert CXC', roughNode);
return roughNode;
}, ':first-child');
// Should we affect the options instead of doing this?
rect.select('path:nth-child(2)').attr('style', borderStyles.join(';'));
rect.select('path').attr('style', backgroundStyles.join(';').replace('fill', 'stroke'));
} else {
// add the rect
rect = shapeSvg.insert('rect', ':first-child');
// center the rect around its coordinate
rect
.attr('style', nodeStyles)
.attr('rx', node.rx)
.attr('ry', node.ry)
.attr('x', x)
.attr('y', y)
.attr('width', width)
.attr('height', height);
}
const { subGraphTitleTopMargin } = getSubGraphTitleMargins(siteConfig);
labelEl.attr(
'transform',
// This puts the label on top of the box instead of inside it
`translate(${node.x - bbox.width / 2}, ${node.y - node.height / 2 + subGraphTitleTopMargin})`
);
if (labelStyles) {
const span = labelEl.select('span');
if (span) {
span.attr('style', labelStyles);
}
}
// Center the label
const rectBox = rect.node().getBBox();
node.offsetX = 0;
node.width = rectBox.width;
node.height = rectBox.height;
// Used by layout engine to position subgraph in parent
node.offsetY = bbox.height - node.padding / 2;
node.intersect = function (point) {
return intersectRect(node, point);
};
return { cluster: shapeSvg, labelBBox: bbox };
};
const divider = (parent, node) => {
const siteConfig = getConfig();
@@ -355,10 +466,18 @@ const shapes = {
roundedWithTitle,
noteGroup,
divider,
kanbanSection,
};
let clusterElems = new Map();
/**
* @typedef {keyof typeof shapes} ClusterShapeID
*/
/**
* @param {import('../types.js').ClusterNode} node - Shape defaults to 'rect'
*/
export const insertCluster = async (elem, node) => {
const shape = node.shape || 'rect';
const cluster = await shapes[shape](elem, node);

View File

@@ -463,15 +463,6 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod
let lineData = points.filter((p) => !Number.isNaN(p.y));
lineData = fixCorners(lineData);
let lastPoint = lineData[lineData.length - 1];
if (lineData.length > 1) {
lastPoint = lineData[lineData.length - 1];
const secondLastPoint = lineData[lineData.length - 2];
const diffX = (lastPoint.x - secondLastPoint.x) / 2;
const diffY = (lastPoint.y - secondLastPoint.y) / 2;
const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY };
lineData.splice(-1, 0, midPoint);
}
let curve = curveBasis;
if (edge.curve) {
curve = edge.curve;

View File

@@ -1,380 +0,0 @@
import { log } from '../../logger.js';
import { state } from './shapes/state.ts';
import { roundedRect } from './shapes/roundedRect.ts';
import { squareRect } from './shapes/squareRect.ts';
import { stateStart } from './shapes/stateStart.ts';
import { stateEnd } from './shapes/stateEnd.ts';
import { forkJoin } from './shapes/forkJoin.ts';
import { choice } from './shapes/choice.ts';
import { note } from './shapes/note.ts';
import { stadium } from './shapes/stadium.js';
import { rectWithTitle } from './shapes/rectWithTitle.js';
import { subroutine } from './shapes/subroutine.js';
import { cylinder } from './shapes/cylinder.js';
import { circle } from './shapes/circle.js';
import { doublecircle } from './shapes/doubleCircle.js';
import { rect_left_inv_arrow } from './shapes/rectLeftInvArrow.js';
import { question } from './shapes/question.js';
import { hexagon } from './shapes/hexagon.js';
import { text } from './shapes/text.js';
import { card } from './shapes/card.js';
import { shadedProcess } from './shapes/shadedProcess.js';
import { anchor } from './shapes/anchor.js';
import { lean_right } from './shapes/leanRight.js';
import { lean_left } from './shapes/leanLeft.js';
import { trapezoid } from './shapes/trapezoid.js';
import { inv_trapezoid } from './shapes/invertedTrapezoid.js';
import { labelRect } from './shapes/labelRect.js';
import { triangle } from './shapes/triangle.js';
import { halfRoundedRectangle } from './shapes/halfRoundedRectangle.js';
import { curvedTrapezoid } from './shapes/curvedTrapezoid.js';
import { slopedRect } from './shapes/slopedRect.js';
import { bowTieRect } from './shapes/bowTieRect.js';
import { dividedRectangle } from './shapes/dividedRect.js';
import { crossedCircle } from './shapes/crossedCircle.js';
import { waveRectangle } from './shapes/waveRectangle.js';
import { tiltedCylinder } from './shapes/tiltedCylinder.js';
import { trapezoidalPentagon } from './shapes/trapezoidalPentagon.js';
import { flippedTriangle } from './shapes/flippedTriangle.js';
import { hourglass } from './shapes/hourglass.js';
import { taggedRect } from './shapes/taggedRect.js';
import { multiRect } from './shapes/multiRect.js';
import { linedCylinder } from './shapes/linedCylinder.js';
import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js';
import { lightningBolt } from './shapes/lightningBolt.js';
import { filledCircle } from './shapes/filledCircle.js';
import { multiWaveEdgedRectangle } from './shapes/multiWaveEdgedRectangle.js';
import { windowPane } from './shapes/windowPane.js';
import { linedWaveEdgedRect } from './shapes/linedWaveEdgedRect.js';
import { taggedWaveEdgedRectangle } from './shapes/taggedWaveEdgedRectangle.js';
import { curlyBraceLeft } from './shapes/curlyBraceLeft.js';
import { curlyBraceRight } from './shapes/curlyBraceRight.js';
import { curlyBraces } from './shapes/curlyBraces.js';
import { iconSquare } from './shapes/iconSquare.js';
import { iconCircle } from './shapes/iconCircle.js';
import { icon } from './shapes/icon.js';
import { imageSquare } from './shapes/imageSquare.js';
import { iconRounded } from './shapes/iconRounded.js';
//Use these names as the left side to render shapes.
export const shapes = {
// States
state,
stateStart,
stateEnd,
forkJoin,
choice,
note,
// Rectangles
rectWithTitle,
roundedRect,
squareRect,
// Rectangle with alias: 'process', 'rect', 'proc', 'rectangle'
rectangle: squareRect,
rect: squareRect,
process: squareRect,
proc: squareRect,
// Rounded Rectangle with alias: 'event', 'rounded'
rounded: roundedRect,
event: roundedRect,
// Stadium with alias: 'terminal','pill', 'stadium'
stadium,
pill: stadium,
terminal: stadium,
// Subprocess with alias: 'fr-rect', 'subproc', 'subprocess', 'framed-rectangle', 'subroutine'
subroutine,
'framed-rectangle': subroutine,
'fr-rect': subroutine,
subprocess: subroutine,
subproc: subroutine,
// Cylinder with alias: 'db', 'database', 'cylinder', 'cyl'
cylinder,
db: cylinder,
cyl: cylinder,
database: cylinder,
// Diamond with alias: 'diam', 'decision', 'diamond'
question,
diam: question,
diamond: question,
decision: question,
// Hexagon with alias: 'hex', 'hexagon', 'prepare'
hexagon,
hex: hexagon,
prepare: hexagon,
// Lean Right with alias: 'lean-r', 'lean-right', 'in-out'
lean_right, // used in old syntax for flowchart
'lean-r': lean_right,
'lean-right': lean_right,
'in-out': lean_right,
// Lean Left with alias: 'lean-l', 'lean-left', 'out-in'
lean_left, // used in old syntax for flowchart
'lean-l': lean_left,
'lean-left': lean_left,
'out-in': lean_left,
// Trapezoid with alias: 'trap-b', 'trapezoid-bottom', 'priority'
trapezoid, // used in old syntax for flowchart
'trap-b': trapezoid,
'trapezoid-bottom': trapezoid,
priority: trapezoid,
// Inverted Trapezoid with alias: 'inv-trapezoid', 'trapezoid-top', 'trap-t', 'manual'
inv_trapezoid, // used in old syntax for flowchart
'inv-trapezoid': inv_trapezoid,
'trapezoid-top': inv_trapezoid,
'trap-t': inv_trapezoid,
manual: inv_trapezoid,
// Double Circle with alias: 'dbl-circ', 'double-circle'
doublecircle, // used in old syntax for flowchart
'dbl-circ': doublecircle,
'double-circle': doublecircle,
// circle with alias: 'circ', 'circle'
circle,
circ: circle,
// Rect Left Inv Arrow with alias: 'odd', 'rect-left-inv-arrow'
rect_left_inv_arrow,
odd: rect_left_inv_arrow,
// Notched Rectangle with alias: 'notched-rectangle', 'notch-rect', 'card'
card,
'notched-rectangle': card,
'notch-rect': card,
// Lined rectangle with alias: 'lin-rect', 'lined-rectangle', 'lin-proc', lined-process', 'shaded-process'
'lined-rectangle': shadedProcess,
'lin-rect': shadedProcess,
'lin-proc': shadedProcess,
'lined-process': shadedProcess,
'shaded-process': shadedProcess,
// Small circle with alias: 'sm-circ', 'small-circle', 'start'
'small-circle': stateStart,
'sm-circ': stateStart,
start: stateStart,
// framed circle with alias: 'stop', 'framed-circle', 'fr-circ'
stop: stateEnd,
'framed-circle': stateEnd,
'fr-circ': stateEnd,
// fork with alias: 'join', 'fork'
join: forkJoin,
fork: forkJoin,
// comment with alias: 'comment', 'brace-l'
comment: curlyBraceLeft,
'brace-l': curlyBraceLeft,
// lightening bolt with alias: 'bolt', 'com-link', 'lightning-bolt'
bolt: lightningBolt,
'com-link': lightningBolt,
'lightning-bolt': lightningBolt,
// document with alias: 'doc', 'document'
doc: waveEdgedRectangle,
document: waveEdgedRectangle,
// delay with alias: 'delay', 'half-rounded-rectangle'
delay: halfRoundedRectangle,
'half-rounded-rectangle': halfRoundedRectangle,
// horizontal cylinder with alias: 'h-cyl', 'das', 'horizontal-cylinder'
'horizontal-cylinder': tiltedCylinder,
'h-cyl': tiltedCylinder,
das: tiltedCylinder,
// lined cylinder with alias: 'lin-cyl', 'lined-cylinder', 'disk'
'lined-cylinder': linedCylinder,
'lin-cyl': linedCylinder,
disk: linedCylinder,
// curved trapezoid with alias: 'curv-trap', 'curved-trapezoid', 'display'
'curved-trapezoid': curvedTrapezoid,
'curv-trap': curvedTrapezoid,
display: curvedTrapezoid,
// divided rectangle with alias: 'div-rect', 'divided-rectangle', 'div-proc', 'divided-process'
'divided-rectangle': dividedRectangle,
'div-rect': dividedRectangle,
'div-proc': dividedRectangle,
'divided-process': dividedRectangle,
// triangle with alias: 'tri', 'triangle', 'extract'
triangle,
tri: triangle,
extract: triangle,
// window pane with alias: 'window-pane', 'win-pane', 'internal-storage'
'window-pane': windowPane,
'win-pane': windowPane,
'internal-storage': windowPane,
// filled circle with alias: 'f-circ', 'filled-circle', 'junction'
'f-circ': filledCircle,
junction: filledCircle,
'filled-circle': filledCircle,
// lined document with alias: 'lin-doc', 'lined-document'
'lin-doc': linedWaveEdgedRect,
'lined-document': linedWaveEdgedRect,
// notched pentagon with alias: 'notch-pent', 'notched-pentagon', 'loop-limit'
'notched-pentagon': trapezoidalPentagon,
'notch-pent': trapezoidalPentagon,
'loop-limit': trapezoidalPentagon,
// flipped triangle with alias: 'flip-tri', 'flipped-triangle', 'manual-file'
'flipped-triangle': flippedTriangle,
'flip-tri': flippedTriangle,
'manual-file': flippedTriangle,
// sloped rectangle with alias: 'sl-rect', 'sloped-rectangle', 'manual-input'
'sloped-rectangle': slopedRect,
'sl-rect': slopedRect,
'manual-input': slopedRect,
// documents with alias: 'docs', 'documents', 'st-doc', 'stacked-document'
docs: multiWaveEdgedRectangle,
documents: multiWaveEdgedRectangle,
'st-doc': multiWaveEdgedRectangle,
'stacked-document': multiWaveEdgedRectangle,
// 'processes' with alias: 'procs', 'processes', 'st-rect', 'stacked-rectangle'
processes: multiRect,
procs: multiRect,
'stacked-rectangle': multiRect,
'st-rect': multiRect,
// flag with alias: 'flag', 'paper-tape'
flag: waveRectangle,
'paper-tape': waveRectangle,
// bow tie rectangle with alias: 'bow-rect', 'bow-tie-rectangle', 'stored-data'
'bow-tie-rectangle': bowTieRect,
'bow-rect': bowTieRect,
'stored-data': bowTieRect,
// crossed circle with alias: 'cross-circ', 'crossed-circle', 'summary'
'crossed-circle': crossedCircle,
'cross-circ': crossedCircle,
summary: crossedCircle,
// tagged document with alias: 'tag-doc', 'tagged-document'
'tag-doc': taggedWaveEdgedRectangle,
'tagged-document': taggedWaveEdgedRectangle,
// tagged rectangle with alias: 'tag-rect', 'tagged-rectangle', 'tag-proc', 'tagged-process'
'tag-rect': taggedRect,
'tagged-rectangle': taggedRect,
'tag-proc': taggedRect,
'tagged-process': taggedRect,
// hourglass with alias: 'hourglass', 'collate'
hourglass,
collate: hourglass,
text,
anchor,
brace: curlyBraceLeft,
labelRect,
'brace-r': curlyBraceRight,
braces: curlyBraces,
iconSquare,
iconCircle,
icon,
iconRounded,
imageSquare,
};
const nodeElems = new Map();
export const insertNode = async (elem, node, renderOptions) => {
let newEl;
let el;
// console.log("node is ", node.icon, node.shape)
//special check for rect shape (with or without rounded corners)
if (node.shape === 'rect') {
if (node.rx && node.ry) {
node.shape = 'roundedRect';
} else {
node.shape = 'squareRect';
}
}
if (!shapes[node.shape]) {
throw new Error(`No such shape: ${node.shape}. Please check your syntax.`);
}
if (node.link) {
// Add link when appropriate
let target;
if (renderOptions.config.securityLevel === 'sandbox') {
target = '_top';
} else if (node.linkTarget) {
target = node.linkTarget || '_blank';
}
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
el = await shapes[node.shape](newEl, node, renderOptions);
} else {
el = await shapes[node.shape](elem, node, renderOptions);
newEl = el;
}
if (node.tooltip) {
el.attr('title', node.tooltip);
}
nodeElems.set(node.id, newEl);
if (node.haveCallback) {
nodeElems.get(node.id).attr('class', nodeElems.get(node.id).attr('class') + ' clickable');
}
return newEl;
};
export const setNodeElem = (elem, node) => {
nodeElems.set(node.id, elem);
};
export const clear = () => {
nodeElems.clear();
};
export const positionNode = (node) => {
const el = nodeElems.get(node.id);
log.trace(
'Transforming node',
node.diff,
node,
'translate(' + (node.x - node.width / 2 - 5) + ', ' + node.width / 2 + ')'
);
const padding = 8;
const diff = node.diff || 0;
if (node.clusterNode) {
el.attr(
'transform',
'translate(' +
(node.x + diff - node.width / 2) +
', ' +
(node.y - node.height / 2 - padding) +
')'
);
} else {
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
}
return diff;
};

View File

@@ -1,5 +1,5 @@
import exp from 'constants';
import { shapes } from './nodes.js';
import { shapes } from './shapes.js';
import { describe, it, expect } from 'vitest';
describe('Test Alias for shapes', function () {
// for each shape in docs/syntax/flowchart.md, along with its semantic name, short name, and alias name, add a test case
@@ -84,7 +84,7 @@ describe('Test Alias for shapes', function () {
});
it('should support alias for shadedProcess shape ', function () {
const aliases = ['lined-process', 'lined-rectangle', 'lin-proc', 'lin-rect'];
const aliases = ['lined-process', 'lined-rectangle', 'lin-proc', 'lin-rect'] as const;
for (const alias of aliases) {
expect(shapes[alias]).toBe(shapes['shaded-process']);
}

View File

@@ -0,0 +1,96 @@
import { log } from '../../logger.js';
import { shapes } from './shapes.js';
import type { Node, NonClusterNode, ShapeRenderOptions } from '../types.js';
import type { SVGGroup } from '../../mermaid.js';
import type { D3Selection } from '../../types.js';
import type { graphlib } from 'dagre-d3-es';
type ShapeHandler = (typeof shapes)[keyof typeof shapes];
type NodeElement = D3Selection<SVGAElement> | Awaited<ReturnType<ShapeHandler>>;
const nodeElems = new Map<string, NodeElement>();
export async function insertNode(
elem: SVGGroup,
node: NonClusterNode,
renderOptions: ShapeRenderOptions
) {
let newEl: NodeElement | undefined;
let el;
//special check for rect shape (with or without rounded corners)
if (node.shape === 'rect') {
if (node.rx && node.ry) {
node.shape = 'roundedRect';
} else {
node.shape = 'squareRect';
}
}
const shapeHandler = node.shape ? shapes[node.shape] : undefined;
if (!shapeHandler) {
throw new Error(`No such shape: ${node.shape}. Please check your syntax.`);
}
if (node.link) {
// Add link when appropriate
let target;
if (renderOptions.config.securityLevel === 'sandbox') {
target = '_top';
} else if (node.linkTarget) {
target = node.linkTarget || '_blank';
}
newEl = elem
.insert<SVGAElement>('svg:a')
.attr('xlink:href', node.link)
.attr('target', target ?? null);
el = await shapeHandler(newEl, node, renderOptions);
} else {
el = await shapeHandler(elem, node, renderOptions);
newEl = el;
}
if (node.tooltip) {
el.attr('title', node.tooltip);
}
nodeElems.set(node.id, newEl);
if (node.haveCallback) {
newEl.attr('class', newEl.attr('class') + ' clickable');
}
return newEl;
}
export const setNodeElem = (elem: NodeElement, node: Pick<Node, 'id'>) => {
nodeElems.set(node.id, elem);
};
export const clear = () => {
nodeElems.clear();
};
export const positionNode = (node: ReturnType<graphlib.Graph['node']>) => {
const el = nodeElems.get(node.id)!;
log.trace(
'Transforming node',
node.diff,
node,
'translate(' + (node.x - node.width / 2 - 5) + ', ' + node.width / 2 + ')'
);
const padding = 8;
const diff = node.diff || 0;
if (node.clusterNode) {
el.attr(
'transform',
'translate(' +
(node.x + diff - node.width / 2) +
', ' +
(node.y - node.height / 2 - padding) +
')'
);
} else {
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
}
return diff;
};

View File

@@ -0,0 +1,504 @@
import type { Entries } from 'type-fest';
import type { D3Selection, MaybePromise } from '../../types.js';
import type { Node, ShapeRenderOptions } from '../types.js';
import { anchor } from './shapes/anchor.js';
import { bowTieRect } from './shapes/bowTieRect.js';
import { card } from './shapes/card.js';
import { choice } from './shapes/choice.js';
import { circle } from './shapes/circle.js';
import { crossedCircle } from './shapes/crossedCircle.js';
import { curlyBraceLeft } from './shapes/curlyBraceLeft.js';
import { curlyBraceRight } from './shapes/curlyBraceRight.js';
import { curlyBraces } from './shapes/curlyBraces.js';
import { curvedTrapezoid } from './shapes/curvedTrapezoid.js';
import { cylinder } from './shapes/cylinder.js';
import { dividedRectangle } from './shapes/dividedRect.js';
import { doublecircle } from './shapes/doubleCircle.js';
import { filledCircle } from './shapes/filledCircle.js';
import { flippedTriangle } from './shapes/flippedTriangle.js';
import { forkJoin } from './shapes/forkJoin.js';
import { halfRoundedRectangle } from './shapes/halfRoundedRectangle.js';
import { hexagon } from './shapes/hexagon.js';
import { hourglass } from './shapes/hourglass.js';
import { icon } from './shapes/icon.js';
import { iconCircle } from './shapes/iconCircle.js';
import { iconRounded } from './shapes/iconRounded.js';
import { iconSquare } from './shapes/iconSquare.js';
import { imageSquare } from './shapes/imageSquare.js';
import { inv_trapezoid } from './shapes/invertedTrapezoid.js';
import { labelRect } from './shapes/labelRect.js';
import { lean_left } from './shapes/leanLeft.js';
import { lean_right } from './shapes/leanRight.js';
import { lightningBolt } from './shapes/lightningBolt.js';
import { linedCylinder } from './shapes/linedCylinder.js';
import { linedWaveEdgedRect } from './shapes/linedWaveEdgedRect.js';
import { multiRect } from './shapes/multiRect.js';
import { multiWaveEdgedRectangle } from './shapes/multiWaveEdgedRectangle.js';
import { note } from './shapes/note.js';
import { question } from './shapes/question.js';
import { rect_left_inv_arrow } from './shapes/rectLeftInvArrow.js';
import { rectWithTitle } from './shapes/rectWithTitle.js';
import { roundedRect } from './shapes/roundedRect.js';
import { shadedProcess } from './shapes/shadedProcess.js';
import { slopedRect } from './shapes/slopedRect.js';
import { squareRect } from './shapes/squareRect.js';
import { stadium } from './shapes/stadium.js';
import { state } from './shapes/state.js';
import { stateEnd } from './shapes/stateEnd.js';
import { stateStart } from './shapes/stateStart.js';
import { subroutine } from './shapes/subroutine.js';
import { taggedRect } from './shapes/taggedRect.js';
import { taggedWaveEdgedRectangle } from './shapes/taggedWaveEdgedRectangle.js';
import { text } from './shapes/text.js';
import { tiltedCylinder } from './shapes/tiltedCylinder.js';
import { trapezoid } from './shapes/trapezoid.js';
import { trapezoidalPentagon } from './shapes/trapezoidalPentagon.js';
import { triangle } from './shapes/triangle.js';
import { waveEdgedRectangle } from './shapes/waveEdgedRectangle.js';
import { waveRectangle } from './shapes/waveRectangle.js';
import { windowPane } from './shapes/windowPane.js';
import { classBox } from './shapes/classBox.js';
import { kanbanItem } from './shapes/kanbanItem.js';
type ShapeHandler = <T extends SVGGraphicsElement>(
parent: D3Selection<T>,
node: Node,
options: ShapeRenderOptions
) => MaybePromise<D3Selection<SVGGElement>>;
export interface ShapeDefinition {
semanticName: string;
name: string;
shortName: string;
description: string;
/**
* Aliases can include descriptive names, other short names, etc.
*/
aliases?: string[];
/**
* These are names used by mermaid before the introduction of new shapes. These will not be in standard formats, and shouldn't be used by the users
*/
internalAliases?: string[];
handler: ShapeHandler;
}
export const shapesDefs = [
{
semanticName: 'Process',
name: 'Rectangle',
shortName: 'rect',
description: 'Standard process shape',
aliases: ['proc', 'process', 'rectangle'],
internalAliases: ['squareRect'],
handler: squareRect,
},
{
semanticName: 'Event',
name: 'Rounded Rectangle',
shortName: 'rounded',
description: 'Represents an event',
aliases: ['event'],
internalAliases: ['roundedRect'],
handler: roundedRect,
},
{
semanticName: 'Terminal Point',
name: 'Stadium',
shortName: 'stadium',
description: 'Terminal point',
aliases: ['terminal', 'pill'],
handler: stadium,
},
{
semanticName: 'Subprocess',
name: 'Framed Rectangle',
shortName: 'fr-rect',
description: 'Subprocess',
aliases: ['subprocess', 'subproc', 'framed-rectangle', 'subroutine'],
handler: subroutine,
},
{
semanticName: 'Database',
name: 'Cylinder',
shortName: 'cyl',
description: 'Database storage',
aliases: ['db', 'database', 'cylinder'],
handler: cylinder,
},
{
semanticName: 'Start',
name: 'Circle',
shortName: 'circle',
description: 'Starting point',
aliases: ['circ'],
handler: circle,
},
{
semanticName: 'Decision',
name: 'Diamond',
shortName: 'diam',
description: 'Decision-making step',
aliases: ['decision', 'diamond', 'question'],
handler: question,
},
{
semanticName: 'Prepare Conditional',
name: 'Hexagon',
shortName: 'hex',
description: 'Preparation or condition step',
aliases: ['hexagon', 'prepare'],
handler: hexagon,
},
{
semanticName: 'Data Input/Output',
name: 'Lean Right',
shortName: 'lean-r',
description: 'Represents input or output',
aliases: ['lean-right', 'in-out'],
internalAliases: ['lean_right'],
handler: lean_right,
},
{
semanticName: 'Data Input/Output',
name: 'Lean Left',
shortName: 'lean-l',
description: 'Represents output or input',
aliases: ['lean-left', 'out-in'],
internalAliases: ['lean_left'],
handler: lean_left,
},
{
semanticName: 'Priority Action',
name: 'Trapezoid Base Bottom',
shortName: 'trap-b',
description: 'Priority action',
aliases: ['priority', 'trapezoid-bottom', 'trapezoid'],
handler: trapezoid,
},
{
semanticName: 'Manual Operation',
name: 'Trapezoid Base Top',
shortName: 'trap-t',
description: 'Represents a manual task',
aliases: ['manual', 'trapezoid-top', 'inv-trapezoid'],
internalAliases: ['inv_trapezoid'],
handler: inv_trapezoid,
},
{
semanticName: 'Stop',
name: 'Double Circle',
shortName: 'dbl-circ',
description: 'Represents a stop point',
aliases: ['double-circle'],
internalAliases: ['doublecircle'],
handler: doublecircle,
},
{
semanticName: 'Text Block',
name: 'Text Block',
shortName: 'text',
description: 'Text block',
handler: text,
},
{
semanticName: 'Card',
name: 'Notched Rectangle',
shortName: 'notch-rect',
description: 'Represents a card',
aliases: ['card', 'notched-rectangle'],
handler: card,
},
{
semanticName: 'Lined/Shaded Process',
name: 'Lined Rectangle',
shortName: 'lin-rect',
description: 'Lined process shape',
aliases: ['lined-rectangle', 'lined-process', 'lin-proc', 'shaded-process'],
handler: shadedProcess,
},
{
semanticName: 'Start',
name: 'Small Circle',
shortName: 'sm-circ',
description: 'Small starting point',
aliases: ['start', 'small-circle'],
internalAliases: ['stateStart'],
handler: stateStart,
},
{
semanticName: 'Stop',
name: 'Framed Circle',
shortName: 'fr-circ',
description: 'Stop point',
aliases: ['stop', 'framed-circle'],
internalAliases: ['stateEnd'],
handler: stateEnd,
},
{
semanticName: 'Fork/Join',
name: 'Filled Rectangle',
shortName: 'fork',
description: 'Fork or join in process flow',
aliases: ['join'],
internalAliases: ['forkJoin'],
handler: forkJoin,
},
{
semanticName: 'Collate',
name: 'Hourglass',
shortName: 'hourglass',
description: 'Represents a collate operation',
aliases: ['hourglass', 'collate'],
handler: hourglass,
},
{
semanticName: 'Comment',
name: 'Curly Brace',
shortName: 'brace',
description: 'Adds a comment',
aliases: ['comment', 'brace-l'],
handler: curlyBraceLeft,
},
{
semanticName: 'Comment Right',
name: 'Curly Brace',
shortName: 'brace-r',
description: 'Adds a comment',
handler: curlyBraceRight,
},
{
semanticName: 'Comment with braces on both sides',
name: 'Curly Braces',
shortName: 'braces',
description: 'Adds a comment',
handler: curlyBraces,
},
{
semanticName: 'Com Link',
name: 'Lightning Bolt',
shortName: 'bolt',
description: 'Communication link',
aliases: ['com-link', 'lightning-bolt'],
handler: lightningBolt,
},
{
semanticName: 'Document',
name: 'Document',
shortName: 'doc',
description: 'Represents a document',
aliases: ['doc', 'document'],
handler: waveEdgedRectangle,
},
{
semanticName: 'Delay',
name: 'Half-Rounded Rectangle',
shortName: 'delay',
description: 'Represents a delay',
aliases: ['half-rounded-rectangle'],
handler: halfRoundedRectangle,
},
{
semanticName: 'Direct Access Storage',
name: 'Horizontal Cylinder',
shortName: 'h-cyl',
description: 'Direct access storage',
aliases: ['das', 'horizontal-cylinder'],
handler: tiltedCylinder,
},
{
semanticName: 'Disk Storage',
name: 'Lined Cylinder',
shortName: 'lin-cyl',
description: 'Disk storage',
aliases: ['disk', 'lined-cylinder'],
handler: linedCylinder,
},
{
semanticName: 'Display',
name: 'Curved Trapezoid',
shortName: 'curv-trap',
description: 'Represents a display',
aliases: ['curved-trapezoid', 'display'],
handler: curvedTrapezoid,
},
{
semanticName: 'Divided Process',
name: 'Divided Rectangle',
shortName: 'div-rect',
description: 'Divided process shape',
aliases: ['div-proc', 'divided-rectangle', 'divided-process'],
handler: dividedRectangle,
},
{
semanticName: 'Extract',
name: 'Triangle',
shortName: 'tri',
description: 'Extraction process',
aliases: ['extract', 'triangle'],
handler: triangle,
},
{
semanticName: 'Internal Storage',
name: 'Window Pane',
shortName: 'win-pane',
description: 'Internal storage',
aliases: ['internal-storage', 'window-pane'],
handler: windowPane,
},
{
semanticName: 'Junction',
name: 'Filled Circle',
shortName: 'f-circ',
description: 'Junction point',
aliases: ['junction', 'filled-circle'],
handler: filledCircle,
},
{
semanticName: 'Loop Limit',
name: 'Trapezoidal Pentagon',
shortName: 'notch-pent',
description: 'Loop limit step',
aliases: ['loop-limit', 'notched-pentagon'],
handler: trapezoidalPentagon,
},
{
semanticName: 'Manual File',
name: 'Flipped Triangle',
shortName: 'flip-tri',
description: 'Manual file operation',
aliases: ['manual-file', 'flipped-triangle'],
handler: flippedTriangle,
},
{
semanticName: 'Manual Input',
name: 'Sloped Rectangle',
shortName: 'sl-rect',
description: 'Manual input step',
aliases: ['manual-input', 'sloped-rectangle'],
handler: slopedRect,
},
{
semanticName: 'Multi-Document',
name: 'Stacked Document',
shortName: 'docs',
description: 'Multiple documents',
aliases: ['documents', 'st-doc', 'stacked-document'],
handler: multiWaveEdgedRectangle,
},
{
semanticName: 'Multi-Process',
name: 'Stacked Rectangle',
shortName: 'st-rect',
description: 'Multiple processes',
aliases: ['procs', 'processes', 'stacked-rectangle'],
handler: multiRect,
},
{
semanticName: 'Stored Data',
name: 'Bow Tie Rectangle',
shortName: 'bow-rect',
description: 'Stored data',
aliases: ['stored-data', 'bow-tie-rectangle'],
handler: bowTieRect,
},
{
semanticName: 'Summary',
name: 'Crossed Circle',
shortName: 'cross-circ',
description: 'Summary',
aliases: ['summary', 'crossed-circle'],
handler: crossedCircle,
},
{
semanticName: 'Tagged Document',
name: 'Tagged Document',
shortName: 'tag-doc',
description: 'Tagged document',
aliases: ['tag-doc', 'tagged-document'],
handler: taggedWaveEdgedRectangle,
},
{
semanticName: 'Tagged Process',
name: 'Tagged Rectangle',
shortName: 'tag-rect',
description: 'Tagged process',
aliases: ['tagged-rectangle', 'tag-proc', 'tagged-process'],
handler: taggedRect,
},
{
semanticName: 'Paper Tape',
name: 'Flag',
shortName: 'flag',
description: 'Paper tape',
aliases: ['paper-tape'],
handler: waveRectangle,
},
{
semanticName: 'Odd',
name: 'Odd',
shortName: 'odd',
description: 'Odd shape',
internalAliases: ['rect_left_inv_arrow'],
handler: rect_left_inv_arrow,
},
{
semanticName: 'Lined Document',
name: 'Lined Document',
shortName: 'lin-doc',
description: 'Lined document',
aliases: ['lined-document'],
handler: linedWaveEdgedRect,
},
] as const satisfies ShapeDefinition[];
const generateShapeMap = () => {
// These are the shapes that didn't have documentation present
const undocumentedShapes = {
// States
state,
choice,
note,
// Rectangles
rectWithTitle,
labelRect,
// Icons
iconSquare,
iconCircle,
icon,
iconRounded,
imageSquare,
anchor,
// Kanban diagram
kanbanItem,
// class diagram
classBox,
} as const;
const entries = [
...(Object.entries(undocumentedShapes) as Entries<typeof undocumentedShapes>),
...shapesDefs.flatMap((shape) => {
const aliases = [
shape.shortName,
...('aliases' in shape ? shape.aliases : []),
...('internalAliases' in shape ? shape.internalAliases : []),
];
return aliases.map((alias) => [alias, shape.handler] as const);
}),
];
return Object.fromEntries(entries) as Record<
(typeof entries)[number][0],
(typeof entries)[number][1]
> satisfies Record<string, ShapeHandler>;
};
export const shapes = generateShapeMap();
export function isValidShape(shape: string): shape is ShapeID {
return shape in shapes;
}
export type ShapeID = keyof typeof shapes;

View File

@@ -1,11 +1,13 @@
import { log } from '../../../logger.js';
import { updateNodeBounds, getNodeClasses } from './util.js';
import intersect from '../intersect/index.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import { handleUndefinedAttr } from '../../../utils.js';
import type { D3Selection } from '../../../types.js';
export const anchor = (parent: SVGAElement, node: Node): Promise<SVGAElement> => {
export function anchor<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { labelStyles } = styles2String(node);
node.labelStyle = labelStyles;
const classes = getNodeClasses(node);
@@ -14,7 +16,6 @@ export const anchor = (parent: SVGAElement, node: Node): Promise<SVGAElement> =>
cssClasses = 'anchor';
}
const shapeSvg = parent
// @ts-ignore - SVGElement is not typed
.insert('g')
.attr('class', cssClasses)
.attr('id', node.domId || node.id);
@@ -23,6 +24,7 @@ export const anchor = (parent: SVGAElement, node: Node): Promise<SVGAElement> =>
const { cssStyles } = node;
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, { fill: 'black', stroke: 'none', fillStyle: 'solid' });
@@ -31,7 +33,7 @@ export const anchor = (parent: SVGAElement, node: Node): Promise<SVGAElement> =>
}
const roughNode = rc.circle(0, 0, radius * 2, options);
const circleElem = shapeSvg.insert(() => roughNode, ':first-child');
circleElem.attr('class', 'anchor').attr('style', cssStyles);
circleElem.attr('class', 'anchor').attr('style', handleUndefinedAttr(cssStyles));
updateNodeBounds(node, circleElem);
@@ -41,4 +43,4 @@ export const anchor = (parent: SVGAElement, node: Node): Promise<SVGAElement> =>
};
return shapeSvg;
};
}

View File

@@ -1,8 +1,9 @@
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js';
import intersect from '../intersect/index.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import type { D3Selection } from '../../../types.js';
function generateArcPoints(
x1: number,
@@ -70,7 +71,7 @@ function generateArcPoints(
return points;
}
export const bowTieRect = async (parent: SVGAElement, node: Node) => {
export async function bowTieRect<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
@@ -91,6 +92,7 @@ export const bowTieRect = async (parent: SVGAElement, node: Node) => {
...generateArcPoints(w / 2, h / 2, w / 2, -h / 2, rx, ry, true),
];
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
@@ -122,4 +124,4 @@ export const bowTieRect = async (parent: SVGAElement, node: Node) => {
};
return shapeSvg;
};
}

View File

@@ -1,11 +1,12 @@
import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js';
import intersect from '../intersect/index.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import { insertPolygonShape } from './insertPolygonShape.js';
import { createPathFromPoints } from './util.js';
import type { D3Selection } from '../../../types.js';
// const createPathFromPoints = (points: { x: number; y: number }[]): string => {
// const pointStrings = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`);
@@ -13,7 +14,7 @@ import { createPathFromPoints } from './util.js';
// return pointStrings.join(' ');
// };
export async function card(parent: SVGAElement, node: Node): Promise<SVGAElement> {
export async function card<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node));
@@ -34,10 +35,11 @@ export async function card(parent: SVGAElement, node: Node): Promise<SVGAElement
{ x: left + padding, y: top },
];
let polygon: d3.Selection<SVGPolygonElement | SVGGElement, unknown, null, undefined>;
let polygon: D3Selection<SVGGElement> | Awaited<ReturnType<typeof insertPolygonShape>>;
const { cssStyles } = node;
if (node.look === 'handDrawn') {
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
const pathData = createPathFromPoints(points);

View File

@@ -1,12 +1,11 @@
import intersect from '../intersect/index.js';
import type { Node } from '../../types.js';
import type { SVG } from '../../../diagram-api/types.js';
// @ts-ignore TODO: Fix rough typings
import rough from 'roughjs';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import { createPathFromPoints, getNodeClasses } from './util.js';
import type { D3Selection } from '../../../types.js';
export const choice = (parent: SVG, node: Node) => {
export function choice<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { nodeStyles } = styles2String(node);
node.label = '';
const shapeSvg = parent
@@ -24,7 +23,7 @@ export const choice = (parent: SVG, node: Node) => {
{ x: -s / 2, y: 0 },
];
// @ts-ignore TODO: Fix rough typings
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
@@ -53,4 +52,4 @@ export const choice = (parent: SVG, node: Node) => {
};
return shapeSvg;
};
}

View File

@@ -4,8 +4,10 @@ import intersect from '../intersect/index.js';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import type { D3Selection } from '../../../types.js';
import { handleUndefinedAttr } from '../../../utils.js';
export const circle = async (parent: SVGAElement, node: Node): Promise<SVGAElement> => {
export async function circle<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, getNodeClasses(node));
@@ -15,12 +17,13 @@ export const circle = async (parent: SVGAElement, node: Node): Promise<SVGAEleme
const { cssStyles } = node;
if (node.look === 'handDrawn') {
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
const roughNode = rc.circle(0, 0, radius * 2, options);
circleElem = shapeSvg.insert(() => roughNode, ':first-child');
circleElem.attr('class', 'basic label-container').attr('style', cssStyles);
circleElem.attr('class', 'basic label-container').attr('style', handleUndefinedAttr(cssStyles));
} else {
circleElem = shapeSvg
.insert('circle', ':first-child')
@@ -39,4 +42,4 @@ export const circle = async (parent: SVGAElement, node: Node): Promise<SVGAEleme
};
return shapeSvg;
};
}

View File

@@ -0,0 +1,207 @@
import { updateNodeBounds } from './util.js';
import { getConfig } from '../../../diagram-api/diagramAPI.js';
import { select } from 'd3';
import type { Node } from '../../types.js';
import type { ClassNode } from '../../../diagrams/class/classTypes.js';
import rough from 'roughjs';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import intersect from '../intersect/index.js';
import { textHelper } from '../../../diagrams/class/shapeUtil.js';
import { evaluate } from '../../../diagrams/common/common.js';
import type { D3Selection } from '../../../types.js';
export async function classBox<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const config = getConfig();
const PADDING = config.class!.padding ?? 12;
const GAP = PADDING;
const useHtmlLabels = node.useHtmlLabels ?? evaluate(config.htmlLabels) ?? true;
// Treat node as classNode
const classNode = node as unknown as ClassNode;
classNode.annotations = classNode.annotations ?? [];
classNode.members = classNode.members ?? [];
classNode.methods = classNode.methods ?? [];
const { shapeSvg, bbox } = await textHelper(parent, node, config, useHtmlLabels, GAP);
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
node.cssStyles = classNode.styles || '';
const styles = classNode.styles?.join(';') || nodeStyles || '';
if (!node.cssStyles) {
node.cssStyles = styles.replaceAll('!important', '').split(';');
}
const renderExtraBox =
classNode.members.length === 0 &&
classNode.methods.length === 0 &&
!config.class?.hideEmptyMembersBox;
// Setup roughjs
// @ts-ignore TODO: Fix rough typings
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
if (node.look !== 'handDrawn') {
options.roughness = 0;
options.fillStyle = 'solid';
}
const w = bbox.width;
let h = bbox.height;
if (classNode.members.length === 0 && classNode.methods.length === 0) {
h += GAP;
} else if (classNode.members.length > 0 && classNode.methods.length === 0) {
h += GAP * 2;
}
const x = -w / 2;
const y = -h / 2;
// Create and center rectangle
const roughRect = rc.rectangle(
x - PADDING,
y -
PADDING -
(renderExtraBox
? PADDING
: classNode.members.length === 0 && classNode.methods.length === 0
? -PADDING / 2
: 0),
w + 2 * PADDING,
h +
2 * PADDING +
(renderExtraBox
? PADDING * 2
: classNode.members.length === 0 && classNode.methods.length === 0
? -PADDING
: 0),
options
);
const rect = shapeSvg.insert(() => roughRect, ':first-child');
rect.attr('class', 'basic label-container');
const rectBBox = rect.node()!.getBBox();
// Rect is centered so now adjust labels.
// TODO: Fix types
shapeSvg.selectAll('.text').each((_: any, i: number, nodes: any) => {
const text = select<any, unknown>(nodes[i]);
// Get the current transform attribute
const transform = text.attr('transform');
// Initialize variables for the translation values
let translateY = 0;
// Check if the transform attribute exists
if (transform) {
const regex = RegExp(/translate\(([^,]+),([^)]+)\)/);
const translate = regex.exec(transform);
if (translate) {
translateY = parseFloat(translate[2]);
}
}
// Add to the y value
let newTranslateY =
translateY +
y +
PADDING -
(renderExtraBox
? PADDING
: classNode.members.length === 0 && classNode.methods.length === 0
? -PADDING / 2
: 0);
if (!useHtmlLabels) {
// Fix so non html labels are better centered.
// BBox of text seems to be slightly different when calculated so we offset
newTranslateY -= 4;
}
let newTranslateX = x;
if (
text.attr('class').includes('label-group') ||
text.attr('class').includes('annotation-group')
) {
newTranslateX = -text.node()?.getBBox().width / 2 || 0;
shapeSvg.selectAll('text').each(function (_: any, i: number, nodes: any) {
if (window.getComputedStyle(nodes[i]).textAnchor === 'middle') {
newTranslateX = 0;
}
});
}
// Set the updated transform attribute
text.attr('transform', `translate(${newTranslateX}, ${newTranslateY})`);
});
// Render divider lines.
const annotationGroupHeight =
(shapeSvg.select('.annotation-group').node() as SVGGraphicsElement).getBBox().height -
(renderExtraBox ? PADDING / 2 : 0) || 0;
const labelGroupHeight =
(shapeSvg.select('.label-group').node() as SVGGraphicsElement).getBBox().height -
(renderExtraBox ? PADDING / 2 : 0) || 0;
const membersGroupHeight =
(shapeSvg.select('.members-group').node() as SVGGraphicsElement).getBBox().height -
(renderExtraBox ? PADDING / 2 : 0) || 0;
// First line (under label)
if (classNode.members.length > 0 || classNode.methods.length > 0 || renderExtraBox) {
const roughLine = rc.line(
rectBBox.x,
annotationGroupHeight + labelGroupHeight + y + PADDING,
rectBBox.x + rectBBox.width,
annotationGroupHeight + labelGroupHeight + y + PADDING,
options
);
const line = shapeSvg.insert(() => roughLine);
line.attr('class', 'divider').attr('style', styles);
}
// Second line (under members)
if (renderExtraBox || classNode.members.length > 0 || classNode.methods.length > 0) {
const roughLine = rc.line(
rectBBox.x,
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + GAP * 2 + PADDING,
rectBBox.x + rectBBox.width,
annotationGroupHeight + labelGroupHeight + membersGroupHeight + y + PADDING + GAP * 2,
options
);
const line = shapeSvg.insert(() => roughLine);
line.attr('class', 'divider').attr('style', styles);
}
/// Apply styles ///
if (classNode.look !== 'handDrawn') {
shapeSvg.selectAll('path').attr('style', styles);
}
// Apply other styles like stroke-width and stroke-dasharray to border (not background of shape)
rect.select(':nth-child(2)').attr('style', styles);
// Divider lines
shapeSvg.selectAll('.divider').select('path').attr('style', styles);
// Text elements
if (node.labelStyle) {
shapeSvg.selectAll('span').attr('style', node.labelStyle);
} else {
shapeSvg.selectAll('span').attr('style', styles);
}
// SVG text uses fill not color
if (!useHtmlLabels) {
// We just want to apply color to the text
const colorRegex = RegExp(/color\s*:\s*([^;]*)/);
const match = colorRegex.exec(styles);
if (match) {
const colorStyle = match[0].replace('color', 'fill');
shapeSvg.selectAll('tspan').attr('style', colorStyle);
} else if (labelStyles) {
const match = colorRegex.exec(labelStyles);
if (match) {
const colorStyle = match[0].replace('color', 'fill');
shapeSvg.selectAll('tspan').attr('style', colorStyle);
}
}
}
updateNodeBounds(node, rect);
node.intersect = function (point) {
return intersect.rect(node, point);
};
return shapeSvg;
}

View File

@@ -1,10 +1,10 @@
import { log } from '../../../logger.js';
import { getNodeClasses, updateNodeBounds } from './util.js';
import type { SVG } from '../../../diagram-api/types.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import intersect from '../intersect/index.js';
import type { D3Selection } from '../../../types.js';
function createLine(r: number) {
const xAxis45 = Math.cos(Math.PI / 4); // cosine of 45 degrees
@@ -20,7 +20,7 @@ function createLine(r: number) {
M ${pointQ1.x},${pointQ1.y} L ${pointQ3.x},${pointQ3.y}`;
}
export const crossedCircle = (parent: SVG, node: Node) => {
export function crossedCircle<T extends SVGGraphicsElement>(parent: D3Selection<T>, node: Node) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
node.label = '';
@@ -31,7 +31,7 @@ export const crossedCircle = (parent: SVG, node: Node) => {
const radius = Math.max(30, node?.width ?? 0);
const { cssStyles } = node;
// @ts-expect-error shapeSvg d3 class is incorrect?
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, {});
@@ -64,4 +64,4 @@ export const crossedCircle = (parent: SVG, node: Node) => {
};
return shapeSvg;
};
}

View File

@@ -1,8 +1,9 @@
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js';
import intersect from '../intersect/index.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import type { D3Selection } from '../../../types.js';
function generateCirclePoints(
centerX: number,
@@ -34,7 +35,10 @@ function generateCirclePoints(
return points;
}
export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => {
export async function curlyBraceLeft<T extends SVGGraphicsElement>(
parent: D3Selection<T>,
node: Node
) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
@@ -66,6 +70,7 @@ export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => {
{ x: w / 2, y: h / 2 + radius },
];
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, { fill: 'none' });
@@ -107,4 +112,4 @@ export const curlyBraceLeft = async (parent: SVGAElement, node: Node) => {
};
return shapeSvg;
};
}

View File

@@ -1,8 +1,9 @@
import { labelHelper, updateNodeBounds, getNodeClasses, createPathFromPoints } from './util.js';
import intersect from '../intersect/index.js';
import type { Node } from '../../types.d.ts';
import type { Node } from '../../types.js';
import { styles2String, userNodeOverrides } from './handDrawnShapeStyles.js';
import rough from 'roughjs';
import type { D3Selection } from '../../../types.js';
function generateCirclePoints(
centerX: number,
@@ -34,7 +35,10 @@ function generateCirclePoints(
return points;
}
export const curlyBraceRight = async (parent: SVGAElement, node: Node) => {
export async function curlyBraceRight<T extends SVGGraphicsElement>(
parent: D3Selection<T>,
node: Node
) {
const { labelStyles, nodeStyles } = styles2String(node);
node.labelStyle = labelStyles;
const { shapeSvg, bbox, label } = await labelHelper(parent, node, getNodeClasses(node));
@@ -66,6 +70,7 @@ export const curlyBraceRight = async (parent: SVGAElement, node: Node) => {
{ x: -w / 2, y: h / 2 + radius },
];
// @ts-expect-error -- Passing a D3.Selection seems to work for some reason
const rc = rough.svg(shapeSvg);
const options = userNodeOverrides(node, { fill: 'none' });
@@ -107,4 +112,4 @@ export const curlyBraceRight = async (parent: SVGAElement, node: Node) => {
};
return shapeSvg;
};
}

Some files were not shown because too many files have changed in this diff Show More