feat(modeling): snap initial participant to diagram contents
Closes #241
This commit is contained in:
parent
0381811d03
commit
de648520d5
|
@ -70,6 +70,7 @@ Modeler.prototype._modelingModules = [
|
|||
require('diagram-js/lib/features/resize'),
|
||||
require('diagram-js/lib/features/space-tool'),
|
||||
require('diagram-js/lib/features/lasso-tool'),
|
||||
require('./features/snapping'),
|
||||
require('./features/modeling'),
|
||||
require('./features/context-pad'),
|
||||
require('./features/palette')
|
||||
|
|
|
@ -4,8 +4,6 @@ var inherits = require('inherits');
|
|||
|
||||
var CommandInterceptor = require('diagram-js/lib/command/CommandInterceptor');
|
||||
|
||||
var getBoundingBox = require('diagram-js/lib/util/Elements').getBBox,
|
||||
assign = require('lodash/object/assign');
|
||||
|
||||
/**
|
||||
* BPMN specific create behavior
|
||||
|
@ -29,22 +27,6 @@ function CreateBehavior(eventBus, modeling) {
|
|||
if (parent.businessObject.$instanceOf('bpmn:Process') &&
|
||||
shape.businessObject.$instanceOf('bpmn:Participant')) {
|
||||
|
||||
// update shape size + position of new participant to
|
||||
// encapsulate all existing children
|
||||
if (parent.children.length) {
|
||||
var bbox = getBoundingBox(parent.children);
|
||||
|
||||
assign(shape, {
|
||||
width: bbox.width + 60,
|
||||
height: bbox.height + 40
|
||||
});
|
||||
|
||||
position = {
|
||||
x: bbox.x - 40 + shape.width / 2,
|
||||
y: bbox.y - 20 + shape.height / 2
|
||||
};
|
||||
}
|
||||
|
||||
// this is going to detach the process root
|
||||
// and set the returned collaboration element
|
||||
// as the new root element
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
'use strict';
|
||||
|
||||
var getBoundingBox = require('diagram-js/lib/util/Elements').getBBox;
|
||||
|
||||
/**
|
||||
* BPMN specific snapping functionality
|
||||
*
|
||||
* * snap on process elements if a pool is created inside a
|
||||
* process diagram
|
||||
*
|
||||
* @param {EventBus} eventBus
|
||||
* @param {Canvas} canvas
|
||||
*/
|
||||
function BpmnSnapping(eventBus, canvas) {
|
||||
|
||||
/**
|
||||
* Drop articipant on process <> process elements snapping
|
||||
*/
|
||||
|
||||
function initParticipantSnapping(context, shape, elements) {
|
||||
|
||||
if (!elements.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var snapBox = getBoundingBox(elements.filter(function(e) {
|
||||
return !e.labelTarget && !e.waypoints;
|
||||
}));
|
||||
|
||||
snapBox.x -= 50;
|
||||
snapBox.y -= 20;
|
||||
snapBox.width += 70;
|
||||
snapBox.height += 40;
|
||||
|
||||
// adjust shape height to include bounding box
|
||||
shape.width = Math.max(shape.width, snapBox.width);
|
||||
shape.height = Math.max(shape.height, snapBox.height);
|
||||
|
||||
context.participantSnapBox = snapBox;
|
||||
}
|
||||
|
||||
function snapParticipant(snapBox, shape, event) {
|
||||
|
||||
var shapeHalfWidth = shape.width / 2 - 30,
|
||||
shapeHalfHeight = shape.height / 2;
|
||||
|
||||
var currentTopLeft = {
|
||||
x: event.x - shapeHalfWidth - 30,
|
||||
y: event.y - shapeHalfHeight
|
||||
};
|
||||
|
||||
var currentBottomRight = {
|
||||
x: event.x + shapeHalfWidth + 30,
|
||||
y: event.y + shapeHalfHeight
|
||||
};
|
||||
|
||||
var snapTopLeft = snapBox,
|
||||
snapBottomRight = {
|
||||
x: snapBox.x + snapBox.width,
|
||||
y: snapBox.y + snapBox.height
|
||||
};
|
||||
|
||||
if (currentTopLeft.x >= snapTopLeft.x) {
|
||||
event.x = snapTopLeft.x + 30 + shapeHalfWidth;
|
||||
event.snapping = true;
|
||||
} else
|
||||
if (currentBottomRight.x <= snapBottomRight.x) {
|
||||
event.x = snapBottomRight.x - 30 - shapeHalfWidth;
|
||||
event.snapping = true;
|
||||
}
|
||||
|
||||
if (currentTopLeft.y >= snapTopLeft.y) {
|
||||
event.y = snapTopLeft.y + shapeHalfHeight;
|
||||
event.snapping = true;
|
||||
} else
|
||||
if (currentBottomRight.y <= snapBottomRight.y) {
|
||||
event.y = snapBottomRight.y - shapeHalfHeight;
|
||||
event.snapping = true;
|
||||
}
|
||||
}
|
||||
|
||||
eventBus.on('create.start', function(event) {
|
||||
|
||||
var context = event.context,
|
||||
shape = context.shape,
|
||||
rootElement = canvas.getRootElement();
|
||||
|
||||
// snap participant around existing elements (if any)
|
||||
if (shape.businessObject.$instanceOf('bpmn:Participant') &&
|
||||
rootElement.businessObject.$instanceOf('bpmn:Process')) {
|
||||
|
||||
initParticipantSnapping(context, shape, rootElement.children);
|
||||
}
|
||||
});
|
||||
|
||||
eventBus.on([ 'create.move', 'create.end' ], 1500, function(event) {
|
||||
|
||||
var context = event.context,
|
||||
shape = context.shape,
|
||||
participantSnapBox = context.participantSnapBox;
|
||||
|
||||
if (!event.snapping && participantSnapBox) {
|
||||
snapParticipant(participantSnapBox, shape, event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
BpmnSnapping.$inject = [ 'eventBus', 'canvas' ];
|
||||
|
||||
module.exports = BpmnSnapping;
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
__init__: [ 'bpmnSnapping' ],
|
||||
bpmnSnapping: [ 'type', require('./BpmnSnapping') ]
|
||||
};
|
|
@ -0,0 +1,168 @@
|
|||
'use strict';
|
||||
|
||||
var TestHelper = require('../../../TestHelper');
|
||||
|
||||
/* global bootstrapModeler, inject */
|
||||
|
||||
var Events = require('diagram-js/test/util/Events');
|
||||
|
||||
var coreModule = require('../../../../lib/core'),
|
||||
snappingModule = require('../../../../lib/features/snapping'),
|
||||
modelingModule = require('../../../../lib/features/modeling'),
|
||||
createModule = require('diagram-js/lib/features/create'),
|
||||
rulesModule = require('../../../../lib/features/modeling/rules');
|
||||
|
||||
var pick = require('lodash/object/pick');
|
||||
|
||||
|
||||
function bounds(element) {
|
||||
return pick(element, [ 'width', 'height', 'x', 'y' ]);
|
||||
}
|
||||
|
||||
|
||||
describe('features/snapping - BpmnSnapping', function() {
|
||||
|
||||
var testModules = [ coreModule, snappingModule, modelingModule, createModule, rulesModule ];
|
||||
|
||||
|
||||
describe('when dropping on non-empty process', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/collaboration/process.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
var createEvent;
|
||||
|
||||
beforeEach(inject(function(canvas, dragging) {
|
||||
createEvent = Events.scopedCreate(canvas);
|
||||
dragging.setOptions({ manual: true });
|
||||
}));
|
||||
|
||||
|
||||
it('should snap to process children bounds / top left',
|
||||
inject(function(canvas, create, dragging, elementFactory) {
|
||||
|
||||
// given
|
||||
var participantShape = elementFactory.createParticipantShape(false),
|
||||
rootElement = canvas.getRootElement(),
|
||||
rootGfx = canvas.getGraphics(rootElement);
|
||||
|
||||
// when
|
||||
create.start(createEvent({ x: 50, y: 50 }), participantShape);
|
||||
|
||||
dragging.hover({ element: rootElement, gfx: rootGfx });
|
||||
|
||||
dragging.move(createEvent({ x: 65, y: 65 }));
|
||||
dragging.end(createEvent({ x: 65, y: 65 }));
|
||||
|
||||
// then
|
||||
expect(bounds(participantShape)).toEqual({
|
||||
width: 600, height: 300, x: 18, y: -58
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should snap to process children bounds / bottom right',
|
||||
inject(function(canvas, create, dragging, elementFactory) {
|
||||
|
||||
// given
|
||||
var participantShape = elementFactory.createParticipantShape(false),
|
||||
rootElement = canvas.getRootElement(),
|
||||
rootGfx = canvas.getGraphics(rootElement);
|
||||
|
||||
// when
|
||||
create.start(createEvent({ x: 50, y: 50 }), participantShape);
|
||||
|
||||
dragging.hover({ element: rootElement, gfx: rootGfx });
|
||||
|
||||
dragging.move(createEvent({ x: 400, y: 400 }));
|
||||
dragging.end(createEvent({ x: 400, y: 400 }));
|
||||
|
||||
// then
|
||||
expect(bounds(participantShape)).toEqual({
|
||||
width: 600, height: 300, x: 100, y: 52
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('when dropping on empty process', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/collaboration/process-empty.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
var createEvent;
|
||||
|
||||
beforeEach(inject(function(canvas, dragging) {
|
||||
createEvent = Events.scopedCreate(canvas);
|
||||
dragging.setOptions({ manual: true });
|
||||
}));
|
||||
|
||||
|
||||
it('should not snap', inject(function(canvas, create, dragging, elementFactory) {
|
||||
|
||||
// given
|
||||
var participantShape = elementFactory.createParticipantShape(false),
|
||||
rootElement = canvas.getRootElement(),
|
||||
rootGfx = canvas.getGraphics(rootElement);
|
||||
|
||||
// when
|
||||
create.start(createEvent({ x: 50, y: 50 }), participantShape);
|
||||
|
||||
dragging.hover({ element: rootElement, gfx: rootGfx });
|
||||
|
||||
dragging.move(createEvent({ x: 400, y: 400 }));
|
||||
dragging.end(createEvent({ x: 400, y: 400 }));
|
||||
|
||||
// then
|
||||
expect(bounds(participantShape)).toEqual({
|
||||
width: 600, height: 300, x: 100, y: 250
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('when dropping on collaboration', function() {
|
||||
|
||||
var diagramXML = require('../../../fixtures/bpmn/collaboration/collaboration-participant.bpmn');
|
||||
|
||||
beforeEach(bootstrapModeler(diagramXML, { modules: testModules }));
|
||||
|
||||
|
||||
var createEvent;
|
||||
|
||||
beforeEach(inject(function(canvas, dragging) {
|
||||
createEvent = Events.scopedCreate(canvas);
|
||||
dragging.setOptions({ manual: true });
|
||||
}));
|
||||
|
||||
|
||||
it('should not snap', inject(function(canvas, create, dragging, elementFactory) {
|
||||
|
||||
// given
|
||||
var participantShape = elementFactory.createParticipantShape(false),
|
||||
rootElement = canvas.getRootElement(),
|
||||
rootGfx = canvas.getGraphics(rootElement);
|
||||
|
||||
// when
|
||||
create.start(createEvent({ x: 50, y: 50 }), participantShape);
|
||||
|
||||
dragging.hover({ element: rootElement, gfx: rootGfx });
|
||||
|
||||
dragging.move(createEvent({ x: 400, y: 400 }));
|
||||
dragging.end(createEvent({ x: 400, y: 400 }));
|
||||
|
||||
// then
|
||||
expect(bounds(participantShape)).toEqual({
|
||||
width: 600, height: 300, x: 100, y: 250
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue