Created
July 18, 2025 11:10
-
-
Save cannap/f5df4990c5416c28f02c70b05f8f2bc5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup lang="ts"> | |
import type { DateValue } from '@internationalized/date' | |
import { CalendarDate, getLocalTimeZone, today } from '@internationalized/date' | |
import { ref, watch } from 'vue' | |
const props = withDefaults(defineProps<{ | |
label?: string | |
required?: boolean | |
}>(), { | |
label: 'Datum', | |
required: false, | |
}) | |
const locale = 'de-DE' | |
const id = useId() | |
// v-model support for parent binding | |
// Reka DatePicker expects a DateValue (CalendarDate) or null | |
// Use DateValue type for compatibility | |
const modelValue = defineModel<DateValue | null>({ default: null }) | |
const selectedDate = ref<DateValue | null>() | |
// Keep local and parent in sync | |
watch(selectedDate, val => modelValue.value = val as CalendarDate | null) | |
watch(modelValue, val => selectedDate.value = val) | |
function setTodayAndClose() { | |
// Get today's date in the user's local timezone as a CalendarDate | |
const t = today(getLocalTimeZone()) | |
selectedDate.value = new CalendarDate(t.year, t.month, t.day) | |
} | |
</script> | |
<template> | |
<Label | |
class="inline-block text-sm font-medium mb-2 cursor-pointer text-text-dark-gray" | |
:for="id" | |
> | |
{{ props.label }} | |
</Label> | |
<DatePickerRoot | |
:id="id" | |
v-model="selectedDate" | |
modal | |
:is-date-unavailable="date => date.day === 19" | |
:locale="locale" | |
> | |
<DatePickerField | |
v-slot="{ segments }" | |
class="w-full flex select-none bg-white items-center rounded-lg text-center justify-between text-gray-900 border border-gray-200 p-1 data-[invalid]:border-red-500" | |
> | |
<div class="flex items-center"> | |
<template | |
v-for="item in segments" | |
:key="item.part" | |
> | |
<DatePickerInput | |
v-if="item.part === 'literal'" | |
:part="item.part" | |
class="text-gray-400" | |
> | |
{{ item.value }} | |
</DatePickerInput> | |
<DatePickerInput | |
v-else | |
:part="item.part" | |
class="w-full px-3 py-2 border rounded-md focus:ring-2 focus:border-brand-pink focus:ring-brand-pink/20 transition-colors duration-200 focus:outline-none text-gray-900 data-[placeholder]:text-gray-400 | |
border-light-gray hover:border-gray-custom | |
data-[invalid]:border-brand-pink data-[invalid]:focus:ring-brand-pink/20 data-[invalid]:focus:border-brand-pink" | |
> | |
{{ item.value }} | |
</DatePickerInput> | |
</template> | |
</div> | |
<DatePickerTrigger | |
class="focus:shadow-[0_0_0_2px_var(--color-brand-pink)] rounded p-1" | |
> | |
<Icon name="i-lucide-calendar" class="w-4 h-4 text-[#e0003f]" /> | |
</DatePickerTrigger> | |
</DatePickerField> | |
<DatePickerContent | |
side="left" | |
:collision-padding="{ top: 4, right: 4, bottom: 89, left: 4 }" | |
class="rounded-xl bg-white border border-gray-200 shadow-sm will-change-[transform,opacity] data-[state=open]:data-[side=top]:animate-slideDownAndFade data-[state=open]:data-[side=right]:animate-slideLeftAndFade data-[state=open]:data-[side=bottom]:animate-slideUpAndFade data-[state=open]:data-[side=left]:animate-slideRightAndFade" | |
> | |
<DatePickerArrow class="fill-white stroke-gray-200" /> | |
<DatePickerCalendar | |
v-slot="{ weekDays, grid }" | |
class="p-4" | |
> | |
<DatePickerHeader class="flex items-center justify-between"> | |
<DatePickerPrev | |
class="inline-flex items-center cursor-pointer text-gray-700 justify-center rounded-md bg-transparent w-7 h-7 hover:bg-gray-100 active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black" | |
> | |
<Icon name="i-lucide-chevron-left" class="w-4 h-4" /> | |
</DatePickerPrev> | |
<DatePickerHeading class="text-gray-900 font-medium" /> | |
<DatePickerNext | |
class="inline-flex items-center cursor-pointer text-gray-700 justify-center rounded-md bg-transparent w-7 h-7 hover:bg-gray-100 active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black" | |
> | |
<Icon name="i-lucide-chevron-right" class="w-4 h-4" /> | |
</DatePickerNext> | |
</DatePickerHeader> | |
<div | |
class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0" | |
> | |
<DatePickerGrid | |
v-for="month in grid" | |
:key="month.value.toString()" | |
class="w-full border-collapse select-none space-y-1" | |
> | |
<DatePickerGridHead> | |
<DatePickerGridRow class="mb-1 flex w-full justify-between"> | |
<DatePickerHeadCell | |
v-for="day in weekDays" | |
:key="day" | |
class="w-8 rounded-md text-xs text-gray-500" | |
> | |
{{ day }} | |
</DatePickerHeadCell> | |
</DatePickerGridRow> | |
</DatePickerGridHead> | |
<DatePickerGridBody> | |
<DatePickerGridRow | |
v-for="(weekDates, index) in month.rows" | |
:key="`weekDate-${index}`" | |
class="flex w-full" | |
> | |
<DatePickerCell | |
v-for="weekDate in weekDates" | |
:key="weekDate.toString()" | |
:date="weekDate" | |
> | |
<DatePickerCellTrigger | |
:day="weekDate" | |
:month="month.value" | |
class="relative flex items-center justify-center whitespace-nowrap rounded-[9px] border border-transparent bg-transparent text-sm font-normal text-gray-900 w-10 h-10 outline-none focus:shadow-[0_0_0_2px_var(--color-brand-pink)] hover:border-[#e0003f] data-[selected]:bg-[#e0003f] data-[selected]:font-medium data-[outside-view]:text-gray-400 data-[selected]:text-white data-[unavailable]:pointer-events-none data-[unavailable]:text-gray-400 data-[unavailable]:line-through before:absolute before:top-[5px] before:hidden before:rounded-full before:w-1 before:h-1 before:bg-white data-[today]:before:block data-[today]:before:bg-green-500 data-[selected]:before:bg-white" | |
/> | |
</DatePickerCell> | |
</DatePickerGridRow> | |
</DatePickerGridBody> | |
</DatePickerGrid> | |
</div> | |
<div class="mt-4 flex gap-2"> | |
<button | |
type="button" | |
class="flex-1 rounded-lg bg-[#e0003f] text-white py-2 px-4 text-sm font-medium hover:bg-[#b80032] transition-colors focus:outline-none focus:ring-2 focus:ring-brand-pink/20" | |
@click="setTodayAndClose" | |
> | |
Heute | |
</button> | |
</div> | |
</DatePickerCalendar> | |
</DatePickerContent> | |
</DatePickerRoot> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment