import * as _angular_core from '@angular/core'; import { Signal, InjectionToken, OnInit } from '@angular/core'; /** * Configuration for {@link injectVirtualizer}. The consumer owns the data and * the DOM; the virtualizer only computes which slice of the data is visible. */ interface VirtualizerOptions { /** Reactive total number of items in the list. */ readonly count: Signal; /** * Estimated size, in CSS pixels, of the item at `index` along the scroll * axis (height when vertical, width when horizontal). Used until an item is * measured. Should be a stable function reference. */ readonly estimateSize: (index: number) => number; /** Reactive reference to the scroll container element (e.g. a `viewChild`). */ readonly scrollElement: Signal; /** Scroll axis. Defaults to `'vertical'`. */ readonly orientation?: 'vertical' | 'horizontal'; /** * Number of items to render beyond the visible window on each side, to * reduce blank flashes while scrolling. Defaults to `5`. */ readonly overscan?: number; /** Stable key for the item at `index`. Defaults to the index itself. */ readonly getItemKey?: (index: number) => string | number; /** * Offset, in CSS pixels, added before the first item along the scroll axis. * Every item's computed offset and every `scrollToIndex` / `scrollToOffset` * alignment shifts by this amount, so a sticky header rendered inside the * scroller no longer overlaps the row a cross-window keyboard move lands on. * Defaults to `0`. Should be a stable value. */ readonly scrollMargin?: number; } /** A single item in the currently rendered window. */ interface VirtualItem { /** Index of the item in the full list. */ readonly index: number; /** Stable key (from `getItemKey`, or the index). */ readonly key: string | number; /** Offset of the item from the start of the scroll container, in pixels. */ readonly start: number; /** Size of the item along the scroll axis, in pixels. */ readonly size: number; } /** Reactive handle returned by {@link injectVirtualizer}. */ interface ForVirtualizer { /** The items in the currently visible window plus overscan. */ readonly virtualItems: Signal; /** Total scroll size of all items, in pixels (drives the spacer element). */ readonly totalSize: Signal; /** * The inclusive-exclusive `[firstIndex, lastIndex + 1)` index window currently * rendered (visible window plus overscan), or `[0, 0]` when nothing is rendered. * Plugs straight into a list primitive's `[visibleRange]`-style input (e.g. * `[forCombobox][visibleRange]`) so windowing composes without the consumer * re-deriving the range from {@link ForVirtualizer.virtualItems}. */ readonly range: Signal; /** Scroll the container so the item at `index` is in view. */ scrollToIndex(index: number, options?: { align?: 'start' | 'center' | 'end' | 'auto'; }): void; /** Scroll the container to an absolute pixel offset. */ scrollToOffset(offset: number): void; /** * Record the measured size of a rendered item element (dynamic sizes). * Passing `null` sweeps detached (evicted) elements from the measurement * cache and stops observing them, so recycled rows scrolled out of the window * are not retained/observed until the directive is destroyed. */ measureElement(element: HTMLElement | null): void; /** * The item at `index` as computed from the core's measurement cache — its * `start` reflects measured sizes and `scrollMargin`, not pure estimate math. * Returns `null` before the core has mounted or when `index` is out of range. * Used to position a retained (pinned) row on its real offset rather than * recomputing it from `estimateSize`. */ measurementFor(index: number): VirtualItem | null; } /** * Headless windowing core: given a reactive item count, a size estimator and a * scroll container, returns the slice of items currently visible (plus * overscan), the total scroll size, and imperative scroll/measure helpers. The * consumer renders the items with their own `@for` and applies the position * transform — this primitive owns no DOM. * * Backed by `@tanstack/virtual-core`. SSR-safe: off-browser it returns an empty * window and the estimate-based total without touching `document`/`window`; the * first real window is produced after the first browser render. * * Must be called from an injection context (a component/directive constructor * or field initializer). * * @param options Reactive count, size estimator, scroll element and tuning. * @returns A {@link ForVirtualizer} handle of signals + imperative methods. */ declare function injectVirtualizer(options: VirtualizerOptions): ForVirtualizer; /** * Configuration for {@link injectInfiniteScroll}. The consumer owns the data and * the fetch; this core only decides *when* to ask for more. */ interface InfiniteScrollOptions { /** * The rendered window, `[firstIndex, lastIndex + 1)` — e.g. * `injectVirtualizer(...).range`. An empty `[0, 0]` window never fires. */ readonly range: Signal; /** Reactive total number of currently-loaded items. */ readonly count: Signal; /** * Fire when the window's last index comes within this many items of `count`. * Defaults to `5` (mirrors the windowing core's default overscan). */ readonly threshold?: number; /** When this resolves to `true` the detector never fires. */ readonly disabled?: Signal; /** * Called once per threshold crossing. If it returns a promise, the next fire * is suppressed until that promise settles (`pending` reflects the in-flight * state); the detector re-arms when `count` grows. */ readonly onLoadMore: () => void | Promise; } /** Reactive handle returned by {@link injectInfiniteScroll}. */ interface ForInfiniteScroll { /** True while an `onLoadMore` promise is in flight. */ readonly pending: Signal; } /** * Headless infinite-scroll detector: composes on top of any windowed list's * `range` + `count` signals and fires `onLoadMore` once per threshold crossing, * suppressing re-fire while a returned promise is pending and re-arming when * `count` grows (a page was appended). It owns no DOM, adds no scroll listener — * the trigger rides the existing reactive recompute — and is SSR-safe by * construction: off-browser the window is `[0, 0]`, so it never fires. * * Must be called from an injection context (a component/directive constructor * or field initializer). * * @param options Reactive `range` + `count`, an optional `threshold`/`disabled`, * and the `onLoadMore` callback. * @returns A {@link ForInfiniteScroll} handle exposing the `pending` signal. */ declare function injectInfiniteScroll(options: InfiniteScrollOptions): ForInfiniteScroll; /** * Coordination surface a {@link ForVirtualViewport} exposes to the * `*forVirtualFor` structural directive nested inside it. */ interface ForVirtualViewportContext { /** The items in the currently visible window plus overscan. */ readonly virtualItems: Signal; /** The total number of items in the full (non-windowed) list. */ readonly count: Signal; /** Scroll axis, resolved once when the viewport initializes. */ readonly orientation: Signal<'vertical' | 'horizontal'>; } /** DI token carrying the {@link ForVirtualViewportContext}. */ declare const FOR_VIRTUAL_VIEWPORT_CONTEXT: InjectionToken; /** * Scroll viewport for the ergonomic virtualization layer. Decorate a fixed-size * scroll container with `[forVirtualViewport]`, give it `[virtualCount]` and an * `[estimateSize]`, and nest a single `*forVirtualFor` inside it — the viewport * owns the scroll container, the total-size sizer, and the windowing core, so * the consumer writes no manual spacer or position transform. * * Built on the headless {@link injectVirtualizer} core; for full manual control * (custom DOM, dynamic measurement, window/document scroller) use that directly. * * The viewport forces `overflow: auto` on its host and renders a relatively * positioned sizer whose main-axis size tracks `totalSize()`; `*forVirtualFor` * projects its rows into that sizer and positions each one absolutely. * * `orientation` and `overscan` are read once when the viewport initializes; * change them before first render, not at runtime. */ declare class ForVirtualViewport implements ForVirtualViewportContext, OnInit { #private; /** Total number of items in the full list. */ readonly virtualCount: _angular_core.InputSignal; /** Estimated item size in px along the scroll axis: a number or a per-index estimator. */ readonly estimateSize: _angular_core.InputSignal number)>; /** Scroll axis. Resolved once on init; runtime changes are not tracked by the core. */ readonly orientation: _angular_core.InputSignal<"vertical" | "horizontal">; /** Items rendered beyond the visible window on each side. Resolved once on init. */ readonly overscan: _angular_core.InputSignal; /** Stable key for the item at `index`. Defaults to the index. */ readonly getItemKey: _angular_core.InputSignal<((index: number) => string | number) | undefined>; /** * Emits when the rendered window comes within ~`overscan` items of the end of * the list, signalling the consumer to load the next page. Built on * {@link injectInfiniteScroll}; fires once per threshold crossing and re-arms * when the bound count grows. The consumer owns the fetch (e.g. via `resource()`). */ readonly endReached: _angular_core.OutputEmitterRef; /** * The total number of items in the full (non-windowed) list — the * {@link ForVirtualViewportContext.count} the nested `*forVirtualFor` reads. * Aliases the `virtualCount` input signal directly (no wrapper node). */ readonly count: _angular_core.InputSignal; /** * The items in the currently visible window plus overscan, augmented to always * include the row pinned via {@link setReorderingIndex} even when it is scrolled * out of view, so a drag-reorder's lifted row is never recycled out from under * the gesture. Pinning never widens {@link range} (sourced from the underlying * virtualizer), so it leaves infinite-scroll untouched. */ readonly virtualItems: Signal; /** Total scroll size of all items, in pixels (drives the sizer). */ readonly totalSize: Signal; protected readonly sizerWidth: Signal; protected readonly sizerHeight: Signal; constructor(); ngOnInit(): void; /** * Pin the row at the absolute `index` into the rendered window so it stays * mounted even when the window scrolls past it — used by `[forVirtualReorder]` * to keep a drag-reorder's lifted row alive across auto-scroll and dataset-wide * keyboard stepping. Pass `null` to release. No-op when the index is out of * range; rarely needed directly. */ setReorderingIndex(index: number | null): void; /** Scroll the container so the item at `index` is in view. No-op until initialized. */ scrollToIndex(index: number, options?: { align?: 'start' | 'center' | 'end' | 'auto'; }): void; /** Scroll the container to an absolute pixel offset. No-op until initialized. */ scrollToOffset(offset: number): void; /** * Record the measured size of a rendered item element. Passing `null` sweeps * detached (recycled) rows from the measurement cache and stops observing * them — the viewport already performs this sweep after every render, so a * monotonically scrolling list never retains one detached element per row * scrolled past. No-op until initialized. */ measureElement(element: HTMLElement | null): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration; static ɵcmp: _angular_core.ɵɵComponentDeclaration; } /** Template context exposed to each row rendered by `*forVirtualFor`. */ interface ForVirtualForContext { /** The row data at this index (`let row`). */ $implicit: T; /** The virtual item metadata: index, key, start offset, size (`let item = virtualItem`). */ virtualItem: VirtualItem; /** The item's index in the full list (`let i = index`). */ index: number; /** The total number of items in the full list (`let n = count`). */ count: number; } /** * Structural directive that renders only the visible window of a list inside a * `[forVirtualViewport]`. Pass the full data array; the directive iterates the * viewport's `virtualItems()`, exposes each row plus its `virtualItem` to the * template, positions it absolutely (so the consumer writes no transform), and * binds `aria-setsize` (true total) / `aria-posinset` (`index + 1`) so screen * readers announce the real list size. * * ```html *
*
{{ row.label }}
*
* ``` * * A row that stays in the window across a re-render **keeps its DOM node in place**: views * that left the window are removed before the surviving ones are re-indexed, so a view whose * position in the window changed never has to be detached and re-inserted to get there. That * is what makes focus survive a window jump — `ViewContainerRef.move` removes the node from * the document before re-inserting it, which blurs whatever inside it was focused, and the * row pinned by `[forVirtualReorder]` is focused for the whole gesture. */ declare class ForVirtualFor { #private; /** The full list of items to virtualize. */ readonly forVirtualForOf: _angular_core.InputSignal; constructor(); /** Narrows the template context type for `let row of …` strict template checking. */ static ngTemplateContextGuard(_directive: ForVirtualFor, _context: unknown): _context is ForVirtualForContext; static ɵfac: _angular_core.ɵɵFactoryDeclaration, never>; static ɵdir: _angular_core.ɵɵDirectiveDeclaration, "[forVirtualFor][forVirtualForOf]", never, { "forVirtualForOf": { "alias": "forVirtualForOf"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>; } export { FOR_VIRTUAL_VIEWPORT_CONTEXT, ForVirtualFor, ForVirtualViewport, injectInfiniteScroll, injectVirtualizer }; export type { ForInfiniteScroll, ForVirtualForContext, ForVirtualViewportContext, ForVirtualizer, InfiniteScrollOptions, VirtualItem, VirtualizerOptions };