in saga/seata-saga-statemachine-designer/src/layout/behavior/LayoutConnectionBehavior.js [173:317]
export default function LayoutConnectionBehavior(injector, layouter, modeling) {
injector.invoke(CommandInterceptor, this);
// specify connection start and end on connection create
this.preExecute([
'connection.create',
'connection.reconnect',
], (context) => {
const source = context.newSource || context.source;
const target = context.newTarget || context.target;
const orientation = getOrientation(source, target);
if (!context.hints) {
context.hints = {};
}
assign(context.hints, getConnectionHints(source, target, orientation));
}, true);
/**
* Update incoming connections.
*
* @param {djs.model.Shape} target
* @param {Array<djs.model.Connection>} [connection]
* @param {string} [orientation]
*/
function updateConnections(target, connection, orientation) {
// (1) get connection
if (!connection) {
connection = target.incoming;
}
let incomingConnectionsByOrientation = {};
// (2) get connections per orientation
if (orientation) {
incomingConnectionsByOrientation[orientation] = connection;
} else {
incomingConnectionsByOrientation = getConnectionByOrientation(target, connection);
}
// (3) update connections per orientation
forEach(
incomingConnectionsByOrientation,
(connections, ot) => {
// (3.1) sort connections
connections = sortConnections(connections, ot);
// (3.2) get new connection start and end
const connectionStartEnd = getConnectionsStartEnd(connections, target, ot);
// (3.3) update connections
connections.forEach((conn, index) => {
const connectionStart = connectionStartEnd[index].start;
const connectionEnd = connectionStartEnd[index].end;
const waypoints = layouter.layoutConnection(conn, {
connectionStart,
connectionEnd,
});
modeling.updateWaypoints(conn, waypoints);
});
},
);
}
// update connections on connection create and delete
// update connections of new target on connection reconnect
this.postExecuted([
'connection.create',
'connection.delete',
'connection.reconnect',
], (context) => {
const { connection } = context;
const source = connection.source || context.source;
const target = connection.target || context.target;
const orientation = getOrientation(source, target);
// update all connections with same orientation
const connections = target.incoming.filter((incoming) => {
const incomingOrientation = getOrientation(incoming.source, incoming.target);
return isSameOrientation(incomingOrientation, orientation);
});
if (!connections.length) {
return;
}
updateConnections(target, connections, orientation);
}, true);
// update connections of old target on connection reconnect
this.preExecute('connection.reconnect', (context) => {
const { connection } = context;
const { source } = connection;
const { target } = connection;
const orientation = getOrientation(source, target);
// update all connections with same orientation except reconnected
const connections = target.incoming.filter((incoming) => {
const incomingOrientation = getOrientation(incoming.source, incoming.target);
return incoming !== connection
&& isSameOrientation(incomingOrientation, orientation);
});
if (!connections.length) {
return;
}
updateConnections(target, connections, orientation);
}, true);
// update connections on elements move
this.postExecuted('elements.move', LOW_PRIORITY, (context) => {
const { shapes } = context;
const { closure } = context;
const { enclosedConnections } = closure;
shapes.forEach((shape) => {
// (1) update incoming connections
const incomingConnections = shape.incoming.filter((incoming) => {
return !enclosedConnections[incoming.id];
});
if (incomingConnections.length) {
updateConnections(shape, incomingConnections);
}
// (2) update outgoing connections
shape.outgoing.forEach((outgoing) => {
if (enclosedConnections[outgoing.id]) {
return;
}
updateConnections(outgoing.target);
});
});
}, true);
}