ionic-framework
ionic-framework copied to clipboard
bug: popover position does not update when viewport is resized or rotated
Prerequisites
- [X] I have read the Contributing Guidelines.
- [X] I agree to follow the Code of Conduct.
- [X] I have searched for existing issues that already report this problem, without success.
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.
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?
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.
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?
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);
}
}