/** * Anchor positioning for top-layer popovers. See * `notes/architecture/positioning.md` for the full model. * * Two runtime paths: CSS Anchor Positioning (modern browsers) and a JS * fallback that measures the trigger and writes `top`/`left`. * * Both paths honour `offset.gap` and `offset.crossAxisShift`. The * CSS path passes the values through as strings; the JS fallback resolves * them to pixels via a hidden DOM probe (see `resolveCssLengthToPixels`) * so tokens, `calc()`, `var()`, etc all work. The JS fallback hides the * popover with `opacity: 0` until the first measurement completes, so it * is never painted at the wrong location. */ import { type RefObject } from 'react'; import { type TPlacementOptions } from '../internal/resolve-placement'; /** * Hook that positions an element relative to an anchor element using * CSS Anchor Positioning (with a JS fallback for older browsers). * * This hook is the positioning primitive. It has no knowledge of popovers, * visibility, or animation. Compose it with `Popover` for anchor-positioned * top-layer content. * * When CSS Anchor Positioning is supported, it sets CSS properties * (`anchor-name`, `position-anchor`, `position-area`, `position-try-fallbacks`) * directly on the elements via `el.style.setProperty()`. * * When not supported, it falls back to JavaScript-based positioning using * `position: fixed` with measured coordinates, re-running on scroll (capture) * and resize events. */ export declare function useAnchorPosition({ anchorRef, popoverRef, placement, forceFallbackPositioning, isEnabled, isOpen, }: { /** * Element to position relative to. */ anchorRef: RefObject; /** * Element being positioned (typically a popover). * * The popover host may be unmounted between opens (the `Popover` * primitive unmounts after its exit animation). Pass `isOpen` so the * positioning effect re-runs against the freshly mounted element on * the next open. Without it, the effect's bound listeners and styles * would target the previous (detached) element. */ popoverRef: RefObject; /** * Where to place the element relative to the anchor. * `offset.gap` and `offset.crossAxisShift` are part of the * placement object. * * All fields are optional. Omitted fields fall back to: * - `axis`: `'block'` * - `edge`: `'end'` * - `align`: `'center'` * - `offset.gap`: `token('space.100', '8px')` * - `offset.crossAxisShift.value`: `'0px'` * - `offset.crossAxisShift.direction`: `'forwards'` * * The full default placement (`{}`) renders the popover centered * below the trigger with one `space.100` of gap and no cross-axis * shift. * * `offset.gap` and `offset.crossAxisShift.value` accept either a * number (pixels) or a CSS length string (eg `token('space.200')`). */ placement?: TPlacementOptions; /** * Forces the JavaScript positioning fallback even when the browser * supports CSS Anchor Positioning. Useful for testing fallback * behavior in any environment, including production. */ forceFallbackPositioning?: boolean; /** * When `false`, the hook is a no-op and applies no positioning. * Defaults to `true`. */ isEnabled?: boolean; /** * Whether the popover is currently open. Drives a re-run of the * positioning effect when the popover host element is unmounted * and remounted across open cycles, so listeners and styles are * always wired to the live host element. */ isOpen: boolean; }): void;