Skip to content

Commit

Permalink
feat(grid-snapping): integrate auto resize
Browse files Browse the repository at this point in the history
  • Loading branch information
philippfromme committed Apr 18, 2019
1 parent e183fea commit cd74f22
Show file tree
Hide file tree
Showing 7 changed files with 503 additions and 11 deletions.
58 changes: 50 additions & 8 deletions lib/features/auto-resize/AutoResize.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import {
assign,
flatten,
forEach,
groupBy,
isArray,
values,
groupBy
pick,
values
} from 'min-dash';

import CommandInterceptor from '../../command/CommandInterceptor';
Expand Down Expand Up @@ -191,7 +192,9 @@ AutoResize.prototype._expand = function(elements, target) {
}

// resize the parent shape
this.resize(target, newBounds);
this.resize(target, newBounds, {
autoResize: getResizeDirections(pick(target, [ 'x', 'y', 'width', 'height' ]), newBounds)
});

var parent = target.parent;

Expand All @@ -218,11 +221,9 @@ AutoResize.prototype.getOffset = function(shape) {
* 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) {
AutoResize.prototype.getPadding = function() {
return { top: 2, bottom: 2, left: 15, right: 15 };
};

Expand All @@ -233,8 +234,8 @@ AutoResize.prototype.getPadding = function(shape) {
* @param {djs.model.Shape} target
* @param {Object} newBounds
*/
AutoResize.prototype.resize = function(target, newBounds) {
this._modeling.resizeShape(target, newBounds);
AutoResize.prototype.resize = function(target, newBounds, hints) {
this._modeling.resizeShape(target, newBounds, null, hints);
};


Expand All @@ -245,4 +246,45 @@ function boundsChanged(newBounds, oldBounds) {
newBounds.width !== oldBounds.width ||
newBounds.height !== oldBounds.height
);
}

/**
* Get directions of resize as <nwse>.
*
* @param {Object} oldBounds - Old bounds.
* @param {number} oldBounds.x - Old x.
* @param {number} oldBounds.y - Old y.
* @param {number} oldBounds.width - Old width.
* @param {number} oldBounds.height - Old height.
* @param {Object} newBounds - New bounds.
* @param {number} newBounds.x - New x.
* @param {number} newBounds.y - New y.
* @param {number} newBounds.width - New width.
* @param {number} newBounds.height - New height.
*
* @returns {string}
*/
function getResizeDirections(oldBounds, newBounds) {
var directions = '';

oldBounds = asTRBL(oldBounds);
newBounds = asTRBL(newBounds);

if (oldBounds.top > newBounds.top) {
directions = directions.concat('n');
}

if (oldBounds.right < newBounds.right) {
directions = directions.concat('w');
}

if (oldBounds.bottom < newBounds.bottom) {
directions = directions.concat('s');
}

if (oldBounds.left > newBounds.left) {
directions = directions.concat('e');
}

return directions;
}
183 changes: 183 additions & 0 deletions lib/features/grid-snapping/behavior/ResizeBehavior.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import inherits from 'inherits';

import CommandInterceptor from '../../../command/CommandInterceptor';

import {
assign,
isString
} from 'min-dash';


/**
* Integrates resizing with grid snapping.
*/
export default function ResizeBehavior(eventBus, gridSnapping) {
CommandInterceptor.call(this, eventBus);

this._gridSnapping = gridSnapping;

var self = this;

this.preExecute('shape.resize', function(event) {
var context = event.context,
hints = context.hints || {},
autoResize = hints.autoResize;

if (!autoResize) {
return;
}

var shape = context.shape,
newBounds = context.newBounds;

if (isString(autoResize)) {
return self.snapComplex(shape, newBounds, autoResize);
}

self.snapSimple(shape, newBounds);
});
}

ResizeBehavior.$inject = [
'eventBus',
'gridSnapping',
'modeling'
];

inherits(ResizeBehavior, CommandInterceptor);

/**
* Snap width and height in relation to center.
*
* @param {djs.model.shape} shape - Shape.
* @param {Object} newBounds - New bounds of shape.
* @param {number} newBounds.x - New x of shape.
* @param {number} newBounds.y - New y of shape.
* @param {number} newBounds.width - New width of shape.
* @param {number} newBounds.height - New height of shape.
*/
ResizeBehavior.prototype.snapSimple = function(shape, newBounds) {
var gridSnapping = this._gridSnapping;

newBounds.width = gridSnapping.snapValue(newBounds.width, {
min: newBounds.width
});

newBounds.height = gridSnapping.snapValue(newBounds.height, {
min: newBounds.height
});

newBounds.x = shape.x + (shape.width / 2) - (newBounds.width / 2);
newBounds.y = shape.y + (shape.height / 2) - (newBounds.height / 2);
};

/**
* Snap x, y, width and height according to given directions.
*
* @param {djs.model.shape} shape - Shape.
* @param {Object} newBounds - New bounds of shape.
* @param {number} newBounds.x - New x of shape.
* @param {number} newBounds.y - New y of shape.
* @param {number} newBounds.width - New width of shape.
* @param {number} newBounds.height - New height of shape.
* @param {string} directions - Directions as {n|w|s|e}.
*/
ResizeBehavior.prototype.snapComplex = function(shape, newBounds, directions) {
if (/w|e/.test(directions)) {
this.snapHorizontally(shape, newBounds, directions);
}

if (/n|s/.test(directions)) {
this.snapVertically(shape, newBounds, directions);
}
};

/**
* Snap in one or both directions horizontally.
*
* @param {djs.model.shape} shape - Shape.
* @param {Object} newBounds - New bounds of shape.
* @param {number} newBounds.x - New x of shape.
* @param {number} newBounds.y - New y of shape.
* @param {number} newBounds.width - New width of shape.
* @param {number} newBounds.height - New height of shape.
* @param {string} directions - Directions as {n|w|s|e}.
*/
ResizeBehavior.prototype.snapHorizontally = function(shape, newBounds, directions) {
var gridSnapping = this._gridSnapping,
west = /w/.test(directions),
east = /e/.test(directions);

var snappedNewBounds = {};

snappedNewBounds.width = gridSnapping.snapValue(newBounds.width, {
min: newBounds.width
});

if (east) {

// handle <we>
if (west) {
snappedNewBounds.x = gridSnapping.snapValue(newBounds.x, {
max: newBounds.x
});

snappedNewBounds.width += gridSnapping.snapValue(newBounds.x - snappedNewBounds.x, {
min: newBounds.x - snappedNewBounds.x
});
}

// handle <e>
else {
newBounds.x = newBounds.x + newBounds.width - snappedNewBounds.width;
}
}

// assign snapped x and width
assign(newBounds, snappedNewBounds);
};

/**
* Snap in one or both directions vertically.
*
* @param {djs.model.shape} shape - Shape.
* @param {Object} newBounds - New bounds of shape.
* @param {number} newBounds.x - New x of shape.
* @param {number} newBounds.y - New y of shape.
* @param {number} newBounds.width - New width of shape.
* @param {number} newBounds.height - New height of shape.
* @param {string} directions - Directions as {n|w|s|e}.
*/
ResizeBehavior.prototype.snapVertically = function(shape, newBounds, directions) {
var gridSnapping = this._gridSnapping,
north = /n/.test(directions),
south = /s/.test(directions);

var snappedNewBounds = {};

snappedNewBounds.height = gridSnapping.snapValue(newBounds.height, {
min: newBounds.height
});

if (north) {

// handle <ns>
if (south) {
snappedNewBounds.y = gridSnapping.snapValue(newBounds.y, {
max: newBounds.y
});

snappedNewBounds.height += gridSnapping.snapValue(newBounds.y - snappedNewBounds.y, {
min: newBounds.y - snappedNewBounds.y
});
}

// handle <n>
else {
newBounds.y = newBounds.y + newBounds.height - snappedNewBounds.height;
}
}

// assign snapped y and height
assign(newBounds, snappedNewBounds);
};
13 changes: 13 additions & 0 deletions lib/features/grid-snapping/behavior/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import GridSnapping from '../GridSnapping';

import ResizeBehavior from './ResizeBehavior';

export default {
__depends__: [
GridSnapping
],
__init__: [
'gridSnappingResizeBehavior'
],
gridSnappingResizeBehavior: [ 'type', ResizeBehavior ]
};
3 changes: 3 additions & 0 deletions lib/features/grid-snapping/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import Grid from './Grid';
import GridSnapping from './GridSnapping';

import BehaviorModule from './behavior';

export default {
__depends__: [ BehaviorModule ],
__init__: [ 'grid', 'gridSnapping' ],
grid: [ 'type', Grid ],
gridSnapping: [ 'type', GridSnapping ]
Expand Down
5 changes: 3 additions & 2 deletions lib/features/modeling/Modeling.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,12 @@ Modeling.prototype.alignElements = function(elements, alignment) {
this._commandStack.execute('elements.align', context);
};

Modeling.prototype.resizeShape = function(shape, newBounds, minBounds) {
Modeling.prototype.resizeShape = function(shape, newBounds, minBounds, hints) {
var context = {
shape: shape,
newBounds: newBounds,
minBounds: minBounds
minBounds: minBounds,
hints: hints
};

this._commandStack.execute('shape.resize', context);
Expand Down
50 changes: 49 additions & 1 deletion test/spec/features/auto-resize/AutoResizeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -783,4 +783,52 @@ describe('features/auto-resize', function() {
expect(getOffsetSpy).to.have.been.calledWith(parentShape);
}));

});

describe('hints', function() {

describe('autoResize', function() {

it('on move <ne>', inject(function(modeling, autoResize) {

// given
var resizeSpy = spy(autoResize, 'resize');

// when
modeling.moveElements([ childShape1 ], { x: -100, y: -100 }, parentShape);

// then
expect(getHints(resizeSpy)).to.exist;
expect(getHints(resizeSpy)).to.eql({
autoResize: 'ne'
});
}));


it('on resize child shape <nwse>', inject(function(modeling, autoResize) {

// given
var resizeSpy = spy(autoResize, 'resize');

var newBounds = { x: 0, y: 0, width: 500, height: 500 };

// when
modeling.resizeShape(childShape1, newBounds);

// then
expect(getHints(resizeSpy)).to.exist;
expect(getHints(resizeSpy)).to.eql({
autoResize: 'nwse'
});
}));

});

});

});

// helpers //////////

function getHints(spy) {
return spy.getCall(0).lastArg;
}
Loading

0 comments on commit cd74f22

Please sign in to comment.