Selection and ranges
Two different things: rows are selected (a set of rows, for an action on them), cells are taken in a block (a rectangle, for copy, paste and fill).
Selecting rows
Section titled “Selecting rows”The selection setting turns it on; no feature is needed.
import { createGrid } from '@dzgrid/dom';import { idOfDataKey, type SelectionState } from '@dzgrid/core';import { EN } from '@dzgrid/dom/locales/en';
type Seed = { id: string; plant: string; packets: number };
const grid = createGrid<Seed>(document.getElementById('seeds')!, { rows: [{ id: 's1', plant: 'Moon squash', packets: 12 }, { id: 's2', plant: 'Glass bean', packets: 0 }], columns: [{ id: 'plant', header: 'Plant' }, { id: 'packets', header: 'Packets', type: 'number' }], getRowId: (s) => s.id, messages: EN, selection: { mode: 'multi', checkbox: true, keepOnFilter: true }, onEvent: (e) => { if (e.type === 'selectionChanged') console.log(describe(e.selection), e.count); },});
function describe(s: SelectionState): string { const ids = [...s.keys].map(idOfDataKey); return s.mode === 'include' ? `only ${ids.join(', ')}` : `every row but ${ids.join(', ')}`;}
grid.kernel.dispatch({ type: 'selection.all' }, 'api');import { Component, signal } from '@angular/core';import { DzGrid, DzgController, selectedIds, type DzgColumnOrGroup } from '@dzgrid/angular';import type { SelectionState } from '@dzgrid/core';import { EN } from '@dzgrid/dom/locales/en';
type Seed = { id: string; plant: string; packets: number };
@Component({ selector: 'app-seeds', imports: [DzGrid], template: ` <dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" [selection]="{ mode: 'multi', checkbox: true, keepOnFilter: true }" [(selected)]="selected" (gridReady)="ctrl = $event" /> <button type="button" (click)="order()">Order the selected seeds</button>`,})export class Seeds { readonly rows: Seed[] = [{ id: 's1', plant: 'Moon squash', packets: 12 }, { id: 's2', plant: 'Glass bean', packets: 0 }]; readonly columns: DzgColumnOrGroup<Seed>[] = [{ id: 'plant', header: 'Plant' }, { id: 'packets', header: 'Packets', type: 'number' }]; readonly rowId = (s: Seed) => s.id; readonly messages = EN; readonly selected = signal<SelectionState | undefined>(undefined); ctrl: DzgController<Seed> | null = null;
order() { // The ids, with the query when the selection is « every row but these ». if (this.ctrl) console.log(selectedIds(this.ctrl)); }}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { idOfDataKey, type GridEvent } from '@dzgrid/core';import { EN } from '@dzgrid/dom/locales/en';
type Seed = { id: string; plant: string; packets: number };
const grid = document.querySelector<DzGridElement<Seed>>('dz-grid')!;grid.columns = [{ id: 'plant', header: 'Plant' }, { id: 'packets', header: 'Packets', type: 'number' }];grid.getRowId = (s) => s.id;grid.messages = EN;grid.settings = { selection: { mode: 'multi', checkbox: true, keepOnFilter: true } };grid.rows = [{ id: 's1', plant: 'Moon squash', packets: 12 }, { id: 's2', plant: 'Glass bean', packets: 0 }];
grid.addEventListener('dzg-selection-changed', (e) => { const event = (e as CustomEvent<GridEvent<Seed>>).detail; if (event.type === 'selectionChanged') console.log(event.selection.mode, [...event.selection.keys].map(idOfDataKey));});Click selects one row, Ctrl/Cmd+click adds or removes, Shift+click selects a run; the header's box selects all.
mode:none,singleormulti.checkbox: trueadds a tick column with a header box;click: truelets a click select; without it a click only moves the focus (select with Space or the box).- With
click: true, a click selects one row, Ctrl/Cmd+click toggles, Shift+click selects a run in screen order; Space toggles the focused row, Ctrl+A selects all. The selection survives sorting and filtering. - The selection holds row keys, not your ids.
idOfDataKey(key)gives the id back; in Angular,selectedIds(controller)gives the ids of the whole selection with the query. mode: 'exclude'means « every row the query matches, but these keys »: after « select all » on paged or server rows, the grid cannot list them, your server can by replaying the query.canSelect(row, selection, context)returnsfalse, or the sentence to say, to refuse a row; it needs therules()feature from@dzgrid/dom/rules(texts-rules), which says why to the user.
Every key of the selection setting is on its page: selection.
Blocks of cells and the clipboard
Section titled “Blocks of cells and the clipboard”range() from @dzgrid/dom/range adds blocks: Shift+arrows, a drag, Ctrl+A. Ctrl+C copies the block as tab-separated
text that spreadsheets read (numbers in the locale’s form, formula starts guarded), Ctrl+X cuts, Ctrl+V pastes into
editable cells, Delete empties them. Pasting and cutting need editing. Texts -range, CSS range.css.
fill(blocks) from @dzgrid/dom/fill adds the fill handle at the block’s corner: drag it to copy the block on, or to
continue a series (1, 2, 3…; days by days); Ctrl+D and Ctrl+R fill down and right from the keyboard. Texts -fill,
CSS fill.css. A fill or a paste is one save request and one undo.
import { createGrid, type GridMessages } from '@dzgrid/dom';import { edit } from '@dzgrid/dom/edit';import { editors } from '@dzgrid/dom/editors-base';import { range } from '@dzgrid/dom/range';import { fill } from '@dzgrid/dom/fill';import { EN } from '@dzgrid/dom/locales/en';import { EN_EDIT } from '@dzgrid/dom/locales/en-edit';import { EN_EDITORS } from '@dzgrid/dom/locales/en-editors';import { EN_RANGE } from '@dzgrid/dom/locales/en-range';import { EN_FILL } from '@dzgrid/dom/locales/en-fill';import '@dzgrid/theme/edit.css';import '@dzgrid/theme/range.css';import '@dzgrid/theme/fill.css';
type Shelf = { id: string; aisle: string; jars: number };
const blocks = range();const handle = fill(blocks);const messages: GridMessages = { ...EN, text: { ...EN.text, ...EN_EDIT.text, ...EN_EDITORS.text, ...EN_RANGE.text, ...EN_FILL.text },};
const grid = createGrid<Shelf>(document.getElementById('pantry')!, { rows: [{ id: 'a1', aisle: 'Jam', jars: 14 }, { id: 'a2', aisle: 'Honey', jars: 9 }], columns: [ { id: 'aisle', header: 'Aisle' }, { id: 'jars', header: 'Jars', type: 'number', editable: true }, ], getRowId: (s) => s.id, messages, features: [edit<Shelf>(), editors(), blocks, handle], onEvent: (e) => { if (e.type === 'rangeCopied') console.log(`${e.cells} cells copied`); },});
const copied = blocks.api(grid)?.copy({ headers: true }); // the block as text, as Ctrl+C wouldconsole.log(copied);import { Component } from '@angular/core';import { DzGrid, DzgController, type DzgColumnOrGroup } from '@dzgrid/angular';import { edit } from '@dzgrid/dom/edit';import { editors } from '@dzgrid/dom/editors-base';import { range } from '@dzgrid/dom/range';import { fill } from '@dzgrid/dom/fill';import { EN } from '@dzgrid/dom/locales/en';import { EN_EDIT } from '@dzgrid/dom/locales/en-edit';import { EN_EDITORS } from '@dzgrid/dom/locales/en-editors';import { EN_RANGE } from '@dzgrid/dom/locales/en-range';import { EN_FILL } from '@dzgrid/dom/locales/en-fill';
type Shelf = { id: string; aisle: string; jars: number };
const blocks = range();
@Component({ selector: 'app-pantry', imports: [DzGrid], template: ` <dz-grid [rows]="rows" [columns]="columns" [getRowId]="rowId" [messages]="messages" [features]="features" (rangeCopied)="copied = $event.cells" (gridReady)="ctrl = $event" /> <button type="button" (click)="copy()">Copy with the titles</button> <p>{{ copied }} cells copied</p>`,})export class Pantry { readonly rows: Shelf[] = [{ id: 'a1', aisle: 'Jam', jars: 14 }, { id: 'a2', aisle: 'Honey', jars: 9 }]; readonly columns: DzgColumnOrGroup<Shelf>[] = [{ id: 'aisle', header: 'Aisle' }, { id: 'jars', header: 'Jars', type: 'number', editable: true }]; readonly rowId = (s: Shelf) => s.id; readonly messages = { ...EN, text: { ...EN.text, ...EN_EDIT.text, ...EN_EDITORS.text, ...EN_RANGE.text, ...EN_FILL.text } }; readonly features = [edit<Shelf>(), editors(), blocks, fill(blocks)]; ctrl: DzgController<Shelf> | null = null; copied = 0;
copy() { console.log(this.ctrl?.range()?.copy({ headers: true })); }}import '@dzgrid/element/define';import type { DzGridElement } from '@dzgrid/element';import { edit } from '@dzgrid/dom/edit';import { editors } from '@dzgrid/dom/editors-base';import { range } from '@dzgrid/dom/range';import { fill } from '@dzgrid/dom/fill';import { EN } from '@dzgrid/dom/locales/en';import { EN_EDIT } from '@dzgrid/dom/locales/en-edit';import { EN_EDITORS } from '@dzgrid/dom/locales/en-editors';import { EN_RANGE } from '@dzgrid/dom/locales/en-range';import { EN_FILL } from '@dzgrid/dom/locales/en-fill';
type Shelf = { id: string; aisle: string; jars: number };
const blocks = range();const grid = document.querySelector<DzGridElement<Shelf>>('dz-grid')!;grid.columns = [{ id: 'aisle', header: 'Aisle' }, { id: 'jars', header: 'Jars', type: 'number', editable: true }];grid.getRowId = (s) => s.id;grid.messages = { ...EN, text: { ...EN.text, ...EN_EDIT.text, ...EN_EDITORS.text, ...EN_RANGE.text, ...EN_FILL.text } };grid.features = [edit<Shelf>(), editors(), blocks, fill(blocks)];grid.rows = [{ id: 'a1', aisle: 'Jam', jars: 14 }, { id: 'a2', aisle: 'Honey', jars: 9 }];
await grid.ready;if (grid.grid) console.log(blocks.api(grid.grid)?.copy({ headers: true }));Shift+arrows take a block; drag the handle at its corner down Shelf or Checked to continue the series; right-click for the menu.
The right-click menu (contextMenu({ use: { range: blocks, fill: handle } }) from @dzgrid/dom/context-menu)
offers the same actions to the mouse.