feat : sélecteur d'année dans le calendrier (3ᵉ niveau) (#83)

## Sélecteur d'année dans le calendrier (3ᵉ niveau de navigation)

Ajoute un 3ᵉ niveau de navigation à la famille de composants date, et corrige le bornage min/max du sélecteur de mois.

### Comportement
- Clic sur le champ → calendrier (vue **jours**)
- Clic sur l'en-tête → **sélecteur de mois**
- **Re-clic sur l'en-tête → sélecteur d'année** (grille de 12 ans, chevrons paginant par pas de 12 ans, fenêtre centrée sur l'année courante − 5)
- Clic sur une année → retour au sélecteur de mois ; clic sur un mois → retour à la grille de jours
- Les props `min`/`max` **grisent les mois ET les années** hors plage (corrige l'asymétrie : le `MonthPicker` affichait jusqu'ici tous les mois)

En-tête contextuel : « Mai 2026 » (jours) / « 2026 » (mois) / « 2020 – 2031 » (années).

### Périmètre
- Shell partagé `internal/CalendarField.vue` → bénéficie aux 4 composants publics `Date`, `DateRange`, `DateTime`, `DateWeek`
- **Aucune API publique modifiée**
- Nouveau composant `internal/YearPicker.vue` (calqué sur `MonthPicker`)
- Helpers purs `isMonthInRange` / `isYearInRange` (comparaison par préfixe ISO, bornes inclusives)
- State machine `viewMode` à 3 niveaux (`useCalendarPopover` / `useCalendarView`)

### Tests
- Suite date **246/246 verte**, ESLint propre
- Unitaires : helpers, `YearPicker`, `MonthPicker` (grisage), composables (pagination ±12, recentrage, `selectYear`)
- e2e `Date.test.ts` : flux complet jours→mois→années→mois→jours + grisage min/max

### Process
Développé en brainstorming → spec → plan → exécution TDD (un commit par étape). Spec et plan inclus sous `docs/superpowers/`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #83
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #83.
This commit is contained in:
2026-06-22 09:28:55 +00:00
committed by Autin
parent b0f060f909
commit 41010060ff
21 changed files with 1443 additions and 24 deletions
+1
View File
@@ -56,6 +56,7 @@ Liste des évolutions de la librairie Malio layer UI
* [#MUI-43] CalendarField : la croix d'effacement réinitialise désormais la saisie clavier même après une date invalide (le `v-model` restant `null`, le champ se vidait pas).
* [#MUI-44] MalioDate / MalioDateTime : event `update:rawValue` (string) exposant la saisie brute sur un canal séparé pour la validation back-autoritative — saisie invalide (non parsable ou hors `min`/`max`) → texte trimmé tel que tapé, saisie valide/vide + clear + sélection au calendrier → `''`. `modelValue` reste `string` ISO `| null` (la saisie invalide n'y transite jamais) ; le parent construit son payload via `valid ? modelValue : rawValue`.
* [#MUI-45] MalioDate : prop `markedDates` (`Record<"YYYY-MM-DD", 'success' | 'danger'>`) appliquant un fond tokenisé par jour dans la grille (générique, fourni par le consommateur ; précédence sélection/`today` > variante marquée > défaut) + event `month-change` (`{ month: 0-11, year }`) émis à l'ouverture du popover et à chaque navigation de mois. Sert l'écran *Heures* de SIRH (jours validés en vert, chargement du mois visible à la volée).
* Calendrier (Date/DateRange/DateTime/DateWeek) : sélecteur d'année (3ᵉ niveau de navigation — jours → mois → années) et grisage des mois et années hors `min`/`max`.
### Changed
* Cohérence du mode **`disabled`** sur toute la famille formulaire (calqué sur InputText : texte + label grisés, `cursor-not-allowed`, aucune affordance interactive). Concrètement, quand `disabled` : le **bouton « + »** d'ajout disparaît (InputPhone, InputEmail), l'**œil** de révélation disparaît (InputPassword), le **chevron** disparaît (Select, SelectCheckbox, InputAutocomplete), la **croix d'effacement** reste masquée (date, upload, time), le **label** passe en `text-m-muted` (Select, SelectCheckbox, famille Date via CalendarField, TimePicker), et les **tags** du SelectCheckbox + la valeur du Select passent en gris. (InputText, InputAmount, InputNumber, InputTextArea, InputRichText, Checkbox, RadioButton, InputUpload étaient déjà conformes.)