feat(modeling): reconnect flows on collapse
This commit is contained in:
parent
b8ed73b7f8
commit
12fe06bfa6
|
@ -0,0 +1,74 @@
|
||||||
|
|
||||||
|
import inherits from 'inherits';
|
||||||
|
|
||||||
|
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
|
||||||
|
|
||||||
|
import {
|
||||||
|
forEach
|
||||||
|
} from 'min-dash';
|
||||||
|
|
||||||
|
import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
|
||||||
|
import { selfAndAllChildren } from 'diagram-js/lib/util/Elements';
|
||||||
|
|
||||||
|
import { isExpanded } from '../../../util/DiUtil';
|
||||||
|
|
||||||
|
|
||||||
|
export default function ToggleCollapseConnectionBehaviour(
|
||||||
|
eventBus, modeling
|
||||||
|
) {
|
||||||
|
|
||||||
|
CommandInterceptor.call(this, eventBus);
|
||||||
|
|
||||||
|
this.postExecuted('shape.toggleCollapse', 1500, function(context) {
|
||||||
|
|
||||||
|
// var shape = context.shape;
|
||||||
|
var shape = context.shape;
|
||||||
|
|
||||||
|
// only change connections when collapsing
|
||||||
|
if (isExpanded(shape)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var allChildren = selfAndAllChildren(shape);
|
||||||
|
|
||||||
|
allChildren.forEach(function(child) {
|
||||||
|
|
||||||
|
// Ensure that the connection array is not modified during iteration
|
||||||
|
var incomingConnections = child.incoming.slice(),
|
||||||
|
outgoingConnections = child.outgoing.slice();
|
||||||
|
|
||||||
|
forEach(incomingConnections, function(c) {
|
||||||
|
handleConnection(c, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
forEach(outgoingConnections, function(c) {
|
||||||
|
handleConnection(c, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function handleConnection(c, incoming) {
|
||||||
|
if (allChildren.includes(c.source) && allChildren.includes(c.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incoming) {
|
||||||
|
modeling.reconnectEnd(c, shape, getMid(shape));
|
||||||
|
} else {
|
||||||
|
modeling.reconnectStart(c, shape, getMid(shape));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(ToggleCollapseConnectionBehaviour, CommandInterceptor);
|
||||||
|
|
||||||
|
ToggleCollapseConnectionBehaviour.$inject = [
|
||||||
|
'eventBus',
|
||||||
|
'modeling',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import RemoveElementBehavior from './RemoveElementBehavior';
|
||||||
import SpaceToolBehavior from './SpaceToolBehavior';
|
import SpaceToolBehavior from './SpaceToolBehavior';
|
||||||
import SubProcessStartEventBehavior from './SubProcessStartEventBehavior';
|
import SubProcessStartEventBehavior from './SubProcessStartEventBehavior';
|
||||||
import SubProcessPlaneBehavior from './SubProcessPlaneBehavior';
|
import SubProcessPlaneBehavior from './SubProcessPlaneBehavior';
|
||||||
|
import ToggleCollapseConnectionBehaviour from './ToggleCollapseConnectionBehaviour';
|
||||||
import ToggleElementCollapseBehaviour from './ToggleElementCollapseBehaviour';
|
import ToggleElementCollapseBehaviour from './ToggleElementCollapseBehaviour';
|
||||||
import UnclaimIdBehavior from './UnclaimIdBehavior';
|
import UnclaimIdBehavior from './UnclaimIdBehavior';
|
||||||
import UpdateFlowNodeRefsBehavior from './UpdateFlowNodeRefsBehavior';
|
import UpdateFlowNodeRefsBehavior from './UpdateFlowNodeRefsBehavior';
|
||||||
|
@ -64,6 +65,7 @@ export default {
|
||||||
'replaceElementBehaviour',
|
'replaceElementBehaviour',
|
||||||
'resizeBehavior',
|
'resizeBehavior',
|
||||||
'resizeLaneBehavior',
|
'resizeLaneBehavior',
|
||||||
|
'toggleCollapseConnectionBehaviour',
|
||||||
'toggleElementCollapseBehaviour',
|
'toggleElementCollapseBehaviour',
|
||||||
'spaceToolBehavior',
|
'spaceToolBehavior',
|
||||||
'subProcessStartEventBehavior',
|
'subProcessStartEventBehavior',
|
||||||
|
@ -100,6 +102,7 @@ export default {
|
||||||
resizeBehavior: [ 'type', ResizeBehavior ],
|
resizeBehavior: [ 'type', ResizeBehavior ],
|
||||||
resizeLaneBehavior: [ 'type', ResizeLaneBehavior ],
|
resizeLaneBehavior: [ 'type', ResizeLaneBehavior ],
|
||||||
removeElementBehavior: [ 'type', RemoveElementBehavior ],
|
removeElementBehavior: [ 'type', RemoveElementBehavior ],
|
||||||
|
toggleCollapseConnectionBehaviour: [ 'type', ToggleCollapseConnectionBehaviour ],
|
||||||
toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ],
|
toggleElementCollapseBehaviour : [ 'type', ToggleElementCollapseBehaviour ],
|
||||||
spaceToolBehavior: [ 'type', SpaceToolBehavior ],
|
spaceToolBehavior: [ 'type', SpaceToolBehavior ],
|
||||||
subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ],
|
subProcessStartEventBehavior: [ 'type', SubProcessStartEventBehavior ],
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_0aznxr3" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.11.1" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0">
|
||||||
|
<bpmn:collaboration id="Collaboration_01harm7">
|
||||||
|
<bpmn:participant id="Participant_11e0vmc" processRef="Process_1vdnuf7" />
|
||||||
|
<bpmn:participant id="Participant_0dgf5bz" processRef="Process_03jefgm" />
|
||||||
|
<bpmn:messageFlow id="MessageFlow_1" sourceRef="Activity_0y7prp6" targetRef="Task_1" />
|
||||||
|
<bpmn:messageFlow id="MessageFlow_2" sourceRef="Task_1" targetRef="Activity_0y7prp6" />
|
||||||
|
</bpmn:collaboration>
|
||||||
|
<bpmn:process id="Process_1vdnuf7" isExecutable="true">
|
||||||
|
<bpmn:dataStoreReference id="DataStoreReference_03oe1ud" />
|
||||||
|
<bpmn:startEvent id="Event_19nvrv6" />
|
||||||
|
<bpmn:subProcess id="Subprocess_1">
|
||||||
|
<bpmn:startEvent id="Event_02ek85q">
|
||||||
|
<bpmn:outgoing>Flow_01hgf3n</bpmn:outgoing>
|
||||||
|
</bpmn:startEvent>
|
||||||
|
<bpmn:task id="Task_1">
|
||||||
|
<bpmn:incoming>Flow_01hgf3n</bpmn:incoming>
|
||||||
|
<bpmn:outgoing>Flow_0h4tfi2</bpmn:outgoing>
|
||||||
|
<bpmn:property id="Property_0kwu583" name="__targetRef_placeholder" />
|
||||||
|
<bpmn:dataInputAssociation id="DataAssociation_1">
|
||||||
|
<bpmn:sourceRef>DataStoreReference_03oe1ud</bpmn:sourceRef>
|
||||||
|
<bpmn:targetRef>Property_0kwu583</bpmn:targetRef>
|
||||||
|
</bpmn:dataInputAssociation>
|
||||||
|
<bpmn:dataOutputAssociation id="DataAssociation_2">
|
||||||
|
<bpmn:targetRef>DataStoreReference_03oe1ud</bpmn:targetRef>
|
||||||
|
</bpmn:dataOutputAssociation>
|
||||||
|
</bpmn:task>
|
||||||
|
<bpmn:sequenceFlow id="Flow_01hgf3n" sourceRef="Event_02ek85q" targetRef="Task_1" />
|
||||||
|
<bpmn:endEvent id="Event_1qrx5os">
|
||||||
|
<bpmn:incoming>Flow_0h4tfi2</bpmn:incoming>
|
||||||
|
</bpmn:endEvent>
|
||||||
|
<bpmn:sequenceFlow id="Flow_0h4tfi2" sourceRef="Task_1" targetRef="Event_1qrx5os" />
|
||||||
|
<bpmn:association id="Association_1" sourceRef="Task_1" targetRef="TextAnnotation_06fcwe1" />
|
||||||
|
</bpmn:subProcess>
|
||||||
|
<bpmn:textAnnotation id="TextAnnotation_06fcwe1" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmn:process id="Process_03jefgm" isExecutable="false">
|
||||||
|
<bpmn:task id="Activity_0y7prp6" />
|
||||||
|
</bpmn:process>
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||||
|
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_01harm7">
|
||||||
|
<bpmndi:BPMNShape id="Participant_11e0vmc_di" bpmnElement="Participant_11e0vmc" isHorizontal="true">
|
||||||
|
<dc:Bounds x="160" y="230" width="600" height="317" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="TextAnnotation_06fcwe1_di" bpmnElement="TextAnnotation_06fcwe1">
|
||||||
|
<dc:Bounds x="520" y="250" width="100" height="30" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_19nvrv6_di" bpmnElement="Event_19nvrv6">
|
||||||
|
<dc:Bounds x="202" y="399" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Subprocess_1_di" bpmnElement="Subprocess_1" isExpanded="true">
|
||||||
|
<dc:Bounds x="290" y="310" width="350" height="207" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_01hgf3n_di" bpmnElement="Flow_01hgf3n">
|
||||||
|
<di:waypoint x="366" y="417" />
|
||||||
|
<di:waypoint x="420" y="417" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0h4tfi2_di" bpmnElement="Flow_0h4tfi2">
|
||||||
|
<di:waypoint x="520" y="417" />
|
||||||
|
<di:waypoint x="582" y="417" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="Event_02ek85q_di" bpmnElement="Event_02ek85q">
|
||||||
|
<dc:Bounds x="330" y="399" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
|
||||||
|
<dc:Bounds x="420" y="377" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Event_1qrx5os_di" bpmnElement="Event_1qrx5os">
|
||||||
|
<dc:Bounds x="582" y="399" width="36" height="36" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Association_0hx47wy_di" bpmnElement="Association_1">
|
||||||
|
<di:waypoint x="496" y="377" />
|
||||||
|
<di:waypoint x="560" y="280" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNShape id="Participant_0dgf5bz_di" bpmnElement="Participant_0dgf5bz" isHorizontal="true">
|
||||||
|
<dc:Bounds x="160" y="590" width="600" height="250" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="Activity_0y7prp6_di" bpmnElement="Activity_0y7prp6">
|
||||||
|
<dc:Bounds x="420" y="680" width="100" height="80" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNShape id="DataStoreReference_03oe1ud_di" bpmnElement="DataStoreReference_03oe1ud">
|
||||||
|
<dc:Bounds x="235" y="85" width="50" height="50" />
|
||||||
|
</bpmndi:BPMNShape>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_1dx18am_di" bpmnElement="MessageFlow_1">
|
||||||
|
<di:waypoint x="460" y="680" />
|
||||||
|
<di:waypoint x="460" y="457" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="Flow_0vd4mvq_di" bpmnElement="MessageFlow_2">
|
||||||
|
<di:waypoint x="490" y="457" />
|
||||||
|
<di:waypoint x="490" y="680" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="DataInputAssociation_1kczmjn_di" bpmnElement="DataAssociation_1">
|
||||||
|
<di:waypoint x="278" y="135" />
|
||||||
|
<di:waypoint x="453" y="377" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
<bpmndi:BPMNEdge id="DataOutputAssociation_0xkes2r_di" bpmnElement="DataAssociation_2">
|
||||||
|
<di:waypoint x="442" y="377" />
|
||||||
|
<di:waypoint x="269" y="135" />
|
||||||
|
</bpmndi:BPMNEdge>
|
||||||
|
</bpmndi:BPMNPlane>
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</bpmn:definitions>
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {
|
||||||
|
bootstrapModeler,
|
||||||
|
inject
|
||||||
|
} from 'test/TestHelper';
|
||||||
|
|
||||||
|
|
||||||
|
import coreModule from 'lib/core';
|
||||||
|
import modelingModule from 'lib/features/modeling';
|
||||||
|
|
||||||
|
var testModules = [
|
||||||
|
coreModule,
|
||||||
|
modelingModule,
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('features/modeling - Toggle Collapse Connection Behavior', function() {
|
||||||
|
|
||||||
|
var diagramXML = require('./ToggleCollapseConnectionBehaviourSpec.bpmn');
|
||||||
|
|
||||||
|
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||||
|
|
||||||
|
describe('Subprocess', function() {
|
||||||
|
|
||||||
|
it('should reconnect flows on collapse', inject(function(elementRegistry, modeling) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var subProcess = elementRegistry.get('Subprocess_1'),
|
||||||
|
commentConnection = elementRegistry.get('Association_1'),
|
||||||
|
incommingDataConnection = elementRegistry.get('DataAssociation_1'),
|
||||||
|
outgoingDataConnection = elementRegistry.get('DataAssociation_2'),
|
||||||
|
incommingMessageFlow = elementRegistry.get('MessageFlow_1'),
|
||||||
|
outgoingMessageFlow = elementRegistry.get('MessageFlow_2');
|
||||||
|
|
||||||
|
// when
|
||||||
|
modeling.toggleCollapse(subProcess);
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(commentConnection.source).to.equal(subProcess);
|
||||||
|
expect(incommingDataConnection.target).to.equal(subProcess);
|
||||||
|
expect(outgoingDataConnection.source).to.equal(subProcess);
|
||||||
|
expect(incommingMessageFlow.target).to.equal(subProcess);
|
||||||
|
expect(outgoingMessageFlow.source).to.equal(subProcess);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should undo', inject(function(elementRegistry, modeling, commandStack) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var subProcess = elementRegistry.get('Subprocess_1'),
|
||||||
|
task = elementRegistry.get('Task_1'),
|
||||||
|
commentConnection = elementRegistry.get('Association_1'),
|
||||||
|
incommingDataConnection = elementRegistry.get('DataAssociation_1'),
|
||||||
|
outgoingDataConnection = elementRegistry.get('DataAssociation_2'),
|
||||||
|
incommingMessageFlow = elementRegistry.get('MessageFlow_1'),
|
||||||
|
outgoingMessageFlow = elementRegistry.get('MessageFlow_2');
|
||||||
|
|
||||||
|
modeling.toggleCollapse(subProcess);
|
||||||
|
|
||||||
|
// when
|
||||||
|
commandStack.undo();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(commentConnection.source).to.equal(task);
|
||||||
|
expect(incommingDataConnection.target).to.equal(task);
|
||||||
|
expect(outgoingDataConnection.source).to.equal(task);
|
||||||
|
expect(incommingMessageFlow.target).to.equal(task);
|
||||||
|
expect(outgoingMessageFlow.source).to.equal(task);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
it('should redo', inject(function(elementRegistry, modeling, commandStack) {
|
||||||
|
|
||||||
|
// given
|
||||||
|
var subProcess = elementRegistry.get('Subprocess_1'),
|
||||||
|
commentConnection = elementRegistry.get('Association_1'),
|
||||||
|
incommingDataConnection = elementRegistry.get('DataAssociation_1'),
|
||||||
|
outgoingDataConnection = elementRegistry.get('DataAssociation_2'),
|
||||||
|
incommingMessageFlow = elementRegistry.get('MessageFlow_1'),
|
||||||
|
outgoingMessageFlow = elementRegistry.get('MessageFlow_2');
|
||||||
|
|
||||||
|
modeling.toggleCollapse(subProcess);
|
||||||
|
|
||||||
|
// when
|
||||||
|
commandStack.undo();
|
||||||
|
commandStack.redo();
|
||||||
|
|
||||||
|
// then
|
||||||
|
expect(commentConnection.source).to.equal(subProcess);
|
||||||
|
expect(incommingDataConnection.target).to.equal(subProcess);
|
||||||
|
expect(outgoingDataConnection.source).to.equal(subProcess);
|
||||||
|
expect(incommingMessageFlow.target).to.equal(subProcess);
|
||||||
|
expect(outgoingMessageFlow.source).to.equal(subProcess);
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue