You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bazarr/frontend/src/components/tables/BaseTable.tsx

127 lines
3.2 KiB

import { useIsLoading } from "@/contexts";
import { usePageSize } from "@/utilities/storage";
import { Box, createStyles, Skeleton, Table, Text } from "@mantine/core";
import { ReactNode, useMemo } from "react";
import { HeaderGroup, Row, TableInstance } from "react-table";
export type BaseTableProps<T extends object> = TableInstance<T> & {
tableStyles?: TableStyleProps<T>;
};
export interface TableStyleProps<T extends object> {
emptyText?: string;
striped?: boolean;
placeholder?: number;
hideHeader?: boolean;
fixHeader?: boolean;
headersRenderer?: (headers: HeaderGroup<T>[]) => JSX.Element[];
rowRenderer?: (row: Row<T>) => Nullable<JSX.Element>;
}
const useStyles = createStyles((theme) => {
return {
container: {
display: "block",
maxWidth: "100%",
overflowX: "auto",
},
table: {
borderCollapse: "collapse",
},
header: {},
};
});
function DefaultHeaderRenderer<T extends object>(
headers: HeaderGroup<T>[]
): JSX.Element[] {
return headers.map((col) => (
<th style={{ whiteSpace: "nowrap" }} {...col.getHeaderProps()}>
{col.render("Header")}
</th>
));
}
function DefaultRowRenderer<T extends object>(row: Row<T>): JSX.Element | null {
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
))}
</tr>
);
}
export default function BaseTable<T extends object>(props: BaseTableProps<T>) {
const {
headerGroups,
rows,
prepareRow,
getTableProps,
getTableBodyProps,
tableStyles,
} = props;
const headersRenderer = tableStyles?.headersRenderer ?? DefaultHeaderRenderer;
const rowRenderer = tableStyles?.rowRenderer ?? DefaultRowRenderer;
const { classes } = useStyles();
const colCount = useMemo(() => {
return headerGroups.reduce(
(prev, curr) => (curr.headers.length > prev ? curr.headers.length : prev),
0
);
}, [headerGroups]);
const empty = rows.length === 0;
const [pageSize] = usePageSize();
const isLoading = useIsLoading();
let body: ReactNode;
if (isLoading) {
body = Array(tableStyles?.placeholder ?? pageSize)
.fill(0)
.map((_, i) => (
<tr key={i}>
<td colSpan={colCount}>
<Skeleton height={24}></Skeleton>
</td>
</tr>
));
} else if (empty && tableStyles?.emptyText) {
body = (
<tr>
<td colSpan={colCount}>
<Text align="center">{tableStyles.emptyText}</Text>
</td>
</tr>
);
} else {
body = rows.map((row) => {
prepareRow(row);
return rowRenderer(row);
});
}
return (
<Box className={classes.container}>
<Table
className={classes.table}
striped={tableStyles?.striped ?? true}
{...getTableProps()}
>
<thead className={classes.header} hidden={tableStyles?.hideHeader}>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headersRenderer(headerGroup.headers)}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>{body}</tbody>
</Table>
</Box>
);
}