feat(ui) : MalioDate/DateTime — event update:rawValue pour validation back-autoritative (#MUI-44) (#74)
## 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>
This commit was merged in pull request #74.
This commit is contained in:
@@ -103,6 +103,10 @@ const props = withDefaults(
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string | null): void
|
||||
(e: 'update:valid', value: boolean): void
|
||||
// Canal séparé pour la saisie invalide (validation back-autoritative) : texte brut
|
||||
// tel que tapé sur saisie non parsable/hors plage, '' sinon. Ne JAMAIS transiter
|
||||
// par modelValue, qui doit rester ISO|null pour l'affichage et le round-trip.
|
||||
(e: 'update:rawValue', value: string): void
|
||||
}>()
|
||||
|
||||
// pendingTime : heure réglée avant qu'un jour ne soit choisi (sinon on ne peut pas émettre).
|
||||
@@ -129,6 +133,7 @@ function onSelectDay(iso: string) {
|
||||
const now = new Date()
|
||||
const time = parts.value.time || pendingTime.value || formatTime(now.getHours(), now.getMinutes())
|
||||
setError('')
|
||||
emit('update:rawValue', '')
|
||||
emit('update:modelValue', composeDateTime(iso, time))
|
||||
}
|
||||
|
||||
@@ -136,6 +141,7 @@ function onTimeChange(value: string | null) {
|
||||
if (!value) return
|
||||
if (datePart.value) {
|
||||
setError('')
|
||||
emit('update:rawValue', '')
|
||||
emit('update:modelValue', composeDateTime(datePart.value, value))
|
||||
}
|
||||
else {
|
||||
@@ -147,21 +153,25 @@ function onCommit(text: string) {
|
||||
const trimmed = text.trim()
|
||||
if (trimmed === '') {
|
||||
setError('')
|
||||
emit('update:rawValue', '')
|
||||
emit('update:modelValue', null)
|
||||
return
|
||||
}
|
||||
const iso = parseDisplayToIsoDateTime(trimmed)
|
||||
if (iso && isDateInRange(iso, props.min, props.max)) {
|
||||
setError('')
|
||||
emit('update:rawValue', '')
|
||||
emit('update:modelValue', iso)
|
||||
return
|
||||
}
|
||||
setError(props.invalidMessage)
|
||||
emit('update:rawValue', trimmed)
|
||||
}
|
||||
|
||||
function onClear() {
|
||||
setError('')
|
||||
pendingTime.value = ''
|
||||
emit('update:rawValue', '')
|
||||
emit('update:modelValue', null)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user