InputDate

An input component for date selection.

Usage

Use the v-model directive to control the selected date.

2
3
2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'

const value = shallowRef(new CalendarDate(2022, 2, 3))
</script>

<template>
  <PInputDate v-model="value" />
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
import { CalendarDate } from '@internationalized/date'

const value = shallowRef(new CalendarDate(2022, 2, 3))
</script>

<template>
  <PInputDate v-model="value" />
</template>

Use the default-value prop to set the initial value when you do not need to control its state.

2
6
2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'

const defaultValue = shallowRef(new CalendarDate(2022, 2, 6))
</script>

<template>
  <PInputDate :default-value="defaultValue" />
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
import { CalendarDate } from '@internationalized/date'

const defaultValue = shallowRef(new CalendarDate(2022, 2, 6))
</script>

<template>
  <PInputDate :default-value="defaultValue" />
</template>
This component uses the @internationalized/date package for locale-aware formatting. The date format is determined by the locale prop of the App component.
This component uses the @internationalized/date package for locale-aware formatting. The date format is determined by the locale prop of the App component.

Range

Use the range prop to select a range of dates.

2
3
2022
2
20
2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date'

const value = shallowRef({
  start: new CalendarDate(2022, 2, 3),
  end: new CalendarDate(2022, 2, 20)
})
</script>

<template>
  <PInputDate range v-model="value" />
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
import { CalendarDate } from '@internationalized/date'

const value = shallowRef({
  start: new CalendarDate(2022, 2, 3),
  end: new CalendarDate(2022, 2, 20)
})
</script>

<template>
  <PInputDate range v-model="value" />
</template>

Color

Use the color prop to change the color of the InputDate.

mm
dd
yyyy
<template>
  <PInputDate color="neutral" highlight />
</template>

Variant

Use the variant prop to change the variant of the InputDate.

mm
dd
yyyy
<template>
  <PInputDate variant="subtle" />
</template>

Size

Use the size prop to change the size of the InputDate.

mm
dd
yyyy
<template>
  <PInputDate size="xl" />
</template>

Icon

Use the icon prop to show an Icon inside the InputDate.

mm
dd
yyyy
<template>
  <PInputDate icon="i-lucide-calendar" />
</template>
Use the leading and trailing props to set the icon position or the leading-icon and trailing-icon props to set a different icon for each position.

Separator Icon

Use the separator-icon prop to change the Icon of the range separator. Defaults to i-lucide-minus.

mm
dd
yyyy
mm
dd
yyyy
<template>
  <PInputDate range separator-icon="i-lucide-arrow-right" />
</template>
You can customize this icon globally in your app.config.ts under ui.icons.minus key.
You can customize this icon globally in your vite.config.ts under ui.icons.minus key.

Avatar

Use the avatar prop to show an Avatar inside the InputDate.

mm
dd
yyyy
<template>
  <PInputDate
    :avatar="{
      src: 'https://github.com/vuejs.png',
      loading: 'lazy'
    }"
    size="md"
    variant="outline"
  />
</template>

Disabled

Use the disabled prop to disable the InputDate.

mm
dd
yyyy
<template>
  <PInputDate disabled />
</template>

Examples

With unavailable dates

Use the is-date-unavailable prop with a function to mark specific dates as unavailable.

1
1
2022
1
9
2022
<script setup lang="ts">
import type { DateValue } from '@internationalized/date';
import { CalendarDate } from '@internationalized/date';

const modelValue = shallowRef({
  start: new CalendarDate(2022, 1, 1),
  end: new CalendarDate(2022, 1, 9),
});

function isDateUnavailable(date: DateValue) {
  return date.day >= 10 && date.day <= 16;
}
</script>

<template>
  <PInputDate
    v-model="modelValue"
    :is-date-unavailable="isDateUnavailable"
    range
  />
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
import type { DateValue } from '@internationalized/date';
import { CalendarDate } from '@internationalized/date';

const modelValue = shallowRef({
  start: new CalendarDate(2022, 1, 1),
  end: new CalendarDate(2022, 1, 9),
});

function isDateUnavailable(date: DateValue) {
  return date.day >= 10 && date.day <= 16;
}
</script>

<template>
  <PInputDate
    v-model="modelValue"
    :is-date-unavailable="isDateUnavailable"
    range
  />
</template>

With min/max dates

Use the min-value and max-value props to limit the dates.

9
10
2023
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';

const modelValue = shallowRef(new CalendarDate(2023, 9, 10));
const minDate = new CalendarDate(2023, 9, 1);
const maxDate = new CalendarDate(2023, 9, 30);
</script>

<template>
  <PInputDate
    v-model="modelValue"
    :min-value="minDate"
    :max-value="maxDate"
  />
</template>
<script setup lang="ts">
import { shallowRef } from 'vue'
import { CalendarDate } from '@internationalized/date';

const modelValue = shallowRef(new CalendarDate(2023, 9, 10));
const minDate = new CalendarDate(2023, 9, 1);
const maxDate = new CalendarDate(2023, 9, 30);
</script>

<template>
  <PInputDate
    v-model="modelValue"
    :min-value="minDate"
    :max-value="maxDate"
  />
</template>

As a date picker

Use a Calendar and a Popover component to create a date picker.

1
10
2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';

const inputDate = useTemplateRef('inputDate');

const modelValue = shallowRef(new CalendarDate(2022, 1, 10));
</script>

<template>
  <PInputDate
    ref="inputDate"
    v-model="modelValue"
  >
    <template #trailing>
      <PPopover :reference="inputDate?.inputsRef[3]?.$el">
        <PButton
          color="neutral"
          variant="link"
          size="sm"
          icon="i-lucide-calendar"
          aria-label="Select a date"
          class="px-0"
        />

        <template #content>
          <PCalendar
            v-model="modelValue"
            class="p-2"
          />
        </template>
      </PPopover>
    </template>
  </PInputDate>
</template>
<script setup lang="ts">
import { shallowRef, useTemplateRef } from 'vue'
import { CalendarDate } from '@internationalized/date';

const inputDate = useTemplateRef('inputDate');

const modelValue = shallowRef(new CalendarDate(2022, 1, 10));
</script>

<template>
  <PInputDate
    ref="inputDate"
    v-model="modelValue"
  >
    <template #trailing>
      <PPopover :reference="inputDate?.inputsRef[3]?.$el">
        <PButton
          color="neutral"
          variant="link"
          size="sm"
          icon="i-lucide-calendar"
          aria-label="Select a date"
          class="px-0"
        />

        <template #content>
          <PCalendar
            v-model="modelValue"
            class="p-2"
          />
        </template>
      </PPopover>
    </template>
  </PInputDate>
</template>

As a date range picker

Use a Calendar and a Popover component to create a date range picker.

1
10
2022
1
20
2022
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';

const inputDate = useTemplateRef('inputDate');

const modelValue = shallowRef({
  start: new CalendarDate(2022, 1, 10),
  end: new CalendarDate(2022, 1, 20),
});
</script>

<template>
  <PInputDate
    ref="inputDate"
    v-model="modelValue"
    range
  >
    <template #trailing>
      <PPopover :reference="inputDate?.inputsRef[0]?.$el">
        <PButton
          color="neutral"
          variant="link"
          size="sm"
          icon="i-lucide-calendar"
          aria-label="Select a date range"
          class="px-0"
        />

        <template #content>
          <PCalendar
            v-model="modelValue"
            class="p-2"
            :number-of-months="2"
            range
          />
        </template>
      </PPopover>
    </template>
  </PInputDate>
</template>
<script setup lang="ts">
import { shallowRef, useTemplateRef } from 'vue'
import { CalendarDate } from '@internationalized/date';

const inputDate = useTemplateRef('inputDate');

const modelValue = shallowRef({
  start: new CalendarDate(2022, 1, 10),
  end: new CalendarDate(2022, 1, 20),
});
</script>

<template>
  <PInputDate
    ref="inputDate"
    v-model="modelValue"
    range
  >
    <template #trailing>
      <PPopover :reference="inputDate?.inputsRef[0]?.$el">
        <PButton
          color="neutral"
          variant="link"
          size="sm"
          icon="i-lucide-calendar"
          aria-label="Select a date range"
          class="px-0"
        />

        <template #content>
          <PCalendar
            v-model="modelValue"
            class="p-2"
            :number-of-months="2"
            range
          />
        </template>
      </PPopover>
    </template>
  </PInputDate>
</template>

API

Props

Prop Default Type

Slots

Slot Type

Emits

Event Type

Theme

app.config.ts
export default defineAppConfig({
  ui: {
    inputDate: {
      slots: {
        base: [
          'group relative inline-flex items-center rounded-md select-none',
          'transition-colors'
        ],
        leading: 'absolute inset-y-0 start-0 flex items-center',
        leadingIcon: 'shrink-0 color-text-dimmed',
        leadingAvatar: 'shrink-0',
        leadingAvatarSize: '',
        trailing: 'absolute inset-y-0 end-0 flex items-center',
        trailingIcon: 'shrink-0 color-text-dimmed',
        segment: [
          'data-[placeholder]:color-text-dimmed data-[segment=literal]:color-text-muted data-[invalid]:color-error text-center outline-hidden rounded-sm data-[disabled]:opacity-75 data-[disabled]:cursor-not-allowed',
          'transition-colors'
        ],
        separatorIcon: 'shrink-0 size-4 color-text-muted'
      },
      variants: {
        fieldGroup: {
          horizontal: 'first:not-[*:only-child]:rounded-e-none last:not-[*:only-child]:rounded-s-none not-last:not-first:rounded-none focus-visible:z-1',
          vertical: 'first:not-[*:only-child]:rounded-b-none last:not-[*:only-child]:rounded-t-none not-last:not-first:rounded-none focus-visible:z-1'
        },
        size: {
          xs: {
            base: [
              'px-2 py-1 text-sm/4 gap-1',
              'pohon:gap-0.25'
            ],
            leading: 'ps-2',
            trailing: 'pe-2',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4',
            segment: 'data-[segment=day]:w-8 data-[segment=month]:w-8 data-[segment=year]:w-10'
          },
          sm: {
            base: [
              'px-2.5 py-1.5 text-sm/4 gap-1.5',
              'pohon:gap-0.5'
            ],
            leading: 'ps-2.5',
            trailing: 'pe-2.5',
            leadingIcon: 'size-4',
            leadingAvatarSize: '3xs',
            trailingIcon: 'size-4',
            segment: 'data-[segment=day]:w-8 data-[segment=month]:w-8 data-[segment=year]:w-10'
          },
          md: {
            base: [
              'px-2.5 py-1.5 text-base/5 gap-1.5',
              'pohon:gap-0.5'
            ],
            leading: 'ps-2.5',
            trailing: 'pe-2.5',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5',
            segment: 'data-[segment=day]:w-9 data-[segment=month]:w-9 data-[segment=year]:w-11'
          },
          lg: {
            base: [
              'px-3 py-2 text-base/5 gap-2',
              'pohon:gap-0.75'
            ],
            leading: 'ps-3',
            trailing: 'pe-3',
            leadingIcon: 'size-5',
            leadingAvatarSize: '2xs',
            trailingIcon: 'size-5',
            segment: 'data-[segment=day]:w-9 data-[segment=month]:w-9 data-[segment=year]:w-11'
          },
          xl: {
            base: [
              'px-3 py-2 text-base gap-2',
              'pohon:gap-0.75'
            ],
            leading: 'ps-3',
            trailing: 'pe-3',
            leadingIcon: 'size-6',
            leadingAvatarSize: 'xs',
            trailingIcon: 'size-6',
            segment: 'data-[segment=day]:w-10 data-[segment=month]:w-10 data-[segment=year]:w-12'
          }
        },
        variant: {
          outline: 'color-text-highlighted bg-background ring ring-inset ring-border-accented',
          soft: 'color-text-highlighted bg-background-elevated/50 hover:bg-background-elevated has-focus:bg-background-elevated disabled:bg-background-elevated/50',
          subtle: 'color-text-highlighted bg-background-elevated ring ring-inset ring-border-accented',
          ghost: 'color-text-highlighted bg-transparent hover:bg-background-elevated has-focus:bg-background-elevated disabled:bg-transparent dark:disabled:bg-transparent',
          none: 'color-text-highlighted bg-transparent has-focus:outline-none'
        },
        color: {
          primary: '',
          secondary: '',
          success: '',
          info: '',
          warning: '',
          error: '',
          neutral: ''
        },
        leading: {
          true: ''
        },
        trailing: {
          true: ''
        },
        loading: {
          true: ''
        },
        highlight: {
          true: ''
        },
        fixed: {
          false: ''
        },
        type: {
          file: 'file:me-1.5 file:font-500 file:color-text-muted file:outline-none'
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
        },
        {
          color: 'primary',
          variant: [
            'soft',
            'ghost'
          ],
          class: 'outline-primary/25 has-focus-visible:outline-3'
        },
        {
          color: 'primary',
          highlight: true,
          class: 'ring ring-inset pohon:ring-primary'
        },
        {
          color: 'neutral',
          variant: [
            'outline',
            'subtle'
          ],
          class: 'outline-border-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-border-inverted'
        },
        {
          color: 'neutral',
          variant: [
            'soft',
            'ghost'
          ],
          class: 'outline-border-inverted/25 has-focus-visible:outline-3'
        },
        {
          color: 'neutral',
          highlight: true,
          class: 'ring ring-inset ring-border-inverted'
        },
        {
          leading: true,
          size: 'xs',
          class: 'ps-7'
        },
        {
          leading: true,
          size: 'sm',
          class: 'ps-8'
        },
        {
          leading: true,
          size: 'md',
          class: 'ps-9'
        },
        {
          leading: true,
          size: 'lg',
          class: 'ps-10'
        },
        {
          leading: true,
          size: 'xl',
          class: 'ps-11'
        },
        {
          trailing: true,
          size: 'xs',
          class: 'pe-7'
        },
        {
          trailing: true,
          size: 'sm',
          class: 'pe-8'
        },
        {
          trailing: true,
          size: 'md',
          class: 'pe-9'
        },
        {
          trailing: true,
          size: 'lg',
          class: 'pe-10'
        },
        {
          trailing: true,
          size: 'xl',
          class: 'pe-11'
        },
        {
          loading: true,
          leading: true,
          class: {
            leadingIcon: 'animate-spin'
          }
        },
        {
          loading: true,
          leading: false,
          trailing: true,
          class: {
            trailingIcon: 'animate-spin'
          }
        },
        {
          fixed: false,
          size: 'xs',
          class: 'md:text-xs'
        },
        {
          fixed: false,
          size: 'sm',
          class: 'md:text-xs'
        },
        {
          fixed: false,
          size: 'md',
          class: 'md:text-sm'
        },
        {
          fixed: false,
          size: 'lg',
          class: 'md:text-sm'
        },
        {
          variant: 'outline',
          class: {
            segment: 'focus:bg-background-elevated'
          }
        },
        {
          variant: 'soft',
          class: {
            segment: 'focus:bg-background-accented/50 group-hover:focus:bg-background-accented'
          }
        },
        {
          variant: 'subtle',
          class: {
            segment: 'focus:bg-background-accented'
          }
        },
        {
          variant: 'ghost',
          class: {
            segment: 'focus:bg-background-elevated group-hover:focus:bg-background-accented'
          }
        },
        {
          variant: 'none',
          class: {
            segment: 'focus:bg-background-elevated'
          }
        }
      ],
      defaultVariants: {
        size: 'md',
        color: 'primary',
        variant: 'outline'
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from 'pohon-ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        inputDate: {
          slots: {
            base: [
              'group relative inline-flex items-center rounded-md select-none',
              'transition-colors'
            ],
            leading: 'absolute inset-y-0 start-0 flex items-center',
            leadingIcon: 'shrink-0 color-text-dimmed',
            leadingAvatar: 'shrink-0',
            leadingAvatarSize: '',
            trailing: 'absolute inset-y-0 end-0 flex items-center',
            trailingIcon: 'shrink-0 color-text-dimmed',
            segment: [
              'data-[placeholder]:color-text-dimmed data-[segment=literal]:color-text-muted data-[invalid]:color-error text-center outline-hidden rounded-sm data-[disabled]:opacity-75 data-[disabled]:cursor-not-allowed',
              'transition-colors'
            ],
            separatorIcon: 'shrink-0 size-4 color-text-muted'
          },
          variants: {
            fieldGroup: {
              horizontal: 'first:not-[*:only-child]:rounded-e-none last:not-[*:only-child]:rounded-s-none not-last:not-first:rounded-none focus-visible:z-1',
              vertical: 'first:not-[*:only-child]:rounded-b-none last:not-[*:only-child]:rounded-t-none not-last:not-first:rounded-none focus-visible:z-1'
            },
            size: {
              xs: {
                base: [
                  'px-2 py-1 text-sm/4 gap-1',
                  'pohon:gap-0.25'
                ],
                leading: 'ps-2',
                trailing: 'pe-2',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs',
                trailingIcon: 'size-4',
                segment: 'data-[segment=day]:w-8 data-[segment=month]:w-8 data-[segment=year]:w-10'
              },
              sm: {
                base: [
                  'px-2.5 py-1.5 text-sm/4 gap-1.5',
                  'pohon:gap-0.5'
                ],
                leading: 'ps-2.5',
                trailing: 'pe-2.5',
                leadingIcon: 'size-4',
                leadingAvatarSize: '3xs',
                trailingIcon: 'size-4',
                segment: 'data-[segment=day]:w-8 data-[segment=month]:w-8 data-[segment=year]:w-10'
              },
              md: {
                base: [
                  'px-2.5 py-1.5 text-base/5 gap-1.5',
                  'pohon:gap-0.5'
                ],
                leading: 'ps-2.5',
                trailing: 'pe-2.5',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs',
                trailingIcon: 'size-5',
                segment: 'data-[segment=day]:w-9 data-[segment=month]:w-9 data-[segment=year]:w-11'
              },
              lg: {
                base: [
                  'px-3 py-2 text-base/5 gap-2',
                  'pohon:gap-0.75'
                ],
                leading: 'ps-3',
                trailing: 'pe-3',
                leadingIcon: 'size-5',
                leadingAvatarSize: '2xs',
                trailingIcon: 'size-5',
                segment: 'data-[segment=day]:w-9 data-[segment=month]:w-9 data-[segment=year]:w-11'
              },
              xl: {
                base: [
                  'px-3 py-2 text-base gap-2',
                  'pohon:gap-0.75'
                ],
                leading: 'ps-3',
                trailing: 'pe-3',
                leadingIcon: 'size-6',
                leadingAvatarSize: 'xs',
                trailingIcon: 'size-6',
                segment: 'data-[segment=day]:w-10 data-[segment=month]:w-10 data-[segment=year]:w-12'
              }
            },
            variant: {
              outline: 'color-text-highlighted bg-background ring ring-inset ring-border-accented',
              soft: 'color-text-highlighted bg-background-elevated/50 hover:bg-background-elevated has-focus:bg-background-elevated disabled:bg-background-elevated/50',
              subtle: 'color-text-highlighted bg-background-elevated ring ring-inset ring-border-accented',
              ghost: 'color-text-highlighted bg-transparent hover:bg-background-elevated has-focus:bg-background-elevated disabled:bg-transparent dark:disabled:bg-transparent',
              none: 'color-text-highlighted bg-transparent has-focus:outline-none'
            },
            color: {
              primary: '',
              secondary: '',
              success: '',
              info: '',
              warning: '',
              error: '',
              neutral: ''
            },
            leading: {
              true: ''
            },
            trailing: {
              true: ''
            },
            loading: {
              true: ''
            },
            highlight: {
              true: ''
            },
            fixed: {
              false: ''
            },
            type: {
              file: 'file:me-1.5 file:font-500 file:color-text-muted file:outline-none'
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              variant: [
                'outline',
                'subtle'
              ],
              class: 'outline-primary/25 has-focus-visible:outline-3 has-focus-visible:ring-primary'
            },
            {
              color: 'primary',
              variant: [
                'soft',
                'ghost'
              ],
              class: 'outline-primary/25 has-focus-visible:outline-3'
            },
            {
              color: 'primary',
              highlight: true,
              class: 'ring ring-inset pohon:ring-primary'
            },
            {
              color: 'neutral',
              variant: [
                'outline',
                'subtle'
              ],
              class: 'outline-border-inverted/25 has-focus-visible:outline-3 has-focus-visible:ring-border-inverted'
            },
            {
              color: 'neutral',
              variant: [
                'soft',
                'ghost'
              ],
              class: 'outline-border-inverted/25 has-focus-visible:outline-3'
            },
            {
              color: 'neutral',
              highlight: true,
              class: 'ring ring-inset ring-border-inverted'
            },
            {
              leading: true,
              size: 'xs',
              class: 'ps-7'
            },
            {
              leading: true,
              size: 'sm',
              class: 'ps-8'
            },
            {
              leading: true,
              size: 'md',
              class: 'ps-9'
            },
            {
              leading: true,
              size: 'lg',
              class: 'ps-10'
            },
            {
              leading: true,
              size: 'xl',
              class: 'ps-11'
            },
            {
              trailing: true,
              size: 'xs',
              class: 'pe-7'
            },
            {
              trailing: true,
              size: 'sm',
              class: 'pe-8'
            },
            {
              trailing: true,
              size: 'md',
              class: 'pe-9'
            },
            {
              trailing: true,
              size: 'lg',
              class: 'pe-10'
            },
            {
              trailing: true,
              size: 'xl',
              class: 'pe-11'
            },
            {
              loading: true,
              leading: true,
              class: {
                leadingIcon: 'animate-spin'
              }
            },
            {
              loading: true,
              leading: false,
              trailing: true,
              class: {
                trailingIcon: 'animate-spin'
              }
            },
            {
              fixed: false,
              size: 'xs',
              class: 'md:text-xs'
            },
            {
              fixed: false,
              size: 'sm',
              class: 'md:text-xs'
            },
            {
              fixed: false,
              size: 'md',
              class: 'md:text-sm'
            },
            {
              fixed: false,
              size: 'lg',
              class: 'md:text-sm'
            },
            {
              variant: 'outline',
              class: {
                segment: 'focus:bg-background-elevated'
              }
            },
            {
              variant: 'soft',
              class: {
                segment: 'focus:bg-background-accented/50 group-hover:focus:bg-background-accented'
              }
            },
            {
              variant: 'subtle',
              class: {
                segment: 'focus:bg-background-accented'
              }
            },
            {
              variant: 'ghost',
              class: {
                segment: 'focus:bg-background-elevated group-hover:focus:bg-background-accented'
              }
            },
            {
              variant: 'none',
              class: {
                segment: 'focus:bg-background-elevated'
              }
            }
          ],
          defaultVariants: {
            size: 'md',
            color: 'primary',
            variant: 'outline'
          }
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

Changelog

No recent changes