前端UI组件设计系统【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design. Design to Code in one click项目地址https://gitcode.com/gh_mirrors/se/semi-design点击查看免费下载Dropdown下拉菜单是 Semi Design 中基于 Tooltip 能力封装的展示类组件它把任意触发元素children与一个向下弹出的菜单面板组合起来默认鼠标移入即展开也支持点击、聚焦、右键或完全自定义的触发方式。本文以 content/show/dropdown/index-en-US.md 为主线结合packages/semi-ui/dropdown与packages/semi-foundation/dropdown的源码实现带你掌握组件组合式 API、五种触发模式、JSON 菜单配置、键盘无障碍交互等完整实战能力读完即可在业务中直接落地使用。快速上手引入与基本用法Semi Design 的所有组件都从douyinfe/semi-ui统一导出Dropdown 也不例外import { Dropdown } from douyinfe/semi-ui;Dropdown 的核心使用模型可以概括为两句话触发元素放在 Dropdown 的children中默认以 hover鼠标移入触发展开可通过props.trigger修改为click、custom、contextMenu等值指定不同触发方式面板内容通过render指定下拉框的具体内容——用Dropdown.Menu作为父容器组合使用Dropdown.Item、Dropdown.Divider、Dropdown.Title。简单场景下只搭配Dropdown.Menu与Dropdown.Item即可其他元素不是必须的。其中Dropdown.Item通过disabled可以禁用某个选项配置type可以展示不同颜色的文本设置icon可以快速配置图标更复杂的自定义结构则可以通过children传入ReactNode自定义渲染。下面是一个完整的综合示例展示了分组标题、带图标菜单项、快捷键提示、分隔线以及五种文字类型tertiary / warning / dangerimport React from react; import { Dropdown, Button, HotKeys } from douyinfe/semi-ui; import { IconBox, IconSetting, IconForward, IconRefresh, IconSearch, IconAlertCircle } from douyinfe/semi-icons; import { IconToken } from douyinfe/semi-icons-lab; function Demo() { return ( Dropdown positionbottomLeft render{ Dropdown.Menu Dropdown.TitleGroup 1/Dropdown.Title Dropdown.Item icon{IconBox /} Menu Item 1 HotKeys style{{ marginLeft: 20 }} hotKeys{[HotKeys.Keys.Control, HotKeys.Keys.B]} content{[Ctrl, B]} /HotKeys /Dropdown.Item Dropdown.Item icon{IconSetting /} Menu Item 2 HotKeys style{{ marginLeft: 20 }} hotKeys{[HotKeys.Keys.Control, HotKeys.Keys.V]} content{[Ctrl, V]} /HotKeys /Dropdown.Item Dropdown.Item disabled icon{IconForward /} Menu Item 3 HotKeys style{{ marginLeft: 20 }} hotKeys{[HotKeys.Keys.Control, HotKeys.Keys.F3]} content{[Ctrl, F3]} /HotKeys /Dropdown.Item Dropdown.Divider / Dropdown.TitleGroup 2/Dropdown.Title Dropdown.Item typetertiary icon{IconRefresh /}Tertiary text/Dropdown.Item Dropdown.Item typewarning icon{IconSearch /} Warning Text /Dropdown.Item Dropdown.Item typedanger icon{IconAlertCircle /}Danger text/Dropdown.Item /Dropdown.Menu } Button themeoutline typetertiary icon{IconToken /} Hover Me /Button /Dropdown ); }从源码实现看packages/semi-ui/dropdown/index.tsxDropdown是一个典型的组合型组件它在类上静态挂载了四个子组件——static Menu DropdownMenu、static Item DropdownItem、static Divider DropdownDivider、static Title DropdownTitle这也是你能直接写Dropdown.Menu、Dropdown.Item的原因同时它通过DropdownContextpackages/semi-ui/dropdown/context.ts向下传递level嵌套层级、showTick与trigger三个上下文值供子组件消费。嵌套使用多级菜单用户可以对Dropdown进行嵌套使用此类情况适合具有多个子级选项的场景。嵌套时只需在某个Dropdown.Item外层再套一层Dropdown并通过position控制子菜单展开方向如rightTop、leftTopimport React, { useMemo } from react; import { Dropdown, Tag } from douyinfe/semi-ui; function Demo() { const subDropdown useMemo( () ( Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu ), [] ); return ( div style{{ margin: 100 }} Dropdown render{ Dropdown.Menu Dropdown position{rightTop} render{subDropdown} Dropdown.ItemMenu Item 1/Dropdown.Item /Dropdown Dropdown position{leftTop} render{subDropdown} Dropdown.ItemMenu Item 2/Dropdown.Item /Dropdown Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } TagHover Me/Tag /Dropdown /div ); }嵌套场景下的两个源码细节值得注意间距自适应在 index.tsx 的render()中组件会读取context.level判断当前是否处于嵌套层级。当level 0时spacing若未显式传入数值会被替换为numbers.NESTED_SPACING2px只有顶层才使用默认的numbers.SPACING4px。这一常量定义在 packages/semi-foundation/dropdown/constants.ts 中保证子菜单与父菜单之间保持紧凑贴合。嵌套点击事件差异在 dropdownItem.tsx 中当context.level ! 1即处于嵌套 Dropdown 内部时onClick会被改写为onMouseDown并只在e.button 0鼠标左键时触发——这是为了避免嵌套菜单在展开/收起交互中产生意外冒泡。弹出位置控制Dropdown 支持的位置与 Tooltip 完全一致可参见 Tooltip 位置说明常用的是bottom、bottomLeft、bottomRight这三种。位置集合POSITION_SET在 constants.ts 中直接从 Tooltip 的strings.POSITION_SET复用而来组件默认位置为bottom。import React from react; import { Dropdown, Tag } from douyinfe/semi-ui; function Demo() { return ( div Dropdown position{bottom} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } TagBottom/Tag /Dropdown br / br / Dropdown position{bottomLeft} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } TagbottomLeft/Tag /Dropdown br / br / Dropdown position{bottomRight} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } TagbottomRight/Tag /Dropdown /div ); }与位置相关的两个实用属性autoAdjustOverflow默认true弹出层被遮挡时是否自动调整方向spacing默认4弹出层与 Trigger 元素即 Dropdown children的距离单位 px嵌套时默认收紧为 2px见上文。此外marginv2.25.0 起支持作用同 Tooltip margin用于为弹出层计算溢出时增加冗余值适合被 fixed 元素遮挡的场景详见 issue #549。触发方式hover / focus / click / custom / contextMenuDropdown 默认是移入触发hover也可以通过获取焦点focus、点击click、自定义事件custom甚至鼠标右键contextMenuv2.42 起提供触发菜单展开。五种触发值在 constants.ts 中定义为TRIGGER_SET: [hover, focus, click, custom, contextMenu]组件默认值为hover。import React from react; import { Dropdown, Tag, Input, Button } from douyinfe/semi-ui; function Demo() { return ( div Dropdown trigger{hover} position{bottomLeft} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } TagHover me/Tag /Dropdown br / br / Dropdown trigger{focus} position{bottomLeft} render{ Dropdown.Menu tabindex{-1} Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } div style{{ border: 1px solid var(--semi-color-border), borderRadius: 4, height: 36, width: 220 }} Please use Tab to focus this div /div /Dropdown br / br / Dropdown trigger{click} position{bottomLeft} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } ButtonClick me/Button /Dropdown /div ); }focus触发时建议给Dropdown.Menu设置tabindex{-1}让菜单容器自身可被聚焦从而避免焦点在菜单内循环时丢失custom触发则需要配合visible受控属性使用由你完全掌控展开时机。右键菜单contextMenu的典型写法如下来自同文档的中文版演示Dropdown trigger{contextMenu} position{bottomRight} render{ Dropdown.Menu Dropdown.ItemMenu Item 1/Dropdown.Item Dropdown.ItemMenu Item 2/Dropdown.Item Dropdown.ItemMenu Item 3/Dropdown.Item /Dropdown.Menu } Button themesolid typesecondary style{{ marginBottom: 20 }} Right click (ContextMenu) /Button /Dropdown从 foundation.ts 可以看出触发逻辑的底层实现handleVisibleChange(visible)会先调用setPopVisible更新状态再通过notifyVisibleChange回调onVisibleChange当visible为真且trigger click时会自动把焦点移到菜单的第一个菜单项setFocusToFirstMenuItem。而handleKeyDown则在 Trigger 上拦截键盘事件——Enter/Space直接event.target.click()激活ArrowDown/ArrowUp分别把焦点移到第一个/最后一个菜单项。另外两个与 hover 体验相关的延迟属性mouseEnterDelay默认50鼠标移入 Trigger 后延迟显示的时间毫秒仅 trigger 为 hover/focus 时生效mouseLeaveDelay鼠标移出弹出层后延迟消失的时间毫秒仅 trigger 为 hover/focus 时生效。文档表格标注默认值为50而当前仓库实现中 defaultProps 实际取的是 constants.ts 里的DEFAULT_LEAVE_DELAY100ms使用时建议按需显式设置。disableFocusListenerv2.17.0 起用于在trigger为hover时不响应键盘聚焦弹出浮层事件详见 issue #977。触发事件菜单项上的鼠标事件点击菜单项后可触发不同鼠标事件支持onClick、onMouseEnter、onMouseLeave和onContextMenu四种import React from react; import { Dropdown, Button, Toast } from douyinfe/semi-ui; import { IconToken } from douyinfe/semi-icons-lab; () { return ( Dropdown trigger{click} position{bottomLeft} render{ Dropdown.Menu Dropdown.Item onClick{() Toast.info({ content: You clicked me! })} 1: click me! /Dropdown.Item Dropdown.Item onMouseEnter{() Toast.info({ content: Nice to meet you! })} 2: mouse enter /Dropdown.Item Dropdown.Item onMouseLeave{() Toast.info({ content: See ya! })} 3: mouse leave /Dropdown.Item Dropdown.Item onContextMenu{() Toast.info({ content: Right clicked! })} 4: right click /Dropdown.Item /Dropdown.Menu } Button themeoutline typetertiary icon{IconToken /} Click Me /Button /Dropdown ); };这些事件在 dropdownItem.tsx 中统一处理当Dropdown.Item未禁用!disabled时onClick、onMouseEnter、onMouseLeave、onContextMenu四个事件才会被绑定到li元素上禁用状态下所有鼠标事件都不会响应。此外禁用项会渲染aria-disabled{disabled}菜单项本身使用rolemenuitem和tabIndex{-1}详见下文无障碍章节。JSON 配置用法通过 menu 属性快速构建菜单除了 JSX 组合写法还可以通过menu属性传入 JSON Array 快速配置下拉框菜单内容。每项通过node字段声明类型title/item/divider并支持透传对应子组件的全部属性如type、active、onClickimport React from react; import { Dropdown, Button } from douyinfe/semi-ui; import { IconToken } from douyinfe/semi-icons-lab; function DropdownEvents() { const menu [ { node: title, name: Group1 }, { node: item, name: primary1, type: primary, onClick: () console.log(click primary) }, { node: item, name: secondary, type: secondary }, { node: divider }, { node: title, name: Group2 }, { node: item, name: tertiary, type: tertiary }, { node: item, name: warning, type: warning, active: true }, { node: item, name: danger, type: danger }, ]; return ( Dropdown trigger{click} showTick position{bottomLeft} menu{menu} Button themeoutline typetertiary icon{IconToken /} Click Me /Button /Dropdown ); }其底层实现在 index.tsx 的renderMenu()中组件遍历menu数组根据m.node的取值title/item/divider分别渲染为Dropdown.Title、Dropdown.Item与Dropdown.Divider并自动剥离node、name字段、把其余属性透传给对应子组件最终统一包裹进Dropdown.Menu。也就是说JSON 写法与 JSX 组合写法最终渲染出的 DOM 结构完全一致menu只是提供了一种更利于动态配置例如由后端下发的菜单数据的声明式入口。API 参考Dropdown属性说明类型默认值版本autoAdjustOverflow弹出层被遮挡时是否自动调整方向booleantrueclassName下拉弹层外层样式类名stringcloseOnEsc在 trigger 或弹出层按 Esc 键是否关闭面板受控visible 受控时不生效booleantrue2.13.0children触发弹出层的 Trigger 元素ReactNodeclickToHide在弹出层内点击时是否自动关闭弹出层boolean-contentClassName下拉菜单根元素类名stringdisableFocusListenertrigger 为hover时不响应键盘聚焦弹出浮层事件详见 issue #977booleantrue2.17.0keepDOM关闭时是否保留内部组件 DOM 不销毁booleanfalse2.31.0getPopupContainer指定父级 DOM弹层将渲染至该 DOM 中自定义时需设置position: relative。这会改变浮层 DOM 树位置但不会改变视图渲染位置function():HTMLElement() document.bodymargin弹出层计算溢出时的冗余值用于被 fixed 元素遮挡的场景详见 issue #549作用同 Tooltip marginobject|number2.25.0mouseEnterDelay鼠标移入 Trigger 后延迟显示的时间毫秒仅 trigger 为 hover/focus 时生效number50mouseLeaveDelay鼠标移出弹出层后延迟消失的时间毫秒仅 trigger 为 hover/focus 时生效number50menu通过传入 JSON Array 快速配置 Dropdown 内容ArrayDropdownMenuItem[]-position弹出菜单的位置常用bottom、bottomLeft、bottomRight更多详见 Tooltip 位置stringbottomrender弹出层的内容由Dropdown.Menu及Dropdown.Item、Dropdown.Title构成ReactNoderePosKey更新该项的值可手动触发弹出层的重新定位string | numberspacing弹出层与 Trigger 元素即 Dropdown children的距离单位 pxnumber4style弹出层内联样式objectshowTick是否自动在 active 的 Dropdown.Item 项左侧展示表示选中的勾booleanfalse-stopPropagation是否阻止弹出层上的点击事件冒泡booleanfalse-trigger触发下拉的行为可选hover、focus、click、custom、contextMenuv2.42 起提供stringhovervisible是否显示菜单需配合 trigger custom 使用booleanzIndex弹出层 z-index 值number1050onClickOutSide当弹出层处于展示状态点击非 Children、非弹出层内部区域时的回调仅 trigger 为 custom、click 时有效function(e:event)2.1.0onEscKeyDown在 trigger 或弹出层按 Esc 键时调用function(e:event)2.13.0onVisibleChange弹出层显示状态改变时的回调function(visible: boolean)补充说明Dropdown的defaultProps在 index.tsx 中定义除上表外还包括zIndex取 Tooltip 的DEFAULT_Z_INDEX即表中标注的 1050、closeOnEsc: true、onVisibleChange与onEscKeyDown默认为空函数noop、motion: true等。Dropdown.Menu属性说明类型默认值版本style下拉弹层菜单样式object-className下拉弹层菜单样式类名string-children下拉弹层菜单包裹的子元素一般为Dropdown.Item或Dropdown.TitleReactNodeDropdown.Item属性说明类型默认值版本active当前项是否处于激活态激活态时左侧有 √字体加粗、颜色加深。当 Dropdown 的 showTick 为 false 时即使 Item 的 active 为 true√ 也不会展示booleanfalseclassName样式类名stringdisabled是否禁用菜单booleanfalseicon图标展示在左侧ReactNode-style内联样式objecttype类型可选值primary、secondary、tertiary、warning、dangerstringtertiaryonClick单击触发的回调事件functiononContextMenu鼠标右键触发的回调事件function-onMouseEnterMouseEnter 触发的回调事件functiononMouseLeaveMouseLeave 触发的回调事件functiontype的可选值与默认值同样定义在 constants.ts 的ITEM_TYPE: [primary, secondary, tertiary, warning, danger]中。showTick的实现细节在 dropdownItem.tsx 中当showTick active时渲染实心IconTick当showTick !active时渲染一个color: transparent的透明IconTick占位保证菜单项文字在有无选中态之间不产生位移抖动。Dropdown.Title属性说明类型默认值className样式类名stringstyle内联样式object{}DropdownMenuItem属性说明类型node菜单类型title、item、dividerstringname菜单内容标题或 Item 的文本string其他属性与 Title、Item、Divider 的属性对应无障碍AccessibilityARIADropdown.Menu的role被设置为menuaria-orientation被设置为verticalDropdown.Item的role被设置为menuitem。这两条规则直接体现在渲染层dropdownMenu.tsx 中ul rolemenu aria-orientationverticaldropdownItem.tsx 中li rolemenuitem tabIndex{-1} aria-disabled{disabled}。键盘与焦点Dropdown 的触发器可被聚焦目前支持 3 种触发方式触发方式设置为 hover 或 focus 时鼠标悬浮或聚焦时打开 Dropdown打开后用户可以使用下箭头将焦点移动到 Dropdown 内对应 foundation.ts 中handleKeyDown对ArrowDown的处理触发方式设置为 click 时点击触发器或聚焦时使用Enter或Space键打开 Dropdown此时焦点自动聚焦到 Dropdown 中的第一个非禁用项上handleVisibleChange中的setFocusToFirstMenuItem当焦点位于 Dropdown 内的菜单项上时对应 menuFoundation.ts 的onMenuKeydown键盘用户可以使用上箭头或下箭头切换可交互元素使用Enter键或Space键可以激活聚焦的菜单项若菜单项绑定了onClick事件会被触发按下可打印字符字母/数字等时会按字符首字母快速定位菜单项setFocusByFirstCharacter结合findIndexByCharacter实现键盘用户可以通过按Esc关闭 Dropdown关闭后焦点返回到触发器上closeOnEsc默认开启且关闭时returnFocusOnClose{true}会把焦点还给 Triggercustom触发模式下Esc还会通过getMenuButton找回触发按钮并聚焦键盘交互暂未完整支持嵌套场景。提示菜单项内部的键盘事件统一由Dropdown.Menu的onKeyDown分发onMenuKeydown而 Trigger 上的Enter/Space/方向键则由 Dropdown 根组件的onKeyDownfoundation.handleKeyDown处理两层分工明确且都刻意保留了事件冒泡避免影响 Trigger 上自带的输入框按键逻辑。文案规范下拉框内选项内容需要表述准确且包含信息使用户在浏览时更容易在选项间做出选择使用语句式的大小写并且简洁明了地书写选项如果是动作选项使用动词或动词短语来描述用户选择该选项后会发生的动作。例如 Move、Log time、Hide labels不使用介词。✅ 推荐用法❌ 不推荐用法Add text / Add link / Add image / Add videoAdd a text / Add a link / Add a image / Add a video设计令牌与主题定制下拉菜单的视觉样式完全由 Semi Design 的设计令牌Design Tokens驱动。DesignToken/会在文档站点上自动渲染当前组件全部可用的设计令牌包括颜色如--semi-color-primary、--semi-color-danger、圆角--semi-radius-*、阴影、字体大小与间距等。想要深度定制时可以直接在 semi-theme-default 中查找对应的 SCSS 变量并结合 主题定制指南 将其映射为品牌化主题菜单项的样式类结构如semi-dropdown-item、semi-dropdown-item-disabled、semi-dropdown-item-selected定义在 packages/semi-foundation/dropdown/dropdown.scss 中可作为自定义 CSS 的锚点。常见问题 FAQ为什么 Dropdown 浮层在靠近屏幕边界宽度不够时会丢失宽度意外换行在 Chromium 104 之后浏览器对屏幕边界处文本宽度不足时的换行渲染策略发生了变化详细原因见 issue #1022导致浮层可能出现意外的换行行为。Semi Design 已经在 v2.17.0 版本修复了该问题升级到该版本及以上即可避免。源码结构速览如果你想继续深入阅读 Dropdown 的实现仓库中相关的关键文件如下组件渲染层packages/semi-ui/dropdown/index.tsxDropdown 主组件、dropdownMenu.tsx、dropdownItem.tsx、dropdownTitle.tsx、dropdownDivider.tsx、context.ts行为与状态层packages/semi-foundation/dropdown/foundation.ts显隐与 Trigger 键盘逻辑、menuFoundation.ts菜单内键盘导航、constants.ts位置/触发/类型常量与默认间距样式层packages/semi-foundation/dropdown/dropdown.scss、variables.scss、animation.scss测试用例packages/semi-ui/dropdown/test/dropdown.test.js 覆盖了显隐控制、事件回调、键盘交互等核心行为是理解组件契约的很好补充。赞分享前端UI组件设计系统【免费下载链接】semi-designA modern, comprehensive, flexible design system and React UI library, AI-friendly built-in.Provide 3000 Design Tokens, easy to build your design system. Make Semi Design to Any Design. Design to Code in one click项目地址https://gitcode.com/gh_mirrors/se/semi-design点击查看免费下载相关推荐Semi Design Notification 组件完全指南API 用法、底层实现与无障碍实践Semi Design Notification 组件完全指南API 用法、底层实现与无障碍实践 Notification 是 Semi Design d前端UI组件设计系统Ant Design Dropdown 下拉菜单组件完全指南API、交互模式与源码解析Ant Design Dropdown 下拉菜单组件完全指南API、交互模式与源码解析 Dropdown 是 Ant Design 导航体系中的核心组件用于前端UI组件设计系统Ant Design Dropdown 按钮式下拉菜单Button with dropdown menu组合实战指南Ant Design Dropdown 按钮式下拉菜单Button with dropdown menu组合实战指南 本指南围绕 ant design 官方前端UI组件设计系统上一篇Beyond Compare 5终极激活指南3种简单方法永久免费使用文件对比神器下一篇Beyond Compare 5密钥生成器3种方法实现永久激活的文件对比工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
