@reactorui/datagrid 1.0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +778 -0
  3. package/dist/components/DataGrid/DataGrid.d.ts +5 -0
  4. package/dist/components/DataGrid/DataGrid.d.ts.map +1 -0
  5. package/dist/components/DataGrid/DataGrid.js +87 -0
  6. package/dist/components/DataGrid/index.d.ts +3 -0
  7. package/dist/components/DataGrid/index.d.ts.map +1 -0
  8. package/dist/components/DataGrid/index.js +1 -0
  9. package/dist/components/Filter/FilterControls.d.ts +11 -0
  10. package/dist/components/Filter/FilterControls.d.ts.map +1 -0
  11. package/dist/components/Filter/FilterControls.js +78 -0
  12. package/dist/components/Filter/index.d.ts +2 -0
  13. package/dist/components/Filter/index.d.ts.map +1 -0
  14. package/dist/components/Filter/index.js +1 -0
  15. package/dist/components/Pagination/Pagination.d.ts +17 -0
  16. package/dist/components/Pagination/Pagination.d.ts.map +1 -0
  17. package/dist/components/Pagination/Pagination.js +12 -0
  18. package/dist/components/Pagination/index.d.ts +2 -0
  19. package/dist/components/Pagination/index.d.ts.map +1 -0
  20. package/dist/components/Pagination/index.js +1 -0
  21. package/dist/components/Search/SearchInput.d.ts +11 -0
  22. package/dist/components/Search/SearchInput.d.ts.map +1 -0
  23. package/dist/components/Search/SearchInput.js +9 -0
  24. package/dist/components/Search/index.d.ts +2 -0
  25. package/dist/components/Search/index.d.ts.map +1 -0
  26. package/dist/components/Search/index.js +1 -0
  27. package/dist/components/Table/TableBody.d.ts +20 -0
  28. package/dist/components/Table/TableBody.d.ts.map +1 -0
  29. package/dist/components/Table/TableBody.js +56 -0
  30. package/dist/components/Table/TableHeader.d.ts +13 -0
  31. package/dist/components/Table/TableHeader.d.ts.map +1 -0
  32. package/dist/components/Table/TableHeader.js +24 -0
  33. package/dist/components/Table/index.d.ts +3 -0
  34. package/dist/components/Table/index.d.ts.map +1 -0
  35. package/dist/components/Table/index.js +2 -0
  36. package/dist/hooks/index.d.ts +2 -0
  37. package/dist/hooks/index.d.ts.map +1 -0
  38. package/dist/hooks/index.js +1 -0
  39. package/dist/hooks/useDataGrid.d.ts +49 -0
  40. package/dist/hooks/useDataGrid.d.ts.map +1 -0
  41. package/dist/hooks/useDataGrid.js +356 -0
  42. package/dist/index.d.ts +7 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +8 -0
  45. package/dist/setupTests.d.ts +12 -0
  46. package/dist/setupTests.d.ts.map +1 -0
  47. package/dist/setupTests.js +1 -0
  48. package/dist/themes/index.d.ts +22 -0
  49. package/dist/themes/index.d.ts.map +1 -0
  50. package/dist/themes/index.js +31 -0
  51. package/dist/types/index.d.ts +108 -0
  52. package/dist/types/index.d.ts.map +1 -0
  53. package/dist/types/index.js +1 -0
  54. package/dist/utils/index.d.ts +12 -0
  55. package/dist/utils/index.d.ts.map +1 -0
  56. package/dist/utils/index.js +209 -0
  57. package/package.json +80 -0
package/README.md ADDED
@@ -0,0 +1,778 @@
1
+ # @reactorui/datagrid
2
+
3
+ A high-performance, feature-rich React data grid component with TypeScript support, server-side integration, and advanced filtering capabilities.
4
+
5
+ ## โœจ Features
6
+
7
+ - ๐Ÿš€ **High Performance** - Optimized rendering and data processing
8
+ - ๐Ÿ” **Advanced Filtering** - Type-aware filters with multiple operators (string, number, date, boolean)
9
+ - ๐Ÿ”„ **Flexible Data Sources** - Static data or server-side with any API
10
+ - ๐Ÿ“ฑ **Responsive Design** - Mobile-first with touch-friendly interactions
11
+ - ๐ŸŽจ **Customizable Theming** - Multiple built-in variants and custom styling
12
+ - ๐ŸŒ™ **Dark Mode Ready** - Built-in dark mode support with CSS variables
13
+ - โ™ฟ **Accessibility First** - WCAG compliant with keyboard navigation and ARIA labels
14
+ - ๐Ÿ”ง **TypeScript Native** - Full type safety and comprehensive IntelliSense support
15
+ - ๐ŸŽฏ **Rich Event System** - 15+ events covering every user interaction
16
+ - ๐Ÿ” **Secure Authentication** - Bearer token, API key, and custom header support
17
+ - โšก **Zero Dependencies** - Only React as peer dependency
18
+
19
+ ## ๐Ÿ“ฆ Installation
20
+
21
+ ```bash
22
+ npm install @reactorui/datagrid
23
+ # or
24
+ yarn add @reactorui/datagrid
25
+ # or
26
+ pnpm add @reactorui/datagrid
27
+ ```
28
+
29
+ ## ๐Ÿš€ Basic Usage
30
+
31
+ ```tsx
32
+ import { DataGrid } from '@reactorui/datagrid';
33
+
34
+ const data = [
35
+ { id: 1, name: 'John Doe', email: 'john@example.com', age: 28 },
36
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', age: 34 },
37
+ ];
38
+
39
+ function App() {
40
+ return <DataGrid data={data} />;
41
+ }
42
+ ```
43
+
44
+ ## ๐Ÿ›  With Custom Columns & Styling
45
+
46
+ ```tsx
47
+ import { DataGrid, Column } from '@reactorui/datagrid';
48
+
49
+ interface User {
50
+ id: number;
51
+ name: string;
52
+ email: string;
53
+ status: 'active' | 'inactive';
54
+ joinDate: string;
55
+ }
56
+
57
+ const columns: Column<User>[] = [
58
+ {
59
+ key: 'name',
60
+ label: 'Full Name',
61
+ sortable: true,
62
+ render: (value, row) => (
63
+ <div className="flex items-center gap-2">
64
+ <div className="w-8 h-8 bg-blue-500 text-white rounded-full flex items-center justify-center text-sm">
65
+ {value.charAt(0)}
66
+ </div>
67
+ {value}
68
+ </div>
69
+ ),
70
+ },
71
+ { key: 'email', label: 'Email Address', sortable: true },
72
+ {
73
+ key: 'status',
74
+ label: 'Status',
75
+ dataType: 'string',
76
+ render: (status) => (
77
+ <span
78
+ className={`px-2 py-1 text-xs font-medium rounded-full ${
79
+ status === 'active' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
80
+ }`}
81
+ >
82
+ {status}
83
+ </span>
84
+ ),
85
+ },
86
+ {
87
+ key: 'joinDate',
88
+ label: 'Join Date',
89
+ dataType: 'date',
90
+ sortable: true,
91
+ },
92
+ ];
93
+
94
+ function App() {
95
+ return (
96
+ <DataGrid
97
+ data={users}
98
+ columns={columns}
99
+ variant="bordered"
100
+ size="comfortable"
101
+ enableSelection={true}
102
+ onSelectionChange={(selected) => console.log('Selected:', selected)}
103
+ />
104
+ );
105
+ }
106
+ ```
107
+
108
+ ## ๐ŸŒ Server-Side Data
109
+
110
+ ```tsx
111
+ import { DataGrid } from '@reactorui/datagrid';
112
+
113
+ function App() {
114
+ return (
115
+ <DataGrid
116
+ endpoint="/api/users"
117
+ httpConfig={{
118
+ bearerToken: 'your-jwt-token',
119
+ method: 'POST',
120
+ postDataFormat: 'json',
121
+ customHeaders: {
122
+ 'X-Custom-Header': 'value',
123
+ },
124
+ }}
125
+ serverPageSize={100}
126
+ pageSize={25}
127
+ onDataLoad={(data) => console.log(`Loaded ${data.items.length} items`)}
128
+ onDataError={(error, context) => console.error(`Error in ${context}:`, error)}
129
+ />
130
+ );
131
+ }
132
+ ```
133
+
134
+ ## ๐ŸŽฏ Comprehensive Event System
135
+
136
+ The DataGrid provides 15+ events covering every user interaction:
137
+
138
+ ```tsx
139
+ <DataGrid
140
+ data={users}
141
+ // Data loading events
142
+ onDataLoad={(data) => {
143
+ console.log('Data loaded:', data.items.length);
144
+ hideLoadingSpinner();
145
+ }}
146
+ onDataError={(error, context) => {
147
+ console.error(`Error in ${context}:`, error.message);
148
+ showErrorToast(error.message);
149
+ }}
150
+ onLoadingStateChange={(loading, context) => {
151
+ setIsLoading(loading);
152
+ console.log(`${context} loading: ${loading}`);
153
+ }}
154
+ // Pagination events
155
+ onPageChange={(page, paginationInfo) => {
156
+ console.log(`Page ${page} of ${paginationInfo.totalPages}`);
157
+ updateUrl(`?page=${page}`);
158
+ }}
159
+ onPageSizeChange={(pageSize, paginationInfo) => {
160
+ console.log(`Showing ${pageSize} items per page`);
161
+ saveUserPreference('pageSize', pageSize);
162
+ }}
163
+ // Interaction events
164
+ onSortChange={(sortConfig) => {
165
+ console.log(`Sorted by ${sortConfig.column} ${sortConfig.direction}`);
166
+ updateUrl(`?sort=${sortConfig.column}&order=${sortConfig.direction}`);
167
+ }}
168
+ onFilterChange={(filters) => {
169
+ console.log(`${filters.length} filters active`);
170
+ setBadgeCount(filters.length);
171
+ }}
172
+ onSearchChange={(searchTerm) => {
173
+ console.log(`Searching for: "${searchTerm}"`);
174
+ trackSearchQuery(searchTerm);
175
+ }}
176
+ onTableRefresh={() => {
177
+ console.log('Table refreshed');
178
+ showSuccessMessage('Data refreshed');
179
+ }}
180
+ />
181
+ ```
182
+
183
+ **Row & Cell Interaction Events**<br>
184
+
185
+ ```tsx
186
+ <DataGrid
187
+ data={users}
188
+ // Row interaction events
189
+ onTableRowClick={(row, event) => {
190
+ console.log('Row clicked:', row.name);
191
+ highlightRow(row.id);
192
+ }}
193
+ onTableRowDoubleClick={(row, event) => {
194
+ console.log('Row double-clicked:', row.name);
195
+ openEditModal(row);
196
+ return false; // Prevent default selection behavior
197
+ }}
198
+ onTableRowHover={(row, event) => {
199
+ if (row) {
200
+ console.log('Hovering over:', row.name);
201
+ showPreviewTooltip(row);
202
+ } else {
203
+ hidePreviewTooltip();
204
+ }
205
+ }}
206
+ // Selection events
207
+ onRowSelect={(row, isSelected) => {
208
+ console.log(`${row.name} ${isSelected ? 'selected' : 'deselected'}`);
209
+ updateRowActions(row, isSelected);
210
+ }}
211
+ onSelectionChange={(selectedRows) => {
212
+ console.log(`${selectedRows.length} rows selected`);
213
+ setBulkActionsEnabled(selectedRows.length > 0);
214
+ updateSelectionToolbar(selectedRows);
215
+ }}
216
+ // Cell interaction events
217
+ onCellClick={(value, row, column, event) => {
218
+ console.log(`Clicked ${column.label}: ${value}`);
219
+ if (column.key === 'email') {
220
+ window.open(`mailto:${value}`);
221
+ }
222
+ }}
223
+ />
224
+ ```
225
+
226
+ ## Real-World Event Usage Example
227
+
228
+ ```tsx
229
+ import React, { useState } from 'react';
230
+ import { DataGrid } from '@reactorui/datagrid';
231
+
232
+ function UserManagement() {
233
+ const [loading, setLoading] = useState(false);
234
+ const [selectedUsers, setSelectedUsers] = useState([]);
235
+ const [currentPage, setCurrentPage] = useState(1);
236
+
237
+ return (
238
+ <div>
239
+ {/* Bulk Actions Toolbar */}
240
+ {selectedUsers.length > 0 && (
241
+ <div className="bg-blue-50 p-4 mb-4 rounded">
242
+ <span className="font-medium">{selectedUsers.length} users selected</span>
243
+ <div className="ml-4 space-x-2">
244
+ <button onClick={() => bulkExport(selectedUsers)}>Export</button>
245
+ <button onClick={() => bulkDeactivate(selectedUsers)}>Deactivate</button>
246
+ <button onClick={() => bulkDelete(selectedUsers)}>Delete</button>
247
+ </div>
248
+ </div>
249
+ )}
250
+
251
+ {/* DataGrid with comprehensive event handling */}
252
+ <DataGrid
253
+ endpoint="/api/users"
254
+ enableSelection={true}
255
+ // Sync with component state
256
+ onLoadingStateChange={(loading) => setLoading(loading)}
257
+ onPageChange={(page) => setCurrentPage(page)}
258
+ onSelectionChange={(users) => setSelectedUsers(users)}
259
+ // User interaction handlers
260
+ onTableRowDoubleClick={(user) => openUserProfile(user.id)}
261
+ onDataError={(error) => showErrorNotification(error.message)}
262
+ onTableRefresh={() => showSuccessMessage('Users refreshed')}
263
+ // Analytics tracking
264
+ onSearchChange={(term) => analytics.track('users_searched', { term })}
265
+ onSortChange={(sort) => analytics.track('users_sorted', { column: sort.column })}
266
+ onFilterChange={(filters) => analytics.track('users_filtered', { count: filters.length })}
267
+ />
268
+
269
+ {/* Status indicators */}
270
+ <div className="mt-4 text-sm text-gray-500">
271
+ Page {currentPage} โ€ข {selectedUsers.length} selected โ€ข {loading ? 'Loading...' : 'Ready'}
272
+ </div>
273
+ </div>
274
+ );
275
+ }
276
+ ```
277
+
278
+ ## ๐Ÿ“– Complete API Reference
279
+
280
+ **DataGrid Props**
281
+
282
+ | **Prop** | **Type** | **Default** | **Description** |
283
+ | ---------------------- | -------------------------------------- | ------------------ | ---------------------------------------- |
284
+ | **Data Configuration** | | | |
285
+ | `data` | `T[]` | โ€“ | Static data array for client-side mode |
286
+ | `endpoint` | `string` | โ€“ | API endpoint for server-side data |
287
+ | `columns` | `Column<T>[]` | Auto-detected | Column configuration array |
288
+ | **Feature Toggles** | | | |
289
+ | `enableSearch` | `boolean` | `true` | Enable global search functionality |
290
+ | `enableSorting` | `boolean` | `true` | Enable column sorting |
291
+ | `enableFilters` | `boolean` | `true` | Enable advanced filtering |
292
+ | `enableSelection` | `boolean` | `true` | Enable row selection with checkboxes |
293
+ | **Pagination** | | | |
294
+ | `pageSize` | `number` | `10` | Client-side pagination size |
295
+ | `serverPageSize` | `number` | `100` | Server request batch size |
296
+ | `pageSizeOptions` | `number[]` | `[5,10,25,50,100]` | Available page size options |
297
+ | **Styling** | | | |
298
+ | `variant` | `'default' \| 'striped' \| 'bordered'` | `'default'` | Visual theme variant |
299
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant for padding and text |
300
+ | `className` | `string` | `''` | Additional CSS classes |
301
+ | **HTTP Configuration** | | | |
302
+ | `httpConfig` | `HttpConfig` | โ€“ | Authentication and request configuration |
303
+
304
+ ## Column Configuration
305
+
306
+ ```tsx
307
+ interface Column<T> {
308
+ key: keyof T | string; // Data property key
309
+ label: string; // Display header label
310
+ sortable?: boolean; // Enable sorting (default: true)
311
+ filterable?: boolean; // Enable in advanced filters (default: true)
312
+ dataType?: 'string' | 'number' | 'boolean' | 'date' | 'datetime';
313
+ width?: string | number; // Fixed column width
314
+ minWidth?: string | number; // Minimum column width
315
+ maxWidth?: string | number; // Maximum column width
316
+ align?: 'left' | 'center' | 'right';
317
+ render?: (value: any, row: T, index: number) => ReactNode;
318
+ }
319
+ ```
320
+
321
+ ## HTTP Configuration
322
+
323
+ ```tsx
324
+ interface HttpConfig {
325
+ bearerToken?: string; // Authorization: Bearer <token>
326
+ apiKey?: string; // X-API-Key header
327
+ customHeaders?: Record<string, string>;
328
+ method?: 'GET' | 'POST'; // HTTP method (default: GET)
329
+ postDataFormat?: 'form' | 'json'; // POST body format
330
+ withCredentials?: boolean; // Include cookies
331
+ timeout?: number; // Request timeout in ms
332
+ }
333
+ ```
334
+
335
+ # Server Request & Response Format
336
+
337
+ **Request sent to your API:**
338
+
339
+ ```tsx
340
+ interface ServerRequest {
341
+ page: number; // Current page number
342
+ pageSize: number; // Items per page
343
+ search: string; // Global search term
344
+ sortColumn: string; // Column to sort by
345
+ filters: ActiveFilter[]; // Applied filters
346
+ continuationToken: // Token to grab more records (For server side pagination, if enabled)
347
+ }
348
+ ```
349
+
350
+ **Expected response format:**
351
+
352
+ ```tsx
353
+ interface ServerResponse<T> {
354
+ items: T[]; // Data array for current page
355
+ count: number; // Total number of records
356
+ hasMore: boolean; // Whether more pages available
357
+ continuationToken: string; // Token to grab more records (For server side pagination, if enabled)
358
+ }
359
+ ```
360
+
361
+ ### Event Callbacks
362
+
363
+ | **Event** | **Signature** | **Description** |
364
+ | ----------------------- | -------------------------------------------------------------------- | -------------------------------------------- |
365
+ | **Data & State Events** | | |
366
+ | `onDataLoad` | `(data: ServerResponse<T>) => void` | Called when server data loads successfully |
367
+ | `onDataError` | `(error: Error, context: string) => void` | Called when any error occurs |
368
+ | `onLoadingStateChange` | `(loading: boolean, context: string) => void` | Called when loading state changes |
369
+ | `onPageChange` | `(page: number, paginationInfo: PaginationInfo) => void` | Called when user navigates pages |
370
+ | `onPageSizeChange` | `(size: number, paginationInfo: PaginationInfo) => void` | Called when page size changes |
371
+ | `onSortChange` | `(sortConfig: SortConfig) => void` | Called when sorting changes |
372
+ | `onFilterChange` | `(filters: ActiveFilter[]) => void` | Called when filters change |
373
+ | `onSearchChange` | `(searchTerm: string) => void` | Called when search term changes |
374
+ | `onTableRefresh` | `() => void` | Called when refresh is triggered |
375
+ | **Row & Cell Events** | | |
376
+ | `onTableRowClick` | `(row: T, event: MouseEvent) => void` | Called on single row click |
377
+ | `onTableRowDoubleClick` | `(row: T, event: MouseEvent) => boolean \| void` | Called on row double-click |
378
+ | `onRowSelect` | `(row: T, isSelected: boolean) => void` | Called when individual row selection changes |
379
+ | `onSelectionChange` | `(selectedRows: T[]) => void` | Called when overall selection changes |
380
+ | `onTableRowHover` | `(row: T \| null, event: MouseEvent) => void` | Called when hovering over rows |
381
+ | `onCellClick` | `(value: any, row: T, column: Column<T>, event: MouseEvent) => void` | Called when clicking individual cells |
382
+
383
+ |
384
+
385
+ ## ๐Ÿ“– Complete API Reference
386
+
387
+ **DataGrid Props**
388
+
389
+ | **Prop** | **Type** | **Default** | **Description** |
390
+ | ---------------------- | -------------------------------------- | ---------------------- | ---------------------------------------- |
391
+ | **Data Configuration** | | | |
392
+ | `data` | `T[]` | `-` | Static data array for client-side mode |
393
+ | `endpoint` | `string` | `-` | API endpoint for server-side data |
394
+ | `columns` | `Column<T>[]` | auto-detected | Column configuration array |
395
+ | **Feature Toggles** | | | |
396
+ | `enableSearch` | `boolean` | `true` | Enable global search functionality |
397
+ | `enableSorting` | `boolean` | `true` | Enable column sorting |
398
+ | `enableFilters` | `boolean` | `true` | Enable advanced filtering |
399
+ | `enableSelection` | `boolean` | `true` | Enable row selection with checkboxes |
400
+ | **Pagination** | | | |
401
+ | `pageSize` | `number` | `10` | Client-side pagination size |
402
+ | `serverPageSize` | `number` | `100` | Server request batch size |
403
+ | `pageSizeOptions` | `number[]` | `[5, 10, 25, 50, 100]` | Available page size options |
404
+ | **Styling** | | | |
405
+ | `variant` | `'default' \| 'striped' \| 'bordered'` | `'default'` | Visual theme variant |
406
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Size variant for padding and text |
407
+ | `className` | `string` | `''` | Additional CSS classes |
408
+ | **HTTP Configuration** | | | |
409
+ | `httpConfig` | `HttpConfig` | `-` | Authentication and request configuration |
410
+
411
+ ## ๐ŸŽจ Theming & Styling
412
+
413
+ ```tsx
414
+ // Clean, minimal design
415
+ <DataGrid variant="default" data={data} />
416
+
417
+ // Alternating row colors
418
+ <DataGrid variant="striped" data={data} />
419
+
420
+ // Full borders around cells
421
+ <DataGrid variant="bordered" data={data} />
422
+ ```
423
+
424
+ **Size Variants**
425
+
426
+ ```tsx
427
+ // Compact spacing
428
+ <DataGrid size="sm" data={data} />
429
+
430
+ // Standard spacing (default)
431
+ <DataGrid size="md" data={data} />
432
+
433
+ // Comfortable spacing
434
+ <DataGrid size="lg" data={data} />
435
+ ```
436
+
437
+ **Dark Mode Support**<br>
438
+ The DataGrid automatically adapts to dark mode when using Tailwind CSS:
439
+
440
+ ```tsx
441
+ // Wrap in dark mode provider
442
+ <div className="dark">
443
+ <DataGrid data={data} />
444
+ </div>
445
+ ```
446
+
447
+ **Custom Styling**<br>
448
+
449
+ ```tsx
450
+ <DataGrid
451
+ data={data}
452
+ className="shadow-xl rounded-xl overflow-hidden"
453
+ // Add custom CSS classes for complete control
454
+ />
455
+ ```
456
+
457
+ ## ๐Ÿ”ง Advanced Usage
458
+
459
+ **Custom Styling**<br>
460
+
461
+ ```tsx
462
+ const columns: Column<Employee>[] = [
463
+ {
464
+ key: 'employee',
465
+ label: 'Employee',
466
+ render: (_, employee) => (
467
+ <div className="flex items-center space-x-3">
468
+ <img src={employee.avatar} alt={employee.name} className="w-10 h-10 rounded-full" />
469
+ <div>
470
+ <div className="font-medium">{employee.name}</div>
471
+ <div className="text-sm text-gray-500">{employee.title}</div>
472
+ </div>
473
+ </div>
474
+ ),
475
+ },
476
+ {
477
+ key: 'performance',
478
+ label: 'Performance',
479
+ render: (score) => (
480
+ <div className="flex items-center">
481
+ <div className="flex-1 bg-gray-200 rounded-full h-2 mr-2">
482
+ <div className="bg-blue-500 h-2 rounded-full" style={{ width: `${score}%` }} />
483
+ </div>
484
+ <span className="text-sm font-medium">{score}%</span>
485
+ </div>
486
+ ),
487
+ },
488
+ ];
489
+ ```
490
+
491
+ **Advanced Filtering**
492
+
493
+ ```tsx
494
+ // The DataGrid automatically provides appropriate filter inputs:
495
+ // - String: text input with contains/equals/starts with/ends with
496
+ // - Number: number input with comparison operators
497
+ // - Date: date picker with before/after/on
498
+ // - Boolean: dropdown with true/false options
499
+
500
+ const columns = [
501
+ { key: 'name', label: 'Name', dataType: 'string' },
502
+ { key: 'salary', label: 'Salary', dataType: 'number' },
503
+ { key: 'startDate', label: 'Start Date', dataType: 'date' },
504
+ { key: 'isActive', label: 'Active', dataType: 'boolean' },
505
+ ];
506
+ ```
507
+
508
+ **Real-time Data with WebSockets**
509
+
510
+ ```tsx
511
+ function LiveDataGrid() {
512
+ const [data, setData] = useState([]);
513
+
514
+ useEffect(() => {
515
+ const ws = new WebSocket('ws://localhost:8080');
516
+ ws.onmessage = (event) => {
517
+ const updatedData = JSON.parse(event.data);
518
+ setData(updatedData);
519
+ };
520
+ return () => ws.close();
521
+ }, []);
522
+
523
+ return (
524
+ <DataGrid
525
+ data={data}
526
+ onTableRefresh={() => {
527
+ // Trigger server refresh
528
+ fetch('/api/refresh', { method: 'POST' });
529
+ }}
530
+ />
531
+ );
532
+ }
533
+ ```
534
+
535
+ ## ๐ŸŒ Server Integration Examples
536
+
537
+ **Node.js/Expresse**
538
+
539
+ ```tsx
540
+ app.post('/api/users', async (req, res) => {
541
+ const { page, pageSize, search, filters, continuationToken } = JSON.parse(req.body.request);
542
+
543
+ let query = User.find();
544
+
545
+ // Apply search
546
+ if (search) {
547
+ query = query.where({
548
+ $or: [
549
+ { name: { $regex: search, $options: 'i' } },
550
+ { email: { $regex: search, $options: 'i' } },
551
+ ],
552
+ });
553
+ }
554
+
555
+ // Apply filters
556
+ filters.forEach((filter) => {
557
+ query = query.where(filter.column)[getOperator(filter.operator)](filter.value);
558
+ });
559
+
560
+ // Handle continuation token (simple ID-based cursor)
561
+ if (continuationToken) {
562
+ const { lastId } = JSON.parse(Buffer.from(continuationToken, 'base64').toString());
563
+ query = query.where('_id').gt(lastId);
564
+ }
565
+
566
+ // Execute query
567
+ const items = await query.limit(pageSize + 1);
568
+ const hasMore = items.length > pageSize;
569
+ const resultItems = hasMore ? items.slice(0, pageSize) : items;
570
+
571
+ // Generate next token
572
+ let nextToken;
573
+ if (hasMore && resultItems.length > 0) {
574
+ const lastItem = resultItems[resultItems.length - 1];
575
+ nextToken = Buffer.from(JSON.stringify({ lastId: lastItem._id })).toString('base64');
576
+ }
577
+
578
+ res.json({
579
+ items: resultItems, // lowercase works
580
+ continuationToken: nextToken, // camelCase works
581
+ hasMore: hasMore, // camelCase works
582
+ count: resultItems.length, // lowercase works
583
+ });
584
+ });
585
+ ```
586
+
587
+ **ASP.Net Core**
588
+
589
+ ```tsx
590
+ [HttpPost("api/users")]
591
+ public async Task<IActionResult> GetUsers([FromBody] DataTableRequest request)
592
+ {
593
+ var query = _context.Users.AsQueryable();
594
+
595
+ // Apply search
596
+ if (!string.IsNullOrEmpty(request.Search))
597
+ query = query.Where(u => u.Name.Contains(request.Search) || u.Email.Contains(request.Search));
598
+
599
+ // Apply filters
600
+ foreach (var filter in request.Filters)
601
+ query = ApplyFilter(query, filter);
602
+
603
+ // Handle continuation token
604
+ if (!string.IsNullOrEmpty(request.ContinuationToken))
605
+ {
606
+ var token = JsonSerializer.Deserialize<ContinuationToken>(
607
+ Encoding.UTF8.GetString(Convert.FromBase64String(request.ContinuationToken)));
608
+ query = query.Where(u => u.Id > token.LastId);
609
+ }
610
+
611
+ query = query.OrderBy(u => u.Id);
612
+ var items = await query.Take(request.PageSize + 1).ToListAsync();
613
+ var hasMore = items.Count > request.PageSize;
614
+ var resultItems = hasMore ? items.Take(request.PageSize).ToList() : items;
615
+
616
+ string nextToken = null;
617
+ if (hasMore && resultItems.Any())
618
+ {
619
+ var lastItem = resultItems.Last();
620
+ var tokenData = new ContinuationToken { LastId = lastItem.Id };
621
+ nextToken = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(tokenData)));
622
+ }
623
+
624
+ return Ok(new
625
+ {
626
+ Items = resultItems, // PascalCase works
627
+ ContinuationToken = nextToken, // PascalCase works
628
+ HasMore = hasMore, // PascalCase works
629
+ Count = resultItems.Count // PascalCase works
630
+ });
631
+ }
632
+ ```
633
+
634
+ **Laravel/PHP**
635
+
636
+ ```tsx
637
+ Route::post('/api/users', function (Request $request) {
638
+ $requestData = json_decode($request->input('request'), true);
639
+ $query = User::query();
640
+
641
+ // Apply search
642
+ if (!empty($requestData['search'])) {
643
+ $query->where(function($q) use ($requestData) {
644
+ $q->where('name', 'like', "%{$requestData['search']}%")
645
+ ->orWhere('email', 'like', "%{$requestData['search']}%");
646
+ });
647
+ }
648
+
649
+ // Apply filters
650
+ foreach ($requestData['filters'] as $filter) {
651
+ $query->where($filter['column'], $filter['operator'], $filter['value']);
652
+ }
653
+
654
+ // Handle continuation token
655
+ if (!empty($requestData['continuationToken'])) {
656
+ $tokenData = json_decode(base64_decode($requestData['continuationToken']), true);
657
+ $query->where('id', '>', $tokenData['lastId']);
658
+ }
659
+
660
+ $query->orderBy('id', 'asc');
661
+ $items = $query->take($requestData['pageSize'] + 1)->get();
662
+ $hasMore = $items->count() > $requestData['pageSize'];
663
+ $resultItems = $hasMore ? $items->take($requestData['pageSize']) : $items;
664
+
665
+ $nextToken = null;
666
+ if ($hasMore && $resultItems->isNotEmpty()) {
667
+ $lastItem = $resultItems->last();
668
+ $nextToken = base64_encode(json_encode(['lastId' => $lastItem->id]));
669
+ }
670
+
671
+ return response()->json([
672
+ 'data' => $resultItems->values(), // Laravel convention works
673
+ 'continuation_token' => $nextToken, // snake_case works
674
+ 'has_more' => $hasMore, // snake_case works
675
+ 'total' => $resultItems->count() // Laravel convention works
676
+ ]);
677
+ });
678
+ ```
679
+
680
+ ## ๐Ÿงช Testing
681
+
682
+ ```bash
683
+ # Run test suite
684
+ npm test
685
+
686
+ # Watch mode for development
687
+ npm run test:watch
688
+
689
+ # Coverage report
690
+ npm run test:coverage
691
+ ```
692
+
693
+ **Testing with Jest & React Testing Library**
694
+
695
+ ```tsx
696
+ import { render, screen, fireEvent } from '@testing-library/react';
697
+ import { DataGrid } from '@reactorui/datagrid';
698
+
699
+ test('handles user interactions', () => {
700
+ const onSelectionChange = jest.fn();
701
+ const testData = [{ id: 1, name: 'John', email: 'john@test.com' }];
702
+
703
+ render(<DataGrid data={testData} onSelectionChange={onSelectionChange} />);
704
+
705
+ // Test search
706
+ fireEvent.change(screen.getByPlaceholderText('Search...'), {
707
+ target: { value: 'John' },
708
+ });
709
+
710
+ // Test selection
711
+ fireEvent.click(screen.getAllByRole('checkbox')[1]);
712
+
713
+ expect(onSelectionChange).toHaveBeenCalledWith([testData[0]]);
714
+ });
715
+ ```
716
+
717
+ ## ๐Ÿ“š Examples
718
+
719
+ Check out the examples/ directory for complete working examples:
720
+
721
+ examples/basic/ - Simple usage with auto-detected columns
722
+ examples/advanced/ - Custom columns, renderers, and styling
723
+ examples/events/ - Comprehensive event handling demonstration
724
+
725
+ ## ๐Ÿš€ Performance Tips
726
+
727
+ Use server-side pagination for datasets > 1000 records
728
+ Implement custom renderers efficiently with React.memo
729
+ Debounce search for better UX with large datasets
730
+ Use specific column keys instead of auto-detection for better performance
731
+
732
+ ```tsx
733
+ const StatusBadge = React.memo(({ status }: { status: string }) => (
734
+ <span className={`badge ${status === 'active' ? 'bg-green' : 'bg-red'}`}>{status}</span>
735
+ ));
736
+
737
+ // Debounced search
738
+ const [debouncedSearch] = useDebounce(searchTerm, 300);
739
+ ```
740
+
741
+ ## ๐Ÿ”ง Development
742
+
743
+ ```bash
744
+ # Install dependencies
745
+ npm install
746
+
747
+ # Run tests
748
+ npm test
749
+
750
+ # Build library
751
+ npm run build
752
+
753
+ # Type checking
754
+ npm run typecheck
755
+
756
+ # Linting
757
+ npm run lint
758
+
759
+ # Format code
760
+ npm run format
761
+ ```
762
+
763
+ ## ๐Ÿค Contributing
764
+
765
+ We welcome contributions! Please see our Contributing Guide for details.
766
+
767
+ Fork the repository
768
+ Create your feature branch (git checkout -b feature/amazing-feature)
769
+ Write tests for your changes
770
+ Commit your changes (git commit -m 'Add amazing feature')
771
+ Push to the branch (git push origin feature/amazing-feature)
772
+ Open a Pull Request
773
+
774
+ ## ๐Ÿ“„ License
775
+
776
+ This project is licensed under the MIT License - see the LICENSE file for details.
777
+
778
+ Made with โค๏ธ by ReactorUI