Skip to content

Commit 2dd7ec7

Browse files
crisbetoyifange
authored andcommitted
fix(drag-drop): auto scrolling not working if list uses scroll snapping (angular#18294)
When scrolling a drop list we increment `scrollTop` or `scrollLeft`, but if the list has scroll snapping the browser will only scroll it according to the snap points. These changes reset the scroll snapping while the user is dragging. Fixes angular#18162.
1 parent da8951f commit 2dd7ec7

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3560,6 +3560,57 @@ describe('CdkDrag', () => {
35603560
expect(list.lockAxis).toBe('y');
35613561
}));
35623562

3563+
it('should disable scroll snapping while the user is dragging', fakeAsync(() => {
3564+
const fixture = createComponent(DraggableInDropZone);
3565+
fixture.detectChanges();
3566+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
3567+
const styles: any = fixture.componentInstance.dropInstance.element.nativeElement.style;
3568+
3569+
// This test only applies to browsers that support scroll snapping.
3570+
if (!('scrollSnapType' in styles) && !('msScrollSnapType' in styles)) {
3571+
return;
3572+
}
3573+
3574+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBeFalsy();
3575+
3576+
startDraggingViaMouse(fixture, item);
3577+
3578+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBe('none');
3579+
3580+
dispatchMouseEvent(document, 'mouseup');
3581+
fixture.detectChanges();
3582+
flush();
3583+
fixture.detectChanges();
3584+
3585+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBeFalsy();
3586+
}));
3587+
3588+
it('should restore the previous inline scroll snap value', fakeAsync(() => {
3589+
const fixture = createComponent(DraggableInDropZone);
3590+
fixture.detectChanges();
3591+
const item = fixture.componentInstance.dragItems.toArray()[1].element.nativeElement;
3592+
const styles: any = fixture.componentInstance.dropInstance.element.nativeElement.style;
3593+
3594+
// This test only applies to browsers that support scroll snapping.
3595+
if (!('scrollSnapType' in styles) && !('msScrollSnapType' in styles)) {
3596+
return;
3597+
}
3598+
3599+
styles.scrollSnapType = styles.msScrollSnapType = 'block';
3600+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBe('block');
3601+
3602+
startDraggingViaMouse(fixture, item);
3603+
3604+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBe('none');
3605+
3606+
dispatchMouseEvent(document, 'mouseup');
3607+
fixture.detectChanges();
3608+
flush();
3609+
fixture.detectChanges();
3610+
3611+
expect(styles.scrollSnapType || styles.msScrollSnapType).toBe('block');
3612+
}));
3613+
35633614
});
35643615

35653616
describe('in a connected drop container', () => {

src/cdk/drag-drop/drop-list-ref.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ export class DropListRef<T = any> {
198198
/** Elements that can be scrolled while the user is dragging. */
199199
private _scrollableElements: HTMLElement[];
200200

201+
/** Initial value for the element's `scroll-snap-type` style. */
202+
private _initialScrollSnap: string;
203+
201204
constructor(
202205
element: ElementRef<HTMLElement> | HTMLElement,
203206
private _dragDropRegistry: DragDropRegistry<DragRef, DropListRef>,
@@ -233,8 +236,15 @@ export class DropListRef<T = any> {
233236

234237
/** Starts dragging an item. */
235238
start(): void {
239+
const styles = coerceElement(this.element).style;
236240
this.beforeStarted.next();
237241
this._isDragging = true;
242+
243+
// We need to disable scroll snapping while the user is dragging, because it breaks automatic
244+
// scrolling. The browser seems to round the value based on the snapping points which means
245+
// that we can't increment/decrement the scroll position.
246+
this._initialScrollSnap = styles.msScrollSnapType || (styles as any).scrollSnapType || '';
247+
(styles as any).scrollSnapType = styles.msScrollSnapType = 'none';
238248
this._cacheItems();
239249
this._siblings.forEach(sibling => sibling._startReceiving(this));
240250
this._viewportScrollSubscription.unsubscribe();
@@ -597,6 +607,9 @@ export class DropListRef<T = any> {
597607
private _reset() {
598608
this._isDragging = false;
599609

610+
const styles = coerceElement(this.element).style;
611+
(styles as any).scrollSnapType = styles.msScrollSnapType = this._initialScrollSnap;
612+
600613
// TODO(crisbeto): may have to wait for the animations to finish.
601614
this._activeDraggables.forEach(item => item.getRootElement().style.transform = '');
602615
this._siblings.forEach(sibling => sibling._stopReceiving(this));

0 commit comments

Comments
 (0)