react-base-virtual-list 1.0.1 → 1.2.4

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
@@ -1,8 +1,8 @@
1
- # react-base-virtual-list ![GitHub License](https://img.shields.io/github/license/phphe/react-base-virtual-list) ![NPM Version](https://img.shields.io/npm/v/react-base-virtual-list) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/phphe/react-base-virtual-list/build.yml) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-base-virtual-list)
1
+ # react-base-virtual-list ![License](https://img.shields.io/github/license/phphe/react-base-virtual-list) ![NPM Version](https://img.shields.io/npm/v/react-base-virtual-list) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/phphe/react-base-virtual-list/publish.yml) [![Changelog](https://img.shields.io/badge/changelog-latest-blue.svg)](./CHANGELOG.md)
2
2
 
3
3
  [中文](README_CN.md)
4
4
 
5
- React basic virtual list, supports common features and is easy to customize. [Online Demo](https://phphe.github.io/react-base-virtual-list/)
5
+ React basic virtual list, supports common features and is easy to customize. Support React 18, 19. [Online Demo](https://phphe.github.io/react-base-virtual-list/) | [Changelog](./CHANGELOG.md)
6
6
 
7
7
  ## Features
8
8
 
@@ -83,6 +83,7 @@ export default function BaseExample() {
83
83
  - `renderFoot`: `() => ReactNode`. Customize the persistent rendering content of the list footer. Suitable for absolutely positioned, fixed positioned elements, and elements that do not take up too much space.
84
84
  - `className`: `string`. Add a CSS class to the list root element.
85
85
  - `style`: `React.CSSProperties`. Add CSS styles to the list root element.
86
+ - `innerClassName`: `string`. Add a CSS class to the list inner element.
86
87
 
87
88
  ## Exposed Methods
88
89
 
@@ -110,7 +111,7 @@ Irrelevant parts are omitted in the above code. `VirtualListHandle` is a `typesc
110
111
  interface VirtualListHandle {
111
112
  scrollToIndex(
112
113
  index: number,
113
- block?: "start" | "end" | "center" | "nearest"
114
+ block?: "start" | "end" | "center" | "nearest",
114
115
  ): void;
115
116
  getRootElement(): HTMLElement;
116
117
  forceUpdate(): void;
@@ -141,10 +142,6 @@ You can also use the following third-party CDN url to include it.
141
142
  ## Development
142
143
 
143
144
  - `lib`: The main files, also the files that are packaged into the library. Running `npm run build` will package the files in this directory into the `dist` folder. The corresponding Vite configuration file is `vite.build.ts`.
144
- - `src`: The files used for development and debugging. Running `npm run dev` will run the code in this directory in the browser. Running `npm run build:web` will package the code in this directory into the `dist` folder. The corresponding Vite configuration file is `vite.config.ts`.
145
+ - `src`: The files used for development and debugging. Running `npm run dev` will run the code in this directory in the browser. Running `npm run demo:build` will package the code in this directory into the `dist-demo` folder. The corresponding Vite configuration file is `vite.config.ts`.
145
146
  - `uno.config.ts`: [unocss](https://github.com/unocss/unocss) configuration file. `unocss` only works in the `src` folder. With the current configuration, you can use `Tailwindcss` style class names.
146
- - `.github/workflows/build.yml`: Some automated actions performed when publishing to GitHub. You can delete or modify it.
147
-
148
- ## Changelog
149
-
150
- https://github.com/phphe/react-base-virtual-list/releases
147
+ - `.github/workflows/publish.yml`: Some automated actions performed when publishing to GitHub pages and npmjs.com. You can delete or modify it.
package/dist/index.cjs CHANGED
@@ -2,4 +2,4 @@
2
2
  * react-base-virtual-list
3
3
  * Author: phphe <phphe@outlook.com> (https://github.com/phphe)
4
4
  * Released under the MIT License.
5
- */Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("react/jsx-runtime"),c=require("react"),G=c.forwardRef,F={listSize:1e3,virtual:!0},j=G(function(e,N){var M,L;const[s,O]=c.useState(e.itemSize||100),m=c.useMemo(()=>e.buffer||Math.max(s*5,100),[e.buffer,s]),f=e.items.length,r=c.useRef(null),h=c.useRef(null),I=c.useRef(0),d=c.useRef(),[P,C]=c.useState([]),[g,w]=c.useState(0),[b,v]=c.useState(e.listSize),[$,q]=c.useState([]),x=c.useRef(!1),B=c.useMemo(()=>{var V;const n=s*f;let t=g-m,i=n-g-b-m,l=0,a=0;t<=0?(t=0,l=0):l=Math.floor(t/s),i<0&&(i=0),n<=b?a=f:a=f-Math.floor(i/s),e.virtual||(l=0,a=f);let o=Array.from({length:a-l},(S,z)=>z+l).concat(e.persistentIndices||[]);return(V=e.persistentIndices)!=null&&V.length&&(o=[...new Set(o)].sort((S,z)=>S-z)),{visible:o.map(S=>e.items[S]),visibleIndices:o,topSpace:t,bottomSpace:i,totalSpace:n}},[e.items,s,f,g,m,b,e.virtual,e.persistentIndices]),{visible:D,visibleIndices:y,topSpace:U,bottomSpace:H,totalSpace:k}=B,T={paddingTop:`${U}px`,boxSizing:"border-box"};H<s*5?T.paddingBottom=`${H}px`:T.height=`${k}px`,c.useLayoutEffect(()=>{if(r.current&&(v(r.current.clientHeight),e.itemSize==null)){const n=h.current;let t=parseFloat(getComputedStyle(n).rowGap);t=isNaN(t)?0:t;let i=0,l=0;const a=new Set(e.persistentIndices||[]);let R=-1;for(const o of n.children){if(R++,a.has(y[R]))continue;const u=getComputedStyle(o);u.display!=="none"&&(u.position!=="static"&&u.position!=="relative"||(l+=o.offsetHeight+parseFloat(u.marginTop)+parseFloat(u.marginBottom)+t,i++))}O(l/i)}},[e.itemSize,e.items,$]);const A=function(n){var t;if(e.virtual&&!d.current){if(v(r.current.clientHeight),x.current)x.current=!1;else{const i=r.current.scrollTop;Math.abs(I.current-i)>(e.triggerDistance??s)&&(w(i),I.current=i)}(t=e.onScroll)==null||t.call(this,n)}};return c.useImperativeHandle(N,()=>({scrollToIndex(n,t="start"){d.current={index:n,block:t};const i=n*s;r.current.scrollTop=i,w(i),I.current=i,C([])},getRootElement(){return r.current},forceUpdate(){q([])}}),[s]),c.useLayoutEffect(()=>{if(d.current){const{index:n,block:t}=d.current;d.current=void 0;const i=y.indexOf(n),l=h.current.children[i];l&&(l.scrollIntoView({block:t}),x.current=!0)}},[P]),c.useLayoutEffect(()=>{const{ResizeObserver:n}=window,t=n&&new n(()=>{v(r.current.clientHeight)});return t==null||t.observe(r.current),()=>{t==null||t.disconnect()}},[]),E.jsxs("div",{ref:r,onScroll:A,className:e.className,style:{overflow:"auto",...e.style},children:[(M=e.renderHead)==null?void 0:M.call(e),E.jsx("div",{ref:h,style:{display:"flex",flexDirection:"column",...e.virtual&&T},children:D.map((n,t)=>e.renderItem(n,y[t]))}),(L=e.renderFoot)==null?void 0:L.call(e)]})});j.defaultProps=F;exports.VirtualList=j;exports.defaultProps=F;
5
+ */Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const V=require("react/jsx-runtime"),l=require("react"),G=l.forwardRef,E={listSize:1e3,virtual:!0},F=G(function(e,j){var M,L;const[s,C]=l.useState(e.itemSize||100),m=l.useMemo(()=>e.buffer||Math.max(s*5,100),[e.buffer,s]),f=e.items.length,r=l.useRef(null),h=l.useRef(null),I=l.useRef(0),d=l.useRef(),[O,P]=l.useState([]),[g,w]=l.useState(0),[b,v]=l.useState(e.listSize),[$,q]=l.useState([]),x=l.useRef(!1),B=l.useMemo(()=>{var N;const n=s*f;let t=g-m,i=n-g-b-m,c=0,a=0;t<=0?(t=0,c=0):c=Math.floor(t/s),i<0&&(i=0),n<=b?a=f:a=f-Math.floor(i/s),e.virtual||(c=0,a=f);let o=Array.from({length:a-c},(S,z)=>z+c).concat(e.persistentIndices||[]);return(N=e.persistentIndices)!=null&&N.length&&(o=[...new Set(o)].sort((S,z)=>S-z)),{visible:o.map(S=>e.items[S]),visibleIndices:o,topSpace:t,bottomSpace:i,totalSpace:n}},[e.items,s,f,g,m,b,e.virtual,e.persistentIndices]),{visible:D,visibleIndices:y,topSpace:U,bottomSpace:H,totalSpace:k}=B,T={paddingTop:`${U}px`,boxSizing:"border-box"};H<s*5?T.paddingBottom=`${H}px`:T.height=`${k}px`,l.useLayoutEffect(()=>{if(r.current&&(v(r.current.clientHeight),e.itemSize==null)){const n=h.current;let t=parseFloat(getComputedStyle(n).rowGap);t=isNaN(t)?0:t;let i=0,c=0;const a=new Set(e.persistentIndices||[]);let R=-1;for(const o of n.children){if(R++,a.has(y[R]))continue;const u=getComputedStyle(o);u.display!=="none"&&(u.position!=="static"&&u.position!=="relative"||(c+=o.offsetHeight+parseFloat(u.marginTop)+parseFloat(u.marginBottom)+t,i++))}C(c/i)}},[e.itemSize,e.items,$]);const A=function(n){var t;if(e.virtual&&!d.current){if(v(r.current.clientHeight),x.current)x.current=!1;else{const i=r.current.scrollTop;Math.abs(I.current-i)>(e.triggerDistance??s)&&(w(i),I.current=i)}(t=e.onScroll)==null||t.call(this,n)}};return l.useImperativeHandle(j,()=>({scrollToIndex(n,t="start"){d.current={index:n,block:t};const i=n*s;r.current.scrollTop=i,w(i),I.current=i,P([])},getRootElement(){return r.current},forceUpdate(){q([])}}),[s]),l.useLayoutEffect(()=>{if(d.current){const{index:n,block:t}=d.current;d.current=void 0;const i=y.indexOf(n),c=h.current.children[i];c&&(c.scrollIntoView({block:t}),x.current=!0)}},[O]),l.useLayoutEffect(()=>{const{ResizeObserver:n}=window,t=n&&new n(()=>{v(r.current.clientHeight)});return t==null||t.observe(r.current),()=>{t==null||t.disconnect()}},[]),V.jsxs("div",{ref:r,onScroll:A,className:e.className,style:{overflow:"auto",...e.style},children:[(M=e.renderHead)==null?void 0:M.call(e),V.jsx("div",{ref:h,className:e.innerClassName,style:{display:"flex",flexDirection:"column",...e.virtual&&T},children:D.map((n,t)=>e.renderItem(n,y[t]))}),(L=e.renderFoot)==null?void 0:L.call(e)]})});F.defaultProps=E;exports.VirtualList=F;exports.defaultProps=E;
@@ -2,5 +2,5 @@ var reactBaseVirtualList=function(S,L,i){"use strict";/*!
2
2
  * react-base-virtual-list
3
3
  * Author: phphe <phphe@outlook.com> (https://github.com/phphe)
4
4
  * Released under the MIT License.
5
- */const j=i.forwardRef,M={listSize:1e3,virtual:!0},V=j(function(e,B){var N,O;const[s,C]=i.useState(e.itemSize||100),h=i.useMemo(()=>e.buffer||Math.max(s*5,100),[e.buffer,s]),f=e.items.length,c=i.useRef(null),I=i.useRef(null),g=i.useRef(0),d=i.useRef(),[$,D]=i.useState([]),[b,E]=i.useState(0),[v,x]=i.useState(e.listSize),[U,k]=i.useState([]),y=i.useRef(!1),A=i.useMemo(()=>{var P;const n=s*f;let t=b-h,l=n-b-v-h,r=0,u=0;t<=0?(t=0,r=0):r=Math.floor(t/s),l<0&&(l=0),n<=v?u=f:u=f-Math.floor(l/s),e.virtual||(r=0,u=f);let o=Array.from({length:u-r},(m,H)=>H+r).concat(e.persistentIndices||[]);return(P=e.persistentIndices)!=null&&P.length&&(o=[...new Set(o)].sort((m,H)=>m-H)),{visible:o.map(m=>e.items[m]),visibleIndices:o,topSpace:t,bottomSpace:l,totalSpace:n}},[e.items,s,f,b,h,v,e.virtual,e.persistentIndices]),{visible:G,visibleIndices:T,topSpace:_,bottomSpace:F,totalSpace:q}=A,z={paddingTop:`${_}px`,boxSizing:"border-box"};F<s*5?z.paddingBottom=`${F}px`:z.height=`${q}px`,i.useLayoutEffect(()=>{if(c.current&&(x(c.current.clientHeight),e.itemSize==null)){const n=I.current;let t=parseFloat(getComputedStyle(n).rowGap);t=isNaN(t)?0:t;let l=0,r=0;const u=new Set(e.persistentIndices||[]);let w=-1;for(const o of n.children){if(w++,u.has(T[w]))continue;const a=getComputedStyle(o);a.display!=="none"&&(a.position!=="static"&&a.position!=="relative"||(r+=o.offsetHeight+parseFloat(a.marginTop)+parseFloat(a.marginBottom)+t,l++))}C(r/l)}},[e.itemSize,e.items,U]);const J=function(n){var t;if(e.virtual&&!d.current){if(x(c.current.clientHeight),y.current)y.current=!1;else{const l=c.current.scrollTop;Math.abs(g.current-l)>(e.triggerDistance??s)&&(E(l),g.current=l)}(t=e.onScroll)==null||t.call(this,n)}};return i.useImperativeHandle(B,()=>({scrollToIndex(n,t="start"){d.current={index:n,block:t};const l=n*s;c.current.scrollTop=l,E(l),g.current=l,D([])},getRootElement(){return c.current},forceUpdate(){k([])}}),[s]),i.useLayoutEffect(()=>{if(d.current){const{index:n,block:t}=d.current;d.current=void 0;const l=T.indexOf(n),r=I.current.children[l];r&&(r.scrollIntoView({block:t}),y.current=!0)}},[$]),i.useLayoutEffect(()=>{const{ResizeObserver:n}=window,t=n&&new n(()=>{x(c.current.clientHeight)});return t==null||t.observe(c.current),()=>{t==null||t.disconnect()}},[]),L.jsxs("div",{ref:c,onScroll:J,className:e.className,style:{overflow:"auto",...e.style},children:[(N=e.renderHead)==null?void 0:N.call(e),L.jsx("div",{ref:I,style:{display:"flex",flexDirection:"column",...e.virtual&&z},children:G.map((n,t)=>e.renderItem(n,T[t]))}),(O=e.renderFoot)==null?void 0:O.call(e)]})});return V.defaultProps=M,S.VirtualList=V,S.defaultProps=M,Object.defineProperty(S,Symbol.toStringTag,{value:"Module"}),S}({},jsxRuntime,React);
5
+ */const P=i.forwardRef,M={listSize:1e3,virtual:!0},V=P(function(e,j){var F,C;const[r,B]=i.useState(e.itemSize||100),h=i.useMemo(()=>e.buffer||Math.max(r*5,100),[e.buffer,r]),f=e.items.length,c=i.useRef(null),I=i.useRef(null),g=i.useRef(0),d=i.useRef(),[$,D]=i.useState([]),[b,N]=i.useState(0),[v,x]=i.useState(e.listSize),[U,k]=i.useState([]),y=i.useRef(!1),A=i.useMemo(()=>{var O;const n=r*f;let t=b-h,l=n-b-v-h,s=0,u=0;t<=0?(t=0,s=0):s=Math.floor(t/r),l<0&&(l=0),n<=v?u=f:u=f-Math.floor(l/r),e.virtual||(s=0,u=f);let o=Array.from({length:u-s},(m,H)=>H+s).concat(e.persistentIndices||[]);return(O=e.persistentIndices)!=null&&O.length&&(o=[...new Set(o)].sort((m,H)=>m-H)),{visible:o.map(m=>e.items[m]),visibleIndices:o,topSpace:t,bottomSpace:l,totalSpace:n}},[e.items,r,f,b,h,v,e.virtual,e.persistentIndices]),{visible:G,visibleIndices:T,topSpace:_,bottomSpace:E,totalSpace:q}=A,z={paddingTop:`${_}px`,boxSizing:"border-box"};E<r*5?z.paddingBottom=`${E}px`:z.height=`${q}px`,i.useLayoutEffect(()=>{if(c.current&&(x(c.current.clientHeight),e.itemSize==null)){const n=I.current;let t=parseFloat(getComputedStyle(n).rowGap);t=isNaN(t)?0:t;let l=0,s=0;const u=new Set(e.persistentIndices||[]);let w=-1;for(const o of n.children){if(w++,u.has(T[w]))continue;const a=getComputedStyle(o);a.display!=="none"&&(a.position!=="static"&&a.position!=="relative"||(s+=o.offsetHeight+parseFloat(a.marginTop)+parseFloat(a.marginBottom)+t,l++))}B(s/l)}},[e.itemSize,e.items,U]);const J=function(n){var t;if(e.virtual&&!d.current){if(x(c.current.clientHeight),y.current)y.current=!1;else{const l=c.current.scrollTop;Math.abs(g.current-l)>(e.triggerDistance??r)&&(N(l),g.current=l)}(t=e.onScroll)==null||t.call(this,n)}};return i.useImperativeHandle(j,()=>({scrollToIndex(n,t="start"){d.current={index:n,block:t};const l=n*r;c.current.scrollTop=l,N(l),g.current=l,D([])},getRootElement(){return c.current},forceUpdate(){k([])}}),[r]),i.useLayoutEffect(()=>{if(d.current){const{index:n,block:t}=d.current;d.current=void 0;const l=T.indexOf(n),s=I.current.children[l];s&&(s.scrollIntoView({block:t}),y.current=!0)}},[$]),i.useLayoutEffect(()=>{const{ResizeObserver:n}=window,t=n&&new n(()=>{x(c.current.clientHeight)});return t==null||t.observe(c.current),()=>{t==null||t.disconnect()}},[]),L.jsxs("div",{ref:c,onScroll:J,className:e.className,style:{overflow:"auto",...e.style},children:[(F=e.renderHead)==null?void 0:F.call(e),L.jsx("div",{ref:I,className:e.innerClassName,style:{display:"flex",flexDirection:"column",...e.virtual&&z},children:G.map((n,t)=>e.renderItem(n,T[t]))}),(C=e.renderFoot)==null?void 0:C.call(e)]})});return V.defaultProps=M,S.VirtualList=V,S.defaultProps=M,Object.defineProperty(S,Symbol.toStringTag,{value:"Module"}),S}({},jsxRuntime,React);
6
6
  //# sourceMappingURL=index.iife.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.iife.js","sources":["../lib/VirtualList.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo, useRef, ReactNode, useLayoutEffect, useImperativeHandle\n} from 'react';\n\n// fix forwardRef type for generic types. refer: https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref\nexport type FixedForwardRef = < T, P = {} > (\n render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,\n) => (props: P & React.RefAttributes<T>) => React.ReactElement | null;\nconst forwardRef = React.forwardRef as FixedForwardRef\n\nexport type VirtualListProps<ITEM> = {\n /**\n * Estimated average size of each list item.\n */\n itemSize?: number,\n /**\n * render space = list visible space + buffer x 2\n */\n buffer?: number,\n /**\n * List of items to render.\n */\n items: ITEM[],\n /**\n * Render function for each list item.\n */\n renderItem: (item: ITEM, index: number) => ReactNode,\n /**\n * These items won't be removed when scroll. You can use css 'position:sticky' make them sticky.\n */\n persistentIndices?: number[], // index[]\n /**\n * Minimum distance for triggering a calculation when scrolling.\n */\n triggerDistance?: number,\n /**\n * listen to scroll event.\n */\n onScroll?: React.UIEventHandler<HTMLElement>,\n /**\n * Insert elements at the head. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.\n */\n renderHead?: () => ReactNode,\n /**\n * Insert elements at the foot. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.\n */\n renderFoot?: () => ReactNode,\n className?: string,\n style?: React.CSSProperties,\n} & Partial<typeof defaultProps>\n\nexport const defaultProps = {\n /**\n * The visible space of the list. It is only used before DOM created(SSR).\n */\n listSize: 1000,\n /**\n * Whether to enable the virtual list feature.\n */\n virtual: true,\n}\n\nexport interface VirtualListHandle {\n scrollToIndex(index: number, block?: 'start' | 'end' | 'center' | 'nearest'): void\n getRootElement(): HTMLElement\n forceUpdate(): void\n}\n\nexport const VirtualList = forwardRef(function <ITEM>(\n props: VirtualListProps<ITEM>,\n ref: React.ForwardedRef<VirtualListHandle>\n) {\n const [itemSize, setItemSize] = useState(props.itemSize || 100);\n const buffer = useMemo(() => props.buffer || Math.max(itemSize * 5, 100), [props.buffer, itemSize]);\n const count = props.items.length\n const list = useRef<HTMLDivElement>(null);\n const listInner = useRef<HTMLDivElement>(null);\n const prevScrollTop = useRef(0);\n const scrollToIndexRef = useRef<{ index: number, block: string }>();\n const [shouldScrollToIndex, setShouldScrollToIndex] = useState([]);\n const [scrollTop, setScrollTop] = useState(0);\n const [listSize, setListSize] = useState(props.listSize!);\n const [forceRerender, setForceRerender] = useState([]); // change value to force rerender\n const ignoreUpdateScrollTopOnce = useRef(false);\n // \n const mainCache = useMemo(() => {\n const totalSpace = itemSize * count\n let topSpace = scrollTop - buffer\n let bottomSpace = totalSpace - scrollTop - listSize - buffer\n let startIndex = 0, endIndex = 0\n\n if (topSpace <= 0) {\n topSpace = 0\n startIndex = 0\n } else {\n startIndex = Math.floor(topSpace / itemSize)\n }\n if (bottomSpace < 0) {\n bottomSpace = 0\n }\n if (totalSpace <= listSize) {\n endIndex = count\n } else {\n endIndex = count - Math.floor(bottomSpace / itemSize)\n }\n if (!props.virtual) {\n startIndex = 0\n endIndex = count\n }\n const mainVisibleIndices = Array.from({ length: endIndex - startIndex }, (_, index) => index + startIndex);\n let visibleIndices = mainVisibleIndices.concat(props.persistentIndices || [])\n if (props.persistentIndices?.length) {\n visibleIndices = [...new Set(visibleIndices)].sort((a, b) => a - b)\n }\n const visible = visibleIndices.map(i => props.items[i])\n return { visible, visibleIndices, topSpace, bottomSpace, totalSpace }\n }, [props.items, itemSize, count, scrollTop, buffer, listSize, props.virtual, props.persistentIndices]);\n const { visible, visibleIndices, topSpace, bottomSpace, totalSpace } = mainCache\n\n // \n const listInnerStyle: any = { paddingTop: `${topSpace}px`, boxSizing: 'border-box' }\n if (bottomSpace < itemSize * 5) {\n listInnerStyle['paddingBottom'] = `${bottomSpace}px`\n } else {\n listInnerStyle['height'] = `${totalSpace}px`\n }\n\n useLayoutEffect(() => {\n // list may be null in test environment\n if (!list.current) {\n return\n }\n setListSize(list.current.clientHeight)\n // get avg item size\n if (props.itemSize == null) {\n // get gap\n const listInnerEl = listInner.current as HTMLElement\n let gap = parseFloat(getComputedStyle(listInnerEl).rowGap)\n gap = isNaN(gap) ? 0 : gap\n // \n let count = 0\n let totalHeight = 0\n const persistentIndices = new Set(props.persistentIndices || [])\n let i = -1\n for (const el of listInnerEl.children) {\n i++\n if (persistentIndices.has(visibleIndices[i])) {\n continue\n }\n const style = getComputedStyle(el)\n if (style.display === 'none') {\n continue\n }\n if (style.position !== 'static' && style.position !== 'relative') {\n continue\n }\n totalHeight += (el as HTMLElement).offsetHeight + parseFloat(style.marginTop) + parseFloat(style.marginBottom) + gap\n count++\n }\n setItemSize(totalHeight / count)\n }\n }, [props.itemSize, props.items, forceRerender]);\n //\n\n const handleScroll = function (event: unknown) {\n if (!props.virtual) {\n return\n }\n if (scrollToIndexRef.current) {\n return\n }\n\n setListSize(list.current!.clientHeight)\n\n if (ignoreUpdateScrollTopOnce.current) {\n ignoreUpdateScrollTopOnce.current = false\n } else {\n const scrollTop = list.current!.scrollTop;\n if (Math.abs(prevScrollTop.current - scrollTop) > (props.triggerDistance ?? itemSize)) {\n setScrollTop(scrollTop)\n prevScrollTop.current = scrollTop\n }\n }\n // @ts-ignore\n props.onScroll?.call(this, event)\n }\n // \n useImperativeHandle(ref, () => ({\n scrollToIndex(index, block = 'start') {\n scrollToIndexRef.current = {\n index,\n block\n }\n const scrollTop = index * itemSize // estimated value\n list.current!.scrollTop = scrollTop\n setScrollTop(scrollTop)\n prevScrollTop.current = scrollTop\n setShouldScrollToIndex([]) // ensure re-render but exclude itemSize. setForceRerender will re calculate avg itemSize, so don't use it here.\n },\n getRootElement() {\n return list.current!\n },\n forceUpdate() {\n setForceRerender([])\n },\n }), [itemSize]);\n // scrollToIndex\n useLayoutEffect(() => {\n if (scrollToIndexRef.current) {\n const { index, block } = scrollToIndexRef.current;\n scrollToIndexRef.current = undefined\n const indexInVisible = visibleIndices.indexOf(index)\n const el = listInner.current!.children[indexInVisible] as HTMLElement\n if (el) {\n // @ts-ignore\n el.scrollIntoView({ block })\n ignoreUpdateScrollTopOnce.current = true\n }\n }\n }, [shouldScrollToIndex])\n // use ResizeObserver listen list size change\n useLayoutEffect(() => {\n const { ResizeObserver } = window\n const observer = ResizeObserver && new ResizeObserver(() => {\n setListSize(list.current!.clientHeight)\n })\n // observer is undefined in test environment\n observer?.observe(list.current as HTMLElement)\n return () => {\n observer?.disconnect()\n }\n }, [])\n // \n return <div ref={list} onScroll={handleScroll} className={props.className} style={{ overflow: 'auto', ...props.style }}>\n {props.renderHead?.()}\n <div ref={listInner} style={{ display: 'flex', flexDirection: 'column', ...(props.virtual && listInnerStyle) }}>\n {visible.map((item, i) => props.renderItem(item, visibleIndices[i]))}\n </div>\n {props.renderFoot?.()}\n </div>\n})\n\n// @ts-ignore\nVirtualList.defaultProps = defaultProps\n"],"names":["forwardRef","React","defaultProps","VirtualList","props","ref","itemSize","setItemSize","useState","buffer","useMemo","count","list","useRef","listInner","prevScrollTop","scrollToIndexRef","shouldScrollToIndex","setShouldScrollToIndex","scrollTop","setScrollTop","listSize","setListSize","forceRerender","setForceRerender","ignoreUpdateScrollTopOnce","mainCache","totalSpace","topSpace","bottomSpace","startIndex","endIndex","visibleIndices","_","index","_a","a","b","i","visible","listInnerStyle","useLayoutEffect","listInnerEl","gap","totalHeight","persistentIndices","el","style","handleScroll","event","useImperativeHandle","block","indexInVisible","ResizeObserver","observer","jsx","item","_b"],"mappings":"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"}
1
+ {"version":3,"file":"index.iife.js","sources":["../lib/VirtualList.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo, useRef, ReactNode, useLayoutEffect, useImperativeHandle\n} from 'react';\n\n// fix forwardRef type for generic types. refer: https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref\nexport type FixedForwardRef = < T, P = {} > (\n render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,\n) => (props: P & React.RefAttributes<T>) => React.ReactElement | null;\nconst forwardRef = React.forwardRef as FixedForwardRef\n\nexport type VirtualListProps<ITEM> = {\n /**\n * Estimated average size of each list item.\n */\n itemSize?: number,\n /**\n * render space = list visible space + buffer x 2\n */\n buffer?: number,\n /**\n * List of items to render.\n */\n items: ITEM[],\n /**\n * Render function for each list item.\n */\n renderItem: (item: ITEM, index: number) => ReactNode,\n /**\n * These items won't be removed when scroll. You can use css 'position:sticky' make them sticky.\n */\n persistentIndices?: number[], // index[]\n /**\n * Minimum distance for triggering a calculation when scrolling.\n */\n triggerDistance?: number,\n /**\n * listen to scroll event.\n */\n onScroll?: React.UIEventHandler<HTMLElement>,\n /**\n * Insert elements at the head. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.\n */\n renderHead?: () => ReactNode,\n /**\n * Insert elements at the foot. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.\n */\n renderFoot?: () => ReactNode,\n className?: string,\n style?: React.CSSProperties,\n innerClassName?: string,\n} & Partial<typeof defaultProps>\n\nexport const defaultProps = {\n /**\n * The visible space of the list. It is only used before DOM created(SSR).\n */\n listSize: 1000,\n /**\n * Whether to enable the virtual list feature.\n */\n virtual: true,\n}\n\nexport interface VirtualListHandle {\n scrollToIndex(index: number, block?: 'start' | 'end' | 'center' | 'nearest'): void\n getRootElement(): HTMLElement\n forceUpdate(): void\n}\n\nexport const VirtualList = forwardRef(function <ITEM>(\n props: VirtualListProps<ITEM>,\n ref: React.ForwardedRef<VirtualListHandle>\n) {\n const [itemSize, setItemSize] = useState(props.itemSize || 100);\n const buffer = useMemo(() => props.buffer || Math.max(itemSize * 5, 100), [props.buffer, itemSize]);\n const count = props.items.length\n const list = useRef<HTMLDivElement>(null);\n const listInner = useRef<HTMLDivElement>(null);\n const prevScrollTop = useRef(0);\n const scrollToIndexRef = useRef<{ index: number, block: string }>();\n const [shouldScrollToIndex, setShouldScrollToIndex] = useState([]);\n const [scrollTop, setScrollTop] = useState(0);\n const [listSize, setListSize] = useState(props.listSize!);\n const [forceRerender, setForceRerender] = useState([]); // change value to force rerender\n const ignoreUpdateScrollTopOnce = useRef(false);\n // \n const mainCache = useMemo(() => {\n const totalSpace = itemSize * count\n let topSpace = scrollTop - buffer\n let bottomSpace = totalSpace - scrollTop - listSize - buffer\n let startIndex = 0, endIndex = 0\n\n if (topSpace <= 0) {\n topSpace = 0\n startIndex = 0\n } else {\n startIndex = Math.floor(topSpace / itemSize)\n }\n if (bottomSpace < 0) {\n bottomSpace = 0\n }\n if (totalSpace <= listSize) {\n endIndex = count\n } else {\n endIndex = count - Math.floor(bottomSpace / itemSize)\n }\n if (!props.virtual) {\n startIndex = 0\n endIndex = count\n }\n const mainVisibleIndices = Array.from({ length: endIndex - startIndex }, (_, index) => index + startIndex);\n let visibleIndices = mainVisibleIndices.concat(props.persistentIndices || [])\n if (props.persistentIndices?.length) {\n visibleIndices = [...new Set(visibleIndices)].sort((a, b) => a - b)\n }\n const visible = visibleIndices.map(i => props.items[i])\n return { visible, visibleIndices, topSpace, bottomSpace, totalSpace }\n }, [props.items, itemSize, count, scrollTop, buffer, listSize, props.virtual, props.persistentIndices]);\n const { visible, visibleIndices, topSpace, bottomSpace, totalSpace } = mainCache\n\n // \n const listInnerStyle: any = { paddingTop: `${topSpace}px`, boxSizing: 'border-box' }\n if (bottomSpace < itemSize * 5) {\n listInnerStyle['paddingBottom'] = `${bottomSpace}px`\n } else {\n listInnerStyle['height'] = `${totalSpace}px`\n }\n\n useLayoutEffect(() => {\n // list may be null in test environment\n if (!list.current) {\n return\n }\n setListSize(list.current.clientHeight)\n // get avg item size\n if (props.itemSize == null) {\n // get gap\n const listInnerEl = listInner.current as HTMLElement\n let gap = parseFloat(getComputedStyle(listInnerEl).rowGap)\n gap = isNaN(gap) ? 0 : gap\n // \n let count = 0\n let totalHeight = 0\n const persistentIndices = new Set(props.persistentIndices || [])\n let i = -1\n for (const el of listInnerEl.children) {\n i++\n if (persistentIndices.has(visibleIndices[i])) {\n continue\n }\n const style = getComputedStyle(el)\n if (style.display === 'none') {\n continue\n }\n if (style.position !== 'static' && style.position !== 'relative') {\n continue\n }\n totalHeight += (el as HTMLElement).offsetHeight + parseFloat(style.marginTop) + parseFloat(style.marginBottom) + gap\n count++\n }\n setItemSize(totalHeight / count)\n }\n }, [props.itemSize, props.items, forceRerender]);\n //\n\n const handleScroll = function (event: unknown) {\n if (!props.virtual) {\n return\n }\n if (scrollToIndexRef.current) {\n return\n }\n\n setListSize(list.current!.clientHeight)\n\n if (ignoreUpdateScrollTopOnce.current) {\n ignoreUpdateScrollTopOnce.current = false\n } else {\n const scrollTop = list.current!.scrollTop;\n if (Math.abs(prevScrollTop.current - scrollTop) > (props.triggerDistance ?? itemSize)) {\n setScrollTop(scrollTop)\n prevScrollTop.current = scrollTop\n }\n }\n // @ts-ignore\n props.onScroll?.call(this, event)\n }\n // \n useImperativeHandle(ref, () => ({\n scrollToIndex(index, block = 'start') {\n scrollToIndexRef.current = {\n index,\n block\n }\n const scrollTop = index * itemSize // estimated value\n list.current!.scrollTop = scrollTop\n setScrollTop(scrollTop)\n prevScrollTop.current = scrollTop\n setShouldScrollToIndex([]) // ensure re-render but exclude itemSize. setForceRerender will re calculate avg itemSize, so don't use it here.\n },\n getRootElement() {\n return list.current!\n },\n forceUpdate() {\n setForceRerender([])\n },\n }), [itemSize]);\n // scrollToIndex\n useLayoutEffect(() => {\n if (scrollToIndexRef.current) {\n const { index, block } = scrollToIndexRef.current;\n scrollToIndexRef.current = undefined\n const indexInVisible = visibleIndices.indexOf(index)\n const el = listInner.current!.children[indexInVisible] as HTMLElement\n if (el) {\n // @ts-ignore\n el.scrollIntoView({ block })\n ignoreUpdateScrollTopOnce.current = true\n }\n }\n }, [shouldScrollToIndex])\n // use ResizeObserver listen list size change\n useLayoutEffect(() => {\n const { ResizeObserver } = window\n const observer = ResizeObserver && new ResizeObserver(() => {\n setListSize(list.current!.clientHeight)\n })\n // observer is undefined in test environment\n observer?.observe(list.current as HTMLElement)\n return () => {\n observer?.disconnect()\n }\n }, [])\n // \n return <div ref={list} onScroll={handleScroll} className={props.className} style={{ overflow: 'auto', ...props.style }}>\n {props.renderHead?.()}\n <div ref={listInner} className={props.innerClassName} style={{ display: 'flex', flexDirection: 'column', ...(props.virtual && listInnerStyle) }}>\n {visible.map((item, i) => props.renderItem(item, visibleIndices[i]))}\n </div>\n {props.renderFoot?.()}\n </div>\n})\n\n// @ts-ignore\nVirtualList.defaultProps = defaultProps\n"],"names":["forwardRef","React","defaultProps","VirtualList","props","ref","itemSize","setItemSize","useState","buffer","useMemo","count","list","useRef","listInner","prevScrollTop","scrollToIndexRef","shouldScrollToIndex","setShouldScrollToIndex","scrollTop","setScrollTop","listSize","setListSize","forceRerender","setForceRerender","ignoreUpdateScrollTopOnce","mainCache","totalSpace","topSpace","bottomSpace","startIndex","endIndex","visibleIndices","_","index","_a","a","b","i","visible","listInnerStyle","useLayoutEffect","listInnerEl","gap","totalHeight","persistentIndices","el","style","handleScroll","event","useImperativeHandle","block","indexInVisible","ResizeObserver","observer","jsx","item","_b"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,16 +15,16 @@ const Q = J.forwardRef, W = {
15
15
  */
16
16
  virtual: !0
17
17
  }, X = Q(function(e, L) {
18
- var V, N;
19
- const [c, O] = d(e.itemSize || 100), h = E(() => e.buffer || Math.max(c * 5, 100), [e.buffer, c]), u = e.items.length, r = m(null), I = m(null), g = m(0), f = m(), [$, j] = d([]), [b, F] = d(0), [x, v] = d(e.listSize), [B, D] = d([]), T = m(!1), P = E(() => {
18
+ var M, V;
19
+ const [c, O] = d(e.itemSize || 100), h = E(() => e.buffer || Math.max(c * 5, 100), [e.buffer, c]), u = e.items.length, r = m(null), I = m(null), g = m(0), f = m(), [$, j] = d([]), [b, N] = d(0), [x, v] = d(e.listSize), [B, D] = d([]), T = m(!1), P = E(() => {
20
20
  var C;
21
21
  const n = c * u;
22
22
  let t = b - h, i = n - b - x - h, l = 0, s = 0;
23
23
  t <= 0 ? (t = 0, l = 0) : l = Math.floor(t / c), i < 0 && (i = 0), n <= x ? s = u : s = u - Math.floor(i / c), e.virtual || (l = 0, s = u);
24
24
  let o = Array.from({ length: s - l }, (S, R) => R + l).concat(e.persistentIndices || []);
25
25
  return (C = e.persistentIndices) != null && C.length && (o = [...new Set(o)].sort((S, R) => S - R)), { visible: o.map((S) => e.items[S]), visibleIndices: o, topSpace: t, bottomSpace: i, totalSpace: n };
26
- }, [e.items, c, u, b, h, x, e.virtual, e.persistentIndices]), { visible: U, visibleIndices: y, topSpace: k, bottomSpace: M, totalSpace: A } = P, z = { paddingTop: `${k}px`, boxSizing: "border-box" };
27
- M < c * 5 ? z.paddingBottom = `${M}px` : z.height = `${A}px`, H(() => {
26
+ }, [e.items, c, u, b, h, x, e.virtual, e.persistentIndices]), { visible: U, visibleIndices: y, topSpace: k, bottomSpace: F, totalSpace: A } = P, z = { paddingTop: `${k}px`, boxSizing: "border-box" };
27
+ F < c * 5 ? z.paddingBottom = `${F}px` : z.height = `${A}px`, H(() => {
28
28
  if (r.current && (v(r.current.clientHeight), e.itemSize == null)) {
29
29
  const n = I.current;
30
30
  let t = parseFloat(getComputedStyle(n).rowGap);
@@ -48,7 +48,7 @@ const Q = J.forwardRef, W = {
48
48
  T.current = !1;
49
49
  else {
50
50
  const i = r.current.scrollTop;
51
- Math.abs(g.current - i) > (e.triggerDistance ?? c) && (F(i), g.current = i);
51
+ Math.abs(g.current - i) > (e.triggerDistance ?? c) && (N(i), g.current = i);
52
52
  }
53
53
  (t = e.onScroll) == null || t.call(this, n);
54
54
  }
@@ -60,7 +60,7 @@ const Q = J.forwardRef, W = {
60
60
  block: t
61
61
  };
62
62
  const i = n * c;
63
- r.current.scrollTop = i, F(i), g.current = i, j([]);
63
+ r.current.scrollTop = i, N(i), g.current = i, j([]);
64
64
  },
65
65
  getRootElement() {
66
66
  return r.current;
@@ -83,9 +83,9 @@ const Q = J.forwardRef, W = {
83
83
  t == null || t.disconnect();
84
84
  };
85
85
  }, []), /* @__PURE__ */ _("div", { ref: r, onScroll: G, className: e.className, style: { overflow: "auto", ...e.style }, children: [
86
- (V = e.renderHead) == null ? void 0 : V.call(e),
87
- /* @__PURE__ */ q("div", { ref: I, style: { display: "flex", flexDirection: "column", ...e.virtual && z }, children: U.map((n, t) => e.renderItem(n, y[t])) }),
88
- (N = e.renderFoot) == null ? void 0 : N.call(e)
86
+ (M = e.renderHead) == null ? void 0 : M.call(e),
87
+ /* @__PURE__ */ q("div", { ref: I, className: e.innerClassName, style: { display: "flex", flexDirection: "column", ...e.virtual && z }, children: U.map((n, t) => e.renderItem(n, y[t])) }),
88
+ (V = e.renderFoot) == null ? void 0 : V.call(e)
89
89
  ] });
90
90
  });
91
91
  X.defaultProps = W;
@@ -1,4 +1,5 @@
1
- import React, { ReactNode } from 'react';
1
+ import { default as React, ReactNode } from 'react';
2
+
2
3
  export type FixedForwardRef = <T, P = {}>(render: (props: P, ref: React.Ref<T>) => React.ReactElement | null) => (props: P & React.RefAttributes<T>) => React.ReactElement | null;
3
4
  export type VirtualListProps<ITEM> = {
4
5
  /**
@@ -39,6 +40,7 @@ export type VirtualListProps<ITEM> = {
39
40
  renderFoot?: () => ReactNode;
40
41
  className?: string;
41
42
  style?: React.CSSProperties;
43
+ innerClassName?: string;
42
44
  } & Partial<typeof defaultProps>;
43
45
  export declare const defaultProps: {
44
46
  /**
@@ -59,11 +61,11 @@ export declare const VirtualList: <ITEM>(props: {
59
61
  /**
60
62
  * Estimated average size of each list item.
61
63
  */
62
- itemSize?: number | undefined;
64
+ itemSize?: number;
63
65
  /**
64
66
  * render space = list visible space + buffer x 2
65
67
  */
66
- buffer?: number | undefined;
68
+ buffer?: number;
67
69
  /**
68
70
  * List of items to render.
69
71
  */
@@ -75,25 +77,26 @@ export declare const VirtualList: <ITEM>(props: {
75
77
  /**
76
78
  * These items won't be removed when scroll. You can use css 'position:sticky' make them sticky.
77
79
  */
78
- persistentIndices?: number[] | undefined;
80
+ persistentIndices?: number[];
79
81
  /**
80
82
  * Minimum distance for triggering a calculation when scrolling.
81
83
  */
82
- triggerDistance?: number | undefined;
84
+ triggerDistance?: number;
83
85
  /**
84
86
  * listen to scroll event.
85
87
  */
86
- onScroll?: React.UIEventHandler<HTMLElement> | undefined;
88
+ onScroll?: React.UIEventHandler<HTMLElement>;
87
89
  /**
88
90
  * Insert elements at the head. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.
89
91
  */
90
- renderHead?: (() => ReactNode) | undefined;
92
+ renderHead?: () => ReactNode;
91
93
  /**
92
94
  * Insert elements at the foot. Recommended to only insert elements that do not take up space or take very little space, such as position absolute.
93
95
  */
94
- renderFoot?: (() => ReactNode) | undefined;
95
- className?: string | undefined;
96
- style?: React.CSSProperties | undefined;
96
+ renderFoot?: () => ReactNode;
97
+ className?: string;
98
+ style?: React.CSSProperties;
99
+ innerClassName?: string;
97
100
  } & Partial<{
98
101
  /**
99
102
  * The visible space of the list. It is only used before DOM created(SSR).
@@ -1 +1 @@
1
- export * from "./VirtualList";
1
+ export * from './VirtualList';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-base-virtual-list",
3
- "version": "1.0.1",
3
+ "version": "1.2.4",
4
4
  "license": "MIT",
5
5
  "author": "phphe <phphe@outlook.com> (https://github.com/phphe)",
6
6
  "description": "React base virtual list component.",
@@ -26,21 +26,12 @@
26
26
  "files": [
27
27
  "dist"
28
28
  ],
29
- "scripts": {
30
- "dev": "vite",
31
- "build": "vite build --config vite.build.js && vite build --config vite.build.js -- --iife && rm -rf dist/src",
32
- "build:web": "tsc && vite build",
33
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
34
- "preview": "vite preview"
35
- },
36
- "dependencies": {},
37
29
  "peerDependencies": {
38
- "react": "^18",
39
- "react-dom": "^18"
30
+ "react": "^18 || ^19",
31
+ "react-dom": "^18 || ^19"
40
32
  },
41
33
  "devDependencies": {
42
- "react": "^18.2.0",
43
- "react-dom": "^18.2.0",
34
+ "@changesets/cli": "^2.29.8",
44
35
  "@types/react": "^18.2.43",
45
36
  "@types/react-dom": "^18.2.17",
46
37
  "@typescript-eslint/eslint-plugin": "^6.14.0",
@@ -50,9 +41,24 @@
50
41
  "eslint": "^8.55.0",
51
42
  "eslint-plugin-react-hooks": "^4.6.0",
52
43
  "eslint-plugin-react-refresh": "^0.4.5",
44
+ "react": "^18.2.0",
45
+ "react-dom": "^18.2.0",
53
46
  "typescript": "^5.2.2",
54
47
  "unocss": "^0.58.4",
55
48
  "vite": "^5.0.8",
56
49
  "vite-plugin-dts": "^3.7.2"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public",
53
+ "provenance": true
54
+ },
55
+ "scripts": {
56
+ "dev": "vite",
57
+ "build": "vite build --config vite.build.js && vite build --config vite.build.js -- --iife && rm -rf dist/src",
58
+ "demo:build": "tsc && vite build",
59
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
60
+ "preview": "vite preview",
61
+ "change": "changeset",
62
+ "version": "changeset version"
57
63
  }
58
- }
64
+ }