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.

Revisions

  1. cannap created this gist Jul 18, 2025.
    158 changes: 158 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,158 @@
    <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>