> Leaf UI 0.8.1 · Package: @ifhover/leaf-ui · Source: 435bab23cad7d343653d9b63b049c0b0601eeefb
> Browser documentation: https://ifhover.github.io/leaf-ui/components/menu.html

> For AI agents: the complete documentation index is available at https://ifhover.github.io/leaf-ui/llms.txt, the full documentation bundle is available at https://ifhover.github.io/leaf-ui/llms-full.txt.

# Menu 菜单

按层级组织页面导航。

## 基本用法

通过 `itemRender` 在菜单文字后追加状态标签和数字标签。一级菜单保留图标，二级菜单仅展示文字，并与一级菜单文字对齐。子菜单采用更紧凑的行高与较淡的文字；可选条目悬停和按下时仅改变文字颜色，选中项保留底色，祖先菜单保持普通样式。


工作台

项目管理

设计资源

发布管理新

团队成员12

设置

basic.tsx

复制代码

```tsx lineNumbers file="../../src/examples/menu/basic.tsx"
import { Menu, Tag } from '@ifhover/leaf-ui';

import { FolderKanban, House, Settings, Users } from 'lucide-react';

export function MenuBasic() {
  return (
    <Menu
      mode="inline"
      defaultSelectedKey="design"
      defaultOpenKeys={['projects']}
      itemRender={(item, content) => (
        <>
          {content}
          {item.key === 'release' && (
            <Tag size="sm" color="primary">
              新
            </Tag>
          )}
          {item.key === 'team' && (
            <Tag size="sm" color="info">
              12
            </Tag>
          )}
        </>
      )}
      items={[
        { key: 'home', label: '工作台', icon: <House /> },
        {
          key: 'projects',
          label: '项目管理',
          icon: <FolderKanban />,
          children: [
            { key: 'design', label: '设计资源' },
            { key: 'release', label: '发布管理' },
          ],
        },
        { key: 'team', label: '团队成员', icon: <Users /> },
        { key: 'settings', label: '设置', icon: <Settings /> },
      ]}
    />
  );
}
```

展开代码

## 排列方式

通过 Tabs 切换垂直、内嵌、横向和折叠侧栏。垂直与内嵌示例默认展开子菜单；横向导航和折叠侧栏使用浮层展示二级菜单。折叠侧栏还可以通过按钮展开，比较两种状态。


垂直

内嵌

横向

折叠侧栏

概览

资源

组件

主题

设置

advanced.tsx

复制代码

```tsx lineNumbers file="../../src/examples/menu/advanced.tsx"
import type { MenuItem } from '@ifhover/leaf-ui';

import { Button, Flex, Menu, Tabs } from '@ifhover/leaf-ui';

import { Folder, House, Settings } from 'lucide-react';

import { useState } from 'react';

const items: MenuItem[] = [
  { key: 'home', label: '概览', icon: <House /> },
  {
    key: 'resources',
    label: '资源',
    icon: <Folder />,
    children: [
      { key: 'components', label: '组件' },
      { key: 'themes', label: '主题' },
    ],
  },
  { key: 'settings', label: '设置', icon: <Settings /> },
];

export function MenuLayouts() {
  const [collapsed, setCollapsed] = useState(true);
  return (
    <Tabs
      aria-label="菜单排列方式"
      items={[
        {
          key: 'vertical',
          label: '垂直',
          children: (
            <Menu
              mode="vertical"
              items={items}
              defaultSelectedKey="components"
              defaultOpenKeys={['resources']}
            />
          ),
        },
        {
          key: 'inline',
          label: '内嵌',
          children: (
            <Menu
              mode="inline"
              items={items}
              defaultSelectedKey="components"
              defaultOpenKeys={['resources']}
            />
          ),
        },
        {
          key: 'horizontal',
          label: '横向',
          children: <Menu mode="horizontal" items={items} defaultSelectedKey="components" />,
        },
        {
          key: 'collapsed',
          label: '折叠侧栏',
          children: (
            <Flex direction="column" gap="md" align="start">
              <Button
                variant="soft"
                size="sm"
                aria-expanded={!collapsed}
                onClick={() => setCollapsed((value) => !value)}
              >
                {collapsed ? '展开侧栏' : '折叠侧栏'}
              </Button>
              <Menu collapsed={collapsed} items={items} defaultSelectedKey="components" />
            </Flex>
          ),
        },
      ]}
    />
  );
}
```

展开代码

## 垂直菜单分组

在 `items` 中使用 `type: 'group'`，通过 `label` 设置分组标题、`children` 设置菜单项。标题支持 ReactNode，不参与选择和方向键导航；折叠菜单隐藏标题，保留菜单项。


工作空间

概览

项目

账户

设置

groups.tsx

复制代码

```tsx lineNumbers file="../../src/examples/menu/groups.tsx"
import { Menu } from '@ifhover/leaf-ui';

export function MenuGroups() {
  return (
    <Menu
      defaultSelectedKey="overview"
      items={[
        {
          key: 'workspace',
          type: 'group',
          label: '工作空间',
          children: [
            { key: 'overview', label: '概览' },
            { key: 'projects', label: '项目' },
          ],
        },
        {
          key: 'account',
          type: 'group',
          label: '账户',
          children: [{ key: 'settings', label: '设置' }],
        },
      ]}
    />
  );
}
```

展开代码

## 复用分组标题

`MenuGroup` 复用菜单的分组标题样式，`title` 接受 ReactNode，`children` 可以放菜单、说明、按钮或任意自定义元素。只有菜单项时优先使用上面的 `items` 分组，共享同一个 Menu 的选择状态与键盘导航。

自定义内容可以作为 Menu 的相邻区域放进独立的 `MenuGroup`，控件保留自己的 Tab 顺序和键盘行为。`MenuGroup` 自身不管理选中项、展开或折叠；侧栏收起时，由应用决定是否隐藏这个区域。


快捷操作

分组标题下可以放说明、按钮和其他自定义内容。创建项目

custom-group.tsx

复制代码

```tsx lineNumbers file="../../src/examples/menu/custom-group.tsx"
import { Button, MenuGroup, Space, Text } from '@ifhover/leaf-ui';

import { useState } from 'react';

export function MenuCustomGroup() {
  const [created, setCreated] = useState(false);
  return (
    <MenuGroup title="快捷操作">
      <Space direction="vertical" align="start">
        <Text tone="muted">分组标题下可以放说明、按钮和其他自定义内容。</Text>
        <Button onClick={() => setCreated(true)}>创建项目</Button>
        {created && <Text role="status">项目已创建</Text>}
      </Space>
    </MenuGroup>
  );
}
```

展开代码

## 受控导航与路由

`selectedKey` 跟随当前路由。`linkRender` 可替换默认链接，例如 React Router 或 Next.js 的 Link；保留传入链接的事件、类名和 ARIA 属性。


[概览](#overview)

资源

[使用指南](#guides)

[设置](#settings)

当前页面：overview。示例切换本地状态；接入路由时使用自己的 Link，并保留传入属性。

capabilities.tsx

复制代码

```tsx lineNumbers file="../../src/examples/menu/capabilities.tsx"
import type { MenuItem } from '@ifhover/leaf-ui';

import { Menu, Space, Text } from '@ifhover/leaf-ui';

import { BookOpen, House } from 'lucide-react';

import { type AnchorHTMLAttributes, cloneElement, isValidElement, type ReactElement, useState } from 'react';

export function MenuRouting() {
  const [selected, setSelected] = useState('overview');
  const items: MenuItem[] = [
    { key: 'overview', label: '概览', href: '#overview', icon: <House /> },
    {
      key: 'resources',
      label: '资源',
      icon: <BookOpen />,
      children: [
        { key: 'guides', label: '使用指南', href: '#guides' },
        {
          key: 'settings',
          label: '设置',
          href: '#settings',
        },
      ],
    },
  ];
  return (
    <Space direction="vertical" align="center">
      <Menu
        aria-label={'应用导航'}
        items={items}
        selectedKey={selected}
        defaultOpenKeys={['resources']}
        onSelect={setSelected}
        linkRender={(_item, link) => {
          if (!isValidElement(link)) return link;
          const element = link as ReactElement<AnchorHTMLAttributes<HTMLAnchorElement>>;
          return cloneElement(element, {
            onClick: (event) => {
              element.props.onClick?.(event);
              event.preventDefault();
            },
          });
        }}
      />
      <Text tone="muted">
        {`当前页面：${selected}。示例切换本地状态；接入路由时使用自己的 Link，并保留传入属性。`}
      </Text>
    </Space>
  );
}
```

展开代码

## 使用说明

纵向菜单默认 240px，折叠时 60px，可通过 style.width 调整。items 支持嵌套、分组、分隔线与链接。selectedKey 管理当前选中项，openKeys 管理展开项；横向模式与折叠模式使用浮层展示子菜单。

横向导航的子菜单在触发项下方展开；折叠侧栏的子菜单向侧边展开，并显示父级标题。浮层统一图标、文字、分组与选中背景，仅当前页面高亮，所属父项保持普通样式。RTL 布局自动反转侧向展开方向，靠近屏幕边缘时浮层会调整位置。

子菜单展开、收起以及侧栏宽度变化均带有过渡。收起的子项不会参与键盘导航；系统开启减少动态效果时，过渡会自动关闭。

菜单项、悬停背景和选中高亮与按钮共用基础圆角。通过 `ConfigProvider` 的 `theme.borderRadius` 统一调整，也可用 `theme.components.Menu.borderRadius` 单独设置菜单圆角。

可选条目悬停、键盘聚焦和按下时只改变文字与图标颜色，背景保持不变，选中项保留主题色底色；危险项使用危险色反馈。带子菜单的展开项继续使用浅色悬停背景和较深的按下背景。子菜单浮层沿用相同规则，子菜单默认行高为 32px，紧凑密度为 26px，并跟随区域控件高度调整。

## 类型

组装数据、定义回调和使用 ref 时，可以导入这些类型。

```ts
import type { MenuGroupProps, MenuItem, MenuProps } from '@ifhover/leaf-ui';
```

## MenuItem API

| 属性         | 类型                                         | 默认值 | 说明          |
| ---------- | ------------------------------------------ | --- | ----------- |
| `key`      | `string`                                   | 必填  | 必需。唯一且稳定的标识 |
| `label`    | `ReactNode`                                | 必填  | 必需。显示内容     |
| `icon`     | `ReactNode`                                | —   | 自定义图标       |
| `href`     | `string`                                   | —   | 目标链接        |
| `disabled` | `boolean`                                  | —   | 禁用交互。       |
| `danger`   | `boolean`                                  | —   | 危险操作颜色      |
| `children` | `readonly `[`MenuItem`](#menuitem-api)`[]` | —   | 内容或下级项目     |
| `type`     | `'item' \| 'group' \| 'divider'`           | —   | 显示或选项类型     |

## MenuProps API

| 属性                   | 类型                                                                       | 默认值          | 说明                     |
| -------------------- | ------------------------------------------------------------------------ | ------------ | ---------------------- |
| `items`              | `readonly `[`MenuItem`](#menuitem-api)`[]`                               | 必填           | 必需。数据项目，保持 key 唯一且稳定   |
| `mode`               | `'vertical' \| 'horizontal' \| 'inline'`                                 | `'vertical'` | 菜单排列方式                 |
| `selectedKey`        | `string`                                                                 | —            | 受控选中项目                 |
| `defaultSelectedKey` | `string`                                                                 | `''`         | 初始选中项目                 |
| `onSelect`           | `(key: string, item: `[`MenuItem`](#menuitem-api)`) => void`             | —            | 选择项目                   |
| `openKeys`           | `readonly string[]`                                                      | —            | 受控展开项目                 |
| `defaultOpenKeys`    | `readonly string[]`                                                      | `[]`         | 初始展开项目                 |
| `onOpenChange`       | `(keys: string[]) => void`                                               | —            | 展开状态变化                 |
| `collapsed`          | `boolean`                                                                | `false`      | 受控收起状态                 |
| `itemRender`         | `(item: `[`MenuItem`](#menuitem-api)`, content: ReactNode) => ReactNode` | —            | 自定义条目内容。               |
| `linkRender`         | `(item: `[`MenuItem`](#menuitem-api)`, link: ReactNode) => ReactNode`    | —            | 接入路由链接时保留传入的可访问性和事件属性。 |
| `itemClassName`      | `string \| ((item: `[`MenuItem`](#menuitem-api)`) => string)`            | —            | 条目类名或按条目计算的类名。         |

## MenuGroup API

支持原生 div 属性及指向 `HTMLDivElement` 的 ref。分组通过标题提供可访问名称，也可用 `aria-label` 或 `aria-labelledby` 覆盖。可从根入口或 `@ifhover/leaf-ui/menu` 导入。

| 属性         | 类型          | 默认值 | 说明                      |
| ---------- | ----------- | --- | ----------------------- |
| `title`    | `ReactNode` | 必填  | 必需。分组标题，与 Menu 内的分组共用样式 |
| `children` | `ReactNode` | —   | 菜单或任意自定义内容              |
