1/* oxlint-disable react/no-array-index-key -- Table rows and columns have stable positions and may contain identical values. */
2import { parseDelimitedPreview } from "@t3tools/shared/delimitedPreview";
3import { useMemo } from "react";
4
5import { FileSurfaceNotice } from "./fileSurfaceChrome";
6
7/** A bounded, readable table for CSV and TSV text; the source view keeps every byte. */
8export function DelimitedTablePreview(props: {
9 readonly name: string;
10 readonly text: string;
11 readonly delimiter: "," | "\t";
12}) {
13 const table = useMemo(
14 () => parseDelimitedPreview(props.text, props.delimiter),
15 [props.text, props.delimiter],
16 );
17 const [header, ...body] = table.rows;
18 return (
19 <div className="flex min-h-0 flex-1 flex-col">
20 {table.truncated ? (
21 <FileSurfaceNotice>
22 Table limited to the first 100 rows and 30 columns. Switch to source for the rest.
23 </FileSurfaceNotice>
24 ) : null}
25 <div className="min-h-0 flex-1 overflow-auto">
26 <table
27 className="min-w-full border-separate border-spacing-0 text-xs"
28 aria-label={props.name}
29 >
30 {header ? (
31 <thead className="sticky top-0 z-10">
32 <tr>
33 {header.map((cell, columnIndex) => (
34 <th
35 key={columnIndex}
36 scope="col"
37 className="max-w-80 border-b border-border bg-muted/60 px-3 py-1.5 text-left align-bottom font-medium whitespace-pre-wrap break-words backdrop-blur"
38 >
39 {cell}
40 </th>
41 ))}
42 </tr>
43 </thead>
44 ) : null}
45 <tbody>
46 {body.map((row, rowIndex) => (
47 <tr key={rowIndex} className="even:bg-muted/30">
48 {row.map((cell, columnIndex) => (
49 <td
50 key={columnIndex}
51 className="max-w-80 border-b border-border/60 px-3 py-1.5 align-top whitespace-pre-wrap break-words tabular-nums"
52 >
53 {cell}
54 </td>
55 ))}
56 </tr>
57 ))}
58 </tbody>
59 </table>
60 </div>
61 </div>
62 );
63}