8
8
9
9
import { PositionStrategy } from './position-strategy' ;
10
10
import { ElementRef } from '@angular/core' ;
11
- import { ViewportRuler , CdkScrollable } from '@angular/cdk/scrolling' ;
11
+ import { ViewportRuler , CdkScrollable , ViewportScrollPosition } from '@angular/cdk/scrolling' ;
12
12
import {
13
13
ConnectedOverlayPositionChange ,
14
14
ConnectionPositionPair ,
@@ -108,6 +108,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
108
108
/** Selector to be used when finding the elements on which to set the transform origin. */
109
109
private _transformOriginSelector : string ;
110
110
111
+ /** Amount by which the overlay was pushed in each axis during the last time it was positioned. */
112
+ private _previousPushAmount : { x : number , y : number } | null ;
113
+
111
114
/** Observable sequence of position changes. */
112
115
positionChanges : Observable < ConnectedOverlayPositionChange > =
113
116
this . _positionChanges . asObservable ( ) ;
@@ -269,6 +272,8 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
269
272
}
270
273
271
274
detach ( ) {
275
+ this . _lastPosition = null ;
276
+ this . _previousPushAmount = null ;
272
277
this . _resizeSubscription . unsubscribe ( ) ;
273
278
}
274
279
@@ -528,23 +533,37 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
528
533
* the viewport, the top-left corner will be pushed on-screen (with overflow occuring on the
529
534
* right and bottom).
530
535
*
531
- * @param start The starting point from which the overlay is pushed.
532
- * @param overlay The overlay dimensions.
536
+ * @param start Starting point from which the overlay is pushed.
537
+ * @param overlay Dimensions of the overlay.
538
+ * @param scrollPosition Current viewport scroll position.
533
539
* @returns The point at which to position the overlay after pushing. This is effectively a new
534
540
* originPoint.
535
541
*/
536
- private _pushOverlayOnScreen ( start : Point , overlay : ClientRect ) : Point {
542
+ private _pushOverlayOnScreen ( start : Point ,
543
+ overlay : ClientRect ,
544
+ scrollPosition : ViewportScrollPosition ) : Point {
545
+ // If the position is locked and we've pushed the overlay already, reuse the previous push
546
+ // amount, rather than pushing it again. If we were to continue pushing, the element would
547
+ // remain in the viewport, which goes against the expectations when position locking is enabled.
548
+ if ( this . _previousPushAmount && this . _positionLocked ) {
549
+ return {
550
+ x : start . x + this . _previousPushAmount . x ,
551
+ y : start . y + this . _previousPushAmount . y
552
+ } ;
553
+ }
554
+
537
555
const viewport = this . _viewportRect ;
538
556
539
- // Determine how much the overlay goes outside the viewport on each side, which we'll use to
540
- // decide which direction to push it.
557
+ // Determine how much the overlay goes outside the viewport on each
558
+ // side, which we'll use to decide which direction to push it.
541
559
const overflowRight = Math . max ( start . x + overlay . width - viewport . right , 0 ) ;
542
560
const overflowBottom = Math . max ( start . y + overlay . height - viewport . bottom , 0 ) ;
543
- const overflowTop = Math . max ( viewport . top - start . y , 0 ) ;
544
- const overflowLeft = Math . max ( viewport . left - start . x , 0 ) ;
561
+ const overflowTop = Math . max ( viewport . top - scrollPosition . top - start . y , 0 ) ;
562
+ const overflowLeft = Math . max ( viewport . left - scrollPosition . left - start . x , 0 ) ;
545
563
546
- // Amount by which to push the overlay in each direction such that it remains on-screen.
547
- let pushX , pushY = 0 ;
564
+ // Amount by which to push the overlay in each axis such that it remains on-screen.
565
+ let pushX = 0 ;
566
+ let pushY = 0 ;
548
567
549
568
// If the overlay fits completely within the bounds of the viewport, push it from whichever
550
569
// direction is goes off-screen. Otherwise, push the top-left corner such that its in the
@@ -561,6 +580,8 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
561
580
pushY = viewport . top - start . y ;
562
581
}
563
582
583
+ this . _previousPushAmount = { x : pushX , y : pushY } ;
584
+
564
585
return {
565
586
x : start . x + pushX ,
566
587
y : start . y + pushY ,
@@ -776,8 +797,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
776
797
const styles = { } as CSSStyleDeclaration ;
777
798
778
799
if ( this . _hasExactPosition ( ) ) {
779
- extendStyles ( styles , this . _getExactOverlayY ( position , originPoint ) ) ;
780
- extendStyles ( styles , this . _getExactOverlayX ( position , originPoint ) ) ;
800
+ const scrollPosition = this . _viewportRuler . getViewportScrollPosition ( ) ;
801
+ extendStyles ( styles , this . _getExactOverlayY ( position , originPoint , scrollPosition ) ) ;
802
+ extendStyles ( styles , this . _getExactOverlayX ( position , originPoint , scrollPosition ) ) ;
781
803
} else {
782
804
styles . position = 'static' ;
783
805
}
@@ -816,14 +838,16 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
816
838
}
817
839
818
840
/** Gets the exact top/bottom for the overlay when not using flexible sizing or when pushing. */
819
- private _getExactOverlayY ( position : ConnectedPosition , originPoint : Point ) {
841
+ private _getExactOverlayY ( position : ConnectedPosition ,
842
+ originPoint : Point ,
843
+ scrollPosition : ViewportScrollPosition ) {
820
844
// Reset any existing styles. This is necessary in case the
821
845
// preferred position has changed since the last `apply`.
822
846
let styles = { top : null , bottom : null } as CSSStyleDeclaration ;
823
847
let overlayPoint = this . _getOverlayPoint ( originPoint , this . _overlayRect , position ) ;
824
848
825
849
if ( this . _isPushed ) {
826
- overlayPoint = this . _pushOverlayOnScreen ( overlayPoint , this . _overlayRect ) ;
850
+ overlayPoint = this . _pushOverlayOnScreen ( overlayPoint , this . _overlayRect , scrollPosition ) ;
827
851
}
828
852
829
853
// We want to set either `top` or `bottom` based on whether the overlay wants to appear
@@ -841,14 +865,16 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
841
865
}
842
866
843
867
/** Gets the exact left/right for the overlay when not using flexible sizing or when pushing. */
844
- private _getExactOverlayX ( position : ConnectedPosition , originPoint : Point ) {
868
+ private _getExactOverlayX ( position : ConnectedPosition ,
869
+ originPoint : Point ,
870
+ scrollPosition : ViewportScrollPosition ) {
845
871
// Reset any existing styles. This is necessary in case the preferred position has
846
872
// changed since the last `apply`.
847
873
let styles = { left : null , right : null } as CSSStyleDeclaration ;
848
874
let overlayPoint = this . _getOverlayPoint ( originPoint , this . _overlayRect , position ) ;
849
875
850
876
if ( this . _isPushed ) {
851
- overlayPoint = this . _pushOverlayOnScreen ( overlayPoint , this . _overlayRect ) ;
877
+ overlayPoint = this . _pushOverlayOnScreen ( overlayPoint , this . _overlayRect , scrollPosition ) ;
852
878
}
853
879
854
880
// We want to set either `left` or `right` based on whether the overlay wants to appear "before"
0 commit comments