Skip to content

Commit

Permalink
added table gradient scroll bar info.
Browse files Browse the repository at this point in the history
  • Loading branch information
dwolosz committed Dec 5, 2024
1 parent 09b272b commit 1b37596
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 130 deletions.
9 changes: 9 additions & 0 deletions ngui/ui/src/components/Table/Table.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ const useStyles = makeStyles()(() => ({
display: "block",
wordBreak: "initial" // shown inside MUI Drawer table inherits word-break: "break-word" which leads to letter-by-letter break in side modals, for example
},
tableGradientOverlay: {
position: "absolute",
top: "0",
right: "0",
width: "250px",
height: "100%",
background: "linear-gradient(to left, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0))",
pointerEvents: "none"
},
hoverableRow: {
cursor: "pointer"
}
Expand Down
294 changes: 164 additions & 130 deletions ngui/ui/src/components/Table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useRef } from "react";
import { useEffect, useRef, useState } from "react";
import { Box, TableBody, TableCell, TableFooter, TableHead, TableRow } from "@mui/material";
import MuiTable from "@mui/material/Table";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
Expand Down Expand Up @@ -92,6 +92,35 @@ const Table = ({
manualGlobalFiltering
}) => {
const headerRef = useRef();
const tableContainerRef = useRef<HTMLElement>();
const [isGradientVisible, setIsGradientVisible] = useState(false);

const updateGradientVisibility = () => {
const container = tableContainerRef.current as HTMLElement;
if (container) {
const isScrollable = container.scrollWidth > container.clientWidth;
const atScrollEnd = container.scrollLeft + container.clientWidth >= container.scrollWidth;
setIsGradientVisible(isScrollable && !atScrollEnd);
}
};

useEffect(() => {
const container = tableContainerRef.current;

updateGradientVisibility();

if (container) {
container.addEventListener("scroll", updateGradientVisibility);
window.addEventListener("resize", updateGradientVisibility);
}

return () => {
if (container) {
container.removeEventListener("scroll", updateGradientVisibility);
window.removeEventListener("resize", updateGradientVisibility);
}
};
}, []);

const { classes } = useStyles();

Expand Down Expand Up @@ -273,141 +302,146 @@ const Table = ({
/>
{/* Wrap with box in order to make table fit 100% width with small amount of columns */}
{/* TODO: Consider using MUI TableContainer */}
<Box
className={classes.tableContainer}
sx={{
overflowX
}}
>
{isLoading ? (
<TableLoader columnsCounter={columns.length ?? 5} showHeader />
) : (
<MuiTable
sx={{
tableLayout
}}
style={{
...stickyTableStyles
}}
>
{withHeader && (
<TableHead ref={headerRef}>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHeaderCell
key={header.id}
isSelectionColumn={header.column.id === SELECTION_COLUMN_ID}
headerContext={header}
stickyStyles={stickyHeaderCellStyles}
getHeaderCellClassName={getHeaderCellClassName}
/>
))}
</TableRow>
))}
</TableHead>
)}
<TableBody>
{isEmptyArray(rows) ? (
<TableRow
sx={
disableBottomBorderForLastRow
? {
"&:last-child > td": {
borderBottom: "none"
<div style={{ position: "relative" }}>
<Box
ref={tableContainerRef}
className={classes.tableContainer}
sx={{
overflowX,
position: "relative"
}}
>
{isLoading ? (
<TableLoader columnsCounter={columns.length ?? 5} showHeader />
) : (
<MuiTable
sx={{
tableLayout
}}
style={{
...stickyTableStyles
}}
>
{withHeader && (
<TableHead ref={headerRef}>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHeaderCell
key={header.id}
isSelectionColumn={header.column.id === SELECTION_COLUMN_ID}
headerContext={header}
stickyStyles={stickyHeaderCellStyles}
getHeaderCellClassName={getHeaderCellClassName}
/>
))}
</TableRow>
))}
</TableHead>
)}
<TableBody>
{isEmptyArray(rows) ? (
<TableRow
sx={
disableBottomBorderForLastRow
? {
"&:last-child > td": {
borderBottom: "none"
}
}
}
: {}
}
>
<TableCell align="center" colSpan={columns.length}>
<FormattedMessage id={localization.emptyMessageId || DEFAULT_EMPTY_MESSAGE_ID} />
</TableCell>
</TableRow>
) : (
rows.map((row, index) => {
const rowStyle = typeof getRowStyle === "function" ? getRowStyle(row.original) : {};

return (
<TableRow
{...getRowHoverProperties(row)}
data-test-id={`row_${index}`}
key={row.id}
style={rowStyle}
sx={
disableBottomBorderForLastRow
? {
"&:last-child > td": {
borderBottom: "none"
: {}
}
>
<TableCell align="center" colSpan={columns.length}>
<FormattedMessage id={localization.emptyMessageId || DEFAULT_EMPTY_MESSAGE_ID} />
</TableCell>
</TableRow>
) : (
rows.map((row, index) => {
const rowStyle = typeof getRowStyle === "function" ? getRowStyle(row.original) : {};

return (
<TableRow
{...getRowHoverProperties(row)}
data-test-id={`row_${index}`}
key={row.id}
style={rowStyle}
sx={
disableBottomBorderForLastRow
? {
"&:last-child > td": {
borderBottom: "none"
}
}
}
: {}
}
>
{row.getVisibleCells().map((cell) => {
if (cell.column.id === SELECTION_COLUMN_ID) {
return <TableBodyCell className={"tableRowSelection"} key={cell.id} cell={cell} />;
: {}
}
const Cell = memoBodyCells ? MemoTableBodyCell : TableBodyCell;

return (
<Cell
key={cell.id}
cell={cell}
className={
typeof getRowCellClassName === "function" ? getRowCellClassName(cell.getContext()) : undefined
}
/>
);
})}
>
{row.getVisibleCells().map((cell) => {
if (cell.column.id === SELECTION_COLUMN_ID) {
return <TableBodyCell className={"tableRowSelection"} key={cell.id} cell={cell} />;
}
const Cell = memoBodyCells ? MemoTableBodyCell : TableBodyCell;

return (
<Cell
key={cell.id}
cell={cell}
className={
typeof getRowCellClassName === "function" ? getRowCellClassName(cell.getContext()) : undefined
}
/>
);
})}
</TableRow>
);
})
)}
</TableBody>
{withFooter && (
<TableFooter>
{table.getFooterGroups().map((footerGroup) => (
<TableRow key={footerGroup.id}>
{footerGroup.headers.map((footerContext) => (
<TableFooterCell key={footerContext.id} footerContext={footerContext} />
))}
</TableRow>
);
})
))}
</TableFooter>
)}
</TableBody>
{withFooter && (
<TableFooter>
{table.getFooterGroups().map((footerGroup) => (
<TableRow key={footerGroup.id}>
{footerGroup.headers.map((footerContext) => (
<TableFooterCell key={footerContext.id} footerContext={footerContext} />
))}
</TableRow>
))}
</TableFooter>
)}
</MuiTable>
)}
</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
paddingTop: SPACING_1,
flexWrap: "wrap",
flexDirection: { xs: "column-reverse", md: "row" },
":empty": { display: "none" }
}}
>
<InfoArea
counters={counters}
rowsCount={rows.length}
selectedRowsCount={selectedRowsCount}
dataTestIds={dataTestIds.infoAreaTestIds}
showAllLink={showAllLink}
pagination={paginationSettings}
withSearch={withSearch}
withSelection={withSelection}
withPagination={withPagination}
/>
{paginationSettings.pageCount > 1 && (
<Pagination
count={paginationSettings.pageCount}
page={paginationSettings.pageIndex + 1}
paginationHandler={paginationSettings.onPageIndexChange}
</MuiTable>
)}
</Box>
{isGradientVisible && <div className={classes.tableGradientOverlay} />}
<Box
sx={{
display: "flex",
alignItems: "center",
paddingTop: SPACING_1,
flexWrap: "wrap",
flexDirection: { xs: "column-reverse", md: "row" },
":empty": { display: "none" }
}}
>
<InfoArea
counters={counters}
rowsCount={rows.length}
selectedRowsCount={selectedRowsCount}
dataTestIds={dataTestIds.infoAreaTestIds}
showAllLink={showAllLink}
pagination={paginationSettings}
withSearch={withSearch}
withSelection={withSelection}
withPagination={withPagination}
/>
)}
</Box>
{paginationSettings.pageCount > 1 && (
<Pagination
count={paginationSettings.pageCount}
page={paginationSettings.pageIndex + 1}
paginationHandler={paginationSettings.onPageIndexChange}
/>
)}
</Box>
</div>
</div>
);
};
Expand Down

0 comments on commit 1b37596

Please sign in to comment.