Skip to content

Commit

Permalink
chore(bpmn-snapping): seperate snapping into create/move and connect
Browse files Browse the repository at this point in the history
* move create/move snapping to BpmnCreateMoveSnapping
* move connect snapping to BpmnConnectSnapping
* refactor tests

Related camunda/camunda-modeler#1290
  • Loading branch information
philippfromme committed May 29, 2019
1 parent 94b3a5d commit 84dbeb3
Show file tree
Hide file tree
Showing 16 changed files with 987 additions and 1,468 deletions.
98 changes: 98 additions & 0 deletions lib/features/snapping/BpmnConnectSnapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import {
mid,
setSnapped
} from 'diagram-js/lib/features/snapping/SnapUtil';

import { isCmd } from 'diagram-js/lib/features/keyboard/KeyboardUtil';

import { is } from '../../util/ModelUtil';

import { some } from 'min-dash';

var HIGHER_PRIORITY = 1250;


/**
* 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
});

if (target && isAnyType(connectionAttrs, [
'bpmn:Association',
'bpmn:DataInputAssociation',
'bpmn:DataOutputAssociation',
'bpmn:SequenceFlow'
])) {

// snap source
context.sourcePosition = mid(source);

// snap target
snapToPosition(event, mid(target));
} 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'
];

// 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);
});
}
137 changes: 137 additions & 0 deletions lib/features/snapping/BpmnCreateMoveSnapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import inherits from 'inherits';

import CreateMoveSnapping from 'diagram-js/lib/features/snapping/CreateMoveSnapping';

import {
isSnapped,
setSnapped,
} from 'diagram-js/lib/features/snapping/SnapUtil';

import { is } from '../../util/ModelUtil';

import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil';

import { getBoundaryAttachment } from './BpmnSnappingUtil';

var HIGH_PRIORITY = 1500;


/**
* Snap during create and move.
*
* @param {BpmnRules} bpmnRules
* @param {EventBus} eventBus
* @param {Injector} injector
*/
export default function BpmnCreateMoveSnapping(bpmnRules, eventBus, injector) {
injector.invoke(CreateMoveSnapping, this);

// creating first participant
eventBus.on([ 'create.move', 'create.end' ], HIGH_PRIORITY, setSnappedIfConstrained);

function canAttach(shape, target, position) {
return bpmnRules.canAttach([ shape ], target, null, position) === 'attach';
}

// snap boundary events
eventBus.on([
'create.move',
'create.end',
'shape.move.move',
'shape.move.end'
], HIGH_PRIORITY, function(event) {
var context = event.context,
target = context.target,
shape = context.shape;

if (target && canAttach(shape, target, event) && !isSnapped(event)) {
snapBoundaryEvent(event, target);
}
});
}

inherits(BpmnCreateMoveSnapping, CreateMoveSnapping);

BpmnCreateMoveSnapping.$inject = [
'bpmnRules',
'eventBus',
'injector'
];

BpmnCreateMoveSnapping.prototype.initSnap = function(event) {
var snapContext = CreateMoveSnapping.prototype.initSnap.call(this, event);

var shape = event.shape;

if (is(shape, 'bpmn:Participant')) {

// snap to borders with higher priority
snapContext.setSnapLocations([ 'top-left', 'bottom-right', 'mid' ]);
}

return snapContext;
};

BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shape, target) {
CreateMoveSnapping.prototype.addSnapTargetPoints.call(this, snapPoints, shape, target);

// add sequence flow parents as snap targets
if (is(target, 'bpmn:SequenceFlow')) {
snapPoints = this.addSnapTargetPoints(snapPoints, shape, target.parent);
}

return snapPoints;
};

BpmnCreateMoveSnapping.prototype.getSnapTargets = function(shape, target) {
return CreateMoveSnapping.prototype.getSnapTargets.call(this, shape, target)
.filter(function(snapTarget) {

// do not snap to lanes
return !is(snapTarget, 'bpmn:Lane');
});
};

// helpers //////////

function snapBoundaryEvent(event, target) {
var targetTRBL = asTRBL(target);

var direction = getBoundaryAttachment(event, target);

if (/top/.test(direction)) {
setSnapped(event, 'y', targetTRBL.top);
} else
if (/bottom/.test(direction)) {
setSnapped(event, 'y', targetTRBL.bottom);
}

if (/left/.test(direction)) {
setSnapped(event, 'x', targetTRBL.left);
} else
if (/right/.test(direction)) {
setSnapped(event, 'x', targetTRBL.right);
}
}

function setSnappedIfConstrained(event) {
var context = event.context,
createConstraints = context.createConstraints;

if (!createConstraints) {
return;
}

var top = createConstraints.top,
right = createConstraints.right,
bottom = createConstraints.bottom,
left = createConstraints.left;

if ((left && left >= event.x) || (right && right <= event.x)) {
setSnapped(event, 'x', event.x);
}

if ((top && top >= event.y) || (bottom && bottom <= event.y)) {
setSnapped(event, 'y', event.y);
}
}
Loading

0 comments on commit 84dbeb3

Please sign in to comment.