From 9b9ff934d2352ad082383c61adc069dc0ca0212d Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Wed, 19 Jun 2019 08:02:00 +0200 Subject: [PATCH] feat(snapping): snap to task mid on center interaction Closes #1086 --- lib/features/snapping/BpmnConnectSnapping.js | 48 ++++++++++++++--- .../snapping/BpmnConnectSnappingSpec.js | 53 +++++++++++++++++++ 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/lib/features/snapping/BpmnConnectSnapping.js b/lib/features/snapping/BpmnConnectSnapping.js index bb41deec..f1b1c99a 100644 --- a/lib/features/snapping/BpmnConnectSnapping.js +++ b/lib/features/snapping/BpmnConnectSnapping.js @@ -11,7 +11,10 @@ import { import { is } from '../../util/ModelUtil'; -import { some } from 'min-dash'; +import { + every, + some +} from 'min-dash'; import { isAny } from '../modeling/util/ModelingUtil'; @@ -21,6 +24,8 @@ var BOUNDARY_TO_HOST_THRESHOLD = 40; var TARGET_BOUNDS_PADDING = 20; +var TARGET_CENTER_PADDING = 20; + var AXES = [ 'x', 'y' ]; var abs = Math.abs; @@ -55,7 +60,7 @@ export default function BpmnConnectSnapping(eventBus, rules) { }); if (target && connectionAttrs) { - snapInsideTarget(event, target); + snapInsideTarget(event, target, getTargetBoundsPadding(target)); } if (target && isAnyType(connectionAttrs, [ @@ -72,6 +77,10 @@ export default function BpmnConnectSnapping(eventBus, rules) { snapToPosition(event, mid(target)); } + if (is(target, 'bpmn:Task')) { + snapTargetMidOnCenter(event, target); + } + if (is(source, 'bpmn:BoundaryEvent') && target === source.host) { snapBoundaryEventLoop(event, source, target); } @@ -103,16 +112,16 @@ BpmnConnectSnapping.$inject = [ 'rules' ]; -function snapInsideTarget(event, target) { +function snapInsideTarget(event, target, padding) { AXES.forEach(function(axis) { var matchingTargetDimension = getDimensionForAxis(axis, target), newCoordinate; - if (event[axis] < target[axis] + TARGET_BOUNDS_PADDING) { - newCoordinate = target[axis] + TARGET_BOUNDS_PADDING; - } else if (event[axis] > target[axis] + matchingTargetDimension - TARGET_BOUNDS_PADDING) { - newCoordinate = target[axis] + matchingTargetDimension - TARGET_BOUNDS_PADDING; + if (event[axis] < target[axis] + padding) { + newCoordinate = target[axis] + padding; + } else if (event[axis] > target[axis] + matchingTargetDimension - padding) { + newCoordinate = target[axis] + matchingTargetDimension - padding; } if (newCoordinate) { @@ -121,6 +130,23 @@ function snapInsideTarget(event, target) { }); } +// 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)); + } + +} + // snap outside of Boundary Event surroundings function snapBoundaryEventLoop(event, source, target) { var sourceMid = mid(source), @@ -171,3 +197,11 @@ function isAnyType(attrs, types) { function getDimensionForAxis(axis, element) { return axis === 'x' ? element.width : element.height; } + +function getTargetBoundsPadding(target) { + if (is(target, 'bpmn:Task')) { + return 10; + } else { + return TARGET_BOUNDS_PADDING; + } +} diff --git a/test/spec/features/snapping/BpmnConnectSnappingSpec.js b/test/spec/features/snapping/BpmnConnectSnappingSpec.js index 2e82825c..c9034166 100644 --- a/test/spec/features/snapping/BpmnConnectSnappingSpec.js +++ b/test/spec/features/snapping/BpmnConnectSnappingSpec.js @@ -142,6 +142,59 @@ describe('features/snapping - BpmnConnectSnapping', function() { }); + describe('Task target', function() { + + it('should snap to task mid', + inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_1'), + task = elementRegistry.get('Task_1'), + taskGfx = elementRegistry.getGraphics(task); + + // when + connect.start(canvasEvent({ x: 210, y: 60 }), startEvent); + + dragging.hover({ element: task, gfx: taskGfx }); + + dragging.move(canvasEvent({ x: 300, y: 300 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints[3].y).to.eql(300); + }) + ); + + + it('should snap to grid point', + inject(function(connect, dragging, elementRegistry) { + + // given + var startEvent = elementRegistry.get('StartEvent_1'), + task = elementRegistry.get('Task_1'), + taskGfx = elementRegistry.getGraphics(task); + + // when + connect.start(canvasEvent({ x: 210, y: 60 }), startEvent); + + dragging.hover({ element: task, gfx: taskGfx }); + + dragging.move(canvasEvent({ x: 300, y: 260 })); + + dragging.end(); + + // then + var waypoints = startEvent.outgoing[0].waypoints; + + expect(waypoints[3].y).to.eql(270); + }) + ); + }); + + it('should snap event if close to target bounds', inject(function(connect, dragging, elementRegistry) {