radix-vue CalendarPrev 组件完全指南:日期控件向前翻页的 Props、Slots 与源码解析
【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue
导读
CalendarPrev是 radix-vue 无障碍日历组件Calendar家族中的"上一页"导航按钮,负责让用户在月历视图中向前(过去方向)翻页。本文以组件官方元数据文档为主体,结合packages/core/src/Calendar/下的源码实现与测试用例,完整讲解其as、asChild、prevPage三个 Props 与disabled插槽的语义、默认值、调用链及边界行为。读完本文,你将掌握如何将CalendarPrev正确接入日历头部导航、如何用prevPage自定义翻页步长(如一次后退十年),以及它如何与CalendarRoot的minValue、disabled等约束协同工作。
CalendarPrev 在日历组件中的位置
radix-vue 的日历由多个细分组件组合而成:CalendarRoot负责状态与网格数据、CalendarHeader承载头部、CalendarHeading显示当前月份标题,而CalendarPrev与CalendarNext则对称地提供"上/下一页"导航能力。它们都通过provide/inject方式从根组件消费上下文,因此必须作为CalendarRoot的子组件使用,单独渲染没有意义。
一个最小可用的组合方式参考官方 story 用例 story/_Calendar.vue:
<CalendarRoot v-slot="{ weekDays, grid }" :model-value="value"> <CalendarHeader> <CalendarPrev /> <CalendarHeading /> <CalendarNext /> </CalendarHeader> <!-- 渲染 CalendarGrid / CalendarCell / CalendarCellTrigger ... --> </CalendarRoot>Props 详解
根据官方元数据文档 docs/content/meta/CalendarPrev.md,CalendarPrev共暴露 3 个 Props,其中as与asChild继承自PrimitiveProps基类,prevPage是日历翻页专用参数。
| Name | Description | Type | Required | Default |
|---|---|---|---|---|
as | The element or component this component should render as. Can be overwritten by asChild. | AsTag \| Component | No | "button" |
asChild | Change the default rendered element for the one passed as a child, merging their props and behavior. | boolean | No | - |
prevPage | The function to be used for the prev page. Overwrites the prevPage function set on the CalendarRoot. | ((placeholder: DateValue) => DateValue) | No | - |
as:渲染为任意元素或组件
默认渲染为原生<button>。类型为AsTag | Component,可以改成a、span乃至任意自定义组件。在 CalendarPrev.vue 中,组件通过withDefaults指定了默认值as: 'button';模板中还会根据as是否为'button'决定是否附加原生type="button"属性,避免在表单场景下触发意外提交:
:type="props.as === 'button' ? 'button' : undefined"asChild:以子元素为渲染本体
asChild为true时,组件不再渲染自身元素,而是将 Props、行为与事件合并到传入的单个子元素上。这是 radix-vue 通用的组合(Composition)机制,适合把翻页逻辑挂到自定义图标按钮或 Tooltip 包裹的触发器上。此时as会被asChild覆盖。
prevPage:自定义向前翻页函数
prevPage接收当前placeholder(类型DateValue,来自@internationalized/date),返回新的占位日期,从而完全接管"向前翻页"的行为。它的优先级高于CalendarRoot上设置的prevPage函数——在 CalendarPrev.vue 的点击处理中,组件把自己的prevPage传给根上下文:
function handleClick() { if (disabled.value) return rootContext.prevPage(props.prevPage) }而根上下文中的prevPage(实现在 useCalendar.ts)遵循"优先使用传入函数,否则使用CalendarRoot配置,再否则使用默认月步进"的三级回退逻辑:默认情况下向前翻一页等价于firstDate.subtract({ months: pagedNavigation ? numberOfMonths : 1 }),即单月显示时后退一个月、多月显示配合pagedNavigation时一次后退全部可见月份数。
典型自定义场景——一次后退十年,测试 Calendar.test.ts 验证了这一点:
<CalendarPrev :prev-page="(date) => date.subtract({ years: 10 })" />Slots 详解:disabled 状态插槽
CalendarPrev仅暴露一个默认插槽:
| Name | Description | Type |
|---|---|---|
disabled | Current disable state | boolean |
插槽的disabled属性反映按钮当前的禁用状态。其计算逻辑在 CalendarPrev.vue:
const disabled = computed( () => rootContext.disabled.value || rootContext.isPrevButtonDisabled(props.prevPage), )即只要满足以下任一条件按钮即被禁用:
- 整个日历被禁用(
CalendarRoot的disabled为true); - 已到达可导航边界——
isPrevButtonDisabled(useCalendar.ts)在设置了minValue且再向前翻一页会越过该最小值时返回true(未设置minValue时永不因边界禁用)。
插槽默认内容为文本Prev page,实际项目中通常用自定义图标替换,并借助插槽属性实现视觉反馈:
<CalendarPrev v-slot="{ disabled }"> <ChevronLeftIcon :class="{ 'opacity-40': disabled }" /> </CalendarPrev>由于插槽属性与组件自身的aria-disabled、data-disabled状态一致(模板中:aria-disabled="disabled || undefined"、:data-disabled="disabled || undefined"、:disabled="disabled"三处同时写入),禁用时原生disabled属性也会生效,鼠标点击被浏览器拦截,同时键盘焦点也不会落入按钮。
可访问性细节
CalendarPrev在模板中固定输出aria-label="Previous page"(见 CalendarPrev.vue),为屏幕阅读器提供语义化名称;当按钮禁用时通过aria-disabled而非仅靠原生disabled暴露状态,配合data-disabled供 CSS 定制样式。整体日历通过CalendarRoot的隐藏标题与完整无障碍标签(fullCalendarLabel)构建 ARIA 网格,Calendar.test.ts中的 axe 无障碍测试(Calendar.test.ts)确认组件树通过自动检测。
与 CalendarRoot 关键参数的联动
CalendarPrev的行为受根组件多个参数影响,理解其联动有助于避免踩坑:
- minValue:向前翻页的硬边界。测试 Calendar.test.ts 演示了当
minValue为 1979-11-25 时,从 1980 年 1 月连点两次CalendarPrev后按钮出现aria-disabled与data-disabled,第三次点击视图不再变化。 - disabled / readonly:
disabled时按钮不可点击也不可聚焦(测试见 Calendar.test.ts);readonly仅禁止选择日期,不影响翻页导航。 - pagedNavigation + numberOfMonths:多月显示时默认每次翻一页移动的月数与可见月数一致,除非自定义
prevPage。
完整示例:带图标与禁用反馈的翻页头部
结合上述内容,一个可直接运行的完整头部导航如下:
<script setup lang="ts"> import type { DateValue } from '@internationalized/date' import { CalendarDate } from '@internationalized/date' import { ref } from 'vue' import { CalendarGrid, CalendarGridBody, CalendarGridHead, CalendarGridRow, CalendarHeadCell, CalendarHeader, CalendarHeading, CalendarNext, CalendarPrev, CalendarRoot, CalendarCell, CalendarCellTrigger, } from 'radix-vue' const value = ref<DateValue>(new CalendarDate(2026, 9, 16)) </script> <template> <CalendarRoot v-slot="{ weekDays, grid }" v-model="value" min-value="2026-01-01"> <CalendarHeader class="flex items-center justify-between"> <CalendarPrev v-slot="{ disabled }" class="rounded p-2 disabled:opacity-40" :prev-page="(date: DateValue) => date.subtract({ months: 1 })" > <span aria-hidden="true">‹</span> </CalendarPrev> <CalendarHeading /> <CalendarNext> <span aria-hidden="true">›</span> </CalendarNext> </CalendarHeader> <CalendarGrid v-for="month in grid" :key="month.value.toString()"> <CalendarGridHead> <CalendarGridRow> <CalendarHeadCell v-for="day in weekDays" :key="day"> {{ day }} </CalendarHeadCell> </CalendarGridRow> </CalendarGridHead> <CalendarGridBody> <CalendarGridRow v-for="(week, i) in month.rows" :key="i"> <CalendarCell v-for="date in week" :key="date.toString()" :date="date"> <CalendarCellTrigger :day="date" :month="month.value"> {{ date.day }} </CalendarCellTrigger> </CalendarCell> </CalendarGridRow> </CalendarGridBody> </CalendarGrid> </CalendarRoot> </template>注意上例中minValue会随CalendarPrev的翻页自动传导为按钮禁用态——当视图已回到 2026 年 1 月时,按钮自动置灰,无需手写边界判断。
小结
CalendarPrev虽小,却是日历可用性的关键一环:它以Primitive为基础提供灵活渲染,通过prevPage实现可覆盖的自定义翻页策略,通过disabled插槽透出禁用态,并在aria-label、aria-disabled、原生disabled三层面保障无障碍体验。与CalendarRoot的minValue、disabled、pagedNavigation等参数联动后,几乎可以覆盖从单月后退到十年跳转的所有真实业务场景。更多进阶用法(如多选、fixedWeeks、键盘导航)可继续阅读 CalendarRoot 元数据 与 Calendar 组件文档。
【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考