Merge branch 'release/9.4.0'

This commit is contained in:
Per Brolin 2023-02-15 16:20:22 +01:00
commit 98bec36f81
180 changed files with 8956 additions and 1622 deletions

View File

@ -3,4 +3,5 @@ dist/**
docs/Setup.md docs/Setup.md
cypress.config.js cypress.config.js
cypress/plugins/index.js cypress/plugins/index.js
coverage coverage
*.json

150
.eslintrc.cjs Normal file
View File

@ -0,0 +1,150 @@
module.exports = {
env: {
browser: true,
es6: true,
'jest/globals': true,
node: true,
},
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
tsconfigRootDir: __dirname,
sourceType: 'module',
ecmaVersion: 2020,
allowAutomaticSingleRunInference: true,
project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
parser: '@typescript-eslint/parser',
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:json/recommended',
'plugin:markdown/recommended',
'plugin:@cspell/recommended',
'prettier',
],
plugins: [
'@typescript-eslint',
'no-only-tests',
'html',
'jest',
'jsdoc',
'json',
'@cspell',
'lodash',
'unicorn',
],
rules: {
curly: 'error',
'no-console': 'error',
'no-prototype-builtins': 'off',
'no-unused-vars': 'off',
'cypress/no-async-tests': 'off',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-expect-error': 'allow-with-description',
'ts-ignore': 'allow-with-description',
'ts-nocheck': 'allow-with-description',
'ts-check': 'allow-with-description',
minimumDescriptionLength: 10,
},
],
'json/*': ['error', 'allowComments'],
'@cspell/spellchecker': [
'error',
{
checkIdentifiers: false,
checkStrings: false,
checkStringTemplates: false,
},
],
'no-empty': [
'error',
{
allowEmptyCatch: true,
},
],
'no-only-tests/no-only-tests': 'error',
'lodash/import-scope': ['error', 'method'],
'unicorn/better-regex': 'error',
'unicorn/no-abusive-eslint-disable': 'error',
'unicorn/no-array-push-push': 'error',
'unicorn/no-for-loop': 'error',
'unicorn/no-instanceof-array': 'error',
'unicorn/no-typeof-undefined': 'error',
'unicorn/no-unnecessary-await': 'error',
'unicorn/no-unsafe-regex': 'warn',
'unicorn/no-useless-promise-resolve-reject': 'error',
'unicorn/prefer-array-find': 'error',
'unicorn/prefer-array-flat-map': 'error',
'unicorn/prefer-array-index-of': 'error',
'unicorn/prefer-array-some': 'error',
'unicorn/prefer-default-parameters': 'error',
'unicorn/prefer-includes': 'error',
'unicorn/prefer-negative-index': 'error',
'unicorn/prefer-object-from-entries': 'error',
'unicorn/prefer-string-starts-ends-with': 'error',
'unicorn/prefer-string-trim-start-end': 'error',
'unicorn/string-content': 'error',
'unicorn/prefer-spread': 'error',
'unicorn/no-lonely-if': 'error',
},
overrides: [
{
files: ['cypress/**', 'demos/**'],
rules: {
'no-console': 'off',
},
},
{
files: ['*.{js,jsx,mjs,cjs}'],
extends: ['plugin:jsdoc/recommended'],
rules: {
'jsdoc/check-indentation': 'off',
'jsdoc/check-alignment': 'off',
'jsdoc/check-line-alignment': 'off',
'jsdoc/multiline-blocks': 'off',
'jsdoc/newline-after-description': 'off',
'jsdoc/tag-lines': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns': 'off',
'jsdoc/require-returns-description': 'off',
},
},
{
files: ['*.{ts,tsx}'],
plugins: ['tsdoc'],
rules: {
'tsdoc/syntax': 'error',
},
},
{
files: ['*.spec.{ts,js}', 'cypress/**', 'demos/**', '**/docs/**'],
rules: {
'jsdoc/require-jsdoc': 'off',
'@typescript-eslint/no-unused-vars': 'off',
},
},
{
files: ['*.html', '*.md', '**/*.md/*'],
rules: {
'no-var': 'error',
'no-undef': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-floating-promises': 'off',
'@typescript-eslint/no-misused-promises': 'off',
},
parserOptions: {
project: null,
},
},
],
};

View File

@ -1,137 +0,0 @@
{
"env": {
"browser": true,
"es6": true,
"jest/globals": true,
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
},
"sourceType": "module"
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:json/recommended",
"plugin:markdown/recommended",
"plugin:@cspell/recommended",
"prettier"
],
"plugins": [
"@typescript-eslint",
"no-only-tests",
"html",
"jest",
"jsdoc",
"json",
"@cspell",
"lodash",
"unicorn"
],
"rules": {
"curly": "error",
"no-console": "error",
"no-prototype-builtins": "off",
"no-unused-vars": "off",
"cypress/no-async-tests": "off",
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
"ts-ignore": "allow-with-description",
"ts-nocheck": "allow-with-description",
"ts-check": "allow-with-description",
"minimumDescriptionLength": 10
}
],
"json/*": ["error", "allowComments"],
"@cspell/spellchecker": [
"error",
{
"checkIdentifiers": false,
"checkStrings": false,
"checkStringTemplates": false
}
],
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-only-tests/no-only-tests": "error",
"lodash/import-scope": ["error", "method"],
"unicorn/better-regex": "error",
"unicorn/no-abusive-eslint-disable": "error",
"unicorn/no-array-push-push": "error",
"unicorn/no-for-loop": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-typeof-undefined": "error",
"unicorn/no-unnecessary-await": "error",
"unicorn/no-unsafe-regex": "warn",
"unicorn/no-useless-promise-resolve-reject": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-default-parameters": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-object-from-entries": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-string-trim-start-end": "error",
"unicorn/string-content": "error",
"unicorn/prefer-spread": "error",
"unicorn/no-lonely-if": "error"
},
"overrides": [
{
"files": ["cypress/**", "demos/**"],
"rules": {
"no-console": "off"
}
},
{
"files": ["*.{js,jsx,mjs,cjs}"],
"extends": ["plugin:jsdoc/recommended"],
"rules": {
"jsdoc/check-indentation": "off",
"jsdoc/check-alignment": "off",
"jsdoc/check-line-alignment": "off",
"jsdoc/multiline-blocks": "off",
"jsdoc/newline-after-description": "off",
"jsdoc/tag-lines": "off",
"jsdoc/require-param-description": "off",
"jsdoc/require-param-type": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-returns-description": "off"
}
},
{
"files": ["*.{ts,tsx}"],
"plugins": ["tsdoc"],
"rules": {
"tsdoc/syntax": "error"
}
},
{
"files": ["*.spec.{ts,js}", "cypress/**", "demos/**", "**/docs/**"],
"rules": {
"jsdoc/require-jsdoc": "off",
"@typescript-eslint/no-unused-vars": "off"
}
},
{
"files": ["*.html", "*.md", "**/*.md/*"],
"rules": {
"no-var": "error",
"no-undef": "off",
"@typescript-eslint/no-unused-vars": "off"
}
}
]
}

4
.github/FUNDING.yml vendored
View File

@ -1,6 +1,8 @@
# These are supported funding model platforms # These are supported funding model platforms
github: [knsv] github:
- knsv
- sidharthv96
#patreon: # Replace with a single Patreon username #patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username #open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username #ko_fi: # Replace with a single Ko-fi username

View File

@ -14,4 +14,5 @@ Make sure you
- [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md) - [ ] :book: have read the [contribution guidelines](https://github.com/mermaid-js/mermaid/blob/develop/CONTRIBUTING.md)
- [ ] :computer: have added unit/e2e tests (if appropriate) - [ ] :computer: have added unit/e2e tests (if appropriate)
- [ ] :notebook: have added documentation (if appropriate)
- [ ] :bookmark: targeted `develop` branch - [ ] :bookmark: targeted `develop` branch

View File

@ -32,6 +32,7 @@ jobs:
# and run all Cypress tests # and run all Cypress tests
- name: Cypress run - name: Cypress run
uses: cypress-io/github-action@v4 uses: cypress-io/github-action@v4
id: cypress
# If CYPRESS_RECORD_KEY is set, run in parallel on all containers # If CYPRESS_RECORD_KEY is set, run in parallel on all containers
# Otherwise (e.g. if running from fork), we run on a single container only # Otherwise (e.g. if running from fork), we run on a single container only
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }} if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
@ -44,3 +45,10 @@ jobs:
parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }} parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
env: env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
if: ${{ failure() && steps.cypress.conclusion == 'failure' }}
with:
name: error-snapshots
path: cypress/snapshots/**/__diff_output__/*

View File

@ -13,11 +13,10 @@ on:
- master - master
pull_request: pull_request:
branches: branches:
- develop
- master - master
schedule: schedule:
# * is a special character in YAML so you have to quote this string # * is a special character in YAML so you have to quote this string
- cron: '30 8 * * 5' - cron: '30 8 * * *'
jobs: jobs:
linkChecker: linkChecker:

View File

@ -7,6 +7,7 @@ on:
- opened - opened
- synchronize - synchronize
- ready_for_review - ready_for_review
workflow_dispatch:
permissions: permissions:
contents: write contents: write
@ -36,7 +37,20 @@ jobs:
CYPRESS_CACHE_FOLDER: .cache/Cypress CYPRESS_CACHE_FOLDER: .cache/Cypress
- name: Run Linting - name: Run Linting
run: pnpm run lint shell: bash
run: |
if ! pnpm run lint; then
# print a nice error message on lint failure
ERROR_MESSAGE='Running `pnpm run lint` failed.'
ERROR_MESSAGE+=' Running `pnpm run lint:fix` may fix this issue. '
ERROR_MESSAGE+=" If this error doesn't occur on your local machine,"
ERROR_MESSAGE+=' make sure your packages are up-to-date by running `pnpm install`.'
ERROR_MESSAGE+=' You may also need to delete your prettier cache by running'
ERROR_MESSAGE+=' `rm ./node_modules/.cache/prettier/.prettier-cache`.'
echo "::error title=Lint failure::${ERROR_MESSAGE}"
# make sure to return an error exitcode so that GitHub actions shows a red-cross
exit 1
fi
- name: Verify Docs - name: Verify Docs
id: verifyDocs id: verifyDocs

View File

@ -1,5 +1,11 @@
export default { export default {
'!(docs/**/*)*.{ts,js,json,html,md,mts}': ['eslint --fix', 'prettier --write'], '!(docs/**/*)*.{ts,js,json,html,md,mts}': [
'eslint --cache --cache-strategy content --fix',
// don't cache prettier yet, since we use `prettier-plugin-jsdoc`,
// and prettier doesn't invalidate cache on plugin updates"
// https://prettier.io/docs/en/cli.html#--cache
'prettier --write',
],
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'], 'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'], '**/*.jison': ['pnpm -w run lint:jison'],
}; };

View File

@ -9,5 +9,8 @@ https://mkdocs.org/
https://osawards.com/javascript/#nominees https://osawards.com/javascript/#nominees
https://osawards.com/javascript/2019 https://osawards.com/javascript/2019
# Timeout error, maybe Twitter has anti-bot defences against GitHub's CI servers?
https://twitter.com/mermaidjs_
# Don't check files that are generated during the build via `pnpm docs:code` # Don't check files that are generated during the build via `pnpm docs:code`
packages/mermaid/src/docs/config/setup/* packages/mermaid/src/docs/config/setup/*

View File

@ -36,16 +36,11 @@ const packageOptions = {
packageName: 'mermaid', packageName: 'mermaid',
file: 'mermaid.ts', file: 'mermaid.ts',
}, },
'mermaid-mindmap': { 'mermaid-example-diagram': {
name: 'mermaid-mindmap', name: 'mermaid-example-diagram',
packageName: 'mermaid-mindmap', packageName: 'mermaid-example-diagram',
file: 'detector.ts', file: 'detector.ts',
}, },
// 'mermaid-example-diagram-detector': {
// name: 'mermaid-example-diagram-detector',
// packageName: 'mermaid-example-diagram',
// file: 'detector.ts',
// },
}; };
interface BuildOptions { interface BuildOptions {
@ -119,11 +114,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
if (watch && config.build) { if (watch && config.build) {
config.build.watch = { config.build.watch = {
include: [ include: ['packages/mermaid-example-diagram/src/**', 'packages/mermaid/src/**'],
'packages/mermaid-mindmap/src/**',
'packages/mermaid/src/**',
// 'packages/mermaid-example-diagram/src/**',
],
}; };
} }
@ -146,10 +137,9 @@ const main = async () => {
}; };
if (watch) { if (watch) {
build(getBuildConfig({ minify: false, watch, core: true, entryName: 'mermaid' })); build(getBuildConfig({ minify: false, watch, core: false, entryName: 'mermaid' }));
if (!mermaidOnly) { if (!mermaidOnly) {
build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-mindmap' })); build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
// build(getBuildConfig({ minify: false, watch, entryName: 'mermaid-example-diagram' }));
} }
} else if (visualize) { } else if (visualize) {
await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' })); await build(getBuildConfig({ minify: false, core: true, entryName: 'mermaid' }));

View File

@ -1,6 +1,5 @@
import express, { NextFunction, Request, Response } from 'express'; import express, { NextFunction, Request, Response } from 'express';
import { createServer as createViteServer } from 'vite'; import { createServer as createViteServer } from 'vite';
// import { getBuildConfig } from './build';
const cors = (req: Request, res: Response, next: NextFunction) => { const cors = (req: Request, res: Response, next: NextFunction) => {
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
@ -22,8 +21,8 @@ async function createServer() {
app.use(cors); app.use(cors);
app.use(express.static('./packages/mermaid/dist')); app.use(express.static('./packages/mermaid/dist'));
// app.use(express.static('./packages/mermaid-example-diagram/dist'));
app.use(express.static('./packages/mermaid-example-diagram/dist')); app.use(express.static('./packages/mermaid-example-diagram/dist'));
app.use(express.static('./packages/mermaid-mindmap/dist'));
app.use(vite.middlewares); app.use(vite.middlewares);
app.use(express.static('demos')); app.use(express.static('demos'));
app.use(express.static('cypress/platform')); app.use(express.static('cypress/platform'));
@ -33,5 +32,4 @@ async function createServer() {
}); });
} }
// build(getBuildConfig({ minify: false, watch: true }));
createServer(); createServer();

View File

@ -1,6 +1,6 @@
# mermaid # mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_) [![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=twitter)](https://twitter.com/mermaidjs_)
English | [简体中文](./README.zh-CN.md) English | [简体中文](./README.zh-CN.md)

View File

@ -1,6 +1,6 @@
# mermaid # mermaid
[![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_) [![Build CI Status](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml/badge.svg)](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [![NPM](https://img.shields.io/npm/v/mermaid)](https://www.npmjs.com/package/mermaid) [![npm minified gzipped bundle size](https://img.shields.io/bundlephobia/minzip/mermaid)](https://bundlephobia.com/package/mermaid) [![Coverage Status](https://coveralls.io/repos/github/mermaid-js/mermaid/badge.svg?branch=master)](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [![CDN Status](https://img.shields.io/jsdelivr/npm/hm/mermaid)](https://www.jsdelivr.com/package/npm/mermaid) [![NPM](https://img.shields.io/npm/dm/mermaid)](https://www.npmjs.com/package/mermaid) [![Join our Slack!](https://img.shields.io/static/v1?message=join%20chat&color=9cf&logo=slack&label=slack)](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [![Twitter Follow](https://img.shields.io/badge/Social-mermaidjs__-blue?style=social&logo=twitter)](https://twitter.com/mermaidjs_)
[English](./README.md) | 简体中文 [English](./README.md) | 简体中文
@ -10,7 +10,7 @@
**感谢所有参与进来提交 PR解答疑问的人们! 🙏** **感谢所有参与进来提交 PR解答疑问的人们! 🙏**
<a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/img/book-banner-pre-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a> <a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a>
## 关于 Mermaid ## 关于 Mermaid

View File

@ -6,6 +6,7 @@
"adamiecki", "adamiecki",
"alois", "alois",
"antiscript", "antiscript",
"appli",
"applitools", "applitools",
"asciidoctor", "asciidoctor",
"ashish", "ashish",
@ -13,6 +14,7 @@
"bbox", "bbox",
"bilkent", "bilkent",
"bisheng", "bisheng",
"blrs",
"braintree", "braintree",
"brkt", "brkt",
"brolin", "brolin",
@ -31,6 +33,7 @@
"doku", "doku",
"dompurify", "dompurify",
"edgechromium", "edgechromium",
"elkjs",
"faber", "faber",
"flatmap", "flatmap",
"ftplugin", "ftplugin",
@ -39,6 +42,7 @@
"gitgraph", "gitgraph",
"globby", "globby",
"graphlib", "graphlib",
"graphviz",
"grav", "grav",
"greywolf", "greywolf",
"inkdrop", "inkdrop",
@ -52,8 +56,10 @@
"knut", "knut",
"laganeckas", "laganeckas",
"lintstagedrc", "lintstagedrc",
"logmsg",
"lucida", "lucida",
"matthieu", "matthieu",
"mdast",
"mdbook", "mdbook",
"mermerd", "mermerd",
"mindaugas", "mindaugas",
@ -69,6 +75,7 @@
"pnpm", "pnpm",
"podlite", "podlite",
"quence", "quence",
"radious",
"ranksep", "ranksep",
"rect", "rect",
"rects", "rects",
@ -78,17 +85,20 @@
"setupgraphviewbox", "setupgraphviewbox",
"shiki", "shiki",
"sidharth", "sidharth",
"sidharthv",
"sphinxcontrib", "sphinxcontrib",
"statediagram", "statediagram",
"stylis", "stylis",
"substate", "substate",
"sveidqvist", "sveidqvist",
"swimm",
"techn", "techn",
"teststr", "teststr",
"textlength", "textlength",
"treemap", "treemap",
"ts-nocheck", "ts-nocheck",
"tuleap", "tuleap",
"ugge",
"unist", "unist",
"verdana", "verdana",
"viewports", "viewports",

View File

@ -2,7 +2,7 @@ const utf8ToB64 = (str) => {
return window.btoa(unescape(encodeURIComponent(str))); return window.btoa(unescape(encodeURIComponent(str)));
}; };
const batchId = 'mermid-batch' + new Date().getTime(); const batchId = 'mermaid-batch' + new Date().getTime();
export const mermaidUrl = (graphStr, options, api) => { export const mermaidUrl = (graphStr, options, api) => {
const obj = { const obj = {
@ -46,8 +46,22 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
if (!options.fontSize) { if (!options.fontSize) {
options.fontSize = '16px'; options.fontSize = '16px';
} }
const url = mermaidUrl(graphStr, options, api);
openURLAndVerifyRendering(url, options, validation);
};
export const urlSnapshotTest = (url, _options, api = false, validation) => {
const options = Object.assign(_options);
openURLAndVerifyRendering(url, options, validation);
};
export const renderGraph = (graphStr, options, api) => {
const url = mermaidUrl(graphStr, options, api);
openURLAndVerifyRendering(url, options);
};
const openURLAndVerifyRendering = (url, options, validation = undefined) => {
const useAppli = Cypress.env('useAppli'); const useAppli = Cypress.env('useAppli');
cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
if (useAppli) { if (useAppli) {
@ -60,82 +74,20 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
}); });
} }
const url = mermaidUrl(graphStr, options, api);
cy.visit(url); cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
if (validation) { if (validation) {
cy.get('svg').should(validation); cy.get('svg').should(validation);
} }
cy.get('svg');
// Default name to test title
if (useAppli) { if (useAppli) {
cy.log('Check eyes' + Cypress.spec.name); cy.log('Check eyes' + Cypress.spec.name);
cy.eyesCheckWindow('Click!'); cy.eyesCheckWindow('Click!');
cy.log('Closing eyes: ' + Cypress.spec.name); cy.log('Closing eyes' + Cypress.spec.name);
cy.eyesClose(); cy.eyesClose();
} else { } else {
cy.matchImageSnapshot(name); cy.matchImageSnapshot(name);
} }
}; };
export const urlSnapshotTest = (url, _options, api = false, validation) => {
cy.log(_options);
const options = Object.assign(_options);
if (!options.fontFamily) {
options.fontFamily = 'courier';
}
if (!options.sequence) {
options.sequence = {};
}
if (!options.sequence || (options.sequence && !options.sequence.actorFontFamily)) {
options.sequence.actorFontFamily = 'courier';
}
if (options.sequence && !options.sequence.noteFontFamily) {
options.sequence.noteFontFamily = 'courier';
}
options.sequence.actorFontFamily = 'courier';
options.sequence.noteFontFamily = 'courier';
options.sequence.messageFontFamily = 'courier';
if (options.sequence && !options.sequence.actorFontFamily) {
options.sequence.actorFontFamily = 'courier';
}
if (!options.fontSize) {
options.fontSize = '16px';
}
const useAppli = Cypress.env('useAppli');
cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
if (useAppli) {
cy.log('Opening eyes 2' + Cypress.spec.name);
cy.eyesOpen({
appName: 'Mermaid',
testName: name,
batchName: Cypress.spec.name,
batchId: batchId + Cypress.spec.name,
});
}
cy.visit(url);
if (validation) {
cy.get('svg').should(validation);
}
cy.get('body');
// Default name to test title
if (useAppli) {
cy.log('Check eyes 2' + Cypress.spec.name);
cy.eyesCheckWindow('Click!');
cy.log('Closing eyes 2' + Cypress.spec.name);
cy.eyesClose();
} else {
cy.matchImageSnapshot(name);
}
};
export const renderGraph = (graphStr, options, api) => {
const url = mermaidUrl(graphStr, options, api);
cy.visit(url);
};

View File

@ -2,8 +2,8 @@ import { urlSnapshotTest } from '../../helpers/util';
describe('mermaid', () => { describe('mermaid', () => {
describe('registerDiagram', () => { describe('registerDiagram', () => {
it('should work on @mermaid-js/mermaid-mindmap and mermaid-example-diagram', () => { it('should work on @mermaid-js/mermaid-example-diagram', () => {
const url = 'http://localhost:9000/external-diagrams-mindmap.html'; const url = 'http://localhost:9000/external-diagrams-example-diagram.html';
urlSnapshotTest(url, {}, false, false); urlSnapshotTest(url, {}, false, false);
}); });
}); });

View File

@ -0,0 +1,122 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
describe('C4 diagram', () => {
it('should render a simple C4Context diagram', () => {
imgSnapshotTest(
`
C4Context
accTitle: C4 context demo
accDescr: Many large C4 diagrams
title System Context diagram for Internet Banking System
Enterprise_Boundary(b0, "BankBoundary0") {
Person(customerA, "Banking Customer A", "A customer of the bank, with personal bank accounts.")
System(SystemAA, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments.")
Enterprise_Boundary(b1, "BankBoundary") {
System_Ext(SystemC, "E-mail system", "The internal Microsoft Exchange e-mail system.")
}
}
BiRel(customerA, SystemAA, "Uses")
Rel(SystemAA, SystemC, "Sends e-mails", "SMTP")
Rel(SystemC, customerA, "Sends e-mails to")
UpdateElementStyle(customerA, $fontColor="red", $bgColor="grey", $borderColor="red")
UpdateRelStyle(customerA, SystemAA, $textColor="blue", $lineColor="blue", $offsetX="5")
UpdateRelStyle(SystemC, customerA, $textColor="red", $lineColor="red", $offsetX="-50", $offsetY="20")
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Container diagram', () => {
imgSnapshotTest(
`
C4Container
title Container diagram for Internet Banking System
System_Ext(email_system, "E-Mail System", "The internal Microsoft Exchange system", $tags="v1.0")
Person(customer, Customer, "A customer of the bank, with personal bank accounts", $tags="v1.0")
Container_Boundary(c1, "Internet Banking") {
Container(spa, "Single-Page App", "JavaScript, Angular", "Provides all the Internet banking functionality to customers via their web browser")
}
Rel(customer, spa, "Uses", "HTTPS")
Rel(email_system, customer, "Sends e-mails to")
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Component diagram', () => {
imgSnapshotTest(
`
C4Component
title Component diagram for Internet Banking System - API Application
Container(spa, "Single Page Application", "javascript and angular", "Provides all the internet banking functionality to customers via their web browser.")
Container_Boundary(api, "API Application") {
Component(sign, "Sign In Controller", "MVC Rest Controller", "Allows users to sign in to the internet banking system")
}
Rel_Back(spa, sign, "Uses", "JSON/HTTPS")
UpdateRelStyle(spa, sign, $offsetY="-40")
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Dynamic diagram', () => {
imgSnapshotTest(
`
C4Dynamic
title Dynamic diagram for Internet Banking System - API Application
ContainerDb(c4, "Database", "Relational Database Schema", "Stores user registration information, hashed authentication credentials, access logs, etc.")
Container(c1, "Single-Page Application", "JavaScript and Angular", "Provides all of the Internet banking functionality to customers via their web browser.")
Container_Boundary(b, "API Application") {
Component(c3, "Security Component", "Spring Bean", "Provides functionality Related to signing in, changing passwords, etc.")
Component(c2, "Sign In Controller", "Spring MVC Rest Controller", "Allows users to sign in to the Internet Banking System.")
}
Rel(c1, c2, "Submits credentials to", "JSON/HTTPS")
Rel(c2, c3, "Calls isAuthenticated() on")
Rel(c3, c4, "select * from users where username = ?", "JDBC")
UpdateRelStyle(c1, c2, $textColor="red", $offsetY="-40")
UpdateRelStyle(c2, c3, $textColor="red", $offsetX="-40", $offsetY="60")
UpdateRelStyle(c3, c4, $textColor="red", $offsetY="-40", $offsetX="10")
`,
{}
);
cy.get('svg');
});
it('should render a simple C4Deployment diagram', () => {
imgSnapshotTest(
`
C4Deployment
title Deployment Diagram for Internet Banking System - Live
Deployment_Node(mob, "Customer's mobile device", "Apple IOS or Android"){
Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
}
Deployment_Node(plc, "Big Bank plc", "Big Bank plc data center"){
Deployment_Node(dn, "bigbank-api*** x8", "Ubuntu 16.04 LTS"){
Deployment_Node(apache, "Apache Tomcat", "Apache Tomcat 8.x"){
Container(api, "API Application", "Java and Spring MVC", "Provides Internet Banking functionality via a JSON/HTTPS API.")
}
}
}
Rel(mobile, api, "Makes API calls to", "json/HTTPS")
`,
{}
);
cy.get('svg');
});
});

View File

@ -182,6 +182,20 @@ describe('Entity Relationship Diagram', () => {
cy.get('svg'); cy.get('svg');
}); });
it('should render entities with length in attributes type', () => {
renderGraph(
`
erDiagram
CLUSTER {
varchar(99) name
string(255) description
}
`,
{ logLevel: 1 }
);
cy.get('svg');
});
it('should render entities and attributes with big and small entity names', () => { it('should render entities and attributes with big and small entity names', () => {
renderGraph( renderGraph(
` `

View File

@ -0,0 +1,687 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
describe('Flowchart ELK', () => {
it('1-elk: should render a simple flowchart', () => {
imgSnapshotTest(
`flowchart-elk TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{}
);
imgSnapshotTest(
`flowchart TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { defaultRenderer: 'elk' } }
);
});
it('2-elk: should render a simple flowchart with diagramPadding set to 0', () => {
imgSnapshotTest(
`flowchart-elk TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
%% this is a comment
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { diagramPadding: 0 } }
);
});
it('3-elk: a link with correct arrowhead to a subgraph', () => {
imgSnapshotTest(
`flowchart-elk TD
P1
P1 -->P1.5
subgraph P1.5
P2
P2.5(( A ))
P3
end
P2 --> P4
P3 --> P6
P1.5 --> P5
`,
{}
);
});
it('4-elk: Length of edges', () => {
imgSnapshotTest(
`flowchart-elk TD
L1 --- L2
L2 --- C
M1 ---> C
R1 .-> R2
R2 <.-> C
C -->|Label 1| E1
C <-- Label 2 ---> E2
C ----> E3
C <-...-> E4
C ======> E5
`,
{}
);
});
it('5-elk: should render escaped without html labels', () => {
imgSnapshotTest(
`flowchart-elk TD
a["<strong>Haiya</strong>"]---->b
`,
{ htmlLabels: false, flowchart: { htmlLabels: false } }
);
});
it('6-elk: should render non-escaped with html labels', () => {
imgSnapshotTest(
`flowchart-elk TD
a["<strong>Haiya</strong>"]===>b
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('7-elk: should render a flowchart when useMaxWidth is true (default)', () => {
renderGraph(
`flowchart-elk TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: true } }
);
cy.get('svg').should((svg) => {
expect(svg).to.have.attr('width', '100%');
// expect(svg).to.have.attr('height');
// use within because the absolute value can be slightly different depending on the environment ±5%
// const height = parseFloat(svg.attr('height'));
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
const style = svg.attr('style');
expect(style).to.match(/^max-width: [\d.]+px;$/);
const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
expect(maxWidthValue).to.be.within(230 * 0.95, 230 * 1.05);
});
});
it('8-elk: should render a flowchart when useMaxWidth is false', () => {
renderGraph(
`flowchart-elk TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
C -->|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
{ flowchart: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
// const height = parseFloat(svg.attr('height'));
const width = parseFloat(svg.attr('width'));
// use within because the absolute value can be slightly different depending on the environment ±5%
// expect(height).to.be.within(446 * 0.95, 446 * 1.05);
expect(width).to.be.within(230 * 0.95, 230 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
it('V2 elk - 16: Render Stadium shape', () => {
imgSnapshotTest(
` flowchart-elk TD
A([stadium shape test])
A -->|Get money| B([Go shopping])
B --> C([Let me think...<br />Do I want something for work,<br />something to spend every free second with,<br />or something to get around?])
C -->|One| D([Laptop])
C -->|Two| E([iPhone])
C -->|Three| F([Car<br/>wroom wroom])
click A "index.html#link-clicked" "link test"
click B testClick "click test"
classDef someclass fill:#f96;
class A someclass;
class C someclass;
`,
{ flowchart: { htmlLabels: false }, fontFamily: 'courier' }
);
});
it('50-elk: handle nested subgraphs in reverse order', () => {
imgSnapshotTest(
`flowchart-elk LR
a -->b
subgraph A
B
end
subgraph B
b
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('51-elk: handle nested subgraphs in reverse order', () => {
imgSnapshotTest(
`flowchart-elk LR
a -->b
subgraph A
B
end
subgraph B
b
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('52-elk: handle nested subgraphs in several levels', () => {
imgSnapshotTest(
`flowchart-elk TB
b-->B
a-->c
subgraph O
A
end
subgraph B
c
end
subgraph A
a
b
B
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('53-elk: handle nested subgraphs with edges in and out', () => {
imgSnapshotTest(
`flowchart-elk TB
internet
nat
routeur
lb1
lb2
compute1
compute2
subgraph project
routeur
nat
subgraph subnet1
compute1
lb1
end
subgraph subnet2
compute2
lb2
end
end
internet --> routeur
routeur --> subnet1 & subnet2
subnet1 & subnet2 --> nat --> internet
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('54-elk: handle nested subgraphs with outgoing links', () => {
imgSnapshotTest(
`flowchart-elk TD
subgraph main
subgraph subcontainer
subcontainer-child
end
subcontainer-child--> subcontainer-sibling
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('55-elk: handle nested subgraphs with outgoing links 2', () => {
imgSnapshotTest(
`flowchart-elk TD
subgraph one[One]
subgraph sub_one[Sub One]
_sub_one
end
subgraph sub_two[Sub Two]
_sub_two
end
_one
end
%% here, either the first or the second one
sub_one --> sub_two
_one --> b
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('56-elk: handle nested subgraphs with outgoing links 3', () => {
imgSnapshotTest(
`flowchart-elk TB
subgraph container_Beta
process_C-->Process_D
end
subgraph container_Alpha
process_A-->process_B
process_A-->|messages|process_C
end
process_B-->|via_AWSBatch|container_Beta
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('57-elk: handle nested subgraphs with outgoing links 4', () => {
imgSnapshotTest(
`flowchart-elk LR
subgraph A
a -->b
end
subgraph B
b
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('57-elk: handle nested subgraphs with outgoing links 2', () => {
imgSnapshotTest(
`flowchart-elk TB
c1-->a2
subgraph one
a1-->a2
end
subgraph two
b1-->b2
end
subgraph three
c1-->c2
end
one --> two
three --> two
two --> c2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('57.x: handle nested subgraphs with outgoing links 5', () => {
imgSnapshotTest(
`%% this does not produce the desired result
flowchart-elk TB
subgraph container_Beta
process_C-->Process_D
end
subgraph container_Alpha
process_A-->process_B
process_B-->|via_AWSBatch|container_Beta
process_A-->|messages|process_C
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('58-elk: handle styling with style expressions', () => {
imgSnapshotTest(
`
flowchart-elk 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
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('59-elk: handle styling of subgraphs and links', () => {
imgSnapshotTest(
`
flowchart-elk TD
A[Christmas] ==> D
A[Christmas] -->|Get money| B(Go shopping)
A[Christmas] ==> C
subgraph T ["Test"]
A
B
C
end
classDef Test fill:#F84E68,stroke:#333,color:white;
class A,T Test
classDef TestSub fill:green;
class T TestSub
linkStyle 0,1 color:orange, stroke: orange;
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('60-elk: handle styling for all node shapes - v2', () => {
imgSnapshotTest(
`
flowchart-elk LR
A[red text] -->|default style| B(blue text)
C([red text]) -->|default style| D[[blue text]]
E[(red text)] -->|default style| F((blue text))
G>red text] -->|default style| H{blue text}
I{{red text}} -->|default style| J[/blue text/]
K[\\ red text\\] -->|default style| L[/blue text\\]
M[\\ red text/] -->|default style| N[blue text];
O(((red text))) -->|default style| P(((blue text)));
linkStyle default color:Sienna;
style A stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style B stroke:#0000ff,fill:#ccccff,color:#0000ff;
style C stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style D stroke:#0000ff,fill:#ccccff,color:#0000ff;
style E stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style F stroke:#0000ff,fill:#ccccff,color:#0000ff;
style G stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style H stroke:#0000ff,fill:#ccccff,color:#0000ff;
style I stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style J stroke:#0000ff,fill:#ccccff,color:#0000ff;
style K stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style L stroke:#0000ff,fill:#ccccff,color:#0000ff;
style M stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style N stroke:#0000ff,fill:#ccccff,color:#0000ff;
style O stroke:#ff0000,fill:#ffcccc,color:#ff0000;
style P stroke:#0000ff,fill:#ccccff,color:#0000ff;
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose', logLevel: 2 }
);
});
it('61-elk: fontawesome icons in edge labels', () => {
imgSnapshotTest(
`
flowchart-elk TD
C -->|fa:fa-car Car| F[fa:fa-car Car]
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('62-elk: should render styled subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk TB
A
B
subgraph foo[Foo SubGraph]
C
D
end
subgraph bar[Bar SubGraph]
E
F
end
G
A-->B
B-->C
C-->D
B-->D
D-->E
E-->A
E-->F
F-->D
F-->G
B-->G
G-->D
style foo fill:#F99,stroke-width:2px,stroke:#F0F,color:darkred
style bar fill:#999,stroke-width:10px,stroke:#0F0,color:blue
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('63-elk: title on subgraphs should be themable', () => {
imgSnapshotTest(
`
%%{init:{"theme":"base", "themeVariables": {"primaryColor":"#411d4e", "titleColor":"white", "darkMode":true}}}%%
flowchart-elk LR
subgraph A
a --> b
end
subgraph B
i -->f
end
A --> B
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('65-elk: text-color from classes', () => {
imgSnapshotTest(
`
flowchart-elk LR
classDef dark fill:#000,stroke:#000,stroke-width:4px,color:#fff
Lorem --> Ipsum --> Dolor
class Lorem,Dolor dark
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('66-elk: More nested subgraph cases (TB)', () => {
imgSnapshotTest(
`
flowchart-elk TB
subgraph two
b1
end
subgraph three
c2
end
three --> two
two --> c2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('67-elk: More nested subgraph cases (RL)', () => {
imgSnapshotTest(
`
flowchart-elk RL
subgraph two
b1
end
subgraph three
c2
end
three --> two
two --> c2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('68-elk: More nested subgraph cases (BT)', () => {
imgSnapshotTest(
`
flowchart-elk BT
subgraph two
b1
end
subgraph three
c2
end
three --> two
two --> c2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('69-elk: More nested subgraph cases (LR)', () => {
imgSnapshotTest(
`
flowchart-elk LR
subgraph two
b1
end
subgraph three
c2
end
three --> two
two --> c2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('70-elk: Handle nested subgraph cases (TB) link out and link between subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk TB
subgraph S1
sub1 -->sub2
end
subgraph S2
sub4
end
S1 --> S2
sub1 --> sub4
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('71-elk: Handle nested subgraph cases (RL) link out and link between subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk RL
subgraph S1
sub1 -->sub2
end
subgraph S2
sub4
end
S1 --> S2
sub1 --> sub4
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('72-elk: Handle nested subgraph cases (BT) link out and link between subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk BT
subgraph S1
sub1 -->sub2
end
subgraph S2
sub4
end
S1 --> S2
sub1 --> sub4
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('74-elk: Handle nested subgraph cases (RL) link out and link between subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk RL
subgraph S1
sub1 -->sub2
end
subgraph S2
sub4
end
S1 --> S2
sub1 --> sub4
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('74-elk: Handle labels for multiple edges from and to the same couple of nodes', () => {
imgSnapshotTest(
`
flowchart-elk RL
subgraph one
a1 -- l1 --> a2
a1 -- l2 --> a2
end
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('76-elk: handle unicode encoded character with HTML labels true', () => {
imgSnapshotTest(
`flowchart-elk TB
a{{"Lorem 'ipsum' dolor 'sit' amet, 'consectetur' adipiscing 'elit'."}}
--> b{{"Lorem #quot;ipsum#quot; dolor #quot;sit#quot; amet,#quot;consectetur#quot; adipiscing #quot;elit#quot;."}}
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('2050-elk: handling of different rendering direction in subgraphs', () => {
imgSnapshotTest(
`
flowchart-elk LR
subgraph TOP
direction TB
subgraph B1
direction RL
i1 -->f1
end
subgraph B2
direction BT
i2 -->f2
end
end
A --> TOP --> B
B1 --> B2
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('2388-elk: handling default in the node name', () => {
imgSnapshotTest(
`
flowchart-elk LR
default-index.js --> dot.template.js
index.js --> module-utl.js
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('2824-elk: Clipping of edges', () => {
imgSnapshotTest(
`
flowchart-elk TD
A --> B
A --> C
B --> C
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('1433-elk: should render a titled flowchart with titleTopMargin set to 0', () => {
imgSnapshotTest(
`---
title: Simple flowchart
---
flowchart-elk TD
A --> B
`,
{ titleTopMargin: 0 }
);
});
});

View File

@ -1,4 +1,4 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js'; import { imgSnapshotTest } from '../../helpers/util.js';
/** /**
* Check whether the SVG Element has a Mindmap root * Check whether the SVG Element has a Mindmap root
@ -158,7 +158,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
it('rounded rect shape', () => { it('rounded rect shape', () => {
imgSnapshotTest( imgSnapshotTest(
@ -172,7 +171,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
it('circle shape', () => { it('circle shape', () => {
imgSnapshotTest( imgSnapshotTest(
@ -186,7 +184,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
it('default shape', () => { it('default shape', () => {
imgSnapshotTest( imgSnapshotTest(
@ -198,7 +195,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
it('adding children', () => { it('adding children', () => {
imgSnapshotTest( imgSnapshotTest(
@ -212,7 +208,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
it('adding grand children', () => { it('adding grand children', () => {
imgSnapshotTest( imgSnapshotTest(
@ -227,7 +222,6 @@ mindmap
undefined, undefined,
shouldHaveRoot shouldHaveRoot
); );
cy.get('svg');
}); });
/* The end */ /* The end */
}); });

View File

@ -3,6 +3,42 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util'; import { imgSnapshotTest, renderGraph } from '../../helpers/util';
context('Sequence diagram', () => { context('Sequence diagram', () => {
it('should render a sequence diagram with boxes', () => {
renderGraph(
`
sequenceDiagram
box LightGrey Alice and Bob
participant Alice
participant Bob
end
participant John as John<br/>Second Line
Alice ->> Bob: Hello Bob, how are you?
Bob-->>John: How about you John?
Bob--x Alice: I am good thanks!
Bob-x John: I am good thanks!
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
Bob-->Alice: Checking with John...
alt either this
Alice->>John: Yes
else or this
Alice->>John: No
else or this will happen
Alice->John: Maybe
end
par this happens in parallel
Alice -->> Bob: Parallel message 1
and
Alice -->> John: Parallel message 2
end
`,
{ sequence: { useMaxWidth: false } }
);
cy.get('svg').should((svg) => {
const width = parseFloat(svg.attr('width'));
expect(width).to.be.within(830 * 0.95, 830 * 1.05);
expect(svg).to.not.have.attr('style');
});
});
it('should render a simple sequence diagram', () => { it('should render a simple sequence diagram', () => {
imgSnapshotTest( imgSnapshotTest(
` `

View File

@ -0,0 +1,164 @@
import { imgSnapshotTest } from '../../helpers/util.js';
describe('Timeline diagram', () => {
it('1: should render a simple timeline with no specific sections', () => {
imgSnapshotTest(
`timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
`,
{}
);
});
it('2: should render a timeline diagram with sections', () => {
imgSnapshotTest(
`timeline
title Timeline of Industrial Revolution
section 17th-20th century
Industry 1.0 : Machinery, Water power, Steam <br>power
Industry 2.0 : Electricity, Internal combustion engine, Mass production
Industry 3.0 : Electronics, Computers, Automation
section 21st century
Industry 4.0 : Internet, Robotics, Internet of Things
Industry 5.0 : Artificial intelligence, Big data,3D printing
`,
{}
);
});
it('3: should render a complex timeline with sections, and long events text with <br>', () => {
imgSnapshotTest(
`timeline
title England's History Timeline
section Stone Age
7600 BC : Britain's oldest known house was built in Orkney, Scotland
6000 BC : Sea levels rise and Britain becomes an island.<br> The people who live here are hunter-gatherers.
section Broze Age
2300 BC : People arrive from Europe and settle in Britain. <br>They bring farming and metalworking.
: New styles of pottery and ways of burying the dead appear.
2200 BC : The last major building works are completed at Stonehenge.<br> People now bury their dead in stone circles.
: The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
`,
{}
);
});
it('4: should render a simple timeline with directives and disableMultiColor:true ', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'base', 'timeline': {'disableMulticolor': true}}}%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
`,
{}
);
});
it('5: should render a simple timeline with directive overriden colors', () => {
imgSnapshotTest(
` %%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
'cScale0': '#ff0000',
'cScale1': '#00ff00',
'cScale2': '#0000ff'
} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
it('6: should render a simple timeline in base theme', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
it('7: should render a simple timeline in default theme', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'default' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
it('8: should render a simple timeline in dark theme', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
it('9: should render a simple timeline in neutral theme', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'neutral' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
it('10: should render a simple timeline in forest theme', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
`,
{}
);
});
});

View File

@ -0,0 +1,231 @@
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
background-color: #eee;
background-image: radial-gradient(#fff 1%, transparent 11%),
radial-gradient(#fff 1%, transparent 11%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
background-repeat: repeat;
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
</style>
</head>
<body>
<div>Security check</div>
<pre id="diagram" class="mermaid2">
timeline
title My day
section section with no tasks
section Go to work at the dog office
1930 : first step : second step is a long step
: third step
1940 : fourth step : fifth step
section Go home
1950 : India got independent and already won war against Pakistan
1960 : India fights poverty, looses war to China and gets nuclear weapons from USA and USSR
1970 : Green Revolution comes to india
section Another section with no tasks
I am a big big big tasks
I am not so big tasks
</pre>
<pre id="diagram" class="mermaid">
timeline
title MermaidChart 2023 Timeline
section 2023 Q1 <br> Release Personal Tier
Buttet 1 : sub-point 1a : sub-point 1b
: sub-point 1c
Bullet 2 : sub-point 2a : sub-point 2b
section 2023 Q2 <br> Release XYZ Tier
Buttet 3 : sub-point <br> 3a : sub-point 3b
: sub-point 3c
Bullet 4 : sub-point 4a : sub-point 4b
</pre>
<pre id="diagram" class="mermaid">
timeline
title England's History Timeline
section Stone Age
7600 BC : Britain's oldest known house was built in Orkney, Scotland
6000 BC : Sea levels rise and Britain becomes an island. The people who live here are hunter-gatherers.
section Broze Age
2300 BC : People arrive from Europe and settle in Britain. They bring farming and metalworking.
: New styles of pottery and ways of burying the dead appear.
2200 BC : The last major building works are completed at Stonehenge. People now bury their dead in stone circles.
: The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
</pre>
<pre id="diagram" class="mermaid2">
%%{'init': { 'logLevel': 'debug', 'theme': 'default', 'timeline': {'disableMulticolor':false} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google : Pixar
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008s : Instagram
2010 : Pinterest
</pre>
<pre id="diagram" class="mermaid2">
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'themeVariables': {
'cScale0': '#ff0000',
'cScale1': '#00ff00',
'cScale2': '#ff0000'
} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google : Pixar
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008s : Instagram
2010 : Pinterest
</pre>
<pre id="diagram" class="mermaid2">
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
'cScale0': '#ff0000',
'cScale1': '#00ff00',
'cScale2': '#0000ff'
} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
</pre>
<pre id="diagram" class="mermaid2">
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008s : Instagram
2010 : Pinterest
</pre>
<pre id="diagram" class="mermaid2">
mindmap
root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
gc7((grand<br/>grand<br/>child 8))
</pre>
<pre id="diagram" class="mermaid2">
flowchart-elk TB
a --> b
a --> c
b --> d
c --> d
</pre>
<!-- <div id="cy"></div> -->
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
<!-- <script src="./mermaid-example-diagram-detector.js"></script> -->
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
<script type="module">
//import mindmap from '../../packages/mermaid-mindmap/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector';
// import timeline from '../../packages/mermaid-timeline/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid';
// await mermaid.registerExternalDiagrams([]);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
theme: 'base',
startOnLoad: true,
logLevel: 0,
flowchart: {
useMaxWidth: false,
htmlLabels: true,
},
gantt: {
useMaxWidth: false,
},
timeline: {
disableMulticolor: false,
htmlLabels: false,
},
useMaxWidth: true,
lazyLoadedDiagrams: [
// './mermaid-mindmap-detector.esm.mjs',
// './mermaid-example-diagram-detector.esm.mjs',
//'./mermaid-timeline-detector.esm.mjs',
],
});
function callback() {
alert('It worked');
}
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
</script>
</body>
</html>

View File

@ -46,13 +46,9 @@
<pre class="mermaid" style="width: 100%; height: 20%"> <pre class="mermaid" style="width: 100%; height: 20%">
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%% %%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%
classDiagram-v2 classDiagram-v2
class BankAccount{ classA <|-- classB : implements
+String owner classC *-- classD : composition
+BigDecimal balance classE o-- classF : aggregation
+deposit(amount) bool
+withdrawl(amount) int
}
cssClass "BankAccount" customCss
</pre> </pre>
<pre class="mermaid2" style="width: 100%; height: 20%"> <pre class="mermaid2" style="width: 100%; height: 20%">
%%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%% %%{init: {'theme': 'base', 'fontFamily': 'courier', 'themeVariables': { 'primaryColor': '#fff000'}}}%%

View File

@ -2,34 +2,8 @@
<body> <body>
<h1>Should correctly load a third-party diagram using registerDiagram</h1> <h1>Should correctly load a third-party diagram using registerDiagram</h1>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
mindmap example-diagram
root
A
B
C
D
E
A2
B2
C2
D2
E2
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
gc7((grand<br/>grand<br/>child 8))
</pre> </pre>
<!-- <pre id="diagram" class="mermaid2">
example-diagram
</pre> -->
<!-- <div id="cy"></div> --> <!-- <div id="cy"></div> -->
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> --> <!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
@ -37,13 +11,16 @@ mindmap
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> --> <!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
<!-- <script type="module" src="./external-diagrams-mindmap.mjs" /> --> <!-- <script type="module" src="./external-diagrams-mindmap.mjs" /> -->
<script type="module"> <script type="module">
import mindmap from '../../packages/mermaid-mindmap/src/detector'; import exampleDiagram from '../../packages/mermaid-example-diagram/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector'; // import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid'; import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([mindmap]); await mermaid.registerExternalDiagrams([exampleDiagram]);
await mermaid.initialize({ logLevel: 0 }); await mermaid.initialize({ logLevel: 0 });
await mermaid.initThrowsErrorsAsync(); await mermaid.initThrowsErrorsAsync();
if (window.Cypress) {
window.rendered = true;
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -21,6 +21,9 @@
const diagram = document.getElementById('diagram'); const diagram = document.getElementById('diagram');
const svg = mermaid.render('diagram-svg', graph); const svg = mermaid.render('diagram-svg', graph);
diagram.innerHTML = svg; diagram.innerHTML = svg;
if (window.Cypress) {
window.rendered = true;
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -21,6 +21,9 @@
const diagram = document.getElementById('diagram'); const diagram = document.getElementById('diagram');
const svg = mermaid.render('diagram-svg', graph); const svg = mermaid.render('diagram-svg', graph);
diagram.innerHTML = svg; diagram.innerHTML = svg;
if (window.Cypress) {
window.rendered = true;
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -94,6 +94,9 @@
// document.querySelector('#diagram').innerHTML = diagram; // document.querySelector('#diagram').innerHTML = diagram;
mermaid.render('diagram', diagram, (res) => { mermaid.render('diagram', diagram, (res) => {
document.querySelector('#res').innerHTML = res; document.querySelector('#res').innerHTML = res;
if (window.Cypress) {
window.rendered = true;
}
}); });
</script> </script>
</body> </body>

View File

@ -54,39 +54,205 @@
</style> </style>
</head> </head>
<body> <body>
<div>Security check</div>
<pre id="diagram" class="mermaid"> <pre id="diagram" class="mermaid">
flowchart LR %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
%% Actors graph BT
A a{The cat in the hat} -- 1o --> b
subgraph Sub a -- 2o --> c
B --> C a -- 3o --> d
end g --2i--> a
d --1i--> a
%% Accusations h --3i -->a
A --L --> Sub b --> d(The dog in the hog)
c --> d
%% Offense
B --> A
</pre>
<pre id="diagram" class="mermaid">
stateDiagram-v2
[*] --> S1
S1 --> S2: long line using<br/>should work
S1 --> S3: long line using <br>should work
S1 --> S4: long line using \\nshould work
</pre> </pre>
<pre id="diagram" class="mermaid2"> <pre id="diagram" class="mermaid2">
gantt flowchart-elk TB
title Style today marker (vertical line should be 5px wide and half-transparent blue) a --> b
dateFormat YYYY-MM-DD a --> c
axisFormat %d b --> d
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5 c --> d
section Section1 </pre>
Today: 1, -1h <pre id="diagram" class="mermaid2">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
<pre id="diagram" class="mermaid2">
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
&nbsp;
<pre id="diagram" class="mermaid2">
flowchart LR
B1 --be be--x B2
B1 --bo bo--o B3
subgraph Ugge
B2
B3
subgraph inner
B4
B5
end
subgraph inner2
subgraph deeper
C4
C5
end
C6
end
B4 --> C4
B3 -- X --> B4
B2 --> inner
C4 --> C5
end
subgraph outer
B6
end
B6 --> B5
</pre
>
<pre id="diagram" class="mermaid2">
sequenceDiagram
Customer->>+Stripe: Makes a payment request
Stripe->>+Bank: Forwards the payment request to the bank
Bank->>+Customer: Asks for authorization
Customer->>+Bank: Provides authorization
Bank->>+Stripe: Sends a response with payment details
Stripe->>+Merchant: Sends a notification of payment receipt
Merchant->>+Stripe: Confirms the payment
Stripe->>+Customer: Sends a confirmation of payment
Customer->>+Merchant: Receives goods or services
</pre
>
<pre id="diagram" class="mermaid2">
mindmap
root((mindmap))
Origins
Long history
::icon(fa fa-book)
Popularisation
British popular psychology author Tony Buzan
Research
On effectiveness<br/>and features
On Automatic creation
Uses
Creative techniques
Strategic planning
Argument mapping
Tools
Pen and paper
Mermaid
</pre>
<br />
<pre id="diagram" class="mermaid2">
example-diagram
</pre> </pre>
<!-- <div id="cy"></div> --> <!-- <div id="cy"></div> -->
@ -96,18 +262,19 @@ flowchart LR
<!-- <script src="./mermaid.js"></script> --> <!-- <script src="./mermaid.js"></script> -->
<script type="module"> <script type="module">
import mindmap from '../../packages/mermaid-mindmap/src/detector'; // import mindmap from '../../packages/mermaid-mindmap/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector'; import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid'; import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([mindmap]); await mermaid.registerExternalDiagrams([example]);
mermaid.parseError = function (err, hash) { mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err); // console.error('Mermaid error: ', err);
}; };
mermaid.initialize({ mermaid.initialize({
theme: 'default', theme: 'forest',
startOnLoad: true, startOnLoad: true,
logLevel: 0, logLevel: 5,
flowchart: { flowchart: {
// defaultRenderer: 'elk',
useMaxWidth: false, useMaxWidth: false,
htmlLabels: true, htmlLabels: true,
}, },

View File

@ -1,81 +1,93 @@
<html> <!DOCTYPE html>
<html lang="en">
<head> <head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" /> <meta charset="utf-8" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link <title>Mindmap Mermaid Quick Test Page</title>
rel="stylesheet" <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style> <style>
body { div.mermaid {
/* background: rgb(221, 208, 208); */ /* font-family: 'trebuchet ms', verdana, arial; */
/* background:#333; */ font-family: 'Courier New', Courier, monospace !important;
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
background-color: #eee;
background-image: radial-gradient(#fff 1%, transparent 11%),
radial-gradient(#fff 1%, transparent 11%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
background-repeat: repeat;
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
} }
</style> </style>
</head> </head>
<body> <body>
<div>Security check</div> <h1>Mindmap diagram demo</h1>
<pre id="diagram" class="mermaid"> <pre class="mermaid">
graph TD mindmap
A["test"] --"<p><style> * { display : none}</style>test</p>"--> B root
child1((Circle))
grandchild 1
grandchild 2
child2(Round rectangle)
grandchild 3
grandchild 4
child3[Square]
grandchild 5
::icon(mdi mdi-fire)
gc6((grand<br/>child 6))
::icon(mdi mdi-fire)
gc7((grand<br/>grand<br/>child 8))
</pre> </pre>
<!-- <div id="cy"></div> --> <h2>Mindmap with root wrapping text and a shape</h2>
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> --> <pre class="mermaid">
<!-- <script src="./mermaid-example-diagram-detector.js"></script> --> mindmap
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> --> root[A root with a long text that wraps to keep the node size in check]
<!-- <script src="./mermaid.js"></script> --> </pre>
<script type="module"> <script type="module">
import mindmap from '../../packages/mermaid-mindmap/src/detector'; // import mermaid from './mermaid.esm.mjs';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid'; import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([mindmap]); // import mermaidMindmap from './mermaid-mindmap.esm.mjs';
// import mermaidMindmap from 'https://cdn.jsdelivr.net/npm/@mermaid-js/mermaid-mindmap@9.3.0/+esm';
// await mermaid.registerExternalDiagrams([mermaidMindmap]);
const ALLOWED_TAGS = [
'a',
'b',
'blockquote',
'br',
'dd',
'div',
'dl',
'dt',
'em',
'foreignObject',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'h7',
'h8',
'hr',
'i',
'li',
'ul',
'ol',
'p',
'pre',
'span',
'strike',
'strong',
'table',
'tbody',
'td',
'tfoot',
'th',
'thead',
'tr',
];
mermaid.parseError = function (err, hash) { mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err); // console.error('Mermaid error: ', err);
}; };
mermaid.initialize({ mermaid.initialize({
theme: 'default', theme: 'base',
startOnLoad: true, startOnLoad: true,
logLevel: 0, logLevel: 0,
flowchart: { flowchart: {
@ -94,10 +106,6 @@
console.error('In parse error:'); console.error('In parse error:');
console.error(err); console.error(err);
}; };
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,14 +1,283 @@
<html> <html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat&display=swap" rel="stylesheet" />
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<link
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<style>
body {
/* background: rgb(221, 208, 208); */
/* background:#333; */
font-family: 'Arial';
/* font-size: 18px !important; */
}
h1 {
color: grey;
}
.mermaid2 {
display: none;
}
.mermaid svg {
/* font-size: 18px !important; */
background-color: #eee;
background-image: radial-gradient(#fff 1%, transparent 11%),
radial-gradient(#fff 1%, transparent 11%);
background-size: 20px 20px;
background-position: 0 0, 10px 10px;
background-repeat: repeat;
}
.malware {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 150px;
background: red;
color: black;
display: flex;
display: flex;
justify-content: center;
align-items: center;
font-family: monospace;
font-size: 72px;
}
</style>
</head>
<body> <body>
<pre class="mermaid"> <pre id="diagram" class="mermaid">
none %%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
hello world graph TB
a --> b
a --> c
b --> d
c --> d
</pre> </pre>
<script src="./mermaid.js"></script> <pre id="diagram" class="mermaid">
<script> flowchart-elk LR
subgraph A
a -->b
end
subgraph B
b
end
</pre>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
<pre id="diagram" class="mermaid">
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
core0[IBM PowerPC Broadway Core 0]
core1[IBM PowerPC Broadway Core 1]
core2[IBM PowerPC Broadway Core 2]
rom[16 KB ROM]
core0 --- core2
rom --> core2
end
subgraph amd[AMD Latte GPU]
mem[Memory & I/O Bridge]
dram[DRAM Controller]
edram[32 MB EDRAM MEM1]
rom[512 B SEEPROM]
sata[SATA IF]
exi[EXI]
subgraph gx[GX]
sram[3 MB 1T-SRAM]
end
radeon[AMD Radeon R7xx GX2]
mem --- gx
mem --- radeon
rom --- mem
mem --- sata
mem --- exi
dram --- sata
dram --- exi
end
ddr3[2 GB DDR3 RAM MEM2]
mem --- ddr3
dram --- ddr3
edram --- ddr3
core1 --- mem
exi --- rtc
rtc{{rtc}}
</pre
>
<br />
&nbsp;
<pre id="diagram" class="mermaid2">
flowchart LR
B1 --be be--x B2
B1 --bo bo--o B3
subgraph Ugge
B2
B3
subgraph inner
B4
B5
end
subgraph inner2
subgraph deeper
C4
C5
end
C6
end
B4 --> C4
B3 -- X --> B4
B2 --> inner
C4 --> C5
end
subgraph outer
B6
end
B6 --> B5
</pre
>
<pre id="diagram" class="mermaid2">
sequenceDiagram
Customer->>+Stripe: Makes a payment request
Stripe->>+Bank: Forwards the payment request to the bank
Bank->>+Customer: Asks for authorization
Customer->>+Bank: Provides authorization
Bank->>+Stripe: Sends a response with payment details
Stripe->>+Merchant: Sends a notification of payment receipt
Merchant->>+Stripe: Confirms the payment
Stripe->>+Customer: Sends a confirmation of payment
Customer->>+Merchant: Receives goods or services
</pre
>
<pre id="diagram" class="mermaid2">
gantt
title Style today marker (vertical line should be 5px wide and half-transparent blue)
dateFormat YYYY-MM-DD
axisFormat %d
todayMarker stroke-width:5px,stroke:#00f,opacity:0.5
section Section1
Today: 1, -1h
</pre>
<!-- <div id="cy"></div> -->
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
<!-- <script src="./mermaid-example-diagram-detector.js"></script> -->
<!-- <script src="//cdn.jsdelivr.net/npm/mermaid@9.1.7/dist/mermaid.min.js"></script> -->
<!-- <script src="./mermaid.js"></script> -->
<script type="module">
import mindmap from '../../packages/mermaid-mindmap/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([mindmap]);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({ mermaid.initialize({
logLevel: 1, // theme: 'forest',
startOnLoad: true,
logLevel: 0,
flowchart: {
// defaultRenderer: 'elk',
useMaxWidth: false,
htmlLabels: true,
},
gantt: {
useMaxWidth: false,
},
useMaxWidth: false,
}); });
function callback() {
alert('It worked');
}
mermaid.parseError = function (err, hash) {
console.error('In parse error:');
console.error(err);
};
// mermaid.test1('first_slow', 1200).then((r) => console.info(r));
// mermaid.test1('second_fast', 200).then((r) => console.info(r));
// mermaid.test1('third_fast', 200).then((r) => console.info(r));
// mermaid.test1('forth_slow', 1200).then((r) => console.info(r));
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,10 +1,17 @@
import mermaid2 from '../../packages/mermaid/src/mermaid'; import mermaid2 from '../../packages/mermaid/src/mermaid';
import mindmap from '../../packages/mermaid-mindmap/src/detector'; import externalExample from '../../packages/mermaid-example-diagram/src/detector';
function b64ToUtf8(str) { function b64ToUtf8(str) {
return decodeURIComponent(escape(window.atob(str))); return decodeURIComponent(escape(window.atob(str)));
} }
// Adds a rendered flag to window when rendering is done, so cypress can wait for it.
function markRendered() {
if (window.Cypress) {
window.rendered = true;
}
}
/** /**
* ##contentLoaded Callback function that is called when page is loaded. This functions fetches * ##contentLoaded Callback function that is called when page is loaded. This functions fetches
* configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the * configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the
@ -37,9 +44,10 @@ const contentLoaded = async function () {
document.getElementsByTagName('body')[0].appendChild(div); document.getElementsByTagName('body')[0].appendChild(div);
} }
await mermaid2.registerExternalDiagrams([mindmap]); await mermaid2.registerExternalDiagrams([externalExample]);
mermaid2.initialize(graphObj.mermaid); mermaid2.initialize(graphObj.mermaid);
mermaid2.init(); await mermaid2.init();
markRendered();
} }
}; };
@ -128,6 +136,7 @@ const contentLoadedApi = function () {
); );
} }
} }
markRendered();
}; };
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
@ -142,7 +151,7 @@ if (typeof document !== 'undefined') {
contentLoadedApi(); contentLoadedApi();
} else { } else {
this.console.log('Not using api'); this.console.log('Not using api');
contentLoaded(); void contentLoaded();
} }
}, },
false false

View File

@ -17,10 +17,10 @@
<h1>Class diagram demos</h1> <h1>Class diagram demos</h1>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: Demo Class Diagram title: Demo Class Diagram
--- ---
classDiagram classDiagram
accTitle: Demo Class Diagram accTitle: Demo Class Diagram
accDescr: This class diagram show the abstract Animal class, and 3 classes that inherit from it: Duck, Fish, and Zebra. accDescr: This class diagram show the abstract Animal class, and 3 classes that inherit from it: Duck, Fish, and Zebra.
@ -138,7 +138,20 @@ title: Demo Class Diagram
Pineapple : -int leafCount() Pineapple : -int leafCount()
Pineapple : -int spikeCount() Pineapple : -int spikeCount()
</pre> </pre>
<hr />
<pre class="mermaid">
classDiagram
class Person {
+Id : Guid
+FirstName : string
+LastName : string
-privateProperty : string
#ProtectedProperty : string
~InternalProperty : string
~AnotherInternalProperty : List~List~string~~
}
</pre>
<hr /> <hr />
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>

View File

@ -19,43 +19,94 @@
<body> <body>
<pre class="mermaid"> <pre class="mermaid">
---
title: This is a title
---
erDiagram
%% title This is a title
%% accDescription Test a description
--- "Person . CUSTOMER"||--o{ ORDER : places
title: This is a title
---
erDiagram
%% title This is a title
%% accDescription Test a description
"Person . CUSTOMER"||--o{ ORDER : places ORDER ||--|{ "€£LINE_ITEM ¥" : contains
ORDER ||--|{ "€£LINE_ITEM ¥" : contains "Person . CUSTOMER" }|..|{ "Address//StreetAddress::[DELIVERY ADDRESS]" : uses
"Person . CUSTOMER" }|..|{ "Address//StreetAddress::[DELIVERY ADDRESS]" : uses "Address//StreetAddress::[DELIVERY ADDRESS]" {
int customerID FK
string line1 "this is the first address line comment"
string line2
string city
string region
string state
string(5) postal_code
string country
}
"Address//StreetAddress::[DELIVERY ADDRESS]" { "a_~`!@#$^&*()-_=+[]{}|/;:'.?¡⁄™€£‹¢›∞fi§‡•°ª·º‚≠±œŒ∑„®†ˇ¥Á¨ˆˆØπ∏“«»åÅßÍ∂΃ϩ˙Ó∆Ô˚¬Ò…ÚæÆΩ¸≈π˛çÇ√◊∫ı˜µÂ≤¯≥˘÷¿" {
int customerID FK string name "this is an entity with an absurd name just to show characters that are now acceptable as long as the name is in double quotes"
string line1 "this is the first address line comment" }
string line2
string city "€£LINE_ITEM ¥" {
string region int orderID FK
string state int currencyId FK
string postal_code number price
string country number quantity
number adjustment
number final_price
}
</pre>
<hr />
<pre class="mermaid">
erDiagram
"HOSPITAL" {
int id PK
int doctor_id FK
string address UK
string name
string phone_number
string fax_number
} }
</pre>
<hr />
"a_~`!@#$^&*()-_=+[]{}|/;:'.?¡⁄™€£‹¢›∞fi§‡•°ª·º‚≠±œŒ∑„®†ˇ¥Á¨ˆˆØπ∏“«»åÅßÍ∂΃ϩ˙Ó∆Ô˚¬Ò…ÚæÆΩ¸≈π˛çÇ√◊∫ı˜µÂ≤¯≥˘÷¿" { <pre class="mermaid">
string name "this is an entity with an absurd name just to show characters that are now acceptable as long as the name is in double quotes" erDiagram
} "HOSPITAL" {
int id PK
int doctor_id PK, FK
string address UK
string name
string phone_number
string fax_number
}
</pre
>
<hr />
"€£LINE_ITEM ¥" { <pre class="mermaid">
int orderID FK erDiagram
int currencyId FK CAR ||--o{ NAMED-DRIVER : allows
number price CAR {
number quantity string registrationNumber PK
number adjustment string make
number final_price string model
} string[] parts
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string driversLicense PK "The license #"
string(99) firstName "Only 99 characters are allowed"
string lastName
string phone UK
int age
}
NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK,FK
}
MANUFACTURER only one to zero or more CAR : makes
</pre> </pre>
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>

View File

@ -17,9 +17,9 @@
<h2>Sample 1</h2> <h2>Sample 1</h2>
<h3>graph</h3> <h3>graph</h3>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: This is a complicated flow title: This is a complicated flow
--- ---
graph LR graph LR
accTitle: This is a complicated flow accTitle: This is a complicated flow
accDescr: This is the descriptoin for the complicated flow. accDescr: This is the descriptoin for the complicated flow.
@ -224,9 +224,9 @@ title: This is a complicated flow
<h2>Sample 2</h2> <h2>Sample 2</h2>
<h3>graph</h3> <h3>graph</h3>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: What to buy title: What to buy
--- ---
graph TD graph TD
accTitle: What to buy accTitle: What to buy
accDescr: Options of what to buy with Christmas money accDescr: Options of what to buy with Christmas money

View File

@ -16,9 +16,9 @@
<body> <body>
<h1>Git diagram demo</h1> <h1>Git diagram demo</h1>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: Simple Git diagram title: Simple Git diagram
--- ---
gitGraph: gitGraph:
options options
{ {

View File

@ -16,9 +16,9 @@
<body> <body>
<h1>Journey diagram demo</h1> <h1>Journey diagram demo</h1>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: My working day title: My working day
--- ---
journey journey
accTitle: Very simple journey demo accTitle: Very simple journey demo
accDescr: 2 main sections: work and home, each with just a few tasks accDescr: 2 main sections: work and home, each with just a few tasks

View File

@ -128,6 +128,22 @@
</pre> </pre>
<hr /> <hr />
<pre class="mermaid">
sequenceDiagram
box lightgreen Alice & John
participant A
participant J
end
box Another Group very very long description not wrapped
participant B
end
A->>J: Hello John, how are you?
J->>A: Great!
A->>B: Hello Bob, how are you ?
</pre
>
<hr />
<script src="./mermaid.js"></script> <script src="./mermaid.js"></script>
<script> <script>
mermaid.initialize({ mermaid.initialize({

View File

@ -17,11 +17,11 @@
<h1>State diagram demos</h1> <h1>State diagram demos</h1>
<h2>Very simple showing change from State1 to State2</h2> <h2>Very simple showing change from State1 to State2</h2>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: Very simple diagram title: Very simple diagram
--- ---
stateDiagram stateDiagram
accTitle: This is the accessible title accTitle: This is the accessible title
accDescr:This is an accessible description accDescr:This is an accessible description
State1 --> State2 State1 --> State2
</pre> </pre>
@ -47,13 +47,13 @@ title: Very simple diagram
</code> </code>
</p> </p>
<pre class="mermaid"> <pre class="mermaid">
--- ---
title: Very simple diagram title: Very simple diagram
--- ---
stateDiagram stateDiagram
direction TB direction TB
accTitle: This is the accessible title accTitle: This is the accessible title
accDescr: This is an accessible description accDescr: This is an accessible description
classDef notMoving fill:white classDef notMoving fill:white
@ -161,12 +161,19 @@ title: Very simple diagram
First --> Second First --> Second
First --> Third First --> Third
state First { state "the first composite" as First {
[*] --> 1st [*] --> 1st
1st --> [*] state innerFirst {
state "1 in innerFirst" as 1st1st
1st2nd: 2 in innerFirst
[*] --> 1st1st
1st1st --> 1st2nd
%% 1st2nd --> 1st
}
1st --> innerFirst
innerFirst --> 2nd
} }
state Second { state Second {
[*] --> 2nd
2nd --> [*] 2nd --> [*]
} }
state Third { state Third {

38
demos/timeline.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<style>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
}
</style>
</head>
<body>
<pre class="mermaid">
timeline
title My day
section Go to work
1930 : first step : second step
: third step
1940 : fourth step : fifth step
</pre>
<script src="./mermaid.js"></script>
<script>
mermaid.initialize({
theme: 'forest',
logLevel: 1,
securityLevel: 'loose',
flowchart: { curve: 'basis' },
gantt: { axisFormat: '%m/%d/%Y' },
sequence: { actorMargin: 50 },
});
</script>
</body>
</html>

View File

@ -497,7 +497,7 @@ mermaidAPI.initialize({
- Clarify the need for a CSS stylesheet [#413](https://github.com/knsv/mermaid/pull/413) ([sifb](https://github.com/sifb)) - Clarify the need for a CSS stylesheet [#413](https://github.com/knsv/mermaid/pull/413) ([sifb](https://github.com/sifb))
- Added hads downstream project [#412](https://github.com/knsv/mermaid/pull/412) ([sinedied](https://github.com/sinedied)) - Added hads downstream project [#412](https://github.com/knsv/mermaid/pull/412) ([sinedied](https://github.com/sinedied))
- update usage and fix #273 [#406](https://github.com/knsv/mermaid/pull/406) ([jinntrance](https://github.com/jinntrance)) - update usage and fix #273 [#406](https://github.com/knsv/mermaid/pull/406) ([jinntrance](https://github.com/jinntrance))
- Add https://github.com/raghur/mermaid-filter to downstream projects docs page [#404](https://github.com/knsv/mermaid/pull/404) ([raghur](https://github.com/raghur)) - Add <https://github.com/raghur/mermaid-filter> to downstream projects docs page [#404](https://github.com/knsv/mermaid/pull/404) ([raghur](https://github.com/raghur))
- New neutral theme [#395](https://github.com/knsv/mermaid/pull/395) ([sinedied](https://github.com/sinedied)) - New neutral theme [#395](https://github.com/knsv/mermaid/pull/395) ([sinedied](https://github.com/sinedied))
- fix cli issues [#390](https://github.com/knsv/mermaid/pull/390) ([ben-page](https://github.com/ben-page)) - fix cli issues [#390](https://github.com/knsv/mermaid/pull/390) ([ben-page](https://github.com/ben-page))
- Add missing space for 'Labels out of bounds' section [#386](https://github.com/knsv/mermaid/pull/386) ([The-Alchemist](https://github.com/The-Alchemist)) - Add missing space for 'Labels out of bounds' section [#386](https://github.com/knsv/mermaid/pull/386) ([The-Alchemist](https://github.com/The-Alchemist))
@ -661,7 +661,7 @@ mermaidAPI.initialize({
**Closed issues:** **Closed issues:**
- Installing “atom-mermaid@0.1.3” failed [#218](https://github.com/knsv/mermaid/issues/218) - Installing “atom-mermaid\@0.1.3” failed [#218](https://github.com/knsv/mermaid/issues/218)
- node feature request [#211](https://github.com/knsv/mermaid/issues/211) - node feature request [#211](https://github.com/knsv/mermaid/issues/211)
- Please add prefix for styles [#208](https://github.com/knsv/mermaid/issues/208) - Please add prefix for styles [#208](https://github.com/knsv/mermaid/issues/208)
- Bad handling of block arguments [#207](https://github.com/knsv/mermaid/issues/207) - Bad handling of block arguments [#207](https://github.com/knsv/mermaid/issues/207)
@ -671,7 +671,7 @@ mermaidAPI.initialize({
- Broken CLI Graphs? (v0.5.1) [#196](https://github.com/knsv/mermaid/issues/196) - Broken CLI Graphs? (v0.5.1) [#196](https://github.com/knsv/mermaid/issues/196)
- Static site does not render under HTTPS [#194](https://github.com/knsv/mermaid/issues/194) - Static site does not render under HTTPS [#194](https://github.com/knsv/mermaid/issues/194)
- Error on simple graph [#192](https://github.com/knsv/mermaid/issues/192) - Error on simple graph [#192](https://github.com/knsv/mermaid/issues/192)
- Escape "~" [#191](https://github.com/knsv/mermaid/issues/191) - Escape "\~" [#191](https://github.com/knsv/mermaid/issues/191)
- Trying to add link using 'click' to flowchart [#188](https://github.com/knsv/mermaid/issues/188) - Trying to add link using 'click' to flowchart [#188](https://github.com/knsv/mermaid/issues/188)
- cli: no lines and arrowheads rendered / only dotted lines [#187](https://github.com/knsv/mermaid/issues/187) - cli: no lines and arrowheads rendered / only dotted lines [#187](https://github.com/knsv/mermaid/issues/187)
- text of mermaid div displayed on page [#186](https://github.com/knsv/mermaid/issues/186) - text of mermaid div displayed on page [#186](https://github.com/knsv/mermaid/issues/186)
@ -793,7 +793,7 @@ mermaidAPI.initialize({
**Closed issues:** **Closed issues:**
- subgraph background is black in rendered flowchart PNG via CLI [#121](https://github.com/knsv/mermaid/issues/121) - subgraph background is black in rendered flowchart PNG via CLI [#121](https://github.com/knsv/mermaid/issues/121)
- Integrate editor at https://github.com/naseer/mermaid-webapp [#110](https://github.com/knsv/mermaid/issues/110) - Integrate editor at <https://github.com/naseer/mermaid-webapp> [#110](https://github.com/knsv/mermaid/issues/110)
- Internet Explorer Support [#99](https://github.com/knsv/mermaid/issues/99) - Internet Explorer Support [#99](https://github.com/knsv/mermaid/issues/99)
## [0.3.5](https://github.com/knsv/mermaid/tree/0.3.5) (2015-02-15) ## [0.3.5](https://github.com/knsv/mermaid/tree/0.3.5) (2015-02-15)

View File

@ -60,7 +60,7 @@ Place the renderer in the diagram folder.
### Step 3: Detection of the new diagram type ### Step 3: Detection of the new diagram type
The second thing to do is to add the capability to detect the new new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type. The second thing to do is to add the capability to detect the new diagram to type to the detectType in utils.js. The detection should return a key for the new diagram type.
[This key will be used to as the aria roledescription](#aria-roledescription), so it should be a word that clearly describes the diagram type. [This key will be used to as the aria roledescription](#aria-roledescription), so it should be a word that clearly describes the diagram type.
For example, if your new diagram use a UML deployment diagram, a good key would be "UMLDeploymentDiagram" because assistive technologies such as a screen reader For example, if your new diagram use a UML deployment diagram, a good key would be "UMLDeploymentDiagram" because assistive technologies such as a screen reader
would voice that as "U-M-L Deployment diagram." Another good key would be "deploymentDiagram" because that would be voiced as "Deployment Diagram." A bad key would be "deployment" because that would not sufficiently describe the diagram. would voice that as "U-M-L Deployment diagram." Another good key would be "deploymentDiagram" because that would be voiced as "Deployment Diagram." A bad key would be "deployment" because that would not sufficiently describe the diagram.

View File

@ -32,15 +32,15 @@ Examples are provided in [Getting Started](../intro/n00b-gettingStarted.md)
**CodePen Examples:** **CodePen Examples:**
https://codepen.io/CarlBoneri/pen/BQwZzq <https://codepen.io/CarlBoneri/pen/BQwZzq>
https://codepen.io/tdkn/pen/vZxQzd <https://codepen.io/tdkn/pen/vZxQzd>
https://codepen.io/janzeteachesit/pen/OWWZKN <https://codepen.io/janzeteachesit/pen/OWWZKN>
## Mermaid with Text Area ## Mermaid with Text Area
https://codepen.io/Ryuno-Ki/pen/LNxwgR <https://codepen.io/Ryuno-Ki/pen/LNxwgR>
## Mermaid in open source docs ## Mermaid in open source docs

View File

@ -80,9 +80,12 @@ A **multiple line accessible description** _does not have a colon (`:`) after th
Ex: Ex:
accDescr { The official Bob's Burgers corporate processes that are used ```markdown
for making very, very big decisions. accDescr {
This is actually a very simple flow: see the big decision and then make the big decision.} This is a multiple line accessible description.
It does not have a colon and is surrounded by curly brackets.
}
```
See [the accTitle and accDescr usage examples](#acctitle-and-accdescr-usage-examples) for how this can be used in a diagram and the resulting HTML generated. See [the accTitle and accDescr usage examples](#acctitle-and-accdescr-usage-examples) for how this can be used in a diagram and the resulting HTML generated.

View File

@ -32,7 +32,8 @@ Mermaid basically supports two types of configuration options to be overridden b
**NOTE:** These options listed here are not all the configuration options. To get hold of all the configuration options, please refer to the [defaultConfig.ts](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/defaultConfig.ts) in the source code. **NOTE:** These options listed here are not all the configuration options. To get hold of all the configuration options, please refer to the [defaultConfig.ts](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/defaultConfig.ts) in the source code.
Soon we plan to publish a complete list of top-level configurations & all the diagram specific configurations, with their possible values in the docs > **Note**
> We plan to publish a complete list of top-level configurations & all the diagram specific configurations, with their possible values in the docs soon.
## Declaring directives ## Declaring directives

View File

@ -2,7 +2,7 @@
> >
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. > ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
> >
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/misc/faq.md](../../packages/mermaid/src/docs/misc/faq.md). > ## Please edit the corresponding file in [/packages/mermaid/src/docs/config/faq.md](../../packages/mermaid/src/docs/config/faq.md).
# Frequently Asked Questions # Frequently Asked Questions

View File

@ -10,17 +10,21 @@
A more condensed html code can be achieved by embedding the mermaid code in its own .js file, which is referenced like so: A more condensed html code can be achieved by embedding the mermaid code in its own .js file, which is referenced like so:
stuff stuff ```html
</div> ...
</body> </div>
</html> </body>
</html>
```
The actual mermaid file could for example look like this: The actual mermaid file could for example look like this:
mermaid content... ```javascript
mermaid content ...
--- ```
## mermaid configuration options ## mermaid configuration options
... ```markdown
(coming soon)
```

View File

@ -14,7 +14,7 @@
#### Defined in #### Defined in
[defaultConfig.ts:1933](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1933) [defaultConfig.ts:2084](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2084)
--- ---

View File

@ -20,7 +20,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in #### Defined in
[mermaidAPI.ts:72](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L72) [mermaidAPI.ts:74](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L74)
## Variables ## Variables
@ -90,7 +90,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:961](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L961) [mermaidAPI.ts:886](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L886)
## Functions ## Functions
@ -121,7 +121,7 @@ Return the last node appended
#### Defined in #### Defined in
[mermaidAPI.ts:285](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L285) [mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
--- ---
@ -147,7 +147,7 @@ the cleaned up svgCode
#### Defined in #### Defined in
[mermaidAPI.ts:236](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L236) [mermaidAPI.ts:238](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L238)
--- ---
@ -159,11 +159,11 @@ Create the user styles
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| :---------- | :-------------- | :----------------------------------------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | :---------- | :------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ |
| `config` | `MermaidConfig` | configuration that has style and theme settings to use | | `config` | `MermaidConfig` | configuration that has style and theme settings to use |
| `graphType` | `string` | used for checking if classDefs should be applied | | `graphType` | `string` | used for checking if classDefs should be applied |
| `classDefs` | `undefined` | `null` | `Record`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) | | `classDefs` | `undefined` \| `null` \| `Record`<`string`, `DiagramStyleClassDef`> | the classDefs in the diagram text. Might be null if none were defined. Usually is the result of a call to getClasses(...) |
#### Returns #### Returns
@ -173,7 +173,7 @@ the string with all the user styles
#### Defined in #### Defined in
[mermaidAPI.ts:165](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L165) [mermaidAPI.ts:167](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L167)
--- ---
@ -196,7 +196,7 @@ the string with all the user styles
#### Defined in #### Defined in
[mermaidAPI.ts:213](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L213) [mermaidAPI.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L215)
--- ---
@ -223,7 +223,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in #### Defined in
[mermaidAPI.ts:149](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L149) [mermaidAPI.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L151)
--- ---
@ -243,7 +243,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in #### Defined in
[mermaidAPI.ts:129](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L129) [mermaidAPI.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L131)
--- ---
@ -263,7 +263,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in #### Defined in
[mermaidAPI.ts:100](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L100) [mermaidAPI.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L102)
--- ---
@ -289,7 +289,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in #### Defined in
[mermaidAPI.ts:264](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L264) [mermaidAPI.ts:266](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L266)
--- ---
@ -314,4 +314,4 @@ Remove any existing elements from the given document
#### Defined in #### Defined in
[mermaidAPI.ts:335](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L335) [mermaidAPI.ts:337](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L337)

View File

@ -12,15 +12,15 @@ Themes can now be customized at the site-wide level, or on individual Mermaid di
## Available Themes ## Available Themes
1. **default** - This is the default theme for all diagrams. 1. [**default**](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/themes/theme-default.js) - This is the default theme for all diagrams.
2. **neutral** - This theme is great for black and white documents that will be printed. 2. [**neutral**](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/themes/theme-neutral.js) - This theme is great for black and white documents that will be printed.
3. **dark** - This theme goes well with dark-colored elements or dark-mode. 3. [**dark**](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/themes/theme-dark.js) - This theme goes well with dark-colored elements or dark-mode.
4. **forest** - This theme contains shades of green. 4. [**forest**](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/themes/theme-forest.js) - This theme contains shades of green.
5. **base** - This is the only theme that can be modified. Use this theme as the base for customizations. 5. [**base**](https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/themes/theme-base.js) - This is the only theme that can be modified. Use this theme as the base for customizations.
## Site-wide Theme ## Site-wide Theme
@ -53,6 +53,18 @@ Example of `init` directive setting the `theme` to `forest`:
a --> b a --> b
``` ```
```mermaid-example
%%{init: {'theme':'forest'}}%%
graph TD
a --> b
```
```mermaid
%%{init: {'theme':'forest'}}%%
graph TD
a --> b
```
> **Reminder**: the only theme that can be customed is the `base` theme. The following section covers how to use `themeVariables` for customizations. > **Reminder**: the only theme that can be customed is the `base` theme. The following section covers how to use `themeVariables` for customizations.
## Customizing Themes with `themeVariables` ## Customizing Themes with `themeVariables`
@ -127,6 +139,66 @@ Example of modifying `themeVariables` using the `init` directive:
end end
``` ```
```mermaid-example
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#BB2528',
'primaryTextColor': '#fff',
'primaryBorderColor': '#7C0000',
'lineColor': '#F8B229',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}
}
}%%
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
B --> G[/Another/]
C ==>|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
subgraph section
C
D
E
F
G
end
```
```mermaid
%%{
init: {
'theme': 'base',
'themeVariables': {
'primaryColor': '#BB2528',
'primaryTextColor': '#fff',
'primaryBorderColor': '#7C0000',
'lineColor': '#F8B229',
'secondaryColor': '#006100',
'tertiaryColor': '#fff'
}
}
}%%
graph TD
A[Christmas] -->|Get money| B(Go shopping)
B --> C{Let me think}
B --> G[/Another/]
C ==>|One| D[Laptop]
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
subgraph section
C
D
E
F
G
end
```
## Color and Color Calculation ## Color and Color Calculation
To ensure diagram readability, the default value of certain variables is calculated or derived from other variables. For example, `primaryBorderColor` is derived from the `primaryColor` variable. So if the `primaryColor` variable is customized, Mermaid will adjust `primaryBorderColor` automatically. Adjustments can mean a color inversion, a hue change, a darkening/lightening by 10%, etc. To ensure diagram readability, the default value of certain variables is calculated or derived from other variables. For example, `primaryBorderColor` is derived from the `primaryColor` variable. So if the `primaryColor` variable is customized, Mermaid will adjust `primaryBorderColor` automatically. Adjustments can mean a color inversion, a hue change, a darkening/lightening by 10%, etc.
@ -142,13 +214,10 @@ The theming engine will only recognize hex colors and not color names. So, the v
| fontFamily | trebuchet ms, verdana, arial | | | fontFamily | trebuchet ms, verdana, arial | |
| fontSize | 16px | Font size in pixels | | fontSize | 16px | Font size in pixels |
| primaryColor | #fff4dd | Color to be used as background in nodes, other colors will be derived from this | | primaryColor | #fff4dd | Color to be used as background in nodes, other colors will be derived from this |
| primaryBorderColor | calculated from primaryColor | Color to be used as border in nodes using `primaryColor` |
| primaryBorderColor | calculated from primaryColor | Color to be used as border in nodes using `primaryColor` |
| primaryTextColor | calculated from darkMode #ddd/#333 | Color to be used as text color in nodes using `primaryColor` | | primaryTextColor | calculated from darkMode #ddd/#333 | Color to be used as text color in nodes using `primaryColor` |
| secondaryColor | calculated from primaryColor | | | secondaryColor | calculated from primaryColor | |
| primaryBorderColor | calculated from primaryColor | Color to be used as border in nodes using `primaryColor` | | primaryBorderColor | calculated from primaryColor | Color to be used as border in nodes using `primaryColor` |
| secondaryBorderColor | calculated from secondaryColor | Color to be used as border in nodes using `secondaryColor` | | secondaryBorderColor | calculated from secondaryColor | Color to be used as border in nodes using `secondaryColor` |
| primaryBorderColor | calculated from primaryColor | Color to be used as border in nodes using `primaryColor` |
| secondaryTextColor | calculated from secondaryColor | Color to be used as text color in nodes using `secondaryColor` | | secondaryTextColor | calculated from secondaryColor | Color to be used as text color in nodes using `secondaryColor` |
| tertiaryColor | calculated from primaryColor | | | tertiaryColor | calculated from primaryColor | |
| tertiaryBorderColor | calculated from tertiaryColor | Color to be used as border in nodes using `tertiaryColor` | | tertiaryBorderColor | calculated from tertiaryColor | Color to be used as border in nodes using `tertiaryColor` |

View File

@ -22,24 +22,21 @@ For the majority of users, Using the [Live Editor](https://mermaid.live/) would
We have compiled some Video [Tutorials](./Tutorials.md) on how to use the mermaid Live Editor. We have compiled some Video [Tutorials](./Tutorials.md) on how to use the mermaid Live Editor.
**Installing and Hosting Mermaid on a Webpage** ### Installing and Hosting Mermaid on a Webpage
**Using the npm package** **Using the npm package:**
1. You will need to install node v16, which would have npm. 1. You will need to install `node v16`, which would have npm.
2. download yarn using npm. 2. Download `yarn` using npm.
3. enter the following command: 3. Enter the following command: `yarn add mermaid`.
yarn add mermaid
4. At this point, you can add mermaid as a dev dependency using this command: 4. At this point, you can add mermaid as a dev dependency using this command: `yarn add --dev mermaid`.
yarn add --dev mermaid
5. Alternatively, you can also deploy mermaid using the script tag in an HTML file with mermaid diagram descriptions. 5. Alternatively, you can also deploy mermaid using the script tag in an HTML file with mermaid diagram descriptions as is shown in the example below.
as is shown in the example below
**Hosting mermaid on a web page.** **Hosting mermaid on a web page:**
> Note:This topic explored in greater depth in the [User Guide for Beginners](../intro/n00b-gettingStarted.md) > Note:This topic explored in greater depth in the [User Guide for Beginners](../intro/n00b-gettingStarted.md)
@ -152,7 +149,7 @@ $(document).ready(function () {
}); });
``` ```
Not doing so will most likely result in mermaid rendering graphs that have labels out of bounds. The default integration in mermaid uses the window.load event to start rendering. Not doing so will most likely result in mermaid rendering graphs that have labels out of bounds. The default integration in mermaid uses the window\.load event to start rendering.
If your page has other fonts in its body those might be used instead of the mermaid font. Specifying the font in your styling is a workaround for this. If your page has other fonts in its body those might be used instead of the mermaid font. Specifying the font in your styling is a workaround for this.

View File

@ -2,7 +2,7 @@
> >
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. > ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
> >
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/misc/integrations.md](../../packages/mermaid/src/docs/misc/integrations.md). > ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/integrations.md](../../packages/mermaid/src/docs/ecosystem/integrations.md).
# Integrations # Integrations
@ -21,6 +21,7 @@ They also serve as proof of concept, for the variety of things that can be built
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**) - [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
- [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**) - [Tuleap](https://docs.tuleap.org/user-guide/writing-in-tuleap.html#graphs) (**Native support**)
- [Joplin](https://joplinapp.org) (**Native support**) - [Joplin](https://joplinapp.org) (**Native support**)
- [Swimm](https://swimm.io) (**Native support**)
- [Notion](https://notion.so) (**Native support**) - [Notion](https://notion.so) (**Native support**)
- [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**) - [Observable](https://observablehq.com/@observablehq/mermaid) (**Native support**)
- [Obsidian](https://help.obsidian.md/How+to/Format+your+notes#Diagram) (**Native support**) - [Obsidian](https://help.obsidian.md/How+to/Format+your+notes#Diagram) (**Native support**)
@ -94,9 +95,10 @@ They also serve as proof of concept, for the variety of things that can be built
## Editor Plugins ## Editor Plugins
- [Vs Code](https://code.visualstudio.com/) - [VS Code](https://code.visualstudio.com/)
- [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid) - [Markdown Preview Mermaid Support](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-mermaid)
- [Mermaid Preview](https://marketplace.visualstudio.com/items?itemName=vstirbu.vscode-mermaid-preview) - [Mermaid Preview](https://marketplace.visualstudio.com/items?itemName=vstirbu.vscode-mermaid-preview)
- [Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)
- [Mermaid Markdown Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=bpruitt-goddard.mermaid-markdown-syntax-highlighting) - [Mermaid Markdown Syntax Highlighting](https://marketplace.visualstudio.com/items?itemName=bpruitt-goddard.mermaid-markdown-syntax-highlighting)
- [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor) - [Mermaid Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export) - [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)
@ -109,10 +111,10 @@ They also serve as proof of concept, for the variety of things that can be built
- [md-it-mermaid](https://github.com/iamcco/md-it-mermaid) - [md-it-mermaid](https://github.com/iamcco/md-it-mermaid)
- [markdown-it-mermaid-fence-new](https://github.com/Revomatico/markdown-it-mermaid-fence-new) - [markdown-it-mermaid-fence-new](https://github.com/Revomatico/markdown-it-mermaid-fence-new)
- [markdown-it-mermaid-less](https://github.com/searKing/markdown-it-mermaid-less) - [markdown-it-mermaid-less](https://github.com/searKing/markdown-it-mermaid-less)
- [Atom](https://atom.io) - Atom _(Atom has been [archived.](https://github.blog/2022-06-08-sunsetting-atom/))_
- [Markdown Preview Enhanced](https://atom.io/packages/markdown-preview-enhanced) - [Markdown Preview Enhanced](https://github.com/shd101wyy/markdown-preview-enhanced)
- [Atom Mermaid](https://atom.io/packages/atom-mermaid) - [Atom Mermaid](https://github.com/y-takey/atom-mermaid)
- [Language Mermaid Syntax Highlighter](https://atom.io/packages/language-mermaid) - [Language Mermaid Syntax Highlighter](https://github.com/ytisf/language-mermaid)
- [Sublime Text 3](https://sublimetext.com) - [Sublime Text 3](https://sublimetext.com)
- [Mermaid Package](https://packagecontrol.io/packages/Mermaid) - [Mermaid Package](https://packagecontrol.io/packages/Mermaid)
- [Astah](https://astah.net) - [Astah](https://astah.net)

View File

@ -0,0 +1,9 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/showcases.md](../../packages/mermaid/src/docs/ecosystem/showcases.md).
# Showcases
- [Swimm - Up-to-date diagrams with Swimm, the knowledge management tool for code](https://docs.swimm.io/Features/diagrams-and-charts).

View File

@ -367,7 +367,7 @@ _Unfortunately you can not have a cake and eat it at the same time which in this
## Reporting vulnerabilities ## Reporting vulnerabilities
To report a vulnerability, please e-mail security@mermaid.live with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue. To report a vulnerability, please e-mail <security@mermaid.live> with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue.
## Appreciation ## Appreciation

View File

@ -79,9 +79,9 @@ Editing is as easy as pasting your **Diagram code**, into the `code` section of
The Gist you create should have a code.mmd file and optionally a config.json. [Example](https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a) The Gist you create should have a code.mmd file and optionally a config.json. [Example](https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a)
To load a gist into the Editor, you can use https://mermaid.live/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a To load a gist into the Editor, you can use <https://mermaid.live/edit?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a>
and to View, https://mermaid.live/view?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a and to View, <https://mermaid.live/view?gist=https://gist.github.com/sidharthv96/6268a23e673a533dcb198f241fd7012a>
## 2. Using Mermaid Plugins: ## 2. Using Mermaid Plugins:

View File

@ -130,121 +130,121 @@ The number of shapes per row and the number of boundaries can be adjusted using
The following unfinished features are not supported in the short term. The following unfinished features are not supported in the short term.
- \[ ] sprite - [ ] sprite
- \[ ] tags - [ ] tags
- \[ ] link - [ ] link
- \[ ] Legend - [ ] Legend
- \[x] System Context - [x] System Context
- - \[x] Person(alias, label, ?descr, ?sprite, ?tags, $link) - - [x] Person(alias, label, ?descr, ?sprite, ?tags, $link)
- - \[x] Person_Ext - - [x] Person_Ext
- - \[x] System(alias, label, ?descr, ?sprite, ?tags, $link) - - [x] System(alias, label, ?descr, ?sprite, ?tags, $link)
- - \[x] SystemDb - - [x] SystemDb
- - \[x] SystemQueue - - [x] SystemQueue
- - \[x] System_Ext - - [x] System_Ext
- - \[x] SystemDb_Ext - - [x] SystemDb_Ext
- - \[x] SystemQueue_Ext - - [x] SystemQueue_Ext
- - \[x] Boundary(alias, label, ?type, ?tags, $link) - - [x] Boundary(alias, label, ?type, ?tags, $link)
- - \[x] Enterprise_Boundary(alias, label, ?tags, $link) - - [x] Enterprise_Boundary(alias, label, ?tags, $link)
- - \[x] System_Boundary - - [x] System_Boundary
- \[x] Container diagram - [x] Container diagram
- - \[x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link) - - [x] Container(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
- - \[x] ContainerDb - - [x] ContainerDb
- - \[x] ContainerQueue - - [x] ContainerQueue
- - \[x] Container_Ext - - [x] Container_Ext
- - \[x] ContainerDb_Ext - - [x] ContainerDb_Ext
- - \[x] ContainerQueue_Ext - - [x] ContainerQueue_Ext
- - \[x] Container_Boundary(alias, label, ?tags, $link) - - [x] Container_Boundary(alias, label, ?tags, $link)
- \[x] Component diagram - [x] Component diagram
- - \[x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link) - - [x] Component(alias, label, ?techn, ?descr, ?sprite, ?tags, $link)
- - \[x] ComponentDb - - [x] ComponentDb
- - \[x] ComponentQueue - - [x] ComponentQueue
- - \[x] Component_Ext - - [x] Component_Ext
- - \[x] ComponentDb_Ext - - [x] ComponentDb_Ext
- - \[x] ComponentQueue_Ext - - [x] ComponentQueue_Ext
- \[x] Dynamic diagram - [x] Dynamic diagram
- - \[x] RelIndex(index, from, to, label, ?tags, $link) - - [x] RelIndex(index, from, to, label, ?tags, $link)
- \[x] Deployment diagram - [x] Deployment diagram
- - \[x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link) - - [x] Deployment_Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link)
- - \[x] Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link): short name of Deployment_Node() - - [x] Node(alias, label, ?type, ?descr, ?sprite, ?tags, $link): short name of Deployment_Node()
- - \[x] Node_L(alias, label, ?type, ?descr, ?sprite, ?tags, $link): left aligned Node() - - [x] Node_L(alias, label, ?type, ?descr, ?sprite, ?tags, $link): left aligned Node()
- - \[x] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node() - - [x] Node_R(alias, label, ?type, ?descr, ?sprite, ?tags, $link): right aligned Node()
- \[x] Relationship Types - [x] Relationship Types
- - \[x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link) - - [x] Rel(from, to, label, ?techn, ?descr, ?sprite, ?tags, $link)
- - \[x] BiRel (bidirectional relationship) - - [x] BiRel (bidirectional relationship)
- - \[x] Rel_U, Rel_Up - - [x] Rel_U, Rel_Up
- - \[x] Rel_D, Rel_Down - - [x] Rel_D, Rel_Down
- - \[x] Rel_L, Rel_Left - - [x] Rel_L, Rel_Left
- - \[x] Rel_R, Rel_Right - - [x] Rel_R, Rel_Right
- - \[x] Rel_Back - - [x] Rel_Back
- - \[x] RelIndex \* Compatible with C4-Plantuml syntax, but ignores the index parameter. The sequence number is determined by the order in which the rel statements are written. - - [x] RelIndex \* Compatible with C4-Plantuml syntax, but ignores the index parameter. The sequence number is determined by the order in which the rel statements are written.
- \[ ] Custom tags/stereotypes support and skin param updates - [ ] Custom tags/stereotypes support and skin param updates
- - \[ ] AddElementTag(tagStereo, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new element tag. The styles of the tagged elements are updated and the tag is displayed in the calculated legend. - - [ ] AddElementTag(tagStereo, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new element tag. The styles of the tagged elements are updated and the tag is displayed in the calculated legend.
- - \[ ] AddRelTag(tagStereo, ?textColor, ?lineColor, ?lineStyle, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new Relationship tag. The styles of the tagged relationships are updated and the tag is displayed in the calculated legend. - - [ ] AddRelTag(tagStereo, ?textColor, ?lineColor, ?lineStyle, ?sprite, ?techn, ?legendText, ?legendSprite): Introduces a new Relationship tag. The styles of the tagged relationships are updated and the tag is displayed in the calculated legend.
- - \[x] UpdateElementStyle(elementName, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): This call updates the default style of the elements (component, ...) and creates no additional legend entry. - - [x] UpdateElementStyle(elementName, ?bgColor, ?fontColor, ?borderColor, ?shadowing, ?shape, ?sprite, ?techn, ?legendText, ?legendSprite): This call updates the default style of the elements (component, ...) and creates no additional legend entry.
- - \[x] UpdateRelStyle(from, to, ?textColor, ?lineColor, ?offsetX, ?offsetY): This call updates the default relationship colors and creates no additional legend entry. Two new parameters, offsetX and offsetY, are added to set the offset of the original position of the text. - - [x] UpdateRelStyle(from, to, ?textColor, ?lineColor, ?offsetX, ?offsetY): This call updates the default relationship colors and creates no additional legend entry. Two new parameters, offsetX and offsetY, are added to set the offset of the original position of the text.
- - \[ ] RoundedBoxShape(): This call returns the name of the rounded box shape and can be used as ?shape argument. - - [ ] RoundedBoxShape(): This call returns the name of the rounded box shape and can be used as ?shape argument.
- - \[ ] EightSidedShape(): This call returns the name of the eight sided shape and can be used as ?shape argument. - - [ ] EightSidedShape(): This call returns the name of the eight sided shape and can be used as ?shape argument.
- - \[ ] DashedLine(): This call returns the name of the dashed line and can be used as ?lineStyle argument. - - [ ] DashedLine(): This call returns the name of the dashed line and can be used as ?lineStyle argument.
- - \[ ] DottedLine(): This call returns the name of the dotted line and can be used as ?lineStyle argument. - - [ ] DottedLine(): This call returns the name of the dotted line and can be used as ?lineStyle argument.
- - \[ ] BoldLine(): This call returns the name of the bold line and can be used as ?lineStyle argument. - - [ ] BoldLine(): This call returns the name of the bold line and can be used as ?lineStyle argument.
- - \[x] UpdateLayoutConfig(?c4ShapeInRow, ?c4BoundaryInRow): New. This call updates the default c4ShapeInRow(4) and c4BoundaryInRow(2). - - [x] UpdateLayoutConfig(?c4ShapeInRow, ?c4BoundaryInRow): New. This call updates the default c4ShapeInRow(4) and c4BoundaryInRow(2).
There are two ways to assign parameters with question marks. One uses the non-named parameter assignment method in the order of the parameters, and the other uses the named parameter assignment method, where the name must start with a $ symbol. There are two ways to assign parameters with question marks. One uses the non-named parameter assignment method in the order of the parameters, and the other uses the named parameter assignment method, where the name must start with a $ symbol.

View File

@ -206,7 +206,7 @@ class BankAccount{
#### Generic Types #### Generic Types
Members can be defined using generic types, such as `List<int>`, for fields, parameters, and return types by enclosing the type within `~` (**tilde**). Note: **nested** type declarations such as `List<List<int>>` are not currently supported. Members can be defined using generic types, such as `List<int>`, for fields, parameters, and return types by enclosing the type within `~` (**tilde**). **Nested** type declarations such as `List<List<int>>` are supported.
Generics can be represented as part of a class definition and also in the parameters or the return value of a method/function: Generics can be represented as part of a class definition and also in the parameters or the return value of a method/function:
@ -222,6 +222,7 @@ class Square~Shape~{
Square : -List~string~ messages Square : -List~string~ messages
Square : +setMessages(List~string~ messages) Square : +setMessages(List~string~ messages)
Square : +getMessages() List~string~ Square : +getMessages() List~string~
Square : +getDistanceMatrix() List~List~int~~
``` ```
```mermaid ```mermaid
@ -236,12 +237,9 @@ class Square~Shape~{
Square : -List~string~ messages Square : -List~string~ messages
Square : +setMessages(List~string~ messages) Square : +setMessages(List~string~ messages)
Square : +getMessages() List~string~ Square : +getMessages() List~string~
Square : +getDistanceMatrix() List~List~int~~
``` ```
#### Return Type
Optionally you can end the method/function definition with the data type that will be returned.
#### Visibility #### Visibility
To describe the visibility (or encapsulation) of an attribute or method/function that is a part of a class (i.e. a class member), optional notation may be placed before that members' name: To describe the visibility (or encapsulation) of an attribute or method/function that is a part of a class (i.e. a class member), optional notation may be placed before that members' name:
@ -271,7 +269,7 @@ There are eight different types of relations defined for classes under UML which
| Type | Description | | Type | Description |
| ------- | ------------- | | ------- | ------------- |
| `<\|--` | Inheritance | | `<\|--` | Inheritance |
| `\*--` | Composition | | `*--` | Composition |
| `o--` | Aggregation | | `o--` | Aggregation |
| `-->` | Association | | `-->` | Association |
| `--` | Link (Solid) | | `--` | Link (Solid) |

View File

@ -162,7 +162,7 @@ erDiagram
### Attributes ### Attributes
Attributes can be defined for entities by specifying the entity name followed by a block containing multiple `type name` pairs, where a block is delimited by an opening `{` and a closing `}`. For example: Attributes can be defined for entities by specifying the entity name followed by a block containing multiple `type name` pairs, where a block is delimited by an opening `{` and a closing `}`. The attributes are rendered inside the entity boxes. For example:
```mermaid-example ```mermaid-example
erDiagram erDiagram
@ -196,82 +196,58 @@ erDiagram
} }
``` ```
The attributes are rendered inside the entity boxes: The `type` and `name` values must begin with an alphabetic character and may contain digits, hyphens, underscores, parentheses and square brackets. Other than that, there are no restrictions, and there is no implicit set of valid data types.
```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber
string make
string model
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string firstName
string lastName
int age
}
```
```mermaid
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber
string make
string model
}
PERSON ||--o{ NAMED-DRIVER : is
PERSON {
string firstName
string lastName
int age
}
```
The `type` and `name` values must begin with an alphabetic character and may contain digits, hyphens or underscores. Other than that, there are no restrictions, and there is no implicit set of valid data types.
#### Attribute Keys and Comments #### Attribute Keys and Comments
Attributes may also have a `key` or comment defined. Keys can be "PK" or "FK", for Primary Key or Foreign Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them. Attributes may also have a `key` or comment defined. Keys can be `PK`, `FK` or `UK`, for Primary Key, Foreign Key or Unique Key. To specify multiple key constraints on a single attribute, separate them with a comma (e.g., `PK, FK`).. A `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
```mermaid-example ```mermaid-example
erDiagram erDiagram
CAR ||--o{ NAMED-DRIVER : allows CAR ||--o{ NAMED-DRIVER : allows
CAR { CAR {
string allowedDriver FK "The license of the allowed driver" string registrationNumber PK
string registrationNumber
string make string make
string model string model
string[] parts
} }
PERSON ||--o{ NAMED-DRIVER : is PERSON ||--o{ NAMED-DRIVER : is
PERSON { PERSON {
string driversLicense PK "The license #" string driversLicense PK "The license #"
string firstName string(99) firstName "Only 99 characters are allowed"
string lastName string lastName
string phone UK
int age int age
} }
MANUFACTURER only one to zero or more CAR NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
``` ```
```mermaid ```mermaid
erDiagram erDiagram
CAR ||--o{ NAMED-DRIVER : allows CAR ||--o{ NAMED-DRIVER : allows
CAR { CAR {
string allowedDriver FK "The license of the allowed driver" string registrationNumber PK
string registrationNumber
string make string make
string model string model
string[] parts
} }
PERSON ||--o{ NAMED-DRIVER : is PERSON ||--o{ NAMED-DRIVER : is
PERSON { PERSON {
string driversLicense PK "The license #" string driversLicense PK "The license #"
string firstName string(99) firstName "Only 99 characters are allowed"
string lastName string lastName
string phone UK
int age int age
} }
MANUFACTURER only one to zero or more CAR NAMED-DRIVER {
string carRegistrationNumber PK, FK
string driverLicence PK, FK
}
MANUFACTURER only one to zero or more CAR : makes
``` ```
### Other Things ### Other Things

View File

@ -842,8 +842,8 @@ In the example below the style defined in the linkStyle statement will belong to
### Styling line curves ### Styling line curves
It is possible to style the type of curve used for lines between items, if the default method does not meet your needs. It is possible to style the type of curve used for lines between items, if the default method does not meet your needs.
Available curve styles include `basis`, `bump`, `linear`, `monotoneX`, `monotoneY`, `natural`, `step`, `stepAfter`, Available curve styles include `basis`, `bumpX`, `bumpY`, `cardinal`, `catmullRom`, `linear`, `monotoneX`, `monotoneY`,
and `stepBefore`. `natural`, `step`, `stepAfter`, and `stepBefore`.
In this example, a left-to-right graph uses the `stepBefore` curve style: In this example, a left-to-right graph uses the `stepBefore` curve style:
@ -990,7 +990,22 @@ flowchart LR
C -->|Two| E[Result two] C -->|Two| E[Result two]
``` ```
## Configuration... ## Configuration
### Renderer
The layout of the diagram is done with the renderer. The default renderer is dagre.
Starting with Mermaid version 9.4, you can use an alternate renderer named elk. The elk renderer is better for larger and/or more complex diagrams.
The _elk_ renderer is an experimenal feature.
You can change the renderer to elk by adding this directive:
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
Note that the site needs to use mermaid version 9.4+ for this to work and have this featured enabled in the lazy-loading configuration.
### Width
It is possible to adjust the width of the rendered flowchart. It is possible to adjust the width of the rendered flowchart.

View File

@ -172,65 +172,72 @@ Final milestone : milestone, m2, 18:14, 2min
The default input date format is `YYYY-MM-DD`. You can define your custom `dateFormat`. The default input date format is `YYYY-MM-DD`. You can define your custom `dateFormat`.
dateFormat YYYY-MM-DD ```markdown
dateFormat YYYY-MM-DD
```
The following formatting options are supported: The following formatting options are supported:
Input Example Description: | Input | Example | Description |
YYYY 2014 4 digit year | ---------- | -------------- | ------------------------------------------------------ |
YY 14 2 digit year | `YYYY` | 2014 | 4 digit year |
Q 1..4 Quarter of year. Sets month to first month in quarter. | `YY` | 14 | 2 digit year |
M MM 1..12 Month number | `Q` | 1..4 | Quarter of year. Sets month to first month in quarter. |
MMM MMMM January..Dec Month name in locale set by moment.locale() | `M MM` | 1..12 | Month number |
D DD 1..31 Day of month | `MMM MMMM` | January..Dec | Month name in locale set by `moment.locale()` |
Do 1st..31st Day of month with ordinal | `D DD` | 1..31 | Day of month |
DDD DDDD 1..365 Day of year | `Do` | 1st..31st | Day of month with ordinal |
X 1410715640.579 Unix timestamp | `DDD DDDD` | 1..365 | Day of year |
x 1410715640579 Unix ms timestamp | `X` | 1410715640.579 | Unix timestamp |
H HH 0..23 24 hour time | `x` | 1410715640579 | Unix ms timestamp |
h hh 1..12 12 hour time used with a A. | `H HH` | 0..23 | 24 hour time |
a A am pm Post or ante meridiem | `h hh` | 1..12 | 12 hour time used with `a A`. |
m mm 0..59 Minutes | `a A` | am pm | Post or ante meridiem |
s ss 0..59 Seconds | `m mm` | 0..59 | Minutes |
S 0..9 Tenths of a second | `s ss` | 0..59 | Seconds |
SS 0..99 Hundreds of a second | `S` | 0..9 | Tenths of a second |
SSS 0..999 Thousandths of a second | `SS` | 0..99 | Hundreds of a second |
Z ZZ +12:00 Offset from UTC as +-HH:mm, +-HHmm, or Z | `SSS` | 0..999 | Thousandths of a second |
| `Z ZZ` | +12:00 | Offset from UTC as +-HH:mm, +-HHmm, or Z |
More info in: https://momentjs.com/docs/#/parsing/string-format/ More info in: <https://momentjs.com/docs/#/parsing/string-format/>
### Output date format on the axis ### Output date format on the axis
The default output date format is `YYYY-MM-DD`. You can define your custom `axisFormat`, like `2020-Q1` for the first quarter of the year 2020. The default output date format is `YYYY-MM-DD`. You can define your custom `axisFormat`, like `2020-Q1` for the first quarter of the year 2020.
axisFormat %Y-%m-%d ```markdown
axisFormat %Y-%m-%d
```
The following formatting strings are supported: The following formatting strings are supported:
%a - abbreviated weekday name. | Format | Definition |
%A - full weekday name. | ------ | ------------------------------------------------------------------------------------------ |
%b - abbreviated month name. | %a | abbreviated weekday name |
%B - full month name. | %A | full weekday name |
%c - date and time, as "%a %b %e %H:%M:%S %Y". | %b | abbreviated month name |
%d - zero-padded day of the month as a decimal number [01,31]. | %B | full month name |
%e - space-padded day of the month as a decimal number [ 1,31]; equivalent to %_d. | %c | date and time, as "%a %b %e %H:%M:%S %Y" |
%H - hour (24-hour clock) as a decimal number [00,23]. | %d | zero-padded day of the month as a decimal number \[01,31] |
%I - hour (12-hour clock) as a decimal number [01,12]. | %e | space-padded day of the month as a decimal number \[ 1,31]; equivalent to %\_d |
%j - day of the year as a decimal number [001,366]. | %H | hour (24-hour clock) as a decimal number \[00,23] |
%m - month as a decimal number [01,12]. | %I | hour (12-hour clock) as a decimal number \[01,12] |
%M - minute as a decimal number [00,59]. | %j | day of the year as a decimal number \[001,366] |
%L - milliseconds as a decimal number [000, 999]. | %m | month as a decimal number \[01,12] |
%p - either AM or PM. | %M | minute as a decimal number \[00,59] |
%S - second as a decimal number [00,61]. | %L | milliseconds as a decimal number \[000, 999] |
%U - week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. | %p | either AM or PM |
%w - weekday as a decimal number [0(Sunday),6]. | %S | second as a decimal number \[00,61] |
%W - week number of the year (Monday as the first day of the week) as a decimal number [00,53]. | %U | week number of the year (Sunday as the first day of the week) as a decimal number \[00,53] |
%x - date, as "%m/%d/%Y". | %w | weekday as a decimal number \[0(Sunday),6] |
%X - time, as "%H:%M:%S". | %W | week number of the year (Monday as the first day of the week) as a decimal number \[00,53] |
%y - year without century as a decimal number [00,99]. | %x | date, as "%m/%d/%Y" |
%Y - year with century as a decimal number. | %X | time, as "%H:%M:%S" |
%Z - time zone offset, such as "-0700". | %y | year without century as a decimal number \[00,99] |
%% - a literal "%" character. | %Y | year with century as a decimal number |
| %Z | time zone offset, such as "-0700" |
| %% | a literal "%" character |
More info in: <https://github.com/d3/d3-time-format/tree/v4.0.0#locale_format> More info in: <https://github.com/d3/d3-time-format/tree/v4.0.0#locale_format>
@ -238,11 +245,15 @@ More info in: <https://github.com/d3/d3-time-format/tree/v4.0.0#locale_format>
The default output ticks are auto. You can custom your `tickInterval`, like `1day` or `1week`. The default output ticks are auto. You can custom your `tickInterval`, like `1day` or `1week`.
tickInterval 1day ```markdown
tickInterval 1day
```
The pattern is: The pattern is:
/^([1-9][0-9]*)(minute|hour|day|week|month)$/ ```javascript
/^([1-9][0-9]*)(minute|hour|day|week|month)$/;
```
More info in: <https://github.com/d3/d3-time#interval_every> More info in: <https://github.com/d3/d3-time#interval_every>

View File

@ -88,7 +88,7 @@ In this way we can use a text outline to generate a hierarchical mindmap.
## Different shapes ## Different shapes
Mermaids mindmaps can show node using different shapes. When specifying a shape for a node the syntax for the is similar to flowchart nodes, with an id followed by the shape definition and with the text within the shape delimiters. Where possible we try/will try to keep the same shapes as for flowcharts even though they are not all supported from the start. Mermaid mindmaps can show nodes using different shapes. When specifying a shape for a node the syntax is similar to flowchart nodes, with an id followed by the shape definition and with the text within the shape delimiters. Where possible we try/will try to keep the same shapes as for flowcharts, even though they are not all supported from the start.
Mindmap can show the following shapes: Mindmap can show the following shapes:

View File

@ -94,6 +94,59 @@ sequenceDiagram
J->>A: Great! J->>A: Great!
``` ```
### Grouping / Box
The actor(s) can be grouped in vertical boxes. You can define a color (if not, it will be transparent) and/or a descriptive label using the following notation:
box Aqua Group Description
... actors ...
end
box Group without description
... actors ...
end
box rgb(33,66,99)
... actors ...
end
> **Note**
> If your group name is a color you can force the color to be transparent:
box transparent Aqua
... actors ...
end
```mermaid-example
sequenceDiagram
box Purple Alice & John
participant A
participant J
end
box Another Group
participant B
participant C
end
A->>J: Hello John, how are you?
J->>A: Great!
A->>B: Hello Bob, how is Charly ?
B->>C: Hello Charly, how are you?
```
```mermaid
sequenceDiagram
box Purple Alice & John
participant A
participant J
end
box Another Group
participant B
participant C
end
A->>J: Hello John, how are you?
J->>A: Great!
A->>B: Hello Bob, how is Charly ?
B->>C: Hello Charly, how are you?
```
## Messages ## Messages
Messages can be of two displayed either solid or with a dotted line. Messages can be of two displayed either solid or with a dotted line.
@ -102,16 +155,16 @@ Messages can be of two displayed either solid or with a dotted line.
There are six types of arrows currently supported: There are six types of arrows currently supported:
| Type | Description | | Type | Description |
| ---- | ------------------------------------------------ | | ------ | ------------------------------------------------ |
| -> | Solid line without arrow | | `->` | Solid line without arrow |
| --> | Dotted line without arrow | | `-->` | Dotted line without arrow |
| ->> | Solid line with arrowhead | | `->>` | Solid line with arrowhead |
| -->> | Dotted line with arrowhead | | `-->>` | Dotted line with arrowhead |
| -x | Solid line with a cross at the end | | `-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) | | `-)` | Solid line with an open arrow at the end (async) |
| --) | Dotted line with a open arrow at the end (async) | | `--)` | Dotted line with a open arrow at the end (async) |
## Activations ## Activations
@ -198,6 +251,20 @@ sequenceDiagram
Note over Alice,John: A typical interaction Note over Alice,John: A typical interaction
``` ```
It is also possible to add a line break (applies to text input in general):
```mermaid-example
sequenceDiagram
Alice->John: Hello John, how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```
```mermaid
sequenceDiagram
Alice->John: Hello John, how are you?
Note over Alice,John: A typical interaction<br/>But now in two lines
```
## Loops ## Loops
It is possible to express loops in a sequence diagram. This is done by the notation It is possible to express loops in a sequence diagram. This is done by the notation

462
docs/syntax/timeline.md Normal file
View File

@ -0,0 +1,462 @@
> **Warning**
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/timeline.md](../../packages/mermaid/src/docs/syntax/timeline.md).
# Timeline Diagram
> Timeline: This is an experimental diagram for now. The syntax and properties can change in future releases. The syntax is stable except for the icon integration which is the experimental part.
"A timeline is a type of diagram used to illustrate a chronology of events, dates, or periods of time. It is usually presented graphically to indicate the passing of time, and it is usually organized chronologically. A basic timeline presents a list of events in chronological order, usually using dates as markers. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life. A timeline can also be used to show the relationship between events, such as the relationship between the events of a person's life." Wikipedia
### An example of a timeline.
```mermaid-example
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook
: Google
2005 : Youtube
2006 : Twitter
```
```mermaid
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook
: Google
2005 : Youtube
2006 : Twitter
```
## Syntax
The syntax for creating Timeline diagram is simple. You always start with the `timeline` keyword to let mermaid know that you want to create a timeline diagram.
After that there is a possibility to add a title to the timeline. This is done by adding a line with the keyword `title` followed by the title text.
Then you add the timeline data, where you always start with a time period, followed by a colon and then the text for the event. Optionally you can add a second colon and then the text for the event. So, you can have one or more events per time period.
```json
{time period} : {event}
```
or
```json
{time period} : {event} : {event}
```
or
```json
{time period} : {event}
: {event}
: {event}
```
NOTE: Both time period and event are simple text, and not limited to numbers.
Let us look at the syntax for the example above.
```mermaid-example
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
```mermaid
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
In this way we can use a text outline to generate a timeline diagram.
The sequence of time period and events is important, as it will be used to draw the timeline. The first time period will be placed at the left side of the timeline, and the last time period will be placed at the right side of the timeline.
Similarly, the first event will be placed at the top for that specific time period, and the last event will be placed at the bottom.
## Grouping of time periods in sections/ages
You can group time periods in sections/ages. This is done by adding a line with the keyword `section` followed by the section name.
All subsequent time periods will be placed in this section until a new section is defined.
If no section is defined, all time periods will be placed in the default section.
Let us look at an example, where we have grouped the time periods in sections.
```mermaid-example
timeline
title Timeline of Industrial Revolution
section 17th-20th century
Industry 1.0 : Machinery, Water power, Steam <br>power
Industry 2.0 : Electricity, Internal combustion engine, Mass production
Industry 3.0 : Electronics, Computers, Automation
section 21st century
Industry 4.0 : Internet, Robotics, Internet of Things
Industry 5.0 : Artificial intelligence, Big data,3D printing
```
```mermaid
timeline
title Timeline of Industrial Revolution
section 17th-20th century
Industry 1.0 : Machinery, Water power, Steam <br>power
Industry 2.0 : Electricity, Internal combustion engine, Mass production
Industry 3.0 : Electronics, Computers, Automation
section 21st century
Industry 4.0 : Internet, Robotics, Internet of Things
Industry 5.0 : Artificial intelligence, Big data,3D printing
```
As you can see, the time periods are placed in the sections, and the sections are placed in the order they are defined.
All time periods and events under a given section follow a similar color scheme. This is done to make it easier to see the relationship between time periods and events.
## Wrapping of text for long time-periods or events
By default, the text for time-periods and events will be wrapped if it is too long. This is done to avoid that the text is drawn outside the diagram.
You can also use `<br>` to force a line break.
Let us look at another example, where we have a long time period, and a long event.
```mermaid-example
timeline
title England's History Timeline
section Stone Age
7600 BC : Britain's oldest known house was built in Orkney, Scotland
6000 BC : Sea levels rise and Britain becomes an island.<br> The people who live here are hunter-gatherers.
section Broze Age
2300 BC : People arrive from Europe and settle in Britain. <br>They bring farming and metalworking.
: New styles of pottery and ways of burying the dead appear.
2200 BC : The last major building works are completed at Stonehenge.<br> People now bury their dead in stone circles.
: The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
```
```mermaid
timeline
title England's History Timeline
section Stone Age
7600 BC : Britain's oldest known house was built in Orkney, Scotland
6000 BC : Sea levels rise and Britain becomes an island.<br> The people who live here are hunter-gatherers.
section Broze Age
2300 BC : People arrive from Europe and settle in Britain. <br>They bring farming and metalworking.
: New styles of pottery and ways of burying the dead appear.
2200 BC : The last major building works are completed at Stonehenge.<br> People now bury their dead in stone circles.
: The first metal objects are made in Britain.Some other nice things happen. it is a good time to be alive.
```
```mermaid-example
timeline
title MermaidChart 2023 Timeline
section 2023 Q1 <br> Release Personal Tier
Buttet 1 : sub-point 1a : sub-point 1b
: sub-point 1c
Bullet 2 : sub-point 2a : sub-point 2b
section 2023 Q2 <br> Release XYZ Tier
Buttet 3 : sub-point <br> 3a : sub-point 3b
: sub-point 3c
Bullet 4 : sub-point 4a : sub-point 4b
```
```mermaid
timeline
title MermaidChart 2023 Timeline
section 2023 Q1 <br> Release Personal Tier
Buttet 1 : sub-point 1a : sub-point 1b
: sub-point 1c
Bullet 2 : sub-point 2a : sub-point 2b
section 2023 Q2 <br> Release XYZ Tier
Buttet 3 : sub-point <br> 3a : sub-point 3b
: sub-point 3c
Bullet 4 : sub-point 4a : sub-point 4b
```
## Styling of time periods and events
As explained earlier, each section has a color scheme, and each time period and event under a section follow the similar color scheme.
However, if there is no section defined, then we have two possibilities:
1. Style time periods individually, i.e. each time period(and its coressponding events) will have its own color scheme. This is the DEFAULT behavior.
```mermaid-example
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
```mermaid
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
Note that this is no, section defined, and each time period and its corresponding events will have its own color scheme.
2. Disable the multiColor option using the `disableMultiColor` option. This will make all time periods and events follow the same color scheme.
You will need to add this option either via mermaid.intialize function or directives.
```javascript
mermaid.initialize({
theme: 'base',
startOnLoad: true,
logLevel: 0,
timeline: {
disableMulticolor: false,
},
...
...
```
let us look at same example, where we have disabled the multiColor option.
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'timeline': {'disableMulticolor': true}}}%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'timeline': {'disableMulticolor': true}}}%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
```
### Customizing Color scheme
You can customize the color scheme using the `cScale0` to `cScale11` theme variables. Mermaid allows you to set unique colors for up-to 12, where `cScale0` variable will drive the value of the first section or time-period, `cScale1` will drive the value of the second section and so on.
In case you have more than 12 sections, the color scheme will start to repeat.
NOTE: Default values for these theme variables are picked from the selected theme. If you want to override the default values, you can use the `initialize` call to add your custom theme variable values.
Example:
Now let's override the default values for the `cScale0` to `cScale2` variables:
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
'cScale0': '#ff0000',
'cScale1': '#00ff00',
'cScale2': '#0000ff'
} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
'cScale0': '#ff0000',
'cScale1': '#00ff00',
'cScale2': '#0000ff'
} } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
See how the colors are changed to the values specified in the theme variables.
## Themes
Mermaid supports a bunch of pre-defined themes which you can use to find the right one for you. PS: you can actually override an existing theme's variable to get your own custom theme going. Learn more about theming your diagram [here](../config/theming.md).
The following are the different pre-defined theme options:
- `base`
- `forest`
- `dark`
- `default`
- `neutral`
**NOTE**: To change theme you can either use the `initialize` call or _directives_. Learn more about [directives](../config/directives.md)
Let's put them to use, and see how our sample diagram looks in different themes:
### Base Theme
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
### Forest Theme
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
### Dark Theme
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'dark' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
### Default Theme
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'default' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'default' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
### Neutral Theme
```mermaid-example
%%{init: { 'logLevel': 'debug', 'theme': 'neutral' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'neutral' } }%%
timeline
title History of Social Media Platform
2002 : LinkedIn
2004 : Facebook : Google
2005 : Youtube
2006 : Twitter
2007 : Tumblr
2008 : Instagram
2010 : Pinterest
```
You can also refer the implementation in the live editor [here](https://github.com/mermaid-js/mermaid-live-editor/blob/fcf53c98c25604c90a218104268c339be53035a6/src/lib/util/mermaid.ts) to see how the async loading is done.

View File

@ -1,10 +1,10 @@
{ {
"name": "mermaid-monorepo", "name": "mermaid-monorepo",
"private": true, "private": true,
"version": "9.3.0-rc1", "version": "9.4.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module", "type": "module",
"packageManager": "pnpm@7.17.1", "packageManager": "pnpm@7.25.1",
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",
@ -18,12 +18,12 @@
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts", "build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
"build:mermaid": "pnpm build:vite --mermaid", "build:mermaid": "pnpm build:vite --mermaid",
"build:viz": "pnpm build:mermaid --visualize", "build:viz": "pnpm build:mermaid --visualize",
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly", "build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
"build:watch": "pnpm build:vite --watch", "build:watch": "pnpm build:vite --watch",
"build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"", "build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"",
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"", "dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
"release": "pnpm build", "release": "pnpm build",
"lint": "eslint --cache --ignore-path .gitignore . && pnpm lint:jison && prettier --check .", "lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .",
"lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts", "lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts",
"lint:jison": "ts-node-esm ./scripts/jison/lint.mts", "lint:jison": "ts-node-esm ./scripts/jison/lint.mts",
"cypress": "cypress run", "cypress": "cypress run",
@ -67,17 +67,17 @@
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/prettier": "^2.7.1", "@types/prettier": "^2.7.1",
"@types/rollup-plugin-visualizer": "^4.2.1", "@types/rollup-plugin-visualizer": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.42.1", "@typescript-eslint/parser": "^5.48.2",
"@vitest/coverage-c8": "^0.25.1", "@vitest/coverage-c8": "^0.27.0",
"@vitest/ui": "^0.25.1", "@vitest/ui": "^0.27.0",
"concurrently": "^7.5.0", "concurrently": "^7.5.0",
"coveralls": "^3.1.1", "coveralls": "^3.1.1",
"cypress": "^10.11.0", "cypress": "^10.11.0",
"cypress-image-snapshot": "^4.0.1", "cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.15.13", "esbuild": "^0.17.0",
"eslint": "^8.27.0", "eslint": "^8.32.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.6.0",
"eslint-plugin-cypress": "^2.12.1", "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-html": "^7.1.0", "eslint-plugin-html": "^7.1.0",
"eslint-plugin-jest": "^27.1.5", "eslint-plugin-jest": "^27.1.5",
@ -106,9 +106,9 @@
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.8.4", "typescript": "^4.8.4",
"vite": "^3.2.3", "vite": "^3.2.3",
"vitest": "^0.25.3" "vitest": "^0.27.1"
}, },
"volta": { "volta": {
"node": "18.12.1" "node": "18.13.0"
} }
} }

View File

@ -1,3 +0,0 @@
### Do not refer this package. It is not ready.
### Refer mermaid-mindmap instead.

View File

@ -1,36 +1,25 @@
{ {
"name": "@mermaid-js/mermaid-example-diagram", "name": "@mermaid-js/mermaid-example-diagram",
"version": "9.2.0-rc2", "version": "9.3.0",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Example of external diagram module for MermaidJS.",
"main": "dist/mermaid-mindmap.core.mjs", "module": "dist/mermaid-example-diagram.core.mjs",
"module": "dist/mermaid-mindmap.core.mjs", "types": "dist/detector.d.ts",
"type": "module", "type": "module",
"exports": { "exports": {
".": { ".": {
"require": "./dist/mermaid-example-diagram.min.js", "import": "./dist/mermaid-example-diagram.core.mjs",
"import": "./dist/mermaid-example-diagram.core.mjs" "types": "./dist/detector.d.ts"
}, },
"./*": "./*" "./*": "./*"
}, },
"keywords": [ "keywords": [
"diagram", "diagram",
"markdown", "markdown",
"mindmap", "example",
"mermaid" "mermaid"
], ],
"scripts": { "scripts": {
"clean": "rimraf dist", "prepublishOnly": "pnpm -w run build"
"build:types": "tsc -p ./tsconfig.json --emitDeclarationOnly",
"build:watch": "yarn build:code --watch",
"build:esbuild": "concurrently \"yarn build:code\" \"yarn build:types\"",
"build": "yarn clean; yarn build:esbuild",
"dev": "node .esbuild/serve.cjs",
"release": "yarn build",
"lint": "eslint --cache --ignore-path .gitignore . && yarn lint:jison && prettier --check .",
"lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write .",
"lint:jison": "ts-node-esm src/jison/lint.mts",
"todo-prepare": "concurrently \"husky install ../../.husky\" \"yarn build\"",
"todo-pre-commit": "lint-staged"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -48,9 +37,19 @@
"page" "page"
] ]
}, },
"dependencies": {}, "dependencies": {
"@braintree/sanitize-url": "^6.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.0.0",
"khroma": "^2.0.0",
"non-layered-tidy-tree-layout": "^2.0.2"
},
"devDependencies": { "devDependencies": {
"@types/cytoscape": "^3.19.9",
"concurrently": "^7.5.0", "concurrently": "^7.5.0",
"mermaid": "workspace:*",
"rimraf": "^3.0.2" "rimraf": "^3.0.2"
}, },
"resolutions": { "resolutions": {

View File

@ -1,18 +1,20 @@
// @ts-ignore: TODO Fix ts errors import type { ExternalDiagramDefinition } from 'mermaid';
export const id = 'example-diagram';
/** const id = 'example-diagram';
* Detector function that will be called by mermaid to determine if the diagram is this type of diagram.
*
* @param txt - The diagram text will be passed to the detector
* @returns True if the diagram text matches a diagram of this type
*/
export const detector = (txt: string) => { const detector = (txt: string) => {
return txt.match(/^\s*example-diagram/) !== null; return txt.match(/^\s*example-diagram/) !== null;
}; };
export const loadDiagram = async () => { const loader = async () => {
const { diagram } = await import('./diagram-definition'); const { diagram } = await import('./diagram-definition');
return { id, diagram }; return { id, diagram };
}; };
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@ -12,5 +12,3 @@ export const diagram = {
styles, styles,
injectUtils, injectUtils,
}; };
export { detector, id } from './detector';

View File

@ -1,5 +1,17 @@
import { parser } from './parser/exampleDiagram'; import { parser } from './parser/exampleDiagram';
import db from './exampleDiagramDb'; import * as db from './exampleDiagramDb';
import { injectUtils } from './mermaidUtils';
// Todo fix utils functions for tests
import {
log,
setLogLevel,
getConfig,
sanitizeText,
setupGraphViewBox,
} from '../../mermaid/src/diagram-api/diagramAPI';
injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewBox);
describe('when parsing an info graph it', function () { describe('when parsing an info graph it', function () {
let ex; let ex;
beforeEach(function () { beforeEach(function () {

View File

@ -1,4 +1,8 @@
const warning = () => null; const warning = (s: string) => {
// Todo remove debug code
// eslint-disable-next-line no-console
console.error('Log function was called before initialization', s);
};
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
@ -19,12 +23,11 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
error: warning, error: warning,
fatal: warning, fatal: warning,
}; };
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void; export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
export let getConfig: () => object; export let getConfig: () => object;
export let sanitizeText: (str: string) => string; export let sanitizeText: (str: string) => string;
/** export let commonDb: () => object;
* Placeholder for the real function that will be injected by mermaid.
*/
// eslint-disable @typescript-eslint/no-explicit-any // eslint-disable @typescript-eslint/no-explicit-any
export let setupGraphViewbox: ( export let setupGraphViewbox: (
graph: any, graph: any,
@ -33,23 +36,15 @@ export let setupGraphViewbox: (
useMaxWidth: boolean useMaxWidth: boolean
) => void; ) => void;
/**
* Function called by mermaid that injects utility functions that help the diagram to be a good citizen.
*
* @param _log - log from mermaid/src/diagramAPI.ts
* @param _setLogLevel - setLogLevel from mermaid/src/diagramAPI.ts
* @param _getConfig - getConfig from mermaid/src/diagramAPI.ts
* @param _sanitizeText - sanitizeText from mermaid/src/diagramAPI.ts
* @param _setupGraphViewbox - setupGraphViewbox from mermaid/src/diagramAPI.ts
*/
export const injectUtils = ( export const injectUtils = (
_log: Record<keyof typeof LEVELS, typeof console.log>, _log: Record<keyof typeof LEVELS, typeof console.log>,
_setLogLevel: typeof setLogLevel, _setLogLevel: any,
_getConfig: typeof getConfig, _getConfig: any,
_sanitizeText: typeof sanitizeText, _sanitizeText: any,
_setupGraphViewbox: typeof setupGraphViewbox _setupGraphViewbox: any,
_commonDb: any
) => { ) => {
_log.debug('Mermaid utils injected into example-diagram'); _log.info('Mermaid utils injected');
log.trace = _log.trace; log.trace = _log.trace;
log.debug = _log.debug; log.debug = _log.debug;
log.info = _log.info; log.info = _log.info;
@ -60,4 +55,5 @@ export const injectUtils = (
getConfig = _getConfig; getConfig = _getConfig;
sanitizeText = _sanitizeText; sanitizeText = _sanitizeText;
setupGraphViewbox = _setupGraphViewbox; setupGraphViewbox = _setupGraphViewbox;
commonDb = _commonDb;
}; };

View File

@ -1,5 +1,6 @@
{ {
"extends": "../../tsconfig.json", "extends": "../../tsconfig.json",
"module": "esnext",
"compilerOptions": { "compilerOptions": {
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist" "outDir": "./dist"

View File

@ -1,64 +0,0 @@
{
"name": "@mermaid-js/mermaid-mindmap",
"version": "9.3.0",
"description": "Mindmap diagram module for MermaidJS.",
"module": "dist/mermaid-mindmap.core.mjs",
"types": "dist/detector.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/mermaid-mindmap.core.mjs",
"types": "./dist/detector.d.ts"
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"mindmap",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
},
"repository": {
"type": "git",
"url": "https://github.com/mermaid-js/mermaid"
},
"author": "Knut Sveidqvist",
"license": "MIT",
"standard": {
"ignore": [
"**/parser/*.js",
"dist/**/*.js",
"cypress/**/*.js"
],
"globals": [
"page"
]
},
"dependencies": {
"@braintree/sanitize-url": "^6.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.0.0",
"khroma": "^2.0.0",
"non-layered-tidy-tree-layout": "^2.0.2"
},
"devDependencies": {
"concurrently": "^7.5.0",
"mermaid": "workspace:*",
"rimraf": "^3.0.2"
},
"resolutions": {
"d3": "^7.0.0"
},
"files": [
"dist"
],
"sideEffects": [
"**/*.css",
"**/*.scss"
]
}

View File

@ -1,56 +0,0 @@
const warning = (s: string) => {
// Todo remove debug code
// eslint-disable-next-line no-console
console.error('Log function was called before initialization', s);
};
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
export const LEVELS: Record<LogLevel, number> = {
trace: 0,
debug: 1,
info: 2,
warn: 3,
error: 4,
fatal: 5,
};
export const log: Record<keyof typeof LEVELS, typeof console.log> = {
trace: warning,
debug: warning,
info: warning,
warn: warning,
error: warning,
fatal: warning,
};
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
export let getConfig: () => object;
export let sanitizeText: (str: string) => string;
// eslint-disable @typescript-eslint/no-explicit-any
export let setupGraphViewbox: (
graph: any,
svgElem: any,
padding: any,
useMaxWidth: boolean
) => void;
export const injectUtils = (
_log: Record<keyof typeof LEVELS, typeof console.log>,
_setLogLevel: any,
_getConfig: any,
_sanitizeText: any,
_setupGraphViewbox: any
) => {
_log.info('Mermaid utils injected');
log.trace = _log.trace;
log.debug = _log.debug;
log.info = _log.info;
log.warn = _log.warn;
log.error = _log.error;
log.fatal = _log.fatal;
setLogLevel = _setLogLevel;
getConfig = _getConfig;
sanitizeText = _sanitizeText;
setupGraphViewbox = _setupGraphViewbox;
};

View File

@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"module": "esnext",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["./src/**/*.ts"],
"typeRoots": ["./src/types"]
}

View File

@ -10,7 +10,7 @@
**感谢所有参与进来提交 PR解答疑问的人们! 🙏** **感谢所有参与进来提交 PR解答疑问的人们! 🙏**
<a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/img/book-banner-pre-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a> <a href="https://mermaid-js.github.io/mermaid/landing/"><img src="https://github.com/mermaid-js/mermaid/blob/master/docs/intro/img/book-banner-post-release.jpg" alt="Explore Mermaid.js in depth, with real-world examples, tips & tricks from the creator... The first official book on Mermaid is available for purchase. Check it out!"></a>
## 关于 Mermaid ## 关于 Mermaid

View File

@ -1,6 +1,6 @@
{ {
"name": "mermaid", "name": "mermaid",
"version": "9.3.0", "version": "9.4.0",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.", "description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "./dist/mermaid.min.js", "main": "./dist/mermaid.min.js",
"module": "./dist/mermaid.core.mjs", "module": "./dist/mermaid.core.mjs",
@ -53,17 +53,23 @@
}, },
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^6.0.0", "@braintree/sanitize-url": "^6.0.0",
"cytoscape": "^3.23.0",
"cytoscape-cose-bilkent": "^4.1.0",
"cytoscape-fcose": "^2.1.0",
"d3": "^7.0.0", "d3": "^7.0.0",
"dagre-d3-es": "7.0.6", "dagre-d3-es": "7.0.8",
"dompurify": "2.4.1", "dompurify": "2.4.3",
"elkjs": "^0.8.2",
"khroma": "^2.0.0", "khroma": "^2.0.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"moment-mini": "^2.24.0", "moment": "^2.29.4",
"non-layered-tidy-tree-layout": "^2.0.2", "non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2", "stylis": "^4.1.2",
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/dompurify": "^2.4.0", "@types/dompurify": "^2.4.0",
"@types/jsdom": "^20.0.1", "@types/jsdom": "^20.0.1",
@ -84,10 +90,11 @@
"js-base64": "^3.7.2", "js-base64": "^3.7.2",
"jsdom": "^20.0.2", "jsdom": "^20.0.2",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"moment": "^2.29.4",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"remark": "^14.0.2", "remark": "^14.0.2",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^1.14.0",
"typedoc": "^0.23.18", "typedoc": "^0.23.18",

View File

@ -3,9 +3,10 @@ import { log } from './logger';
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI'; import { getDiagram, registerDiagram } from './diagram-api/diagramAPI';
import { detectType, getDiagramLoader } from './diagram-api/detectType'; import { detectType, getDiagramLoader } from './diagram-api/detectType';
import { extractFrontMatter } from './diagram-api/frontmatter'; import { extractFrontMatter } from './diagram-api/frontmatter';
import { isDetailedError, type DetailedError } from './utils'; import { isDetailedError } from './utils';
import type { DetailedError } from './utils';
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void; export type ParseErrorFunction = (err: string | DetailedError | unknown, hash?: any) => void;
export class Diagram { export class Diagram {
type = 'graph'; type = 'graph';
@ -43,7 +44,7 @@ export class Diagram {
this.parser.parser.yy = this.db; this.parser.parser.yy = this.db;
if (diagram.init) { if (diagram.init) {
diagram.init(cnf); diagram.init(cnf);
log.debug('Initialized diagram ' + this.type, cnf); log.info('Initialized diagram ' + this.type, cnf);
} }
this.txt += '\n'; this.txt += '\n';

View File

@ -6,6 +6,13 @@ describe('accessibility', () => {
const fauxSvgNode = new MockedD3(); const fauxSvgNode = new MockedD3();
describe('setA11yDiagramInfo', () => { describe('setA11yDiagramInfo', () => {
it('sets the svg element role to "graphics-document document"', () => {
// @ts-ignore Required to easily handle the d3 select types
const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
setA11yDiagramInfo(fauxSvgNode, 'flowchart');
expect(svgAttrSpy).toHaveBeenCalledWith('role', 'graphics-document document');
});
it('sets the aria-roledescription to the diagram type', () => { it('sets the aria-roledescription to the diagram type', () => {
// @ts-ignore Required to easily handle the d3 select types // @ts-ignore Required to easily handle the d3 select types
const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode); const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
@ -13,11 +20,12 @@ describe('accessibility', () => {
expect(svgAttrSpy).toHaveBeenCalledWith('aria-roledescription', 'flowchart'); expect(svgAttrSpy).toHaveBeenCalledWith('aria-roledescription', 'flowchart');
}); });
it('does nothing if the diagram type is empty', () => { it('does not set the aria-roledescription if the diagram type is empty', () => {
// @ts-ignore Required to easily handle the d3 select types // @ts-ignore Required to easily handle the d3 select types
const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode); const svgAttrSpy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
setA11yDiagramInfo(fauxSvgNode, ''); setA11yDiagramInfo(fauxSvgNode, '');
expect(svgAttrSpy).not.toHaveBeenCalled(); expect(svgAttrSpy).toHaveBeenCalledTimes(1);
expect(svgAttrSpy).toHaveBeenCalledWith('role', expect.anything()); // only called to set the role
}); });
}); });

View File

@ -1,5 +1,8 @@
/** /**
* Accessibility (a11y) functions, types, helpers * Accessibility (a11y) functions, types, helpers
* @see https://www.w3.org/WAI/
* @see https://www.w3.org/TR/wai-aria-1.1/
* @see https://www.w3.org/TR/svg-aam-1.0/
* *
*/ */
import { D3Element } from './mermaidAPI'; import { D3Element } from './mermaidAPI';
@ -7,12 +10,24 @@ import { D3Element } from './mermaidAPI';
import isEmpty from 'lodash-es/isEmpty.js'; import isEmpty from 'lodash-es/isEmpty.js';
/** /**
* Add aria-roledescription to the svg element to the diagramType * SVG element role:
* The SVG element role _should_ be set to 'graphics-document' per SVG standard
* but in practice is not always done by browsers, etc. (As of 2022-12-08).
* A fallback role of 'document' should be set for those browsers, etc., that only support ARIA 1.0.
*
* @see https://www.w3.org/TR/svg-aam-1.0/#roleMappingGeneralRules
* @see https://www.w3.org/TR/graphics-aria-1.0/#graphics-document
*/
const SVG_ROLE = 'graphics-document document';
/**
* Add role and aria-roledescription to the svg element
* *
* @param svg - d3 object that contains the SVG HTML element * @param svg - d3 object that contains the SVG HTML element
* @param diagramType - diagram name for to the aria-roledescription * @param diagramType - diagram name for to the aria-roledescription
*/ */
export function setA11yDiagramInfo(svg: D3Element, diagramType: string | null | undefined) { export function setA11yDiagramInfo(svg: D3Element, diagramType: string | null | undefined) {
svg.attr('role', SVG_ROLE);
if (!isEmpty(diagramType)) { if (!isEmpty(diagramType)) {
svg.attr('aria-roledescription', diagramType); svg.attr('aria-roledescription', diagramType);
} }

View File

@ -26,6 +26,7 @@ export interface MermaidConfig {
sequence?: SequenceDiagramConfig; sequence?: SequenceDiagramConfig;
gantt?: GanttDiagramConfig; gantt?: GanttDiagramConfig;
journey?: JourneyDiagramConfig; journey?: JourneyDiagramConfig;
timeline?: TimelineDiagramConfig;
class?: ClassDiagramConfig; class?: ClassDiagramConfig;
state?: StateDiagramConfig; state?: StateDiagramConfig;
er?: ErDiagramConfig; er?: ErDiagramConfig;
@ -292,6 +293,30 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
sectionColours?: string[]; sectionColours?: string[];
} }
export interface TimelineDiagramConfig extends BaseDiagramConfig {
diagramMarginX?: number;
diagramMarginY?: number;
leftMargin?: number;
width?: number;
height?: number;
boxMargin?: number;
boxTextMargin?: number;
noteMargin?: number;
messageMargin?: number;
messageAlign?: string;
bottomMarginAdj?: number;
rightAngles?: boolean;
taskFontSize?: string | number;
taskFontFamily?: string;
taskMargin?: number;
activationWidth?: number;
textPlacement?: string;
actorColours?: string[];
sectionFills?: string[];
sectionColours?: string[];
disableMulticolor?: boolean;
}
export interface GanttDiagramConfig extends BaseDiagramConfig { export interface GanttDiagramConfig extends BaseDiagramConfig {
titleTopMargin?: number; titleTopMargin?: number;
barHeight?: number; barHeight?: number;

View File

@ -107,6 +107,7 @@ export const insertEdgeLabel = (elem, edge) => {
terminalLabels[edge.id].endRight = endEdgeLabelRight; terminalLabels[edge.id].endRight = endEdgeLabelRight;
setTerminalWidth(fo, edge.endLabelRight); setTerminalWidth(fo, edge.endLabelRight);
} }
return labelElement;
}; };
/** /**

View File

@ -142,7 +142,7 @@ const point = (elem, type) => {
.append('marker') .append('marker')
.attr('id', type + '-pointEnd') .attr('id', type + '-pointEnd')
.attr('class', 'marker ' + type) .attr('class', 'marker ' + type)
.attr('viewBox', '0 0 10 10') .attr('viewBox', '0 0 12 20')
.attr('refX', 10) .attr('refX', 10)
.attr('refY', 5) .attr('refY', 5)
.attr('markerUnits', 'userSpaceOnUse') .attr('markerUnits', 'userSpaceOnUse')

View File

@ -1007,6 +1007,7 @@ const class_box = (parent, node) => {
}; };
const shapes = { const shapes = {
rhombus: question,
question, question,
rect, rect,
labelRect, labelRect,
@ -1064,6 +1065,7 @@ export const insertNode = (elem, node, dir) => {
if (node.haveCallback) { if (node.haveCallback) {
nodeElems[node.id].attr('class', nodeElems[node.id].attr('class') + ' clickable'); nodeElems[node.id].attr('class', nodeElems[node.id].attr('class') + ' clickable');
} }
return newEl;
}; };
export const setNodeElem = (elem, node) => { export const setNodeElem = (elem, node) => {
nodeElems[node.id] = elem; nodeElems[node.id] = elem;

View File

@ -247,12 +247,13 @@ const config: Partial<MermaidConfig> = {
/** /**
* | Parameter | Description | Type | Required | Values | * | Parameter | Description | Type | Required | Values |
* | --------------- | ----------- | ------- | -------- | ----------------------- | * | --------------- | ----------- | ------- | -------- | ----------------------- |
* | defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper | * | defaultRenderer | See notes | boolean | 4 | dagre-d3, dagre-wrapper, elk |
* *
* **Notes:** * **Notes:**
* *
* Decides which rendering engine that is to be used for the rendering. Legal values are: * Decides which rendering engine that is to be used for the rendering. Legal values are:
* dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid * dagre-d3 dagre-wrapper - wrapper for dagre implemented in mermaid, elk for layout using
* elkjs
* *
* Default value: 'dagre-wrapper' * Default value: 'dagre-wrapper'
*/ */
@ -861,6 +862,156 @@ const config: Partial<MermaidConfig> = {
sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'], sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'],
sectionColours: ['#fff'], sectionColours: ['#fff'],
}, },
/** The object containing configurations specific for timeline diagrams */
timeline: {
/**
* | Parameter | Description | Type | Required | Values |
* | -------------- | ---------------------------------------------------- | ------- | -------- | ------------------ |
* | diagramMarginX | Margin to the right and left of the sequence diagram | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 50
*/
diagramMarginX: 50,
/**
* | Parameter | Description | Type | Required | Values |
* | -------------- | -------------------------------------------------- | ------- | -------- | ------------------ |
* | diagramMarginY | Margin to the over and under the sequence diagram. | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 10
*/
diagramMarginY: 10,
/**
* | Parameter | Description | Type | Required | Values |
* | ----------- | --------------------- | ------- | -------- | ------------------ |
* | actorMargin | Margin between actors | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 50
*/
leftMargin: 150,
/**
* | Parameter | Description | Type | Required | Values |
* | --------- | -------------------- | ------- | -------- | ------------------ |
* | width | Width of actor boxes | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 150
*/
width: 150,
/**
* | Parameter | Description | Type | Required | Values |
* | --------- | --------------------- | ------- | -------- | ------------------ |
* | height | Height of actor boxes | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 65
*/
height: 50,
/**
* | Parameter | Description | Type | Required | Values |
* | --------- | ------------------------ | ------- | -------- | ------------------ |
* | boxMargin | Margin around loop boxes | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 10
*/
boxMargin: 10,
/**
* | Parameter | Description | Type | Required | Values |
* | ------------- | -------------------------------------------- | ------- | -------- | ------------------ |
* | boxTextMargin | Margin around the text in loop/alt/opt boxes | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 5
*/
boxTextMargin: 5,
/**
* | Parameter | Description | Type | Required | Values |
* | ---------- | ------------------- | ------- | -------- | ------------------ |
* | noteMargin | Margin around notes | Integer | Required | Any Positive Value |
*
* **Notes:** Default value: 10
*/
noteMargin: 10,
/**
* | Parameter | Description | Type | Required | Values |
* | ------------- | ----------------------- | ------- | -------- | ------------------ |
* | messageMargin | Space between messages. | Integer | Required | Any Positive Value |
*
* **Notes:**
*
* Space between messages.
*
* Default value: 35
*/
messageMargin: 35,
/**
* | Parameter | Description | Type | Required | Values |
* | ------------ | --------------------------- | ---- | -------- | ------------------------- |
* | messageAlign | Multiline message alignment | 3 | 4 | 'left', 'center', 'right' |
*
* **Notes:** Default value: 'center'
*/
messageAlign: 'center',
/**
* | Parameter | Description | Type | Required | Values |
* | --------------- | ------------------------------------------ | ------- | -------- | ------------------ |
* | bottomMarginAdj | Prolongs the edge of the diagram downwards | Integer | 4 | Any Positive Value |
*
* **Notes:**
*
* Depending on css styling this might need adjustment.
*
* Default value: 1
*/
bottomMarginAdj: 1,
/**
* | Parameter | Description | Type | Required | Values |
* | ----------- | ----------- | ------- | -------- | ----------- |
* | useMaxWidth | See notes | boolean | 4 | true, false |
*
* **Notes:**
*
* When this flag is set the height and width is set to 100% and is then scaling with the
* available space if not the absolute space required is used.
*
* Default value: true
*/
useMaxWidth: true,
/**
* | Parameter | Description | Type | Required | Values |
* | ----------- | --------------------------------- | ---- | -------- | ----------- |
* | rightAngles | Curved Arrows become Right Angles | 3 | 4 | true, false |
*
* **Notes:**
*
* This will display arrows that start and begin at the same node as right angles, rather than a
* curves
*
* Default value: false
*/
rightAngles: false,
taskFontSize: 14,
taskFontFamily: '"Open Sans", sans-serif',
taskMargin: 50,
// width of activation box
activationWidth: 10,
// text placement as: tspan | fo | old only text as before
textPlacement: 'fo',
actorColours: ['#8FBC8F', '#7CFC00', '#00FFFF', '#20B2AA', '#B0E0E6', '#FFFFE0'],
sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'],
sectionColours: ['#fff'],
disableMulticolor: false,
},
class: { class: {
/** /**
* ### titleTopMargin * ### titleTopMargin

View File

@ -1,6 +1,11 @@
import { MermaidConfig } from '../config.type'; import { MermaidConfig } from '../config.type';
import { log } from '../logger'; import { log } from '../logger';
import { DetectorRecord, DiagramDetector, DiagramLoader } from './types'; import type {
DetectorRecord,
DiagramDetector,
DiagramLoader,
ExternalDiagramDefinition,
} from './types';
import { frontMatterRegex } from './frontmatter'; import { frontMatterRegex } from './frontmatter';
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi; const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
@ -42,11 +47,18 @@ export const detectType = function (text: string, config?: MermaidConfig): strin
throw new Error(`No diagram type detected for text: ${text}`); throw new Error(`No diagram type detected for text: ${text}`);
}; };
export const registerLazyLoadedDiagrams = (...diagrams: ExternalDiagramDefinition[]) => {
for (const { id, detector, loader } of diagrams) {
addDetector(id, detector, loader);
}
};
export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => { export const addDetector = (key: string, detector: DiagramDetector, loader?: DiagramLoader) => {
if (detectors[key]) { if (detectors[key]) {
throw new Error(`Detector with key ${key} already exists`); log.error(`Detector with key ${key} already exists`);
} else {
detectors[key] = { detector, loader };
} }
detectors[key] = { detector, loader };
log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`); log.debug(`Detector with key ${key} added${loader ? ' with loader' : ''}`);
}; };

View File

@ -1,5 +1,4 @@
import { registerDiagram } from './diagramAPI'; import { registerDiagram } from './diagramAPI';
// @ts-ignore: TODO Fix ts errors // @ts-ignore: TODO Fix ts errors
import gitGraphParser from '../diagrams/git/parser/gitGraph'; import gitGraphParser from '../diagrams/git/parser/gitGraph';
import { gitGraphDetector } from '../diagrams/git/gitGraphDetector'; import { gitGraphDetector } from '../diagrams/git/gitGraphDetector';
@ -94,6 +93,13 @@ import { setConfig } from '../config';
import errorRenderer from '../diagrams/error/errorRenderer'; import errorRenderer from '../diagrams/error/errorRenderer';
import errorStyles from '../diagrams/error/styles'; import errorStyles from '../diagrams/error/styles';
import flowchartElk from '../diagrams/flowchart/elk/detector';
import { registerLazyLoadedDiagrams } from './detectType';
// Lazy loaded diagrams
import timelineDetector from '../diagrams/timeline/detector';
import mindmapDetector from '../diagrams/mindmap/detector';
let hasLoadedDiagrams = false; let hasLoadedDiagrams = false;
export const addDiagrams = () => { export const addDiagrams = () => {
if (hasLoadedDiagrams) { if (hasLoadedDiagrams) {
@ -102,6 +108,8 @@ export const addDiagrams = () => {
// This is added here to avoid race-conditions. // This is added here to avoid race-conditions.
// We could optimize the loading logic somehow. // We could optimize the loading logic somehow.
hasLoadedDiagrams = true; hasLoadedDiagrams = true;
registerLazyLoadedDiagrams(flowchartElk, timelineDetector, mindmapDetector);
registerDiagram( registerDiagram(
'error', 'error',
// Special diagram with error messages but setup as a regular diagram // Special diagram with error messages but setup as a regular diagram
@ -125,6 +133,33 @@ export const addDiagrams = () => {
}, },
(text) => text.toLowerCase().trim() === 'error' (text) => text.toLowerCase().trim() === 'error'
); );
registerDiagram(
'---',
// --- diagram type may appear if YAML front-matter is not parsed correctly
{
db: {
clear: () => {
// Quite ok, clear needs to be there for --- to work as a regular diagram
},
},
styles: errorStyles, // should never be used
renderer: errorRenderer, // should never be used
parser: {
parser: { yy: {} },
parse: () => {
throw new Error(
'Diagrams beginning with --- are not valid. ' +
'If you were trying to use a YAML front-matter, please ensure that ' +
"you've correctly opened and closed the YAML front-matter with unindented `---` blocks"
);
},
},
init: () => null, // no op
},
(text) => {
return text.toLowerCase().trimStart().startsWith('---');
}
);
registerDiagram( registerDiagram(
'c4', 'c4',

View File

@ -5,6 +5,8 @@ import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox'; import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
import { addStylesForDiagram } from '../styles'; import { addStylesForDiagram } from '../styles';
import { DiagramDefinition, DiagramDetector } from './types'; import { DiagramDefinition, DiagramDetector } from './types';
import * as _commonDb from '../commonDb';
import { parseDirective as _parseDirective } from '../directiveUtils';
/* /*
Packaging and exposing resources for external diagrams so that they can import Packaging and exposing resources for external diagrams so that they can import
@ -16,6 +18,11 @@ export const setLogLevel = _setLogLevel;
export const getConfig = _getConfig; export const getConfig = _getConfig;
export const sanitizeText = (text: string) => _sanitizeText(text, getConfig()); export const sanitizeText = (text: string) => _sanitizeText(text, getConfig());
export const setupGraphViewbox = _setupGraphViewbox; export const setupGraphViewbox = _setupGraphViewbox;
export const getCommonDb = () => {
return _commonDb;
};
export const parseDirective = (p: any, statement: string, context: string, type: string) =>
_parseDirective(p, statement, context, type);
const diagrams: Record<string, DiagramDefinition> = {}; const diagrams: Record<string, DiagramDefinition> = {};
export interface Detectors { export interface Detectors {
@ -46,7 +53,15 @@ export const registerDiagram = (
addStylesForDiagram(id, diagram.styles); addStylesForDiagram(id, diagram.styles);
if (diagram.injectUtils) { if (diagram.injectUtils) {
diagram.injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox); diagram.injectUtils(
log,
setLogLevel,
getConfig,
sanitizeText,
setupGraphViewbox,
getCommonDb(),
parseDirective
);
} }
}; };

View File

@ -6,6 +6,8 @@ export interface InjectUtils {
_getConfig: any; _getConfig: any;
_sanitizeText: any; _sanitizeText: any;
_setupGraphViewbox: any; _setupGraphViewbox: any;
_commonDb: any;
_parseDirective: any;
} }
/** /**
@ -29,7 +31,9 @@ export interface DiagramDefinition {
_setLogLevel: InjectUtils['_setLogLevel'], _setLogLevel: InjectUtils['_setLogLevel'],
_getConfig: InjectUtils['_getConfig'], _getConfig: InjectUtils['_getConfig'],
_sanitizeText: InjectUtils['_sanitizeText'], _sanitizeText: InjectUtils['_sanitizeText'],
_setupGraphViewbox: InjectUtils['_setupGraphViewbox'] _setupGraphViewbox: InjectUtils['_setupGraphViewbox'],
_commonDb: InjectUtils['_commonDb'],
_parseDirective: InjectUtils['_parseDirective']
) => void; ) => void;
} }

View File

@ -234,7 +234,7 @@ export const drawC4Shape = function (elem, c4Shape, conf) {
const c4ShapeElem = elem.append('g'); const c4ShapeElem = elem.append('g');
c4ShapeElem.attr('class', 'person-man'); c4ShapeElem.attr('class', 'person-man');
// <rect fill="#08427B" height="119.2188" rx="2.5" ry="2.5" style="stroke:#073B6F;stroke-width:0.5;" width="110" x="120" y="7"/> // <rect fill="#08427B" height="119.2188" rx="2.5" ry="2.5" stroke="#073B6F" stroke-width="0.5" width="110" x="120" y="7"/>
// draw rect of c4Shape // draw rect of c4Shape
const rect = getNoteRect(); const rect = getNoteRect();
switch (c4Shape.typeC4Shape.text) { switch (c4Shape.typeC4Shape.text) {
@ -251,9 +251,10 @@ export const drawC4Shape = function (elem, c4Shape, conf) {
rect.fill = fillColor; rect.fill = fillColor;
rect.width = c4Shape.width; rect.width = c4Shape.width;
rect.height = c4Shape.height; rect.height = c4Shape.height;
rect.style = 'stroke:' + strokeColor + ';stroke-width:0.5;'; rect.stroke = strokeColor;
rect.rx = 2.5; rect.rx = 2.5;
rect.ry = 2.5; rect.ry = 2.5;
rect.attrs = { 'stroke-width': 0.5 };
drawRect(c4ShapeElem, rect); drawRect(c4ShapeElem, rect);
break; break;
case 'system_db': case 'system_db':
@ -371,12 +372,12 @@ export const drawC4Shape = function (elem, c4Shape, conf) {
textFontConf = conf[c4Shape.typeC4Shape.text + 'Font'](); textFontConf = conf[c4Shape.typeC4Shape.text + 'Font']();
textFontConf.fontColor = fontColor; textFontConf.fontColor = fontColor;
if (c4Shape.thchn && c4Shape.thchn.text !== '') { if (c4Shape.techn && c4Shape.techn?.text !== '') {
_drawTextCandidateFunc(conf)( _drawTextCandidateFunc(conf)(
c4Shape.thchn.text, c4Shape.techn.text,
c4ShapeElem, c4ShapeElem,
c4Shape.x, c4Shape.x,
c4Shape.y + c4Shape.thchn.Y, c4Shape.y + c4Shape.techn.Y,
c4Shape.width, c4Shape.width,
c4Shape.height, c4Shape.height,
{ fill: fontColor, 'font-style': 'italic' }, { fill: fontColor, 'font-style': 'italic' },

View File

@ -68,5 +68,7 @@ describe('generic parser', function () {
expect(parseGenericTypes('test ~Array~Array~string[]~~~')).toEqual( expect(parseGenericTypes('test ~Array~Array~string[]~~~')).toEqual(
'test <Array<Array<string[]>>>' 'test <Array<Array<string[]>>>'
); );
expect(parseGenericTypes('~test')).toEqual('~test');
expect(parseGenericTypes('~test Array~string~')).toEqual('~test Array<string>');
}); });
}); });

View File

@ -154,11 +154,17 @@ export const evaluate = (val?: string | boolean): boolean =>
export const parseGenericTypes = function (text: string): string { export const parseGenericTypes = function (text: string): string {
let cleanedText = text; let cleanedText = text;
if (text.includes('~')) { if (text.split('~').length - 1 >= 2) {
cleanedText = cleanedText.replace(/~([^~].*)/, '<$1'); let newCleanedText = cleanedText;
cleanedText = cleanedText.replace(/~([^~]*)$/, '>$1');
return parseGenericTypes(cleanedText); // use a do...while loop instead of replaceAll to detect recursion
// e.g. Array~Array~T~~
do {
cleanedText = newCleanedText;
newCleanedText = cleanedText.replace(/~([^\s,:;]+)~/, '<$1>');
} while (newCleanedText != cleanedText);
return parseGenericTypes(newCleanedText);
} else { } else {
return cleanedText; return cleanedText;
} }

View File

@ -7,7 +7,7 @@ import utils from '../../utils';
import erMarkers from './erMarkers'; import erMarkers from './erMarkers';
import { configureSvgSize } from '../../setupGraphViewbox'; import { configureSvgSize } from '../../setupGraphViewbox';
import { parseGenericTypes } from '../common/common'; import { parseGenericTypes } from '../common/common';
import { v4 as uuid4 } from 'uuid'; import { v5 as uuid5 } from 'uuid';
/** Regex used to remove chars from the entity name so the result can be used in an id */ /** Regex used to remove chars from the entity name so the result can be used in an id */
const BAD_ID_CHARS_REGEXP = /[^\dA-Za-z](\W)*/g; const BAD_ID_CHARS_REGEXP = /[^\dA-Za-z](\W)*/g;
@ -59,7 +59,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
// Check to see if any of the attributes has a key or a comment // Check to see if any of the attributes has a key or a comment
attributes.forEach((item) => { attributes.forEach((item) => {
if (item.attributeKeyType !== undefined) { if (item.attributeKeyTypeList !== undefined && item.attributeKeyTypeList.length > 0) {
hasKeyType = true; hasKeyType = true;
} }
@ -112,6 +112,9 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
nodeHeight = Math.max(typeBBox.height, nameBBox.height); nodeHeight = Math.max(typeBBox.height, nameBBox.height);
if (hasKeyType) { if (hasKeyType) {
const keyTypeNodeText =
item.attributeKeyTypeList !== undefined ? item.attributeKeyTypeList.join(',') : '';
const keyTypeNode = groupNode const keyTypeNode = groupNode
.append('text') .append('text')
.classed('er entityLabel', true) .classed('er entityLabel', true)
@ -122,7 +125,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
.style('text-anchor', 'left') .style('text-anchor', 'left')
.style('font-family', getConfig().fontFamily) .style('font-family', getConfig().fontFamily)
.style('font-size', attrFontSize + 'px') .style('font-size', attrFontSize + 'px')
.text(item.attributeKeyType || ''); .text(keyTypeNodeText);
attributeNode.kn = keyTypeNode; attributeNode.kn = keyTypeNode;
const keyTypeBBox = keyTypeNode.node().getBBox(); const keyTypeBBox = keyTypeNode.node().getBBox();
@ -643,9 +646,24 @@ export const draw = function (text, id, _version, diagObj) {
svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`); svg.attr('viewBox', `${svgBounds.x - padding} ${svgBounds.y - padding} ${width} ${height}`);
}; // draw }; // draw
/**
* UUID namespace for ER diagram IDs
*
* This can be generated via running:
*
* ```js
* const { v5: uuid5 } = await import('uuid');
* uuid5(
* 'https://mermaid-js.github.io/mermaid/syntax/entityRelationshipDiagram.html',
* uuid5.URL
* );
* ```
*/
const MERMAID_ERDIAGRAM_UUID = '28e9f9db-3c8d-5aa5-9faf-44286ae5937c';
/** /**
* Return a unique id based on the given string. Start with the prefix, then a hyphen, then the * Return a unique id based on the given string. Start with the prefix, then a hyphen, then the
* simplified str, then a hyphen, then a unique uuid. (Hyphens are only included if needed.) * simplified str, then a hyphen, then a unique uuid based on the str. (Hyphens are only included if needed.)
* Although the official XML standard for ids says that many more characters are valid in the id, * Although the official XML standard for ids says that many more characters are valid in the id,
* this keeps things simple by accepting only A-Za-z0-9. * this keeps things simple by accepting only A-Za-z0-9.
* *
@ -656,7 +674,11 @@ export const draw = function (text, id, _version, diagObj) {
*/ */
export function generateId(str = '', prefix = '') { export function generateId(str = '', prefix = '') {
const simplifiedStr = str.replace(BAD_ID_CHARS_REGEXP, ''); const simplifiedStr = str.replace(BAD_ID_CHARS_REGEXP, '');
return `${strWithHyphen(prefix)}${strWithHyphen(simplifiedStr)}${uuid4()}`; // we use `uuid v5` so that UUIDs are consistent given a string.
return `${strWithHyphen(prefix)}${strWithHyphen(simplifiedStr)}${uuid5(
str,
MERMAID_ERDIAGRAM_UUID
)}`;
} }
/** /**

View File

@ -0,0 +1,12 @@
import { generateId } from './erRenderer';
describe('erRenderer', () => {
describe('generateId', () => {
it('should be deterministic', () => {
const id1 = generateId('hello world', 'my-prefix');
const id2 = generateId('hello world', 'my-prefix');
expect(id1).toBe(id2);
});
});
});

View File

@ -28,10 +28,11 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
\"[^"]*\" return 'WORD'; \"[^"]*\" return 'WORD';
"erDiagram" return 'ER_DIAGRAM'; "erDiagram" return 'ER_DIAGRAM';
"{" { this.begin("block"); return 'BLOCK_START'; } "{" { this.begin("block"); return 'BLOCK_START'; }
<block>"," return 'COMMA';
<block>\s+ /* skip whitespace in block */ <block>\s+ /* skip whitespace in block */
<block>\b((?:PK)|(?:FK))\b return 'ATTRIBUTE_KEY' <block>\b((?:PK)|(?:FK)|(?:UK))\b return 'ATTRIBUTE_KEY'
<block>(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD'; <block>(.*?)[~](.*?)*[~] return 'ATTRIBUTE_WORD';
<block>[A-Za-z][A-Za-z0-9\-_\[\]]* return 'ATTRIBUTE_WORD' <block>[A-Za-z_][A-Za-z0-9\-_\[\]\(\)]* return 'ATTRIBUTE_WORD'
<block>\"[^"]*\" return 'COMMENT'; <block>\"[^"]*\" return 'COMMENT';
<block>[\n]+ /* nothing */ <block>[\n]+ /* nothing */
<block>"}" { this.popState(); return 'BLOCK_STOP'; } <block>"}" { this.popState(); return 'BLOCK_STOP'; }
@ -80,7 +81,7 @@ start
document document
: /* empty */ { $$ = [] } : /* empty */ { $$ = [] }
| document line {$1.push($2);$$ = $1} | document line {$1.push($2);$$ = $1}
; ;
line line
@ -131,11 +132,12 @@ attributes
attribute attribute
: attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; } : attributeType attributeName { $$ = { attributeType: $1, attributeName: $2 }; }
| attributeType attributeName attributeKeyType { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3 }; } | attributeType attributeName attributeKeyTypeList { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3 }; }
| attributeType attributeName attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; } | attributeType attributeName attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeComment: $3 }; }
| attributeType attributeName attributeKeyType attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyType: $3, attributeComment: $4 }; } | attributeType attributeName attributeKeyTypeList attributeComment { $$ = { attributeType: $1, attributeName: $2, attributeKeyTypeList: $3, attributeComment: $4 }; }
; ;
attributeType attributeType
: ATTRIBUTE_WORD { $$=$1; } : ATTRIBUTE_WORD { $$=$1; }
; ;
@ -144,6 +146,11 @@ attributeName
: ATTRIBUTE_WORD { $$=$1; } : ATTRIBUTE_WORD { $$=$1; }
; ;
attributeKeyTypeList
: attributeKeyType { $$ = [$1]; }
| attributeKeyTypeList COMMA attributeKeyType { $1.push($3); $$ = $1; }
;
attributeKeyType attributeKeyType
: ATTRIBUTE_KEY { $$=$1; } : ATTRIBUTE_KEY { $$=$1; }
; ;

View File

@ -135,6 +135,37 @@ describe('when parsing ER diagram it...', function () {
}); });
}); });
describe('attribute name', () => {
it('should allow alphanumeric characters, dashes, underscores and brackets (not leading chars)', function () {
const entity = 'BOOK';
const attribute1 = 'string myBookTitle';
const attribute2 = 'string MYBOOKSUBTITLE_1';
const attribute3 = 'string author-ref[name](1)';
erDiagram.parser.parse(
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n}`
);
const entities = erDb.getEntities();
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(3);
expect(entities[entity].attributes[0].attributeName).toBe('myBookTitle');
expect(entities[entity].attributes[1].attributeName).toBe('MYBOOKSUBTITLE_1');
expect(entities[entity].attributes[2].attributeName).toBe('author-ref[name](1)');
});
it('should not allow leading numbers, dashes or brackets', function () {
const entity = 'BOOK';
const nonLeadingChars = '0-[]()';
[...nonLeadingChars].forEach((nonLeadingChar) => {
expect(() => {
const attribute = `string ${nonLeadingChar}author`;
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute}\n}`);
}).toThrow();
});
});
});
it('should allow an entity with a single attribute to be defined', function () { it('should allow an entity with a single attribute to be defined', function () {
const entity = 'BOOK'; const entity = 'BOOK';
const attribute = 'string title'; const attribute = 'string title';
@ -176,17 +207,40 @@ describe('when parsing ER diagram it...', function () {
expect(entities[entity].attributes.length).toBe(1); expect(entities[entity].attributes.length).toBe(1);
}); });
it('should allow an entity with attribute starting with fk or pk and a comment', function () { it('should allow an entity with attribute starting with fk, pk or uk and a comment', function () {
const entity = 'BOOK'; const entity = 'BOOK';
const attribute1 = 'int fk_title FK'; const attribute1 = 'int fk_title FK';
const attribute2 = 'string pk_author PK'; const attribute2 = 'string pk_author PK';
const attribute3 = 'float pk_price PK "comment"'; const attribute3 = 'string uk_address UK';
const attribute4 = 'float pk_price PK "comment"';
erDiagram.parser.parse( erDiagram.parser.parse(
`erDiagram\n${entity} {\n${attribute1} \n\n${attribute2}\n${attribute3}\n}` `erDiagram\n${entity} {\n${attribute1} \n\n${attribute2}\n${attribute3}\n${attribute4}\n}`
); );
const entities = erDb.getEntities(); const entities = erDb.getEntities();
expect(entities[entity].attributes.length).toBe(3); expect(entities[entity].attributes.length).toBe(4);
});
it('should allow an entity with attributes that have many constraints and comments', function () {
const entity = 'CUSTOMER';
const attribute1 = 'int customer_number PK, FK "comment1"';
const attribute2 = 'datetime customer_status_start_datetime PK,UK, FK';
const attribute3 = 'datetime customer_status_end_datetime PK , UK "comment3"';
const attribute4 = 'string customer_firstname';
const attribute5 = 'string customer_lastname "comment5"';
erDiagram.parser.parse(
`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n${attribute3}\n${attribute4}\n${attribute5}\n}`
);
const entities = erDb.getEntities();
expect(entities[entity].attributes[0].attributeKeyTypeList).toEqual(['PK', 'FK']);
expect(entities[entity].attributes[0].attributeComment).toBe('comment1');
expect(entities[entity].attributes[1].attributeKeyTypeList).toEqual(['PK', 'UK', 'FK']);
expect(entities[entity].attributes[2].attributeKeyTypeList).toEqual(['PK', 'UK']);
expect(entities[entity].attributes[2].attributeComment).toBe('comment3');
expect(entities[entity].attributes[3].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeKeyTypeList).toBeUndefined();
expect(entities[entity].attributes[4].attributeComment).toBe('comment5');
}); });
it('should allow an entity with attribute that has a generic type', function () { it('should allow an entity with attribute that has a generic type', function () {
@ -214,6 +268,19 @@ describe('when parsing ER diagram it...', function () {
expect(entities[entity].attributes.length).toBe(2); expect(entities[entity].attributes.length).toBe(2);
}); });
it('should allow an entity with attribute that is a limited length string', function () {
const entity = 'BOOK';
const attribute1 = 'character(10) isbn FK';
const attribute2 = 'varchar(5) postal_code "Five digits"';
erDiagram.parser.parse(`erDiagram\n${entity} {\n${attribute1}\n${attribute2}\n}`);
const entities = erDb.getEntities();
expect(Object.keys(entities).length).toBe(1);
expect(entities[entity].attributes.length).toBe(2);
expect(entities[entity].attributes[0].attributeType).toBe('character(10)');
expect(entities[entity].attributes[1].attributeType).toBe('varchar(5)');
});
it('should allow an entity with multiple attributes to be defined', function () { it('should allow an entity with multiple attributes to be defined', function () {
const entity = 'BOOK'; const entity = 'BOOK';
const attribute1 = 'string title'; const attribute1 = 'string title';

View File

@ -0,0 +1,55 @@
import plugin from './detector';
import { describe, it } from 'vitest';
const { detector } = plugin;
describe('flowchart-elk detector', () => {
it('should fail for dagre-d3', () => {
expect(
detector('flowchart', {
flowchart: {
defaultRenderer: 'dagre-d3',
},
})
).toBe(false);
});
it('should fail for dagre-wrapper', () => {
expect(
detector('flowchart', {
flowchart: {
defaultRenderer: 'dagre-wrapper',
},
})
).toBe(false);
});
it('should succeed for elk', () => {
expect(
detector('flowchart', {
flowchart: {
defaultRenderer: 'elk',
},
})
).toBe(true);
expect(
detector('graph', {
flowchart: {
defaultRenderer: 'elk',
},
})
).toBe(true);
});
it('should detect flowchart-elk', () => {
expect(detector('flowchart-elk')).toBe(true);
});
it('should not detect class with defaultRenderer set to elk', () => {
expect(
detector('class', {
flowchart: {
defaultRenderer: 'elk',
},
})
).toBe(false);
});
});

View File

@ -0,0 +1,29 @@
import type { MermaidConfig } from '../../../config.type';
import type { ExternalDiagramDefinition, DiagramDetector } from '../../../diagram-api/types';
const id = 'flowchart-elk';
const detector: DiagramDetector = (txt: string, config?: MermaidConfig): boolean => {
if (
// If diagram explicitly states flowchart-elk
txt.match(/^\s*flowchart-elk/) ||
// If a flowchart/graph diagram has their default renderer set to elk
(txt.match(/^\s*flowchart|graph/) && config?.flowchart?.defaultRenderer === 'elk')
) {
return true;
}
return false;
};
const loader = async () => {
const { diagram } = await import('./diagram-definition');
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

@ -0,0 +1,13 @@
// @ts-ignore: JISON typing missing
import parser from '../parser/flow';
import * as db from '../flowDb';
import renderer from './flowRenderer-elk';
import styles from './styles';
export const diagram = {
db,
renderer,
parser,
styles,
};

View File

@ -0,0 +1,953 @@
import { select, line, curveLinear } from 'd3';
import { insertNode } from '../../../dagre-wrapper/nodes.js';
import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { getConfig } from '../../../config';
import { log } from '../../../logger';
import { setupGraphViewbox } from '../../../setupGraphViewbox';
import common, { evaluate } from '../../common/common';
import { interpolateToCurve, getStylesFromArray } from '../../../utils';
import ELK from 'elkjs/lib/elk.bundled.js';
const elk = new ELK();
const portPos = {};
const conf = {};
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
for (const key of keys) {
conf[key] = cnf[key];
}
};
let nodeDb = {};
// /**
// * Function that adds the vertices found during parsing to the graph to be rendered.
// *
// * @param vert Object containing the vertices.
// * @param g The graph that is to be drawn.
// * @param svgId
// * @param root
// * @param doc
// * @param diagObj
// */
export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
const svg = root.select(`[id="${svgId}"]`);
const nodes = svg.insert('g').attr('class', 'nodes');
const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) {
const vertex = vert[id];
/**
* Variable for storing the classes for the vertex
*
* @type {string}
*/
let classStr = 'default';
if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' ');
}
const styles = getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
// We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode;
const labelData = { width: 0, height: 0 };
if (evaluate(getConfig().flowchart.htmlLabels)) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = {
label: vertexText.replace(
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
};
vertexNode = addHtmlLabel(svg, node).node();
const bbox = vertexNode.getBBox();
labelData.width = bbox.width;
labelData.height = bbox.height;
labelData.labelNode = vertexNode;
vertexNode.parentNode.removeChild(vertexNode);
} else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
const rows = vertexText.split(common.lineBreakRegex);
for (const row of rows) {
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
tspan.textContent = row;
svgLabel.appendChild(tspan);
}
vertexNode = svgLabel;
const bbox = vertexNode.getBBox();
labelData.width = bbox.width;
labelData.height = bbox.height;
labelData.labelNode = vertexNode;
}
const ports = [
{
id: vertex.id + '-west',
layoutOptions: {
'port.side': 'WEST',
},
},
{
id: vertex.id + '-east',
layoutOptions: {
'port.side': 'EAST',
},
},
{
id: vertex.id + '-south',
layoutOptions: {
'port.side': 'SOUTH',
},
},
{
id: vertex.id + '-north',
layoutOptions: {
'port.side': 'NORTH',
},
},
];
let radious = 0;
let _shape = '';
let layoutOptions = {};
// Set the shape based parameters
switch (vertex.type) {
case 'round':
radious = 5;
_shape = 'rect';
break;
case 'square':
_shape = 'rect';
break;
case 'diamond':
_shape = 'question';
layoutOptions = {
portConstraints: 'FIXED_SIDE',
};
break;
case 'hexagon':
_shape = 'hexagon';
break;
case 'odd':
_shape = 'rect_left_inv_arrow';
break;
case 'lean_right':
_shape = 'lean_right';
break;
case 'lean_left':
_shape = 'lean_left';
break;
case 'trapezoid':
_shape = 'trapezoid';
break;
case 'inv_trapezoid':
_shape = 'inv_trapezoid';
break;
case 'odd_right':
_shape = 'rect_left_inv_arrow';
break;
case 'circle':
_shape = 'circle';
break;
case 'ellipse':
_shape = 'ellipse';
break;
case 'stadium':
_shape = 'stadium';
break;
case 'subroutine':
_shape = 'subroutine';
break;
case 'cylinder':
_shape = 'cylinder';
break;
case 'group':
_shape = 'rect';
break;
case 'doublecircle':
_shape = 'doublecircle';
break;
default:
_shape = 'rect';
}
// // Add the node
const node = {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
class: classStr,
style: styles.style,
id: vertex.id,
link: vertex.link,
linkTarget: vertex.linkTarget,
tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
haveCallback: vertex.haveCallback,
width: vertex.type === 'group' ? 500 : undefined,
dir: vertex.dir,
type: vertex.type,
props: vertex.props,
padding: getConfig().flowchart.padding,
};
let boundingBox;
let nodeEl;
if (node.type !== 'group') {
nodeEl = insertNode(nodes, node, vertex.dir);
boundingBox = nodeEl.node().getBBox();
}
const data = {
id: vertex.id,
ports: vertex.type === 'diamond' ? ports : [],
// labelStyle: styles.labelStyle,
// shape: _shape,
layoutOptions,
labelText: vertexText,
labelData,
// labels: [{ text: vertexText }],
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// link: vertex.link,
// linkTarget: vertex.linkTarget,
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
// haveCallback: vertex.haveCallback,
width: boundingBox?.width,
height: boundingBox?.height,
// dir: vertex.dir,
type: vertex.type,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// boundingBox,
el: nodeEl,
parent: parentLookupDb.parentById[vertex.id],
};
// if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
// graph.children.push({
// ...data,
// });
// }
nodeDb[node.id] = data;
// log.trace('setNode', {
// labelStyle: styles.labelStyle,
// shape: _shape,
// labelText: vertexText,
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// id: vertex.id,
// domId: diagObj.db.lookUpDomId(vertex.id),
// width: vertex.type === 'group' ? 500 : undefined,
// type: vertex.type,
// dir: vertex.dir,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// parent: parentLookupDb.parentById[vertex.id],
// });
});
return graph;
};
const getNextPosition = (position, edgeDirection, graphDirection) => {
const portPos = {
TB: {
in: {
north: 'north',
},
out: {
south: 'west',
west: 'east',
east: 'south',
},
},
LR: {
in: {
west: 'west',
},
out: {
east: 'south',
south: 'north',
north: 'east',
},
},
RL: {
in: {
east: 'east',
},
out: {
west: 'north',
north: 'south',
south: 'west',
},
},
BT: {
in: {
south: 'south',
},
out: {
north: 'east',
east: 'west',
west: 'north',
},
},
};
portPos.TD = portPos.TB;
log.info('abc88', graphDirection, edgeDirection, position);
return portPos[graphDirection][edgeDirection][position];
// return 'south';
};
const getNextPort = (node, edgeDirection, graphDirection) => {
log.info('getNextPort abc88', { node, edgeDirection, graphDirection });
if (!portPos[node]) {
switch (graphDirection) {
case 'TB':
case 'TD':
portPos[node] = {
inPosition: 'north',
outPosition: 'south',
};
break;
case 'BT':
portPos[node] = {
inPosition: 'south',
outPosition: 'north',
};
break;
case 'RL':
portPos[node] = {
inPosition: 'east',
outPosition: 'west',
};
break;
case 'LR':
portPos[node] = {
inPosition: 'west',
outPosition: 'east',
};
break;
}
}
const result = edgeDirection === 'in' ? portPos[node].inPosition : portPos[node].outPosition;
if (edgeDirection === 'in') {
portPos[node].inPosition = getNextPosition(
portPos[node].inPosition,
edgeDirection,
graphDirection
);
} else {
portPos[node].outPosition = getNextPosition(
portPos[node].outPosition,
edgeDirection,
graphDirection
);
}
return result;
};
const getEdgeStartEndPoint = (edge, dir) => {
let source = edge.start;
let target = edge.end;
const startNode = nodeDb[source];
const endNode = nodeDb[target];
if (!startNode || !endNode) {
return { source, target };
}
if (startNode.type === 'diamond') {
source = `${source}-${getNextPort(source, 'out', dir)}`;
}
if (endNode.type === 'diamond') {
target = `${target}-${getNextPort(target, 'in', dir)}`;
}
// Add the edge to the graph
return { source, target };
};
/**
* Add edges to graph based on parsed graph definition
*
* @param {object} edges The edges to add to the graph
* @param {object} g The graph object
* @param cy
* @param diagObj
* @param graph
* @param svg
*/
export const addEdges = function (edges, diagObj, graph, svg) {
log.info('abc78 edges = ', edges);
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
let linkIdCnt = {};
let dir = diagObj.db.getDirection();
let defaultStyle;
let defaultLabelStyle;
if (edges.defaultStyle !== undefined) {
const defaultStyles = getStylesFromArray(edges.defaultStyle);
defaultStyle = defaultStyles.style;
defaultLabelStyle = defaultStyles.labelStyle;
}
edges.forEach(function (edge) {
// Identify Link
var linkIdBase = 'L-' + edge.start + '-' + edge.end;
// count the links from+to the same node to give unique id
if (linkIdCnt[linkIdBase] === undefined) {
linkIdCnt[linkIdBase] = 0;
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
} else {
linkIdCnt[linkIdBase]++;
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
}
let linkId = linkIdBase + '-' + linkIdCnt[linkIdBase];
log.info('abc78 new link id to be used is', linkIdBase, linkId, linkIdCnt[linkIdBase]);
var linkNameStart = 'LS-' + edge.start;
var linkNameEnd = 'LE-' + edge.end;
const edgeData = { style: '', labelStyle: '' };
edgeData.minlen = edge.length || 1;
//edgeData.id = 'id' + cnt;
// Set link type for rendering
if (edge.type === 'arrow_open') {
edgeData.arrowhead = 'none';
} else {
edgeData.arrowhead = 'normal';
}
// Check of arrow types, placed here in order not to break old rendering
edgeData.arrowTypeStart = 'arrow_open';
edgeData.arrowTypeEnd = 'arrow_open';
/* eslint-disable no-fallthrough */
switch (edge.type) {
case 'double_arrow_cross':
edgeData.arrowTypeStart = 'arrow_cross';
case 'arrow_cross':
edgeData.arrowTypeEnd = 'arrow_cross';
break;
case 'double_arrow_point':
edgeData.arrowTypeStart = 'arrow_point';
case 'arrow_point':
edgeData.arrowTypeEnd = 'arrow_point';
break;
case 'double_arrow_circle':
edgeData.arrowTypeStart = 'arrow_circle';
case 'arrow_circle':
edgeData.arrowTypeEnd = 'arrow_circle';
break;
}
let style = '';
let labelStyle = '';
switch (edge.stroke) {
case 'normal':
style = 'fill:none;';
if (defaultStyle !== undefined) {
style = defaultStyle;
}
if (defaultLabelStyle !== undefined) {
labelStyle = defaultLabelStyle;
}
edgeData.thickness = 'normal';
edgeData.pattern = 'solid';
break;
case 'dotted':
edgeData.thickness = 'normal';
edgeData.pattern = 'dotted';
edgeData.style = 'fill:none;stroke-width:2px;stroke-dasharray:3;';
break;
case 'thick':
edgeData.thickness = 'thick';
edgeData.pattern = 'solid';
edgeData.style = 'stroke-width: 3.5px;fill:none;';
break;
}
if (edge.style !== undefined) {
const styles = getStylesFromArray(edge.style);
style = styles.style;
labelStyle = styles.labelStyle;
}
edgeData.style = edgeData.style += style;
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
if (edge.interpolate !== undefined) {
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
} else if (edges.defaultInterpolate !== undefined) {
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
} else {
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
}
if (edge.text === undefined) {
if (edge.style !== undefined) {
edgeData.arrowheadStyle = 'fill: #333';
}
} else {
edgeData.arrowheadStyle = 'fill: #333';
edgeData.labelpos = 'c';
}
edgeData.labelType = 'text';
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (edge.style === undefined) {
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
}
edgeData.labelStyle = edgeData.labelStyle.replace('color:', 'fill:');
edgeData.id = linkId;
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
const labelEl = insertEdgeLabel(labelsEl, edgeData);
// calculate start and end points of the edge
const { source, target } = getEdgeStartEndPoint(edge, dir);
log.debug('abc78 source and target', source, target);
// Add the edge to the graph
graph.edges.push({
id: 'e' + edge.start + edge.end,
sources: [source],
targets: [target],
labelEl: labelEl,
labels: [
{
width: edgeData.width,
height: edgeData.height,
orgWidth: edgeData.width,
orgHeight: edgeData.height,
text: edgeData.label,
layoutOptions: {
'edgeLabels.inline': 'true',
'edgeLabels.placement': 'CENTER',
},
},
],
edgeData,
});
});
return graph;
};
// TODO: break out and share with dagre wrapper. The current code in dagre wrapper also adds
// adds the line to the graph, but we don't need that here. This is why we cant use the dagre
// wrapper directly for this
/**
* Add the markers to the edge depending on the type of arrow is
* @param svgPath
* @param edgeData
* @param diagramType
* @param arrowMarkerAbsolute
*/
const addMarkersToEdge = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute) {
let url = '';
// Check configuration for absolute path
if (arrowMarkerAbsolute) {
url =
window.location.protocol +
'//' +
window.location.host +
window.location.pathname +
window.location.search;
url = url.replace(/\(/g, '\\(');
url = url.replace(/\)/g, '\\)');
}
// look in edge data and decide which marker to use
switch (edgeData.arrowTypeStart) {
case 'arrow_cross':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-crossStart' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-pointStart' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-barbStart' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-circleStart' + ')');
break;
case 'aggregation':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-aggregationStart' + ')');
break;
case 'extension':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-extensionStart' + ')');
break;
case 'composition':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-compositionStart' + ')');
break;
case 'dependency':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-dependencyStart' + ')');
break;
case 'lollipop':
svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-lollipopStart' + ')');
break;
default:
}
switch (edgeData.arrowTypeEnd) {
case 'arrow_cross':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-crossEnd' + ')');
break;
case 'arrow_point':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-pointEnd' + ')');
break;
case 'arrow_barb':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-barbEnd' + ')');
break;
case 'arrow_circle':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-circleEnd' + ')');
break;
case 'aggregation':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-aggregationEnd' + ')');
break;
case 'extension':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-extensionEnd' + ')');
break;
case 'composition':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-compositionEnd' + ')');
break;
case 'dependency':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-dependencyEnd' + ')');
break;
case 'lollipop':
svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-lollipopEnd' + ')');
break;
default:
}
};
/**
* Returns the all the styles from classDef statements in the graph definition.
*
* @param text
* @param diagObj
* @returns {object} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
diagObj.db.clear('ver-2');
try {
// Parse the graph definition
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
return {};
}
};
const addSubGraphs = function (db) {
const parentLookupDb = { parentById: {}, childrenById: {} };
const subgraphs = db.getSubGraphs();
log.info('Subgraphs - ', subgraphs);
subgraphs.forEach(function (subgraph) {
subgraph.nodes.forEach(function (node) {
parentLookupDb.parentById[node] = subgraph.id;
if (parentLookupDb.childrenById[subgraph.id] === undefined) {
parentLookupDb.childrenById[subgraph.id] = [];
}
parentLookupDb.childrenById[subgraph.id].push(node);
});
});
subgraphs.forEach(function (subgraph) {
const data = { id: subgraph.id };
if (parentLookupDb.parentById[subgraph.id] !== undefined) {
data.parent = parentLookupDb.parentById[subgraph.id];
}
});
return parentLookupDb;
};
const calcOffset = function (src, dest, parentLookupDb) {
const ancestor = findCommonAncestor(src, dest, parentLookupDb);
if (ancestor === undefined || ancestor === 'root') {
return { x: 0, y: 0 };
}
const ancestorOffset = nodeDb[ancestor].offset;
return { x: ancestorOffset.posX, y: ancestorOffset.posY };
};
const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
const offset = calcOffset(edge.sources[0], edge.targets[0], parentLookupDb);
const src = edge.sections[0].startPoint;
const dest = edge.sections[0].endPoint;
const segments = edge.sections[0].bendPoints ? edge.sections[0].bendPoints : [];
const segPoints = segments.map((segment) => [segment.x + offset.x, segment.y + offset.y]);
const points = [
[src.x + offset.x, src.y + offset.y],
...segPoints,
[dest.x + offset.x, dest.y + offset.y],
];
// const curve = line().curve(curveBasis);
const curve = line().curve(curveLinear);
const edgePath = edgesEl
.insert('path')
.attr('d', curve(points))
.attr('class', 'path')
.attr('fill', 'none');
const edgeG = edgesEl.insert('g').attr('class', 'edgeLabel');
const edgeWithLabel = select(edgeG.node().appendChild(edge.labelEl));
const box = edgeWithLabel.node().firstChild.getBoundingClientRect();
edgeWithLabel.attr('width', box.width);
edgeWithLabel.attr('height', box.height);
edgeG.attr(
'transform',
`translate(${edge.labels[0].x + offset.x}, ${edge.labels[0].y + offset.y})`
);
addMarkersToEdge(edgePath, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute);
};
/**
* Recursive function that iterates over an array of nodes and inserts the children of each node.
* It also recursively populates the inserts the children of the children and so on.
* @param {*} graph
* @param nodeArray
* @param parentLookupDb
*/
const insertChildren = (nodeArray, parentLookupDb) => {
nodeArray.forEach((node) => {
// Check if we have reached the end of the tree
if (!node.children) {
node.children = [];
}
// Check if the node has children
const childIds = parentLookupDb.childrenById[node.id];
// If the node has children, add them to the node
if (childIds) {
childIds.forEach((childId) => {
node.children.push(nodeDb[childId]);
});
}
// Recursive call
insertChildren(node.children, parentLookupDb);
});
};
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
*
* @param text
* @param id
*/
export const draw = async function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
nodeDb = {};
diagObj.db.setGen('gen-2');
// Parse the graph definition
diagObj.parser.parse(text);
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
let graph = {
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'org.eclipse.elk.padding': '[top=100, left=100, bottom=110, right=110]',
'elk.layered.spacing.edgeNodeBetweenLayers': '30',
// 'elk.layered.mergeEdges': 'true',
'elk.direction': 'DOWN',
// 'elk.ports.sameLayerEdges': true,
// 'nodePlacement.strategy': 'SIMPLE',
},
children: [],
edges: [],
};
log.info('Drawing flowchart using v3 renderer', elk);
// Set the direction,
// Fetch the default direction, use TD if none was found
let dir = diagObj.db.getDirection();
switch (dir) {
case 'BT':
graph.layoutOptions['elk.direction'] = 'UP';
break;
case 'TB':
graph.layoutOptions['elk.direction'] = 'DOWN';
break;
case 'LR':
graph.layoutOptions['elk.direction'] = 'RIGHT';
break;
case 'RL':
graph.layoutOptions['elk.direction'] = 'LEFT';
break;
}
const { securityLevel, flowchart: conf } = getConfig();
// Find the root dom node to ne used in rendering
// Handle root and document for when rendering in sandbox mode
let sandboxElement;
if (securityLevel === 'sandbox') {
sandboxElement = select('#i' + id);
}
const root =
securityLevel === 'sandbox'
? select(sandboxElement.nodes()[0].contentDocument.body)
: select('body');
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const svg = root.select(`[id="${id}"]`);
// Define the supported markers for the diagram
const markers = ['point', 'circle', 'cross'];
// Add the marker definitions to the svg as marker tags
insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const vert = diagObj.db.getVertices();
// Setup nodes from the subgraphs with type group, these will be used
// as nodes with children in the subgraph
let subG;
const subGraphs = diagObj.db.getSubGraphs();
log.info('Subgraphs - ', subGraphs);
for (let i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i];
diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
}
// Add an element in the svg to be used to hold the subgraphs container
// elements
const subGraphsEl = svg.insert('g').attr('class', 'subgraphs');
// Create the lookup db for the subgraphs and their children to used when creating
// the tree structured graph
const parentLookupDb = addSubGraphs(diagObj.db);
// Add the nodes to the graph, this will entail creating the actual nodes
// in order to get the size of the node. You can't get the size of a node
// that is not in the dom so we need to add it to the dom, get the size
// we will position the nodes when we get the layout from elkjs
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
// Time for the edges, we start with adding an element in the node to hold the edges
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
// Fetch the edges form the parsed graph definition
const edges = diagObj.db.getEdges();
// Add the edges to the graph, this will entail creating the actual edges
graph = addEdges(edges, diagObj, graph, svg);
// Iterate through all nodes and add the top level nodes to the graph
const nodes = Object.keys(nodeDb);
nodes.forEach((nodeId) => {
const node = nodeDb[nodeId];
if (!node.parent) {
graph.children.push(node);
}
// Subgraph
if (parentLookupDb.childrenById[nodeId] !== undefined) {
node.labels = [
{
text: node.labelText,
layoutOptions: {
'nodeLabels.placement': '[H_CENTER, V_TOP, INSIDE]',
},
width: node.labelData.width,
height: node.labelData.height,
},
];
delete node.x;
delete node.y;
delete node.width;
delete node.height;
}
});
insertChildren(graph.children, parentLookupDb);
log.info('after layout', JSON.stringify(graph, null, 2));
const g = await elk.layout(graph);
drawNodes(0, 0, g.children, svg, subGraphsEl, diagObj, 0);
log.info('after layout', g);
g.edges?.map((edge) => {
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb);
});
setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
// Remove element after layout
renderEl.remove();
};
const drawNodes = (relX, relY, nodeArray, svg, subgraphsEl, diagObj, depth) => {
nodeArray.forEach(function (node) {
if (node) {
nodeDb[node.id].offset = {
posX: node.x + relX,
posY: node.y + relY,
x: relX,
y: relY,
depth,
width: node.width,
height: node.height,
};
if (node.type === 'group') {
const subgraphEl = subgraphsEl.insert('g').attr('class', 'subgraph');
subgraphEl
.insert('rect')
.attr('class', 'subgraph subgraph-lvl-' + (depth % 5) + ' node')
.attr('x', node.x + relX)
.attr('y', node.y + relY)
.attr('width', node.width)
.attr('height', node.height);
const label = subgraphEl.insert('g').attr('class', 'label');
label.attr(
'transform',
`translate(${node.labels[0].x + relX + node.x}, ${node.labels[0].y + relY + node.y})`
);
label.node().appendChild(node.labelData.labelNode);
log.info('Id (UGH)= ', node.type, node.labels);
} else {
log.info('Id (UGH)= ', node.id);
node.el.attr(
'transform',
`translate(${node.x + relX + node.width / 2}, ${node.y + relY + node.height / 2})`
);
}
}
});
nodeArray.forEach(function (node) {
if (node && node.type === 'group') {
drawNodes(relX + node.x, relY + node.y, node.children, svg, subgraphsEl, diagObj, depth + 1);
}
});
};
export default {
getClasses,
draw,
};

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