Skip to content
Lenso UI

Calendar 日历

基于 React Aria Calendar 的可组合日期选择器,支持月网格、导航与年份选择

用法

import { Calendar } from '@lenso/ui';

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";
/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";
export function Basic() {  return (    <Calendar aria-label="Event 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>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

组件结构

import {Calendar} from '@lenso/ui';
export default () => (  <Calendar aria-label="Event 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>)

示例

禁用

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import { getLocalTimeZone, today } from "@internationalized/date";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function Disabled() {  return (    <div {...stylex.props(layout.stack)}>      <Calendar isDisabled aria-label="Event date" defaultValue={today(getLocalTimeZone())}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Calendar is disabled</CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

只读

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import { getLocalTimeZone, today } from "@internationalized/date";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function ReadOnly() {  return (    <div {...stylex.props(layout.stack)}>      <Calendar isReadOnly aria-label="Event date" defaultValue={today(getLocalTimeZone())}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Calendar is read-only</CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

默认值

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";import { parseDate } from "@internationalized/date";import { CalendarHeader, CalendarGrid } from "./demo-parts";export function DefaultValue() {  return (    <Calendar aria-label="Event date" defaultValue={parseDate("2025-02-14")}>      <CalendarHeader />      <CalendarGrid />    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

年份选择

Calendar.YearPickerTrigger、Calendar.YearPickerGrid 及其 body/cell 子组件提供集成的年份导航模式。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";
export function YearPicker() {  return (    <Calendar aria-label="Event 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 year={year} />}        </Calendar.YearPickerGridBody>      </Calendar.YearPickerGrid>    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

受控组件

使用受控的 value 与 focusedValue 进行外部状态协调与自定义快捷操作。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX and native Base UI Button. */import { Button, ButtonGroup, Calendar } from "@lenso/ui";import {  getLocalTimeZone,  parseDate,  startOfMonth,  startOfWeek,  today,  type CalendarDate,} from "@internationalized/date";import { useState } from "react";import { useLocale } from "react-aria-components/I18nProvider";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function Controlled() {  const [value, setValue] = useState<CalendarDate | null>(null);  const [focusedDate, setFocusedDate] = useState<CalendarDate>(() => parseDate("2025-12-25"));  const { locale } = useLocale();  const select = (date: CalendarDate) => {    setValue(date);    setFocusedDate(date);  };  return (    <div {...stylex.props(layout.stack)}>      <ButtonGroup fullWidth size="sm" variant="tertiary">        <Button onClick={() => select(today(getLocalTimeZone()))}>Today</Button>        <Button onClick={() => select(startOfWeek(today(getLocalTimeZone()), locale))}>Week</Button>        <Button onClick={() => select(startOfMonth(today(getLocalTimeZone())))}>Month</Button>      </ButtonGroup>      <Calendar        aria-label="Event date"        focusedValue={focusedDate}        value={value}        onChange={setValue}        onFocusChange={setFocusedDate}      >        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Selected date: {value ? value.toString() : "(none)"}</CalendarNote>      <div {...stylex.props(layout.controls)}>        <Button size="sm" variant="secondary" onClick={() => select(today(getLocalTimeZone()))}>          Set Today        </Button>        <Button size="sm" variant="secondary" onClick={() => select(parseDate("2025-12-25"))}>          Set Christmas        </Button>        <Button size="sm" variant="tertiary" onClick={() => setValue(null)}>          Clear        </Button>      </div>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

日期范围限制

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import { getLocalTimeZone, today } from "@internationalized/date";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function MinMaxDates() {  const now = today(getLocalTimeZone());  const maxDate = now.add({ months: 3 });  return (    <div {...stylex.props(layout.stack)}>      <Calendar aria-label="Appointment date" maxValue={maxDate} minValue={now}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Select a date between today and {maxDate.toString()}</CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

不可用日期

使用 isDateUnavailable 阻止周末、节假日或已预订时段等日期。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import { isWeekend } from "@internationalized/date";import { useLocale } from "react-aria-components/I18nProvider";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function UnavailableDates() {  const { locale } = useLocale();  return (    <div {...stylex.props(layout.stack)}>      <Calendar aria-label="Appointment date" isDateUnavailable={(date) => isWeekend(date, locale)}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Weekends are unavailable</CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

月份周数

将 weeksInMonth 设为固定值(如 6)可在月份切换时保持网格高度稳定。在非公历 locale 中请谨慎使用,类似 firstDayOfWeek。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function WeeksInMonth() {  return (    <div {...stylex.props(layout.stack)}>      <Calendar aria-label="Event date" weeksInMonth={6}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>        Always shows 6 weeks per month to avoid layout shift when navigating      </CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

周视图

设置 visibleDuration={{ weeks: n }} 可一次显示一周或多周。导航按可见周范围前进。显示多周时使用 pageBehavior="single" 可每次移动一周。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX and native Base UI Select. */import { Calendar } from "@lenso/ui";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, DurationSelect, layout } from "./demo-parts";export function WeekView() {  const [weeks, setWeeks] = useState(1);  return (    <div {...stylex.props(layout.controlsStack)}>      <DurationSelect unit="weeks" value={weeks} onChange={setWeeks} />      <Calendar aria-label="Week view" visibleDuration={{ weeks }}>        <CalendarHeader />        <CalendarGrid />      </Calendar>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

日视图

设置 visibleDuration={{ days: n }} 可显示连续日期的滚动窗口。导航按可见日范围前进。显示多天时使用 pageBehavior="single" 可每次移动一天。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX and native Base UI Select. */import { Calendar } from "@lenso/ui";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, DurationSelect, layout } from "./demo-parts";export function DayView() {  const [days, setDays] = useState(5);  return (    <div {...stylex.props(layout.controlsStack)}>      <DurationSelect unit="days" value={days} onChange={setDays} />      <Calendar aria-label="Day view" visibleDuration={{ days }}>        <CalendarHeader />        <CalendarGrid />      </Calendar>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

多选

设置 selectionMode="multiple" 允许选择多个日期。value、defaultValue 与 onChange 使用日期数组。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import type { DateValue } from "@internationalized/date";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { CalendarNote } from "./demo-parts";
const styles = stylex.create({  root: { display: "flex", flexDirection: "column", alignItems: "center", gap: 16 },  description: { textAlign: "center" },});
export function MultipleSelection() {  const [value, setValue] = useState<readonly DateValue[]>([]);  return (    <div {...stylex.props(styles.root)}>      <Calendar aria-label="Event dates" selectionMode="multiple" value={value} onChange={setValue}>        <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>      <CalendarNote>        {value.length ? `${value.length} date(s) selected` : "Select multiple dates"}      </CalendarNote>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

聚焦值

使用 focusedValue 与 onFocusChange 以编程方式控制聚焦日期。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX and native Base UI Button. */import { Button, Calendar } from "@lenso/ui";import { parseDate, type DateValue } from "@internationalized/date";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, CalendarNote, layout } from "./demo-parts";export function FocusedValue() {  const [focusedDate, setFocusedDate] = useState<DateValue>(() => parseDate("2025-06-15"));  return (    <div {...stylex.props(layout.stack)}>      <Calendar aria-label="Event date" focusedValue={focusedDate} onFocusChange={setFocusedDate}>        <CalendarHeader />        <CalendarGrid />      </Calendar>      <CalendarNote>Focused: {focusedDate.toString()}</CalendarNote>      <div {...stylex.props(layout.controls)}>        <Button          size="sm"          variant="secondary"          onClick={() => setFocusedDate(parseDate("2025-01-01"))}        >          Go to Jan        </Button>        <Button          size="sm"          variant="secondary"          onClick={() => setFocusedDate(parseDate("2025-06-15"))}        >          Go to Jun        </Button>        <Button          size="sm"          variant="secondary"          onClick={() => setFocusedDate(parseDate("2025-12-25"))}        >          Go to Christmas        </Button>      </div>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

单元格标记

可自定义 Calendar.Cell 子节点,并使用 Calendar.CellIndicator 显示事件等元数据。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";import { getLocalTimeZone, isToday } from "@internationalized/date";import { CalendarHeader, CalendarGrid } from "./demo-parts";const datesWithEvents = [3, 7, 12, 15, 21, 28];export function WithIndicators() {  return (    <Calendar aria-label="Event date">      <CalendarHeader />      <CalendarGrid        cell={(date) => (          <Calendar.Cell date={date}>            {({ formattedDate }) => (              <>                {formattedDate}                {(isToday(date, getLocalTimeZone()) || datesWithEvents.includes(date.day)) && (                  <Calendar.CellIndicator />                )}              </>            )}          </Calendar.Cell>        )}      />    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义导航图标

向 Calendar.NavButton 传入子节点以替换默认 chevron 图标。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";import { CalendarGrid } from "./demo-parts";export function CustomIcons() {  return (    <Calendar aria-label="Event date">      <Calendar.Header>        <Calendar.Heading />        <Calendar.NavButton slot="previous">          <svg height={24} viewBox="0 0 24 24" width={24} xmlns="http://www.w3.org/2000/svg">            <path d="M15.41 16.59L10.83 12l4.58-4.59L14 6l-6 6l6 6z" fill="currentColor" />          </svg>        </Calendar.NavButton>        <Calendar.NavButton slot="next">          <svg height={24} viewBox="0 0 24 24" width={24} xmlns="http://www.w3.org/2000/svg">            <path d="M8.59 16.59L13.17 12L8.59 7.41L10 6l6 6l-6 6z" fill="currentColor" />          </svg>        </Calendar.NavButton>      </Calendar.Header>      <CalendarGrid />    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

多月份展示

使用 visibleDuration 与 offset 渲染多个月份网格,适用于预订与规划场景。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  root: { containerType: "normal", width: "100%", maxWidth: "none", overflowX: "auto" },  months: { marginInline: "auto", display: "flex", width: "max-content", gap: 32 },  month: { width: 256 },  heading: { flex: "none" },  spacer: { width: 24, height: 24 },});
export function MultipleMonths() {  return (    <Calendar aria-label="Event date" xstyle={styles.root} visibleDuration={{ months: 2 }}>      <div {...stylex.props(styles.months)}>        <div {...stylex.props(styles.month)}>          <Calendar.Header>            <Calendar.NavButton slot="previous" />            <Calendar.Heading xstyle={styles.heading} />            <div {...stylex.props(styles.spacer)} />          </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>        </div>        <div {...stylex.props(styles.month)}>          <Calendar.Header>            <div {...stylex.props(styles.spacer)} />            <Calendar.Heading xstyle={styles.heading} offset={{ months: 1 }} />            <Calendar.NavButton slot="next" />          </Calendar.Header>          <Calendar.Grid offset={{ months: 1 }}>            <Calendar.GridHeader>              {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}            </Calendar.GridHeader>            <Calendar.GridBody>{(date) => <Calendar.Cell date={date} />}</Calendar.GridBody>          </Calendar.Grid>        </div>      </div>    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

典型场景

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX and native Base UI Button. */import { Button, Calendar } from "@lenso/ui";import { getLocalTimeZone, isWeekend, today, type CalendarDate } from "@internationalized/date";import { useState } from "react";import { useLocale } from "react-aria-components/I18nProvider";import * as stylex from "@stylexjs/stylex";import { CalendarHeader, CalendarGrid, layout } from "./demo-parts";const bookedDates = [5, 6, 12, 13, 14, 20];export function BookingCalendar() {  const [selectedDate, setSelectedDate] = useState<CalendarDate | null>(null);  const { locale } = useLocale();  return (    <div {...stylex.props(layout.stack)}>      <Calendar        aria-label="Booking date"        isDateUnavailable={(date) => isWeekend(date, locale) || bookedDates.includes(date.day)}        minValue={today(getLocalTimeZone())}        value={selectedDate}        onChange={setSelectedDate}      >        <CalendarHeader />        <CalendarGrid          cell={(date) => (            <Calendar.Cell date={date}>              {({ formattedDate, isUnavailable }) => (                <>                  {formattedDate}                  {!isUnavailable && !isWeekend(date, locale) && bookedDates.includes(date.day) && (                    <Calendar.CellIndicator />                  )}                </>              )}            </Calendar.Cell>          )}        />      </Calendar>      <div {...stylex.props(layout.bookingDetails)}>        <div {...stylex.props(layout.legend)}>          <span {...stylex.props(layout.legendItem)}>            <span {...stylex.props(layout.mutedDot)} /> Has bookings          </span>          <span {...stylex.props(layout.legendItem)}>            <span {...stylex.props(layout.defaultDot)} /> Weekend/Unavailable          </span>        </div>        {selectedDate ? (          <Button size="sm" variant="primary">            Book {selectedDate.toString()}          </Button>        ) : null}      </div>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

国际化日历

默认情况下,Calendar 使用用户 locale 的历法系统显示日期。可用 I18nProvider 包裹 Calendar 并设置 Unicode 历法 locale 扩展 来覆盖。

以下示例展示印度历法系统:

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. */import { Calendar } from "@lenso/ui";import { getLocalTimeZone, today } from "@internationalized/date";import { I18nProvider } from "react-aria-components/I18nProvider";
export function InternationalCalendar() {  return (    <I18nProvider locale="hi-IN-u-ca-indian">      <Calendar aria-label="Event date" defaultValue={today(getLocalTimeZone())}>        <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 year={year} />}          </Calendar.YearPickerGridBody>        </Calendar.YearPickerGrid>      </Calendar>    </I18nProvider>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Note: onChange 事件始终返回与 value 或 defaultValue 相同历法系统的日期(未提供 value 时为公历),无论显示 locale 如何。这确保应用逻辑在单一历法系统下一致运行,同时仍以用户偏好的格式显示日期。

自定义样式

Tailwind CSS

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";/** HeroUI v3.2.6 derived work. Copyright NextUI Inc. Apache-2.0. Modified for StyleX. */import { Calendar } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  root: {    width: 252,    borderRadius: "var(--radius-2xl)",    borderWidth: 1,    borderStyle: "solid",    borderColor: {      default: "color-mix(in oklab,var(--border) 80%,transparent)",      ':is([data-theme="dark"] *)': "color-mix(in oklab,var(--border) 90%,transparent)",    },    backgroundColor: "var(--surface)",    padding: 12,    boxShadow: {      default:        "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 1px color-mix(in oklab,var(--accent) 5%,transparent)",      ':is([data-theme="dark"] *)':        "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 1px color-mix(in oklab,var(--accent) 10%,transparent)",    },  },  heading: { color: "var(--foreground)" },  cell: {    backgroundColor: {      default: null,      ":hover": "var(--default)",      ":is([data-hovered])": "var(--default)",      ":is([data-selected])": "var(--accent)",      ":is([data-selected]):is(:hover,[data-hovered])": "var(--accent-hover)",      ":is([data-selected][data-outside-month])": "var(--default)",      ":is([data-today])": "var(--accent-soft)",      ":is([data-today]):is(:hover,[data-hovered])": "var(--accent-soft-hover)",      ":is([data-selected][data-today])": "var(--accent)",      ":is([data-selected][data-today]):hover": "var(--accent-hover)",    },    color: {      default: null,      ":is([data-outside-month])": "var(--muted)",      ":is([data-selected])": "var(--accent-foreground)",      ":is([data-today])": "var(--accent-soft-foreground)",      ":is([data-selected][data-today])": "var(--accent-foreground)",    },  },});
export function CustomStyles() {  return (    <Calendar aria-label="Custom styled calendar" xstyle={styles.root}>      <Calendar.Header>        <Calendar.Heading xstyle={styles.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 xstyle={styles.cell} date={date} />}        </Calendar.GridBody>      </Calendar.Grid>    </Calendar>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

全局 CSS

@layer components {  .calendar {    @apply w-72 rounded-2xl border border-border bg-surface p-3 shadow-sm;  }
  .calendar__heading {    @apply text-sm font-semibold text-default-700;  }
  .calendar__cell[data-selected="true"] {    @apply bg-accent text-accent-foreground;  }}

样式参考

HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。

CSS 类

Calendar 在 packages/styles/components/calendar.css 与 packages/styles/components/calendar-year-picker.css 中使用以下类:

  • .calendar - 根容器
  • .calendar__header - 包含导航按钮与标题的头部行
  • .calendar__heading - 当前月份标签
  • .calendar__nav-button - 上/下月导航控件
  • .calendar__grid - 主日期网格
  • .calendar__grid-header - 星期标题行包装器
  • .calendar__grid-body - 日期行包装器
  • .calendar__header-cell - 星期标题单元格
  • .calendar__cell - 可交互的日期单元格
  • .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 - 单个年份选项

交互状态

Calendar 同时支持伪类与 React Aria data 属性:

  • Selected:[data-selected="true"]
  • Today:[data-today="true"]
  • Unavailable:[data-unavailable="true"]
  • Outside month:[data-outside-month="true"]
  • Hovered::hover 或 [data-hovered="true"]
  • Pressed::active 或 [data-pressed="true"]
  • Focus visible::focus-visible 或 [data-focus-visible="true"]
  • Disabled::disabled 或 [data-disabled="true"]

API 参考

Calendar

Calendar 继承 React Aria Calendar 的所有属性。

Prop类型默认值描述
selectionMode'single' | 'multiple''single'是否可选择单个或多个日期
valueDateValue | null 或 DateValue[] | null-受控选中日期。selectionMode 为 multiple 时使用数组
defaultValueDateValue | null 或 DateValue[] | null-初始选中日期(非受控)
onChange(value: DateValue | null) 或 (value: DateValue[] | null) => void-选择变化时调用
focusedValueDateValue-受控聚焦日期
onFocusChange(value: DateValue) => void-焦点移至其他日期时调用
minValueDateValue历法感知的 1900-01-01最早可选日期
maxValueDateValue历法感知的 2099-12-31最晚可选日期
weeksInMonthnumber-月份中的周数,覆盖 locale 默认值
isDateUnavailable(date: DateValue) => boolean-标记日期为不可用
firstDayOfWeek'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat'-覆盖 locale 默认的一周起始日
pageBehavior'visible' | 'single''visible'翻页按可见时长还是单个单位前进
selectionAlignment'start' | 'center' | 'end''center'初始渲染时可见范围与选择的对齐方式
isDisabledbooleanfalse禁用交互与选择
isReadOnlybooleanfalse内容可读但不可更改选择
isInvalidbooleanfalse标记日历为无效以显示验证 UI
visibleDuration{months?: number; weeks?: number; days?: number}{months: 1}可见时间范围。月视图用 { months: n },周视图用 { weeks: n },日视图用 { days: n }
defaultYearPickerOpenbooleanfalse内部年份选择器初始打开状态
isYearPickerOpenboolean-受控年份选择器打开状态
onYearPickerOpenChange(isOpen: boolean) => void-年份选择器打开状态变化时调用

Composition Parts

ComponentDescription
Calendar.Header导航与标题的头部容器
Calendar.Heading可见范围的格式化标题。支持 offset(多月布局)与 format(月/年/日选项)
Calendar.NavButton上/下月导航控件(slot="previous" 或 slot="next")
Calendar.Grid单个月份的日期网格(多月布局支持 offset)
Calendar.GridHeader星期标题容器
Calendar.GridBody日期单元格 body 容器
Calendar.HeaderCell星期标签单元格
Calendar.Cell单个日期单元格
Calendar.CellIndicator自定义元数据的可选指示元素
Calendar.YearPickerTrigger切换年份选择器模式的触发器
Calendar.YearPickerTriggerHeading年份选择器触发器内的本地化标题内容
Calendar.YearPickerTriggerIndicator年份选择器触发器内的切换图标
Calendar.YearPickerGrid覆盖式年份选择网格容器
Calendar.YearPickerGridBody年份网格单元格的 body 渲染器
Calendar.YearPickerCell单个年份选项单元格

Year Picker Parts

年份选择器子组件继承 React Aria useCalendarHeading 与 useCalendarYearPicker 的格式化属性。

ComponentProp类型默认值描述
Calendar.YearPickerTriggerHeadingformatDateFormatterOptions-自定义月/年标签(如 {month: 'short'})
Calendar.YearPickerTriggerHeadingoffset{months?: number}-相对聚焦日期偏移标题(多月布局)
Calendar.YearPickerGridformatDateFormatterOptions{year: 'numeric'}自定义年份单元格标签(纪元、历法等)
Calendar.YearPickerGridvisibleYearsnumbermin–max 跨度或 20滑动窗口中显示的年份数。同时设置 minValue 与 maxValue 时默认为完整范围

Calendar.Cell Render

Calendar.Cell 子节点为函数时,可使用 React Aria render props:

Prop类型描述
formattedDatestring单元格的本地化日期标签
isSelectedboolean日期是否选中
isUnavailableboolean日期是否不可用
isDisabledboolean单元格是否禁用
isOutsideMonthboolean日期是否属于相邻月份

有关支持的历法系统及其标识符的完整列表,请参阅:

  • @internationalized/date — 所有日期组件使用的日期类型(CalendarDate、CalendarDateTime、ZonedDateTime)与工具
  • I18nProvider — 覆盖子树的 locale
  • useLocale — 读取当前 locale 与布局方向

相关组件