computePlacement
Position a floating panel (dropdown, popover, tooltip) relative to an anchor rect: flips to the
opposite side when the preferred side lacks room and the opposite side has more, then shifts
along the cross axis to stay within a boundary. Mirrors Floating UI's flip/shift middleware,
minus the dependency.
Pure geometry: it never touches the DOM itself. Pass in getBoundingClientRect() results and
it hands back the coordinates to write.
Usage
import { computePlacement } from 'ranuts/utils';
const anchorRect = trigger.getBoundingClientRect();
const { top, left, placement } = computePlacement({
anchor: anchorRect,
floating: { width: panel.offsetWidth, height: panel.offsetHeight },
placement: 'bottom',
offset: 4,
});
panel.style.position = 'absolute';
panel.style.top = `${top + window.scrollY}px`;
panel.style.left = `${left + window.scrollX}px`;
// `placement` is the side actually used, after flip — use it to pick an entrance
// animation class or an arrow direction.API
computePlacement
Parameters
| Parameter | Description | Type | Default |
|---|---|---|---|
options.anchor |
Anchor (trigger) rect, in viewport coordinates (e.g. getBoundingClientRect()) |
{ top, left, width, height } |
Required |
options.floating |
The floating panel's own size | { width, height } |
Required |
options.placement |
Preferred side. Flips to the opposite side when it lacks room and the opposite side has more | 'top' | 'bottom' | 'left' | 'right' |
Required |
options.offset |
Gap kept between the anchor and the floating panel, in px | number |
0 |
options.boundary |
Region the panel must stay inside, in viewport coordinates | { top, left, width, height } |
Window viewport |
options.padding |
Minimum gap kept between the panel and the boundary edge when shifting, in px | number |
8 |
Return
| Argument | Description | Type |
|---|---|---|
top |
Resolved top, in the same coordinate space as anchor |
number |
left |
Resolved left, in the same coordinate space as anchor |
number |
placement |
The side actually used, after flip | 'top' | 'bottom' | 'left' | 'right' |
Notes
- Coordinates are viewport-relative throughout, the same space as
anchor. If the panel is positioned withposition: absoluteagainst the document, addscrollX/scrollYyourself when writing the style (see the usage example above). - No real layout, no flip/shift. When
anchororfloatinghas a zero width or height (jsdom, which never performs actual layout, or a panel read before its content has settled), the space calculations would spuriously "detect" a collision on every call. SocomputePlacementskips flip/shift entirely and returns the caller's preferredplacementas-is. - Shift is skipped when the panel is larger than the boundary itself: clamping would only move it further off-screen the other way.
- Used internally by
ranui'sr-popoverandr-selectto keep a body-portaled dropdown on screen.