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