/** * Position results used to be memoised in an LRU keyed on rounded geometry. That * cache predated the per-frame scroll repositioning and actively worked against * it: the placement decision is now stateful (see `currentPlacement` hysteresis), * so a cache hit could resurrect a side the popover had already flipped away * from. The math here is a couple of dozen arithmetic ops — cheaper than the * key-building string join the cache needed — so it simply runs every time. * * Kept as an exported no-op because it is part of the public surface. */ export declare const clearOverlayPositionCache: () => void; export interface Rect { x: number; y: number; width: number; height: number; } export interface Viewport { width: number; height: number; padding: number; } export interface PositionResult { x: number; y: number; placement: PlacementType; maxWidth?: number; maxHeight?: number; /** Indicates if the popover was flipped to stay in bounds */ flipped: boolean; /** Indicates if the popover was shifted to stay in bounds */ shifted: boolean; /** Final calculated dimensions that fit in viewport */ finalWidth: number; finalHeight: number; /** * Viewport edge the popover should be pinned to on its main axis, and the * distance from that edge. * * This is the important half of the result for vertical placements. Pinning to * the trigger-adjacent edge (`top` for a dropdown below, `bottom` for one * above) makes the rendered position independent of the popover's own height: * the content grows and shrinks *away* from the trigger. Without it, a `top` * placement is `y = anchorTop - popoverHeight - offset`, so every content * change — an AutoComplete list filtering down as you type — moves the whole * popover, and any discrepancy between the estimated and measured height shows * up as a jump on first paint. * * `y` remains populated as a best-effort absolute coordinate for consumers * that need one, but renderers should prefer these fields when present. */ anchorEdge?: 'top' | 'bottom'; anchorOffset?: number; } export type PlacementType = 'top' | 'bottom' | 'left' | 'right' | 'auto' | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 'right-start' | 'right-end'; export interface PositioningOptions { placement?: PlacementType; offset?: number; viewport?: Viewport; strategy?: 'absolute' | 'fixed'; /** Enable flipping to opposite side when popover would go off-screen */ flip?: boolean; /** Enable shifting within bounds when popover would go off-screen */ shift?: boolean; /** Minimum distance from viewport edges */ boundary?: number; /** Fallback placements to try if primary placement doesn't fit */ fallbackPlacements?: PlacementType[]; /** Match the anchor element's width (useful for dropdown inputs) */ matchAnchorWidth?: boolean; /** * How tall the popover expects to be, in px, *before* it has been measured. * * This is what lets the very first calculation pick the correct side. A * dropdown almost always knows this up front (its `maxH`, or row height × * option count, whichever is smaller); supplying it means the pre-measure pass * and the post-measure pass reach the same conclusion, so there is no visible * flip. Falls back to the measured height, then to `DEFAULT_DESIRED_HEIGHT`. */ desiredHeight?: number; /** * Space at the bottom of the viewport that is covered by something the popover * must avoid — in practice the on-screen keyboard. * * Passed separately rather than baked into `viewport.height` because the two * are needed for different things: available-space math has to exclude the * keyboard, but a bottom edge pin is resolved by the platform against the * *real* viewport, so pinning against a shrunken height would lift the popover * by the keyboard height twice. */ viewportInsetBottom?: number; /** * The placement currently on screen, if any. Used for flip hysteresis: an open * popover only switches sides when the other side is meaningfully better, so * scrolling across the decision threshold doesn't make it ping-pong. */ currentPlacement?: PlacementType; /** How much extra space (px) the opposite side must offer before re-flipping an already-open popover. */ flipHysteresis?: number; } /** * Enhanced overlay positioning that prevents off-screen rendering. */ export declare function calculateOverlayPositionEnhanced(anchor: Rect, overlay: { width: number; height: number; }, options?: PositioningOptions): PositionResult; /** * Get current viewport dimensions */ export declare function getViewport(): Viewport; /** * Measure element dimensions and position */ export declare function measureElement(ref: any): Promise; /** * Check if a point is inside a rectangle */ export declare function pointInRect(point: { x: number; y: number; }, rect: Rect): boolean; /** * Get scroll position for web compatibility */ export declare function getScrollPosition(): { x: number; y: number; };