@ackplus/react-tanstack-data-table 1.0.19-beta-0.16 → 1.0.21
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
|
@@ -132,7 +132,9 @@ function MyDataTable() {
|
|
|
132
132
|
|------|------|---------|-------------|
|
|
133
133
|
| `enableRowSelection` | `boolean \| ((row) => boolean)` | `false` | Enable row selection |
|
|
134
134
|
| `enableMultiRowSelection` | `boolean` | `true` | Allow multiple row selection |
|
|
135
|
-
| `
|
|
135
|
+
| `selectMode` | `'page' \| 'all'` | `'page'` | Selection scope (page or all data) |
|
|
136
|
+
| `isRowSelectable` | `(params: {row: T, id: string}) => boolean` | - | Control if specific row is selectable |
|
|
137
|
+
| `onSelectionChange` | `(selection: SelectionState) => void` | - | Selection state change callback |
|
|
136
138
|
| `enableBulkActions` | `boolean` | `false` | Enable bulk actions toolbar |
|
|
137
139
|
| `bulkActions` | `(selectionState: SelectionState) => ReactNode` | - | Custom bulk actions component |
|
|
138
140
|
|
|
@@ -158,6 +160,7 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
158
160
|
|------|------|---------|-------------|
|
|
159
161
|
| `enablePagination` | `boolean` | `true` | Enable pagination |
|
|
160
162
|
| `paginationMode` | `'client' \| 'server'` | `'client'` | Pagination mode |
|
|
163
|
+
| `onPaginationChange` | `(pagination: PaginationState) => void` | - | Pagination change callback |
|
|
161
164
|
| `initialState.pagination` | `{pageIndex: number, pageSize: number}` | `{pageIndex: 0, pageSize: 50}` | Initial pagination state |
|
|
162
165
|
|
|
163
166
|
### Filtering & Search
|
|
@@ -167,7 +170,9 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
167
170
|
| `enableGlobalFilter` | `boolean` | `true` | Enable global search |
|
|
168
171
|
| `enableColumnFilter` | `boolean` | `false` | Enable individual column filters |
|
|
169
172
|
| `filterMode` | `'client' \| 'server'` | `'client'` | Filtering mode |
|
|
170
|
-
| `onColumnFiltersChange` | `(
|
|
173
|
+
| `onColumnFiltersChange` | `(filterState: ColumnFilterState) => void` | - | Column filters change callback |
|
|
174
|
+
| `onGlobalFilterChange` | `(globalFilter: string) => void` | - | Global filter change callback |
|
|
175
|
+
| `onColumnFilterChange` | `(columnFilter: ColumnFilterState) => void` | - | Column filter change callback |
|
|
171
176
|
| `extraFilter` | `ReactNode` | - | Additional filter components |
|
|
172
177
|
|
|
173
178
|
### Sorting
|
|
@@ -184,10 +189,11 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
184
189
|
|------|------|---------|-------------|
|
|
185
190
|
| `enableColumnVisibility` | `boolean` | `true` | Show/hide columns control |
|
|
186
191
|
| `enableColumnResizing` | `boolean` | `false` | Allow column resizing |
|
|
192
|
+
| `columnResizeMode` | `ColumnResizeMode` | `'onChange'` | Column resize mode |
|
|
187
193
|
| `enableColumnPinning` | `boolean` | `false` | Allow column pinning |
|
|
188
|
-
| `
|
|
194
|
+
| `enableColumnDragging` | `boolean` | `false` | Enable column reordering |
|
|
189
195
|
| `onColumnDragEnd` | `(order: string[]) => void` | - | Column reorder callback |
|
|
190
|
-
| `onColumnPinningChange` | `(pinning) => void` | - | Column pinning callback |
|
|
196
|
+
| `onColumnPinningChange` | `(pinning: ColumnPinningState) => void` | - | Column pinning callback |
|
|
191
197
|
|
|
192
198
|
### Export Features
|
|
193
199
|
|
|
@@ -195,12 +201,15 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
195
201
|
|------|------|---------|-------------|
|
|
196
202
|
| `enableExport` | `boolean` | `true` | Enable data export |
|
|
197
203
|
| `exportFilename` | `string` | `'export'` | Default export filename |
|
|
198
|
-
| `onExportProgress` | `(progress) => void` | - | Export progress callback |
|
|
199
|
-
| `onExportComplete` | `(result) => void` | - | Export completion callback |
|
|
200
|
-
| `onExportError` | `(error) => void` | - | Export error callback |
|
|
201
|
-
| `onServerExport` | `(filters) => Promise<{data, total}>` | - | Server-side export handler |
|
|
204
|
+
| `onExportProgress` | `(progress: {processedRows?, totalRows?, percentage?}) => void` | - | Export progress callback |
|
|
205
|
+
| `onExportComplete` | `(result: {success: boolean, filename: string, totalRows: number}) => void` | - | Export completion callback |
|
|
206
|
+
| `onExportError` | `(error: {message: string, code: string}) => void` | - | Export error callback |
|
|
207
|
+
| `onServerExport` | `(filters?: Partial<TableState>, selection?: SelectionState) => Promise<{data: any[], total: number}>` | - | Server-side export handler |
|
|
208
|
+
| `onExportCancel` | `() => void` | - | Export cancellation callback |
|
|
202
209
|
|
|
203
|
-
### Expandable Rows
|
|
210
|
+
### Expandable Rows (Enhanced Slot System)
|
|
211
|
+
|
|
212
|
+
Expandable rows are now fully integrated with the enhanced slot system, providing better customization and type safety.
|
|
204
213
|
|
|
205
214
|
| Prop | Type | Default | Description |
|
|
206
215
|
|------|------|---------|-------------|
|
|
@@ -208,6 +217,8 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
208
217
|
| `getRowCanExpand` | `(row) => boolean` | - | Determine if row can expand |
|
|
209
218
|
| `renderSubComponent` | `(row) => ReactNode` | - | Render expanded row content |
|
|
210
219
|
|
|
220
|
+
The expanding column is automatically added and can be customized through `slotProps.expandColumn` (see Special Column Configuration section above).
|
|
221
|
+
|
|
211
222
|
### Styling & Layout
|
|
212
223
|
|
|
213
224
|
| Prop | Type | Default | Description |
|
|
@@ -218,6 +229,8 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
218
229
|
| `fitToScreen` | `boolean` | `true` | Fit table to container width |
|
|
219
230
|
| `enableStickyHeaderOrFooter` | `boolean` | `false` | Sticky header/footer |
|
|
220
231
|
| `maxHeight` | `string \| number` | `'400px'` | Max table height |
|
|
232
|
+
| `tableContainerProps` | `object` | `{}` | Props for table container |
|
|
233
|
+
| `tableProps` | `object` | `{}` | Props for table element |
|
|
221
234
|
|
|
222
235
|
### Virtualization
|
|
223
236
|
|
|
@@ -232,8 +245,243 @@ This allows for efficient handling of large datasets where you might select "all
|
|
|
232
245
|
|------|------|---------|-------------|
|
|
233
246
|
| `slots` | `Partial<DataTableSlots<T>>` | `{}` | Custom component slots |
|
|
234
247
|
| `slotProps` | `PartialSlotProps<T>` | `{}` | Props for slot components |
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
248
|
+
| `initialState` | `Partial<TableState>` | `{}` | Initial table state |
|
|
249
|
+
| `skeletonRows` | `number` | `5` | Number of skeleton rows for loading state |
|
|
250
|
+
| `footerFilter` | `ReactNode` | - | Additional filter components in footer |
|
|
251
|
+
|
|
252
|
+
### Special Column Configuration (Enhanced Slot System)
|
|
253
|
+
|
|
254
|
+
Special columns (selection and expanding) are now handled through the enhanced slot system, providing better customization and type safety.
|
|
255
|
+
|
|
256
|
+
#### Selection Column Configuration
|
|
257
|
+
|
|
258
|
+
The selection column is automatically added when `enableRowSelection` is true and can be customized through `slotProps.selectionColumn`:
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
<DataTable
|
|
262
|
+
data={data}
|
|
263
|
+
columns={columns}
|
|
264
|
+
enableRowSelection
|
|
265
|
+
enableMultiRowSelection
|
|
266
|
+
slotProps={{
|
|
267
|
+
selectionColumn: {
|
|
268
|
+
width: 80,
|
|
269
|
+
pinLeft: true,
|
|
270
|
+
id: 'custom-selection',
|
|
271
|
+
// Custom column configuration
|
|
272
|
+
header: ({ table }) => (
|
|
273
|
+
<Checkbox
|
|
274
|
+
checked={table.getIsAllRowsSelected()}
|
|
275
|
+
indeterminate={table.getIsSomeRowsSelected()}
|
|
276
|
+
onChange={() => table.toggleAllRowsSelected()}
|
|
277
|
+
sx={{ color: 'primary.main' }}
|
|
278
|
+
/>
|
|
279
|
+
),
|
|
280
|
+
cell: ({ row, table }) => (
|
|
281
|
+
<Checkbox
|
|
282
|
+
checked={table.getIsRowSelected(row.id)}
|
|
283
|
+
onChange={() => table.toggleRowSelected(row.id)}
|
|
284
|
+
sx={{ color: 'secondary.main' }}
|
|
285
|
+
/>
|
|
286
|
+
),
|
|
287
|
+
},
|
|
288
|
+
}}
|
|
289
|
+
/>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
#### Expanding Column Configuration
|
|
293
|
+
|
|
294
|
+
The expanding column is automatically added when `enableExpanding` is true and can be customized through `slotProps.expandColumn`:
|
|
295
|
+
|
|
296
|
+
```tsx
|
|
297
|
+
<DataTable
|
|
298
|
+
data={data}
|
|
299
|
+
columns={columns}
|
|
300
|
+
enableExpanding
|
|
301
|
+
getRowCanExpand={(row) => row.original.details != null}
|
|
302
|
+
renderSubComponent={(row) => (
|
|
303
|
+
<Box p={2}>
|
|
304
|
+
<Typography variant="h6">Details</Typography>
|
|
305
|
+
<pre>{JSON.stringify(row.original.details, null, 2)}</pre>
|
|
306
|
+
</Box>
|
|
307
|
+
)}
|
|
308
|
+
slotProps={{
|
|
309
|
+
expandColumn: {
|
|
310
|
+
width: 60,
|
|
311
|
+
pinLeft: true,
|
|
312
|
+
id: 'custom-expand',
|
|
313
|
+
// Custom column configuration
|
|
314
|
+
header: 'Expand',
|
|
315
|
+
cell: ({ row }) => (
|
|
316
|
+
<IconButton
|
|
317
|
+
onClick={row.getToggleExpandedHandler()}
|
|
318
|
+
size="small"
|
|
319
|
+
sx={{ color: 'primary.main' }}
|
|
320
|
+
>
|
|
321
|
+
{row.getIsExpanded() ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
|
322
|
+
</IconButton>
|
|
323
|
+
),
|
|
324
|
+
},
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
#### Advanced Special Column Customization
|
|
330
|
+
|
|
331
|
+
You can completely replace the special column components using the slots system:
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
import { createSelectionColumn, createExpandingColumn } from '@ackplus/react-tanstack-data-table';
|
|
335
|
+
|
|
336
|
+
function CustomTable() {
|
|
337
|
+
// Create custom selection column
|
|
338
|
+
const customSelectionColumn = createSelectionColumn({
|
|
339
|
+
width: 100,
|
|
340
|
+
pinLeft: true,
|
|
341
|
+
header: ({ table }) => (
|
|
342
|
+
<Tooltip title="Select All">
|
|
343
|
+
<Checkbox
|
|
344
|
+
checked={table.getIsAllRowsSelected()}
|
|
345
|
+
indeterminate={table.getIsSomeRowsSelected()}
|
|
346
|
+
onChange={() => table.toggleAllRowsSelected()}
|
|
347
|
+
sx={{
|
|
348
|
+
color: 'primary.main',
|
|
349
|
+
'&.Mui-checked': { color: 'primary.main' }
|
|
350
|
+
}}
|
|
351
|
+
/>
|
|
352
|
+
</Tooltip>
|
|
353
|
+
),
|
|
354
|
+
cell: ({ row, table }) => (
|
|
355
|
+
<Tooltip title="Select Row">
|
|
356
|
+
<Checkbox
|
|
357
|
+
checked={table.getIsRowSelected(row.id)}
|
|
358
|
+
onChange={() => table.toggleRowSelected(row.id)}
|
|
359
|
+
sx={{
|
|
360
|
+
color: 'secondary.main',
|
|
361
|
+
'&.Mui-checked': { color: 'secondary.main' }
|
|
362
|
+
}}
|
|
363
|
+
/>
|
|
364
|
+
</Tooltip>
|
|
365
|
+
),
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Create custom expanding column
|
|
369
|
+
const customExpandingColumn = createExpandingColumn({
|
|
370
|
+
width: 80,
|
|
371
|
+
pinLeft: true,
|
|
372
|
+
header: 'Details',
|
|
373
|
+
cell: ({ row }) => (
|
|
374
|
+
<Tooltip title={row.getIsExpanded() ? "Collapse" : "Expand"}>
|
|
375
|
+
<IconButton
|
|
376
|
+
onClick={row.getToggleExpandedHandler()}
|
|
377
|
+
size="small"
|
|
378
|
+
sx={{
|
|
379
|
+
color: 'primary.main',
|
|
380
|
+
transition: 'transform 0.2s',
|
|
381
|
+
transform: row.getIsExpanded() ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
382
|
+
}}
|
|
383
|
+
>
|
|
384
|
+
<KeyboardArrowDownIcon />
|
|
385
|
+
</IconButton>
|
|
386
|
+
</Tooltip>
|
|
387
|
+
),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<DataTable
|
|
392
|
+
data={data}
|
|
393
|
+
columns={[customSelectionColumn, customExpandingColumn, ...columns]}
|
|
394
|
+
enableRowSelection
|
|
395
|
+
enableExpanding
|
|
396
|
+
// ... other props
|
|
397
|
+
/>
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Special Column Configuration Options
|
|
403
|
+
|
|
404
|
+
| Property | Type | Default | Description |
|
|
405
|
+
|----------|------|---------|-------------|
|
|
406
|
+
| `width` | `number` | `60` | Column width in pixels |
|
|
407
|
+
| `pinLeft` | `boolean` | `false` | Pin column to the left |
|
|
408
|
+
| `id` | `string` | Auto-generated | Custom column ID |
|
|
409
|
+
| `header` | `ReactNode \| (props) => ReactNode` | Default header | Custom header component |
|
|
410
|
+
| `cell` | `(props) => ReactNode` | Default cell | Custom cell component |
|
|
411
|
+
| `sx` | `SxProps` | `{}` | Custom styling |
|
|
412
|
+
| `className` | `string` | - | Custom CSS class |
|
|
413
|
+
| `style` | `CSSProperties` | - | Custom inline styles |
|
|
414
|
+
|
|
415
|
+
#### Utility Functions for Special Columns
|
|
416
|
+
|
|
417
|
+
The library provides utility functions to create custom special columns:
|
|
418
|
+
|
|
419
|
+
> **Note**: Special columns are now handled through the enhanced slot system instead of table props. This provides better type safety, more customization options, and consistent behavior with the rest of the component system.
|
|
420
|
+
|
|
421
|
+
```tsx
|
|
422
|
+
import { createSelectionColumn, createExpandingColumn } from '@ackplus/react-tanstack-data-table';
|
|
423
|
+
|
|
424
|
+
// Create a custom selection column
|
|
425
|
+
const customSelectionColumn = createSelectionColumn({
|
|
426
|
+
width: 100,
|
|
427
|
+
pinLeft: true,
|
|
428
|
+
multiSelect: true, // Enable multi-select
|
|
429
|
+
header: ({ table }) => (
|
|
430
|
+
<Tooltip title="Select All Rows">
|
|
431
|
+
<Checkbox
|
|
432
|
+
checked={table.getIsAllRowsSelected()}
|
|
433
|
+
indeterminate={table.getIsSomeRowsSelected()}
|
|
434
|
+
onChange={() => table.toggleAllRowsSelected()}
|
|
435
|
+
sx={{ color: 'primary.main' }}
|
|
436
|
+
/>
|
|
437
|
+
</Tooltip>
|
|
438
|
+
),
|
|
439
|
+
cell: ({ row, table }) => (
|
|
440
|
+
<Tooltip title="Select Row">
|
|
441
|
+
<Checkbox
|
|
442
|
+
checked={table.getIsRowSelected(row.id)}
|
|
443
|
+
onChange={() => table.toggleRowSelected(row.id)}
|
|
444
|
+
sx={{ color: 'secondary.main' }}
|
|
445
|
+
/>
|
|
446
|
+
</Tooltip>
|
|
447
|
+
),
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Create a custom expanding column
|
|
451
|
+
const customExpandingColumn = createExpandingColumn({
|
|
452
|
+
width: 80,
|
|
453
|
+
pinLeft: true,
|
|
454
|
+
header: 'Details',
|
|
455
|
+
cell: ({ row }) => (
|
|
456
|
+
<Tooltip title={row.getIsExpanded() ? "Collapse Details" : "Expand Details"}>
|
|
457
|
+
<IconButton
|
|
458
|
+
onClick={row.getToggleExpandedHandler()}
|
|
459
|
+
size="small"
|
|
460
|
+
sx={{
|
|
461
|
+
color: 'primary.main',
|
|
462
|
+
transition: 'all 0.2s ease',
|
|
463
|
+
transform: row.getIsExpanded() ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
464
|
+
'&:hover': {
|
|
465
|
+
backgroundColor: 'primary.light',
|
|
466
|
+
color: 'primary.contrastText',
|
|
467
|
+
},
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
<KeyboardArrowDownIcon />
|
|
471
|
+
</IconButton>
|
|
472
|
+
</Tooltip>
|
|
473
|
+
),
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// Use in your table
|
|
477
|
+
<DataTable
|
|
478
|
+
columns={[customSelectionColumn, customExpandingColumn, ...columns]}
|
|
479
|
+
data={data}
|
|
480
|
+
enableRowSelection
|
|
481
|
+
enableExpanding
|
|
482
|
+
// ... other props
|
|
483
|
+
/>
|
|
484
|
+
```
|
|
237
485
|
|
|
238
486
|
## 🔥 Advanced Examples
|
|
239
487
|
|
|
@@ -278,9 +526,11 @@ function ServerSideTable() {
|
|
|
278
526
|
}
|
|
279
527
|
```
|
|
280
528
|
|
|
281
|
-
### Row Selection with Bulk Actions
|
|
529
|
+
### Row Selection with Bulk Actions and Enhanced Slot System
|
|
282
530
|
|
|
283
531
|
```tsx
|
|
532
|
+
import { CheckCircleIcon, RadioButtonUncheckedIcon } from '@mui/icons-material';
|
|
533
|
+
|
|
284
534
|
function SelectableTable() {
|
|
285
535
|
const [selectedUsers, setSelectedUsers] = useState([]);
|
|
286
536
|
|
|
@@ -322,7 +572,34 @@ function SelectableTable() {
|
|
|
322
572
|
enableMultiRowSelection
|
|
323
573
|
enableBulkActions
|
|
324
574
|
bulkActions={bulkActions}
|
|
325
|
-
|
|
575
|
+
onSelectionChange={setSelectedUsers}
|
|
576
|
+
slotProps={{
|
|
577
|
+
selectionColumn: {
|
|
578
|
+
width: 80,
|
|
579
|
+
pinLeft: true,
|
|
580
|
+
header: ({ table }) => (
|
|
581
|
+
<Checkbox
|
|
582
|
+
checked={table.getIsAllRowsSelected()}
|
|
583
|
+
indeterminate={table.getIsSomeRowsSelected()}
|
|
584
|
+
onChange={() => table.toggleAllRowsSelected()}
|
|
585
|
+
sx={{
|
|
586
|
+
color: 'primary.main',
|
|
587
|
+
'&.Mui-checked': { color: 'primary.main' }
|
|
588
|
+
}}
|
|
589
|
+
/>
|
|
590
|
+
),
|
|
591
|
+
cell: ({ row, table }) => (
|
|
592
|
+
<Checkbox
|
|
593
|
+
checked={table.getIsRowSelected(row.id)}
|
|
594
|
+
onChange={() => table.toggleRowSelected(row.id)}
|
|
595
|
+
sx={{
|
|
596
|
+
color: 'secondary.main',
|
|
597
|
+
'&.Mui-checked': { color: 'secondary.main' }
|
|
598
|
+
}}
|
|
599
|
+
/>
|
|
600
|
+
),
|
|
601
|
+
},
|
|
602
|
+
}}
|
|
326
603
|
/>
|
|
327
604
|
);
|
|
328
605
|
}
|
|
@@ -369,9 +646,11 @@ function FilterableTable() {
|
|
|
369
646
|
}
|
|
370
647
|
```
|
|
371
648
|
|
|
372
|
-
### Expandable Rows
|
|
649
|
+
### Expandable Rows with Enhanced Slot System
|
|
373
650
|
|
|
374
651
|
```tsx
|
|
652
|
+
import { ExpandMoreIcon, ExpandLessIcon } from '@mui/icons-material';
|
|
653
|
+
|
|
375
654
|
function ExpandableTable() {
|
|
376
655
|
const renderSubComponent = (row) => (
|
|
377
656
|
<Box p={2}>
|
|
@@ -396,6 +675,26 @@ function ExpandableTable() {
|
|
|
396
675
|
enableExpanding
|
|
397
676
|
getRowCanExpand={(row) => row.original.details != null}
|
|
398
677
|
renderSubComponent={renderSubComponent}
|
|
678
|
+
slotProps={{
|
|
679
|
+
expandColumn: {
|
|
680
|
+
width: 60,
|
|
681
|
+
pinLeft: true,
|
|
682
|
+
header: 'Details',
|
|
683
|
+
cell: ({ row }) => (
|
|
684
|
+
<IconButton
|
|
685
|
+
onClick={row.getToggleExpandedHandler()}
|
|
686
|
+
size="small"
|
|
687
|
+
sx={{
|
|
688
|
+
color: 'primary.main',
|
|
689
|
+
transition: 'transform 0.2s',
|
|
690
|
+
transform: row.getIsExpanded() ? 'rotate(180deg)' : 'rotate(0deg)',
|
|
691
|
+
}}
|
|
692
|
+
>
|
|
693
|
+
{row.getIsExpanded() ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
|
694
|
+
</IconButton>
|
|
695
|
+
),
|
|
696
|
+
},
|
|
697
|
+
}}
|
|
399
698
|
/>
|
|
400
699
|
);
|
|
401
700
|
}
|
|
@@ -487,6 +786,137 @@ interface DataTableColumn<T> extends ColumnDef<T> {
|
|
|
487
786
|
}
|
|
488
787
|
```
|
|
489
788
|
|
|
789
|
+
### DataTable API
|
|
790
|
+
|
|
791
|
+
The DataTable exposes a comprehensive API through refs for programmatic control:
|
|
792
|
+
|
|
793
|
+
```tsx
|
|
794
|
+
import { useRef } from 'react';
|
|
795
|
+
import { DataTable, DataTableApi } from '@ackplus/react-tanstack-data-table';
|
|
796
|
+
|
|
797
|
+
function MyComponent() {
|
|
798
|
+
const tableRef = useRef<DataTableApi<User>>(null);
|
|
799
|
+
|
|
800
|
+
const handleGetData = () => {
|
|
801
|
+
const allData = tableRef.current?.data.getAllData();
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
return (
|
|
805
|
+
<DataTable
|
|
806
|
+
ref={tableRef}
|
|
807
|
+
columns={columns}
|
|
808
|
+
data={data}
|
|
809
|
+
/>
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
#### Available API Methods
|
|
815
|
+
|
|
816
|
+
**Column Management:**
|
|
817
|
+
- `columnVisibility.showColumn(columnId)` - Show specific column
|
|
818
|
+
- `columnVisibility.hideColumn(columnId)` - Hide specific column
|
|
819
|
+
- `columnVisibility.toggleColumn(columnId)` - Toggle column visibility
|
|
820
|
+
- `columnVisibility.showAllColumns()` - Show all columns
|
|
821
|
+
- `columnVisibility.hideAllColumns()` - Hide all columns
|
|
822
|
+
- `columnVisibility.resetColumnVisibility()` - Reset to default visibility
|
|
823
|
+
|
|
824
|
+
**Column Ordering:**
|
|
825
|
+
- `columnOrdering.setColumnOrder(order)` - Set column order
|
|
826
|
+
- `columnOrdering.moveColumn(columnId, toIndex)` - Move column to position
|
|
827
|
+
- `columnOrdering.resetColumnOrder()` - Reset to default order
|
|
828
|
+
|
|
829
|
+
**Column Pinning:**
|
|
830
|
+
- `columnPinning.pinColumnLeft(columnId)` - Pin column to left
|
|
831
|
+
- `columnPinning.pinColumnRight(columnId)` - Pin column to right
|
|
832
|
+
- `columnPinning.unpinColumn(columnId)` - Unpin column
|
|
833
|
+
- `columnPinning.setPinning(pinning)` - Set pinning state
|
|
834
|
+
- `columnPinning.resetColumnPinning()` - Reset pinning
|
|
835
|
+
|
|
836
|
+
**Column Resizing:**
|
|
837
|
+
- `columnResizing.resizeColumn(columnId, width)` - Resize column
|
|
838
|
+
- `columnResizing.autoSizeColumn(columnId)` - Auto-size column
|
|
839
|
+
- `columnResizing.autoSizeAllColumns()` - Auto-size all columns
|
|
840
|
+
- `columnResizing.resetColumnSizing()` - Reset column sizing
|
|
841
|
+
|
|
842
|
+
**Filtering:**
|
|
843
|
+
- `filtering.setGlobalFilter(filter)` - Set global filter
|
|
844
|
+
- `filtering.clearGlobalFilter()` - Clear global filter
|
|
845
|
+
- `filtering.setColumnFilters(filters)` - Set column filters
|
|
846
|
+
- `filtering.addColumnFilter(columnId, operator, value)` - Add column filter
|
|
847
|
+
- `filtering.removeColumnFilter(filterId)` - Remove column filter
|
|
848
|
+
- `filtering.clearAllFilters()` - Clear all filters
|
|
849
|
+
- `filtering.resetFilters()` - Reset all filters
|
|
850
|
+
|
|
851
|
+
**Sorting:**
|
|
852
|
+
- `sorting.setSorting(sortingState)` - Set sorting state
|
|
853
|
+
- `sorting.sortColumn(columnId, direction)` - Sort specific column
|
|
854
|
+
- `sorting.clearSorting()` - Clear all sorting
|
|
855
|
+
- `sorting.resetSorting()` - Reset sorting
|
|
856
|
+
|
|
857
|
+
**Pagination:**
|
|
858
|
+
- `pagination.goToPage(pageIndex)` - Go to specific page
|
|
859
|
+
- `pagination.nextPage()` - Go to next page
|
|
860
|
+
- `pagination.previousPage()` - Go to previous page
|
|
861
|
+
- `pagination.setPageSize(pageSize)` - Set page size
|
|
862
|
+
- `pagination.goToFirstPage()` - Go to first page
|
|
863
|
+
- `pagination.goToLastPage()` - Go to last page
|
|
864
|
+
|
|
865
|
+
**Selection:**
|
|
866
|
+
- `selection.selectRow(rowId)` - Select specific row
|
|
867
|
+
- `selection.deselectRow(rowId)` - Deselect specific row
|
|
868
|
+
- `selection.toggleRowSelection(rowId)` - Toggle row selection
|
|
869
|
+
- `selection.selectAll()` - Select all rows
|
|
870
|
+
- `selection.deselectAll()` - Deselect all rows
|
|
871
|
+
- `selection.toggleSelectAll()` - Toggle select all
|
|
872
|
+
- `selection.getSelectionState()` - Get current selection state
|
|
873
|
+
- `selection.getSelectedRows()` - Get selected rows
|
|
874
|
+
- `selection.getSelectedCount()` - Get selected count
|
|
875
|
+
- `selection.isRowSelected(rowId)` - Check if row is selected
|
|
876
|
+
|
|
877
|
+
**Data Management:**
|
|
878
|
+
- `data.refresh()` - Refresh data
|
|
879
|
+
- `data.reload()` - Reload data
|
|
880
|
+
- `data.getAllData()` - Get all data
|
|
881
|
+
- `data.getRowData(rowId)` - Get specific row data
|
|
882
|
+
- `data.getRowByIndex(index)` - Get row by index
|
|
883
|
+
- `data.updateRow(rowId, updates)` - Update specific row
|
|
884
|
+
- `data.updateRowByIndex(index, updates)` - Update row by index
|
|
885
|
+
- `data.insertRow(newRow, index?)` - Insert new row
|
|
886
|
+
- `data.deleteRow(rowId)` - Delete specific row
|
|
887
|
+
- `data.deleteRowByIndex(index)` - Delete row by index
|
|
888
|
+
- `data.deleteSelectedRows()` - Delete selected rows
|
|
889
|
+
- `data.replaceAllData(newData)` - Replace all data
|
|
890
|
+
- `data.updateMultipleRows(updates)` - Update multiple rows
|
|
891
|
+
- `data.insertMultipleRows(newRows, startIndex?)` - Insert multiple rows
|
|
892
|
+
- `data.deleteMultipleRows(rowIds)` - Delete multiple rows
|
|
893
|
+
- `data.updateField(rowId, fieldName, value)` - Update specific field
|
|
894
|
+
- `data.updateFieldByIndex(index, fieldName, value)` - Update field by index
|
|
895
|
+
- `data.findRows(predicate)` - Find rows by predicate
|
|
896
|
+
- `data.findRowIndex(predicate)` - Find row index by predicate
|
|
897
|
+
- `data.getDataCount()` - Get data count
|
|
898
|
+
- `data.getFilteredDataCount()` - Get filtered data count
|
|
899
|
+
|
|
900
|
+
**Layout Management:**
|
|
901
|
+
- `layout.resetLayout()` - Reset layout
|
|
902
|
+
- `layout.resetAll()` - Reset everything
|
|
903
|
+
- `layout.saveLayout()` - Save current layout
|
|
904
|
+
- `layout.restoreLayout(layout)` - Restore saved layout
|
|
905
|
+
|
|
906
|
+
**Export:**
|
|
907
|
+
- `export.exportCSV(options?)` - Export to CSV
|
|
908
|
+
- `export.exportExcel(options?)` - Export to Excel
|
|
909
|
+
- `export.exportServerData(options)` - Server-side export
|
|
910
|
+
- `export.isExporting()` - Check if exporting
|
|
911
|
+
- `export.cancelExport()` - Cancel export
|
|
912
|
+
|
|
913
|
+
**Table State:**
|
|
914
|
+
- `state.getTableState()` - Get current table state
|
|
915
|
+
- `state.getCurrentFilters()` - Get current filters
|
|
916
|
+
- `state.getCurrentSorting()` - Get current sorting
|
|
917
|
+
- `state.getCurrentPagination()` - Get current pagination
|
|
918
|
+
- `state.getCurrentSelection()` - Get current selection
|
|
919
|
+
|
|
490
920
|
### useDataTableApi Hook
|
|
491
921
|
|
|
492
922
|
Access the table's imperative API:
|
|
@@ -512,46 +942,446 @@ function MyComponent() {
|
|
|
512
942
|
}
|
|
513
943
|
```
|
|
514
944
|
|
|
515
|
-
## 🎨 Customization
|
|
945
|
+
## 🎨 Enhanced Customization with Slots System
|
|
946
|
+
|
|
947
|
+
The enhanced slot system provides powerful customization capabilities for DataTable components without limitations. This system allows you to override any component with full prop control and proper TypeScript support.
|
|
948
|
+
|
|
949
|
+
### Key Features
|
|
516
950
|
|
|
517
|
-
|
|
951
|
+
- **Full Component Customization**: Replace any component without limitations
|
|
952
|
+
- **Intelligent Prop Merging**: Special handling for `sx`, `style`, and `className` props
|
|
953
|
+
- **Enhanced Type Safety**: Better TypeScript inference and proper component prop typing
|
|
954
|
+
- **Performance Optimized**: Efficient prop merging and component creation
|
|
955
|
+
- **Easy Migration**: Works with existing code while providing enhanced capabilities
|
|
518
956
|
|
|
519
|
-
|
|
957
|
+
### Basic Slot Customization
|
|
520
958
|
|
|
521
959
|
```tsx
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
pagination: MyCustomPagination,
|
|
525
|
-
loadingRow: MyCustomLoadingRow,
|
|
526
|
-
emptyRow: MyCustomEmptyRow,
|
|
527
|
-
};
|
|
960
|
+
import { DataTable } from '@ackplus/react-tanstack-data-table';
|
|
961
|
+
import { Star as StarIcon } from '@mui/icons-material';
|
|
528
962
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
963
|
+
// Custom icon component
|
|
964
|
+
const CustomSearchIcon = (props) => (
|
|
965
|
+
<StarIcon {...props} sx={{ color: 'warning.main', ...props.sx }} />
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
function MyTable() {
|
|
969
|
+
return (
|
|
970
|
+
<DataTable
|
|
971
|
+
data={data}
|
|
972
|
+
columns={columns}
|
|
973
|
+
slots={{
|
|
974
|
+
searchIcon: CustomSearchIcon,
|
|
975
|
+
}}
|
|
976
|
+
slotProps={{
|
|
977
|
+
searchIcon: {
|
|
978
|
+
fontSize: 'large',
|
|
979
|
+
sx: {
|
|
980
|
+
animation: 'pulse 2s infinite',
|
|
981
|
+
'@keyframes pulse': {
|
|
982
|
+
'0%': { transform: 'scale(1)' },
|
|
983
|
+
'50%': { transform: 'scale(1.1)' },
|
|
984
|
+
'100%': { transform: 'scale(1)' },
|
|
985
|
+
},
|
|
986
|
+
},
|
|
987
|
+
},
|
|
988
|
+
}}
|
|
989
|
+
/>
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
### Advanced Component Replacement
|
|
995
|
+
|
|
996
|
+
```tsx
|
|
997
|
+
import { styled, alpha } from '@mui/material/styles';
|
|
998
|
+
|
|
999
|
+
const CustomToolbar = styled(Box)(({ theme }) => ({
|
|
1000
|
+
display: 'flex',
|
|
1001
|
+
justifyContent: 'space-between',
|
|
1002
|
+
alignItems: 'center',
|
|
1003
|
+
padding: theme.spacing(2),
|
|
1004
|
+
backgroundColor: alpha(theme.palette.primary.main, 0.05),
|
|
1005
|
+
borderRadius: theme.shape.borderRadius,
|
|
1006
|
+
}));
|
|
1007
|
+
|
|
1008
|
+
const CustomSearchInput = styled('input')(({ theme }) => ({
|
|
1009
|
+
padding: theme.spacing(1, 2),
|
|
1010
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
1011
|
+
borderRadius: theme.shape.borderRadius,
|
|
1012
|
+
fontSize: theme.typography.body2.fontSize,
|
|
1013
|
+
'&:focus': {
|
|
1014
|
+
outline: 'none',
|
|
1015
|
+
borderColor: theme.palette.primary.main,
|
|
1016
|
+
boxShadow: `0 0 0 2px ${alpha(theme.palette.primary.main, 0.2)}`,
|
|
1017
|
+
},
|
|
1018
|
+
}));
|
|
1019
|
+
|
|
1020
|
+
function AdvancedTable() {
|
|
1021
|
+
return (
|
|
1022
|
+
<DataTable
|
|
1023
|
+
data={data}
|
|
1024
|
+
columns={columns}
|
|
1025
|
+
slots={{
|
|
1026
|
+
toolbar: CustomToolbar,
|
|
1027
|
+
searchInput: ({ value, onChange, placeholder, ...props }) => (
|
|
1028
|
+
<CustomSearchInput
|
|
1029
|
+
type="text"
|
|
1030
|
+
value={value}
|
|
1031
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1032
|
+
placeholder={placeholder}
|
|
1033
|
+
{...props}
|
|
1034
|
+
/>
|
|
1035
|
+
),
|
|
1036
|
+
}}
|
|
1037
|
+
slotProps={{
|
|
1038
|
+
toolbar: {
|
|
1039
|
+
sx: {
|
|
1040
|
+
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
1041
|
+
color: 'white',
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
searchInput: {
|
|
1045
|
+
placeholder: 'Search anything...',
|
|
1046
|
+
style: {
|
|
1047
|
+
minWidth: '300px',
|
|
1048
|
+
},
|
|
1049
|
+
},
|
|
1050
|
+
}}
|
|
1051
|
+
/>
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
### Complete Customization Example
|
|
1057
|
+
|
|
1058
|
+
```tsx
|
|
1059
|
+
function FullyCustomizedTable() {
|
|
1060
|
+
const theme = useTheme();
|
|
1061
|
+
|
|
1062
|
+
return (
|
|
1063
|
+
<DataTable
|
|
1064
|
+
data={data}
|
|
1065
|
+
columns={columns}
|
|
1066
|
+
slots={{
|
|
1067
|
+
// Custom toolbar with complete styling freedom
|
|
1068
|
+
toolbar: CustomToolbar,
|
|
1069
|
+
|
|
1070
|
+
// Custom search input with full control
|
|
1071
|
+
searchInput: ({ value, onChange, placeholder, ...props }) => (
|
|
1072
|
+
<CustomSearchInput
|
|
1073
|
+
type="text"
|
|
1074
|
+
value={value}
|
|
1075
|
+
onChange={(e) => onChange(e.target.value)}
|
|
1076
|
+
placeholder={placeholder || 'Search anything...'}
|
|
1077
|
+
{...props}
|
|
1078
|
+
/>
|
|
1079
|
+
),
|
|
1080
|
+
|
|
1081
|
+
// Custom column visibility control
|
|
1082
|
+
columnVisibilityControl: (props) => {
|
|
1083
|
+
const { table, color, ...buttonProps } = props;
|
|
1084
|
+
return (
|
|
1085
|
+
<CustomButton
|
|
1086
|
+
variant="outlined"
|
|
1087
|
+
startIcon={<VisibilityIcon />}
|
|
1088
|
+
{...buttonProps}
|
|
1089
|
+
>
|
|
1090
|
+
Columns
|
|
1091
|
+
</CustomButton>
|
|
1092
|
+
);
|
|
1093
|
+
},
|
|
1094
|
+
|
|
1095
|
+
// Custom export button
|
|
1096
|
+
exportButton: (props) => {
|
|
1097
|
+
const { table, color, ...buttonProps } = props;
|
|
1098
|
+
return (
|
|
1099
|
+
<CustomButton
|
|
1100
|
+
variant="contained"
|
|
1101
|
+
startIcon={<ExportIcon />}
|
|
1102
|
+
sx={{
|
|
1103
|
+
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
|
|
1104
|
+
color: 'white',
|
|
1105
|
+
...buttonProps.sx
|
|
1106
|
+
}}
|
|
1107
|
+
{...buttonProps}
|
|
1108
|
+
>
|
|
1109
|
+
Export Data
|
|
1110
|
+
</CustomButton>
|
|
1111
|
+
);
|
|
1112
|
+
},
|
|
1113
|
+
|
|
1114
|
+
// Custom table with enhanced styling
|
|
1115
|
+
table: ({ children, ...props }) => (
|
|
1116
|
+
<Paper
|
|
1117
|
+
elevation={3}
|
|
1118
|
+
sx={{
|
|
1119
|
+
borderRadius: 2,
|
|
1120
|
+
overflow: 'hidden',
|
|
1121
|
+
border: `2px solid ${theme.palette.primary.main}`,
|
|
1122
|
+
}}
|
|
1123
|
+
>
|
|
1124
|
+
<table {...props} style={{ width: '100%' }}>
|
|
1125
|
+
{children}
|
|
1126
|
+
</table>
|
|
1127
|
+
</Paper>
|
|
1128
|
+
),
|
|
1129
|
+
|
|
1130
|
+
// Custom row with hover effects
|
|
1131
|
+
row: ({ children, row, ...props }) => (
|
|
1132
|
+
<tr
|
|
1133
|
+
{...props}
|
|
1134
|
+
style={{
|
|
1135
|
+
backgroundColor: row.index % 2 === 0 ? alpha(theme.palette.primary.main, 0.03) : 'transparent',
|
|
1136
|
+
transition: 'background-color 0.2s ease',
|
|
1137
|
+
cursor: 'pointer',
|
|
1138
|
+
...props.style,
|
|
1139
|
+
}}
|
|
1140
|
+
onMouseEnter={(e) => {
|
|
1141
|
+
e.currentTarget.style.backgroundColor = alpha(theme.palette.primary.main, 0.1);
|
|
1142
|
+
}}
|
|
1143
|
+
onMouseLeave={(e) => {
|
|
1144
|
+
e.currentTarget.style.backgroundColor = row.index % 2 === 0 ? alpha(theme.palette.primary.main, 0.03) : 'transparent';
|
|
1145
|
+
}}
|
|
1146
|
+
>
|
|
1147
|
+
{children}
|
|
1148
|
+
</tr>
|
|
1149
|
+
),
|
|
1150
|
+
}}
|
|
1151
|
+
|
|
1152
|
+
slotProps={{
|
|
1153
|
+
// Customize toolbar props
|
|
1154
|
+
toolbar: {
|
|
1155
|
+
title: 'Custom Users Table',
|
|
1156
|
+
subtitle: 'Manage your users with enhanced controls',
|
|
1157
|
+
sx: {
|
|
1158
|
+
background: `linear-gradient(135deg, ${alpha(theme.palette.primary.main, 0.1)} 0%, ${alpha(theme.palette.secondary.main, 0.1)} 100%)`,
|
|
1159
|
+
border: `1px solid ${alpha(theme.palette.primary.main, 0.2)}`,
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
|
|
1163
|
+
// Customize search input
|
|
1164
|
+
searchInput: {
|
|
1165
|
+
placeholder: 'Search users by name, email, or role...',
|
|
1166
|
+
style: {
|
|
1167
|
+
minWidth: '300px',
|
|
1168
|
+
fontSize: '14px',
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
|
|
1172
|
+
// Customize column visibility control
|
|
1173
|
+
columnVisibilityControl: {
|
|
1174
|
+
title: 'Manage Columns',
|
|
1175
|
+
menuSx: {
|
|
1176
|
+
minWidth: 250,
|
|
1177
|
+
maxHeight: 400,
|
|
1178
|
+
},
|
|
1179
|
+
titleSx: {
|
|
1180
|
+
color: theme.palette.primary.main,
|
|
1181
|
+
fontWeight: 'bold',
|
|
1182
|
+
},
|
|
1183
|
+
checkboxProps: {
|
|
1184
|
+
color: 'primary',
|
|
1185
|
+
},
|
|
1186
|
+
},
|
|
1187
|
+
|
|
1188
|
+
// Customize table container
|
|
1189
|
+
tableContainer: {
|
|
1190
|
+
sx: {
|
|
1191
|
+
maxHeight: '600px',
|
|
1192
|
+
'&::-webkit-scrollbar': {
|
|
1193
|
+
width: '8px',
|
|
1194
|
+
},
|
|
1195
|
+
'&::-webkit-scrollbar-track': {
|
|
1196
|
+
backgroundColor: alpha(theme.palette.grey[300], 0.5),
|
|
1197
|
+
borderRadius: '4px',
|
|
1198
|
+
},
|
|
1199
|
+
'&::-webkit-scrollbar-thumb': {
|
|
1200
|
+
backgroundColor: theme.palette.primary.main,
|
|
1201
|
+
borderRadius: '4px',
|
|
1202
|
+
'&:hover': {
|
|
1203
|
+
backgroundColor: theme.palette.primary.dark,
|
|
1204
|
+
},
|
|
1205
|
+
},
|
|
1206
|
+
},
|
|
1207
|
+
},
|
|
1208
|
+
|
|
1209
|
+
// Customize pagination
|
|
1210
|
+
pagination: {
|
|
1211
|
+
rowsPerPageOptions: [10, 25, 50, 100, 300, 500, 1000],
|
|
1212
|
+
sx: {
|
|
1213
|
+
'& .MuiTablePagination-toolbar': {
|
|
1214
|
+
backgroundColor: alpha(theme.palette.primary.main, 0.05),
|
|
1215
|
+
borderTop: `1px solid ${theme.palette.divider}`,
|
|
1216
|
+
},
|
|
1217
|
+
'& .MuiTablePagination-selectLabel, & .MuiTablePagination-displayedRows': {
|
|
1218
|
+
color: theme.palette.primary.main,
|
|
1219
|
+
fontWeight: 'medium',
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
},
|
|
1223
|
+
}}
|
|
1224
|
+
/>
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
538
1227
|
```
|
|
539
1228
|
|
|
540
1229
|
### Available Slots
|
|
541
1230
|
|
|
542
|
-
|
|
543
|
-
- `
|
|
544
|
-
- `
|
|
545
|
-
- `tableContainer` - Table container
|
|
1231
|
+
#### Container Slots
|
|
1232
|
+
- `root` - Main container
|
|
1233
|
+
- `tableContainer` - Table container wrapper
|
|
546
1234
|
- `table` - Table element
|
|
1235
|
+
|
|
1236
|
+
#### Header Slots
|
|
1237
|
+
- `toolbar` - Main toolbar
|
|
547
1238
|
- `header` - Table header
|
|
1239
|
+
- `headerRow` - Header row
|
|
1240
|
+
- `headerCell` - Header cell
|
|
1241
|
+
|
|
1242
|
+
#### Body Slots
|
|
548
1243
|
- `body` - Table body
|
|
549
1244
|
- `row` - Table row
|
|
550
1245
|
- `cell` - Table cell
|
|
551
|
-
|
|
552
|
-
|
|
1246
|
+
|
|
1247
|
+
#### Control Slots
|
|
1248
|
+
- `searchInput` - Search input component
|
|
1249
|
+
- `columnVisibilityControl` - Column visibility control
|
|
1250
|
+
- `columnCustomFilterControl` - Column filter control
|
|
1251
|
+
- `columnPinningControl` - Column pinning control
|
|
1252
|
+
- `exportButton` - Export button
|
|
1253
|
+
- `resetButton` - Reset button
|
|
1254
|
+
- `tableSizeControl` - Table size control
|
|
1255
|
+
- `bulkActionsToolbar` - Bulk actions toolbar
|
|
1256
|
+
|
|
1257
|
+
#### Icon Slots
|
|
1258
|
+
- `searchIcon` - Search icon
|
|
1259
|
+
- `filterIcon` - Filter icon
|
|
1260
|
+
- `exportIcon` - Export icon
|
|
1261
|
+
- `columnIcon` - Column visibility icon
|
|
1262
|
+
- `resetIcon` - Reset icon
|
|
1263
|
+
- `pinIcon` - Pin column icon
|
|
1264
|
+
- `unpinIcon` - Unpin column icon
|
|
1265
|
+
- `csvIcon` - CSV export icon
|
|
1266
|
+
- `excelIcon` - Excel export icon
|
|
1267
|
+
- `viewComfortableIcon` - Comfortable view icon
|
|
1268
|
+
- `viewCompactIcon` - Compact view icon
|
|
1269
|
+
|
|
1270
|
+
#### Special Slots
|
|
553
1271
|
- `loadingRow` - Loading state row
|
|
554
1272
|
- `emptyRow` - Empty state row
|
|
1273
|
+
- `footer` - Table footer
|
|
1274
|
+
- `pagination` - Pagination component
|
|
1275
|
+
|
|
1276
|
+
### Best Practices
|
|
1277
|
+
|
|
1278
|
+
#### 1. Component Composition
|
|
1279
|
+
```tsx
|
|
1280
|
+
// Good: Compose components properly
|
|
1281
|
+
const CustomControl = ({ children, ...props }) => (
|
|
1282
|
+
<Box sx={{ display: 'flex', gap: 1 }} {...props}>
|
|
1283
|
+
{children}
|
|
1284
|
+
</Box>
|
|
1285
|
+
);
|
|
1286
|
+
|
|
1287
|
+
// Usage
|
|
1288
|
+
slots={{
|
|
1289
|
+
toolbar: ({ children, ...props }) => (
|
|
1290
|
+
<CustomControl {...props}>
|
|
1291
|
+
{children}
|
|
1292
|
+
</CustomControl>
|
|
1293
|
+
),
|
|
1294
|
+
}}
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
#### 2. Prop Forwarding
|
|
1298
|
+
```tsx
|
|
1299
|
+
// Good: Always forward props
|
|
1300
|
+
const CustomIcon = (props) => (
|
|
1301
|
+
<StarIcon {...props} sx={{ color: 'primary.main', ...props.sx }} />
|
|
1302
|
+
);
|
|
1303
|
+
|
|
1304
|
+
// Bad: Not forwarding props
|
|
1305
|
+
const CustomIcon = () => <StarIcon color="primary" />;
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
#### 3. TypeScript Support
|
|
1309
|
+
```tsx
|
|
1310
|
+
// Good: Use proper typing
|
|
1311
|
+
interface CustomButtonProps {
|
|
1312
|
+
onClick?: () => void;
|
|
1313
|
+
children: React.ReactNode;
|
|
1314
|
+
[key: string]: any; // Allow additional props
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const CustomButton: React.FC<CustomButtonProps> = ({ children, ...props }) => (
|
|
1318
|
+
<Button {...props}>{children}</Button>
|
|
1319
|
+
);
|
|
1320
|
+
```
|
|
1321
|
+
|
|
1322
|
+
#### 4. Performance Considerations
|
|
1323
|
+
```tsx
|
|
1324
|
+
// Good: Memoize expensive components
|
|
1325
|
+
const CustomToolbar = React.memo(({ children, ...props }) => (
|
|
1326
|
+
<Box {...props}>{children}</Box>
|
|
1327
|
+
));
|
|
1328
|
+
|
|
1329
|
+
// Good: Use callbacks for event handlers
|
|
1330
|
+
const handleClick = useCallback(() => {
|
|
1331
|
+
// Handle click
|
|
1332
|
+
}, []);
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
### Migration from Basic Slots
|
|
1336
|
+
|
|
1337
|
+
```tsx
|
|
1338
|
+
// Before
|
|
1339
|
+
<DataTable
|
|
1340
|
+
slots={{
|
|
1341
|
+
searchIcon: MyIcon,
|
|
1342
|
+
}}
|
|
1343
|
+
slotProps={{
|
|
1344
|
+
searchIcon: { color: 'primary' },
|
|
1345
|
+
}}
|
|
1346
|
+
/>
|
|
1347
|
+
|
|
1348
|
+
// After (Enhanced)
|
|
1349
|
+
<DataTable
|
|
1350
|
+
slots={{
|
|
1351
|
+
searchIcon: MyIcon,
|
|
1352
|
+
}}
|
|
1353
|
+
slotProps={{
|
|
1354
|
+
searchIcon: {
|
|
1355
|
+
color: 'primary',
|
|
1356
|
+
sx: { fontSize: 20 }, // Now supports sx prop merging
|
|
1357
|
+
},
|
|
1358
|
+
}}
|
|
1359
|
+
/>
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
### Troubleshooting
|
|
1363
|
+
|
|
1364
|
+
#### Common Issues
|
|
1365
|
+
|
|
1366
|
+
1. **Props not merging correctly**
|
|
1367
|
+
- Ensure you're using the enhanced slot system
|
|
1368
|
+
- Check prop priority order (user > slot > default)
|
|
1369
|
+
|
|
1370
|
+
2. **TypeScript errors**
|
|
1371
|
+
- Use `[key: string]: any` for flexible prop interfaces
|
|
1372
|
+
- Ensure proper prop forwarding with spread operator
|
|
1373
|
+
- Filter out incompatible props like `table` and `color` for Button components
|
|
1374
|
+
|
|
1375
|
+
3. **Styling conflicts**
|
|
1376
|
+
- Check sx prop merging order
|
|
1377
|
+
- Use proper CSS specificity
|
|
1378
|
+
|
|
1379
|
+
4. **Performance issues**
|
|
1380
|
+
- Memoize expensive components
|
|
1381
|
+
- Use callbacks for event handlers
|
|
1382
|
+
- Avoid inline function definitions
|
|
1383
|
+
|
|
1384
|
+
The enhanced slot system provides unprecedented flexibility for customizing DataTable components. With proper prop merging, full TypeScript support, and intelligent component composition, you can create highly customized tables without limitations.
|
|
555
1385
|
|
|
556
1386
|
## 🔧 Migration Guide
|
|
557
1387
|
|
package/package.json
CHANGED
|
@@ -25,7 +25,7 @@ const DEFAULT_INITIAL_STATE = {
|
|
|
25
25
|
pageIndex: 0,
|
|
26
26
|
pageSize: 10,
|
|
27
27
|
},
|
|
28
|
-
|
|
28
|
+
selectionState: { ids: [], type: 'include' },
|
|
29
29
|
globalFilter: '',
|
|
30
30
|
expanded: {},
|
|
31
31
|
columnOrder: [],
|
|
@@ -33,7 +33,7 @@ const DEFAULT_INITIAL_STATE = {
|
|
|
33
33
|
left: [],
|
|
34
34
|
right: [],
|
|
35
35
|
},
|
|
36
|
-
|
|
36
|
+
columnFilter: {
|
|
37
37
|
filters: [],
|
|
38
38
|
logic: 'AND',
|
|
39
39
|
pendingFilters: [],
|
|
@@ -46,28 +46,23 @@ exports.DataTable = (0, react_1.forwardRef)(function DataTable({ initialState, c
|
|
|
46
46
|
const isServerPagination = paginationMode === 'server' || isServerMode;
|
|
47
47
|
const isServerFiltering = filterMode === 'server' || isServerMode;
|
|
48
48
|
const isServerSorting = sortingMode === 'server' || isServerMode;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const [
|
|
53
|
-
const [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const [
|
|
60
|
-
const [
|
|
61
|
-
const [columnOrder, setColumnOrder] = (0, react_1.useState)(DEFAULT_INITIAL_STATE.columnOrder);
|
|
62
|
-
const [columnPinning, setColumnPinning] = (0, react_1.useState)(DEFAULT_INITIAL_STATE.columnPinning);
|
|
49
|
+
const initialStateConfig = (0, react_1.useMemo)(() => {
|
|
50
|
+
return Object.assign(Object.assign({}, DEFAULT_INITIAL_STATE), initialState);
|
|
51
|
+
}, [initialState]);
|
|
52
|
+
const [sorting, setSorting] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.sorting) || DEFAULT_INITIAL_STATE.sorting);
|
|
53
|
+
const [pagination, setPagination] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.pagination) || DEFAULT_INITIAL_STATE.pagination);
|
|
54
|
+
const [globalFilter, setGlobalFilter] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.globalFilter) || DEFAULT_INITIAL_STATE.globalFilter);
|
|
55
|
+
const [selectionState, setSelectionState] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.selectionState) || DEFAULT_INITIAL_STATE.selectionState);
|
|
56
|
+
const [columnFilter, setColumnFilter] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.columnFilter) || DEFAULT_INITIAL_STATE.columnFilter);
|
|
57
|
+
const [expanded, setExpanded] = (0, react_1.useState)({});
|
|
58
|
+
const [tableSize, setTableSize] = (0, react_1.useState)();
|
|
59
|
+
const [columnOrder, setColumnOrder] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.columnOrder) || DEFAULT_INITIAL_STATE.columnOrder);
|
|
60
|
+
const [columnPinning, setColumnPinning] = (0, react_1.useState)((initialState === null || initialState === void 0 ? void 0 : initialState.columnPinning) || DEFAULT_INITIAL_STATE.columnPinning);
|
|
63
61
|
const [serverData, setServerData] = (0, react_1.useState)(null);
|
|
64
62
|
const [serverTotal, setServerTotal] = (0, react_1.useState)(0);
|
|
65
63
|
const [exportController, setExportController] = (0, react_1.useState)(null);
|
|
66
64
|
const tableContainerRef = (0, react_1.useRef)(null);
|
|
67
65
|
const internalApiRef = (0, react_1.useRef)(null);
|
|
68
|
-
const initialStateConfig = (0, react_1.useMemo)(() => {
|
|
69
|
-
return Object.assign(Object.assign({}, DEFAULT_INITIAL_STATE), initialState);
|
|
70
|
-
}, [initialState]);
|
|
71
66
|
const { debouncedFetch, isLoading: fetchLoading } = (0, debounced_fetch_utils_1.useDebouncedFetch)(onFetchData);
|
|
72
67
|
const tableData = (0, react_1.useMemo)(() => serverData ? serverData : data, [onFetchData, serverData, data]);
|
|
73
68
|
const tableTotalRow = (0, react_1.useMemo)(() => serverData ? serverTotal : totalRow, [onFetchData, serverTotal, totalRow]);
|
|
@@ -307,7 +302,7 @@ exports.DataTable = (0, react_1.forwardRef)(function DataTable({ initialState, c
|
|
|
307
302
|
}
|
|
308
303
|
}, 0);
|
|
309
304
|
}, [isServerFiltering, fetchData, tableStateChange]);
|
|
310
|
-
const table = (0, react_table_1.useReactTable)(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ _features: [column_filter_feature_1.ColumnFilterFeature, features_1.SelectionFeature], data: tableData, columns: enhancedColumns, initialState: Object.assign({},
|
|
305
|
+
const table = (0, react_table_1.useReactTable)(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ _features: [column_filter_feature_1.ColumnFilterFeature, features_1.SelectionFeature], data: tableData, columns: enhancedColumns, initialState: Object.assign({}, DEFAULT_INITIAL_STATE), state: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (enableSorting ? { sorting } : {})), (enablePagination ? { pagination } : {})), (enableGlobalFilter ? { globalFilter } : {})), (enableExpanding ? { expanded } : {})), (enableColumnDragging ? { columnOrder } : {})), (enableColumnPinning ? { columnPinning } : {})), (enableColumnFilter ? { columnFilter } : {})), (enableRowSelection ? { selectionState } : {})), selectMode: selectMode, enableAdvanceSelection: !!enableRowSelection, isRowSelectable: isRowSelectable }, (enableRowSelection ? { onSelectionStateChange: handleSelectionStateChange } : {})), { enableAdvanceColumnFilter: enableColumnFilter, onColumnFilterChange: onColumnFilterChangeHandler, onColumnFilterApply: onColumnFilterApplyHandler }), (enableSorting ? { onSortingChange: handleSortingChange } : {})), (enablePagination ? { onPaginationChange: handlePaginationChange } : {})), (enableRowSelection ? { onRowSelectionChange: handleSelectionStateChange } : {})), (enableGlobalFilter ? { onGlobalFilterChange: handleGlobalFilterChange } : {})), (enableExpanding ? { onExpandedChange: setExpanded } : {})), (enableColumnDragging ? { onColumnOrderChange: handleColumnOrderChange } : {})), (enableColumnPinning ? { onColumnPinningChange: handleColumnPinningChange } : {})), { getCoreRowModel: (0, react_table_1.getCoreRowModel)() }), (enableSorting ? { getSortedRowModel: (0, react_table_1.getSortedRowModel)() } : {})), (enableColumnFilter ? { getFilteredRowModel: (0, column_filter_feature_1.getCombinedFilteredRowModel)() } : {})), (enablePagination ? { getPaginationRowModel: (0, react_table_1.getPaginationRowModel)() } : {})), { enableSorting: enableSorting, manualSorting: isServerSorting, manualFiltering: isServerFiltering, enableColumnResizing: enableColumnResizing, columnResizeMode: columnResizeMode, enableColumnPinning: enableColumnPinning }), (enableExpanding ? { getRowCanExpand: getRowCanExpand } : {})), { manualPagination: isServerPagination, autoResetPageIndex: false, rowCount: tableTotalRow, getRowId: (row, index) => (0, utils_1.generateRowId)(row, index, idKey), debugAll: false }));
|
|
311
306
|
const rows = ((_a = table.getRowModel()) === null || _a === void 0 ? void 0 : _a.rows) || [];
|
|
312
307
|
const rowVirtualizer = (0, react_virtual_1.useVirtualizer)({
|
|
313
308
|
count: rows.length,
|
|
@@ -366,7 +361,7 @@ exports.DataTable = (0, react_1.forwardRef)(function DataTable({ initialState, c
|
|
|
366
361
|
enhancedColumns,
|
|
367
362
|
enablePagination,
|
|
368
363
|
enableColumnPinning,
|
|
369
|
-
initialStateConfig,
|
|
364
|
+
initialStateConfig: Object.assign(Object.assign({}, DEFAULT_INITIAL_STATE), initialState),
|
|
370
365
|
selectMode,
|
|
371
366
|
onSelectionChange: handleSelectionStateChange,
|
|
372
367
|
handleColumnFilterStateChange,
|
|
@@ -52,7 +52,6 @@ export interface DataTableProps<T> {
|
|
|
52
52
|
row: T;
|
|
53
53
|
id: string;
|
|
54
54
|
}) => boolean;
|
|
55
|
-
onRowSelectionChange?: (selectedRows: T[], selection?: SelectionState) => void;
|
|
56
55
|
onSelectionChange?: (selection: SelectionState) => void;
|
|
57
56
|
enableBulkActions?: boolean;
|
|
58
57
|
bulkActions?: (selectionState: SelectionState) => ReactNode;
|
|
@@ -94,23 +93,6 @@ export interface DataTableProps<T> {
|
|
|
94
93
|
onPaginationChange?: (pagination: PaginationState) => void;
|
|
95
94
|
onGlobalFilterChange?: (globalFilter: string) => void;
|
|
96
95
|
onColumnFilterChange?: (columnFilter: ColumnFilterState) => void;
|
|
97
|
-
selectionColumn?: {
|
|
98
|
-
width?: number;
|
|
99
|
-
pinLeft?: boolean;
|
|
100
|
-
customColumn?: DataTableColumn<T>;
|
|
101
|
-
id?: string;
|
|
102
|
-
};
|
|
103
|
-
actionColumn?: {
|
|
104
|
-
pinRight?: boolean;
|
|
105
|
-
customColumn?: DataTableColumn<T>;
|
|
106
|
-
id?: string;
|
|
107
|
-
};
|
|
108
|
-
expandingColumn?: {
|
|
109
|
-
width?: number;
|
|
110
|
-
pinLeft?: boolean;
|
|
111
|
-
customColumn?: DataTableColumn<T>;
|
|
112
|
-
id?: string;
|
|
113
|
-
};
|
|
114
96
|
slots?: Partial<DataTableSlots<T>>;
|
|
115
97
|
slotProps?: PartialSlotProps<T>;
|
|
116
98
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ColumnPinningState, SortingState, ColumnOrderState, TableState, Row } from '@tanstack/react-table';
|
|
1
|
+
import { ColumnPinningState, SortingState, ColumnOrderState, TableState, Row, Table } from '@tanstack/react-table';
|
|
2
2
|
import { ColumnFilterState } from './table.types';
|
|
3
3
|
import { SelectionState } from '../features';
|
|
4
4
|
export interface DataTableApi<T = any> {
|
|
@@ -10,6 +10,9 @@ export interface DataTableApi<T = any> {
|
|
|
10
10
|
hideAllColumns: () => void;
|
|
11
11
|
resetColumnVisibility: () => void;
|
|
12
12
|
};
|
|
13
|
+
table: {
|
|
14
|
+
getTable: () => Table<T>;
|
|
15
|
+
};
|
|
13
16
|
columnOrdering: {
|
|
14
17
|
setColumnOrder: (columnOrder: ColumnOrderState) => void;
|
|
15
18
|
moveColumn: (columnId: string, toIndex: number) => void;
|