From 248c7a38cb6f8899aa2026ecf898da957f7dd4e8 Mon Sep 17 00:00:00 2001 From: Martin Stamm Date: Mon, 27 May 2024 13:47:19 +0200 Subject: [PATCH] feat: render markers as part of djs-visual related to https://github.com/camunda/camunda-modeler/issues/4307 --- lib/BaseViewer.js | 2 +- lib/draw/BpmnRenderer.js | 71 ++++++++++++++---------------- test/spec/draw/BpmnRendererSpec.js | 58 +++++++++++++++--------- 3 files changed, 73 insertions(+), 58 deletions(-) diff --git a/lib/BaseViewer.js b/lib/BaseViewer.js index a5316bb379..10058c5170 100644 --- a/lib/BaseViewer.js +++ b/lib/BaseViewer.js @@ -476,7 +476,7 @@ BaseViewer.prototype.saveSVG = async function saveSVG() { const canvas = this.get('canvas'); const contentNode = canvas.getActiveLayer(), - defsNode = domQuery('defs', canvas._svg); + defsNode = domQuery(':scope > defs', canvas._svg); const contents = innerSVG(contentNode), defs = defsNode ? '' + innerSVG(defsNode) + '' : ''; diff --git a/lib/draw/BpmnRenderer.js b/lib/draw/BpmnRenderer.js index 203ffdca5a..86486616a8 100644 --- a/lib/draw/BpmnRenderer.js +++ b/lib/draw/BpmnRenderer.js @@ -65,7 +65,7 @@ import Ids from 'ids'; import { black } from './BpmnRenderUtil'; -var rendererIds = new Ids(); +var markerIds = new Ids(); var ELEMENT_LABEL_DISTANCE = 10, INNER_OUTER_DIST = 3, @@ -116,10 +116,6 @@ export default function BpmnRenderer( defaultStrokeColor = config && config.defaultStrokeColor, defaultLabelColor = config && config.defaultLabelColor; - var rendererId = rendererIds.next(); - - var markers = {}; - function shapeStyle(attrs) { return styles.computeStyle(attrs, { strokeLinecap: 'round', @@ -143,7 +139,8 @@ export default function BpmnRenderer( var { ref = { x: 0, y: 0 }, scale = 1, - element + element, + parentGfx = canvas._svg } = options; var marker = svgCreate('marker', { @@ -158,36 +155,28 @@ export default function BpmnRenderer( svgAppend(marker, element); - var defs = domQuery('defs', canvas._svg); + var defs = domQuery(':scope > defs', parentGfx); if (!defs) { defs = svgCreate('defs'); - svgAppend(canvas._svg, defs); + svgAppend(parentGfx, defs); } svgAppend(defs, marker); - - markers[id] = marker; } - function colorEscape(str) { + function marker(parentGfx, type, fill, stroke) { - // only allow characters and numbers - return str.replace(/[^0-9a-zA-Z]+/g, '_'); - } - function marker(type, fill, stroke) { - var id = type + '-' + colorEscape(fill) + '-' + colorEscape(stroke) + '-' + rendererId; + var id = markerIds.nextPrefixed('marker-'); - if (!markers[id]) { - createMarker(id, type, fill, stroke); - } + createMarker(parentGfx, id, type, fill, stroke); return 'url(#' + id + ')'; } - function createMarker(id, type, fill, stroke) { + function createMarker(parentGfx, id, type, fill, stroke) { if (type === 'sequenceflow-end') { var sequenceflowEnd = svgCreate('path', { @@ -202,7 +191,8 @@ export default function BpmnRenderer( addMarker(id, { element: sequenceflowEnd, ref: { x: 11, y: 10 }, - scale: 0.5 + scale: 0.5, + parentGfx }); } @@ -224,7 +214,8 @@ export default function BpmnRenderer( addMarker(id, { element: messageflowStart, - ref: { x: 6, y: 6 } + ref: { x: 6, y: 6 }, + parentGfx }); } @@ -244,7 +235,8 @@ export default function BpmnRenderer( addMarker(id, { element: messageflowEnd, - ref: { x: 8.5, y: 5 } + ref: { x: 8.5, y: 5 }, + parentGfx }); } @@ -265,7 +257,8 @@ export default function BpmnRenderer( addMarker(id, { element: associationStart, ref: { x: 1, y: 10 }, - scale: 0.5 + scale: 0.5, + parentGfx }); } @@ -286,7 +279,8 @@ export default function BpmnRenderer( addMarker(id, { element: associationEnd, ref: { x: 11, y: 10 }, - scale: 0.5 + scale: 0.5, + parentGfx }); } @@ -302,7 +296,8 @@ export default function BpmnRenderer( addMarker(id, { element: conditionalFlowMarker, ref: { x: -1, y: 10 }, - scale: 0.5 + scale: 0.5, + parentGfx }); } @@ -310,14 +305,16 @@ export default function BpmnRenderer( var defaultFlowMarker = svgCreate('path', { d: 'M 6 4 L 10 16', ...shapeStyle({ - stroke: stroke + stroke: stroke, + fill: 'none' }) }); addMarker(id, { element: defaultFlowMarker, ref: { x: 0, y: 10 }, - scale: 0.5 + scale: 0.5, + parentGfx }); } } @@ -1105,11 +1102,11 @@ export default function BpmnRenderer( if (semantic.get('associationDirection') === 'One' || semantic.get('associationDirection') === 'Both') { - attrs.markerEnd = marker('association-end', fill, stroke); + attrs.markerEnd = marker(parentGfx, 'association-end', fill, stroke); } if (semantic.get('associationDirection') === 'Both') { - attrs.markerStart = marker('association-start', fill, stroke); + attrs.markerStart = marker(parentGfx, 'association-start', fill, stroke); } attrs = pickAttrs(attrs, [ @@ -1403,7 +1400,7 @@ export default function BpmnRenderer( return renderAssociation(parentGfx, element, { ...attrs, - markerEnd: marker('association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke)) + markerEnd: marker(parentGfx, 'association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke)) }); }, 'bpmn:DataObject': function(parentGfx, element, attrs = {}) { @@ -1441,7 +1438,7 @@ export default function BpmnRenderer( return renderAssociation(parentGfx, element, { ...attrs, - markerEnd: marker('association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke)) + markerEnd: marker(parentGfx, 'association-end', getFillColor(element, defaultFillColor, attrs.fill), getStrokeColor(element, defaultStrokeColor, attrs.stroke)) }); }, 'bpmn:DataStoreReference': function(parentGfx, element, attrs = {}) { @@ -1707,8 +1704,8 @@ export default function BpmnRenderer( stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke); var path = drawConnectionSegments(parentGfx, element.waypoints, { - markerEnd: marker('messageflow-end', fill, stroke), - markerStart: marker('messageflow-start', fill, stroke), + markerEnd: marker(parentGfx, 'messageflow-end', fill, stroke), + markerStart: marker(parentGfx, 'messageflow-start', fill, stroke), stroke, strokeDasharray: '10, 11', strokeWidth: 1.5 @@ -1964,7 +1961,7 @@ export default function BpmnRenderer( stroke = getStrokeColor(element, defaultStrokeColor, attrs.stroke); var connection = drawConnectionSegments(parentGfx, element.waypoints, { - markerEnd: marker('sequenceflow-end', fill, stroke), + markerEnd: marker(parentGfx, 'sequenceflow-end', fill, stroke), stroke }); @@ -1978,7 +1975,7 @@ export default function BpmnRenderer( // conditional flow marker if (semantic.get('conditionExpression') && is(sourceSemantic, 'bpmn:Activity')) { svgAttr(connection, { - markerStart: marker('conditional-flow-marker', fill, stroke) + markerStart: marker(parentGfx, 'conditional-flow-marker', fill, stroke) }); } @@ -1986,7 +1983,7 @@ export default function BpmnRenderer( if (sourceSemantic.get('default') && (is(sourceSemantic, 'bpmn:Gateway') || is(sourceSemantic, 'bpmn:Activity')) && sourceSemantic.get('default') === semantic) { svgAttr(connection, { - markerStart: marker('conditional-default-flow-marker', fill, stroke) + markerStart: marker(parentGfx, 'conditional-default-flow-marker', fill, stroke) }); } } diff --git a/test/spec/draw/BpmnRendererSpec.js b/test/spec/draw/BpmnRendererSpec.js index b0d5a26244..ad00814d8a 100644 --- a/test/spec/draw/BpmnRendererSpec.js +++ b/test/spec/draw/BpmnRendererSpec.js @@ -359,7 +359,7 @@ describe('draw - bpmn renderer', function() { var svg = canvas._svg; var markers = svg.querySelectorAll('marker'); - expect(markers[0].id).to.match(/^sequenceflow-end-white-hsl_225_10_15_-[A-Za-z0-9]+$/); + expect(markers[0].id).to.match(/^marker-[A-Za-z0-9]+$/); })(); }); }); @@ -375,17 +375,26 @@ describe('draw - bpmn renderer', function() { expect(err).not.to.exist; inject(function(canvas) { - var svg = canvas._svg, - markers = svg.querySelectorAll('marker'); - - expect(markers).to.have.length(7); - expect(markers[0].id).to.match(/^sequenceflow-end-rgb_255_224_178_-rgb_251_140_0_-[A-Za-z0-9]{25}$/); - expect(markers[1].id).to.match(/^sequenceflow-end-yellow-blue-[A-Za-z0-9]{25}$/); - expect(markers[2].id).to.match(/^sequenceflow-end-white-_FB8C00-[A-Za-z0-9]{25}$/); - expect(markers[3].id).to.match(/^sequenceflow-end-white-rgba_255_0_0_0_9_-[A-Za-z0-9]{25}$/); - expect(markers[4].id).to.match(/^association-end-_FFE0B2-_FB8C00-[A-Za-z0-9]{25}$/); - expect(markers[5].id).to.match(/^messageflow-end-_FFE0B2-_FB8C00-[A-Za-z0-9]{25}$/); - expect(markers[6].id).to.match(/^messageflow-start-_FFE0B2-_FB8C00-[A-Za-z0-9]{25}$/); + + [ + [ 'SequenceFlow_1jrsqqc' , 'blue' , 'blue' ], + [ 'SequenceFlow_0h9s0mp' , 'rgba(255, 0, 0, 0.9)' ], + [ 'SequenceFlow_0pqo7zt' , 'rgb(251, 140, 0)' , 'rgb(251, 140, 0)' ], + [ 'SequenceFlow_1qt82pt' , 'blue' , 'blue' ], + [ 'SequenceFlow_17ohrlh' , 'rgb(251, 140, 0)' , 'rgb(251, 140, 0)' ], + [ 'MessageFlow_11bysyp' , 'rgb(251, 140, 0)' , 'rgb(255, 224, 178)' ], + [ 'MessageFlow_1qyovto' , 'rgb(251, 140, 0)' , 'rgb(255, 224, 178)' ], + [ 'DataInputAssociation_1ncouqr' , 'rgb(251, 140, 0)' , 'none' ], + [ 'DataOutputAssociation_1i89wkc' , 'rgb(251, 140, 0)' , 'none' ] + ].forEach(([ id, stroke, fill ]) => { + var svg = canvas._svg, + markerPath = svg.querySelector(`[data-element-id="${id}"] marker path`); + + expect(markerPath).to.exist; + + stroke && expect(markerPath.style.stroke).to.eql(stroke); + fill && expect(markerPath.style.fill).to.eql(fill); + }); })(); }); }); @@ -401,14 +410,23 @@ describe('draw - bpmn renderer', function() { expect(err).not.to.exist; inject(function(canvas) { - var svg = canvas._svg, - markers = svg.querySelectorAll('marker'); - - expect(markers).to.have.length(4); - expect(markers[0].id).to.match(/^association-end-rgb_23_100_344_-rgb_23_100_344_-[A-Za-z0-9]{25}$/); - expect(markers[1].id).to.match(/^association-end-_E1BEE7-_8E24AA-[A-Za-z0-9]{25}$/); - expect(markers[2].id).to.match(/^messageflow-end-rgb_23_100_344_-rgb_23_100_344_-[A-Za-z0-9]{25}$/); - expect(markers[3].id).to.match(/^messageflow-start-rgb_23_100_344_-rgb_23_100_344_-[A-Za-z0-9]{25}$/); + + [ + [ 'MessageFlow_1facuin', 'rgb(23, 100, 255)', 'rgb(23, 100, 255)' ], + [ 'MessageFlow_1vmbq3n', 'rgb(23, 100, 255)', 'rgb(23, 100, 255)' ], + [ 'DataInputAssociation', 'rgb(23, 100, 255)', 'none' ], + [ 'DataOutputAssociation_0ixhole', 'rgb(142, 36, 170)', 'none' ], + ].forEach(([ id, stroke, fill ]) => { + var svg = canvas._svg, + markerPath = svg.querySelector(`[data-element-id="${id}"] marker path`); + + console.log(id, markerPath); + expect(markerPath).to.exist; + + stroke && expect(markerPath.style.stroke).to.eql(stroke); + fill && expect(markerPath.style.fill).to.eql(fill); + + }); })(); }); });