9.9 KiB
9.9 KiB
name, overview, design, todos
| name | overview | design | todos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| dropdown-component | 在 fms-vue 组件库中新增 Dropdown 下拉菜单组件:基于现有 Popover 浮层派生,采用数据驱动 items(label/icon/shortcut/disabled/danger/divider/子菜单)+ 默认插槽逃生舱,支持 click/hover/contextmenu 触发与键盘导航,样式参考 shadcn(白卡片、圆角、hover 高亮、分隔线、危险项、快捷键),并补充 demo 与 App.vue 导航注册。 |
|
|
产品概述
在 fms-vue 组件库中新增 Dropdown 下拉菜单组件:功能与交互参考 antdv-next(数据驱动 items、多种触发方式、子菜单、键盘操作),样式与设计参考 shadcn DropdownMenu(卡片式浮层、hover 高亮、危险项、快捷键、图标、勾选指示),并严格遵循 fms-vue/开发规范.md。
核心功能
- 数据驱动菜单:
itemsprop 定义菜单项,结构支持key / label / icon / shortcut / disabled / danger / divider / type: 'label'(分组标题)/ children(子菜单);不传 items 时提供默认插槽逃生舱(对齐 Select 模式) - 多种触发方式:
click(默认)/hover/contextmenu(右键,需扩展 Popover 以支持右键坐标定位) - 子菜单:
children嵌套渲染,hover 展开右侧子菜单 - 键盘导航:打开后聚焦菜单容器,方向键/Home/End 移动高亮,Enter 选择,Esc 关闭,
aria-activedescendant无障碍 - 受控/非受控:
open/default-open,事件update:open/openChange/click - 视觉要素(shadcn 风格):卡片浮层(圆角/边框/阴影)、菜单项 hover/focus 高亮、禁用灰、danger 红、分隔线、分组小标签、快捷键右侧、图标左侧
- 演示接入:新增
demo/dropdown.vue并在 App.vue「数据展示」分类注册,演示基础/图标+快捷键/分隔线/禁用/危险/子菜单/hover/右键/受控
技术栈
- Vue 3
<script setup>、Sass、@lucide/vue图标按需导入 - 复用现有
Popover(Teleport 定位、外部点击关闭、Esc 关闭、滚动/缩放跟随)与utils/position.js纯函数 - 样式复用
--fms-*token,缺失 token 补到src/theme/tokens.css
实现方案
1. 扩展 Popover 支持 contextmenu 触发
popover.vue:triggervalidator 增加'contextmenu';新增onContextMenu处理——preventDefault,用事件clientX/clientY构造零尺寸锚点 rect({left, top, width:0, height:0, right:clientX, bottom:clientY})存入变量,setOpen(true)后updatePosition()updatePosition():锚点 rect 存在时优先使用,否则回退triggerEl.getBoundingClientRect()(position.js纯函数不改,仅传参变化)- 关闭时清空锚点 rect;模板触发器元素绑定
@contextmenu
2. Dropdown 主组件(dropdown.vue)
- props:
items(Array)、open/default-open、trigger(click/hover/contextmenu,validator)、placement(默认bottom-start)、disabled、mouse-enter-delay/mouse-leave-delay、arrow=false - 结构:
<Popover :trigger="trigger" :placement="placement" :arrow="false">,#trigger插槽透传默认插槽;#content渲染菜单容器 - 菜单渲染:
items循环——divider渲染分隔线;type==='label'渲染分组标签;children渲染嵌套Popover(hover、right-start、offset 4)作为子菜单,父项右侧 ChevronRight 箭头;常规项含 icon/label/shortcut - 键盘导航(参照 Select 模式):菜单容器
role="menu"tabindex="-1",打开后nextTickfocus;activeIndex仅遍历可交互项(跳过 divider/label/disabled),方向键/Home/End 移动并滚动到可视,Enter 触发click事件,Esc 关闭;aria-activedescendant指向高亮项 - 事件:点击项
emit('click', { key, item })后关闭;disabled/danger 项均可触发(danger 是视觉变体);子菜单项事件冒泡由嵌套 Popover 隔离 - 插槽逃生舱:无
items时渲染默认插槽(透出close函数)
3. 样式(参考 shadcn style-nova.css 的 cn-dropdown-menu-*)
- 菜单容器:
min-width: 160px、padding: 4px、background: --fms-card、border: 1px solid --fms-border、border-radius: --fms-control-radius、box-shadow: --fms-popover-shadow、max-height溢出滚动 - 菜单项:flex、
gap: 8px、padding: 5px 8px、border-radius: 4px、hover/focus 背景--fms-secondary-hover;icon/svg1em;shortcutmargin-left: auto、12px、--fms-text-secondary;danger 文字--fms-danger;disabled 用--fms-disabled-text+not-allowed - 分隔线:
height: 1px、background: --fms-border、margin: 4px 0;分组标签:12px、--fms-text-secondary - 过渡复用 popover 的 scale+fade transition(低开销、支持 reduced-motion)
4. Token 新增(tokens.css)
/* 下拉菜单(dropdown) */
--fms-dropdown-min-width: 160px;
--fms-dropdown-max-height: 256px;
--fms-dropdown-item-padding-y: 5px;
--fms-dropdown-item-padding-x: 8px;
--fms-dropdown-item-radius: 4px;
--fms-dropdown-divider-margin: 4px 0;
5. 演示接入
src/components/ui/demo/dropdown.vue:基础菜单、图标+快捷键、分隔线+分组标签、禁用项、危险项、子菜单、hover 触发、右键触发、受控模式App.vue:import DropdownDemo,demoMap注册,分类「数据展示」加{ key: 'dropdown', label: 'Dropdown 下拉菜单' }
性能与可靠性
- 全局监听(外部点击/Esc/scroll/resize)由 Popover 打开时注册、关闭时清理,无长期监听
- 键盘导航仅维护
activeIndex单一状态,scrollIntoView({ block: 'nearest' })惰性滚动,无高频计算 - 子菜单使用嵌套 Popover,复用其移入浮层不关闭、Esc 关闭能力;多层 Esc 一次性全部关闭(MVP 行为,后续可逐层)
- 右键定位基于事件坐标构造零尺寸 rect,
computePosition的 flip/shift 兜底防溢出
风险与边界
- contextmenu 锚点为鼠标点,浮层大小变化时由 ResizeObserver 重新定位(Popover 已实现)
trigger数组组合(如 click+hover 并存)本期不支持,保持单一枚举(antdv 亦以单一触发为主),API 最小- 不提供 size 枚举、不做旧 API 兼容分支(遵循规范 7.1/4.2)
设计风格
Dropdown 为浮层类菜单组件,视觉参考 shadcn DropdownMenu,与项目现有 Popover/Select 下拉视觉体系统一。
- 浮层卡片:与 Popover 一致的白/深色卡片(
--fms-card),1px 边框(--fms-border)、6px 圆角、柔和投影(--fms-popover-shadow),内容内边距 4px,最小宽度 160px,超长列表滚动 - 菜单项:紧凑单行布局——图标(1em 灰色)在左,文字居中偏左,快捷键右对齐浅灰小字;hover/聚焦时整行浅灰高亮(
--fms-secondary-hover),选中态主色;禁用项灰字不可点;危险项(如删除/退出)文字为红色--fms-danger - 分组与分隔:分组小标签(12px 浅灰小字)、1px 分隔线,信息层级清晰
- 子菜单:父项右侧 ChevronRight 小箭头指示,hover 展开右侧次级菜单卡片,视觉同主菜单
- 动效:淡入 + 轻微缩放(150ms,复用 popover 过渡),hover 高亮 150ms 过渡,支持
prefers-reduced-motion - 交互反馈:打开时菜单项方向键可达、Enter 确认、Esc 关闭;触发器打开态可用 popover 现有高亮语义
Agent Extensions
Skill
- antdv-next
- Purpose: 查询 antdv-next Dropdown 的 props/events/slots API 文档与触发交互细节(trigger、placement、子菜单),验证 API 设计一致性
- Expected outcome: 确认 Dropdown 的 trigger 枚举、事件签名、子菜单数据结构,确保与参考实现对齐
- ui-ux-pro-max
- Purpose: 为下拉菜单浮层样式与交互细节(hover 态、危险项、分组分隔、动效时长)提供设计参考
- Expected outcome: 产出与 shadcn DropdownMenu 风格一致的视觉规范,落地到 index.scss
SubAgent
- code-explorer
- Purpose: 实施前确认 Popover 触发机制、Select 键盘导航细节与 App.vue demoMap 注册模式,避免遗漏调用点
- Expected outcome: 提供精确的修改目标清单与插入位置