@@ -23,24 +23,11 @@ let _uniqueIdCounter = 0;
23
23
*/
24
24
const DROP_PROXIMITY_THRESHOLD = 0.05 ;
25
25
26
- /**
27
- * Object used to cache the position of a drag list, its items. and siblings.
28
- * @docs -private
29
- */
30
- interface PositionCache {
31
- /** Cached positions of the items in the list. */
32
- items : ItemPositionCacheEntry [ ] ;
33
- /** Cached positions of the connected lists. */
34
- siblings : ListPositionCacheEntry [ ] ;
35
- /** Dimensions of the list itself. */
36
- self : ClientRect ;
37
- }
38
-
39
26
/**
40
27
* Entry in the position cache for draggable items.
41
28
* @docs -private
42
29
*/
43
- interface ItemPositionCacheEntry {
30
+ interface CachedItemPosition {
44
31
/** Instance of the drag item. */
45
32
drag : DragRef ;
46
33
/** Dimensions of the item. */
@@ -49,17 +36,6 @@ interface ItemPositionCacheEntry {
49
36
offset : number ;
50
37
}
51
38
52
- /**
53
- * Entry in the position cache for drop lists.
54
- * @docs -private
55
- */
56
- interface ListPositionCacheEntry {
57
- /** Instance of the drop list. */
58
- drop : DropListRef ;
59
- /** Dimensions of the list. */
60
- clientRect : ClientRect ;
61
- }
62
-
63
39
/**
64
40
* Internal compile-time-only representation of a `DropListRef`.
65
41
* Used to avoid circular import issues between the `DropListRef` and the `DragRef`.
@@ -131,8 +107,11 @@ export class DropListRef<T = any> {
131
107
/** Whether an item in the list is being dragged. */
132
108
private _isDragging = false ;
133
109
134
- /** Cache of the dimensions of all the items and the sibling containers. */
135
- private _positionCache : PositionCache = { items : [ ] , siblings : [ ] , self : { } as ClientRect } ;
110
+ /** Cache of the dimensions of all the items inside the container. */
111
+ private _itemPositions : CachedItemPosition [ ] = [ ] ;
112
+
113
+ /** Cached `ClientRect` of the drop list. */
114
+ private _clientRect : ClientRect ;
136
115
137
116
/**
138
117
* Draggable items that are currently active inside the container. Includes the items
@@ -150,13 +129,14 @@ export class DropListRef<T = any> {
150
129
/** Draggable items in the container. */
151
130
private _draggables : DragRef [ ] ;
152
131
132
+ /** Drop lists that are connected to the current one. */
153
133
private _siblings : DropListRef [ ] = [ ] ;
154
134
155
135
/** Direction in which the list is oriented. */
156
136
private _orientation : 'horizontal' | 'vertical' = 'vertical' ;
157
137
158
- /** Amount of connected siblings that currently have a dragged item. */
159
- private _activeSiblings = 0 ;
138
+ /** Connected siblings that currently have a dragged item. */
139
+ private _activeSiblings = new Set < DropListRef > ( ) ;
160
140
161
141
constructor (
162
142
public element : ElementRef < HTMLElement > ,
@@ -174,6 +154,7 @@ export class DropListRef<T = any> {
174
154
this . exited . complete ( ) ;
175
155
this . dropped . complete ( ) ;
176
156
this . sorted . complete ( ) ;
157
+ this . _activeSiblings . clear ( ) ;
177
158
this . _dragDropRegistry . removeDropContainer ( this ) ;
178
159
}
179
160
@@ -187,8 +168,9 @@ export class DropListRef<T = any> {
187
168
this . beforeStarted . next ( ) ;
188
169
this . _isDragging = true ;
189
170
this . _activeDraggables = this . _draggables . slice ( ) ;
190
- this . _cachePositions ( ) ;
191
- this . _positionCache . siblings . forEach ( sibling => sibling . drop . _toggleIsReceiving ( true ) ) ;
171
+ this . _cacheOwnPosition ( ) ;
172
+ this . _cacheItemPositions ( ) ;
173
+ this . _siblings . forEach ( sibling => sibling . _startReceiving ( this ) ) ;
192
174
}
193
175
194
176
/**
@@ -230,7 +212,7 @@ export class DropListRef<T = any> {
230
212
231
213
// Note that the positions were already cached when we called `start` above,
232
214
// but we need to refresh them since the amount of items has changed.
233
- this . _cachePositions ( ) ;
215
+ this . _cacheItemPositions ( ) ;
234
216
}
235
217
236
218
/**
@@ -304,7 +286,7 @@ export class DropListRef<T = any> {
304
286
// The rest of the logic still stands no matter what orientation we're in, however
305
287
// we need to invert the array when determining the index.
306
288
const items = this . _orientation === 'horizontal' && this . _dir && this . _dir . value === 'rtl' ?
307
- this . _positionCache . items . slice ( ) . reverse ( ) : this . _positionCache . items ;
289
+ this . _itemPositions . slice ( ) . reverse ( ) : this . _itemPositions ;
308
290
309
291
return findIndex ( items , currentItem => currentItem . drag === item ) ;
310
292
}
@@ -314,7 +296,7 @@ export class DropListRef<T = any> {
314
296
* is currently being dragged inside a connected drop list.
315
297
*/
316
298
isReceiving ( ) : boolean {
317
- return this . _activeSiblings > 0 ;
299
+ return this . _activeSiblings . size > 0 ;
318
300
}
319
301
320
302
/**
@@ -331,7 +313,7 @@ export class DropListRef<T = any> {
331
313
return ;
332
314
}
333
315
334
- const siblings = this . _positionCache . items ;
316
+ const siblings = this . _itemPositions ;
335
317
const newIndex = this . _getItemIndexFromPointerPosition ( item , pointerX , pointerY , pointerDelta ) ;
336
318
337
319
if ( newIndex === - 1 && siblings . length > 0 ) {
@@ -398,54 +380,43 @@ export class DropListRef<T = any> {
398
380
} ) ;
399
381
}
400
382
383
+ /** Caches the position of the drop list. */
384
+ private _cacheOwnPosition ( ) {
385
+ this . _clientRect = this . element . nativeElement . getBoundingClientRect ( ) ;
386
+ }
387
+
401
388
/** Refreshes the position cache of the items and sibling containers. */
402
- private _cachePositions ( ) {
389
+ private _cacheItemPositions ( ) {
403
390
const isHorizontal = this . _orientation === 'horizontal' ;
404
391
405
- this . _positionCache . self = this . element . nativeElement . getBoundingClientRect ( ) ;
406
- this . _positionCache . items = this . _activeDraggables
407
- . map ( drag => {
408
- const elementToMeasure = this . _dragDropRegistry . isDragging ( drag ) ?
409
- // If the element is being dragged, we have to measure the
410
- // placeholder, because the element is hidden.
411
- drag . getPlaceholderElement ( ) :
412
- drag . getRootElement ( ) ;
413
- const clientRect = elementToMeasure . getBoundingClientRect ( ) ;
414
-
415
- return {
416
- drag,
417
- offset : 0 ,
418
- // We need to clone the `clientRect` here, because all the values on it are readonly
419
- // and we need to be able to update them. Also we can't use a spread here, because
420
- // the values on a `ClientRect` aren't own properties. See:
421
- // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect#Notes
422
- clientRect : {
423
- top : clientRect . top ,
424
- right : clientRect . right ,
425
- bottom : clientRect . bottom ,
426
- left : clientRect . left ,
427
- width : clientRect . width ,
428
- height : clientRect . height
429
- }
430
- } ;
431
- } )
432
- . sort ( ( a , b ) => {
433
- return isHorizontal ? a . clientRect . left - b . clientRect . left :
434
- a . clientRect . top - b . clientRect . top ;
435
- } ) ;
436
-
437
- this . _positionCache . siblings = this . _siblings . map ( drop => ( {
438
- drop,
439
- clientRect : drop . element . nativeElement . getBoundingClientRect ( )
440
- } ) ) ;
441
- }
442
-
443
- /**
444
- * Toggles whether the list can receive the item that is currently being dragged.
445
- * Usually called by a sibling that initiated the dragging.
446
- */
447
- _toggleIsReceiving ( isDragging : boolean ) {
448
- this . _activeSiblings = Math . max ( 0 , this . _activeSiblings + ( isDragging ? 1 : - 1 ) ) ;
392
+ this . _itemPositions = this . _activeDraggables . map ( drag => {
393
+ const elementToMeasure = this . _dragDropRegistry . isDragging ( drag ) ?
394
+ // If the element is being dragged, we have to measure the
395
+ // placeholder, because the element is hidden.
396
+ drag . getPlaceholderElement ( ) :
397
+ drag . getRootElement ( ) ;
398
+ const clientRect = elementToMeasure . getBoundingClientRect ( ) ;
399
+
400
+ return {
401
+ drag,
402
+ offset : 0 ,
403
+ // We need to clone the `clientRect` here, because all the values on it are readonly
404
+ // and we need to be able to update them. Also we can't use a spread here, because
405
+ // the values on a `ClientRect` aren't own properties. See:
406
+ // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect#Notes
407
+ clientRect : {
408
+ top : clientRect . top ,
409
+ right : clientRect . right ,
410
+ bottom : clientRect . bottom ,
411
+ left : clientRect . left ,
412
+ width : clientRect . width ,
413
+ height : clientRect . height
414
+ }
415
+ } ;
416
+ } ) . sort ( ( a , b ) => {
417
+ return isHorizontal ? a . clientRect . left - b . clientRect . left :
418
+ a . clientRect . top - b . clientRect . top ;
419
+ } ) ;
449
420
}
450
421
451
422
/** Resets the container to its initial state. */
@@ -454,10 +425,9 @@ export class DropListRef<T = any> {
454
425
455
426
// TODO(crisbeto): may have to wait for the animations to finish.
456
427
this . _activeDraggables . forEach ( item => item . getRootElement ( ) . style . transform = '' ) ;
457
- this . _positionCache . siblings . forEach ( sibling => sibling . drop . _toggleIsReceiving ( false ) ) ;
428
+ this . _siblings . forEach ( sibling => sibling . _stopReceiving ( this ) ) ;
458
429
this . _activeDraggables = [ ] ;
459
- this . _positionCache . items = [ ] ;
460
- this . _positionCache . siblings = [ ] ;
430
+ this . _itemPositions = [ ] ;
461
431
this . _previousSwap . drag = null ;
462
432
this . _previousSwap . delta = 0 ;
463
433
}
@@ -469,7 +439,7 @@ export class DropListRef<T = any> {
469
439
* @param delta Direction in which the user is moving.
470
440
*/
471
441
private _getSiblingOffsetPx ( currentIndex : number ,
472
- siblings : ItemPositionCacheEntry [ ] ,
442
+ siblings : CachedItemPosition [ ] ,
473
443
delta : 1 | - 1 ) {
474
444
475
445
const isHorizontal = this . _orientation === 'horizontal' ;
@@ -501,7 +471,7 @@ export class DropListRef<T = any> {
501
471
* @param pointerY Coordinates along the Y axis.
502
472
*/
503
473
private _isPointerNearDropContainer ( pointerX : number , pointerY : number ) : boolean {
504
- const { top, right, bottom, left, width, height} = this . _positionCache . self ;
474
+ const { top, right, bottom, left, width, height} = this . _clientRect ;
505
475
const xThreshold = width * DROP_PROXIMITY_THRESHOLD ;
506
476
const yThreshold = height * DROP_PROXIMITY_THRESHOLD ;
507
477
@@ -538,10 +508,9 @@ export class DropListRef<T = any> {
538
508
*/
539
509
private _getItemIndexFromPointerPosition ( item : DragRef , pointerX : number , pointerY : number ,
540
510
delta ?: { x : number , y : number } ) {
541
-
542
511
const isHorizontal = this . _orientation === 'horizontal' ;
543
512
544
- return findIndex ( this . _positionCache . items , ( { drag, clientRect} , _ , array ) => {
513
+ return findIndex ( this . _itemPositions , ( { drag, clientRect} , _ , array ) => {
545
514
if ( drag === item ) {
546
515
// If there's only one item left in the container, it must be
547
516
// the dragged item itself so we use it as a reference.
@@ -572,7 +541,7 @@ export class DropListRef<T = any> {
572
541
* @param y Pointer position along the Y axis.
573
542
*/
574
543
_isOverContainer ( x : number , y : number ) : boolean {
575
- return isInsideClientRect ( this . _positionCache . self , x , y ) ;
544
+ return isInsideClientRect ( this . _clientRect , x , y ) ;
576
545
}
577
546
578
547
/**
@@ -582,38 +551,60 @@ export class DropListRef<T = any> {
582
551
* @param x Position of the item along the X axis.
583
552
* @param y Position of the item along the Y axis.
584
553
*/
585
- _getSiblingContainerFromPosition ( item : DragRef , x : number , y : number ) : DropListRef | null {
586
- const results = this . _positionCache . siblings . filter ( sibling => {
587
- return isInsideClientRect ( sibling . clientRect , x , y ) ;
588
- } ) ;
554
+ _getSiblingContainerFromPosition ( item : DragRef , x : number , y : number ) : DropListRef | undefined {
555
+ return this . _siblings . find ( sibling => sibling . _canReceive ( item , x , y ) ) ;
556
+ }
589
557
590
- // No drop containers are intersecting with the pointer.
591
- if ( ! results . length ) {
592
- return null ;
558
+ /**
559
+ * Checks whether the drop list can receive the passed-in item.
560
+ * @param item Item that is being dragged into the list.
561
+ * @param x Position of the item along the X axis.
562
+ * @param y Position of the item along the Y axis.
563
+ */
564
+ _canReceive ( item : DragRef , x : number , y : number ) : boolean {
565
+ if ( ! this . enterPredicate ( item , this ) || ! isInsideClientRect ( this . _clientRect , x , y ) ) {
566
+ return false ;
593
567
}
594
568
595
569
const elementFromPoint = this . _document . elementFromPoint ( x , y ) ;
596
570
597
571
// If there's no element at the pointer position, then
598
572
// the client rect is probably scrolled out of the view.
599
573
if ( ! elementFromPoint ) {
600
- return null ;
574
+ return false ;
601
575
}
602
576
577
+ const element = this . element . nativeElement ;
578
+
603
579
// The `ClientRect`, that we're using to find the container over which the user is
604
580
// hovering, doesn't give us any information on whether the element has been scrolled
605
581
// out of the view or whether it's overlapping with other containers. This means that
606
582
// we could end up transferring the item into a container that's invisible or is positioned
607
583
// below another one. We use the result from `elementFromPoint` to get the top-most element
608
584
// at the pointer position and to find whether it's one of the intersecting drop containers.
609
- const result = results . find ( sibling => {
610
- const element = sibling . drop . element . nativeElement ;
611
- return element === elementFromPoint || element . contains ( elementFromPoint ) ;
612
- } ) ;
585
+ return elementFromPoint === element || element . contains ( elementFromPoint ) ;
586
+ }
587
+
588
+ /**
589
+ * Called by one of the connected drop lists when a dragging sequence has started.
590
+ * @param sibling Sibling in which dragging has started.
591
+ */
592
+ _startReceiving ( sibling : DropListRef ) {
593
+ const activeSiblings = this . _activeSiblings ;
613
594
614
- return result && result . drop . enterPredicate ( item , result . drop ) ? result . drop : null ;
595
+ if ( ! activeSiblings . has ( sibling ) ) {
596
+ activeSiblings . add ( sibling ) ;
597
+ this . _cacheOwnPosition ( ) ;
598
+ }
615
599
}
616
600
601
+ /**
602
+ * Called by a connected drop list when dragging has stopped.
603
+ * @param sibling Sibling whose dragging has stopped.
604
+ */
605
+ _stopReceiving ( sibling : DropListRef ) {
606
+ this . _activeSiblings . delete ( sibling ) ;
607
+ }
617
608
}
618
609
619
610
0 commit comments