Skip to content

Commit aa1d01d

Browse files
authored
fix(cdk/drag-drop): constrainPosition now working well with boundary (#27730)
* fix(cdk/drag-drop): constrainPosition now working well with boundary * Add tests for x/y lock while using constrainPosition
1 parent 09433fb commit aa1d01d

File tree

5 files changed

+157
-10
lines changed

5 files changed

+157
-10
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,32 @@ describe('CdkDrag', () => {
560560
expect(dragElement.style.transform).toBe('translate3d(150px, 0px, 0px)');
561561
}));
562562

563+
it('should be able to lock dragging along the x axis while using constrainPosition', fakeAsync(() => {
564+
const fixture = createComponent(StandaloneDraggable);
565+
fixture.detectChanges();
566+
fixture.componentInstance.dragInstance.lockAxis = 'x';
567+
fixture.componentInstance.dragInstance.constrainPosition = (
568+
{x, y}: Point,
569+
_dragRef: DragRef,
570+
_dimensions: ClientRect,
571+
pickup: Point,
572+
) => {
573+
x -= pickup.x;
574+
y -= pickup.y;
575+
return {x, y};
576+
};
577+
578+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
579+
580+
expect(dragElement.style.transform).toBeFalsy();
581+
582+
dragElementViaMouse(fixture, dragElement, 50, 100);
583+
expect(dragElement.style.transform).toBe('translate3d(50px, 0px, 0px)');
584+
585+
dragElementViaMouse(fixture, dragElement, 100, 200);
586+
expect(dragElement.style.transform).toBe('translate3d(150px, 0px, 0px)');
587+
}));
588+
563589
it('should be able to lock dragging along the y axis', fakeAsync(() => {
564590
const fixture = createComponent(StandaloneDraggable);
565591
fixture.detectChanges();
@@ -576,6 +602,33 @@ describe('CdkDrag', () => {
576602
expect(dragElement.style.transform).toBe('translate3d(0px, 300px, 0px)');
577603
}));
578604

605+
it('should be able to lock dragging along the y axis while using constrainPosition', fakeAsync(() => {
606+
const fixture = createComponent(StandaloneDraggable);
607+
fixture.detectChanges();
608+
609+
fixture.componentInstance.dragInstance.lockAxis = 'y';
610+
fixture.componentInstance.dragInstance.constrainPosition = (
611+
{x, y}: Point,
612+
_dragRef: DragRef,
613+
_dimensions: ClientRect,
614+
pickup: Point,
615+
) => {
616+
x -= pickup.x;
617+
y -= pickup.y;
618+
return {x, y};
619+
};
620+
621+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
622+
623+
expect(dragElement.style.transform).toBeFalsy();
624+
625+
dragElementViaMouse(fixture, dragElement, 50, 100);
626+
expect(dragElement.style.transform).toBe('translate3d(0px, 100px, 0px)');
627+
628+
dragElementViaMouse(fixture, dragElement, 100, 200);
629+
expect(dragElement.style.transform).toBe('translate3d(0px, 300px, 0px)');
630+
}));
631+
579632
it('should add a class while an element is being dragged', fakeAsync(() => {
580633
const fixture = createComponent(StandaloneDraggable);
581634
fixture.detectChanges();
@@ -946,6 +999,29 @@ describe('CdkDrag', () => {
946999
expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)');
9471000
}));
9481001

1002+
it('should allow for dragging to be constrained to an element while using constrainPosition', fakeAsync(() => {
1003+
const fixture = createComponent(StandaloneDraggable);
1004+
fixture.componentInstance.boundary = '.wrapper';
1005+
fixture.detectChanges();
1006+
1007+
fixture.componentInstance.dragInstance.constrainPosition = (
1008+
{x, y}: Point,
1009+
_dragRef: DragRef,
1010+
_dimensions: ClientRect,
1011+
pickup: Point,
1012+
) => {
1013+
x -= pickup.x;
1014+
y -= pickup.y;
1015+
return {x, y};
1016+
};
1017+
1018+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
1019+
1020+
expect(dragElement.style.transform).toBeFalsy();
1021+
dragElementViaMouse(fixture, dragElement, 300, 300);
1022+
expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)');
1023+
}));
1024+
9491025
it('should be able to pass in a DOM node as the boundary', fakeAsync(() => {
9501026
const fixture = createComponent(StandaloneDraggable);
9511027
fixture.componentInstance.boundary = fixture.nativeElement.querySelector('.wrapper');

src/cdk/drag-drop/drag-ref.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,13 +1238,22 @@ export class DragRef<T = any> {
12381238
: point;
12391239

12401240
if (this.lockAxis === 'x' || dropContainerLock === 'x') {
1241-
y = this._pickupPositionOnPage.y;
1241+
y =
1242+
this._pickupPositionOnPage.y -
1243+
(this.constrainPosition ? this._pickupPositionInElement.y : 0);
12421244
} else if (this.lockAxis === 'y' || dropContainerLock === 'y') {
1243-
x = this._pickupPositionOnPage.x;
1245+
x =
1246+
this._pickupPositionOnPage.x -
1247+
(this.constrainPosition ? this._pickupPositionInElement.x : 0);
12441248
}
12451249

12461250
if (this._boundaryRect) {
1247-
const {x: pickupX, y: pickupY} = this._pickupPositionInElement;
1251+
// If not using a custom constrain we need to account for the pickup position in the element
1252+
// otherwise we do not need to do this, as it has already been accounted for
1253+
const {x: pickupX, y: pickupY} = !this.constrainPosition
1254+
? this._pickupPositionInElement
1255+
: {x: 0, y: 0};
1256+
12481257
const boundaryRect = this._boundaryRect;
12491258
const {width: previewWidth, height: previewHeight} = this._getPreviewRect();
12501259
const minY = boundaryRect.top + pickupY;

src/dev-app/drag-drop/drag-drop-demo.html

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<div cdkDropListGroup>
22
<div class="demo-list">
3-
43
<h2>To do</h2>
54
<div
65
cdkDropList
76
(cdkDropListDropped)="drop($event)"
87
[cdkDropListLockAxis]="axisLock"
9-
[cdkDropListData]="todo">
8+
[cdkDropListData]="todo"
9+
>
1010
<div *ngFor="let item of todo" cdkDrag>
1111
{{item}}
1212
<mat-icon cdkDragHandle svgIcon="dnd-move"></mat-icon>
@@ -20,7 +20,8 @@ <h2>Done</h2>
2020
cdkDropList
2121
(cdkDropListDropped)="drop($event)"
2222
[cdkDropListLockAxis]="axisLock"
23-
[cdkDropListData]="done">
23+
[cdkDropListData]="done"
24+
>
2425
<div *ngFor="let item of done" cdkDrag>
2526
{{item}}
2627
<mat-icon cdkDragHandle svgIcon="dnd-move"></mat-icon>
@@ -37,7 +38,8 @@ <h2>Ages</h2>
3738
cdkDropListOrientation="horizontal"
3839
(cdkDropListDropped)="drop($event)"
3940
[cdkDropListLockAxis]="axisLock"
40-
[cdkDropListData]="ages">
41+
[cdkDropListData]="ages"
42+
>
4143
<div *ngFor="let item of ages" cdkDrag>
4244
{{item}}
4345
<mat-icon cdkDragHandle svgIcon="dnd-move"></mat-icon>
@@ -52,7 +54,8 @@ <h2>Preferred Ages</h2>
5254
cdkDropListOrientation="horizontal"
5355
(cdkDropListDropped)="drop($event)"
5456
[cdkDropListLockAxis]="axisLock"
55-
[cdkDropListData]="preferredAges">
57+
[cdkDropListData]="preferredAges"
58+
>
5659
<div *ngFor="let item of preferredAges" cdkDrag>
5760
{{item}}
5861
<mat-icon cdkDragHandle svgIcon="dnd-move"></mat-icon>
@@ -63,7 +66,45 @@ <h2>Preferred Ages</h2>
6366

6467
<div class="demo-list">
6568
<h2>Free dragging</h2>
66-
<div cdkDrag class="demo-free-draggable" [cdkDragLockAxis]="axisLock" [cdkDragStartDelay]="dragStartDelay">Drag me around</div>
69+
<div
70+
cdkDrag
71+
class="demo-free-draggable"
72+
[cdkDragLockAxis]="axisLock"
73+
[cdkDragStartDelay]="dragStartDelay"
74+
>
75+
Drag me around
76+
</div>
77+
</div>
78+
79+
<div>
80+
<h2>Drag with box boundary</h2>
81+
<div class="demo-constrain-box" id="constrain-box-1">
82+
<div
83+
cdkDrag
84+
class="demo-free-draggable"
85+
[cdkDragLockAxis]="axisLock"
86+
[cdkDragStartDelay]="dragStartDelay"
87+
cdkDragBoundary="#constrain-box-1"
88+
>
89+
Drag me around
90+
</div>
91+
</div>
92+
</div>
93+
94+
<div>
95+
<h2>Drag with box boundary and custom constrain</h2>
96+
<div class="demo-constrain-box" id="constrain-box-2">
97+
<div
98+
cdkDrag
99+
class="demo-free-draggable"
100+
[cdkDragLockAxis]="axisLock"
101+
[cdkDragStartDelay]="dragStartDelay"
102+
cdkDragBoundary="#constrain-box-2"
103+
[cdkDragConstrainPosition]="constrainPosition"
104+
>
105+
Drag me around
106+
</div>
107+
</div>
67108
</div>
68109

69110
<div>
@@ -90,6 +131,6 @@ <h2>Axis locking</h2>
90131
<h2>Drag start delay</h2>
91132

92133
<mat-form-field>
93-
<input matInput placeholder="Drag start delay" value="0" [(ngModel)]="dragStartDelay">
134+
<input matInput placeholder="Drag start delay" value="0" [(ngModel)]="dragStartDelay" />
94135
</mat-form-field>
95136
</div>

src/dev-app/drag-drop/drag-drop-demo.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,9 @@ pre {
9999
justify-content: center;
100100
align-items: center;
101101
}
102+
103+
.demo-constrain-box {
104+
width: 600px;
105+
height: 400px;
106+
border: 1px solid black;
107+
}

src/dev-app/drag-drop/drag-drop-demo.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {
1414
DragDropModule,
1515
moveItemInArray,
1616
transferArrayItem,
17+
Point,
18+
DragRef,
19+
CdkDrag,
1720
} from '@angular/cdk/drag-drop';
1821
import {CommonModule} from '@angular/common';
1922
import {FormsModule} from '@angular/forms';
@@ -74,4 +77,16 @@ export class DragAndDropDemo {
7477
);
7578
}
7679
}
80+
81+
constrainPosition(
82+
{x, y}: Point,
83+
_dragRef: DragRef,
84+
_dimensions: ClientRect,
85+
pickup: Point,
86+
): Point {
87+
// Just returning the original top left corner to not modify position
88+
x -= pickup.x;
89+
y -= pickup.y;
90+
return {x, y};
91+
}
7792
}

0 commit comments

Comments
 (0)