Describe the tables as TanStack columns

Add ui/DataTable, which builds ReUI's DataGrid from `columns` and `data`, and move
all 23 tables onto it. What the grid does not take (a class on a row or cell,
`aria-sort`, `style`, `colSpan`, `role`, `hidden`) is put on the elements it drew
after every render. The column definitions keep their identity between renders, so
cells are not mounted again, and the grid's own paging is off.

The stacked rows on a phone, the column labels and the sort order of the original
work as before. In Blocking > Lists a group heading is a row whose first cell spans
the four columns. docs/reui-migration-plan.md records it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Sonnet 5.5 committed 2026-10-27 00:40:53 +07:00
1 parent f3531c64f9
commit 4c0a5de7e7
24 files changed
+1611 -1175

No files matched your search

+78 -49
View File
@@ -21,7 +21,7 @@ import { toIso, dateTime } from './dates'
import { Loading } from '../../ui/Empty'
import { Check } from '../../ui/Check'
import { Tag } from '../../ui/Tag'
import { Table } from '../../ui/Table'
import { DataTable } from '../../ui/DataTable'
import styles from './Logs.module.css'
import { pageWindow } from '../../lib/pagination'
import { Pagination } from '../../ui/Pagination'
@@ -927,53 +927,82 @@ export function QueryLogs({ tabs, token, node = '', request }: QueryLogsProps) {
{/* Always all seven, whether or not they appear on this page: see `LEGEND_ROWS`. */}
<RowLegend fold={fold} />
<Table
<DataTable
data={page.entries}
getRowId={(e) => String(e.rowNumber)}
rowProps={(e) => ({ className: rowClass(e) })}
stack
tableClass={styles.queryTable}
header={
<>
<th>#</th>
<th>Timestamp</th>
<th>Client IP Address</th>
<th>Protocol</th>
<th>Response Type</th>
<th>RCODE</th>
<th>Domain</th>
<th>Type</th>
<th>Class</th>
<th>Answer</th>
<th className={tbl.actionsCell} />
</>
}
>
{page.entries.map((e) => (
<tr key={e.rowNumber} className={rowClass(e)}>
<td className={styles.mono}>{e.rowNumber}</td>
<td className={`${styles.mono} ${styles.stamp}`}>
<Stamp text={dateTime(e.timestamp)} />
</td>
<td className={styles.mono}>
<Clip text={e.clientIpAddress} className={styles.clipClient} />
</td>
<td>{e.protocol}</td>
<td>
columns={[
{
id: 'c0',
header: '#',
td: () => ({ className: styles.mono }),
cell: (e) => e.rowNumber,
},
{
id: 'c1',
header: 'Timestamp',
td: () => ({ className: `${styles.mono} ${styles.stamp}` }),
cell: (e) => (<Stamp text={dateTime(e.timestamp)} />),
},
{
id: 'c2',
header: 'Client IP Address',
td: () => ({ className: styles.mono }),
cell: (e) => (<Clip text={e.clientIpAddress} className={styles.clipClient} />),
},
{
id: 'c3',
header: 'Protocol',
cell: (e) => e.protocol,
},
{
id: 'c4',
header: 'Response Type',
cell: (e) => (
<>
{/* The row's colour, in this cell: see `.tone` in Logs.module.css. */}
<span className={styles.tone}>{e.responseType}</span>
{e.responseRtt != null && (
<div className={styles.rtt}>({e.responseRtt.toFixed(2)} ms)</div>
)}
</td>
<td>{e.rcode}</td>
{/* logs.js:518. The root is written with a dot. */}
<td className={`${styles.mono} ${tbl.lead}`}>
<Clip text={e.qname === '' ? '.' : (e.qname ?? '')} className={styles.clipDomain} />
</td>
<td className={tbl.lead}>{e.qtype ?? ''}</td>
<td>{e.qclass ?? ''}</td>
<td className={`${styles.mono} ${styles.answer}`}>
<Clip text={e.answer ?? ''} className={styles.clipAnswer} />
</td>
<td className={tbl.actionsCell}>
<span className={styles.tone}>{e.responseType}</span>
{e.responseRtt != null && (
<div className={styles.rtt}>({e.responseRtt.toFixed(2)} ms)</div>
)}
</>
),
},
{
id: 'c5',
header: 'RCODE',
cell: (e) => e.rcode,
},
{
id: 'c6',
header: 'Domain',
td: () => ({ className: `${styles.mono} ${tbl.lead}` }),
cell: (e) => (<Clip text={e.qname === '' ? '.' : (e.qname ?? '')} className={styles.clipDomain} />),
},
{
id: 'c7',
header: 'Type',
td: () => ({ className: tbl.lead }),
cell: (e) => e.qtype ?? '',
},
{
id: 'c8',
header: 'Class',
cell: (e) => e.qclass ?? '',
},
{
id: 'c9',
header: 'Answer',
td: () => ({ className: `${styles.mono} ${styles.answer}` }),
cell: (e) => (<Clip text={e.answer ?? ''} className={styles.clipAnswer} />),
},
{
id: 'c10',
th: { className: tbl.actionsCell },
td: () => ({ className: tbl.actionsCell }),
cell: (e) => (
<Menu label={`Actions for row ${e.rowNumber}`}>
{(close) => {
const verb = verbFor(e.responseType)
@@ -1008,10 +1037,10 @@ export function QueryLogs({ tabs, token, node = '', request }: QueryLogsProps) {
)
}}
</Menu>
</td>
</tr>
))}
</Table>
),
},
]}
/>
<div className={styles.count}>
<span>{statusText(page)}</span>