support both left and right column pinning

This commit is contained in:
jeffvli
2025-10-03 18:06:52 -07:00
parent 92af51cd4d
commit 070aca44bf
2 changed files with 291 additions and 91 deletions
@@ -23,7 +23,7 @@
min-height: 0; min-height: 0;
} }
.item-table-sticky-rows-container { .item-table-pinned-rows-container {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
flex-direction: column; flex-direction: column;
@@ -31,26 +31,33 @@
min-height: 0; min-height: 0;
} }
.item-table-sticky-rows-grid-container { .item-table-pinned-rows-grid-container {
position: relative; position: relative;
flex: 0 1 auto; flex: 0 1 auto;
min-width: 0; min-width: 0;
} }
.item-table-sticky-columns-grid-container { .item-table-pinned-columns-grid-container {
display: flex; display: flex;
flex: 0 1 auto; flex: 0 1 auto;
flex-direction: column; flex-direction: column;
min-height: 0; min-height: 0;
} }
.item-table-sticky-intersection-grid-container { .item-table-pinned-intersection-grid-container {
position: relative; position: relative;
flex: 0 1 auto; flex: 0 1 auto;
min-width: 0; min-width: 0;
} }
.item-table-sticky-columns-container { .item-table-pinned-columns-container {
flex: 1 1 auto;
min-width: 0;
height: 100%;
min-height: 0;
}
.item-table-pinned-right-columns-container {
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 0;
height: 100%; height: 100%;
@@ -65,7 +72,7 @@
height: 100%; height: 100%;
} }
.item-table-sticky-header-shadow { .item-table-pinned-header-shadow {
position: absolute; position: absolute;
top: 100%; top: 100%;
right: 0; right: 0;
@@ -97,6 +104,22 @@
); );
} }
.item-table-right-scroll-shadow {
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 1;
width: 8px;
pointer-events: none;
background: linear-gradient(
to left,
rgb(0 0 0 / 50%) 0%,
rgb(0 0 0 / 5%) 50%,
transparent 100%
);
}
.list-expanded-container { .list-expanded-container {
height: 500px; height: 500px;
} }
@@ -64,10 +64,11 @@ interface ItemTableListProps {
onScroll?: (event: UIEvent<HTMLDivElement>) => void; onScroll?: (event: UIEvent<HTMLDivElement>) => void;
onScrollEnd?: () => void; onScrollEnd?: () => void;
onStartReached?: (index: number) => void; onStartReached?: (index: number) => void;
pinnedLeftColumnCount: number;
pinnedRightColumnCount?: number;
ref?: Ref<GridImperativeAPI>; ref?: Ref<GridImperativeAPI>;
rowHeight: ((index: number, cellProps: CellProps) => number) | number; rowHeight: ((index: number, cellProps: CellProps) => number) | number;
size?: 'compact' | 'default'; size?: 'compact' | 'default';
stickyColumnCount: number;
totalItemCount: number; totalItemCount: number;
} }
@@ -104,21 +105,25 @@ export const ItemTableList = ({
onScroll, onScroll,
onScrollEnd, onScrollEnd,
onStartReached, onStartReached,
pinnedLeftColumnCount,
pinnedRightColumnCount = 0,
ref, ref,
rowHeight, rowHeight,
size = 'default', size = 'default',
stickyColumnCount,
totalItemCount, totalItemCount,
}: ItemTableListProps) => { }: ItemTableListProps) => {
const stickyRowCount = enableHeader ? 1 : 0; const pinnedRowCount = enableHeader ? 1 : 0;
const totalRowCount = totalItemCount - (stickyRowCount ?? 0); const totalRowCount = totalItemCount - (pinnedRowCount ?? 0);
const totalColumnCount = columnCount - (stickyColumnCount ?? 0); const totalColumnCount =
const stickyRowRef = useRef<HTMLDivElement>(null); columnCount - (pinnedLeftColumnCount ?? 0) - (pinnedRightColumnCount ?? 0);
const pinnedRowRef = useRef<HTMLDivElement>(null);
const rowRef = useRef<HTMLDivElement>(null); const rowRef = useRef<HTMLDivElement>(null);
const stickyColumnRef = useRef<HTMLDivElement>(null); const pinnedLeftColumnRef = useRef<HTMLDivElement>(null);
const pinnedRightColumnRef = useRef<HTMLDivElement>(null);
const scrollContainerRef = useRef<HTMLDivElement | null>(null); const scrollContainerRef = useRef<HTMLDivElement | null>(null);
const mergedRowRef = useMergedRef(rowRef, scrollContainerRef); const mergedRowRef = useMergedRef(rowRef, scrollContainerRef);
const [showLeftShadow, setShowLeftShadow] = useState(false); const [showLeftShadow, setShowLeftShadow] = useState(false);
const [showRightShadow, setShowRightShadow] = useState(false);
const [initialize] = useOverlayScrollbars({ const [initialize] = useOverlayScrollbars({
defer: true, defer: true,
@@ -156,26 +161,37 @@ export const ItemTableList = ({
}, [initialize]); }, [initialize]);
useEffect(() => { useEffect(() => {
const header = stickyRowRef.current?.childNodes[0] as HTMLDivElement; const header = pinnedRowRef.current?.childNodes[0] as HTMLDivElement;
const row = rowRef.current?.childNodes[0] as HTMLDivElement; const row = rowRef.current?.childNodes[0] as HTMLDivElement;
const sticky = stickyColumnRef.current?.childNodes[0] as HTMLDivElement; const pinnedLeft = pinnedLeftColumnRef.current?.childNodes[0] as HTMLDivElement;
const pinnedRight = pinnedRightColumnRef.current?.childNodes[0] as HTMLDivElement;
// At minimum, we need the main row element // At minimum, we need the main row element
if (row) { if (row) {
// Ensure all containers have the same height // Ensure all containers have the same height
const syncHeights = () => { const syncHeights = () => {
if (sticky) { const rowHeight = row.scrollHeight;
const rowHeight = row.scrollHeight; let targetHeight = rowHeight;
const stickyHeight = sticky.scrollHeight;
// Set consistent heights - use the larger of the two if (pinnedLeft) {
const targetHeight = Math.max(rowHeight, stickyHeight); const pinnedLeftHeight = pinnedLeft.scrollHeight;
if (sticky.style.height !== `${targetHeight}px`) { targetHeight = Math.max(targetHeight, pinnedLeftHeight);
sticky.style.height = `${targetHeight}px`; }
}
if (row.style.height !== `${targetHeight}px`) { if (pinnedRight) {
row.style.height = `${targetHeight}px`; const pinnedRightHeight = pinnedRight.scrollHeight;
} targetHeight = Math.max(targetHeight, pinnedRightHeight);
}
// Set consistent heights for all elements
if (pinnedLeft && pinnedLeft.style.height !== `${targetHeight}px`) {
pinnedLeft.style.height = `${targetHeight}px`;
}
if (pinnedRight && pinnedRight.style.height !== `${targetHeight}px`) {
pinnedRight.style.height = `${targetHeight}px`;
}
if (row.style.height !== `${targetHeight}px`) {
row.style.height = `${targetHeight}px`;
} }
}; };
@@ -231,7 +247,12 @@ export const ItemTableList = ({
const scrollLeft = (e.currentTarget as HTMLDivElement).scrollLeft; const scrollLeft = (e.currentTarget as HTMLDivElement).scrollLeft;
// Prevent recursive scroll events // Prevent recursive scroll events
const isScrolling = { header: false, row: false, sticky: false }; const isScrolling = {
header: false,
pinnedLeft: false,
pinnedRight: false,
row: false,
};
// Sync horizontal scroll between header and main content (only if header exists) // Sync horizontal scroll between header and main content (only if header exists)
if (header && e.currentTarget === header && !isScrolling.row) { if (header && e.currentTarget === header && !isScrolling.row) {
@@ -245,8 +266,13 @@ export const ItemTableList = ({
}, 0); }, 0);
} }
// Sync from main content to header and sticky column // Sync from main content to header and sticky columns
if (e.currentTarget === row && !isScrolling.header && !isScrolling.sticky) { if (
e.currentTarget === row &&
!isScrolling.header &&
!isScrolling.pinnedLeft &&
!isScrolling.pinnedRight
) {
if (header) { if (header) {
isScrolling.header = true; isScrolling.header = true;
header.scrollTo({ header.scrollTo({
@@ -254,21 +280,41 @@ export const ItemTableList = ({
left: scrollLeft, left: scrollLeft,
}); });
} }
if (sticky) { if (pinnedLeft) {
isScrolling.sticky = true; isScrolling.pinnedLeft = true;
sticky.scrollTo({ pinnedLeft.scrollTo({
behavior: 'instant',
top: scrollTop,
});
}
if (pinnedRight) {
isScrolling.pinnedRight = true;
pinnedRight.scrollTo({
behavior: 'instant', behavior: 'instant',
top: scrollTop, top: scrollTop,
}); });
} }
setTimeout(() => { setTimeout(() => {
isScrolling.header = false; isScrolling.header = false;
isScrolling.sticky = false; isScrolling.pinnedLeft = false;
isScrolling.pinnedRight = false;
}, 0); }, 0);
} }
// Sync vertical scroll between sticky column and main content (only if sticky exists) // Sync vertical scroll between left pinned column and main content (only if pinnedLeft exists)
if (sticky && e.currentTarget === sticky && !isScrolling.row) { if (pinnedLeft && e.currentTarget === pinnedLeft && !isScrolling.row) {
isScrolling.row = true;
row.scrollTo({
behavior: 'instant',
top: scrollTop,
});
setTimeout(() => {
isScrolling.row = false;
}, 0);
}
// Sync vertical scroll between right pinned column and main content (only if pinnedRight exists)
if (pinnedRight && e.currentTarget === pinnedRight && !isScrolling.row) {
isScrolling.row = true; isScrolling.row = true;
row.scrollTo({ row.scrollTo({
behavior: 'instant', behavior: 'instant',
@@ -289,10 +335,15 @@ export const ItemTableList = ({
row.addEventListener('pointermove', setActiveElement); row.addEventListener('pointermove', setActiveElement);
row.addEventListener('wheel', setActiveElementFromWheel); row.addEventListener('wheel', setActiveElementFromWheel);
row.addEventListener('scroll', syncScroll); row.addEventListener('scroll', syncScroll);
if (sticky) { if (pinnedLeft) {
sticky.addEventListener('pointermove', setActiveElement); pinnedLeft.addEventListener('pointermove', setActiveElement);
sticky.addEventListener('wheel', setActiveElementFromWheel); pinnedLeft.addEventListener('wheel', setActiveElementFromWheel);
sticky.addEventListener('scroll', syncScroll); pinnedLeft.addEventListener('scroll', syncScroll);
}
if (pinnedRight) {
pinnedRight.addEventListener('pointermove', setActiveElement);
pinnedRight.addEventListener('wheel', setActiveElementFromWheel);
pinnedRight.addEventListener('scroll', syncScroll);
} }
// Add resize observer to maintain height sync // Add resize observer to maintain height sync
@@ -301,8 +352,11 @@ export const ItemTableList = ({
}); });
resizeObserver.observe(row); resizeObserver.observe(row);
if (sticky) { if (pinnedLeft) {
resizeObserver.observe(sticky); resizeObserver.observe(pinnedLeft);
}
if (pinnedRight) {
resizeObserver.observe(pinnedRight);
} }
return () => { return () => {
@@ -319,10 +373,15 @@ export const ItemTableList = ({
row.removeEventListener('pointermove', setActiveElement); row.removeEventListener('pointermove', setActiveElement);
row.removeEventListener('wheel', setActiveElementFromWheel); row.removeEventListener('wheel', setActiveElementFromWheel);
row.removeEventListener('scroll', syncScroll); row.removeEventListener('scroll', syncScroll);
if (sticky) { if (pinnedLeft) {
sticky.removeEventListener('pointermove', setActiveElement); pinnedLeft.removeEventListener('pointermove', setActiveElement);
sticky.removeEventListener('wheel', setActiveElementFromWheel); pinnedLeft.removeEventListener('wheel', setActiveElementFromWheel);
sticky.removeEventListener('scroll', syncScroll); pinnedLeft.removeEventListener('scroll', syncScroll);
}
if (pinnedRight) {
pinnedRight.removeEventListener('pointermove', setActiveElement);
pinnedRight.removeEventListener('wheel', setActiveElementFromWheel);
pinnedRight.removeEventListener('scroll', syncScroll);
} }
resizeObserver.disconnect(); resizeObserver.disconnect();
}; };
@@ -331,18 +390,22 @@ export const ItemTableList = ({
return undefined; return undefined;
}, []); }, []);
// Handle left shadow visibility based on horizontal scroll // Handle left and right shadow visibility based on horizontal scroll
useEffect(() => { useEffect(() => {
const row = rowRef.current?.childNodes[0] as HTMLDivElement; const row = rowRef.current?.childNodes[0] as HTMLDivElement;
if (!row || !stickyColumnCount) { if (!row) {
setShowLeftShadow(false); setShowLeftShadow(false);
setShowRightShadow(false);
return; return;
} }
const checkScrollPosition = () => { const checkScrollPosition = () => {
const scrollLeft = row.scrollLeft; const scrollLeft = row.scrollLeft;
setShowLeftShadow(scrollLeft > 0); const maxScrollLeft = row.scrollWidth - row.clientWidth;
setShowLeftShadow(pinnedLeftColumnCount > 0 && scrollLeft > 0);
setShowRightShadow(pinnedRightColumnCount > 0 && scrollLeft < maxScrollLeft);
}; };
checkScrollPosition(); checkScrollPosition();
@@ -352,7 +415,7 @@ export const ItemTableList = ({
return () => { return () => {
row.removeEventListener('scroll', checkScrollPosition); row.removeEventListener('scroll', checkScrollPosition);
}; };
}, [stickyColumnCount]); }, [pinnedLeftColumnCount, pinnedRightColumnCount]);
const getRowHeight = useCallback( const getRowHeight = useCallback(
(index: number, cellProps: CellProps) => { (index: number, cellProps: CellProps) => {
@@ -360,13 +423,13 @@ export const ItemTableList = ({
typeof rowHeight === 'number' ? rowHeight : rowHeight(index, cellProps); typeof rowHeight === 'number' ? rowHeight : rowHeight(index, cellProps);
// If enableHeader is true and this is the first sticky row, use fixed header height // If enableHeader is true and this is the first sticky row, use fixed header height
if (enableHeader && index === 0 && stickyRowCount > 0) { if (enableHeader && index === 0 && pinnedRowCount > 0) {
return headerHeight; return headerHeight;
} }
return baseHeight; return baseHeight;
}, },
[enableHeader, headerHeight, rowHeight, stickyRowCount], [enableHeader, headerHeight, rowHeight, pinnedRowCount],
); );
const internalState = useItemListState(); const internalState = useItemListState();
@@ -402,41 +465,70 @@ export const ItemTableList = ({
? ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) => { ? ({ columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex }) => {
return onCellsRendered!( return onCellsRendered!(
{ {
columnStartIndex: columnStartIndex + (stickyColumnCount ?? 0), columnStartIndex: columnStartIndex + (pinnedLeftColumnCount ?? 0),
columnStopIndex: columnStopIndex + (stickyColumnCount ?? 0), columnStopIndex: columnStopIndex + (pinnedLeftColumnCount ?? 0),
rowStartIndex: rowStartIndex + (stickyRowCount ?? 0), rowStartIndex: rowStartIndex + (pinnedRowCount ?? 0),
rowStopIndex: rowStopIndex + (stickyRowCount ?? 0), rowStopIndex: rowStopIndex + (pinnedRowCount ?? 0),
}, },
cells, cells,
); );
} }
: undefined; : undefined;
}, },
[onCellsRendered, onRangeChanged, stickyColumnCount, stickyRowCount], [onCellsRendered, onRangeChanged, pinnedLeftColumnCount, pinnedRowCount],
); );
const StickyRowCell = useCallback( const PinnedRowCell = useCallback(
(cellProps: CellComponentProps & CellProps) => { (cellProps: CellComponentProps & CellProps) => {
return ( return (
<CellComponent <CellComponent
{...cellProps} {...cellProps}
columnIndex={cellProps.columnIndex + (stickyColumnCount ?? 0)} columnIndex={cellProps.columnIndex + (pinnedLeftColumnCount ?? 0)}
/> />
); );
}, },
[stickyColumnCount, CellComponent], [pinnedLeftColumnCount, CellComponent],
); );
const StickyColumnCell = useCallback( const PinnedColumnCell = useCallback(
(cellProps: CellComponentProps & CellProps) => { (cellProps: CellComponentProps & CellProps) => {
return ( return (
<CellComponent <CellComponent
{...cellProps} {...cellProps}
rowIndex={cellProps.rowIndex + (stickyRowCount ?? 0)} rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
/> />
); );
}, },
[stickyRowCount, CellComponent], [pinnedRowCount, CellComponent],
);
const PinnedRightColumnCell = useCallback(
(cellProps: CellComponentProps & CellProps) => {
return (
<CellComponent
{...cellProps}
columnIndex={
cellProps.columnIndex + (pinnedLeftColumnCount ?? 0) + totalColumnCount
}
rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
/>
);
},
[pinnedLeftColumnCount, pinnedRowCount, totalColumnCount, CellComponent],
);
const PinnedRightIntersectionCell = useCallback(
(cellProps: CellComponentProps & CellProps) => {
return (
<CellComponent
{...cellProps}
columnIndex={
cellProps.columnIndex + (pinnedLeftColumnCount ?? 0) + totalColumnCount
}
/>
);
},
[pinnedLeftColumnCount, totalColumnCount, CellComponent],
); );
const RowCell = useCallback( const RowCell = useCallback(
@@ -444,15 +536,15 @@ export const ItemTableList = ({
return ( return (
<CellComponent <CellComponent
{...cellProps} {...cellProps}
columnIndex={cellProps.columnIndex + (stickyColumnCount ?? 0)} columnIndex={cellProps.columnIndex + (pinnedLeftColumnCount ?? 0)}
// onClick={(e) => { // onClick={(e) => {
// onItemClick?.(cellProps.data[cellProps.rowIndex], cellProps.rowIndex, e); // onItemClick?.(cellProps.data[cellProps.rowIndex], cellProps.rowIndex, e);
// }} // }}
rowIndex={cellProps.rowIndex + (stickyRowCount ?? 0)} rowIndex={cellProps.rowIndex + (pinnedRowCount ?? 0)}
/> />
); );
}, },
[stickyColumnCount, stickyRowCount, CellComponent], [pinnedLeftColumnCount, pinnedRowCount, CellComponent],
); );
const cellProps = { const cellProps = {
@@ -478,9 +570,12 @@ export const ItemTableList = ({
> >
<div className={styles.itemTableContainer}> <div className={styles.itemTableContainer}>
<div <div
className={styles.itemTableStickyColumnsGridContainer} className={styles.itemTablePinnedColumnsGridContainer}
style={{ style={{
minWidth: `${Array.from({ length: stickyColumnCount ?? 0 }, () => 0).reduce( minWidth: `${Array.from(
{ length: pinnedLeftColumnCount ?? 0 },
() => 0,
).reduce(
(a, _, i) => (a, _, i) =>
a + a +
(typeof columnWidth === 'number' (typeof columnWidth === 'number'
@@ -490,12 +585,12 @@ export const ItemTableList = ({
)}px`, )}px`,
}} }}
> >
{!!(stickyColumnCount || stickyRowCount) && ( {!!(pinnedLeftColumnCount || pinnedRowCount) && (
<div <div
className={styles.itemTableStickyIntersectionGridContainer} className={styles.itemTablePinnedIntersectionGridContainer}
style={{ style={{
minHeight: `${Array.from( minHeight: `${Array.from(
{ length: stickyRowCount ?? 0 }, { length: pinnedRowCount ?? 0 },
() => 0, () => 0,
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`, ).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
}} }}
@@ -504,64 +599,67 @@ export const ItemTableList = ({
cellComponent={CellComponent as any} cellComponent={CellComponent as any}
cellProps={cellProps} cellProps={cellProps}
className={styles.noScrollbar} className={styles.noScrollbar}
columnCount={stickyColumnCount} columnCount={pinnedLeftColumnCount}
columnWidth={columnWidth} columnWidth={columnWidth}
rowCount={stickyRowCount} rowCount={pinnedRowCount}
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
{enableHeader && <div className={styles.itemTableStickyHeaderShadow} />} {enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
</div> </div>
)} )}
{!!stickyColumnCount && ( {!!pinnedLeftColumnCount && (
<div <div
className={styles.itemTableStickyColumnsContainer} className={styles.itemTablePinnedColumnsContainer}
ref={stickyColumnRef} ref={pinnedLeftColumnRef}
> >
<Grid <Grid
cellComponent={StickyColumnCell} cellComponent={PinnedColumnCell}
cellProps={cellProps} cellProps={cellProps}
className={clsx(styles.noScrollbar, styles.height100)} className={clsx(styles.noScrollbar, styles.height100)}
columnCount={stickyColumnCount} columnCount={pinnedLeftColumnCount}
columnWidth={columnWidth} columnWidth={columnWidth}
rowCount={totalRowCount} rowCount={totalRowCount}
rowHeight={(index, cellProps) => { rowHeight={(index, cellProps) => {
return getRowHeight(index + (stickyRowCount ?? 0), cellProps); return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
}} }}
/> />
</div> </div>
)} )}
</div> </div>
<div className={styles.itemTableStickyRowsContainer}> <div className={styles.itemTablePinnedRowsContainer}>
{!!stickyRowCount && ( {!!pinnedRowCount && (
<div <div
className={styles.itemTableStickyRowsGridContainer} className={styles.itemTablePinnedRowsGridContainer}
ref={stickyRowRef} ref={pinnedRowRef}
style={ style={
{ {
'--header-height': `${headerHeight}px`, '--header-height': `${headerHeight}px`,
minHeight: `${Array.from( minHeight: `${Array.from(
{ length: stickyRowCount ?? 0 }, { length: pinnedRowCount ?? 0 },
() => 0, () => 0,
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`, ).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
} as React.CSSProperties } as React.CSSProperties
} }
> >
<Grid <Grid
cellComponent={StickyRowCell} cellComponent={PinnedRowCell}
cellProps={cellProps} cellProps={cellProps}
className={styles.noScrollbar} className={styles.noScrollbar}
columnCount={totalColumnCount} columnCount={totalColumnCount}
columnWidth={(index, cellProps) => { columnWidth={(index, cellProps) => {
return typeof columnWidth === 'number' return typeof columnWidth === 'number'
? columnWidth ? columnWidth
: columnWidth(index + (stickyColumnCount ?? 0), cellProps); : columnWidth(
index + (pinnedLeftColumnCount ?? 0),
cellProps,
);
}} }}
rowCount={ rowCount={
Array.from({ length: stickyRowCount ?? 0 }, () => 0).length Array.from({ length: pinnedRowCount ?? 0 }, () => 0).length
} }
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
{enableHeader && <div className={styles.itemTableStickyHeaderShadow} />} {enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
</div> </div>
)} )}
<div className={styles.itemTableGridContainer} ref={mergedRowRef}> <div className={styles.itemTableGridContainer} ref={mergedRowRef}>
@@ -573,19 +671,98 @@ export const ItemTableList = ({
columnWidth={(index, cellProps) => { columnWidth={(index, cellProps) => {
return typeof columnWidth === 'number' return typeof columnWidth === 'number'
? columnWidth ? columnWidth
: columnWidth(index + (stickyColumnCount ?? 0), cellProps); : columnWidth(index + (pinnedLeftColumnCount ?? 0), cellProps);
}} }}
onCellsRendered={handleOnCellsRendered} onCellsRendered={handleOnCellsRendered}
rowCount={totalRowCount} rowCount={totalRowCount}
rowHeight={(index, cellProps) => { rowHeight={(index, cellProps) => {
return getRowHeight(index + (stickyRowCount ?? 0), cellProps); return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
}} }}
/> />
{stickyColumnCount > 0 && showLeftShadow && ( {pinnedLeftColumnCount > 0 && showLeftShadow && (
<div className={styles.itemTableLeftScrollShadow} /> <div className={styles.itemTableLeftScrollShadow} />
)} )}
{pinnedRightColumnCount > 0 && showRightShadow && (
<div className={styles.itemTableRightScrollShadow} />
)}
</div> </div>
</div> </div>
{!!pinnedRightColumnCount && (
<div
className={styles.itemTablePinnedColumnsGridContainer}
style={{
minWidth: `${Array.from(
{ length: pinnedRightColumnCount ?? 0 },
() => 0,
).reduce(
(a, _, i) =>
a +
(typeof columnWidth === 'number'
? columnWidth
: columnWidth(
i + pinnedLeftColumnCount + totalColumnCount,
cellProps,
)),
0,
)}px`,
}}
>
{!!(pinnedRightColumnCount || pinnedRowCount) && (
<div
className={styles.itemTablePinnedIntersectionGridContainer}
style={{
minHeight: `${Array.from(
{ length: pinnedRowCount ?? 0 },
() => 0,
).reduce((a, _, i) => a + getRowHeight(i, cellProps), 0)}px`,
}}
>
<Grid
cellComponent={PinnedRightIntersectionCell}
cellProps={cellProps}
className={styles.noScrollbar}
columnCount={pinnedRightColumnCount}
columnWidth={(index, cellProps) => {
return typeof columnWidth === 'number'
? columnWidth
: columnWidth(
index + pinnedLeftColumnCount + totalColumnCount,
cellProps,
);
}}
rowCount={pinnedRowCount}
rowHeight={getRowHeight}
/>
{enableHeader && (
<div className={styles.itemTablePinnedHeaderShadow} />
)}
</div>
)}
<div
className={styles.itemTablePinnedRightColumnsContainer}
ref={pinnedRightColumnRef}
>
<Grid
cellComponent={PinnedRightColumnCell}
cellProps={cellProps}
className={clsx(styles.noScrollbar, styles.height100)}
columnCount={pinnedRightColumnCount}
columnWidth={(index, cellProps) => {
return typeof columnWidth === 'number'
? columnWidth
: columnWidth(
index + pinnedLeftColumnCount + totalColumnCount,
cellProps,
);
}}
rowCount={totalRowCount}
rowHeight={(index, cellProps) => {
return getRowHeight(index + (pinnedRowCount ?? 0), cellProps);
}}
/>
</div>
</div>
)}
</div> </div>
<AnimatePresence initial={false}> <AnimatePresence initial={false}>
{hasExpanded && ( {hasExpanded && (