virtua 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/lib/core/resizer.d.ts +1 -1
- package/lib/core/scroller.d.ts +1 -1
- package/lib/core/store.d.ts +4 -4
- package/lib/core/utils.d.ts +1 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +2 -1
- package/lib/index.mjs.map +1 -1
- package/lib/react/DefaultWindow.d.ts +29 -0
- package/lib/react/VGrid.d.ts +33 -15
- package/lib/react/VList.d.ts +7 -14
- package/lib/react/useStore.d.ts +2 -0
- package/package.json +2 -5
- package/lib/react/types.d.ts +0 -2
- package/lib/react/useSyncExternalStore.d.ts +0 -1
package/README.md
CHANGED
|
@@ -110,6 +110,30 @@ export const App = () => {
|
|
|
110
110
|
};
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
### React Server Components (RSC) support
|
|
114
|
+
|
|
115
|
+
This library is marked as a Client Component. You can render RSC as children of VList.
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
// page.tsx in App Router of Next.js
|
|
119
|
+
import { VList } from "virtua";
|
|
120
|
+
|
|
121
|
+
export default async () => {
|
|
122
|
+
return (
|
|
123
|
+
<div>
|
|
124
|
+
<div>This is Server Component</div>
|
|
125
|
+
<VList style={{ height: 300 }}>
|
|
126
|
+
{Array.from({ length: 1000 }).map((_, i) => (
|
|
127
|
+
<div key={i} style={{ border: "solid 1px gray", height: 80 }}>
|
|
128
|
+
{i}
|
|
129
|
+
</div>
|
|
130
|
+
))}
|
|
131
|
+
</VList>
|
|
132
|
+
</div>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
```
|
|
136
|
+
|
|
113
137
|
And see [examples](./stories) for more usages.
|
|
114
138
|
|
|
115
139
|
## Documentation
|
|
@@ -138,6 +162,7 @@ WIP
|
|
|
138
162
|
| Infinite scroll | ✅ | ✅ | 🟠 (needs [react-window-infinite-loader](https://github.com/bvaughn/react-window-infinite-loader)) | 🟠 (needs [InfiniteLoader](https://github.com/bvaughn/react-virtualized/blob/master/docs/InfiniteLoader.md)) | ✅ | ✅ |
|
|
139
163
|
| RTL | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
140
164
|
| SSR support | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
165
|
+
| React Server Components (RSC) support | ✅ | ? | ? | ? | ✅ (hook) | ✅ (hook) |
|
|
141
166
|
| Display exceeding browser's max element size limit | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
142
167
|
|
|
143
168
|
- ✅ - Built-in supported
|
package/lib/core/resizer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { VirtualStore } from "./store";
|
|
2
|
-
export declare const createResizer: (store: VirtualStore) => {
|
|
2
|
+
export declare const createResizer: (store: VirtualStore, isHorizontal: boolean) => {
|
|
3
3
|
_observeRoot(root: HTMLElement): () => void;
|
|
4
4
|
_observeItem(el: HTMLElement, i: number): () => void;
|
|
5
5
|
_isJustResized(): boolean;
|
package/lib/core/scroller.d.ts
CHANGED
|
@@ -6,4 +6,4 @@ export type Scroller = {
|
|
|
6
6
|
_scrollToIndex: (index: number, count: number) => void;
|
|
7
7
|
_fixScrollJump: (jump: ScrollJump, startIndex: number) => void;
|
|
8
8
|
};
|
|
9
|
-
export declare const createScroller: (store: VirtualStore, isJustResized: () => boolean) => Scroller;
|
|
9
|
+
export declare const createScroller: (store: VirtualStore, isHorizontal: boolean, isRtl: boolean, isJustResized: () => boolean) => Scroller;
|
package/lib/core/store.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare const ACTION_WINDOW_RESIZE = 2;
|
|
|
12
12
|
export declare const ACTION_SCROLL = 3;
|
|
13
13
|
export declare const ACTION_MANUAL_SCROLL = 4;
|
|
14
14
|
type Actions = [type: typeof ACTION_ITEM_RESIZE, entries: ItemResize[]] | [type: typeof ACTION_WINDOW_RESIZE, size: number] | [type: typeof ACTION_SCROLL, offset: number] | [type: typeof ACTION_MANUAL_SCROLL, offset: number];
|
|
15
|
+
type Subscriber = (sync?: boolean) => void;
|
|
15
16
|
export type VirtualStore = {
|
|
16
17
|
_getRange(): ItemsRange;
|
|
17
18
|
_isUnmeasuredItem(index: number): boolean;
|
|
@@ -21,16 +22,15 @@ export type VirtualStore = {
|
|
|
21
22
|
_getScrollOffset(): number;
|
|
22
23
|
_getViewportSize(): number;
|
|
23
24
|
_getScrollSize(): number;
|
|
25
|
+
_getScrollableDomSize(): number;
|
|
24
26
|
_getJump(): ScrollJump;
|
|
25
|
-
_isHorizontal(): boolean;
|
|
26
|
-
_isRtl(): boolean;
|
|
27
27
|
_getItemIndexForScrollTo(offset: number): number;
|
|
28
28
|
_waitForScrollDestinationItemsMeasured(): Promise<void>;
|
|
29
|
-
_subscribe(cb:
|
|
29
|
+
_subscribe(cb: Subscriber): () => void;
|
|
30
30
|
_update(...action: Actions): void;
|
|
31
31
|
_getScrollDirection(): ScrollDirection;
|
|
32
32
|
_setScrollDirection(direction: ScrollDirection): void;
|
|
33
33
|
_updateCacheLength(length: number): void;
|
|
34
34
|
};
|
|
35
|
-
export declare const createVirtualStore: (itemCount: number, itemSize: number,
|
|
35
|
+
export declare const createVirtualStore: (itemCount: number, itemSize: number, initialItemCount: number | undefined, isReverse: boolean, onScrollStateChange: (scrolling: boolean) => void, onScrollOffsetChange: (offset: number) => void) => VirtualStore;
|
|
36
36
|
export {};
|
package/lib/core/utils.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const min: (...values: number[]) => number;
|
|
2
2
|
export declare const max: (...values: number[]) => number;
|
|
3
|
+
export declare const abs: (x: number) => number;
|
|
3
4
|
export declare const now: () => number;
|
|
4
5
|
export declare const exists: <T>(v: T) => v is Exclude<T, null | undefined>;
|
|
5
6
|
export declare const range: <T>(length: number, cb: (i: number) => T) => T[];
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { VList } from "./react/VList";
|
|
2
|
-
export type { VListProps, VListHandle, CustomItemComponent, CustomItemComponentProps,
|
|
2
|
+
export type { VListProps, VListHandle, CustomItemComponent, CustomItemComponentProps, ScrollMode, } from "./react/VList";
|
|
3
3
|
export { VGrid } from "./react/VGrid";
|
|
4
|
-
export type { VGridProps, VGridHandle, CustomCellComponent, CustomCellComponentProps,
|
|
5
|
-
export type { WindowComponentAttributes } from "./react/
|
|
4
|
+
export type { VGridProps, VGridHandle, CustomCellComponent, CustomCellComponentProps, } from "./react/VGrid";
|
|
5
|
+
export type { WindowComponentAttributes, CustomWindowComponent, CustomWindowComponentProps, } from "./react/DefaultWindow";
|
package/lib/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
var e=require("react/jsx-runtime"),t=require("react"),r=require("react-dom");const n=Math.min,o=Math.max,i=Math.abs,l=Date.now,s=e=>null!=e,c=(e,t)=>Array.from({length:e},((e,r)=>t(r))),u=e=>{let t,r;return(...n)=>(t||(t=!0,r=e(...n)),r)},_=(e,t)=>{const r=e.t[t];return-1===r?e.o:r},a=(e,t,r)=>{if(!e.i)return 0;if(e.l>=t)return r?e.u[t]+_(e,t):e.u[t];let n=e.l,o=e.u[n];for(;n<=t&&(e.u[n]=o,n!==t||r);)o+=_(e,n),n++;return e.l=t,o},d=(e,t)=>a(e,t),h=(e,t,r)=>{let i=0;if(r>=0)for(;t<e.i-1;){const n=_(e,t++);if((i+=n)>=r){i-n/2>=r&&t--;break}}else for(;t>0;){const n=_(e,--t);if((i-=n)<=r){i+n/2<r&&t++;break}}return n(o(t,0),e.i-1)},f=(e,t,r,n)=>h(e,r,t-n),g=h,m=(e,t,r)=>({o:t,i:e,l:r?n(r.l,e-1):0,t:c(e,(e=>{const t=r&&r.t[e];return s(t)?t:-1})),u:c(e,(e=>{if(0===e)return 0;const t=r&&r.u[e];return s(t)?t:-1}))}),w=(e,t,r=0,l,s,c)=>{let u,h=t*o(r-1,0),w=0,S=[],v=m(e,t),p=0,z=[0,r];const I=new Set,b=()=>(e=>a(e,e.i-1,!0))(v);return{_(){const[e,t]=z,r=d(v,e),n=f(v,w,e,r),o=g(v,n,h);return e===n&&t===o?z:z=[n,o]},h:e=>-1===v.t[e],g:e=>((e,t,r)=>{for(let n=t;n<=r;n++)if(-1===e.t[n])return!0;return!1})(v,e,g(v,e,h)),m(e){const t=d(v,e);return l?t+o(0,h-b()):t},S:e=>_(v,e),v:()=>w,p:()=>h,I:()=>b(),R:()=>o(b(),h),T:()=>S,M:e=>f(v,e,0,0),O:()=>(u&&u[1](),new Promise(((e,t)=>{u=[()=>{Promise.resolve().then((()=>{e(),u=void 0}))},t]}))),C:e=>(I.add(e),()=>{I.delete(e)}),H(e,t){let r;const o=(()=>{switch(e){case 1:{const e=t.filter((([e,t])=>v.t[e]!==t));return!!e.length&&(S=[],e.forEach((([e,t])=>{S.push([t-_(v,e),e]),((e,t,r)=>{e.t[t]=r,e.l=n(t,e.l)})(v,e,t)})),r=!0,!0)}case 2:return h!==t&&(h=t,!0);case 3:case 4:{const n=w,o=(w=t)!==n;return o&&3===e&&(r=i(n-t)>h),o}}})();o&&(I.forEach((e=>{e(r)})),3===e?c(w):u&&1===e&&u[0]())},D:()=>p,k(e){const t=p;p=e,0===p?s(!1):0!==t||1!==p&&2!==p||s(!0)},J(e){v.i!==e&&(v=m(e,t,v))}}},S="undefined"!=typeof window?t.useLayoutEffect:t.useEffect,v="current",p=e=>{const r=t.useRef(e);return S((()=>{r[v]=e}),[e]),r},z=(e,n,o)=>{const[i,l]=t.useState(n),s=p(n);if(t.useLayoutEffect((()=>{const t=()=>{l((()=>s[v]()))};return e.C((e=>{e?r.flushSync(t):t()}))}),[]),o){const e=n();i!==e&&l(e)}return i},I=u((e=>{const t="scrollLeft",r=e[t];e[t]=1;const n=e[t]<1;return e[t]=r,n})),b=(e,t,r,i)=>{let c;const u=t?"scrollLeft":"scrollTop",_=()=>c?t?c.scrollWidth:c.scrollHeight:0,a=(t,r)=>I(c)||r?-t:e.I()-e.p()-t,d=(n,o)=>{c&&(t&&r&&(n=a(n,o)),o?c[u]+=n:(c[u]=n,e.k(3)))},h=async(t,r)=>{const n=()=>{let t=r();const n=_(),o=e.p();return n-(t+o)<=0&&(t=n-o),t};if(e.g(t)){do{e.H(4,n());try{await e.O()}catch(e){return}}while(e.g(t));d(n())}else{const t=n();d(t),e.H(4,t)}},f=e=>e.reduce(((e,[t])=>e+t),0);return{W(n){c=n;const o=()=>{let o=n[u];t&&r&&(o=a(o));const l=e.v();if(l===o)return;const s=e.D(),c=i();0!==s&&c||3===s||e.k(l>o?2:1),e.H(3,o)},_=(()=>{let t;const r=()=>{s(t)&&clearTimeout(t)},n=()=>{r(),t=setTimeout((()=>{t=null,o(),e.k(0)}),150)};return n.$=r,n})(),d=()=>{o(),_()},h=(()=>{let r=l()-50;return(...n)=>{const o=l();r+50<o&&(r=o,(r=>{if(0!==e.D()&&!r.ctrlKey&&(t?r.deltaX:r.deltaY)){const t=e.v();t>0&&t<e.I()-e.p()&&_()}})(...n))}})();return n.addEventListener("scroll",d),n.addEventListener("wheel",h,{passive:!0}),()=>{n.removeEventListener("scroll",d),n.removeEventListener("wheel",h),_.$()}},q:_,L(t){t=o(t,0),h(e.M(t),(()=>t))},A(t,r){t=o(n(t,r-1),0),h(t,(()=>e.m(t)))},B:(t,r)=>{const n=e.D();if(2===n){const e=f(t);e&&d(e,!0)}else if(3===n){const n=e.v();if(0===n);else{const o=f(t);if(e.I()-(n+e.p()+o)<=0)o&&d(n+o);else{const e=t.reduce(((e,[t,n])=>(n<r&&(e+=t),e)),0);e&&d(e,!0)}}}}}},x=e=>{const r=t.useRef();return r[v]||(r[v]=e())},R=t.forwardRef((({children:r,attrs:n,width:o,height:i,scrolling:l},s)=>e.jsx("div",{ref:s,...n,children:e.jsx("div",{style:t.useMemo((()=>({position:"relative",visibility:"hidden",width:null!=o?o:"100%",height:null!=i?i:"100%",pointerEvents:l?"none":"auto"})),[o,i,l]),children:r})}))),y=t.memo((({F:r,P:n,U:o,j:i,V:l,G:s,K:c})=>{const u=t.useRef(null),_=z(o,(()=>o.m(i)),!0),a=z(o,(()=>o.h(i)),!0);return S((()=>n.N(u[v],i)),[i]),e.jsx(l,{ref:u,style:t.useMemo((()=>{const e=c?"right":"left",t={margin:0,padding:0,position:"absolute",[s?"height":"width"]:"100%",[s?"top":e]:0,[s?e:"top"]:_,visibility:a?"hidden":"visible"};return s&&(t.display="flex"),t}),[_,a]),children:r})})),T=({F:r,X:n,U:o,V:i,Y:l,Z:s,G:c})=>{const u=z(o,o.I);return e.jsx(i,{ref:n,width:c?u:void 0,height:c?void 0:u,scrolling:l,attrs:t.useMemo((()=>({...s,style:{overflow:c?"auto hidden":"hidden auto",contain:"strict",width:"100%",height:"100%",padding:0,margin:0,...s.style}})),[s]),children:r})},M=t.forwardRef((({children:r,itemSize:i=40,overscan:l=4,initialItemCount:c,horizontal:_,mode:a,element:d=R,itemElement:h="div",onScroll:f,onScrollStop:g,onRangeChange:m,...I},M)=>{const O=t.useMemo((()=>{const e=[];return t.Children.forEach(r,(t=>{(e=>!s(e)||"boolean"==typeof e)(t)||e.push(t)})),e}),[r]),C=O.length,H=p(f),D=p(g),[k,J]=t.useState(!1),[W,$,q,E,L]=x((()=>{const e=!!_,t="rtl"===a,r=w(C,i,c,"reverse"===a,(e=>{J(e),e||D[v]&&D[v]()}),(e=>{H[v]&&H[v](e)})),n=((e,t)=>{let r,n=!1;const o=t?"width":"height",i=new WeakMap,l=u((()=>new ResizeObserver((t=>{const l=[];for(const{target:n,contentRect:c}of t)if(n===r)e.H(2,c[o]);else{const e=i.get(n);s(e)&&l.push([e,c[o]])}l.length&&(e.H(1,l),n=!0)}))));return{ee(e){r=e;const t=l();return t.observe(e),()=>{t.disconnect()}},N(e,t){const r=l();return i.set(e,t),r.observe(e),()=>{i.delete(e),r.unobserve(e)}},te(){const e=n;return n=!1,e}}})(r,e);return[r,n,b(r,e,t,n.te),e,t]}));W.J(C);const[A,B]=z(W,W._),F=z(W,W.T),P=t.useRef(null);S((()=>{const e=P[v],t=$.ee(e),r=q.W(e);return()=>{t(),r()}}),[]),S((()=>{F.length&&q.B(F,A)}),[F]),t.useEffect((()=>{m&&m({start:A,end:B,count:C})}),[A,B]),t.useImperativeHandle(M,(()=>({get scrollOffset(){return W.v()},get scrollSize(){return q.q()},get viewportSize(){return W.p()},scrollToIndex(e){q.A(e,C)},scrollTo:q.L,scrollBy(e){q.L(W.v()+e)}})),[C]);const U=o(A-l,0),j=n(B+l,C-1),V=t.useMemo((()=>{const t=[];for(let r=U;r<=j;r++){const n=O[r];s(n)&&t.push(e.jsx(y,{P:$,U:W,j:r,V:h,F:n,G:E,K:L},(null==n?void 0:n.key)||r))}return t}),[O,U,j]);return e.jsx(T,{X:P,U:W,V:d,Y:k,F:V,Z:I,G:E})})),O=(e,t)=>`${e}-${t}`,C=t.memo((({F:r,P:n,re:o,ne:i,oe:l,ie:s,V:c,K:u})=>{const _=t.useRef(null),a=z(o,(()=>o.m(l)),!0),d=z(i,(()=>i.m(s)),!0),h=z(o,(()=>o.h(l)),!0),f=z(i,(()=>i.h(s)),!0),g=z(o,(()=>o.S(l)),!0),m=z(i,(()=>i.S(s)),!0);return S((()=>n.N(_[v],l,s)),[s,l]),e.jsx(c,{ref:_,style:t.useMemo((()=>({display:"grid",margin:0,padding:0,position:"absolute",top:a,[u?"right":"left"]:d,visibility:h||f?"hidden":"visible",minHeight:g,minWidth:m})),[a,d,m,g,h,f]),children:r})})),H=({F:r,X:n,le:o,se:i,V:l,Y:s,Z:c})=>{const u=z(o,o.I),_=z(i,i.I);return e.jsx(l,{ref:n,width:_,height:u,scrolling:s,attrs:t.useMemo((()=>({...c,style:{overflow:"auto",contain:"strict",width:"100%",height:"100%",padding:0,margin:0,...c.style}})),[c]),children:r})},D=t.forwardRef((({children:r,row:i,col:l,cellHeight:s=40,cellWidth:c=100,overscan:_=2,initialRowCount:a,initialColCount:d,rtl:h,element:f=R,cellElement:g="div",...m},p)=>{const[I,y]=t.useState(!1),[T,M]=t.useState(!1),[D,k,J,W,$,q]=x((()=>{const e=!!h,t=()=>{},r=w(i,s,a,!1,y,t),n=w(l,c,d,!1,M,t),_=((e,t)=>{let r,n=!1,i=!1;const l="height",s="width",c=new WeakMap,_=new Set,a=new Set,d=new Map,h=(e,t)=>`${e}-${t}`,f=u((()=>new ResizeObserver((u=>{const f=new Set,g=new Set;for(const{target:n,contentRect:o}of u)if(n===r)e.H(2,o[l]),t.H(2,o[s]);else{const e=c.get(n);if(e){const[t,r]=e,n=h(t,r),i=d.get(n),c=[o[l],o[s]];let u,_;i?(i[0]!==c[0]&&(u=!0),i[1]!==c[1]&&(_=!0)):u=_=!0,u&&f.add(t),_&&g.add(r),(u||_)&&d.set(n,c)}}if(f.size){const t=[];f.forEach((e=>{let r=0;a.forEach((t=>{const n=d.get(h(e,t));n&&(r=o(r,n[0]))})),r&&t.push([e,r])})),e.H(1,t),n=!0}if(g.size){const e=[];g.forEach((t=>{let r=0;_.forEach((e=>{const n=d.get(h(e,t));n&&(r=o(r,n[1]))})),r&&e.push([t,r])})),t.H(1,e),i=!0}}))));return{ee(e){r=e;const t=f();return t.observe(e),()=>{t.disconnect()}},N(e,t,r){const n=f();return c.set(e,[t,r]),_.add(t),a.add(r),n.observe(e),()=>{c.delete(e),n.unobserve(e)}},te(e){const t=e?i:n;return e?i=!1:n=!1,t}}})(r,n);return[r,n,_,b(r,!1,e,(()=>_.te())),b(n,!0,e,(()=>_.te(!0))),e]}));D.J(i),k.J(l);const[E,L]=z(D,D._),[A,B]=z(k,k._),F=z(D,D.T),P=z(k,k.T),U=t.useRef(null);S((()=>{const e=U[v],t=J.ee(e),r=W.W(e),n=$.W(e);return()=>{t(),r(),n()}}),[]),S((()=>{F.length&&W.B(F,E)}),[F]),S((()=>{P.length&&$.B(P,A)}),[P]),t.useImperativeHandle(p,(()=>({get scrollOffset(){return[k.v(),D.v()]},get scrollSize(){return[$.q(),W.q()]},get viewportSize(){return[k.p(),D.p()]},scrollToIndex(e,t){$.A(e,l),W.A(t,i)},scrollTo(e,t){$.L(e),W.L(t)},scrollBy(e,t){$.L(k.v()+e),W.L(D.v()+t)}})),[i,l]);const j=t.useMemo((()=>{const e=new Map;return(t,n)=>{let o=e.get(O(t,n));return o||e.set(O(t,n),o=r({rowIndex:t,colIndex:n})),o}}),[r]),V=o(E-_,0),G=n(L+_,i-1),K=o(A-_,0),N=n(B+_,l-1),Q=t.useMemo((()=>{const t=[];for(let r=V;r<=G;r++)for(let n=K;n<=N;n++)t.push(e.jsx(C,{P:J,re:D,ne:k,oe:r,ie:n,V:g,F:j(r,n),K:q},O(r,n)));return t}),[j,V,G,K,N]);return e.jsx(H,{X:U,le:D,se:k,V:f,Y:I||T,F:Q,Z:m})}));exports.VGrid=D,exports.VList=M;
|
|
2
3
|
//# sourceMappingURL=index.js.map
|