Skip to content

Data Grid Formatting

DataGridContextMenuPlugin provides a complete, task-oriented menu without requiring every grid to define copy, edit, inspection, row, filter, formatting, sizing, grouping, column-management, and export actions from scratch. It auto-installs ContextMenuPlugin, ColumnDialogPlugin, Column Hide support, the cell inspector, and the spreadsheet formatting runtime. Add AutoFillPlugin and EventManagerPlugin explicitly when dragging a range handle should also stretch advanced formats such as Rating together with the values.

Open the four-framework standalone example or browse its source in revogrid-demos/pro-data-grid-context-menu.

import { DataGridContextMenuPlugin } from '@revolist/revogrid-pro';
grid.plugins = [DataGridContextMenuPlugin];

Installing the plugin enables its defaults. Set the property to false to keep the plugin registered while suppressing the preset:

grid.dataGridContextMenu = false;

The preset is a prepended context-menu contribution. Existing rowContextMenu, columnContextMenu, and independent plugin contributions remain after it by default.

Hide or disable one generated command without rebuilding its parent submenu:

grid.dataGridContextMenu = {
hiddenItems: {
'row.delete': true,
},
disabledItems: {
'row.duplicate': context => context.rows.length > 20,
},
};

Resolvers receive a semantic DataGridContextMenuContext with the active surface, target rows, range, leaf columns, column group, and low-level menu context.

The surfaces are cell, rowHeader, rowGroup, columnHeader, and columnGroupHeader. Disable a whole surface with areas:

grid.dataGridContextMenu = {
areas: { rowGroup: false },
};

Use items for a simple extension. Use getItems when a surface needs complete replacement or transformation.

grid.dataGridContextMenu = {
items: context => [{
id: 'app.audit',
name: 'Open audit history',
action: () => openAuditHistory(context.rows[0]?.model),
}],
getItems: (context, defaults) =>
context.surface === 'columnGroupHeader'
? [{ id: 'app.groupSummary', name: 'Open group summary' }]
: defaults,
};

Set includeHostItems: false to suppress the application-provided base menu on enabled preset surfaces. Other plugin contributions still compose independently.

Every built-in command first emits a cancelable datagrid-context-menu-command event:

grid.addEventListener('datagrid-context-menu-command', event => {
if (event.detail.id === 'row.delete' && usesRemoteRows) {
event.preventDefault();
void deleteRowsOnServer(event.detail.context.rows);
}
});

A commandHandlers entry also replaces local behavior:

grid.dataGridContextMenu = {
commandHandlers: {
'row.delete': context => api.deleteRows(
context.rows.map(row => row.model.id),
),
},
};

Local row mutation is hidden for remote/server-grouped runtimes until a handler is provided. Clipboard permission and export failures emit datagrid-context-menu-error; Cut clears cells only after the clipboard write succeeds.

Add HistoryPlugin to make built-in mutating context-menu commands participate in the same chronological undo/redo stack as direct edits and row ordering:

import {
DataGridContextMenuPlugin,
HistoryPlugin,
} from '@revolist/revogrid-pro';
grid.plugins = [HistoryPlugin, DataGridContextMenuPlugin];

Each command is one atomic history step, including multi-cell Cut, Paste, cell Clear, and row Clear. Structural tracking covers row insert, duplicate, delete, pin, and unpin commands, plus column insert, duplicate, delete, pin, unpin, and move commands. Undo and redo restore the complete affected row sources or authored column tree.

Copy and Copy with headers are intentionally excluded because they do not mutate grid state. Canceled, unavailable, and no-op commands create no entry. Commands replaced through commandHandlers are application-owned and are not snapshotted automatically, so remote side effects remain under application control.

In-memory history preserves row objects and function-valued column definitions, including nested groups, renderers, and editors. Persistent History adapters require serializable row and column definitions; JSON storage cannot recreate functions. See History Undo/Redo for stack, persistence, and replay details.

Commands appear only when their runtime exists. Add the matching plugin to expose filtering, Excel export, row selection, column auto-size, column collapse, tree, or server-grouping actions. Set unavailableItems: 'disable' to show unavailable commands in a disabled state instead.

grid.dataGridContextMenu = {
unavailableItems: 'disable',
rowPinning: true,
};

Row pinning is opt-in. Column insertion, duplication, and deletion are also opt-in and require an application factory so every new column receives a unique schema key:

grid.dataGridContextMenu = {
columnSchema: {
createColumn({ action, sourceColumn }) {
return {
...sourceColumn,
prop: `${String(sourceColumn.prop)}-${action}-${crypto.randomUUID()}`,
};
},
},
};

Cell and column-header menus include a type-aware Format submenu. Native numeric values expose Number, Currency, Accounting, Percentage, and Scientific presets; native Date values expose Date, Date & time, and Time. More formats… opens a separate Format Cells dialog for locale, decimal and date styles, font and fill, alignment, wrapping, borders, and a live preview.

See Format Cells for the complete popup guide, including selection behavior, physical coordinates, render-time application, direct plugin usage, and runtime persistence limits.

Formatting changes presentation only. Raw source values used by editors, sorting, filtering, and formulas are not rewritten. Cell commands target the active range; column-header commands create a true column formatting default.

Map application column types explicitly when their values do not carry native number or Date types:

grid.dataGridContextMenu = {
formatting: {
locale: 'en-GB',
currencies: ['GBP', 'EUR', 'USD'],
columnTypeKinds: {
money: 'number',
applicationDate: 'date',
},
},
};

Set formatting: false to remove the whole formatting submenu. Formatting commands respect grid and per-cell readonly rules. Value presets preserve authored custom cell templates; the rich dialog can still compose appearance through cellProperties without replacing interactive content.

Inspect cell opens a compact read-only popup for the exact invoked cell. It shows the column label and property, row dimension and authored source index, plus the raw value and its JavaScript type. The bounded serializer displays dates, undefined, bigint, symbols, functions, accessors, and circular references as selectable JSON-like inspection text without invoking getters or toJSON.

Full enumerable row snapshots are opt-in because source models can contain hidden or internal fields. Inspect column is available from leaf column headers and shows the property, pinned dimension, visible and physical indexes, type, readonly mode, sort order, filter configuration, rendered width, and authored width bounds. The complete resolved column definition is also opt-in because schema objects can contain private application metadata.

Limits and an application redaction hook are available when complete row or column inspection is appropriate:

grid.dataGridContextMenu = {
inspection: {
includeRowData: true,
includeColumnData: true,
serialization: { maxDepth: 5, maxEntries: 100, maxOutputLength: 20_000 },
redact(value, { kind }) {
return kind === 'row' || kind === 'column'
? redactPrivateFields(value)
: value;
},
localeText: { title: 'Cell details' },
},
};

The limits bound recursive traversal and rendered output after JavaScript has discovered an object’s own keys. They are not a sandbox for hostile Proxy traps or extremely wide objects; use redact to replace untrusted complex values.

Both inspectors remain available for readonly cells and readonly grids. Hide them like any other built-in item with hiddenItems: { 'cell.inspect': true, 'column.inspect': true }, or replace their behavior through the matching commandHandlers entry.