veza/apps/web/src/components/ui/table.tsx

227 lines
5.1 KiB
TypeScript

import * as React from 'react';
import { cn } from '@/lib/utils';
/**
* Table - Composant de tableau avec design system Kodo
*
* Composant de tableau pour afficher des données structurées.
* Utilise le design system Kodo avec des styles cohérents et support pour le scroll.
*
* @example
* ```tsx
* <Table>
* <TableHeader>
* <TableRow>
* <TableHead>Nom</TableHead>
* <TableHead>Email</TableHead>
* </TableRow>
* </TableHeader>
* <TableBody>
* <TableRow>
* <TableCell>John Doe</TableCell>
* <TableCell>john@example.com</TableCell>
* </TableRow>
* </TableBody>
* </Table>
* ```
*
* @component
* @param {React.HTMLAttributes<HTMLTableElement>} props - Propriétés HTML standard de table
* @returns {JSX.Element} Élément div contenant un tableau stylisé avec scroll
*/
// CRITIQUE FIX #40: Interface étendue pour supporter aria-label et caption
export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
/**
* Label accessible pour le tableau (aria-label)
* Si non fourni, aria-label sera undefined et il faudra utiliser TableCaption
*/
'aria-label'?: string;
/**
* ID d'un élément qui décrit le tableau (aria-labelledby)
*/
'aria-labelledby'?: string;
}
const Table = React.forwardRef<HTMLTableElement, TableProps>(
(
{
className,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
...props
},
ref,
) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn('w-full caption-bottom text-sm', className)}
// CRITIQUE FIX #40: Ajouter aria-label si fourni pour l'accessibilité
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
{...props}
/>
</div>
),
);
Table.displayName = 'Table';
/**
* TableHeader - En-tête du tableau
*
* Conteneur pour les lignes d'en-tête du tableau.
*
* @component
*/
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead
ref={ref}
className={cn('[&_tr]:border-b border-kodo-steel', className)}
{...props}
/>
));
TableHeader.displayName = 'TableHeader';
/**
* TableBody - Corps du tableau
*
* Conteneur pour les lignes de données du tableau.
*
* @component
*/
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn('[&_tr:last-child]:border-0', className)}
{...props}
/>
));
TableBody.displayName = 'TableBody';
/**
* TableFooter - Pied du tableau
*
* Conteneur pour les lignes de pied du tableau (totaux, etc.).
*
* @component
*/
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
'border-t border-kodo-steel bg-kodo-steel/30 font-medium [&>tr]:last:border-b-0',
className,
)}
{...props}
/>
));
TableFooter.displayName = 'TableFooter';
/**
* TableRow - Ligne du tableau
*
* Ligne individuelle du tableau avec effet hover et support pour l'état sélectionné.
*
* @component
*/
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
'border-b border-kodo-steel transition-colors hover:bg-white/5 data-[state=selected]:bg-white/10',
className,
)}
{...props}
/>
));
TableRow.displayName = 'TableRow';
/**
* TableHead - Cellule d'en-tête
*
* Cellule d'en-tête du tableau avec style en gras et uppercase.
*
* @component
*/
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
'h-12 px-4 text-left align-middle font-bold text-kodo-content-dim uppercase tracking-wider [&:has([role=checkbox])]:pr-0',
className,
)}
{...props}
/>
));
TableHead.displayName = 'TableHead';
/**
* TableCell - Cellule de données
*
* Cellule de données du tableau avec padding et alignement.
*
* @component
*/
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
'p-4 align-middle [&:has([role=checkbox])]:pr-0 text-kodo-text-main',
className,
)}
{...props}
/>
));
TableCell.displayName = 'TableCell';
/**
* TableCaption - Légende du tableau
*
* Légende optionnelle affichée sous le tableau.
*
* @component
*/
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn('mt-4 text-sm text-kodo-content-dim', className)}
{...props}
/>
));
TableCaption.displayName = 'TableCaption';
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
};