Files
2026-03-02 12:14:13 -08:00

59 lines
1.8 KiB
TypeScript

import { ReactNode } from 'react'
import clsx from 'clsx'
interface Column<T> {
header: string
accessor: (row: T) => ReactNode
className?: string
}
interface TableProps<T> {
columns: Column<T>[]
data: T[]
className?: string
/** Stable key for each row (e.g. row => row.id or row => row.hash). Falls back to index if not provided. */
keyExtractor?: (row: T) => string | number
}
export function Table<T>({ columns, data, className, keyExtractor }: TableProps<T>) {
return (
<div className={clsx('overflow-x-auto', className)}>
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-800">
<tr>
{columns.map((column, index) => (
<th
key={index}
className={clsx(
'px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider',
column.className
)}
>
{column.header}
</th>
))}
</tr>
</thead>
<tbody className="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
{data.map((row, rowIndex) => (
<tr key={keyExtractor ? keyExtractor(row) : rowIndex} className="hover:bg-gray-50 dark:hover:bg-gray-800">
{columns.map((column, colIndex) => (
<td
key={colIndex}
className={clsx(
'px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100',
column.className
)}
>
{column.accessor(row)}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
)
}