feat: datepicker script setup (#2456)

Co-authored-by: Dominik Pschenitschni <mail@celement.de>
Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2456
Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
Dominik Pschenitschni 2022-10-01 22:13:50 +00:00 committed by konrad
parent 63fb8a1962
commit ff1968aa36
2 changed files with 131 additions and 139 deletions

View file

@ -88,12 +88,10 @@
</div> </div>
</template> </template>
<script lang="ts"> <script setup lang="ts">
import {defineComponent} from 'vue' import {ref, onMounted, onBeforeUnmount, toRef, watch, computed, type PropType} from 'vue'
import flatPickr from 'vue-flatpickr-component' import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css' import 'flatpickr/dist/flatpickr.css'
import {i18n} from '@/i18n'
import BaseButton from '@/components/base/BaseButton.vue' import BaseButton from '@/components/base/BaseButton.vue'
@ -102,146 +100,140 @@ import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
import {calculateNearestHours} from '@/helpers/time/calculateNearestHours' import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside' import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {createDateFromString} from '@/helpers/time/createDateFromString' import {createDateFromString} from '@/helpers/time/createDateFromString'
import {mapState} from 'pinia'
import {useAuthStore} from '@/stores/auth' import {useAuthStore} from '@/stores/auth'
import {useI18n} from 'vue-i18n'
export default defineComponent({ const props = defineProps({
name: 'datepicker',
data() {
return {
date: null,
show: false,
changed: false,
}
},
components: {
flatPickr,
BaseButton,
},
props: {
modelValue: { modelValue: {
type: [Date, null, String] as PropType<Date | null | string>,
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string', validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
default: null,
}, },
chooseDateLabel: { chooseDateLabel: {
type: String, type: String,
default() { default() {
return i18n.global.t('input.datepicker.chooseDate') const {t} = useI18n({useScope: 'global'})
return t('input.datepicker.chooseDate')
}, },
}, },
disabled: { disabled: {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
}, })
emits: ['update:modelValue', 'close', 'close-on-change'],
mounted() { const emit = defineEmits(['update:modelValue', 'close', 'close-on-change'])
document.addEventListener('click', this.hideDatePopup)
}, const {t} = useI18n({useScope: 'global'})
beforeUnmount() {
document.removeEventListener('click', this.hideDatePopup) const date = ref<Date | null>()
}, const show = ref(false)
watch: { const changed = ref(false)
modelValue: {
handler: 'setDateValue', onMounted(() => document.addEventListener('click', hideDatePopup))
immediate: true, onBeforeUnmount(() =>document.removeEventListener('click', hideDatePopup))
},
}, const modelValue = toRef(props, 'modelValue')
computed: { watch(
...mapState(useAuthStore, { modelValue,
weekStart: (state) => state.settings.weekStart, setDateValue,
}), {immediate: true},
flatPickerConfig() { )
return {
altFormat: this.$t('date.altFormatLong'), const authStore = useAuthStore()
const weekStart = computed(() => authStore.settings.weekStart)
const flatPickerConfig = computed(() => ({
altFormat: t('date.altFormatLong'),
altInput: true, altInput: true,
dateFormat: 'Y-m-d H:i', dateFormat: 'Y-m-d H:i',
enableTime: true, enableTime: true,
time_24hr: true, time_24hr: true,
inline: true, inline: true,
locale: { locale: {
firstDayOfWeek: this.weekStart, firstDayOfWeek: weekStart.value,
},
}
}, },
}))
// Since flatpickr dates are strings, we need to convert them to native date objects. // Since flatpickr dates are strings, we need to convert them to native date objects.
// To make that work, we need a separate variable since flatpickr does not have a change event. // To make that work, we need a separate variable since flatpickr does not have a change event.
flatPickrDate: { const flatPickrDate = computed({
set(newValue) { set(newValue: string | Date) {
this.date = createDateFromString(newValue) date.value = createDateFromString(newValue)
this.updateData() updateData()
}, },
get() { get() {
if (!this.date) { if (!date.value) {
return '' return ''
} }
return formatDate(this.date, 'yyy-LL-dd H:mm') return formatDate(date.value, 'yyy-LL-dd H:mm')
}, },
}, })
},
methods: {
formatDateShort, function setDateValue(dateString: string | Date | null) {
setDateValue(newVal) { if (dateString === null) {
if (newVal === null) { date.value = null
this.date = null
return return
} }
this.date = createDateFromString(newVal) date.value = createDateFromString(dateString)
}, }
updateData() {
this.changed = true function updateData() {
this.$emit('update:modelValue', this.date) changed.value = true
}, emit('update:modelValue', date.value)
toggleDatePopup() { }
if (this.disabled) {
function toggleDatePopup() {
if (props.disabled) {
return return
} }
this.show = !this.show show.value = !show.value
},
hideDatePopup(e) {
if (this.show) {
closeWhenClickedOutside(e, this.$refs.datepickerPopup, this.close)
} }
},
close() { const datepickerPopup = ref<HTMLElement | null>(null)
function hideDatePopup(e) {
if (show.value) {
closeWhenClickedOutside(e, datepickerPopup.value, close)
}
}
function close() {
// Kind of dirty, but the timeout allows us to enter a time and click on "confirm" without // Kind of dirty, but the timeout allows us to enter a time and click on "confirm" without
// having to click on another input field before it is actually used. // having to click on another input field before it is actually used.
setTimeout(() => { setTimeout(() => {
this.show = false show.value = false
this.$emit('close', this.changed) emit('close', changed.value)
if (this.changed) { if (changed.value) {
this.changed = false changed.value = false
this.$emit('close-on-change', this.changed) emit('close-on-change', changed.value)
} }
}, 200) }, 200)
},
setDate(date) {
if (this.date === null) {
this.date = new Date()
} }
const interval = calculateDayInterval(date) function setDate(dateString: string) {
if (date.value === null) {
date.value = new Date()
}
const interval = calculateDayInterval(dateString)
const newDate = new Date() const newDate = new Date()
newDate.setDate(newDate.getDate() + interval) newDate.setDate(newDate.getDate() + interval)
newDate.setHours(calculateNearestHours(newDate)) newDate.setHours(calculateNearestHours(newDate))
newDate.setMinutes(0) newDate.setMinutes(0)
newDate.setSeconds(0) newDate.setSeconds(0)
this.date = newDate date.value = newDate
this.flatPickrDate = newDate flatPickrDate.value = newDate
this.updateData() updateData()
}, }
getDayIntervalFromString(date) {
return calculateDayInterval(date) function getWeekdayFromStringInterval(dateString: string) {
}, const interval = calculateDayInterval(dateString)
getWeekdayFromStringInterval(date) {
const interval = calculateDayInterval(date)
const newDate = new Date() const newDate = new Date()
newDate.setDate(newDate.getDate() + interval) newDate.setDate(newDate.getDate() + interval)
return formatDate(newDate, 'E') return formatDate(newDate, 'E')
}, }
},
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -1,5 +1,5 @@
export function calculateDayInterval(date, currentDay = (new Date().getDay())) { export function calculateDayInterval(dateString: string, currentDay = (new Date().getDay())) {
switch (date) { switch (dateString) {
case 'today': case 'today':
return 0 return 0
case 'tomorrow': case 'tomorrow':