Adding support for markdown string in flowchart-elk

This commit is contained in:
Knut Sveidqvist
2023-03-29 16:01:08 +02:00
parent 89193d7360
commit 4caf7d7c7b
13 changed files with 147 additions and 83 deletions

View File

@@ -57,11 +57,11 @@
</style>
</head>
<body>
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
%%
graph BT
a("`The **cat** in the hat} -- 1o --> b
graph LR
a{"`The **cat** in the hat`"} -- 1o --> b
a -- 2o --> c
a -- 3o --> d
g --2i--> a
@@ -69,39 +69,52 @@ d --1i--> a
h --3i -->a
b --> d(The dog in the hog)
c --> d
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
</pre>
</pre
>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart LR
b("`The dog in **the** hog... a a a a *very long text* about it
b("`The dog in **the** hog.(1)
NL`") --"`1o **bold**`"--> c
</pre
>
<pre id="diagram" class="mermaid">
flowchart-elk LR
b("`The dog in **the** hog.(1)
NL`") --"`1o **bold**`"--> c
</pre
>
<pre id="diagram" class="mermaid">
flowchart-elk LR
b("`The dog in **the** hog.(1).. a a a a *very long text* about it
Word!
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `") --> c
</pre
>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"htmlLabels": true}} }%%
flowchart LR
b("`The dog in **the** hog... a a a a *very long text* about it
flowchart-elk LR
b("`The dog in **the** hog(2)... a a a a *very long text* about it
Word!
Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. Another line with many, many words. `")
</pre
>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"htmlLabels": false}} }%%
flowchart LR
flowchart-elk LR
b("The dog in the hog... a very<br/>long text about it<br/>Word!")
</pre>
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"htmlLabels": true}} }%%
flowchart LR
flowchart-elk LR
b("The dog in the hog... a very<br/>long text about it<br/>Word!")
</pre>
<pre id="diagram" class="mermaid">
flowchart LR
flowchart-elk LR
subgraph "One"
a("`The **cat**
in the hat`") -- 1o --> b{{"`The **dog** in the hog`"}}
in the hat`") -- "1o" --> b{{"`The **dog** in the hog`"}}
end
subgraph "`**Two**`"
c("`The **cat**
@@ -129,7 +142,7 @@ mindmap
id2["`The dog in **the** hog... a *very long text* about it
Word!`"]
</pre>
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
%%{init: {"flowchart": {"defaultRenderer": "elk"}} }%%
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
@@ -185,7 +198,7 @@ flowchart TB
</pre
>
<br />
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
flowchart TB
%% I could not figure out how to use double quotes in labels in Mermaid
subgraph ibm[IBM Espresso CPU]
@@ -241,7 +254,7 @@ flowchart TB
>
<br />
&nbsp;
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
flowchart LR
B1 --be be--x B2
B1 --bo bo--o B3
@@ -274,7 +287,7 @@ flowchart TB
B6 --> B5
</pre
>
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
sequenceDiagram
Customer->>+Stripe: Makes a payment request
Stripe->>+Bank: Forwards the payment request to the bank
@@ -287,7 +300,7 @@ sequenceDiagram
Customer->>+Merchant: Receives goods or services
</pre
>
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
mindmap
root((mindmap))
Origins
@@ -307,7 +320,7 @@ mindmap
Mermaid
</pre>
<br />
<pre id="diagram" class="mermaid2">
<pre id="diagram" class="mermaid">
example-diagram
</pre>
@@ -332,8 +345,8 @@ mindmap
flowchart: {
// defaultRenderer: 'elk',
useMaxWidth: false,
// htmlLabels: false,
htmlLabels: false,
// htmlLabels: true,
},
htmlLabels: false,
gantt: {

View File

@@ -14,7 +14,7 @@
#### Defined in
[defaultConfig.ts:2093](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2093)
[defaultConfig.ts:2105](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L2105)
---

View File

@@ -44,7 +44,6 @@ export class Diagram {
// calls diagram.db.clear(), which would reset anything set by
// extractFrontMatter().
this.parser.parse = (text: string) => originalParse(extractFrontMatter(text, this.db));
// this.parser.parse = (text: string) => {
// console.log('parse called');
// try {

View File

@@ -385,6 +385,7 @@ export interface FlowchartDiagramConfig extends BaseDiagramConfig {
curve?: string;
padding?: number;
defaultRenderer?: string;
wrappingWidth?: number;
}
export interface FontConfig {

View File

@@ -19,7 +19,11 @@ export const insertEdgeLabel = (elem, edge) => {
// Create the actual text element
const labelElement =
edge.labelType === 'markdown'
? createText(elem, edge.label, { style: edge.labelStyle, useHtmlLabels })
? createText(elem, edge.label, {
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
})
: createLabel(edge.label, edge.labelStyle);
log.info('abc82', edge, edge.labelType);

View File

@@ -997,6 +997,7 @@ export const insertNode = (elem, node, dir) => {
el.attr('class', 'node default ' + node.class);
}
/* MC: 7e790808-9c49-4f74-93de-15c22872377f */
nodeElems[node.id] = newEl;
if (node.haveCallback) {

View File

@@ -34,7 +34,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
// text = textNode;
text = createText(label, sanitizeText(decodeEntities(labelText), getConfig()), {
useHtmlLabels: getConfig().flowchart.htmlLabels,
width: node.width || 200,
width: node.width || getConfig().flowchart.wrappingWidth,
classes: 'markdown-node-label',
});
} else {
@@ -67,7 +67,7 @@ export const labelHelper = (parent, node, _classes, isNode) => {
} else {
label.attr('transform', 'translate(' + 0 + ', ' + -bbox.height / 2 + ')');
}
label.insert('rect', ':first-child');
return { shapeSvg, bbox, halfPadding, label };
};

View File

@@ -258,6 +258,18 @@ const config: Partial<MermaidConfig> = {
* Default value: 'dagre-wrapper'
*/
defaultRenderer: 'dagre-wrapper',
/**
* | Parameter | Description | Type | Required | Values |
* | --------------- | ----------- | ------- | -------- | ----------------------- |
* | wrappingWidth | See notes | number | 4 | width of nodes where text is wrapped |
*
* **Notes:**
*
* When using markdown strings the text ius wrapped automatically, this
* value sets the max width of a text before it continues on a new line.
* Default value: 'dagre-wrapper'
*/
wrappingWidth: 200,
},
/** The object containing configurations specific for sequence diagrams */

View File

@@ -3,6 +3,7 @@ import { insertNode } from '../../../dagre-wrapper/nodes.js';
import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertEdgeLabel } from '../../../dagre-wrapper/edges.js';
import { findCommonAncestor } from './render-utils';
import { labelHelper } from '../../../dagre-wrapper/shapes/util';
import { addHtmlLabel } from 'dagre-d3-es/src/dagre-js/label/add-html-label.js';
import { getConfig } from '../../../config';
import { log } from '../../../logger';
@@ -52,7 +53,7 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' ');
}
classStr = classStr + ' flowchart-label';
const styles = getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition
@@ -61,40 +62,6 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
// We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode;
const labelData = { width: 0, height: 0 };
if (evaluate(getConfig().flowchart.htmlLabels)) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
const node = {
label: vertexText.replace(
/fa[blrs]?:fa-[\w-]+/g,
(s) => `<i class='${s.replace(':', ' ')}'></i>`
),
};
vertexNode = addHtmlLabel(svg, node).node();
const bbox = vertexNode.getBBox();
labelData.width = bbox.width;
labelData.height = bbox.height;
labelData.labelNode = vertexNode;
vertexNode.parentNode.removeChild(vertexNode);
} else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
const rows = vertexText.split(common.lineBreakRegex);
for (const row of rows) {
const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
tspan.setAttribute('dy', '1em');
tspan.setAttribute('x', '1');
tspan.textContent = row;
svgLabel.appendChild(tspan);
}
vertexNode = svgLabel;
const bbox = vertexNode.getBBox();
labelData.width = bbox.width;
labelData.height = bbox.height;
labelData.labelNode = vertexNode;
}
const ports = [
{
@@ -186,11 +153,13 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
default:
_shape = 'rect';
}
// Add the node
const node = {
labelStyle: styles.labelStyle,
shape: _shape,
labelText: vertexText,
labelType: vertex.labelType,
rx: radious,
ry: radious,
class: classStr,
@@ -209,10 +178,33 @@ export const addVertices = function (vert, svgId, root, doc, diagObj, parentLook
};
let boundingBox;
let nodeEl;
// Add the element to the DOM
if (node.type !== 'group') {
nodeEl = insertNode(nodes, node, vertex.dir);
boundingBox = nodeEl.node().getBBox();
} else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
// const rows = vertexText.split(common.lineBreakRegex);
// for (const row of rows) {
// const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
// tspan.setAttribute('dy', '1em');
// tspan.setAttribute('x', '1');
// tspan.textContent = row;
// svgLabel.appendChild(tspan);
// }
// vertexNode = svgLabel;
// const bbox = vertexNode.getBBox();
const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true);
labelData.width = bbox.width;
labelData.wrappingWidth = getConfig().flowchart.wrappingWidth;
labelData.height = bbox.height;
labelData.labelNode = shapeSvg.node();
node.labelData = labelData;
}
// const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true);
const data = {
id: vertex.id,
@@ -520,7 +512,7 @@ export const addEdges = function (edges, diagObj, graph, svg) {
edgeData.labelpos = 'c';
}
edgeData.labelType = 'text';
edgeData.labelType = edge.labelType;
edgeData.label = edge.text.replace(common.lineBreakRegex, '\n');
if (edge.style === undefined) {
@@ -845,9 +837,17 @@ export const draw = async function (text, id, _version, diagObj) {
log.info('Subgraphs - ', subGraphs);
for (let i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i];
diagObj.db.addVertex(subG.id, subG.title, 'group', undefined, subG.classes, subG.dir);
diagObj.db.addVertex(
subG.id,
{ text: subG.title, type: subG.labelType },
'group',
undefined,
subG.classes,
subG.dir
);
}
// debugger;
// Add an element in the svg to be used to hold the subgraphs container
// elements
const subGraphsEl = svg.insert('g').attr('class', 'subgraphs');
@@ -860,7 +860,7 @@ export const draw = async function (text, id, _version, diagObj) {
// in order to get the size of the node. You can't get the size of a node
// that is not in the dom so we need to add it to the dom, get the size
// we will position the nodes when we get the layout from elkjs
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg);
// Time for the edges, we start with adding an element in the node to hold the edges
const edgesEl = svg.insert('g').attr('class', 'edges edgePath');
@@ -887,6 +887,8 @@ export const draw = async function (text, id, _version, diagObj) {
},
width: node.labelData.width,
height: node.labelData.height,
// width: 100,
// height: 100,
},
];
delete node.x;
@@ -895,6 +897,7 @@ export const draw = async function (text, id, _version, diagObj) {
delete node.height;
}
});
insertChildren(graph.children, parentLookupDb);
log.info('after layout', JSON.stringify(graph, null, 2));
const g = await elk.layout(graph);

View File

@@ -81,7 +81,7 @@ const getStyles = (options: FlowChartStyleOptions) =>
.edgeLabel {
background-color: ${options.edgeLabelBackground};
rect {
opacity: 0.5;
opacity: 0.85;
background-color: ${options.edgeLabelBackground};
fill: ${options.edgeLabelBackground};
}
@@ -132,6 +132,11 @@ const getStyles = (options: FlowChartStyleOptions) =>
// fill:#ccc;
// // stroke:black;
// }
.flowchart-label text {
text-anchor: middle;
}
${genSections(options)}
`;

View File

@@ -143,6 +143,7 @@ export const addSingleLink = function (_start, _end, type) {
// log.info('Got edge...', start, end);
const edge = { start: start, end: end, type: undefined, text: '', labelType: 'text' };
log.info('abc78 Got edge...', edge);
const linkTextObj = type.text;
if (linkTextObj !== undefined) {
@@ -163,6 +164,7 @@ export const addSingleLink = function (_start, _end, type) {
edges.push(edge);
};
export const addLink = function (_start, _end, type) {
log.info('addLink (abc78)', _start, _end, type);
let i, j;
for (i = 0; i < _start.length; i++) {
for (j = 0; j < _end.length; j++) {

View File

@@ -440,7 +440,7 @@ arrowText:
text: textToken
{ $$={text:$1, type: 'text'};}
| text textToken
{$$={text:$1.text+''+$2, type: 'text'};}
{ $$={text:$1.text+''+$2, type: $1.type};}
| STR
{ $$={text: $1, type: 'text'};}
| MD_STR

View File

@@ -83,11 +83,13 @@ function createTspan(textElement, lineIndex, lineHeight) {
* @param {number} width - The maximum allowed width of the text.
* @param {object} g - The parent group element to append the formatted text.
* @param {Array} structuredText - The structured text data to format.
* @param addBackground
*/
function createFormattedText(width, g, structuredText) {
function createFormattedText(width, g, structuredText, addBackground = false) {
const lineHeight = 1.1;
const textElement = g.append('text').attr('y', '-10.1');
const labelGroup = g.append('g');
let bkg = labelGroup.insert('rect').attr('class', 'background');
const textElement = labelGroup.append('text').attr('y', '-10.1');
// .attr('dominant-baseline', 'middle')
// .attr('text-anchor', 'middle');
// .attr('text-anchor', 'middle');
@@ -118,8 +120,20 @@ function createFormattedText(width, g, structuredText) {
}
}
});
if (addBackground) {
const bbox = textElement.node().getBBox();
const padding = 2;
bkg
.attr('x', -padding)
.attr('y', -padding)
.attr('width', bbox.width + 2 * padding)
.attr('height', bbox.height + 2 * padding);
// .style('fill', 'red');
return labelGroup.node();
} else {
return textElement.node();
// return g.node();
}
}
/**
@@ -183,9 +197,17 @@ function updateTextContentAndStyles(tspan, wrappedLine) {
export const createText = (
el,
text = '',
{ style = '', isTitle = false, classes = '', useHtmlLabels = true, isNode = true, width } = {}
{
style = '',
isTitle = false,
classes = '',
useHtmlLabels = true,
isNode = true,
width,
addSvgBackground = false,
} = {}
) => {
log.info('createText', text, style, isTitle, classes, useHtmlLabels, isNode);
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 />');
@@ -203,6 +225,8 @@ export const createText = (
return vertexNode;
} else {
const structuredText = markdownToLines(text);
return createFormattedText(width, el, structuredText);
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
return svgLabel;
}
};