-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Problem
usePopover.ts hardcodes whileElementsMounted: autoUpdate with no options:
const data = useFloating({
// ...
whileElementsMounted: autoUpdate,
});This enables all default autoUpdate behaviors: ancestorScroll, ancestorResize, elementResize, and layoutShift. There is no way for consumers to customize this.
The layoutShift detection (which uses IntersectionObserver to track reference element drift) can create a positioning feedback loop in certain layouts — for example, when a PopoverNext opens near a live-resizing element like a MapLibre GL canvas that uses ResizeObserver to call map.resize(). The cycle is:
- Popover opens →
autoUpdatepositions it → sub-pixel layout shift - Adjacent element's
ResizeObserverfires → triggers resize/relayout autoUpdate'slayoutShiftobserver detects the reference moved → repositions- Goto 2 — visible as screen "vibration"
This was not an issue with the legacy Popover (Popper.js) because it used scheduleUpdate on demand rather than continuous observation.
Proposed solution
Add an optional autoUpdateOptions prop to PopoverNextProps:
interface PopoverNextProps {
/** Options forwarded to floating-ui's autoUpdate. All enabled by default. */
autoUpdateOptions?: AutoUpdateOptions;
// ...
}And in usePopover.ts:
const data = useFloating({
// ...
whileElementsMounted: autoUpdateOptions
? (reference, floating, update) => autoUpdate(reference, floating, update, autoUpdateOptions)
: autoUpdate,
});This lets consumers disable specific behaviors when needed:
<PopoverNext autoUpdateOptions={{ layoutShift: false }}>Workaround
We're currently using a yarn patch to disable layoutShift globally in usePopover.ts.