Range Calendar
AlphaS | M | T | W | T | F | S |
---|---|---|---|---|---|---|
29 | 30 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import type { DateValue } from '@internationalized/date'
import { RangeCalendarCell, RangeCalendarCellTrigger, RangeCalendarGrid, RangeCalendarGridBody, RangeCalendarGridHead, RangeCalendarGridRow, RangeCalendarHeadCell, RangeCalendarHeader, RangeCalendarHeading, RangeCalendarNext, RangeCalendarPrev, RangeCalendarRoot } from 'radix-vue'
function isDateUnavailable(date: DateValue) {
return date.day === 17 || date.day === 18
}
</script>
<template>
<RangeCalendarRoot
v-slot="{ weekDays, grid }"
:is-date-unavailable="isDateUnavailable"
class="rounded-xl bg-white p-4 shadow-xl"
fixed-weeks
>
<RangeCalendarHeader class="flex items-center justify-between">
<RangeCalendarPrev
class="inline-flex items-center cursor-pointer text-black justify-center rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-left"
class="w-6 h-6"
/>
</RangeCalendarPrev>
<RangeCalendarHeading class="text-[15px] text-black font-medium" />
<RangeCalendarNext
class="inline-flex items-center cursor-pointer justify-center text-black rounded-[9px] bg-transparent w-8 h-8 hover:bg-black hover:text-white active:scale-98 active:transition-all focus:shadow-[0_0_0_2px] focus:shadow-black"
>
<Icon
icon="radix-icons:chevron-right"
class="w-6 h-6"
/>
</RangeCalendarNext>
</RangeCalendarHeader>
<div
class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0"
>
<RangeCalendarGrid
v-for="month in grid"
:key="month.value.toString()"
class="w-full border-collapse select-none space-y-1"
>
<RangeCalendarGridHead>
<RangeCalendarGridRow class="mb-1 grid w-full grid-cols-7">
<RangeCalendarHeadCell
v-for="day in weekDays"
:key="day"
class="rounded-md text-xs text-green8"
>
{{ day }}
</RangeCalendarHeadCell>
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody class="grid">
<RangeCalendarGridRow
v-for="(weekDates, index) in month.rows"
:key="`weekDate-${index}`"
class="grid grid-cols-7"
>
<RangeCalendarCell
v-for="weekDate in weekDates"
:key="weekDate.toString()"
:date="weekDate"
>
<RangeCalendarCellTrigger
:day="weekDate"
:month="month.value"
class="relative flex items-center justify-center rounded-full whitespace-nowrap text-sm font-normal text-black w-8 h-8 outline-none focus:shadow-[0_0_0_2px] focus:shadow-black data-[disabled]:text-black/30 data-[selected]:!bg-green10 data-[selected]:text-white hover:bg-green5 data-[highlighted]:bg-green5 data-[unavailable]:pointer-events-none data-[unavailable]:text-black/30 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-green9 "
/>
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</div>
</RangeCalendarRoot>
</template>
Credit
This component was built taking inspiration from the implementation in melt-ui.
Features
- Full keyboard navigation
- Can be controlled or uncontrolled
- Focus is fully managed
- Localization support
- Highly composable
Preface
The component depends on the @internationalized/date package, which solves a lot of the problems that come with working with dates and times in JavaScript.
We highly recommend reading through the documentation for the package to get a solid feel for how it works, and you'll need to install it in your project to use the date-related components.
Installation
Install the date package.
$ npm add @internationalized/date
Install the component from your command line.
$ npm add radix-vue
Anatomy
Import all parts and piece them together.
<script setup>
import {
RangeCalendarCell,
RangeCalendarCellTrigger,
RangeCalendarGrid,
RangeCalendarGridBody,
RangeCalendarGridHead,
RangeCalendarGridRow,
RangeCalendarHeadCell,
RangeCalendarHeader,
RangeCalendarHeading,
RangeCalendarNext,
RangeCalendarPrev,
RangeCalendarRoot
} from 'radix-vue'
</script>
<template>
<RangeCalendarRoot>
<RangeCalendarHeader>
<RangeCalendarPrev />
<RangeCalendarHeading />
<RangeCalendarNext />
</RangeCalendarHeader>
<RangeCalendarGrid>
<RangeCalendarGridHead>
<RangeCalendarGridRow>
<RangeCalendarHeadCell />
</RangeCalendarGridRow>
</RangeCalendarGridHead>
<RangeCalendarGridBody>
<RangeCalendarGridRow>
<RangeCalendarCell>
<RangeCalendarCellTrigger />
</RangeCalendarCell>
</RangeCalendarGridRow>
</RangeCalendarGridBody>
</RangeCalendarGrid>
</RangeCalendarRoot>
</template>
API Reference
Root
Contains all the parts of a calendar
Prop | Default | Type |
---|---|---|
allowNonContiguousRanges | false | boolean When combined with isDateUnavailable, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. |
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
calendarLabel | string The accessible label for the calendar | |
defaultPlaceholder | DateValue The default placeholder date | |
defaultValue | { start: undefined, end: undefined } | DateRange The default value for the calendar |
dir | 'ltr' | 'rtl' The reading direction of the calendar when applicable. | |
disabled | false | boolean Whether or not the calendar is disabled |
fixedWeeks | false | boolean Whether or not to always display 6 weeks in the calendar |
initialFocus | false | boolean If true, the calendar will focus the selected day, today, or the first day of the month depending on what is visible when the calendar is mounted |
isDateDisabled | Matcher A function that returns whether or not a date is disabled | |
isDateUnavailable | Matcher A function that returns whether or not a date is unavailable | |
locale | 'en' | string The locale to use for formatting dates |
maxValue | DateValue The maximum date that can be selected | |
minValue | DateValue The minimum date that can be selected | |
modelValue | DateRange The controlled checked state of the calendar. Can be bound as | |
nextPage | ((placeholder: DateValue) => DateValue) A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component. | |
numberOfMonths | 1 | number The number of months to display at once |
pagedNavigation | false | boolean This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month |
placeholder | DateValue The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view | |
preventDeselect | false | boolean Whether or not to prevent the user from deselecting a date without selecting another date first |
prevPage | ((placeholder: DateValue) => DateValue) A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component. | |
readonly | false | boolean Whether or not the calendar is readonly |
weekdayFormat | 'narrow' | 'narrow' | 'short' | 'long' The format to use for the weekday strings provided via the weekdays slot prop |
weekStartsOn | 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 The day of the week to start the calendar on |
Emit | Payload |
---|---|
update:modelValue | [date: DateRange] Event handler called whenever the model value changes |
update:placeholder | [date: DateValue] Event handler called whenever the placeholder value changes |
update:startValue | [date: DateValue] Event handler called whenever the start value changes |
Slots (default) | Payload |
---|---|
date | DateValue The current date of the placeholder |
grid | Grid<DateValue>[] The grid of dates |
weekDays | string[] The days of the week |
weekStartsOn | 0 | 1 | 2 | 3 | 4 | 5 | 6 The start of the week |
locale | string The calendar locale |
fixedWeeks | boolean Whether or not to always display 6 weeks in the calendar |
Data Attribute | Value |
---|---|
[data-readonly] | Present when readonly |
[data-disabled] | Present when disabled |
[data-invalid] | Present when invalid |
Header
Contains the navigation buttons and the heading segments.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Prev Button
Calendar navigation button. It navigates the calendar one month/year/decade in the past based on the current calendar view.
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
Prop | Default | Type |
---|---|---|
as | 'button' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
prevPage | ((placeholder: DateValue) => DateValue) The function to be used for the prev page. Overwrites the | |
step | 'month' | 'year' The calendar unit to go forward |
Next Button
Calendar navigation button. It navigates the calendar one month/year/decade in the future based on the current calendar view.
Prop | Default | Type |
---|---|---|
as | 'button' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
nextPage | ((placeholder: DateValue) => DateValue) The function to be used for the next page. Overwrites the | |
step | 'month' | 'year' The calendar unit to go forward |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
Heading
Heading for displaying the current month and year.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Slots (default) | Payload |
---|---|
headingValue | string Current month and year |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
Grid
Container for wrapping the calendar grid.
Prop | Default | Type |
---|---|---|
as | 'table' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Data Attribute | Value |
---|---|
[data-readonly] | Present when readonly |
[data-disabled] | Present when disabled |
Grid Head
Container for wrapping the grid head.
Prop | Default | Type |
---|---|---|
as | 'thead' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Grid Body
Container for wrapping the grid body.
Prop | Default | Type |
---|---|---|
as | 'tbody' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Grid Row
Container for wrapping the grid row.
Prop | Default | Type |
---|---|---|
as | 'tr' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Head Cell
Container for wrapping the head cell. Used for displaying the week days.
Prop | Default | Type |
---|---|---|
as | 'th' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Cell
Container for wrapping the calendar cells.
Prop | Default | Type |
---|---|---|
as | 'td' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
date* | DateValue |
Data Attribute | Value |
---|---|
[data-disabled] | Present when disabled |
Cell Trigger
Interactable container for displaying the cell dates. Clicking it selects the date.
Prop | Default | Type |
---|---|---|
as | 'div' | AsTag | Component The element or component this component should render as. Can be overwrite by |
asChild | boolean Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
day* | DateValue | |
month* | DateValue |
Slots (default) | Payload |
---|---|
dayValue | string Current day |
Data Attribute | Value |
---|---|
[data-selected] | Present when selected |
[data-value] | The ISO string value of the date. |
[data-disabled] | Present when disabled |
[data-unavailable] | Present when unavailable |
[data-today] | Present when today |
[data-outside-view] | Present when the date is outside the current month it is displayed in. |
[data-outside-visible-view] | Present when the date is outside the months that are visible on the calendar. |
[data-selection-start] | Present when the date is the start of the selection. |
[data-selection-end] | Present when the date is the end of the selection. |
[data-highlighted] | Present when the date is highlighted by the user as they select a range. |
[data-highlighted-start] | Present when the date is the start of the range that is highlighted by the user. |
[data-highlighted-end] | Present when the date is the end of the range that is highlighted by the user. |
[data-focused] | Present when focused |
Accessibility
Keyboard Interactions
Key | Description |
---|---|
Tab | When focus moves onto the calendar, focuses the first navigation button. |
Space |
When the focus is on either CalendarNext or CalendarPrev , it navigates the calendar. Otherwise, it selects the date.
|
Enter |
When the focus is on either CalendarNext or CalendarPrev , it navigates the calendar. Otherwise, it selects the date.
|
ArrowLeftArrowRightArrowUpArrowDown |
When the focus is on CalendarCellTrigger , it navigates the dates, changing the month/year/decade if necessary.
|