react-layout-virtual 0.0.7 → 0.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.
@@ -3,11 +3,5 @@
3
3
  * @license MIT
4
4
  * @author Alexandr Kalabin
5
5
  */
6
- import { type ListItemProps } from './ReactRenderer';
7
- export interface VirtualizedListReactProps<T> {
8
- scrollerRef?: React.RefObject<HTMLElement>;
9
- overscanHeight?: number;
10
- data: T[];
11
- renderItem: (props: ListItemProps<T>) => React.ReactNode;
12
- }
6
+ import type { VirtualizedListReactProps } from './types';
13
7
  export default function VirtualizedListReact<ItemData = unknown>(props: VirtualizedListReactProps<ItemData>): import("react/jsx-runtime").JSX.Element;
@@ -4,20 +4,9 @@
4
4
  * @author Alexandr Kalabin
5
5
  */
6
6
  import { BaseRenderer } from 'layout-virtual/core';
7
- import type { IRangeRenderer, ScrollDirection, VirtualScrollStructure } from "layout-virtual/types";
8
- export interface ListItemProps<T = unknown> {
9
- data: T;
10
- ref: React.Ref<HTMLDivElement> | undefined;
11
- index: number;
12
- }
13
- export type ItemRenderer<T> = React.FC<ListItemProps<T>>;
14
- type ReactRendererOptions = {
15
- itemsSetter: React.Dispatch<React.SetStateAction<React.ReactNode[]>>;
16
- } & VirtualScrollStructure;
17
- interface IReactRenderer {
18
- commit: () => void;
19
- }
20
- export default class ReactRenderer<DataType = unknown> extends BaseRenderer implements IRangeRenderer<DataType, ItemRenderer<DataType>>, IReactRenderer {
7
+ import type { IRangeRenderer, ScrollDirection } from "layout-virtual/types";
8
+ import type { ItemRenderer, ReactRendererOptions } from './types';
9
+ export default class ReactRenderer<DataType = unknown> extends BaseRenderer implements IRangeRenderer<DataType, ItemRenderer<DataType>> {
21
10
  private _store;
22
11
  private _renderItem;
23
12
  private _itemsSetter;
@@ -37,4 +26,3 @@ export default class ReactRenderer<DataType = unknown> extends BaseRenderer impl
37
26
  commit(): void;
38
27
  get dataSize(): number;
39
28
  }
40
- export {};
package/dist/index.d.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * @author Alexandr Kalabin
5
5
  */
6
6
  export { default } from './ReactLayoutVirtual';
7
- export { type ListItemProps } from './ReactRenderer';
7
+ export type { ListItemProps, VirtualizedListReactProps, VirtualizedListReactClasses } from './types';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{useRef as n,useState as i,useLayoutEffect as s,useEffect as o}from"react";import{flushSync as l,createPortal as c}from"react-dom";import{BaseRenderer as h,DynamicListLayout as a,LayoutVirtual as d}from"layout-virtual/core";class u extends h{t=[];i=null;o;l=/* @__PURE__ */new Map;h=[];u=()=>{this.o(this.h)};constructor(e){super(e),this.o=e.itemsSetter}renderRange(t,r,n){const i=this.t,s=this.h,o=[],l=this.l;for(let c=t;c<=r;c++){const t=i[c];if(t){const r=this.i;if(r){const n=l.get(c)||{current:null,idx:c};l.set(c,n),o.push(/* @__PURE__ */e(r,{data:t,ref:n,index:c},c))}}}this.h="down"===n?s.concat(o):"up"===n?o.concat(s):this.h}removeRange(e,t,r){const n=super.removeRange(e,t),i=n.itemsToRemove.length;return i&&(this.h="down"===r?this.h.slice(i):"up"===r?this.h.slice(0,-i):this.h),n}clear(){super.clear(),this.h=[],this.o(this.h)}setData(e){this.t=e}setRenderItem(e){this.i=e}flush(){return l(this.u),Promise.resolve()}commit(){const e=this.l;for(const t of e.values()){const{idx:e,current:r}=t;r&&this.registerElement(e,r)}e.clear()}get dataSize(){return this.t.length}}function f(l){const{overscanHeight:h=200,data:f,renderItem:m,scrollerRef:p}=l,v=n(null),g=n(null),w=n(null),x=n(null),R=n(null),_=n(null),I=n(null),[S,y]=i([]),H=n(void 0);s(()=>{H.current=new u({container:p?.current||v.current,scrollHeightFiller:g.current,viewportContainer:w.current,scrollCanvas:x.current,topSpacer:R.current,contentLayer:_.current,bottomSpacer:I.current,itemsSetter:y});const e=new a({overscanHeight:h,renderer:H.current}),t=new d({layout:e});t.setData(f),t.setRenderItem(m)},[]),o(()=>{H.current?.commit()},[S]);const C=/* @__PURE__ */t(r,{children:[
2
- /* @__PURE__ */e("div",{ref:g}),
3
- /* @__PURE__ */e("div",{ref:w,children:/* @__PURE__ */t("div",{ref:x,children:[
1
+ import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{useRef as s,useState as n,useLayoutEffect as i,useEffect as o}from"react";import{flushSync as l,createPortal as c}from"react-dom";import{BaseRenderer as a,DynamicListLayout as h,LayoutVirtual as d}from"layout-virtual/core";class u extends a{t=[];i=null;o;l=/* @__PURE__ */new Map;h=[];u=()=>{this.o(this.h)};constructor(e){super(e),this.o=e.itemsSetter}renderRange(t,r,s){const n=this.t,i=this.h,o=[],l=this.l;for(let c=t;c<=r;c++){const t=n[c];if(t){const r=this.i;if(r){const s=l.get(c)||{current:null,idx:c};l.set(c,s),o.push(/* @__PURE__ */e(r,{data:t,ref:s,index:c},c))}}}this.h="down"===s?i.concat(o):"up"===s?o.concat(i):this.h}removeRange(e,t,r){const s=super.removeRange(e,t),n=s.itemsToRemove.length;return n&&(this.h="down"===r?this.h.slice(n):"up"===r?this.h.slice(0,-n):this.h),s}clear(){super.clear(),this.h=[],this.o(this.h)}setData(e){this.t=e}setRenderItem(e){this.i=e}flush(){return l(this.u),Promise.resolve()}commit(){const e=this.l;for(const t of e.values()){const{idx:e,current:r}=t;r&&this.registerElement(e,r)}e.clear()}get dataSize(){return this.t.length}}function m(l){const{overscanHeight:a=200,data:m,renderItem:f,scrollerRef:p}=l,{scrollerClass:v,viewportClass:w,contentLayerClass:g}=l,x=s(null),R=s(null),_=s(null),C=s(null),I=s(null),S=s(null),y=s(null),[H,N]=n([]),L=s(void 0);i(()=>{L.current=new u({container:p?.current||x.current,scrollHeightFiller:R.current,viewportContainer:_.current,scrollCanvas:C.current,topSpacer:I.current,contentLayer:S.current,bottomSpacer:y.current,itemsSetter:N});const e=new h({overscanHeight:a,renderer:L.current}),t=new d({layout:e});t.setData(m),t.setRenderItem(f);const r=p?.current;r&&r.classList.add(v||"")},[]),o(()=>{L.current?.commit()},[H]);const P=/* @__PURE__ */t(r,{children:[
4
2
  /* @__PURE__ */e("div",{ref:R}),
5
- /* @__PURE__ */e("div",{ref:_,children:S}),
6
- /* @__PURE__ */e("div",{ref:I})]})})]});return p&&p.current?c(C,p.current):/* @__PURE__ */e("div",{ref:v,children:C})}export{f as default};
3
+ /* @__PURE__ */e("div",{className:w,ref:_,children:/* @__PURE__ */t("div",{ref:C,children:[
4
+ /* @__PURE__ */e("div",{ref:I}),
5
+ /* @__PURE__ */e("div",{className:g,ref:S,children:H}),
6
+ /* @__PURE__ */e("div",{ref:y})]})})]});return p&&p.current?c(P,p.current):/* @__PURE__ */e("div",{className:v,ref:x,children:P})}export{m as default};
7
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/ReactRenderer.tsx","../src/ReactLayoutVirtual.tsx"],"sourcesContent":["/**\n * @fileoverview ReactRenderer.\n * @license MIT\n * @author Alexandr Kalabin\n */\n\nimport { flushSync } from 'react-dom';\nimport { BaseRenderer } from 'layout-virtual/core';\nimport type { IRangeRenderer, ScrollDirection, VirtualScrollStructure } from \"layout-virtual/types\";\n\nexport interface ListItemProps<T = unknown> {\n data: T;\n ref: React.Ref<HTMLDivElement> | undefined;\n index: number;\n}\n\nexport type ItemRenderer<T> = React.FC<ListItemProps<T>>;\n\ntype ReactRendererOptions = {\n itemsSetter: React.Dispatch<React.SetStateAction<React.ReactNode[]>>;\n} & VirtualScrollStructure;\n\ninterface IReactRenderer {\n commit: () => void;\n}\n\ntype IndexedRef = React.RefObject<HTMLDivElement | null> & { idx: number };\n\nexport default class ReactRenderer<DataType = unknown> extends BaseRenderer implements IRangeRenderer<DataType, ItemRenderer<DataType>>, IReactRenderer {\n private _store: DataType[] = [];\n private _renderItem: ItemRenderer<DataType> | null = null;\n private _itemsSetter: React.Dispatch<React.SetStateAction<React.ReactNode[]>>;\n private _renderedRangeRefPool = new Map<number, IndexedRef>();\n private _listItems: React.ReactNode[] = [];\n private _flushItems = () => { this._itemsSetter(this._listItems); };\n\n constructor(opts: ReactRendererOptions) {\n super(opts);\n this._itemsSetter = opts.itemsSetter;\n }\n\n renderRange(startIndex: number, endIndex: number, direction: ScrollDirection) {\n const store = this._store;\n const listItems = this._listItems;\n const itemsToAdd: React.ReactNode[] = [];\n const refPool = this._renderedRangeRefPool;\n\n for (let index = startIndex; index <= endIndex; index++) {\n const data = store[index];\n\n if (data) {\n const ListItem = this._renderItem;\n\n if (ListItem) {\n const ref: IndexedRef = refPool.get(index) || { current: null, idx: index };\n\n refPool.set(index, ref);\n itemsToAdd.push(<ListItem data={data} key={index} ref={ref} index={index} />);\n }\n }\n }\n\n this._listItems = direction === 'down' \n ? listItems.concat(itemsToAdd)\n : direction === 'up'\n ? itemsToAdd.concat(listItems)\n : this._listItems;\n }\n\n removeRange(startIndex: number, endIndex: number, direction?: ScrollDirection) {\n const removal = super.removeRange(startIndex, endIndex);\n const removedItemsCount = removal.itemsToRemove.length;\n\n if (removedItemsCount) {\n this._listItems = direction === 'down'\n ? this._listItems.slice(removedItemsCount)\n : direction === 'up'\n ? this._listItems.slice(0, -removedItemsCount)\n : this._listItems;\n }\n\n return removal;\n }\n\n clear() {\n super.clear();\n this._listItems = [];\n this._itemsSetter(this._listItems);\n }\n\n setData(store: DataType[]) {\n this._store = store;\n }\n\n setRenderItem(renderItem: ItemRenderer<DataType>) {\n this._renderItem = renderItem;\n }\n\n flush() {\n flushSync(this._flushItems);\n return Promise.resolve();\n }\n\n commit() {\n const renderedRefs = this._renderedRangeRefPool;\n\n for (const ref of renderedRefs.values()) {\n const { idx, current: element } = ref;\n\n if (element) {\n this.registerElement(idx, element);\n }\n }\n\n renderedRefs.clear();\n }\n\n get dataSize() {\n return this._store.length;\n }\n}\n","/**\n * @fileoverview VirtualizedList React component.\n * @license MIT\n * @author Alexandr Kalabin\n */\n\nimport { useRef, useLayoutEffect, useState, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { LayoutVirtual, DynamicListLayout } from 'layout-virtual/core';\nimport ReactRenderer, { type ListItemProps, type ItemRenderer } from './ReactRenderer';\n\nexport interface VirtualizedListReactProps<T> {\n scrollerRef?: React.RefObject<HTMLElement>;\n overscanHeight?: number; \n data: T[];\n renderItem: (props: ListItemProps<T>) => React.ReactNode;\n}\n\nexport default function VirtualizedListReact<ItemData = unknown>(props: VirtualizedListReactProps<ItemData>) {\n const { overscanHeight = 200, data, renderItem, scrollerRef } = props;\n const containerRef = useRef<HTMLDivElement>(null);\n const scrollHeightFillerRef = useRef<HTMLDivElement>(null);\n const viewportContainerRef = useRef<HTMLDivElement>(null);\n const scrollCanvasRef = useRef<HTMLDivElement>(null);\n const topSpacerRef = useRef<HTMLDivElement>(null);\n const contentLayerRef = useRef<HTMLDivElement>(null);\n const bottomSpacerRef = useRef<HTMLDivElement>(null);\n const [visibleItems, setVisibleItems] = useState<React.ReactNode[]>([]);\n const renderer = useRef<ReactRenderer<ItemData> | undefined>(undefined);\n\n useLayoutEffect(() => {\n renderer.current = new ReactRenderer<ItemData>({\n container: scrollerRef?.current || containerRef.current!,\n scrollHeightFiller: scrollHeightFillerRef.current!,\n viewportContainer: viewportContainerRef.current!,\n scrollCanvas: scrollCanvasRef.current!,\n topSpacer: topSpacerRef.current!,\n contentLayer: contentLayerRef.current!,\n bottomSpacer: bottomSpacerRef.current!,\n itemsSetter: setVisibleItems,\n });\n\n const layout = new DynamicListLayout<ItemData, ItemRenderer<ItemData>>({ overscanHeight, renderer: renderer.current });\n const list = new LayoutVirtual<ItemData, ItemRenderer<ItemData>>({ layout });\n\n list.setData(data);\n list.setRenderItem(renderItem);\n }, []);\n\n useEffect(() => {\n renderer.current?.commit();\n }, [visibleItems]);\n\n const scrollerContent = <>\n <div ref={scrollHeightFillerRef}></div>\n <div ref={viewportContainerRef}>\n <div ref={scrollCanvasRef}>\n <div ref={topSpacerRef}></div>\n <div ref={contentLayerRef}>{ visibleItems }</div>\n <div ref={bottomSpacerRef}></div>\n </div>\n </div>\n </>;\n\n return (\n scrollerRef && scrollerRef.current \n ? createPortal(scrollerContent, scrollerRef.current)\n : <div ref={containerRef}>{ scrollerContent }</div>\n );\n};\n"],"names":["ReactRenderer","BaseRenderer","_store","_renderItem","_itemsSetter","_renderedRangeRefPool","Map","_listItems","_flushItems","this","constructor","opts","super","itemsSetter","renderRange","startIndex","endIndex","direction","store","listItems","itemsToAdd","refPool","index","data","ListItem","ref","get","current","idx","set","push","concat","removeRange","removal","removedItemsCount","itemsToRemove","length","slice","clear","setData","setRenderItem","renderItem","flush","flushSync","Promise","resolve","commit","renderedRefs","values","element","registerElement","dataSize","VirtualizedListReact","props","overscanHeight","scrollerRef","containerRef","useRef","scrollHeightFillerRef","viewportContainerRef","scrollCanvasRef","topSpacerRef","contentLayerRef","bottomSpacerRef","visibleItems","setVisibleItems","useState","renderer","useLayoutEffect","container","scrollHeightFiller","viewportContainer","scrollCanvas","topSpacer","contentLayer","bottomSpacer","layout","DynamicListLayout","list","LayoutVirtual","useEffect","scrollerContent","jsxs","Fragment","children","jsx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/ReactRenderer.tsx","../src/ReactLayoutVirtual.tsx"],"sourcesContent":["/**\n * @fileoverview ReactRenderer.\n * @license MIT\n * @author Alexandr Kalabin\n */\n\nimport { flushSync } from 'react-dom';\nimport { BaseRenderer } from 'layout-virtual/core';\nimport type { IRangeRenderer, ScrollDirection } from \"layout-virtual/types\";\nimport type { ItemRenderer, ReactRendererOptions } from './types';\n\ntype IndexedRef = React.RefObject<HTMLDivElement | null> & { idx: number };\n\nexport default class ReactRenderer<DataType = unknown> extends BaseRenderer implements IRangeRenderer<DataType, ItemRenderer<DataType>> {\n private _store: DataType[] = [];\n private _renderItem: ItemRenderer<DataType> | null = null;\n private _itemsSetter: React.Dispatch<React.SetStateAction<React.ReactNode[]>>;\n private _renderedRangeRefPool = new Map<number, IndexedRef>();\n private _listItems: React.ReactNode[] = [];\n private _flushItems = () => { this._itemsSetter(this._listItems); };\n\n constructor(opts: ReactRendererOptions) {\n super(opts);\n this._itemsSetter = opts.itemsSetter;\n }\n\n renderRange(startIndex: number, endIndex: number, direction: ScrollDirection) {\n const store = this._store;\n const listItems = this._listItems;\n const itemsToAdd: React.ReactNode[] = [];\n const refPool = this._renderedRangeRefPool;\n\n for (let index = startIndex; index <= endIndex; index++) {\n const data = store[index];\n\n if (data) {\n const ListItem = this._renderItem;\n\n if (ListItem) {\n const ref: IndexedRef = refPool.get(index) || { current: null, idx: index };\n\n refPool.set(index, ref);\n itemsToAdd.push(<ListItem data={data} key={index} ref={ref} index={index} />);\n }\n }\n }\n\n this._listItems = direction === 'down' \n ? listItems.concat(itemsToAdd)\n : direction === 'up'\n ? itemsToAdd.concat(listItems)\n : this._listItems;\n }\n\n removeRange(startIndex: number, endIndex: number, direction?: ScrollDirection) {\n const removal = super.removeRange(startIndex, endIndex);\n const removedItemsCount = removal.itemsToRemove.length;\n\n if (removedItemsCount) {\n this._listItems = direction === 'down'\n ? this._listItems.slice(removedItemsCount)\n : direction === 'up'\n ? this._listItems.slice(0, -removedItemsCount)\n : this._listItems;\n }\n\n return removal;\n }\n\n clear() {\n super.clear();\n this._listItems = [];\n this._itemsSetter(this._listItems);\n }\n\n setData(store: DataType[]) {\n this._store = store;\n }\n\n setRenderItem(renderItem: ItemRenderer<DataType>) {\n this._renderItem = renderItem;\n }\n\n flush() {\n flushSync(this._flushItems);\n return Promise.resolve();\n }\n\n commit() {\n const renderedRefs = this._renderedRangeRefPool;\n\n for (const ref of renderedRefs.values()) {\n const { idx, current: element } = ref;\n\n if (element) {\n this.registerElement(idx, element);\n }\n }\n\n renderedRefs.clear();\n }\n\n get dataSize() {\n return this._store.length;\n }\n}\n","/**\n * @fileoverview VirtualizedList React component.\n * @license MIT\n * @author Alexandr Kalabin\n */\n\nimport { useRef, useLayoutEffect, useState, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { LayoutVirtual, DynamicListLayout } from 'layout-virtual/core';\nimport ReactRenderer from './ReactRenderer';\nimport type { ItemRenderer, VirtualizedListReactProps } from './types';\n\nexport default function VirtualizedListReact<ItemData = unknown>(props: VirtualizedListReactProps<ItemData>) {\n const { overscanHeight = 200, data, renderItem, scrollerRef } = props;\n const { scrollerClass, viewportClass, contentLayerClass } = props;\n const containerRef = useRef<HTMLDivElement>(null);\n const scrollHeightFillerRef = useRef<HTMLDivElement>(null);\n const viewportContainerRef = useRef<HTMLDivElement>(null);\n const scrollCanvasRef = useRef<HTMLDivElement>(null);\n const topSpacerRef = useRef<HTMLDivElement>(null);\n const contentLayerRef = useRef<HTMLDivElement>(null);\n const bottomSpacerRef = useRef<HTMLDivElement>(null);\n const [visibleItems, setVisibleItems] = useState<React.ReactNode[]>([]);\n const renderer = useRef<ReactRenderer<ItemData> | undefined>(undefined);\n\n useLayoutEffect(() => {\n renderer.current = new ReactRenderer<ItemData>({\n container: scrollerRef?.current || containerRef.current!,\n scrollHeightFiller: scrollHeightFillerRef.current!,\n viewportContainer: viewportContainerRef.current!,\n scrollCanvas: scrollCanvasRef.current!,\n topSpacer: topSpacerRef.current!,\n contentLayer: contentLayerRef.current!,\n bottomSpacer: bottomSpacerRef.current!,\n itemsSetter: setVisibleItems,\n });\n\n const layout = new DynamicListLayout<ItemData, ItemRenderer<ItemData>>({ overscanHeight, renderer: renderer.current });\n const list = new LayoutVirtual<ItemData, ItemRenderer<ItemData>>({ layout });\n\n list.setData(data);\n list.setRenderItem(renderItem);\n\n const scroller = scrollerRef?.current;\n\n if (scroller) {\n scroller.classList.add(scrollerClass || '');\n }\n }, []);\n\n useEffect(() => {\n renderer.current?.commit();\n }, [visibleItems]);\n\n const scrollerContent = <>\n <div ref={scrollHeightFillerRef}></div>\n <div className={viewportClass} ref={viewportContainerRef}>\n <div ref={scrollCanvasRef}>\n <div ref={topSpacerRef}></div>\n <div className={contentLayerClass} ref={contentLayerRef}>{ visibleItems }</div>\n <div ref={bottomSpacerRef}></div>\n </div>\n </div>\n </>;\n\n return (\n scrollerRef && scrollerRef.current \n ? createPortal(scrollerContent, scrollerRef.current)\n : <div className={scrollerClass} ref={containerRef}>{ scrollerContent }</div>\n );\n};\n"],"names":["ReactRenderer","BaseRenderer","_store","_renderItem","_itemsSetter","_renderedRangeRefPool","Map","_listItems","_flushItems","this","constructor","opts","super","itemsSetter","renderRange","startIndex","endIndex","direction","store","listItems","itemsToAdd","refPool","index","data","ListItem","ref","get","current","idx","set","push","concat","removeRange","removal","removedItemsCount","itemsToRemove","length","slice","clear","setData","setRenderItem","renderItem","flush","flushSync","Promise","resolve","commit","renderedRefs","values","element","registerElement","dataSize","VirtualizedListReact","props","overscanHeight","scrollerRef","scrollerClass","viewportClass","contentLayerClass","containerRef","useRef","scrollHeightFillerRef","viewportContainerRef","scrollCanvasRef","topSpacerRef","contentLayerRef","bottomSpacerRef","visibleItems","setVisibleItems","useState","renderer","useLayoutEffect","container","scrollHeightFiller","viewportContainer","scrollCanvas","topSpacer","contentLayer","bottomSpacer","layout","DynamicListLayout","list","LayoutVirtual","scroller","classList","add","useEffect","scrollerContent","jsxs","Fragment","children","jsx","className","createPortal"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Types for VirtualizedList React renderer and component.
3
+ * @license MIT
4
+ * @author Alexandr Kalabin
5
+ */
6
+ import type { VirtualScrollStructure } from "layout-virtual/types";
7
+ export interface ListItemProps<T = unknown> {
8
+ data: T;
9
+ ref: React.Ref<HTMLDivElement> | undefined;
10
+ index: number;
11
+ }
12
+ export type ItemRenderer<T> = React.FC<ListItemProps<T>>;
13
+ export type ReactRendererOptions = {
14
+ itemsSetter: React.Dispatch<React.SetStateAction<React.ReactNode[]>>;
15
+ } & VirtualScrollStructure;
16
+ export interface VirtualizedListReactClasses {
17
+ scrollerClass?: string | undefined;
18
+ viewportClass?: string | undefined;
19
+ contentLayerClass?: string | undefined;
20
+ }
21
+ export interface VirtualizedListReactProps<T> extends VirtualizedListReactClasses {
22
+ scrollerRef?: React.RefObject<HTMLElement>;
23
+ overscanHeight?: number;
24
+ data: T[];
25
+ renderItem: (props: ListItemProps<T>) => React.ReactNode;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-layout-virtual",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
4
4
  "description": "React virtual scrolling component for responsive lists and grids with dynamic item sizes.",
5
5
  "keywords": [
6
6
  "virtual",
@@ -43,7 +43,7 @@
43
43
  "prepublishOnly": "npm run build"
44
44
  },
45
45
  "dependencies": {
46
- "layout-virtual": "^0.1.2"
46
+ "layout-virtual": "^0.2.0"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "react": "^19.2.5",