mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
fix table not re-rendering on column configuration change
This commit is contained in:
@@ -130,312 +130,432 @@ interface VirtualizedTableGridProps {
|
|||||||
totalRowCount: number;
|
totalRowCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VirtualizedTableGrid = React.memo(
|
const VirtualizedTableGrid = ({
|
||||||
({
|
activeRowId,
|
||||||
activeRowId,
|
calculatedColumnWidths,
|
||||||
calculatedColumnWidths,
|
CellComponent,
|
||||||
CellComponent,
|
cellPadding,
|
||||||
cellPadding,
|
controls,
|
||||||
controls,
|
data,
|
||||||
data,
|
enableAlternateRowColors,
|
||||||
enableAlternateRowColors,
|
enableColumnReorder,
|
||||||
enableColumnReorder,
|
enableColumnResize,
|
||||||
enableColumnResize,
|
enableDrag,
|
||||||
enableDrag,
|
enableExpansion,
|
||||||
enableExpansion,
|
enableHeader,
|
||||||
enableHeader,
|
enableHorizontalBorders,
|
||||||
enableHorizontalBorders,
|
enableRowHoverHighlight,
|
||||||
enableRowHoverHighlight,
|
enableSelection,
|
||||||
enableSelection,
|
enableVerticalBorders,
|
||||||
enableVerticalBorders,
|
getRowHeight,
|
||||||
getRowHeight,
|
groups,
|
||||||
groups,
|
headerHeight,
|
||||||
headerHeight,
|
internalState,
|
||||||
internalState,
|
itemType,
|
||||||
itemType,
|
mergedRowRef,
|
||||||
mergedRowRef,
|
onRangeChanged,
|
||||||
onRangeChanged,
|
parsedColumns,
|
||||||
parsedColumns,
|
pinnedLeftColumnCount,
|
||||||
pinnedLeftColumnCount,
|
pinnedLeftColumnRef,
|
||||||
pinnedLeftColumnRef,
|
pinnedRightColumnCount,
|
||||||
pinnedRightColumnCount,
|
pinnedRightColumnRef,
|
||||||
pinnedRightColumnRef,
|
pinnedRowCount,
|
||||||
pinnedRowCount,
|
pinnedRowRef,
|
||||||
pinnedRowRef,
|
playerContext,
|
||||||
playerContext,
|
showLeftShadow,
|
||||||
showLeftShadow,
|
showRightShadow,
|
||||||
showRightShadow,
|
showTopShadow,
|
||||||
showTopShadow,
|
size,
|
||||||
size,
|
startRowIndex,
|
||||||
startRowIndex,
|
tableId,
|
||||||
tableId,
|
totalColumnCount,
|
||||||
totalColumnCount,
|
totalRowCount,
|
||||||
totalRowCount,
|
}: VirtualizedTableGridProps) => {
|
||||||
}: VirtualizedTableGridProps) => {
|
const columnWidth = useCallback(
|
||||||
const columnWidth = useCallback(
|
(index: number) => calculatedColumnWidths[index],
|
||||||
(index: number) => calculatedColumnWidths[index],
|
[calculatedColumnWidths],
|
||||||
[calculatedColumnWidths],
|
);
|
||||||
|
|
||||||
|
// Calculate pinned column widths for group header positioning
|
||||||
|
const pinnedLeftColumnWidths = useMemo(() => {
|
||||||
|
return Array.from({ length: pinnedLeftColumnCount }, (_, i) => columnWidth(i));
|
||||||
|
}, [pinnedLeftColumnCount, columnWidth]);
|
||||||
|
|
||||||
|
const pinnedRightColumnWidths = useMemo(() => {
|
||||||
|
return Array.from({ length: pinnedRightColumnCount }, (_, i) =>
|
||||||
|
columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
|
||||||
);
|
);
|
||||||
|
}, [pinnedRightColumnCount, pinnedLeftColumnCount, totalColumnCount, columnWidth]);
|
||||||
|
|
||||||
// Calculate pinned column widths for group header positioning
|
// Create data array with group headers inserted as null values
|
||||||
const pinnedLeftColumnWidths = useMemo(() => {
|
// Groups are defined by itemCount, so we calculate indexes based on cumulative item counts
|
||||||
return Array.from({ length: pinnedLeftColumnCount }, (_, i) => columnWidth(i));
|
const dataWithGroups = useMemo(() => {
|
||||||
}, [pinnedLeftColumnCount, columnWidth]);
|
const result: (null | unknown)[] = enableHeader ? [null] : [];
|
||||||
|
|
||||||
const pinnedRightColumnWidths = useMemo(() => {
|
if (!groups || groups.length === 0) {
|
||||||
return Array.from({ length: pinnedRightColumnCount }, (_, i) =>
|
// No groups, just add all data
|
||||||
columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
|
result.push(...data);
|
||||||
);
|
|
||||||
}, [pinnedRightColumnCount, pinnedLeftColumnCount, totalColumnCount, columnWidth]);
|
|
||||||
|
|
||||||
// Create data array with group headers inserted as null values
|
|
||||||
// Groups are defined by itemCount, so we calculate indexes based on cumulative item counts
|
|
||||||
const dataWithGroups = useMemo(() => {
|
|
||||||
const result: (null | unknown)[] = enableHeader ? [null] : [];
|
|
||||||
|
|
||||||
if (!groups || groups.length === 0) {
|
|
||||||
// No groups, just add all data
|
|
||||||
result.push(...data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate group header indexes based on itemCounts
|
|
||||||
const groupIndexes: number[] = [];
|
|
||||||
let cumulativeDataIndex = 0;
|
|
||||||
const headerOffset = enableHeader ? 1 : 0;
|
|
||||||
|
|
||||||
groups.forEach((group, groupIndex) => {
|
|
||||||
// Group header appears before its items
|
|
||||||
// Index = header offset + cumulative data index + number of previous group headers
|
|
||||||
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
|
||||||
groupIndexes.push(groupHeaderIndex);
|
|
||||||
cumulativeDataIndex += group.itemCount;
|
|
||||||
});
|
|
||||||
|
|
||||||
let dataIndex = 0;
|
|
||||||
const startIndex = enableHeader ? 1 : 0;
|
|
||||||
let groupHeaderCount = 0;
|
|
||||||
|
|
||||||
// Iterate through the expanded row space (data + group headers)
|
|
||||||
for (
|
|
||||||
let rowIndex = startIndex;
|
|
||||||
rowIndex < startIndex + data.length + groupIndexes.length;
|
|
||||||
rowIndex++
|
|
||||||
) {
|
|
||||||
// Check if this row should have a group header
|
|
||||||
const expectedGroupIndex = groupIndexes[groupHeaderCount];
|
|
||||||
if (expectedGroupIndex !== undefined && rowIndex === expectedGroupIndex) {
|
|
||||||
result.push(null); // Group header row
|
|
||||||
groupHeaderCount++;
|
|
||||||
} else if (dataIndex < data.length) {
|
|
||||||
result.push(data[dataIndex]);
|
|
||||||
dataIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}, [data, enableHeader, groups]);
|
}
|
||||||
|
|
||||||
const adjustedRowIndexMap = useMemo(() => {
|
// Calculate group header indexes based on itemCounts
|
||||||
const map = new Map<number, number>();
|
const groupIndexes: number[] = [];
|
||||||
|
let cumulativeDataIndex = 0;
|
||||||
|
const headerOffset = enableHeader ? 1 : 0;
|
||||||
|
|
||||||
if (!groups || groups.length === 0) {
|
groups.forEach((group, groupIndex) => {
|
||||||
const startIndex = enableHeader ? 1 : 0;
|
// Group header appears before its items
|
||||||
const endIndex = enableHeader ? dataWithGroups.length : dataWithGroups.length;
|
// Index = header offset + cumulative data index + number of previous group headers
|
||||||
for (let rowIndex = startIndex; rowIndex < endIndex; rowIndex++) {
|
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
||||||
map.set(rowIndex, enableHeader ? rowIndex : rowIndex + 1);
|
groupIndexes.push(groupHeaderIndex);
|
||||||
}
|
cumulativeDataIndex += group.itemCount;
|
||||||
return map;
|
});
|
||||||
|
|
||||||
|
let dataIndex = 0;
|
||||||
|
const startIndex = enableHeader ? 1 : 0;
|
||||||
|
let groupHeaderCount = 0;
|
||||||
|
|
||||||
|
// Iterate through the expanded row space (data + group headers)
|
||||||
|
for (
|
||||||
|
let rowIndex = startIndex;
|
||||||
|
rowIndex < startIndex + data.length + groupIndexes.length;
|
||||||
|
rowIndex++
|
||||||
|
) {
|
||||||
|
// Check if this row should have a group header
|
||||||
|
const expectedGroupIndex = groupIndexes[groupHeaderCount];
|
||||||
|
if (expectedGroupIndex !== undefined && rowIndex === expectedGroupIndex) {
|
||||||
|
result.push(null); // Group header row
|
||||||
|
groupHeaderCount++;
|
||||||
|
} else if (dataIndex < data.length) {
|
||||||
|
result.push(data[dataIndex]);
|
||||||
|
dataIndex++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, [data, enableHeader, groups]);
|
||||||
|
|
||||||
const groupIndexes: number[] = [];
|
const adjustedRowIndexMap = useMemo(() => {
|
||||||
let cumulativeDataIndex = 0;
|
const map = new Map<number, number>();
|
||||||
const headerOffset = enableHeader ? 1 : 0;
|
|
||||||
|
|
||||||
groups.forEach((group, groupIndex) => {
|
|
||||||
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
|
||||||
groupIndexes.push(groupHeaderIndex);
|
|
||||||
cumulativeDataIndex += group.itemCount;
|
|
||||||
});
|
|
||||||
|
|
||||||
let adjustedIndex = 1;
|
|
||||||
const startIndex = enableHeader ? 0 : 0;
|
|
||||||
const endIndex = dataWithGroups.length;
|
|
||||||
|
|
||||||
|
if (!groups || groups.length === 0) {
|
||||||
|
const startIndex = enableHeader ? 1 : 0;
|
||||||
|
const endIndex = enableHeader ? dataWithGroups.length : dataWithGroups.length;
|
||||||
for (let rowIndex = startIndex; rowIndex < endIndex; rowIndex++) {
|
for (let rowIndex = startIndex; rowIndex < endIndex; rowIndex++) {
|
||||||
if (enableHeader && rowIndex === 0) {
|
map.set(rowIndex, enableHeader ? rowIndex : rowIndex + 1);
|
||||||
// Header row
|
|
||||||
map.set(rowIndex, 0);
|
|
||||||
} else if (groupIndexes.includes(rowIndex)) {
|
|
||||||
// Group header row - don't increment adjustedIndex
|
|
||||||
map.set(rowIndex, 0);
|
|
||||||
} else {
|
|
||||||
// Data row
|
|
||||||
map.set(rowIndex, adjustedIndex);
|
|
||||||
adjustedIndex++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}, [dataWithGroups, enableHeader, groups]);
|
}
|
||||||
|
|
||||||
const itemProps: TableItemProps = useMemo(
|
const groupIndexes: number[] = [];
|
||||||
() => ({
|
let cumulativeDataIndex = 0;
|
||||||
activeRowId,
|
const headerOffset = enableHeader ? 1 : 0;
|
||||||
adjustedRowIndexMap,
|
|
||||||
calculatedColumnWidths,
|
|
||||||
cellPadding,
|
|
||||||
columns: parsedColumns,
|
|
||||||
controls,
|
|
||||||
data: dataWithGroups,
|
|
||||||
enableAlternateRowColors,
|
|
||||||
enableColumnReorder,
|
|
||||||
enableColumnResize,
|
|
||||||
enableDrag,
|
|
||||||
enableExpansion,
|
|
||||||
enableHeader,
|
|
||||||
enableHorizontalBorders,
|
|
||||||
enableRowHoverHighlight,
|
|
||||||
enableSelection,
|
|
||||||
enableVerticalBorders,
|
|
||||||
getRowHeight,
|
|
||||||
groups,
|
|
||||||
internalState,
|
|
||||||
itemType,
|
|
||||||
pinnedLeftColumnCount,
|
|
||||||
pinnedLeftColumnWidths,
|
|
||||||
pinnedRightColumnCount,
|
|
||||||
pinnedRightColumnWidths,
|
|
||||||
playerContext,
|
|
||||||
size,
|
|
||||||
startRowIndex,
|
|
||||||
tableId,
|
|
||||||
}),
|
|
||||||
[
|
|
||||||
activeRowId,
|
|
||||||
adjustedRowIndexMap,
|
|
||||||
calculatedColumnWidths,
|
|
||||||
cellPadding,
|
|
||||||
parsedColumns,
|
|
||||||
controls,
|
|
||||||
dataWithGroups,
|
|
||||||
enableAlternateRowColors,
|
|
||||||
enableColumnReorder,
|
|
||||||
enableColumnResize,
|
|
||||||
enableDrag,
|
|
||||||
enableExpansion,
|
|
||||||
enableHeader,
|
|
||||||
enableHorizontalBorders,
|
|
||||||
enableRowHoverHighlight,
|
|
||||||
enableSelection,
|
|
||||||
enableVerticalBorders,
|
|
||||||
getRowHeight,
|
|
||||||
groups,
|
|
||||||
internalState,
|
|
||||||
itemType,
|
|
||||||
pinnedLeftColumnCount,
|
|
||||||
pinnedLeftColumnWidths,
|
|
||||||
pinnedRightColumnCount,
|
|
||||||
pinnedRightColumnWidths,
|
|
||||||
playerContext,
|
|
||||||
size,
|
|
||||||
startRowIndex,
|
|
||||||
tableId,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
const PinnedRowCell = useCallback(
|
groups.forEach((group, groupIndex) => {
|
||||||
(cellProps: CellComponentProps & TableItemProps) => {
|
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
|
||||||
return (
|
groupIndexes.push(groupHeaderIndex);
|
||||||
<CellComponent
|
cumulativeDataIndex += group.itemCount;
|
||||||
{...cellProps}
|
});
|
||||||
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[pinnedLeftColumnCount, CellComponent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const PinnedColumnCell = useCallback(
|
let adjustedIndex = 1;
|
||||||
(cellProps: CellComponentProps & TableItemProps) => {
|
const startIndex = enableHeader ? 0 : 0;
|
||||||
return (
|
const endIndex = dataWithGroups.length;
|
||||||
<CellComponent {...cellProps} rowIndex={cellProps.rowIndex + pinnedRowCount} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[pinnedRowCount, CellComponent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const PinnedRightColumnCell = useCallback(
|
for (let rowIndex = startIndex; rowIndex < endIndex; rowIndex++) {
|
||||||
(cellProps: CellComponentProps & TableItemProps) => {
|
if (enableHeader && rowIndex === 0) {
|
||||||
return (
|
// Header row
|
||||||
<CellComponent
|
map.set(rowIndex, 0);
|
||||||
{...cellProps}
|
} else if (groupIndexes.includes(rowIndex)) {
|
||||||
columnIndex={
|
// Group header row - don't increment adjustedIndex
|
||||||
cellProps.columnIndex + pinnedLeftColumnCount + totalColumnCount
|
map.set(rowIndex, 0);
|
||||||
|
} else {
|
||||||
|
// Data row
|
||||||
|
map.set(rowIndex, adjustedIndex);
|
||||||
|
adjustedIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}, [dataWithGroups, enableHeader, groups]);
|
||||||
|
|
||||||
|
const itemProps: TableItemProps = useMemo(
|
||||||
|
() => ({
|
||||||
|
activeRowId,
|
||||||
|
adjustedRowIndexMap,
|
||||||
|
calculatedColumnWidths,
|
||||||
|
cellPadding,
|
||||||
|
columns: parsedColumns,
|
||||||
|
controls,
|
||||||
|
data: dataWithGroups,
|
||||||
|
enableAlternateRowColors,
|
||||||
|
enableColumnReorder,
|
||||||
|
enableColumnResize,
|
||||||
|
enableDrag,
|
||||||
|
enableExpansion,
|
||||||
|
enableHeader,
|
||||||
|
enableHorizontalBorders,
|
||||||
|
enableRowHoverHighlight,
|
||||||
|
enableSelection,
|
||||||
|
enableVerticalBorders,
|
||||||
|
getRowHeight,
|
||||||
|
groups,
|
||||||
|
internalState,
|
||||||
|
itemType,
|
||||||
|
pinnedLeftColumnCount,
|
||||||
|
pinnedLeftColumnWidths,
|
||||||
|
pinnedRightColumnCount,
|
||||||
|
pinnedRightColumnWidths,
|
||||||
|
playerContext,
|
||||||
|
size,
|
||||||
|
startRowIndex,
|
||||||
|
tableId,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
activeRowId,
|
||||||
|
adjustedRowIndexMap,
|
||||||
|
calculatedColumnWidths,
|
||||||
|
cellPadding,
|
||||||
|
parsedColumns,
|
||||||
|
controls,
|
||||||
|
dataWithGroups,
|
||||||
|
enableAlternateRowColors,
|
||||||
|
enableColumnReorder,
|
||||||
|
enableColumnResize,
|
||||||
|
enableDrag,
|
||||||
|
enableExpansion,
|
||||||
|
enableHeader,
|
||||||
|
enableHorizontalBorders,
|
||||||
|
enableRowHoverHighlight,
|
||||||
|
enableSelection,
|
||||||
|
enableVerticalBorders,
|
||||||
|
getRowHeight,
|
||||||
|
groups,
|
||||||
|
internalState,
|
||||||
|
itemType,
|
||||||
|
pinnedLeftColumnCount,
|
||||||
|
pinnedLeftColumnWidths,
|
||||||
|
pinnedRightColumnCount,
|
||||||
|
pinnedRightColumnWidths,
|
||||||
|
playerContext,
|
||||||
|
size,
|
||||||
|
startRowIndex,
|
||||||
|
tableId,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedRowCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & TableItemProps) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedColumnCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & TableItemProps) => {
|
||||||
|
return <CellComponent {...cellProps} rowIndex={cellProps.rowIndex + pinnedRowCount} />;
|
||||||
|
},
|
||||||
|
[pinnedRowCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedRightColumnCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & TableItemProps) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount + totalColumnCount}
|
||||||
|
rowIndex={cellProps.rowIndex + pinnedRowCount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, pinnedRowCount, totalColumnCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const PinnedRightIntersectionCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps & TableItemProps) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount + totalColumnCount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, totalColumnCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const RowCell = useCallback(
|
||||||
|
(cellProps: CellComponentProps<TableItemProps>) => {
|
||||||
|
return (
|
||||||
|
<CellComponent
|
||||||
|
{...cellProps}
|
||||||
|
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount}
|
||||||
|
rowIndex={cellProps.rowIndex + pinnedRowCount}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[pinnedLeftColumnCount, pinnedRowCount, CellComponent],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOnCellsRendered = useCallback(
|
||||||
|
(items: {
|
||||||
|
columnStartIndex: number;
|
||||||
|
columnStopIndex: number;
|
||||||
|
rowStartIndex: number;
|
||||||
|
rowStopIndex: number;
|
||||||
|
}) => {
|
||||||
|
onRangeChanged?.({
|
||||||
|
startIndex: items.rowStartIndex,
|
||||||
|
stopIndex: items.rowStopIndex,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[onRangeChanged],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.itemTableContainer}>
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedColumnsGridContainer}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--header-height': `${headerHeight}px`,
|
||||||
|
minWidth: `${Array.from({ length: pinnedLeftColumnCount }, () => 0).reduce(
|
||||||
|
(a, _, i) => a + columnWidth(i),
|
||||||
|
0,
|
||||||
|
)}px`,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{!!(pinnedLeftColumnCount || pinnedRowCount) && (
|
||||||
|
<div
|
||||||
|
className={clsx(styles.itemTablePinnedIntersectionGridContainer, {
|
||||||
|
[styles.withHeader]: enableHeader,
|
||||||
|
})}
|
||||||
|
style={{
|
||||||
|
minHeight: `${Array.from({ length: pinnedRowCount }, () => 0).reduce(
|
||||||
|
(a, _, i) => a + getRowHeight(i, itemProps),
|
||||||
|
0,
|
||||||
|
)}px`,
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
cellComponent={CellComponent as any}
|
||||||
|
cellProps={itemProps}
|
||||||
|
className={styles.noScrollbar}
|
||||||
|
columnCount={pinnedLeftColumnCount}
|
||||||
|
columnWidth={columnWidth}
|
||||||
|
rowCount={pinnedRowCount}
|
||||||
|
rowHeight={getRowHeight}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{enableHeader && showTopShadow && (
|
||||||
|
<div className={styles.itemTableTopScrollShadow} />
|
||||||
|
)}
|
||||||
|
{!!pinnedLeftColumnCount && (
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedColumnsContainer}
|
||||||
|
ref={pinnedLeftColumnRef}
|
||||||
|
>
|
||||||
|
<Grid
|
||||||
|
cellComponent={PinnedColumnCell}
|
||||||
|
cellProps={itemProps}
|
||||||
|
className={clsx(styles.noScrollbar, styles.height100)}
|
||||||
|
columnCount={pinnedLeftColumnCount}
|
||||||
|
columnWidth={columnWidth}
|
||||||
|
rowCount={totalRowCount}
|
||||||
|
rowHeight={(index, cellProps) => {
|
||||||
|
return getRowHeight(index + pinnedRowCount, cellProps);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={styles.itemTablePinnedRowsContainer}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--header-height': `${headerHeight}px`,
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{!!pinnedRowCount && (
|
||||||
|
<div
|
||||||
|
className={clsx(styles.itemTablePinnedRowsGridContainer, {
|
||||||
|
[styles.withHeader]: enableHeader,
|
||||||
|
})}
|
||||||
|
ref={pinnedRowRef}
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
'--header-height': `${headerHeight}px`,
|
||||||
|
minHeight: `${Array.from(
|
||||||
|
{ length: pinnedRowCount },
|
||||||
|
() => 0,
|
||||||
|
).reduce((a, _, i) => a + getRowHeight(i, itemProps), 0)}px`,
|
||||||
|
overflow: 'hidden',
|
||||||
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
rowIndex={cellProps.rowIndex + pinnedRowCount}
|
>
|
||||||
|
<Grid
|
||||||
|
cellComponent={PinnedRowCell}
|
||||||
|
cellProps={itemProps}
|
||||||
|
className={styles.noScrollbar}
|
||||||
|
columnCount={totalColumnCount}
|
||||||
|
columnWidth={(index) => {
|
||||||
|
return columnWidth(index + pinnedLeftColumnCount);
|
||||||
|
}}
|
||||||
|
rowCount={Array.from({ length: pinnedRowCount }, () => 0).length}
|
||||||
|
rowHeight={getRowHeight}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{enableHeader && showTopShadow && (
|
||||||
|
<div className={styles.itemTableTopScrollShadow} />
|
||||||
|
)}
|
||||||
|
<div className={styles.itemTableGridContainer} ref={mergedRowRef}>
|
||||||
|
<Grid
|
||||||
|
cellComponent={RowCell}
|
||||||
|
cellProps={itemProps}
|
||||||
|
className={styles.height100}
|
||||||
|
columnCount={totalColumnCount}
|
||||||
|
columnWidth={(index) => {
|
||||||
|
return columnWidth(index + pinnedLeftColumnCount);
|
||||||
|
}}
|
||||||
|
onCellsRendered={handleOnCellsRendered}
|
||||||
|
rowCount={totalRowCount}
|
||||||
|
rowHeight={(index, cellProps) => {
|
||||||
|
return getRowHeight(index + pinnedRowCount, cellProps);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
{pinnedLeftColumnCount > 0 && showLeftShadow && (
|
||||||
},
|
<div className={styles.itemTableLeftScrollShadow} />
|
||||||
[pinnedLeftColumnCount, pinnedRowCount, totalColumnCount, CellComponent],
|
)}
|
||||||
);
|
{pinnedRightColumnCount > 0 && showRightShadow && (
|
||||||
|
<div className={styles.itemTableRightScrollShadow} />
|
||||||
const PinnedRightIntersectionCell = useCallback(
|
)}
|
||||||
(cellProps: CellComponentProps & TableItemProps) => {
|
</div>
|
||||||
return (
|
</div>
|
||||||
<CellComponent
|
{!!pinnedRightColumnCount && (
|
||||||
{...cellProps}
|
|
||||||
columnIndex={
|
|
||||||
cellProps.columnIndex + pinnedLeftColumnCount + totalColumnCount
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[pinnedLeftColumnCount, totalColumnCount, CellComponent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const RowCell = useCallback(
|
|
||||||
(cellProps: CellComponentProps<TableItemProps>) => {
|
|
||||||
return (
|
|
||||||
<CellComponent
|
|
||||||
{...cellProps}
|
|
||||||
columnIndex={cellProps.columnIndex + pinnedLeftColumnCount}
|
|
||||||
rowIndex={cellProps.rowIndex + pinnedRowCount}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[pinnedLeftColumnCount, pinnedRowCount, CellComponent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnCellsRendered = useCallback(
|
|
||||||
(items: {
|
|
||||||
columnStartIndex: number;
|
|
||||||
columnStopIndex: number;
|
|
||||||
rowStartIndex: number;
|
|
||||||
rowStopIndex: number;
|
|
||||||
}) => {
|
|
||||||
onRangeChanged?.({
|
|
||||||
startIndex: items.rowStartIndex,
|
|
||||||
stopIndex: items.rowStopIndex,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[onRangeChanged],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.itemTableContainer}>
|
|
||||||
<div
|
<div
|
||||||
className={styles.itemTablePinnedColumnsGridContainer}
|
className={styles.itemTablePinnedColumnsGridContainer}
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
'--header-height': `${headerHeight}px`,
|
'--header-height': `${headerHeight}px`,
|
||||||
minWidth: `${Array.from(
|
minWidth: `${Array.from(
|
||||||
{ length: pinnedLeftColumnCount },
|
{ length: pinnedRightColumnCount },
|
||||||
() => 0,
|
() => 0,
|
||||||
).reduce((a, _, i) => a + columnWidth(i), 0)}px`,
|
).reduce(
|
||||||
|
(a, _, i) =>
|
||||||
|
a + columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
|
||||||
|
0,
|
||||||
|
)}px`,
|
||||||
} as React.CSSProperties
|
} as React.CSSProperties
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{!!(pinnedLeftColumnCount || pinnedRowCount) && (
|
{!!(pinnedRightColumnCount || pinnedRowCount) && (
|
||||||
<div
|
<div
|
||||||
className={clsx(styles.itemTablePinnedIntersectionGridContainer, {
|
className={clsx(styles.itemTablePinnedIntersectionGridContainer, {
|
||||||
[styles.withHeader]: enableHeader,
|
[styles.withHeader]: enableHeader,
|
||||||
@@ -449,11 +569,15 @@ const VirtualizedTableGrid = React.memo(
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Grid
|
<Grid
|
||||||
cellComponent={CellComponent as any}
|
cellComponent={PinnedRightIntersectionCell}
|
||||||
cellProps={itemProps}
|
cellProps={itemProps}
|
||||||
className={styles.noScrollbar}
|
className={styles.noScrollbar}
|
||||||
columnCount={pinnedLeftColumnCount}
|
columnCount={pinnedRightColumnCount}
|
||||||
columnWidth={columnWidth}
|
columnWidth={(index) => {
|
||||||
|
return columnWidth(
|
||||||
|
index + pinnedLeftColumnCount + totalColumnCount,
|
||||||
|
);
|
||||||
|
}}
|
||||||
rowCount={pinnedRowCount}
|
rowCount={pinnedRowCount}
|
||||||
rowHeight={getRowHeight}
|
rowHeight={getRowHeight}
|
||||||
/>
|
/>
|
||||||
@@ -462,192 +586,31 @@ const VirtualizedTableGrid = React.memo(
|
|||||||
{enableHeader && showTopShadow && (
|
{enableHeader && showTopShadow && (
|
||||||
<div className={styles.itemTableTopScrollShadow} />
|
<div className={styles.itemTableTopScrollShadow} />
|
||||||
)}
|
)}
|
||||||
{!!pinnedLeftColumnCount && (
|
<div
|
||||||
<div
|
className={styles.itemTablePinnedRightColumnsContainer}
|
||||||
className={styles.itemTablePinnedColumnsContainer}
|
ref={pinnedRightColumnRef}
|
||||||
ref={pinnedLeftColumnRef}
|
>
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
cellComponent={PinnedColumnCell}
|
|
||||||
cellProps={itemProps}
|
|
||||||
className={clsx(styles.noScrollbar, styles.height100)}
|
|
||||||
columnCount={pinnedLeftColumnCount}
|
|
||||||
columnWidth={columnWidth}
|
|
||||||
rowCount={totalRowCount}
|
|
||||||
rowHeight={(index, cellProps) => {
|
|
||||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={styles.itemTablePinnedRowsContainer}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
'--header-height': `${headerHeight}px`,
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{!!pinnedRowCount && (
|
|
||||||
<div
|
|
||||||
className={clsx(styles.itemTablePinnedRowsGridContainer, {
|
|
||||||
[styles.withHeader]: enableHeader,
|
|
||||||
})}
|
|
||||||
ref={pinnedRowRef}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
'--header-height': `${headerHeight}px`,
|
|
||||||
minHeight: `${Array.from(
|
|
||||||
{ length: pinnedRowCount },
|
|
||||||
() => 0,
|
|
||||||
).reduce((a, _, i) => a + getRowHeight(i, itemProps), 0)}px`,
|
|
||||||
overflow: 'hidden',
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
cellComponent={PinnedRowCell}
|
|
||||||
cellProps={itemProps}
|
|
||||||
className={styles.noScrollbar}
|
|
||||||
columnCount={totalColumnCount}
|
|
||||||
columnWidth={(index) => {
|
|
||||||
return columnWidth(index + pinnedLeftColumnCount);
|
|
||||||
}}
|
|
||||||
rowCount={Array.from({ length: pinnedRowCount }, () => 0).length}
|
|
||||||
rowHeight={getRowHeight}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{enableHeader && showTopShadow && (
|
|
||||||
<div className={styles.itemTableTopScrollShadow} />
|
|
||||||
)}
|
|
||||||
<div className={styles.itemTableGridContainer} ref={mergedRowRef}>
|
|
||||||
<Grid
|
<Grid
|
||||||
cellComponent={RowCell}
|
cellComponent={PinnedRightColumnCell}
|
||||||
cellProps={itemProps}
|
cellProps={itemProps}
|
||||||
className={styles.height100}
|
className={clsx(styles.noScrollbar, styles.height100)}
|
||||||
columnCount={totalColumnCount}
|
columnCount={pinnedRightColumnCount}
|
||||||
columnWidth={(index) => {
|
columnWidth={(index) => {
|
||||||
return columnWidth(index + pinnedLeftColumnCount);
|
return columnWidth(
|
||||||
|
index + pinnedLeftColumnCount + totalColumnCount,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
onCellsRendered={handleOnCellsRendered}
|
|
||||||
rowCount={totalRowCount}
|
rowCount={totalRowCount}
|
||||||
rowHeight={(index, cellProps) => {
|
rowHeight={(index, cellProps) => {
|
||||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
return getRowHeight(index + pinnedRowCount, cellProps);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{pinnedLeftColumnCount > 0 && showLeftShadow && (
|
|
||||||
<div className={styles.itemTableLeftScrollShadow} />
|
|
||||||
)}
|
|
||||||
{pinnedRightColumnCount > 0 && showRightShadow && (
|
|
||||||
<div className={styles.itemTableRightScrollShadow} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!!pinnedRightColumnCount && (
|
)}
|
||||||
<div
|
</div>
|
||||||
className={styles.itemTablePinnedColumnsGridContainer}
|
);
|
||||||
style={
|
};
|
||||||
{
|
|
||||||
'--header-height': `${headerHeight}px`,
|
|
||||||
minWidth: `${Array.from(
|
|
||||||
{ length: pinnedRightColumnCount },
|
|
||||||
() => 0,
|
|
||||||
).reduce(
|
|
||||||
(a, _, i) =>
|
|
||||||
a +
|
|
||||||
columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
|
|
||||||
0,
|
|
||||||
)}px`,
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{!!(pinnedRightColumnCount || pinnedRowCount) && (
|
|
||||||
<div
|
|
||||||
className={clsx(styles.itemTablePinnedIntersectionGridContainer, {
|
|
||||||
[styles.withHeader]: enableHeader,
|
|
||||||
})}
|
|
||||||
style={{
|
|
||||||
minHeight: `${Array.from(
|
|
||||||
{ length: pinnedRowCount },
|
|
||||||
() => 0,
|
|
||||||
).reduce((a, _, i) => a + getRowHeight(i, itemProps), 0)}px`,
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
cellComponent={PinnedRightIntersectionCell}
|
|
||||||
cellProps={itemProps}
|
|
||||||
className={styles.noScrollbar}
|
|
||||||
columnCount={pinnedRightColumnCount}
|
|
||||||
columnWidth={(index) => {
|
|
||||||
return columnWidth(
|
|
||||||
index + pinnedLeftColumnCount + totalColumnCount,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
rowCount={pinnedRowCount}
|
|
||||||
rowHeight={getRowHeight}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{enableHeader && showTopShadow && (
|
|
||||||
<div className={styles.itemTableTopScrollShadow} />
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={styles.itemTablePinnedRightColumnsContainer}
|
|
||||||
ref={pinnedRightColumnRef}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
cellComponent={PinnedRightColumnCell}
|
|
||||||
cellProps={itemProps}
|
|
||||||
className={clsx(styles.noScrollbar, styles.height100)}
|
|
||||||
columnCount={pinnedRightColumnCount}
|
|
||||||
columnWidth={(index) => {
|
|
||||||
return columnWidth(
|
|
||||||
index + pinnedLeftColumnCount + totalColumnCount,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
rowCount={totalRowCount}
|
|
||||||
rowHeight={(index, cellProps) => {
|
|
||||||
return getRowHeight(index + pinnedRowCount, cellProps);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(prevProps, nextProps) => {
|
|
||||||
const prevColumnIds = prevProps.parsedColumns.map((col) => col.id).join(',');
|
|
||||||
const nextColumnIds = nextProps.parsedColumns.map((col) => col.id).join(',');
|
|
||||||
|
|
||||||
const columnWidthsEqual = prevProps.calculatedColumnWidths.every(
|
|
||||||
(width, index) => width === nextProps.calculatedColumnWidths[index],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (prevColumnIds !== nextColumnIds) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
columnWidthsEqual &&
|
|
||||||
prevProps.activeRowId === nextProps.activeRowId &&
|
|
||||||
prevProps.data === nextProps.data &&
|
|
||||||
prevProps.size === nextProps.size &&
|
|
||||||
prevProps.startRowIndex === nextProps.startRowIndex &&
|
|
||||||
prevProps.enableVerticalBorders === nextProps.enableVerticalBorders &&
|
|
||||||
prevProps.enableHorizontalBorders === nextProps.enableHorizontalBorders &&
|
|
||||||
prevProps.enableRowHoverHighlight === nextProps.enableRowHoverHighlight &&
|
|
||||||
prevProps.enableAlternateRowColors === nextProps.enableAlternateRowColors &&
|
|
||||||
prevProps.pinnedLeftColumnCount === nextProps.pinnedLeftColumnCount &&
|
|
||||||
prevProps.pinnedRightColumnCount === nextProps.pinnedRightColumnCount &&
|
|
||||||
prevProps.totalColumnCount === nextProps.totalColumnCount &&
|
|
||||||
prevProps.totalRowCount === nextProps.totalRowCount
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
VirtualizedTableGrid.displayName = 'VirtualizedTableGrid';
|
VirtualizedTableGrid.displayName = 'VirtualizedTableGrid';
|
||||||
|
|
||||||
|
|||||||
@@ -774,7 +774,7 @@ const TableColumnItem = memo(
|
|||||||
postProcess: 'sentenceCase',
|
postProcess: 'sentenceCase',
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
variant={item.align === 'end' ? 'filled' : 'subtle'}
|
variant={item.align === 'end' ? 'outline' : 'subtle'}
|
||||||
/>
|
/>
|
||||||
</ActionIconGroup>
|
</ActionIconGroup>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
|
|||||||
Reference in New Issue
Block a user