spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1import type { ReactNode } from 'react';2import { t } from '@/i18n';3import { cn } from '@/lib/cn';45export interface DataTableColumn<T> {6 key: string;7 header: ReactNode;8 cell: (row: T) => ReactNode;9 numeric?: boolean;10 /** Hide on the definition-list (mobile) layout. */11 hideOnMobile?: boolean;12 className?: string;13}1415/**16 * Data table that becomes a definition list on < sm (no horizontal overflow). Numeric columns are17 * right-aligned and tabular. Pass `rowKey` for stable keys and `caption` for AT.18 */19export function DataTable<T>({ rows, columns, rowKey, caption, className, dense }: { rows: T[]; columns: DataTableColumn<T>[]; rowKey: (row: T) => string; caption?: string; className?: string; dense?: boolean }) {20 return (21 <div className={cn('min-w-0', className)}>22 <table className="hidden w-full border-collapse text-sm sm:table">23 {caption ? <caption className="sr-only">{caption}</caption> : null}24 <thead>25 <tr className="border-b border-rule text-left text-xs text-ink-3">26 {columns.map((c) => (27 <th key={c.key} scope="col" className={cn('py-2 pr-3 font-medium', c.numeric && 'text-right', c.className)}>28 {c.header}29 </th>30 ))}31 </tr>32 </thead>33 <tbody className="divide-y divide-rule">34 {rows.map((r) => (35 <tr key={rowKey(r)} className="hover:bg-surface-2/60">36 {columns.map((c) => (37 <td key={c.key} className={cn(dense ? 'py-1.5' : 'py-2.5', 'pr-3 align-top', c.numeric && 'tnum text-right', c.className)}>38 {c.cell(r)}39 </td>40 ))}41 </tr>42 ))}43 </tbody>44 </table>45 <ul className="divide-y divide-rule sm:hidden" aria-label={caption ?? t('table.definitionList')}>46 {rows.map((r) => (47 <li key={rowKey(r)} className="py-3">48 <dl className="grid grid-cols-[minmax(0,1fr)_auto] gap-x-4 gap-y-1">49 {columns50 .filter((c) => !c.hideOnMobile)51 .map((c, i) => (52 <div key={c.key} className={cn('contents', i === 0 && 'font-medium')}>53 <dt className={cn('text-xs text-ink-3', i === 0 && 'sr-only')}>{c.header}</dt>54 <dd className={cn('min-w-0 text-sm', c.numeric && 'tnum text-right', i === 0 && 'col-span-2 text-base')}>{c.cell(r)}</dd>55 </div>56 ))}57 </dl>58 </li>59 ))}60 </ul>61 </div>62 );63}64