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' | 是否可选择单个或多个日期 |
value | DateValue | null 或 DateValue[] | null | - | 受控选中日期。selectionMode 为 multiple 时使用数组 |
defaultValue | DateValue | null 或 DateValue[] | null | - | 初始选中日期(非受控) |
onChange | (value: DateValue | null) 或 (value: DateValue[] | null) => void | - | 选择变化时调用 |
focusedValue | DateValue | - | 受控聚焦日期 |
onFocusChange | (value: DateValue) => void | - | 焦点移至其他日期时调用 |
minValue | DateValue | 历法感知的 1900-01-01 | 最早可选日期 |
maxValue | DateValue | 历法感知的 2099-12-31 | 最晚可选日期 |
weeksInMonth | number | - | 月份中的周数,覆盖 locale 默认值 |
isDateUnavailable | (date: DateValue) => boolean | - | 标记日期为不可用 |
firstDayOfWeek | 'sun' | 'mon' | 'tue' | 'wed' | 'thu' | 'fri' | 'sat' | - | 覆盖 locale 默认的一周起始日 |
pageBehavior | 'visible' | 'single' | 'visible' | 翻页按可见时长还是单个单位前进 |
selectionAlignment | 'start' | 'center' | 'end' | 'center' | 初始渲染时可见范围与选择的对齐方式 |
isDisabled | boolean | false | 禁用交互与选择 |
isReadOnly | boolean | false | 内容可读但不可更改选择 |
isInvalid | boolean | false | 标记日历为无效以显示验证 UI |
visibleDuration | {months?: number; weeks?: number; days?: number} | {months: 1} | 可见时间范围。月视图用 { months: n },周视图用 { weeks: n },日视图用 { days: n } |
defaultYearPickerOpen | boolean | false | 内部年份选择器初始打开状态 |
isYearPickerOpen | boolean | - | 受控年份选择器打开状态 |
onYearPickerOpenChange | (isOpen: boolean) => void | - | 年份选择器打开状态变化时调用 |
Composition Parts
| Component | Description |
|---|---|
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 的格式化属性。
| Component | Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|---|
Calendar.YearPickerTriggerHeading | format | DateFormatterOptions | - | 自定义月/年标签(如 {month: 'short'}) |
Calendar.YearPickerTriggerHeading | offset | {months?: number} | - | 相对聚焦日期偏移标题(多月布局) |
Calendar.YearPickerGrid | format | DateFormatterOptions | {year: 'numeric'} | 自定义年份单元格标签(纪元、历法等) |
Calendar.YearPickerGrid | visibleYears | number | min–max 跨度或 20 | 滑动窗口中显示的年份数。同时设置 minValue 与 maxValue 时默认为完整范围 |
Calendar.Cell Render
Calendar.Cell 子节点为函数时,可使用 React Aria render props:
| Prop | 类型 | 描述 |
|---|---|---|
formattedDate | string | 单元格的本地化日期标签 |
isSelected | boolean | 日期是否选中 |
isUnavailable | boolean | 日期是否不可用 |
isDisabled | boolean | 单元格是否禁用 |
isOutsideMonth | boolean | 日期是否属于相邻月份 |
有关支持的历法系统及其标识符的完整列表,请参阅:
Related packages
@internationalized/date— 所有日期组件使用的日期类型(CalendarDate、CalendarDateTime、ZonedDateTime)与工具I18nProvider— 覆盖子树的 localeuseLocale— 读取当前 locale 与布局方向