@ackplus/react-tanstack-data-table 1.1.19 → 1.1.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.
Files changed (63) hide show
  1. package/dist/lib/hooks/use-data-table-engine.d.ts.map +1 -1
  2. package/dist/lib/hooks/use-data-table-engine.js +54 -15
  3. package/dist/lib/types/data-table.types.d.ts +2 -1
  4. package/dist/lib/types/data-table.types.d.ts.map +1 -1
  5. package/package.json +3 -4
  6. package/src/index.ts +0 -75
  7. package/src/lib/components/data-table-view.tsx +0 -386
  8. package/src/lib/components/droupdown/menu-dropdown.tsx +0 -103
  9. package/src/lib/components/filters/filter-value-input.tsx +0 -225
  10. package/src/lib/components/filters/index.ts +0 -126
  11. package/src/lib/components/headers/draggable-header.tsx +0 -326
  12. package/src/lib/components/headers/index.ts +0 -6
  13. package/src/lib/components/headers/table-header.tsx +0 -175
  14. package/src/lib/components/index.ts +0 -21
  15. package/src/lib/components/pagination/data-table-pagination.tsx +0 -111
  16. package/src/lib/components/pagination/index.ts +0 -5
  17. package/src/lib/components/rows/data-table-row.tsx +0 -218
  18. package/src/lib/components/rows/empty-data-row.tsx +0 -69
  19. package/src/lib/components/rows/index.ts +0 -7
  20. package/src/lib/components/rows/loading-rows.tsx +0 -164
  21. package/src/lib/components/toolbar/bulk-actions-toolbar.tsx +0 -125
  22. package/src/lib/components/toolbar/column-filter-control.tsx +0 -432
  23. package/src/lib/components/toolbar/column-pinning-control.tsx +0 -275
  24. package/src/lib/components/toolbar/column-reset-control.tsx +0 -74
  25. package/src/lib/components/toolbar/column-visibility-control.tsx +0 -105
  26. package/src/lib/components/toolbar/data-table-toolbar.tsx +0 -257
  27. package/src/lib/components/toolbar/index.ts +0 -17
  28. package/src/lib/components/toolbar/table-export-control.tsx +0 -233
  29. package/src/lib/components/toolbar/table-refresh-control.tsx +0 -62
  30. package/src/lib/components/toolbar/table-search-control.tsx +0 -155
  31. package/src/lib/components/toolbar/table-size-control.tsx +0 -102
  32. package/src/lib/contexts/data-table-context.tsx +0 -126
  33. package/src/lib/data-table.tsx +0 -29
  34. package/src/lib/features/README.md +0 -161
  35. package/src/lib/features/column-filter.feature.ts +0 -493
  36. package/src/lib/features/index.ts +0 -23
  37. package/src/lib/features/selection.feature.ts +0 -322
  38. package/src/lib/hooks/index.ts +0 -2
  39. package/src/lib/hooks/use-data-table-engine.ts +0 -1516
  40. package/src/lib/icons/add-icon.tsx +0 -23
  41. package/src/lib/icons/csv-icon.tsx +0 -15
  42. package/src/lib/icons/delete-icon.tsx +0 -30
  43. package/src/lib/icons/excel-icon.tsx +0 -15
  44. package/src/lib/icons/index.ts +0 -7
  45. package/src/lib/icons/unpin-icon.tsx +0 -18
  46. package/src/lib/icons/view-comfortable-icon.tsx +0 -45
  47. package/src/lib/icons/view-compact-icon.tsx +0 -55
  48. package/src/lib/types/column.types.ts +0 -63
  49. package/src/lib/types/data-table-api.ts +0 -191
  50. package/src/lib/types/data-table.types.ts +0 -192
  51. package/src/lib/types/export.types.ts +0 -223
  52. package/src/lib/types/index.ts +0 -24
  53. package/src/lib/types/slots.types.ts +0 -342
  54. package/src/lib/types/table.types.ts +0 -88
  55. package/src/lib/utils/column-helpers.ts +0 -72
  56. package/src/lib/utils/debounced-fetch.utils.ts +0 -131
  57. package/src/lib/utils/export-utils.ts +0 -712
  58. package/src/lib/utils/index.ts +0 -27
  59. package/src/lib/utils/logger.ts +0 -203
  60. package/src/lib/utils/slot-helpers.tsx +0 -194
  61. package/src/lib/utils/special-columns.utils.ts +0 -101
  62. package/src/lib/utils/styling-helpers.ts +0 -126
  63. package/src/lib/utils/table-helpers.ts +0 -106
@@ -1,1516 +0,0 @@
1
- import {
2
- getCoreRowModel,
3
- getPaginationRowModel,
4
- getSortedRowModel,
5
- PaginationState,
6
- useReactTable,
7
- type ColumnOrderState,
8
- type ColumnPinningState,
9
- type SortingState,
10
- type Updater,
11
- } from "@tanstack/react-table";
12
- import { useVirtualizer } from "@tanstack/react-virtual";
13
- import { useTheme } from "@mui/material/styles";
14
- import { useMemo, useReducer, useState, useRef, useCallback, useEffect, RefObject, CSSProperties } from "react";
15
-
16
- // your types
17
- import type {
18
- ColumnFilterState,
19
- DataFetchMeta,
20
- DataRefreshOptions,
21
- DataTableProps,
22
- ExportPhase,
23
- ExportProgressPayload,
24
- ExportStateChange,
25
- TableFiltersForFetch,
26
- TableState,
27
- } from "../types";
28
- import type { DataTableApi, DataTableExportApiOptions } from "../types/data-table-api";
29
-
30
- // your features / utils
31
- import { ColumnFilterFeature, getCombinedFilteredRowModel } from "../features/column-filter.feature";
32
- import { SelectionFeature, SelectionState } from "../features";
33
- import { createExpandingColumn, createSelectionColumn } from "../utils/special-columns.utils";
34
- import {
35
- exportClientData,
36
- exportServerData,
37
- generateRowId,
38
- withIdsDeep,
39
- type DataTableSize,
40
- } from "../utils";
41
- import { useDebouncedFetch } from "../utils/debounced-fetch.utils";
42
-
43
- const DEFAULT_INITIAL_STATE = {
44
- sorting: [] as SortingState,
45
- pagination: { pageIndex: 0, pageSize: 10 },
46
- selectionState: { ids: [], type: "include" } as SelectionState,
47
- globalFilter: "",
48
- expanded: {} as Record<string, boolean>,
49
- columnOrder: [] as ColumnOrderState,
50
- columnPinning: { left: [], right: [] } as ColumnPinningState,
51
- columnVisibility: {} as Record<string, boolean>,
52
- columnSizing: {} as Record<string, number>,
53
- columnFilter: {
54
- filters: [],
55
- logic: "AND",
56
- pendingFilters: [],
57
- pendingLogic: "AND",
58
- } as ColumnFilterState,
59
- };
60
-
61
- type EngineUIState = {
62
- sorting: SortingState;
63
- pagination: { pageIndex: number; pageSize: number };
64
- globalFilter: string;
65
- selectionState: SelectionState;
66
- columnFilter: ColumnFilterState;
67
- expanded: Record<string, boolean>;
68
- tableSize: DataTableSize;
69
- columnOrder: ColumnOrderState;
70
- columnPinning: ColumnPinningState;
71
- columnVisibility: Record<string, boolean>;
72
- columnSizing: Record<string, number>;
73
- };
74
- type EngineAction =
75
- | { type: "SET_SORTING_RESET_PAGE"; payload: SortingState }
76
- | { type: "SET_PAGINATION"; payload: { pageIndex: number; pageSize: number } }
77
- | { type: "SET_GLOBAL_FILTER_RESET_PAGE"; payload: string }
78
- | { type: "SET_SELECTION"; payload: SelectionState }
79
- | { type: "SET_COLUMN_FILTER"; payload: ColumnFilterState }
80
- | { type: "SET_COLUMN_FILTER_RESET_PAGE"; payload: ColumnFilterState }
81
- | { type: "SET_EXPANDED"; payload: Record<string, boolean> }
82
- | { type: "SET_TABLE_SIZE"; payload: DataTableSize }
83
- | { type: "SET_COLUMN_ORDER"; payload: ColumnOrderState }
84
- | { type: "SET_COLUMN_PINNING"; payload: ColumnPinningState }
85
- | { type: "SET_COLUMN_VISIBILITY"; payload: Record<string, boolean> }
86
- | { type: "SET_COLUMN_SIZING"; payload: Record<string, number> }
87
- | { type: "RESET_ALL"; payload: Partial<EngineUIState> } // payload = computed reset state
88
- | { type: "RESTORE_LAYOUT"; payload: Partial<EngineUIState> };
89
-
90
-
91
- function uiReducer(state: EngineUIState, action: EngineAction): EngineUIState {
92
- switch (action.type) {
93
- case "SET_SORTING_RESET_PAGE":
94
- return { ...state, sorting: action.payload, pagination: { pageIndex: 0, pageSize: state.pagination.pageSize } };
95
- case "SET_PAGINATION":
96
- return { ...state, pagination: action.payload };
97
- case "SET_GLOBAL_FILTER_RESET_PAGE":
98
- return { ...state, globalFilter: action.payload, pagination: { pageIndex: 0, pageSize: state.pagination.pageSize } };
99
- case "SET_SELECTION":
100
- return { ...state, selectionState: action.payload };
101
- case "SET_COLUMN_FILTER":
102
- return { ...state, columnFilter: action.payload };
103
- case "SET_COLUMN_FILTER_RESET_PAGE":
104
- return { ...state, columnFilter: action.payload, pagination: { pageIndex: 0, pageSize: state.pagination.pageSize } };
105
- case "SET_EXPANDED":
106
- return { ...state, expanded: action.payload };
107
- case "SET_TABLE_SIZE":
108
- return { ...state, tableSize: action.payload };
109
- case "SET_COLUMN_ORDER":
110
- return { ...state, columnOrder: action.payload };
111
- case "SET_COLUMN_PINNING":
112
- return { ...state, columnPinning: action.payload };
113
- case "SET_COLUMN_VISIBILITY":
114
- return { ...state, columnVisibility: action.payload };
115
- case "SET_COLUMN_SIZING":
116
- return { ...state, columnSizing: action.payload };
117
- case "RESTORE_LAYOUT":
118
- return { ...state, ...action.payload };
119
- case "RESET_ALL":
120
- return { ...state, ...action.payload };
121
- default:
122
- return state;
123
- }
124
- }
125
-
126
- function useLatestRef<T>(value: T) {
127
- const ref = useRef(value);
128
- useEffect(() => {
129
- ref.current = value;
130
- }, [value]);
131
- return ref;
132
- }
133
-
134
- function useEvent<T extends (...args: any[]) => any>(fn: T): T {
135
- const fnRef = useLatestRef(fn);
136
- return useCallback(((...args: any[]) => fnRef.current(...args)) as T, [fnRef]) as T;
137
- }
138
-
139
-
140
- export interface EngineResult<T = any> {
141
- table: ReturnType<typeof useReactTable<T>>;
142
- refs: {
143
- tableContainerRef: RefObject<HTMLDivElement>;
144
- apiRef: RefObject<DataTableApi<T> | null>;
145
- exportControllerRef: RefObject<AbortController | null>;
146
- };
147
- derived: {
148
- isServerMode: boolean;
149
- isServerPagination: boolean;
150
- isServerFiltering: boolean;
151
- isServerSorting: boolean;
152
- tableData: T[];
153
- tableTotalRow: number;
154
- tableLoading: boolean;
155
- rows: ReturnType<ReturnType<typeof useReactTable<T>>["getRowModel"]>["rows"];
156
- visibleLeafColumns: ReturnType<typeof useReactTable<T>>["getVisibleLeafColumns"];
157
- useFixedLayout: boolean;
158
- tableStyle: CSSProperties;
159
- isExporting: boolean;
160
- exportPhase: ExportPhase | null;
161
- exportProgress: ExportProgressPayload;
162
- isSomeRowsSelected: boolean;
163
- selectedRowCount: number;
164
- };
165
- state: EngineUIState;
166
- actions: {
167
- fetchData: (overrides?: Partial<TableState>, options?: { delay?: number; meta?: DataFetchMeta }) => Promise<any>;
168
- handleSortingChange: (updaterOrValue: any) => void;
169
- handlePaginationChange: (updater: any) => void;
170
- handleGlobalFilterChange: (updaterOrValue: any) => void;
171
- handleColumnFilterChangeHandler: (updater: any, isApply?: boolean) => void;
172
- handleColumnOrderChange: (updatedColumnOrder: Updater<ColumnOrderState>) => void;
173
- handleColumnPinningChange: (updater: Updater<ColumnPinningState>) => void;
174
- handleColumnVisibilityChange: (updater: any) => void;
175
- handleColumnSizingChange: (updater: any) => void;
176
- handleColumnReorder: (draggedColumnId: string, targetColumnId: string) => void;
177
- resetAllAndReload: () => void;
178
- triggerRefresh: (options?: boolean | DataRefreshOptions, fallbackReason?: string) => Promise<void>;
179
- setTableSize: (size: DataTableSize) => void;
180
- handleCancelExport: () => void;
181
- renderRowModel: { rowVirtualizer: ReturnType<typeof useVirtualizer> };
182
- };
183
- api: DataTableApi<T>;
184
- providerProps: {
185
- table: ReturnType<typeof useReactTable<T>>;
186
- apiRef: RefObject<DataTableApi<T> | null>;
187
- dataMode: "client" | "server";
188
- tableSize: DataTableSize;
189
- onTableSizeChange: (size: DataTableSize) => void;
190
- columnFilter: ColumnFilterState;
191
- onChangeColumnFilter: (filter: ColumnFilterState) => void;
192
- slots: Record<string, any>;
193
- slotProps: Record<string, any>;
194
- isExporting: boolean;
195
- exportController: AbortController | null;
196
- exportPhase: ExportPhase | null;
197
- exportProgress: ExportProgressPayload;
198
- onCancelExport: () => void;
199
- exportFilename: string;
200
- onExportProgress?: (progress: ExportProgressPayload) => void;
201
- onExportComplete?: (result: { success: boolean; filename: string; totalRows: number }) => void;
202
- onExportError?: (error: { message: string; code: string }) => void;
203
- onServerExport?: (filters?: Partial<any>, selection?: SelectionState, signal?: AbortSignal) => Promise<any>;
204
- };
205
- }
206
-
207
- export function useDataTableEngine<T extends Record<string, any>>(
208
- props: DataTableProps<T>
209
- ): EngineResult<T> {
210
- const {
211
- initialState,
212
- columns,
213
- data = [],
214
- totalRow = 0,
215
- idKey = "id" as keyof T,
216
-
217
- dataMode = "client",
218
- initialLoadData = true,
219
- onFetchData,
220
- onFetchStateChange,
221
- onDataStateChange,
222
-
223
- enableRowSelection = false,
224
- enableMultiRowSelection = true,
225
- selectMode = "page",
226
- isRowSelectable,
227
- onSelectionChange,
228
- enableBulkActions = false,
229
-
230
- enableColumnResizing = false,
231
- columnResizeMode = "onChange",
232
- onColumnSizingChange,
233
-
234
- enableColumnDragging = false,
235
- onColumnDragEnd,
236
-
237
- enableColumnPinning = false,
238
- onColumnPinningChange,
239
-
240
- onColumnVisibilityChange,
241
- enableColumnVisibility = true,
242
-
243
- enableExpanding = false,
244
- getRowCanExpand,
245
-
246
- enablePagination = false,
247
- paginationMode = "client",
248
-
249
- enableGlobalFilter = true,
250
-
251
- enableColumnFilter = false,
252
- filterMode = "client",
253
-
254
- enableSorting = true,
255
- sortingMode = "client",
256
- onSortingChange,
257
-
258
- exportFilename = "export",
259
- exportConcurrency = "cancelAndRestart",
260
- exportChunkSize = 1000,
261
- exportStrictTotalCheck = false,
262
- exportSanitizeCSV = true,
263
- onExportProgress,
264
- onExportComplete,
265
- onExportError,
266
- onServerExport,
267
- onExportCancel,
268
- onExportStateChange,
269
-
270
- fitToScreen = true,
271
- tableSize: initialTableSize = "medium",
272
- enableVirtualization = false,
273
- estimateRowHeight = 52,
274
-
275
- loading = false,
276
-
277
- onColumnFiltersChange,
278
- onPaginationChange,
279
- onGlobalFilterChange,
280
-
281
- slots = {},
282
- slotProps = {},
283
- } = props;
284
-
285
- const theme = useTheme();
286
-
287
- const isServerMode = dataMode === "server";
288
- const isServerPagination = paginationMode === "server" || isServerMode;
289
- const isServerFiltering = filterMode === "server" || isServerMode;
290
- const isServerSorting = sortingMode === "server" || isServerMode;
291
-
292
- // --- initial config (memo)
293
- const initialStateConfig = useMemo(() => {
294
- const config = { ...DEFAULT_INITIAL_STATE, ...initialState };
295
- return config;
296
- }, [initialState]);
297
-
298
- const initialUIState: EngineUIState = useMemo(
299
- () => ({
300
- sorting: initialStateConfig.sorting ?? DEFAULT_INITIAL_STATE.sorting,
301
- pagination: initialStateConfig.pagination ?? DEFAULT_INITIAL_STATE.pagination,
302
- globalFilter: initialStateConfig.globalFilter ?? DEFAULT_INITIAL_STATE.globalFilter,
303
- selectionState: initialStateConfig.selectionState ?? DEFAULT_INITIAL_STATE.selectionState,
304
- columnFilter: initialStateConfig.columnFilter ?? DEFAULT_INITIAL_STATE.columnFilter,
305
- expanded: initialStateConfig.expanded ?? {},
306
- tableSize: (initialTableSize || "medium") as DataTableSize,
307
- columnOrder: initialStateConfig.columnOrder ?? DEFAULT_INITIAL_STATE.columnOrder,
308
- columnPinning: initialStateConfig.columnPinning ?? DEFAULT_INITIAL_STATE.columnPinning,
309
- columnVisibility: initialStateConfig.columnVisibility ?? DEFAULT_INITIAL_STATE.columnVisibility,
310
- columnSizing: initialStateConfig.columnSizing ?? DEFAULT_INITIAL_STATE.columnSizing,
311
- }),
312
- [initialStateConfig, initialTableSize]
313
- );
314
-
315
- // --- UI state (reducer)
316
- const [ui, dispatch] = useReducer(uiReducer, initialUIState);
317
-
318
- // --- server data state (UI-affecting)
319
- const [serverData, setServerData] = useState<T[] | null>(null);
320
- const [serverTotal, setServerTotal] = useState<number>(0);
321
-
322
- // --- export UI state
323
- const [exportPhase, setExportPhase] = useState<ExportPhase | null>(null);
324
- const [exportProgress, setExportProgress] = useState<ExportProgressPayload>({});
325
- const [exportController, setExportController] = useState<AbortController | null>(null);
326
- const [queuedExportCount, setQueuedExportCount] = useState(0);
327
-
328
- // --- refs (no-render control)
329
- const tableContainerRef = useRef<HTMLDivElement>(null);
330
- const apiRef = useRef<DataTableApi<T> | null>(null);
331
- const exportControllerRef = useRef<AbortController | null>(null);
332
- const exportQueueRef = useRef<Promise<void>>(Promise.resolve());
333
- const lastSentRef = useRef<string>("");
334
-
335
- // --- latest refs (prevent stale closures in stable API)
336
- const uiRef = useLatestRef(ui);
337
- const dataRef = useLatestRef(data);;
338
- const serverDataRef = useLatestRef(serverData);
339
- const nextFetchDelayRef = useRef<number>(0);
340
-
341
- // callbacks refs (super important)
342
- const onFetchDataRef = useLatestRef(onFetchData);
343
- const onFetchStateChangeRef = useLatestRef(onFetchStateChange);
344
- const onDataStateChangeRef = useLatestRef(onDataStateChange);
345
-
346
- const onSortingChangeRef = useLatestRef(onSortingChange);
347
- const onPaginationChangeRef = useLatestRef(onPaginationChange);
348
- const onGlobalFilterChangeRef = useLatestRef(onGlobalFilterChange);
349
- const onColumnFiltersChangeRef = useLatestRef(onColumnFiltersChange);
350
-
351
- const onColumnDragEndRef = useLatestRef(onColumnDragEnd);
352
- const onColumnPinningChangeRef = useLatestRef(onColumnPinningChange);
353
- const onColumnVisibilityChangeRef = useLatestRef(onColumnVisibilityChange);
354
- const onColumnSizingChangeRef = useLatestRef(onColumnSizingChange);
355
- const onSelectionChangeRef = useLatestRef(onSelectionChange);
356
-
357
- const onExportProgressRef = useLatestRef(onExportProgress);
358
- const onExportCompleteRef = useLatestRef(onExportComplete);
359
- const onExportErrorRef = useLatestRef(onExportError);
360
- const onExportCancelRef = useLatestRef(onExportCancel);
361
- const onExportStateChangeRef = useLatestRef(onExportStateChange);
362
- const onServerExportRef = useLatestRef(onServerExport);
363
-
364
-
365
- // --- debounced fetch helper (can stay as-is)
366
- const fetchHandler = useEvent((filters: any, opts: any) => onFetchDataRef.current?.(filters, opts));
367
- const { debouncedFetch, isLoading: fetchLoading } = useDebouncedFetch(fetchHandler);
368
-
369
- const tableData = useMemo(() => {
370
- return serverData !== null ? serverData : data;
371
- }, [serverData, data]);
372
-
373
- const tableTotalRow = useMemo(() => {
374
- return serverData !== null ? serverTotal : totalRow || data.length;
375
- }, [serverData, serverTotal, totalRow, data]);
376
-
377
- const tableLoading = useMemo(() => {
378
- return onFetchData ? loading || fetchLoading : loading;
379
- }, [onFetchData, loading, fetchLoading]);
380
-
381
-
382
- // --- columns enhancement
383
- const enhancedColumns = useMemo(() => {
384
- let cols = [...columns];
385
- if (enableExpanding) {
386
- cols = [
387
- createExpandingColumn<T>({
388
- ...(slotProps?.expandColumn && typeof slotProps.expandColumn === "object"
389
- ? slotProps.expandColumn
390
- : {}),
391
- }),
392
- ...cols,
393
- ];
394
- }
395
- if (enableRowSelection) {
396
- cols = [
397
- createSelectionColumn<T>({
398
- ...(slotProps?.selectionColumn && typeof slotProps.selectionColumn === "object"
399
- ? slotProps.selectionColumn
400
- : {}),
401
- multiSelect: enableMultiRowSelection,
402
- }),
403
- ...cols,
404
- ];
405
- }
406
- return withIdsDeep(cols);
407
- }, [
408
- columns,
409
- enableExpanding,
410
- enableRowSelection,
411
- enableMultiRowSelection,
412
- slotProps?.expandColumn,
413
- slotProps?.selectionColumn,
414
- ]);
415
-
416
- // --- fetchData: useEvent so it's stable but reads latest state refs
417
- const fetchData = useEvent(async (overrides: Partial<TableState> = {}, options?: { delay?: number; meta?: DataFetchMeta }) => {
418
- const s = uiRef.current;
419
-
420
- const filters: Partial<TableFiltersForFetch> = {
421
- globalFilter: s.globalFilter,
422
- pagination: s.pagination,
423
- columnFilter: s.columnFilter,
424
- sorting: s.sorting,
425
- ...overrides,
426
- };
427
-
428
- onFetchStateChangeRef.current?.(filters, options?.meta);
429
-
430
- const handler = onFetchDataRef.current;
431
- if (!handler) return;
432
-
433
- const delay = options?.delay ?? 0;
434
- const result = await debouncedFetch(filters, { debounceDelay: delay, meta: options?.meta });
435
-
436
- if (result && Array.isArray(result.data) && result.total !== undefined) {
437
- setServerData(result.data);
438
- setServerTotal(result.total);
439
- }
440
-
441
- return result;
442
- });
443
-
444
- // --- derived selection counts
445
- const isSomeRowsSelected = useMemo(() => {
446
- if (!enableBulkActions || !enableRowSelection) return false;
447
- if (ui.selectionState.type === "exclude") return ui.selectionState.ids.length < tableTotalRow;
448
- return ui.selectionState.ids.length > 0;
449
- }, [enableBulkActions, enableRowSelection, ui.selectionState, tableTotalRow]);
450
-
451
- const selectedRowCount = useMemo(() => {
452
- if (!enableBulkActions || !enableRowSelection) return 0;
453
- if (ui.selectionState.type === "exclude") return tableTotalRow - ui.selectionState.ids.length;
454
- return ui.selectionState.ids.length;
455
- }, [enableBulkActions, enableRowSelection, ui.selectionState, tableTotalRow]);
456
-
457
-
458
-
459
- // --- TanStack Table
460
- const table = useReactTable({
461
- _features: [ColumnFilterFeature, SelectionFeature],
462
- data: tableData,
463
- columns: enhancedColumns,
464
- initialState: initialStateConfig,
465
- state: {
466
- ...(enableSorting ? { sorting: ui.sorting } : {}),
467
- ...(enablePagination ? { pagination: ui.pagination } : {}),
468
- ...(enableGlobalFilter ? { globalFilter: ui.globalFilter } : {}),
469
- ...(enableExpanding ? { expanded: ui.expanded } : {}),
470
- ...(enableColumnDragging ? { columnOrder: ui.columnOrder } : {}),
471
- ...(enableColumnPinning ? { columnPinning: ui.columnPinning } : {}),
472
- ...(enableColumnVisibility ? { columnVisibility: ui.columnVisibility } : {}),
473
- ...(enableColumnResizing ? { columnSizing: ui.columnSizing } : {}),
474
- ...(enableColumnFilter ? { columnFilter: ui.columnFilter } : {}),
475
- ...(enableRowSelection ? { selectionState: ui.selectionState } : {}),
476
- },
477
-
478
- selectMode,
479
- enableAdvanceSelection: !!enableRowSelection,
480
- isRowSelectable: isRowSelectable as any,
481
-
482
- ...(enableRowSelection
483
- ? {
484
- onSelectionStateChange: (updaterOrValue: any) => {
485
- dispatch({
486
- type: "SET_SELECTION",
487
- payload:
488
- typeof updaterOrValue === "function"
489
- ? updaterOrValue(uiRef.current.selectionState)
490
- : updaterOrValue,
491
- });
492
- },
493
- }
494
- : {}),
495
-
496
- enableAdvanceColumnFilter: enableColumnFilter,
497
- onColumnFilterChange: (updater: any) => {
498
- const next = typeof updater === "function" ? updater(uiRef.current.columnFilter) : updater;
499
- dispatch({ type: "SET_COLUMN_FILTER", payload: next });
500
- },
501
- onColumnFilterApply: (state: ColumnFilterState) => {
502
- dispatch({ type: "SET_COLUMN_FILTER_RESET_PAGE", payload: state });
503
- },
504
-
505
- ...(enableSorting
506
- ? {
507
- onSortingChange: (updaterOrValue: any) => {
508
- const prev = uiRef.current.sorting;
509
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
510
- const cleaned = (next || []).filter((s: any) => s?.id);
511
- onSortingChangeRef.current?.(cleaned);
512
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
513
- },
514
- }
515
- : {}),
516
-
517
- ...(enablePagination
518
- ? {
519
- onPaginationChange: (updater: any) => {
520
- const prev = uiRef.current.pagination;
521
- const next = typeof updater === "function" ? updater(prev) : updater;
522
- onPaginationChangeRef.current?.(next);
523
- dispatch({ type: "SET_PAGINATION", payload: next });
524
- },
525
- }
526
- : {}),
527
-
528
- ...(enableGlobalFilter
529
- ? {
530
- onGlobalFilterChange: (updaterOrValue: any) => {
531
- const prev = uiRef.current.globalFilter;
532
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
533
- onGlobalFilterChangeRef.current?.(next);
534
- nextFetchDelayRef.current = 400;
535
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: next });
536
- },
537
- }
538
- : {}),
539
-
540
- ...(enableExpanding ? { onExpandedChange: (u: any) => dispatch({ type: "SET_EXPANDED", payload: typeof u === "function" ? u(uiRef.current.expanded) : u }) } : {}),
541
- ...(enableColumnDragging ? { onColumnOrderChange: (u: any) => dispatch({ type: "SET_COLUMN_ORDER", payload: typeof u === "function" ? u(uiRef.current.columnOrder) : u }) } : {}),
542
- ...(enableColumnPinning ? { onColumnPinningChange: (u: any) => dispatch({ type: "SET_COLUMN_PINNING", payload: typeof u === "function" ? u(uiRef.current.columnPinning) : u }) } : {}),
543
- ...(enableColumnVisibility ? { onColumnVisibilityChange: (u: any) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: typeof u === "function" ? u(uiRef.current.columnVisibility) : u }) } : {}),
544
- ...(enableColumnResizing ? { onColumnSizingChange: (u: any) => dispatch({ type: "SET_COLUMN_SIZING", payload: typeof u === "function" ? u(uiRef.current.columnSizing) : u }) } : {}),
545
-
546
- getCoreRowModel: getCoreRowModel(),
547
- ...(enableSorting ? { getSortedRowModel: getSortedRowModel() } : {}),
548
- ...(enableColumnFilter || enableGlobalFilter ? { getFilteredRowModel: getCombinedFilteredRowModel<T>() } : {}),
549
- ...(enablePagination && !isServerPagination ? { getPaginationRowModel: getPaginationRowModel() } : {}),
550
-
551
- enableSorting,
552
- manualSorting: isServerSorting,
553
- manualFiltering: isServerFiltering,
554
-
555
- enableColumnResizing,
556
- columnResizeMode,
557
- columnResizeDirection: theme.direction,
558
-
559
- enableColumnPinning,
560
- ...(enableExpanding ? { getRowCanExpand: getRowCanExpand as any } : {}),
561
-
562
- manualPagination: isServerPagination,
563
- autoResetPageIndex: false,
564
-
565
- rowCount: enablePagination ? (tableTotalRow ?? tableData.length) : tableData.length,
566
- getRowId: (row: any, index: number) => generateRowId(row, index, idKey),
567
- });
568
-
569
- // --- layout sizing
570
- const allLeafColumns = table.getAllLeafColumns();
571
- const hasExplicitSizing = allLeafColumns.some((col) => {
572
- const { size, minSize, maxSize } = col.columnDef;
573
- return size !== undefined || minSize !== undefined || maxSize !== undefined;
574
- });
575
- const useFixedLayout = fitToScreen || enableColumnResizing || hasExplicitSizing;
576
- const tableTotalSize = table.getTotalSize();
577
- const tableWidth = fitToScreen ? "100%" : useFixedLayout ? tableTotalSize : "100%";
578
-
579
- const tableStyle: CSSProperties = {
580
- width: tableWidth,
581
- minWidth: fitToScreen ? tableTotalSize : undefined,
582
- tableLayout: useFixedLayout ? "fixed" : "auto",
583
- };
584
-
585
- // --- virtualization
586
- const rows = table.getRowModel().rows;
587
- const rowVirtualizer = useVirtualizer({
588
- count: rows.length,
589
- getScrollElement: () => tableContainerRef.current,
590
- estimateSize: () => estimateRowHeight,
591
- overscan: 10,
592
- enabled: enableVirtualization && !enablePagination && rows.length > 0,
593
- });
594
-
595
-
596
- const serverKey = useMemo(() => {
597
- if (!(isServerMode || isServerPagination || isServerFiltering || isServerSorting)) return null;
598
-
599
- return JSON.stringify({
600
- sorting: ui.sorting,
601
- pagination: ui.pagination,
602
- globalFilter: ui.globalFilter,
603
- columnFilter: { filters: ui.columnFilter.filters, logic: ui.columnFilter.logic }, // only applied
604
- });
605
- }, [isServerMode, isServerPagination, isServerFiltering, isServerSorting, ui.sorting, ui.pagination, ui.globalFilter, ui.columnFilter]);
606
- const serverKeyRef = useLatestRef(serverKey);
607
- const lastServerKeyRef = useRef<string | null>(null);
608
-
609
- // --- initial fetch
610
- useEffect(() => {
611
- if (!initialLoadData) return;
612
- // If we're in server mode, mark current serverKey as already handled
613
- // so the serverKey effect doesn't immediately fetch again.
614
- if (serverKeyRef.current) {
615
- lastServerKeyRef.current = serverKeyRef.current;
616
- }
617
- if (onFetchData || onFetchStateChange) {
618
- void fetchData({}, { delay: 0, meta: { reason: "initial" } });
619
- }
620
- // eslint-disable-next-line react-hooks/exhaustive-deps
621
- }, []);
622
-
623
- useEffect(() => {
624
- if (!serverKey) return;
625
- if (serverKey === lastServerKeyRef.current) return;
626
- lastServerKeyRef.current = serverKey;
627
-
628
- const delay = nextFetchDelayRef.current ?? 0;
629
- nextFetchDelayRef.current = 0; // reset after using
630
-
631
- const timeoutId = setTimeout(() => {
632
- void fetchData({}, { delay, meta: { reason: "stateChange" } });
633
- }, 0);
634
- return () => clearTimeout(timeoutId);
635
- }, [serverKey, fetchData]);
636
-
637
- // columnFilter apply handler stays explicit (button), but you can also auto-fetch on change if needed
638
- const handleColumnFilterChangeHandler = useCallback(
639
- (updater: any, isApply = false) => {
640
- const prev = uiRef.current.columnFilter;
641
- const next = typeof updater === "function" ? updater(prev) : updater;
642
- if (isApply) {
643
- nextFetchDelayRef.current = 0;
644
- dispatch({ type: "SET_COLUMN_FILTER_RESET_PAGE", payload: next });
645
- } else {
646
- dispatch({ type: "SET_COLUMN_FILTER", payload: next });
647
- }
648
- onColumnFiltersChangeRef.current?.(next, isApply);
649
- },
650
- [onColumnFiltersChangeRef, uiRef]
651
- );
652
-
653
- // --- emit table state (dedupe)
654
- useEffect(() => {
655
- const cb = onDataStateChangeRef.current;
656
- if (!cb) return;
657
-
658
- const live = table.getState();
659
- const payload = {
660
- sorting: live.sorting,
661
- pagination: live.pagination,
662
- globalFilter: live.globalFilter,
663
- columnFilter: live.columnFilter,
664
- columnVisibility: live.columnVisibility,
665
- columnSizing: live.columnSizing,
666
- columnOrder: live.columnOrder,
667
- columnPinning: live.columnPinning,
668
- };
669
- const key = JSON.stringify(payload);
670
- if (key === lastSentRef.current) return;
671
- lastSentRef.current = key;
672
- cb(payload);
673
- }, [table, ui.sorting, ui.pagination, ui.globalFilter, ui.columnFilter, ui.columnVisibility, ui.columnSizing, ui.columnOrder, ui.columnPinning, onDataStateChangeRef]);
674
-
675
- // --- helpers
676
- const resetPageToFirst = useCallback(() => {
677
- return { pageIndex: 0, pageSize: uiRef.current.pagination.pageSize };
678
- }, [uiRef]);
679
-
680
- const normalizeRefreshOptions = useCallback(
681
- (options?: boolean | DataRefreshOptions, fallbackReason: string = "refresh") => {
682
- if (typeof options === "boolean") return { resetPagination: options, force: false, reason: fallbackReason };
683
- return {
684
- resetPagination: options?.resetPagination ?? false,
685
- force: options?.force ?? false,
686
- reason: options?.reason ?? fallbackReason,
687
- };
688
- },
689
- []
690
- );
691
-
692
- const triggerRefresh = useCallback(
693
- async (options?: boolean | DataRefreshOptions, fallbackReason: string = "refresh") => {
694
- const n = normalizeRefreshOptions(options, fallbackReason);
695
- const current = uiRef.current.pagination;
696
- const nextPagination = enablePagination
697
- ? { pageIndex: n.resetPagination ? 0 : current.pageIndex, pageSize: current.pageSize }
698
- : undefined;
699
-
700
- if (nextPagination) {
701
- nextFetchDelayRef.current = 0;
702
- dispatch({ type: "SET_PAGINATION", payload: nextPagination });
703
- onPaginationChangeRef.current?.(nextPagination);
704
- }
705
- const paginationChanged = !!nextPagination &&
706
- (nextPagination.pageIndex !== current.pageIndex || nextPagination.pageSize !== current.pageSize);
707
-
708
- if (!paginationChanged) {
709
- await fetchData({}, { delay: 0, meta: { reason: n.reason, force: n.force } });
710
- }
711
- },
712
- [enablePagination, fetchData, normalizeRefreshOptions, onPaginationChangeRef, uiRef]
713
- );
714
-
715
- const getResetPayload = useCallback((): Partial<EngineUIState> => {
716
- const resetSorting = initialStateConfig.sorting || [];
717
- const resetGlobalFilter = initialStateConfig.globalFilter ?? "";
718
- const resetColumnFilter = (initialStateConfig.columnFilter ?? DEFAULT_INITIAL_STATE.columnFilter) as ColumnFilterState;
719
-
720
- const resetPagination = enablePagination
721
- ? (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 })
722
- : uiRef.current.pagination;
723
-
724
- return {
725
- sorting: resetSorting,
726
- globalFilter: resetGlobalFilter,
727
- columnFilter: resetColumnFilter,
728
- ...(enablePagination ? { pagination: resetPagination } : {}),
729
- selectionState: initialStateConfig.selectionState ?? DEFAULT_INITIAL_STATE.selectionState,
730
- expanded: {},
731
- columnVisibility: initialStateConfig.columnVisibility || {},
732
- columnSizing: initialStateConfig.columnSizing || {},
733
- columnOrder: initialStateConfig.columnOrder || [],
734
- columnPinning: initialStateConfig.columnPinning || { left: [], right: [] },
735
- };
736
- }, [enablePagination, initialStateConfig]);
737
-
738
- const resetAllAndReload = useCallback(() => {
739
- const payload = getResetPayload();
740
- dispatch({ type: "RESET_ALL", payload });
741
- void fetchData(
742
- {
743
- sorting: payload.sorting as any,
744
- globalFilter: payload.globalFilter as any,
745
- columnFilter: payload.columnFilter as any,
746
- ...(enablePagination ? { pagination: payload.pagination as any } : {}),
747
- },
748
- { delay: 0, meta: { reason: "reset", force: true } }
749
- );
750
- }, [enablePagination, fetchData, getResetPayload]);
751
-
752
- // --- export (refs + small UI state)
753
- const setExportControllerSafely = useCallback(
754
- (value: AbortController | null | ((current: AbortController | null) => AbortController | null)) => {
755
- setExportController((current) => {
756
- const next = typeof value === "function" ? (value as any)(current) : value;
757
- exportControllerRef.current = next;
758
- return next;
759
- });
760
- },
761
- []
762
- );
763
-
764
- const handleExportProgressInternal = useCallback((p: ExportProgressPayload) => {
765
- setExportProgress(p || {});
766
- onExportProgressRef.current?.(p);
767
- }, []);
768
-
769
- const handleExportStateChangeInternal = useCallback((s: ExportStateChange) => {
770
- setExportPhase(s.phase);
771
- if (s.processedRows !== undefined || s.totalRows !== undefined || s.percentage !== undefined) {
772
- setExportProgress({
773
- processedRows: s.processedRows,
774
- totalRows: s.totalRows,
775
- percentage: s.percentage,
776
- });
777
- }
778
- onExportStateChangeRef.current?.(s);
779
- }, []);
780
-
781
- const runExportWithPolicy = useCallback(
782
- async (options: { format: "csv" | "excel"; filename: string; mode: "client" | "server"; execute: (controller: AbortController) => Promise<void> }) => {
783
- const { format, filename, mode, execute } = options;
784
-
785
- const startExecution = async () => {
786
- const controller = new AbortController();
787
- setExportProgress({});
788
- setExportControllerSafely(controller);
789
- try {
790
- await execute(controller);
791
- } finally {
792
- setExportControllerSafely((cur) => (cur === controller ? null : cur));
793
- }
794
- };
795
-
796
- if (exportConcurrency === "queue") {
797
- setQueuedExportCount((p) => p + 1);
798
- const runQueued = async () => {
799
- setQueuedExportCount((p) => Math.max(0, p - 1));
800
- await startExecution();
801
- };
802
- const queuedPromise = exportQueueRef.current.catch(() => undefined).then(runQueued);
803
- exportQueueRef.current = queuedPromise;
804
- return queuedPromise;
805
- }
806
-
807
- const active = exportControllerRef.current;
808
- if (active) {
809
- if (exportConcurrency === "ignoreIfRunning") {
810
- handleExportStateChangeInternal({
811
- phase: "error",
812
- mode,
813
- format,
814
- filename,
815
- message: "An export is already running",
816
- code: "EXPORT_IN_PROGRESS",
817
- endedAt: Date.now(),
818
- } as any);
819
- onExportErrorRef.current?.({ message: "An export is already running", code: "EXPORT_IN_PROGRESS" });
820
- return;
821
- }
822
- if (exportConcurrency === "cancelAndRestart") active.abort();
823
- }
824
-
825
- await startExecution();
826
- },
827
- [exportConcurrency, handleExportStateChangeInternal, onExportErrorRef, setExportControllerSafely]
828
- );
829
-
830
- const handleCancelExport = useCallback(() => {
831
- const active = exportControllerRef.current;
832
- if (!active) return;
833
- active.abort();
834
- setExportControllerSafely((cur) => (cur === active ? null : cur));
835
- onExportCancelRef.current?.();
836
- }, [onExportCancelRef, setExportControllerSafely]);
837
-
838
- const isExporting = exportController !== null;
839
-
840
- // --- stable API (created once)
841
- if (!apiRef.current) {
842
- apiRef.current = {} as DataTableApi<T>;
843
-
844
- // IMPORTANT: do NOT capture `table/ui/data` here. Always read from refs inside methods.
845
- (apiRef.current as any).table = { getTable: () => table }; // will be updated below via tableRef
846
- // We'll overwrite getTable below with a ref-backed function.
847
- }
848
-
849
- // table ref so API always returns latest table instance
850
- const tableRef = useLatestRef(table);
851
-
852
-
853
- useEffect(() => {
854
- const api = apiRef.current!;
855
- api.table = { getTable: () => tableRef.current } as any;
856
-
857
- // --- state getters
858
- api.state = {
859
- getTableState: () => tableRef.current.getState(),
860
- getCurrentFilters: () => tableRef.current.getState().columnFilter,
861
- getCurrentSorting: () => tableRef.current.getState().sorting,
862
- getCurrentPagination: () => tableRef.current.getState().pagination,
863
- getCurrentSelection: () => uiRef.current.selectionState,
864
- getGlobalFilter: () => tableRef.current.getState().globalFilter,
865
- };
866
-
867
- // --- data
868
- const getBaseData = () => {
869
- const sData = serverDataRef.current;
870
- return sData !== null ? sData : dataRef.current;
871
- };
872
- const getRowIndexById = (arr: T[], rowId: string) =>
873
- arr.findIndex((row, i) => generateRowId(row, i, idKey) === rowId);
874
- api.data = {
875
- refresh: (options?: boolean | DataRefreshOptions) => void triggerRefresh(options, "refresh"),
876
- reload: (options: DataRefreshOptions = {}) => void triggerRefresh({ ...options, reason: options.reason ?? "reload" }, "reload"),
877
- resetAll: () => resetAllAndReload(),
878
- getAllData: () => [...getBaseData()],
879
- getRowData: (rowId: string) => {
880
- const rows = tableRef.current.getRowModel().rows;
881
- const row = rows.find((r: any) => r.id === rowId);
882
- return row?.original as T | undefined;
883
- },
884
- getRowByIndex: (index: number) => tableRef.current.getRowModel().rows[index]?.original as T | undefined,
885
- getDataCount: () => getBaseData().length,
886
- getFilteredDataCount: () => tableRef.current.getFilteredRowModel().rows.length,
887
- updateRow: (rowId: string, updates: Partial<T>) => {
888
- const base = getBaseData();
889
- const idx = getRowIndexById(base, rowId);
890
- if (idx === -1) return;
891
- const next = [...base];
892
- next[idx] = { ...next[idx], ...updates } as T;
893
- setServerData(next);
894
- setServerTotal(next.length);
895
- },
896
- updateRowByIndex: (index: number, updates: Partial<T>) => {
897
- const base = getBaseData();
898
- if (index < 0 || index >= base.length) return;
899
- const next = [...base];
900
- next[index] = { ...next[index], ...updates } as T;
901
- setServerData(next);
902
- setServerTotal(next.length);
903
- },
904
- insertRow: (newRow: T, index?: number) => {
905
- const base = getBaseData();
906
- const next = index == null ? [...base, newRow] : [...base.slice(0, index), newRow, ...base.slice(index)];
907
- setServerData(next);
908
- setServerTotal(next.length);
909
- },
910
- deleteRow: (rowId: string) => {
911
- const base = getBaseData();
912
- const idx = getRowIndexById(base, rowId);
913
- if (idx === -1) return;
914
- const next = base.filter((_, i) => i !== idx);
915
- setServerData(next);
916
- setServerTotal(next.length);
917
- },
918
- deleteRowByIndex: (index: number) => {
919
- const base = getBaseData();
920
- if (index < 0 || index >= base.length) return;
921
- const next = base.filter((_, i) => i !== index);
922
- setServerData(next);
923
- setServerTotal(next.length);
924
- },
925
- deleteSelectedRows: () => {
926
- const state = tableRef.current.getSelectionState?.();
927
- if (!state || state.type !== "include" || !state.ids.length) return;
928
- const base = getBaseData();
929
- const ids = new Set(state.ids);
930
- const next = base.filter((row, i) => !ids.has(generateRowId(row, i, idKey)));
931
- setServerData(next);
932
- setServerTotal(next.length);
933
- tableRef.current.deselectAll?.();
934
- },
935
- replaceAllData: (newData: T[]) => {
936
- setServerData(newData);
937
- setServerTotal(newData.length);
938
- },
939
- updateMultipleRows: (updates: Array<{ rowId: string; data: Partial<T> }>) => {
940
- const base = getBaseData();
941
- const next = [...base];
942
- for (const { rowId, data: u } of updates) {
943
- const idx = getRowIndexById(next, rowId);
944
- if (idx !== -1) next[idx] = { ...next[idx], ...u } as T;
945
- }
946
- setServerData(next);
947
- setServerTotal(next.length);
948
- },
949
- insertMultipleRows: (newRows: T[], startIndex?: number) => {
950
- const base = getBaseData();
951
- const idx = startIndex ?? base.length;
952
- const next = [...base.slice(0, idx), ...newRows, ...base.slice(idx)];
953
- setServerData(next);
954
- setServerTotal(next.length);
955
- },
956
- deleteMultipleRows: (rowIds: string[]) => {
957
- const ids = new Set(rowIds);
958
- const base = getBaseData();
959
- const next = base.filter((row, i) => !ids.has(generateRowId(row, i, idKey)));
960
- setServerData(next);
961
- setServerTotal(next.length);
962
- },
963
- updateField: (rowId: string, fieldName: keyof T, value: any) => {
964
- api.data.updateRow(rowId, { [fieldName]: value } as Partial<T>);
965
- },
966
- updateFieldByIndex: (index: number, fieldName: keyof T, value: any) => {
967
- api.data.updateRowByIndex(index, { [fieldName]: value } as Partial<T>);
968
- },
969
- findRows: (predicate: (row: T) => boolean) => getBaseData().filter(predicate),
970
- findRowIndex: (predicate: (row: T) => boolean) => getBaseData().findIndex(predicate),
971
- } as any;
972
-
973
- // --- layout (save/restore column visibility, order, sizing, pinning)
974
- api.layout = {
975
- saveLayout: () => {
976
- const s = tableRef.current.getState();
977
- return {
978
- columnVisibility: s.columnVisibility ?? {},
979
- columnOrder: s.columnOrder ?? [],
980
- columnSizing: s.columnSizing ?? {},
981
- columnPinning: s.columnPinning ?? { left: [], right: [] },
982
- pagination: s.pagination ?? { pageIndex: 0, pageSize: 10 },
983
- globalFilter: s.globalFilter ?? "",
984
- columnFilter: s.columnFilter ?? [],
985
- };
986
- },
987
- restoreLayout: (layout: any) => {
988
-
989
- if (layout.columnVisibility) dispatch({ type: "SET_COLUMN_VISIBILITY", payload: layout.columnVisibility });
990
- if (layout.columnOrder) { dispatch({ type: "SET_COLUMN_ORDER", payload: layout.columnOrder }); onColumnDragEndRef.current?.(layout.columnOrder); }
991
- if (layout.columnSizing) { dispatch({ type: "SET_COLUMN_SIZING", payload: layout.columnSizing }); onColumnSizingChangeRef.current?.(layout.columnSizing); }
992
- if (layout.columnPinning) { dispatch({ type: "SET_COLUMN_PINNING", payload: layout.columnPinning }); onColumnPinningChangeRef.current?.(layout.columnPinning); }
993
- if (layout.pagination && enablePagination) dispatch({ type: "SET_PAGINATION", payload: layout.pagination as any });
994
- if (layout.globalFilter !== undefined) dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: layout.globalFilter });
995
- if (layout.columnFilter) dispatch({ type: "SET_COLUMN_FILTER", payload: layout.columnFilter as any });
996
- },
997
- resetLayout: () => {
998
- const vis = initialStateConfig.columnVisibility || {};
999
- const order = initialStateConfig.columnOrder || [];
1000
- const sizing = initialStateConfig.columnSizing || {};
1001
- const pinning = initialStateConfig.columnPinning || { left: [] as string[], right: [] as string[] };
1002
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: vis });
1003
- dispatch({ type: "SET_COLUMN_ORDER", payload: order });
1004
- dispatch({ type: "SET_COLUMN_SIZING", payload: sizing });
1005
- dispatch({ type: "SET_COLUMN_PINNING", payload: pinning });
1006
- dispatch({ type: "SET_PAGINATION", payload: { pageIndex: 0, pageSize: 10 } });
1007
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: "" });
1008
- dispatch({ type: "SET_COLUMN_FILTER", payload: [] });
1009
- onColumnDragEndRef.current?.(order);
1010
- onColumnSizingChangeRef.current?.(sizing);
1011
- onColumnPinningChangeRef.current?.(pinning);
1012
- },
1013
- resetAll: () => {
1014
- api.layout.resetLayout();
1015
- resetAllAndReload();
1016
- },
1017
- } as any;
1018
-
1019
- // --- sorting/pagination/filtering - dispatch + callbacks + server fetch policies
1020
- api.sorting = {
1021
- setSorting: (next: SortingState) => {
1022
- const cleaned = (next || []).filter((s: any) => s?.id);
1023
- onSortingChangeRef.current?.(cleaned);
1024
- nextFetchDelayRef.current = 0;
1025
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
1026
- },
1027
- sortColumn: (columnId: string, direction: 'asc' | 'desc' | false) => {
1028
- const next: SortingState = direction === false ? [] : [{ id: columnId, desc: direction === 'desc' }];
1029
- onSortingChangeRef.current?.(next);
1030
- nextFetchDelayRef.current = 0;
1031
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: next });
1032
- },
1033
- clearSorting: () => {
1034
- onSortingChangeRef.current?.([]);
1035
- nextFetchDelayRef.current = 0;
1036
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: [] });
1037
- },
1038
- resetSorting: () => {
1039
- const next = (initialStateConfig.sorting || []) as SortingState;
1040
- onSortingChangeRef.current?.(next);
1041
- nextFetchDelayRef.current = 0;
1042
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: next });
1043
- },
1044
- } as any;
1045
-
1046
- api.pagination = {
1047
- goToPage: (pageIndex: number) => {
1048
- const prev = uiRef.current.pagination;
1049
- const next = { ...prev, pageIndex };
1050
- onPaginationChangeRef.current?.(next);
1051
- nextFetchDelayRef.current = 0;
1052
- dispatch({ type: "SET_PAGINATION", payload: next });
1053
- },
1054
- nextPage: () => {
1055
- const prev = uiRef.current.pagination;
1056
- api.pagination.goToPage(Math.min(prev.pageIndex + 1, Math.max(0, Math.ceil((tableTotalRow ?? 0) / prev.pageSize) - 1)));
1057
- },
1058
- previousPage: () => {
1059
- const prev = uiRef.current.pagination;
1060
- api.pagination.goToPage(Math.max(0, prev.pageIndex - 1));
1061
- },
1062
- setPageSize: (pageSize: number) => {
1063
- const next = { pageIndex: 0, pageSize };
1064
- onPaginationChangeRef.current?.(next);
1065
- nextFetchDelayRef.current = 0;
1066
- dispatch({ type: "SET_PAGINATION", payload: next });
1067
- },
1068
- goToFirstPage: () => api.pagination.goToPage(0),
1069
- goToLastPage: () => {
1070
- const prev = uiRef.current.pagination;
1071
- api.pagination.goToPage(Math.max(0, Math.ceil((tableTotalRow ?? 0) / prev.pageSize) - 1));
1072
- },
1073
- resetPagination: () => {
1074
- const next = (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 }) as any;
1075
- onPaginationChangeRef.current?.(next);
1076
- nextFetchDelayRef.current = 0;
1077
- dispatch({ type: "SET_PAGINATION", payload: next });
1078
- },
1079
- } as any;
1080
-
1081
- api.filtering = {
1082
- setGlobalFilter: (filter: string) => {
1083
- onGlobalFilterChangeRef.current?.(filter);
1084
- nextFetchDelayRef.current = 400;
1085
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: filter });
1086
- },
1087
- clearGlobalFilter: () => {
1088
- onGlobalFilterChangeRef.current?.("");
1089
- nextFetchDelayRef.current = 400;
1090
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: "" });
1091
- },
1092
- setColumnFilters: (filters: ColumnFilterState, isApply = false) => handleColumnFilterChangeHandler(filters, isApply),
1093
- addColumnFilter: (columnId: string, operator: string, value: any) => tableRef.current.addColumnFilter?.(columnId, operator, value),
1094
- removeColumnFilter: (filterId: string) => tableRef.current.removeColumnFilter?.(filterId),
1095
- clearAllFilters: () => tableRef.current.resetColumnFilter?.(),
1096
- resetFilters: () => {
1097
- const reset = (initialStateConfig.columnFilter ?? DEFAULT_INITIAL_STATE.columnFilter) as ColumnFilterState;
1098
- handleColumnFilterChangeHandler(reset, true);
1099
- },
1100
- } as any;
1101
-
1102
- api.columnVisibility = {
1103
- showColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: true } }),
1104
- hideColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: false } }),
1105
- toggleColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: !uiRef.current.columnVisibility[id] } }),
1106
- showAllColumns: () => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: {} }),
1107
- hideAllColumns: () => {
1108
- const all = tableRef.current.getAllLeafColumns().reduce((acc, col) => ({ ...acc, [col.id]: false }), {} as Record<string, boolean>);
1109
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: all });
1110
- },
1111
- resetColumnVisibility: () => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: initialStateConfig.columnVisibility || {} }),
1112
- } as any;
1113
-
1114
- api.columnOrdering = {
1115
- setColumnOrder: (next: ColumnOrderState) => {
1116
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1117
- onColumnDragEndRef.current?.(next);
1118
- },
1119
- moveColumn: (columnId: string, toIndex: number) => {
1120
- const order = uiRef.current.columnOrder.length ? uiRef.current.columnOrder : tableRef.current.getAllLeafColumns().map((c: any) => c.id);
1121
- const from = order.indexOf(columnId);
1122
- if (from === -1 || toIndex < 0 || toIndex >= order.length) return;
1123
- const next = [...order];
1124
- next.splice(from, 1);
1125
- next.splice(toIndex, 0, columnId);
1126
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1127
- onColumnDragEndRef.current?.(next);
1128
- },
1129
- resetColumnOrder: () => dispatch({ type: "SET_COLUMN_ORDER", payload: initialStateConfig.columnOrder || [] }),
1130
- } as any;
1131
-
1132
- api.columnPinning = {
1133
- pinColumnLeft: (columnId: string) => {
1134
- const cur = uiRef.current.columnPinning;
1135
- const left = cur.left.includes(columnId) ? cur.left : [...cur.left.filter((id: string) => id !== columnId), columnId];
1136
- const right = cur.right.filter((id: string) => id !== columnId);
1137
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1138
- onColumnPinningChangeRef.current?.({ left, right });
1139
- },
1140
- pinColumnRight: (columnId: string) => {
1141
- const cur = uiRef.current.columnPinning;
1142
- const left = cur.left.filter((id: string) => id !== columnId);
1143
- const right = cur.right.includes(columnId) ? cur.right : [...cur.right.filter((id: string) => id !== columnId), columnId];
1144
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1145
- onColumnPinningChangeRef.current?.({ left, right });
1146
- },
1147
- unpinColumn: (columnId: string) => {
1148
- const cur = uiRef.current.columnPinning;
1149
- const left = cur.left.filter((id: string) => id !== columnId);
1150
- const right = cur.right.filter((id: string) => id !== columnId);
1151
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1152
- onColumnPinningChangeRef.current?.({ left, right });
1153
- },
1154
- setPinning: (next: ColumnPinningState) => {
1155
- dispatch({ type: "SET_COLUMN_PINNING", payload: next });
1156
- onColumnPinningChangeRef.current?.(next);
1157
- },
1158
- resetColumnPinning: () => dispatch({ type: "SET_COLUMN_PINNING", payload: initialStateConfig.columnPinning || { left: [], right: [] } }),
1159
- } as any;
1160
-
1161
- api.columnResizing = {
1162
- resizeColumn: (columnId: string, width: number) => {
1163
- const cur = uiRef.current.columnSizing;
1164
- dispatch({ type: "SET_COLUMN_SIZING", payload: { ...cur, [columnId]: width } });
1165
- onColumnSizingChangeRef.current?.({ ...cur, [columnId]: width });
1166
- },
1167
- autoSizeColumn: (columnId: string) => { void columnId; /* no-op: would require measure; use reset for default */ },
1168
- autoSizeAllColumns: () => { /* no-op */ },
1169
- resetColumnSizing: () => dispatch({ type: "SET_COLUMN_SIZING", payload: initialStateConfig.columnSizing || {} }),
1170
- } as any;
1171
-
1172
- api.selection = {
1173
- selectRow: (rowId: string) => tableRef.current.selectRow?.(rowId),
1174
- deselectRow: (rowId: string) => tableRef.current.deselectRow?.(rowId),
1175
- toggleRowSelection: (rowId: string) => tableRef.current.toggleRowSelected?.(rowId),
1176
- selectAll: () => tableRef.current.selectAll?.(),
1177
- deselectAll: () => tableRef.current.deselectAll?.(),
1178
- toggleSelectAll: () => tableRef.current.toggleAllRowsSelected?.(),
1179
- getSelectionState: () => tableRef.current.getSelectionState?.() || ({ ids: [], type: "include" } as const),
1180
- getSelectedRows: () => tableRef.current.getSelectedRows(),
1181
- getSelectedCount: () => tableRef.current.getSelectedCount(),
1182
- isRowSelected: (rowId: string) => tableRef.current.getIsRowSelected(rowId) || false,
1183
- } as any;
1184
-
1185
- // --- export API (use your existing exportClientData/exportServerData)
1186
- api.export = {
1187
- exportCSV: async (options: DataTableExportApiOptions = {}) => {
1188
- const fn = options.filename ?? exportFilename;
1189
- const chunkSize = options.chunkSize ?? exportChunkSize;
1190
- const strictTotalCheck = options.strictTotalCheck ?? exportStrictTotalCheck;
1191
- const sanitizeCSV = options.sanitizeCSV ?? exportSanitizeCSV;
1192
-
1193
- const mode: "client" | "server" = dataMode === "server" && !!onServerExportRef.current ? "server" : "client";
1194
-
1195
- await runExportWithPolicy({
1196
- format: "csv",
1197
- filename: fn,
1198
- mode,
1199
- execute: async (controller) => {
1200
- // TODO: keep your state-change event mapping (starting/progress/completed/cancel/error)
1201
- if (mode === "server" && onServerExportRef.current) {
1202
- await exportServerData(tableRef.current, {
1203
- format: "csv",
1204
- filename: fn,
1205
- fetchData: (filters: any, selection: any, signal?: AbortSignal) =>
1206
- onServerExportRef.current?.(filters, selection, signal),
1207
- currentFilters: {
1208
- globalFilter: tableRef.current.getState().globalFilter,
1209
- columnFilter: tableRef.current.getState().columnFilter,
1210
- sorting: tableRef.current.getState().sorting,
1211
- pagination: tableRef.current.getState().pagination,
1212
- },
1213
- selection: tableRef.current.getSelectionState?.(),
1214
- onProgress: handleExportProgressInternal,
1215
- onComplete: onExportCompleteRef.current,
1216
- onError: onExportErrorRef.current,
1217
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1218
- signal: controller.signal,
1219
- chunkSize,
1220
- strictTotalCheck,
1221
- sanitizeCSV,
1222
- });
1223
- return;
1224
- }
1225
-
1226
- await exportClientData(tableRef.current, {
1227
- format: "csv",
1228
- filename: fn,
1229
- onProgress: handleExportProgressInternal,
1230
- onComplete: onExportCompleteRef.current,
1231
- onError: onExportErrorRef.current,
1232
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1233
- signal: controller.signal,
1234
- sanitizeCSV,
1235
- });
1236
- },
1237
- });
1238
- },
1239
-
1240
- exportExcel: async (options: DataTableExportApiOptions = {}) => {
1241
- const fn = options.filename ?? exportFilename;
1242
- const chunkSize = options.chunkSize ?? exportChunkSize;
1243
- const strictTotalCheck = options.strictTotalCheck ?? exportStrictTotalCheck;
1244
- const sanitizeCSV = options.sanitizeCSV ?? exportSanitizeCSV;
1245
-
1246
- const mode: "client" | "server" = dataMode === "server" && !!onServerExportRef.current ? "server" : "client";
1247
-
1248
- await runExportWithPolicy({
1249
- format: "excel",
1250
- filename: fn,
1251
- mode,
1252
- execute: async (controller) => {
1253
- // TODO: keep your state-change event mapping (starting/progress/completed/cancel/error)
1254
- if (mode === "server" && onServerExportRef.current) {
1255
- await exportServerData(tableRef.current, {
1256
- format: "excel",
1257
- filename: fn,
1258
- fetchData: (filters: any, selection: any, signal?: AbortSignal) =>
1259
- onServerExportRef.current?.(filters, selection, signal),
1260
- currentFilters: {
1261
- globalFilter: tableRef.current.getState().globalFilter,
1262
- columnFilter: tableRef.current.getState().columnFilter,
1263
- sorting: tableRef.current.getState().sorting,
1264
- pagination: tableRef.current.getState().pagination,
1265
- },
1266
- selection: tableRef.current.getSelectionState?.(),
1267
- onProgress: handleExportProgressInternal,
1268
- onComplete: onExportCompleteRef.current,
1269
- onError: onExportErrorRef.current,
1270
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1271
- signal: controller.signal,
1272
- chunkSize,
1273
- strictTotalCheck,
1274
- sanitizeCSV,
1275
- });
1276
- return;
1277
- }
1278
-
1279
- await exportClientData(tableRef.current, {
1280
- format: "excel",
1281
- filename: fn,
1282
- onProgress: handleExportProgressInternal,
1283
- onComplete: onExportCompleteRef.current,
1284
- onError: onExportErrorRef.current,
1285
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1286
- signal: controller.signal,
1287
- sanitizeCSV,
1288
- });
1289
- },
1290
- });
1291
- },
1292
-
1293
- exportServerData: async (options: { format: 'csv' | 'excel'; filename?: string; fetchData: (filters?: Partial<any>, selection?: SelectionState, signal?: AbortSignal) => Promise<any>; pageSize?: number; includeHeaders?: boolean; chunkSize?: number; strictTotalCheck?: boolean; sanitizeCSV?: boolean }) => {
1294
- const { format, filename: fn = exportFilename, fetchData: customFetchData, chunkSize: cs = exportChunkSize, strictTotalCheck: st = exportStrictTotalCheck, sanitizeCSV: san = exportSanitizeCSV } = options;
1295
- await runExportWithPolicy({
1296
- format,
1297
- filename: fn,
1298
- mode: "server",
1299
- execute: async (controller) => {
1300
- await exportServerData(tableRef.current, {
1301
- format,
1302
- filename: fn,
1303
- fetchData: customFetchData,
1304
- currentFilters: { globalFilter: tableRef.current.getState().globalFilter, columnFilter: tableRef.current.getState().columnFilter, sorting: tableRef.current.getState().sorting, pagination: tableRef.current.getState().pagination },
1305
- selection: tableRef.current.getSelectionState?.(),
1306
- onProgress: handleExportProgressInternal,
1307
- onComplete: onExportCompleteRef.current,
1308
- onError: onExportErrorRef.current,
1309
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode: "server", format, filename: fn, queueLength: queuedExportCount } as any),
1310
- signal: controller.signal,
1311
- chunkSize: cs,
1312
- strictTotalCheck: st,
1313
- sanitizeCSV: san,
1314
- });
1315
- },
1316
- });
1317
- },
1318
- isExporting: () => exportControllerRef.current != null,
1319
- cancelExport: () => handleCancelExport(),
1320
- } as any;
1321
- }, [dataMode, exportChunkSize, exportFilename, exportSanitizeCSV, exportStrictTotalCheck, fetchData, handleCancelExport, handleColumnFilterChangeHandler, handleExportProgressInternal, handleExportStateChangeInternal, initialStateConfig, isServerMode, isServerPagination, isServerSorting, resetAllAndReload, resetPageToFirst, runExportWithPolicy, triggerRefresh, queuedExportCount, tableTotalRow, idKey, tableRef, uiRef, serverDataRef, dataRef, onSortingChangeRef, onPaginationChangeRef, onGlobalFilterChangeRef, onColumnDragEndRef, onColumnPinningChangeRef, onColumnSizingChangeRef, onServerExportRef, onExportCompleteRef, onExportErrorRef]);
1322
-
1323
- // --- imperative handlers (used by TanStack callbacks above or view)
1324
- const handleSortingChange = useCallback(
1325
- (updaterOrValue: Updater<SortingState>) => {
1326
- const prev = uiRef.current.sorting;
1327
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
1328
- const cleaned = (next || []).filter((s: any) => s?.id);
1329
- onSortingChangeRef.current?.(cleaned);
1330
- nextFetchDelayRef.current = 0;
1331
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
1332
- },
1333
- [onSortingChangeRef, uiRef]
1334
- );
1335
-
1336
- const handlePaginationChange = useCallback(
1337
- (updater: Updater<PaginationState>) => {
1338
- const prev = uiRef.current.pagination;
1339
- const next = typeof updater === "function" ? updater(prev) : updater;
1340
- onPaginationChangeRef.current?.(next);
1341
- nextFetchDelayRef.current = 0;
1342
- dispatch({ type: "SET_PAGINATION", payload: next });
1343
- },
1344
- [onPaginationChangeRef, uiRef]
1345
- );
1346
-
1347
- const handleGlobalFilterChange = useCallback(
1348
- (updaterOrValue: Updater<string>) => {
1349
- const prev = uiRef.current.globalFilter;
1350
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
1351
- onGlobalFilterChangeRef.current?.(next);
1352
- nextFetchDelayRef.current = 400;
1353
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: next });
1354
- },
1355
- [onGlobalFilterChangeRef, uiRef]
1356
- );
1357
-
1358
- const handleColumnOrderChange = useCallback(
1359
- (updated: Updater<ColumnOrderState>) => {
1360
- const prev = uiRef.current.columnOrder;
1361
- const next = typeof updated === "function" ? updated(prev) : updated;
1362
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1363
- onColumnDragEndRef.current?.(next);
1364
- },
1365
- [onColumnDragEndRef, uiRef]
1366
- );
1367
-
1368
- const handleColumnPinningChange = useCallback(
1369
- (updated: Updater<ColumnPinningState>) => {
1370
- const prev = uiRef.current.columnPinning;
1371
- const next = typeof updated === "function" ? updated(prev) : updated;
1372
- dispatch({ type: "SET_COLUMN_PINNING", payload: next });
1373
- onColumnPinningChangeRef.current?.(next);
1374
- },
1375
- [onColumnPinningChangeRef, uiRef]
1376
- );
1377
-
1378
- const handleColumnVisibilityChange = useCallback(
1379
- (updated: any) => {
1380
- const prev = uiRef.current.columnVisibility;
1381
- const next = typeof updated === "function" ? updated(prev) : updated;
1382
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: next });
1383
- onColumnVisibilityChangeRef.current?.(next);
1384
- },
1385
- [onColumnVisibilityChangeRef, uiRef]
1386
- );
1387
-
1388
- const handleColumnSizingChange = useCallback(
1389
- (updated: any) => {
1390
- const prev = uiRef.current.columnSizing;
1391
- const next = typeof updated === "function" ? updated(prev) : updated;
1392
- dispatch({ type: "SET_COLUMN_SIZING", payload: next });
1393
- onColumnSizingChangeRef.current?.(next);
1394
- },
1395
- [onColumnSizingChangeRef, uiRef]
1396
- );
1397
-
1398
- const handleColumnReorder = useCallback(
1399
- (draggedId: string, targetId: string) => {
1400
- const currentOrder =
1401
- uiRef.current.columnOrder.length > 0
1402
- ? uiRef.current.columnOrder
1403
- : enhancedColumns.map((c: any, idx: number) => c.id ?? c.accessorKey ?? `column_${idx}`);
1404
-
1405
- const from = currentOrder.indexOf(draggedId);
1406
- const to = currentOrder.indexOf(targetId);
1407
- if (from === -1 || to === -1) return;
1408
-
1409
- const next = [...currentOrder];
1410
- next.splice(from, 1);
1411
- next.splice(to, 0, draggedId);
1412
-
1413
- handleColumnOrderChange(next);
1414
- },
1415
- [enhancedColumns, handleColumnOrderChange, uiRef]
1416
- );
1417
-
1418
- // optional: selection callback
1419
- useEffect(() => {
1420
- onSelectionChangeRef.current?.(ui.selectionState);
1421
- }, [onSelectionChangeRef, ui.selectionState]);
1422
-
1423
-
1424
-
1425
- // --- provider props
1426
- const providerProps = useMemo(
1427
- () => ({
1428
- table,
1429
- apiRef,
1430
- dataMode,
1431
- tableSize: ui.tableSize,
1432
- onTableSizeChange: (size: DataTableSize) => dispatch({ type: "SET_TABLE_SIZE", payload: size }),
1433
- columnFilter: ui.columnFilter,
1434
- onChangeColumnFilter: (f: ColumnFilterState) => handleColumnFilterChangeHandler(f, false),
1435
- slots,
1436
- slotProps,
1437
- isExporting,
1438
- exportController,
1439
- exportPhase,
1440
- exportProgress,
1441
- onCancelExport: handleCancelExport,
1442
- exportFilename,
1443
- onExportProgress,
1444
- onExportComplete,
1445
- onExportError,
1446
- onServerExport,
1447
- }),
1448
- [
1449
- table,
1450
- dataMode,
1451
- ui.tableSize,
1452
- ui.columnFilter,
1453
- slots,
1454
- slotProps,
1455
- isExporting,
1456
- exportController,
1457
- exportPhase,
1458
- exportProgress,
1459
- handleCancelExport,
1460
- exportFilename,
1461
- onExportProgress,
1462
- onExportComplete,
1463
- onExportError,
1464
- onServerExport,
1465
- handleColumnFilterChangeHandler,
1466
- ]
1467
- );
1468
-
1469
-
1470
- return {
1471
- table,
1472
- refs: {
1473
- tableContainerRef,
1474
- apiRef,
1475
- exportControllerRef,
1476
- },
1477
- derived: {
1478
- isServerMode,
1479
- isServerPagination,
1480
- isServerFiltering,
1481
- isServerSorting,
1482
- tableData,
1483
- tableTotalRow,
1484
- tableLoading,
1485
- rows,
1486
- visibleLeafColumns: table.getVisibleLeafColumns,
1487
- useFixedLayout,
1488
- tableStyle,
1489
- isExporting,
1490
- exportPhase,
1491
- exportProgress,
1492
- isSomeRowsSelected,
1493
- selectedRowCount,
1494
- },
1495
- state: ui,
1496
- actions: {
1497
- fetchData,
1498
- handleSortingChange,
1499
- handlePaginationChange,
1500
- handleGlobalFilterChange,
1501
- handleColumnFilterChangeHandler,
1502
- handleColumnOrderChange,
1503
- handleColumnPinningChange,
1504
- handleColumnVisibilityChange,
1505
- handleColumnSizingChange,
1506
- handleColumnReorder,
1507
- resetAllAndReload,
1508
- triggerRefresh,
1509
- setTableSize: (size: DataTableSize) => dispatch({ type: "SET_TABLE_SIZE", payload: size }),
1510
- handleCancelExport,
1511
- renderRowModel: { rowVirtualizer },
1512
- },
1513
- api: apiRef.current!,
1514
- providerProps,
1515
- };
1516
- }