Skip to content

Commit 7ff42a8

Browse files
committed
feat(drag-drop): allow for dragging sequence to be delayed
Adds the `cdkDragStartDelay` input that allows for the dragging sequence to be delayed by X amount of milliseconds. Fixes #13908.
1 parent e7b0e40 commit 7ff42a8

File tree

4 files changed

+52
-1
lines changed

4 files changed

+52
-1
lines changed

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,36 @@ describe('CdkDrag', () => {
680680
}).toThrowError(/^cdkDrag must be attached to an element node/);
681681
}));
682682

683+
it('should allow for the dragging sequence to be delayed', fakeAsync(() => {
684+
// We can't use Jasmine's `clock` because Zone.js interferes with it.
685+
spyOn(Date, 'now').and.callFake(() => currentTime);
686+
let currentTime = 0;
687+
688+
const fixture = createComponent(StandaloneDraggable);
689+
fixture.componentInstance.dragStartDelay = 1000;
690+
fixture.detectChanges();
691+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
692+
693+
expect(dragElement.style.transform).toBeFalsy('Expected element not to be moved by default.');
694+
695+
startDraggingViaMouse(fixture, dragElement);
696+
currentTime += 750;
697+
dispatchMouseEvent(document, 'mousemove', 50, 100);
698+
fixture.detectChanges();
699+
700+
expect(dragElement.style.transform)
701+
.toBeFalsy('Expected element not to be moved if the drag timeout has not passed.');
702+
703+
// The first `mousemove` here starts the sequence and the second one moves the element.
704+
currentTime += 500;
705+
dispatchMouseEvent(document, 'mousemove', 50, 100);
706+
dispatchMouseEvent(document, 'mousemove', 50, 100);
707+
fixture.detectChanges();
708+
709+
expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)',
710+
'Expected element to be dragged after all the time has passed.');
711+
}));
712+
683713
});
684714

685715
describe('draggable with a handle', () => {
@@ -2804,6 +2834,7 @@ describe('CdkDrag', () => {
28042834
<div
28052835
cdkDrag
28062836
[cdkDragBoundary]="boundarySelector"
2837+
[cdkDragStartDelay]="dragStartDelay"
28072838
(cdkDragStarted)="startedSpy($event)"
28082839
(cdkDragReleased)="releasedSpy($event)"
28092840
(cdkDragEnded)="endedSpy($event)"
@@ -2819,6 +2850,7 @@ class StandaloneDraggable {
28192850
endedSpy = jasmine.createSpy('ended spy');
28202851
releasedSpy = jasmine.createSpy('released spy');
28212852
boundarySelector: string;
2853+
dragStartDelay: number;
28222854
}
28232855

28242856
@Component({

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
108108
*/
109109
@Input('cdkDragBoundary') boundaryElementSelector: string;
110110

111+
/**
112+
* Amount of milliseconds to wait after the user has put their
113+
* pointer down before starting to drag the element.
114+
*/
115+
@Input('cdkDragStartDelay') dragStartDelay: number = 0;
116+
111117
/** Whether starting to drag this element is disabled. */
112118
@Input('cdkDragDisabled')
113119
get disabled(): boolean {
@@ -300,6 +306,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
300306

301307
ref.disabled = this.disabled;
302308
ref.lockAxis = this.lockAxis;
309+
ref.dragStartDelay = this.dragStartDelay;
303310
ref
304311
.withBoundaryElement(this._getBoundaryElement())
305312
.withPlaceholderTemplate(placeholder)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ export class DragRef<T = any> {
167167
*/
168168
private _lastTouchEventTime: number;
169169

170+
/** Time at which the last dragging sequence was started. */
171+
private _dragStartTime: number;
172+
170173
/** Cached reference to the boundary element. */
171174
private _boundaryElement: HTMLElement | null = null;
172175

@@ -200,6 +203,12 @@ export class DragRef<T = any> {
200203
/** Axis along which dragging is locked. */
201204
lockAxis: 'x' | 'y';
202205

206+
/**
207+
* Amount of milliseconds to wait after the user has put their
208+
* pointer down before starting to drag the element.
209+
*/
210+
dragStartDelay: number = 0;
211+
203212
/** Whether starting to drag this element is disabled. */
204213
get disabled(): boolean {
205214
return this._disabled || !!(this._dropContainer && this._dropContainer.disabled);
@@ -474,12 +483,13 @@ export class DragRef<T = any> {
474483
const pointerPosition = this._getPointerPositionOnPage(event);
475484
const distanceX = Math.abs(pointerPosition.x - this._pickupPositionOnPage.x);
476485
const distanceY = Math.abs(pointerPosition.y - this._pickupPositionOnPage.y);
486+
const isOverThreshold = distanceX + distanceY >= this._config.dragStartThreshold;
477487

478488
// Only start dragging after the user has moved more than the minimum distance in either
479489
// direction. Note that this is preferrable over doing something like `skip(minimumDistance)`
480490
// in the `pointerMove` subscription, because we're not guaranteed to have one move event
481491
// per pixel of movement (e.g. if the user moves their pointer quickly).
482-
if (distanceX + distanceY >= this._config.dragStartThreshold) {
492+
if (isOverThreshold && (Date.now() >= this._dragStartTime + (this.dragStartDelay || 0))) {
483493
this._hasStartedDragging = true;
484494
this._ngZone.run(() => this._startDragSequence(event));
485495
}
@@ -675,6 +685,7 @@ export class DragRef<T = any> {
675685
const pointerPosition = this._pickupPositionOnPage = this._getPointerPositionOnPage(event);
676686
this._pointerDirectionDelta = {x: 0, y: 0};
677687
this._pointerPositionAtLastDirectionChange = {x: pointerPosition.x, y: pointerPosition.y};
688+
this._dragStartTime = Date.now();
678689
this._dragDropRegistry.startDragging(this, event);
679690
}
680691

tools/public_api_guard/cdk/drag-drop.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDes
1414
boundaryElementSelector: string;
1515
data: T;
1616
disabled: boolean;
17+
dragStartDelay: number;
1718
dropContainer: CdkDropList;
1819
dropped: EventEmitter<CdkDragDrop<any>>;
1920
element: ElementRef<HTMLElement>;

0 commit comments

Comments
 (0)