@ackplus/react-tanstack-data-table 1.1.20 → 1.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/lib/hooks/use-data-table-engine.d.ts.map +1 -1
  2. package/dist/lib/hooks/use-data-table-engine.js +4 -0
  3. package/package.json +3 -4
  4. package/src/index.ts +0 -75
  5. package/src/lib/components/data-table-view.tsx +0 -386
  6. package/src/lib/components/droupdown/menu-dropdown.tsx +0 -103
  7. package/src/lib/components/filters/filter-value-input.tsx +0 -225
  8. package/src/lib/components/filters/index.ts +0 -126
  9. package/src/lib/components/headers/draggable-header.tsx +0 -326
  10. package/src/lib/components/headers/index.ts +0 -6
  11. package/src/lib/components/headers/table-header.tsx +0 -175
  12. package/src/lib/components/index.ts +0 -21
  13. package/src/lib/components/pagination/data-table-pagination.tsx +0 -111
  14. package/src/lib/components/pagination/index.ts +0 -5
  15. package/src/lib/components/rows/data-table-row.tsx +0 -218
  16. package/src/lib/components/rows/empty-data-row.tsx +0 -69
  17. package/src/lib/components/rows/index.ts +0 -7
  18. package/src/lib/components/rows/loading-rows.tsx +0 -164
  19. package/src/lib/components/toolbar/bulk-actions-toolbar.tsx +0 -125
  20. package/src/lib/components/toolbar/column-filter-control.tsx +0 -432
  21. package/src/lib/components/toolbar/column-pinning-control.tsx +0 -275
  22. package/src/lib/components/toolbar/column-reset-control.tsx +0 -74
  23. package/src/lib/components/toolbar/column-visibility-control.tsx +0 -105
  24. package/src/lib/components/toolbar/data-table-toolbar.tsx +0 -257
  25. package/src/lib/components/toolbar/index.ts +0 -17
  26. package/src/lib/components/toolbar/table-export-control.tsx +0 -233
  27. package/src/lib/components/toolbar/table-refresh-control.tsx +0 -62
  28. package/src/lib/components/toolbar/table-search-control.tsx +0 -155
  29. package/src/lib/components/toolbar/table-size-control.tsx +0 -102
  30. package/src/lib/contexts/data-table-context.tsx +0 -126
  31. package/src/lib/data-table.tsx +0 -29
  32. package/src/lib/features/README.md +0 -161
  33. package/src/lib/features/column-filter.feature.ts +0 -493
  34. package/src/lib/features/index.ts +0 -23
  35. package/src/lib/features/selection.feature.ts +0 -322
  36. package/src/lib/hooks/index.ts +0 -2
  37. package/src/lib/hooks/use-data-table-engine.ts +0 -1552
  38. package/src/lib/icons/add-icon.tsx +0 -23
  39. package/src/lib/icons/csv-icon.tsx +0 -15
  40. package/src/lib/icons/delete-icon.tsx +0 -30
  41. package/src/lib/icons/excel-icon.tsx +0 -15
  42. package/src/lib/icons/index.ts +0 -7
  43. package/src/lib/icons/unpin-icon.tsx +0 -18
  44. package/src/lib/icons/view-comfortable-icon.tsx +0 -45
  45. package/src/lib/icons/view-compact-icon.tsx +0 -55
  46. package/src/lib/types/column.types.ts +0 -63
  47. package/src/lib/types/data-table-api.ts +0 -191
  48. package/src/lib/types/data-table.types.ts +0 -193
  49. package/src/lib/types/export.types.ts +0 -223
  50. package/src/lib/types/index.ts +0 -24
  51. package/src/lib/types/slots.types.ts +0 -342
  52. package/src/lib/types/table.types.ts +0 -88
  53. package/src/lib/utils/column-helpers.ts +0 -72
  54. package/src/lib/utils/debounced-fetch.utils.ts +0 -131
  55. package/src/lib/utils/export-utils.ts +0 -712
  56. package/src/lib/utils/index.ts +0 -27
  57. package/src/lib/utils/logger.ts +0 -203
  58. package/src/lib/utils/slot-helpers.tsx +0 -194
  59. package/src/lib/utils/special-columns.utils.ts +0 -101
  60. package/src/lib/utils/styling-helpers.ts +0 -126
  61. package/src/lib/utils/table-helpers.ts +0 -106
@@ -1,493 +0,0 @@
1
- /**
2
- * Custom Column Filter Feature for TanStack Table
3
- *
4
- * This feature adds advanced column filtering capabilities to TanStack Table
5
- * following the official custom features pattern introduced in v8.14.0
6
- */
7
- import {
8
- Table,
9
- TableFeature,
10
- RowData,
11
- Updater,
12
- functionalUpdate,
13
- makeStateUpdater,
14
- RowModel,
15
- Row,
16
- getFilteredRowModel as getDefaultFilter,
17
- } from '@tanstack/react-table';
18
-
19
- // Import from types to avoid circular dependency
20
- import type { ColumnFilterState } from '../types/table.types';
21
- import moment from 'moment';
22
-
23
- // Types for the custom column filter feature
24
- export interface ColumnFilterRule {
25
- id: string;
26
- columnId: string;
27
- operator: string;
28
- value: any;
29
- columnType?: string;
30
- }
31
-
32
- export interface ColumnFilterOptions {
33
- enableAdvanceColumnFilter?: boolean;
34
- onColumnFilterChange?: (updater: Updater<ColumnFilterState>) => void;
35
- // Add callback for when filters are applied
36
- onColumnFilterApply?: (state: ColumnFilterState) => void;
37
- }
38
-
39
- // Declaration merging to extend TanStack Table types
40
- declare module '@tanstack/react-table' {
41
- interface TableState {
42
- columnFilter: ColumnFilterState;
43
- }
44
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
- interface TableOptionsResolved<TData extends RowData> {
46
- enableAdvanceColumnFilter?: boolean;
47
- onColumnFilterChange?: (updater: Updater<ColumnFilterState>) => void;
48
- onColumnFilterApply?: (state: ColumnFilterState) => void;
49
- }
50
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
- interface Table<TData extends RowData> {
52
- setColumnFilterState: (updater: Updater<ColumnFilterState> | ColumnFilterState) => void;
53
-
54
- // Pending filter methods (for draft state)
55
- addPendingColumnFilter: (columnId: string, operator: string, value: any) => void;
56
- updatePendingColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
57
- removePendingColumnFilter: (filterId: string) => void;
58
- clearAllPendingColumnFilters: () => void;
59
- setPendingFilterLogic: (logic: 'AND' | 'OR') => void;
60
-
61
- // Apply pending filters to active filters
62
- applyPendingColumnFilters: () => void;
63
- resetColumnFilter: () => void;
64
-
65
- // Legacy methods (for backward compatibility)
66
- addColumnFilter: (columnId: string, operator: string, value: any) => void;
67
- updateColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
68
- removeColumnFilter: (filterId: string) => void;
69
- clearAllColumnFilters: () => void;
70
- setFilterLogic: (logic: 'AND' | 'OR') => void;
71
-
72
- // Getters
73
- getActiveColumnFilters: () => ColumnFilterRule[];
74
- getPendingColumnFilters: () => ColumnFilterRule[];
75
- getColumnFilterState: () => ColumnFilterState;
76
- }
77
- }
78
-
79
- // Table instance methods for custom column filtering
80
- // export interface ColumnFilterInstance<TData extends RowData> {
81
- // setColumnFilterState: (updater: Updater<ColumnFilterState>) => void;
82
-
83
- // // Pending filter methods (for draft state)
84
- // addPendingColumnFilter: (columnId: string, operator: string, value: any) => void;
85
- // updatePendingColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
86
- // removePendingColumnFilter: (filterId: string) => void;
87
- // clearAllPendingColumnFilters: () => void;
88
- // setPendingFilterLogic: (logic: 'AND' | 'OR') => void;
89
-
90
- // // Apply pending filters to active filters
91
- // applyPendingColumnFilters: () => void;
92
-
93
- // // Legacy methods (for backward compatibility)
94
- // addColumnFilter: (columnId: string, operator: string, value: any) => void;
95
- // updateColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
96
- // removeColumnFilter: (filterId: string) => void;
97
- // clearAllColumnFilters: () => void;
98
- // setFilterLogic: (logic: 'AND' | 'OR') => void;
99
-
100
- // // Getters
101
- // getActiveColumnFilters: () => ColumnFilterRule[];
102
- // getPendingColumnFilters: () => ColumnFilterRule[];
103
- // getColumnFilterState: () => ColumnFilterState;
104
- // }
105
-
106
- // The custom feature implementation
107
- export const ColumnFilterFeature: TableFeature<any> = {
108
- // Define the feature's initial state
109
- getInitialState: (state): { columnFilter: ColumnFilterState } => {
110
- return {
111
- columnFilter: {
112
- filters: [],
113
- logic: 'AND',
114
- pendingFilters: [],
115
- pendingLogic: 'AND',
116
- },
117
- ...state,
118
- };
119
- },
120
-
121
- // Define the feature's default options
122
- getDefaultOptions: <TData extends RowData>(
123
- table: Table<TData>
124
- ): ColumnFilterOptions => {
125
- return {
126
- enableAdvanceColumnFilter: true,
127
- onColumnFilterChange: makeStateUpdater('columnFilter', table),
128
- onColumnFilterApply: () => {
129
- // Implementation of onColumnFilterApply
130
- },
131
- } as ColumnFilterOptions;
132
- },
133
-
134
- // Define the feature's table instance methods
135
- createTable: <TData extends RowData>(table: Table<TData>): void => {
136
- table.setColumnFilterState = (updater) => {
137
- if (!table.options.enableAdvanceColumnFilter) return;
138
- const safeUpdater: Updater<ColumnFilterState> = (old) => {
139
- const newState = functionalUpdate(updater, old);
140
- return newState;
141
- };
142
- return table.options.onColumnFilterChange?.(safeUpdater);
143
- };
144
-
145
- // === PENDING FILTER METHODS (Draft state) ===
146
- table.addPendingColumnFilter = (columnId: string, operator: string, value: any) => {
147
- if (!table.options.enableAdvanceColumnFilter) return;
148
- table.setColumnFilterState((old) => {
149
- const newFilter: ColumnFilterRule = {
150
- id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
151
- columnId,
152
- operator,
153
- value,
154
- };
155
- return {
156
- ...old,
157
- pendingFilters: [...old.pendingFilters, newFilter],
158
- };
159
- });
160
- };
161
-
162
- table.updatePendingColumnFilter = (filterId: string, updates: Partial<ColumnFilterRule>) => {
163
- if (!table.options.enableAdvanceColumnFilter) return;
164
- table.setColumnFilterState((old) => {
165
- const updatedFilters = old.pendingFilters.map((filter) =>
166
- filter.id === filterId ? { ...filter, ...updates } : filter
167
- );
168
- return {
169
- ...old,
170
- pendingFilters: updatedFilters,
171
- };
172
- });
173
- };
174
-
175
- table.removePendingColumnFilter = (filterId: string) => {
176
- if (!table.options.enableAdvanceColumnFilter) return;
177
- table.setColumnFilterState((old) => ({
178
- ...old,
179
- pendingFilters: old.pendingFilters.filter((filter) => filter.id !== filterId),
180
- }));
181
- };
182
-
183
- table.clearAllPendingColumnFilters = () => {
184
- if (!table.options.enableAdvanceColumnFilter) return;
185
- table.setColumnFilterState((old) => ({
186
- ...old,
187
- pendingFilters: [],
188
- }));
189
- };
190
-
191
- table.resetColumnFilter = () => {
192
- if (!table.options.enableAdvanceColumnFilter) return;
193
- const newState: ColumnFilterState = {
194
- pendingFilters: [],
195
- pendingLogic: 'AND',
196
- filters: [],
197
- logic: 'AND',
198
- };
199
- table.setColumnFilterState(newState);
200
- // Notify engine so it can reset pagination and refetch (server mode)
201
- table.options.onColumnFilterApply?.(newState);
202
- };
203
-
204
- table.setPendingFilterLogic = (logic: 'AND' | 'OR') => {
205
- if (!table.options.enableAdvanceColumnFilter) return;
206
- table.setColumnFilterState((old) => ({
207
- ...old,
208
- pendingLogic: logic,
209
- }));
210
- };
211
-
212
- // === APPLY PENDING FILTERS ===
213
- table.applyPendingColumnFilters = () => {
214
- if (!table.options.enableAdvanceColumnFilter) return;
215
- table.setColumnFilterState((old) => {
216
- const newState = {
217
- ...old,
218
- filters: [...old.pendingFilters],
219
- logic: old.pendingLogic,
220
- };
221
-
222
- // Call the apply callback after state update
223
- setTimeout(() => {
224
- table.options.onColumnFilterApply?.(newState);
225
- }, 0);
226
-
227
- return newState;
228
- });
229
- };
230
-
231
- // === LEGACY METHODS (for backward compatibility) ===
232
- table.addColumnFilter = (columnId: string, operator: string, value: any) => {
233
- if (!table.options.enableAdvanceColumnFilter) return;
234
- // For backward compatibility, add directly to active filters
235
- table.setColumnFilterState((old) => {
236
- const newFilter: ColumnFilterRule = {
237
- id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
238
- columnId,
239
- operator,
240
- value,
241
- };
242
- return {
243
- ...old,
244
- filters: [...old.filters, newFilter],
245
- };
246
- });
247
- };
248
-
249
- table.updateColumnFilter = (filterId: string, updates: Partial<ColumnFilterRule>) => {
250
- if (!table.options.enableAdvanceColumnFilter) return;
251
- table.setColumnFilterState((old) => {
252
- const updatedFilters = old.filters.map((filter) =>
253
- filter.id === filterId ? { ...filter, ...updates } : filter
254
- );
255
- return {
256
- ...old,
257
- filters: updatedFilters,
258
- };
259
- });
260
- };
261
-
262
- table.removeColumnFilter = (filterId: string) => {
263
- if (!table.options.enableAdvanceColumnFilter) return;
264
- table.setColumnFilterState((old) => ({
265
- ...old,
266
- filters: old.filters.filter((filter) => filter.id !== filterId),
267
- }));
268
- };
269
-
270
- table.clearAllColumnFilters = () => {
271
- if (!table.options.enableAdvanceColumnFilter) return;
272
- table.setColumnFilterState((old) => ({
273
- ...old,
274
- filters: [],
275
- }));
276
- };
277
-
278
- table.setFilterLogic = (logic: 'AND' | 'OR') => {
279
- if (!table.options.enableAdvanceColumnFilter) return;
280
- table.setColumnFilterState((old) => ({
281
- ...old,
282
- logic,
283
- }));
284
- };
285
-
286
- // === GETTERS ===
287
- table.getActiveColumnFilters = () => {
288
- const state = table.getState().columnFilter;
289
- return state.filters.filter((f) => f.columnId && f.operator);
290
- };
291
-
292
- table.getPendingColumnFilters = () => {
293
- const state = table.getState().columnFilter;
294
- return state.pendingFilters.filter((f) => f.columnId && f.operator);
295
- };
296
-
297
- table.getColumnFilterState = () => {
298
- return table.getState().columnFilter;
299
- };
300
- },
301
- };
302
-
303
- /**
304
- * Utility function to check if a row matches the custom column filters
305
- * This can be used for client-side filtering
306
- */
307
- export function matchesCustomColumnFilters(
308
- row: any,
309
- filters: ColumnFilterRule[],
310
- logic: 'AND' | 'OR' = 'AND'
311
- ): boolean {
312
- if (filters.length === 0) return true;
313
-
314
- const activeFilters = filters.filter((f) => f.columnId && f.operator);
315
- if (activeFilters.length === 0) return true;
316
-
317
- const results = activeFilters.map((filter) => {
318
- let columnValue;
319
- let columnType = filter.columnType || 'text';
320
- try {
321
- // Try to get the value safely to avoid infinite loops
322
- const column = row.getAllCells().find((cell: any) => cell.column.id === filter.columnId);
323
- if (column) {
324
- columnValue = column.getValue();
325
- // Try to get type from columnDef if not set
326
- if (!filter.columnType && column.column.columnDef && column.column.columnDef.type) {
327
- columnType = column.column.columnDef.type;
328
- }
329
- }
330
- } catch (error) {
331
- console.warn(`Error getting value for column ${filter.columnId}:`, error);
332
- columnValue = row.original?.[filter.columnId] || '';
333
- }
334
- return evaluateFilterCondition(columnValue, filter.operator, filter.value, columnType);
335
- });
336
-
337
- return logic === 'AND' ? results.every(Boolean) : results.some(Boolean);
338
- }
339
-
340
- export const getCombinedFilteredRowModel = <TData,>() => {
341
- return (table: Table<TData>) => (): RowModel<TData> => {
342
- // Respect server/manual filtering: skip client filtering when manualFiltering is enabled
343
- if (table.options.manualFiltering) {
344
- return table.getCoreRowModel();
345
- }
346
-
347
- // Run the built-in global + column filters first:
348
- const baseFilteredModel = getDefaultFilter<TData>()(table)();
349
-
350
- const { filters, logic } = table.getState().columnFilter ?? {
351
- filters: [],
352
- logic: 'AND',
353
- };
354
-
355
- if (!filters.length || !table.options.enableAdvanceColumnFilter) {
356
- return baseFilteredModel;
357
- }
358
-
359
- // Apply custom column filters to pre-filtered rows
360
- const filteredRows = baseFilteredModel.rows.filter(row =>
361
- matchesCustomColumnFilters(row, filters, logic)
362
- );
363
-
364
- const flatRows: Row<TData>[] = [];
365
- const rowsById: Record<string, Row<TData>> = {};
366
-
367
- const addRow = (row: Row<TData>) => {
368
- flatRows.push(row);
369
- rowsById[row.id] = row;
370
- row.subRows?.forEach(addRow);
371
- };
372
-
373
- filteredRows.forEach(addRow);
374
-
375
- return {
376
- rows: filteredRows,
377
- flatRows,
378
- rowsById,
379
- };
380
- };
381
- };
382
-
383
- /**
384
- * Evaluate a single filter condition
385
- */
386
- function evaluateFilterCondition(columnValue: any, operator: string, filterValue: any, type: string = 'text'): boolean {
387
- // --- Date helpers using moment ---
388
- function toMoment(val: any) {
389
- if (!val) return null;
390
- const m = moment(val);
391
- return m.isValid() ? m : null;
392
- }
393
-
394
- // --- Date type logic ---
395
- if (type === 'date') {
396
- if (operator === 'isEmpty') {
397
- return columnValue === null || columnValue === undefined || columnValue === '';
398
- }
399
- if (operator === 'isNotEmpty') {
400
- return columnValue !== null && columnValue !== undefined && columnValue !== '';
401
- }
402
-
403
- const mCol = columnValue ? toMoment(columnValue) : null;
404
- const mFilter = filterValue ? toMoment(filterValue) : null;
405
- if (!mCol || !mFilter || !mCol.isValid() || !mFilter.isValid()) return false;
406
- switch (operator) {
407
- case 'equals':
408
- return mCol.isSame(mFilter, 'day');
409
- case 'notEquals':
410
- return !mCol.isSame(mFilter, 'day');
411
- case 'after':
412
- return mCol.isAfter(mFilter, 'day');
413
- case 'before':
414
- return mCol.isBefore(mFilter, 'day');
415
- default:
416
- return true;
417
- }
418
- }
419
-
420
- // --- Boolean type logic ---
421
- if (type === 'boolean') {
422
- switch (operator) {
423
- case 'is':
424
- if (filterValue === 'any') return true;
425
- if (filterValue === 'true') return (columnValue === true || columnValue === 'true' || columnValue === 1 || columnValue === '1' || columnValue === 'Yes' || columnValue === 'yes');
426
- if (filterValue === 'false') return (columnValue === false || columnValue === 'false' || columnValue === 0 || columnValue === '0' || columnValue === 'No' || columnValue === 'no');
427
- return false;
428
-
429
- default:
430
- return true;
431
- }
432
- }
433
-
434
- // --- Select type logic (in, notIn, single select) ---
435
- if (type === 'select') {
436
- if (operator === 'in' || operator === 'notIn') {
437
- const values = Array.isArray(filterValue)
438
- ? filterValue
439
- : [filterValue].filter((value) => value !== undefined && value !== null && value !== '');
440
-
441
- if (values.length === 0) {
442
- return operator === 'notIn';
443
- }
444
-
445
- if (operator === 'in') return values.includes(columnValue);
446
- if (operator === 'notIn') return !values.includes(columnValue);
447
- }
448
- if (operator === 'equals' || operator === 'notEquals') {
449
- return operator === 'equals'
450
- ? columnValue === filterValue
451
- : columnValue !== filterValue;
452
- }
453
- }
454
-
455
- // --- Text/Number type logic ---
456
- if (type === 'number') {
457
- switch (operator) {
458
- case 'equals':
459
- return Number(columnValue) === Number(filterValue);
460
- case 'notEquals':
461
- return Number(columnValue) !== Number(filterValue);
462
- case 'greaterThan':
463
- return Number(columnValue) > Number(filterValue);
464
- case 'greaterThanOrEqual':
465
- return Number(columnValue) >= Number(filterValue);
466
- case 'lessThan':
467
- return Number(columnValue) < Number(filterValue);
468
- case 'lessThanOrEqual':
469
- return Number(columnValue) <= Number(filterValue);
470
- }
471
- }
472
-
473
- switch (operator) {
474
- case 'contains':
475
- return String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
476
- case 'notContains':
477
- return !String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
478
- case 'startsWith':
479
- return String(columnValue).toLowerCase().startsWith(String(filterValue).toLowerCase());
480
- case 'endsWith':
481
- return String(columnValue).toLowerCase().endsWith(String(filterValue).toLowerCase());
482
- case 'equals':
483
- return columnValue === filterValue;
484
- case 'notEquals':
485
- return columnValue !== filterValue;
486
- case 'isEmpty':
487
- return columnValue === null || columnValue === undefined || columnValue === '';
488
- case 'isNotEmpty':
489
- return columnValue !== null && columnValue !== undefined && columnValue !== '';
490
- default:
491
- return true;
492
- }
493
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Custom TanStack Table Features
3
- *
4
- * This module exports custom features that extend TanStack Table functionality
5
- * following the official custom features pattern introduced in v8.14.0
6
- */
7
-
8
- export {
9
- ColumnFilterFeature,
10
- matchesCustomColumnFilters,
11
- type ColumnFilterRule,
12
- type ColumnFilterOptions,
13
- } from './column-filter.feature';
14
-
15
- // Export custom selection feature
16
- export {
17
- SelectionFeature,
18
- type SelectionState,
19
- type SelectMode,
20
- type SelectionOptions,
21
- type SelectionTableState,
22
- type SelectionInstance,
23
- } from './selection.feature';