LyteNyte Grid PRO lets you open popovers on demand, but leaves the actual UI to your chosen component library. This prevents bundling multiple popover libraries into the same application.
Register a popover frame on the grid using the popoverFrame
property. Open a popover with:
grid.api.popoverFrameOpen(id, target, context?)
. The popover frame with the given id
will be opened.
An optional context for that instance may be provided. The popover frame should be positioned by the target
element.grid.api.popoverFrameClose(id?)
method will then close a popover frame with the given id
.LyteNyte Grid manages the popover state, while the developer is responsible for the rendering using their provided library.
The example below uses the popover component from Base UI to display popovers next to cells when they are clicked.
"use client";
import { useClientRowDataSource, Grid } 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 { Popover } from "@base-ui-components/react/popover";
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 PopoverFrame() {
const ds = useClientRowDataSource({ data: bankDataSmall });
const grid = Grid.useLyteNyte({
gridId: useId(),
rowDataSource: ds,
columns,
popoverFrames: {
myFrame: {
component: (params) => {
return (
<Popover.Root
open
onOpenChange={(b) => !b && params.grid.api.popoverFrameClose()}
key={params.context.columnId + params.context.rowIndex}
>
<Popover.Portal>
<Popover.Positioner anchor={params.target}>
<Popover.Popup className="z-50 border-ln-gray-50 border fixed px-4 rounded bg-ln-gray-10 w-[200px]">
This is my popover frame content. This popover is for for
the cell at row {params.context.rowIndex} with column id{" "}
{params.context.columnId}.
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
);
},
},
},
});
const view = grid.view.useValue();
return (
<div>
<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"
onClick={(e) => {
grid.api.popoverFrameClose();
grid.api.popoverFrameOpen(
"myFrame",
e.currentTarget,
{
rowIndex: c.rowIndex,
columnId: c.column.id,
}
);
}}
/>
);
})}
</Grid.Row>
);
})}
</Grid.RowsCenter>
</Grid.RowsContainer>
</Grid.Viewport>
</Grid.Root>
</div>
</div>
);
}
The key advantage is context management. Defining dialogs at the grid level means:
This approach simplifies integration and ensures popovers work seamlessly with grid-level state and interactions.