feat(snapping): add lane snapping

This commit is contained in:
Nico Rehwaldt 2015-10-10 01:41:32 +02:00 committed by pedesen
parent 5e26068f99
commit fefc748a9a
6 changed files with 719 additions and 78 deletions

View File

@ -5,14 +5,15 @@ var inherits = require('inherits');
var forEach = require('lodash/collection/forEach');
var getBoundingBox = require('diagram-js/lib/util/Elements').getBBox;
var is = require('../modeling/ModelingUtil').is,
var is = require('../../util/ModelUtil').is,
isAny = require('../modeling/util/ModelingUtil').isAny,
isExpanded = require('../../util/DiUtil').isExpanded;
var Snapping = require('diagram-js/lib/features/snapping/Snapping'),
SnapUtil = require('diagram-js/lib/features/snapping/SnapUtil');
var is = require('../../util/ModelUtil').is;
var asTRBL = require('diagram-js/lib/layout/LayoutUtil').asTRBL;
var round = Math.round;
@ -20,8 +21,12 @@ var mid = SnapUtil.mid,
topLeft = SnapUtil.topLeft,
bottomRight = SnapUtil.bottomRight,
isSnapped = SnapUtil.isSnapped,
setSnapped = SnapUtil.setSnapped,
getBoundaryAttachment = require('./BpmnSnappingUtil').getBoundaryAttachment;
setSnapped = SnapUtil.setSnapped;
var getBoundaryAttachment = require('./BpmnSnappingUtil').getBoundaryAttachment,
getParticipantSizeConstraints = require('./BpmnSnappingUtil').getParticipantSizeConstraints,
getLanesRoot = require('../modeling/util/LaneUtil').getLanesRoot;
/**
* BPMN specific snapping functionality
@ -32,7 +37,7 @@ var mid = SnapUtil.mid,
* @param {EventBus} eventBus
* @param {Canvas} canvas
*/
function BpmnSnapping(eventBus, canvas, bpmnRules) {
function BpmnSnapping(eventBus, canvas, bpmnRules, elementRegistry) {
// instantiate super
Snapping.call(this, eventBus, canvas);
@ -84,7 +89,12 @@ function BpmnSnapping(eventBus, canvas, bpmnRules) {
/**
* Snap boundary events to elements border
*/
eventBus.on([ 'create.move', 'create.end' ], 1500, function(event) {
eventBus.on([
'create.move',
'create.end',
'shape.move.move',
'shape.move.end'
], 1500, function(event) {
var context = event.context,
target = context.target,
@ -95,18 +105,118 @@ function BpmnSnapping(eventBus, canvas, bpmnRules) {
}
});
eventBus.on([ 'shape.move.move', 'shape.move.end' ], 1500, function(event) {
/**
* Adjust parent for flowElements to the target participant
* when droping onto lanes.
*/
eventBus.on([
'shape.move.hover',
'shape.move.move',
'shape.move.end',
'create.hover',
'create.move',
'create.end'
], 1500, function(event) {
var context = event.context,
target = context.target,
shape = context.shape;
shape = context.shape,
hover = event.hover;
if (target && !isSnapped(event) && canAttach(shape, target, event)) {
snapBoundaryEvent(event, shape, target);
if (is(hover, 'bpmn:Lane') && !isAny(shape, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
event.hover = getLanesRoot(hover);
event.hoverGfx = elementRegistry.getGraphics(event.hover);
}
});
var abs = Math.abs;
var filter = require('lodash/collection/filter'),
assign = require('lodash/object/assign');
eventBus.on([
'create.move',
'shape.move.move'
], function(event) {
var context = event.context,
shape = context.shape,
target = context.target;
var threshold = 30;
if (is(shape, 'bpmn:Lane')) {
if (isAny(target, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
var childLanes = filter(target.children, function(c) {
return is(c, 'bpmn:Lane');
});
var y = event.y,
targetTrbl;
var insert = childLanes.reduce(function(insert, l) {
var laneTrbl = asTRBL(l);
if (abs(laneTrbl.top - y) < threshold) {
insert = assign(insert || {}, { before: { element: l, y: laneTrbl.top } });
} else
if (abs(laneTrbl.bottom - y) < threshold) {
insert = assign(insert || {}, { after: { element: l, y: laneTrbl.bottom } });
} else
if (laneTrbl.top < y && laneTrbl.bottom > y) {
if (abs(laneTrbl.top - y) > abs(laneTrbl.bottom - y)) {
insert = assign(insert || {}, { after: { element: l, y: laneTrbl.bottom } });
} else {
insert = assign(insert || {}, { before: { element: l, y: laneTrbl.top } });
}
}
return insert;
}, false);
if (!insert) {
targetTrbl = asTRBL(target);
if (abs(targetTrbl.top - y) < threshold) {
insert = { before: { element: target, y: targetTrbl.top } };
} else
if (abs(targetTrbl.bottom - y) < threshold) {
insert = { after: { element: target, y: targetTrbl.bottom } };
} else {
insert = { into: { element: target, y: (targetTrbl.top + targetTrbl.bottom) / 2 } };
}
}
if (insert.before && insert.after) {
console.log('insert between', insert.before.element.id, 'and', insert.after.element.id);
setSnapped(event, 'x', insert.before.element.x + insert.before.element.width / 2);
setSnapped(event, 'y', insert.before.y);
} else
if (insert.after) {
console.log('insert after', insert.after.element.id);
setSnapped(event, 'x', insert.after.element.x + insert.after.element.width / 2);
setSnapped(event, 'y', insert.after.y);
} else
if (insert.before) {
console.log('insert before', insert.before.element.id);
setSnapped(event, 'x', insert.before.element.x + insert.before.element.width / 2);
setSnapped(event, 'y', insert.before.y);
} else
if (insert.into) {
console.log('insert into', insert.into.element.id);
setSnapped(event, 'x', insert.into.element.x + insert.into.element.width / 2);
setSnapped(event, 'y', insert.into.y);
}
}
}
});
eventBus.on('resize.start', 1500, function(event) {
var context = event.context,
shape = context.shape;
@ -119,8 +229,8 @@ function BpmnSnapping(eventBus, canvas, bpmnRules) {
context.minDimensions = { width: 300, height: 150 };
}
if (is(shape, 'bpmn:Participant') || is(shape, 'bpmn:Lane')) {
context.minBounds = computeParticipantMinBounds(shape);
if (is(shape, 'bpmn:Lane') || is(shape, 'bpmn:Participant')) {
context.resizeConstraints = getParticipantSizeConstraints(shape, context.direction);
}
if (is(shape, 'bpmn:TextAnnotation')) {
@ -132,7 +242,7 @@ function BpmnSnapping(eventBus, canvas, bpmnRules) {
inherits(BpmnSnapping, Snapping);
BpmnSnapping.$inject = [ 'eventBus', 'canvas', 'bpmnRules' ];
BpmnSnapping.$inject = [ 'eventBus', 'canvas', 'bpmnRules', 'elementRegistry' ];
module.exports = BpmnSnapping;
@ -227,6 +337,12 @@ BpmnSnapping.prototype.addTargetSnaps = function(snapPoints, shape, target) {
var siblings = this.getSiblings(shape, target) || [];
forEach(siblings, function(s) {
// do not snap to lanes
if (is(s, 'bpmn:Lane')) {
return;
}
snapPoints.add('mid', mid(s));
if (is(s, 'bpmn:Participant')) {
@ -321,11 +437,8 @@ function snapParticipant(snapBox, shape, event, offset) {
/////// boundary event snapping /////////////////////////
var LayoutUtil = require('diagram-js/lib/layout/LayoutUtil');
function snapBoundaryEvent(event, shape, target) {
var targetTRBL = LayoutUtil.asTRBL(target);
var targetTRBL = asTRBL(target);
var direction = getBoundaryAttachment(event, target);
@ -343,46 +456,3 @@ function snapBoundaryEvent(event, shape, target) {
setSnapped(event, 'x', targetTRBL.right);
}
}
//////// participant / lane min bounds
var getBBox = require('diagram-js/lib/util/Elements').getBBox,
addPadding = require('diagram-js/lib/features/resize/ResizeUtil').addPadding;
function computeParticipantMinBounds(element) {
var lanePadding = {
left: 30,
top: 0,
right: 0,
bottom: 0
};
var flowElementPadding = {
left: 50,
right: 35
};
function getChildBox(child) {
if (is(child, 'bpmn:Lane')) {
return addPadding(child, lanePadding);
}
if (child.labelTarget || child.waypoints) {
return null;
}
return addPadding(child, flowElementPadding);
}
function nonNull(e) {
return !!e;
}
var childrenBoxes = element.children.map(getChildBox).filter(nonNull);
if (childrenBoxes.length) {
return getBBox(childrenBoxes);
}
}

View File

@ -3,7 +3,7 @@
var getOrientation = require('diagram-js/lib/layout/LayoutUtil').getOrientation;
module.exports.getBoundaryAttachment = function(position, targetBounds) {
function getBoundaryAttachment(position, targetBounds) {
var orientation = getOrientation(position, targetBounds, -15);
@ -12,4 +12,149 @@ module.exports.getBoundaryAttachment = function(position, targetBounds) {
} else {
return null;
}
};
}
module.exports.getBoundaryAttachment = getBoundaryAttachment;
// participant snapping box implementation /////////////////
var is = require('../../util/ModelUtil').is;
var asTRBL = require('diagram-js/lib/layout/LayoutUtil').asTRBL;
var collectLanes = require('../modeling/util/LaneUtil').collectLanes,
getLanesRoot = require('../modeling/util/LaneUtil').getLanesRoot;
var abs = Math.abs,
min = Math.min,
max = Math.max;
function addToTrbl(trbl, attr, value, choice) {
var current = trbl[attr];
// make sure to set the value if it does not exist
// or apply the correct value by comparing against
// choice(value, currentValue)
trbl[attr] = current === undefined ? value : choice(value, current);
}
function addMin(trbl, attr, value) {
return addToTrbl(trbl, attr, value, min);
}
function addMax(trbl, attr, value) {
return addToTrbl(trbl, attr, value, max);
}
var LANE_MIN_HEIGHT = 60,
LANE_MIN_WIDTH = 300,
LANE_RIGHT_PADDING = 20,
LANE_LEFT_PADDING = 50,
LANE_TOP_PADDING = 20,
LANE_BOTTOM_PADDING = 20;
function getParticipantSizeConstraints(laneShape, resizeDirection) {
var lanesRoot = getLanesRoot(laneShape);
var isFirst = true,
isLast = true;
///// max top/bottom size for lanes
var allLanes = collectLanes(lanesRoot, [ lanesRoot ]);
var laneTrbl = asTRBL(laneShape);
var maxTrbl = {},
minTrbl = {};
if (/e/.test(resizeDirection)) {
minTrbl.right = laneTrbl.left + LANE_MIN_WIDTH;
} else
if (/w/.test(resizeDirection)) {
minTrbl.left = laneTrbl.right - LANE_MIN_WIDTH;
}
allLanes.forEach(function(other) {
var otherTrbl = asTRBL(other);
if (/n/.test(resizeDirection)) {
if (otherTrbl.top < (laneTrbl.top - 10)) {
isFirst = false;
}
// max top size (based on next element)
if (abs(laneTrbl.top - otherTrbl.bottom) < 10) {
addMax(maxTrbl, 'top', otherTrbl.top + LANE_MIN_HEIGHT);
}
// min top size (based on self or nested element)
if (abs(laneTrbl.top - otherTrbl.top) < 5) {
addMin(minTrbl, 'top', otherTrbl.bottom - LANE_MIN_HEIGHT);
}
}
if (/s/.test(resizeDirection)) {
if (otherTrbl.bottom > (laneTrbl.bottom + 10)) {
isLast = false;
}
// max bottom size (based on previous element)
if (abs(laneTrbl.bottom - otherTrbl.top) < 10) {
addMin(maxTrbl, 'bottom', otherTrbl.bottom - LANE_MIN_HEIGHT);
}
// min bottom size (based on self or nested element)
if (abs(laneTrbl.bottom - otherTrbl.bottom) < 5) {
addMax(minTrbl, 'bottom', otherTrbl.top + LANE_MIN_HEIGHT);
}
}
});
///// max top/bottom/left/right size based on flow nodes
var flowElements = lanesRoot.children.filter(function(s) {
return !s.hidden && !s.waypoints && (is(s, 'bpmn:FlowElement') || is(s, 'bpmn:Artifact'));
});
flowElements.forEach(function(flowElement) {
var flowElementTrbl = asTRBL(flowElement);
if (isFirst && /n/.test(resizeDirection)) {
addMin(minTrbl, 'top', flowElementTrbl.top - LANE_TOP_PADDING);
}
if (/e/.test(resizeDirection)) {
addMax(minTrbl, 'right', flowElementTrbl.right + LANE_RIGHT_PADDING);
}
if (isLast && /s/.test(resizeDirection)) {
addMax(minTrbl, 'bottom', flowElementTrbl.bottom + LANE_BOTTOM_PADDING);
}
if (/w/.test(resizeDirection)) {
addMin(minTrbl, 'left', flowElementTrbl.left - LANE_LEFT_PADDING);
}
});
return {
min: minTrbl,
max: maxTrbl
};
}
module.exports.getParticipantSizeConstraints = getParticipantSizeConstraints;

View File

@ -14,9 +14,18 @@ var coreModule = require('../../../../lib/core'),
moveModule = require('diagram-js/lib/features/move'),
rulesModule = require('../../../../lib/features/rules');
describe('features/snapping - BpmnSnapping', function() {
var testModules = [ coreModule, snappingModule, modelingModule, createModule, rulesModule, moveModule ];
var testModules = [
coreModule,
snappingModule,
modelingModule,
createModule,
rulesModule,
moveModule
];
describe('on Boundary Events', function() {
@ -225,7 +234,13 @@ describe('features/snapping - BpmnSnapping', function() {
var diagramXML = require('./BpmnSnapping.participant-resize.bpmn');
var testResizeModules = [ coreModule, resizeModule, rulesModule, snappingModule ];
var testResizeModules = [
coreModule,
resizeModule,
modelingModule,
rulesModule,
snappingModule
];
beforeEach(bootstrapModeler(diagramXML, { modules: testResizeModules }));
@ -238,7 +253,7 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.move(canvasEvent({ x: 0, y: 0 }));
dragging.end();
expect(participant.width).to.equal(497);
expect(participant.width).to.equal(482);
expect(participant.height).to.equal(252);
}));
@ -265,7 +280,7 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.end();
expect(participant.width).to.equal(300);
expect(participant.height).to.equal(150);
expect(participant.height).to.equal(60);
}));
@ -278,7 +293,7 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.end();
expect(participant.width).to.equal(300);
expect(participant.height).to.equal(150);
expect(participant.height).to.equal(60);
}));
@ -290,7 +305,7 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.move(canvasEvent({ x: 0, y: 0 }));
dragging.end();
expect(participant.width).to.equal(320);
expect(participant.width).to.equal(305);
// snap to children rather than min dimensions
expect(participant.height).to.equal(143);
@ -316,7 +331,13 @@ describe('features/snapping - BpmnSnapping', function() {
var diagramXML = require('./BpmnSnapping.lanes-resize.bpmn');
var testResizeModules = [ coreModule, resizeModule, rulesModule, snappingModule ];
var testResizeModules = [
coreModule,
resizeModule,
modelingModule,
rulesModule,
snappingModule
];
beforeEach(bootstrapModeler(diagramXML, { modules: testResizeModules }));
@ -329,8 +350,8 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.move(canvasEvent({ x: 500, y: 500 }));
dragging.end();
expect(participant.width).to.equal(600);
expect(participant.height).to.equal(254);
expect(participant.width).to.equal(563);
expect(participant.height).to.equal(223);
}));
@ -342,8 +363,8 @@ describe('features/snapping - BpmnSnapping', function() {
dragging.move(canvasEvent({ x: -500, y: -500 }));
dragging.end();
expect(lane.width).to.equal(570);
expect(lane.height).to.equal(118);
expect(lane.width).to.equal(313);
expect(lane.height).to.equal(122);
}));
});

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_4bAZoD9WEeWLcNBL4nCk1A" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:collaboration id="_Collaboration_2">
<bpmn2:participant id="Participant_Lane" name="Participant_Lane" processRef="Process_Lane"/>
</bpmn2:collaboration>
<bpmn2:process id="Process_Lane" isExecutable="false">
<bpmn2:laneSet id="LaneSet_1" name="Lane Set 1">
<bpmn2:lane id="Lane_A" name="Lane_A">
<bpmn2:childLaneSet xsi:type="bpmn2:tLaneSet" id="LaneSet_2">
<bpmn2:lane id="Nested_Lane_A" name="Nested_Lane_A">
<bpmn2:flowNodeRef>Boundary</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>Task_Boundary</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="Nested_Lane_B" name="Nested_Lane_B"/>
</bpmn2:childLaneSet>
</bpmn2:lane>
<bpmn2:lane id="Lane_B" name="Lane_B">
<bpmn2:flowNodeRef>Task</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:boundaryEvent id="Boundary" name="Boundary" attachedToRef="Task_Boundary">
<bpmn2:outgoing>SequenceFlow_From_Boundary</bpmn2:outgoing>
</bpmn2:boundaryEvent>
<bpmn2:sequenceFlow id="SequenceFlow_From_Boundary" name="" sourceRef="Boundary" targetRef="Task"/>
<bpmn2:task id="Task_Boundary" name="Task_Boundary">
<bpmn2:outgoing>SequenceFlow</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="SequenceFlow" name="" sourceRef="Task_Boundary" targetRef="Task"/>
<bpmn2:task id="Task" name="Task">
<bpmn2:incoming>SequenceFlow_From_Boundary</bpmn2:incoming>
<bpmn2:incoming>SequenceFlow</bpmn2:incoming>
</bpmn2:task>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="_Collaboration_2">
<bpmndi:BPMNShape id="_BPMNShape_Participant_2" bpmnElement="Participant_Lane" isHorizontal="true">
<dc:Bounds height="532.0" width="540.0" x="156.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_2" bpmnElement="Task_Boundary">
<dc:Bounds height="80.0" width="100.0" x="348.0" y="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_BoundaryEvent_2" bpmnElement="Boundary">
<dc:Bounds height="36.0" width="36.0" x="395.0" y="92.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="21.0" width="61.0" x="336.0" y="127.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Task_3" bpmnElement="Task">
<dc:Bounds height="80.0" width="100.0" x="516.0" y="384.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_1" bpmnElement="SequenceFlow_From_Boundary" sourceElement="_BPMNShape_BoundaryEvent_2" targetElement="_BPMNShape_Task_3">
<di:waypoint xsi:type="dc:Point" x="413.0" y="128.0"/>
<di:waypoint xsi:type="dc:Point" x="413.0" y="156.0"/>
<di:waypoint xsi:type="dc:Point" x="413.0" y="423.0"/>
<di:waypoint xsi:type="dc:Point" x="516.0" y="424.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="410.0" y="206.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_2" bpmnElement="SequenceFlow" sourceElement="_BPMNShape_Task_2" targetElement="_BPMNShape_Task_3">
<di:waypoint xsi:type="dc:Point" x="448.0" y="70.0"/>
<di:waypoint xsi:type="dc:Point" x="482.0" y="70.0"/>
<di:waypoint xsi:type="dc:Point" x="566.0" y="70.0"/>
<di:waypoint xsi:type="dc:Point" x="566.0" y="384.0"/>
<bpmndi:BPMNLabel>
<dc:Bounds height="6.0" width="6.0" x="487.0" y="70.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_Lane_3" bpmnElement="Lane_A" isHorizontal="true">
<dc:Bounds height="361.0" width="510.0" x="186.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_4" bpmnElement="Nested_Lane_A" isHorizontal="true">
<dc:Bounds height="181.0" width="480.0" x="216.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_5" bpmnElement="Nested_Lane_B" isHorizontal="true">
<dc:Bounds height="181.0" width="480.0" x="216.0" y="180.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_6" bpmnElement="Lane_B" isHorizontal="true">
<dc:Bounds height="172.0" width="510.0" x="186.0" y="360.0"/>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="_4bAZoD9WEeWLcNBL4nCk1A" exporter="camunda modeler" exporterVersion="2.6.0" targetNamespace="http://activiti.org/bpmn">
<bpmn2:collaboration id="_Collaboration_2">
<bpmn2:participant id="Participant_Lane" name="Participant_Lane" processRef="Process_Lane"/>
</bpmn2:collaboration>
<bpmn2:process id="Process_Lane" isExecutable="false">
<bpmn2:laneSet id="LaneSet_1" name="Lane Set 1">
<bpmn2:lane id="Lane_A" name="Lane_A">
<bpmn2:childLaneSet xsi:type="bpmn2:tLaneSet" id="LaneSet_2">
<bpmn2:lane id="Nested_Lane_A" name="Nested_Lane_A"/>
<bpmn2:lane id="Nested_Lane_B" name="Nested_Lane_B"/>
</bpmn2:childLaneSet>
</bpmn2:lane>
<bpmn2:lane id="Lane_B" name="Lane_B"/>
</bpmn2:laneSet>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="_Collaboration_2">
<bpmndi:BPMNShape id="_BPMNShape_Participant_2" bpmnElement="Participant_Lane" isHorizontal="true">
<dc:Bounds height="532.0" width="540.0" x="156.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_3" bpmnElement="Lane_A" isHorizontal="true">
<dc:Bounds height="361.0" width="510.0" x="186.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_4" bpmnElement="Nested_Lane_A" isHorizontal="true">
<dc:Bounds height="181.0" width="480.0" x="216.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_5" bpmnElement="Nested_Lane_B" isHorizontal="true">
<dc:Bounds height="181.0" width="480.0" x="216.0" y="180.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="_BPMNShape_Lane_6" bpmnElement="Lane_B" isHorizontal="true">
<dc:Bounds height="172.0" width="510.0" x="186.0" y="360.0"/>
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -0,0 +1,286 @@
'use strict';
var TestHelper = require('test/TestHelper');
/* global bootstrapModeler, inject */
var getParticipantSizeConstraints = require('lib/features/snapping/BpmnSnappingUtil').getParticipantSizeConstraints;
var coreModule = require('lib/core');
var LANE_MIN_HEIGHT = 60,
LANE_RIGHT_PADDING = 20,
LANE_LEFT_PADDING = 50,
LANE_TOP_PADDING = 20,
LANE_BOTTOM_PADDING = 20;
describe('features/snapping - BpmnSnappingUtil', function() {
describe('#getParticipantSizeConstraints', function() {
describe('lanes', function() {
var diagramXML = require('./BpmnSnappingUtil.lanes.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: [ coreModule ] }));
it('resize participant (S)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Participant_Lane'),
otherLaneShape = elementRegistry.get('Lane_B');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 's');
// then
expect(sizeConstraints).to.eql({
min: {
bottom: otherLaneShape.y + LANE_MIN_HEIGHT
},
max: {}
});
}));
it('bottom lane (S)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Lane_B'),
otherLaneShape = elementRegistry.get('Lane_B');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 's');
// then
expect(sizeConstraints).to.eql({
min: {
bottom: otherLaneShape.y + LANE_MIN_HEIGHT
},
max: {}
});
}));
it('resize participant (N)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Participant_Lane'),
otherLaneShape = elementRegistry.get('Nested_Lane_A');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'n');
// then
expect(sizeConstraints).to.eql({
min: {
top: otherLaneShape.y + otherLaneShape.height - LANE_MIN_HEIGHT
},
max: {}
});
}));
it('resize top lane (N)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Lane_A'),
otherLaneShape = elementRegistry.get('Nested_Lane_A');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'n');
// then
expect(sizeConstraints).to.eql({
min: {
top: otherLaneShape.y + otherLaneShape.height - LANE_MIN_HEIGHT
},
max: {}
});
}));
it('resize middle lane (N)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Nested_Lane_B'),
aboveLaneShape = elementRegistry.get('Nested_Lane_A');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'n');
// then
expect(sizeConstraints).to.eql({
min: {
top: resizeShape.y + resizeShape.height - LANE_MIN_HEIGHT
},
max: {
top: aboveLaneShape.y + LANE_MIN_HEIGHT
}
});
}));
it('resize middle lane (S)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Nested_Lane_B'),
otherLaneShape = elementRegistry.get('Lane_B');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 's');
// then
expect(sizeConstraints).to.eql({
min: {
bottom: resizeShape.y + LANE_MIN_HEIGHT
},
max: {
bottom: otherLaneShape.y + otherLaneShape.height - LANE_MIN_HEIGHT
}
});
}));
});
describe('flowNodes', function() {
var diagramXML = require('./BpmnSnappingUtil.lanes-flowNodes.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: [ coreModule ] }));
it('resize participant (S)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Participant_Lane'),
taskShape = elementRegistry.get('Task');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 's');
// then
expect(sizeConstraints).to.eql({
min: {
bottom: taskShape.y + taskShape.height + LANE_BOTTOM_PADDING
},
max: {}
});
}));
it('bottom lane (S)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Lane_B'),
taskShape = elementRegistry.get('Task');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 's');
// then
expect(sizeConstraints).to.eql({
min: {
bottom: taskShape.y + taskShape.height + LANE_BOTTOM_PADDING
},
max: {}
});
}));
it('resize participant (N)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Participant_Lane'),
taskShape = elementRegistry.get('Task_Boundary');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'n');
// then
expect(sizeConstraints).to.eql({
min: {
top: taskShape.y - LANE_TOP_PADDING
},
max: {}
});
}));
it('resize top lane (N)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Lane_A'),
taskShape = elementRegistry.get('Task_Boundary');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'n');
// then
expect(sizeConstraints).to.eql({
min: {
top: taskShape.y - LANE_TOP_PADDING
},
max: {}
});
}));
it('resize lane (W)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Nested_Lane_B'),
otherShape = elementRegistry.get('Boundary_label');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'w');
// then
expect(sizeConstraints).to.eql({
min: {
left: otherShape.x - LANE_LEFT_PADDING
},
max: { }
});
}));
it('resize lane (E)', inject(function(elementRegistry) {
// given
var resizeShape = elementRegistry.get('Lane_B'),
otherShape = elementRegistry.get('Task');
// when
var sizeConstraints = getParticipantSizeConstraints(resizeShape, 'e');
// then
expect(sizeConstraints).to.eql({
min: {
right: otherShape.x + otherShape.width + LANE_RIGHT_PADDING
},
max: { }
});
}));
});
});
});