React menu UI component library providing comprehensive menu components for interactive navigation systems
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The SubMenu system provides nested menu functionality with support for both popup and inline display modes, configurable triggers, animations, and comprehensive event handling for complex menu hierarchies.
Nested menu component supporting both popup and inline modes with extensive customization options.
/**
* Nested menu component with popup and inline support
* @param props - SubMenu configuration props
* @returns React component
*/
declare const SubMenu: React.ForwardRefExoticComponent<SubMenuProps & React.RefAttributes<HTMLLIElement>>;
interface SubMenuProps {
// Content
title?: React.ReactNode;
children?: React.ReactNode;
// State
disabled?: boolean;
// Popup Configuration
popupClassName?: string;
popupStyle?: React.CSSProperties;
popupOffset?: number[];
overflowedIndicator?: React.ReactNode;
// Icons
itemIcon?: React.ReactNode | ((props: any) => React.ReactNode);
expandIcon?: React.ReactNode | ((props: any) => React.ReactNode);
// Event Handlers
onTitleClick?: (info: { key: string; domEvent: React.MouseEvent | React.KeyboardEvent }) => void;
onTitleMouseEnter?: (info: { key: string; domEvent: React.MouseEvent }) => void;
onTitleMouseLeave?: (info: { key: string; domEvent: React.MouseEvent }) => void;
}Usage Examples:
import Menu, { SubMenu, MenuItem } from "rc-menu";
// Basic submenu
<Menu mode="vertical">
<MenuItem key="1">Option 1</MenuItem>
<SubMenu key="sub1" title="Sub Menu">
<MenuItem key="2">Sub Option 1</MenuItem>
<MenuItem key="3">Sub Option 2</MenuItem>
</SubMenu>
</Menu>
// Submenu with custom icon
<SubMenu
key="sub1"
title="Settings"
itemIcon={<SettingsIcon />}
expandIcon={({ isOpen }) => isOpen ? <UpIcon /> : <DownIcon />}
>
<MenuItem key="profile">Profile</MenuItem>
<MenuItem key="preferences">Preferences</MenuItem>
</SubMenu>
// Submenu with popup styling
<SubMenu
key="sub1"
title="Advanced"
popupClassName="custom-popup"
popupStyle={{ borderRadius: '8px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)' }}
popupOffset={[0, 8]}
>
<MenuItem key="advanced1">Advanced Option 1</MenuItem>
<MenuItem key="advanced2">Advanced Option 2</MenuItem>
</SubMenu>SubMenu behavior changes based on the parent Menu's mode property.
Inline Mode:
// SubMenus expand/collapse within the menu container
<Menu mode="inline" defaultOpenKeys={['sub1']}>
<MenuItem key="1">Item 1</MenuItem>
<SubMenu key="sub1" title="Inline SubMenu">
<MenuItem key="2">Sub Item 1</MenuItem>
<MenuItem key="3">Sub Item 2</MenuItem>
<SubMenu key="sub2" title="Nested SubMenu">
<MenuItem key="4">Nested Item</MenuItem>
</SubMenu>
</SubMenu>
</Menu>Vertical Mode:
// SubMenus appear as popups to the right
<Menu mode="vertical" triggerSubMenuAction="hover">
<MenuItem key="1">Item 1</MenuItem>
<SubMenu key="sub1" title="Vertical SubMenu">
<MenuItem key="2">Popup Item 1</MenuItem>
<MenuItem key="3">Popup Item 2</MenuItem>
</SubMenu>
</Menu>Horizontal Mode:
// SubMenus appear as dropdowns below
<Menu mode="horizontal" triggerSubMenuAction="hover">
<MenuItem key="1">Home</MenuItem>
<SubMenu key="sub1" title="Products">
<MenuItem key="2">Product 1</MenuItem>
<MenuItem key="3">Product 2</MenuItem>
</SubMenu>
<MenuItem key="4">About</MenuItem>
</Menu>Configure how submenus open and close using different trigger actions.
// Hover trigger (default for horizontal/vertical modes)
<Menu mode="vertical" triggerSubMenuAction="hover">
<SubMenu key="sub1" title="Hover to Open">
<MenuItem key="1">Item 1</MenuItem>
</SubMenu>
</Menu>
// Click trigger
<Menu mode="vertical" triggerSubMenuAction="click">
<SubMenu key="sub1" title="Click to Open">
<MenuItem key="1">Item 1</MenuItem>
</SubMenu>
</Menu>
// Custom delay timing
<Menu
mode="vertical"
triggerSubMenuAction="hover"
subMenuOpenDelay={0.2}
subMenuCloseDelay={0.3}
>
<SubMenu key="sub1" title="Delayed Hover">
<MenuItem key="1">Item 1</MenuItem>
</SubMenu>
</Menu>SubMenus provide detailed event handling for both the submenu container and title interactions.
const handleTitleClick = (info: { key: string; domEvent: React.MouseEvent | React.KeyboardEvent }) => {
console.log('SubMenu title clicked:', info.key);
console.log('DOM event:', info.domEvent);
};
const handleTitleHover = (info: { key: string; domEvent: React.MouseEvent }) => {
console.log('SubMenu title hovered:', info.key);
};
const handleSubMenuHover = (info: { key: string; domEvent: React.MouseEvent }) => {
console.log('SubMenu container hovered:', info.key);
};
<SubMenu
key="sub1"
title="Event SubMenu"
onTitleClick={handleTitleClick}
onTitleMouseEnter={handleTitleHover}
onTitleMouseLeave={handleTitleHover}
onMouseEnter={handleSubMenuHover}
onMouseLeave={handleSubMenuHover}
>
<MenuItem key="1">Item 1</MenuItem>
<MenuItem key="2">Item 2</MenuItem>
</SubMenu>SubMenus can be nested to create multi-level menu hierarchies.
<Menu mode="inline" defaultOpenKeys={['sub1', 'sub1-1']}>
<MenuItem key="1">Top Level Item</MenuItem>
<SubMenu key="sub1" title="Level 1 SubMenu">
<MenuItem key="2">Level 1 Item</MenuItem>
<SubMenu key="sub1-1" title="Level 2 SubMenu">
<MenuItem key="3">Level 2 Item</MenuItem>
<SubMenu key="sub1-1-1" title="Level 3 SubMenu">
<MenuItem key="4">Level 3 Item</MenuItem>
</SubMenu>
</SubMenu>
</SubMenu>
</Menu>Fine-tune popup appearance and positioning for non-inline modes.
// Custom popup container
const getPopupContainer = (node: HTMLElement) => {
return document.getElementById('menu-container') || document.body;
};
// Custom placements for popup positioning
const customPlacements = {
rightTop: {
points: ['tl', 'tr'],
overflow: { adjustX: 1, adjustY: 1 },
offset: [4, 0],
},
};
<Menu
mode="vertical"
getPopupContainer={getPopupContainer}
builtinPlacements={customPlacements}
>
<SubMenu
key="sub1"
title="Custom Positioned SubMenu"
popupClassName="custom-submenu-popup"
popupOffset={[8, 0]}
>
<MenuItem key="1">Custom Item 1</MenuItem>
<MenuItem key="2">Custom Item 2</MenuItem>
</SubMenu>
</Menu>Control SubMenu open/close states programmatically.
import { useState } from 'react';
const [openKeys, setOpenKeys] = useState<string[]>(['sub1']);
const handleOpenChange = (keys: string[]) => {
// Keep only one submenu open at a time
const latestOpenKey = keys.find(key => openKeys.indexOf(key) === -1);
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
};
<Menu
mode="inline"
openKeys={openKeys}
onOpenChange={handleOpenChange}
>
<SubMenu key="sub1" title="SubMenu 1">
<MenuItem key="1">Item 1</MenuItem>
</SubMenu>
<SubMenu key="sub2" title="SubMenu 2">
<MenuItem key="2">Item 2</MenuItem>
</SubMenu>
</Menu>Handle overflow scenarios in horizontal menus with custom indicators.
<Menu mode="horizontal">
<MenuItem key="1">Item 1</MenuItem>
<MenuItem key="2">Item 2</MenuItem>
<MenuItem key="3">Item 3</MenuItem>
<SubMenu
key="sub1"
title="More Items"
overflowedIndicator={<MoreIcon />}
>
<MenuItem key="4">Overflow Item 1</MenuItem>
<MenuItem key="5">Overflow Item 2</MenuItem>
</SubMenu>
</Menu>Install with Tessl CLI
npx tessl i tessl/npm-rc-menu