feat(ui) : MalioDate/DateTime exposent update:valid + saisie clavier DateTime (#MUI-43)

- MalioDate : event update:valid (malforme/hors-plage => false), emis au montage
- MalioDateTime : prop editable (saisie JJ/MM/AAAA HH:MM) + meme update:valid
- CalendarField : masque maska configurable via prop mask
- datetimeFormat : nouveau parseur parseDisplayToIsoDateTime
- fix test Date « Entree » (key 'Enter' reel vs trigger keydown.enter)
- doc COMPONENTS.md + CHANGELOG.md + champ editable dans le playground

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 16:03:04 +02:00
parent 23a9729dcd
commit fee894e895
10 changed files with 350 additions and 18 deletions
+20 -8
View File
@@ -87,44 +87,56 @@ const props = withDefaults(
},
)
const emit = defineEmits<{(e: 'update:modelValue', value: string | null): void}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string | null): void
(e: 'update:valid', value: boolean): void
}>()
const displayValue = computed(() => formatIsoToDisplay(props.modelValue ?? null))
const internalError = ref('')
const mergedError = computed(() => props.error || internalError.value)
// La validité ne reflète que la saisie : malformée/hors plage → false. Un champ
// vide est valide (l'obligation `required` reste à la charge du parent).
const setError = (message: string) => {
internalError.value = message
emit('update:valid', message === '')
}
const onCommit = (text: string) => {
const trimmed = text.trim()
if (trimmed === '') {
internalError.value = ''
setError('')
emit('update:modelValue', null)
return
}
const iso = parseDisplayToIso(trimmed)
if (iso && isDateInRange(iso, props.min, props.max)) {
internalError.value = ''
setError('')
emit('update:modelValue', iso)
return
}
internalError.value = props.invalidMessage
setError(props.invalidMessage)
}
const onClear = () => {
internalError.value = ''
setError('')
emit('update:modelValue', null)
}
const onSelect = (iso: string, close: () => void) => {
internalError.value = ''
setError('')
emit('update:modelValue', iso)
close()
}
// immediate : émet aussi la validité au montage, pour que le parent connaisse
// l'état d'un champ pré-rempli (formulaire d'édition) sans interaction préalable.
watch(() => props.modelValue, (val) => {
internalError.value = ''
setError('')
if (val && !isValidIso(val) && import.meta.dev) {
console.warn(`[MalioDate] modelValue invalide ignoré : "${val}"`)
}
})
}, {immediate: true})
</script>