@dynostack/react-grid 0.1.0
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/LICENSE +201 -0
- package/NOTICE +25 -0
- package/README.md +572 -0
- package/dist/index.cjs +2689 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +316 -0
- package/dist/index.d.ts +316 -0
- package/dist/index.js +2671 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +71 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @dynostack/react-grid
|
|
4
|
+
|
|
5
|
+
**Enterprise-grade React data grid. Drop-in.**
|
|
6
|
+
|
|
7
|
+
Built on [TanStack Table v8](https://tanstack.com/table) · [Radix UI](https://www.radix-ui.com/) · [Tailwind CSS](https://tailwindcss.com/) · ships shadcn/ui look-and-feel out of the box.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@dynostack/react-grid)
|
|
10
|
+
[](https://bundlephobia.com/package/@dynostack/react-grid)
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
[](./dist/index.d.ts)
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
A single `<DataTable />` component that gives you ag-grid–level functionality with a fraction of the API surface and a shadcn/ui aesthetic. Every behavior is opt-in via props — drop it in and it works; configure it and it scales.
|
|
19
|
+
|
|
20
|
+
## Highlights
|
|
21
|
+
|
|
22
|
+
- **Filters that actually filter** — text, number, date with operators (`contains`, `not contains`, `equals`, `before`, `after`, `in range`, `blank`, `not blank`, …), AND/OR combine of two conditions, and a set filter with search + select-all
|
|
23
|
+
- **Inline editing** — double-click cell to edit, or enter row-edit mode with `Save` / `Cancel`
|
|
24
|
+
- **Add row** — local optimistic insert, edit, then commit on save
|
|
25
|
+
- **Per-column sort, hide, pin, resize, drag-reorder** — pinned columns are fully opaque while you scroll horizontally
|
|
26
|
+
- **Selection + bulk actions** — pinned `__select` column with select-all, clear, bulk delete
|
|
27
|
+
- **Expandable rows** — provide a `renderSubRow` panel or use TanStack's nested `getSubRows`
|
|
28
|
+
- **CSV / Excel export** — selection-aware (export selected vs. all)
|
|
29
|
+
- **Theming** — built on shadcn/ui CSS variables; per-instance overrides with a `theme` prop and ready-made presets
|
|
30
|
+
- **Density** — `compact` · `default` · `comfortable`
|
|
31
|
+
- **i18n / labels** — every visible string is overridable
|
|
32
|
+
- **Feature flags** — turn off any toolbar control or table capability with a single boolean
|
|
33
|
+
- **Tiny API, full TypeScript** — one component, fully typed generics, no provider context to wire up
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Table of contents
|
|
38
|
+
|
|
39
|
+
- [Install](#install)
|
|
40
|
+
- [Tailwind setup](#tailwind-setup)
|
|
41
|
+
- [Theme tokens](#theme-tokens)
|
|
42
|
+
- [Quick start](#quick-start)
|
|
43
|
+
- [Theming](#theming)
|
|
44
|
+
- [Density](#density)
|
|
45
|
+
- [Feature flags](#feature-flags)
|
|
46
|
+
- [Labels (i18n)](#labels-i18n)
|
|
47
|
+
- [Column meta](#column-meta)
|
|
48
|
+
- [Editing](#editing)
|
|
49
|
+
- [Filters](#filters)
|
|
50
|
+
- [Selection & bulk actions](#selection--bulk-actions)
|
|
51
|
+
- [Expandable rows](#expandable-rows)
|
|
52
|
+
- [Export](#export)
|
|
53
|
+
- [Custom row actions](#custom-row-actions)
|
|
54
|
+
- [Server-side data](#server-side-data)
|
|
55
|
+
- [API reference](#api-reference)
|
|
56
|
+
- [Compatibility](#compatibility)
|
|
57
|
+
- [Roadmap](#roadmap)
|
|
58
|
+
- [Contributing](#contributing)
|
|
59
|
+
- [License](#license)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
npm i @dynostack/react-grid
|
|
67
|
+
# or
|
|
68
|
+
pnpm add @dynostack/react-grid
|
|
69
|
+
# or
|
|
70
|
+
yarn add @dynostack/react-grid
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Peer deps:** `react >= 18`, `react-dom >= 18`. All other deps (`@tanstack/react-table`, `radix-ui`, `lucide-react`, `class-variance-authority`, `clsx`, `tailwind-merge`) are bundled.
|
|
74
|
+
|
|
75
|
+
## Tailwind setup
|
|
76
|
+
|
|
77
|
+
The component ships Tailwind class names verbatim. Tell your Tailwind config to scan the package files:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
// tailwind.config.{js,ts}
|
|
81
|
+
export default {
|
|
82
|
+
content: [
|
|
83
|
+
"./src/**/*.{ts,tsx}",
|
|
84
|
+
"./node_modules/@dynostack/react-grid/dist/**/*.{js,mjs,cjs}",
|
|
85
|
+
],
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> Tailwind v4? Add the same path to your `@source` directive instead.
|
|
90
|
+
|
|
91
|
+
## Theme tokens
|
|
92
|
+
|
|
93
|
+
If your app already has [shadcn/ui](https://ui.shadcn.com/docs/theming) tokens defined on `:root`, you're done — the table inherits them automatically.
|
|
94
|
+
|
|
95
|
+
If not, import the default-tokens stylesheet once:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import "@dynostack/react-grid/styles.css"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Either way you can still override any token per-instance via the [`theme`](#theming) prop.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Quick start
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { DataTable } from "@dynostack/react-grid"
|
|
109
|
+
import "@dynostack/react-grid/styles.css" // optional — only if you don't have shadcn tokens
|
|
110
|
+
|
|
111
|
+
type User = {
|
|
112
|
+
id: number
|
|
113
|
+
name: string
|
|
114
|
+
email: string
|
|
115
|
+
role: "admin" | "viewer"
|
|
116
|
+
joinedAt: string
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const columns = [
|
|
120
|
+
{ accessorKey: "id", header: "ID", size: 70 },
|
|
121
|
+
{
|
|
122
|
+
accessorKey: "name",
|
|
123
|
+
header: "Name",
|
|
124
|
+
meta: { label: "Name", editor: "text", filterType: "text" },
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
accessorKey: "email",
|
|
128
|
+
header: "Email",
|
|
129
|
+
meta: { label: "Email", editor: "text", filterType: "text" },
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
accessorKey: "role",
|
|
133
|
+
header: "Role",
|
|
134
|
+
meta: {
|
|
135
|
+
label: "Role",
|
|
136
|
+
editor: "select",
|
|
137
|
+
filterType: "multi-select",
|
|
138
|
+
selectOptions: [
|
|
139
|
+
{ value: "admin", label: "Admin" },
|
|
140
|
+
{ value: "viewer", label: "Viewer" },
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
accessorKey: "joinedAt",
|
|
146
|
+
header: "Joined",
|
|
147
|
+
meta: { label: "Joined", editor: "date", filterType: "date" },
|
|
148
|
+
},
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
export function Users({ data }: { data: User[] }) {
|
|
152
|
+
return (
|
|
153
|
+
<DataTable<User>
|
|
154
|
+
data={data}
|
|
155
|
+
columns={columns}
|
|
156
|
+
onCellEdit={(row, columnId, value) => save({ ...row, [columnId]: value })}
|
|
157
|
+
onRowSave={(row, draft) => save({ ...row, ...draft })}
|
|
158
|
+
onAddRow={() => ({ name: "", email: "", role: "viewer", joinedAt: today() })}
|
|
159
|
+
onBulkDelete={(rows) => removeMany(rows.map((r) => r.id))}
|
|
160
|
+
initialColumnPinning={{ left: ["__select", "id", "name"], right: ["__actions"] }}
|
|
161
|
+
/>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
That's it. You now have sort + filter + edit + add + delete + export + pin + resize + reorder + select.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Theming
|
|
171
|
+
|
|
172
|
+
All shadcn tokens are supported plus `radius` and `fontFamily`. Anything you omit falls through to the consumer's `:root`.
|
|
173
|
+
|
|
174
|
+
### Use a preset
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
import { DataTable, themePresets } from "@dynostack/react-grid"
|
|
178
|
+
|
|
179
|
+
<DataTable data={data} columns={columns} theme={themePresets.violet} />
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Available presets: `light` · `dark` · `emerald` · `violet` · `amber`.
|
|
183
|
+
|
|
184
|
+
### Custom tokens
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<DataTable
|
|
188
|
+
data={data}
|
|
189
|
+
columns={columns}
|
|
190
|
+
theme={{
|
|
191
|
+
primary: "oklch(0.6 0.2 200)",
|
|
192
|
+
primaryForeground: "oklch(1 0 0)",
|
|
193
|
+
accent: "oklch(0.94 0.05 200)",
|
|
194
|
+
radius: "0.25rem",
|
|
195
|
+
fontFamily: "Inter, system-ui, sans-serif",
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
CSS variables are emitted on the table root, so multiple instances on the same page can wear different themes.
|
|
201
|
+
|
|
202
|
+
### Compose with a preset
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
<DataTable
|
|
206
|
+
theme={{ ...themePresets.dark, primary: "oklch(0.7 0.18 250)" }}
|
|
207
|
+
/>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Density
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<DataTable density="compact" /* tighter rows */ />
|
|
216
|
+
<DataTable density="default" /* shadcn defaults */ />
|
|
217
|
+
<DataTable density="comfortable" /* extra padding */ />
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Feature flags
|
|
223
|
+
|
|
224
|
+
Every toolbar control and table capability is a switch. Defaults are sensible — only set what you want to disable.
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
<DataTable
|
|
228
|
+
features={{
|
|
229
|
+
search: true, // global search input
|
|
230
|
+
refresh: true, // refresh button (when onRefresh is provided)
|
|
231
|
+
columnVisibility: true, // columns popover
|
|
232
|
+
export: true, // CSV / Excel menu
|
|
233
|
+
addRow: true, // "Add row" button (when onAddRow is provided)
|
|
234
|
+
pagination: true, // bottom pagination bar
|
|
235
|
+
sorting: true, // sort headers
|
|
236
|
+
filtering: true, // per-column filter popovers
|
|
237
|
+
resizing: true, // resize handles
|
|
238
|
+
reordering: true, // drag-to-reorder columns
|
|
239
|
+
pinning: true, // pin / unpin column controls
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Labels (i18n)
|
|
247
|
+
|
|
248
|
+
Every user-facing string is overridable.
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
<DataTable
|
|
252
|
+
labels={{
|
|
253
|
+
search: "Rechercher...",
|
|
254
|
+
addRow: "Ajouter",
|
|
255
|
+
delete: "Supprimer",
|
|
256
|
+
clear: "Effacer",
|
|
257
|
+
selected: "sélectionné(s)",
|
|
258
|
+
refresh: "Actualiser",
|
|
259
|
+
columns: "Colonnes",
|
|
260
|
+
export: "Exporter",
|
|
261
|
+
csv: "CSV",
|
|
262
|
+
excel: "Excel",
|
|
263
|
+
total: "Total",
|
|
264
|
+
noData: "Aucune donnée.",
|
|
265
|
+
noResults: "Aucun résultat.",
|
|
266
|
+
refreshing: "Actualisation",
|
|
267
|
+
rowsPerPage: "Lignes par page",
|
|
268
|
+
page: "Page",
|
|
269
|
+
of: "sur",
|
|
270
|
+
}}
|
|
271
|
+
/>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Column meta
|
|
277
|
+
|
|
278
|
+
Each column can declare:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
type ColumnMeta = {
|
|
282
|
+
label?: string // header label & filter title
|
|
283
|
+
editor?:
|
|
284
|
+
| "text" | "number" | "currency" | "date"
|
|
285
|
+
| "select" | "switch" | "checkbox"
|
|
286
|
+
filterType?:
|
|
287
|
+
| "text" | "number" | "date"
|
|
288
|
+
| "select" | "multi-select" | "boolean"
|
|
289
|
+
selectOptions?: { value: string; label: string }[]
|
|
290
|
+
align?: "left" | "right" | "center"
|
|
291
|
+
cellClassName?: string
|
|
292
|
+
headerClassName?: string
|
|
293
|
+
exportable?: boolean
|
|
294
|
+
isEditable?: boolean | ((row) => boolean)
|
|
295
|
+
badgeMap?: Partial<
|
|
296
|
+
Record<string,
|
|
297
|
+
"default" | "secondary" | "destructive" |
|
|
298
|
+
"success" | "warning" | "outline"
|
|
299
|
+
>
|
|
300
|
+
>
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
The default `filterFn` for a column is wired automatically from `meta.filterType`. You can still set a custom `filterFn` on the column to override it.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Editing
|
|
309
|
+
|
|
310
|
+
Two modes, both prop-driven, both work simultaneously:
|
|
311
|
+
|
|
312
|
+
### Single cell — double-click
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
<DataTable
|
|
316
|
+
onCellEdit={(row, columnId, value) =>
|
|
317
|
+
saveMutation.mutate({ ...row, [columnId]: value })
|
|
318
|
+
}
|
|
319
|
+
/>
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Whole row — Edit action → Save / Cancel
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
<DataTable
|
|
326
|
+
onRowSave={(row, draft) =>
|
|
327
|
+
saveMutation.mutate({ ...row, ...draft })
|
|
328
|
+
}
|
|
329
|
+
/>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`isEditable` on `meta` can disable editing for individual rows or columns:
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
{
|
|
336
|
+
accessorKey: "email",
|
|
337
|
+
meta: {
|
|
338
|
+
editor: "text",
|
|
339
|
+
isEditable: (row) => row.role !== "billing",
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Filters
|
|
347
|
+
|
|
348
|
+
The package exports the filter primitives so you can build custom panels too:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import {
|
|
352
|
+
textFilterFn,
|
|
353
|
+
numberFilterFn,
|
|
354
|
+
dateFilterFn,
|
|
355
|
+
setFilterFn,
|
|
356
|
+
booleanFilterFn,
|
|
357
|
+
type AdvFilter,
|
|
358
|
+
type SetFilter,
|
|
359
|
+
type TextOp,
|
|
360
|
+
type NumberOp,
|
|
361
|
+
type DateOp,
|
|
362
|
+
} from "@dynostack/react-grid"
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
| `filterType` | Operators | Value shape |
|
|
366
|
+
| -------------- | -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
|
|
367
|
+
| `text` | `contains`, `notContains`, `equals`, `notEqual`, `startsWith`, `endsWith`, `blank`, `notBlank` | `AdvFilter<TextOp, string>` |
|
|
368
|
+
| `number` | `equals`, `notEqual`, `lessThan`, `lessThanOrEqual`, `greaterThan`, `greaterThanOrEqual`, `inRange`, `blank`, `notBlank` | `AdvFilter<NumberOp, number>` |
|
|
369
|
+
| `date` | `equals`, `notEqual`, `before`, `after`, `inRange`, `blank`, `notBlank` | `AdvFilter<DateOp, string>` |
|
|
370
|
+
| `select` | set filter | `SetFilter` (`{ selected: string[] }`) |
|
|
371
|
+
| `multi-select` | set filter | `SetFilter` (`{ selected: string[] }`) |
|
|
372
|
+
| `boolean` | `All` / `True` / `False` | `boolean \| undefined` |
|
|
373
|
+
|
|
374
|
+
Text/number/date panels also expose **AND/OR combine** of a second condition, ag-grid style.
|
|
375
|
+
|
|
376
|
+
The set filter automatically derives unique values from the visible rows when `selectOptions` is not declared — search box, "Select all (filtered)" with indeterminate state, individual checkboxes.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Selection & bulk actions
|
|
381
|
+
|
|
382
|
+
Selection is on by default (`enableSelection: true`). When any row is selected the toolbar swaps in:
|
|
383
|
+
|
|
384
|
+
- A `<count> selected` badge
|
|
385
|
+
- `Delete` button → wires to `onBulkDelete?(rows)`
|
|
386
|
+
- `Clear` button → resets selection
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
<DataTable
|
|
390
|
+
onBulkDelete={(rows) => removeMany(rows.map((r) => r.id))}
|
|
391
|
+
/>
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Expandable rows
|
|
397
|
+
|
|
398
|
+
### Sub-row panel (custom JSX)
|
|
399
|
+
|
|
400
|
+
```tsx
|
|
401
|
+
<DataTable
|
|
402
|
+
renderSubRow={(row) => <UserAuditPanel user={row} />}
|
|
403
|
+
/>
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Nested rows (TanStack `getSubRows`)
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
<DataTable
|
|
410
|
+
getSubRows={(row) => row.children}
|
|
411
|
+
/>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
When either is set, an `__expand` chevron column is added and pinned right next to `__select`.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## Export
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
<DataTable exportFileName="users" />
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Toolbar `Export` menu offers **CSV** and **Excel**. If any rows are selected, the menu becomes "Export N selected"; otherwise it exports all visible (filtered) rows.
|
|
425
|
+
|
|
426
|
+
Mark a column non-exportable via `meta.exportable: false`.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Custom row actions
|
|
431
|
+
|
|
432
|
+
```tsx
|
|
433
|
+
<DataTable
|
|
434
|
+
rowActions={["view", "edit", "duplicate", "delete"]}
|
|
435
|
+
customRowActions={[
|
|
436
|
+
{
|
|
437
|
+
id: "suspend",
|
|
438
|
+
label: "Suspend",
|
|
439
|
+
icon: <BanIcon />,
|
|
440
|
+
danger: true,
|
|
441
|
+
show: (r) => r.status !== "suspended",
|
|
442
|
+
},
|
|
443
|
+
{ id: "archive", label: "Archive", icon: <ArchiveIcon /> },
|
|
444
|
+
]}
|
|
445
|
+
onRowAction={(action, row) => {
|
|
446
|
+
if (action === "delete") deleteMutation.mutate([row.id])
|
|
447
|
+
if (action === "suspend") saveMutation.mutate({ ...row, status: "suspended" })
|
|
448
|
+
// ...
|
|
449
|
+
}}
|
|
450
|
+
/>
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Server-side data
|
|
456
|
+
|
|
457
|
+
Provide a controlled global filter and refetch on change:
|
|
458
|
+
|
|
459
|
+
```tsx
|
|
460
|
+
const [q, setQ] = useState("")
|
|
461
|
+
const usersQuery = useQuery({
|
|
462
|
+
queryKey: ["users", q],
|
|
463
|
+
queryFn: () => fetchUsers({ q }),
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
<DataTable
|
|
467
|
+
data={usersQuery.data ?? []}
|
|
468
|
+
isLoading={usersQuery.isLoading}
|
|
469
|
+
isFetching={usersQuery.isFetching}
|
|
470
|
+
onRefresh={() => usersQuery.refetch()}
|
|
471
|
+
globalFilter={q}
|
|
472
|
+
onGlobalFilterChange={setQ}
|
|
473
|
+
totalRecords={usersQuery.data?.length ?? 0}
|
|
474
|
+
/>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Pair with TanStack Query's pagination/cursor utilities for cursor-based grids.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## API reference
|
|
482
|
+
|
|
483
|
+
| Prop | Type | Default | Description |
|
|
484
|
+
| ------------------------- | ---------------------------------------------------------- | ---------------------- | ------------------------------------------------------ |
|
|
485
|
+
| `data` | `TData[]` | — | Row data. |
|
|
486
|
+
| `columns` | `ColumnDef<TData>[]` | — | TanStack column definitions. |
|
|
487
|
+
| `isLoading` | `boolean` | `false` | Initial skeleton state. |
|
|
488
|
+
| `isFetching` | `boolean` | `false` | Background-refresh indicator. |
|
|
489
|
+
| `onRefresh` | `() => void` | — | Refresh button handler. |
|
|
490
|
+
| `totalRecords` | `number` | `data.length` | Total count badge in toolbar. |
|
|
491
|
+
| `exportFileName` | `string` | `"export"` | Base filename for CSV / Excel export. |
|
|
492
|
+
| `enableSelection` | `boolean` | `true` | Show the `__select` column. |
|
|
493
|
+
| `renderSubRow` | `(row: TData) => ReactNode` | — | Custom expandable panel. |
|
|
494
|
+
| `getSubRows` | `(row: TData) => TData[] \| undefined` | — | Nested rows accessor. |
|
|
495
|
+
| `rowActions` | `("view" \| "edit" \| "duplicate" \| "delete")[]` | all four | Built-in row actions. |
|
|
496
|
+
| `customRowActions` | `CustomRowAction<TData>[]` | `[]` | Extra row actions. |
|
|
497
|
+
| `onRowAction` | `(action, row) => void` | — | Row action handler. |
|
|
498
|
+
| `onCellEdit` | `(row, columnId, value) => void` | — | Single-cell save handler. |
|
|
499
|
+
| `onRowSave` | `(row, draft) => void` | — | Row-edit save handler. |
|
|
500
|
+
| `onAddRow` | `() => Partial<TData>` | — | Returns the empty draft for "Add row". |
|
|
501
|
+
| `onBulkDelete` | `(rows: TData[]) => void` | — | Bulk delete handler. |
|
|
502
|
+
| `initialPageSize` | `number` | `10` | Initial pagination size. |
|
|
503
|
+
| `pageSizeOptions` | `number[]` | shadcn defaults | Page-size dropdown options. |
|
|
504
|
+
| `initialColumnPinning` | `ColumnPinningState` | `{ left: [], right: [] }` | Initial pinned columns. |
|
|
505
|
+
| `initialColumnVisibility` | `VisibilityState` | `{}` | Initial hidden columns. |
|
|
506
|
+
| `globalFilter` | `string` | uncontrolled | Controlled global filter value. |
|
|
507
|
+
| `onGlobalFilterChange` | `(value: string) => void` | — | Controlled global filter setter. |
|
|
508
|
+
| `className` | `string` | — | Extra classes on the table root. |
|
|
509
|
+
| `toolbarSlot` | `ReactNode` | — | Custom JSX prepended into the toolbar. |
|
|
510
|
+
| `features` | `DataTableFeatures` | all on | Feature flags. |
|
|
511
|
+
| `labels` | `DataTableLabels` | English defaults | i18n labels. |
|
|
512
|
+
| `density` | `"compact" \| "default" \| "comfortable"` | `"default"` | Row density. |
|
|
513
|
+
| `theme` | `DataTableTheme` | inherits `:root` | Per-instance CSS-variable overrides. |
|
|
514
|
+
|
|
515
|
+
`TData` must extend `{ id: string \| number }`.
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## Compatibility
|
|
520
|
+
|
|
521
|
+
| Stack | Tested on |
|
|
522
|
+
| ----------------- | -------------------------- |
|
|
523
|
+
| React | 18.x · 19.x |
|
|
524
|
+
| TanStack Table | 8.21+ |
|
|
525
|
+
| Tailwind CSS | 3.x · 4.x |
|
|
526
|
+
| Bundler | Vite · Next.js · Webpack 5 |
|
|
527
|
+
|
|
528
|
+
ESM and CJS bundles ship side-by-side. Tree-shakeable. Marked `"use client"` for Next.js App Router compatibility.
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
## Roadmap
|
|
533
|
+
|
|
534
|
+
- [ ] Server-side pagination/sorting helpers (controlled-state recipes)
|
|
535
|
+
- [ ] Column groups (header rowSpan/colSpan)
|
|
536
|
+
- [ ] Pivot mode
|
|
537
|
+
- [ ] Aggregation row (sum, avg, min, max, count)
|
|
538
|
+
- [ ] Saved view profiles (filter + visibility + pinning snapshots)
|
|
539
|
+
- [ ] Virtualized rows (TanStack Virtual integration)
|
|
540
|
+
- [ ] Storybook + visual regression tests
|
|
541
|
+
- [ ] CodeSandbox / StackBlitz starter
|
|
542
|
+
|
|
543
|
+
Have a use case that isn't covered? [Open an issue](https://github.com/wanted-coder-vijay/wcv-data-grid/issues/new) — happy to consider it.
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Contributing
|
|
548
|
+
|
|
549
|
+
```sh
|
|
550
|
+
git clone https://github.com/wanted-coder-vijay/wcv-data-grid.git
|
|
551
|
+
cd wcv-data-grid
|
|
552
|
+
npm install
|
|
553
|
+
npm run dev # tsup --watch
|
|
554
|
+
npm run typecheck # tsc --noEmit
|
|
555
|
+
npm run build # produce dist/
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
PRs welcome. Please keep the prop API additive — feature toggles over breaking changes.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## License
|
|
563
|
+
|
|
564
|
+
[Apache-2.0](./LICENSE) · Copyright © 2026 vijay kumar anchupogu (wanted-coder-vijay)
|
|
565
|
+
|
|
566
|
+
See [NOTICE](./NOTICE) for attribution requirements.
|
|
567
|
+
|
|
568
|
+
> **Package history.** This package was briefly published as
|
|
569
|
+
> `@dynostack/gridstack@0.1.0` under the MIT license before being
|
|
570
|
+
> renamed to `@dynostack/react-grid` and relicensed under Apache-2.0.
|
|
571
|
+
> The old name is deprecated; new code should depend on
|
|
572
|
+
> `@dynostack/react-grid` only.
|