Skip to content

Commit 9459f5b

Browse files
crisbetojelbourn
authored andcommitted
fix(drag-drop): auto scrolling not working if list uses scroll snapping (#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 #18162.
1 parent 29eec77 commit 9459f5b

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)