Context menu is only available in PRO version
ReactGrid comes with a custom context menu feature. Custom menu entries can be easily created thus
allowing for personalized experiences.
By default, the context menu is disabled. You can enable it by providing a handler function
to your ReactGrid using the onContextMenu
property.
Enabling simple context menu
This guide is based on handling data changes.
- Update imports by adding
Id
,SelectionMode
andMenuOption
interfaces
import { ReactGrid, CellChange, Row, Column, Id, MenuOption, SelectionMode } from "@silevis/reactgrid";
- Implement the context menu handler function
The menuOptions
argument of simpleHandleContextMenu
contains a list of default menu entries
provided by ReactGrid - copy, cut and paste.
For now, we'll simply define it, which will enable the default ReactGrid context menu.
const simpleHandleContextMenu = (
selectedRowIds: Id[],
selectedColIds: Id[],
selectionMode: SelectionMode,
menuOptions: MenuOption[]
): MenuOption[] => {
return menuOptions;
};
return (
<ReactGrid
rows={rows}
columns={columns}
onCellsChanged={handleChanges}
onContextMenu={simpleHandleContextMenu} // highlight-line
/>
);
Here's the result. Right-click anywhere in the grid to see the context menu.
Code
const SimpleContextMenuHandlingSample = () => { const [columns] = React.useState<Column[]>(() => [ { columnId: "Name", width: 100 }, { columnId: "Surname", width: 100 } ]); const [rows, setRows] = React.useState<Row[]>(() => [ { rowId: 0, cells: [ { type: "header", text: "Name" }, { type: "header", text: "Surname" } ] }, { rowId: 1, cells: [ { type: "text", text: "Thomas" }, { type: "text", text: "Goldman" } ] }, { rowId: 2, cells: [ { type: "text", text: "Susie" }, { type: "text", text: "Spencer" } ] }, { rowId: 3, cells: [ { type: "text", text: "" }, { type: "text", text: "" } ] } ]); const handleChanges = (changes: CellChange[]) => { setRows((prevRows) => { changes.forEach((change) => { const changeRowIdx = prevRows.findIndex( (el) => el.rowId === change.rowId ); const changeColumnIdx = columns.findIndex( (el) => el.columnId === change.columnId ); prevRows[changeRowIdx].cells[changeColumnIdx] = change.newCell; }); return [...prevRows]; }); }; const simpleHandleContextMenu = ( selectedRowIds: Id[], selectedColIds: Id[], selectionMode: SelectionMode, menuOptions: MenuOption[] ): MenuOption[] => { return menuOptions; }; return ( <ReactGrid rows={rows} columns={columns} onCellsChanged={handleChanges} onContextMenu={simpleHandleContextMenu} /> ); } render(<SimpleContextMenuHandlingSample/>)
Preview
Advanced example - removing columns and rows
The default menu, though pretty, may seem a bit lacklustre when it comes to the functionality it provides. Let's try and extend it a bit - we'll make it possible to remove columns and rows using the context menu.
- Implement a more advanced context menu handler function
const handleContextMenu = (
selectedRowIds: Id[],
selectedColIds: Id[],
selectionMode: SelectionMode,
menuOptions: MenuOption[]
): MenuOption[] => {
if (selectionMode === "row") {
menuOptions = [
...menuOptions,
{
id: "removeRow",
label: "Remove row",
handler: () => setRows(rows.filter(row => !selectedRowIds.includes(row.rowId)))
}
];
}
if (selectionMode === "column") {
menuOptions = [
...menuOptions,
{
id: "removeColumn",
label: "Remove column",
handler: () => {
const cols = columns.filter(column => !selectedColIds.includes(column.columnId));
const columnsIdxs = columns
.map((column, idx) => {
if (!cols.includes(column)) return idx;
return undefined;
})
.filter(idx => idx !== undefined);
setRows(rows.map(row => ({
...row,
cells: row.cells.filter((_, idx) => !columnsIdxs.includes(idx))
})));
setColumns(cols);
}
}
];
}
return menuOptions;
};
- Update the
ReactGrid
component's properties. Note that columns and rows must be selectable for our feature to work. After all, we will be removing the currently selected row or column.
return (
<ReactGrid
rows={rows}
columns={columns}
onCellsChanged={handleChanges} // highlight-line
onContextMenu={handleContextMenu} // highlight-line
enableColumnSelection // highlight-line
enableRowSelection // highlight-line
/>
)
Live demo
You can see the effect of the changes we've just introduced in the demo below. To test the new feature, simply select a column or a row by clicking a header cell or one of the leftmost cells, then right-click anywhere and use the new menu entry.
Code
const AdvancedContextMenuHandlingSample = () => { const [columns, setColumns] = React.useState<Column[]>(() => [ { columnId: "Name", width: 100 }, { columnId: "Surname", width: 100 } ]); const [rows, setRows] = React.useState<Row[]>(() => [ { rowId: 0, cells: [ { type: "header", text: "Name" }, { type: "header", text: "Surname" } ] }, { rowId: 1, cells: [ { type: "text", text: "Thomas" }, { type: "text", text: "Goldman" } ] }, { rowId: 2, cells: [ { type: "text", text: "Susie" }, { type: "text", text: "Spencer" } ] }, { rowId: 3, cells: [ { type: "text", text: "" }, { type: "text", text: "" } ] } ]); const handleChanges = (changes: CellChange[]) => { setRows((prevRows) => { changes.forEach((change) => { const changeRowIdx = prevRows.findIndex( (el) => el.rowId === change.rowId ); const changeColumnIdx = columns.findIndex( (el) => el.columnId === change.columnId ); prevRows[changeRowIdx].cells[changeColumnIdx] = change.newCell; }); return [...prevRows]; }); }; const handleContextMenu = ( selectedRowIds: Id[], selectedColIds: Id[], selectionMode: SelectionMode, menuOptions: MenuOption[] ): MenuOption[] => { if (selectionMode === "row") { menuOptions = [ ...menuOptions, { id: "removeRow", label: "Remove row", handler: () => setRows(rows.filter(row => !selectedRowIds.includes(row.rowId))) } ]; } if (selectionMode === "column") { menuOptions = [ ...menuOptions, { id: "removeColumn", label: "Remove column", handler: () => { const cols = columns.filter(column => !selectedColIds.includes(column.columnId)); const columnsIdxs = columns .map((column, idx) => { if (!cols.includes(column)) return idx; return undefined; }) .filter(idx => idx !== undefined); setRows(rows.map(row => ({ ...row, cells: row.cells.filter((_, idx) => !columnsIdxs.includes(idx)) }))); setColumns(cols); } } ]; } return menuOptions; }; return ( <ReactGrid rows={rows} columns={columns} onCellsChanged={handleChanges} onContextMenu={handleContextMenu} enableFillHandle enableRangeSelection enableColumnSelection enableRowSelection /> ); } render(<AdvancedContextMenuHandlingSample/>)
Preview