@snowpact/react-tanstack-query-table 1.3.0 → 1.4.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,55 +2,99 @@
2
2
 
3
3
  Ultra-light, registry-based data table for React + TanStack Table + TanStack Query.
4
4
 
5
+ **[Live Demo](https://snowpact.github.io/react-tanstack-query-table/)**
6
+
5
7
  ## Features
6
8
 
7
- - **Zero heavy dependencies**: No clsx, no tailwind-merge, no lucide-react bundled
9
+ - **Zero heavy dependencies**: Only `@tanstack/react-query` and `@tanstack/react-table` as peer dependencies
8
10
  - **Registry-based**: Inject your own i18n, Link component, confirmation dialogs
9
11
  - **TypeScript**: Full type support with generics
10
12
  - **Two modes**: Client-side and Server-side pagination/filtering/sorting
11
- - **Customizable**: Override styles via registry
13
+ - **Customizable**: Override styles via CSS variables or registry
14
+
15
+ ## Quick Setup
12
16
 
13
- ## Installation
17
+ ### 1. Install
14
18
 
15
19
  ```bash
20
+ npm install @tanstack/react-query @tanstack/react-table
16
21
  npm install @snowpact/react-tanstack-query-table
17
- # or
18
- pnpm add @snowpact/react-tanstack-query-table
19
22
  ```
20
23
 
21
- ### Peer Dependencies
24
+ ### 2. Import styles
22
25
 
23
- ```bash
24
- npm install @tanstack/react-query @tanstack/react-table react react-dom
26
+ ```tsx
27
+ // In your app entry point (main.tsx or App.tsx)
28
+ import '@snowpact/react-tanstack-query-table/styles.css';
25
29
  ```
26
30
 
27
- ### Import Styles
31
+ ### 3. Setup once
28
32
 
29
- Import the library CSS in your app entry point:
33
+ ```tsx
34
+ // In your app entry point (main.tsx or App.tsx)
35
+ import { setupSnowTable } from '@snowpact/react-tanstack-query-table';
36
+ import { Link } from 'react-router-dom';
37
+
38
+ // Your translation function (or just return the key)
39
+ const t = (key: string) => translations[key] || key;
40
+
41
+ setupSnowTable({
42
+ t,
43
+ LinkComponent: Link,
44
+ confirm: ({ title }) => window.confirm(title),
45
+ });
46
+ ```
47
+
48
+ ### 4. Use the table
30
49
 
31
50
  ```tsx
32
- import '@snowpact/react-tanstack-query-table/styles.css';
51
+ import { SnowClientDataTable, SnowColumnConfig } from '@snowpact/react-tanstack-query-table';
52
+
53
+ type User = { id: string; name: string; email: string; status: string };
54
+
55
+ const columns: SnowColumnConfig<User>[] = [
56
+ { key: 'name', label: 'Name' },
57
+ { key: 'email', label: 'Email' },
58
+ { key: 'status', label: 'Status', render: (item) => <Badge>{item.status}</Badge> },
59
+ ];
60
+
61
+ <SnowClientDataTable
62
+ queryKey={['users']}
63
+ fetchAllItemsEndpoint={() => fetchUsers()}
64
+ columnConfig={columns}
65
+ enableGlobalSearch
66
+ enablePagination
67
+ enableSorting
68
+ enableColumnConfiguration
69
+ defaultPageSize={20}
70
+ defaultSortBy="name"
71
+ defaultSortOrder="asc"
72
+ persistState
73
+ />
33
74
  ```
34
75
 
35
- ### Customization (Optional)
76
+ That's it! You have a working data table.
77
+
78
+ ---
36
79
 
37
- You can customize the library appearance by overriding CSS variables:
80
+ ## Advanced Configuration
81
+
82
+ ### Theme Customization
83
+
84
+ Override CSS variables to match your design:
38
85
 
39
86
  ```css
40
87
  :root {
41
- --snow-background: #ffffff; /* Main background (table, rows, inputs) */
42
- --snow-foreground: #0a0a0a; /* Main text color */
43
- --snow-secondary: #f5f5f5; /* Secondary background (headers, hover) */
44
- --snow-secondary-foreground: #737373; /* Secondary text color */
45
- --snow-border: #d4d4d4; /* Border color */
46
- --snow-ring: #a3a3a3; /* Focus ring color */
47
- --snow-radius: 0.375rem; /* Border radius */
88
+ --snow-background: #ffffff;
89
+ --snow-foreground: #0a0a0a;
90
+ --snow-secondary: #f5f5f5;
91
+ --snow-secondary-foreground: #737373;
92
+ --snow-border: #d4d4d4;
93
+ --snow-ring: #a3a3a3;
94
+ --snow-radius: 0.375rem;
48
95
  }
49
- ```
50
-
51
- **Dark mode example:**
52
96
 
53
- ```css
97
+ /* Dark mode */
54
98
  .dark {
55
99
  --snow-background: #1a1a2e;
56
100
  --snow-foreground: #eaeaea;
@@ -61,73 +105,102 @@ You can customize the library appearance by overriding CSS variables:
61
105
  }
62
106
  ```
63
107
 
64
- ## Quick Start
65
-
66
- ### 1. Setup (once in your app)
108
+ ### Setup with i18n (react-i18next)
67
109
 
68
110
  ```tsx
69
- // Import styles first
70
- import '@snowpact/react-tanstack-query-table/styles.css';
71
-
72
- import { setupSnowTable } from '@snowpact/react-tanstack-query-table';
73
111
  import { useTranslation } from 'react-i18next';
74
- import { Link } from 'react-router-dom';
75
- import { useConfirm } from './your-confirm-dialog';
112
+
113
+ // Get t function at module level or use a hook wrapper
114
+ const { t } = i18n;
76
115
 
77
116
  setupSnowTable({
78
- useTranslation: () => useTranslation(),
117
+ t: (key) => t(key),
79
118
  LinkComponent: Link,
80
- useConfirm: () => useConfirm(),
119
+ confirm: ({ title, content }) => {
120
+ const message = typeof content === 'string' ? `${title}\n\n${content}` : title;
121
+ return window.confirm(message);
122
+ },
81
123
  });
82
124
  ```
83
125
 
84
- > **Full setup example**: See [examples/full-setup.md](./examples/full-setup.md) for a complete Shadcn/UI integration with custom `useConfirm` hook and translations.
126
+ The `t` function is automatically called with:
127
+ - All column `key` values from your `columnConfig` (e.g., `t('name')`, `t('email')`, `t('status')`)
128
+ - Internal UI keys:
129
+ - `dataTable.search` - Search placeholder
130
+ - `dataTable.elements` - "elements" label
131
+ - `dataTable.searchEmpty` - Empty state text
132
+ - `dataTable.resetFilters` - Reset button tooltip
133
+ - `dataTable.columns` - Columns button label
85
134
 
86
- ### 2. Use a Client Table
135
+ ### Setup with custom confirm dialog
87
136
 
88
137
  ```tsx
89
- import { SnowClientDataTable, SnowColumnConfig } from '@snowpact/react-tanstack-query-table';
90
- import { Edit, Trash } from 'lucide-react';
138
+ import { useConfirmDialog } from './your-confirm-hook';
91
139
 
92
- type User = { id: string; name: string; email: string };
140
+ // If you have a hook-based confirm dialog
141
+ const confirmDialog = useConfirmDialog();
93
142
 
94
- const columns: SnowColumnConfig<User>[] = [{ key: 'name' }, { key: 'email' }];
143
+ setupSnowTable({
144
+ t,
145
+ LinkComponent: Link,
146
+ confirm: async ({ title, content, confirmText, cancelText }) => {
147
+ return confirmDialog.open({
148
+ title,
149
+ description: content,
150
+ confirmLabel: confirmText,
151
+ cancelLabel: cancelText,
152
+ });
153
+ },
154
+ });
155
+ ```
95
156
 
96
- <SnowClientDataTable
97
- queryKey={['users']}
98
- fetchAllItemsEndpoint={() => fetchUsers()}
99
- columnConfig={columns}
100
- actions={[
101
- { type: 'click', icon: Edit, label: 'Edit', onClick: user => editUser(user) },
102
- { type: 'endpoint', icon: Trash, label: 'Delete', endpoint: user => deleteUser(user.id) },
103
- ]}
104
- enableGlobalSearch
105
- enablePagination
106
- />;
157
+ ### Override component styles (rare)
158
+
159
+ For deep customization, override internal Tailwind classes:
160
+
161
+ ```tsx
162
+ setupSnowTable({
163
+ t,
164
+ LinkComponent: Link,
165
+ confirm: ({ title }) => window.confirm(title),
166
+ styles: {
167
+ button: {
168
+ visual: 'rounded-full bg-primary text-primary-foreground',
169
+ hover: 'hover:bg-primary/90',
170
+ },
171
+ table: {
172
+ header: 'bg-slate-100 dark:bg-slate-800',
173
+ rowHover: 'hover:bg-slate-50',
174
+ },
175
+ input: 'rounded-full border-2 border-primary',
176
+ },
177
+ });
107
178
  ```
108
179
 
180
+ ---
181
+
109
182
  ## Client vs Server Mode
110
183
 
111
- | Mode | Component | Use case | Data handling |
112
- | ---------- | ----------------- | ------------- | ----------------------------------------------- |
113
- | **Client** | `SnowClientDataTable` | < 5,000 items | All data loaded, filtered/sorted locally |
114
- | **Server** | `SnowServerDataTable` | > 5,000 items | Paginated API, server handles filtering/sorting |
184
+ | Mode | Component | Use case | Data handling |
185
+ | ---------- | --------------------- | ------------- | ---------------------------------------- |
186
+ | **Client** | `SnowClientDataTable` | < 5,000 items | All data loaded, filtered/sorted locally |
187
+ | **Server** | `SnowServerDataTable` | > 5,000 items | Server handles pagination/filtering |
115
188
 
116
189
  ### SnowClientDataTable
117
190
 
118
- Best for small to medium datasets. Fetches all data once via React Query, then handles pagination, search, and sorting entirely in the browser.
191
+ Fetches all data once, handles everything in the browser:
119
192
 
120
193
  ```tsx
121
194
  <SnowClientDataTable
122
195
  queryKey={['users']}
123
- fetchAllItemsEndpoint={() => api.getUsers()} // Returns User[]
196
+ fetchAllItemsEndpoint={() => api.getUsers()}
124
197
  columnConfig={columns}
125
198
  />
126
199
  ```
127
200
 
128
201
  ### SnowServerDataTable
129
202
 
130
- Best for large datasets. The server handles pagination, search, filtering, and sorting. Returns paginated results with a total count.
203
+ Server handles pagination, search, filtering, and sorting:
131
204
 
132
205
  ```tsx
133
206
  import { SnowServerDataTable, ServerFetchParams } from '@snowpact/react-tanstack-query-table';
@@ -141,21 +214,25 @@ const fetchUsers = async (params: ServerFetchParams) => {
141
214
  };
142
215
  };
143
216
 
144
- <SnowServerDataTable queryKey={['users']} fetchServerEndpoint={fetchUsers} columnConfig={columns} />;
217
+ <SnowServerDataTable
218
+ queryKey={['users']}
219
+ fetchServerEndpoint={fetchUsers}
220
+ columnConfig={columns}
221
+ />
145
222
  ```
146
223
 
224
+ ---
225
+
147
226
  ## Actions
148
227
 
149
- Actions appear as buttons in each row. Three types are available:
228
+ Actions appear as buttons in each row:
150
229
 
151
230
  ### Click Action
152
231
 
153
- Simple callback when clicked.
154
-
155
232
  ```tsx
156
233
  {
157
234
  type: 'click',
158
- icon: Edit,
235
+ icon: EditIcon,
159
236
  label: 'Edit',
160
237
  onClick: (item) => openEditModal(item),
161
238
  }
@@ -163,12 +240,10 @@ Simple callback when clicked.
163
240
 
164
241
  ### Link Action
165
242
 
166
- Navigates to a URL. Uses your registered `LinkComponent`.
167
-
168
243
  ```tsx
169
244
  {
170
245
  type: 'link',
171
- icon: Eye,
246
+ icon: EyeIcon,
172
247
  label: 'View',
173
248
  href: (item) => `/users/${item.id}`,
174
249
  external: false, // true for target="_blank"
@@ -177,123 +252,57 @@ Navigates to a URL. Uses your registered `LinkComponent`.
177
252
 
178
253
  ### Endpoint Action
179
254
 
180
- Calls an async endpoint (API mutation). Integrates with React Query invalidation.
181
-
182
- ```tsx
183
- {
184
- type: 'endpoint',
185
- icon: Trash,
186
- label: 'Delete',
187
- endpoint: (item) => api.deleteUser(item.id),
188
- onSuccess: () => queryClient.invalidateQueries(['users']),
189
- onError: (error) => toast.error(error.message),
190
- }
191
- ```
192
-
193
- ### Confirmation Dialog
194
-
195
- Any action can require confirmation before executing:
196
-
197
255
  ```tsx
198
256
  {
199
257
  type: 'endpoint',
200
- icon: Trash,
258
+ icon: TrashIcon,
201
259
  label: 'Delete',
202
260
  variant: 'danger',
203
261
  endpoint: (item) => api.deleteUser(item.id),
262
+ onSuccess: () => queryClient.invalidateQueries(['users']),
204
263
  confirm: {
205
264
  title: 'Delete user?',
206
265
  content: 'This action cannot be undone.',
207
- confirmText: 'Delete',
208
- cancelText: 'Cancel',
209
266
  },
210
267
  }
211
268
  ```
212
269
 
213
- For forms inside confirm dialogs, use a function to access the `close` helper:
214
-
215
- ```tsx
216
- {
217
- type: 'click',
218
- icon: Edit,
219
- label: 'Change Status',
220
- onClick: () => {},
221
- confirm: {
222
- title: 'Change Status',
223
- hideButtons: true, // Form handles its own buttons
224
- content: ({ close }) => (
225
- <StatusForm
226
- onSuccess={() => {
227
- queryClient.invalidateQueries(['users']);
228
- close();
229
- }}
230
- />
231
- ),
232
- },
233
- }
234
- ```
235
-
236
- ### Action Options
237
-
238
- | Option | Type | Description |
239
- | ----------- | ----------------------------------------------------------- | ---------------------------------------- |
240
- | `icon` | `ComponentType<SVGProps>` | Icon component (lucide-react or any SVG) |
241
- | `label` | `string` | Button label (used for tooltip) |
242
- | `variant` | `'default' \| 'danger' \| 'warning' \| 'info' \| 'success'` | Button color variant |
243
- | `display` | `'button' \| 'dropdown'` | Show as button or in dropdown menu |
244
- | `hidden` | `boolean` | Conditionally hide the action |
245
- | `disabled` | `boolean` | Disable the action |
246
- | `showLabel` | `boolean` | Show label text next to icon |
247
-
248
270
  ### Dynamic Actions
249
271
 
250
- Actions can be functions that return the action config, allowing per-row customization:
251
-
252
272
  ```tsx
253
273
  actions={[
254
274
  (item) => ({
255
275
  type: 'click',
256
- icon: item.isActive ? Pause : Play,
276
+ icon: item.isActive ? PauseIcon : PlayIcon,
257
277
  label: item.isActive ? 'Deactivate' : 'Activate',
258
278
  onClick: () => toggleStatus(item),
259
- hidden: item.role === 'admin', // Hide for admins
279
+ hidden: item.role === 'admin',
260
280
  }),
261
281
  ]}
262
282
  ```
263
283
 
264
- ## Search & Filtering
284
+ ---
265
285
 
266
- ### Global Search
267
-
268
- Enable fuzzy search across all columns:
269
-
270
- ```tsx
271
- <SnowClientDataTable enableGlobalSearch texts={{ searchPlaceholder: 'Search users...' }} />
272
- ```
286
+ ## Filters
273
287
 
274
- For custom search values (computed columns):
288
+ ### Global Search
275
289
 
276
290
  ```tsx
277
- const columns: SnowColumnConfig<User>[] = [
278
- {
279
- key: '_extra_fullName',
280
- label: 'Full Name',
281
- render: item => `${item.firstName} ${item.lastName}`,
282
- searchableValue: item => `${item.firstName} ${item.lastName}`,
283
- },
284
- ];
291
+ <SnowClientDataTable
292
+ enableGlobalSearch
293
+ texts={{ searchPlaceholder: 'Search users...' }}
294
+ />
285
295
  ```
286
296
 
287
297
  ### Column Filters
288
298
 
289
- Multi-select dropdown filters:
290
-
291
299
  ```tsx
292
300
  <SnowClientDataTable
293
301
  filters={[
294
302
  {
295
303
  key: 'status',
296
304
  label: 'Status',
305
+ multipleSelection: true, // Allow multiple values
297
306
  options: [
298
307
  { value: 'active', label: 'Active' },
299
308
  { value: 'inactive', label: 'Inactive' },
@@ -313,14 +322,11 @@ Multi-select dropdown filters:
313
322
 
314
323
  ### Prefilters (Tabs)
315
324
 
316
- Quick segmentation via tabs:
317
-
318
325
  ```tsx
319
326
  <SnowClientDataTable
320
327
  prefilters={[
321
328
  { id: 'all', label: 'All' },
322
329
  { id: 'active', label: 'Active' },
323
- { id: 'archived', label: 'Archived' },
324
330
  ]}
325
331
  prefilterFn={(item, prefilterId) => {
326
332
  if (prefilterId === 'all') return true;
@@ -329,72 +335,37 @@ Quick segmentation via tabs:
329
335
  />
330
336
  ```
331
337
 
332
- For server mode, the `prefilter` value is sent to your endpoint.
338
+ ---
333
339
 
334
- ## Advanced Configuration
340
+ ## Other Features
335
341
 
336
- ### Column Configuration
337
-
338
- Users can show/hide columns via a settings button. Configuration is saved in cookies.
342
+ ### URL State Persistence
339
343
 
340
344
  ```tsx
341
- <SnowClientDataTable
342
- enableColumnConfiguration
343
- columnConfig={[
344
- { key: 'name' }, // Always visible
345
- { key: 'email' },
346
- { key: 'details', meta: { defaultHidden: true } }, // Hidden by default
347
- ]}
348
- />
345
+ <SnowClientDataTable persistState />
349
346
  ```
350
347
 
351
- ### URL State Persistence
348
+ Saves pagination, search, filters, and sorting in URL params.
352
349
 
353
- Persist table state (pagination, search, filters, sorting) in URL query params:
350
+ ### Column Configuration
354
351
 
355
352
  ```tsx
356
353
  <SnowClientDataTable
357
- persistState // State saved in URL
354
+ enableColumnConfiguration
355
+ columnConfig={[
356
+ { key: 'name' },
357
+ { key: 'details', meta: { defaultHidden: true } },
358
+ ]}
358
359
  />
359
360
  ```
360
361
 
361
- URL params used:
362
-
363
- - `dt_page`, `dt_pageSize` - Pagination
364
- - `dt_search` - Search query
365
- - `dt_prefilter` - Active prefilter
366
- - `dt_filters` - Column filters
367
- - `dt_sortBy`, `dt_sortDesc` - Sorting
368
-
369
- ### Column Meta Properties
370
-
371
- ```tsx
372
- {
373
- key: 'actions',
374
- meta: {
375
- width: '100px', // Fixed width
376
- minWidth: '80px', // Minimum width
377
- center: true, // Center content
378
- defaultHidden: true, // Hidden by default
379
- disableColumnClick: true, // Disable row click for this column
380
- },
381
- }
382
- ```
383
-
384
362
  ### Sorting
385
363
 
386
- ```tsx
387
- <SnowClientDataTable enableSorting defaultSortBy="createdAt" defaultSortOrder="desc" />
388
- ```
389
-
390
- ### Pagination
391
-
392
364
  ```tsx
393
365
  <SnowClientDataTable
394
- enablePagination
395
- defaultPageSize={25}
396
- displayTotalNumber // Show "X elements"
397
- enableElementLabel // Show element label
366
+ enableSorting
367
+ defaultSortBy="createdAt"
368
+ defaultSortOrder="desc"
398
369
  />
399
370
  ```
400
371
 
@@ -402,35 +373,35 @@ URL params used:
402
373
 
403
374
  ```tsx
404
375
  <SnowClientDataTable
405
- onRowClick={item => navigate(`/users/${item.id}`)}
406
- activeRowId={selectedUserId} // Highlight active row
376
+ onRowClick={(item) => navigate(`/users/${item.id}`)}
377
+ activeRowId={selectedUserId}
407
378
  />
408
379
  ```
409
380
 
410
- ### Custom Styles
411
-
412
- Override default Tailwind classes via the registry:
381
+ ### Custom Column Rendering
413
382
 
414
383
  ```tsx
415
- setupSnowTable({
416
- // ... other options
417
- styles: {
418
- table: {
419
- wrapper: 'border border-gray-200 rounded-lg',
420
- header: 'bg-gray-50',
421
- row: 'hover:bg-gray-100',
422
- },
423
- button: {
424
- visual: 'bg-blue-500 text-white',
425
- hover: 'hover:bg-blue-600',
426
- },
384
+ const columns: SnowColumnConfig<User>[] = [
385
+ { key: 'name', label: 'Name' },
386
+ {
387
+ key: 'status',
388
+ label: 'Status',
389
+ render: (item) => (
390
+ <span className={item.status === 'active' ? 'text-green-500' : 'text-red-500'}>
391
+ {item.status}
392
+ </span>
393
+ ),
427
394
  },
428
- });
395
+ {
396
+ key: '_extra_fullName', // Use _extra_ prefix for computed columns
397
+ label: 'Full Name',
398
+ render: (item) => `${item.firstName} ${item.lastName}`,
399
+ searchableValue: (item) => `${item.firstName} ${item.lastName}`,
400
+ },
401
+ ];
429
402
  ```
430
403
 
431
- ### Action Tooltips
432
-
433
- Action buttons automatically display tooltips on hover. The tooltip uses your theme's CSS variables (`--snow-foreground`, `--snow-background`, `--snow-radius`) for consistent styling.
404
+ ---
434
405
 
435
406
  ## API Reference
436
407
 
@@ -441,23 +412,22 @@ Action buttons automatically display tooltips on hover. The tooltip uses your th
441
412
  | `queryKey` | `string[]` | Required | React Query cache key |
442
413
  | `fetchAllItemsEndpoint` | `() => Promise<T[]>` | Required | Data fetching function |
443
414
  | `columnConfig` | `SnowColumnConfig<T>[]` | Required | Column definitions |
444
- | `actions` | `TableAction<T>[]` | `[]` | Row actions |
445
- | `filters` | `FilterConfig<T>[]` | `[]` | Column filters |
446
- | `prefilters` | `PreFilter[]` | `[]` | Tab filters |
415
+ | `actions` | `TableAction<T>[]` | - | Row actions |
416
+ | `filters` | `FilterConfig<T>[]` | - | Column filters |
417
+ | `prefilters` | `PreFilter[]` | - | Tab filters |
447
418
  | `prefilterFn` | `(item, id) => boolean` | - | Client-side prefilter logic |
448
419
  | `persistState` | `boolean` | `false` | Persist state in URL |
449
420
  | `enableGlobalSearch` | `boolean` | `false` | Enable search bar |
450
- | `enablePagination` | `boolean` | `false` | Enable pagination |
451
- | `enableSorting` | `boolean` | `false` | Enable column sorting |
421
+ | `enablePagination` | `boolean` | `true` | Enable pagination |
422
+ | `enableSorting` | `boolean` | `true` | Enable column sorting |
452
423
  | `enableColumnConfiguration` | `boolean` | `false` | Enable column visibility toggle |
453
- | `enableResetFilters` | `boolean` | `false` | Show reset filters button |
454
424
  | `defaultPageSize` | `number` | `10` | Initial page size |
455
425
  | `defaultSortBy` | `string` | - | Initial sort column |
456
426
  | `defaultSortOrder` | `'asc' \| 'desc'` | `'asc'` | Initial sort direction |
457
427
 
458
428
  ### SnowServerDataTable Props
459
429
 
460
- Same as `SnowClientDataTable`, except:
430
+ Same as `SnowClientDataTable`, plus:
461
431
 
462
432
  | Prop | Type | Description |
463
433
  | --------------------- | -------------------------------------------------------------------- | ------------------------ |