Skip to content

Commit 997d441

Browse files
committed
hooks now returns itemSlice, infinity loader example
1 parent 732b6a0 commit 997d441

File tree

6 files changed

+134
-52
lines changed

6 files changed

+134
-52
lines changed

build_examples/demo.js

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ const fixed_grid_1 = require("../fixed-grid");
3737
const itemsLine = Array.from({ length: 300 }).map((_, i) => `item ${i}`);
3838
const itemsGrid = Array.from({ length: 300 }).map((_, iy) => Array.from({ length: 300 }).map((_, ix) => `item ${ix} ${iy}`));
3939
function ListWithHookExample() {
40-
const items = itemsLine;
40+
const [items, setItems] = (0, react_1.useState)([]);
4141
const containerRef = (0, react_1.useRef)(undefined);
4242
const infoRef = (0, react_1.useRef)(undefined);
4343
const itemHeight = 40;
44-
const { renderedItems, updateViewRect } = (0, __1.useVirtualOverflowY)({
44+
const { renderedItems, updateViewRect, itemSlice } = (0, __1.useVirtualOverflowY)({
4545
containerRef,
4646
itemHeight,
4747
itemsLengthY: items.length,
@@ -57,6 +57,12 @@ function ListWithHookExample() {
5757
infoRef.current.innerText = `Visible rect of content:\n\n${JSON.stringify(visibleRect, null, 2)}`;
5858
}, 24);
5959
}, []);
60+
(0, react_1.useEffect)(() => {
61+
if (itemSlice.topStartIndex + itemSlice.lengthY >= items.length - 4) {
62+
// load more
63+
setItems(prev => [...prev, ...itemsLine]);
64+
}
65+
}, [itemSlice.topStartIndex, itemSlice.lengthY]);
6066
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { ref: infoRef, style: { position: 'fixed', top: 0, right: 0, paddingRight: '40px', width: '200px' } }), (0, jsx_runtime_1.jsx)("div", { style: { overflowY: 'scroll', height: '300px', background: 'lightgreen' }, children: (0, jsx_runtime_1.jsx)("div", { ref: containerRef, style: { position: 'relative', height: `${itemHeight * items.length}px` }, children: renderedItems }) })] }));
6167
}
6268
function VerticalListExample() {
@@ -80,7 +86,7 @@ const rootElement = document.getElementById("demo");
8086
const root = client_1.default.createRoot(rootElement);
8187
root.render((0, jsx_runtime_1.jsx)(react_1.default.StrictMode, { children: (0, jsx_runtime_1.jsx)(App, {}) }));
8288

83-
},{"..":4,"../fixed-grid":2,"../fixed-list-y":3,"../utils":5,"react":15,"react-dom/client":9,"react/jsx-runtime":16}],2:[function(require,module,exports){
89+
},{"..":4,"../fixed-grid":2,"../fixed-list-y":3,"../utils":6,"react":16,"react-dom/client":10,"react/jsx-runtime":17}],2:[function(require,module,exports){
8490
"use strict";
8591
Object.defineProperty(exports, "__esModule", { value: true });
8692
exports.VirtualGrid = void 0;
@@ -108,7 +114,7 @@ function VirtualGrid(props) {
108114
}
109115
exports.VirtualGrid = VirtualGrid;
110116

111-
},{".":4,"react":15,"react/jsx-runtime":16}],3:[function(require,module,exports){
117+
},{".":4,"react":16,"react/jsx-runtime":17}],3:[function(require,module,exports){
112118
"use strict";
113119
Object.defineProperty(exports, "__esModule", { value: true });
114120
exports.VirtualListY = void 0;
@@ -134,21 +140,12 @@ function VirtualListY(props) {
134140
}
135141
exports.VirtualListY = VirtualListY;
136142

137-
},{".":4,"react":15,"react/jsx-runtime":16}],4:[function(require,module,exports){
143+
},{".":4,"react":16,"react/jsx-runtime":17}],4:[function(require,module,exports){
138144
"use strict";
139145
Object.defineProperty(exports, "__esModule", { value: true });
140146
exports.useVirtualOverflowGrid = exports.useVirtualOverflowX = exports.useVirtualOverflowY = exports.useCalcVirtualOverflow = exports.virtualOverflowCalcItems = exports.virtualOverflowCalcVisibleRect = void 0;
141147
const react_1 = require("react");
142-
function debounceAnimationFrame(func) {
143-
let frameRequest = 0;
144-
return {
145-
requestFrame: () => {
146-
cancelAnimationFrame(frameRequest);
147-
frameRequest = requestAnimationFrame((frameTime) => func.call(undefined, frameTime));
148-
},
149-
cancelFrame: () => cancelAnimationFrame(frameRequest)
150-
};
151-
}
148+
const small_utils_1 = require("./small-utils");
152149
function virtualOverflowCalcVisibleRect(element) {
153150
const elementRect = element.getBoundingClientRect();
154151
const visibleRect = {
@@ -204,7 +201,7 @@ function useCalcVirtualOverflow(params, deps) {
204201
leftStartIndex: 0,
205202
lengthX: 0,
206203
});
207-
const { requestFrame: updateViewRect, cancelFrame } = (0, react_1.useMemo)(() => debounceAnimationFrame((frameTime) => {
204+
const { requestFrame: updateViewRect, cancelFrame } = (0, react_1.useMemo)(() => (0, small_utils_1.rvoDebounceAnimationFrame)((frameTime) => {
208205
if (!containerRef.current)
209206
return;
210207
const visibleRect = calcVisibleRect(containerRef.current, frameTime);
@@ -254,6 +251,7 @@ function useVirtualOverflowY(params, deps = []) {
254251
return {
255252
renderedItems: utilRenderItems1D(itemSlice.topStartIndex, itemSlice.lengthY, params.itemHeight, params.renderItem),
256253
updateViewRect,
254+
itemSlice,
257255
};
258256
}
259257
exports.useVirtualOverflowY = useVirtualOverflowY;
@@ -262,6 +260,7 @@ function useVirtualOverflowX(params, deps = []) {
262260
return {
263261
renderedItems: utilRenderItems1D(itemSlice.leftStartIndex, itemSlice.lengthX, params.itemWidth, params.renderItem),
264262
updateViewRect,
263+
itemSlice,
265264
};
266265
}
267266
exports.useVirtualOverflowX = useVirtualOverflowX;
@@ -277,12 +276,29 @@ function useVirtualOverflowGrid(params, deps = []) {
277276
}
278277
return {
279278
renderedItems,
280-
updateViewRect
279+
updateViewRect,
280+
itemSlice
281281
};
282282
}
283283
exports.useVirtualOverflowGrid = useVirtualOverflowGrid;
284284

285-
},{"react":15}],5:[function(require,module,exports){
285+
},{"./small-utils":5,"react":16}],5:[function(require,module,exports){
286+
"use strict";
287+
Object.defineProperty(exports, "__esModule", { value: true });
288+
exports.rvoDebounceAnimationFrame = void 0;
289+
function rvoDebounceAnimationFrame(func) {
290+
let frameRequest = 0;
291+
return {
292+
requestFrame: () => {
293+
cancelAnimationFrame(frameRequest);
294+
frameRequest = requestAnimationFrame((frameTime) => func.call(undefined, frameTime));
295+
},
296+
cancelFrame: () => cancelAnimationFrame(frameRequest),
297+
};
298+
}
299+
exports.rvoDebounceAnimationFrame = rvoDebounceAnimationFrame;
300+
301+
},{}],6:[function(require,module,exports){
286302
"use strict";
287303
Object.defineProperty(exports, "__esModule", { value: true });
288304
exports.virtualOverflowUtils = void 0;
@@ -371,7 +387,7 @@ var virtualOverflowUtils;
371387
virtualOverflowUtils.calcVisibleRectOverflowed = calcVisibleRectOverflowed;
372388
})(virtualOverflowUtils || (exports.virtualOverflowUtils = virtualOverflowUtils = {}));
373389

374-
},{}],6:[function(require,module,exports){
390+
},{}],7:[function(require,module,exports){
375391
// shim for using process in browser
376392
var process = module.exports = {};
377393

@@ -557,7 +573,7 @@ process.chdir = function (dir) {
557573
};
558574
process.umask = function() { return 0; };
559575

560-
},{}],7:[function(require,module,exports){
576+
},{}],8:[function(require,module,exports){
561577
(function (process){(function (){
562578
/**
563579
* @license React
@@ -30429,7 +30445,7 @@ if (
3042930445
}
3043030446

3043130447
}).call(this)}).call(this,require('_process'))
30432-
},{"_process":6,"react":15,"scheduler":19}],8:[function(require,module,exports){
30448+
},{"_process":7,"react":16,"scheduler":20}],9:[function(require,module,exports){
3043330449
/**
3043430450
* @license React
3043530451
* react-dom.production.min.js
@@ -30754,7 +30770,7 @@ exports.hydrateRoot=function(a,b,c){if(!ol(a))throw Error(p(405));var d=null!=c&
3075430770
e);return new nl(b)};exports.render=function(a,b,c){if(!pl(b))throw Error(p(200));return sl(null,a,b,!1,c)};exports.unmountComponentAtNode=function(a){if(!pl(a))throw Error(p(40));return a._reactRootContainer?(Sk(function(){sl(null,null,a,!1,function(){a._reactRootContainer=null;a[uf]=null})}),!0):!1};exports.unstable_batchedUpdates=Rk;
3075530771
exports.unstable_renderSubtreeIntoContainer=function(a,b,c,d){if(!pl(c))throw Error(p(200));if(null==a||void 0===a._reactInternals)throw Error(p(38));return sl(a,b,c,!1,d)};exports.version="18.2.0-next-9e3b772b8-20220608";
3075630772

30757-
},{"react":15,"scheduler":19}],9:[function(require,module,exports){
30773+
},{"react":16,"scheduler":20}],10:[function(require,module,exports){
3075830774
(function (process){(function (){
3075930775
'use strict';
3076030776

@@ -30783,7 +30799,7 @@ if (process.env.NODE_ENV === 'production') {
3078330799
}
3078430800

3078530801
}).call(this)}).call(this,require('_process'))
30786-
},{"_process":6,"react-dom":10}],10:[function(require,module,exports){
30802+
},{"_process":7,"react-dom":11}],11:[function(require,module,exports){
3078730803
(function (process){(function (){
3078830804
'use strict';
3078930805

@@ -30825,7 +30841,7 @@ if (process.env.NODE_ENV === 'production') {
3082530841
}
3082630842

3082730843
}).call(this)}).call(this,require('_process'))
30828-
},{"./cjs/react-dom.development.js":7,"./cjs/react-dom.production.min.js":8,"_process":6}],11:[function(require,module,exports){
30844+
},{"./cjs/react-dom.development.js":8,"./cjs/react-dom.production.min.js":9,"_process":7}],12:[function(require,module,exports){
3082930845
(function (process){(function (){
3083030846
/**
3083130847
* @license React
@@ -32143,7 +32159,7 @@ exports.jsxs = jsxs;
3214332159
}
3214432160

3214532161
}).call(this)}).call(this,require('_process'))
32146-
},{"_process":6,"react":15}],12:[function(require,module,exports){
32162+
},{"_process":7,"react":16}],13:[function(require,module,exports){
3214732163
/**
3214832164
* @license React
3214932165
* react-jsx-runtime.production.min.js
@@ -32156,7 +32172,7 @@ exports.jsxs = jsxs;
3215632172
'use strict';var f=require("react"),k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
3215732173
function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return{$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}exports.Fragment=l;exports.jsx=q;exports.jsxs=q;
3215832174

32159-
},{"react":15}],13:[function(require,module,exports){
32175+
},{"react":16}],14:[function(require,module,exports){
3216032176
(function (process){(function (){
3216132177
/**
3216232178
* @license React
@@ -34899,7 +34915,7 @@ if (
3489934915
}
3490034916

3490134917
}).call(this)}).call(this,require('_process'))
34902-
},{"_process":6}],14:[function(require,module,exports){
34918+
},{"_process":7}],15:[function(require,module,exports){
3490334919
/**
3490434920
* @license React
3490534921
* react.production.min.js
@@ -34927,7 +34943,7 @@ exports.useCallback=function(a,b){return U.current.useCallback(a,b)};exports.use
3492734943
exports.useInsertionEffect=function(a,b){return U.current.useInsertionEffect(a,b)};exports.useLayoutEffect=function(a,b){return U.current.useLayoutEffect(a,b)};exports.useMemo=function(a,b){return U.current.useMemo(a,b)};exports.useReducer=function(a,b,e){return U.current.useReducer(a,b,e)};exports.useRef=function(a){return U.current.useRef(a)};exports.useState=function(a){return U.current.useState(a)};exports.useSyncExternalStore=function(a,b,e){return U.current.useSyncExternalStore(a,b,e)};
3492834944
exports.useTransition=function(){return U.current.useTransition()};exports.version="18.2.0";
3492934945

34930-
},{}],15:[function(require,module,exports){
34946+
},{}],16:[function(require,module,exports){
3493134947
(function (process){(function (){
3493234948
'use strict';
3493334949

@@ -34938,7 +34954,7 @@ if (process.env.NODE_ENV === 'production') {
3493834954
}
3493934955

3494034956
}).call(this)}).call(this,require('_process'))
34941-
},{"./cjs/react.development.js":13,"./cjs/react.production.min.js":14,"_process":6}],16:[function(require,module,exports){
34957+
},{"./cjs/react.development.js":14,"./cjs/react.production.min.js":15,"_process":7}],17:[function(require,module,exports){
3494234958
(function (process){(function (){
3494334959
'use strict';
3494434960

@@ -34949,7 +34965,7 @@ if (process.env.NODE_ENV === 'production') {
3494934965
}
3495034966

3495134967
}).call(this)}).call(this,require('_process'))
34952-
},{"./cjs/react-jsx-runtime.development.js":11,"./cjs/react-jsx-runtime.production.min.js":12,"_process":6}],17:[function(require,module,exports){
34968+
},{"./cjs/react-jsx-runtime.development.js":12,"./cjs/react-jsx-runtime.production.min.js":13,"_process":7}],18:[function(require,module,exports){
3495334969
(function (process,setImmediate){(function (){
3495434970
/**
3495534971
* @license React
@@ -35587,7 +35603,7 @@ if (
3558735603
}
3558835604

3558935605
}).call(this)}).call(this,require('_process'),require("timers").setImmediate)
35590-
},{"_process":6,"timers":20}],18:[function(require,module,exports){
35606+
},{"_process":7,"timers":21}],19:[function(require,module,exports){
3559135607
(function (setImmediate){(function (){
3559235608
/**
3559335609
* @license React
@@ -35610,7 +35626,7 @@ exports.unstable_scheduleCallback=function(a,b,c){var d=exports.unstable_now();"
3561035626
exports.unstable_shouldYield=M;exports.unstable_wrapCallback=function(a){var b=y;return function(){var c=y;y=b;try{return a.apply(this,arguments)}finally{y=c}}};
3561135627

3561235628
}).call(this)}).call(this,require("timers").setImmediate)
35613-
},{"timers":20}],19:[function(require,module,exports){
35629+
},{"timers":21}],20:[function(require,module,exports){
3561435630
(function (process){(function (){
3561535631
'use strict';
3561635632

@@ -35621,7 +35637,7 @@ if (process.env.NODE_ENV === 'production') {
3562135637
}
3562235638

3562335639
}).call(this)}).call(this,require('_process'))
35624-
},{"./cjs/scheduler.development.js":17,"./cjs/scheduler.production.min.js":18,"_process":6}],20:[function(require,module,exports){
35640+
},{"./cjs/scheduler.development.js":18,"./cjs/scheduler.production.min.js":19,"_process":7}],21:[function(require,module,exports){
3562535641
(function (setImmediate,clearImmediate){(function (){
3562635642
var nextTick = require('process/browser.js').nextTick;
3562735643
var apply = Function.prototype.apply;
@@ -35700,4 +35716,4 @@ exports.clearImmediate = typeof clearImmediate === "function" ? clearImmediate :
3570035716
delete immediateIds[id];
3570135717
};
3570235718
}).call(this)}).call(this,require("timers").setImmediate,require("timers").clearImmediate)
35703-
},{"process/browser.js":6,"timers":20}]},{},[1]);
35719+
},{"process/browser.js":7,"timers":21}]},{},[1]);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-virtual-overflow",
3-
"version": "1.1.0",
3+
"version": "1.1.2",
44
"description": "virtual scroll without headache",
55
"main": "lib/index.cjs.js",
66
"module": "lib/index.esm.js",

readme.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Similar to [react-virtualized](https://github.com/bvaughn/react-virtualized), bu
1010
- No magical divs will wrap your list with position: absolute and height: 0
1111
- No scroll syncing problems
1212
- No AutoWindow over AutoSize with VerticalSpecialList
13+
- Dead simple infinity loader
1314
- Full rendering controll
1415
- It just works
1516
- ~0.5kb gzipped
@@ -283,6 +284,13 @@ And returns:
283284

284285
// method that will force update calculations
285286
updateViewRect: () => void,
287+
288+
itemSlice: {
289+
topStartIndex: number;
290+
lengthY: number;
291+
leftStartIndex: number;
292+
lengthX: number;
293+
}
286294
}
287295
```
288296

@@ -331,6 +339,13 @@ And returns:
331339

332340
// method that will force update calculations
333341
updateViewRect: () => void,
342+
343+
itemSlice: {
344+
topStartIndex: number;
345+
lengthY: number;
346+
leftStartIndex: number;
347+
lengthX: number;
348+
}
334349
}
335350
```
336351

@@ -385,6 +400,13 @@ And returns:
385400

386401
// method that will force update calculations
387402
updateViewRect: () => void,
403+
404+
itemSlice: {
405+
topStartIndex: number;
406+
lengthY: number;
407+
leftStartIndex: number;
408+
lengthX: number;
409+
}
388410
}
389411
```
390412

@@ -507,6 +529,40 @@ const verticalSlice = virtualOverflowCalcItems(
507529

508530
</details>
509531

532+
533+
534+
<details>
535+
<summary>
536+
<b>Infinity loader</b>
537+
</summary>
538+
539+
<br>
540+
541+
All hooks (`useCalcVirtualOverflow`, `useVirtualOverflowY`, `useVirtualOverflowX`, `useVirtualOverflowGrid`) returns `itemSlice` which you can use to trigger infinity loading.
542+
543+
For example:
544+
545+
```tsx
546+
const [items, setItems] = useState([] as any[]);
547+
548+
// here we get current rendered itemSlice
549+
const { renderedItems, itemSlice } = useVirtualOverflowY({
550+
itemsLengthY: items.length,
551+
// ...
552+
});
553+
554+
// here we check if we render bottom range of items
555+
useEffect(() => {
556+
if (itemSlice.topStartIndex + itemSlice.lengthY >= items.length - 4) {
557+
// load more
558+
setItems((prev) => [...prev, ...newItems]);
559+
}
560+
}, [itemSlice.topStartIndex, itemSlice.lengthY]);
561+
```
562+
563+
</details>
564+
565+
510566
### utils
511567

512568
All methods here are inside `virtualOverflowUtils` namespace in `react-virtual-overflow/utils`. I will not write namespace here below for readability purposes.

0 commit comments

Comments
 (0)