1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
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>
);
}
|