diff --git a/src/cdk/drag-drop/drag.spec.ts b/src/cdk/drag-drop/drag.spec.ts index eda825c4fb9..da479455c57 100644 --- a/src/cdk/drag-drop/drag.spec.ts +++ b/src/cdk/drag-drop/drag.spec.ts @@ -569,6 +569,45 @@ describe('CdkDrag', () => { .toEqual(['One', 'Two', 'Zero', 'Three']); })); + it('should dispatch the correct `dropped` event in RTL horizontal drop zone', fakeAsync(() => { + const fixture = createComponent(DraggableInHorizontalDropZone, [{ + provide: Directionality, + useValue: ({value: 'rtl'}) + }]); + + fixture.nativeElement.setAttribute('dir', 'rtl'); + fixture.detectChanges(); + const dragItems = fixture.componentInstance.dragItems; + + expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim())) + .toEqual(['Zero', 'One', 'Two', 'Three']); + + const firstItem = dragItems.first; + const thirdItemRect = dragItems.toArray()[2].element.nativeElement.getBoundingClientRect(); + + dragElementViaMouse(fixture, firstItem.element.nativeElement, + thirdItemRect.right - 1, thirdItemRect.top + 1); + flush(); + fixture.detectChanges(); + + expect(fixture.componentInstance.droppedSpy).toHaveBeenCalledTimes(1); + + const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0]; + + // Assert the event like this, rather than `toHaveBeenCalledWith`, because Jasmine will + // go into an infinite loop trying to stringify the event, if the test fails. + expect(event).toEqual({ + previousIndex: 0, + currentIndex: 2, + item: firstItem, + container: fixture.componentInstance.dropInstance, + previousContainer: fixture.componentInstance.dropInstance + }); + + expect(dragItems.map(drag => drag.element.nativeElement.textContent!.trim())) + .toEqual(['One', 'Two', 'Zero', 'Three']); + })); + it('should not move items in a horizontal list if pointer is too far away', fakeAsync(() => { const fixture = createComponent(DraggableInHorizontalDropZone); fixture.detectChanges(); diff --git a/src/cdk/drag-drop/drop.ts b/src/cdk/drag-drop/drop.ts index a6b5307b9a7..722c9bbc341 100644 --- a/src/cdk/drag-drop/drop.ts +++ b/src/cdk/drag-drop/drop.ts @@ -20,7 +20,9 @@ import { Output, QueryList, ViewEncapsulation, + Optional, } from '@angular/core'; +import {Directionality} from '@angular/cdk/bidi'; import {CdkDrag} from './drag'; import {DragDropRegistry} from './drag-drop-registry'; import {CdkDragDrop, CdkDragEnter, CdkDragExit} from './drag-events'; @@ -103,7 +105,8 @@ export class CdkDrop implements OnInit, OnDestroy { constructor( public element: ElementRef, - private _dragDropRegistry: DragDropRegistry>) {} + private _dragDropRegistry: DragDropRegistry>, + @Optional() private _dir?: Directionality) {} ngOnInit() { this._dragDropRegistry.registerDropContainer(this); @@ -217,9 +220,17 @@ export class CdkDrop implements OnInit, OnDestroy { * @param item Item whose index should be determined. */ getItemIndex(item: CdkDrag): number { - return this._dragging ? - findIndex(this._positionCache.items, currentItem => currentItem.drag === item) : - this._draggables.toArray().indexOf(item); + if (!this._dragging) { + return this._draggables.toArray().indexOf(item); + } + + // Items are sorted always by top/left in the cache, however they flow differently in RTL. + // The rest of the logic still stands no matter what orientation we're in, however + // we need to invert the array when determining the index. + const items = this.orientation === 'horizontal' && this._dir && this._dir.value === 'rtl' ? + this._positionCache.items.slice().reverse() : this._positionCache.items; + + return findIndex(items, currentItem => currentItem.drag === item); } /**