@@ -115,6 +115,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
115
115
/** The last position to have been calculated as the best fit position. */
116
116
private _lastPosition : ConnectedPosition | null ;
117
117
118
+ /** The last calculated scroll visibility. Only tracked */
119
+ private _lastScrollVisibility : ScrollingVisibility | null ;
120
+
118
121
/** Subject that emits whenever the position changes. */
119
122
private readonly _positionChanges = new Subject < ConnectedOverlayPositionChange > ( ) ;
120
123
@@ -710,18 +713,28 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
710
713
this . _addPanelClasses ( position . panelClass ) ;
711
714
}
712
715
713
- // Save the last connected position in case the position needs to be re-calculated.
714
- this . _lastPosition = position ;
715
-
716
716
// Notify that the position has been changed along with its change properties.
717
717
// We only emit if we've got any subscriptions, because the scroll visibility
718
718
// calculations can be somewhat expensive.
719
719
if ( this . _positionChanges . observers . length ) {
720
- const scrollableViewProperties = this . _getScrollVisibility ( ) ;
721
- const changeEvent = new ConnectedOverlayPositionChange ( position , scrollableViewProperties ) ;
722
- this . _positionChanges . next ( changeEvent ) ;
720
+ const scrollVisibility = this . _getScrollVisibility ( ) ;
721
+
722
+ // We're recalculating on scroll, but we only want to emit if anything
723
+ // changed since downstream code might be hitting the `NgZone`.
724
+ if (
725
+ position !== this . _lastPosition ||
726
+ ! this . _lastScrollVisibility ||
727
+ ! compareScrollVisibility ( this . _lastScrollVisibility , scrollVisibility )
728
+ ) {
729
+ const changeEvent = new ConnectedOverlayPositionChange ( position , scrollVisibility ) ;
730
+ this . _positionChanges . next ( changeEvent ) ;
731
+ }
732
+
733
+ this . _lastScrollVisibility = scrollVisibility ;
723
734
}
724
735
736
+ // Save the last connected position in case the position needs to be re-calculated.
737
+ this . _lastPosition = position ;
725
738
this . _isInitialRender = false ;
726
739
}
727
740
@@ -1289,6 +1302,20 @@ function getRoundedBoundingClientRect(clientRect: Dimensions): Dimensions {
1289
1302
} ;
1290
1303
}
1291
1304
1305
+ /** Returns whether two `ScrollingVisibility` objects are identical. */
1306
+ function compareScrollVisibility ( a : ScrollingVisibility , b : ScrollingVisibility ) : boolean {
1307
+ if ( a === b ) {
1308
+ return true ;
1309
+ }
1310
+
1311
+ return (
1312
+ a . isOriginClipped === b . isOriginClipped &&
1313
+ a . isOriginOutsideView === b . isOriginOutsideView &&
1314
+ a . isOverlayClipped === b . isOverlayClipped &&
1315
+ a . isOverlayOutsideView === b . isOverlayOutsideView
1316
+ ) ;
1317
+ }
1318
+
1292
1319
export const STANDARD_DROPDOWN_BELOW_POSITIONS : ConnectedPosition [ ] = [
1293
1320
{ originX : 'start' , originY : 'bottom' , overlayX : 'start' , overlayY : 'top' } ,
1294
1321
{ originX : 'start' , originY : 'top' , overlayX : 'start' , overlayY : 'bottom' } ,
0 commit comments