@ackplus/react-tanstack-data-table 1.0.35 → 1.1.3

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/{src → dist}/index.d.ts +21 -4
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +63 -0
  5. package/{src → dist}/lib/components/droupdown/menu-dropdown.d.ts +2 -1
  6. package/dist/lib/components/droupdown/menu-dropdown.d.ts.map +1 -0
  7. package/{src → dist}/lib/components/droupdown/menu-dropdown.js +38 -7
  8. package/{src → dist}/lib/components/filters/filter-value-input.d.ts +3 -1
  9. package/dist/lib/components/filters/filter-value-input.d.ts.map +1 -0
  10. package/dist/lib/components/filters/filter-value-input.js +83 -0
  11. package/{src → dist}/lib/components/filters/index.d.ts +1 -0
  12. package/dist/lib/components/filters/index.d.ts.map +1 -0
  13. package/dist/lib/components/filters/index.js +142 -0
  14. package/{src → dist}/lib/components/headers/draggable-header.d.ts +2 -2
  15. package/dist/lib/components/headers/draggable-header.d.ts.map +1 -0
  16. package/{src → dist}/lib/components/headers/draggable-header.js +81 -17
  17. package/dist/lib/components/headers/index.d.ts +6 -0
  18. package/dist/lib/components/headers/index.d.ts.map +1 -0
  19. package/dist/lib/components/headers/index.js +21 -0
  20. package/{src → dist}/lib/components/headers/table-header.d.ts +15 -1
  21. package/dist/lib/components/headers/table-header.d.ts.map +1 -0
  22. package/{src → dist}/lib/components/headers/table-header.js +50 -17
  23. package/{src → dist}/lib/components/index.d.ts +6 -1
  24. package/dist/lib/components/index.d.ts.map +1 -0
  25. package/dist/lib/components/index.js +32 -0
  26. package/{src → dist}/lib/components/pagination/data-table-pagination.d.ts +2 -1
  27. package/dist/lib/components/pagination/data-table-pagination.d.ts.map +1 -0
  28. package/{src → dist}/lib/components/pagination/data-table-pagination.js +20 -6
  29. package/dist/lib/components/pagination/index.d.ts +5 -0
  30. package/dist/lib/components/pagination/index.d.ts.map +1 -0
  31. package/dist/lib/components/pagination/index.js +20 -0
  32. package/{src → dist}/lib/components/rows/data-table-row.d.ts +15 -2
  33. package/dist/lib/components/rows/data-table-row.d.ts.map +1 -0
  34. package/{src → dist}/lib/components/rows/data-table-row.js +58 -25
  35. package/{src → dist}/lib/components/rows/empty-data-row.d.ts +3 -3
  36. package/dist/lib/components/rows/empty-data-row.d.ts.map +1 -0
  37. package/{src → dist}/lib/components/rows/empty-data-row.js +12 -4
  38. package/dist/lib/components/rows/index.d.ts +7 -0
  39. package/dist/lib/components/rows/index.d.ts.map +1 -0
  40. package/dist/lib/components/rows/index.js +22 -0
  41. package/{src → dist}/lib/components/rows/loading-rows.d.ts +3 -1
  42. package/dist/lib/components/rows/loading-rows.d.ts.map +1 -0
  43. package/{src → dist}/lib/components/rows/loading-rows.js +27 -19
  44. package/{src → dist}/lib/components/toolbar/bulk-actions-toolbar.d.ts +4 -3
  45. package/dist/lib/components/toolbar/bulk-actions-toolbar.d.ts.map +1 -0
  46. package/dist/lib/components/toolbar/bulk-actions-toolbar.js +49 -0
  47. package/{src → dist}/lib/components/toolbar/column-filter-control.d.ts +3 -1
  48. package/dist/lib/components/toolbar/column-filter-control.d.ts.map +1 -0
  49. package/{src → dist}/lib/components/toolbar/column-filter-control.js +73 -4
  50. package/{src → dist}/lib/components/toolbar/column-pinning-control.d.ts +2 -1
  51. package/dist/lib/components/toolbar/column-pinning-control.d.ts.map +1 -0
  52. package/{src → dist}/lib/components/toolbar/column-pinning-control.js +70 -6
  53. package/{src → dist}/lib/components/toolbar/column-reset-control.d.ts +3 -1
  54. package/dist/lib/components/toolbar/column-reset-control.d.ts.map +1 -0
  55. package/{src → dist}/lib/components/toolbar/column-reset-control.js +9 -2
  56. package/{src → dist}/lib/components/toolbar/column-visibility-control.d.ts +2 -1
  57. package/dist/lib/components/toolbar/column-visibility-control.d.ts.map +1 -0
  58. package/dist/lib/components/toolbar/column-visibility-control.js +77 -0
  59. package/{src → dist}/lib/components/toolbar/data-table-toolbar.d.ts +3 -2
  60. package/dist/lib/components/toolbar/data-table-toolbar.d.ts.map +1 -0
  61. package/{src → dist}/lib/components/toolbar/data-table-toolbar.js +17 -4
  62. package/{src → dist}/lib/components/toolbar/index.d.ts +4 -0
  63. package/dist/lib/components/toolbar/index.d.ts.map +1 -0
  64. package/{src → dist}/lib/components/toolbar/index.js +6 -0
  65. package/dist/lib/components/toolbar/table-export-control.d.ts +12 -0
  66. package/dist/lib/components/toolbar/table-export-control.d.ts.map +1 -0
  67. package/dist/lib/components/toolbar/table-export-control.js +67 -0
  68. package/{src → dist}/lib/components/toolbar/table-search-control.d.ts +3 -1
  69. package/dist/lib/components/toolbar/table-search-control.d.ts.map +1 -0
  70. package/{src → dist}/lib/components/toolbar/table-search-control.js +45 -2
  71. package/{src → dist}/lib/components/toolbar/table-size-control.d.ts +3 -1
  72. package/dist/lib/components/toolbar/table-size-control.d.ts.map +1 -0
  73. package/{src → dist}/lib/components/toolbar/table-size-control.js +20 -8
  74. package/{src → dist}/lib/contexts/data-table-context.d.ts +6 -2
  75. package/dist/lib/contexts/data-table-context.d.ts.map +1 -0
  76. package/{src → dist}/lib/contexts/data-table-context.js +34 -1
  77. package/dist/lib/data-table.d.ts +5 -0
  78. package/dist/lib/data-table.d.ts.map +1 -0
  79. package/{src/lib/components/table → dist/lib}/data-table.js +427 -143
  80. package/dist/lib/features/column-filter.feature.d.ts +55 -0
  81. package/dist/lib/features/column-filter.feature.d.ts.map +1 -0
  82. package/{src → dist}/lib/features/column-filter.feature.js +116 -18
  83. package/dist/lib/features/index.d.ts +9 -0
  84. package/dist/lib/features/index.d.ts.map +1 -0
  85. package/{src → dist}/lib/features/index.js +7 -0
  86. package/{src → dist}/lib/features/selection.feature.d.ts +8 -1
  87. package/dist/lib/features/selection.feature.d.ts.map +1 -0
  88. package/{src → dist}/lib/features/selection.feature.js +76 -15
  89. package/dist/lib/icons/add-icon.d.ts +4 -0
  90. package/dist/lib/icons/add-icon.d.ts.map +1 -0
  91. package/dist/lib/icons/add-icon.js +12 -0
  92. package/dist/lib/icons/csv-icon.d.ts +4 -0
  93. package/dist/lib/icons/csv-icon.d.ts.map +1 -0
  94. package/dist/lib/icons/csv-icon.js +12 -0
  95. package/dist/lib/icons/delete-icon.d.ts +4 -0
  96. package/dist/lib/icons/delete-icon.d.ts.map +1 -0
  97. package/dist/lib/icons/delete-icon.js +12 -0
  98. package/dist/lib/icons/excel-icon.d.ts +4 -0
  99. package/dist/lib/icons/excel-icon.d.ts.map +1 -0
  100. package/dist/lib/icons/excel-icon.js +12 -0
  101. package/dist/lib/icons/index.d.ts +8 -0
  102. package/dist/lib/icons/index.d.ts.map +1 -0
  103. package/dist/lib/icons/unpin-icon.d.ts +4 -0
  104. package/dist/lib/icons/unpin-icon.d.ts.map +1 -0
  105. package/dist/lib/icons/unpin-icon.js +12 -0
  106. package/{src → dist}/lib/icons/view-comfortable-icon.d.ts +3 -1
  107. package/dist/lib/icons/view-comfortable-icon.d.ts.map +1 -0
  108. package/dist/lib/icons/view-comfortable-icon.js +12 -0
  109. package/dist/lib/icons/view-compact-icon.d.ts +4 -0
  110. package/dist/lib/icons/view-compact-icon.d.ts.map +1 -0
  111. package/dist/lib/icons/view-compact-icon.js +12 -0
  112. package/{src → dist}/lib/types/column.types.d.ts +10 -1
  113. package/dist/lib/types/column.types.d.ts.map +1 -0
  114. package/{src → dist}/lib/types/data-table-api.d.ts +2 -1
  115. package/dist/lib/types/data-table-api.d.ts.map +1 -0
  116. package/{src/lib/components/table → dist/lib/types}/data-table.types.d.ts +10 -11
  117. package/dist/lib/types/data-table.types.d.ts.map +1 -0
  118. package/{src → dist}/lib/types/export.types.d.ts +38 -0
  119. package/dist/lib/types/export.types.d.ts.map +1 -0
  120. package/dist/lib/types/export.types.js +6 -0
  121. package/{src → dist}/lib/types/index.d.ts +6 -0
  122. package/dist/lib/types/index.d.ts.map +1 -0
  123. package/dist/lib/types/index.js +32 -0
  124. package/{src → dist}/lib/types/slots.types.d.ts +50 -3
  125. package/dist/lib/types/slots.types.d.ts.map +1 -0
  126. package/{src → dist}/lib/types/table.types.d.ts +14 -0
  127. package/dist/lib/types/table.types.d.ts.map +1 -0
  128. package/{src → dist}/lib/utils/column-helpers.d.ts +10 -0
  129. package/dist/lib/utils/column-helpers.d.ts.map +1 -0
  130. package/{src → dist}/lib/utils/column-helpers.js +20 -4
  131. package/{src → dist}/lib/utils/debounced-fetch.utils.d.ts +3 -5
  132. package/dist/lib/utils/debounced-fetch.utils.d.ts.map +1 -0
  133. package/{src → dist}/lib/utils/debounced-fetch.utils.js +12 -6
  134. package/{src → dist}/lib/utils/export-utils.d.ts +13 -0
  135. package/dist/lib/utils/export-utils.d.ts.map +1 -0
  136. package/dist/lib/utils/export-utils.js +252 -0
  137. package/{src → dist}/lib/utils/index.d.ts +4 -0
  138. package/dist/lib/utils/index.d.ts.map +1 -0
  139. package/dist/lib/utils/index.js +35 -0
  140. package/{src → dist}/lib/utils/logger.d.ts +43 -0
  141. package/dist/lib/utils/logger.d.ts.map +1 -0
  142. package/{src → dist}/lib/utils/logger.js +22 -2
  143. package/{src → dist}/lib/utils/slot-helpers.d.ts +39 -1
  144. package/dist/lib/utils/slot-helpers.d.ts.map +1 -0
  145. package/{src → dist}/lib/utils/slot-helpers.js +55 -6
  146. package/{src → dist}/lib/utils/special-columns.utils.d.ts +10 -0
  147. package/dist/lib/utils/special-columns.utils.d.ts.map +1 -0
  148. package/{src → dist}/lib/utils/special-columns.utils.js +41 -5
  149. package/{src → dist}/lib/utils/styling-helpers.d.ts +20 -0
  150. package/dist/lib/utils/styling-helpers.d.ts.map +1 -0
  151. package/dist/lib/utils/styling-helpers.js +108 -0
  152. package/{src → dist}/lib/utils/table-helpers.d.ts +25 -0
  153. package/dist/lib/utils/table-helpers.d.ts.map +1 -0
  154. package/{src → dist}/lib/utils/table-helpers.js +24 -0
  155. package/package.json +36 -11
  156. package/src/index.ts +71 -0
  157. package/src/lib/components/droupdown/menu-dropdown.tsx +97 -0
  158. package/src/lib/components/filters/filter-value-input.tsx +225 -0
  159. package/src/lib/components/filters/{index.js → index.ts} +3 -6
  160. package/src/lib/components/headers/draggable-header.tsx +326 -0
  161. package/src/lib/components/headers/{index.d.ts → index.ts} +4 -0
  162. package/src/lib/components/headers/table-header.tsx +173 -0
  163. package/src/lib/components/index.ts +21 -0
  164. package/src/lib/components/pagination/data-table-pagination.tsx +99 -0
  165. package/src/lib/components/pagination/index.ts +5 -0
  166. package/src/lib/components/rows/data-table-row.tsx +208 -0
  167. package/src/lib/components/rows/empty-data-row.tsx +69 -0
  168. package/src/lib/components/rows/{index.d.ts → index.ts} +4 -0
  169. package/src/lib/components/rows/loading-rows.tsx +160 -0
  170. package/src/lib/components/toolbar/bulk-actions-toolbar.tsx +125 -0
  171. package/src/lib/components/toolbar/column-filter-control.tsx +374 -0
  172. package/src/lib/components/toolbar/column-pinning-control.tsx +275 -0
  173. package/src/lib/components/toolbar/column-reset-control.tsx +74 -0
  174. package/src/lib/components/toolbar/column-visibility-control.tsx +105 -0
  175. package/src/lib/components/toolbar/data-table-toolbar.tsx +229 -0
  176. package/src/lib/components/toolbar/index.ts +17 -0
  177. package/src/lib/components/toolbar/table-export-control.tsx +179 -0
  178. package/src/lib/components/toolbar/table-search-control.tsx +155 -0
  179. package/src/lib/components/toolbar/table-size-control.tsx +102 -0
  180. package/src/lib/contexts/data-table-context.tsx +112 -0
  181. package/src/lib/data-table.tsx +1911 -0
  182. package/src/lib/features/README.md +161 -0
  183. package/src/lib/features/column-filter.feature.ts +456 -0
  184. package/src/lib/features/index.ts +23 -0
  185. package/src/lib/features/selection.feature.ts +318 -0
  186. package/src/lib/icons/add-icon.tsx +23 -0
  187. package/src/lib/icons/csv-icon.tsx +15 -0
  188. package/src/lib/icons/delete-icon.tsx +30 -0
  189. package/src/lib/icons/excel-icon.tsx +15 -0
  190. package/src/lib/icons/unpin-icon.tsx +18 -0
  191. package/src/lib/icons/view-comfortable-icon.tsx +45 -0
  192. package/src/lib/icons/view-compact-icon.tsx +55 -0
  193. package/src/lib/types/column.types.ts +44 -0
  194. package/src/lib/types/data-table-api.ts +169 -0
  195. package/src/lib/types/data-table.types.ts +136 -0
  196. package/src/lib/types/export.types.ts +154 -0
  197. package/src/lib/types/index.ts +24 -0
  198. package/src/lib/types/slots.types.ts +332 -0
  199. package/src/lib/types/table.types.ts +90 -0
  200. package/src/lib/utils/column-helpers.ts +72 -0
  201. package/src/lib/utils/debounced-fetch.utils.ts +54 -0
  202. package/src/lib/utils/export-utils.ts +285 -0
  203. package/src/lib/utils/index.ts +27 -0
  204. package/src/lib/utils/logger.ts +203 -0
  205. package/src/lib/utils/slot-helpers.tsx +194 -0
  206. package/src/lib/utils/special-columns.utils.ts +94 -0
  207. package/src/lib/utils/styling-helpers.ts +126 -0
  208. package/src/lib/utils/table-helpers.ts +106 -0
  209. package/src/index.js +0 -27
  210. package/src/lib/components/filters/filter-value-input.js +0 -41
  211. package/src/lib/components/headers/index.js +0 -5
  212. package/src/lib/components/index.js +0 -10
  213. package/src/lib/components/pagination/index.d.ts +0 -1
  214. package/src/lib/components/pagination/index.js +0 -4
  215. package/src/lib/components/rows/index.js +0 -6
  216. package/src/lib/components/table/data-table.d.ts +0 -4
  217. package/src/lib/components/table/index.d.ts +0 -2
  218. package/src/lib/components/table/index.js +0 -5
  219. package/src/lib/components/toolbar/bulk-actions-toolbar.js +0 -30
  220. package/src/lib/components/toolbar/column-visibility-control.js +0 -31
  221. package/src/lib/components/toolbar/table-export-control.d.ts +0 -31
  222. package/src/lib/components/toolbar/table-export-control.js +0 -56
  223. package/src/lib/examples/advanced-features-example.d.ts +0 -1
  224. package/src/lib/examples/advanced-features-example.js +0 -269
  225. package/src/lib/examples/bulk-actions-test.d.ts +0 -1
  226. package/src/lib/examples/bulk-actions-test.js +0 -44
  227. package/src/lib/examples/custom-column-filter-example.d.ts +0 -1
  228. package/src/lib/examples/custom-column-filter-example.js +0 -60
  229. package/src/lib/examples/index.d.ts +0 -8
  230. package/src/lib/examples/index.js +0 -19
  231. package/src/lib/examples/selection-test-example.d.ts +0 -1
  232. package/src/lib/examples/selection-test-example.js +0 -101
  233. package/src/lib/examples/server-side-fetching-example.d.ts +0 -1
  234. package/src/lib/examples/server-side-fetching-example.js +0 -245
  235. package/src/lib/examples/server-side-test.d.ts +0 -1
  236. package/src/lib/examples/server-side-test.js +0 -9
  237. package/src/lib/examples/simple-local-example.d.ts +0 -1
  238. package/src/lib/examples/simple-local-example.js +0 -95
  239. package/src/lib/examples/simple-slots-example.d.ts +0 -1
  240. package/src/lib/examples/simple-slots-example.js +0 -115
  241. package/src/lib/features/column-filter.feature.d.ts +0 -45
  242. package/src/lib/features/index.d.ts +0 -2
  243. package/src/lib/hooks/index.d.ts +0 -1
  244. package/src/lib/hooks/index.js +0 -4
  245. package/src/lib/hooks/use-data-table-api.d.ts +0 -46
  246. package/src/lib/hooks/use-data-table-api.js +0 -690
  247. package/src/lib/icons/add-icon.d.ts +0 -2
  248. package/src/lib/icons/add-icon.js +0 -8
  249. package/src/lib/icons/csv-icon.d.ts +0 -2
  250. package/src/lib/icons/csv-icon.js +0 -8
  251. package/src/lib/icons/delete-icon.d.ts +0 -2
  252. package/src/lib/icons/delete-icon.js +0 -8
  253. package/src/lib/icons/excel-icon.d.ts +0 -2
  254. package/src/lib/icons/excel-icon.js +0 -8
  255. package/src/lib/icons/unpin-icon.d.ts +0 -2
  256. package/src/lib/icons/unpin-icon.js +0 -8
  257. package/src/lib/icons/view-comfortable-icon.js +0 -8
  258. package/src/lib/icons/view-compact-icon.d.ts +0 -2
  259. package/src/lib/icons/view-compact-icon.js +0 -8
  260. package/src/lib/types/export.types.js +0 -2
  261. package/src/lib/types/index.js +0 -8
  262. package/src/lib/utils/export-utils.js +0 -175
  263. package/src/lib/utils/index.js +0 -11
  264. package/src/lib/utils/styling-helpers.js +0 -70
  265. package/tsconfig.tsbuildinfo +0 -1
  266. /package/{src → dist}/lib/icons/index.js +0 -0
  267. /package/{src → dist}/lib/types/column.types.js +0 -0
  268. /package/{src → dist}/lib/types/data-table-api.js +0 -0
  269. /package/{src/lib/components/table → dist/lib/types}/data-table.types.js +0 -0
  270. /package/{src → dist}/lib/types/slots.types.js +0 -0
  271. /package/{src → dist}/lib/types/table.types.js +0 -0
  272. /package/src/lib/icons/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,161 @@
1
+ # Custom TanStack Table Features
2
+
3
+ This directory contains custom features that extend TanStack Table functionality following the official custom features pattern introduced in v8.14.0.
4
+
5
+ ## Custom Column Filter Feature
6
+
7
+ The Custom Column Filter Feature adds advanced column filtering capabilities to TanStack Table, providing a more sophisticated filtering UI with AND/OR logic and multiple operators.
8
+
9
+ ### Features
10
+
11
+ - **Multiple Filter Conditions**: Add multiple filter rules for different columns
12
+ - **Logic Operators**: Choose between AND/OR logic for combining filters
13
+ - **Various Operators**: Support for equals, contains, greater than, less than, empty checks, etc.
14
+ - **Type-Aware Filtering**: Different operators available based on column type
15
+ - **Integrated State Management**: Filters are managed within TanStack Table's state system
16
+
17
+ ### Usage
18
+
19
+ #### 1. Import the Feature
20
+
21
+ ```typescript
22
+ import { CustomColumnFilterFeature } from '@your-package/features';
23
+ ```
24
+
25
+ #### 2. Add to Table Options
26
+
27
+ ```typescript
28
+ const table = useReactTable({
29
+ _features: [CustomColumnFilterFeature], // Add the custom feature
30
+ data,
31
+ columns,
32
+ // ... other options
33
+ });
34
+ ```
35
+
36
+ #### 3. Use the Feature Methods
37
+
38
+ The feature adds several methods to your table instance:
39
+
40
+ ```typescript
41
+ // Add a new filter
42
+ table.addColumnFilter('columnId', 'contains', 'search value');
43
+
44
+ // Update an existing filter
45
+ table.updateColumnFilter('filterId', { value: 'new value' });
46
+
47
+ // Remove a filter
48
+ table.removeColumnFilter('filterId');
49
+
50
+ // Clear all filters
51
+ table.clearAllColumnFilters();
52
+
53
+ // Get active filters
54
+ const activeFilters = table.getActiveColumnFilters();
55
+
56
+ // Set filter logic (AND/OR)
57
+ table.setFilterLogic('OR');
58
+
59
+ // Get current filter state
60
+ const filterState = table.getColumnFilterState();
61
+ ```
62
+
63
+ #### 4. Handle State Changes
64
+
65
+ ```typescript
66
+ const table = useReactTable({
67
+ _features: [CustomColumnFilterFeature],
68
+ // ... other options
69
+ onCustomColumnFilterChange: (updater) => {
70
+ // Handle filter state changes
71
+ const newState = typeof updater === 'function'
72
+ ? updater(currentState)
73
+ : updater;
74
+
75
+ // Update your local state or trigger server requests
76
+ handleFilterChange(newState);
77
+ },
78
+ });
79
+ ```
80
+
81
+ ### Available Filter Operators
82
+
83
+ | Operator | Description | Types |
84
+ |----------|-------------|-------|
85
+ | `equals` | Exact match | All |
86
+ | `notEquals` | Not equal to | All |
87
+ | `contains` | Contains substring | Text |
88
+ | `notContains` | Does not contain | Text |
89
+ | `startsWith` | Starts with | Text |
90
+ | `endsWith` | Ends with | Text |
91
+ | `isEmpty` | Is empty/null | All |
92
+ | `isNotEmpty` | Is not empty/null | All |
93
+ | `greaterThan` | Greater than | Number, Date |
94
+ | `greaterThanOrEqual` | Greater than or equal | Number, Date |
95
+ | `lessThan` | Less than | Number, Date |
96
+ | `lessThanOrEqual` | Less than or equal | Number, Date |
97
+ | `between` | Between two values | Number, Date |
98
+ | `in` | In array of values | All |
99
+ | `notIn` | Not in array of values | All |
100
+
101
+ ### Client-Side Filtering
102
+
103
+ For client-side filtering, you can use the provided utility function:
104
+
105
+ ```typescript
106
+ import { matchesCustomColumnFilters } from '@your-package/features';
107
+
108
+ const filteredRows = rows.filter(row =>
109
+ matchesCustomColumnFilters(row, filters, logic)
110
+ );
111
+ ```
112
+
113
+ ### Server-Side Filtering
114
+
115
+ For server-side filtering, listen to filter changes and send the filter state to your backend:
116
+
117
+ ```typescript
118
+ const handleFilterChange = (filterState) => {
119
+ // Send to server
120
+ fetchData({
121
+ filters: filterState.filters,
122
+ logic: filterState.logic,
123
+ // ... other params
124
+ });
125
+ };
126
+ ```
127
+
128
+ ### TypeScript Integration
129
+
130
+ The feature includes full TypeScript support with declaration merging that extends TanStack Table's types:
131
+
132
+ ```typescript
133
+ // These types are automatically available after importing the feature
134
+ interface CustomColumnFilterState {
135
+ filters: ColumnFilterRule[];
136
+ logic: 'AND' | 'OR';
137
+ }
138
+
139
+ interface ColumnFilterRule {
140
+ id: string;
141
+ columnId: string;
142
+ operator: string;
143
+ value: any;
144
+ columnType?: string;
145
+ }
146
+ ```
147
+
148
+ ### Example
149
+
150
+ See `custom-column-filter-example.tsx` for a complete working example.
151
+
152
+ ## Creating Additional Custom Features
153
+
154
+ To create additional custom features, follow the TanStack Table custom features pattern:
155
+
156
+ 1. Define TypeScript types
157
+ 2. Use declaration merging to extend TanStack Table types
158
+ 3. Create the feature object with `getInitialState`, `getDefaultOptions`, and `createTable`
159
+ 4. Add the feature to your table's `_features` array
160
+
161
+ Refer to the TanStack Table documentation for more details on creating custom features.
@@ -0,0 +1,456 @@
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>) => 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
+
64
+ // Legacy methods (for backward compatibility)
65
+ addColumnFilter: (columnId: string, operator: string, value: any) => void;
66
+ updateColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
67
+ removeColumnFilter: (filterId: string) => void;
68
+ clearAllColumnFilters: () => void;
69
+ setFilterLogic: (logic: 'AND' | 'OR') => void;
70
+
71
+ // Getters
72
+ getActiveColumnFilters: () => ColumnFilterRule[];
73
+ getPendingColumnFilters: () => ColumnFilterRule[];
74
+ getColumnFilterState: () => ColumnFilterState;
75
+ }
76
+ }
77
+
78
+ // Table instance methods for custom column filtering
79
+ // export interface ColumnFilterInstance<TData extends RowData> {
80
+ // setColumnFilterState: (updater: Updater<ColumnFilterState>) => void;
81
+
82
+ // // Pending filter methods (for draft state)
83
+ // addPendingColumnFilter: (columnId: string, operator: string, value: any) => void;
84
+ // updatePendingColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
85
+ // removePendingColumnFilter: (filterId: string) => void;
86
+ // clearAllPendingColumnFilters: () => void;
87
+ // setPendingFilterLogic: (logic: 'AND' | 'OR') => void;
88
+
89
+ // // Apply pending filters to active filters
90
+ // applyPendingColumnFilters: () => void;
91
+
92
+ // // Legacy methods (for backward compatibility)
93
+ // addColumnFilter: (columnId: string, operator: string, value: any) => void;
94
+ // updateColumnFilter: (filterId: string, updates: Partial<ColumnFilterRule>) => void;
95
+ // removeColumnFilter: (filterId: string) => void;
96
+ // clearAllColumnFilters: () => void;
97
+ // setFilterLogic: (logic: 'AND' | 'OR') => void;
98
+
99
+ // // Getters
100
+ // getActiveColumnFilters: () => ColumnFilterRule[];
101
+ // getPendingColumnFilters: () => ColumnFilterRule[];
102
+ // getColumnFilterState: () => ColumnFilterState;
103
+ // }
104
+
105
+ // The custom feature implementation
106
+ export const ColumnFilterFeature: TableFeature<any> = {
107
+ // Define the feature's initial state
108
+ getInitialState: (state): { columnFilter: ColumnFilterState } => {
109
+ return {
110
+ columnFilter: {
111
+ filters: [],
112
+ logic: 'AND',
113
+ pendingFilters: [],
114
+ pendingLogic: 'AND',
115
+ },
116
+ ...state,
117
+ };
118
+ },
119
+
120
+ // Define the feature's default options
121
+ getDefaultOptions: <TData extends RowData>(
122
+ table: Table<TData>
123
+ ): ColumnFilterOptions => {
124
+ return {
125
+ enableAdvanceColumnFilter: true,
126
+ onColumnFilterChange: makeStateUpdater('columnFilter', table),
127
+ onColumnFilterApply: () => {
128
+ // Implementation of onColumnFilterApply
129
+ },
130
+ } as ColumnFilterOptions;
131
+ },
132
+
133
+ // Define the feature's table instance methods
134
+ createTable: <TData extends RowData>(table: Table<TData>): void => {
135
+ table.setColumnFilterState = (updater) => {
136
+ if (!table.options.enableAdvanceColumnFilter) return;
137
+ const safeUpdater: Updater<ColumnFilterState> = (old) => {
138
+ const newState = functionalUpdate(updater, old);
139
+ return newState;
140
+ };
141
+ return table.options.onColumnFilterChange?.(safeUpdater);
142
+ };
143
+
144
+ // === PENDING FILTER METHODS (Draft state) ===
145
+ table.addPendingColumnFilter = (columnId: string, operator: string, value: any) => {
146
+ if (!table.options.enableAdvanceColumnFilter) return;
147
+ table.setColumnFilterState((old) => {
148
+ const newFilter: ColumnFilterRule = {
149
+ id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
150
+ columnId,
151
+ operator,
152
+ value,
153
+ };
154
+ return {
155
+ ...old,
156
+ pendingFilters: [...old.pendingFilters, newFilter],
157
+ };
158
+ });
159
+ };
160
+
161
+ table.updatePendingColumnFilter = (filterId: string, updates: Partial<ColumnFilterRule>) => {
162
+ if (!table.options.enableAdvanceColumnFilter) return;
163
+ table.setColumnFilterState((old) => {
164
+ const updatedFilters = old.pendingFilters.map((filter) =>
165
+ filter.id === filterId ? { ...filter, ...updates } : filter
166
+ );
167
+ return {
168
+ ...old,
169
+ pendingFilters: updatedFilters,
170
+ };
171
+ });
172
+ };
173
+
174
+ table.removePendingColumnFilter = (filterId: string) => {
175
+ if (!table.options.enableAdvanceColumnFilter) return;
176
+ table.setColumnFilterState((old) => ({
177
+ ...old,
178
+ pendingFilters: old.pendingFilters.filter((filter) => filter.id !== filterId),
179
+ }));
180
+ };
181
+
182
+ table.clearAllPendingColumnFilters = () => {
183
+ if (!table.options.enableAdvanceColumnFilter) return;
184
+ table.setColumnFilterState((old) => ({
185
+ ...old,
186
+ pendingFilters: [],
187
+ }));
188
+ };
189
+
190
+ table.setPendingFilterLogic = (logic: 'AND' | 'OR') => {
191
+ if (!table.options.enableAdvanceColumnFilter) return;
192
+ table.setColumnFilterState((old) => ({
193
+ ...old,
194
+ pendingLogic: logic,
195
+ }));
196
+ };
197
+
198
+ // === APPLY PENDING FILTERS ===
199
+ table.applyPendingColumnFilters = () => {
200
+ if (!table.options.enableAdvanceColumnFilter) return;
201
+ table.setColumnFilterState((old) => {
202
+ const newState = {
203
+ ...old,
204
+ filters: [...old.pendingFilters],
205
+ logic: old.pendingLogic,
206
+ };
207
+
208
+ // Call the apply callback after state update
209
+ setTimeout(() => {
210
+ table.options.onColumnFilterApply?.(newState);
211
+ }, 0);
212
+
213
+ return newState;
214
+ });
215
+ };
216
+
217
+ // === LEGACY METHODS (for backward compatibility) ===
218
+ table.addColumnFilter = (columnId: string, operator: string, value: any) => {
219
+ if (!table.options.enableAdvanceColumnFilter) return;
220
+ // For backward compatibility, add directly to active filters
221
+ table.setColumnFilterState((old) => {
222
+ const newFilter: ColumnFilterRule = {
223
+ id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
224
+ columnId,
225
+ operator,
226
+ value,
227
+ };
228
+ return {
229
+ ...old,
230
+ filters: [...old.filters, newFilter],
231
+ };
232
+ });
233
+ };
234
+
235
+ table.updateColumnFilter = (filterId: string, updates: Partial<ColumnFilterRule>) => {
236
+ if (!table.options.enableAdvanceColumnFilter) return;
237
+ table.setColumnFilterState((old) => {
238
+ const updatedFilters = old.filters.map((filter) =>
239
+ filter.id === filterId ? { ...filter, ...updates } : filter
240
+ );
241
+ return {
242
+ ...old,
243
+ filters: updatedFilters,
244
+ };
245
+ });
246
+ };
247
+
248
+ table.removeColumnFilter = (filterId: string) => {
249
+ if (!table.options.enableAdvanceColumnFilter) return;
250
+ table.setColumnFilterState((old) => ({
251
+ ...old,
252
+ filters: old.filters.filter((filter) => filter.id !== filterId),
253
+ }));
254
+ };
255
+
256
+ table.clearAllColumnFilters = () => {
257
+ if (!table.options.enableAdvanceColumnFilter) return;
258
+ table.setColumnFilterState((old) => ({
259
+ ...old,
260
+ filters: [],
261
+ }));
262
+ };
263
+
264
+ table.setFilterLogic = (logic: 'AND' | 'OR') => {
265
+ if (!table.options.enableAdvanceColumnFilter) return;
266
+ table.setColumnFilterState((old) => ({
267
+ ...old,
268
+ logic,
269
+ }));
270
+ };
271
+
272
+ // === GETTERS ===
273
+ table.getActiveColumnFilters = () => {
274
+ const state = table.getState().columnFilter;
275
+ return state.filters.filter((f) => f.columnId && f.operator);
276
+ };
277
+
278
+ table.getPendingColumnFilters = () => {
279
+ const state = table.getState().columnFilter;
280
+ return state.pendingFilters.filter((f) => f.columnId && f.operator);
281
+ };
282
+
283
+ table.getColumnFilterState = () => {
284
+ return table.getState().columnFilter;
285
+ };
286
+ },
287
+ };
288
+
289
+ /**
290
+ * Utility function to check if a row matches the custom column filters
291
+ * This can be used for client-side filtering
292
+ */
293
+ export function matchesCustomColumnFilters(
294
+ row: any,
295
+ filters: ColumnFilterRule[],
296
+ logic: 'AND' | 'OR' = 'AND'
297
+ ): boolean {
298
+ if (filters.length === 0) return true;
299
+
300
+ const activeFilters = filters.filter((f) => f.columnId && f.operator);
301
+ if (activeFilters.length === 0) return true;
302
+
303
+ const results = activeFilters.map((filter) => {
304
+ let columnValue;
305
+ let columnType = filter.columnType || 'text';
306
+ try {
307
+ // Try to get the value safely to avoid infinite loops
308
+ const column = row.getAllCells().find((cell: any) => cell.column.id === filter.columnId);
309
+ if (column) {
310
+ columnValue = column.getValue();
311
+ // Try to get type from columnDef if not set
312
+ if (!filter.columnType && column.column.columnDef && column.column.columnDef.type) {
313
+ columnType = column.column.columnDef.type;
314
+ }
315
+ }
316
+ } catch (error) {
317
+ console.warn(`Error getting value for column ${filter.columnId}:`, error);
318
+ columnValue = row.original?.[filter.columnId] || '';
319
+ }
320
+ return evaluateFilterCondition(columnValue, filter.operator, filter.value, columnType);
321
+ });
322
+
323
+ return logic === 'AND' ? results.every(Boolean) : results.some(Boolean);
324
+ }
325
+
326
+ export const getCombinedFilteredRowModel = <TData,>() => {
327
+ return (table: Table<TData>) => (): RowModel<TData> => {
328
+ // Respect server/manual filtering: skip client filtering when manualFiltering is enabled
329
+ if (table.options.manualFiltering) {
330
+ return table.getCoreRowModel();
331
+ }
332
+
333
+ // Run the built-in global + column filters first:
334
+ const baseFilteredModel = getDefaultFilter<TData>()(table)();
335
+
336
+ const { filters, logic } = table.getState().columnFilter ?? {
337
+ filters: [],
338
+ logic: 'AND',
339
+ };
340
+
341
+ if (!filters.length || !table.options.enableAdvanceColumnFilter) return baseFilteredModel;
342
+
343
+ // Apply custom column filters to pre-filtered rows
344
+ const filteredRows = baseFilteredModel.rows.filter(row =>
345
+ matchesCustomColumnFilters(row, filters, logic)
346
+ );
347
+
348
+ const flatRows: Row<TData>[] = [];
349
+ const rowsById: Record<string, Row<TData>> = {};
350
+
351
+ const addRow = (row: Row<TData>) => {
352
+ flatRows.push(row);
353
+ rowsById[row.id] = row;
354
+ row.subRows?.forEach(addRow);
355
+ };
356
+
357
+ filteredRows.forEach(addRow);
358
+
359
+ return {
360
+ rows: filteredRows,
361
+ flatRows,
362
+ rowsById,
363
+ };
364
+ };
365
+ };
366
+
367
+ /**
368
+ * Evaluate a single filter condition
369
+ */
370
+ function evaluateFilterCondition(columnValue: any, operator: string, filterValue: any, type: string = 'text'): boolean {
371
+ // --- Date helpers using moment ---
372
+ function toMoment(val: any) {
373
+ if (!val) return null;
374
+ const m = moment(val);
375
+ return m.isValid() ? m : null;
376
+ }
377
+
378
+ // --- Date type logic ---
379
+ if (type === 'date') {
380
+ const mCol = toMoment(columnValue);
381
+ const mFilter = toMoment(filterValue);
382
+ if (!mCol || !mFilter) return false;
383
+ switch (operator) {
384
+ case 'equals':
385
+ return mCol.isSame(mFilter, 'day');
386
+ case 'notEquals':
387
+ return !mCol.isSame(mFilter, 'day');
388
+ case 'after':
389
+ return mCol.isAfter(mFilter, 'day');
390
+ case 'before':
391
+ return mCol.isBefore(mFilter, 'day');
392
+ case 'isEmpty':
393
+ return !columnValue;
394
+ case 'isNotEmpty':
395
+ return !!columnValue;
396
+ default:
397
+ return true;
398
+ }
399
+ }
400
+
401
+ // --- Boolean type logic ---
402
+ if (type === 'boolean') {
403
+ switch (operator) {
404
+ case 'is':
405
+ if (filterValue === 'any') return true;
406
+ if (filterValue === 'true') return (columnValue === true || columnValue === 'true' || columnValue === 1 || columnValue === '1' || columnValue === 'Yes' || columnValue === 'yes');
407
+ if (filterValue === 'false') return (columnValue === false || columnValue === 'false' || columnValue === 0 || columnValue === '0' || columnValue === 'No' || columnValue === 'no');
408
+ return false;
409
+
410
+ default:
411
+ return true;
412
+ }
413
+ }
414
+
415
+ // --- Select type logic (in, notIn, single select) ---
416
+ if (type === 'select') {
417
+ if (operator === 'in' || operator === 'notIn') {
418
+ if (Array.isArray(filterValue)) {
419
+ if (operator === 'in') return filterValue.includes(columnValue);
420
+ if (operator === 'notIn') return !filterValue.includes(columnValue);
421
+ }
422
+ return false;
423
+ }
424
+ if (operator === 'equals' || operator === 'notEquals') {
425
+ return operator === 'equals'
426
+ ? columnValue === filterValue
427
+ : columnValue !== filterValue;
428
+ }
429
+ }
430
+
431
+ // --- Text/Number type logic ---
432
+ switch (operator) {
433
+ case 'contains':
434
+ return String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
435
+ case 'notContains':
436
+ return !String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
437
+ case 'startsWith':
438
+ return String(columnValue).toLowerCase().startsWith(String(filterValue).toLowerCase());
439
+ case 'endsWith':
440
+ return String(columnValue).toLowerCase().endsWith(String(filterValue).toLowerCase());
441
+ case 'isEmpty':
442
+ return columnValue === null || columnValue === undefined || columnValue === '';
443
+ case 'isNotEmpty':
444
+ return columnValue !== null && columnValue !== undefined && columnValue !== '';
445
+ case 'greaterThan':
446
+ return Number(columnValue) > Number(filterValue);
447
+ case 'greaterThanOrEqual':
448
+ return Number(columnValue) >= Number(filterValue);
449
+ case 'lessThan':
450
+ return Number(columnValue) < Number(filterValue);
451
+ case 'lessThanOrEqual':
452
+ return Number(columnValue) <= Number(filterValue);
453
+ default:
454
+ return true;
455
+ }
456
+ }
@@ -0,0 +1,23 @@
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';