资讯动态

Radix Vue 组件指南:YearPickerGridBody 网格主体剖析与使用

发布时间:2026/9/17 16:40:43 来源:尧图企业网站定制
Radix Vue 组件指南YearPickerGridBody 网格主体剖析与使用【免费下载链接】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本文基于 radix-vue 仓库Radix VueVue 的高质量无障碍 UI 组件库中的 YearPickerGridBody.md 文档展开深入解析 YearPicker年份选择器中网格主体容器的 Props 设计、底层 Primitive 渲染机制并结合源码、示例与测试说明其在真实项目中的组合方式与无障碍细节帮助读者掌握 Radix Vue 组件的组合Composition模式。一、组件定位YearPicker 网格结构中的“tbody”在 Radix Vue 中YearPicker是一个专注于年份选择的无障碍日历组件其内部采用了一套与 HTML 表格语义对齐的组合式结构组件默认渲染元素语义/角色YearPickerRootdiv整体容器roleapplication的载体YearPickerHeaderdiv头部导航上/下一年按钮、标题YearPickerGridtable网格容器roleapplicationYearPickerGridBodytbody网格主体容器本文核心YearPickerGridRowtr行容器rolerowYearPickerCelltd单元格容器rolegridcellYearPickerCellTriggerbutton可交互的年份按钮含选中/禁用/今天等 data 状态YearPickerGridBody在语义上对应 HTML 表格的tbody负责承载若干行Row年份单元格是整个年份网格的“主体躯干”。它自身不承担交互逻辑而是作为结构化的分组容器配合YearPickerGrid与YearPickerGridRow完成可访问的网格布局。二、Props 全量说明as 与 asChildYearPickerGridBody的完整 API 继承自 Primitive仅暴露两个属性NameDescriptionTypeRequiredDefaultas该组件要渲染成的元素或组件。可被asChild覆盖。AsTag \| ComponentNotbodyasChild将默认渲染的元素替换为传入的子元素并合并其 props 与行为。详见 Composition 指南。booleanNo-这两个属性均由 Primitive.ts 中的PrimitiveProps定义YearPickerGridBody通过interface YearPickerGridBodyProps extends PrimitiveProps {}直接继承见 YearPickerGridBody.vue并在withDefaults(definePropsYearPickerGridBodyProps(), { as: tbody })中把默认渲染元素固定为tbody。2.1as替换渲染元素默认情况下YearPickerGridBody会渲染为tbody。当需要把网格主体变成其他元素或自定义组件时通过as指定YearPickerGridBody asdiv !-- 用 div 替代 tbody -- /YearPickerGridBody需要说明的是as支持的类型AsTag是一个白名单字符串联合类型a | button | div | form | h2 | h3 | img | input | label | li | nav | ol | p | span | svg | ul | template以及任意字符串同时也允许传入组件对象AsTag | Component。Primitive 内部通过h(props.as, attrs, { default: slots.default })渲染目标元素并针对area、img、input这类自闭合标签做了 hydration 特殊处理见 Primitive.ts。2.2asChild完全接管子元素asChild是 Radix VueRadix Vue 系组合模式的精髓。当为true时组件不再渲染自己的默认元素而是把内部行为与 props 合并到插槽中的第一个子元素上YearPickerGridBody asChild tbody classcustom-body !-- 自定义 tbody同时继承 YearPickerGridBody 的 props 与行为 -- /tbody /YearPickerGridBody从 Primitive.ts 的源码可以看到asChild模式下内部改用Slot组件渲染asTag template分支这正是“合并 props 与行为”的实现基础。这种机制允许你把组件的能力无缝嫁接到自己的设计系统组件或自定义 DOM 上例如在 Tailwind 布局中把网格主体渲染为 CSS Grid 容器见下文示例。注意Composition 指南特别提醒见 composition.md——当通过as或asChild改变默认元素时需要自行确保可访问性。例如把可交互的 trigger 换成div会失去键盘焦点能力。对于YearPickerGridBody这类纯容器风险相对低但若改变其表格语义仍需自行维护对应的 ARIA 角色。三、源码实现极简容器背后的设计哲学3.1 实现文件YearPickerGridBody的完整实现非常精简YearPickerGridBody.vuescript langts import type { PrimitiveProps } from /Primitive export interface YearPickerGridBodyProps extends PrimitiveProps {} /script script setup langts import { Primitive } from /Primitive const props withDefaults(definePropsYearPickerGridBodyProps(), { as: tbody }) /script template Primitive v-bindprops slot / /Primitive /template对比其“邻居”组件可以看到分层设计YearPickerGridYearPickerGrid.vue默认渲染为table并注入tabindex-1、roleapplication、aria-labelledby指向 headingId、aria-readonly、aria-disabled以及data-readonly/data-disabled等属性——网格级状态由它统一承载。YearPickerGridRowYearPickerGridRow.vue默认渲染为tr并固定注入rolerow。YearPickerGridBody默认渲染为tbody不注入任何角色或状态属性纯粹作为结构化容器。这种“根/行携带状态、主体/单元格只做结构”的分工让YearPickerGridBody保持极低的心智负担它不关心选中、禁用、只读等状态这些由YearPickerGrid与YearPickerCell处理只负责把若干行年份组织在一个语义正确的分组里。3.2 与兄弟组件的协作在 YearPickerDefault.story.vue 的完整示例中可以看到它的真实用法——结合 Tailwind 把表格语义重构成 CSS Grid 布局YearPickerGrid classw-full border-collapse select-none space-y-1 mt-4 YearPickerGridBody classgrid YearPickerGridRow v-for(yearRow, rowIndex) in grid.rows :keyyearRow-${rowIndex} classgrid grid-cols-4 gap-1 YearPickerCell v-foryear in yearRow :keyyear.toString() :dateyear classrelative text-center text-sm YearPickerCellTrigger :yearyear classrelative flex items-center justify-center ... / /YearPickerCell /YearPickerGridRow /YearPickerGridBody /YearPickerGrid注意这里YearPickerGridBody通过classgrid变身为 CSS Grid 容器行组件用grid-cols-4实现每行 4 个年份的布局——这正对应createYearGrid生成的“3 行 × 4 列 12 年/页”的网格结构见下文。grid数据来自YearPickerRoot的默认插槽解构v-slot{ grid }。四、数据流与网格生成原理YearPickerGridBody本身不产生数据它消费的是YearPickerRoot通过createContext注入的网格数据。数据链路如下网格生成useYearPickeruseYearPicker.ts调用createYearGrid生成GridDateValue含value、cells、rows。网格结构createYearGridcalendar.ts默认按年代对齐decade-aligned生成网格——从当前日期所在年代的第一年开始按yearsPerPage默认12逐年生成并按每行 4 个chunk成rows形成 3 行 × 4 列的布局export function createYearGrid(props: CreateSelectProps { yearsPerPage?: number, decadeAligned?: boolean }): GridDateValue { const { dateObj, yearsPerPage 12, decadeAligned true } props let startYear: number if (decadeAligned) startYear startOfDecade(dateObj).year else startYear dateObj.year const years Array.from({ length: yearsPerPage }, (_, i) startOfYear(dateObj.set({ year: startYear i }))) const firstYear years[0] return { value: firstYear, cells: years, rows: chunk(years, 4) } }注入消费YearPickerRoot把grid、disabled、readonly、headingId等打包成 context见 YearPickerRoot.vueYearPickerGrid从中读取disabled/readonly/headingId并映射为 ARIA 属性YearPickerCell则读取isYearSelected/isYearDisabled/isYearUnavailable等匹配函数。YearPickerGridBody不注入也不消费任何 context只做透传渲染——这也是它实现如此简洁的原因。补充YearPickerRoot还支持multiple多选、preventDeselect禁止取消选中、minValue/maxValue范围限制、locale本地化、initialFocus挂载后聚焦选中/今天/首个年份等能力见 YearPickerRoot.vue。其中minValue/maxValue会经由isYearDisabled逻辑影响网格中哪些年份单元格被标记为data-disabled见 useYearPicker.ts。五、无障碍与数据属性虽然YearPickerGridBody自身不渲染 data 属性但它所在的网格体系具备完整的无障碍语义网格容器YearPickerGridroleapplicationaria-labelledby关联年份范围标题 aria-readonly/aria-disabled状态并输出data-readonly、data-disabled见 YearPickerGrid.vue。行容器rolerow。单元格YearPickerCellrolegridcellaria-selected/aria-disabled并输出data-disabled见 YearPickerCell.vue。年份触发器YearPickerCellTrigger支持data-selected、data-valueISO 日期字符串、data-disabled、data-unavailable、data-today、data-focused等状态属性见 year-picker.md可直接用于样式定制。测试用例对状态的验证YearPicker.test.ts也印证了这一点// disabled 时网格与年份单元格均被标记且不可聚焦、不可点击 expect(grid).toHaveAttribute(aria-disabled, true) expect(grid).toHaveAttribute(data-disabled) expect(year1980).toHaveAttribute(aria-disabled, true) expect(year1980).toHaveAttribute(data-disabled) // readonly 时禁止选择但允许聚焦 expect(grid).toHaveAttribute(aria-readonly, true) expect(grid).toHaveAttribute(data-readonly)键盘交互方面YearPicker 支持方向键在网格内移动焦点跨页时自动翻页、PageUp/PageDown前后翻页、Space/Enter选择年份等完整键盘导航见 year-picker.md。六、完整实战示例将以上知识整合一个完整的 YearPicker含网格主体实现如下script setup langts import { YearPickerCell, YearPickerCellTrigger, YearPickerGrid, YearPickerGridBody, YearPickerGridRow, YearPickerHeader, YearPickerHeading, YearPickerNext, YearPickerPrev, YearPickerRoot, } from reka-ui // radix-vue 的发布名本仓库即为其源码 /script template YearPickerRoot v-slot{ grid } classw-64 YearPickerHeader YearPickerPrev / YearPickerHeading / YearPickerNext / /YearPickerHeader YearPickerGrid YearPickerGridBody YearPickerGridRow v-for(yearRow, rowIndex) in grid.rows :keyyearRow-${rowIndex} YearPickerCell v-foryear in yearRow :keyyear.toString() :dateyear YearPickerCellTrigger :yearyear / /YearPickerCell /YearPickerGridRow /YearPickerGridBody /YearPickerGrid /YearPickerRoot /template要点总结YearPickerGridBody默认渲染tbody是年份网格的结构化主体通过as可替换渲染元素通过asChild可把行为合并到自定义子元素上参见 Composition 指南网格数据由YearPickerRoot注入默认 12 年/页、年代对齐、每行 4 列grid.rows驱动行与单元格的渲染状态与无障碍属性由YearPickerGrid、YearPickerCell、YearPickerCellTrigger分层承担YearPickerGridBody保持纯粹的结构职责测试用例YearPicker.test.ts与 story 示例YearPickerDefault.story.vue提供了可运行、可验证的参考。提示radix-vue 仓库中的日期相关组件依赖internationalized/date包使用前需安装该依赖详见 year-picker.md。【免费下载链接】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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价