handle row groups with pinned table columns

This commit is contained in:
jeffvli
2025-11-17 21:46:51 -08:00
parent 80831e6bde
commit df1a46a466
2 changed files with 44 additions and 16 deletions
@@ -77,10 +77,8 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
const shouldEnableDrag = !!props.enableDrag && isDataRow && !!item; const shouldEnableDrag = !!props.enableDrag && isDataRow && !!item;
// Check if this row should render a group header (must be before conditional returns) // Check if this row should render a group header (must be before conditional returns)
// Group headers are rendered in the main grid at columnIndex 0 (first unpinned column) // Group headers need to be rendered consistently across all grids (pinned left, main, pinned right)
// We detect this by checking if columnIndex equals pinnedLeftColumnCount (first column of main grid) // to maintain proper styling and row heights
// or if columnIndex is 0 and there are no pinned columns
// Groups are defined by itemCount, so we calculate which group this row belongs to
let groupHeader: 'GROUP_HEADER' | null | ReactElement = null; let groupHeader: 'GROUP_HEADER' | null | ReactElement = null;
if (props.groups && isDataRow && props.groups.length > 0) { if (props.groups && isDataRow && props.groups.length > 0) {
// Calculate which group this row index belongs to // Calculate which group this row index belongs to
@@ -94,12 +92,18 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex; const groupHeaderIndex = headerOffset + cumulativeDataIndex + groupIndex;
if (props.rowIndex === groupHeaderIndex) { if (props.rowIndex === groupHeaderIndex) {
// Determine where to render the group header content:
// - If pinned left columns exist, render in the first pinned left column
// - Otherwise, render in the first column of the main grid
const hasPinnedLeftColumns = (props.pinnedLeftColumnCount || 0) > 0;
const isFirstPinnedLeftColumn = props.columnIndex === 0 && hasPinnedLeftColumns;
const isMainGridFirstColumn = const isMainGridFirstColumn =
props.columnIndex === (props.pinnedLeftColumnCount || 0) || !hasPinnedLeftColumns &&
(props.columnIndex === 0 && (props.pinnedLeftColumnCount || 0) === 0); (props.columnIndex === (props.pinnedLeftColumnCount || 0) ||
(props.columnIndex === 0 && (props.pinnedLeftColumnCount || 0) === 0));
// Only render group header in the first column of the main grid // Render group header content in the first pinned left column (if exists) or first main grid column
if (isMainGridFirstColumn) { if (isFirstPinnedLeftColumn || isMainGridFirstColumn) {
groupHeader = group.render({ groupHeader = group.render({
data: originalData, data: originalData,
groupIndex, groupIndex,
@@ -108,7 +112,7 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
startDataIndex: cumulativeDataIndex, startDataIndex: cumulativeDataIndex,
}); });
} else { } else {
// For other columns in this row, return marker to skip rendering // For other columns, mark as group header row for styled rendering
groupHeader = 'GROUP_HEADER'; groupHeader = 'GROUP_HEADER';
} }
break; break;
@@ -309,11 +313,14 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
// Render group header if this row should have one // Render group header if this row should have one
if (groupHeader) { if (groupHeader) {
if (groupHeader === 'GROUP_HEADER') { if (groupHeader === 'GROUP_HEADER') {
// For non-first columns, render empty cell (group header spans all columns) // For non-first columns (pinned left, other main columns, pinned right),
return null; // render a styled cell that matches the group header styling
// This ensures consistent row heights and styling across all grids
return <div style={{ ...props.style }} />;
} }
// For first column of main grid, render the group header spanning full table width // Render the group header spanning full table width
// Calculate widths to span across all grids using calculated column widths // If rendering in pinned left column, extend right to cover all columns
// If rendering in main grid, extend left to cover pinned columns
const pinnedLeftWidth = const pinnedLeftWidth =
props.pinnedLeftColumnWidths?.reduce((sum, width) => sum + width, 0) || 0; props.pinnedLeftColumnWidths?.reduce((sum, width) => sum + width, 0) || 0;
const pinnedRightWidth = const pinnedRightWidth =
@@ -332,12 +339,31 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
.reduce((sum, col) => sum + col.width, 0) + .reduce((sum, col) => sum + col.width, 0) +
pinnedRightWidth; pinnedRightWidth;
// Use negative margins to extend beyond cell boundaries and span full width // Determine if we're rendering in the first pinned left column
// Apply props.style for virtualization positioning (top, left, position, etc.) const isFirstPinnedLeftColumn =
props.columnIndex === 0 && (props.pinnedLeftColumnCount || 0) > 0;
if (isFirstPinnedLeftColumn) {
return ( return (
<div <div
style={{ style={{
...props.style, // Apply virtualization styles (position, top, left, width, height) ...props.style,
backgroundColor: 'var(--theme-bg-secondary)',
borderBottom: '1px solid var(--theme-border-color)',
marginLeft: 0,
marginRight: 0,
}}
>
{groupHeader}
</div>
);
}
// For main grid, use negative margin to extend left
return (
<div
style={{
...props.style,
backgroundColor: 'var(--theme-bg-secondary)', backgroundColor: 'var(--theme-bg-secondary)',
borderBottom: '1px solid var(--theme-border-color)', borderBottom: '1px solid var(--theme-border-color)',
marginLeft: pinnedLeftWidth > 0 ? `-${pinnedLeftWidth}px` : 0, marginLeft: pinnedLeftWidth > 0 ? `-${pinnedLeftWidth}px` : 0,
@@ -158,6 +158,8 @@
min-width: 0; min-width: 0;
height: 100%; height: 100%;
min-height: 0; min-height: 0;
overflow-x: visible; /* Allow group headers to extend beyond container */
overflow-y: auto; /* Maintain vertical scrolling */
} }
.item-table-pinned-right-columns-container { .item-table-pinned-right-columns-container {