@@ -20,8 +20,10 @@ import {
20
20
Optional ,
21
21
Directive ,
22
22
ChangeDetectorRef ,
23
+ Inject ,
23
24
} from '@angular/core' ;
24
25
import { Directionality } from '@angular/cdk/bidi' ;
26
+ import { DOCUMENT } from '@angular/common' ;
25
27
import { CdkDrag } from './drag' ;
26
28
import { DragDropRegistry } from './drag-drop-registry' ;
27
29
import { CdkDragDrop , CdkDragEnter , CdkDragExit , CdkDragSortEvent } from './drag-events' ;
@@ -90,6 +92,8 @@ interface ListPositionCacheEntry {
90
92
}
91
93
} )
92
94
export class CdkDropList < T = any > implements OnInit , OnDestroy {
95
+ private _document : Document | undefined ;
96
+
93
97
/** Draggable items in the container. */
94
98
@ContentChildren ( forwardRef ( ( ) => CdkDrag ) ) _draggables : QueryList < CdkDrag > ;
95
99
@@ -157,7 +161,17 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
157
161
private _dragDropRegistry : DragDropRegistry < CdkDrag , CdkDropList < T > > ,
158
162
private _changeDetectorRef : ChangeDetectorRef ,
159
163
@Optional ( ) private _dir ?: Directionality ,
160
- @Optional ( ) private _group ?: CdkDropListGroup < CdkDropList > ) { }
164
+ @Optional ( ) private _group ?: CdkDropListGroup < CdkDropList > ,
165
+ // @breaking -change 8.0.0 `_document` parameter to be made required.
166
+ @Optional ( ) @Inject ( DOCUMENT ) _document ?: any ) {
167
+
168
+ // @breaking -change 8.0.0 Remove null checks once `_document` parameter is required.
169
+ if ( _document ) {
170
+ this . _document = _document ;
171
+ } else if ( typeof document !== 'undefined' ) {
172
+ this . _document = document ;
173
+ }
174
+ }
161
175
162
176
ngOnInit ( ) {
163
177
this . _dragDropRegistry . registerDropContainer ( this ) ;
@@ -376,8 +390,39 @@ export class CdkDropList<T = any> implements OnInit, OnDestroy {
376
390
* @param y Position of the item along the Y axis.
377
391
*/
378
392
_getSiblingContainerFromPosition ( item : CdkDrag , x : number , y : number ) : CdkDropList | null {
379
- const result = this . _positionCache . siblings
380
- . find ( sibling => isInsideClientRect ( sibling . clientRect , x , y ) ) ;
393
+ const results = this . _positionCache . siblings . filter ( sibling => {
394
+ return isInsideClientRect ( sibling . clientRect , x , y ) ;
395
+ } ) ;
396
+
397
+ // No drop containers are intersecting with the pointer.
398
+ if ( ! results . length ) {
399
+ return null ;
400
+ }
401
+
402
+ let result : ListPositionCacheEntry | undefined = results [ 0 ] ;
403
+
404
+ // @breaking -change 8.0.0 remove null check once the
405
+ // `_document` is made into a required parameter.
406
+ if ( this . _document ) {
407
+ const elementFromPoint = this . _document . elementFromPoint ( x , y ) ;
408
+
409
+ // If there's no element at the pointer position, then
410
+ // the client rect is probably scrolled out of the view.
411
+ if ( ! elementFromPoint ) {
412
+ return null ;
413
+ }
414
+
415
+ // The `ClientRect`, that we're using to find the container over which the user is
416
+ // hovering, doesn't give us any information on whether the element has been scrolled
417
+ // out of the view or whether it's overlapping with other containers. This means that
418
+ // we could end up transferring the item into a container that's invisible or is positioned
419
+ // below another one. We use the result from `elementFromPoint` to get the top-most element
420
+ // at the pointer position and to find whether it's one of the intersecting drop containers.
421
+ result = results . find ( sibling => {
422
+ const element = sibling . drop . element . nativeElement ;
423
+ return element === elementFromPoint || element . contains ( elementFromPoint ) ;
424
+ } ) ;
425
+ }
381
426
382
427
return result && result . drop . enterPredicate ( item , result . drop ) ? result . drop : null ;
383
428
}
0 commit comments