import { memo, useMemo } from 'react'; import { __ } from '@wordpress/i18n'; import clsx from 'clsx'; import type { BarDataTableProps } from './types'; /** * Derives a CSS grid track size for a column. * * Left-aligned columns grow to fill available space (`1fr`). * Right- and center-aligned columns use their explicit width or a sensible * pixel default derived from `minWidth`. * * @param {Object} col - The column definition. * @return {string} A CSS grid track value. */ function colTrack( col: { align?: string; width?: string; minWidth?: number }): string { if ( col.width ) { return col.width; } if ( ! col.align || 'left' === col.align ) { return '1fr'; } return `${ col.minWidth ?? 80 }px`; } /** * Generic, minimalist data table with a full-row proportional background bar. * * The bar spans the entire row width and is drawn behind all cell content, * giving readers an instant visual sense of scale without obscuring any text. * Because CSS `position: absolute` cannot reliably escape a ``, this * component uses a div-grid layout with ARIA table roles for accessibility. * * Header and body rows share a single parent CSS grid via `subgrid`, so * columns are always perfectly aligned regardless of content width. * * @template T - The row data type. * @param {BarDataTableProps} props - Component props. * @return {JSX.Element} The rendered table. */ function BarDataTableInner>({ columns, data, rowKey, barColumnKey, isLoading = false, emptyState, className }: BarDataTableProps ) { const gridTemplate = useMemo( () => columns.map( colTrack ).join( ' ' ), [ columns ] ); const barMax = useMemo( () => { if ( ! barColumnKey || 0 === data.length ) { return 0; } return Math.max( ...data.map( ( row ) => { const v = row[ barColumnKey ]; return 'number' === typeof v ? v : 0; }) ); }, [ data, barColumnKey ]); const showSkeleton = isLoading && 0 === data.length; return (
{/* Header row. */}
{ columns.map( ( col ) => (
{ col.label }
) ) }
{/* Body rows. */}
{ showSkeleton && Array.from({ length: 5 }).map( ( _, i ) => (
{ columns.map( ( col ) => (
) ) }
) ) } { ! showSkeleton && 0 === data.length && (
{ emptyState ?? __( 'No data available.', 'burst-statistics' ) }
) } { ! showSkeleton && data.map( ( row, index ) => { const barValue = barColumnKey ? ( row[ barColumnKey ] as number ) : 0; const barPct = 0 < barMax && barColumnKey ? ( barValue / barMax ) * 100 : 0; return (
{/* Full-row background bar — rendered behind all cells. */} { barColumnKey && 0 < barPct && ( ); }) }
); } /** * Memoized export of the generic BarDataTable. * Cast is required because memo loses the generic type parameter. */ export const BarDataTable = memo( BarDataTableInner ) as typeof BarDataTableInner; export default BarDataTable;