mirror of
https://github.com/sartography/bpmn-js.git
synced 2025-02-22 05:38:09 +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 ImportDockingFix from './ImportDockingFix';
|
||||||
import IsHorizontalFix from './IsHorizontalFix';
|
import IsHorizontalFix from './IsHorizontalFix';
|
||||||
import LabelBehavior from './LabelBehavior';
|
import LabelBehavior from './LabelBehavior';
|
||||||
|
import LayoutConnectionBehavior from './LayoutConnectionBehavior';
|
||||||
import MessageFlowBehavior from './MessageFlowBehavior';
|
import MessageFlowBehavior from './MessageFlowBehavior';
|
||||||
import ModelingFeedback from './ModelingFeedback';
|
import ModelingFeedback from './ModelingFeedback';
|
||||||
import RemoveEmbeddedLabelBoundsBehavior from './RemoveEmbeddedLabelBoundsBehavior';
|
import RemoveEmbeddedLabelBoundsBehavior from './RemoveEmbeddedLabelBoundsBehavior';
|
||||||
@ -57,6 +58,7 @@ export default {
|
|||||||
'importDockingFix',
|
'importDockingFix',
|
||||||
'isHorizontalFix',
|
'isHorizontalFix',
|
||||||
'labelBehavior',
|
'labelBehavior',
|
||||||
|
'layoutConnectionBehavior',
|
||||||
'messageFlowBehavior',
|
'messageFlowBehavior',
|
||||||
'modelingFeedback',
|
'modelingFeedback',
|
||||||
'removeElementBehavior',
|
'removeElementBehavior',
|
||||||
@ -95,6 +97,7 @@ export default {
|
|||||||
importDockingFix: [ 'type', ImportDockingFix ],
|
importDockingFix: [ 'type', ImportDockingFix ],
|
||||||
isHorizontalFix: [ 'type', IsHorizontalFix ],
|
isHorizontalFix: [ 'type', IsHorizontalFix ],
|
||||||
labelBehavior: [ 'type', LabelBehavior ],
|
labelBehavior: [ 'type', LabelBehavior ],
|
||||||
|
layoutConnectionBehavior: [ 'type', LayoutConnectionBehavior ],
|
||||||
messageFlowBehavior: [ 'type', MessageFlowBehavior ],
|
messageFlowBehavior: [ 'type', MessageFlowBehavior ],
|
||||||
modelingFeedback: [ 'type', ModelingFeedback ],
|
modelingFeedback: [ 'type', ModelingFeedback ],
|
||||||
removeElementBehavior: [ 'type', RemoveElementBehavior ],
|
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