feat(grid-snapping): integrate grid snapping with auto place feature
Closes #1003
This commit is contained in:
parent
4388359fb1
commit
867b41c04f
|
@ -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';
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import AutoPlaceBehavior from './AutoPlaceBehavior';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: [
|
||||||
|
'gridSnappingAutoPlaceBehavior'
|
||||||
|
],
|
||||||
|
gridSnappingAutoPlaceBehavior: [ 'type', AutoPlaceBehavior ]
|
||||||
|
};
|
|
@ -1,8 +1,13 @@
|
||||||
import BpmnGridSnapping from './BpmnGridSnapping';
|
import BpmnGridSnapping from './BpmnGridSnapping';
|
||||||
import GridSnappingModule from 'diagram-js/lib/features/grid-snapping';
|
import GridSnappingModule from 'diagram-js/lib/features/grid-snapping';
|
||||||
|
|
||||||
|
import GridSnappingBehaviorModule from './behavior';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
__depends__: [ GridSnappingModule ],
|
__depends__: [
|
||||||
|
GridSnappingModule,
|
||||||
|
GridSnappingBehaviorModule
|
||||||
|
],
|
||||||
__init__: [ 'bpmnGridSnapping' ],
|
__init__: [ 'bpmnGridSnapping' ],
|
||||||
bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
|
bpmnGridSnapping: [ 'type', BpmnGridSnapping ]
|
||||||
};
|
};
|
|
@ -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>
|
|
@ -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
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue