55 lines
2.3 KiB
TypeScript
55 lines
2.3 KiB
TypeScript
import { useState } from 'react';
|
|
import type { DatasetSheet } from '../../domain/dataset';
|
|
import type { FindingRecord } from '../../domain/finding';
|
|
import { highlightText } from '../HighlightText/highlight-text';
|
|
import { useVirtualRows } from '../../hooks/use-virtual-rows';
|
|
|
|
interface Props {
|
|
sheet: DatasetSheet;
|
|
records: FindingRecord[];
|
|
search?: string;
|
|
}
|
|
|
|
/**
|
|
* Geschaetzte Zeilenhoehe, nur fuer die Fenstermathematik - siehe
|
|
* `RECORD_ROW_HEIGHT` in DatasetViewer.tsx fuer die gleiche Ueberlegung.
|
|
* Groesser als die Kartenzeile, weil einzelne Spalten (Description,
|
|
* Likelihood of Compromise) auch bei einer Rasterzeile umbrechen koennen.
|
|
*/
|
|
const TABLE_ROW_HEIGHT = 44;
|
|
|
|
export function DataTable({ sheet, records, search }: Props) {
|
|
const [markedRecordId, setMarkedRecordId] = useState<string | null>(null);
|
|
const rowWindow = useVirtualRows(records.length, TABLE_ROW_HEIGHT, 12);
|
|
const columnCount = sheet.analysis.headers.length + 1;
|
|
// Berechnete Geometrie der Virtualisierung als benannte Werte statt eines
|
|
// literalen Inline-Style-Objekts - siehe DatasetViewer.tsx fuer dieselbe
|
|
// Begruendung.
|
|
const topSpacerStyle = { height: rowWindow.paddingTop, padding: 0, border: 0 };
|
|
const bottomSpacerStyle = { height: rowWindow.paddingBottom, padding: 0, border: 0 };
|
|
|
|
return (
|
|
<div className="table-scroll" ref={rowWindow.containerRef}>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th className="row-index">#</th>
|
|
{sheet.analysis.headers.map(header => <th key={header}>{highlightText(header, search)}</th>)}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{rowWindow.paddingTop > 0 && <tr aria-hidden="true"><td colSpan={columnCount} style={topSpacerStyle} /></tr>}
|
|
{records.slice(rowWindow.startIndex, rowWindow.endIndex).map(record => (
|
|
<tr key={record.id} data-row-marked={markedRecordId === record.id ? 'true' : undefined}
|
|
onClick={() => setMarkedRecordId(record.id)}>
|
|
<td className="row-index">{record.index + 1}</td>
|
|
{record.fields.map(field => <td key={`${record.id}-${field.index}`}>{highlightText(field.value, search)}</td>)}
|
|
</tr>
|
|
))}
|
|
{rowWindow.paddingBottom > 0 && <tr aria-hidden="true"><td colSpan={columnCount} style={bottomSpacerStyle} /></tr>}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|