Skip to content
Lenso UI

RangeCalendar

RangeCalendar 从 HeroUI v2 到 v3 的迁移指南。

完整的 API 参考、样式指南与高级示例请参阅 v3 RangeCalendar 文档。本指南只关注从 HeroUI v2 的迁移。

结构变化

在 v2 中,RangeCalendar 是一个完全通过 props 配置的单一组件:

import { RangeCalendar } from "@lenso/ui";import { today, getLocalTimeZone } from "@internationalized/date";
export default function App() {  return (    <RangeCalendar      aria-label="Trip dates"      defaultValue={{        start: today(getLocalTimeZone()),        end: today(getLocalTimeZone()).add({ weeks: 1 }),      }}    />  );}

在 v3 中,RangeCalendar 改用带显式子组件的复合组件模式:

import { RangeCalendar } from "@lenso/ui";import { today, getLocalTimeZone } from "@internationalized/date";
export default function App() {  return (    <RangeCalendar      aria-label="Trip dates"      defaultValue={{        start: today(getLocalTimeZone()),        end: today(getLocalTimeZone()).add({ weeks: 1 }),      }}    >      <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>  );}

主要变化

1. 组件结构

v2: 单一 RangeCalendar 组件,所有布局都在内部处理
v3: 复合组件:RangeCalendar.Header、RangeCalendar.Heading、RangeCalendar.NavButton、RangeCalendar.Grid、RangeCalendar.GridHeader、RangeCalendar.GridBody、RangeCalendar.HeaderCell、RangeCalendar.Cell、RangeCalendar.CellIndicator

2. 年份选择器

v2: 通过 showMonthAndYearPickers prop 提供内置月份 / 年份选择器
v3: 使用专用复合组件:RangeCalendar.YearPickerTrigger、RangeCalendar.YearPickerGrid、RangeCalendar.YearPickerGridBody、RangeCalendar.YearPickerCell

3. Prop 变更

v2 propv3 等效项说明
valuevalue保持一致
defaultValuedefaultValue保持一致
onChangeonChange保持一致
focusedValuefocusedValue保持一致
onFocusChangeonFocusChange保持一致
minValueminValue保持一致
maxValuemaxValue保持一致
isDateUnavailableisDateUnavailable保持一致
allowsNonContiguousRangesallowsNonContiguousRanges保持一致
isDisabledisDisabled保持一致
isReadOnlyisReadOnly保持一致
isInvalidisInvalid保持一致
pageBehaviorpageBehavior保持一致
visibleMonthsvisibleDuration改为 {months: number} 对象
showMonthAndYearPickers-使用 RangeCalendar.YearPickerTrigger 和 RangeCalendar.YearPickerGrid
onHeaderExpandedChangeonYearPickerOpenChange已重命名
color-已移除(请改用 Tailwind CSS)
calendarWidth-已移除(请改用 className 或 Tailwind CSS)
weekdayStyle-已移除
firstDayOfWeek-改用 I18nProvider 的 locale
topContent-将自定义内容作为 RangeCalendar children 放在网格之前
bottomContent-将自定义内容作为 RangeCalendar children 放在网格之后
errorMessage-已移除(请在外部处理校验)
showHelper-已移除
disableAnimation-已移除
classNames-在各个复合组件上使用 className

迁移示例

基本范围选择

import { RangeCalendar } from "@lenso/ui";import { today, getLocalTimeZone } from "@internationalized/date";
<RangeCalendar  aria-label="Trip dates"  defaultValue={{    start: today(getLocalTimeZone()),    end: today(getLocalTimeZone()).add({ weeks: 1 }),  }}/>

受控状态

import { useState } from "react";import { RangeCalendar } from "@lenso/ui";import { parseDate } from "@internationalized/date";
const [value, setValue] = useState({  start: parseDate("2024-03-01"),  end: parseDate("2024-03-07"),});
<RangeCalendar  aria-label="Trip dates"  value={value}  onChange={setValue}/>

月份和年份选择器

<RangeCalendar  aria-label="Trip dates"  showMonthAndYearPickers/>

多个月份

<RangeCalendar  aria-label="Trip dates"  visibleMonths={2}/>

带非连续范围的不可用日期

import { isWeekend } from "@internationalized/date";import { useLocale } from "@react-aria/i18n";
const { locale } = useLocale();
<RangeCalendar  aria-label="Trip dates"  isDateUnavailable={(date) => isWeekend(date, locale)}  allowsNonContiguousRanges/>

顶部与底部内容

<RangeCalendar  aria-label="Trip dates"  topContent={<p>Select your travel dates</p>}  bottomContent={    <button onClick={handleReset}>Reset</button>  }/>

样式变化

v2:classNames prop

<RangeCalendar  classNames={{    base: "custom-base",    prevButton: "custom-prev",    nextButton: "custom-next",    title: "custom-title",    cell: "custom-cell",    cellButton: "custom-cell-button",  }}/>

v3:直接使用 className prop

<RangeCalendar className="custom-base">  <RangeCalendar.Header>    <RangeCalendar.Heading className="custom-title" />    <RangeCalendar.NavButton slot="previous" className="custom-prev" />    <RangeCalendar.NavButton slot="next" className="custom-next" />  </RangeCalendar.Header>  <RangeCalendar.Grid>    <RangeCalendar.GridHeader>      {(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}    </RangeCalendar.GridHeader>    <RangeCalendar.GridBody>      {(date) => <RangeCalendar.Cell date={date} className="custom-cell" />}    </RangeCalendar.GridBody>  </RangeCalendar.Grid></RangeCalendar>

组件结构

v3 RangeCalendar 遵循以下结构:

RangeCalendar (Root)  ├── [Custom top content]  ├── RangeCalendar.Header  │   ├── RangeCalendar.Heading (or RangeCalendar.YearPickerTrigger)  │   ├── RangeCalendar.NavButton slot="previous"  │   └── RangeCalendar.NavButton slot="next"  ├── RangeCalendar.Grid (one per visible month)  │   ├── RangeCalendar.GridHeader  │   │   └── RangeCalendar.HeaderCell (render prop)  │   └── RangeCalendar.GridBody  │       └── RangeCalendar.Cell (render prop)  │           └── RangeCalendar.CellIndicator (optional)  ├── RangeCalendar.YearPickerGrid (optional)  │   └── RangeCalendar.YearPickerGridBody  │       └── RangeCalendar.YearPickerCell  └── [Custom bottom content]

总结

  1. 组件结构:单一组件 → 带显式布局控制的复合组件
  2. 年份选择器:showMonthAndYearPickers prop → 专用的 RangeCalendar.YearPickerTrigger 和 RangeCalendar.YearPickerGrid 组件
  3. 多个月份:visibleMonths={n} → visibleDuration={{months: n}},并使用多个带 offset 的 RangeCalendar.Grid 组件
  4. color 已移除:请改用 Tailwind CSS 类
  5. 顶部 / 底部内容:prop 已移除 → 直接将内容作为 children 放在 RangeCalendar 内
  6. 单元格自定义:新增 RangeCalendar.CellIndicator,且 RangeCalendar.Cell 支持渲染 prop
  7. 样式:classNames prop → 各个复合组件上的 className
  8. 已移除 prop:calendarWidth、weekdayStyle、showHelper、errorMessage、disableAnimation —— 请使用 Tailwind CSS 或在外部处理