From 3b4251967d64c7cb2037dc8dde4c0fcf78845dbc Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Wed, 26 Jun 2019 15:35:18 +0200 Subject: [PATCH] feat(snapping): add TRBL snapping against containers Adds TRBL snapping with containers only. Closes #1104 --- .../snapping/BpmnCreateMoveSnapping.js | 33 +++++ .../BpmnCreateMoveSnapping.trbl-snapping.bpmn | 45 ++++++ .../snapping/BpmnCreateMoveSnappingSpec.js | 133 ++++++++++++++++-- 3 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 test/spec/features/snapping/BpmnCreateMoveSnapping.trbl-snapping.bpmn diff --git a/lib/features/snapping/BpmnCreateMoveSnapping.js b/lib/features/snapping/BpmnCreateMoveSnapping.js index bbe2002cee..392048aeae 100644 --- a/lib/features/snapping/BpmnCreateMoveSnapping.js +++ b/lib/features/snapping/BpmnCreateMoveSnapping.js @@ -5,8 +5,14 @@ import CreateMoveSnapping from 'diagram-js/lib/features/snapping/CreateMoveSnapp import { isSnapped, setSnapped, + topLeft, + bottomRight } from 'diagram-js/lib/features/snapping/SnapUtil'; +import { + isExpanded +} from '../../util/DiUtil'; + import { is } from '../../util/ModelUtil'; import { @@ -105,6 +111,18 @@ BpmnCreateMoveSnapping.prototype.addSnapTargetPoints = function(snapPoints, shap var snapTargets = this.getSnapTargets(shape, target); + forEach(snapTargets, function(snapTarget) { + + // handle TRBL alignment + // + // * with container elements + // * with text annotations + if (isContainer(snapTarget) || areAll([ shape, snapTarget ], 'bpmn:TextAnnotation')) { + snapPoints.add('top-left', topLeft(snapTarget)); + snapPoints.add('bottom-right', bottomRight(snapTarget)); + } + }); + // snap to docking points forEach(shape.incoming, function(connection) { @@ -168,6 +186,21 @@ function snapBoundaryEvent(event, target) { } } +function areAll(elements, type) { + return elements.every(function(el) { + return is(el, type); + }); +} + +function isContainer(element) { + if (is(element, 'bpmn:SubProcess') && isExpanded(element)) { + return true; + } + + return is(element, 'bpmn:Participant'); +} + + function setSnappedIfConstrained(event) { var context = event.context, createConstraints = context.createConstraints; diff --git a/test/spec/features/snapping/BpmnCreateMoveSnapping.trbl-snapping.bpmn b/test/spec/features/snapping/BpmnCreateMoveSnapping.trbl-snapping.bpmn new file mode 100644 index 0000000000..438c5c74b4 --- /dev/null +++ b/test/spec/features/snapping/BpmnCreateMoveSnapping.trbl-snapping.bpmn @@ -0,0 +1,45 @@ + + + + + + + TEXT_1 + + + TEXT_2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js b/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js index df82795a44..fae59f1733 100644 --- a/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js +++ b/test/spec/features/snapping/BpmnCreateMoveSnappingSpec.js @@ -1,5 +1,6 @@ import { bootstrapModeler, + getBpmnJS, inject } from 'test/TestHelper'; @@ -32,7 +33,12 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { modelingModule, moveModule, rulesModule, - snappingModule + snappingModule, + { + __init__: [ function(dragging) { + dragging.setOptions({ manual: true }); + } ] + } ]; @@ -160,8 +166,6 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { create.start(canvasEvent({ x: 0, y: 0 }), intermediateThrowEvent); dragging.hover({ element: task, gfx: taskGfx }); - - dragging.setOptions({ manual: false }); })); @@ -251,8 +255,6 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { var process = elementRegistry.get('Process_1'), processGfx = elementRegistry.getGraphics(process); - dragging.setOptions({ manual: true }); - move.start(canvasEventTopLeft({ x: 100, y: 400 }, task), task, true); dragging.hover({ element: process, gfx: processGfx }); @@ -303,8 +305,6 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { create.start(canvasEvent({ x: 0, y: 0 }), task); dragging.hover({ element: sequenceFlow, gfx: sequenceFlowGfx }); - - dragging.setOptions({ manual: false }); })); @@ -340,8 +340,6 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { beforeEach(inject(function(dragging, elementRegistry, move) { task = elementRegistry.get('Task_1'); - dragging.setOptions({ manual: false }); - move.start(canvasEvent({ x: 200, y: 165 }), task); })); @@ -380,8 +378,6 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { beforeEach(inject(function(dragging, elementRegistry, move) { participant = elementRegistry.get('Participant_2'); participantGfx = elementRegistry.getGraphics(participant); - - dragging.setOptions({ manual: true }); })); @@ -438,6 +434,121 @@ describe('features/snapping - BpmnCreateMoveSnapping', function() { }); + + describe('TRBL snapping', function() { + + var diagramXML = require('./BpmnCreateMoveSnapping.trbl-snapping.bpmn'); + + beforeEach(bootstrapModeler(diagramXML, { + modules: testModules + })); + + + function get(element) { + return getBpmnJS().invoke(function(elementRegistry) { + return elementRegistry.get(element); + }); + } + + function absoluteMove(element, toPosition) { + + getBpmnJS().invoke(function(elementRegistry, move, dragging, canvas) { + + var parent = element.parent; + + move.start(canvasEvent({ x: 0, y: 0 }), element); + + dragging.hover({ + element: parent, + gfx: canvas.getGraphics(parent) + }); + + dragging.move(canvasEvent({ x: 100, y: 100 }), element); + + dragging.move(canvasEvent({ + x: toPosition.x - element.x, + y: toPosition.y - element.y + })); + + dragging.end(); + }); + + } + + + it('should snap text annotations', function() { + + // given + var annotation = get('TEXT_1'); + var otherAnnotation = get('TEXT_2'); + + // when + absoluteMove(annotation, { + x: otherAnnotation.x + 5, + y: otherAnnotation.y - 5 + }); + + // then + expect(annotation).to.have.position(otherAnnotation); + }); + + + it('should snap task to container', function() { + + // given + var task = get('TASK'); + var subProcess = get('SUB_PROCESS_1'); + + // when + absoluteMove(task, { + x: subProcess.x, + y: subProcess.y - 5 + }); + + // then + expect(task).to.have.position(subProcess); + }); + + + it('should snap container to container', function() { + + // given + var participant = get('PARTICIPANT_1'); + var otherParticipant = get('PARTICIPANT_2'); + + // when + absoluteMove(participant, { + x: otherParticipant.x + 5, + y: otherParticipant.y + }); + + // then + expect(participant).to.have.position(otherParticipant); + }); + + + it('should snap container to container right', function() { + + // given + var participant = get('PARTICIPANT_1'); + var otherParticipant = get('PARTICIPANT_2'); + + // when + absoluteMove(participant, { + x: otherParticipant.x + otherParticipant.width - participant.width + 5, + y: 5 + }); + + // then + expect(participant).to.have.position({ + x: otherParticipant.x + otherParticipant.width - participant.width, + y: 5 + }); + + }); + + }); + }); // helpers //////////