@candidstartup/react-virtual-scroll 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2022, Tim Wiegand
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @candidstartup/react-virtual-scroll
2
+
3
+ React virtual scrolling components for lists and grids following the same philosophy as [react-window](https://github.com/bvaughn/react-window). Written in TypeScript using modern React. Scalable to trillions of rows and columns.
4
+
5
+ ## Interface
6
+
7
+ The interface is similar to `react-window` with two main changes. First, the components are functional rather than class based. Second, rather than having fixed and variable size variants of each component, the components are passed an `ItemOffsetMapping` object as a prop. This is an interface that the components query to determine the size and position of each item.
8
+
9
+ The interface is [designed](https://www.thecandidstartup.org/2024/02/12/modern-react-virtual-scroll-grid-3.html) to ensure that the components have no scaling issues even with trillions of rows or columns. The package includes basic fixed and variable size item mappings.
10
+
11
+ ## Implementation
12
+
13
+ Most of the logic is implemented by custom hooks that are used by both `VirtualList` and `VirtualGrid` controls. The logic that manages scrolling in a single dimension is implemented in `useVirtualScroll`. It's based on an [improved version](https://www.thecandidstartup.org/2024/04/29/modern-react-virtual-scroll-grid-9.html) of [SlickGrid's](https://github.com/6pac/SlickGrid) paged scrolling algorithm. One instance of the hook is used by `VirtualList` and two instances by `VirtualGrid`.
14
+
15
+ ## VirtualList Example
16
+
17
+ ```jsx
18
+ import { VirtualList, useVariableSizeItemOffsetMapping } from '@candidstartup/react-virtual-scroll';
19
+
20
+ const mapping = useVariableSizeItemOffsetMapping(30, [50]);
21
+
22
+ const Row = ({ index, isScrolling, style }) => (
23
+ <div className={ index == 0 ? "header" : ( isScrolling ? "cellScroll" : "cell") } style={style}>
24
+ { (index == 0) ? "Header" : "Item " + index }
25
+ </div>
26
+ );
27
+
28
+ <VirtualList
29
+ height={240}
30
+ itemCount={1000000000000}
31
+ itemOffsetMapping={mapping}
32
+ useIsScrolling={true}
33
+ width={600}>
34
+ {Row}
35
+ </VirtualList>
36
+ ```
37
+
38
+ ## VirtualGrid Example
39
+
40
+ ```jsx
41
+ import { VirtualList, useVariableSizeItemOffsetMapping, useFixedSizeItemOffsetMapping } from '@candidstartup/react-virtual-scroll';
42
+
43
+ const rowMapping = useVariableSizeItemOffsetMapping(30, [50]);
44
+ const columnMapping = useFixedSizeItemOffsetMapping(280);
45
+
46
+ const Cell = ({ rowIndex, columnIndex, style }) => (
47
+ <div className={ rowIndex == 0 ? "header" : "cell" } style={style}>
48
+ { (rowIndex == 0) ? `${columnIndex}` : `${rowIndex}:${columnIndex}` }
49
+ </div>
50
+
51
+ <VirtualGrid
52
+ height={240}
53
+ rowCount={1000000000000}
54
+ rowOffsetMapping={rowMapping}
55
+ columnCount={1000000000000}
56
+ columnOffsetMapping={columnMapping}
57
+ width={600}>
58
+ {Cell}
59
+ </VirtualGrid>
60
+ ```
61
+
62
+ # More
63
+
64
+ Want to know more? Check out my [blog](https://www.thecandidstartup.org/topics/frontend.html)
@@ -0,0 +1,74 @@
1
+ import React from 'react';
2
+
3
+ interface VirtualBaseItemProps {
4
+ data: any;
5
+ isScrolling?: boolean;
6
+ style: Object;
7
+ }
8
+ interface VirtualBaseProps {
9
+ height: number;
10
+ width: number;
11
+ itemData?: any;
12
+ useIsScrolling?: boolean;
13
+ }
14
+ interface ItemOffsetMapping {
15
+ itemSize(itemIndex: number): number;
16
+ itemOffset(itemIndex: number): number;
17
+ offsetToItem(offset: number): [itemIndex: number, startOffset: number];
18
+ }
19
+
20
+ interface VirtualGridItemProps extends VirtualBaseItemProps {
21
+ rowIndex: number;
22
+ columnIndex: number;
23
+ }
24
+ type VirtualGridItem = React.ComponentType<VirtualGridItemProps>;
25
+ interface VirtualGridProps extends VirtualBaseProps {
26
+ children: VirtualGridItem;
27
+ rowCount: number;
28
+ rowOffsetMapping: ItemOffsetMapping;
29
+ columnCount: number;
30
+ columnOffsetMapping: ItemOffsetMapping;
31
+ itemKey?: (rowIndex: number, columnIndex: number, data: any) => any;
32
+ }
33
+ interface VirtualGridProxy {
34
+ scrollTo(rowOffset: number, columnOffset: number): void;
35
+ scrollToItem(rowIndex: number, columnIndex: number): void;
36
+ }
37
+ declare const VirtualGrid: React.ForwardRefExoticComponent<VirtualGridProps & React.RefAttributes<VirtualGridProxy>>;
38
+
39
+ interface VirtualListItemProps extends VirtualBaseItemProps {
40
+ index: number;
41
+ }
42
+ type VirtualListItem = React.ComponentType<VirtualListItemProps>;
43
+ interface VirtualListProps extends VirtualBaseProps {
44
+ children: VirtualListItem;
45
+ itemCount: number;
46
+ itemOffsetMapping: ItemOffsetMapping;
47
+ itemKey?: (index: number, data: any) => any;
48
+ }
49
+ interface VirtualListProxy {
50
+ scrollTo(offset: number): void;
51
+ scrollToItem(index: number): void;
52
+ }
53
+ declare const VirtualList: React.ForwardRefExoticComponent<VirtualListProps & React.RefAttributes<VirtualListProxy>>;
54
+
55
+ declare class FixedSizeItemOffsetMapping implements ItemOffsetMapping {
56
+ constructor(itemSize: number);
57
+ itemSize(_itemIndex: number): number;
58
+ itemOffset(itemIndex: number): number;
59
+ offsetToItem(offset: number): [itemIndex: number, startOffset: number];
60
+ fixedItemSize: number;
61
+ }
62
+ declare function useFixedSizeItemOffsetMapping(itemSize: number): FixedSizeItemOffsetMapping;
63
+
64
+ declare class VariableSizeItemOffsetMapping implements ItemOffsetMapping {
65
+ constructor(defaultItemSize: number, sizes: number[]);
66
+ itemSize(itemIndex: number): number;
67
+ itemOffset(itemIndex: number): number;
68
+ offsetToItem(offset: number): [itemIndex: number, startOffset: number];
69
+ defaultItemSize: number;
70
+ sizes: number[];
71
+ }
72
+ declare function useVariableSizeItemOffsetMapping(defaultItemSize: number, sizes?: number[]): VariableSizeItemOffsetMapping;
73
+
74
+ export { VirtualGrid, type VirtualGridItemProps, type VirtualGridProps, type VirtualGridProxy, VirtualList, type VirtualListItemProps, type VirtualListProps, type VirtualListProxy, useFixedSizeItemOffsetMapping, useVariableSizeItemOffsetMapping };
package/dist/index.js ADDED
@@ -0,0 +1,384 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import React, { useState, createRef, useRef, useEffect, Fragment } from 'react';
3
+
4
+ function getRangeToRender(itemCount, itemOffsetMapping, clientExtent, scrollOffset) {
5
+ if (itemCount == 0) {
6
+ return [0, 0, []];
7
+ }
8
+ var [itemIndex, startOffset] = itemOffsetMapping.offsetToItem(scrollOffset);
9
+ itemIndex = Math.max(0, Math.min(itemCount - 1, itemIndex));
10
+ var endOffset = scrollOffset + clientExtent;
11
+ const overscanBackward = 1;
12
+ const overscanForward = 1;
13
+ for (let step = 0; step < overscanBackward && itemIndex > 0; step++) {
14
+ itemIndex--;
15
+ startOffset -= itemOffsetMapping.itemSize(itemIndex);
16
+ }
17
+ const startIndex = itemIndex;
18
+ var offset = startOffset;
19
+ const sizes = [];
20
+ while (offset < endOffset && itemIndex < itemCount) {
21
+ const size = itemOffsetMapping.itemSize(itemIndex);
22
+ sizes.push(size);
23
+ offset += size;
24
+ itemIndex++;
25
+ }
26
+ for (let step = 0; step < overscanForward && itemIndex < itemCount; step++) {
27
+ const size = itemOffsetMapping.itemSize(itemIndex);
28
+ sizes.push(size);
29
+ itemIndex++;
30
+ }
31
+ return [startIndex, startOffset, sizes];
32
+ }
33
+
34
+ // Max size that is safe across all browsers (Firefox is the limiting factor)
35
+ // SlickGrid tries to dynamically determine limit on other browsers (Chrome will do 30M) but
36
+ // I prefer simplicity of same behavior across all browsers.
37
+ const MAX_SUPPORTED_CSS_SIZE = 6000000;
38
+ const MIN_NUMBER_PAGES = 100;
39
+ function useVirtualScroll(totalSize) {
40
+ let renderSize = 0, pageSize = 0, numPages = 0;
41
+ if (totalSize < MAX_SUPPORTED_CSS_SIZE) {
42
+ // No paging needed
43
+ renderSize = pageSize = totalSize;
44
+ numPages = 1;
45
+ }
46
+ else {
47
+ // Break into pages
48
+ renderSize = MAX_SUPPORTED_CSS_SIZE;
49
+ pageSize = renderSize / MIN_NUMBER_PAGES;
50
+ numPages = Math.floor(totalSize / pageSize);
51
+ }
52
+ function pageToRenderOffset(page) {
53
+ if (page <= 0)
54
+ return 0;
55
+ if (page >= numPages - 1)
56
+ return totalSize - renderSize;
57
+ return Math.round((page - 1) * (totalSize - renderSize) / (numPages - 3));
58
+ }
59
+ const initValue = {
60
+ scrollOffset: 0,
61
+ renderOffset: 0,
62
+ page: 0,
63
+ scrollDirection: "forward",
64
+ };
65
+ const [scrollState, setScrollState] = useState(initValue);
66
+ function onScroll(clientExtent, scrollExtent, scrollOffset) {
67
+ if (scrollState.scrollOffset == scrollOffset) {
68
+ // No need to change state if scroll position unchanged
69
+ return scrollOffset;
70
+ }
71
+ // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
72
+ let newOffset = Math.max(0, Math.min(scrollOffset, scrollExtent - clientExtent));
73
+ const newScrollDirection = scrollState.scrollOffset <= newOffset ? 'forward' : 'backward';
74
+ // Switch pages if needed
75
+ let newPage, newRenderOffset;
76
+ let retScrollOffset = scrollOffset;
77
+ const scrollDist = Math.abs(newOffset - scrollState.scrollOffset);
78
+ if (scrollDist < clientExtent) {
79
+ // Scrolling part of visible window, don't want to skip items, so can't scale up movement
80
+ // If we cross page boundary we need to reset scroll bar position back to where it should be at start of page
81
+ newPage = Math.min(numPages - 1, Math.floor((scrollOffset + scrollState.renderOffset) / pageSize));
82
+ newRenderOffset = pageToRenderOffset(newPage);
83
+ if (newPage != scrollState.page) {
84
+ // Be very intentional about when we ask caller to reset scroll bar
85
+ // Don't want to trigger event loops
86
+ newOffset = scrollOffset + scrollState.renderOffset - newRenderOffset;
87
+ retScrollOffset = newOffset;
88
+ }
89
+ }
90
+ else {
91
+ // Large scale scrolling, choosing page from a rolodex
92
+ // First and last page are mapped 1:1 between grid and container
93
+ if (newOffset < pageSize) {
94
+ newPage = 0;
95
+ }
96
+ else if (newOffset >= renderSize - pageSize) {
97
+ newPage = numPages - 1;
98
+ }
99
+ else {
100
+ const scaleFactor = (totalSize - pageSize * 2) / (renderSize - pageSize * 2);
101
+ newPage = Math.min(numPages - 3, Math.floor((newOffset - pageSize) * scaleFactor / pageSize)) + 1;
102
+ }
103
+ newRenderOffset = pageToRenderOffset(newPage);
104
+ }
105
+ setScrollState({ scrollOffset: newOffset, renderOffset: newRenderOffset, page: newPage, scrollDirection: newScrollDirection });
106
+ return retScrollOffset;
107
+ }
108
+ function doScrollTo(offset, clientExtent) {
109
+ const safeOffset = Math.min(totalSize - clientExtent, Math.max(offset, 0));
110
+ const scrollDirection = (scrollState.scrollOffset + scrollState.renderOffset) <= safeOffset ? 'forward' : 'backward';
111
+ const page = Math.min(numPages - 1, Math.floor(safeOffset / pageSize));
112
+ const renderOffset = pageToRenderOffset(page);
113
+ const scrollOffset = safeOffset - renderOffset;
114
+ setScrollState({ scrollOffset, renderOffset, page, scrollDirection });
115
+ return scrollOffset;
116
+ }
117
+ return { ...scrollState, renderSize, onScroll, doScrollTo };
118
+ }
119
+
120
+ // Based on https://github.com/realwugang/use-event-listener
121
+ // and https://github.com/donavon/use-event-listener/blob/develop/src/index.js
122
+ function isListener(element) {
123
+ return element.addEventListener !== undefined;
124
+ }
125
+ function useEventListener(eventName, handler, element = window, options = {}) {
126
+ const savedHandler = useRef();
127
+ const { capture, passive, once } = options;
128
+ useEffect(() => {
129
+ savedHandler.current = handler;
130
+ }, [handler]);
131
+ useEffect(() => {
132
+ if (!element)
133
+ return;
134
+ const el = isListener(element) ? element : element.current;
135
+ if (!el)
136
+ return;
137
+ const eventListener = (event) => savedHandler.current(event);
138
+ const opts = { capture, passive, once };
139
+ el.addEventListener(eventName, eventListener, opts);
140
+ return () => {
141
+ el.removeEventListener(eventName, eventListener, opts);
142
+ };
143
+ }, [eventName, element, capture, passive, once]);
144
+ }
145
+ // In-source testing for private helper functions
146
+ if (import.meta.vitest) {
147
+ const { it, expect } = import.meta.vitest;
148
+ it('isListener', () => {
149
+ expect(isListener(window)).toBe(true);
150
+ expect(isListener(document)).toBe(true);
151
+ expect(isListener(document.createElement("div"))).toBe(true);
152
+ expect(isListener(createRef())).toBe(false);
153
+ });
154
+ }
155
+
156
+ // Based on https://overreacted.io/making-setinterval-declarative-with-react-hooks/
157
+ // and https://www.joshwcomeau.com/snippets/react-hooks/use-timeout/
158
+ // and https://github.com/bvaughn/react-window/blob/master/src/timer.js
159
+ //
160
+ // Equivalent functionality to a useTimeout hook but based on requestAnimationFrame instead of setTimeout. Use
161
+ // when making frequent requests for short duration timeouts where browser may throttle setTimeout.
162
+ function useAnimationTimeout(callback, delay, key) {
163
+ const requestRef = useRef();
164
+ const savedCallback = useRef(callback);
165
+ // Remember the latest callback
166
+ useEffect(() => {
167
+ savedCallback.current = callback;
168
+ }, [callback]);
169
+ const start = performance.now();
170
+ useEffect(() => {
171
+ function tick() {
172
+ requestRef.current = undefined;
173
+ if (delay === null)
174
+ return;
175
+ if (performance.now() - start >= delay) {
176
+ savedCallback.current();
177
+ }
178
+ else {
179
+ requestRef.current = requestAnimationFrame(tick);
180
+ }
181
+ }
182
+ tick();
183
+ return () => {
184
+ if (typeof requestRef.current === 'number') {
185
+ cancelAnimationFrame(requestRef.current);
186
+ requestRef.current = undefined;
187
+ }
188
+ };
189
+ }, [delay, key]);
190
+ }
191
+
192
+ const DEBOUNCE_INTERVAL = 150;
193
+ const FALLBACK_INTERVAL = 500;
194
+ function useIsScrolling(element = window) {
195
+ const [scrollCount, setScrollCount] = useState(0);
196
+ // scrollend implementations in both Chrome and Firefox are buggy with missing scrollend events
197
+ // in some circumstances (using keyboard to scroll past end in Chrome, intermittently when using mouse wheel in Firefox)
198
+ // Use a timeout even when scrollend is supported to handle missing events. In this case we use a longer interval as
199
+ // don't want it to be over sensitive.
200
+ const supportsScrollEnd = ('onscrollend' in window);
201
+ const delay = supportsScrollEnd ? FALLBACK_INTERVAL : DEBOUNCE_INTERVAL;
202
+ useEventListener("scroll", () => setScrollCount(c => c + 1), element);
203
+ useEventListener("scrollend", () => setScrollCount(0), supportsScrollEnd ? element : null);
204
+ useAnimationTimeout(() => setScrollCount(0), (scrollCount == 0) ? null : delay, scrollCount);
205
+ return scrollCount > 0;
206
+ }
207
+
208
+ const defaultItemKey$1 = (rowIndex, columnIndex, _data) => `${rowIndex}:${columnIndex}`;
209
+ // Using a named function rather than => so that the name shows up in React Developer Tools
210
+ const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
211
+ const { width, height, rowCount, rowOffsetMapping, columnCount, columnOffsetMapping, children, itemData = undefined, itemKey = defaultItemKey$1, useIsScrolling: useIsScrolling$1 = false } = props;
212
+ // Total size is same as offset to item one off the end
213
+ const totalRowSize = rowOffsetMapping.itemOffset(rowCount);
214
+ const totalColumnSize = columnOffsetMapping.itemOffset(columnCount);
215
+ const outerRef = React.useRef(null);
216
+ const { scrollOffset: scrollRowOffset, renderOffset: renderRowOffset, renderSize: renderRowSize, onScroll: onScrollRow, doScrollTo: doScrollToRow } = useVirtualScroll(totalRowSize);
217
+ const { scrollOffset: scrollColumnOffset, renderOffset: renderColumnOffset, renderSize: renderColumnSize, onScroll: onScrollColumn, doScrollTo: doScrollToColumn } = useVirtualScroll(totalColumnSize);
218
+ const isScrolling = useIsScrolling(outerRef);
219
+ React.useImperativeHandle(ref, () => {
220
+ return {
221
+ scrollTo(rowOffset, columnOffset) {
222
+ const outer = outerRef.current;
223
+ /* istanbul ignore else */
224
+ if (outer)
225
+ outer.scrollTo(doScrollToColumn(columnOffset, outer.clientWidth), doScrollToRow(rowOffset, outer.clientHeight));
226
+ },
227
+ scrollToItem(rowIndex, columnIndex) {
228
+ this.scrollTo(rowOffsetMapping.itemOffset(rowIndex), columnOffsetMapping.itemOffset(columnIndex));
229
+ }
230
+ };
231
+ }, [rowOffsetMapping, columnOffsetMapping]);
232
+ function onScroll(event) {
233
+ const { clientWidth, clientHeight, scrollWidth, scrollHeight, scrollLeft, scrollTop } = event.currentTarget;
234
+ const newScrollTop = onScrollRow(clientHeight, scrollHeight, scrollTop);
235
+ const newScrollLeft = onScrollColumn(clientWidth, scrollWidth, scrollLeft);
236
+ if (outerRef.current && (newScrollTop != scrollTop || newScrollLeft != scrollLeft))
237
+ outerRef.current.scrollTo(newScrollLeft, newScrollTop);
238
+ }
239
+ const [startRowIndex, startRowOffset, rowSizes] = getRangeToRender(rowCount, rowOffsetMapping, height, scrollRowOffset + renderRowOffset);
240
+ const [startColumnIndex, startColumnOffset, columnSizes] = getRangeToRender(columnCount, columnOffsetMapping, width, scrollColumnOffset + renderColumnOffset);
241
+ // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
242
+ // naming convention as components do.
243
+ const ChildVar = children;
244
+ // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator.
245
+ // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator
246
+ // returns the result of the final statement which makes the iteration a little clumsier.
247
+ let nextRowOffset = startRowOffset - renderRowOffset;
248
+ let rowIndex = 0, rowOffset = 0;
249
+ let nextColumnOffset = 0, columnIndex = 0, columnOffset = 0;
250
+ return (jsx("div", { onScroll: onScroll, ref: outerRef, style: { position: "relative", height, width, overflow: "auto", willChange: "transform" }, children: jsx("div", { style: { height: renderRowSize, width: renderColumnSize }, children: rowSizes.map((rowSize, rowArrayIndex) => (rowOffset = nextRowOffset,
251
+ nextRowOffset += rowSize,
252
+ rowIndex = startRowIndex + rowArrayIndex,
253
+ nextColumnOffset = startColumnOffset - renderColumnOffset,
254
+ jsx(Fragment, { children: columnSizes.map((columnSize, columnArrayIndex) => (columnOffset = nextColumnOffset,
255
+ nextColumnOffset += columnSize,
256
+ columnIndex = startColumnIndex + columnArrayIndex,
257
+ jsx(ChildVar, { data: itemData, rowIndex: rowIndex, columnIndex: columnIndex, isScrolling: useIsScrolling$1 ? isScrolling : undefined, style: { position: "absolute", top: rowOffset, height: rowSize, left: columnOffset, width: columnSize } }, itemKey(rowIndex, columnIndex, itemData)))) }, itemKey(rowIndex, 0, itemData)))) }) }));
258
+ });
259
+
260
+ const defaultItemKey = (index, _data) => index;
261
+ // Using a named function rather than => so that the name shows up in React Developer Tools
262
+ const VirtualList = React.forwardRef(function VirtualList(props, ref) {
263
+ const { width, height, itemCount, itemOffsetMapping, children, itemData = undefined, itemKey = defaultItemKey, useIsScrolling: useIsScrolling$1 = false } = props;
264
+ // Total size is same as offset to item one off the end
265
+ const totalSize = itemOffsetMapping.itemOffset(itemCount);
266
+ const outerRef = React.useRef(null);
267
+ const { scrollOffset, renderOffset, renderSize, onScroll: onScrollExtent, doScrollTo } = useVirtualScroll(totalSize);
268
+ const isScrolling = useIsScrolling(outerRef);
269
+ React.useImperativeHandle(ref, () => {
270
+ return {
271
+ scrollTo(offset) {
272
+ const outer = outerRef.current;
273
+ /* istanbul ignore else */
274
+ if (outer)
275
+ outer.scrollTo(0, doScrollTo(offset, outer.clientHeight));
276
+ },
277
+ scrollToItem(index) {
278
+ this.scrollTo(itemOffsetMapping.itemOffset(index));
279
+ }
280
+ };
281
+ }, [itemOffsetMapping]);
282
+ function onScroll(event) {
283
+ const { clientHeight, scrollHeight, scrollTop, scrollLeft } = event.currentTarget;
284
+ const newScrollTop = onScrollExtent(clientHeight, scrollHeight, scrollTop);
285
+ if (newScrollTop != scrollTop && outerRef.current)
286
+ outerRef.current.scrollTo(scrollLeft, newScrollTop);
287
+ }
288
+ const [startIndex, startOffset, sizes] = getRangeToRender(itemCount, itemOffsetMapping, height, scrollOffset + renderOffset);
289
+ // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
290
+ // naming convention as components do.
291
+ const ChildVar = children;
292
+ // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator.
293
+ // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator
294
+ // returns the result of the final statement which makes the iteration a little clumsier.
295
+ let nextOffset = startOffset - renderOffset;
296
+ let index, offset;
297
+ return (jsx("div", { onScroll: onScroll, ref: outerRef, style: { position: "relative", height, width, overflow: "auto", willChange: "transform" }, children: jsx("div", { style: { height: renderSize, width: "100%" }, children: sizes.map((size, arrayIndex) => (offset = nextOffset,
298
+ nextOffset += size,
299
+ index = startIndex + arrayIndex,
300
+ jsx(ChildVar, { data: itemData, index: index, isScrolling: useIsScrolling$1 ? isScrolling : undefined, style: { position: "absolute", top: offset, height: size, width: "100%" } }, itemKey(index, itemData)))) }) }));
301
+ });
302
+
303
+ class FixedSizeItemOffsetMapping {
304
+ constructor(itemSize) {
305
+ Object.defineProperty(this, "fixedItemSize", {
306
+ enumerable: true,
307
+ configurable: true,
308
+ writable: true,
309
+ value: void 0
310
+ });
311
+ this.fixedItemSize = itemSize;
312
+ }
313
+ itemSize(_itemIndex) {
314
+ return this.fixedItemSize;
315
+ }
316
+ itemOffset(itemIndex) {
317
+ return itemIndex * this.fixedItemSize;
318
+ }
319
+ offsetToItem(offset) {
320
+ const itemIndex = Math.floor(offset / this.fixedItemSize);
321
+ const startOffset = itemIndex * this.fixedItemSize;
322
+ return [itemIndex, startOffset];
323
+ }
324
+ }
325
+ function useFixedSizeItemOffsetMapping(itemSize) {
326
+ return new FixedSizeItemOffsetMapping(itemSize);
327
+ }
328
+
329
+ class VariableSizeItemOffsetMapping {
330
+ constructor(defaultItemSize, sizes) {
331
+ Object.defineProperty(this, "defaultItemSize", {
332
+ enumerable: true,
333
+ configurable: true,
334
+ writable: true,
335
+ value: void 0
336
+ });
337
+ Object.defineProperty(this, "sizes", {
338
+ enumerable: true,
339
+ configurable: true,
340
+ writable: true,
341
+ value: void 0
342
+ });
343
+ this.defaultItemSize = defaultItemSize;
344
+ this.sizes = sizes;
345
+ }
346
+ itemSize(itemIndex) {
347
+ return (itemIndex < this.sizes.length) ? this.sizes[itemIndex] : this.defaultItemSize;
348
+ }
349
+ itemOffset(itemIndex) {
350
+ var offset = 0;
351
+ let length = this.sizes.length;
352
+ if (itemIndex > length) {
353
+ const numDefaultSize = itemIndex - length;
354
+ offset = numDefaultSize * this.defaultItemSize;
355
+ }
356
+ else {
357
+ length = itemIndex;
358
+ }
359
+ for (let i = 0; i < length; i++) {
360
+ offset += this.sizes[i];
361
+ }
362
+ return offset;
363
+ }
364
+ offsetToItem(offset) {
365
+ var startOffset = 0;
366
+ const length = this.sizes.length;
367
+ for (let i = 0; i < length; i++) {
368
+ const size = this.sizes[i];
369
+ if (startOffset + size > offset) {
370
+ return [i, startOffset];
371
+ }
372
+ startOffset += size;
373
+ }
374
+ const itemIndex = Math.floor((offset - startOffset) / this.defaultItemSize);
375
+ startOffset += itemIndex * this.defaultItemSize;
376
+ return [itemIndex + length, startOffset];
377
+ }
378
+ }
379
+ function useVariableSizeItemOffsetMapping(defaultItemSize, sizes) {
380
+ return new VariableSizeItemOffsetMapping(defaultItemSize, sizes || []);
381
+ }
382
+
383
+ export { VirtualGrid, VirtualList, useFixedSizeItemOffsetMapping, useVariableSizeItemOffsetMapping };
384
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/VirtualBase.ts","../src/useVirtualScroll.ts","../src/useEventListener.ts","../src/useAnimationTimeout.ts","../src/useIsScrolling.ts","../src/VirtualGrid.tsx","../src/VirtualList.tsx","../src/useFixedSizeItemOffsetMapping.ts","../src/useVariableSizeItemOffsetMapping.ts"],"sourcesContent":["\nexport interface VirtualBaseItemProps {\n data: any,\n isScrolling?: boolean,\n style: Object,\n};\n\nexport interface VirtualBaseProps {\n height: number,\n width: number,\n itemData?: any,\n useIsScrolling?: boolean,\n};\n\nexport interface ItemOffsetMapping {\n itemSize(itemIndex: number): number;\n itemOffset(itemIndex: number): number;\n offsetToItem(offset: number): [itemIndex: number, startOffset: number];\n};\n\nexport type ScrollEvent = React.SyntheticEvent<HTMLDivElement>;\n\ntype RangeToRender = [\n startIndex: number,\n startOffset: number,\n sizes: number[]\n];\n\nexport function getRangeToRender(itemCount: number, itemOffsetMapping: ItemOffsetMapping, clientExtent: number, scrollOffset: number): RangeToRender {\n if (itemCount == 0) {\n return [0, 0, []];\n }\n\n var [itemIndex, startOffset] = itemOffsetMapping.offsetToItem(scrollOffset);\n itemIndex = Math.max(0, Math.min(itemCount - 1, itemIndex));\n var endOffset = scrollOffset + clientExtent;\n\n const overscanBackward = 1;\n const overscanForward = 1;\n\n for (let step = 0; step < overscanBackward && itemIndex > 0; step ++) {\n itemIndex --;\n startOffset -= itemOffsetMapping.itemSize(itemIndex);\n }\n\n const startIndex = itemIndex;\n var offset = startOffset;\n const sizes: number[] = [];\n\n while (offset < endOffset && itemIndex < itemCount) {\n const size = itemOffsetMapping.itemSize(itemIndex);\n sizes.push(size);\n offset += size;\n itemIndex ++;\n }\n\n for (let step = 0; step < overscanForward && itemIndex < itemCount; step ++) {\n const size = itemOffsetMapping.itemSize(itemIndex);\n sizes.push(size);\n itemIndex ++;\n }\n\n return [startIndex, startOffset, sizes];\n}\n","import { useState } from \"react\";\n\nexport type ScrollLayout = \"horizontal\" | \"vertical\";\nexport type ScrollDirection = \"forward\" | \"backward\";\nexport interface ScrollState { \n scrollOffset: number, \n renderOffset: number,\n page: number, \n scrollDirection: ScrollDirection, \n};\n\nexport interface VirtualScroll extends ScrollState {\n renderSize: number;\n\n // Returns updated scrollOffset. Caller should update scroll bar position if different from value passed in. \n onScroll(clientExtent: number, scrollExtent: number, scrollOffset: number): number;\n\n // Scroll to offset in logical space returning offset to update scroll bar position to\n doScrollTo(offset: number, clientExtent: number): number;\n};\n\n// Max size that is safe across all browsers (Firefox is the limiting factor)\n// SlickGrid tries to dynamically determine limit on other browsers (Chrome will do 30M) but\n// I prefer simplicity of same behavior across all browsers.\nconst MAX_SUPPORTED_CSS_SIZE = 6000000;\nconst MIN_NUMBER_PAGES = 100;\n\nexport function useVirtualScroll(totalSize: number): VirtualScroll {\n let renderSize=0, pageSize=0, numPages=0;\n if (totalSize < MAX_SUPPORTED_CSS_SIZE) {\n // No paging needed\n renderSize = pageSize = totalSize;\n numPages = 1;\n } else {\n // Break into pages\n renderSize = MAX_SUPPORTED_CSS_SIZE;\n pageSize = renderSize / MIN_NUMBER_PAGES;\n numPages = Math.floor(totalSize / pageSize);\n }\n\n function pageToRenderOffset(page: number): number {\n if (page <= 0)\n return 0;\n\n if (page >= numPages-1)\n return totalSize - renderSize;\n\n return Math.round((page-1) * (totalSize - renderSize) / (numPages - 3));\n }\n\n const initValue: ScrollState = { \n scrollOffset: 0, \n renderOffset: 0,\n page: 0,\n scrollDirection: \"forward\",\n };\n const [scrollState, setScrollState] = useState(initValue);\n\n function onScroll(clientExtent: number, scrollExtent: number, scrollOffset: number) {\n if (scrollState.scrollOffset == scrollOffset) {\n // No need to change state if scroll position unchanged\n return scrollOffset;\n }\n\n // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.\n let newOffset = Math.max(0, Math.min(scrollOffset, scrollExtent - clientExtent));\n const newScrollDirection = scrollState.scrollOffset <= newOffset ? 'forward' : 'backward';\n\n // Switch pages if needed\n let newPage, newRenderOffset;\n let retScrollOffset = scrollOffset;\n const scrollDist = Math.abs(newOffset - scrollState.scrollOffset);\n if (scrollDist < clientExtent) {\n // Scrolling part of visible window, don't want to skip items, so can't scale up movement\n // If we cross page boundary we need to reset scroll bar position back to where it should be at start of page\n newPage = Math.min(numPages - 1, Math.floor((scrollOffset + scrollState.renderOffset) / pageSize));\n newRenderOffset = pageToRenderOffset(newPage);\n if (newPage != scrollState.page) {\n // Be very intentional about when we ask caller to reset scroll bar\n // Don't want to trigger event loops\n newOffset = scrollOffset + scrollState.renderOffset - newRenderOffset;\n retScrollOffset = newOffset;\n }\n } else {\n // Large scale scrolling, choosing page from a rolodex\n // First and last page are mapped 1:1 between grid and container\n if (newOffset < pageSize) {\n newPage = 0;\n } else if (newOffset >= renderSize - pageSize) {\n newPage = numPages - 1;\n } else {\n const scaleFactor = (totalSize - pageSize*2) / (renderSize - pageSize*2);\n newPage = Math.min(numPages - 3, Math.floor((newOffset - pageSize) * scaleFactor / pageSize)) + 1;\n }\n newRenderOffset = pageToRenderOffset(newPage);\n }\n\n setScrollState({ scrollOffset: newOffset, renderOffset: newRenderOffset, page: newPage, scrollDirection: newScrollDirection });\n return retScrollOffset;\n }\n\n function doScrollTo(offset: number, clientExtent: number) {\n const safeOffset = Math.min(totalSize - clientExtent, Math.max(offset, 0));\n const scrollDirection = (scrollState.scrollOffset + scrollState.renderOffset) <= safeOffset ? 'forward' : 'backward';\n const page = Math.min(numPages - 1, Math.floor(safeOffset / pageSize));\n const renderOffset = pageToRenderOffset(page);\n const scrollOffset = safeOffset - renderOffset;\n\n setScrollState({ scrollOffset, renderOffset, page, scrollDirection });\n return scrollOffset;\n }\n\n return {...scrollState, renderSize, onScroll, doScrollTo} as const;\n}\n\nexport default useVirtualScroll;\n","// Based on https://github.com/realwugang/use-event-listener\n// and https://github.com/donavon/use-event-listener/blob/develop/src/index.js\n\nimport { useRef, useEffect, RefObject, createRef } from 'react';\n\ninterface Options {\n capture?: boolean\n once?: boolean\n passive?: boolean\n};\n\ntype Listener = Window | Document | HTMLElement;\n\nfunction isListener(element: Listener | RefObject<HTMLElement>): element is Listener {\n return (element as Listener).addEventListener !== undefined;\n}\n\nexport function useEventListener (eventName: string, \n handler: (event: Event) => void, \n element: Listener | RefObject<HTMLElement> | null = window, \n options: Options = {}) {\n const savedHandler = useRef<any>();\n const { capture, passive, once } = options;\n\n useEffect(() => {\n savedHandler.current = handler\n }, [handler])\n\n useEffect(() => {\n if (!element)\n return;\n\n const el = isListener(element) ? element : element.current;\n if (!el)\n return;\n\n const eventListener = (event: Event) => savedHandler.current(event);\n const opts = { capture, passive, once };\n el.addEventListener(eventName, eventListener, opts);\n return () => {\n el.removeEventListener(eventName, eventListener, opts);\n };\n }, [eventName, element, capture, passive, once]);\n}\n\nexport default useEventListener;\n\n// In-source testing for private helper functions\nif (import.meta.vitest) {\n const { it, expect } = import.meta.vitest\n it('isListener', () => {\n expect(isListener(window)).toBe(true)\n expect(isListener(document)).toBe(true)\n expect(isListener(document.createElement(\"div\"))).toBe(true)\n expect(isListener(createRef())).toBe(false)\n })\n}","// Based on https://overreacted.io/making-setinterval-declarative-with-react-hooks/\n// and https://www.joshwcomeau.com/snippets/react-hooks/use-timeout/\n// and https://github.com/bvaughn/react-window/blob/master/src/timer.js\n//\n// Equivalent functionality to a useTimeout hook but based on requestAnimationFrame instead of setTimeout. Use\n// when making frequent requests for short duration timeouts where browser may throttle setTimeout.\nimport { useEffect, useRef } from 'react';\n\ntype Callback = () => void;\n\nexport function useAnimationTimeout(callback: Callback, delay: number | null, key?: unknown) {\n const requestRef = useRef<number>();\n const savedCallback = useRef<Callback>(callback);\n\n // Remember the latest callback\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n \n const start = performance.now();\n \n useEffect(() => {\n function tick() {\n requestRef.current = undefined;\n if (delay === null)\n return;\n\n if (performance.now() - start >= delay) {\n savedCallback.current();\n } else {\n requestRef.current = requestAnimationFrame(tick);\n }\n }\n\n tick();\n\n return () => {\n if (typeof requestRef.current === 'number') {\n cancelAnimationFrame(requestRef.current);\n requestRef.current = undefined;\n }\n }\n }, [delay, key]);\n}\n\nexport default useAnimationTimeout;","import { useState, RefObject } from \"react\";\nimport { useEventListener } from './useEventListener';\nimport { useAnimationTimeout } from './useAnimationTimeout';\n\nconst DEBOUNCE_INTERVAL = 150;\nconst FALLBACK_INTERVAL = 500;\n\nexport function useIsScrolling(element: Window | HTMLElement | RefObject<HTMLElement> | null = window): boolean {\n const [scrollCount, setScrollCount] = useState(0);\n\n // scrollend implementations in both Chrome and Firefox are buggy with missing scrollend events\n // in some circumstances (using keyboard to scroll past end in Chrome, intermittently when using mouse wheel in Firefox)\n // Use a timeout even when scrollend is supported to handle missing events. In this case we use a longer interval as\n // don't want it to be over sensitive. \n const supportsScrollEnd = ('onscrollend' in window);\n const delay = supportsScrollEnd ? FALLBACK_INTERVAL : DEBOUNCE_INTERVAL;\n\n useEventListener(\"scroll\", () => setScrollCount(c => c + 1), element);\n useEventListener(\"scrollend\", () => setScrollCount(0), supportsScrollEnd ? element : null);\n useAnimationTimeout(() => setScrollCount(0), (scrollCount == 0) ? null : delay, scrollCount);\n\n return scrollCount > 0;\n}\n\nexport default useIsScrolling;","import React from \"react\";\nimport { Fragment } from \"react\";\nimport { ItemOffsetMapping, getRangeToRender, VirtualBaseItemProps, VirtualBaseProps, ScrollEvent } from './VirtualBase';\nimport { useVirtualScroll } from './useVirtualScroll';\nimport { useIsScrolling as useIsScrollingHook} from './useIsScrolling';\n\nexport interface VirtualGridItemProps extends VirtualBaseItemProps {\n rowIndex: number,\n columnIndex: number,\n};\n\ntype VirtualGridItem = React.ComponentType<VirtualGridItemProps>;\n\nexport interface VirtualGridProps extends VirtualBaseProps {\n children: VirtualGridItem,\n rowCount: number,\n rowOffsetMapping: ItemOffsetMapping,\n columnCount: number,\n columnOffsetMapping: ItemOffsetMapping,\n itemKey?: (rowIndex: number, columnIndex: number, data: any) => any,\n};\n\nexport interface VirtualGridProxy {\n scrollTo(rowOffset: number, columnOffset: number): void;\n scrollToItem(rowIndex: number, columnIndex: number): void;\n};\n\nconst defaultItemKey = (rowIndex: number, columnIndex: number, _data: any) => `${rowIndex}:${columnIndex}`;\n\n// Using a named function rather than => so that the name shows up in React Developer Tools\nexport const VirtualGrid = React.forwardRef<VirtualGridProxy, VirtualGridProps>(function VirtualGrid(props, ref) {\n const { width, height, rowCount, rowOffsetMapping, columnCount, columnOffsetMapping, children, \n itemData = undefined, itemKey = defaultItemKey, useIsScrolling = false } = props;\n\n // Total size is same as offset to item one off the end\n const totalRowSize = rowOffsetMapping.itemOffset(rowCount);\n const totalColumnSize = columnOffsetMapping.itemOffset(columnCount);\n\n const outerRef = React.useRef<HTMLDivElement>(null);\n const { scrollOffset: scrollRowOffset, renderOffset: renderRowOffset, renderSize: renderRowSize,\n onScroll: onScrollRow, doScrollTo: doScrollToRow } = useVirtualScroll(totalRowSize);\n const { scrollOffset: scrollColumnOffset, renderOffset: renderColumnOffset, renderSize: renderColumnSize,\n onScroll: onScrollColumn, doScrollTo: doScrollToColumn} = useVirtualScroll(totalColumnSize);\n const isScrolling = useIsScrollingHook(outerRef); \n\n React.useImperativeHandle(ref, () => {\n return {\n scrollTo(rowOffset: number, columnOffset: number): void {\n const outer = outerRef.current;\n /* istanbul ignore else */\n if (outer)\n outer.scrollTo(doScrollToColumn(columnOffset, outer.clientWidth), doScrollToRow(rowOffset, outer.clientHeight));\n },\n\n scrollToItem(rowIndex: number, columnIndex: number): void {\n this.scrollTo(rowOffsetMapping.itemOffset(rowIndex), columnOffsetMapping.itemOffset(columnIndex));\n }\n }\n }, [ rowOffsetMapping, columnOffsetMapping ]);\n\n\n function onScroll(event: ScrollEvent) {\n const { clientWidth, clientHeight, scrollWidth, scrollHeight, scrollLeft, scrollTop } = event.currentTarget;\n const newScrollTop = onScrollRow(clientHeight, scrollHeight, scrollTop);\n const newScrollLeft = onScrollColumn(clientWidth, scrollWidth, scrollLeft);\n if (outerRef.current && (newScrollTop != scrollTop || newScrollLeft != scrollLeft ))\n outerRef.current.scrollTo(newScrollLeft, newScrollTop);\n }\n\n const [startRowIndex, startRowOffset, rowSizes] = \n getRangeToRender(rowCount, rowOffsetMapping, height, scrollRowOffset + renderRowOffset);\n const [startColumnIndex, startColumnOffset, columnSizes] = \n getRangeToRender(columnCount, columnOffsetMapping, width, scrollColumnOffset + renderColumnOffset);\n\n // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized\n // naming convention as components do. \n const ChildVar = children;\n\n // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator. \n // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator\n // returns the result of the final statement which makes the iteration a little clumsier.\n let nextRowOffset = startRowOffset - renderRowOffset;\n let rowIndex=0, rowOffset=0;\n let nextColumnOffset=0, columnIndex=0, columnOffset=0;\n\n return (\n <div onScroll={onScroll} ref={outerRef} style={{ position: \"relative\", height, width, overflow: \"auto\", willChange: \"transform\" }}>\n <div style={{ height: renderRowSize, width: renderColumnSize }}>\n {rowSizes.map((rowSize, rowArrayIndex) => (\n rowOffset = nextRowOffset,\n nextRowOffset += rowSize,\n rowIndex = startRowIndex + rowArrayIndex,\n nextColumnOffset = startColumnOffset - renderColumnOffset,\n <Fragment key={itemKey(rowIndex, 0, itemData)}>\n {columnSizes.map((columnSize, columnArrayIndex) => (\n columnOffset = nextColumnOffset,\n nextColumnOffset += columnSize,\n columnIndex = startColumnIndex + columnArrayIndex,\n <ChildVar data={itemData} key={itemKey(rowIndex, columnIndex, itemData)}\n rowIndex={rowIndex} columnIndex={columnIndex}\n isScrolling={useIsScrolling ? isScrolling : undefined}\n style={{ position: \"absolute\", top: rowOffset, height: rowSize, left: columnOffset, width: columnSize }}/>\n ))}\n </Fragment>\n ))}\n </div>\n </div>\n );\n});\n\nexport default VirtualGrid;\n","import React from \"react\";\nimport { ItemOffsetMapping, getRangeToRender, VirtualBaseItemProps, VirtualBaseProps, ScrollEvent } from './VirtualBase';\nimport { useVirtualScroll } from './useVirtualScroll';\nimport { useIsScrolling as useIsScrollingHook} from './useIsScrolling';\n\nexport interface VirtualListItemProps extends VirtualBaseItemProps {\n index: number,\n};\n\ntype VirtualListItem = React.ComponentType<VirtualListItemProps>;\n\nexport interface VirtualListProps extends VirtualBaseProps {\n children: VirtualListItem,\n itemCount: number,\n itemOffsetMapping: ItemOffsetMapping,\n itemKey?: (index: number, data: any) => any,\n};\n\nexport interface VirtualListProxy {\n scrollTo(offset: number): void;\n scrollToItem(index: number): void;\n};\n\nconst defaultItemKey = (index: number, _data: any) => index;\n\n// Using a named function rather than => so that the name shows up in React Developer Tools\nexport const VirtualList = React.forwardRef<VirtualListProxy, VirtualListProps>(function VirtualList(props, ref) {\n const { width, height, itemCount, itemOffsetMapping, children, \n itemData = undefined, itemKey = defaultItemKey, useIsScrolling = false } = props;\n\n // Total size is same as offset to item one off the end\n const totalSize = itemOffsetMapping.itemOffset(itemCount);\n\n const outerRef = React.useRef<HTMLDivElement>(null);\n const { scrollOffset, renderOffset, renderSize, onScroll: onScrollExtent, doScrollTo } = useVirtualScroll(totalSize);\n const isScrolling = useIsScrollingHook(outerRef); \n\n React.useImperativeHandle(ref, () => {\n return {\n scrollTo(offset: number): void {\n const outer = outerRef.current;\n /* istanbul ignore else */\n if (outer)\n outer.scrollTo(0, doScrollTo(offset, outer.clientHeight));\n },\n\n scrollToItem(index: number): void {\n this.scrollTo(itemOffsetMapping.itemOffset(index));\n }\n }\n }, [ itemOffsetMapping ]);\n\n function onScroll(event: ScrollEvent) {\n const { clientHeight, scrollHeight, scrollTop, scrollLeft } = event.currentTarget;\n const newScrollTop = onScrollExtent(clientHeight, scrollHeight, scrollTop);\n if (newScrollTop != scrollTop && outerRef.current)\n outerRef.current.scrollTo(scrollLeft, newScrollTop);\n }\n\n const [startIndex, startOffset, sizes] = getRangeToRender(itemCount, itemOffsetMapping, height, scrollOffset+renderOffset);\n\n // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized\n // naming convention as components do. \n const ChildVar = children;\n\n // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator. \n // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator\n // returns the result of the final statement which makes the iteration a little clumsier.\n let nextOffset = startOffset - renderOffset;\n let index, offset;\n\n return (\n <div onScroll={onScroll} ref={outerRef} style={{ position: \"relative\", height, width, overflow: \"auto\", willChange: \"transform\" }}>\n <div style={{ height: renderSize, width: \"100%\" }}>\n {sizes.map((size, arrayIndex) => (\n offset = nextOffset,\n nextOffset += size,\n index = startIndex + arrayIndex,\n <ChildVar data={itemData} key={itemKey(index, itemData)} index={index}\n isScrolling={useIsScrolling ? isScrolling : undefined}\n style={{ position: \"absolute\", top: offset, height: size, width: \"100%\" }}/>\n ))}\n </div>\n </div>\n );\n});\n\nexport default VirtualList;\n","import { ItemOffsetMapping } from './VirtualBase';\n\nclass FixedSizeItemOffsetMapping implements ItemOffsetMapping {\n constructor (itemSize: number) {\n this.fixedItemSize = itemSize;\n }\n\n itemSize(_itemIndex: number): number {\n return this.fixedItemSize;\n }\n\n itemOffset(itemIndex: number): number {\n return itemIndex * this.fixedItemSize;\n }\n\n offsetToItem(offset: number): [itemIndex: number, startOffset: number] {\n const itemIndex = Math.floor(offset / this.fixedItemSize);\n const startOffset = itemIndex * this.fixedItemSize;\n\n return [itemIndex, startOffset];\n }\n\n fixedItemSize: number;\n};\n\nexport function useFixedSizeItemOffsetMapping(itemSize: number) {\n return new FixedSizeItemOffsetMapping(itemSize);\n};\n\nexport default useFixedSizeItemOffsetMapping;\n","import { ItemOffsetMapping } from './VirtualBase';\n\nclass VariableSizeItemOffsetMapping implements ItemOffsetMapping {\n constructor (defaultItemSize: number, sizes: number[]) {\n this.defaultItemSize = defaultItemSize;\n this.sizes = sizes;\n }\n\n itemSize(itemIndex: number): number {\n return (itemIndex < this.sizes.length) ? this.sizes[itemIndex] : this.defaultItemSize;\n }\n\n itemOffset(itemIndex: number): number {\n var offset = 0;\n let length = this.sizes.length;\n if (itemIndex > length) {\n const numDefaultSize = itemIndex - length;\n offset = numDefaultSize * this.defaultItemSize;\n } else {\n length = itemIndex;\n }\n \n for (let i = 0; i < length; i ++)\n {\n offset += this.sizes[i];\n }\n\n return offset;\n }\n\n offsetToItem(offset: number): [itemIndex: number, startOffset: number] {\n var startOffset = 0;\n const length = this.sizes.length;\n for (let i = 0; i < length; i ++) {\n const size = this.sizes[i];\n if (startOffset + size > offset) {\n return [i, startOffset];\n }\n startOffset += size;\n }\n\n const itemIndex = Math.floor((offset - startOffset) / this.defaultItemSize);\n startOffset += itemIndex * this.defaultItemSize;\n\n return [itemIndex+length, startOffset];\n }\n\n defaultItemSize: number;\n sizes: number[];\n};\n\nexport function useVariableSizeItemOffsetMapping(defaultItemSize: number, sizes?: number[]) {\n return new VariableSizeItemOffsetMapping(defaultItemSize, sizes || []);\n};\n\nexport default useVariableSizeItemOffsetMapping;"],"names":["defaultItemKey","useIsScrolling","useIsScrollingHook","_jsx"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@candidstartup/react-virtual-scroll",
3
+ "private": false,
4
+ "version": "0.1.1",
5
+ "description": "Modern React components for lists and grids that scale to trillions of rows and columns",
6
+ "author": "Tim Wiegand <tim.wiegand@thecandidstartup.org>",
7
+ "license": "BSD-3-Clause",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/TheCandidStartup/infinisheet.git",
11
+ "directory": "packages/react-virtual-scroll"
12
+ },
13
+ "bugs": "https://github.com/TheCandidStartup/infinisheet/issues",
14
+ "homepage": "https://github.com/TheCandidStartup/infinisheet/blob/main/packages/react-virtual-scroll/README.md",
15
+ "keywords": [
16
+ "react",
17
+ "reactjs",
18
+ "virtual",
19
+ "window",
20
+ "windowed",
21
+ "list",
22
+ "scroll",
23
+ "scrolling",
24
+ "infinite",
25
+ "virtualized",
26
+ "table",
27
+ "grid",
28
+ "modern",
29
+ "scalable"
30
+ ],
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "type": "module",
35
+ "files": [
36
+ "dist"
37
+ ],
38
+ "module": "./dist/index.js",
39
+ "exports": {
40
+ ".": {
41
+ "import": "./dist/index.js"
42
+ }
43
+ },
44
+ "scripts": {
45
+ "dev": "vite",
46
+ "clean": "rimraf dist",
47
+ "prebuild": "npm run clean",
48
+ "build-tsc": "tsc -p tsconfig.build.json",
49
+ "build-rollup": "rollup -c ../rollup.config.mjs",
50
+ "build": "npm run build-rollup",
51
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
52
+ "test": "vitest"
53
+ },
54
+ "peerDependencies": {
55
+ "react": "^18.2.0"
56
+ },
57
+ "gitHead": "0d944ff1084e8815da6f7dd5e8f35993f0ccd443"
58
+ }