munza-x-data-grid 3.0.0-beta.8 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +471 -187
  2. package/dist/components/ui/button.d.ts +10 -0
  3. package/dist/components/ui/checkbox.d.ts +4 -0
  4. package/dist/components/ui/collapsible.d.ts +5 -0
  5. package/dist/components/ui/debounced-input.d.ts +7 -0
  6. package/dist/components/ui/dropdown-menu.d.ts +29 -0
  7. package/dist/components/ui/empty.d.ts +11 -0
  8. package/dist/components/ui/input.d.ts +3 -0
  9. package/dist/components/ui/label.d.ts +4 -0
  10. package/dist/components/ui/native-select.d.ts +8 -0
  11. package/dist/components/ui/skeleton.d.ts +2 -0
  12. package/dist/components/ui/table.d.ts +10 -0
  13. package/dist/index.cjs +60 -0
  14. package/dist/index.css +56 -0
  15. package/dist/index.d.ts +1 -7
  16. package/dist/index.js +11168 -0
  17. package/dist/lib/utils.d.ts +2 -0
  18. package/dist/package/components/feedback/NoDataFoundMsg.d.ts +2 -0
  19. package/dist/package/components/feedback/TableError.d.ts +2 -0
  20. package/dist/package/components/feedback/TableErrorMsg.d.ts +2 -0
  21. package/dist/package/components/feedback/TableLeftNoData.d.ts +2 -0
  22. package/dist/package/components/feedback/TableLeftSkeleton.d.ts +2 -0
  23. package/dist/package/components/feedback/TableNoData.d.ts +2 -0
  24. package/dist/package/components/feedback/TableRightNoData.d.ts +2 -0
  25. package/dist/package/components/feedback/TableRightSkeleton.d.ts +2 -0
  26. package/dist/package/components/feedback/TableRowSkeleton.d.ts +7 -0
  27. package/dist/package/components/feedback/TableSkeleton.d.ts +2 -0
  28. package/dist/package/components/header/HeaderFilter.d.ts +5 -0
  29. package/dist/package/components/header/HeaderMenu.d.ts +5 -0
  30. package/dist/package/components/header/HeaderResizing.d.ts +5 -0
  31. package/dist/package/components/header/HeaderSort.d.ts +5 -0
  32. package/dist/package/components/pagination/index.d.ts +4 -0
  33. package/dist/package/components/table/TBody.d.ts +3 -0
  34. package/dist/package/components/table/TCell.d.ts +6 -0
  35. package/dist/package/components/table/TGetLeftBody.d.ts +3 -0
  36. package/dist/package/components/table/TGetLeftHeader.d.ts +2 -0
  37. package/dist/package/components/table/TGetLeftMain.d.ts +2 -0
  38. package/dist/package/components/table/TGetRightBody.d.ts +3 -0
  39. package/dist/package/components/table/TGetRightHeader.d.ts +2 -0
  40. package/dist/package/components/table/TGetRightMain.d.ts +2 -0
  41. package/dist/package/components/table/THead.d.ts +6 -0
  42. package/dist/package/components/table/THeader.d.ts +2 -0
  43. package/dist/package/components/table/TMain.d.ts +3 -0
  44. package/dist/package/components/table/TableCellPin.d.ts +6 -0
  45. package/dist/package/components/table/TableLeftCellPin.d.ts +5 -0
  46. package/dist/package/components/table/TableRightCellPin.d.ts +5 -0
  47. package/dist/package/components/toolbar/ToolbarColumns.d.ts +2 -0
  48. package/dist/package/components/toolbar/ToolbarDnd.d.ts +2 -0
  49. package/dist/package/components/toolbar/ToolbarFilters.d.ts +3 -0
  50. package/dist/package/components/toolbar/ToolbarRows.d.ts +2 -0
  51. package/dist/package/components/toolbar/ToolbarSettings.d.ts +2 -0
  52. package/dist/package/components/toolbar/index.d.ts +4 -0
  53. package/dist/package/contexts/GridContext.d.ts +56 -0
  54. package/dist/package/core/index.d.ts +3 -0
  55. package/dist/package/features/columnOrder.d.ts +4 -0
  56. package/dist/package/features/columnPinning.d.ts +5 -0
  57. package/dist/package/features/columnSizing.d.ts +5 -0
  58. package/dist/package/features/columnVisibility.d.ts +5 -0
  59. package/dist/package/features/rowDensity.d.ts +24 -0
  60. package/dist/package/features/rowPinning.d.ts +5 -0
  61. package/dist/package/features/splitView.d.ts +4 -0
  62. package/dist/package/hooks/useGridState.d.ts +19 -0
  63. package/dist/package/hooks/useQueryArgs.d.ts +12 -0
  64. package/dist/package/hooks/useSyncScroll.d.ts +7 -0
  65. package/dist/package/index.d.ts +8 -0
  66. package/dist/package/types/index.d.ts +303 -0
  67. package/dist/package/utils/URLSearch.d.ts +21 -0
  68. package/dist/package/utils/buildQueryString.d.ts +2 -0
  69. package/dist/package/utils/buildSortString.d.ts +4 -0
  70. package/dist/package/utils/getPinStyles.d.ts +3 -0
  71. package/package.json +63 -49
  72. package/dist/App.d.ts +0 -2
  73. package/dist/favicon.svg +0 -1
  74. package/dist/icons.svg +0 -24
  75. package/dist/main.d.ts +0 -1
  76. package/dist/munza-x-data-grid.cjs.js +0 -5
  77. package/dist/munza-x-data-grid.es.js +0 -29
  78. package/dist/style.css +0 -3
package/README.md CHANGED
@@ -1,288 +1,572 @@
1
- # munza-x-data-grid
1
+ # Grid Component — Usage Guide
2
2
 
3
- A flexible, feature-rich React data grid component built on top of [@tanstack/react-table](https://tanstack.com/table), with Tailwind CSS v4 styling and shadcn/ui primitives.
3
+ `Grid` is a reusable data-table component built on top of `@tanstack/react-table`, with built-in support for sorting, filtering, pagination, column pinning, row pinning, column resizing, and more.
4
4
 
5
- ---
6
-
7
- ## Installation
5
+ ## 1. Installation
8
6
 
9
7
  ```bash
10
8
  npm install munza-x-data-grid
9
+ # or
10
+ yarn add munza-x-data-grid
11
+ # or
12
+ pnpm add munza-x-data-grid
11
13
  ```
12
14
 
13
- Import the stylesheet in your app entry point:
15
+ **Peer dependencies** (must already be installed in your project):
14
16
 
15
- ```js
16
- import 'munza-x-data-grid/style.css';
17
+ ```bash
18
+ npm install react@^18 || ^19 react-dom@^18 || ^19
17
19
  ```
18
20
 
19
- ### Peer Dependencies
21
+ ### Importing the stylesheet
20
22
 
21
- ```bash
22
- npm install react react-dom
23
- ```
23
+ The package ships with a CSS file that needs to be imported once at your app's entry point (e.g. `main.tsx` or `App.tsx`):
24
24
 
25
- Requires **React 18 or 19**.
25
+ ```tsx
26
+ import 'munza-x-data-grid/style.css';
27
+ ```
26
28
 
27
- ---
29
+ ### Imports
28
30
 
29
- ## Basic Usage
31
+ Everything components, hooks, utilities, and types — is exported from the single package entry point:
30
32
 
31
33
  ```tsx
32
- import { Grid, useGridState, type ColumnDef } from 'munza-x-data-grid';
33
- import 'munza-x-data-grid/style.css';
34
+ import {
35
+ Grid,
36
+ useGrid,
37
+ useGridState,
38
+ useQueryArgs,
39
+ URLSearch,
40
+ type ColumnDef,
41
+ } from 'munza-x-data-grid';
42
+ ```
34
43
 
35
- type Person = {
36
- firstName: string;
37
- lastName: string;
38
- age: number;
39
- };
44
+ > Note: if you're importing from a local source within the same repo (monorepo / linked package), the path may differ, e.g. `./package` or `../package`.
40
45
 
41
- const columns: ColumnDef<Person>[] = [
42
- { accessorKey: 'firstName', header: 'First Name' },
43
- { accessorKey: 'lastName', header: 'Last Name' },
44
- { accessorKey: 'age', header: 'Age' },
45
- ];
46
+ ## 2. Basic Usage
46
47
 
47
- const data: Person[] = [{ firstName: 'Alice', lastName: 'Smith', age: 30 }];
48
+ ```tsx
49
+ import { useMemo } from 'react';
50
+ import { Grid, useGridState, type ColumnDef } from 'munza-x-data-grid';
51
+
52
+ const App = () => {
53
+ const columns = useMemo<ColumnDef<MyDataType, unknown>[]>(
54
+ () => [
55
+ {
56
+ id: 'id',
57
+ accessorKey: 'id',
58
+ header: () => <div>ID</div>,
59
+ cell: (info) => info.getValue(),
60
+ meta: { filterVariant: 'text' },
61
+ enableHiding: false,
62
+ },
63
+ {
64
+ id: 'name',
65
+ accessorKey: 'name',
66
+ header: () => <div>Name</div>,
67
+ cell: (info) => info.getValue(),
68
+ meta: { filterVariant: 'text' },
69
+ },
70
+ ],
71
+ [],
72
+ );
48
73
 
49
- export default function App() {
50
74
  const { state, handlers } = useGridState();
51
75
 
52
76
  return (
53
77
  <Grid
78
+ payload={{ data: myData, total: 0 }}
54
79
  columns={columns}
55
- payload={{ data, total: data.length }}
80
+ isLoading={false}
81
+ isError={false}
82
+ manualPagination={true}
56
83
  state={state}
57
84
  {...handlers}
58
85
  />
59
86
  );
60
- }
61
- ```
87
+ };
62
88
 
63
- ---
89
+ export default App;
90
+ ```
64
91
 
65
- ## API Reference
92
+ ## 3. The `useGridState` Hook
66
93
 
67
- ### `<Grid />`
94
+ Use this hook to manage all table state (`columnFilters`, `pagination`, `sorting`, `rowSelection`, `globalFilter`) and their change handlers in one place.
68
95
 
69
- The top-level component that renders the full data grid.
96
+ ```tsx
97
+ const { state, handlers, rowSelection } = useGridState();
70
98
 
71
- | Prop | Type | Required | Description |
72
- | ----------------------- | ------------------------------------------ | -------- | -------------------------------------------------------- |
73
- | `columns` | `ColumnDef<T>[]` | ✅ | Column definitions (TanStack Table format) |
74
- | `payload` | `{ data: T[], total: number }` | ✅ | Row data and total count for pagination |
75
- | `state` | `Partial<TableState>` | ✅ | Controlled table state (from `useGridState`) |
76
- | `onColumnFiltersChange` | `OnChangeFn<ColumnFiltersState>` | — | Callback for column filter changes |
77
- | `onPaginationChange` | `OnChangeFn<PaginationState>` | — | Callback for pagination changes |
78
- | `onSortingChange` | `OnChangeFn<SortingState>` | — | Callback for sort changes |
79
- | `setGlobalFilter` | `Dispatch<SetStateAction<string>>` | — | Callback to update the global search filter |
80
- | `isLoading` | `boolean` | — | Displays a loading state |
81
- | `isError` | `boolean` | — | Displays an error state |
82
- | `manualPagination` | `boolean` | — | Set `true` for server-side pagination (default: `false`) |
83
- | `getRowCanExpand` | `(row: Row<T>) => boolean` | — | Controls which rows are expandable |
84
- | `renderSubComponent` | `(props: { row: Row<T> }) => ReactElement` | — | Renders expanded row content |
99
+ <Grid
100
+ columns={columns}
101
+ payload={{ data, total }}
102
+ state={state}
103
+ {...handlers}
104
+ />;
105
+ ```
85
106
 
86
- ---
107
+ `handlers` includes:
87
108
 
88
- ### `useGridState()`
109
+ - `onColumnFiltersChange`
110
+ - `onPaginationChange`
111
+ - `onSortingChange`
112
+ - `setGlobalFilter`
113
+ - `onRowSelectionChange`
89
114
 
90
- Hook that provides controlled state and event handlers to pass into `<Grid />`.
115
+ You can also use pieces separately, e.g. tracking selected rows via `rowSelection`:
91
116
 
92
117
  ```tsx
93
- const { state, handlers } = useGridState();
118
+ console.log(rowSelection);
94
119
  ```
95
120
 
96
- **Returns:**
121
+ ## 4. `Grid` Component Props
122
+
123
+ | Prop | Type | Description |
124
+ | ----------------------- | ------------------------------------------ | ----------------------------------------------------------------- |
125
+ | `columns` | `ColumnDef<T>[]` | Table column definitions (required) |
126
+ | `payload` | `{ data: T[]; total: number }` | Table data and total row count |
127
+ | `state` | `Partial<TableState>` | State obtained from `useGridState` |
128
+ | `onColumnFiltersChange` | `OnChangeFn<ColumnFiltersState>` | Handler for column filter changes |
129
+ | `onPaginationChange` | `OnChangeFn<PaginationState>` | Handler for pagination changes |
130
+ | `onSortingChange` | `OnChangeFn<SortingState>` | Handler for sorting changes |
131
+ | `onRowSelectionChange` | `OnChangeFn<RowSelectionState>` | Handler for row selection changes |
132
+ | `setGlobalFilter` | `Dispatch<SetStateAction<string>>` | Function to set the global search/filter |
133
+ | `manualPagination` | `boolean` | Set to `true` to enable server-side pagination |
134
+ | `enableRowSelection` | `boolean` | Enables/disables row selection (default `true`) |
135
+ | `isLoading` | `boolean` | Shows the loading state |
136
+ | `isError` | `boolean` | Shows the error state |
137
+ | `isFetching` | `boolean` | For a refetching indicator |
138
+ | `refetch` | `() => void` | Function to reload the data |
139
+ | `renderSubComponent` | `(props: { row: Row<T> }) => ReactElement` | Custom content for expandable rows |
140
+ | `getRowCanExpand` | `(row: Row<T>) => boolean` | Determines which rows can be expanded |
141
+ | `height` | `string` | Table height (default `'65vh'`) |
142
+ | `isToolbar` | `boolean` | Whether to show the toolbar (default `true`) |
143
+ | `isPagination` | `boolean` | Whether to show the pagination component (default `true`) |
144
+ | `name` | `string` | Unique grid name, used to persist local state (default `'munza'`) |
145
+ | `children` | `ReactNode` | Renders extra content inside the grid |
146
+
147
+ ## 5. Special Column Definition Features
148
+
149
+ ### Filter variant
150
+
151
+ Set `meta.filterVariant` on each column to determine the filter type. The following variants are supported:
152
+
153
+ - `'text'`
154
+ - `'number'`
155
+ - `'tel'`
156
+ - `'url'`
157
+ - `'color'`
158
+ - `'range'`
159
+ - `'select'`
160
+ - `'dateRange'`
161
+ - `'date'`
162
+ - `'datetime-local'`
163
+ - `'month'`
164
+ - `'time'`
165
+ - `'week'`
97
166
 
98
- | Key | Description |
99
- | ---------- | ----------------------------------------------------------------------------------------------------- |
100
- | `state` | Partial `TableState` object (sorting, pagination, filters, etc.) |
101
- | `handlers` | Object containing `onSortingChange`, `onPaginationChange`, `onColumnFiltersChange`, `setGlobalFilter` |
167
+ ```tsx
168
+ meta: {
169
+ filterVariant: 'text', // 'number' | 'tel' | 'url' | 'color' | 'range' | 'select' | 'dateRange' | 'date' | 'datetime-local' | 'month' | 'time' | 'week'
170
+ },
171
+ ```
102
172
 
103
- Spread `handlers` directly onto `<Grid />`:
173
+ Example a numeric range filter:
104
174
 
105
175
  ```tsx
106
- <Grid state={state} {...handlers} columns={columns} payload={payload} />
176
+ {
177
+ id: 'price',
178
+ accessorKey: 'price',
179
+ header: () => <div>Price</div>,
180
+ cell: (info) => info.getValue(),
181
+ meta: { filterVariant: 'range' },
182
+ },
107
183
  ```
108
184
 
109
- ### Tailwind v4 Setup
185
+ Example a date filter:
110
186
 
111
- If you're using Tailwind v4, add this to your `app.css` so Tailwind scans the grid's classes:
112
-
113
- ```css
114
- @source "../node_modules/munza-x-data-grid/**/*.{js,ts,jsx,tsx}";
115
- @theme inline {
116
- --color-background: var(--background);
117
- --color-foreground: var(--foreground);
118
- --color-border: var(--border);
119
- --color-muted: var(--muted);
120
- --color-accent: var(--accent);
121
- }
187
+ ```tsx
188
+ {
189
+ id: 'createdAt',
190
+ accessorKey: 'createdAt',
191
+ header: () => <div>Created At</div>,
192
+ cell: (info) => info.getValue(),
193
+ meta: { filterVariant: 'date' },
194
+ },
195
+ ```
122
196
 
123
- :root {
124
- --background: oklch(1 0 0);
125
- --foreground: oklch(0.145 0 0);
126
- --border: oklch(0.922 0 0);
127
- --muted: oklch(0.97 0 0);
128
- --accent: oklch(0.97 0 0);
129
- }
197
+ To disable filtering on a specific column:
130
198
 
131
- .dark {
132
- --background: oklch(0.145 0 0);
133
- --foreground: oklch(0.985 0 0);
134
- --border: oklch(1 0 0 / 10%);
135
- --muted: oklch(0.269 0 0);
136
- --accent: oklch(0.269 0 0);
137
- }
199
+ ```tsx
200
+ enableColumnFilter: false,
138
201
  ```
139
202
 
140
- ---
141
-
142
- ## Column Definitions
203
+ ### Row pinning
143
204
 
144
- Columns follow the standard [TanStack Table `ColumnDef`](https://tanstack.com/table/latest/docs/api/core/column-def) format with an additional `meta` field for filter configuration.
205
+ Use `row.pin()` and `row.getIsPinned()` to pin/unpin rows:
145
206
 
146
- ### Filter Variants
207
+ ```tsx
208
+ cell: ({ row }) =>
209
+ row.getIsPinned() ? (
210
+ <button onClick={() => row.pin(false)}>❌</button>
211
+ ) : (
212
+ <>
213
+ <button onClick={() => row.pin('top')}>⬆️</button>
214
+ <button onClick={() => row.pin('bottom')}>⬇️</button>
215
+ </>
216
+ ),
217
+ ```
147
218
 
148
- Set `meta.filterVariant` on a column to control its filter UI:
219
+ ### Row number column
149
220
 
150
221
  ```tsx
151
222
  {
152
- accessorKey: 'status',
153
- header: 'Status',
154
- meta: {
155
- filterVariant: 'select', // dropdown filter
223
+ accessorFn: (_row, index) => index + 1,
224
+ id: 'rowNumber',
225
+ header: '#',
226
+ cell: ({ row, table }) => {
227
+ const { pageIndex, pageSize } = table.getState().pagination;
228
+ return pageIndex * pageSize + row.index + 1;
156
229
  },
157
- }
230
+ },
231
+ ```
158
232
 
159
- {
160
- accessorKey: 'age',
161
- header: 'Age',
162
- meta: {
163
- filterVariant: 'text', // text input filter
164
- },
165
- }
233
+ ### Column size limits
234
+
235
+ ```tsx
236
+ size: 52,
237
+ maxSize: 52,
166
238
  ```
167
239
 
168
- ### Row Number Column
240
+ ### Non-hideable column
169
241
 
170
242
  ```tsx
171
- {
172
- accessorFn: (_row, index) => index + 1,
173
- cell: ({ row }) => row.index + 1,
174
- id: 'rowNumber',
175
- header: '',
176
- size: 54,
177
- maxSize: 54,
178
- enableColumnFilter: false,
179
- }
243
+ enableHiding: false,
180
244
  ```
181
245
 
182
- ### Checkbox Selection Column
246
+ ## 6. Building Server-Side Queries
247
+
248
+ Use the `useQueryArgs` hook to build query parameters from the table state that can be sent to an API:
183
249
 
184
250
  ```tsx
185
- {
186
- id: 'select',
187
- header: ({ table }) => (
188
- <Checkbox
189
- checked={
190
- table.getIsAllPageRowsSelected() ||
191
- (table.getIsSomePageRowsSelected() && 'indeterminate')
192
- }
193
- onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
194
- aria-label="Select all"
195
- />
196
- ),
197
- cell: ({ row }) => (
198
- <Checkbox
199
- checked={row.getIsSelected()}
200
- onCheckedChange={(value) => row.toggleSelected(!!value)}
201
- aria-label="Select row"
202
- />
203
- ),
204
- size: 40,
205
- maxSize: 40,
206
- enableColumnFilter: false,
207
- }
251
+ import { useQueryArgs } from 'munza-x-data-grid';
252
+
253
+ const { pagination, queryParams, sort, globalFilter } = useQueryArgs(state);
208
254
  ```
209
255
 
210
- ---
256
+ (`useQueryArgs` is exported from the same main entry point, no separate sub-path needed.)
257
+
258
+ It returns:
259
+
260
+ - `pagination` — current page/page size
261
+ - `queryParams` — query parameters built from column filters
262
+ - `sort` — sort string
263
+ - `globalFilter` — global search text
264
+
265
+ ## 7. Syncing State to the URL — `URLSearch`
211
266
 
212
- ## Server-Side Pagination
267
+ `URLSearch` converts the grid's query state (`QueryArgs`) into a MongoDB-style query string, so you can sync table state to the URL or forward it straight to a REST API that understands Mongo-style filter operators.
213
268
 
214
- Set `manualPagination` to `true` and pass `payload.total` as the full server-side count.
269
+ ### Import
215
270
 
216
271
  ```tsx
217
- <Grid
218
- columns={columns}
219
- payload={{ data: serverData, total: serverTotal }}
220
- state={state}
221
- {...handlers}
222
- manualPagination={true}
223
- />
272
+ import { URLSearch } from 'munza-x-data-grid';
224
273
  ```
225
274
 
226
- The built-in pagination UI supports the following page sizes:
275
+ ### `QueryArgs` shape
227
276
 
277
+ ```ts
278
+ interface ColumnFilter {
279
+ id: string;
280
+ value: unknown;
281
+ }
282
+
283
+ interface SortingItem {
284
+ id: string;
285
+ desc: boolean;
286
+ }
287
+
288
+ interface Pagination {
289
+ pageIndex: number;
290
+ pageSize: number;
291
+ }
292
+
293
+ interface QueryArgs {
294
+ columnFilters: ColumnFilter[];
295
+ globalFilter?: string;
296
+ pagination: Pagination;
297
+ sorting: SortingItem[];
298
+ rowSelection?: Record<string, boolean>;
299
+ }
228
300
  ```
229
- 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000
301
+
302
+ ### Basic usage
303
+
304
+ ```tsx
305
+ const { state } = useGridState();
306
+
307
+ console.log(URLSearch(state));
308
+ // e.g. "?page=1&limit=20&sort=-createdAt&q=john"
230
309
  ```
231
310
 
232
- ---
311
+ ### What it does
312
+
313
+ `URLSearch(queryArgs: QueryArgs): string` builds a `URLSearchParams` string with the following rules:
314
+
315
+ | State | Resulting query param(s) |
316
+ | ---------------------- | ----------------------------------------------------------------------------------------------- |
317
+ | `pagination.pageIndex` | `page` (1-indexed, so `pageIndex + 1`) |
318
+ | `pagination.pageSize` | `limit` |
319
+ | `sorting` | `sort` — comma-separated column ids, prefixed with `-` for descending (e.g. `sort=-price,name`) |
320
+ | `globalFilter` | `q` — only included if non-empty |
321
+ | `columnFilters` | one or more params per filter, see below |
322
+
323
+ ### Column filter handling
324
+
325
+ Each entry in `columnFilters` is passed through `appendColumnFilter`, which converts the filter's `id` into dot-notation (underscores become dots — useful for nested/populated fields) and then branches on the shape of `value`:
326
+
327
+ 1. **Range value** — a 2-item array (e.g. from a `range` or `dateRange` filter):
328
+
329
+ ```ts
330
+ { id: 'price', value: [10, 100] }
331
+ // → price[gte]=10&price[lte]=100
332
+ ```
333
+
334
+ Empty/`null` bounds are skipped, so `[null, 100]` only sets `price[lte]=100`.
335
+
336
+ 2. **Operator object** — a plain object whose keys are MongoDB-style operators:
337
+
338
+ ```ts
339
+ { id: 'status', value: { in: ['active', 'pending'] } }
340
+ // → status[in]=active,pending
341
+ ```
342
+
343
+ Only keys present in the supported operator list are included; unsupported keys and empty/`null` values are ignored.
233
344
 
234
- ## Expandable Rows
345
+ 3. **Plain scalar value** — anything else (string, number, boolean):
346
+ ```ts
347
+ { id: 'name', value: 'john' }
348
+ // → name=john
349
+ ```
350
+ Empty strings and `null`/`undefined` are skipped entirely.
351
+
352
+ ### Supported MongoDB-style operators
353
+
354
+ ```
355
+ eq, ne, gt, gte, lt, lte, in, nin, regex, exists, all, size,
356
+ elemMatch, type, mod, not, and, or, nor, text, where,
357
+ geoWithin, geoIntersects, near, nearSphere, expr, jsonSchema,
358
+ bitsAllClear, bitsAllSet, bitsAnyClear, bitsAnySet, rand
359
+ ```
360
+
361
+ Any operator outside this list is silently dropped from the query string.
362
+
363
+ ### Full example
235
364
 
236
365
  ```tsx
237
- <Grid
238
- columns={columns}
239
- payload={payload}
240
- state={state}
241
- {...handlers}
242
- getRowCanExpand={(row) => !!row.original.subRows?.length}
243
- renderSubComponent={({ row }) => (
244
- <div className="p-4">
245
- <pre>{JSON.stringify(row.original, null, 2)}</pre>
246
- </div>
247
- )}
248
- />
366
+ const queryArgs: QueryArgs = {
367
+ pagination: { pageIndex: 0, pageSize: 20 },
368
+ sorting: [{ id: 'createdAt', desc: true }],
369
+ globalFilter: 'john',
370
+ columnFilters: [
371
+ { id: 'price', value: [10, 100] },
372
+ { id: 'status', value: { in: ['active', 'pending'] } },
373
+ { id: 'department_name', value: 'Engineering' },
374
+ ],
375
+ };
376
+
377
+ URLSearch(queryArgs);
378
+ // "?page=1&limit=20&price[gte]=10&price[lte]=100&status[in]=active%2Cpending&department.name=Engineering&sort=-createdAt&q=john"
249
379
  ```
250
380
 
251
- ---
381
+ > Note: `department_name` becomes `department.name` — underscores in a column `id` are converted to dots, which is handy for filtering on nested/populated document fields in MongoDB-backed APIs.
252
382
 
253
- ## Column Pinning
383
+ ## 8. Customizing via `useGrid` (inside `<Grid>` children)
254
384
 
255
- Column pinning is managed internally by the grid. No additional configuration is requireduse the built-in toolbar UI to pin columns left or right.
385
+ `Grid` exposes its internal context the live `@tanstack/react-table` `table` instance, density, split-view state, column pinning, scroll pane refs, and more through the `useGrid()` hook. This lets you build custom toolbar buttons, headers, footers, or any other UI that needs access to the table without having to lift state yourself.
256
386
 
257
- ---
387
+ > **Important:** `useGrid()` only works **inside** `<Grid>` — i.e. in a component rendered as a _child_ of `<Grid>`. Calling it in the same component that renders `<Grid>` (like your `App` component) will throw, because the context provider wraps `Grid`'s children, not `Grid` itself.
258
388
 
259
- ## Default Column Sizing
389
+ ### Import
260
390
 
261
- | Property | Default |
262
- | --------- | ------- |
263
- | `size` | `180px` |
264
- | `minSize` | `180px` |
265
- | `maxSize` | `180px` |
391
+ ```tsx
392
+ import { Grid, useGrid } from 'munza-x-data-grid';
393
+ ```
266
394
 
267
- Override per-column:
395
+ ### What `useGrid()` returns
396
+
397
+ | Property | Type | Description |
398
+ | -------------------------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
399
+ | `table` | `Table<T>` | The underlying `@tanstack/react-table` instance — full access to rows, columns, filtering/sorting/pagination APIs, etc. |
400
+ | `density` | `DensityState` | Current row density (e.g. compact/comfortable) |
401
+ | `isSplit` / `setIsSplit` | `boolean` / setter | Split-view state, for side-by-side pinned/unpinned panes |
402
+ | `columnPinning` | `ColumnPinningState` | Current left/right pinned columns |
403
+ | `globalFilter` | `string` | The current global search text |
404
+ | `setGlobalFilter` | setter | Update the global search text |
405
+ | `isLoading` / `isError` / `isFetching` | `boolean` | Data-fetch status flags passed down to `Grid` |
406
+ | `refetch` | `() => void` | Re-triggers the data fetch, if provided to `Grid` |
407
+ | `height` | `string` | The table height passed to `Grid` |
408
+ | `renderSubComponent` | function | The expanded-row renderer passed to `Grid` |
409
+ | `gridWrapperRef` | `RefObject<HTMLDivElement>` | Ref to the grid's outer wrapper element |
410
+ | `paneRef1`–`paneRef6` | `RefObject<HTMLDivElement>` | Refs to the internal scroll panes (pinned-left/main/pinned-right, header/body), used for scroll syncing |
411
+
412
+ ### Example — a custom toolbar button using `table`
268
413
 
269
414
  ```tsx
270
- {
271
- accessorKey: 'id',
272
- size: 60,
273
- minSize: 60,
274
- maxSize: 60,
415
+ import { Grid, useGrid, useGridState } from 'munza-x-data-grid';
416
+
417
+ const ResetFiltersButton = () => {
418
+ const { table } = useGrid();
419
+
420
+ return (
421
+ <button onClick={() => table.resetColumnFilters()}>Clear filters</button>
422
+ );
423
+ };
424
+
425
+ const App = () => {
426
+ const { state, handlers } = useGridState();
427
+
428
+ return (
429
+ <Grid
430
+ payload={{ data, total }}
431
+ columns={columns}
432
+ state={state}
433
+ {...handlers}
434
+ >
435
+ <ResetFiltersButton />
436
+ </Grid>
437
+ );
438
+ };
439
+ ```
440
+
441
+ ### Example — reading selected rows from `table`
442
+
443
+ ```tsx
444
+ const SelectionSummary = () => {
445
+ const { table } = useGrid();
446
+ const selectedCount = table.getSelectedRowModel().rows.length;
447
+
448
+ return <span>{selectedCount} row(s) selected</span>;
449
+ };
450
+
451
+ <Grid columns={columns} payload={{ data, total }} state={state} {...handlers}>
452
+ <SelectionSummary />
453
+ </Grid>;
454
+ ```
455
+
456
+ ### Example — toggling density or split view
457
+
458
+ ```tsx
459
+ const ViewControls = () => {
460
+ const { density, isSplit, setIsSplit } = useGrid();
461
+
462
+ return (
463
+ <div>
464
+ <span>Density: {density}</span>
465
+ <button onClick={() => setIsSplit((prev) => !prev)}>
466
+ {isSplit ? 'Unsplit view' : 'Split view'}
467
+ </button>
468
+ </div>
469
+ );
470
+ };
471
+
472
+ <Grid columns={columns} payload={{ data, total }} state={state} {...handlers}>
473
+ <ViewControls />
474
+ </Grid>;
475
+ ```
476
+
477
+ ## 9. Theming
478
+
479
+ `Grid` is styled with CSS variables, so you can restyle it (including light/dark mode) without touching component code. Define these variables in your global stylesheet — the `:root` block sets the light theme, and the `.dark` block overrides it for dark mode (e.g. toggled via a `dark` class on `<html>` or `<body>`).
480
+
481
+ ```css
482
+ :root {
483
+ --background: oklch(100% 0 0);
484
+ --foreground: oklch(14.5% 0 0);
485
+ --primary: oklch(20.5% 0 0);
486
+ --primary-foreground: oklch(98.5% 0 0);
487
+ --secondary: oklch(97% 0 0);
488
+ --secondary-foreground: oklch(20.5% 0 0);
489
+ --muted: oklch(97% 0 0);
490
+ --muted-foreground: oklch(55.6% 0 0);
491
+ --accent: oklch(97% 0 0);
492
+ --accent-foreground: oklch(20.5% 0 0);
493
+ --destructive: oklch(57.7% 0.245 27.325);
494
+ --border: oklch(92.2% 0 0);
495
+ --input: oklch(92.2% 0 0);
496
+ --ring: oklch(70.8% 0 0);
497
+ --radius: 0.625rem;
498
+ }
499
+
500
+ .dark {
501
+ --background: oklch(14.5% 0 0);
502
+ --foreground: oklch(98.5% 0 0);
503
+ --primary: oklch(92.2% 0 0);
504
+ --primary-foreground: oklch(20.5% 0 0);
505
+ --secondary: oklch(26.9% 0 0);
506
+ --secondary-foreground: oklch(98.5% 0 0);
507
+ --muted: oklch(26.9% 0 0);
508
+ --muted-foreground: oklch(70.8% 0 0);
509
+ --accent: oklch(26.9% 0 0);
510
+ --accent-foreground: oklch(98.5% 0 0);
511
+ --destructive: oklch(70.4% 0.191 22.216);
512
+ --border: oklch(100% 0 0/0.1);
513
+ --input: oklch(100% 0 0/0.15);
514
+ --ring: oklch(55.6% 0 0);
275
515
  }
276
516
  ```
277
517
 
278
- ---
518
+ ### Where to put it
279
519
 
280
- ## TypeScript
520
+ Add this CSS **after** the package stylesheet import, so your custom values override the defaults:
281
521
 
282
- All props and hooks are fully typed. Import types directly from the package:
522
+ ```tsx
523
+ import 'munza-x-data-grid/style.css';
524
+ import './theme.css'; // your theme variables above
525
+ ```
283
526
 
284
- ```ts
285
- import type { ColumnDef } from 'munza-x-data-grid';
527
+ ### Variable reference
528
+
529
+ | Variable | Used for |
530
+ | ------------------------ | ------------------------------------------------------------ |
531
+ | `--background` | Base grid/page background |
532
+ | `--foreground` | Default text color |
533
+ | `--primary` | Primary buttons/actions, active states |
534
+ | `--primary-foreground` | Text/icon color on top of `--primary` |
535
+ | `--secondary` | Secondary buttons, toolbar surfaces |
536
+ | `--secondary-foreground` | Text/icon color on top of `--secondary` |
537
+ | `--muted` | Subtle backgrounds (e.g. striped rows, disabled states) |
538
+ | `--muted-foreground` | Secondary/lower-emphasis text |
539
+ | `--accent` | Hover/highlight backgrounds |
540
+ | `--accent-foreground` | Text/icon color on top of `--accent` |
541
+ | `--destructive` | Error states, destructive actions (e.g. delete) |
542
+ | `--border` | Table/cell/input borders |
543
+ | `--input` | Input field borders/backgrounds |
544
+ | `--ring` | Focus ring color |
545
+ | `--radius` | Base border radius, used to derive component corner rounding |
546
+
547
+ ### Toggling dark mode
548
+
549
+ Add/remove the `dark` class on a parent element (commonly `<html>`):
550
+
551
+ ```tsx
552
+ document.documentElement.classList.toggle('dark');
286
553
  ```
287
554
 
288
- TanStack Table types (`Row`, `TableState`, `PaginationState`, etc.) are re-exported and available from `@tanstack/react-table`.
555
+ ## 10. Example: Full Flow
556
+
557
+ ```tsx
558
+ const { state, handlers } = useGridState();
559
+ const queryArgs = useQueryArgs(state);
560
+
561
+ // Call your API with queryArgs, then pass the result as payload
562
+ <Grid
563
+ payload={{ data: apiResponse.data, total: apiResponse.total }}
564
+ columns={columns}
565
+ manualPagination
566
+ isLoading={isFetching}
567
+ isError={isError}
568
+ refetch={refetch}
569
+ state={state}
570
+ {...handlers}
571
+ />;
572
+ ```