Skip to content

Commit

Permalink
feat: initial WIP resizable implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Lewis committed May 26, 2016
1 parent 88a87bd commit 22af52f
Showing 1 changed file with 127 additions and 14 deletions.
141 changes: 127 additions & 14 deletions src/resizable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,145 @@ import {
HostListener,
Renderer,
ElementRef,
OnInit
} from '@angular/core';
import {Subject} from 'rxjs';

const isNumberCloseTo: Function = (value1: number, value2: number, precision: number = 3): boolean => {
const diff: number = Math.abs(value1 - value2);
return diff < precision;
};

@Directive({
selector: '[mwl-resizeable]'
})
export class Resizable {
export class Resizable implements OnInit {

private mouseup: Subject<any> = new Subject();
private mousedown: Subject<any> = new Subject();
private mousemove: Subject<any> = new Subject();

constructor(private renderer: Renderer, private elm: ElementRef) {}

private isNumberCloseTo(value1: number, value2: number, precision: number = 3): boolean {
const diff: number = Math.abs(value1 - value2);
return diff < precision;
ngOnInit(): void {

let currentResize;

this.mousemove.subscribe(({mouseX, mouseY}) => {

const resizeEdges = this.getResizeEdges({mouseX, mouseY});
if (resizeEdges.left || resizeEdges.right) {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'ew-resize');
} else if (resizeEdges.top || resizeEdges.bottom) {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'ns-resize');
} else {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'auto');
}

});

const mousedrag = this.mousedown.flatMap(startCoords => {
return this.mousemove.map(moveCoords => {
return {
mouseX: moveCoords.mouseX - startCoords.mouseX,
mouseY: moveCoords.mouseY - startCoords.mouseY
};
});
}).takeUntil(this.mouseup);

mousedrag.subscribe(({mouseX, mouseY}) => {
if (currentResize) {

const newBoundingRect = {
top: currentResize.startingRect.top,
bottom: currentResize.startingRect.bottom,
left: currentResize.startingRect.left,
right: currentResize.startingRect.right
};

if (currentResize.edges.top) {
newBoundingRect.top += mouseY;
}
if (currentResize.edges.bottom) {
newBoundingRect.bottom += mouseY;
}
if (currentResize.edges.left) {
newBoundingRect.left += mouseX;
}
if (currentResize.edges.right) {
newBoundingRect.right += mouseX;
}
newBoundingRect.height = newBoundingRect.bottom - newBoundingRect.top;
newBoundingRect.width = newBoundingRect.right - newBoundingRect.left;

const translateY = (newBoundingRect.top - currentResize.startingRect.top);
let translateX = (newBoundingRect.left - currentResize.startingRect.left);

if (currentResize.edges.right) {
translateX += (mouseX / 2);
} else if (currentResize.edges.left) {
translateX -= (mouseX / 2);
}

if (newBoundingRect.height > 0 && newBoundingRect.width > 0) {
this.renderer.setElementStyle(this.elm.nativeElement, 'height', newBoundingRect.height + 'px');
this.renderer.setElementStyle(this.elm.nativeElement, 'width', newBoundingRect.width + 'px');
this.renderer.setElementStyle(this.elm.nativeElement, 'transform', `translate(${translateX}px,${translateY}px)`);
}

}
});

this.mousedown.subscribe(({mouseX, mouseY}) => {
const resizeEdges = this.getResizeEdges({mouseX, mouseY});
if (Object.keys(resizeEdges).length > 0) {
currentResize = {
edges: resizeEdges,
startingRect: this.elm.nativeElement.getBoundingClientRect()
};
console.log('resize started', currentResize);
}
});

this.mouseup.subscribe(() => {
if (currentResize) {
console.log('resize ended');
currentResize = null;
}
});

}

@HostListener('document:mouseup', ['$event.clientX', '$event.clientY'])
private onMouseup(mouseX: number, mouseY: number): void {
this.mouseup.next({mouseX, mouseY});
}

// TODO - refactor this to use more observables like this
// https://github.com/AngularClass/angular2-examples/blob/master/rx-draggable/directives/draggable.ts
@HostListener('mousemove', ['$event.clientX', '$event.clientY'])
private onMouseMove(mouseX: number, mouseY: number): void {
@HostListener('document:mousedown', ['$event.clientX', '$event.clientY'])
private onMousedown(mouseX: number, mouseY: number): void {
this.mousedown.next({mouseX, mouseY});
}

@HostListener('document:mousemove', ['$event.clientX', '$event.clientY'])
private onMousemove(mouseX: number, mouseY: number): void {
this.mousemove.next({mouseX, mouseY});
}

private getResizeEdges({mouseX, mouseY}) {

const elmPosition: ClientRect = this.elm.nativeElement.getBoundingClientRect();
if (this.isNumberCloseTo(mouseX, elmPosition.left) || this.isNumberCloseTo(mouseX, elmPosition.right)) {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'ew-resize');
} else if (this.isNumberCloseTo(mouseY, elmPosition.top) || this.isNumberCloseTo(mouseY, elmPosition.bottom)) {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'ns-resize');
} else {
this.renderer.setElementStyle(this.elm.nativeElement, 'cursor', 'auto');
if (isNumberCloseTo(mouseX, elmPosition.left)) {
return {left: true};
} if (isNumberCloseTo(mouseX, elmPosition.right)) {
return {right: true};
} if (isNumberCloseTo(mouseY, elmPosition.top)) {
return {top: true};
} if (isNumberCloseTo(mouseY, elmPosition.bottom)) {
return {bottom: true};
} else {
return {};
}

}

}

0 comments on commit 22af52f

Please sign in to comment.