Row Sorting
Configure and control row sorting in LyteNyte Grid using sort models, column configurations, and programmatic API methods.
LyteNyte Grid sorts rows based on the sortModel property in grid state. The row data source you set
on the grid performs the actual sorting.
Sort Model
The sortModel is an array of sort configurations. LyteNyte Grid supports three standard sort types and
one custom type.
Example:
{ columnId: "age", sort: { kind: "number" }, isDescending: false },
Each sort configuration includes:
columnId- The column to sort.sort.kind- The sort type:"number""string""date""custom"- Requires a custom comparator function.
isDescending- Boolean indicating sort direction.
Row Sort Model
"use client";import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";import "@1771technologies/lytenyte-pro/grid.css";import { ArrowDownIcon, ArrowUpIcon } from "@1771technologies/lytenyte-pro/icons";import type {Column,HeaderCellRendererParams,SortModelItem,} from "@1771technologies/lytenyte-pro/types";import { bankDataSmall } from "@1771technologies/grid-sample-data/bank-data-smaller";import { useId } from "react";type BankData = (typeof bankDataSmall)[number];const columns: Column<BankData>[] = [{ id: "education" },{ id: "marital" },{ id: "age", type: "number" },{ id: "job" },{ id: "balance", type: "number" },{ id: "default" },{ id: "housing" },{ id: "loan" },{ id: "contact" },{ id: "day", type: "number" },{ id: "month" },{ id: "duration" },{ id: "campaign" },{ id: "pdays" },{ id: "previous" },{ id: "poutcome", name: "P Outcome" },{ id: "y" },];export default function RowSorting() {const ds = useClientRowDataSource({data: bankDataSmall,});const grid = Grid.useLyteNyte({gridId: useId(),rowDataSource: ds,columns,columnBase: {headerRenderer: Header,},sortModel: [{ columnId: "age", sort: { kind: "number" }, isDescending: false }],});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.HeaderCellkey={c.id}cell={c}className="flex h-full w-full items-center px-2 capitalize"/>);})}</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.Cellkey={c.id}cell={c}className="flex h-full w-full items-center px-2 text-sm"/>);})}</Grid.Row>);})}</Grid.RowsCenter></Grid.RowsContainer></Grid.Viewport></Grid.Root></div>);}function Header({ column, grid }: HeaderCellRendererParams<BankData>) {const sort = grid.state.sortModel.useValue().find((c) => c.columnId === column.id);const isDescending = sort?.isDescending ?? false;return (<divclassName="hover:bg-ln-gray-10 flex h-full w-full items-center px-2 text-sm transition-all"onClick={() => {const current = grid.api.sortForColumn(column.id);if (current == null) {let sort: SortModelItem<BankData>;const columnId = column.id;if (column.type === "datetime") {sort = {columnId,sort: { kind: "date", options: { includeTime: true } },};} else if (column.type === "number") {sort = { columnId, sort: { kind: "number" } };} else {sort = { columnId, sort: { kind: "string" } };}grid.state.sortModel.set([sort]);return;}if (!current.sort.isDescending) {grid.state.sortModel.set([{ ...current.sort, isDescending: true }]);} else {grid.state.sortModel.set([]);}}}>{column.name ?? column.id}{sort && (<>{!isDescending ? (<ArrowUpIcon className="size-4" />) : (<ArrowDownIcon className="size-4" />)}</>)}</div>);}
Multi-Column Sort
Because sortModel is an array, you can define multiple sort rules. This creates a multi-way sort:
- The first item in the array takes priority.
- If values are equal, the next sort rule is applied, and so on.
In the example below hold Ctrl (Windows/Linux) or Command (macOS) when clicking a header
to add it to the sorting list. See the Header component in the
example for one way to implement this.
If header clicks are reserved for another action or feel unintuitive, use the Sort Manager component for a dedicated multi-sort UI.
Row Sort Model Multi
"use client";import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";import "@1771technologies/lytenyte-pro/grid.css";import { ArrowDownIcon, ArrowUpIcon } from "@1771technologies/lytenyte-pro/icons";import type {Column,HeaderCellRendererParams,SortModelItem,} from "@1771technologies/lytenyte-pro/types";import { bankDataSmall } from "@1771technologies/grid-sample-data/bank-data-smaller";import { useId } from "react";type BankData = (typeof bankDataSmall)[number];const columns: Column<BankData>[] = [{ id: "education" },{ id: "marital" },{ id: "age", type: "number" },{ id: "job" },{ id: "balance", type: "number" },{ id: "default" },{ id: "housing" },{ id: "loan" },{ id: "contact" },{ id: "day", type: "number" },{ id: "month" },{ id: "duration" },{ id: "campaign" },{ id: "pdays" },{ id: "previous" },{ id: "poutcome", name: "P Outcome" },{ id: "y" },];export default function RowSortingMulti() {const ds = useClientRowDataSource({data: bankDataSmall,});const grid = Grid.useLyteNyte({gridId: useId(),rowDataSource: ds,columns,columnBase: {headerRenderer: Header,},sortModel: [{ columnId: "education", sort: { kind: "string" }, isDescending: false },{ columnId: "age", sort: { kind: "number" }, isDescending: 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.HeaderCellkey={c.id}cell={c}className="flex h-full w-full items-center px-2 capitalize"/>);})}</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.Cellkey={c.id}cell={c}className="flex h-full w-full items-center px-2 text-sm"/>);})}</Grid.Row>);})}</Grid.RowsCenter></Grid.RowsContainer></Grid.Viewport></Grid.Root></div>);}function Header({ column, grid }: HeaderCellRendererParams<BankData>) {const sortModel = grid.state.sortModel.useValue();const sortIndex = sortModel.findIndex((c) => c.columnId === column.id);const sort = sortIndex === -1 ? null : sortModel[sortIndex];const isDescending = sort?.isDescending ?? false;return (<divclassName="hover:bg-ln-gray-10 flex h-full w-full items-center px-2 text-sm transition-all"onClick={(ev) => {const current = grid.api.sortForColumn(column.id);const isAdditive = ev.ctrlKey || ev.metaKey;let sort: SortModelItem<BankData> | null = null;if (current == null) {const columnId = column.id;if (column.type === "datetime") {sort = {columnId,sort: { kind: "date", options: { includeTime: true } },};} else if (column.type === "number") {sort = { columnId, sort: { kind: "number" } };} else {sort = { columnId, sort: { kind: "string" } };}} else {// If additive, we allow the current sort direction to continuously flip direction rather than being removedif (isAdditive) {sort = {...current.sort,isDescending: !current.sort.isDescending,};} else if (!current.sort.isDescending) {sort = {...current.sort,isDescending: true,};}}if (isAdditive) {// Remove the sort from the current list if the next sort in the cycle is nullif (!sort) {const nextSortModel = sortModel.filter((x) => x !== sort);grid.state.sortModel.set(nextSortModel);return;}// Otherwise add the next sort to the list. If this is a new sort we push// it to the endconst nextSortModel = [...sortModel];if (sortIndex === -1) nextSortModel.push(sort);else nextSortModel[sortIndex] = sort;grid.state.sortModel.set(nextSortModel);} else {grid.state.sortModel.set(sort ? [sort] : []);}}}>{column.name ?? column.id}{sort && (<div className="relative">{!isDescending ? (<ArrowUpIcon className="size-4" />) : (<ArrowDownIcon className="size-4" />)}{sortIndex >= 0 && sortModel.length > 1 && (<div className="text-ln-primary-5 absolute right-[-2px] top-[-2px] text-xs">{sortIndex + 1}</div>)}</div>)}</div>);}
Row Selection
Configure and manage single and multiple row selection in LyteNyte Grid with checkboxes, click interactions, and programmatic control.
Row Detail
Create expandable detail sections beneath rows in LyteNyte Grid with customizable React components, expansion controls, and programmatic API methods.