diff --git a/cypress/platform/current.html b/cypress/platform/current.html index 55655dc8f..c341c70cf 100644 --- a/cypress/platform/current.html +++ b/cypress/platform/current.html @@ -31,7 +31,7 @@ G-->H G-->c -
+
flowchart LR subgraph id1 [Test] b @@ -56,21 +56,13 @@
stateDiagram-v2 - [*] --> First - - state First { - [*] --> Second - - state Second { - [*] --> second - second --> Third - - state Third { - [*] --> third - third --> [*] - } - } - } + State1: The state with a note + note right of State1 + Important information! You can write + notes. + end note + State1 --> State2 + note left of State2 : This is the note to the left.
stateDiagram-v2 diff --git a/src/dagre-wrapper/clusters.js b/src/dagre-wrapper/clusters.js index 813d2dc6e..55db0b32e 100644 --- a/src/dagre-wrapper/clusters.js +++ b/src/dagre-wrapper/clusters.js @@ -55,11 +55,47 @@ const rect = (parent, node) => { return shapeSvg; }; +/** + * Non visiable cluster where the note is group with its + */ +const noteGroup = (parent, node) => { + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', 'note-cluster') + .attr('id', node.id); + + // add the rect + const rect = shapeSvg.insert('rect', ':first-child'); + + const padding = 0 * node.padding; + const halfPadding = padding / 2; + + // center the rect around its coordinate + rect + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('x', node.x - node.width / 2 - halfPadding) + .attr('y', node.y - node.height / 2 - halfPadding) + .attr('width', node.width + padding) + .attr('height', node.height + padding) + .attr('fill', 'none'); + + const rectBox = rect.node().getBBox(); + node.width = rectBox.width; + node.height = rectBox.height; + + node.intersect = function(point) { + return intersectRect(node, point); + }; + + return shapeSvg; +}; const roundedWithTitle = (parent, node) => { // Add outer g element const shapeSvg = parent .insert('g') - .attr('class', node.class) + .attr('class', node.classes) .attr('id', node.id); // add the rect @@ -114,7 +150,7 @@ const roundedWithTitle = (parent, node) => { return shapeSvg; }; -const shapes = { rect, roundedWithTitle }; +const shapes = { rect, roundedWithTitle, noteGroup }; let clusterElems = {}; diff --git a/src/dagre-wrapper/createLabel.js b/src/dagre-wrapper/createLabel.js index 3f01e4071..b7edb1572 100644 --- a/src/dagre-wrapper/createLabel.js +++ b/src/dagre-wrapper/createLabel.js @@ -2,14 +2,14 @@ const createLabel = (vertexText, style) => { const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svgLabel.setAttribute('style', style.replace('color:', 'fill:')); - const rows = vertexText.split(//gi); + const rows = vertexText.split(/\n|/gi); 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'); - tspan.textContent = rows[j]; + tspan.textContent = rows[j].trim(); svgLabel.appendChild(tspan); } return svgLabel; diff --git a/src/dagre-wrapper/edges.js b/src/dagre-wrapper/edges.js index 41ba9ea90..ecf4254a8 100644 --- a/src/dagre-wrapper/edges.js +++ b/src/dagre-wrapper/edges.js @@ -192,7 +192,7 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { .append('path') .attr('d', lineFunction(lineData)) .attr('id', edge.id) - .attr('class', 'transition'); + .attr('class', 'transition' + (edge.classes ? ' ' + edge.classes : '')); // DEBUG code, adds a red circle at each edge coordinate // edge.points.forEach(point => { diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js index 21ffc77f4..f1db09a43 100644 --- a/src/dagre-wrapper/nodes.js +++ b/src/dagre-wrapper/nodes.js @@ -1,49 +1,8 @@ import intersect from './intersect/index.js'; import { logger } from '../logger'; // eslint-disable-line -import createLabel from './createLabel'; +import { labelHelper, updateNodeBounds, insertPolygonShape } from './shapes/util'; +import note from './shapes/note'; -const labelHelper = (parent, node) => { - // Add outer g element - const shapeSvg = parent - .insert('g') - .attr('class', 'node default') - .attr('id', node.id); - - // 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)); - - // Get the size of the label - const bbox = text.getBBox(); - - const halfPadding = node.padding / 2; - - // Center the label - label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); - - return { shapeSvg, bbox, halfPadding, label }; -}; - -const updateNodeBounds = (node, element) => { - const bbox = element.node().getBBox(); - node.width = bbox.width; - node.height = bbox.height; -}; - -function insertPolygonShape(parent, w, h, points) { - return parent - .insert('polygon', ':first-child') - .attr( - 'points', - points - .map(function(d) { - return d.x + ',' + d.y; - }) - .join(' ') - ) - .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); -} const question = (parent, node) => { const { shapeSvg, bbox } = labelHelper(parent, node); @@ -287,8 +246,9 @@ const cylinder = (parent, node) => { }; const rect = (parent, node) => { - const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node); + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, 'node ' + node.classes); + logger.info('Classes = ', node.classes); // add the rect const rect = shapeSvg.insert('rect', ':first-child'); @@ -418,7 +378,8 @@ const shapes = { rect_right_inv_arrow, cylinder, start, - end + end, + note }; let nodeElems = {}; diff --git a/src/dagre-wrapper/shapes/note.js b/src/dagre-wrapper/shapes/note.js new file mode 100644 index 000000000..34bb4356d --- /dev/null +++ b/src/dagre-wrapper/shapes/note.js @@ -0,0 +1,29 @@ +import { updateNodeBounds, labelHelper } from './util'; +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); + + logger.info('Classes = ', node.classes); + // add the rect + const rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('x', -bbox.width / 2 - halfPadding) + .attr('y', -bbox.height / 2 - halfPadding) + .attr('width', bbox.width + node.padding) + .attr('height', bbox.height + node.padding); + + updateNodeBounds(node, rect); + + node.intersect = function(point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +}; + +export default note; diff --git a/src/dagre-wrapper/shapes/util.js b/src/dagre-wrapper/shapes/util.js new file mode 100644 index 000000000..632652e16 --- /dev/null +++ b/src/dagre-wrapper/shapes/util.js @@ -0,0 +1,50 @@ +import createLabel from '../createLabel'; + +export const labelHelper = (parent, node, _classes) => { + let classes; + if (!_classes) { + classes = 'node default'; + } else { + classes = _classes; + } + // Add outer g element + const shapeSvg = parent + .insert('g') + .attr('class', classes) + .attr('id', node.id); + + // 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)); + + // Get the size of the label + const bbox = text.getBBox(); + + const halfPadding = node.padding / 2; + + // Center the label + label.attr('transform', 'translate(' + -bbox.width / 2 + ', ' + -bbox.height / 2 + ')'); + + return { shapeSvg, bbox, halfPadding, label }; +}; + +export const updateNodeBounds = (node, element) => { + const bbox = element.node().getBBox(); + node.width = bbox.width; + node.height = bbox.height; +}; + +export function insertPolygonShape(parent, w, h, points) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function(d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); +} diff --git a/src/diagrams/state/parser/stateDiagram.jison b/src/diagrams/state/parser/stateDiagram.jison index fbbce5234..b57265fc3 100644 --- a/src/diagrams/state/parser/stateDiagram.jison +++ b/src/diagrams/state/parser/stateDiagram.jison @@ -114,7 +114,7 @@ line statement : idStatement { /*console.warn('got id and descr', $1);*/$$={ stmt: 'state', id: $1, type: 'default', description: ''};} - | idStatement DESCR { /*console.warn('got id and descr', $1, $2.trim());*/$$={ stmt: 'state', id: $1, type: 'default', description: $2.trim()};} + | idStatement DESCR { /*console.warn('got id and descr', $1, $2.trim());*/$$={ stmt: 'state', id: $1, type: 'default', description: yy.trimColon($2)};} | idStatement '-->' idStatement { /*console.warn('got id', $1);yy.addRelation($1, $3);*/ diff --git a/src/diagrams/state/stateDb.js b/src/diagrams/state/stateDb.js index 3419fdad5..9b8031f75 100644 --- a/src/diagrams/state/stateDb.js +++ b/src/diagrams/state/stateDb.js @@ -180,6 +180,8 @@ export const relationType = { DEPENDENCY: 3 }; +const trimColon = str => (str && str[0] === ':' ? str.substr(1).trim() : str.trim()); + export default { addState, clear, @@ -198,5 +200,6 @@ export default { getRootDoc, setRootDoc, getRootDocV2, - extract + extract, + trimColon }; diff --git a/src/diagrams/state/stateRenderer-v2.js b/src/diagrams/state/stateRenderer-v2.js index 3418df584..76dc2f186 100644 --- a/src/diagrams/state/stateRenderer-v2.js +++ b/src/diagrams/state/stateRenderer-v2.js @@ -47,7 +47,8 @@ const setupNode = (g, parent, node, altFlag) => { nodeDb[node.id] = { id: node.id, shape, - description: node.id + description: node.id, + classes: 'statediagram-state' }; } @@ -64,6 +65,10 @@ const setupNode = (g, parent, node, altFlag) => { logger.info('Setting cluser for ', node.id); nodeDb[node.id].type = 'group'; nodeDb[node.id].shape = 'roundedWithTitle'; + nodeDb[node.id].classes = + nodeDb[node.id].classes + + ' ' + + (altFlag ? 'statediagram-cluster statediagram-cluster-alt' : 'statediagram-cluster'); } const nodeData = { @@ -72,14 +77,68 @@ const setupNode = (g, parent, node, altFlag) => { shape: nodeDb[node.id].shape, label: node.id, labelText: nodeDb[node.id].description, - class: altFlag ? 'statediagram-cluster statediagram-cluster-alt' : 'statediagram-cluster', //classStr, + classes: nodeDb[node.id].classes, //classStr, style: '', //styles.style, id: node.id, type: nodeDb[node.id].type, padding: 15 //getConfig().flowchart.padding }; - g.setNode(node.id, nodeData); + if (node.note) { + // Todo: set random id + const noteData = { + labelType: 'svg', + labelStyle: '', + shape: 'note', + label: node.id, + labelText: node.note.text, + classes: 'statediagram-note', //classStr, + style: '', //styles.style, + id: node.id + '----note', + type: nodeDb[node.id].type, + padding: 15 //getConfig().flowchart.padding + }; + const groupData = { + labelType: 'svg', + labelStyle: '', + shape: 'noteGroup', + label: node.id + '----parent', + labelText: node.note.text, + classes: nodeDb[node.id].classes, //classStr, + style: '', //styles.style, + id: node.id + '----parent', + type: 'group', + padding: 0 //getConfig().flowchart.padding + }; + g.setNode(node.id + '----parent', groupData); + + g.setNode(noteData.id, noteData); + g.setNode(node.id, nodeData); + + g.setParent(node.id, node.id + '----parent'); + g.setParent(noteData.id, node.id + '----parent'); + + let from = node.id; + let to = noteData.id; + + if (node.note.position === 'left of') { + from = noteData.id; + to = node.id; + } + g.setEdge(from, to, { + arrowhead: 'none', + arrowType: '', + style: 'fill:none', + labelStyle: '', + classes: 'note-edge', + arrowheadStyle: 'fill: #333', + labelpos: 'c', + labelType: 'text', + label: '' + }); + } else { + g.setNode(node.id, nodeData); + } } if (parent) { @@ -166,6 +225,7 @@ export const draw = function(text, id) { }); // logger.info(stateDb.getRootDoc()); + stateDb.extract(stateDb.getRootDocV2().doc); logger.info(stateDb.getRootDocV2()); setupNode(g, undefined, stateDb.getRootDocV2(), true); diff --git a/src/themes/state.scss b/src/themes/state.scss index 1e0bcc92d..aa8bd4df3 100644 --- a/src/themes/state.scss +++ b/src/themes/state.scss @@ -98,4 +98,21 @@ g.stateGroup line { .statediagram-cluster .inner { rx:0; ry:0; -} \ No newline at end of file +} + +.statediagram-state rect { + rx: 5px; + ry: 5px; +} + +.note-edge { + stroke-dasharray: 5; +} + +.statediagram-note rect { + fill: $noteBkgColor; + stroke: $noteBorderColor; + stroke-width: 1px; + rx: 0; + ry: 0; +}