'use client' import type { ReactNode } from 'react' import { cn } from '../../../utils/cn' import type { NoDataProps } from '../no-data' import { useDataTableContext } from './data-table' import { DataTableEmpty } from './data-table-empty' import { DataTableRow } from './data-table-row' import { DataTableSkeleton, ROW_HEIGHT_DESKTOP, ROW_HEIGHT_MOBILE, ROW_SHELL_CLASSES, } from './data-table-skeleton' export interface DataTableBodyProps { /** Show skeleton rows while `loading` is true and data is empty. */ loading?: boolean /** @deprecated Use `emptyState` instead. Legacy single-line message; mapped to the empty state's title. */ emptyMessage?: string /** Props for the empty state (NoData) shown when there are no rows. Overrides `emptyMessage`. */ emptyState?: NoDataProps /** Skeleton row count when loading. Default `10`. */ skeletonRows?: number className?: string /** * Per-row class name. Prefer `useCallback` for function form to avoid * breaking `React.memo` on rows. */ rowClassName?: string | ((item: T, index: number) => string) /** Dense row height. */ compact?: boolean /** * Treat the design row height as a minimum so multi-line cell content grows * the row instead of clipping. Default keeps the fixed height. */ autoHeight?: boolean /** * Click anywhere on a row (except elements with `data-no-row-click`). Prefer * `useCallback` to avoid breaking `React.memo` on rows. */ onRowClick?: (item: T) => void /** * Turn each row into a `next/link` to the returned URL. Ignored if * `onRowClick` is set. Prefer `useCallback` for the same reason. */ rowHref?: (item: T) => string | null | undefined /** * Keep table height stable by padding with invisible rows when data is short. * Typically set to the same value as `skeletonRows`. Pass `0` to disable. */ minRows?: number /** * Render expandable content below a row, inside the same card. Return a node to * attach it (the cells row gains a bottom divider), or null/undefined for none. * Interactive content inside must carry `data-no-row-click`. */ renderSubRow?: (item: T) => ReactNode } /** * Renders skeleton / empty state / rows based on table context state. Place * inside ``. Rows use `React.memo` for performance — memoize * `onRowClick` / `rowHref` / `rowClassName` (if function) with `useCallback` * in the consumer to get the full benefit. */ export function DataTableBody({ loading, emptyMessage, emptyState, skeletonRows = 10, className, rowClassName, compact, autoHeight, onRowClick, rowHref, minRows, renderSubRow, }: DataTableBodyProps) { const table = useDataTableContext() const rows = table.getRowModel().rows if (loading && rows.length === 0) { return (
) } if (rows.length === 0) { return (
{emptyState ? ( ) : emptyMessage != null ? ( ) : ( )}
) } const padCount = minRows ? Math.max(0, minRows - rows.length) : 0 return (
{rows.map((row, index) => { const item = row.original const href = rowHref?.(item) ?? undefined const cls = typeof rowClassName === 'function' ? rowClassName(item, index) : rowClassName return ( key={row.id} row={row} onClick={onRowClick} href={href} compact={compact} autoHeight={autoHeight} className={cls} subRow={renderSubRow?.(item)} /> ) })} {padCount > 0 && Array.from({ length: padCount }).map((_, i) => (