RangeCalendar 范围日历
基于 React Aria RangeCalendar 的可组合日期范围选择器,包含月份网格、导航与年份选择支持。
引入
import { RangeCalendar } from '@heroui/react';用法
组件结构
import {RangeCalendar} from '@heroui/react';
export default () => (
<RangeCalendar aria-label="Trip dates">
<RangeCalendar.Header>
<RangeCalendar.Heading />
<RangeCalendar.NavButton slot="previous" />
<RangeCalendar.NavButton slot="next" />
</RangeCalendar.Header>
<RangeCalendar.Grid>
<RangeCalendar.GridHeader>
{(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}
</RangeCalendar.GridHeader>
<RangeCalendar.GridBody>
{(date) => <RangeCalendar.Cell date={date} />}
</RangeCalendar.GridBody>
</RangeCalendar.Grid>
</RangeCalendar>
)年份选择
RangeCalendar.YearPickerTrigger、RangeCalendar.YearPickerGrid 及其 body/cell 子组件提供一体化的年份导航模式。
默认值
受控
最小与最大日期
不可用日期
使用 isDateUnavailable 禁用周末、节假日或已被预订的日期等。
基于锚点的不可用日期
选择范围时,isDateUnavailable 的第二个参数 anchorDate 为用户选中的开始日期。可据此限制结束日期(例如仅允许开始日期前后 7 天)。
固定周数
将 weeksInMonth 设为固定值(例如 6),可在月份切换时保持网格高度稳定。
周视图
设置 visibleDuration={{ weeks: n }} 可一次显示一个或多个周。翻页会按可见周范围前进。显示多周时可配合 pageBehavior="single" 每次仅移动一周。
日视图
设置 visibleDuration={{ days: n }} 可显示连续多天的滚动窗口。翻页会按可见天数范围前进。显示多天时配合 pageBehavior="single" 可每次仅移动一天。
允许非连续范围
启用 allowsNonContiguousRanges,允许选择跨越不可用日期的范围。
禁用
只读
无效
焦点日期
单元格指示器
你可以自定义 RangeCalendar.Cell 的子节点,并使用 RangeCalendar.CellIndicator 展示活动等元数据。
多个月份
使用 visibleDuration 与 offset 渲染多个月份网格,适用于预订与规划场景。在各列头部为 RangeCalendar.Heading 设置 offset(例如 offset={{ months: 1 }})以显示对应月份标题。
国际化历法
默认情况下,RangeCalendar 按用户语言环境的历法显示日期。你可以使用 I18nProvider 包裹 RangeCalendar,并通过 Unicode 历法语言扩展 覆盖。
下方示例展示印度历法系统:
说明: onChange 事件始终返回与 value 或 defaultValue 相同历法系统中的日期(若未提供值则为公历),与界面展示的本地化格式无关。
实际场景示例
样式
传入 Tailwind CSS 类
import {RangeCalendar} from '@heroui/react';
function CustomRangeCalendar() {
return (
<RangeCalendar aria-label="Trip dates" className="w-80 rounded-2xl border border-border bg-surface p-3 shadow-sm">
<RangeCalendar.Header className="pb-3">
<RangeCalendar.Heading className="text-default" />
<RangeCalendar.NavButton slot="previous" className="text-default" />
<RangeCalendar.NavButton slot="next" className="text-default" />
</RangeCalendar.Header>
<RangeCalendar.Grid>
<RangeCalendar.GridHeader>
{(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}
</RangeCalendar.GridHeader>
<RangeCalendar.GridBody>
{(date) => <RangeCalendar.Cell date={date} />}
</RangeCalendar.GridBody>
</RangeCalendar.Grid>
</RangeCalendar>
);
}自定义组件类
@layer components {
.range-calendar {
@apply w-80 rounded-2xl border border-border bg-surface p-3 shadow-sm;
}
.range-calendar__heading {
@apply text-sm font-semibold text-default;
}
.range-calendar__cell[data-selected="true"] .range-calendar__cell-button {
@apply bg-accent text-accent-foreground;
}
}CSS 类
RangeCalendar 在 packages/styles/components/range-calendar.css 与 packages/styles/components/calendar-year-picker.css 中使用以下类:
.range-calendar- 根容器。.range-calendar__header- 含导航按钮与标题的头部行。.range-calendar__heading- 当前月份标签。.range-calendar__nav-button- 上一月/下一月导航控件。.range-calendar__grid- 主体日期网格。.range-calendar__grid-header- 星期标题行外层。.range-calendar__grid-body- 日期行外层。.range-calendar__header-cell- 星期标题单元格。.range-calendar__cell- 可交互日期单元格外层。.range-calendar__cell-button- 单元格内的可交互日期按钮。.range-calendar__cell-indicator- 日期单元格内的圆点指示器。.calendar-year-picker__trigger- 年份选择器切换按钮。.calendar-year-picker__trigger-heading- 年份选择触发器内的标题文案。.calendar-year-picker__trigger-indicator- 年份选择触发器内的指示图标。.calendar-year-picker__year-grid- 可选年份的覆盖网格。.calendar-year-picker__year-cell- 单个年份选项。
交互状态
RangeCalendar 同时支持伪类与 React Aria 的 data 属性:
- 已选中:
[data-selected="true"] - 范围起点:
[data-selection-start="true"] - 范围终点:
[data-selection-end="true"] - 范围内:
[data-selection-in-range="true"] - 今天:
[data-today="true"] - 不可用:
[data-unavailable="true"] - 跨月:
[data-outside-month="true"] - 悬停:
:hover或[data-hovered="true"] - 按下:
:active或[data-pressed="true"] - 焦点可见:
:focus-visible或[data-focus-visible="true"] - 禁用:
:disabled或[data-disabled="true"]
API 参考
RangeCalendar Props
RangeCalendar 继承 React Aria RangeCalendar 的全部 props。
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
value | RangeValue<DateValue> | null | - | 受控的选中范围。 |
defaultValue | RangeValue<DateValue> | null | - | 初始选中范围(非受控)。 |
onChange | (value: RangeValue<DateValue>) => void | - | 选中变化时调用。 |
focusedValue | DateValue | - | 受控的焦点日期。 |
onFocusChange | (value: DateValue) => void | - | 焦点移动到其它日期时调用。 |
minValue | DateValue | 历法感知的 1900-01-01 | 可选的最早日期。 |
maxValue | DateValue | 历法感知的 2099-12-31 | 可选的最晚日期。 |
weeksInMonth | number | - | 一个月的周数。该值会覆盖区域设置的默认值。 |
isDateUnavailable | (date: DateValue, anchorDate: CalendarDate | null) => boolean | - | 将日期标记为不可用。anchorDate 为当前范围选择中的首个日期。 |
firstDayOfWeek | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | - | 覆盖区域设置的一周起始日。 |
pageBehavior | 'visible' | 'single' | 'visible' | 翻页按可见范围或单步前进。 |
selectionAlignment | 'start' | 'center' | 'end' | 'center' | 初始渲染时按选中项对齐可见范围。 |
allowsNonContiguousRanges | boolean | false | 允许范围跨越不可用日期。 |
isDisabled | boolean | false | 禁用交互与选择。 |
isReadOnly | boolean | false | 内容只读,不可更改选中。 |
isInvalid | boolean | false | 标记为无效以配合校验样式。 |
visibleDuration | {months?: number; weeks?: number; days?: number} | {months: 1} | 可见时间范围。使用 { months: n } 为月视图,{ weeks: n } 为周视图,{ days: n } 为日视图。 |
defaultYearPickerOpen | boolean | false | 内置年份选择器的初始展开状态。 |
isYearPickerOpen | boolean | - | 受控的年份选择器展开状态。 |
onYearPickerOpenChange | (isOpen: boolean) => void | - | 年份选择器展开状态变化时调用。 |
组合部件
| 组件 | 描述 |
|---|---|
RangeCalendar.Header | 导航与标题的头部容器。 |
RangeCalendar.Heading | 可见范围的格式化标题。支持 offset(多月份布局)与 format(月/年/日格式选项)。 |
RangeCalendar.NavButton | 上一页/下一页导航(slot="previous" 或 slot="next")。 |
RangeCalendar.Grid | 单个月的日期网格(多月份布局支持 offset)。 |
RangeCalendar.GridHeader | 星期标题容器。 |
RangeCalendar.GridBody | 日期单元格主体容器。 |
RangeCalendar.HeaderCell | 星期标签单元格。 |
RangeCalendar.Cell | 单个日期单元格。 |
RangeCalendar.CellIndicator | 用于自定义元数据的可选指示元素。 |
RangeCalendar.YearPickerTrigger | 切换年份选择模式的触发器。 |
RangeCalendar.YearPickerTriggerHeading | 年份选择触发器内的本地化标题内容。 |
RangeCalendar.YearPickerTriggerIndicator | 年份选择触发器内的切换图标。 |
RangeCalendar.YearPickerGrid | 年份选择覆盖网格容器。 |
RangeCalendar.YearPickerGridBody | 年份网格单元格的 body 渲染器。 |
RangeCalendar.YearPickerCell | 单个年份选项单元格。 |
年份选择器子组件
年份选择器子组件继承 React Aria useCalendarHeading 与 useCalendarYearPicker 的格式化属性。
| 组件 | 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|---|
RangeCalendar.YearPickerTriggerHeading | format | DateFormatterOptions | - | 自定义月/年标题(如 {month: 'short'})。 |
RangeCalendar.YearPickerTriggerHeading | offset | {months?: number} | - | 相对聚焦日期偏移标题(多月布局)。 |
RangeCalendar.YearPickerGrid | format | DateFormatterOptions | {year: 'numeric'} | 自定义年份单元格标签(纪元、历法系统等)。 |
RangeCalendar.YearPickerGrid | visibleYears | number | min–max 跨度或 20 | 滑动窗口中显示的年份数量。当同时设置 minValue 与 maxValue 时,默认为二者之间的完整范围。 |
RangeCalendar.Cell Render Props
当 RangeCalendar.Cell 的 children 为函数时,可使用 React Aria 的渲染参数:
| Prop | 类型 | 描述 |
|---|---|---|
formattedDate | string | 单元格日期的本地化文案。 |
isSelected | boolean | 该日期是否已选中。 |
isSelectionStart | boolean | 是否为选中范围的起点。 |
isSelectionEnd | boolean | 是否为选中范围的终点。 |
isUnavailable | boolean | 该日期是否不可用。 |
isDisabled | boolean | 单元格是否禁用。 |
isOutsideMonth | boolean | 是否属于相邻月份。 |
支持的历法系统及其标识符完整列表见:
Related packages
@internationalized/date— 各日期组件共用的日期类型(CalendarDate、CalendarDateTime、ZonedDateTime)与工具函数I18nProvider— 为子树覆盖语言环境useLocale— 读取当前语言环境与书写方向





