fix(natural-date-input): preserve input on edit, use reliable update event
Two reliability bugs in the natural-language date input: 1. Clicking Edit on a saved-date pill and changing the value immediately re-showed the saved value. clearAndEdit pre-fills the input with the formatted saved date so the admin doesn't start over, but chrono parses that string on the very first keystroke, re-sets parsedDate, and the auto-hide template flips the pill back. Added an isEditing flag that keeps the input visible across re-parses and clears on blur once we have a valid parse. 2. Typing "tomorrow at 2pm" sometimes committed "tomorrow at <current time>". UInput's template spreads \$attrs onto the inner <input> alongside its own @input="onInput", and Vue's listener-array merge intermittently drops the fall-through @input mid-typing — in the reproduction, the final 'm' never reached parseNaturalInput, so chrono's last successful read was "tomorrow at 2p" matching just "tomorrow". Switched to @update:model-value (a declared emit on UInput, so it goes through the reliable component-emit path) and made onBlur always re-parse the final value as a backup.
This commit is contained in:
parent
9e4030ccfd
commit
96470a604a
1 changed files with 24 additions and 6 deletions
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="space-y-2">
|
||||
<div v-if="!parsedDate || hasError" class="relative">
|
||||
<div v-if="!parsedDate || hasError || isEditing" class="relative">
|
||||
<UInput
|
||||
v-model="naturalInput"
|
||||
:model-value="naturalInput"
|
||||
:placeholder="placeholder"
|
||||
:color="
|
||||
hasError && naturalInput.trim()
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
? 'success'
|
||||
: undefined
|
||||
"
|
||||
@input="parseNaturalInput"
|
||||
@update:model-value="onNaturalInputChange"
|
||||
@blur="onBlur"
|
||||
>
|
||||
<template #trailing>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="parsedDate && isValidParse"
|
||||
v-if="parsedDate && isValidParse && !isEditing"
|
||||
class="text-sm px-3 py-2"
|
||||
style="color: var(--candle); background: color-mix(in srgb, var(--candle) 15%, transparent); border: 1px solid var(--candle)"
|
||||
>
|
||||
|
|
@ -108,6 +108,7 @@ const isValidParse = ref(false);
|
|||
const hasError = ref(false);
|
||||
const errorMessage = ref("");
|
||||
const datetimeValue = ref("");
|
||||
const isEditing = ref(false);
|
||||
|
||||
// Initialize with current value
|
||||
onMounted(() => {
|
||||
|
|
@ -132,6 +133,7 @@ watch(
|
|||
datetimeValue.value = formatForDatetimeLocal(date);
|
||||
isValidParse.value = true;
|
||||
naturalInput.value = ""; // Clear natural input when set externally
|
||||
isEditing.value = false;
|
||||
}
|
||||
} else if (!newValue) {
|
||||
reset();
|
||||
|
|
@ -175,11 +177,23 @@ const parseNaturalInput = () => {
|
|||
}
|
||||
};
|
||||
|
||||
// UInput's fall-through @input listener fires unreliably (the Nuxt UI Input
|
||||
// template merges $attrs with an explicit @input and can drop keystrokes).
|
||||
// update:model-value is a declared emit and fires for every change.
|
||||
const onNaturalInputChange = (value) => {
|
||||
naturalInput.value = value;
|
||||
parseNaturalInput();
|
||||
};
|
||||
|
||||
const onBlur = () => {
|
||||
// If we have a valid parse but the input changed, try to parse again
|
||||
if (naturalInput.value.trim() && !isValidParse.value) {
|
||||
// Always re-parse on blur so the final typed value wins, even if an
|
||||
// intermediate state produced a stale parse.
|
||||
if (naturalInput.value.trim()) {
|
||||
parseNaturalInput();
|
||||
}
|
||||
if (isValidParse.value) {
|
||||
isEditing.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onDatetimeChange = () => {
|
||||
|
|
@ -190,6 +204,7 @@ const onDatetimeChange = () => {
|
|||
isValidParse.value = true;
|
||||
hasError.value = false;
|
||||
naturalInput.value = ""; // Clear natural input when using traditional picker
|
||||
isEditing.value = false;
|
||||
emit("update:modelValue", datetimeValue.value);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -227,6 +242,9 @@ const clearAndEdit = () => {
|
|||
parsedDate.value = null;
|
||||
isValidParse.value = false;
|
||||
hasError.value = false;
|
||||
// Prevent auto-hide while editing: chrono re-parses the pre-filled string
|
||||
// on the first keystroke, which would otherwise hide the input.
|
||||
isEditing.value = true;
|
||||
};
|
||||
|
||||
const formatForDatetimeLocal = (date) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue