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.
  • Loading branch information
crisbeto committed Feb 20, 2024
1 parent facd027 commit a2b5fb9
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 a2b5fb9

Please sign in to comment.