Skip to content

Commit 0244663

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

File tree

2 files changed

+44
-20
lines changed

2 files changed

+44
-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
@@ -68,9 +68,9 @@ describe('DragDropRegistry', () => {
6868
const subscription = registry.pointerMove.subscribe(spy);
6969

7070
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
71-
dispatchMouseEvent(document, 'mousemove');
71+
const event = dispatchMouseEvent(document, 'mousemove');
7272

73-
expect(spy).toHaveBeenCalled();
73+
expect(spy).toHaveBeenCalledWith(event);
7474

7575
subscription.unsubscribe();
7676
});
@@ -81,9 +81,9 @@ describe('DragDropRegistry', () => {
8181

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

86-
expect(spy).toHaveBeenCalled();
86+
expect(spy).toHaveBeenCalledWith(event);
8787

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

108108
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
109-
dispatchMouseEvent(document, 'mouseup');
109+
const event = dispatchMouseEvent(document, 'mouseup');
110110

111-
expect(spy).toHaveBeenCalled();
111+
expect(spy).toHaveBeenCalledWith(event);
112112

113113
subscription.unsubscribe();
114114
});
@@ -119,9 +119,22 @@ describe('DragDropRegistry', () => {
119119

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

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

126139
subscription.unsubscribe();
127140
});

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

Lines changed: 23 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', {
47-
handler: PointerEventHandler,
48-
options?: AddEventListenerOptions | boolean
49-
}>();
46+
private _globalListeners =
47+
new Map<'touchmove' | 'mousemove' | 'touchend' | 'mouseup' | 'touchcancel', {
48+
handler: PointerEventHandler,
49+
options?: AddEventListenerOptions | boolean
50+
}>();
5051

5152
/**
5253
* Emits the `touchmove` or `mousemove` events that are dispatched
@@ -119,6 +120,10 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
119120
const isTouchEvent = event.type.startsWith('touch');
120121
const moveEvent = isTouchEvent ? 'touchmove' : 'mousemove';
121122
const upEvent = isTouchEvent ? 'touchend' : 'mouseup';
123+
const upConfig = {
124+
handler: (e: TouchEvent | MouseEvent) => this.pointerUp.next(e),
125+
options: activeCapturingEventOptions
126+
};
122127

123128
// We need to disable the native interactions on the entire body, because
124129
// the user can start marking text if they drag too far in Safari.
@@ -132,15 +137,21 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
132137
handler: e => this.pointerMove.next(e),
133138
options: activeCapturingEventOptions
134139
})
135-
.set(upEvent, {
136-
handler: e => this.pointerUp.next(e),
137-
options: true
138-
})
139-
.forEach((config, name) => {
140-
this._ngZone.runOutsideAngular(() => {
141-
this._document.addEventListener(name, config.handler, config.options);
142-
});
140+
.set(upEvent, upConfig);
141+
142+
if (isTouchEvent) {
143+
// Treat `touchcancel` events the same as `touchend`. `touchcancel` will fire for cases
144+
// like an OS-level event interrupting the touch sequence or the user putting too many
145+
// finger on the screen at the same time.
146+
this._globalListeners.set('touchcancel', upConfig);
147+
}
148+
149+
this._globalListeners.forEach((config, name) => {
150+
this._ngZone.runOutsideAngular(() => {
151+
this._document.addEventListener(name, config.handler, config.options);
143152
});
153+
});
154+
144155
}
145156
}
146157

0 commit comments

Comments
 (0)