Component
context menu
dx components add context_menuA context menu component for displaying a list of actions or option after right clicking an area.
Installation
Use the CLI command for the common path, or copy the component files manually.
Manual installation files
Copy the component source and CSS into your app. Import the shared theme CSS once near your app root.
use dioxus::prelude::*;
use dioxus_icons::lucide::ChevronRight;
use dioxus_primitives::context_menu::{
self, ContextMenuContentProps, ContextMenuItemProps, ContextMenuProps,
ContextMenuSubContentProps, ContextMenuSubItemProps, ContextMenuSubProps,
ContextMenuSubTriggerProps, ContextMenuTriggerProps,
};
#[component]
pub fn ContextMenu(props: ContextMenuProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenu {
disabled: props.disabled,
open: props.open,
default_open: props.default_open,
on_open_change: props.on_open_change,
roving_loop: props.roving_loop,
attributes: props.attributes,
{props.children}
}
}
}
#[component]
pub fn ContextMenuTrigger(props: ContextMenuTriggerProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuTrigger {
padding: "20px",
background: "var(--primary-color)",
border: "1px dashed var(--primary-color-6)",
border_radius: ".5rem",
cursor: "context-menu",
user_select: "none",
text_align: "center",
attributes: props.attributes,
{props.children}
}
}
}
#[component]
pub fn ContextMenuContent(props: ContextMenuContentProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuContent {
class: "dx-context-menu-content",
id: props.id,
attributes: props.attributes,
{props.children}
}
}
}
#[component]
pub fn ContextMenuItem(props: ContextMenuItemProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuItem {
class: "dx-context-menu-item",
disabled: props.disabled,
value: props.value,
index: props.index,
text_value: props.text_value,
on_select: props.on_select,
attributes: props.attributes,
{props.children}
}
}
}
/// Themed wrapper for [`context_menu::ContextMenuSub`] -- row 68's nested
/// submenu. Renders no element itself (neither does the primitive it
/// wraps), so there is no `class`/`attributes` to attach.
#[component]
pub fn ContextMenuSub(props: ContextMenuSubProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuSub {
open: props.open,
default_open: props.default_open,
on_open_change: props.on_open_change,
roving_loop: props.roving_loop,
{props.children}
}
}
}
/// Themed wrapper for [`context_menu::ContextMenuSubTrigger`]. Carries both
/// `dx-context-menu-item` (the same hover/focus/disabled chrome every plain
/// item gets) and `dx-context-menu-sub-trigger` (the open-state highlight)
/// -- see `style.css`. Appends a `ChevronRight` icon after the caller's own
/// content, same as `dropdown_menu/component.rs`'s identical
/// `DropdownMenuSubTrigger` wrapper -- see that component's doc for why a
/// real icon element, not CSS-generated `content:` text, matters here.
#[component]
pub fn ContextMenuSubTrigger(props: ContextMenuSubTriggerProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuSubTrigger {
class: "dx-context-menu-item dx-context-menu-sub-trigger",
index: props.index,
id: props.id,
disabled: props.disabled,
text_value: props.text_value,
attributes: props.attributes,
{props.children}
ChevronRight { class: "dx-context-menu-sub-trigger-icon", size: "16px" }
}
}
}
/// Themed wrapper for [`context_menu::ContextMenuSubContent`]. Carries both
/// `dx-context-menu-content` (the shared menu chrome) and
/// `dx-context-menu-sub-content` (the side-anchored, not click-point,
/// static fallback position) -- see `style.css`.
#[component]
pub fn ContextMenuSubContent(props: ContextMenuSubContentProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuSubContent {
class: "dx-context-menu-content dx-context-menu-sub-content",
id: props.id,
attributes: props.attributes,
{props.children}
}
}
}
/// Themed wrapper for [`context_menu::ContextMenuSubItem`]. Reuses the
/// plain `dx-context-menu-item` chrome.
#[component]
pub fn ContextMenuSubItem(props: ContextMenuSubItemProps) -> Element {
rsx! {
document::Link { rel: "stylesheet", href: asset!("/src/components/context_menu/style.css") }
context_menu::ContextMenuSubItem {
class: "dx-context-menu-item",
disabled: props.disabled,
value: props.value,
index: props.index,
text_value: props.text_value,
on_select: props.on_select,
attributes: props.attributes,
{props.children}
}
}
}Usage notes
The context menu component can be used to define a context menu that is displayed when the user right-clicks on an element. It can contain various menu items that the user can interact with.
Component Structure
// The context menu component must wrap all context menu items.
ContextMenu {
// The context menu trigger is the element that will display the context menu when right-clicked.
ContextMenuTrigger {
// The content of the trigger
{children}
}
// The context menu content contains all the items that will be displayed in the context menu.
ContextMenuContent {
// Each context menu item represents an individual action in the context menu. Items are displayed in order based on the order of the index property.
ContextMenuItem {
// The index of the item, used to determine the order in which items are displayed.
index: 0,
// The value of the item which will be passed to the on_select callback when the item is selected.
value: "",
on_select: |value: String| {
// This callback is triggered when the item is selected.
// The value parameter contains the value of the selected item.
},
}
}
}Nested submenus
A ContextMenuContent may nest a submenu with ContextMenuSub/
ContextMenuSubTrigger/ContextMenuSubContent/ContextMenuSubItem, the
same APG "Menu and Menubar" pattern submenu contract
dropdown_menu's DropdownMenuSub implements (ArrowRight/Enter/Space
opens and moves focus to the first item; Escape/ArrowLeft closes and
returns focus to the sub-trigger; the parent menu stays open). A submenu
may hold plain items but not a further nested submenu of its own (one
level of nesting).
ContextMenuContent {
ContextMenuSub {
// The sub-trigger is itself an item of the enclosing menu, so it
// takes an `index` the same way `ContextMenuItem` does.
ContextMenuSubTrigger {
index: 0,
"More tools"
}
// Only rendered while the submenu is open.
ContextMenuSubContent {
ContextMenuSubItem {
index: 0,
value: "duplicate".to_string(),
on_select: |value: String| {
// Selecting a sub-item closes the entire menu tree,
// not just this submenu.
},
}
}
}
}Direction / RTL
ContextMenu accepts a dir: Option<Direction> prop, inherited by every ContextMenuSub. Same open/close-key and side flip as DropdownMenu's -- see that component's note. See the rtl variant.
Variants
Alternative examples for common configurations.