import { Effect, Option, Schema as S } from 'effect'; import { type Update } from 'foldkit'; import * as Command from 'foldkit/command'; import { type ChildAttribute, type Html, type TagName } from 'foldkit/html'; import { type View as SubmodelView } from 'foldkit/submodel'; import * as Subscription from 'foldkit/subscription'; /** Schema for the virtual list's state. Tracks scroll position, container * measurement, and any in-flight programmatic scroll. */ export declare const Model: S.Struct<{ readonly id: S.String; readonly rowHeightPx: S.Number; readonly scrollTop: S.Number; readonly measurement: S.Union, import("foldkit/schema").CallableTaggedStruct<"Measured", { containerHeight: S.Number; }>]>; readonly pendingScroll: S.Union, import("foldkit/schema").CallableTaggedStruct<"ScrollingToIndex", { index: S.Number; version: S.Number; }>]>; readonly pendingScrollVersion: S.Number; }>; export type Model = typeof Model.Type; /** Union of all messages the virtual list component can produce. */ export declare const Message: import("foldkit/message").MessageUnion<{ readonly ScrolledContainer: { readonly scrollTop: S.Number; }; readonly MeasuredContainer: { readonly containerHeight: S.Number; }; readonly CompletedApplyScroll: { readonly version: S.Number; }; }>; export type ScrolledContainer = typeof Message.ScrolledContainer.Type; export type MeasuredContainer = typeof Message.MeasuredContainer.Type; export type Message = typeof Message.Type; /** Configuration for creating a virtual list model with `init`. */ export type InitConfig = Readonly<{ id: string; rowHeightPx: number; initialScrollTop?: number; }>; /** Creates an initial virtual list model from a config. The container starts * in `Unmeasured` state. The first `ResizeObserver` entry transitions it to * `Measured`. */ export declare const init: (config: InitConfig) => Model; export declare const ApplyScroll: Command.CommandDefinitionWithArgs<"ApplyScroll", { id: S.String; scrollTop: S.Number; version: S.Number; }, Effect.Effect<{ readonly _tag: "CompletedApplyScroll"; readonly version: number; }, never, never>>; /** Processes a VirtualList Message and returns the next Model and optional Commands. */ export declare const update: (model: Model, message: Message) => Readonly<{ model: { readonly id: string; readonly rowHeightPx: number; readonly scrollTop: number; readonly measurement: { readonly _tag: "Unmeasured"; } | { readonly _tag: "Measured"; readonly containerHeight: number; }; readonly pendingScroll: { readonly _tag: "Idle"; } | { readonly _tag: "ScrollingToIndex"; readonly index: number; readonly version: number; }; readonly pendingScrollVersion: number; }; commands?: Update.Commands<{ readonly _tag: "ScrolledContainer"; readonly scrollTop: number; } | { readonly _tag: "MeasuredContainer"; readonly containerHeight: number; } | { readonly _tag: "CompletedApplyScroll"; readonly version: number; }, never>; outMessage?: never; }>; type ScrollReturn = Update.Return; /** Programmatically scrolls the container so the row at `index` is visible. * Returns the next Model and a Command that mutates `element.scrollTop`. The * natural scroll event then flows back through `ScrolledContainer` and the * component re-renders the new visible slice. * * Uses version-based cancellation: each call increments * `pendingScrollVersion` so a stale `CompletedApplyScroll` (e.g. from a * previous in-flight scroll) is ignored when its version no longer matches. * * Should be called after the container has rendered. If the container is not * yet in the DOM the Command silently no-ops (the Model still transitions * through `ScrollingToIndex` → `Idle` via the version-matched completion). * * Assumes uniform row heights: target scroll position is computed as * `index * model.rowHeightPx`. For variable-height rows, use * `scrollToIndexVariable`. */ export declare const scrollToIndex: (model: Model, index: number) => ScrollReturn; /** Variable-height counterpart of `scrollToIndex`. Walks the heights of items * before `index` to compute the target `scrollTop`. Use this when rendering * the list with `itemToRowHeightPx`; use `scrollToIndex` for uniform heights. * * Out-of-range indices clamp to the corresponding edge: negative or zero * scrolls to the top, indices past the end scroll past the last row. * * Note: when restoring `initialScrollTop` on the first measurement of a * variable-height list, the runtime falls back to uniform-height math (using * `model.rowHeightPx`) because items aren't reachable from the `update` * function. Consumers who need an accurate initial scroll on a * variable-height list should call `scrollToIndexVariable` after the first * `MeasuredContainer` arrives. */ export declare const scrollToIndexVariable: (model: Model, items: ReadonlyArray, itemToRowHeightPx: (item: Item, index: number) => number, index: number) => ScrollReturn; /** Slice of the data array that the view should render, plus the spacer * heights that keep the scrollbar physically correct. The first row in the * slice corresponds to data index `startIndex`. */ export type VisibleWindow = Readonly<{ startIndex: number; endIndex: number; topSpacerHeight: number; bottomSpacerHeight: number; }>; /** Computes the visible slice of a data array given the current scroll * position, container height, row height, and an overscan buffer. * * Assumes uniform row heights via `model.rowHeightPx`. For variable-height * rows, use `visibleWindowVariable`. * * Returns `Option.none()` when the container has not yet been measured; * callers should render a placeholder (or `Html.empty`) and wait for the * first `MeasuredContainer` message. */ export declare const visibleWindow: (model: Model, itemCount: number, overscan: number) => Option.Option; /** Variable-height counterpart of `visibleWindow`. Walks the heights of every * item to build a prefix-sum array, then locates the visible slice with two * linear searches. * * Cost is O(N) per call, walking the whole `items` array once to build the * prefix sums. For lists in the 10k-item range, this comfortably fits inside * a 60Hz scroll budget. Larger lists or hotter scroll paths can layer a * prefix-sum cache invalidated when items change; that lives behind the same * return shape so consumers don't have to know. * * Returns `Option.none()` when the container has not yet been measured. */ export declare const visibleWindowVariable: (model: Model, items: ReadonlyArray, itemToRowHeightPx: (item: Item, index: number) => number, overscan: number) => Option.Option; /** Subscriptions that track the container's scroll position and size. * * - **scroll**: listens for `scroll` events on the container element and * emits `ScrolledContainer` with the new `scrollTop`. * - **resize**: observes the container with `ResizeObserver` and emits * `MeasuredContainer` with the new height. * * A `MutationObserver` watches the document for the container element * appearing and disappearing, so the listeners attach the moment the * element is inserted into the DOM and clean up when it is removed. This * makes the subscription robust across SPA route changes: navigating to a * page that mounts the list, away, and back all reattach correctly without * the consumer having to teach the framework about navigation. */ export declare const subscriptions: { readonly containerEvents: Subscription.EntryWithoutKeepAlive<{ readonly id: string; readonly rowHeightPx: number; readonly scrollTop: number; readonly measurement: { readonly _tag: "Unmeasured"; } | { readonly _tag: "Measured"; readonly containerHeight: number; }; readonly pendingScroll: { readonly _tag: "Idle"; } | { readonly _tag: "ScrollingToIndex"; readonly index: number; readonly version: number; }; readonly pendingScrollVersion: number; }, { readonly _tag: "ScrolledContainer"; readonly scrollTop: number; } | { readonly _tag: "MeasuredContainer"; readonly containerHeight: number; } | { readonly _tag: "CompletedApplyScroll"; readonly version: number; }, { readonly id: string; }, never> & { readonly __subscription: never; }; }; /** Per-render view inputs passed to `view` via `h.submodel`'s `viewInputs` field. * * VirtualList does not surface event handlers in the view. All input * (scroll events and resize observations) flows through the * `containerEvents` Subscription. The consumer wraps that * Subscription's stream into their parent Message in their own * `subscriptions` definition. */ export type ViewInputs = Readonly<{ items: ReadonlyArray; itemToKey: (item: Item, index: number) => string; itemToView: (item: Item, index: number) => Html; itemToRowHeightPx?: (item: Item, index: number) => number; overscan?: number; rowElement?: TagName; containerClassName?: string; containerAttributes?: ReadonlyArray; }>; /** Renders a virtualized list. Only items inside the viewport (plus an * overscan buffer) are mounted; spacer elements above and below the * slice keep the scrollbar's apparent total height correct. * * Generic over `Item`: call as `VirtualList.view()` at the * embed site to get a `SubmodelView` typed for your item type. The * underlying view implementation is shared; the call only narrows the * type. */ type ViewForItem = SubmodelView>; export declare const view: () => ViewForItem; export {}; //# sourceMappingURL=index.d.ts.map