admin管理员组

文章数量:1312686

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?

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?

Share Improve this question edited Feb 10 at 10:41 vsync 131k59 gold badges340 silver badges422 bronze badges asked Jan 31 at 15:04 JöckerJöcker 6,8083 gold badges44 silver badges52 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

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.

Taken from my other answer here you can apply the below CSS globally and it will add a scroll shadow to MUI <DataGrid> component (v6 only, as v7 has a completely "fake" scroll):

Remember to add scrollShadow as className to the DataGrid component

/* From yair's codepen: https://codepen.io/vsync/pen/LYwdNvQ?editors=1100 */
.scrollShadow,
.dataGridScrollShadow .MuiDataGrid-virtualScroller {
  --shadow-size: 20px;

  background-image: linear-gradient(
    var(--shadow-color) 0%,
    transparent calc((var(--scroll)) * var(--shadow-size)),
    transparent calc(100% - var(--shadow-size) * (1 - var(--scroll))),
    var(--shadow-color) 100%
  );

  background-position-y: var(--shadow-offset);
  background-size: 100% calc(100% - var(--shadow-offset));
  background-repeat: no-repeat;

  animation: scroll-shadows cubic-bezier(0.25, 0.85, 0.8, 0.22);
  animation-timeline: scroll(self);
  animation-range: 0 100%;
}

/* Aniatable custom property */
@property --scroll {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

@keyframes scroll-shadows {
  /* Sets the shadow-color only if the element is scrollable */
  from,
  to {
    --shadow-color: #0002;
  }
  to {
    --scroll: 1;
  }
}

本文标签: reactjsShow scroll shadow for MUI DatatGrid componentStack Overflow