feat(drag-drop): allow for dragging sequence to be delayed (#14732)

Adds the `cdkDragStartDelay` input that allows for the dragging sequence to be delayed by X amount of milliseconds.

Fixes #13908.
This commit is contained in:
Kristiyan Kostadinov
2019-02-22 16:04:40 +01:00
committed by Jeremy Elbourn
parent 5d3f5157ca
commit bcf278179b
4 changed files with 53 additions and 1 deletions
+32
View File
@@ -703,6 +703,36 @@ describe('CdkDrag', () => {
}).toThrowError(/^cdkDrag must be attached to an element node/);
}));
it('should allow for the dragging sequence to be delayed', fakeAsync(() => {
// We can't use Jasmine's `clock` because Zone.js interferes with it.
spyOn(Date, 'now').and.callFake(() => currentTime);
let currentTime = 0;
const fixture = createComponent(StandaloneDraggable);
fixture.componentInstance.dragStartDelay = 1000;
fixture.detectChanges();
const dragElement = fixture.componentInstance.dragElement.nativeElement;
expect(dragElement.style.transform).toBeFalsy('Expected element not to be moved by default.');
startDraggingViaMouse(fixture, dragElement);
currentTime += 750;
dispatchMouseEvent(document, 'mousemove', 50, 100);
fixture.detectChanges();
expect(dragElement.style.transform)
.toBeFalsy('Expected element not to be moved if the drag timeout has not passed.');
// The first `mousemove` here starts the sequence and the second one moves the element.
currentTime += 500;
dispatchMouseEvent(document, 'mousemove', 50, 100);
dispatchMouseEvent(document, 'mousemove', 50, 100);
fixture.detectChanges();
expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)',
'Expected element to be dragged after all the time has passed.');
}));
});
describe('draggable with a handle', () => {
@@ -2832,6 +2862,7 @@ describe('CdkDrag', () => {
<div
cdkDrag
[cdkDragBoundary]="boundarySelector"
[cdkDragStartDelay]="dragStartDelay"
(cdkDragStarted)="startedSpy($event)"
(cdkDragReleased)="releasedSpy($event)"
(cdkDragEnded)="endedSpy($event)"
@@ -2847,6 +2878,7 @@ class StandaloneDraggable {
endedSpy = jasmine.createSpy('ended spy');
releasedSpy = jasmine.createSpy('released spy');
boundarySelector: string;
dragStartDelay: number;
}
@Component({
+7
View File
@@ -110,6 +110,12 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
*/
@Input('cdkDragBoundary') boundaryElementSelector: string;
/**
* Amount of milliseconds to wait after the user has put their
* pointer down before starting to drag the element.
*/
@Input('cdkDragStartDelay') dragStartDelay: number = 0;
/** Whether starting to drag this element is disabled. */
@Input('cdkDragDisabled')
get disabled(): boolean {
@@ -303,6 +309,7 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
ref.disabled = this.disabled;
ref.lockAxis = this.lockAxis;
ref.dragStartDelay = this.dragStartDelay;
ref
.withBoundaryElement(this._getBoundaryElement())
.withPlaceholderTemplate(placeholder)
+12 -1
View File
@@ -167,6 +167,9 @@ export class DragRef<T = any> {
*/
private _lastTouchEventTime: number;
/** Time at which the last dragging sequence was started. */
private _dragStartTime: number;
/** Cached reference to the boundary element. */
private _boundaryElement: HTMLElement | null = null;
@@ -200,6 +203,12 @@ export class DragRef<T = any> {
/** Axis along which dragging is locked. */
lockAxis: 'x' | 'y';
/**
* Amount of milliseconds to wait after the user has put their
* pointer down before starting to drag the element.
*/
dragStartDelay: number = 0;
/** Whether starting to drag this element is disabled. */
get disabled(): boolean {
return this._disabled || !!(this._dropContainer && this._dropContainer.disabled);
@@ -474,12 +483,13 @@ export class DragRef<T = any> {
const pointerPosition = this._getPointerPositionOnPage(event);
const distanceX = Math.abs(pointerPosition.x - this._pickupPositionOnPage.x);
const distanceY = Math.abs(pointerPosition.y - this._pickupPositionOnPage.y);
const isOverThreshold = distanceX + distanceY >= this._config.dragStartThreshold;
// Only start dragging after the user has moved more than the minimum distance in either
// direction. Note that this is preferrable over doing something like `skip(minimumDistance)`
// in the `pointerMove` subscription, because we're not guaranteed to have one move event
// per pixel of movement (e.g. if the user moves their pointer quickly).
if (distanceX + distanceY >= this._config.dragStartThreshold) {
if (isOverThreshold && (Date.now() >= this._dragStartTime + (this.dragStartDelay || 0))) {
this._hasStartedDragging = true;
this._ngZone.run(() => this._startDragSequence(event));
}
@@ -675,6 +685,7 @@ export class DragRef<T = any> {
const pointerPosition = this._pickupPositionOnPage = this._getPointerPositionOnPage(event);
this._pointerDirectionDelta = {x: 0, y: 0};
this._pointerPositionAtLastDirectionChange = {x: pointerPosition.x, y: pointerPosition.y};
this._dragStartTime = Date.now();
this._dragDropRegistry.startDragging(this, event);
}
+2
View File
@@ -14,6 +14,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDes
boundaryElementSelector: string;
data: T;
disabled: boolean;
dragStartDelay: number;
dropContainer: CdkDropList;
dropped: EventEmitter<CdkDragDrop<any>>;
element: ElementRef<HTMLElement>;
@@ -204,6 +205,7 @@ export declare class DragRef<T = any> {
beforeStarted: Subject<void>;
data: T;
disabled: boolean;
dragStartDelay: number;
dropped: Subject<{
previousIndex: number;
currentIndex: number;