Skip to content

Commit 26c73e0

Browse files
crisbetovivian-hu-zz
authored andcommitted
refactor(drag-drop): add sorted event (#13887)
Adds a new `cdkDropListSorted` event that emits the item's previous index and current index as it's being sorted in a list. Fixes #13863.
1 parent df5d18f commit 26c73e0

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,15 @@ export interface CdkDragMove<T = any> {
7171
*/
7272
delta: {x: -1 | 0 | 1, y: -1 | 0 | 1};
7373
}
74+
75+
/** Event emitted when the user swaps the position of two drag items. */
76+
export interface CdkDragSortEvent<T = any, I = T> {
77+
/** Index from which the item was sorted previously. */
78+
previousIndex: number;
79+
/** Index that the item is currently in. */
80+
currentIndex: number;
81+
/** Container that the item belongs to. */
82+
container: CdkDropListContainer<T>;
83+
/** Item that is being sorted. */
84+
item: CdkDrag<I>;
85+
}

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,36 @@ describe('CdkDrag', () => {
698698
.toEqual(['One', 'Two', 'Zero', 'Three']);
699699
}));
700700

701+
it('should dispatch the `sorted` event as an item is being sorted', fakeAsync(() => {
702+
const fixture = createComponent(DraggableInDropZone);
703+
fixture.detectChanges();
704+
705+
const items = fixture.componentInstance.dragItems.map(item => item.element.nativeElement);
706+
const draggedItem = items[0];
707+
const {top, left} = draggedItem.getBoundingClientRect();
708+
709+
startDraggingViaMouse(fixture, draggedItem, left, top);
710+
711+
// Drag over each item one-by-one going downwards.
712+
for (let i = 1; i < items.length; i++) {
713+
const elementRect = items[i].getBoundingClientRect();
714+
715+
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.top + 5);
716+
fixture.detectChanges();
717+
718+
expect(fixture.componentInstance.sortedSpy.calls.mostRecent().args[0]).toEqual({
719+
previousIndex: i - 1,
720+
currentIndex: i,
721+
item: fixture.componentInstance.dragItems.first,
722+
container: fixture.componentInstance.dropInstance
723+
});
724+
}
725+
726+
dispatchMouseEvent(document, 'mouseup');
727+
fixture.detectChanges();
728+
flush();
729+
}));
730+
701731
it('should not move items in a vertical list if the pointer is too far away', fakeAsync(() => {
702732
const fixture = createComponent(DraggableInDropZone);
703733
fixture.detectChanges();
@@ -2192,6 +2222,7 @@ const DROP_ZONE_FIXTURE_TEMPLATE = `
21922222
style="width: 100px; background: pink;"
21932223
[id]="dropZoneId"
21942224
[cdkDropListData]="items"
2225+
(cdkDropListSorted)="sortedSpy($event)"
21952226
(cdkDropListDropped)="droppedSpy($event)">
21962227
<div
21972228
*ngFor="let item of items"
@@ -2214,6 +2245,7 @@ class DraggableInDropZone {
22142245
{value: 'Three', height: ITEM_HEIGHT, margin: 0}
22152246
];
22162247
dropZoneId = 'items';
2248+
sortedSpy = jasmine.createSpy('sorted spy');
22172249
droppedSpy = jasmine.createSpy('dropped spy').and.callFake((event: CdkDragDrop<string[]>) => {
22182250
moveItemInArray(this.items, event.previousIndex, event.currentIndex);
22192251
});

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
import {Directionality} from '@angular/cdk/bidi';
2525
import {CdkDrag} from './drag';
2626
import {DragDropRegistry} from './drag-drop-registry';
27-
import {CdkDragDrop, CdkDragEnter, CdkDragExit} from './drag-events';
27+
import {CdkDragDrop, CdkDragEnter, CdkDragExit, CdkDragSortEvent} from './drag-events';
2828
import {moveItemInArray} from './drag-utils';
2929
import {CDK_DROP_LIST_CONTAINER} from './drop-list-container';
3030
import {CdkDropListGroup} from './drop-list-group';
@@ -140,6 +140,10 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
140140
@Output('cdkDropListExited')
141141
exited: EventEmitter<CdkDragExit<T>> = new EventEmitter<CdkDragExit<T>>();
142142

143+
/** Emits as the user is swapping items while actively dragging. */
144+
@Output('cdkDropListSorted')
145+
sorted: EventEmitter<CdkDragSortEvent<T>> = new EventEmitter<CdkDragSortEvent<T>>();
146+
143147
constructor(
144148
public element: ElementRef<HTMLElement>,
145149
private _dragDropRegistry: DragDropRegistry<CdkDrag, CdkDropList<T>>,
@@ -321,6 +325,13 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
321325
// Shuffle the array in place.
322326
moveItemInArray(siblings, currentIndex, newIndex);
323327

328+
this.sorted.emit({
329+
previousIndex: currentIndex,
330+
currentIndex: newIndex,
331+
container: this,
332+
item
333+
});
334+
324335
siblings.forEach((sibling, index) => {
325336
// Don't do anything if the position hasn't changed.
326337
if (oldOrder[index] === sibling) {

0 commit comments

Comments
 (0)