npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

weabee-custom-table

v1.2.4

Published

A customizable React table component with sorting, pagination, and selection features.

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 renderers personnalisé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éfaut true. |

    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()}
    />;