Skip to content

Commit

Permalink
feat(cdk/drag-drop): introduce resetToBoundary
Browse files Browse the repository at this point in the history
this commit introduces `resetToBoundary` in DragRef which allows user to align DragItem to
its boundary on demand if at one point it was at a place where the boundary element
used to be and has shrinked causing DragItem to be outside of the boundary box

fixes #30325
  • Loading branch information
naaajii committed Feb 2, 2025
1 parent 04ae202 commit fb03d76
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/cdk/drag-drop/directives/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,11 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
this._dragRef.reset();
}

/** Resets drag item to end of boundary element. */
resetToBoundary() {
this._dragRef.resetToBoundary();
}

/**
* Gets the pixel coordinates of the draggable outside of a drop container.
*/
Expand Down
43 changes: 43 additions & 0 deletions src/cdk/drag-drop/directives/standalone-drag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ describe('Standalone CdkDrag', () => {
expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)');
}));

it('should reset drag item to boundary', fakeAsync(() => {
const fixture = createComponent(DragWithResizeableBoundary);
fixture.detectChanges();
let dragElement = fixture.componentInstance.dragElement.nativeElement;

expect(dragElement.style.transform).toBeFalsy();
dragElementViaMouse(fixture, dragElement, 50, 100);
expect(dragElement.style.transform).toBe('translate3d(50px, 100px, 0px)');

dragElementViaMouse(fixture, dragElement, 300, 300);
expect(dragElement.style.transform).toBe('translate3d(300px, 300px, 0px)');

fixture.componentInstance.resizeBoundary();
fixture.detectChanges();

const position = fixture.componentInstance.dragInstance.getFreeDragPosition();
expect(position).toEqual({x: 100, y: 0});
expect(dragElement.style.transform).toBe('translate3d(100px, 0px, 0px)');
}));

it('should drag an element freely to a particular position when the page is scrolled', fakeAsync(() => {
const fixture = createComponent(StandaloneDraggable);
fixture.detectChanges();
Expand Down Expand Up @@ -2047,3 +2067,26 @@ class PlainStandaloneDraggable {
class StandaloneDraggableWithExternalTemplateHandle {
@ViewChild('dragElement') dragElement: ElementRef<HTMLElement>;
}

@Component({
template: `
<div #boundaryElement class="example-boundary" style="width: 400px; height: 400px">
<div #dragElement class="example-box" cdkDragBoundary=".example-boundary" cdkDrag style="width: 100px; height: 100px">
I can only be dragged within the dotted container
</div>
</div>
`,
imports: [CdkDrag],
})
class DragWithResizeableBoundary {
@ViewChild('boundaryElement') boundaryElement: ElementRef<HTMLElement>;

@ViewChild('dragElement') dragElement: ElementRef<HTMLElement>;
@ViewChild(CdkDrag) dragInstance: CdkDrag;

resizeBoundary() {
this.boundaryElement.nativeElement.style.height = '200px';
this.boundaryElement.nativeElement.style.width = '200px';
this.dragInstance.resetToBoundary();
}
}
13 changes: 13 additions & 0 deletions src/cdk/drag-drop/drag-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,19 @@ export class DragRef<T = any> {
this._passiveTransform = {x: 0, y: 0};
}

/** Resets drag item to end of boundary element. */
resetToBoundary(): void {
// can be null if the drag item was never dragged.
if (this._boundaryElement) {
let x = this._boundaryElement.offsetWidth - this._rootElement.offsetWidth,
y = 0; // align it to the top of its boundary

this._rootElement.style.transform = getTransform(x, y);
this._activeTransform = {x, y};
this._passiveTransform = {x, y};
}
}

/**
* Sets a handle as disabled. While a handle is disabled, it'll capture and interrupt dragging.
* @param handle Handle element that should be disabled.
Expand Down

0 comments on commit fb03d76

Please sign in to comment.