Skip to content

Commit

Permalink
feat(resizeSnapGrid): allow resizing to fit to a snap grid
Browse files Browse the repository at this point in the history
Closes #3
  • Loading branch information
Matt Lewis committed Jun 26, 2016
1 parent 4c59b05 commit 74424ba
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 1 deletion.
1 change: 1 addition & 0 deletions demo/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {Resizable, ResizeEvent, ResizeHandle} from './../angular2-resizable';
[validateResize]="validate"
[resizeEdges]="{bottom: true, right: true, top: true, left: true}"
[enableResizeStyling]="true"
[resizeSnapGrid]="{left: 50, right: 50}"
(onResizeEnd)="onResizeEnd($event)">
<img
src="http://i.imgur.com/eqzz2dl.gif"
Expand Down
64 changes: 63 additions & 1 deletion src/resizable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/pairwise';
import 'rxjs/add/operator/take';

export interface Edges {
top?: boolean | number;
Expand All @@ -40,6 +42,11 @@ export interface ResizeEvent {
edges: Edges;
}

interface Coordinate {
x: number;
y: number;
}

const isNumberCloseTo: Function = (value1: number, value2: number, precision: number = 3): boolean => {
const diff: number = Math.abs(value1 - value2);
return diff < precision;
Expand Down Expand Up @@ -154,6 +161,7 @@ export class Resizable implements OnInit, AfterViewInit {
@Input() validateResize: Function;
@Input() resizeEdges: Edges = {};
@Input() enableResizeStyling: boolean = false;
@Input() resizeSnapGrid: Edges = {};
@Output() onResizeStart: EventEmitter<Object> = new EventEmitter(false);
@Output() onResize: EventEmitter<Object> = new EventEmitter(false);
@Output() onResizeEnd: EventEmitter<Object> = new EventEmitter(false);
Expand Down Expand Up @@ -202,12 +210,66 @@ export class Resizable implements OnInit, AfterViewInit {
});

const mousedrag: Observable<any> = this.mousedown.flatMap(startCoords => {
return this.mousemove.map(moveCoords => {

const getDiff: Function = moveCoords => {
return {
mouseX: moveCoords.mouseX - startCoords.mouseX,
mouseY: moveCoords.mouseY - startCoords.mouseY
};
};

const getSnapGrid: Function = () => {
const snapGrid: Coordinate = {x: 1, y: 1};

if (currentResize) {
if (this.resizeSnapGrid.left && currentResize.edges.left) {
snapGrid.x = +this.resizeSnapGrid.left;
} else if (this.resizeSnapGrid.right && currentResize.edges.right) {
snapGrid.x = +this.resizeSnapGrid.right;
}

if (this.resizeSnapGrid.top && currentResize.edges.top) {
snapGrid.y = +this.resizeSnapGrid.top;
} else if (this.resizeSnapGrid.bottom && currentResize.edges.bottom) {
snapGrid.y = +this.resizeSnapGrid.bottom;
}
}

return snapGrid;
};

const getGrid: Function = (coords, snapGrid) => {
return {
x: Math.ceil(coords.mouseX / snapGrid.x),
y: Math.ceil(coords.mouseY / snapGrid.y)
};
};

return merge(
this.mousemove.take(1).map(coords => [, coords]),
this.mousemove.pairwise()
).map(([previousCoords, newCoords]) => {
return [previousCoords ? getDiff(previousCoords) : previousCoords, getDiff(newCoords)];
}).filter(([previousCoords, newCoords]) => {

if (!previousCoords) {
return true;
}

const snapGrid: Coordinate = getSnapGrid();
const previousGrid: Coordinate = getGrid(previousCoords, snapGrid);
const newGrid: Coordinate = getGrid(newCoords, snapGrid);

return (previousGrid.x !== newGrid.x || previousGrid.y !== newGrid.y);

}).map(([, newCoords]) => {
const snapGrid: Coordinate = getSnapGrid();
return {
mouseX: Math.round(newCoords.mouseX / snapGrid.x) * snapGrid.x,
mouseY: Math.round(newCoords.mouseY / snapGrid.y) * snapGrid.y
};
}).takeUntil(merge(this.mouseup, this.mousedown));

}).filter(() => !!currentResize);

mousedrag.map(({mouseX, mouseY}) => {
Expand Down
73 changes: 73 additions & 0 deletions test/resizable.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('resizable directive', () => {
[validateResize]="validate"
[resizeEdges]="resizeEdges"
[enableResizeStyling]="enableResizeStyling"
[resizeSnapGrid]="resizeSnapGrid"
(onResizeStart)="onResizeStart($event)"
(onResize)="onResize($event)"
(onResizeEnd)="onResizeEnd($event)">
Expand All @@ -52,6 +53,7 @@ describe('resizable directive', () => {
public validate: jasmine.Spy = jasmine.createSpy('validate');
public resizeEdges: Edges = {top: true, bottom: true, left: true, right: true};
public enableResizeStyling: boolean = true;
public resizeSnapGrid: Object = {};

constructor() {
this.validate.and.returnValue(true);
Expand Down Expand Up @@ -910,4 +912,75 @@ describe('resizable directive', () => {

}));

it('should support resizing to a snap grid', async(() => {

createComponent().then((fixture: ComponentFixture<TestCmp>) => {
fixture.componentInstance.resizeSnapGrid = {left: 10};
fixture.detectChanges();
const elm: HTMLElement = fixture.componentInstance.resizable.elm.nativeElement;
triggerDomEvent('mousedown', elm, {
clientX: 100,
clientY: 205
});
triggerDomEvent('mousemove', elm, {
clientX: 99,
clientY: 205
});
expect(fixture.componentInstance.onResize).toHaveBeenCalledWith({
edges: {
left: 0
},
rectangle: {
top: 200,
left: 100,
width: 300,
height: 150,
right: 400,
bottom: 350
}
});
triggerDomEvent('mousemove', elm, {
clientX: 95,
clientY: 205
});
expect(fixture.componentInstance.onResize.calls.count()).toBe(1);
triggerDomEvent('mousemove', elm, {
clientX: 89,
clientY: 205
});
expect(fixture.componentInstance.onResize).toHaveBeenCalledWith({
edges: {
left: -10
},
rectangle: {
top: 200,
left: 90,
width: 310,
height: 150,
right: 400,
bottom: 350
}
});
expect(fixture.componentInstance.onResize.calls.count()).toBe(2);
triggerDomEvent('mouseup', elm, {
clientX: 89,
clientY: 205
});
expect(fixture.componentInstance.onResizeEnd).toHaveBeenCalledWith({
edges: {
left: -10
},
rectangle: {
top: 200,
left: 90,
width: 310,
height: 150,
right: 400,
bottom: 350
}
});
});

}));

});
12 changes: 12 additions & 0 deletions webpack.config.dist.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ module.exports = {
commonjs: 'rxjs/operator/filter',
commonjs2: 'rxjs/operator/filter',
amd: 'rxjs/operator/filter'
},
'rxjs/add/operator/pairwise': {
root: ['rx', 'Observable'],
commonjs: 'rxjs/operator/pairwise',
commonjs2: 'rxjs/operator/pairwise',
amd: 'rxjs/operator/pairwise'
},
'rxjs/add/operator/take': {
root: ['rx', 'Observable'],
commonjs: 'rxjs/operator/take',
commonjs2: 'rxjs/operator/take',
amd: 'rxjs/operator/take'
}
},
devtool: 'source-map',
Expand Down

0 comments on commit 74424ba

Please sign in to comment.