Skip to content

Commit f36ed2d

Browse files
committed
fix: preserve original element styles after dragging
1 parent 7e5ad44 commit f36ed2d

File tree

2 files changed

+67
-102
lines changed

2 files changed

+67
-102
lines changed

src/draggable.directive.ts

+36-51
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
136136
touchcancel?: () => void;
137137
} = {};
138138

139+
private ghostElement: HTMLElement | null;
140+
139141
/**
140142
* @hidden
141143
*/
@@ -226,37 +228,41 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
226228

227229
if (this.ghostDragEnabled) {
228230
const rect = this.element.nativeElement.getBoundingClientRect();
229-
const clone = this.element.nativeElement.cloneNode(true);
230-
this.renderer.setStyle(clone, 'visibility', 'hidden');
231+
const clone = this.element.nativeElement.cloneNode(
232+
true
233+
) as HTMLElement;
234+
this.renderer.setStyle(
235+
this.element.nativeElement,
236+
'visibility',
237+
'hidden'
238+
);
231239
this.element.nativeElement.parentNode!.insertBefore(
232240
clone,
233-
this.element.nativeElement
241+
this.element.nativeElement.nextSibling
234242
);
243+
this.ghostElement = clone;
235244

236-
this.setElementStyles({
245+
this.setElementStyles(clone, {
237246
position: 'fixed',
238247
top: `${rect.top}px`,
239248
left: `${rect.left}px`,
240249
width: `${rect.width}px`,
241250
height: `${rect.height}px`,
242-
zIndex: '10'
251+
zIndex: '10',
252+
cursor: this.dragCursor
243253
});
244254

245255
dragEnd$.subscribe(() => {
246256
clone.parentElement!.removeChild(clone);
247-
this.setElementStyles({
248-
position: '',
249-
top: '',
250-
left: '',
251-
width: '',
252-
height: '',
253-
zIndex: ''
254-
});
257+
this.ghostElement = null;
258+
this.renderer.setStyle(
259+
this.element.nativeElement,
260+
'visibility',
261+
''
262+
);
255263
});
256264
}
257265

258-
this.setCursor(this.dragCursor);
259-
260266
this.draggableHelper.currentDrag.next(currentDrag);
261267
});
262268

@@ -265,14 +271,6 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
265271
this.zone.run(() => {
266272
this.dragEnd.next({ x, y });
267273
});
268-
this.setCssTransform('');
269-
if (this.ghostDragEnabled) {
270-
this.renderer.setStyle(
271-
this.element.nativeElement,
272-
'pointerEvents',
273-
''
274-
);
275-
}
276274
this.renderer.removeClass(
277275
this.element.nativeElement,
278276
this.dragActiveClass
@@ -304,14 +302,17 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
304302
this.zone.run(() => {
305303
this.dragging.next({ x, y });
306304
});
307-
if (this.ghostDragEnabled) {
308-
this.renderer.setStyle(
309-
this.element.nativeElement,
310-
'pointerEvents',
311-
'none'
312-
);
305+
if (this.ghostElement) {
306+
const transform = `translate(${x}px, ${y}px)`;
307+
this.setElementStyles(this.ghostElement, {
308+
pointerEvents: 'none',
309+
transform,
310+
'-webkit-transform': transform,
311+
'-ms-transform': transform,
312+
'-moz-transform': transform,
313+
'-o-transform': transform
314+
});
313315
}
314-
this.setCssTransform(`translate(${x}px, ${y}px)`);
315316
currentDrag.next({
316317
clientX,
317318
clientY,
@@ -475,25 +476,6 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
475476
this.setCursor('');
476477
}
477478

478-
private setCssTransform(value: string): void {
479-
if (this.ghostDragEnabled) {
480-
const transformAttributes = [
481-
'transform',
482-
'-webkit-transform',
483-
'-ms-transform',
484-
'-moz-transform',
485-
'-o-transform'
486-
];
487-
transformAttributes.forEach(transformAttribute => {
488-
this.renderer.setStyle(
489-
this.element.nativeElement,
490-
transformAttribute,
491-
value
492-
);
493-
});
494-
}
495-
}
496-
497479
private canDrag(): boolean {
498480
return this.dragAxis.x || this.dragAxis.y;
499481
}
@@ -509,9 +491,12 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
509491
});
510492
}
511493

512-
private setElementStyles(styles: { [key: string]: string }) {
494+
private setElementStyles(
495+
element: HTMLElement,
496+
styles: { [key: string]: string }
497+
) {
513498
Object.keys(styles).forEach(key => {
514-
this.renderer.setStyle(this.element.nativeElement, key, styles[key]);
499+
this.renderer.setStyle(element, key, styles[key]);
515500
});
516501
}
517502
}

test/draggable.directive.spec.ts

+31-51
Original file line numberDiff line numberDiff line change
@@ -75,37 +75,37 @@ describe('draggable directive', () => {
7575
x: 2,
7676
y: 0
7777
});
78-
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
78+
const ghostElement = draggableElement.nextSibling as HTMLElement;
79+
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
7980
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 8 });
8081
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
8182
x: 2,
8283
y: -2
8384
});
84-
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
85+
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
8586
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
8687
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
8788
x: 2,
8889
y: -2
8990
});
90-
expect(draggableElement.style.transform).not.to.be.ok;
9191
});
9292

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

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

107107
it('should end the pointerMove observable when the component is destroyed', () => {
108-
const complete: sinon.SinonSpy = sinon.spy();
108+
const complete = sinon.spy();
109109
fixture.componentInstance.draggable.pointerMove.subscribe({ complete });
110110
fixture.destroy();
111111
expect(complete).to.have.been.calledOnce;
@@ -122,7 +122,8 @@ describe('draggable directive', () => {
122122
x: 0,
123123
y: 2
124124
});
125-
expect(draggableElement.style.transform).to.equal('translate(0px, 2px)');
125+
const ghostElement = draggableElement.nextSibling as HTMLElement;
126+
expect(ghostElement.style.transform).to.equal('translate(0px, 2px)');
126127
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
127128
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
128129
x: 0,
@@ -141,7 +142,8 @@ describe('draggable directive', () => {
141142
x: 2,
142143
y: 0
143144
});
144-
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
145+
const ghostElement = draggableElement.nextSibling as HTMLElement;
146+
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
145147
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
146148
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
147149
x: 2,
@@ -168,7 +170,6 @@ describe('draggable directive', () => {
168170
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 12 });
169171
expect(fixture.componentInstance.dragStart).not.to.have.been.called;
170172
expect(fixture.componentInstance.dragging).not.to.have.been.called;
171-
expect(draggableElement.style.transform).not.to.be.ok;
172173
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 12 });
173174
expect(fixture.componentInstance.dragEnd).not.to.have.been.called;
174175
});
@@ -301,19 +302,19 @@ describe('draggable directive', () => {
301302
x: 2,
302303
y: 0
303304
});
304-
expect(draggableElement.style.transform).not.to.ok;
305+
expect(draggableElement.nextSibling).not.to.ok;
305306
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 8 });
306307
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
307308
x: 2,
308309
y: -2
309310
});
310-
expect(draggableElement.style.transform).not.to.ok;
311+
expect(draggableElement.nextSibling).not.to.ok;
311312
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
312313
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
313314
x: 2,
314315
y: -2
315316
});
316-
expect(draggableElement.style.transform).not.to.ok;
317+
expect(draggableElement.nextSibling).not.to.ok;
317318
});
318319

319320
it('should auto set the mouse cursor to the move icon on hover', () => {
@@ -445,23 +446,23 @@ describe('draggable directive', () => {
445446
x: 2,
446447
y: 0
447448
});
448-
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
449+
const ghostElement = draggableElement.nextSibling as HTMLElement;
450+
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
449451
triggerDomEvent('touchmove', draggableElement, {
450452
targetTouches: [{ clientX: 7, clientY: 8 }]
451453
});
452454
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
453455
x: 2,
454456
y: -2
455457
});
456-
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
458+
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
457459
triggerDomEvent('touchend', draggableElement, {
458460
changedTouches: [{ clientX: 7, clientY: 8 }]
459461
});
460462
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
461463
x: 2,
462464
y: -2
463465
});
464-
expect(draggableElement.style.transform).not.to.be.ok;
465466
});
466467

467468
it('should work use the touch cancel event to end the drag', () => {
@@ -481,23 +482,23 @@ describe('draggable directive', () => {
481482
x: 2,
482483
y: 0
483484
});
484-
expect(draggableElement.style.transform).to.equal('translate(2px, 0px)');
485+
const ghostElement = draggableElement.nextSibling as HTMLElement;
486+
expect(ghostElement.style.transform).to.equal('translate(2px, 0px)');
485487
triggerDomEvent('touchmove', draggableElement, {
486488
targetTouches: [{ clientX: 7, clientY: 8 }]
487489
});
488490
expect(fixture.componentInstance.dragging).to.have.been.calledWith({
489491
x: 2,
490492
y: -2
491493
});
492-
expect(draggableElement.style.transform).to.equal('translate(2px, -2px)');
494+
expect(ghostElement.style.transform).to.equal('translate(2px, -2px)');
493495
triggerDomEvent('touchcancel', draggableElement, {
494496
changedTouches: [{ clientX: 7, clientY: 8 }]
495497
});
496498
expect(fixture.componentInstance.dragEnd).to.have.been.calledWith({
497499
x: 2,
498500
y: -2
499501
});
500-
expect(draggableElement.style.transform).not.to.be.ok;
501502
});
502503

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

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

570-
it('should create a clone of the element and make the host static', () => {
570+
it('should create a clone of the element', () => {
571571
const draggableElement: HTMLElement =
572572
fixture.componentInstance.draggable.element.nativeElement;
573-
expect(
574-
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
575-
'mwldraggable'
576-
)
577-
).to.be.false;
578573
triggerDomEvent('mousedown', draggableElement, { clientX: 5, clientY: 10 });
579574
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 10 });
580-
expect(draggableElement.style.position).to.equal('fixed');
581-
expect(draggableElement.style.top).to.be.ok;
582-
expect(draggableElement.style.left).to.be.ok;
583-
expect(draggableElement.style.width).to.be.ok;
584-
expect(draggableElement.style.height).to.be.ok;
585-
expect(draggableElement.style.zIndex).to.equal('10');
586-
expect(draggableElement.previousSibling).to.be.ok;
587-
expect(
588-
(draggableElement.previousElementSibling as HTMLElement).style.visibility
589-
).to.equal('hidden');
590-
expect(
591-
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
592-
'mwldraggable'
593-
)
594-
).to.be.true;
575+
const ghostElement = draggableElement.nextSibling as HTMLElement;
576+
expect(ghostElement.style.position).to.equal('fixed');
577+
expect(ghostElement.style.top).to.be.ok;
578+
expect(ghostElement.style.left).to.be.ok;
579+
expect(ghostElement.style.width).to.be.ok;
580+
expect(ghostElement.style.height).to.be.ok;
581+
expect(ghostElement.style.zIndex).to.equal('10');
582+
expect(draggableElement.style.visibility).to.equal('hidden');
583+
expect((ghostElement as HTMLElement).hasAttribute('mwldraggable')).to.be
584+
.true;
595585
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
596-
expect(draggableElement.style.position).not.to.be.ok;
597-
expect(draggableElement.style.top).not.to.be.ok;
598-
expect(draggableElement.style.left).not.to.be.ok;
599-
expect(draggableElement.style.width).not.to.be.ok;
600-
expect(draggableElement.style.height).not.to.be.ok;
601-
expect(draggableElement.style.zIndex).not.to.be.ok;
602-
expect(
603-
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
604-
'mwldraggable'
605-
)
606-
).to.be.false;
586+
expect(draggableElement.style.visibility).not.to.be.ok;
607587
});
608588

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

0 commit comments

Comments
 (0)