import { Placement } from "@floating-ui/dom"; //#region src/ui/overlay/constraint.d.ts /** * The constraint — one of the two spatial primitives (with the anchor). * A `Region` is a reactive rect; `constraint()` builds one from an * element, a plain rect, or nothing (the live viewport). `confine()` * applies a region to an overlay by syncing it into the * `--overlay-constraint-top/-left/-width/-height` channels every location * clamp and gesture bound derives from (declared in index.css with * viewport defaults). Derived values build on regions — `detents()` * quantizes one, `rubber()` resists at its edges. */ /** A reactive rect — the shape `createElementRect` returns, so an * `Element` works directly and custom rects (a static region, a virtual * area) can be supplied too. */ interface Region { top(): number; left(): number; width(): number; height(): number; } /** A plain rect a `Region` can be built from. */ interface RectInit { top: number; left: number; width: number; height: number; } /** * Builds a {@link Region}: from an `Element` (observed via * `ResizeObserver` through `createElementRect`; cleanup routes through * the current scope), from a plain rect (static), or with no argument — * the live viewport (read at call time, not observed). */ declare function constraint(source?: Element | RectInit): Region; interface OverlayConstraint { dispose(): void; [Symbol.dispose](): void; } /** * Resolves a custom property holding a length to pixels. Plain `px` values * parse directly; anything else (`svh` / `calc()` / fractions of the * constraint) resolves natively by measuring a hidden probe. `fallback` is * a CSS length expression used when the property is unset. */ declare function resolveVarPx(overlay: HTMLElement, name: string, axis: "height" | "width", fallback?: string): number; /** Resolves the constraint rect every gesture bound derives from. */ declare function resolveConstraint(overlay: HTMLElement): { top: number; left: number; width: number; height: number; }; /** * Confines an overlay to a {@link Region} by syncing it into the * `--overlay-constraint-*` variables. Every location clamp and gesture * bound derives from those variables, so the overlay re-clamps when the * region changes. * * Caveat: an element region observes size changes (`ResizeObserver`) — a * container that moves without resizing (e.g. page scroll) does not * retrigger the sync. * * Registers its cleanup with the current scope (`onCleanup`) and also * returns it as `dispose` / `Symbol.dispose`; disposing removes the * variables, restoring the viewport constraint. * * @example * ```ts * import { constraint, confine } from "elements-kit/ui/overlay"; * * const panel = document.querySelector("dialog.x-overlay")!; * confine(panel, constraint(document.querySelector("main")!)); * ``` */ declare function confine(overlay: HTMLElement, region: Region): OverlayConstraint; //#endregion //#region src/ui/overlay/anchor.d.ts /** * The anchor — one of the two spatial primitives (with the constraint). * `anchor(overlay, follow?)` gives the overlay an anchor ELEMENT it * follows for its whole life; the anchor element follows `follow` until * something else moves it (a `draggable()` service, author code). The * overlay itself has no states — dragging, tearing off, re-pinning are * all things that happen to the anchor. * * Two engines, chosen once at wire time, never switched: * * - Native CSS anchor positioning (compound gate, no `within`/`arrow`): * the overlay's `position-anchor` points at the anchor element; when * `follow` is an element the anchor pins itself to it through a second * native hop (`[data-follow]` rule in index.css mirrors the followed * box via `anchor()`/`anchor-size()`). Placement, flip and scroll * tracking are compositor-side through both hops — zero JS. * - Floating UI (below the gate, or `within`/`arrow` requested — native * CSS has no boundary control and no flip signal): `autoUpdate` writes * the box center into the `--overlay-x`/`-y` channels while the * overlay is open; a `dragmove` event from the drag service triggers * an immediate reposition. The initial positioning write is instant; * geometry transitions re-enable after it (Base UI's `data-instant` * semantics), so re-pins and live area changes morph by the * stylesheet. During an anchor drag, writes suppress geometry * transitions again — the overlay must not ease behind the finger. * * `data-anchor="element"` is a static wiring marker (stamped here, * removed on dispose, never toggled). `data-placed` is output state — * the settled side, feeding the arrow and `transform-origin` (a hint * from `--overlay-area` under the native engine, the real side under * Floating UI). A first pointer-down from `draggable()` tears the * follow pin (the `data-follow` contract); a fresh open re-pins. */ interface AnchorOptions { /** Flip/shift boundary — placement confined to this region instead of * the viewport. Forces the Floating UI engine. */ within?: Region; /** Caret pointing at the anchor (`.x-overlay-arrow`, injected when not * authored; a number sets `--overlay-arrow-size` in px). Forces the * Floating UI engine. */ arrow?: number | boolean; } /** * Maps a `position-area` value to the Floating UI placement. Spanning * toward an edge leaves the box flush with the *opposite* edge, so * `span-*-end` is a `-start` alignment (and vice versa) — Floating UI * resolves `-start`/`-end` logically, so the span tokens need no dir * check; only the physical inline sides do. Unknown values fall back to * `bottom` (the CSS default, `block-end`). */ declare function areaToPlacement(area: string, rtl?: boolean): Placement; /** * Gives `overlay` its anchor element and returns it. The overlay follows * the anchor for life; the anchor follows `follow` (element or rect) * until something moves it. Pass the returned element to `draggable()` * to make the composition tearable — dragging moves the anchor. * * `follow` may be a getter reading a signal — re-pinning on change is * how a shared popover slides between nav triggers: the native chain * glides there on the anchor element's CSS transition; the channel * engine lets that one write animate. * * Registers all cleanup with the current scope (`onCleanup`): the anchor * element, the wiring, and the stamped attributes are removed together. * * @example * ```ts * import { anchor, draggable, rubber } from "elements-kit/ui/overlay"; * * const a = anchor(panel, trigger); * draggable(a, undefined, rubber()).attach(panel); * * // shared nav popover — re-anchors (and glides) when the signal changes * const active = signal(firstTrigger); * anchor(menu, () => active(), { arrow: true }); * ``` */ declare function anchor(overlay: HTMLElement, follow?: Element | RectInit | (() => Element | RectInit | null | undefined), opts?: AnchorOptions): HTMLElement; //#endregion //#region src/ui/overlay/resize-strategy.d.ts /** * Resize strategies — pluggable policy for a resize drag's live bounds * and resting size. Pure (no DOM); the gesture injects the context. * Built-in: `freeResize` (default). `detents()` (detents.ts) quantizes a * constraint region and doubles as a strategy. */ /** How far (ms) a release velocity is projected when picking a rest. */ declare const PROJECTION_MS = 160; /** Clamp `value` into `[min, max]`. */ declare function clamp(value: number, min: number, max: number): number; /** * The release context a `ResizeStrategy` decides against. The gesture * builds it per drag; `resolve` turns a step value into pixels. */ interface ResizeContext { /** Dragged size along the axis (px, before clamping). */ size: number; /** Size at the gesture's start (px). */ startSize: number; /** Release velocity along the axis (px/ms; positive = shrinking). */ velocity: number; /** Resize axis. */ axis: "width" | "height"; /** Hard room the surface may occupy on the axis (px). */ min: number; max: number; /** Whether a drag/flick past the minimum may dismiss. */ dismissible: boolean; /** Release velocity (px/ms) past which a sub-minimum release dismisses. */ velocityThreshold: number; /** Resolves a step to px — a number is a fraction of the constraint * along the axis; a string is any CSS length. */ resolve(value: number | string): number; } /** * Decides where a resize drag rests. The gesture calls `bounds()` for the * live rubber-band and `rest()` on release (the resting size, or `null` * to dismiss). Built-ins: `freeResize` (default), `detents`. */ interface ResizeStrategy { /** Soft `[lo, hi]` bounds for the live drag — rubber-band past these. * Defaults to the hard room when omitted. */ bounds?(ctx: ResizeContext): [number, number]; /** Resting size (px) on release, or `null` to dismiss. */ rest(ctx: ResizeContext): number | null; } /** * Picks the index of the detent closest to the released size, projected * along the release velocity (px/ms, positive = shrinking). Returns `-1` * when the gesture should dismiss instead. */ declare function closestDetent(sizePx: number, detentsPx: readonly number[], velocityPxPerMs?: number, dismissible?: boolean, velocityThreshold?: number): number; /** * Free resize: drag to any size within the room; a flick or shrink past * the minimum dismisses. The default strategy. */ declare function freeResize(opts?: { min?: number; }): ResizeStrategy; //#endregion //#region src/ui/overlay/detents.d.ts /** * Detents — a customization of the constraint: the region, quantized. * One snapping concept for both services: `resize()` (and the markup * gestures) snap extents to the stops; `draggable()` snaps positions to * them. A bottom sheet's size detents and its top-edge position detents * were always the same thing — here they are literally the same object. */ /** A {@link Region} with quantized stops. Doubles as a `ResizeStrategy` * (the stops resolve per axis through the gesture's `ResizeContext`). */ interface Space extends ResizeStrategy { readonly region: Region; readonly stops: readonly (number | string)[]; /** The numeric stops resolved against a region axis (px, sorted) — * position snapping for `draggable()`. String stops (CSS lengths) need * an element context and only apply to resizes. */ positions(axis: "width" | "height"): number[]; } /** * Quantizes a region: each stop is a fraction of the region along the * axis (number `0–1`) or a CSS length (string, resize-only). Flick-aware * on release; shrinking past the smallest stop dismisses (when the * consumer allows dismissal). * * @example * ```ts * import { constraint, detents } from "elements-kit/ui/overlay"; * * const d = detents(constraint(), [0.25, 0.6, 0.9]); // of the viewport * ``` */ declare function detents(region: Region, stops: readonly (number | string)[]): Space; //#endregion //#region src/ui/overlay/rubber.d.ts /** * Effects — composable physics for motion through a space. Services * (`draggable`, `resize`) run each axis value through the effect chain: * `during` on every move, `settle` on release (`null` = dismiss signal). * `rubber()` is the built-in; custom effects are just objects. */ interface Effect { /** Transform a live value (px on one axis) against the axis bounds. */ during?(value: number, bounds: readonly [number, number]): number; /** Pick the resting value on release, or `null` to signal dismissal. */ settle?(value: number, velocity: number, bounds: readonly [number, number]): number | null; } /** * Edge resistance — values past the bounds move at a fraction of the * pointer (the iOS rubber band). Pure `resist()` under the hood; no * channels involved, so it applies to anything a service moves. */ declare function rubber(): Effect; //#endregion //#region src/ui/overlay/draggable.d.ts /** * The drag service — moves a target element through a space. Generic by * design: drag any positioned element by any handle. For overlays, the * target is the anchor element from `anchor()` — dragging the anchor * drags the overlay, with no overlay state involved. * * The pipeline runs per axis: live values go through each effect's * `during` (e.g. `rubber()` resistance at the space edges); on release * the value goes through each `settle`, then snaps to the space's * numeric stops when a `detents()` space is given (`null` from a settle * = dismiss signal, surfaced on the `dragend` detail). Events on the * target: `dragmove` (`{x, y}`) every move, `dragend` * (`{x, y, velocity, rest}`) on release. * * First pointer-down tears a `data-follow` pin (the `anchor()` contract): * the target freezes at its current rect and the pointer takes over. */ interface DragService { /** Move the target programmatically (through the `during` effects). */ update(point: { x: number; y: number; }): void; /** Wire the pointer plumbing to a handle. */ attach(handle: Element): { dispose(): void; }; dispose(): void; [Symbol.dispose](): void; } declare function draggable(target: HTMLElement, space?: Region | Space, ...effects: Effect[]): DragService; //#endregion //#region src/ui/overlay/gestures.d.ts interface OverlayGestureOptions { /** How a resize drag rests. Default: `freeResize()`. */ resize?: ResizeStrategy; /** Allow a drag/flick past the minimum to close. Default `true`. */ dismissible?: boolean; /** Release velocity (px/ms, shrinking) that dismisses. Default `0.5`. */ velocityThreshold?: number; } interface OverlayGestures { /** Resize to a size (px) along the resize axis — animated by CSS. */ resize(size: number): void; dispose(): void; [Symbol.dispose](): void; } /** * Opt-in pointer gestures for `.x-overlay`, dispatched by the gesture * attributes (structure stays in markup; policy is options): * * - An edge word (`block-*` / `inline-*`) is a whole-surface size drag * along that axis — block drags the height (sheets), inline the width * (drawers), `:dir(rtl)` flips the inline sign. The side names the * handle; the opposite edge stays put. * - A corner word (`start-start` / … — block side first) is a desktop- * window resize from a square zone at that corner, anchored at the * opposite corner so the surface never grows past the constraint. The * width follows the `resize` strategy; the height is a free clamp. * - `data-draggable` moves the surface in x/y from the top strip, * rubber-banding at the edges; flinging it off the constraint dismisses * (when `dismissible`). * * Layered for testability: `gesture-model` owns the pure mode reducers + * math, and `overlay-dom` owns all DOM contact (pointer plumbing + channel * I/O). This function is the wiring — it picks a pure `Session` from * `detectEngagement` and adapts it to the recognizer through the io. * The `resize` strategy (`freeResize` by default, or `detents`) decides the * rested size, written to the public `--overlay-w`/`--overlay-h` channels; * CSS renders and animates them. JS never touches `translate`/`top`/`left`. * * Registers its cleanup with the current scope (`onCleanup`) and also * returns it as `dispose` / `Symbol.dispose`. * * @example * ```ts * import { constraint, createOverlayGestures, detents } from "elements-kit/ui/overlay"; * * const el = document.querySelector("dialog.x-overlay")!; * createOverlayGestures(el, { resize: detents(constraint(), [0.25, 0.6, 0.9]) }); * el.addEventListener("resizechange", (e) => console.log(e.detail)); * ``` */ declare function createOverlayGestures(overlay: HTMLElement, options?: OverlayGestureOptions): OverlayGestures; //#endregion //#region src/ui/overlay/resize.d.ts /** * The resize service — sizes a target through a space. The handle you * `attach()` is the whole gesture zone (unlike the markup gestures, * which zone-detect on the overlay surface); the side comes from the * target's `data-resize` value, same vocabulary as the stylesheet. * * The space is any `ResizeStrategy` — pass a `detents()` space to snap, * omit for `freeResize()`. Effects apply on release: each `settle` runs * over the strategy's rested size (`null` = dismiss). Live rubber past * the strategy bounds is built into the sessions. */ interface ResizeService { /** Resize to a size (px) along the resize axis — animated by CSS. */ update(size: number): void; /** Wire the pointer plumbing to a handle. */ attach(handle: Element): { dispose(): void; }; dispose(): void; [Symbol.dispose](): void; } declare function resize(target: HTMLElement, space?: ResizeStrategy, ...effects: Effect[]): ResizeService; //#endregion export { AnchorOptions, DragService, Effect, OverlayConstraint, OverlayGestureOptions, OverlayGestures, PROJECTION_MS, RectInit, Region, ResizeContext, ResizeService, ResizeStrategy, Space, anchor, areaToPlacement, clamp, closestDetent, confine, constraint, createOverlayGestures, detents, draggable, freeResize, resize, resolveConstraint, resolveVarPx, rubber };