Compare commits

..

1 Commits

Author SHA1 Message Date
Sidharth Vinod
f85f4bb661 Use undefined instead of null 2023-01-17 14:06:50 +05:30
145 changed files with 2429 additions and 6490 deletions

View File

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

View File

@@ -1,150 +0,0 @@
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,
},
},
],
};

138
.eslintrc.json Normal file
View File

@@ -0,0 +1,138 @@
{
"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-null": "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,8 +1,6 @@
# These are supported funding model platforms
github:
- knsv
- sidharthv96
github: [knsv]
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username

View File

@@ -32,7 +32,6 @@ jobs:
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v4
id: cypress
# 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
if: ${{ ( env.CYPRESS_RECORD_KEY != '' ) || ( matrix.containers == 1 ) }}
@@ -45,10 +44,3 @@ jobs:
parallel: ${{ secrets.CYPRESS_RECORD_KEY != '' }}
env:
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

@@ -37,20 +37,7 @@ jobs:
CYPRESS_CACHE_FOLDER: .cache/Cypress
- name: Run Linting
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
run: pnpm run lint
- name: Verify Docs
id: verifyDocs

View File

@@ -1,11 +1,5 @@
export default {
'!(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',
],
'!(docs/**/*)*.{ts,js,json,html,md,mts}': ['eslint --fix', 'prettier --write'],
'cSpell.json': ['ts-node-esm scripts/fixCSpell.ts'],
'**/*.jison': ['pnpm -w run lint:jison'],
};

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# 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/badge/Social-mermaidjs__-blue?style=social&logo=twitter)](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/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
English | [简体中文](./README.zh-CN.md)

View File

@@ -1,6 +1,6 @@
# 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/badge/Social-mermaidjs__-blue?style=social&logo=twitter)](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/twitter/follow/mermaidjs_?style=social)](https://twitter.com/mermaidjs_)
[English](./README.md) | 简体中文

View File

@@ -6,7 +6,6 @@
"adamiecki",
"alois",
"antiscript",
"appli",
"applitools",
"asciidoctor",
"ashish",
@@ -14,7 +13,6 @@
"bbox",
"bilkent",
"bisheng",
"blrs",
"braintree",
"brkt",
"brolin",
@@ -56,10 +54,8 @@
"knut",
"laganeckas",
"lintstagedrc",
"logmsg",
"lucida",
"matthieu",
"mdast",
"mdbook",
"mermerd",
"mindaugas",
@@ -85,7 +81,6 @@
"setupgraphviewbox",
"shiki",
"sidharth",
"sidharthv",
"sphinxcontrib",
"statediagram",
"stylis",

View File

@@ -2,7 +2,7 @@ const utf8ToB64 = (str) => {
return window.btoa(unescape(encodeURIComponent(str)));
};
const batchId = 'mermaid-batch' + new Date().getTime();
const batchId = 'mermid-batch' + new Date().getTime();
export const mermaidUrl = (graphStr, options, api) => {
const obj = {
@@ -46,22 +46,8 @@ export const imgSnapshotTest = (graphStr, _options, api = false, validation) =>
if (!options.fontSize) {
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');
cy.log('Hello ' + useAppli ? 'Appli' : 'image-snapshot');
const name = (options.name || cy.state('runnable').fullTitle()).replace(/\s+/g, '-');
if (useAppli) {
@@ -74,20 +60,82 @@ const openURLAndVerifyRendering = (url, options, validation = undefined) => {
});
}
cy.visit(url);
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
const url = mermaidUrl(graphStr, options, api);
cy.visit(url);
if (validation) {
cy.get('svg').should(validation);
}
cy.get('svg');
// Default name to test title
if (useAppli) {
cy.log('Check eyes' + Cypress.spec.name);
cy.eyesCheckWindow('Click!');
cy.log('Closing eyes' + Cypress.spec.name);
cy.log('Closing eyes: ' + Cypress.spec.name);
cy.eyesClose();
} else {
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('registerDiagram', () => {
it('should work on @mermaid-js/mermaid-example-diagram', () => {
const url = 'http://localhost:9000/external-diagrams-example-diagram.html';
it('should work on @mermaid-js/mermaid-mindmap and mermaid-example-diagram', () => {
const url = 'http://localhost:9000/external-diagrams-mindmap.html';
urlSnapshotTest(url, {}, false, false);
});
});

View File

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

View File

@@ -3,42 +3,6 @@
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
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', () => {
imgSnapshotTest(
`

View File

@@ -1,164 +0,0 @@
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

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

View File

@@ -2,8 +2,34 @@
<body>
<h1>Should correctly load a third-party diagram using registerDiagram</h1>
<pre id="diagram" class="mermaid">
example-diagram
mindmap
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 id="diagram" class="mermaid2">
example-diagram
</pre> -->
<!-- <div id="cy"></div> -->
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
@@ -11,16 +37,13 @@ example-diagram
<!-- <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">
import exampleDiagram from '../../packages/mermaid-example-diagram/src/detector';
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([exampleDiagram]);
await mermaid.registerExternalDiagrams([mindmap]);
await mermaid.initialize({ logLevel: 0 });
await mermaid.initThrowsErrorsAsync();
if (window.Cypress) {
window.rendered = true;
}
</script>
</body>
</html>

View File

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

View File

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

View File

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

View File

@@ -56,24 +56,13 @@
<body>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
graph BT
a{The cat in the hat} -- 1o --> b
a -- 2o --> c
a -- 3o --> d
g --2i--> a
d --1i--> a
h --3i -->a
b --> d(The dog in the hog)
c --> d
</pre>
<pre id="diagram" class="mermaid2">
flowchart-elk TB
graph TB
a --> b
a --> c
b --> d
c --> d
</pre>
<pre id="diagram" class="mermaid2">
<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
@@ -129,7 +118,7 @@ flowchart TB
</pre
>
<br />
<pre id="diagram" class="mermaid2">
<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]
@@ -232,27 +221,13 @@ sequenceDiagram
</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
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> -->
@@ -262,17 +237,18 @@ mindmap
<!-- <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 mindmap from '../../packages/mermaid-mindmap/src/detector';
import flowV3 from '../../packages/mermaid-flowchart-v3/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid';
await mermaid.registerExternalDiagrams([example]);
await mermaid.registerExternalDiagrams([mindmap, flowV3]);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
theme: 'forest',
// theme: 'forest',
startOnLoad: true,
logLevel: 5,
logLevel: 0,
flowchart: {
// defaultRenderer: 'elk',
useMaxWidth: false,

View File

@@ -1,93 +1,81 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Mindmap Mermaid Quick Test Page</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=" />
<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>
div.mermaid {
/* font-family: 'trebuchet ms', verdana, arial; */
font-family: 'Courier New', Courier, monospace !important;
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>
<h1>Mindmap diagram demo</h1>
<pre class="mermaid">
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))
<div>Security check</div>
<pre id="diagram" class="mermaid">
graph TD
A["test"] --"<p><style> * { display : none}</style>test</p>"--> B
</pre>
<h2>Mindmap with root wrapping text and a shape</h2>
<pre class="mermaid">
mindmap
root[A root with a long text that wraps to keep the node size in check]
</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 mermaid from './mermaid.esm.mjs';
import mindmap from '../../packages/mermaid-mindmap/src/detector';
// import example from '../../packages/mermaid-example-diagram/src/detector';
import mermaid from '../../packages/mermaid/src/mermaid';
// 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',
];
await mermaid.registerExternalDiagrams([mindmap]);
mermaid.parseError = function (err, hash) {
// console.error('Mermaid error: ', err);
};
mermaid.initialize({
theme: 'base',
theme: 'default',
startOnLoad: true,
logLevel: 0,
flowchart: {
@@ -106,6 +94,10 @@
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

@@ -1,283 +1,14 @@
<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>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
graph TB
a --> b
a --> c
b --> d
c --> d
<pre class="mermaid">
none
hello world
</pre>
<pre id="diagram" class="mermaid">
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);
};
<script src="./mermaid.js"></script>
<script>
mermaid.initialize({
// theme: 'forest',
startOnLoad: true,
logLevel: 0,
flowchart: {
// defaultRenderer: 'elk',
useMaxWidth: false,
htmlLabels: true,
},
gantt: {
useMaxWidth: false,
},
useMaxWidth: false,
logLevel: 1,
});
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

@@ -1,17 +1,10 @@
import mermaid2 from '../../packages/mermaid/src/mermaid';
import externalExample from '../../packages/mermaid-example-diagram/src/detector';
import mindmap from '../../packages/mermaid-mindmap/src/detector';
function b64ToUtf8(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
* configuration for mermaid rendering and calls init for rendering the mermaid diagrams on the
@@ -44,10 +37,9 @@ const contentLoaded = async function () {
document.getElementsByTagName('body')[0].appendChild(div);
}
await mermaid2.registerExternalDiagrams([externalExample]);
await mermaid2.registerExternalDiagrams([mindmap]);
mermaid2.initialize(graphObj.mermaid);
await mermaid2.init();
markRendered();
mermaid2.init();
}
};
@@ -136,7 +128,6 @@ const contentLoadedApi = function () {
);
}
}
markRendered();
};
if (typeof document !== 'undefined') {
@@ -151,7 +142,7 @@ if (typeof document !== 'undefined') {
contentLoadedApi();
} else {
this.console.log('Not using api');
void contentLoaded();
contentLoaded();
}
},
false

View File

@@ -71,44 +71,6 @@
</pre>
<hr />
<pre class="mermaid">
erDiagram
"HOSPITAL" {
int id PK
int doctor_id PK, FK
string address UK
string name
string phone_number
string fax_number
}
</pre
>
<hr />
<pre class="mermaid">
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string make
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>
<script src="./mermaid.js"></script>
<script type="module">
mermaid.initialize({

View File

@@ -128,22 +128,6 @@
</pre>
<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>
mermaid.initialize({

View File

@@ -161,19 +161,12 @@
First --> Second
First --> Third
state "the first composite" as First {
state First {
[*] --> 1st
state innerFirst {
state "1 in innerFirst" as 1st1st
1st2nd: 2 in innerFirst
[*] --> 1st1st
1st1st --> 1st2nd
%% 1st2nd --> 1st
}
1st --> innerFirst
innerFirst --> 2nd
1st --> [*]
}
state Second {
[*] --> 2nd
2nd --> [*]
}
state Third {

View File

@@ -1,38 +0,0 @@
<!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

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

View File

@@ -20,7 +20,7 @@ Renames and re-exports [mermaidAPI](mermaidAPI.md#mermaidapi)
#### Defined in
[mermaidAPI.ts:74](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L74)
[mermaidAPI.ts:73](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L73)
## Variables
@@ -90,7 +90,7 @@ mermaid.initialize(config);
#### Defined in
[mermaidAPI.ts:886](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L886)
[mermaidAPI.ts:962](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L962)
## Functions
@@ -121,7 +121,7 @@ Return the last node appended
#### Defined in
[mermaidAPI.ts:287](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L287)
[mermaidAPI.ts:286](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L286)
---
@@ -147,7 +147,7 @@ the cleaned up svgCode
#### Defined in
[mermaidAPI.ts:238](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L238)
[mermaidAPI.ts:237](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L237)
---
@@ -173,7 +173,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:167](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L167)
[mermaidAPI.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L166)
---
@@ -196,7 +196,7 @@ the string with all the user styles
#### Defined in
[mermaidAPI.ts:215](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L215)
[mermaidAPI.ts:214](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L214)
---
@@ -223,7 +223,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:151](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L151)
[mermaidAPI.ts:150](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L150)
---
@@ -243,7 +243,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:131](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L131)
[mermaidAPI.ts:130](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L130)
---
@@ -263,7 +263,7 @@ with an enclosing block that has each of the cssClasses followed by !important;
#### Defined in
[mermaidAPI.ts:102](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L102)
[mermaidAPI.ts:101](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L101)
---
@@ -289,7 +289,7 @@ Put the svgCode into an iFrame. Return the iFrame code
#### Defined in
[mermaidAPI.ts:266](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L266)
[mermaidAPI.ts:265](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L265)
---
@@ -314,4 +314,4 @@ Remove any existing elements from the given document
#### Defined in
[mermaidAPI.ts:337](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L337)
[mermaidAPI.ts:336](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L336)

View File

@@ -1,9 +0,0 @@
> **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

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

View File

@@ -2,7 +2,7 @@
>
> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT.
>
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/ecosystem/integrations.md](../../packages/mermaid/src/docs/ecosystem/integrations.md).
> ## Please edit the corresponding file in [/packages/mermaid/src/docs/misc/integrations.md](../../packages/mermaid/src/docs/misc/integrations.md).
# Integrations
@@ -95,10 +95,9 @@ They also serve as proof of concept, for the variety of things that can be built
## 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)
- [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 Editor](https://marketplace.visualstudio.com/items?itemName=tomoyukim.vscode-mermaid-editor)
- [Mermaid Export](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.mermaid-export)

View File

@@ -200,13 +200,14 @@ The `type` and `name` values must begin with an alphabetic character and may con
#### Attribute Keys and Comments
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.
Attributes may also have a `key` or comment defined. Keys can be "PK", "FK" or "UK", for Primary Key, Foreign Key or Unique Key. And a `comment` is defined by double quotes at the end of an attribute. Comments themselves cannot have double-quote characters in them.
```mermaid-example
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string make
string model
string[] parts
@@ -216,21 +217,17 @@ erDiagram
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
MANUFACTURER only one to zero or more CAR
```
```mermaid
erDiagram
CAR ||--o{ NAMED-DRIVER : allows
CAR {
string registrationNumber PK
string allowedDriver FK "The license of the allowed driver"
string registrationNumber UK
string make
string model
string[] parts
@@ -240,14 +237,9 @@ erDiagram
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
MANUFACTURER only one to zero or more CAR
```
### 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
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`, `bumpX`, `bumpY`, `cardinal`, `catmullRom`, `linear`, `monotoneX`, `monotoneY`,
`natural`, `step`, `stepAfter`, and `stepBefore`.
Available curve styles include `basis`, `bump`, `linear`, `monotoneX`, `monotoneY`, `natural`, `step`, `stepAfter`,
and `stepBefore`.
In this example, a left-to-right graph uses the `stepBefore` curve style:

View File

@@ -88,7 +88,7 @@ In this way we can use a text outline to generate a hierarchical mindmap.
## Different shapes
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.
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.
Mindmap can show the following shapes:

View File

@@ -94,59 +94,6 @@ sequenceDiagram
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 can be of two displayed either solid or with a dotted line.

View File

@@ -1,462 +0,0 @@
> **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",
"private": true,
"version": "9.4.0",
"version": "9.3.0-rc1",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"type": "module",
"packageManager": "pnpm@7.25.1",
"packageManager": "pnpm@7.25.0",
"keywords": [
"diagram",
"markdown",
@@ -18,12 +18,12 @@
"build:vite": "ts-node-esm --transpileOnly .vite/build.ts",
"build:mermaid": "pnpm build:vite --mermaid",
"build:viz": "pnpm build:mermaid --visualize",
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-example-diagram/tsconfig.json --emitDeclarationOnly",
"build:types": "tsc -p ./packages/mermaid/tsconfig.json --emitDeclarationOnly && tsc -p ./packages/mermaid-mindmap/tsconfig.json --emitDeclarationOnly",
"build:watch": "pnpm build:vite --watch",
"build": "pnpm run -r clean && concurrently \"pnpm build:vite\" \"pnpm build:types\"",
"dev": "concurrently \"pnpm build:vite --watch\" \"ts-node-esm .vite/server.ts\"",
"release": "pnpm build",
"lint": "eslint --cache --cache-strategy content --ignore-path .gitignore . && pnpm lint:jison && prettier --cache --check .",
"lint": "eslint --cache --ignore-path .gitignore . && pnpm lint:jison && prettier --check .",
"lint:fix": "eslint --fix --ignore-path .gitignore . && prettier --write . && ts-node-esm scripts/fixCSpell.ts",
"lint:jison": "ts-node-esm ./scripts/jison/lint.mts",
"cypress": "cypress run",
@@ -67,8 +67,8 @@
"@types/node": "^18.11.9",
"@types/prettier": "^2.7.1",
"@types/rollup-plugin-visualizer": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
"@typescript-eslint/eslint-plugin": "^5.42.1",
"@typescript-eslint/parser": "^5.42.1",
"@vitest/coverage-c8": "^0.27.0",
"@vitest/ui": "^0.27.0",
"concurrently": "^7.5.0",
@@ -76,8 +76,8 @@
"cypress": "^10.11.0",
"cypress-image-snapshot": "^4.0.1",
"esbuild": "^0.17.0",
"eslint": "^8.32.0",
"eslint-config-prettier": "^8.6.0",
"eslint": "^8.27.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-jest": "^27.1.5",

View File

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

View File

@@ -1,25 +1,36 @@
{
"name": "@mermaid-js/mermaid-example-diagram",
"version": "9.3.0",
"description": "Example of external diagram module for MermaidJS.",
"module": "dist/mermaid-example-diagram.core.mjs",
"types": "dist/detector.d.ts",
"version": "9.2.0-rc2",
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "dist/mermaid-mindmap.core.mjs",
"module": "dist/mermaid-mindmap.core.mjs",
"type": "module",
"exports": {
".": {
"import": "./dist/mermaid-example-diagram.core.mjs",
"types": "./dist/detector.d.ts"
"require": "./dist/mermaid-example-diagram.min.js",
"import": "./dist/mermaid-example-diagram.core.mjs"
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"example",
"mindmap",
"mermaid"
],
"scripts": {
"prepublishOnly": "pnpm -w run build"
"clean": "rimraf dist",
"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": {
"type": "git",
@@ -37,19 +48,9 @@
"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"
},
"dependencies": {},
"devDependencies": {
"@types/cytoscape": "^3.19.9",
"concurrently": "^7.5.0",
"mermaid": "workspace:*",
"rimraf": "^3.0.2"
},
"resolutions": {

View File

@@ -1,20 +1,18 @@
import type { ExternalDiagramDefinition } from 'mermaid';
// @ts-ignore: TODO Fix ts errors
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
*/
const detector = (txt: string) => {
export const detector = (txt: string) => {
return txt.match(/^\s*example-diagram/) !== null;
};
const loader = async () => {
export const loadDiagram = async () => {
const { diagram } = await import('./diagram-definition');
return { id, diagram };
};
const plugin: ExternalDiagramDefinition = {
id,
detector,
loader,
};
export default plugin;

View File

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

View File

@@ -1,17 +1,5 @@
import { parser } from './parser/exampleDiagram';
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);
import db from './exampleDiagramDb';
describe('when parsing an info graph it', function () {
let ex;
beforeEach(function () {

View File

@@ -1,8 +1,4 @@
const warning = (s: string) => {
// Todo remove debug code
// eslint-disable-next-line no-console
console.error('Log function was called before initialization', s);
};
const warning = () => undefined;
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
@@ -23,11 +19,12 @@ export const log: Record<keyof typeof LEVELS, typeof console.log> = {
error: warning,
fatal: warning,
};
export let setLogLevel: (level: keyof typeof LEVELS | number | string) => void;
export let getConfig: () => object;
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
export let setupGraphViewbox: (
graph: any,
@@ -36,15 +33,23 @@ export let setupGraphViewbox: (
useMaxWidth: boolean
) => 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 = (
_log: Record<keyof typeof LEVELS, typeof console.log>,
_setLogLevel: any,
_getConfig: any,
_sanitizeText: any,
_setupGraphViewbox: any,
_commonDb: any
_setLogLevel: typeof setLogLevel,
_getConfig: typeof getConfig,
_sanitizeText: typeof sanitizeText,
_setupGraphViewbox: typeof setupGraphViewbox
) => {
_log.info('Mermaid utils injected');
_log.debug('Mermaid utils injected into example-diagram');
log.trace = _log.trace;
log.debug = _log.debug;
log.info = _log.info;
@@ -55,5 +60,4 @@ export const injectUtils = (
getConfig = _getConfig;
sanitizeText = _sanitizeText;
setupGraphViewbox = _setupGraphViewbox;
commonDb = _commonDb;
};

View File

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

View File

@@ -0,0 +1,65 @@
{
"name": "@mermaid-js/mermaid-flowchart-v3",
"version": "9.4.0",
"description": "An extension for the Mermaid diagramming library that utilizes elkjs for layout, enabling the creation of visually appealing and easy-to-understand flowcharts.",
"module": "dist/mermaid-flowchart-v3.core.mjs",
"types": "dist/detector.d.ts",
"type": "module",
"exports": {
".": {
"import": "./dist/mermaid-flowchart-v3.core.mjs",
"types": "./dist/detector.d.ts"
},
"./*": "./*"
},
"keywords": [
"diagram",
"markdown",
"flowchart",
"mermaid",
"elkjs"
],
"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",
"graphlib": "^2.1.0",
"dagre-d3-es": "7.0.6",
"elkjs": "^0.8.2",
"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

@@ -0,0 +1,31 @@
import type { ExternalDiagramDefinition } from 'mermaid';
const id = 'flowchart-v3';
const detector = (txt: string, config) => {
if (config?.flowchart?.defaultRenderer === 'dagre-d3') {
return false;
}
if (config?.flowchart?.defaultRenderer === 'dagre-wrapper') {
return false;
}
// If we have configured to use dagre-wrapper then we should return true in this function for graph code thus making it use the new flowchart diagram
if (txt.match(/^\s*graph/) !== null) {
return true;
}
return txt.match(/^\s*flowchart/) !== null;
};
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,14 @@
// @ts-ignore: TODO Fix ts errors
import parser from '../../mermaid/src/diagrams/flowchart/parser/flow';
import * as db from '../../mermaid/src/diagrams/flowchart/flowDb';
import renderer from './flowRenderer-v3';
import styles from './styles';
import { injectUtils } from './mermaidUtils';
export const diagram = {
db,
renderer,
parser,
styles,
injectUtils,
};

View File

@@ -0,0 +1,756 @@
import graphlib from 'graphlib';
import { select, line, curveLinear, curveCardinal, curveBasis, selectAll } from 'd3';
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js';
import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js';
import dagre from 'cytoscape-dagre';
// Replace with other function to avoid dependency to dagre-d3
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import common, { evaluate } from '../../mermaid/src/diagrams/common/common';
import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils';
import cytoscape from 'cytoscape';
cytoscape.use(dagre);
const conf = {};
export const setConf = function (cnf) {
const keys = Object.keys(cnf);
for (const key of keys) {
conf[key] = cnf[key];
}
};
// /**
// * 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;
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();
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;
}
let radious = 0;
let _shape = '';
// Set the shape based parameters
switch (vertex.type) {
case 'round':
radious = 5;
_shape = 'rect';
break;
case 'square':
_shape = 'rect';
break;
case 'diamond':
_shape = 'question';
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,
};
const nodeEl = insertNode(nodes, node, vertex.dir);
const boundingBox = nodeEl.node().getBBox();
const data = {
id: vertex.id,
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
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: vertex.type === 'group' ? 500 : undefined,
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.elements.nodes.push({
group: 'nodes',
// data,
data,
});
// }
log.trace('setNode', {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
rx: radious,
ry: radious,
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;
};
/**
* 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
*/
export const addEdges = function (edges, diagObj, graph) {
// log.info('abc78 edges = ', edges);
let cnt = 0;
let linkIdCnt = {};
let defaultStyle;
let defaultLabelStyle;
if (edges.defaultStyle !== undefined) {
const defaultStyles = getStylesFromArray(edges.defaultStyle);
defaultStyle = defaultStyles.style;
defaultLabelStyle = defaultStyles.labelStyle;
}
edges.forEach(function (edge) {
cnt++;
// 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;
// Add the edge to the graph
graph.elements.edges.push({
group: 'edges',
data: { source: edge.start, target: edge.end, edgeData, id: cnt },
});
});
return graph;
};
const addmarkers = function (svgPath, edgeData, diagramType, arrowMarkerAbsolute) {
// // TODO: Can we load this config only from the rendered graph type?
let url;
if (arrowMarkerAbsolute) {
url =
window.location.protocol +
'//' +
window.location.host +
window.location.pathname +
window.location.search;
url = url.replace(/\(/g, '\\(');
url = url.replace(/\)/g, '\\)');
}
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];
}
// cy.add({
// group: 'nodes',
// data,
// });
});
return parentLookUpDb;
};
const insertEdge = function (edgesEl, edge, edgeData, bounds, diagObj) {
const src = edge.sourceEndpoint();
const segments = edge.segmentPoints();
// const dest = edge.target().position();
const dest = edge.targetEndpoint();
const segPoints = segments.map((segment) => [segment.x, segment.y]);
const points = [
[src.x, src.y],
[segments[0].x, segments[0].y],
[dest.x, dest.y],
];
// console.log('Edge ctrl points:', edge.segmentPoints(), 'Bounds:', bounds, edge.source(), points);
// console.log('Edge ctrl points:', points);
const curve = line().curve(curveCardinal);
const edge2 = edgesEl
.insert('path')
.attr('d', curve(points))
.attr('class', 'path')
.attr('fill', 'none');
addmarkers(edge2, edgeData, diagObj.type, diagObj.arrowMarkerAbsolute);
// edgesEl
// .append('circle')
// .style('stroke', 'red')
// .style('fill', 'red')
// .attr('r', 1)
// .attr('cx', src.x)
// .attr('cy', src.y);
// edgesEl
// .append('circle')
// .style('stroke', 'white')
// .style('fill', 'white')
// .attr('r', 1)
// .attr('cx', segments[0].x)
// .attr('cy', segments[0].y);
// edgesEl
// .append('circle')
// .style('stroke', 'pink')
// .style('fill', 'pink')
// .attr('r', 1)
// .attr('cx', dest.x)
// .attr('cy', dest.y);
};
/**
* Draws a flowchart in the tag with id: id based on the graph definition in text.
*
* @param text
* @param id
*/
export const draw = function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
diagObj.db.setGen('gen-2');
// Parse the graph definition
diagObj.parser.parse(text);
return new Promise(function (resolve, reject) {
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
// .attr('style', 'display:none')
let graph = {
styleEnabled: true,
// animate: false,
// ready: function () {
// log.info('Ready', this);
// },
container: document.getElementById('cy'), // container to render in
boxSelectionEnabled: false,
style: [
{
selector: 'node',
css: {
content: 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
},
},
{
selector: ':parent',
css: {
'text-valign': 'top',
'text-halign': 'center',
},
},
{
selector: 'edge',
css: {
'curve-style': 'bezier',
'target-arrow-shape': 'triangle',
},
},
],
elements: {
nodes: [
{ data: { id: 'a', parent: 'b' } },
{ data: { id: 'b' } },
{ data: { id: 'c', parent: 'b' } },
{ data: { id: 'd' } },
{ data: { id: 'e' } },
{ data: { id: 'f', parent: 'e' } },
],
edges: [
{ data: { id: 'ad', source: 'a', target: 'd' } },
{ data: { id: 'eb', source: 'e', target: 'b' } },
],
},
};
log.info('Drawing flowchart using v3 renderer');
// Fetch the default direction, use TD if none was found
let dir = diagObj.db.getDirection();
if (dir === undefined) {
dir = 'TD';
}
const { securityLevel, flowchart: conf } = getConfig();
// 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}"]`);
const markers = ['point', 'circle', 'cross'];
insertMarkers(svg, markers, diagObj.type, diagObj.arrowMarkerAbsolute);
// Fetch the vertices/nodes and edges/links from the parsed graph definition
const vert = diagObj.db.getVertices();
let subG;
const subGraphs = diagObj.db.getSubGraphs();
log.info('Subgraphs - ', subGraphs);
for (let i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i];
log.info('Subgraph - ', subG);
diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
}
const parentLookUpDb = addSubGraphs(diagObj.db);
graph = addVertices(vert, id, root, doc, diagObj, parentLookUpDb, graph);
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
const edges = diagObj.db.getEdges();
graph = addEdges(edges, diagObj, graph);
const cy = cytoscape(graph);
// c.style();
// Make cytoscape care about the dimensions of the nodes
cy.nodes().forEach(function (n) {
const boundingBox = n.data().boundingBox;
if (boundingBox) {
n.style('width', boundingBox.width);
n.style('height', boundingBox.height);
}
n.style('shape', 'rectangle');
// n.layoutDimensions = () => {
// // console.log('Node dimensions', boundingBox.width, boundingBox.height);
// if (boundingBox) {
// return { w: boundingBox.width, h: boundingBox.height };
// }
// // return { w: boundingBox.width, h: boundingBox.height };
// // const data = n.data();
// // return { w: data.width, h: data.height };
// return { w: 206, h: 160 };
// };
});
cy.layout({
// name: 'dagre',
// name: 'preset',
// name: 'cose',
// name: 'circle',
name: 'concentric',
headless: false,
styleEnabled: true,
animate: false,
}).run();
// function runLayouts(fit, callBack) {
// // step-1 position child nodes
// var parentNodes = cy.nodes(':parent');
// var grid_layout = parentNodes.descendants().layout({
// name: 'grid',
// cols: 1,
// fit: fit,
// });
// grid_layout.promiseOn('layoutstop').then(function (event) {
// // step-2 position parent nodes
// var dagre_layout = parentNodes.layout({
// name: 'dagre',
// rankDir: 'TB',
// fit: fit,
// });
// dagre_layout.promiseOn('layoutstop').then(function (event) {
// if (callBack) {
// callBack.call(cy, event);
// }
// });
// dagre_layout.run();
// });
// grid_layout.run();
// }
// runLayouts();
// log.info('Positions', cy.nodes().positions());
// window.cy = cy;
cy.ready((e) => {
log.info('Ready', e, cy.data());
// // setTimeout(() => {
cy.nodes().map((node, id) => {
const data = node.data();
log.info(
'Position: (',
node.position().x,
', ',
node.position().y,
')',
data,
cy.elements()[0].renderedBoundingBox()
);
if (data.el) {
data.el.attr('transform', `translate(${node.position().x}, ${node.position().y})`);
// document
// .querySelector(`[id="${data.domId}"]`)
// .setAttribute('transform', `translate(${node.position().x}, ${node.position().y})`);
log.info('Id = ', data.domId, svg.select(`[id="${data.domId}"]`), data.el.node());
}
// else {
// // console.log('No element found for node', data, node.position(), node.size());
// }
});
cy.edges().map((edge, id) => {
const data = edge.data();
if (edge[0]._private.bodyBounds) {
const bounds = edge[0]._private.rscratch;
// insertEdge(edgesEl, edge, data.edgeData, bounds, diagObj);
}
});
log.info(cy.json());
setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
// Remove element after layout
// renderEl.remove();
resolve();
// }, 500);
});
});
};
export default {
// setConf,
// addVertices,
// addEdges,
getClasses,
draw,
};

View File

@@ -1,20 +1,24 @@
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 graphlib from 'graphlib';
import { select, line, curveLinear, curveCardinal, curveBasis, selectAll } from 'd3';
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
import { insertNode } from '../../mermaid/src/dagre-wrapper/nodes.js';
import insertMarkers from '../../mermaid/src/dagre-wrapper/markers.js';
import createLabel from '../../mermaid/src/dagre-wrapper/createLabel';
import { insertEdgeLabel, positionEdgeLabel } from '../../mermaid/src/dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils';
// Replace with other function to avoid dependency to dagre-d3
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 common, { evaluate } from '../../mermaid/src/diagrams/common/common';
import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils';
// import ELK from 'elkjs/lib/elk-api';
// const elk = new ELK({
// workerUrl: './elk-worker.min.js',
// });
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);
@@ -97,36 +101,8 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
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':
@@ -138,9 +114,6 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
break;
case 'diamond':
_shape = 'question';
layoutOptions = {
portConstraints: 'FIXED_SIDE',
};
break;
case 'hexagon':
_shape = 'hexagon';
@@ -217,15 +190,13 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
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,
// rx: radious,
// ry: radious,
// class: classStr,
// style: styles.style,
// link: vertex.link,
@@ -253,8 +224,8 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
// labelStyle: styles.labelStyle,
// shape: _shape,
// labelText: vertexText,
// rx: radius,
// ry: radius,
// rx: radious,
// ry: radious,
// class: classStr,
// style: styles.style,
// id: vertex.id,
@@ -270,127 +241,6 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
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
*
@@ -402,10 +252,11 @@ const getEdgeStartEndPoint = (edge, dir) => {
* @param svg
*/
export const addEdges = function (edges, diagObj, graph, svg) {
log.info('abc78 edges = ', edges);
// log.info('abc78 edges = ', edges);
const labelsEl = svg.insert('g').attr('class', 'edgeLabels');
let cnt = 0;
let linkIdCnt = {};
let dir = diagObj.db.getDirection();
let defaultStyle;
let defaultLabelStyle;
@@ -416,6 +267,8 @@ export const addEdges = function (edges, diagObj, graph, svg) {
}
edges.forEach(function (edge) {
cnt++;
// Identify Link
var linkIdBase = 'L-' + edge.start + '-' + edge.end;
// count the links from+to the same node to give unique id
@@ -529,20 +382,19 @@ export const addEdges = function (edges, diagObj, graph, svg) {
edgeData.id = linkId;
edgeData.classes = 'flowchart-link ' + linkNameStart + ' ' + linkNameEnd;
const edgesNode = select(edges);
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);
// console.log('labelEl', labelEl, edgeData.width);
// Add the edge to the graph
graph.edges.push({
id: 'e' + edge.start + edge.end,
sources: [source],
targets: [target],
sources: [edge.start],
targets: [edge.end],
labelEl: labelEl,
labels: [
{
width: edgeData.width,
// width: 80,
height: edgeData.height,
orgWidth: edgeData.width,
orgHeight: edgeData.height,
@@ -554,6 +406,8 @@ export const addEdges = function (edges, diagObj, graph, svg) {
},
],
edgeData,
// targetPort: 'PortSide.NORTH',
// id: cnt,
});
});
return graph;
@@ -661,7 +515,7 @@ export const getClasses = function (text, diagObj) {
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
return {};
return;
}
};
@@ -688,17 +542,45 @@ const addSubGraphs = function (db) {
return parentLookupDb;
};
/* Reverse engineered with trial and error */
const calcOffsetOld = function (src, dest, sourceId, targetId, srcDepth, targetDepth, so, to) {
// if (src === dest) {
// return src;
// }
// if (sourceId === 'B6') {
// return 0;
// }
// if (sourceId === 'B4') {
// return 318;
// }
if (srcDepth < targetDepth) {
return src;
}
if (srcDepth > targetDepth) {
return dest;
}
if (srcDepth === targetDepth) {
return src;
}
// if (src < dest) {
// return dest + src;
// }
return 0;
};
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 ancestoprOffset = nodeDb[ancestor].offset;
return { x: ancestoprOffset.posX, y: ancestoprOffset.posY };
};
const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb) {
const srcOffset = nodeDb[edge.sources[0]].offset;
const targetOffset = nodeDb[edge.targets[0]].offset;
const offset = calcOffset(edge.sources[0], edge.targets[0], parentLookupDb);
const src = edge.sections[0].startPoint;
@@ -765,7 +647,7 @@ const insertChildren = (nodeArray, parentLookupDb) => {
* @param id
*/
export const draw = async function (text, id, _version, diagObj) {
export const draw = function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
nodeDb = {};
@@ -773,133 +655,149 @@ export const draw = async function (text, id, _version, diagObj) {
// 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);
return new Promise(function (resolve, reject) {
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
// .attr('style', 'display:none')
let graph = {
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
// 'elk.hierarchyHandling': 'SEPARATE_CHILDREN',
'org.eclipse.elk.padding': '[top=100, left=100, bottom=110, right=110]',
// 'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 120,
// 'elk.layered.spacing.nodeNodeBetweenLayers': '140',
'elk.layered.spacing.edgeNodeBetweenLayers': '30',
// 'elk.algorithm': 'layered',
'elk.direction': 'DOWN',
// 'elk.port.side': 'SOUTH',
// 'nodePlacement.strategy': 'SIMPLE',
// 'org.eclipse.elk.spacing.labelLabel': 120,
// 'org.eclipse.elk.graphviz.concentrate': true,
// 'org.eclipse.elk.spacing.nodeNode': 120,
// 'org.eclipse.elk.spacing.edgeEdge': 120,
// 'org.eclipse.elk.spacing.edgeNode': 120,
// 'org.eclipse.elk.spacing.nodeEdge': 120,
// 'org.eclipse.elk.spacing.componentComponent': 120,
},
children: [],
edges: [],
};
log.info('Drawing flowchart using v3 renderer');
// 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);
// 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;
}
// Subgraph
if (parentLookupDb.childrenById[nodeId] !== undefined) {
node.labels = [
{
text: node.labelText,
layoutOptions: {
'nodeLabels.placement': '[H_CENTER, V_TOP, INSIDE]',
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);
}
// node.nodePadding = [120, 50, 50, 50];
// node['org.eclipse.elk.spacing.nodeNode'] = 120;
// 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,
},
width: node.labelData.width,
height: node.labelData.height,
},
];
delete node.x;
delete node.y;
delete node.width;
delete node.height;
}
];
delete node.x;
delete node.y;
delete node.width;
delete node.height;
}
});
insertChildren(graph.children, parentLookupDb);
elk.layout(graph).then(function (g) {
drawNodes(0, 0, g.children, svg, subGraphsEl, diagObj, 0);
g.edges.map((edge, id) => {
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb);
});
setupGraphViewbox({}, svg, conf.diagramPadding, conf.useMaxWidth);
resolve();
});
// Remove element after layout
renderEl.remove();
});
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) => {
@@ -948,6 +846,9 @@ const drawNodes = (relX, relY, nodeArray, svg, subgraphsEl, diagObj, depth) => {
};
export default {
// setConf,
// addVertices,
// addEdges,
getClasses,
draw,
};

View File

@@ -0,0 +1,56 @@
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,9 +1,4 @@
export interface TreeData {
parentById: Record<string, string>;
childrenById: Record<string, string[]>;
}
export const findCommonAncestor = (id1: string, id2: string, treeData: TreeData) => {
export const findCommonAncestor = (id1, id2, treeData) => {
const { parentById } = treeData;
const visited = new Set();
let currentId = id1;

View File

@@ -0,0 +1,24 @@
import { findCommonAncestor } from './render-utils';
describe('when rendering a flowchart using elk ', function () {
let lookupDb;
beforeEach(function () {
lookupDb = JSON.parse(
'{"parentById":{"B4":"inner","B5":"inner","C4":"inner2","C5":"inner2","B2":"Ugge","B3":"Ugge","inner":"Ugge","inner2":"Ugge","B6":"outer"},"childrenById":{"inner":["B4","B5"],"inner2":["C4","C5"],"Ugge":["B2","B3","inner","inner2"],"outer":["B6"]}}'
);
});
it('Sieblings in a subgraph', function () {
expect(findCommonAncestor('B4', 'B5', lookupDb)).toBe('inner');
});
it('Find an uncle', function () {
expect(findCommonAncestor('B4', 'B2', lookupDb)).toBe('Ugge');
});
it('Find a cousin', function () {
expect(findCommonAncestor('B4', 'C4', lookupDb)).toBe('Ugge');
});
it('Find a grandparent', function () {
expect(findCommonAncestor('B4', 'B6', lookupDb)).toBe('root');
});
it('Sieblings in the root', function () {
expect(findCommonAncestor('B1', 'outer', lookupDb)).toBe('root');
});
});

View File

@@ -13,10 +13,9 @@ export interface FlowChartStyleOptions {
tertiaryColor: string;
textColor: string;
titleColor: string;
[key: string]: string;
}
const genSections = (options: FlowChartStyleOptions) => {
const genSections = (options) => {
let sections = '';
for (let i = 0; i < 5; i++) {

View File

@@ -0,0 +1,64 @@
{
"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,4 +1,5 @@
import type { ExternalDiagramDefinition } from '../../diagram-api/types';
import type { ExternalDiagramDefinition } from 'mermaid';
const id = 'mindmap';
const detector = (txt: string) => {

View File

@@ -3,10 +3,12 @@ import mindmapParser from './parser/mindmap';
import * as mindmapDb from './mindmapDb';
import mindmapRenderer from './mindmapRenderer';
import mindmapStyles from './styles';
import { injectUtils } from './mermaidUtils';
export const diagram = {
db: mindmapDb,
renderer: mindmapRenderer,
parser: mindmapParser,
styles: mindmapStyles,
injectUtils,
};

View File

@@ -0,0 +1,56 @@
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,16 +1,16 @@
import { parser as mindmap } from './parser/mindmap';
import * as mindmapDB from './mindmapDb';
// import { injectUtils } from './mermaidUtils';
import { injectUtils } from './mermaidUtils';
// Todo fix utils functions for tests
import {
// log,
log,
setLogLevel,
// getConfig,
// sanitizeText,
// setupGraphViewBox,
} from '../../diagram-api/diagramAPI';
getConfig,
sanitizeText,
setupGraphViewBox,
} from '../../mermaid/src/diagram-api/diagramAPI';
// injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewBox);
injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewBox);
describe('when parsing a mindmap ', function () {
beforeEach(function () {
@@ -347,40 +347,4 @@ root
expect(child.children.length).toEqual(2);
expect(child.children[1].nodeId).toEqual('b');
});
it('MMP-23 Rows with only spaces should not interfere', function () {
let str = 'mindmap\nroot\n A\n \n\n B';
mindmap.parse(str);
const mm = mindmap.yy.getMindmap();
expect(mm.nodeId).toEqual('root');
expect(mm.children.length).toEqual(2);
const child = mm.children[0];
expect(child.nodeId).toEqual('A');
const child2 = mm.children[1];
expect(child2.nodeId).toEqual('B');
});
it('MMP-24 Handle rows above the mindmap declarations', function () {
let str = '\n \nmindmap\nroot\n A\n \n\n B';
mindmap.parse(str);
const mm = mindmap.yy.getMindmap();
expect(mm.nodeId).toEqual('root');
expect(mm.children.length).toEqual(2);
const child = mm.children[0];
expect(child.nodeId).toEqual('A');
const child2 = mm.children[1];
expect(child2.nodeId).toEqual('B');
});
it('MMP-25 Handle rows above the mindmap declarations, no space', function () {
let str = '\n\n\nmindmap\nroot\n A\n \n\n B';
mindmap.parse(str);
const mm = mindmap.yy.getMindmap();
expect(mm.nodeId).toEqual('root');
expect(mm.children.length).toEqual(2);
const child = mm.children[0];
expect(child.nodeId).toEqual('A');
const child2 = mm.children[1];
expect(child2.nodeId).toEqual('B');
});
});

View File

@@ -1,8 +1,5 @@
import { getConfig } from '../../config';
import { sanitizeText as _sanitizeText } from '../../diagrams/common/common';
import { log } from '../../logger';
export const sanitizeText = (text) => _sanitizeText(text, getConfig());
/** Created by knut on 15-01-14. */
import { sanitizeText, getConfig, log } from './mermaidUtils';
let nodes = [];
let cnt = 0;
@@ -20,11 +17,11 @@ const getParent = function (level) {
}
}
// No parent found
return null;
return undefined;
};
export const getMindmap = () => {
return nodes.length > 0 ? nodes[0] : null;
return nodes.length > 0 ? nodes[0] : undefined;
};
export const addNode = (level, id, descr, type) => {
log.info('addNode', level, id, descr, type);

View File

@@ -1,8 +1,6 @@
/** Created by knut on 14-12-11. */
import { select } from 'd3';
import { log } from '../../logger';
import { getConfig } from '../../config';
import { setupGraphViewbox } from '../../setupGraphViewbox';
import { log, getConfig, setupGraphViewbox } from './mermaidUtils';
import svgDraw from './svgDraw';
import cytoscape from 'cytoscape';
import coseBilkent from 'cytoscape-cose-bilkent';
@@ -91,7 +89,6 @@ function addNodes(mindmap, cy, conf, level) {
/**
* @param node
* @param conf
* @param cy
*/
function layoutMindmap(node, conf) {
return new Promise((resolve) => {
@@ -134,10 +131,7 @@ function layoutMindmap(node, conf) {
});
}
/**
* @param node
* @param cy
* @param positionedMindmap
* @param conf
*/
function positionNodes(cy) {
cy.nodes().map((node, id) => {

View File

@@ -25,7 +25,6 @@
<CLASS>\n { this.popState();}
// [\s]*"::icon(" { this.begin('ICON'); }
"::icon(" { yy.getLogger().trace('Begin icon');this.begin('ICON'); }
[\s]+[\n] {yy.getLogger().trace('SPACELINE');return 'SPACELINE' /* skip all whitespace */ ;}
[\n]+ return 'NL';
<ICON>[^\)]+ { return 'ICON'; }
<ICON>\) {yy.getLogger().trace('end icon');this.popState();}
@@ -65,25 +64,14 @@
start
// %{ : info document 'EOF' { return yy; } }
: mindMap
| spaceLines mindMap
;
spaceLines
: SPACELINE
| spaceLines SPACELINE
| spaceLines NL
;
mindMap
: MINDMAP document { return yy; }
| MINDMAP NL document { return yy; }
;
: MINDMAP document { return yy; }
| MINDMAP NL document { return yy; }
| SPACELIST MINDMAP document { return yy; }
;
stop
: NL {yy.getLogger().trace('Stop NL ');}
| EOF {yy.getLogger().trace('Stop EOF ');}
| SPACELINE
| stop NL {yy.getLogger().trace('Stop NL2 ');}
| stop EOF {yy.getLogger().trace('Stop EOF2 ');}
;
@@ -93,10 +81,9 @@ document
;
statement
: SPACELIST node { yy.getLogger().info('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type); }
: SPACELIST node { yy.getLogger().trace('Node: ',$2.id);yy.addNode($1.length, $2.id, $2.descr, $2.type); }
| SPACELIST ICON { yy.getLogger().trace('Icon: ',$2);yy.decorateNode({icon: $2}); }
| SPACELIST CLASS { yy.decorateNode({class: $2}); }
| SPACELINE { yy.getLogger().trace('SPACELIST');}
| node { yy.getLogger().trace('Node: ',$1.id);yy.addNode(0, $1.id, $1.descr, $1.type); }
| ICON { yy.decorateNode({icon: $1}); }
| CLASS { yy.decorateNode({class: $1}); }

View File

@@ -19,7 +19,7 @@ function wrap(text, width) {
y = text.attr('y'),
dy = parseFloat(text.attr('dy')),
tspan = text
.text(null)
.text(undefined)
.append('tspan')
.attr('x', 0)
.attr('y', y)
@@ -203,14 +203,15 @@ const roundedRectBkg = function (elem, node) {
* @returns {number} The height nodes dom element
*/
export const drawNode = function (elem, node, fullSection, conf) {
const section = fullSection % (MAX_SECTIONS - 1);
const section = (fullSection % MAX_SECTIONS) - 1;
const nodeElem = elem.append('g');
node.section = section;
let sectionClass = 'section-' + section;
if (section < 0) {
sectionClass += ' section-root';
}
nodeElem.attr('class', (node.class ? node.class + ' ' : '') + 'mindmap-node ' + sectionClass);
nodeElem.attr(
'class',
(node.class ? node.class + ' ' : '') +
'mindmap-node ' +
(section < 0 ? 'section-root' : 'section-' + section)
);
const bkgElem = nodeElem.append('g');
// Create the wrapped text element
@@ -304,7 +305,7 @@ export const drawNode = function (elem, node, fullSection, conf) {
};
export const drawEdge = function drawEdge(edgesElem, mindmap, parent, depth, fullSection) {
const section = fullSection % (MAX_SECTIONS - 1);
const section = (fullSection % MAX_SECTIONS) - 1;
const sx = parent.x + parent.width / 2;
const sy = parent.y + parent.height / 2;
const ex = mindmap.x + mindmap.width / 2;

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "mermaid",
"version": "9.4.0",
"version": "9.3.0",
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
"main": "./dist/mermaid.min.js",
"module": "./dist/mermaid.core.mjs",
@@ -53,23 +53,18 @@
},
"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",
"dagre-d3-es": "7.0.8",
"dagre-d3-es": "7.0.6",
"dompurify": "2.4.3",
"elkjs": "^0.8.2",
"khroma": "^2.0.0",
"lodash-es": "^4.17.21",
"moment": "^2.29.4",
"moment-mini": "^2.24.0",
"non-layered-tidy-tree-layout": "^2.0.2",
"stylis": "^4.1.2",
"ts-dedent": "^2.2.0",
"uuid": "^9.0.0"
},
"devDependencies": {
"@types/cytoscape": "^3.19.9",
"@types/d3": "^7.4.0",
"@types/dompurify": "^2.4.0",
"@types/jsdom": "^20.0.1",
@@ -90,10 +85,10 @@
"js-base64": "^3.7.2",
"jsdom": "^20.0.2",
"micromatch": "^4.0.5",
"moment": "^2.29.4",
"path-browserify": "^1.0.1",
"prettier": "^2.7.1",
"remark": "^14.0.2",
"remark-frontmatter": "^4.0.1",
"remark-gfm": "^3.0.1",
"rimraf": "^3.0.2",
"start-server-and-test": "^1.14.0",

View File

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

View File

@@ -26,7 +26,6 @@ export interface MermaidConfig {
sequence?: SequenceDiagramConfig;
gantt?: GanttDiagramConfig;
journey?: JourneyDiagramConfig;
timeline?: TimelineDiagramConfig;
class?: ClassDiagramConfig;
state?: StateDiagramConfig;
er?: ErDiagramConfig;
@@ -293,30 +292,6 @@ export interface JourneyDiagramConfig extends BaseDiagramConfig {
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 {
titleTopMargin?: number;
barHeight?: number;

View File

@@ -1007,7 +1007,6 @@ const class_box = (parent, node) => {
};
const shapes = {
rhombus: question,
question,
rect,
labelRect,

View File

@@ -862,156 +862,6 @@ const config: Partial<MermaidConfig> = {
sectionFills: ['#191970', '#8B008B', '#4B0082', '#2F4F4F', '#800000', '#8B4513', '#00008B'],
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: {
/**
* ### titleTopMargin

View File

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

View File

@@ -1,4 +1,5 @@
import { registerDiagram } from './diagramAPI';
// @ts-ignore: TODO Fix ts errors
import gitGraphParser from '../diagrams/git/parser/gitGraph';
import { gitGraphDetector } from '../diagrams/git/gitGraphDetector';
@@ -93,13 +94,6 @@ import { setConfig } from '../config';
import errorRenderer from '../diagrams/error/errorRenderer';
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;
export const addDiagrams = () => {
if (hasLoadedDiagrams) {
@@ -108,8 +102,6 @@ export const addDiagrams = () => {
// This is added here to avoid race-conditions.
// We could optimize the loading logic somehow.
hasLoadedDiagrams = true;
registerLazyLoadedDiagrams(flowchartElk, timelineDetector, mindmapDetector);
registerDiagram(
'error',
// Special diagram with error messages but setup as a regular diagram
@@ -149,12 +141,12 @@ export const addDiagrams = () => {
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"
'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
init: () => undefined, // no op
},
(text) => {
return text.toLowerCase().trimStart().startsWith('---');

View File

@@ -5,8 +5,6 @@ import { sanitizeText as _sanitizeText } from '../diagrams/common/common';
import { setupGraphViewbox as _setupGraphViewbox } from '../setupGraphViewbox';
import { addStylesForDiagram } from '../styles';
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
@@ -18,11 +16,6 @@ export const setLogLevel = _setLogLevel;
export const getConfig = _getConfig;
export const sanitizeText = (text: string) => _sanitizeText(text, getConfig());
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> = {};
export interface Detectors {
@@ -53,15 +46,7 @@ export const registerDiagram = (
addStylesForDiagram(id, diagram.styles);
if (diagram.injectUtils) {
diagram.injectUtils(
log,
setLogLevel,
getConfig,
sanitizeText,
setupGraphViewbox,
getCommonDb(),
parseDirective
);
diagram.injectUtils(log, setLogLevel, getConfig, sanitizeText, setupGraphViewbox);
}
};

View File

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

View File

@@ -12,8 +12,8 @@ let boundarys = [
alias: 'global',
label: { text: 'global' },
type: { text: 'global' },
tags: null,
link: null,
tags: undefined,
link: undefined,
parentBoundary: '',
},
];
@@ -728,8 +728,8 @@ export const clear = function () {
alias: 'global',
label: { text: 'global' },
type: { text: 'global' },
tags: null,
link: null,
tags: undefined,
link: undefined,
parentBoundary: '',
},
];

View File

@@ -347,7 +347,7 @@ let getIntersectPoint = function (fromNode, endPoint) {
let fromDYX = fromNode.height / fromNode.width;
let returnPoint = null;
let returnPoint = undefined;
if (y1 == y2 && x1 < x2) {
returnPoint = new Point(x1 + fromNode.width, fromCenterY);
@@ -599,7 +599,7 @@ export const draw = function (_text, id, _version, diagObj) {
c4ShapeInRow = db.getC4ShapeInRow();
c4BoundaryInRow = db.getC4BoundaryInRow();
log.debug(`C:${JSON.stringify(conf, null, 2)}`);
log.debug(`C:${JSON.stringify(conf, undefined, 2)}`);
const diagram =
securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : select(`[id="${id}"]`);

View File

@@ -59,7 +59,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
// Check to see if any of the attributes has a key or a comment
attributes.forEach((item) => {
if (item.attributeKeyTypeList !== undefined && item.attributeKeyTypeList.length > 0) {
if (item.attributeKeyType !== undefined) {
hasKeyType = true;
}
@@ -112,9 +112,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
nodeHeight = Math.max(typeBBox.height, nameBBox.height);
if (hasKeyType) {
const keyTypeNodeText =
item.attributeKeyTypeList !== undefined ? item.attributeKeyTypeList.join(',') : '';
const keyTypeNode = groupNode
.append('text')
.classed('er entityLabel', true)
@@ -125,7 +122,7 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
.style('text-anchor', 'left')
.style('font-family', getConfig().fontFamily)
.style('font-size', attrFontSize + 'px')
.text(keyTypeNodeText);
.text(item.attributeKeyType || '');
attributeNode.kn = keyTypeNode;
const keyTypeBBox = keyTypeNode.node().getBBox();

View File

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

View File

@@ -135,37 +135,6 @@ 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 () {
const entity = 'BOOK';
const attribute = 'string title';
@@ -221,28 +190,6 @@ describe('when parsing ER diagram it...', function () {
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 () {
const entity = 'BOOK';
const attribute1 = 'type~T~ type';

View File

@@ -1,55 +0,0 @@
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

@@ -1,29 +0,0 @@
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

@@ -1,13 +0,0 @@
// @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

@@ -1,40 +0,0 @@
import { findCommonAncestor, TreeData } from './render-utils';
describe('when rendering a flowchart using elk ', () => {
let lookupDb: TreeData;
beforeEach(() => {
lookupDb = {
parentById: {
B4: 'inner',
B5: 'inner',
C4: 'inner2',
C5: 'inner2',
B2: 'Ugge',
B3: 'Ugge',
inner: 'Ugge',
inner2: 'Ugge',
B6: 'outer',
},
childrenById: {
inner: ['B4', 'B5'],
inner2: ['C4', 'C5'],
Ugge: ['B2', 'B3', 'inner', 'inner2'],
outer: ['B6'],
},
};
});
it('to find parent of siblings in a subgraph', () => {
expect(findCommonAncestor('B4', 'B5', lookupDb)).toBe('inner');
});
it('to find an uncle', () => {
expect(findCommonAncestor('B4', 'B2', lookupDb)).toBe('Ugge');
});
it('to find a cousin', () => {
expect(findCommonAncestor('B4', 'C4', lookupDb)).toBe('Ugge');
});
it('to find a grandparent', () => {
expect(findCommonAncestor('B4', 'B6', lookupDb)).toBe('root');
});
it('to find ancestor of siblings in the root', () => {
expect(findCommonAncestor('B1', 'outer', lookupDb)).toBe('root');
});
});

View File

@@ -238,9 +238,6 @@ export const setDirection = function (dir) {
if (direction.match(/.*v/)) {
direction = 'TB';
}
if (direction === 'TD') {
direction = 'TB';
}
};
/**

View File

@@ -82,7 +82,6 @@ that id.
<click>[\s\n] this.popState();
<click>[^\s\n]* return 'CLICK';
"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"subgraph" return 'subgraph';

View File

@@ -1,4 +1,4 @@
import moment from 'moment';
import moment from 'moment-mini';
import { sanitizeUrl } from '@braintree/sanitize-url';
import { log } from '../../logger';
import * as configApi from '../../config';
@@ -176,7 +176,7 @@ const checkTaskDates = function (task, dateFormat, excludes, includes) {
const fixTaskDates = function (startTime, endTime, dateFormat, excludes, includes) {
let invalid = false;
let renderEndTime = null;
let renderEndTime = undefined;
while (startTime <= endTime) {
if (!invalid) {
renderEndTime = endTime.toDate();
@@ -199,7 +199,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
if (afterStatement !== null) {
// check all after ids and take the latest
let latestEndingTask = null;
let latestEndingTask = undefined;
afterStatement[1].split(' ').forEach(function (id) {
let task = findTaskById(id);
if (task !== undefined) {
@@ -419,7 +419,7 @@ export const addTask = function (descr, data) {
type: currentSection,
processed: false,
manualEndTime: false,
renderEndTime: null,
renderEndTime: undefined,
raw: { data: data },
task: descr,
classes: [],

View File

@@ -1,5 +1,5 @@
// @ts-nocheck TODO: Fix TS
import moment from 'moment';
import moment from 'moment-mini';
import ganttDb from './ganttDb';
import { convert } from '../../tests/util';
@@ -191,7 +191,7 @@ describe('when using the ganttDb', function () {
expect(tasks[3].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[3].endTime).toEqual(moment('2019-02-20', 'YYYY-MM-DD').toDate());
expect(tasks[3].renderEndTime).toBeNull(); // Fixed end
expect(tasks[3].renderEndTime).toBeUndefined(); // Fixed end
expect(tasks[3].id).toEqual('id4');
expect(tasks[3].task).toEqual('test4');
@@ -360,13 +360,13 @@ describe('when using the ganttDb', function () {
expect(tasks[0].startTime).toEqual(moment('2019-09-30', 'YYYY-MM-DD').toDate());
expect(tasks[0].endTime).toEqual(moment('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].renderEndTime).toBeUndefined(); // Fixed end
expect(tasks[0].id).toEqual('id1');
expect(tasks[0].task).toEqual('test1');
expect(tasks[1].startTime).toEqual(moment('2019-10-11', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-10-31', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].renderEndTime).toBeUndefined(); // Fixed end
expect(tasks[1].id).toEqual('id2');
expect(tasks[1].task).toEqual('test2');
});
@@ -387,7 +387,7 @@ describe('when using the ganttDb', function () {
expect(tasks[1].startTime).toEqual(moment('2019-02-01', 'YYYY-MM-DD').toDate());
expect(tasks[1].endTime).toEqual(moment('2019-02-04', 'YYYY-MM-DD').toDate());
expect(tasks[1].renderEndTime).toBeNull(); // Fixed end
expect(tasks[1].renderEndTime).toBeUndefined(); // Fixed end
expect(tasks[1].manualEndTime).toBeTruthy();
expect(tasks[1].id).toEqual('id2');
expect(tasks[1].task).toEqual('test2');

View File

@@ -1,4 +1,4 @@
import moment from 'moment';
import moment from 'moment-mini';
import { log } from '../../logger';
import {
select,
@@ -434,7 +434,7 @@ export const draw = function (text, id, version, diagObj) {
}
const excludeRanges = [];
let range = null;
let range = undefined;
let d = moment(minTime);
while (d.valueOf() <= maxTime) {
if (diagObj.db.isInvalidDate(d, dateFormat, excludes, includes)) {
@@ -449,7 +449,7 @@ export const draw = function (text, id, version, diagObj) {
} else {
if (range) {
excludeRanges.push(range);
range = null;
range = undefined;
}
}
d.add(1, 'd');

View File

@@ -125,6 +125,7 @@ describe('when parsing a gantt diagram it', function () {
'click cl2 call ganttTestClick()\n';
expect(parserFnConstructor(str)).not.toThrow();
// eslint-disable-next-line unicorn/no-null
expect(ganttDb.setClickEvent).toHaveBeenCalledWith('cl2', 'ganttTestClick', null);
});
it('should parse callback specifier with arbitrary number of args', function () {

View File

@@ -17,7 +17,7 @@ import {
let mainBranchName = getConfig().gitGraph.mainBranchName;
let mainBranchOrder = getConfig().gitGraph.mainBranchOrder;
let commits = {};
let head = null;
let head = undefined;
let branchesConfig = {};
branchesConfig[mainBranchName] = { name: mainBranchName, order: mainBranchOrder };
let branches = {};
@@ -120,7 +120,7 @@ export const commit = function (msg, id, type, tag) {
seq: seq++,
type: type ? type : commitType.NORMAL,
tag: tag ? tag : '',
parents: head == null ? [] : [head.id],
parents: head == undefined ? [] : [head.id],
branch: curBranch,
};
head = commit;
@@ -246,7 +246,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
id: custom_id ? custom_id : seq + '-' + getId(),
message: 'merged branch ' + otherBranch + ' into ' + curBranch,
seq: seq++,
parents: [head == null ? null : head.id, branches[otherBranch]],
parents: [head?.id, branches[otherBranch]],
branch: curBranch,
type: commitType.MERGE,
customType: override_type,
@@ -330,7 +330,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
id: seq + '-' + getId(),
message: 'cherry-picked ' + sourceCommit + ' into ' + curBranch,
seq: seq++,
parents: [head == null ? null : head.id, sourceCommit.id],
parents: [head?.id, sourceCommit.id],
branch: curBranch,
type: commitType.CHERRY_PICK,
tag: tag ?? 'cherry-pick:' + sourceCommit.id,
@@ -443,7 +443,7 @@ export const prettyPrint = function () {
export const clear = function () {
commits = {};
head = null;
head = undefined;
let mainBranch = getConfig().gitGraph.mainBranchName;
let mainBranchOrder = getConfig().gitGraph.mainBranchOrder;
branches = {};

View File

@@ -851,15 +851,9 @@ describe('when parsing a gitGraph', function () {
merge test1
`;
try {
parser.parse(str);
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe(
'Incorrect usage of "merge". Branch to be merged (test1) has no commits'
);
}
expect(() => parser.parse(str)).to.throw(
'Incorrect usage of "merge". Branch to be merged (test1) has no commits'
);
});
describe('accessibility', () => {
it('should handle a title and a description (accDescr)', () => {

View File

@@ -6,10 +6,10 @@ export const getCommits = () => {
seq: 1,
message: '',
branch: 'master',
parents: null,
parents: undefined,
tag: 'v0.1',
commitType: 'normal',
note: null,
note: undefined,
},
'0000002': {
id: '0000002',
@@ -17,9 +17,9 @@ export const getCommits = () => {
message: '',
branch: 'develop',
parents: ['0000001'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000003': {
id: '0000003',
@@ -27,9 +27,9 @@ export const getCommits = () => {
message: '',
branch: 'featureB',
parents: ['0000002'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000004': {
id: '0000004',
@@ -37,9 +37,9 @@ export const getCommits = () => {
message: '',
branch: 'hotfix',
parents: ['0000001'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000005': {
id: '0000005',
@@ -47,9 +47,9 @@ export const getCommits = () => {
message: '',
branch: 'develop',
parents: ['0000002'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000006': {
id: '0000006',
@@ -57,9 +57,9 @@ export const getCommits = () => {
message: '',
branch: 'featureB',
parents: ['0000003'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000007': {
id: '0000007',
@@ -69,7 +69,7 @@ export const getCommits = () => {
parents: ['0000004'],
tag: 'v0.2',
commitType: 'normal',
note: null,
note: undefined,
},
'0000008': {
id: '0000008',
@@ -77,9 +77,9 @@ export const getCommits = () => {
message: '',
branch: 'featureB',
parents: ['0000006'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000009': {
id: '0000009',
@@ -87,9 +87,9 @@ export const getCommits = () => {
message: '',
branch: 'featureA',
parents: ['0000005'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000010': {
id: '0000010',
@@ -97,9 +97,9 @@ export const getCommits = () => {
message: '',
branch: 'develop',
parents: ['0000004', '0000005'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000011': {
id: '0000011',
@@ -107,7 +107,7 @@ export const getCommits = () => {
message: '',
branch: 'featureA',
parents: ['0000009'],
tag: null,
tag: undefined,
commitType: 'normal',
note: '',
},
@@ -117,9 +117,9 @@ export const getCommits = () => {
message: '',
branch: 'featureB',
parents: ['0000008'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000013': {
id: '0000013',
@@ -127,9 +127,9 @@ export const getCommits = () => {
message: '',
branch: 'develop',
parents: ['0000010', '0000011'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000014': {
id: '0000014',
@@ -137,9 +137,9 @@ export const getCommits = () => {
message: '',
branch: 'release',
parents: ['0000013'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000015': {
id: '0000015',
@@ -147,9 +147,9 @@ export const getCommits = () => {
message: '',
branch: 'master',
parents: ['0000007'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
'0000016': {
id: '0000016',
@@ -159,7 +159,7 @@ export const getCommits = () => {
parents: ['0000014', '0000015'],
tag: 'v1.0',
commitType: 'normal',
note: null,
note: undefined,
},
'0000017': {
id: '0000017',
@@ -167,9 +167,9 @@ export const getCommits = () => {
message: '',
branch: 'develop',
parents: ['0000013', '0000016'],
tag: null,
tag: undefined,
commitType: 'normal',
note: null,
note: undefined,
},
};
};

View File

@@ -35,9 +35,8 @@
\%%(?!\{)[^\n]* /* skip comments */
[^\}]\%\%[^\n]* /* skip comments */
[0-9]+(?=[ \n]+) return 'NUM';
"box" { this.begin('LINE'); return 'box'; }
"participant" { this.begin('ID'); return 'participant'; }
"actor" { this.begin('ID'); return 'participant_actor'; }
"actor" { this.begin('ID'); return 'participant_actor'; }
<ID>[^\->:\n,;]+?([\-]*[^\->:\n,;]+?)*?(?=((?!\n)\s)+"as"(?!\n)\s|[#\n;]|$) { yytext = yytext.trim(); this.begin('ALIAS'); return 'ACTOR'; }
<ALIAS>"as" { this.popState(); this.popState(); this.begin('LINE'); return 'AS'; }
<ALIAS>(?:) { this.popState(); this.popState(); return 'NEWLINE'; }
@@ -118,30 +117,16 @@ line
| NEWLINE { $$=[]; }
;
box_section
: /* empty */ { $$ = [] }
| box_section box_line {$1.push($2);$$ = $1}
;
box_line
: SPACE participant_statement { $$ = $2 }
| participant_statement { $$ = $1 }
| NEWLINE { $$=[]; }
;
directive
: openDirective typeDirective closeDirective 'NEWLINE'
| openDirective typeDirective ':' argDirective closeDirective 'NEWLINE'
;
statement
: participant_statement
| 'box' restOfLine box_section end
{
$3.unshift({type: 'boxStart', boxData:yy.parseBoxData($2) });
$3.push({type: 'boxEnd', boxText:$2});
$$=$3;}
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
| signal 'NEWLINE'
| autonumber NUM NUM 'NEWLINE' { $$= {type:'sequenceIndex',sequenceIndex: Number($2), sequenceIndexStep:Number($3), sequenceVisible:true, signalType:yy.LINETYPE.AUTONUMBER};}
| autonumber NUM 'NEWLINE' { $$ = {type:'sequenceIndex',sequenceIndex: Number($2), sequenceIndexStep:1, sequenceVisible:true, signalType:yy.LINETYPE.AUTONUMBER};}
@@ -224,13 +209,6 @@ else_sections
{ $$ = $1.concat([{type: 'else', altText:yy.parseMessage($3), signalType: yy.LINETYPE.ALT_ELSE}, $4]); }
;
participant_statement
: 'participant' actor 'AS' restOfLine 'NEWLINE' {$2.type='addParticipant';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant' actor 'NEWLINE' {$2.type='addParticipant';$$=$2;}
| 'participant_actor' actor 'AS' restOfLine 'NEWLINE' {$2.type='addActor';$2.description=yy.parseMessage($4); $$=$2;}
| 'participant_actor' actor 'NEWLINE' {$2.type='addActor'; $$=$2;}
;
note_statement
: 'note' placement actor text2
{

View File

@@ -14,81 +14,45 @@ import {
let prevActor = undefined;
let actors = {};
let boxes = [];
let messages = [];
const notes = [];
let sequenceNumbersEnabled = false;
let wrapEnabled;
let currentBox = undefined;
export const parseDirective = function (statement, context, type) {
mermaidAPI.parseDirective(this, statement, context, type);
};
export const addBox = function (data) {
boxes.push({
name: data.text,
wrap: (data.wrap === undefined && autoWrap()) || !!data.wrap,
fill: data.color,
actorKeys: [],
});
currentBox = boxes.slice(-1)[0];
};
export const addActor = function (id, name, description, type) {
let assignedBox = currentBox;
// Don't allow description nulling
const old = actors[id];
if (old) {
// If already set and trying to set to a new one throw error
if (currentBox && old.box && currentBox !== old.box) {
throw new Error(
'A same participant should only be defined in one Box: ' +
old.name +
" can't be in '" +
old.box.name +
"' and in '" +
currentBox.name +
"' at the same time."
);
}
// Don't change the box if already
assignedBox = old.box ? old.box : currentBox;
old.box = assignedBox;
// Don't allow description nulling
if (old && name === old.name && description == null) {
return;
}
if (old && name === old.name && description == undefined) {
return;
}
// Don't allow null descriptions, either
if (description == null || description.text == null) {
description = { text: name, wrap: null, type };
if (description == undefined || description.text == undefined) {
description = { text: name, wrap: undefined, type };
}
if (type == null || description.text == null) {
description = { text: name, wrap: null, type };
if (type == undefined || description.text == undefined) {
description = { text: name, wrap: undefined, type };
}
actors[id] = {
box: assignedBox,
name: name,
description: description.text,
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
prevActor: prevActor,
links: {},
properties: {},
actorCnt: null,
rectData: null,
actorCnt: undefined,
rectData: undefined,
type: type || 'participant',
};
if (prevActor && actors[prevActor]) {
actors[prevActor].nextActor = id;
}
if (currentBox) {
currentBox.actorKeys.push(id);
}
prevActor = id;
};
@@ -147,21 +111,10 @@ export const addSignal = function (
return true;
};
export const hasAtLeastOneBox = function () {
return boxes.length > 0;
};
export const hasAtLeastOneBoxWithTitle = function () {
return boxes.some((b) => b.name);
};
export const getMessages = function () {
return messages;
};
export const getBoxes = function () {
return boxes;
};
export const getActors = function () {
return actors;
};
@@ -194,7 +147,6 @@ export const autoWrap = () => {
export const clear = function () {
actors = {};
boxes = [];
messages = [];
sequenceNumbersEnabled = false;
commonClear();
@@ -215,47 +167,6 @@ export const parseMessage = function (str) {
return message;
};
// We expect the box statement to be color first then description
// The color can be rgb,rgba,hsl,hsla, or css code names #hex codes are not supported for now because of the way the char # is handled
// We extract first segment as color, the rest of the line is considered as text
export const parseBoxData = function (str) {
const match = str.match(/^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/);
let color = match != null && match[1] ? match[1].trim() : 'transparent';
let title = match != null && match[2] ? match[2].trim() : undefined;
// check that the string is a color
if (window && window.CSS) {
if (!window.CSS.supports('color', color)) {
color = 'transparent';
title = str.trim();
}
} else {
const style = new Option().style;
style.color = color;
if (style.color !== color) {
color = 'transparent';
title = str.trim();
}
}
const boxData = {
color: color,
text:
title !== undefined
? sanitizeText(title.replace(/^:?(?:no)?wrap:/, ''), configApi.getConfig())
: undefined,
wrap:
title !== undefined
? title.match(/^:?wrap:/) !== null
? true
: title.match(/^:?nowrap:/) !== null
? false
: undefined
: undefined,
};
return boxData;
};
export const LINETYPE = {
SOLID: 0,
DOTTED: 1,
@@ -363,13 +274,10 @@ export const addALink = function (actorId, text) {
* @param {any} links
*/
function insertLinks(actor, links) {
if (actor.links == null) {
actor.links = links;
} else {
for (let key in links) {
actor.links[key] = links[key];
}
}
actor.links = {
...actor.links,
...links,
};
}
export const addProperties = function (actorId, text) {
@@ -391,7 +299,7 @@ export const addProperties = function (actorId, text) {
* @param {any} properties
*/
function insertProperties(actor, properties) {
if (actor.properties == null) {
if (actor.properties == undefined) {
actor.properties = properties;
} else {
for (let key in properties) {
@@ -400,13 +308,6 @@ function insertProperties(actor, properties) {
}
}
/**
*
*/
function boxEnd() {
currentBox = undefined;
}
export const addDetails = function (actorId, text) {
// find the actor
const actor = getActor(actorId);
@@ -487,12 +388,6 @@ export const apply = function (param) {
case 'addMessage':
addSignal(param.from, param.to, param.msg, param.signalType);
break;
case 'boxStart':
addBox(param.boxData);
break;
case 'boxEnd':
boxEnd();
break;
case 'loopStart':
addSignal(undefined, undefined, param.loopText, param.signalType);
break;
@@ -569,14 +464,12 @@ export default {
getActorKeys,
getActorProperty,
getAccTitle,
getBoxes,
getDiagramTitle,
setDiagramTitle,
parseDirective,
getConfig: () => configApi.getConfig().sequence,
clear,
parseMessage,
parseBoxData,
LINETYPE,
ARROWTYPE,
PLACEMENT,
@@ -585,6 +478,4 @@ export default {
apply,
setAccDescription,
getAccDescription,
hasAtLeastOneBox,
hasAtLeastOneBoxWithTitle,
};

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