"use client"; import { ArrowIcon } from "../ui/icons.tsx"; import { useMemo, useState, type ReactNode } from "react"; export type DataColumn = { id: string; header: string; render: (row: T) => ReactNode; sortValue?: (row: T) => string | number | null; align?: "left" | "right"; }; export function DataTable({ rows, columns, label, getRowId, empty = "No rows to show.", loading = false, className = "", }: { rows: readonly T[]; columns: readonly DataColumn[]; label: string; getRowId: (row: T) => string; empty?: ReactNode; loading?: boolean; className?: string; }) { const [sort, setSort] = useState<{ id: string; direction: 1 | -1 } | null>( null, ); const ordered = useMemo(() => { const column = columns.find((c) => c.id === sort?.id); if (!column?.sortValue || !sort) return rows; const value = column.sortValue; return [...rows].sort((a, b) => { const x = value(a), y = value(b); if (x === null) return y === null ? 0 : 1; if (y === null) return -1; return ( sort.direction * (typeof x === "number" && typeof y === "number" ? x - y : String(x).localeCompare(String(y), undefined, { numeric: true })) ); }); }, [rows, columns, sort]); return (
{columns.map((c) => ( ))} {ordered.map((row) => ( {columns.map((c) => ( ))} ))} {!rows.length && ( )}
{label}
{c.sortValue ? ( ) : ( c.header )}
{c.render(row)}
{loading ? "Loading rows…" : empty}
); }