diff --git a/cypress/integration/rendering/marker_unique_id.spec.js b/cypress/integration/rendering/marker_unique_id.spec.js new file mode 100644 index 0000000000..617189db05 --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('should render a blue arrow tip in second digram', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/platform/marker_unique_id.html b/cypress/platform/marker_unique_id.html new file mode 100644 index 0000000000..e49169c556 --- /dev/null +++ b/cypress/platform/marker_unique_id.html @@ -0,0 +1,52 @@ + + + +

Example

+
+      %%{init:{"theme":"base", "themeVariables": {"lineColor":"red"}}}%%
+      flowchart LR
+      subgraph red
+      A --> B
+      end
+    
+
+      %%{init:{"theme":"base", "themeVariables": {"lineColor":"blue"}}}%%
+      flowchart LR
+      subgraph black
+      A --> B
+      end
+    
+
+      ---
+      config:
+        theme: base
+        themeVariables:
+          lineColor: yellow
+      ---
+      flowchart LR
+      subgraph red
+      A --> B
+      end
+    
+
+      ---
+      config:
+        theme: base
+        themeVariables:
+          lineColor: green
+      ---
+      flowchart LR
+      subgraph black
+      A --> B
+      end
+    
+ + + diff --git a/packages/mermaid/src/dagre-wrapper/edges.js b/packages/mermaid/src/dagre-wrapper/edges.js index a47d5f4741..1b3e172c01 100644 --- a/packages/mermaid/src/dagre-wrapper/edges.js +++ b/packages/mermaid/src/dagre-wrapper/edges.js @@ -369,7 +369,7 @@ const cutPathAtIntersect = (_points, boundryNode) => { return points; }; -export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph) { +export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph, id) { let points = edge.points; let pointsHasChanged = false; const tail = graph.node(e.v); @@ -508,61 +508,103 @@ export const insertEdge = function (elem, e, edge, clusterDb, diagramType, graph switch (edge.arrowTypeStart) { case 'arrow_cross': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-crossStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-crossStart' + ')' + ); break; case 'arrow_point': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-pointStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-pointStart' + ')' + ); break; case 'arrow_barb': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-barbStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-barbStart' + ')' + ); break; case 'arrow_circle': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-circleStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-circleStart' + ')' + ); break; case 'aggregation': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-aggregationStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-aggregationStart' + ')' + ); break; case 'extension': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-extensionStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-extensionStart' + ')' + ); break; case 'composition': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-compositionStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-compositionStart' + ')' + ); break; case 'dependency': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-dependencyStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-dependencyStart' + ')' + ); break; case 'lollipop': - svgPath.attr('marker-start', 'url(' + url + '#' + diagramType + '-lollipopStart' + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '_' + diagramType + '-lollipopStart' + ')' + ); break; default: } switch (edge.arrowTypeEnd) { case 'arrow_cross': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-crossEnd' + ')'); + svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-crossEnd' + ')'); break; case 'arrow_point': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-pointEnd' + ')'); + svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-pointEnd' + ')'); break; case 'arrow_barb': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-barbEnd' + ')'); + svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-barbEnd' + ')'); break; case 'arrow_circle': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-circleEnd' + ')'); + svgPath.attr('marker-end', 'url(' + url + '#' + id + '_' + diagramType + '-circleEnd' + ')'); break; case 'aggregation': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-aggregationEnd' + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '_' + diagramType + '-aggregationEnd' + ')' + ); break; case 'extension': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-extensionEnd' + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '_' + diagramType + '-extensionEnd' + ')' + ); break; case 'composition': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-compositionEnd' + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '_' + diagramType + '-compositionEnd' + ')' + ); break; case 'dependency': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-dependencyEnd' + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '_' + diagramType + '-dependencyEnd' + ')' + ); break; case 'lollipop': - svgPath.attr('marker-end', 'url(' + url + '#' + diagramType + '-lollipopEnd' + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '_' + diagramType + '-lollipopEnd' + ')' + ); break; default: } diff --git a/packages/mermaid/src/dagre-wrapper/index.js b/packages/mermaid/src/dagre-wrapper/index.js index 279c5d9ddd..9843adb8b0 100644 --- a/packages/mermaid/src/dagre-wrapper/index.js +++ b/packages/mermaid/src/dagre-wrapper/index.js @@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters.js'; import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges.js'; import { log } from '../logger.js'; -const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => { +const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster) => { log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster); const dir = graph.graph().rankdir; log.trace('Dir in recursive render - dir:', dir); @@ -52,7 +52,7 @@ const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => { if (node && node.clusterNode) { // const children = graph.children(v); log.info('Cluster identified', v, node.width, graph.node(v)); - const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v)); + const o = await recursiveRender(nodes, node.graph, diagramtype, id, graph.node(v)); const newEl = o.elem; updateNodeBounds(node, newEl); node.diff = o.diff || 0; @@ -134,7 +134,7 @@ const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => { const edge = graph.edge(e); log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge); - const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph); + const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph, id); positionEdgeLabel(edge, paths); }); @@ -159,7 +159,7 @@ export const render = async (elem, graph, markers, diagramtype, id) => { adjustClustersAndEdges(graph); log.warn('Graph after:', JSON.stringify(graphlibJson.write(graph))); // log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph)); - await recursiveRender(elem, graph, diagramtype); + await recursiveRender(elem, graph, diagramtype, id); }; // const shapeDefinitions = {}; diff --git a/packages/mermaid/src/dagre-wrapper/markers.js b/packages/mermaid/src/dagre-wrapper/markers.js index 223e682540..91f7ecc80a 100644 --- a/packages/mermaid/src/dagre-wrapper/markers.js +++ b/packages/mermaid/src/dagre-wrapper/markers.js @@ -14,7 +14,7 @@ const extension = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-extensionStart') + .attr('id', id + '_' + type + '-extensionStart') .attr('class', 'marker extension ' + type) .attr('refX', 18) .attr('refY', 7) @@ -27,7 +27,7 @@ const extension = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-extensionEnd') + .attr('id', id + '_' + type + '-extensionEnd') .attr('class', 'marker extension ' + type) .attr('refX', 1) .attr('refY', 7) @@ -38,11 +38,11 @@ const extension = (elem, type, id) => { .attr('d', 'M 1,1 V 13 L18,7 Z'); // this is actual shape for arrowhead }; -const composition = (elem, type) => { +const composition = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-compositionStart') + .attr('id', id + '_' + type + '-compositionStart') .attr('class', 'marker composition ' + type) .attr('refX', 18) .attr('refY', 7) @@ -55,7 +55,7 @@ const composition = (elem, type) => { elem .append('defs') .append('marker') - .attr('id', type + '-compositionEnd') + .attr('id', id + '_' + type + '-compositionEnd') .attr('class', 'marker composition ' + type) .attr('refX', 1) .attr('refY', 7) @@ -65,11 +65,11 @@ const composition = (elem, type) => { .append('path') .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); }; -const aggregation = (elem, type) => { +const aggregation = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-aggregationStart') + .attr('id', id + '_' + type + '-aggregationStart') .attr('class', 'marker aggregation ' + type) .attr('refX', 18) .attr('refY', 7) @@ -82,7 +82,7 @@ const aggregation = (elem, type) => { elem .append('defs') .append('marker') - .attr('id', type + '-aggregationEnd') + .attr('id', id + '_' + type + '-aggregationEnd') .attr('class', 'marker aggregation ' + type) .attr('refX', 1) .attr('refY', 7) @@ -92,11 +92,11 @@ const aggregation = (elem, type) => { .append('path') .attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z'); }; -const dependency = (elem, type) => { +const dependency = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-dependencyStart') + .attr('id', id + '_' + type + '-dependencyStart') .attr('class', 'marker dependency ' + type) .attr('refX', 6) .attr('refY', 7) @@ -109,7 +109,7 @@ const dependency = (elem, type) => { elem .append('defs') .append('marker') - .attr('id', type + '-dependencyEnd') + .attr('id', id + '_' + type + '-dependencyEnd') .attr('class', 'marker dependency ' + type) .attr('refX', 13) .attr('refY', 7) @@ -119,11 +119,11 @@ const dependency = (elem, type) => { .append('path') .attr('d', 'M 18,7 L9,13 L14,7 L9,1 Z'); }; -const lollipop = (elem, type) => { +const lollipop = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-lollipopStart') + .attr('id', id + '_' + type + '-lollipopStart') .attr('class', 'marker lollipop ' + type) .attr('refX', 13) .attr('refY', 7) @@ -140,7 +140,7 @@ const lollipop = (elem, type) => { elem .append('defs') .append('marker') - .attr('id', type + '-lollipopEnd') + .attr('id', id + '_' + type + '-lollipopEnd') .attr('class', 'marker lollipop ' + type) .attr('refX', 1) .attr('refY', 7) @@ -154,10 +154,10 @@ const lollipop = (elem, type) => { .attr('cy', 7) .attr('r', 6); }; -const point = (elem, type) => { +const point = (elem, type, id) => { elem .append('marker') - .attr('id', type + '-pointEnd') + .attr('id', id + '_' + type + '-pointEnd') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') .attr('refX', 6) @@ -173,7 +173,7 @@ const point = (elem, type) => { .style('stroke-dasharray', '1,0'); elem .append('marker') - .attr('id', type + '-pointStart') + .attr('id', id + '_' + type + '-pointStart') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') .attr('refX', 4.5) @@ -188,10 +188,10 @@ const point = (elem, type) => { .style('stroke-width', 1) .style('stroke-dasharray', '1,0'); }; -const circle = (elem, type) => { +const circle = (elem, type, id) => { elem .append('marker') - .attr('id', type + '-circleEnd') + .attr('id', id + '_' + type + '-circleEnd') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') .attr('refX', 11) @@ -210,7 +210,7 @@ const circle = (elem, type) => { elem .append('marker') - .attr('id', type + '-circleStart') + .attr('id', id + '_' + type + '-circleStart') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') .attr('refX', -1) @@ -227,10 +227,10 @@ const circle = (elem, type) => { .style('stroke-width', 1) .style('stroke-dasharray', '1,0'); }; -const cross = (elem, type) => { +const cross = (elem, type, id) => { elem .append('marker') - .attr('id', type + '-crossEnd') + .attr('id', id + '_' + type + '-crossEnd') .attr('class', 'marker cross ' + type) .attr('viewBox', '0 0 11 11') .attr('refX', 12) @@ -248,7 +248,7 @@ const cross = (elem, type) => { elem .append('marker') - .attr('id', type + '-crossStart') + .attr('id', id + '_' + type + '-crossStart') .attr('class', 'marker cross ' + type) .attr('viewBox', '0 0 11 11') .attr('refX', -1) @@ -264,11 +264,11 @@ const cross = (elem, type) => { .style('stroke-width', 2) .style('stroke-dasharray', '1,0'); }; -const barb = (elem, type) => { +const barb = (elem, type, id) => { elem .append('defs') .append('marker') - .attr('id', type + '-barbEnd') + .attr('id', id + '_' + type + '-barbEnd') .attr('refX', 19) .attr('refY', 7) .attr('markerWidth', 20)