Skip to main content

Merged Cells

Merge a rectangular block of cells into one, like Excel's Merge Cells. The block's top-left cell (the anchor) is drawn across the whole block; the cells it covers are not rendered.

Live Demo​

Region, quarter and note cells are merged - try the arrow keys, Shift+click and Ctrl+C
Live

Quick Example​

import { OGrid } from '@alaarab/ogrid-react-radix';
import type { IMergedCell } from '@alaarab/ogrid-react-radix';

const mergedCells: IMergedCell[] = [
// "region" of row n1 spans 4 rows
{ rowId: 'n1', columnId: 'region', rowSpan: 4 },
// a 2 x 3 block anchored at row s1, column "quarter"
{ rowId: 's1', columnId: 'quarter', rowSpan: 2, colSpan: 3 },
];

<OGrid
columns={columns}
data={rows}
getRowId={(r) => r.id}
mergedCells={mergedCells}
/>;

The same prop works on @alaarab/ogrid-react-fluent and on the bare DataGridTable.

The IMergedCell contract​

interface IMergedCell {
rowId: RowId; // the anchor row (getRowId value)
columnId: string; // the anchor column
rowSpan?: number; // default 1
colSpan?: number; // default 1
}

Spans count displayed rows (after sorting, filtering and paging) and visible columns, starting at the anchor. Hide a column and a span that crossed it now reaches one column further; sort the grid and the merge follows its anchor row and covers the rows displayed below it.

Behavior​

Merged cells behave like Excel:

ActionResult
Click any part of the blockThe anchor becomes the active cell
Arrow keysMove over the block as one cell; entering it lands on the anchor
Tab / Shift+TabCovered cells are skipped
Range selection (drag, Shift+click, Shift+arrows)Grows to include every merge it touches
CopyThe anchor's value is copied once; covered cells copy as empty
PasteOnly the anchor receives a value
Enter / F2 / double-clickEdits the anchor; Enter-commit moves below the block

When a merge no longer fits the view​

Merges degrade instead of breaking the layout:

  • Anchor not displayed (filtered out, or on another page): the merge is ignored.
  • Span past the end of the page or the last column: clipped to what is displayed.
  • Pinned columns: a merge never crosses from pinned to unpinned columns (or between left and right pins); it is clipped at the boundary.
  • Frozen rows: a merge never crosses the frozen-row boundary; it is clipped there.
  • Overlapping merges: the first one in the array wins; later overlapping entries are ignored.
  • Virtual scrolling: when the block is partly scrolled out of the rendered window, its visible portion is drawn (showing the anchor's content), so nothing misaligns.
  • Windowed data sources (getRowCount + getRows): merges are not applied, because the grid never holds whole spans.

Resolution is available as a pure function for headless use: resolveMergedCells, with expandRangeToMerges and isCoveredCell for selection and clipboard logic.