From 58207e7db2e09666c4ed78f2912b967970455d17 Mon Sep 17 00:00:00 2001 From: pedesen Date: Thu, 27 Jul 2017 13:40:21 +0200 Subject: [PATCH] feat(DropOnFlowBehavior): drop existing elements on flows Closes #695 --- .../modeling/behavior/DropOnFlowBehavior.js | 49 ++++--- .../modeling/behavior/DropOnFlowBehavior.bpmn | 6 +- .../behavior/DropOnFlowBehaviorSpec.js | 127 +++++++++++++++++- 3 files changed, 164 insertions(+), 18 deletions(-) diff --git a/lib/features/modeling/behavior/DropOnFlowBehavior.js b/lib/features/modeling/behavior/DropOnFlowBehavior.js index c2bd3161..f332f74a 100644 --- a/lib/features/modeling/behavior/DropOnFlowBehavior.js +++ b/lib/features/modeling/behavior/DropOnFlowBehavior.js @@ -2,7 +2,8 @@ var inherits = require('inherits'); -var assign = require('lodash/object/assign'); +var assign = require('lodash/object/assign'), + find = require('lodash/collection/find'); var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor'); @@ -85,24 +86,40 @@ function DropOnFlow(eventBus, bpmnRules, modeling) { this.preExecute('elements.move', function(context) { - var parent = context.newParent, + var newParent = context.newParent, shapes = context.shapes, - shape, - shapeMid, - delta = context.delta; + delta = context.delta, + shape = shapes[0]; - if (bpmnRules.canInsert(shapes, parent)) { - - shape = shapes[0]; - shapeMid = getMid(shape); - - context.targetFlow = parent; - context.newParent = parent.parent; - context.position = { - x: shapeMid.x + delta.x, - y: shapeMid.y + delta.y - }; + if (!shape || !newParent) { + return; } + + // if the new parent is a connection, + // change it to the new parent's parent + if (newParent && newParent.waypoints) { + context.newParent = newParent = newParent.parent; + } + + var shapeMid = getMid(shape); + var newShapeMid = { + x: shapeMid.x + delta.x, + y: shapeMid.y + delta.y + }; + + // find a connection which intersects with the + // element's mid point + var connection = find(newParent.children, function(element) { + var canInsert = bpmnRules.canInsert(shapes, element); + + return canInsert && getApproxIntersection(element.waypoints, newShapeMid); + }); + + if (connection) { + context.targetFlow = connection; + context.position = newShapeMid; + } + }, true); this.postExecuted('elements.move', function(context) { diff --git a/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn b/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn index 2f7ecd0f..59e6a830 100644 --- a/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn +++ b/test/spec/features/modeling/behavior/DropOnFlowBehavior.bpmn @@ -1,5 +1,5 @@ - + SequenceFlow_1 @@ -16,6 +16,7 @@ SequenceFlow_2 + @@ -62,6 +63,9 @@ + + + diff --git a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js index f01fd330..2ebf8ab9 100644 --- a/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js @@ -13,7 +13,6 @@ var coreModule = require('../../../../../lib/core'), var canvasEvent = require('../../../../util/MockEvents').createCanvasEvent; - describe('modeling/behavior - drop on connection', function() { var diagramXML = require('./DropOnFlowBehavior.bpmn'); @@ -264,6 +263,132 @@ describe('modeling/behavior - drop on connection', function() { })); + it('should connect start -> target -> end (hovering parent)', + inject(function(dragging, move, elementRegistry, selection, canvas) { + + // given + var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_foo'); + + var startEvent = elementRegistry.get('StartEvent'), + sequenceFlow = elementRegistry.get('SequenceFlow_1'), + task = elementRegistry.get('Task_1'), + rootElement = canvas.getRootElement(), + rootElementGfx = elementRegistry.getGraphics(rootElement); + + var originalWaypoints = sequenceFlow.waypoints; + + // when + selection.select(intermediateThrowEvent); + + move.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent); + + dragging.hover({ + element: rootElement, + gfx: rootElementGfx + }); + + dragging.move(canvasEvent({ x: 150, y: 0 })); + dragging.end(); + + // then + var targetConnection = intermediateThrowEvent.outgoing[0]; + + // new incoming connection + expect(intermediateThrowEvent.incoming.length).to.equal(1); + expect(intermediateThrowEvent.incoming[0]).to.eql(sequenceFlow); + + // new outgoing connection + expect(intermediateThrowEvent.outgoing.length).to.equal(1); + expect(targetConnection).to.be.ok; + expect(targetConnection.type).to.equal('bpmn:SequenceFlow'); + + expect(startEvent.outgoing[0]).to.equal(intermediateThrowEvent.incoming[0]); + expect(task.incoming[0]).to.equal(intermediateThrowEvent.outgoing[0]); + + // split target at insertion point + expect(sequenceFlow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 2), + { x: 341, y: 192 } + ])); + + expect(sequenceFlow).to.have.endDocking({ x: 341, y: 210 }); + + expect(targetConnection).to.have.waypoints(flatten([ + { x: 341, y: 228 }, + originalWaypoints.slice(2) + ])); + + expect(targetConnection).to.have.startDocking({ x: 341, y: 210 }); + } + )); + + + it('should connect start -> target -> end (with bendpointBefore inside bbox)', + inject(function(elementRegistry, selection, move, dragging) { + // given + var task3 = elementRegistry.get('Task_3'), + sequenceFlow = elementRegistry.get('SequenceFlow_1'), + sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow), + originalWaypoints = sequenceFlow.waypoints; + + // when + selection.select(task3); + + move.start(canvasEvent({ x: 0, y: 0 }), task3); + + dragging.hover({ + element: sequenceFlow, + gfx: sequenceFlowGfx + }); + + dragging.move(canvasEvent({ x: 150, y: -130 })); + dragging.end(); + + // then + // split target but don't keep insertion point + expect(sequenceFlow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 2), + { x: 341, y: 241 } + ])); + + expect(sequenceFlow).to.have.endDocking({ x: 341, y: 281 }); + } + )); + + + it('should connect start -> target -> end (with bendpointAfter inside bbox)', + inject(function(elementRegistry, selection, move, dragging) { + // given + var task3 = elementRegistry.get('Task_3'), + sequenceFlow = elementRegistry.get('SequenceFlow_1'), + sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow), + originalWaypoints = sequenceFlow.waypoints; + + // when + selection.select(task3); + + move.start(canvasEvent({ x: 0, y: 0 }), task3); + + dragging.hover({ + element: sequenceFlow, + gfx: sequenceFlowGfx + }); + + dragging.move(canvasEvent({ x: 170, y: -110 })); + dragging.end(); + + // then + // split target but don't keep insertion point + expect(sequenceFlow).to.have.waypoints(flatten([ + originalWaypoints.slice(0, 2), + { x: 340, y: 261 } + ])); + + expect(sequenceFlow).to.have.endDocking({ x: 340, y: 299 }); + } + )); + + it('should connect start -> target', inject(function(modeling, elementRegistry, selection, move, dragging) { // given