import type { ReactNode } from 'react'; import { NavIcon } from '../NavIcon'; import { Card } from './Card'; export type Column = { id: string; header: ReactNode; /** Label for the column picker when `header` is not a plain string. */ columnPickerLabel?: string; /** * REST `orderby` value; when {@link DataTableProps.sortState} and `onSortColumn` are set, * the header becomes a sort control (WordPress list-table style). */ sortKey?: string; /** If true, column stays visible and is omitted from the column picker. */ alwaysVisible?: boolean; /** Initial hidden state until toggled (localStorage overrides after first save). */ defaultHidden?: boolean; headerClassName?: string; cellClassName?: string; render: (row: T) => ReactNode; }; export type DataTableSortState = { orderby: string; order: 'asc' | 'desc'; }; type DataTableProps = { columns: Column[]; rows: T[]; rowKey: (row: T) => string | number; /** Plain-text fallback when `emptyContent` is not set. */ emptyMessage?: string; /** Full empty state (illustration, CTA). Takes precedence over `emptyMessage`. */ emptyContent?: ReactNode; /** When false, omit outer {@link Card} (e.g. inside {@link ListPanel}). */ wrapInCard?: boolean; /** Extra classes for each body row (e.g. faded trashed rows on “All”). */ getRowClassName?: (row: T) => string | undefined; /** Server sort; pair with `onSortColumn` and column `sortKey`. */ sortState?: DataTableSortState; /** Toggle or switch sort when a sortable header is activated. */ onSortColumn?: (orderby: string) => void; }; /** * App-owned data table (not WP_List_Table). Use with {@link TableSkeleton} for loading state. */ function renderSortableHeader( col: Column, sortState: DataTableSortState | undefined, onSortColumn: ((orderby: string) => void) | undefined ): ReactNode { const sk = col.sortKey; if (!sk || !sortState || !onSortColumn) { return col.header; } const active = sortState.orderby === sk; const orderWord = active ? (sortState.order === 'asc' ? 'ascending' : 'descending') : undefined; return ( ); } export function DataTable({ columns, rows, rowKey, emptyMessage = 'No rows to display.', emptyContent, wrapInCard = true, getRowClassName, sortState, onSortColumn, }: DataTableProps) { const table = (
{columns.map((col) => ( ))} {rows.length === 0 ? ( ) : ( {rows.map((row) => ( {columns.map((col) => ( ))} ))} )}
{renderSortableHeader(col as Column, sortState, onSortColumn)}
{emptyContent ?? (
{emptyMessage}
)}
{col.render(row)}
); if (!wrapInCard) { return table; } return {table}; }