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