mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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`.
This commit is contained in:
committed by
mmalerba
parent
03527c632b
commit
91b171cd13
@@ -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 {
|
||||
<div
|
||||
*ngFor="let item of items"
|
||||
cdkDrag
|
||||
[data]="item"
|
||||
[cdkDragData]="item"
|
||||
style="width: 100%; height: ${ITEM_HEIGHT}px; background: red;">{{item}}</div>
|
||||
</cdk-drop>
|
||||
`
|
||||
@@ -1239,7 +1267,7 @@ export class DraggableInDropZoneWithCustomPlaceholder {
|
||||
[data]="todo"
|
||||
[connectedTo]="[doneZone]"
|
||||
(dropped)="droppedSpy($event)">
|
||||
<div *ngFor="let item of todo" cdkDrag>{{item}}</div>
|
||||
<div [cdkDragData]="item" *ngFor="let item of todo" cdkDrag>{{item}}</div>
|
||||
</cdk-drop>
|
||||
|
||||
<cdk-drop
|
||||
@@ -1247,7 +1275,7 @@ export class DraggableInDropZoneWithCustomPlaceholder {
|
||||
[data]="done"
|
||||
[connectedTo]="[todoZone]"
|
||||
(dropped)="droppedSpy($event)">
|
||||
<div *ngFor="let item of done" cdkDrag>{{item}}</div>
|
||||
<div [cdkDragData]="item" *ngFor="let item of done" cdkDrag>{{item}}</div>
|
||||
</cdk-drop>
|
||||
`
|
||||
})
|
||||
|
||||
@@ -123,7 +123,7 @@ export class CdkDrag<T = any> 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<T = any> 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(() => {
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface CdkDropContainer<T = any> {
|
||||
getItemIndex(item: CdkDrag): number;
|
||||
_sortItem(item: CdkDrag, xOffset: number, yOffset: number): void;
|
||||
_draggables: QueryList<CdkDrag>;
|
||||
_getSiblingContainerFromPosition(x: number, y: number): CdkDropContainer | null;
|
||||
_getSiblingContainerFromPosition(item: CdkDrag, x: number, y: number): CdkDropContainer | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,6 +72,12 @@ export class CdkDrop<T = any> 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<CdkDragDrop<T, any>> = new EventEmitter<CdkDragDrop<T, any>>();
|
||||
|
||||
@@ -252,16 +258,17 @@ export class CdkDrop<T = any> 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. */
|
||||
|
||||
Reference in New Issue
Block a user