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.
212 lines
5.1 KiB
212 lines
5.1 KiB
import { |
|
mid, |
|
setSnapped |
|
} from 'diagram-js/lib/features/snapping/SnapUtil'; |
|
|
|
import { isCmd } from 'diagram-js/lib/features/keyboard/KeyboardUtil'; |
|
|
|
import { |
|
getOrientation |
|
} from 'diagram-js/lib/layout/LayoutUtil'; |
|
|
|
import { is } from '../../util/ModelUtil'; |
|
|
|
import { isAny } from '../modeling/util/ModelingUtil'; |
|
|
|
import { some } from 'min-dash'; |
|
|
|
var HIGHER_PRIORITY = 1250; |
|
|
|
var BOUNDARY_TO_HOST_THRESHOLD = 40; |
|
|
|
var TARGET_BOUNDS_PADDING = 20, |
|
TASK_BOUNDS_PADDING = 10; |
|
|
|
var TARGET_CENTER_PADDING = 20; |
|
|
|
var AXES = [ 'x', 'y' ]; |
|
|
|
var abs = Math.abs; |
|
|
|
/** |
|
* Snap during connect. |
|
* |
|
* @param {EventBus} eventBus |
|
*/ |
|
export default function BpmnConnectSnapping(eventBus) { |
|
eventBus.on([ |
|
'connect.hover', |
|
'connect.move', |
|
'connect.end', |
|
], HIGHER_PRIORITY, function(event) { |
|
var context = event.context, |
|
canExecute = context.canExecute, |
|
start = context.start, |
|
hover = context.hover, |
|
source = context.source, |
|
target = context.target; |
|
|
|
// do NOT snap on CMD |
|
if (event.originalEvent && isCmd(event.originalEvent)) { |
|
return; |
|
} |
|
|
|
if (!context.initialConnectionStart) { |
|
context.initialConnectionStart = context.connectionStart; |
|
} |
|
|
|
// snap hover |
|
if (canExecute && hover) { |
|
snapToShape(event, hover, getTargetBoundsPadding(hover)); |
|
} |
|
|
|
if (hover && isAnyType(canExecute, [ |
|
'bpmn:Association', |
|
'bpmn:DataInputAssociation', |
|
'bpmn:DataOutputAssociation', |
|
'bpmn:SequenceFlow' |
|
])) { |
|
context.connectionStart = mid(start); |
|
|
|
// snap hover |
|
if (isAny(hover, [ 'bpmn:Event', 'bpmn:Gateway' ])) { |
|
snapToPosition(event, mid(hover)); |
|
} |
|
|
|
// snap hover |
|
if (isAny(hover, [ 'bpmn:Task', 'bpmn:SubProcess' ])) { |
|
snapToTargetMid(event, hover); |
|
} |
|
|
|
// snap source and target |
|
if (is(source, 'bpmn:BoundaryEvent') && target === source.host) { |
|
snapBoundaryEventLoop(event); |
|
} |
|
|
|
} else if (isType(canExecute, 'bpmn:MessageFlow')) { |
|
|
|
if (is(start, 'bpmn:Event')) { |
|
|
|
// snap start |
|
context.connectionStart = mid(start); |
|
} |
|
|
|
if (is(hover, 'bpmn:Event')) { |
|
|
|
// snap hover |
|
snapToPosition(event, mid(hover)); |
|
} |
|
|
|
} else { |
|
|
|
// un-snap source |
|
context.connectionStart = context.initialConnectionStart; |
|
} |
|
}); |
|
} |
|
|
|
BpmnConnectSnapping.$inject = [ 'eventBus' ]; |
|
|
|
|
|
// helpers ////////// |
|
|
|
// snap to target if event in target |
|
function snapToShape(event, target, padding) { |
|
AXES.forEach(function(axis) { |
|
var dimensionForAxis = getDimensionForAxis(axis, target); |
|
|
|
if (event[ axis ] < target[ axis ] + padding) { |
|
setSnapped(event, axis, target[ axis ] + padding); |
|
} else if (event[ axis ] > target[ axis ] + dimensionForAxis - padding) { |
|
setSnapped(event, axis, target[ axis ] + dimensionForAxis - padding); |
|
} |
|
}); |
|
} |
|
|
|
// snap to target mid if event in target mid |
|
function snapToTargetMid(event, target) { |
|
var targetMid = mid(target); |
|
|
|
AXES.forEach(function(axis) { |
|
if (isMid(event, target, axis)) { |
|
setSnapped(event, axis, targetMid[ axis ]); |
|
} |
|
}); |
|
} |
|
|
|
// snap to prevent loop overlapping boundary event |
|
function snapBoundaryEventLoop(event) { |
|
var context = event.context, |
|
source = context.source, |
|
target = context.target; |
|
|
|
if (isReverse(context)) { |
|
return; |
|
} |
|
|
|
var sourceMid = mid(source), |
|
orientation = getOrientation(sourceMid, target, -10), |
|
axes = []; |
|
|
|
if (/top|bottom/.test(orientation)) { |
|
axes.push('x'); |
|
} |
|
|
|
if (/left|right/.test(orientation)) { |
|
axes.push('y'); |
|
} |
|
|
|
axes.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); |
|
} |
|
}); |
|
} |
|
|
|
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); |
|
}); |
|
} |
|
|
|
function getDimensionForAxis(axis, element) { |
|
return axis === 'x' ? element.width : element.height; |
|
} |
|
|
|
function getTargetBoundsPadding(target) { |
|
if (is(target, 'bpmn:Task')) { |
|
return TASK_BOUNDS_PADDING; |
|
} else { |
|
return TARGET_BOUNDS_PADDING; |
|
} |
|
} |
|
|
|
function isMid(event, target, axis) { |
|
return event[ axis ] > target[ axis ] + TARGET_CENTER_PADDING |
|
&& event[ axis ] < target[ axis ] + getDimensionForAxis(axis, target) - TARGET_CENTER_PADDING; |
|
} |
|
|
|
function isReverse(context) { |
|
var hover = context.hover, |
|
source = context.source; |
|
|
|
return hover && source && hover === source; |
|
} |