Skip to content

Commit 1735502

Browse files
committed
fix(drag-drop): stop dragging sequence on touchcancel
Adds handling for the `touchcancel` event to the the `DragDropRegistry`.
1 parent ba31c72 commit 1735502

File tree

2 files changed

+40
-20
lines changed

2 files changed

+40
-20
lines changed

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

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ describe('DragDropRegistry', () => {
6969
const subscription = registry.pointerMove.subscribe(spy);
7070

7171
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
72-
dispatchMouseEvent(document, 'mousemove');
72+
const event = dispatchMouseEvent(document, 'mousemove');
7373

74-
expect(spy).toHaveBeenCalled();
74+
expect(spy).toHaveBeenCalledWith(event);
7575

7676
subscription.unsubscribe();
7777
});
@@ -82,9 +82,9 @@ describe('DragDropRegistry', () => {
8282

8383
registry.startDragging(testComponent.dragItems.first,
8484
createTouchEvent('touchstart') as TouchEvent);
85-
dispatchTouchEvent(document, 'touchmove');
85+
const event = dispatchTouchEvent(document, 'touchmove');
8686

87-
expect(spy).toHaveBeenCalled();
87+
expect(spy).toHaveBeenCalledWith(event);
8888

8989
subscription.unsubscribe();
9090
});
@@ -107,9 +107,9 @@ describe('DragDropRegistry', () => {
107107
const subscription = registry.pointerUp.subscribe(spy);
108108

109109
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
110-
dispatchMouseEvent(document, 'mouseup');
110+
const event = dispatchMouseEvent(document, 'mouseup');
111111

112-
expect(spy).toHaveBeenCalled();
112+
expect(spy).toHaveBeenCalledWith(event);
113113

114114
subscription.unsubscribe();
115115
});
@@ -120,9 +120,22 @@ describe('DragDropRegistry', () => {
120120

121121
registry.startDragging(testComponent.dragItems.first,
122122
createTouchEvent('touchstart') as TouchEvent);
123-
dispatchTouchEvent(document, 'touchend');
123+
const event = dispatchTouchEvent(document, 'touchend');
124124

125-
expect(spy).toHaveBeenCalled();
125+
expect(spy).toHaveBeenCalledWith(event);
126+
127+
subscription.unsubscribe();
128+
});
129+
130+
it('should dispatch `touchcancel` events if the drag was interrupted', () => {
131+
const spy = jasmine.createSpy('pointerUp spy');
132+
const subscription = registry.pointerUp.subscribe(spy);
133+
134+
registry.startDragging(testComponent.dragItems.first,
135+
createTouchEvent('touchstart') as TouchEvent);
136+
const event = dispatchTouchEvent(document, 'touchcancel');
137+
138+
expect(spy).toHaveBeenCalledWith(event);
126139

127140
subscription.unsubscribe();
128141
});

src/cdk/drag-drop/drag-drop-registry.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
4343
private _activeDragInstances = new Set<I>();
4444

4545
/** Keeps track of the event listeners that we've bound to the `document`. */
46-
private _globalListeners = new Map<'touchmove' | 'mousemove' | 'touchend' | 'mouseup' | 'wheel', {
47-
handler: PointerEventHandler,
48-
options?: AddEventListenerOptions | boolean
49-
}>();
46+
private _globalListeners =
47+
new Map<'touchmove' | 'mousemove' | 'touchend' | 'mouseup' | 'touchcancel' | 'wheel', {
48+
handler: PointerEventHandler,
49+
options?: AddEventListenerOptions | boolean
50+
}>();
5051

5152
/**
5253
* Emits the `touchmove` or `mousemove` events that are dispatched
@@ -122,6 +123,10 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
122123
const isTouchEvent = event.type.startsWith('touch');
123124
const moveEvent = isTouchEvent ? 'touchmove' : 'mousemove';
124125
const upEvent = isTouchEvent ? 'touchend' : 'mouseup';
126+
const upConfig = {
127+
handler: (e: TouchEvent | MouseEvent) => this.pointerUp.next(e),
128+
options: true
129+
};
125130

126131
// We need to disable the native interactions on the entire body, because
127132
// the user can start marking text if they drag too far in Safari.
@@ -135,14 +140,16 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
135140
handler: e => this.pointerMove.next(e),
136141
options: activeCapturingEventOptions
137142
})
138-
.set(upEvent, {
139-
handler: e => this.pointerUp.next(e),
140-
options: true
141-
});
142-
143-
// TODO(crisbeto): prevent mouse wheel scrolling while
144-
// dragging until we've set up proper scroll handling.
145-
if (!isTouchEvent) {
143+
.set(upEvent, upConfig);
144+
145+
if (isTouchEvent) {
146+
// Treat `touchcancel` events the same as `touchend`. `touchcancel` will fire for cases
147+
// like an OS-level event interrupting the touch sequence or the user putting too many
148+
// finger on the screen at the same time.
149+
this._globalListeners.set('touchcancel', upConfig);
150+
} else {
151+
// TODO(crisbeto): prevent mouse wheel scrolling while
152+
// dragging until we've set up proper scroll handling.
146153
this._globalListeners.set('wheel', {
147154
handler: this._preventScrollListener,
148155
options: activeCapturingEventOptions

0 commit comments

Comments
 (0)