SelectSearch
Select custom pour filtrer par catégorie. Affiche Toutes les {label}s + la liste d'options, gère le clear X, le clic hors zone et la touche Escape. Re-sélectionner la même option désélectionne.
Démo en direct — filtre simple
Sélectionnez un rôle — le bouton X efface la sélection, Escape ou clic hors zone ferme.
Sélection : aucune
Démo en direct — pré-sélectionné
Avec une valeur initiale, le composant affiche directement l'option.
Valeur : Utilisateur
Démo en direct — combiné InputSearch + SelectSearch + Table
Pattern fréquent : barre de filtres au-dessus d'un tableau.
6 résultat(s) — recherche: "—" — rôle: tous
Utilisateurs: 6
| Nom | Rôle |
|---|---|
Alice Martin | Administrateur |
Bernard Dupont | Utilisateur |
Camille Petit | Modérateur |
David Leroy | Utilisateur |
Émilie Moreau | Administrateur |
Lignes par page :
Page 1 sur 2
Combinez les deux composants pour un filtrage multi-critères — chaque filtre reste contrôlé par son state.
Code source complet — copier-coller
Exemple complet avec filtres + Table.
"use client";
import { useState, useMemo } from "react";
import { InputSearch, SelectSearch, Table } from "@ramses1er/cbk-toolkit/client";
import type { SelectSearchOption, Column } from "@ramses1er/cbk-toolkit/client";
const ROLE_OPTIONS: SelectSearchOption[] = [
{ id: "admin", name: "Administrateur" },
{ id: "user", name: "Utilisateur" },
{ id: "moderator", name: "Modérateur" },
];
type User = { id: number; fullname: string; role: string };
export default function UsersPage() {
const [search, setSearch] = useState("");
const [role, setRole] = useState<SelectSearchOption | null>(null);
const filtered = useMemo(() => {
return users.filter((u) => {
const matchSearch = !search || u.fullname.toLowerCase().includes(search.toLowerCase());
const matchRole = !role || u.role === role.id;
return matchSearch && matchRole;
});
}, [search, role]);
const columns: Column<User>[] = [
{ header: "Nom", accessor: "fullname" },
{ header: "Rôle", render: (row) => row.role },
];
return (
<div className="space-y-4">
<div className="flex gap-3">
<InputSearch value={search} onChange={setSearch} placeholder="Rechercher..." />
<SelectSearch label="rôle" options={ROLE_OPTIONS} selectedOption={role} onSelect={setRole} />
</div>
<Table data={filtered} columns={columns} label="Utilisateurs" />
</div>
);
}Props
| Prop | Type | Défaut | Description |
|---|---|---|---|
| label | string | requis | Utilisé pour "Toutes les {label}s" et placeholder |
| options | SelectSearchOption[] | requis | Liste des options {id, name} |
| selectedOption | SelectSearchOption | null | null | Option sélectionnée (contrôlé) |
| onSelect | (option: SelectSearchOption | null) => void | requis | Callback — null = désélection / "Toutes" |
| placeholder | string | "Filtrer par {label}" | Texte quand aucune sélection |
| className | string | — | Classes pour le conteneur |
Type — SelectSearchOption
type SelectSearchOption = {
id: string;
name: string;
};Comportement
- Clic sur le bouton ouvre/ferme la liste
- Clic hors zone ou touche Escape ferme
- Option "Toutes les {label}s" en tête — désélectionne
- Re-cliquer sur la même option désélectionne (toggle)
- Si aucune sélection, bouton X masqué ; sinon X visible à droite du Chevron
- Si options vide → "Aucune option"