usePopoverPositioning

The measure-and-place engine under useFloating: attach anchorRef (and popoverRef) and it returns where the popover fits in the viewport — coordinates, the side it landed on after flipping, and the space available. useTooltipPositioning is a preset with tooltip defaults, and useDropdownPositioning adds showOverlay / hideOverlay to render the result through the overlay host; for most uses, reach for useFloating instead.
usePopoverPositioning(isOpen, options) returns { position, anchorRef, popoverRef, updatePosition, isPositioning }; while isOpen is true, position holds the popover's x / y in viewport coordinates, the physical placement (after RTL mirroring and flipping), flipped / shifted and maxWidth / maxHeight, and it is null while closed. Until popoverRef is on a rendered element the popover is assumed to be 200px wide with no height, and drawing it at those coordinates (position: fixed on web, a portal on native) is up to you, which is exactly what useFloating handles. It re-computes every frame while the page scrolls or resizes on web and after rotation on native, and with keyboardAvoidance (on by default) it keeps clear of the keyboard KeyboardManagerProvider reports. useTooltipPositioning(isOpen, placement) presets flip, shift, an 8px offset and boundary, while useDropdownPositioning({ isOpen, onClose, …options }) also returns showOverlay(content) / hideOverlay() and closes the overlay when isOpen turns false.

Loading demo…

import { useState } from 'react'; import { Block, Button, Text, usePopoverPositioning } from '@plocks/ui'; export function Demo() { const [measuring, setMeasuring] = useState(false); const { anchorRef, position } = usePopoverPositioning(measuring, { placement: 'top', offset: 8, }); return ( <Block align="center"> <Button ref={anchorRef} onPress={() => setMeasuring((current) => !current)}> {measuring ? 'Stop' : 'Measure'} </Button> {position ? ( <Text size="sm" ff="monospace"> {position.placement} · x {Math.round(position.x)} · y {Math.round(position.y)} </Text> ) : null} </Block> ); }