weabee-custom-table
v1.2.4
Published
A customizable React table component with sorting, pagination, and selection features.
Maintainers
Readme
CustomTable
Composant React générique et personnalisable pour tableaux avec gestion complète du contexte, filtres, tri, pagination, sélection et actions (ligne, globales, en-tête).
Fonctionnalités principales
- ✅ Contexte React global (
TableProvider) pour l'état et les actions - 📊 Colonnes dynamiques avec
rendererspersonnalisés (optionnel) - 🔍 Filtres configurables : texte, liste, booléen, date (optionnel)
- 📄 Pagination intégrée avec personnalisation (optionnel)
- 🎨 Thème personnalisable par fusion CSS (optionnel)
- 🧩 Actions ligne / globales / en-tête (optionnel)
- 📦 Export JSON, CSV, Excel (optionnel)
- 🔁 Callback sur changement de sélection
- 🌍 Traductions personnalisables
Installation
npm install weabee-custom-table
# ou
yarn add weabee-custom-table
# ou
bun add weabee-custom-table
Usage
import CustomTable from "weabee-custom-table";
<CustomTable
initialData={data}
columns={columns}
customOptions={customOptions}
baseColor="#34D399"
customTheme={customTheme}
globalActions={getGlobalActions(users, handleAction)}
headerActions={getHeaderActions(router)}
lineActions={getLineActions(handleAction)}
renderers={getRenderers()}
/>;Sommaire
How it works
initialData (obligatoire) :
Jeu de données de base à afficher dans le tableau. Chaque entrée doit :
contenir un champ id unique (string ou number),
être une structure plate (pas d’objets imbriqués ou de tableaux internes).
Exemple :
const data: User[] = [ { id: "1", name: "Alice", role: "ADMIN", active: true, createdAt: new Date("2024-05-01"), }, { id: "2", name: "Bob", role: "USER", active: false, createdAt: new Date("2024-06-15"), }, { id: "3", name: "Charlie", role: "USER", active: true, createdAt: new Date("2024-07-01"), }, ];columns (obligatoire) :
Liste des colonnes du tableau, définies par le type ColumnDefinition. Chaque colonne correspond à un champ de initialData et peut inclure :
field (obligatoire) : nom du champ de la donnée (clé de l’objet).
label (optionnel) : libellé affiché dans l’en-tête. Si non défini, field sera utilisé.
sortable (optionnel) : active le tri sur cette colonne.
filter (optionnel) : définit un filtre personnalisable pour cette colonne.
🔍 Définition d’un filtre (filter)
Le filtre est un objet de type FilterDefinition, qui contient :
type : type de filtre parmi :
"search" : champ texte libre
"list" : liste de choix (string ou number)
"boolean" : vrai/faux
"dateRange" : période entre deux dates
multiple (optionnel) : pour "list", autorise la sélection multiple.
filterFunction (optionnel) : fonction de filtrage personnalisée : (rowValue: T[K], filterValue: FilterValue) => boolean
Exemple complet :
const columns: ColumnDefinition<User>[] = [ { field: "name", label: "Nom", sortable: true, filter: { type: "search", // filtre texte libre }, }, { field: "role", label: "Rôle", filter: { type: "list", // filtre liste avec sélection multiple multiple: true, filterFunction: (rowValue, selectedRoles) => Array.isArray(selectedRoles) ? selectedRoles.includes(rowValue) : true, }, }, { field: "active", label: "Actif", filter: { type: "boolean", // filtre booléen vrai/faux }, }, { field: "createdAt", label: "Date de création", sortable: true, filter: { type: "dateRange", // filtre plage de dates }, }, ];
customOptions (optionnel) :
Objet de configuration optionnel pour personnaliser certains comportements et textes du tableau.
| Propriété | Type | Description | | -------------- | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | |
translations|{ resultLines: string; selectedLines: string; numberOfLines: string; }(optionnel) | Textes personnalisés utilisés dans les messages du tableau. Permet d’adapter le libellé des compteurs, sélections, etc. Exemple :"5 lignes affichées". | |export|ExportOptions(optionnel) | Active et configure les options d’export des données du tableau. | |pagination|boolean(optionnel) | Active (true) ou désactive (false) la pagination. Par défauttrue. |ExportOptions : | Propriété | Type | Description | | --------- | --------- | -------------------------------- | |
json|boolean| Active l’export au format JSON. | |csv|boolean| Active l’export au format CSV. | |excel|boolean| Active l’export au format Excel. |Interface associée :
interface TableOptions { translations?: { resultLines: string; selectedLines: string; numberOfLines: string; }; export?: ExportOptions; pagination?: boolean; } interface ExportOptions { json: boolean; csv: boolean; excel: boolean; }Exemple complet :
const customOptions = { pagination: true, export: { json: true, csv: true, excel: true, }, translations: { resultLines: "Showing {start} to {end} of {total} results", selectedLines: "({selected} selected line{selectedPlural})", numberOfLines: "Rows per page:", }, };Informations :
Ces variables permettent de personnaliser les textes affichés dans le résumé du tableau, généralement situé en bas (ou en haut), indiquant les lignes actuellement visibles, le nombre total d’éléments, et ceux sélectionnés.
| Clé | Description | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | |
{start}| Numéro de la première ligne visible dans la page courante (ex. : 11 si on est en page 2 avec 10 éléments par page). | |{end}| Numéro de la dernière ligne visible dans la page courante (ex. : 20 pour une page 2 avec 10 éléments). | |{total}| Nombre total de lignes (avant ou après filtrage selon l’implémentation). | |{selected}| Nombre d’éléments actuellement sélectionnés par l’utilisateur. | |{selectedPlural}| Version plurielle du texte concernant les éléments sélectionnés (utile pour les langues avec accords, ex. : "éléments sélectionnés"). |baseColor (optionnel) :
Couleur principale définie par l’utilisateur, servant de base pour générer automatiquement une palette cohérente et déclinée selon les différentes zones du composant.
Exemple :
<CustomTable initialData={myData} columns={myColumns} baseColor="#1e90ff" // Bleu Dodger Blue />customTheme (optionnel) :
Objet de thème entièrement personnalisable respectant l’interface TableTheme. Permet de définir manuellement chaque couleur, style de texte, hauteur de ligne ou apparence des boutons. Cette approche offre un contrôle total sur le rendu visuel du tableau, sans dépendre du générateur automatique basé sur une baseColor.
Interface associée :
interface TableTheme { fontFamily: string; borderColor: string; headerBgColor: string; headerTextColor: string; rowBgColor: string; rowTextColor: string; rowHoverBgColor: string; rowHeight: string; backgroundColor: string; checkboxColor: string; actionBar: { buttonColor: string; buttonTextColor: string; buttonHoverColor: string; }; headerBar: { buttonColor: string; buttonTextColor: string; buttonHoverColor: string; }; pagination: { buttonColor: string; buttonTextColor: string; buttonHoverColor: string; buttonBorder: boolean; buttonBorderColor: string; buttonArrowColor: string; buttonArrowTextColor: string; buttonArrowHoverColor: string; buttonArrowBorder: boolean; buttonArrowBorderColor: string; activeButtonColor: string; activeButtonTextColor: string; numberLineTextColor: string; numberLineinputTextColor: string; }; }Exemple complet
Le thème ci-dessous définit manuellement l'apparence du tableau en respectant l'interface
TableTheme. Il permet d'ajuster précisément chaque propriété, telles que les couleurs, la police et les styles des zones du tableau.const myCustomTheme = { fontFamily: "'Inter', sans-serif", borderColor: "#e0e0e0", headerBgColor: "#2c3e50", headerTextColor: "#ffffff", rowBgColor: "#ffffff", rowTextColor: "#333333", rowHoverBgColor: "#f2f2f2", rowHeight: "40px", backgroundColor: "#f9f9f9", checkboxColor: "#3498db", actionBar: { buttonColor: "#3498db", buttonTextColor: "#ffffff", buttonHoverColor: "#2980b9", }, headerBar: { buttonColor: "#34495e", buttonTextColor: "#ecf0f1", buttonHoverColor: "#2c3e50", }, pagination: { buttonColor: "#ecf0f1", buttonTextColor: "#2c3e50", buttonHoverColor: "#bdc3c7", buttonBorder: true, buttonBorderColor: "#95a5a6", buttonArrowColor: "transparent", buttonArrowTextColor: "#2c3e50", buttonArrowHoverColor: "#dfe6e9", buttonArrowBorder: false, buttonArrowBorderColor: "#bdc3c7", activeButtonColor: "#3498db", activeButtonTextColor: "#ffffff", numberLineTextColor: "#7f8c8d", numberLineinputTextColor: "#2c3e50", }, }; <CustomTable initialData={rows} columns={columns} customTheme={myCustomTheme} />;Ce thème utilise une palette bleue et grise afin d'assurer un contraste optimal et une lisibilité améliorée, tout en offrant une esthétique moderne et cohérente pour le tableau.
globalActions (optionnel) :
Ensemble d’actions personnalisées déclenchées depuis la barre d’actions globale, appliquées à une sélection multiple de lignes. Chaque action est définie sous forme de paire clé/valeur, où la clé correspond à un identifiant unique de l’action, et la valeur à un objet GlobalAction décrivant son comportement.
Permet notamment de gérer des opérations comme la suppression, l’export ou le regroupement sur plusieurs éléments sélectionnés.
Type :
Partial<Record<string, HeaderAction>>;Interface associée :
type GlobalAction = { label: string; icon?: ReactNode; disabled?: boolean; onClick: (row: T) => void | Promise<void>; isDisabled?: (row: T) => boolean; tooltip?: string; hidden?: (row: T) => boolean; };Exemple complet
function getGlobalActions( users: UserWithProfileFlattened[], handleAction: ( ids: string | string[], actionType: "delete" | "archive" | "edit" ) => Promise<void> ) { return { sendMail: { label: "Envoyer un mail", icon: <HiOutlineMail size={20} />, tooltip: "Envoyer un mail aux utilisateurs sélectionnés", isDisabled: (ids: string[]) => ids.length === 0, onClick: (ids: string[]) => { const emails = users .filter((user) => ids.includes(user.id)) .map((user) => user.email) .filter(Boolean); if (emails.length === 0) { alert("Aucun email valide pour les éléments sélectionnés."); return; } const subject = encodeURIComponent("Sujet de l'email"); const body = encodeURIComponent("Bonjour à tous, ..."); window.location.href = `mailto:?bcc=${emails.join( "," )}&subject=${subject}&body=${body}`; }, }, delete: { label: "Supprimer", icon: <FaTrash />, onClick: async (ids: string[]) => { await handleAction(ids, "delete"); }, isDisabled: (ids: string[]) => ids.length === 0, tooltip: "Supprimer les éléments sélectionnés", }, archive: { label: "Archiver", onClick: (ids: string[]) => { handleAction(ids, "archive"); }, isDisabled: (ids: string[]) => ids.length === 0, }, }; } <CustomTable initialData={rows} columns={columns} globalActions={getGlobalActions(users, handleAction)} />;headerActions (optionnel) :
Définit une collection d’actions positionnées dans l’en-tête du tableau, indépendantes de la sélection ou du contenu des lignes. Ces actions peuvent par exemple servir à ajouter un élément ou déclencher une opération externe.
Chaque action est identifiée par une clé unique et suit le contrat HeaderAction.
Type :
Partial<Record<string, HeaderAction>>;Interface associée :
type HeaderAction = { label: string; icon?: ReactNode; disabled?: boolean; onClick: (row: T) => void | Promise<void>; isDisabled?: (row: T) => boolean; tooltip?: string; hidden?: (row: T) => boolean; style?: React.CSSProperties; };Exemple complet
function getHeaderActions(router: ReturnType<typeof useRouter>) { return { create: { label: "Nouvel utilisateur", icon: <FaPlus size={18} />, style: { backgroundColor: "red", color: "black" }, onClick: () => { router.push("/dashboard/users/create"); }, tooltip: "Créer un nouvel utilisateur", }, }; } <CustomTable initialData={rows} columns={columns} headerActions={getHeaderActions(router)} />;
lineActions (optionnel) :
Définit un ensemble d’actions spécifiques à chaque ligne du tableau. Ces actions permettent d’interagir directement avec une entrée, comme modifier, supprimer, archiver, etc.
Chaque action est identifiée par une clé unique et suit le contrat LineAction.
Type :
Partial<Record<string, LineAction<T>>>;Interface associée :
type LineAction<T> = { label: string; icon?: ReactNode; disabled?: boolean; onClick: (row: T) => void | Promise<void>; isDisabled?: (row: T) => boolean; tooltip?: string; hidden?: (row: T) => boolean; render?: (row: T) => ReactNode; // Surcharge complète possible style?: React.CSSProperties; };Exemple complet
import { Pencil, Trash2 } from "lucide-react"; import type { LineActions } from "@/components/customTable/types"; import type { Client } from "@/types/db"; const lineActions: LineActions<Client> = { edit: { label: "Modifier", icon: <Pencil size={16} />, tooltip: "Modifier ce client", onClick: (client) => { openEditModal(client.id); }, }, delete: { label: "Supprimer", icon: <Trash2 size={16} />, tooltip: "Supprimer ce client", onClick: async (client) => { const confirmed = await confirmDelete(client.name); if (confirmed) { await deleteClient(client.id); } }, isDisabled: (client) => client.role === "ADMIN", hidden: (client) => client.archived === true, }, };Renderers (optionnel)
Permet de personnaliser le rendu des cellules pour certaines colonnes spécifiques. Chaque clé correspond à un champ de la donnée (keyof T) et sa valeur est une fonction de rendu qui reçoit la valeur brute et la ligne complète.
Ce mécanisme facilite la transformation des données (formats de date, badges, icônes, etc.) sans modifier la structure du tableau.
Type :
Partial<{ [K in keyof T]: (value: T[K], row: T) => ReactNode }>;Exemple complet
import { FiCheckCircle, FiPauseCircle, FiXCircle, FiClock, } from "react-icons/fi"; function getRenderers() { return { role: (value: unknown) => ( <span className="badge">{value?.toString().toLowerCase() ?? "—"}</span> ), createdAt: (value: string | Date) => { // Exemple d'une fonction utilitaire formatDateFr qui retourne une date formatée FR return formatDateFr(value); }, status: (value: string) => { switch (value) { case "ACTIVE": return ( <span className="flex badge badge-success text-green-500 items-center"> <FiCheckCircle className="mr-1" /> Actif </span> ); case "INACTIVE": return ( <span className="flex badge badge-warning text-blue-600 items-center"> <FiPauseCircle className="mr-1" /> Inactif </span> ); case "BLOCKED": return ( <span className="flex badge badge-error text-red-600 items-center"> <FiXCircle className="mr-1" /> Bloqué </span> ); case "PENDING": return ( <span className="flex badge badge-warning text-yellow-400 items-center"> <FiClock className="mr-1" /> En attente </span> ); default: return <span className="badge">—</span>; } }, }; } <CustomTable initialData={rows} columns={columns} renderers={getRenderers()} />;
