react-base-virtual-list 1.0.1 → 1.1.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 CHANGED
@@ -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
 
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":";;;;yDASA,MAAMA,EAAaC,EAAM,WA2CZC,EAAe,CAI1B,SAAU,IAIV,QAAS,EACX,EAQaC,EAAcH,EAAW,SACpCI,EACAC,EACA,SACA,KAAM,CAACC,EAAUC,CAAW,EAAIC,EAAS,SAAAJ,EAAM,UAAY,GAAG,EACxDK,EAASC,EAAAA,QAAQ,IAAMN,EAAM,QAAU,KAAK,IAAIE,EAAW,EAAG,GAAG,EAAG,CAACF,EAAM,OAAQE,CAAQ,CAAC,EAC5FK,EAAQP,EAAM,MAAM,OACpBQ,EAAOC,SAAuB,IAAI,EAClCC,EAAYD,SAAuB,IAAI,EACvCE,EAAgBF,SAAO,CAAC,EACxBG,EAAmBH,EAAAA,SACnB,CAACI,EAAqBC,CAAsB,EAAIV,EAAA,SAAS,CAAE,CAAA,EAC3D,CAACW,EAAWC,CAAY,EAAIZ,WAAS,CAAC,EACtC,CAACa,EAAUC,CAAW,EAAId,EAAAA,SAASJ,EAAM,QAAS,EAClD,CAACmB,EAAeC,CAAgB,EAAIhB,EAAA,SAAS,CAAE,CAAA,EAC/CiB,EAA4BZ,SAAO,EAAK,EAExCa,EAAYhB,EAAAA,QAAQ,IAAM,OAC9B,MAAMiB,EAAarB,EAAWK,EAC9B,IAAIiB,EAAWT,EAAYV,EACvBoB,EAAcF,EAAaR,EAAYE,EAAWZ,EAClDqB,EAAa,EAAGC,EAAW,EAE3BH,GAAY,GACdA,EAAW,EACEE,EAAA,GAEAA,EAAA,KAAK,MAAMF,EAAWtB,CAAQ,EAEzCuB,EAAc,IAChBA,EAAc,GAEZF,GAAcN,EACLU,EAAApB,EAEXoB,EAAWpB,EAAQ,KAAK,MAAMkB,EAAcvB,CAAQ,EAEjDF,EAAM,UACI0B,EAAA,EACFC,EAAApB,GAGb,IAAIqB,EADuB,MAAM,KAAK,CAAE,OAAQD,EAAWD,CAAW,EAAG,CAACG,EAAGC,IAAUA,EAAQJ,CAAU,EACjE,OAAO1B,EAAM,mBAAqB,CAAA,CAAE,EACxE,OAAA+B,EAAA/B,EAAM,oBAAN,MAAA+B,EAAyB,SAC3BH,EAAiB,CAAC,GAAG,IAAI,IAAIA,CAAc,CAAC,EAAE,KAAK,CAACI,EAAGC,IAAMD,EAAIC,CAAC,GAG7D,CAAE,QADOL,EAAe,OAAS5B,EAAM,MAAMkC,CAAC,CAAC,EACpC,eAAAN,EAAgB,SAAAJ,EAAU,YAAAC,EAAa,WAAAF,CAAW,CACnE,EAAA,CAACvB,EAAM,MAAOE,EAAUK,EAAOQ,EAAWV,EAAQY,EAAUjB,EAAM,QAASA,EAAM,iBAAiB,CAAC,EAChG,CAAE,QAAAmC,EAAS,eAAAP,EAAgB,SAAAJ,EAAU,YAAAC,EAAa,WAAAF,CAAe,EAAAD,EAGjEc,EAAsB,CAAE,WAAY,GAAGZ,CAAQ,KAAM,UAAW,cAClEC,EAAcvB,EAAW,EACZkC,EAAA,cAAmB,GAAGX,CAAW,KAEjCW,EAAA,OAAY,GAAGb,CAAU,KAG1Cc,EAAAA,gBAAgB,IAAM,CAEhB,GAAC7B,EAAK,UAGEU,EAAAV,EAAK,QAAQ,YAAY,EAEjCR,EAAM,UAAY,MAAM,CAE1B,MAAMsC,EAAc5B,EAAU,QAC9B,IAAI6B,EAAM,WAAW,iBAAiBD,CAAW,EAAE,MAAM,EACnDC,EAAA,MAAMA,CAAG,EAAI,EAAIA,EAEvB,IAAIhC,EAAQ,EACRiC,EAAc,EAClB,MAAMC,EAAoB,IAAI,IAAIzC,EAAM,mBAAqB,CAAE,CAAA,EAC/D,IAAIkC,EAAI,GACG,UAAAQ,KAAMJ,EAAY,SAAU,CAErC,GADAJ,IACIO,EAAkB,IAAIb,EAAeM,CAAC,CAAC,EACzC,SAEI,MAAAS,EAAQ,iBAAiBD,CAAE,EAC7BC,EAAM,UAAY,SAGlBA,EAAM,WAAa,UAAYA,EAAM,WAAa,aAGtCH,GAAAE,EAAmB,aAAe,WAAWC,EAAM,SAAS,EAAI,WAAWA,EAAM,YAAY,EAAIJ,EACjHhC,KACF,CACAJ,EAAYqC,EAAcjC,CAAK,CACjC,CAAA,EACC,CAACP,EAAM,SAAUA,EAAM,MAAOmB,CAAa,CAAC,EAGzC,MAAAyB,EAAe,SAAUC,EAAgB,OACzC,GAAC7C,EAAM,SAGP,CAAAY,EAAiB,QAMrB,IAFYM,EAAAV,EAAK,QAAS,YAAY,EAElCa,EAA0B,QAC5BA,EAA0B,QAAU,OAC/B,CACCN,MAAAA,EAAYP,EAAK,QAAS,UAC5B,KAAK,IAAIG,EAAc,QAAUI,CAAS,GAAKf,EAAM,iBAAmBE,KAC1Ec,EAAaD,CAAS,EACtBJ,EAAc,QAAUI,EAE5B,EAEMgB,EAAA/B,EAAA,WAAA,MAAA+B,EAAU,KAAK,KAAMc,GAAK,EAGlCC,OAAAA,EAAA,oBAAoB7C,EAAK,KAAO,CAC9B,cAAc6B,EAAOiB,EAAQ,QAAS,CACpCnC,EAAiB,QAAU,CACzB,MAAAkB,EACA,MAAAiB,CAAA,EAEF,MAAMhC,EAAYe,EAAQ5B,EAC1BM,EAAK,QAAS,UAAYO,EAC1BC,EAAaD,CAAS,EACtBJ,EAAc,QAAUI,EACxBD,EAAuB,CAAE,CAAA,CAC3B,EACA,gBAAiB,CACf,OAAON,EAAK,OACd,EACA,aAAc,CACZY,EAAiB,CAAE,CAAA,CACrB,CAAA,GACE,CAAClB,CAAQ,CAAC,EAEdmC,EAAAA,gBAAgB,IAAM,CACpB,GAAIzB,EAAiB,QAAS,CAC5B,KAAM,CAAE,MAAAkB,EAAO,MAAAiB,GAAUnC,EAAiB,QAC1CA,EAAiB,QAAU,OACrB,MAAAoC,EAAiBpB,EAAe,QAAQE,CAAK,EAC7CY,EAAKhC,EAAU,QAAS,SAASsC,CAAc,EACjDN,IAECA,EAAA,eAAe,CAAE,MAAAK,CAAA,CAAO,EAC3B1B,EAA0B,QAAU,GAExC,CAAA,EACC,CAACR,CAAmB,CAAC,EAExBwB,EAAAA,gBAAgB,IAAM,CACd,KAAA,CAAE,eAAAY,CAAmB,EAAA,OACrBC,EAAWD,GAAkB,IAAIA,EAAe,IAAM,CAC9C/B,EAAAV,EAAK,QAAS,YAAY,CAAA,CACvC,EAES,OAAA0C,GAAA,MAAAA,EAAA,QAAQ1C,EAAK,SAChB,IAAM,CACX0C,GAAA,MAAAA,EAAU,YAAW,CAEzB,EAAG,CAAE,CAAA,SAEG,MAAI,CAAA,IAAK1C,EAAM,SAAUoC,EAAc,UAAW5C,EAAM,UAAW,MAAO,CAAE,SAAU,OAAQ,GAAGA,EAAM,KAC5G,EAAA,SAAA,EAAA+B,EAAA/B,EAAM,aAAN,YAAA+B,EAAA,KAAA/B,GACDmD,EAAAA,IAAC,MAAI,CAAA,IAAKzC,EAAW,MAAO,CAAE,QAAS,OAAQ,cAAe,SAAU,GAAIV,EAAM,SAAWoC,GAC1F,SAAQD,EAAA,IAAI,CAACiB,EAAMlB,IAAMlC,EAAM,WAAWoD,EAAMxB,EAAeM,CAAC,CAAC,CAAC,CACrE,CAAA,GACCmB,EAAArD,EAAM,aAAN,YAAAqD,EAAA,KAAArD,EACH,CAAA,CAAA,CACF,CAAC,EAGD,OAAAD,EAAY,aAAeD"}
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.1.0",
4
4
  "license": "MIT",
5
5
  "author": "phphe <phphe@outlook.com> (https://github.com/phphe)",
6
6
  "description": "React base virtual list component.",
@@ -55,4 +55,4 @@
55
55
  "vite": "^5.0.8",
56
56
  "vite-plugin-dts": "^3.7.2"
57
57
  }
58
- }
58
+ }