diff --git a/lib/features/modeling/behavior/SubProcessPlaneBehavior.js b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js index 3f50a075..27407123 100644 --- a/lib/features/modeling/behavior/SubProcessPlaneBehavior.js +++ b/lib/features/modeling/behavior/SubProcessPlaneBehavior.js @@ -4,7 +4,7 @@ import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; import { find } from 'min-dash'; import { isExpanded } from '../../../util/DiUtil'; -import { getBusinessObject, is } from '../../../util/ModelUtil'; +import { getBusinessObject, getDi, is } from '../../../util/ModelUtil'; import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; import { getBBox } from 'diagram-js/lib/util/Elements'; import { @@ -89,6 +89,20 @@ export default function SubProcessPlaneBehavior( }, true); + this.postExecuted('shape.create', function(context) { + var shape = context.shape, + rootElement = context.newRootElement; + + if (!rootElement || !shape.children) { + return; + } + + self._showRecursively(shape.children); + + self._moveChildrenToShape(shape, rootElement); + }, true); + + this.reverted('shape.create', function(context) { var shape = context.shape; if (!isCollapsedSubProcess(shape)) { @@ -284,6 +298,66 @@ export default function SubProcessPlaneBehavior( } }, true); + + // copy-paste /////////// + + // add elements in plane to tree + eventBus.on('copyPaste.createTree', function(context) { + var element = context.element, + children = context.children; + + if (!isCollapsedSubProcess(element)) { + return; + } + + var id = planeId(element); + var parent = elementRegistry.get(id); + + if (parent) { + + // Don't copy invisible root element + children.push.apply(children, parent.children); + } + }); + + // set plane children as direct children of collapsed shape + eventBus.on('copyPaste.copyElement', function(context) { + var descriptor = context.descriptor, + element = context.element, + elements = context.elements; + + var parent = element.parent; + + var isPlane = is(getDi(parent), 'bpmndi:BPMNPlane'); + if (!isPlane) { + return; + } + + var parentId = parent.id.replace(planePostfix, ''); + var referencedShape = find(elements, function(element) { + return element.id === parentId; + }); + + if (!referencedShape) { + return; + } + + descriptor.parent = referencedShape.id; + }); + + // hide children during pasting + eventBus.on('copyPaste.pasteElement', function(context) { + var descriptor = context.descriptor; + + if (!descriptor.parent) { + return; + } + + if (isCollapsedSubProcess(descriptor.parent) || descriptor.parent.hidden) { + descriptor.hidden = true; + } + }); + } inherits(SubProcessPlaneBehavior, CommandInterceptor); @@ -317,11 +391,13 @@ SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target return; } + var childrenBounds = getBBox(visibleChildren); + // target is a plane if (!target.x) { offset = { - x: DEFAULT_POSITION.x - source.x , - y: DEFAULT_POSITION.y - source.y + x: DEFAULT_POSITION.x - childrenBounds.x, + y: DEFAULT_POSITION.y - childrenBounds.y }; } @@ -330,7 +406,6 @@ SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target // move relative to the center of the shape var targetMid = getMid(target); - var childrenBounds = getBBox(visibleChildren); var childrenMid = getMid(childrenBounds); offset = { diff --git a/test/spec/features/modeling/behavior/SubProcessBehavior.copy-paste.bpmn b/test/spec/features/modeling/behavior/SubProcessBehavior.copy-paste.bpmn new file mode 100644 index 00000000..45cdbce7 --- /dev/null +++ b/test/spec/features/modeling/behavior/SubProcessBehavior.copy-paste.bpmn @@ -0,0 +1,218 @@ + + + + + + + + + SequenceFlow_1 + + + SequenceFlow_2 + + + SequenceFlow_1 + SequenceFlow_2 + SequenceFlow_3 + + + + + + + + + + + SequenceFlow_3 + + + foo + + + + foo + + + + + + + + Flow_0jd8k12 + + + Flow_0d51bg2 + + + Flow_0jd8k12 + Flow_0fotq2x + Flow_0d51bg2 + + + Flow_0fotq2x + + + + + + + + + foo + + + + + + foo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/modeling/behavior/SubProcessPlaneBehaviorSpec.js b/test/spec/features/modeling/behavior/SubProcessPlaneBehaviorSpec.js index 60b23f8c..8985bbc4 100644 --- a/test/spec/features/modeling/behavior/SubProcessPlaneBehaviorSpec.js +++ b/test/spec/features/modeling/behavior/SubProcessPlaneBehaviorSpec.js @@ -6,7 +6,11 @@ import { import coreModule from 'lib/core'; import modelingModule from 'lib/features/modeling'; import replaceModule from 'lib/features/replace'; +import bpmnCopyPasteModule from 'lib/features/copy-paste'; +import copyPasteModule from 'diagram-js/lib/features/copy-paste'; + import { is } from 'lib/util/ModelUtil'; +import { keys } from 'min-dash'; describe('features/modeling/behavior - subprocess planes', function() { @@ -59,6 +63,31 @@ describe('features/modeling/behavior - subprocess planes', function() { })); + it('should move children to plane for collapsed subprocess', inject(function(elementFactory, modeling, canvas, bpmnjs) { + + // given + var subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + isExpanded: false + }); + + var child = elementFactory.createShape({ + type: 'bpmn:Task', + parent: subProcess + }); + + // when + modeling.createElements([subProcess, child], { x: 300, y: 300 }, canvas.getRootElement()); + + // then + var diagrams = bpmnjs.getDefinitions().diagrams; + var newPlane = canvas.findRoot(planeId(subProcess)); + expect(diagrams.length).to.equal(2); + expect(newPlane).to.exist; + expect(child.parent).to.equal(newPlane); + })); + + it('should undo', inject(function(elementFactory, modeling, commandStack, canvas, bpmnjs) { // given @@ -309,6 +338,69 @@ describe('features/modeling/behavior - subprocess planes', function() { }); + + describe('copy/paste', function() { + + var copyXML = require('./SubProcessBehavior.copy-paste.bpmn'); + + beforeEach(bootstrapModeler(copyXML, { + modules: [ + coreModule, + modelingModule, + bpmnCopyPasteModule, + copyPasteModule + ] + })); + + + it('should copy collapsed sub process', inject(function(copyPaste, elementRegistry) { + + var subprcoess = elementRegistry.get('SubProcess_3'); + + + // when + var tree = copyPaste.copy([subprcoess]); + + // then + expect(keys(tree)).to.have.length(3); + + + expect(tree[0]).to.have.length(1); + expect(tree[1]).to.have.length(3); + expect(tree[2]).to.have.length(12); + })); + + + it('should paste subprocess plane', inject( + function(canvas, copyPaste, elementRegistry) { + + // given + var subprcoess = elementRegistry.get('SubProcess_3'), + rootElement = canvas.getRootElement(); + + copyPaste.copy(subprcoess); + + // when + var elements = copyPaste.paste({ + element: rootElement, + point: { + x: 300, + y: 300 + } + }); + + + // then + var subprocess = elements[0]; + var newRoot = canvas.findRoot(planeId(subprocess)); + + expect(newRoot).to.exist; + expect(newRoot.children).to.have.length(6); + } + )); + + }); + });