/
components.tsx
362 lines (308 loc) · 11.9 KB
/
components.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
import { ForwardRefExoticComponent, ReactElement, Ref } from 'react'
import { Grid, GridHandle } from './Grid'
import {
Components,
ComputeItemKey,
FollowOutput,
GridComponents,
GridComputeItemKey,
GridItemContent,
GroupContent,
GroupItemContent,
IndexLocationWithAlign,
ItemContent,
ListItem,
ListRange,
ScrollSeekConfiguration,
} from './interfaces'
import { List, ListHandle } from './List'
import { ScrollIntoViewLocation } from './scrollIntoViewSystem'
type CompProps<T> = T extends ForwardRefExoticComponent<infer R> ? R : never
type ListProps = CompProps<typeof List>
type GridProps = CompProps<typeof Grid>
export interface VirtuosoProps<D> extends Omit<ListProps, 'groupCounts' | 'groupContent' | 'itemsRendered'> {
/**
* The total amount of items to be rendered.
*/
totalCount?: number
/**
* The data items to be rendered. If data is set, the total count will be inferred from the length of the array.
*/
data?: readonly D[]
/**
* Set the overscan property to make the component "chunk" the rendering of new items on scroll.
* The property causes the component to render more items than the necessary, but reduces the re-renders on scroll.
* Setting `{ main: number, reverse: number }` lets you extend the list in both the main and the reverse scrollable directions.
* See the `increaseViewportBy` property for a similar behavior (equivalent to the `overscan` in `react-window`).
*/
overscan?: number | { main: number; reverse: number }
/**
* Set the increaseViewportBy property to artificially increase the viewport size, causing items to be rendered before outside of the viewport.
* The property causes the component to render more items than the necessary, but can help with slow loading content.
* Using `{ top?: number, bottom?: number }` lets you set the increase for each end separately.
*/
increaseViewportBy?: number | { top: number; bottom: number }
/**
* Set the amount of items to remain fixed at the top of the list.
*
* For a header that scrolls away when scrolling, check the `components.Header` property.
*/
topItemCount?: number
/**
* Set to a value between 0 and totalCount - 1 to make the list start scrolled to that item.
*/
initialTopMostItemIndex?: number
/**
* Set this value to offset the initial location of the list.
* Warning: using this property will still run a render cycle at the scrollTop: 0 list window.
* If possible, avoid using it and stick to `initialTopMostItemIndex` instead.
*/
initialScrollTop?: number
/**
* Use for server-side rendering - if set, the list will render the specified amount of items
* regardless of the container / item size.
*/
initialItemCount?: number
/**
* Use the `components` property for advanced customization of the elements rendered by the list.
*/
components?: Components
/**
* Set the callback to specify the contents of the item.
*/
itemContent?: ItemContent<D>
/**
* If specified, the component will use the function to generate the `key` property for each list item.
*/
computeItemKey?: ComputeItemKey<D>
/**
* By default, the component assumes the default item height from the first rendered item (rendering it as a "probe").
*
* If the first item turns out to be an outlier (very short or tall), the rest of the rendering will be slower,
* as multiple passes of rendering should happen for the list to fill the viewport.
*
* Setting `defaultItemHeight` causes the component to skip the "probe" rendering and use the property
* value as default height instead.
*/
defaultItemHeight?: number
/**
* Allows customizing the height/width calculation of `Item` elements.
*
* The default implementation reads `el.getBoundingClientRect().height` and `el.getBoundingClientRect().width`.
*/
itemSize?: (el: HTMLElement, field: 'offsetHeight' | 'offsetWidth') => number
/**
* Can be used to improve performance if the rendered items are of known size.
* Setting it causes the component to skip item measurements.
*/
fixedItemHeight?: number
/**
* Use to display placeholders if the user scrolls fast through the list.
*
* Set `components.ScrollSeekPlaceholder` to change the placeholder content.
*/
scrollSeekConfiguration?: ScrollSeekConfiguration | false
/**
* If set to `true`, the list automatically scrolls to bottom if the total count is changed.
* Set to `"smooth"` for an animated scrolling.
*
* By default, `followOutput` scrolls down only if the list is already at the bottom.
* To implement an arbitrary logic behind that, pass a function:
*
* ```tsx
* <Virtuoso
* followOutput={(isAtBottom: boolean) => {
* if (expression) {
* return 'smooth' // can be 'auto' or false to avoid scrolling
* } else {
* return false
* }
* }} />
* ```
*/
followOutput?: FollowOutput
/**
* Set to customize the wrapper tag for the header and footer components (default is `div`).
*/
headerFooterTag?: string
/**
* Use when implementing inverse infinite scrolling - decrease the value this property
* in combination with `data` or `totalCount` to prepend items to the top of the list.
*
* Warning: the firstItemIndex should **be a positive number**, based on the total amount of items to be displayed.
*/
firstItemIndex?: number
/**
* Called when the list starts/stops scrolling.
*/
isScrolling?: (isScrolling: boolean) => void
/**
* Gets called when the user scrolls to the end of the list.
* Receives the last item index as an argument. Can be used to implement endless scrolling.
*/
endReached?: (index: number) => void
/**
* Called when the user scrolls to the start of the list.
*/
startReached?: (index: number) => void
/**
* Called with the new set of items each time the list items are rendered due to scrolling.
*/
rangeChanged?: (range: ListRange) => void
/**
* Called with true / false when the list has reached the bottom / gets scrolled up.
* Can be used to load newer items, like `tail -f`.
*/
atBottomStateChange?: (atBottom: boolean) => void
/**
* Called with `true` / `false` when the list has reached the top / gets scrolled down.
*/
atTopStateChange?: (atTop: boolean) => void
/**
* Called when the total list height is changed due to new items or viewport resize.
*/
totalListHeightChanged?: (height: number) => void
/**
* Called with the new set of items each time the list items are rendered due to scrolling.
*/
itemsRendered?: (items: ListItem<D>[]) => void
/**
* Setting `alignToBottom` to `true` aligns the items to the bottom of the list if the list is shorter than the viewport.
* Use `followOutput` property to keep the list aligned when new items are appended.
*/
alignToBottom?: boolean
/**
* Uses the document scroller rather than wrapping the list in its own.
*/
useWindowScroll?: boolean
/**
* Provides access to the root DOM element
*/
scrollerRef?: (ref: HTMLElement | Window | null) => any
/**
* By default `4`. Redefine to change how much away from the bottom the scroller can be before the list is not considered not at bottom.
*/
atBottomThreshold?: number
}
export interface GroupedVirtuosoProps<D>
extends Omit<VirtuosoProps<D>, 'totalCount' | 'itemContent'>,
Pick<ListProps, 'groupCounts' | 'groupContent'> {
/**
* Specifies the amount of items in each group (and, actually, how many groups are there).
* For example, passing [20, 30] will display 2 groups with 20 and 30 items each.
*/
groupCounts?: number[]
/**
* Specifies how each each group header gets rendered. The callback receives the zero-based index of the group.
*/
groupContent?: GroupContent
/**
* Specifies how each each item gets rendered.
*/
itemContent?: GroupItemContent<D>
}
export interface VirtuosoGridProps extends GridProps {
/**
* The total amount of items to be rendered.
*/
totalCount: number
/**
* Set the callback to specify the contents of the item.
*/
itemContent?: GridItemContent
/**
* Use the `components` property for advanced customization of the elements rendered by the list.
*/
components?: GridComponents
/**
* Set the overscan property to make the component "chunk" the rendering of new items on scroll.
* The property causes the component to render more items than the necessary, but reduces the re-renders on scroll.
* Setting `{ main: number, reverse: number }` lets you extend the list in both the main and the reverse scrollable directions.
*/
overscan?: number | { main: number; reverse: number }
/**
* If specified, the component will use the function to generate the `key` property for each list item.
*/
computeItemKey?: GridComputeItemKey
/**
* Use to display placeholders if the user scrolls fast through the list.
*
* Set `components.ScrollSeekPlaceholder` to change the placeholder content.
*/
scrollSeekConfiguration?: ScrollSeekConfiguration | false
/**
* Called when the list starts/stops scrolling.
*/
isScrolling?: (isScrolling: boolean) => void
/**
* Gets called when the user scrolls to the end of the list.
* Receives the last item index as an argument. Can be used to implement endless scrolling.
*/
endReached?: (index: number) => void
/**
* Called when the user scrolls to the start of the list.
*/
startReached?: (index: number) => void
/**
* Called with the new set of items each time the list items are rendered due to scrolling.
*/
rangeChanged?: (range: ListRange) => void
/**
* Called with true / false when the list has reached the bottom / gets scrolled up.
* Can be used to load newer items, like `tail -f`.
*/
atBottomStateChange?: (atBottom: boolean) => void
/**
* Called with `true` / `false` when the list has reached the top / gets scrolled down.
*/
atTopStateChange?: (atTop: boolean) => void
/**
* Provides access to the root DOM element
*/
scrollerRef?: (ref: HTMLElement | null) => any
/**
* Sets the className for the list DOM element
*/
listClassName?: string
/**
* Sets the grid items' className
*/
itemClassName?: string
/**
* Uses the document scroller rather than wrapping the grid in its own.
*/
useWindowScroll?: boolean
}
export interface VirtuosoHandle extends ListHandle {
/**
* Scrolls the component to the specified item index. See {{IndexLocationWithAlign}} for more options.
*/
scrollToIndex(location: number | IndexLocationWithAlign): void
/**
* Scrolls the item into view if necessary. See [the website example](http://virtuoso.dev/keyboard-navigation/) for an implementation.
*/
scrollIntoView(location: ScrollIntoViewLocation): void
/**
* Scrolls the component to the specified location. See [ScrollToOptions (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions)
*/
scrollTo(location: ScrollToOptions): void
/**
* Scrolls the component with the specified amount. See [ScrollToOptions (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions)
*/
scrollBy(location: ScrollToOptions): void
}
export interface GroupedVirtuosoHandle extends ListHandle {
scrollToIndex(location: number | IndexLocationWithAlign): void
scrollTo(location: ScrollToOptions): void
scrollBy(location: ScrollToOptions): void
}
export interface VirtuosoGridHandle extends GridHandle {
scrollToIndex(location: number | IndexLocationWithAlign): void
scrollTo(location: ScrollToOptions): void
scrollBy(location: ScrollToOptions): void
}
export const Virtuoso = List as <D extends unknown = any>(props: VirtuosoProps<D> & { ref?: Ref<VirtuosoHandle> }) => ReactElement
export const GroupedVirtuoso = List as <D extends unknown = any>(
props: GroupedVirtuosoProps<D> & { ref?: Ref<GroupedVirtuosoHandle> }
) => ReactElement
export const VirtuosoGrid = Grid as (props: VirtuosoGridProps & { ref?: Ref<VirtuosoGridHandle> }) => ReactElement