# React Table with fix Scrollbar :::spoiler <實作 TableContainerFixScrollbar compenment> {state="open"} ```javascripr= import React from 'react'; import PropTypes from 'prop-types'; import { makeStyles } from '@mui/styles'; import TableContainer from '@mui/material/TableContainer'; const useStyles = makeStyles(() => ({ scrollbarWrapper: { position: 'sticky', bottom: 0, left: 0, right: 0, height: '20px', overflowX: 'auto', overflowY: 'hidden', zIndex: 1000, background: 'white', borderTop: '1px solid rgba(224, 224, 224, 1)', '&::-webkit-scrollbar': { height: '12px', }, '&::-webkit-scrollbar-track': { background: '#f1f1f1', }, '&::-webkit-scrollbar-thumb': { background: '#888', borderRadius: '6px', }, '&::-webkit-scrollbar-thumb:hover': { background: '#555', }, }, scrollbarContent: { height: '1px', }, tableContainer: { '&::-webkit-scrollbar': { display: 'none', // hide scrollbar }, scrollbarWidth: 'none', // Firefox hide scrollbar msOverflowStyle: 'none', // IE/Edge hide scrollbar }, })); const TableContainerFixScrollbar = ({ children, data }) => { const classes = useStyles(); const tableContainerRef = React.useRef(null); const scrollbarRef = React.useRef(null); React.useEffect(() => { const tableContainer = tableContainerRef.current; const scrollbar = scrollbarRef.current; if (!tableContainer || !scrollbar) return; const handleTableScroll = () => { scrollbar.scrollLeft = tableContainer.scrollLeft; }; const handleScrollbarScroll = () => { tableContainer.scrollLeft = scrollbar.scrollLeft; }; tableContainer.addEventListener('scroll', handleTableScroll); scrollbar.addEventListener('scroll', handleScrollbarScroll); // set scrollbar width same with table const updateScrollbarWidth = () => { const table = tableContainer.querySelector('table'); if (table && scrollbar.firstChild) { scrollbar.firstChild.style.width = `${table.offsetWidth}px`; } }; updateScrollbarWidth(); return () => { tableContainer.removeEventListener('scroll', handleTableScroll); scrollbar.removeEventListener('scroll', handleScrollbarScroll); }; }, [data]); return ( <> <TableContainer className={classes.tableContainer} ref={tableContainerRef}> {children} </TableContainer> <div className={classes.scrollbarWrapper} ref={scrollbarRef}> <div className={classes.scrollbarContent}></div> </div> </> ); }; TableContainerFixScrollbar.propTypes = { children: PropTypes.node.isRequired, data: PropTypes.array }; export default TableContainerFixScrollbar; ``` ::: :::spoiler <使用 TableContainerFixScrollbar compenment> ```javascript 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'; function createData( name: string, calories: number, fat: number, carbs: number, protein: number, ) { return { name, calories, fat, carbs, protein }; } const rows = [ createData('Frozen yoghurt', 159, 6.0, 24, 4.0), createData('Ice cream sandwich', 237, 9.0, 37, 4.3), createData('Eclair', 262, 16.0, 24, 6.0), createData('Cupcake', 305, 3.7, 67, 4.3), createData('Gingerbread', 356, 16.0, 49, 3.9), ]; export default function BasicTable() { return ( <TableContainerFixScrollbar data={rows}> <Table sx={{ minWidth: 650 }} aria-label="simple table"> <TableHead> <TableRow> <TableCell>Dessert (100g serving)</TableCell> <TableCell align="right">Calories</TableCell> <TableCell align="right">Fat&nbsp;(g)</TableCell> <TableCell align="right">Carbs&nbsp;(g)</TableCell> <TableCell align="right">Protein&nbsp;(g)</TableCell> </TableRow> </TableHead> <TableBody> {rows.map((row) => ( <TableRow key={row.name} sx={{ '&:last-child td, &:last-child th': { border: 0 } }} > <TableCell component="th" scope="row"> {row.name} </TableCell> <TableCell align="right">{row.calories}</TableCell> <TableCell align="right">{row.fat}</TableCell> <TableCell align="right">{row.carbs}</TableCell> <TableCell align="right">{row.protein}</TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> ); } ``` ::: 要讓 Scrollbar 可以固定顯示在螢幕畫面下方,須將 `position 設為 'sticky'`,因此需用一個新的 div 模仿 scrollbar 的功能,跟原來的 table 切開 (line 87~89、 css scrollbarWrapper) 原來 table 的 scroball 則需隱藏 (css tableContainer) Scrollbar 的寬度會因資料不同而變動,所以當有新資料傳入時,透過 `React.useEffect` 重新計算