Skip to content

Clipboard Plugin

The Clipboard plugin brings familiar copy/paste functionality to your grid with full keyboard shortcut support (Ctrl+C, Ctrl+V). It handles single cells, multi-cell selections, and integrates seamlessly with Excel and other spreadsheet applications via tab-delimited output.

import '@toolbox-web/grid/features/clipboard';

Just enable the feature and you’re ready to go—keyboard shortcuts work automatically. Configure options like includeHeaders for copying column headers or delimiter for custom CSV formats.

import { queryGrid } from '@toolbox-web/grid';
const grid = queryGrid('tbw-grid');
grid.gridConfig = {
columns: [
{ field: 'name', header: 'Name' },
{ field: 'email', header: 'Email' },
{ field: 'department', header: 'Department' }
],
features: {
clipboard: {
includeHeaders: true,
quoteStrings: true, // Wrap text in quotes for CSV compatibility
},
},
};
Include headersInclude column headers when copying
Quote stringsWrap string values in quotes

Select cells and use Ctrl+C to copy, Ctrl+V to paste.

X1 Y1 Z1 X2 Y2 Z2
Try it: Select the sample data above → Ctrl+C → Click a cell in the grid → Ctrl+V to paste.
Or paste tab/comma-separated data from Excel or Sheets!
Also try to select an area to paste in. Pasting will be restricted to that area only.

By default, the ClipboardPlugin handles paste operations automatically—no event handling needed. Just add the plugin and paste works out of the box.

For advanced use cases, provide a custom pasteHandler to validate, transform, or integrate with state management.

Click a cell and press Ctrl+C to copy its value. No range selection — only the focused cell is copied.

Without a SelectionPlugin, the clipboard operates on a single focused cell — press Ctrl+C to copy just that cell’s value.

OptionTypeDefaultDescription
includeHeadersbooleanfalseInclude column headers in copied data
delimiterstring'\t'Column delimiter (tab for Excel compatibility)
newlinestring'\n'Row delimiter
quoteStringsbooleanfalseWrap string values in quotes
escapeFormulasbooleantruePrefix values starting with =, +, -, @, tab or CR with ' so a spreadsheet treats them as text. Since 3.5.0
fillSelectionbooleanfalseTile the clipboard source to fill a larger multi-cell selection (see Fill Selection)
processCell(value, field, row) => string-Custom cell value processor for copy operations
pasteHandlerPasteHandler | nulldefaultPasteHandlerCustom paste handler. Set to null to disable auto-paste and handle the paste event manually

Per-Column Paste Guard/Transform (column.onPaste)

Section titled “Per-Column Paste Guard/Transform (column.onPaste)”

Use column.onPaste for cell-level paste rules without replacing the whole paste pipeline:

  • false rejects pastes into that column,
  • (ctx) => false rejects a specific cell,
  • (ctx) => ({ value }) transforms the incoming value,
  • omitted / true accepts as-is.
columns: [
{ field: 'id', onPaste: false },
{
field: 'price',
onPaste: ({ value, sourceField }) => {
// Reject cross-column pastes into this field.
if (sourceField && sourceField !== 'price') return false;
const n = Number(String(value).replace(/[^0-9.]/g, ''));
return Number.isFinite(n) ? { value: n } : false;
},
},
];

Cells rejected by onPaste emit one paste-rejected event per paste with a list of rejected cells.

The clipboard plugin respects both selection bounds and the editable column property when pasting:

Paste only works on columns marked editable: true. Non-editable columns are skipped during paste, preserving column alignment:

columns: [
{ field: 'id', header: 'ID' }, // NOT editable - paste skipped
{ field: 'name', header: 'Name', editable: true }, // Paste allowed
{ field: 'email', header: 'Email', editable: true }, // Paste allowed
]

If you paste "1\t2\t3" into this grid, only the name and email columns receive values (2 and 3). The id column remains unchanged.

When your grid has a getRowId and the EditingPlugin loaded, paste routes each cell change through the same commit pipeline as an interactive edit. That means a paste automatically:

  • marks rows dirty (surfaces in isDirty() / getDirtyRows() when dirtyTracking: true),
  • is undoable with the UndoRedoPlugin (Ctrl+Z reverts the whole paste),
  • emits cell-commit per cell — a handler that calls preventDefault() vetoes that value, so you can coerce or reject pasted input,
  • triggers cascades and other editing side effects.
grid.addEventListener('cell-commit', (e) => {
// Reject a pasted value that fails validation.
if (e.detail.field === 'age' && Number.isNaN(Number(e.detail.value))) {
e.preventDefault();
}
});

When includeHeaders: true, copied text carries a header row so it round-trips cleanly to spreadsheets and text editors. Pasting back into cells drops that header row automatically — only values land in cells, never the header label. The header is stripped when the pasted first row consists entirely of this grid’s column labels, so it is removed even for a cross-column paste (copy one column, paste into another), while external pastes without a matching header row are left intact.

Structured Values (Same-Grid & Cross-Window Paste)

Section titled “Structured Values (Same-Grid & Cross-Window Paste)”

Copy is WYSIWYG for external targetsprocessCell (or the column format) turns each cell into text, so pasting into a spreadsheet or editor looks right. At the same time the grid writes a structured payload: it keeps the raw cell values in memory and embeds them in a text/html representation on the clipboard.

When you paste back into a grid — the same grid, another grid, or another browser window/tab — those raw values are restored, so an object-valued cell (e.g. { id, name }, arrays) round-trips losslessly instead of degrading to its display text. The same-grid path uses the in-memory snapshot (preserving exact value types such as Date); cross-grid / cross-window uses the text/html payload. Each pasted cell is cloned, so tiling one source across many cells (fill-selection) never shares a reference, and array→cells pastes (equal or unequal counts, expansion, and clipping) behave exactly like a text paste.

For an external paste into an object column (e.g. from Excel), only text is available; the grid can’t reconstruct an arbitrary object shape from a string, so your app decides how to handle it (e.g. reject via validation, or map the text to a lookup).

Selection TypePaste Behavior
Single cellPaste expands freely from that cell, adding rows if needed
Range selectionPaste is clipped to fit within the selected range
Row selectionPaste is clipped to the selected rows (all columns within those rows)
No active rangePaste starts at the selection anchor/focused cell when available; otherwise row 0, column 0

Example: If you have a 2×2 range selected and paste 3×3 data, only the 2×2 portion that fits will be applied.

With fillSelection: true, pasting a source that is smaller than a bounded multi-cell selection tiles (repeats) the source to fill the whole selection. This is handy for applying one value across many cells without copying it repeatedly.

new ClipboardPlugin({ fillSelection: true });
Copied sourceSelectionResult
1 cell4 cellsvalue repeated in all 4 cells
v1, v24 cellsv1, v2, v1, v2
2×2 block4×4 rangethe block tiled four times

Fill only applies to a bounded (multi-cell) selection and never grows the grid. Non-editable columns are still skipped. When fillSelection is false (the default), paste writes only the source extent.

ShortcutAction
Ctrl+C / Cmd+CCopy selected cells
Ctrl+V / Cmd+VPaste into selected cells

The ClipboardPlugin exposes methods for programmatic copy operations with fine-grained control over which columns and rows to include. This is ideal for workflows where users select rows in the grid, then choose columns via a dialog before copying.

const plugin = grid.getPluginByName('clipboard');
// Copy current selection to clipboard
await plugin.copy();
// Copy with headers included
await plugin.copy({ includeHeaders: true });

Copy with Column/Row Control (CopyOptions)

Section titled “Copy with Column/Row Control (CopyOptions)”

The copy() method accepts CopyOptions to precisely control what gets copied—independently of the current selection:

const plugin = grid.getPluginByName('clipboard');
// Copy specific columns from specific rows
await plugin.copy({
rowIndices: [0, 3, 7], // Non-contiguous rows supported
columns: ['name', 'email'], // Only these columns
includeHeaders: true,
});
// Copy specific rows with all visible columns
await plugin.copyRows([0, 5]);
// Copy specific rows with column filter
await plugin.copyRows([0, 5], { columns: ['name', 'department'] });

Use getSelectionAsText() to get the formatted text without writing to the clipboard—useful for preview dialogs:

const text = plugin.getSelectionAsText({
columns: ['name', 'email', 'department'],
includeHeaders: true,
});
// "Name\tEmail\tDepartment\nAlice\talice@example.com\tEngineering\n..."
OptionTypeDefaultDescription
columnsstring[]-Specific column fields to include
rowIndicesnumber[]-Specific row indices to copy (non-contiguous OK)
includeHeadersbooleanconfig valueInclude column headers in copied text
delimiterstringconfig valueColumn delimiter override
newlinestringconfig valueRow delimiter override
processCell(value, field, row) => stringconfig valueCustom cell value processor for this operation
// Paste from clipboard
await plugin.paste();
// Get last copied info
const lastCopied = plugin.getLastCopied();
// { text: '...', timestamp: 1234567890 }

The default tab delimiter ensures copied data pastes correctly into Excel:

features: {
clipboard: {
delimiter: '\t', // Tab for Excel
includeHeaders: true, // Include column names
},
},

Select cells and use Ctrl+C / Ctrl+V to copy and paste.

Event Log:
EventDetailDescription
copy{ text, rowCount, columnCount }Fired after cells are copied
paste{ rows, text, target, fields, rawRows?, sourceFields?, fillSelection? }Fired before data is applied (default handler or custom pasteHandler can consume it)
paste-rejected{ rejected: PasteRejectedCell[] }Fired when one or more cells are rejected by column onPaste

The clipboard plugin doesn’t add visible UI elements. Selection styling is handled by the SelectionPlugin.

AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://toolboxjs.com/llms-full.txt