Skip to content

Commit 399f25e

Browse files
crisbetojelbourn
authored andcommitted
feat(drag-drop): allow element to be passed in as the boundary (#15810)
Allows for an `HTMLElement` or an `ElementRef` to be passed in as the boundary of a `cdkDrag`, in addition to a selector. Fixes #15766.
1 parent 5aaca54 commit 399f25e

File tree

3 files changed

+52
-9
lines changed

3 files changed

+52
-9
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,18 @@ describe('CdkDrag', () => {
694694

695695
it('should allow for dragging to be constrained to an element', fakeAsync(() => {
696696
const fixture = createComponent(StandaloneDraggable);
697-
fixture.componentInstance.boundarySelector = '.wrapper';
697+
fixture.componentInstance.boundary = '.wrapper';
698+
fixture.detectChanges();
699+
const dragElement = fixture.componentInstance.dragElement.nativeElement;
700+
701+
expect(dragElement.style.transform).toBeFalsy();
702+
dragElementViaMouse(fixture, dragElement, 300, 300);
703+
expect(dragElement.style.transform).toBe('translate3d(100px, 100px, 0px)');
704+
}));
705+
706+
it('should be able to pass in a DOM node as the boundary', fakeAsync(() => {
707+
const fixture = createComponent(StandaloneDraggable);
708+
fixture.componentInstance.boundary = fixture.nativeElement.querySelector('.wrapper');
698709
fixture.detectChanges();
699710
const dragElement = fixture.componentInstance.dragElement.nativeElement;
700711

@@ -3287,7 +3298,7 @@ describe('CdkDrag', () => {
32873298
<div class="wrapper" style="width: 200px; height: 200px; background: green;">
32883299
<div
32893300
cdkDrag
3290-
[cdkDragBoundary]="boundarySelector"
3301+
[cdkDragBoundary]="boundary"
32913302
[cdkDragStartDelay]="dragStartDelay"
32923303
[cdkDragConstrainPosition]="constrainPosition"
32933304
[cdkDragFreeDragPosition]="freeDragPosition"
@@ -3305,7 +3316,7 @@ class StandaloneDraggable {
33053316
startedSpy = jasmine.createSpy('started spy');
33063317
endedSpy = jasmine.createSpy('ended spy');
33073318
releasedSpy = jasmine.createSpy('released spy');
3308-
boundarySelector: string;
3319+
boundary: string | HTMLElement;
33093320
dragStartDelay: number | string;
33103321
constrainPosition: (point: Point) => Point;
33113322
freeDragPosition?: {x: number, y: number};

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

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ import {
2828
OnChanges,
2929
SimpleChanges,
3030
ChangeDetectorRef,
31+
isDevMode,
3132
} from '@angular/core';
32-
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
33+
import {coerceBooleanProperty, coerceNumberProperty, coerceElement} from '@angular/cdk/coercion';
3334
import {Observable, Observer, Subject, merge} from 'rxjs';
3435
import {startWith, take, map, takeUntil, switchMap, tap} from 'rxjs/operators';
3536
import {
@@ -100,12 +101,27 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
100101
*/
101102
@Input('cdkDragRootElement') rootElementSelector: string;
102103

104+
/**
105+
* Node or selector that will be used to determine the element to which the draggable's
106+
* position will be constrained. If a string is passed in, it'll be used as a selector that
107+
* will be matched starting from the element's parent and going up the DOM until a match
108+
* has been found.
109+
*/
110+
@Input('cdkDragBoundary') boundaryElement: string | ElementRef<HTMLElement> | HTMLElement;
111+
103112
/**
104113
* Selector that will be used to determine the element to which the draggable's position will
105114
* be constrained. Matching starts from the element's parent and goes up the DOM until a matching
106-
* element has been found.
115+
* element has been found
116+
* @deprecated Use `boundaryElement` instead.
117+
* @breaking-change 9.0.0
107118
*/
108-
@Input('cdkDragBoundary') boundaryElementSelector: string;
119+
get boundaryElementSelector(): string {
120+
return typeof this.boundaryElement === 'string' ? this.boundaryElement : undefined!;
121+
}
122+
set boundaryElementSelector(selector: string) {
123+
this.boundaryElement = selector;
124+
}
109125

110126
/**
111127
* Amount of milliseconds to wait after the user has put their
@@ -292,10 +308,25 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
292308
this._dragRef.withRootElement(rootElement || element);
293309
}
294310

295-
/** Gets the boundary element, based on the `boundaryElementSelector`. */
311+
/** Gets the boundary element, based on the `boundaryElement` value. */
296312
private _getBoundaryElement() {
297-
const selector = this.boundaryElementSelector;
298-
return selector ? getClosestMatchingAncestor(this.element.nativeElement, selector) : null;
313+
const boundary = this.boundaryElement;
314+
315+
if (!boundary) {
316+
return null;
317+
}
318+
319+
if (typeof boundary === 'string') {
320+
return getClosestMatchingAncestor(this.element.nativeElement, boundary);
321+
}
322+
323+
const element = coerceElement(boundary);
324+
325+
if (isDevMode() && !element.contains(this.element.nativeElement)) {
326+
throw Error('Draggable element is not inside of the node passed into cdkDragBoundary.');
327+
}
328+
329+
return element;
299330
}
300331

301332
/** Syncs the inputs of the CdkDrag with the options of the underlying DragRef. */

tools/public_api_guard/cdk/drag-drop.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export declare class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDes
1111
_handles: QueryList<CdkDragHandle>;
1212
_placeholderTemplate: CdkDragPlaceholder;
1313
_previewTemplate: CdkDragPreview;
14+
boundaryElement: string | ElementRef<HTMLElement> | HTMLElement;
1415
boundaryElementSelector: string;
1516
constrainPosition?: (point: Point) => Point;
1617
data: T;

0 commit comments

Comments
 (0)