Develop Frontend Components for Project, Package, and Instance Views
This commit is contained in:
86
frontend/src/components/DataTable.tsx
Normal file
86
frontend/src/components/DataTable.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user