ionic-framework icon indicating copy to clipboard operation
ionic-framework copied to clipboard

bug: popover position does not update when viewport is resized or rotated

Open WolfWalter opened this issue 2 years ago • 4 comments

Prerequisites

Ionic Framework Version

  • [X] v4.x
  • [X] v5.x
  • [X] v6.x

Current Behavior

Same issue as described in https://github.com/ionic-team/ionic-framework/issues/10139 which was not resolved. After resize or screen orientation change, the popover can be misplaced or even be outside of the visible area.

This could also be triggered if there is a layout shift. For example if the popover has no backdrop and the user toggles the ion-split-pane.

Expected Behavior

Popoverer gets moved when resized or the screen orientation changes. If this is too complicated at least close the popover on resize.

Steps to Reproduce

Run the example from the docs with chrome and check the behavior on resize.

Code Reproduction URL

https://raw.githubusercontent.com/ionic-team/ionic-docs/main/static/demos/api/popover/index.html

Ionic Info

Ionic:

Ionic CLI : 6.19.0 (/home/wolf/.config/yarn/global/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 6.0.13 @angular-devkit/build-angular : 13.3.0 @angular-devkit/schematics : 13.3.0 @angular/cli : 13.3.0 @ionic/angular-toolkit : 6.1.0

Capacitor:

Capacitor CLI : 3.4.3 @capacitor/android : 3.4.3 @capacitor/core : 3.4.3 @capacitor/ios : 3.4.3

Utility:

cordova-res : not installed globally native-run : 1.5.0

System:

NodeJS : v14.19.1 (/usr/bin/node) npm : 6.14.16 OS : Linux 5.13

Additional Information

I tired to implement the "close popover on resize" behavior with a ResizeObserver, but had trouble when using a ion-select with popover interface. There seems to be no way to close that popover programmatically.

WolfWalter avatar Mar 30 '22 15:03 WolfWalter

I noticed that when the ion-popover is on the left side of the window, it sticks with its trigger. But when the ion-popover is in the middle or right side it does not stick with its trigger when the window is resized.

Code Reproduction URL: https://stackblitz.com/edit/angular-rduzd8-9shlka?file=src/components/Example.vue

Any workarounds or updates on a fix?

MaxWeisen avatar Jan 04 '23 01:01 MaxWeisen

The popover behaves very buggy when the viewport is resized or rotated (orientation). The position is not updating. The popover can move outside the view and overflow or change the parents size.

My workaround is to close all popovers when screen size/orientation changes. It's better than unpredictable popover which appears outside the screen or whatever. I think this is an issue that should be given higher priority.

infacto avatar Jun 07 '23 09:06 infacto

Closing is not an option for us since we show a loader and progress. Is there an internal "updatePosition" that I can trigger on resize/rotate?

muuvmuuv avatar Jan 10 '24 10:01 muuvmuuv

For someone to pick up:

  • presents calls animations which sets the absolute position: https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/popover.tsx#L499
  • its called here: https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/overlays.ts#L609
  • For iOS it is set here (android in same folder): https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/popover/animations/ios.enter.ts#L137C7-L137C16

I think the present function need to keep the position centered after the animation has run. But window events are usually set with @Listen so idk what is the proper way here.

Little tinkering start:

export const centerPopover = (baseEl: HTMLElement, opts?: any) => {
	// re-use stuff from ios.enter and md.enter
	
	const {
	  top,
	  left
	} = calculateWindowAdjustment(/* ... */)
	
	contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
	contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
}

// Idk if it would make more sense to have this in the popover component itself
// because you have @Listen('resize', { target: 'window' })
export const handleOrientationChanges = (el: HTMLElement, opts?: any) => {
	window.addEventListener('orientationchange', () => {
		centerPopover(el, opts);
	})
	window.addEventListener('resize', () => {
		centerPopover(el, opts);
	})
	// TODO: remove listeners on popover dismiss
}

// core/src/utils/overlay.ts

export const present = async (/* ... */) => {
	const completed = await overlayAnimation(overlay, animationBuilder, overlay.el, opts);
	if (completed) {
		overlay.didPresent.emit();
		overlay.didPresentShorthand?.emit();
		handleOrientationChanges(overlay.el, opts);
	}
}

muuvmuuv avatar Jan 10 '24 11:01 muuvmuuv