I have an MUI table with sticky headers. I want that whenever the table is at the top, it shows a shadow at the top below the header. When the scroll is totally at the bottom, it shows a shadow at the bottom. When it is in the middle position, it shows both shadows. How can I do it?
After a lot of trying, this is how I managed to do it
import * as React from "react";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
export default function DenseTable() {
const headerRef = React.useRef<HTMLTableSectionElement>(null);
const headerHeight = headerRef.current?.offsetHeight ?? 0;
const tableContainerRef = React.useRef<HTMLDivElement>(null);
const [isAtTop, setIsAtTop] = React.useState(true);
const [isAtBottom, setIsAtBottom] = React.useState(false);
React.useEffect(() => {
const handleScroll = () => {
if (tableContainerRef.current) {
const { scrollTop, scrollHeight, clientHeight } =
tableContainerRef.current;
setIsAtTop(scrollTop === 0);
// + 1 for some tolerance
setIsAtBottom(scrollTop + clientHeight >= scrollHeight - 1);
}
};
const container = tableContainerRef.current;
container?.addEventListener("scroll", handleScroll);
return () => {
container?.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<TableContainer
component={Paper}
sx={{ height: "300px" }}
ref={tableContainerRef}
>
<div
style={{
zIndex: 100,
content: '""',
position: "sticky",
// no idea where the -40px come from
marginTop: "calc(-100% - 40px)",
top: 0,
left: 0,
right: 0,
height: "100%",
pointerEvents: "none",
}}
>
{!isAtTop && (
<div
style={{
"--header-height": `${headerHeight}px`,
content: '""',
position: "absolute",
background:
"linear-gradient(180deg, rgba(20, 20, 20, 0.6) 0%, rgba(20, 20, 20, 0.6) 10%, rgba(30, 30, 30, 0) 100%)",
top: "calc(var(--header-height) - 1px)",
left: 0,
width: "100%",
height: "10px",
}}
className="shadow-top"
/>
)}
{!isAtBottom && (
<div
style={{
content: '""',
position: "absolute",
background:
"linear-gradient(0deg, rgba(20, 20, 20, 0.6) 0%, rgba(20, 20, 20, 0.6) 10%, rgba(30, 30, 30, 0) 100%)",
bottom: 0,
left: 0,
width: "100%",
height: "10px",
}}
className="shadow-bottom"
/>
)}
</div>
<Table
stickyHeader
sx={{ minWidth: 650 }}
size="small"
aria-label="a dense table"
>
<TableHead ref={headerRef}>
...
</TableHead>
<TableBody>
...
</TableBody>
</Table>
</TableContainer>
);
}
I still don't know how to calculate the - 40px
. Happy to receive comments about it.
Here the codesandbox for it.