diff --git a/lib/features/snapping/BpmnSnapping.js b/lib/features/snapping/BpmnSnapping.js index 85525cdc..2f0c91ac 100644 --- a/lib/features/snapping/BpmnSnapping.js +++ b/lib/features/snapping/BpmnSnapping.js @@ -373,18 +373,34 @@ BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) { var siblings = this.getSiblings(shape, target) || []; - forEach(siblings, function(s) { + forEach(siblings, function(sibling) { // do not snap to lanes - if (is(s, 'bpmn:Lane')) { + if (is(sibling, 'bpmn:Lane')) { return; } - snapPoints.add('mid', mid(s)); + if (sibling.waypoints) { + forEach(sibling.waypoints, function(waypoint, i) { + var nextWaypoint = sibling.waypoints[i+1]; - if (is(s, 'bpmn:Participant')) { - snapPoints.add('top-left', topLeft(s)); - snapPoints.add('bottom-right', bottomRight(s)); + if (!nextWaypoint) { + return; + } + + if (nextWaypoint.x === waypoint.x || nextWaypoint.y === waypoint.y) { + snapPoints.add('mid', waypoint); + } + }); + + return; + } + + snapPoints.add('mid', mid(sibling)); + + if (is(sibling, 'bpmn:Participant')) { + snapPoints.add('top-left', topLeft(sibling)); + snapPoints.add('bottom-right', bottomRight(sibling)); } }); diff --git a/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn b/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn new file mode 100644 index 00000000..9e81382b --- /dev/null +++ b/test/spec/features/snapping/BpmnSnapping.sequenceFlow.bpmn @@ -0,0 +1,41 @@ + + + + + SequenceFlow_1 + + + SequenceFlow_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnSnappingSpec.js b/test/spec/features/snapping/BpmnSnappingSpec.js index 08a17e67..7ba30b1f 100644 --- a/test/spec/features/snapping/BpmnSnappingSpec.js +++ b/test/spec/features/snapping/BpmnSnappingSpec.js @@ -99,6 +99,72 @@ describe('features/snapping - BpmnSnapping', function() { }); + describe('on Sequence Flows', function() { + + var diagramXML = require('./BpmnSnapping.sequenceFlow.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + var sequenceFlow, task; + + beforeEach(inject(function(elementFactory, elementRegistry, dragging) { + sequenceFlow = elementRegistry.get('SequenceFlow_1'); + + task = elementFactory.createShape({ + type: 'bpmn:Task' + }); + + dragging.setOptions({ manual: true }); + })); + + afterEach(inject(function(dragging) { + dragging.setOptions({ manual: false }); + })); + + + it('should snap vertically', + inject(function(canvas, create, dragging) { + + // given + var sequenceFlowGfx = canvas.getGraphics(sequenceFlow); + + // when + create.start(canvasEvent({ x: 0, y: 0 }), task); + + dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); + + dragging.move(canvasEvent({ x: 300, y: 245 })); + dragging.end(); + + // then + expect(task.x).to.eql(254); + }) + ); + + + it('should snap horizontally', + inject(function(canvas, create, dragging) { + + // given + var sequenceFlowGfx = canvas.getGraphics(sequenceFlow); + + // when + create.start(canvasEvent({ x: 0, y: 0 }), task); + + dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); + + dragging.move(canvasEvent({ x: 410, y: 320 })); + dragging.end(); + + // then + expect(task.y).to.eql(285); + }) + ); + }); + + describe('on Participant create', function() { describe('in non-empty process', function() {