- 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>
- Survol : highlight pleine largeur porté par le <li> (fond m-primary/10 +
texte m-primary + semi-bold), espacement pt-1 pb-1. Couleur de base text-black
sur le <li> et le <a> n'impose plus sa couleur (héritage) : sinon, sur les
bandes pt-1/pb-1 hors du <a>, le fond passait bleu mais le texte restait noir.
- Lien actif : texte m-primary + semi-bold, sans fond (active-class avec
!important car appliqué hors twMerge).
- Pas de transition sur le hover (texte + fond basculent ensemble, sans délai).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Les liens passent le texte en m-primary au survol (hover:text-m-primary),
en plus du fond hover:bg-m-surface existant. Inclut l'alignement du test de
largeur sur la valeur actuelle (w-[232px]).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
L'icône calendrier (overlay absolu) interceptait le clic sans le traiter et ne
le laissait pas retomber sur le @click de l'input. Ajout d'un handler onFieldClick
sur l'icône (+ cursor-pointer). Couvre Date, DateTime, DateRange, DateWeek via le
CalendarField partagé. La croix d'effacement garde son comportement (efface, n'ouvre pas).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le bornage par tokens ne contraignait que le 1er chiffre (positionnel, sans
mémoire du chiffre précédent) : 33 (jour) ou 19 (mois) restaient tapables.
Remplacé par un preProcess maska qui valide chaque champ progressivement : un
chiffre n'est accepté que s'il existe encore une complétion dans [min, max].
Borne donc le 1er ET le 2e chiffre ; les impossibilités calendaires fines
(31/02, 29/02 non bissextile, hors min/max) restent captées par la validation.
Tests d'intégration : 32/13 (désormais non tapable) remplacé par 31/02 comme
date « champs valides mais inexistante » ; garde sur l'exemple métier 33/19.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le DateTime partage CalendarField, donc buildBoundedMask bornait déjà sa saisie
(heure 0-2, minute 0-5) — couvert par maskTemplate.test.ts. Ce test rend la
garantie explicite côté composant : 99/99/9999 99:99 ne peut pas être tapé et
n'émet aucun datetime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le masque maska n'imposait que la forme (##/##/####), donc 99/99/9999 était
saisissable puis rejeté a posteriori par la validation. Le métier veut que ce
soit impossible à taper.
buildBoundedMask(template) borne le premier chiffre de chaque champ (jour 0-3,
mois 0-1, heure 0-2, minute 0-5) ; il distingue le mois des minutes (même lettre
M) selon la présence d'heures. Les impossibilités fines (31/02, 29/02 non
bissextile, hors min/max) restent captées par la validation, en filet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>