Client Row Filtering
LyteNyte Grid's client row source lets you to set custom filters to exclude specific rows from view. These filtered rows are omitted before actions such as grouping, sorting, or aggregating are executed.
Info
This guide offers an overview of filters, focusing on the client row source properties. For detailed guidance on Filtering, refer to our Filtering Section, beginning with the Filter Text guide.
Filter Property
The filter property on the client row source accepts a filter predicate or an
array of filter predicates. A filter predicate is a function that receives a
leaf row and returns true if the grid should keep the row or false if the
grid should filter the row out.
A basic filter function is shown below. Click the Mastercard button to view only Mastercard payments, or the Visa button to view only Visa payments.
Client Row Filter Function
16 collapsed lines
1import "@1771technologies/lytenyte-pro/light-dark.css";2import "@1771technologies/lytenyte-pro/components.css";3import type { OrderData } from "@1771technologies/grid-sample-data/orders";4import { data } from "@1771technologies/grid-sample-data/orders";5import {6 AvatarCell,7 EmailCell,8 IdCell,9 PaymentMethodCell,10 PriceCell,11 ProductCell,12 PurchaseDateCell,13} from "./components.jsx";14import { useClientDataSource, Grid } from "@1771technologies/lytenyte-pro";15import { useState } from "react";16import { ViewportShadows } from "@1771technologies/lytenyte-pro/components";17
18export interface GridSpec {19 readonly data: OrderData;20}21
22const columns: Grid.Column<GridSpec>[] = [23 { id: "id", width: 60, widthMin: 60, cellRenderer: IdCell, name: "ID" },24 { id: "product", cellRenderer: ProductCell, width: 200, name: "Product" },25 { id: "price", type: "number", cellRenderer: PriceCell, width: 100, name: "Price" },26 { id: "customer", cellRenderer: AvatarCell, width: 180, name: "Customer" },27 { id: "purchaseDate", cellRenderer: PurchaseDateCell, name: "Purchase Date", width: 130 },28 { id: "paymentMethod", cellRenderer: PaymentMethodCell, name: "Payment Method", width: 150 },29 { id: "email", cellRenderer: EmailCell, width: 220, name: "Email" },30];31
32export default function ClientDemo() {33 const [filter, setFilter] = useState<Grid.T.FilterFn<GridSpec["data"]> | null>(null);34 const ds = useClientDataSource<GridSpec>({ data: data, filter });35
36 return (37 <>38 <div className="border-ln-border flex gap-4 border-b px-4 py-3">39 <button40 data-ln-button="website"41 data-ln-size="md"42 onClick={() => {43 setFilter(44 () => (row: Grid.T.RowLeaf<GridSpec["data"]>) => row.data.paymentMethod === "Mastercard",45 );46 }}47 >48 Mastercard49 </button>50 <button51 data-ln-button="website"52 data-ln-size="md"53 onClick={() => {54 setFilter(() => (row: Grid.T.RowLeaf<GridSpec["data"]>) => row.data.paymentMethod === "Visa");55 }}56 >57 Visa58 </button>59 <button60 data-ln-button="website"61 data-ln-size="md"62 onClick={() => {63 setFilter(null);64 }}65 >66 Clear Filters67 </button>68 </div>69
70 <div className="ln-grid">71 <div className={"ln-grid"} style={{ height: 500 }}>72 <Grid rowHeight={50} columns={columns} rowSource={ds} slotShadows={ViewportShadows} />73 </div>74 </div>75 </>76 );77}1import { format } from "date-fns";2import { type JSX, type ReactNode } from "react";3import type { Grid } from "@1771technologies/lytenyte-pro";4import type { GridSpec } from "./demo.jsx";5
6export function ProductCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {7 if (!api.rowIsLeaf(row) || !row.data) return;8
9 const url = row.data?.productThumbnail;10 const title = row.data.product;11 const desc = row.data.productDescription;12
13 return (14 <div className="flex h-full w-full items-center gap-2">15 <img className="border-ln-border-strong h-7 w-7 rounded-lg border" src={url} alt={title + desc} />16 <div className="text-ln-text-dark flex flex-col gap-0.5">17 <div className="font-semibold">{title}</div>18 <div className="text-ln-text-light text-xs">{desc}</div>19 </div>20 </div>21 );22}23
24export function AvatarCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {25 if (!api.rowIsLeaf(row) || !row.data) return;26
27 const url = row.data?.customerAvatar;28
29 const name = row.data.customer;30
31 return (32 <div className="flex h-full w-full items-center gap-2">33 <img className="border-ln-border-strong h-7 w-7 rounded-full border" src={url} alt={name} />34 <div className="text-ln-text-dark flex flex-col gap-0.5">35 <div>{name}</div>36 </div>37 </div>38 );39}40
41const formatter = new Intl.NumberFormat("en-Us", {42 minimumFractionDigits: 2,43 maximumFractionDigits: 2,44});45export function PriceCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {46 if (!api.rowIsLeaf(row) || !row.data) return;47
48 const price = formatter.format(row.data.price);49 const [dollars, cents] = price.split(".");50
51 return (52 <div className="flex h-full w-full items-center justify-end">53 <div className="flex items-baseline tabular-nums">54 <span className="text-ln-text font-semibold">${dollars}</span>.55 <span className="relative text-xs">{cents}</span>56 </div>57 </div>58 );59}60
61export function PurchaseDateCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {62 if (!api.rowIsLeaf(row) || !row.data) return;63
64 const formattedDate = format(row.data.purchaseDate, "dd MMM, yyyy");65
66 return <div className="flex h-full w-full items-center">{formattedDate}</div>;67}68export function IdCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {69 if (!api.rowIsLeaf(row) || !row.data) return;70
71 return <div className="text-xs tabular-nums">{row.data.id}</div>;72}73
74export function PaymentMethodCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {75 if (!api.rowIsLeaf(row) || !row.data) return;76
77 const cardNumber = row.data.cardNumber;78 const provider = row.data.paymentMethod;79
80 let Logo: ReactNode = null;81 if (provider === "Visa") Logo = <VisaLogo className="w-6" />;82 if (provider === "Mastercard") Logo = <MastercardLogo className="w-6" />;83
84 return (85 <div className="flex h-full w-full items-center gap-2">86 <div className="flex w-7 items-center justify-center">{Logo}</div>87 <div className="flex items-center gap-px">88 <div className="bg-ln-gray-40 size-2 rounded-full"></div>89 <div className="bg-ln-gray-40 size-2 rounded-full"></div>90 <div className="bg-ln-gray-40 size-2 rounded-full"></div>91 <div className="bg-ln-gray-40 size-2 rounded-full"></div>92 </div>93 <div className="tabular-nums">{cardNumber}</div>94 </div>95 );96}97
98export function EmailCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {99 if (!api.rowIsLeaf(row) || !row.data) return;100
101 return <div className="text-ln-primary-50 flex h-full w-full items-center">{row.data.email}</div>;102}103
104const VisaLogo = (props: JSX.IntrinsicElements["svg"]) => (105 <svg xmlns="http://www.w3.org/2000/svg" width={2500} height={812} viewBox="0.5 0.5 999 323.684" {...props}>106 <path107 fill="#1434cb"108 d="M651.185.5c-70.933 0-134.322 36.766-134.322 104.694 0 77.9 112.423 83.28 112.423 122.415 0 16.478-18.884 31.229-51.137 31.229-45.773 0-79.984-20.611-79.984-20.611l-14.638 68.547s39.41 17.41 91.734 17.41c77.552 0 138.576-38.572 138.576-107.66 0-82.316-112.89-87.537-112.89-123.86 0-12.91 15.501-27.053 47.662-27.053 36.286 0 65.892 14.99 65.892 14.99l14.326-66.204S696.614.5 651.185.5zM2.218 5.497.5 15.49s29.842 5.461 56.719 16.356c34.606 12.492 37.072 19.765 42.9 42.353l63.51 244.832h85.138L379.927 5.497h-84.942L210.707 218.67l-34.39-180.696c-3.154-20.68-19.13-32.477-38.685-32.477H2.218zm411.865 0L347.449 319.03h80.999l66.4-313.534h-80.765zm451.759 0c-19.532 0-29.88 10.457-37.474 28.73L709.699 319.03h84.942l16.434-47.468h103.483l9.994 47.468H999.5L934.115 5.497h-68.273zm11.047 84.707 25.178 117.653h-67.454z"109 />110 </svg>111);112
113const MastercardLogo = (props: JSX.IntrinsicElements["svg"]) => (114 <svg115 xmlns="http://www.w3.org/2000/svg"116 width={2500}117 height={1524}118 viewBox="55.2 38.3 464.5 287.8"119 {...props}120 >121 <path122 fill="#f79f1a"123 d="M519.7 182.2c0 79.5-64.3 143.9-143.6 143.9s-143.6-64.4-143.6-143.9S296.7 38.3 376 38.3s143.7 64.4 143.7 143.9z"124 />125 <path126 fill="#ea001b"127 d="M342.4 182.2c0 79.5-64.3 143.9-143.6 143.9S55.2 261.7 55.2 182.2 119.5 38.3 198.8 38.3s143.6 64.4 143.6 143.9z"128 />129 <path130 fill="#ff5f01"131 d="M287.4 68.9c-33.5 26.3-55 67.3-55 113.3s21.5 87 55 113.3c33.5-26.3 55-67.3 55-113.3s-21.5-86.9-55-113.3z"132 />133 </svg>134);The demo creates a filter function that is passed to the client source, as shown below. This minimal approach supports any filter model, since filtering is an arbitrary predicate function.
1const [filter, setFilter] = useState<Grid.T.FilterFn<GridSpec["data"]> | null>(null);2
3const ds = useClientDataSource<GridSpec>({ data: data, filter });4
5// Later in the code6
7<button8 onClick={() => {9 setFilter(10 () => (row: Grid.T.RowLeaf<GridSpec["data"]>) => row.data.paymentMethod === "Mastercard",11 );12 }}13>Filter Array
Providing a single filter function is cumbersome when filters originate from different sources.
For example, if you support per-column text search, you can write one filter function
per column and pass those column filter functions to useClientDataSource.
Filter Function Array
39 collapsed lines
1import "@1771technologies/lytenyte-pro/light-dark.css";2import type { OrderData } from "@1771technologies/grid-sample-data/orders";3import { data } from "@1771technologies/grid-sample-data/orders";4import {5 AvatarCell,6 EmailCell,7 FloatingFilter,8 IdCell,9 PaymentMethodCell,10 PriceCell,11 ProductCell,12 PurchaseDateCell,13} from "./components.jsx";14import {15 useClientDataSource,16 Grid,17 type PieceWritable,18 usePiece,19} from "@1771technologies/lytenyte-pro";20import { useMemo, useState } from "react";21
22const columns: Grid.Column<GridSpec>[] = [23 { id: "id", width: 60, widthMin: 60, cellRenderer: IdCell, name: "ID" },24 { id: "product", cellRenderer: ProductCell, width: 200, name: "Product", type: "string" },25 { id: "price", type: "number", cellRenderer: PriceCell, width: 100, name: "Price" },26 { id: "customer", cellRenderer: AvatarCell, width: 180, name: "Customer", type: "string" },27 { id: "purchaseDate", cellRenderer: PurchaseDateCell, name: "Purchase Date", width: 130 },28 { id: "paymentMethod", cellRenderer: PaymentMethodCell, name: "Payment Method", width: 150 },29 { id: "email", cellRenderer: EmailCell, width: 220, name: "Email", type: "string" },30];31
32export interface GridSpec {33 readonly data: OrderData;34 readonly api: {35 readonly filterModel: PieceWritable<Record<string, string | null>>;36 };37}38
39const base: Grid.ColumnBase<GridSpec> = { floatingCellRenderer: FloatingFilter };40
41export default function ClientDemo() {42 const [filterModel, setFilterModel] = useState<Record<string, string | null>>({});43
44 const filter$ = usePiece(filterModel, setFilterModel);45 const apiExtension: GridSpec["api"] = useMemo(() => {46 return {47 filterModel: filter$,48 };49 }, [filter$]);50
51 const filterFunctions = useMemo(() => {52 const entries = Object.entries(filterModel);53
54 return entries.map<Grid.T.FilterFn<GridSpec["data"]>>(([key, value]) => {55 return (row: Grid.T.RowLeaf<GridSpec["data"]>) => {56 const data = row.data[key as keyof OrderData];57
58 if (!data) return false;59 if (!`${data}`.toLowerCase().includes(value?.toLowerCase() ?? "")) return false;60
61 return true;62 };63 });64 }, [filterModel]);65
66 const ds = useClientDataSource<GridSpec>({ data: data, filter: filterFunctions });67
68 return (69 <div className={"ln-grid"} style={{ height: 500 }}>70 <Grid71 apiExtension={apiExtension}72 rowHeight={50}73 columns={columns}74 columnBase={base}75 rowSource={ds}76 floatingRowEnabled77 />78 </div>79 );80}1import { format } from "date-fns";2import { type JSX, type ReactNode } from "react";3import type { Grid } from "@1771technologies/lytenyte-pro";4import type { GridSpec } from "./demo.jsx";5
6export function ProductCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {7 if (!api.rowIsLeaf(row) || !row.data) return;8
9 const url = row.data?.productThumbnail;10 const title = row.data.product;11 const desc = row.data.productDescription;12
13 return (14 <div className="flex h-full w-full items-center gap-2">15 <img className="border-ln-border-strong h-7 w-7 rounded-lg border" src={url} alt={title + desc} />16 <div className="text-ln-text-dark flex flex-col gap-0.5">17 <div className="font-semibold">{title}</div>18 <div className="text-ln-text-light text-xs">{desc}</div>19 </div>20 </div>21 );22}23
24export function AvatarCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {25 if (!api.rowIsLeaf(row) || !row.data) return;26
27 const url = row.data?.customerAvatar;28
29 const name = row.data.customer;30
31 return (32 <div className="flex h-full w-full items-center gap-2">33 <img className="border-ln-border-strong h-7 w-7 rounded-full border" src={url} alt={name} />34 <div className="text-ln-text-dark flex flex-col gap-0.5">35 <div>{name}</div>36 </div>37 </div>38 );39}40
41const formatter = new Intl.NumberFormat("en-Us", {42 minimumFractionDigits: 2,43 maximumFractionDigits: 2,44});45export function PriceCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {46 if (!api.rowIsLeaf(row) || !row.data) return;47
48 const price = formatter.format(row.data.price);49 const [dollars, cents] = price.split(".");50
51 return (52 <div className="flex h-full w-full items-center justify-end">53 <div className="flex items-baseline tabular-nums">54 <span className="text-ln-text font-semibold">${dollars}</span>.55 <span className="relative text-xs">{cents}</span>56 </div>57 </div>58 );59}60
61export function PurchaseDateCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {62 if (!api.rowIsLeaf(row) || !row.data) return;63
64 const formattedDate = format(row.data.purchaseDate, "dd MMM, yyyy");65
66 return <div className="flex h-full w-full items-center">{formattedDate}</div>;67}68export function IdCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {69 if (!api.rowIsLeaf(row) || !row.data) return;70
71 return <div className="text-xs tabular-nums">{row.data.id}</div>;72}73
74export function PaymentMethodCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {75 if (!api.rowIsLeaf(row) || !row.data) return;76
77 const cardNumber = row.data.cardNumber;78 const provider = row.data.paymentMethod;79
80 let Logo: ReactNode = null;81 if (provider === "Visa") Logo = <VisaLogo className="w-6" />;82 if (provider === "Mastercard") Logo = <MastercardLogo className="w-6" />;83
84 return (85 <div className="flex h-full w-full items-center gap-2">86 <div className="flex w-7 items-center justify-center">{Logo}</div>87 <div className="flex items-center gap-px">88 <div className="bg-ln-gray-40 size-2 rounded-full"></div>89 <div className="bg-ln-gray-40 size-2 rounded-full"></div>90 <div className="bg-ln-gray-40 size-2 rounded-full"></div>91 <div className="bg-ln-gray-40 size-2 rounded-full"></div>92 </div>93 <div className="tabular-nums">{cardNumber}</div>94 </div>95 );96}97
98export function EmailCell({ api, row }: Grid.T.CellRendererParams<GridSpec>) {99 if (!api.rowIsLeaf(row) || !row.data) return;100
101 return <div className="text-ln-primary-50 flex h-full w-full items-center">{row.data.email}</div>;102}103
104const VisaLogo = (props: JSX.IntrinsicElements["svg"]) => (105 <svg xmlns="http://www.w3.org/2000/svg" width={2500} height={812} viewBox="0.5 0.5 999 323.684" {...props}>106 <path107 fill="#1434cb"108 d="M651.185.5c-70.933 0-134.322 36.766-134.322 104.694 0 77.9 112.423 83.28 112.423 122.415 0 16.478-18.884 31.229-51.137 31.229-45.773 0-79.984-20.611-79.984-20.611l-14.638 68.547s39.41 17.41 91.734 17.41c77.552 0 138.576-38.572 138.576-107.66 0-82.316-112.89-87.537-112.89-123.86 0-12.91 15.501-27.053 47.662-27.053 36.286 0 65.892 14.99 65.892 14.99l14.326-66.204S696.614.5 651.185.5zM2.218 5.497.5 15.49s29.842 5.461 56.719 16.356c34.606 12.492 37.072 19.765 42.9 42.353l63.51 244.832h85.138L379.927 5.497h-84.942L210.707 218.67l-34.39-180.696c-3.154-20.68-19.13-32.477-38.685-32.477H2.218zm411.865 0L347.449 319.03h80.999l66.4-313.534h-80.765zm451.759 0c-19.532 0-29.88 10.457-37.474 28.73L709.699 319.03h84.942l16.434-47.468h103.483l9.994 47.468H999.5L934.115 5.497h-68.273zm11.047 84.707 25.178 117.653h-67.454z"109 />110 </svg>111);112
113const MastercardLogo = (props: JSX.IntrinsicElements["svg"]) => (114 <svg115 xmlns="http://www.w3.org/2000/svg"116 width={2500}117 height={1524}118 viewBox="55.2 38.3 464.5 287.8"119 {...props}120 >121 <path122 fill="#f79f1a"123 d="M519.7 182.2c0 79.5-64.3 143.9-143.6 143.9s-143.6-64.4-143.6-143.9S296.7 38.3 376 38.3s143.7 64.4 143.7 143.9z"124 />125 <path126 fill="#ea001b"127 d="M342.4 182.2c0 79.5-64.3 143.9-143.6 143.9S55.2 261.7 55.2 182.2 119.5 38.3 198.8 38.3s143.6 64.4 143.6 143.9z"128 />129 <path130 fill="#ff5f01"131 d="M287.4 68.9c-33.5 26.3-55 67.3-55 113.3s21.5 87 55 113.3c33.5-26.3 55-67.3 55-113.3s-21.5-86.9-55-113.3z"132 />133 </svg>134);135
136export function FloatingFilter({ column, api }: Grid.T.HeaderParams<GridSpec>) {137 const filters = api.filterModel.useValue();138 if (column.type !== "string") return "-";139
140 // Greatly simply the filter functionality for the demo purposes141 const filterForColumn = filters[column.id] || null;142
143 return (144 <input145 className={146 "border-ln-gray-30 focus:outline-ln-primary-50 h-[calc(100%-8px)] w-full rounded-lg border px-2 text-sm focus:outline-1"147 }148 value={filterForColumn ?? ""}149 placeholder="Type to search..."150 onChange={(e) => {151 if (e.target.value === "") {152 api.filterModel.set((prev) => {153 const next = { ...prev };154 delete next[column.id];155 return next;156 });157 } else {158 api.filterModel.set((prev) => {159 const next = { ...prev };160 next[column.id] = e.target.value;161 return next;162 });163 }164 }}165 />166 );167}Filter Leaf Rows
Filter callbacks passed via the filter property receive leaf rows only
because filtering runs before grouping or aggregation.
Apply filters to raw rows, not grouped or aggregated output.
For post-grouping filtering in the client source, see the Client Row Having Filter guide.
Next Steps
- Client Row Sorting: Sort rows in ascending or descending order with the client row source.
- Client Row Having Filters: Use client-side filters to exclude rows after row grouping.
- Client Row Grouping: Create a hierarchical representation of your data by grouping rows.
