add shadow to table header

This commit is contained in:
jeffvli
2025-11-14 11:51:04 -08:00
parent e8e82977d7
commit b502f3a5ce
2 changed files with 108 additions and 25 deletions
@@ -27,6 +27,7 @@
} }
.item-table-pinned-rows-container { .item-table-pinned-rows-container {
position: relative;
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
flex-direction: column; flex-direction: column;
@@ -41,10 +42,21 @@
} }
.item-table-pinned-rows-grid-container.with-header { .item-table-pinned-rows-grid-container.with-header {
border-bottom: 1px solid var(--theme-colors-border); position: relative;
}
.item-table-pinned-rows-grid-container.with-header::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 1px;
content: '';
background-color: var(--theme-colors-border);
} }
.item-table-pinned-columns-grid-container { .item-table-pinned-columns-grid-container {
position: relative;
display: flex; display: flex;
flex: 0 1 auto; flex: 0 1 auto;
flex-direction: column; flex-direction: column;
@@ -58,7 +70,17 @@
} }
.item-table-pinned-intersection-grid-container.with-header { .item-table-pinned-intersection-grid-container.with-header {
border-bottom: 1px solid var(--theme-colors-border); position: relative;
}
.item-table-pinned-intersection-grid-container.with-header::after {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 1px;
content: '';
background-color: var(--theme-colors-border);
} }
.item-table-pinned-columns-container { .item-table-pinned-columns-container {
@@ -130,3 +152,19 @@
transparent 100% transparent 100%
); );
} }
.item-table-top-scroll-shadow {
position: absolute;
top: var(--header-height, 40px);
right: 0;
left: 0;
z-index: 1;
height: 8px;
pointer-events: none;
background: linear-gradient(
to bottom,
rgb(0 0 0 / 50%) 0%,
rgb(0 0 0 / 5%) 50%,
transparent 100%
);
}
@@ -115,6 +115,7 @@ interface VirtualizedTableGridProps {
playerContext: PlayerContext; playerContext: PlayerContext;
showLeftShadow: boolean; showLeftShadow: boolean;
showRightShadow: boolean; showRightShadow: boolean;
showTopShadow: boolean;
size: 'compact' | 'default' | 'large'; size: 'compact' | 'default' | 'large';
tableId: string; tableId: string;
totalColumnCount: number; totalColumnCount: number;
@@ -154,6 +155,7 @@ const VirtualizedTableGrid = React.memo(
playerContext, playerContext,
showLeftShadow, showLeftShadow,
showRightShadow, showRightShadow,
showTopShadow,
size, size,
tableId, tableId,
totalColumnCount, totalColumnCount,
@@ -293,12 +295,15 @@ const VirtualizedTableGrid = React.memo(
<div className={styles.itemTableContainer}> <div className={styles.itemTableContainer}>
<div <div
className={styles.itemTablePinnedColumnsGridContainer} className={styles.itemTablePinnedColumnsGridContainer}
style={{ style={
minWidth: `${Array.from({ length: pinnedLeftColumnCount }, () => 0).reduce( {
(a, _, i) => a + columnWidth(i), '--header-height': `${headerHeight}px`,
0, minWidth: `${Array.from(
)}px`, { length: pinnedLeftColumnCount },
}} () => 0,
).reduce((a, _, i) => a + columnWidth(i), 0)}px`,
} as React.CSSProperties
}
> >
{!!(pinnedLeftColumnCount || pinnedRowCount) && ( {!!(pinnedLeftColumnCount || pinnedRowCount) && (
<div <div
@@ -322,9 +327,11 @@ const VirtualizedTableGrid = React.memo(
rowCount={pinnedRowCount} rowCount={pinnedRowCount}
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
{enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
</div> </div>
)} )}
{enableHeader && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} />
)}
{!!pinnedLeftColumnCount && ( {!!pinnedLeftColumnCount && (
<div <div
className={styles.itemTablePinnedColumnsContainer} className={styles.itemTablePinnedColumnsContainer}
@@ -344,7 +351,14 @@ const VirtualizedTableGrid = React.memo(
</div> </div>
)} )}
</div> </div>
<div className={styles.itemTablePinnedRowsContainer}> <div
className={styles.itemTablePinnedRowsContainer}
style={
{
'--header-height': `${headerHeight}px`,
} as React.CSSProperties
}
>
{!!pinnedRowCount && ( {!!pinnedRowCount && (
<div <div
className={clsx(styles.itemTablePinnedRowsGridContainer, { className={clsx(styles.itemTablePinnedRowsGridContainer, {
@@ -373,9 +387,11 @@ const VirtualizedTableGrid = React.memo(
rowCount={Array.from({ length: pinnedRowCount }, () => 0).length} rowCount={Array.from({ length: pinnedRowCount }, () => 0).length}
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
{enableHeader && <div className={styles.itemTablePinnedHeaderShadow} />}
</div> </div>
)} )}
{enableHeader && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} />
)}
<div className={styles.itemTableGridContainer} ref={mergedRowRef}> <div className={styles.itemTableGridContainer} ref={mergedRowRef}>
<Grid <Grid
cellComponent={RowCell} cellComponent={RowCell}
@@ -402,16 +418,20 @@ const VirtualizedTableGrid = React.memo(
{!!pinnedRightColumnCount && ( {!!pinnedRightColumnCount && (
<div <div
className={styles.itemTablePinnedColumnsGridContainer} className={styles.itemTablePinnedColumnsGridContainer}
style={{ style={
minWidth: `${Array.from( {
{ length: pinnedRightColumnCount }, '--header-height': `${headerHeight}px`,
() => 0, minWidth: `${Array.from(
).reduce( { length: pinnedRightColumnCount },
(a, _, i) => () => 0,
a + columnWidth(i + pinnedLeftColumnCount + totalColumnCount), ).reduce(
0, (a, _, i) =>
)}px`, a +
}} columnWidth(i + pinnedLeftColumnCount + totalColumnCount),
0,
)}px`,
} as React.CSSProperties
}
> >
{!!(pinnedRightColumnCount || pinnedRowCount) && ( {!!(pinnedRightColumnCount || pinnedRowCount) && (
<div <div
@@ -439,11 +459,11 @@ const VirtualizedTableGrid = React.memo(
rowCount={pinnedRowCount} rowCount={pinnedRowCount}
rowHeight={getRowHeight} rowHeight={getRowHeight}
/> />
{enableHeader && (
<div className={styles.itemTablePinnedHeaderShadow} />
)}
</div> </div>
)} )}
{enableHeader && showTopShadow && (
<div className={styles.itemTableTopScrollShadow} />
)}
<div <div
className={styles.itemTablePinnedRightColumnsContainer} className={styles.itemTablePinnedRightColumnsContainer}
ref={pinnedRightColumnRef} ref={pinnedRightColumnRef}
@@ -523,7 +543,7 @@ interface ItemTableListProps {
onColumnReordered?: ( onColumnReordered?: (
columnIdFrom: TableColumn, columnIdFrom: TableColumn,
columnIdTo: TableColumn, columnIdTo: TableColumn,
edge: 'top' | 'bottom' | 'left' | 'right' | null, edge: 'bottom' | 'left' | 'right' | 'top' | null,
) => void; ) => void;
onColumnResized?: (columnId: TableColumn, width: number) => void; onColumnResized?: (columnId: TableColumn, width: number) => void;
onRangeChanged?: (range: { startIndex: number; stopIndex: number }) => void; onRangeChanged?: (range: { startIndex: number; stopIndex: number }) => void;
@@ -659,6 +679,7 @@ export const ItemTableList = ({
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 [showRightShadow, setShowRightShadow] = useState(false);
const [showTopShadow, setShowTopShadow] = useState(false);
const handleRef = useRef<ItemListHandle | null>(null); const handleRef = useRef<ItemListHandle | null>(null);
const containerFocusRef = useRef<HTMLDivElement | null>(null); const containerFocusRef = useRef<HTMLDivElement | null>(null);
@@ -1181,6 +1202,29 @@ export const ItemTableList = ({
}; };
}, [pinnedLeftColumnCount, pinnedRightColumnCount]); }, [pinnedLeftColumnCount, pinnedRightColumnCount]);
// Handle top shadow visibility based on vertical scroll
useEffect(() => {
const row = rowRef.current?.childNodes[0] as HTMLDivElement;
if (!row || !enableHeader) {
setShowTopShadow(false);
return;
}
const checkScrollPosition = () => {
const scrollTop = row.scrollTop;
setShowTopShadow(scrollTop > 0);
};
checkScrollPosition();
row.addEventListener('scroll', checkScrollPosition);
return () => {
row.removeEventListener('scroll', checkScrollPosition);
};
}, [enableHeader]);
const getRowHeight = useCallback( const getRowHeight = useCallback(
(index: number, cellProps: TableItemProps) => { (index: number, cellProps: TableItemProps) => {
const height = size === 'compact' ? 40 : size === 'large' ? 88 : 64; const height = size === 'compact' ? 40 : size === 'large' ? 88 : 64;
@@ -1427,6 +1471,7 @@ export const ItemTableList = ({
playerContext={playerContext} playerContext={playerContext}
showLeftShadow={showLeftShadow} showLeftShadow={showLeftShadow}
showRightShadow={showRightShadow} showRightShadow={showRightShadow}
showTopShadow={showTopShadow}
size={size} size={size}
tableId={tableId} tableId={tableId}
totalColumnCount={totalColumnCount} totalColumnCount={totalColumnCount}