Skip to content

Instantly share code, notes, and snippets.

@cannap
Created July 18, 2025 11:10
Show Gist options
  • Save cannap/f5df4990c5416c28f02c70b05f8f2bc5 to your computer and use it in GitHub Desktop.
Save cannap/f5df4990c5416c28f02c70b05f8f2bc5 to your computer and use it in GitHub Desktop.
<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