import inherits from 'inherits'; import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor'; import { 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); }); } }); function ensureCollaboration(context) { var parent = context.parent, collaboration; var rootElement = canvas.getRootElement(); if (is(rootElement, 'bpmn:Collaboration')) { collaboration = rootElement; } else { // update root element by making collaboration collaboration = modeling.makeCollaboration(); // re-use process when creating first participant context.process = parent; } context.parent = collaboration; } // turn process into collaboration before adding participant this.preExecute('shape.create', function(context) { var parent = context.parent, shape = context.shape; if (is(shape, 'bpmn:Participant') && is(parent, 'bpmn:Process')) { ensureCollaboration(context); } }, true); this.execute('shape.create', function(context) { var process = context.process, shape = context.shape; if (process) { context.oldProcessRef = shape.businessObject.processRef; // re-use process when creating first participant shape.businessObject.processRef = process.businessObject; } }, true); this.revert('shape.create', function(context) { var process = context.process, shape = context.shape; if (process) { // re-use process when creating first participant shape.businessObject.processRef = context.oldProcessRef; } }, true); this.postExecute('shape.create', function(context) { var process = context.process, shape = context.shape; if (process) { // move children from process to participant var processChildren = process.children.slice(); modeling.moveElements(processChildren, { x: 0, y: 0 }, shape); } }, true); // turn process into collaboration when creating participants this.preExecute('elements.create', HIGH_PRIORITY, function(context) { var elements = context.elements, parent = context.parent, participant; var hasParticipants = findParticipant(elements); if (hasParticipants && is(parent, 'bpmn:Process')) { ensureCollaboration(context); participant = findParticipant(elements); context.oldProcessRef = participant.businessObject.processRef; // re-use process when creating first participant participant.businessObject.processRef = parent.businessObject; } }, true); this.revert('elements.create', function(context) { var elements = context.elements, process = context.process, participant; if (process) { participant = findParticipant(elements); // re-use process when creating first participant participant.businessObject.processRef = context.oldProcessRef; } }, true); this.postExecute('elements.create', function(context) { var elements = context.elements, process = context.process, participant; if (process) { participant = findParticipant(elements); // move children from process to first participant var processChildren = process.children.slice(); modeling.moveElements(processChildren, { 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'); }); }