e5ec5bb6a0
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
97 lines
2.9 KiB
Vue
97 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from 'vue'
|
|
import { formatDisplayDate, formatDisplayTime, formatLocalIsoDate, formatLocalIsoTime } from '@/utils/format'
|
|
|
|
interface Props {
|
|
modelValue?: string | null
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: string | null]
|
|
}>()
|
|
|
|
const dateValue = ref('')
|
|
const timeValue = ref('')
|
|
const dateMenu = ref(false)
|
|
const timeMenu = ref(false)
|
|
|
|
watch(() => props.modelValue, (value) => {
|
|
const date = value ? new Date(value) : new Date()
|
|
dateValue.value = formatLocalIsoDate(date)
|
|
timeValue.value = formatLocalIsoTime(date)
|
|
}, { immediate: true })
|
|
|
|
const emitValue = () => {
|
|
if (!dateValue.value || !timeValue.value) return
|
|
emit('update:modelValue', new Date(`${dateValue.value}T${timeValue.value}`).toISOString())
|
|
}
|
|
|
|
const pickerDate = (value: string): Date | null => value ? new Date(`${value}T00:00:00`) : null
|
|
|
|
const normalizePickerDate = (value: unknown): string => {
|
|
const selected = Array.isArray(value) ? value[0] : value
|
|
const date = selected instanceof Date ? selected : new Date(String(selected))
|
|
return Number.isNaN(date.getTime()) ? '' : formatLocalIsoDate(date)
|
|
}
|
|
|
|
const selectDate = (value: unknown) => {
|
|
const date = normalizePickerDate(value)
|
|
if (date) dateValue.value = date
|
|
dateMenu.value = false
|
|
emitValue()
|
|
}
|
|
|
|
watch(timeValue, () => emitValue())
|
|
</script>
|
|
|
|
<template>
|
|
<v-row dense>
|
|
<v-col cols="6">
|
|
<v-menu v-model="dateMenu" :close-on-content-click="false" location="bottom start">
|
|
<template #activator="{ props: activatorProps }">
|
|
<v-text-field
|
|
v-bind="activatorProps"
|
|
:model-value="formatDisplayDate(dateValue)"
|
|
label="Date"
|
|
prepend-inner-icon="mdi-calendar"
|
|
variant="outlined"
|
|
density="compact"
|
|
hide-details
|
|
readonly
|
|
/>
|
|
</template>
|
|
<v-date-picker
|
|
:model-value="pickerDate(dateValue)"
|
|
color="primary"
|
|
show-adjacent-months
|
|
@update:model-value="selectDate"
|
|
/>
|
|
</v-menu>
|
|
</v-col>
|
|
<v-col cols="6">
|
|
<v-menu v-model="timeMenu" :close-on-content-click="false" location="bottom start">
|
|
<template #activator="{ props: activatorProps }">
|
|
<v-text-field
|
|
v-bind="activatorProps"
|
|
:model-value="formatDisplayTime(timeValue)"
|
|
label="Time"
|
|
prepend-inner-icon="mdi-clock-outline"
|
|
variant="outlined"
|
|
density="compact"
|
|
hide-details
|
|
readonly
|
|
/>
|
|
</template>
|
|
<v-card>
|
|
<v-time-picker v-model="timeValue" color="primary" format="ampm" />
|
|
<v-card-actions class="justify-end">
|
|
<v-btn variant="text" @click="timeMenu = false">Done</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-menu>
|
|
</v-col>
|
|
</v-row>
|
|
</template>
|