From ed80791a5fac47fe7ca3e24e48ae8e325ad7acf9 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 9 Dec 2025 15:53:46 -0500 Subject: [PATCH 1/3] fix(content): add an internal method to recalculate dimensions --- core/src/components.d.ts | 4 ++++ core/src/components/content/content.tsx | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 7721801eb60..5e47667392b 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -868,6 +868,10 @@ export namespace Components { * Get the element where the actual scrolling takes place. This element can be used to subscribe to `scroll` events or manually modify `scrollTop`. However, it's recommended to use the API provided by `ion-content`: i.e. Using `ionScroll`, `ionScrollStart`, `ionScrollEnd` for scrolling events and `scrollToPoint()` to scroll the content into a certain point. */ "getScrollElement": () => Promise; + /** + * Recalculate content dimensions. Called by overlays (e.g., popover) when sibling elements like headers or footers have finished rendering and their heights are available, ensuring accurate offset-top calculations. + */ + "recalculateDimensions": () => Promise; /** * Scroll by a specified X/Y distance in the component. * @param x The amount to scroll by on the horizontal axis. diff --git a/core/src/components/content/content.tsx b/core/src/components/content/content.tsx index 74e44c63597..361939f7c27 100644 --- a/core/src/components/content/content.tsx +++ b/core/src/components/content/content.tsx @@ -254,6 +254,17 @@ export class Content implements ComponentInterface { } } + /** + * Recalculate content dimensions. Called by overlays (e.g., popover) when + * sibling elements like headers or footers have finished rendering and their + * heights are available, ensuring accurate offset-top calculations. + * @internal + */ + @Method() + async recalculateDimensions(): Promise { + readTask(() => this.readDimensions()); + } + private readDimensions() { const page = getPageElement(this.el); const top = Math.max(this.el.offsetTop, 0); From de3ab99895d3f10ce42dba76a8a5d109faa5e0c4 Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Tue, 9 Dec 2025 17:23:47 -0500 Subject: [PATCH 2/3] fix(popover): recalculate the content dimensions after the header has a height --- core/src/components/popover/popover.tsx | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index 9b07ab2f275..7429b3d06eb 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -491,6 +491,8 @@ export class Popover implements ComponentInterface, PopoverInterface { inline ); + this.recalculateContentOnHeaderReady(); + if (!this.keyboardEvents) { this.configureKeyboardInteraction(); } @@ -540,6 +542,38 @@ export class Popover implements ComponentInterface, PopoverInterface { unlock(); } + /** + * Watch the header for height changes and trigger content dimension + * recalculation when the header has a height > 0. This sets the offset-top + * of the content to the height of the header correctly. + */ + private recalculateContentOnHeaderReady() { + const popoverContent = this.el.shadowRoot?.querySelector('.popover-content'); + if (!popoverContent) { + return; + } + + const contentContainer = this.usersElement || popoverContent; + + const header = contentContainer.querySelector('ion-header') as HTMLElement | null; + const contentElements = contentContainer.querySelectorAll('ion-content'); + + if (!header || contentElements.length === 0) { + return; + } + + const ro = new ResizeObserver(async () => { + if (header.offsetHeight > 0) { + ro.disconnect(); + for (const contentEl of contentElements) { + await contentEl.recalculateDimensions(); + } + } + }); + + ro.observe(header); + } + /** * Dismiss the popover overlay after it has been presented. * This is a no-op if the overlay has not been presented yet. If you want From 7fa3edeb476ef39c3b672b5b6b2fdeb5b3cbeedd Mon Sep 17 00:00:00 2001 From: Brandy Smith <6577830+brandyscarney@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:54:25 -0500 Subject: [PATCH 3/3] fix(popover): disconnect ResizeObserver in disconnectedCallback --- core/src/components/popover/popover.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index 7429b3d06eb..af0e613297d 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -64,6 +64,7 @@ export class Popover implements ComponentInterface, PopoverInterface { private destroyTriggerInteraction?: () => void; private destroyKeyboardInteraction?: () => void; private destroyDismissInteraction?: () => void; + private headerResizeObserver?: ResizeObserver; private inline = false; private workingDelegate?: FrameworkDelegate; @@ -361,6 +362,11 @@ export class Popover implements ComponentInterface, PopoverInterface { if (destroyTriggerInteraction) { destroyTriggerInteraction(); } + + if (this.headerResizeObserver) { + this.headerResizeObserver.disconnect(); + this.headerResizeObserver = undefined; + } } componentWillLoad() { @@ -562,16 +568,17 @@ export class Popover implements ComponentInterface, PopoverInterface { return; } - const ro = new ResizeObserver(async () => { + this.headerResizeObserver = new ResizeObserver(async () => { if (header.offsetHeight > 0) { - ro.disconnect(); + this.headerResizeObserver?.disconnect(); + this.headerResizeObserver = undefined; for (const contentEl of contentElements) { await contentEl.recalculateDimensions(); } } }); - ro.observe(header); + this.headerResizeObserver.observe(header); } /**