diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 885868c167..c27b9decd6 120000 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -./packages/mermaid/src/docs/community/contributing.md \ No newline at end of file +./packages/mermaid/src/docs/community/contributing.md diff --git a/cypress/integration/rendering/marker_unique_id_c4.spec.js b/cypress/integration/rendering/marker_unique_id_c4.spec.js new file mode 100644 index 0000000000..65254f29da --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_c4.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show an arrow on each and of the link', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_c4.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/integration/rendering/marker_unique_id_er.spec.js b/cypress/integration/rendering/marker_unique_id_er.spec.js new file mode 100644 index 0000000000..60895d42e8 --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_er.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show a marker on each end of each link', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_er.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/integration/rendering/marker_unique_id_journey.spec.js b/cypress/integration/rendering/marker_unique_id_journey.spec.js new file mode 100644 index 0000000000..f66578332e --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_journey.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show the arrow pointer (pointing right)', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_journey.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/integration/rendering/marker_unique_id_requirement.spec.js b/cypress/integration/rendering/marker_unique_id_requirement.spec.js new file mode 100644 index 0000000000..7fa986a282 --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_requirement.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show an arrow and a cross markers on the links', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_requirement.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/integration/rendering/marker_unique_id_sequence.spec.js b/cypress/integration/rendering/marker_unique_id_sequence.spec.js new file mode 100644 index 0000000000..444d28347e --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_sequence.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show circles behind the sequence numbers, and three types of pointers (▶, ≻, ×)', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_sequence.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/integration/rendering/marker_unique_id_timeline.spec.js b/cypress/integration/rendering/marker_unique_id_timeline.spec.js new file mode 100644 index 0000000000..bb11e86638 --- /dev/null +++ b/cypress/integration/rendering/marker_unique_id_timeline.spec.js @@ -0,0 +1,10 @@ +import { urlSnapshotTest } from '../../helpers/util.ts'; + +describe('Marker Unique IDs Per Diagram', () => { + it('Should show the arrow pointers (pointing right and down)', () => { + urlSnapshotTest('http://localhost:9000/marker_unique_id_timeline.html', { + logLevel: 1, + flowchart: { htmlLabels: false }, + }); + }); +}); diff --git a/cypress/platform/marker_unique_id_c4.html b/cypress/platform/marker_unique_id_c4.html new file mode 100644 index 0000000000..a29a26fe3f --- /dev/null +++ b/cypress/platform/marker_unique_id_c4.html @@ -0,0 +1,27 @@ + + + +
+
+        C4Context
+        Person(A, "A")
+        System(B, "B")
+        BiRel(A, B, "")
+      
+
+
+      C4Context
+      Person(A, "A")
+      System(B, "B")
+      BiRel(A, B, "")
+    
+ + + diff --git a/cypress/platform/marker_unique_id_er.html b/cypress/platform/marker_unique_id_er.html new file mode 100644 index 0000000000..68ada55217 --- /dev/null +++ b/cypress/platform/marker_unique_id_er.html @@ -0,0 +1,29 @@ + + + +
+
+        erDiagram
+        START_OO ||--|| END_OO : ONLY_ONE
+        START_ZOO |o--o| END_ZOO : ZERO_OR_ONE
+        START_ZOM }o--o{ END_ZOM : ZERO_OR_MORE
+        START_OOM }|--|{ END_OOM : ONE_OR_MORE
+      
+
+
+      erDiagram
+      START_OO ||--|| END_OO : ONLY_ONE
+      START_ZOO |o--o| END_ZOO : ZERO_OR_ONE
+      START_ZOM }o--o{ END_ZOM : ZERO_OR_MORE
+      START_OOM }|--|{ END_OOM : ONE_OR_MORE
+    
+ + + diff --git a/cypress/platform/marker_unique_id_journey.html b/cypress/platform/marker_unique_id_journey.html new file mode 100644 index 0000000000..f0cf4291ca --- /dev/null +++ b/cypress/platform/marker_unique_id_journey.html @@ -0,0 +1,27 @@ + + + +
+
+        journey
+        title My working day
+        section Go to work
+        Make tea: 5: Me
+      
+
+
+      journey
+      title My working day
+      section Go to work
+      Make tea: 5: Me
+    
+ + + diff --git a/cypress/platform/marker_unique_id_requirement.html b/cypress/platform/marker_unique_id_requirement.html new file mode 100644 index 0000000000..83a0c07280 --- /dev/null +++ b/cypress/platform/marker_unique_id_requirement.html @@ -0,0 +1,37 @@ + + + +
+
+        requirementDiagram
+        element test_entity {
+        }
+        requirement test_req {
+        }
+        performanceRequirement test_req_perf {
+        }
+        test_entity - satisfies -> test_req
+        test_req - contains -> test_req_perf
+      
+
+
+      requirementDiagram
+      element test_entity {
+      }
+      requirement test_req {
+      }
+      performanceRequirement test_req_perf {
+      }
+      test_entity - satisfies -> test_req
+      test_req - contains -> test_req_perf
+    
+ + + diff --git a/cypress/platform/marker_unique_id_sequence.html b/cypress/platform/marker_unique_id_sequence.html new file mode 100644 index 0000000000..4eba30bb7f --- /dev/null +++ b/cypress/platform/marker_unique_id_sequence.html @@ -0,0 +1,29 @@ + + + +
+
+        sequenceDiagram
+        autonumber
+        A ->> B: arrowhead
+        A -) B: filled-head
+        A -x B: crosshead
+      
+
+
+      sequenceDiagram
+      autonumber
+      A ->> B: arrowhead
+      A -) B: filled-head
+      A -x B: crosshead
+    
+ + + diff --git a/cypress/platform/marker_unique_id_timeline.html b/cypress/platform/marker_unique_id_timeline.html new file mode 100644 index 0000000000..1700ba947d --- /dev/null +++ b/cypress/platform/marker_unique_id_timeline.html @@ -0,0 +1,23 @@ + + + +
+
+        timeline
+        2000 : Event
+      
+
+
+      timeline
+      2000 : Event
+    
+ + + diff --git a/packages/mermaid/src/diagrams/c4/c4Renderer.js b/packages/mermaid/src/diagrams/c4/c4Renderer.js index 58dd808fda..9d332dca4c 100644 --- a/packages/mermaid/src/diagrams/c4/c4Renderer.js +++ b/packages/mermaid/src/diagrams/c4/c4Renderer.js @@ -406,7 +406,7 @@ let getIntersectPoints = function (fromNode, endNode) { return { startPoint: startPoint, endPoint: endPoint }; }; -export const drawRels = function (diagram, rels, getC4ShapeObj, diagObj) { +export const drawRels = function (diagram, id, rels, getC4ShapeObj, diagObj) { let i = 0; for (let rel of rels) { i = i + 1; @@ -435,7 +435,7 @@ export const drawRels = function (diagram, rels, getC4ShapeObj, diagObj) { rel.startPoint = points.startPoint; rel.endPoint = points.endPoint; } - svgDraw.drawRels(diagram, rels, conf); + svgDraw.drawRels(diagram, id, rels, conf); }; /** @@ -604,9 +604,9 @@ export const draw = function (_text, id, _version, diagObj) { const diagram = securityLevel === 'sandbox' ? root.select(`[id="${id}"]`) : select(`[id="${id}"]`); - svgDraw.insertComputerIcon(diagram); - svgDraw.insertDatabaseIcon(diagram); - svgDraw.insertClockIcon(diagram); + svgDraw.insertComputerIcon(diagram, id); + svgDraw.insertDatabaseIcon(diagram, id); + svgDraw.insertClockIcon(diagram, id); let screenBounds = new Bounds(diagObj); @@ -630,12 +630,12 @@ export const draw = function (_text, id, _version, diagObj) { // } // The arrow head definition is attached to the svg once - svgDraw.insertArrowHead(diagram); - svgDraw.insertArrowEnd(diagram); - svgDraw.insertArrowCrossHead(diagram); - svgDraw.insertArrowFilledHead(diagram); + svgDraw.insertArrowHead(diagram, id); + svgDraw.insertArrowEnd(diagram, id); + svgDraw.insertArrowCrossHead(diagram, id); + svgDraw.insertArrowFilledHead(diagram, id); - drawRels(diagram, diagObj.db.getRels(), diagObj.db.getC4Shape, diagObj); + drawRels(diagram, id, diagObj.db.getRels(), diagObj.db.getC4Shape, diagObj); screenBounds.data.stopx = globalBoundaryMaxX; screenBounds.data.stopy = globalBoundaryMaxY; diff --git a/packages/mermaid/src/diagrams/c4/svgDraw.js b/packages/mermaid/src/diagrams/c4/svgDraw.js index 6bff267f6f..22619e7320 100644 --- a/packages/mermaid/src/diagrams/c4/svgDraw.js +++ b/packages/mermaid/src/diagrams/c4/svgDraw.js @@ -16,7 +16,7 @@ export const drawImage = function (elem, width, height, x, y, link) { imageElem.attr('xlink:href', sanitizedLink); }; -export const drawRels = (elem, rels, conf) => { +export const drawRels = (elem, id, rels, conf) => { const relsElem = elem.append('g'); let i = 0; for (let rel of rels) { @@ -25,7 +25,6 @@ export const drawRels = (elem, rels, conf) => { let offsetX = rel.offsetX ? parseInt(rel.offsetX) : 0; let offsetY = rel.offsetY ? parseInt(rel.offsetY) : 0; - let url = ''; if (i === 0) { let line = relsElem.append('line'); line.attr('x1', rel.startPoint.x); @@ -37,10 +36,10 @@ export const drawRels = (elem, rels, conf) => { line.attr('stroke', strokeColor); line.style('fill', 'none'); if (rel.type !== 'rel_b') { - line.attr('marker-end', 'url(' + url + '#arrowhead)'); + line.attr('marker-end', 'url(#' + id + '-arrowhead)'); } if (rel.type === 'birel' || rel.type === 'rel_b') { - line.attr('marker-start', 'url(' + url + '#arrowend)'); + line.attr('marker-start', 'url(#' + id + '-arrowend)'); } i = -1; } else { @@ -65,10 +64,10 @@ export const drawRels = (elem, rels, conf) => { .replaceAll('stopy', rel.endPoint.y) ); if (rel.type !== 'rel_b') { - line.attr('marker-end', 'url(' + url + '#arrowhead)'); + line.attr('marker-end', 'url(#' + id + '-arrowhead)'); } if (rel.type === 'birel' || rel.type === 'rel_b') { - line.attr('marker-start', 'url(' + url + '#arrowend)'); + line.attr('marker-start', 'url(#' + id + '-arrowend)'); } } @@ -397,11 +396,12 @@ export const drawC4Shape = function (elem, c4Shape, conf) { return c4Shape.height; }; -export const insertDatabaseIcon = function (elem) { +export const insertDatabaseIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'database') + .attr('id', id + '-database') + .attr('class', 'mermaid-marker-c4-database') .attr('fill-rule', 'evenodd') .attr('clip-rule', 'evenodd') .append('path') @@ -412,11 +412,12 @@ export const insertDatabaseIcon = function (elem) { ); }; -export const insertComputerIcon = function (elem) { +export const insertComputerIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'computer') + .attr('id', id + '-computer') + .attr('class', 'mermaid-marker-c4-computer') .attr('width', '24') .attr('height', '24') .append('path') @@ -427,11 +428,12 @@ export const insertComputerIcon = function (elem) { ); }; -export const insertClockIcon = function (elem) { +export const insertClockIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'clock') + .attr('id', id + '-clock') + .attr('class', 'mermaid-marker-c4-clock') .attr('width', '24') .attr('height', '24') .append('path') @@ -447,11 +449,12 @@ export const insertClockIcon = function (elem) { * * @param elem */ -export const insertArrowHead = function (elem) { +export const insertArrowHead = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'arrowhead') + .attr('id', id + '-arrowhead') + .attr('class', 'mermaid-marker-c4-arrowhead') .attr('refX', 9) .attr('refY', 5) .attr('markerUnits', 'userSpaceOnUse') @@ -462,11 +465,12 @@ export const insertArrowHead = function (elem) { .attr('d', 'M 0 0 L 10 5 L 0 10 z'); // this is actual shape for arrowhead }; -export const insertArrowEnd = function (elem) { +export const insertArrowEnd = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'arrowend') + .attr('id', id + '-arrowend') + .attr('class', 'mermaid-marker-c4-arrowend') .attr('refX', 1) .attr('refY', 5) .attr('markerUnits', 'userSpaceOnUse') @@ -482,11 +486,12 @@ export const insertArrowEnd = function (elem) { * * @param {any} elem */ -export const insertArrowFilledHead = function (elem) { +export const insertArrowFilledHead = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'filled-head') + .attr('id', id + '-filled-head') + .attr('class', 'mermaid-marker-c4-filled-head') .attr('refX', 18) .attr('refY', 7) .attr('markerWidth', 20) @@ -501,11 +506,12 @@ export const insertArrowFilledHead = function (elem) { * * @param {any} elem */ -export const insertDynamicNumber = function (elem) { +export const insertDynamicNumber = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'sequencenumber') + .attr('id', id + '-sequencenumber') + .attr('class', 'mermaid-marker-c4-sequencenumber') .attr('refX', 15) .attr('refY', 15) .attr('markerWidth', 60) @@ -522,12 +528,14 @@ export const insertDynamicNumber = function (elem) { * Setup arrow head and define the marker. The result is appended to the svg. * * @param {any} elem + * @param {any} id */ -export const insertArrowCrossHead = function (elem) { +export const insertArrowCrossHead = function (elem, id) { const defs = elem.append('defs'); const marker = defs .append('marker') - .attr('id', 'crosshead') + .attr('id', id + '-crosshead') + .attr('class', 'mermaid-marker-c4-crosshead') .attr('markerWidth', 15) .attr('markerHeight', 8) .attr('orient', 'auto') diff --git a/packages/mermaid/src/diagrams/er/erMarkers.js b/packages/mermaid/src/diagrams/er/erMarkers.js index 48cafae657..407512f935 100644 --- a/packages/mermaid/src/diagrams/er/erMarkers.js +++ b/packages/mermaid/src/diagrams/er/erMarkers.js @@ -16,14 +16,16 @@ const ERMarkers = { * * @param elem * @param conf + * @param id */ -const insertMarkers = function (elem, conf) { +const insertMarkers = function (elem, conf, id) { let marker; elem .append('defs') .append('marker') - .attr('id', ERMarkers.MD_PARENT_START) + .attr('id', id + '-' + ERMarkers.MD_PARENT_START) + .attr('class', 'mermaid-marker-er-MD_PARENT_START') .attr('refX', 0) .attr('refY', 7) .attr('markerWidth', 190) @@ -35,7 +37,8 @@ const insertMarkers = function (elem, conf) { elem .append('defs') .append('marker') - .attr('id', ERMarkers.MD_PARENT_END) + .attr('id', id + '-' + ERMarkers.MD_PARENT_END) + .attr('class', 'mermaid-marker-er-MD_PARENT_END') .attr('refX', 19) .attr('refY', 7) .attr('markerWidth', 20) @@ -47,7 +50,8 @@ const insertMarkers = function (elem, conf) { elem .append('defs') .append('marker') - .attr('id', ERMarkers.ONLY_ONE_START) + .attr('id', id + '-' + ERMarkers.ONLY_ONE_START) + .attr('class', 'mermaid-marker-er-ONLY_ONE_START') .attr('refX', 0) .attr('refY', 9) .attr('markerWidth', 18) @@ -61,7 +65,8 @@ const insertMarkers = function (elem, conf) { elem .append('defs') .append('marker') - .attr('id', ERMarkers.ONLY_ONE_END) + .attr('id', id + '-' + ERMarkers.ONLY_ONE_END) + .attr('class', 'mermaid-marker-er-ONLY_ONE_END') .attr('refX', 18) .attr('refY', 9) .attr('markerWidth', 18) @@ -75,7 +80,8 @@ const insertMarkers = function (elem, conf) { marker = elem .append('defs') .append('marker') - .attr('id', ERMarkers.ZERO_OR_ONE_START) + .attr('id', id + '-' + ERMarkers.ZERO_OR_ONE_START) + .attr('class', 'mermaid-marker-er-ZERO_OR_ONE_START') .attr('refX', 0) .attr('refY', 9) .attr('markerWidth', 30) @@ -93,7 +99,8 @@ const insertMarkers = function (elem, conf) { marker = elem .append('defs') .append('marker') - .attr('id', ERMarkers.ZERO_OR_ONE_END) + .attr('id', id + '-' + ERMarkers.ZERO_OR_ONE_END) + .attr('class', 'mermaid-marker-er-ZERO_OR_ONE_END') .attr('refX', 30) .attr('refY', 9) .attr('markerWidth', 30) @@ -111,7 +118,8 @@ const insertMarkers = function (elem, conf) { elem .append('defs') .append('marker') - .attr('id', ERMarkers.ONE_OR_MORE_START) + .attr('id', id + '-' + ERMarkers.ONE_OR_MORE_START) + .attr('class', 'mermaid-marker-er-ONE_OR_MORE_START') .attr('refX', 18) .attr('refY', 18) .attr('markerWidth', 45) @@ -125,7 +133,8 @@ const insertMarkers = function (elem, conf) { elem .append('defs') .append('marker') - .attr('id', ERMarkers.ONE_OR_MORE_END) + .attr('id', id + '-' + ERMarkers.ONE_OR_MORE_END) + .attr('class', 'mermaid-marker-er-ONE_OR_MORE_END') .attr('refX', 27) .attr('refY', 18) .attr('markerWidth', 45) @@ -139,7 +148,8 @@ const insertMarkers = function (elem, conf) { marker = elem .append('defs') .append('marker') - .attr('id', ERMarkers.ZERO_OR_MORE_START) + .attr('id', id + '-' + ERMarkers.ZERO_OR_MORE_START) + .attr('class', 'mermaid-marker-er-ZERO_OR_MORE_START') .attr('refX', 18) .attr('refY', 18) .attr('markerWidth', 57) @@ -161,7 +171,8 @@ const insertMarkers = function (elem, conf) { marker = elem .append('defs') .append('marker') - .attr('id', ERMarkers.ZERO_OR_MORE_END) + .attr('id', id + '-' + ERMarkers.ZERO_OR_MORE_END) + .attr('class', 'mermaid-marker-er-ZERO_OR_MORE_END') .attr('refX', 39) .attr('refY', 18) .attr('markerWidth', 57) diff --git a/packages/mermaid/src/diagrams/er/erRenderer.js b/packages/mermaid/src/diagrams/er/erRenderer.js index 0327bfc9d5..95d1020e1f 100644 --- a/packages/mermaid/src/diagrams/er/erRenderer.js +++ b/packages/mermaid/src/diagrams/er/erRenderer.js @@ -415,7 +415,7 @@ let relCnt = 0; * sit 'behind' opaque entity boxes) * @param diagObj */ -const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { +const drawRelationshipFromLayout = function (svg, id, rel, g, insert, diagObj) { relCnt++; // Find the edge relating to this relationship @@ -467,19 +467,34 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { // Note that the 'A' entity's marker is at the end of the relationship and the 'B' entity's marker is at the start switch (rel.relSpec.cardA) { case diagObj.db.Cardinality.ZERO_OR_ONE: - svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_END + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ZERO_OR_ONE_END + ')' + ); break; case diagObj.db.Cardinality.ZERO_OR_MORE: - svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_END + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ZERO_OR_MORE_END + ')' + ); break; case diagObj.db.Cardinality.ONE_OR_MORE: - svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_END + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ONE_OR_MORE_END + ')' + ); break; case diagObj.db.Cardinality.ONLY_ONE: - svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_END + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ONLY_ONE_END + ')' + ); break; case diagObj.db.Cardinality.MD_PARENT: - svgPath.attr('marker-end', 'url(' + url + '#' + erMarkers.ERMarkers.MD_PARENT_END + ')'); + svgPath.attr( + 'marker-end', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.MD_PARENT_END + ')' + ); break; } @@ -487,26 +502,32 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { case diagObj.db.Cardinality.ZERO_OR_ONE: svgPath.attr( 'marker-start', - 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_ONE_START + ')' + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ZERO_OR_ONE_START + ')' ); break; case diagObj.db.Cardinality.ZERO_OR_MORE: svgPath.attr( 'marker-start', - 'url(' + url + '#' + erMarkers.ERMarkers.ZERO_OR_MORE_START + ')' + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ZERO_OR_MORE_START + ')' ); break; case diagObj.db.Cardinality.ONE_OR_MORE: svgPath.attr( 'marker-start', - 'url(' + url + '#' + erMarkers.ERMarkers.ONE_OR_MORE_START + ')' + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ONE_OR_MORE_START + ')' ); break; case diagObj.db.Cardinality.ONLY_ONE: - svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.ONLY_ONE_START + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.ONLY_ONE_START + ')' + ); break; case diagObj.db.Cardinality.MD_PARENT: - svgPath.attr('marker-start', 'url(' + url + '#' + erMarkers.ERMarkers.MD_PARENT_START + ')'); + svgPath.attr( + 'marker-start', + 'url(' + url + '#' + id + '-' + erMarkers.ERMarkers.MD_PARENT_START + ')' + ); break; } @@ -585,7 +606,7 @@ export const draw = function (text, id, _version, diagObj) { const svg = root.select(`[id='${id}']`); // Add cardinality marker definitions to the svg - erMarkers.insertMarkers(svg, conf); + erMarkers.insertMarkers(svg, conf, id); // Now we have to construct the diagram in a specific way: // --- @@ -642,7 +663,7 @@ export const draw = function (text, id, _version, diagObj) { // Draw the relationships relationships.forEach(function (rel) { - drawRelationshipFromLayout(svg, rel, g, firstEntity, diagObj); + drawRelationshipFromLayout(svg, id, rel, g, firstEntity, diagObj); }); const padding = conf.diagramPadding; diff --git a/packages/mermaid/src/diagrams/er/styles.js b/packages/mermaid/src/diagrams/er/styles.js index 08ea2e8510..697294750e 100644 --- a/packages/mermaid/src/diagrams/er/styles.js +++ b/packages/mermaid/src/diagrams/er/styles.js @@ -33,12 +33,12 @@ const getStyles = (options) => font-size: 18px; fill: ${options.textColor}; } - #MD_PARENT_START { + .mermaid-marker-er-MD_PARENT_START { fill: #f5f5f5 !important; stroke: ${options.lineColor} !important; stroke-width: 1; } - #MD_PARENT_END { + .mermaid-marker-er-MD_PARENT_END { fill: #f5f5f5 !important; stroke: ${options.lineColor} !important; stroke-width: 1; diff --git a/packages/mermaid/src/diagrams/requirement/requirementMarkers.js b/packages/mermaid/src/diagrams/requirement/requirementMarkers.js index 96e42d78da..42daadbd40 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementMarkers.js +++ b/packages/mermaid/src/diagrams/requirement/requirementMarkers.js @@ -3,11 +3,12 @@ const ReqMarkers = { ARROW: 'arrow', }; -const insertLineEndings = (parentNode, conf) => { +const insertLineEndings = (parentNode, id, conf) => { let containsNode = parentNode .append('defs') .append('marker') - .attr('id', ReqMarkers.CONTAINS + '_line_ending') + .attr('id', id + '-' + ReqMarkers.CONTAINS + '_line_ending') + .attr('class', 'mermaid-marker-req-' + ReqMarkers.CONTAINS + '_line_ending') .attr('refX', 0) .attr('refY', conf.line_height / 2) .attr('markerWidth', conf.line_height) @@ -45,7 +46,8 @@ const insertLineEndings = (parentNode, conf) => { parentNode .append('defs') .append('marker') - .attr('id', ReqMarkers.ARROW + '_line_ending') + .attr('id', id + '-' + ReqMarkers.ARROW + '_line_ending') + .attr('class', 'mermaid-marker-req-' + ReqMarkers.ARROW + '_line_ending') .attr('refX', conf.line_height) .attr('refY', 0.5 * conf.line_height) .attr('markerWidth', conf.line_height) diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js index 778dc36b1c..9f4dfd291c 100644 --- a/packages/mermaid/src/diagrams/requirement/requirementRenderer.js +++ b/packages/mermaid/src/diagrams/requirement/requirementRenderer.js @@ -148,7 +148,7 @@ const addEdgeLabel = (parentNode, svgPath, conf, txt) => { .attr('fill-opacity', '85%'); }; -const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { +const drawRelationshipFromLayout = function (svg, id, rel, g, insert, diagObj) { // Find the edge relating to this relationship const edge = g.edge(elementString(rel.src), elementString(rel.dst)); @@ -171,7 +171,14 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { if (rel.type == diagObj.db.Relationships.CONTAINS) { svgPath.attr( 'marker-start', - 'url(' + common.getUrl(conf.arrowMarkerAbsolute) + '#' + rel.type + '_line_ending' + ')' + 'url(' + + common.getUrl(conf.arrowMarkerAbsolute) + + '#' + + id + + '-' + + rel.type + + '_line_ending' + + ')' ); } else { svgPath.attr('stroke-dasharray', '10,7'); @@ -180,6 +187,8 @@ const drawRelationshipFromLayout = function (svg, rel, g, insert, diagObj) { 'url(' + common.getUrl(conf.arrowMarkerAbsolute) + '#' + + id + + '-' + markers.ReqMarkers.ARROW + '_line_ending' + ')' @@ -327,7 +336,7 @@ export const draw = (text, id, _version, diagObj) => { : select('body'); const svg = root.select(`[id='${id}']`); - markers.insertLineEndings(svg, conf); + markers.insertLineEndings(svg, id, conf); const g = new graphlib.Graph({ multigraph: false, @@ -357,7 +366,7 @@ export const draw = (text, id, _version, diagObj) => { adjustEntities(svg, g); relationships.forEach(function (rel) { - drawRelationshipFromLayout(svg, rel, g, id, diagObj); + drawRelationshipFromLayout(svg, id, rel, g, id, diagObj); }); const padding = conf.rect_padding; diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts index 951d84b862..c358ee1410 100644 --- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts +++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts @@ -361,11 +361,12 @@ async function boundMessage(_diagram, msgModel): Promise { * Draws a message. Note that the bounds have previously been updated by boundMessage. * * @param diagram - The parent of the message element + * @param id - id of the diagram * @param msgModel - The model containing fields describing a message * @param lineStartY - The Y coordinate at which the message line starts * @param diagObj - The diagram object. */ -const drawMessage = async function (diagram, msgModel, lineStartY: number, diagObj: Diagram) { +const drawMessage = async function (diagram, id, msgModel, lineStartY: number, diagObj: Diagram) { const { startx, stopx, starty, message, type, sequenceIndex, sequenceVisible } = msgModel; const textDims = utils.calculateTextDimensions(message, messageFont(conf)); const textObj = svgDrawCommon.getTextObj(); @@ -463,26 +464,26 @@ const drawMessage = async function (diagram, msgModel, lineStartY: number, diagO line.attr('stroke', 'none'); // handled by theme/css anyway line.style('fill', 'none'); // remove any fill colour if (type === diagObj.db.LINETYPE.SOLID || type === diagObj.db.LINETYPE.DOTTED) { - line.attr('marker-end', 'url(' + url + '#arrowhead)'); + line.attr('marker-end', 'url(' + url + '#' + id + '-arrowhead)'); } if ( type === diagObj.db.LINETYPE.BIDIRECTIONAL_SOLID || type === diagObj.db.LINETYPE.BIDIRECTIONAL_DOTTED ) { - line.attr('marker-start', 'url(' + url + '#arrowhead)'); - line.attr('marker-end', 'url(' + url + '#arrowhead)'); + line.attr('marker-start', 'url(' + url + '#' + id + '-arrowhead)'); + line.attr('marker-end', 'url(' + url + '#' + id + '-arrowhead)'); } if (type === diagObj.db.LINETYPE.SOLID_POINT || type === diagObj.db.LINETYPE.DOTTED_POINT) { - line.attr('marker-end', 'url(' + url + '#filled-head)'); + line.attr('marker-end', 'url(' + url + '#' + id + '-filled-head)'); } if (type === diagObj.db.LINETYPE.SOLID_CROSS || type === diagObj.db.LINETYPE.DOTTED_CROSS) { - line.attr('marker-end', 'url(' + url + '#crosshead)'); + line.attr('marker-end', 'url(' + url + '#' + id + '-crosshead)'); } // add node number if (sequenceVisible || conf.showSequenceNumbers) { - line.attr('marker-start', 'url(' + url + '#sequencenumber)'); + line.attr('marker-start', 'url(' + url + '#' + id + '-sequencenumber)'); diagram .append('text') .attr('x', startx) @@ -802,9 +803,9 @@ export const draw = async function (_text: string, id: string, _version: string, const maxMessageWidthPerActor = await getMaxMessageWidthPerActor(actors, messages, diagObj); conf.height = await calculateActorMargins(actors, maxMessageWidthPerActor, boxes); - svgDraw.insertComputerIcon(diagram); - svgDraw.insertDatabaseIcon(diagram); - svgDraw.insertClockIcon(diagram); + svgDraw.insertComputerIcon(diagram, id); + svgDraw.insertDatabaseIcon(diagram, id); + svgDraw.insertClockIcon(diagram, id); if (hasBoxes) { bounds.bumpVerticalPos(conf.boxMargin); @@ -826,10 +827,10 @@ export const draw = async function (_text: string, id: string, _version: string, const loopWidths = await calculateLoopBounds(messages, actors, maxMessageWidthPerActor, diagObj); // The arrow head definition is attached to the svg once - svgDraw.insertArrowHead(diagram); - svgDraw.insertArrowCrossHead(diagram); - svgDraw.insertArrowFilledHead(diagram); - svgDraw.insertSequenceNumber(diagram); + svgDraw.insertArrowHead(diagram, id); + svgDraw.insertArrowCrossHead(diagram, id); + svgDraw.insertArrowFilledHead(diagram, id); + svgDraw.insertSequenceNumber(diagram, id); /** * @param msg - The message to draw. @@ -1060,7 +1061,7 @@ export const draw = async function (_text: string, id: string, _version: string, await drawActors(diagram, actors, actorKeys, false); for (const e of messagesToDraw) { - await drawMessage(diagram, e.messageModel, e.lineStartY, diagObj); + await drawMessage(diagram, id, e.messageModel, e.lineStartY, diagObj); } if (conf.mirrorActors) { await drawActors(diagram, actors, actorKeys, true); diff --git a/packages/mermaid/src/diagrams/sequence/styles.js b/packages/mermaid/src/diagrams/sequence/styles.js index 5c36b4ed19..223bd0ffc2 100644 --- a/packages/mermaid/src/diagrams/sequence/styles.js +++ b/packages/mermaid/src/diagrams/sequence/styles.js @@ -25,7 +25,7 @@ const getStyles = (options) => stroke: ${options.signalColor}; } - #arrowhead path { + .mermaid-marker-seq-arrowhead path { fill: ${options.signalColor}; stroke: ${options.signalColor}; } @@ -34,11 +34,11 @@ const getStyles = (options) => fill: ${options.sequenceNumberColor}; } - #sequencenumber { + .mermaid-marker-seq-sequencenumber { fill: ${options.signalColor}; } - #crosshead path { + .mermaid-marker-seq-crosshead path { fill: ${options.signalColor}; stroke: ${options.signalColor}; } @@ -71,7 +71,6 @@ const getStyles = (options) => } .note { - //stroke: #decc93; stroke: ${options.noteBorderColor}; fill: ${options.noteBkgColor}; } diff --git a/packages/mermaid/src/diagrams/sequence/svgDraw.js b/packages/mermaid/src/diagrams/sequence/svgDraw.js index c681c94918..4beae88006 100644 --- a/packages/mermaid/src/diagrams/sequence/svgDraw.js +++ b/packages/mermaid/src/diagrams/sequence/svgDraw.js @@ -676,11 +676,12 @@ export const drawBackgroundRect = function (elem, bounds) { svgDrawCommon.drawBackgroundRect(elem, bounds); }; -export const insertDatabaseIcon = function (elem) { +export const insertDatabaseIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'database') + .attr('id', id + '-database') + .attr('class', 'mermaid-marker-seq-database') .attr('fill-rule', 'evenodd') .attr('clip-rule', 'evenodd') .append('path') @@ -691,11 +692,12 @@ export const insertDatabaseIcon = function (elem) { ); }; -export const insertComputerIcon = function (elem) { +export const insertComputerIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'computer') + .attr('id', id + '-computer') + .attr('class', 'mermaid-marker-seq-computer') .attr('width', '24') .attr('height', '24') .append('path') @@ -706,11 +708,12 @@ export const insertComputerIcon = function (elem) { ); }; -export const insertClockIcon = function (elem) { +export const insertClockIcon = function (elem, id) { elem .append('defs') .append('symbol') - .attr('id', 'clock') + .attr('id', id + '-clock') + .attr('class', 'mermaid-marker-seq-clock') .attr('width', '24') .attr('height', '24') .append('path') @@ -726,11 +729,12 @@ export const insertClockIcon = function (elem) { * * @param elem */ -export const insertArrowHead = function (elem) { +export const insertArrowHead = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'arrowhead') + .attr('id', id + '-arrowhead') + .attr('class', 'mermaid-marker-seq-arrowhead') .attr('refX', 7.9) .attr('refY', 5) .attr('markerUnits', 'userSpaceOnUse') @@ -746,11 +750,12 @@ export const insertArrowHead = function (elem) { * * @param {any} elem */ -export const insertArrowFilledHead = function (elem) { +export const insertArrowFilledHead = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'filled-head') + .attr('id', id + '-filled-head') + .attr('class', 'mermaid-marker-seq-filled-head') .attr('refX', 15.5) .attr('refY', 7) .attr('markerWidth', 20) @@ -765,11 +770,12 @@ export const insertArrowFilledHead = function (elem) { * * @param {any} elem */ -export const insertSequenceNumber = function (elem) { +export const insertSequenceNumber = function (elem, id) { elem .append('defs') .append('marker') - .attr('id', 'sequencenumber') + .attr('id', id + '-sequencenumber') + .attr('class', 'mermaid-marker-seq-sequencenumber') .attr('refX', 15) .attr('refY', 15) .attr('markerWidth', 60) @@ -787,11 +793,12 @@ export const insertSequenceNumber = function (elem) { * * @param {any} elem */ -export const insertArrowCrossHead = function (elem) { +export const insertArrowCrossHead = function (elem, id) { const defs = elem.append('defs'); const marker = defs .append('marker') - .attr('id', 'crosshead') + .attr('id', id + '-crosshead') + .attr('class', 'mermaid-marker-seq-crosshead') .attr('markerWidth', 15) .attr('markerHeight', 8) .attr('orient', 'auto') diff --git a/packages/mermaid/src/diagrams/timeline/svgDraw.js b/packages/mermaid/src/diagrams/timeline/svgDraw.js index 723dc46f17..9ddf3e6b16 100644 --- a/packages/mermaid/src/diagrams/timeline/svgDraw.js +++ b/packages/mermaid/src/diagrams/timeline/svgDraw.js @@ -433,11 +433,12 @@ const _drawTextCandidateFunc = (function () { }; })(); -const initGraphics = function (graphics) { +const initGraphics = function (graphics, id) { graphics .append('defs') .append('marker') - .attr('id', 'arrowhead') + .attr('id', id + '-arrowhead') + .attr('class', 'mermaid-marker-tl-arrowhead') .attr('refX', 5) .attr('refY', 2) .attr('markerWidth', 6) diff --git a/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts b/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts index b3405bc1bf..238e87fba2 100644 --- a/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts +++ b/packages/mermaid/src/diagrams/timeline/timelineRenderer.ts @@ -56,7 +56,7 @@ export const draw = function (text: string, id: string, version: string, diagObj log.debug('task', tasks); //5. Initialize the diagram - svgDraw.initGraphics(svg); + svgDraw.initGraphics(svg, id); // fetch Sections // @ts-expect-error - db not typed yet @@ -158,6 +158,7 @@ export const draw = function (text: string, id: string, version: string, diagObj if (tasksForSection.length > 0) { drawTasks( svg, + id, tasksForSection, sectionNumber, masterX, @@ -181,6 +182,7 @@ export const draw = function (text: string, id: string, version: string, diagObj hasSections = false; drawTasks( svg, + id, tasks, sectionNumber, masterX, @@ -220,7 +222,7 @@ export const draw = function (text: string, id: string, version: string, diagObj .attr('y2', depthY) .attr('stroke-width', 4) .attr('stroke', 'black') - .attr('marker-end', 'url(#arrowhead)'); + .attr('marker-end', 'url(#' + id + '-arrowhead)'); // Setup the view box and size of the svg element setupGraphViewbox( @@ -235,6 +237,7 @@ export const draw = function (text: string, id: string, version: string, diagObj export const drawTasks = function ( diagram: Selection, + id: string, tasks: TimelineTask[], sectionColor: number, masterX: number, @@ -297,7 +300,7 @@ export const drawTasks = function ( ) .attr('stroke-width', 2) .attr('stroke', 'black') - .attr('marker-end', 'url(#arrowhead)') + .attr('marker-end', 'url(#' + id + '-arrowhead)') .attr('stroke-dasharray', '5,5'); } diff --git a/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts b/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts index 13eb31a024..21ff66ff9b 100644 --- a/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts +++ b/packages/mermaid/src/diagrams/user-journey/journeyRenderer.ts @@ -65,7 +65,7 @@ export const draw = function (text, id, version, diagObj) { bounds.init(); const diagram = root.select('#' + id); - svgDraw.initGraphics(diagram); + svgDraw.initGraphics(diagram, id); const tasks = diagObj.db.getTasks(); const title = diagObj.db.getDiagramTitle(); @@ -112,7 +112,7 @@ export const draw = function (text, id, version, diagObj) { .attr('y2', conf.height * 4) .attr('stroke-width', 4) .attr('stroke', 'black') - .attr('marker-end', 'url(#arrowhead)'); + .attr('marker-end', 'url(#' + id + '-arrowhead)'); const extraVertForTitle = title ? 70 : 0; diagram.attr('viewBox', `${box.startx} -25 ${width} ${height + extraVertForTitle}`); diff --git a/packages/mermaid/src/diagrams/user-journey/svgDraw.js b/packages/mermaid/src/diagrams/user-journey/svgDraw.js index 7a8f791fac..783a1fa896 100644 --- a/packages/mermaid/src/diagrams/user-journey/svgDraw.js +++ b/packages/mermaid/src/diagrams/user-journey/svgDraw.js @@ -388,11 +388,12 @@ const _drawTextCandidateFunc = (function () { }; })(); -const initGraphics = function (graphics) { +const initGraphics = function (graphics, id) { graphics .append('defs') .append('marker') - .attr('id', 'arrowhead') + .attr('id', id + '-arrowhead') + .attr('class', 'mermaid-marker-journey-arrowhead') .attr('refX', 5) .attr('refY', 2) .attr('markerWidth', 6)