Documentation

Range selection, fill handle and clipboard

Three behaviours, one switch. There is no rangeSelection, fillHandle or clipboard prop — all of it is editing.range, and it rides on the editing layer:

import { EnterpriseDataGrid } from '@kanunilabs/datagrid-react-enterprise';

<EnterpriseDataGrid
  licenseKey={key}
  dataSource={rows}
  rowKey="id"
  columns={columns}
  editing={{ mode: 'cell', range: true }}
/>

range defaults to true, so enabling editing already gives you the rectangle. Turning editing off turns range off with it: internally it resolves to editingEnabled && (editing.range ?? true).

See it working →

What the user can do

GestureResult
Drag from a cellDraws the rectangle
Shift-clickExtends it to the clicked cell
Drag the small square on the anchor cellFill handle — extends the values
Ctrl+CCopies the rectangle as TSV
Ctrl+XCopies, then clears the cells the user may write
Ctrl+VPastes from the anchor cell
Arrow / Home / End / PageUp / PageDown / TabCollapses the rectangle

That last row is Excel's rule, and it surprises people who expect "select, then nudge with the arrows".

Which cells may change

allowUpdating is the single gate for editing, fill, paste and cut:

editing={{
  mode: 'cell',
  allowUpdating: (row, columnId) => columnId !== 'id' && !row.locked,
}}

A cell the predicate refuses is still copied — it just is not cleared or overwritten. Validation rules apply too: cut writes null, so a required-field rule can refuse it, and the cell keeps its value.

Watching it happen

Every gesture is published as a bracket — a Start and an End — and the End fires even if the handler throws:

onReady={(controller) => {
  controller.events.on('fillStart', (e) => console.log('filling', e.cellCount));
  controller.events.on('fillEnd', (e) => console.log('done', e.performed));

  controller.events.on('cellValueChanged', (e) => {
    // 'edit' | 'cut' | 'fill' | 'paste' | 'undo' | 'redo'
    if (e.source === 'paste') audit(e.rowKey, e.columnId, e.value);
  });
}}

The pairs are cutStart/cutEnd, copyStart/copyEnd, pasteStart/pasteEnd and fillStart/fillEnd. A whole paste is one undo step, not one per cell.

Selection statistics

Set statusBar and the rectangle reports itself — cells, sum, average, min, max:

statusBar={{ items: ['totalRows', 'selectedRows', 'rangeStats'] }}

Without a status bar the stats appear in their own strip instead, so the numbers show up exactly once either way. Non-numeric cells are skipped, not counted as zero.

What this does not do

Written down because finding out mid-integration costs more than reading it here.

  • No keyboard path to the rectangle. Shift+arrow extends the row selection, not the cell rectangle, and Ctrl+A selects rows. The rectangle is mouse-only, and the fill handle is aria-hidden — it has no keyboard or screen-reader equivalent.
  • One rectangle at a time. No Ctrl-click union, no disjoint ranges.
  • Paste needs the async clipboard. It reads navigator.clipboard.readText() with no clipboardData fallback, so it needs a secure context and the permission. Where the browser refuses, paste does nothing.
  • Paste crops, it never grows the grid. Copy 100 rows onto the last row and only the rows that fit are written.
  • Type coercion covers numbers and booleans. A pasted date is written as text; so is a number the parser cannot read.
  • Fill extrapolates numeric series only. Two or more numbers become an arithmetic sequence; anything else repeats the pattern. No date series, no month names, no Item 1 → Item 2. Fill writes values, never formats.
  • Fill is single-axis. A diagonal drag resolves to one direction; there is no L-shaped fill.
  • Copied text is what the user sees — the lookup label, then the valueFormatter. That is one-way: pasting Antalya back does not turn it into the stored code.
  • Sorting or filtering does not clear the rectangle. Range coordinates are view indices and only columnStateChanged clears them, so after a sort the rectangle sits on the same positions over different rows. Clear it yourself if that matters.
  • No event for the rectangle. selectionChanged is row selection; there is no rangeSelectionChanged. You can observe the gestures, not the shape.
  • Statistics stop at 100,000 cells and report truncated instead.
  • Community has none of it — no rectangle, no fill, no cut, no paste. Community copies whole rows with Ctrl+C.

Without a licence key the features still run, watermarked. The boundary is distribution and the watermark, not a code path that refuses you.