Menu items of the ContextMenu can be customized according to the requirement. The section explains about how to customize table template in sub menu item.
This can be achieved by appending table layout while li
rendering by using beforeItemRender
event.
import { enableRipple } from '@syncfusion/ej2-base';
import { ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import * as ReactDom from 'react-dom';
enableRipple(true);
class App extends React.Component<{}, {}> {
private menuItems: MenuItemModel[] = [
{
iconCss: 'e-cm-icons e-cut',
text: 'Cut',
},
{
iconCss: 'e-icons e-copy',
text: 'Copy'
},
{
iconCss: 'e-cm-icons e-paste',
text: 'Paste'
},
{
separator: true
},
{
iconCss: 'e-icons e-link',
text: 'Link'
},
{
iconCss: 'e-icons e-table',
items: [
{
id: 'table'
}
],
text: 'Table'
}
];
public createHeader = () => {
const header = document.createElement('h4');
header.textContent = 'Insert Table';
return header;
}
public createTable = () => {
const table = document.createElement('table');
for (let i: number = 0; i < 5; i++) {
const row = document.createElement('tr');
table.appendChild(row);
for (let j: number = 0; j < 6; j++) {
const col = document.createElement('td');
row.appendChild(col);
col.setAttribute('class', 'data');
}
}
return table;
}
public itemBeforeEvent: any = (args: MenuEventArgs) => {
if (args.item.id === 'table') {
args.element.classList.add('bg-transparent');
args.element.appendChild(this.createHeader());
args.element.appendChild(this.createTable());
}
}
public render() {
return (
<div className="container">
<div id='target'>Right click / Touch hold to open the ContextMenu</div>
<ContextMenuComponent id='contextmenu' target='#target'
items={this.menuItems} beforeItemRender={this.itemBeforeEvent}/>
</div>
);
}
}
ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React ContextMenu</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
</head>
<style>
.e-contextmenu-wrapper ul .e-menu-item.bg-transparent {
background-color: transparent;
line-height: normal;
height: auto;
}
h4 {
text-align: center;
margin-top: 5px;
margin-bottom: 5px;
}
</style>
<body>
<div id='element'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
#target {
border: 1px dashed;
height: 150px;
padding: 10px;
position: relative;
text-align: justify;
color: gray;
user-select: none;
}
.data {
border: 1px solid #212121;
padding: 8px;
}
@font-face {
font-family: 'ddb-icons';
src:
url(data:application/x-font-ttf;charset=utf-8;base64,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) format('truetype');
font-weight: normal;
font-style: normal;
}
.e-cm-icons {
font-family: 'ddb-icons' !important;
speak: none;
font-size: 55px;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.e-cut::before {
content: '\e700';
}
.e-copy::before {
content: '\e70a';
}
.e-paste::before {
content: '\e701';
}
.e-link::before {
content: '\e290';
}
.e-table::before {
content: '\e705';
}
import { enableRipple } from '@syncfusion/ej2-base';
import { ContextMenuComponent } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import * as ReactDom from 'react-dom';
enableRipple(true);
class App extends React.Component {
constructor() {
super(...arguments);
this.menuItems = [
{
iconCss: 'e-cm-icons e-cut',
text: 'Cut',
},
{
iconCss: 'e-icons e-copy',
text: 'Copy'
},
{
iconCss: 'e-cm-icons e-paste',
text: 'Paste'
},
{
separator: true
},
{
iconCss: 'e-icons e-link',
text: 'Link'
},
{
iconCss: 'e-icons e-table',
items: [
{
id: 'table'
}
],
text: 'Table'
}
];
this.createHeader = () => {
const header = document.createElement('h4');
header.textContent = 'Insert Table';
return header;
};
this.createTable = () => {
const table = document.createElement('table');
for (let i = 0; i < 5; i++) {
const row = document.createElement('tr');
table.appendChild(row);
for (let j = 0; j < 6; j++) {
const col = document.createElement('td');
row.appendChild(col);
col.setAttribute('class', 'data');
}
}
return table;
};
this.itemBeforeEvent = (args) => {
if (args.item.id === 'table') {
args.element.classList.add('bg-transparent');
args.element.appendChild(this.createHeader());
args.element.appendChild(this.createTable());
}
};
}
render() {
return (<div className="container">
<div id='target'>Right click / Touch hold to open the ContextMenu</div>
<ContextMenuComponent id='contextmenu' target='#target' items={this.menuItems} beforeItemRender={this.itemBeforeEvent}/>
</div>);
}
}
ReactDom.render(<App />, document.getElementById('element'));
UI components can also be placed inside the each li
element of ContextMenu.
In the following example, CheckBox component is placed inside each li
element and this can be achieved by creating
CheckBox component in beforeItemRender
event and appending it into the li
element.
import { closest, createElement, enableRipple} from '@syncfusion/ej2-base';
import { createCheckBox } from '@syncfusion/ej2-buttons';
import { BeforeOpenCloseMenuEventArgs, ContextMenuComponent, MenuEventArgs, MenuItemModel } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import * as ReactDom from 'react-dom';
enableRipple(true);
class App extends React.Component<{}, {}> {
public menuItems: MenuItemModel[] = [
{ text: 'Option 1' },
{ text: 'Option 2' },
{ text: 'Option 3' }
];
public itemBeforeEvent: any = (args: MenuEventArgs) => {
const check = createCheckBox(createElement, false, {
checked: (args.item.text === 'Option 1' || args.item.text === 'Option 2') ? true : false,
label: args.item.text
});
args.element.innerHTML = '';
args.element.appendChild(check);
}
public beforeClose: any = (args: BeforeOpenCloseMenuEventArgs) => {
if (closest((args.event.target as HTMLElement), '.e-menu-item')) {
args.cancel = true;
const selectedElem = args.element.querySelectorAll('.e-selected');
for (const elem of selectedElem as any) {
const ele = elem as HTMLElement;
ele.classList.remove('e-selected');
}
const checkbox = closest(args.event.target as Element, '.e-checkbox-wrapper') as HTMLElement;
const frame = checkbox.querySelector('.e-frame');
if (checkbox && frame.classList.contains('e-check')) {
frame.classList.remove('e-check');
} else if (checkbox) {
frame.classList.add('e-check');
}
}
}
public render() {
return (
<div className="container">
<div id='target'>Right click / Touch hold to open the ContextMenu</div>
<ContextMenuComponent id='contextmenu' target='#target' items={this.menuItems} beforeItemRender={this.itemBeforeEvent} beforeClose={this.beforeClose} />
</div>
);
}
}
ReactDom.render(<App />,document.getElementById('element'));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Syncfusion React ContextMenu</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Essential JS 2 for React Components" />
<meta name="author" content="Syncfusion" />
<link href="//cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel="stylesheet" />
<link href="//cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel="stylesheet" />
<link href="index.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js"></script>
<script src="systemjs.config.js"></script>
</head>
<body>
<div id='element'>
<div id='loader'>Loading....</div>
</div>
</body>
</html>
#loader {
color: #008cff;
height: 40px;
left: 45%;
position: absolute;
top: 45%;
width: 30%;
}
.e-link::before {
content: '\e7e8';
}
#target {
border: 1px dashed;
height: 150px;
padding: 10px;
position: relative;
text-align: justify;
color: gray;
user-select: none;
}
import { closest, createElement, enableRipple } from '@syncfusion/ej2-base';
import { createCheckBox } from '@syncfusion/ej2-buttons';
import { ContextMenuComponent } from '@syncfusion/ej2-react-navigations';
import * as React from 'react';
import * as ReactDom from 'react-dom';
enableRipple(true);
class App extends React.Component {
constructor() {
super(...arguments);
this.menuItems = [
{ text: 'Option 1' },
{ text: 'Option 2' },
{ text: 'Option 3' }
];
this.itemBeforeEvent = (args) => {
const check = createCheckBox(createElement, false, {
checked: (args.item.text === 'Option 1' || args.item.text === 'Option 2') ? true : false,
label: args.item.text
});
args.element.innerHTML = '';
args.element.appendChild(check);
};
this.beforeClose = (args) => {
if (closest(args.event.target, '.e-menu-item')) {
args.cancel = true;
const selectedElem = args.element.querySelectorAll('.e-selected');
for (const elem of selectedElem) {
const ele = elem;
ele.classList.remove('e-selected');
}
const checkbox = closest(args.event.target, '.e-checkbox-wrapper');
const frame = checkbox.querySelector('.e-frame');
if (checkbox && frame.classList.contains('e-check')) {
frame.classList.remove('e-check');
}
else if (checkbox) {
frame.classList.add('e-check');
}
}
};
}
render() {
return (<div className="container">
<div id='target'>Right click / Touch hold to open the ContextMenu</div>
<ContextMenuComponent id='contextmenu' target='#target' items={this.menuItems} beforeItemRender={this.itemBeforeEvent} beforeClose={this.beforeClose}/>
</div>);
}
}
ReactDom.render(<App />, document.getElementById('element'));