ProComponents, templates & AI tooling
HeroUI
27.7k

DatePicker

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

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

结构变化

在 v2 中,DatePicker 是一个自包含组件,通过 props 处理标签、描述、日历和时间输入:

import { DatePicker } from "@heroui/react";

export default function App() {
  return (
    <DatePicker label="Birth date" />
  );
}

在 v3 中,DatePicker 改用组合优先的 API,你需要显式组合 DateFieldCalendar

import { DatePicker, DateField, Calendar, Label } from "@heroui/react";

export default function App() {
  return (
    <DatePicker>
      <Label>Birth date</Label>
      <DateField.Group>
        <DateField.Input>
          {(segment) => <DateField.Segment segment={segment} />}
        </DateField.Input>
        <DateField.Suffix>
          <DatePicker.Trigger>
            <DatePicker.TriggerIndicator />
          </DatePicker.Trigger>
        </DateField.Suffix>
      </DateField.Group>
      <DatePicker.Popover>
        <Calendar aria-label="Choose date">
          <Calendar.Header>
            <Calendar.YearPickerTrigger>
              <Calendar.YearPickerTriggerHeading />
              <Calendar.YearPickerTriggerIndicator />
            </Calendar.YearPickerTrigger>
            <Calendar.NavButton slot="previous" />
            <Calendar.NavButton slot="next" />
          </Calendar.Header>
          <Calendar.Grid>
            <Calendar.GridHeader>
              {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
            </Calendar.GridHeader>
            <Calendar.GridBody>
              {(date) => <Calendar.Cell date={date} />}
            </Calendar.GridBody>
          </Calendar.Grid>
        </Calendar>
      </DatePicker.Popover>
    </DatePicker>
  );
}

主要变化

1. 组件结构

v2: 单一 DatePicker 组件,所有部分(输入、日历、标签、popover)都通过 props 在内部处理
v3: 基于组合的 API,由 DatePickerDateFieldCalendarLabel 共同组成。每个部分都是独立组件。

2. 组成部分

v3 组件描述
DatePicker根容器与状态持有者
DatePicker.Trigger打开日历 popover 的按钮
DatePicker.TriggerIndicator日历图标(默认)或自定义指示器
DatePicker.Popover日历的 popover 包装层
DateField.Group输入组包装层
DateField.Input分段日期输入
DateField.Segment单独的日期片段(月、日、年)
DateField.Suffix触发按钮的后缀 slot
Calendar完整的日历组件(参见 Calendar 迁移
Label外部 Label 组件

3. Prop 变更

v2 propv3 等效项说明
valuevalue保持一致
defaultValuedefaultValue保持一致
onChangeonChange保持一致
minValueminValue保持一致
maxValuemaxValue保持一致
isDateUnavailableisDateUnavailable保持一致
isDisabledisDisabled保持一致
isReadOnlyisReadOnly保持一致
isRequiredisRequired保持一致
isInvalidisInvalid保持一致
granularitygranularity保持一致
hourCyclehourCycle保持一致
hideTimeZonehideTimeZone保持一致
shouldForceLeadingZerosshouldForceLeadingZeros保持一致
pageBehavior-直接设置在 Calendar
label-使用 Label 组件
description-使用 Description 组件
errorMessage-使用 FieldError 组件
variant-使用 DateField.Groupvariant prop 或 Tailwind CSS
color-已移除(请改用 Tailwind CSS)
size-已移除(请改用 Tailwind CSS)
radius-已移除(请改用 Tailwind CSS)
labelPlacement-使用 Tailwind CSS 控制布局
startContent-DateField.Group 内组合
endContent-DateField.Group 内组合
selectorIcon-将 children 传给 DatePicker.TriggerIndicator
visibleMonths-使用 CalendarvisibleDuration prop
showMonthAndYearPickers-使用 Calendar.YearPickerTriggerCalendar.YearPickerGrid
calendarProps-直接将 props 传给 Calendar
popoverProps-直接将 props 传给 DatePicker.Popover
selectorButtonProps-直接将 props 传给 DatePicker.Trigger
timeInputProps-单独组合时间输入
CalendarBottomContent-将内容放在 DatePicker.Popover 内、Calendar 之后
validate-在外部处理校验
placeholderValueplaceholderValue保持一致
autoFocusautoFocus保持一致
disableAnimation-已移除
classNames-在各个组件上使用 className

迁移示例

基本日期选择器

import { DatePicker } from "@heroui/react";

<DatePicker label="Birth date" />
import { DatePicker, DateField, Calendar, Label } from "@heroui/react";

<DatePicker>
  <Label>Birth date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger>
        <DatePicker.TriggerIndicator />
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <DatePicker.Popover>
    <Calendar aria-label="Choose date">
      <Calendar.Header>
        <Calendar.YearPickerTrigger>
          <Calendar.YearPickerTriggerHeading />
          <Calendar.YearPickerTriggerIndicator />
        </Calendar.YearPickerTrigger>
        <Calendar.NavButton slot="previous" />
        <Calendar.NavButton slot="next" />
      </Calendar.Header>
      <Calendar.Grid>
        <Calendar.GridHeader>
          {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
        </Calendar.GridHeader>
        <Calendar.GridBody>
          {(date) => <Calendar.Cell date={date} />}
        </Calendar.GridBody>
      </Calendar.Grid>
    </Calendar>
  </DatePicker.Popover>
</DatePicker>

受控状态

import { useState } from "react";
import { DatePicker } from "@heroui/react";
import { parseDate } from "@internationalized/date";

const [value, setValue] = useState(parseDate("2024-03-07"));

<DatePicker
  label="Date"
  value={value}
  onChange={setValue}
/>
import { useState } from "react";
import { DatePicker, DateField, Calendar, Label } from "@heroui/react";
import { parseDate } from "@internationalized/date";

const [value, setValue] = useState(parseDate("2024-03-07"));

<DatePicker value={value} onChange={setValue}>
  <Label>Date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger>
        <DatePicker.TriggerIndicator />
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <DatePicker.Popover>
    <Calendar aria-label="Choose date">
      <Calendar.Header>
        <Calendar.Heading />
        <Calendar.NavButton slot="previous" />
        <Calendar.NavButton slot="next" />
      </Calendar.Header>
      <Calendar.Grid>
        <Calendar.GridHeader>
          {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
        </Calendar.GridHeader>
        <Calendar.GridBody>
          {(date) => <Calendar.Cell date={date} />}
        </Calendar.GridBody>
      </Calendar.Grid>
    </Calendar>
  </DatePicker.Popover>
</DatePicker>

带描述和错误信息

<DatePicker
  label="Event date"
  description="Choose a future date"
  errorMessage="Date must be in the future"
  isInvalid={isInvalid}
/>
import { DatePicker, DateField, Calendar, Label, Description, FieldError } from "@heroui/react";

<DatePicker isInvalid={isInvalid}>
  <Label>Event date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger>
        <DatePicker.TriggerIndicator />
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <Description>Choose a future date</Description>
  <FieldError>Date must be in the future</FieldError>
  <DatePicker.Popover>
    <Calendar aria-label="Choose date">
      {/* Calendar compound components */}
    </Calendar>
  </DatePicker.Popover>
</DatePicker>

自定义选择器图标

import { Icon } from "@iconify/react";

<DatePicker
  label="Date"
  selectorIcon={<Icon icon="gravity-ui:calendar" />}
/>
import { Icon } from "@iconify/react";

<DatePicker>
  <Label>Date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger>
        <DatePicker.TriggerIndicator>
          <Icon icon="gravity-ui:calendar" />
        </DatePicker.TriggerIndicator>
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <DatePicker.Popover>
    <Calendar aria-label="Choose date">
      {/* Calendar compound components */}
    </Calendar>
  </DatePicker.Popover>
</DatePicker>

带月份 / 年份选择器和日历底部内容

<DatePicker
  label="Date"
  showMonthAndYearPickers
  CalendarBottomContent={
    <button onClick={() => setValue(today(getLocalTimeZone()))}>Today</button>
  }
/>
<DatePicker>
  <Label>Date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger>
        <DatePicker.TriggerIndicator />
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <DatePicker.Popover>
    <Calendar aria-label="Choose date">
      <Calendar.Header>
        <Calendar.YearPickerTrigger>
          <Calendar.YearPickerTriggerHeading />
          <Calendar.YearPickerTriggerIndicator />
        </Calendar.YearPickerTrigger>
        <Calendar.NavButton slot="previous" />
        <Calendar.NavButton slot="next" />
      </Calendar.Header>
      <Calendar.Grid>
        <Calendar.GridHeader>
          {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
        </Calendar.GridHeader>
        <Calendar.GridBody>
          {(date) => <Calendar.Cell date={date} />}
        </Calendar.GridBody>
      </Calendar.Grid>
      <Calendar.YearPickerGrid>
        <Calendar.YearPickerGridBody>
          {(year) => <Calendar.YearPickerCell date={year} />}
        </Calendar.YearPickerGridBody>
      </Calendar.YearPickerGrid>
    </Calendar>
    <button onClick={() => setValue(today(getLocalTimeZone()))}>Today</button>
  </DatePicker.Popover>
</DatePicker>

样式变化

v2:classNames prop

<DatePicker
  classNames={{
    base: "custom-base",
    selectorButton: "custom-trigger",
    selectorIcon: "custom-icon",
    popoverContent: "custom-popover",
    calendar: "custom-calendar",
  }}
/>

v3:直接使用 className prop

<DatePicker className="custom-base">
  <Label>Date</Label>
  <DateField.Group>
    <DateField.Input>
      {(segment) => <DateField.Segment segment={segment} />}
    </DateField.Input>
    <DateField.Suffix>
      <DatePicker.Trigger className="custom-trigger">
        <DatePicker.TriggerIndicator className="custom-icon" />
      </DatePicker.Trigger>
    </DateField.Suffix>
  </DateField.Group>
  <DatePicker.Popover className="custom-popover">
    <Calendar aria-label="Choose date" className="custom-calendar">
      {/* Calendar compound components */}
    </Calendar>
  </DatePicker.Popover>
</DatePicker>

组件结构

v3 DatePicker 遵循以下结构:

DatePicker (Root)
  ├── Label
  ├── DateField.Group
  │   ├── DateField.Input
  │   │   └── DateField.Segment (render prop per segment)
  │   └── DateField.Suffix
  │       └── DatePicker.Trigger
  │           └── DatePicker.TriggerIndicator
  ├── Description (optional)
  ├── FieldError (optional)
  └── DatePicker.Popover
      ├── Calendar (see Calendar migration guide)
      └── [Custom bottom content]

总结

  1. 组件结构:单一组件 → 由 DatePickerDateFieldCalendarLabel 组合而成
  2. 标签label prop → Label 组件
  3. 描述 / 错误信息:props → DescriptionFieldError 组件
  4. 日历:内置日历 → 在 DatePicker.Popover 内组合带有自身复合组件的 Calendar
  5. 选择器图标selectorIcon prop → 将 children 传给 DatePicker.TriggerIndicator
  6. 年份选择器showMonthAndYearPickers prop → 使用 Calendar.YearPickerTriggerCalendar.YearPickerGrid
  7. 日历底部内容CalendarBottomContent prop → 将内容放在 DatePicker.Popover 内、Calendar 之后
  8. 已移除样式 propvariantcolorsizeradius → 使用 Tailwind CSS 或 DateField.Group 的变体
  9. 已移除 classNames:在各个复合组件上使用 className
  10. 多个月份visibleMonths prop → 使用 CalendarvisibleDuration prop

本页目录