Skip to content

Commit 6d54d7f

Browse files
committed
feat(drag-drop): include dragged distance in events
Adds the distance that the user has dragged in the `ended`, `moved` and `drop` events. This makes cases like resize handles to be implemented more conveniently.
1 parent 03a9a39 commit 6d54d7f

File tree

7 files changed

+171
-34
lines changed

7 files changed

+171
-34
lines changed

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

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,31 @@ describe('CdkDrag', () => {
356356

357357
// Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will
358358
// go into an infinite loop trying to stringify the event, if the test fails.
359-
expect(event).toEqual({source: fixture.componentInstance.dragInstance});
359+
expect(event).toEqual({
360+
source: fixture.componentInstance.dragInstance,
361+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
362+
});
363+
}));
364+
365+
it('should include the drag distance in the ended event', fakeAsync(() => {
366+
const fixture = createComponent(StandaloneDraggable);
367+
fixture.detectChanges();
368+
369+
dragElementViaMouse(fixture, fixture.componentInstance.dragElement.nativeElement, 25, 30);
370+
let event = fixture.componentInstance.endedSpy.calls.mostRecent().args[0];
371+
372+
expect(event).toEqual({
373+
source: jasmine.anything(),
374+
distance: {x: 25, y: 30}
375+
});
376+
377+
dragElementViaMouse(fixture, fixture.componentInstance.dragElement.nativeElement, 40, 50);
378+
event = fixture.componentInstance.endedSpy.calls.mostRecent().args[0];
379+
380+
expect(event).toEqual({
381+
source: jasmine.anything(),
382+
distance: {x: 40, y: 50}
383+
});
360384
}));
361385

362386
it('should emit when the user is moving the drag element', () => {
@@ -844,6 +868,30 @@ describe('CdkDrag', () => {
844868
expect(dragElement.style.transform).toBe('translate3d(150px, 300px, 0px)');
845869
}));
846870

871+
it('should include the dragged distance as the user is dragging', fakeAsync(() => {
872+
const fixture = createComponent(StandaloneDraggable);
873+
fixture.detectChanges();
874+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
875+
const spy = jasmine.createSpy('moved spy');
876+
const subscription = fixture.componentInstance.dragInstance.moved.subscribe(spy);
877+
878+
startDraggingViaMouse(fixture, dragElement);
879+
880+
dispatchMouseEvent(document, 'mousemove', 50, 100);
881+
fixture.detectChanges();
882+
883+
let event = spy.calls.mostRecent().args[0];
884+
expect(event.distance).toEqual({x: 50, y: 100});
885+
886+
dispatchMouseEvent(document, 'mousemove', 75, 50);
887+
fixture.detectChanges();
888+
889+
event = spy.calls.mostRecent().args[0];
890+
expect(event.distance).toEqual({x: 75, y: 50});
891+
892+
subscription.unsubscribe();
893+
}));
894+
847895
});
848896

849897
describe('draggable with a handle', () => {
@@ -1149,7 +1197,8 @@ describe('CdkDrag', () => {
11491197
item: firstItem,
11501198
container: fixture.componentInstance.dropInstance,
11511199
previousContainer: fixture.componentInstance.dropInstance,
1152-
isPointerOverContainer: true
1200+
isPointerOverContainer: true,
1201+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
11531202
});
11541203

11551204
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -1176,6 +1225,24 @@ describe('CdkDrag', () => {
11761225
expect(event.isPointerOverContainer).toBe(true);
11771226
}));
11781227

1228+
it('should expose the drag distance when an item is dropped', fakeAsync(() => {
1229+
const fixture = createComponent(DraggableInDropZone);
1230+
fixture.detectChanges();
1231+
const dragItems = fixture.componentInstance.dragItems;
1232+
const firstItem = dragItems.first;
1233+
1234+
dragElementViaMouse(fixture, firstItem.element.nativeElement, 50, 60);
1235+
flush();
1236+
fixture.detectChanges();
1237+
1238+
expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1);
1239+
1240+
const event: CdkDragDrop<any> =
1241+
fixture.componentInstance.droppedSpy.calls.mostRecent().args[0];
1242+
1243+
expect(event.distance).toEqual({x: 50, y: 60});
1244+
}));
1245+
11791246
it('should expose whether an item was dropped outside of a container', fakeAsync(() => {
11801247
const fixture = createComponent(DraggableInDropZone);
11811248
fixture.detectChanges();
@@ -1256,7 +1323,8 @@ describe('CdkDrag', () => {
12561323
item: firstItem,
12571324
container: fixture.componentInstance.dropInstance,
12581325
previousContainer: fixture.componentInstance.dropInstance,
1259-
isPointerOverContainer: false
1326+
isPointerOverContainer: false,
1327+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
12601328
});
12611329

12621330
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -1314,7 +1382,8 @@ describe('CdkDrag', () => {
13141382
item: firstItem,
13151383
container: fixture.componentInstance.dropInstance,
13161384
previousContainer: fixture.componentInstance.dropInstance,
1317-
isPointerOverContainer: true
1385+
isPointerOverContainer: true,
1386+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
13181387
});
13191388

13201389
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -1354,7 +1423,8 @@ describe('CdkDrag', () => {
13541423
item: firstItem,
13551424
container: fixture.componentInstance.dropInstance,
13561425
previousContainer: fixture.componentInstance.dropInstance,
1357-
isPointerOverContainer: true
1426+
isPointerOverContainer: true,
1427+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
13581428
});
13591429

13601430
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -1390,7 +1460,8 @@ describe('CdkDrag', () => {
13901460
item: firstItem,
13911461
container: fixture.componentInstance.dropInstance,
13921462
previousContainer: fixture.componentInstance.dropInstance,
1393-
isPointerOverContainer: false
1463+
isPointerOverContainer: false,
1464+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
13941465
});
13951466

13961467
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -2508,7 +2579,8 @@ describe('CdkDrag', () => {
25082579
item: firstItem,
25092580
container: dropInstance,
25102581
previousContainer: dropInstance,
2511-
isPointerOverContainer: true
2582+
isPointerOverContainer: true,
2583+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
25122584
});
25132585

25142586
expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim()))
@@ -2543,7 +2615,8 @@ describe('CdkDrag', () => {
25432615
item,
25442616
container: fixture.componentInstance.dropInstances.toArray()[1],
25452617
previousContainer: fixture.componentInstance.dropInstances.first,
2546-
isPointerOverContainer: true
2618+
isPointerOverContainer: true,
2619+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
25472620
});
25482621
}));
25492622

@@ -2645,7 +2718,8 @@ describe('CdkDrag', () => {
26452718
item: groups[0][1],
26462719
container: dropInstances[1],
26472720
previousContainer: dropInstances[0],
2648-
isPointerOverContainer: true
2721+
isPointerOverContainer: true,
2722+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
26492723
});
26502724
}));
26512725

@@ -2675,7 +2749,8 @@ describe('CdkDrag', () => {
26752749
item: groups[0][1],
26762750
container: dropInstances[0],
26772751
previousContainer: dropInstances[0],
2678-
isPointerOverContainer: false
2752+
isPointerOverContainer: false,
2753+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
26792754
});
26802755
}));
26812756

@@ -2705,7 +2780,8 @@ describe('CdkDrag', () => {
27052780
item: groups[0][1],
27062781
container: dropInstances[0],
27072782
previousContainer: dropInstances[0],
2708-
isPointerOverContainer: false
2783+
isPointerOverContainer: false,
2784+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
27092785
});
27102786
}));
27112787

@@ -2828,7 +2904,8 @@ describe('CdkDrag', () => {
28282904
item: groups[0][1],
28292905
container: dropInstances[1],
28302906
previousContainer: dropInstances[0],
2831-
isPointerOverContainer: true
2907+
isPointerOverContainer: true,
2908+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
28322909
});
28332910
}));
28342911

@@ -2854,7 +2931,8 @@ describe('CdkDrag', () => {
28542931
item: groups[0][1],
28552932
container: dropInstances[1],
28562933
previousContainer: dropInstances[0],
2857-
isPointerOverContainer: true
2934+
isPointerOverContainer: true,
2935+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
28582936
});
28592937
}));
28602938

@@ -2885,7 +2963,8 @@ describe('CdkDrag', () => {
28852963
item: groups[0][1],
28862964
container: dropInstances[1],
28872965
previousContainer: dropInstances[0],
2888-
isPointerOverContainer: true
2966+
isPointerOverContainer: true,
2967+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
28892968
});
28902969
}));
28912970

@@ -2920,7 +2999,8 @@ describe('CdkDrag', () => {
29202999
item,
29213000
container: fixture.componentInstance.dropInstances.toArray()[1],
29223001
previousContainer: fixture.componentInstance.dropInstances.first,
2923-
isPointerOverContainer: true
3002+
isPointerOverContainer: true,
3003+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
29243004
});
29253005

29263006
expect(dropContainers[0].contains(item.element.nativeElement)).toBe(true,
@@ -3016,7 +3096,8 @@ describe('CdkDrag', () => {
30163096
item: groups[0][1],
30173097
container: dropInstances[0],
30183098
previousContainer: dropInstances[0],
3019-
isPointerOverContainer: false
3099+
isPointerOverContainer: false,
3100+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
30203101
});
30213102
}));
30223103

@@ -3136,7 +3217,8 @@ describe('CdkDrag', () => {
31363217
item: groups[0][1],
31373218
container: dropInstances[2],
31383219
previousContainer: dropInstances[0],
3139-
isPointerOverContainer: false
3220+
isPointerOverContainer: false,
3221+
distance: {x: jasmine.any(Number), y: jasmine.any(Number)}
31403222
}));
31413223

31423224
}));

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
170170
source: this,
171171
pointerPosition: movedEvent.pointerPosition,
172172
event: movedEvent.event,
173-
delta: movedEvent.delta
173+
delta: movedEvent.delta,
174+
distance: movedEvent.distance
174175
}))).subscribe(observer);
175176

176177
return () => {
@@ -344,8 +345,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
344345
this.released.emit({source: this});
345346
});
346347

347-
ref.ended.subscribe(() => {
348-
this.ended.emit({source: this});
348+
ref.ended.subscribe(event => {
349+
this.ended.emit({source: this, distance: event.distance});
349350

350351
// Since all of these events run outside of change detection,
351352
// we need to ensure that everything is marked correctly.
@@ -373,7 +374,8 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
373374
previousContainer: event.previousContainer.data,
374375
container: event.container.data,
375376
isPointerOverContainer: event.isPointerOverContainer,
376-
item: this
377+
item: this,
378+
distance: event.distance
377379
});
378380
});
379381
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,8 @@ export class CdkDropList<T = any> implements CdkDropListContainer, AfterContentI
341341
previousContainer: event.previousContainer.data,
342342
container: event.container.data,
343343
item: event.item.data,
344-
isPointerOverContainer: event.isPointerOverContainer
344+
isPointerOverContainer: event.isPointerOverContainer,
345+
distance: event.distance
345346
});
346347

347348
// Mark for check since all of these events run outside of change

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ export interface CdkDragRelease<T = any> {
2525
export interface CdkDragEnd<T = any> {
2626
/** Draggable that emitted the event. */
2727
source: CdkDrag<T>;
28+
/** Distance in pixels that the user has dragged since the drag sequence started. */
29+
distance: {x: number, y: number};
2830
}
2931

3032
/** Event emitted when the user moves an item into a new drop container. */
@@ -61,6 +63,8 @@ export interface CdkDragDrop<T, O = T> {
6163
previousContainer: CdkDropList<O>;
6264
/** Whether the user's pointer was over the container when the item was dropped. */
6365
isPointerOverContainer: boolean;
66+
/** Distance in pixels that the user has dragged since the drag sequence started. */
67+
distance: {x: number, y: number};
6468
}
6569

6670
/** Event emitted as the user is dragging a draggable item. */
@@ -71,6 +75,8 @@ export interface CdkDragMove<T = any> {
7175
pointerPosition: {x: number, y: number};
7276
/** Native event that is causing the dragging. */
7377
event: MouseEvent | TouchEvent;
78+
/** Distance in pixels that the user has dragged since the drag sequence started. */
79+
distance: {x: number, y: number};
7480
/**
7581
* Indicates the direction in which the user is dragging the element along each axis.
7682
* `1` means that the position is increasing (e.g. the user is moving to the right or downwards),

0 commit comments

Comments
 (0)