From aa1d01d1696360d25e18cb0328f56b9fca9d71c4 Mon Sep 17 00:00:00 2001 From: Mathias Jensen Date: Thu, 31 Aug 2023 18:00:09 +0200 Subject: [PATCH] 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 --- src/cdk/drag-drop/directives/drag.spec.ts | 76 +++++++++++++++++++++++ src/cdk/drag-drop/drag-ref.ts | 15 ++++- src/dev-app/drag-drop/drag-drop-demo.html | 55 +++++++++++++--- src/dev-app/drag-drop/drag-drop-demo.scss | 6 ++ src/dev-app/drag-drop/drag-drop-demo.ts | 15 +++++ 5 files changed, 157 insertions(+), 10 deletions(-) diff --git a/src/cdk/drag-drop/directives/drag.spec.ts b/src/cdk/drag-drop/directives/drag.spec.ts index 766a33e89630..b318dd989094 100644 --- a/src/cdk/drag-drop/directives/drag.spec.ts +++ b/src/cdk/drag-drop/directives/drag.spec.ts @@ -560,6 +560,32 @@ describe('CdkDrag', () => { expect(dragElement.style.transform).toBe('translate3d(150px, 0px, 0px)'); })); + it('should be able to lock dragging along the x axis while using constrainPosition', fakeAsync(() => { + const fixture = createComponent(StandaloneDraggable); + fixture.detectChanges(); + fixture.componentInstance.dragInstance.lockAxis = 'x'; + fixture.componentInstance.dragInstance.constrainPosition = ( + {x, y}: Point, + _dragRef: DragRef, + _dimensions: ClientRect, + pickup: Point, + ) => { + x -= pickup.x; + y -= pickup.y; + return {x, y}; + }; + + const dragElement = fixture.componentInstance.dragElement.nativeElement; + + expect(dragElement.style.transform).toBeFalsy(); + + dragElementViaMouse(fixture, dragElement, 50, 100); + expect(dragElement.style.transform).toBe('translate3d(50px, 0px, 0px)'); + + dragElementViaMouse(fixture, dragElement, 100, 200); + expect(dragElement.style.transform).toBe('translate3d(150px, 0px, 0px)'); + })); + it('should be able to lock dragging along the y axis', fakeAsync(() => { const fixture = createComponent(StandaloneDraggable); fixture.detectChanges(); @@ -576,6 +602,33 @@ describe('CdkDrag', () => { expect(dragElement.style.transform).toBe('translate3d(0px, 300px, 0px)'); })); + it('should be able to lock dragging along the y axis while using constrainPosition', fakeAsync(() => { + const fixture = createComponent(StandaloneDraggable); + fixture.detectChanges(); + + fixture.componentInstance.dragInstance.lockAxis = 'y'; + fixture.componentInstance.dragInstance.constrainPosition = ( + {x, y}: Point, + _dragRef: DragRef, + _dimensions: ClientRect, + pickup: Point, + ) => { + x -= pickup.x; + y -= pickup.y; + return {x, y}; + }; + + const dragElement = fixture.componentInstance.dragElement.nativeElement; + + expect(dragElement.style.transform).toBeFalsy(); + + dragElementViaMouse(fixture, dragElement, 50, 100); + expect(dragElement.style.transform).toBe('translate3d(0px, 100px, 0px)'); + + dragElementViaMouse(fixture, dragElement, 100, 200); + expect(dragElement.style.transform).toBe('translate3d(0px, 300px, 0px)'); + })); + it('should add a class while an element is being dragged', fakeAsync(() => { const fixture = createComponent(StandaloneDraggable); fixture.detectChanges(); @@ -946,6 +999,29 @@ describe('CdkDrag', () => { expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)'); })); + it('should allow for dragging to be constrained to an element while using constrainPosition', fakeAsync(() => { + const fixture = createComponent(StandaloneDraggable); + fixture.componentInstance.boundary = '.wrapper'; + fixture.detectChanges(); + + fixture.componentInstance.dragInstance.constrainPosition = ( + {x, y}: Point, + _dragRef: DragRef, + _dimensions: ClientRect, + pickup: Point, + ) => { + x -= pickup.x; + y -= pickup.y; + return {x, y}; + }; + + const dragElement = fixture.componentInstance.dragElement.nativeElement; + + expect(dragElement.style.transform).toBeFalsy(); + dragElementViaMouse(fixture, dragElement, 300, 300); + expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)'); + })); + it('should be able to pass in a DOM node as the boundary', fakeAsync(() => { const fixture = createComponent(StandaloneDraggable); fixture.componentInstance.boundary = fixture.nativeElement.querySelector('.wrapper'); diff --git a/src/cdk/drag-drop/drag-ref.ts b/src/cdk/drag-drop/drag-ref.ts index 82bb3da0c17a..242f1baeea18 100644 --- a/src/cdk/drag-drop/drag-ref.ts +++ b/src/cdk/drag-drop/drag-ref.ts @@ -1238,13 +1238,22 @@ export class DragRef { : point; if (this.lockAxis === 'x' || dropContainerLock === 'x') { - y = this._pickupPositionOnPage.y; + y = + this._pickupPositionOnPage.y - + (this.constrainPosition ? this._pickupPositionInElement.y : 0); } else if (this.lockAxis === 'y' || dropContainerLock === 'y') { - x = this._pickupPositionOnPage.x; + x = + this._pickupPositionOnPage.x - + (this.constrainPosition ? this._pickupPositionInElement.x : 0); } if (this._boundaryRect) { - const {x: pickupX, y: pickupY} = this._pickupPositionInElement; + // If not using a custom constrain we need to account for the pickup position in the element + // otherwise we do not need to do this, as it has already been accounted for + const {x: pickupX, y: pickupY} = !this.constrainPosition + ? this._pickupPositionInElement + : {x: 0, y: 0}; + const boundaryRect = this._boundaryRect; const {width: previewWidth, height: previewHeight} = this._getPreviewRect(); const minY = boundaryRect.top + pickupY; diff --git a/src/dev-app/drag-drop/drag-drop-demo.html b/src/dev-app/drag-drop/drag-drop-demo.html index 8a599a0d37ec..035c0125592e 100644 --- a/src/dev-app/drag-drop/drag-drop-demo.html +++ b/src/dev-app/drag-drop/drag-drop-demo.html @@ -1,12 +1,12 @@
-

To do

+ [cdkDropListData]="todo" + >
{{item}} @@ -20,7 +20,8 @@

Done

cdkDropList (cdkDropListDropped)="drop($event)" [cdkDropListLockAxis]="axisLock" - [cdkDropListData]="done"> + [cdkDropListData]="done" + >
{{item}} @@ -37,7 +38,8 @@

Ages

cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)" [cdkDropListLockAxis]="axisLock" - [cdkDropListData]="ages"> + [cdkDropListData]="ages" + >
{{item}} @@ -52,7 +54,8 @@

Preferred Ages

cdkDropListOrientation="horizontal" (cdkDropListDropped)="drop($event)" [cdkDropListLockAxis]="axisLock" - [cdkDropListData]="preferredAges"> + [cdkDropListData]="preferredAges" + >
{{item}} @@ -63,7 +66,45 @@

Preferred Ages

Free dragging

-
Drag me around
+
+ Drag me around +
+
+ +
+

Drag with box boundary

+
+
+ Drag me around +
+
+
+ +
+

Drag with box boundary and custom constrain

+
+
+ Drag me around +
+
@@ -90,6 +131,6 @@

Axis locking

Drag start delay

- +
diff --git a/src/dev-app/drag-drop/drag-drop-demo.scss b/src/dev-app/drag-drop/drag-drop-demo.scss index db92087975c0..0b7eb561a206 100644 --- a/src/dev-app/drag-drop/drag-drop-demo.scss +++ b/src/dev-app/drag-drop/drag-drop-demo.scss @@ -99,3 +99,9 @@ pre { justify-content: center; align-items: center; } + +.demo-constrain-box { + width: 600px; + height: 400px; + border: 1px solid black; +} diff --git a/src/dev-app/drag-drop/drag-drop-demo.ts b/src/dev-app/drag-drop/drag-drop-demo.ts index ff1bb4bc4439..9fa3dd3c8211 100644 --- a/src/dev-app/drag-drop/drag-drop-demo.ts +++ b/src/dev-app/drag-drop/drag-drop-demo.ts @@ -14,6 +14,9 @@ import { DragDropModule, moveItemInArray, transferArrayItem, + Point, + DragRef, + CdkDrag, } from '@angular/cdk/drag-drop'; import {CommonModule} from '@angular/common'; import {FormsModule} from '@angular/forms'; @@ -74,4 +77,16 @@ export class DragAndDropDemo { ); } } + + constrainPosition( + {x, y}: Point, + _dragRef: DragRef, + _dimensions: ClientRect, + pickup: Point, + ): Point { + // Just returning the original top left corner to not modify position + x -= pickup.x; + y -= pickup.y; + return {x, y}; + } }