99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
|
|
import type { Table as TableModel } from "@tanstack/react-table";
|
||
|
|
|
||
|
|
import type { EntityColumn } from "./entity-list";
|
||
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table";
|
||
|
|
export function EntityTableContent<T extends { id: string }>({
|
||
|
|
table,
|
||
|
|
columns,
|
||
|
|
title,
|
||
|
|
loading,
|
||
|
|
unavailable,
|
||
|
|
message,
|
||
|
|
rowClassName,
|
||
|
|
onSort,
|
||
|
|
}: {
|
||
|
|
table: TableModel<T>;
|
||
|
|
columns: readonly EntityColumn<T>[];
|
||
|
|
title: string;
|
||
|
|
loading: boolean;
|
||
|
|
unavailable: boolean;
|
||
|
|
message: string;
|
||
|
|
rowClassName: ((item: T) => string) | undefined;
|
||
|
|
onSort: () => void;
|
||
|
|
}) {
|
||
|
|
const visibleColumns = table.getVisibleLeafColumns();
|
||
|
|
const total = table.getFilteredRowModel().rows.length;
|
||
|
|
return (
|
||
|
|
<Table aria-label={title}>
|
||
|
|
<TableHeader>
|
||
|
|
<TableRow>
|
||
|
|
{visibleColumns.map((column) => {
|
||
|
|
const direction = column.getIsSorted();
|
||
|
|
return (
|
||
|
|
<TableHead
|
||
|
|
aria-sort={
|
||
|
|
direction === "asc"
|
||
|
|
? "ascending"
|
||
|
|
: direction === "desc"
|
||
|
|
? "descending"
|
||
|
|
: undefined
|
||
|
|
}
|
||
|
|
className="first:min-w-40"
|
||
|
|
key={column.id}
|
||
|
|
scope="col"
|
||
|
|
>
|
||
|
|
{column.getCanSort() ? (
|
||
|
|
<button
|
||
|
|
className="inline-flex items-center gap-2 rounded py-1 text-left focus-visible:outline-2 focus-visible:outline-ring"
|
||
|
|
onClick={() => {
|
||
|
|
column.toggleSorting();
|
||
|
|
onSort();
|
||
|
|
}}
|
||
|
|
type="button"
|
||
|
|
>
|
||
|
|
{String(column.columnDef.header)}
|
||
|
|
<span aria-hidden="true">
|
||
|
|
{direction === "asc" ? "↑" : direction === "desc" ? "↓" : "↕"}
|
||
|
|
</span>
|
||
|
|
</button>
|
||
|
|
) : (
|
||
|
|
String(column.columnDef.header)
|
||
|
|
)}
|
||
|
|
</TableHead>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</TableRow>
|
||
|
|
</TableHeader>
|
||
|
|
<TableBody>
|
||
|
|
{loading || unavailable ? (
|
||
|
|
<TableRow>
|
||
|
|
<TableCell colSpan={visibleColumns.length}>
|
||
|
|
<span className="text-muted-foreground">{message}</span>
|
||
|
|
</TableCell>
|
||
|
|
</TableRow>
|
||
|
|
) : (
|
||
|
|
table.getRowModel().rows.map((row) => (
|
||
|
|
<TableRow className={rowClassName?.(row.original)} key={row.id}>
|
||
|
|
{visibleColumns.map((column) => (
|
||
|
|
<TableCell
|
||
|
|
className="h-14 first:min-w-40 max-w-lg whitespace-normal break-words px-3 py-3"
|
||
|
|
key={column.id}
|
||
|
|
>
|
||
|
|
{columns.find((item) => item.id === column.id)?.render(row.original)}
|
||
|
|
</TableCell>
|
||
|
|
))}
|
||
|
|
</TableRow>
|
||
|
|
))
|
||
|
|
)}
|
||
|
|
{!(loading || unavailable || total) && (
|
||
|
|
<TableRow>
|
||
|
|
<TableCell className="p-6 text-muted-foreground" colSpan={visibleColumns.length}>
|
||
|
|
{message}
|
||
|
|
</TableCell>
|
||
|
|
</TableRow>
|
||
|
|
)}
|
||
|
|
</TableBody>
|
||
|
|
</Table>
|
||
|
|
);
|
||
|
|
}
|