## 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>
## MUI-44 — Exposer la saisie brute invalide (`@update:rawValue`)
Suite de MUI-43. Une app consommatrice (Starseed/ERP) fait de la **validation back-autoritative** : plutôt que bloquer le submit côté front, elle transmet la saisie invalide au serveur qui renvoie un `422` mappé inline. Or `MalioDate`/`MalioDateTime` **avalent** la saisie invalide (ni `modelValue`, ni texte brut) → le parent ne peut rien envoyer.
### Changements
- Nouvel emit `(e: 'update:rawValue', value: string)` sur `Date.vue` et `DateTime.vue`, émis à chaque commit :
- saisie **invalide** (non parsable ou hors `min`/`max`) → chaîne brute trimmée telle que tapée (ex. `"32/13/2026"`), **sans** emit `update:modelValue` ;
- saisie **valide ou vide**, **clear**, **sélection au calendrier** (+ réglage d'heure pour DateTime) → `''`.
- Canal **séparé** : `modelValue` reste `string` ISO `| null` (affichage + round-trip). Le parent construit son payload via `valid ? modelValue : rawValue`.
### Tests (TDD)
6 cas ajoutés par composant : malformé, hors bornes, valide, vidé, clear, sélection calendrier. Suite complète **987 ✓**, ESLint 0 erreur.
### Doc
`COMPONENTS.md` (paragraphe + Events + exemples) et `CHANGELOG.md` (entrée MUI-44) à jour.
### Hors périmètre
`DateRange`/`DateWeek` (pas de saisie texte libre). Branchement Starseed (`collectDenormalizationErrors`, `useFormErrors`) traité côté ERP.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: #74
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
Ticket MUI-43 : exposer l'état de validité de MalioDate (saisie invalide avalée silencieusement) + portage de la saisie clavier sur MalioDateTime.
## Contenu
**MalioDate**
- Nouvel event `update:valid(boolean)` : `false` sur saisie malformée ou hors min/max (qui n'émet pas `modelValue`), `true` sinon ; émis dès le montage. La validité ne couvre pas `required` (champ vide = valide).
**MalioDateTime**
- Prop `editable` : saisie clavier `JJ/MM/AAAA HH:MM` (masque maska, validation au blur/Entrée, `invalidMessage`) + même `update:valid`.
- Nouveau parseur `parseDisplayToIsoDateTime`.
**Famille Date editable (Date + DateTime)**
- Gabarit fantôme progressif : le format s'affiche en gris et se remplit au fil de la saisie (overlay ghost mirror, texte de l'input transparent).
- Séparateurs (/, espace, :) posés automatiquement (maska `eager`), espace insécable pour éviter le collage `12/12/1999HH:MM`.
- `CalendarField` : prop `placeholderTemplate` (le masque maska en est dérivé).
**Corrections**
- La croix d'effacement réinitialise la saisie clavier même après une date invalide (le v-model restant null, le champ ne se vidait pas).
- Fix d'un test `Date.test.ts` cassé sur develop (`trigger('keydown.enter')` envoie key='enter' ≠ handler `e.key === 'Enter'`).
## Portée
MalioDate seul pour la validité (les cousins DateRange/DateWeek n'ont pas de saisie clavier donc pas le bug). Sémantique `valid` = malformé only.
## Tests
`app/components/malio/date/` : 187/187, ESLint propre. Vérifié visuellement dans le playground (page Date & heure).
## Doc
COMPONENTS.md + CHANGELOG.md à jour.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed-on: #71
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
| | |
## Description de la PR
## Modification du .env
## Check list
- [x] Pas de régression
- [x] TU/TI/TF rédigée
- [x] TU/TI/TF OK
- [x] CHANGELOG modifié
Reviewed-on: #50
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>