ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

radix-vue MonthPickerNext 源码级解析:Month Picker 的年份前进导航按钮是如何实现的

radix-vue MonthPickerNext 源码级解析:Month Picker 的年份前进导航按钮是如何实现的 radix-vue MonthPickerNext 源码级解析Month Picker 的年份前进导航按钮是如何实现的【免费下载链接】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本文以 reka-uiradix-vue中的MonthPickerNext组件为核心完整覆盖其 Props/Slots API并结合 MonthPickerNext.vue、MonthPickerRoot.vue 与 useMonthPicker.ts 的源码实现讲清该按钮的禁用判定逻辑、nextPage覆写机制以及无障碍a11y行为帮助你在 Vue 项目中正确组合 Month Picker 并按需定制翻页行为。MonthPickerNext 是什么MonthPickerNext是 Month Picker 头部导航区中的下一年按钮。Month Picker 是 radix-vue 提供的按月选择日历组件月选择器文档 中标记为 Alpha 阶段每页展示一整年 12 个月而MonthPickerNext负责把日历向后推进一年。官方组件文档对其的定义是Calendar navigation button. It navigates the calendar one year in the future.日历导航按钮将日历推进到未来一年它必须与MonthPickerPrev向前一年、MonthPickerHeading当前年份标题一起放在MonthPickerHeader中并嵌套在MonthPickerRoot内。完整的组件组合方式如下源自 Month Picker 文档 的 Anatomy 章节script setup import { MonthPickerCell, MonthPickerCellTrigger, MonthPickerGrid, MonthPickerGridBody, MonthPickerGridRow, MonthPickerHeader, MonthPickerHeading, MonthPickerNext, MonthPickerPrev, MonthPickerRoot, } from reka-ui /script template MonthPickerRoot MonthPickerHeader MonthPickerPrev / MonthPickerHeading / MonthPickerNext / /MonthPickerHeader MonthPickerGrid MonthPickerGridBody MonthPickerGridRow MonthPickerCell MonthPickerCellTrigger / /MonthPickerCell /MonthPickerGridRow /MonthPickerGridBody /MonthPickerGrid /MonthPickerRoot /template前置依赖方面Month Picker 系列组件基于internationalized/date包处理日期逻辑使用该组件前需要在项目中安装reka-ui与internationalized/date两个依赖。API 参考Props 与 Slots以下内容继承自 MonthPickerNext 元数据文档并结合源码补充了默认值与行为细节。PropsNameDescriptionTypeRequiredDefaultasThe element or component this component should render as. Can be overwritten by asChild.指定渲染的元素或组件可被 asChild 覆盖AsTag \| ComponentNobuttonasChildChange the default rendered element for the one passed as a child, merging their props and behavior.把默认渲染元素换成传入的子元素并合并 props 与行为booleanNo-nextPageThe function to be used for the next page. Overwrites thenextPagefunction set on theMonthPickerRoot.翻页函数覆盖在MonthPickerRoot上设置的nextPage(placeholder: DateValue) DateValueNo-源码印证在 MonthPickerNext.vue 中MonthPickerNextProps继承自PrimitiveProps即as/asChild来自 Primitive 层并声明了类型精确的nextPage属性export interface MonthPickerNextProps extends PrimitiveProps { /** The function to be used for the next page. Overwrites the nextPage function set on the MonthPickerRoot. */ nextPage?: (placeholder: DateValue) DateValue } const props withDefaults(definePropsMonthPickerNextProps(), { as: button })可以注意两点默认渲染为buttonwithDefaults中as: button与 Props 表中的默认值一致nextPage的签名接收当前 placeholderDateValue必须返回新的目标日期这个返回值将决定翻到哪一年。SlotsNameDescriptionTypedisabledCurrent disable state当前禁用状态boolean模板中的插槽用法MonthPickerNext.vueslot :disabled Next year /slot因此可以基于disabled做条件渲染或样式绑定例如MonthPickerNext template #default{ disabled } button :aria-disableddisabled classnav-btn {{ disabled ? — : Next year }} /button /template /MonthPickerNext若不需要自定义内容不传插槽时默认文案为Next year。Data Attributes[data-disabled]Present when disabled.禁用时存在可配合 CSS 选择器做禁用样式渲染行为与无障碍细节MonthPickerNext的完整模板如下Primitive :asprops.as :as-childprops.asChild aria-labelNext year :typeprops.as button ? button : undefined :aria-disableddisabled || undefined :data-disableddisabled || undefined :disableddisabled clickhandleClick 从源码结构看这里体现了几个值得注意的实现决策固定的aria-labelNext year按钮的无障碍标签被硬编码为 Next year而MonthPickerPrev对应 Previous year见 MonthPickerPrev.vue。由于 Month Picker 一页是一年下一页 语义上即下一年。as为button时才注入typebutton避免在表单上下文中被误当作 submit 按钮提交当as换成其他元素如div、span时不设置type。禁用态的双通道表达disabled原生属性仅在asbutton时真正生效与aria-disabled/data-disabled供辅助技术与 CSS 使用。asChild组合能力通过Primitive的 asChild 机制可以把MonthPickerNext的行为合并到你自己的元素上而不额外生成一层 DOM。点击处理function handleClick() { if (disabled.value) return rootContext.nextPage(props.nextPage) }点击时先做禁用短路判断然后把自身携带的nextPage函数可能为undefined作为可选参数传给 Root 上下文提供的nextPage。这种子组件传入函数、Root 统一调度的设计是 radix-vue 日期组件的通用模式。禁用逻辑何时不能前进disabled是一个计算属性MonthPickerNext.vueconst rootContext injectMonthPickerRootContext() const disabled computed( () rootContext.disabled.value || rootContext.isNextButtonDisabled(props.nextPage) )即两个条件任一成立即禁用Root 整体禁用MonthPickerRoot的disabledprop 为true时Prev/Next 按钮全部失效到达上限isNextButtonDisabled判定再前进一页是否超出maxValue。isNextButtonDisabled的实现在 useMonthPicker.tsconst isNextButtonDisabled (nextPageFunc?: (date: DateValue) DateValue) { if (!props.maxValue.value) return false if (props.disabled.value) return true const currentDate grid.value.value if (nextPageFunc || props.nextPage.value) { const nextDate (nextPageFunc || props.nextPage.value)!(currentDate) return isAfter(nextDate.set({ month: 1, day: 1 }), props.maxValue.value) } const nextYear currentDate.add({ years: 1 }).set({ month: 1, day: 1 }) return isAfter(nextYear, props.maxValue.value) }可以拆解出三层判定未设置maxValue→ 永不因边界禁用返回false只能被 Root 的disabled禁用存在自定义nextPage按钮级或 Root 级用该函数实际计算出的目标日期取其 1 月 1 日与maxValue比较保证按你自定义的步进规则做边界检查而不是简单 1 年默认规则目标年份取当前年份 1若maxValue早于该年 1 月 1 日则禁用。与之对称的是isPrevButtonDisabled同文件 L115-L129它检查的是目标年份 12 月 31 日是否早于minValue。测试用例印证了上述行为见 MonthPicker.test.tsit(should not allow navigation after the maxValue (next button), async () { const { pickerProps } { modelValue: calendarDate, maxValue: new CalendarDate(1981, 6, 30) } // 初始 1980点击 next 后到达 1981 await user.click(nextBtn) expect(heading).toHaveTextContent(1981) expect(nextBtn).toHaveAttribute(aria-disabled, true) expect(nextBtn).toHaveAttribute(data-disabled) // 再次点击年份不再变化 await user.click(nextBtn) expect(heading).toHaveTextContent(1981) })即maxValue为 1981-06-30 时从 1980 前进一次到 1981 后按钮获得aria-disabledtrue与data-disabled属性再次点击无效。另有 测试用例 验证了当按钮被渲染为asdiv且禁用时点击同样不会触发导航因为handleClick中的短路判断与原生disabled无关始终生效。nextPage覆写机制与翻页实现nextPage是MonthPickerNext唯一的功能性 prop它的优先级高于MonthPickerRoot上同名 prop——这正是文档中 Overwrites the nextPage function set on the MonthPickerRoot 的含义。执行链路为点击按钮 →handleClick调用rootContext.nextPage(props.nextPage)Root 把该函数交给useMonthPicker中的nextPageuseMonthPicker.tsconst nextPage (nextPageFunc?: (date: DateValue) DateValue) { const currentDate grid.value.value if (nextPageFunc || props.nextPage.value) { // 按钮级 nextPage 优先其次是 Root 级 const newDate (nextPageFunc || props.nextPage.value)!(currentDate) grid.value createMonthGrid({ dateObj: newDate }) props.placeholder.value newDate.set({ month: props.placeholder.value.month, day: props.placeholder.value.day, }) return } // 默认步进1 年 const newDate currentDate.add({ years: 1 }) grid.value createMonthGrid({ dateObj: newDate }) props.placeholder.value newDate.set({ month: props.placeholder.value.month, day: props.placeholder.value.day, }) }三个要点函数入参是grid.value.value当前网格的锚点日期而不是选中值——它决定从哪一年翻返回的newDate会重建 12 个月的网格createMonthGrid同时更新placeholder保留原 placeholder 的月和日只换年份placeholder的变化又会联动标题渲染未提供函数时默认1 年currentDate.add({ years: 1 })。因此典型用法是在 Root 上声明统一步进如按 5 年翻或在个别按钮上局部覆盖。Root 侧nextPage的 prop 声明MonthPickerRoot.vue/** A function that returns the next page of the month picker. Receives the current placeholder as an argument. */ nextPage?: (placeholder: DateValue) DateValue示例按 5 年步进按钮级覆盖按 1 年MonthPickerRoot :max-valuemaxDate :next-page(d) d.add({ years: 5 }) MonthPickerHeader MonthPickerPrev / MonthPickerHeading / !-- 局部覆盖此按钮仍按 1 年前进 -- MonthPickerNext :next-page(d) d.add({ years: 1 }) / /MonthPickerHeader !-- ...grid 部分... -- /MonthPickerRoot由于禁用判定见上一节会调用同一个nextPageFunc计算目标日期自定义步进与maxValue的边界检查是天然一致的例如按 5 年步进、maxValue在 1983 时从 1980 前进会落到 1985超出上限按钮会直接处于禁用态。与其他组件的协作关系MonthPickerNext自身不做任何状态存储它的全部行为来自injectMonthPickerRootContext注入的 Root 上下文MonthPickerRoot.vue 中provideMonthPickerRootContext提供了nextPage、isNextButtonDisabled、disabled、grid、placeholder等成员。从源码结构看这意味着按钮必须在MonthPickerRoot的子树中使用否则无法拿到上下文状态单一来源在 Root网格、placeholder、min/max 边界都在 Root 管理Prev/Next 只是发令器与键盘导航等价文档的 Keyboard Interactions 章节说明PageDown键在MonthPickerCellTrigger上按同月前进一年键盘表测试用例 MonthPicker.test.ts 验证了PageDown/PageUp的效果与点击 Next/Prev 按钮一致——两者最终都走 Root 的nextPage/prevPage。组件在包中的导出入口为 packages/core/src/MonthPicker/index.tsMonthPickerNext与MonthPickerNextProps类型官方示例可参考 MonthPicker 的 story 文件。小结MonthPickerNext是 Month Picker 的年份前进按钮默认渲染为带aria-labelNext year的button支持as/asChild定制它的nextPageprop 覆盖 Root 级同名函数接收当前网格锚点日期并返回目标日期未设置时默认 1 年禁用条件为 Rootdisabled或按自定义步进规则前进后将超出maxValue禁用时同时输出aria-disabled、data-disabled并短路点击组件无本地状态所有导航逻辑由MonthPickerRoot与 useMonthPicker 集中实现并通过测试 MonthPicker.test.ts 中的边界与交互用例持续验证。【免费下载链接】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),仅供参考
返回列表