104 lines
3.3 KiB
Vue
104 lines
3.3 KiB
Vue
<script setup>
|
|
import InputLabel from './InputLabel.vue'
|
|
import InputError from './InputError.vue'
|
|
import { computed } from 'vue'
|
|
|
|
/*
|
|
DatePickerField (v-calendar)
|
|
- A thin wrapper around <VDatePicker> with a label and error support.
|
|
- Uses v-calendar which handles popovers/teleport well inside modals.
|
|
API: kept compatible with previous usage where possible.
|
|
Props:
|
|
- modelValue: Date | string | number | null
|
|
- id: string
|
|
- label: string
|
|
- format: string (default 'dd.MM.yyyy')
|
|
- enableTimePicker: boolean (default false)
|
|
- inline: boolean (default false) // When true, keeps the popover visible
|
|
- placeholder: string
|
|
- error: string | string[]
|
|
Note: Props like teleportTarget/autoPosition/menuClassName/fixed/closeOn... were for the old picker
|
|
and are accepted for compatibility but are not used by v-calendar.
|
|
*/
|
|
|
|
const props = defineProps({
|
|
modelValue: { type: [Date, String, Number, null], default: null },
|
|
id: { type: String, default: undefined },
|
|
label: { type: String, default: undefined },
|
|
format: { type: String, default: 'dd.MM.yyyy' },
|
|
enableTimePicker: { type: Boolean, default: false },
|
|
inline: { type: Boolean, default: false },
|
|
// legacy/unused in v-calendar (kept to prevent breaking callers)
|
|
autoApply: { type: Boolean, default: false },
|
|
teleportTarget: { type: [Boolean, String], default: 'body' },
|
|
autoPosition: { type: Boolean, default: true },
|
|
menuClassName: { type: String, default: 'dp-over-modal' },
|
|
fixed: { type: Boolean, default: true },
|
|
closeOnAutoApply: { type: Boolean, default: true },
|
|
closeOnScroll: { type: Boolean, default: true },
|
|
placeholder: { type: String, default: '' },
|
|
error: { type: [String, Array], default: undefined },
|
|
})
|
|
|
|
const emit = defineEmits(['update:modelValue', 'change'])
|
|
|
|
const valueProxy = computed({
|
|
get: () => props.modelValue,
|
|
set: (val) => {
|
|
emit('update:modelValue', val)
|
|
emit('change', val)
|
|
},
|
|
})
|
|
|
|
// Convert common date mask from lowercase tokens to v-calendar tokens
|
|
const inputMask = computed(() => {
|
|
let m = props.format || 'dd.MM.yyyy'
|
|
return m
|
|
.replace(/yyyy/g, 'YYYY')
|
|
.replace(/dd/g, 'DD')
|
|
.replace(/MM/g, 'MM')
|
|
+ (props.enableTimePicker ? ' HH:mm' : '')
|
|
})
|
|
|
|
const popoverCfg = computed(() => ({
|
|
visibility: props.inline ? 'visible' : 'click',
|
|
placement: 'bottom-start',
|
|
}))
|
|
</script>
|
|
|
|
<template>
|
|
<div class="col-span-6 sm:col-span-4">
|
|
<InputLabel v-if="label" :for="id" :value="label" />
|
|
|
|
<!-- VCalendar DatePicker with custom input to keep Tailwind styling -->
|
|
<VDatePicker
|
|
v-model="valueProxy"
|
|
:mode="enableTimePicker ? 'dateTime' : 'date'"
|
|
:masks="{ input: inputMask }"
|
|
:popover="popoverCfg"
|
|
:is24hr="true"
|
|
>
|
|
<template #default="{ inputValue, inputEvents }">
|
|
<input
|
|
:id="id"
|
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
|
|
:placeholder="placeholder"
|
|
:value="inputValue"
|
|
v-on="inputEvents"
|
|
/>
|
|
</template>
|
|
</VDatePicker>
|
|
|
|
<template v-if="error">
|
|
<InputError v-if="Array.isArray(error)" v-for="(e, idx) in error" :key="idx" :message="e" />
|
|
<InputError v-else :message="error" />
|
|
</template>
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<style>
|
|
/* Ensure the date picker menu overlays modals/dialogs */
|
|
|
|
</style>
|