diff --git a/src/components-lib/media-height-controller.ts b/src/components-lib/media-height-controller.ts index 06ae0341..374904d9 100644 --- a/src/components-lib/media-height-controller.ts +++ b/src/components-lib/media-height-controller.ts @@ -53,6 +53,16 @@ export class MediaHeightController { this._debouncedSetHeight(); } + // Recalculate the height. This is necessary because ResizeObserver may not + // fire if the height of the media is constrained by the host's own max-height + // (e.g. during initial load, everything is 100% height of the host, so the + // rendered height never changes and thus ResizeObserver never fires). This + // manual "wakeup" allows the controller to temporarily lift the constraint + // and peek at the true desired height. + public recalculate(): void { + this._debouncedSetHeight(); + } + public destroy(): void { this._mutationObserver.disconnect(); this._resizeObserver.disconnect(); @@ -76,6 +86,7 @@ export class MediaHeightController { // Calculate the true height. const selectedHeight = this._selectedChild.getBoundingClientRect().height; + const hostHeight = this._host.getBoundingClientRect().height; // Reset the original height so that browser transition animation can be // applied from the current to the target. @@ -85,7 +96,12 @@ export class MediaHeightController { this._selectedChild.getBoundingClientRect(); if (selectedHeight && !isNaN(selectedHeight) && selectedHeight > 0) { - this._host.style.maxHeight = `${selectedHeight}px`; + // Set the height to the larger of the selected child or the host itself. + // This ensures that the host (the carousel) expands to fit the media, but + // does not shrink smaller than itself (which would cause a "flash" or + // jump). + // See: https://github.com/dermotduffy/advanced-camera-card/issues/2109 + this._host.style.maxHeight = `${Math.max(selectedHeight, hostHeight)}px`; } } diff --git a/src/components/live/carousel.ts b/src/components/live/carousel.ts index dffb4de7..567eba34 100644 --- a/src/components/live/carousel.ts +++ b/src/components/live/carousel.ts @@ -344,6 +344,7 @@ export class AdvancedCameraCardLiveCarousel extends LitElement { @advanced-camera-card:carousel:select=${this._setViewHandler.bind(this)} @advanced-camera-card:media:loaded=${() => { this._mediaHasLoaded = true; + this._mediaHeightController.recalculate(); }} @advanced-camera-card:media:unloaded=${() => { this._mediaHasLoaded = false; diff --git a/src/components/viewer/carousel.ts b/src/components/viewer/carousel.ts index b9cc6ff5..900fe197 100644 --- a/src/components/viewer/carousel.ts +++ b/src/components/viewer/carousel.ts @@ -346,6 +346,7 @@ export class AdvancedCameraCardViewerCarousel extends LitElement { }} @advanced-camera-card:media:loaded=${(ev: CustomEvent) => { this._loadedMediaPlayerController = ev.detail.mediaPlayerController ?? null; + this._mediaHeightController.recalculate(); this._seekHandler(); }} @advanced-camera-card:media:unloaded=${() => { diff --git a/tests/components-lib/media-height-controller.test.ts b/tests/components-lib/media-height-controller.test.ts index cd821d5b..73076c97 100644 --- a/tests/components-lib/media-height-controller.test.ts +++ b/tests/components-lib/media-height-controller.test.ts @@ -139,30 +139,29 @@ describe('MediaHeightController', () => { expect(host.style.maxHeight).toBe('200px'); }); - it('should ignore new chil to new children being added', () => { + it('should set height on recalculate', () => { const host = document.createElement('div'); const controller = new MediaHeightController(host, 'div'); const root = document.createElement('div'); - const child_0 = document.createElement('div'); - child_0.getBoundingClientRect = vi.fn().mockReturnValue({ - height: 100, + const child = document.createElement('div'); + child.getBoundingClientRect = vi.fn().mockReturnValue({ + height: 700, }); - root.appendChild(child_0); + root.appendChild(child); controller.setRoot(root); + controller.setSelected(0); - const child_1 = document.createElement('div'); - child_1.getBoundingClientRect = vi.fn().mockReturnValue({ - height: 200, + expect(host.style.maxHeight).toBe('700px'); + + child.getBoundingClientRect = vi.fn().mockReturnValue({ + height: 900, }); - root.appendChild(child_1); - callMutationHandler(); + controller.recalculate(); - controller.setSelected(1); - - expect(host.style.maxHeight).toBe('200px'); + expect(host.style.maxHeight).toBe('900px'); }); });