ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

react-admin ra-navigation 的 IconMenu 图标菜单组件:极简侧边栏与多级导航实战指南

react-admin ra-navigation 的 IconMenu 图标菜单组件:极简侧边栏与多级导航实战指南 react-admin ra-navigation 的 IconMenu 图标菜单组件极简侧边栏与多级导航实战指南【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin本篇技术指南围绕 react-admin 官方文档体系中的IconMenu组件展开。它来自 Enterprise Edition企业版的react-admin/ra-navigation包用于将传统侧边栏菜单替换为一条窄图标栏 滑出面板的导航界面极大节省屏幕空间并能承载任意复杂度的二级/多级子菜单。读完本文你将掌握IconMenu的完整接入流程自定义菜单 → 自定义布局 → 注入Admin、子菜单面板的构建方法、主题适配技巧以及其底层的 App Location应用位置声明机制与 react-admin 资源定义 Hook 的配合方式。为什么需要IconMenu导航空间争夺战当一个 react-admin 应用逐渐壮大即使引入了带子菜单的导航例如ra-navigation的MultiLevelMenu也可能仍然不够。子菜单层级越多侧边栏越臃肿留给内容区域的屏幕空间就越少。IconMenu针对这一问题提供了一种截然不同的交互范式一条竖直的窄栏每个一级菜单项只显示图标 小号文字标签。点击任意一项会在其右侧滑出一块面板里面可以放置任意数量的导航链接且布局完全由你掌控例如用CardContent排版标题与分组。它显著节省屏幕空间同时允许子菜单具有任意复杂度。在ra-navigation系列组件中它与其他导航组件形成了互补定位组件形态适用场景react-admin 默认Menu左侧完整列表菜单资源较少、导航层级简单的应用MultiLevelMenu左侧可折叠多级列表资源较多、需要无限层级子菜单IconMenu窄图标栏 滑出面板需要极简导航、子菜单内容可自由排布注意IconMenu属于 react-adminEnterprise Edition企业版的react-admin/ra-navigation包本仓库OSS 开源版中并不包含其实现源码。但它的所有集成点——Layout、Sidebar、主题系统、useResourceDefinitionsHook——都是 react-admin 开源核心ra-ui-materialui、ra-core提供的因此本文将以开源仓库的源码作为底层原理依据完整还原接入与使用流程。三步接入把IconMenu装进你的应用第一步编写自定义菜单组件在src/MyMenu.js中用IconMenu作为根组件用IconMenu.Item声明每个一级菜单项// in src/MyMenu.js import { IconMenu } from react-admin/ra-navigation; import DashboardIcon from mui/icons-material/Dashboard; import MusicIcon from mui/icons-material/MusicNote; import PeopleIcon from mui/icons-material/People; const MyMenu () ( IconMenu variantcategories IconMenu.Item namedashboard to/ labelDashboard icon{DashboardIcon /} / IconMenu.Item namesongs to/songs labelSongs icon{MusicIcon /} / {/* The empty filter is required to avoid falling back to the previously set filter */} IconMenu.Item nameartists to/artists labelArtists icon{PeopleIcon /} / /IconMenu );这段代码有几点值得注意variantcategories是分组categories形态用于将菜单项聚合成分类式窄栏不传时则使用默认形态二者在后续代码示例中均有出现每个IconMenu.Item都有一个唯一的name它对应页面声明的 App Location见下文App Location 机制用于高亮当前所在位置to是点击后跳转的路径注释中特别提示使用?filter形式的带筛选目标时空的 filter 是必须的否则会回退到上一次设置的筛选条件。第二步编写自定义布局并包裹AppLocationContextIconMenu需要通过 react-admin 的Layout组件注入。与此同时必须用AppLocationContext包裹整个布局// in src/MyLayout.js import { Layout } from react-admin; import { AppLocationContext } from react-admin/ra-navigation; import { MyMenu } from ./MyMenu; export const MyLayout ({ children }) ( AppLocationContext Layout menu{MyMenu} {children} /Layout /AppLocationContext );为什么必须包裹AppLocationContext因为ra-navigation不通过 URL 判断当前所在位置而是由页面组件用useDefineAppLocation()Hook主动声明自己的位置。这种设计允许构建多级嵌套的复杂站点地图例如artists.edit.songs.edit这样的四级位置这是纯 URL 匹配难以优雅表达的。第三步注入Admin并应用 ra-navigation 主题// in src/App.js import { Admin, Resource } from react-admin; import { theme } from react-admin/ra-navigation; import { MyLayout } from ./MyLayout; const App () ( Admin layout{MyLayout} dataProvider{...} theme{theme} // ... /Admin );主题适配让Sidebar宽度匹配图标栏IconMenu是一条窄栏而 react-admin 默认的Sidebar展开宽度是 240px见 packages/ra-ui-materialui/src/theme/defaultTheme.tswidth: 240, closedWidth: 50。如果不做调整两者会不协调。官方提供了两种方案方案一推荐直接应用react-admin/ra-navigation导出的theme即上文第三步的做法。该主题已针对categories形态调整好侧边栏尺寸。方案二把 sidebar 尺寸合并进你自己的自定义主题import merge from lodash/merge; import { defaultTheme } from react-admin; import { ThemeOptions } from react-admin/ra-navigation; export const theme: ThemeOptions merge({}, defaultTheme, { sidebar: { width: 96, closedWidth: 48, }, overrides: { RaSidebar: { fixed: { zIndex: 1200, }, }, }, });从源码角度看Sidebar正是通过theme.sidebar来解析宽度的在 packages/ra-ui-materialui/src/layout/Sidebar.tsx 中执行lodashGet(theme, sidebar.closedWidth, CLOSED_DRAWER_WIDTH)packages/ra-ui-materialui/src/layout/Menu.tsx 中也有同样的读取逻辑。因此只要theme.sidebar.width与closedWidth设置得当IconMenu的窄栏与 react-admin 的侧边栏行为就能无缝对齐overrides.RaSidebar.fixed.zIndex用于确保固定定位的侧边栏层级正确。Tip官方建议使用IconMenu /后侧边栏在收起模式下可能因为 react-admin 的内部机制导致标签文字消失。因此官方建议实现自己的AppBar /并隐藏汉堡按钮Hamburger Button——IconMenu /本身足够窄不会妨碍导航并不需要收起/展开的切换入口。Props 一览PropRequiredTypeDefaultDescriptionchildrenOptionalReactNode-要渲染的菜单项IconMenu.Item链接。sxOptionalSxProps-样式覆盖由 MUI System 提供能力。此外其余 props 会原样传递给根div元素。children定义一级菜单项将IconMenu.Item作为IconMenu的子元素即可定义菜单的主入口项// in src/MyMenu.js import { IconMenu } from react-admin/ra-navigation; import DashboardIcon from mui/icons-material/Dashboard; import MusicIcon from mui/icons-material/MusicNote; import PeopleIcon from mui/icons-material/People; const MyMenu () ( IconMenu IconMenu.Item namedashboard to/ labelDashboard icon{DashboardIcon /} / IconMenu.Item namesongs to/songs labelSongs icon{MusicIcon /} / IconMenu.Item nameartists to/artists labelArtists icon{PeopleIcon /} / /IconMenu );sxCSS API 与全局样式覆盖通过sx可以微调组件及其内部元素的样式export const MyMenu () ( IconMenu sx{{ marginTop: 0 }} // ... /IconMenu );如果需要应用级统一覆盖IconMenu的样式而不是逐个实例覆盖请使用RaMenuRoot这个样式 key。其原理与 react-admin 其他组件的Ra*命名空间一致在自定义主题中通过overrides或components为RaMenuRoot设置规则即可全局生效。IconMenu.Item菜单项详析IconMenu.Item负责渲染标签 图标的菜单项IconMenu.Item namedashboard to/ labelDashboard icon{DashboardIcon /} /必需 propsname要匹配的位置名称用于高亮当前所在位置对应页面声明的 App Locationto点击后跳转到的位置label菜单项显示文本icon显示的图标元素。可选 propschildren滑出面板的内容——当菜单被点击时展示详见下文添加子菜单sxMUI System 驱动的样式覆盖。其余 props 会传递给底层 Material UI 的listItem组件。添加子菜单滑出面板的构建在IconMenu.Item中放入 children即可定义点击该菜单项时滑出的面板内容。由于IconMenu会把面板内容渲染在 Material UI 的Card内部通常用CardContent包裹内容以获得一致的间距。下面是一个完整的示例给 Artists 菜单添加三个按艺术家类型过滤的子入口Rock / Jazz / Classicalimport { IconMenu, MenuItemList, MenuItemNode, } from react-admin/ra-navigation; import DashboardIcon from mui/icons-material/Dashboard; import MusicIcon from mui/icons-material/MusicNote; import PeopleIcon from mui/icons-material/People; const MyMenu () ( IconMenu IconMenu.Item namedashboard to/ labelDashboard icon{DashboardIcon /} / IconMenu.Item namesongs to/songs labelSongs icon{MusicIcon /} / IconMenu.Item nameartists to/artists labelArtists icon{PeopleIcon /} CardContent {/* to get consistent spacing */} Typography varianth3 gutterBottom Artist Categories /Typography {/* Note that we must wrap our MenuItemNode components in a MenuItemList */} MenuItemList MenuItemNode nameartists.rock to{/artists?filter{type:rock}} labelRock / MenuItemNode nameartists.jazz to{/artists?filter{type:jazz}} labelJazz / MenuItemNode nameartists.classical to{/artists?filter{type:classical}} labelClassical / /MenuItemList /CardContent /IconMenu.Item /IconMenu );要点面板内部可以自由排版——示例中使用Typography标题对子项进行分组说明MenuItemNode必须被包裹在MenuItemList中源码注释与文档均明确强调以保证列表的布局与导航语义正确每个MenuItemNode同样有唯一的name这里用点号命名空间artists.rock、artists.jazz……来体现层级关系子入口的目标可以是带filter的同一个页面实现同一列表、不同筛选视图的导航体验。为资源批量生成菜单项useResourceDefinitions实战如果你希望保留默认资源菜单项的同时追加自定义项react-admin 提供了useResourceDefinitionsHook。它返回应用中所有资源的定义name、hasList、hasEdit、hasShow、hasCreate、options、icon、recordRepresentation等详见其源码 packages/ra-core/src/core/useResourceDefinitions.ts。// in src/MyMenu.js import { createElement } from react; import { useResourceDefinitions } from react-admin; import { IconMenu } from react-admin/ra-navigation; import LabelIcon from mui/icons-material/Label; export const MyMenu () { const resources useResourceDefinitions(); return ( IconMenu {Object.keys(resources).map(name ( IconMenu.Item key{name} name{name} to{/${name}} label{resources[name].options resources[name].options.label || name} icon{createElement(resources[name].icon)} / ))} IconMenu.Item namecustom.route to/custom-route labelMiscellaneous icon{LabelIcon /} / /IconMenu ); };解读这段代码的底层行为Object.keys(resources)返回所有已注册资源的name每个资源的options.label优先作为菜单标签未定义时回退为资源名nameresources[name].icon取自Resource icon{...}的配置这正是Resource文档中icon属性的用途用createElement渲染为 React 元素循环结束后可以继续追加自定义入口如示例中的custom.route。这种资源驱动的菜单生成方式保证了菜单与Admin中声明的资源永远保持一致新增资源后无需手动维护菜单。深入App Location 机制IconMenu的高亮、面包屑与面板联动都建立在App Location之上。它是一串以点分隔的位置片段如artists.edit.songs.edit由页面通过useDefineAppLocationHook 声明。相关文档详见 docs/useDefineAppLocation.md。基本用法——在页面组件中声明自己的位置// in src/UserPreferences.jsx import { useDefineAppLocation } from react-admin/ra-navigation; const UserPreferences () { useDefineAppLocation(user.preferences); return spanMy Preferences/span; };然后把它挂到自定义路由上// in src/App.jsx import { Admin, Resource, CustomRoutes, } from react-admin; import { Route } from react-router-dom; import { MyLayout } from ./MyLayout; import { UserPreferences } from ./UserPreferences; const App () ( Admin dataProvider{dataProvider} layout{MyLayout} ... CustomRoutes Route exact path/preferences component{UserPreferences} /, /CustomRoutes /Admin );IconMenu.Item nameuser.preferences ...中的name一旦与页面声明的 App Location 匹配菜单项即被高亮。CRUD 页面无需手动声明——react-admin 会为每个资源页面自动生成默认位置List[resource].listCreate[resource].createEdit[resource].edit位置中同时携带当前recordShow[resource].show位置中同时携带当前record同时你也可以覆盖默认值例如把 Post 列表页的位置声明为posts.publishedimport { List, DataTable } from react-admin; import { useDefineAppLocation } from react-admin/ra-navigation; export const PublishedPostsList () { useDefineAppLocation(posts.published); return ( List filter{{ isPublished: true }} DataTable DataTable.Col sourcetitle / ... /DataTable /List ); }兜底逻辑如果页面任何位置都没有调用useDefineAppLocationAppLocationContext会根据当前 URL 路径推断资源位置例如/artists/:id会被推断为artists.edit。这一点保证了未接入声明机制的页面也不会破坏导航高亮。最后IconMenu所在的ra-navigation生态中读取 App Location 的组件还包括Breadcrumb、MultiLevelMenu、HorizontalMenu以及ContainerLayout。如果你的应用规模不大、更偏好顶栏导航也可以考虑ContainerLayout与HorizontalMenu的组合而需要更密集的左侧多级列表时MultiLevelMenu是另一个选择。导航组件的选型应基于内容规模与交互习惯资源少选默认Menu层级深选MultiLevelMenu追求极致空间利用率则选IconMenu。【免费下载链接】react-adminA frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design项目地址: https://gitcode.com/gh_mirrors/re/react-admin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表