feat(modeling): create collapsed pools via morph menu

You are now able to morph between collapsed and expanded pools

* Not possible to drop elements in a collapsed pool
* if a expanded pool collapses, the children are deleted

Closes #365
This commit is contained in:
Jan Stümmel 2016-03-08 17:21:41 +01:00 committed by Nico Rehwaldt
parent 49173abdad
commit 4732dcfc74
10 changed files with 194 additions and 12 deletions

View File

@ -5,6 +5,7 @@ var assign = require('lodash/object/assign'),
forEach = require('lodash/collection/forEach'),
isArray = require('lodash/lang/isArray'),
is = require('../../util/ModelUtil').is,
isExpanded = require('../../util/DiUtil').isExpanded,
isAny = require('../modeling/util/ModelingUtil').isAny,
getChildLanes = require('../modeling/util/LaneUtil').getChildLanes,
isEventSubProcess = require('../../util/DiUtil').isEventSubProcess;
@ -145,7 +146,8 @@ ContextPadProvider.prototype.getContextPadEntries = function(element) {
};
}
if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ])) {
if (isAny(businessObject, [ 'bpmn:Lane', 'bpmn:Participant' ]) && isExpanded(businessObject)) {
var childLanes = getChildLanes(element);

View File

@ -5,10 +5,11 @@ var assign = require('lodash/object/assign'),
var is = require('../../util/ModelUtil').is;
var isExpanded = require('../../util/DiUtil').isExpanded;
var BaseElementFactory = require('diagram-js/lib/core/ElementFactory'),
LabelUtil = require('../../util/LabelUtil');
/**
* A bpmn-aware factory for diagram-js shapes
*/
@ -73,6 +74,10 @@ ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
}
}
if (attrs.processRef) {
businessObject.processRef = attrs.processRef;
}
if (attrs.isExpanded) {
businessObject.di.isExpanded = attrs.isExpanded;
}
@ -120,9 +125,8 @@ ElementFactory.prototype.createBpmnElement = function(elementType, attrs) {
ElementFactory.prototype._getDefaultSize = function(semantic) {
if (is(semantic, 'bpmn:SubProcess')) {
var isExpanded = semantic.di.isExpanded === true;
if (isExpanded) {
if (isExpanded(semantic)) {
return { width: 350, height: 200 };
} else {
return { width: 100, height: 80 };
@ -142,7 +146,11 @@ ElementFactory.prototype._getDefaultSize = function(semantic) {
}
if (is(semantic, 'bpmn:Participant')) {
return { width: 600, height: 250 };
if (!isExpanded(semantic)) {
return { width: 400, height: 100 };
} else {
return { width: 600, height: 250 };
}
}
if (is(semantic, 'bpmn:Lane')) {
@ -167,11 +175,11 @@ ElementFactory.prototype._getDefaultSize = function(semantic) {
ElementFactory.prototype.createParticipantShape = function(collapsed) {
var participantShape = this.createShape({ type: 'bpmn:Participant' });
var attrs = { type: 'bpmn:Participant' };
if (!collapsed) {
participantShape.businessObject.processRef = this._bpmnFactory.create('bpmn:Process');
attrs.processRef = this._bpmnFactory.create('bpmn:Process');
}
return participantShape;
return this.createShape(attrs);
};

View File

@ -68,6 +68,16 @@ ReplaceMenuProvider.prototype.getEntries = function(element) {
return this._createEntries(element, entries);
}
// expanded/collapsed pools
if (is(businessObject, 'bpmn:Participant')) {
entries = filter(replaceOptions.PARTICIPANT, function(entry) {
return isExpanded(businessObject) !== entry.target.isExpanded;
});
return this._createEntries(element, entries);
}
// start events inside event sub processes
if (is(businessObject, 'bpmn:StartEvent') && isEventSubProcess(businessObject.$parent)) {
@ -253,7 +263,6 @@ ReplaceMenuProvider.prototype._createEntries = function(element, replaceOptions)
return menuEntries;
};
/**
* Creates an array of menu entry objects for a given sequence flow.
*
@ -462,4 +471,4 @@ ReplaceMenuProvider.prototype._getAdHocEntry = function(element) {
return adHocEntry;
};
module.exports = ReplaceMenuProvider;
module.exports = ReplaceMenuProvider;

View File

@ -65,6 +65,22 @@ function BpmnReplace(bpmnFactory, replace, selection, modeling) {
newElement.isExpanded = isExpanded(oldBusinessObject);
}
// transform collapsed/expanded pools
if (is(oldBusinessObject, 'bpmn:Participant')) {
// create expanded pool
if (target.isExpanded === true) {
newBusinessObject.processRef = bpmnFactory.create('bpmn:Process');
} else {
// remove children when transforming to collapsed pool
hints.moveChildren = false;
}
// apply same size
newElement.width = element.width;
newElement.height = element.height;
}
newBusinessObject.name = oldBusinessObject.name;
// retain loop characteristics if the target element is not an event sub process
@ -85,7 +101,7 @@ function BpmnReplace(bpmnFactory, replace, selection, modeling) {
newBusinessObject.isForCompensation = true;
}
newElement = replace.replaceElement(element, newElement);
newElement = replace.replaceElement(element, newElement, hints);
if (hints.select !== false) {
selection.select(newElement);

View File

@ -745,3 +745,25 @@ module.exports.SEQUENCE_FLOW = [
className: 'bpmn-icon-conditional-flow'
}
];
module.exports.PARTICIPANT = [
{
label: 'Expanded Pool',
actionName: 'replace-with-expanded-pool',
className: 'bpmn-icon-participant',
target: {
type: 'bpmn:Participant',
isExpanded: true
}
},
{
label: 'Collapsed Pool',
actionName: 'replace-with-collapsed-pool',
// TODO(@janstuemmel): maybe design new icon
className: 'bpmn-icon-lane',
target: {
type: 'bpmn:Participant',
isExpanded: false
}
}
];

View File

@ -341,6 +341,11 @@ function canDrop(element, target, position) {
return true;
}
// disallow to create elements on collapsed pools
if (is(target, 'bpmn:Participant') && !isExpanded(target)) {
return false;
}
// allow to create new participants on
// on existing collaboration and process diagrams
if (is(element, 'bpmn:Participant')) {
@ -401,6 +406,7 @@ function isBoundaryCandidate(element) {
function canAttach(elements, target, source, position) {
if (!Array.isArray(elements)) {
elements = [ elements ];
}

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="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" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:collaboration id="Collaboration_1wsnry3">
<bpmn:participant id="Participant_1" name="Pool" processRef="Process_1" />
<bpmn:participant id="Participant_2" name="Pool" />
<bpmn:messageFlow id="MessageFlow_2" sourceRef="Participant_2" targetRef="Participant_1" />
<bpmn:messageFlow id="MessageFlow_1" sourceRef="Task_1" targetRef="Participant_2" />
<bpmn:messageFlow id="MessageFlow_3" sourceRef="EndEvent_1" targetRef="Participant_2" />
</bpmn:collaboration>
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:laneSet>
<bpmn:lane id="Lane_1">
<bpmn:flowNodeRef>Task_1</bpmn:flowNodeRef>
<bpmn:flowNodeRef>EndEvent_1</bpmn:flowNodeRef>
</bpmn:lane>
<bpmn:lane id="Lane_2" />
</bpmn:laneSet>
<bpmn:task id="Task_1" />
<bpmn:endEvent id="EndEvent_1">
<bpmn:messageEventDefinition />
</bpmn:endEvent>
</bpmn:process>
<bpmn:process id="Process_18059hr" isExecutable="false" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1wsnry3">
<bpmndi:BPMNShape id="Participant_0dhyklk_di" bpmnElement="Participant_1">
<dc:Bounds x="181" y="82" width="458" height="271" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_1lr6kkb_di" bpmnElement="Lane_1">
<dc:Bounds x="211" y="82" width="428" height="138" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Lane_0c9o07a_di" bpmnElement="Lane_2">
<dc:Bounds x="211" y="220" width="428" height="133" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_06fwm9a_di" bpmnElement="Participant_2">
<dc:Bounds x="237" y="436" width="378" height="115" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="MessageFlow_1xdh89f_di" bpmnElement="MessageFlow_2">
<di:waypoint xsi:type="dc:Point" x="368" y="436" />
<di:waypoint xsi:type="dc:Point" x="368" y="353" />
<bpmndi:BPMNLabel>
<dc:Bounds x="381" y="384.5" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Task_0atv48s_di" bpmnElement="Task_1">
<dc:Bounds x="276" y="112" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="MessageFlow_19wrxbj_di" bpmnElement="MessageFlow_1">
<di:waypoint xsi:type="dc:Point" x="326" y="192" />
<di:waypoint xsi:type="dc:Point" x="326" y="436" />
<bpmndi:BPMNLabel>
<dc:Bounds x="281" y="304" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1ooqi8q_di" bpmnElement="EndEvent_1">
<dc:Bounds x="535" y="134" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="508" y="170" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="MessageFlow_15s8xey_di" bpmnElement="MessageFlow_3">
<di:waypoint xsi:type="dc:Point" x="553" y="170" />
<di:waypoint xsi:type="dc:Point" x="553" y="434" />
<bpmndi:BPMNLabel>
<dc:Bounds x="508" y="293" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -187,6 +187,43 @@ describe('features/replace - bpmn replace', function() {
});
describe('should replace in collaboration', function() {
var diagramXML = require('./BpmnReplace.collaboration.bpmn');
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
it('expanded with collapsed pool', inject(function(elementRegistry, bpmnReplace) {
// given
var shape = elementRegistry.get('Participant_1');
// when
var newShape = bpmnReplace.replaceElement(shape, { type: 'bpmn:Participant', isExpanded: false });
// then
expect(isExpanded(newShape)).to.be.false; // collapsed
expect(newShape.children).to.be.empty;
}));
it('collapsed with expande pool', inject(function(elementRegistry, bpmnReplace) {
// given
var shape = elementRegistry.get('Participant_2');
// when
var newShape = bpmnReplace.replaceElement(shape, { type: 'bpmn:Participant', isExpanded: true });
// then
expect(isExpanded(newShape)).to.be.true; // expanded
expect(newShape.children).to.be.empty;
}));
});
describe('position and size', function() {
var diagramXML = require('../../../fixtures/bpmn/features/replace/01_replace.bpmn');

View File

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="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="_biH3sOTeEeS2YerRfpjPrw" targetNamespace="http://activiti.org/bpmn" exporter="camunda modeler" exporterVersion="2.6.0" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:collaboration id="Collaboration">
<bpmn2:textAnnotation id="TextAnnotation_Global" />
<bpmn2:participant id="Participant" name="Pool" processRef="Process" />
<bpmn2:participant id="OtherParticipant" name="Pool" processRef="OtherProcess" />
<bpmn2:participant id="CollapsedParticipant" name="Pool" />
<bpmn2:textAnnotation id="TextAnnotation_Global" />
</bpmn2:collaboration>
<bpmn2:process id="Process" isExecutable="false">
<bpmn2:startEvent id="StartEvent_None" />
@ -39,6 +40,7 @@
<bpmn2:task id="Task_in_OtherParticipant" />
<bpmn2:textAnnotation id="TextAnnotation_OtherParticipant" />
</bpmn2:process>
<bpmn2:process id="Process_18q5tmq" isExecutable="false" />
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration">
<bpmndi:BPMNShape id="_BPMNShape_Participant_3" bpmnElement="Participant" isHorizontal="true">
@ -128,6 +130,9 @@
<dc:Bounds x="299" y="114" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Participant_09perow_di" bpmnElement="CollapsedParticipant">
<dc:Bounds x="125" y="704" width="594" height="117" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View File

@ -839,6 +839,13 @@ describe('features/modeling/rules - BpmnRules', function() {
expectCanDrop('TextAnnotation_Global', 'Participant', true);
}));
it('drop element -> collapsed Participant', inject(function(canvas){
expectCanDrop('StartEvent_None', 'CollapsedParticipant', false);
expectCanDrop('SubProcess', 'CollapsedParticipant', false);
expectCanDrop('Task_in_SubProcess', 'CollapsedParticipant', false);
expectCanDrop('TextAnnotation_Global', 'CollapsedParticipant', false);
}));
});