/** * Port of Base UI React's `useAnchorPositioning` hook * (`packages/react/src/utils/useAnchorPositioning.ts`). * * This is the heavy-weight orchestrator that threads `anchor`, * `collisionBoundary`, `sticky`, `disableAnchorTracking`, * `collisionAvoidance`, `adaptiveOrigin`, `side`, `align`, offsets and * arrow padding through `computePosition` + middleware. It composes the * Solid port's `useFloating` — the low-level positioning engine lives * there. This file's job is purely middleware assembly + anchor wiring. * * ## React → Solid deviations * * - Accessor-based reactive inputs. Every prop that React allowed to * change between renders is modeled here as `Accessor`; callers * pass getters. Primitive-style values (e.g. numeric `sideOffset`) may * be passed as a plain number because components usually compute them * inside a reactive memo and pass the memo. The orchestration below * wraps all inputs with `() => ...` at read time so either shape works. * - `React.useState` → `createSignal`, `React.useRef` → `{ current }`, * `useLayoutEffect` → `createRenderEffect` (via `useIsoLayoutEffect`), * `useEffect` → `createEffect`. * - `offset` / `flip` / `shift` middleware don't accept a `deps` array in * `@floating-ui/dom` the way `@floating-ui/react-dom` did — Solid's * reactive reads inside the middleware functions handle the same * contract (middleware rebuilds on each `computePosition` call). * - React gates the initial autoUpdate-less path on `keepMounted`; Solid * `useFloating` already wires `autoUpdate` when reference + floating * are attached, so we thread `disableAnchorTracking` through by * conditionally returning `false` from `useFloating`'s `open` read. * (Not quite — we can't short-circuit `autoUpdate` from here without * patching `useFloating`. Instead we skip registering the anchor * entirely while `disableAnchorTracking` is true after first * position.) See the `registerAnchor` effect below. * - The React source computes `middleware` as a fresh array on every * render. Solid runs setup once; we rebuild the array via a getter so * `useFloating` sees the latest reactive values. */ import { type Middleware, type Padding, type Side as PhysicalSide, type VirtualElement } from "@floating-ui/dom"; import { type Rect } from "@floating-ui/utils"; import type { JSX } from "@solidjs/web"; import { type Accessor } from "solid-js"; import { useFloating } from "../floating-ui/hooks/use-floating.js"; import type { FloatingContext, FloatingRootContext, FloatingTreeStoreType as FloatingTreeStore, MutableRef } from "../floating-ui/types.js"; export type Side = "top" | "bottom" | "left" | "right" | "inline-end" | "inline-start"; export type Align = "start" | "center" | "end"; export type Boundary = "clipping-ancestors" | Element | Element[] | Rect; export type OffsetFunction = (data: { side: Side; align: Align; anchor: { width: number; height: number; }; positioner: { width: number; height: number; }; }) => number; interface SideFlipMode { side?: "flip" | "none" | undefined; align?: "flip" | "shift" | "none" | undefined; fallbackAxisSide?: "start" | "end" | "none" | undefined; } interface SideShiftMode { side?: "shift" | "none" | undefined; align?: "shift" | "none" | undefined; fallbackAxisSide?: "start" | "end" | "none" | undefined; } export type CollisionAvoidance = SideFlipMode | SideShiftMode; /** Ref-like shape matching the React source; accepted for the `anchor` prop. */ export interface AnchorRefObject { current: T; } export type AnchorInput = Element | VirtualElement | AnchorRefObject | (() => Element | VirtualElement | null) | null | undefined; export interface UseAnchorPositioningSharedParameters { anchor?: Accessor | AnchorInput | undefined; positionMethod?: Accessor<"absolute" | "fixed"> | "absolute" | "fixed" | undefined; side?: Accessor | Side | undefined; sideOffset?: Accessor | number | OffsetFunction | undefined; align?: Accessor | Align | undefined; alignOffset?: Accessor | number | OffsetFunction | undefined; collisionBoundary?: Accessor | Boundary | undefined; collisionPadding?: Accessor | Padding | undefined; sticky?: Accessor | boolean | undefined; arrowPadding?: Accessor | number | undefined; disableAnchorTracking?: Accessor | boolean | undefined; collisionAvoidance?: Accessor | CollisionAvoidance | undefined; } export interface UseAnchorPositioningParameters extends UseAnchorPositioningSharedParameters { keepMounted?: Accessor | boolean | undefined; floatingRootContext?: FloatingRootContext | undefined; mounted: Accessor | boolean; disableAnchorTracking: Accessor | boolean; collisionAvoidance: Accessor | CollisionAvoidance; shiftCrossAxis?: Accessor | boolean | undefined; nodeId?: string | undefined; adaptiveOrigin?: Accessor | Middleware | undefined; lazyFlip?: Accessor | boolean | undefined; externalTree?: FloatingTreeStore | undefined; } export interface UseAnchorPositioningReturnValue { readonly positionerStyles: JSX.CSSProperties; readonly arrowStyles: JSX.CSSProperties; readonly arrowRef: MutableRef; readonly arrowUncentered: boolean; readonly side: Side; readonly align: Align; readonly physicalSide: PhysicalSide; readonly anchorHidden: boolean; readonly refs: ReturnType["refs"]; readonly context: FloatingContext; readonly isPositioned: boolean; readonly update: () => void; } /** * Orchestrates anchor positioning for Base UI popup compounds. Composes * `useFloating` + the `offset` / `flip` / `shift` / `size` / `arrow` / * `hide` / `adaptiveOrigin` middleware chain. * * All reactive inputs can be passed as accessors. Primitive-typed props * accept both forms — the hook normalizes internally. The returned * object is a collection of getters backed by signals, suitable for * reactive spreads in JSX or reads inside memos. */ export declare function useAnchorPositioning(params: UseAnchorPositioningParameters): UseAnchorPositioningReturnValue; export {}; //# sourceMappingURL=use-anchor-positioning.d.ts.map