Selection Plugin
The Selection plugin adds cell, row, and range selection capabilities to the grid with full keyboard support. Whether you need simple cell highlighting or complex multi-range selections, this plugin has you covered.
Installation
Section titled “Installation”import '@toolbox-web/grid/features/selection';Basic Usage
Section titled “Basic Usage”import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/selection';
const grid = queryGrid('tbw-grid');grid.gridConfig = { columns: [ { field: 'id', header: 'ID' }, { field: 'name', header: 'Name' }, { field: 'email', header: 'Email' }, ], features: { selection: 'row', },};grid.rows = data;import '@toolbox-web/grid-react/features/selection';import { DataGrid } from '@toolbox-web/grid-react';
function MyGrid({ data }) { return ( <DataGrid rows={data} columns={[ { field: 'id', header: 'ID' }, { field: 'name', header: 'Name' }, { field: 'email', header: 'Email' }, ]} selection={{ mode: 'row' }} style={{ height: '400px' }} /> );}<script setup>import '@toolbox-web/grid-vue/features/selection';import { TbwGrid, TbwGridColumn } from '@toolbox-web/grid-vue';const data = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' },];</script>
<template> <TbwGrid :rows="data" selection="row" style="height: 400px"> <TbwGridColumn field="id" header="ID" /> <TbwGridColumn field="name" header="Name" /> <TbwGridColumn field="email" header="Email" /> </TbwGrid></template>import '@toolbox-web/grid-angular/features/selection';import { Component } from '@angular/core';import { Grid } from '@toolbox-web/grid-angular';import type { ColumnConfig } from '@toolbox-web/grid';
@Component({ selector: 'app-my-grid', imports: [Grid], template: ` <tbw-grid [rows]="rows" [columns]="columns" [selection]="'row'" style="height: 400px; display: block;"> </tbw-grid> `,})export class MyGridComponent { rows = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }, ]; columns: ColumnConfig[] = [ { field: 'id', header: 'ID' }, { field: 'name', header: 'Name' }, { field: 'email', header: 'Email' }, ];}Interactive Demo
Section titled “Interactive Demo”Switch between selection modes to see how each one behaves. The state panel below the grid shows the current selection in real time.
- Cell mode: Click cells to select them individually
- Row mode: Click anywhere in a row to select the entire row. Ctrl+Click to toggle, Shift+Click for range
- Range mode: Click and drag to select rectangular ranges. Ctrl+drag for multiple ranges
import '@toolbox-web/grid';import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/selection';
const grid = queryGrid('tbw-grid');const output = document.querySelector<HTMLElement>('[data-output-id="selection-demo"]');
if (grid) { const sampleData = [ { id: 1, name: 'Alice', department: 'Engineering', salary: 95000, email: 'alice@example.com' }, { id: 2, name: 'Bob', department: 'Marketing', salary: 75000, email: 'bob@example.com' }, { id: 3, name: 'Carol', department: 'Engineering', salary: 105000, email: 'carol@example.com' }, { id: 4, name: 'Dan', department: 'Sales', salary: 85000, email: 'dan@example.com' }, { id: 5, name: 'Eve', department: 'Marketing', salary: 72000, email: 'eve@example.com' }, { id: 6, name: 'Frank', department: 'Engineering', salary: 98000, email: 'frank@example.com' }, { id: 7, name: 'Grace', department: 'Sales', salary: 88000, email: 'grace@example.com' }, { id: 8, name: 'Henry', department: 'HR', salary: 65000, email: 'henry@example.com' }, ];
const columns = [ { field: 'id', header: 'ID', type: 'number' as const }, { field: 'name', header: 'Name' }, { field: 'department', header: 'Department' }, { field: 'salary', header: 'Salary', type: 'number' as const }, { field: 'email', header: 'Email' }, ];
function setupGrid(mode: string) { grid.gridConfig = { columns, features: { selection: mode as 'cell' | 'row' | 'range' } }; grid.rows = sampleData;
grid.on('selection-change', () => { const plugin = grid.getPluginByName('selection'); if (!plugin || !output) return; const sel = plugin.getSelection(); const lines = ['<b>mode:</b> ' + sel.mode];
if (sel.mode === 'row') { const indices = plugin.getSelectedRowIndices(); lines.push('<b>selectedRows:</b> [' + indices.join(', ') + ']'); if (indices.length > 0) { const names = indices.map((i: number) => sampleData[i]?.name).filter(Boolean); lines.push('<b>rowData:</b> ' + names.map((n: string) => '"' + n + '"').join(', ')); } }
if (sel.ranges.length > 0) { const rangeStrs = sel.ranges.map( (r: { from: { row: number; col: number }; to: { row: number; col: number } }) => '{ from: {row:' + r.from.row + ', col:' + r.from.col + '}, to: {row:' + r.to.row + ', col:' + r.to.col + '} }' ); lines.push('<b>ranges:</b> [' + rangeStrs.join(', ') + ']'); if (sel.mode === 'range') { lines.push('<b>cellCount:</b> ' + plugin.getSelectedCells().length); } } else { lines.push('<b>ranges:</b> []'); }
output.innerHTML = lines.join('<br>'); }); }
setupGrid('cell');
}<div class="grid-playground not-content">
<tbw-grid style="height: 350px;"></tbw-grid> <div class="grid-playground-output" data-output-id="selection-demo"> Interact with the grid to see state updates... </div>Checkbox Selection
Section titled “Checkbox Selection”Add checkbox: true to show a selection checkbox column with a “select all” header. Works exclusively in row mode.
<tbw-grid style="height: 250px;"></tbw-grid>import '@toolbox-web/grid';import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/selection';
const grid = queryGrid('tbw-grid');if (grid) { grid.gridConfig = { columns: [ { field: 'id', header: 'ID', type: 'number' as const }, { field: 'name', header: 'Name' }, { field: 'department', header: 'Department' }, { field: 'salary', header: 'Salary', type: 'number' as const }, ], features: { selection: { mode: 'row', checkbox: true } }, }; grid.rows = [ { id: 1, name: 'Alice', department: 'Engineering', salary: 95000 }, { id: 2, name: 'Bob', department: 'Marketing', salary: 75000 }, { id: 3, name: 'Carol', department: 'Engineering', salary: 105000 }, { id: 4, name: 'Dan', department: 'Sales', salary: 85000 }, { id: 5, name: 'Eve', department: 'Marketing', salary: 72000 }, ];}features: { selection: { mode: 'row', checkbox: true } }Configuration Options
Section titled “Configuration Options”Grid-Level Toggle
Section titled “Grid-Level Toggle”You can disable selection grid-wide using gridConfig.selectable:
grid.gridConfig = { selectable: false, // Disables ALL selection features: { selection: 'range' },};Plugin Options
Section titled “Plugin Options”See SelectionConfig for the full list of options and defaults.
Double-Click Selection Trigger
Section titled “Double-Click Selection Trigger”In data-entry grids, you may want single-click to only focus the row/cell for keyboard navigation, while double-click changes the selection state.
features: { selection: { mode: 'row', triggerOn: 'dblclick', // Single-click focuses, double-click selects },}Conditional Selection
Section titled “Conditional Selection”Use the isSelectable callback to prevent selection of specific rows or cells:
features: { selection: { mode: 'row', isSelectable: (row) => row.status !== 'locked', },}Behavior of non-selectable rows/cells:
| Aspect | Behavior |
|---|---|
| Click | Ignored (no selection change) |
| Keyboard | Skipped with Shift+Arrow |
| Select All | Excluded |
| Visual | Muted via [data-selectable="false"] attribute |
| Focus | Still navigable |
Rows with status "locked" cannot be selected. Try clicking or using Shift+Click — locked rows are skipped.
<tbw-grid style="height: 300px;"></tbw-grid>import '@toolbox-web/grid';import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/selection';
const grid = queryGrid('tbw-grid');grid.gridConfig = { columns: [ { field: 'id', header: 'ID', type: 'number', width: 80 }, { field: 'name', header: 'Name' }, { field: 'department', header: 'Department' }, { field: 'status', header: 'Status' }, ], features: { selection: { mode: 'row', isSelectable: (row: { status: string }) => row.status !== 'locked', }, },};
grid.rows = [ { id: 1, name: 'Alice Johnson', department: 'Engineering', status: 'active' }, { id: 2, name: 'Bob Smith', department: 'Marketing', status: 'locked' }, { id: 3, name: 'Carol Davis', department: 'Engineering', status: 'active' }, { id: 4, name: 'Dan Wilson', department: 'Sales', status: 'locked' }, { id: 5, name: 'Eve Brown', department: 'HR', status: 'active' }, { id: 6, name: 'Frank Miller', department: 'Engineering', status: 'active' }, { id: 7, name: 'Grace Lee', department: 'Finance', status: 'locked' }, { id: 8, name: 'Hank Taylor', department: 'Sales', status: 'active' },];Keyboard Shortcuts
Section titled “Keyboard Shortcuts”| Shortcut | Action |
|---|---|
Arrow Keys | Move focus |
Shift + Arrow | Extend selection (range mode) |
Ctrl/⌘ + Click | Toggle row/cell (multi-select) |
Shift + Click | Extend selection from anchor |
Ctrl/⌘ + A | Select all (row and range modes) |
Escape | Clear selection |
Programmatic API
Section titled “Programmatic API”const plugin = grid.getPluginByName('selection');
// Queryconst selection = plugin.getSelection();plugin.isCellSelected(2, 1);
// Row modeplugin.selectRows([0, 2, 4]);const rows = plugin.getSelectedRows<Employee>();
// Range modeplugin.setRanges([{ from: { row: 0, col: 0 }, to: { row: 5, col: 3 } }]);
// Actionsplugin.selectAll();plugin.clearSelection();Events
Section titled “Events”| Event | Description |
|---|---|
selection-change | Fired when the selection is modified |
import '@toolbox-web/grid';import { queryGrid } from '@toolbox-web/grid';import '@toolbox-web/grid/features/selection';
const grid = queryGrid('tbw-grid');const logEl = document.querySelector('#selection-events-log')!;const clearBtn = document.querySelector('#selection-events-clear-btn');
grid.gridConfig = { columns: [ { field: 'id', header: 'ID', type: 'number', width: 60 }, { field: 'name', header: 'Name' }, { field: 'department', header: 'Department' }, { field: 'salary', header: 'Salary', type: 'number', align: 'right' }, ], features: { selection: 'range' },};
grid.rows = [ { id: 1, name: 'Alice Johnson', department: 'Engineering', salary: 85000 }, { id: 2, name: 'Bob Smith', department: 'Marketing', salary: 72000 }, { id: 3, name: 'Carol White', department: 'Sales', salary: 68000 }, { id: 4, name: 'David Brown', department: 'Engineering', salary: 92000 }, { id: 5, name: 'Eve Davis', department: 'HR', salary: 65000 },];
function addLog(msg: string) { const entry = document.createElement('div'); entry.className = 'event-log-entry'; entry.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; logEl.prepend(entry);}
grid.on('selection-change', (detail) => { addLog(`selection-change — ${detail.selectedCount ?? 0} cell(s) selected`);});
grid.on('row-select', (detail) => { addLog(`row-select — row ${detail.rowIndex}`);});
clearBtn?.addEventListener('click', () => { logEl.innerHTML = '';});<div class="selection-events-layout"> <tbw-grid style="height: 300px;"></tbw-grid> <div class="event-log-panel"> <div class="event-log-header"> <strong>Event Log</strong> <button>Clear</button> </div> <div class="event-log-body"></div> </div></div>selection-change Detail
Section titled “selection-change Detail”See SelectionChangeDetail and CellRange for the full event payload types.
Styling
Section titled “Styling”CSS Custom Properties
Section titled “CSS Custom Properties”| Property | Default | Description |
|---|---|---|
--tbw-focus-background | rgba(accent, 12%) | Focused row background |
--tbw-range-selection-bg | rgba(accent, 12%) | Range selection fill |
--tbw-range-border-color | var(--tbw-color-accent) | Range selection border |
--tbw-color-accent | #3b82f6 | Primary accent color |
tbw-grid { --tbw-range-selection-bg: rgba(76, 175, 80, 0.15); --tbw-range-border-color: #4caf50; --tbw-focus-background: rgba(76, 175, 80, 0.1);}CSS Classes
Section titled “CSS Classes”| Class | Element |
|---|---|
.selecting | Grid during range drag |
.row-focus | Focused row (row mode) |
.cell-focus | Focused cell (cell mode) |
.selected | Selected cell in range |
.selected.top / .bottom / .first / .last | Range boundary edges |
Works Well With
Section titled “Works Well With”| Plugin | Integration |
|---|---|
| EditingPlugin | Click-to-select + double-click-to-edit |
| ClipboardPlugin | Copy/paste selected cells (requires SelectionPlugin) |
| FilteringPlugin | Filter data, then select from results |
| ContextMenuPlugin | Right-click selected rows for actions |
See Also
Section titled “See Also”- Common Patterns — Full application recipes using selection
- Plugins Overview — All available plugins and compatibility
- Events Reference —
selection-changeevent details