feat: datepicker script setup
continuous-integration/drone/pr Build is failing Details

This commit is contained in:
Dominik Pschenitschni 2022-05-21 00:52:49 +02:00
parent 2c270d063e
commit f10849dbfd
Signed by: dpschen
GPG Key ID: B257AC0149F43A77
1 changed files with 109 additions and 132 deletions

View File

@ -110,12 +110,13 @@
</div>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
<script lang="ts" setup>
import {ref, computed, watch, onMounted, onBeforeUnmount} from 'vue'
import {useI18n} from 'vue-i18n'
import {useStore} from 'vuex'
import flatPickr from 'vue-flatpickr-component'
import 'flatpickr/dist/flatpickr.css'
import {i18n} from '@/i18n'
import {format} from 'date-fns'
import {calculateDayInterval} from '@/helpers/time/calculateDayInterval'
@ -123,139 +124,115 @@ import {calculateNearestHours} from '@/helpers/time/calculateNearestHours'
import {closeWhenClickedOutside} from '@/helpers/closeWhenClickedOutside'
import {createDateFromString} from '@/helpers/time/createDateFromString'
export default defineComponent({
name: 'datepicker',
data() {
return {
date: null,
show: false,
changed: false,
}
const props = defineProps({
modelValue: {
type: [Date, String],
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
},
components: {
flatPickr,
chooseDateLabel: {
type: String,
default: () => t('input.datepicker.chooseDate'),
},
props: {
modelValue: {
validator: prop => prop instanceof Date || prop === null || typeof prop === 'string',
},
chooseDateLabel: {
type: String,
default() {
return i18n.global.t('input.datepicker.chooseDate')
},
},
disabled: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue', 'change', 'close', 'close-on-change'],
mounted() {
document.addEventListener('click', this.hideDatePopup)
},
beforeUnmount() {
document.removeEventListener('click', this.hideDatePopup)
},
watch: {
modelValue: {
handler: 'setDateValue',
immediate: true,
},
},
computed: {
flatPickerConfig() {
return {
altFormat: this.$t('date.altFormatLong'),
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
inline: true,
locale: {
firstDayOfWeek: this.$store.state.auth.settings.weekStart,
},
}
},
// 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.
flatPickrDate: {
set(newValue) {
this.date = createDateFromString(newValue)
this.updateData()
},
get() {
if (!this.date) {
return ''
}
return format(this.date, 'yyy-LL-dd H:mm')
},
},
},
methods: {
setDateValue(newVal) {
if (newVal === null) {
this.date = null
return
}
this.date = createDateFromString(newVal)
},
updateData() {
this.changed = true
this.$emit('update:modelValue', this.date)
this.$emit('change', this.date)
},
toggleDatePopup() {
if (this.disabled) {
return
}
this.show = !this.show
},
hideDatePopup(e) {
if (this.show) {
closeWhenClickedOutside(e, this.$refs.datepickerPopup, this.close)
}
},
close() {
// 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.
setTimeout(() => {
this.show = false
this.$emit('close', this.changed)
if (this.changed) {
this.changed = false
this.$emit('close-on-change', this.changed)
}
}, 200)
},
setDate(date) {
if (this.date === null) {
this.date = new Date()
}
const interval = calculateDayInterval(date)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
newDate.setHours(calculateNearestHours(newDate))
newDate.setMinutes(0)
newDate.setSeconds(0)
this.date = newDate
this.flatPickrDate = newDate
this.updateData()
},
getDayIntervalFromString(date) {
return calculateDayInterval(date)
},
getWeekdayFromStringInterval(date) {
const interval = calculateDayInterval(date)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
return format(newDate, 'E')
},
disabled: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:modelValue', 'change', 'close', 'close-on-change'])
const {t} = useI18n()
const store = useStore()
const date = ref<Date>()
const show = ref(false)
const changed = ref(false)
onMounted(() => document.addEventListener('click', hideDatePopup))
onBeforeUnmount(() => document.removeEventListener('click', hideDatePopup))
watch(
() => props.modelValue,
(value) => setDateValue(value),
{ immediate: true },
)
const flatPickerConfig = computed(() => ({
altFormat: t('date.altFormatLong'),
altInput: true,
dateFormat: 'Y-m-d H:i',
enableTime: true,
time_24hr: true,
inline: true,
locale: {
firstDayOfWeek: store.state.auth.settings.weekStart,
},
}))
function setDateValue(newVal) {
if (newVal === null) {
date.value = undefined
return
}
date.value = createDateFromString(newVal)
}
function updateData() {
changed.value = true
emit('update:modelValue', date.value)
emit('change', date.value)
}
function toggleDatePopup() {
if (props.disabled) {
return
}
show.value = !show.value
}
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
// having to click on another input field before it is actually used.
setTimeout(() => {
show.value = false
emit('close', changed.value)
if (changed.value) {
changed.value = false
emit('close-on-change', changed.value)
}
}, 200)
}
const flatPickrDate = ref()
function setDate(date) {
if (date.value === null) {
date.value = new Date()
}
const interval = calculateDayInterval(date)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
newDate.setHours(calculateNearestHours(newDate))
newDate.setMinutes(0)
newDate.setSeconds(0)
date.value = newDate
flatPickrDate.value = newDate
updateData()
}
function getWeekdayFromStringInterval(date) {
const interval = calculateDayInterval(date)
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)
return format(newDate, 'E')
}
</script>
<style lang="scss" scoped>