@candidstartup/react-virtual-scroll 0.5.0 → 0.6.0

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/dist/index.js CHANGED
@@ -1,34 +1,72 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import React, { useState, createRef, useRef, useEffect, Fragment } from 'react';
3
3
 
4
- function getRangeToRender(itemCount, itemOffsetMapping, clientExtent, scrollOffset) {
5
- if (itemCount == 0) {
6
- return [0, 0, []];
7
- }
8
- let [itemIndex, startOffset] = itemOffsetMapping.offsetToItem(scrollOffset);
9
- itemIndex = Math.max(0, Math.min(itemCount - 1, itemIndex));
10
- const 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
- let 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];
4
+ const defaultContainerRender = ({ ...rest }, ref) => (jsx("div", { ref: ref, ...rest }));
5
+ /**
6
+ * Wrapper around a div used by other components in {@link @candidstartup/react-virtual-scroll!}. Most props are passed through to the div. Use the
7
+ * {@link VirtualContainerComponentProps.render} prop to override the default behavior.
8
+ *
9
+ * @group Components
10
+ */
11
+ const VirtualContainer = React.forwardRef(function VirtualContainer({ render = defaultContainerRender, ...rest }, ref) {
12
+ return render(rest, ref);
13
+ });
14
+
15
+ /**
16
+ * HOC that calculates the size available to it and makes the computed size available to children.
17
+ * The size available depends on DOM layout and style applied wherever the AutoSizer finds itself.
18
+ * You will probably want to pass something appropriate via the `className` or `style` props.
19
+ *
20
+ * Accepts props defined by {@link AutoSizerProps}.
21
+ * You must pass a single instance of {@link AutoSizerRender} as a child.
22
+ * @group Components
23
+ */
24
+ function AutoSizer(props) {
25
+ const { children, className, style } = props;
26
+ // Using separate primitive states rather than one composite so that React
27
+ // can detect duplicates values and bail out of redundant renders.
28
+ const [width, setWidth] = React.useState(0);
29
+ const [height, setHeight] = React.useState(0);
30
+ const ref = React.useRef(null);
31
+ // Make sure resize callback is a stable value so we're not constantly
32
+ // creating and disconnecting resize observers.
33
+ const resizeCallback = React.useCallback((entries) => {
34
+ entries.forEach(entry => {
35
+ // Context box sizes can contain fractional values while clientWidth
36
+ // and clientHeight properties are always rounded to nearest integer.
37
+ // Always use integer values to avoid confusion.
38
+ const newWidth = Math.round(entry.contentBoxSize[0].inlineSize);
39
+ setWidth(newWidth);
40
+ const newHeight = Math.round(entry.borderBoxSize[0].blockSize);
41
+ setHeight(newHeight);
42
+ });
43
+ }, []);
44
+ // Expect effect to run only on initial mount
45
+ React.useLayoutEffect(() => {
46
+ const div = ref.current;
47
+ /* istanbul ignore if*/
48
+ if (!div)
49
+ return;
50
+ // Size on initial mount
51
+ setHeight(div.clientHeight);
52
+ setWidth(div.clientWidth);
53
+ // Updates size on any subsequent resize. Only available in browser
54
+ // environment so avoid crashing out when server side rendering, or
55
+ // running unit test without ResizeObserver being mocked.
56
+ if (typeof ResizeObserver !== 'undefined') {
57
+ const resizeObserver = new ResizeObserver(resizeCallback);
58
+ resizeObserver.observe(div);
59
+ return () => { resizeObserver.disconnect(); };
60
+ }
61
+ }, [resizeCallback]);
62
+ // No point rendering children until we've measured size and found a usable area
63
+ const renderChildren = height > 0 && width > 0;
64
+ // Ensure that size is driven only by parent. Wrapping child in a zero sized inner div
65
+ // which it can overflow stops child's size having any impact on size of outer div.
66
+ // Otherwise can end up in infinite loop if child makes itself bigger than the
67
+ // actual width and height we pass to it. That could be because child has
68
+ // padding/borders/margins or because child renders itself bigger than size it's given.
69
+ return (jsx("div", { ref: ref, className: className, style: style, children: jsx("div", { style: { overflow: 'visible', width: 0, height: 0 }, children: renderChildren && children({ height, width }) }) }));
32
70
  }
33
71
 
34
72
  // Max size that is safe across all browsers (Firefox is the limiting factor)
@@ -206,40 +244,58 @@ function useIsScrolling(element = window) {
206
244
  return scrollCount > 0;
207
245
  }
208
246
 
209
- const defaultItemKey$1 = (rowIndex, columnIndex, _data) => `${rowIndex}:${columnIndex}`;
210
- const Inner$1 = React.forwardRef(function VirtualGridInner({ render, ...rest }, ref) {
211
- return render(rest, ref);
212
- });
213
- function defaultInnerRender$1({ ...rest }, ref) {
214
- return jsx("div", { ref: ref, ...rest });
215
- }
216
- const Outer$1 = React.forwardRef(function VirtualGridOuter({ render, ...rest }, ref) {
217
- return render(rest, ref);
218
- });
219
- function defaultOuterRender$1({ ...rest }, ref) {
220
- return jsx("div", { ref: ref, ...rest });
247
+ /**
248
+ * Returns the offset needed to scroll in one dimension for a specified range
249
+ *
250
+ * Used internally to implement {@link VirtualScrollProxy.scrollToArea}. Can be used directly for
251
+ * advanced customization scenarios.
252
+ */
253
+ function getOffsetToScrollRange(offset, size, clientExtent, scrollOffset, option) {
254
+ if (offset === undefined)
255
+ return undefined;
256
+ if (option != 'visible')
257
+ return offset;
258
+ // Start of item offscreen before start of viewport?
259
+ if (offset < scrollOffset)
260
+ return offset;
261
+ size = size || 0;
262
+ // Already completely visible?
263
+ const endOffset = offset + size;
264
+ const endViewport = scrollOffset + clientExtent;
265
+ if (endOffset <= endViewport)
266
+ return undefined;
267
+ // Item offscreen past end of viewport
268
+ // Item bigger than viewport? Make sure start is in view
269
+ if (size > clientExtent)
270
+ return offset;
271
+ // Scroll so end of item aligns with end of viewport
272
+ return offset - clientExtent + size;
221
273
  }
274
+
222
275
  // Using a named function rather than => so that the name shows up in React Developer Tools
223
276
  /**
224
- * Virtual Scrolling Grid
277
+ * Customizable Virtual Scrolling Component
225
278
  *
226
- * Accepts props defined by {@link VirtualGridProps}.
227
- * Refs are forwarded to {@link VirtualGridProxy}.
228
- * You must pass a single instance of {@link VirtualGridItem} as a child.
279
+ * Allows user to scroll over a virtual area `scrollHeight` x `scrollWidth` pixels.
280
+ * Use `onScroll` to track scroll state and `innerRender` to render scroll state specific content into the viewport
281
+ *
282
+ * Accepts props defined by {@link VirtualScrollProps}.
283
+ * Refs are forwarded to {@link VirtualScrollProxy}.
229
284
  * @group Components
230
285
  */
231
- const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
232
- const { width, height, rowCount, rowOffsetMapping, columnCount, columnOffsetMapping, children, className, innerClassName, itemData = undefined, itemKey = defaultItemKey$1, onScroll: onScrollCallback, useIsScrolling: useIsScrolling$1 = false } = props;
233
- // Total size is same as offset to item one off the end
234
- const totalRowSize = rowOffsetMapping.itemOffset(rowCount);
235
- const totalColumnSize = columnOffsetMapping.itemOffset(columnCount);
286
+ const VirtualScroll = React.forwardRef(function VirtualScroll(props, ref) {
287
+ const { width, height, scrollWidth = 0, scrollHeight = 0, className, innerClassName, children, onScroll: onScrollCallback, useIsScrolling: useIsScrolling$1 = false, innerRender, outerRender } = props;
236
288
  const outerRef = React.useRef(null);
237
- const { scrollOffset: scrollRowOffset, renderOffset: renderRowOffset, renderSize: renderRowSize, onScroll: onScrollRow, doScrollTo: doScrollToRow } = useVirtualScroll(totalRowSize, props.maxCssSize, props.minNumPages);
238
- const { scrollOffset: scrollColumnOffset, renderOffset: renderColumnOffset, renderSize: renderColumnSize, onScroll: onScrollColumn, doScrollTo: doScrollToColumn } = useVirtualScroll(totalColumnSize, props.maxCssSize, props.minNumPages);
239
- const isScrolling = useIsScrolling(outerRef);
289
+ const { scrollOffset: scrollRowOffset, renderOffset: renderRowOffset, renderSize: renderRowSize, onScroll: onScrollRow, doScrollTo: doScrollToRow } = useVirtualScroll(scrollHeight, props.maxCssSize, props.minNumPages);
290
+ const currentVerticalOffset = scrollRowOffset + renderRowOffset;
291
+ const { scrollOffset: scrollColOffset, renderOffset: renderColOffset, renderSize: renderColumnSize, onScroll: onScrollColumn, doScrollTo: doScrollToColumn } = useVirtualScroll(scrollWidth, props.maxCssSize, props.minNumPages);
292
+ const currentHorizontalOffset = scrollColOffset + renderColOffset;
293
+ const isActuallyScrolling = useIsScrolling(outerRef);
240
294
  React.useImperativeHandle(ref, () => {
241
295
  return {
242
296
  scrollTo(rowOffset, columnOffset) {
297
+ if (rowOffset === undefined && columnOffset === undefined)
298
+ return;
243
299
  const outer = outerRef.current;
244
300
  /* istanbul ignore else */
245
301
  if (outer) {
@@ -251,10 +307,14 @@ const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
251
307
  outer.scrollTo(options);
252
308
  }
253
309
  },
254
- scrollToItem(rowIndex, columnIndex) {
255
- const rowOffset = (rowIndex != undefined) ? rowOffsetMapping.itemOffset(rowIndex) : undefined;
256
- const columnOffset = (columnIndex != undefined) ? columnOffsetMapping.itemOffset(columnIndex) : undefined;
257
- this.scrollTo(rowOffset, columnOffset);
310
+ scrollToArea(verticalOffset, verticalSize, horizontalOffset, horizontalSize, option) {
311
+ const outer = outerRef.current;
312
+ /* istanbul ignore if*/
313
+ if (!outer)
314
+ return;
315
+ const rowOffset = getOffsetToScrollRange(verticalOffset, verticalSize, outer.clientHeight, currentVerticalOffset, option);
316
+ const colOffset = getOffsetToScrollRange(horizontalOffset, horizontalSize, outer.clientWidth, currentHorizontalOffset, option);
317
+ this.scrollTo(rowOffset, colOffset);
258
318
  },
259
319
  get clientWidth() {
260
320
  return outerRef.current ? outerRef.current.clientWidth : /* istanbul ignore next */ 0;
@@ -263,7 +323,7 @@ const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
263
323
  return outerRef.current ? outerRef.current.clientHeight : /* istanbul ignore next */ 0;
264
324
  }
265
325
  };
266
- }, [rowOffsetMapping, columnOffsetMapping, doScrollToRow, doScrollToColumn]);
326
+ }, [doScrollToRow, doScrollToColumn, currentVerticalOffset, currentHorizontalOffset]);
267
327
  function onScroll(event) {
268
328
  const { clientWidth, clientHeight, scrollWidth, scrollHeight, scrollLeft, scrollTop } = event.currentTarget;
269
329
  const [newScrollTop, newRowScrollState] = onScrollRow(clientHeight, scrollHeight, scrollTop);
@@ -272,113 +332,263 @@ const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
272
332
  outerRef.current.scrollTo(newScrollLeft, newScrollTop);
273
333
  onScrollCallback?.(newRowScrollState.scrollOffset + newRowScrollState.renderOffset, newColumnScrollState.scrollOffset + newColumnScrollState.renderOffset, newRowScrollState, newColumnScrollState);
274
334
  }
275
- const [startRowIndex, startRowOffset, rowSizes] = getRangeToRender(rowCount, rowOffsetMapping, height, scrollRowOffset + renderRowOffset);
276
- const [startColumnIndex, startColumnOffset, columnSizes] = getRangeToRender(columnCount, columnOffsetMapping, width, scrollColumnOffset + renderColumnOffset);
335
+ const isScrolling = useIsScrolling$1 ? isActuallyScrolling : undefined;
336
+ return (jsxs(VirtualContainer, { className: className, render: outerRender, onScroll: onScroll, ref: outerRef, style: { position: "relative", height, width, overflow: "auto", willChange: "transform" }, children: [jsx(VirtualContainer, { className: innerClassName, render: innerRender, style: { zIndex: 1, position: 'sticky', top: 0, left: 0, width: '100%', height: '100%' }, children: children({ isScrolling }) }), jsx("div", { style: { position: 'absolute', top: 0, left: 0,
337
+ height: scrollHeight ? renderRowSize : '100%',
338
+ width: scrollWidth ? renderColumnSize : '100%' } })] }));
339
+ });
340
+
341
+ function getRangeToRender(itemCount, itemOffsetMapping, clientExtent, scrollOffset) {
342
+ if (itemCount == 0) {
343
+ return [0, 0, 0, []];
344
+ }
345
+ // Negative offset equivalent to reducing the size of the window (possibly down to nothing)
346
+ if (scrollOffset < 0) {
347
+ clientExtent += scrollOffset;
348
+ scrollOffset = 0;
349
+ }
350
+ if (clientExtent <= 0) {
351
+ return [0, 0, 0, []];
352
+ }
353
+ const [baseIndex, startOffset] = itemOffsetMapping.offsetToItem(scrollOffset);
354
+ if (baseIndex >= itemCount) {
355
+ return [0, 0, 0, []];
356
+ }
357
+ let itemIndex = Math.max(0, Math.min(itemCount - 1, baseIndex));
358
+ const endOffset = scrollOffset + clientExtent;
359
+ const startIndex = itemIndex;
360
+ let offset = startOffset;
361
+ const sizes = [];
362
+ let totalSize = 0;
363
+ while (offset < endOffset && itemIndex < itemCount) {
364
+ const size = itemOffsetMapping.itemSize(itemIndex);
365
+ sizes.push(size);
366
+ totalSize += size;
367
+ offset += size;
368
+ itemIndex++;
369
+ }
370
+ return [startIndex, startOffset, totalSize, sizes];
371
+ }
372
+ function formatRepeat(repeat, size) {
373
+ return (repeat == 1) ? `${size}px` : `repeat(${repeat},${size}px)`;
374
+ }
375
+ function join(a, s) {
376
+ return a ? a + ' ' + s : s;
377
+ }
378
+ function getGridTemplate(sizes) {
379
+ const count = sizes.length;
380
+ if (count == 0)
381
+ return undefined;
382
+ let ret = undefined;
383
+ let lastSize = sizes[0];
384
+ let repeat = 1;
385
+ for (let i = 1; i < count; i++) {
386
+ const size = sizes[i];
387
+ if (size == lastSize) {
388
+ repeat++;
389
+ }
390
+ else {
391
+ const s = formatRepeat(repeat, lastSize);
392
+ ret = join(ret, s);
393
+ lastSize = size;
394
+ repeat = 1;
395
+ }
396
+ }
397
+ const s = formatRepeat(repeat, lastSize);
398
+ return join(ret, s);
399
+ }
400
+
401
+ const defaultItemKey$1 = (index, _data) => index;
402
+ const boxStyle$1 = { boxSizing: 'border-box' };
403
+ /**
404
+ * Displays a window onto the contents of a virtualized list starting from `offset`.
405
+ *
406
+ * Accepts props defined by {@link DisplayListProps}.
407
+ * You must pass a single instance of {@link DisplayListItem} as a child.
408
+ * @group Components
409
+ */
410
+ function DisplayList(props) {
411
+ const { width, height, itemCount, itemOffsetMapping, className, innerClassName, offset: renderOffset, children, itemData, itemKey = defaultItemKey$1, layout = 'vertical', outerRender, innerRender, isScrolling } = props;
412
+ const isVertical = layout === 'vertical';
413
+ const [startIndex, startOffset, renderSize, sizes] = getRangeToRender(itemCount, itemOffsetMapping, isVertical ? height : width, renderOffset);
414
+ const template = getGridTemplate(sizes);
415
+ const offset = startOffset - renderOffset;
277
416
  // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
278
- // naming convention as components do.
417
+ // naming convention as components do.
279
418
  const ChildVar = children;
280
- const outerRender = props.outerRender || defaultOuterRender$1;
281
- const innerRender = props.innerRender || defaultInnerRender$1;
282
- // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator.
283
- // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator
284
- // returns the result of the final statement which makes the iteration a little clumsier.
285
- let nextRowOffset = startRowOffset - renderRowOffset;
286
- let rowIndex = 0, rowOffset = 0;
287
- let nextColumnOffset = 0, columnIndex = 0, columnOffset = 0;
288
- return (jsx(Outer$1, { className: className, render: outerRender, onScroll: onScroll, ref: outerRef, style: { position: "relative", height, width, overflow: "auto", willChange: "transform" }, children: jsx(Inner$1, { className: innerClassName, render: innerRender, style: { height: renderRowSize, width: renderColumnSize }, children: rowSizes.map((rowSize, rowArrayIndex) => (rowOffset = nextRowOffset,
289
- nextRowOffset += rowSize,
290
- rowIndex = startRowIndex + rowArrayIndex,
291
- nextColumnOffset = startColumnOffset - renderColumnOffset,
292
- jsx(Fragment, { children: columnSizes.map((columnSize, columnArrayIndex) => (columnOffset = nextColumnOffset,
293
- nextColumnOffset += columnSize,
294
- columnIndex = startColumnIndex + columnArrayIndex,
295
- 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)))) }) }));
296
- });
419
+ return (jsx(VirtualContainer, { className: className, render: outerRender, style: { position: "relative", height, width, overflow: "hidden", willChange: "transform" }, children: jsx(VirtualContainer, { className: innerClassName, render: innerRender, style: { position: 'absolute',
420
+ display: 'grid',
421
+ gridTemplateColumns: isVertical ? undefined : template,
422
+ gridTemplateRows: isVertical ? template : undefined,
423
+ top: isVertical ? offset : 0,
424
+ left: isVertical ? 0 : offset,
425
+ height: isVertical ? renderSize : "100%",
426
+ width: isVertical ? "100%" : renderSize }, children: sizes.map((_size, arrayIndex) => (jsx(ChildVar, { data: itemData, isScrolling: isScrolling, index: startIndex + arrayIndex, style: boxStyle$1 }, itemKey(startIndex + arrayIndex, itemData)))) }) }));
427
+ }
297
428
 
298
- const defaultItemKey = (index, _data) => index;
299
- const Inner = React.forwardRef(function VirtualListInner({ render, ...rest }, ref) {
300
- return render(rest, ref);
301
- });
302
- function defaultInnerRender({ ...rest }, ref) {
303
- return jsx("div", { ref: ref, ...rest });
429
+ const defaultItemKey = (rowIndex, columnIndex, _data) => `${rowIndex}:${columnIndex}`;
430
+ const boxStyle = { boxSizing: 'border-box' };
431
+ /**
432
+ * Displays a window onto the contents of a virtualized grid starting from `rowOffset`, `columnOffset`.
433
+ *
434
+ * Accepts props defined by {@link DisplayGridProps}.
435
+ * You must pass a single instance of {@link DisplayGridItem} as a child.
436
+ * @group Components
437
+ */
438
+ function DisplayGrid(props) {
439
+ const { width, height, rowCount, rowOffsetMapping, columnCount, columnOffsetMapping, className, innerClassName, rowOffset: rowRenderOffset, columnOffset: colRenderOffset, children, itemData, itemKey = defaultItemKey, outerRender, innerRender, isScrolling } = props;
440
+ const [rowStartIndex, rowStartOffset, rowRenderSize, rowSizes] = getRangeToRender(rowCount, rowOffsetMapping, height, rowRenderOffset);
441
+ const rowTemplate = getGridTemplate(rowSizes);
442
+ const [colStartIndex, colStartOffset, colRenderSize, colSizes] = getRangeToRender(columnCount, columnOffsetMapping, width, colRenderOffset);
443
+ const colTemplate = getGridTemplate(colSizes);
444
+ const rowOffset = rowStartOffset - rowRenderOffset;
445
+ const colOffset = colStartOffset - colRenderOffset;
446
+ // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
447
+ // naming convention as components do.
448
+ const ChildVar = children;
449
+ return (jsx(VirtualContainer, { className: className, render: outerRender, style: { position: "relative", height, width, overflow: "hidden", willChange: "transform" }, children: jsx(VirtualContainer, { className: innerClassName, render: innerRender, style: { position: 'absolute',
450
+ display: 'grid',
451
+ gridTemplateColumns: colTemplate,
452
+ gridTemplateRows: rowTemplate,
453
+ top: rowOffset,
454
+ left: colOffset,
455
+ height: rowRenderSize,
456
+ width: colRenderSize }, children: rowSizes.map((_rowSize, rowIndex) => (jsx(Fragment, { children: colSizes.map((_size, colIndex) => (jsx(ChildVar, { data: itemData, isScrolling: isScrolling, rowIndex: rowStartIndex + rowIndex, columnIndex: colStartIndex + colIndex, style: boxStyle }, itemKey(rowStartIndex + rowIndex, colStartIndex + colIndex, itemData)))) }, itemKey(rowStartIndex + rowIndex, 0, itemData)))) }) }));
304
457
  }
305
- const Outer = React.forwardRef(function VirtualListOuter({ render, ...rest }, ref) {
306
- return render(rest, ref);
458
+
459
+ /**
460
+ * Returns the {@link ScrollRange} corresponding to a specified item.
461
+ *
462
+ * Used internally to implement {@link VirtualGridProxy.scrollToItem}. Can be used directly for
463
+ * advanced customization scenarios.
464
+ */
465
+ function getRangeToScroll(index, mapping) {
466
+ if (index === undefined)
467
+ return [undefined, undefined];
468
+ return [mapping.itemOffset(index), mapping.itemSize(index)];
469
+ }
470
+ /**
471
+ * Same logic as {@link VirtualGridProxy.scrollToItem} usable with your own {@link VirtualScroll}
472
+ *
473
+ * You're encouraged to put together your own combination of {@link VirtualScroll} and {@link DisplayGrid} for
474
+ * advanced customization scenarios. This function provides `ScrollToItem` functionality for use with your own {@link VirtualScroll}.
475
+ */
476
+ function virtualGridScrollToItem(scrollRef, rowOffsetMapping, columnOffsetMapping, rowIndex, columnIndex, option) {
477
+ const scroll = scrollRef.current;
478
+ /* istanbul ignore if */
479
+ if (!scroll)
480
+ return;
481
+ const [rowOffset, rowSize] = getRangeToScroll(rowIndex, rowOffsetMapping);
482
+ const [colOffset, colSize] = getRangeToScroll(columnIndex, columnOffsetMapping);
483
+ scroll.scrollToArea(rowOffset, rowSize, colOffset, colSize, option);
484
+ }
485
+
486
+ // Using a named function rather than => so that the name shows up in React Developer Tools
487
+ /**
488
+ * Virtual Scrolling Grid
489
+ *
490
+ * Accepts props defined by {@link VirtualGridProps}.
491
+ * Refs are forwarded to {@link VirtualGridProxy}.
492
+ * You must pass a single instance of {@link DisplayGridItem} as a child.
493
+ * @group Components
494
+ */
495
+ const VirtualGrid = React.forwardRef(function VirtualGrid(props, ref) {
496
+ const { rowCount, rowOffsetMapping, columnCount, columnOffsetMapping, children, innerClassName, innerRender, itemData, itemKey, onScroll: onScrollCallback, ...scrollProps } = props;
497
+ // Total size is same as offset to item one off the end
498
+ const totalRowSize = rowOffsetMapping.itemOffset(rowCount);
499
+ const totalColumnSize = columnOffsetMapping.itemOffset(columnCount);
500
+ const [state, setState] = React.useState([0, 0]);
501
+ const scrollRef = React.useRef(null);
502
+ React.useImperativeHandle(ref, () => {
503
+ return {
504
+ scrollTo(rowOffset, columnOffset) {
505
+ const scroll = scrollRef.current;
506
+ /* istanbul ignore else */
507
+ if (scroll)
508
+ scroll.scrollTo(rowOffset, columnOffset);
509
+ },
510
+ scrollToItem(rowIndex, columnIndex, option) {
511
+ virtualGridScrollToItem(scrollRef, rowOffsetMapping, columnOffsetMapping, rowIndex, columnIndex, option);
512
+ },
513
+ get clientWidth() {
514
+ return scrollRef.current ? scrollRef.current.clientWidth : /* istanbul ignore next */ 0;
515
+ },
516
+ get clientHeight() {
517
+ return scrollRef.current ? scrollRef.current.clientHeight : /* istanbul ignore next */ 0;
518
+ }
519
+ };
520
+ }, [rowOffsetMapping, columnOffsetMapping]);
521
+ // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
522
+ // naming convention as components do.
523
+ const ChildVar = children;
524
+ return (jsx(VirtualScroll, { ref: scrollRef, ...scrollProps, scrollHeight: totalRowSize, scrollWidth: totalColumnSize, onScroll: (verticalOffset, horizontalOffset, verticalScrollState, horizontalScrollState) => {
525
+ setState([verticalOffset, horizontalOffset]);
526
+ if (onScrollCallback)
527
+ onScrollCallback(verticalOffset, horizontalOffset, verticalScrollState, horizontalScrollState);
528
+ }, children: ({ isScrolling }) => (jsx(AutoSizer, { style: { height: '100%', width: '100%' }, children: ({ height, width }) => (jsx(DisplayGrid, { innerClassName: innerClassName, innerRender: innerRender, rowOffset: state[0], columnOffset: state[1], height: height, rowCount: rowCount, columnCount: columnCount, itemData: itemData, itemKey: itemKey, isScrolling: isScrolling, rowOffsetMapping: rowOffsetMapping, columnOffsetMapping: columnOffsetMapping, width: width, children: ChildVar })) })) }));
307
529
  });
308
- function defaultOuterRender({ ...rest }, ref) {
309
- return jsx("div", { ref: ref, ...rest });
530
+
531
+ /**
532
+ * Same logic as {@link VirtualListProxy.scrollToItem} usable with your own {@link VirtualScroll}
533
+ *
534
+ * You're encouraged to put together your own combination of {@link VirtualScroll} and {@link DisplayList} for
535
+ * advanced customization scenarios. This function provides `ScrollToItem` functionality for use with your own {@link VirtualScroll}.
536
+ */
537
+ function virtualListScrollToItem(scrollRef, itemOffsetMapping, isVertical, index, option) {
538
+ const scroll = scrollRef.current;
539
+ /* istanbul ignore if */
540
+ if (!scroll)
541
+ return;
542
+ const itemOffset = itemOffsetMapping.itemOffset(index);
543
+ const itemSize = itemOffsetMapping.itemSize(index);
544
+ if (isVertical)
545
+ scroll.scrollToArea(itemOffset, itemSize, undefined, undefined, option);
546
+ else
547
+ scroll.scrollToArea(undefined, undefined, itemOffset, itemSize, option);
310
548
  }
549
+
311
550
  // Using a named function rather than => so that the name shows up in React Developer Tools
312
551
  /**
313
552
  * Virtual Scrolling List
314
553
  *
315
554
  * Accepts props defined by {@link VirtualListProps}.
316
555
  * Refs are forwarded to {@link VirtualListProxy}.
317
- * You must pass a single instance of {@link VirtualListItem} as a child.
556
+ * You must pass a single instance of {@link DisplayListItem} as a child.
318
557
  * @group Components
319
558
  */
320
559
  const VirtualList = React.forwardRef(function VirtualList(props, ref) {
321
- const { width, height, itemCount, itemOffsetMapping, children, className, innerClassName, itemData = undefined, itemKey = defaultItemKey, layout = 'vertical', onScroll: onScrollCallback, useIsScrolling: useIsScrolling$1 = false } = props;
560
+ const { itemCount, itemOffsetMapping, children, layout = 'vertical', onScroll: onScrollCallback, innerClassName, innerRender, itemData, itemKey, ...scrollProps } = props;
322
561
  // Total size is same as offset to item one off the end
323
- const totalSize = itemOffsetMapping.itemOffset(itemCount);
324
- const outerRef = React.useRef(null);
325
- const { scrollOffset, renderOffset, renderSize, onScroll: onScrollExtent, doScrollTo } = useVirtualScroll(totalSize, props.maxCssSize, props.minNumPages);
326
- const isScrolling = useIsScrolling(outerRef);
562
+ const renderSize = itemOffsetMapping.itemOffset(itemCount);
563
+ const [offset, setOffset] = React.useState(0);
564
+ const scrollRef = React.useRef(null);
327
565
  const isVertical = layout === 'vertical';
328
566
  React.useImperativeHandle(ref, () => {
329
567
  return {
330
568
  scrollTo(offset) {
331
- const outer = outerRef.current;
332
- /* istanbul ignore else */
333
- if (outer) {
334
- if (isVertical)
335
- outer.scrollTo(0, doScrollTo(offset, outer.clientHeight));
336
- else
337
- outer.scrollTo(doScrollTo(offset, outer.clientWidth), 0);
338
- }
569
+ const scroll = scrollRef.current;
570
+ /* istanbul ignore if */
571
+ if (!scroll)
572
+ return;
573
+ if (isVertical)
574
+ scroll.scrollTo(offset, undefined);
575
+ else
576
+ scroll.scrollTo(undefined, offset);
339
577
  },
340
- scrollToItem(index) {
341
- this.scrollTo(itemOffsetMapping.itemOffset(index));
578
+ scrollToItem(index, option) {
579
+ virtualListScrollToItem(scrollRef, itemOffsetMapping, isVertical, index, option);
342
580
  }
343
581
  };
344
- }, [itemOffsetMapping, isVertical, doScrollTo]);
345
- function onScroll(event) {
346
- if (isVertical) {
347
- const { clientHeight, scrollHeight, scrollTop, scrollLeft } = event.currentTarget;
348
- const [newScrollTop, newScrollState] = onScrollExtent(clientHeight, scrollHeight, scrollTop);
349
- if (newScrollTop != scrollTop && outerRef.current)
350
- outerRef.current.scrollTo(scrollLeft, newScrollTop);
351
- onScrollCallback?.(newScrollState.scrollOffset + newScrollState.renderOffset, newScrollState);
352
- }
353
- else {
354
- const { clientWidth, scrollWidth, scrollTop, scrollLeft } = event.currentTarget;
355
- const [newScrollLeft, newScrollState] = onScrollExtent(clientWidth, scrollWidth, scrollLeft);
356
- if (newScrollLeft != scrollLeft && outerRef.current)
357
- outerRef.current.scrollTo(newScrollLeft, scrollTop);
358
- onScrollCallback?.(newScrollState.scrollOffset + newScrollState.renderOffset, newScrollState);
359
- }
360
- }
361
- const [startIndex, startOffset, sizes] = getRangeToRender(itemCount, itemOffsetMapping, isVertical ? height : width, scrollOffset + renderOffset);
582
+ }, [itemOffsetMapping, isVertical]);
362
583
  // We can decide the JSX child type at runtime as long as we use a variable that uses the same capitalized
363
584
  // naming convention as components do.
364
585
  const ChildVar = children;
365
- const outerRender = props.outerRender || defaultOuterRender;
366
- const innerRender = props.innerRender || defaultInnerRender;
367
- // Being far too clever. Implementing a complex iteration in JSX in a map expression by abusing the comma operator.
368
- // You can't declare local variables in an expression so they need to be hoisted out of the JSX. The comma operator
369
- // returns the result of the final statement which makes the iteration a little clumsier.
370
- let nextOffset = startOffset - renderOffset;
371
- let index, offset;
372
- return (jsx(Outer, { className: className, render: outerRender, onScroll: onScroll, ref: outerRef, style: { position: "relative", height, width, overflow: "auto", willChange: "transform" }, children: jsx(Inner, { className: innerClassName, render: innerRender, style: { height: isVertical ? renderSize : "100%", width: isVertical ? "100%" : renderSize }, children: sizes.map((size, arrayIndex) => (offset = nextOffset,
373
- nextOffset += size,
374
- index = startIndex + arrayIndex,
375
- jsx(ChildVar, { data: itemData, index: index, isScrolling: useIsScrolling$1 ? isScrolling : undefined, style: {
376
- position: "absolute",
377
- top: isVertical ? offset : undefined,
378
- left: isVertical ? undefined : offset,
379
- height: isVertical ? size : "100%",
380
- width: isVertical ? "100%" : size,
381
- } }, itemKey(index, itemData)))) }) }));
586
+ return (jsx(VirtualScroll, { ref: scrollRef, ...scrollProps, scrollHeight: isVertical ? renderSize : undefined, scrollWidth: isVertical ? undefined : renderSize, onScroll: (verticalOffset, horizontalOffset, verticalScrollState, horizontalScrollState) => {
587
+ const newOffset = isVertical ? verticalOffset : horizontalOffset;
588
+ setOffset(newOffset);
589
+ if (onScrollCallback)
590
+ onScrollCallback(newOffset, isVertical ? verticalScrollState : horizontalScrollState);
591
+ }, children: ({ isScrolling }) => (jsx(AutoSizer, { style: { height: '100%', width: '100%' }, children: ({ height, width }) => (jsx(DisplayList, { innerClassName: innerClassName, innerRender: innerRender, layout: layout, offset: offset, height: height, itemCount: itemCount, itemData: itemData, itemKey: itemKey, isScrolling: isScrolling, itemOffsetMapping: itemOffsetMapping, width: width, children: ChildVar })) })) }));
382
592
  });
383
593
 
384
594
  class FixedSizeItemOffsetMapping {
@@ -472,5 +682,5 @@ function useVariableSizeItemOffsetMapping(defaultItemSize, sizes) {
472
682
  return new VariableSizeItemOffsetMapping(defaultItemSize, sizes || []);
473
683
  }
474
684
 
475
- export { VirtualGrid, VirtualList, useFixedSizeItemOffsetMapping, useVariableSizeItemOffsetMapping };
685
+ export { AutoSizer, DisplayGrid, DisplayList, VirtualContainer, VirtualGrid, VirtualList, VirtualScroll, getOffsetToScrollRange, getRangeToScroll, useFixedSizeItemOffsetMapping, useVariableSizeItemOffsetMapping, virtualGridScrollToItem, virtualListScrollToItem };
476
686
  //# sourceMappingURL=index.js.map