ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

radix-vue 组件源码解析:YearPickerHeader —— 年份选择器的头部容器与导航骨架

radix-vue 组件源码解析:YearPickerHeader —— 年份选择器的头部容器与导航骨架 radix-vue 组件源码解析YearPickerHeader —— 年份选择器的头部容器与导航骨架【免费下载链接】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导读YearPickerHeader是 radix-vue即 Reka UIVue 版本的 Radix UI中YearPicker年份选择器的头部容器组件负责把上一页 / 年份区间标题 / 下一页三段式导航骨架组织在一起。本文以docs/content/meta/YearPickerHeader.md的 API 定义为主线结合仓库中的YearPicker源码实现YearPickerHeader.vue、YearPickerRoot.vue、useYearPicker.ts与官方文档year-picker.md讲解其 Props 语义、在组件结构中的位置、底层 Primitive 渲染机制及实际使用示例帮助你理解并自定义年份选择器的头部区域。YearPickerHeader 是什么在YearPicker的官方 Anatomyyear-picker.md中YearPickerHeader是紧跟在YearPickerRoot之下的第一个子组件官方描述为Contains the navigation buttons and the heading segments.即它只负责容纳导航按钮与标题片段本身不渲染任何年份数据也不承载交互逻辑。一个标准的年份选择器结构如下script setup import { YearPickerCell, YearPickerCellTrigger, YearPickerGrid, YearPickerGridBody, YearPickerGridRow, YearPickerHeader, YearPickerHeading, YearPickerNext, YearPickerPrev, YearPickerRoot, } from reka-ui /script template YearPickerRoot YearPickerHeader YearPickerPrev / YearPickerHeading / YearPickerNext / /YearPickerHeader YearPickerGrid YearPickerGridBody YearPickerGridRow YearPickerCell YearPickerCellTrigger / /YearPickerCell /YearPickerGridRow /YearPickerGridBody /YearPickerGrid /YearPickerRoot /template其中YearPickerPrev向前翻页按钮默认每次前进 12 年一页YearPickerHeading展示当前页的年份区间例如2020 - 2031YearPickerNext向后翻页按钮。YearPickerHeader在语义上对应传统日历组件的导航条Navigation Bar是YearPicker键盘可达性与屏幕阅读器体验的起点——按Tab聚焦到年份选择器时焦点首先落在第一个导航按钮上见下文无障碍小节。Props API 详解YearPickerHeader的完整 API 定义来自 YearPickerHeader.md其内部结构为YearPickerHeaderProps extends PrimitiveProps因此它只继承并暴露Primitive的两个通用 PropsNameDescriptionTypeRequiredDefaultasThe element or component this component should render as. Can be overwritten by asChild.AsTag \| ComponentNodivasChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details.booleanNo-as自定义渲染的标签或组件YearPickerHeader默认渲染为div因为其源码为const props withDefaults(definePropsYearPickerHeaderProps(), { as: div })当你想让头部容器直接渲染为header、nav或其它语义化标签时通过as指定即可YearPickerHeader asheader YearPickerPrev / YearPickerHeading / YearPickerNext / /YearPickerHeader提示从无障碍角度用asheader或asnav可以进一步强化文档大纲结构这与官方 Calendar 系组件的做法一致。asChild完全接管渲染内容asChild为true时组件不再渲染自身默认的 DOM 元素而是把内部逻辑合并到作为唯一子节点传入的元素上最终渲染的节点就是那个子元素。它优先于as源码注释中的说明为Can be overwritten byasChild。典型用法是把头部容器合并进你自定义的 flex 布局节点YearPickerHeader as-child div classmy-year-picker__header YearPickerPrev / YearPickerHeading / YearPickerNext / /div /YearPickerHeaderasChild是 radix-vue / reka-ui 全组件库统一的组合Composition机制更多细节可参考官方 composition 指南与 styling 指南。正是因为YearPickerHeaderProps只是PrimitiveProps的透传它才得以与全库其它组件如DialogContent、TooltipContent保持完全一致的组合能力。源码级实现Primitive 透传与插槽YearPickerHeader的完整实现非常精简YearPickerHeader.vuescript langts import type { PrimitiveProps } from /Primitive export interface YearPickerHeaderProps extends PrimitiveProps {} /script script setup langts import { Primitive } from /Primitive const props withDefaults(definePropsYearPickerHeaderProps(), { as: div }) /script template Primitive v-bindprops slot / /Primitive /template可以总结出三点实现事实零上下文依赖它不像YearPickerHeading、YearPickerNext那样调用injectYearPickerRootContext()注入根上下文因此它是一个纯布局型组件不关心选中态、禁用态与分页逻辑Primitive 透传所有 Props 原样交给 Primitive 处理as/asChild的渲染决策统一由 Primitive 完成这正是它默认渲染为div、且能任意改标签的原因默认插槽唯一的插槽就是默认插槽内容由调用方自由编排——官方示例中放入的是YearPickerPrev、YearPickerHeading、YearPickerNext三件套。值得一提的是YearPickerHeader本身不携带data-disabled等 Data Attribute相关状态属性位于YearPickerRoot[data-readonly]、[data-disabled]、[data-invalid]、YearPickerHeading/YearPickerGrid[data-disabled]以及YearPickerPrev/YearPickerNext[data-disabled]上这也从侧面印证了头部容器只管布局、不管状态的职责边界。头部三件套如何联动虽然YearPickerHeader本身没有逻辑但它容纳的三个子组件共同构成了年份选择器的导航核心理解它们有助于你判断该在头部放什么、如何自定义。YearPickerHeading区间标题YearPickerHeading.vue 从根上下文注入headingId与headingValue渲染为roleheading、aria-level2并把headingId绑定到id默认内容为headingValue其格式由 useYearPicker.ts 中的computed生成const headingValue computed(() { const firstYear grid.value.cells[0] const lastYear grid.value.cells.at(-1)! return ${formatter.fullYear(toDate(firstYear), headingFormatOptions.value)} - ${formatter.fullYear(toDate(lastYear), headingFormatOptions.value)} })即形如2020 - 2031的区间字符串且会跟随locale使用internationalized/date的格式化器输出本地化格式对于公历BC时代还会附加era: short。YearPickerHeading还提供具名插槽default({ headingValue })允许你自定义标题样式YearPickerHeading v-slot{ headingValue } span classmy-heading{{ headingValue }}/span /YearPickerHeadingYearPickerPrev / YearPickerNext翻页按钮两个按钮组件结构对称YearPickerPrev.vue、YearPickerNext.vue默认渲染为buttonas: button并自动带上aria-labelPrevious page/Next pagetypebutton仅当as仍为button时计算后的disabled/aria-disabled/data-disabled——禁用条件为根组件disabled或到达minValue/maxValue边界见isPrevButtonDisabled/isNextButtonDisabledclick触发根上下文提供的prevPage/nextPage默认每次平移一页yearsPerPage默认 12 年。两者还支持传入各自的nextPage/prevPage函数以覆盖根组件上的翻页函数这在自定义跨页跳转场景下非常实用。完整可运行示例官方 Demodocs/components/demo/YearPicker/css/index.vue展示了带图标按钮的真实写法可直接复制运行script setup import { Icon } from iconify/vue import { CalendarDate } from internationalized/date import { YearPickerCell, YearPickerCellTrigger, YearPickerGrid, YearPickerGridBody, YearPickerGridRow, YearPickerHeader, YearPickerHeading, YearPickerNext, YearPickerPrev, YearPickerRoot, } from reka-ui import ./styles.css const date new CalendarDate(2024, 1, 1) /script template YearPickerRoot v-slot{ grid } :default-valuedate classYearPicker YearPickerHeader classYearPickerHeader YearPickerPrev classYearPickerNavButton Icon iconradix-icons:chevron-left classIcon / /YearPickerPrev YearPickerHeading classYearPickerHeading / YearPickerNext classYearPickerNavButton Icon iconradix-icons:chevron-right classIcon / /YearPickerNext /YearPickerHeader div classYearPickerWrapper YearPickerGrid classYearPickerGrid YearPickerGridBody classYearPickerGridWrapper YearPickerGridRow v-for(years, index) in grid.rows :keyyear-${index} classYearPickerGridRow YearPickerCell v-foryear in years :keyyear.toString() :dateyear classYearPickerCell YearPickerCellTrigger :yearyear classYearPickerCellTrigger / /YearPickerCell /YearPickerGridRow /YearPickerGridBody /YearPickerGrid /div /YearPickerRoot /template要点说明YearPickerRoot通过v-slot{ grid }暴露年份网格头部与网格共享同一份数据:default-value传入CalendarDate来自internationalized/date需先安装该依赖详见 year-picker.md 的 Installation 章节给YearPickerHeader挂 class 即可控制头部布局如 flex 排布三个子组件、设置间距样式方案既可用纯 CSS也可用 Tailwind仓库同时提供css与tailwind两套 Demo。无障碍与键盘交互年份选择器的键盘交互定义在 year-picker.md 的 Accessibility 章节其中与头部直接相关的是按键行为Tab焦点进入年份选择器时首先聚焦到第一个导航按钮即头部内的YearPickerPrevSpace/Enter焦点位于YearPickerNext/YearPickerPrev时执行翻页否则选中年份PageUp/PageDown焦点位于YearPickerCellTrigger时跳到上一页 / 下一页这意味着YearPickerHeader内部子组件的排列顺序决定了 Tab 聚焦的初始位置官方三件套顺序Prev → Heading → Next是经过设计验证的默认导航路径。头部还承担了读屏器定位的职责YearPickerRoot内部包含一个视觉隐藏的roleheading区域显示fullCalendarLabel如Year Picker, 2020 - 2031而YearPickerHeading的可视标题同样带roleheading与aria-level2共同保证结构可被辅助技术完整感知。常见自定义场景小结改成header/navYearPickerHeader asheader强化语义并入自定义布局节点YearPickerHeader as-child让容器直接渲染为你已有的包裹元素自定义图标与文案在YearPickerPrev/YearPickerNext的默认插槽内放置任意图标或文字如上面示例中的Icon自定义标题展示用YearPickerHeading的v-slot{ headingValue }重排标题内容调整每页年份数在YearPickerRoot上设置years-per-page默认 12头部按钮与标题区间会随之联动。总结YearPickerHeader是YearPicker中职责单一但不可缺失的布局组件它通过Primitive提供as/asChild两个组合 Props默认渲染为div用于承载上一页 / 标题 / 下一页导航三件套状态与交互逻辑全部由YearPickerRoot及其注入的上下文headingValue、prevPage、nextPage、禁用边界判断等统一管理。理解它的 Props 语义与源码实现你就能在不破坏无障碍与键盘导航的前提下自由定制年份选择器的头部结构与样式。更多细节可继续阅读 YearPickerRoot.md、YearPickerHeading.md、YearPickerPrev.md、YearPickerNext.md 以及核心实现 useYearPicker.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),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进