feat(copy-paste): copy collapsed subprocesses planes

This commit is contained in:
Martin Stamm 2022-01-25 11:39:16 +01:00 committed by fake-join[bot]
parent fb6c649570
commit 892a92f6ec
3 changed files with 389 additions and 4 deletions

View File

@ -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 = {

View File

@ -0,0 +1,218 @@
<?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" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.2.1">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:laneSet />
<bpmn:subProcess id="SubProcess_1">
<bpmn:subProcess id="SubProcess_2">
<bpmn:multiInstanceLoopCharacteristics isSequential="true" />
<bpmn:startEvent id="StartEvent_1" name="hello">
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="EndEvent_1" name="world!">
<bpmn:incoming>SequenceFlow_2</bpmn:incoming>
</bpmn:endEvent>
<bpmn:task id="Task_1" name="wait for it.." default="SequenceFlow_2">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_2</bpmn:outgoing>
<bpmn:outgoing>SequenceFlow_3</bpmn:outgoing>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_2" name="" sourceRef="Task_1" targetRef="EndEvent_1" />
<bpmn:sequenceFlow id="SequenceFlow_1" name="blah" sourceRef="StartEvent_1" targetRef="Task_1" />
<bpmn:boundaryEvent id="BoundaryEvent_2" attachedToRef="Task_1">
<bpmn:timerEventDefinition />
</bpmn:boundaryEvent>
<bpmn:boundaryEvent id="BoundaryEvent_1" attachedToRef="Task_1">
<bpmn:messageEventDefinition />
</bpmn:boundaryEvent>
<bpmn:intermediateThrowEvent id="IntermediateThrowEvent_1">
<bpmn:incoming>SequenceFlow_3</bpmn:incoming>
</bpmn:intermediateThrowEvent>
<bpmn:sequenceFlow id="SequenceFlow_3" sourceRef="Task_1" targetRef="IntermediateThrowEvent_1">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">foo</bpmn:conditionExpression>
</bpmn:sequenceFlow>
</bpmn:subProcess>
<bpmn:textAnnotation id="TextAnnotation_1">
<bpmn:text>foo</bpmn:text>
</bpmn:textAnnotation>
<bpmn:association id="Association_1f53xbo" sourceRef="SubProcess_2" targetRef="TextAnnotation_1" />
</bpmn:subProcess>
<bpmn:subProcess id="SubProcess_3">
<bpmn:subProcess id="Activity_1ee0a66">
<bpmn:multiInstanceLoopCharacteristics isSequential="true" />
<bpmn:startEvent id="Event_1b2gd6e" name="hello">
<bpmn:outgoing>Flow_0jd8k12</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:endEvent id="Event_1dv9cu8" name="world!">
<bpmn:incoming>Flow_0d51bg2</bpmn:incoming>
</bpmn:endEvent>
<bpmn:task id="Activity_1joj57w" name="wait for it.." default="Flow_0d51bg2">
<bpmn:incoming>Flow_0jd8k12</bpmn:incoming>
<bpmn:outgoing>Flow_0fotq2x</bpmn:outgoing>
<bpmn:outgoing>Flow_0d51bg2</bpmn:outgoing>
</bpmn:task>
<bpmn:intermediateThrowEvent id="Event_17rtxbp">
<bpmn:incoming>Flow_0fotq2x</bpmn:incoming>
</bpmn:intermediateThrowEvent>
<bpmn:boundaryEvent id="Event_0pxvi7j" attachedToRef="Activity_1joj57w">
<bpmn:messageEventDefinition id="MessageEventDefinition_1qw0h7u" />
</bpmn:boundaryEvent>
<bpmn:boundaryEvent id="Event_180mojf" attachedToRef="Activity_1joj57w">
<bpmn:timerEventDefinition id="TimerEventDefinition_070cdk8" />
</bpmn:boundaryEvent>
<bpmn:sequenceFlow id="Flow_0fotq2x" sourceRef="Activity_1joj57w" targetRef="Event_17rtxbp">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">foo</bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:sequenceFlow id="Flow_0jd8k12" name="blah" sourceRef="Event_1b2gd6e" targetRef="Activity_1joj57w" />
<bpmn:sequenceFlow id="Flow_0d51bg2" name="" sourceRef="Activity_1joj57w" targetRef="Event_1dv9cu8" />
</bpmn:subProcess>
<bpmn:textAnnotation id="TextAnnotation_0zlz2tq">
<bpmn:text>foo</bpmn:text>
</bpmn:textAnnotation>
<bpmn:association id="Association_0kvpg31" sourceRef="Activity_1ee0a66" targetRef="TextAnnotation_0zlz2tq" />
</bpmn:subProcess>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="SubProcess_1kd6ist_di" bpmnElement="SubProcess_1" isExpanded="true">
<dc:Bounds x="156" y="34" width="613" height="372" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="SubProcess_0gev7mx_di" bpmnElement="SubProcess_2" isExpanded="true">
<dc:Bounds x="215" y="118" width="461" height="259" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_07vo2r8_di" bpmnElement="SequenceFlow_3">
<di:waypoint x="411" y="256" />
<di:waypoint x="411" y="305" />
<di:waypoint x="526" y="305" />
<bpmndi:BPMNLabel>
<dc:Bounds x="381" y="250.5" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_1rtr33r_di" bpmnElement="SequenceFlow_1">
<di:waypoint x="271" y="216" />
<di:waypoint x="361" y="216" />
<bpmndi:BPMNLabel>
<dc:Bounds x="305" y="321" width="21" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="SequenceFlow_0y69l8f_di" bpmnElement="SequenceFlow_2">
<di:waypoint x="461" y="216" />
<di:waypoint x="611" y="216" />
<bpmndi:BPMNLabel>
<dc:Bounds x="421" y="166" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="235" y="198" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="241" y="149" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="EndEvent_0yejuvr_di" bpmnElement="EndEvent_1">
<dc:Bounds x="611" y="198" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="615" y="272" width="29" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1fo63a7_di" bpmnElement="Task_1">
<dc:Bounds x="361" y="176" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="IntermediateThrowEvent_09kpyzx_di" bpmnElement="IntermediateThrowEvent_1">
<dc:Bounds x="526" y="287" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="431" y="323" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_1404oxd_di" bpmnElement="BoundaryEvent_1">
<dc:Bounds x="343" y="238" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="248" y="274" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="BoundaryEvent_1c94bi9_di" bpmnElement="BoundaryEvent_2">
<dc:Bounds x="443" y="238" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="348" y="274" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="TextAnnotation_0h1hhgg_di" bpmnElement="TextAnnotation_1">
<dc:Bounds x="632" y="58" width="100" height="30" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Association_1f53xbo_di" bpmnElement="Association_1f53xbo">
<di:waypoint x="663" y="118" />
<di:waypoint x="649" y="88" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="SubProcess_3_di" bpmnElement="SubProcess_3" isExpanded="false" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="155.5" y="430" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
<bpmndi:BPMNDiagram id="BPMNDiagram_1k76h3w">
<bpmndi:BPMNPlane id="BPMNPlane_121uvzu" bpmnElement="SubProcess_3">
<bpmndi:BPMNShape id="TextAnnotation_0zlz2tq_di" bpmnElement="TextAnnotation_0zlz2tq" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="597" y="160" width="100" height="30" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1ee0a66_di" bpmnElement="Activity_1ee0a66" isExpanded="true" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="180" y="220" width="461" height="259" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_0fotq2x_di" bpmnElement="Flow_0fotq2x" $type="bpmndi:BPMNEdge" businessObject="[object Object]">
<di:waypoint x="376" y="358" />
<di:waypoint x="376" y="407" />
<di:waypoint x="491" y="407" />
<bpmndi:BPMNLabel>
<dc:Bounds x="381" y="250.5" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0jd8k12_di" bpmnElement="Flow_0jd8k12" $type="bpmndi:BPMNEdge" businessObject="[object Object]">
<di:waypoint x="236" y="318" />
<di:waypoint x="326" y="318" />
<bpmndi:BPMNLabel>
<dc:Bounds x="270" y="423" width="21" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0d51bg2_di" bpmnElement="Flow_0d51bg2" $type="bpmndi:BPMNEdge" businessObject="[object Object]">
<di:waypoint x="426" y="318" />
<di:waypoint x="576" y="318" />
<bpmndi:BPMNLabel>
<dc:Bounds x="421" y="166" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Event_1b2gd6e_di" bpmnElement="Event_1b2gd6e" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="200" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="206" y="251" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1dv9cu8_di" bpmnElement="Event_1dv9cu8" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="576" y="300" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="580" y="374" width="30" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1joj57w_di" bpmnElement="Activity_1joj57w" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="326" y="278" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_17rtxbp_di" bpmnElement="Event_17rtxbp" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="491" y="389" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="431" y="323" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0pxvi7j_di" bpmnElement="Event_0pxvi7j" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="308" y="340" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="248" y="274" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_180mojf_di" bpmnElement="Event_180mojf" $type="bpmndi:BPMNShape" businessObject="[object Object]">
<dc:Bounds x="408" y="340" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="348" y="274" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Association_0kvpg31_di" bpmnElement="Association_0kvpg31" $type="bpmndi:BPMNEdge" businessObject="[object Object]">
<di:waypoint x="628" y="220" />
<di:waypoint x="614" y="190" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -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);
}
));
});
});