'use client'; import type { ReactNode } from 'react'; import { flexRender, type Table as TanstackTable, type Row } from '@tanstack/react-table'; import { cn } from '@/lib/utils'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; /** * Renders the same TanStack `Table` instance as a desktop table on `lg+` and * as a card list on mobile, using `cardRender(row)` for the per-row card body. * * Filters and sort live above the rendered rows; callers pass them as * `headerSlot`. On desktop the rows are sortable via column header clicks * (TanStack default); on mobile, sort is exposed via a `` opened by * the caller's headerSlot — this primitive doesn't enforce a sort UI. */ export function DataView({ table, cardRender, headerSlot, emptyState, className, }: { table: TanstackTable; cardRender: (row: Row) => ReactNode; headerSlot?: ReactNode; emptyState?: ReactNode; className?: string; }) { const rows = table.getRowModel().rows; const isEmpty = rows.length === 0; return (
{headerSlot ?
{headerSlot}
: null} {/* Desktop: TanStack table */}
{table.getHeaderGroups().map((group) => ( {group.headers.map((header) => ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ))} ))} {isEmpty ? ( {emptyState ?? 'No results.'} ) : ( rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) )}
{/* Mobile: card list */}
    {isEmpty ? (
  • {emptyState ?? 'No results.'}
  • ) : ( rows.map((row) => (
  • {cardRender(row)}
  • )) )}
); }