Skip to content

Commit

Permalink
feat(draggable): add dragContainer option
Browse files Browse the repository at this point in the history
Closes #10
  • Loading branch information
Matt Lewis committed Dec 12, 2016
1 parent fb1dd99 commit fb75711
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/draggable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ export type ValidateDrag = (coordinates: Coordinates) => boolean;

const MOVE_CURSOR: string = 'move';

function isInside(outer: ClientRect, inner: ClientRect): boolean {
return outer.left <= inner.left &&
inner.left <= outer.right &&
outer.left <= inner.right &&
inner.right <= outer.right &&
outer.top <= inner.top &&
inner.top <= outer.bottom &&
outer.top <= inner.bottom &&
inner.bottom <= outer.bottom;
}

@Directive({
selector: '[mwlDraggable]'
})
Expand All @@ -36,6 +47,8 @@ export class Draggable implements OnInit, OnDestroy {

@Input() validateDrag: ValidateDrag;

@Input() dragContainer: HTMLElement;

@Output() dragStart: EventEmitter<Coordinates> = new EventEmitter<Coordinates>();

@Output() dragging: EventEmitter<Coordinates> = new EventEmitter<Coordinates>();
Expand All @@ -58,6 +71,7 @@ export class Draggable implements OnInit, OnDestroy {

this.dragStart.next({x: 0, y: 0});
this.setCursor(MOVE_CURSOR);
const startPosition: ClientRect = this.element.nativeElement.getBoundingClientRect();

if (this.ghostDragEnabled) {
this.renderer.setElementStyle(this.element.nativeElement, 'pointerEvents', 'none');
Expand Down Expand Up @@ -105,6 +119,22 @@ export class Draggable implements OnInit, OnDestroy {

return moveData;
})
.filter((moveData: Coordinates) => {

if (!this.dragContainer) {
return true;
}

const newRect: ClientRect = Object.assign({}, startPosition, {
left: startPosition.left + moveData.x,
right: startPosition.right + moveData.x,
top: startPosition.top + moveData.y,
bottom: startPosition.bottom + moveData.y
});

return isInside(this.dragContainer.getBoundingClientRect(), newRect);

})
.takeUntil(Observable.merge(this.mouseUp, this.mouseDown));

mouseMove.takeLast(1).subscribe(({x, y}) => {
Expand Down
39 changes: 38 additions & 1 deletion test/draggable.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,30 @@ describe('draggable directive', () => {
[dragSnapGrid]="dragSnapGrid"
[ghostDragEnabled]="ghostDragEnabled"
[validateDrag]="validateDrag"
[dragContainer]="dragContainer"
(dragStart)="dragStart($event)"
(dragging)="dragging($event)"
(dragEnd)="dragEnd($event)">
Drag me!
</div>`,
</div>
<div class="drag-container"></div>
`,
styles: [`
[mwlDraggable] {
position: absolute;
top: 0;
left: 0;
width: 5px;
height: 5px;
}
.drag-container {
position: absolute;
top: 0;
left: 0;
width: 10px;
height: 10px;
}
`]
})
class TestCmp {

Expand All @@ -32,6 +51,7 @@ describe('draggable directive', () => {
public dragSnapGrid: any = {};
public ghostDragEnabled: boolean = true;
public validateDrag: ValidateDrag;
public dragContainer: HTMLElement;

}

Expand Down Expand Up @@ -239,4 +259,21 @@ describe('draggable directive', () => {
expect(fixture.componentInstance.dragEnd).to.have.been.calledOnce;
});

it('should constrain dragging within the drag container', () => {
const dragContainer: HTMLElement = fixture.nativeElement.querySelector('.drag-container');
const draggableElement: HTMLElement = fixture.componentInstance.draggable.element.nativeElement;
fixture.componentInstance.dragContainer = dragContainer;
fixture.detectChanges();
triggerDomEvent('mousedown', draggableElement, {clientX: 1, clientY: 1});
triggerDomEvent('mousemove', draggableElement, {clientX: 5, clientY: 5});
expect(fixture.componentInstance.dragging).to.have.been.calledWith({x: 4, y: 4});
triggerDomEvent('mousemove', draggableElement, {clientX: 6, clientY: 6});
expect(fixture.componentInstance.dragging).to.have.been.calledWith({x: 5, y: 5});
triggerDomEvent('mousemove', draggableElement, {clientX: 7, clientY: 7});
expect(fixture.componentInstance.dragging).not.to.have.been.calledWith({x: 6, y: 6});
triggerDomEvent('mousemove', draggableElement, {clientX: 8, clientY: 8});
triggerDomEvent('mouseup', draggableElement, {clientX: 8, clientY: 8});
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({x: 5, y: 5});
});

});

0 comments on commit fb75711

Please sign in to comment.