- UI组件
- 前端
【免费下载链接】react-day-picker
DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications.
导读
labelGrid()是 React DayPicker 无障碍(Accessibility)体系中的核心标签函数之一,专门为日历的**月份网格(month grid)**生成 ARIA 标签,屏幕阅读器在用户进入网格时播报这段文字。本文基于 React DayPicker 9.14.0 版本的 API 文档与源码,完整讲解labelGrid()的函数签名、参数语义、底层实现原理、国际化差异,以及如何通过labels属性定制这段无障碍文案,帮助开发者把日期选择器打造成可被屏幕阅读器正确朗读、符合 WAI-ARIA 规范的日历控件。
一、函数签名与用途
labelGrid()定义于 packages/react-day-picker/src/labels/labelGrid.ts,类型签名如下:
labelGrid(date: Date, options?: DateLibOptions, dateLib?: DateLib): string该函数的职责正如其 JSDoc 注释所述:"Generates the ARIA label for the month grid, which is announced when entering the grid"——即生成月份网格的 ARIA 标签,该标签在用户(通过键盘焦点或屏幕阅读器)进入网格时被播报。它与组件的role="grid"语义相配合,是日历无障碍体验的重要一环。
在 DayPicker.tsx 中,labelGrid()的返回值被直接挂到月份网格的aria-label上:
<components.MonthGrid role="grid" aria-multiselectable={mode === "multiple" || mode === "range"} aria-label={ labelGrid(calendarMonth.date, dateLib.options, dateLib) || undefined } // ... >其中第一个参数calendarMonth.date是当前渲染月份的代表日期(通常是该月某一天,用于推导月份与年份),第二个参数传入dateLib.options(即DateLibOptions),第三个参数传入当前DateLib实例。这里传入dateLib意味着复用组件内部已实例化好的日期库对象,避免重复构造。
二、参数详解
| 参数 | 类型 | 说明 |
|---|---|---|
date | Date | 代表当前月份的一个日期,用于提取月份与年份信息 |
options? | DateLibOptions | 可选,日期格式化库的配置,包含locale、timeZone、numerals、Date等 |
dateLib? | DateLib | 可选,一个DateLib实例;缺省时函数内部会根据options自行构造 |
其中DateLibOptions的关键属性(定义于 DateLib.ts)包括:
locale?: DayPickerLocale——用于日期格式化的语言区域,同时也是 DayPicker 各标签本地化翻译的载体;timeZone?: string——日期所应用的时区(自 9.5.0 起支持);numerals?: Numerals——数字编号系统(自 9.5.0 起支持);Date?: DateConstructor——Date对象的构造函数,可用于注入自定义日期实现。
DateLib是 React DayPicker 对 date-fns 的一层封装(自 9.2.0 起引入),提供日期运算、格式化等统一能力,其完整方法列表可参阅 DateLib API 文档。
三、返回值与默认值
函数返回string类型——月份网格的 ARIA 标签文本。默认输出为符合当前 locale 习惯的"月份 + 年份"组合,例如英语环境下为:
November 2022在日语等"年份在前"的语言环境下则为:
2022年11月四、底层实现:一行代码背后的日期库
labelGrid()的实现非常精简,本质上是把格式化工作委托给了DateLib:
export function labelGrid( date: Date, options?: DateLibOptions, dateLib?: DateLib, ) { const lib = dateLib ?? new DateLib(options); return lib.formatMonthYear(date); }即:若未传入dateLib实例,则以options为参数构造一个新的DateLib(new DateLib(options)),随后调用lib.formatMonthYear(date)完成"月份 + 年份"的格式化。
formatMonthYear()的国际化细节
真正的格式化逻辑在 DateLib.ts 的formatMonthYear()方法中(该方法自 9.11.0 起提供)。其处理流程体现了 DayPicker 对国际化差异的细致考量:
- 先通过
DateLib.yearFirstLocales集合判断当前 locale 是否属于"年份在前"习惯; - 若是,则优先尝试使用
Intl.DateTimeFormat(localeCode, { month: "long", year: "numeric", timeZone, numberingSystem: numerals })直接格式化,此时timeZone与numerals配置都会被尊重; - 若
Intl.DateTimeFormat抛异常(例如个别运行环境不支持),则回退到 date-fns 的format()路径; - 对于"月份在前"的 locale,使用 date-fns 格式化模式
"LLLL y"(如November 2022);对于"年份在前"的 locale,使用"y LLLL"(如2022年11月)。
"年份在前"的 locale 集合(yearFirstLocales,见 DateLib.ts)包括:eu、hu、ja、ja-Hira、ja-JP、ko、ko-KR、lt、lt-LT、lv、lv-LV、mn、mn-MN、zh、zh-CN、zh-HK、zh-TW。这也解释了为何同一份"月份 + 年份"文案在不同语言下会呈现不同的词序。
测试用例验证
packages/react-day-picker/src/labels/labelGrid.test.ts 给出了两组最直接的验证:
const day = new Date(2022, 10, 21); test("return the label", () => { expect(labelGrid(day)).toEqual("November 2022"); }); test("returns year-first labels when required", () => { expect(labelGrid(day, { locale: ja })).toEqual("2022年11月"); });可以看到:默认情况下2022-11-21会输出November 2022;当传入日语 localeja后,输出变为年份在前的2022年11月。这也是options参数直接影响输出结果的直接证据。
五、通过labels属性定制网格标签
在实际应用中,开发者往往需要将"2022年11月"这类文案替换为符合产品语境的表述(例如"2022年11月日历")。DayPicker 提供了labels属性,其中labelGrid键即可覆盖该函数。
覆盖写法
import { DayPicker } from "react-day-picker"; <DayPicker labels={{ labelGrid: (date, options, dateLib) => { const lib = dateLib ?? new DateLib(options); return `日历:${lib.formatMonthYear(date)}`; }, }} />Labels类型的定义位于 packages/react-day-picker/src/types/shared.ts,其中对labelGrid的类型约束为typeof labelGrid,保证自定义函数与默认函数拥有完全一致的签名。
标签解析优先级
自定义标签并非直接替换默认实现,而是经过 helpers/getLabels.ts 中的resolveLabel合并逻辑处理,优先级为:
labels属性中传入的自定义函数(最高优先级);- locale 自带的翻译(
options.locale.labels中的同名键,既可以是字符串也可以是函数); - 默认标签实现(兜底)。
因此,如果某个 locale 已经通过labels提供了labelGrid翻译,DayPicker 会优先采用它;只有开发者显式在labels中传入labelGrid时,才会覆盖 locale 级翻译。例如 locale/ja.ts 中日语 locale 就自带labelGrid实现(内部同样调用formatMonthYear)。
一个实用的自定义场景
若希望网格标签包含年份与月份之外的信息(比如多个月视图中的"第 2 个月"),可以这样写:
<DayPicker numberOfMonths={2} labels={{ labelGrid: (date, options, dateLib) => { const lib = dateLib ?? new DateLib(options); return `第 ${date.getMonth() + 1} 月,${lib.formatMonthYear(date)}`; }, }} />需要注意:labelGrid在组件渲染时会对每个月份网格分别调用(对应DayPicker.tsx中每个MonthGrid的aria-label),因此自定义实现中不要依赖闭包内的单次状态,而应完全基于传入的date参数计算结果。
六、与相关标签函数的配合
labelGrid()属于 DayPicker 标签函数家族的一员,与之并列的还有(全部导出自 packages/react-day-picker/src/labels/index.ts):
| 标签函数 | 作用对象 |
|---|---|
labelGrid | 月份网格(role="grid"的容器) |
labelGridcell | 网格单元格(当日历不可交互时使用) |
labelNav | 导航工具栏 |
labelPrevious/labelNext | 上一月 / 下一月按钮 |
labelMonthDropdown/labelYearDropdown | 月份 / 年份下拉框 |
labelWeekday | 星期表头 |
labelWeekNumber/labelWeekNumberHeader | 周数单元格及其表头 |
labelDayButton | 日期按钮 |
这些函数共同构成了 DayPicker 完整的 ARIA 标签体系。其中与labelGrid语义最接近的是labelGridcell——前者描述整个网格,后者描述网格中的单元格。关于整套标签体系的定制与本地化实践,可参考官方翻译指南(对应 apps/website/versioned_docs/version-9.14.0/guides 中 "aria-labels" 一节的讨论)。
七、无障碍实践要点
综合以上源码分析,使用labelGrid()时值得注意以下几点:
- 默认行为已满足多数场景:DayPicker 内置的
labelGrid()会自动依据locale输出正确的"月份 + 年份"文案,且对中、日、韩、匈牙利语等"年份在前"的语言做了专门处理,无需额外配置; - 传入
dateLib可提升性能:在自定义实现中,优先复用传入的dateLib实例(dateLib ?? new DateLib(options)),避免每次渲染重复构造对象; - 文案应保持简短:网格标签会在进入网格时被整体播报,建议保持与默认值相当的简短程度(月份 + 年份),不要把冗长的说明塞进
aria-label; - 不要返回空字符串:
DayPicker.tsx中对返回值做了|| undefined兜底,即空字符串会使aria-label属性被移除,可能导致网格失去无障碍标识,自定义时需保证始终返回非空字符串; - 与 locale 翻译协同:若在
labels中全局覆盖labelGrid,会同时覆盖所有 locale 的对应翻译;若仅需针对某个语言定制,优先在该语言 locale 的labels字段中提供翻译,而不是覆盖全局labels。
结语
labelGrid()虽然只是一个十几行的工具函数,但它串联起了DateLib国际化格式化、labels自定义体系、WAI-ARIA 网格语义与屏幕阅读器播报流程。理解它的签名、默认实现与覆盖机制,是构建具备良好无障碍体验的 React 日期选择器的关键一步。相关源码均可继续在 packages/react-day-picker/src/labels/labelGrid.ts、packages/react-day-picker/src/classes/DateLib.ts 与 packages/react-day-picker/src/helpers/getLabels.ts 中深入研读。
- UI组件
- 前端
【免费下载链接】react-day-picker
DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications.
相关推荐
react-day-picker 的 labelGrid():为月份网格生成无障碍 ARIA 标签的完整解析
react day picker 的 labelGrid :为月份网格生成无障碍 ARIA 标签的完整解析 labelGrid 是 react day pick
UI组件前端React DayPicker `labelPrevious()` 函数解析:为"上个月"按钮生成 ARIA 无障碍标签
React DayPicker labelPrevious 函数解析:为"上个月"按钮生成 ARIA 无障碍标签 labelPrevious 是 React D
UI组件前端React DayPicker labelMonthDropdown 函数详解:月份下拉框的 ARIA 无障碍标签
React DayPicker labelMonthDropdown 函数详解:月份下拉框的 ARIA 无障碍标签 labelMonthDropdown 是 R
UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考