bpmn-js/lib/features/modeling/behavior/CreateParticipantBehavior.js

230 lines
6.7 KiB
JavaScript

import inherits from 'inherits';
import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
import { getBusinessObject, is } from '../../../util/ModelUtil';
import { isLabel } from '../../../util/LabelUtil';
import { getBBox } from 'diagram-js/lib/util/Elements';
import {
assign,
find
} from 'min-dash';
import { asTRBL } from 'diagram-js/lib/layout/LayoutUtil';
var HORIZONTAL_PARTICIPANT_PADDING = 20,
VERTICAL_PARTICIPANT_PADDING = 20;
export var PARTICIPANT_BORDER_WIDTH = 30;
var HIGH_PRIORITY = 2000;
/**
* BPMN-specific behavior for creating participants.
*/
export default function CreateParticipantBehavior(canvas, eventBus, modeling) {
CommandInterceptor.call(this, eventBus);
// fit participant
eventBus.on([
'create.start',
'shape.move.start'
], HIGH_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
rootElement = canvas.getRootElement();
if (!is(shape, 'bpmn:Participant') ||
!is(rootElement, 'bpmn:Process') ||
!rootElement.children.length) {
return;
}
// ignore connections, groups and labels
var children = rootElement.children.filter(function(element) {
return !is(element, 'bpmn:Group') &&
!isLabel(element) &&
!isConnection(element);
});
// ensure for available children to calculate bounds
if (!children.length) {
return;
}
var childrenBBox = getBBox(children);
var participantBounds = getParticipantBounds(shape, childrenBBox);
// assign width and height
assign(shape, participantBounds);
// assign create constraints
context.createConstraints = getParticipantCreateConstraints(shape, childrenBBox);
});
// force hovering process when creating first participant
eventBus.on('create.start', HIGH_PRIORITY, function(event) {
var context = event.context,
shape = context.shape,
rootElement = canvas.getRootElement(),
rootElementGfx = canvas.getGraphics(rootElement);
function ensureHoveringProcess(event) {
event.element = rootElement;
event.gfx = rootElementGfx;
}
if (is(shape, 'bpmn:Participant') && is(rootElement, 'bpmn:Process')) {
eventBus.on('element.hover', HIGH_PRIORITY, ensureHoveringProcess);
eventBus.once('create.cleanup', function() {
eventBus.off('element.hover', ensureHoveringProcess);
});
}
});
// turn process into collaboration when creating first participant
function getOrCreateCollaboration() {
var rootElement = canvas.getRootElement();
if (is(rootElement, 'bpmn:Collaboration')) {
return rootElement;
}
return modeling.makeCollaboration();
}
// when creating mutliple elements through `elements.create` parent must be set to collaboration
// and passed to `shape.create` as hint
this.preExecute('elements.create', HIGH_PRIORITY, function(context) {
var elements = context.elements,
parent = context.parent,
participant = findParticipant(elements),
hints;
if (participant && is(parent, 'bpmn:Process')) {
context.parent = getOrCreateCollaboration();
hints = context.hints = context.hints || {};
hints.participant = participant;
hints.process = parent;
hints.processRef = getBusinessObject(participant).get('processRef');
}
}, true);
// when creating single shape through `shape.create` parent must be set to collaboration
// unless it was already set through `elements.create`
this.preExecute('shape.create', function(context) {
var parent = context.parent,
shape = context.shape;
if (is(shape, 'bpmn:Participant') && is(parent, 'bpmn:Process')) {
context.parent = getOrCreateCollaboration();
context.process = parent;
context.processRef = getBusinessObject(shape).get('processRef');
}
}, true);
// #execute necessary because #preExecute not called on CommandStack#redo
this.execute('shape.create', function(context) {
var hints = context.hints || {},
process = context.process || hints.process,
shape = context.shape,
participant = hints.participant;
// both shape.create and elements.create must be handled
if (process && (!participant || shape === participant)) {
// monkey-patch process ref
getBusinessObject(shape).set('processRef', getBusinessObject(process));
}
}, true);
this.revert('shape.create', function(context) {
var hints = context.hints || {},
process = context.process || hints.process,
processRef = context.processRef || hints.processRef,
shape = context.shape,
participant = hints.participant;
// both shape.create and elements.create must be handled
if (process && (!participant || shape === participant)) {
// monkey-patch process ref
getBusinessObject(shape).set('processRef', processRef);
}
}, true);
this.postExecute('shape.create', function(context) {
var hints = context.hints || {},
process = context.process || context.hints.process,
shape = context.shape,
participant = hints.participant;
if (process) {
var children = process.children.slice();
// both shape.create and elements.create must be handled
if (!participant) {
modeling.moveElements(children, { x: 0, y: 0 }, shape);
} else if (shape === participant) {
modeling.moveElements(children, { x: 0, y: 0 }, participant);
}
}
}, true);
}
CreateParticipantBehavior.$inject = [
'canvas',
'eventBus',
'modeling'
];
inherits(CreateParticipantBehavior, CommandInterceptor);
// helpers //////////
function getParticipantBounds(shape, childrenBBox) {
childrenBBox = {
width: childrenBBox.width + HORIZONTAL_PARTICIPANT_PADDING * 2 + PARTICIPANT_BORDER_WIDTH,
height: childrenBBox.height + VERTICAL_PARTICIPANT_PADDING * 2
};
var width = Math.max(shape.width, childrenBBox.width),
height = Math.max(shape.height, childrenBBox.height);
return {
x: -width / 2,
y: -height / 2,
width: width,
height: height
};
}
function getParticipantCreateConstraints(shape, childrenBBox) {
childrenBBox = asTRBL(childrenBBox);
return {
bottom: childrenBBox.top + shape.height / 2 - VERTICAL_PARTICIPANT_PADDING,
left: childrenBBox.right - shape.width / 2 + HORIZONTAL_PARTICIPANT_PADDING,
top: childrenBBox.bottom - shape.height / 2 + VERTICAL_PARTICIPANT_PADDING,
right: childrenBBox.left + shape.width / 2 - HORIZONTAL_PARTICIPANT_PADDING - PARTICIPANT_BORDER_WIDTH
};
}
function isConnection(element) {
return !!element.waypoints;
}
function findParticipant(elements) {
return find(elements, function(element) {
return is(element, 'bpmn:Participant');
});
}