Skip to content

Commit d29e59f

Browse files
committed
fix: virtualized spacers
1 parent 2c66f7b commit d29e59f

File tree

2 files changed

+152
-58
lines changed

2 files changed

+152
-58
lines changed

src/components/PaginatedTable/PaginatedTable.tsx

Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React from 'react';
22

33
import {usePaginatedTableState} from './PaginatedTableContext';
4-
import {TableChunk} from './TableChunk';
54
import {TableHead} from './TableHead';
65
import {DEFAULT_TABLE_ROW_HEIGHT} from './constants';
76
import {b} from './shared';
@@ -15,6 +14,7 @@ import type {
1514
RenderErrorMessage,
1615
} from './types';
1716
import {useScrollBasedChunks} from './useScrollBasedChunks';
17+
import {useVirtualizedTbodies} from './useVirtualizedTbodies';
1818

1919
import './PaginatedTable.scss';
2020

@@ -120,63 +120,22 @@ export const PaginatedTable = <T, F>({
120120
setIsInitialLoad(true);
121121
}, [initialEntitiesCount, setTotalEntities, setFoundEntities, setIsInitialLoad]);
122122

123-
const renderChunks = () => {
124-
const chunks: React.ReactElement[] = [];
125-
let i = 0;
126-
127-
while (i < activeChunks.length) {
128-
const isActive = activeChunks[i];
129-
130-
if (isActive) {
131-
// Render active chunk normally
132-
chunks.push(
133-
<TableChunk<T, F>
134-
key={i}
135-
id={i}
136-
calculatedCount={i === activeChunks.length - 1 ? lastChunkSize : chunkSize}
137-
chunkSize={chunkSize}
138-
rowHeight={rowHeight}
139-
columns={columns}
140-
fetchData={fetchData}
141-
filters={filters}
142-
tableName={tableName}
143-
sortParams={sortParams}
144-
getRowClassName={getRowClassName}
145-
renderErrorMessage={renderErrorMessage}
146-
renderEmptyDataMessage={renderEmptyDataMessage}
147-
onDataFetched={handleDataFetched}
148-
isActive={isActive}
149-
keepCache={keepCache}
150-
/>,
151-
);
152-
i++;
153-
} else {
154-
// Find consecutive inactive chunks and merge them
155-
const startIndex = i;
156-
let totalHeight = 0;
157-
158-
while (i < activeChunks.length && !activeChunks[i]) {
159-
const currentChunkSize =
160-
i === activeChunks.length - 1 ? lastChunkSize : chunkSize;
161-
totalHeight += currentChunkSize * rowHeight;
162-
i++;
163-
}
164-
165-
// Render merged empty tbody for consecutive inactive chunks
166-
chunks.push(
167-
<tbody
168-
key={`merged-${startIndex}-${i - 1}`}
169-
style={{
170-
height: `${totalHeight}px`,
171-
display: 'block',
172-
}}
173-
/>,
174-
);
175-
}
176-
}
177-
178-
return chunks;
179-
};
123+
const {renderChunks} = useVirtualizedTbodies({
124+
activeChunks,
125+
chunkSize,
126+
lastChunkSize,
127+
rowHeight,
128+
columns,
129+
fetchData,
130+
filters,
131+
tableName,
132+
sortParams,
133+
getRowClassName,
134+
renderErrorMessage,
135+
renderEmptyDataMessage,
136+
onDataFetched: handleDataFetched,
137+
keepCache,
138+
});
180139

181140
const renderTable = () => (
182141
<table className={b('table')}>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import React from 'react';
2+
3+
import {TableChunk} from './TableChunk';
4+
import type {
5+
Column,
6+
FetchData,
7+
GetRowClassName,
8+
PaginatedTableData,
9+
RenderEmptyDataMessage,
10+
RenderErrorMessage,
11+
SortParams,
12+
} from './types';
13+
14+
interface UseVirtualizedTbodiesProps<T, F> {
15+
activeChunks: boolean[];
16+
chunkSize: number;
17+
lastChunkSize: number;
18+
rowHeight: number;
19+
columns: Column<T>[];
20+
fetchData: FetchData<T, F>;
21+
filters?: F;
22+
tableName: string;
23+
sortParams?: SortParams;
24+
getRowClassName?: GetRowClassName<T>;
25+
renderErrorMessage?: RenderErrorMessage;
26+
renderEmptyDataMessage?: RenderEmptyDataMessage;
27+
onDataFetched: (data?: PaginatedTableData<T>) => void;
28+
keepCache?: boolean;
29+
}
30+
31+
export const useVirtualizedTbodies = <T, F>({
32+
activeChunks,
33+
chunkSize,
34+
lastChunkSize,
35+
rowHeight,
36+
columns,
37+
fetchData,
38+
filters,
39+
tableName,
40+
sortParams,
41+
getRowClassName,
42+
renderErrorMessage,
43+
renderEmptyDataMessage,
44+
onDataFetched,
45+
keepCache = true,
46+
}: UseVirtualizedTbodiesProps<T, F>) => {
47+
// Reusable spacer tbody elements (max 2: before and after active chunks)
48+
const beforeSpacerRef = React.useRef<HTMLTableSectionElement>(null);
49+
const afterSpacerRef = React.useRef<HTMLTableSectionElement>(null);
50+
51+
const renderChunks = React.useCallback(() => {
52+
const chunks: React.ReactElement[] = [];
53+
54+
// Count empty start chunks
55+
let startEmptyCount = 0;
56+
while (startEmptyCount < activeChunks.length && !activeChunks[startEmptyCount]) {
57+
startEmptyCount++;
58+
}
59+
60+
// Push start spacer if needed
61+
if (startEmptyCount > 0) {
62+
chunks.push(
63+
<tbody
64+
key="spacer-start"
65+
ref={beforeSpacerRef}
66+
style={{
67+
height: `${startEmptyCount * chunkSize * rowHeight}px`,
68+
display: 'block',
69+
}}
70+
/>,
71+
);
72+
}
73+
74+
// Collect and push active chunks
75+
for (let i = startEmptyCount; i < activeChunks.length && activeChunks[i]; i++) {
76+
chunks.push(
77+
<TableChunk<T, F>
78+
key={i}
79+
id={i}
80+
calculatedCount={i === activeChunks.length - 1 ? lastChunkSize : chunkSize}
81+
chunkSize={chunkSize}
82+
rowHeight={rowHeight}
83+
columns={columns}
84+
fetchData={fetchData}
85+
filters={filters}
86+
tableName={tableName}
87+
sortParams={sortParams}
88+
getRowClassName={getRowClassName}
89+
renderErrorMessage={renderErrorMessage}
90+
renderEmptyDataMessage={renderEmptyDataMessage}
91+
onDataFetched={onDataFetched}
92+
isActive={true}
93+
keepCache={keepCache}
94+
/>,
95+
);
96+
startEmptyCount = i + 1;
97+
}
98+
99+
// Count empty end chunks
100+
const endEmptyCount = activeChunks.length - startEmptyCount;
101+
102+
// Push end spacer if needed
103+
if (endEmptyCount > 0) {
104+
chunks.push(
105+
<tbody
106+
key="spacer-end"
107+
ref={afterSpacerRef}
108+
style={{
109+
height: `${endEmptyCount * chunkSize * rowHeight}px`,
110+
display: 'block',
111+
}}
112+
/>,
113+
);
114+
}
115+
116+
return chunks;
117+
}, [
118+
activeChunks,
119+
chunkSize,
120+
lastChunkSize,
121+
rowHeight,
122+
columns,
123+
fetchData,
124+
filters,
125+
tableName,
126+
sortParams,
127+
getRowClassName,
128+
renderErrorMessage,
129+
renderEmptyDataMessage,
130+
onDataFetched,
131+
keepCache,
132+
]);
133+
134+
return {renderChunks};
135+
};

0 commit comments

Comments
 (0)