metis-desk/packages/shadcn-ui/src/components/entity-table-content.tsx

99 lines
3.1 KiB
TypeScript
Raw Normal View History

2026-10-07 15:27:23 +00:00
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>
);
}