parent
58f5965335
commit
084d831d9e
|
@ -25,11 +25,6 @@ function CreateBehavior(eventBus, modeling) {
|
|||
shape = context.shape,
|
||||
position = context.position;
|
||||
|
||||
if (is(parent, 'bpmn:SequenceFlow')){
|
||||
context.insertTarget = parent;
|
||||
context.parent = context.parent.parent;
|
||||
}
|
||||
|
||||
if (is(parent, 'bpmn:Process') && is(shape, 'bpmn:Participant')) {
|
||||
|
||||
// this is going to detach the process root
|
||||
|
@ -80,20 +75,6 @@ function CreateBehavior(eventBus, modeling) {
|
|||
var processChildren = processRoot.children.slice();
|
||||
modeling.moveShapes(processChildren, { x: 0, y: 0 }, shape);
|
||||
}
|
||||
|
||||
if (context.insertTarget) {
|
||||
|
||||
var initialTarget = context.insertTarget.target;
|
||||
var insertShape = context.shape;
|
||||
|
||||
// reconnecting end to inserted shape
|
||||
modeling.reconnectEnd(context.insertTarget, insertShape, context.position);
|
||||
|
||||
// create new connection between inserted shape and initial target
|
||||
modeling.createConnection(insertShape, initialTarget, {
|
||||
type: context.insertTarget.type,
|
||||
}, context.parent);
|
||||
}
|
||||
}, true);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
'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;
|
|
@ -2,6 +2,7 @@ module.exports = {
|
|||
__init__: [
|
||||
'appendBehavior',
|
||||
'createBehavior',
|
||||
'createOnFlowBehavior',
|
||||
'dropBehavior',
|
||||
'removeBehavior',
|
||||
'modelingFeedback'
|
||||
|
@ -9,6 +10,7 @@ module.exports = {
|
|||
appendBehavior: [ 'type', require('./AppendBehavior') ],
|
||||
dropBehavior: [ 'type', require('./DropBehavior') ],
|
||||
createBehavior: [ 'type', require('./CreateBehavior') ],
|
||||
createOnFlowBehavior: [ 'type', require('./CreateOnFlowBehavior') ],
|
||||
removeBehavior: [ 'type', require('./RemoveBehavior') ],
|
||||
modelingFeedback: [ 'type', require('./ModelingFeedback') ]
|
||||
};
|
|
@ -94,6 +94,8 @@ BpmnRules.prototype.canMove = canMove;
|
|||
|
||||
BpmnRules.prototype.canDrop = canDrop;
|
||||
|
||||
BpmnRules.prototype.canInsert = canInsert;
|
||||
|
||||
BpmnRules.prototype.canCreate = canCreate;
|
||||
|
||||
BpmnRules.prototype.canConnect = canConnect;
|
||||
|
@ -339,11 +341,7 @@ function canCreate(shape, target, source) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (canInsert(shape, target)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return canDrop(shape, target);
|
||||
return canDrop(shape, target) || canInsert(shape, target);
|
||||
}
|
||||
|
||||
function canResize(shape, newBounds) {
|
||||
|
@ -391,20 +389,15 @@ function canConnectSequenceFlow(source, target) {
|
|||
!(is(source, 'bpmn:EventBasedGateway') && !isEventBasedTarget(target));
|
||||
}
|
||||
|
||||
function canInsert(shape, target) {
|
||||
function canInsert(shape, flow) {
|
||||
|
||||
var startEvent = target.source;
|
||||
var endEvent = target.target;
|
||||
|
||||
if (!is(target, 'bpmn:SequenceFlow')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(is(shape, 'bpmn:IntermediateThrowEvent') &&
|
||||
is(startEvent, 'bpmn:FlowElement') &&
|
||||
is(endEvent, 'bpmn:FlowElement')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
// return true if we can drop on the
|
||||
// underlying flow parent
|
||||
//
|
||||
// at this point we are not really able to talk
|
||||
// about connection rules (yet)
|
||||
return (
|
||||
is(flow, 'bpmn:SequenceFlow') ||
|
||||
is(flow, 'bpmn:MessageFlow')
|
||||
) && canDrop(shape, flow.parent);
|
||||
}
|
|
@ -137,7 +137,7 @@ function BpmnSnapping(eventBus, canvas) {
|
|||
context.minDimensions = { width: 50, height: 50 };
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
inherits(BpmnSnapping, Snapping);
|
||||
|
@ -227,6 +227,9 @@ BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) {
|
|||
|
||||
var siblings = this.getSiblings(shape, target);
|
||||
|
||||
if (is(target, 'bpmn:SequenceFlow')) {
|
||||
this.addTargetSnaps(snapPoints, shape, target.parent);
|
||||
}
|
||||
|
||||
forEach(siblings, function(s) {
|
||||
snapPoints.add('mid', mid(s));
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
|
||||
describe('drop on conection', function(){
|
||||
|
||||
var bpmnRules = require('../../../../lib/features/modeling/rules');
|
||||
var diagramXML = require('./diagram.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, {modules: bpmnRules}));
|
||||
|
||||
it('should be allowed for an IntermediateThrowEvent', inject(function(elementRegistry, bpmnRules, elementFactory) {
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_0lk9mnl');
|
||||
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||
|
||||
expect(bpmnRules.canCreate(intermediateThrowEvent, sequenceFlow)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should rearrange connections', inject(function(modeling, elementRegistry, elementFactory){
|
||||
var intermediateThrowEvent = elementFactory.createShape({ type: 'bpmn:IntermediateThrowEvent' });
|
||||
var startEvent = elementRegistry.get('StartEvent_1');
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_0lk9mnl');
|
||||
var task = elementRegistry.get('Task_195jx60');
|
||||
var position = {x: startEvent.x + startEvent.height/2 + 100,
|
||||
y: startEvent.y + startEvent.width/2};
|
||||
|
||||
// create new intermediateThrowEvent onto sequenceFlow
|
||||
modeling.createShape(intermediateThrowEvent, position, sequenceFlow);
|
||||
|
||||
// check rearragned connection
|
||||
expect(startEvent.outgoing[0].id).toBe(intermediateThrowEvent.incoming[0].id);
|
||||
|
||||
// check newly created connection
|
||||
expect(intermediateThrowEvent.outgoing[0].id).toBe(task.incoming[0].id);
|
||||
}));
|
||||
});
|
|
@ -1,25 +1,27 @@
|
|||
<?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:process id="Process_1" isExecutable="false">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>SequenceFlow_0lk9mnl</bpmn:outgoing>
|
||||
<bpmn:process id="Process" isExecutable="false">
|
||||
<bpmn:startEvent id="StartEvent">
|
||||
<bpmn:outgoing>SequenceFlow</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:task id="Task_195jx60">
|
||||
<bpmn:incoming>SequenceFlow_0lk9mnl</bpmn:incoming>
|
||||
<bpmn:task id="Task">
|
||||
<bpmn:incoming>SequenceFlow</bpmn:incoming>
|
||||
</bpmn:task>
|
||||
<bpmn:sequenceFlow id="SequenceFlow_0lk9mnl" sourceRef="StartEvent_1" targetRef="Task_195jx60" />
|
||||
<bpmn:sequenceFlow id="SequenceFlow" sourceRef="StartEvent" targetRef="Task" />
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process">
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent">
|
||||
<dc:Bounds x="173" y="102" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Task_195jx60_di" bpmnElement="Task_195jx60">
|
||||
<dc:Bounds x="476" y="80" width="100" height="80" />
|
||||
<bpmndi:BPMNShape id="Task_di" bpmnElement="Task">
|
||||
<dc:Bounds x="502" y="259" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_0lk9mnl_di" bpmnElement="SequenceFlow_0lk9mnl">
|
||||
<bpmndi:BPMNEdge id="SequenceFlow_di" bpmnElement="SequenceFlow">
|
||||
<di:waypoint xsi:type="dc:Point" x="209" y="120" />
|
||||
<di:waypoint xsi:type="dc:Point" x="476" y="120" />
|
||||
<di:waypoint xsi:type="dc:Point" x="340" y="120" />
|
||||
<di:waypoint xsi:type="dc:Point" x="340" y="299" />
|
||||
<di:waypoint xsi:type="dc:Point" x="502" y="299" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="297.5" y="110" width="90" height="20" />
|
||||
</bpmndi:BPMNLabel>
|
|
@ -0,0 +1,143 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../../TestHelper'),
|
||||
Matchers = require('../../../../Matchers');
|
||||
|
||||
/* global inject, bootstrapModeler */
|
||||
|
||||
|
||||
var modelingModule = require('../../../../../lib/features/modeling');
|
||||
|
||||
|
||||
describe('modeling/behavior - drop on connection', function(){
|
||||
|
||||
beforeEach(Matchers.addDeepEquals);
|
||||
|
||||
|
||||
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)).toBe(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 position = { x: 340, y: 120 }; // first bendpoint
|
||||
|
||||
// when
|
||||
var newShape = modeling.createShape(intermediateThrowEvent, position, sequenceFlow);
|
||||
|
||||
// then
|
||||
|
||||
var targetConnection = newShape.outgoing[0];
|
||||
|
||||
// new incoming connection
|
||||
expect(newShape.incoming.length).toBe(1);
|
||||
expect(newShape.incoming[0]).toBe(sequenceFlow);
|
||||
|
||||
// new outgoing connection
|
||||
expect(newShape.outgoing.length).toBe(1);
|
||||
expect(targetConnection).toBeTruthy();
|
||||
expect(targetConnection.type).toBe('bpmn:SequenceFlow');
|
||||
|
||||
expect(startEvent.outgoing[0]).toBe(newShape.incoming[0]);
|
||||
expect(task.incoming[0]).toBe(newShape.outgoing[0]);
|
||||
|
||||
// split target at insertion point
|
||||
expect(sequenceFlow.waypoints).toDeepEqual([
|
||||
{ original: { x: 209, y: 120 }, x: 209, y: 120 },
|
||||
{ original: { x: 340, y: 120 }, x: 322, y: 120 }
|
||||
]);
|
||||
|
||||
expect(targetConnection.waypoints).toDeepEqual([
|
||||
{ original: { x: 340, y: 120 }, x: 340, y: 138 },
|
||||
{ x: 340, y: 299 },
|
||||
{ original: { x: 502, y: 299 }, x: 502, y: 299 }
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should connect start -> target', inject(function(modeling, elementRegistry, elementFactory) {
|
||||
|
||||
// given
|
||||
var endEventShape = elementFactory.createShape({ type: 'bpmn:EndEvent' });
|
||||
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow');
|
||||
|
||||
var position = { x: 340, y: 120 }; // first bendpoint
|
||||
|
||||
// when
|
||||
var newShape = modeling.createShape(endEventShape, position, sequenceFlow);
|
||||
|
||||
// then
|
||||
|
||||
// new incoming connection
|
||||
expect(newShape.incoming.length).toBe(1);
|
||||
expect(newShape.incoming[0]).toBe(sequenceFlow);
|
||||
|
||||
// no outgoing edges
|
||||
expect(newShape.outgoing.length).toBe(0);
|
||||
|
||||
// split target at insertion point
|
||||
expect(sequenceFlow.waypoints).toDeepEqual([
|
||||
{ original: { x: 209, y: 120 }, x: 209, y: 120 },
|
||||
{ original: { x: 340, y: 120 }, 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 position = { x: 340, y: 120 }; // first bendpoint
|
||||
|
||||
// when
|
||||
var newShape = modeling.createShape(startEventShape, position, sequenceFlow);
|
||||
|
||||
// then
|
||||
|
||||
// no incoming connection
|
||||
expect(newShape.incoming.length).toBe(0);
|
||||
|
||||
// no outgoing edges
|
||||
expect(newShape.outgoing.length).toBe(1);
|
||||
expect(newShape.outgoing[0]).toBe(sequenceFlow);
|
||||
|
||||
// split target at insertion point
|
||||
expect(sequenceFlow.waypoints).toDeepEqual([
|
||||
{ original: { x: 340, y: 120 }, x: 340, y: 138 },
|
||||
{ x: 340, y: 299 },
|
||||
{ original: { x: 502, y: 299 }, x: 502, y: 299 }
|
||||
]);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue