UNPKG

bpmn-js

Version:

A bpmn 2.0 toolkit and web modeler

261 lines (201 loc) 6.05 kB
import { reduce, keys, forEach, assign } from 'min-dash'; import { getBusinessObject, getDi } from '../../../util/ModelUtil'; /** * @typedef {import('diagram-js/lib/command/CommandHandler').default} CommandHandler * @typedef {import('diagram-js/lib/command/CommandStack').CommandContext} CommandContext * * @typedef {import('diagram-js/lib/core/ElementRegistry').default} ElementRegistry * @typedef {import('../../../model/Types').Moddle} Moddle * @typedef {import('../Modeling').default} Modeling * @typedef {import('../../../draw/TextRenderer').default} TextRenderer * * @typedef {import('../../../model/Types').Element} Element */ var DEFAULT_FLOW = 'default', ID = 'id', DI = 'di'; var NULL_DIMENSIONS = { width: 0, height: 0 }; /** * A handler that implements a BPMN 2.0 property update. * * This should be used to set simple properties on elements with * an underlying BPMN business object. * * Use respective diagram-js provided handlers if you would * like to perform automated modeling. * * @implements {CommandHandler} * * @param {ElementRegistry} elementRegistry * @param {Moddle} moddle * @param {Modeling} modeling * @param {TextRenderer} textRenderer */ export default function UpdatePropertiesHandler( elementRegistry, moddle, modeling, textRenderer) { this._elementRegistry = elementRegistry; this._moddle = moddle; this._modeling = modeling; this._textRenderer = textRenderer; } UpdatePropertiesHandler.$inject = [ 'elementRegistry', 'moddle', 'modeling', 'textRenderer' ]; // api ////////////////////// /** * Update a BPMN element's properties. * * @param { { * element: Element; * properties: Record<string, any>; * } & CommandContext } context * * @return {Element[]} */ UpdatePropertiesHandler.prototype.execute = function(context) { var element = context.element, changed = [ element ]; if (!element) { throw new Error('element required'); } var elementRegistry = this._elementRegistry, ids = this._moddle.ids; var businessObject = element.businessObject, properties = unwrapBusinessObjects(context.properties), oldProperties = context.oldProperties || getProperties(element, properties); if (isIdChange(properties, businessObject)) { ids.unclaim(businessObject[ID]); elementRegistry.updateId(element, properties[ID]); ids.claim(properties[ID], businessObject); } // correctly indicate visual changes on default flow updates if (DEFAULT_FLOW in properties) { if (properties[DEFAULT_FLOW]) { changed.push(elementRegistry.get(properties[DEFAULT_FLOW].id)); } if (businessObject[DEFAULT_FLOW]) { changed.push(elementRegistry.get(businessObject[DEFAULT_FLOW].id)); } } // update properties setProperties(element, properties); // store old values context.oldProperties = oldProperties; context.changed = changed; // indicate changed on objects affected by the update return changed; }; UpdatePropertiesHandler.prototype.postExecute = function(context) { var element = context.element, label = element.label; var text = label && getBusinessObject(label).name; if (!text) { return; } // get layouted text bounds and resize external // external label accordingly var newLabelBounds = this._textRenderer.getExternalLabelBounds(label, text); this._modeling.resizeShape(label, newLabelBounds, NULL_DIMENSIONS); }; /** * Revert updating a BPMN element's properties. * * @param { { * element: Element; * properties: Record<string, any>; * oldProperties: Record<string, any>; * } & CommandContext } context * * @return {Element[]} */ UpdatePropertiesHandler.prototype.revert = function(context) { var element = context.element, properties = context.properties, oldProperties = context.oldProperties, businessObject = element.businessObject, elementRegistry = this._elementRegistry, ids = this._moddle.ids; // update properties setProperties(element, oldProperties); if (isIdChange(properties, businessObject)) { ids.unclaim(properties[ID]); elementRegistry.updateId(element, oldProperties[ID]); ids.claim(oldProperties[ID], businessObject); } return context.changed; }; function isIdChange(properties, businessObject) { return ID in properties && properties[ID] !== businessObject[ID]; } function getProperties(element, properties) { var propertyNames = keys(properties), businessObject = element.businessObject, di = getDi(element); return reduce(propertyNames, function(result, key) { // handle DI separately if (key !== DI) { result[key] = businessObject.get(key); } else { result[key] = getDiProperties(di, keys(properties.di)); } return result; }, {}); } function getDiProperties(di, propertyNames) { return reduce(propertyNames, function(result, key) { result[key] = di && di.get(key); return result; }, {}); } function setProperties(element, properties) { var businessObject = element.businessObject, di = getDi(element); forEach(properties, function(value, key) { if (key !== DI) { businessObject.set(key, value); } else { // only update, if di exists if (di) { setDiProperties(di, value); } } }); } function setDiProperties(di, properties) { forEach(properties, function(value, key) { di.set(key, value); }); } var referencePropertyNames = [ 'default' ]; /** * Make sure we unwrap the actual business object behind diagram element that * may have been passed as arguments. * * @param {Record<string, any>} properties * * @return {Record<string, any>} unwrappedProps */ function unwrapBusinessObjects(properties) { var unwrappedProps = assign({}, properties); referencePropertyNames.forEach(function(name) { if (name in properties) { unwrappedProps[name] = getBusinessObject(unwrappedProps[name]); } }); return unwrappedProps; }