73 lines
2.7 KiB
Vue
73 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch } from 'vue';
|
|
|
|
const props = defineProps<{
|
|
modelValue: string; // ISO Date String (YYYY-MM-DD)
|
|
error?: boolean;
|
|
}>();
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
// Internal state for raw input
|
|
const inputValue = ref('');
|
|
|
|
// Format: YYYY-MM-DD -> DD.MM.YYYY for display
|
|
const formatDate = (iso: string) => {
|
|
if (!iso) return '';
|
|
const [y, m, d] = iso.split('-');
|
|
return `${d}.${m}.${y}`;
|
|
};
|
|
|
|
// Initialize
|
|
watch(() => props.modelValue, (val) => {
|
|
// Only update if the formatted value is different to avoid cursor jumping
|
|
const formatted = formatDate(val);
|
|
if (inputValue.value !== formatted && val.length === 10) {
|
|
inputValue.value = formatted;
|
|
}
|
|
}, { immediate: true });
|
|
|
|
const onInput = (e: Event) => {
|
|
let val = (e.target as HTMLInputElement).value.replace(/\D/g, ''); // Remove non-digits
|
|
|
|
// Auto-insert dots
|
|
if (val.length > 2) val = val.slice(0, 2) + '.' + val.slice(2);
|
|
if (val.length > 5) val = val.slice(0, 5) + '.' + val.slice(5);
|
|
if (val.length > 10) val = val.slice(0, 10);
|
|
|
|
inputValue.value = val;
|
|
|
|
// Only emit if full date is entered
|
|
if (val.length === 10) {
|
|
const [d, m, y] = val.split('.');
|
|
// Basic validation
|
|
const day = parseInt(d);
|
|
const month = parseInt(m);
|
|
const year = parseInt(y);
|
|
|
|
if (day > 0 && day <= 31 && month > 0 && month <= 12 && year > 1900 && year < 2100) {
|
|
emit('update:modelValue', `${y}-${m}-${d}`);
|
|
}
|
|
} else if (val.length === 0) {
|
|
emit('update:modelValue', '');
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="relative w-full">
|
|
<input
|
|
type="text"
|
|
:value="inputValue"
|
|
@input="onInput"
|
|
placeholder="DD.MM.YYYY"
|
|
class="flex h-10 w-full rounded-md border bg-[#0a0a0a] px-3 py-2 text-sm text-white shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[#00f2ff] disabled:cursor-not-allowed disabled:opacity-50 placeholder:text-[#444]"
|
|
:class="error ? 'border-red-500' : 'border-[#151515] hover:border-[#333]'"
|
|
maxlength="10"
|
|
/>
|
|
<div class="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none text-[#666]">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="4" rx="2" ry="2"/><line x1="16" x2="16" y1="2" y2="6"/><line x1="8" x2="8" y1="2" y2="6"/><line x1="3" x2="21" y1="10" y2="10"/></svg>
|
|
</div>
|
|
</div>
|
|
</template>
|