153 lines
3.7 KiB
Vue
153 lines
3.7 KiB
Vue
<script setup>
|
|
import { computed, ref, useAttrs } from "vue";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { Calendar } from "@/Components/ui/calendar";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/Components/ui/popover";
|
|
import { cn } from "@/lib/utils";
|
|
import { CalendarIcon } from "lucide-vue-next";
|
|
import { format } from "date-fns";
|
|
import { sl } from "date-fns/locale";
|
|
import { CalendarDate, parseDate } from "@internationalized/date";
|
|
|
|
defineOptions({ inheritAttrs: false });
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: [Date, String, null],
|
|
default: null,
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
default: "Izberi datum",
|
|
},
|
|
format: {
|
|
type: String,
|
|
default: "dd.MM.yyyy",
|
|
},
|
|
disabled: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
id: {
|
|
type: String,
|
|
default: undefined,
|
|
},
|
|
error: {
|
|
type: [String, Array],
|
|
default: undefined,
|
|
},
|
|
});
|
|
|
|
const emit = defineEmits(["update:modelValue"]);
|
|
|
|
const attrs = useAttrs();
|
|
const forwardedAttrs = computed(() => {
|
|
const { class: _class, id: _id, ...rest } = attrs;
|
|
return rest;
|
|
});
|
|
const controlId = computed(() => attrs.id ?? props.id);
|
|
|
|
// Convert string/Date to CalendarDate
|
|
const toCalendarDate = (value) => {
|
|
if (!value) return null;
|
|
let dateObj;
|
|
if (value instanceof Date) {
|
|
dateObj = value;
|
|
} else if (typeof value === "string") {
|
|
// Handle YYYY-MM-DD format
|
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
try {
|
|
const [year, month, day] = value.split("-").map(Number);
|
|
return new CalendarDate(year, month, day);
|
|
} catch {
|
|
dateObj = new Date(value);
|
|
}
|
|
} else {
|
|
dateObj = new Date(value);
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
if (dateObj && !isNaN(dateObj.getTime())) {
|
|
return new CalendarDate(
|
|
dateObj.getFullYear(),
|
|
dateObj.getMonth() + 1,
|
|
dateObj.getDate()
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Convert CalendarDate to ISO string (YYYY-MM-DD)
|
|
const fromCalendarDate = (calendarDate) => {
|
|
if (!calendarDate) return null;
|
|
return `${String(calendarDate.year).padStart(4, "0")}-${String(calendarDate.month).padStart(2, "0")}-${String(calendarDate.day).padStart(2, "0")}`;
|
|
};
|
|
|
|
const calendarDate = computed({
|
|
get: () => toCalendarDate(props.modelValue),
|
|
set: (value) => {
|
|
const isoString = fromCalendarDate(value);
|
|
emit("update:modelValue", isoString);
|
|
},
|
|
});
|
|
|
|
// Format for display
|
|
const formattedDate = computed(() => {
|
|
if (!calendarDate.value) return props.placeholder;
|
|
try {
|
|
const dateObj = new Date(
|
|
calendarDate.value.year,
|
|
calendarDate.value.month - 1,
|
|
calendarDate.value.day
|
|
);
|
|
const formatMap = {
|
|
"dd.MM.yyyy": "dd.MM.yyyy",
|
|
"yyyy-MM-dd": "yyyy-MM-dd",
|
|
};
|
|
const dateFormat = formatMap[props.format] || "dd.MM.yyyy";
|
|
return format(dateObj, dateFormat, { locale: sl });
|
|
} catch {
|
|
return props.placeholder;
|
|
}
|
|
});
|
|
|
|
const open = ref(false);
|
|
</script>
|
|
|
|
<template>
|
|
<Popover v-model:open="open">
|
|
<PopoverTrigger as-child>
|
|
<Button
|
|
v-bind="forwardedAttrs"
|
|
:id="controlId"
|
|
variant="outline"
|
|
:class="
|
|
cn(
|
|
'w-full justify-start text-left font-normal',
|
|
!calendarDate && 'text-muted-foreground',
|
|
error && 'border-red-500 focus:border-red-500 focus:ring-red-500',
|
|
attrs.class
|
|
)
|
|
"
|
|
:disabled="disabled"
|
|
>
|
|
<CalendarIcon class="mr-2 h-4 w-4" />
|
|
{{ formattedDate }}
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent class="w-auto p-0" align="start">
|
|
<Calendar v-model="calendarDate" :disabled="disabled" />
|
|
</PopoverContent>
|
|
</Popover>
|
|
<p v-if="error" class="mt-1 text-sm text-red-600">
|
|
{{ Array.isArray(error) ? error[0] : error }}
|
|
</p>
|
|
</template>
|
|
|