Context menu in EJ2 TypeScript Diagram control
15 Dec 202424 minutes to read
In a graphical user interface (GUI), a context menu is a type of menu that appears when you perform a right-click operation. It offers users a set of actions relevant to the current context. In diagrams, context menus can be customized extensively. The Diagram control provides built-in context menu items while also allowing users to define custom menu items through the contextMenuSettings
property. This flexibility enables tailoring menus to specific application needs, including creating nested levels of menu items for more intricate user interactions.
NOTE
If you want to use contextMenu in diagram, you need to inject
DiagramContextMenu
Module in the diagram.
Default context menu
Diagram provides some default context menu items to ease the execution of some frequently used commands. The show
property helps you to enable/disable the context menu. The following code illustrates how to enable the default context menu items.
import {
Diagram,
DiagramContextMenu,
ConnectorModel,
NodeModel,
} from '@syncfusion/ej2-diagrams';
Diagram.Inject(DiagramContextMenu);
//Initializes the connector
let connector: ConnectorModel = {
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
type: 'Straight',
style: {
strokeColor: '#6BA5D7',
fill: '#6BA5D7',
strokeWidth: 2,
},
targetDecorator: {
style: {
fill: '#6BA5D7',
strokeColor: '#6BA5D7',
},
},
};
//Initializes the nodes
let node: NodeModel = {
id: 'node1',
width: 100,
height: 100,
offsetX: 100,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [{ content: 'Rectangle1' }],
};
let node2: NodeModel = {
id: 'node2',
width: 100,
height: 100,
offsetX: 300,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [{ content: 'Rectangle2' }],
};
//Initializes the Diagram component
let diagram: Diagram = new Diagram(
{
width: '100%',
height: '350px',
nodes: [node, node2],
connectors: [connector],
//Enables the context menu
contextMenuSettings: {
show: true,
},
},
'#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Diagram</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Typescript UI Controls" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-diagrams/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-navigations/styles/fabric.css" rel="stylesheet" />
<script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='loader'>Loading....</div>
<div id='container'>
<div id='element'></div>
</div>
</body>
</html>
Custom context menu
Context menus can be customized for individual nodes by defining specific menu items beyond the default options. To add additional context menu items, you need to define and incorporate them into the items
property of the context menu.
Each custom item can be defined with specific text and ID using the text
and ID
properties, respectively. Additionally, you can enhance visual cues by associating icons through the iconCss
for enabling the use of font icons. The target
property specifies where each menu item should appear, and separators can be included using the separator
property to visually group menu items. This flexibility allows for a tailored user interface that meets specific application needs efficiently. Nested menu items are defined within the items
property of a parent menu item.
To Display custom menu alone
To display the custom context menu items alone, set the showCustomMenuOnly
property to true.
Context menu click
Upon clicking custom menu items, actions are handled using the contextMenuClick
event in the diagram. This event allows you to define actions based on which menu item is clicked. For instance, in the example below, the cloning of nodes and the change of fill color for nodes and annotations are efficiently managed and implemented through this event.
import {
Diagram,
DiagramContextMenu,
NodeModel,
} from '@syncfusion/ej2-diagrams';
import { MenuEventArgs } from '@syncfusion/ej2-navigations';
Diagram.Inject(DiagramContextMenu);
//Initializes the nodes
let node: NodeModel = {
id: 'node1',
width: 100,
height: 100,
offsetX: 100,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [
{
id: 'label1',
content: 'Rectangle1',
style: {
color: 'white',
},
},
],
};
let node2: NodeModel = {
id: 'node2',
width: 100,
height: 100,
offsetX: 300,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [
{
id: 'label2',
content: 'Rectangle2',
style: {
color: 'white',
},
},
],
};
//Initializes the Diagram component
let diagram: Diagram = new Diagram(
{
width: '100%',
height: '350px',
nodes: [node, node2],
contextMenuSettings: {
//Enables the context menu
show: true,
// Defines the custom context menu items
items: [
{
// Text to be displayed
text: 'Fill',
items: [
{ id: 'red', text: 'Red' },
{ id: 'yellow', text: 'Yellow' },
{ id: 'green', text: 'Green' },
{ id: 'blue', text: 'Blue' },
],
//Sets the id for the item
id: 'fill',
target: '.e-elementcontent',
// Sets the css icons for the item
iconCss: 'e-icons e-paint-bucket',
},
{
text: 'Annotation color',
id: 'annotationColor',
items: [
{ id: 'pink', text: 'Pink' },
{ id: 'orange', text: 'Orange' },
{ id: 'violet', text: 'Violet' },
{ id: 'brown', text: 'Brown' },
],
target: '.e-elementcontent',
iconCss: 'e-icons e-font-color',
},
{
text: 'Clone',
id: 'clone',
target: '.e-elementcontent',
iconCss: 'e-icons e-copy',
},
],
// Hides the default context menu items
showCustomMenuOnly: true,
},
contextMenuClick: function (args: MenuEventArgs) {
let selectedNode = diagram.selectedItems.nodes[0];
if (
selectedNode &&
args.item.id !== 'fill' &&
args.item.id !== 'annotationColor'
) {
if (
args.item.text === 'Red' ||
args.item.text === 'Blue' ||
args.item.text === 'Yellow' ||
args.item.text === 'Green'
) {
selectedNode.style.fill = args.item.text;
diagram.dataBind();
} else if (
args.item.text === 'Pink' ||
args.item.text === 'Violet' ||
args.item.text === 'Orange' ||
args.item.text === 'Brown'
) {
selectedNode.annotations[0].style.fill = args.item.text;
diagram.dataBind();
} else {
diagram.copy();
diagram.paste();
}
}
},
},
'#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Diagram</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Typescript UI Controls" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-diagrams/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-navigations/styles/fabric.css" rel="stylesheet" />
<script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='loader'>Loading....</div>
<div id='container'>
<div id='element'></div>
</div>
</body>
</html>
Context menu open
In certain situations, you may want to hide specific menu items based on the selected elements in the diagram. This can be achieved using the contextMenuOpen
event. When the context menu is opened via right-click, the contextMenuOpen
event is triggered. Within this event, you can create an array of menu items to hide for the selected element and pass it to the hiddenItems
property of the contextMenuOpen event argument. The following example demonstrates how to display different custom menu items for nodes, connectors, and the diagram based on the selection.
import {
Diagram,
DiagramContextMenu,
ConnectorModel,
NodeModel,
DiagramBeforeMenuOpenEventArgs,
} from '@syncfusion/ej2-diagrams';
import { MenuEventArgs } from '@syncfusion/ej2-navigations';
Diagram.Inject(DiagramContextMenu);
let connectors: ConnectorModel[] = [
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
},
];
//Initializes the nodes
let node: NodeModel = {
id: 'node1',
width: 100,
height: 100,
offsetX: 100,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'black',
strokeWidth: 2,
},
annotations: [
{
id: 'label1',
content: 'Rectangle1',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
let node2: NodeModel = {
id: 'node2',
width: 100,
height: 100,
offsetX: 300,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'black',
strokeWidth: 2,
},
annotations: [
{
id: 'label2',
content: 'Rectangle2',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
//Initializes the Diagram component
let diagram: Diagram = new Diagram(
{
width: '100%',
height: '350px',
nodes: [node, node2],
connectors: connectors,
contextMenuSettings: {
//Enables the context menu
show: true,
items: [
{
text: 'Change fill',
id: 'applyFill',
target: '.e-diagramcontent',
iconCss: 'e-icons e-paint-bucket',
},
{
text: 'Change stroke',
id: 'applyStroke',
target: '.e-diagramcontent',
iconCss: 'e-icons e-edit',
},
{
text: 'Select All',
id: 'selectAll',
target: '.e-diagramcontent',
},
],
// Hides the default context menu items
showCustomMenuOnly: true,
},
contextMenuOpen: function (args: DiagramBeforeMenuOpenEventArgs) {
let hiddenItems = [];
let selectedNode = diagram.selectedItems.nodes[0];
let selectedConnector = diagram.selectedItems.connectors[0];
if (selectedNode || selectedConnector) {
hiddenItems.push('selectAll');
} else {
hiddenItems = ['applyFill', 'applyStroke'];
}
args.hiddenItems = hiddenItems;
},
contextMenuClick: function (args: MenuEventArgs) {
let selectedNode = diagram.selectedItems.nodes[0];
let selectedConnector = diagram.selectedItems.connectors[0];
if (selectedNode || selectedConnector) {
if (selectedNode) {
if (args.item.id === 'applyFill') {
selectedNode.style.fill =
selectedNode.style.fill === '#6BA5D7' ? 'green' : '#6BA5D7';
} else if (args.item.id === 'applyStroke') {
selectedNode.style.strokeColor =
selectedNode.style.strokeColor === 'black' ? 'yellow' : 'black';
}
}
if (selectedConnector) {
if (args.item.id === 'applyFill') {
selectedConnector.targetDecorator.style.fill =
selectedConnector.targetDecorator.style.fill === 'yellow'
? 'black'
: 'yellow';
}
selectedConnector.style.strokeColor =
selectedConnector.style.strokeColor === 'black'
? 'yellow'
: 'black';
}
} else {
diagram.selectAll();
}
},
},
'#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Diagram</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Typescript UI Controls" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-diagrams/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-navigations/styles/fabric.css" rel="stylesheet" />
<script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='loader'>Loading....</div>
<div id='container'>
<div id='element'></div>
</div>
</body>
</html>
Context menu with Url
url
property of the menu item is used to set the url of any website which will be opened upon clicking on them. The following example shows the context menu with url for three websites.
import { Diagram, DiagramContextMenu, NodeModel } from '@syncfusion/ej2-diagrams';
Diagram.Inject(DiagramContextMenu);
const nodes: NodeModel[] = [
{
id: 'node1', width: 100, height: 100, offsetX: 100, offsetY: 100,
style: { fill: '#6BA5D7', strokeColor: 'white', strokeWidth: 1 },
annotations: [{
id: 'label1', content: 'Rectangle1', offset: { x: 0.5, y: 0.5 },
style: { color: 'white' }
}]
},
{
id: 'node2', width: 100, height: 100, offsetX: 300, offsetY: 100,
style: { fill: '#6BA5D7', strokeColor: 'white', strokeWidth: 1 },
annotations: [{
id: 'label2', content: 'Rectangle2',
offset: { x: 0.5, y: 0.5 },
style: { color: 'white' }
}]
},
];
const menuItems = [
{ text: 'Google.com', id: 'google', url: 'https://www.google.co.in/' },
{ text: 'w3schools.com', id: 'W3Schools', url: 'https://www.w3schools.com/' },
{ text: 'stackoverflow.com', id: 'stackoverflow', url: 'https://stackoverflow.com/' },
];
new Diagram({
width: '100%',
height: '350px',
nodes,
contextMenuSettings: {
show: true,
items: menuItems.map(item => ({ ...item, target: '.e-diagramcontent' })),
showCustomMenuOnly: true,
},
}, '#element');
Template Support for Context menu
Diagram provides template support for the context menu. The template for the context menu items can be customized before rendering by using the contextMenuBeforeItemRender
event, which triggers while rendering each menu item.
In the following example, menu items are rendered with shortcut key codes for specific actions in the context menu using a template. The key codes for cut, copy, and paste actions are displayed at the right corner of the menu items by adding a span element in the contextMenuBeforeItemRender
event.
import {
Diagram,
DiagramContextMenu,
ConnectorModel,
NodeModel,
} from '@syncfusion/ej2-diagrams';
import { MenuEventArgs } from '@syncfusion/ej2-navigations';
import { createElement } from '@syncfusion/ej2-base';
Diagram.Inject(DiagramContextMenu);
//Initializes the nodes
let node: NodeModel = {
id: 'node1',
width: 100,
height: 100,
offsetX: 100,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [
{
id: 'label1',
content: 'Rectangle1',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
let node2: NodeModel = {
id: 'node2',
width: 100,
height: 100,
offsetX: 300,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'white',
strokeWidth: 1,
},
annotations: [
{
id: 'label2',
content: 'Rectangle2',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
//Initializes the Diagram component
let diagram: Diagram = new Diagram(
{
width: '100%',
height: '350px',
nodes: [node, node2],
contextMenuSettings: {
//Enables the context menu
show: true,
items: [
{
text: 'Cut',
id: 'Cut',
target: '.e-diagramcontent',
iconCss: 'e-cut e-icons',
},
{
text: 'Copy',
id: 'Copy',
target: '.e-diagramcontent',
iconCss: 'e-icons e-copy',
},
{
text: 'Paste',
id: 'Paste',
target: '.e-diagramcontent',
iconCss: 'e-icons e-paste',
},
],
// Hides the default context menu items
showCustomMenuOnly: true,
},
contextMenuBeforeItemRender: (args: MenuEventArgs) => {
// To render template in li.
let shortCutSpan: HTMLElement = createElement('span');
let text: string = args.item.text;
let shortCutText: string =
text === 'Cut' ? 'Ctrl + X' : text === 'Copy' ? 'Ctrl + C' : 'Ctrl + V';
shortCutSpan.textContent = shortCutText;
//Added CSS styles for the class shortcut to customize its position and appearance
shortCutSpan.setAttribute('class', 'shortcut');
args.element.appendChild(shortCutSpan);
},
contextMenuClick: function (args: MenuEventArgs) {
let selectedNode = diagram.selectedItems.nodes[0];
if (selectedNode) {
if (args.item.text === 'Cut') {
diagram.cut();
} else if (args.item.text === 'Copy') {
diagram.copy();
}
}
if (args.item.text === 'Paste') {
diagram.paste();
}
},
},
'#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Diagram</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Typescript UI Controls" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-diagrams/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-navigations/styles/fabric.css" rel="stylesheet" />
<script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<style>
.shortcut {
float: right;
color: #999999;
font-size: 12px;
margin-left: 20px;
}
</style>
<body>
<div id='loader'>Loading....</div>
<div id='container'>
<div id='element'></div>
</div>
</body>
</html>
Context menu events
Event | Description |
---|---|
contextMenuBeforeItemRender |
Triggers while initializing each menu item. |
contextMenuOpen |
Triggers upon right-click before opening the context menu. |
contextMenuClick |
Triggers when a menu item is clicked. |
The following example shows how to get these events.
import {
Diagram,
DiagramContextMenu,
ConnectorModel,
NodeModel,
DiagramBeforeMenuOpenEventArgs,
} from '@syncfusion/ej2-diagrams';
import { MenuEventArgs } from '@syncfusion/ej2-navigations';
Diagram.Inject(DiagramContextMenu);
let connectors: ConnectorModel[] = [
{
id: 'connector1',
sourceID: 'node1',
targetID: 'node2',
},
];
//Initializes the nodes
let node: NodeModel = {
id: 'node1',
width: 100,
height: 100,
offsetX: 100,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'black',
strokeWidth: 2,
},
annotations: [
{
id: 'label1',
content: 'Rectangle1',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
let node2: NodeModel = {
id: 'node2',
width: 100,
height: 100,
offsetX: 300,
offsetY: 100,
style: {
fill: '#6BA5D7',
strokeColor: 'black',
strokeWidth: 2,
},
annotations: [
{
id: 'label2',
content: 'Rectangle2',
offset: {
x: 0.5,
y: 0.5,
},
style: {
color: 'white',
},
},
],
};
//Initializes the Diagram component
let diagram: Diagram = new Diagram(
{
width: '100%',
height: '350px',
nodes: [node, node2],
connectors: connectors,
contextMenuSettings: {
//Enables the context menu
show: true,
items: [
{
text: 'menu item 1',
id: 'item1',
},
{
text: 'menu item 2',
id: 'item2',
},
],
// Hides the default context menu items
showCustomMenuOnly: true,
},
contextMenuBeforeItemRender: (args: MenuEventArgs) => {
//Triggers for each menu item
console.log('context menu before item render');
},
contextMenuOpen: function (args: DiagramBeforeMenuOpenEventArgs) {
//Triggers when the menu is openned
console.log('context menu openned');
},
contextMenuClick: function (args: MenuEventArgs) {
//Triggers when the item is clicked
console.log('context menu clicked');
},
},
'#element'
);
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Diagram</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Typescript UI Controls" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-base/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-splitbuttons/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-diagrams/styles/material.css" rel="stylesheet" />
<link href="https://cdn.syncfusion.com/ej2/28.1.33/ej2-navigations/styles/fabric.css" rel="stylesheet" />
<script src="systemjs.config.js"></script>
<script src="https://cdn.syncfusion.com/ej2/syncfusion-helper.js" type ="text/javascript"></script>
</head>
<body>
<div id='loader'>Loading....</div>
<div id='container'>
<div id='element'></div>
</div>
</body>
</html>