Forme DS

Composants

TabNav

Le rail de filtres piloté par l'URL des vues Forme (factures, clients…). Chaque onglet est un lien portant un href complet ; la sélection suit active, sans état interne. Un compteur optionnel s'affiche en Badge.

Modèle RSC / URL : TabList.onChange est un no-op, value = active. En rendu serveur, tu passes active depuis le query param. Dans l'aperçu ci-dessous, un linkComponent intercepte le clic (useState) pour rendre la démo manipulable.

Rail de filtres

import { TabNav } from "@forme-ch/ui/controls";

<TabNav
  active={statut ?? "toutes"}
  linkComponent={Link}
  items={[
    { key: "toutes", label: "Toutes", count: 3303, href: "/admin/factures" },
    { key: "brouillon", label: "Brouillons", count: 1, href: "/admin/factures?statut=brouillon" },
    { key: "payees", label: "Payées", count: 2044, href: "/admin/factures?statut=payees" },
    { key: "impayees", label: "Impayées", count: null, href: "/admin/factures?statut=impayees" },
  ]}
/>

Pleine largeur, sans filet

layout="fill" répartit les onglets ; hasDivider= retire le filet inférieur.

<TabNav
  active={onglet}
  layout="fill"
  hasDivider={false}
  linkComponent={Link}
  items={[
    { key: "resume", label: "Résumé", href: "#resume" },
    { key: "activite", label: "Activité", href: "#activite" },
    { key: "documents", label: "Documents", href: "#documents" },
  ]}
/>

Props

PropTypeDéfautDescription
items * TabNavItem[] — Onglets : { key, label, count?, href, endContent? }.
active * string — Clé de l'onglet actif — doit matcher un item.key.
ariaLabel string "Filtres" Libellé a11y du rail d'onglets.
hasDivider boolean true Filet sous les onglets (rail de filtres Forme).
size "sm" | "md" | "lg" "md" Taille des cibles (alignée Button / TextField).
layout "hug" | "fill" "hug" Onglets ajustés au contenu (« hug ») ou répartis (« fill »).
linkComponent ElementType <a> natif Composant de lien (ex. next/link). Doit accepter href, className, style, children.

TabNavItem

PropTypeDéfautDescription
key * string — Clé de l'onglet (matche le query param ou la valeur « défaut »).
label * string — Libellé affiché.
count number | null — Compteur optionnel — omis ou null = pas de badge.
href * string — URL complète. L'appelant préserve les autres params.
endContent ReactNode — Contenu de fin custom (remplace le badge de compteur).

Accessibilité

Renseigne ariaLabel pour nommer le rail (« Filtres factures »). Comme les onglets sont de vrais liens, la navigation clavier et le retour arrière du navigateur fonctionnent nativement. Le count est formaté fr-CH et rendu en Badge neutre ; un endContent le remplace si fourni. Le rail scrolle horizontalement sur mobile.

Do & Don't

✓ À faire

Piloter la sélection par l'URL et fournir un href complet par onglet. Passer next/link en linkComponent.

✕ À éviter

Gérer l'onglet actif par un état local en production (c'est un modèle URL). Confondre avec des onglets de contenu (panneaux) — ici ce sont des filtres/navigation.