123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- import inherits from 'inherits';
- import { getBBox as getBoundingBox } from '../../util/Elements';
- import {
- asTRBL,
- asBounds
- } from '../../layout/LayoutUtil';
- import {
- assign,
- flatten,
- forEach,
- isArray,
- values,
- groupBy
- } from 'min-dash';
- import CommandInterceptor from '../../command/CommandInterceptor';
- /**
- * An auto resize component that takes care of expanding a parent element
- * if child elements are created or moved close the parents edge.
- *
- * @param {EventBus} eventBus
- * @param {ElementRegistry} elementRegistry
- * @param {Modeling} modeling
- * @param {Rules} rules
- */
- export default function AutoResize(eventBus, elementRegistry, modeling, rules) {
- CommandInterceptor.call(this, eventBus);
- this._elementRegistry = elementRegistry;
- this._modeling = modeling;
- this._rules = rules;
- var self = this;
- this.postExecuted([ 'shape.create' ], function(event) {
- var context = event.context,
- hints = context.hints,
- shape = context.shape,
- parent = context.parent || context.newParent;
- if (hints && (hints.root === false || hints.autoResize === false)) {
- return;
- }
- self._expand([ shape ], parent);
- });
- this.postExecuted([ 'elements.move' ], function(event) {
- var context = event.context,
- elements = flatten(values(context.closure.topLevel)),
- hints = context.hints;
- var autoResize = hints ? hints.autoResize : true;
- if (autoResize === false) {
- return;
- }
- var expandings = groupBy(elements, function(element) {
- return element.parent.id;
- });
- forEach(expandings, function(elements, parentId) {
- // optionally filter elements to be considered when resizing
- if (isArray(autoResize)) {
- elements = elements.filter(function(element) {
- return autoResize.indexOf(element) !== -1;
- });
- }
- self._expand(elements, parentId);
- });
- });
- this.postExecuted([ 'shape.toggleCollapse' ], function(event) {
- var context = event.context,
- hints = context.hints,
- shape = context.shape;
- if (hints && (hints.root === false || hints.autoResize === false)) {
- return;
- }
- if (shape.collapsed) {
- return;
- }
- self._expand(shape.children || [], shape);
- });
- this.postExecuted([ 'shape.resize' ], function(event) {
- var context = event.context,
- hints = context.hints,
- shape = context.shape,
- parent = shape.parent;
- if (hints && (hints.root === false || hints.autoResize === false)) {
- return;
- }
- if (parent) {
- self._expand([ shape ], parent);
- }
- });
- }
- AutoResize.$inject = [
- 'eventBus',
- 'elementRegistry',
- 'modeling',
- 'rules'
- ];
- inherits(AutoResize, CommandInterceptor);
- /**
- * Calculate the new bounds of the target shape, given
- * a number of elements have been moved or added into the parent.
- *
- * This method considers the current size, the added elements as well as
- * the provided padding for the new bounds.
- *
- * @param {Array<djs.model.Shape>} elements
- * @param {djs.model.Shape} target
- */
- AutoResize.prototype._getOptimalBounds = function(elements, target) {
- var offset = this.getOffset(target),
- padding = this.getPadding(target);
- var elementsTrbl = asTRBL(getBoundingBox(elements)),
- targetTrbl = asTRBL(target);
- var newTrbl = {};
- if (elementsTrbl.top - targetTrbl.top < padding.top) {
- newTrbl.top = elementsTrbl.top - offset.top;
- }
- if (elementsTrbl.left - targetTrbl.left < padding.left) {
- newTrbl.left = elementsTrbl.left - offset.left;
- }
- if (targetTrbl.right - elementsTrbl.right < padding.right) {
- newTrbl.right = elementsTrbl.right + offset.right;
- }
- if (targetTrbl.bottom - elementsTrbl.bottom < padding.bottom) {
- newTrbl.bottom = elementsTrbl.bottom + offset.bottom;
- }
- return asBounds(assign({}, targetTrbl, newTrbl));
- };
- /**
- * Expand the target shape respecting rules, offset and padding
- *
- * @param {Array<djs.model.Shape>} elements
- * @param {djs.model.Shape|String} target|targetId
- */
- AutoResize.prototype._expand = function(elements, target) {
- if (typeof target === 'string') {
- target = this._elementRegistry.get(target);
- }
- var allowed = this._rules.allowed('element.autoResize', {
- elements: elements,
- target: target
- });
- if (!allowed) {
- return;
- }
- // calculate the new bounds
- var newBounds = this._getOptimalBounds(elements, target);
- if (!boundsChanged(newBounds, target)) {
- return;
- }
- // resize the parent shape
- this.resize(target, newBounds);
- var parent = target.parent;
- // recursively expand parent elements
- if (parent) {
- this._expand([ target ], parent);
- }
- };
- /**
- * Get the amount to expand the given shape in each direction.
- *
- * @param {djs.model.Shape} shape
- *
- * @return {Object} {top, bottom, left, right}
- */
- AutoResize.prototype.getOffset = function(shape) {
- return { top: 60, bottom: 60, left: 100, right: 100 };
- };
- /**
- * Get the activation threshold for each side for which
- * resize triggers.
- *
- * @param {djs.model.Shape} shape
- *
- * @return {Object} {top, bottom, left, right}
- */
- AutoResize.prototype.getPadding = function(shape) {
- return { top: 2, bottom: 2, left: 15, right: 15 };
- };
- /**
- * Perform the actual resize operation.
- *
- * @param {djs.model.Shape} target
- * @param {Object} newBounds
- */
- AutoResize.prototype.resize = function(target, newBounds) {
- this._modeling.resizeShape(target, newBounds);
- };
- function boundsChanged(newBounds, oldBounds) {
- return (
- newBounds.x !== oldBounds.x ||
- newBounds.y !== oldBounds.y ||
- newBounds.width !== oldBounds.width ||
- newBounds.height !== oldBounds.height
- );
- }
|