From fb4d7a06de0ee884f1d2e204f9da5f5417160075 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Jul 2022 17:59:55 +0200 Subject: [PATCH] fix(material-experimental/mdc-slider): thumb incorrectly positioned when inside an overlay (#25288) Fixes an issue where the thumb of an MDC slider wasn't positioned correctly when it has an initial value inside an overlay that is being animated. There are multiple ways to resolve this, but I ended up doing it by removing our logic that skips the first `ResizeObserver` emission since we can get around the performance issues with the `layout` call by reusing the dimensions provided in the callback. Fixes #25286. --- .../mdc-slider/slider.ts | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/src/material-experimental/mdc-slider/slider.ts b/src/material-experimental/mdc-slider/slider.ts index 789883465a48..4f968bf9a337 100644 --- a/src/material-experimental/mdc-slider/slider.ts +++ b/src/material-experimental/mdc-slider/slider.ts @@ -702,6 +702,9 @@ export class MatSlider /** Timeout used to debounce resize listeners. */ private _resizeTimer: number; + /** Cached dimensions of the host element. */ + private _cachedHostRect: DOMRect | null; + constructor( readonly _ngZone: NgZone, readonly _cdr: ChangeDetectorRef, @@ -941,6 +944,11 @@ export class MatSlider return this.disabled || this.disableRipple || !!this._globalRippleOptions?.disabled; } + /** Gets the dimensions of the host element. */ + _getHostDimensions() { + return this._cachedHostRect || this._elementRef.nativeElement.getBoundingClientRect(); + } + /** Starts observing and updating the slider if the host changes its size. */ private _observeHostResize() { if (typeof ResizeObserver === 'undefined' || !ResizeObserver) { @@ -949,18 +957,18 @@ export class MatSlider // MDC only updates the slider when the window is resized which // doesn't capture changes of the container itself. We use a resize - // observer to ensure that the layout is correct (see #24590). + // observer to ensure that the layout is correct (see #24590 and #25286). this._ngZone.runOutsideAngular(() => { - // The callback will fire as soon as an element is observed and - // we only want to know after the initial layout. - let hasResized = false; - this._resizeObserver = new ResizeObserver(() => { - if (hasResized) { - // Debounce the layouts since they can happen frequently. - clearTimeout(this._resizeTimer); - this._resizeTimer = setTimeout(this._layout, 50); - } - hasResized = true; + this._resizeObserver = new ResizeObserver(entries => { + clearTimeout(this._resizeTimer); + this._resizeTimer = setTimeout(() => { + // The `layout` call is going to call `getBoundingClientRect` to update the dimensions + // of the host. Since the `ResizeObserver` already calculated them, we can save some + // work by returning them instead of having to check the DOM again. + this._cachedHostRect = entries[0]?.contentRect; + this._layout(); + this._cachedHostRect = null; + }, 50); }); this._resizeObserver.observe(this._elementRef.nativeElement); }); @@ -1130,7 +1138,7 @@ class SliderAdapter implements MDCSliderAdapter { return this._delegate._getThumbElement(thumbPosition).getBoundingClientRect(); }; getBoundingClientRect = (): DOMRect => { - return this._delegate._elementRef.nativeElement.getBoundingClientRect(); + return this._delegate._getHostDimensions(); }; getValueIndicatorContainerWidth = (thumbPosition: Thumb): number => { return this._delegate._getValueIndicatorContainerElement(thumbPosition).getBoundingClientRect()