DropdownMenu
TIP
If you need to customize your work of the dropdown menu, refer to the documentation for intergalactic/popper
The component is a wrapper over the intergalactic/dropdown that allows for the following:
- Displaying a list of options in a dropdown
- Scrolling through the list of options using keyboard
Basic usage
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
const Demo = () => {
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>Actions</DropdownMenu.Trigger>
<DropdownMenu.Menu>
<DropdownMenu.Item>Save</DropdownMenu.Item>
<DropdownMenu.Item>Rename</DropdownMenu.Item>
<DropdownMenu.Item>Download</DropdownMenu.Item>
<DropdownMenu.Item>Delete</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
);
};
export default Demo;
Dropdown menu
There are a few ways to display the dropdown menu in this component.
First method
The easiest way is to use DropdownMenu.Menu
.
This is best when you only need to manage the content within the options list.
DropdownMenu.Menu
is a wrapper around DropdownMenu.Popper
and DropdownMenu.List
, and all props pass through to DropdownMenu.List
.
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
import { Text } from '@semcore/typography';
import { Flex } from '@semcore/flex-box';
const Demo = () => {
return (
<Flex direction='column'>
<Text tag='label' size={200} htmlFor='dropdown-menu-children-items'>
Your action
</Text>
<DropdownMenu>
<DropdownMenu.Trigger tag={Button} mt={2} mr='auto' id='dropdown-menu-children-items'>
Explore menu items
</DropdownMenu.Trigger>
{/* Adding max-height to the dropdown menu */}
<DropdownMenu.Menu hMax={'180px'}>
<DropdownMenu.Group title={'List heading'} subTitle={'Subtitle'}>
<DropdownMenu.Item>Menu item 1</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 2</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 3</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 4</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 5</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 6</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 7</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 8</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 9</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Menu>
</DropdownMenu>
</Flex>
);
};
export default Demo;
Second method
Use a combination of two components:
DropdownMenu.Popper
—for the dropdown layoutDropdownMenu.List
andScrollArea
—for the option list styles
This method works well when you need flexible customization of the dropdown menu content.
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
import Link from '@semcore/link';
import { Text } from '@semcore/typography';
import Notice from '@semcore/notice';
import SpinContainer from '@semcore/spin-container';
import FileExportM from '@semcore/icon/FileExport/m';
const Demo = () => {
const [loading, setLoading] = React.useState(false);
const handleClick = () => {
setLoading(true);
setTimeout(() => setLoading(false), 1000);
};
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>
<Button.Addon>
<FileExportM />
</Button.Addon>
<Button.Text>Export</Button.Text>
</DropdownMenu.Trigger>
<DropdownMenu.Popper wMax='256px' aria-label={'Export options'}>
<SpinContainer loading={loading}>
<DropdownMenu.List>
<DropdownMenu.Item onClick={handleClick}>Excel</DropdownMenu.Item>
<DropdownMenu.Item onClick={handleClick}>CSV</DropdownMenu.Item>
<DropdownMenu.Item onClick={handleClick}>CSV Semicolon</DropdownMenu.Item>
</DropdownMenu.List>
<Notice
theme='warning'
style={{
padding: 'var(--intergalactic-spacing-3x) var(--intergalactic-spacing-2x)',
borderWidth: 0,
borderTopWidth: '1px',
borderRadius:
'0 0 var(--intergalactic-rounded-medium) var(--intergalactic-rounded-medium)',
}}
>
<Notice.Content>
<Text tag='strong' mb={1} style={{ display: 'block' }}>
Export failed
</Text>
<Text>
If the problem persists, please contact us at{' '}
<Link inline href='mailto:feedback@semrush.com'>
feedback@semrush.com
</Link>
</Text>
</Notice.Content>
</Notice>
</SpinContainer>
</DropdownMenu.Popper>
</DropdownMenu>
);
};
export default Demo;
Menu item types
The component offers several options for laying out list item types:
DropdownMenu.Item
: A list element that can be selected with the keyboard.DropdownMenu.Item.Content
: The content within an item, used when you need to include a hint or submenu.DropdownMenu.Item.Addon
: Used to add, for example, icons.DropdownMenu.Item.Text
: Used for wrapping text if used with addons.DropdownMenu.Item.Hint
: A subheading or message with additional information (can't be selected with the keyboard).
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
import Tooltip from '@semcore/tooltip';
import DesktopIconM from '@semcore/icon/Desktop/m';
const TooltipContent = () => {
const tooltipIndex = React.useContext(DropdownMenu.selectedIndexContext);
return <div>Some tooltip for {tooltipIndex + 1}</div>;
};
const Demo = () => {
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>Explore menu item types</DropdownMenu.Trigger>
<DropdownMenu.Menu>
<Tooltip placement={'right'} timeout={[0, 50]}>
<DropdownMenu.Group title={'Menu title'} subTitle={'Subtitle'}>
<DropdownMenu.Item tag={Tooltip.Trigger}>
<DropdownMenu.Item.Content>Menu item 1</DropdownMenu.Item.Content>
</DropdownMenu.Item>
<DropdownMenu.Item tag={Tooltip.Trigger}>
<DropdownMenu.Item.Content>Menu item 2</DropdownMenu.Item.Content>
<DropdownMenu.Item.Hint>Hint for menu item 2</DropdownMenu.Item.Hint>
</DropdownMenu.Item>
<DropdownMenu.Item tag={Tooltip.Trigger}>
<DropdownMenu.Item.Content>
<DropdownMenu.Item.Addon>
<DesktopIconM />
</DropdownMenu.Item.Addon>
<DropdownMenu.Item.Text>Menu item 3</DropdownMenu.Item.Text>
</DropdownMenu.Item.Content>
<DropdownMenu.Item.Hint>Hint for menu item 3</DropdownMenu.Item.Hint>
</DropdownMenu.Item>
<DropdownMenu.Item tag={Tooltip.Trigger}>
<DropdownMenu.Item.Content>Menu item 4</DropdownMenu.Item.Content>
</DropdownMenu.Item>
</DropdownMenu.Group>
<Tooltip.Popper w={120} aria-hidden={true}>
<TooltipContent />
</Tooltip.Popper>
</Tooltip>
</DropdownMenu.Menu>
</DropdownMenu>
);
};
export default Demo;
Menu item with actions
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import { Flex } from '@semcore/flex-box';
import TrashM from '@semcore/icon/Trash/m';
import PlusM from '@semcore/icon/MathPlus/m';
import Button from '@semcore/button';
import ChevronRightIcon from '@semcore/icon/ChevronRight/m';
const Demo = () => {
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>Explore menu items with actions</DropdownMenu.Trigger>
<DropdownMenu.Menu>
<DropdownMenu.Item>Menu item 1</DropdownMenu.Item>
<DropdownMenu.Item>Menu item 2</DropdownMenu.Item>
<DropdownMenu.Item>
<DropdownMenu inlineActions placement={'right'}>
<Flex justifyContent='space-between'>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Menu item 3
</DropdownMenu.Item.Content>
<DropdownMenu.Actions gap={1}>
<DropdownMenu.Item tag={Button} addonLeft={PlusM} title={'Add new'} />
<DropdownMenu.Item tag={Button} addonLeft={TrashM} title={'Delete'} />
</DropdownMenu.Actions>
</Flex>
</DropdownMenu>
</DropdownMenu.Item>
<DropdownMenu.Item>
<DropdownMenu
placement={'right-start'}
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Menu item 4
<ChevronRightIcon color='icon-secondary-neutral' />
</DropdownMenu.Item.Content>
<DropdownMenu.Menu>
<DropdownMenu.Item>Add</DropdownMenu.Item>
<DropdownMenu.Item>Delete</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
);
};
export default Demo;
Nested menus
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import Button from '@semcore/button';
import ChevronRightIcon from '@semcore/icon/ChevronRight/m';
const Demo = () => {
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>Nested menus</DropdownMenu.Trigger>
<DropdownMenu.Menu>
<DropdownMenu.Item>Item 1</DropdownMenu.Item>
<DropdownMenu.Item>Item 2</DropdownMenu.Item>
<DropdownMenu.Item>Item 3</DropdownMenu.Item>
<DropdownMenu.Item>
<DropdownMenu
placement='right-start'
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Item 4
<DropdownMenu.Item.Addon tag={ChevronRightIcon} color='icon-secondary-neutral' />
</DropdownMenu.Item.Content>
<DropdownMenu.Menu w={120}>
<DropdownMenu.Item>
<DropdownMenu
placement='right-start'
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Item 4.1
<DropdownMenu.Item.Addon
tag={ChevronRightIcon}
color='icon-secondary-neutral'
/>
</DropdownMenu.Item.Content>
<DropdownMenu.Menu w={120}>
<DropdownMenu.Item>Item 4.1.1</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.1.2</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.1.3</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DropdownMenu.Item>
<DropdownMenu.Item>
<DropdownMenu
placement='right-start'
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Item 4.2
<DropdownMenu.Item.Addon
tag={ChevronRightIcon}
color='icon-secondary-neutral'
/>
</DropdownMenu.Item.Content>
<DropdownMenu.Menu w={120}>
<DropdownMenu.Item>
<DropdownMenu
placement='right-start'
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
Item 4.2.1
<DropdownMenu.Item.Addon
tag={ChevronRightIcon}
color='icon-secondary-neutral'
/>
</DropdownMenu.Item.Content>
<DropdownMenu.Menu w={120}>
<DropdownMenu.Item>Item 4.2.1.1</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.2.1.2</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.2.1.3</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.2.2</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.2.3</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.3</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
</DropdownMenu.Item>
<DropdownMenu.Item>Item 5</DropdownMenu.Item>
</DropdownMenu.Menu>
</DropdownMenu>
);
};
export default Demo;
Nested menus with focusable elements
tsx
import React from 'react';
import DropdownMenu from '@semcore/dropdown-menu';
import ChevronRightIcon from '@semcore/icon/ChevronRight/m';
import InputNumber from '@semcore/input-number';
import NeighborLocation from '@semcore/neighbor-location';
import Divider from '@semcore/divider';
import { Box } from '@semcore/flex-box';
import Button from '@semcore/button';
const options = ['Item 1', 'Item 2', 'Item 3'];
const min = 1;
const max = 8;
const Demo = () => {
return (
<DropdownMenu>
<DropdownMenu.Trigger tag={Button}>Click me</DropdownMenu.Trigger>
<DropdownMenu.Menu>
{options.map((item) => {
return (
<DropdownMenu.Item key={item}>
<DropdownMenu
placement='right-start'
interaction={DropdownMenu.nestedMenuInteraction}
timeout={[0, 300]}
offset={[-11, 12]}
>
<DropdownMenu.Item.Content tag={DropdownMenu.Trigger}>
{item}
<DropdownMenu.Item.Addon tag={ChevronRightIcon} color='icon-secondary-neutral' />
</DropdownMenu.Item.Content>
<DropdownMenu.Popper w={150} aria-label={'Some item 4 options and controls'}>
<DropdownMenu.List>
<DropdownMenu.Item>Item 4.1.1</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.1.2</DropdownMenu.Item>
<DropdownMenu.Item>Item 4.1.3</DropdownMenu.Item>
</DropdownMenu.List>
<Divider my={1} />
<Box p={2}>
<NeighborLocation>
<InputNumber w='50%'>
<InputNumber.Value min={min} max={max} placeholder={min.toString()} />
<InputNumber.Controls />
</InputNumber>
<InputNumber w='50%'>
<InputNumber.Value min={min} max={max} placeholder={max.toString()} />
<InputNumber.Controls />
</InputNumber>
</NeighborLocation>
<Button w='100%' mt={1} use='primary'>
Apply
</Button>
</Box>
</DropdownMenu.Popper>
</DropdownMenu>
</DropdownMenu.Item>
);
})}
</DropdownMenu.Menu>
</DropdownMenu>
);
};
export default Demo;
export const App = () => <Demo />;
Last updated: