@nexgrid/react 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A server-driven data grid for React and Next.js.
4
4
 
5
- NexGrid renders one page of rows at a time and never holds the dataset. Every
5
+ TableX renders one page of rows at a time and never holds the dataset. Every
6
6
  piece of user intent — page, page size, sort, search, filters — is expressed as
7
7
  a single `QueryState` object that **you** own; the grid hands you the next one
8
8
  and re-renders when you hand back the matching page. That is the whole contract.
@@ -10,16 +10,23 @@ There is no local sort that quietly reorders 10 rows out of 40,000, and no
10
10
  client-side filter that hides records the total still counts.
11
11
 
12
12
  Around that core it provides the things every real admin table ends up needing:
13
- debounced global search, a sort cycle, column visibility, row density,
14
- selection, formatted Excel and CSV export, a paginated footer with a page-jump,
15
- loading / empty / error states, and a card layout for phones all styled by one
16
- stylesheet shared with the Angular and vanilla adapters, so the same grid looks
17
- identical on every platform.
13
+ - 🗂️ **Column Header Grouping (Multi-Level / Stacked Headers)** — Group sub-columns beneath parent categories with automatic `colSpan` and `rowSpan` calculation.
14
+ - 🚀 **Client-Side Pagination & In-Memory Engine** — Zero-config in-memory paging, sorting, search, filtering, and export over local arrays.
15
+ - 💾 **Grid State Persistence (`storageKey`)** Automatically saves column widths, column order, hidden columns, and row density to `localStorage`.
16
+ - ↔️ **Column Resizing & Auto-Fit** Interactive drag resize handles and double-click auto-fit measuring.
17
+ - 🏷️ **Active Filter Pills Bar** — Interactive chip badges beneath the toolbar for active search & column filters with one-click `✕` removal and "Clear all".
18
+ - 🔍 **Debounced global search** (350 ms), 3-state sorting cycle (`asc → desc → cleared`), multi-column sorting.
19
+ - 📌 **Pinned columns** (left / right freeze), master-detail accordion row expansion, summary/aggregation footer row.
20
+ - 📊 **Formatted Excel (`.xls`) and CSV export**, including whole-dataset export across pages.
21
+ - 📱 **Responsive layout** — Full desktop table at ≥ 768 px, smart card list below.
22
+ - 💛 **Dual Language Support** — 100% compatible with both **TypeScript (TSX)** and **plain JavaScript (JSX)** with JSDoc typing.
18
23
 
19
24
  - Zero runtime dependencies beyond `@nexgrid/core`. React is a peer dependency.
20
- - Written for strict TypeScript, generic over your row type.
21
- - Ships ESM and CJS, with a `"use client"` banner so it drops straight into the
22
- Next.js App Router.
25
+ - Ships ESM and CJS, with a `"use client"` banner so it drops straight into the Next.js App Router.
26
+
27
+ <p align="center">
28
+ <img src="https://raw.githubusercontent.com/ChhaganSinha/NexGrid/master/docs/assets/tablex-preview.png" alt="TableX React Data Grid Preview" width="100%" />
29
+ </p>
23
30
 
24
31
  ## Installation
25
32
 
@@ -43,10 +50,10 @@ changes, and pass the result straight through.
43
50
 
44
51
  import { useCallback, useEffect, useState } from "react";
45
52
  import {
46
- NexGrid,
53
+ TableX,
47
54
  defaultQuery,
48
55
  serializeQuery,
49
- type NexGridReactColumn,
56
+ type TableXReactColumn,
50
57
  type PagedResponse,
51
58
  type QueryState,
52
59
  } from "@nexgrid/react";
@@ -60,7 +67,7 @@ interface Student {
60
67
  joinedAt: string;
61
68
  }
62
69
 
63
- const columns: NexGridReactColumn<Student>[] = [
70
+ const columns: TableXReactColumn<Student>[] = [
64
71
  { accessorKey: "name", header: "Name", meta: { minWidth: 180 } },
65
72
  { accessorKey: "email", header: "Email" },
66
73
  {
@@ -105,7 +112,7 @@ export function StudentsGrid() {
105
112
  }, [load, query]);
106
113
 
107
114
  return (
108
- <NexGrid
115
+ <TableX
109
116
  caption="Students"
110
117
  columns={columns}
111
118
  data={page?.items ?? []}
@@ -132,7 +139,7 @@ Your endpoint must answer with a `PagedResponse<T>`:
132
139
  { "items": [], "page": 1, "pageSize": 10, "total": 0, "totalPages": 1 }
133
140
  ```
134
141
 
135
- If your API is ASP.NET Core, `NexGrid.AspNetCore` binds exactly the query string
142
+ If your API is ASP.NET Core, `TableX.AspNetCore` binds exactly the query string
136
143
  `serializeQuery` produces and returns exactly this shape.
137
144
 
138
145
  ### Putting the query in the URL
@@ -145,7 +152,7 @@ const searchParams = useSearchParams();
145
152
  const router = useRouter();
146
153
  const query = useMemo(() => parseQuery(searchParams.toString()), [searchParams]);
147
154
 
148
- <NexGrid
155
+ <TableX
149
156
  query={query}
150
157
  onQueryChange={(next) => router.replace(`?${serializeQuery(next)}`)}
151
158
  {...rest}
@@ -158,7 +165,7 @@ never put the grid into an impossible state.
158
165
 
159
166
  ## Next.js App Router
160
167
 
161
- The published bundle starts with `"use client"`, so `<NexGrid />` can be imported
168
+ The published bundle starts with `"use client"`, so `<TableX />` can be imported
162
169
  directly from a Server Component without a wrapper:
163
170
 
164
171
  ```tsx
@@ -182,33 +189,41 @@ Two notes:
182
189
 
183
190
  | Prop | Type | Default | Description |
184
191
  |------|------|---------|-------------|
185
- | `columns` | `NexGridReactColumn<TData>[]` | required | Column definitions, in display order. |
186
- | `data` | `TData[]` | required | The **current page** of rows only. |
187
- | `total` | `number` | required | Total filtered row count from the server. Drives the pager. |
188
- | `query` | `QueryState` | required | The query the `data` above answers. |
189
- | `onQueryChange` | `(next: QueryState) => void` | required | Called with the next query on page / size / sort / search changes. |
190
- | `caption` | `string` | required | Accessible name for the table; also the default export file name and sheet title. |
191
- | `density` | `"compact" \| "default" \| "comfortable"` | `"default"` | **Initial** density. The user owns it afterwards. |
192
- | `isLoading` | `boolean` | `false` | Replaces the rows with a spinner. The toolbar and footer stay usable. |
193
- | `error` | `boolean` | `false` | Replaces the **whole grid** with an error card. |
194
- | `onRetry` | `() => void` | | When set, the error card offers a retry button. |
195
- | `enableSelection` | `boolean` | `false` | Renders selection checkboxes. |
196
- | `onSelectionChange` | `(ids: string[], allAcrossSelected: boolean) => void` | | Fires after each selection change. `allAcrossSelected` is reserved and always `false`. |
192
+ | `columns` | `TableXReactColumn<TData>[]` | required | Column definitions, in display order (supports multi-level `columns`). |
193
+ | `data` | `TData[]` | required | The current page of rows, or the entire array if `clientSidePagination` is true. |
194
+ | `total` | `number` | optional in client mode | Total filtered row count from the server. Drives the pager. |
195
+ | `query` | `QueryState` | optional in client mode | The query the `data` above answers. |
196
+ | `onQueryChange` | `(next: QueryState) => void` | optional in client mode | Called with the next query on page / size / sort / search changes. |
197
+ | `clientSidePagination` | `boolean` | `false` | Enables zero-config in-memory paging, sorting, search, and filtering over local data. |
198
+ | `storageKey` | `string` | | Persists custom column widths, column order, hidden columns, and density to `localStorage`. |
199
+ | `caption` | `string` | required | Accessible name for the table; also default export filename and sheet title. |
200
+ | `density` | `"compact" \| "default" \| "comfortable"` | `"default"` | Initial density preset. |
201
+ | `enableColumnResize` | `boolean` | `true` | Enables interactive drag-to-resize and double-click auto-fit on column borders. |
202
+ | `enableColumnFilters` | `boolean` | `true` | Enables 3-dot column filter popovers (⋮) for text, select, and range filters. |
203
+ | `enableSorting` | `boolean` | `true` | Global switch for column sorting. |
204
+ | `enableSummaryRow` | `boolean` | `false` | Renders a summary / aggregation row in `<tfoot>`. |
205
+ | `enableRowExpansion` | `boolean` | `false` | Master-detail accordion expandable sub-rows. |
206
+ | `renderExpandedRow` | `(row: TData) => ReactNode` | — | Render function for expanded row content. |
207
+ | `enableBulkActions` | `boolean` | `false` | Floating bottom pill bar for batch operations on selected rows. |
208
+ | `isLoading` | `boolean` | `false` | Replaces rows with a spinner while keeping toolbar and footer interactive. |
209
+ | `error` | `boolean` | `false` | Replaces the whole grid with an accessible error card. |
210
+ | `onRetry` | `() => void` | — | When set, the error card displays a retry button. |
211
+ | `enableSelection` | `boolean` | `false` | Renders row selection checkboxes. |
212
+ | `selectionMode` | `"multi" \| "single"` | `"multi"` | Whether multiple rows or only one row can be selected. |
213
+ | `onSelectionChange` | `(ids: string[]) => void` | — | Fires after each selection change. |
197
214
  | `enableSearch` | `boolean` | `true` | Shows the debounced global search box. |
198
215
  | `searchPlaceholder` | `string` | `locale.searchPlaceholder` | Placeholder text for the search box. |
199
- | `toolbarActions` | `ReactNode` | — | Rendered at the end of the toolbar, after the export menu. |
200
- | `onRowClick` | `(row: TData) => void` | — | Row / card click handler. Adds a pointer cursor and makes rows keyboard-activatable. |
216
+ | `toolbarActions` | `ReactNode` | — | Custom actions rendered at the end of the toolbar. |
217
+ | `onRowClick` | `(row: TData) => void` | — | Row / card click handler. Adds pointer cursor and keyboard activation. |
201
218
  | `getRowId` | `(row: TData) => string` | `String(row.id ?? row)` | Stable row identity, used for selection and React keys. |
202
- | `className` | `string` | | Extra class(es) on the grid root. |
203
- | `showSerialNumber` | `boolean` | `true` | Shows the automatic `S.No.` column, numbered across the whole result set. |
204
- | `enableExport` | `boolean` | `true` | Shows the export menu. |
205
- | `exportFileName` | `string` | caption, lower-cased and underscored | File name prefix, without extension. |
206
- | `onExportAll` | `() => void \| Promise<void>` | | Takes over exporting entirely; the built-in flow never runs. |
207
- | `fetchEndpoint` | `string` | | List endpoint used to page in the rest of the dataset when exporting. |
208
- | `badgeRules` | `readonly ExcelBadgeRule[]` | core's `DEFAULT_BADGE_RULES` | Value-based cell styling for the Excel export. |
209
- | `locale` | `Partial<NexGridLocale>` | English defaults | Overrides for any user-facing string. |
210
- | `onNotify` | `(notice: NexGridNotice) => void` | no-op | Receives `{ type, message }` for export progress, failures, and successes. |
211
- | `theme` | `"light" \| "dark" \| "auto"` | `"light"` | Adds `.nxg-dark` / `.nxg-auto` to the root. |
219
+ | `showSerialNumber` | `boolean` | `true` | Shows automatic `S.No.` column, numbered across pages. |
220
+ | `enableExport` | `boolean` | `true` | Shows the export menu (Excel, CSV, Clipboard). |
221
+ | `exportFileName` | `string` | caption, slug-safe | File name prefix without extension. |
222
+ | `fetchEndpoint` | `string` | | Endpoint used to page in the full dataset during export. |
223
+ | `badgeRules` | `readonly ExcelBadgeRule[]` | core's `DEFAULT_BADGE_RULES` | Value-based cell styling for Excel export. |
224
+ | `locale` | `Partial<TableXLocale>` | English defaults | Overrides for any user-facing string. |
225
+ | `onNotify` | `(notice: TableXNotice) => void` | no-op | Receives `{ type, message }` for notifications. |
226
+ | `theme` | `"light" \| "dark" \| "auto"` | `"light"` | Sets `.tbx-dark` / `.tbx-auto` theme mode. |
212
227
 
213
228
  ## Column definitions
214
229
 
@@ -220,9 +235,10 @@ A column is a plain object, structurally compatible with TanStack Table's
220
235
  | `id` | `string` | Column id. Falls back to `accessorKey`. |
221
236
  | `accessorKey` | `string` | The row property this column reads. |
222
237
  | `header` | `string \| (ctx) => ReactNode` | Header content. A string is also used for menus and export headers. |
238
+ | `columns` | `TableXReactColumn<TData>[]` | Nested sub-columns for multi-level stacked column header groups. |
223
239
  | `cell` | `(ctx: { row: { original: TData }, getValue(): unknown }) => ReactNode` | Custom cell renderer. Without it the raw value is rendered as text. |
224
240
  | `enableSorting` | `boolean` | Sorting is on by default; set `false` to opt out. |
225
- | `meta` | `NexGridColumnMeta` | Layout and behavior hints — see below. |
241
+ | `meta` | `TableXColumnMeta` | Layout and behavior hints — see below. |
226
242
 
227
243
  ### `meta`
228
244
 
@@ -245,7 +261,7 @@ hideable, and never exported.
245
261
  the mobile card list, so the two can never drift apart.
246
262
 
247
263
  ```tsx
248
- const columns: NexGridReactColumn<Student>[] = [
264
+ const columns: TableXReactColumn<Student>[] = [
249
265
  // A status pill.
250
266
  {
251
267
  accessorKey: "status",
@@ -298,38 +314,38 @@ Every color and shape in the stylesheet reads a CSS custom property, so you
298
314
  re-skin the grid by overriding tokens — no class overrides, no `!important`.
299
315
 
300
316
  ```css
301
- .nxg-root {
302
- --nxg-primary: #7c3aed;
303
- --nxg-primary-fg: #ffffff;
304
- --nxg-radius: 8px;
305
- --nxg-font: "Inter", system-ui, sans-serif;
317
+ .tbx-root {
318
+ --tbx-primary: #7c3aed;
319
+ --tbx-primary-fg: #ffffff;
320
+ --tbx-radius: 8px;
321
+ --tbx-font: "Inter", system-ui, sans-serif;
306
322
  }
307
323
  ```
308
324
 
309
325
  | Token | Purpose |
310
326
  |-------|---------|
311
- | `--nxg-font`, `--nxg-font-mono` | Body font, and the serial-number font. |
312
- | `--nxg-bg` | Input and pager background. |
313
- | `--nxg-card`, `--nxg-card-2` | Panel background, and the table header band. |
314
- | `--nxg-border` | Every border and divider. |
315
- | `--nxg-fg`, `--nxg-muted-fg` | Primary and secondary text. |
316
- | `--nxg-muted` | Hover fills and subtle chips. |
317
- | `--nxg-primary`, `--nxg-primary-fg` | Accent: sort icons, current page, selection. |
318
- | `--nxg-danger` | Destructive accents. |
319
- | `--nxg-radius`, `--nxg-radius-sm` | Panel and control corner radii. |
320
- | `--nxg-shadow`, `--nxg-focus-ring` | Elevation, and the focus ring. |
327
+ | `--tbx-font`, `--tbx-font-mono` | Body font, and the serial-number font. |
328
+ | `--tbx-bg` | Input and pager background. |
329
+ | `--tbx-card`, `--tbx-card-2` | Panel background, and the table header band. |
330
+ | `--tbx-border` | Every border and divider. |
331
+ | `--tbx-fg`, `--tbx-muted-fg` | Primary and secondary text. |
332
+ | `--tbx-muted` | Hover fills and subtle chips. |
333
+ | `--tbx-primary`, `--tbx-primary-fg` | Accent: sort icons, current page, selection. |
334
+ | `--tbx-danger` | Destructive accents. |
335
+ | `--tbx-radius`, `--tbx-radius-sm` | Panel and control corner radii. |
336
+ | `--tbx-shadow`, `--tbx-focus-ring` | Elevation, and the focus ring. |
321
337
 
322
338
  Dark mode is a class, not a media query, so it can follow whatever your app
323
339
  already uses:
324
340
 
325
341
  ```tsx
326
- <NexGrid theme="dark" {...props} /> {/* always dark */}
327
- <NexGrid theme="auto" {...props} /> {/* follows the OS */}
342
+ <TableX theme="dark" {...props} /> {/* always dark */}
343
+ <TableX theme="auto" {...props} /> {/* follows the OS */}
328
344
  ```
329
345
 
330
- `theme="dark"` puts `.nxg-dark` on the grid root. If your app already toggles a
331
- dark class higher up the tree, add `nxg-dark` alongside it and leave `theme`
332
- alone — the stylesheet matches `.nxg-dark .nxg-root` as well.
346
+ `theme="dark"` puts `.tbx-dark` on the grid root. If your app already toggles a
347
+ dark class higher up the tree, add `tbx-dark` alongside it and leave `theme`
348
+ alone — the stylesheet matches `.tbx-dark .tbx-root` as well.
333
349
 
334
350
  Responsive behavior is driven entirely by the stylesheet: the grid renders both a
335
351
  table and a card list, and CSS shows the table at ≥ 768px and the cards below it.
@@ -348,7 +364,7 @@ If those requests fail it notifies you and falls back to the current page rather
348
364
  than producing nothing.
349
365
 
350
366
  ```tsx
351
- <NexGrid
367
+ <TableX
352
368
  fetchEndpoint="/api/students"
353
369
  exportFileName="student_roster"
354
370
  badgeRules={[
@@ -375,7 +391,7 @@ is `"info" | "success" | "error"`, ready to forward to whatever you already use.
375
391
  Every user-facing string comes from a locale object. Override any subset:
376
392
 
377
393
  ```tsx
378
- <NexGrid
394
+ <TableX
379
395
  locale={{
380
396
  searchPlaceholder: "Rechercher…",
381
397
  emptyText: "Aucun enregistrement ne correspond à votre recherche.",
@@ -428,5 +444,5 @@ one and that the sort cycle stays `asc → desc → cleared` across every adapte
428
444
 
429
445
  ## License
430
446
 
431
- [MIT](https://github.com/ChhaganSinha/NexGrid/blob/main/LICENSE) © 2026 Chhagan Sinha
447
+ [MIT](https://github.com/ChhaganSinha/TableX/blob/main/LICENSE) © 2026 Chhagan Sinha
432
448