feat: Implement infra code for infinite scroll implementation. (#39225)
## Description Implements infinite scroll functionality for table widget using react-window-infinite-loader. Introduces new components and hooks to manage virtualized table rendering with dynamic loading of rows. Fixes #39082 _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Table, @tag.Sanity" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/13329193341> > Commit: 0c58fcf83dbfd520958c9989ffb607cf57d1fdb1 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=13329193341&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Table, @tag.Sanity` > Spec: > <hr>Fri, 14 Feb 2025 13:18:37 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added infinite scrolling support to table views, enabling seamless data loading as you scroll. - Enhanced table interfaces with improved loading indicators and smoother virtualized rendering for large datasets. - **Chores** - Updated supporting libraries to underpin the improved scrolling and data handling capabilities. <!-- end of auto-generated comment: release notes by coderabbit.ai --> ---------
This commit is contained in:
parent
22688f994f
commit
5b9153cb19
|
|
@ -91,6 +91,7 @@
|
|||
"@types/d3-geo": "^3.1.0",
|
||||
"@types/google.maps": "^3.51.0",
|
||||
"@types/react-page-visibility": "^6.4.1",
|
||||
"@types/react-window-infinite-loader": "^1.0.9",
|
||||
"@types/web": "^0.0.99",
|
||||
"@uppy/core": "^1.16.0",
|
||||
"@uppy/dashboard": "^1.16.0",
|
||||
|
|
@ -203,6 +204,7 @@
|
|||
"react-virtuoso": "^4.5.0",
|
||||
"react-webcam": "^7.0.1",
|
||||
"react-window": "^1.8.6",
|
||||
"react-window-infinite-loader": "^1.0.10",
|
||||
"react-zoom-pan-pinch": "^1.6.1",
|
||||
"redux": "^4.0.1",
|
||||
"redux-form": "^8.2.6",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ type StaticTableProps = TableColumnHeaderProps & {
|
|||
scrollContainerStyles: any;
|
||||
useVirtual: boolean;
|
||||
tableBodyRef?: React.MutableRefObject<HTMLDivElement | null>;
|
||||
isLoading: boolean;
|
||||
loadMoreFromEvaluations: () => void;
|
||||
};
|
||||
|
||||
const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
|
||||
|
|
@ -81,6 +83,8 @@ const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
|
|||
getTableBodyProps={props.getTableBodyProps}
|
||||
height={props.height}
|
||||
isAddRowInProgress={props.isAddRowInProgress}
|
||||
isLoading={props.isLoading}
|
||||
loadMoreFromEvaluations={props.loadMoreFromEvaluations}
|
||||
multiRowSelection={!!props.multiRowSelection}
|
||||
pageSize={props.pageSize}
|
||||
prepareRow={props.prepareRow}
|
||||
|
|
|
|||
|
|
@ -473,8 +473,10 @@ export function Table(props: TableProps) {
|
|||
headerGroups={headerGroups}
|
||||
height={props.height}
|
||||
isAddRowInProgress={props.isAddRowInProgress}
|
||||
isLoading={props.isLoading}
|
||||
isResizingColumn={isResizingColumn}
|
||||
isSortable={props.isSortable}
|
||||
loadMoreFromEvaluations={props.nextPageClick}
|
||||
multiRowSelection={props?.multiRowSelection}
|
||||
pageSize={props.pageSize}
|
||||
prepareRow={prepareRow}
|
||||
|
|
@ -512,8 +514,10 @@ export function Table(props: TableProps) {
|
|||
height={props.height}
|
||||
isAddRowInProgress={props.isAddRowInProgress}
|
||||
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
||||
isLoading={props.isLoading}
|
||||
isResizingColumn={isResizingColumn}
|
||||
isSortable={props.isSortable}
|
||||
loadMoreFromEvaluations={props.nextPageClick}
|
||||
multiRowSelection={props?.multiRowSelection}
|
||||
pageSize={props.pageSize}
|
||||
prepareRow={prepareRow}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import React, { type Ref } from "react";
|
||||
import type { Row as ReactTableRowType } from "react-table";
|
||||
import { type ReactElementType } from "react-window";
|
||||
import InfiniteLoader from "react-window-infinite-loader";
|
||||
import type SimpleBar from "simplebar-react";
|
||||
import type { TableSizes } from "../../Constants";
|
||||
import { useInfiniteVirtualization } from "./useInfiniteVirtualization";
|
||||
import { FixedInfiniteVirtualList } from "../VirtualList";
|
||||
|
||||
interface InfiniteScrollBodyProps {
|
||||
rows: ReactTableRowType<Record<string, unknown>>[];
|
||||
height: number;
|
||||
tableSizes: TableSizes;
|
||||
innerElementType?: ReactElementType;
|
||||
isLoading: boolean;
|
||||
totalRecordsCount?: number;
|
||||
itemCount: number;
|
||||
loadMoreFromEvaluations: () => void;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
const InfiniteScrollBody = React.forwardRef(
|
||||
(props: InfiniteScrollBodyProps, ref: Ref<SimpleBar>) => {
|
||||
const { isLoading, loadMoreFromEvaluations, pageSize, rows } = props;
|
||||
const { isItemLoaded, itemCount, loadMoreItems } =
|
||||
useInfiniteVirtualization({
|
||||
rows,
|
||||
totalRecordsCount: rows.length,
|
||||
isLoading,
|
||||
loadMore: loadMoreFromEvaluations,
|
||||
pageSize,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="simplebar-content-wrapper">
|
||||
<InfiniteLoader
|
||||
isItemLoaded={isItemLoaded}
|
||||
itemCount={itemCount + 5}
|
||||
loadMoreItems={loadMoreItems}
|
||||
>
|
||||
{({ onItemsRendered, ref: infiniteLoaderRef }) => (
|
||||
<FixedInfiniteVirtualList
|
||||
height={props.height}
|
||||
infiniteLoaderListRef={infiniteLoaderRef}
|
||||
innerElementType={props.innerElementType}
|
||||
onItemsRendered={onItemsRendered}
|
||||
outerRef={ref}
|
||||
pageSize={props.pageSize}
|
||||
rows={props.rows}
|
||||
tableSizes={props.tableSizes}
|
||||
/>
|
||||
)}
|
||||
</InfiniteLoader>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default InfiniteScrollBody;
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import { renderHook } from "@testing-library/react-hooks";
|
||||
import { useInfiniteVirtualization } from "./useInfiniteVirtualization";
|
||||
import { act } from "@testing-library/react";
|
||||
import type { Row as ReactTableRowType } from "react-table";
|
||||
|
||||
describe("useInfiniteVirtualization", () => {
|
||||
const mockRows: ReactTableRowType<Record<string, unknown>>[] = [
|
||||
{
|
||||
id: "1",
|
||||
original: { id: 1, name: "Test 1" },
|
||||
index: 0,
|
||||
cells: [],
|
||||
values: {},
|
||||
getRowProps: jest.fn(),
|
||||
allCells: [],
|
||||
subRows: [],
|
||||
isExpanded: false,
|
||||
canExpand: false,
|
||||
depth: 0,
|
||||
toggleRowExpanded: jest.fn(),
|
||||
state: {},
|
||||
toggleRowSelected: jest.fn(),
|
||||
getToggleRowExpandedProps: jest.fn(),
|
||||
isSelected: false,
|
||||
isSomeSelected: false,
|
||||
isGrouped: false,
|
||||
groupByID: "",
|
||||
groupByVal: "",
|
||||
leafRows: [],
|
||||
getToggleRowSelectedProps: jest.fn(),
|
||||
setState: jest.fn(),
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
original: { id: 2, name: "Test 2" },
|
||||
index: 1,
|
||||
cells: [],
|
||||
values: {},
|
||||
getRowProps: jest.fn(),
|
||||
allCells: [],
|
||||
subRows: [],
|
||||
isExpanded: false,
|
||||
canExpand: false,
|
||||
depth: 0,
|
||||
toggleRowExpanded: jest.fn(),
|
||||
state: {},
|
||||
toggleRowSelected: jest.fn(),
|
||||
getToggleRowExpandedProps: jest.fn(),
|
||||
isSelected: false,
|
||||
isSomeSelected: false,
|
||||
isGrouped: false,
|
||||
groupByID: "",
|
||||
groupByVal: "",
|
||||
leafRows: [],
|
||||
getToggleRowSelectedProps: jest.fn(),
|
||||
setState: jest.fn(),
|
||||
},
|
||||
];
|
||||
|
||||
const defaultProps = {
|
||||
rows: mockRows,
|
||||
isLoading: false,
|
||||
loadMore: jest.fn(),
|
||||
pageSize: 10,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should return correct itemCount when totalRecordsCount is provided", () => {
|
||||
const totalRecordsCount = 100;
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization({
|
||||
...defaultProps,
|
||||
totalRecordsCount,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.itemCount).toBe(totalRecordsCount);
|
||||
});
|
||||
|
||||
it("should return rows length as itemCount when totalRecordsCount is not provided", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization(defaultProps),
|
||||
);
|
||||
|
||||
expect(result.current.itemCount).toBe(defaultProps.rows.length);
|
||||
});
|
||||
|
||||
it("should call loadMore when loadMoreItems is called and not loading", async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization(defaultProps),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.loadMoreItems(0, 10);
|
||||
});
|
||||
|
||||
expect(defaultProps.loadMore).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should not call loadMore when loadMoreItems is called and is loading", async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization({
|
||||
...defaultProps,
|
||||
isLoading: true,
|
||||
}),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.loadMoreItems(0, 10);
|
||||
});
|
||||
|
||||
expect(defaultProps.loadMore).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should return correct isItemLoaded state for different scenarios", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization(defaultProps),
|
||||
);
|
||||
|
||||
// Index within rows length and not loading
|
||||
expect(result.current.isItemLoaded(1)).toBe(true);
|
||||
|
||||
// Index beyond rows length and not loading
|
||||
expect(result.current.isItemLoaded(5)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for isItemLoaded when loading", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization({
|
||||
...defaultProps,
|
||||
isLoading: true,
|
||||
}),
|
||||
);
|
||||
|
||||
// Even for index within rows length, should return false when loading
|
||||
expect(result.current.isItemLoaded(1)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return zero itemCount when there are no records", () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInfiniteVirtualization({
|
||||
...defaultProps,
|
||||
rows: [],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.current.itemCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import { useCallback } from "react";
|
||||
import type { Row as ReactTableRowType } from "react-table";
|
||||
|
||||
interface InfiniteVirtualizationProps {
|
||||
rows: ReactTableRowType<Record<string, unknown>>[];
|
||||
totalRecordsCount?: number;
|
||||
isLoading: boolean;
|
||||
loadMore: () => void;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
interface UseInfiniteVirtualizationReturn {
|
||||
itemCount: number;
|
||||
loadMoreItems: (startIndex: number, stopIndex: number) => void;
|
||||
isItemLoaded: (index: number) => boolean;
|
||||
}
|
||||
|
||||
export const useInfiniteVirtualization = ({
|
||||
isLoading,
|
||||
loadMore,
|
||||
rows,
|
||||
totalRecordsCount,
|
||||
}: InfiniteVirtualizationProps): UseInfiniteVirtualizationReturn => {
|
||||
const loadMoreItems = useCallback(async () => {
|
||||
if (!isLoading) {
|
||||
loadMore();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}, [isLoading, loadMore]);
|
||||
|
||||
return {
|
||||
itemCount: totalRecordsCount ?? rows.length,
|
||||
loadMoreItems,
|
||||
isItemLoaded: (index) => !isLoading && index < rows.length,
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import type { ListOnItemsRenderedProps, ReactElementType } from "react-window";
|
||||
import { FixedSizeList, areEqual } from "react-window";
|
||||
import React from "react";
|
||||
import type { ListChildComponentProps } from "react-window";
|
||||
import type { Row as ReactTableRowType } from "react-table";
|
||||
import { WIDGET_PADDING } from "constants/WidgetConstants";
|
||||
import { EmptyRow, Row } from "./Row";
|
||||
import type { TableSizes } from "../Constants";
|
||||
import type SimpleBar from "simplebar-react";
|
||||
|
||||
const rowRenderer = React.memo((rowProps: ListChildComponentProps) => {
|
||||
const { data, index, style } = rowProps;
|
||||
|
||||
if (index < data.length) {
|
||||
const row = data[index];
|
||||
|
||||
return (
|
||||
<Row
|
||||
className="t--virtual-row"
|
||||
index={index}
|
||||
key={index}
|
||||
row={row}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <EmptyRow style={style} />;
|
||||
}
|
||||
}, areEqual);
|
||||
|
||||
interface BaseVirtualListProps {
|
||||
height: number;
|
||||
tableSizes: TableSizes;
|
||||
rows: ReactTableRowType<Record<string, unknown>>[];
|
||||
pageSize: number;
|
||||
innerElementType?: ReactElementType;
|
||||
outerRef?: React.Ref<SimpleBar>;
|
||||
onItemsRendered?: (props: ListOnItemsRenderedProps) => void;
|
||||
infiniteLoaderListRef?: React.Ref<FixedSizeList>;
|
||||
}
|
||||
|
||||
const BaseVirtualList = React.memo(function BaseVirtualList({
|
||||
height,
|
||||
infiniteLoaderListRef,
|
||||
innerElementType,
|
||||
onItemsRendered,
|
||||
outerRef,
|
||||
pageSize,
|
||||
rows,
|
||||
tableSizes,
|
||||
}: BaseVirtualListProps) {
|
||||
return (
|
||||
<FixedSizeList
|
||||
className="virtual-list simplebar-content"
|
||||
height={
|
||||
height -
|
||||
tableSizes.TABLE_HEADER_HEIGHT -
|
||||
2 * tableSizes.VERTICAL_PADDING
|
||||
}
|
||||
innerElementType={innerElementType}
|
||||
itemCount={Math.max(rows.length, pageSize)}
|
||||
itemData={rows}
|
||||
itemSize={tableSizes.ROW_HEIGHT}
|
||||
onItemsRendered={onItemsRendered}
|
||||
outerRef={outerRef}
|
||||
ref={infiniteLoaderListRef}
|
||||
width={`calc(100% + ${2 * WIDGET_PADDING}px)`}
|
||||
>
|
||||
{rowRenderer}
|
||||
</FixedSizeList>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* The difference between next two components is in the number of arguments they expect.
|
||||
*/
|
||||
export const FixedInfiniteVirtualList = React.memo(
|
||||
function FixedInfiniteVirtualList(props: BaseVirtualListProps) {
|
||||
return <BaseVirtualList {...props} />;
|
||||
},
|
||||
);
|
||||
|
||||
type FixedVirtualListProps = Omit<
|
||||
BaseVirtualListProps,
|
||||
"onItemsRendered" | "infiniteLoaderListRef"
|
||||
>;
|
||||
export const FixedVirtualList = React.memo(function FixedVirtualList(
|
||||
props: FixedVirtualListProps,
|
||||
) {
|
||||
return <BaseVirtualList {...props} />;
|
||||
});
|
||||
|
|
@ -5,13 +5,13 @@ import type {
|
|||
TableBodyPropGetter,
|
||||
TableBodyProps,
|
||||
} from "react-table";
|
||||
import type { ListChildComponentProps, ReactElementType } from "react-window";
|
||||
import { FixedSizeList, areEqual } from "react-window";
|
||||
import { WIDGET_PADDING } from "constants/WidgetConstants";
|
||||
import { EmptyRows, EmptyRow, Row } from "./Row";
|
||||
import { type ReactElementType } from "react-window";
|
||||
import type SimpleBar from "simplebar-react";
|
||||
import type { ReactTableColumnProps, TableSizes } from "../Constants";
|
||||
import type { HeaderComponentProps } from "../Table";
|
||||
import type SimpleBar from "simplebar-react";
|
||||
import InfiniteScrollBody from "./InifiniteScrollBody";
|
||||
import { EmptyRows, Row } from "./Row";
|
||||
import { FixedVirtualList } from "./VirtualList";
|
||||
|
||||
export type BodyContextType = {
|
||||
accentColor: string;
|
||||
|
|
@ -49,26 +49,6 @@ export const BodyContext = React.createContext<BodyContextType>({
|
|||
totalColumnsWidth: 0,
|
||||
});
|
||||
|
||||
const rowRenderer = React.memo((rowProps: ListChildComponentProps) => {
|
||||
const { data, index, style } = rowProps;
|
||||
|
||||
if (index < data.length) {
|
||||
const row = data[index];
|
||||
|
||||
return (
|
||||
<Row
|
||||
className="t--virtual-row"
|
||||
index={index}
|
||||
key={index}
|
||||
row={row}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return <EmptyRow style={style} />;
|
||||
}
|
||||
}, areEqual);
|
||||
|
||||
interface BodyPropsType {
|
||||
getTableBodyProps(
|
||||
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
|
||||
|
|
@ -80,28 +60,22 @@ interface BodyPropsType {
|
|||
tableSizes: TableSizes;
|
||||
innerElementType?: ReactElementType;
|
||||
isInfiniteScrollEnabled?: boolean;
|
||||
isLoading: boolean;
|
||||
loadMoreFromEvaluations: () => void;
|
||||
}
|
||||
|
||||
const TableVirtualBodyComponent = React.forwardRef(
|
||||
(props: BodyPropsType, ref: Ref<SimpleBar>) => {
|
||||
return (
|
||||
<div className="simplebar-content-wrapper">
|
||||
<FixedSizeList
|
||||
className="virtual-list simplebar-content"
|
||||
height={
|
||||
props.height -
|
||||
props.tableSizes.TABLE_HEADER_HEIGHT -
|
||||
2 * props.tableSizes.VERTICAL_PADDING
|
||||
}
|
||||
<FixedVirtualList
|
||||
height={props.height}
|
||||
innerElementType={props.innerElementType}
|
||||
itemCount={Math.max(props.rows.length, props.pageSize)}
|
||||
itemData={props.rows}
|
||||
itemSize={props.tableSizes.ROW_HEIGHT}
|
||||
outerRef={ref}
|
||||
width={`calc(100% + ${2 * WIDGET_PADDING}px)`}
|
||||
>
|
||||
{rowRenderer}
|
||||
</FixedSizeList>
|
||||
pageSize={props.pageSize}
|
||||
rows={props.rows}
|
||||
tableSizes={props.tableSizes}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
@ -191,7 +165,12 @@ export const TableBody = React.forwardRef(
|
|||
}}
|
||||
>
|
||||
{isInfiniteScrollEnabled ? (
|
||||
<div>Infinite Scroll</div>
|
||||
<InfiniteScrollBody
|
||||
itemCount={rows.length}
|
||||
ref={ref}
|
||||
rows={rows}
|
||||
{...restOfProps}
|
||||
/>
|
||||
) : useVirtual ? (
|
||||
<TableVirtualBodyComponent
|
||||
isInfiniteScrollEnabled={false}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ type VirtualTableProps = TableColumnHeaderProps & {
|
|||
scrollContainerStyles: any;
|
||||
useVirtual: boolean;
|
||||
isInfiniteScrollEnabled: boolean;
|
||||
isLoading: boolean;
|
||||
loadMoreFromEvaluations: () => void;
|
||||
};
|
||||
|
||||
const VirtualTable = (props: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
|
||||
|
|
@ -61,8 +63,10 @@ const VirtualTable = (props: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
|
|||
innerElementType={VirtualTableInnerElement}
|
||||
isAddRowInProgress={props.isAddRowInProgress}
|
||||
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
||||
isLoading={props.isLoading}
|
||||
isResizingColumn={props.isResizingColumn}
|
||||
isSortable={props.isSortable}
|
||||
loadMoreFromEvaluations={props.loadMoreFromEvaluations}
|
||||
multiRowSelection={!!props.multiRowSelection}
|
||||
pageSize={props.pageSize}
|
||||
prepareRow={props.prepareRow}
|
||||
|
|
|
|||
|
|
@ -11192,12 +11192,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-window@npm:^1.8.2":
|
||||
version: 1.8.2
|
||||
resolution: "@types/react-window@npm:1.8.2"
|
||||
"@types/react-window-infinite-loader@npm:^1.0.9":
|
||||
version: 1.0.9
|
||||
resolution: "@types/react-window-infinite-loader@npm:1.0.9"
|
||||
dependencies:
|
||||
"@types/react": "*"
|
||||
checksum: c127ed420d881510fe647539342e7c494802aab12fd6cb61f9f8ba47ef16d3683e632b7a6a07eb0d284ea8f0953ae7941eafa2c51c0bcb3b176d009eac09c79a
|
||||
"@types/react-window": "*"
|
||||
checksum: 9f2c27f24bfa726ceaef6612a4adbda745f3455c877193f68dfa48591274c670a6df4fa6870785cff5f948e289ceb9a247fb7cbf67e3cd555ab16d11866fd63f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-window@npm:*, @types/react-window@npm:^1.8.2":
|
||||
version: 1.8.8
|
||||
resolution: "@types/react-window@npm:1.8.8"
|
||||
dependencies:
|
||||
"@types/react": "*"
|
||||
checksum: 253c9d6e0c942f34633edbddcbc369324403c42458ff004457c5bd5972961d8433a909c0cc1a89c918063d5eb85ecbdd774142af2555fae61f4ceb3ba9884b5a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -13003,6 +13013,7 @@ __metadata:
|
|||
"@types/react-tabs": ^2.3.1
|
||||
"@types/react-test-renderer": ^17.0.1
|
||||
"@types/react-window": ^1.8.2
|
||||
"@types/react-window-infinite-loader": ^1.0.9
|
||||
"@types/redux-form": ^8.1.9
|
||||
"@types/redux-mock-store": ^1.0.2
|
||||
"@types/shallowequal": ^1.1.5
|
||||
|
|
@ -13206,6 +13217,7 @@ __metadata:
|
|||
react-virtuoso: ^4.5.0
|
||||
react-webcam: ^7.0.1
|
||||
react-window: ^1.8.6
|
||||
react-window-infinite-loader: ^1.0.10
|
||||
react-zoom-pan-pinch: ^1.6.1
|
||||
redux: ^4.0.1
|
||||
redux-devtools-extension: ^2.13.8
|
||||
|
|
@ -29019,6 +29031,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-window-infinite-loader@npm:^1.0.10":
|
||||
version: 1.0.10
|
||||
resolution: "react-window-infinite-loader@npm:1.0.10"
|
||||
peerDependencies:
|
||||
react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
checksum: 3ee79ce325e45a7d4d9f92c13e7ff4c523578fa454de3a440980b286d964eb951095c012a7f43ca75e9d86ed2b052c81b08134dfa8827144f44b059cc56514c3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-window@npm:^1.8.6":
|
||||
version: 1.8.8
|
||||
resolution: "react-window@npm:1.8.8"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user