mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(drag-drop): prevent text selection while dragging on Safari (#14405)
Fixes being able to select text while dragging an item on Safari. Fixes #14403.
This commit is contained in:
committed by
vivian-hu
parent
53d2b58311
commit
220e3885f2
@@ -190,6 +190,16 @@ describe('DragDropRegistry', () => {
|
||||
expect(dispatchFakeEvent(document, 'wheel').defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
it('should not prevent the default `selectstart` actions when nothing is being dragged', () => {
|
||||
expect(dispatchFakeEvent(document, 'selectstart').defaultPrevented).toBe(false);
|
||||
});
|
||||
|
||||
it('should prevent the default `selectstart` action when an item is being dragged', () => {
|
||||
registry.startDragging(testComponent.dragItems.first, createMouseEvent('mousedown'));
|
||||
expect(dispatchFakeEvent(document, 'selectstart').defaultPrevented).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -17,9 +17,6 @@ const activeCapturingEventOptions = normalizePassiveListenerOptions({
|
||||
capture: true
|
||||
});
|
||||
|
||||
/** Handler for a pointer event callback. */
|
||||
type PointerEventHandler = (event: TouchEvent | MouseEvent) => void;
|
||||
|
||||
/**
|
||||
* Service that keeps track of all the drag item and drop container
|
||||
* instances, and manages global event listeners on the `document`.
|
||||
@@ -42,8 +39,8 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
|
||||
private _activeDragInstances = new Set<I>();
|
||||
|
||||
/** Keeps track of the event listeners that we've bound to the `document`. */
|
||||
private _globalListeners = new Map<'touchmove' | 'mousemove' | 'touchend' | 'mouseup' | 'wheel', {
|
||||
handler: PointerEventHandler,
|
||||
private _globalListeners = new Map<string, {
|
||||
handler: (event: Event) => void,
|
||||
options?: AddEventListenerOptions | boolean
|
||||
}>();
|
||||
|
||||
@@ -87,7 +84,7 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
|
||||
this._ngZone.runOutsideAngular(() => {
|
||||
// The event handler has to be explicitly active,
|
||||
// because newer browsers make it passive by default.
|
||||
this._document.addEventListener('touchmove', this._preventScrollListener,
|
||||
this._document.addEventListener('touchmove', this._preventDefaultWhileDragging,
|
||||
activeCapturingEventOptions);
|
||||
});
|
||||
}
|
||||
@@ -104,7 +101,7 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
|
||||
this.stopDragging(drag);
|
||||
|
||||
if (this._dragInstances.size === 0) {
|
||||
this._document.removeEventListener('touchmove', this._preventScrollListener,
|
||||
this._document.removeEventListener('touchmove', this._preventDefaultWhileDragging,
|
||||
activeCapturingEventOptions);
|
||||
}
|
||||
}
|
||||
@@ -127,19 +124,27 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
|
||||
// use `preventDefault` to prevent the page from scrolling while the user is dragging.
|
||||
this._globalListeners
|
||||
.set(moveEvent, {
|
||||
handler: e => this.pointerMove.next(e),
|
||||
handler: (e: Event) => this.pointerMove.next(e as TouchEvent | MouseEvent),
|
||||
options: activeCapturingEventOptions
|
||||
})
|
||||
.set(upEvent, {
|
||||
handler: e => this.pointerUp.next(e),
|
||||
handler: (e: Event) => this.pointerUp.next(e as TouchEvent | MouseEvent),
|
||||
options: true
|
||||
})
|
||||
// Preventing the default action on `mousemove` isn't enough to disable text selection
|
||||
// on Safari so we need to prevent the selection event as well. Alternatively this can
|
||||
// be done by setting `user-select: none` on the `body`, however it has causes a style
|
||||
// recalculation which can be expensive on pages with a lot of elements.
|
||||
.set('selectstart', {
|
||||
handler: this._preventDefaultWhileDragging,
|
||||
options: activeCapturingEventOptions
|
||||
});
|
||||
|
||||
// TODO(crisbeto): prevent mouse wheel scrolling while
|
||||
// dragging until we've set up proper scroll handling.
|
||||
if (!isTouchEvent) {
|
||||
this._globalListeners.set('wheel', {
|
||||
handler: this._preventScrollListener,
|
||||
handler: this._preventDefaultWhileDragging,
|
||||
options: activeCapturingEventOptions
|
||||
});
|
||||
}
|
||||
@@ -184,9 +189,10 @@ export class DragDropRegistry<I, C extends {id: string}> implements OnDestroy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener used to prevent `touchmove` and `wheel` events while the element is being dragged.
|
||||
* Event listener that will prevent the default browser action while the user is dragging.
|
||||
* @param event Event whose default action should be prevented.
|
||||
*/
|
||||
private _preventScrollListener = (event: Event) => {
|
||||
private _preventDefaultWhileDragging = (event: Event) => {
|
||||
if (this._activeDragInstances.size) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@@ -720,6 +720,8 @@ export class DragRef<T = any> {
|
||||
zIndex: '1000'
|
||||
});
|
||||
|
||||
toggleNativeDragInteractions(preview, false);
|
||||
|
||||
preview.classList.add('cdk-drag-preview');
|
||||
preview.setAttribute('dir', this._dir ? this._dir.value : 'ltr');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user