mirror of
https://github.com/sartography/bpmn-js.git
synced 2025-02-19 20:28:06 +00:00
feat(modeling): update associations on connection change
This commit is contained in:
parent
7acddf6b3d
commit
5814b72e95
117
lib/features/modeling/behavior/LayoutConnectionBehavior.js
Normal file
117
lib/features/modeling/behavior/LayoutConnectionBehavior.js
Normal file
@ -0,0 +1,117 @@
|
||||
import {
|
||||
assign
|
||||
} from 'min-dash';
|
||||
|
||||
import inherits from 'inherits';
|
||||
|
||||
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
|
||||
|
||||
import { getConnectionAdjustment as getConnectionAnchorPoint } from './util/ConnectionLayoutUtil';
|
||||
|
||||
/**
|
||||
* A component that makes sure that Associations connected to Connections
|
||||
* are updated together with the Connection.
|
||||
*
|
||||
* @param {EventBus} eventBus
|
||||
* @param {Modeling} modeling
|
||||
*/
|
||||
export default function LayoutConnectionBehavior(
|
||||
eventBus, modeling) {
|
||||
|
||||
CommandInterceptor.call(this, eventBus);
|
||||
|
||||
function getnewAnchorPoint(event, point) {
|
||||
|
||||
var context = event.context,
|
||||
connection = context.connection,
|
||||
hints = assign({}, context.hints),
|
||||
newWaypoints = context.newWaypoints || connection.waypoints,
|
||||
oldWaypoints = context.oldWaypoints;
|
||||
|
||||
|
||||
if (typeof hints.startChanged === 'undefined') {
|
||||
hints.startChanged = !!hints.connectionStart;
|
||||
}
|
||||
|
||||
if (typeof hints.endChanged === 'undefined') {
|
||||
hints.endChanged = !!hints.connectionEnd;
|
||||
}
|
||||
|
||||
return getConnectionAnchorPoint(point, newWaypoints, oldWaypoints, hints);
|
||||
}
|
||||
|
||||
this.postExecute([
|
||||
'connection.layout',
|
||||
'connection.updateWaypoints'
|
||||
], function(event) {
|
||||
var context = event.context;
|
||||
|
||||
var connection = context.connection,
|
||||
outgoing = connection.outgoing,
|
||||
incoming = connection.incoming;
|
||||
|
||||
incoming.forEach(function(connection) {
|
||||
var endPoint = connection.waypoints[connection.waypoints.length - 1];
|
||||
var newEndpoint = getnewAnchorPoint(event, endPoint);
|
||||
|
||||
var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]);
|
||||
|
||||
modeling.updateWaypoints(connection, newWaypoints);
|
||||
});
|
||||
|
||||
outgoing.forEach(function(connection) {
|
||||
var startpoint = connection.waypoints[0];
|
||||
var newStartpoint = getnewAnchorPoint(event, startpoint);
|
||||
|
||||
var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(0, -1));
|
||||
|
||||
modeling.updateWaypoints(connection, newWaypoints);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
this.postExecute([
|
||||
'connection.move'
|
||||
], function(event) {
|
||||
var context = event.context;
|
||||
|
||||
var connection = context.connection,
|
||||
outgoing = connection.outgoing,
|
||||
incoming = connection.incoming,
|
||||
delta = context.delta;
|
||||
|
||||
incoming.forEach(function(connection) {
|
||||
var endPoint = connection.waypoints[connection.waypoints.length - 1];
|
||||
var newEndpoint = {
|
||||
x: endPoint.x + delta.x,
|
||||
y: endPoint.y + delta.y
|
||||
};
|
||||
|
||||
var newWaypoints = [].concat(connection.waypoints.slice(0, -1), [ newEndpoint ]);
|
||||
|
||||
modeling.updateWaypoints(connection, newWaypoints);
|
||||
});
|
||||
|
||||
outgoing.forEach(function(connection) {
|
||||
var startpoint = connection.waypoints[0];
|
||||
var newStartpoint = {
|
||||
x: startpoint.x + delta.x,
|
||||
y: startpoint.y + delta.y
|
||||
};
|
||||
|
||||
var newWaypoints = [].concat([ newStartpoint ], connection.waypoints.slice(0, -1));
|
||||
|
||||
modeling.updateWaypoints(connection, newWaypoints);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
inherits(LayoutConnectionBehavior, CommandInterceptor);
|
||||
|
||||
LayoutConnectionBehavior.$inject = [
|
||||
'eventBus',
|
||||
'modeling'
|
||||
];
|
@ -17,6 +17,7 @@ import GroupBehavior from './GroupBehavior';
|
||||
import ImportDockingFix from './ImportDockingFix';
|
||||
import IsHorizontalFix from './IsHorizontalFix';
|
||||
import LabelBehavior from './LabelBehavior';
|
||||
import LayoutConnectionBehavior from './LayoutConnectionBehavior';
|
||||
import MessageFlowBehavior from './MessageFlowBehavior';
|
||||
import ModelingFeedback from './ModelingFeedback';
|
||||
import RemoveEmbeddedLabelBoundsBehavior from './RemoveEmbeddedLabelBoundsBehavior';
|
||||
@ -57,6 +58,7 @@ export default {
|
||||
'importDockingFix',
|
||||
'isHorizontalFix',
|
||||
'labelBehavior',
|
||||
'layoutConnectionBehavior',
|
||||
'messageFlowBehavior',
|
||||
'modelingFeedback',
|
||||
'removeElementBehavior',
|
||||
@ -95,6 +97,7 @@ export default {
|
||||
importDockingFix: [ 'type', ImportDockingFix ],
|
||||
isHorizontalFix: [ 'type', IsHorizontalFix ],
|
||||
labelBehavior: [ 'type', LabelBehavior ],
|
||||
layoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ],
|
||||
messageFlowBehavior: [ 'type', MessageFlowBehavior ],
|
||||
modelingFeedback: [ 'type', ModelingFeedback ],
|
||||
removeElementBehavior: [ 'type', RemoveElementBehavior ],
|
||||
|
16
lib/features/modeling/behavior/util/ConnectionLayoutUtil.js
Normal file
16
lib/features/modeling/behavior/util/ConnectionLayoutUtil.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { getAnchorPointAdjustment } from './LayoutUtil';
|
||||
|
||||
/**
|
||||
* Calculate the new point after the connection waypoints got updated.
|
||||
*
|
||||
* @param {djs.model.Label} label
|
||||
* @param {Array<Point>} newWaypoints
|
||||
* @param {Array<Point>} oldWaypoints
|
||||
* @param {Object} hints
|
||||
*
|
||||
* @return {Point} point
|
||||
*/
|
||||
export function getConnectionAdjustment(position, newWaypoints, oldWaypoints, hints) {
|
||||
return getAnchorPointAdjustment(position, newWaypoints, oldWaypoints, hints).point;
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="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="simple" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.0.0-rc.0" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
|
||||
<bpmn2:process id="Process_1" isExecutable="false">
|
||||
<bpmn2:endEvent id="EndEvent_1" name="End Event">
|
||||
<bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
|
||||
</bpmn2:endEvent>
|
||||
<bpmn2:startEvent id="StartEvent_1" name="Start">
|
||||
<bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
|
||||
</bpmn2:startEvent>
|
||||
<bpmn2:sequenceFlow id="SequenceFlow_1" name="Flow" sourceRef="StartEvent_1" targetRef="EndEvent_1" />
|
||||
<bpmn2:textAnnotation id="TextAnnotation_1" />
|
||||
<bpmn2:association id="Association_1" sourceRef="TextAnnotation_1" targetRef="SequenceFlow_1" />
|
||||
</bpmn2:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
||||
<bpmndi:BPMNEdge id="BPMNEdge_SequenceFlow_3" bpmnElement="SequenceFlow_1" sourceElement="_BPMNShape_StartEvent_11" targetElement="_BPMNShape_EndEvent_2">
|
||||
<di:waypoint x="194" y="150" />
|
||||
<di:waypoint x="700" y="150" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="259" y="124" width="24" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_EndEvent_2" bpmnElement="EndEvent_1">
|
||||
<dc:Bounds x="700" y="132" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="693" y="168" width="51" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_11" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="158" y="132" width="36" height="36" />
|
||||
<bpmndi:BPMNLabel>
|
||||
<dc:Bounds x="164" y="173" width="24" height="14" />
|
||||
</bpmndi:BPMNLabel>
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="TextAnnotation_1yj4jk8_di" bpmnElement="TextAnnotation_1">
|
||||
<dc:Bounds x="500" y="80" width="100" height="30" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNEdge id="Association_1_di" bpmnElement="Association_1">
|
||||
<di:waypoint x="525" y="110" />
|
||||
<di:waypoint x="460" y="150" />
|
||||
</bpmndi:BPMNEdge>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn2:definitions>
|
@ -0,0 +1,113 @@
|
||||
import {
|
||||
bootstrapModeler,
|
||||
inject
|
||||
} from 'test/TestHelper';
|
||||
|
||||
|
||||
import {
|
||||
assign,
|
||||
map,
|
||||
} from 'min-dash';
|
||||
|
||||
import modelingModule from 'lib/features/modeling';
|
||||
import coreModule from 'lib/core';
|
||||
|
||||
describe('behavior - LayoutConnectionBehavior', function() {
|
||||
|
||||
var diagramXML = require('./LayoutConnectionBehavior.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, {
|
||||
modules: [
|
||||
modelingModule,
|
||||
coreModule
|
||||
]
|
||||
}));
|
||||
|
||||
|
||||
describe('association', function() {
|
||||
|
||||
it('should reconnect on bendpoint move', inject(function(elementRegistry, modeling) {
|
||||
|
||||
// given
|
||||
var sequenceFlow = elementRegistry.get('SequenceFlow_1');
|
||||
var association = elementRegistry.get('Association_1');
|
||||
|
||||
// when
|
||||
var newWaypoints = copyWaypoints(sequenceFlow);
|
||||
newWaypoints.splice(1, 0,
|
||||
{ x: 500, y: 300 }
|
||||
);
|
||||
|
||||
var hints = {
|
||||
bendpointMove: {
|
||||
bendpointIndex: 1,
|
||||
insert: true
|
||||
}
|
||||
};
|
||||
|
||||
modeling.updateWaypoints(sequenceFlow, newWaypoints, hints);
|
||||
|
||||
// then
|
||||
expectWaypoints(association, [
|
||||
{ x: 525, y: 110 },
|
||||
{ x: 355, y: 229 },
|
||||
]);
|
||||
}));
|
||||
|
||||
|
||||
it('should reconnect on connection move', inject(function(elementRegistry, modeling) {
|
||||
|
||||
// given
|
||||
var startEvent = elementRegistry.get('StartEvent_1');
|
||||
var endEvent = elementRegistry.get('EndEvent_1');
|
||||
var association = elementRegistry.get('Association_1');
|
||||
|
||||
// when
|
||||
modeling.moveElements([ startEvent, endEvent ], { x: 0, y: 200 });
|
||||
|
||||
// then
|
||||
expectWaypoints(association, [
|
||||
{ x: 525, y: 110 },
|
||||
{ x: 460, y: 350 },
|
||||
]);
|
||||
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
// helpers //////////
|
||||
|
||||
function copyWaypoint(waypoint) {
|
||||
return assign({}, waypoint);
|
||||
}
|
||||
|
||||
function copyWaypoints(connection) {
|
||||
return map(connection.waypoints, function(waypoint) {
|
||||
|
||||
waypoint = copyWaypoint(waypoint);
|
||||
|
||||
if (waypoint.original) {
|
||||
waypoint.original = copyWaypoint(waypoint.original);
|
||||
}
|
||||
|
||||
return waypoint;
|
||||
});
|
||||
}
|
||||
|
||||
function expectWaypoints(connection, expectedWaypoints) {
|
||||
|
||||
var actualWaypoints = connection.waypoints;
|
||||
|
||||
expect(actualWaypoints).to.exist;
|
||||
expect(expectedWaypoints).to.exist;
|
||||
|
||||
expect(connection.waypoints.length).to.eql(expectedWaypoints.length);
|
||||
|
||||
for (var i in actualWaypoints) {
|
||||
expect(actualWaypoints[i].x).to.eql(expectedWaypoints[i].x);
|
||||
expect(actualWaypoints[i].y).to.eql(expectedWaypoints[i].y);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user