diff --git a/cypress/platform/current.html b/cypress/platform/current.html index 7bfa3ab8d..326e3fc06 100644 --- a/cypress/platform/current.html +++ b/cypress/platform/current.html @@ -18,37 +18,28 @@

info below

-
- flowchart LR - a --> b +
+ stateDiagram-v2 - subgraph b [Test] - c --> d -->e - end + [*] --> S1 + state "Some long name" as S1: The
-
- flowchart LR - a --> b +
+ stateDiagram-v2 - subgraph id1 [Test] - a --apa--> c - b - c-->b - b-->H - end - G-->H - G-->c + [*] --> S1 + state "Some long name" as S1: The description\nwith multiple lines
-
+
flowchart LR - A{{A}}-->B{{B}}; + A{{A}}-- apa -->B{{B}}; click A callback "Tooltip" click B "http://www.github.com" "This is a link"
-
-flowchart LR - A{{A}}-->B{{B}}; +
+graph LR + A{{A}}--apa-->B{{B}};
@@ -264,7 +255,7 @@ stateDiagram-v2 // arrowMarkerAbsolute: true, // themeCSS: '.edgePath .path {stroke: red;} .arrowheadPath {fill: red;}', logLevel: 0, - flowchart: { curve: 'linear', "htmlLabels": false }, + flowchart: { curve: 'linear', "htmlLabels": true }, // gantt: { axisFormat: '%m/%d/%Y' }, sequence: { actorMargin: 50, showSequenceNumbers: true }, // sequenceDiagram: { actorMargin: 300 } // deprecated diff --git a/src/dagre-wrapper/clusters.js b/src/dagre-wrapper/clusters.js index ac13f484d..db3aee001 100644 --- a/src/dagre-wrapper/clusters.js +++ b/src/dagre-wrapper/clusters.js @@ -1,6 +1,8 @@ import intersectRect from './intersect/intersect-rect'; import { logger as log } from '../logger'; // eslint-disable-line import createLabel from './createLabel'; +import { select } from 'd3'; +import { getConfig } from '../config'; const rect = (parent, node) => { log.trace('Creating subgraph rect for ', node.id, node); @@ -17,10 +19,20 @@ const rect = (parent, node) => { // Create the label and insert it after the rect const label = shapeSvg.insert('g').attr('class', 'cluster-label'); - const text = label.node().appendChild(createLabel(node.labelText, node.labelStyle)); + const text = label + .node() + .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true)); // Get the size of the label - const bbox = text.getBBox(); + let bbox = text.getBBox(); + + if (getConfig().flowchart.htmlLabels) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } const padding = 0 * node.padding; const halfPadding = padding / 2; @@ -106,11 +118,20 @@ const roundedWithTitle = (parent, node) => { const label = shapeSvg.insert('g').attr('class', 'cluster-label'); const innerRect = shapeSvg.append('rect'); - const text = label.node().appendChild(createLabel(node.labelText, node.labelStyle)); + const text = label + .node() + .appendChild(createLabel(node.labelText, node.labelStyle, undefined, true)); // Get the size of the label - const bbox = text.getBBox(); - + let bbox = text.getBBox(); + if (getConfig().flowchart.htmlLabels) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + bbox = text.getBBox(); const padding = 0 * node.padding; const halfPadding = padding / 2; @@ -134,7 +155,7 @@ const roundedWithTitle = (parent, node) => { 'translate(' + (node.x - bbox.width / 2) + ', ' + - (node.y - node.height / 2 - node.padding / 3 + 3) + + (node.y - node.height / 2 - node.padding / 3 + (getConfig().flowchart.htmlLabels ? 5 : 3)) + ')' ); @@ -191,7 +212,7 @@ export const insertCluster = (elem, node) => { clusterElems[node.id] = shapes[shape](elem, node); }; export const getClusterTitleWidth = (elem, node) => { - const label = createLabel(node.labelText, node.labelStyle); + const label = createLabel(node.labelText, node.labelStyle, undefined, true); elem.node().appendChild(label); const width = label.getBBox().width; elem.node().removeChild(label); diff --git a/src/dagre-wrapper/createLabel.js b/src/dagre-wrapper/createLabel.js index 4c430b9f9..055f6cfb5 100644 --- a/src/dagre-wrapper/createLabel.js +++ b/src/dagre-wrapper/createLabel.js @@ -1,29 +1,123 @@ -const createLabel = (vertexText, style, isTitle) => { - const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); - svgLabel.setAttribute('style', style.replace('color:', 'fill:')); - let rows = []; - if (typeof vertexText === 'string') { - rows = vertexText.split(/\\n|\n|/gi); - } else if (Array.isArray(vertexText)) { - rows = vertexText; - } else { - rows = []; - } +import { select } from 'd3'; +import { logger } from '../logger'; // eslint-disable-line +// let vertexNode; +// if (getConfig().flowchart.htmlLabels) { +// // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? +// const node = { +// label: vertexText.replace(/fa[lrsb]?:fa-[\w-]+/g, s => ``) +// }; +// vertexNode = addHtmlLabel(svg, node).node(); +// vertexNode.parentNode.removeChild(vertexNode); +// } else { +// const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); +// svgLabel.setAttribute('style', styles.labelStyle.replace('color:', 'fill:')); - for (let j = 0; j < rows.length; j++) { - const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); - tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); - tspan.setAttribute('dy', '1em'); - tspan.setAttribute('x', '0'); - if (isTitle) { - tspan.setAttribute('class', 'title-row'); - } else { - tspan.setAttribute('class', 'row'); - } - tspan.textContent = rows[j].trim(); - svgLabel.appendChild(tspan); +// const rows = vertexText.split(common.lineBreakRegex); + +// for (let j = 0; j < rows.length; j++) { +// const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); +// tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); +// tspan.setAttribute('dy', '1em'); +// tspan.setAttribute('x', '1'); +// tspan.textContent = rows[j]; +// svgLabel.appendChild(tspan); +// } +// vertexNode = svgLabel; +// } +import { getConfig } from '../config'; + +function applyStyle(dom, styleFn) { + if (styleFn) { + dom.attr('style', styleFn); + } +} + +function addHtmlLabel(node) { + // var fo = root.append('foreignObject').attr('width', '100000'); + + // var div = fo.append('xhtml:div'); + // div.attr('xmlns', 'http://www.w3.org/1999/xhtml'); + + // var label = node.label; + // switch (typeof label) { + // case 'function': + // div.insert(label); + // break; + // case 'object': + // // Currently we assume this is a DOM object. + // div.insert(function() { + // return label; + // }); + // break; + // default: + // div.html(label); + // } + + // applyStyle(div, node.labelStyle); + // div.style('display', 'inline-block'); + // // Fix for firefox + // div.style('white-space', 'nowrap'); + + // var client = div.node().getBoundingClientRect(); + // fo.attr('width', client.width).attr('height', client.height); + const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')); + const div = fo.append('xhtml:div'); + + const label = node.label; + const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; + div.html('' + label + ''); + + applyStyle(div, node.labelStyle); + div.style('display', 'inline-block'); + // Fix for firefox + div.style('white-space', 'nowrap'); + div.attr('xmlns', 'http://www.w3.org/1999/xhtml'); + return fo.node(); +} + +const createLabel = (_vertexText, style, isTitle, isNode) => { + let vertexText = _vertexText || ''; + if (getConfig().flowchart.htmlLabels) { + // TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that? + vertexText = vertexText.replace(/\\n|\n/g, '
'); + logger.info('vertexText' + vertexText); + const node = { + isNode, + label: vertexText.replace( + /fa[lrsb]?:fa-[\w-]+/g, + s => `` + ) + }; + let vertexNode = addHtmlLabel(node); + // vertexNode.parentNode.removeChild(vertexNode); + return vertexNode; + } else { + const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + svgLabel.setAttribute('style', style.replace('color:', 'fill:')); + let rows = []; + if (typeof vertexText === 'string') { + rows = vertexText.split(/\\n|\n|/gi); + } else if (Array.isArray(vertexText)) { + rows = vertexText; + } else { + rows = []; + } + + for (let j = 0; j < rows.length; j++) { + const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + tspan.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'xml:space', 'preserve'); + tspan.setAttribute('dy', '1em'); + tspan.setAttribute('x', '0'); + if (isTitle) { + tspan.setAttribute('class', 'title-row'); + } else { + tspan.setAttribute('class', 'row'); + } + tspan.textContent = rows[j].trim(); + svgLabel.appendChild(tspan); + } + return svgLabel; } - return svgLabel; }; export default createLabel; diff --git a/src/dagre-wrapper/edges.js b/src/dagre-wrapper/edges.js index d3db26372..a53f035ac 100644 --- a/src/dagre-wrapper/edges.js +++ b/src/dagre-wrapper/edges.js @@ -1,6 +1,6 @@ import { logger } from '../logger'; // eslint-disable-line import createLabel from './createLabel'; -import { line, curveBasis } from 'd3'; +import { line, curveBasis, select } from 'd3'; import { getConfig } from '../config'; let edgeLabels = {}; @@ -21,7 +21,14 @@ export const insertEdgeLabel = (elem, edge) => { label.node().appendChild(labelElement); // Center the label - const bbox = labelElement.getBBox(); + let bbox = labelElement.getBBox(); + if (getConfig().flowchart.htmlLabels) { + const div = labelElement.children[0]; + const dv = select(labelElement); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); // Make element accessible by id for positioning diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js index c66f02be9..309c505cb 100644 --- a/src/dagre-wrapper/nodes.js +++ b/src/dagre-wrapper/nodes.js @@ -2,11 +2,12 @@ import intersect from './intersect/index.js'; import { select } from 'd3'; import { logger } from '../logger'; // eslint-disable-line import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util'; +import { getConfig } from '../config'; import createLabel from './createLabel'; import note from './shapes/note'; const question = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -28,7 +29,7 @@ const question = (parent, node) => { }; const hexagon = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const f = 4; const h = bbox.height + node.padding; @@ -53,7 +54,7 @@ const hexagon = (parent, node) => { }; const rect_left_inv_arrow = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -75,7 +76,7 @@ const rect_left_inv_arrow = (parent, node) => { return shapeSvg; }; const lean_right = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -97,7 +98,7 @@ const lean_right = (parent, node) => { }; const lean_left = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -119,7 +120,7 @@ const lean_left = (parent, node) => { }; const trapezoid = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -140,7 +141,7 @@ const trapezoid = (parent, node) => { }; const inv_trapezoid = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -160,7 +161,7 @@ const inv_trapezoid = (parent, node) => { return shapeSvg; }; const rect_right_inv_arrow = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -181,7 +182,7 @@ const rect_right_inv_arrow = (parent, node) => { return shapeSvg; }; const cylinder = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const rx = w / 2; @@ -248,7 +249,7 @@ const cylinder = (parent, node) => { }; const rect = (parent, node) => { - const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes); + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); logger.trace('Classes = ', node.classes); // add the rect @@ -296,20 +297,54 @@ const rectWithTitle = (parent, node) => { const text2 = node.labelText.flat(); logger.info('Label text', text2[0]); - const text = label.node().appendChild(createLabel(text2[0], node.labelStyle, true)); + const text = label.node().appendChild(createLabel(text2[0], node.labelStyle, true, true)); + let bbox; + if (getConfig().flowchart.htmlLabels) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + logger.info('Text 2', text2); const textRows = text2.slice(1, text2.length); let titleBox = text.getBBox(); const descr = label .node() - .appendChild(createLabel(textRows.join('
'), node.labelStyle, true)); + .appendChild(createLabel(textRows.join('
'), node.labelStyle, true, true)); - logger.info(descr); + if (getConfig().flowchart.htmlLabels) { + const div = descr.children[0]; + const dv = select(descr); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } + // bbox = label.getBBox(); + // logger.info(descr); const halfPadding = node.padding / 2; - select(descr).attr('transform', 'translate( 0' + ', ' + (titleBox.height + halfPadding) + ')'); + select(descr).attr( + 'transform', + 'translate( ' + + // (titleBox.width - bbox.width) / 2 + + (bbox.width > titleBox.width ? 0 : (titleBox.width - bbox.width) / 2) + + ', ' + + (titleBox.height + halfPadding + 5) + + ')' + ); + select(text).attr( + 'transform', + 'translate( ' + + // (titleBox.width - bbox.width) / 2 + + (bbox.width < titleBox.width ? 0 : -(titleBox.width - bbox.width) / 2) + + ', ' + + 0 + + ')' + ); // Get the size of the label // Bounding box for title and text - const bbox = label.node().getBBox(); + bbox = label.node().getBBox(); // Center the label label.attr( @@ -341,7 +376,7 @@ const rectWithTitle = (parent, node) => { }; const stadium = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const h = bbox.height + node.padding; const w = bbox.width + h / 4 + node.padding; @@ -365,7 +400,7 @@ const stadium = (parent, node) => { return shapeSvg; }; const circle = (parent, node) => { - const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node); + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); const circle = shapeSvg.insert('circle', ':first-child'); // center the circle around its coordinate @@ -386,7 +421,7 @@ const circle = (parent, node) => { }; const subroutine = (parent, node) => { - const { shapeSvg, bbox } = labelHelper(parent, node); + const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; diff --git a/src/dagre-wrapper/shapes/note.js b/src/dagre-wrapper/shapes/note.js index 34bb4356d..1fcfc1237 100644 --- a/src/dagre-wrapper/shapes/note.js +++ b/src/dagre-wrapper/shapes/note.js @@ -3,7 +3,7 @@ import { logger } from '../../logger'; // eslint-disable-line import intersect from '../intersect/index.js'; const note = (parent, node) => { - const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes); + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes, true); logger.info('Classes = ', node.classes); // add the rect diff --git a/src/dagre-wrapper/shapes/util.js b/src/dagre-wrapper/shapes/util.js index cf7ff123f..0d4055554 100644 --- a/src/dagre-wrapper/shapes/util.js +++ b/src/dagre-wrapper/shapes/util.js @@ -1,6 +1,7 @@ import createLabel from '../createLabel'; - -export const labelHelper = (parent, node, _classes) => { +import { getConfig } from '../../config'; +import { select } from 'd3'; +export const labelHelper = (parent, node, _classes, isNode) => { let classes; if (!_classes) { classes = 'node default'; @@ -16,10 +17,20 @@ export const labelHelper = (parent, node, _classes) => { // Create the label and insert it after the rect const label = shapeSvg.insert('g').attr('class', 'label'); - const text = label.node().appendChild(createLabel(node.labelText, node.labelStyle)); + const text = label + .node() + .appendChild(createLabel(node.labelText, node.labelStyle, false, isNode)); // Get the size of the label - const bbox = text.getBBox(); + let bbox = text.getBBox(); + + if (getConfig().flowchart.htmlLabels) { + const div = text.children[0]; + const dv = select(text); + bbox = div.getBoundingClientRect(); + dv.attr('width', bbox.width); + dv.attr('height', bbox.height); + } const halfPadding = node.padding / 2; diff --git a/src/diagrams/state/stateDb.js b/src/diagrams/state/stateDb.js index b5609bd40..aec95f7c4 100644 --- a/src/diagrams/state/stateDb.js +++ b/src/diagrams/state/stateDb.js @@ -148,6 +148,12 @@ export const clear = function() { root: newDoc() }; currentDocument = documents.root; + + currentDocument = documents.root; + + startCnt = 0; + endCnt = 0; // eslint-disable-line + classes = []; }; export const getState = function(id) { @@ -213,7 +219,7 @@ const getDividerId = () => { return 'divider-id-' + dividerCnt; }; -const classes = []; +let classes = []; const getClasses = () => classes; diff --git a/src/diagrams/state/stateRenderer-v2.js b/src/diagrams/state/stateRenderer-v2.js index a5f26bd17..a58bfcc60 100644 --- a/src/diagrams/state/stateRenderer-v2.js +++ b/src/diagrams/state/stateRenderer-v2.js @@ -15,7 +15,7 @@ export const setConf = function(cnf) { } }; -const nodeDb = {}; +let nodeDb = {}; /** * Returns the all the styles from classDef statements in the graph definition. @@ -203,6 +203,7 @@ const setupDoc = (g, parent, doc, altFlag) => { export const draw = function(text, id) { logger.info('Drawing state diagram (v2)', id); stateDb.clear(); + nodeDb = {}; const parser = state.parser; parser.yy = stateDb;