From e1f0e69263b091e9399af781a073160e12ca26e8 Mon Sep 17 00:00:00 2001 From: Guy Adler Date: Wed, 16 Feb 2022 23:05:46 +0100 Subject: [PATCH 1/5] feat(flowchart): implement double circle node The implementation uses two circles, inside each other. A double circle node is opend with `()(` and closed with `)()`. --- .../rendering/flowchart-v2.spec.js | 3 ++ demos/flowchart.html | 3 ++ src/dagre-wrapper/nodes.js | 39 +++++++++++++++++++ src/diagrams/flowchart/flowRenderer-v2.js | 3 ++ src/diagrams/flowchart/flowRenderer.spec.js | 1 + .../flowchart/parser/flow-singlenode.spec.js | 34 ++++++++++++++++ src/diagrams/flowchart/parser/flow.jison | 4 ++ 7 files changed, 87 insertions(+) diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index 60fba981e..b31d501c4 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -368,6 +368,7 @@ flowchart TD I{{red text}} -->|default style| J[/blue text/] K[\\ red text\\] -->|default style| L[/blue text\\] M[\\ red text/] -->|default style| N[blue text]; + O()(red text)() -->|default style| P()(blue text)(); linkStyle default color:Sienna; style A stroke:#ff0000,fill:#ffcccc,color:#ff0000; style B stroke:#0000ff,fill:#ccccff,color:#0000ff; @@ -383,6 +384,8 @@ flowchart TD style L stroke:#0000ff,fill:#ccccff,color:#0000ff; style M stroke:#ff0000,fill:#ffcccc,color:#ff0000; style N stroke:#0000ff,fill:#ccccff,color:#0000ff; + style O stroke:#ff0000,fill:#ffcccc,color:#ff0000; + style P stroke:#0000ff,fill:#ccccff,color:#0000ff; `, { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose', logLevel: 2 } ); diff --git a/demos/flowchart.html b/demos/flowchart.html index 1263a5b54..c0265baef 100644 --- a/demos/flowchart.html +++ b/demos/flowchart.html @@ -1047,6 +1047,7 @@ L1[/red text\] <-->|default style| L2[/blue text\] M1[\red text/] <-->|default style| M2[\blue text/] N1[red text] <-->|default style| N2[blue text] + O1()(red text)() <-->|default style| O2()(blue text)() linkStyle default color:Sienna; style A1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 style B1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 @@ -1062,6 +1063,7 @@ style L1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 style M1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 style N1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 + style O1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 style A2 stroke:#0000ff,fill:#ccccff,color:#0000ff style B2 stroke:#0000ff,fill:#ccccff,color:#0000ff style C2 stroke:#0000ff,fill:#ccccff,color:#0000ff @@ -1076,6 +1078,7 @@ style L2 stroke:#0000ff,fill:#ccccff,color:#0000ff style M2 stroke:#0000ff,fill:#ccccff,color:#0000ff style N2 stroke:#0000ff,fill:#ccccff,color:#0000ff + style O2 stroke:#0000ff,fill:#ccccff,color:#0000ff
diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js index 2b164ea92..b79e8853c 100644 --- a/src/dagre-wrapper/nodes.js +++ b/src/dagre-wrapper/nodes.js @@ -552,6 +552,42 @@ const circle = (parent, node) => { return shapeSvg; }; +const doublecircle = (parent, node) => { + const { shapeSvg, bbox, halfPadding } = labelHelper(parent, node, undefined, true); + const gap = 5; + const circleGroup = shapeSvg.insert('g', ':first-child'); + const outerCircle = circleGroup.insert('circle'); + const innerCircle = circleGroup.insert('circle'); + + // center the circle around its coordinate + outerCircle + .attr('style', node.style) + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('r', bbox.width / 2 + halfPadding + gap) + .attr('width', bbox.width + node.padding + gap * 2) + .attr('height', bbox.height + node.padding + gap * 2); + + innerCircle + .attr('style', node.style) + .attr('rx', node.rx) + .attr('ry', node.ry) + .attr('r', bbox.width / 2 + halfPadding) + .attr('width', bbox.width + node.padding) + .attr('height', bbox.height + node.padding); + + log.info('DoubleCircle main'); + + updateNodeBounds(node, outerCircle); + + node.intersect = function (point) { + log.info('DoubleCircle intersect', node, bbox.width / 2 + halfPadding + gap, point); + return intersect.circle(node, bbox.width / 2 + halfPadding + gap, point); + }; + + return shapeSvg; +}; + const subroutine = (parent, node) => { const { shapeSvg, bbox } = labelHelper(parent, node, undefined, true); @@ -941,6 +977,7 @@ const shapes = { rectWithTitle, choice, circle, + doublecircle, stadium, hexagon, rect_left_inv_arrow, @@ -965,6 +1002,8 @@ export const insertNode = (elem, node, dir) => { let newEl; let el; + console.log(shapes); + // Add link when appropriate if (node.link) { newEl = elem diff --git a/src/diagrams/flowchart/flowRenderer-v2.js b/src/diagrams/flowchart/flowRenderer-v2.js index b046d9c67..aa1be1e3e 100644 --- a/src/diagrams/flowchart/flowRenderer-v2.js +++ b/src/diagrams/flowchart/flowRenderer-v2.js @@ -131,6 +131,9 @@ export const addVertices = function (vert, g, svgId) { case 'group': _shape = 'rect'; break; + case 'doublecircle': + _shape = 'doublecircle'; + break; default: _shape = 'rect'; } diff --git a/src/diagrams/flowchart/flowRenderer.spec.js b/src/diagrams/flowchart/flowRenderer.spec.js index 43f1511e0..21ecad908 100644 --- a/src/diagrams/flowchart/flowRenderer.spec.js +++ b/src/diagrams/flowchart/flowRenderer.spec.js @@ -26,6 +26,7 @@ describe('the flowchart renderer', function () { ['subroutine', 'subroutine'], ['cylinder', 'cylinder'], ['group', 'rect'], + ['doublecircle', 'doublecircle'], ].forEach(function ([type, expectedShape, expectedRadios = 0]) { it(`should add the correct shaped node to the graph for vertex type ${type}`, function () { const addedNodes = []; diff --git a/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/src/diagrams/flowchart/parser/flow-singlenode.spec.js index acf0263fb..27ca2e519 100644 --- a/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -159,6 +159,40 @@ describe('[Singlenodes] when parsing', () => { expect(vert['a'].text).toBe('A
end'); }); + it('should handle a single double circle node', function () { + // Silly but syntactically correct + const res = flow.parser.parse('graph TD;a()(A)();'); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(edges.length).toBe(0); + expect(vert['a'].type).toBe('doublecircle'); + }); + + it('should handle a single double circle node with whitespace after it', function () { + // Silly but syntactically correct + const res = flow.parser.parse('graph TD;a()(A)() ;'); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(edges.length).toBe(0); + expect(vert['a'].type).toBe('doublecircle'); + }); + + it('should handle a single double circle node with html in it (SN3)', function () { + // Silly but syntactically correct + const res = flow.parser.parse('graph TD;a()(A
end)();'); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(edges.length).toBe(0); + expect(vert['a'].type).toBe('doublecircle'); + expect(vert['a'].text).toBe('A
end'); + }); + it('should handle a single node with alphanumerics starting on a char', function () { // Silly but syntactically correct const res = flow.parser.parse('graph TD;id1;'); diff --git a/src/diagrams/flowchart/parser/flow.jison b/src/diagrams/flowchart/parser/flow.jison index f07182395..5dc7e65d9 100644 --- a/src/diagrams/flowchart/parser/flow.jison +++ b/src/diagrams/flowchart/parser/flow.jison @@ -121,6 +121,8 @@ that id. "[|" return 'VERTEX_WITH_PROPS_START'; "[(" return 'CYLINDERSTART'; ")]" return 'CYLINDEREND'; +"()(" return 'DOUBLECIRCLESTART'; +")()" return 'DOUBLECIRCLEEND'; \- return 'MINUS'; "." return 'DOT'; [\_] return 'UNDERSCORE'; @@ -373,6 +375,8 @@ node: vertex vertex: idString SQS text SQE {$$ = $1;yy.addVertex($1,$3,'square');} + | idString DOUBLECIRCLESTART text DOUBLECIRCLEEND + {$$ = $1;yy.addVertex($1,$3,'doublecircle');} | idString PS PS text PE PE {$$ = $1;yy.addVertex($1,$4,'circle');} | idString '(-' text '-)' From 1fc10a7857de8f4728f014c1eed1087c3535a4af Mon Sep 17 00:00:00 2001 From: Guy Adler <44903310+Guy-Adler@users.noreply.github.com> Date: Wed, 16 Feb 2022 23:16:58 +0100 Subject: [PATCH 2/5] Update documentation Added the double circle to the list of node shapes. --- docs/flowchart.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/flowchart.md b/docs/flowchart.md index 2868008d3..39e54dd19 100644 --- a/docs/flowchart.md +++ b/docs/flowchart.md @@ -141,6 +141,13 @@ flowchart TD B[\Go shopping/] ``` +### Double circle + +```mermaid-example +flowchart TD + id1()(This is the text in the circle)() +``` + ## Links between nodes Nodes can be connected with links/edges. It is possible to have different types of links or attach a text string to a link. From 1e4be4eb6aeb3f04d687453b3a8b42521dd577ec Mon Sep 17 00:00:00 2001 From: Guy Adler <44903310+Guy-Adler@users.noreply.github.com> Date: Wed, 16 Feb 2022 23:53:08 +0100 Subject: [PATCH 3/5] removed unnecessary console.log --- src/dagre-wrapper/nodes.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js index b79e8853c..0462a8c03 100644 --- a/src/dagre-wrapper/nodes.js +++ b/src/dagre-wrapper/nodes.js @@ -1002,8 +1002,6 @@ export const insertNode = (elem, node, dir) => { let newEl; let el; - console.log(shapes); - // Add link when appropriate if (node.link) { newEl = elem From d8c7a2894e93a31bf5d7678f88c80fb10ffadb72 Mon Sep 17 00:00:00 2001 From: Guy Adler Date: Thu, 17 Feb 2022 10:37:07 +0100 Subject: [PATCH 4/5] test: removed unit test for double circle in the flowRendererv1 The feature is only available using flowRendererv2, which does not have unit tests. --- src/diagrams/flowchart/flowRenderer.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/diagrams/flowchart/flowRenderer.spec.js b/src/diagrams/flowchart/flowRenderer.spec.js index 21ecad908..43f1511e0 100644 --- a/src/diagrams/flowchart/flowRenderer.spec.js +++ b/src/diagrams/flowchart/flowRenderer.spec.js @@ -26,7 +26,6 @@ describe('the flowchart renderer', function () { ['subroutine', 'subroutine'], ['cylinder', 'cylinder'], ['group', 'rect'], - ['doublecircle', 'doublecircle'], ].forEach(function ([type, expectedShape, expectedRadios = 0]) { it(`should add the correct shaped node to the graph for vertex type ${type}`, function () { const addedNodes = []; From 04454cece04a07eca0caaf26842cdf6952985526 Mon Sep 17 00:00:00 2001 From: Guy Adler Date: Thu, 17 Feb 2022 17:22:38 +0100 Subject: [PATCH 5/5] fix: changed open and close markers as recommended chnged from `()(` and `())` to `(((` and `)))` --- cypress/integration/rendering/flowchart-v2.spec.js | 2 +- demos/flowchart.html | 2 +- docs/flowchart.md | 2 +- src/diagrams/flowchart/parser/flow-singlenode.spec.js | 6 +++--- src/diagrams/flowchart/parser/flow.jison | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cypress/integration/rendering/flowchart-v2.spec.js b/cypress/integration/rendering/flowchart-v2.spec.js index b31d501c4..10731de05 100644 --- a/cypress/integration/rendering/flowchart-v2.spec.js +++ b/cypress/integration/rendering/flowchart-v2.spec.js @@ -368,7 +368,7 @@ flowchart TD I{{red text}} -->|default style| J[/blue text/] K[\\ red text\\] -->|default style| L[/blue text\\] M[\\ red text/] -->|default style| N[blue text]; - O()(red text)() -->|default style| P()(blue text)(); + O(((red text))) -->|default style| P(((blue text))); linkStyle default color:Sienna; style A stroke:#ff0000,fill:#ffcccc,color:#ff0000; style B stroke:#0000ff,fill:#ccccff,color:#0000ff; diff --git a/demos/flowchart.html b/demos/flowchart.html index c0265baef..0ab1f6481 100644 --- a/demos/flowchart.html +++ b/demos/flowchart.html @@ -1047,7 +1047,7 @@ L1[/red text\] <-->|default style| L2[/blue text\] M1[\red text/] <-->|default style| M2[\blue text/] N1[red text] <-->|default style| N2[blue text] - O1()(red text)() <-->|default style| O2()(blue text)() + O1(((red text))) <-->|default style| O2(((blue text))) linkStyle default color:Sienna; style A1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 style B1 stroke:#ff0000,fill:#ffcccc,color:#ff0000 diff --git a/docs/flowchart.md b/docs/flowchart.md index 39e54dd19..3ad8bf3a3 100644 --- a/docs/flowchart.md +++ b/docs/flowchart.md @@ -145,7 +145,7 @@ flowchart TD ```mermaid-example flowchart TD - id1()(This is the text in the circle)() + id1(((This is the text in the circle))) ``` ## Links between nodes diff --git a/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/src/diagrams/flowchart/parser/flow-singlenode.spec.js index 27ca2e519..ee41a5c39 100644 --- a/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -161,7 +161,7 @@ describe('[Singlenodes] when parsing', () => { it('should handle a single double circle node', function () { // Silly but syntactically correct - const res = flow.parser.parse('graph TD;a()(A)();'); + const res = flow.parser.parse('graph TD;a(((A)));'); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -172,7 +172,7 @@ describe('[Singlenodes] when parsing', () => { it('should handle a single double circle node with whitespace after it', function () { // Silly but syntactically correct - const res = flow.parser.parse('graph TD;a()(A)() ;'); + const res = flow.parser.parse('graph TD;a(((A))) ;'); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -183,7 +183,7 @@ describe('[Singlenodes] when parsing', () => { it('should handle a single double circle node with html in it (SN3)', function () { // Silly but syntactically correct - const res = flow.parser.parse('graph TD;a()(A
end)();'); + const res = flow.parser.parse('graph TD;a(((A
end)));'); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); diff --git a/src/diagrams/flowchart/parser/flow.jison b/src/diagrams/flowchart/parser/flow.jison index 5dc7e65d9..79175307a 100644 --- a/src/diagrams/flowchart/parser/flow.jison +++ b/src/diagrams/flowchart/parser/flow.jison @@ -121,8 +121,8 @@ that id. "[|" return 'VERTEX_WITH_PROPS_START'; "[(" return 'CYLINDERSTART'; ")]" return 'CYLINDEREND'; -"()(" return 'DOUBLECIRCLESTART'; -")()" return 'DOUBLECIRCLEEND'; +"(((" return 'DOUBLECIRCLESTART'; +")))" return 'DOUBLECIRCLEEND'; \- return 'MINUS'; "." return 'DOT'; [\_] return 'UNDERSCORE';