mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-08 16:49:38 +02:00
Merge branch 'develop' into sidv/splitDiagrams
* develop: (79 commits) Minor change feat: Add @include support to docs feat: Add @include example to docs feat: Add @include support to docs cleanup fix lines fix Async rendering Revert "sync" chore(deps): update pnpm to v7.17.1 chore(deps): remove dependency on `graphlib` test(e2e): make gitgraph snapshots consistent chore: Fix lint test: Update vitest Add official vim plugin to list in integrations chore: Cleanup package.json chore: Cleanup package.json chore: Cleanup package.json fix lock Docs Fix: array concat ...
This commit is contained in:
@@ -21,7 +21,17 @@
|
||||
"plugin:@cspell/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"plugins": ["@typescript-eslint", "no-only-tests", "html", "jest", "jsdoc", "json", "@cspell"],
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"no-only-tests",
|
||||
"html",
|
||||
"jest",
|
||||
"jsdoc",
|
||||
"json",
|
||||
"@cspell",
|
||||
"lodash",
|
||||
"unicorn"
|
||||
],
|
||||
"rules": {
|
||||
"curly": "error",
|
||||
"no-console": "error",
|
||||
@@ -53,7 +63,30 @@
|
||||
"allowEmptyCatch": true
|
||||
}
|
||||
],
|
||||
"no-only-tests/no-only-tests": "error"
|
||||
"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": [
|
||||
{
|
||||
|
28
.github/workflows/docs.yml
vendored
28
.github/workflows/docs.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Documentation Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- 'packages/mermaid/src/docs/**/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- develop
|
||||
paths:
|
||||
- 'packages/mermaid/src/docs/**/*'
|
||||
jobs:
|
||||
spellcheck:
|
||||
name: 'Docs: Spellcheck'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
name: Check out the code
|
||||
- uses: actions/setup-node@v3
|
||||
name: Setup node
|
||||
with:
|
||||
node-version: '18'
|
||||
- run: npm install -g cspell
|
||||
name: Install cSpell
|
||||
- run: cspell --config ./cSpell.json "packages/mermaid/src/docs/**/*.md" --no-progress
|
||||
name: Run cSpell
|
2
.github/workflows/link-checker.yml
vendored
2
.github/workflows/link-checker.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
- name: Link Checker
|
||||
uses: lycheeverse/lychee-action@v1.5.2
|
||||
uses: lycheeverse/lychee-action@v1.5.4
|
||||
with:
|
||||
args: --verbose --no-progress --cache --max-cache-age 1d packages/mermaid/src/docs/**/*.md README.md README.zh-CN.md
|
||||
fail: true
|
||||
|
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"!(docs/**/*)*.{ts,js,json,html,md,mts}": ["eslint --fix", "prettier --write"],
|
||||
"cSpell.json": ["ts-node-esm scripts/fixCSpell.ts"]
|
||||
}
|
5
.lintstagedrc.mjs
Normal file
5
.lintstagedrc.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
export default {
|
||||
'!(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'],
|
||||
};
|
@@ -3,4 +3,5 @@ cypress/platform/xss3.html
|
||||
.cache
|
||||
coverage
|
||||
# Autogenerated by PNPM
|
||||
pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
stats
|
@@ -16,13 +16,13 @@ type OutputOptions = Exclude<
|
||||
undefined
|
||||
>['output'];
|
||||
|
||||
const visualizerOptions = (packageName: string): PluginOption[] => {
|
||||
const visualizerOptions = (packageName: string, core = false): PluginOption[] => {
|
||||
if (packageName !== 'mermaid' || !visualize) {
|
||||
return [];
|
||||
}
|
||||
return ['network', 'treemap', 'sunburst'].map((chartType) =>
|
||||
visualizer({
|
||||
filename: `./stats/${chartType}.html`,
|
||||
filename: `./stats/${chartType}${core ? '.core' : ''}.html`,
|
||||
template: chartType as TemplateType,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
@@ -56,7 +56,7 @@ interface BuildOptions {
|
||||
}
|
||||
|
||||
export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions): InlineConfig => {
|
||||
const external = ['require', 'fs', 'path'];
|
||||
const external: (string | RegExp)[] = ['require', 'fs', 'path'];
|
||||
console.log(entryName, packageOptions[entryName]);
|
||||
const { name, file, packageName } = packageOptions[entryName];
|
||||
let output: OutputOptions = [
|
||||
@@ -80,7 +80,9 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
);
|
||||
// Core build is used to generate file without bundled dependencies.
|
||||
// This is used by downstream projects to bundle dependencies themselves.
|
||||
external.push(...Object.keys(dependencies));
|
||||
// Ignore dependencies and any dependencies of dependencies
|
||||
// Adapted from the RegEx used by `rollup-plugin-node`
|
||||
external.push(new RegExp('^(?:' + Object.keys(dependencies).join('|') + ')(?:/.+)?$'));
|
||||
// This needs to be an array. Otherwise vite will build esm & umd with same name and overwrite esm with umd.
|
||||
output = [
|
||||
{
|
||||
@@ -112,7 +114,7 @@ export const getBuildConfig = ({ minify, core, watch, entryName }: BuildOptions)
|
||||
resolve: {
|
||||
extensions: ['.jison', '.js', '.ts', '.json'],
|
||||
},
|
||||
plugins: [jisonPlugin(), ...visualizerOptions(packageName)],
|
||||
plugins: [jisonPlugin(), ...visualizerOptions(packageName, core)],
|
||||
};
|
||||
|
||||
if (watch && config.build) {
|
||||
@@ -149,6 +151,9 @@ if (watch) {
|
||||
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' }));
|
||||
await build(getBuildConfig({ minify: false, core: false, entryName: 'mermaid' }));
|
||||
} else {
|
||||
void main();
|
||||
}
|
||||
|
19
README.md
19
README.md
@@ -1,23 +1,6 @@
|
||||
# mermaid
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
# Whoa, what's going on here?
|
||||
|
||||
We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will:
|
||||
|
||||
- Make it possible to select which diagrams to include on your site
|
||||
- Open up for lazy loading
|
||||
- Make it possible to add diagrams from outside of the Mermaid repository
|
||||
- Separate the release flow between different diagrams and the Mermaid core
|
||||
|
||||
As such be aware of some changes..
|
||||
|
||||
# We use pnpm now
|
||||
|
||||
# The source code has moved
|
||||
|
||||
It is now located in the src folder for each respective package located as subfolders in packages.
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
English | [简体中文](./README.zh-CN.md)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# mermaid
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
[English](./README.md) | 简体中文
|
||||
|
||||
|
@@ -53,6 +53,18 @@ export const MockD3 = (name, parent) => {
|
||||
get __parent() {
|
||||
return parent;
|
||||
},
|
||||
node() {
|
||||
return {
|
||||
getBBox() {
|
||||
return {
|
||||
x: 5,
|
||||
y: 10,
|
||||
height: 15,
|
||||
width: 20,
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
elem.append = (name) => {
|
||||
const mockElem = MockD3(name, elem);
|
||||
|
@@ -13,8 +13,10 @@
|
||||
"bbox",
|
||||
"bilkent",
|
||||
"bisheng",
|
||||
"braintree",
|
||||
"brolin",
|
||||
"brotli",
|
||||
"classdef",
|
||||
"codedoc",
|
||||
"colour",
|
||||
"cpettitt",
|
||||
@@ -30,9 +32,11 @@
|
||||
"edgechromium",
|
||||
"faber",
|
||||
"flatmap",
|
||||
"ftplugin",
|
||||
"gantt",
|
||||
"gitea",
|
||||
"gitgraph",
|
||||
"globby",
|
||||
"graphlib",
|
||||
"grav",
|
||||
"greywolf",
|
||||
@@ -40,11 +44,13 @@
|
||||
"jaoude",
|
||||
"jison",
|
||||
"kaufmann",
|
||||
"khroma",
|
||||
"klemm",
|
||||
"klink",
|
||||
"knsv",
|
||||
"knut",
|
||||
"laganeckas",
|
||||
"lintstagedrc",
|
||||
"lucida",
|
||||
"matthieu",
|
||||
"mdbook",
|
||||
@@ -74,6 +80,7 @@
|
||||
"treemap",
|
||||
"ts-nocheck",
|
||||
"tuleap",
|
||||
"unist",
|
||||
"verdana",
|
||||
"viewports",
|
||||
"vinod",
|
||||
|
@@ -496,4 +496,16 @@ describe('Class diagram V2', () => {
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('1433: should render a simple class with a title', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
title: simple class diagram
|
||||
---
|
||||
classDiagram-v2
|
||||
class Class10
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -273,4 +273,17 @@ describe('Entity Relationship Diagram', () => {
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
|
||||
it('1433: should render a simple ER diagram with a title', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
title: simple ER diagram
|
||||
---
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -663,4 +663,15 @@ flowchart RL
|
||||
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
|
||||
);
|
||||
});
|
||||
it('1433: should render a titled flowchart with titleTopMargin set to 0', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
title: Simple flowchart
|
||||
---
|
||||
flowchart TD
|
||||
A --> B
|
||||
`,
|
||||
{ titleTopMargin: 0 }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -326,7 +326,7 @@ describe('Gantt diagram', () => {
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
const el = svg.get(0);
|
||||
const children = Array.from(el.children);
|
||||
const children = [...el.children];
|
||||
|
||||
const titleEl = children.find(function (node) {
|
||||
return node.tagName === 'title';
|
||||
|
@@ -322,4 +322,15 @@ describe('Git Graph diagram', () => {
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('1433: should render a simple gitgraph with a title', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
title: simple gitGraph
|
||||
---
|
||||
gitGraph
|
||||
commit id: "1-abcdefg"
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -1,75 +0,0 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
describe('Mindmap', () => {
|
||||
it('square shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root[
|
||||
The root
|
||||
]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('rounded rect shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root((
|
||||
The root
|
||||
))
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('circle shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root(
|
||||
The root
|
||||
)
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('default shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('adding children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('adding grand children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
child3
|
||||
`,
|
||||
{}
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
});
|
@@ -1,115 +0,0 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
describe('Mindmaps', () => {
|
||||
it('Only a root', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with a shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[root]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with wrapping text and a shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[A root with a long text that wraps to keep the node size in check]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with an icon', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[root]
|
||||
::icon(mdi mdi-fire)
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('Blang and cloud shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root))bang((
|
||||
::icon(mdi mdi-fire)
|
||||
a))Another bang((
|
||||
::icon(mdi mdi-fire)
|
||||
a)A cloud(
|
||||
::icon(mdi mdi-fire)
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('Blang and cloud shape with icons', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root))bang((
|
||||
|
||||
a))Another bang((
|
||||
a)A cloud(
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('braches', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('braches with shapes and labels', () => {
|
||||
imgSnapshotTest(
|
||||
`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)
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
it('text shouhld wrap with icon', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
/* The end */
|
||||
});
|
233
cypress/integration/rendering/mindmap.spec.ts
Normal file
233
cypress/integration/rendering/mindmap.spec.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
import { imgSnapshotTest, renderGraph } from '../../helpers/util.js';
|
||||
|
||||
/**
|
||||
* Check whether the SVG Element has a Mindmap root
|
||||
*
|
||||
* Sometimes, Cypress takes a snapshot before the mermaid mindmap has finished
|
||||
* generating the SVG.
|
||||
*
|
||||
* @param $p - The element to check.
|
||||
*/
|
||||
function shouldHaveRoot($p: JQuery<SVGSVGElement>) {
|
||||
// get HTML Element from jquery element
|
||||
const svgElement = $p[0];
|
||||
expect(svgElement.nodeName).equal('svg');
|
||||
|
||||
const sectionRoots = svgElement.getElementsByClassName('mindmap-node section-root');
|
||||
// mindmap should have at least one root section
|
||||
expect(sectionRoots).to.have.lengthOf.at.least(1);
|
||||
}
|
||||
|
||||
describe('Mindmaps', () => {
|
||||
it('Only a root', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with a shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[root]
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with wrapping text and a shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[A root with a long text that wraps to keep the node size in check]
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('a root with an icon', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root[root]
|
||||
::icon(mdi mdi-fire)
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('Blang and cloud shape', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root))bang((
|
||||
::icon(mdi mdi-fire)
|
||||
a))Another bang((
|
||||
::icon(mdi mdi-fire)
|
||||
a)A cloud(
|
||||
::icon(mdi mdi-fire)
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('Blang and cloud shape with icons', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root))bang((
|
||||
|
||||
a))Another bang((
|
||||
a)A cloud(
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('braches', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
child1
|
||||
grandchild 1
|
||||
grandchild 2
|
||||
child2
|
||||
grandchild 3
|
||||
grandchild 4
|
||||
child3
|
||||
grandchild 5
|
||||
grandchild 6
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
|
||||
it('braches with shapes and labels', () => {
|
||||
imgSnapshotTest(
|
||||
`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)
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
it('text shouhld wrap with icon', () => {
|
||||
imgSnapshotTest(
|
||||
`mindmap
|
||||
root
|
||||
Child3(A node with an icon and with a long text that wraps to keep the node size in check)
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
});
|
||||
it('square shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root[
|
||||
The root
|
||||
]
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('rounded rect shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root((
|
||||
The root
|
||||
))
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('circle shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
root(
|
||||
The root
|
||||
)
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('default shape', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('adding children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
it('adding grand children', () => {
|
||||
imgSnapshotTest(
|
||||
`
|
||||
mindmap
|
||||
The root
|
||||
child1
|
||||
child2
|
||||
child3
|
||||
`,
|
||||
{},
|
||||
undefined,
|
||||
shouldHaveRoot
|
||||
);
|
||||
cy.get('svg');
|
||||
});
|
||||
/* The end */
|
||||
});
|
@@ -96,7 +96,7 @@ describe('Requirement diagram', () => {
|
||||
);
|
||||
cy.get('svg').should((svg) => {
|
||||
const el = svg.get(0);
|
||||
const children = Array.from(el.children);
|
||||
const children = [...el.children];
|
||||
|
||||
const titleEl = children.find(function (node) {
|
||||
return node.tagName === 'title';
|
||||
|
@@ -559,4 +559,16 @@ stateDiagram-v2
|
||||
);
|
||||
});
|
||||
});
|
||||
it('1433: should render a simple state diagram with a title', () => {
|
||||
imgSnapshotTest(
|
||||
`---
|
||||
title: simple state diagram
|
||||
---
|
||||
stateDiagram-v2
|
||||
[*] --> State1
|
||||
State1 --> [*]
|
||||
`,
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@@ -25,6 +25,7 @@ describe('themeCSS balancing, it', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Delete/Rename this describe, keeping the inner contents.
|
||||
describe('Pie Chart', () => {
|
||||
// beforeEach(()=>{
|
||||
// cy.clock((new Date('2014-06-09')).getTime());
|
||||
|
@@ -56,10 +56,11 @@
|
||||
<body>
|
||||
<div>Security check</div>
|
||||
<pre id="diagram" class="mermaid">
|
||||
flowchart TD
|
||||
A --> B
|
||||
B --> C
|
||||
A --> C
|
||||
graph LR
|
||||
subgraph external
|
||||
inside
|
||||
end
|
||||
outside --> external
|
||||
</pre>
|
||||
<pre id="diagram" class="mermaid">
|
||||
mindmap
|
||||
@@ -91,9 +92,13 @@ mindmap
|
||||
<!-- <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 src="./mermaid.js"></script> -->
|
||||
|
||||
<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);
|
||||
};
|
||||
|
@@ -54,7 +54,7 @@ function merge(current, update) {
|
||||
if (
|
||||
current.hasOwnProperty(key) &&
|
||||
typeof current[key] === 'object' &&
|
||||
!(current[key] instanceof Array)
|
||||
!Array.isArray(current[key])
|
||||
) {
|
||||
merge(current[key], update[key]);
|
||||
|
||||
|
8
cypress/tsconfig.json
Normal file
8
cypress/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"lib": ["es2020", "dom"],
|
||||
"types": ["cypress", "node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
}
|
@@ -17,6 +17,9 @@
|
||||
<h1>Class diagram demos</h1>
|
||||
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: Demo Class Diagram
|
||||
---
|
||||
classDiagram
|
||||
accTitle: Demo Class Diagram
|
||||
accDescr: This class diagram show the abstract Animal class, and 3 classes that inherit from it: Duck, Fish, and Zebra.
|
||||
|
@@ -20,6 +20,9 @@
|
||||
<body>
|
||||
<pre class="mermaid">
|
||||
|
||||
---
|
||||
title: This is a title
|
||||
---
|
||||
erDiagram
|
||||
%% title This is a title
|
||||
%% accDescription Test a description
|
||||
|
@@ -17,6 +17,9 @@
|
||||
<h2>Sample 1</h2>
|
||||
<h3>graph</h3>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: This is a complicated flow
|
||||
---
|
||||
graph LR
|
||||
accTitle: This is a complicated flow
|
||||
accDescr: This is the descriptoin for the complicated flow.
|
||||
@@ -221,6 +224,9 @@
|
||||
<h2>Sample 2</h2>
|
||||
<h3>graph</h3>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: What to buy
|
||||
---
|
||||
graph TD
|
||||
accTitle: What to buy
|
||||
accDescr: Options of what to buy with Christmas money
|
||||
|
@@ -16,6 +16,9 @@
|
||||
<body>
|
||||
<h1>Git diagram demo</h1>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: Simple Git diagram
|
||||
---
|
||||
gitGraph:
|
||||
options
|
||||
{
|
||||
|
@@ -48,6 +48,9 @@
|
||||
<li>
|
||||
<h2><a href="./journey.html">Journey</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a href="./mindmap.html">Mindmap</a></h2>
|
||||
</li>
|
||||
<li>
|
||||
<h2><a href="./pie.html">Pie</a></h2>
|
||||
</li>
|
||||
|
@@ -16,8 +16,10 @@
|
||||
<body>
|
||||
<h1>Journey diagram demo</h1>
|
||||
<pre class="mermaid">
|
||||
journey
|
||||
title My working day
|
||||
---
|
||||
title: My working day
|
||||
---
|
||||
journey
|
||||
accTitle: Very simple journey demo
|
||||
accDescr: 2 main sections: work and home, each with just a few tasks
|
||||
|
||||
|
108
demos/mindmap.html
Normal file
108
demos/mindmap.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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=" />
|
||||
<style>
|
||||
div.mermaid {
|
||||
/* font-family: 'trebuchet ms', verdana, arial; */
|
||||
font-family: 'Courier New', Courier, monospace !important;
|
||||
}
|
||||
</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))
|
||||
</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>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from './mermaid.esm.mjs';
|
||||
import mermaidMindmap from './mermaid-mindmap.esm.mjs';
|
||||
|
||||
const ALLOWED_TAGS = [
|
||||
'a',
|
||||
'b',
|
||||
'blockquote',
|
||||
'br',
|
||||
'dd',
|
||||
'div',
|
||||
'dl',
|
||||
'dt',
|
||||
'em',
|
||||
'foreignObject',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'h7',
|
||||
'h8',
|
||||
'hr',
|
||||
'i',
|
||||
'li',
|
||||
'ul',
|
||||
'ol',
|
||||
'p',
|
||||
'pre',
|
||||
'span',
|
||||
'strike',
|
||||
'strong',
|
||||
'table',
|
||||
'tbody',
|
||||
'td',
|
||||
'tfoot',
|
||||
'th',
|
||||
'thead',
|
||||
'tr',
|
||||
];
|
||||
mermaid.parseError = function (err, hash) {
|
||||
// console.error('Mermaid error: ', err);
|
||||
};
|
||||
await mermaid.registerExternalDiagrams([mermaidMindmap]);
|
||||
mermaid.initialize({
|
||||
theme: 'base',
|
||||
startOnLoad: true,
|
||||
logLevel: 0,
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
htmlLabels: true,
|
||||
},
|
||||
gantt: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
useMaxWidth: false,
|
||||
});
|
||||
function callback() {
|
||||
alert('It worked');
|
||||
}
|
||||
mermaid.parseError = function (err, hash) {
|
||||
console.error('In parse error:');
|
||||
console.error(err);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
@@ -17,6 +17,9 @@
|
||||
<h1>State diagram demos</h1>
|
||||
<h2>Very simple showing change from State1 to State2</h2>
|
||||
<pre class="mermaid">
|
||||
---
|
||||
title: Very simple diagram
|
||||
---
|
||||
stateDiagram
|
||||
accTitle: This is the accessible title
|
||||
accDescr:This is an accessible description
|
||||
@@ -30,8 +33,9 @@
|
||||
<p>
|
||||
<code>
|
||||
classDef notMoving fill:white<br />
|
||||
classDef movement font-style:italic;<br />
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold<br />
|
||||
classDef movement font-style:italic<br />
|
||||
classDef badBadEvent
|
||||
fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow<br />
|
||||
</code>
|
||||
</p>
|
||||
<h4>And these are how they are applied:</h4>
|
||||
@@ -43,15 +47,20 @@
|
||||
</code>
|
||||
</p>
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
---
|
||||
title: Very simple diagram
|
||||
---
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic;
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold
|
||||
classDef movement font-style:italic
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*] --> Still
|
||||
[*]--> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
@@ -61,10 +70,57 @@
|
||||
class Still notMoving
|
||||
class Moving, Crash movement
|
||||
class Crash badBadEvent
|
||||
class end badBadEvent
|
||||
</pre>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Here is a diagram that uses the ::: operator to apply styles to states</h2>
|
||||
<h4>Here are the <code>classDef</code> statements:</h4>
|
||||
<p>
|
||||
<code>
|
||||
classDef notMoving fill:white<br />
|
||||
classDef movement font-style:italic<br />
|
||||
classDef badBadEvent
|
||||
fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow<br />
|
||||
</code>
|
||||
</p>
|
||||
<h4>And these are how they are applied:</h4>
|
||||
<p>
|
||||
<code>
|
||||
[*] --> Still:::notMoving<br />
|
||||
...<br />
|
||||
Still --> Moving:::movement<br />
|
||||
...<br />
|
||||
Moving --> Crash:::movement<br />
|
||||
Crash:::badBadEvent --> [*]<br />
|
||||
</code>
|
||||
</p>
|
||||
<p>
|
||||
Note that both the starting state and the end state have styles applied:<br />
|
||||
The start state has the <i>start</i> classDef style<br />and the end state has the
|
||||
<i>stop</i> classDef style applied.
|
||||
</p>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*] --> Still:::notMoving
|
||||
Still --> [*]
|
||||
Still --> Moving:::movement
|
||||
Moving --> Still
|
||||
Moving --> Crash:::movement
|
||||
Crash:::badBadEvent --> [*]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
accTitle: very very simple state
|
||||
@@ -73,6 +129,20 @@
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>States with spaces in them</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram
|
||||
classDef yourState font-style:italic,font-weight:bold,fill:white
|
||||
|
||||
yswsii: Your state with spaces in it
|
||||
[*] --> yswsii:::yourState
|
||||
[*] --> SomeOtherState
|
||||
SomeOtherState --> YetAnotherState
|
||||
yswsii --> YetAnotherState
|
||||
YetAnotherState --> [*]
|
||||
</pre>
|
||||
<hr />
|
||||
|
||||
<h2>You can label the relationships</h2>
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
@@ -121,7 +191,7 @@
|
||||
<pre class="mermaid">
|
||||
stateDiagram-v2
|
||||
[*] --> S1
|
||||
S1 --> S2: This long line uses a br tag<br/>to create multiple<br/>lines.
|
||||
S1 --> S2: This long line uses a br tag<br />to create multiple<br />lines.
|
||||
S1 --> S3: This transition descripton uses \na newline character\nto create multiple\nlines.
|
||||
|
||||
</pre>
|
||||
@@ -133,7 +203,7 @@
|
||||
direction LR
|
||||
State1: A state with a note
|
||||
note right of State1
|
||||
Important information!<br />You can write notes.<br/>And\nthey\ncan\nbe\nmulti-\nline.
|
||||
Important information!<br />You can write notes.<br />And\nthey\ncan\nbe\nmulti-\nline.
|
||||
end note
|
||||
State1 --> State2
|
||||
note left of State2 : Notes can be to the left of a state\n(like this one).
|
||||
|
@@ -36,7 +36,7 @@ Pushes in a directive to the configuration
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:193](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L193)
|
||||
[config.ts:191](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L191)
|
||||
|
||||
---
|
||||
|
||||
@@ -60,7 +60,7 @@ The currentConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:138](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L138)
|
||||
[config.ts:137](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L137)
|
||||
|
||||
---
|
||||
|
||||
@@ -84,7 +84,7 @@ The siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:97](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L97)
|
||||
[config.ts:96](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L96)
|
||||
|
||||
---
|
||||
|
||||
@@ -118,7 +118,7 @@ The siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:225](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L225)
|
||||
[config.ts:223](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L223)
|
||||
|
||||
---
|
||||
|
||||
@@ -147,7 +147,7 @@ options in-place
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:153](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L153)
|
||||
[config.ts:152](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L152)
|
||||
|
||||
---
|
||||
|
||||
@@ -167,7 +167,7 @@ options in-place
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:76](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L76)
|
||||
[config.ts:75](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L75)
|
||||
|
||||
---
|
||||
|
||||
@@ -199,7 +199,7 @@ The currentConfig merged with the sanitized conf
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:114](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L114)
|
||||
[config.ts:113](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L113)
|
||||
|
||||
---
|
||||
|
||||
@@ -232,7 +232,7 @@ The new siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:62](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L62)
|
||||
[config.ts:61](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L61)
|
||||
|
||||
---
|
||||
|
||||
@@ -273,4 +273,4 @@ The new siteConfig
|
||||
|
||||
#### Defined in
|
||||
|
||||
[config.ts:80](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L80)
|
||||
[config.ts:79](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.ts#L79)
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
#### Defined in
|
||||
|
||||
[defaultConfig.ts:1881](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1881)
|
||||
[defaultConfig.ts:1933](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L1933)
|
||||
|
||||
---
|
||||
|
||||
|
@@ -80,7 +80,7 @@ mermaid.initialize(config);
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:938](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L938)
|
||||
[mermaidAPI.ts:939](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L939)
|
||||
|
||||
## Functions
|
||||
|
||||
@@ -111,7 +111,7 @@ Return the last node appended
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:289](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L289)
|
||||
[mermaidAPI.ts:284](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L284)
|
||||
|
||||
---
|
||||
|
||||
@@ -137,7 +137,7 @@ the cleaned up svgCode
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:240](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L240)
|
||||
[mermaidAPI.ts:235](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L235)
|
||||
|
||||
---
|
||||
|
||||
@@ -163,7 +163,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:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L164)
|
||||
|
||||
---
|
||||
|
||||
@@ -186,7 +186,7 @@ the string with all the user styles
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:217](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L217)
|
||||
[mermaidAPI.ts:212](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L212)
|
||||
|
||||
---
|
||||
|
||||
@@ -213,7 +213,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:148](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L148)
|
||||
|
||||
---
|
||||
|
||||
@@ -279,7 +279,7 @@ Put the svgCode into an iFrame. Return the iFrame code
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:268](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L268)
|
||||
[mermaidAPI.ts:263](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L263)
|
||||
|
||||
---
|
||||
|
||||
@@ -305,4 +305,4 @@ Remove any existing elements from the given document
|
||||
|
||||
#### Defined in
|
||||
|
||||
[mermaidAPI.ts:340](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L340)
|
||||
[mermaidAPI.ts:335](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L335)
|
||||
|
@@ -348,7 +348,7 @@ on what kind of integration you use.
|
||||
|
||||
## Using the mermaid object
|
||||
|
||||
Is it possible to set some configuration via the mermaid object. The two parameters that are supported using this
|
||||
It is possible to set some configuration via the mermaid object. The two parameters that are supported using this
|
||||
approach are:
|
||||
|
||||
- mermaid.startOnLoad
|
||||
|
@@ -1,179 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>
|
||||
mermaid - Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams,
|
||||
gantt charts and git graphs.
|
||||
</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs."
|
||||
/>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
/>
|
||||
<!-- <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css"> -->
|
||||
<link rel="stylesheet" href="theme.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"
|
||||
/>
|
||||
<script
|
||||
defer
|
||||
data-domain="mermaid-js.github.io"
|
||||
src="https://plausible.io/js/plausible.js"
|
||||
></script>
|
||||
<script>
|
||||
const require = {
|
||||
paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs' },
|
||||
};
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/loader.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/editor/editor.main.nls.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.29.1/min/vs/editor/editor.main.js"></script>
|
||||
<script>
|
||||
exports = {};
|
||||
</script>
|
||||
<script src="https://unpkg.com/monaco-mermaid/browser.js"></script>
|
||||
|
||||
<style>
|
||||
.markdown-section {
|
||||
max-width: 1200px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module">
|
||||
import mermaid from 'https://unpkg.com/mermaid@9/dist/mermaid.esm.min.mjs';
|
||||
import mindmap from 'https://unpkg.com/@mermaid-js/mermaid-mindmap@9/dist/mermaid-mindmap.esm.min.mjs';
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
|
||||
window.mermaid = mermaid;
|
||||
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const conf = {
|
||||
logLevel: 4,
|
||||
startOnLoad: true,
|
||||
themeCSS: '.label { font-family: Source Sans Pro,Helvetica Neue,Arial,sans-serif; }',
|
||||
};
|
||||
if (isDarkMode) conf.theme = 'dark';
|
||||
|
||||
async function loadMermaid() {
|
||||
mermaid.parseError = (e) => {
|
||||
console.log('parse error', e); // eslint-disable-line
|
||||
};
|
||||
await mermaid.registerExternalDiagrams([mindmap]);
|
||||
mermaid.initialize(conf);
|
||||
console.log('mermaid initialized'); // eslint-disable-line
|
||||
}
|
||||
|
||||
await loadMermaid();
|
||||
</script>
|
||||
<script>
|
||||
let initEditor = exports.default;
|
||||
let parser = new DOMParser();
|
||||
let currentCodeExample = 0;
|
||||
let colorize = [];
|
||||
let num = 0;
|
||||
|
||||
function colorizeEverything(html) {
|
||||
initEditor(monaco);
|
||||
return new Promise((resolve, reject) => {
|
||||
monaco.editor.setTheme('mermaid');
|
||||
const parsed = parser.parseFromString(html, 'text/html').body;
|
||||
Promise.all(
|
||||
[...parsed.querySelectorAll('pre[id*="code"]')].map((codeBlock) =>
|
||||
monaco.editor.colorize(codeBlock.innerText, 'mermaid')
|
||||
)
|
||||
).then((result) => {
|
||||
parsed
|
||||
.querySelectorAll('pre[id*="code"]')
|
||||
.forEach((codeBlock, index) => (codeBlock.innerHTML = result[index]));
|
||||
resolve(parsed.innerHTML);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHTML(html) {
|
||||
return html
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"')
|
||||
.replaceAll("'", ''');
|
||||
}
|
||||
|
||||
window.$docsify = {
|
||||
search: 'auto',
|
||||
name: 'mermaid',
|
||||
repo: 'https://github.com/mermaid-js/mermaid',
|
||||
loadSidebar: true,
|
||||
mergeNavbar: true,
|
||||
maxLevel: 4,
|
||||
subMaxLevel: 2,
|
||||
markdown: {
|
||||
renderer: {
|
||||
code: function (code, lang) {
|
||||
if (lang === 'mermaid-example') {
|
||||
console.log('An example'); // eslint-disable-line
|
||||
currentCodeExample++;
|
||||
colorize.push(currentCodeExample);
|
||||
return '<pre id="code' + currentCodeExample + '">' + escapeHTML(code) + '</pre>';
|
||||
} else if (lang === 'mermaid') {
|
||||
return '<pre class="mermaid">' + code + '</pre>';
|
||||
}
|
||||
return this.origin.code.apply(this, arguments);
|
||||
},
|
||||
heading: function (text) {
|
||||
if (text.includes('THIS IS AN AUTOGENERATED FILE. DO NOT EDIT')) {
|
||||
return '';
|
||||
}
|
||||
return this.origin.heading.apply(this, arguments);
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
function (hook, vm) {
|
||||
hook.beforeEach(function (html) {
|
||||
url = 'https://github.com/mermaid-js/mermaid/blob/develop/src/docs/' + vm.route.file;
|
||||
const editHtml = '[:memo: Edit this Page](' + url + ')\n';
|
||||
return editHtml + html;
|
||||
});
|
||||
// Invoked on each page load after new HTML has been appended to the DOM
|
||||
hook.doneEach(async function () {
|
||||
await mermaid.init();
|
||||
});
|
||||
|
||||
hook.afterEach(function (html, next) {
|
||||
next(html);
|
||||
(async () => {
|
||||
while (!window.hasOwnProperty('monaco'))
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
colorizeEverything(html).then(
|
||||
(newHTML) =>
|
||||
(document.querySelector('article.markdown-section').innerHTML = newHTML)
|
||||
);
|
||||
})();
|
||||
});
|
||||
},
|
||||
],
|
||||
};
|
||||
</script>
|
||||
<script>
|
||||
window.onhashchange = function (a) {
|
||||
// if (location && ga) {
|
||||
// ga('send', 'pageview', location.hash);
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<!-- <script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/ga.min.js"></script> -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-coffeescript.min.js"></script>
|
||||
</body>
|
||||
</html>
|
@@ -14,7 +14,7 @@ It is a JavaScript based diagramming and charting tool that renders Markdown-ins
|
||||
|
||||
<img src="/header.png" alt="" />
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
<!-- Mermaid book banner -->
|
||||
|
||||
|
@@ -39,6 +39,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [markdown-for-mermaid-plugin](https://github.com/jamieh-mongolian/markdown-for-mermaid-plugin)
|
||||
- [JetBrains IDE eg Pycharm](https://www.jetbrains.com/go/guide/tips/mermaid-js-support-in-markdown/)
|
||||
- [mermerd](https://github.com/KarnerTh/mermerd)
|
||||
- Visual Studio Code [Polyglot Interactive Notebooks](https://github.com/dotnet/interactive#net-interactive)
|
||||
|
||||
## CRM/ERP/Similar
|
||||
|
||||
@@ -121,6 +122,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Draw.io](https://draw.io) - [Plugin](https://github.com/nopeslide/drawio_mermaid_plugin)
|
||||
- [Inkdrop](https://www.inkdrop.app) - [Plugin](https://github.com/inkdropapp/inkdrop-mermaid)
|
||||
- [Vim](https://www.vim.org)
|
||||
- [Official Vim Syntax and ftplugin](https://github.com/craigmac/vim-mermaid)
|
||||
- [Vim Diagram Syntax](https://github.com/zhaozg/vim-diagram)
|
||||
- [GNU Emacs](https://www.gnu.org/software/emacs/)
|
||||
- [Major mode for .mmd files](https://github.com/abrochard/mermaid-mode)
|
||||
|
@@ -567,7 +567,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
|
||||
Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
|
||||
}
|
||||
|
||||
Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){
|
||||
Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){
|
||||
Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,<br/> Apple Safari or Microsoft Edge"){
|
||||
Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.")
|
||||
}
|
||||
@@ -619,7 +619,7 @@ UpdateRelStyle(customerA, bankA, $offsetY="60")
|
||||
Container(mobile, "Mobile App", "Xamarin", "Provides a limited subset of the Internet Banking functionality to customers via their mobile device.")
|
||||
}
|
||||
|
||||
Deployment_Node(comp, "Customer's computer", "Mircosoft Windows or Apple macOS"){
|
||||
Deployment_Node(comp, "Customer's computer", "Microsoft Windows or Apple macOS"){
|
||||
Deployment_Node(browser, "Web Browser", "Google Chrome, Mozilla Firefox,<br/> Apple Safari or Microsoft Edge"){
|
||||
Container(spa, "Single Page Application", "JavaScript and Angular", "Provides all of the Internet Banking functionality to customers via their web browser.")
|
||||
}
|
||||
|
@@ -14,6 +14,9 @@ The class diagram is the main building block of object-oriented modeling. It is
|
||||
Mermaid can render class diagrams.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Animal example
|
||||
---
|
||||
classDiagram
|
||||
note "From Duck till Zebra"
|
||||
Animal <|-- Duck
|
||||
@@ -40,6 +43,9 @@ classDiagram
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Animal example
|
||||
---
|
||||
classDiagram
|
||||
note "From Duck till Zebra"
|
||||
Animal <|-- Duck
|
||||
@@ -77,6 +83,9 @@ A single instance of a class in the diagram contains three compartments:
|
||||
- The bottom compartment contains the operations the class can execute. They are also left-aligned and the first letter is lowercase.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Bank example
|
||||
---
|
||||
classDiagram
|
||||
class BankAccount
|
||||
BankAccount : +String owner
|
||||
@@ -87,6 +96,9 @@ classDiagram
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Bank example
|
||||
---
|
||||
classDiagram
|
||||
class BankAccount
|
||||
BankAccount : +String owner
|
||||
@@ -559,7 +571,7 @@ You would define these actions on a separate line after all classes have been de
|
||||
|
||||
## Notes
|
||||
|
||||
It is possible to add notes on digram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"`
|
||||
It is possible to add notes on diagram using `note "line1\nline2"` or note for class using `note for class "line1\nline2"`
|
||||
|
||||
### Examples
|
||||
|
||||
|
@@ -13,6 +13,9 @@ Note that practitioners of ER modelling almost always refer to _entity types_ si
|
||||
Mermaid can render ER diagrams
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Order example
|
||||
---
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
@@ -20,6 +23,9 @@ erDiagram
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Order example
|
||||
---
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE-ITEM : contains
|
||||
|
@@ -15,11 +15,17 @@ It can also accommodate different arrow types, multi directional arrows, and lin
|
||||
### A node (default)
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Node
|
||||
---
|
||||
flowchart LR
|
||||
id
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Node
|
||||
---
|
||||
flowchart LR
|
||||
id
|
||||
```
|
||||
@@ -33,11 +39,17 @@ found for the node that will be used. Also if you define edges for the node late
|
||||
one previously defined will be used when rendering the box.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Node with text
|
||||
---
|
||||
flowchart LR
|
||||
id1[This is the text in the box]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Node with text
|
||||
---
|
||||
flowchart LR
|
||||
id1[This is the text in the box]
|
||||
```
|
||||
@@ -980,7 +992,7 @@ flowchart LR
|
||||
|
||||
## Configuration...
|
||||
|
||||
Is it possible to adjust the width of the rendered flowchart.
|
||||
It is possible to adjust the width of the rendered flowchart.
|
||||
|
||||
This is done by defining **mermaid.flowchartConfig** or by the CLI to use a JSON file with the configuration. How to use the CLI is described in the mermaidCLI page.
|
||||
mermaid.flowchartConfig can be set to a JSON string with config parameters or the corresponding object.
|
||||
|
@@ -13,31 +13,37 @@ These kind of diagram are particularly helpful to developers and devops teams to
|
||||
Mermaid can render Git diagrams
|
||||
|
||||
```mermaid-example
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
---
|
||||
title: Example Git diagram
|
||||
---
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
---
|
||||
title: Example Git diagram
|
||||
---
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
checkout develop
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
merge develop
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
In Mermaid, we support the basic git operations like:
|
||||
|
@@ -21,7 +21,7 @@ mindmap
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectivness<br/>and eatures
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
@@ -42,7 +42,7 @@ mindmap
|
||||
Popularisation
|
||||
British popular psychology author Tony Buzan
|
||||
Research
|
||||
On effectivness<br/>and eatures
|
||||
On effectiveness<br/>and features
|
||||
On Automatic creation
|
||||
Uses
|
||||
Creative techniques
|
||||
@@ -152,6 +152,18 @@ mindmap
|
||||
id)I am a cloud(
|
||||
```
|
||||
|
||||
### Hexagon
|
||||
|
||||
```mermaid-example
|
||||
mindmap
|
||||
id{{I am a hexagon}}
|
||||
```
|
||||
|
||||
```mermaid
|
||||
mindmap
|
||||
id{{I am a hexagon}}
|
||||
```
|
||||
|
||||
### Default
|
||||
|
||||
```mermaid-example
|
||||
|
@@ -35,7 +35,7 @@ Drawing a pie chart is really simple in mermaid.
|
||||
- Followed by dataSet. Pie slices will be ordered clockwise in the same order as the labels.
|
||||
- `label` for a section in the pie diagram within `" "` quotes.
|
||||
- Followed by `:` colon as separator
|
||||
- Followed by `positive numeric value` (supported upto two decimal places)
|
||||
- Followed by `positive numeric value` (supported up to two decimal places)
|
||||
|
||||
\[pie] \[showData] (OPTIONAL)
|
||||
\[title] \[titlevalue] (OPTIONAL)
|
||||
|
@@ -727,7 +727,7 @@ text.actor {
|
||||
|
||||
## Configuration
|
||||
|
||||
Is it possible to adjust the margins for rendering the sequence diagram.
|
||||
It is possible to adjust the margins for rendering the sequence diagram.
|
||||
|
||||
This is done by defining `mermaid.sequenceConfig` or by the CLI to use a json file with the configuration.
|
||||
How to use the CLI is described in the [mermaidCLI](../config/mermaidCLI.md) page.
|
||||
|
@@ -6,11 +6,17 @@
|
||||
|
||||
# State diagrams
|
||||
|
||||
> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems. State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the case, while at other times this is a reasonable abstraction." Wikipedia
|
||||
> "A state diagram is a type of diagram used in computer science and related fields to describe the behavior of systems.
|
||||
> State diagrams require that the system described is composed of a finite number of states; sometimes, this is indeed the
|
||||
> case, while at other times this is a reasonable abstraction." Wikipedia
|
||||
|
||||
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make it easier for users to share diagrams between mermaid and plantUml.
|
||||
Mermaid can render state diagrams. The syntax tries to be compliant with the syntax used in plantUml as this will make
|
||||
it easier for users to share diagrams between mermaid and plantUml.
|
||||
|
||||
```mermaid-example
|
||||
---
|
||||
title: Simple sample
|
||||
---
|
||||
stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
@@ -22,6 +28,9 @@ stateDiagram-v2
|
||||
```
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Simple sample
|
||||
---
|
||||
stateDiagram-v2
|
||||
[*] --> Still
|
||||
Still --> [*]
|
||||
@@ -56,20 +65,23 @@ stateDiagram
|
||||
Crash --> [*]
|
||||
```
|
||||
|
||||
In state diagrams systems are described in terms of its states and how the systems state can change to another state via a transitions. The example diagram above shows three states **Still**, **Moving** and **Crash**. You start in the state of Still. From Still you can change the state to Moving. In Moving you can change the state either back to Still or to Crash. There is no transition from Still to Crash.
|
||||
In state diagrams systems are described in terms of _states_ and how one _state_ can change to another _state_ via
|
||||
a _transition._ The example diagram above shows three states: **Still**, **Moving** and **Crash**. You start in the
|
||||
**Still** state. From **Still** you can change to the **Moving** state. From **Moving** you can change either back to the **Still** state or to
|
||||
the **Crash** state. There is no transition from **Still** to **Crash**. (You can't crash if you're still.)
|
||||
|
||||
## States
|
||||
|
||||
A state can be declared in multiple ways. The simplest way is to define a state id as a description.
|
||||
A state can be declared in multiple ways. The simplest way is to define a state with just an id:
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
s1
|
||||
stateId
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
s1
|
||||
stateId
|
||||
```
|
||||
|
||||
Another way is by using the state keyword with a description as per below:
|
||||
@@ -100,7 +112,8 @@ stateDiagram-v2
|
||||
|
||||
Transitions are path/edges when one state passes into another. This is represented using text arrow, "-->".
|
||||
|
||||
When you define a transition between two states and the states are not already defined the undefined states are defined with the id from the transition. You can later add descriptions to states defined this way.
|
||||
When you define a transition between two states and the states are not already defined, the undefined states are defined
|
||||
with the id from the transition. You can later add descriptions to states defined this way.
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
@@ -112,7 +125,7 @@ stateDiagram-v2
|
||||
s1 --> s2
|
||||
```
|
||||
|
||||
It is possible to add text to a transition. To describe what it represents.
|
||||
It is possible to add text to a transition to describe what it represents:
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
@@ -126,7 +139,8 @@ stateDiagram-v2
|
||||
|
||||
## Start and End
|
||||
|
||||
There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and the direction of the transition to it defines it either as a start or a stop state.
|
||||
There are two special states indicating the start and stop of the diagram. These are written with the \[\*] syntax and
|
||||
the direction of the transition to it defines it either as a start or a stop state.
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
@@ -142,10 +156,11 @@ stateDiagram-v2
|
||||
|
||||
## Composite states
|
||||
|
||||
In a real world use of state diagrams you often end up with diagrams that are multi-dimensional as one state can
|
||||
In a real world use of state diagrams you often end up with diagrams that are multidimensional as one state can
|
||||
have several internal states. These are called composite states in this terminology.
|
||||
|
||||
In order to define a composite state you need to use the state keyword followed by an id and the body of the composite state between {}. See the example below:
|
||||
In order to define a composite state you need to use the state keyword followed by an id and the body of the composite
|
||||
state between {}. See the example below:
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
@@ -305,7 +320,7 @@ It is possible to specify a fork in the diagram using <\<fork>> <\<join>>.
|
||||
|
||||
## Notes
|
||||
|
||||
Sometimes nothing says it better then a Post-it note. That is also the case in state diagrams.
|
||||
Sometimes nothing says it better than a Post-it note. That is also the case in state diagrams.
|
||||
|
||||
Here you can choose to put the note to the _right of_ or to the _left of_ a node.
|
||||
|
||||
@@ -375,7 +390,8 @@ stateDiagram-v2
|
||||
|
||||
## Setting the direction of the diagram
|
||||
|
||||
With state diagrams you can use the direction statement to set the direction which the diagram will render like in this example.
|
||||
With state diagrams you can use the direction statement to set the direction which the diagram will render like in this
|
||||
example.
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram
|
||||
@@ -405,7 +421,9 @@ stateDiagram
|
||||
|
||||
## Comments
|
||||
|
||||
Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next newline will be treated as a comment, including any diagram syntax
|
||||
Comments can be entered within a state diagram chart, which will be ignored by the parser. Comments need to be on their
|
||||
own line, and must be prefaced with `%%` (double percent signs). Any text after the start of the comment to the next
|
||||
newline will be treated as a comment, including any diagram syntax
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
@@ -429,22 +447,204 @@ stateDiagram-v2
|
||||
Crash --> [*]
|
||||
```
|
||||
|
||||
## Styling
|
||||
## Styling with classDefs
|
||||
|
||||
Styling of the a state diagram is done by defining a number of css classes. During rendering these classes are extracted from the file located at src/themes/state.scss
|
||||
As with other diagrams (like flowcharts), you can define a style in the diagram itself and apply that named style to a
|
||||
state or states in the diagram.
|
||||
|
||||
**These are the current limitations with state diagram classDefs:**
|
||||
|
||||
1. Cannot be applied to start or end states
|
||||
2. Cannot be applied to or within composite states
|
||||
|
||||
_These are in development and will be available in a future version._
|
||||
|
||||
You define a style using the `classDef` keyword, which is short for "class definition" (where "class" means something
|
||||
like a _CSS class_)
|
||||
followed by _a name for the style,_
|
||||
and then one or more _property-value pairs_. Each _property-value pair_ is
|
||||
a _[valid CSS property name](https://www.w3.org/TR/CSS/#properties)_ followed by a colon (`:`) and then a _value._
|
||||
|
||||
Here is an example of a classDef with just one property-value pair:
|
||||
|
||||
classDef movement font-style:italic;
|
||||
|
||||
where
|
||||
|
||||
- the _name_ of the style is `movement`
|
||||
- the only _property_ is `font-style` and its _value_ is `italic`
|
||||
|
||||
If you want to have more than one _property-value pair_ then you put a comma (`,`) between each _property-value pair._
|
||||
|
||||
Here is an example with three property-value pairs:
|
||||
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
where
|
||||
|
||||
- the _name_ of the style is `badBadEvent`
|
||||
- the first _property_ is `fill` and its _value_ is `#f00`
|
||||
- the second _property_ is `color` and its _value_ is `white`
|
||||
- the third _property_ is `font-weight` and its _value_ is `bold`
|
||||
- the fourth _property_ is `stroke-width` and its _value_ is `2px`
|
||||
- the fifth _property_ is `stroke` and its _value_ is `yello`
|
||||
|
||||
### Apply classDef styles to states
|
||||
|
||||
There are two ways to apply a `classDef` style to a state:
|
||||
|
||||
1. use the `class` keyword to apply a classDef style to one or more states in a single statement, or
|
||||
2. use the `:::` operator to apply a classDef style to a state as it is being used in a transition statement (e.g. with an arrow
|
||||
to/from another state)
|
||||
|
||||
#### 1. `class` statement
|
||||
|
||||
A `class` statement tells Mermaid to apply the named classDef to one or more classes. The form is:
|
||||
|
||||
```text
|
||||
class [one or more state names, separated by commas] [name of a style defined with classDef]
|
||||
```
|
||||
|
||||
Here is an example applying the `badBadEvent` style to a state named `Crash`:
|
||||
|
||||
```text
|
||||
class Crash badBadEvent
|
||||
```
|
||||
|
||||
Here is an example applying the `movement` style to the two states `Moving` and `Crash`:
|
||||
|
||||
```text
|
||||
class Moving, Crash movement
|
||||
```
|
||||
|
||||
Here is a diagram that shows the examples in use. Note that the `Crash` state has two classDef styles applied: `movement`
|
||||
and `badBadEvent`
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*]--> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
|
||||
class Still notMoving
|
||||
class Moving, Crash movement
|
||||
class Crash badBadEvent
|
||||
class end badBadEvent
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*]--> Still
|
||||
Still --> [*]
|
||||
Still --> Moving
|
||||
Moving --> Still
|
||||
Moving --> Crash
|
||||
Crash --> [*]
|
||||
|
||||
class Still notMoving
|
||||
class Moving, Crash movement
|
||||
class Crash badBadEvent
|
||||
class end badBadEvent
|
||||
```
|
||||
|
||||
#### 2. `:::` operator to apply a style to a state
|
||||
|
||||
You can apply a classDef style to a state using the `:::` (three colons) operator. The syntax is
|
||||
|
||||
```text
|
||||
[state]:::[style name]
|
||||
```
|
||||
|
||||
You can use this in a diagram within a statement using a class. This includes the start and end states. For example:
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic;
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*] --> Still:::notMoving
|
||||
Still --> [*]
|
||||
Still --> Moving:::movement
|
||||
Moving --> Still
|
||||
Moving --> Crash:::movement
|
||||
Crash:::badBadEvent --> [*]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram
|
||||
direction TB
|
||||
|
||||
accTitle: This is the accessible title
|
||||
accDescr: This is an accessible description
|
||||
|
||||
classDef notMoving fill:white
|
||||
classDef movement font-style:italic;
|
||||
classDef badBadEvent fill:#f00,color:white,font-weight:bold,stroke-width:2px,stroke:yellow
|
||||
|
||||
[*] --> Still:::notMoving
|
||||
Still --> [*]
|
||||
Still --> Moving:::movement
|
||||
Moving --> Still
|
||||
Moving --> Crash:::movement
|
||||
Crash:::badBadEvent --> [*]
|
||||
```
|
||||
|
||||
## Spaces in state names
|
||||
|
||||
Spaces can be added to a state by defining it at the top and referencing the acronym later.
|
||||
Spaces can be added to a state by first defining the state with an id and then referencing the id later.
|
||||
|
||||
In the following example there is a state with the id **yswsii** and description **Your state with spaces in it**.
|
||||
After it has been defined, **yswsii** is used in the diagram in the first transition (`[*] --> yswsii`)
|
||||
and also in the transition to **YetAnotherState** (`yswsii --> YetAnotherState`).\
|
||||
(**yswsii** has been styled so that it is different from the other states.)
|
||||
|
||||
```mermaid-example
|
||||
stateDiagram-v2
|
||||
Yswsii: Your state with spaces in it
|
||||
[*] --> Yswsii
|
||||
stateDiagram
|
||||
classDef yourState font-style:italic,font-weight:bold,fill:white
|
||||
|
||||
yswsii: Your state with spaces in it
|
||||
[*] --> yswsii:::yourState
|
||||
[*] --> SomeOtherState
|
||||
SomeOtherState --> YetAnotherState
|
||||
yswsii --> YetAnotherState
|
||||
YetAnotherState --> [*]
|
||||
```
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
Yswsii: Your state with spaces in it
|
||||
[*] --> Yswsii
|
||||
stateDiagram
|
||||
classDef yourState font-style:italic,font-weight:bold,fill:white
|
||||
|
||||
yswsii: Your state with spaces in it
|
||||
[*] --> yswsii:::yourState
|
||||
[*] --> SomeOtherState
|
||||
SomeOtherState --> YetAnotherState
|
||||
yswsii --> YetAnotherState
|
||||
YetAnotherState --> [*]
|
||||
```
|
||||
|
19
package.json
19
package.json
@@ -4,7 +4,7 @@
|
||||
"version": "9.2.2",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@7.15.0",
|
||||
"packageManager": "pnpm@7.17.1",
|
||||
"keywords": [
|
||||
"diagram",
|
||||
"markdown",
|
||||
@@ -60,6 +60,7 @@
|
||||
"@cspell/eslint-plugin": "^6.14.2",
|
||||
"@types/eslint": "^8.4.10",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/lodash": "^4.14.188",
|
||||
"@types/mdast": "^3.0.10",
|
||||
@@ -82,15 +83,17 @@
|
||||
"eslint-plugin-jest": "^27.1.5",
|
||||
"eslint-plugin-jsdoc": "^39.6.2",
|
||||
"eslint-plugin-json": "^3.1.0",
|
||||
"eslint-plugin-lodash": "^7.4.0",
|
||||
"eslint-plugin-markdown": "^3.0.0",
|
||||
"eslint-plugin-no-only-tests": "^3.1.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"eslint-plugin-unicorn": "^45.0.0",
|
||||
"express": "^4.18.2",
|
||||
"globby": "^13.1.2",
|
||||
"husky": "^8.0.2",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.3.1",
|
||||
"jison": "^0.4.18",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsdom": "^20.0.2",
|
||||
"lint-staged": "^13.0.3",
|
||||
"path-browserify": "^1.0.1",
|
||||
@@ -103,18 +106,8 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.8.4",
|
||||
"vite": "^3.2.3",
|
||||
"vitepress": "^1.0.0-alpha.28",
|
||||
"vitepress-plugin-mermaid": "^2.0.8",
|
||||
"vitepress-plugin-search": "^1.0.4-alpha.15",
|
||||
"vitest": "^0.25.1"
|
||||
"vitest": "^0.25.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"d3": "^7.6.1"
|
||||
},
|
||||
"sideEffects": [
|
||||
"**/*.css",
|
||||
"**/*.scss"
|
||||
],
|
||||
"volta": {
|
||||
"node": "18.12.1"
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
const warning = (s: string) => {
|
||||
// Todo remove debug code
|
||||
console.error('Log function was called before initialization', s); // eslint-disable-line
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Log function was called before initialization', s);
|
||||
};
|
||||
|
||||
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
@@ -172,6 +172,18 @@ root
|
||||
expect(mm.children.length).toEqual(0);
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.BANG);
|
||||
});
|
||||
|
||||
it('MMP-12-a mutiple types (hexagon)', function () {
|
||||
let str = `mindmap
|
||||
root{{the root}}
|
||||
`;
|
||||
|
||||
mindmap.parse(str);
|
||||
const mm = mindmap.yy.getMindmap();
|
||||
expect(mm.type).toEqual(mindmap.yy.nodeType.HEXAGON);
|
||||
expect(mm.descr).toEqual('the root');
|
||||
expect(mm.children.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
describe('decorations', function () {
|
||||
it('MMP-13 should be possible to set an icon for the node', function () {
|
||||
|
@@ -42,6 +42,9 @@ export const addNode = (level, id, descr, type) => {
|
||||
case nodeType.RECT:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
case nodeType.HEXAGON:
|
||||
node.padding = 2 * conf.mindmap.padding;
|
||||
break;
|
||||
default:
|
||||
node.padding = conf.mindmap.padding;
|
||||
}
|
||||
@@ -79,6 +82,7 @@ export const nodeType = {
|
||||
CIRCLE: 3,
|
||||
CLOUD: 4,
|
||||
BANG: 5,
|
||||
HEXAGON: 6,
|
||||
};
|
||||
|
||||
export const getType = (startStr, endStr) => {
|
||||
@@ -94,6 +98,8 @@ export const getType = (startStr, endStr) => {
|
||||
return nodeType.CLOUD;
|
||||
case '))':
|
||||
return nodeType.BANG;
|
||||
case '{{':
|
||||
return nodeType.HEXAGON;
|
||||
default:
|
||||
return nodeType.DEFAULT;
|
||||
}
|
||||
@@ -127,6 +133,8 @@ export const type2Str = (type) => {
|
||||
return 'cloud';
|
||||
case nodeType.BANG:
|
||||
return 'bang';
|
||||
case nodeType.HEXAGON:
|
||||
return 'hexgon';
|
||||
default:
|
||||
return 'no-border';
|
||||
}
|
||||
|
@@ -92,10 +92,6 @@ function addNodes(mindmap, cy, conf, level) {
|
||||
*/
|
||||
function layoutMindmap(node, conf) {
|
||||
return new Promise((resolve) => {
|
||||
if (node.children.length === 0) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Add temporary render element
|
||||
const renderEl = select('body').append('div').attr('id', 'cy').attr('style', 'display:none');
|
||||
const cy = cytoscape({
|
||||
|
@@ -33,11 +33,12 @@
|
||||
"))" { yy.getLogger().trace('Explosion Bang'); this.begin('NODE');return 'NODE_DSTART'; }
|
||||
")" { yy.getLogger().trace('Cloud Bang'); this.begin('NODE');return 'NODE_DSTART'; }
|
||||
"((" { this.begin('NODE');return 'NODE_DSTART'; }
|
||||
"{{" { this.begin('NODE');return 'NODE_DSTART'; }
|
||||
"(" { this.begin('NODE');return 'NODE_DSTART'; }
|
||||
"[" { this.begin('NODE');return 'NODE_DSTART'; }
|
||||
[\s]+ return 'SPACELIST' /* skip all whitespace */ ;
|
||||
// !(-\() return 'NODE_ID';
|
||||
[^\(\[\n\-\)]+ return 'NODE_ID';
|
||||
[^\(\[\n\-\)\{\}]+ return 'NODE_ID';
|
||||
<<EOF>> return 'EOF';
|
||||
<NODE>["] { yy.getLogger().trace('Starting NSTR');this.begin("NSTR");}
|
||||
<NSTR>[^"]+ { yy.getLogger().trace('description:', yytext); return "NODE_DESCR";}
|
||||
@@ -45,11 +46,12 @@
|
||||
<NODE>[\)]\) {this.popState();yy.getLogger().trace('node end ))');return "NODE_DEND";}
|
||||
<NODE>[\)] {this.popState();yy.getLogger().trace('node end )');return "NODE_DEND";}
|
||||
<NODE>[\]] {this.popState();yy.getLogger().trace('node end ...',yytext);return "NODE_DEND";}
|
||||
<NODE>"}}" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
|
||||
<NODE>"(-" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
|
||||
<NODE>"-)" {this.popState();yy.getLogger().trace('node end (-');return "NODE_DEND";}
|
||||
<NODE>"((" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
|
||||
<NODE>"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
|
||||
<NODE>[^\)\]\(]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
|
||||
<NODE>"(" {this.popState();yy.getLogger().trace('node end ((');return "NODE_DEND";}
|
||||
<NODE>[^\)\]\(\}]+ { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
|
||||
<NODE>.+(?!\(\() { yy.getLogger().trace('Long description:', yytext); return 'NODE_DESCR';}
|
||||
// [\[] return 'NODE_START';
|
||||
// .+ return 'TXT' ;
|
||||
|
@@ -17,7 +17,7 @@ const genSections = (options) => {
|
||||
sections += `
|
||||
.section-${i - 1} rect, .section-${i - 1} path, .section-${i - 1} circle, .section-${
|
||||
i - 1
|
||||
} path {
|
||||
} polygon, .section-${i - 1} path {
|
||||
fill: ${options['cScale' + i]};
|
||||
}
|
||||
.section-${i - 1} text {
|
||||
@@ -55,7 +55,7 @@ const getStyles = (options) =>
|
||||
stroke-width: 3;
|
||||
}
|
||||
${genSections(options)}
|
||||
.section-root rect, .section-root path, .section-root circle {
|
||||
.section-root rect, .section-root path, .section-root circle, .section-root polygon {
|
||||
fill: ${options.git0};
|
||||
}
|
||||
.section-root text {
|
||||
|
@@ -145,6 +145,45 @@ const circleBkg = function (elem, node) {
|
||||
.attr('class', 'node-bkg node-' + db.type2Str(node.type))
|
||||
.attr('r', node.width / 2);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param parent
|
||||
* @param w
|
||||
* @param h
|
||||
* @param points
|
||||
* @param node
|
||||
*/
|
||||
function insertPolygonShape(parent, w, h, points, node) {
|
||||
return parent
|
||||
.insert('polygon', ':first-child')
|
||||
.attr(
|
||||
'points',
|
||||
points
|
||||
.map(function (d) {
|
||||
return d.x + ',' + d.y;
|
||||
})
|
||||
.join(' ')
|
||||
)
|
||||
.attr('transform', 'translate(' + (node.width - w) / 2 + ', ' + h + ')');
|
||||
}
|
||||
|
||||
const hexagonBkg = function (elem, node) {
|
||||
const h = node.height;
|
||||
const f = 4;
|
||||
const m = h / f;
|
||||
const w = node.width - node.padding + 2 * m;
|
||||
const points = [
|
||||
{ x: m, y: 0 },
|
||||
{ x: w - m, y: 0 },
|
||||
{ x: w, y: -h / 2 },
|
||||
{ x: w - m, y: -h },
|
||||
{ x: m, y: -h },
|
||||
{ x: 0, y: -h / 2 },
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(elem, w, h, points, node);
|
||||
};
|
||||
|
||||
const roundedRectBkg = function (elem, node) {
|
||||
elem
|
||||
.append('rect')
|
||||
@@ -252,6 +291,9 @@ export const drawNode = function (elem, node, fullSection, conf) {
|
||||
case db.nodeType.BANG:
|
||||
bangBkg(bkgElem, node, section, conf);
|
||||
break;
|
||||
case db.nodeType.HEXAGON:
|
||||
hexagonBkg(bkgElem, node, section, conf);
|
||||
break;
|
||||
}
|
||||
|
||||
// Position the node to its coordinate
|
||||
|
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"src/docs/**": ["pnpm --filter mermaid run docs:build --git"],
|
||||
"src/docs.mts": ["pnpm --filter mermaid run docs:build --git"],
|
||||
"src/(defaultConfig|config|mermaidAPI).ts": ["pnpm --filter mermaid run docs:build --git"],
|
||||
"*.jison": ["pnpm run lint:jison"]
|
||||
}
|
7
packages/mermaid/.lintstagedrc.mjs
Normal file
7
packages/mermaid/.lintstagedrc.mjs
Normal file
@@ -0,0 +1,7 @@
|
||||
import baseConfig from '../../.lintstagedrc.mjs';
|
||||
export default {
|
||||
...baseConfig,
|
||||
'src/docs/**': ['pnpm --filter mermaid run docs:build --git'],
|
||||
'src/docs.mts': ['pnpm --filter mermaid run docs:build --git'],
|
||||
'src/(defaultConfig|config|mermaidAPI).ts': ['pnpm --filter mermaid run docs:build --git'],
|
||||
};
|
@@ -1,23 +1,6 @@
|
||||
# mermaid
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
# Whoa, what's going on here?
|
||||
|
||||
We are transforming the Mermaid repository to a so called mono-repo. This is a part of the effort to decouple the diagrams from the core of mermaid. This will:
|
||||
|
||||
- Make it possible to select which diagrams to include on your site
|
||||
- Open up for lazy loading
|
||||
- Make it possible to add diagrams from outside of the Mermaid repository
|
||||
- Separate the release flow between different diagrams and the Mermaid core
|
||||
|
||||
As such be aware of some changes..
|
||||
|
||||
# We use pnpm now
|
||||
|
||||
# The source code has moved
|
||||
|
||||
It is now located in the src folder for each respective package located as subfolders in packages.
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
English | [简体中文](./README.zh-CN.md)
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# mermaid
|
||||
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
[](https://github.com/mermaid-js/mermaid/actions/workflows/build.yml) [](https://www.npmjs.com/package/mermaid) [](https://bundlephobia.com/package/mermaid) [](https://coveralls.io/github/mermaid-js/mermaid?branch=master) [](https://www.jsdelivr.com/package/npm/mermaid) [](https://www.npmjs.com/package/mermaid) [](https://join.slack.com/t/mermaid-talk/shared_invite/enQtNzc4NDIyNzk4OTAyLWVhYjQxOTI2OTg4YmE1ZmJkY2Y4MTU3ODliYmIwOTY3NDJlYjA0YjIyZTdkMDMyZTUwOGI0NjEzYmEwODcwOTE) [](https://twitter.com/mermaidjs_)
|
||||
|
||||
[English](./README.md) | 简体中文
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "9.2.3-rc.1",
|
||||
"description": "Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"main": "./dist/mermaid.min.js",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
"types": "./dist/mermaid.d.ts",
|
||||
@@ -25,12 +25,13 @@
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"docs:code": "typedoc src/defaultConfig.ts src/config.ts src/mermaidAPI.ts && prettier --write ./src/docs/config/setup",
|
||||
"docs:build": "rimraf ../../docs && pnpm docs:code && ts-node-esm src/docs.mts",
|
||||
"docs:verify": "pnpm docs:code && ts-node-esm src/docs.mts --verify",
|
||||
"docs:build": "rimraf ../../docs && pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts",
|
||||
"docs:verify": "pnpm docs:spellcheck && pnpm docs:code && ts-node-esm src/docs.mts --verify",
|
||||
"docs:pre:vitepress": "rimraf src/vitepress && pnpm docs:code && ts-node-esm src/docs.mts --vitepress",
|
||||
"docs:build:vitepress": "pnpm docs:pre:vitepress && vitepress build src/vitepress",
|
||||
"docs:dev": "pnpm docs:pre:vitepress && concurrently \"vitepress dev src/vitepress\" \"ts-node-esm src/docs.mts --watch --vitepress\"",
|
||||
"docs:serve": "pnpm docs:build:vitepress && vitepress serve src/vitepress",
|
||||
"docs:spellcheck": "cspell --config ../../cSpell.json \"src/docs/**/*.md\"",
|
||||
"release": "pnpm build",
|
||||
"prepublishOnly": "pnpm -w run build"
|
||||
},
|
||||
@@ -53,13 +54,10 @@
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.0",
|
||||
"d3": "^7.0.0",
|
||||
"dagre": "^0.8.5",
|
||||
"dagre-d3": "^0.6.4",
|
||||
"dagre-d3-es": "7.0.4",
|
||||
"dompurify": "2.4.1",
|
||||
"fast-clone": "^1.5.13",
|
||||
"graphlib": "^2.1.8",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash": "^4.14.189",
|
||||
"lodash-es": "^4.17.21",
|
||||
"moment-mini": "^2.24.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.2",
|
||||
@@ -69,9 +67,9 @@
|
||||
"@types/d3": "^7.4.0",
|
||||
"@types/dompurify": "^2.4.0",
|
||||
"@types/jsdom": "^20.0.1",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@types/micromatch": "^4.0.2",
|
||||
"@types/prettier": "^2.7.1",
|
||||
"@types/lodash": "^4.14.189",
|
||||
"@types/stylis": "^4.0.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.42.1",
|
||||
@@ -79,8 +77,8 @@
|
||||
"chokidar": "^3.5.3",
|
||||
"concurrently": "^7.5.0",
|
||||
"coveralls": "^3.1.1",
|
||||
"cspell": "^6.14.3",
|
||||
"globby": "^13.1.2",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jison": "^0.4.18",
|
||||
"js-base64": "^3.7.2",
|
||||
"jsdom": "^20.0.2",
|
||||
@@ -94,10 +92,9 @@
|
||||
"typedoc": "^0.23.18",
|
||||
"typedoc-plugin-markdown": "^3.13.6",
|
||||
"typescript": "^4.8.4",
|
||||
"unist-util-flatmap": "^1.0.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"d3": "^7.0.0"
|
||||
"unist-util-flatmap": "^1.0.0",
|
||||
"vitepress": "^1.0.0-alpha.28",
|
||||
"vitepress-plugin-search": "^1.0.4-alpha.15"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
@@ -2,6 +2,7 @@ import * as configApi from './config';
|
||||
import { log } from './logger';
|
||||
import { getDiagram, registerDiagram } from './diagram-api/diagramAPI';
|
||||
import { detectType, getDiagramLoader } from './diagram-api/detectType';
|
||||
import { extractFrontMatter } from './diagram-api/frontmatter';
|
||||
import { isDetailedError, type DetailedError } from './utils';
|
||||
|
||||
export type ParseErrorFunction = (err: string | DetailedError, hash?: any) => void;
|
||||
@@ -10,7 +11,7 @@ export class Diagram {
|
||||
type = 'graph';
|
||||
parser;
|
||||
renderer;
|
||||
db;
|
||||
db: any;
|
||||
private detectTypeFailed = false;
|
||||
constructor(public txt: string, parseError?: ParseErrorFunction) {
|
||||
const cnf = configApi.getConfig();
|
||||
@@ -29,6 +30,16 @@ export class Diagram {
|
||||
this.db.clear?.();
|
||||
this.renderer = diagram.renderer;
|
||||
this.parser = diagram.parser;
|
||||
const originalParse = this.parser.parse.bind(this.parser);
|
||||
// Wrap the jison parse() method to handle extracting frontmatter.
|
||||
//
|
||||
// This can't be done in this.parse() because some code
|
||||
// directly calls diagram.parser.parse(), bypassing this.parse().
|
||||
//
|
||||
// Similarly, we can't do this in getDiagramFromText() because some code
|
||||
// calls diagram.db.clear(), which would reset anything set by
|
||||
// extractFrontMatter().
|
||||
this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db));
|
||||
this.parser.parser.yy = this.db;
|
||||
if (diagram.init) {
|
||||
diagram.init(cnf);
|
||||
@@ -45,7 +56,7 @@ export class Diagram {
|
||||
}
|
||||
try {
|
||||
text = text + '\n';
|
||||
this.db.clear();
|
||||
this.db.clear?.();
|
||||
this.parser.parse(text);
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
@@ -11,7 +11,7 @@
|
||||
* @param id
|
||||
*/
|
||||
export default function addSVGAccessibilityFields(yy_parser, svg, id) {
|
||||
if (typeof svg.insert === 'undefined') {
|
||||
if (svg.insert === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -32,20 +32,20 @@ const assignWithDepth = function (dst, src, config) {
|
||||
return dst;
|
||||
} else if (Array.isArray(src) && Array.isArray(dst)) {
|
||||
src.forEach((s) => {
|
||||
if (dst.indexOf(s) === -1) {
|
||||
if (!dst.includes(s)) {
|
||||
dst.push(s);
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
if (typeof dst === 'undefined' || depth <= 0) {
|
||||
if (dst === undefined || depth <= 0) {
|
||||
if (dst !== undefined && dst !== null && typeof dst === 'object' && typeof src === 'object') {
|
||||
return Object.assign(dst, src);
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
if (typeof src !== 'undefined' && typeof dst === 'object' && typeof src === 'object') {
|
||||
if (src !== undefined && typeof dst === 'object' && typeof src === 'object') {
|
||||
Object.keys(src).forEach((key) => {
|
||||
if (
|
||||
typeof src[key] === 'object' &&
|
||||
|
@@ -18,8 +18,7 @@ export const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: any[])
|
||||
|
||||
// Join directives
|
||||
let sumOfDirectives: MermaidConfig = {};
|
||||
for (let i = 0; i < _directives.length; i++) {
|
||||
const d = _directives[i];
|
||||
for (const d of _directives) {
|
||||
sanitize(d);
|
||||
|
||||
// Apply the data from the directive where the the overrides the themeVariables
|
||||
@@ -153,7 +152,7 @@ export const getConfig = (): MermaidConfig => {
|
||||
export const sanitize = (options: any) => {
|
||||
// Checking that options are not in the list of excluded options
|
||||
['secure', ...(siteConfig.secure ?? [])].forEach((key) => {
|
||||
if (typeof options[key] !== 'undefined') {
|
||||
if (options[key] !== undefined) {
|
||||
// DO NOT attempt to print options[key] within `${}` as a malicious script
|
||||
// can exploit the logger's attempt to stringify the value and execute arbitrary code
|
||||
log.debug(`Denied attempt to modify a secure key ${key}`, options[key]);
|
||||
@@ -170,14 +169,13 @@ export const sanitize = (options: any) => {
|
||||
// Check that there no attempts of xss, there should be no tags at all in the directive
|
||||
// blocking data urls as base64 urls can contain svg's with inline script tags
|
||||
Object.keys(options).forEach((key) => {
|
||||
if (typeof options[key] === 'string') {
|
||||
if (
|
||||
options[key].indexOf('<') > -1 ||
|
||||
options[key].indexOf('>') > -1 ||
|
||||
options[key].indexOf('url(data:') > -1
|
||||
) {
|
||||
delete options[key];
|
||||
}
|
||||
if (
|
||||
typeof options[key] === 'string' &&
|
||||
(options[key].includes('<') ||
|
||||
options[key].includes('>') ||
|
||||
options[key].includes('url(data:'))
|
||||
) {
|
||||
delete options[key];
|
||||
}
|
||||
if (typeof options[key] === 'object') {
|
||||
sanitize(options[key]);
|
||||
|
@@ -189,6 +189,7 @@ export interface C4DiagramConfig extends BaseDiagramConfig {
|
||||
}
|
||||
|
||||
export interface GitGraphDiagramConfig extends BaseDiagramConfig {
|
||||
titleTopMargin?: number;
|
||||
diagramPadding?: number;
|
||||
nodeLabel?: NodeLabel;
|
||||
mainBranchName?: string;
|
||||
@@ -227,6 +228,7 @@ export interface MindmapDiagramConfig extends BaseDiagramConfig {
|
||||
export type PieDiagramConfig = BaseDiagramConfig;
|
||||
|
||||
export interface ErDiagramConfig extends BaseDiagramConfig {
|
||||
titleTopMargin?: number;
|
||||
diagramPadding?: number;
|
||||
layoutDirection?: string;
|
||||
minEntityWidth?: number;
|
||||
@@ -238,6 +240,7 @@ export interface ErDiagramConfig extends BaseDiagramConfig {
|
||||
}
|
||||
|
||||
export interface StateDiagramConfig extends BaseDiagramConfig {
|
||||
titleTopMargin?: number;
|
||||
arrowMarkerAbsolute?: boolean;
|
||||
dividerMargin?: number;
|
||||
sizeUnit?: number;
|
||||
@@ -258,6 +261,7 @@ export interface StateDiagramConfig extends BaseDiagramConfig {
|
||||
}
|
||||
|
||||
export interface ClassDiagramConfig extends BaseDiagramConfig {
|
||||
titleTopMargin?: number;
|
||||
arrowMarkerAbsolute?: boolean;
|
||||
dividerMargin?: number;
|
||||
padding?: number;
|
||||
@@ -343,6 +347,7 @@ export interface SequenceDiagramConfig extends BaseDiagramConfig {
|
||||
}
|
||||
|
||||
export interface FlowchartDiagramConfig extends BaseDiagramConfig {
|
||||
titleTopMargin?: number;
|
||||
arrowMarkerAbsolute?: boolean;
|
||||
diagramPadding?: number;
|
||||
htmlLabels?: boolean;
|
||||
|
@@ -54,7 +54,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||
const node = {
|
||||
isNode,
|
||||
label: decodeEntities(vertexText).replace(
|
||||
/fa[lrsb]?:fa-[\w-]+/g,
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
labelStyle: style.replace('fill:', 'color:'),
|
||||
@@ -74,7 +74,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||
rows = [];
|
||||
}
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
for (const row of rows) {
|
||||
const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
|
||||
tspan.setAttribute('dy', '1em');
|
||||
@@ -84,7 +84,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
|
||||
} else {
|
||||
tspan.setAttribute('class', 'row');
|
||||
}
|
||||
tspan.textContent = rows[j].trim();
|
||||
tspan.textContent = row.trim();
|
||||
svgLabel.appendChild(tspan);
|
||||
}
|
||||
return svgLabel;
|
||||
|
@@ -324,7 +324,7 @@ const cutPathAtIntersect = (_points, boundryNode) => {
|
||||
pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y);
|
||||
});
|
||||
// // if (!pointPresent) {
|
||||
if (!points.find((e) => e.x === inter.x && e.y === inter.y)) {
|
||||
if (!points.some((e) => e.x === inter.x && e.y === inter.y)) {
|
||||
points.push(inter);
|
||||
} else {
|
||||
log.warn('abc88 no intersect', inter, points);
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import dagre from 'dagre';
|
||||
import graphlib from 'graphlib';
|
||||
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
|
||||
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
|
||||
import insertMarkers from './markers';
|
||||
import { updateNodeBounds } from './shapes/util';
|
||||
import {
|
||||
@@ -15,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
|
||||
import { log } from '../logger';
|
||||
|
||||
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
log.info('Graph in recursive render: XXX', graphlib.json.write(graph), parentCluster);
|
||||
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
|
||||
const dir = graph.graph().rankdir;
|
||||
log.trace('Dir in recursive render - dir:', dir);
|
||||
|
||||
@@ -37,7 +37,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
// to the abstract node and is later used by dagre for the layout
|
||||
graph.nodes().forEach(function (v) {
|
||||
const node = graph.node(v);
|
||||
if (typeof parentCluster !== 'undefined') {
|
||||
if (parentCluster !== undefined) {
|
||||
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
||||
// data.clusterPositioning = true;
|
||||
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
|
||||
@@ -95,8 +95,8 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
log.info('### Layout ###');
|
||||
log.info('#############################################');
|
||||
log.info(graph);
|
||||
dagre.layout(graph);
|
||||
log.info('Graph after layout:', graphlib.json.write(graph));
|
||||
dagreLayout(graph);
|
||||
log.info('Graph after layout:', graphlibJson.write(graph));
|
||||
// Move the nodes to the correct place
|
||||
let diff = 0;
|
||||
sortNodesByHierarchy(graph).forEach(function (v) {
|
||||
@@ -153,10 +153,10 @@ export const render = (elem, graph, markers, diagramtype, id) => {
|
||||
clearClusters();
|
||||
clearGraphlib();
|
||||
|
||||
log.warn('Graph at first:', graphlib.json.write(graph));
|
||||
log.warn('Graph at first:', graphlibJson.write(graph));
|
||||
adjustClustersAndEdges(graph);
|
||||
log.warn('Graph after:', graphlib.json.write(graph));
|
||||
// log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph));
|
||||
log.warn('Graph after:', graphlibJson.write(graph));
|
||||
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
|
||||
recursiveRender(elem, graph, diagramtype);
|
||||
};
|
||||
|
||||
|
@@ -1,29 +1,23 @@
|
||||
/** Decorates with functions required by mermaids dagre-wrapper. */
|
||||
import { log } from '../logger';
|
||||
import graphlib from 'graphlib';
|
||||
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
|
||||
export let clusterDb = {};
|
||||
let decendants = {};
|
||||
let descendants = {};
|
||||
let parents = {};
|
||||
|
||||
export const clear = () => {
|
||||
decendants = {};
|
||||
descendants = {};
|
||||
parents = {};
|
||||
clusterDb = {};
|
||||
};
|
||||
|
||||
const isDecendant = (id, ancenstorId) => {
|
||||
const isDescendant = (id, ancenstorId) => {
|
||||
// if (id === ancenstorId) return true;
|
||||
|
||||
log.trace(
|
||||
'In isDecendant',
|
||||
ancenstorId,
|
||||
' ',
|
||||
id,
|
||||
' = ',
|
||||
decendants[ancenstorId].indexOf(id) >= 0
|
||||
);
|
||||
if (decendants[ancenstorId].indexOf(id) >= 0) {
|
||||
log.trace('In isDecendant', ancenstorId, ' ', id, ' = ', descendants[ancenstorId].includes(id));
|
||||
if (descendants[ancenstorId].includes(id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -31,7 +25,7 @@ const isDecendant = (id, ancenstorId) => {
|
||||
};
|
||||
|
||||
const edgeInCluster = (edge, clusterId) => {
|
||||
log.info('Decendants of ', clusterId, ' is ', decendants[clusterId]);
|
||||
log.info('Decendants of ', clusterId, ' is ', descendants[clusterId]);
|
||||
log.info('Edge is ', edge);
|
||||
// Edges to/from the cluster is not in the cluster, they are in the parent
|
||||
if (edge.v === clusterId) {
|
||||
@@ -41,15 +35,15 @@ const edgeInCluster = (edge, clusterId) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!decendants[clusterId]) {
|
||||
if (!descendants[clusterId]) {
|
||||
log.debug('Tilt, ', clusterId, ',not in decendants');
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
decendants[clusterId].indexOf(edge.v) >= 0 ||
|
||||
isDecendant(edge.v, clusterId) ||
|
||||
isDecendant(edge.w, clusterId) ||
|
||||
decendants[clusterId].indexOf(edge.w) >= 0
|
||||
descendants[clusterId].includes(edge.v) ||
|
||||
isDescendant(edge.v, clusterId) ||
|
||||
isDescendant(edge.w, clusterId) ||
|
||||
descendants[clusterId].includes(edge.w)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -131,14 +125,14 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
graph.removeNode(node);
|
||||
});
|
||||
};
|
||||
export const extractDecendants = (id, graph) => {
|
||||
export const extractDescendants = (id, graph) => {
|
||||
// log.debug('Extracting ', id);
|
||||
const children = graph.children(id);
|
||||
let res = [].concat(children);
|
||||
let res = [...children];
|
||||
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
parents[children[i]] = id;
|
||||
res = res.concat(extractDecendants(children[i], graph));
|
||||
for (const child of children) {
|
||||
parents[child] = id;
|
||||
res = [...res, ...extractDescendants(child, graph)];
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -153,13 +147,13 @@ export const extractDecendants = (id, graph) => {
|
||||
export const validate = (graph) => {
|
||||
const edges = graph.edges();
|
||||
log.trace('Edges: ', edges);
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
if (graph.children(edges[i].v).length > 0) {
|
||||
log.trace('The node ', edges[i].v, ' is part of and edge even though it has children');
|
||||
for (const edge of edges) {
|
||||
if (graph.children(edge.v).length > 0) {
|
||||
log.trace('The node ', edge.v, ' is part of and edge even though it has children');
|
||||
return false;
|
||||
}
|
||||
if (graph.children(edges[i].w).length > 0) {
|
||||
log.trace('The node ', edges[i].w, ' is part of and edge even though it has children');
|
||||
if (graph.children(edge.w).length > 0) {
|
||||
log.trace('The node ', edge.w, ' is part of and edge even though it has children');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -182,8 +176,8 @@ export const findNonClusterChild = (id, graph) => {
|
||||
log.trace('This is a valid node', id);
|
||||
return id;
|
||||
}
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const _id = findNonClusterChild(children[i], graph);
|
||||
for (const child of children) {
|
||||
const _id = findNonClusterChild(child, graph);
|
||||
if (_id) {
|
||||
log.trace('Found replacement for', id, ' => ', _id);
|
||||
return _id;
|
||||
@@ -225,7 +219,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
' Replacement id in edges: ',
|
||||
findNonClusterChild(id, graph)
|
||||
);
|
||||
decendants[id] = extractDecendants(id, graph);
|
||||
descendants[id] = extractDescendants(id, graph);
|
||||
clusterDb[id] = { id: findNonClusterChild(id, graph), clusterData: graph.node(id) };
|
||||
}
|
||||
});
|
||||
@@ -235,7 +229,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
const children = graph.children(id);
|
||||
const edges = graph.edges();
|
||||
if (children.length > 0) {
|
||||
log.debug('Cluster identified', id, decendants);
|
||||
log.debug('Cluster identified', id, descendants);
|
||||
edges.forEach((edge) => {
|
||||
// log.debug('Edge, decendants: ', edge, decendants[id]);
|
||||
|
||||
@@ -244,19 +238,19 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
// Any edge where either the one of the nodes is descending to the cluster but not the other
|
||||
// if (decendants[id].indexOf(edge.v) < 0 && decendants[id].indexOf(edge.w) < 0) {
|
||||
|
||||
const d1 = isDecendant(edge.v, id);
|
||||
const d2 = isDecendant(edge.w, id);
|
||||
const d1 = isDescendant(edge.v, id);
|
||||
const d2 = isDescendant(edge.w, id);
|
||||
|
||||
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
|
||||
if (d1 ^ d2) {
|
||||
log.warn('Edge: ', edge, ' leaves cluster ', id);
|
||||
log.warn('Decendants of XXX ', id, ': ', decendants[id]);
|
||||
log.warn('Decendants of XXX ', id, ': ', descendants[id]);
|
||||
clusterDb[id].externalConnections = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.debug('Not a cluster ', id, decendants);
|
||||
log.debug('Not a cluster ', id, descendants);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -276,7 +270,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
'ids:',
|
||||
e.v,
|
||||
e.w,
|
||||
'Translateing: ',
|
||||
'Translating: ',
|
||||
clusterDb[e.v],
|
||||
' --- ',
|
||||
clusterDb[e.w]
|
||||
@@ -322,7 +316,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
graph.setEdge(v, w, edge, e.name);
|
||||
}
|
||||
});
|
||||
log.warn('Adjusted Graph', graphlib.json.write(graph));
|
||||
log.warn('Adjusted Graph', graphlibJson.write(graph));
|
||||
extractor(graph, 0);
|
||||
|
||||
log.trace(clusterDb);
|
||||
@@ -336,7 +330,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
};
|
||||
|
||||
export const extractor = (graph, depth) => {
|
||||
log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
|
||||
log.warn('extractor - ', depth, graphlibJson.write(graph), graph.children('D'));
|
||||
if (depth > 10) {
|
||||
log.error('Bailing out');
|
||||
return;
|
||||
@@ -346,8 +340,7 @@ export const extractor = (graph, depth) => {
|
||||
// for (let i = 0;)
|
||||
let nodes = graph.nodes();
|
||||
let hasChildren = false;
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
for (const node of nodes) {
|
||||
const children = graph.children(node);
|
||||
hasChildren = hasChildren || children.length > 0;
|
||||
}
|
||||
@@ -359,9 +352,7 @@ export const extractor = (graph, depth) => {
|
||||
// const clusters = Object.keys(clusterDb);
|
||||
// clusters.forEach(clusterId => {
|
||||
log.debug('Nodes = ', nodes, depth);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
|
||||
for (const node of nodes) {
|
||||
log.debug(
|
||||
'Extracting node',
|
||||
node,
|
||||
@@ -393,11 +384,9 @@ export const extractor = (graph, depth) => {
|
||||
|
||||
const graphSettings = graph.graph();
|
||||
let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB';
|
||||
if (clusterDb[node]) {
|
||||
if (clusterDb[node].clusterData && clusterDb[node].clusterData.dir) {
|
||||
dir = clusterDb[node].clusterData.dir;
|
||||
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
|
||||
}
|
||||
if (clusterDb[node] && clusterDb[node].clusterData && clusterDb[node].clusterData.dir) {
|
||||
dir = clusterDb[node].clusterData.dir;
|
||||
log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir);
|
||||
}
|
||||
|
||||
const clusterGraph = new graphlib.Graph({
|
||||
@@ -415,7 +404,7 @@ export const extractor = (graph, depth) => {
|
||||
return {};
|
||||
});
|
||||
|
||||
log.warn('Old graph before copy', graphlib.json.write(graph));
|
||||
log.warn('Old graph before copy', graphlibJson.write(graph));
|
||||
copy(node, graph, clusterGraph, node);
|
||||
graph.setNode(node, {
|
||||
clusterNode: true,
|
||||
@@ -424,8 +413,8 @@ export const extractor = (graph, depth) => {
|
||||
labelText: clusterDb[node].labelText,
|
||||
graph: clusterGraph,
|
||||
});
|
||||
log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph));
|
||||
log.debug('Old graph after copy', graphlib.json.write(graph));
|
||||
log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph));
|
||||
log.debug('Old graph after copy', graphlibJson.write(graph));
|
||||
} else {
|
||||
log.warn(
|
||||
'Cluster ** ',
|
||||
@@ -445,8 +434,7 @@ export const extractor = (graph, depth) => {
|
||||
|
||||
nodes = graph.nodes();
|
||||
log.warn('New list of nodes', nodes);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
for (const node of nodes) {
|
||||
const data = graph.node(node);
|
||||
log.warn(' Now next level', node, data);
|
||||
if (data.clusterNode) {
|
||||
@@ -463,7 +451,7 @@ const sorter = (graph, nodes) => {
|
||||
nodes.forEach((node) => {
|
||||
const children = graph.children(node);
|
||||
const sorted = sorter(graph, children);
|
||||
result = result.concat(sorted);
|
||||
result = [...result, ...sorted];
|
||||
});
|
||||
|
||||
return result;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import graphlib from 'graphlib';
|
||||
import dagre from 'dagre';
|
||||
import * as graphlibJson from 'dagre-d3-es/src/graphlib/json';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
import {
|
||||
validate,
|
||||
adjustClustersAndEdges,
|
||||
extractDecendants,
|
||||
extractDescendants,
|
||||
sortNodesByHierarchy,
|
||||
} from './mermaid-graphlib';
|
||||
import { setLogLevel, log } from '../logger';
|
||||
@@ -233,9 +233,9 @@ describe('Graphlib decorations', () => {
|
||||
g.setParent('D', 'C');
|
||||
|
||||
// log.info('Graph before', g.node('D'))
|
||||
// log.info('Graph before', graphlib.json.write(g))
|
||||
// log.info('Graph before', graphlibJson.write(g))
|
||||
adjustClustersAndEdges(g);
|
||||
// log.info('Graph after', graphlib.json.write(g), g.node('C').graph)
|
||||
// log.info('Graph after', graphlibJson.write(g), g.node('C').graph)
|
||||
|
||||
const CGraph = g.node('C').graph;
|
||||
const DGraph = CGraph.node('D').graph;
|
||||
@@ -279,9 +279,9 @@ describe('Graphlib decorations', () => {
|
||||
g.setEdge('A', 'C', { data: 'link2' }, '2');
|
||||
|
||||
log.info('Graph before', g.node('D'));
|
||||
log.info('Graph before', graphlib.json.write(g));
|
||||
log.info('Graph before', graphlibJson.write(g));
|
||||
adjustClustersAndEdges(g);
|
||||
log.trace('Graph after', graphlib.json.write(g));
|
||||
log.trace('Graph after', graphlibJson.write(g));
|
||||
expect(g.nodes()).toEqual(['C', 'B', 'A']);
|
||||
expect(g.nodes().length).toBe(3);
|
||||
expect(g.edges().length).toBe(2);
|
||||
@@ -334,11 +334,11 @@ describe('Graphlib decorations', () => {
|
||||
g.setEdge('c', 'd', { data: 'link2' }, '2');
|
||||
g.setEdge('d', 'e', { data: 'link2' }, '2');
|
||||
|
||||
log.info('Graph before', graphlib.json.write(g));
|
||||
log.info('Graph before', graphlibJson.write(g));
|
||||
adjustClustersAndEdges(g);
|
||||
const bGraph = g.node('b').graph;
|
||||
// log.trace('Graph after', graphlib.json.write(g))
|
||||
log.info('Graph after', graphlib.json.write(bGraph));
|
||||
// log.trace('Graph after', graphlibJson.write(g))
|
||||
log.info('Graph after', graphlibJson.write(bGraph));
|
||||
expect(bGraph.nodes().length).toBe(3);
|
||||
expect(bGraph.edges().length).toBe(2);
|
||||
});
|
||||
@@ -360,13 +360,13 @@ describe('Graphlib decorations', () => {
|
||||
g.setParent('c', 'b');
|
||||
g.setParent('e', 'c');
|
||||
|
||||
log.info('Graph before', graphlib.json.write(g));
|
||||
log.info('Graph before', graphlibJson.write(g));
|
||||
adjustClustersAndEdges(g);
|
||||
const aGraph = g.node('a').graph;
|
||||
const bGraph = aGraph.node('b').graph;
|
||||
log.info('Graph after', graphlib.json.write(aGraph));
|
||||
log.info('Graph after', graphlibJson.write(aGraph));
|
||||
const cGraph = bGraph.node('c').graph;
|
||||
// log.trace('Graph after', graphlib.json.write(g))
|
||||
// log.trace('Graph after', graphlibJson.write(g))
|
||||
expect(aGraph.nodes().length).toBe(1);
|
||||
expect(bGraph.nodes().length).toBe(1);
|
||||
expect(cGraph.nodes().length).toBe(1);
|
||||
@@ -388,19 +388,19 @@ flowchart TB
|
||||
const exportedGraph = JSON.parse(
|
||||
'{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}'
|
||||
);
|
||||
const gr = graphlib.json.read(exportedGraph);
|
||||
const gr = graphlibJson.read(exportedGraph);
|
||||
|
||||
log.info('Graph before', graphlib.json.write(gr));
|
||||
log.info('Graph before', graphlibJson.write(gr));
|
||||
adjustClustersAndEdges(gr);
|
||||
const aGraph = gr.node('A').graph;
|
||||
const bGraph = aGraph.node('B').graph;
|
||||
log.info('Graph after', graphlib.json.write(aGraph));
|
||||
// log.trace('Graph after', graphlib.json.write(g))
|
||||
log.info('Graph after', graphlibJson.write(aGraph));
|
||||
// log.trace('Graph after', graphlibJson.write(g))
|
||||
expect(aGraph.parent('c')).toBe('B');
|
||||
expect(aGraph.parent('B')).toBe(undefined);
|
||||
});
|
||||
});
|
||||
describe('extractDecendants', function () {
|
||||
describe('extractDescendants', function () {
|
||||
let g;
|
||||
beforeEach(function () {
|
||||
setLogLevel(1);
|
||||
@@ -443,9 +443,9 @@ describe('extractDecendants', function () {
|
||||
g.setEdge('A', 'C', { data: 'link2' }, '2');
|
||||
|
||||
// log.info(g.edges())
|
||||
const d1 = extractDecendants('A', g);
|
||||
const d2 = extractDecendants('B', g);
|
||||
const d3 = extractDecendants('C', g);
|
||||
const d1 = extractDescendants('A', g);
|
||||
const d2 = extractDescendants('B', g);
|
||||
const d3 = extractDescendants('C', g);
|
||||
|
||||
expect(d1).toEqual(['a']);
|
||||
expect(d2).toEqual(['b']);
|
||||
|
@@ -391,12 +391,10 @@ const labelRect = (parent, node) => {
|
||||
function applyNodePropertyBorders(rect, borders, totalWidth, totalHeight) {
|
||||
const strokeDashArray = [];
|
||||
const addBorder = (length) => {
|
||||
strokeDashArray.push(length);
|
||||
strokeDashArray.push(0);
|
||||
strokeDashArray.push(length, 0);
|
||||
};
|
||||
const skipBorder = (length) => {
|
||||
strokeDashArray.push(0);
|
||||
strokeDashArray.push(length);
|
||||
strokeDashArray.push(0, length);
|
||||
};
|
||||
if (borders.includes('t')) {
|
||||
log.debug('add top border');
|
||||
|
@@ -21,7 +21,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
|
||||
|
||||
// Replace labelText with default value if undefined
|
||||
let labelText;
|
||||
if (typeof node.labelText === 'undefined') {
|
||||
if (node.labelText === undefined) {
|
||||
labelText = '';
|
||||
} else {
|
||||
labelText = typeof node.labelText === 'string' ? node.labelText : node.labelText[0];
|
||||
|
@@ -154,6 +154,17 @@ const config: Partial<MermaidConfig> = {
|
||||
|
||||
/** The object containing configurations specific for flowcharts */
|
||||
flowchart: {
|
||||
/**
|
||||
* ### titleTopMargin
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | titleTopMargin | Margin top for the text over the flowchart | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 25
|
||||
*/
|
||||
titleTopMargin: 25,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ |
|
||||
@@ -851,6 +862,16 @@ const config: Partial<MermaidConfig> = {
|
||||
sectionColours: ['#fff'],
|
||||
},
|
||||
class: {
|
||||
/**
|
||||
* ### titleTopMargin
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | titleTopMargin | Margin top for the text over the class diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 25
|
||||
*/
|
||||
titleTopMargin: 25,
|
||||
arrowMarkerAbsolute: false,
|
||||
dividerMargin: 10,
|
||||
padding: 5,
|
||||
@@ -884,6 +905,16 @@ const config: Partial<MermaidConfig> = {
|
||||
defaultRenderer: 'dagre-wrapper',
|
||||
},
|
||||
state: {
|
||||
/**
|
||||
* ### titleTopMargin
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | titleTopMargin | Margin top for the text over the state diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 25
|
||||
*/
|
||||
titleTopMargin: 25,
|
||||
dividerMargin: 10,
|
||||
sizeUnit: 5,
|
||||
padding: 8,
|
||||
@@ -932,6 +963,17 @@ const config: Partial<MermaidConfig> = {
|
||||
|
||||
/** The object containing configurations specific for entity relationship diagrams */
|
||||
er: {
|
||||
/**
|
||||
* ### titleTopMargin
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | titleTopMargin | Margin top for the text over the diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 25
|
||||
*/
|
||||
titleTopMargin: 25,
|
||||
|
||||
/**
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ----------------------------------------------- | ------- | -------- | ------------------ |
|
||||
@@ -1085,6 +1127,16 @@ const config: Partial<MermaidConfig> = {
|
||||
line_height: 20,
|
||||
},
|
||||
gitGraph: {
|
||||
/**
|
||||
* ### titleTopMargin
|
||||
*
|
||||
* | Parameter | Description | Type | Required | Values |
|
||||
* | -------------- | ---------------------------------------------- | ------- | -------- | ------------------ |
|
||||
* | titleTopMargin | Margin top for the text over the Git diagram | Integer | Required | Any Positive Value |
|
||||
*
|
||||
* **Notes:** Default value: 25
|
||||
*/
|
||||
titleTopMargin: 25,
|
||||
diagramPadding: 8,
|
||||
nodeLabel: {
|
||||
width: 75,
|
||||
|
@@ -2,9 +2,9 @@ import { MermaidConfig } from '../config.type';
|
||||
import { log } from '../logger';
|
||||
import { DetectorRecord, DiagramDetector, DiagramLoader } from './types';
|
||||
import { ExternalDiagramDefinition } from '../diagram-api/types';
|
||||
import { frontMatterRegex } from './frontmatter';
|
||||
|
||||
const directive =
|
||||
/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi;
|
||||
const directive = /%{2}{\s*(?:(\w+)\s*:|(\w+))\s*(?:(\w+)|((?:(?!}%{2}).|\r?\n)*))?\s*(?:}%{2})?/gi;
|
||||
const anyComment = /\s*%%.*\n/gm;
|
||||
|
||||
const detectors: Record<string, DetectorRecord> = {};
|
||||
@@ -32,7 +32,7 @@ const detectors: Record<string, DetectorRecord> = {};
|
||||
* @returns A graph definition key
|
||||
*/
|
||||
export const detectType = function (text: string, config?: MermaidConfig): string {
|
||||
text = text.replace(directive, '').replace(anyComment, '\n');
|
||||
text = text.replace(frontMatterRegex, '').replace(directive, '').replace(anyComment, '\n');
|
||||
for (const [key, { detector }] of Object.entries(detectors)) {
|
||||
const diagram = detector(text, config);
|
||||
if (diagram) {
|
||||
|
78
packages/mermaid/src/diagram-api/frontmatter.spec.ts
Normal file
78
packages/mermaid/src/diagram-api/frontmatter.spec.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { vi } from 'vitest';
|
||||
import { extractFrontMatter } from './frontmatter';
|
||||
|
||||
const dbMock = () => ({ setDiagramTitle: vi.fn() });
|
||||
|
||||
describe('extractFrontmatter', () => {
|
||||
it('returns text unchanged if no frontmatter', () => {
|
||||
expect(extractFrontMatter('diagram', dbMock())).toEqual('diagram');
|
||||
});
|
||||
|
||||
it('returns text unchanged if frontmatter lacks closing delimiter', () => {
|
||||
const text = `---\ntitle: foo\ndiagram`;
|
||||
expect(extractFrontMatter(text, dbMock())).toEqual(text);
|
||||
});
|
||||
|
||||
it('handles empty frontmatter', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\n\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles frontmatter without mappings', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\n1\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not try to parse frontmatter at the end', () => {
|
||||
const db = dbMock();
|
||||
const text = `diagram\n---\ntitle: foo\n---\n`;
|
||||
expect(extractFrontMatter(text, db)).toEqual(text);
|
||||
expect(db.setDiagramTitle).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handles frontmatter with multiple delimiters', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\ntitle: foo---bar\n---\ndiagram\n---\ntest`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram\n---\ntest');
|
||||
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo---bar');
|
||||
});
|
||||
|
||||
it('handles frontmatter with multi-line string and multiple delimiters', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\ntitle: |\n multi-line string\n ---\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).toHaveBeenCalledWith('multi-line string\n---\n');
|
||||
});
|
||||
|
||||
it('handles frontmatter with title', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\ntitle: foo\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
|
||||
});
|
||||
|
||||
it('handles booleans in frontmatter properly', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\ntitle: true\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).toHaveBeenCalledWith('true');
|
||||
});
|
||||
|
||||
it('ignores unspecified frontmatter keys', () => {
|
||||
const db = dbMock();
|
||||
const text = `---\ninvalid: true\ntitle: foo\ntest: bar\n---\ndiagram`;
|
||||
expect(extractFrontMatter(text, db)).toEqual('diagram');
|
||||
expect(db.setDiagramTitle).toHaveBeenCalledWith('foo');
|
||||
});
|
||||
|
||||
it('throws exception for invalid YAML syntax', () => {
|
||||
const text = `---\n!!!\n---\ndiagram`;
|
||||
expect(() => extractFrontMatter(text, dbMock())).toThrow(
|
||||
'tag suffix cannot contain exclamation marks'
|
||||
);
|
||||
});
|
||||
});
|
40
packages/mermaid/src/diagram-api/frontmatter.ts
Normal file
40
packages/mermaid/src/diagram-api/frontmatter.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { DiagramDb } from './types';
|
||||
// The "* as yaml" part is necessary for tree-shaking
|
||||
import * as yaml from 'js-yaml';
|
||||
|
||||
// Match Jekyll-style front matter blocks (https://jekyllrb.com/docs/front-matter/).
|
||||
// Based on regex used by Jekyll: https://github.com/jekyll/jekyll/blob/6dd3cc21c40b98054851846425af06c64f9fb466/lib/jekyll/document.rb#L10
|
||||
// Note that JS doesn't support the "\A" anchor, which means we can't use
|
||||
// multiline mode.
|
||||
// Relevant YAML spec: https://yaml.org/spec/1.2.2/#914-explicit-documents
|
||||
export const frontMatterRegex = /^-{3}\s*[\n\r](.*?)[\n\r]-{3}\s*[\n\r]+/s;
|
||||
|
||||
type FrontMatterMetadata = {
|
||||
title?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract and parse frontmatter from text, if present, and sets appropriate
|
||||
* properties in the provided db.
|
||||
* @param text - The text that may have a YAML frontmatter.
|
||||
* @param db - Diagram database, could be of any diagram.
|
||||
* @returns text with frontmatter stripped out
|
||||
*/
|
||||
export function extractFrontMatter(text: string, db: DiagramDb): string {
|
||||
const matches = text.match(frontMatterRegex);
|
||||
if (matches) {
|
||||
const parsed: FrontMatterMetadata = yaml.load(matches[1], {
|
||||
// To keep things simple, only allow strings, arrays, and plain objects.
|
||||
// https://www.yaml.org/spec/1.2/spec.html#id2802346
|
||||
schema: yaml.FAILSAFE_SCHEMA,
|
||||
}) as FrontMatterMetadata;
|
||||
|
||||
if (parsed?.title) {
|
||||
db.setDiagramTitle?.(parsed.title);
|
||||
}
|
||||
|
||||
return text.slice(matches[0].length);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
}
|
@@ -8,8 +8,16 @@ export interface InjectUtils {
|
||||
_setupGraphViewbox: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic Diagram DB that may apply to any diagram type.
|
||||
*/
|
||||
export interface DiagramDb {
|
||||
clear?: () => void;
|
||||
setDiagramTitle?: (title: string) => void;
|
||||
}
|
||||
|
||||
export interface DiagramDefinition {
|
||||
db: any;
|
||||
db: DiagramDb;
|
||||
renderer: any;
|
||||
parser: any;
|
||||
styles: any;
|
||||
|
@@ -48,7 +48,7 @@ class Bounds {
|
||||
}
|
||||
|
||||
updateVal(obj, key, val, fun) {
|
||||
if (typeof obj[key] === 'undefined') {
|
||||
if (obj[key] === undefined) {
|
||||
obj[key] = val;
|
||||
} else {
|
||||
obj[key] = fun(val, obj[key]);
|
||||
@@ -177,12 +177,12 @@ function calcC4ShapeTextWH(textType, c4Shape, c4ShapeTextWrap, textConf, textLim
|
||||
let lineHeight = 0;
|
||||
c4Shape[textType].height = 0;
|
||||
c4Shape[textType].width = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
for (const line of lines) {
|
||||
c4Shape[textType].width = Math.max(
|
||||
calculateTextWidth(lines[i], textConf),
|
||||
calculateTextWidth(line, textConf),
|
||||
c4Shape[textType].width
|
||||
);
|
||||
lineHeight = calculateTextHeight(lines[i], textConf);
|
||||
lineHeight = calculateTextHeight(line, textConf);
|
||||
c4Shape[textType].height = c4Shape[textType].height + lineHeight;
|
||||
}
|
||||
// c4Shapes[textType].height = c4Shapes[textType].textLines * textConf.fontSize;
|
||||
@@ -212,9 +212,9 @@ export const drawC4ShapeArray = function (currentBounds, diagram, c4ShapeArray,
|
||||
// Upper Y is relative point
|
||||
let Y = 0;
|
||||
// Draw the c4ShapeArray
|
||||
for (let i = 0; i < c4ShapeKeys.length; i++) {
|
||||
for (const c4ShapeKey of c4ShapeKeys) {
|
||||
Y = 0;
|
||||
const c4Shape = c4ShapeArray[c4ShapeKeys[i]];
|
||||
const c4Shape = c4ShapeArray[c4ShapeKey];
|
||||
|
||||
// calc c4 shape type width and height
|
||||
|
||||
@@ -461,8 +461,7 @@ function drawInsideBoundary(
|
||||
// conf.width * conf.c4ShapeInRow + conf.c4ShapeMargin * conf.c4ShapeInRow * 2,
|
||||
// parentBounds.data.widthLimit / Math.min(conf.c4BoundaryInRow, currentBoundaries.length)
|
||||
// );
|
||||
for (let i = 0; i < currentBoundaries.length; i++) {
|
||||
let currentBoundary = currentBoundaries[i];
|
||||
for (let [i, currentBoundary] of currentBoundaries.entries()) {
|
||||
let Y = 0;
|
||||
currentBoundary.image = { width: 0, height: 0, Y: 0 };
|
||||
if (currentBoundary.sprite) {
|
||||
|
@@ -52,8 +52,8 @@ export const drawText = function (elem, textData) {
|
||||
let dy = 0;
|
||||
let yfunc = () => textData.y;
|
||||
if (
|
||||
typeof textData.valign !== 'undefined' &&
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.valign !== undefined &&
|
||||
textData.textMargin !== undefined &&
|
||||
textData.textMargin > 0
|
||||
) {
|
||||
switch (textData.valign) {
|
||||
@@ -78,9 +78,9 @@ export const drawText = function (elem, textData) {
|
||||
}
|
||||
}
|
||||
if (
|
||||
typeof textData.anchor !== 'undefined' &&
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
typeof textData.width !== 'undefined'
|
||||
textData.anchor !== undefined &&
|
||||
textData.textMargin !== undefined &&
|
||||
textData.width !== undefined
|
||||
) {
|
||||
switch (textData.anchor) {
|
||||
case 'left':
|
||||
@@ -106,12 +106,11 @@ export const drawText = function (elem, textData) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
for (let [i, line] of lines.entries()) {
|
||||
if (
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.textMargin !== undefined &&
|
||||
textData.textMargin === 0 &&
|
||||
typeof textData.fontSize !== 'undefined'
|
||||
textData.fontSize !== undefined
|
||||
) {
|
||||
dy = i * textData.fontSize;
|
||||
}
|
||||
@@ -119,28 +118,28 @@ export const drawText = function (elem, textData) {
|
||||
const textElem = elem.append('text');
|
||||
textElem.attr('x', textData.x);
|
||||
textElem.attr('y', yfunc());
|
||||
if (typeof textData.anchor !== 'undefined') {
|
||||
if (textData.anchor !== undefined) {
|
||||
textElem
|
||||
.attr('text-anchor', textData.anchor)
|
||||
.attr('dominant-baseline', textData.dominantBaseline)
|
||||
.attr('alignment-baseline', textData.alignmentBaseline);
|
||||
}
|
||||
if (typeof textData.fontFamily !== 'undefined') {
|
||||
if (textData.fontFamily !== undefined) {
|
||||
textElem.style('font-family', textData.fontFamily);
|
||||
}
|
||||
if (typeof textData.fontSize !== 'undefined') {
|
||||
if (textData.fontSize !== undefined) {
|
||||
textElem.style('font-size', textData.fontSize);
|
||||
}
|
||||
if (typeof textData.fontWeight !== 'undefined') {
|
||||
if (textData.fontWeight !== undefined) {
|
||||
textElem.style('font-weight', textData.fontWeight);
|
||||
}
|
||||
if (typeof textData.fill !== 'undefined') {
|
||||
if (textData.fill !== undefined) {
|
||||
textElem.attr('fill', textData.fill);
|
||||
}
|
||||
if (typeof textData.class !== 'undefined') {
|
||||
if (textData.class !== undefined) {
|
||||
textElem.attr('class', textData.class);
|
||||
}
|
||||
if (typeof textData.dy !== 'undefined') {
|
||||
if (textData.dy !== undefined) {
|
||||
textElem.attr('dy', textData.dy);
|
||||
} else if (dy !== 0) {
|
||||
textElem.attr('dy', dy);
|
||||
@@ -149,7 +148,7 @@ export const drawText = function (elem, textData) {
|
||||
if (textData.tspan) {
|
||||
const span = textElem.append('tspan');
|
||||
span.attr('x', textData.x);
|
||||
if (typeof textData.fill !== 'undefined') {
|
||||
if (textData.fill !== undefined) {
|
||||
span.attr('fill', textData.fill);
|
||||
}
|
||||
span.text(line);
|
||||
@@ -157,8 +156,8 @@ export const drawText = function (elem, textData) {
|
||||
textElem.text(line);
|
||||
}
|
||||
if (
|
||||
typeof textData.valign !== 'undefined' &&
|
||||
typeof textData.textMargin !== 'undefined' &&
|
||||
textData.valign !== undefined &&
|
||||
textData.textMargin !== undefined &&
|
||||
textData.textMargin > 0
|
||||
) {
|
||||
textHeight += (textElem._groups || textElem)[0][0].getBBox().height;
|
||||
|
@@ -10,6 +10,8 @@ import {
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
} from '../../commonDb';
|
||||
|
||||
const MERMAID_DOM_ID_PREFIX = 'classid-';
|
||||
@@ -50,7 +52,7 @@ const splitClassNameAndType = function (id) {
|
||||
export const addClass = function (id) {
|
||||
let classId = splitClassNameAndType(id);
|
||||
// Only add class if not exists
|
||||
if (typeof classes[classId.className] !== 'undefined') {
|
||||
if (classes[classId.className] !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,9 +77,9 @@ export const addClass = function (id) {
|
||||
*/
|
||||
export const lookUpDomId = function (id) {
|
||||
const classKeys = Object.keys(classes);
|
||||
for (let i = 0; i < classKeys.length; i++) {
|
||||
if (classes[classKeys[i]].id === id) {
|
||||
return classes[classKeys[i]].domId;
|
||||
for (const classKey of classKeys) {
|
||||
if (classes[classKey].id === id) {
|
||||
return classes[classKey].domId;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -205,7 +207,7 @@ export const setCssClass = function (ids, className) {
|
||||
if (_id[0].match(/\d/)) {
|
||||
id = MERMAID_DOM_ID_PREFIX + id;
|
||||
}
|
||||
if (typeof classes[id] !== 'undefined') {
|
||||
if (classes[id] !== undefined) {
|
||||
classes[id].cssClasses.push(className);
|
||||
}
|
||||
});
|
||||
@@ -220,7 +222,7 @@ export const setCssClass = function (ids, className) {
|
||||
const setTooltip = function (ids, tooltip) {
|
||||
const config = configApi.getConfig();
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
if (tooltip !== undefined) {
|
||||
classes[id].tooltip = common.sanitizeText(tooltip, config);
|
||||
}
|
||||
});
|
||||
@@ -242,7 +244,7 @@ export const setLink = function (ids, linkStr, target) {
|
||||
if (_id[0].match(/\d/)) {
|
||||
id = MERMAID_DOM_ID_PREFIX + id;
|
||||
}
|
||||
if (typeof classes[id] !== 'undefined') {
|
||||
if (classes[id] !== undefined) {
|
||||
classes[id].link = utils.formatUrl(linkStr, config);
|
||||
if (config.securityLevel === 'sandbox') {
|
||||
classes[id].linkTarget = '_top';
|
||||
@@ -279,10 +281,10 @@ const setClickFunc = function (domId, functionName, functionArgs) {
|
||||
if (config.securityLevel !== 'loose') {
|
||||
return;
|
||||
}
|
||||
if (typeof functionName === 'undefined') {
|
||||
if (functionName === undefined) {
|
||||
return;
|
||||
}
|
||||
if (typeof classes[id] !== 'undefined') {
|
||||
if (classes[id] !== undefined) {
|
||||
let argList = [];
|
||||
if (typeof functionArgs === 'string') {
|
||||
/* Splits functionArgs by ',', ignoring all ',' in double quoted strings */
|
||||
@@ -408,4 +410,6 @@ export default {
|
||||
getTooltip,
|
||||
setTooltip,
|
||||
lookUpDomId,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
};
|
||||
|
@@ -1,16 +1,15 @@
|
||||
import { select } from 'd3';
|
||||
import graphlib from 'graphlib';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
import { log } from '../../logger';
|
||||
import { getConfig } from '../../config';
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import utils from '../../utils';
|
||||
import { curveLinear } from 'd3';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
import { setupGraphViewbox } from '../../setupGraphViewbox';
|
||||
import common from '../common/common';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
|
||||
let idCache = {};
|
||||
|
||||
const sanitizeText = (txt) => common.sanitizeText(txt, getConfig());
|
||||
|
||||
let conf = {
|
||||
@@ -274,16 +273,16 @@ export const addRelations = function (relations, g) {
|
||||
let style = '';
|
||||
let labelStyle = '';
|
||||
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.style !== undefined) {
|
||||
const styles = getStylesFromArray(edge.style);
|
||||
style = styles.style;
|
||||
labelStyle = styles.labelStyle;
|
||||
} else {
|
||||
style = 'fill:none';
|
||||
if (typeof defaultStyle !== 'undefined') {
|
||||
if (defaultStyle !== undefined) {
|
||||
style = defaultStyle;
|
||||
}
|
||||
if (typeof defaultLabelStyle !== 'undefined') {
|
||||
if (defaultLabelStyle !== undefined) {
|
||||
labelStyle = defaultLabelStyle;
|
||||
}
|
||||
}
|
||||
@@ -291,17 +290,17 @@ export const addRelations = function (relations, g) {
|
||||
edgeData.style = style;
|
||||
edgeData.labelStyle = labelStyle;
|
||||
|
||||
if (typeof edge.interpolate !== 'undefined') {
|
||||
if (edge.interpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
|
||||
} else if (typeof relations.defaultInterpolate !== 'undefined') {
|
||||
} else if (relations.defaultInterpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(relations.defaultInterpolate, curveLinear);
|
||||
} else {
|
||||
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
|
||||
}
|
||||
|
||||
edge.text = edge.title;
|
||||
if (typeof edge.text === 'undefined') {
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.text === undefined) {
|
||||
if (edge.style !== undefined) {
|
||||
edgeData.arrowheadStyle = 'fill: #333';
|
||||
}
|
||||
} else {
|
||||
@@ -315,7 +314,7 @@ export const addRelations = function (relations, g) {
|
||||
edgeData.labelType = 'text';
|
||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||
|
||||
if (typeof edge.style === 'undefined') {
|
||||
if (edge.style === undefined) {
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
||||
}
|
||||
|
||||
@@ -429,15 +428,15 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
id
|
||||
);
|
||||
|
||||
utils.insertTitle(svg, 'classTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
|
||||
|
||||
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
|
||||
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
||||
for (let k = 0; k < labels.length; k++) {
|
||||
const label = labels[k];
|
||||
|
||||
for (const label of labels) {
|
||||
// Get dimensions of label
|
||||
const dim = label.getBBox();
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { select } from 'd3';
|
||||
import dagre from 'dagre';
|
||||
import graphlib from 'graphlib';
|
||||
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
|
||||
import { log } from '../../logger';
|
||||
import svgDraw from './svgDraw';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||
@@ -180,8 +180,8 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
const classes = diagObj.db.getClasses();
|
||||
const keys = Object.keys(classes);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const classDef = classes[keys[i]];
|
||||
for (const key of keys) {
|
||||
const classDef = classes[key];
|
||||
const node = svgDraw.drawClass(diagram, classDef, conf, diagObj);
|
||||
idCache[node.id] = node;
|
||||
|
||||
@@ -238,9 +238,9 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
}
|
||||
});
|
||||
|
||||
dagre.layout(g);
|
||||
dagreLayout(g);
|
||||
g.nodes().forEach(function (v) {
|
||||
if (typeof v !== 'undefined' && typeof g.node(v) !== 'undefined') {
|
||||
if (v !== undefined && g.node(v) !== undefined) {
|
||||
log.debug('Node ' + v + ': ' + JSON.stringify(g.node(v)));
|
||||
root
|
||||
.select('#' + (diagObj.db.lookUpDomId(v) || v))
|
||||
@@ -256,7 +256,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
});
|
||||
|
||||
g.edges().forEach(function (e) {
|
||||
if (typeof e !== 'undefined' && typeof g.edge(e) !== 'undefined') {
|
||||
if (e !== undefined && g.edge(e) !== undefined) {
|
||||
log.debug('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(g.edge(e)));
|
||||
svgDraw.drawEdge(diagram, g.edge(e), g.edge(e).relation, conf, diagObj);
|
||||
}
|
||||
|
@@ -148,6 +148,11 @@ g.classGroup line {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.classTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${options.textColor};
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
@@ -102,7 +102,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
|
||||
p2_card_y = cardinality_2_point.y;
|
||||
}
|
||||
|
||||
if (typeof relation.title !== 'undefined') {
|
||||
if (relation.title !== undefined) {
|
||||
const g = elem.append('g').attr('class', 'classLabel');
|
||||
const label = g
|
||||
.append('text')
|
||||
@@ -125,7 +125,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
|
||||
}
|
||||
|
||||
log.info('Rendering relation ' + JSON.stringify(relation));
|
||||
if (typeof relation.relationTitle1 !== 'undefined' && relation.relationTitle1 !== 'none') {
|
||||
if (relation.relationTitle1 !== undefined && relation.relationTitle1 !== 'none') {
|
||||
const g = elem.append('g').attr('class', 'cardinality');
|
||||
g.append('text')
|
||||
.attr('class', 'type1')
|
||||
@@ -135,7 +135,7 @@ export const drawEdge = function (elem, path, relation, conf, diagObj) {
|
||||
.attr('font-size', '6')
|
||||
.text(relation.relationTitle1);
|
||||
}
|
||||
if (typeof relation.relationTitle2 !== 'undefined' && relation.relationTitle2 !== 'none') {
|
||||
if (relation.relationTitle2 !== undefined && relation.relationTitle2 !== 'none') {
|
||||
const g = elem.append('g').attr('class', 'cardinality');
|
||||
g.append('text')
|
||||
.attr('class', 'type2')
|
||||
@@ -355,8 +355,8 @@ export const drawNote = function (elem, note, conf, diagObj) {
|
||||
};
|
||||
|
||||
export const parseMember = function (text) {
|
||||
const fieldRegEx = /^(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+) *(\*|\$)?$/;
|
||||
const methodRegEx = /^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/;
|
||||
const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/;
|
||||
const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/;
|
||||
|
||||
let fieldMatch = text.match(fieldRegEx);
|
||||
let methodMatch = text.match(methodRegEx);
|
||||
@@ -420,7 +420,6 @@ const buildLegacyDisplay = function (text) {
|
||||
// if for some reason we don't have any match, use old format to parse text
|
||||
let displayText = '';
|
||||
let cssStyle = '';
|
||||
let memberText = '';
|
||||
let returnType = '';
|
||||
let methodStart = text.indexOf('(');
|
||||
let methodEnd = text.indexOf(')');
|
||||
@@ -433,7 +432,7 @@ const buildLegacyDisplay = function (text) {
|
||||
if (firstChar.match(/\w/)) {
|
||||
methodName = text.substring(0, methodStart).trim();
|
||||
} else {
|
||||
if (firstChar.match(/\+|-|~|#/)) {
|
||||
if (firstChar.match(/[#+~-]/)) {
|
||||
visibility = firstChar;
|
||||
}
|
||||
|
||||
|
@@ -152,7 +152,7 @@ export const evaluate = (val?: string | boolean): boolean =>
|
||||
export const parseGenericTypes = function (text: string): string {
|
||||
let cleanedText = text;
|
||||
|
||||
if (text.indexOf('~') !== -1) {
|
||||
if (text.includes('~')) {
|
||||
cleanedText = cleanedText.replace(/~([^~].*)/, '<$1');
|
||||
cleanedText = cleanedText.replace(/~([^~]*)$/, '>$1');
|
||||
|
||||
|
@@ -8,6 +8,8 @@ import {
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
} from '../../commonDb';
|
||||
|
||||
let entities = {};
|
||||
@@ -30,7 +32,7 @@ export const parseDirective = function (statement, context, type) {
|
||||
};
|
||||
|
||||
const addEntity = function (name) {
|
||||
if (typeof entities[name] === 'undefined') {
|
||||
if (entities[name] === undefined) {
|
||||
entities[name] = { attributes: [] };
|
||||
log.info('Added new entity :', name);
|
||||
}
|
||||
@@ -94,4 +96,6 @@ export default {
|
||||
getAccTitle,
|
||||
setAccDescription,
|
||||
getAccDescription,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
};
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import graphlib from 'graphlib';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
import { line, curveBasis, select } from 'd3';
|
||||
import dagre from 'dagre';
|
||||
import { layout as dagreLayout } from 'dagre-d3-es/src/dagre/index.js';
|
||||
import { getConfig } from '../../config';
|
||||
import { log } from '../../logger';
|
||||
import utils from '../../utils';
|
||||
import erMarkers from './erMarkers';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox';
|
||||
import addSVGAccessibilityFields from '../../accessibility';
|
||||
@@ -10,7 +11,7 @@ import { parseGenericTypes } from '../common/common';
|
||||
import { v4 as uuid4 } from 'uuid';
|
||||
|
||||
/** Regex used to remove chars from the entity name so the result can be used in an id */
|
||||
const BAD_ID_CHARS_REGEXP = /[^A-Za-z0-9]([\W])*/g;
|
||||
const BAD_ID_CHARS_REGEXP = /[^\dA-Za-z](\W)*/g;
|
||||
|
||||
// Configuration
|
||||
let conf = {};
|
||||
@@ -27,8 +28,8 @@ let entityNameIds = new Map();
|
||||
*/
|
||||
export const setConf = function (cnf) {
|
||||
const keys = Object.keys(cnf);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
conf[keys[i]] = cnf[keys[i]];
|
||||
for (const key of keys) {
|
||||
conf[key] = cnf[key];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -210,9 +211,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
const typeRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.tn.node().id)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', 0)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxTypeWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@@ -230,9 +228,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
const nameRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.nn.node().id)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', nameXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxNameWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@@ -252,9 +247,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
const keyTypeRect = groupNode
|
||||
.insert('rect', '#' + attributeNode.kn.node().id)
|
||||
.classed(`er ${attribStyle}`, true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', keyTypeAndCommentXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxKeyWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@@ -275,9 +267,6 @@ const drawAttributes = (groupNode, entityTextNode, attributes) => {
|
||||
groupNode
|
||||
.insert('rect', '#' + attributeNode.cn.node().id)
|
||||
.classed(`er ${attribStyle}`, 'true')
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', keyTypeAndCommentXOffset)
|
||||
.attr('y', heightOffset)
|
||||
.attr('width', maxCommentWidth + widthPadding * 2 + spareColumnWidth)
|
||||
@@ -347,9 +336,6 @@ const drawEntities = function (svgNode, entities, graph) {
|
||||
const rectNode = groupNode
|
||||
.insert('rect', '#' + textId)
|
||||
.classed('er entityBox', true)
|
||||
.style('fill', conf.fill)
|
||||
.style('fill-opacity', '100%')
|
||||
.style('stroke', conf.stroke)
|
||||
.attr('x', 0)
|
||||
.attr('y', 0)
|
||||
.attr('width', entityWidth)
|
||||
@@ -370,7 +356,7 @@ const drawEntities = function (svgNode, entities, graph) {
|
||||
|
||||
const adjustEntities = function (svgNode, graph) {
|
||||
graph.nodes().forEach(function (v) {
|
||||
if (typeof v !== 'undefined' && typeof graph.node(v) !== 'undefined') {
|
||||
if (v !== undefined && graph.node(v) !== undefined) {
|
||||
svgNode
|
||||
.select('#' + v)
|
||||
.attr(
|
||||
@@ -401,7 +387,7 @@ const getEdgeName = function (rel) {
|
||||
* Add each relationship to the graph
|
||||
*
|
||||
* @param relationships The relationships to be added
|
||||
* @param {Graph} g The graph
|
||||
* @param g The graph
|
||||
* @returns {Array} The array of relationships
|
||||
*/
|
||||
const addRelationships = function (relationships, g) {
|
||||
@@ -547,9 +533,7 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) {
|
||||
.attr('x', labelPoint.x - labelBBox.width / 2)
|
||||
.attr('y', labelPoint.y - labelBBox.height / 2)
|
||||
.attr('width', labelBBox.width)
|
||||
.attr('height', labelBBox.height)
|
||||
.style('fill', 'white')
|
||||
.style('fill-opacity', '85%');
|
||||
.attr('height', labelBBox.height);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -637,7 +621,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
// Add all the relationships to the graph
|
||||
const relationships = addRelationships(diagObj.db.getRelationships(), g);
|
||||
|
||||
dagre.layout(g); // Node and edge positions will be updated
|
||||
dagreLayout(g); // Node and edge positions will be updated
|
||||
|
||||
// Adjust the positions of the entities so that they adhere to the layout
|
||||
adjustEntities(svg, g);
|
||||
@@ -649,6 +633,8 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
|
||||
const padding = conf.diagramPadding;
|
||||
|
||||
utils.insertTitle(svg, 'entityTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
|
||||
|
||||
const svgBounds = svg.node().getBBox();
|
||||
const width = svgBounds.width + padding * 2;
|
||||
const height = svgBounds.height + padding * 2;
|
||||
@@ -666,10 +652,8 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
* Although the official XML standard for ids says that many more characters are valid in the id,
|
||||
* this keeps things simple by accepting only A-Za-z0-9.
|
||||
*
|
||||
* @param {string} [str?=''] Given string to use as the basis for the id. Default is `''`
|
||||
* @param {string} [prefix?=''] String to put at the start, followed by '-'. Default is `''`
|
||||
* @param str
|
||||
* @param prefix
|
||||
* @param {string} str Given string to use as the basis for the id. Default is `''`
|
||||
* @param {string} prefix String to put at the start, followed by '-'. Default is `''`
|
||||
* @returns {string}
|
||||
* @see https://www.w3.org/TR/xml/#NT-Name
|
||||
*/
|
||||
|
@@ -27,6 +27,12 @@ const getStyles = (options) =>
|
||||
.relationshipLine {
|
||||
stroke: ${options.lineColor};
|
||||
}
|
||||
|
||||
.entityTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${options.textColor};
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import dagreD3 from 'dagre-d3';
|
||||
import { intersectPolygon } from 'dagre-d3-es/src/dagre-js/intersect/intersect-polygon.js';
|
||||
import { intersectRect } from 'dagre-d3-es/src/dagre-js/intersect/intersect-rect.js';
|
||||
|
||||
/**
|
||||
* @param parent
|
||||
@@ -17,7 +18,7 @@ function question(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, s, s, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -42,7 +43,7 @@ function hexagon(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -64,7 +65,7 @@ function rect_left_inv_arrow(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -85,7 +86,7 @@ function lean_right(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -106,7 +107,7 @@ function lean_left(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -127,7 +128,7 @@ function trapezoid(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -148,7 +149,7 @@ function inv_trapezoid(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -170,7 +171,7 @@ function rect_right_inv_arrow(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -194,7 +195,7 @@ function stadium(parent, bbox, node) {
|
||||
.attr('height', h);
|
||||
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.rect(node, point);
|
||||
return intersectRect(node, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -221,7 +222,7 @@ function subroutine(parent, bbox, node) {
|
||||
];
|
||||
const shapeSvg = insertPolygonShape(parent, w, h, points);
|
||||
node.intersect = function (point) {
|
||||
return dagreD3.intersect.polygon(node, points, point);
|
||||
return intersectPolygon(node, points, point);
|
||||
};
|
||||
return shapeSvg;
|
||||
}
|
||||
@@ -270,7 +271,7 @@ function cylinder(parent, bbox, node) {
|
||||
.attr('transform', 'translate(' + -w / 2 + ',' + -(h / 2 + ry) + ')');
|
||||
|
||||
node.intersect = function (point) {
|
||||
const pos = dagreD3.intersect.rect(node, point);
|
||||
const pos = intersectRect(node, point);
|
||||
const x = pos.x - node.x;
|
||||
|
||||
if (
|
||||
|
@@ -10,6 +10,8 @@ import {
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
} from '../../commonDb';
|
||||
|
||||
const MERMAID_DOM_ID_PREFIX = 'flowchart-';
|
||||
@@ -44,9 +46,9 @@ export const parseDirective = function (statement, context, type) {
|
||||
*/
|
||||
export const lookUpDomId = function (id) {
|
||||
const veritceKeys = Object.keys(vertices);
|
||||
for (let i = 0; i < veritceKeys.length; i++) {
|
||||
if (vertices[veritceKeys[i]].id === id) {
|
||||
return vertices[veritceKeys[i]].domId;
|
||||
for (const veritceKey of veritceKeys) {
|
||||
if (vertices[veritceKey].id === id) {
|
||||
return vertices[veritceKey].domId;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
@@ -66,7 +68,7 @@ export const lookUpDomId = function (id) {
|
||||
export const addVertex = function (_id, text, type, style, classes, dir, props = {}) {
|
||||
let txt;
|
||||
let id = _id;
|
||||
if (typeof id === 'undefined') {
|
||||
if (id === undefined) {
|
||||
return;
|
||||
}
|
||||
if (id.trim().length === 0) {
|
||||
@@ -75,7 +77,7 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
||||
|
||||
// if (id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
|
||||
if (typeof vertices[id] === 'undefined') {
|
||||
if (vertices[id] === undefined) {
|
||||
vertices[id] = {
|
||||
id: id,
|
||||
domId: MERMAID_DOM_ID_PREFIX + id + '-' + vertexCounter,
|
||||
@@ -84,7 +86,7 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
||||
};
|
||||
}
|
||||
vertexCounter++;
|
||||
if (typeof text !== 'undefined') {
|
||||
if (text !== undefined) {
|
||||
config = configApi.getConfig();
|
||||
txt = sanitizeText(text.trim());
|
||||
|
||||
@@ -95,33 +97,29 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
||||
|
||||
vertices[id].text = txt;
|
||||
} else {
|
||||
if (typeof vertices[id].text === 'undefined') {
|
||||
if (vertices[id].text === undefined) {
|
||||
vertices[id].text = _id;
|
||||
}
|
||||
}
|
||||
if (typeof type !== 'undefined') {
|
||||
if (type !== undefined) {
|
||||
vertices[id].type = type;
|
||||
}
|
||||
if (typeof style !== 'undefined') {
|
||||
if (style !== null) {
|
||||
style.forEach(function (s) {
|
||||
vertices[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
if (style !== undefined && style !== null) {
|
||||
style.forEach(function (s) {
|
||||
vertices[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
if (typeof classes !== 'undefined') {
|
||||
if (classes !== null) {
|
||||
classes.forEach(function (s) {
|
||||
vertices[id].classes.push(s);
|
||||
});
|
||||
}
|
||||
if (classes !== undefined && classes !== null) {
|
||||
classes.forEach(function (s) {
|
||||
vertices[id].classes.push(s);
|
||||
});
|
||||
}
|
||||
if (typeof dir !== 'undefined') {
|
||||
if (dir !== undefined) {
|
||||
vertices[id].dir = dir;
|
||||
}
|
||||
if (typeof vertices[id].props === 'undefined') {
|
||||
if (vertices[id].props === undefined) {
|
||||
vertices[id].props = props;
|
||||
} else if (typeof props !== 'undefined') {
|
||||
} else if (props !== undefined) {
|
||||
Object.assign(vertices[id].props, props);
|
||||
}
|
||||
};
|
||||
@@ -132,9 +130,9 @@ export const addVertex = function (_id, text, type, style, classes, dir, props =
|
||||
* @param _start
|
||||
* @param _end
|
||||
* @param type
|
||||
* @param linktext
|
||||
* @param linkText
|
||||
*/
|
||||
export const addSingleLink = function (_start, _end, type, linktext) {
|
||||
export const addSingleLink = function (_start, _end, type, linkText) {
|
||||
let start = _start;
|
||||
let end = _end;
|
||||
// if (start[0].match(/\d/)) start = MERMAID_DOM_ID_PREFIX + start;
|
||||
@@ -142,18 +140,18 @@ export const addSingleLink = function (_start, _end, type, linktext) {
|
||||
// log.info('Got edge...', start, end);
|
||||
|
||||
const edge = { start: start, end: end, type: undefined, text: '' };
|
||||
linktext = type.text;
|
||||
linkText = type.text;
|
||||
|
||||
if (typeof linktext !== 'undefined') {
|
||||
edge.text = sanitizeText(linktext.trim());
|
||||
if (linkText !== undefined) {
|
||||
edge.text = sanitizeText(linkText.trim());
|
||||
|
||||
// strip quotes if string starts and exnds with a quote
|
||||
// strip quotes if string starts and ends with a quote
|
||||
if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') {
|
||||
edge.text = edge.text.substring(1, edge.text.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof type !== 'undefined') {
|
||||
if (type !== undefined) {
|
||||
edge.type = type.type;
|
||||
edge.stroke = type.stroke;
|
||||
edge.length = type.length;
|
||||
@@ -205,21 +203,19 @@ export const updateLink = function (positions, style) {
|
||||
};
|
||||
|
||||
export const addClass = function (id, style) {
|
||||
if (typeof classes[id] === 'undefined') {
|
||||
if (classes[id] === undefined) {
|
||||
classes[id] = { id: id, styles: [], textStyles: [] };
|
||||
}
|
||||
|
||||
if (typeof style !== 'undefined') {
|
||||
if (style !== null) {
|
||||
style.forEach(function (s) {
|
||||
if (s.match('color')) {
|
||||
const newStyle1 = s.replace('fill', 'bgFill');
|
||||
const newStyle2 = newStyle1.replace('color', 'fill');
|
||||
classes[id].textStyles.push(newStyle2);
|
||||
}
|
||||
classes[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
if (style !== undefined && style !== null) {
|
||||
style.forEach(function (s) {
|
||||
if (s.match('color')) {
|
||||
const newStyle1 = s.replace('fill', 'bgFill');
|
||||
const newStyle2 = newStyle1.replace('color', 'fill');
|
||||
classes[id].textStyles.push(newStyle2);
|
||||
}
|
||||
classes[id].styles.push(s);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -255,11 +251,11 @@ export const setClass = function (ids, className) {
|
||||
// let id = version === 'gen-2' ? lookUpDomId(_id) : _id;
|
||||
let id = _id;
|
||||
// if (_id[0].match(/\d/)) id = MERMAID_DOM_ID_PREFIX + id;
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
if (vertices[id] !== undefined) {
|
||||
vertices[id].classes.push(className);
|
||||
}
|
||||
|
||||
if (typeof subGraphLookup[id] !== 'undefined') {
|
||||
if (subGraphLookup[id] !== undefined) {
|
||||
subGraphLookup[id].classes.push(className);
|
||||
}
|
||||
});
|
||||
@@ -267,7 +263,7 @@ export const setClass = function (ids, className) {
|
||||
|
||||
const setTooltip = function (ids, tooltip) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof tooltip !== 'undefined') {
|
||||
if (tooltip !== undefined) {
|
||||
tooltips[version === 'gen-1' ? lookUpDomId(id) : id] = sanitizeText(tooltip);
|
||||
}
|
||||
});
|
||||
@@ -279,7 +275,7 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
if (configApi.getConfig().securityLevel !== 'loose') {
|
||||
return;
|
||||
}
|
||||
if (typeof functionName === 'undefined') {
|
||||
if (functionName === undefined) {
|
||||
return;
|
||||
}
|
||||
let argList = [];
|
||||
@@ -302,7 +298,7 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
argList.push(id);
|
||||
}
|
||||
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
if (vertices[id] !== undefined) {
|
||||
vertices[id].haveCallback = true;
|
||||
funs.push(function () {
|
||||
const elem = document.querySelector(`[id="${domId}"]`);
|
||||
@@ -328,7 +324,7 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
*/
|
||||
export const setLink = function (ids, linkStr, target) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
if (typeof vertices[id] !== 'undefined') {
|
||||
if (vertices[id] !== undefined) {
|
||||
vertices[id].link = utils.formatUrl(linkStr, config);
|
||||
vertices[id].linkTarget = target;
|
||||
}
|
||||
@@ -482,7 +478,7 @@ export const addSubGraph = function (_id, list, _title) {
|
||||
if (type in prims) {
|
||||
return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
|
||||
} else {
|
||||
return objs.indexOf(item) >= 0 ? false : objs.push(item);
|
||||
return objs.includes(item) ? false : objs.push(item);
|
||||
}
|
||||
});
|
||||
return { nodeList, dir };
|
||||
@@ -528,8 +524,8 @@ export const addSubGraph = function (_id, list, _title) {
|
||||
};
|
||||
|
||||
const getPosForId = function (id) {
|
||||
for (let i = 0; i < subGraphs.length; i++) {
|
||||
if (subGraphs[i].id === id) {
|
||||
for (const [i, subGraph] of subGraphs.entries()) {
|
||||
if (subGraph.id === id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -620,11 +616,11 @@ const destructStartLink = (_str) => {
|
||||
|
||||
let stroke = 'normal';
|
||||
|
||||
if (str.indexOf('=') !== -1) {
|
||||
if (str.includes('=')) {
|
||||
stroke = 'thick';
|
||||
}
|
||||
|
||||
if (str.indexOf('.') !== -1) {
|
||||
if (str.includes('.')) {
|
||||
stroke = 'dotted';
|
||||
}
|
||||
|
||||
@@ -785,4 +781,6 @@ export default {
|
||||
},
|
||||
exists,
|
||||
makeUniq,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
};
|
||||
|
@@ -1,11 +1,12 @@
|
||||
import graphlib from 'graphlib';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
import { select, curveLinear, selectAll } from 'd3';
|
||||
|
||||
import flowDb from './flowDb';
|
||||
import { getConfig } from '../../config';
|
||||
import utils from '../../utils';
|
||||
|
||||
import { render } from '../../dagre-wrapper/index.js';
|
||||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||
import { log } from '../../logger';
|
||||
import common, { evaluate } from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
@@ -15,8 +16,8 @@ import addSVGAccessibilityFields from '../../accessibility';
|
||||
const conf = {};
|
||||
export const setConf = function (cnf) {
|
||||
const keys = Object.keys(cnf);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
conf[keys[i]] = cnf[keys[i]];
|
||||
for (const key of keys) {
|
||||
conf[key] = cnf[key];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,7 +60,7 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = {
|
||||
label: vertexText.replace(
|
||||
/fa[lrsb]?:fa-[\w-]+/g,
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
};
|
||||
@@ -71,12 +72,12 @@ export const addVertices = function (vert, g, svgId, root, doc, diagObj) {
|
||||
|
||||
const rows = vertexText.split(common.lineBreakRegex);
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
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 = rows[j];
|
||||
tspan.textContent = row;
|
||||
svgLabel.appendChild(tspan);
|
||||
}
|
||||
vertexNode = svgLabel;
|
||||
@@ -197,7 +198,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
let defaultStyle;
|
||||
let defaultLabelStyle;
|
||||
|
||||
if (typeof edges.defaultStyle !== 'undefined') {
|
||||
if (edges.defaultStyle !== undefined) {
|
||||
const defaultStyles = getStylesFromArray(edges.defaultStyle);
|
||||
defaultStyle = defaultStyles.style;
|
||||
defaultLabelStyle = defaultStyles.labelStyle;
|
||||
@@ -209,7 +210,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
// Identify Link
|
||||
var linkIdBase = 'L-' + edge.start + '-' + edge.end;
|
||||
// count the links from+to the same node to give unique id
|
||||
if (typeof linkIdCnt[linkIdBase] === 'undefined') {
|
||||
if (linkIdCnt[linkIdBase] === undefined) {
|
||||
linkIdCnt[linkIdBase] = 0;
|
||||
log.info('abc78 new entry', linkIdBase, linkIdCnt[linkIdBase]);
|
||||
} else {
|
||||
@@ -261,10 +262,10 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
switch (edge.stroke) {
|
||||
case 'normal':
|
||||
style = 'fill:none;';
|
||||
if (typeof defaultStyle !== 'undefined') {
|
||||
if (defaultStyle !== undefined) {
|
||||
style = defaultStyle;
|
||||
}
|
||||
if (typeof defaultLabelStyle !== 'undefined') {
|
||||
if (defaultLabelStyle !== undefined) {
|
||||
labelStyle = defaultLabelStyle;
|
||||
}
|
||||
edgeData.thickness = 'normal';
|
||||
@@ -281,7 +282,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.style = 'stroke-width: 3.5px;fill:none;';
|
||||
break;
|
||||
}
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.style !== undefined) {
|
||||
const styles = getStylesFromArray(edge.style);
|
||||
style = styles.style;
|
||||
labelStyle = styles.labelStyle;
|
||||
@@ -290,16 +291,16 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.style = edgeData.style += style;
|
||||
edgeData.labelStyle = edgeData.labelStyle += labelStyle;
|
||||
|
||||
if (typeof edge.interpolate !== 'undefined') {
|
||||
if (edge.interpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
|
||||
} else if (typeof edges.defaultInterpolate !== 'undefined') {
|
||||
} else if (edges.defaultInterpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
|
||||
} else {
|
||||
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
|
||||
}
|
||||
|
||||
if (typeof edge.text === 'undefined') {
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.text === undefined) {
|
||||
if (edge.style !== undefined) {
|
||||
edgeData.arrowheadStyle = 'fill: #333';
|
||||
}
|
||||
} else {
|
||||
@@ -310,7 +311,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.labelType = 'text';
|
||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||
|
||||
if (typeof edge.style === 'undefined') {
|
||||
if (edge.style === undefined) {
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none;';
|
||||
}
|
||||
|
||||
@@ -359,7 +360,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = diagObj.db.getDirection();
|
||||
if (typeof dir === 'undefined') {
|
||||
if (dir === undefined) {
|
||||
dir = 'TD';
|
||||
}
|
||||
|
||||
@@ -437,6 +438,8 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
const element = root.select('#' + id + ' g');
|
||||
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
|
||||
|
||||
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
|
||||
|
||||
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
|
||||
|
||||
// Index nodes
|
||||
@@ -445,9 +448,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
||||
for (let k = 0; k < labels.length; k++) {
|
||||
const label = labels[k];
|
||||
|
||||
for (const label of labels) {
|
||||
// Get dimensions of label
|
||||
const dim = label.getBBox();
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import graphlib from 'graphlib';
|
||||
import * as graphlib from 'dagre-d3-es/src/graphlib';
|
||||
import { select, curveLinear, selectAll } from 'd3';
|
||||
import { getConfig } from '../../config';
|
||||
import dagreD3 from 'dagre-d3';
|
||||
import addHtmlLabel from 'dagre-d3/lib/label/add-html-label.js';
|
||||
import { render as Render } from 'dagre-d3-es';
|
||||
import { applyStyle } from 'dagre-d3-es/src/dagre-js/util.js';
|
||||
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
|
||||
import { log } from '../../logger';
|
||||
import common, { evaluate } from '../common/common';
|
||||
import { interpolateToCurve, getStylesFromArray } from '../../utils';
|
||||
@@ -13,8 +14,8 @@ import addSVGAccessibilityFields from '../../accessibility';
|
||||
const conf = {};
|
||||
export const setConf = function (cnf) {
|
||||
const keys = Object.keys(cnf);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
conf[keys[i]] = cnf[keys[i]];
|
||||
for (const key of keys) {
|
||||
conf[key] = cnf[key];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,7 +59,7 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
|
||||
const node = {
|
||||
label: vertexText.replace(
|
||||
/fa[lrsb]?:fa-[\w-]+/g,
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
),
|
||||
};
|
||||
@@ -70,12 +71,12 @@ export const addVertices = function (vert, g, svgId, root, _doc, diagObj) {
|
||||
|
||||
const rows = vertexText.split(common.lineBreakRegex);
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
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 = rows[j];
|
||||
tspan.textContent = row;
|
||||
svgLabel.appendChild(tspan);
|
||||
}
|
||||
vertexNode = svgLabel;
|
||||
@@ -166,7 +167,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
let defaultStyle;
|
||||
let defaultLabelStyle;
|
||||
|
||||
if (typeof edges.defaultStyle !== 'undefined') {
|
||||
if (edges.defaultStyle !== undefined) {
|
||||
const defaultStyles = getStylesFromArray(edges.defaultStyle);
|
||||
defaultStyle = defaultStyles.style;
|
||||
defaultLabelStyle = defaultStyles.labelStyle;
|
||||
@@ -192,7 +193,7 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
let style = '';
|
||||
let labelStyle = '';
|
||||
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.style !== undefined) {
|
||||
const styles = getStylesFromArray(edge.style);
|
||||
style = styles.style;
|
||||
labelStyle = styles.labelStyle;
|
||||
@@ -200,10 +201,10 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
switch (edge.stroke) {
|
||||
case 'normal':
|
||||
style = 'fill:none';
|
||||
if (typeof defaultStyle !== 'undefined') {
|
||||
if (defaultStyle !== undefined) {
|
||||
style = defaultStyle;
|
||||
}
|
||||
if (typeof defaultLabelStyle !== 'undefined') {
|
||||
if (defaultLabelStyle !== undefined) {
|
||||
labelStyle = defaultLabelStyle;
|
||||
}
|
||||
break;
|
||||
@@ -219,16 +220,16 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.style = style;
|
||||
edgeData.labelStyle = labelStyle;
|
||||
|
||||
if (typeof edge.interpolate !== 'undefined') {
|
||||
if (edge.interpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(edge.interpolate, curveLinear);
|
||||
} else if (typeof edges.defaultInterpolate !== 'undefined') {
|
||||
} else if (edges.defaultInterpolate !== undefined) {
|
||||
edgeData.curve = interpolateToCurve(edges.defaultInterpolate, curveLinear);
|
||||
} else {
|
||||
edgeData.curve = interpolateToCurve(conf.curve, curveLinear);
|
||||
}
|
||||
|
||||
if (typeof edge.text === 'undefined') {
|
||||
if (typeof edge.style !== 'undefined') {
|
||||
if (edge.text === undefined) {
|
||||
if (edge.style !== undefined) {
|
||||
edgeData.arrowheadStyle = 'fill: #333';
|
||||
}
|
||||
} else {
|
||||
@@ -240,14 +241,14 @@ export const addEdges = function (edges, g, diagObj) {
|
||||
edgeData.label = `<span id="L-${linkId}" class="edgeLabel L-${linkNameStart}' L-${linkNameEnd}" style="${
|
||||
edgeData.labelStyle
|
||||
}">${edge.text.replace(
|
||||
/fa[lrsb]?:fa-[\w-]+/g,
|
||||
/fa[blrs]?:fa-[\w-]+/g,
|
||||
(s) => `<i class='${s.replace(':', ' ')}'></i>`
|
||||
)}</span>`;
|
||||
} else {
|
||||
edgeData.labelType = 'text';
|
||||
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
|
||||
|
||||
if (typeof edge.style === 'undefined') {
|
||||
if (edge.style === undefined) {
|
||||
edgeData.style = edgeData.style || 'stroke: #333; stroke-width: 1.5px;fill:none';
|
||||
}
|
||||
|
||||
@@ -315,7 +316,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
|
||||
// Fetch the default direction, use TD if none was found
|
||||
let dir = diagObj.db.getDirection();
|
||||
if (typeof dir === 'undefined') {
|
||||
if (dir === undefined) {
|
||||
dir = 'TD';
|
||||
}
|
||||
const nodeSpacing = conf.nodeSpacing || 50;
|
||||
@@ -370,7 +371,6 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
addEdges(edges, g, diagObj);
|
||||
|
||||
// Create the renderer
|
||||
const Render = dagreD3.render;
|
||||
const render = new Render();
|
||||
|
||||
// Add custom shapes
|
||||
@@ -390,7 +390,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
.attr('orient', 'auto');
|
||||
|
||||
const path = marker.append('path').attr('d', 'M 0 0 L 0 0 L 0 0 z');
|
||||
dagreD3.util.applyStyle(path, edge[type + 'Style']);
|
||||
applyStyle(path, edge[type + 'Style']);
|
||||
};
|
||||
|
||||
// Override normal arrowhead defined in d3. Remove style & add class to allow css styling.
|
||||
@@ -459,9 +459,7 @@ export const draw = function (text, id, _version, diagObj) {
|
||||
// Add label rects for non html labels
|
||||
if (!conf.htmlLabels) {
|
||||
const labels = doc.querySelectorAll('[id="' + id + '"] .edgeLabel .label');
|
||||
for (let k = 0; k < labels.length; k++) {
|
||||
const label = labels[k];
|
||||
|
||||
for (const label of labels) {
|
||||
// Get dimensions of label
|
||||
const dim = label.getBBox();
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import flowDb from '../flowDb';
|
||||
import flow from './flow';
|
||||
import filter from 'lodash/filter';
|
||||
import filter from 'lodash-es/filter';
|
||||
import { setConfig } from '../../../config';
|
||||
|
||||
setConfig({
|
||||
@@ -79,8 +79,8 @@ describe('when parsing directions', function () {
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
|
||||
const subgraphA = subgraphs.find((o) => o.id === 'A');
|
||||
const subgraphB = subgraphs.find((o) => o.id === 'B');
|
||||
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphB.dir).toBe('LR');
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import flowDb from '../flowDb';
|
||||
import flow from './flow';
|
||||
import filter from 'lodash/filter';
|
||||
import filter from 'lodash-es/filter';
|
||||
import { setConfig } from '../../../config';
|
||||
|
||||
setConfig({
|
||||
@@ -254,8 +254,8 @@ describe('when parsing subgraphs', function () {
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
|
||||
const subgraphA = subgraphs.find((o) => o.id === 'A');
|
||||
const subgraphB = subgraphs.find((o) => o.id === 'B');
|
||||
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
@@ -279,8 +279,8 @@ describe('when parsing subgraphs', function () {
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
|
||||
const subgraphA = subgraphs.find((o) => o.id === 'A');
|
||||
const subgraphB = subgraphs.find((o) => o.id === 'B');
|
||||
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
@@ -302,8 +302,8 @@ describe('when parsing subgraphs', function () {
|
||||
const subgraphs = flow.parser.yy.getSubGraphs();
|
||||
expect(subgraphs.length).toBe(2);
|
||||
|
||||
const subgraphA = filter(subgraphs, (o) => o.id === 'A')[0];
|
||||
const subgraphB = filter(subgraphs, (o) => o.id === 'B')[0];
|
||||
const subgraphA = subgraphs.find((o) => o.id === 'A');
|
||||
const subgraphB = subgraphs.find((o) => o.id === 'B');
|
||||
expect(subgraphB.nodes[0]).toBe('c');
|
||||
expect(subgraphA.nodes).toContain('B');
|
||||
expect(subgraphA.nodes).toContain('b');
|
||||
|
@@ -103,6 +103,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
||||
pointer-events: none;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.flowchartTitleText {
|
||||
text-anchor: middle;
|
||||
font-size: 18px;
|
||||
fill: ${options.textColor};
|
||||
}
|
||||
`;
|
||||
|
||||
export default getStyles;
|
||||
|
@@ -150,16 +150,16 @@ export const getTasks = function () {
|
||||
};
|
||||
|
||||
export const isInvalidDate = function (date, dateFormat, excludes, includes) {
|
||||
if (includes.indexOf(date.format(dateFormat.trim())) >= 0) {
|
||||
if (includes.includes(date.format(dateFormat.trim()))) {
|
||||
return false;
|
||||
}
|
||||
if (date.isoWeekday() >= 6 && excludes.indexOf('weekends') >= 0) {
|
||||
if (date.isoWeekday() >= 6 && excludes.includes('weekends')) {
|
||||
return true;
|
||||
}
|
||||
if (excludes.indexOf(date.format('dddd').toLowerCase()) >= 0) {
|
||||
if (excludes.includes(date.format('dddd').toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
return excludes.indexOf(date.format(dateFormat.trim())) >= 0;
|
||||
return excludes.includes(date.format(dateFormat.trim()));
|
||||
};
|
||||
|
||||
const checkTaskDates = function (task, dateFormat, excludes, includes) {
|
||||
@@ -202,7 +202,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
|
||||
let latestEndingTask = null;
|
||||
afterStatement[1].split(' ').forEach(function (id) {
|
||||
let task = findTaskById(id);
|
||||
if (typeof task !== 'undefined') {
|
||||
if (task !== undefined) {
|
||||
if (!latestEndingTask) {
|
||||
latestEndingTask = task;
|
||||
} else {
|
||||
@@ -230,7 +230,7 @@ const getStartDate = function (prevTime, dateFormat, str) {
|
||||
log.debug('Invalid date:' + str);
|
||||
log.debug('With date format:' + dateFormat.trim());
|
||||
const d = new Date(str);
|
||||
if (typeof d === 'undefined' || isNaN(d.getTime())) {
|
||||
if (d === undefined || isNaN(d.getTime())) {
|
||||
throw new Error('Invalid date:' + str);
|
||||
}
|
||||
return d;
|
||||
@@ -258,15 +258,14 @@ const getStartDate = function (prevTime, dateFormat, str) {
|
||||
* string.
|
||||
*/
|
||||
const parseDuration = function (str) {
|
||||
const statement = /^(\d+(?:\.\d+)?)([yMwdhms]|ms)$/.exec(str.trim());
|
||||
const statement = /^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(str.trim());
|
||||
if (statement !== null) {
|
||||
return moment.duration(Number.parseFloat(statement[1]), statement[2]);
|
||||
}
|
||||
return moment.duration.invalid();
|
||||
};
|
||||
|
||||
const getEndDate = function (prevTime, dateFormat, str, inclusive) {
|
||||
inclusive = inclusive || false;
|
||||
const getEndDate = function (prevTime, dateFormat, str, inclusive = false) {
|
||||
str = str.trim();
|
||||
|
||||
// Check for actual date
|
||||
@@ -288,7 +287,7 @@ const getEndDate = function (prevTime, dateFormat, str, inclusive) {
|
||||
|
||||
let taskCnt = 0;
|
||||
const parseId = function (idStr) {
|
||||
if (typeof idStr === 'undefined') {
|
||||
if (idStr === undefined) {
|
||||
taskCnt = taskCnt + 1;
|
||||
return 'task' + taskCnt;
|
||||
}
|
||||
@@ -510,10 +509,10 @@ const compileTasks = function () {
|
||||
};
|
||||
|
||||
let allProcessed = true;
|
||||
for (let i = 0; i < rawTasks.length; i++) {
|
||||
for (const [i, rawTask] of rawTasks.entries()) {
|
||||
compileTask(i);
|
||||
|
||||
allProcessed = allProcessed && rawTasks[i].processed;
|
||||
allProcessed = allProcessed && rawTask.processed;
|
||||
}
|
||||
return allProcessed;
|
||||
};
|
||||
@@ -531,7 +530,7 @@ export const setLink = function (ids, _linkStr) {
|
||||
}
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id);
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
if (rawTask !== undefined) {
|
||||
pushFun(id, () => {
|
||||
window.open(linkStr, '_self');
|
||||
});
|
||||
@@ -550,7 +549,7 @@ export const setLink = function (ids, _linkStr) {
|
||||
export const setClass = function (ids, className) {
|
||||
ids.split(',').forEach(function (id) {
|
||||
let rawTask = findTaskById(id);
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
if (rawTask !== undefined) {
|
||||
rawTask.classes.push(className);
|
||||
}
|
||||
});
|
||||
@@ -560,7 +559,7 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
if (configApi.getConfig().securityLevel !== 'loose') {
|
||||
return;
|
||||
}
|
||||
if (typeof functionName === 'undefined') {
|
||||
if (functionName === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -585,7 +584,7 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
}
|
||||
|
||||
let rawTask = findTaskById(id);
|
||||
if (typeof rawTask !== 'undefined') {
|
||||
if (rawTask !== undefined) {
|
||||
pushFun(id, () => {
|
||||
utils.runFunc(functionName, ...argList);
|
||||
});
|
||||
@@ -600,24 +599,26 @@ const setClickFun = function (id, functionName, functionArgs) {
|
||||
* @param callbackFunction A function to be executed when clicked on the task or the task's text
|
||||
*/
|
||||
const pushFun = function (id, callbackFunction) {
|
||||
funs.push(function () {
|
||||
// const elem = d3.select(element).select(`[id="${id}"]`)
|
||||
const elem = document.querySelector(`[id="${id}"]`);
|
||||
if (elem !== null) {
|
||||
elem.addEventListener('click', function () {
|
||||
callbackFunction();
|
||||
});
|
||||
funs.push(
|
||||
function () {
|
||||
// const elem = d3.select(element).select(`[id="${id}"]`)
|
||||
const elem = document.querySelector(`[id="${id}"]`);
|
||||
if (elem !== null) {
|
||||
elem.addEventListener('click', function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
},
|
||||
function () {
|
||||
// const elem = d3.select(element).select(`[id="${id}-text"]`)
|
||||
const elem = document.querySelector(`[id="${id}-text"]`);
|
||||
if (elem !== null) {
|
||||
elem.addEventListener('click', function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
funs.push(function () {
|
||||
// const elem = d3.select(element).select(`[id="${id}-text"]`)
|
||||
const elem = document.querySelector(`[id="${id}-text"]`);
|
||||
if (elem !== null) {
|
||||
elem.addEventListener('click', function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -46,11 +46,11 @@ export const draw = function (text, id, version, diagObj) {
|
||||
const elem = doc.getElementById(id);
|
||||
w = elem.parentElement.offsetWidth;
|
||||
|
||||
if (typeof w === 'undefined') {
|
||||
if (w === undefined) {
|
||||
w = 1200;
|
||||
}
|
||||
|
||||
if (typeof conf.useWidth !== 'undefined') {
|
||||
if (conf.useWidth !== undefined) {
|
||||
w = conf.useWidth;
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
|
||||
let categories = [];
|
||||
|
||||
for (let i = 0; i < taskArray.length; i++) {
|
||||
categories.push(taskArray[i].type);
|
||||
for (const element of taskArray) {
|
||||
categories.push(element.type);
|
||||
}
|
||||
|
||||
const catsUnfiltered = categories; // for vert labels
|
||||
@@ -178,8 +178,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
})
|
||||
.attr('height', theGap)
|
||||
.attr('class', function (d) {
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
for (const [i, category] of categories.entries()) {
|
||||
if (d.type === category) {
|
||||
return 'section section' + (i % conf.numberSectionStyles);
|
||||
}
|
||||
}
|
||||
@@ -247,8 +247,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
}
|
||||
|
||||
let secNum = 0;
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
for (const [i, category] of categories.entries()) {
|
||||
if (d.type === category) {
|
||||
secNum = i % conf.numberSectionStyles;
|
||||
}
|
||||
}
|
||||
@@ -339,8 +339,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
}
|
||||
|
||||
let secNum = 0;
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d.type === categories[i]) {
|
||||
for (const [i, category] of categories.entries()) {
|
||||
if (d.type === category) {
|
||||
secNum = i % conf.numberSectionStyles;
|
||||
}
|
||||
}
|
||||
@@ -400,7 +400,7 @@ export const draw = function (text, id, version, diagObj) {
|
||||
|
||||
rectangles
|
||||
.filter(function (d) {
|
||||
return typeof links[d.id] !== 'undefined';
|
||||
return links[d.id] !== undefined;
|
||||
})
|
||||
.each(function (o) {
|
||||
var taskRect = doc.querySelector('#' + o.id);
|
||||
@@ -500,7 +500,7 @@ export const draw = function (text, id, version, diagObj) {
|
||||
.tickSize(-h + theTopPad + conf.gridLineStartPadding)
|
||||
.tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'));
|
||||
|
||||
const reTickInterval = /^([1-9][0-9]*)(minute|hour|day|week|month)$/;
|
||||
const reTickInterval = /^([1-9]\d*)(minute|hour|day|week|month)$/;
|
||||
const resultTickInterval = reTickInterval.exec(
|
||||
diagObj.db.getTickInterval() || conf.tickInterval
|
||||
);
|
||||
@@ -588,8 +588,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
const numOccurances = [];
|
||||
let prevGap = 0;
|
||||
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
numOccurances[i] = [categories[i], getCount(categories[i], catsUnfiltered)];
|
||||
for (const [i, category] of categories.entries()) {
|
||||
numOccurances[i] = [category, getCount(category, catsUnfiltered)];
|
||||
}
|
||||
|
||||
svg
|
||||
@@ -604,14 +604,14 @@ export const draw = function (text, id, version, diagObj) {
|
||||
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
svgLabel.setAttribute('dy', dy + 'em');
|
||||
|
||||
for (let j = 0; j < rows.length; j++) {
|
||||
for (const [j, row] of rows.entries()) {
|
||||
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
|
||||
tspan.setAttribute('alignment-baseline', 'central');
|
||||
tspan.setAttribute('x', '10');
|
||||
if (j > 0) {
|
||||
tspan.setAttribute('dy', '1em');
|
||||
}
|
||||
tspan.textContent = rows[j];
|
||||
tspan.textContent = row;
|
||||
svgLabel.appendChild(tspan);
|
||||
}
|
||||
return svgLabel;
|
||||
@@ -630,8 +630,8 @@ export const draw = function (text, id, version, diagObj) {
|
||||
.attr('font-size', conf.sectionFontSize)
|
||||
.attr('font-size', conf.sectionFontSize)
|
||||
.attr('class', function (d) {
|
||||
for (let i = 0; i < categories.length; i++) {
|
||||
if (d[0] === categories[i]) {
|
||||
for (const [i, category] of categories.entries()) {
|
||||
if (d[0] === category) {
|
||||
return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles);
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ import {
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
} from '../../commonDb';
|
||||
|
||||
let mainBranchName = getConfig().gitGraph.mainBranchName;
|
||||
@@ -129,7 +131,7 @@ export const commit = function (msg, id, type, tag) {
|
||||
|
||||
export const branch = function (name, order) {
|
||||
name = common.sanitizeText(name, configApi.getConfig());
|
||||
if (typeof branches[name] === 'undefined') {
|
||||
if (branches[name] === undefined) {
|
||||
branches[name] = head != null ? head.id : null;
|
||||
branchesConfig[name] = { name, order: order ? parseInt(order, 10) : null };
|
||||
checkout(name);
|
||||
@@ -167,7 +169,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
|
||||
expected: ['branch abc'],
|
||||
};
|
||||
throw error;
|
||||
} else if (typeof currentCommit === 'undefined' || !currentCommit) {
|
||||
} else if (currentCommit === undefined || !currentCommit) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "merge". Current branch (' + curBranch + ')has no commits'
|
||||
);
|
||||
@@ -179,7 +181,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
|
||||
expected: ['commit'],
|
||||
};
|
||||
throw error;
|
||||
} else if (typeof branches[otherBranch] === 'undefined') {
|
||||
} else if (branches[otherBranch] === undefined) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') does not exist'
|
||||
);
|
||||
@@ -191,7 +193,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
|
||||
expected: ['branch ' + otherBranch],
|
||||
};
|
||||
throw error;
|
||||
} else if (typeof otherCommit === 'undefined' || !otherCommit) {
|
||||
} else if (otherCommit === undefined || !otherCommit) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "merge". Branch to be merged (' + otherBranch + ') has no commits'
|
||||
);
|
||||
@@ -213,7 +215,7 @@ export const merge = function (otherBranch, custom_id, override_type, custom_tag
|
||||
expected: ['branch abc'],
|
||||
};
|
||||
throw error;
|
||||
} else if (custom_id && typeof commits[custom_id] !== 'undefined') {
|
||||
} else if (custom_id && commits[custom_id] !== undefined) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "merge". Commit with id:' +
|
||||
custom_id +
|
||||
@@ -265,7 +267,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
||||
targetId = common.sanitizeText(targetId, configApi.getConfig());
|
||||
tag = common.sanitizeText(tag, configApi.getConfig());
|
||||
|
||||
if (!sourceId || typeof commits[sourceId] === 'undefined') {
|
||||
if (!sourceId || commits[sourceId] === undefined) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "cherryPick". Source commit id should exist and provided'
|
||||
);
|
||||
@@ -294,7 +296,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
if (!targetId || typeof commits[targetId] === 'undefined') {
|
||||
if (!targetId || commits[targetId] === undefined) {
|
||||
// cherry-pick source commit to current branch
|
||||
|
||||
if (sourceCommitBranch === curBranch) {
|
||||
@@ -311,7 +313,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
||||
throw error;
|
||||
}
|
||||
const currentCommit = commits[branches[curBranch]];
|
||||
if (typeof currentCommit === 'undefined' || !currentCommit) {
|
||||
if (currentCommit === undefined || !currentCommit) {
|
||||
let error = new Error(
|
||||
'Incorrect usage of "cherry-pick". Current branch (' + curBranch + ')has no commits'
|
||||
);
|
||||
@@ -342,7 +344,7 @@ export const cherryPick = function (sourceId, targetId, tag) {
|
||||
};
|
||||
export const checkout = function (branch) {
|
||||
branch = common.sanitizeText(branch, configApi.getConfig());
|
||||
if (typeof branches[branch] === 'undefined') {
|
||||
if (branches[branch] === undefined) {
|
||||
let error = new Error(
|
||||
'Trying to checkout branch which is not yet created. (Help try using "branch ' + branch + '")'
|
||||
);
|
||||
@@ -529,5 +531,7 @@ export default {
|
||||
getAccTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
commitType,
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user