LyteNyte Grid logo for light mode. Links back to the documentation home page.
Cells

Cell Range Selection

LyteNyte Grid supports selecting single or multiple ranges. A range is a rectangular block of cells that the grid marks as selected.

Set the cellSelectionMode property on the grid to configure the cell selection behavior of LyteNyte Grid. This property accepts three possible values:

  • "range": Allows a single range of cells to be selected.
  • "multi-range": Supports selecting multiple cell ranges. Hold Control or Command to add ranges.
  • "none": Disables cell selection in the grid. By default, cell selection is disabled.

Single-Range Selection

Set cellSelectionMode to "range" to enable single-range selection. Click a cell, then drag across the grid to select a range.

Single-Range Selection

Fork code on stack blitzFork code on code sandbox

To enable cell selection, the demo sets the cellSelectionMode property as shown in the code below.

<Grid columns={columns} columnBase={base} rowSource={ds} cellSelectionMode="range" />

Uncontrolled Cell Selection

When cell selection is enabled, LyteNyte Grid tracks the selection state internally. Call cellSelections to retrieve the current selection. Uncontrolled cell selection works well for operations like copy-to-clipboard without managing selection state in your app.

In the demo below, select a cell range and press Control+C or Command+C to copy to your clipboard.

Copy-to-Clipboard

Fork code on stack blitzFork code on code sandbox

The demo listens for the viewport keyDown event, then:

  • Retrieves the current selection bounds via api.cellSelections.
  • Exports the selected range data using api.exportData, converts it to a string, and writes it to the clipboard.
  • Applies copy-flash to highlight the selection.

The implementation code is shown below.

<Grid
columns={columns}
columnBase={base}
rowSource={ds}
cellSelectionMode="range"
events={useMemo<Grid.Events<GridSpec>>(
() => ({
viewport: {
keyDown: async (ev, vp, api) => {
if (ev.key === "c" && (ev.metaKey || ev.ctrlKey)) {
const rect = api.cellSelections()?.[0];
if (!rect) return;
const v = await api.exportData({ rect });
vp.classList.add("copy-flash");
const asString = v.data.map((x) => `${x.join(", ")}`).join("\n");
await navigator.clipboard.writeText(asString);
setTimeout(() => vp.classList.remove("copy-flash"), 1000);
}
},
},
}),
[],
)}
/>

Controlled Cell Selection

Some use cases require you to manage the cell selection state. Enable controlled selection by passing an array of ranges to the cellSelections prop. Update the selection state via the onCellSelectionChange callback.

The demo below stores cellSelections in React state using useState. It passes the selection to a status bar that displays averages for numeric columns.

Selection Status Bar

Avg. Price:
281.33USD
Fork code on stack blitzFork code on code sandbox

The cellSelections property expects an array of DataRect objects. A DataRect defines a rectangular selection area using the following interface. The rowEnd and columnEnd values are exclusive and are not included in the selected cells.

interface DataRect {
readonly rowStart: number;
readonly rowEnd: number;
readonly columnStart: number;
readonly columnEnd: number;
}

Warning

cellSelections refers to both an API method and a grid property:

  • API Method (api.cellSelections): Returns the current selection rectangles.
  • Property (cellSelections): Controls the selection state.

In controlled selection mode, api.cellSelections returns the same value you pass to cellSelections.

Header Highlights

Headers of columns containing selected cells receive the data-ln-cell-selected="true" attribute. Use this attribute to style those headers. In the demo below, CSS highlights any header whose column contains a selected cell.

Column Header Highlights

Fork code on stack blitzFork code on code sandbox

The demo uses the LyteNyte Grid Tailwind plugin to apply the following Tailwind CSS class:

"ln-header:data-[ln-cell-selected=true]:bg-ln-primary-05"

If you are not using Tailwind, the class is equivalent to the CSS below:

.ln-grid {
[data-ln-header-cell="true"][data-ln-cell-selected="true"] {
background-color: var(--ln-primary-05);
}
}

Excluding Marker Column Selection

The marker column is an internal column that is automatically pinned to the start of the grid. It does not appear in the columns array you pass to the grid.

By default, selection ranges can include the marker column. Set cellSelectionExcludeMarker to true to prevent selection of this column.

Tip

LyteNyte Grid also applies the data-ln-cell-selected attribute to rows that contain selected cells. Use the attribute for row-level styling.

Exclude Marker Column

Fork code on stack blitzFork code on code sandbox

Even with cellSelectionExcludeMarker set to true, the grid still reserves space for the marker column. When the marker column is enabled, it uses column index 0, so the first non-marker column always has column index 1.

Column and Row Selection

To select an entire row or column, set the cellSelections prop to a selection rectangle that covers the target cells. This works regardless of cellSelectionMode. The demo below illustrates this:

  • Column Selection: Click a header to select the entire column.
  • Row Selection: Click a cell in the marker column to select an entire row.

By default, LyteNyte Grid clears selection when focus moves to a non-cell element, such as a header cell. Set cellSelectionMaintainOnNonCellPosition to preserve selection when headers receive focus.

Column / Row Selection

Fork code on stack blitzFork code on code sandbox

Spanned Cell Selection

When a selection includes a spanning cell, LyteNyte Grid expands the selection rectangle to cover the cell’s full row/column span. In the demo below, selecting a spanning cell expands the selected area accordingly.

Cell Selection With Spans

Fork code on stack blitzFork code on code sandbox

Pinned Area Selection

Rows and columns can be pinned to the edges of the grid, keeping them visible while the rest of the grid scrolls.

When you drag a selection from the scrollable area toward a pinned area, the grid includes pinned cells only after the viewport reaches the pinned edge. This ensures the selection range remains continuous:

  • Pinned Start/Top: The grid must be scrolled to the start or top.
  • Pinned End/Bottom: The grid must be scrolled to the end or bottom.

As you drag toward the edge, LyteNyte Grid automatically scrolls the viewport. Once the viewport reaches the pinned edge, the selection expands to include the pinned cells. These constraints only apply when the selection begins outside the pinned area.

The demo includes pinned rows at the top and bottom, and columns at the start and end. Select different areas to see how selection expands into pinned regions.

Pinned Cell Selection

Fork code on stack blitzFork code on code sandbox

When a selection spans pinned areas, the selection rectangle appears visually split. This split indicates that the selection crosses both pinned and scrollable viewport regions. Despite the visual separation, the cellSelections state contains a single, continuous selection rectangle.

Multi-Range Selection

Set cellSelectionMode to "multi-range" to enable multiple selection ranges.

  • Add Range: Hold Control (or Command) and drag to add a new range.
  • Deselect Area: Hold Control (or Command) and start a selection inside an existing range to deselect that area.

Multiple Range Selections

Fork code on stack blitzFork code on code sandbox

Multiple ranges are useful for comparing values across separate cell groups. However, they complicate operations like copying. When multiple ranges exist, the cellSelections state stores multiple selection rectangles, which can make it unclear which range to copy.

Use "range" mode unless your application specifically requires multiple ranges.

Styling Cell Selections

LyteNyte Grid renders inert div overlays to visualize selections. These elements are unstyled by default. To style selection rectangles, target their data attributes.

The CSS below illustrates the approach used by built-in themes. For more information, refer to the Grid Theming guide.

[data-ln-cell-selection-rect]:not([data-ln-cell-selection-is-unit="true"]) {
background-color: var(--ln-primary-10);
box-sizing: border-box;
&[data-ln-cell-selection-is-deselect="true"] {
background-color: var(--ln-red-30);
&[data-ln-cell-selection-border-top="true"],
&[data-ln-cell-selection-border-bottom="true"],
&[data-ln-cell-selection-border-start="true"],
&[data-ln-cell-selection-border-end="true"] {
border-color: var(--ln-red-50);
}
}
&[data-ln-cell-selection-border-top="true"] {
border-top: 1px solid var(--ln-primary-50);
}
&[data-ln-cell-selection-border-bottom="true"] {
border-bottom: 1px solid var(--ln-primary-50);
}
&[data-ln-cell-selection-border-start="true"] {
border-inline-start: 1px solid var(--ln-primary-50);
}
&[data-ln-cell-selection-border-end="true"] {
border-inline-end: 1px solid var(--ln-primary-50);
}
}

Next Steps