Skip to content

Commit c87a2e4

Browse files
authored
fix(AnalyticalTable): improve sub-component size calculation & performance (#1461)
1 parent 616e167 commit c87a2e4

2 files changed

Lines changed: 111 additions & 20 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React, { useEffect, useRef } from 'react';
2+
import { VirtualItem } from 'react-virtual';
3+
4+
interface RowSubComponent {
5+
subComponentsHeight: Record<string, { rowId: string; subComponentHeight?: number }>;
6+
virtualRow: VirtualItem;
7+
dispatch: (e: { type: string; payload?: any }) => void;
8+
row: any;
9+
rowHeight: number;
10+
children: any;
11+
rows: any[];
12+
alwaysShowSubComponent: boolean;
13+
}
14+
15+
export const RowSubComponent = (props: RowSubComponent) => {
16+
const { subComponentsHeight, virtualRow, dispatch, row, rowHeight, children, rows, alwaysShowSubComponent } = props;
17+
const subComponentRef = useRef(null);
18+
19+
useEffect(() => {
20+
const subComponentHeightObserver = new ResizeObserver((entries) => {
21+
entries.forEach((entry) => {
22+
if (entry.borderBoxSize) {
23+
// Firefox implements `borderBoxSize` as a single content rect, rather than an array
24+
const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize;
25+
if (
26+
subComponentsHeight?.[virtualRow.index]?.subComponentHeight !== borderBoxSize.blockSize &&
27+
borderBoxSize.blockSize !== 0
28+
) {
29+
// use most common sub-component height of first 10 sub-components as default height
30+
if (alwaysShowSubComponent && subComponentsHeight && Object.keys(subComponentsHeight).length === 10) {
31+
const objGroupedByHeight = Object.values(subComponentsHeight).reduce((acc, cur) => {
32+
const count = acc?.[cur.subComponentHeight];
33+
if (typeof count === 'number') {
34+
return { ...acc, [cur.subComponentHeight]: count + 1 };
35+
}
36+
return { ...acc, [cur.subComponentHeight]: 1 };
37+
}, {});
38+
39+
const mostUsedHeight = Object.keys(objGroupedByHeight).reduce((a, b) =>
40+
objGroupedByHeight[a] > objGroupedByHeight[b] ? a : b
41+
);
42+
const estimatedHeights = rows.reduce((acc, cur, index) => {
43+
acc[index] = { subComponentHeight: parseInt(mostUsedHeight), rowId: cur.id };
44+
return acc;
45+
}, {});
46+
dispatch({
47+
type: 'SUB_COMPONENTS_HEIGHT',
48+
payload: { ...estimatedHeights, ...subComponentsHeight }
49+
});
50+
} else {
51+
dispatch({
52+
type: 'SUB_COMPONENTS_HEIGHT',
53+
payload: {
54+
...subComponentsHeight,
55+
[virtualRow.index]: { subComponentHeight: borderBoxSize.blockSize, rowId: row.id }
56+
}
57+
});
58+
}
59+
}
60+
}
61+
});
62+
});
63+
if (subComponentRef.current?.firstChild) {
64+
subComponentHeightObserver.observe(subComponentRef.current?.firstChild);
65+
}
66+
return () => {
67+
subComponentHeightObserver.disconnect();
68+
};
69+
}, [
70+
subComponentRef.current?.firstChild,
71+
subComponentsHeight,
72+
row.id,
73+
subComponentsHeight?.[virtualRow.index]?.subComponentHeight,
74+
virtualRow.index
75+
]);
76+
77+
return (
78+
<div
79+
ref={subComponentRef}
80+
style={{
81+
transform: `translateY(${rowHeight}px)`,
82+
position: 'absolute',
83+
width: '100%'
84+
}}
85+
>
86+
{children}
87+
</div>
88+
);
89+
};

packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBody.tsx

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import '@ui5/webcomponents-icons/dist/navigation-right-arrow';
33
import { useConsolidatedRef } from '@ui5/webcomponents-react-base/dist/useConsolidatedRef';
44
import React, { MutableRefObject, ReactNode, useCallback, useMemo, useRef } from 'react';
55
import { useVirtual } from 'react-virtual';
6+
import { RowSubComponent as SubComponent } from './RowSubComponent';
67

78
interface VirtualTableBodyProps {
89
classes: Record<string, string>;
@@ -191,17 +192,6 @@ export const VirtualTableBody = (props: VirtualTableBodyProps) => {
191192
>
192193
{rowVirtualizer.virtualItems.map((virtualRow) => {
193194
const row = rows[virtualRow.index];
194-
const setSubcomponentsRefs = (el) => {
195-
if (el?.offsetHeight && subComponentsHeight?.[virtualRow.index]?.subComponentHeight !== el?.offsetHeight) {
196-
dispatch({
197-
type: 'SUB_COMPONENTS_HEIGHT',
198-
payload: {
199-
...subComponentsHeight,
200-
[virtualRow.index]: { subComponentHeight: el?.offsetHeight, rowId: row.id }
201-
}
202-
});
203-
}
204-
};
205195
if (!row) {
206196
return (
207197
<div
@@ -217,7 +207,18 @@ export const VirtualTableBody = (props: VirtualTableBodyProps) => {
217207
prepareRow(row);
218208
const rowProps = row.getRowProps();
219209
const isNavigatedCell = markNavigatedRow(row);
220-
const RowSubComponent = typeof renderRowSubComponent === 'function' ? renderRowSubComponent(row) : null;
210+
const RowSubComponent = typeof renderRowSubComponent === 'function' ? renderRowSubComponent(row) : undefined;
211+
212+
if (!RowSubComponent && subComponentsHeight && subComponentsHeight?.[virtualRow.index]?.subComponentHeight) {
213+
dispatch({
214+
type: 'SUB_COMPONENTS_HEIGHT',
215+
payload: {
216+
...subComponentsHeight,
217+
[virtualRow.index]: { subComponentHeight: 0, rowId: row.id }
218+
}
219+
});
220+
}
221+
221222
return (
222223
<div
223224
{...rowProps}
@@ -228,16 +229,17 @@ export const VirtualTableBody = (props: VirtualTableBodyProps) => {
228229
}}
229230
>
230231
{RowSubComponent && (row.isExpanded || alwaysShowSubComponent) && (
231-
<div
232-
ref={setSubcomponentsRefs}
233-
style={{
234-
transform: `translateY(${rowHeight}px)`,
235-
position: 'absolute',
236-
width: '100%'
237-
}}
232+
<SubComponent
233+
subComponentsHeight={subComponentsHeight}
234+
virtualRow={virtualRow}
235+
dispatch={dispatch}
236+
row={row}
237+
rowHeight={rowHeight}
238+
rows={rows}
239+
alwaysShowSubComponent={alwaysShowSubComponent}
238240
>
239241
{RowSubComponent}
240-
</div>
242+
</SubComponent>
241243
)}
242244
{columnVirtualizer.virtualItems.map((virtualColumn, index) => {
243245
const cell = row.cells[virtualColumn.index];

0 commit comments

Comments
 (0)