Skip to content

Commit 8abe167

Browse files
committed
fix scroll customization and refactor
1 parent e5654e5 commit 8abe167

File tree

5 files changed

+835
-768
lines changed

5 files changed

+835
-768
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import { default as Table, TableProps, ColumnType } from "antd/es/table";
2+
import React, { useCallback, useMemo, useRef, useState } from "react";
3+
import { Resizable } from "react-resizable";
4+
import styled from "styled-components";
5+
import _ from "lodash";
6+
import { useUserViewMode } from "util/hooks";
7+
import { ReactRef, ResizeHandleAxis } from "layout/gridLayoutPropTypes";
8+
import { COL_MIN_WIDTH, RecordType, CustomColumnType } from "./tableUtils";
9+
import { RowColorViewType, RowHeightViewType } from "./tableTypes";
10+
import { TableColumnStyleType, TableColumnLinkStyleType } from "comps/controls/styleControlConstants";
11+
import { CellColorViewType } from "./column/tableColumnComp";
12+
import { TableCellView } from "./TableCell";
13+
import { TableRowView } from "./TableRow";
14+
15+
const TitleResizeHandle = styled.span`
16+
position: absolute;
17+
top: 0;
18+
right: -5px;
19+
width: 10px;
20+
height: 100%;
21+
cursor: col-resize;
22+
z-index: 1;
23+
`;
24+
25+
const TableTh = styled.th<{ width?: number }>`
26+
overflow: hidden;
27+
28+
> div {
29+
overflow: hidden;
30+
white-space: pre;
31+
text-overflow: ellipsis;
32+
}
33+
34+
${(props) => props.width && `width: ${props.width}px`};
35+
`;
36+
37+
const ResizeableTitle = React.forwardRef<HTMLTableHeaderCellElement, any>((props, ref) => {
38+
const { onResize, onResizeStop, width, viewModeResizable, ...restProps } = props;
39+
const [childWidth, setChildWidth] = useState(0);
40+
const resizeRef = useRef<HTMLTableHeaderCellElement>(null);
41+
const isUserViewMode = useUserViewMode();
42+
43+
const updateChildWidth = useCallback(() => {
44+
if (resizeRef.current) {
45+
const width = resizeRef.current.getBoundingClientRect().width;
46+
setChildWidth(width);
47+
}
48+
}, []);
49+
50+
React.useEffect(() => {
51+
updateChildWidth();
52+
const resizeObserver = new ResizeObserver(() => {
53+
updateChildWidth();
54+
});
55+
56+
if (resizeRef.current) {
57+
resizeObserver.observe(resizeRef.current);
58+
}
59+
60+
return () => {
61+
resizeObserver.disconnect();
62+
};
63+
}, [updateChildWidth]);
64+
65+
React.useImperativeHandle(ref, () => resizeRef.current!, []);
66+
67+
const isNotDataColumn = _.isNil(restProps.title);
68+
if ((isUserViewMode && !restProps.viewModeResizable) || isNotDataColumn) {
69+
return <TableTh ref={resizeRef} {...restProps} width={width} />;
70+
}
71+
72+
return (
73+
<Resizable
74+
width={width > 0 ? width : childWidth}
75+
height={0}
76+
onResize={(e: React.SyntheticEvent, { size }: { size: { width: number } }) => {
77+
e.stopPropagation();
78+
onResize(size.width);
79+
}}
80+
onResizeStart={(e: React.SyntheticEvent) => {
81+
updateChildWidth();
82+
e.stopPropagation();
83+
e.preventDefault();
84+
}}
85+
onResizeStop={onResizeStop}
86+
draggableOpts={{ enableUserSelectHack: false }}
87+
handle={(axis: ResizeHandleAxis, ref: ReactRef<HTMLDivElement>) => (
88+
<TitleResizeHandle
89+
ref={ref}
90+
onClick={(e) => {
91+
e.preventDefault();
92+
e.stopPropagation();
93+
}}
94+
/>
95+
)}
96+
>
97+
<TableTh ref={resizeRef} {...restProps} title="" />
98+
</Resizable>
99+
);
100+
});
101+
102+
type CustomTableProps<RecordType> = Omit<TableProps<RecordType>, "components" | "columns"> & {
103+
columns: CustomColumnType<RecordType>[];
104+
viewModeResizable: boolean;
105+
visibleResizables: boolean;
106+
rowColorFn: RowColorViewType;
107+
rowHeightFn: RowHeightViewType;
108+
columnsStyle: TableColumnStyleType;
109+
size?: string;
110+
rowAutoHeight?: boolean;
111+
customLoading?: boolean;
112+
onCellClick: (columnName: string, dataIndex: string) => void;
113+
virtual?: boolean;
114+
scroll?: {
115+
x?: number | string;
116+
y?: number | string;
117+
};
118+
};
119+
120+
function ResizeableTableComp<RecordType extends object>(props: CustomTableProps<RecordType>) {
121+
const {
122+
columns,
123+
viewModeResizable,
124+
visibleResizables,
125+
rowColorFn,
126+
rowHeightFn,
127+
columnsStyle,
128+
size,
129+
rowAutoHeight,
130+
customLoading,
131+
onCellClick,
132+
...restProps
133+
} = props;
134+
const [resizeData, setResizeData] = useState({ index: -1, width: -1 });
135+
136+
// Memoize resize handlers
137+
const handleResize = useCallback((width: number, index: number) => {
138+
setResizeData({ index, width });
139+
}, []);
140+
141+
const handleResizeStop = useCallback((width: number, index: number, onWidthResize?: (width: number) => void) => {
142+
setResizeData({ index: -1, width: -1 });
143+
if (onWidthResize) {
144+
onWidthResize(width);
145+
}
146+
}, []);
147+
148+
// Memoize cell handlers
149+
const createCellHandler = useCallback((col: CustomColumnType<RecordType>) => {
150+
return (record: RecordType, index: number) => ({
151+
record,
152+
title: String(col.dataIndex),
153+
rowColorFn,
154+
rowHeightFn,
155+
cellColorFn: col.cellColorFn,
156+
rowIndex: index,
157+
columnsStyle,
158+
columnStyle: col.style,
159+
linkStyle: col.linkStyle,
160+
tableSize: size,
161+
autoHeight: rowAutoHeight,
162+
onClick: () => onCellClick(col.titleText, String(col.dataIndex)),
163+
loading: customLoading,
164+
customAlign: col.align,
165+
});
166+
}, [rowColorFn, rowHeightFn, columnsStyle, size, rowAutoHeight, onCellClick, customLoading]);
167+
168+
// Memoize header cell handlers
169+
const createHeaderCellHandler = useCallback((col: CustomColumnType<RecordType>, index: number, resizeWidth: number) => {
170+
return () => ({
171+
width: resizeWidth,
172+
title: col.titleText,
173+
viewModeResizable,
174+
onResize: (width: React.SyntheticEvent) => {
175+
if (width) {
176+
handleResize(Number(width), index);
177+
}
178+
},
179+
onResizeStop: (e: React.SyntheticEvent, { size }: { size: { width: number } }) => {
180+
handleResizeStop(size.width, index, col.onWidthResize);
181+
},
182+
});
183+
}, [viewModeResizable, handleResize, handleResizeStop]);
184+
185+
// Memoize columns to prevent unnecessary re-renders
186+
const memoizedColumns = useMemo(() => {
187+
return columns.map((col, index) => {
188+
const { width, style, linkStyle, cellColorFn, onWidthResize, ...restCol } = col;
189+
const resizeWidth = (resizeData.index === index ? resizeData.width : col.width) ?? 0;
190+
191+
const column: ColumnType<RecordType> = {
192+
...restCol,
193+
width: typeof resizeWidth === "number" && resizeWidth > 0 ? resizeWidth : undefined,
194+
minWidth: typeof resizeWidth === "number" && resizeWidth > 0 ? undefined : COL_MIN_WIDTH,
195+
onCell: (record: RecordType, index?: number) => createCellHandler(col)(record, index ?? 0),
196+
onHeaderCell: () => createHeaderCellHandler(col, index, Number(resizeWidth))(),
197+
};
198+
return column;
199+
});
200+
}, [columns, resizeData, createCellHandler, createHeaderCellHandler]);
201+
202+
return (
203+
<Table<RecordType>
204+
components={{
205+
header: {
206+
cell: ResizeableTitle,
207+
},
208+
body: {
209+
cell: TableCellView,
210+
row: TableRowView,
211+
},
212+
}}
213+
{...restProps}
214+
pagination={false}
215+
columns={memoizedColumns}
216+
/>
217+
);
218+
}
219+
ResizeableTableComp.whyDidYouRender = true;
220+
221+
export const ResizeableTable = React.memo(ResizeableTableComp) as typeof ResizeableTableComp;
222+
export type { CustomTableProps };

0 commit comments

Comments
 (0)