diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index a77582994059..5b94227eeede 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -5838,6 +5838,33 @@ describe('CdkDrag', () => { }), ); + it('should set the receiving class when the list is wrapped in an OnPush component', fakeAsync(() => { + const fixture = createComponent(ConnectedDropListsInOnPush, undefined, undefined, [ + DraggableInOnPushDropZone, + ]); + fixture.detectChanges(); + + const dropZones = Array.from( + fixture.nativeElement.querySelectorAll('.cdk-drop-list'), + ); + const item = dropZones[0].querySelector('.cdk-drag')!; + + expect(dropZones.every(c => !c.classList.contains('cdk-drop-list-receiving'))) + .withContext('Expected neither of the containers to have the class.') + .toBe(true); + + startDraggingViaMouse(fixture, item); + fixture.detectChanges(); + + expect(dropZones[0].classList) + .withContext('Expected source container not to have the receiving class.') + .not.toContain('cdk-drop-list-receiving'); + + expect(dropZones[1].classList) + .withContext('Expected target container to have the receiving class.') + .toContain('cdk-drop-list-receiving'); + })); + it( 'should be able to move the item over an intermediate container before ' + 'dropping it into the final one', @@ -6763,11 +6790,22 @@ class DraggableInDropZone implements AfterViewInit { } @Component({ + selector: 'draggable-in-on-push-zone', template: DROP_ZONE_FIXTURE_TEMPLATE, changeDetection: ChangeDetectionStrategy.OnPush, }) class DraggableInOnPushDropZone extends DraggableInDropZone {} +@Component({ + template: ` +
+ + +
+ `, +}) +class ConnectedDropListsInOnPush {} + @Component({ template: DROP_ZONE_FIXTURE_TEMPLATE, diff --git a/src/cdk/drag-drop/directives/drop-list.ts b/src/cdk/drag-drop/directives/drop-list.ts index 357f1c692534..6b3844933a87 100644 --- a/src/cdk/drag-drop/directives/drop-list.ts +++ b/src/cdk/drag-drop/directives/drop-list.ts @@ -35,7 +35,7 @@ import {DropListRef} from '../drop-list-ref'; import {DragRef} from '../drag-ref'; import {DragDrop} from '../drag-drop'; import {DropListOrientation, DragAxis, DragDropConfig, CDK_DRAG_CONFIG} from './config'; -import {Subject} from 'rxjs'; +import {merge, Subject} from 'rxjs'; import {startWith, takeUntil} from 'rxjs/operators'; import {assertElementNode} from './assertions'; @@ -375,6 +375,10 @@ export class CdkDropList implements OnDestroy { // detection and we're not guaranteed for something else to have triggered it. this._changeDetectorRef.markForCheck(); }); + + merge(ref.receivingStarted, ref.receivingStopped).subscribe(() => + this._changeDetectorRef.markForCheck(), + ); } /** Assigns the default input values based on a provided config object. */ diff --git a/src/cdk/drag-drop/drop-list-ref.ts b/src/cdk/drag-drop/drop-list-ref.ts index 085c5c2012b7..23c5b3c6edae 100644 --- a/src/cdk/drag-drop/drop-list-ref.ts +++ b/src/cdk/drag-drop/drop-list-ref.ts @@ -130,6 +130,19 @@ export class DropListRef { item: DragRef; }>(); + /** Emits when a dragging sequence is started in a list connected to the current one. */ + readonly receivingStarted = new Subject<{ + receiver: DropListRef; + initiator: DropListRef; + items: DragRef[]; + }>(); + + /** Emits when a dragging sequence is stopped from a list connected to the current one. */ + readonly receivingStopped = new Subject<{ + receiver: DropListRef; + initiator: DropListRef; + }>(); + /** Arbitrary data that can be attached to the drop list. */ data: T; @@ -207,6 +220,8 @@ export class DropListRef { this.exited.complete(); this.dropped.complete(); this.sorted.complete(); + this.receivingStarted.complete(); + this.receivingStopped.complete(); this._activeSiblings.clear(); this._scrollNode = null!; this._parentPositions.clear(); @@ -637,6 +652,11 @@ export class DropListRef { activeSiblings.add(sibling); this._cacheParentPositions(); this._listenToScrollEvents(); + this.receivingStarted.next({ + initiator: sibling, + receiver: this, + items, + }); } } @@ -647,6 +667,7 @@ export class DropListRef { _stopReceiving(sibling: DropListRef) { this._activeSiblings.delete(sibling); this._viewportScrollSubscription.unsubscribe(); + this.receivingStopped.next({initiator: sibling, receiver: this}); } /** diff --git a/tools/public_api_guard/cdk/drag-drop.md b/tools/public_api_guard/cdk/drag-drop.md index 6d3426946205..dbb41960c603 100644 --- a/tools/public_api_guard/cdk/drag-drop.md +++ b/tools/public_api_guard/cdk/drag-drop.md @@ -494,6 +494,15 @@ export class DropListRef { _isOverContainer(x: number, y: number): boolean; isReceiving(): boolean; lockAxis: 'x' | 'y'; + readonly receivingStarted: Subject<{ + receiver: DropListRef; + initiator: DropListRef; + items: DragRefInternal[]; + }>; + readonly receivingStopped: Subject<{ + receiver: DropListRef; + initiator: DropListRef; + }>; readonly sorted: Subject<{ previousIndex: number; currentIndex: number;