Skip to content

Commit 6e0a1a3

Browse files
committed
fix: allow dragging the element inside a scrollable container
BREAKING CHANGE: A cloned element is now created when the element is being dragged, and the dragged element is set to be positioned fixed. This may break some apps in some edge cases. Fixes #25
1 parent d0093e8 commit 6e0a1a3

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

src/draggable.directive.ts

+38-4
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
135135
* @hidden
136136
*/
137137
constructor(
138-
public element: ElementRef,
138+
public element: ElementRef<HTMLElement>,
139139
private renderer: Renderer2,
140140
private draggableHelper: DraggableHelper,
141141
private zone: NgZone
@@ -206,19 +206,47 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
206206
.pipe(takeUntil(merge(this.pointerUp, this.pointerDown)))
207207
.pipe(share());
208208

209-
pointerMove.pipe(take(1)).subscribe(() => {
209+
const dragStart$ = pointerMove.pipe(take(1)).pipe(share());
210+
const dragEnd$ = pointerMove.pipe(takeLast(1)).pipe(share());
211+
212+
dragStart$.subscribe(() => {
210213
pointerDownEvent.event.preventDefault();
211214

212215
this.zone.run(() => {
213216
this.dragStart.next({ x: 0, y: 0 });
214217
});
215218

219+
if (this.ghostDragEnabled) {
220+
const rect = this.element.nativeElement.getBoundingClientRect();
221+
const clone = this.element.nativeElement.cloneNode(true);
222+
this.renderer.setStyle(clone, 'visibility', 'hidden');
223+
this.element.nativeElement.parentNode!.insertBefore(
224+
clone,
225+
this.element.nativeElement
226+
);
227+
228+
this.setElementStyles({
229+
position: 'fixed',
230+
top: `${rect.top}px`,
231+
left: `${rect.left}px`
232+
});
233+
234+
dragEnd$.subscribe(() => {
235+
clone.parentElement!.removeChild(clone);
236+
this.setElementStyles({
237+
position: '',
238+
top: '',
239+
left: ''
240+
});
241+
});
242+
}
243+
216244
this.setCursor(this.dragCursor);
217245

218246
this.draggableHelper.currentDrag.next(currentDrag);
219247
});
220248

221-
pointerMove.pipe(takeLast(1)).subscribe(({ x, y }) => {
249+
dragEnd$.subscribe(({ x, y }) => {
222250
this.zone.run(() => {
223251
this.dragEnd.next({ x, y });
224252
});
@@ -272,7 +300,7 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
272300
}
273301

274302
ngOnChanges(changes: SimpleChanges): void {
275-
if (changes['dragAxis']) {
303+
if (changes.dragAxis) {
276304
this.checkEventListeners();
277305
}
278306
}
@@ -459,4 +487,10 @@ export class DraggableDirective implements OnInit, OnChanges, OnDestroy {
459487
delete (this as any).eventListenerSubscriptions[type];
460488
});
461489
}
490+
491+
private setElementStyles(styles: { [key: string]: string }) {
492+
Object.keys(styles).forEach(key => {
493+
this.renderer.setStyle(this.element.nativeElement, key, styles[key]);
494+
});
495+
}
462496
}

test/draggable.directive.spec.ts

+33
Original file line numberDiff line numberDiff line change
@@ -564,4 +564,37 @@ describe('draggable directive', () => {
564564
fixture.componentInstance.dragEnd
565565
);
566566
});
567+
568+
it('should create a clone of the element and make the host static', () => {
569+
const draggableElement: HTMLElement =
570+
fixture.componentInstance.draggable.element.nativeElement;
571+
expect(
572+
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
573+
'mwldraggable'
574+
)
575+
).to.be.false;
576+
triggerDomEvent('mousedown', draggableElement, { clientX: 5, clientY: 10 });
577+
triggerDomEvent('mousemove', draggableElement, { clientX: 7, clientY: 10 });
578+
expect(draggableElement.style.position).to.equal('fixed');
579+
expect(draggableElement.style.top).to.be.ok;
580+
expect(draggableElement.style.left).to.be.ok;
581+
expect(draggableElement.previousSibling).to.be.ok;
582+
expect(
583+
(draggableElement.previousElementSibling as HTMLElement).style.visibility
584+
).to.equal('hidden');
585+
expect(
586+
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
587+
'mwldraggable'
588+
)
589+
).to.be.true;
590+
triggerDomEvent('mouseup', draggableElement, { clientX: 7, clientY: 8 });
591+
expect(draggableElement.style.position).not.to.be.ok;
592+
expect(draggableElement.style.top).not.to.be.ok;
593+
expect(draggableElement.style.left).not.to.be.ok;
594+
expect(
595+
(draggableElement.previousElementSibling as HTMLElement).hasAttribute(
596+
'mwldraggable'
597+
)
598+
).to.be.false;
599+
});
567600
});

0 commit comments

Comments
 (0)