ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

radix-vue 中 DropdownMenuCheckboxItem 的 API 全解:可控复选菜单项的实现与用法

radix-vue 中 DropdownMenuCheckboxItem 的 API 全解:可控复选菜单项的实现与用法 radix-vue 中 DropdownMenuCheckboxItem 的 API 全解可控复选菜单项的实现与用法【免费下载链接】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 Vue现以 reka-ui 包名发布下拉菜单体系中的DropdownMenuCheckboxItem组件完整覆盖其 Props 与 Events API结合 DropdownMenuCheckboxItem.vue、MenuCheckboxItem.vue 等源码剖析v-model勾选状态、indeterminate半选态、select事件阻止菜单关闭等关键行为帮助你在菜单中实现可勾选、可受控、可定制指示器的完整方案。组件定位Dropdown Menu 家族中的可勾选项DropdownMenuCheckboxItem是 Dropdown Menu 组件族的一个 Part官方文档对其的定义是An item that can be controlled and rendered like a checkbox一个可被受控、并渲染得像复选框的菜单项。它常用于显示/隐藏某列开启某项功能这类多选开关场景与单选场景的DropdownMenuRadioGroup/DropdownMenuRadioItem相对。在完整的菜单结构中它通常位于DropdownMenuContent内部并搭配DropdownMenuItemIndicator渲染选中指示器完整 API 与 Anatomy 可参考 dropdown-menu 组件文档template DropdownMenuRoot DropdownMenuTrigger / DropdownMenuPortal DropdownMenuContent DropdownMenuCheckboxItem DropdownMenuItemIndicator / /DropdownMenuCheckboxItem /DropdownMenuContent /DropdownMenuPortal /DropdownMenuRoot /template从源码结构看DropdownMenu 并非独立实现而是一个薄封装层DropdownMenuCheckboxItem.vue 的全部逻辑就是把 props 和 emits 原样转发给 MenuCheckboxItem.vueMenu 基础层组件并在其内部渲染MenuCheckboxItem// packages/core/src/DropdownMenu/DropdownMenuCheckboxItem.vue export type DropdownMenuCheckboxItemEmits MenuCheckboxItemEmits export interface DropdownMenuCheckboxItemProps extends MenuCheckboxItemProps {} // 模板部分 // MenuCheckboxItem v-bind{ ...props, ...emitsAsProps }slot //MenuCheckboxItem这意味着真正的勾选状态管理、ARIA 属性、键盘行为全部实现在 Menu 层本文后续的源码分析均基于 Menu 层实现。Props 完整参考以下 API 表格继承自 DropdownMenuCheckboxItem 元数据文档是所有可配置项的完整清单NameDescriptionTypeRequiredDefaultasThe element or component this component should render as. Can be overwritten byasChild.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-disabledWhentrue, prevents the user from interacting with the item.booleanNo-modelValueThe controlled checked state of the item. Can be used asv-model.false \| true \| indeterminateNo-textValueOptional text used for typeahead purposes. By default the typeahead behavior will use the.textContentof the item. Use this when the content is complex, or you have non-textual content inside.stringNo-几个要点展开说明modelValue三态类型声明为false | true | indeterminate对应源码中的CheckedState类型utils.ts 中export type CheckedState boolean | indeterminate。支持indeterminate半选态是它与普通布尔开关的最大区别典型场景是部分子项已选中的目录树菜单。源码中modelValue的默认值为falseMenuCheckboxItem.vue。textValue与 typeahead菜单内置输入即定位能力快速按键匹配项文本。当菜单项内容复杂例如包含图标、图片、多层嵌套文本时.textContent可能无法准确表达该项的可检索文本此时用textValue显式指定。as/asChild组合渲染这是 reka-ui 组合式渲染的基础能力。as改变组件渲染的根元素默认divasChild则把 props 与行为合并到唯一的子元素上避免多余 DOM 层级。disabled为true时禁止用户交互在 Menu 层实现中handleSelect会先判断if (!props.disabled menuItem)才触发选择逻辑MenuItem.vue因此禁用项既不会响应鼠标点击也不会响应键盘选择。Events 完整参考NameDescriptionTypeselectEvent handler called when the user selects an item (via mouse or keyboard). Callingevent.preventDefaultin this handler will prevent the menu from closing when selecting that item.[event: Event]update:modelValueEvent handler called when the value changes.[payload: boolean]select事件既能响应点击又能阻止菜单关闭select事件在鼠标或键盘选择该项时触发。文档中的关键行为是在回调里调用event.preventDefault()可以阻止菜单在该项被选中时自动关闭。这一行为在源码中的实现链路非常清晰。MenuCheckboxItem渲染MenuItem并监听select而 MenuItem.vue 的handleSelect会构造一个bubbles: true, cancelable: true的CustomEvent事件名为menu.itemSelect先分发给外层的select监听者再等待nextTick后检查defaultPrevented// packages/core/src/Menu/MenuItem.vue async function handleSelect() { const menuItem currentElement.value if (!props.disabled menuItem) { const itemSelectEvent new CustomEvent(ITEM_SELECT, { bubbles: true, cancelable: true, }) emits(select, itemSelectEvent) // let select event finish await nextTick() if (itemSelectEvent.defaultPrevented) isPointerDownRef.value false else rootContext.onClose() // 未阻止则关闭整个菜单 } }也就是说事件可取消 → 未取消则调用rootContext.onClose()关闭菜单取消了则菜单保持打开、焦点留在该项上。这个能力非常适合实现勾选多个项后手动关闭的批量选择交互。update:modelValue配合v-model的受控更新update:modelValue在勾选状态变化时触发payload 为boolean可被v-model直接消费。在 MenuCheckboxItem.vue 中内部状态通过 VueUse 的useVModel(props, modelValue, emits)桥接——父组件传入v-model时为受控模式否则组件自行维护内部状态非受控模式。勾选切换逻辑是理解该组件的核心。源码中select处理函数如下MenuCheckboxItem.vueselect async (event) { emits(select, event); if (isIndeterminate(modelValue)) { modelValue true; // 半选态 → 选中 } else { modelValue !modelValue; // 常规布尔翻转 } } 行为要点布尔翻转true↔false简单取反半选态收敛indeterminate项被选中后固定变为true而非直接取反成未选这符合 WAI-ARIA 中半选态菜单项的常见语义——用户主动点击半选项即视为确认选中注意emits(select, event)先于状态切换执行因此在select回调中读取到的仍是旧状态可据此做切换前的副作用如日志、校验。渲染输出ARIA 属性与 data 属性实际渲染的 DOM 特征MenuCheckboxItem最终渲染的是MenuItem并显式设置了菜单复选项的语义MenuCheckboxItem.vueMenuItem rolemenuitemcheckbox v-bindforwarded :aria-checkedisIndeterminate(modelValue) ? mixed : modelValue :data-stategetCheckedState(modelValue) ... rolemenuitemcheckbox标识这是一个可勾选的菜单项符合菜单按钮 ARIA 模式aria-checked三态映射为true/false/mixedindeterminate时输出mixeddata-state由 getCheckedState 计算输出值为indeterminate | checked | unchecked三选一方便 CSS 选择器按状态精确匹配// packages/core/src/Menu/utils.ts export function getCheckedState(checked: CheckedState) { return isIndeterminate(checked) ? indeterminate : checked ? checked : unchecked }可用 Data Attributes 汇总结合 dropdown-menu 文档 中 CheckboxItem 一节的 DataAttributesTableDropdownMenuCheckboxItem上可依赖的 data 属性为AttributeValues[data-state]checked/unchecked/indeterminate[data-highlighted]Present when highlighted键盘高亮时存在[data-disabled]Present when disabled禁用时存在样式化示例给禁用项和未选中项分别设置不同视觉反馈.DropdownMenuCheckboxItem[data-disabled] { color: gainsboro; pointer-events: none; }实战示例带指示器的复选菜单项下面给出 dropdown-menu 文档 中 With checkbox items 的完整示例使用v-model控制勾选状态并通过DropdownMenuItemIndicator渲染选中图标。script setup langts import { Icon } from iconify/vue import { DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuItemIndicator, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuTrigger, } from reka-ui import { ref } from vue const checked ref(false) /script template DropdownMenuRoot DropdownMenuTrigger…/DropdownMenuTrigger DropdownMenuPortal DropdownMenuContent DropdownMenuItem…/DropdownMenuItem DropdownMenuItem…/DropdownMenuItem DropdownMenuSeparator / DropdownMenuCheckboxItem v-modelchecked DropdownMenuItemIndicator Icon iconradix-icons:check / /DropdownMenuItemIndicator Checkbox item /DropdownMenuCheckboxItem /DropdownMenuContent /DropdownMenuPortal /DropdownMenuRoot /template指示器如何感知勾选状态DropdownMenuItemIndicator并不是简单地永远显示图标。其实现MenuItemIndicator.vue通过Presence组件按上下文中的modelValue决定挂载/卸载Presence :present forceMount || isIndeterminate(indicatorContext.modelValue.value) || indicatorContext.modelValue.value true Primitive :asas :as-childasChild :data-stategetCheckedState(indicatorContext.modelValue.value) slot / /Primitive /Presence上下文由MenuCheckboxItem在 setup 阶段通过provideMenuItemIndicatorContext({ modelValue })注入MenuCheckboxItem.vue因此指示器与菜单项共享同一个响应式状态源勾选变化会实时驱动指示器的显隐。两个实用细节指示器自身也带data-statechecked/unchecked/indeterminate可以为半选态渲染减号图标而不是勾号forceMountprop 可强制常驻挂载便于配合 Vue 过渡/动画库手动控制进出场见 MenuItemIndicator.vue 的 JSDoc。默认指示器元素渲染为spanas默认值你也可以直接用asChild合并进自己的图标元素。半选态indeterminate用法把modelValue设为字符串indeterminate即可进入半选态此时aria-checkedmixed、data-stateindeterminatescript setup langts import { ref } from vue // 模拟部分子项已选中的场景 const parentChecked refindeterminate | boolean(indeterminate) /script template DropdownMenuCheckboxItem v-modelparentChecked DropdownMenuItemIndicator / 部分子项已开启 /DropdownMenuCheckboxItem /template用户点击该项后根据前述切换逻辑状态会从indeterminate变为trueupdate:modelValue以true发出——父组件可据此递归选中全部子项这正是indeterminate在树形多选菜单中的典型用途。阻止菜单关闭多选后再手动关闭利用select事件的可取消性可以实现勾选多个项、最后手动点击关闭的交互template DropdownMenuCheckboxItem v-foropt in options :keyopt :model-valueselected.includes(opt) select(e: Event) e.preventDefault() update:model-value(val: boolean) toggle(opt, val) DropdownMenuItemIndicator / {{ opt }} /DropdownMenuCheckboxItem /template在select中调用preventDefault后菜单不会因选中该项而关闭对应MenuItem中defaultPrevented时跳过rootContext.onClose()的分支用户可以连续勾选多项最后通过点击外部区域或按Esc关闭菜单。键盘行为速览勾选状态的切换不依赖鼠标MenuItem.vue 在keydown中监听SELECTION_KEYSutils.ts 中定义为[Enter, ]按下 Enter 或 Space 时触发click()走完整的handleSelect流程——与鼠标点击等价地触发select事件并翻转勾选状态。同时按方向键可在菜单项之间移动高亮data-highlighted属性随之变化按Esc关闭菜单并将焦点交还触发器完整键盘表见 dropdown-menu 文档 的 Accessibility 章节。源码结构与延伸阅读理解DropdownMenuCheckboxItem的数据流可以从这条调用链入手入口导出DropdownMenu/index.ts 导出组件及其Props/Emits类型薄封装层DropdownMenuCheckboxItem.vue 通过useEmitAsPropsuseForwardExpose将 props/emits/方法透传给 Menu 层状态层MenuCheckboxItem.vue 持有useVModel桥接的modelValue、设置role/aria-checked/data-state、实现 indeterminate 切换逻辑并向指示器注入上下文交互层MenuItem.vue 负责点击/键盘触发、可取消的select事件分发与菜单关闭决策工具函数utils.ts 提供CheckedState类型、isIndeterminate、getCheckedState及选择/导航键位常量。此外仓库中还提供了带复选菜单项的可运行示例位于 DropdownMenu.story.vue 与 _DropdownMenu.vue可作为交互行为对照。若你希望按 dropdown-menu 文档 Custom APIs 一节的思路对DropdownMenuCheckboxItem做二次封装内建指示器与默认图标其 props/emits 类型DropdownMenuCheckboxItemProps/DropdownMenuCheckboxItemEmits均可从reka-ui直接导入使用。小结DropdownMenuCheckboxItem的 API 面很小但语义完整modelValue三态支持v-model、disabled、textValue、as/asChild五个 Propsselect/update:modelValue两个事件勾选切换在select时自动完成indeterminate → true的收敛规则、aria-checkedmixed与data-state三态输出均由 Menu 层源码保证样式只需按 data 属性编写需要精细控制交互阻止菜单关闭、动画、二次封装时源码路径 packages/core/src/Menu/MenuCheckboxItem.vue 是定位问题的第一站。【免费下载链接】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

延伸阅读

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