Merge branch 'develop' into sidv/deprecateMermaidAPI

This commit is contained in:
Sidharth Vinod
2024-03-23 18:21:39 +05:30
committed by GitHub
30 changed files with 1159 additions and 101 deletions

View File

@@ -1,4 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
NODE_OPTIONS=--max_old_space_size=8192 pnpm run pre-commit
NODE_OPTIONS="--max_old_space_size=8192" pnpm run pre-commit

View File

@@ -118,8 +118,8 @@ describe('Configuration', () => {
it('should not taint the initial configuration when using multiple directives', () => {
const url = 'http://localhost:9000/regression/issue-1874.html';
cy.visit(url);
cy.get('svg');
cy.window().should('have.property', 'rendered', true);
cy.get('svg').should('be.visible');
cy.matchImageSnapshot(
'configuration.spec-should-not-taint-initial-configuration-when-using-multiple-directives'
);

View File

@@ -844,3 +844,42 @@ end
});
});
});
describe('Title and arrow styling #4813', () => {
it('should render a flowchart with title', () => {
const titleString = 'Test Title';
renderGraph(
`---
title: ${titleString}
---
flowchart LR
A-->B
A-->C`,
{ flowchart: { defaultRenderer: 'elk' } }
);
cy.get('svg').should((svg) => {
const title = svg[0].querySelector('text');
expect(title.textContent).to.contain(titleString);
});
});
it('Render with stylized arrows', () => {
renderGraph(
`
flowchart LR
A-->B
B-.-oC
C==xD
D ~~~ A`,
{ flowchart: { defaultRenderer: 'elk' } }
);
cy.get('svg').should((svg) => {
const edges = svg[0].querySelectorAll('.edges path');
console.log(edges);
expect(edges[0]).to.have.attr('pattern', 'solid');
expect(edges[1]).to.have.attr('pattern', 'dotted');
expect(edges[2]).to.have.css('stroke-width', '3.5px');
expect(edges[3]).to.have.css('stroke-width', '1.5px');
});
});
});

View File

@@ -760,6 +760,51 @@ A ~~~ B
);
});
it('3258: Should render subgraphs with main graph nodeSpacing and rankSpacing', () => {
imgSnapshotTest(
`---
title: Subgraph nodeSpacing and rankSpacing example
---
flowchart LR
X --> Y
subgraph X
direction LR
A
C
end
subgraph Y
B
D
end
`,
{ flowchart: { nodeSpacing: 1, rankSpacing: 1 } }
);
});
it('3258: Should render subgraphs with large nodeSpacing and rankSpacing', () => {
imgSnapshotTest(
`---
title: Subgraph nodeSpacing and rankSpacing example
config:
flowchart:
nodeSpacing: 250
rankSpacing: 250
---
flowchart LR
X --> Y
subgraph X
direction LR
A
C
end
subgraph Y
B
D
end
`
);
});
describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => {
it('With styling and classes', () => {
@@ -904,6 +949,18 @@ end
);
});
});
it('should not auto wrap when markdownAutoWrap is false', () => {
imgSnapshotTest(
`flowchart TD
angular_velocity["\`**angular_velocity**
*angular_displacement / duration*
[rad/s, 1/s]
{vector}\`"]
frequency["frequency\n(1 / period_duration)\n[Hz, 1/s]"]`,
{ markdownAutoWrap: false }
);
});
});
describe('Subgraph title margins', () => {
it('Should render subgraphs with title margins set (LR)', () => {

View File

@@ -1013,4 +1013,450 @@ gitGraph TB:
{ gitGraph: { parallelCommits: true } }
);
});
describe('Git-Graph Bottom-to-Top Orientation Tests', () => {
it('50: should render a simple gitgraph with commit on main branch | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "1"
commit id: "2"
commit id: "3"
`,
{}
);
});
it('51: should render a simple gitgraph with commit on main branch with Id | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "One"
commit id: "Two"
commit id: "Three"
`,
{}
);
});
it('52: should render a simple gitgraph with different commitTypes on main branch | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "Normal Commit"
commit id: "Reverse Commit" type: REVERSE
commit id: "Highlight Commit" type: HIGHLIGHT
`,
{}
);
});
it('53: should render a simple gitgraph with tags commitTypes on main branch | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "Normal Commit with tag" tag: "v1.0.0"
commit id: "Reverse Commit with tag" type: REVERSE tag: "RC_1"
commit id: "Highlight Commit" type: HIGHLIGHT tag: "8.8.4"
`,
{}
);
});
it('54: should render a simple gitgraph with two branches | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "1"
commit id: "2"
branch develop
checkout develop
commit id: "3"
commit id: "4"
checkout main
commit id: "5"
commit id: "6"
`,
{}
);
});
it('55: should render a simple gitgraph with two branches and merge commit | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "1"
commit id: "2"
branch develop
checkout develop
commit id: "3"
commit id: "4"
checkout main
merge develop
commit id: "5"
commit id: "6"
`,
{}
);
});
it('56: should render a simple gitgraph with three branches and tagged merge commit | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "1"
commit id: "2"
branch nice_feature
checkout nice_feature
commit id: "3"
checkout main
commit id: "4"
checkout nice_feature
branch very_nice_feature
checkout very_nice_feature
commit id: "5"
checkout main
commit id: "6"
checkout nice_feature
commit id: "7"
checkout main
merge nice_feature id: "12345" tag: "my merge commit"
checkout very_nice_feature
commit id: "8"
checkout main
commit id: "9"
`,
{}
);
});
it('57: should render a simple gitgraph with more than 8 branches & overriding variables | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'themeVariables': {
'gitBranchLabel0': '#ffffff',
'gitBranchLabel1': '#ffffff',
'gitBranchLabel2': '#ffffff',
'gitBranchLabel3': '#ffffff',
'gitBranchLabel4': '#ffffff',
'gitBranchLabel5': '#ffffff',
'gitBranchLabel6': '#ffffff',
'gitBranchLabel7': '#ffffff',
} } }%%
gitGraph BT:
checkout main
branch branch1
branch branch2
branch branch3
branch branch4
branch branch5
branch branch6
branch branch7
branch branch8
branch branch9
checkout branch1
commit id: "1"
`,
{}
);
});
it('58: should render a simple gitgraph with rotated labels | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
'rotateCommitLabel': true
} } }%%
gitGraph BT:
commit id: "75f7219e83b321cd3fdde7dcf83bc7c1000a6828"
commit id: "0db4784daf82736dec4569e0dc92980d328c1f2e"
commit id: "7067e9973f9eaa6cd4a4b723c506d1eab598e83e"
commit id: "66972321ad6c199013b5b31f03b3a86fa3f9817d"
`,
{}
);
});
it('59: should render a simple gitgraph with horizontal labels | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`%%{init: { 'logLevel': 'debug', 'theme': 'default' , 'gitGraph': {
'rotateCommitLabel': false
} } }%%
gitGraph BT:
commit id: "Alpha"
commit id: "Beta"
commit id: "Gamma"
commit id: "Delta"
`,
{}
);
});
it('60: should render a simple gitgraph with cherry pick commit | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`
gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
checkout develop
commit id:"C"
`,
{}
);
});
it('61: should render a gitgraph with cherry pick commit with custom tag | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`
gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
commit id:"TWO"
cherry-pick id:"A" tag: "snapshot"
commit id:"THREE"
checkout develop
commit id:"C"
`,
{}
);
});
it('62: should render a gitgraph with cherry pick commit with no tag | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`
gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
checkout main
commit id:"TWO"
cherry-pick id:"A" tag: ""
commit id:"THREE"
checkout develop
commit id:"C"
`,
{}
);
});
it('63: should render a simple gitgraph with two cherry pick commit | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`
gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
branch featureA
commit id:"FIX"
commit id: "FIX-2"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
cherry-pick id:"FIX"
checkout develop
commit id:"C"
merge featureA
`,
{}
);
});
it('64: should render commits for more than 8 branches | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`
gitGraph BT:
checkout main
%% Make sure to manually set the ID of all commits, for consistent visual tests
commit id: "1-abcdefg"
checkout main
branch branch1
commit id: "2-abcdefg"
checkout main
merge branch1
branch branch2
commit id: "3-abcdefg"
checkout main
merge branch2
branch branch3
commit id: "4-abcdefg"
checkout main
merge branch3
branch branch4
commit id: "5-abcdefg"
checkout main
merge branch4
branch branch5
commit id: "6-abcdefg"
checkout main
merge branch5
branch branch6
commit id: "7-abcdefg"
checkout main
merge branch6
branch branch7
commit id: "8-abcdefg"
checkout main
merge branch7
branch branch8
commit id: "9-abcdefg"
checkout main
merge branch8
branch branch9
commit id: "10-abcdefg"
`,
{}
);
});
it('65: should render a simple gitgraph with three branches,custom merge commit id,tag,type | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id: "1"
commit id: "2"
branch nice_feature
checkout nice_feature
commit id: "3"
checkout main
commit id: "4"
checkout nice_feature
branch very_nice_feature
checkout very_nice_feature
commit id: "5"
checkout main
commit id: "6"
checkout nice_feature
commit id: "7"
checkout main
merge nice_feature id: "customID" tag: "customTag" type: REVERSE
checkout very_nice_feature
commit id: "8"
checkout main
commit id: "9"
`,
{}
);
});
it('66: should render a simple gitgraph with a title | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`---
title: simple gitGraph
---
gitGraph BT:
commit id: "1-abcdefg"
`,
{}
);
});
it('67: should render a simple gitgraph overlapping commits | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id:"s1"
commit id:"s2"
branch branch1
commit id:"s3"
commit id:"s4"
checkout main
commit id:"s5"
checkout branch1
commit id:"s6"
commit id:"s7"
merge main
`,
{}
);
});
it('68: should render a simple gitgraph with two branches from same commit | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id:"1-abcdefg"
commit id:"2-abcdefg"
branch feature-001
commit id:"3-abcdefg"
commit id:"4-abcdefg"
checkout main
branch feature-002
commit id:"5-abcdefg"
checkout feature-001
merge feature-002
`,
{}
);
});
it('69: should render GitGraph with branch that is not used immediately | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
`,
{}
);
});
it('70: should render GitGraph with branch and sub-branch neither of which used immediately | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id:"1-abcdefg"
branch x
checkout main
commit id:"2-abcdefg"
checkout x
commit id:"3-abcdefg"
checkout main
merge x
checkout x
branch y
checkout x
commit id:"4-abcdefg"
checkout y
commit id:"5-abcdefg"
checkout x
merge y
`,
{}
);
});
it('71: should render GitGraph with parallel commits | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
commit id:"1-abcdefg"
commit id:"2-abcdefg"
branch develop
commit id:"3-abcdefg"
commit id:"4-abcdefg"
checkout main
branch feature
commit id:"5-abcdefg"
commit id:"6-abcdefg"
checkout main
commit id:"7-abcdefg"
commit id:"8-abcdefg"
`,
{ gitGraph: { parallelCommits: true } }
);
});
it('72: should render GitGraph with unconnected branches and parallel commits | Vertical Branch - Bottom-to-top', () => {
imgSnapshotTest(
`gitGraph BT:
branch dev
branch v2
branch feat
commit id:"1-abcdefg"
commit id:"2-abcdefg"
checkout main
commit id:"3-abcdefg"
checkout dev
commit id:"4-abcdefg"
checkout v2
commit id:"5-abcdefg"
checkout main
commit id:"6-abcdefg"
`,
{ gitGraph: { parallelCommits: true } }
);
});
});
});

View File

@@ -21,7 +21,11 @@ sequenceDiagram
mermaid.initialize({
theme: 'base',
themeVariables: {},
startOnLoad: true,
startOnLoad: false,
});
await mermaid.run();
if (window.Cypress) {
window.rendered = true;
}
</script>
</html>

View File

@@ -1591,6 +1591,33 @@
</pre>
<hr />
<pre class="mermaid">
---
title: Subgraph nodeSpacing and rankSpacing example
config:
flowchart:
nodeSpacing: 1
rankSpacing: 1
---
flowchart LR
X --> Y
subgraph X
direction LR
A
C
end
subgraph Y
direction LR
B
D
end
</pre>
<hr />
<h1 id="link-clicked">Anchor for "link-clicked" test</h1>
<script type="module">

View File

@@ -40,6 +40,19 @@
checkout main
merge newbranch
</pre>
<pre class="mermaid">
---
title: Simple "branch and merge" (bottom-to-top)
---
gitGraph BT:
commit
branch newbranch
checkout newbranch
commit
checkout main
merge newbranch
</pre
>
<h2>Continuous development graph</h2>
<pre class="mermaid">
---
@@ -73,6 +86,23 @@
checkout main
merge develop
</pre>
<pre class="mermaid">
---
title: Continuous development (bottom-to-top)
---
gitGraph BT:
commit
branch develop
checkout develop
commit
checkout main
merge develop
checkout develop
commit
checkout main
merge develop
</pre
>
<h2>Merge feature to advanced main graph</h2>
<pre class="mermaid">
---
@@ -100,6 +130,20 @@
commit
merge newbranch
</pre>
<pre class="mermaid">
---
title: Merge feature to advanced main (bottom-to-top)
---
gitGraph BT:
commit
branch newbranch
checkout newbranch
commit
checkout main
commit
merge newbranch
</pre
>
<h2>Two-way merges</h2>
<pre class="mermaid">
---
@@ -137,6 +181,25 @@
checkout main
merge develop
</pre>
<pre class="mermaid">
---
title: Two-way merges (bottom-to-top)
---
gitGraph BT:
commit
branch develop
checkout develop
commit
checkout main
merge develop
commit
checkout develop
merge main
commit
checkout main
merge develop
</pre
>
<h2>Cherry-pick from branch graph</h2>
<pre class="mermaid">
---
@@ -170,6 +233,22 @@
checkout main
cherry-pick id: "Pick me"
</pre>
<pre class="mermaid">
---
title: Cherry-pick from branch (bottom-to-top)
---
gitGraph BT:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
</pre>
<h2>Cherry-pick from main graph</h2>
<pre class="mermaid">
---
@@ -199,6 +278,21 @@
commit
cherry-pick id: "A"
</pre>
<pre class="mermaid">
---
title: Cherry-pick from main (bottom-to-top)
---
gitGraph BT:
commit
branch develop
commit
checkout main
commit id:"A"
checkout develop
commit
cherry-pick id: "A"
</pre
>
<h2>Cherry-pick then merge graph</h2>
<pre class="mermaid">
---
@@ -234,6 +328,24 @@
cherry-pick id: "Pick me"
merge newbranch
</pre>
<pre class="mermaid">
---
title: Cherry-pick then merge (bottom-to-top)
---
gitGraph BT:
commit
branch newbranch
checkout newbranch
commit id: "Pick me"
checkout main
commit
checkout newbranch
commit
checkout main
cherry-pick id: "Pick me"
merge newbranch
</pre
>
<h2>Merge from main onto undeveloped branch graph</h2>
<pre class="mermaid">
---
@@ -261,6 +373,20 @@
checkout develop
merge main
</pre>
<pre class="mermaid">
---
title: Merge from main onto undeveloped branch (bottom-to-top)
---
gitGraph BT:
commit
branch develop
commit
checkout main
commit
checkout develop
merge main
</pre
>
<h2>Merge from main onto developed branch graph</h2>
<pre class="mermaid">
---
@@ -290,6 +416,21 @@
commit
merge main
</pre>
<pre class="mermaid">
---
title: Merge from main onto developed branch (bottom-to-top)
---
gitGraph BT:
commit
branch develop
commit
checkout main
commit
checkout develop
commit
merge main
</pre
>
<h2>Two branches from same commit graph</h2>
<pre class="mermaid">
---
@@ -323,6 +464,23 @@
checkout feature-001
merge feature-002
</pre>
<pre class="mermaid">
---
title: Two branches from same commit (bottom-to-top)
---
gitGraph BT:
commit
commit
branch feature-001
commit
commit
checkout main
branch feature-002
commit
checkout feature-001
merge feature-002
</pre
>
<h2>Three branches and a cherry-pick from each graph</h2>
<pre class="mermaid">
---
@@ -372,6 +530,30 @@
commit id:"C"
merge featureA
</pre>
<pre class="mermaid">
---
title: Three branches and a cherry-pick from each (bottom-to-top)
---
gitGraph BT:
commit id: "ZERO"
branch develop
commit id:"A"
checkout main
commit id:"ONE"
checkout develop
commit id:"B"
branch featureA
commit id:"FIX"
commit id: "FIX-2"
checkout main
commit id:"TWO"
cherry-pick id:"A"
commit id:"THREE"
cherry-pick id:"FIX"
checkout develop
commit id:"C"
merge featureA
</pre>
<script type="module">
import mermaid from './mermaid.esm.mjs';
const ALLOWED_TAGS = [

View File

@@ -50,7 +50,7 @@ Pushes in a directive to the configuration
| --------- | ------------------------- | ----------- | ------------------------------ |
| getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |
**Notes**: Returns **any** the currentConfig
**Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.
#### Returns

View File

@@ -852,6 +852,16 @@ Formatting:
This feature is applicable to node labels, edge labels, and subgraph labels.
The auto wrapping can be disabled by using
```
---
config:
markdownAutoWrap: false
---
graph LR
```
## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.

View File

@@ -836,9 +836,9 @@ Here, we have changed the default main branch name to `MetroLine1`.
## Orientation (v10.3.0+)
Mermaid supports two graph orientations: **Left-to-Right** (default) and **Top-to-Bottom**.
Mermaid supports three graph orientations: **Left-to-Right** (default), **Top-to-Bottom**, and **Bottom-to-Top**.
You can set this with either `LR:` (for [**Left-to-Right**](#left-to-right-default-lr)) or `TB:` (for [**Top-to-Bottom**](#top-to-bottom-tb)) after `gitGraph`.
You can set this with either `LR:` (for [**Left-to-Right**](#left-to-right-default-lr)), `TB:` (for [**Top-to-Bottom**](#top-to-bottom-tb)) or `BT:` (for [**Bottom-to-Top**](#bottom-to-top-bt)) after `gitGraph`.
### Left to Right (default, `LR:`)
@@ -916,6 +916,44 @@ Usage example:
commit
```
### Bottom to Top (`BT:`) (v\<MERMAID_RELEASE_VERSION>+)
In `BT` (**Bottom-to-Top**) orientation, the commits run from bottom to top of the graph and branches are arranged side-by-side.
To orient the graph this way, you need to add `BT:` after gitGraph.
Usage example:
```mermaid-example
gitGraph BT:
commit
commit
branch develop
commit
commit
checkout main
commit
commit
merge develop
commit
commit
```
```mermaid
gitGraph BT:
commit
commit
branch develop
commit
commit
checkout main
commit
commit
merge develop
commit
commit
```
## Parallel commits (v10.8.0+)
Commits in Mermaid display temporal information in gitgraph by default. For example if two commits are one commit away from its parent, the commit that was made earlier is rendered closer to its parent. You can turn this off by enabling the `parallelCommits` flag.

View File

@@ -1,6 +1,6 @@
// @ts-ignore: JISON typing missing
import parser from '../../mermaid/src/diagrams/flowchart/parser/flow.jison';
import * as db from '../../mermaid/src/diagrams/flowchart/flowDb.js';
import db from '../../mermaid/src/diagrams/flowchart/flowDb.js';
import styles from '../../mermaid/src/diagrams/flowchart/styles.js';
import renderer from './flowRenderer-elk.js';

View File

@@ -6,6 +6,7 @@ import { findCommonAncestor } from './render-utils.js';
import { labelHelper } from '../../mermaid/src/dagre-wrapper/shapes/util.js';
import { getConfig } from '../../mermaid/src/config.js';
import { log } from '../../mermaid/src/logger.js';
import utils from '../../mermaid/src/utils.js';
import { setupGraphViewbox } from '../../mermaid/src/setupGraphViewbox.js';
import common from '../../mermaid/src/diagrams/common/common.js';
import { interpolateToCurve, getStylesFromArray } from '../../mermaid/src/utils.js';
@@ -655,6 +656,11 @@ const insertEdge = function (edgesEl, edge, edgeData, diagObj, parentLookupDb, i
.attr('d', curve(points))
.attr('class', 'path ' + edgeData.classes)
.attr('fill', 'none');
Object.entries(edgeData).forEach(([key, value]) => {
if (key !== 'classes') {
edgePath.attr(key, value);
}
});
const edgeG = edgesEl.insert('g').attr('class', 'edgeLabel');
const edgeWithLabel = select(edgeG.node().appendChild(edge.labelEl));
const box = edgeWithLabel.node().firstChild.getBoundingClientRect();
@@ -702,21 +708,15 @@ const insertChildren = (nodeArray, parentLookupDb) => {
*/
export const draw = async function (text, id, _version, diagObj) {
// Add temporary render element
diagObj.db.clear();
const { securityLevel, flowchart: conf } = getConfig();
nodeDb = {};
portPos = {};
diagObj.db.setGen('gen-2');
// Parse the graph definition
diagObj.parser.parse(text);
const renderEl = select('body').append('div').attr('style', 'height:400px').attr('id', 'cy');
let graph = {
id: 'root',
layoutOptions: {
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
'org.eclipse.elk.padding': '[top=100, left=100, bottom=110, right=110]',
'elk.layered.spacing.edgeNodeBetweenLayers': '30',
'elk.layered.spacing.edgeNodeBetweenLayers': conf?.nodeSpacing ? `${conf.nodeSpacing}` : '30',
// 'elk.layered.mergeEdges': 'true',
'elk.direction': 'DOWN',
// 'elk.ports.sameLayerEdges': true,
@@ -744,7 +744,6 @@ export const draw = async function (text, id, _version, diagObj) {
graph.layoutOptions['elk.direction'] = 'LEFT';
break;
}
const { securityLevel, flowchart: conf } = getConfig();
// Find the root dom node to ne used in rendering
// Handle root and document for when rendering in sandbox mode
@@ -759,7 +758,6 @@ export const draw = async function (text, id, _version, diagObj) {
const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document;
const svg = root.select(`[id="${id}"]`);
// Define the supported markers for the diagram
const markers = ['point', 'circle', 'cross'];
@@ -841,6 +839,7 @@ export const draw = async function (text, id, _version, diagObj) {
log.info('after layout', JSON.stringify(graph, null, 2));
const g = await elk.layout(graph);
drawNodes(0, 0, g.children, svg, subGraphsEl, diagObj, 0);
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
log.info('after layout', g);
g.edges?.map((edge) => {
insertEdge(edgesEl, edge, edge.edgeData, diagObj, parentLookupDb, id);

View File

@@ -124,7 +124,7 @@ export const setConfig = (conf: MermaidConfig): MermaidConfig => {
* | --------- | ------------------------- | ----------- | ------------------------------ |
* | getConfig | Obtains the currentConfig | Get Request | Any Values from current Config |
*
* **Notes**: Returns **any** the currentConfig
* **Notes**: Avoid calling this function repeatedly. Instead, store the result in a variable and use it, and pass it down to function calls.
*
* @returns The currentConfig
*/

View File

@@ -159,6 +159,7 @@ export interface MermaidConfig {
dompurifyConfig?: DOMPurifyConfiguration;
wrap?: boolean;
fontSize?: number;
markdownAutoWrap?: boolean;
/**
* Suppresses inserting 'Syntax error' diagram in the DOM.
* This is useful when you want to control how to handle syntax errors in your application.

View File

@@ -30,7 +30,7 @@ const rect = (parent, node) => {
// .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
const text =
node.labelType === 'markdown'
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels })
? createText(label, node.labelText, { style: node.labelStyle, useHtmlLabels }, siteConfig)
: label.node().appendChild(createLabel(node.labelText, node.labelStyle, undefined, true));
// Get the size of the label

View File

@@ -18,15 +18,21 @@ export const clear = () => {
};
export const insertEdgeLabel = (elem, edge) => {
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
const config = getConfig();
const useHtmlLabels = evaluate(config.flowchart.htmlLabels);
// Create the actual text element
const labelElement =
edge.labelType === 'markdown'
? createText(elem, edge.label, {
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
})
? createText(
elem,
edge.label,
{
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
},
config
)
: createLabel(edge.label, edge.labelStyle);
// Create outer g, edgeLabel, this will be positioned after graph layout

View File

@@ -54,6 +54,14 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
if (node && node.clusterNode) {
// const children = graph.children(v);
log.info('Cluster identified', v, node.width, graph.node(v));
// `node.graph.setGraph` applies the graph configurations such as nodeSpacing to subgraphs as without this the default values would be used
// We override only the `ranksep` and `nodesep` configurations to allow for setting subgraph spacing while avoiding overriding other properties
const { ranksep, nodesep } = graph.graph();
node.graph.setGraph({
...node.graph.graph(),
ranksep,
nodesep,
});
const o = await recursiveRender(
nodes,
node.graph,

View File

@@ -6,8 +6,9 @@ import { evaluate, sanitizeText } from '../../diagrams/common/common.js';
import { decodeEntities } from '../../utils.js';
export const labelHelper = async (parent, node, _classes, isNode) => {
const config = getConfig();
let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
const useHtmlLabels = node.useHtmlLabels || evaluate(config.flowchart.htmlLabels);
if (!_classes) {
classes = 'node default';
} else {
@@ -35,26 +36,26 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
let text;
if (node.labelType === 'markdown') {
// text = textNode;
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth,
classes: 'markdown-node-label',
});
text = createText(
label,
sanitizeText(decodeEntities(labelText), config),
{
useHtmlLabels,
width: node.width || config.flowchart.wrappingWidth,
classes: 'markdown-node-label',
},
config
);
} else {
text = textNode.appendChild(
createLabel(
sanitizeText(decodeEntities(labelText), getConfig()),
node.labelStyle,
false,
isNode
)
createLabel(sanitizeText(decodeEntities(labelText), config), node.labelStyle, false, isNode)
);
}
// Get the size of the label
let bbox = text.getBBox();
const halfPadding = node.padding / 2;
if (evaluate(getConfig().flowchart.htmlLabels)) {
if (evaluate(config.flowchart.htmlLabels)) {
const div = text.children[0];
const dv = select(text);
@@ -76,8 +77,8 @@ export const labelHelper = async (parent, node, _classes, isNode) => {
if (noImgText) {
// default size if no text
const bodyFontSize = getConfig().fontSize
? getConfig().fontSize
const bodyFontSize = config.fontSize
? config.fontSize
: window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
const width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';

View File

@@ -346,11 +346,9 @@ export const renderKatex = async (text: string, config: MermaidConfig): Promise<
.split(lineBreakRegex)
.map((line) =>
hasKatex(line)
? `
<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
? `<div style="display: flex; align-items: center; justify-content: center; white-space: nowrap;">
${line}
</div>
`
</div>`
: `<div>${line}</div>`
)
.join('')

View File

@@ -54,7 +54,7 @@ describe('when parsing a gitGraph', function () {
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should handle set direction', function () {
it('should handle set direction top to bottom', function () {
const str = 'gitGraph TB:\n' + 'commit\n';
parser.parse(str);
@@ -66,6 +66,18 @@ describe('when parsing a gitGraph', function () {
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should handle set direction bottom to top', function () {
const str = 'gitGraph BT:\n' + 'commit\n';
parser.parse(str);
const commits = parser.yy.getCommits();
expect(Object.keys(commits).length).toBe(1);
expect(parser.yy.getCurrentBranch()).toBe('main');
expect(parser.yy.getDirection()).toBe('BT');
expect(Object.keys(parser.yy.getBranches()).length).toBe(1);
});
it('should checkout a branch', function () {
const str = 'gitGraph:\n' + 'branch new\n' + 'checkout new\n';

View File

@@ -20,6 +20,7 @@ let commitPos = {};
let lanes = [];
let maxPos = 0;
let dir = 'LR';
let defaultPos = 30;
const clear = () => {
branchPos = {};
commitPos = {};
@@ -77,7 +78,7 @@ const findClosestParent = (parents) => {
let maxPosition = 0;
parents.forEach((parent) => {
const parentPosition = dir === 'TB' ? commitPos[parent].y : commitPos[parent].x;
const parentPosition = dir === 'TB' || dir === 'BT' ? commitPos[parent].y : commitPos[parent].x;
if (parentPosition >= maxPosition) {
closestParent = parent;
maxPosition = parentPosition;
@@ -87,6 +88,83 @@ const findClosestParent = (parents) => {
return closestParent || undefined;
};
/**
* Searches for the closest parent from the parents list passed as argument for Bottom-to-Top orientation.
* The parents list comes from an individual commit. The closest parent is actually
* the one farther down the graph, since that means it is closer to its child.
*
* @param {string[]} parents
* @returns {string | undefined}
*/
const findClosestParentBT = (parents) => {
let closestParent = '';
let maxPosition = Infinity;
parents.forEach((parent) => {
const parentPosition = commitPos[parent].y;
if (parentPosition <= maxPosition) {
closestParent = parent;
maxPosition = parentPosition;
}
});
return closestParent || undefined;
};
/**
* Sets the position of the commit elements when the orientation is set to BT-Parallel.
* This is needed to render the chart in Bottom-to-Top mode while keeping the parallel
* commits in the correct position. First, it finds the correct position of the root commit
* using the findClosestParent method. Then, it uses the findClosestParentBT to set the position
* of the remaining commits.
*
* @param {any} sortedKeys
* @param {any} commits
* @param {any} defaultPos
* @param {any} commitStep
* @param {any} layoutOffset
*/
const setParallelBTPos = (sortedKeys, commits, defaultPos, commitStep, layoutOffset) => {
let curPos = defaultPos;
let maxPosition = defaultPos;
let roots = [];
sortedKeys.forEach((key) => {
const commit = commits[key];
if (commit.parents.length) {
const closestParent = findClosestParent(commit.parents);
curPos = commitPos[closestParent].y + commitStep;
if (curPos >= maxPosition) {
maxPosition = curPos;
}
} else {
roots.push(commit);
}
const x = branchPos[commit.branch].pos;
const y = curPos + layoutOffset;
commitPos[commit.id] = { x: x, y: y };
});
curPos = maxPosition;
roots.forEach((commit) => {
const posWithOffset = curPos + defaultPos;
const y = posWithOffset;
const x = branchPos[commit.branch].pos;
commitPos[commit.id] = { x: x, y: y };
});
sortedKeys.forEach((key) => {
const commit = commits[key];
if (commit.parents.length) {
const closestParent = findClosestParentBT(commit.parents);
curPos = commitPos[closestParent].y - commitStep;
if (curPos <= maxPosition) {
maxPosition = curPos;
}
const x = branchPos[commit.branch].pos;
const y = curPos - layoutOffset;
commitPos[commit.id] = { x: x, y: y };
}
});
};
/**
* Draws the commits with its symbol and labels. The function has two modes, one which only
* calculates the positions and one that does the actual drawing. This for a simple way getting the
@@ -102,39 +180,54 @@ const drawCommits = (svg, commits, modifyGraph) => {
const gLabels = svg.append('g').attr('class', 'commit-labels');
let pos = 0;
if (dir === 'TB') {
pos = 30;
if (dir === 'TB' || dir === 'BT') {
pos = defaultPos;
}
const keys = Object.keys(commits);
const sortedKeys = keys.sort((a, b) => {
return commits[a].seq - commits[b].seq;
});
const isParallelCommits = gitGraphConfig.parallelCommits;
const layoutOffset = 10;
const commitStep = 40;
let sortedKeys =
dir !== 'BT' || (dir === 'BT' && isParallelCommits)
? keys.sort((a, b) => {
return commits[a].seq - commits[b].seq;
})
: keys
.sort((a, b) => {
return commits[a].seq - commits[b].seq;
})
.reverse();
if (dir === 'BT' && isParallelCommits) {
setParallelBTPos(sortedKeys, commits, pos, commitStep, layoutOffset);
sortedKeys = sortedKeys.reverse();
}
sortedKeys.forEach((key) => {
const commit = commits[key];
if (isParallelCommits) {
if (commit.parents.length) {
const closestParent = findClosestParent(commit.parents);
pos =
dir === 'TB'
? commitPos[closestParent].y + commitStep
: commitPos[closestParent].x + commitStep;
} else {
pos = 0;
const closestParent =
dir === 'BT' ? findClosestParentBT(commit.parents) : findClosestParent(commit.parents);
if (dir === 'TB') {
pos = 30;
pos = commitPos[closestParent].y + commitStep;
} else if (dir === 'BT') {
pos = commitPos[key].y - commitStep;
} else {
pos = commitPos[closestParent].x + commitStep;
}
} else {
if (dir === 'TB') {
pos = defaultPos;
} else if (dir === 'BT') {
pos = commitPos[key].y - commitStep;
} else {
pos = 0;
}
}
}
const posWithOffset = pos + layoutOffset;
const y = dir === 'TB' ? posWithOffset : branchPos[commit.branch].pos;
const x = dir === 'TB' ? branchPos[commit.branch].pos : posWithOffset;
const posWithOffset = dir === 'BT' && isParallelCommits ? pos : pos + layoutOffset;
const y = dir === 'TB' || dir === 'BT' ? posWithOffset : branchPos[commit.branch].pos;
const x = dir === 'TB' || dir === 'BT' ? branchPos[commit.branch].pos : posWithOffset;
// Don't draw the commits now but calculate the positioning which is used by the branch lines etc.
if (modifyGraph) {
@@ -258,7 +351,7 @@ const drawCommits = (svg, commits, modifyGraph) => {
}
}
}
if (dir === 'TB') {
if (dir === 'TB' || dir === 'BT') {
commitPos[commit.id] = { x: x, y: posWithOffset };
} else {
commitPos[commit.id] = { x: posWithOffset, y: y };
@@ -295,16 +388,14 @@ const drawCommits = (svg, commits, modifyGraph) => {
.attr('width', bbox.width + 2 * py)
.attr('height', bbox.height + 2 * py);
if (dir === 'TB') {
if (dir === 'TB' || dir === 'BT') {
labelBkg.attr('x', x - (bbox.width + 4 * px + 5)).attr('y', y - 12);
text.attr('x', x - (bbox.width + 4 * px)).attr('y', y + bbox.height - 12);
}
if (dir !== 'TB') {
} else {
text.attr('x', posWithOffset - bbox.width / 2);
}
if (gitGraphConfig.rotateCommitLabel) {
if (dir === 'TB') {
if (dir === 'TB' || dir === 'BT') {
text.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
labelBkg.attr('transform', 'rotate(' + -45 + ', ' + x + ', ' + y + ')');
} else {
@@ -348,7 +439,7 @@ const drawCommits = (svg, commits, modifyGraph) => {
.attr('r', 1.5)
.attr('class', 'tag-hole');
if (dir === 'TB') {
if (dir === 'TB' || dir === 'BT') {
rect
.attr('class', 'tag-label-bkg')
.attr(
@@ -373,7 +464,7 @@ const drawCommits = (svg, commits, modifyGraph) => {
}
}
}
pos += commitStep + layoutOffset;
pos = dir === 'BT' && isParallelCommits ? pos + commitStep : pos + commitStep + layoutOffset;
if (pos > maxPos) {
maxPos = pos;
}
@@ -389,7 +480,6 @@ const drawCommits = (svg, commits, modifyGraph) => {
*
* @param {any} commitA
* @param {any} commitB
* @param branchToGetCurve
* @param p1
* @param p2
* @param allCommits
@@ -402,7 +492,7 @@ const drawCommits = (svg, commits, modifyGraph) => {
* return true
*/
const shouldRerouteArrow = (commitA, commitB, p1, p2, allCommits) => {
const commitBIsFurthest = dir === 'TB' ? p1.x < p2.x : p1.y < p2.y;
const commitBIsFurthest = dir === 'TB' || dir === 'BT' ? p1.x < p2.x : p1.y < p2.y;
const branchToGetCurve = commitBIsFurthest ? commitB.branch : commitA.branch;
const isOnBranchToGetCurve = (x) => x.branch === branchToGetCurve;
const isBetweenCommits = (x) => x.seq > commitA.seq && x.seq < commitB.seq;
@@ -485,6 +575,21 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
p1.y + offset
} L ${lineX} ${p2.y - radius} ${arc2} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`;
}
} else if (dir === 'BT') {
if (p1.x < p2.x) {
// Source commit is on branch position left of destination commit
// so render arrow rightward with colour of destination branch
lineDef = `M ${p1.x} ${p1.y} L ${lineX - radius} ${p1.y} ${arc} ${lineX} ${
p1.y - offset
} L ${lineX} ${p2.y + radius} ${arc2} ${lineX + offset} ${p2.y} L ${p2.x} ${p2.y}`;
} else {
// Source commit is on branch position right of destination commit
// so render arrow leftward with colour of source branch
colorClassNum = branchPos[commitA.branch].index;
lineDef = `M ${p1.x} ${p1.y} L ${lineX + radius} ${p1.y} ${arc2} ${lineX} ${
p1.y - offset
} L ${lineX} ${p2.y + radius} ${arc} ${lineX - offset} ${p2.y} L ${p2.x} ${p2.y}`;
}
} else {
if (p1.y < p2.y) {
// Source commit is on branch positioned above destination commit
@@ -524,7 +629,6 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
arc2 = 'A 20 20, 0, 0, 1,';
radius = 20;
offset = 20;
if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) {
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y - radius} ${arc2} ${p1.x - offset} ${
p2.y
@@ -536,6 +640,38 @@ const drawArrow = (svg, commitA, commitB, allCommits) => {
}
}
if (p1.x === p2.x) {
lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`;
}
} else if (dir === 'BT') {
if (p1.x < p2.x) {
if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) {
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc2} ${p1.x + offset} ${
p2.y
} L ${p2.x} ${p2.y}`;
} else {
lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${
p1.y - offset
} L ${p2.x} ${p2.y}`;
}
}
if (p1.x > p2.x) {
arc = 'A 20 20, 0, 0, 0,';
arc2 = 'A 20 20, 0, 0, 1,';
radius = 20;
offset = 20;
if (commitB.type === commitType.MERGE && commitA.id !== commitB.parents[0]) {
lineDef = `M ${p1.x} ${p1.y} L ${p1.x} ${p2.y + radius} ${arc} ${p1.x - offset} ${
p2.y
} L ${p2.x} ${p2.y}`;
} else {
lineDef = `M ${p1.x} ${p1.y} L ${p2.x - radius} ${p1.y} ${arc} ${p2.x} ${
p1.y - offset
} L ${p2.x} ${p2.y}`;
}
}
if (p1.x === p2.x) {
lineDef = `M ${p1.x} ${p1.y} L ${p2.x} ${p2.y}`;
}
@@ -607,12 +743,16 @@ const drawBranches = (svg, branches) => {
line.attr('class', 'branch branch' + adjustIndexForTheme);
if (dir === 'TB') {
line.attr('y1', 30);
line.attr('y1', defaultPos);
line.attr('x1', pos);
line.attr('y2', maxPos);
line.attr('x2', pos);
} else if (dir === 'BT') {
line.attr('y1', maxPos);
line.attr('x1', pos);
line.attr('y2', defaultPos);
line.attr('x2', pos);
}
lanes.push(pos);
let name = branch.name;
@@ -646,8 +786,10 @@ const drawBranches = (svg, branches) => {
if (dir === 'TB') {
bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', 0);
label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + 0 + ')');
}
if (dir !== 'TB') {
} else if (dir === 'BT') {
bkg.attr('x', pos - bbox.width / 2 - 10).attr('y', maxPos);
label.attr('transform', 'translate(' + (pos - bbox.width / 2 - 5) + ', ' + maxPos + ')');
} else {
bkg.attr('transform', 'translate(' + -19 + ', ' + (pos - bbox.height / 2) + ')');
}
});
@@ -681,7 +823,10 @@ export const draw = function (txt, id, ver, diagObj) {
let bbox = labelElement.getBBox();
branchPos[branch.name] = { pos, index };
pos += 50 + (gitGraphConfig.rotateCommitLabel ? 40 : 0) + (dir === 'TB' ? bbox.width / 2 : 0);
pos +=
50 +
(gitGraphConfig.rotateCommitLabel ? 40 : 0) +
(dir === 'TB' || dir === 'BT' ? bbox.width / 2 : 0);
label.remove();
branchLabel.remove();
g.remove();

View File

@@ -44,6 +44,7 @@ cherry\-pick(?=\s|$) return 'CHERRY_PICK';
checkout(?=\s|$) return 'CHECKOUT';
"LR" return 'DIR';
"TB" return 'DIR';
"BT" return 'DIR';
":" return ':';
"^" return 'CARET'
"options"\r?\n this.begin("options"); //

View File

@@ -196,11 +196,16 @@ export const drawNode = function (
// Create the wrapped text element
const textElem = nodeElem.append('g');
const description = node.descr.replace(/(<br\/*>)/g, '\n');
const newEl = createText(textElem, description, {
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
});
const newEl = createText(
textElem,
description,
{
useHtmlLabels: htmlLabels,
width: node.width,
classes: 'mindmap-node-label',
},
conf
);
if (!htmlLabels) {
textElem

View File

@@ -537,6 +537,16 @@ Formatting:
This feature is applicable to node labels, edge labels, and subgraph labels.
The auto wrapping can be disabled by using
```
---
config:
markdownAutoWrap: false
---
graph LR
```
## Interaction
It is possible to bind a click event to a node, the click can lead to either a javascript callback or to a link which will be opened in a new browser tab.

View File

@@ -519,9 +519,9 @@ Here, we have changed the default main branch name to `MetroLine1`.
## Orientation (v10.3.0+)
Mermaid supports two graph orientations: **Left-to-Right** (default) and **Top-to-Bottom**.
Mermaid supports three graph orientations: **Left-to-Right** (default), **Top-to-Bottom**, and **Bottom-to-Top**.
You can set this with either `LR:` (for [**Left-to-Right**](#left-to-right-default-lr)) or `TB:` (for [**Top-to-Bottom**](#top-to-bottom-tb)) after `gitGraph`.
You can set this with either `LR:` (for [**Left-to-Right**](#left-to-right-default-lr)), `TB:` (for [**Top-to-Bottom**](#top-to-bottom-tb)) or `BT:` (for [**Bottom-to-Top**](#bottom-to-top-bt)) after `gitGraph`.
### Left to Right (default, `LR:`)
@@ -569,6 +569,29 @@ Usage example:
commit
```
### Bottom to Top (`BT:`) (v<MERMAID_RELEASE_VERSION>+)
In `BT` (**Bottom-to-Top**) orientation, the commits run from bottom to top of the graph and branches are arranged side-by-side.
To orient the graph this way, you need to add `BT:` after gitGraph.
Usage example:
```mermaid-example
gitGraph BT:
commit
commit
branch develop
commit
commit
checkout main
commit
commit
merge develop
commit
commit
```
## Parallel commits (v10.8.0+)
Commits in Mermaid display temporal information in gitgraph by default. For example if two commits are one commit away from its parent, the commit that was made earlier is rendered closer to its parent. You can turn this off by enabling the `parallelCommits` flag.

View File

@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck TODO: Fix types
import type { MermaidConfig } from '../config.type.js';
import type { Group } from '../diagram-api/types.js';
import type { D3TSpanElement, D3TextElement } from '../diagrams/common/commonTypes.js';
import { log } from '../logger.js';
@@ -21,8 +22,7 @@ function addHtmlSpan(element, node, width, classes, addBackground = false) {
const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
`
<span class="${labelClass} ${classes}" ` +
`<span class="${labelClass} ${classes}" ` +
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
'>' +
label +
@@ -181,14 +181,14 @@ export const createText = (
isNode = true,
width = 200,
addSvgBackground = false,
} = {}
} = {},
config: MermaidConfig
) => {
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode, addSvgBackground);
if (useHtmlLabels) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
// text = text.replace(/\\n|\n/g, '<br />');
const htmlText = markdownToHTML(text);
// log.info('markdownToHTML' + text, markdownToHTML(text));
const htmlText = markdownToHTML(text, config);
const node = {
isNode,
label: decodeEntities(htmlText).replace(
@@ -200,7 +200,7 @@ export const createText = (
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
return vertexNode;
} else {
const structuredText = markdownToLines(text);
const structuredText = markdownToLines(text, config);
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
return svgLabel;
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-irregular-whitespace */
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
import { test, expect } from 'vitest';
@@ -203,6 +204,31 @@ Word!`;
expect(output).toEqual(expectedOutput);
});
test('markdownToLines - No auto wrapping', () => {
expect(
markdownToLines(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot(`
[
[
{
"content": "Hello, how do",
"type": "normal",
},
],
[
{
"content": "you do?",
"type": "normal",
},
],
]
`);
});
test('markdownToHTML - Basic test', () => {
const input = `This is regular text
Here is a new line
@@ -262,3 +288,13 @@ test('markdownToHTML - Unsupported formatting', () => {
- l3`)
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
});
test('markdownToHTML - no auto wrapping', () => {
expect(
markdownToHTML(
`Hello, how do
you do?`,
{ markdownAutoWrap: false }
)
).toMatchInlineSnapshot('"<p>Hello,&nbsp;how&nbsp;do<br/>you&nbsp;do?</p>"');
});

View File

@@ -2,24 +2,28 @@ import type { Content } from 'mdast';
import { fromMarkdown } from 'mdast-util-from-markdown';
import { dedent } from 'ts-dedent';
import type { MarkdownLine, MarkdownWordType } from './types.js';
import type { MermaidConfig } from '../config.type.js';
/**
* @param markdown - markdown to process
* @returns processed markdown
*/
function preprocessMarkdown(markdown: string): string {
function preprocessMarkdown(markdown: string, { markdownAutoWrap }: MermaidConfig): string {
// Replace multiple newlines with a single newline
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
// Remove extra spaces at the beginning of each line
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
if (markdownAutoWrap === false) {
return withoutExtraSpaces.replace(/ /g, '&nbsp;');
}
return withoutExtraSpaces;
}
/**
* @param markdown - markdown to split into lines
*/
export function markdownToLines(markdown: string): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown);
export function markdownToLines(markdown: string, config: MermaidConfig = {}): MarkdownLine[] {
const preprocessedMarkdown = preprocessMarkdown(markdown, config);
const { children } = fromMarkdown(preprocessedMarkdown);
const lines: MarkdownLine[] = [[]];
let currentLine = 0;
@@ -56,11 +60,14 @@ export function markdownToLines(markdown: string): MarkdownLine[] {
return lines;
}
export function markdownToHTML(markdown: string) {
export function markdownToHTML(markdown: string, { markdownAutoWrap }: MermaidConfig = {}) {
const { children } = fromMarkdown(markdown);
function output(node: Content): string {
if (node.type === 'text') {
if (markdownAutoWrap === false) {
return node.value.replace(/\n/g, '<br/>').replace(/ /g, '&nbsp;');
}
return node.value.replace(/\n/g, '<br/>');
} else if (node.type === 'strong') {
return `<strong>${node.children.map(output).join('')}</strong>`;

View File

@@ -243,6 +243,9 @@ properties:
fontSize:
type: number
default: 16
markdownAutoWrap:
type: boolean
default: true
suppressErrorRendering:
type: boolean
default: false