ARTICLE DETAIL

资讯详情

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

radix-vue YearRangePickerCell 与 YearRangePickerCellTrigger 深度指南:Props、Slots 与交互实现全解

radix-vue YearRangePickerCell 与 YearRangePickerCellTrigger 深度指南:Props、Slots 与交互实现全解 radix-vue YearRangePickerCell 与 YearRangePickerCellTrigger 深度指南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本文以 radix-vue 仓库组件对外以reka-ui包名分发中YearRangePicker的单元格组件为研究对象完整拆解YearRangePickerCell与YearRangePickerCellTrigger的属性定义、作用域插槽、渲染输出与底层交互逻辑并结合源码给出可直接复用的实战示例。读完本文你将掌握一年粒度区间选择器中单元格层的全部 API 与实现原理能够熟练定制单元格样式、理解其选中/高亮/禁用状态机并能为自己的日期区间组件复刻这套交互范式。组件定位一年粒度区间选择视图中的单元格YearRangePickerCell与YearRangePickerCellTrigger是 YearRangePicker一年粒度区间选择器文档标注为 Alpha 阶段的两层核心单元YearRangePickerCell负责承载并标记某一年的格子YearRangePickerCellTrigger则是格子内真正可交互的触发元素——点击它即选中该年份键盘聚焦时它承接完整的区间选择导航能力。从组件树看它们处于YearRangePickerRoot → YearRangePickerGrid → YearRangePickerGridBody → YearRangePickerGridRow → YearRangePickerCell → YearRangePickerCellTrigger链路的末端是用户最终看得见、点得着的节点。整条链路的所有公共部件均从 packages/core/src/YearRangePicker/index.ts 统一导出。在深入 Props 之前先明确两个前提源自 year-range-picker.md 的 Preface 与 Installation 章节该组件依赖internationalized/date包DateValue、DateRange、CalendarDate等类型均来自该包使用前需要先安装它组件本身随reka-ui包一起安装YearRangePickerCell、YearRangePickerCellTrigger可直接从reka-ui具名导入。组件结构Cell 与 CellTrigger 的分工两个组件职责清晰分离对应两份元数据文档组件元数据文档默认渲染元素核心职责YearRangePickerCellYearRangePickerCell.mdtdrolegridcell容器标注选中/禁用状态承载 triggerYearRangePickerCellTriggerYearRangePickerCellTrigger.mddivrolebutton交互点击选中、键盘导航、暴露全部状态插槽从源码看YearRangePickerCell渲染的是一个无逻辑的Primitive容器Primitive :asas :as-childasChild rolegridcell :aria-selectedrootContext.isSelected(date) ? true : undefined :aria-disabledrootContext.isYearDisabled(date) || rootContext.isYearUnavailable?.(date) :data-disabledrootContext.isYearDisabled(date) ? : undefined slot / /Primitive对应源码见 YearRangePickerCell.vue它只做三件事——设置gridcell角色、根据区间选择状态输出aria-selected、根据禁用/不可用判定输出aria-disabled与data-disabled。真正的交互逻辑全部下沉到 Trigger 层。YearRangePickerCell Props 详解依据 YearRangePickerCell.mdYearRangePickerCell共暴露 3 个属性NameDescriptionTypeRequiredDefaultas组件应渲染为的元素或组件可被asChild覆盖AsTag \| ComponentNotdasChild将默认渲染元素替换为传入的子元素并合并其 props 与行为booleanNo-date单元格对应的日期值DateValueYes-as 与 asChildas与asChild是 radix-vue 全库统一的 Primitive 组合能力来自/Primitive类型定义见 YearRangePickerCell.vueas默认渲染为td可改为任意 HTML 标签或 Vue 组件asChild一旦开启默认元素被完全替换为你在插槽里写的第一个元素两者通过运行时合并 props 与事件实现继承行为。典型场景是单元格需渲染为th表头语义或自定义组件时使用绝大多数情况下保持默认td即可。datedate是必填属性类型为internationalized/date的DateValue。它是单元格的身份标识Root 层的isSelected、isYearDisabled、isYearUnavailable等判定函数都以它为准。实际取值通常由上一级YearRangePickerGridBody在遍历gridGridDateValue时逐格生成用户一般不手工构造。YearRangePickerCellTrigger Props 详解依据 YearRangePickerCellTrigger.mdTrigger 层同样暴露 3 个属性NameDescriptionTypeRequiredDefaultas组件应渲染为的元素或组件可被asChild覆盖AsTag \| ComponentNodivasChild将默认渲染元素替换为传入的子元素并合并其 props 与行为booleanNo-year提供给单元格触发器的日期值DateValueYes-as默认值为div注意与 Cell 的td不同结合rolebutton构成语义化的可点击节点asChild用法与 Cell 一致常用于把年份数字渲染进button或spanyear与 Cell 的date对应传同一个DateValue即可Trigger 内部通过toDate(props.year)与formatter.fullYear将其格式化为年份文本见 YearRangePickerCellTrigger.vue。CellTrigger 的 10 个作用域插槽状态这是 Trigger 层最具价值的能力默认插槽暴露10 个布尔/字符串状态让单元格样式完全由数据驱动。完整清单见 YearRangePickerCellTrigger.mdNameDescriptionTypeyearValue当前年份值stringdisabled当前禁用状态booleanselected当前选中状态booleantoday当前年份是否为今年booleanunavailable当前不可用状态booleanhighlighted当前高亮状态用户拖选/键盘框选过程中booleanhighlightedStart当前是否为高亮区间的起点booleanhighlightedEnd当前是否为高亮区间的终点booleanselectionStart当前是否为选中区间的起点booleanselectionEnd当前是否为选中区间的终点boolean源码中这些状态的来源YearRangePickerCellTrigger.vuetoday用toCalendar(today(getLocalTimeZone()), props.year.calendar)换算当前日期后与year做isSameYear比较因此支持非公历日历系统unavailable直接调用 Root 注入的isYearUnavailable?.(props.year)highlighted判断year是否落在highlightedRange区间内区间由 useRangeYearPicker.ts 计算得出见下文高亮区间计算selected/selectionStart/selectionEnd分别对应isSelected、isSelectionStart、isSelectionEnd。一个典型用法是把这些状态映射为 Tailwind/CSS 类YearRangePickerCellTrigger v-slot{ yearValue, selected, today, selectionStart, selectionEnd, disabled } span :class{ bg-blue-500 text-white: selected, rounded-l: selectionStart, rounded-r: selectionEnd, ring-1 ring-blue-200: today !selected, opacity-50: disabled, } {{ yearValue }}/span /YearRangePickerCellTrigger渲染输出与 Data Attributes 映射Trigger 的渲染结果远不止一个年份文本它向外暴露了一套完整的data-*属性供样式选择器与测试使用见 year-range-picker.md 中 Cell Trigger 一节及 YearRangePickerCellTrigger.vue 模板Attribute含义[data-selected]存在即选中[data-value]日期的 ISO 字符串值如2024-01-01[data-disabled]存在即禁用[data-unavailable]存在即不可用[data-today]存在即当年[data-selection-start]/[data-selection-end]选中区间起点 / 终点[data-highlighted]/[data-highlighted-start]/[data-highlighted-end]高亮区间及端点[data-focused]存在即当前聚焦项[data-reka-year-range-picker-cell-trigger]组件标记固定输出同时输出的 ARIA 属性包括rolebutton、aria-label年份文本、aria-pressed选中时且仅在allowNonContiguousRanges开启或年份未不可用时、aria-disabled。tabindex采用 roving tabindex 策略——聚焦年份为0、其余为-1、禁用年份不设tabindex。CSS 定位一个高亮区间中的普通年份只需[data-reka-year-range-picker-cell-trigger][data-highlighted] { background-color: var(--highlight-bg); } [data-reka-year-range-picker-cell-trigger][data-selection-start] { border-start-start-radius: 6px; }源码实现基于 Primitive 与注入式上下文两个组件都是典型的薄壳 上下文实现核心依赖两件事Primitiveradix-vue 的通用渲染原语/Primitive负责as/asChild的运行时解析、属性合并与事件透传两个组件都通过withDefaults(defineProps...(), { as: td | div })声明默认渲染元素injectYearRangePickerRootContextRoot 在 YearRangePickerRoot.vue 通过provideYearRangePickerRootContext注入的上下文对象包含startValue/endValue、isSelected、isYearDisabled、isYearUnavailable、allowNonContiguousRanges、highlightedRange、minValue/maxValue、yearsPerPage、prevPage/nextPage等二十余项状态与函数。Cell 与 CellTrigger 的所有判定都从该上下文读取保证单一数据源、无状态漂移。这套上下文类型定义在 YearRangePickerRoot.vuecreateContext工具位于/shared任何不处于 Root 内的孤立使用都会得到明确报错提示。高亮区间计算原理highlightedRange由 useRangeYearPicker.ts 计算规则可以总结为已选完完整区间start 与 end 都存在且未设fixedDate时返回null不再高亮仅选中起点、焦点focusedValue落在另一侧时以起点与焦点为端点构造候选区间若设置了maximumYears候选区间会被钳制在anchor ± (maximumYears - 1)年内最后通过areAllYearsBetweenValid校验区间内所有年份是否可用allowNonContiguousRanges开启时跳过不可用校验允许选择非连续区间否则任一年份不可用即视为非法返回null。isInvalid的判定逻辑同样在此文件中起点/终点被isYearDisabled判为禁用、或终点年份早于起点年份时整个区间置为无效useRangeYearPicker.tsRoot 会相应输出[data-invalid]。交互原理点击选择与键盘导航点击选择的状态机handleClick → changeYear实现了完整的区间选择状态机YearRangePickerCellTrigger.vue核心分支包括只选了起点、未选终点点击新年份时以e.preventDefault()阻止事件并更新lastPressedDateValue待下一次点击确定终点同时支持再次点击同一位置调整起点已选完整区间且起点终点当前年再次点击清空整个选区除非preventDeselect从空状态开始第一次点击设startValue第二次点击设endValue已选完整区间再点击未设fixedDate时以新点击为起点重新开始新区间设fixedDatestart/end时按与固定端比较结果决定只移动另一端选中/点击不可用或禁用年份直接 return不产生任何副作用。readonly状态下点击完全被忽略Esc 键在编辑进行中isEditing会回滚到validModelValue上一个合法区间见 YearRangePickerRoot.vue。键盘导航Trigger 上挂载了keydown.up.down.left.right.space.enter.page-up.page-downYearRangePickerCellTrigger.vue行为与文档中的 Keyboard Table 一致按键行为Tab首次聚焦进入选择器时聚焦第一个导航按钮Space/Enter焦点在 Next/Prev 上翻页在 CellTrigger 上选中该年份ArrowLeft/Right/Up/Down在年网格内移动Up/Down 每次 ±4 年跨页时自动翻页并继续移动RTL 下左右方向翻转PageUp/PageDown跳转到上一页/下一页年份每页yearsPerPage年默认 12Escape取消当前选择恢复上一个合法区间方向键实现中有两个值得注意的细节YearRangePickerCellTrigger.vue候选年份超出minValue/maxValue范围时停止移动目标元素不存在时自动调用nextPage/prevPagenextTick后再递归查找最多回溯 48 层防止死循环Ctrl/Meta/Alt Enter/Space组合键会被放行让事件冒泡给上层如表单提交避免被网格拦截。这些行为在 YearRangePicker.test.ts 中有自动化测试覆盖例如给定默认区间后重新选择会重置选区受控模式下保持 end 值以及axe无障碍零违规断言该测试同时覆盖CalendarDate、CalendarDateTime、ZonedDateTime三种日期类型均得到相同选择结果说明 Cell/Trigger 完全基于统一的DateValue抽象工作。完整示例从 Anatomy 到自定义样式将上述 API 组合起来得到一份开箱即用的完整实现Anatomy 源自 year-range-picker.md这里补充了状态样式与受控绑定script setup import { ref } from vue import { CalendarDate } from internationalized/date import { YearRangePickerCell, YearRangePickerCellTrigger, YearRangePickerGrid, YearRangePickerGridBody, YearRangePickerGridRow, YearRangePickerHeader, YearRangePickerHeading, YearRangePickerNext, YearRangePickerPrev, YearRangePickerRoot, } from reka-ui const range ref({ start: new CalendarDate(2020, 1, 1), end: undefined }) /script template YearRangePickerRoot v-modelrange :maximum-years10 YearRangePickerHeader YearRangePickerPrev / YearRangePickerHeading / YearRangePickerNext / /YearRangePickerHeader YearRangePickerGrid YearRangePickerGridBody YearRangePickerGridRow YearRangePickerCell YearRangePickerCellTrigger v-slot{ yearValue, selected, highlighted, selectionStart, selectionEnd, today, disabled } span :class{ bg-blue-500 text-white: selected, bg-blue-100: highlighted !selected, rounded-l-full: selectionStart, rounded-r-full: selectionEnd, underline decoration-dotted: today, text-gray-300: disabled, } {{ yearValue }}/span /YearRangePickerCellTrigger /YearRangePickerCell /YearRangePickerGridRow /YearRangePickerGridBody /YearRangePickerGrid /YearRangePickerRoot /template其中maximum-years10会把可选区间钳制在起点 ± 9 年内超出部分的年份由rangeIsYearDisabled判定为禁用useRangeYearPicker.ts在 Trigger 上体现为data-disabled与disabled插槽状态。无障碍与测试验证无障碍Cell 承担gridcell语义与选中/禁用 ARIA 标注Trigger 承担button语义与aria-pressed/aria-label/aria-disabledRoot 同时提供隐藏的roleheading区域aria-level2输出fullCalendarLabel供读屏器完整感知选区见 YearRangePickerRoot.vue键盘全覆盖方向键、翻页键、Esc 取消均已实现并有测试佐证自动化测试YearRangePicker.test.ts 断言了默认值渲染1980–1983 区间高亮 4 个年份、标题显示1980 - 1991页、modelValue为null不崩溃、重新选择时区间重置、以及axe无障碍零违规。小结YearRangePickerCell与YearRangePickerCellTrigger是 radix-vue 区间选择体系中最贴近用户的一层前者用最薄的容器承担语义与状态标注后者承载完整的选中状态机、10 维插槽状态与键盘导航。理解这两层你既能在几分钟内定制出带完整选中/高亮/禁用反馈的年区间选择器也能顺着 YearRangePickerRoot.vue、useRangeYearPicker.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),仅供参考
返回列表