react-virtual-renderer 1.1.3 → 1.1.5

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.
Files changed (92) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +65 -3
  3. package/dist/App.js.map +1 -1
  4. package/dist/components/VirtualGrid.d.ts +30 -0
  5. package/dist/components/VirtualGrid.d.ts.map +1 -1
  6. package/dist/components/VirtualGrid.js +147 -117
  7. package/dist/components/VirtualGrid.js.map +1 -1
  8. package/dist/components/VirtualList.d.ts +0 -4
  9. package/dist/components/VirtualList.d.ts.map +1 -1
  10. package/dist/components/VirtualList.js +76 -86
  11. package/dist/components/VirtualList.js.map +1 -1
  12. package/dist/components/VirtualLiveRegion.d.ts +12 -0
  13. package/dist/components/VirtualLiveRegion.d.ts.map +1 -0
  14. package/dist/components/VirtualLiveRegion.js +27 -0
  15. package/dist/components/VirtualLiveRegion.js.map +1 -0
  16. package/dist/core/FenwickTree.d.ts +47 -0
  17. package/dist/core/FenwickTree.d.ts.map +1 -0
  18. package/dist/core/FenwickTree.js +206 -0
  19. package/dist/core/FenwickTree.js.map +1 -0
  20. package/dist/core/GridLayoutEngine.d.ts +64 -0
  21. package/dist/core/GridLayoutEngine.d.ts.map +1 -0
  22. package/dist/core/GridLayoutEngine.js +153 -0
  23. package/dist/core/GridLayoutEngine.js.map +1 -0
  24. package/dist/core/GroupedListEngine.d.ts +21 -0
  25. package/dist/core/GroupedListEngine.d.ts.map +1 -0
  26. package/dist/core/GroupedListEngine.js +22 -0
  27. package/dist/core/GroupedListEngine.js.map +1 -0
  28. package/dist/core/ListController.d.ts +31 -0
  29. package/dist/core/ListController.d.ts.map +1 -0
  30. package/dist/core/ListController.js +71 -0
  31. package/dist/core/ListController.js.map +1 -0
  32. package/dist/core/MeasurementSystem.d.ts +1 -1
  33. package/dist/core/MeasurementSystem.d.ts.map +1 -1
  34. package/dist/core/MeasurementSystem.js +22 -10
  35. package/dist/core/MeasurementSystem.js.map +1 -1
  36. package/dist/core/VirtualizationEngine.d.ts +24 -13
  37. package/dist/core/VirtualizationEngine.d.ts.map +1 -1
  38. package/dist/core/VirtualizationEngine.js +50 -87
  39. package/dist/core/VirtualizationEngine.js.map +1 -1
  40. package/dist/examples/DynamicHeightLists.d.ts +2 -0
  41. package/dist/examples/DynamicHeightLists.d.ts.map +1 -0
  42. package/dist/examples/DynamicHeightLists.js +42 -0
  43. package/dist/examples/DynamicHeightLists.js.map +1 -0
  44. package/dist/examples/FixedGrid.d.ts +2 -0
  45. package/dist/examples/FixedGrid.d.ts.map +1 -0
  46. package/dist/examples/FixedGrid.js +40 -0
  47. package/dist/examples/FixedGrid.js.map +1 -0
  48. package/dist/examples/GroupedContactList.d.ts +2 -0
  49. package/dist/examples/GroupedContactList.d.ts.map +1 -0
  50. package/dist/examples/GroupedContactList.js +20 -0
  51. package/dist/examples/GroupedContactList.js.map +1 -0
  52. package/dist/examples/InfiniteScrollLists.d.ts +2 -0
  53. package/dist/examples/InfiniteScrollLists.d.ts.map +1 -0
  54. package/dist/examples/InfiniteScrollLists.js +93 -0
  55. package/dist/examples/InfiniteScrollLists.js.map +1 -0
  56. package/dist/examples/MasonaryGrid.d.ts +2 -0
  57. package/dist/examples/MasonaryGrid.d.ts.map +1 -0
  58. package/dist/examples/MasonaryGrid.js +36 -0
  59. package/dist/examples/MasonaryGrid.js.map +1 -0
  60. package/dist/examples/PerfHarness.d.ts +2 -0
  61. package/dist/examples/PerfHarness.d.ts.map +1 -0
  62. package/dist/examples/PerfHarness.js +131 -0
  63. package/dist/examples/PerfHarness.js.map +1 -0
  64. package/dist/hooks/useBidirectionalInfiniteScroll.d.ts +32 -0
  65. package/dist/hooks/useBidirectionalInfiniteScroll.d.ts.map +1 -0
  66. package/dist/hooks/useBidirectionalInfiniteScroll.js +89 -0
  67. package/dist/hooks/useBidirectionalInfiniteScroll.js.map +1 -0
  68. package/dist/hooks/useInfiniteScroll.d.ts +36 -12
  69. package/dist/hooks/useInfiniteScroll.d.ts.map +1 -1
  70. package/dist/hooks/useInfiniteScroll.js +46 -45
  71. package/dist/hooks/useInfiniteScroll.js.map +1 -1
  72. package/dist/hooks/useRevoingTabIndex.d.ts +11 -0
  73. package/dist/hooks/useRevoingTabIndex.d.ts.map +1 -0
  74. package/dist/hooks/useRevoingTabIndex.js +28 -0
  75. package/dist/hooks/useRevoingTabIndex.js.map +1 -0
  76. package/dist/hooks/useVirtualAllyProps.d.ts +23 -0
  77. package/dist/hooks/useVirtualAllyProps.d.ts.map +1 -0
  78. package/dist/hooks/useVirtualAllyProps.js +25 -0
  79. package/dist/hooks/useVirtualAllyProps.js.map +1 -0
  80. package/dist/hooks/useVirtualList.d.ts +2 -0
  81. package/dist/hooks/useVirtualList.d.ts.map +1 -1
  82. package/dist/hooks/useVirtualList.js +12 -4
  83. package/dist/hooks/useVirtualList.js.map +1 -1
  84. package/dist/index.cjs.js +1 -1
  85. package/dist/index.cjs.js.map +1 -1
  86. package/dist/index.esm.js +1 -1
  87. package/dist/index.esm.js.map +1 -1
  88. package/dist/main.js +4 -4
  89. package/dist/main.js.map +1 -1
  90. package/dist/types.d.ts +10 -0
  91. package/dist/types.d.ts.map +1 -1
  92. package/package.json +1 -1
@@ -1,20 +1,12 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { forwardRef, useEffect, useImperativeHandle, useRef, } from 'react';
3
3
  import { useVirtualList } from '../hooks/useVirtualList';
4
- /**
5
- * VirtualList Component
6
- * Drop-in replacement for long lists with virtualization support
7
- */
8
- export const VirtualList = forwardRef(({ items, renderItem, itemWrapper, itemKey, height = 400, width = 500, itemSize, overscan = 5, scrollDirection = 'forward', onScroll, className = '', itemClassName = '', style = {}, estimatedItemSize = 35, getItemSize, stickyIndices = [], onScrollEnd, innerRef, scrollOffsetRef, }, containerRef) => {
9
- const { virtualItems, isScrolling, scrollOffset, totalSize, containerRef: internalContainerRef, innerRef: internalInnerRef, getItemStyle, } = useVirtualList({
4
+ export const VirtualList = forwardRef(({ items, renderItem, itemWrapper, itemKey, height = 400, width = 500, itemSize, overscan = 5, scrollDirection = 'forward', onScroll, className = '', itemClassName = '', style = {}, innerClassName = '', innerStyle = {}, itemStyle = {}, estimatedItemSize = 35, getItemSize, stickyIndices = [], onScrollEnd, innerRef, scrollOffsetRef, footer, }, containerRef) => {
5
+ const { virtualItems, isScrolling, scrollOffset, totalSize, containerRef: internalContainerRef, innerRef: internalInnerRef, getItemStyle, observeItem, unobserveItem, } = useVirtualList({
10
6
  items,
11
7
  getItemKey: itemKey,
12
- containerHeight: typeof height === 'number'
13
- ? height
14
- : 400,
15
- containerWidth: typeof width === 'number'
16
- ? width
17
- : 500,
8
+ containerHeight: typeof height === 'number' ? height : 400,
9
+ containerWidth: typeof width === 'number' ? width : 500,
18
10
  itemSize: getItemSize || itemSize,
19
11
  overscan,
20
12
  scrollDirection,
@@ -23,31 +15,15 @@ export const VirtualList = forwardRef(({ items, renderItem, itemWrapper, itemKey
23
15
  onScrollEnd,
24
16
  stickyIndices,
25
17
  });
26
- /**
27
- * Local refs
28
- */
29
18
  const mergedContainerRef = useRef(null);
30
19
  const mergedInnerRef = useRef(null);
31
- /**
32
- * Expose container ref
33
- */
34
20
  useImperativeHandle(containerRef, () => mergedContainerRef.current);
35
- /**
36
- * Expose inner ref
37
- */
38
21
  useImperativeHandle(innerRef, () => mergedInnerRef.current);
39
- /**
40
- * Expose current scroll offset
41
- */
42
22
  useEffect(() => {
43
23
  if (scrollOffsetRef) {
44
- scrollOffsetRef.current =
45
- scrollOffset;
24
+ scrollOffsetRef.current = scrollOffset;
46
25
  }
47
26
  }, [scrollOffset, scrollOffsetRef]);
48
- /**
49
- * Container styles
50
- */
51
27
  const containerStyle = {
52
28
  height,
53
29
  width,
@@ -55,70 +31,84 @@ export const VirtualList = forwardRef(({ items, renderItem, itemWrapper, itemKey
55
31
  position: 'relative',
56
32
  ...style,
57
33
  };
58
- /**
59
- * Inner wrapper styles
60
- */
61
- const innerStyle = {
34
+ const innerStyleComputed = {
35
+ width: '100%',
36
+ ...innerStyle,
62
37
  position: 'relative',
63
38
  height: totalSize,
64
- width: '100%',
65
39
  };
40
+ const cx = (...parts) => parts.filter(Boolean).join(' ').trim();
41
+ /**
42
+ * FIX: tracks the currently-mounted element per index, so the ref
43
+ * callback below can unobserve the OLD element when it unmounts
44
+ * (React gives you `null` on unmount, not the removed element, so
45
+ * you need to have kept a reference yourself to unobserve it -
46
+ * without this, ResizeObserver keeps a permanent reference to
47
+ * every item that's ever scrolled past = memory leak).
48
+ */
49
+ const observedElementsRef = useRef(new Map());
50
+ const measureRef = (index) => (el) => {
51
+ const prev = observedElementsRef.current.get(index);
52
+ if (prev && prev !== el) {
53
+ unobserveItem(prev);
54
+ observedElementsRef.current.delete(index);
55
+ }
56
+ if (el) {
57
+ observedElementsRef.current.set(index, el);
58
+ observeItem(index, el);
59
+ }
60
+ };
61
+ // stop observing everything on full unmount
62
+ useEffect(() => {
63
+ return () => {
64
+ observedElementsRef.current.forEach((el) => unobserveItem(el));
65
+ observedElementsRef.current.clear();
66
+ };
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
+ }, []);
66
69
  return (_jsx("div", { ref: (el) => {
67
- mergedContainerRef.current =
68
- el;
69
- internalContainerRef.current =
70
- el;
71
- }, className: `virtualize-container ${className}`, style: containerStyle, onScroll: (e) => {
70
+ mergedContainerRef.current = el;
71
+ internalContainerRef.current = el;
72
+ }, className: cx('virtualize-container', className), style: containerStyle, onScroll: (e) => {
72
73
  const target = e.target;
73
74
  const handler = renderItem?.__scrollHandler;
74
- if (handler) {
75
+ if (handler)
75
76
  handler(e);
76
- }
77
- if (onScroll) {
77
+ if (onScroll)
78
78
  onScroll(target.scrollTop, 'auto');
79
- }
80
- }, children: _jsx("div", { ref: (el) => {
81
- mergedInnerRef.current =
82
- el;
83
- internalInnerRef.current =
84
- el;
85
- }, style: innerStyle, className: "virtualize-inner", children: virtualItems.map((virtualItem) => {
86
- const item = items[virtualItem.index];
87
- const key = itemKey
88
- ? itemKey(virtualItem.index, item)
89
- : virtualItem.index;
90
- const virtualStyle = getItemStyle(virtualItem.index, virtualItem.size);
91
- const renderProps = {
92
- item,
93
- index: virtualItem.index,
94
- /**
95
- * @deprecated
96
- * Kept only for backwards compatibility.
97
- * Avoid spreading this style in renderItem.
98
- */
99
- style: virtualStyle,
100
- isScrolling,
101
- };
102
- const content = renderItem(renderProps);
103
- /**
104
- * Advanced wrapper API
105
- */
106
- if (itemWrapper) {
107
- return (_jsx(React.Fragment, { children: itemWrapper({
108
- item,
109
- index: virtualItem.index,
110
- style: virtualStyle,
111
- children: content,
112
- }) }, key));
113
- }
114
- /**
115
- * Default wrapper
116
- */
117
- return (_jsx("div", { "data-virtualize-index": virtualItem.index, className: itemClassName, style: {
118
- ...virtualStyle,
119
- boxSizing: 'border-box',
120
- }, children: content }, key));
121
- }) }) }));
79
+ }, children: _jsxs("div", { ref: (el) => {
80
+ mergedInnerRef.current = el;
81
+ internalInnerRef.current = el;
82
+ }, style: innerStyleComputed, className: cx('virtualize-inner', innerClassName), children: [virtualItems.map((virtualItem) => {
83
+ const item = items[virtualItem.index];
84
+ const key = itemKey ? itemKey(virtualItem.index, item) : virtualItem.index;
85
+ const virtualStyle = getItemStyle(virtualItem.index, virtualItem.size);
86
+ const renderProps = {
87
+ item,
88
+ index: virtualItem.index,
89
+ style: virtualStyle,
90
+ isScrolling,
91
+ };
92
+ const content = renderItem(renderProps);
93
+ if (itemWrapper) {
94
+ // Advanced API: consumer owns the wrapper DOM node, so
95
+ // THEY need to attach measureRef(index) to their root
96
+ // element for dynamic-height measurement to work here -
97
+ // we can't reach into arbitrary returned JSX to do it
98
+ // for them. Documented in types-patch.md.
99
+ return (_jsx(React.Fragment, { children: itemWrapper({
100
+ item,
101
+ index: virtualItem.index,
102
+ style: virtualStyle,
103
+ children: content,
104
+ }) }, key));
105
+ }
106
+ return (_jsx("div", { ref: measureRef(virtualItem.index), "data-virtualize-index": virtualItem.index, className: cx('virtualize-item', itemClassName), style: {
107
+ ...itemStyle,
108
+ ...virtualStyle,
109
+ boxSizing: 'border-box',
110
+ }, children: content }, key));
111
+ }), footer && (_jsx("div", { style: { position: 'absolute', top: totalSize, left: 0, width: '100%' }, children: footer }))] }) }));
122
112
  });
123
113
  VirtualList.displayName = 'VirtualList';
124
114
  //# sourceMappingURL=VirtualList.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualList.js","sourceRoot":"","sources":["../../src/components/VirtualList.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"VirtualList.js","sourceRoot":"","sources":["../../src/components/VirtualList.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EACV,UAAU,EACV,SAAS,EACT,mBAAmB,EACnB,MAAM,GACT,MAAM,OAAO,CAAC;AAEf,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAMzD,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAIjC,CACI,EACI,KAAK,EACL,UAAU,EACV,WAAW,EACX,OAAO,EACP,MAAM,GAAG,GAAG,EACZ,KAAK,GAAG,GAAG,EACX,QAAQ,EACR,QAAQ,GAAG,CAAC,EACZ,eAAe,GAAG,SAAS,EAC3B,QAAQ,EACR,SAAS,GAAG,EAAE,EACd,aAAa,GAAG,EAAE,EAClB,KAAK,GAAG,EAAE,EACV,cAAc,GAAG,EAAE,EACnB,UAAU,GAAG,EAAE,EACf,SAAS,GAAG,EAAE,EACd,iBAAiB,GAAG,EAAE,EACtB,WAAW,EACX,aAAa,GAAG,EAAE,EAClB,WAAW,EACX,QAAQ,EACR,eAAe,EACf,MAAM,GACT,EACD,YAAY,EACd,EAAE;IACA,MAAM,EACF,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,EACT,YAAY,EAAE,oBAAoB,EAClC,QAAQ,EAAE,gBAAgB,EAC1B,YAAY,EACZ,WAAW,EACX,aAAa,GAChB,GAAG,cAAc,CAAC;QACf,KAAK;QACL,UAAU,EAAE,OAAO;QACnB,eAAe,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG;QAC1D,cAAc,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG;QACvD,QAAQ,EAAE,WAAW,IAAI,QAAQ;QACjC,QAAQ;QACR,eAAe;QACf,iBAAiB;QACjB,QAAQ;QACR,WAAW;QACX,aAAa;KAChB,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IAC/D,MAAM,cAAc,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IAE3D,mBAAmB,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,OAAyB,CAAC,CAAC;IACtF,mBAAmB,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,OAAyB,CAAC,CAAC;IAE9E,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,eAAe,EAAE,CAAC;YAClB,eAAe,CAAC,OAAO,GAAG,YAAY,CAAC;QAC3C,CAAC;IACL,CAAC,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC;IAEpC,MAAM,cAAc,GAAwB;QACxC,MAAM;QACN,KAAK;QACL,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,UAAU;QACpB,GAAG,KAAK;KACX,CAAC;IAEF,MAAM,kBAAkB,GAAwB;QAC5C,KAAK,EAAE,MAAM;QACb,GAAG,UAAU;QACb,QAAQ,EAAE,UAAU;QACpB,MAAM,EAAE,SAAS;KACpB,CAAC;IAEF,MAAM,EAAE,GAAG,CAAC,GAAG,KAAwC,EAAE,EAAE,CACvD,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAE3C;;;;;;;OAOG;IACH,MAAM,mBAAmB,GAAG,MAAM,CAA2B,IAAI,GAAG,EAAE,CAAC,CAAC;IAExE,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,EAAyB,EAAE,EAAE;QAChE,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEpD,IAAI,IAAI,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,CAAC;YACpB,mBAAmB,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC9C,CAAC;QAED,IAAI,EAAE,EAAE,CAAC;YACL,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,WAAW,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC3B,CAAC;IACL,CAAC,CAAC;IAEF,4CAA4C;IAC5C,SAAS,CAAC,GAAG,EAAE;QACX,OAAO,GAAG,EAAE;YACR,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/D,mBAAmB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,CAAC,CAAC;QACF,uDAAuD;IAC3D,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACH,cACI,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE;YACR,kBAAkB,CAAC,OAAO,GAAG,EAAE,CAAC;YAChC,oBAAoB,CAAC,OAAO,GAAG,EAAE,CAAC;QACtC,CAAC,EACD,SAAS,EAAE,EAAE,CAAC,sBAAsB,EAAE,SAAS,CAAC,EAChD,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;YACZ,MAAM,MAAM,GAAG,CAAC,CAAC,MAAqB,CAAC;YACvC,MAAM,OAAO,GAAI,UAAkB,EAAE,eAAe,CAAC;YACrD,IAAI,OAAO;gBAAE,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,IAAI,QAAQ;gBAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACrD,CAAC,YAED,eACI,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE;gBACR,cAAc,CAAC,OAAO,GAAG,EAAE,CAAC;gBAC5B,gBAAgB,CAAC,OAAO,GAAG,EAAE,CAAC;YAClC,CAAC,EACD,KAAK,EAAE,kBAAkB,EACzB,SAAS,EAAE,EAAE,CAAC,kBAAkB,EAAE,cAAc,CAAC,aAEhD,YAAY,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE;oBAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;oBACtC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC;oBAC3E,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;oBAEvE,MAAM,WAAW,GAAyB;wBACtC,IAAI;wBACJ,KAAK,EAAE,WAAW,CAAC,KAAK;wBACxB,KAAK,EAAE,YAAY;wBACnB,WAAW;qBACd,CAAC;oBAEF,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;oBAExC,IAAI,WAAW,EAAE,CAAC;wBACd,uDAAuD;wBACvD,sDAAsD;wBACtD,wDAAwD;wBACxD,sDAAsD;wBACtD,0CAA0C;wBAC1C,OAAO,CACH,KAAC,KAAK,CAAC,QAAQ,cACV,WAAW,CAAC;gCACT,IAAI;gCACJ,KAAK,EAAE,WAAW,CAAC,KAAK;gCACxB,KAAK,EAAE,YAAY;gCACnB,QAAQ,EAAE,OAAO;6BACpB,CAAC,IANe,GAAG,CAOP,CACpB,CAAC;oBACN,CAAC;oBAED,OAAO,CACH,cAEI,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,2BACX,WAAW,CAAC,KAAK,EACxC,SAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,aAAa,CAAC,EAC/C,KAAK,EAAE;4BACH,GAAG,SAAS;4BACZ,GAAG,YAAY;4BACf,SAAS,EAAE,YAAY;yBAC1B,YAEA,OAAO,IAVH,GAAG,CAWN,CACT,CAAC;gBACN,CAAC,CAAC,EAED,MAAM,IAAI,CACP,cAAK,KAAK,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,YACvE,MAAM,GACL,CACT,IACC,GACJ,CACT,CAAC;AACN,CAAC,CACJ,CAAC;AAEF,WAAW,CAAC,WAAW,GAAG,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Visually-hidden aria-live region that announces the visible range
3
+ * once scrolling settles (debounced), so screen reader users get
4
+ * "showing items 240 to 260 of 50,000" instead of silence.
5
+ */
6
+ export declare function VirtualLiveRegion({ start, end, total, isScrolling }: {
7
+ start: number;
8
+ end: number;
9
+ total: number;
10
+ isScrolling: boolean;
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ //# sourceMappingURL=VirtualLiveRegion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualLiveRegion.d.ts","sourceRoot":"","sources":["../../src/components/VirtualLiveRegion.tsx"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE;IAClE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;CACnE,2CAyBA"}
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ /**
4
+ * Visually-hidden aria-live region that announces the visible range
5
+ * once scrolling settles (debounced), so screen reader users get
6
+ * "showing items 240 to 260 of 50,000" instead of silence.
7
+ */
8
+ export function VirtualLiveRegion({ start, end, total, isScrolling }) {
9
+ const [announcement, setAnnouncement] = useState('');
10
+ const timeoutRef = useRef(null);
11
+ useEffect(() => {
12
+ if (isScrolling)
13
+ return; // only announce once scrolling has settled
14
+ if (timeoutRef.current)
15
+ clearTimeout(timeoutRef.current);
16
+ timeoutRef.current = setTimeout(() => {
17
+ setAnnouncement(`Showing items ${start + 1} to ${end + 1} of ${total}`);
18
+ }, 400);
19
+ return () => { if (timeoutRef.current)
20
+ clearTimeout(timeoutRef.current); };
21
+ }, [start, end, total, isScrolling]);
22
+ return (_jsx("div", { "aria-live": "polite", "aria-atomic": "true", style: {
23
+ position: 'absolute', width: 1, height: 1, padding: 0, margin: -1,
24
+ overflow: 'hidden', clip: 'rect(0,0,0,0)', whiteSpace: 'nowrap', border: 0,
25
+ }, children: announcement }));
26
+ }
27
+ //# sourceMappingURL=VirtualLiveRegion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VirtualLiveRegion.js","sourceRoot":"","sources":["../../src/components/VirtualLiveRegion.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAEjE;IACG,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IAEtE,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,WAAW;YAAE,OAAO,CAAC,2CAA2C;QACpE,IAAI,UAAU,CAAC,OAAO;YAAE,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACzD,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACjC,eAAe,CAAC,iBAAiB,KAAK,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QAC5E,CAAC,EAAE,GAAG,CAAC,CAAC;QACR,OAAO,GAAG,EAAE,GAAG,IAAI,UAAU,CAAC,OAAO;YAAE,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAErC,OAAO,CACH,2BACc,QAAQ,iBACN,MAAM,EAClB,KAAK,EAAE;YACH,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;YACjE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;SAC7E,YAEA,YAAY,GACX,CACT,CAAC;AACN,CAAC"}
@@ -0,0 +1,47 @@
1
+ export declare class FenwickTree {
2
+ private capacity;
3
+ private tree;
4
+ private raw;
5
+ private n;
6
+ constructor(initialCount?: number);
7
+ size(): number;
8
+ /**
9
+ * Full linear-time rebuild from `raw[]`, the source of truth. Only
10
+ * called when capacity changes (doubling) - never on the per-item
11
+ * update hot path.
12
+ */
13
+ private rebuild;
14
+ /**
15
+ * Grow or shrink to hold `count` items. New slots default to
16
+ * `defaultValue` (the estimated item size). Shrinking zeroes the
17
+ * vacated raw slots so a later regrow never resurfaces stale data.
18
+ */
19
+ resize(count: number, defaultValue: number): void;
20
+ /**
21
+ * Point update: set the value at `index` (0-indexed) to `value`.
22
+ * O(log n) - safe at any time because it never needs to propagate
23
+ * beyond the current (fixed until the next doubling) capacity.
24
+ */
25
+ update(index: number, value: number): void;
26
+ /** Value currently stored at `index` (0-indexed). O(1). */
27
+ getValue(index: number): number;
28
+ /** Sum of values in [0, index) - the pixel offset at which `index` starts. O(log n). */
29
+ prefixSum(index: number): number;
30
+ /** Sum of all n values - the total content size. O(log n). */
31
+ total(): number;
32
+ /**
33
+ * Locate the index whose range contains cumulative offset `target`.
34
+ * Runs on every scroll frame - an O(log n) walk directly over the
35
+ * tree structure (the classic Fenwick "find by prefix sum" walk),
36
+ * so it never needs a separately-maintained sorted array.
37
+ */
38
+ findIndexAtOffset(target: number): number;
39
+ /**
40
+ * Reset every value to `defaultValue` without changing capacity.
41
+ * Intentionally the one O(n)-ish path in this class (a full rebuild
42
+ * plus n point updates), reserved for explicit full invalidation -
43
+ * never used on the scroll hot path.
44
+ */
45
+ resetAll(defaultValue: number): void;
46
+ }
47
+ //# sourceMappingURL=FenwickTree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FenwickTree.d.ts","sourceRoot":"","sources":["../../src/core/FenwickTree.ts"],"names":[],"mappings":"AAqDA,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAW;IACvB,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,CAAC,CAAK;gBAEF,YAAY,SAAI;IAU5B,IAAI,IAAI,MAAM;IAId;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAYf;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAgCjD;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAY1C,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI/B,wFAAwF;IACxF,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAQhC,8DAA8D;IAC9D,KAAK,IAAI,MAAM;IAIf;;;;;OAKG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAiBzC;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;CASvC"}
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Fenwick Tree (Binary Indexed Tree) specialized for virtualization
3
+ * offset math.
4
+ *
5
+ * WHY THIS EXISTS:
6
+ * The previous VirtualizationEngine kept a plain prefix-sum array and
7
+ * rebuilt it in FULL - O(n) - every time a single item's measured size
8
+ * changed. For a dynamic-height list, that's not an edge case, it's the
9
+ * hot path: every batch of newly-measured rows during scroll paid an
10
+ * O(n) cost. That's the exact "gets slower as the list grows" trap
11
+ * virtualization exists to avoid.
12
+ *
13
+ * CORRECTNESS NOTE (read this before changing the growth logic):
14
+ * An earlier version of this class grew the tree by simply extending
15
+ * the array and point-updating only the newly added indices. That is
16
+ * WRONG for a Fenwick tree: a point update only propagates up to
17
+ * whatever `n` (capacity) was AT THE TIME of the update. If the tree
18
+ * later grows past that bound, ancestor nodes that only come into
19
+ * existence at the larger size never receive the earlier elements'
20
+ * contributions, and totals silently undercount. This was caught by
21
+ * a differential test against a naive reference array (see the repo's
22
+ * verify/fenwick_v2.test.mjs) - total() returned 3 instead of 8 in a
23
+ * minimal repro.
24
+ *
25
+ * The fix used here is the standard one: capacity is always a power
26
+ * of two, `raw[]` is kept as the single source of truth, and growth
27
+ * beyond current capacity triggers a full O(capacity) rebuild from
28
+ * raw[] (not from partial tree state). Because capacity doubles each
29
+ * time, the total rebuild work across the tree's lifetime is a
30
+ * geometric series - amortized O(1) extra cost per element, the same
31
+ * argument as dynamic array doubling. Growth WITHIN current capacity,
32
+ * and ordinary re-measurement of existing items, stay O(log n) point
33
+ * updates with no rebuild at all.
34
+ *
35
+ * Complexity summary for a tree of n items:
36
+ * - re-measure one item: O(log n) (O(capacity) amortized O(1) on the rare doubling frame)
37
+ * - cumulative offset of any index: O(log n)
38
+ * - total content size: O(log n)
39
+ * - locate the item under a scroll offset (every scroll frame): O(log n)
40
+ * - append k new items: O(k log n), amortized
41
+ *
42
+ * All indices in the public API are 0-indexed.
43
+ */
44
+ function lowbit(i) {
45
+ return i & -i;
46
+ }
47
+ function nextPow2(x) {
48
+ let p = 1;
49
+ while (p < x)
50
+ p <<= 1;
51
+ return p;
52
+ }
53
+ export class FenwickTree {
54
+ constructor(initialCount = 0) {
55
+ Object.defineProperty(this, "capacity", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: void 0
60
+ });
61
+ Object.defineProperty(this, "tree", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: void 0
66
+ });
67
+ Object.defineProperty(this, "raw", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: void 0
72
+ });
73
+ Object.defineProperty(this, "n", {
74
+ enumerable: true,
75
+ configurable: true,
76
+ writable: true,
77
+ value: 0
78
+ });
79
+ this.capacity = nextPow2(Math.max(1, initialCount));
80
+ this.tree = new Array(this.capacity + 1).fill(0);
81
+ this.raw = new Array(this.capacity).fill(0);
82
+ if (initialCount > 0) {
83
+ this.resize(initialCount, 0);
84
+ }
85
+ }
86
+ size() {
87
+ return this.n;
88
+ }
89
+ /**
90
+ * Full linear-time rebuild from `raw[]`, the source of truth. Only
91
+ * called when capacity changes (doubling) - never on the per-item
92
+ * update hot path.
93
+ */
94
+ rebuild() {
95
+ this.tree = new Array(this.capacity + 1).fill(0);
96
+ for (let i = 1; i <= this.capacity; i++) {
97
+ this.tree[i] += this.raw[i - 1] ?? 0;
98
+ const parent = i + lowbit(i);
99
+ if (parent <= this.capacity) {
100
+ this.tree[parent] += this.tree[i];
101
+ }
102
+ }
103
+ }
104
+ /**
105
+ * Grow or shrink to hold `count` items. New slots default to
106
+ * `defaultValue` (the estimated item size). Shrinking zeroes the
107
+ * vacated raw slots so a later regrow never resurfaces stale data.
108
+ */
109
+ resize(count, defaultValue) {
110
+ if (count > this.capacity) {
111
+ const newCapacity = nextPow2(count);
112
+ const oldRaw = this.raw;
113
+ this.raw = new Array(newCapacity).fill(0);
114
+ for (let i = 0; i < oldRaw.length; i++) {
115
+ this.raw[i] = oldRaw[i];
116
+ }
117
+ for (let i = this.n; i < count; i++) {
118
+ this.raw[i] = defaultValue;
119
+ }
120
+ this.capacity = newCapacity;
121
+ this.rebuild();
122
+ this.n = count;
123
+ return;
124
+ }
125
+ if (count > this.n) {
126
+ for (let i = this.n; i < count; i++) {
127
+ this.update(i, defaultValue);
128
+ }
129
+ this.n = count;
130
+ }
131
+ else if (count < this.n) {
132
+ for (let i = count; i < this.n; i++) {
133
+ this.update(i, 0);
134
+ }
135
+ this.n = count;
136
+ }
137
+ }
138
+ /**
139
+ * Point update: set the value at `index` (0-indexed) to `value`.
140
+ * O(log n) - safe at any time because it never needs to propagate
141
+ * beyond the current (fixed until the next doubling) capacity.
142
+ */
143
+ update(index, value) {
144
+ if (index < 0 || index >= this.capacity)
145
+ return;
146
+ const delta = value - (this.raw[index] ?? 0);
147
+ if (delta === 0)
148
+ return;
149
+ this.raw[index] = value;
150
+ for (let i = index + 1; i <= this.capacity; i += lowbit(i)) {
151
+ this.tree[i] += delta;
152
+ }
153
+ }
154
+ /** Value currently stored at `index` (0-indexed). O(1). */
155
+ getValue(index) {
156
+ return this.raw[index] ?? 0;
157
+ }
158
+ /** Sum of values in [0, index) - the pixel offset at which `index` starts. O(log n). */
159
+ prefixSum(index) {
160
+ let sum = 0;
161
+ for (let i = Math.min(index, this.n); i > 0; i -= lowbit(i)) {
162
+ sum += this.tree[i];
163
+ }
164
+ return sum;
165
+ }
166
+ /** Sum of all n values - the total content size. O(log n). */
167
+ total() {
168
+ return this.prefixSum(this.n);
169
+ }
170
+ /**
171
+ * Locate the index whose range contains cumulative offset `target`.
172
+ * Runs on every scroll frame - an O(log n) walk directly over the
173
+ * tree structure (the classic Fenwick "find by prefix sum" walk),
174
+ * so it never needs a separately-maintained sorted array.
175
+ */
176
+ findIndexAtOffset(target) {
177
+ if (this.n === 0)
178
+ return 0;
179
+ let pos = 0;
180
+ let remaining = target;
181
+ for (let pw = this.capacity; pw > 0; pw >>= 1) {
182
+ const next = pos + pw;
183
+ if (next <= this.capacity && this.tree[next] <= remaining) {
184
+ pos = next;
185
+ remaining -= this.tree[next];
186
+ }
187
+ }
188
+ return Math.min(pos, this.n - 1);
189
+ }
190
+ /**
191
+ * Reset every value to `defaultValue` without changing capacity.
192
+ * Intentionally the one O(n)-ish path in this class (a full rebuild
193
+ * plus n point updates), reserved for explicit full invalidation -
194
+ * never used on the scroll hot path.
195
+ */
196
+ resetAll(defaultValue) {
197
+ for (let i = 0; i < this.n; i++) {
198
+ this.raw[i] = 0;
199
+ }
200
+ this.rebuild();
201
+ for (let i = 0; i < this.n; i++) {
202
+ this.update(i, defaultValue);
203
+ }
204
+ }
205
+ }
206
+ //# sourceMappingURL=FenwickTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FenwickTree.js","sourceRoot":"","sources":["../../src/core/FenwickTree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { GridPosition } from '../types';
2
+ export type RowHeightInput = number | ((index: number) => number);
3
+ export interface GridLayoutParams {
4
+ cols: number;
5
+ colWidth: number;
6
+ gap: number;
7
+ layout: 'grid' | 'masonry';
8
+ rowHeight: RowHeightInput;
9
+ }
10
+ /**
11
+ * Incremental, append-only masonry/grid position engine.
12
+ *
13
+ * PERFORMANCE: the previous VirtualGrid recomputed EVERY item's
14
+ * position from scratch (a full O(n) pass) whenever the items array
15
+ * changed identity - including the ordinary infinite-scroll case of
16
+ * appending 20 new items to an existing 50,000-item masonry board,
17
+ * which turned a single "load more" into an O(n) pass over everything
18
+ * already laid out.
19
+ *
20
+ * This engine keeps running state - per-column heights for masonry, a
21
+ * running row-offset for dynamic-height grid rows - and only computes
22
+ * positions for items it hasn't seen before. Appending k new items
23
+ * costs O(k), never O(n + k). A full reset (columns/width/gap/layout
24
+ * changed) is the only path that pays an O(n) cost, same as any other
25
+ * virtualization library when the layout itself is invalidated.
26
+ *
27
+ * NOTE ON VIRTUAL_GRID'S FAST PATH: this engine is only used for
28
+ * masonry layout, or grid layout with a per-index rowHeight function.
29
+ * Plain grid layout with a fixed numeric rowHeight never touches this
30
+ * file at all - VirtualGrid computes those positions with direct O(1)
31
+ * arithmetic and no array (see VirtualGrid.tsx), which is faster still.
32
+ */
33
+ export declare class GridLayoutEngine {
34
+ private positions;
35
+ private colHeights;
36
+ private rowOffsets;
37
+ private currentRowMax;
38
+ private maxBottom;
39
+ private params;
40
+ private sameParams;
41
+ private reset;
42
+ private sizeOf;
43
+ /** Masonry: place each new item in whichever column is currently shortest. */
44
+ private appendMasonry;
45
+ /**
46
+ * Grid with per-index rowHeight function: rows advance by the
47
+ * tallest item seen in that row so far. `rowOffsets[r]` is only
48
+ * ever written once (when row r's first item is appended), reading
49
+ * `currentRowMax` accumulated while row r-1 was being built - so
50
+ * this stays correct across multiple incremental append() calls,
51
+ * not just a single full pass.
52
+ */
53
+ private appendGrid;
54
+ /**
55
+ * Ensure positions exist for `itemCount` items under the given
56
+ * params. Existing entries are reused by reference - callers get
57
+ * back the same array instances for indices they've already seen,
58
+ * which keeps this cheap to call on every render.
59
+ */
60
+ sync(itemCount: number, p: GridLayoutParams): GridPosition[];
61
+ /** O(1) - tracked incrementally as items are appended, never rescanned. */
62
+ getTotalHeight(): number;
63
+ }
64
+ //# sourceMappingURL=GridLayoutEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GridLayoutEngine.d.ts","sourceRoot":"","sources":["../../src/core/GridLayoutEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,EAAE,cAAc,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,gBAAgB;IACzB,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,MAAM,CAAiC;IAE/C,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,MAAM;IAId,8EAA8E;IAC9E,OAAO,CAAC,aAAa;IAmBrB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAyBlB;;;;;OAKG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,gBAAgB,GAAG,YAAY,EAAE;IAiB5D,2EAA2E;IAC3E,cAAc,IAAI,MAAM;CAG3B"}