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).
What the user can do
| Gesture | Result |
|---|---|
| Drag from a cell | Draws the rectangle |
| Shift-click | Extends it to the clicked cell |
| Drag the small square on the anchor cell | Fill handle — extends the values |
| Ctrl+C | Copies the rectangle as TSV |
| Ctrl+X | Copies, then clears the cells the user may write |
| Ctrl+V | Pastes from the anchor cell |
| Arrow / Home / End / PageUp / PageDown / Tab | Collapses 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 noclipboardDatafallback, 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: pastingAntalyaback does not turn it into the stored code. - Sorting or filtering does not clear the rectangle. Range coordinates are
view indices and only
columnStateChangedclears 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.
selectionChangedis row selection; there is norangeSelectionChanged. You can observe the gestures, not the shape. - Statistics stop at 100,000 cells and report
truncatedinstead. - 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.