Skip to content

[Positioners] Callback values for side/align #1769

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
atomiks opened this issue Apr 23, 2025 · 1 comment
Open

[Positioners] Callback values for side/align #1769

atomiks opened this issue Apr 23, 2025 · 1 comment
Labels
new feature New feature or request

Comments

@atomiks
Copy link
Contributor

atomiks commented Apr 23, 2025

Feature request

Mentioned in #1665 (comment)

Summary

You sometimes want to change the side or align based on the viewport size (e.g. right/left sides never really work on mobile screens - you need to use a useMediaQuery hook to change it to top/bottom) or interaction type (e.g. context menus should be positioned on top, not bottom, to avoid the fingers obscuring it).

The fallbackAxisSideDirection option for flip() in use Floating UI is supposed to solve this by default for the viewport size scenario, but it has some issues with undesirable repositioning while scrolling, although this setting might still suffice for nested menus (positioned on the right for Menu and NavigationMenu) on narrow viewports

sideOffset and alignOffset take callbacks with the side/align/dimensions, so side and align taking a callback with the interaction type and viewport size would be consistent and useful.

@atomiks
Copy link
Contributor Author

atomiks commented Apr 30, 2025

The key problem with fallbackAxisSideDirection is that for edge-aligned placements (align="start|end"), it flips undesirably while scrolling. You don't want it to flip randomly in this scenario (what Vlad mentioned building the infotips on the docs), especially since it preserves the start align but that's the opposite of what's wanted here.

Screen.Recording.2025-04-30.at.6.23.20.pm.mov

In the above scenario, flip() too eagerly changes the side axis (you want shift to take over flip here instead). But edge aligned placements use this ordering:

// https://floating-ui.com/docs/flip#combining-with-shift
if (alignParam !== 'center') {
  middleware.push(flipMiddleware, shiftMiddleware);
} else {
  middleware.push(shiftMiddleware, flipMiddleware);
}

So flip has the priority over shift to change when fallbackAxisSideDirection !== 'none'.

For align="center" there's an easy fix that works well, because shift() has priority so no overflow occurs on the crossAxis anyway:

flip({
  crossAxis: false, // disable crossAxis
  fallbackAxisSideDirection: 'end',
})

Disabling crossAxis with align="center" means it will only flip the side to the different axis for left/right if the viewport is too narrow — exactly what we want. But by default, we do want to flip the align for edge aligned placements, so we can't disable crossAxis.

So what's why a callback for side to read viewportSize.width seems to be better as it's only reactive with respect to the viewport size rather than the overflow on any axis

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant