import React, { useState, useMemo } from 'react'; import clsx from 'clsx'; export interface TableColumn { key: string; header: string; sortable?: boolean; render?: (row: T) => React.ReactNode; accessor?: (row: T) => string | number; width?: string; } export interface TableProps { columns: TableColumn[]; data: T[]; rowKey: (row: T) => string; onRowClick?: (row: T) => void; emptyMessage?: string; loading?: boolean; } type SortDirection = 'asc' | 'desc'; export function Table>({ columns, data, rowKey, onRowClick, emptyMessage = 'Keine Daten vorhanden', loading = false, }: TableProps) { const [sortColumn, setSortColumn] = useState(null); const [sortDirection, setSortDirection] = useState('asc'); const sortedData = useMemo(() => { if (!sortColumn) return data; const column = columns.find((c) => c.key === sortColumn); if (!column?.sortable) return data; const accessor = column.accessor || ((row: T) => row[sortColumn]); return [...data].sort((a, b) => { const aVal = accessor(a); const bVal = accessor(b); if (aVal < bVal) return sortDirection === 'asc' ? -1 : 1; if (aVal > bVal) return sortDirection === 'asc' ? 1 : -1; return 0; }); }, [data, sortColumn, sortDirection, columns]); const handleSort = (column: TableColumn) => { if (!column.sortable) return; if (sortColumn === column.key) { setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); } else { setSortColumn(column.key); setSortDirection('asc'); } }; return (
{columns.map((col) => ( ))} {loading ? ( ) : sortedData.length === 0 ? ( ) : ( sortedData.map((row) => ( onRowClick(row) : undefined} tabIndex={onRowClick ? 0 : undefined} onKeyDown={onRowClick ? (e) => { if (e.key === 'Enter') onRowClick(row); } : undefined} > {columns.map((col) => ( ))} )) )}
handleSort(col)} onKeyDown={(e) => { if (col.sortable && (e.key === 'Enter' || e.key === ' ')) { e.preventDefault(); handleSort(col); } }} tabIndex={col.sortable ? 0 : undefined} role={col.sortable ? 'button' : undefined} aria-label={col.sortable ? `${col.header}, sortierbar` : undefined} >
{col.header} {col.sortable && ( )}
Wird geladen...
{emptyMessage}
{col.render ? col.render(row) : (row[col.key] ?? '—')}
); }