@@ -10,6 +10,7 @@ import {Directionality} from '@angular/cdk/bidi';
10
10
import { ViewportRuler } from '@angular/cdk/scrolling' ;
11
11
import { DOCUMENT } from '@angular/common' ;
12
12
import {
13
+ AfterViewInit ,
13
14
ContentChild ,
14
15
ContentChildren ,
15
16
Directive ,
@@ -27,7 +28,7 @@ import {
27
28
ViewContainerRef ,
28
29
} from '@angular/core' ;
29
30
import { merge , Observable , Subject } from 'rxjs' ;
30
- import { takeUntil } from 'rxjs/operators' ;
31
+ import { takeUntil , take } from 'rxjs/operators' ;
31
32
import { DragDropRegistry } from './drag-drop-registry' ;
32
33
import {
33
34
CdkDragDrop ,
@@ -60,11 +61,9 @@ const POINTER_DIRECTION_CHANGE_THRESHOLD = 5;
60
61
host : {
61
62
'class' : 'cdk-drag' ,
62
63
'[class.cdk-drag-dragging]' : '_isDragging()' ,
63
- '(mousedown)' : '_startDragging($event)' ,
64
- '(touchstart)' : '_startDragging($event)' ,
65
64
}
66
65
} )
67
- export class CdkDrag < T = any > implements OnDestroy {
66
+ export class CdkDrag < T = any > implements AfterViewInit , OnDestroy {
68
67
private _document : Document ;
69
68
private _destroyed = new Subject < void > ( ) ;
70
69
@@ -127,6 +126,9 @@ export class CdkDrag<T = any> implements OnDestroy {
127
126
/** Pointer position at which the last change in the delta occurred. */
128
127
private _pointerPositionAtLastDirectionChange : Point ;
129
128
129
+ /** Root element that will be dragged by the user. */
130
+ private _rootElement : HTMLElement ;
131
+
130
132
/** Elements that can be used to drag the draggable item. */
131
133
@ContentChildren ( CdkDragHandle ) _handles : QueryList < CdkDragHandle > ;
132
134
@@ -142,6 +144,13 @@ export class CdkDrag<T = any> implements OnDestroy {
142
144
/** Locks the position of the dragged element along the specified axis. */
143
145
@Input ( 'cdkDragLockAxis' ) lockAxis : 'x' | 'y' ;
144
146
147
+ /**
148
+ * Selector that will be used to determine the root draggable element, starting from
149
+ * the `cdkDrag` element and going up the DOM. Passing an alternate root element is useful
150
+ * when trying to enable dragging on an element that you might not have access to.
151
+ */
152
+ @Input ( 'cdkDragRootElement' ) rootElementSelector : string ;
153
+
145
154
/** Emits when the user starts dragging the item. */
146
155
@Output ( 'cdkDragStarted' ) started : EventEmitter < CdkDragStart > = new EventEmitter < CdkDragStart > ( ) ;
147
156
@@ -197,7 +206,26 @@ export class CdkDrag<T = any> implements OnDestroy {
197
206
return this . _placeholder ;
198
207
}
199
208
209
+ /** Returns the root draggable element. */
210
+ getRootElement ( ) : HTMLElement {
211
+ return this . _rootElement ;
212
+ }
213
+
214
+ ngAfterViewInit ( ) {
215
+ // We need to wait for the zone to stabilize, in order for the reference
216
+ // element to be in the proper place in the DOM. This is mostly relevant
217
+ // for draggable elements inside portals since they get stamped out in
218
+ // their original DOM position and then they get transferred to the portal.
219
+ this . _ngZone . onStable . asObservable ( ) . pipe ( take ( 1 ) ) . subscribe ( ( ) => {
220
+ const rootElement = this . _rootElement = this . _getRootElement ( ) ;
221
+ rootElement . addEventListener ( 'mousedown' , this . _startDragging ) ;
222
+ rootElement . addEventListener ( 'touchstart' , this . _startDragging ) ;
223
+ } ) ;
224
+ }
225
+
200
226
ngOnDestroy ( ) {
227
+ this . _rootElement . removeEventListener ( 'mousedown' , this . _startDragging ) ;
228
+ this . _rootElement . removeEventListener ( 'touchstart' , this . _startDragging ) ;
201
229
this . _destroyPreview ( ) ;
202
230
this . _destroyPlaceholder ( ) ;
203
231
@@ -206,7 +234,7 @@ export class CdkDrag<T = any> implements OnDestroy {
206
234
if ( this . _isDragging ( ) ) {
207
235
// Since we move out the element to the end of the body while it's being
208
236
// dragged, we have to make sure that it's removed if it gets destroyed.
209
- this . _removeElement ( this . element . nativeElement ) ;
237
+ this . _removeElement ( this . _rootElement ) ;
210
238
}
211
239
212
240
this . _nextSibling = null ;
@@ -217,7 +245,7 @@ export class CdkDrag<T = any> implements OnDestroy {
217
245
}
218
246
219
247
/** Starts the dragging sequence. */
220
- _startDragging ( event : MouseEvent | TouchEvent ) {
248
+ _startDragging = ( event : MouseEvent | TouchEvent ) => {
221
249
// Delegate the event based on whether it started from a handle or the element itself.
222
250
if ( this . _handles . length ) {
223
251
const targetHandle = this . _handles . find ( handle => {
@@ -227,10 +255,10 @@ export class CdkDrag<T = any> implements OnDestroy {
227
255
} ) ;
228
256
229
257
if ( targetHandle ) {
230
- this . _pointerDown ( targetHandle . element , event ) ;
258
+ this . _pointerDown ( targetHandle . element . nativeElement , event ) ;
231
259
}
232
260
} else {
233
- this . _pointerDown ( this . element , event ) ;
261
+ this . _pointerDown ( this . _rootElement , event ) ;
234
262
}
235
263
}
236
264
@@ -240,7 +268,7 @@ export class CdkDrag<T = any> implements OnDestroy {
240
268
}
241
269
242
270
/** Handler for when the pointer is pressed down on the element or the handle. */
243
- private _pointerDown = ( referenceElement : ElementRef < HTMLElement > ,
271
+ private _pointerDown = ( referenceElement : HTMLElement ,
244
272
event : MouseEvent | TouchEvent ) => {
245
273
246
274
const isDragging = this . _isDragging ( ) ;
@@ -277,7 +305,7 @@ export class CdkDrag<T = any> implements OnDestroy {
277
305
this . started . emit ( { source : this } ) ;
278
306
279
307
if ( this . dropContainer ) {
280
- const element = this . element . nativeElement ;
308
+ const element = this . _rootElement ;
281
309
282
310
// Grab the `nextSibling` before the preview and placeholder
283
311
// have been created so we don't get the preview by accident.
@@ -318,7 +346,7 @@ export class CdkDrag<T = any> implements OnDestroy {
318
346
pointerPosition . x - this . _pickupPositionOnPage . x + this . _passiveTransform . x ;
319
347
activeTransform . y =
320
348
pointerPosition . y - this . _pickupPositionOnPage . y + this . _passiveTransform . y ;
321
- this . _setTransform ( this . element . nativeElement , activeTransform . x , activeTransform . y ) ;
349
+ this . _setTransform ( this . _rootElement , activeTransform . x , activeTransform . y ) ;
322
350
}
323
351
324
352
// Since this event gets fired for every pixel while dragging, we only
@@ -362,12 +390,12 @@ export class CdkDrag<T = any> implements OnDestroy {
362
390
// It's important that we maintain the position, because moving the element around in the DOM
363
391
// can throw off `NgFor` which does smart diffing and re-creates elements only when necessary,
364
392
// while moving the existing elements in all other cases.
365
- this . element . nativeElement . style . display = '' ;
393
+ this . _rootElement . style . display = '' ;
366
394
367
395
if ( this . _nextSibling ) {
368
- this . _nextSibling . parentNode ! . insertBefore ( this . element . nativeElement , this . _nextSibling ) ;
396
+ this . _nextSibling . parentNode ! . insertBefore ( this . _rootElement , this . _nextSibling ) ;
369
397
} else {
370
- this . _placeholder . parentNode ! . appendChild ( this . element . nativeElement ) ;
398
+ this . _placeholder . parentNode ! . appendChild ( this . _rootElement ) ;
371
399
}
372
400
373
401
this . _destroyPreview ( ) ;
@@ -430,7 +458,7 @@ export class CdkDrag<T = any> implements OnDestroy {
430
458
this . _previewRef = viewRef ;
431
459
this . _setTransform ( preview , this . _pickupPositionOnPage . x , this . _pickupPositionOnPage . y ) ;
432
460
} else {
433
- const element = this . element . nativeElement ;
461
+ const element = this . _rootElement ;
434
462
const elementRect = element . getBoundingClientRect ( ) ;
435
463
436
464
preview = element . cloneNode ( true ) as HTMLElement ;
@@ -456,7 +484,7 @@ export class CdkDrag<T = any> implements OnDestroy {
456
484
) ;
457
485
placeholder = this . _placeholderRef . rootNodes [ 0 ] ;
458
486
} else {
459
- placeholder = this . element . nativeElement . cloneNode ( true ) as HTMLElement ;
487
+ placeholder = this . _rootElement . cloneNode ( true ) as HTMLElement ;
460
488
}
461
489
462
490
placeholder . classList . add ( 'cdk-drag-placeholder' ) ;
@@ -468,10 +496,10 @@ export class CdkDrag<T = any> implements OnDestroy {
468
496
* @param referenceElement Element that initiated the dragging.
469
497
* @param event Event that initiated the dragging.
470
498
*/
471
- private _getPointerPositionInElement ( referenceElement : ElementRef < HTMLElement > ,
499
+ private _getPointerPositionInElement ( referenceElement : HTMLElement ,
472
500
event : MouseEvent | TouchEvent ) : Point {
473
- const elementRect = this . element . nativeElement . getBoundingClientRect ( ) ;
474
- const handleElement = referenceElement === this . element ? null : referenceElement . nativeElement ;
501
+ const elementRect = this . _rootElement . getBoundingClientRect ( ) ;
502
+ const handleElement = referenceElement === this . _rootElement ? null : referenceElement ;
475
503
const referenceRect = handleElement ? handleElement . getBoundingClientRect ( ) : elementRect ;
476
504
const x = this . _isTouchEvent ( event ) ?
477
505
event . targetTouches [ 0 ] . pageX - referenceRect . left - this . _scrollPosition . left :
@@ -632,6 +660,23 @@ export class CdkDrag<T = any> implements OnDestroy {
632
660
positionSinceLastChange . y = y ;
633
661
}
634
662
}
663
+
664
+ /** Gets the root draggable element, based on the `rootElementSelector`. */
665
+ private _getRootElement ( ) : HTMLElement {
666
+ if ( this . rootElementSelector ) {
667
+ let currentElement = this . element . nativeElement . parentElement as HTMLElement | null ;
668
+
669
+ while ( currentElement ) {
670
+ if ( currentElement . matches ( this . rootElementSelector ) ) {
671
+ return currentElement ;
672
+ }
673
+
674
+ currentElement = currentElement . parentElement ;
675
+ }
676
+ }
677
+
678
+ return this . element . nativeElement ;
679
+ }
635
680
}
636
681
637
682
/** Parses a CSS time value to milliseconds. */
@@ -650,7 +695,6 @@ function getTransitionDurationInMs(element: HTMLElement): number {
650
695
return parseCssTimeUnitsToMs ( rawDuration ) + parseCssTimeUnitsToMs ( rawDelay ) ;
651
696
}
652
697
653
-
654
698
/** Point on the page or within an element. */
655
699
interface Point {
656
700
x : number ;
0 commit comments