From 91b171cd1395b5f8c04f8e967b87737c88cca763 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 15 Aug 2018 23:41:54 +0200 Subject: [PATCH] feat(drag-drop): add function to determine whether an item is allowed to be transferred (#12673) Adds the `enterPredicate` function which allows consumers to determine whether a particular item is allowed to be moved into a drop container. This is more granular than excluding entire groups of items using `connectedTo`. --- src/cdk-experimental/drag-drop/drag.spec.ts | 34 +++++++++++++++++-- src/cdk-experimental/drag-drop/drag.ts | 4 +-- .../drag-drop/drop-container.ts | 2 +- src/cdk-experimental/drag-drop/drop.ts | 11 ++++-- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/cdk-experimental/drag-drop/drag.spec.ts b/src/cdk-experimental/drag-drop/drag.spec.ts index a8cf6b3bc2e..487a684e723 100644 --- a/src/cdk-experimental/drag-drop/drag.spec.ts +++ b/src/cdk-experimental/drag-drop/drag.spec.ts @@ -942,6 +942,34 @@ describe('CdkDrag', () => { }); })); + it('should not be able to transfer an item that does not match the `enterPredicate`', + fakeAsync(() => { + const fixture = createComponent(ConnectedDropZones); + + fixture.detectChanges(); + fixture.componentInstance.dropInstances.forEach(d => d.enterPredicate = () => false); + fixture.detectChanges(); + + const groups = fixture.componentInstance.groupedDragItems.slice(); + const element = groups[0][1].element.nativeElement; + const dropInstances = fixture.componentInstance.dropInstances.toArray(); + const targetRect = groups[1][2].element.nativeElement.getBoundingClientRect(); + + dragElementViaMouse(fixture, element, targetRect.left + 1, targetRect.top + 1); + flush(); + fixture.detectChanges(); + + const event = fixture.componentInstance.droppedSpy.calls.mostRecent().args[0]; + + expect(event).toBeTruthy(); + expect(event).toEqual({ + previousIndex: 1, + currentIndex: 1, + item: groups[0][1], + container: dropInstances[0], + previousContainer: dropInstances[0] + }); + })); it('should be able to start dragging after an item has been transferred', fakeAsync(() => { const fixture = createComponent(ConnectedDropZones); @@ -1129,7 +1157,7 @@ export class StandaloneDraggableWithMultipleHandles {
{{item}}
` @@ -1239,7 +1267,7 @@ export class DraggableInDropZoneWithCustomPlaceholder { [data]="todo" [connectedTo]="[doneZone]" (dropped)="droppedSpy($event)"> -
{{item}}
+
{{item}}
-
{{item}}
+
{{item}}
` }) diff --git a/src/cdk-experimental/drag-drop/drag.ts b/src/cdk-experimental/drag-drop/drag.ts index 2c665b866fe..ac4ddad852c 100644 --- a/src/cdk-experimental/drag-drop/drag.ts +++ b/src/cdk-experimental/drag-drop/drag.ts @@ -123,7 +123,7 @@ export class CdkDrag implements OnDestroy { @ContentChild(CdkDragPlaceholder) _placeholderTemplate: CdkDragPlaceholder; /** Arbitrary data to attach to this drag instance. */ - @Input() data: T; + @Input('cdkDragData') data: T; /** Locks the position of the dragged element along the specified axis. */ @Input('cdkDragLockAxis') lockAxis: 'x' | 'y'; @@ -364,7 +364,7 @@ export class CdkDrag implements OnDestroy { */ private _updateActiveDropContainer({x, y}) { // Drop container that draggable has been moved into. - const newContainer = this.dropContainer._getSiblingContainerFromPosition(x, y); + const newContainer = this.dropContainer._getSiblingContainerFromPosition(this, x, y); if (newContainer) { this._ngZone.run(() => { diff --git a/src/cdk-experimental/drag-drop/drop-container.ts b/src/cdk-experimental/drag-drop/drop-container.ts index 7030d0684c2..4bbed64e63d 100644 --- a/src/cdk-experimental/drag-drop/drop-container.ts +++ b/src/cdk-experimental/drag-drop/drop-container.ts @@ -54,7 +54,7 @@ export interface CdkDropContainer { getItemIndex(item: CdkDrag): number; _sortItem(item: CdkDrag, xOffset: number, yOffset: number): void; _draggables: QueryList; - _getSiblingContainerFromPosition(x: number, y: number): CdkDropContainer | null; + _getSiblingContainerFromPosition(item: CdkDrag, x: number, y: number): CdkDropContainer | null; } /** diff --git a/src/cdk-experimental/drag-drop/drop.ts b/src/cdk-experimental/drag-drop/drop.ts index d8fcc2c8025..613dc414c71 100644 --- a/src/cdk-experimental/drag-drop/drop.ts +++ b/src/cdk-experimental/drag-drop/drop.ts @@ -72,6 +72,12 @@ export class CdkDrop implements OnInit, OnDestroy { /** Locks the position of the draggable elements inside the container along the specified axis. */ @Input() lockAxis: 'x' | 'y'; + /** + * Function that is used to determine whether an item + * is allowed to be moved into a drop container. + */ + @Input() enterPredicate: (drag?: CdkDrag, drop?: CdkDrop) => boolean = () => true; + /** Emits when the user drops an item inside the container. */ @Output() dropped: EventEmitter> = new EventEmitter>(); @@ -252,16 +258,17 @@ export class CdkDrop implements OnInit, OnDestroy { /** * Figures out whether an item should be moved into a sibling * drop container, based on its current position. + * @param item Drag item that is being moved. * @param x Position of the item along the X axis. * @param y Position of the item along the Y axis. */ - _getSiblingContainerFromPosition(x: number, y: number): CdkDrop | null { + _getSiblingContainerFromPosition(item: CdkDrag, x: number, y: number): CdkDrop | null { const result = this._positionCache.siblings.find(({clientRect}) => { const {top, bottom, left, right} = clientRect; return y >= top && y <= bottom && x >= left && x <= right; }); - return result ? result.drop : null; + return result && result.drop.enterPredicate(item, this) ? result.drop : null; } /** Refreshes the position cache of the items and sibling containers. */