LyteNyte Grid supports flexible row selection, including single and multiple selection modes. Users can select rows through clicks, checkboxes, or both.
Set the rowSelectionMode
property to "single"
or "multiple"
. To enable click-based selection,
set rowSelectionActivator
to "single-click"
or "double-click"
.
"use client";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import type { Column } from "@1771technologies/lytenyte-pro/types";
import { bankDataSmall } from "@1771technologies/sample-data/bank-data-smaller";
import { useId } from "react";
type BankData = (typeof bankDataSmall)[number];
const columns: Column<BankData>[] = [
{ id: "age", type: "number" },
{ id: "job" },
{ id: "balance", type: "number" },
{ id: "education" },
{ id: "marital" },
{ id: "default" },
{ id: "housing" },
{ id: "loan" },
{ id: "contact" },
{ id: "day", type: "number" },
{ id: "month" },
{ id: "duration" },
{ id: "campaign" },
{ id: "pdays" },
{ id: "previous" },
{ id: "poutcome" },
{ id: "y" },
];
export default function RowSelection() {
const ds = useClientRowDataSource({
data: bankDataSmall,
});
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
rowSelectionMode: "single",
rowSelectionActivator: "single-click",
});
const view = grid.view.useValue();
return (
<div className="lng-grid" style={{ height: 500 }}>
<Grid.Root grid={grid}>
<Grid.Viewport>
<Grid.Header>
{view.header.layout.map((row, i) => {
return (
<Grid.HeaderRow key={i} headerRowIndex={i}>
{row.map((c) => {
if (c.kind === "group") return null;
return (
<Grid.HeaderCell
key={c.id}
cell={c}
className="flex w-full h-full capitalize px-2 items-center"
/>
);
})}
</Grid.HeaderRow>
);
})}
</Grid.Header>
<Grid.RowsContainer>
<Grid.RowsCenter>
{view.rows.center.map((row) => {
if (row.kind === "full-width") return null;
return (
<Grid.Row row={row} key={row.id}>
{row.cells.map((c) => {
return (
<Grid.Cell
key={c.id}
cell={c}
className="text-sm flex items-center px-2 h-full w-full"
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
);
}
Set rowSelectionMode
to "multiple"
to allow selecting multiple rows. Clicking a row toggles its
selection. Hold Shift and click another row to select the range between it and the last selected
row.
"use client";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import type { Column } from "@1771technologies/lytenyte-pro/types";
import { bankDataSmall } from "@1771technologies/sample-data/bank-data-smaller";
import { useId } from "react";
type BankData = (typeof bankDataSmall)[number];
const columns: Column<BankData>[] = [
{ id: "age", type: "number" },
{ id: "job" },
{ id: "balance", type: "number" },
{ id: "education" },
{ id: "marital" },
{ id: "default" },
{ id: "housing" },
{ id: "loan" },
{ id: "contact" },
{ id: "day", type: "number" },
{ id: "month" },
{ id: "duration" },
{ id: "campaign" },
{ id: "pdays" },
{ id: "previous" },
{ id: "poutcome" },
{ id: "y" },
];
export default function RowSelectionMulti() {
const ds = useClientRowDataSource({
data: bankDataSmall,
});
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
rowSelectionMode: "multiple",
rowSelectionActivator: "single-click",
});
const view = grid.view.useValue();
return (
<div className="lng-grid" style={{ height: 500 }}>
<Grid.Root grid={grid}>
<Grid.Viewport>
<Grid.Header>
{view.header.layout.map((row, i) => {
return (
<Grid.HeaderRow key={i} headerRowIndex={i}>
{row.map((c) => {
if (c.kind === "group") return null;
return (
<Grid.HeaderCell
key={c.id}
cell={c}
className="flex w-full h-full capitalize px-2 items-center"
/>
);
})}
</Grid.HeaderRow>
);
})}
</Grid.Header>
<Grid.RowsContainer>
<Grid.RowsCenter>
{view.rows.center.map((row) => {
if (row.kind === "full-width") return null;
return (
<Grid.Row row={row} key={row.id}>
{row.cells.map((c) => {
return (
<Grid.Cell
key={c.id}
cell={c}
className="text-sm flex items-center px-2 h-full w-full"
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
);
}
Group rows create hierarchical relationships with their child rows. Control whether selecting a
group row also selects its children with the rowSelectChildren
property.
"use client";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import {
ChevronDownIcon,
ChevronRightIcon,
} from "@1771technologies/lytenyte-pro/icons";
import type { Column } from "@1771technologies/lytenyte-pro/types";
import { bankDataSmall } from "@1771technologies/sample-data/bank-data-smaller";
import { useId } from "react";
type BankData = (typeof bankDataSmall)[number];
const columns: Column<BankData>[] = [
{ id: "age", type: "number" },
{ id: "job" },
{ id: "balance", type: "number" },
{ id: "education" },
{ id: "marital" },
{ id: "default" },
{ id: "housing" },
{ id: "loan" },
{ id: "contact" },
{ id: "day", type: "number" },
{ id: "month" },
{ id: "duration" },
{ id: "campaign" },
{ id: "pdays" },
{ id: "previous" },
{ id: "poutcome" },
{ id: "y" },
];
export default function RowSelectionGroups() {
const ds = useClientRowDataSource({
data: bankDataSmall,
});
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
rowGroupModel: ["job", "education"],
rowSelectionMode: "multiple",
rowSelectChildren: true,
rowSelectedIds: new Set(["root:services"]),
rowGroupColumn: {
cellRenderer: ({ grid, row, column }) => {
if (!grid.api.rowIsGroup(row)) return null;
const field = grid.api.columnField(column, row);
const isExpanded = grid.api.rowGroupIsExpanded(row);
return (
<div
className="flex items-center gap-2 w-full h-full"
style={{ paddingLeft: row.depth * 16 }}
>
<button
className="flex items-center justify-center"
onClick={(e) => {
e.stopPropagation();
grid.api.rowGroupToggle(row);
}}
>
{!isExpanded && <ChevronRightIcon />}
{isExpanded && <ChevronDownIcon />}
</button>
<div>{`${field}`}</div>
</div>
);
},
},
rowGroupExpansions: {
"root:services": true,
"root:services/secondary": true,
},
});
const view = grid.view.useValue();
return (
<div className="lng-grid" style={{ height: 500 }}>
<Grid.Root grid={grid}>
<Grid.Viewport>
<Grid.Header>
{view.header.layout.map((row, i) => {
return (
<Grid.HeaderRow key={i} headerRowIndex={i}>
{row.map((c) => {
if (c.kind === "group") return null;
return (
<Grid.HeaderCell
key={c.id}
cell={c}
className="flex w-full h-full capitalize px-2 items-center"
/>
);
})}
</Grid.HeaderRow>
);
})}
</Grid.Header>
<Grid.RowsContainer>
<Grid.RowsCenter>
{view.rows.center.map((row) => {
if (row.kind === "full-width") return null;
return (
<Grid.Row row={row} key={row.id}>
{row.cells.map((c) => {
return (
<Grid.Cell
key={c.id}
cell={c}
className="text-sm flex items-center px-2 h-full w-full"
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
);
}
When combining selection with group toggle buttons, stop event propagation to avoid triggering both
actions. Alternatively, set selection to "double-click"
or implement your own checkbox selection
logic.
"use client";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import type { Column } from "@1771technologies/lytenyte-pro/types";
import { bankDataSmall } from "@1771technologies/sample-data/bank-data-smaller";
import { useId } from "react";
type BankData = (typeof bankDataSmall)[number];
const columns: Column<BankData>[] = [
{ id: "age", type: "number" },
{ id: "job" },
{ id: "balance", type: "number" },
{ id: "education" },
{ id: "marital" },
{ id: "default" },
{ id: "housing" },
{ id: "loan" },
{ id: "contact" },
{ id: "day", type: "number" },
{ id: "month" },
{ id: "duration" },
{ id: "campaign" },
{ id: "pdays" },
{ id: "previous" },
{ id: "poutcome" },
{ id: "y" },
];
export default function RowSelectionCheckbox() {
const ds = useClientRowDataSource({
data: bankDataSmall,
});
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
columnMarkerEnabled: true,
columnMarker: {
cellRenderer: ({ rowSelected, grid }) => {
return (
<div className="flex w-full h-full items-center justify-center">
<input
type="checkbox"
checked={rowSelected}
onChange={() => {}}
onClick={(e) => {
grid.api.rowHandleSelect({
target: e.currentTarget,
shiftKey: e.shiftKey,
});
}}
/>
</div>
);
},
},
rowSelectionMode: "multiple",
rowSelectionActivator: "none",
});
const view = grid.view.useValue();
return (
<div className="lng-grid" style={{ height: 500 }}>
<Grid.Root grid={grid}>
<Grid.Viewport>
<Grid.Header>
{view.header.layout.map((row, i) => {
return (
<Grid.HeaderRow key={i} headerRowIndex={i}>
{row.map((c) => {
if (c.kind === "group") return null;
return (
<Grid.HeaderCell
key={c.id}
cell={c}
className="flex w-full h-full capitalize px-2 items-center"
/>
);
})}
</Grid.HeaderRow>
);
})}
</Grid.Header>
<Grid.RowsContainer>
<Grid.RowsCenter>
{view.rows.center.map((row) => {
if (row.kind === "full-width") return null;
return (
<Grid.Row row={row} key={row.id}>
{row.cells.map((c) => {
return (
<Grid.Cell
key={c.id}
cell={c}
className="text-sm flex items-center px-2 h-full w-full"
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
);
}
Use grid.api.rowHandleSelect
to simplify selection handling in components. This method supports
shift-based range selection out of the box.
<input
type="checkbox"
checked={rowSelected}
onChange={() => {}}
onClick={(e) => {
grid.api.rowHandleSelect({
target: e.currentTarget,
shiftKey: e.shiftKey,
});
}}
/>
Attach the selection logic to the onClick
event to easily detect Shift key presses.
To block selection for certain rows, listen for the rowSelectBegin
event and call
preventDefault()
. In the example below, odd-numbered rows cannot be selected.
See the event guide for more on handling grid events.
"use client";
import "@1771technologies/lytenyte-pro/grid.css";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import type { Column } from "@1771technologies/lytenyte-pro/types";
import { bankDataSmall } from "@1771technologies/sample-data/bank-data-smaller";
import { useId } from "react";
type BankData = (typeof bankDataSmall)[number];
const columns: Column<BankData>[] = [
{ id: "age", type: "number" },
{ id: "job" },
{ id: "balance", type: "number" },
{ id: "education" },
{ id: "marital" },
{ id: "default" },
{ id: "housing" },
{ id: "loan" },
{ id: "contact" },
{ id: "day", type: "number" },
{ id: "month" },
{ id: "duration" },
{ id: "campaign" },
{ id: "pdays" },
{ id: "previous" },
{ id: "poutcome" },
{ id: "y" },
];
export default function RowSelectionPreventing() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
rowSelectionMode: "multiple",
rowSelectionActivator: "single-click",
});
const view = grid.view.useValue();
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<div style={{ height: 500 }}>
<Grid.Root
grid={grid}
onRowSelectBegin={({ preventDefault, selected }) => {
if (Number.parseInt(selected) % 2) preventDefault();
}}
>
<Grid.Viewport>
<Grid.Header>
{view.header.layout.map((row, i) => {
return (
<Grid.HeaderRow key={i} headerRowIndex={i}>
{row.map((c) => {
if (c.kind === "group") return null;
return (
<Grid.HeaderCell
key={c.id}
cell={c}
className="flex w-full h-full capitalize px-2 items-center"
/>
);
})}
</Grid.HeaderRow>
);
})}
</Grid.Header>
<Grid.RowsContainer>
<Grid.RowsCenter>
{view.rows.center.map((row) => {
if (row.kind === "full-width") return null;
return (
<Grid.Row row={row} key={row.id}>
{row.cells.map((c) => {
return (
<Grid.Cell
key={c.id}
cell={c}
className="text-sm flex items-center px-2 h-full w-full"
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
</div>
);
}