From 4732dcfc74d2023894b9be2ecf30313985501530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20St=C3=BCmmel?= Date: Tue, 8 Mar 2016 17:21:41 +0100 Subject: [PATCH] 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 --- .../context-pad/ContextPadProvider.js | 4 +- lib/features/modeling/ElementFactory.js | 22 ++++-- .../popup-menu/ReplaceMenuProvider.js | 13 +++- lib/features/replace/BpmnReplace.js | 18 ++++- lib/features/replace/ReplaceOptions.js | 22 ++++++ lib/features/rules/BpmnRules.js | 6 ++ .../replace/BpmnReplace.collaboration.bpmn | 70 +++++++++++++++++++ test/spec/features/replace/BpmnReplaceSpec.js | 37 ++++++++++ .../rules/BpmnRules.collaboration.bpmn | 7 +- test/spec/features/rules/BpmnRulesSpec.js | 7 ++ 10 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 test/spec/features/replace/BpmnReplace.collaboration.bpmn diff --git a/lib/features/context-pad/ContextPadProvider.js b/lib/features/context-pad/ContextPadProvider.js index b852594c..f54aba10 100644 --- a/lib/features/context-pad/ContextPadProvider.js +++ b/lib/features/context-pad/ContextPadProvider.js @@ -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); diff --git a/lib/features/modeling/ElementFactory.js b/lib/features/modeling/ElementFactory.js index 5e073578..ae9bf9e6 100644 --- a/lib/features/modeling/ElementFactory.js +++ b/lib/features/modeling/ElementFactory.js @@ -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); }; diff --git a/lib/features/popup-menu/ReplaceMenuProvider.js b/lib/features/popup-menu/ReplaceMenuProvider.js index cfd6a0ac..dc5cf8e2 100644 --- a/lib/features/popup-menu/ReplaceMenuProvider.js +++ b/lib/features/popup-menu/ReplaceMenuProvider.js @@ -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; \ No newline at end of file +module.exports = ReplaceMenuProvider; diff --git a/lib/features/replace/BpmnReplace.js b/lib/features/replace/BpmnReplace.js index b50bbafd..66a35e65 100644 --- a/lib/features/replace/BpmnReplace.js +++ b/lib/features/replace/BpmnReplace.js @@ -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); diff --git a/lib/features/replace/ReplaceOptions.js b/lib/features/replace/ReplaceOptions.js index 57012865..e620dec7 100644 --- a/lib/features/replace/ReplaceOptions.js +++ b/lib/features/replace/ReplaceOptions.js @@ -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 + } + } +]; diff --git a/lib/features/rules/BpmnRules.js b/lib/features/rules/BpmnRules.js index e56bb675..022dfc1d 100644 --- a/lib/features/rules/BpmnRules.js +++ b/lib/features/rules/BpmnRules.js @@ -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 ]; } diff --git a/test/spec/features/replace/BpmnReplace.collaboration.bpmn b/test/spec/features/replace/BpmnReplace.collaboration.bpmn new file mode 100644 index 00000000..aace6282 --- /dev/null +++ b/test/spec/features/replace/BpmnReplace.collaboration.bpmn @@ -0,0 +1,70 @@ + + + + + + + + + + + + + Task_1 + EndEvent_1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/replace/BpmnReplaceSpec.js b/test/spec/features/replace/BpmnReplaceSpec.js index 2b224480..c83e2283 100644 --- a/test/spec/features/replace/BpmnReplaceSpec.js +++ b/test/spec/features/replace/BpmnReplaceSpec.js @@ -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'); diff --git a/test/spec/features/rules/BpmnRules.collaboration.bpmn b/test/spec/features/rules/BpmnRules.collaboration.bpmn index 89906be0..d7d5fb7c 100644 --- a/test/spec/features/rules/BpmnRules.collaboration.bpmn +++ b/test/spec/features/rules/BpmnRules.collaboration.bpmn @@ -1,9 +1,10 @@ - + + @@ -39,6 +40,7 @@ + @@ -128,6 +130,9 @@ + + + diff --git a/test/spec/features/rules/BpmnRulesSpec.js b/test/spec/features/rules/BpmnRulesSpec.js index d00fd1ef..ba22121a 100644 --- a/test/spec/features/rules/BpmnRulesSpec.js +++ b/test/spec/features/rules/BpmnRulesSpec.js @@ -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); + })); + });