Skip to content

Commit 910420b

Browse files
authored
Virtualization (#82)
1 parent 4e33c23 commit 910420b

8 files changed

Lines changed: 392 additions & 264 deletions

File tree

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ coverage
2727

2828
storybook-static
2929
.terraform
30-
terraform
30+
terraform
31+
32+
# Ignore vs files
33+
.vs

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Added
1515

16+
- `virtualization` features.
1617
- `pagingNavigationComponents` property to customize the paging navigation components
1718

1819
## [5.12.0] - 2025-09-15

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@dnd-kit/sortable": "^8.0.0",
5252
"@neolution-ch/react-pattern-ui": "^5.3.0",
5353
"@tanstack/react-table": "^8.12.0",
54+
"@tanstack/react-virtual": "^3.13.12",
5455
"react-loading-skeleton": "^3.3.1"
5556
},
5657
"devDependencies": {

src/lib/ReactDataTable/ReactDataTable.tsx

Lines changed: 253 additions & 263 deletions
Large diffs are not rendered by default.

src/lib/ReactDataTable/ReactDataTableProps.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { CSSProperties } from "react";
33
import { FilterModel } from "../types/TableState";
44
import { DragAndDropOptions } from "./DragAndDropOptions";
5+
import { VirtualizationOptions } from "./VirtualizationOptions";
56
import { PagingNavigationComponents } from "@neolution-ch/react-pattern-ui";
67

78
/**
@@ -98,6 +99,11 @@ export interface ReactDataTableProps<TData, TFilter extends FilterModel> {
9899
*/
99100
dragAndDropOptions?: DragAndDropOptions;
100101

102+
/**
103+
* to define virtualizer options
104+
*/
105+
virtualizerOptions?: VirtualizationOptions;
106+
101107
/**
102108
* to override the default message in case no entries is found
103109
*/
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { DraggableRow, InternalTableRow } from "./TableRows";
2+
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
3+
import { Row, Table } from "@tanstack/react-table";
4+
import { CSSProperties, useMemo } from "react";
5+
import { FilterModel } from "../types/TableState";
6+
import { ReactDataTableProps } from "./ReactDataTableProps";
7+
import { Virtualizer } from "@tanstack/react-virtual";
8+
9+
interface TableBodyProps<TData, TFilter extends FilterModel = Record<string, never>>
10+
extends Pick<ReactDataTableProps<TData, TFilter>, "enableRowClick" | "onRowClick" | "subRowComponent"> {
11+
enableDragAndDrop: boolean;
12+
table: Table<TData>;
13+
rowStyle?: (row: TData) => CSSProperties;
14+
virtualizer?: Virtualizer<HTMLDivElement, Element>;
15+
}
16+
17+
interface InternalRow<TData> {
18+
row: Row<TData>;
19+
rowStyle?: CSSProperties;
20+
}
21+
22+
const TableBody = <TData, TFilter extends FilterModel = Record<string, never>>(props: TableBodyProps<TData, TFilter>) => {
23+
const { enableDragAndDrop, table, rowStyle, enableRowClick, onRowClick, virtualizer, subRowComponent } = props;
24+
25+
if (enableDragAndDrop && !table.options.getRowId) {
26+
throw new Error("You must provide 'getRowId()' to data-table options in order to use the drag-and-drop feature.");
27+
}
28+
29+
const {
30+
options: { enableRowSelection, enableExpanding, fullRowSelectable },
31+
} = table;
32+
33+
const { rows } = table.getRowModel();
34+
35+
const virtualizerRows = virtualizer?.getVirtualItems();
36+
37+
const rowsToRender: InternalRow<TData>[] = useMemo(
38+
() =>
39+
virtualizerRows
40+
? virtualizerRows.map((virtualRow, index) => ({
41+
row: rows[virtualRow.index],
42+
rowStyle: {
43+
height: `${virtualRow.size}px`,
44+
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
45+
},
46+
}))
47+
: rows.map((row) => ({ row })),
48+
[rows, virtualizerRows],
49+
);
50+
51+
return enableDragAndDrop ? (
52+
<SortableContext items={table.getRowModel().rows.map((row) => row.id)} strategy={verticalListSortingStrategy}>
53+
{rowsToRender.map((x, index) => {
54+
const { row } = x;
55+
return (
56+
<DraggableRow<TData, TFilter>
57+
key={index}
58+
row={row}
59+
enableRowClick={enableRowClick as ReactDataTableProps<TData, TFilter>["enableRowClick"]}
60+
onRowClick={onRowClick as ReactDataTableProps<TData, TFilter>["onRowClick"]}
61+
enableRowSelection={enableRowSelection as boolean | ((row: Row<TData>) => boolean)}
62+
enableExpanding={enableExpanding as boolean | ((row: Row<TData>) => boolean)}
63+
rowStyle={{
64+
...(x.rowStyle ?? {}),
65+
...(rowStyle ? rowStyle(row.original) : {}),
66+
}}
67+
fullRowSelectable={fullRowSelectable}
68+
subRowComponent={subRowComponent as ReactDataTableProps<TData, TFilter>["subRowComponent"]}
69+
/>
70+
);
71+
})}
72+
</SortableContext>
73+
) : (
74+
<>
75+
{rowsToRender.map((x, index) => {
76+
const { row } = x;
77+
return (
78+
<InternalTableRow<TData, TFilter>
79+
key={index}
80+
row={row}
81+
enableRowClick={enableRowClick as ReactDataTableProps<TData, TFilter>["enableRowClick"]}
82+
onRowClick={onRowClick as ReactDataTableProps<TData, TFilter>["onRowClick"]}
83+
enableRowSelection={enableRowSelection as boolean | ((row: Row<TData>) => boolean)}
84+
enableExpanding={enableExpanding as boolean | ((row: Row<TData>) => boolean)}
85+
rowStyle={{
86+
...(x.rowStyle ?? {}),
87+
...(rowStyle ? rowStyle(row.original) : {}),
88+
}}
89+
fullRowSelectable={fullRowSelectable}
90+
hasPinnedColumns={table.getIsSomeColumnsPinned()}
91+
subRowComponent={subRowComponent as ReactDataTableProps<TData, TFilter>["subRowComponent"]}
92+
/>
93+
);
94+
})}
95+
</>
96+
);
97+
};
98+
99+
export { TableBody };
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { useVirtualizer } from "@tanstack/react-virtual";
2+
3+
type VirtualizaterOptions = Omit<Parameters<typeof useVirtualizer<HTMLDivElement, Element>>[0], "getScrollElement" | "count" | "enabled">;
4+
5+
export interface VirtualizationOptions extends VirtualizaterOptions {
6+
/**
7+
* indicates whether to enable virtualization
8+
* @default false
9+
*/
10+
enabled?: boolean;
11+
/**
12+
* indicates the height of the virtualized container
13+
* @default 600
14+
*/
15+
height?: number;
16+
}

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,11 +3029,23 @@
30293029
dependencies:
30303030
"@tanstack/table-core" "8.20.5"
30313031

3032+
"@tanstack/react-virtual@^3.13.12":
3033+
version "3.13.12"
3034+
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz#d372dc2783739cc04ec1a728ca8203937687a819"
3035+
integrity sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==
3036+
dependencies:
3037+
"@tanstack/virtual-core" "3.13.12"
3038+
30323039
"@tanstack/table-core@8.20.5":
30333040
version "8.20.5"
30343041
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
30353042
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==
30363043

3044+
"@tanstack/virtual-core@3.13.12":
3045+
version "3.13.12"
3046+
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
3047+
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==
3048+
30373049
"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.13.0", "@testing-library/dom@^8.3.0":
30383050
version "8.20.0"
30393051
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6"

0 commit comments

Comments
 (0)