You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.2 KiB
211 lines
5.2 KiB
import inherits from 'inherits-browser'; |
|
|
|
import { |
|
assign, |
|
filter, |
|
find, |
|
isNumber |
|
} from 'min-dash'; |
|
|
|
import { getMid } from 'diagram-js/lib/layout/LayoutUtil'; |
|
|
|
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; |
|
|
|
import { |
|
getApproxIntersection |
|
} from 'diagram-js/lib/util/LineIntersection'; |
|
|
|
|
|
export default function DropOnFlowBehavior(eventBus, bpmnRules, modeling) { |
|
|
|
CommandInterceptor.call(this, eventBus); |
|
|
|
/** |
|
* Reconnect start / end of a connection after |
|
* dropping an element on a flow. |
|
*/ |
|
|
|
function insertShape(shape, targetFlow, positionOrBounds) { |
|
var waypoints = targetFlow.waypoints, |
|
waypointsBefore, |
|
waypointsAfter, |
|
dockingPoint, |
|
source, |
|
target, |
|
incomingConnection, |
|
outgoingConnection, |
|
oldOutgoing = shape.outgoing.slice(), |
|
oldIncoming = shape.incoming.slice(); |
|
|
|
var mid; |
|
|
|
if (isNumber(positionOrBounds.width)) { |
|
mid = getMid(positionOrBounds); |
|
} else { |
|
mid = positionOrBounds; |
|
} |
|
|
|
var intersection = getApproxIntersection(waypoints, mid); |
|
|
|
if (intersection) { |
|
waypointsBefore = waypoints.slice(0, intersection.index); |
|
waypointsAfter = waypoints.slice(intersection.index + (intersection.bendpoint ? 1 : 0)); |
|
|
|
// due to inaccuracy intersection might have been found |
|
if (!waypointsBefore.length || !waypointsAfter.length) { |
|
return; |
|
} |
|
|
|
dockingPoint = intersection.bendpoint ? waypoints[intersection.index] : mid; |
|
|
|
// if last waypointBefore is inside shape's bounds, ignore docking point |
|
if (waypointsBefore.length === 1 || !isPointInsideBBox(shape, waypointsBefore[waypointsBefore.length - 1])) { |
|
waypointsBefore.push(copy(dockingPoint)); |
|
} |
|
|
|
// if first waypointAfter is inside shape's bounds, ignore docking point |
|
if (waypointsAfter.length === 1 || !isPointInsideBBox(shape, waypointsAfter[0])) { |
|
waypointsAfter.unshift(copy(dockingPoint)); |
|
} |
|
} |
|
|
|
source = targetFlow.source; |
|
target = targetFlow.target; |
|
|
|
if (bpmnRules.canConnect(source, shape, targetFlow)) { |
|
|
|
// reconnect source -> inserted shape |
|
modeling.reconnectEnd(targetFlow, shape, waypointsBefore || mid); |
|
|
|
incomingConnection = targetFlow; |
|
} |
|
|
|
if (bpmnRules.canConnect(shape, target, targetFlow)) { |
|
|
|
if (!incomingConnection) { |
|
|
|
// reconnect inserted shape -> end |
|
modeling.reconnectStart(targetFlow, shape, waypointsAfter || mid); |
|
|
|
outgoingConnection = targetFlow; |
|
} else { |
|
outgoingConnection = modeling.connect( |
|
shape, target, { type: targetFlow.type, waypoints: waypointsAfter } |
|
); |
|
} |
|
} |
|
|
|
var duplicateConnections = [].concat( |
|
|
|
incomingConnection && filter(oldIncoming, function(connection) { |
|
return connection.source === incomingConnection.source; |
|
}) || [], |
|
|
|
outgoingConnection && filter(oldOutgoing, function(connection) { |
|
return connection.target === outgoingConnection.target; |
|
}) || [] |
|
); |
|
|
|
if (duplicateConnections.length) { |
|
modeling.removeElements(duplicateConnections); |
|
} |
|
} |
|
|
|
this.preExecute('elements.move', function(context) { |
|
|
|
var newParent = context.newParent, |
|
shapes = context.shapes, |
|
delta = context.delta, |
|
shape = shapes[0]; |
|
|
|
if (!shape || !newParent) { |
|
return; |
|
} |
|
|
|
// if the new parent is a connection, |
|
// change it to the new parent's parent |
|
if (newParent && newParent.waypoints) { |
|
context.newParent = newParent = newParent.parent; |
|
} |
|
|
|
var shapeMid = getMid(shape); |
|
var newShapeMid = { |
|
x: shapeMid.x + delta.x, |
|
y: shapeMid.y + delta.y |
|
}; |
|
|
|
// find a connection which intersects with the |
|
// element's mid point |
|
var connection = find(newParent.children, function(element) { |
|
var canInsert = bpmnRules.canInsert(shapes, element); |
|
|
|
return canInsert && getApproxIntersection(element.waypoints, newShapeMid); |
|
}); |
|
|
|
if (connection) { |
|
context.targetFlow = connection; |
|
context.position = newShapeMid; |
|
} |
|
|
|
}, true); |
|
|
|
this.postExecuted('elements.move', function(context) { |
|
|
|
var shapes = context.shapes, |
|
targetFlow = context.targetFlow, |
|
position = context.position; |
|
|
|
if (targetFlow) { |
|
insertShape(shapes[0], targetFlow, position); |
|
} |
|
|
|
}, true); |
|
|
|
this.preExecute('shape.create', function(context) { |
|
|
|
var parent = context.parent, |
|
shape = context.shape; |
|
|
|
if (bpmnRules.canInsert(shape, parent)) { |
|
context.targetFlow = parent; |
|
context.parent = parent.parent; |
|
} |
|
}, true); |
|
|
|
this.postExecuted('shape.create', function(context) { |
|
|
|
var shape = context.shape, |
|
targetFlow = context.targetFlow, |
|
positionOrBounds = context.position; |
|
|
|
if (targetFlow) { |
|
insertShape(shape, targetFlow, positionOrBounds); |
|
} |
|
}, true); |
|
} |
|
|
|
inherits(DropOnFlowBehavior, CommandInterceptor); |
|
|
|
DropOnFlowBehavior.$inject = [ |
|
'eventBus', |
|
'bpmnRules', |
|
'modeling' |
|
]; |
|
|
|
|
|
// helpers ///////////////////// |
|
|
|
function isPointInsideBBox(bbox, point) { |
|
var x = point.x, |
|
y = point.y; |
|
|
|
return x >= bbox.x && |
|
x <= bbox.x + bbox.width && |
|
y >= bbox.y && |
|
y <= bbox.y + bbox.height; |
|
} |
|
|
|
function copy(obj) { |
|
return assign({}, obj); |
|
} |
|
|
|
|