beautiful-grid 1.0.2 → 1.0.3

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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![NPM version](https://img.shields.io/npm/v/beautiful-grid.svg?style=flat)](https://npmjs.org/package/beautiful-grid)
6
6
  [![NPM downloads](https://img.shields.io/npm/dm/beautiful-grid.svg?style=flat)](https://npmjs.org/package/beautiful-grid)
7
7
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
8
- [![Coverage](https://img.shields.io/badge/coverage-84.52%25-brightgreen.svg)](https://github.com/axisj/beautiful-grid/actions/workflows/tests.yml)
8
+ [![Coverage](https://img.shields.io/badge/coverage-85.02%25-brightgreen.svg)](https://github.com/axisj/beautiful-grid/actions/workflows/tests.yml)
9
9
 
10
10
  BeautifulGrid is a high-performance, feature-packed, and beautifully designed open-source React Data Grid for data-heavy business applications. It combines zero-runtime-CSS styling with virtual scrolling, spreadsheet-like cell selection and clipboard operations, built-in and plugin cell editing, multi-level grouped headers, filtering & sorting toolboxes, server/client pagination, pivot table transforms, row reordering, and flexible theming.
11
11
 
@@ -319,6 +319,25 @@ function Table(props) {
319
319
  var trHeight = itemHeight + itemPadding * 2;
320
320
  var scrollableBodyHeight = Math.max(contentBodyHeight - frozenRowsHeight, 0);
321
321
  var mainViewportWidth = Math.max(width - (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0), 0);
322
+ var logicalBodyContentHeight = data.length * trHeight;
323
+ var virtualScrollWindowMetrics = React.useMemo(function () {
324
+ return (0, utils_1.getVirtualScrollWindowMetrics)({
325
+ logicalContentHeight: logicalBodyContentHeight,
326
+ viewportHeight: contentBodyHeight,
327
+ enabled: scrollbar.variant !== 'native',
328
+ });
329
+ }, [contentBodyHeight, logicalBodyContentHeight, scrollbar.variant]);
330
+ var _18 = __read(React.useState(0), 2), virtualScrollBase = _18[0], setVirtualScrollBase = _18[1];
331
+ var virtualScrollBaseRef = (0, react_1.useRef)(0);
332
+ var containerRef = (0, react_1.useRef)(null);
333
+ var searchPopoverRef = (0, react_1.useRef)(null);
334
+ var editorPortalRef = (0, react_1.useRef)(null);
335
+ var bodyContainerRef = (0, react_1.useRef)(null);
336
+ var scrollContainerRef = (0, react_1.useRef)(null);
337
+ var logicalVerticalScrollState = React.useMemo(function () { return ({
338
+ scrollTop: scrollTop,
339
+ scrollHeight: virtualScrollWindowMetrics.logicalContentHeight,
340
+ }); }, [scrollTop, virtualScrollWindowMetrics.logicalContentHeight]);
322
341
  var hasMultiCellSelection = React.useMemo(function () {
323
342
  return cellSelectionRanges.length > 1 ||
324
343
  cellSelectionRanges.some(function (range) {
@@ -418,7 +437,7 @@ function Table(props) {
418
437
  top: topQuadrant ? 0 : scrollTop,
419
438
  width: leftQuadrant ? frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0 : mainViewportWidth,
420
439
  height: topQuadrant ? frozenRowsHeight : scrollableBodyHeight,
421
- } }));
440
+ }, offsetTop: !topQuadrant && virtualScrollWindowMetrics.enabled ? virtualScrollBase : 0 }));
422
441
  };
423
442
  var visibleScrollableRows = React.useMemo(function () {
424
443
  var _a;
@@ -436,15 +455,14 @@ function Table(props) {
436
455
  startRowIndex: visibleScrollableRows.startRowIndex,
437
456
  endRowIndex: visibleScrollableRows.endRowIndex,
438
457
  }); }, [visibleScrollableRows.endRowIndex, visibleScrollableRows.startRowIndex]);
439
- var frozenScrollableBodyStyle = React.useMemo(function () { return ({ top: visibleScrollableRows.paddingTop }); }, [visibleScrollableRows.paddingTop]);
440
- var containerRef = (0, react_1.useRef)(null);
441
- var searchPopoverRef = (0, react_1.useRef)(null);
458
+ var frozenScrollableBodyStyle = React.useMemo(function () { return ({
459
+ top: virtualScrollWindowMetrics.enabled
460
+ ? visibleScrollableRows.paddingTop - virtualScrollBase
461
+ : visibleScrollableRows.paddingTop,
462
+ }); }, [virtualScrollBase, virtualScrollWindowMetrics.enabled, visibleScrollableRows.paddingTop]);
442
463
  var warnedContextMenuIdsRef = (0, react_1.useRef)(new Set());
443
464
  var warnedContextMenuFactoryRef = (0, react_1.useRef)(false);
444
- var editorPortalRef = (0, react_1.useRef)(null);
445
465
  var editorPortalContext = React.useMemo(function () { return ({ gridRef: containerRef, portalRef: editorPortalRef }); }, []);
446
- var bodyContainerRef = (0, react_1.useRef)(null);
447
- var scrollContainerRef = (0, react_1.useRef)(null);
448
466
  var rowReorderController = (0, useRowReorderController_1.useRowReorderController)({
449
467
  containerRef: containerRef,
450
468
  bodyContainerRef: bodyContainerRef,
@@ -556,7 +574,8 @@ function Table(props) {
556
574
  var stickyBottomHeight = (summary === null || summary === void 0 ? void 0 : summary.position) === 'bottom' ? summaryHeight : 0;
557
575
  var stickyFixedHeight = stickyTopHeight + stickyBottomHeight;
558
576
  var scrollViewportHeight = contentBodyHeight + stickyFixedHeight;
559
- var scrollPlaneHeight = stickyFixedHeight + frozenRowsHeight + visibleScrollableRows.scrollContentHeight;
577
+ var physicalScrollableRowsHeight = Math.max(virtualScrollWindowMetrics.physicalContentHeight - frozenRowsHeight, 0);
578
+ var scrollPlaneHeight = stickyFixedHeight + virtualScrollWindowMetrics.physicalContentHeight;
560
579
  var scrollPlaneContentWidth = (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0) + scrollableColumnsWidth;
561
580
  var scrollPlaneWidth = Math.max(width, scrollPlaneContentWidth);
562
581
  var customVerticalScrollbarGutter = scrollbar.vertical.visible && scrollbar.variant === 'classic'
@@ -567,7 +586,16 @@ function Table(props) {
567
586
  var scrollPlaneMinWidth = customVerticalScrollbarGutter
568
587
  ? "max(100%, calc(".concat(scrollPlaneContentWidth, "px + ").concat(customVerticalScrollbarGutter, "))")
569
588
  : scrollPlaneWidth;
570
- var scrollbarMetrics = (0, scrollbar_1.useScrollbarMetrics)(scrollContainerRef, [data.length, itemHeight, itemPadding, contentBodyHeight, width], scrollPlaneContentWidth, stickyFixedHeight);
589
+ var measuredScrollbarMetrics = (0, scrollbar_1.useScrollbarMetrics)(scrollContainerRef, [data.length, itemHeight, itemPadding, contentBodyHeight, width], scrollPlaneContentWidth, stickyFixedHeight);
590
+ var scrollbarMetrics = React.useMemo(function () {
591
+ return virtualScrollWindowMetrics.enabled
592
+ ? __assign(__assign({}, measuredScrollbarMetrics), { vertical: {
593
+ viewportSize: contentBodyHeight,
594
+ contentSize: virtualScrollWindowMetrics.logicalContentHeight,
595
+ maxScroll: virtualScrollWindowMetrics.logicalMaxScroll,
596
+ hasOverflow: virtualScrollWindowMetrics.logicalMaxScroll > 1,
597
+ } }) : measuredScrollbarMetrics;
598
+ }, [contentBodyHeight, measuredScrollbarMetrics, virtualScrollWindowMetrics]);
571
599
  var measuredVerticalScrollbarGutter = customVerticalScrollbarGutter && scrollbarMetrics.horizontal.hasOverflow
572
600
  ? Math.max(scrollbarMetrics.horizontal.contentSize - scrollPlaneContentWidth, 0)
573
601
  : 0;
@@ -587,11 +615,6 @@ function Table(props) {
587
615
  scrollContainerRef.current.scrollLeft = offset;
588
616
  }
589
617
  }, []);
590
- var handleScrollTopChange = (0, react_1.useCallback)(function (offset) {
591
- if (scrollContainerRef.current) {
592
- scrollContainerRef.current.scrollTop = offset;
593
- }
594
- }, []);
595
618
  var latestScrollRef = (0, react_1.useRef)({ top: scrollTop, left: scrollLeft });
596
619
  var latestCommittedScrollRef = (0, react_1.useRef)({ top: scrollTop, left: scrollLeft });
597
620
  var scrollRafRef = (0, react_1.useRef)(null);
@@ -608,6 +631,7 @@ function Table(props) {
608
631
  frozenRowCount: frozenRowCount,
609
632
  frozenColumnsWidth: frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0,
610
633
  frozenRowsHeight: frozenRowsHeight,
634
+ scrollTop: scrollTop,
611
635
  trHeight: trHeight,
612
636
  });
613
637
  (0, react_1.useEffect)(function () {
@@ -619,9 +643,10 @@ function Table(props) {
619
643
  frozenRowCount: frozenRowCount,
620
644
  frozenColumnsWidth: frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0,
621
645
  frozenRowsHeight: frozenRowsHeight,
646
+ scrollTop: scrollTop,
622
647
  trHeight: trHeight,
623
648
  };
624
- }, [columns, data.length, props.frozenColumnIndex, frozenColumnsWidth, frozenRowCount, frozenRowsHeight, trHeight]);
649
+ }, [columns, data.length, props.frozenColumnIndex, frozenColumnsWidth, frozenRowCount, frozenRowsHeight, scrollTop, trHeight]);
625
650
  var markScrollActive = (0, react_1.useCallback)(function () {
626
651
  var _a;
627
652
  (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.setAttribute('data-bgrid-scrolling', 'true');
@@ -633,6 +658,28 @@ function Table(props) {
633
658
  (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.removeAttribute('data-bgrid-scrolling');
634
659
  }, SCROLL_IDLE_DELAY);
635
660
  }, []);
661
+ var commitScroll = (0, react_1.useCallback)(function (top, left) {
662
+ var scrollContainer = scrollContainerRef.current;
663
+ if (!scrollContainer)
664
+ return;
665
+ var position = (0, utils_1.getVirtualScrollWindowPosition)(top, virtualScrollWindowMetrics);
666
+ if (virtualScrollBaseRef.current !== position.base) {
667
+ virtualScrollBaseRef.current = position.base;
668
+ setVirtualScrollBase(position.base);
669
+ }
670
+ if (scrollContainer.scrollTop !== position.physicalScrollTop) {
671
+ scrollContainer.scrollTop = position.physicalScrollTop;
672
+ }
673
+ if (scrollContainer.scrollLeft !== left)
674
+ scrollContainer.scrollLeft = left;
675
+ latestScrollRef.current = { top: position.logicalScrollTop, left: left };
676
+ latestCommittedScrollRef.current = { top: position.logicalScrollTop, left: left };
677
+ setScroll(position.logicalScrollTop, left);
678
+ }, [setScroll, virtualScrollWindowMetrics]);
679
+ var handleScrollTopChange = (0, react_1.useCallback)(function (offset) {
680
+ var _a, _b;
681
+ commitScroll(offset, (_b = (_a = scrollContainerRef.current) === null || _a === void 0 ? void 0 : _a.scrollLeft) !== null && _b !== void 0 ? _b : scrollLeft);
682
+ }, [commitScroll, scrollLeft]);
636
683
  var scheduleScrollFrame = (0, react_1.useCallback)(function () {
637
684
  if (scrollRafRef.current !== null)
638
685
  return;
@@ -642,91 +689,42 @@ function Table(props) {
642
689
  var scrollContainer = scrollContainerRef.current;
643
690
  if (!scrollContainer)
644
691
  return;
645
- var scrollTop = scrollContainer.scrollTop, scrollLeft = scrollContainer.scrollLeft;
646
- latestScrollRef.current = { top: scrollTop, left: scrollLeft };
692
+ var physicalScrollTop = scrollContainer.scrollTop, scrollLeft = scrollContainer.scrollLeft;
693
+ var position = (0, utils_1.rebaseVirtualScrollWindow)(virtualScrollBaseRef.current, physicalScrollTop, virtualScrollWindowMetrics);
694
+ if (virtualScrollBaseRef.current !== position.base) {
695
+ virtualScrollBaseRef.current = position.base;
696
+ setVirtualScrollBase(position.base);
697
+ }
698
+ if (physicalScrollTop !== position.physicalScrollTop) {
699
+ scrollContainer.scrollTop = position.physicalScrollTop;
700
+ }
701
+ var logicalScrollTop = position.logicalScrollTop;
702
+ latestScrollRef.current = { top: logicalScrollTop, left: scrollLeft };
647
703
  var _b = latestCommittedScrollRef.current, committedTop = _b.top, committedLeft = _b.left;
648
- if (committedTop !== scrollTop || committedLeft !== scrollLeft) {
649
- latestCommittedScrollRef.current = { top: scrollTop, left: scrollLeft };
650
- setScroll(scrollTop, scrollLeft);
704
+ if (committedTop !== logicalScrollTop || committedLeft !== scrollLeft) {
705
+ latestCommittedScrollRef.current = { top: logicalScrollTop, left: scrollLeft };
706
+ setScroll(logicalScrollTop, scrollLeft);
651
707
  }
652
708
  if ((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.hasAttribute('data-bgrid-scrolling')) {
653
709
  scrollRafRef.current = requestAnimationFrame(pollNativeScrollPosition);
654
710
  }
655
711
  };
656
712
  scrollRafRef.current = requestAnimationFrame(pollNativeScrollPosition);
657
- }, [setScroll]);
713
+ }, [setScroll, virtualScrollWindowMetrics]);
658
714
  var onScroll = (0, react_1.useCallback)(function () {
659
715
  if (scrollContainerRef.current) {
660
- var _a = scrollContainerRef.current, scrollTop_1 = _a.scrollTop, scrollLeft_1 = _a.scrollLeft;
661
- latestScrollRef.current = { top: scrollTop_1, left: scrollLeft_1 };
662
716
  markScrollActive();
663
717
  scheduleScrollFrame();
664
718
  }
665
719
  }, [markScrollActive, scheduleScrollFrame]);
666
- var onWheel = (0, react_1.useCallback)(function (evt) {
667
- var scrollContainer = scrollContainerRef.current;
668
- if (!scrollContainer)
669
- return;
670
- var delta = { x: 0, y: 0 };
671
- if (evt.detail) {
672
- delta.y = evt.detail * 10;
673
- }
674
- else if (typeof evt.deltaY === 'undefined') {
675
- delta.y = -evt.wheelDelta;
676
- delta.x = 0;
677
- }
678
- else {
679
- delta.y = evt.deltaY;
680
- delta.x = evt.deltaX;
681
- }
682
- if (evt.deltaMode === 1) {
683
- delta.y = delta.y * 16;
684
- delta.x = delta.x * 16;
685
- }
686
- else if (evt.deltaMode === 2) {
687
- delta.y = delta.y * scrollContainer.clientHeight;
688
- delta.x = delta.x * scrollContainer.clientWidth;
689
- }
690
- var maxScrollTop = Math.max(scrollContainer.scrollHeight - scrollContainer.clientHeight, 0);
691
- var maxScrollLeft = Math.max(scrollContainer.scrollWidth - scrollContainer.clientWidth, 0);
692
- var nextScrollTop = Math.min(maxScrollTop, Math.max(0, scrollContainer.scrollTop + delta.y));
693
- var nextScrollLeft = Math.min(maxScrollLeft, Math.max(0, scrollContainer.scrollLeft + delta.x));
694
- var didScrollTop = nextScrollTop !== scrollContainer.scrollTop;
695
- var didScrollLeft = nextScrollLeft !== scrollContainer.scrollLeft;
696
- var isVertical = Math.abs(delta.y) > Math.abs(delta.x);
697
- if (isVertical) {
698
- if (!didScrollTop)
699
- return;
700
- }
701
- else {
702
- if (!didScrollLeft)
703
- return;
704
- }
705
- evt.preventDefault();
706
- if (didScrollTop)
707
- scrollContainer.scrollTop = nextScrollTop;
708
- if (didScrollLeft)
709
- scrollContainer.scrollLeft = nextScrollLeft;
710
- }, []);
711
720
  var resetScrollPosition = (0, react_1.useCallback)(function (type) {
712
- var _a, _b;
713
- if (type === 'all' || type === 'left')
714
- (_a = scrollContainerRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ left: 0 });
715
- if (type === 'all' || type === 'top')
716
- (_b = scrollContainerRef.current) === null || _b === void 0 ? void 0 : _b.scrollTo({ top: 0 });
717
- }, []);
718
- var commitScroll = (0, react_1.useCallback)(function (top, left) {
719
721
  var scrollContainer = scrollContainerRef.current;
720
722
  if (!scrollContainer)
721
723
  return;
722
- if (scrollContainer.scrollTop !== top)
723
- scrollContainer.scrollTop = top;
724
- if (scrollContainer.scrollLeft !== left)
725
- scrollContainer.scrollLeft = left;
726
- latestScrollRef.current = { top: top, left: left };
727
- latestCommittedScrollRef.current = { top: top, left: left };
728
- setScroll(top, left);
729
- }, [setScroll]);
724
+ var nextTop = type === 'all' || type === 'top' ? 0 : latestScrollRef.current.top;
725
+ var nextLeft = type === 'all' || type === 'left' ? 0 : scrollContainer.scrollLeft;
726
+ commitScroll(nextTop, nextLeft);
727
+ }, [commitScroll]);
730
728
  (0, react_1.useEffect)(function () {
731
729
  var _a, _b;
732
730
  if (!searchOpen || activeSearchMatchIndex === undefined)
@@ -751,6 +749,7 @@ function Table(props) {
751
749
  frozenRowCount: frozenRowCount,
752
750
  columns: columns,
753
751
  rowHeight: trHeight,
752
+ verticalScrollState: logicalVerticalScrollState,
754
753
  viewportInsets: viewportInsets,
755
754
  });
756
755
  if (result.didScroll)
@@ -760,6 +759,7 @@ function Table(props) {
760
759
  columns,
761
760
  commitScroll,
762
761
  frozenRowCount,
762
+ logicalVerticalScrollState,
763
763
  props.frozenColumnIndex,
764
764
  searchMatches,
765
765
  searchOpen,
@@ -992,18 +992,18 @@ function Table(props) {
992
992
  if (!dragState || !pointer || !scrollContainer || !bodyContainer)
993
993
  return;
994
994
  var delta = getSelectionAutoScrollDelta(pointer.clientX, pointer.clientY, bodyContainer, scrollContainer);
995
- var maxScrollTop = Math.max(scrollContainer.scrollHeight - scrollContainer.clientHeight, 0);
995
+ var maxScrollTop = virtualScrollWindowMetrics.logicalMaxScroll;
996
996
  var maxScrollLeft = Math.max(scrollContainer.scrollWidth - scrollContainer.clientWidth, 0);
997
- var nextScrollTop = (0, coordinate_1.clamp)(scrollContainer.scrollTop + delta.y, 0, maxScrollTop);
997
+ var nextScrollTop = (0, coordinate_1.clamp)(latestScrollRef.current.top + delta.y, 0, maxScrollTop);
998
998
  var nextScrollLeft = (0, coordinate_1.clamp)(scrollContainer.scrollLeft + delta.x, 0, maxScrollLeft);
999
- if (nextScrollTop !== scrollContainer.scrollTop || nextScrollLeft !== scrollContainer.scrollLeft) {
999
+ if (nextScrollTop !== latestScrollRef.current.top || nextScrollLeft !== scrollContainer.scrollLeft) {
1000
1000
  commitScroll(nextScrollTop, nextScrollLeft);
1001
1001
  }
1002
1002
  updateSelectionByPointer(pointer.clientX, pointer.clientY);
1003
1003
  if (cellSelectionDragRef.current && (delta.x !== 0 || delta.y !== 0)) {
1004
1004
  selectionAutoScrollRafRef.current = requestAnimationFrame(runSelectionAutoScroll);
1005
1005
  }
1006
- }, [commitScroll, updateSelectionByPointer]);
1006
+ }, [commitScroll, updateSelectionByPointer, virtualScrollWindowMetrics.logicalMaxScroll]);
1007
1007
  var selectAllCells = (0, react_1.useCallback)(function () {
1008
1008
  if (!cellSelectionEnabled)
1009
1009
  return false;
@@ -1693,8 +1693,6 @@ function Table(props) {
1693
1693
  if (scrollContainerRefCurrent) {
1694
1694
  scrollContainerRefCurrent.removeEventListener('scroll', onScroll);
1695
1695
  scrollContainerRefCurrent.addEventListener('scroll', onScroll, { passive: true, capture: true });
1696
- // scrollContainerRefCurrent.removeEventListener('wheel', onWheel);
1697
- // scrollContainerRefCurrent.addEventListener('wheel', onWheel, { passive: false, capture: true });
1698
1696
  }
1699
1697
  return function () {
1700
1698
  if (scrollRafRef.current !== null) {
@@ -1708,18 +1706,17 @@ function Table(props) {
1708
1706
  containerRefCurrent === null || containerRefCurrent === void 0 ? void 0 : containerRefCurrent.removeAttribute('data-bgrid-scrolling');
1709
1707
  scrollContainerRefCurrent === null || scrollContainerRefCurrent === void 0 ? void 0 : scrollContainerRefCurrent.removeEventListener('scroll', onScroll);
1710
1708
  };
1711
- }, [onScroll, onWheel, setInitialized]);
1709
+ }, [onScroll, setInitialized]);
1712
1710
  React.useLayoutEffect(function () {
1711
+ var _a, _b;
1713
1712
  if (!scrollContainerRef.current)
1714
1713
  return;
1715
- if (props.scrollLeft !== undefined) {
1716
- scrollContainerRef.current.scrollLeft = props.scrollLeft;
1717
- }
1718
- if (props.scrollTop !== undefined) {
1719
- scrollContainerRef.current.scrollTop = props.scrollTop;
1720
- }
1721
- }, [props.scrollLeft, props.scrollTop]);
1714
+ var nextTop = (_a = props.scrollTop) !== null && _a !== void 0 ? _a : latestScrollRef.current.top;
1715
+ var nextLeft = (_b = props.scrollLeft) !== null && _b !== void 0 ? _b : latestScrollRef.current.left;
1716
+ commitScroll(nextTop, nextLeft);
1717
+ }, [commitScroll, props.scrollLeft, props.scrollTop]);
1722
1718
  (0, react_1.useEffect)(function () {
1719
+ latestScrollRef.current = { top: scrollTop, left: scrollLeft };
1723
1720
  latestCommittedScrollRef.current = { top: scrollTop, left: scrollLeft };
1724
1721
  }, [scrollTop, scrollLeft]);
1725
1722
  (0, react_1.useEffect)(function () {
@@ -1801,6 +1798,7 @@ function Table(props) {
1801
1798
  searchOpen: searchOpen,
1802
1799
  searchOptions: searchOptions,
1803
1800
  trHeight: trHeight,
1801
+ verticalScrollState: logicalVerticalScrollState,
1804
1802
  viewportInsets: keyboardViewportInsets,
1805
1803
  });
1806
1804
  keyboardRuntimeRef.current = {
@@ -1833,6 +1831,7 @@ function Table(props) {
1833
1831
  searchOpen: searchOpen,
1834
1832
  searchOptions: searchOptions,
1835
1833
  trHeight: trHeight,
1834
+ verticalScrollState: logicalVerticalScrollState,
1836
1835
  viewportInsets: keyboardViewportInsets,
1837
1836
  };
1838
1837
  (0, react_1.useEffect)(function () {
@@ -1849,7 +1848,7 @@ function Table(props) {
1849
1848
  };
1850
1849
  var moveWithArrowKey = function (key, extendSelection, toBoundary) {
1851
1850
  if (toBoundary === void 0) { toBoundary = false; }
1852
- var _a = keyboardRuntimeRef.current, activeCell = _a.activeCell, cellInteractionSession = _a.cellInteractionSession, cellNavigationOptions = _a.cellNavigationOptions, cellSelectionEnabled = _a.cellSelectionEnabled, columns = _a.columns, commitScroll = _a.commitScroll, dataLength = _a.dataLength, frozenColumnIndex = _a.frozenColumnIndex, frozenRowCount = _a.frozenRowCount, moveActiveCell = _a.moveActiveCell, trHeight = _a.trHeight, viewportInsets = _a.viewportInsets;
1851
+ var _a = keyboardRuntimeRef.current, activeCell = _a.activeCell, cellInteractionSession = _a.cellInteractionSession, cellNavigationOptions = _a.cellNavigationOptions, cellSelectionEnabled = _a.cellSelectionEnabled, columns = _a.columns, commitScroll = _a.commitScroll, dataLength = _a.dataLength, frozenColumnIndex = _a.frozenColumnIndex, frozenRowCount = _a.frozenRowCount, moveActiveCell = _a.moveActiveCell, trHeight = _a.trHeight, verticalScrollState = _a.verticalScrollState, viewportInsets = _a.viewportInsets;
1853
1852
  var container = containerRef.current;
1854
1853
  var activeElement = document.activeElement;
1855
1854
  if (!container ||
@@ -1883,6 +1882,7 @@ function Table(props) {
1883
1882
  frozenRowCount: frozenRowCount,
1884
1883
  columns: columns,
1885
1884
  rowHeight: trHeight,
1885
+ verticalScrollState: verticalScrollState,
1886
1886
  viewportInsets: viewportInsets,
1887
1887
  });
1888
1888
  if (result.didScroll) {
@@ -1916,7 +1916,7 @@ function Table(props) {
1916
1916
  };
1917
1917
  var handleKeyDown = function (evt) {
1918
1918
  var _a, _b, _c, _d, _e;
1919
- var _f = keyboardRuntimeRef.current, activeCell = _f.activeCell, cellInteractionSession = _f.cellInteractionSession, cellNavigationOptions = _f.cellNavigationOptions, cellSelectionEnabled = _f.cellSelectionEnabled, clearCellSelection = _f.clearCellSelection, clearCellSelectionOnEscape = _f.clearCellSelectionOnEscape, columns = _f.columns, commitCheckboxCell = _f.commitCheckboxCell, commitScroll = _f.commitScroll, copySelectedCells = _f.copySelectedCells, data = _f.data, dataLength = _f.dataLength, editable = _f.editable, editItemIndex = _f.editItemIndex, endCellSelectionDrag = _f.endCellSelectionDrag, frozenColumnIndex = _f.frozenColumnIndex, frozenRowCount = _f.frozenRowCount, height = _f.height, handleClick = _f.handleClick, moveActiveCell = _f.moveActiveCell, openCellContextMenu = _f.openCellContextMenu, selectAllCells = _f.selectAllCells, setEditItem = _f.setEditItem, requestSearchOpen = _f.requestSearchOpen, searchOpen = _f.searchOpen, searchOptions = _f.searchOptions, trHeight = _f.trHeight, viewportInsets = _f.viewportInsets;
1919
+ var _f = keyboardRuntimeRef.current, activeCell = _f.activeCell, cellInteractionSession = _f.cellInteractionSession, cellNavigationOptions = _f.cellNavigationOptions, cellSelectionEnabled = _f.cellSelectionEnabled, clearCellSelection = _f.clearCellSelection, clearCellSelectionOnEscape = _f.clearCellSelectionOnEscape, columns = _f.columns, commitCheckboxCell = _f.commitCheckboxCell, commitScroll = _f.commitScroll, copySelectedCells = _f.copySelectedCells, data = _f.data, dataLength = _f.dataLength, editable = _f.editable, editItemIndex = _f.editItemIndex, endCellSelectionDrag = _f.endCellSelectionDrag, frozenColumnIndex = _f.frozenColumnIndex, frozenRowCount = _f.frozenRowCount, height = _f.height, handleClick = _f.handleClick, moveActiveCell = _f.moveActiveCell, openCellContextMenu = _f.openCellContextMenu, selectAllCells = _f.selectAllCells, setEditItem = _f.setEditItem, requestSearchOpen = _f.requestSearchOpen, searchOpen = _f.searchOpen, searchOptions = _f.searchOptions, trHeight = _f.trHeight, verticalScrollState = _f.verticalScrollState, viewportInsets = _f.viewportInsets;
1920
1920
  var container = containerRef.current;
1921
1921
  var activeElement = document.activeElement;
1922
1922
  if (!container || (activeElement !== container && !container.contains(activeElement)))
@@ -2045,6 +2045,7 @@ function Table(props) {
2045
2045
  frozenRowCount: frozenRowCount,
2046
2046
  columns: columns,
2047
2047
  rowHeight: trHeight,
2048
+ verticalScrollState: verticalScrollState,
2048
2049
  viewportInsets: viewportInsets,
2049
2050
  });
2050
2051
  if (result.didScroll) {
@@ -2088,6 +2089,7 @@ function Table(props) {
2088
2089
  frozenRowCount: frozenRowCount,
2089
2090
  columns: columns,
2090
2091
  rowHeight: trHeight,
2092
+ verticalScrollState: verticalScrollState,
2091
2093
  viewportInsets: viewportInsets,
2092
2094
  });
2093
2095
  if (result.didScroll) {
@@ -2115,6 +2117,7 @@ function Table(props) {
2115
2117
  frozenRowCount: frozenRowCount,
2116
2118
  columns: columns,
2117
2119
  rowHeight: trHeight,
2120
+ verticalScrollState: verticalScrollState,
2118
2121
  viewportInsets: viewportInsets,
2119
2122
  });
2120
2123
  if (result.didScroll) {
@@ -2222,7 +2225,7 @@ function Table(props) {
2222
2225
  event.stopPropagation();
2223
2226
  }
2224
2227
  } },
2225
- React.createElement(ScrollPlane, { style: { height: scrollPlaneHeight, minWidth: scrollPlaneMinWidth } },
2228
+ React.createElement(ScrollPlane, { style: { height: scrollPlaneHeight, minWidth: scrollPlaneMinWidth }, "data-bgrid-virtual-scroll-window": virtualScrollWindowMetrics.enabled ? 'true' : undefined, "data-bgrid-logical-height": virtualScrollWindowMetrics.logicalContentHeight, "data-bgrid-physical-height": virtualScrollWindowMetrics.physicalContentHeight, "data-bgrid-logical-scroll-top": Math.round(scrollTop), "data-bgrid-virtual-scroll-base": virtualScrollWindowMetrics.enabled ? Math.round(virtualScrollBase) : undefined },
2226
2229
  React.createElement(HeaderContainer, { style: { height: headerHeight, top: 0 }, role: 'rfdg-header-container' },
2227
2230
  (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0) > 0 && (React.createElement(FrozenHeader, { style: { width: frozenColumnsWidth }, role: 'rfdg-frozen-header' },
2228
2231
  React.createElement(TableHeadFrozen_1.default, { container: containerRef }))),
@@ -2233,7 +2236,7 @@ function Table(props) {
2233
2236
  React.createElement(TableSummaryFronzen_1.TableSummaryFrozen, { position: 'top' }))),
2234
2237
  React.createElement(Summary, { style: { left: frozenColumnsWidth }, role: 'rfdg-summary' },
2235
2238
  React.createElement(TableSummary_1.TableSummary, { position: 'top' })))),
2236
- React.createElement("div", { className: 'bgrid-body-scroll-content', style: { height: frozenRowsHeight + visibleScrollableRows.scrollContentHeight } },
2239
+ React.createElement("div", { className: 'bgrid-body-scroll-content', style: { height: virtualScrollWindowMetrics.physicalContentHeight } },
2237
2240
  frozenRowCount > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-layer', style: { height: frozenRowsHeight, top: stickyTopHeight }, "data-bgrid-row-band": 'frozen' },
2238
2241
  (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0) > 0 && (React.createElement("div", { className: 'bgrid-frozen-rows-left bgrid-frozen-column-boundary', style: { width: frozenColumnsWidth }, role: 'rfdg-frozen-rows-left' },
2239
2242
  React.createElement(TableBodyFrozen_1.default, { scrollContainerRef: scrollContainerRef, rowRange: frozenRowRange, role: 'rfdg-body-top-frozen', quadrant: 'top-left', allowRowReorder: false }),
@@ -2241,17 +2244,19 @@ function Table(props) {
2241
2244
  React.createElement("div", { className: 'bgrid-frozen-rows-main', style: { left: frozenColumnsWidth }, role: 'rfdg-frozen-rows-main' },
2242
2245
  React.createElement(TableBody_1.default, { scrollContainerRef: scrollContainerRef, rowRange: frozenRowRange, role: 'rfdg-body-top', quadrant: 'top-main', allowRowReorder: false }),
2243
2246
  renderSelectionOverlay('top-main')))),
2244
- React.createElement("div", { className: 'bgrid-scrollable-rows-layer', style: { height: visibleScrollableRows.scrollContentHeight }, "data-bgrid-row-band": 'scrollable' },
2247
+ React.createElement("div", { className: 'bgrid-scrollable-rows-layer', style: { height: physicalScrollableRowsHeight }, "data-bgrid-row-band": 'scrollable' },
2245
2248
  (frozenColumnsWidth !== null && frozenColumnsWidth !== void 0 ? frozenColumnsWidth : 0) > 0 && (React.createElement(FrozenScrollContent, { style: {
2246
2249
  width: frozenColumnsWidth,
2247
- height: visibleScrollableRows.scrollContentHeight,
2250
+ height: physicalScrollableRowsHeight,
2248
2251
  }, role: 'rfdg-frozen-scroll-container' },
2249
2252
  React.createElement(TableBodyFrozen_1.default, { scrollContainerRef: scrollContainerRef, rowRange: scrollableRowRange, style: frozenScrollableBodyStyle, quadrant: 'body-left', onRowReorderPointerDown: rowReorderController.onPointerDown, onRowReorderKeyDown: rowReorderController.onKeyDown }),
2250
2253
  renderSelectionOverlay('body-left'))),
2251
2254
  React.createElement(ScrollContent, { style: {
2252
2255
  left: frozenColumnsWidth,
2253
- paddingTop: visibleScrollableRows.paddingTop,
2254
- height: visibleScrollableRows.scrollContentHeight,
2256
+ paddingTop: virtualScrollWindowMetrics.enabled
2257
+ ? visibleScrollableRows.paddingTop - virtualScrollBase
2258
+ : visibleScrollableRows.paddingTop,
2259
+ height: physicalScrollableRowsHeight,
2255
2260
  } },
2256
2261
  React.createElement(TableBody_1.default, { scrollContainerRef: scrollContainerRef, rowRange: scrollableRowRange, quadrant: 'body-main' }),
2257
2262
  renderSelectionOverlay('body-main')))),
@@ -2343,7 +2348,7 @@ function getCellPositionFromPointer(_a) {
2343
2348
  var rowIndex = metrics.frozenRowCount > 0 && clientY < frozenRowsBottom
2344
2349
  ? (0, coordinate_1.clamp)(Math.floor((clientY - bodyRect.top) / metrics.trHeight), 0, metrics.frozenRowCount - 1)
2345
2350
  : (0, coordinate_1.clamp)(metrics.frozenRowCount +
2346
- Math.floor((scrollContainer.scrollTop +
2351
+ Math.floor((metrics.scrollTop +
2347
2352
  (0, coordinate_1.clamp)(clientY - scrollRect.top, 0, Math.max(scrollContainer.clientHeight - 1, 0))) /
2348
2353
  metrics.trHeight), 0, metrics.dataLength - 1);
2349
2354
  var columnIndex = getColumnIndexFromPointer({
@@ -37,15 +37,15 @@ exports.CellSelectionOverlayLayer = CellSelectionOverlayLayer;
37
37
  var React = __importStar(require("react"));
38
38
  var cellSelectionGeometry_1 = require("../../utils/cellSelectionGeometry");
39
39
  function CellSelectionOverlayLayer(_a) {
40
- var quadrant = _a.quadrant, selectionFragments = _a.selectionFragments, activeFragments = _a.activeFragments, activeFill = _a.activeFill, activeRing = _a.activeRing, viewport = _a.viewport;
40
+ var quadrant = _a.quadrant, selectionFragments = _a.selectionFragments, activeFragments = _a.activeFragments, activeFill = _a.activeFill, activeRing = _a.activeRing, viewport = _a.viewport, _b = _a.offsetTop, offsetTop = _b === void 0 ? 0 : _b;
41
41
  var quadrantSelectionFragments = getVisibleFragments(selectionFragments, quadrant, viewport);
42
42
  var quadrantActiveFragments = getVisibleFragments(activeFragments, quadrant, viewport);
43
43
  if (quadrantSelectionFragments.length === 0 && quadrantActiveFragments.length === 0)
44
44
  return null;
45
45
  return (React.createElement("div", { className: 'bgrid-cell-selection-overlay-layer', "data-bgrid-selection-quadrant": quadrant, "aria-hidden": 'true' },
46
- quadrantSelectionFragments.map(function (fragment) { return (React.createElement("div", { key: "selection-".concat(fragment.rangeIndex), className: 'bgrid-cell-selection-fragment', "data-bgrid-selection-fragment": 'true', "data-bgrid-selection-range-index": fragment.rangeIndex, "data-edge-top": fragment.edges.top ? 'true' : undefined, "data-edge-right": fragment.edges.right ? 'true' : undefined, "data-edge-bottom": fragment.edges.bottom ? 'true' : undefined, "data-edge-left": fragment.edges.left ? 'true' : undefined, style: getFragmentStyle(fragment) })); }),
46
+ quadrantSelectionFragments.map(function (fragment) { return (React.createElement("div", { key: "selection-".concat(fragment.rangeIndex), className: 'bgrid-cell-selection-fragment', "data-bgrid-selection-fragment": 'true', "data-bgrid-selection-range-index": fragment.rangeIndex, "data-edge-top": fragment.edges.top ? 'true' : undefined, "data-edge-right": fragment.edges.right ? 'true' : undefined, "data-edge-bottom": fragment.edges.bottom ? 'true' : undefined, "data-edge-left": fragment.edges.left ? 'true' : undefined, style: getFragmentStyle(fragment, offsetTop) })); }),
47
47
  (activeFill || activeRing) &&
48
- quadrantActiveFragments.map(function (fragment) { return (React.createElement("div", { key: "active-".concat(fragment.rangeIndex), className: 'bgrid-cell-active-fragment', "data-bgrid-active-fragment": 'true', "data-active-fill": activeFill ? 'true' : undefined, "data-active-ring": activeRing ? 'true' : undefined, style: getFragmentStyle(fragment) })); })));
48
+ quadrantActiveFragments.map(function (fragment) { return (React.createElement("div", { key: "active-".concat(fragment.rangeIndex), className: 'bgrid-cell-active-fragment', "data-bgrid-active-fragment": 'true', "data-active-fill": activeFill ? 'true' : undefined, "data-active-ring": activeRing ? 'true' : undefined, style: getFragmentStyle(fragment, offsetTop) })); })));
49
49
  }
50
50
  function getVisibleFragments(fragments, quadrant, viewport) {
51
51
  return fragments.flatMap(function (fragment) {
@@ -55,10 +55,10 @@ function getVisibleFragments(fragments, quadrant, viewport) {
55
55
  return visibleFragment ? [visibleFragment] : [];
56
56
  });
57
57
  }
58
- function getFragmentStyle(fragment) {
58
+ function getFragmentStyle(fragment, offsetTop) {
59
59
  return {
60
60
  left: fragment.left,
61
- top: fragment.top,
61
+ top: fragment.top - offsetTop,
62
62
  width: fragment.width,
63
63
  height: fragment.height,
64
64
  };
@@ -53,10 +53,10 @@ function getRowBottom(rowIndex, rowHeight) {
53
53
  return (rowIndex + 1) * rowHeight;
54
54
  }
55
55
  function ensureCellVisible(_a) {
56
- var _b, _c, _d, _e, _f, _g;
57
- var cell = _a.cell, scrollContainer = _a.scrollContainer, _h = _a.frozenColumnCount, frozenColumnCount = _h === void 0 ? 0 : _h, _j = _a.frozenRowCount, frozenRowCount = _j === void 0 ? 0 : _j, columns = _a.columns, rowHeight = _a.rowHeight, viewportInsets = _a.viewportInsets;
58
- var currentScrollTop = (_b = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollTop) !== null && _b !== void 0 ? _b : 0;
59
- var currentScrollLeft = (_c = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollLeft) !== null && _c !== void 0 ? _c : 0;
56
+ var _b, _c, _d, _e, _f, _g, _h, _j;
57
+ var cell = _a.cell, scrollContainer = _a.scrollContainer, _k = _a.frozenColumnCount, frozenColumnCount = _k === void 0 ? 0 : _k, _l = _a.frozenRowCount, frozenRowCount = _l === void 0 ? 0 : _l, columns = _a.columns, rowHeight = _a.rowHeight, verticalScrollState = _a.verticalScrollState, viewportInsets = _a.viewportInsets;
58
+ var currentScrollTop = (_c = (_b = verticalScrollState === null || verticalScrollState === void 0 ? void 0 : verticalScrollState.scrollTop) !== null && _b !== void 0 ? _b : scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollTop) !== null && _c !== void 0 ? _c : 0;
59
+ var currentScrollLeft = (_d = scrollContainer === null || scrollContainer === void 0 ? void 0 : scrollContainer.scrollLeft) !== null && _d !== void 0 ? _d : 0;
60
60
  var nextScrollTop = currentScrollTop;
61
61
  var nextScrollLeft = currentScrollLeft;
62
62
  // Vertical scroll calculation
@@ -65,11 +65,11 @@ function ensureCellVisible(_a) {
65
65
  var rowTop = getRowTop(targetRowIndex, rowHeight);
66
66
  var rowBottom = getRowBottom(targetRowIndex, rowHeight);
67
67
  var clientHeight = scrollContainer.clientHeight;
68
- var scrollHeight = scrollContainer.scrollHeight;
68
+ var scrollHeight = (_e = verticalScrollState === null || verticalScrollState === void 0 ? void 0 : verticalScrollState.scrollHeight) !== null && _e !== void 0 ? _e : scrollContainer.scrollHeight;
69
69
  var maxScrollTop = Math.max(0, scrollHeight - clientHeight);
70
70
  if (clientHeight > 0) {
71
- var topInset = clamp((_d = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.top) !== null && _d !== void 0 ? _d : 0, 0, clientHeight);
72
- var bottomInset = clamp((_e = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.bottom) !== null && _e !== void 0 ? _e : 0, 0, Math.max(clientHeight - topInset, 0));
71
+ var topInset = clamp((_f = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.top) !== null && _f !== void 0 ? _f : 0, 0, clientHeight);
72
+ var bottomInset = clamp((_g = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.bottom) !== null && _g !== void 0 ? _g : 0, 0, Math.max(clientHeight - topInset, 0));
73
73
  var viewportTop = currentScrollTop + topInset;
74
74
  var viewportBottom = currentScrollTop + clientHeight - bottomInset;
75
75
  if (rowTop < viewportTop) {
@@ -90,8 +90,8 @@ function ensureCellVisible(_a) {
90
90
  var scrollWidth = scrollContainer.scrollWidth;
91
91
  var maxScrollLeft = Math.max(0, scrollWidth - clientWidth);
92
92
  if (clientWidth > 0) {
93
- var leftInset = clamp((_f = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.left) !== null && _f !== void 0 ? _f : 0, 0, clientWidth);
94
- var rightInset = clamp((_g = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.right) !== null && _g !== void 0 ? _g : 0, 0, Math.max(clientWidth - leftInset, 0));
93
+ var leftInset = clamp((_h = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.left) !== null && _h !== void 0 ? _h : 0, 0, clientWidth);
94
+ var rightInset = clamp((_j = viewportInsets === null || viewportInsets === void 0 ? void 0 : viewportInsets.right) !== null && _j !== void 0 ? _j : 0, 0, Math.max(clientWidth - leftInset, 0));
95
95
  var viewportLeft = currentScrollLeft + leftInset;
96
96
  var viewportRight = currentScrollLeft + clientWidth - rightInset;
97
97
  var visibleWidth = Math.max(clientWidth - leftInset - rightInset, 0);
@@ -108,7 +108,7 @@ function ensureCellVisible(_a) {
108
108
  var didScrollLeft = nextScrollLeft !== currentScrollLeft;
109
109
  var didScroll = didScrollTop || didScrollLeft;
110
110
  if (scrollContainer) {
111
- if (didScrollTop) {
111
+ if (didScrollTop && !verticalScrollState) {
112
112
  scrollContainer.scrollTop = nextScrollTop;
113
113
  }
114
114
  if (didScrollLeft) {
@@ -31,6 +31,7 @@ __exportStar(require("./scrollbar"), exports);
31
31
  __exportStar(require("./number"), exports);
32
32
  __exportStar(require("./buildHeaderMatrix"), exports);
33
33
  __exportStar(require("./getVisibleScrollableRowRange"), exports);
34
+ __exportStar(require("./virtualScrollWindow"), exports);
34
35
  __exportStar(require("./cellEditState"), exports);
35
36
  __exportStar(require("./getCellSelectionAxisState"), exports);
36
37
  __exportStar(require("./mergedCells"), exports);
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BGRID_MAX_PHYSICAL_SCROLL_HEIGHT = void 0;
4
+ exports.getVirtualScrollWindowMetrics = getVirtualScrollWindowMetrics;
5
+ exports.getLogicalScrollTop = getLogicalScrollTop;
6
+ exports.getVirtualScrollWindowPosition = getVirtualScrollWindowPosition;
7
+ exports.rebaseVirtualScrollWindow = rebaseVirtualScrollWindow;
8
+ exports.BGRID_MAX_PHYSICAL_SCROLL_HEIGHT = 1000000;
9
+ function getVirtualScrollWindowMetrics(_a) {
10
+ var logicalContentHeight = _a.logicalContentHeight, viewportHeight = _a.viewportHeight, enabled = _a.enabled, _b = _a.maxPhysicalHeight, maxPhysicalHeight = _b === void 0 ? exports.BGRID_MAX_PHYSICAL_SCROLL_HEIGHT : _b;
11
+ var safeViewportHeight = Math.max(viewportHeight, 0);
12
+ var safeLogicalContentHeight = Math.max(logicalContentHeight, safeViewportHeight);
13
+ var safeMaxPhysicalHeight = Math.max(maxPhysicalHeight, safeViewportHeight);
14
+ var windowingEnabled = enabled && safeLogicalContentHeight > safeMaxPhysicalHeight;
15
+ var physicalContentHeight = windowingEnabled ? safeMaxPhysicalHeight : safeLogicalContentHeight;
16
+ return {
17
+ enabled: windowingEnabled,
18
+ logicalContentHeight: safeLogicalContentHeight,
19
+ physicalContentHeight: physicalContentHeight,
20
+ logicalMaxScroll: Math.max(safeLogicalContentHeight - safeViewportHeight, 0),
21
+ physicalMaxScroll: Math.max(physicalContentHeight - safeViewportHeight, 0),
22
+ };
23
+ }
24
+ function getLogicalScrollTop(base, physicalScrollTop, metrics) {
25
+ return clamp(base + physicalScrollTop, 0, metrics.logicalMaxScroll);
26
+ }
27
+ function getVirtualScrollWindowPosition(logicalScrollTop, metrics) {
28
+ var logicalTop = clamp(logicalScrollTop, 0, metrics.logicalMaxScroll);
29
+ if (!metrics.enabled) {
30
+ return {
31
+ base: 0,
32
+ physicalScrollTop: logicalTop,
33
+ logicalScrollTop: logicalTop,
34
+ };
35
+ }
36
+ var preferredPhysicalTop = metrics.physicalMaxScroll / 2;
37
+ var maxBase = Math.max(metrics.logicalMaxScroll - metrics.physicalMaxScroll, 0);
38
+ var base = clamp(logicalTop - preferredPhysicalTop, 0, maxBase);
39
+ return {
40
+ base: base,
41
+ physicalScrollTop: logicalTop - base,
42
+ logicalScrollTop: logicalTop,
43
+ };
44
+ }
45
+ function rebaseVirtualScrollWindow(base, physicalScrollTop, metrics) {
46
+ var logicalScrollTop = getLogicalScrollTop(base, physicalScrollTop, metrics);
47
+ if (!metrics.enabled || metrics.physicalMaxScroll <= 0) {
48
+ return getVirtualScrollWindowPosition(logicalScrollTop, metrics);
49
+ }
50
+ var lowerGuard = metrics.physicalMaxScroll * 0.25;
51
+ var upperGuard = metrics.physicalMaxScroll * 0.75;
52
+ var maxBase = Math.max(metrics.logicalMaxScroll - metrics.physicalMaxScroll, 0);
53
+ var canMoveBackward = base > 0;
54
+ var canMoveForward = base < maxBase;
55
+ if ((physicalScrollTop < lowerGuard && canMoveBackward) ||
56
+ (physicalScrollTop > upperGuard && canMoveForward)) {
57
+ return getVirtualScrollWindowPosition(logicalScrollTop, metrics);
58
+ }
59
+ return {
60
+ base: base,
61
+ physicalScrollTop: clamp(physicalScrollTop, 0, metrics.physicalMaxScroll),
62
+ logicalScrollTop: logicalScrollTop,
63
+ };
64
+ }
65
+ function clamp(value, min, max) {
66
+ return Math.min(Math.max(value, min), max);
67
+ }