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/d3-geo": "^3.1.0",
|
||||||
"@types/google.maps": "^3.51.0",
|
"@types/google.maps": "^3.51.0",
|
||||||
"@types/react-page-visibility": "^6.4.1",
|
"@types/react-page-visibility": "^6.4.1",
|
||||||
|
"@types/react-window-infinite-loader": "^1.0.9",
|
||||||
"@types/web": "^0.0.99",
|
"@types/web": "^0.0.99",
|
||||||
"@uppy/core": "^1.16.0",
|
"@uppy/core": "^1.16.0",
|
||||||
"@uppy/dashboard": "^1.16.0",
|
"@uppy/dashboard": "^1.16.0",
|
||||||
|
|
@ -203,6 +204,7 @@
|
||||||
"react-virtuoso": "^4.5.0",
|
"react-virtuoso": "^4.5.0",
|
||||||
"react-webcam": "^7.0.1",
|
"react-webcam": "^7.0.1",
|
||||||
"react-window": "^1.8.6",
|
"react-window": "^1.8.6",
|
||||||
|
"react-window-infinite-loader": "^1.0.10",
|
||||||
"react-zoom-pan-pinch": "^1.6.1",
|
"react-zoom-pan-pinch": "^1.6.1",
|
||||||
"redux": "^4.0.1",
|
"redux": "^4.0.1",
|
||||||
"redux-form": "^8.2.6",
|
"redux-form": "^8.2.6",
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ type StaticTableProps = TableColumnHeaderProps & {
|
||||||
scrollContainerStyles: any;
|
scrollContainerStyles: any;
|
||||||
useVirtual: boolean;
|
useVirtual: boolean;
|
||||||
tableBodyRef?: React.MutableRefObject<HTMLDivElement | null>;
|
tableBodyRef?: React.MutableRefObject<HTMLDivElement | null>;
|
||||||
|
isLoading: boolean;
|
||||||
|
loadMoreFromEvaluations: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
|
const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
|
||||||
|
|
@ -81,6 +83,8 @@ const StaticTable = (props: StaticTableProps, ref: React.Ref<SimpleBar>) => {
|
||||||
getTableBodyProps={props.getTableBodyProps}
|
getTableBodyProps={props.getTableBodyProps}
|
||||||
height={props.height}
|
height={props.height}
|
||||||
isAddRowInProgress={props.isAddRowInProgress}
|
isAddRowInProgress={props.isAddRowInProgress}
|
||||||
|
isLoading={props.isLoading}
|
||||||
|
loadMoreFromEvaluations={props.loadMoreFromEvaluations}
|
||||||
multiRowSelection={!!props.multiRowSelection}
|
multiRowSelection={!!props.multiRowSelection}
|
||||||
pageSize={props.pageSize}
|
pageSize={props.pageSize}
|
||||||
prepareRow={props.prepareRow}
|
prepareRow={props.prepareRow}
|
||||||
|
|
|
||||||
|
|
@ -473,8 +473,10 @@ export function Table(props: TableProps) {
|
||||||
headerGroups={headerGroups}
|
headerGroups={headerGroups}
|
||||||
height={props.height}
|
height={props.height}
|
||||||
isAddRowInProgress={props.isAddRowInProgress}
|
isAddRowInProgress={props.isAddRowInProgress}
|
||||||
|
isLoading={props.isLoading}
|
||||||
isResizingColumn={isResizingColumn}
|
isResizingColumn={isResizingColumn}
|
||||||
isSortable={props.isSortable}
|
isSortable={props.isSortable}
|
||||||
|
loadMoreFromEvaluations={props.nextPageClick}
|
||||||
multiRowSelection={props?.multiRowSelection}
|
multiRowSelection={props?.multiRowSelection}
|
||||||
pageSize={props.pageSize}
|
pageSize={props.pageSize}
|
||||||
prepareRow={prepareRow}
|
prepareRow={prepareRow}
|
||||||
|
|
@ -512,8 +514,10 @@ export function Table(props: TableProps) {
|
||||||
height={props.height}
|
height={props.height}
|
||||||
isAddRowInProgress={props.isAddRowInProgress}
|
isAddRowInProgress={props.isAddRowInProgress}
|
||||||
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
||||||
|
isLoading={props.isLoading}
|
||||||
isResizingColumn={isResizingColumn}
|
isResizingColumn={isResizingColumn}
|
||||||
isSortable={props.isSortable}
|
isSortable={props.isSortable}
|
||||||
|
loadMoreFromEvaluations={props.nextPageClick}
|
||||||
multiRowSelection={props?.multiRowSelection}
|
multiRowSelection={props?.multiRowSelection}
|
||||||
pageSize={props.pageSize}
|
pageSize={props.pageSize}
|
||||||
prepareRow={prepareRow}
|
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,
|
TableBodyPropGetter,
|
||||||
TableBodyProps,
|
TableBodyProps,
|
||||||
} from "react-table";
|
} from "react-table";
|
||||||
import type { ListChildComponentProps, ReactElementType } from "react-window";
|
import { type ReactElementType } from "react-window";
|
||||||
import { FixedSizeList, areEqual } from "react-window";
|
import type SimpleBar from "simplebar-react";
|
||||||
import { WIDGET_PADDING } from "constants/WidgetConstants";
|
|
||||||
import { EmptyRows, EmptyRow, Row } from "./Row";
|
|
||||||
import type { ReactTableColumnProps, TableSizes } from "../Constants";
|
import type { ReactTableColumnProps, TableSizes } from "../Constants";
|
||||||
import type { HeaderComponentProps } from "../Table";
|
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 = {
|
export type BodyContextType = {
|
||||||
accentColor: string;
|
accentColor: string;
|
||||||
|
|
@ -49,26 +49,6 @@ export const BodyContext = React.createContext<BodyContextType>({
|
||||||
totalColumnsWidth: 0,
|
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 {
|
interface BodyPropsType {
|
||||||
getTableBodyProps(
|
getTableBodyProps(
|
||||||
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
|
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
|
||||||
|
|
@ -80,28 +60,22 @@ interface BodyPropsType {
|
||||||
tableSizes: TableSizes;
|
tableSizes: TableSizes;
|
||||||
innerElementType?: ReactElementType;
|
innerElementType?: ReactElementType;
|
||||||
isInfiniteScrollEnabled?: boolean;
|
isInfiniteScrollEnabled?: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
loadMoreFromEvaluations: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableVirtualBodyComponent = React.forwardRef(
|
const TableVirtualBodyComponent = React.forwardRef(
|
||||||
(props: BodyPropsType, ref: Ref<SimpleBar>) => {
|
(props: BodyPropsType, ref: Ref<SimpleBar>) => {
|
||||||
return (
|
return (
|
||||||
<div className="simplebar-content-wrapper">
|
<div className="simplebar-content-wrapper">
|
||||||
<FixedSizeList
|
<FixedVirtualList
|
||||||
className="virtual-list simplebar-content"
|
height={props.height}
|
||||||
height={
|
|
||||||
props.height -
|
|
||||||
props.tableSizes.TABLE_HEADER_HEIGHT -
|
|
||||||
2 * props.tableSizes.VERTICAL_PADDING
|
|
||||||
}
|
|
||||||
innerElementType={props.innerElementType}
|
innerElementType={props.innerElementType}
|
||||||
itemCount={Math.max(props.rows.length, props.pageSize)}
|
|
||||||
itemData={props.rows}
|
|
||||||
itemSize={props.tableSizes.ROW_HEIGHT}
|
|
||||||
outerRef={ref}
|
outerRef={ref}
|
||||||
width={`calc(100% + ${2 * WIDGET_PADDING}px)`}
|
pageSize={props.pageSize}
|
||||||
>
|
rows={props.rows}
|
||||||
{rowRenderer}
|
tableSizes={props.tableSizes}
|
||||||
</FixedSizeList>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -191,7 +165,12 @@ export const TableBody = React.forwardRef(
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isInfiniteScrollEnabled ? (
|
{isInfiniteScrollEnabled ? (
|
||||||
<div>Infinite Scroll</div>
|
<InfiniteScrollBody
|
||||||
|
itemCount={rows.length}
|
||||||
|
ref={ref}
|
||||||
|
rows={rows}
|
||||||
|
{...restOfProps}
|
||||||
|
/>
|
||||||
) : useVirtual ? (
|
) : useVirtual ? (
|
||||||
<TableVirtualBodyComponent
|
<TableVirtualBodyComponent
|
||||||
isInfiniteScrollEnabled={false}
|
isInfiniteScrollEnabled={false}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ type VirtualTableProps = TableColumnHeaderProps & {
|
||||||
scrollContainerStyles: any;
|
scrollContainerStyles: any;
|
||||||
useVirtual: boolean;
|
useVirtual: boolean;
|
||||||
isInfiniteScrollEnabled: boolean;
|
isInfiniteScrollEnabled: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
loadMoreFromEvaluations: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VirtualTable = (props: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
|
const VirtualTable = (props: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
|
||||||
|
|
@ -61,8 +63,10 @@ const VirtualTable = (props: VirtualTableProps, ref: React.Ref<SimpleBar>) => {
|
||||||
innerElementType={VirtualTableInnerElement}
|
innerElementType={VirtualTableInnerElement}
|
||||||
isAddRowInProgress={props.isAddRowInProgress}
|
isAddRowInProgress={props.isAddRowInProgress}
|
||||||
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
isInfiniteScrollEnabled={props.isInfiniteScrollEnabled}
|
||||||
|
isLoading={props.isLoading}
|
||||||
isResizingColumn={props.isResizingColumn}
|
isResizingColumn={props.isResizingColumn}
|
||||||
isSortable={props.isSortable}
|
isSortable={props.isSortable}
|
||||||
|
loadMoreFromEvaluations={props.loadMoreFromEvaluations}
|
||||||
multiRowSelection={!!props.multiRowSelection}
|
multiRowSelection={!!props.multiRowSelection}
|
||||||
pageSize={props.pageSize}
|
pageSize={props.pageSize}
|
||||||
prepareRow={props.prepareRow}
|
prepareRow={props.prepareRow}
|
||||||
|
|
|
||||||
|
|
@ -11192,12 +11192,22 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/react-window@npm:^1.8.2":
|
"@types/react-window-infinite-loader@npm:^1.0.9":
|
||||||
version: 1.8.2
|
version: 1.0.9
|
||||||
resolution: "@types/react-window@npm:1.8.2"
|
resolution: "@types/react-window-infinite-loader@npm:1.0.9"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react": "*"
|
"@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
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|
@ -13003,6 +13013,7 @@ __metadata:
|
||||||
"@types/react-tabs": ^2.3.1
|
"@types/react-tabs": ^2.3.1
|
||||||
"@types/react-test-renderer": ^17.0.1
|
"@types/react-test-renderer": ^17.0.1
|
||||||
"@types/react-window": ^1.8.2
|
"@types/react-window": ^1.8.2
|
||||||
|
"@types/react-window-infinite-loader": ^1.0.9
|
||||||
"@types/redux-form": ^8.1.9
|
"@types/redux-form": ^8.1.9
|
||||||
"@types/redux-mock-store": ^1.0.2
|
"@types/redux-mock-store": ^1.0.2
|
||||||
"@types/shallowequal": ^1.1.5
|
"@types/shallowequal": ^1.1.5
|
||||||
|
|
@ -13206,6 +13217,7 @@ __metadata:
|
||||||
react-virtuoso: ^4.5.0
|
react-virtuoso: ^4.5.0
|
||||||
react-webcam: ^7.0.1
|
react-webcam: ^7.0.1
|
||||||
react-window: ^1.8.6
|
react-window: ^1.8.6
|
||||||
|
react-window-infinite-loader: ^1.0.10
|
||||||
react-zoom-pan-pinch: ^1.6.1
|
react-zoom-pan-pinch: ^1.6.1
|
||||||
redux: ^4.0.1
|
redux: ^4.0.1
|
||||||
redux-devtools-extension: ^2.13.8
|
redux-devtools-extension: ^2.13.8
|
||||||
|
|
@ -29019,6 +29031,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"react-window@npm:^1.8.6":
|
||||||
version: 1.8.8
|
version: 1.8.8
|
||||||
resolution: "react-window@npm:1.8.8"
|
resolution: "react-window@npm:1.8.8"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user