Une barre de puces de filtre qui défile, en React

La rangée de puces sous chaque barre de recherche — sujets YouTube, filtres de boutique, sélecteurs de tags — est un conteneur de défilement sur une seule ligne rempli de boutons à bascule. Les 10 % difficiles concernent ce qui se passe aux bords : de nouvelles puces apparaissant hors écran, des glissers qui ne doivent rien basculer, et des flèches qui savent quand elles sont inutiles.

Ajoutez un filtre — la rangée fait défiler la nouvelle puce jusqu'à la vue toute seule.

Les cas limites sont la fonctionnalité

Toute rangée flex avec overflow-x: auto défile. Une barre de puces se distingue sur les détails :

L'état reste entre vos mains

La bibliothèque défile ; elle ne possède pas la sélection. Les puces sont vos boutons — aria-pressed pour les bascules multi-sélection, un état ordinaire pour la sélection unique — et le menu a seulement besoin que chacune porte un itemId. Cela signifie que l'état des puces se compose avec ce que vous avez déjà : paramètres d'URL, une bibliothèque de formulaires, un modèle de filtre piloté par serveur. Supprimer une puce, c'est retirer un élément ; l'animer en sortie est l'exemple d'animation d'éléments.

Mobile : un avertissement sur le défilement du corps de page

Sur les écrans tactiles, un balayage horizontal à l'intérieur de la barre peut entraîner la page de côté avec lui sur certains navigateurs. Si vous constatez cela, l'exemple d'empêcher le défilement du corps montre le touch-action et le confinement de surdéfilement pour le verrouiller — CSS uniquement, aucune bibliothèque de gestes.

Le motif, minimal

Les puces sont des boutons à bascule avec un itemId ; une ref vers l'API du menu fait défiler une puce nouvellement ajoutée jusqu'à la vue.

ChipBar.tsx
function ChipBar({ options }: { options: string[] }) {
  const apiRef = React.useRef<publicApiType>(null);
  const [selected, setSelected] = React.useState<string[]>([]);

  const toggle = (id: string) =>
    setSelected((cur) =>
      cur.includes(id) ? cur.filter((c) => c !== id) : [...cur, id],
    );

  // A chip appended off-screen scrolls itself into view.
  const addChip = (id: string) => {
    toggle(id);
    requestAnimationFrame(() => {
      const el = apiRef.current?.getItemElementById(id);
      if (el) apiRef.current?.scrollToItem(el, 'smooth', 'end');
    });
  };

  return (
    <ScrollMenu apiRef={apiRef}>
      {options.map((id) => (
        <Chip itemId={id} key={id} pressed={selected.includes(id)}
          onToggle={() => toggle(id)} />
      ))}
    </ScrollMenu>
  );
}

Ou installez-le comme composant shadcn

L'élément de registre chip-bar fournit ceci comme composant contrôlé — options, selected, onSelectedChange — stylé avec Tailwind dans votre components/ui/ :

shadcn
npx shadcn@latest add https://react-horizontal-scrolling-menu.dev/r/chip-bar.json

Exemples associés

Tous les exemples (21)