@ackplus/react-tanstack-data-table 1.1.11 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +143 -11
  2. package/dist/lib/components/droupdown/menu-dropdown.d.ts.map +1 -1
  3. package/dist/lib/components/droupdown/menu-dropdown.js +8 -1
  4. package/dist/lib/components/filters/filter-value-input.js +2 -2
  5. package/dist/lib/components/pagination/data-table-pagination.d.ts.map +1 -1
  6. package/dist/lib/components/pagination/data-table-pagination.js +10 -1
  7. package/dist/lib/components/toolbar/data-table-toolbar.d.ts.map +1 -1
  8. package/dist/lib/components/toolbar/data-table-toolbar.js +5 -2
  9. package/dist/lib/components/toolbar/table-export-control.d.ts.map +1 -1
  10. package/dist/lib/components/toolbar/table-export-control.js +46 -12
  11. package/dist/lib/components/toolbar/table-refresh-control.d.ts +15 -0
  12. package/dist/lib/components/toolbar/table-refresh-control.d.ts.map +1 -0
  13. package/dist/lib/components/toolbar/table-refresh-control.js +61 -0
  14. package/dist/lib/contexts/data-table-context.d.ts +7 -10
  15. package/dist/lib/contexts/data-table-context.d.ts.map +1 -1
  16. package/dist/lib/contexts/data-table-context.js +5 -1
  17. package/dist/lib/data-table.d.ts.map +1 -1
  18. package/dist/lib/data-table.js +1110 -946
  19. package/dist/lib/features/column-filter.feature.js +38 -21
  20. package/dist/lib/features/selection.feature.d.ts.map +1 -1
  21. package/dist/lib/features/selection.feature.js +11 -3
  22. package/dist/lib/types/column.types.d.ts +19 -0
  23. package/dist/lib/types/column.types.d.ts.map +1 -1
  24. package/dist/lib/types/data-table-api.d.ts +25 -18
  25. package/dist/lib/types/data-table-api.d.ts.map +1 -1
  26. package/dist/lib/types/data-table.types.d.ts +37 -10
  27. package/dist/lib/types/data-table.types.d.ts.map +1 -1
  28. package/dist/lib/types/export.types.d.ts +57 -13
  29. package/dist/lib/types/export.types.d.ts.map +1 -1
  30. package/dist/lib/types/slots.types.d.ts +12 -1
  31. package/dist/lib/types/slots.types.d.ts.map +1 -1
  32. package/dist/lib/types/table.types.d.ts +1 -3
  33. package/dist/lib/types/table.types.d.ts.map +1 -1
  34. package/dist/lib/utils/debounced-fetch.utils.d.ts +8 -4
  35. package/dist/lib/utils/debounced-fetch.utils.d.ts.map +1 -1
  36. package/dist/lib/utils/debounced-fetch.utils.js +63 -14
  37. package/dist/lib/utils/export-utils.d.ts +14 -4
  38. package/dist/lib/utils/export-utils.d.ts.map +1 -1
  39. package/dist/lib/utils/export-utils.js +362 -66
  40. package/dist/lib/utils/slot-helpers.d.ts +1 -1
  41. package/dist/lib/utils/slot-helpers.d.ts.map +1 -1
  42. package/package.json +4 -2
  43. package/src/lib/components/droupdown/menu-dropdown.tsx +9 -3
  44. package/src/lib/components/filters/filter-value-input.tsx +2 -2
  45. package/src/lib/components/pagination/data-table-pagination.tsx +14 -2
  46. package/src/lib/components/toolbar/data-table-toolbar.tsx +15 -1
  47. package/src/lib/components/toolbar/table-export-control.tsx +65 -9
  48. package/src/lib/components/toolbar/table-refresh-control.tsx +58 -0
  49. package/src/lib/contexts/data-table-context.tsx +16 -2
  50. package/src/lib/data-table.tsx +1282 -932
  51. package/src/lib/features/column-filter.feature.ts +40 -19
  52. package/src/lib/features/selection.feature.ts +11 -5
  53. package/src/lib/types/column.types.ts +20 -1
  54. package/src/lib/types/data-table-api.ts +37 -15
  55. package/src/lib/types/data-table.types.ts +59 -3
  56. package/src/lib/types/export.types.ts +79 -10
  57. package/src/lib/types/slots.types.ts +11 -1
  58. package/src/lib/types/table.types.ts +1 -3
  59. package/src/lib/utils/debounced-fetch.utils.ts +90 -18
  60. package/src/lib/utils/export-utils.ts +496 -69
  61. package/src/lib/utils/slot-helpers.tsx +1 -1
@@ -89,7 +89,7 @@ const DEFAULT_INITIAL_STATE = {
89
89
  */
90
90
  exports.DataTable = (0, react_1.forwardRef)(function DataTable({ initialState, columns, data = [], totalRow = 0, idKey = 'id', extraFilter = null, footerFilter = null,
91
91
  // Data management mode (MUI DataGrid style)
92
- dataMode = 'client', initialLoadData = true, onFetchData, onDataStateChange,
92
+ dataMode = 'client', initialLoadData = true, onFetchData, onRefreshData, onDataChange, onDataStateChange,
93
93
  // Selection props
94
94
  enableRowSelection = false, enableMultiRowSelection = true, selectMode = 'page', isRowSelectable, onSelectionChange,
95
95
  // Row click props
@@ -111,7 +111,7 @@ enablePagination = false, paginationMode = 'client',
111
111
  // Filtering props
112
112
  enableGlobalFilter = true, enableColumnFilter = false, filterMode = 'client',
113
113
  // Sorting props
114
- enableSorting = true, sortingMode = 'client', onSortingChange, exportFilename = 'export', onExportProgress, onExportComplete, onExportError, onServerExport, onExportCancel,
114
+ enableSorting = true, sortingMode = 'client', onSortingChange, exportFilename = 'export', exportConcurrency = 'cancelAndRestart', exportChunkSize = 1000, exportStrictTotalCheck = false, exportSanitizeCSV = true, onExportProgress, onExportComplete, onExportError, onServerExport, onExportCancel, onExportStateChange,
115
115
  // Styling props
116
116
  enableHover = true, enableStripes = false, tableProps = {}, fitToScreen = true, tableSize: initialTableSize = 'medium',
117
117
  // Sticky header/footer props
@@ -119,7 +119,7 @@ enableStickyHeaderOrFooter = false, maxHeight = '400px',
119
119
  // Virtualization props
120
120
  enableVirtualization = false, estimateRowHeight = 52,
121
121
  // Toolbar props
122
- enableTableSizeControl = true, enableExport = false, enableReset = true,
122
+ enableTableSizeControl = true, enableExport = false, enableReset = true, enableRefresh = false,
123
123
  // Loading and empty states
124
124
  loading = false, emptyMessage = 'No data available', skeletonRows = 5,
125
125
  // Column filters props
@@ -184,14 +184,24 @@ logging, }, ref) {
184
184
  const [serverData, setServerData] = (0, react_1.useState)(null);
185
185
  const [serverTotal, setServerTotal] = (0, react_1.useState)(0);
186
186
  const [exportController, setExportController] = (0, react_1.useState)(null);
187
+ const [exportProgress, setExportProgress] = (0, react_1.useState)({});
188
+ const [exportPhase, setExportPhase] = (0, react_1.useState)(null);
189
+ const [queuedExportCount, setQueuedExportCount] = (0, react_1.useState)(0);
187
190
  // -------------------------------
188
191
  // Ref hooks (grouped together)
189
192
  // -------------------------------
190
193
  const tableContainerRef = (0, react_1.useRef)(null);
191
194
  const internalApiRef = (0, react_1.useRef)(null);
195
+ const exportControllerRef = (0, react_1.useRef)(null);
196
+ const exportQueueRef = (0, react_1.useRef)(Promise.resolve());
197
+ const isExternallyControlledData = (0, react_1.useMemo)(() => !onFetchData && (!!onDataChange || !!onRefreshData), [onFetchData, onDataChange, onRefreshData]);
192
198
  const { debouncedFetch, isLoading: fetchLoading } = (0, debounced_fetch_utils_1.useDebouncedFetch)(onFetchData);
193
- const tableData = (0, react_1.useMemo)(() => serverData ? serverData : data, [serverData, data]);
194
- const tableTotalRow = (0, react_1.useMemo)(() => serverData ? serverTotal : totalRow || data.length, [serverData, serverTotal, totalRow, data]);
199
+ const tableData = (0, react_1.useMemo)(() => {
200
+ if (isExternallyControlledData)
201
+ return data;
202
+ return serverData !== null ? serverData : data;
203
+ }, [isExternallyControlledData, serverData, data]);
204
+ const tableTotalRow = (0, react_1.useMemo)(() => (isExternallyControlledData ? (totalRow || data.length) : (serverData !== null ? serverTotal : totalRow || data.length)), [isExternallyControlledData, serverData, serverTotal, totalRow, data]);
195
205
  const tableLoading = (0, react_1.useMemo)(() => onFetchData ? (loading || fetchLoading) : loading, [onFetchData, loading, fetchLoading]);
196
206
  const enhancedColumns = (0, react_1.useMemo)(() => {
197
207
  let columnsMap = [...columns];
@@ -239,7 +249,7 @@ logging, }, ref) {
239
249
  // Callback hooks (grouped together)
240
250
  // -------------------------------
241
251
  const fetchData = (0, react_1.useCallback)(async (overrides = {}, options) => {
242
- var _a, _b;
252
+ var _a, _b, _c, _d, _e;
243
253
  if (!onFetchData) {
244
254
  if (logger.isLevelEnabled('debug')) {
245
255
  logger.debug('onFetchData not provided, skipping fetch', { overrides, columnFilter, sorting, pagination });
@@ -254,17 +264,25 @@ logging, }, ref) {
254
264
  ...overrides,
255
265
  };
256
266
  if (logger.isLevelEnabled('info')) {
257
- logger.info('Requesting data', { filters });
267
+ logger.info('Requesting data', {
268
+ filters,
269
+ reason: (_a = options === null || options === void 0 ? void 0 : options.meta) === null || _a === void 0 ? void 0 : _a.reason,
270
+ force: (_b = options === null || options === void 0 ? void 0 : options.meta) === null || _b === void 0 ? void 0 : _b.force,
271
+ });
258
272
  }
259
273
  try {
260
- const result = await debouncedFetch(filters, options === undefined ? 0 : options.delay || 300);
274
+ const delay = (_c = options === null || options === void 0 ? void 0 : options.delay) !== null && _c !== void 0 ? _c : 300; // respects 0
275
+ const result = await debouncedFetch(filters, {
276
+ debounceDelay: delay,
277
+ meta: options === null || options === void 0 ? void 0 : options.meta,
278
+ });
261
279
  if (logger.isLevelEnabled('info')) {
262
280
  logger.info('Fetch resolved', {
263
- rows: (_b = (_a = result === null || result === void 0 ? void 0 : result.data) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0,
281
+ rows: (_e = (_d = result === null || result === void 0 ? void 0 : result.data) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0,
264
282
  total: result === null || result === void 0 ? void 0 : result.total,
265
283
  });
266
284
  }
267
- if ((result === null || result === void 0 ? void 0 : result.data) && (result === null || result === void 0 ? void 0 : result.total) !== undefined) {
285
+ if (result && Array.isArray(result.data) && result.total !== undefined) {
268
286
  setServerData(result.data);
269
287
  setServerTotal(result.total);
270
288
  }
@@ -286,67 +304,35 @@ logging, }, ref) {
286
304
  debouncedFetch,
287
305
  logger,
288
306
  ]);
289
- const tableStateChange = (0, react_1.useCallback)((overrides = {}) => {
290
- if (!onDataStateChange) {
291
- if (logger.isLevelEnabled('debug')) {
292
- logger.debug('No onDataStateChange handler registered; skipping state update notification', { overrides });
293
- }
294
- return;
307
+ const normalizeRefreshOptions = (0, react_1.useCallback)((options, fallbackReason = 'refresh') => {
308
+ var _a, _b, _c;
309
+ if (typeof options === 'boolean') {
310
+ return {
311
+ resetPagination: options,
312
+ force: false,
313
+ reason: fallbackReason,
314
+ };
295
315
  }
296
- const currentState = {
297
- globalFilter,
298
- columnFilter,
299
- sorting,
300
- pagination,
301
- columnOrder,
302
- columnPinning,
303
- columnVisibility,
304
- columnSizing,
305
- ...overrides,
316
+ return {
317
+ resetPagination: (_a = options === null || options === void 0 ? void 0 : options.resetPagination) !== null && _a !== void 0 ? _a : false,
318
+ force: (_b = options === null || options === void 0 ? void 0 : options.force) !== null && _b !== void 0 ? _b : false,
319
+ reason: (_c = options === null || options === void 0 ? void 0 : options.reason) !== null && _c !== void 0 ? _c : fallbackReason,
306
320
  };
307
- if (logger.isLevelEnabled('debug')) {
308
- logger.debug('Emitting tableStateChange', currentState);
309
- }
310
- onDataStateChange === null || onDataStateChange === void 0 ? void 0 : onDataStateChange(currentState);
311
- }, [
312
- onDataStateChange,
313
- globalFilter,
314
- columnFilter,
315
- sorting,
316
- pagination,
317
- columnOrder,
318
- columnPinning,
319
- columnVisibility,
320
- columnSizing,
321
- logger,
322
- ]);
321
+ }, []);
323
322
  const handleSelectionStateChange = (0, react_1.useCallback)((updaterOrValue) => {
324
323
  setSelectionState((prevState) => {
325
- const newSelectionState = typeof updaterOrValue === 'function'
326
- ? updaterOrValue(prevState)
327
- : updaterOrValue;
328
- setTimeout(() => {
329
- if (onSelectionChange) {
330
- onSelectionChange(newSelectionState);
331
- }
332
- if (onDataStateChange) {
333
- tableStateChange({ selectionState: newSelectionState });
334
- }
335
- }, 0);
336
- return newSelectionState;
324
+ const next = typeof updaterOrValue === 'function' ? updaterOrValue(prevState) : updaterOrValue;
325
+ onSelectionChange === null || onSelectionChange === void 0 ? void 0 : onSelectionChange(next);
326
+ return next;
337
327
  });
338
- }, [onSelectionChange, onDataStateChange, tableStateChange]);
328
+ }, [onSelectionChange]);
339
329
  const handleColumnFilterStateChange = (0, react_1.useCallback)((filterState) => {
340
330
  if (!filterState || typeof filterState !== 'object')
341
331
  return;
342
332
  setColumnFilter(filterState);
343
- if (onColumnFiltersChange) {
344
- setTimeout(() => onColumnFiltersChange(filterState), 0);
345
- }
346
- if (onDataStateChange) {
347
- setTimeout(() => tableStateChange({ columnFilter: filterState }), 0);
348
- }
349
- }, [onColumnFiltersChange, onDataStateChange, tableStateChange]);
333
+ onColumnFiltersChange === null || onColumnFiltersChange === void 0 ? void 0 : onColumnFiltersChange(filterState);
334
+ return filterState;
335
+ }, [onColumnFiltersChange]);
350
336
  const resetPageToFirst = (0, react_1.useCallback)(() => {
351
337
  if (logger.isLevelEnabled('info')) {
352
338
  logger.info('Resetting to first page due to state change', {
@@ -360,40 +346,17 @@ logging, }, ref) {
360
346
  return newPagination;
361
347
  }, [pagination, logger, onPaginationChange]);
362
348
  const handleSortingChange = (0, react_1.useCallback)((updaterOrValue) => {
363
- let newSorting = typeof updaterOrValue === 'function'
364
- ? updaterOrValue(sorting)
365
- : updaterOrValue;
366
- newSorting = newSorting.filter((sort) => sort.id);
367
- setSorting(newSorting);
368
- onSortingChange === null || onSortingChange === void 0 ? void 0 : onSortingChange(newSorting);
369
- if (logger.isLevelEnabled('debug')) {
370
- logger.debug('Sorting change applied', {
371
- sorting: newSorting,
372
- serverMode: isServerMode,
373
- serverSorting: isServerSorting,
374
- });
375
- }
376
- if (isServerMode || isServerSorting) {
377
- const pagination = resetPageToFirst();
378
- if (logger.isLevelEnabled('debug')) {
379
- logger.debug('Sorting change triggered server fetch', { pagination, sorting: newSorting });
349
+ setSorting((prev) => {
350
+ const next = typeof updaterOrValue === 'function' ? updaterOrValue(prev) : updaterOrValue;
351
+ const cleaned = next.filter((s) => s === null || s === void 0 ? void 0 : s.id);
352
+ onSortingChange === null || onSortingChange === void 0 ? void 0 : onSortingChange(cleaned);
353
+ const nextPagination = resetPageToFirst();
354
+ if (isServerMode || isServerSorting) {
355
+ fetchData({ sorting: cleaned, pagination: nextPagination }, { delay: 0 });
380
356
  }
381
- tableStateChange({ sorting: newSorting, pagination });
382
- fetchData({
383
- sorting: newSorting,
384
- pagination,
385
- });
386
- }
387
- else if (onDataStateChange) {
388
- const pagination = resetPageToFirst();
389
- setTimeout(() => {
390
- if (logger.isLevelEnabled('debug')) {
391
- logger.debug('Sorting change notified client state change', { pagination, sorting: newSorting });
392
- }
393
- tableStateChange({ sorting: newSorting, pagination });
394
- }, 0);
395
- }
396
- }, [sorting, onSortingChange, logger, isServerMode, isServerSorting, onDataStateChange, resetPageToFirst, tableStateChange, fetchData]);
357
+ return cleaned;
358
+ });
359
+ }, [onSortingChange, isServerMode, isServerSorting, resetPageToFirst, fetchData]);
397
360
  const handleColumnOrderChange = (0, react_1.useCallback)((updatedColumnOrder) => {
398
361
  const newColumnOrder = typeof updatedColumnOrder === 'function'
399
362
  ? updatedColumnOrder(columnOrder)
@@ -403,131 +366,52 @@ logging, }, ref) {
403
366
  onColumnDragEnd(newColumnOrder);
404
367
  }
405
368
  }, [onColumnDragEnd, columnOrder]);
406
- const handleColumnPinningChange = (0, react_1.useCallback)((updatedColumnPinning) => {
407
- const newColumnPinning = typeof updatedColumnPinning === 'function'
408
- ? updatedColumnPinning(columnPinning)
409
- : updatedColumnPinning;
410
- setColumnPinning(newColumnPinning);
411
- if (onColumnPinningChange) {
412
- onColumnPinningChange(newColumnPinning);
413
- }
414
- }, [onColumnPinningChange, columnPinning]);
369
+ const handleColumnPinningChange = (0, react_1.useCallback)((updater) => {
370
+ setColumnPinning((prev) => {
371
+ const next = typeof updater === "function" ? updater(prev) : updater;
372
+ // keep direct callback here (optional)
373
+ onColumnPinningChange === null || onColumnPinningChange === void 0 ? void 0 : onColumnPinningChange(next);
374
+ return next;
375
+ });
376
+ }, [onColumnPinningChange]);
415
377
  // Column visibility change handler - same pattern as column order
416
378
  const handleColumnVisibilityChange = (0, react_1.useCallback)((updater) => {
417
- const newVisibility = typeof updater === 'function'
418
- ? updater(columnVisibility)
419
- : updater;
420
- setColumnVisibility(newVisibility);
421
- if (onColumnVisibilityChange) {
422
- setTimeout(() => {
423
- onColumnVisibilityChange(newVisibility);
424
- }, 0);
425
- }
426
- if (onDataStateChange) {
427
- setTimeout(() => {
428
- tableStateChange({ columnVisibility: newVisibility });
429
- }, 0);
430
- }
431
- }, [onColumnVisibilityChange, onDataStateChange, tableStateChange, columnVisibility]);
379
+ setColumnVisibility((prev) => {
380
+ const next = typeof updater === 'function' ? updater(prev) : updater;
381
+ onColumnVisibilityChange === null || onColumnVisibilityChange === void 0 ? void 0 : onColumnVisibilityChange(next);
382
+ return next;
383
+ });
384
+ }, [onColumnVisibilityChange]);
432
385
  // Column sizing change handler - same pattern as column order
433
386
  const handleColumnSizingChange = (0, react_1.useCallback)((updater) => {
434
- const newSizing = typeof updater === 'function'
435
- ? updater(columnSizing)
436
- : updater;
437
- setColumnSizing(newSizing);
438
- if (onColumnSizingChange) {
439
- setTimeout(() => {
440
- onColumnSizingChange(newSizing);
441
- }, 0);
442
- }
443
- if (onDataStateChange) {
444
- setTimeout(() => {
445
- tableStateChange({ columnSizing: newSizing });
446
- }, 0);
447
- }
448
- }, [onColumnSizingChange, onDataStateChange, tableStateChange, columnSizing]);
387
+ setColumnSizing((prev) => {
388
+ const next = typeof updater === 'function' ? updater(prev) : updater;
389
+ onColumnSizingChange === null || onColumnSizingChange === void 0 ? void 0 : onColumnSizingChange(next);
390
+ return next;
391
+ });
392
+ }, [onColumnSizingChange]);
449
393
  const handlePaginationChange = (0, react_1.useCallback)((updater) => {
450
- const newPagination = typeof updater === 'function' ? updater(pagination) : updater;
451
- if (logger.isLevelEnabled('debug')) {
452
- logger.debug('Pagination change requested', {
453
- previous: pagination,
454
- next: newPagination,
455
- serverSide: isServerMode || isServerPagination,
456
- });
457
- }
458
- // Update pagination state
459
- setPagination(newPagination);
460
- onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(newPagination);
461
- if (logger.isLevelEnabled('debug')) {
462
- logger.debug('Pagination state updated', newPagination);
463
- }
464
- // Notify state change and fetch data if needed
465
- if (isServerMode || isServerPagination) {
466
- setTimeout(() => {
467
- if (logger.isLevelEnabled('debug')) {
468
- logger.debug('Notifying server-side pagination change', newPagination);
469
- }
470
- tableStateChange({ pagination: newPagination });
471
- fetchData({ pagination: newPagination });
472
- }, 0);
473
- }
474
- else if (onDataStateChange) {
475
- setTimeout(() => {
476
- if (logger.isLevelEnabled('debug')) {
477
- logger.debug('Notifying client-side pagination change', newPagination);
478
- }
479
- tableStateChange({ pagination: newPagination });
480
- }, 0);
481
- }
482
- }, [
483
- pagination,
484
- isServerMode,
485
- isServerPagination,
486
- onDataStateChange,
487
- fetchData,
488
- tableStateChange,
489
- logger,
490
- onPaginationChange,
491
- ]);
394
+ setPagination((prev) => {
395
+ const next = typeof updater === 'function' ? updater(prev) : updater;
396
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(next);
397
+ if (isServerMode || isServerPagination) {
398
+ fetchData({ pagination: next }, { delay: 0 });
399
+ }
400
+ return next;
401
+ });
402
+ }, [isServerMode, isServerPagination, fetchData, onPaginationChange]);
492
403
  const handleGlobalFilterChange = (0, react_1.useCallback)((updaterOrValue) => {
493
- const newFilter = typeof updaterOrValue === 'function'
494
- ? updaterOrValue(globalFilter)
495
- : updaterOrValue;
496
- setGlobalFilter(newFilter);
497
- if (logger.isLevelEnabled('debug')) {
498
- logger.debug('Global filter change applied', {
499
- value: newFilter,
500
- serverMode: isServerMode,
501
- serverFiltering: isServerFiltering,
502
- });
503
- }
504
- if (isServerMode || isServerFiltering) {
505
- const pagination = resetPageToFirst();
506
- setTimeout(() => {
507
- if (logger.isLevelEnabled('debug')) {
508
- logger.debug('Global filter change triggering server fetch', {
509
- pagination,
510
- value: newFilter,
511
- });
512
- }
513
- tableStateChange({ globalFilter: newFilter, pagination });
514
- fetchData({ globalFilter: newFilter, pagination });
515
- }, 0);
516
- }
517
- else if (onDataStateChange) {
518
- const pagination = resetPageToFirst();
519
- setTimeout(() => {
520
- if (logger.isLevelEnabled('debug')) {
521
- logger.debug('Global filter change notifying client listeners', {
522
- pagination,
523
- value: newFilter,
524
- });
525
- }
526
- tableStateChange({ globalFilter: newFilter, pagination });
527
- }, 0);
528
- }
529
- onGlobalFilterChange === null || onGlobalFilterChange === void 0 ? void 0 : onGlobalFilterChange(newFilter);
530
- }, [globalFilter, logger, isServerMode, isServerFiltering, onDataStateChange, onGlobalFilterChange, resetPageToFirst, tableStateChange, fetchData]);
404
+ setGlobalFilter((prev) => {
405
+ const next = typeof updaterOrValue === 'function' ? updaterOrValue(prev) : updaterOrValue;
406
+ onGlobalFilterChange === null || onGlobalFilterChange === void 0 ? void 0 : onGlobalFilterChange(next);
407
+ if (isServerMode || isServerFiltering) {
408
+ const nextPagination = { pageIndex: 0, pageSize: pagination.pageSize };
409
+ setPagination(nextPagination);
410
+ fetchData({ globalFilter: next, pagination: nextPagination }, { delay: 0 });
411
+ }
412
+ return next;
413
+ });
414
+ }, [isServerMode, isServerFiltering, onGlobalFilterChange, fetchData, pagination.pageSize]);
531
415
  const onColumnFilterChangeHandler = (0, react_1.useCallback)((updater) => {
532
416
  const currentState = columnFilter;
533
417
  const newState = typeof updater === 'function'
@@ -544,22 +428,13 @@ logging, }, ref) {
544
428
  const onColumnFilterApplyHandler = (0, react_1.useCallback)((appliedState) => {
545
429
  const pagination = resetPageToFirst();
546
430
  if (isServerFiltering) {
547
- tableStateChange({
548
- columnFilter: appliedState,
549
- pagination,
550
- });
551
431
  fetchData({
552
432
  columnFilter: appliedState,
553
433
  pagination,
554
434
  });
555
435
  }
556
- else if (onDataStateChange) {
557
- setTimeout(() => tableStateChange({ columnFilter: appliedState, pagination }), 0);
558
- }
559
- setTimeout(() => {
560
- onColumnFiltersChange === null || onColumnFiltersChange === void 0 ? void 0 : onColumnFiltersChange(appliedState);
561
- }, 0);
562
- }, [resetPageToFirst, isServerFiltering, onDataStateChange, tableStateChange, fetchData, onColumnFiltersChange]);
436
+ onColumnFiltersChange === null || onColumnFiltersChange === void 0 ? void 0 : onColumnFiltersChange(appliedState);
437
+ }, [resetPageToFirst, isServerFiltering, fetchData, onColumnFiltersChange]);
563
438
  // -------------------------------
564
439
  // Table creation (after callbacks/memo)
565
440
  // -------------------------------
@@ -681,12 +556,21 @@ logging, }, ref) {
681
556
  // -------------------------------
682
557
  // Effects (after callbacks)
683
558
  // -------------------------------
559
+ (0, react_1.useEffect)(() => {
560
+ if (!isExternallyControlledData || serverData === null)
561
+ return;
562
+ setServerData(null);
563
+ setServerTotal(0);
564
+ }, [isExternallyControlledData, serverData]);
684
565
  (0, react_1.useEffect)(() => {
685
566
  if (initialLoadData && onFetchData) {
686
567
  if (logger.isLevelEnabled('info')) {
687
568
  logger.info('Initial data load triggered', { initialLoadData });
688
569
  }
689
- fetchData({});
570
+ fetchData({}, {
571
+ delay: 0,
572
+ meta: { reason: 'initial' },
573
+ });
690
574
  }
691
575
  else if (logger.isLevelEnabled('debug')) {
692
576
  logger.debug('Skipping initial data load', {
@@ -710,502 +594,739 @@ logging, }, ref) {
710
594
  setColumnOrder(initialOrder);
711
595
  }
712
596
  }, [enableColumnDragging, enhancedColumns, columnOrder.length]);
713
- const dataTableApi = (0, react_1.useMemo)(() => ({
714
- table: {
715
- getTable: () => table,
716
- },
717
- // Column Management
718
- columnVisibility: {
719
- showColumn: (columnId) => {
720
- var _a;
721
- (_a = table.getColumn(columnId)) === null || _a === void 0 ? void 0 : _a.toggleVisibility(true);
722
- },
723
- hideColumn: (columnId) => {
724
- var _a;
725
- (_a = table.getColumn(columnId)) === null || _a === void 0 ? void 0 : _a.toggleVisibility(false);
726
- },
727
- toggleColumn: (columnId) => {
728
- var _a;
729
- (_a = table.getColumn(columnId)) === null || _a === void 0 ? void 0 : _a.toggleVisibility();
730
- },
731
- showAllColumns: () => {
732
- table.toggleAllColumnsVisible(true);
733
- },
734
- hideAllColumns: () => {
735
- table.toggleAllColumnsVisible(false);
736
- },
737
- resetColumnVisibility: () => {
738
- const initialVisibility = initialStateConfig.columnVisibility || {};
739
- table.setColumnVisibility(initialVisibility);
740
- // Manually trigger handler to ensure callbacks are called
741
- handleColumnVisibilityChange(initialVisibility);
742
- },
743
- },
744
- // Column Ordering
745
- columnOrdering: {
746
- setColumnOrder: (columnOrder) => {
747
- table.setColumnOrder(columnOrder);
748
- },
749
- moveColumn: (columnId, toIndex) => {
750
- const currentOrder = table.getState().columnOrder || [];
751
- const currentIndex = currentOrder.indexOf(columnId);
752
- if (currentIndex === -1)
753
- return;
754
- const newOrder = [...currentOrder];
755
- newOrder.splice(currentIndex, 1);
756
- newOrder.splice(toIndex, 0, columnId);
757
- table.setColumnOrder(newOrder);
758
- },
759
- resetColumnOrder: () => {
760
- const initialOrder = enhancedColumns.map((col, index) => {
761
- if (col.id)
762
- return col.id;
763
- const anyCol = col;
764
- if (anyCol.accessorKey && typeof anyCol.accessorKey === 'string') {
765
- return anyCol.accessorKey;
766
- }
767
- return `column_${index}`;
768
- });
769
- table.setColumnOrder(initialOrder);
770
- // Manually trigger handler to ensure callbacks are called
771
- handleColumnOrderChange(initialOrder);
772
- },
773
- },
774
- // Column Pinning
775
- columnPinning: {
776
- pinColumnLeft: (columnId) => {
777
- const currentPinning = table.getState().columnPinning;
778
- const newPinning = { ...currentPinning };
779
- // Remove from right if exists
780
- newPinning.right = (newPinning.right || []).filter(id => id !== columnId);
781
- // Add to left if not exists
782
- newPinning.left = [...(newPinning.left || []).filter(id => id !== columnId), columnId];
783
- table.setColumnPinning(newPinning);
784
- },
785
- pinColumnRight: (columnId) => {
786
- const currentPinning = table.getState().columnPinning;
787
- const newPinning = { ...currentPinning };
788
- // Remove from left if exists
789
- newPinning.left = (newPinning.left || []).filter(id => id !== columnId);
790
- // Add to right if not exists - prepend to beginning (appears rightmost to leftmost)
791
- // First column pinned appears rightmost, second appears to its left, etc.
792
- newPinning.right = [columnId, ...(newPinning.right || []).filter(id => id !== columnId)];
793
- table.setColumnPinning(newPinning);
794
- },
795
- unpinColumn: (columnId) => {
796
- const currentPinning = table.getState().columnPinning;
797
- const newPinning = {
798
- left: (currentPinning.left || []).filter(id => id !== columnId),
799
- right: (currentPinning.right || []).filter(id => id !== columnId),
800
- };
801
- table.setColumnPinning(newPinning);
802
- },
803
- setPinning: (pinning) => {
804
- table.setColumnPinning(pinning);
805
- },
806
- resetColumnPinning: () => {
807
- const initialPinning = initialStateConfig.columnPinning || { left: [], right: [] };
808
- table.setColumnPinning(initialPinning);
809
- // Manually trigger handler to ensure callbacks are called
810
- handleColumnPinningChange(initialPinning);
811
- },
812
- },
813
- // Column Resizing
814
- columnResizing: {
815
- resizeColumn: (columnId, width) => {
816
- // Use table's setColumnSizing method
817
- const currentSizing = table.getState().columnSizing;
818
- table.setColumnSizing({
819
- ...currentSizing,
820
- [columnId]: width,
821
- });
822
- },
823
- autoSizeColumn: (columnId) => {
824
- var _a;
825
- // TanStack doesn't have built-in auto-size, so reset to default
826
- (_a = table.getColumn(columnId)) === null || _a === void 0 ? void 0 : _a.resetSize();
827
- },
828
- autoSizeAllColumns: () => {
829
- const initialSizing = initialStateConfig.columnSizing || {};
830
- table.setColumnSizing(initialSizing);
831
- // Manually trigger handler to ensure callbacks are called
832
- handleColumnSizingChange(initialSizing);
833
- },
834
- resetColumnSizing: () => {
835
- const initialSizing = initialStateConfig.columnSizing || {};
836
- table.setColumnSizing(initialSizing);
837
- // Manually trigger handler to ensure callbacks are called
838
- handleColumnSizingChange(initialSizing);
839
- },
840
- },
841
- // Filtering
842
- filtering: {
843
- setGlobalFilter: (filter) => {
844
- table.setGlobalFilter(filter);
845
- },
846
- clearGlobalFilter: () => {
847
- table.setGlobalFilter('');
848
- },
849
- setColumnFilters: (filters) => {
850
- handleColumnFilterStateChange(filters);
851
- },
852
- addColumnFilter: (columnId, operator, value) => {
853
- const newFilter = {
854
- id: `filter_${Date.now()}`,
855
- columnId,
856
- operator,
857
- value,
858
- };
859
- const columnFilter = table.getState().columnFilter;
860
- const currentFilters = columnFilter.filters || [];
861
- const newFilters = [...currentFilters, newFilter];
862
- handleColumnFilterStateChange({
863
- filters: newFilters,
864
- logic: columnFilter.logic,
865
- pendingFilters: columnFilter.pendingFilters || [],
866
- pendingLogic: columnFilter.pendingLogic || 'AND',
867
- });
868
- if (logger.isLevelEnabled('debug')) {
869
- logger.debug(`Adding column filter ${columnId} ${operator} ${value}`, newFilters);
870
- }
871
- },
872
- removeColumnFilter: (filterId) => {
873
- const columnFilter = table.getState().columnFilter;
874
- const currentFilters = columnFilter.filters || [];
875
- const newFilters = currentFilters.filter((f) => f.id !== filterId);
876
- handleColumnFilterStateChange({
877
- filters: newFilters,
878
- logic: columnFilter.logic,
879
- pendingFilters: columnFilter.pendingFilters || [],
880
- pendingLogic: columnFilter.pendingLogic || 'AND',
881
- });
882
- if (logger.isLevelEnabled('debug')) {
883
- logger.debug(`Removing column filter ${filterId}`, newFilters);
884
- }
885
- },
886
- clearAllFilters: () => {
887
- table.setGlobalFilter('');
888
- handleColumnFilterStateChange({
889
- filters: [],
890
- logic: 'AND',
891
- pendingFilters: [],
892
- pendingLogic: 'AND',
597
+ const lastSentRef = (0, react_1.useRef)("");
598
+ const emitTableState = (0, react_1.useCallback)(() => {
599
+ if (!onDataStateChange)
600
+ return;
601
+ const live = table.getState();
602
+ const liveColumnFilter = live.columnFilter;
603
+ // only keep what you persist/store
604
+ const payload = {
605
+ sorting: live.sorting,
606
+ pagination: live.pagination,
607
+ globalFilter: live.globalFilter,
608
+ columnFilter: liveColumnFilter,
609
+ columnVisibility: live.columnVisibility,
610
+ columnSizing: live.columnSizing,
611
+ columnOrder: live.columnOrder,
612
+ columnPinning: live.columnPinning,
613
+ };
614
+ const key = JSON.stringify(payload);
615
+ if (key === lastSentRef.current)
616
+ return;
617
+ lastSentRef.current = key;
618
+ onDataStateChange(payload);
619
+ }, [onDataStateChange, table]);
620
+ (0, react_1.useEffect)(() => {
621
+ emitTableState();
622
+ }, [
623
+ emitTableState,
624
+ sorting,
625
+ pagination,
626
+ globalFilter,
627
+ columnFilter,
628
+ columnVisibility,
629
+ columnSizing,
630
+ columnOrder,
631
+ columnPinning,
632
+ ]);
633
+ const getResetState = (0, react_1.useCallback)(() => {
634
+ var _a;
635
+ const resetSorting = initialStateConfig.sorting || [];
636
+ const resetGlobalFilter = (_a = initialStateConfig.globalFilter) !== null && _a !== void 0 ? _a : '';
637
+ const resetColumnFilter = initialStateConfig.columnFilter;
638
+ const resetPagination = enablePagination
639
+ ? (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 })
640
+ : undefined;
641
+ return {
642
+ sorting: resetSorting,
643
+ globalFilter: resetGlobalFilter,
644
+ columnFilter: resetColumnFilter,
645
+ ...(resetPagination ? { pagination: resetPagination } : {}),
646
+ };
647
+ }, [initialStateConfig, enablePagination]);
648
+ const applyDataMutation = (0, react_1.useCallback)((action, updater, details = {}) => {
649
+ const previousData = [...tableData];
650
+ const nextData = updater(previousData);
651
+ if (nextData === previousData)
652
+ return nextData;
653
+ const nextTotal = Math.max(0, tableTotalRow + (nextData.length - previousData.length));
654
+ if (!isExternallyControlledData) {
655
+ setServerData(nextData);
656
+ setServerTotal(nextTotal);
657
+ }
658
+ onDataChange === null || onDataChange === void 0 ? void 0 : onDataChange(nextData, {
659
+ action,
660
+ previousData,
661
+ nextData,
662
+ totalRow: nextTotal,
663
+ ...details,
664
+ });
665
+ if (logger.isLevelEnabled('debug')) {
666
+ logger.debug('Applied data mutation', {
667
+ action,
668
+ previousCount: previousData.length,
669
+ nextCount: nextData.length,
670
+ totalRow: nextTotal,
671
+ });
672
+ }
673
+ return nextData;
674
+ }, [isExternallyControlledData, logger, onDataChange, tableData, tableTotalRow]);
675
+ const buildRefreshContext = (0, react_1.useCallback)((options, paginationOverride) => {
676
+ const state = table.getState();
677
+ const nextPagination = paginationOverride || state.pagination || pagination;
678
+ return {
679
+ filters: {
680
+ globalFilter,
681
+ pagination: nextPagination,
682
+ columnFilter,
683
+ sorting,
684
+ },
685
+ state: {
686
+ sorting,
687
+ pagination: nextPagination,
688
+ globalFilter,
689
+ columnFilter,
690
+ columnVisibility: state.columnVisibility,
691
+ columnSizing: state.columnSizing,
692
+ columnOrder: state.columnOrder,
693
+ columnPinning: state.columnPinning,
694
+ },
695
+ options,
696
+ };
697
+ }, [table, pagination, globalFilter, columnFilter, sorting]);
698
+ const triggerRefresh = (0, react_1.useCallback)(async (options, fallbackReason = 'refresh') => {
699
+ const normalizedOptions = normalizeRefreshOptions(options, fallbackReason);
700
+ const nextPagination = enablePagination
701
+ ? {
702
+ pageIndex: normalizedOptions.resetPagination ? 0 : pagination.pageIndex,
703
+ pageSize: pagination.pageSize,
704
+ }
705
+ : undefined;
706
+ const shouldUpdatePagination = !!nextPagination
707
+ && (nextPagination.pageIndex !== pagination.pageIndex || nextPagination.pageSize !== pagination.pageSize);
708
+ if (nextPagination && shouldUpdatePagination) {
709
+ setPagination(nextPagination);
710
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(nextPagination);
711
+ }
712
+ const refreshContext = buildRefreshContext(normalizedOptions, nextPagination);
713
+ if (onRefreshData) {
714
+ await onRefreshData(refreshContext);
715
+ return;
716
+ }
717
+ if (onFetchData) {
718
+ await fetchData(nextPagination ? { pagination: nextPagination } : {}, {
719
+ delay: 0,
720
+ meta: {
721
+ reason: normalizedOptions.reason,
722
+ force: normalizedOptions.force,
723
+ },
724
+ });
725
+ return;
726
+ }
727
+ if (logger.isLevelEnabled('debug')) {
728
+ logger.debug('Refresh skipped because no refresh handler is configured', refreshContext);
729
+ }
730
+ }, [
731
+ normalizeRefreshOptions,
732
+ enablePagination,
733
+ pagination,
734
+ onPaginationChange,
735
+ buildRefreshContext,
736
+ onRefreshData,
737
+ onFetchData,
738
+ fetchData,
739
+ logger,
740
+ ]);
741
+ const resetAllAndReload = (0, react_1.useCallback)(() => {
742
+ var _a;
743
+ const resetState = getResetState();
744
+ setSorting(resetState.sorting || []);
745
+ setGlobalFilter((_a = resetState.globalFilter) !== null && _a !== void 0 ? _a : '');
746
+ setColumnFilter(resetState.columnFilter);
747
+ if (resetState.pagination) {
748
+ setPagination(resetState.pagination);
749
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(resetState.pagination);
750
+ }
751
+ setSelectionState(initialSelectionState);
752
+ setExpanded({});
753
+ // layout state
754
+ setColumnVisibility(initialStateConfig.columnVisibility || {});
755
+ setColumnSizing(initialStateConfig.columnSizing || {});
756
+ setColumnOrder(initialStateConfig.columnOrder || []);
757
+ setColumnPinning(initialStateConfig.columnPinning || { left: [], right: [] });
758
+ const resetOptions = normalizeRefreshOptions({
759
+ resetPagination: true,
760
+ force: true,
761
+ reason: 'reset',
762
+ }, 'reset');
763
+ const refreshContext = buildRefreshContext(resetOptions, resetState.pagination);
764
+ if (onRefreshData) {
765
+ void onRefreshData(refreshContext);
766
+ return;
767
+ }
768
+ if (onFetchData) {
769
+ void fetchData(resetState, {
770
+ delay: 0,
771
+ meta: {
772
+ reason: resetOptions.reason,
773
+ force: resetOptions.force,
774
+ },
775
+ });
776
+ }
777
+ }, [
778
+ getResetState,
779
+ initialSelectionState,
780
+ initialStateConfig,
781
+ onPaginationChange,
782
+ normalizeRefreshOptions,
783
+ buildRefreshContext,
784
+ onRefreshData,
785
+ onFetchData,
786
+ fetchData,
787
+ ]);
788
+ const setExportControllerSafely = (0, react_1.useCallback)((value) => {
789
+ setExportController((current) => {
790
+ const next = typeof value === 'function' ? value(current) : value;
791
+ exportControllerRef.current = next;
792
+ return next;
793
+ });
794
+ }, []);
795
+ const handleExportProgressInternal = (0, react_1.useCallback)((progress) => {
796
+ setExportProgress(progress || {});
797
+ onExportProgress === null || onExportProgress === void 0 ? void 0 : onExportProgress(progress);
798
+ }, [onExportProgress]);
799
+ const handleExportStateChangeInternal = (0, react_1.useCallback)((state) => {
800
+ setExportPhase(state.phase);
801
+ if (state.processedRows !== undefined
802
+ || state.totalRows !== undefined
803
+ || state.percentage !== undefined) {
804
+ setExportProgress({
805
+ processedRows: state.processedRows,
806
+ totalRows: state.totalRows,
807
+ percentage: state.percentage,
808
+ });
809
+ }
810
+ onExportStateChange === null || onExportStateChange === void 0 ? void 0 : onExportStateChange(state);
811
+ }, [onExportStateChange]);
812
+ const runExportWithPolicy = (0, react_1.useCallback)(async (options) => {
813
+ const { format, filename, mode, execute } = options;
814
+ const startExecution = async () => {
815
+ const controller = new AbortController();
816
+ setExportProgress({});
817
+ setExportControllerSafely(controller);
818
+ try {
819
+ await execute(controller);
820
+ }
821
+ finally {
822
+ setExportControllerSafely((current) => (current === controller ? null : current));
823
+ }
824
+ };
825
+ if (exportConcurrency === 'queue') {
826
+ setQueuedExportCount((prev) => prev + 1);
827
+ const runQueued = async () => {
828
+ setQueuedExportCount((prev) => Math.max(0, prev - 1));
829
+ await startExecution();
830
+ };
831
+ const queuedPromise = exportQueueRef.current
832
+ .catch(() => undefined)
833
+ .then(runQueued);
834
+ exportQueueRef.current = queuedPromise;
835
+ return queuedPromise;
836
+ }
837
+ const activeController = exportControllerRef.current;
838
+ if (activeController) {
839
+ if (exportConcurrency === 'ignoreIfRunning') {
840
+ handleExportStateChangeInternal({
841
+ phase: 'error',
842
+ mode,
843
+ format,
844
+ filename,
845
+ message: 'An export is already running',
846
+ code: 'EXPORT_IN_PROGRESS',
847
+ endedAt: Date.now(),
893
848
  });
894
- },
895
- resetFilters: () => {
896
- handleColumnFilterStateChange({
897
- filters: [],
898
- logic: 'AND',
899
- pendingFilters: [],
900
- pendingLogic: 'AND',
849
+ onExportError === null || onExportError === void 0 ? void 0 : onExportError({
850
+ message: 'An export is already running',
851
+ code: 'EXPORT_IN_PROGRESS',
901
852
  });
902
- if (logger.isLevelEnabled('debug')) {
903
- logger.debug('Resetting filters');
904
- }
905
- },
906
- },
907
- // Sorting
908
- sorting: {
909
- setSorting: (sortingState) => {
910
- table.setSorting(sortingState);
911
- if (logger.isLevelEnabled('debug')) {
912
- logger.debug(`Setting sorting`, sortingState);
913
- }
914
- },
915
- sortColumn: (columnId, direction) => {
916
- const column = table.getColumn(columnId);
917
- if (!column)
918
- return;
919
- if (direction === false) {
920
- column.clearSorting();
921
- }
922
- else {
923
- column.toggleSorting(direction === 'desc');
924
- }
925
- },
926
- clearSorting: () => {
927
- table.setSorting([]);
928
- // Manually trigger handler to ensure callbacks are called
929
- handleSortingChange([]);
930
- },
931
- resetSorting: () => {
932
- const initialSorting = initialStateConfig.sorting || [];
933
- table.setSorting(initialSorting);
934
- // Manually trigger handler to ensure callbacks are called
935
- handleSortingChange(initialSorting);
936
- },
937
- },
938
- // Pagination
939
- pagination: {
940
- goToPage: (pageIndex) => {
941
- table.setPageIndex(pageIndex);
942
- if (logger.isLevelEnabled('debug')) {
943
- logger.debug(`Going to page ${pageIndex}`);
944
- }
945
- },
946
- nextPage: () => {
947
- table.nextPage();
948
- if (logger.isLevelEnabled('debug')) {
949
- logger.debug('Next page');
950
- }
951
- },
952
- previousPage: () => {
953
- table.previousPage();
954
- if (logger.isLevelEnabled('debug')) {
955
- logger.debug('Previous page');
956
- }
957
- },
958
- setPageSize: (pageSize) => {
959
- table.setPageSize(pageSize);
960
- if (logger.isLevelEnabled('debug')) {
961
- logger.debug(`Setting page size to ${pageSize}`);
962
- }
963
- },
964
- goToFirstPage: () => {
965
- table.setPageIndex(0);
966
- if (logger.isLevelEnabled('debug')) {
967
- logger.debug('Going to first page');
968
- }
969
- },
970
- goToLastPage: () => {
971
- const pageCount = table.getPageCount();
972
- if (pageCount > 0) {
973
- table.setPageIndex(pageCount - 1);
974
- if (logger.isLevelEnabled('debug')) {
975
- logger.debug(`Going to last page ${pageCount - 1}`);
853
+ return;
854
+ }
855
+ if (exportConcurrency === 'cancelAndRestart') {
856
+ activeController.abort();
857
+ }
858
+ }
859
+ await startExecution();
860
+ }, [
861
+ exportConcurrency,
862
+ handleExportStateChangeInternal,
863
+ onExportError,
864
+ setExportControllerSafely,
865
+ ]);
866
+ const dataTableApi = (0, react_1.useMemo)(() => {
867
+ // helpers (avoid repeating boilerplate)
868
+ const buildInitialOrder = () => enhancedColumns.map((col, index) => {
869
+ if (col.id)
870
+ return col.id;
871
+ const anyCol = col;
872
+ if (anyCol.accessorKey && typeof anyCol.accessorKey === "string")
873
+ return anyCol.accessorKey;
874
+ return `column_${index}`;
875
+ });
876
+ const applyColumnOrder = (next) => {
877
+ // handleColumnOrderChange supports both Updater<ColumnOrderState> and array in your impl
878
+ handleColumnOrderChange(next);
879
+ };
880
+ const applyPinning = (next) => {
881
+ handleColumnPinningChange(next);
882
+ };
883
+ const applyVisibility = (next) => {
884
+ handleColumnVisibilityChange(next);
885
+ };
886
+ const applySizing = (next) => {
887
+ handleColumnSizingChange(next);
888
+ };
889
+ const applyPagination = (next) => {
890
+ handlePaginationChange(next);
891
+ };
892
+ const applySorting = (next) => {
893
+ handleSortingChange(next);
894
+ };
895
+ const applyGlobalFilter = (next) => {
896
+ handleGlobalFilterChange(next);
897
+ };
898
+ const getRowIndexById = (rowsToSearch, rowId) => rowsToSearch.findIndex((row, index) => String((0, utils_1.generateRowId)(row, index, idKey)) === rowId);
899
+ const clampInsertIndex = (rowsToMutate, insertIndex) => {
900
+ if (insertIndex === undefined)
901
+ return rowsToMutate.length;
902
+ return Math.max(0, Math.min(insertIndex, rowsToMutate.length));
903
+ };
904
+ return {
905
+ table: {
906
+ getTable: () => table,
907
+ },
908
+ // -------------------------------
909
+ // Column Management
910
+ // -------------------------------
911
+ columnVisibility: {
912
+ showColumn: (columnId) => {
913
+ applyVisibility({ ...table.getState().columnVisibility, [columnId]: true });
914
+ },
915
+ hideColumn: (columnId) => {
916
+ applyVisibility({ ...table.getState().columnVisibility, [columnId]: false });
917
+ },
918
+ toggleColumn: (columnId) => {
919
+ var _a, _b;
920
+ const curr = (_b = (_a = table.getState().columnVisibility) === null || _a === void 0 ? void 0 : _a[columnId]) !== null && _b !== void 0 ? _b : true;
921
+ applyVisibility({ ...table.getState().columnVisibility, [columnId]: !curr });
922
+ },
923
+ showAllColumns: () => {
924
+ // set all known columns true
925
+ const all = {};
926
+ table.getAllLeafColumns().forEach((c) => (all[c.id] = true));
927
+ applyVisibility(all);
928
+ },
929
+ hideAllColumns: () => {
930
+ const all = {};
931
+ table.getAllLeafColumns().forEach((c) => (all[c.id] = false));
932
+ applyVisibility(all);
933
+ },
934
+ resetColumnVisibility: () => {
935
+ const initialVisibility = initialStateConfig.columnVisibility || {};
936
+ applyVisibility(initialVisibility);
937
+ },
938
+ },
939
+ // -------------------------------
940
+ // Column Ordering
941
+ // -------------------------------
942
+ columnOrdering: {
943
+ setColumnOrder: (nextOrder) => {
944
+ applyColumnOrder(nextOrder);
945
+ },
946
+ moveColumn: (columnId, toIndex) => {
947
+ var _a;
948
+ const currentOrder = (((_a = table.getState().columnOrder) === null || _a === void 0 ? void 0 : _a.length) ? table.getState().columnOrder : buildInitialOrder()) || [];
949
+ const fromIndex = currentOrder.indexOf(columnId);
950
+ if (fromIndex === -1)
951
+ return;
952
+ const next = [...currentOrder];
953
+ next.splice(fromIndex, 1);
954
+ next.splice(toIndex, 0, columnId);
955
+ applyColumnOrder(next);
956
+ },
957
+ resetColumnOrder: () => {
958
+ applyColumnOrder(buildInitialOrder());
959
+ },
960
+ },
961
+ // -------------------------------
962
+ // Column Pinning
963
+ // -------------------------------
964
+ columnPinning: {
965
+ pinColumnLeft: (columnId) => {
966
+ const current = table.getState().columnPinning || { left: [], right: [] };
967
+ const next = {
968
+ left: [...(current.left || []).filter((id) => id !== columnId), columnId],
969
+ right: (current.right || []).filter((id) => id !== columnId),
970
+ };
971
+ applyPinning(next);
972
+ },
973
+ pinColumnRight: (columnId) => {
974
+ const current = table.getState().columnPinning || { left: [], right: [] };
975
+ const next = {
976
+ left: (current.left || []).filter((id) => id !== columnId),
977
+ // keep your "prepend" behavior
978
+ right: [columnId, ...(current.right || []).filter((id) => id !== columnId)],
979
+ };
980
+ applyPinning(next);
981
+ },
982
+ unpinColumn: (columnId) => {
983
+ const current = table.getState().columnPinning || { left: [], right: [] };
984
+ const next = {
985
+ left: (current.left || []).filter((id) => id !== columnId),
986
+ right: (current.right || []).filter((id) => id !== columnId),
987
+ };
988
+ applyPinning(next);
989
+ },
990
+ setPinning: (pinning) => {
991
+ applyPinning(pinning);
992
+ },
993
+ resetColumnPinning: () => {
994
+ const initialPinning = initialStateConfig.columnPinning || { left: [], right: [] };
995
+ applyPinning(initialPinning);
996
+ },
997
+ },
998
+ // -------------------------------
999
+ // Column Resizing
1000
+ // -------------------------------
1001
+ columnResizing: {
1002
+ resizeColumn: (columnId, width) => {
1003
+ const currentSizing = table.getState().columnSizing || {};
1004
+ applySizing({ ...currentSizing, [columnId]: width });
1005
+ },
1006
+ autoSizeColumn: (columnId) => {
1007
+ // safe to call tanstack helper; it will feed into onColumnSizingChange if wired,
1008
+ // but since you're controlled, we still prefer to update through handler:
1009
+ const col = table.getColumn(columnId);
1010
+ if (!col)
1011
+ return;
1012
+ col.resetSize();
1013
+ // after resetSize, read state and emit via handler so controlled stays synced
1014
+ applySizing({ ...(table.getState().columnSizing || {}) });
1015
+ },
1016
+ autoSizeAllColumns: () => {
1017
+ const initialSizing = initialStateConfig.columnSizing || {};
1018
+ applySizing(initialSizing);
1019
+ },
1020
+ resetColumnSizing: () => {
1021
+ const initialSizing = initialStateConfig.columnSizing || {};
1022
+ applySizing(initialSizing);
1023
+ },
1024
+ },
1025
+ // -------------------------------
1026
+ // Filtering
1027
+ // -------------------------------
1028
+ filtering: {
1029
+ setGlobalFilter: (filter) => {
1030
+ applyGlobalFilter(filter);
1031
+ },
1032
+ clearGlobalFilter: () => {
1033
+ applyGlobalFilter("");
1034
+ },
1035
+ setColumnFilters: (filters) => {
1036
+ handleColumnFilterStateChange(filters);
1037
+ },
1038
+ addColumnFilter: (columnId, operator, value) => {
1039
+ const newFilter = {
1040
+ id: `filter_${Date.now()}`,
1041
+ columnId,
1042
+ operator,
1043
+ value,
1044
+ };
1045
+ const current = table.getState().columnFilter;
1046
+ const currentFilters = (current === null || current === void 0 ? void 0 : current.filters) || [];
1047
+ const nextFilters = [...currentFilters, newFilter];
1048
+ handleColumnFilterStateChange({
1049
+ filters: nextFilters,
1050
+ logic: current === null || current === void 0 ? void 0 : current.logic,
1051
+ pendingFilters: (current === null || current === void 0 ? void 0 : current.pendingFilters) || [],
1052
+ pendingLogic: (current === null || current === void 0 ? void 0 : current.pendingLogic) || "AND",
1053
+ });
1054
+ if (logger.isLevelEnabled("debug")) {
1055
+ logger.debug(`Adding column filter ${columnId} ${operator} ${value}`, nextFilters);
976
1056
  }
977
- }
978
- },
979
- resetPagination: () => {
980
- const initialPagination = initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 };
981
- table.setPagination(initialPagination);
982
- // Manually trigger handler to ensure callbacks are called
983
- handlePaginationChange(initialPagination);
984
- },
985
- },
986
- // Access via table methods: table.selectRow(), table.getIsRowSelected(), etc.
987
- selection: {
988
- selectRow: (rowId) => { var _a; return (_a = table.selectRow) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
989
- deselectRow: (rowId) => { var _a; return (_a = table.deselectRow) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
990
- toggleRowSelection: (rowId) => { var _a; return (_a = table.toggleRowSelected) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
991
- selectAll: () => { var _a; return (_a = table.selectAll) === null || _a === void 0 ? void 0 : _a.call(table); },
992
- deselectAll: () => { var _a; return (_a = table.deselectAll) === null || _a === void 0 ? void 0 : _a.call(table); },
993
- toggleSelectAll: () => { var _a; return (_a = table.toggleAllRowsSelected) === null || _a === void 0 ? void 0 : _a.call(table); },
994
- getSelectionState: () => { var _a; return ((_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table)) || { ids: [], type: 'include' }; },
995
- getSelectedRows: () => table.getSelectedRows(),
996
- getSelectedCount: () => table.getSelectedCount(),
997
- isRowSelected: (rowId) => table.getIsRowSelected(rowId) || false,
998
- },
999
- // Data Management
1000
- data: {
1001
- refresh: (resetPagination = false) => {
1002
- var _a, _b, _c;
1003
- const filters = table.getState();
1004
- const pagination = {
1005
- pageIndex: resetPagination ? 0 : ((_a = initialStateConfig.pagination) === null || _a === void 0 ? void 0 : _a.pageIndex) || 0,
1006
- pageSize: ((_b = filters.pagination) === null || _b === void 0 ? void 0 : _b.pageSize) || ((_c = initialStateConfig.pagination) === null || _c === void 0 ? void 0 : _c.pageSize) || 10,
1007
- };
1008
- const allState = table.getState();
1009
- setPagination(pagination);
1010
- onDataStateChange === null || onDataStateChange === void 0 ? void 0 : onDataStateChange({ ...allState, pagination });
1011
- fetchData === null || fetchData === void 0 ? void 0 : fetchData({ pagination });
1012
- if (logger.isLevelEnabled('debug')) {
1013
- logger.debug('Refreshing data using Ref', { pagination, allState });
1014
- }
1015
- },
1016
- reload: () => {
1017
- const allState = table.getState();
1018
- onDataStateChange === null || onDataStateChange === void 0 ? void 0 : onDataStateChange(allState);
1019
- fetchData === null || fetchData === void 0 ? void 0 : fetchData({});
1020
- if (logger.isLevelEnabled('debug')) {
1021
- logger.info('Reloading data', allState);
1022
- }
1023
- },
1024
- // Data CRUD operations
1025
- getAllData: () => {
1026
- var _a;
1027
- return ((_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map(row => row.original)) || [];
1028
- },
1029
- getRowData: (rowId) => {
1030
- var _a, _b;
1031
- return (_b = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.find(row => String(row.original[idKey]) === rowId)) === null || _b === void 0 ? void 0 : _b.original;
1032
- },
1033
- getRowByIndex: (index) => {
1034
- var _a, _b;
1035
- return (_b = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.original;
1036
- },
1037
- updateRow: (rowId, updates) => {
1038
- var _a;
1039
- const newData = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map(row => String(row.original[idKey]) === rowId
1040
- ? {
1041
- ...row.original,
1042
- ...updates,
1057
+ },
1058
+ removeColumnFilter: (filterId) => {
1059
+ const current = table.getState().columnFilter;
1060
+ const currentFilters = (current === null || current === void 0 ? void 0 : current.filters) || [];
1061
+ const nextFilters = currentFilters.filter((f) => f.id !== filterId);
1062
+ handleColumnFilterStateChange({
1063
+ filters: nextFilters,
1064
+ logic: current === null || current === void 0 ? void 0 : current.logic,
1065
+ pendingFilters: (current === null || current === void 0 ? void 0 : current.pendingFilters) || [],
1066
+ pendingLogic: (current === null || current === void 0 ? void 0 : current.pendingLogic) || "AND",
1067
+ });
1068
+ if (logger.isLevelEnabled("debug")) {
1069
+ logger.debug(`Removing column filter ${filterId}`, nextFilters);
1043
1070
  }
1044
- : row.original);
1045
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData || []);
1046
- if (logger.isLevelEnabled('debug')) {
1047
- logger.debug(`Updating row ${rowId}`, updates);
1048
- }
1049
- },
1050
- updateRowByIndex: (index, updates) => {
1051
- var _a;
1052
- const newData = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map(row => row.original);
1053
- if (newData === null || newData === void 0 ? void 0 : newData[index]) {
1054
- newData[index] = {
1055
- ...newData[index],
1056
- ...updates,
1057
- };
1058
- setServerData(newData);
1059
- if (logger.isLevelEnabled('debug')) {
1060
- logger.debug(`Updating row by index ${index}`, updates);
1071
+ },
1072
+ clearAllFilters: () => {
1073
+ applyGlobalFilter("");
1074
+ handleColumnFilterStateChange({
1075
+ filters: [],
1076
+ logic: "AND",
1077
+ pendingFilters: [],
1078
+ pendingLogic: "AND",
1079
+ });
1080
+ },
1081
+ resetFilters: () => {
1082
+ handleColumnFilterStateChange({
1083
+ filters: [],
1084
+ logic: "AND",
1085
+ pendingFilters: [],
1086
+ pendingLogic: "AND",
1087
+ });
1088
+ if (logger.isLevelEnabled("debug")) {
1089
+ logger.debug("Resetting filters");
1061
1090
  }
1062
- }
1063
- },
1064
- insertRow: (newRow, index) => {
1065
- var _a;
1066
- const newData = ((_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map(row => row.original)) || [];
1067
- if (index !== undefined) {
1068
- newData.splice(index, 0, newRow);
1069
- }
1070
- else {
1071
- newData.push(newRow);
1072
- }
1073
- setServerData(newData || []);
1074
- if (logger.isLevelEnabled('debug')) {
1075
- logger.debug(`Inserting row`, newRow);
1076
- }
1077
- },
1078
- deleteRow: (rowId) => {
1079
- var _a;
1080
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.filter(row => String(row.original[idKey]) !== rowId);
1081
- setServerData === null || setServerData === void 0 ? void 0 : setServerData((newData === null || newData === void 0 ? void 0 : newData.map(row => row.original)) || []);
1082
- if (logger.isLevelEnabled('debug')) {
1083
- logger.debug(`Deleting row ${rowId}`);
1084
- }
1085
- },
1086
- deleteRowByIndex: (index) => {
1087
- var _a;
1088
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.map(row => row.original);
1089
- newData.splice(index, 1);
1090
- setServerData(newData);
1091
- if (logger.isLevelEnabled('debug')) {
1092
- logger.debug(`Deleting row by index ${index}`);
1093
- }
1094
- },
1095
- deleteSelectedRows: () => {
1096
- var _a, _b, _c;
1097
- const selectedRows = ((_a = table.getSelectedRows) === null || _a === void 0 ? void 0 : _a.call(table)) || [];
1098
- if (selectedRows.length === 0)
1099
- return;
1100
- const selectedIds = new Set(selectedRows.map(row => String(row.original[idKey])));
1101
- const newData = (_b = (table.getRowModel().rows || [])) === null || _b === void 0 ? void 0 : _b.filter(row => !selectedIds.has(String(row.original[idKey])));
1102
- setServerData((newData === null || newData === void 0 ? void 0 : newData.map(row => row.original)) || []);
1103
- (_c = table.deselectAll) === null || _c === void 0 ? void 0 : _c.call(table);
1104
- if (logger.isLevelEnabled('debug')) {
1105
- logger.debug('Deleting selected rows');
1106
- }
1107
- },
1108
- replaceAllData: (newData) => {
1109
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1110
- },
1111
- // Bulk operations
1112
- updateMultipleRows: (updates) => {
1113
- var _a;
1114
- const updateMap = new Map(updates.map(u => [u.rowId, u.data]));
1115
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.map(row => {
1116
- const rowId = String(row.original[idKey]);
1117
- const updateData = updateMap.get(rowId);
1118
- return updateData ? {
1119
- ...row.original,
1120
- ...updateData,
1121
- } : row.original;
1122
- });
1123
- setServerData(newData || []);
1124
- },
1125
- insertMultipleRows: (newRows, startIndex) => {
1126
- var _a;
1127
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.map(row => row.original);
1128
- if (startIndex !== undefined) {
1129
- newData.splice(startIndex, 0, ...newRows);
1130
- }
1131
- else {
1132
- newData.push(...newRows);
1133
- }
1134
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1135
- },
1136
- deleteMultipleRows: (rowIds) => {
1137
- var _a, _b;
1138
- const idsToDelete = new Set(rowIds);
1139
- const newData = (_b = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.filter(row => !idsToDelete.has(String(row.original[idKey])))) === null || _b === void 0 ? void 0 : _b.map(row => row.original);
1140
- setServerData(newData);
1141
- },
1142
- // Field-specific updates
1143
- updateField: (rowId, fieldName, value) => {
1144
- var _a;
1145
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.map(row => String(row.original[idKey]) === rowId
1146
- ? {
1147
- ...row.original,
1148
- [fieldName]: value,
1091
+ },
1092
+ },
1093
+ // -------------------------------
1094
+ // Sorting
1095
+ // -------------------------------
1096
+ sorting: {
1097
+ setSorting: (sortingState) => {
1098
+ applySorting(sortingState);
1099
+ if (logger.isLevelEnabled("debug"))
1100
+ logger.debug("Setting sorting", sortingState);
1101
+ },
1102
+ // NOTE: toggleSorting is okay, but can become "one behind" in controlled server mode.
1103
+ // So we implement deterministic sorting through handler.
1104
+ sortColumn: (columnId, direction) => {
1105
+ const current = table.getState().sorting || [];
1106
+ const filtered = current.filter((s) => s.id !== columnId);
1107
+ if (direction === false) {
1108
+ applySorting(filtered);
1109
+ return;
1149
1110
  }
1150
- : row.original);
1151
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1152
- },
1153
- updateFieldByIndex: (index, fieldName, value) => {
1154
- var _a;
1155
- const newData = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.map(row => row.original);
1156
- if (newData[index]) {
1157
- newData[index] = {
1158
- ...newData[index],
1159
- [fieldName]: value,
1160
- };
1161
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1162
- }
1163
- },
1164
- // Data queries
1165
- findRows: (predicate) => {
1166
- var _a, _b;
1167
- return (_b = (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.filter(row => predicate(row.original))) === null || _b === void 0 ? void 0 : _b.map(row => row.original);
1168
- },
1169
- findRowIndex: (predicate) => {
1170
- var _a;
1171
- return (_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.findIndex(row => predicate(row.original));
1172
- },
1173
- getDataCount: () => {
1174
- var _a;
1175
- return ((_a = (table.getRowModel().rows || [])) === null || _a === void 0 ? void 0 : _a.length) || 0;
1176
- },
1177
- getFilteredDataCount: () => {
1178
- return table.getFilteredRowModel().rows.length;
1179
- },
1180
- },
1181
- // Layout Management
1182
- layout: {
1183
- resetLayout: () => {
1184
- table.resetColumnSizing();
1185
- table.resetColumnVisibility();
1186
- table.resetSorting();
1187
- table.resetGlobalFilter();
1188
- },
1189
- resetAll: () => {
1190
- // Reset everything to initial state
1191
- table.resetColumnSizing();
1192
- table.resetColumnVisibility();
1193
- table.resetSorting();
1194
- table.resetGlobalFilter();
1195
- table.resetColumnOrder();
1196
- table.resetExpanded();
1197
- handleSelectionStateChange(initialSelectionState);
1198
- table.resetColumnPinning();
1199
- handleColumnFilterStateChange(initialStateConfig.columnFilter || { filters: [], logic: 'AND', pendingFilters: [], pendingLogic: 'AND' });
1200
- if (enablePagination) {
1201
- table.setPagination(initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 });
1202
- }
1203
- if (enableColumnPinning) {
1204
- table.setColumnPinning(initialStateConfig.columnPinning || { left: [], right: [] });
1205
- }
1206
- },
1207
- saveLayout: () => {
1208
- return {
1111
+ applySorting([{ id: columnId, desc: direction === "desc" }, ...filtered]);
1112
+ },
1113
+ clearSorting: () => {
1114
+ applySorting([]);
1115
+ },
1116
+ resetSorting: () => {
1117
+ const initialSorting = initialStateConfig.sorting || [];
1118
+ applySorting(initialSorting);
1119
+ },
1120
+ },
1121
+ // -------------------------------
1122
+ // Pagination
1123
+ // -------------------------------
1124
+ pagination: {
1125
+ goToPage: (pageIndex) => {
1126
+ applyPagination((prev) => ({ ...prev, pageIndex }));
1127
+ if (logger.isLevelEnabled("debug"))
1128
+ logger.debug(`Going to page ${pageIndex}`);
1129
+ },
1130
+ nextPage: () => {
1131
+ applyPagination((prev) => { var _a; return ({ ...prev, pageIndex: ((_a = prev === null || prev === void 0 ? void 0 : prev.pageIndex) !== null && _a !== void 0 ? _a : 0) + 1 }); });
1132
+ if (logger.isLevelEnabled("debug"))
1133
+ logger.debug("Next page");
1134
+ },
1135
+ previousPage: () => {
1136
+ applyPagination((prev) => { var _a; return ({ ...prev, pageIndex: Math.max(0, ((_a = prev === null || prev === void 0 ? void 0 : prev.pageIndex) !== null && _a !== void 0 ? _a : 0) - 1) }); });
1137
+ if (logger.isLevelEnabled("debug"))
1138
+ logger.debug("Previous page");
1139
+ },
1140
+ setPageSize: (pageSize) => {
1141
+ // usually want pageIndex reset
1142
+ applyPagination(() => ({ pageIndex: 0, pageSize }));
1143
+ if (logger.isLevelEnabled("debug"))
1144
+ logger.debug(`Setting page size to ${pageSize}`);
1145
+ },
1146
+ goToFirstPage: () => {
1147
+ applyPagination((prev) => ({ ...prev, pageIndex: 0 }));
1148
+ if (logger.isLevelEnabled("debug"))
1149
+ logger.debug("Going to first page");
1150
+ },
1151
+ goToLastPage: () => {
1152
+ var _a, _b;
1153
+ // pageCount can be derived; keep safe fallback
1154
+ const pageCount = (_b = (_a = table.getPageCount) === null || _a === void 0 ? void 0 : _a.call(table)) !== null && _b !== void 0 ? _b : 0;
1155
+ if (pageCount > 0) {
1156
+ applyPagination((prev) => ({ ...prev, pageIndex: pageCount - 1 }));
1157
+ if (logger.isLevelEnabled("debug"))
1158
+ logger.debug(`Going to last page ${pageCount - 1}`);
1159
+ }
1160
+ },
1161
+ resetPagination: () => {
1162
+ const initialPagination = initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 };
1163
+ applyPagination(initialPagination);
1164
+ },
1165
+ },
1166
+ // -------------------------------
1167
+ // Selection
1168
+ // -------------------------------
1169
+ selection: {
1170
+ selectRow: (rowId) => { var _a; return (_a = table.selectRow) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
1171
+ deselectRow: (rowId) => { var _a; return (_a = table.deselectRow) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
1172
+ toggleRowSelection: (rowId) => { var _a; return (_a = table.toggleRowSelected) === null || _a === void 0 ? void 0 : _a.call(table, rowId); },
1173
+ selectAll: () => { var _a; return (_a = table.selectAll) === null || _a === void 0 ? void 0 : _a.call(table); },
1174
+ deselectAll: () => { var _a; return (_a = table.deselectAll) === null || _a === void 0 ? void 0 : _a.call(table); },
1175
+ toggleSelectAll: () => { var _a; return (_a = table.toggleAllRowsSelected) === null || _a === void 0 ? void 0 : _a.call(table); },
1176
+ getSelectionState: () => { var _a; return ((_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table)) || { ids: [], type: "include" }; },
1177
+ getSelectedRows: () => table.getSelectedRows(),
1178
+ getSelectedCount: () => table.getSelectedCount(),
1179
+ isRowSelected: (rowId) => table.getIsRowSelected(rowId) || false,
1180
+ },
1181
+ // -------------------------------
1182
+ // Data Management (kept same, but ensure state changes go through handlers)
1183
+ // -------------------------------
1184
+ data: {
1185
+ refresh: (options) => {
1186
+ void triggerRefresh(options, 'refresh');
1187
+ },
1188
+ reload: (options = {}) => {
1189
+ var _a, _b;
1190
+ void triggerRefresh({
1191
+ ...options,
1192
+ resetPagination: (_a = options.resetPagination) !== null && _a !== void 0 ? _a : false,
1193
+ reason: (_b = options.reason) !== null && _b !== void 0 ? _b : 'reload',
1194
+ }, 'reload');
1195
+ },
1196
+ resetAll: () => resetAllAndReload(),
1197
+ getAllData: () => [...tableData],
1198
+ getRowData: (rowId) => {
1199
+ const rowIndex = getRowIndexById(tableData, rowId);
1200
+ return rowIndex === -1 ? undefined : tableData[rowIndex];
1201
+ },
1202
+ getRowByIndex: (index) => tableData[index],
1203
+ updateRow: (rowId, updates) => {
1204
+ applyDataMutation('updateRow', (rowsToMutate) => {
1205
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1206
+ if (rowIndex === -1)
1207
+ return rowsToMutate;
1208
+ const nextData = [...rowsToMutate];
1209
+ nextData[rowIndex] = { ...nextData[rowIndex], ...updates };
1210
+ return nextData;
1211
+ }, { rowId });
1212
+ },
1213
+ updateRowByIndex: (index, updates) => {
1214
+ applyDataMutation('updateRowByIndex', (rowsToMutate) => {
1215
+ if (!rowsToMutate[index])
1216
+ return rowsToMutate;
1217
+ const nextData = [...rowsToMutate];
1218
+ nextData[index] = { ...nextData[index], ...updates };
1219
+ return nextData;
1220
+ }, { index });
1221
+ },
1222
+ insertRow: (newRow, index) => {
1223
+ applyDataMutation('insertRow', (rowsToMutate) => {
1224
+ const nextData = [...rowsToMutate];
1225
+ nextData.splice(clampInsertIndex(nextData, index), 0, newRow);
1226
+ return nextData;
1227
+ }, { index });
1228
+ },
1229
+ deleteRow: (rowId) => {
1230
+ applyDataMutation('deleteRow', (rowsToMutate) => {
1231
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1232
+ if (rowIndex === -1)
1233
+ return rowsToMutate;
1234
+ const nextData = [...rowsToMutate];
1235
+ nextData.splice(rowIndex, 1);
1236
+ return nextData;
1237
+ }, { rowId });
1238
+ },
1239
+ deleteRowByIndex: (index) => {
1240
+ applyDataMutation('deleteRowByIndex', (rowsToMutate) => {
1241
+ if (index < 0 || index >= rowsToMutate.length)
1242
+ return rowsToMutate;
1243
+ const nextData = [...rowsToMutate];
1244
+ nextData.splice(index, 1);
1245
+ return nextData;
1246
+ }, { index });
1247
+ },
1248
+ deleteSelectedRows: () => {
1249
+ var _a, _b, _c;
1250
+ const currentSelection = ((_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table)) || selectionState;
1251
+ const selectedIds = new Set((currentSelection.ids || []).map((id) => String(id)));
1252
+ const loadedRowIds = tableData.map((row, index) => String((0, utils_1.generateRowId)(row, index, idKey)));
1253
+ const deletableRowIds = currentSelection.type === 'exclude'
1254
+ ? loadedRowIds.filter((rowId) => !selectedIds.has(rowId))
1255
+ : loadedRowIds.filter((rowId) => selectedIds.has(rowId));
1256
+ if (deletableRowIds.length === 0)
1257
+ return;
1258
+ if (currentSelection.type === 'exclude'
1259
+ && table.getRowCount() > loadedRowIds.length
1260
+ && logger.isLevelEnabled('info')) {
1261
+ logger.info('deleteSelectedRows in exclude mode removed currently loaded rows only', {
1262
+ removedRows: deletableRowIds.length,
1263
+ totalSelected: (_b = table.getSelectedCount) === null || _b === void 0 ? void 0 : _b.call(table),
1264
+ });
1265
+ }
1266
+ const deletableRowIdSet = new Set(deletableRowIds);
1267
+ applyDataMutation('deleteSelectedRows', (rowsToMutate) => rowsToMutate.filter((row, index) => !deletableRowIdSet.has(String((0, utils_1.generateRowId)(row, index, idKey)))), { rowIds: deletableRowIds });
1268
+ (_c = table.deselectAll) === null || _c === void 0 ? void 0 : _c.call(table);
1269
+ },
1270
+ replaceAllData: (newData) => {
1271
+ applyDataMutation('replaceAllData', () => [...newData]);
1272
+ },
1273
+ updateMultipleRows: (updates) => {
1274
+ const updateMap = new Map(updates.map((update) => [update.rowId, update.data]));
1275
+ applyDataMutation('updateMultipleRows', (rowsToMutate) => rowsToMutate.map((row, index) => {
1276
+ const currentRowId = String((0, utils_1.generateRowId)(row, index, idKey));
1277
+ const updateData = updateMap.get(currentRowId);
1278
+ return updateData ? { ...row, ...updateData } : row;
1279
+ }));
1280
+ },
1281
+ insertMultipleRows: (newRows, startIndex) => {
1282
+ applyDataMutation('insertMultipleRows', (rowsToMutate) => {
1283
+ const nextData = [...rowsToMutate];
1284
+ nextData.splice(clampInsertIndex(nextData, startIndex), 0, ...newRows);
1285
+ return nextData;
1286
+ }, { index: startIndex });
1287
+ },
1288
+ deleteMultipleRows: (rowIds) => {
1289
+ const idsToDelete = new Set(rowIds);
1290
+ applyDataMutation('deleteMultipleRows', (rowsToMutate) => rowsToMutate.filter((row, index) => !idsToDelete.has(String((0, utils_1.generateRowId)(row, index, idKey)))), { rowIds });
1291
+ },
1292
+ updateField: (rowId, fieldName, value) => {
1293
+ applyDataMutation('updateField', (rowsToMutate) => {
1294
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1295
+ if (rowIndex === -1)
1296
+ return rowsToMutate;
1297
+ const nextData = [...rowsToMutate];
1298
+ nextData[rowIndex] = { ...nextData[rowIndex], [fieldName]: value };
1299
+ return nextData;
1300
+ }, { rowId });
1301
+ },
1302
+ updateFieldByIndex: (index, fieldName, value) => {
1303
+ applyDataMutation('updateFieldByIndex', (rowsToMutate) => {
1304
+ if (!rowsToMutate[index])
1305
+ return rowsToMutate;
1306
+ const nextData = [...rowsToMutate];
1307
+ nextData[index] = { ...nextData[index], [fieldName]: value };
1308
+ return nextData;
1309
+ }, { index });
1310
+ },
1311
+ findRows: (predicate) => tableData.filter(predicate),
1312
+ findRowIndex: (predicate) => tableData.findIndex(predicate),
1313
+ getDataCount: () => tableData.length,
1314
+ getFilteredDataCount: () => table.getFilteredRowModel().rows.length,
1315
+ },
1316
+ // -------------------------------
1317
+ // Layout Management
1318
+ // -------------------------------
1319
+ layout: {
1320
+ resetLayout: () => {
1321
+ var _a;
1322
+ // go through handlers so controlled state updates + emit works
1323
+ applySizing(initialStateConfig.columnSizing || {});
1324
+ applyVisibility(initialStateConfig.columnVisibility || {});
1325
+ applySorting(initialStateConfig.sorting || []);
1326
+ applyGlobalFilter((_a = initialStateConfig.globalFilter) !== null && _a !== void 0 ? _a : "");
1327
+ },
1328
+ resetAll: () => resetAllAndReload(),
1329
+ saveLayout: () => ({
1209
1330
  columnVisibility: table.getState().columnVisibility,
1210
1331
  columnSizing: table.getState().columnSizing,
1211
1332
  columnOrder: table.getState().columnOrder,
@@ -1214,249 +1335,289 @@ logging, }, ref) {
1214
1335
  pagination: table.getState().pagination,
1215
1336
  globalFilter: table.getState().globalFilter,
1216
1337
  columnFilter: table.getState().columnFilter,
1217
- };
1218
- },
1219
- restoreLayout: (layout) => {
1220
- if (layout.columnVisibility) {
1221
- table.setColumnVisibility(layout.columnVisibility);
1222
- }
1223
- if (layout.columnSizing) {
1224
- table.setColumnSizing(layout.columnSizing);
1225
- }
1226
- if (layout.columnOrder) {
1227
- table.setColumnOrder(layout.columnOrder);
1228
- }
1229
- if (layout.columnPinning) {
1230
- table.setColumnPinning(layout.columnPinning);
1231
- }
1232
- if (layout.sorting) {
1233
- table.setSorting(layout.sorting);
1234
- }
1235
- if (layout.pagination && enablePagination) {
1236
- table.setPagination(layout.pagination);
1237
- }
1238
- if (layout.globalFilter !== undefined) {
1239
- table.setGlobalFilter(layout.globalFilter);
1240
- }
1241
- if (layout.columnFilter) {
1242
- handleColumnFilterStateChange(layout.columnFilter);
1243
- }
1244
- },
1245
- },
1246
- // Table State
1247
- state: {
1248
- getTableState: () => {
1249
- return table.getState();
1250
- },
1251
- getCurrentFilters: () => {
1252
- return table.getState().columnFilter;
1253
- },
1254
- getCurrentSorting: () => {
1255
- return table.getState().sorting;
1256
- },
1257
- getCurrentPagination: () => {
1258
- return table.getState().pagination;
1259
- },
1260
- // Backward compatibility: expose the raw selection array expected by older consumers
1261
- getCurrentSelection: () => {
1262
- var _a;
1263
- return (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table);
1264
- },
1265
- },
1266
- // Simplified Export
1267
- export: {
1268
- exportCSV: async (options = {}) => {
1269
- var _a;
1270
- const { filename = exportFilename, } = options;
1271
- try {
1272
- // Create abort controller for this export
1273
- const controller = new AbortController();
1274
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1275
- if (dataMode === 'server' && onServerExport) {
1276
- // Server export with selection data
1277
- const currentFilters = {
1278
- globalFilter: table.getState().globalFilter,
1279
- columnFilter: table.getState().columnFilter,
1280
- sorting: table.getState().sorting,
1281
- pagination: table.getState().pagination,
1282
- };
1283
- if (logger.isLevelEnabled('debug')) {
1284
- logger.debug('Server export CSV', { currentFilters });
1285
- }
1286
- await (0, utils_1.exportServerData)(table, {
1287
- format: 'csv',
1288
- filename,
1289
- fetchData: (filters, selection) => onServerExport(filters, selection),
1290
- currentFilters,
1291
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1292
- onProgress: onExportProgress,
1293
- onComplete: onExportComplete,
1294
- onError: onExportError,
1295
- });
1296
- }
1297
- else {
1298
- // Client export - auto-detect selected rows if not specified
1299
- await (0, utils_1.exportClientData)(table, {
1300
- format: 'csv',
1301
- filename,
1302
- onProgress: onExportProgress,
1303
- onComplete: onExportComplete,
1304
- onError: onExportError,
1305
- });
1306
- if (logger.isLevelEnabled('debug')) {
1307
- logger.debug('Client export CSV', filename);
1338
+ }),
1339
+ restoreLayout: (layout) => {
1340
+ if (layout.columnVisibility)
1341
+ applyVisibility(layout.columnVisibility);
1342
+ if (layout.columnSizing)
1343
+ applySizing(layout.columnSizing);
1344
+ if (layout.columnOrder)
1345
+ applyColumnOrder(layout.columnOrder);
1346
+ if (layout.columnPinning)
1347
+ applyPinning(layout.columnPinning);
1348
+ if (layout.sorting)
1349
+ applySorting(layout.sorting);
1350
+ if (layout.pagination && enablePagination)
1351
+ applyPagination(layout.pagination);
1352
+ if (layout.globalFilter !== undefined)
1353
+ applyGlobalFilter(layout.globalFilter);
1354
+ if (layout.columnFilter)
1355
+ handleColumnFilterStateChange(layout.columnFilter);
1356
+ },
1357
+ },
1358
+ // -------------------------------
1359
+ // Table State
1360
+ // -------------------------------
1361
+ state: {
1362
+ getTableState: () => table.getState(),
1363
+ getCurrentFilters: () => table.getState().columnFilter,
1364
+ getCurrentSorting: () => table.getState().sorting,
1365
+ getCurrentPagination: () => table.getState().pagination,
1366
+ getCurrentSelection: () => { var _a; return (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table); },
1367
+ },
1368
+ // -------------------------------
1369
+ // Export (unchanged mostly)
1370
+ // -------------------------------
1371
+ export: {
1372
+ exportCSV: async (options = {}) => {
1373
+ const { filename = exportFilename, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1374
+ const mode = dataMode === "server" && !!onServerExport ? 'server' : 'client';
1375
+ await runExportWithPolicy({
1376
+ format: 'csv',
1377
+ filename,
1378
+ mode,
1379
+ execute: async (controller) => {
1380
+ var _a;
1381
+ const toStateChange = (state) => {
1382
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1383
+ handleExportStateChangeInternal({
1384
+ phase: state.phase,
1385
+ mode,
1386
+ format: 'csv',
1387
+ filename,
1388
+ processedRows: state.processedRows,
1389
+ totalRows: state.totalRows,
1390
+ percentage: state.percentage,
1391
+ message: state.message,
1392
+ code: state.code,
1393
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1394
+ endedAt: isFinalPhase ? Date.now() : undefined,
1395
+ queueLength: queuedExportCount,
1396
+ });
1397
+ if (state.phase === 'cancelled') {
1398
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1399
+ }
1400
+ };
1401
+ if (mode === 'server' && onServerExport) {
1402
+ const currentFilters = {
1403
+ globalFilter: table.getState().globalFilter,
1404
+ columnFilter: table.getState().columnFilter,
1405
+ sorting: table.getState().sorting,
1406
+ pagination: table.getState().pagination,
1407
+ };
1408
+ if (logger.isLevelEnabled("debug"))
1409
+ logger.debug("Server export CSV", { currentFilters });
1410
+ await (0, utils_1.exportServerData)(table, {
1411
+ format: "csv",
1412
+ filename,
1413
+ fetchData: (filters, selection, signal) => onServerExport(filters, selection, signal),
1414
+ currentFilters,
1415
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1416
+ onProgress: handleExportProgressInternal,
1417
+ onComplete: onExportComplete,
1418
+ onError: onExportError,
1419
+ onStateChange: toStateChange,
1420
+ signal: controller.signal,
1421
+ chunkSize,
1422
+ strictTotalCheck,
1423
+ sanitizeCSV,
1424
+ });
1425
+ return;
1426
+ }
1427
+ await (0, utils_1.exportClientData)(table, {
1428
+ format: "csv",
1429
+ filename,
1430
+ onProgress: handleExportProgressInternal,
1431
+ onComplete: onExportComplete,
1432
+ onError: onExportError,
1433
+ onStateChange: toStateChange,
1434
+ signal: controller.signal,
1435
+ sanitizeCSV,
1436
+ });
1437
+ if (logger.isLevelEnabled("debug"))
1438
+ logger.debug("Client export CSV", filename);
1308
1439
  }
1309
- }
1310
- }
1311
- catch (error) {
1312
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({
1313
- message: error.message || 'Export failed',
1314
- code: 'EXPORT_ERROR',
1315
1440
  });
1316
- }
1317
- finally {
1318
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1319
- }
1320
- },
1321
- exportExcel: async (options = {}) => {
1322
- var _a;
1323
- const { filename = exportFilename } = options;
1324
- try {
1325
- // Create abort controller for this export
1326
- const controller = new AbortController();
1327
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1328
- if (dataMode === 'server' && onServerExport) {
1329
- // Server export with selection data
1330
- const currentFilters = {
1331
- globalFilter: table.getState().globalFilter,
1332
- columnFilter: table.getState().columnFilter,
1333
- sorting: table.getState().sorting,
1334
- pagination: table.getState().pagination,
1335
- };
1336
- if (logger.isLevelEnabled('debug')) {
1337
- logger.debug('Server export Excel', { currentFilters });
1338
- }
1339
- await (0, utils_1.exportServerData)(table, {
1340
- format: 'excel',
1341
- filename,
1342
- fetchData: (filters, selection) => onServerExport(filters, selection),
1343
- currentFilters,
1344
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1345
- onProgress: onExportProgress,
1346
- onComplete: onExportComplete,
1347
- onError: onExportError,
1348
- });
1349
- }
1350
- else {
1351
- // Client export - auto-detect selected rows if not specified
1352
- await (0, utils_1.exportClientData)(table, {
1353
- format: 'excel',
1354
- filename,
1355
- onProgress: onExportProgress,
1356
- onComplete: onExportComplete,
1357
- onError: onExportError,
1358
- });
1359
- if (logger.isLevelEnabled('debug')) {
1360
- logger.debug('Client export Excel', filename);
1441
+ },
1442
+ exportExcel: async (options = {}) => {
1443
+ const { filename = exportFilename, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1444
+ const mode = dataMode === "server" && !!onServerExport ? 'server' : 'client';
1445
+ await runExportWithPolicy({
1446
+ format: 'excel',
1447
+ filename,
1448
+ mode,
1449
+ execute: async (controller) => {
1450
+ var _a;
1451
+ const toStateChange = (state) => {
1452
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1453
+ handleExportStateChangeInternal({
1454
+ phase: state.phase,
1455
+ mode,
1456
+ format: 'excel',
1457
+ filename,
1458
+ processedRows: state.processedRows,
1459
+ totalRows: state.totalRows,
1460
+ percentage: state.percentage,
1461
+ message: state.message,
1462
+ code: state.code,
1463
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1464
+ endedAt: isFinalPhase ? Date.now() : undefined,
1465
+ queueLength: queuedExportCount,
1466
+ });
1467
+ if (state.phase === 'cancelled') {
1468
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1469
+ }
1470
+ };
1471
+ if (mode === 'server' && onServerExport) {
1472
+ const currentFilters = {
1473
+ globalFilter: table.getState().globalFilter,
1474
+ columnFilter: table.getState().columnFilter,
1475
+ sorting: table.getState().sorting,
1476
+ pagination: table.getState().pagination,
1477
+ };
1478
+ if (logger.isLevelEnabled("debug"))
1479
+ logger.debug("Server export Excel", { currentFilters });
1480
+ await (0, utils_1.exportServerData)(table, {
1481
+ format: "excel",
1482
+ filename,
1483
+ fetchData: (filters, selection, signal) => onServerExport(filters, selection, signal),
1484
+ currentFilters,
1485
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1486
+ onProgress: handleExportProgressInternal,
1487
+ onComplete: onExportComplete,
1488
+ onError: onExportError,
1489
+ onStateChange: toStateChange,
1490
+ signal: controller.signal,
1491
+ chunkSize,
1492
+ strictTotalCheck,
1493
+ sanitizeCSV,
1494
+ });
1495
+ return;
1496
+ }
1497
+ await (0, utils_1.exportClientData)(table, {
1498
+ format: "excel",
1499
+ filename,
1500
+ onProgress: handleExportProgressInternal,
1501
+ onComplete: onExportComplete,
1502
+ onError: onExportError,
1503
+ onStateChange: toStateChange,
1504
+ signal: controller.signal,
1505
+ sanitizeCSV,
1506
+ });
1507
+ if (logger.isLevelEnabled("debug"))
1508
+ logger.debug("Client export Excel", filename);
1361
1509
  }
1362
- }
1363
- }
1364
- catch (error) {
1365
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({
1366
- message: error.message || 'Export failed',
1367
- code: 'EXPORT_ERROR',
1368
- });
1369
- if (logger.isLevelEnabled('debug')) {
1370
- logger.debug('Server export Excel failed', error);
1371
- }
1372
- }
1373
- finally {
1374
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1375
- }
1376
- },
1377
- exportServerData: async (options) => {
1378
- var _a;
1379
- const { format, filename = exportFilename, fetchData = onServerExport, } = options;
1380
- if (!fetchData) {
1381
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({
1382
- message: 'No server export function provided',
1383
- code: 'NO_SERVER_EXPORT',
1384
1510
  });
1385
- if (logger.isLevelEnabled('debug')) {
1386
- logger.debug('Server export data failed', 'No server export function provided');
1387
- }
1388
- return;
1389
- }
1390
- try {
1391
- // Create abort controller for this export
1392
- const controller = new AbortController();
1393
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1394
- const currentFilters = {
1395
- globalFilter: table.getState().globalFilter,
1396
- columnFilter: table.getState().columnFilter,
1397
- sorting: table.getState().sorting,
1398
- pagination: table.getState().pagination,
1399
- };
1400
- if (logger.isLevelEnabled('debug')) {
1401
- logger.debug('Server export data', { currentFilters });
1511
+ },
1512
+ exportServerData: async (options) => {
1513
+ const { format, filename = exportFilename, fetchData: fetchFn = onServerExport, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1514
+ if (!fetchFn) {
1515
+ onExportError === null || onExportError === void 0 ? void 0 : onExportError({ message: "No server export function provided", code: "NO_SERVER_EXPORT" });
1516
+ if (logger.isLevelEnabled("debug"))
1517
+ logger.debug("Server export data failed", "No server export function provided");
1518
+ return;
1402
1519
  }
1403
- await (0, utils_1.exportServerData)(table, {
1520
+ await runExportWithPolicy({
1404
1521
  format,
1405
1522
  filename,
1406
- fetchData: (filters, selection) => fetchData(filters, selection),
1407
- currentFilters,
1408
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1409
- onProgress: onExportProgress,
1410
- onComplete: onExportComplete,
1411
- onError: onExportError,
1412
- });
1413
- }
1414
- catch (error) {
1415
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({
1416
- message: error.message || 'Export failed',
1417
- code: 'EXPORT_ERROR',
1523
+ mode: 'server',
1524
+ execute: async (controller) => {
1525
+ var _a;
1526
+ const currentFilters = {
1527
+ globalFilter: table.getState().globalFilter,
1528
+ columnFilter: table.getState().columnFilter,
1529
+ sorting: table.getState().sorting,
1530
+ pagination: table.getState().pagination,
1531
+ };
1532
+ if (logger.isLevelEnabled("debug"))
1533
+ logger.debug("Server export data", { currentFilters });
1534
+ await (0, utils_1.exportServerData)(table, {
1535
+ format,
1536
+ filename,
1537
+ fetchData: (filters, selection, signal) => fetchFn(filters, selection, signal),
1538
+ currentFilters,
1539
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1540
+ onProgress: handleExportProgressInternal,
1541
+ onComplete: onExportComplete,
1542
+ onError: onExportError,
1543
+ onStateChange: (state) => {
1544
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1545
+ handleExportStateChangeInternal({
1546
+ phase: state.phase,
1547
+ mode: 'server',
1548
+ format,
1549
+ filename,
1550
+ processedRows: state.processedRows,
1551
+ totalRows: state.totalRows,
1552
+ percentage: state.percentage,
1553
+ message: state.message,
1554
+ code: state.code,
1555
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1556
+ endedAt: isFinalPhase ? Date.now() : undefined,
1557
+ queueLength: queuedExportCount,
1558
+ });
1559
+ if (state.phase === 'cancelled') {
1560
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1561
+ }
1562
+ },
1563
+ signal: controller.signal,
1564
+ chunkSize,
1565
+ strictTotalCheck,
1566
+ sanitizeCSV,
1567
+ });
1568
+ }
1418
1569
  });
1419
- if (logger.isLevelEnabled('debug')) {
1420
- logger.debug('Server export data failed', error);
1570
+ },
1571
+ isExporting: () => isExporting || false,
1572
+ cancelExport: () => {
1573
+ const activeController = exportControllerRef.current;
1574
+ if (!activeController) {
1575
+ return;
1421
1576
  }
1422
- }
1423
- finally {
1424
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1425
- }
1577
+ activeController.abort();
1578
+ setExportControllerSafely((current) => (current === activeController ? null : current));
1579
+ if (logger.isLevelEnabled("debug"))
1580
+ logger.debug("Export cancelled");
1581
+ },
1426
1582
  },
1427
- // Export state
1428
- isExporting: () => isExporting || false,
1429
- cancelExport: () => {
1430
- exportController === null || exportController === void 0 ? void 0 : exportController.abort();
1431
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1432
- if (logger.isLevelEnabled('debug')) {
1433
- logger.debug('Export cancelled');
1434
- }
1435
- },
1436
- },
1583
+ };
1437
1584
  // eslint-disable-next-line react-hooks/exhaustive-deps
1438
- }), [
1585
+ }, [
1439
1586
  table,
1440
1587
  enhancedColumns,
1588
+ handleColumnOrderChange,
1589
+ handleColumnPinningChange,
1590
+ handleColumnVisibilityChange,
1591
+ handleColumnSizingChange,
1592
+ handlePaginationChange,
1593
+ handleSortingChange,
1594
+ handleGlobalFilterChange,
1441
1595
  handleColumnFilterStateChange,
1442
- idKey,
1443
- onDataStateChange,
1444
- fetchData,
1445
- enableColumnPinning,
1596
+ initialStateConfig,
1446
1597
  enablePagination,
1447
- // Export dependencies
1598
+ idKey,
1599
+ triggerRefresh,
1600
+ applyDataMutation,
1601
+ tableData,
1602
+ selectionState,
1603
+ // export
1448
1604
  exportFilename,
1449
- onExportProgress,
1605
+ exportChunkSize,
1606
+ exportStrictTotalCheck,
1607
+ exportSanitizeCSV,
1450
1608
  onExportComplete,
1451
1609
  onExportError,
1610
+ onExportCancel,
1452
1611
  onServerExport,
1453
- exportController,
1454
- setExportController,
1612
+ queuedExportCount,
1455
1613
  isExporting,
1456
1614
  dataMode,
1457
- selectMode,
1458
- onSelectionChange,
1459
- // Note: custom selection removed from dependency array
1615
+ handleExportProgressInternal,
1616
+ handleExportStateChangeInternal,
1617
+ runExportWithPolicy,
1618
+ setExportControllerSafely,
1619
+ logger,
1620
+ resetAllAndReload,
1460
1621
  ]);
1461
1622
  internalApiRef.current = dataTableApi;
1462
1623
  (0, react_1.useImperativeHandle)(ref, () => dataTableApi, [dataTableApi]);
@@ -1515,14 +1676,12 @@ logging, }, ref) {
1515
1676
  // Export cancel callback
1516
1677
  // -------------------------------
1517
1678
  const handleCancelExport = (0, react_1.useCallback)(() => {
1518
- if (exportController) {
1519
- exportController.abort();
1520
- setExportController(null);
1521
- if (onExportCancel) {
1522
- onExportCancel();
1523
- }
1679
+ const activeController = exportControllerRef.current;
1680
+ if (activeController) {
1681
+ activeController.abort();
1682
+ setExportControllerSafely((current) => (current === activeController ? null : current));
1524
1683
  }
1525
- }, [exportController, onExportCancel]);
1684
+ }, [setExportControllerSafely]);
1526
1685
  // -------------------------------
1527
1686
  // Slot components
1528
1687
  // -------------------------------
@@ -1539,7 +1698,12 @@ logging, }, ref) {
1539
1698
  // -------------------------------
1540
1699
  return ((0, jsx_runtime_1.jsx)(data_table_context_1.DataTableProvider, { table: table, apiRef: internalApiRef, dataMode: dataMode, tableSize: tableSize, onTableSizeChange: (size) => {
1541
1700
  setTableSize(size);
1542
- }, columnFilter: columnFilter, onChangeColumnFilter: handleColumnFilterStateChange, slots: slots, slotProps: slotProps, isExporting: isExporting, exportController: exportController, onCancelExport: handleCancelExport, exportFilename: exportFilename, onExportProgress: onExportProgress, onExportComplete: onExportComplete, onExportError: onExportError, onServerExport: onServerExport, children: (0, jsx_runtime_1.jsxs)(RootComponent, { ...rootSlotProps, children: [(enableGlobalFilter || extraFilter) ? ((0, jsx_runtime_1.jsx)(ToolbarComponent, { extraFilter: extraFilter, enableGlobalFilter: enableGlobalFilter, enableColumnVisibility: enableColumnVisibility, enableColumnFilter: enableColumnFilter, enableExport: enableExport, enableReset: enableReset, enableTableSizeControl: enableTableSizeControl, enableColumnPinning: enableColumnPinning, ...toolbarSlotProps })) : null, enableBulkActions && enableRowSelection && isSomeRowsSelected ? ((0, jsx_runtime_1.jsx)(BulkActionsComponent, { selectionState: selectionState, selectedRowCount: selectedRowCount, bulkActions: bulkActions, sx: {
1701
+ }, columnFilter: columnFilter, onChangeColumnFilter: handleColumnFilterStateChange, slots: slots, slotProps: slotProps, isExporting: isExporting, exportController: exportController, exportPhase: exportPhase, exportProgress: exportProgress, onCancelExport: handleCancelExport, exportFilename: exportFilename, onExportProgress: onExportProgress, onExportComplete: onExportComplete, onExportError: onExportError, onServerExport: onServerExport, children: (0, jsx_runtime_1.jsxs)(RootComponent, { ...rootSlotProps, children: [(enableGlobalFilter || extraFilter) ? ((0, jsx_runtime_1.jsx)(ToolbarComponent, { extraFilter: extraFilter, enableGlobalFilter: enableGlobalFilter, enableColumnVisibility: enableColumnVisibility, enableColumnFilter: enableColumnFilter, enableExport: enableExport, enableReset: enableReset, enableTableSizeControl: enableTableSizeControl, enableColumnPinning: enableColumnPinning, enableRefresh: enableRefresh, ...toolbarSlotProps, refreshButtonProps: {
1702
+ loading: tableLoading, // disable while fetching
1703
+ showSpinnerWhileLoading: false,
1704
+ onRefresh: () => { var _a, _b, _c; return (_c = (_b = (_a = internalApiRef.current) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.refresh) === null || _c === void 0 ? void 0 : _c.call(_b, true); },
1705
+ ...toolbarSlotProps.refreshButtonProps,
1706
+ } })) : null, enableBulkActions && enableRowSelection && isSomeRowsSelected ? ((0, jsx_runtime_1.jsx)(BulkActionsComponent, { selectionState: selectionState, selectedRowCount: selectedRowCount, bulkActions: bulkActions, sx: {
1543
1707
  position: 'relative',
1544
1708
  zIndex: 2,
1545
1709
  ...bulkActionsSlotProps.sx,