|
| 1 | +{/* Copyright 2022 Adobe. All rights reserved. |
| 2 | +This file is licensed to you under the Apache License, Version 2.0 (the "License"); |
| 3 | +you may not use this file except in compliance with the License. You may obtain a copy |
| 4 | +of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| 5 | +Unless required by applicable law or agreed to in writing, software distributed under |
| 6 | +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS |
| 7 | +OF ANY KIND, either express or implied. See the License for the specific language |
| 8 | +governing permissions and limitations under the License. */} |
| 9 | + |
| 10 | +import {Layout} from '@react-spectrum/docs'; |
| 11 | +export default Layout; |
| 12 | + |
| 13 | +import docs from 'docs:@react-spectrum/calendar'; |
| 14 | +import i18nDocs from 'docs:@internationalized/date'; |
| 15 | +import {HeaderInfo, PropTable, PageDescription, TypeLink} from '@react-spectrum/docs'; |
| 16 | +import packageData from '@react-spectrum/calendar/package.json'; |
| 17 | + |
| 18 | +```jsx import |
| 19 | +import {RangeCalendar} from '@react-spectrum/calendar'; |
| 20 | +import {Flex} from '@react-spectrum/layout'; |
| 21 | +``` |
| 22 | + |
| 23 | +--- |
| 24 | +category: Date and Time |
| 25 | +keywords: [date] |
| 26 | +after_version: 3.0.0 |
| 27 | +--- |
| 28 | + |
| 29 | +# RangeCalendar |
| 30 | + |
| 31 | +<PageDescription>{docs.exports.RangeCalendar.description}</PageDescription> |
| 32 | + |
| 33 | +<HeaderInfo |
| 34 | + packageData={packageData} |
| 35 | + componentNames={['RangeCalendar']} |
| 36 | + sourceData={[]} /> |
| 37 | + |
| 38 | +## Example |
| 39 | + |
| 40 | +```tsx example |
| 41 | +<RangeCalendar aria-label="Trip dates" /> |
| 42 | +``` |
| 43 | + |
| 44 | +## Value |
| 45 | + |
| 46 | +A `RangeCalendar` has no selection by default. An initial, uncontrolled value can be provided to the `RangeCalendar` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. |
| 47 | + |
| 48 | +Date ranges are objects with `start` and `end` properties containing date values, which are provided using objects in the `@internationalized/date` package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. |
| 49 | + |
| 50 | +`RangeCalendar` supports values with both date and time components, but only allows users to modify the dates. By default, `RangeCalendar` will emit <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDate} /> objects in the `onChange` event, but if a <TypeLink links={i18nDocs.links} type={i18nDocs.exports.CalendarDateTime} /> or <TypeLink links={i18nDocs.links} type={i18nDocs.exports.ZonedDateTime} /> object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the date and preserving the time components. |
| 51 | + |
| 52 | +```tsx example |
| 53 | +import {parseDate} from '@internationalized/date'; |
| 54 | + |
| 55 | +function Example() { |
| 56 | + let [value, setValue] = React.useState({ |
| 57 | + start: parseDate('2020-02-03'), |
| 58 | + end: parseDate('2020-02-12') |
| 59 | + }); |
| 60 | + |
| 61 | + return ( |
| 62 | + <Flex gap="size-300" wrap> |
| 63 | + <RangeCalendar |
| 64 | + aria-label="Date range (uncontrolled)" |
| 65 | + defaultValue={{ |
| 66 | + start: parseDate('2020-02-03'), |
| 67 | + end: parseDate('2020-02-12') |
| 68 | + }} /> |
| 69 | + <RangeCalendar |
| 70 | + aria-label="Date range (controlled)" |
| 71 | + value={value} |
| 72 | + onChange={setValue} /> |
| 73 | + </Flex> |
| 74 | + ); |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +### International calendars |
| 79 | + |
| 80 | +`RangeCalendar` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the `Provider` component. |
| 81 | + |
| 82 | +Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently. |
| 83 | + |
| 84 | +The below example displays a `RangeCalendar` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar. |
| 85 | + |
| 86 | +```tsx example |
| 87 | +import {Provider} from '@adobe/react-spectrum'; |
| 88 | + |
| 89 | +function Example() { |
| 90 | + let [range, setRange] = React.useState(null); |
| 91 | + return ( |
| 92 | + <Provider locale="hi-IN-u-ca-indian"> |
| 93 | + <RangeCalendar aria-label="Date range" value={range} onChange={setRange} /> |
| 94 | + <p>Start date: {range?.start.toString()}</p> |
| 95 | + <p>End date: {range?.end.toString()}</p> |
| 96 | + </Provider> |
| 97 | + ); |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +## Labeling |
| 102 | + |
| 103 | +An aria-label must be provided to the `RangeCalendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the `id` of the labeling element instead. |
| 104 | + |
| 105 | +### Internationalization |
| 106 | + |
| 107 | +In order to internationalize a `RangeCalendar`, a localized string should be passed to the `aria-label` prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), the layout of the `RangeCalendar` is automatically flipped. Dates are automatically formatted using the current locale. |
| 108 | + |
| 109 | +## Events |
| 110 | + |
| 111 | +`RangeCalendar` accepts an `onChange` prop which is triggered whenever a date is selected by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter. |
| 112 | + |
| 113 | +```tsx example |
| 114 | +import {getLocalTimeZone} from '@internationalized/date'; |
| 115 | +import {useDateFormatter} from '@adobe/react-spectrum'; |
| 116 | + |
| 117 | +function Example() { |
| 118 | + let [range, setRange] = React.useState({ |
| 119 | + start: parseDate('2020-07-03'), |
| 120 | + end: parseDate('2020-07-10') |
| 121 | + }); |
| 122 | + let formatter = useDateFormatter({dateStyle: 'long'}); |
| 123 | + |
| 124 | + return ( |
| 125 | + <> |
| 126 | + <RangeCalendar aria-label="Date range" value={range} onChange={setRange} /> |
| 127 | + <p> |
| 128 | + Selected date:{' '} |
| 129 | + {formatter.formatRange( |
| 130 | + range.start.toDate(getLocalTimeZone()), |
| 131 | + range.end.toDate(getLocalTimeZone()) |
| 132 | + )} |
| 133 | + </p> |
| 134 | + </> |
| 135 | + ); |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +## Validation |
| 140 | + |
| 141 | +By default, `RangeCalendar` allows selecting any date range. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range. |
| 142 | + |
| 143 | +This example only accepts dates after today. |
| 144 | + |
| 145 | +```tsx example |
| 146 | +import {today} from '@internationalized/date'; |
| 147 | + |
| 148 | +<RangeCalendar aria-label="Trip dates" minValue={today(getLocalTimeZone())} /> |
| 149 | +``` |
| 150 | + |
| 151 | +## Props |
| 152 | + |
| 153 | +<PropTable component={docs.exports.RangeCalendar} links={docs.links} /> |
| 154 | + |
| 155 | +## Visual options |
| 156 | + |
| 157 | +### Disabled |
| 158 | + |
| 159 | +```tsx example |
| 160 | +<RangeCalendar aria-label="Trip dates" isDisabled /> |
| 161 | +``` |
| 162 | + |
| 163 | +### Read only |
| 164 | + |
| 165 | +The `isReadOnly` boolean prop makes the Calendar's value immutable. Unlike `isDisabled`, the RangeCalendar remains focusable. |
| 166 | + |
| 167 | +```tsx example |
| 168 | +<RangeCalendar aria-label="Trip dates" value={{start: today(getLocalTimeZone()), end: today(getLocalTimeZone()).add({ weeks: 1 })}} isReadOnly /> |
| 169 | +``` |
| 170 | + |
| 171 | +### Visible months |
| 172 | + |
| 173 | +By default, a `RangeCalendar` displays a single month. The `visibleMonths` prop allows displaying up to 3 months at a time. |
| 174 | + |
| 175 | +```tsx example |
| 176 | +<RangeCalendar aria-label="Trip dates" visibleMonths={3} /> |
| 177 | +``` |
0 commit comments