Skip to content

Commit

Permalink
feat(grid-snapping): integrate grid snapping with auto place feature
Browse files Browse the repository at this point in the history
Closes #1003
  • Loading branch information
philippfromme authored and merge-me[bot] committed Apr 30, 2019
1 parent 4388359 commit 867b41c
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 1 deletion.
57 changes: 57 additions & 0 deletions lib/features/grid-snapping/behavior/AutoPlaceBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getNewShapePosition } from '../../auto-place/AutoPlaceUtil';

import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
import { is } from '../../../util/ModelUtil';


export default function AutoPlaceBehavior(eventBus, gridSnapping) {
eventBus.on('autoPlace', function(context) {
var source = context.source,
sourceMid = getMid(source),
shape = context.shape;

var position = getNewShapePosition(source, shape);

[ 'x', 'y' ].forEach(function(axis) {
var options = {};

// do not snap if x/y equal
if (position[ axis ] === sourceMid[ axis ]) {
return;
}

if (position[ axis ] > sourceMid[ axis ]) {
options.min = position[ axis ];
} else {
options.max = position[ axis ];
}

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

if (isHorizontal(axis)) {
options.offset = -shape.width / 2;
} else {
options.offset = -shape.height / 2;
}

}

position[ axis ] = gridSnapping.snapValue(position[ axis ], options);

});

// must be returned to be considered by auto place
return position;
});
}

AutoPlaceBehavior.$inject = [
'eventBus',
'gridSnapping'
];

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

function isHorizontal(axis) {
return axis === 'x';
}
8 changes: 8 additions & 0 deletions lib/features/grid-snapping/behavior/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import AutoPlaceBehavior from './AutoPlaceBehavior';

export default {
__init__: [
'gridSnappingAutoPlaceBehavior'
],
gridSnappingAutoPlaceBehavior: [ 'type', AutoPlaceBehavior ]
};
7 changes: 6 additions & 1 deletion lib/features/grid-snapping/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import BpmnGridSnapping from './BpmnGridSnapping';
import GridSnappingModule from 'diagram-js/lib/features/grid-snapping';

import GridSnappingBehaviorModule from './behavior';

export default {
__depends__: [ GridSnappingModule ],
__depends__: [
GridSnappingModule,
GridSnappingBehaviorModule
],
__init__: [ 'bpmnGridSnapping' ],
bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
};
17 changes: 17 additions & 0 deletions test/spec/features/grid-snapping/behavior/AutoPlaceBehavior.bpmn
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0">
<bpmn:process id="Process_1" isExecutable="true">
<bpmn:startEvent id="StartEvent_1" />
<bpmn:task id="Task_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="BPMNShape_StartEvent_1" bpmnElement="StartEvent_1">
<dc:Bounds x="82" y="87" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
<dc:Bounds x="50" y="260" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>
198 changes: 198 additions & 0 deletions test/spec/features/grid-snapping/behavior/AutoPlaceBehaviorSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import {
bootstrapModeler,
inject
} from 'test/TestHelper';

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

import autoPlaceModule from 'lib/features/auto-place';
import coreModule from 'lib/core';
import gridSnappingModule from 'lib/features/grid-snapping';
import modelingModule from 'lib/features/modeling';


describe('features/grid-snapping - auto-place', function() {

var diagramXML = require('./AutoPlaceBehavior.bpmn');

beforeEach(bootstrapModeler(diagramXML, {
modules: [
autoPlaceModule,
coreModule,
gridSnappingModule,
modelingModule
]
}));


describe('flow node', function() {

it('without existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'Task_2',
type: 'bpmn:Task'
});

var source = elementRegistry.get('StartEvent_1');

// when
autoPlace.append(source, shape1);

// then
shape1 = elementRegistry.get('Task_2');

expect(getMid(shape1)).to.eql({
x: 220, // 218 snapped to 220
y: 105 // not snapped
});
}));


it('with existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'Task_2',
type: 'bpmn:Task'
});

var source = elementRegistry.get('StartEvent_1');

autoPlace.append(source, shape1);

var shape2 = elementFactory.createShape({
id: 'Task_3',
type: 'bpmn:Task'
});

// when
autoPlace.append(source, shape2);

// then
shape2 = elementRegistry.get('Task_3');

expect(getMid(shape2)).to.eql({
x: 220, // 220 snapped to 220
y: 220 // 215 snapped to 220
});
}));

});


describe('text annotation', function() {

it('without existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'TextAnnotation_1',
type: 'bpmn:TextAnnotation'
});

var source = elementRegistry.get('StartEvent_1');

// when
autoPlace.append(source, shape1);

// then
shape1 = elementRegistry.get('TextAnnotation_1');

expect(getMid(shape1)).to.eql({
x: 170, // 168 snapped to 170
y: 15 // 22 snapped to 15
});
}));


it('with existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'TextAnnotation_1',
type: 'bpmn:TextAnnotation'
});

var source = elementRegistry.get('StartEvent_1');

autoPlace.append(source, shape1);

var shape2 = elementFactory.createShape({
id: 'TextAnnotation_2',
type: 'bpmn:TextAnnotation'
});

// when
autoPlace.append(source, shape2);

// then
shape2 = elementRegistry.get('TextAnnotation_2');

expect(getMid(shape2)).to.eql({
x: 170, // 168 snapped to 170
y: -45 // -45 snapped to -45
});
}));

});


describe('data object/store reference', function() {

it('without existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'DataObjectReference_1',
type: 'bpmn:DataObjectReference'
});

var source = elementRegistry.get('Task_1');

// when
autoPlace.append(source, shape1);

// then
shape1 = elementRegistry.get('DataObjectReference_1');

expect(getMid(shape1)).to.eql({
x: 160, // 158 snapped to 160
y: 400 // 398 snapped to 400
});
}));


it('with existing elements', inject(function(autoPlace, elementFactory, elementRegistry) {

// given
var shape1 = elementFactory.createShape({
id: 'DataObjectReference_1',
type: 'bpmn:DataObjectReference'
});

var source = elementRegistry.get('Task_1');

autoPlace.append(source, shape1);

var shape2 = elementFactory.createShape({
id: 'DataObjectReference_2',
type: 'bpmn:DataObjectReference'
});

// when
autoPlace.append(source, shape2);

// then
shape2 = elementRegistry.get('DataObjectReference_2');

expect(getMid(shape2)).to.eql({
x: 230, // 226 snapped to 230
y: 400 // 398 snapped to 400
});
}));

});

});

0 comments on commit 867b41c

Please sign in to comment.