import type { ReactNode } from "react"; import type { GridLayout } from "../AdaptiveGrid/chooseColumns.ts"; import type { BlockSizeResolver } from "../AdaptiveGrid/useAdaptiveColumns.ts"; export type VirtualizedGridProps = { /** * Where the available block size is read from. Defaults to the * viewport, which is the only thing that can answer "will this * scroll". */ blockSizeResolver?: BlockSizeResolver; /** * Everything down the page that is not this grid, in CSS px — * header, rails, filter rows. Subtracted before asking how many * items stack. */ chromeBlockSize?: number; className?: string; /** * Draw this many columns instead of the automatic answer — the * manual override, for a page that hoisted `useAdaptiveColumns` * itself. */ columns?: number; /** The gap between tracks and rows, in CSS px. */ gap?: number; /** * A stable identity per item. Without one the key is the index, * which is wrong the moment the list is filtered or an item * leaves the middle — React reuses the mounted subtree and the * wrong tile inherits the wrong open menu. */ getItemKey?: (item: Item, index: number) => string | number; /** * One row's block size in CSS px, as a starting guess. * * Unlike `AdaptiveGrid`'s, this is an *estimate* and not a * commitment: every mounted row is measured and the estimate * corrected. It still wants to be close, because it is what the * scrollbar is sized from before anything has been measured — a * bad guess makes the bar jump under the thumb on first scroll. */ itemBlockSize: number; /** Every item, mounted or not. */ items: readonly Item[]; /** * An accessible name for the list. Worth giving: a windowed list * is one whose length a reader cannot count for itself. */ label?: string; /** Opt in to width-first media layout. */ layout?: GridLayout; /** The most columns the automatic answer will take. */ maxColumns?: number; /** The narrowest a column may be, in CSS px. */ minColumnInlineSize?: number; /** * How many rows to mount above and below the viewport. * * Four rather than `@tanstack/react-virtual`'s default of one: * these are media tiles whose images load over the network, and * a row that mounts at the moment it becomes visible shows a * blank box while the image arrives. Overscan buys the load a * head start. */ overscanRows?: number; /** Draw one item. */ renderItem: (item: Item, index: number) => ReactNode; /** * The `localStorage` key a manual column choice is remembered * under. Namespace it to the app; omit it and nothing is * written. */ storageKey?: string; }; /** * `AdaptiveGrid`'s layout, with only the rows you can see mounted. * * The fleet's grids render every item they are given. That is the * right default, and it stops being right somewhere in the low * thousands: QueuePilot's Pending page put **2,162** tiles on one * screen — 19,933 DOM nodes, 2,162 ``, 4,371 `