UNPKG

17 kBTypeScriptView Raw
1import { Component, PureComponent, Requireable, Validator } from "react";
2import { Alignment, Index, Map, OverscanIndexRange } from "../../index";
3import { CellMeasurerCache, CellMeasurerCacheInterface, MeasuredCellParent } from "./CellMeasurer";
4import { List } from "./List";
5import { Table } from "./Table";
6
7export type RenderedSection = {
8 columnOverscanStartIndex: number;
9 columnOverscanStopIndex: number;
10 columnStartIndex: number;
11 columnStopIndex: number;
12 rowOverscanStartIndex: number;
13 rowOverscanStopIndex: number;
14 rowStartIndex: number;
15 rowStopIndex: number;
16};
17
18export type GridCellProps = {
19 columnIndex: number;
20 isScrolling: boolean;
21 isVisible: boolean;
22 key: string;
23 parent: React.Component<GridCoreProps> & MeasuredCellParent;
24 rowIndex: number;
25 style: React.CSSProperties;
26};
27export type GridCellRenderer = (props: GridCellProps) => React.ReactNode;
28
29export type ConfigureParams = {
30 cellCount: number;
31 estimatedCellSize: number;
32};
33export type ContainerSizeAndOffset = {
34 containerSize: number;
35 offset: number;
36};
37export type SizeAndPositionData = {
38 offset: number;
39 size: number;
40};
41export type GetVisibleCellRangeParams = {
42 containerSize: number;
43 offset: number;
44};
45export type VisibleCellRange = {
46 start: number;
47 stop: number;
48};
49export type ScrollParams = {
50 clientHeight: number;
51 clientWidth: number;
52 scrollHeight: number;
53 scrollLeft: number;
54 scrollTop: number;
55 scrollWidth: number;
56};
57export type ScrollbarPresenceParams = {
58 horizontal: boolean;
59 size: number;
60 vertical: boolean;
61};
62export type SectionRenderedParams = RenderedSection;
63export type SCROLL_DIRECTION_HORIZONTAL = "horizontal";
64export type SCROLL_DIRECTION_VERTICAL = "vertical";
65export type SCROLL_DIRECTION_BACKWARD = -1;
66export type SCROLL_DIRECTION_FORWARD = 1;
67export type OverscanIndicesGetterParams = {
68 direction?: SCROLL_DIRECTION_HORIZONTAL | SCROLL_DIRECTION_VERTICAL | undefined;
69 cellCount: number;
70 overscanCellsCount: number;
71 scrollDirection: SCROLL_DIRECTION_BACKWARD | SCROLL_DIRECTION_FORWARD;
72 startIndex: number;
73 stopIndex: number;
74};
75
76export type OverscanIndices = OverscanIndexRange;
77
78export type OverscanIndicesGetter = (params: OverscanIndicesGetterParams) => OverscanIndices;
79
80export type ScrollOffset = {
81 scrollLeft: number;
82 scrollTop: number;
83};
84
85export type CellSizeAndPositionManager = {
86 areOffsetsAdjusted(): boolean;
87 configure({ cellCount, estimatedCellSize }: ConfigureParams): void;
88 getCellCount(): number;
89 getEstimatedCellSize(): number;
90 getLastMeasuredIndex(): number;
91 getOffsetAdjustment({ containerSize, offset /*safe*/ }: ContainerSizeAndOffset): number;
92 /**
93 * This method returns the size and position for the cell at the specified index.
94 * It just-in-time calculates (or used cached values) for cells leading up to the index.
95 */
96 getSizeAndPositionOfCell(index: number): SizeAndPositionData;
97 getSizeAndPositionOfLastMeasuredCell(): SizeAndPositionData;
98 /**
99 * Total size of all cells being measured.
100 * This value will be completedly estimated initially.
101 * As cells as measured the estimate will be updated.
102 */
103 getTotalSize(): number;
104 /**
105 * Determines a new offset that ensures a certain cell is visible, given the current offset.
106 * If the cell is already visible then the current offset will be returned.
107 * If the current offset is too great or small, it will be adjusted just enough to ensure the specified index is visible.
108 *
109 * @param align Desired alignment within container; one of "auto" (default), "start", or "end"
110 * @param containerSize Size (width or height) of the container viewport
111 * @param currentOffset Container's current (x or y) offset
112 * @param totalSize Total size (width or height) of all cells
113 * @return Offset to use to ensure the specified cell is visible
114 */
115 getUpdatedOffsetForIndex(params: {
116 align: string;
117 containerSize: number;
118 currentOffset: number;
119 targetIndex: number;
120 }): number;
121 getVisibleCellRange(params: GetVisibleCellRangeParams): VisibleCellRange;
122 /**
123 * Clear all cached values for cells after the specified index.
124 * This method should be called for any cell that has changed its size.
125 * It will not immediately perform any calculations; they'll be performed the next time getSizeAndPositionOfCell() is called.
126 */
127 resetCell(index: number): void;
128};
129
130export type GridCellRangeProps = {
131 cellCache: Map<any>;
132 cellRenderer: GridCellRenderer;
133 columnSizeAndPositionManager: CellSizeAndPositionManager;
134 columnStartIndex: number;
135 columnStopIndex: number;
136 isScrolling: boolean;
137 isScrollingOptOut: boolean;
138 rowSizeAndPositionManager: CellSizeAndPositionManager;
139 rowStartIndex: number;
140 rowStopIndex: number;
141 scrollLeft: number;
142 scrollTop: number;
143 deferredMeasurementCache: CellMeasurerCache;
144 horizontalOffsetAdjustment: number;
145 parent: React.Component<GridCoreProps> & MeasuredCellParent;
146 styleCache: Map<React.CSSProperties>;
147 verticalOffsetAdjustment: number;
148 visibleColumnIndices: VisibleCellRange;
149 visibleRowIndices: VisibleCellRange;
150};
151export type GridCellRangeRenderer = (params: GridCellRangeProps) => React.ReactNode[];
152
153export type GridCoreProps = {
154 "aria-label"?: string | undefined;
155 "aria-readonly"?: boolean | undefined;
156 /**
157 * Set the width of the inner scrollable container to 'auto'.
158 * This is useful for single-column Grids to ensure that the column doesn't extend below a vertical scrollbar.
159 */
160 autoContainerWidth?: boolean | undefined;
161 /**
162 * Removes fixed height from the scrollingContainer so that the total height of rows can stretch the window.
163 * Intended for use with WindowScroller
164 */
165 autoHeight?: boolean | undefined;
166 /**
167 * Removes fixed width from the scrollingContainer so that the total width of rows can stretch the window.
168 * Intended for use with WindowScroller
169 */
170 autoWidth?: boolean | undefined;
171 /**
172 * Responsible for rendering a group of cells given their index ranges.
173 * Should implement the following interface: ({
174 * cellCache: Map,
175 * cellRenderer: Function,
176 * columnSizeAndPositionManager: CellSizeAndPositionManager,
177 * columnStartIndex: number,
178 * columnStopIndex: number,
179 * isScrolling: boolean,
180 * rowSizeAndPositionManager: CellSizeAndPositionManager,
181 * rowStartIndex: number,
182 * rowStopIndex: number,
183 * scrollLeft: number,
184 * scrollTop: number
185 * }): Array<PropTypes.node>
186 */
187 cellRangeRenderer?: GridCellRangeRenderer | undefined;
188 /**
189 * Optional custom CSS class name to attach to root Grid element.
190 */
191 className?: string | undefined;
192 /** Unfiltered props for the Grid container. */
193 containerProps?: object | undefined;
194 /** ARIA role for the cell-container. */
195 containerRole?: string | undefined;
196 /** Optional inline style applied to inner cell-container */
197 containerStyle?: React.CSSProperties | undefined;
198 /**
199 * If CellMeasurer is used to measure this Grid's children, this should be a pointer to its CellMeasurerCache.
200 * A shared CellMeasurerCache reference enables Grid and CellMeasurer to share measurement data.
201 */
202 deferredMeasurementCache?: CellMeasurerCacheInterface | undefined;
203 /**
204 * Used to estimate the total width of a Grid before all of its columns have actually been measured.
205 * The estimated total width is adjusted as columns are rendered.
206 */
207 estimatedColumnSize?: number | undefined;
208 /**
209 * Used to estimate the total height of a Grid before all of its rows have actually been measured.
210 * The estimated total height is adjusted as rows are rendered.
211 */
212 estimatedRowSize?: number | undefined;
213 /**
214 * Exposed for testing purposes only.
215 */
216 getScrollbarSize?: (() => number) | undefined;
217 /**
218 * Height of Grid; this property determines the number of visible (vs virtualized) rows.
219 */
220 height: number;
221 /**
222 * Optional custom id to attach to root Grid element.
223 */
224 id?: string | undefined;
225 /**
226 * Override internal is-scrolling state tracking.
227 * This property is primarily intended for use with the WindowScroller component.
228 */
229 isScrolling?: boolean | undefined;
230 /**
231 * Optional renderer to be used in place of rows when either :rowCount or :columnCount is 0.
232 */
233 noContentRenderer?: (() => React.ReactNode) | undefined;
234 /**
235 * Callback invoked whenever the scroll offset changes within the inner scrollable region.
236 * This callback can be used to sync scrolling between lists, tables, or grids.
237 * ({ clientHeight, clientWidth, scrollHeight, scrollLeft, scrollTop, scrollWidth }): void
238 */
239 onScroll?: ((params: ScrollParams) => any) | undefined;
240 /**
241 * Called whenever a horizontal or vertical scrollbar is added or removed.
242 * ({ horizontal: boolean, size: number, vertical: boolean }): void
243 */
244 onScrollbarPresenceChange?: ((params: ScrollbarPresenceParams) => any) | undefined;
245 /**
246 * Callback invoked with information about the section of the Grid that was just rendered.
247 * ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }): void
248 */
249 onSectionRendered?: ((params: SectionRenderedParams) => any) | undefined;
250 /**
251 * Number of columns to render before/after the visible section of the grid.
252 * These columns can help for smoother scrolling on touch devices or browsers that send scroll events infrequently.
253 */
254 overscanColumnCount?: number | undefined;
255 /**
256 * Calculates the number of cells to overscan before and after a specified range.
257 * This function ensures that overscanning doesn't exceed the available cells.
258 * Should implement the following interface: ({
259 * cellCount: number,
260 * overscanCellsCount: number,
261 * scrollDirection: number,
262 * startIndex: number,
263 * stopIndex: number
264 * }): {overscanStartIndex: number, overscanStopIndex: number}
265 */
266 overscanIndicesGetter?: OverscanIndicesGetter | undefined;
267 /**
268 * Number of rows to render above/below the visible section of the grid.
269 * These rows can help for smoother scrolling on touch devices or browsers that send scroll events infrequently.
270 */
271 overscanRowCount?: number | undefined;
272 /**
273 * ARIA role for the grid element.
274 */
275 role?: string | undefined;
276 /**
277 * Either a fixed row height (number) or a function that returns the height of a row given its index.
278 * Should implement the following interface: ({ index: number }): number
279 */
280 rowHeight: number | ((params: Index) => number);
281 /**
282 * Number of rows in grid.
283 */
284 rowCount: number;
285 /** Wait this amount of time after the last scroll event before resetting Grid `pointer-events`. */
286 scrollingResetTimeInterval?: number | undefined;
287 /** Horizontal offset. */
288 scrollLeft?: number | undefined;
289 /**
290 * Controls scroll-to-cell behavior of the Grid.
291 * The default ("auto") scrolls the least amount possible to ensure that the specified cell is fully visible.
292 * Use "start" to align cells to the top/left of the Grid and "end" to align bottom/right.
293 */
294 scrollToAlignment?: Alignment | undefined;
295 /**
296 * Column index to ensure visible (by forcefully scrolling if necessary)
297 */
298 scrollToColumn?: number | undefined;
299 /** Vertical offset. */
300 scrollTop?: number | undefined;
301 /**
302 * Row index to ensure visible (by forcefully scrolling if necessary)
303 */
304 scrollToRow?: number | undefined;
305 /** Optional inline style */
306 style?: React.CSSProperties | undefined;
307 /** Tab index for focus */
308 tabIndex?: number | null | undefined;
309 /**
310 * Width of Grid; this property determines the number of visible (vs virtualized) columns.
311 */
312 width: number;
313 /**
314 * PLEASE NOTE
315 * The [key: string]: any; line is here on purpose
316 * This is due to the need of force re-render of PureComponent
317 * Check the following link if you want to know more
318 * https://github.com/bvaughn/react-virtualized#pass-thru-props
319 */
320 [key: string]: any;
321};
322
323export type GridProps = GridCoreProps & {
324 /**
325 * Responsible for rendering a cell given an row and column index.
326 * Should implement the following interface: ({ columnIndex: number, rowIndex: number }): PropTypes.node
327 */
328 cellRenderer: GridCellRenderer;
329 /**
330 * Number of columns in grid.
331 */
332 columnCount: number;
333 /**
334 * Either a fixed column width (number) or a function that returns the width of a column given its index.
335 * Should implement the following interface: (index: number): number
336 */
337 columnWidth: number | ((params: Index) => number);
338};
339
340export type ScrollDirection = "horizontal" | "vertical";
341
342export type GridState = {
343 isScrolling: boolean;
344 scrollDirectionHorizontal: ScrollDirection;
345 scrollDirectionVertical: ScrollDirection;
346 scrollLeft: number;
347 scrollTop: number;
348};
349
350/**
351 * Specifies the number of miliseconds during which to disable pointer events while a scroll is in progress.
352 * This improves performance and makes scrolling smoother.
353 */
354export const DEFAULT_SCROLLING_RESET_TIME_INTERVAL = 150;
355
356/**
357 * Renders tabular data with virtualization along the vertical and horizontal axes.
358 * Row heights and column widths must be known ahead of time and specified as properties.
359 */
360export class Grid extends PureComponent<GridProps, GridState> {
361 static defaultProps: {
362 "aria-label": "grid";
363 "aria-readonly": true;
364 autoContainerWidth: false;
365 autoHeight: false;
366 autoWidth: false;
367 cellRangeRenderer: GridCellRangeRenderer;
368 containerRole: "rowgroup";
369 containerStyle: {};
370 estimatedColumnSize: 100;
371 estimatedRowSize: 30;
372 getScrollbarSize: () => number;
373 noContentRenderer: () => React.ReactNode;
374 onScroll: () => void;
375 onScrollbarPresenceChange: () => void;
376 onSectionRendered: () => void;
377 overscanColumnCount: 0;
378 overscanIndicesGetter: OverscanIndicesGetter;
379 overscanRowCount: 10;
380 role: "grid";
381 scrollingResetTimeInterval: typeof DEFAULT_SCROLLING_RESET_TIME_INTERVAL;
382 scrollToAlignment: "auto";
383 scrollToColumn: -1;
384 scrollToRow: -1;
385 style: {};
386 tabIndex: 0;
387 };
388
389 /**
390 * Gets offsets for a given cell and alignment.
391 */
392 getOffsetForCell(
393 params?: { alignment?: Alignment | undefined; columnIndex?: number | undefined; rowIndex?: number | undefined },
394 ): ScrollOffset;
395
396 /**
397 * This method handles a scroll event originating from an external scroll control.
398 * It's an advanced method and should probably not be used unless you're implementing a custom scroll-bar solution.
399 */
400 handleScrollEvent(params: Partial<ScrollOffset>): void;
401
402 /**
403 * Invalidate Grid size and recompute visible cells.
404 * This is a deferred wrapper for recomputeGridSize().
405 * It sets a flag to be evaluated on cDM/cDU to avoid unnecessary renders.
406 * This method is intended for advanced use-cases like CellMeasurer.
407 */
408 // @TODO (bvaughn) Add automated test coverage for this.
409 invalidateCellSizeAfterRender(params: { columnIndex: number; rowIndex: number }): void;
410
411 /**
412 * Pre-measure all columns and rows in a Grid.
413 * Typically cells are only measured as needed and estimated sizes are used for cells that have not yet been measured.
414 * This method ensures that the next call to getTotalSize() returns an exact size (as opposed to just an estimated one).
415 */
416 measureAllCells(): void;
417
418 /**
419 * Forced recompute of row heights and column widths.
420 * This function should be called if dynamic column or row sizes have changed but nothing else has.
421 * Since Grid only receives :columnCount and :rowCount it has no way of detecting when the underlying data changes.
422 */
423 recomputeGridSize(params?: { columnIndex?: number | undefined; rowIndex?: number | undefined }): void;
424
425 /**
426 * Ensure column and row are visible.
427 */
428 scrollToCell(params: { columnIndex: number; rowIndex: number }): void;
429
430 /**
431 * Scroll to the specified offset(s).
432 * Useful for animating position changes.
433 */
434 scrollToPosition(params?: { scrollLeft: number; scrollTop: number }): void;
435}
436
437export default Grid;
438
439export const defaultCellRangeRenderer: GridCellRangeRenderer;
440
441export const accessibilityOverscanIndicesGetter: OverscanIndicesGetter;
442
443export const defaultOverscanIndicesGetter: OverscanIndicesGetter;