Merge pull request #4268 from Valentine14th/bug/4023-image-rendering

fix: image rendering in nodes
This commit is contained in:
Justin Greywolf 2023-04-10 10:09:40 -07:00 committed by GitHub
commit 727bf30824
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 351 additions and 289 deletions

View File

@ -685,6 +685,16 @@ A ~~~ B
{ titleTopMargin: 0 } { titleTopMargin: 0 }
); );
}); });
it('4023: Should render html labels with images and-or text correctly', () => {
imgSnapshotTest(
`flowchart TD
B[<img src='https://mermaid.js.org/mermaid-logo.svg'>]
B-->C[<img src="https://mermaid.js.org/mermaid-logo.svg"> more text <img src='https://mermaid.js.org/mermaid-logo.svg'>]
B-->D(<img src='https://mermaid.js.org/mermaid-logo.svg'> some text)
B-->E(plain)`,
{}
);
});
describe('Markdown strings flowchart (#4220)', () => { describe('Markdown strings flowchart (#4220)', () => {
describe('html labels', () => { describe('html labels', () => {
it('With styling and classes', () => { it('With styling and classes', () => {

View File

@ -96,7 +96,7 @@ mermaid.initialize(config);
#### Defined in #### Defined in
[mermaidAPI.ts:667](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L667) [mermaidAPI.ts:673](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/mermaidAPI.ts#L673)
## Functions ## Functions

View File

@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges'; import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges';
import { log } from '../logger'; import { log } from '../logger';
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => { const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster); log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir; const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir); log.trace('Dir in recursive render - dir:', dir);
@ -35,44 +35,46 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
// Insert nodes, this will insert them into the dom and each node will get a size. The size is updated // Insert nodes, this will insert them into the dom and each node will get a size. The size is updated
// to the abstract node and is later used by dagre for the layout // to the abstract node and is later used by dagre for the layout
graph.nodes().forEach(function (v) { await Promise.all(
const node = graph.node(v); graph.nodes().map(async function (v) {
if (parentCluster !== undefined) { const node = graph.node(v);
const data = JSON.parse(JSON.stringify(parentCluster.clusterData)); if (parentCluster !== undefined) {
// data.clusterPositioning = true; const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster); // data.clusterPositioning = true;
graph.setNode(parentCluster.id, data); log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
if (!graph.parent(v)) { graph.setNode(parentCluster.id, data);
log.trace('Setting parent', v, parentCluster.id); if (!graph.parent(v)) {
graph.setParent(v, parentCluster.id, data); log.trace('Setting parent', v, parentCluster.id);
graph.setParent(v, parentCluster.id, data);
}
} }
} log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v))); if (node && node.clusterNode) {
if (node && node.clusterNode) { // const children = graph.children(v);
// const children = graph.children(v); log.info('Cluster identified', v, node.width, graph.node(v));
log.info('Cluster identified', v, node.width, graph.node(v)); const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
const o = recursiveRender(nodes, node.graph, diagramtype, graph.node(v)); const newEl = o.elem;
const newEl = o.elem; updateNodeBounds(node, newEl);
updateNodeBounds(node, newEl); node.diff = o.diff || 0;
node.diff = o.diff || 0; log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y);
log.info('Node bounds (abc123)', v, node, node.width, node.x, node.y); setNodeElem(newEl, node);
setNodeElem(newEl, node);
log.warn('Recursive render complete ', newEl, node); log.warn('Recursive render complete ', newEl, node);
} else {
if (graph.children(v).length > 0) {
// This is a cluster but not to be rendered recursively
// Render as before
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
// insertCluster(clusters, graph.node(v));
} else { } else {
log.info('Node - the non recursive path', v, node.id, node); if (graph.children(v).length > 0) {
insertNode(nodes, graph.node(v), dir); // This is a cluster but not to be rendered recursively
// Render as before
log.info('Cluster - the non recursive path XXX', v, node.id, node, graph);
log.info(findNonClusterChild(node.id, graph));
clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
// insertCluster(clusters, graph.node(v));
} else {
log.info('Node - the non recursive path', v, node.id, node);
await insertNode(nodes, graph.node(v), dir);
}
} }
} })
}); );
// Insert labels, this will insert them into the dom so that the width can be calculated // Insert labels, this will insert them into the dom so that the width can be calculated
// Also figure out which edges point to/from clusters and adjust them accordingly // Also figure out which edges point to/from clusters and adjust them accordingly
@ -146,7 +148,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
return { elem, diff }; return { elem, diff };
}; };
export const render = (elem, graph, markers, diagramtype, id) => { export const render = async (elem, graph, markers, diagramtype, id) => {
insertMarkers(elem, markers, diagramtype, id); insertMarkers(elem, markers, diagramtype, id);
clearNodes(); clearNodes();
clearEdges(); clearEdges();
@ -157,7 +159,7 @@ export const render = (elem, graph, markers, diagramtype, id) => {
adjustClustersAndEdges(graph); adjustClustersAndEdges(graph);
log.warn('Graph after:', graphlibJson.write(graph)); log.warn('Graph after:', graphlibJson.write(graph));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph)); // log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
recursiveRender(elem, graph, diagramtype); await recursiveRender(elem, graph, diagramtype);
}; };
// const shapeDefinitions = {}; // const shapeDefinitions = {};

View File

@ -8,8 +8,8 @@ import note from './shapes/note';
import { parseMember } from '../diagrams/class/svgDraw'; import { parseMember } from '../diagrams/class/svgDraw';
import { evaluate } from '../diagrams/common/common'; import { evaluate } from '../diagrams/common/common';
const question = (parent, node) => { const question = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -69,8 +69,8 @@ const choice = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const hexagon = (parent, node) => { const hexagon = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const f = 4; const f = 4;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -96,8 +96,8 @@ const hexagon = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_left_inv_arrow = (parent, node) => { const rect_left_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -122,8 +122,8 @@ const rect_left_inv_arrow = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_right = (parent, node) => { const lean_right = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -145,8 +145,8 @@ const lean_right = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const lean_left = (parent, node) => { const lean_left = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -168,8 +168,8 @@ const lean_left = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const trapezoid = (parent, node) => { const trapezoid = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -191,8 +191,8 @@ const trapezoid = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const inv_trapezoid = (parent, node) => { const inv_trapezoid = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -214,8 +214,8 @@ const inv_trapezoid = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect_right_inv_arrow = (parent, node) => { const rect_right_inv_arrow = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -238,8 +238,8 @@ const rect_right_inv_arrow = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const cylinder = (parent, node) => { const cylinder = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const rx = w / 2; const rx = w / 2;
@ -310,8 +310,13 @@ const cylinder = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const rect = (parent, node) => { const rect = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(
parent,
node,
'node ' + node.classes,
true
);
// add the rect // add the rect
const rect = shapeSvg.insert('rect', ':first-child'); const rect = shapeSvg.insert('rect', ':first-child');
@ -352,8 +357,8 @@ const rect = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const labelRect = (parent, node) => { const labelRect = async (parent, node) => {
const { shapeSvg } = labelHelper(parent, node, 'label', true); const { shapeSvg } = await labelHelper(parent, node, 'label', true);
log.trace('Classes = ', node.classes); log.trace('Classes = ', node.classes);
// add the rect // add the rect
@ -539,8 +544,8 @@ const rectWithTitle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const stadium = (parent, node) => { const stadium = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
const w = bbox.width + h / 4 + node.padding; const w = bbox.width + h / 4 + node.padding;
@ -565,8 +570,8 @@ const stadium = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const circle = (parent, node) => { const circle = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const circle = shapeSvg.insert('circle', ':first-child'); const circle = shapeSvg.insert('circle', ':first-child');
// center the circle around its coordinate // center the circle around its coordinate
@ -590,8 +595,8 @@ const circle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const doublecircle = (parent, node) => { const doublecircle = async (parent, node) => {
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(parent, node, undefined, true);
const gap = 5; const gap = 5;
const circleGroup = shapeSvg.insert('g', ':first-child'); const circleGroup = shapeSvg.insert('g', ':first-child');
const outerCircle = circleGroup.insert('circle'); const outerCircle = circleGroup.insert('circle');
@ -626,8 +631,8 @@ const doublecircle = (parent, node) => {
return shapeSvg; return shapeSvg;
}; };
const subroutine = (parent, node) => { const subroutine = async (parent, node) => {
const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const { shapeSvg, bbox } = await labelHelper(parent, node, undefined, true);
const w = bbox.width + node.padding; const w = bbox.width + node.padding;
const h = bbox.height + node.padding; const h = bbox.height + node.padding;
@ -976,7 +981,7 @@ const shapes = {
let nodeElems = {}; let nodeElems = {};
export const insertNode = (elem, node, dir) => { export const insertNode = async (elem, node, dir) => {
let newEl; let newEl;
let el; let el;
@ -989,9 +994,9 @@ export const insertNode = (elem, node, dir) => {
target = node.linkTarget || '_blank'; target = node.linkTarget || '_blank';
} }
newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target); newEl = elem.insert('svg:a').attr('xlink:href', node.link).attr('target', target);
el = shapes[node.shape](newEl, node, dir); el = await shapes[node.shape](newEl, node, dir);
} else { } else {
el = shapes[node.shape](elem, node, dir); el = await shapes[node.shape](elem, node, dir);
newEl = el; newEl = el;
} }
if (node.tooltip) { if (node.tooltip) {
@ -1017,6 +1022,7 @@ export const clear = () => {
export const positionNode = (node) => { export const positionNode = (node) => {
const el = nodeElems[node.id]; const el = nodeElems[node.id];
log.trace( log.trace(
'Transforming node', 'Transforming node',
node.diff, node.diff,

View File

@ -3,12 +3,17 @@ import { log } from '../../logger';
import { getConfig } from '../../config'; import { getConfig } from '../../config';
import intersect from '../intersect/index.js'; import intersect from '../intersect/index.js';
const note = (parent, node) => { const note = async (parent, node) => {
const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels; const useHtmlLabels = node.useHtmlLabels || getConfig().flowchart.htmlLabels;
if (!useHtmlLabels) { if (!useHtmlLabels) {
node.centerLabel = true; node.centerLabel = true;
} }
const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); const { shapeSvg, bbox, halfPadding } = await labelHelper(
parent,
node,
'node ' + node.classes,
true
);
log.info('Classes = ', node.classes); log.info('Classes = ', node.classes);
// add the rect // add the rect

View File

@ -4,7 +4,8 @@ import { getConfig } from '../../config';
import { decodeEntities } from '../../mermaidAPI'; import { decodeEntities } from '../../mermaidAPI';
import { select } from 'd3'; import { select } from 'd3';
import { evaluate, sanitizeText } from '../../diagrams/common/common'; import { evaluate, sanitizeText } from '../../diagrams/common/common';
export const labelHelper = (parent, node, _classes, isNode) => {
export const labelHelper = async (parent, node, _classes, isNode) => {
let classes; let classes;
const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels); const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig().flowchart.htmlLabels);
if (!_classes) { if (!_classes) {
@ -51,17 +52,47 @@ export const labelHelper = (parent, node, _classes, isNode) => {
// Get the size of the label // Get the size of the label
let bbox = text.getBBox(); let bbox = text.getBBox();
const halfPadding = node.padding / 2;
if (evaluate(getConfig().flowchart.htmlLabels)) { if (evaluate(getConfig().flowchart.htmlLabels)) {
const div = text.children[0]; const div = text.children[0];
const dv = select(text); const dv = select(text);
// if there are images, need to wait for them to load before getting the bounding box
const images = div.getElementsByTagName('img');
if (images) {
const noImgText = labelText.replace(/<img[^>]*>/g, '').trim() === '';
await Promise.all(
[...images].map(
(img) =>
new Promise((res) =>
img.addEventListener('load', function () {
img.style.display = 'flex';
img.style.flexDirection = 'column';
if (noImgText) {
// default size if no text
const bodyFontSize = getConfig().fontSize
? getConfig().fontSize
: window.getComputedStyle(document.body).fontSize;
const enlargingFactor = 5;
img.style.width = parseInt(bodyFontSize, 10) * enlargingFactor + 'px';
} else {
img.style.width = '100%';
}
res(img);
})
)
)
);
}
bbox = div.getBoundingClientRect(); bbox = div.getBoundingClientRect();
dv.attr('width', bbox.width); dv.attr('width', bbox.width);
dv.attr('height', bbox.height); dv.attr('height', bbox.height);
} }
const halfPadding = node.padding / 2;
// Center the label // Center the label
if (useHtmlLabels) { if (useHtmlLabels) {
label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')');

View File

@ -248,7 +248,7 @@ export const setConf = function (cnf: any) {
* @param _version - * @param _version -
* @param diagObj - * @param diagObj -
*/ */
export const draw = function (text: string, id: string, _version: string, diagObj: any) { export const draw = async function (text: string, id: string, _version: string, diagObj: any) {
log.info('Drawing class - ', id); log.info('Drawing class - ', id);
// TODO V10: Why flowchart? Might be a mistake when copying. // TODO V10: Why flowchart? Might be a mistake when copying.
@ -300,7 +300,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
// @ts-ignore Ignore type error for now // @ts-ignore Ignore type error for now
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render( await render(
element, element,
g, g,
['aggregation', 'extension', 'composition', 'dependency', 'lollipop'], ['aggregation', 'extension', 'composition', 'dependency', 'lollipop'],

View File

@ -35,229 +35,231 @@ let nodeDb = {};
// * @param doc // * @param doc
// * @param diagObj // * @param diagObj
// */ // */
export const addVertices = function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) { export const addVertices = async function (vert, svgId, root, doc, diagObj, parentLookupDb, graph) {
const svg = root.select(`[id="${svgId}"]`); const svg = root.select(`[id="${svgId}"]`);
const nodes = svg.insert('g').attr('class', 'nodes'); const nodes = svg.insert('g').attr('class', 'nodes');
const keys = Object.keys(vert); const keys = Object.keys(vert);
// Iterate through each item in the vertex object (containing all the vertices found) in the graph definition // Iterate through each item in the vertex object (containing all the vertices found) in the graph definition
keys.forEach(function (id) { await Promise.all(
const vertex = vert[id]; keys.map(async function (id) {
const vertex = vert[id];
/** /**
* Variable for storing the classes for the vertex * Variable for storing the classes for the vertex
* *
* @type {string} * @type {string}
*/ */
let classStr = 'default'; let classStr = 'default';
if (vertex.classes.length > 0) { if (vertex.classes.length > 0) {
classStr = vertex.classes.join(' '); classStr = vertex.classes.join(' ');
} }
classStr = classStr + ' flowchart-label'; classStr = classStr + ' flowchart-label';
const styles = getStylesFromArray(vertex.styles); const styles = getStylesFromArray(vertex.styles);
// Use vertex id as text in the box if no text is provided by the graph definition // Use vertex id as text in the box if no text is provided by the graph definition
let vertexText = vertex.text !== undefined ? vertex.text : vertex.id; let vertexText = vertex.text !== undefined ? vertex.text : vertex.id;
// We create a SVG label, either by delegating to addHtmlLabel or manually // We create a SVG label, either by delegating to addHtmlLabel or manually
let vertexNode; let vertexNode;
const labelData = { width: 0, height: 0 }; const labelData = { width: 0, height: 0 };
const ports = [ const ports = [
{ {
id: vertex.id + '-west', id: vertex.id + '-west',
layoutOptions: { layoutOptions: {
'port.side': 'WEST', 'port.side': 'WEST',
},
}, },
}, {
{ id: vertex.id + '-east',
id: vertex.id + '-east', layoutOptions: {
layoutOptions: { 'port.side': 'EAST',
'port.side': 'EAST', },
}, },
}, {
{ id: vertex.id + '-south',
id: vertex.id + '-south', layoutOptions: {
layoutOptions: { 'port.side': 'SOUTH',
'port.side': 'SOUTH', },
}, },
}, {
{ id: vertex.id + '-north',
id: vertex.id + '-north', layoutOptions: {
layoutOptions: { 'port.side': 'NORTH',
'port.side': 'NORTH', },
}, },
}, ];
];
let radious = 0; let radious = 0;
let _shape = ''; let _shape = '';
let layoutOptions = {}; let layoutOptions = {};
// Set the shape based parameters // Set the shape based parameters
switch (vertex.type) { switch (vertex.type) {
case 'round': case 'round':
radious = 5; radious = 5;
_shape = 'rect'; _shape = 'rect';
break; break;
case 'square': case 'square':
_shape = 'rect'; _shape = 'rect';
break; break;
case 'diamond': case 'diamond':
_shape = 'question'; _shape = 'question';
layoutOptions = { layoutOptions = {
portConstraints: 'FIXED_SIDE', portConstraints: 'FIXED_SIDE',
}; };
break; break;
case 'hexagon': case 'hexagon':
_shape = 'hexagon'; _shape = 'hexagon';
break; break;
case 'odd': case 'odd':
_shape = 'rect_left_inv_arrow'; _shape = 'rect_left_inv_arrow';
break; break;
case 'lean_right': case 'lean_right':
_shape = 'lean_right'; _shape = 'lean_right';
break; break;
case 'lean_left': case 'lean_left':
_shape = 'lean_left'; _shape = 'lean_left';
break; break;
case 'trapezoid': case 'trapezoid':
_shape = 'trapezoid'; _shape = 'trapezoid';
break; break;
case 'inv_trapezoid': case 'inv_trapezoid':
_shape = 'inv_trapezoid'; _shape = 'inv_trapezoid';
break; break;
case 'odd_right': case 'odd_right':
_shape = 'rect_left_inv_arrow'; _shape = 'rect_left_inv_arrow';
break; break;
case 'circle': case 'circle':
_shape = 'circle'; _shape = 'circle';
break; break;
case 'ellipse': case 'ellipse':
_shape = 'ellipse'; _shape = 'ellipse';
break; break;
case 'stadium': case 'stadium':
_shape = 'stadium'; _shape = 'stadium';
break; break;
case 'subroutine': case 'subroutine':
_shape = 'subroutine'; _shape = 'subroutine';
break; break;
case 'cylinder': case 'cylinder':
_shape = 'cylinder'; _shape = 'cylinder';
break; break;
case 'group': case 'group':
_shape = 'rect'; _shape = 'rect';
break; break;
case 'doublecircle': case 'doublecircle':
_shape = 'doublecircle'; _shape = 'doublecircle';
break; break;
default: default:
_shape = 'rect'; _shape = 'rect';
} }
// Add the node // Add the node
const node = { const node = {
labelStyle: styles.labelStyle, labelStyle: styles.labelStyle,
shape: _shape, shape: _shape,
labelText: vertexText, labelText: vertexText,
labelType: vertex.labelType, labelType: vertex.labelType,
rx: radious, rx: radious,
ry: radious, ry: radious,
class: classStr, class: classStr,
style: styles.style, style: styles.style,
id: vertex.id, id: vertex.id,
link: vertex.link, link: vertex.link,
linkTarget: vertex.linkTarget, linkTarget: vertex.linkTarget,
tooltip: diagObj.db.getTooltip(vertex.id) || '', tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id), domId: diagObj.db.lookUpDomId(vertex.id),
haveCallback: vertex.haveCallback, haveCallback: vertex.haveCallback,
width: vertex.type === 'group' ? 500 : undefined, width: vertex.type === 'group' ? 500 : undefined,
dir: vertex.dir, dir: vertex.dir,
type: vertex.type, type: vertex.type,
props: vertex.props, props: vertex.props,
padding: getConfig().flowchart.padding, padding: getConfig().flowchart.padding,
}; };
let boundingBox; let boundingBox;
let nodeEl; let nodeEl;
// Add the element to the DOM // Add the element to the DOM
if (node.type !== 'group') { if (node.type !== 'group') {
nodeEl = insertNode(nodes, node, vertex.dir); nodeEl = insertNode(nodes, node, vertex.dir);
boundingBox = nodeEl.node().getBBox(); boundingBox = nodeEl.node().getBBox();
} else { } else {
const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text'); const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text');
// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); // svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:'));
// const rows = vertexText.split(common.lineBreakRegex); // const rows = vertexText.split(common.lineBreakRegex);
// for (const row of rows) { // for (const row of rows) {
// const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan'); // const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan');
// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); // tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve');
// tspan.setAttribute('dy', '1em'); // tspan.setAttribute('dy', '1em');
// tspan.setAttribute('x', '1'); // tspan.setAttribute('x', '1');
// tspan.textContent = row; // tspan.textContent = row;
// svgLabel.appendChild(tspan); // svgLabel.appendChild(tspan);
// }
// vertexNode = svgLabel;
// const bbox = vertexNode.getBBox();
const { shapeSvg, bbox } = await 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 } = await labelHelper(svg, node, undefined, true);
const data = {
id: vertex.id,
ports: vertex.type === 'diamond' ? ports : [],
// labelStyle: styles.labelStyle,
// shape: _shape,
layoutOptions,
labelText: vertexText,
labelData,
// labels: [{ text: vertexText }],
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// link: vertex.link,
// linkTarget: vertex.linkTarget,
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
// haveCallback: vertex.haveCallback,
width: boundingBox?.width,
height: boundingBox?.height,
// dir: vertex.dir,
type: vertex.type,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// boundingBox,
el: nodeEl,
parent: parentLookupDb.parentById[vertex.id],
};
// if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
// graph.children.push({
// ...data,
// });
// } // }
// vertexNode = svgLabel; nodeDb[node.id] = data;
// const bbox = vertexNode.getBBox(); // log.trace('setNode', {
const { shapeSvg, bbox } = labelHelper(nodes, node, undefined, true); // labelStyle: styles.labelStyle,
labelData.width = bbox.width; // shape: _shape,
labelData.wrappingWidth = getConfig().flowchart.wrappingWidth; // labelText: vertexText,
labelData.height = bbox.height; // rx: radius,
labelData.labelNode = shapeSvg.node(); // ry: radius,
node.labelData = labelData; // class: classStr,
} // style: styles.style,
// const { shapeSvg, bbox } = labelHelper(svg, node, undefined, true); // id: vertex.id,
// domId: diagObj.db.lookUpDomId(vertex.id),
const data = { // width: vertex.type === 'group' ? 500 : undefined,
id: vertex.id, // type: vertex.type,
ports: vertex.type === 'diamond' ? ports : [], // dir: vertex.dir,
// labelStyle: styles.labelStyle, // props: vertex.props,
// shape: _shape, // padding: getConfig().flowchart.padding,
layoutOptions, // parent: parentLookupDb.parentById[vertex.id],
labelText: vertexText, // });
labelData, })
// labels: [{ text: vertexText }], );
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// link: vertex.link,
// linkTarget: vertex.linkTarget,
// tooltip: diagObj.db.getTooltip(vertex.id) || '',
domId: diagObj.db.lookUpDomId(vertex.id),
// haveCallback: vertex.haveCallback,
width: boundingBox?.width,
height: boundingBox?.height,
// dir: vertex.dir,
type: vertex.type,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// boundingBox,
el: nodeEl,
parent: parentLookupDb.parentById[vertex.id],
};
// if (!Object.keys(parentLookupDb.childrenById).includes(vertex.id)) {
// graph.children.push({
// ...data,
// });
// }
nodeDb[node.id] = data;
// log.trace('setNode', {
// labelStyle: styles.labelStyle,
// shape: _shape,
// labelText: vertexText,
// rx: radius,
// ry: radius,
// class: classStr,
// style: styles.style,
// id: vertex.id,
// domId: diagObj.db.lookUpDomId(vertex.id),
// width: vertex.type === 'group' ? 500 : undefined,
// type: vertex.type,
// dir: vertex.dir,
// props: vertex.props,
// padding: getConfig().flowchart.padding,
// parent: parentLookupDb.parentById[vertex.id],
// });
});
return graph; return graph;
}; };
@ -861,7 +863,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 // 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 // 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 // we will position the nodes when we get the layout from elkjs
graph = addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph, svg); graph = await addVertices(vert, id, root, doc, diagObj, parentLookupDb, graph);
// Time for the edges, we start with adding an element in the node to hold the edges // 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'); const edgesEl = svg.insert('g').attr('class', 'edges edgePath');

View File

@ -362,7 +362,7 @@ export const getClasses = function (text, diagObj) {
* @param id * @param id
*/ */
export const draw = function (text, id, _version, diagObj) { export const draw = async function (text, id, _version, diagObj) {
log.info('Drawing flowchart'); log.info('Drawing flowchart');
diagObj.db.clear(); diagObj.db.clear();
flowDb.setGen('gen-2'); flowDb.setGen('gen-2');
@ -451,7 +451,7 @@ export const draw = function (text, id, _version, diagObj) {
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id); await render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle()); utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());

View File

@ -382,7 +382,7 @@ const getDir = (parsedItem, defaultDir = DEFAULT_NESTED_DOC_DIR) => {
* @param _version * @param _version
* @param diag * @param diag
*/ */
export const draw = function (text, id, _version, diag) { export const draw = async function (text, id, _version, diag) {
log.info('Drawing state diagram (v2)', id); log.info('Drawing state diagram (v2)', id);
// diag.sb.clear(); // diag.sb.clear();
nodeDb = {}; nodeDb = {};
@ -436,7 +436,7 @@ export const draw = function (text, id, _version, diag) {
// Run the renderer. This is what draws the final graph. // Run the renderer. This is what draws the final graph.
const element = root.select('#' + id + ' g'); const element = root.select('#' + id + ' g');
render(element, g, ['barb'], CSS_DIAGRAM, id); await render(element, g, ['barb'], CSS_DIAGRAM, id);
const padding = 8; const padding = 8;

View File

@ -406,6 +406,12 @@ const render = async function (
// clean up text CRLFs // clean up text CRLFs
text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;; text = text.replace(/\r\n?/g, '\n'); // parser problems on CRLF ignore all CR and leave LF;;
// clean up html tags so that all attributes use single quotes, parser throws error on double quotes
text = text.replace(
/<(\w+)([^>]*)>/g,
(match, tag, attributes) => '<' + tag + attributes.replace(/="([^"]*)"/g, "='$1'") + '>'
);
const idSelector = '#' + id; const idSelector = '#' + id;
const iFrameID = 'i' + id; const iFrameID = 'i' + id;
const iFrameID_selector = '#' + iFrameID; const iFrameID_selector = '#' + iFrameID;