@ackplus/react-tanstack-data-table 1.1.12 → 1.1.15

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 (51) 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/table-export-control.d.ts.map +1 -1
  8. package/dist/lib/components/toolbar/table-export-control.js +46 -12
  9. package/dist/lib/contexts/data-table-context.d.ts +7 -10
  10. package/dist/lib/contexts/data-table-context.d.ts.map +1 -1
  11. package/dist/lib/contexts/data-table-context.js +5 -1
  12. package/dist/lib/data-table.d.ts.map +1 -1
  13. package/dist/lib/data-table.js +517 -237
  14. package/dist/lib/features/column-filter.feature.js +38 -21
  15. package/dist/lib/features/selection.feature.d.ts.map +1 -1
  16. package/dist/lib/features/selection.feature.js +11 -3
  17. package/dist/lib/types/column.types.d.ts +19 -0
  18. package/dist/lib/types/column.types.d.ts.map +1 -1
  19. package/dist/lib/types/data-table-api.d.ts +24 -18
  20. package/dist/lib/types/data-table-api.d.ts.map +1 -1
  21. package/dist/lib/types/data-table.types.d.ts +37 -10
  22. package/dist/lib/types/data-table.types.d.ts.map +1 -1
  23. package/dist/lib/types/export.types.d.ts +57 -13
  24. package/dist/lib/types/export.types.d.ts.map +1 -1
  25. package/dist/lib/types/slots.types.d.ts +3 -1
  26. package/dist/lib/types/slots.types.d.ts.map +1 -1
  27. package/dist/lib/types/table.types.d.ts +1 -3
  28. package/dist/lib/types/table.types.d.ts.map +1 -1
  29. package/dist/lib/utils/debounced-fetch.utils.d.ts +8 -4
  30. package/dist/lib/utils/debounced-fetch.utils.d.ts.map +1 -1
  31. package/dist/lib/utils/debounced-fetch.utils.js +63 -14
  32. package/dist/lib/utils/export-utils.d.ts +14 -4
  33. package/dist/lib/utils/export-utils.d.ts.map +1 -1
  34. package/dist/lib/utils/export-utils.js +362 -66
  35. package/package.json +4 -2
  36. package/src/lib/components/droupdown/menu-dropdown.tsx +9 -3
  37. package/src/lib/components/filters/filter-value-input.tsx +2 -2
  38. package/src/lib/components/pagination/data-table-pagination.tsx +14 -2
  39. package/src/lib/components/toolbar/table-export-control.tsx +65 -9
  40. package/src/lib/contexts/data-table-context.tsx +16 -2
  41. package/src/lib/data-table.tsx +647 -231
  42. package/src/lib/features/column-filter.feature.ts +40 -19
  43. package/src/lib/features/selection.feature.ts +11 -5
  44. package/src/lib/types/column.types.ts +20 -1
  45. package/src/lib/types/data-table-api.ts +33 -15
  46. package/src/lib/types/data-table.types.ts +59 -3
  47. package/src/lib/types/export.types.ts +79 -10
  48. package/src/lib/types/slots.types.ts +3 -1
  49. package/src/lib/types/table.types.ts +1 -3
  50. package/src/lib/utils/debounced-fetch.utils.ts +90 -18
  51. package/src/lib/utils/export-utils.ts +496 -69
@@ -89,7 +89,10 @@ 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, // callback to fetch data from the server need to with response { data: T[], total: number }
93
+ onFetchStateChange, // callback to fetch data from the server no need to resonce , this for filter data
94
+ onDataChange, // callback to change data
95
+ onDataStateChange, // callback to change data state
93
96
  // Selection props
94
97
  enableRowSelection = false, enableMultiRowSelection = true, selectMode = 'page', isRowSelectable, onSelectionChange,
95
98
  // Row click props
@@ -111,7 +114,9 @@ enablePagination = false, paginationMode = 'client',
111
114
  // Filtering props
112
115
  enableGlobalFilter = true, enableColumnFilter = false, filterMode = 'client',
113
116
  // Sorting props
114
- enableSorting = true, sortingMode = 'client', onSortingChange, exportFilename = 'export', onExportProgress, onExportComplete, onExportError, onServerExport, onExportCancel,
117
+ enableSorting = true, sortingMode = 'client', onSortingChange,
118
+ //export props
119
+ exportFilename = 'export', exportConcurrency = 'cancelAndRestart', exportChunkSize = 1000, exportStrictTotalCheck = false, exportSanitizeCSV = true, onExportProgress, onExportComplete, onExportError, onServerExport, onExportCancel, onExportStateChange,
115
120
  // Styling props
116
121
  enableHover = true, enableStripes = false, tableProps = {}, fitToScreen = true, tableSize: initialTableSize = 'medium',
117
122
  // Sticky header/footer props
@@ -184,14 +189,24 @@ logging, }, ref) {
184
189
  const [serverData, setServerData] = (0, react_1.useState)(null);
185
190
  const [serverTotal, setServerTotal] = (0, react_1.useState)(0);
186
191
  const [exportController, setExportController] = (0, react_1.useState)(null);
192
+ const [exportProgress, setExportProgress] = (0, react_1.useState)({});
193
+ const [exportPhase, setExportPhase] = (0, react_1.useState)(null);
194
+ const [queuedExportCount, setQueuedExportCount] = (0, react_1.useState)(0);
187
195
  // -------------------------------
188
196
  // Ref hooks (grouped together)
189
197
  // -------------------------------
190
198
  const tableContainerRef = (0, react_1.useRef)(null);
191
199
  const internalApiRef = (0, react_1.useRef)(null);
200
+ const exportControllerRef = (0, react_1.useRef)(null);
201
+ const exportQueueRef = (0, react_1.useRef)(Promise.resolve());
202
+ const isExternallyControlledData = (0, react_1.useMemo)(() => !onFetchData && (!!onDataChange || !!onFetchStateChange), [onFetchData, onDataChange, onFetchStateChange]);
192
203
  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]);
204
+ const tableData = (0, react_1.useMemo)(() => {
205
+ if (isExternallyControlledData)
206
+ return data;
207
+ return serverData !== null ? serverData : data;
208
+ }, [isExternallyControlledData, serverData, data]);
209
+ const tableTotalRow = (0, react_1.useMemo)(() => (isExternallyControlledData ? (totalRow || data.length) : (serverData !== null ? serverTotal : totalRow || data.length)), [isExternallyControlledData, serverData, serverTotal, totalRow, data]);
195
210
  const tableLoading = (0, react_1.useMemo)(() => onFetchData ? (loading || fetchLoading) : loading, [onFetchData, loading, fetchLoading]);
196
211
  const enhancedColumns = (0, react_1.useMemo)(() => {
197
212
  let columnsMap = [...columns];
@@ -239,13 +254,7 @@ logging, }, ref) {
239
254
  // Callback hooks (grouped together)
240
255
  // -------------------------------
241
256
  const fetchData = (0, react_1.useCallback)(async (overrides = {}, options) => {
242
- var _a, _b, _c;
243
- if (!onFetchData) {
244
- if (logger.isLevelEnabled('debug')) {
245
- logger.debug('onFetchData not provided, skipping fetch', { overrides, columnFilter, sorting, pagination });
246
- }
247
- return;
248
- }
257
+ var _a, _b, _c, _d, _e;
249
258
  const filters = {
250
259
  globalFilter,
251
260
  pagination,
@@ -253,20 +262,35 @@ logging, }, ref) {
253
262
  sorting,
254
263
  ...overrides,
255
264
  };
256
- console.log('Fetching data', filters);
265
+ if (onFetchStateChange) {
266
+ onFetchStateChange(filters, options === null || options === void 0 ? void 0 : options.meta);
267
+ }
268
+ if (!onFetchData) {
269
+ if (logger.isLevelEnabled('debug')) {
270
+ logger.debug('onFetchData not provided, skipping fetch', { overrides, columnFilter, sorting, pagination });
271
+ }
272
+ return;
273
+ }
257
274
  if (logger.isLevelEnabled('info')) {
258
- logger.info('Requesting data', { filters });
275
+ logger.info('Requesting data', {
276
+ filters,
277
+ reason: (_a = options === null || options === void 0 ? void 0 : options.meta) === null || _a === void 0 ? void 0 : _a.reason,
278
+ force: (_b = options === null || options === void 0 ? void 0 : options.meta) === null || _b === void 0 ? void 0 : _b.force,
279
+ });
259
280
  }
260
281
  try {
261
- const delay = (_a = options === null || options === void 0 ? void 0 : options.delay) !== null && _a !== void 0 ? _a : 300; // respects 0
262
- const result = await debouncedFetch(filters, delay);
282
+ const delay = (_c = options === null || options === void 0 ? void 0 : options.delay) !== null && _c !== void 0 ? _c : 300; // respects 0
283
+ const result = await debouncedFetch(filters, {
284
+ debounceDelay: delay,
285
+ meta: options === null || options === void 0 ? void 0 : options.meta,
286
+ });
263
287
  if (logger.isLevelEnabled('info')) {
264
288
  logger.info('Fetch resolved', {
265
- rows: (_c = (_b = result === null || result === void 0 ? void 0 : result.data) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0,
289
+ 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,
266
290
  total: result === null || result === void 0 ? void 0 : result.total,
267
291
  });
268
292
  }
269
- if ((result === null || result === void 0 ? void 0 : result.data) && (result === null || result === void 0 ? void 0 : result.total) !== undefined) {
293
+ if (result && Array.isArray(result.data) && result.total !== undefined) {
270
294
  setServerData(result.data);
271
295
  setServerTotal(result.total);
272
296
  }
@@ -287,7 +311,23 @@ logging, }, ref) {
287
311
  sorting,
288
312
  debouncedFetch,
289
313
  logger,
314
+ onFetchStateChange,
290
315
  ]);
316
+ const normalizeRefreshOptions = (0, react_1.useCallback)((options, fallbackReason = 'refresh') => {
317
+ var _a, _b, _c;
318
+ if (typeof options === 'boolean') {
319
+ return {
320
+ resetPagination: options,
321
+ force: false,
322
+ reason: fallbackReason,
323
+ };
324
+ }
325
+ return {
326
+ resetPagination: (_a = options === null || options === void 0 ? void 0 : options.resetPagination) !== null && _a !== void 0 ? _a : false,
327
+ force: (_b = options === null || options === void 0 ? void 0 : options.force) !== null && _b !== void 0 ? _b : false,
328
+ reason: (_c = options === null || options === void 0 ? void 0 : options.reason) !== null && _c !== void 0 ? _c : fallbackReason,
329
+ };
330
+ }, []);
291
331
  const handleSelectionStateChange = (0, react_1.useCallback)((updaterOrValue) => {
292
332
  setSelectionState((prevState) => {
293
333
  const next = typeof updaterOrValue === 'function' ? updaterOrValue(prevState) : updaterOrValue;
@@ -526,11 +566,20 @@ logging, }, ref) {
526
566
  // Effects (after callbacks)
527
567
  // -------------------------------
528
568
  (0, react_1.useEffect)(() => {
529
- if (initialLoadData && onFetchData) {
569
+ if (!isExternallyControlledData || serverData === null)
570
+ return;
571
+ setServerData(null);
572
+ setServerTotal(0);
573
+ }, [isExternallyControlledData, serverData]);
574
+ (0, react_1.useEffect)(() => {
575
+ if (initialLoadData && (onFetchData || onFetchStateChange)) {
530
576
  if (logger.isLevelEnabled('info')) {
531
577
  logger.info('Initial data load triggered', { initialLoadData });
532
578
  }
533
- fetchData({});
579
+ fetchData({}, {
580
+ delay: 0,
581
+ meta: { reason: 'initial' },
582
+ });
534
583
  }
535
584
  else if (logger.isLevelEnabled('debug')) {
536
585
  logger.debug('Skipping initial data load', {
@@ -559,12 +608,13 @@ logging, }, ref) {
559
608
  if (!onDataStateChange)
560
609
  return;
561
610
  const live = table.getState();
611
+ const liveColumnFilter = live.columnFilter;
562
612
  // only keep what you persist/store
563
613
  const payload = {
564
614
  sorting: live.sorting,
565
615
  pagination: live.pagination,
566
616
  globalFilter: live.globalFilter,
567
- columnFilter: live.columnFilter,
617
+ columnFilter: liveColumnFilter,
568
618
  columnVisibility: live.columnVisibility,
569
619
  columnSizing: live.columnSizing,
570
620
  columnOrder: live.columnOrder,
@@ -593,7 +643,7 @@ logging, }, ref) {
593
643
  var _a;
594
644
  const resetSorting = initialStateConfig.sorting || [];
595
645
  const resetGlobalFilter = (_a = initialStateConfig.globalFilter) !== null && _a !== void 0 ? _a : '';
596
- const resetColumnFilter = initialStateConfig.columnFilter || { filters: [], logic: 'AND', pendingFilters: [], pendingLogic: 'AND' };
646
+ const resetColumnFilter = initialStateConfig.columnFilter;
597
647
  const resetPagination = enablePagination
598
648
  ? (initialStateConfig.pagination || { pageIndex: 0, pageSize: 10 })
599
649
  : undefined;
@@ -604,14 +654,66 @@ logging, }, ref) {
604
654
  ...(resetPagination ? { pagination: resetPagination } : {}),
605
655
  };
606
656
  }, [initialStateConfig, enablePagination]);
657
+ const applyDataMutation = (0, react_1.useCallback)((action, updater, details = {}) => {
658
+ const previousData = [...tableData];
659
+ const nextData = updater(previousData);
660
+ if (nextData === previousData)
661
+ return nextData;
662
+ const nextTotal = Math.max(0, tableTotalRow + (nextData.length - previousData.length));
663
+ if (!isExternallyControlledData) {
664
+ setServerData(nextData);
665
+ setServerTotal(nextTotal);
666
+ }
667
+ onDataChange === null || onDataChange === void 0 ? void 0 : onDataChange(nextData, {
668
+ action,
669
+ previousData,
670
+ nextData,
671
+ totalRow: nextTotal,
672
+ ...details,
673
+ });
674
+ if (logger.isLevelEnabled('debug')) {
675
+ logger.debug('Applied data mutation', {
676
+ action,
677
+ previousCount: previousData.length,
678
+ nextCount: nextData.length,
679
+ totalRow: nextTotal,
680
+ });
681
+ }
682
+ return nextData;
683
+ }, [isExternallyControlledData, logger, onDataChange, tableData, tableTotalRow]);
684
+ const triggerRefresh = (0, react_1.useCallback)(async (options, fallbackReason = 'refresh') => {
685
+ const normalizedOptions = normalizeRefreshOptions(options, fallbackReason);
686
+ const nextPagination = enablePagination
687
+ ? {
688
+ pageIndex: normalizedOptions.resetPagination ? 0 : pagination.pageIndex,
689
+ pageSize: pagination.pageSize,
690
+ }
691
+ : undefined;
692
+ const shouldUpdatePagination = !!nextPagination
693
+ && (nextPagination.pageIndex !== pagination.pageIndex || nextPagination.pageSize !== pagination.pageSize);
694
+ if (nextPagination && shouldUpdatePagination) {
695
+ setPagination(nextPagination);
696
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(nextPagination);
697
+ }
698
+ await fetchData(nextPagination ? { pagination: nextPagination } : {}, {
699
+ delay: 0,
700
+ meta: {
701
+ reason: normalizedOptions.reason,
702
+ force: normalizedOptions.force,
703
+ },
704
+ });
705
+ return;
706
+ }, [normalizeRefreshOptions, enablePagination, pagination, onPaginationChange, fetchData]);
607
707
  const resetAllAndReload = (0, react_1.useCallback)(() => {
608
708
  var _a;
609
709
  const resetState = getResetState();
610
710
  setSorting(resetState.sorting || []);
611
711
  setGlobalFilter((_a = resetState.globalFilter) !== null && _a !== void 0 ? _a : '');
612
712
  setColumnFilter(resetState.columnFilter);
613
- if (resetState.pagination)
713
+ if (resetState.pagination) {
614
714
  setPagination(resetState.pagination);
715
+ onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(resetState.pagination);
716
+ }
615
717
  setSelectionState(initialSelectionState);
616
718
  setExpanded({});
617
719
  // layout state
@@ -619,9 +721,97 @@ logging, }, ref) {
619
721
  setColumnSizing(initialStateConfig.columnSizing || {});
620
722
  setColumnOrder(initialStateConfig.columnOrder || []);
621
723
  setColumnPinning(initialStateConfig.columnPinning || { left: [], right: [] });
622
- if (onFetchData)
623
- fetchData(resetState, { delay: 0 });
624
- }, [getResetState, initialSelectionState, initialStateConfig, onFetchData, fetchData]);
724
+ const resetOptions = normalizeRefreshOptions({
725
+ resetPagination: true,
726
+ force: true,
727
+ reason: 'reset',
728
+ }, 'reset');
729
+ void fetchData(resetState, {
730
+ delay: 0,
731
+ meta: {
732
+ reason: resetOptions.reason,
733
+ force: resetOptions.force,
734
+ },
735
+ });
736
+ }, [getResetState, initialSelectionState, initialStateConfig, onPaginationChange, normalizeRefreshOptions, fetchData]);
737
+ const setExportControllerSafely = (0, react_1.useCallback)((value) => {
738
+ setExportController((current) => {
739
+ const next = typeof value === 'function' ? value(current) : value;
740
+ exportControllerRef.current = next;
741
+ return next;
742
+ });
743
+ }, []);
744
+ const handleExportProgressInternal = (0, react_1.useCallback)((progress) => {
745
+ setExportProgress(progress || {});
746
+ onExportProgress === null || onExportProgress === void 0 ? void 0 : onExportProgress(progress);
747
+ }, [onExportProgress]);
748
+ const handleExportStateChangeInternal = (0, react_1.useCallback)((state) => {
749
+ setExportPhase(state.phase);
750
+ if (state.processedRows !== undefined
751
+ || state.totalRows !== undefined
752
+ || state.percentage !== undefined) {
753
+ setExportProgress({
754
+ processedRows: state.processedRows,
755
+ totalRows: state.totalRows,
756
+ percentage: state.percentage,
757
+ });
758
+ }
759
+ onExportStateChange === null || onExportStateChange === void 0 ? void 0 : onExportStateChange(state);
760
+ }, [onExportStateChange]);
761
+ const runExportWithPolicy = (0, react_1.useCallback)(async (options) => {
762
+ const { format, filename, mode, execute } = options;
763
+ const startExecution = async () => {
764
+ const controller = new AbortController();
765
+ setExportProgress({});
766
+ setExportControllerSafely(controller);
767
+ try {
768
+ await execute(controller);
769
+ }
770
+ finally {
771
+ setExportControllerSafely((current) => (current === controller ? null : current));
772
+ }
773
+ };
774
+ if (exportConcurrency === 'queue') {
775
+ setQueuedExportCount((prev) => prev + 1);
776
+ const runQueued = async () => {
777
+ setQueuedExportCount((prev) => Math.max(0, prev - 1));
778
+ await startExecution();
779
+ };
780
+ const queuedPromise = exportQueueRef.current
781
+ .catch(() => undefined)
782
+ .then(runQueued);
783
+ exportQueueRef.current = queuedPromise;
784
+ return queuedPromise;
785
+ }
786
+ const activeController = exportControllerRef.current;
787
+ if (activeController) {
788
+ if (exportConcurrency === 'ignoreIfRunning') {
789
+ handleExportStateChangeInternal({
790
+ phase: 'error',
791
+ mode,
792
+ format,
793
+ filename,
794
+ message: 'An export is already running',
795
+ code: 'EXPORT_IN_PROGRESS',
796
+ endedAt: Date.now(),
797
+ });
798
+ onExportError === null || onExportError === void 0 ? void 0 : onExportError({
799
+ message: 'An export is already running',
800
+ code: 'EXPORT_IN_PROGRESS',
801
+ });
802
+ return;
803
+ }
804
+ if (exportConcurrency === 'cancelAndRestart') {
805
+ activeController.abort();
806
+ }
807
+ }
808
+ await startExecution();
809
+ }, [
810
+ exportConcurrency,
811
+ handleExportStateChangeInternal,
812
+ onExportError,
813
+ setExportControllerSafely,
814
+ ]);
625
815
  const dataTableApi = (0, react_1.useMemo)(() => {
626
816
  // helpers (avoid repeating boilerplate)
627
817
  const buildInitialOrder = () => enhancedColumns.map((col, index) => {
@@ -654,6 +844,12 @@ logging, }, ref) {
654
844
  const applyGlobalFilter = (next) => {
655
845
  handleGlobalFilterChange(next);
656
846
  };
847
+ const getRowIndexById = (rowsToSearch, rowId) => rowsToSearch.findIndex((row, index) => String((0, utils_1.generateRowId)(row, index, idKey)) === rowId);
848
+ const clampInsertIndex = (rowsToMutate, insertIndex) => {
849
+ if (insertIndex === undefined)
850
+ return rowsToMutate.length;
851
+ return Math.max(0, Math.min(insertIndex, rowsToMutate.length));
852
+ };
657
853
  return {
658
854
  table: {
659
855
  getTable: () => table,
@@ -935,129 +1131,135 @@ logging, }, ref) {
935
1131
  // Data Management (kept same, but ensure state changes go through handlers)
936
1132
  // -------------------------------
937
1133
  data: {
938
- refresh: (resetPagination = false) => {
939
- var _a, _b, _c, _d;
940
- const allState = table.getState();
941
- const current = allState.pagination;
942
- const nextPagination = {
943
- pageIndex: resetPagination ? 0 : (_a = current === null || current === void 0 ? void 0 : current.pageIndex) !== null && _a !== void 0 ? _a : 0,
944
- pageSize: (_d = (_b = current === null || current === void 0 ? void 0 : current.pageSize) !== null && _b !== void 0 ? _b : (_c = initialStateConfig.pagination) === null || _c === void 0 ? void 0 : _c.pageSize) !== null && _d !== void 0 ? _d : 10,
945
- };
946
- // must go through handler so server fetch triggers
947
- applyPagination(nextPagination);
948
- // emit persisted state (your emitTableState effect will also do it)
949
- onDataStateChange === null || onDataStateChange === void 0 ? void 0 : onDataStateChange({ ...allState, pagination: nextPagination });
950
- fetchData === null || fetchData === void 0 ? void 0 : fetchData({ pagination: nextPagination });
951
- if (logger.isLevelEnabled("debug")) {
952
- logger.debug("Refreshing data", { nextPagination, allState });
953
- }
1134
+ refresh: (options) => {
1135
+ void triggerRefresh(options, 'refresh');
954
1136
  },
955
- reload: () => {
956
- const allState = table.getState();
957
- onDataStateChange === null || onDataStateChange === void 0 ? void 0 : onDataStateChange(allState);
958
- fetchData === null || fetchData === void 0 ? void 0 : fetchData();
959
- if (logger.isLevelEnabled("debug")) {
960
- logger.info("Reloading data", allState);
961
- }
1137
+ reload: (options = {}) => {
1138
+ var _a, _b;
1139
+ void triggerRefresh({
1140
+ ...options,
1141
+ resetPagination: (_a = options.resetPagination) !== null && _a !== void 0 ? _a : false,
1142
+ reason: (_b = options.reason) !== null && _b !== void 0 ? _b : 'reload',
1143
+ }, 'reload');
1144
+ },
1145
+ resetAll: () => resetAllAndReload(),
1146
+ getAllData: () => [...tableData],
1147
+ getRowData: (rowId) => {
1148
+ const rowIndex = getRowIndexById(tableData, rowId);
1149
+ return rowIndex === -1 ? undefined : tableData[rowIndex];
962
1150
  },
963
- resetAll: () => resetAllAndReload({ resetLayout: true }),
964
- getAllData: () => { var _a; return ((_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map((row) => row.original)) || []; },
965
- getRowData: (rowId) => { var _a, _b; 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; },
966
- getRowByIndex: (index) => { var _a, _b; return (_b = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a[index]) === null || _b === void 0 ? void 0 : _b.original; },
1151
+ getRowByIndex: (index) => tableData[index],
967
1152
  updateRow: (rowId, updates) => {
968
- var _a;
969
- const newData = (_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map((row) => String(row.original[idKey]) === rowId ? { ...row.original, ...updates } : row.original);
970
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData || []);
971
- if (logger.isLevelEnabled("debug"))
972
- logger.debug(`Updating row ${rowId}`, updates);
1153
+ applyDataMutation('updateRow', (rowsToMutate) => {
1154
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1155
+ if (rowIndex === -1)
1156
+ return rowsToMutate;
1157
+ const nextData = [...rowsToMutate];
1158
+ nextData[rowIndex] = { ...nextData[rowIndex], ...updates };
1159
+ return nextData;
1160
+ }, { rowId });
973
1161
  },
974
1162
  updateRowByIndex: (index, updates) => {
975
- var _a;
976
- const newData = ((_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map((row) => row.original)) || [];
977
- if (newData[index]) {
978
- newData[index] = { ...newData[index], ...updates };
979
- setServerData(newData);
980
- if (logger.isLevelEnabled("debug"))
981
- logger.debug(`Updating row by index ${index}`, updates);
982
- }
1163
+ applyDataMutation('updateRowByIndex', (rowsToMutate) => {
1164
+ if (!rowsToMutate[index])
1165
+ return rowsToMutate;
1166
+ const nextData = [...rowsToMutate];
1167
+ nextData[index] = { ...nextData[index], ...updates };
1168
+ return nextData;
1169
+ }, { index });
983
1170
  },
984
1171
  insertRow: (newRow, index) => {
985
- var _a;
986
- const newData = ((_a = table.getRowModel().rows) === null || _a === void 0 ? void 0 : _a.map((row) => row.original)) || [];
987
- if (index !== undefined)
988
- newData.splice(index, 0, newRow);
989
- else
990
- newData.push(newRow);
991
- setServerData(newData || []);
992
- if (logger.isLevelEnabled("debug"))
993
- logger.debug("Inserting row", newRow);
1172
+ applyDataMutation('insertRow', (rowsToMutate) => {
1173
+ const nextData = [...rowsToMutate];
1174
+ nextData.splice(clampInsertIndex(nextData, index), 0, newRow);
1175
+ return nextData;
1176
+ }, { index });
994
1177
  },
995
1178
  deleteRow: (rowId) => {
996
- const newData = (table.getRowModel().rows || []).filter((row) => String(row.original[idKey]) !== rowId);
997
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData.map((r) => r.original) || []);
998
- if (logger.isLevelEnabled("debug"))
999
- logger.debug(`Deleting row ${rowId}`);
1179
+ applyDataMutation('deleteRow', (rowsToMutate) => {
1180
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1181
+ if (rowIndex === -1)
1182
+ return rowsToMutate;
1183
+ const nextData = [...rowsToMutate];
1184
+ nextData.splice(rowIndex, 1);
1185
+ return nextData;
1186
+ }, { rowId });
1000
1187
  },
1001
1188
  deleteRowByIndex: (index) => {
1002
- const newData = (table.getRowModel().rows || []).map((row) => row.original);
1003
- newData.splice(index, 1);
1004
- setServerData(newData);
1005
- if (logger.isLevelEnabled("debug"))
1006
- logger.debug(`Deleting row by index ${index}`);
1189
+ applyDataMutation('deleteRowByIndex', (rowsToMutate) => {
1190
+ if (index < 0 || index >= rowsToMutate.length)
1191
+ return rowsToMutate;
1192
+ const nextData = [...rowsToMutate];
1193
+ nextData.splice(index, 1);
1194
+ return nextData;
1195
+ }, { index });
1007
1196
  },
1008
1197
  deleteSelectedRows: () => {
1009
- var _a, _b;
1010
- const selectedRows = ((_a = table.getSelectedRows) === null || _a === void 0 ? void 0 : _a.call(table)) || [];
1011
- if (selectedRows.length === 0)
1198
+ var _a, _b, _c;
1199
+ const currentSelection = ((_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table)) || selectionState;
1200
+ const selectedIds = new Set((currentSelection.ids || []).map((id) => String(id)));
1201
+ const loadedRowIds = tableData.map((row, index) => String((0, utils_1.generateRowId)(row, index, idKey)));
1202
+ const deletableRowIds = currentSelection.type === 'exclude'
1203
+ ? loadedRowIds.filter((rowId) => !selectedIds.has(rowId))
1204
+ : loadedRowIds.filter((rowId) => selectedIds.has(rowId));
1205
+ if (deletableRowIds.length === 0)
1012
1206
  return;
1013
- const selectedIds = new Set(selectedRows.map((row) => String(row.original[idKey])));
1014
- const newData = (table.getRowModel().rows || [])
1015
- .filter((row) => !selectedIds.has(String(row.original[idKey])))
1016
- .map((row) => row.original);
1017
- setServerData(newData);
1018
- (_b = table.deselectAll) === null || _b === void 0 ? void 0 : _b.call(table);
1019
- if (logger.isLevelEnabled("debug"))
1020
- logger.debug("Deleting selected rows");
1207
+ if (currentSelection.type === 'exclude'
1208
+ && table.getRowCount() > loadedRowIds.length
1209
+ && logger.isLevelEnabled('info')) {
1210
+ logger.info('deleteSelectedRows in exclude mode removed currently loaded rows only', {
1211
+ removedRows: deletableRowIds.length,
1212
+ totalSelected: (_b = table.getSelectedCount) === null || _b === void 0 ? void 0 : _b.call(table),
1213
+ });
1214
+ }
1215
+ const deletableRowIdSet = new Set(deletableRowIds);
1216
+ applyDataMutation('deleteSelectedRows', (rowsToMutate) => rowsToMutate.filter((row, index) => !deletableRowIdSet.has(String((0, utils_1.generateRowId)(row, index, idKey)))), { rowIds: deletableRowIds });
1217
+ (_c = table.deselectAll) === null || _c === void 0 ? void 0 : _c.call(table);
1218
+ },
1219
+ replaceAllData: (newData) => {
1220
+ applyDataMutation('replaceAllData', () => [...newData]);
1021
1221
  },
1022
- replaceAllData: (newData) => setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData),
1023
1222
  updateMultipleRows: (updates) => {
1024
- const updateMap = new Map(updates.map((u) => [u.rowId, u.data]));
1025
- const newData = (table.getRowModel().rows || []).map((row) => {
1026
- const rowId = String(row.original[idKey]);
1027
- const updateData = updateMap.get(rowId);
1028
- return updateData ? { ...row.original, ...updateData } : row.original;
1029
- });
1030
- setServerData(newData || []);
1223
+ const updateMap = new Map(updates.map((update) => [update.rowId, update.data]));
1224
+ applyDataMutation('updateMultipleRows', (rowsToMutate) => rowsToMutate.map((row, index) => {
1225
+ const currentRowId = String((0, utils_1.generateRowId)(row, index, idKey));
1226
+ const updateData = updateMap.get(currentRowId);
1227
+ return updateData ? { ...row, ...updateData } : row;
1228
+ }));
1031
1229
  },
1032
1230
  insertMultipleRows: (newRows, startIndex) => {
1033
- const newData = (table.getRowModel().rows || []).map((row) => row.original);
1034
- if (startIndex !== undefined)
1035
- newData.splice(startIndex, 0, ...newRows);
1036
- else
1037
- newData.push(...newRows);
1038
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1231
+ applyDataMutation('insertMultipleRows', (rowsToMutate) => {
1232
+ const nextData = [...rowsToMutate];
1233
+ nextData.splice(clampInsertIndex(nextData, startIndex), 0, ...newRows);
1234
+ return nextData;
1235
+ }, { index: startIndex });
1039
1236
  },
1040
1237
  deleteMultipleRows: (rowIds) => {
1041
1238
  const idsToDelete = new Set(rowIds);
1042
- const newData = (table.getRowModel().rows || [])
1043
- .filter((row) => !idsToDelete.has(String(row.original[idKey])))
1044
- .map((row) => row.original);
1045
- setServerData(newData);
1239
+ applyDataMutation('deleteMultipleRows', (rowsToMutate) => rowsToMutate.filter((row, index) => !idsToDelete.has(String((0, utils_1.generateRowId)(row, index, idKey)))), { rowIds });
1046
1240
  },
1047
1241
  updateField: (rowId, fieldName, value) => {
1048
- const newData = (table.getRowModel().rows || []).map((row) => String(row.original[idKey]) === rowId ? { ...row.original, [fieldName]: value } : row.original);
1049
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1242
+ applyDataMutation('updateField', (rowsToMutate) => {
1243
+ const rowIndex = getRowIndexById(rowsToMutate, rowId);
1244
+ if (rowIndex === -1)
1245
+ return rowsToMutate;
1246
+ const nextData = [...rowsToMutate];
1247
+ nextData[rowIndex] = { ...nextData[rowIndex], [fieldName]: value };
1248
+ return nextData;
1249
+ }, { rowId });
1050
1250
  },
1051
1251
  updateFieldByIndex: (index, fieldName, value) => {
1052
- const newData = (table.getRowModel().rows || []).map((row) => row.original);
1053
- if (newData[index]) {
1054
- newData[index] = { ...newData[index], [fieldName]: value };
1055
- setServerData === null || setServerData === void 0 ? void 0 : setServerData(newData);
1056
- }
1252
+ applyDataMutation('updateFieldByIndex', (rowsToMutate) => {
1253
+ if (!rowsToMutate[index])
1254
+ return rowsToMutate;
1255
+ const nextData = [...rowsToMutate];
1256
+ nextData[index] = { ...nextData[index], [fieldName]: value };
1257
+ return nextData;
1258
+ }, { index });
1057
1259
  },
1058
- findRows: (predicate) => (table.getRowModel().rows || []).filter((row) => predicate(row.original)).map((row) => row.original),
1059
- findRowIndex: (predicate) => (table.getRowModel().rows || []).findIndex((row) => predicate(row.original)),
1060
- getDataCount: () => (table.getRowModel().rows || []).length || 0,
1260
+ findRows: (predicate) => tableData.filter(predicate),
1261
+ findRowIndex: (predicate) => tableData.findIndex(predicate),
1262
+ getDataCount: () => tableData.length,
1061
1263
  getFilteredDataCount: () => table.getFilteredRowModel().rows.length,
1062
1264
  },
1063
1265
  // -------------------------------
@@ -1072,7 +1274,7 @@ logging, }, ref) {
1072
1274
  applySorting(initialStateConfig.sorting || []);
1073
1275
  applyGlobalFilter((_a = initialStateConfig.globalFilter) !== null && _a !== void 0 ? _a : "");
1074
1276
  },
1075
- resetAll: () => resetAllAndReload({ resetLayout: true }),
1277
+ resetAll: () => resetAllAndReload(),
1076
1278
  saveLayout: () => ({
1077
1279
  columnVisibility: table.getState().columnVisibility,
1078
1280
  columnSizing: table.getState().columnSizing,
@@ -1117,141 +1319,212 @@ logging, }, ref) {
1117
1319
  // -------------------------------
1118
1320
  export: {
1119
1321
  exportCSV: async (options = {}) => {
1120
- var _a;
1121
- const { filename = exportFilename } = options;
1122
- try {
1123
- const controller = new AbortController();
1124
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1125
- if (dataMode === "server" && onServerExport) {
1126
- const currentFilters = {
1127
- globalFilter: table.getState().globalFilter,
1128
- columnFilter: table.getState().columnFilter,
1129
- sorting: table.getState().sorting,
1130
- pagination: table.getState().pagination,
1322
+ const { filename = exportFilename, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1323
+ const mode = dataMode === "server" && !!onServerExport ? 'server' : 'client';
1324
+ await runExportWithPolicy({
1325
+ format: 'csv',
1326
+ filename,
1327
+ mode,
1328
+ execute: async (controller) => {
1329
+ var _a;
1330
+ const toStateChange = (state) => {
1331
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1332
+ handleExportStateChangeInternal({
1333
+ phase: state.phase,
1334
+ mode,
1335
+ format: 'csv',
1336
+ filename,
1337
+ processedRows: state.processedRows,
1338
+ totalRows: state.totalRows,
1339
+ percentage: state.percentage,
1340
+ message: state.message,
1341
+ code: state.code,
1342
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1343
+ endedAt: isFinalPhase ? Date.now() : undefined,
1344
+ queueLength: queuedExportCount,
1345
+ });
1346
+ if (state.phase === 'cancelled') {
1347
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1348
+ }
1131
1349
  };
1132
- if (logger.isLevelEnabled("debug"))
1133
- logger.debug("Server export CSV", { currentFilters });
1134
- await (0, utils_1.exportServerData)(table, {
1135
- format: "csv",
1136
- filename,
1137
- fetchData: (filters, selection) => onServerExport(filters, selection),
1138
- currentFilters,
1139
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1140
- onProgress: onExportProgress,
1141
- onComplete: onExportComplete,
1142
- onError: onExportError,
1143
- });
1144
- }
1145
- else {
1350
+ if (mode === 'server' && onServerExport) {
1351
+ const currentFilters = {
1352
+ globalFilter: table.getState().globalFilter,
1353
+ columnFilter: table.getState().columnFilter,
1354
+ sorting: table.getState().sorting,
1355
+ pagination: table.getState().pagination,
1356
+ };
1357
+ if (logger.isLevelEnabled("debug"))
1358
+ logger.debug("Server export CSV", { currentFilters });
1359
+ await (0, utils_1.exportServerData)(table, {
1360
+ format: "csv",
1361
+ filename,
1362
+ fetchData: (filters, selection, signal) => onServerExport(filters, selection, signal),
1363
+ currentFilters,
1364
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1365
+ onProgress: handleExportProgressInternal,
1366
+ onComplete: onExportComplete,
1367
+ onError: onExportError,
1368
+ onStateChange: toStateChange,
1369
+ signal: controller.signal,
1370
+ chunkSize,
1371
+ strictTotalCheck,
1372
+ sanitizeCSV,
1373
+ });
1374
+ return;
1375
+ }
1146
1376
  await (0, utils_1.exportClientData)(table, {
1147
1377
  format: "csv",
1148
1378
  filename,
1149
- onProgress: onExportProgress,
1379
+ onProgress: handleExportProgressInternal,
1150
1380
  onComplete: onExportComplete,
1151
1381
  onError: onExportError,
1382
+ onStateChange: toStateChange,
1383
+ signal: controller.signal,
1384
+ sanitizeCSV,
1152
1385
  });
1153
1386
  if (logger.isLevelEnabled("debug"))
1154
1387
  logger.debug("Client export CSV", filename);
1155
1388
  }
1156
- }
1157
- catch (error) {
1158
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({ message: error.message || "Export failed", code: "EXPORT_ERROR" });
1159
- }
1160
- finally {
1161
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1162
- }
1389
+ });
1163
1390
  },
1164
1391
  exportExcel: async (options = {}) => {
1165
- var _a;
1166
- const { filename = exportFilename } = options;
1167
- try {
1168
- const controller = new AbortController();
1169
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1170
- if (dataMode === "server" && onServerExport) {
1171
- const currentFilters = {
1172
- globalFilter: table.getState().globalFilter,
1173
- columnFilter: table.getState().columnFilter,
1174
- sorting: table.getState().sorting,
1175
- pagination: table.getState().pagination,
1392
+ const { filename = exportFilename, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1393
+ const mode = dataMode === "server" && !!onServerExport ? 'server' : 'client';
1394
+ await runExportWithPolicy({
1395
+ format: 'excel',
1396
+ filename,
1397
+ mode,
1398
+ execute: async (controller) => {
1399
+ var _a;
1400
+ const toStateChange = (state) => {
1401
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1402
+ handleExportStateChangeInternal({
1403
+ phase: state.phase,
1404
+ mode,
1405
+ format: 'excel',
1406
+ filename,
1407
+ processedRows: state.processedRows,
1408
+ totalRows: state.totalRows,
1409
+ percentage: state.percentage,
1410
+ message: state.message,
1411
+ code: state.code,
1412
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1413
+ endedAt: isFinalPhase ? Date.now() : undefined,
1414
+ queueLength: queuedExportCount,
1415
+ });
1416
+ if (state.phase === 'cancelled') {
1417
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1418
+ }
1176
1419
  };
1177
- if (logger.isLevelEnabled("debug"))
1178
- logger.debug("Server export Excel", { currentFilters });
1179
- await (0, utils_1.exportServerData)(table, {
1180
- format: "excel",
1181
- filename,
1182
- fetchData: (filters, selection) => onServerExport(filters, selection),
1183
- currentFilters,
1184
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1185
- onProgress: onExportProgress,
1186
- onComplete: onExportComplete,
1187
- onError: onExportError,
1188
- });
1189
- }
1190
- else {
1420
+ if (mode === 'server' && onServerExport) {
1421
+ const currentFilters = {
1422
+ globalFilter: table.getState().globalFilter,
1423
+ columnFilter: table.getState().columnFilter,
1424
+ sorting: table.getState().sorting,
1425
+ pagination: table.getState().pagination,
1426
+ };
1427
+ if (logger.isLevelEnabled("debug"))
1428
+ logger.debug("Server export Excel", { currentFilters });
1429
+ await (0, utils_1.exportServerData)(table, {
1430
+ format: "excel",
1431
+ filename,
1432
+ fetchData: (filters, selection, signal) => onServerExport(filters, selection, signal),
1433
+ currentFilters,
1434
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1435
+ onProgress: handleExportProgressInternal,
1436
+ onComplete: onExportComplete,
1437
+ onError: onExportError,
1438
+ onStateChange: toStateChange,
1439
+ signal: controller.signal,
1440
+ chunkSize,
1441
+ strictTotalCheck,
1442
+ sanitizeCSV,
1443
+ });
1444
+ return;
1445
+ }
1191
1446
  await (0, utils_1.exportClientData)(table, {
1192
1447
  format: "excel",
1193
1448
  filename,
1194
- onProgress: onExportProgress,
1449
+ onProgress: handleExportProgressInternal,
1195
1450
  onComplete: onExportComplete,
1196
1451
  onError: onExportError,
1452
+ onStateChange: toStateChange,
1453
+ signal: controller.signal,
1454
+ sanitizeCSV,
1197
1455
  });
1198
1456
  if (logger.isLevelEnabled("debug"))
1199
1457
  logger.debug("Client export Excel", filename);
1200
1458
  }
1201
- }
1202
- catch (error) {
1203
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({ message: error.message || "Export failed", code: "EXPORT_ERROR" });
1204
- if (logger.isLevelEnabled("debug"))
1205
- logger.debug("Server export Excel failed", error);
1206
- }
1207
- finally {
1208
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1209
- }
1459
+ });
1210
1460
  },
1211
1461
  exportServerData: async (options) => {
1212
- var _a;
1213
- const { format, filename = exportFilename, fetchData: fetchFn = onServerExport } = options;
1462
+ const { format, filename = exportFilename, fetchData: fetchFn = onServerExport, chunkSize = exportChunkSize, strictTotalCheck = exportStrictTotalCheck, sanitizeCSV = exportSanitizeCSV, } = options;
1214
1463
  if (!fetchFn) {
1215
1464
  onExportError === null || onExportError === void 0 ? void 0 : onExportError({ message: "No server export function provided", code: "NO_SERVER_EXPORT" });
1216
1465
  if (logger.isLevelEnabled("debug"))
1217
1466
  logger.debug("Server export data failed", "No server export function provided");
1218
1467
  return;
1219
1468
  }
1220
- try {
1221
- const controller = new AbortController();
1222
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(controller);
1223
- const currentFilters = {
1224
- globalFilter: table.getState().globalFilter,
1225
- columnFilter: table.getState().columnFilter,
1226
- sorting: table.getState().sorting,
1227
- pagination: table.getState().pagination,
1228
- };
1229
- if (logger.isLevelEnabled("debug"))
1230
- logger.debug("Server export data", { currentFilters });
1231
- await (0, utils_1.exportServerData)(table, {
1232
- format,
1233
- filename,
1234
- fetchData: (filters, selection) => fetchFn(filters, selection),
1235
- currentFilters,
1236
- selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1237
- onProgress: onExportProgress,
1238
- onComplete: onExportComplete,
1239
- onError: onExportError,
1240
- });
1241
- }
1242
- catch (error) {
1243
- onExportError === null || onExportError === void 0 ? void 0 : onExportError({ message: error.message || "Export failed", code: "EXPORT_ERROR" });
1244
- if (logger.isLevelEnabled("debug"))
1245
- logger.debug("Server export data failed", error);
1246
- }
1247
- finally {
1248
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1249
- }
1469
+ await runExportWithPolicy({
1470
+ format,
1471
+ filename,
1472
+ mode: 'server',
1473
+ execute: async (controller) => {
1474
+ var _a;
1475
+ const currentFilters = {
1476
+ globalFilter: table.getState().globalFilter,
1477
+ columnFilter: table.getState().columnFilter,
1478
+ sorting: table.getState().sorting,
1479
+ pagination: table.getState().pagination,
1480
+ };
1481
+ if (logger.isLevelEnabled("debug"))
1482
+ logger.debug("Server export data", { currentFilters });
1483
+ await (0, utils_1.exportServerData)(table, {
1484
+ format,
1485
+ filename,
1486
+ fetchData: (filters, selection, signal) => fetchFn(filters, selection, signal),
1487
+ currentFilters,
1488
+ selection: (_a = table.getSelectionState) === null || _a === void 0 ? void 0 : _a.call(table),
1489
+ onProgress: handleExportProgressInternal,
1490
+ onComplete: onExportComplete,
1491
+ onError: onExportError,
1492
+ onStateChange: (state) => {
1493
+ const isFinalPhase = state.phase === 'completed' || state.phase === 'cancelled' || state.phase === 'error';
1494
+ handleExportStateChangeInternal({
1495
+ phase: state.phase,
1496
+ mode: 'server',
1497
+ format,
1498
+ filename,
1499
+ processedRows: state.processedRows,
1500
+ totalRows: state.totalRows,
1501
+ percentage: state.percentage,
1502
+ message: state.message,
1503
+ code: state.code,
1504
+ startedAt: state.phase === 'starting' ? Date.now() : undefined,
1505
+ endedAt: isFinalPhase ? Date.now() : undefined,
1506
+ queueLength: queuedExportCount,
1507
+ });
1508
+ if (state.phase === 'cancelled') {
1509
+ onExportCancel === null || onExportCancel === void 0 ? void 0 : onExportCancel();
1510
+ }
1511
+ },
1512
+ signal: controller.signal,
1513
+ chunkSize,
1514
+ strictTotalCheck,
1515
+ sanitizeCSV,
1516
+ });
1517
+ }
1518
+ });
1250
1519
  },
1251
1520
  isExporting: () => isExporting || false,
1252
1521
  cancelExport: () => {
1253
- exportController === null || exportController === void 0 ? void 0 : exportController.abort();
1254
- setExportController === null || setExportController === void 0 ? void 0 : setExportController(null);
1522
+ const activeController = exportControllerRef.current;
1523
+ if (!activeController) {
1524
+ return;
1525
+ }
1526
+ activeController.abort();
1527
+ setExportControllerSafely((current) => (current === activeController ? null : current));
1255
1528
  if (logger.isLevelEnabled("debug"))
1256
1529
  logger.debug("Export cancelled");
1257
1530
  },
@@ -1272,17 +1545,26 @@ logging, }, ref) {
1272
1545
  initialStateConfig,
1273
1546
  enablePagination,
1274
1547
  idKey,
1275
- onDataStateChange,
1276
- fetchData,
1548
+ triggerRefresh,
1549
+ applyDataMutation,
1550
+ tableData,
1551
+ selectionState,
1277
1552
  // export
1278
1553
  exportFilename,
1279
- onExportProgress,
1554
+ exportChunkSize,
1555
+ exportStrictTotalCheck,
1556
+ exportSanitizeCSV,
1280
1557
  onExportComplete,
1281
1558
  onExportError,
1559
+ onExportCancel,
1282
1560
  onServerExport,
1283
- exportController,
1561
+ queuedExportCount,
1284
1562
  isExporting,
1285
1563
  dataMode,
1564
+ handleExportProgressInternal,
1565
+ handleExportStateChangeInternal,
1566
+ runExportWithPolicy,
1567
+ setExportControllerSafely,
1286
1568
  logger,
1287
1569
  resetAllAndReload,
1288
1570
  ]);
@@ -1343,14 +1625,12 @@ logging, }, ref) {
1343
1625
  // Export cancel callback
1344
1626
  // -------------------------------
1345
1627
  const handleCancelExport = (0, react_1.useCallback)(() => {
1346
- if (exportController) {
1347
- exportController.abort();
1348
- setExportController(null);
1349
- if (onExportCancel) {
1350
- onExportCancel();
1351
- }
1628
+ const activeController = exportControllerRef.current;
1629
+ if (activeController) {
1630
+ activeController.abort();
1631
+ setExportControllerSafely((current) => (current === activeController ? null : current));
1352
1632
  }
1353
- }, [exportController, onExportCancel]);
1633
+ }, [setExportControllerSafely]);
1354
1634
  // -------------------------------
1355
1635
  // Slot components
1356
1636
  // -------------------------------
@@ -1367,7 +1647,7 @@ logging, }, ref) {
1367
1647
  // -------------------------------
1368
1648
  return ((0, jsx_runtime_1.jsx)(data_table_context_1.DataTableProvider, { table: table, apiRef: internalApiRef, dataMode: dataMode, tableSize: tableSize, onTableSizeChange: (size) => {
1369
1649
  setTableSize(size);
1370
- }, 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, enableRefresh: enableRefresh, ...toolbarSlotProps, refreshButtonProps: {
1650
+ }, 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: {
1371
1651
  loading: tableLoading, // disable while fetching
1372
1652
  showSpinnerWhileLoading: false,
1373
1653
  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); },