fix(datepicker): emit date value changes as soon as they happen

Flatpickr only returns a change event when the value in the input it's referring to changes. That means it will usually only trigger when the focus is moved out of the input field. This is fine most of the time. However, since we're displaying flatpickr in a popup, the whole html dom instance might get destroyed, before the change event had a chance to fire. In that case, it would not update the date value. To fix this, we're now listening on every change and bubble them up as soon as they happen.

Resolves https://community.vikunja.io/t/due-date-confirm-button-not-working/2104
This commit is contained in:
kolaente 2024-03-13 18:03:49 +01:00
parent a6edf1d325
commit b3caece256
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B

View File

@ -63,6 +63,7 @@
<div class="flatpickr-container">
<flat-pickr
ref="flatPickrRef"
v-model="flatPickrDate"
:config="flatPickerConfig"
/>
@ -70,7 +71,7 @@
</template>
<script lang="ts" setup>
import {ref, toRef, watch, computed, type PropType} from 'vue'
import {computed, onBeforeUnmount, onMounted, type PropType, ref, toRef, watch} from 'vue'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
@ -81,7 +82,7 @@ import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {createDateFromString} from '@/helpers/time/createDateFromString'
import {useI18n} from 'vue-i18n'
import { getFlatpickrLanguage } from '@/helpers/flatpickrLanguage'
import {getFlatpickrLanguage} from '@/helpers/flatpickrLanguage'
const props = defineProps({
modelValue: {
@ -105,6 +106,7 @@ watch(
{immediate: true},
)
const flatPickrRef = ref<HTMLElement | null>(null)
const flatPickerConfig = computed(() => ({
altFormat: t('date.altFormatLong'),
altInput: true,
@ -142,6 +144,41 @@ const flatPickrDate = computed({
},
})
onMounted(() => {
const inputs = flatPickrRef.value?.$el.parentNode.querySelectorAll('.numInputWrapper > input.numInput')
inputs.forEach(i => {
i.addEventListener('input', handleFlatpickrInput)
})
})
onBeforeUnmount(() => {
const inputs = flatPickrRef.value?.$el.parentNode.querySelectorAll('.numInputWrapper > input.numInput')
inputs.forEach(i => {
i.removeEventListener('input', handleFlatpickrInput)
})
})
// Flatpickr only returns a change event when the value in the input it's referring to changes.
// That means it will usually only trigger when the focus is moved out of the input field.
// This is fine most of the time. However, since we're displaying flatpickr in a popup,
// the whole html dom instance might get destroyed, before the change event had a
// chance to fire. In that case, it would not update the date value. To fix
// this, we're now listening on every change and bubble them up as soon
// as they happen.
function handleFlatpickrInput(e) {
const newDate = new Date(date?.value || 'now')
if (e.target.classList.contains('flatpickr-minute')) {
newDate.setMinutes(e.target.value)
}
if (e.target.classList.contains('flatpickr-hour')) {
newDate.setHours(e.target.value)
}
if (e.target.classList.contains('cur-year')) {
newDate.setFullYear(e.target.value)
}
flatPickrDate.value = newDate
}
function setDateValue(dateString: string | Date | null) {
if (dateString === null) {