Skip to content

Row Context Menu

This example demonstrates how to create a custom row context menu with interactive actions. The menu appears when right-clicking on a row header and provides menu items with actions.

Key * Features:

  • Custom row context menu implementation
  • Actions with visual indicators
  • Styled menu buttons with hover effects


src/components/row-header/rowHeader.ts
import { defineCustomElements } from '@revolist/revogrid/loader';
import '@fortawesome/fontawesome-free/css/all.min.css';
defineCustomElements();
import { currentTheme, useRandomData } from '../composables/useRandomData';
import { rowHeaders, ContextMenuPlugin, RowHeaderPlugin, ColumnStretchPlugin } from '@revolist/revogrid-pro';
import type { ContextMenuConfig } from '@/release/plugins/context-menu/context-menu.types';
const { createRandomData } = useRandomData();
const { isDark } = currentTheme();
// Buffer to store copied/cut row data
let rowBuffer: any = null;
const grid = document.querySelector('revo-grid');
if (grid) {
grid.source = createRandomData(100);
// Define columns
grid.columns = [
{
name: '🆔 ID',
prop: 'id',
size: 70,
},
{
name: '🍎 Fruit',
prop: 'name',
},
{
name: '💰 Price',
prop: 'price',
},
];
// Define plugin
grid.plugins = [RowHeaderPlugin, ContextMenuPlugin, ColumnStretchPlugin];
// grid.rowHeaders = rowHeaders({ showHeaderFocusBtn: true });
const rowContextMenu: ContextMenuConfig = {
items: [
{
icon: 'fa-solid fa-copy',
name: 'Copy row',
action: (_, cell) => {
if (!cell) return;
rowBuffer = { ...grid.source[cell.y] };
},
},
{
icon: 'fa-solid fa-cut',
name: 'Cut row',
action: (_, cell) => {
if (!cell) return;
rowBuffer = { ...grid.source[cell.y] };
grid.source.splice(cell.y, 1);
grid.source = [...grid.source];
},
},
{
icon: 'fa-solid fa-paste',
name: 'Paste row',
hidden: () => !rowBuffer,
action: (_, cell) => {
if (!cell || !rowBuffer) return;
const newRow = { ...rowBuffer };
grid.source.splice(cell.y + 1, 0, newRow);
grid.source = [...grid.source];
},
},
{
icon: 'fa-solid fa-arrow-up',
name: 'Add row above',
action: (_, cell) => {
if (!cell) {
return;
}
grid.source.splice(cell.y, 0, {
id: 0,
name: 'New row',
price: 0,
});
grid.source = [...grid.source];
},
},
{
icon: 'fa-solid fa-arrow-down',
name: 'Add row below',
action: (_, cell) => {
if (!cell) {
return;
}
grid.source.splice(cell.y + 1, 0, {
id: 0,
name: 'New row',
price: 0,
});
grid.source = [...grid.source];
},
},
{
icon: 'fa-solid fa-trash',
name: (focused, range) => {
if (!focused) {
return '';
}
if (!range) {
range = {
x: 0,
y: focused.y,
x1: 0,
y1: focused.y,
};
}
const rows = range.y1 - range.y + 1;
if (!range || rows < 2) {
return 'Delete row';
}
return `Delete ${rows} rows`;
},
action: (_, focused, range) => {
if (!focused) {
return;
}
if (!range) {
range = {
x: 0,
y: focused.y,
x1: 0,
y1: focused.y,
};
}
const rows = range.y1 - range.y + 1;
grid.source.splice(range.y, rows);
grid.source = [...grid.source];
},
}
]
};
grid.additionalData = {
// Define context menu
rowContextMenu,
stretch: 'all'
};
// Set theme
grid.theme = isDark() ? 'darkCompact' : 'compact';
}