/** * Cached, batch-read element geometry for drag hit-testing. * * `getBoundingClientRect()` is a *forced synchronous layout* whenever styles have * been written since the last frame — which, during a drag, they always have. * Calling it inside a `pointermove` handler (worse, in a loop over every drop * candidate) makes the browser flush layout several times per frame for geometry * that has not moved. * * `DragRectCache` reads every rect once, in a single uninterrupted read phase, and * answers all subsequent hit tests from plain numbers. Callers invalidate it only * at the moments geometry genuinely changes — drag start, an auto-scroll step, a * live panel move, a resize — never per pointer event. * * Storage is flat `Float64Array`s reused across captures, so a drag that * re-captures on every scroll frame allocates nothing after the first capture * large enough to hold the slot count. * * @packageDocumentation */ /** * The subset of `DOMRect` this cache reads. * * Deliberately structural rather than `DOMRect`: `right` / `bottom` are derived * here instead of read, which keeps the cache usable against any object exposing * the four primary values. */ export interface DragRectLike { readonly top: number; readonly left: number; readonly width: number; readonly height: number; } /** Sentinel returned by every lookup that matches no slot. */ export declare const NO_SLOT = -1; /** * A batch of element rects captured at one instant, with hit tests that touch no * layout. * * ### Complexity * - {@link capture} — O(n), one layout flush total. * - {@link hitTestX}, {@link firstSlotPastMidpointX} — O(log n) when * {@link capture} was given left-ordered elements (the common case: a header * row); the binary search degrades gracefully to a miss otherwise, so callers * with unordered slots should use {@link sortByLeft} first. * - {@link hitTestPoint}, {@link nearestByMidpointX} — O(n) float comparisons, * zero layout. * * @example * ```ts * const cache = new DragRectCache(); * cache.capture(headerCells); // one layout flush, at drag start * * // per frame — no layout: * const slot = cache.hitTestX(pointerX); * if (slot !== NO_SLOT) highlight(headerCells[slot]); * * onAutoScrollStep(() => cache.invalidate()); * ``` */ export declare class DragRectCache { private lefts; private rights; private tops; private bottoms; private els; private len; private valid; /** Number of captured slots. `0` when empty or invalidated. */ get length(): number; /** `true` when the cache holds geometry that has not been invalidated. */ get isValid(): boolean; /** * The elements backing the current capture, in slot order. * * Exposed so callers can read `data-col-id` / `data-group-id` off a hit slot * without maintaining a parallel array. Do not mutate. */ get elements(): readonly HTMLElement[]; /** * Reads every element's rect in one pass and replaces the cache contents. * * All reads happen consecutively with no interleaved writes, so the browser * flushes layout at most once for the whole batch rather than once per element. * * @param elements - Slots to capture, in the order callers will index them. * Pass left-ordered elements to enable the binary-search * lookups, or follow with {@link sortByLeft}. */ capture(elements: readonly HTMLElement[]): void; /** * Re-orders the captured slots by ascending left edge. * * Absolutely-positioned header cells (group rows, depth fillers) appear in * arbitrary DOM order, so sorting is required before the binary-search lookups * are meaningful. O(n log n) on a captured batch — never called per frame. */ sortByLeft(): void; /** * Marks the cached geometry stale without releasing its storage. * * Call at every event that actually moves the captured elements. The next * {@link capture} reuses the existing arrays, so invalidating on a scroll frame * costs nothing in allocation. */ invalidate(): void; /** Releases element references so a finished drag cannot retain detached DOM. */ clear(): void; /** * Slot whose horizontal span contains `x`, or {@link NO_SLOT}. * * Spans are treated as half-open (`left <= x < right`) so adjacent columns * never both claim a boundary pixel. * * @param x - Client x coordinate. * @param offset - Value added to every stored edge before comparison. Used to * correct for horizontal scrolling that has occurred since the * capture, avoiding a re-read. */ hitTestX(x: number, offset?: number): number; /** * First slot whose horizontal midpoint lies to the right of `x`, or * {@link length} when the pointer is past every midpoint. * * This is the "insert before" question every reorder drag asks: the returned * index is the slot the dragged item would take. * * @param x - Client x coordinate. * @param offset - Scroll correction, as in {@link hitTestX}. */ firstSlotPastMidpointX(x: number, offset?: number): number; /** * Slot containing the point, or {@link NO_SLOT}. * * Later slots win on overlap, matching the topmost-wins convention of the * previous `findDropTarget` implementation. * * @param x - Client x coordinate. * @param y - Client y coordinate. */ hitTestPoint(x: number, y: number): number; /** * Slot whose horizontal midpoint is nearest `x`, restricted to slots whose * vertical span contains `y` (within `yTolerance`). Returns {@link NO_SLOT} * when no slot qualifies. * * @param x - Client x coordinate. * @param y - Client y coordinate. * @param yTolerance - Slack added above and below each slot's vertical span. * @param skip - Slot index to ignore, typically the dragged item itself. */ nearestByMidpointX(x: number, y: number, yTolerance?: number, skip?: number): number; /** Left edge of a slot, or `0` when the index is out of range. */ leftOf(i: number): number; /** Right edge of a slot, or `0` when the index is out of range. */ rightOf(i: number): number; /** Top edge of a slot, or `0` when the index is out of range. */ topOf(i: number): number; /** Height of a slot, or `0` when the index is out of range. */ heightOf(i: number): number; /** Width of a slot, or `0` when the index is out of range. */ widthOf(i: number): number; /** Index of `el` among the captured slots, or {@link NO_SLOT}. */ indexOfElement(el: HTMLElement | null): number; /** * Grows the flat arrays to hold at least `n` slots. * * Capacity is never shrunk: a drag re-captures the same panel repeatedly, so * keeping the high-water mark avoids reallocating on every scroll frame. */ private ensureCapacity; } //# sourceMappingURL=drag-rect-cache.d.ts.map