Files
workspace/code/fms/.codebuddy/plans/dropdown-component_025932fb.md
2026-08-16 22:01:32 +08:00

9.9 KiB
Raw Permalink Blame History

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 导航注册。
architecture styleKeywords fontSystem colorSystem
framework
vue
卡片式浮层
紧凑列表
hover 高亮
危险变体
精致微交互
fontFamily heading subheading body
Noto Sans SC, PingFang SC
size weight
14px 500
size weight
12px 500
size weight
14px 400
primary background text functional
#0f172b
#1677ff
#ffffff
#f5f6f8
#1a1d24
#1f2329
#86909c
#ef4444
#e5e6eb
id content status
popover-contextmenu 用 [skill:antdv-next] 确认右键触发交互,扩展 Popover 支持 trigger=contextmenu(validator + 右键坐标定位) completed
id content status
dropdown-tokens tokens.css 新增 --fms-dropdown-* 尺寸 token(min-width/max-height/item padding/radius/divider margin) completed
id content status dependencies
dropdown-style 用 [skill:ui-ux-pro-max] 参考设计,创建 dropdown/index.scss(shadcn 风格菜单卡片/项/分隔线/危险态/子菜单箭头) completed
dropdown-tokens
id content status dependencies
dropdown-component 创建 dropdown/dropdown.vue(items 数据驱动、键盘导航、子菜单、插槽逃生舱、ARIA) completed
popover-contextmenu
dropdown-style
id content status dependencies
dropdown-demo 创建 demo/dropdown.vue 并注册到 App.vue「数据展示」分类与 demoMap completed
dropdown-component
id content status dependencies
static-check 静态检查全部改动文件(oxlint)并汇报变更清单与已知风险 completed
dropdown-demo

产品概述

在 fms-vue 组件库中新增 Dropdown 下拉菜单组件:功能与交互参考 antdv-next(数据驱动 items、多种触发方式、子菜单、键盘操作),样式与设计参考 shadcn DropdownMenu(卡片式浮层、hover 高亮、危险项、快捷键、图标、勾选指示),并严格遵循 fms-vue/开发规范.md。

核心功能

  • 数据驱动菜单:items prop 定义菜单项,结构支持 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:trigger validator 增加 '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",打开后 nextTick focus;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/svg 1em;shortcut margin-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: 提供精确的修改目标清单与插入位置