Skip to content

Commit 6b01a69

Browse files
committed
add beta virtualization for table main
1 parent 262a60b commit 6b01a69

File tree

3 files changed

+176
-11
lines changed

3 files changed

+176
-11
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// hooks/useTableConfiguration.ts
2+
import { useMemo, useState, useEffect, useRef } from 'react';
3+
import { VIRTUAL_ROW_HEIGHTS, VIRTUAL_THRESHOLD, MIN_VIRTUAL_HEIGHT, TOOLBAR_HEIGHT, HEADER_HEIGHT } from '../tableUtils';
4+
5+
// ============= HOOK 1: TABLE MODE =============
6+
export function useTableMode(autoHeight: boolean) {
7+
return useMemo(() => ({
8+
isAutoMode: autoHeight,
9+
isFixedMode: !autoHeight
10+
}), [autoHeight]);
11+
}
12+
13+
// ============= HOOK 2: CONTAINER HEIGHT MEASUREMENT =============
14+
export function useContainerHeight(enabled: boolean) {
15+
const [containerHeight, setContainerHeight] = useState<number>(0);
16+
const containerRef = useRef<HTMLDivElement>(null);
17+
18+
useEffect(() => {
19+
const element = containerRef.current;
20+
if (!enabled || !element) return;
21+
22+
const measureHeight = () => {
23+
if (element) {
24+
setContainerHeight(element.clientHeight);
25+
}
26+
};
27+
28+
measureHeight();
29+
const resizeObserver = new ResizeObserver(measureHeight);
30+
resizeObserver.observe(element);
31+
32+
return () => {
33+
resizeObserver.disconnect();
34+
};
35+
}, [enabled]);
36+
37+
return { containerHeight, containerRef };
38+
}
39+
40+
// ============= HOOK 3: VIRTUALIZATION CALCULATION =============
41+
export function useVirtualization(
42+
containerHeight: number,
43+
dataLength: number,
44+
tableSize: 'small' | 'middle' | 'large',
45+
config: {
46+
showToolbar: boolean;
47+
showHeader: boolean;
48+
stickyToolbar: boolean;
49+
isFixedMode: boolean;
50+
}
51+
) {
52+
return useMemo(() => {
53+
if (!config.isFixedMode) {
54+
return {
55+
enabled: false,
56+
scrollY: undefined,
57+
itemHeight: VIRTUAL_ROW_HEIGHTS[tableSize],
58+
reason: 'auto_mode'
59+
};
60+
}
61+
62+
// Calculate reserved space
63+
const toolbarSpace = config.showToolbar && config.stickyToolbar ? TOOLBAR_HEIGHT : 0;
64+
const headerSpace = config.showHeader ? HEADER_HEIGHT : 0;
65+
const availableHeight = containerHeight - toolbarSpace - headerSpace;
66+
67+
// Check if virtualization should be enabled
68+
const canVirtualize = availableHeight > MIN_VIRTUAL_HEIGHT;
69+
const hasEnoughData = dataLength >= VIRTUAL_THRESHOLD;
70+
const enabled = canVirtualize && hasEnoughData;
71+
72+
return {
73+
enabled,
74+
scrollY: availableHeight > 0 ? availableHeight : undefined,
75+
itemHeight: VIRTUAL_ROW_HEIGHTS[tableSize],
76+
reason: !canVirtualize
77+
? 'insufficient_height'
78+
: !hasEnoughData
79+
? 'insufficient_data'
80+
: 'enabled'
81+
};
82+
}, [containerHeight, dataLength, tableSize, config]);
83+
}
84+
85+
// ============= HOOK 4: SCROLL CONFIGURATION =============
86+
export function useScrollConfiguration(
87+
virtualizationEnabled: boolean,
88+
scrollY: number | undefined,
89+
totalColumnsWidth: number
90+
) {
91+
return useMemo(() => {
92+
const baseScroll = { x: totalColumnsWidth };
93+
94+
if (!virtualizationEnabled || !scrollY) {
95+
return {
96+
scroll: baseScroll,
97+
virtual: false
98+
};
99+
}
100+
101+
return {
102+
scroll: {
103+
x: totalColumnsWidth,
104+
y: scrollY
105+
},
106+
virtual: true
107+
};
108+
}, [virtualizationEnabled, scrollY, totalColumnsWidth]);
109+
}

client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,17 @@ import { TableSummary } from "./tableSummaryComp";
4848
import Skeleton from "antd/es/skeleton";
4949
import { SkeletonButtonProps } from "antd/es/skeleton/Button";
5050
import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
51-
import { useUpdateEffect } from "react-use";
51+
import { useUpdateEffect } from "react-use";import {
52+
useTableMode,
53+
useContainerHeight,
54+
useVirtualization,
55+
useScrollConfiguration
56+
} from './hooks/useTableConfiguration';
5257

5358
export const EMPTY_ROW_KEY = 'empty_row';
5459

60+
61+
5562
function genLinerGradient(color: string) {
5663
return isValidColor(color) ? `linear-gradient(${color}, ${color})` : color;
5764
}
@@ -565,6 +572,11 @@ type CustomTableProps<RecordType> = Omit<TableProps<RecordType>, "components" |
565572
rowAutoHeight?: boolean;
566573
customLoading?: boolean;
567574
onCellClick: (columnName: string, dataIndex: string) => void;
575+
virtual?: boolean;
576+
scroll?: {
577+
x?: number | string;
578+
y?: number | string;
579+
};
568580
};
569581

570582
const TableCellView = React.memo((props: {
@@ -798,9 +810,6 @@ function ResizeableTableComp<RecordType extends object>(props: CustomTableProps<
798810
{...restProps}
799811
pagination={false}
800812
columns={memoizedColumns}
801-
scroll={{
802-
x: COL_MIN_WIDTH * columns.length,
803-
}}
804813
/>
805814
);
806815
}
@@ -848,7 +857,6 @@ export const TableCompView = React.memo((props: {
848857
const toolbarStyle = compChildren.toolbarStyle.getView();
849858
const hideToolbar = compChildren.hideToolbar.getView()
850859
const rowAutoHeight = compChildren.rowAutoHeight.getView();
851-
const tableAutoHeight = comp.getTableAutoHeight();
852860
const showHorizontalScrollbar = compChildren.showHorizontalScrollbar.getView();
853861
const showVerticalScrollbar = compChildren.showVerticalScrollbar.getView();
854862
const visibleResizables = compChildren.visibleResizables.getView();
@@ -872,6 +880,7 @@ export const TableCompView = React.memo((props: {
872880
const onEvent = useMemo(() => compChildren.onEvent.getView(), [compChildren.onEvent]);
873881
const currentExpandedRows = useMemo(() => compChildren.currentExpandedRows.getView(), [compChildren.currentExpandedRows]);
874882
const dynamicColumn = compChildren.dynamicColumn.getView();
883+
875884
const dynamicColumnConfig = useMemo(
876885
() => compChildren.dynamicColumnConfig.getView(),
877886
[compChildren.dynamicColumnConfig]
@@ -1006,6 +1015,31 @@ export const TableCompView = React.memo((props: {
10061015

10071016
const childrenProps = childrenToProps(comp.children);
10081017

1018+
// Table mode and height configuration
1019+
const tableMode = useTableMode(comp.getTableAutoHeight());
1020+
const { containerHeight, containerRef } = useContainerHeight(tableMode.isFixedMode);
1021+
1022+
const virtualizationConfig = useVirtualization(
1023+
containerHeight,
1024+
pageDataInfo.data.length,
1025+
size as 'small' | 'middle' | 'large',
1026+
{
1027+
showToolbar: !hideToolbar,
1028+
showHeader: !compChildren.hideHeader.getView(),
1029+
stickyToolbar: toolbar.fixedToolbar && toolbar.position === 'above',
1030+
isFixedMode: tableMode.isFixedMode
1031+
}
1032+
);
1033+
const totalColumnsWidth = COL_MIN_WIDTH * antdColumns.length;
1034+
const scrollConfig = useScrollConfiguration(
1035+
virtualizationConfig.enabled,
1036+
virtualizationConfig.scrollY,
1037+
totalColumnsWidth
1038+
);
1039+
1040+
1041+
1042+
10091043
useMergeCompStyles(
10101044
childrenProps as Record<string, any>,
10111045
comp.dispatch
@@ -1092,20 +1126,20 @@ export const TableCompView = React.memo((props: {
10921126
return (
10931127
<BackgroundColorContext.Provider value={style.background} >
10941128
<BackgroundWrapper
1095-
ref={ref}
1129+
ref={containerRef}
10961130
$style={style}
1097-
$tableAutoHeight={tableAutoHeight}
1131+
$tableAutoHeight={tableMode.isAutoMode}
10981132
$showHorizontalScrollbar={showHorizontalScrollbar}
10991133
$showVerticalScrollbar={showVerticalScrollbar}
11001134
$fixedToolbar={toolbar.fixedToolbar}
11011135
>
1102-
{toolbar.position === "above" && !hideToolbar && (toolbar.fixedToolbar || (tableAutoHeight && showHorizontalScrollbar)) && toolbarView}
1136+
{toolbar.position === "above" && !hideToolbar && (toolbar.fixedToolbar || (tableMode.isAutoMode && showHorizontalScrollbar)) && toolbarView}
11031137
<ScrollBar
11041138
className="table-scrollbar-wrapper"
11051139
style={{ height: "100%", margin: "0px", padding: "0px" }}
11061140
hideScrollbar={hideScrollbar}
1107-
prefixNode={toolbar.position === "above" && !toolbar.fixedToolbar && !(tableAutoHeight && showHorizontalScrollbar) && toolbarView}
1108-
suffixNode={toolbar.position === "below" && !toolbar.fixedToolbar && !(tableAutoHeight && showHorizontalScrollbar) && toolbarView}
1141+
prefixNode={toolbar.position === "above" && !toolbar.fixedToolbar && !(tableMode.isAutoMode && showHorizontalScrollbar) && toolbarView}
1142+
suffixNode={toolbar.position === "below" && !toolbar.fixedToolbar && !(tableMode.isAutoMode && showHorizontalScrollbar) && toolbarView}
11091143
>
11101144
<TableWrapper
11111145
$style={style}
@@ -1162,13 +1196,15 @@ export const TableCompView = React.memo((props: {
11621196
});
11631197
}}
11641198
summary={summaryView}
1199+
scroll={scrollConfig.scroll}
1200+
virtual={scrollConfig.virtual}
11651201
/>
11661202
<SlotConfigContext.Provider value={{ modalWidth: width && Math.max(width, 300) }}>
11671203
{expansion.expandModalView}
11681204
</SlotConfigContext.Provider>
11691205
</TableWrapper>
11701206
</ScrollBar>
1171-
{toolbar.position === "below" && !hideToolbar && (toolbar.fixedToolbar || (tableAutoHeight && showHorizontalScrollbar)) && toolbarView}
1207+
{toolbar.position === "below" && !hideToolbar && (toolbar.fixedToolbar || (tableMode.isAutoMode && showHorizontalScrollbar)) && toolbarView}
11721208
</BackgroundWrapper>
11731209

11741210
</BackgroundColorContext.Provider>

client/packages/lowcoder/src/comps/comps/tableComp/tableUtils.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ export const OB_ROW_RECORD = "__ob_origin_record";
3030
export const COL_MIN_WIDTH = 55;
3131
export const COL_MAX_WIDTH = 500;
3232

33+
/*
34+
35+
======================== Virtualization constants =========================
36+
37+
*/
38+
export const VIRTUAL_ROW_HEIGHTS = {
39+
small: 32,
40+
middle: 48,
41+
large: 80
42+
} as const;
43+
44+
45+
export const VIRTUAL_THRESHOLD = 50;
46+
export const MIN_VIRTUAL_HEIGHT = 200; // Minimum container height needed for virtualization
47+
export const TOOLBAR_HEIGHT = 48; // Standard toolbar height
48+
export const HEADER_HEIGHT = 40; // Standard header height
49+
50+
/* ========================== End of Virtualization constants ========================== */
51+
52+
3353
/**
3454
* Add __originIndex__, mainly for the logic of the default key
3555
*/

0 commit comments

Comments
 (0)