SelectionPlugin
SelectionPlugin integrates selection with the reactive store using IntervalTree-backed queries.
The SelectionPlugin integrates ZenGrid’s selection functionality with the reactive store system, using IntervalTree for efficient range queries.
Overview
SelectionPlugin runs at phase 40 and provides:
- Reactive selection state management
- Cell, row, column, and range selection modes
- IntervalTree-backed O(log n) queries
- Keyboard and mouse selection
- Selection events
SelectionPlugin depends on CorePlugin and uses IntervalTree from @zengrid/shared for efficient range operations.
Creation
Create the SelectionPlugin using the factory function:
import { createSelectionPlugin } from '@zengrid/core';
const selectionPlugin = createSelectionPlugin();grid.usePlugin(selectionPlugin);Selection Modes
SelectionPlugin supports multiple selection modes:
const grid = createGrid(container, { selection: { mode: 'cell', // Single cell // mode: 'row', // Single or multiple rows // mode: 'column', // Single or multiple columns // mode: 'range', // Rectangular ranges multiple: true // Allow multiple selections }});
grid.usePlugin(createSelectionPlugin());Cell Selection
// Select a single cellgrid.api.selection.selectCell(5, 2);
// Check if cell is selectedconst isSelected = grid.api.selection.isCellSelected(5, 2);
// Get selected cellsconst cells = grid.api.selection.getSelectedCells();// [{ rowIndex: 5, columnIndex: 2 }]Row Selection
// Select a single rowgrid.api.selection.selectRow(5);
// Select multiple rowsgrid.api.selection.selectRows([5, 6, 7]);
// Get selected rowsconst rows = grid.api.selection.getSelectedRows();// [5, 6, 7]
// Check if row is selectedconst isSelected = grid.api.selection.isRowSelected(5);Column Selection
// Select a single columngrid.api.selection.selectColumn(2);
// Select multiple columnsgrid.api.selection.selectColumns([2, 3]);
// Get selected columnsconst columns = grid.api.selection.getSelectedColumns();// [2, 3]Range Selection
// Select a rectangular rangegrid.api.selection.selectRange({ startRow: 5, endRow: 10, startCol: 2, endCol: 4});
// Get selected rangesconst ranges = grid.api.selection.getSelectedRanges();// [{ startRow: 5, endRow: 10, startCol: 2, endCol: 4 }]Range selection is the most flexible mode, supporting rectangular selections with keyboard and mouse.
IntervalTree Integration
SelectionPlugin uses IntervalTree for efficient range queries:
// O(log n) query for selected rows in viewportconst viewportStart = 100;const viewportEnd = 200;
// Efficiently find overlapping selectionsconst selectedInViewport = grid.api.selection.getSelectedInRange( viewportStart, viewportEnd);
// IntervalTree handles complex overlapping rangesgrid.api.selection.selectRange({ startRow: 50, endRow: 150, startCol: 0, endCol: 5 });grid.api.selection.selectRange({ startRow: 120, endRow: 180, startCol: 2, endCol: 7 });
// Merge overlapping ranges automaticallyconst ranges = grid.api.selection.getSelectedRanges();IntervalTree provides O(log n) query performance even with thousands of selection ranges.
Reactive State
SelectionPlugin creates these reactive signals:
// Selection rangesstore.get('selection.ranges') // IntervalTree of selected rangesstore.set('selection.ranges', tree)
// Active cell (keyboard focus)store.get('selection.active') // { row: number, col: number } | nullstore.set('selection.active', cell)
// Computed signalsstore.get('selection.hasSelection') // Boolean: any selectionsstore.get('selection.selectedCount') // Number of selected cellsAPI Methods
SelectionPlugin registers these methods on grid.api.selection:
// Cell selectiongrid.api.selection.selectCell(row, col);grid.api.selection.isCellSelected(row, col);grid.api.selection.getSelectedCells();
// Row selectiongrid.api.selection.selectRow(row);grid.api.selection.selectRows(rows);grid.api.selection.isRowSelected(row);grid.api.selection.getSelectedRows();
// Column selectiongrid.api.selection.selectColumn(col);grid.api.selection.selectColumns(cols);grid.api.selection.isColumnSelected(col);grid.api.selection.getSelectedColumns();
// Range selectiongrid.api.selection.selectRange(range);grid.api.selection.getSelectedRanges();grid.api.selection.getSelectedInRange(startRow, endRow);
// Clear selectiongrid.api.selection.clearSelection();
// Active cellgrid.api.selection.setActiveCell(row, col);grid.api.selection.getActiveCell();Keyboard Selection
SelectionPlugin integrates with keyboard navigation:
// Arrow keys move active cell// Shift + Arrow keys extend selection// Ctrl + A selects all// Ctrl + Click adds to selection
grid.on('selection:keydown', (event) => { console.log('Keyboard selection:', event.key);});Mouse Selection
Handle mouse-based selection:
// Click selects cell// Click + Drag selects range// Ctrl + Click adds to selection// Shift + Click extends selection
grid.on('selection:mousedown', (event) => { console.log('Mouse selection:', event.row, event.col);});Events
SelectionPlugin emits events for selection changes:
grid.on('selection:change', (selection) => { console.log('Selection changed:', selection);});
grid.on('selection:clear', () => { console.log('Selection cleared');});
grid.on('selection:cell', (cell) => { console.log('Cell selected:', cell);});
grid.on('selection:range', (range) => { console.log('Range selected:', range);});Integration with Store
SelectionPlugin integrates seamlessly with the reactive store:
// Watch selection changesstore.effect('logSelectionChanges', () => { const ranges = store.get('selection.ranges'); console.log('Current selection:', ranges);}, 'MyComponent', 100);
// Computed from selectionstore.computed('selection.isEmpty', () => { const ranges = store.get('selection.ranges'); return ranges.size === 0;}, 'MyComponent', 100);Performance
SelectionPlugin optimizes selection operations:
- IntervalTree: O(log n) range queries
- Batch updates: Multiple selections batched into single update
- Incremental updates: Only affected cells re-rendered
- Viewport-aware: Only renders selected cells in viewport
Use range selection with IntervalTree for optimal performance with large selections.
Selection State Structure
interface CellSelection { rowIndex: number; columnIndex: number;}
interface RangeSelection { startRow: number; endRow: number; startCol: number; endCol: number;}
interface ActiveCell { row: number; col: number;}Custom Selection Styling
Customize selection appearance:
const grid = createGrid(container, { selection: { mode: 'range', style: { backgroundColor: 'rgba(0, 120, 215, 0.1)', borderColor: '#0078d7', borderWidth: 2 } }});