base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[useAnchorPositioning] Add `collisionAvoidance` prop

Open atomiks opened this issue 8 months ago • 3 comments

Closes #1546

New options in the demo: https://deploy-preview-1849--base-ui.netlify.app/experiments/anchor-positioning

The collisionAvoidance prop allows users to control the collision preference. The default setting is:

{
  side: 'flip',
  align: 'flip',
  fallbackAxisSide: 'end',
}

This means:

  • For side collision avoidance, prefer to flip e.g. from bottom to top.
  • For align collision avoidance, prefer to flip e.g. from end to start.
  • If the chosen side axis doesn't fit (e.g. left / right on narrow viewports), choose the logical end side. So if you specify side="left", there's no need to worry about it overflowing on narrow viewports since it will flip to bottom (end) by default. @floating-ui/[email protected] solves the viewport size scenario in #1769 when you specify crossAxis: 'alignment', which works better if shift() is being used.

If you specify:

{
  align: 'shift',
}

That solves #1546, since it will shift on the align axis instead.

If you want Select in the alignItemWithTrigger=false mode to work like Material UI's, then you can specify:

{
  side: 'shift',
}

So it won't flip, but rather overlap the trigger

atomiks avatar May 02 '25 10:05 atomiks

Deploy Preview for base-ui ready!

Name Link
Latest commit 5cd5ebba1e589bfaaf6b701485c8b112da1f59f7
Latest deploy log https://app.netlify.com/projects/base-ui/deploys/683541e8bb31fb0008094d01
Deploy Preview https://deploy-preview-1849--base-ui.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

netlify[bot] avatar May 02 '25 10:05 netlify[bot]

Open in StackBlitz

npm i https://pkg.pr.new/@base-ui-components/react@1849

commit: 5cd5ebb

pkg-pr-new[bot] avatar May 05 '25 22:05 pkg-pr-new[bot]

This config object has one impossible state:

{
  side: 'shift',
  align: 'flip',
}

align can't flip when side prefers shift, though the reverse is possible (align=shift, side=flip). I don't think that setting is useful, but the API shouldn't allow such states


A discriminated union can handle this by preventing some strings from being set in combination with others:

interface SideFlipMode {
  side?: 'flip' | 'none';
  align?: 'flip' | 'shift' | 'none';
  fallbackAxisSide?: 'start' | 'end' | 'none';
}

interface SideShiftMode {
  side?: 'shift' | 'none';
  align?: 'shift' | 'none';
  fallbackAxisSide?: 'start' | 'end' | 'none';
}

export type CollisionAvoidance = SideFlipMode | SideShiftMode;

atomiks avatar May 07 '25 12:05 atomiks

@atomiks – I finally got to test this and it's more than I could ask for, thank you so much! 🫶 Only issue is the docs don't specify the default:

CleanShot 2025-05-29 at 21 22 32@2x

benface avatar May 30 '25 01:05 benface

@benface we're overhauling the props tables to be way more user friendly soon, so this will be fixed (it's not possible to display long defaults/types/examples etc in the current setup.)

atomiks avatar May 30 '25 01:05 atomiks