Loading...
Loading...
Implements Syncfusion React ContextMenu (SfContextMenu) for right-click interactions and context-sensitive popup menus. Use this when adding menu items, handling selection events, or customizing templates and styling. Covers setup, data binding, accessibility, keyboard navigation, common methods and properties, and integration patterns.
npx skill4agent add syncfusion/react-ui-components-skills syncfusion-react-context-menu@syncfusion/ej2-react-navigationstargetstring<ContextMenuComponent target="#myElement" items={menuItems} />function App() {
return (
<div>
<div id="target">Right-click here</div>
<ContextMenuComponent target="#target" items={menuItems} />
</div>
);
}itemsMenuItemModel[]const menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ text: 'Paste' },
{ separator: true },
{
text: 'More',
items: [
{ text: 'Properties' },
{ text: 'Delete' }
]
}
];
<ContextMenuComponent target="#target" items={menuItems} />animationSettingsMenuAnimationSettingsModeleffectdurationeasingconst animationSettings = {
effect: 'FadeIn',
duration: 300,
easing: 'ease-out'
};
<ContextMenuComponent
target="#target"
items={menuItems}
animationSettings={animationSettings}
/>| Effect | Description |
|---|---|
| No animation |
| Slide down from top |
| Zoom in effect |
| Fade in opacity |
cssClassstring<ContextMenuComponent
target="#target"
items={menuItems}
cssClass="custom-menu dark-theme"
/>enableScrollingboolean<ContextMenuComponent
target="#target"
items={largeMenuList}
enableScrolling={true}
/>filterstring// Context menu only appears on table rows, not the entire table
<ContextMenuComponent
target="#table"
filter="tr"
items={menuItems}
/>hoverDelaynumber<ContextMenuComponent
target="#target"
items={menuItems}
hoverDelay={500} // 500ms before submenu appears
/>showItemOnClickbooleantrue<ContextMenuComponent
target="#target"
items={menuItems}
showItemOnClick={true} // Submenus open on click only
/>itemTemplatestring | Function${propertyName}const template = `
<div class="menu-item">
<span class="${iconCss}"></span>
<span>${text}</span>
<span class="shortcut">${shortcut}</span>
</div>
`;
<ContextMenuComponent
target="#target"
items={menuItems}
itemTemplate={template}
/>localestring<ContextMenuComponent
target="#target"
items={menuItems}
locale="es-ES" // Spanish localization
/>enableHtmlSanitizerboolean<ContextMenuComponent
target="#target"
items={menuItems}
enableHtmlSanitizer={true} // Sanitize HTML content
/>enablePersistenceboolean<ContextMenuComponent
target="#target"
items={menuItems}
enablePersistence={true}
/>enableRtlboolean<ContextMenuComponent
target="#target"
items={menuItems}
enableRtl={true}
/>MenuItemModeltextstring{ text: 'Cut' }
{ text: 'Copy' }idstring{ id: 'cut-item', text: 'Cut' }
{ id: 'copy-item', text: 'Copy' }iconCssstring{ text: 'Cut', iconCss: 'e-icons e-cut' }
{ text: 'Copy', iconCss: 'e-icons e-copy' }
{ text: 'Delete', iconCss: 'e-icons e-delete' }itemsMenuItemModel[]{
text: 'File',
items: [
{ text: 'New' },
{ text: 'Open' },
{ text: 'Save' }
]
}separatorbooleanconst menuItems: MenuItemModel[] = [
{ text: 'Cut' },
{ text: 'Copy' },
{ separator: true }, // Visual divider
{ text: 'Delete' }
];urlstring{ text: 'Visit Site', url: 'https://example.com' }
{ text: 'Documentation', url: '/docs' }htmlAttributesRecord<string, string>{
text: 'Download',
htmlAttributes: {
'data-action': 'download',
'aria-label': 'Download file',
'title': 'Download the file'
}
}open(top: number, left: number, target?: HTMLElement): voidtoplefttargetfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const openMenuAtClick = (event: React.MouseEvent) => {
menuRef.current?.open(event.clientY, event.clientX);
};
return (
<div>
<button onClick={openMenuAtClick}>Open Menu</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}close(): voidfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const closeMenu = () => {
menuRef.current?.close();
};
const handleItemSelect = (args: MenuEventArgs) => {
console.log('Selected:', args.item?.text);
closeMenu(); // Close menu after selection
};
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
select={handleItemSelect}
/>
);
}showItems(items: string[], isUniqueId?: boolean): voiditemsisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const showDeleteOption = () => {
menuRef.current?.showItems(['Delete', 'Rename']);
};
return (
<div>
<button onClick={showDeleteOption}>Show Options</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}hideItems(items: string[], isUniqueId?: boolean): voiditemsisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
React.useEffect(() => {
// Hide delete option for read-only mode
if (isReadOnly) {
menuRef.current?.hideItems(['Delete', 'Modify']);
}
}, [isReadOnly]);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}enableItems(items: string[], enable: boolean, isUniqueId?: boolean): voiditemsenableisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const disablePasteIfNoClipboard = async () => {
const canPaste = await navigator.permissions.query({ name: 'clipboard-read' });
if (canPaste.state === 'denied') {
menuRef.current?.enableItems(['Paste'], false);
}
};
React.useEffect(() => {
disablePasteIfNoClipboard();
}, []);
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
beforeOpen={disablePasteIfNoClipboard}
/>
);
}insertAfter(items: MenuItemModel[], text: string, isUniqueId?: boolean): voiditemstextisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const addSortByOption = () => {
const newItems: MenuItemModel[] = [
{ text: 'Sort By Name' },
{ text: 'Sort By Date' }
];
menuRef.current?.insertAfter(newItems, 'View');
};
return (
<div>
<button onClick={addSortByOption}>Add Sort Options</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}insertBefore(items: MenuItemModel[], text: string, isUniqueId?: boolean): voidfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const addPremiumOptions = () => {
const premiumItems: MenuItemModel[] = [
{ text: 'Premium Feature 1' },
{ text: 'Premium Feature 2', iconCss: 'e-icons e-star' }
];
// Insert before 'Delete' option
menuRef.current?.insertBefore(premiumItems, 'Delete');
};
React.useEffect(() => {
if (user.isPremium) {
addPremiumOptions();
}
}, [user.isPremium]);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}removeItems(items: string[], isUniqueId?: boolean): voiditemsisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const removeRestrictedActions = () => {
menuRef.current?.removeItems(['Delete', 'Export', 'Archive']);
};
React.useEffect(() => {
if (userRole === 'viewer') {
removeRestrictedActions();
}
}, [userRole]);
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
created={removeRestrictedActions}
/>
);
}getItemIndex(item: MenuItem | string, isUniqueId?: boolean): number[]itemisUniqueIdnumber[]function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const findItemPosition = () => {
const indices = menuRef.current?.getItemIndex('Copy');
console.log('Copy is at index:', indices); // Output: [1] or [0, 1] for nested
const nestedIndices = menuRef.current?.getItemIndex('Open', false);
console.log('Open is at indices:', nestedIndices); // Output: [0, 1] for nested
};
return (
<div>
<button onClick={findItemPosition}>Find Item</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}setItem(item: MenuItem, id?: string, isUniqueId?: boolean): voiditemidisUniqueIdfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const updateDeleteItemStyle = () => {
const updatedItem: MenuItemModel = {
text: 'Delete',
iconCss: 'e-icons e-delete',
htmlAttributes: {
'class': 'dangerous-action'
}
};
menuRef.current?.setItem(updatedItem, 'Delete');
};
return (
<div>
<button onClick={updateDeleteItemStyle}>Update Delete Item</button>
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
</div>
);
}destroy(): voidfunction App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
React.useEffect(() => {
return () => {
// Cleanup on component unmount
menuRef.current?.destroy();
};
}, []);
return (
<ContextMenuComponent ref={menuRef} target="#target" items={menuItems} />
);
}beforeOpenEmitType<BeforeOpenCloseMenuEventArgs>interface BeforeOpenCloseMenuEventArgs {
element: HTMLElement; // Menu element
event: Event; // Browser event object
items: MenuItemModel[]; // Current menu items
cancel: boolean; // Set to true to prevent action
parentItem?: MenuItemModel; // Parent item if submenu
}function App() {
const handleBeforeOpen = (args: BeforeOpenCloseMenuEventArgs) => {
// Prevent opening on read-only elements
const target = args.event?.target as HTMLElement;
if (target?.classList.contains('read-only')) {
args.cancel = true; // Prevent menu from opening
return;
}
// Modify items before opening
const selectedText = window.getSelection()?.toString();
if (!selectedText) {
// Disable text-related operations if no text selected
args.items = args.items?.map(item => ({
...item,
disabled: ['Copy', 'Cut'].includes(item.text as string)
}));
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeOpen={handleBeforeOpen}
/>
);
}onOpenEmitType<OpenCloseMenuEventArgs>interface OpenCloseMenuEventArgs {
element: HTMLElement; // Menu element
event: Event; // Browser event that triggered opening
items: MenuItemModel[]; // Current menu items
}function App() {
const handleOnOpen = (args: OpenCloseMenuEventArgs) => {
console.log('Menu opened at:', new Date());
console.log('Number of items:', args.items?.length);
// Set focus to first menu item for accessibility
setTimeout(() => {
const firstItem = args.element?.querySelector('.e-menu-item');
(firstItem as HTMLElement)?.focus();
}, 0);
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
onOpen={handleOnOpen}
/>
);
}beforeCloseEmitType<BeforeOpenCloseMenuEventArgs>function App() {
const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState(false);
const handleBeforeClose = (args: BeforeOpenCloseMenuEventArgs) => {
if (hasUnsavedChanges) {
const confirmed = window.confirm('Unsaved changes. Close anyway?');
if (!confirmed) {
args.cancel = true; // Prevent menu from closing
}
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeClose={handleBeforeClose}
/>
);
}onCloseEmitType<OpenCloseMenuEventArgs>function App() {
const handleOnClose = (args: OpenCloseMenuEventArgs) => {
console.log('Menu closed');
// Clear temporary selections or states
document.querySelectorAll('.temp-highlight').forEach(el => {
el.classList.remove('temp-highlight');
});
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
onClose={handleOnClose}
/>
);
}selectEmitType<MenuEventArgs>interface MenuEventArgs {
element: HTMLElement; // Menu item element
event: Event; // Click/keyboard event
item?: MenuItemModel; // Selected menu item
items?: MenuItemModel[]; // All menu items
}function App() {
const handleSelect = (args: MenuEventArgs) => {
const itemText = args.item?.text;
switch (itemText) {
case 'Cut':
document.execCommand('cut');
console.log('Cut executed');
break;
case 'Copy':
document.execCommand('copy');
console.log('Copy executed');
break;
case 'Paste':
document.execCommand('paste');
console.log('Paste executed');
break;
case 'Delete':
if (confirm('Confirm delete?')) {
console.log('Delete executed');
}
break;
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
select={handleSelect}
/>
);
}beforeItemRenderEmitType<MenuEventArgs>function App() {
const handleBeforeItemRender = (args: MenuEventArgs) => {
const itemText = args.item?.text;
// Disable delete if user doesn't have permission
if (itemText === 'Delete' && !userPermissions.canDelete) {
args.element?.classList.add('e-disabled');
}
// Highlight recent items
if (itemText?.startsWith('Recent:')) {
args.element?.classList.add('recent-item-highlight');
}
// Add keyboard shortcut indicator
const shortcuts: Record<string, string> = {
'Cut': 'Ctrl+X',
'Copy': 'Ctrl+C',
'Paste': 'Ctrl+V'
};
if (shortcuts[itemText as string]) {
const shortcutEl = document.createElement('span');
shortcutEl.className = 'shortcut-hint';
shortcutEl.textContent = shortcuts[itemText as string];
args.element?.appendChild(shortcutEl);
}
};
return (
<ContextMenuComponent
target="#target"
items={menuItems}
beforeItemRender={handleBeforeItemRender}
/>
);
}createdEmitType<Event>function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const handleCreated = () => {
console.log('ContextMenu created and ready');
// Perform initial setup
menuRef.current?.enableItems(['Advanced Options'], userRole === 'admin');
// Add custom data to items
const items = menuRef.current?.items;
if (items) {
items.forEach((item, index) => {
item.id = `item-${index}`;
});
}
};
return (
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
created={handleCreated}
/>
);
}import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
function App() {
const menuRef = React.useRef<ContextMenuComponent>(null);
const menuItems: MenuItemModel[] = [
{ text: 'Cut', iconCss: 'e-icons e-cut', id: 'cut' },
{ text: 'Copy', iconCss: 'e-icons e-copy', id: 'copy' },
{ text: 'Paste', iconCss: 'e-icons e-paste', id: 'paste' },
{ separator: true },
{
text: 'More',
items: [
{ text: 'Delete' },
{ text: 'Properties' }
]
}
];
const handleSelect = (args: any) => {
console.log('Selected:', args.item?.text);
};
const handleBeforeOpen = (args: any) => {
// Customize menu before opening
};
return (
<div>
<div id="target">Right-click me</div>
<ContextMenuComponent
ref={menuRef}
target="#target"
items={menuItems}
select={handleSelect}
beforeOpen={handleBeforeOpen}
animationSettings={{ effect: 'FadeIn', duration: 300 }}
enableScrolling={true}
/>
</div>
);
}
export default App;// Open menu at coordinates
menuRef.current?.open(100, 150);
// Close menu
menuRef.current?.close();
// Enable/disable items
menuRef.current?.enableItems(['Delete', 'Archive'], false);
// Show/hide items
menuRef.current?.showItems(['Delete']);
menuRef.current?.hideItems(['Export']);
// Add items
menuRef.current?.insertAfter(
[{ text: 'New Option' }],
'Existing Item'
);
// Remove items
menuRef.current?.removeItems(['Outdated Item']);
// Get item index
const indices = menuRef.current?.getItemIndex('Copy');