Skip to content

Commit

Permalink
fix(cdk/overlay): only emit positionChanges when position is different
Browse files Browse the repository at this point in the history
Currently we emit the `positionChanges` event whenever a position is recalculcated which can be on each scroll event. These changes switch to doing so only if either the actual position or the scrolled state has changed.

(cherry picked from commit 01cc6f3)
  • Loading branch information
crisbeto committed Feb 21, 2024
1 parent d7c062a commit 67956e0
Showing 1 changed file with 33 additions and 6 deletions.
39 changes: 33 additions & 6 deletions src/cdk/overlay/position/flexible-connected-position-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
/** The last position to have been calculated as the best fit position. */
private _lastPosition: ConnectedPosition | null;

/** The last calculated scroll visibility. Only tracked */
private _lastScrollVisibility: ScrollingVisibility | null;

/** Subject that emits whenever the position changes. */
private readonly _positionChanges = new Subject<ConnectedOverlayPositionChange>();

Expand Down Expand Up @@ -710,18 +713,28 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy {
this._addPanelClasses(position.panelClass);
}

// Save the last connected position in case the position needs to be re-calculated.
this._lastPosition = position;

// Notify that the position has been changed along with its change properties.
// We only emit if we've got any subscriptions, because the scroll visibility
// calculations can be somewhat expensive.
if (this._positionChanges.observers.length) {
const scrollableViewProperties = this._getScrollVisibility();
const changeEvent = new ConnectedOverlayPositionChange(position, scrollableViewProperties);
this._positionChanges.next(changeEvent);
const scrollVisibility = this._getScrollVisibility();

// We're recalculating on scroll, but we only want to emit if anything
// changed since downstream code might be hitting the `NgZone`.
if (
position !== this._lastPosition ||
!this._lastScrollVisibility ||
!compareScrollVisibility(this._lastScrollVisibility, scrollVisibility)
) {
const changeEvent = new ConnectedOverlayPositionChange(position, scrollVisibility);
this._positionChanges.next(changeEvent);
}

this._lastScrollVisibility = scrollVisibility;
}

// Save the last connected position in case the position needs to be re-calculated.
this._lastPosition = position;
this._isInitialRender = false;
}

Expand Down Expand Up @@ -1289,6 +1302,20 @@ function getRoundedBoundingClientRect(clientRect: Dimensions): Dimensions {
};
}

/** Returns whether two `ScrollingVisibility` objects are identical. */
function compareScrollVisibility(a: ScrollingVisibility, b: ScrollingVisibility): boolean {
if (a === b) {
return true;
}

return (
a.isOriginClipped === b.isOriginClipped &&
a.isOriginOutsideView === b.isOriginOutsideView &&
a.isOverlayClipped === b.isOverlayClipped &&
a.isOverlayOutsideView === b.isOverlayOutsideView
);
}

export const STANDARD_DROPDOWN_BELOW_POSITIONS: ConnectedPosition[] = [
{originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top'},
{originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom'},
Expand Down

0 comments on commit 67956e0

Please sign in to comment.