0558d8c58a
- Le nombre d'onglets affichés en mode fenêtré s'adapte automatiquement à la largeur réelle (ResizeObserver + ligne de mesure cachée). Les chevrons restent fixés aux bords ; le nombre est choisi pour que les onglets tiennent (pas de chevauchement ni de rognage). Calcul isolé en fonction pure testable (tabFit.ts, basée sur les vraies largeurs). maxVisibleTabs devient un plafond optionnel. - BREAKING : suppression de la prop maxWidth (la barre prend toute la largeur). - Survol d'un onglet inactif : même style que l'actif (texte m-primary + barre). - Playground : bac à sable interactif (nb onglets, plafond, icônes, labels longs, cadre redimensionnable) pour tester tous les cas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
// Calcule combien d'onglets afficher pour qu'ils tiennent dans la largeur dispo,
|
|
// en gardant la structure « flèches fixes aux bords » : le nombre est choisi pour
|
|
// que les onglets visibles tiennent → pas de débordement sur les flèches, pas de
|
|
// rognage, barre d'onglet actif intacte.
|
|
//
|
|
// On additionne les VRAIES largeurs d'onglets (pas la pire), donc le résultat
|
|
// n'est pas sur-conservateur (évite de tomber à 1 onglet inutilement).
|
|
//
|
|
// Fonction pure → testable sans DOM. Sans layout (SSR / jsdom : largeurs à 0),
|
|
// on retombe sur le plafond `maxVisibleTabs` (ou tous les onglets).
|
|
export interface TabFitInput {
|
|
count: number // nombre total d'onglets
|
|
containerWidth: number // largeur dispo mesurée (0 si inconnue)
|
|
tabWidths: number[] // largeur mesurée de chaque onglet (vide si inconnu)
|
|
gap: number // espace entre onglets (px)
|
|
chevronReserve: number // place des chevrons + marges quand fenêtré (px)
|
|
maxVisibleTabs?: number // plafond optionnel imposé par le consommateur
|
|
}
|
|
|
|
export function computeVisibleCount(input: TabFitInput): number {
|
|
const {count, containerWidth, tabWidths, gap, chevronReserve, maxVisibleTabs} = input
|
|
|
|
// Pas d'info de layout : on respecte le plafond explicite, sinon tout afficher.
|
|
if (containerWidth <= 0 || tabWidths.length === 0) {
|
|
return maxVisibleTabs != null ? Math.min(maxVisibleTabs, count) : count
|
|
}
|
|
|
|
const fullAvail = containerWidth
|
|
const total = tabWidths.reduce((s, w) => s + w, 0) + gap * Math.max(0, count - 1)
|
|
|
|
let fit: number
|
|
if (total <= fullAvail) {
|
|
fit = count // tout tient, pas de chevrons
|
|
}
|
|
else {
|
|
const avail = fullAvail - chevronReserve
|
|
let used = 0
|
|
let n = 0
|
|
for (const w of tabWidths) {
|
|
const add = w + (n > 0 ? gap : 0)
|
|
if (used + add > avail) break
|
|
used += add
|
|
n++
|
|
}
|
|
fit = Math.max(1, n)
|
|
}
|
|
|
|
return maxVisibleTabs != null ? Math.min(maxVisibleTabs, fit) : fit
|
|
}
|