@ackplus/react-tanstack-data-table 1.1.11 → 1.1.13
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 +143 -11
- package/dist/lib/components/droupdown/menu-dropdown.d.ts.map +1 -1
- package/dist/lib/components/droupdown/menu-dropdown.js +8 -1
- package/dist/lib/components/filters/filter-value-input.js +2 -2
- package/dist/lib/components/pagination/data-table-pagination.d.ts.map +1 -1
- package/dist/lib/components/pagination/data-table-pagination.js +10 -1
- package/dist/lib/components/toolbar/data-table-toolbar.d.ts.map +1 -1
- package/dist/lib/components/toolbar/data-table-toolbar.js +5 -2
- package/dist/lib/components/toolbar/table-export-control.d.ts.map +1 -1
- package/dist/lib/components/toolbar/table-export-control.js +46 -12
- package/dist/lib/components/toolbar/table-refresh-control.d.ts +15 -0
- package/dist/lib/components/toolbar/table-refresh-control.d.ts.map +1 -0
- package/dist/lib/components/toolbar/table-refresh-control.js +61 -0
- package/dist/lib/contexts/data-table-context.d.ts +7 -10
- package/dist/lib/contexts/data-table-context.d.ts.map +1 -1
- package/dist/lib/contexts/data-table-context.js +5 -1
- package/dist/lib/data-table.d.ts.map +1 -1
- package/dist/lib/data-table.js +1110 -946
- package/dist/lib/features/column-filter.feature.js +38 -21
- package/dist/lib/features/selection.feature.d.ts.map +1 -1
- package/dist/lib/features/selection.feature.js +11 -3
- package/dist/lib/types/column.types.d.ts +19 -0
- package/dist/lib/types/column.types.d.ts.map +1 -1
- package/dist/lib/types/data-table-api.d.ts +25 -18
- package/dist/lib/types/data-table-api.d.ts.map +1 -1
- package/dist/lib/types/data-table.types.d.ts +37 -10
- package/dist/lib/types/data-table.types.d.ts.map +1 -1
- package/dist/lib/types/export.types.d.ts +57 -13
- package/dist/lib/types/export.types.d.ts.map +1 -1
- package/dist/lib/types/slots.types.d.ts +12 -1
- package/dist/lib/types/slots.types.d.ts.map +1 -1
- package/dist/lib/types/table.types.d.ts +1 -3
- package/dist/lib/types/table.types.d.ts.map +1 -1
- package/dist/lib/utils/debounced-fetch.utils.d.ts +8 -4
- package/dist/lib/utils/debounced-fetch.utils.d.ts.map +1 -1
- package/dist/lib/utils/debounced-fetch.utils.js +63 -14
- package/dist/lib/utils/export-utils.d.ts +14 -4
- package/dist/lib/utils/export-utils.d.ts.map +1 -1
- package/dist/lib/utils/export-utils.js +362 -66
- package/dist/lib/utils/slot-helpers.d.ts +1 -1
- package/dist/lib/utils/slot-helpers.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/lib/components/droupdown/menu-dropdown.tsx +9 -3
- package/src/lib/components/filters/filter-value-input.tsx +2 -2
- package/src/lib/components/pagination/data-table-pagination.tsx +14 -2
- package/src/lib/components/toolbar/data-table-toolbar.tsx +15 -1
- package/src/lib/components/toolbar/table-export-control.tsx +65 -9
- package/src/lib/components/toolbar/table-refresh-control.tsx +58 -0
- package/src/lib/contexts/data-table-context.tsx +16 -2
- package/src/lib/data-table.tsx +1282 -932
- package/src/lib/features/column-filter.feature.ts +40 -19
- package/src/lib/features/selection.feature.ts +11 -5
- package/src/lib/types/column.types.ts +20 -1
- package/src/lib/types/data-table-api.ts +37 -15
- package/src/lib/types/data-table.types.ts +59 -3
- package/src/lib/types/export.types.ts +79 -10
- package/src/lib/types/slots.types.ts +11 -1
- package/src/lib/types/table.types.ts +1 -3
- package/src/lib/utils/debounced-fetch.utils.ts +90 -18
- package/src/lib/utils/export-utils.ts +496 -69
- package/src/lib/utils/slot-helpers.tsx +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// data-table-pagination.tsx
|
|
2
2
|
import { TablePagination, Box, TablePaginationProps, SxProps } from '@mui/material';
|
|
3
|
-
import { memo, ReactNode } from 'react';
|
|
3
|
+
import { memo, ReactNode, useEffect, useMemo } from 'react';
|
|
4
4
|
|
|
5
5
|
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
6
6
|
import { mergeSlotProps } from '../../utils/slot-helpers';
|
|
@@ -33,6 +33,18 @@ export const DataTablePagination = memo((props: DataTablePaginationProps) => {
|
|
|
33
33
|
} = props;
|
|
34
34
|
|
|
35
35
|
const { table, tableSize } = useDataTableContext();
|
|
36
|
+
const pageSize = pagination?.pageSize || 1;
|
|
37
|
+
const maxPageIndex = Math.max(0, Math.ceil(totalRow / pageSize) - 1);
|
|
38
|
+
const safePageIndex = useMemo(
|
|
39
|
+
() => Math.min(Math.max(pagination?.pageIndex ?? 0, 0), maxPageIndex),
|
|
40
|
+
[maxPageIndex, pagination?.pageIndex]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if ((pagination?.pageIndex ?? 0) !== safePageIndex) {
|
|
45
|
+
table?.setPageIndex(safePageIndex);
|
|
46
|
+
}
|
|
47
|
+
}, [pagination?.pageIndex, safePageIndex, table]);
|
|
36
48
|
|
|
37
49
|
// Extract slot-specific props with enhanced merging
|
|
38
50
|
// const paginationSlotProps = extractSlotProps(slotProps, 'pagination');
|
|
@@ -59,7 +71,7 @@ export const DataTablePagination = memo((props: DataTablePaginationProps) => {
|
|
|
59
71
|
size: tableSize === 'small' ? 'small' : 'medium',
|
|
60
72
|
count: totalRow,
|
|
61
73
|
rowsPerPage: pagination?.pageSize,
|
|
62
|
-
page:
|
|
74
|
+
page: safePageIndex,
|
|
63
75
|
onPageChange: (_, page: number) => {
|
|
64
76
|
// Use TanStack Table's native pagination methods
|
|
65
77
|
table.setPageIndex(page);
|
|
@@ -16,6 +16,7 @@ import { TableExportControl } from './table-export-control';
|
|
|
16
16
|
import { TableSearchControl } from './table-search-control';
|
|
17
17
|
import { TableSizeControl } from './table-size-control';
|
|
18
18
|
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
19
|
+
import { TableRefreshControl } from './table-refresh-control';
|
|
19
20
|
import { getSlotComponent, mergeSlotProps, extractSlotProps } from '../../utils/slot-helpers';
|
|
20
21
|
|
|
21
22
|
export interface DataTableToolbarProps extends ToolbarProps {
|
|
@@ -48,6 +49,7 @@ export function DataTableToolbar(props: DataTableToolbarProps = {}): ReactElemen
|
|
|
48
49
|
enableColumnVisibility = true,
|
|
49
50
|
enableExport = true,
|
|
50
51
|
enableReset = true,
|
|
52
|
+
enableRefresh = false,
|
|
51
53
|
enableColumnFilter = true,
|
|
52
54
|
enableTableSizeControl = true,
|
|
53
55
|
enableColumnPinning = true,
|
|
@@ -70,7 +72,8 @@ export function DataTableToolbar(props: DataTableToolbarProps = {}): ReactElemen
|
|
|
70
72
|
const columnVisibilityControlSlotProps = extractSlotProps(slotProps, 'columnVisibilityControl');
|
|
71
73
|
const resetButtonSlotProps = extractSlotProps(slotProps, 'resetButton');
|
|
72
74
|
const exportButtonSlotProps = extractSlotProps(slotProps, 'exportButton');
|
|
73
|
-
|
|
75
|
+
const refreshButtonSlotProps = extractSlotProps(slotProps, 'refreshButton');
|
|
76
|
+
|
|
74
77
|
const ToolbarSlot = getSlotComponent(slots, 'toolbar', Toolbar);
|
|
75
78
|
const TableSearchControlSlot = getSlotComponent(slots, 'searchInput', TableSearchControl);
|
|
76
79
|
const TableSizeControlSlot = getSlotComponent(slots, 'tableSizeControl', TableSizeControl);
|
|
@@ -79,6 +82,7 @@ export function DataTableToolbar(props: DataTableToolbarProps = {}): ReactElemen
|
|
|
79
82
|
const ColumnVisibilityControlSlot = getSlotComponent(slots, 'columnVisibilityControl', ColumnVisibilityControl);
|
|
80
83
|
const ColumnResetControlSlot = getSlotComponent(slots, 'resetButton', ColumnResetControl);
|
|
81
84
|
const TableExportControlSlot = getSlotComponent(slots, 'exportButton', TableExportControl);
|
|
85
|
+
const TableRefreshControlSlot = getSlotComponent(slots, 'refreshButton', TableRefreshControl);
|
|
82
86
|
|
|
83
87
|
// Merge all props for maximum flexibility
|
|
84
88
|
const mergedToolbarProps = mergeSlotProps(
|
|
@@ -210,6 +214,16 @@ export function DataTableToolbar(props: DataTableToolbarProps = {}): ReactElemen
|
|
|
210
214
|
)}
|
|
211
215
|
/>
|
|
212
216
|
) : null}
|
|
217
|
+
|
|
218
|
+
{enableRefresh ? (
|
|
219
|
+
<TableRefreshControlSlot
|
|
220
|
+
{...mergeSlotProps(
|
|
221
|
+
{},
|
|
222
|
+
refreshButtonSlotProps,
|
|
223
|
+
props.refreshButtonProps || {}
|
|
224
|
+
)}
|
|
225
|
+
/>
|
|
226
|
+
) : null}
|
|
213
227
|
</Stack>
|
|
214
228
|
|
|
215
229
|
{/* Right Section - Extra Filter and More Menu */}
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
Box,
|
|
11
11
|
IconButtonProps,
|
|
12
12
|
SxProps,
|
|
13
|
+
Divider,
|
|
14
|
+
LinearProgress,
|
|
13
15
|
} from '@mui/material';
|
|
14
16
|
|
|
15
17
|
import { MenuDropdown } from '../droupdown/menu-dropdown';
|
|
@@ -46,6 +48,9 @@ export function TableExportControl(props: TableExportControlProps = {}) {
|
|
|
46
48
|
slots,
|
|
47
49
|
slotProps,
|
|
48
50
|
isExporting,
|
|
51
|
+
exportPhase,
|
|
52
|
+
exportProgress,
|
|
53
|
+
onCancelExport,
|
|
49
54
|
// Export callbacks from context (DataTable props)
|
|
50
55
|
exportFilename: contextExportFilename,
|
|
51
56
|
} = useDataTableContext();
|
|
@@ -80,11 +85,18 @@ export function TableExportControl(props: TableExportControlProps = {}) {
|
|
|
80
85
|
}
|
|
81
86
|
};
|
|
82
87
|
|
|
88
|
+
const handleCancelExport = () => {
|
|
89
|
+
if (onCancelExport) {
|
|
90
|
+
onCancelExport();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
apiRef?.current?.export.cancelExport();
|
|
94
|
+
};
|
|
95
|
+
|
|
83
96
|
// Merge all props for maximum flexibility
|
|
84
97
|
const mergedIconButtonProps = mergeSlotProps(
|
|
85
98
|
{
|
|
86
99
|
size: 'small',
|
|
87
|
-
disabled: isExporting,
|
|
88
100
|
sx: { flexShrink: 0 },
|
|
89
101
|
},
|
|
90
102
|
exportIconSlotProps,
|
|
@@ -98,6 +110,25 @@ export function TableExportControl(props: TableExportControlProps = {}) {
|
|
|
98
110
|
menuItemProps || {}
|
|
99
111
|
);
|
|
100
112
|
|
|
113
|
+
const progressPercentage = typeof exportProgress?.percentage === 'number'
|
|
114
|
+
? Math.max(0, Math.min(100, exportProgress.percentage))
|
|
115
|
+
: undefined;
|
|
116
|
+
|
|
117
|
+
const getPhaseLabel = () => {
|
|
118
|
+
switch (exportPhase) {
|
|
119
|
+
case 'fetching':
|
|
120
|
+
return 'Fetching rows from server...';
|
|
121
|
+
case 'processing':
|
|
122
|
+
return 'Preparing export file...';
|
|
123
|
+
case 'downloading':
|
|
124
|
+
return 'Downloading file...';
|
|
125
|
+
case 'starting':
|
|
126
|
+
return 'Starting export...';
|
|
127
|
+
default:
|
|
128
|
+
return 'Export in progress...';
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
101
132
|
return (
|
|
102
133
|
<MenuDropdown
|
|
103
134
|
anchor={(
|
|
@@ -162,15 +193,40 @@ export function TableExportControl(props: TableExportControlProps = {}) {
|
|
|
162
193
|
</MenuItem>
|
|
163
194
|
|
|
164
195
|
{isExporting && (
|
|
165
|
-
|
|
166
|
-
<
|
|
167
|
-
variant="caption"
|
|
168
|
-
|
|
169
|
-
|
|
196
|
+
<>
|
|
197
|
+
<Box sx={{ px: 2, pb: 1 }}>
|
|
198
|
+
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 0.75 }}>
|
|
199
|
+
{getPhaseLabel()}
|
|
200
|
+
</Typography>
|
|
201
|
+
<LinearProgress
|
|
202
|
+
variant={progressPercentage !== undefined ? 'determinate' : 'indeterminate'}
|
|
203
|
+
value={progressPercentage !== undefined ? progressPercentage : 0}
|
|
204
|
+
/>
|
|
205
|
+
{(exportProgress?.processedRows !== undefined || exportProgress?.totalRows !== undefined) && (
|
|
206
|
+
<Typography variant="caption" color="text.secondary" sx={{ display: 'block', mt: 0.75 }}>
|
|
207
|
+
{`${exportProgress?.processedRows ?? 0}${exportProgress?.totalRows !== undefined ? ` / ${exportProgress.totalRows}` : ''}${progressPercentage !== undefined ? ` (${progressPercentage}%)` : ''}`}
|
|
208
|
+
</Typography>
|
|
209
|
+
)}
|
|
210
|
+
</Box>
|
|
211
|
+
<Divider sx={{ my: 1 }} />
|
|
212
|
+
<MenuItem
|
|
213
|
+
onClick={() => {
|
|
214
|
+
handleCancelExport();
|
|
215
|
+
handleClose();
|
|
216
|
+
}}
|
|
217
|
+
{...mergedMenuItemProps}
|
|
170
218
|
>
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
219
|
+
<ListItemText
|
|
220
|
+
primary="Cancel Export"
|
|
221
|
+
secondary="Stop current export job"
|
|
222
|
+
slotProps={{
|
|
223
|
+
primary: {
|
|
224
|
+
color: 'error.main',
|
|
225
|
+
},
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
</MenuItem>
|
|
229
|
+
</>
|
|
174
230
|
)}
|
|
175
231
|
</Box>
|
|
176
232
|
)}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { ReactElement, useCallback } from 'react';
|
|
2
|
+
import { Refresh } from '@mui/icons-material';
|
|
3
|
+
import { IconButton, Tooltip, IconButtonProps, CircularProgress } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { useDataTableContext } from '../../contexts/data-table-context';
|
|
6
|
+
import { extractSlotProps, getSlotComponent, mergeSlotProps } from '../../utils/slot-helpers';
|
|
7
|
+
|
|
8
|
+
export interface TableRefreshControlProps {
|
|
9
|
+
iconButtonProps?: IconButtonProps;
|
|
10
|
+
tooltipProps?: any;
|
|
11
|
+
|
|
12
|
+
/** optional override */
|
|
13
|
+
onRefresh?: () => void | Promise<void>;
|
|
14
|
+
|
|
15
|
+
/** disable + show spinner if true */
|
|
16
|
+
loading?: boolean;
|
|
17
|
+
|
|
18
|
+
/** use spinner instead of icon while loading */
|
|
19
|
+
showSpinnerWhileLoading?: boolean;
|
|
20
|
+
|
|
21
|
+
[key: string]: any;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TableRefreshControl(props: TableRefreshControlProps = {}): ReactElement {
|
|
25
|
+
const { apiRef, slots, slotProps } = useDataTableContext();
|
|
26
|
+
|
|
27
|
+
const refreshIconSlotProps = extractSlotProps(slotProps, 'refreshIcon');
|
|
28
|
+
const RefreshIconSlot = getSlotComponent(slots, 'refreshIcon', Refresh);
|
|
29
|
+
|
|
30
|
+
const handleRefresh = useCallback(() => {
|
|
31
|
+
if (props.onRefresh) return props.onRefresh();
|
|
32
|
+
// Default: use internal api
|
|
33
|
+
apiRef?.current?.data?.reload?.();
|
|
34
|
+
}, [props, apiRef]);
|
|
35
|
+
|
|
36
|
+
const mergedIconButtonProps = mergeSlotProps(
|
|
37
|
+
{
|
|
38
|
+
size: 'small',
|
|
39
|
+
onClick: handleRefresh,
|
|
40
|
+
disabled: !!props.loading,
|
|
41
|
+
sx: { flexShrink: 0 },
|
|
42
|
+
},
|
|
43
|
+
refreshIconSlotProps,
|
|
44
|
+
props.iconButtonProps || {}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Tooltip title="Refresh data" {...props.tooltipProps}>
|
|
49
|
+
<IconButton {...mergedIconButtonProps}>
|
|
50
|
+
{props.loading && props.showSpinnerWhileLoading ? (
|
|
51
|
+
<CircularProgress size={16} />
|
|
52
|
+
) : (
|
|
53
|
+
<RefreshIconSlot {...refreshIconSlotProps} />
|
|
54
|
+
)}
|
|
55
|
+
</IconButton>
|
|
56
|
+
</Tooltip>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -2,7 +2,9 @@ import { Table } from '@tanstack/react-table';
|
|
|
2
2
|
import React, { createContext, useContext, ReactNode, useMemo, RefObject, ReactElement } from 'react';
|
|
3
3
|
|
|
4
4
|
import { ColumnFilterState, TableSize } from '../types';
|
|
5
|
+
import { SelectionState } from '../features';
|
|
5
6
|
import { DataTableApi } from '../types/data-table-api';
|
|
7
|
+
import { ExportPhase, ExportProgressPayload, ServerExportResult } from '../types/export.types';
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
/**
|
|
@@ -22,14 +24,20 @@ interface DataTableContextValue<T = any> {
|
|
|
22
24
|
// Export state - managed by the DataTable component
|
|
23
25
|
isExporting?: boolean;
|
|
24
26
|
exportController?: AbortController | null;
|
|
27
|
+
exportPhase?: ExportPhase | null;
|
|
28
|
+
exportProgress?: ExportProgressPayload;
|
|
25
29
|
onCancelExport?: () => void;
|
|
26
30
|
|
|
27
31
|
// Export callbacks - passed from DataTable props
|
|
28
32
|
exportFilename?: string;
|
|
29
|
-
onExportProgress?: (progress:
|
|
33
|
+
onExportProgress?: (progress: ExportProgressPayload) => void;
|
|
30
34
|
onExportComplete?: (result: { success: boolean; filename: string; totalRows: number }) => void;
|
|
31
35
|
onExportError?: (error: { message: string; code: string }) => void;
|
|
32
|
-
onServerExport?: (
|
|
36
|
+
onServerExport?: (
|
|
37
|
+
filters?: Partial<any>,
|
|
38
|
+
selection?: SelectionState,
|
|
39
|
+
signal?: AbortSignal
|
|
40
|
+
) => Promise<ServerExportResult<any>>;
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
const DataTableContext = createContext<DataTableContextValue | null>(null);
|
|
@@ -51,6 +59,8 @@ export function DataTableProvider<T = any>({
|
|
|
51
59
|
slotProps = {},
|
|
52
60
|
isExporting,
|
|
53
61
|
exportController,
|
|
62
|
+
exportPhase,
|
|
63
|
+
exportProgress,
|
|
54
64
|
onCancelExport,
|
|
55
65
|
exportFilename,
|
|
56
66
|
onExportProgress,
|
|
@@ -70,6 +80,8 @@ export function DataTableProvider<T = any>({
|
|
|
70
80
|
slotProps,
|
|
71
81
|
isExporting,
|
|
72
82
|
exportController,
|
|
83
|
+
exportPhase,
|
|
84
|
+
exportProgress,
|
|
73
85
|
onCancelExport,
|
|
74
86
|
exportFilename,
|
|
75
87
|
onExportProgress,
|
|
@@ -88,6 +100,8 @@ export function DataTableProvider<T = any>({
|
|
|
88
100
|
slotProps,
|
|
89
101
|
isExporting,
|
|
90
102
|
exportController,
|
|
103
|
+
exportPhase,
|
|
104
|
+
exportProgress,
|
|
91
105
|
onCancelExport,
|
|
92
106
|
exportFilename,
|
|
93
107
|
onExportProgress,
|