import type { ReactNode } from "react"; import { EmptyBlock, ErrorBlock } from "./StateBlock"; export interface Column { key: string; header: ReactNode; render: (row: T) => ReactNode; /** Right-align (numeric) cells. */ numeric?: boolean; /** Extra class on the td/th. */ className?: string; } interface DataTableProps { columns: Column[]; rows: T[]; rowKey: (row: T) => string; loading?: boolean; error?: unknown; onRetry?: () => void; emptyTitle?: string; emptyHint?: string; caption?: string; /** Row click handler — renders rows as clickable. */ onRowClick?: (row: T) => void; /** Number of skeleton rows while loading. */ skeletonRows?: number; } /** * Data-dense table with unified loading / error / empty states. * Renders inside a horizontal-scroll wrapper so narrow viewports never * overflow the document. */ export function DataTable({ columns, rows, rowKey, loading = false, error, onRetry, emptyTitle = "No rows", emptyHint, caption, onRowClick, skeletonRows = 6, }: DataTableProps) { if (error !== undefined && error !== null) { return ; } if (loading) { return (
{Array.from({ length: skeletonRows }, (_, i) => (
))}
); } if (rows.length === 0) { return ; } return (
{caption ? : null} {columns.map((col) => ( ))} {rows.map((row) => ( onRowClick(row) : undefined} > {columns.map((col) => ( ))} ))}
{caption}
{col.header}
{col.render(row)}
); }