Skip to content

Commit bcb5cc0

Browse files
authored
Freeze overlay positioning when pinch zooming (#5612)
* rough fix for pinch zooming * fix tests * freeze positioning * cleanup from rebase * more progress but turns out the updated position code breaks stuff in safari pinch zooming + opening a overlay in safari breaks now * remove some comments after testing, punt for later * remove erroneous package changes * fix erroneous package.json edit
1 parent 955654b commit bcb5cc0

File tree

1 file changed

+25
-4
lines changed

1 file changed

+25
-4
lines changed

packages/@react-aria/overlays/src/useOverlayPosition.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import {calculatePosition, PositionResult} from './calculatePosition';
1414
import {DOMAttributes} from '@react-types/shared';
1515
import {Placement, PlacementAxis, PositionProps} from '@react-types/overlays';
16-
import {RefObject, useCallback, useRef, useState} from 'react';
16+
import {RefObject, useCallback, useEffect, useRef, useState} from 'react';
1717
import {useCloseOnScroll} from './useCloseOnScroll';
1818
import {useLayoutEffect, useResizeObserver} from '@react-aria/utils';
1919
import {useLocale} from '@react-aria/i18n';
@@ -124,11 +124,25 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
124124
arrowSize
125125
];
126126

127+
// Note, the position freezing breaks if body sizes itself dynamicly with the visual viewport but that might
128+
// just be a non-realistic use case
129+
// Upon opening a overlay, record the current visual viewport scale so we can freeze the overlay styles
130+
let lastScale = useRef(visualViewport?.scale);
131+
useEffect(() => {
132+
if (isOpen) {
133+
lastScale.current = visualViewport?.scale;
134+
}
135+
}, [isOpen]);
136+
127137
let updatePosition = useCallback(() => {
128138
if (shouldUpdatePosition === false || !isOpen || !overlayRef.current || !targetRef.current || !scrollRef.current || !boundaryElement) {
129139
return;
130140
}
131141

142+
if (visualViewport?.scale !== lastScale.current) {
143+
return;
144+
}
145+
132146
let position = calculatePosition({
133147
placement: translateRTL(placement, direction),
134148
overlayNode: overlayRef.current,
@@ -183,12 +197,19 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
183197
updatePosition();
184198
};
185199

186-
visualViewport?.addEventListener('resize', onResize);
187-
visualViewport?.addEventListener('scroll', onResize);
200+
// Only reposition the overlay if a scroll event happens immediately as a result of resize (aka the virtual keyboard has appears)
201+
// We don't want to reposition the overlay if the user has pinch zoomed in and is scrolling the viewport around.
202+
let onScroll = () => {
203+
if (isResizing.current) {
204+
onResize();
205+
}
206+
};
188207

208+
visualViewport?.addEventListener('resize', onResize);
209+
visualViewport?.addEventListener('scroll', onScroll);
189210
return () => {
190211
visualViewport?.removeEventListener('resize', onResize);
191-
visualViewport?.removeEventListener('scroll', onResize);
212+
visualViewport?.removeEventListener('scroll', onScroll);
192213
};
193214
}, [updatePosition]);
194215

0 commit comments

Comments
 (0)