mirror of
https://github.com/sartography/bpmn-js.git
synced 2025-01-11 17:44:12 +00:00
feat(modeling): add move onto sequence flow
This adds the ability to move flow nodes onto existing sequence flows (similar to the create on flow behavior implemented with an earlier bpmn-js release). Closes camunda/camunda-modeler#432
This commit is contained in:
parent
6dc4af5396
commit
def402971c
@ -1,95 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
var inherits = require('inherits');
|
|
||||||
|
|
||||||
var assign = require('lodash/object/assign');
|
|
||||||
|
|
||||||
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
|
||||||
|
|
||||||
var getApproxIntersection = require('diagram-js/lib/util/LineIntersection').getApproxIntersection;
|
|
||||||
|
|
||||||
|
|
||||||
function copy(obj) {
|
|
||||||
return assign({}, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CreateOnFlowBehavior(eventBus, bpmnRules, modeling) {
|
|
||||||
|
|
||||||
CommandInterceptor.call(this, eventBus);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnect start / end of a connection after
|
|
||||||
* dropping an element on a flow.
|
|
||||||
*/
|
|
||||||
|
|
||||||
this.preExecute('shape.create', function(context) {
|
|
||||||
|
|
||||||
var parent = context.parent,
|
|
||||||
shape = context.shape;
|
|
||||||
|
|
||||||
if (bpmnRules.canInsert(shape, parent)) {
|
|
||||||
context.targetFlow = parent;
|
|
||||||
context.parent = parent.parent;
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
|
|
||||||
this.postExecute('shape.create', function(context) {
|
|
||||||
|
|
||||||
var shape = context.shape,
|
|
||||||
targetFlow = context.targetFlow,
|
|
||||||
position = context.position,
|
|
||||||
source,
|
|
||||||
target,
|
|
||||||
reconnected,
|
|
||||||
intersection,
|
|
||||||
waypoints,
|
|
||||||
waypointsBefore,
|
|
||||||
waypointsAfter,
|
|
||||||
dockingPoint;
|
|
||||||
|
|
||||||
if (targetFlow) {
|
|
||||||
|
|
||||||
waypoints = targetFlow.waypoints;
|
|
||||||
|
|
||||||
|
|
||||||
intersection = getApproxIntersection(waypoints, position);
|
|
||||||
|
|
||||||
if (intersection) {
|
|
||||||
waypointsBefore = waypoints.slice(0, intersection.index);
|
|
||||||
waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));
|
|
||||||
|
|
||||||
dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : position;
|
|
||||||
|
|
||||||
waypointsBefore.push(copy(dockingPoint));
|
|
||||||
waypointsAfter.unshift(copy(dockingPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
source = targetFlow.source;
|
|
||||||
target = targetFlow.target;
|
|
||||||
|
|
||||||
if (bpmnRules.canConnect(source, shape, targetFlow)) {
|
|
||||||
// reconnect source -> inserted shape
|
|
||||||
modeling.reconnectEnd(targetFlow, shape, waypointsBefore || copy(position));
|
|
||||||
|
|
||||||
reconnected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpmnRules.canConnect(shape, target, targetFlow)) {
|
|
||||||
|
|
||||||
if (!reconnected) {
|
|
||||||
// reconnect inserted shape -> end
|
|
||||||
modeling.reconnectStart(targetFlow, shape, waypointsAfter || copy(position));
|
|
||||||
} else {
|
|
||||||
modeling.connect(shape, target, { type: targetFlow.type, waypoints: waypointsAfter });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
inherits(CreateOnFlowBehavior, CommandInterceptor);
|
|
||||||
|
|
||||||
CreateOnFlowBehavior.$inject = [ 'eventBus', 'bpmnRules', 'modeling' ];
|
|
||||||
|
|
||||||
module.exports = CreateOnFlowBehavior;
|
|
121
lib/features/modeling/behavior/DropOnFlowBehavior.js
Normal file
121
lib/features/modeling/behavior/DropOnFlowBehavior.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var inherits = require('inherits');
|
||||||
|
|
||||||
|
var assign = require('lodash/object/assign');
|
||||||
|
|
||||||
|
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
||||||
|
|
||||||
|
var getApproxIntersection = require('diagram-js/lib/util/LineIntersection').getApproxIntersection;
|
||||||
|
|
||||||
|
|
||||||
|
function copy(obj) {
|
||||||
|
return assign({}, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DropOnFlow(eventBus, bpmnRules, modeling) {
|
||||||
|
|
||||||
|
CommandInterceptor.call(this, eventBus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconnect start / end of a connection after
|
||||||
|
* dropping an element on a flow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function insertShape(shape, targetFlow, position) {
|
||||||
|
var waypoints = targetFlow.waypoints,
|
||||||
|
waypointsBefore, waypointsAfter, dockingPoint, source, target, reconnected;
|
||||||
|
|
||||||
|
var intersection = getApproxIntersection(waypoints, position);
|
||||||
|
|
||||||
|
if (intersection) {
|
||||||
|
waypointsBefore = waypoints.slice(0, intersection.index);
|
||||||
|
waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0));
|
||||||
|
|
||||||
|
dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : position;
|
||||||
|
|
||||||
|
waypointsBefore.push(copy(dockingPoint));
|
||||||
|
waypointsAfter.unshift(copy(dockingPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
source = targetFlow.source;
|
||||||
|
target = targetFlow.target;
|
||||||
|
|
||||||
|
if (bpmnRules.canConnect(source, shape, targetFlow)) {
|
||||||
|
// reconnect source -> inserted shape
|
||||||
|
modeling.reconnectEnd(targetFlow, shape, waypointsBefore || position);
|
||||||
|
|
||||||
|
reconnected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpmnRules.canConnect(shape, target, targetFlow)) {
|
||||||
|
|
||||||
|
if (!reconnected) {
|
||||||
|
// reconnect inserted shape -> end
|
||||||
|
modeling.reconnectStart(targetFlow, shape, waypointsAfter || position);
|
||||||
|
} else {
|
||||||
|
modeling.connect(shape, target, { type: targetFlow.type, waypoints: waypointsAfter });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventBus.on('shape.move.move', 250, function(event) {
|
||||||
|
|
||||||
|
var context = event.context,
|
||||||
|
target = context.target,
|
||||||
|
targetFlow = context.targetFlow,
|
||||||
|
shape = context.shape,
|
||||||
|
shapes = context.shapes;
|
||||||
|
|
||||||
|
if (shapes.length === 1 && bpmnRules.canInsert(shape, target)) {
|
||||||
|
context.targetFlow = target;
|
||||||
|
context.target = target.parent;
|
||||||
|
} else if (targetFlow) {
|
||||||
|
context.targetFlow = context.flowParent = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eventBus.on('shape.move.end', 250, function(event) {
|
||||||
|
|
||||||
|
var context = event.context,
|
||||||
|
shape = context.shape,
|
||||||
|
targetFlow = context.targetFlow,
|
||||||
|
position = {
|
||||||
|
x: shape.x + (shape.width / 2),
|
||||||
|
y: shape.y + (shape.height / 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (targetFlow) {
|
||||||
|
insertShape(shape, targetFlow, position);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
this.preExecute('shape.create', function(context) {
|
||||||
|
|
||||||
|
var parent = context.parent,
|
||||||
|
shape = context.shape;
|
||||||
|
|
||||||
|
if (bpmnRules.canInsert(shape, parent)) {
|
||||||
|
context.targetFlow = parent;
|
||||||
|
context.parent = parent.parent;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
|
||||||
|
this.postExecute('shape.create', function(context) {
|
||||||
|
|
||||||
|
var shape = context.shape,
|
||||||
|
targetFlow = context.targetFlow,
|
||||||
|
position = context.position;
|
||||||
|
|
||||||
|
if (targetFlow) {
|
||||||
|
insertShape(shape, targetFlow, position);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(DropOnFlow, CommandInterceptor);
|
||||||
|
|
||||||
|
DropOnFlow.$inject = [ 'eventBus', 'bpmnRules', 'modeling' ];
|
||||||
|
|
||||||
|
module.exports = DropOnFlow;
|
@ -4,7 +4,7 @@ module.exports = {
|
|||||||
'copyPasteBehavior',
|
'copyPasteBehavior',
|
||||||
'createBoundaryEventBehavior',
|
'createBoundaryEventBehavior',
|
||||||
'createDataObjectBehavior',
|
'createDataObjectBehavior',
|
||||||
'createOnFlowBehavior',
|
'dropOnFlowBehavior',
|
||||||
'createParticipantBehavior',
|
'createParticipantBehavior',
|
||||||
'dataInputAssociationBehavior',
|
'dataInputAssociationBehavior',
|
||||||
'deleteLaneBehavior',
|
'deleteLaneBehavior',
|
||||||
@ -25,7 +25,7 @@ module.exports = {
|
|||||||
copyPasteBehavior: [ 'type', require('./CopyPasteBehavior') ],
|
copyPasteBehavior: [ 'type', require('./CopyPasteBehavior') ],
|
||||||
createBoundaryEventBehavior: [ 'type', require('./CreateBoundaryEventBehavior') ],
|
createBoundaryEventBehavior: [ 'type', require('./CreateBoundaryEventBehavior') ],
|
||||||
createDataObjectBehavior: [ 'type', require('./CreateDataObjectBehavior') ],
|
createDataObjectBehavior: [ 'type', require('./CreateDataObjectBehavior') ],
|
||||||
createOnFlowBehavior: [ 'type', require('./CreateOnFlowBehavior') ],
|
dropOnFlowBehavior: [ 'type', require('./DropOnFlowBehavior') ],
|
||||||
createParticipantBehavior: [ 'type', require('./CreateParticipantBehavior') ],
|
createParticipantBehavior: [ 'type', require('./CreateParticipantBehavior') ],
|
||||||
dataInputAssociationBehavior: [ 'type', require('./DataInputAssociationBehavior') ],
|
dataInputAssociationBehavior: [ 'type', require('./DataInputAssociationBehavior') ],
|
||||||
deleteLaneBehavior: [ 'type', require('./DeleteLaneBehavior') ],
|
deleteLaneBehavior: [ 'type', require('./DeleteLaneBehavior') ],
|
||||||
|
@ -83,7 +83,8 @@ BpmnRules.prototype.init = function() {
|
|||||||
|
|
||||||
return canAttach(shapes, target, null, position) ||
|
return canAttach(shapes, target, null, position) ||
|
||||||
canReplace(shapes, target, position) ||
|
canReplace(shapes, target, position) ||
|
||||||
canMove(shapes, target, position);
|
canMove(shapes, target, position) ||
|
||||||
|
canInsert(shapes, target, position);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
|
this.addRule([ 'shape.create', 'shape.append' ], function(context) {
|
||||||
@ -749,6 +750,14 @@ function canConnectDataAssociation(source, target) {
|
|||||||
|
|
||||||
function canInsert(shape, flow, position) {
|
function canInsert(shape, flow, position) {
|
||||||
|
|
||||||
|
if (Array.isArray(shape)) {
|
||||||
|
if (shape.length !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shape = shape[0];
|
||||||
|
}
|
||||||
|
|
||||||
// return true if we can drop on the
|
// return true if we can drop on the
|
||||||
// underlying flow parent
|
// underlying flow parent
|
||||||
//
|
//
|
||||||
|
@ -1,171 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
require('../../../../TestHelper');
|
|
||||||
|
|
||||||
/* global inject, bootstrapModeler */
|
|
||||||
|
|
||||||
var flatten = require('lodash/array/flatten');
|
|
||||||
|
|
||||||
var modelingModule = require('../../../../../lib/features/modeling');
|
|
||||||
|
|
||||||
|
|
||||||
describe('modeling/behavior - drop on connection', function() {
|
|
||||||
|
|
||||||
var diagramXML = require('./CreateOnFlowBehavior.bpmn');
|
|
||||||
|
|
||||||
beforeEach(bootstrapModeler(diagramXML, { modules: modelingModule }));
|
|
||||||
|
|
||||||
|
|
||||||
describe('rules', function() {
|
|
||||||
|
|
||||||
it('should be allowed for an IntermediateThrowEvent', inject(function(elementRegistry, bpmnRules, elementFactory) {
|
|
||||||
|
|
||||||
// when
|
|
||||||
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
|
||||||
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(bpmnRules.canCreate(intermediateThrowEvent, sequenceFlow)).to.be.true;
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('execution', function() {
|
|
||||||
|
|
||||||
it('should connect start -> target -> end', inject(function(modeling, elementRegistry, elementFactory) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
|
||||||
|
|
||||||
var startEvent = elementRegistry.get('StartEvent'),
|
|
||||||
sequenceFlow = elementRegistry.get('SequenceFlow'),
|
|
||||||
task = elementRegistry.get('Task');
|
|
||||||
|
|
||||||
var originalWaypoints = sequenceFlow.waypoints;
|
|
||||||
|
|
||||||
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
|
||||||
|
|
||||||
// when
|
|
||||||
var newShape = modeling.createShape(intermediateThrowEvent, dropPosition, sequenceFlow);
|
|
||||||
|
|
||||||
// then
|
|
||||||
|
|
||||||
var targetConnection = newShape.outgoing[0];
|
|
||||||
|
|
||||||
// new incoming connection
|
|
||||||
expect(newShape.incoming.length).to.equal(1);
|
|
||||||
expect(newShape.incoming[0]).to.eql(sequenceFlow);
|
|
||||||
|
|
||||||
// new outgoing connection
|
|
||||||
expect(newShape.outgoing.length).to.equal(1);
|
|
||||||
expect(targetConnection).to.be.ok;
|
|
||||||
expect(targetConnection.type).to.equal('bpmn:SequenceFlow');
|
|
||||||
|
|
||||||
expect(startEvent.outgoing[0]).to.equal(newShape.incoming[0]);
|
|
||||||
expect(task.incoming[0]).to.equal(newShape.outgoing[0]);
|
|
||||||
|
|
||||||
// split target at insertion point
|
|
||||||
expect(sequenceFlow).to.have.waypoints(flatten([
|
|
||||||
originalWaypoints.slice(0, 1),
|
|
||||||
{ x: 322, y: 120 }
|
|
||||||
]));
|
|
||||||
|
|
||||||
expect(sequenceFlow).to.have.endDocking(dropPosition);
|
|
||||||
|
|
||||||
expect(targetConnection).to.have.waypoints(flatten([
|
|
||||||
{ x: 340, y: 138 },
|
|
||||||
originalWaypoints.slice(2)
|
|
||||||
]));
|
|
||||||
|
|
||||||
expect(targetConnection).to.have.startDocking(dropPosition);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
it('should connect start -> target', inject(function(modeling, elementRegistry, elementFactory) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var endEventShape = elementFactory.createShape({ type: 'bpmn:EndEvent' });
|
|
||||||
|
|
||||||
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
|
||||||
var originalWaypoints = sequenceFlow.waypoints;
|
|
||||||
|
|
||||||
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
|
||||||
|
|
||||||
// when
|
|
||||||
var newShape = modeling.createShape(endEventShape, dropPosition, sequenceFlow);
|
|
||||||
|
|
||||||
// then
|
|
||||||
|
|
||||||
// new incoming connection
|
|
||||||
expect(newShape.incoming.length).to.equal(1);
|
|
||||||
expect(newShape.incoming[0]).to.eql(sequenceFlow);
|
|
||||||
|
|
||||||
// no outgoing edges
|
|
||||||
expect(newShape.outgoing.length).to.equal(0);
|
|
||||||
|
|
||||||
// split target at insertion point
|
|
||||||
expect(sequenceFlow).to.have.waypoints(flatten([
|
|
||||||
originalWaypoints.slice(0, 1),
|
|
||||||
{ x: 322, y: 120 }
|
|
||||||
]));
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
it('should connect target -> end', inject(function(modeling, elementRegistry, elementFactory) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var startEventShape = elementFactory.createShape({ type: 'bpmn:StartEvent' });
|
|
||||||
|
|
||||||
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
|
||||||
var originalWaypoints = sequenceFlow.waypoints;
|
|
||||||
|
|
||||||
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
|
||||||
|
|
||||||
// when
|
|
||||||
var newShape = modeling.createShape(startEventShape, dropPosition, sequenceFlow);
|
|
||||||
|
|
||||||
// then
|
|
||||||
|
|
||||||
// no incoming connection
|
|
||||||
expect(newShape.incoming.length).to.equal(0);
|
|
||||||
|
|
||||||
// no outgoing edges
|
|
||||||
expect(newShape.outgoing.length).to.equal(1);
|
|
||||||
expect(newShape.outgoing[0]).to.eql(sequenceFlow);
|
|
||||||
|
|
||||||
// split target at insertion point
|
|
||||||
expect(sequenceFlow).to.have.waypoints(flatten([
|
|
||||||
{ x: 340, y: 138 },
|
|
||||||
originalWaypoints.slice(2)
|
|
||||||
]));
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('rules', function() {
|
|
||||||
|
|
||||||
it('should not insert participant', inject(function(rules, elementRegistry, elementFactory) {
|
|
||||||
|
|
||||||
// given
|
|
||||||
var participantShape = elementFactory.createShape({ type: 'bpmn:Participant' });
|
|
||||||
|
|
||||||
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
|
||||||
|
|
||||||
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
|
||||||
|
|
||||||
// when
|
|
||||||
var canDrop = rules.allowed('shape.create', {
|
|
||||||
shape: participantShape,
|
|
||||||
parent: sequenceFlow,
|
|
||||||
dropPosition: dropPosition
|
|
||||||
});
|
|
||||||
|
|
||||||
// then
|
|
||||||
expect(canDrop).to.be.false;
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.7.0-dev">
|
||||||
<bpmn:process id="Process" isExecutable="false">
|
<bpmn:process id="Process" isExecutable="false">
|
||||||
<bpmn:startEvent id="StartEvent">
|
<bpmn:startEvent id="StartEvent">
|
||||||
<bpmn:outgoing>SequenceFlow</bpmn:outgoing>
|
<bpmn:outgoing>SequenceFlow</bpmn:outgoing>
|
||||||
@ -8,6 +8,9 @@
|
|||||||
<bpmn:incoming>SequenceFlow</bpmn:incoming>
|
<bpmn:incoming>SequenceFlow</bpmn:incoming>
|
||||||
</bpmn:task>
|
</bpmn:task>
|
||||||
<bpmn:sequenceFlow id="SequenceFlow" sourceRef="StartEvent" targetRef="Task" />
|
<bpmn:sequenceFlow id="SequenceFlow" sourceRef="StartEvent" targetRef="Task" />
|
||||||
|
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent_foo" />
|
||||||
|
<bpmn:endEvent id="EndEvent_foo" />
|
||||||
|
<bpmn:startEvent id="StartEvent_foo" />
|
||||||
</bpmn:process>
|
</bpmn:process>
|
||||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process">
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process">
|
||||||
@ -26,6 +29,24 @@
|
|||||||
<dc:Bounds x="297.5" y="110" width="90" height="20" />
|
<dc:Bounds x="297.5" y="110" width="90" height="20" />
|
||||||
</bpmndi:BPMNLabel>
|
</bpmndi:BPMNLabel>
|
||||||
</bpmndi:BPMNEdge>
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="IntermediateThrowEvent_foo_di" bpmnElement="IntermediateThrowEvent_foo">
|
||||||
|
<dc:Bounds x="173" y="192" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="191" y="228" width="0" height="0" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="EndEvent_foo_di" bpmnElement="EndEvent_foo">
|
||||||
|
<dc:Bounds x="173" y="281" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="191" y="317" width="0" height="0" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="StartEvent_foo_di" bpmnElement="StartEvent_foo">
|
||||||
|
<dc:Bounds x="534" y="192" width="36" height="36" />
|
||||||
|
<bpmndi:BPMNLabel>
|
||||||
|
<dc:Bounds x="552" y="228" width="0" height="0" />
|
||||||
|
</bpmndi:BPMNLabel>
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
</bpmndi:BPMNPlane>
|
</bpmndi:BPMNPlane>
|
||||||
</bpmndi:BPMNDiagram>
|
</bpmndi:BPMNDiagram>
|
||||||
</bpmn:definitions>
|
</bpmn:definitions>
|
364
test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js
Normal file
364
test/spec/features/modeling/behavior/DropOnFlowBehaviorSpec.js
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../../../../TestHelper');
|
||||||
|
|
||||||
|
/* global inject, bootstrapModeler */
|
||||||
|
|
||||||
|
var flatten = require('lodash/array/flatten');
|
||||||
|
|
||||||
|
var coreModule = require('../../../../../lib/core'),
|
||||||
|
moveModule = require('diagram-js/lib/features/move'),
|
||||||
|
modelingModule = require('../../../../../lib/features/modeling'),
|
||||||
|
noTouchInteractionModule = { touchInteractionEvents: ['value', null ] };
|
||||||
|
|
||||||
|
var canvasEvent = require('../../../../util/MockEvents').createCanvasEvent;
|
||||||
|
|
||||||
|
|
||||||
|
describe('modeling/behavior - drop on connection', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./DropOnFlowBehavior.bpmn');
|
||||||
|
|
||||||
|
var testModules = [ noTouchInteractionModule, moveModule, modelingModule, coreModule ];
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||||
|
|
||||||
|
|
||||||
|
describe('rules', function() {
|
||||||
|
|
||||||
|
it('should be allowed for an IntermediateThrowEvent', inject(function(elementRegistry, bpmnRules, elementFactory) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
||||||
|
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(bpmnRules.canCreate(intermediateThrowEvent, sequenceFlow)).to.be.true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('execution', function() {
|
||||||
|
|
||||||
|
describe('create', function() {
|
||||||
|
|
||||||
|
it('should connect start -> target -> end', inject(function(modeling, elementRegistry, elementFactory) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||||
|
|
||||||
|
var startEvent = elementRegistry.get('StartEvent'),
|
||||||
|
sequenceFlow = elementRegistry.get('SequenceFlow'),
|
||||||
|
task = elementRegistry.get('Task');
|
||||||
|
|
||||||
|
var originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
||||||
|
|
||||||
|
// when
|
||||||
|
var newShape = modeling.createShape(intermediateThrowEvent, dropPosition, sequenceFlow);
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
var targetConnection = newShape.outgoing[0];
|
||||||
|
|
||||||
|
// new incoming connection
|
||||||
|
expect(newShape.incoming.length).to.equal(1);
|
||||||
|
expect(newShape.incoming[0]).to.eql(sequenceFlow);
|
||||||
|
|
||||||
|
// new outgoing connection
|
||||||
|
expect(newShape.outgoing.length).to.equal(1);
|
||||||
|
expect(targetConnection).to.be.ok;
|
||||||
|
expect(targetConnection.type).to.equal('bpmn:SequenceFlow');
|
||||||
|
|
||||||
|
expect(startEvent.outgoing[0]).to.equal(newShape.incoming[0]);
|
||||||
|
expect(task.incoming[0]).to.equal(newShape.outgoing[0]);
|
||||||
|
|
||||||
|
// split target at insertion point
|
||||||
|
expect(sequenceFlow).to.have.waypoints(flatten([
|
||||||
|
originalWaypoints.slice(0, 1),
|
||||||
|
{ x: 322, y: 120 }
|
||||||
|
]));
|
||||||
|
|
||||||
|
expect(sequenceFlow).to.have.endDocking(dropPosition);
|
||||||
|
|
||||||
|
expect(targetConnection).to.have.waypoints(flatten([
|
||||||
|
{ x: 340, y: 138 },
|
||||||
|
originalWaypoints.slice(2)
|
||||||
|
]));
|
||||||
|
|
||||||
|
expect(targetConnection).to.have.startDocking(dropPosition);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should connect start -> target', inject(function(modeling, elementRegistry, elementFactory) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var endEventShape = elementFactory.createShape({ type: 'bpmn:EndEvent' });
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
||||||
|
var originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
||||||
|
|
||||||
|
// when
|
||||||
|
var newShape = modeling.createShape(endEventShape, dropPosition, sequenceFlow);
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
// new incoming connection
|
||||||
|
expect(newShape.incoming.length).to.equal(1);
|
||||||
|
expect(newShape.incoming[0]).to.eql(sequenceFlow);
|
||||||
|
|
||||||
|
// no outgoing edges
|
||||||
|
expect(newShape.outgoing.length).to.equal(0);
|
||||||
|
|
||||||
|
// split target at insertion point
|
||||||
|
expect(sequenceFlow).to.have.waypoints(flatten([
|
||||||
|
originalWaypoints.slice(0, 1),
|
||||||
|
{ x: 322, y: 120 }
|
||||||
|
]));
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should connect target -> end', inject(function(modeling, elementRegistry, elementFactory) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var startEventShape = elementFactory.createShape({ type: 'bpmn:StartEvent' });
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
||||||
|
var originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
||||||
|
|
||||||
|
// when
|
||||||
|
var newShape = modeling.createShape(startEventShape, dropPosition, sequenceFlow);
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
// no incoming connection
|
||||||
|
expect(newShape.incoming.length).to.equal(0);
|
||||||
|
|
||||||
|
// no outgoing edges
|
||||||
|
expect(newShape.outgoing.length).to.equal(1);
|
||||||
|
expect(newShape.outgoing[0]).to.eql(sequenceFlow);
|
||||||
|
|
||||||
|
// split target at insertion point
|
||||||
|
expect(sequenceFlow).to.have.waypoints(flatten([
|
||||||
|
{ x: 340, y: 138 },
|
||||||
|
originalWaypoints.slice(2)
|
||||||
|
]));
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('move', function() {
|
||||||
|
|
||||||
|
beforeEach(inject(function(dragging) {
|
||||||
|
dragging.setOptions({ manual: true });
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should connect start -> target -> end', inject(function(dragging, move, elementRegistry, selection) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_foo');
|
||||||
|
|
||||||
|
var startEvent = elementRegistry.get('StartEvent'),
|
||||||
|
sequenceFlow = elementRegistry.get('SequenceFlow'),
|
||||||
|
sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow),
|
||||||
|
task = elementRegistry.get('Task');
|
||||||
|
|
||||||
|
var originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
// when
|
||||||
|
selection.select(intermediateThrowEvent);
|
||||||
|
|
||||||
|
move.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: sequenceFlow,
|
||||||
|
gfx: sequenceFlowGfx
|
||||||
|
});
|
||||||
|
|
||||||
|
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', inject(function(modeling, elementRegistry, selection, move, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var endEventShape = elementRegistry.get('EndEvent_foo');
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow'),
|
||||||
|
sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow),
|
||||||
|
originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
// when
|
||||||
|
selection.select(endEventShape);
|
||||||
|
|
||||||
|
move.start(canvasEvent({ x: 0, y: 0 }), endEventShape);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: sequenceFlow,
|
||||||
|
gfx: sequenceFlowGfx
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: 150, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
// new incoming connection
|
||||||
|
expect(endEventShape.incoming.length).to.equal(1);
|
||||||
|
expect(endEventShape.incoming[0]).to.eql(sequenceFlow);
|
||||||
|
|
||||||
|
// no outgoing edges
|
||||||
|
expect(endEventShape.outgoing.length).to.equal(0);
|
||||||
|
|
||||||
|
// split target at insertion point
|
||||||
|
expect(sequenceFlow).to.have.waypoints(flatten([
|
||||||
|
originalWaypoints.slice(0, 2),
|
||||||
|
{ x: 340, y: 281 }
|
||||||
|
]));
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should connect target -> end', inject(function(modeling, elementRegistry, dragging, selection, move) {
|
||||||
|
|
||||||
|
var startEventShape = elementRegistry.get('StartEvent_foo');
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow'),
|
||||||
|
sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow),
|
||||||
|
originalWaypoints = sequenceFlow.waypoints;
|
||||||
|
|
||||||
|
// when
|
||||||
|
selection.select(startEventShape);
|
||||||
|
|
||||||
|
move.start(canvasEvent({ x: 0, y: 0 }), startEventShape);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: sequenceFlow,
|
||||||
|
gfx: sequenceFlowGfx
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -215, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
|
||||||
|
// no incoming connection
|
||||||
|
expect(startEventShape.incoming.length).to.equal(0);
|
||||||
|
|
||||||
|
// no outgoing edges
|
||||||
|
expect(startEventShape.outgoing.length).to.equal(1);
|
||||||
|
expect(startEventShape.outgoing[0]).to.eql(sequenceFlow);
|
||||||
|
|
||||||
|
// split target at insertion point
|
||||||
|
expect(sequenceFlow).to.have.waypoints(flatten([
|
||||||
|
{ x: 338, y: 228 },
|
||||||
|
originalWaypoints.slice(2)
|
||||||
|
]));
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('rules', function() {
|
||||||
|
|
||||||
|
it('should not insert participant', inject(function(rules, elementRegistry, elementFactory) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var participantShape = elementFactory.createShape({ type: 'bpmn:Participant' });
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
||||||
|
|
||||||
|
var dropPosition = { x: 340, y: 120 }; // first bendpoint
|
||||||
|
|
||||||
|
// when
|
||||||
|
var canDrop = rules.allowed('shape.create', {
|
||||||
|
shape: participantShape,
|
||||||
|
parent: sequenceFlow,
|
||||||
|
dropPosition: dropPosition
|
||||||
|
});
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(canDrop).to.be.false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should not insert multiple with "move"', inject(function(elementRegistry, selection, move, dragging) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var intermediateThrowEvent = elementRegistry.get('IntermediateThrowEvent_foo'),
|
||||||
|
endEventShape = elementRegistry.get('EndEvent_foo');
|
||||||
|
|
||||||
|
var sequenceFlow = elementRegistry.get('SequenceFlow'),
|
||||||
|
sequenceFlowGfx = elementRegistry.getGraphics(sequenceFlow);
|
||||||
|
|
||||||
|
var intInitPosition = {
|
||||||
|
x: intermediateThrowEvent.x,
|
||||||
|
y: intermediateThrowEvent.y
|
||||||
|
},
|
||||||
|
endInitPosition = {
|
||||||
|
x: endEventShape.x,
|
||||||
|
y: endEventShape.y
|
||||||
|
};
|
||||||
|
|
||||||
|
selection.select([ intermediateThrowEvent, endEventShape ]);
|
||||||
|
|
||||||
|
// when
|
||||||
|
move.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent);
|
||||||
|
|
||||||
|
dragging.hover({
|
||||||
|
element: sequenceFlow,
|
||||||
|
gfx: sequenceFlowGfx
|
||||||
|
});
|
||||||
|
|
||||||
|
dragging.move(canvasEvent({ x: -215, y: 0 }));
|
||||||
|
|
||||||
|
dragging.end();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(intermediateThrowEvent).to.have.position(intInitPosition);
|
||||||
|
expect(endEventShape).to.have.position(endInitPosition);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user