2019-05-23 20:37:06 +00:00
|
|
|
import {
|
|
|
|
mid,
|
|
|
|
setSnapped
|
|
|
|
} from 'diagram-js/lib/features/snapping/SnapUtil';
|
|
|
|
|
|
|
|
import { isCmd } from 'diagram-js/lib/features/keyboard/KeyboardUtil';
|
|
|
|
|
2019-06-12 14:38:00 +00:00
|
|
|
import {
|
|
|
|
getOrientation
|
|
|
|
} from 'diagram-js/lib/layout/LayoutUtil';
|
|
|
|
|
2019-05-23 20:37:06 +00:00
|
|
|
import { is } from '../../util/ModelUtil';
|
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
import {
|
|
|
|
every,
|
|
|
|
some
|
|
|
|
} from 'min-dash';
|
2019-05-23 20:37:06 +00:00
|
|
|
|
2019-06-18 13:18:25 +00:00
|
|
|
import { isAny } from '../modeling/util/ModelingUtil';
|
|
|
|
|
2019-05-23 20:37:06 +00:00
|
|
|
var HIGHER_PRIORITY = 1250;
|
|
|
|
|
2019-06-12 14:38:00 +00:00
|
|
|
var BOUNDARY_TO_HOST_THRESHOLD = 40;
|
|
|
|
|
2019-06-07 08:45:58 +00:00
|
|
|
var TARGET_BOUNDS_PADDING = 20;
|
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
var TARGET_CENTER_PADDING = 20;
|
|
|
|
|
2019-06-07 08:45:58 +00:00
|
|
|
var AXES = [ 'x', 'y' ];
|
|
|
|
|
2019-06-12 14:38:00 +00:00
|
|
|
var abs = Math.abs;
|
2019-05-23 20:37:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Snap during connect.
|
|
|
|
*
|
|
|
|
* @param {EventBus} eventBus
|
|
|
|
* @param {Rules} rules
|
|
|
|
*/
|
|
|
|
export default function BpmnConnectSnapping(eventBus, rules) {
|
|
|
|
eventBus.on([
|
|
|
|
'connect.hover',
|
|
|
|
'connect.move',
|
|
|
|
'connect.end',
|
|
|
|
], HIGHER_PRIORITY, function(event) {
|
|
|
|
var context = event.context,
|
|
|
|
source = context.source,
|
|
|
|
target = context.target;
|
|
|
|
|
|
|
|
if (event.originalEvent && isCmd(event.originalEvent)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!context.initialSourcePosition) {
|
|
|
|
context.initialSourcePosition = context.sourcePosition;
|
|
|
|
}
|
|
|
|
|
|
|
|
var connectionAttrs = rules.allowed('connection.create', {
|
|
|
|
source: source,
|
|
|
|
target: target
|
|
|
|
});
|
|
|
|
|
2019-06-07 08:45:58 +00:00
|
|
|
if (target && connectionAttrs) {
|
2019-06-19 06:02:00 +00:00
|
|
|
snapInsideTarget(event, target, getTargetBoundsPadding(target));
|
2019-06-07 08:45:58 +00:00
|
|
|
}
|
|
|
|
|
2019-05-23 20:37:06 +00:00
|
|
|
if (target && isAnyType(connectionAttrs, [
|
|
|
|
'bpmn:Association',
|
|
|
|
'bpmn:DataInputAssociation',
|
|
|
|
'bpmn:DataOutputAssociation',
|
|
|
|
'bpmn:SequenceFlow'
|
|
|
|
])) {
|
|
|
|
|
|
|
|
// snap source
|
|
|
|
context.sourcePosition = mid(source);
|
|
|
|
|
2019-06-18 13:18:25 +00:00
|
|
|
if (isAny(target, ['bpmn:Event', 'bpmn:Gateway'])) {
|
|
|
|
snapToPosition(event, mid(target));
|
|
|
|
}
|
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
if (is(target, 'bpmn:Task')) {
|
|
|
|
snapTargetMidOnCenter(event, target);
|
|
|
|
}
|
|
|
|
|
2019-06-12 14:38:00 +00:00
|
|
|
if (is(source, 'bpmn:BoundaryEvent') && target === source.host) {
|
|
|
|
snapBoundaryEventLoop(event, source, target);
|
|
|
|
}
|
2019-06-18 13:18:25 +00:00
|
|
|
|
2019-05-23 20:37:06 +00:00
|
|
|
} else if (isType(connectionAttrs, 'bpmn:MessageFlow')) {
|
|
|
|
|
|
|
|
if (is(source, 'bpmn:Event')) {
|
|
|
|
|
|
|
|
// snap source
|
|
|
|
context.sourcePosition = mid(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is(target, 'bpmn:Event')) {
|
|
|
|
|
|
|
|
// snap target
|
|
|
|
snapToPosition(event, mid(target));
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// un-snap source
|
|
|
|
context.sourcePosition = context.initialSourcePosition;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
BpmnConnectSnapping.$inject = [
|
|
|
|
'eventBus',
|
|
|
|
'rules'
|
|
|
|
];
|
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
function snapInsideTarget(event, target, padding) {
|
2019-06-07 08:45:58 +00:00
|
|
|
|
|
|
|
AXES.forEach(function(axis) {
|
|
|
|
var matchingTargetDimension = getDimensionForAxis(axis, target),
|
|
|
|
newCoordinate;
|
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
if (event[axis] < target[axis] + padding) {
|
|
|
|
newCoordinate = target[axis] + padding;
|
|
|
|
} else if (event[axis] > target[axis] + matchingTargetDimension - padding) {
|
|
|
|
newCoordinate = target[axis] + matchingTargetDimension - padding;
|
2019-06-07 08:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (newCoordinate) {
|
|
|
|
setSnapped(event, axis, newCoordinate);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2019-06-12 14:38:00 +00:00
|
|
|
|
2019-06-19 06:02:00 +00:00
|
|
|
// snap to target mid if event position in center area
|
|
|
|
function snapTargetMidOnCenter(event, target) {
|
|
|
|
|
|
|
|
var isCenter = every(AXES, function(axis) {
|
|
|
|
var coordinate = event[axis],
|
|
|
|
matchingTargetDimension = getDimensionForAxis(axis, target);
|
|
|
|
|
|
|
|
return coordinate > target[axis] + TARGET_CENTER_PADDING
|
|
|
|
&& coordinate < target[axis] + matchingTargetDimension - TARGET_CENTER_PADDING;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isCenter) {
|
|
|
|
snapToPosition(event, mid(target));
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-12 14:38:00 +00:00
|
|
|
// snap outside of Boundary Event surroundings
|
|
|
|
function snapBoundaryEventLoop(event, source, target) {
|
|
|
|
var sourceMid = mid(source),
|
|
|
|
orientation = getOrientation(sourceMid, target, -10),
|
|
|
|
snappingAxes = [];
|
|
|
|
|
|
|
|
if (/top|bottom/.test(orientation)) {
|
|
|
|
snappingAxes.push('x');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (/left|right/.test(orientation)) {
|
|
|
|
snappingAxes.push('y');
|
|
|
|
}
|
|
|
|
|
|
|
|
snappingAxes.forEach(function(axis) {
|
|
|
|
var coordinate = event[axis], newCoordinate;
|
|
|
|
|
|
|
|
if (abs(coordinate - sourceMid[axis]) < BOUNDARY_TO_HOST_THRESHOLD) {
|
|
|
|
if (coordinate > sourceMid[axis]) {
|
|
|
|
newCoordinate = sourceMid[axis] + BOUNDARY_TO_HOST_THRESHOLD;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
newCoordinate = sourceMid[axis] - BOUNDARY_TO_HOST_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
setSnapped(event, axis, newCoordinate);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-05-23 20:37:06 +00:00
|
|
|
// helpers //////////
|
|
|
|
|
|
|
|
function snapToPosition(event, position) {
|
|
|
|
setSnapped(event, 'x', position.x);
|
|
|
|
setSnapped(event, 'y', position.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
function isType(attrs, type) {
|
|
|
|
return attrs && attrs.type === type;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isAnyType(attrs, types) {
|
|
|
|
return some(types, function(type) {
|
|
|
|
return isType(attrs, type);
|
|
|
|
});
|
2019-06-07 08:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getDimensionForAxis(axis, element) {
|
|
|
|
return axis === 'x' ? element.width : element.height;
|
|
|
|
}
|
2019-06-19 06:02:00 +00:00
|
|
|
|
|
|
|
function getTargetBoundsPadding(target) {
|
|
|
|
if (is(target, 'bpmn:Task')) {
|
|
|
|
return 10;
|
|
|
|
} else {
|
|
|
|
return TARGET_BOUNDS_PADDING;
|
|
|
|
}
|
|
|
|
}
|