Skip to content

Commit

Permalink
fix: preserve original element styles after dragging
Browse files Browse the repository at this point in the history
  • Loading branch information
mattlewis92 committed Jun 7, 2018
1 parent 7e5ad44 commit f36ed2d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 102 deletions.
87 changes: 36 additions & 51 deletions src/draggable.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
touchcancel?: () => void;
} = {};

private ghostElement: HTMLElement | null;

/**
* @hidden
*/
Expand Down Expand Up @@ -226,37 +228,41 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {

if (this.ghostDragEnabled) {
const rect = this.element.nativeElement.getBoundingClientRect();
const clone = this.element.nativeElement.cloneNode(true);
this.renderer.setStyle(clone, 'visibility', 'hidden');
const clone = this.element.nativeElement.cloneNode(
true
) as HTMLElement;
this.renderer.setStyle(
this.element.nativeElement,
'visibility',
'hidden'
);
this.element.nativeElement.parentNode!.insertBefore(
clone,
this.element.nativeElement
this.element.nativeElement.nextSibling
);
this.ghostElement = clone;

this.setElementStyles({
this.setElementStyles(clone, {
position: 'fixed',
top: `${rect.top}px`,
left: `${rect.left}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
zIndex: '10'
zIndex: '10',
cursor: this.dragCursor
});

dragEnd$.subscribe(() => {
clone.parentElement!.removeChild(clone);
this.setElementStyles({
position: '',
top: '',
left: '',
width: '',
height: '',
zIndex: ''
});
this.ghostElement = null;
this.renderer.setStyle(
this.element.nativeElement,
'visibility',
''
);
});
}

this.setCursor(this.dragCursor);

this.draggableHelper.currentDrag.next(currentDrag);
});

Expand All @@ -265,14 +271,6 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
this.zone.run(() => {
this.dragEnd.next({ x, y });
});
this.setCssTransform('');
if (this.ghostDragEnabled) {
this.renderer.setStyle(
this.element.nativeElement,
'pointerEvents',
''
);
}
this.renderer.removeClass(
this.element.nativeElement,
this.dragActiveClass
Expand Down Expand Up @@ -304,14 +302,17 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
this.zone.run(() => {
this.dragging.next({ x, y });
});
if (this.ghostDragEnabled) {
this.renderer.setStyle(
this.element.nativeElement,
'pointerEvents',
'none'
);
if (this.ghostElement) {
const transform = `translate(${x}px, ${y}px)`;
this.setElementStyles(this.ghostElement, {
pointerEvents: 'none',
transform,
'-webkit-transform': transform,
'-ms-transform': transform,
'-moz-transform': transform,
'-o-transform': transform
});
}
this.setCssTransform(`translate(${x}px, ${y}px)`);
currentDrag.next({
clientX,
clientY,
Expand Down Expand Up @@ -475,25 +476,6 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
this.setCursor('');
}

private setCssTransform(value: string): void {
if (this.ghostDragEnabled) {
const transformAttributes = [
'transform',
'-webkit-transform',
'-ms-transform',
'-moz-transform',
'-o-transform'
];
transformAttributes.forEach(transformAttribute => {
this.renderer.setStyle(
this.element.nativeElement,
transformAttribute,
value
);
});
}
}

private canDrag(): boolean {
return this.dragAxis.x || this.dragAxis.y;
}
Expand All @@ -509,9 +491,12 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
});
}

private setElementStyles(styles: { [key: string]: string }) {
private setElementStyles(
element: HTMLElement,
styles: { [key: string]: string }
) {
Object.keys(styles).forEach(key => {
this.renderer.setStyle(this.element.nativeElement, key, styles[key]);
this.renderer.setStyle(element, key, styles[key]);
});
}
}
82 changes: 31 additions & 51 deletions test/draggable.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,37 +75,37 @@ describe('draggable directive', () => {
x: 2,
y: 0
});
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 8 });
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).not.to.be.ok;
});

it('should end the pointerUp observable when the component is destroyed', () => {
const complete: sinon.SinonSpy = sinon.spy();
const complete = sinon.spy();
fixture.componentInstance.draggable.pointerUp.subscribe({ complete });
fixture.destroy();
expect(complete).to.have.been.calledOnce;
});

it('should end the pointerDown observable when the component is destroyed', () => {
const complete: sinon.SinonSpy = sinon.spy();
const complete = sinon.spy();
fixture.componentInstance.draggable.pointerDown.subscribe({ complete });
fixture.destroy();
expect(complete).to.have.been.calledOnce;
});

it('should end the pointerMove observable when the component is destroyed', () => {
const complete: sinon.SinonSpy = sinon.spy();
const complete = sinon.spy();
fixture.componentInstance.draggable.pointerMove.subscribe({ complete });
fixture.destroy();
expect(complete).to.have.been.calledOnce;
Expand All @@ -122,7 +122,8 @@ describe('draggable directive', () => {
x: 0,
y: 2
});
expect(draggableElement.style.transform).to.equal('translate(0px, 2px)');
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.transform).to.equal('translate(0px, 2px)');
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 0,
Expand All @@ -141,7 +142,8 @@ describe('draggable directive', () => {
x: 2,
y: 0
});
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 2,
Expand All @@ -168,7 +170,6 @@ describe('draggable directive', () => {
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 12 });
expect(fixture.componentInstance.dragStart).not.to.have.been.called;
expect(fixture.componentInstance.dragging).not.to.have.been.called;
expect(draggableElement.style.transform).not.to.be.ok;
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
expect(fixture.componentInstance.dragEnd).not.to.have.been.called;
});
Expand Down Expand Up @@ -301,19 +302,19 @@ describe('draggable directive', () => {
x: 2,
y: 0
});
expect(draggableElement.style.transform).not.to.ok;
expect(draggableElement.nextSibling).not.to.ok;
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 8 });
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).not.to.ok;
expect(draggableElement.nextSibling).not.to.ok;
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).not.to.ok;
expect(draggableElement.nextSibling).not.to.ok;
});

it('should auto set the mouse cursor to the move icon on hover', () => {
Expand Down Expand Up @@ -445,23 +446,23 @@ describe('draggable directive', () => {
x: 2,
y: 0
});
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
triggerDomEvent('touchmove', draggableElement, {
targetTouches: [{ clientX: 7, clientY: 8 }]
});
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
triggerDomEvent('touchend', draggableElement, {
changedTouches: [{ clientX: 7, clientY: 8 }]
});
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).not.to.be.ok;
});

it('should work use the touch cancel event to end the drag', () => {
Expand All @@ -481,23 +482,23 @@ describe('draggable directive', () => {
x: 2,
y: 0
});
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
triggerDomEvent('touchmove', draggableElement, {
targetTouches: [{ clientX: 7, clientY: 8 }]
});
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
triggerDomEvent('touchcancel', draggableElement, {
changedTouches: [{ clientX: 7, clientY: 8 }]
});
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
x: 2,
y: -2
});
expect(draggableElement.style.transform).not.to.be.ok;
});

it('should only unregister the touch move listener if it exists', () => {
Expand Down Expand Up @@ -550,7 +551,6 @@ describe('draggable directive', () => {
expect(fixture.componentInstance.dragging).not.to.have.been.called;
triggerDomEvent('mouseup', draggableElement, { clientX: 5, clientY: 10 });
expect(fixture.componentInstance.dragEnd).not.to.have.been.called;
expect(draggableElement.style.transform).not.to.be.ok;
});

it('should call the drag start, dragging and end events in order', () => {
Expand All @@ -567,43 +567,23 @@ describe('draggable directive', () => {
);
});

it('should create a clone of the element and make the host static', () => {
it('should create a clone of the element', () => {
const draggableElement: HTMLElement =
fixture.componentInstance.draggable.element.nativeElement;
expect(
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
'mwldraggable'
)
).to.be.false;
triggerDomEvent('mousedown', draggableElement, { clientX: 5, clientY: 10 });
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 10 });
expect(draggableElement.style.position).to.equal('fixed');
expect(draggableElement.style.top).to.be.ok;
expect(draggableElement.style.left).to.be.ok;
expect(draggableElement.style.width).to.be.ok;
expect(draggableElement.style.height).to.be.ok;
expect(draggableElement.style.zIndex).to.equal('10');
expect(draggableElement.previousSibling).to.be.ok;
expect(
(draggableElement.previousElementSibling as HTMLElement).style.visibility
).to.equal('hidden');
expect(
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
'mwldraggable'
)
).to.be.true;
const ghostElement = draggableElement.nextSibling as HTMLElement;
expect(ghostElement.style.position).to.equal('fixed');
expect(ghostElement.style.top).to.be.ok;
expect(ghostElement.style.left).to.be.ok;
expect(ghostElement.style.width).to.be.ok;
expect(ghostElement.style.height).to.be.ok;
expect(ghostElement.style.zIndex).to.equal('10');
expect(draggableElement.style.visibility).to.equal('hidden');
expect((ghostElement as HTMLElement).hasAttribute('mwldraggable')).to.be
.true;
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
expect(draggableElement.style.position).not.to.be.ok;
expect(draggableElement.style.top).not.to.be.ok;
expect(draggableElement.style.left).not.to.be.ok;
expect(draggableElement.style.width).not.to.be.ok;
expect(draggableElement.style.height).not.to.be.ok;
expect(draggableElement.style.zIndex).not.to.be.ok;
expect(
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
'mwldraggable'
)
).to.be.false;
expect(draggableElement.style.visibility).not.to.be.ok;
});

it('should add and remove the drag active class', () => {
Expand Down

0 comments on commit f36ed2d

Please sign in to comment.