fix(snapping): snap sequence flows mid -> mid

With the latest version of bpmn-js it is possible to draw point
top point connections between elements.

This restores the mid -> mid connection snapping for sequence flows.

Closes #588
This commit is contained in:
Philipp Fromme 2016-07-13 12:57:18 +02:00 committed by Nico Rehwaldt
parent 28f7145b32
commit 7fdd9bc611
3 changed files with 289 additions and 6 deletions

View File

@ -2,7 +2,11 @@
var inherits = require('inherits');
var forEach = require('lodash/collection/forEach');
var abs = Math.abs;
var forEach = require('lodash/collection/forEach'),
filter = require('lodash/collection/filter'),
assign = require('lodash/object/assign');
var getBoundingBox = require('diagram-js/lib/util/Elements').getBBox;
@ -88,6 +92,10 @@ function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistry) {
return bpmnRules.canAttach([ shape ], target, null, position) === 'attach';
}
function canConnect(source, target) {
return bpmnRules.canConnect(source, target);
}
/**
* Snap boundary events to elements border
*/
@ -129,11 +137,38 @@ function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistry) {
}
});
/**
* Snap sequence flows.
*/
eventBus.on([
'connect.move',
'connect.hover',
'connect.end'
], HIGH_PRIORITY, function(event) {
var context = event.context,
source = context.source,
target = context.target;
var abs = Math.abs;
var connection = canConnect(source, target) || {};
var filter = require('lodash/collection/filter'),
assign = require('lodash/object/assign');
if (!context.initialSourcePosition) {
context.initialSourcePosition = context.sourcePosition;
}
if (target && connection.type === 'bpmn:SequenceFlow') {
// snap source
context.sourcePosition = mid(source);
// snap target
assign(event, mid(target));
} else {
// otherwise reset source snap
context.sourcePosition = context.initialSourcePosition;
}
});
eventBus.on([

View File

@ -0,0 +1,36 @@
<?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" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="1.1.1">
<bpmn:collaboration id="Collaboration_1">
<bpmn:participant id="Participant_1" processRef="Process_1" />
<bpmn:participant id="Participant_2" processRef="Process_1e043dv" />
</bpmn:collaboration>
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:startEvent id="StartEvent_1" />
<bpmn:task id="Task_1" />
</bpmn:process>
<bpmn:process id="Process_1e043dv" isExecutable="false">
<bpmn:task id="Task_2" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="Participant_1sh90bv_di" bpmnElement="Participant_1">
<dc:Bounds x="50" y="58" width="370" height="120" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="100" y="100" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="73" y="136" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_005jq1a_di" bpmnElement="Task_1">
<dc:Bounds x="300" y="78" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_1lvgmm2_di" bpmnElement="Participant_2">
<dc:Bounds x="50" y="185" width="370" height="113" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_0ythg78_di" bpmnElement="Task_2">
<dc:Bounds x="240" y="200" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -12,7 +12,8 @@ var coreModule = require('../../../../lib/core'),
createModule = require('diagram-js/lib/features/create'),
resizeModule = require('diagram-js/lib/features/resize'),
moveModule = require('diagram-js/lib/features/move'),
rulesModule = require('../../../../lib/features/rules');
rulesModule = require('../../../../lib/features/rules'),
connectModule = require('diagram-js/lib/features/connect');
describe('features/snapping - BpmnSnapping', function() {
@ -23,7 +24,8 @@ describe('features/snapping - BpmnSnapping', function() {
modelingModule,
createModule,
rulesModule,
moveModule
moveModule,
connectModule
];
describe('on Boundary Events', function() {
@ -541,4 +543,214 @@ describe('features/snapping - BpmnSnapping', function() {
});
describe('on connect', function() {
var diagramXML = require('./BpmnSnapping.connect.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('should snap sequence flow on global connect', inject(function(connect, dragging, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_1'),
task = elementRegistry.get('Task_1');
var mid = {
x: startEvent.x + startEvent.width / 2,
y: startEvent.y + startEvent.height / 2
};
// when
connect.start(canvasEvent({ x: mid.x + 10, y: mid.y + 10 }), startEvent);
dragging.hover({
element: task,
gfx: elementRegistry.getGraphics(task)
});
dragging.move(canvasEvent({
x: task.x + task.width / 2,
y: task.y + task.height / 2
}));
dragging.end();
// then
var expected = [
{
original:
{
x: startEvent.x + startEvent.width / 2,
y: startEvent.y + startEvent.height / 2
},
x: startEvent.x + startEvent.width,
y: startEvent.y + startEvent.height / 2
},
{
original:
{
x: task.x + task.width / 2,
y: task.y + task.height / 2
},
x: task.x,
y: task.y + task.height / 2
}
];
expect(startEvent.outgoing[0].waypoints).to.eql(expected);
}));
it('should snap sequence flow on connect', inject(function(connect, dragging, elementRegistry) {
// given
var startEvent = elementRegistry.get('StartEvent_1'),
task = elementRegistry.get('Task_1');
var mid = { x: task.x + task.width / 2, y: task.y + task.height / 2 };
// when
connect.start(canvasEvent({ x: 0, y: 0 }), startEvent);
dragging.hover({
element: task,
gfx: elementRegistry.getGraphics(task)
});
dragging.move(canvasEvent({ x: mid.x + 10, y: mid.y + 10 }));
dragging.end();
// then
var expected = [
{
original:
{
x: startEvent.x + startEvent.width / 2,
y: startEvent.y + startEvent.height / 2
},
x: startEvent.x + startEvent.width,
y: startEvent.y + startEvent.height / 2
},
{
original:
{
x: task.x + task.width / 2,
y: task.y + task.height / 2
},
x: task.x,
y: task.y + task.height / 2
}
];
expect(startEvent.outgoing[0].waypoints).to.eql(expected);
}));
it('should NOT snap message flow on global connect', inject(function(connect, dragging, elementRegistry) {
// given
var task1 = elementRegistry.get('Task_1'),
task2 = elementRegistry.get('Task_2');
var task1Mid = { x: task1.x + task1.width / 2, y: task1.y + task1.height / 2 },
task2Mid = { x: task2.x + task2.width / 2, y: task2.y + task2.height / 2 };
// when
connect.start(null, task1, { x: 320, y: task1Mid.y + 20 });
dragging.hover({
element: task2,
gfx: elementRegistry.getGraphics(task2)
});
dragging.move(canvasEvent({
x: 320,
y: task2Mid.y - 20
}));
dragging.end();
// then
var expected = [
{
original:
{
x: 320,
y: task1Mid.y + 20
},
x: 320,
y: task1.y + task1.height
},
{
original:
{
x: 320,
y: task2Mid.y - 20
},
x: 320,
y: task2.y
}
];
expect(task1.outgoing[0].waypoints).to.eql(expected);
}));
it('should NOT snap message flow on connect', inject(function(connect, dragging, elementRegistry) {
// given
var task1 = elementRegistry.get('Task_1'),
task2 = elementRegistry.get('Task_2');
var task1Mid = { x: task1.x + task1.width / 2, y: task1.y + task1.height / 2 },
task2Mid = { x: task2.x + task2.width / 2, y: task2.y + task2.height / 2 };
// when
connect.start(canvasEvent({ x: 0, y: 0 }), task1);
dragging.hover({
element: task2,
gfx: elementRegistry.getGraphics(task2)
});
dragging.move(canvasEvent({
x: task2Mid.x + 20,
y: task2Mid.y - 20
}));
dragging.end();
// then
expect(task1.outgoing[0].waypoints.length).to.equal(4);
expect(task1.outgoing[0].waypoints[0]).to.eql({
original:
{
x: task1Mid.x,
y: task1Mid.y
},
x: task1Mid.x,
y: task1.y + task1.height
});
expect(task1.outgoing[0].waypoints[3]).to.eql({
original:
{
x: task2Mid.x + 20,
y: task2Mid.y - 20
},
x: task2Mid.x + 20,
y: task2.y
});
}));
});
});