@ackplus/react-tanstack-data-table 1.1.20 → 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 (61) 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 +4 -0
  3. package/package.json +3 -4
  4. package/src/index.ts +0 -75
  5. package/src/lib/components/data-table-view.tsx +0 -386
  6. package/src/lib/components/droupdown/menu-dropdown.tsx +0 -103
  7. package/src/lib/components/filters/filter-value-input.tsx +0 -225
  8. package/src/lib/components/filters/index.ts +0 -126
  9. package/src/lib/components/headers/draggable-header.tsx +0 -326
  10. package/src/lib/components/headers/index.ts +0 -6
  11. package/src/lib/components/headers/table-header.tsx +0 -175
  12. package/src/lib/components/index.ts +0 -21
  13. package/src/lib/components/pagination/data-table-pagination.tsx +0 -111
  14. package/src/lib/components/pagination/index.ts +0 -5
  15. package/src/lib/components/rows/data-table-row.tsx +0 -218
  16. package/src/lib/components/rows/empty-data-row.tsx +0 -69
  17. package/src/lib/components/rows/index.ts +0 -7
  18. package/src/lib/components/rows/loading-rows.tsx +0 -164
  19. package/src/lib/components/toolbar/bulk-actions-toolbar.tsx +0 -125
  20. package/src/lib/components/toolbar/column-filter-control.tsx +0 -432
  21. package/src/lib/components/toolbar/column-pinning-control.tsx +0 -275
  22. package/src/lib/components/toolbar/column-reset-control.tsx +0 -74
  23. package/src/lib/components/toolbar/column-visibility-control.tsx +0 -105
  24. package/src/lib/components/toolbar/data-table-toolbar.tsx +0 -257
  25. package/src/lib/components/toolbar/index.ts +0 -17
  26. package/src/lib/components/toolbar/table-export-control.tsx +0 -233
  27. package/src/lib/components/toolbar/table-refresh-control.tsx +0 -62
  28. package/src/lib/components/toolbar/table-search-control.tsx +0 -155
  29. package/src/lib/components/toolbar/table-size-control.tsx +0 -102
  30. package/src/lib/contexts/data-table-context.tsx +0 -126
  31. package/src/lib/data-table.tsx +0 -29
  32. package/src/lib/features/README.md +0 -161
  33. package/src/lib/features/column-filter.feature.ts +0 -493
  34. package/src/lib/features/index.ts +0 -23
  35. package/src/lib/features/selection.feature.ts +0 -322
  36. package/src/lib/hooks/index.ts +0 -2
  37. package/src/lib/hooks/use-data-table-engine.ts +0 -1552
  38. package/src/lib/icons/add-icon.tsx +0 -23
  39. package/src/lib/icons/csv-icon.tsx +0 -15
  40. package/src/lib/icons/delete-icon.tsx +0 -30
  41. package/src/lib/icons/excel-icon.tsx +0 -15
  42. package/src/lib/icons/index.ts +0 -7
  43. package/src/lib/icons/unpin-icon.tsx +0 -18
  44. package/src/lib/icons/view-comfortable-icon.tsx +0 -45
  45. package/src/lib/icons/view-compact-icon.tsx +0 -55
  46. package/src/lib/types/column.types.ts +0 -63
  47. package/src/lib/types/data-table-api.ts +0 -191
  48. package/src/lib/types/data-table.types.ts +0 -193
  49. package/src/lib/types/export.types.ts +0 -223
  50. package/src/lib/types/index.ts +0 -24
  51. package/src/lib/types/slots.types.ts +0 -342
  52. package/src/lib/types/table.types.ts +0 -88
  53. package/src/lib/utils/column-helpers.ts +0 -72
  54. package/src/lib/utils/debounced-fetch.utils.ts +0 -131
  55. package/src/lib/utils/export-utils.ts +0 -712
  56. package/src/lib/utils/index.ts +0 -27
  57. package/src/lib/utils/logger.ts +0 -203
  58. package/src/lib/utils/slot-helpers.tsx +0 -194
  59. package/src/lib/utils/special-columns.utils.ts +0 -101
  60. package/src/lib/utils/styling-helpers.ts +0 -126
  61. package/src/lib/utils/table-helpers.ts +0 -106
@@ -1,1552 +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);
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
- console.log('onSortingChange', cleaned);
512
- onSortingChangeRef.current?.(cleaned);
513
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
514
- },
515
- }
516
- : {}),
517
-
518
- ...(enablePagination
519
- ? {
520
- onPaginationChange: (updater: any) => {
521
- const prev = uiRef.current.pagination;
522
- const next = typeof updater === "function" ? updater(prev) : updater;
523
- onPaginationChangeRef.current?.(next);
524
- dispatch({ type: "SET_PAGINATION", payload: next });
525
- },
526
- }
527
- : {}),
528
-
529
- ...(enableGlobalFilter
530
- ? {
531
- onGlobalFilterChange: (updaterOrValue: any) => {
532
- const prev = uiRef.current.globalFilter;
533
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
534
- onGlobalFilterChangeRef.current?.(next);
535
- nextFetchDelayRef.current = 400;
536
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: next });
537
- },
538
- }
539
- : {}),
540
-
541
- ...(enableExpanding ? { onExpandedChange: (u: any) => {
542
- const prev = uiRef.current.expanded;
543
- const next = typeof u === "function" ? u(prev) : u;
544
- dispatch({ type: "SET_EXPANDED", payload: next });
545
- } } : {}),
546
-
547
- ...(enableColumnDragging ? {
548
- onColumnOrderChange: (u: any) => {
549
- const prev = uiRef.current.columnOrder;
550
- const next = typeof u === "function" ? u(prev) : u;
551
- onColumnDragEndRef.current?.(next);
552
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
553
- }
554
- } : {}),
555
- ...(enableColumnPinning ? {
556
- onColumnPinningChange: (u: any) => {
557
- const prev = uiRef.current.columnPinning;
558
- const next = typeof u === "function" ? u(prev) : u;
559
- onColumnPinningChangeRef.current?.(next);
560
- dispatch({ type: "SET_COLUMN_PINNING", payload: next });
561
- }
562
- } : {}),
563
- ...(enableColumnVisibility ? {
564
- onColumnVisibilityChange: (u: any) => {
565
- const prev = uiRef.current.columnVisibility;
566
- const next = typeof u === "function" ? u(prev) : u;
567
- onColumnVisibilityChangeRef.current?.(next);
568
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: next });
569
- }
570
- } : {}),
571
- ...(enableColumnResizing ? {
572
- onColumnSizingChange: (u: any) => {
573
- const prev = uiRef.current.columnSizing;
574
- const next = typeof u === "function" ? u(prev) : u;
575
- onColumnSizingChangeRef.current?.(next);
576
- dispatch({ type: "SET_COLUMN_SIZING", payload: next });
577
-
578
- }
579
- } : {}),
580
-
581
- getCoreRowModel: getCoreRowModel(),
582
- ...(enableSorting ? { getSortedRowModel: getSortedRowModel() } : {}),
583
- ...(enableColumnFilter || enableGlobalFilter ? { getFilteredRowModel: getCombinedFilteredRowModel<T>() } : {}),
584
- ...(enablePagination && !isServerPagination ? { getPaginationRowModel: getPaginationRowModel() } : {}),
585
-
586
- enableSorting,
587
- manualSorting: isServerSorting,
588
- manualFiltering: isServerFiltering,
589
-
590
- enableColumnResizing,
591
- columnResizeMode,
592
- columnResizeDirection: theme.direction,
593
-
594
- enableColumnPinning,
595
- ...(enableExpanding ? { getRowCanExpand: getRowCanExpand as any } : {}),
596
-
597
- manualPagination: isServerPagination,
598
- autoResetPageIndex: false,
599
-
600
- rowCount: enablePagination ? (tableTotalRow ?? tableData.length) : tableData.length,
601
- getRowId: (row: any, index: number) => generateRowId(row, index, idKey),
602
- });
603
-
604
- // --- layout sizing
605
- const allLeafColumns = table.getAllLeafColumns();
606
- const hasExplicitSizing = allLeafColumns.some((col) => {
607
- const { size, minSize, maxSize } = col.columnDef;
608
- return size !== undefined || minSize !== undefined || maxSize !== undefined;
609
- });
610
- const useFixedLayout = fitToScreen || enableColumnResizing || hasExplicitSizing;
611
- const tableTotalSize = table.getTotalSize();
612
- const tableWidth = fitToScreen ? "100%" : useFixedLayout ? tableTotalSize : "100%";
613
-
614
- const tableStyle: CSSProperties = {
615
- width: tableWidth,
616
- minWidth: fitToScreen ? tableTotalSize : undefined,
617
- tableLayout: useFixedLayout ? "fixed" : "auto",
618
- };
619
-
620
- // --- virtualization
621
- const rows = table.getRowModel().rows;
622
-
623
- const shouldVirtualize = enableVirtualization && rows.length > 0;
624
-
625
- const rowVirtualizer = useVirtualizer({
626
- count: rows.length,
627
- getScrollElement: () => tableContainerRef.current,
628
- estimateSize: () => estimateRowHeight,
629
- overscan: 8, // reduce a bit
630
- enabled: shouldVirtualize,
631
- });
632
-
633
-
634
- const serverKey = useMemo(() => {
635
- if (!(isServerMode || isServerPagination || isServerFiltering || isServerSorting)) return null;
636
-
637
- return JSON.stringify({
638
- sorting: ui.sorting,
639
- pagination: ui.pagination,
640
- globalFilter: ui.globalFilter,
641
- columnFilter: { filters: ui.columnFilter.filters, logic: ui.columnFilter.logic }, // only applied
642
- });
643
- }, [isServerMode, isServerPagination, isServerFiltering, isServerSorting, ui.sorting, ui.pagination, ui.globalFilter, ui.columnFilter]);
644
- const serverKeyRef = useLatestRef(serverKey);
645
- const lastServerKeyRef = useRef<string | null>(null);
646
-
647
- // --- initial fetch
648
- useEffect(() => {
649
- if (!initialLoadData) return;
650
- // If we're in server mode, mark current serverKey as already handled
651
- // so the serverKey effect doesn't immediately fetch again.
652
- if (serverKeyRef.current) {
653
- lastServerKeyRef.current = serverKeyRef.current;
654
- }
655
- if (onFetchData || onFetchStateChange) {
656
- void fetchData({}, { delay: 0, meta: { reason: "initial" } });
657
- }
658
- // eslint-disable-next-line react-hooks/exhaustive-deps
659
- }, []);
660
-
661
- useEffect(() => {
662
- if (!serverKey) return;
663
- if (serverKey === lastServerKeyRef.current) return;
664
- lastServerKeyRef.current = serverKey;
665
-
666
- const delay = nextFetchDelayRef.current ?? 0;
667
- nextFetchDelayRef.current = 0; // reset after using
668
-
669
- const timeoutId = setTimeout(() => {
670
- void fetchData({}, { delay, meta: { reason: "stateChange" } });
671
- }, 0);
672
- return () => clearTimeout(timeoutId);
673
- }, [serverKey, fetchData]);
674
-
675
- // columnFilter apply handler stays explicit (button), but you can also auto-fetch on change if needed
676
- const handleColumnFilterChangeHandler = useCallback(
677
- (updater: any, isApply = false) => {
678
- const prev = uiRef.current.columnFilter;
679
- const next = typeof updater === "function" ? updater(prev) : updater;
680
- if (isApply) {
681
- nextFetchDelayRef.current = 0;
682
- dispatch({ type: "SET_COLUMN_FILTER_RESET_PAGE", payload: next });
683
- } else {
684
- dispatch({ type: "SET_COLUMN_FILTER", payload: next });
685
- }
686
- onColumnFiltersChangeRef.current?.(next, isApply);
687
- },
688
- [onColumnFiltersChangeRef, uiRef]
689
- );
690
-
691
- // --- emit table state (dedupe)
692
- useEffect(() => {
693
- const cb = onDataStateChangeRef.current;
694
- if (!cb) return;
695
-
696
- const live = table.getState();
697
- const payload = {
698
- sorting: live.sorting,
699
- pagination: live.pagination,
700
- globalFilter: live.globalFilter,
701
- columnFilter: live.columnFilter,
702
- columnVisibility: live.columnVisibility,
703
- columnSizing: live.columnSizing,
704
- columnOrder: live.columnOrder,
705
- columnPinning: live.columnPinning,
706
- };
707
- const key = JSON.stringify(payload);
708
- if (key === lastSentRef.current) return;
709
- lastSentRef.current = key;
710
- cb(payload);
711
- }, [table, ui.sorting, ui.pagination, ui.globalFilter, ui.columnFilter, ui.columnVisibility, ui.columnSizing, ui.columnOrder, ui.columnPinning, onDataStateChangeRef]);
712
-
713
- // --- helpers
714
- const resetPageToFirst = useCallback(() => {
715
- return { pageIndex: 0, pageSize: uiRef.current.pagination.pageSize };
716
- }, [uiRef]);
717
-
718
- const normalizeRefreshOptions = useCallback(
719
- (options?: boolean | DataRefreshOptions, fallbackReason: string = "refresh") => {
720
- if (typeof options === "boolean") return { resetPagination: options, force: false, reason: fallbackReason };
721
- return {
722
- resetPagination: options?.resetPagination ?? false,
723
- force: options?.force ?? false,
724
- reason: options?.reason ?? fallbackReason,
725
- };
726
- },
727
- []
728
- );
729
-
730
- const triggerRefresh = useCallback(
731
- async (options?: boolean | DataRefreshOptions, fallbackReason: string = "refresh") => {
732
- const n = normalizeRefreshOptions(options, fallbackReason);
733
- const current = uiRef.current.pagination;
734
- const nextPagination = enablePagination
735
- ? { pageIndex: n.resetPagination ? 0 : current.pageIndex, pageSize: current.pageSize }
736
- : undefined;
737
-
738
- if (nextPagination) {
739
- nextFetchDelayRef.current = 0;
740
- dispatch({ type: "SET_PAGINATION", payload: nextPagination });
741
- onPaginationChangeRef.current?.(nextPagination);
742
- }
743
- const paginationChanged = !!nextPagination &&
744
- (nextPagination.pageIndex !== current.pageIndex || nextPagination.pageSize !== current.pageSize);
745
-
746
- if (!paginationChanged) {
747
- await fetchData({}, { delay: 0, meta: { reason: n.reason, force: n.force } });
748
- }
749
- },
750
- [enablePagination, fetchData, normalizeRefreshOptions, onPaginationChangeRef, uiRef]
751
- );
752
-
753
- const getResetPayload = useCallback((): Partial<EngineUIState> => {
754
- const resetSorting = initialStateConfig.sorting || [];
755
- const resetGlobalFilter = initialStateConfig.globalFilter ?? "";
756
- const resetColumnFilter = (initialStateConfig.columnFilter ?? DEFAULT_INITIAL_STATE.columnFilter) as ColumnFilterState;
757
-
758
- const resetPagination = enablePagination
759
- ? (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 })
760
- : uiRef.current.pagination;
761
-
762
- return {
763
- sorting: resetSorting,
764
- globalFilter: resetGlobalFilter,
765
- columnFilter: resetColumnFilter,
766
- ...(enablePagination ? { pagination: resetPagination } : {}),
767
- selectionState: initialStateConfig.selectionState ?? DEFAULT_INITIAL_STATE.selectionState,
768
- expanded: {},
769
- columnVisibility: initialStateConfig.columnVisibility || {},
770
- columnSizing: initialStateConfig.columnSizing || {},
771
- columnOrder: initialStateConfig.columnOrder || [],
772
- columnPinning: initialStateConfig.columnPinning || { left: [], right: [] },
773
- };
774
- }, [enablePagination, initialStateConfig]);
775
-
776
- const resetAllAndReload = useCallback(() => {
777
- const payload = getResetPayload();
778
- dispatch({ type: "RESET_ALL", payload });
779
- void fetchData(
780
- {
781
- sorting: payload.sorting as any,
782
- globalFilter: payload.globalFilter as any,
783
- columnFilter: payload.columnFilter as any,
784
- ...(enablePagination ? { pagination: payload.pagination as any } : {}),
785
- },
786
- { delay: 0, meta: { reason: "reset", force: true } }
787
- );
788
- }, [enablePagination, fetchData, getResetPayload]);
789
-
790
- // --- export (refs + small UI state)
791
- const setExportControllerSafely = useCallback(
792
- (value: AbortController | null | ((current: AbortController | null) => AbortController | null)) => {
793
- setExportController((current) => {
794
- const next = typeof value === "function" ? (value as any)(current) : value;
795
- exportControllerRef.current = next;
796
- return next;
797
- });
798
- },
799
- []
800
- );
801
-
802
- const handleExportProgressInternal = useCallback((p: ExportProgressPayload) => {
803
- setExportProgress(p || {});
804
- onExportProgressRef.current?.(p);
805
- }, []);
806
-
807
- const handleExportStateChangeInternal = useCallback((s: ExportStateChange) => {
808
- setExportPhase(s.phase);
809
- if (s.processedRows !== undefined || s.totalRows !== undefined || s.percentage !== undefined) {
810
- setExportProgress({
811
- processedRows: s.processedRows,
812
- totalRows: s.totalRows,
813
- percentage: s.percentage,
814
- });
815
- }
816
- onExportStateChangeRef.current?.(s);
817
- }, []);
818
-
819
- const runExportWithPolicy = useCallback(
820
- async (options: { format: "csv" | "excel"; filename: string; mode: "client" | "server"; execute: (controller: AbortController) => Promise<void> }) => {
821
- const { format, filename, mode, execute } = options;
822
-
823
- const startExecution = async () => {
824
- const controller = new AbortController();
825
- setExportProgress({});
826
- setExportControllerSafely(controller);
827
- try {
828
- await execute(controller);
829
- } finally {
830
- setExportControllerSafely((cur) => (cur === controller ? null : cur));
831
- }
832
- };
833
-
834
- if (exportConcurrency === "queue") {
835
- setQueuedExportCount((p) => p + 1);
836
- const runQueued = async () => {
837
- setQueuedExportCount((p) => Math.max(0, p - 1));
838
- await startExecution();
839
- };
840
- const queuedPromise = exportQueueRef.current.catch(() => undefined).then(runQueued);
841
- exportQueueRef.current = queuedPromise;
842
- return queuedPromise;
843
- }
844
-
845
- const active = exportControllerRef.current;
846
- if (active) {
847
- if (exportConcurrency === "ignoreIfRunning") {
848
- handleExportStateChangeInternal({
849
- phase: "error",
850
- mode,
851
- format,
852
- filename,
853
- message: "An export is already running",
854
- code: "EXPORT_IN_PROGRESS",
855
- endedAt: Date.now(),
856
- } as any);
857
- onExportErrorRef.current?.({ message: "An export is already running", code: "EXPORT_IN_PROGRESS" });
858
- return;
859
- }
860
- if (exportConcurrency === "cancelAndRestart") active.abort();
861
- }
862
-
863
- await startExecution();
864
- },
865
- [exportConcurrency, handleExportStateChangeInternal, onExportErrorRef, setExportControllerSafely]
866
- );
867
-
868
- const handleCancelExport = useCallback(() => {
869
- const active = exportControllerRef.current;
870
- if (!active) return;
871
- active.abort();
872
- setExportControllerSafely((cur) => (cur === active ? null : cur));
873
- onExportCancelRef.current?.();
874
- }, [onExportCancelRef, setExportControllerSafely]);
875
-
876
- const isExporting = exportController !== null;
877
-
878
- // --- stable API (created once)
879
- if (!apiRef.current) {
880
- apiRef.current = {} as DataTableApi<T>;
881
-
882
- // IMPORTANT: do NOT capture `table/ui/data` here. Always read from refs inside methods.
883
- (apiRef.current as any).table = { getTable: () => table }; // will be updated below via tableRef
884
- // We'll overwrite getTable below with a ref-backed function.
885
- }
886
-
887
- // table ref so API always returns latest table instance
888
- const tableRef = useLatestRef(table);
889
-
890
-
891
- useEffect(() => {
892
- const api = apiRef.current!;
893
- api.table = { getTable: () => tableRef.current } as any;
894
-
895
- // --- state getters
896
- api.state = {
897
- getTableState: () => tableRef.current.getState(),
898
- getCurrentFilters: () => tableRef.current.getState().columnFilter,
899
- getCurrentSorting: () => tableRef.current.getState().sorting,
900
- getCurrentPagination: () => tableRef.current.getState().pagination,
901
- getCurrentSelection: () => uiRef.current.selectionState,
902
- getGlobalFilter: () => tableRef.current.getState().globalFilter,
903
- };
904
-
905
- // --- data
906
- const getBaseData = () => {
907
- const sData = serverDataRef.current;
908
- return sData !== null ? sData : dataRef.current;
909
- };
910
- const getRowIndexById = (arr: T[], rowId: string) =>
911
- arr.findIndex((row, i) => generateRowId(row, i, idKey) === rowId);
912
- api.data = {
913
- refresh: (options?: boolean | DataRefreshOptions) => void triggerRefresh(options, "refresh"),
914
- reload: (options: DataRefreshOptions = {}) => void triggerRefresh({ ...options, reason: options.reason ?? "reload" }, "reload"),
915
- resetAll: () => resetAllAndReload(),
916
- getAllData: () => [...getBaseData()],
917
- getRowData: (rowId: string) => {
918
- const rows = tableRef.current.getRowModel().rows;
919
- const row = rows.find((r: any) => r.id === rowId);
920
- return row?.original as T | undefined;
921
- },
922
- getRowByIndex: (index: number) => tableRef.current.getRowModel().rows[index]?.original as T | undefined,
923
- getDataCount: () => getBaseData().length,
924
- getFilteredDataCount: () => tableRef.current.getFilteredRowModel().rows.length,
925
- updateRow: (rowId: string, updates: Partial<T>) => {
926
- const base = getBaseData();
927
- const idx = getRowIndexById(base, rowId);
928
- if (idx === -1) return;
929
- const next = [...base];
930
- next[idx] = { ...next[idx], ...updates } as T;
931
- setServerData(next);
932
- setServerTotal(next.length);
933
- },
934
- updateRowByIndex: (index: number, updates: Partial<T>) => {
935
- const base = getBaseData();
936
- if (index < 0 || index >= base.length) return;
937
- const next = [...base];
938
- next[index] = { ...next[index], ...updates } as T;
939
- setServerData(next);
940
- setServerTotal(next.length);
941
- },
942
- insertRow: (newRow: T, index?: number) => {
943
- const base = getBaseData();
944
- const next = index == null ? [...base, newRow] : [...base.slice(0, index), newRow, ...base.slice(index)];
945
- setServerData(next);
946
- setServerTotal(next.length);
947
- },
948
- deleteRow: (rowId: string) => {
949
- const base = getBaseData();
950
- const idx = getRowIndexById(base, rowId);
951
- if (idx === -1) return;
952
- const next = base.filter((_, i) => i !== idx);
953
- setServerData(next);
954
- setServerTotal(next.length);
955
- },
956
- deleteRowByIndex: (index: number) => {
957
- const base = getBaseData();
958
- if (index < 0 || index >= base.length) return;
959
- const next = base.filter((_, i) => i !== index);
960
- setServerData(next);
961
- setServerTotal(next.length);
962
- },
963
- deleteSelectedRows: () => {
964
- const state = tableRef.current.getSelectionState?.();
965
- if (!state || state.type !== "include" || !state.ids.length) return;
966
- const base = getBaseData();
967
- const ids = new Set(state.ids);
968
- const next = base.filter((row, i) => !ids.has(generateRowId(row, i, idKey)));
969
- setServerData(next);
970
- setServerTotal(next.length);
971
- tableRef.current.deselectAll?.();
972
- },
973
- replaceAllData: (newData: T[]) => {
974
- setServerData(newData);
975
- setServerTotal(newData.length);
976
- },
977
- updateMultipleRows: (updates: Array<{ rowId: string; data: Partial<T> }>) => {
978
- const base = getBaseData();
979
- const next = [...base];
980
- for (const { rowId, data: u } of updates) {
981
- const idx = getRowIndexById(next, rowId);
982
- if (idx !== -1) next[idx] = { ...next[idx], ...u } as T;
983
- }
984
- setServerData(next);
985
- setServerTotal(next.length);
986
- },
987
- insertMultipleRows: (newRows: T[], startIndex?: number) => {
988
- const base = getBaseData();
989
- const idx = startIndex ?? base.length;
990
- const next = [...base.slice(0, idx), ...newRows, ...base.slice(idx)];
991
- setServerData(next);
992
- setServerTotal(next.length);
993
- },
994
- deleteMultipleRows: (rowIds: string[]) => {
995
- const ids = new Set(rowIds);
996
- const base = getBaseData();
997
- const next = base.filter((row, i) => !ids.has(generateRowId(row, i, idKey)));
998
- setServerData(next);
999
- setServerTotal(next.length);
1000
- },
1001
- updateField: (rowId: string, fieldName: keyof T, value: any) => {
1002
- api.data.updateRow(rowId, { [fieldName]: value } as Partial<T>);
1003
- },
1004
- updateFieldByIndex: (index: number, fieldName: keyof T, value: any) => {
1005
- api.data.updateRowByIndex(index, { [fieldName]: value } as Partial<T>);
1006
- },
1007
- findRows: (predicate: (row: T) => boolean) => getBaseData().filter(predicate),
1008
- findRowIndex: (predicate: (row: T) => boolean) => getBaseData().findIndex(predicate),
1009
- } as any;
1010
-
1011
- // --- layout (save/restore column visibility, order, sizing, pinning)
1012
- api.layout = {
1013
- saveLayout: () => {
1014
- const s = tableRef.current.getState();
1015
- return {
1016
- columnVisibility: s.columnVisibility ?? {},
1017
- columnOrder: s.columnOrder ?? [],
1018
- columnSizing: s.columnSizing ?? {},
1019
- columnPinning: s.columnPinning ?? { left: [], right: [] },
1020
- pagination: s.pagination ?? { pageIndex: 0, pageSize: 10 },
1021
- globalFilter: s.globalFilter ?? "",
1022
- columnFilter: s.columnFilter ?? [],
1023
- };
1024
- },
1025
- restoreLayout: (layout: any) => {
1026
-
1027
- if (layout.columnVisibility) dispatch({ type: "SET_COLUMN_VISIBILITY", payload: layout.columnVisibility });
1028
- if (layout.columnOrder) { dispatch({ type: "SET_COLUMN_ORDER", payload: layout.columnOrder }); onColumnDragEndRef.current?.(layout.columnOrder); }
1029
- if (layout.columnSizing) { dispatch({ type: "SET_COLUMN_SIZING", payload: layout.columnSizing }); onColumnSizingChangeRef.current?.(layout.columnSizing); }
1030
- if (layout.columnPinning) { dispatch({ type: "SET_COLUMN_PINNING", payload: layout.columnPinning }); onColumnPinningChangeRef.current?.(layout.columnPinning); }
1031
- if (layout.pagination && enablePagination) dispatch({ type: "SET_PAGINATION", payload: layout.pagination as any });
1032
- if (layout.globalFilter !== undefined) dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: layout.globalFilter });
1033
- if (layout.columnFilter) dispatch({ type: "SET_COLUMN_FILTER", payload: layout.columnFilter as any });
1034
- },
1035
- resetLayout: () => {
1036
- const vis = initialStateConfig.columnVisibility || {};
1037
- const order = initialStateConfig.columnOrder || [];
1038
- const sizing = initialStateConfig.columnSizing || {};
1039
- const pinning = initialStateConfig.columnPinning || { left: [] as string[], right: [] as string[] };
1040
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: vis });
1041
- dispatch({ type: "SET_COLUMN_ORDER", payload: order });
1042
- dispatch({ type: "SET_COLUMN_SIZING", payload: sizing });
1043
- dispatch({ type: "SET_COLUMN_PINNING", payload: pinning });
1044
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: "" });
1045
- dispatch({ type: "SET_COLUMN_FILTER", payload: initialStateConfig.columnFilter || [] });
1046
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: initialStateConfig.sorting || [] });
1047
- dispatch({ type: "SET_PAGINATION", payload: initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 } });
1048
- },
1049
- resetAll: () => {
1050
- api.layout.resetLayout();
1051
- resetAllAndReload();
1052
- },
1053
- } as any;
1054
-
1055
- // --- sorting/pagination/filtering - dispatch + callbacks + server fetch policies
1056
- api.sorting = {
1057
- setSorting: (next: SortingState) => {
1058
- const cleaned = (next || []).filter((s: any) => s?.id);
1059
- onSortingChangeRef.current?.(cleaned);
1060
- nextFetchDelayRef.current = 0;
1061
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
1062
- },
1063
- sortColumn: (columnId: string, direction: 'asc' | 'desc' | false) => {
1064
- const next: SortingState = direction === false ? [] : [{ id: columnId, desc: direction === 'desc' }];
1065
- onSortingChangeRef.current?.(next);
1066
- nextFetchDelayRef.current = 0;
1067
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: next });
1068
- },
1069
- clearSorting: () => {
1070
- onSortingChangeRef.current?.([]);
1071
- nextFetchDelayRef.current = 0;
1072
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: [] });
1073
- },
1074
- resetSorting: () => {
1075
- const next = (initialStateConfig.sorting || []) as SortingState;
1076
- onSortingChangeRef.current?.(next);
1077
- nextFetchDelayRef.current = 0;
1078
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: next });
1079
- },
1080
- } as any;
1081
-
1082
- api.pagination = {
1083
- goToPage: (pageIndex: number) => {
1084
- const prev = uiRef.current.pagination;
1085
- const next = { ...prev, pageIndex };
1086
- onPaginationChangeRef.current?.(next);
1087
- nextFetchDelayRef.current = 0;
1088
- dispatch({ type: "SET_PAGINATION", payload: next });
1089
- },
1090
- nextPage: () => {
1091
- const prev = uiRef.current.pagination;
1092
- api.pagination.goToPage(Math.min(prev.pageIndex + 1, Math.max(0, Math.ceil((tableTotalRow ?? 0) / prev.pageSize) - 1)));
1093
- },
1094
- previousPage: () => {
1095
- const prev = uiRef.current.pagination;
1096
- api.pagination.goToPage(Math.max(0, prev.pageIndex - 1));
1097
- },
1098
- setPageSize: (pageSize: number) => {
1099
- const next = { pageIndex: 0, pageSize };
1100
- onPaginationChangeRef.current?.(next);
1101
- nextFetchDelayRef.current = 0;
1102
- dispatch({ type: "SET_PAGINATION", payload: next });
1103
- },
1104
- goToFirstPage: () => api.pagination.goToPage(0),
1105
- goToLastPage: () => {
1106
- const prev = uiRef.current.pagination;
1107
- api.pagination.goToPage(Math.max(0, Math.ceil((tableTotalRow ?? 0) / prev.pageSize) - 1));
1108
- },
1109
- resetPagination: () => {
1110
- const next = (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 }) as any;
1111
- onPaginationChangeRef.current?.(next);
1112
- nextFetchDelayRef.current = 0;
1113
- dispatch({ type: "SET_PAGINATION", payload: next });
1114
- },
1115
- } as any;
1116
-
1117
- api.filtering = {
1118
- setGlobalFilter: (filter: string) => {
1119
- onGlobalFilterChangeRef.current?.(filter);
1120
- nextFetchDelayRef.current = 400;
1121
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: filter });
1122
- },
1123
- clearGlobalFilter: () => {
1124
- onGlobalFilterChangeRef.current?.("");
1125
- nextFetchDelayRef.current = 400;
1126
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: "" });
1127
- },
1128
- setColumnFilters: (filters: ColumnFilterState, isApply = false) => handleColumnFilterChangeHandler(filters, isApply),
1129
- addColumnFilter: (columnId: string, operator: string, value: any) => tableRef.current.addColumnFilter?.(columnId, operator, value),
1130
- removeColumnFilter: (filterId: string) => tableRef.current.removeColumnFilter?.(filterId),
1131
- clearAllFilters: () => tableRef.current.resetColumnFilter?.(),
1132
- resetFilters: () => {
1133
- const reset = (initialStateConfig.columnFilter ?? DEFAULT_INITIAL_STATE.columnFilter) as ColumnFilterState;
1134
- handleColumnFilterChangeHandler(reset, true);
1135
- },
1136
- } as any;
1137
-
1138
- api.columnVisibility = {
1139
- showColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: true } }),
1140
- hideColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: false } }),
1141
- toggleColumn: (id: string) => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: { ...uiRef.current.columnVisibility, [id]: !uiRef.current.columnVisibility[id] } }),
1142
- showAllColumns: () => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: {} }),
1143
- hideAllColumns: () => {
1144
- const all = tableRef.current.getAllLeafColumns().reduce((acc, col) => ({ ...acc, [col.id]: false }), {} as Record<string, boolean>);
1145
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: all });
1146
- },
1147
- resetColumnVisibility: () => dispatch({ type: "SET_COLUMN_VISIBILITY", payload: initialStateConfig.columnVisibility || {} }),
1148
- } as any;
1149
-
1150
- api.columnOrdering = {
1151
- setColumnOrder: (next: ColumnOrderState) => {
1152
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1153
- onColumnDragEndRef.current?.(next);
1154
- },
1155
- moveColumn: (columnId: string, toIndex: number) => {
1156
- const order = uiRef.current.columnOrder.length ? uiRef.current.columnOrder : tableRef.current.getAllLeafColumns().map((c: any) => c.id);
1157
- const from = order.indexOf(columnId);
1158
- if (from === -1 || toIndex < 0 || toIndex >= order.length) return;
1159
- const next = [...order];
1160
- next.splice(from, 1);
1161
- next.splice(toIndex, 0, columnId);
1162
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1163
- onColumnDragEndRef.current?.(next);
1164
- },
1165
- resetColumnOrder: () => dispatch({ type: "SET_COLUMN_ORDER", payload: initialStateConfig.columnOrder || [] }),
1166
- } as any;
1167
-
1168
- api.columnPinning = {
1169
- pinColumnLeft: (columnId: string) => {
1170
- const cur = uiRef.current.columnPinning;
1171
- const left = cur.left.includes(columnId) ? cur.left : [...cur.left.filter((id: string) => id !== columnId), columnId];
1172
- const right = cur.right.filter((id: string) => id !== columnId);
1173
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1174
- onColumnPinningChangeRef.current?.({ left, right });
1175
- },
1176
- pinColumnRight: (columnId: string) => {
1177
- const cur = uiRef.current.columnPinning;
1178
- const left = cur.left.filter((id: string) => id !== columnId);
1179
- const right = cur.right.includes(columnId) ? cur.right : [...cur.right.filter((id: string) => id !== columnId), columnId];
1180
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1181
- onColumnPinningChangeRef.current?.({ left, right });
1182
- },
1183
- unpinColumn: (columnId: string) => {
1184
- const cur = uiRef.current.columnPinning;
1185
- const left = cur.left.filter((id: string) => id !== columnId);
1186
- const right = cur.right.filter((id: string) => id !== columnId);
1187
- dispatch({ type: "SET_COLUMN_PINNING", payload: { left, right } });
1188
- onColumnPinningChangeRef.current?.({ left, right });
1189
- },
1190
- setPinning: (next: ColumnPinningState) => {
1191
- dispatch({ type: "SET_COLUMN_PINNING", payload: next });
1192
- onColumnPinningChangeRef.current?.(next);
1193
- },
1194
- resetColumnPinning: () => dispatch({ type: "SET_COLUMN_PINNING", payload: initialStateConfig.columnPinning || { left: [], right: [] } }),
1195
- } as any;
1196
-
1197
- api.columnResizing = {
1198
- resizeColumn: (columnId: string, width: number) => {
1199
- const cur = uiRef.current.columnSizing;
1200
- dispatch({ type: "SET_COLUMN_SIZING", payload: { ...cur, [columnId]: width } });
1201
- onColumnSizingChangeRef.current?.({ ...cur, [columnId]: width });
1202
- },
1203
- autoSizeColumn: (columnId: string) => { void columnId; /* no-op: would require measure; use reset for default */ },
1204
- autoSizeAllColumns: () => { /* no-op */ },
1205
- resetColumnSizing: () => dispatch({ type: "SET_COLUMN_SIZING", payload: initialStateConfig.columnSizing || {} }),
1206
- } as any;
1207
-
1208
- api.selection = {
1209
- selectRow: (rowId: string) => tableRef.current.selectRow?.(rowId),
1210
- deselectRow: (rowId: string) => tableRef.current.deselectRow?.(rowId),
1211
- toggleRowSelection: (rowId: string) => tableRef.current.toggleRowSelected?.(rowId),
1212
- selectAll: () => tableRef.current.selectAll?.(),
1213
- deselectAll: () => tableRef.current.deselectAll?.(),
1214
- toggleSelectAll: () => tableRef.current.toggleAllRowsSelected?.(),
1215
- getSelectionState: () => tableRef.current.getSelectionState?.() || ({ ids: [], type: "include" } as const),
1216
- getSelectedRows: () => tableRef.current.getSelectedRows(),
1217
- getSelectedCount: () => tableRef.current.getSelectedCount(),
1218
- isRowSelected: (rowId: string) => tableRef.current.getIsRowSelected(rowId) || false,
1219
- } as any;
1220
-
1221
- // --- export API (use your existing exportClientData/exportServerData)
1222
- api.export = {
1223
- exportCSV: async (options: DataTableExportApiOptions = {}) => {
1224
- const fn = options.filename ?? exportFilename;
1225
- const chunkSize = options.chunkSize ?? exportChunkSize;
1226
- const strictTotalCheck = options.strictTotalCheck ?? exportStrictTotalCheck;
1227
- const sanitizeCSV = options.sanitizeCSV ?? exportSanitizeCSV;
1228
-
1229
- const mode: "client" | "server" = dataMode === "server" && !!onServerExportRef.current ? "server" : "client";
1230
-
1231
- await runExportWithPolicy({
1232
- format: "csv",
1233
- filename: fn,
1234
- mode,
1235
- execute: async (controller) => {
1236
- // TODO: keep your state-change event mapping (starting/progress/completed/cancel/error)
1237
- if (mode === "server" && onServerExportRef.current) {
1238
- await exportServerData(tableRef.current, {
1239
- format: "csv",
1240
- filename: fn,
1241
- fetchData: (filters: any, selection: any, signal?: AbortSignal) =>
1242
- onServerExportRef.current?.(filters, selection, signal),
1243
- currentFilters: {
1244
- globalFilter: tableRef.current.getState().globalFilter,
1245
- columnFilter: tableRef.current.getState().columnFilter,
1246
- sorting: tableRef.current.getState().sorting,
1247
- pagination: tableRef.current.getState().pagination,
1248
- },
1249
- selection: tableRef.current.getSelectionState?.(),
1250
- onProgress: handleExportProgressInternal,
1251
- onComplete: onExportCompleteRef.current,
1252
- onError: onExportErrorRef.current,
1253
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1254
- signal: controller.signal,
1255
- chunkSize,
1256
- strictTotalCheck,
1257
- sanitizeCSV,
1258
- });
1259
- return;
1260
- }
1261
-
1262
- await exportClientData(tableRef.current, {
1263
- format: "csv",
1264
- filename: fn,
1265
- onProgress: handleExportProgressInternal,
1266
- onComplete: onExportCompleteRef.current,
1267
- onError: onExportErrorRef.current,
1268
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1269
- signal: controller.signal,
1270
- sanitizeCSV,
1271
- });
1272
- },
1273
- });
1274
- },
1275
-
1276
- exportExcel: async (options: DataTableExportApiOptions = {}) => {
1277
- const fn = options.filename ?? exportFilename;
1278
- const chunkSize = options.chunkSize ?? exportChunkSize;
1279
- const strictTotalCheck = options.strictTotalCheck ?? exportStrictTotalCheck;
1280
- const sanitizeCSV = options.sanitizeCSV ?? exportSanitizeCSV;
1281
-
1282
- const mode: "client" | "server" = dataMode === "server" && !!onServerExportRef.current ? "server" : "client";
1283
-
1284
- await runExportWithPolicy({
1285
- format: "excel",
1286
- filename: fn,
1287
- mode,
1288
- execute: async (controller) => {
1289
- // TODO: keep your state-change event mapping (starting/progress/completed/cancel/error)
1290
- if (mode === "server" && onServerExportRef.current) {
1291
- await exportServerData(tableRef.current, {
1292
- format: "excel",
1293
- filename: fn,
1294
- fetchData: (filters: any, selection: any, signal?: AbortSignal) =>
1295
- onServerExportRef.current?.(filters, selection, signal),
1296
- currentFilters: {
1297
- globalFilter: tableRef.current.getState().globalFilter,
1298
- columnFilter: tableRef.current.getState().columnFilter,
1299
- sorting: tableRef.current.getState().sorting,
1300
- pagination: tableRef.current.getState().pagination,
1301
- },
1302
- selection: tableRef.current.getSelectionState?.(),
1303
- onProgress: handleExportProgressInternal,
1304
- onComplete: onExportCompleteRef.current,
1305
- onError: onExportErrorRef.current,
1306
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1307
- signal: controller.signal,
1308
- chunkSize,
1309
- strictTotalCheck,
1310
- sanitizeCSV,
1311
- });
1312
- return;
1313
- }
1314
-
1315
- await exportClientData(tableRef.current, {
1316
- format: "excel",
1317
- filename: fn,
1318
- onProgress: handleExportProgressInternal,
1319
- onComplete: onExportCompleteRef.current,
1320
- onError: onExportErrorRef.current,
1321
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode, format: "csv", filename: fn, queueLength: queuedExportCount } as any),
1322
- signal: controller.signal,
1323
- sanitizeCSV,
1324
- });
1325
- },
1326
- });
1327
- },
1328
-
1329
- 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 }) => {
1330
- const { format, filename: fn = exportFilename, fetchData: customFetchData, chunkSize: cs = exportChunkSize, strictTotalCheck: st = exportStrictTotalCheck, sanitizeCSV: san = exportSanitizeCSV } = options;
1331
- await runExportWithPolicy({
1332
- format,
1333
- filename: fn,
1334
- mode: "server",
1335
- execute: async (controller) => {
1336
- await exportServerData(tableRef.current, {
1337
- format,
1338
- filename: fn,
1339
- fetchData: customFetchData,
1340
- currentFilters: { globalFilter: tableRef.current.getState().globalFilter, columnFilter: tableRef.current.getState().columnFilter, sorting: tableRef.current.getState().sorting, pagination: tableRef.current.getState().pagination },
1341
- selection: tableRef.current.getSelectionState?.(),
1342
- onProgress: handleExportProgressInternal,
1343
- onComplete: onExportCompleteRef.current,
1344
- onError: onExportErrorRef.current,
1345
- onStateChange: (s: any) => handleExportStateChangeInternal({ ...s, mode: "server", format, filename: fn, queueLength: queuedExportCount } as any),
1346
- signal: controller.signal,
1347
- chunkSize: cs,
1348
- strictTotalCheck: st,
1349
- sanitizeCSV: san,
1350
- });
1351
- },
1352
- });
1353
- },
1354
- isExporting: () => exportControllerRef.current != null,
1355
- cancelExport: () => handleCancelExport(),
1356
- } as any;
1357
- }, [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, enablePagination]);
1358
-
1359
- // --- imperative handlers (used by TanStack callbacks above or view)
1360
- const handleSortingChange = useCallback(
1361
- (updaterOrValue: Updater<SortingState>) => {
1362
- const prev = uiRef.current.sorting;
1363
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
1364
- const cleaned = (next || []).filter((s: any) => s?.id);
1365
- onSortingChangeRef.current?.(cleaned);
1366
- nextFetchDelayRef.current = 0;
1367
- dispatch({ type: "SET_SORTING_RESET_PAGE", payload: cleaned });
1368
- },
1369
- [onSortingChangeRef, uiRef]
1370
- );
1371
-
1372
- const handlePaginationChange = useCallback(
1373
- (updater: Updater<PaginationState>) => {
1374
- const prev = uiRef.current.pagination;
1375
- const next = typeof updater === "function" ? updater(prev) : updater;
1376
- onPaginationChangeRef.current?.(next);
1377
- nextFetchDelayRef.current = 0;
1378
- dispatch({ type: "SET_PAGINATION", payload: next });
1379
- },
1380
- [onPaginationChangeRef, uiRef]
1381
- );
1382
-
1383
- const handleGlobalFilterChange = useCallback(
1384
- (updaterOrValue: Updater<string>) => {
1385
- const prev = uiRef.current.globalFilter;
1386
- const next = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
1387
- onGlobalFilterChangeRef.current?.(next);
1388
- nextFetchDelayRef.current = 400;
1389
- dispatch({ type: "SET_GLOBAL_FILTER_RESET_PAGE", payload: next });
1390
- },
1391
- [onGlobalFilterChangeRef, uiRef]
1392
- );
1393
-
1394
- const handleColumnOrderChange = useCallback(
1395
- (updated: Updater<ColumnOrderState>) => {
1396
- const prev = uiRef.current.columnOrder;
1397
- const next = typeof updated === "function" ? updated(prev) : updated;
1398
- dispatch({ type: "SET_COLUMN_ORDER", payload: next });
1399
- onColumnDragEndRef.current?.(next);
1400
- },
1401
- [onColumnDragEndRef, uiRef]
1402
- );
1403
-
1404
- const handleColumnPinningChange = useCallback(
1405
- (updated: Updater<ColumnPinningState>) => {
1406
- const prev = uiRef.current.columnPinning;
1407
- const next = typeof updated === "function" ? updated(prev) : updated;
1408
- dispatch({ type: "SET_COLUMN_PINNING", payload: next });
1409
- onColumnPinningChangeRef.current?.(next);
1410
- },
1411
- [onColumnPinningChangeRef, uiRef]
1412
- );
1413
-
1414
- const handleColumnVisibilityChange = useCallback(
1415
- (updated: any) => {
1416
- const prev = uiRef.current.columnVisibility;
1417
- const next = typeof updated === "function" ? updated(prev) : updated;
1418
- dispatch({ type: "SET_COLUMN_VISIBILITY", payload: next });
1419
- onColumnVisibilityChangeRef.current?.(next);
1420
- },
1421
- [onColumnVisibilityChangeRef, uiRef]
1422
- );
1423
-
1424
- const handleColumnSizingChange = useCallback(
1425
- (updated: any) => {
1426
- const prev = uiRef.current.columnSizing;
1427
- const next = typeof updated === "function" ? updated(prev) : updated;
1428
- dispatch({ type: "SET_COLUMN_SIZING", payload: next });
1429
- onColumnSizingChangeRef.current?.(next);
1430
- },
1431
- [onColumnSizingChangeRef, uiRef]
1432
- );
1433
-
1434
- const handleColumnReorder = useCallback(
1435
- (draggedId: string, targetId: string) => {
1436
- const currentOrder =
1437
- uiRef.current.columnOrder.length > 0
1438
- ? uiRef.current.columnOrder
1439
- : enhancedColumns.map((c: any, idx: number) => c.id ?? c.accessorKey ?? `column_${idx}`);
1440
-
1441
- const from = currentOrder.indexOf(draggedId);
1442
- const to = currentOrder.indexOf(targetId);
1443
- if (from === -1 || to === -1) return;
1444
-
1445
- const next = [...currentOrder];
1446
- next.splice(from, 1);
1447
- next.splice(to, 0, draggedId);
1448
-
1449
- handleColumnOrderChange(next);
1450
- },
1451
- [enhancedColumns, handleColumnOrderChange, uiRef]
1452
- );
1453
-
1454
- // optional: selection callback
1455
- useEffect(() => {
1456
- onSelectionChangeRef.current?.(ui.selectionState);
1457
- }, [onSelectionChangeRef, ui.selectionState]);
1458
-
1459
-
1460
-
1461
- // --- provider props
1462
- const providerProps = useMemo(
1463
- () => ({
1464
- table,
1465
- apiRef,
1466
- dataMode,
1467
- tableSize: ui.tableSize,
1468
- onTableSizeChange: (size: DataTableSize) => dispatch({ type: "SET_TABLE_SIZE", payload: size }),
1469
- columnFilter: ui.columnFilter,
1470
- onChangeColumnFilter: (f: ColumnFilterState) => handleColumnFilterChangeHandler(f, false),
1471
- slots,
1472
- slotProps,
1473
- isExporting,
1474
- exportController,
1475
- exportPhase,
1476
- exportProgress,
1477
- onCancelExport: handleCancelExport,
1478
- exportFilename,
1479
- onExportProgress,
1480
- onExportComplete,
1481
- onExportError,
1482
- onServerExport,
1483
- }),
1484
- [
1485
- table,
1486
- dataMode,
1487
- ui.tableSize,
1488
- ui.columnFilter,
1489
- slots,
1490
- slotProps,
1491
- isExporting,
1492
- exportController,
1493
- exportPhase,
1494
- exportProgress,
1495
- handleCancelExport,
1496
- exportFilename,
1497
- onExportProgress,
1498
- onExportComplete,
1499
- onExportError,
1500
- onServerExport,
1501
- handleColumnFilterChangeHandler,
1502
- ]
1503
- );
1504
-
1505
-
1506
- return {
1507
- table,
1508
- refs: {
1509
- tableContainerRef,
1510
- apiRef,
1511
- exportControllerRef,
1512
- },
1513
- derived: {
1514
- isServerMode,
1515
- isServerPagination,
1516
- isServerFiltering,
1517
- isServerSorting,
1518
- tableData,
1519
- tableTotalRow,
1520
- tableLoading,
1521
- rows,
1522
- visibleLeafColumns: table.getVisibleLeafColumns,
1523
- useFixedLayout,
1524
- tableStyle,
1525
- isExporting,
1526
- exportPhase,
1527
- exportProgress,
1528
- isSomeRowsSelected,
1529
- selectedRowCount,
1530
- },
1531
- state: ui,
1532
- actions: {
1533
- fetchData,
1534
- handleSortingChange,
1535
- handlePaginationChange,
1536
- handleGlobalFilterChange,
1537
- handleColumnFilterChangeHandler,
1538
- handleColumnOrderChange,
1539
- handleColumnPinningChange,
1540
- handleColumnVisibilityChange,
1541
- handleColumnSizingChange,
1542
- handleColumnReorder,
1543
- resetAllAndReload,
1544
- triggerRefresh,
1545
- setTableSize: (size: DataTableSize) => dispatch({ type: "SET_TABLE_SIZE", payload: size }),
1546
- handleCancelExport,
1547
- renderRowModel: { rowVirtualizer },
1548
- },
1549
- api: apiRef.current!,
1550
- providerProps,
1551
- };
1552
- }