Skip to content

Commit ce567b8

Browse files
committed
refactor(drag-drop): add sorted event
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 8b2dc82 commit ce567b8

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
@@ -633,6 +633,36 @@ describe('CdkDrag', () => {
633633
.toEqual(['One', 'Two', 'Zero', 'Three']);
634634
}));
635635

636+
it('should dispatch the `sorted` event as an item is being sorted', fakeAsync(() => {
637+
const fixture = createComponent(DraggableInDropZone);
638+
fixture.detectChanges();
639+
640+
const items = fixture.componentInstance.dragItems.map(item => item.element.nativeElement);
641+
const draggedItem = items[0];
642+
const {top, left} = draggedItem.getBoundingClientRect();
643+
644+
startDraggingViaMouse(fixture, draggedItem, left, top);
645+
646+
// Drag over each item one-by-one going downwards.
647+
for (let i = 1; i < items.length; i++) {
648+
const elementRect = items[i].getBoundingClientRect();
649+
650+
dispatchMouseEvent(document, 'mousemove', elementRect.left, elementRect.top + 5);
651+
fixture.detectChanges();
652+
653+
expect(fixture.componentInstance.sortedSpy.calls.mostRecent().args[0]).toEqual({
654+
previousIndex: i - 1,
655+
currentIndex: i,
656+
item: fixture.componentInstance.dragItems.first,
657+
container: fixture.componentInstance.dropInstance
658+
});
659+
}
660+
661+
dispatchMouseEvent(document, 'mouseup');
662+
fixture.detectChanges();
663+
flush();
664+
}));
665+
636666
it('should not move items in a vertical list if the pointer is too far away', fakeAsync(() => {
637667
const fixture = createComponent(DraggableInDropZone);
638668
fixture.detectChanges();
@@ -2075,6 +2105,7 @@ const DROP_ZONE_FIXTURE_TEMPLATE = `
20752105
style="width: 100px; background: pink;"
20762106
[id]="dropZoneId"
20772107
[cdkDropListData]="items"
2108+
(cdkDropListSorted)="sortedSpy($event)"
20782109
(cdkDropListDropped)="droppedSpy($event)">
20792110
<div
20802111
*ngFor="let item of items"
@@ -2097,6 +2128,7 @@ class DraggableInDropZone {
20972128
{value: 'Three', height: ITEM_HEIGHT, margin: 0}
20982129
];
20992130
dropZoneId = 'items';
2131+
sortedSpy = jasmine.createSpy('sorted spy');
21002132
droppedSpy = jasmine.createSpy('dropped spy').and.callFake((event: CdkDragDrop<string[]>) => {
21012133
moveItemInArray(this.items, event.previousIndex, event.currentIndex);
21022134
});

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

@@ -139,6 +139,10 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
139139
@Output('cdkDropListExited')
140140
exited: EventEmitter<CdkDragExit<T>> = new EventEmitter<CdkDragExit<T>>();
141141

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

318+
this.sorted.emit({
319+
previousIndex: currentIndex,
320+
currentIndex: newIndex,
321+
container: this,
322+
item
323+
});
324+
314325
siblings.forEach((sibling, index) => {
315326
// Don't do anything if the position hasn't changed.
316327
if (oldOrder[index] === sibling) {

0 commit comments

Comments
 (0)