UIPackage

Calendar

Vue date-time
Edit on GitHub

Single-month calendar grid for date selection, built on reka-ui’s Calendar primitive. Pair it with a Popover or use it inline. Supports min/max bounds, disabled dates, and locale formatting.

Also available for React ->

Installation

$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/calendar.json

Or with the named registry: npx shadcn-vue@latest add @uipkge/calendar

Examples

Props

Name Type / Values Default Required
class HTMLAttributes['class']; layout?: LayoutTypes; yearRange?: DateValue[] optional

Dependencies

Used by

Files (15)

  • app/components/ui/calendar/Calendar.vue 6.7 kB
    <script lang="ts" setup>
    import type { CalendarRootEmits, CalendarRootProps, DateValue } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import type { LayoutTypes } from '.'
    import { getLocalTimeZone, today } from '@internationalized/date'
    import { createReusableTemplate, reactiveOmit } from '@vueuse/core'
    import { CalendarRoot, useDateFormatter, useForwardPropsEmits } from 'reka-ui'
    import { createYear, toDate } from 'reka-ui/date'
    import { computed, ref, toRaw } from 'vue'
    import { cn } from '@/lib/utils'
    import {
      CalendarCell,
      CalendarCellTrigger,
      CalendarGrid,
      CalendarGridBody,
      CalendarGridHead,
      CalendarGridRow,
      CalendarHeadCell,
      CalendarHeader,
      CalendarHeading,
      CalendarNextButton,
      CalendarPrevButton,
      NativeSelect,
      NativeSelectOption,
    } from '.'
    
    const props = withDefaults(
      defineProps<CalendarRootProps & { class?: HTMLAttributes['class']; layout?: LayoutTypes; yearRange?: DateValue[] }>(),
      {
        modelValue: undefined,
        layout: undefined,
      },
    )
    const emits = defineEmits<CalendarRootEmits>()
    
    // Fix: Don't pass defaultValue or defaultPlaceholder to reka-ui to avoid .copy() errors
    // Only pass the props that reka-ui needs
    const delegatedProps = reactiveOmit(props, [
      'class',
      'layout',
      'placeholder',
      'modelValue',
      'defaultValue',
      'defaultPlaceholder',
    ])
    
    const forwarded = useForwardPropsEmits(delegatedProps, emits)
    
    const formatter = useDateFormatter(props.locale ?? 'en')
    
    const yearRange = computed(() => {
      if (props.yearRange) return props.yearRange
      const base = toRaw(props.placeholder) ?? today(getLocalTimeZone())
      const start = props?.minValue ?? base.add({ years: -100 })
      const end = props?.maxValue ?? base.add({ years: 10 })
      const years: DateValue[] = []
      let current = start
      while (current.compare(end) <= 0) {
        years.push(current)
        current = current.add({ years: 1 })
      }
      return years
    })
    
    const [DefineMonthTemplate, ReuseMonthTemplate] = createReusableTemplate<{ date: DateValue }>()
    const [DefineYearTemplate, ReuseYearTemplate] = createReusableTemplate<{ date: DateValue }>()
    
    // Fix: Use simple ref for placeholder, not useVModel, to avoid .copy() error in reka-ui
    const internalPlaceholder = ref(props.placeholder ?? today(getLocalTimeZone()))
    
    function setMonth(e: Event) {
      const v = Number((e?.target as HTMLSelectElement | null)?.value)
      internalPlaceholder.value = (internalPlaceholder.value as DateValue).set({ month: v })
    }
    function setYear(e: Event) {
      const v = Number((e?.target as HTMLSelectElement | null)?.value)
      internalPlaceholder.value = (internalPlaceholder.value as DateValue).set({ year: v })
    }
    </script>
    
    <template>
      <DefineMonthTemplate v-slot="{ date }">
        <div class="**:data-[slot=native-select-icon]:right-1">
          <div class="relative">
            <div class="pointer-events-none absolute inset-0 flex h-full items-center pl-2 text-sm">
              {{ formatter.custom(toDate(date), { month: 'short' }) }}
            </div>
            <NativeSelect class="relative h-8 pr-6 pl-2 text-xs text-transparent" @change="setMonth">
              <NativeSelectOption
                v-for="month in createYear({ dateObj: date })"
                :key="month.toString()"
                :value="month.month"
                :selected="date.month === month.month"
              >
                {{ formatter.custom(toDate(month), { month: 'short' }) }}
              </NativeSelectOption>
            </NativeSelect>
          </div>
        </div>
      </DefineMonthTemplate>
    
      <DefineYearTemplate v-slot="{ date }">
        <div class="**:data-[slot=native-select-icon]:right-1">
          <div class="relative">
            <div class="pointer-events-none absolute inset-0 flex h-full items-center pl-2 text-sm">
              {{ formatter.custom(toDate(date), { year: 'numeric' }) }}
            </div>
            <NativeSelect class="relative h-8 pr-6 pl-2 text-xs text-transparent" @change="setYear">
              <NativeSelectOption
                v-for="year in yearRange"
                :key="year.toString()"
                :value="year.year"
                :selected="date.year === year.year"
              >
                {{ formatter.custom(toDate(year), { year: 'numeric' }) }}
              </NativeSelectOption>
            </NativeSelect>
          </div>
        </div>
      </DefineYearTemplate>
    
      <CalendarRoot
        v-slot="{ grid, weekDays, date }"
        v-bind="forwarded"
        :placeholder="internalPlaceholder as DateValue"
        data-uipkge
        data-slot="calendar"
        :class="cn('p-3', props.class)"
        @update:placeholder="
          (val) => {
            internalPlaceholder = val
          }
        "
      >
        <CalendarHeader class="pt-0">
          <nav class="absolute inset-x-0 top-0 flex items-center justify-between gap-1">
            <CalendarPrevButton>
              <slot name="calendar-prev-icon" />
            </CalendarPrevButton>
            <CalendarNextButton>
              <slot name="calendar-next-icon" />
            </CalendarNextButton>
          </nav>
    
          <slot name="calendar-heading" :date="date" :month="ReuseMonthTemplate" :year="ReuseYearTemplate">
            <template v-if="layout === 'month-and-year'">
              <div class="flex items-center justify-center gap-1">
                <ReuseMonthTemplate :date="date" />
                <ReuseYearTemplate :date="date" />
              </div>
            </template>
            <template v-else-if="layout === 'month-only'">
              <div class="flex items-center justify-center gap-1">
                <ReuseMonthTemplate :date="date" />
                {{ formatter.custom(toDate(date), { year: 'numeric' }) }}
              </div>
            </template>
            <template v-else-if="layout === 'year-only'">
              <div class="flex items-center justify-center gap-1">
                {{ formatter.custom(toDate(date), { month: 'short' }) }}
                <ReuseYearTemplate :date="date" />
              </div>
            </template>
            <template v-else>
              <CalendarHeading />
            </template>
          </slot>
        </CalendarHeader>
    
        <div class="mt-4 flex flex-col gap-y-4 sm:flex-row sm:gap-x-4 sm:gap-y-0">
          <CalendarGrid v-for="month in grid" :key="month.value.toString()">
            <CalendarGridHead>
              <CalendarGridRow>
                <CalendarHeadCell v-for="day in weekDays" :key="day">
                  {{ day }}
                </CalendarHeadCell>
              </CalendarGridRow>
            </CalendarGridHead>
            <CalendarGridBody>
              <CalendarGridRow v-for="(weekDates, index) in month.rows" :key="`weekDate-${index}`" class="mt-2 w-full">
                <CalendarCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
                  <CalendarCellTrigger :day="weekDate" :month="month.value">
                    <slot name="cell" :day="weekDate" :month="month.value">
                      {{ weekDate.day }}
                    </slot>
                  </CalendarCellTrigger>
                </CalendarCell>
              </CalendarGridRow>
            </CalendarGridBody>
          </CalendarGrid>
        </div>
      </CalendarRoot>
    </template>
  • app/components/ui/calendar/CalendarCell.vue 0.8 kB
    <script lang="ts" setup>
    import type { CalendarCellProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarCell, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarCellProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarCell
        data-uipkge
        data-slot="calendar-cell"
        :class="
          cn(
            '[&:has([data-selected])]:bg-accent relative flex-1 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md',
            props.class,
          )
        "
        v-bind="forwardedProps"
      >
        <slot />
      </CalendarCell>
    </template>
  • app/components/ui/calendar/CalendarCellTrigger.vue 1.6 kB
    <script lang="ts" setup>
    import type { CalendarCellTriggerProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarCellTrigger, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    import { buttonVariants } from '@/components/ui/button'
    
    const props = withDefaults(defineProps<CalendarCellTriggerProps & { class?: HTMLAttributes['class'] }>(), {
      as: 'button',
    })
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarCellTrigger
        data-uipkge
        data-slot="calendar-cell-trigger"
        :class="
          cn(
            buttonVariants({ variant: 'ghost' }),
            'size-9 cursor-pointer p-0 font-normal aria-selected:opacity-100',
            '[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
            // Selected
            'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground data-[selected]:opacity-100',
            // Disabled
            'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
            // Unavailable
            'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
            // Outside months
            'data-[outside-view]:text-muted-foreground',
            props.class,
          )
        "
        v-bind="forwardedProps"
      >
        <slot />
      </CalendarCellTrigger>
    </template>
  • app/components/ui/calendar/CalendarGrid.vue 0.7 kB
    <script lang="ts" setup>
    import type { CalendarGridProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarGrid, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarGridProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarGrid
        data-uipkge
        data-slot="calendar-grid"
        :class="cn('w-full border-collapse space-x-1', props.class)"
        v-bind="forwardedProps"
      >
        <slot />
      </CalendarGrid>
    </template>
  • app/components/ui/calendar/CalendarGridBody.vue 0.3 kB
    <script lang="ts" setup>
    import type { CalendarGridBodyProps } from 'reka-ui'
    import { CalendarGridBody } from 'reka-ui'
    
    const props = defineProps<CalendarGridBodyProps>()
    </script>
    
    <template>
      <CalendarGridBody data-uipkge data-slot="calendar-grid-body" v-bind="props">
        <slot />
      </CalendarGridBody>
    </template>
  • app/components/ui/calendar/CalendarGridHead.vue 0.4 kB
    <script lang="ts" setup>
    import type { CalendarGridHeadProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { CalendarGridHead } from 'reka-ui'
    
    const props = defineProps<CalendarGridHeadProps & { class?: HTMLAttributes['class'] }>()
    </script>
    
    <template>
      <CalendarGridHead data-uipkge data-slot="calendar-grid-head" v-bind="props">
        <slot />
      </CalendarGridHead>
    </template>
  • app/components/ui/calendar/CalendarGridRow.vue 0.6 kB
    <script lang="ts" setup>
    import type { CalendarGridRowProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarGridRow, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarGridRowProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarGridRow data-uipkge data-slot="calendar-grid-row" :class="cn('flex', props.class)" v-bind="forwardedProps">
        <slot />
      </CalendarGridRow>
    </template>
  • app/components/ui/calendar/CalendarHeadCell.vue 0.7 kB
    <script lang="ts" setup>
    import type { CalendarHeadCellProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarHeadCell, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarHeadCellProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarHeadCell
        data-uipkge
        data-slot="calendar-head-cell"
        :class="cn('text-muted-foreground flex-1 rounded-md text-xs font-normal', props.class)"
        v-bind="forwardedProps"
      >
        <slot />
      </CalendarHeadCell>
    </template>
  • app/components/ui/calendar/CalendarHeader.vue 0.7 kB
    <script lang="ts" setup>
    import type { CalendarHeaderProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarHeader, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarHeaderProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarHeader
        data-uipkge
        data-slot="calendar-header"
        :class="cn('relative flex w-full items-center justify-center px-8 pt-1', props.class)"
        v-bind="forwardedProps"
      >
        <slot />
      </CalendarHeader>
    </template>
  • app/components/ui/calendar/CalendarHeading.vue 0.8 kB
    <script lang="ts" setup>
    import type { CalendarHeadingProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { CalendarHeading, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<CalendarHeadingProps & { class?: HTMLAttributes['class'] }>()
    
    defineSlots<{
      default: (props: { headingValue: string }) => any
    }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarHeading
        v-slot="{ headingValue }"
        data-uipkge
        data-slot="calendar-heading"
        :class="cn('text-sm font-medium', props.class)"
        v-bind="forwardedProps"
      >
        <slot :heading-value>
          {{ headingValue }}
        </slot>
      </CalendarHeading>
    </template>
  • app/components/ui/calendar/CalendarNextButton.vue 1 kB
    <script lang="ts" setup>
    import type { CalendarNextProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { ChevronRight } from 'lucide-vue-next'
    import { CalendarNext, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    import { buttonVariants } from '@/components/ui/button'
    
    const props = defineProps<CalendarNextProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarNext
        data-uipkge
        data-slot="calendar-next-button"
        :class="
          cn(
            buttonVariants({ variant: 'outline' }),
            'size-9 bg-transparent p-0 opacity-70 hover:opacity-100 focus-visible:opacity-100',
            props.class,
          )
        "
        v-bind="forwardedProps"
      >
        <slot>
          <ChevronRight class="size-4" aria-hidden="true" />
        </slot>
      </CalendarNext>
    </template>
  • app/components/ui/calendar/CalendarPrevButton.vue 1 kB
    <script lang="ts" setup>
    import type { CalendarPrevProps } from 'reka-ui'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { ChevronLeft } from 'lucide-vue-next'
    import { CalendarPrev, useForwardProps } from 'reka-ui'
    import { cn } from '@/lib/utils'
    import { buttonVariants } from '@/components/ui/button'
    
    const props = defineProps<CalendarPrevProps & { class?: HTMLAttributes['class'] }>()
    
    const delegatedProps = reactiveOmit(props, 'class')
    
    const forwardedProps = useForwardProps(delegatedProps)
    </script>
    
    <template>
      <CalendarPrev
        data-uipkge
        data-slot="calendar-prev-button"
        :class="
          cn(
            buttonVariants({ variant: 'outline' }),
            'size-9 bg-transparent p-0 opacity-70 hover:opacity-100 focus-visible:opacity-100',
            props.class,
          )
        "
        v-bind="forwardedProps"
      >
        <slot>
          <ChevronLeft class="size-4" aria-hidden="true" />
        </slot>
      </CalendarPrev>
    </template>
  • app/components/ui/calendar/NativeSelect.vue 4.8 kB
    <script setup lang="ts">
    import type { AcceptableValue } from 'reka-ui'
    import { computed } from 'vue'
    import type { HTMLAttributes } from 'vue'
    import { reactiveOmit } from '@vueuse/core'
    import { ChevronDownIcon } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    
    defineOptions({
      inheritAttrs: false,
    })
    
    interface Props {
      modelValue?: AcceptableValue | AcceptableValue[]
      class?: HTMLAttributes['class']
      label?: string
      placeholder?: string
      disabled?: boolean
      readonly?: boolean
      error?: boolean
      errorMessages?: string | string[]
      successMessages?: string | string[]
      hint?: string
      variant?: 'outlined' | 'filled' | 'solo' | 'underlined'
      density?: 'compact' | 'default' | 'comfortable'
      color?: 'primary' | 'secondary' | 'error' | 'success' | 'warning' | 'info'
      hideDetails?: boolean
      bgColor?: string
    }
    
    const props = withDefaults(defineProps<Props>(), {
      modelValue: undefined,
      label: '',
      placeholder: '',
      disabled: false,
      readonly: false,
      error: false,
      errorMessages: () => [],
      successMessages: () => [],
      hint: '',
      variant: 'outlined',
      density: 'default',
      color: 'primary',
      hideDetails: false,
    })
    
    const emit = defineEmits<{
      'update:modelValue': AcceptableValue
    }>()
    
    const modelValue = computed({
      get() {
        return props.modelValue
      },
      set(value) {
        emit('update:modelValue', value)
      },
    })
    
    const delegatedProps = reactiveOmit(
      props,
      'class',
      'label',
      'placeholder',
      'disabled',
      'readonly',
      'error',
      'errorMessages',
      'successMessages',
      'hint',
      'variant',
      'density',
      'color',
      'hideDetails',
      'bgColor',
      'modelValue',
    )
    
    const hasError = computed(
      () => props.error || (Array.isArray(props.errorMessages) ? props.errorMessages.length > 0 : !!props.errorMessages),
    )
    const hasSuccess = computed(
      () =>
        !hasError.value &&
        (Array.isArray(props.successMessages) ? props.successMessages.length > 0 : !!props.successMessages),
    )
    
    const densityClasses = {
      compact: 'h-8 text-xs',
      default: 'h-9 text-sm',
      comfortable: 'h-10 text-base',
    }
    
    const variantClasses = {
      outlined: 'border bg-transparent',
      filled: 'border-b-2 bg-muted/30 border-transparent',
      solo: 'border bg-card shadow-md',
      underlined: 'border-b bg-transparent rounded-none border-x-0 border-t-0',
    }
    
    const stateClasses = computed(() => {
      if (hasError.value) return 'border-destructive focus-visible:ring-destructive/20'
      if (hasSuccess.value) return 'border-[var(--success)] focus-visible:border-[var(--success)]'
      return 'border-input focus-visible:border-ring focus-visible:ring-ring/20 focus-visible:ring-[3px]'
    })
    </script>
    
    <template>
      <div class="relative w-full" :class="densityClasses[density]">
        <!-- Label -->
        <label v-if="label" class="mb-1 block text-sm font-medium" :class="{ 'text-destructive': hasError }">
          {{ label }}
        </label>
    
        <!-- Select Wrapper -->
        <div class="group/native-select relative w-full" :class="props.class">
          <select
            v-bind="{ ...$attrs, ...delegatedProps }"
            v-model="modelValue"
            data-uipkge
            data-slot="native-select"
            :disabled="disabled"
            :readonly="readonly"
            :class="
              cn(
                'border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50',
                variantClasses[variant],
                stateClasses,
                densityClasses[density],
                'w-full min-w-0 appearance-none rounded-md border bg-transparent px-3 pr-9 shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
                bgColor,
              )
            "
          >
            <slot />
          </select>
          <ChevronDownIcon
            class="text-muted-foreground pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 opacity-50 select-none"
            :class="{
              'top-1/2': density === 'default' || density === 'compact' || density === 'comfortable',
            }"
            aria-hidden="true"
            data-uipkge
            data-slot="native-select-icon"
          />
        </div>
    
        <!-- Details: hint, error, success -->
        <div
          v-if="!hideDetails"
          class="mt-1 text-xs"
          :class="{
            'text-destructive': hasError,
            'text-[var(--success)]': hasSuccess,
            'text-muted-foreground': !hasError && !hasSuccess,
          }"
        >
          <template v-if="hasError">
            <span v-if="typeof errorMessages === 'string'">{{ errorMessages }}</span>
            <span v-else>{{ errorMessages[0] }}</span>
          </template>
          <template v-else-if="hasSuccess">
            <span v-if="typeof successMessages === 'string'">{{ successMessages }}</span>
            <span v-else>{{ successMessages[0] }}</span>
          </template>
          <template v-else>{{ hint }}</template>
        </div>
      </div>
    </template>
  • app/components/ui/calendar/NativeSelectOption.vue 0.4 kB
    <!-- @fallthroughAttributes true -->
    <!-- @strictTemplates true -->
    
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    
    const props = defineProps<{ class?: HTMLAttributes['class'] }>()
    const dataAttrs = { 'data-slot': 'native-select-option' }
    </script>
    
    <template>
      <option v-bind="dataAttrs" :class="cn('bg-popover text-popover-foreground', props.class)">
        <slot />
      </option>
    </template>
  • app/components/ui/calendar/index.ts 1 kB
    export { default as Calendar } from './Calendar.vue'
    export { default as CalendarCell } from './CalendarCell.vue'
    export { default as CalendarCellTrigger } from './CalendarCellTrigger.vue'
    export { default as CalendarGrid } from './CalendarGrid.vue'
    export { default as CalendarGridBody } from './CalendarGridBody.vue'
    export { default as CalendarGridHead } from './CalendarGridHead.vue'
    export { default as CalendarGridRow } from './CalendarGridRow.vue'
    export { default as CalendarHeadCell } from './CalendarHeadCell.vue'
    export { default as CalendarHeader } from './CalendarHeader.vue'
    export { default as CalendarHeading } from './CalendarHeading.vue'
    export { default as CalendarNextButton } from './CalendarNextButton.vue'
    export { default as CalendarPrevButton } from './CalendarPrevButton.vue'
    export { default as NativeSelect } from './NativeSelect.vue'
    export { default as NativeSelectOption } from './NativeSelectOption.vue'
    
    export type LayoutTypes = 'month-and-year' | 'month-only' | 'year-only' | undefined

Raw manifest: https://uipkge.dev/r/vue/calendar.json