87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
import { ReactNode } from 'react';
|
|
import './DataTable.css';
|
|
|
|
interface Column<T> {
|
|
key: string;
|
|
header: string;
|
|
render: (item: T) => ReactNode;
|
|
className?: string;
|
|
sortable?: boolean;
|
|
}
|
|
|
|
interface DataTableProps<T> {
|
|
data: T[];
|
|
columns: Column<T>[];
|
|
keyExtractor: (item: T) => string;
|
|
emptyMessage?: string;
|
|
className?: string;
|
|
onSort?: (key: string) => void;
|
|
sortKey?: string;
|
|
sortOrder?: 'asc' | 'desc';
|
|
}
|
|
|
|
export function DataTable<T>({
|
|
data,
|
|
columns,
|
|
keyExtractor,
|
|
emptyMessage = 'No data available',
|
|
className = '',
|
|
onSort,
|
|
sortKey,
|
|
sortOrder,
|
|
}: DataTableProps<T>) {
|
|
if (data.length === 0) {
|
|
return (
|
|
<div className="data-table__empty">
|
|
<p>{emptyMessage}</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={`data-table ${className}`.trim()}>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
{columns.map((column) => (
|
|
<th
|
|
key={column.key}
|
|
className={`${column.className || ''} ${column.sortable ? 'data-table__th--sortable' : ''}`}
|
|
onClick={() => column.sortable && onSort?.(column.key)}
|
|
>
|
|
<span className="data-table__th-content">
|
|
{column.header}
|
|
{column.sortable && sortKey === column.key && (
|
|
<svg
|
|
className={`data-table__sort-icon ${sortOrder === 'desc' ? 'data-table__sort-icon--desc' : ''}`}
|
|
width="12"
|
|
height="12"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
>
|
|
<polyline points="18 15 12 9 6 15" />
|
|
</svg>
|
|
)}
|
|
</span>
|
|
</th>
|
|
))}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{data.map((item) => (
|
|
<tr key={keyExtractor(item)}>
|
|
{columns.map((column) => (
|
|
<td key={column.key} className={column.className}>
|
|
{column.render(item)}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|