LyteNyte Grid lets you organize columns into groups to create visual relationships between related columns. Each column belongs to only one group. Groups can contain nested groups to form hierarchies.
Create column groups in LyteNyte Grid by setting the groupPath
property on columns. The grid builds a hierarchy based on all
groupPath
values in your columns.
"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",
groupPath: ["One Group"],
groupVisibility: "always",
},
{ id: "job", groupPath: ["One Group"], groupVisibility: "always" },
{
id: "balance",
type: "number",
groupPath: ["One Group"],
groupVisibility: "always",
},
{ id: "education" },
{ id: "marital" },
];
export default function ColumnGroups() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
columnBase: {
widthFlex: 1,
},
});
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 (
<Grid.HeaderGroupCell
key={c.idOccurrence}
cell={c}
className="flex items-center gap-2 px-2"
>
<div>{c.groupPath.at(-1)}</div>
</Grid.HeaderGroupCell>
);
}
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>
);
}
The longest path in your hierarchy determines the number of group rows. If a column is not part of any group, or belongs to a group with a shorter path, its header spans the empty group rows.
LyteNyte Grid identifies column groups by the groupPath
property.
Groups appear split if columns from other groups appear between group
members. This creates multiple visual instances of the same logical
group. When you move separated group columns next to each other, the
grid merges them automatically. Pinned columns always appear visually
separate from non-pinned columns, even within the same group.
"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",
groupPath: ["One Group"],
groupVisibility: "always",
},
{
id: "balance",
type: "number",
groupPath: ["One Group"],
groupVisibility: "always",
},
{ id: "education" },
{ id: "marital" },
{ id: "job", groupPath: ["One Group"], groupVisibility: "always" },
];
export default function ColumnGroupsSplit() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
columnBase: {
widthFlex: 1,
},
});
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 (
<Grid.HeaderGroupCell
key={c.idOccurrence}
cell={c}
className="flex items-center gap-2 px-2"
>
<div>{c.groupPath.at(-1)}</div>
</Grid.HeaderGroupCell>
);
}
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>
);
}
Visually split groups still function as a single logical group in grid state. Expanding or collapsing one affects all visual instances.
You can expand or collapse column groups. The groupVisibility
property controls column visibility:
"open"
: Show the column only when the group is expanded."closed"
: Show the column only when the group is collapsed."always"
: Show the column regardless of the group's state.If all columns in a group use "always"
, you cannot collapse the group.
To make a group collapsible, include at least one column hidden when
expanded and one visible when expanded.
"use client";
import { useClientRowDataSource, Grid } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import {
ChevronLeftIcon,
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",
groupPath: ["One Group"],
groupVisibility: "always",
},
{ id: "job", groupPath: ["One Group"] },
{
id: "balance",
type: "number",
groupPath: ["One Group"],
},
{ id: "education" },
{ id: "marital" },
];
export default function ColumnGroupExpansions() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
columnBase: {
widthFlex: 1,
},
});
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 (
<Grid.HeaderGroupCell
key={c.idOccurrence}
cell={c}
className="flex items-center gap-2 px-2 group"
>
<div>{c.groupPath.at(-1)}</div>
<button
className="flex items-center justify-center text-ln-gray-90"
onClick={() => grid.api.columnToggleGroup(c.id)}
>
<ChevronLeftIcon className="hidden group-data-[ln-collapsed=false]:block" />
<ChevronRightIcon className="block group-data-[ln-collapsed=false]:hidden" />
</button>
</Grid.HeaderGroupCell>
);
}
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>
);
}
Configure the initial expansion state of column groups by understanding
how LyteNyte Grid generates group id
s.
The groupPath
array defines the hierarchy. The grid joins the array
using a delimiter (set via columnGroupJoinDelimiter
) to form the group
id
.
The columnGroupExpansions
property controls the initial expansion
state. Keys are group id
s and values are booleans. Groups without an
explicit value use columnGroupDefaultExpansion
(defaults to true
).
"use client";
import { Grid, useClientRowDataSource } from "@1771technologies/lytenyte-pro";
import "@1771technologies/lytenyte-pro/grid.css";
import {
ChevronLeftIcon,
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",
groupPath: ["One Group"],
groupVisibility: "always",
},
{ id: "job", groupPath: ["One Group"] },
{
id: "balance",
type: "number",
groupPath: ["One Group"],
},
{ id: "education" },
{ id: "marital" },
];
export default function ColumnGroupInitialState() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
columnGroupExpansions: {
"One Group": false,
},
columnBase: {
widthFlex: 1,
},
});
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 (
<Grid.HeaderGroupCell
key={c.idOccurrence}
cell={c}
className="flex items-center gap-2 px-2 group"
>
<div>{c.groupPath.at(-1)}</div>
<button
className="flex items-center justify-center text-ln-gray-90"
onClick={() => grid.api.columnToggleGroup(c.id)}
>
<ChevronLeftIcon className="hidden group-data-[ln-collapsed=false]:block" />
<ChevronRightIcon className="block group-data-[ln-collapsed=false]:hidden" />
</button>
</Grid.HeaderGroupCell>
);
}
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>
);
}
Because columnGroupExpansions
, columnGroupDefaultExpansion
, and
columnGroupJoinDelimiter
are part of grid state, you can update them
at runtime:
// Reset all groups to their default expansion state
grid.state.columnGroupExpansions.set({});
Avoid changing columnGroupJoinDelimiter
after setup, since the grid
won't update existing group ids automatically.
Call the columnToggleGroup
API method to toggle a group's expansion
state programmatically.