@ackplus/react-tanstack-data-table 1.0.19-beta-0.6
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.
- package/LICENSE +21 -0
- package/README.md +599 -0
- package/index.d.ts.map +1 -0
- package/index.js +42 -0
- package/lib/components/droupdown/menu-dropdown.d.ts.map +1 -0
- package/lib/components/droupdown/menu-dropdown.js +47 -0
- package/lib/components/export-progress-dialog.d.ts.map +1 -0
- package/lib/components/export-progress-dialog.js +30 -0
- package/lib/components/filters/filter-value-input.d.ts.map +1 -0
- package/lib/components/filters/filter-value-input.js +64 -0
- package/lib/components/filters/index.d.ts.map +1 -0
- package/lib/components/filters/index.js +125 -0
- package/lib/components/headers/draggable-header.d.ts.map +1 -0
- package/lib/components/headers/draggable-header.js +226 -0
- package/lib/components/headers/index.d.ts.map +1 -0
- package/lib/components/headers/index.js +5 -0
- package/lib/components/headers/table-header.d.ts.map +1 -0
- package/lib/components/headers/table-header.js +59 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +18 -0
- package/lib/components/pagination/data-table-pagination.d.ts.map +1 -0
- package/lib/components/pagination/data-table-pagination.js +24 -0
- package/lib/components/pagination/index.d.ts.map +1 -0
- package/lib/components/pagination/index.js +4 -0
- package/lib/components/rows/data-table-row.d.ts.map +1 -0
- package/lib/components/rows/data-table-row.js +42 -0
- package/lib/components/rows/empty-data-row.d.ts.map +1 -0
- package/lib/components/rows/empty-data-row.js +8 -0
- package/lib/components/rows/index.d.ts.map +1 -0
- package/lib/components/rows/index.js +6 -0
- package/lib/components/rows/loading-rows.d.ts.map +1 -0
- package/lib/components/rows/loading-rows.js +46 -0
- package/lib/components/table/data-table.d.ts.map +1 -0
- package/lib/components/table/data-table.js +663 -0
- package/lib/components/table/data-table.types.d.ts.map +1 -0
- package/lib/components/table/data-table.types.js +6 -0
- package/lib/components/table/index.d.ts.map +1 -0
- package/lib/components/table/index.js +5 -0
- package/lib/components/toolbar/bulk-actions-toolbar.d.ts.map +1 -0
- package/lib/components/toolbar/bulk-actions-toolbar.js +31 -0
- package/lib/components/toolbar/column-custom-filter-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-custom-filter-control.js +149 -0
- package/lib/components/toolbar/column-custum-filter-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-custum-filter-control.js +150 -0
- package/lib/components/toolbar/column-pinning-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-pinning-control.js +103 -0
- package/lib/components/toolbar/column-reset-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-reset-control.js +13 -0
- package/lib/components/toolbar/column-visibility-control.d.ts.map +1 -0
- package/lib/components/toolbar/column-visibility-control.js +27 -0
- package/lib/components/toolbar/data-table-toolbar.d.ts.map +1 -0
- package/lib/components/toolbar/data-table-toolbar.js +23 -0
- package/lib/components/toolbar/index.d.ts.map +1 -0
- package/lib/components/toolbar/index.js +13 -0
- package/lib/components/toolbar/table-export-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-export-control.js +94 -0
- package/lib/components/toolbar/table-search-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-search-control.js +61 -0
- package/lib/components/toolbar/table-size-control.d.ts.map +1 -0
- package/lib/components/toolbar/table-size-control.js +33 -0
- package/lib/contexts/data-table-context.d.ts.map +1 -0
- package/lib/contexts/data-table-context.js +50 -0
- package/lib/examples/advanced-features-example.d.ts.map +1 -0
- package/lib/examples/advanced-features-example.js +282 -0
- package/lib/examples/basic-example.d.ts.map +1 -0
- package/lib/examples/basic-example.js +323 -0
- package/lib/examples/bulk-actions-test.d.ts.map +1 -0
- package/lib/examples/bulk-actions-test.js +47 -0
- package/lib/examples/crud-api-example.d.ts.map +1 -0
- package/lib/examples/crud-api-example.js +321 -0
- package/lib/examples/custom-column-filter-example.d.ts.map +1 -0
- package/lib/examples/custom-column-filter-example.js +60 -0
- package/lib/examples/custom-selection-example.d.ts.map +1 -0
- package/lib/examples/custom-selection-example.js +184 -0
- package/lib/examples/export-callbacks-example.d.ts.map +1 -0
- package/lib/examples/export-callbacks-example.js +155 -0
- package/lib/examples/improved-export-example.d.ts.map +1 -0
- package/lib/examples/improved-export-example.js +153 -0
- package/lib/examples/improved-server-selection-example.d.ts.map +1 -0
- package/lib/examples/improved-server-selection-example.js +118 -0
- package/lib/examples/index.d.ts.map +1 -0
- package/lib/examples/index.js +5 -0
- package/lib/examples/selection-test-example.d.ts.map +1 -0
- package/lib/examples/selection-test-example.js +111 -0
- package/lib/examples/simple-local-example.d.ts.map +1 -0
- package/lib/examples/simple-local-example.js +101 -0
- package/lib/examples/simple-server-selection-example.d.ts.map +1 -0
- package/lib/examples/simple-server-selection-example.js +178 -0
- package/lib/examples/virtualized-example.d.ts.map +1 -0
- package/lib/examples/virtualized-example.js +119 -0
- package/lib/features/custom-column-filter.feature.d.ts.map +1 -0
- package/lib/features/custom-column-filter.feature.js +306 -0
- package/lib/features/custom-selection.feature.d.ts.map +1 -0
- package/lib/features/custom-selection.feature.js +224 -0
- package/lib/features/index.d.ts.map +1 -0
- package/lib/features/index.js +9 -0
- package/lib/hooks/index.d.ts.map +1 -0
- package/lib/hooks/index.js +6 -0
- package/lib/hooks/use-data-table-api.d.ts.map +1 -0
- package/lib/hooks/use-data-table-api.js +673 -0
- package/lib/hooks/use-table-state.d.ts.map +1 -0
- package/lib/hooks/use-table-state.js +74 -0
- package/lib/icons/add-icon.d.ts.map +1 -0
- package/lib/icons/add-icon.js +5 -0
- package/lib/icons/csv-icon.d.ts.map +1 -0
- package/lib/icons/csv-icon.js +5 -0
- package/lib/icons/delete-icon.d.ts.map +1 -0
- package/lib/icons/delete-icon.js +5 -0
- package/lib/icons/excel-icon.d.ts.map +1 -0
- package/lib/icons/excel-icon.js +5 -0
- package/lib/icons/index.d.ts.map +1 -0
- package/lib/icons/index.js +7 -0
- package/lib/icons/unpin-icon.d.ts.map +1 -0
- package/lib/icons/unpin-icon.js +5 -0
- package/lib/icons/view-comfortable-icon.d.ts.map +1 -0
- package/lib/icons/view-comfortable-icon.js +5 -0
- package/lib/icons/view-compact-icon.d.ts.map +1 -0
- package/lib/icons/view-compact-icon.js +5 -0
- package/lib/types/column.types.d.ts.map +1 -0
- package/lib/types/column.types.js +2 -0
- package/lib/types/data-table-api.d.ts.map +1 -0
- package/lib/types/data-table-api.js +1 -0
- package/lib/types/export.types.d.ts.map +1 -0
- package/lib/types/export.types.js +5 -0
- package/lib/types/hooks.types.d.ts.map +1 -0
- package/lib/types/hooks.types.js +1 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +14 -0
- package/lib/types/slots.types.d.ts.map +1 -0
- package/lib/types/slots.types.js +1 -0
- package/lib/types/table.types.d.ts.map +1 -0
- package/lib/types/table.types.js +1 -0
- package/lib/utils/column-helpers.d.ts.map +1 -0
- package/lib/utils/column-helpers.js +46 -0
- package/lib/utils/debounced-fetch.utils.d.ts.map +1 -0
- package/lib/utils/debounced-fetch.utils.js +51 -0
- package/lib/utils/export-utils.d.ts.map +1 -0
- package/lib/utils/export-utils.js +181 -0
- package/lib/utils/index.d.ts.map +1 -0
- package/lib/utils/index.js +17 -0
- package/lib/utils/selection-helpers.d.ts.map +1 -0
- package/lib/utils/selection-helpers.js +162 -0
- package/lib/utils/slot-helpers.d.ts.map +1 -0
- package/lib/utils/slot-helpers.js +27 -0
- package/lib/utils/special-columns.utils.d.ts.map +1 -0
- package/lib/utils/special-columns.utils.js +77 -0
- package/lib/utils/styling-helpers.d.ts.map +1 -0
- package/lib/utils/styling-helpers.js +97 -0
- package/lib/utils/table-helpers.d.ts.map +1 -0
- package/lib/utils/table-helpers.js +72 -0
- package/lib/utils/value-helpers.d.ts.map +1 -0
- package/lib/utils/value-helpers.js +48 -0
- package/package.json +57 -0
- package/tsconfig.lib.tsbuildinfo +1 -0
|
@@ -0,0 +1,306 @@
|
|
|
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 { functionalUpdate, makeStateUpdater, getFilteredRowModel as getDefaultFilter, } from '@tanstack/react-table';
|
|
8
|
+
import moment from 'moment';
|
|
9
|
+
// The custom feature implementation
|
|
10
|
+
export const CustomColumnFilterFeature = {
|
|
11
|
+
// Define the feature's initial state
|
|
12
|
+
getInitialState: (state) => {
|
|
13
|
+
return {
|
|
14
|
+
customColumnFilter: {
|
|
15
|
+
filters: [],
|
|
16
|
+
logic: 'AND',
|
|
17
|
+
pendingFilters: [],
|
|
18
|
+
pendingLogic: 'AND',
|
|
19
|
+
},
|
|
20
|
+
...state,
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
// Define the feature's default options
|
|
24
|
+
getDefaultOptions: (table) => {
|
|
25
|
+
return {
|
|
26
|
+
enableCustomColumnFilter: true,
|
|
27
|
+
onCustomColumnFilterChange: makeStateUpdater('customColumnFilter', table),
|
|
28
|
+
onCustomColumnFilterApply: (state) => {
|
|
29
|
+
// Implementation of onCustomColumnFilterApply
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
// Define the feature's table instance methods
|
|
34
|
+
createTable: (table) => {
|
|
35
|
+
table.setCustomColumnFilter = (updater) => {
|
|
36
|
+
const safeUpdater = (old) => {
|
|
37
|
+
const newState = functionalUpdate(updater, old);
|
|
38
|
+
return newState;
|
|
39
|
+
};
|
|
40
|
+
return table.options.onCustomColumnFilterChange?.(safeUpdater);
|
|
41
|
+
};
|
|
42
|
+
// === PENDING FILTER METHODS (Draft state) ===
|
|
43
|
+
table.addPendingColumnFilter = (columnId, operator, value) => {
|
|
44
|
+
table.setCustomColumnFilter((old) => {
|
|
45
|
+
const newFilter = {
|
|
46
|
+
id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
47
|
+
columnId,
|
|
48
|
+
operator,
|
|
49
|
+
value,
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
...old,
|
|
53
|
+
pendingFilters: [...old.pendingFilters, newFilter],
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
table.updatePendingColumnFilter = (filterId, updates) => {
|
|
58
|
+
table.setCustomColumnFilter((old) => {
|
|
59
|
+
const updatedFilters = old.pendingFilters.map((filter) => filter.id === filterId ? { ...filter, ...updates } : filter);
|
|
60
|
+
return {
|
|
61
|
+
...old,
|
|
62
|
+
pendingFilters: updatedFilters,
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
table.removePendingColumnFilter = (filterId) => {
|
|
67
|
+
table.setCustomColumnFilter((old) => ({
|
|
68
|
+
...old,
|
|
69
|
+
pendingFilters: old.pendingFilters.filter((filter) => filter.id !== filterId),
|
|
70
|
+
}));
|
|
71
|
+
};
|
|
72
|
+
table.clearAllPendingColumnFilters = () => {
|
|
73
|
+
table.setCustomColumnFilter((old) => ({
|
|
74
|
+
...old,
|
|
75
|
+
pendingFilters: [],
|
|
76
|
+
}));
|
|
77
|
+
};
|
|
78
|
+
table.setPendingFilterLogic = (logic) => {
|
|
79
|
+
table.setCustomColumnFilter((old) => ({
|
|
80
|
+
...old,
|
|
81
|
+
pendingLogic: logic,
|
|
82
|
+
}));
|
|
83
|
+
};
|
|
84
|
+
// === APPLY PENDING FILTERS ===
|
|
85
|
+
table.applyPendingColumnFilters = () => {
|
|
86
|
+
table.setCustomColumnFilter((old) => {
|
|
87
|
+
const newState = {
|
|
88
|
+
...old,
|
|
89
|
+
filters: [...old.pendingFilters],
|
|
90
|
+
logic: old.pendingLogic,
|
|
91
|
+
};
|
|
92
|
+
// Call the apply callback after state update
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
table.options.onCustomColumnFilterApply?.(newState);
|
|
95
|
+
}, 0);
|
|
96
|
+
return newState;
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
// === LEGACY METHODS (for backward compatibility) ===
|
|
100
|
+
table.addColumnFilter = (columnId, operator, value) => {
|
|
101
|
+
// For backward compatibility, add directly to active filters
|
|
102
|
+
table.setCustomColumnFilter((old) => {
|
|
103
|
+
const newFilter = {
|
|
104
|
+
id: `filter_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
105
|
+
columnId,
|
|
106
|
+
operator,
|
|
107
|
+
value,
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
...old,
|
|
111
|
+
filters: [...old.filters, newFilter],
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
table.updateColumnFilter = (filterId, updates) => {
|
|
116
|
+
table.setCustomColumnFilter((old) => {
|
|
117
|
+
const updatedFilters = old.filters.map((filter) => filter.id === filterId ? { ...filter, ...updates } : filter);
|
|
118
|
+
return {
|
|
119
|
+
...old,
|
|
120
|
+
filters: updatedFilters,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
table.removeColumnFilter = (filterId) => {
|
|
125
|
+
table.setCustomColumnFilter((old) => ({
|
|
126
|
+
...old,
|
|
127
|
+
filters: old.filters.filter((filter) => filter.id !== filterId),
|
|
128
|
+
}));
|
|
129
|
+
};
|
|
130
|
+
table.clearAllColumnFilters = () => {
|
|
131
|
+
table.setCustomColumnFilter((old) => ({
|
|
132
|
+
...old,
|
|
133
|
+
filters: [],
|
|
134
|
+
}));
|
|
135
|
+
};
|
|
136
|
+
table.setFilterLogic = (logic) => {
|
|
137
|
+
table.setCustomColumnFilter((old) => ({
|
|
138
|
+
...old,
|
|
139
|
+
logic,
|
|
140
|
+
}));
|
|
141
|
+
};
|
|
142
|
+
// === GETTERS ===
|
|
143
|
+
table.getActiveColumnFilters = () => {
|
|
144
|
+
const state = table.getState().customColumnFilter;
|
|
145
|
+
return state.filters.filter((f) => f.columnId && f.operator);
|
|
146
|
+
};
|
|
147
|
+
table.getPendingColumnFilters = () => {
|
|
148
|
+
const state = table.getState().customColumnFilter;
|
|
149
|
+
return state.pendingFilters.filter((f) => f.columnId && f.operator);
|
|
150
|
+
};
|
|
151
|
+
table.getCustomColumnFilterState = () => {
|
|
152
|
+
return table.getState().customColumnFilter;
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* Utility function to check if a row matches the custom column filters
|
|
158
|
+
* This can be used for client-side filtering
|
|
159
|
+
*/
|
|
160
|
+
export function matchesCustomColumnFilters(row, filters, logic = 'AND') {
|
|
161
|
+
if (filters.length === 0)
|
|
162
|
+
return true;
|
|
163
|
+
const activeFilters = filters.filter((f) => f.columnId && f.operator);
|
|
164
|
+
if (activeFilters.length === 0)
|
|
165
|
+
return true;
|
|
166
|
+
const results = activeFilters.map((filter) => {
|
|
167
|
+
let columnValue;
|
|
168
|
+
let columnType = filter.columnType || 'text';
|
|
169
|
+
try {
|
|
170
|
+
// Try to get the value safely to avoid infinite loops
|
|
171
|
+
const column = row.getAllCells().find((cell) => cell.column.id === filter.columnId);
|
|
172
|
+
if (column) {
|
|
173
|
+
columnValue = column.getValue();
|
|
174
|
+
// Try to get type from columnDef if not set
|
|
175
|
+
if (!filter.columnType && column.column.columnDef && column.column.columnDef.type) {
|
|
176
|
+
columnType = column.column.columnDef.type;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.warn(`Error getting value for column ${filter.columnId}:`, error);
|
|
182
|
+
columnValue = row.original?.[filter.columnId] || '';
|
|
183
|
+
}
|
|
184
|
+
return evaluateFilterCondition(columnValue, filter.operator, filter.value, columnType);
|
|
185
|
+
});
|
|
186
|
+
return logic === 'AND' ? results.every(Boolean) : results.some(Boolean);
|
|
187
|
+
}
|
|
188
|
+
export const getCombinedFilteredRowModel = () => {
|
|
189
|
+
return (table) => () => {
|
|
190
|
+
// Run the built-in global + column filters first:
|
|
191
|
+
const baseFilteredModel = getDefaultFilter()(table)();
|
|
192
|
+
const { filters, logic } = table.getState().customColumnFilter ?? {
|
|
193
|
+
filters: [],
|
|
194
|
+
logic: 'AND',
|
|
195
|
+
};
|
|
196
|
+
if (!filters.length)
|
|
197
|
+
return baseFilteredModel;
|
|
198
|
+
// Apply custom column filters to pre-filtered rows
|
|
199
|
+
const filteredRows = baseFilteredModel.rows.filter(row => matchesCustomColumnFilters(row, filters, logic));
|
|
200
|
+
const flatRows = [];
|
|
201
|
+
const rowsById = {};
|
|
202
|
+
const addRow = (row) => {
|
|
203
|
+
flatRows.push(row);
|
|
204
|
+
rowsById[row.id] = row;
|
|
205
|
+
row.subRows?.forEach(addRow);
|
|
206
|
+
};
|
|
207
|
+
filteredRows.forEach(addRow);
|
|
208
|
+
return {
|
|
209
|
+
rows: filteredRows,
|
|
210
|
+
flatRows,
|
|
211
|
+
rowsById,
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Evaluate a single filter condition
|
|
217
|
+
*/
|
|
218
|
+
function evaluateFilterCondition(columnValue, operator, filterValue, type = 'text') {
|
|
219
|
+
// --- Date helpers using moment ---
|
|
220
|
+
function toMoment(val) {
|
|
221
|
+
if (!val)
|
|
222
|
+
return null;
|
|
223
|
+
const m = moment(val);
|
|
224
|
+
return m.isValid() ? m : null;
|
|
225
|
+
}
|
|
226
|
+
// --- Date type logic ---
|
|
227
|
+
if (type === 'date') {
|
|
228
|
+
const mCol = toMoment(columnValue);
|
|
229
|
+
const mFilter = toMoment(filterValue);
|
|
230
|
+
if (!mCol || !mFilter)
|
|
231
|
+
return false;
|
|
232
|
+
switch (operator) {
|
|
233
|
+
case 'equals':
|
|
234
|
+
return mCol.isSame(mFilter, 'day');
|
|
235
|
+
case 'notEquals':
|
|
236
|
+
return !mCol.isSame(mFilter, 'day');
|
|
237
|
+
case 'after':
|
|
238
|
+
return mCol.isAfter(mFilter, 'day');
|
|
239
|
+
case 'before':
|
|
240
|
+
return mCol.isBefore(mFilter, 'day');
|
|
241
|
+
case 'isEmpty':
|
|
242
|
+
return !columnValue;
|
|
243
|
+
case 'isNotEmpty':
|
|
244
|
+
return !!columnValue;
|
|
245
|
+
default:
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// --- Boolean type logic ---
|
|
250
|
+
if (type === 'boolean') {
|
|
251
|
+
switch (operator) {
|
|
252
|
+
case 'is':
|
|
253
|
+
if (filterValue === 'any')
|
|
254
|
+
return true;
|
|
255
|
+
if (filterValue === 'true')
|
|
256
|
+
return (columnValue === true || columnValue === 'true' || columnValue === 1 || columnValue === '1' || columnValue === 'Yes' || columnValue === 'yes');
|
|
257
|
+
if (filterValue === 'false')
|
|
258
|
+
return (columnValue === false || columnValue === 'false' || columnValue === 0 || columnValue === '0' || columnValue === 'No' || columnValue === 'no');
|
|
259
|
+
return false;
|
|
260
|
+
default:
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// --- Select type logic (in, notIn, single select) ---
|
|
265
|
+
if (type === 'select') {
|
|
266
|
+
if (operator === 'in' || operator === 'notIn') {
|
|
267
|
+
if (Array.isArray(filterValue)) {
|
|
268
|
+
if (operator === 'in')
|
|
269
|
+
return filterValue.includes(columnValue);
|
|
270
|
+
if (operator === 'notIn')
|
|
271
|
+
return !filterValue.includes(columnValue);
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
if (operator === 'equals' || operator === 'notEquals') {
|
|
276
|
+
return operator === 'equals'
|
|
277
|
+
? columnValue === filterValue
|
|
278
|
+
: columnValue !== filterValue;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// --- Text/Number type logic ---
|
|
282
|
+
switch (operator) {
|
|
283
|
+
case 'contains':
|
|
284
|
+
return String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
285
|
+
case 'notContains':
|
|
286
|
+
return !String(columnValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
287
|
+
case 'startsWith':
|
|
288
|
+
return String(columnValue).toLowerCase().startsWith(String(filterValue).toLowerCase());
|
|
289
|
+
case 'endsWith':
|
|
290
|
+
return String(columnValue).toLowerCase().endsWith(String(filterValue).toLowerCase());
|
|
291
|
+
case 'isEmpty':
|
|
292
|
+
return columnValue === null || columnValue === undefined || columnValue === '';
|
|
293
|
+
case 'isNotEmpty':
|
|
294
|
+
return columnValue !== null && columnValue !== undefined && columnValue !== '';
|
|
295
|
+
case 'greaterThan':
|
|
296
|
+
return Number(columnValue) > Number(filterValue);
|
|
297
|
+
case 'greaterThanOrEqual':
|
|
298
|
+
return Number(columnValue) >= Number(filterValue);
|
|
299
|
+
case 'lessThan':
|
|
300
|
+
return Number(columnValue) < Number(filterValue);
|
|
301
|
+
case 'lessThanOrEqual':
|
|
302
|
+
return Number(columnValue) <= Number(filterValue);
|
|
303
|
+
default:
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-selection.feature.d.ts","sourceRoot":"","sources":["../../../src/lib/features/custom-selection.feature.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAEH,YAAY,EACZ,OAAO,EACP,OAAO,EAGP,GAAG,EACN,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC/B;AAGD,MAAM,MAAM,uBAAuB,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,CAAC,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;AAG3F,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,CAAC;AAGxC,MAAM,WAAW,sBAAsB;IACnC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;CACvE;AAGD,MAAM,WAAW,yBAAyB;IACtC,cAAc,EAAE,cAAc,CAAC;CAClC;AAGD,OAAO,QAAQ,sBAAsB,CAAC;IAClC,UAAU,UAAW,SAAQ,yBAAyB;KAAI;IAC1D,UAAU,oBAAoB,CAAC,KAAK,SAAS,OAAO,CAChD,SAAQ,sBAAsB;KAAI;IACtC,UAAU,KAAK,CAAC,KAAK,SAAS,OAAO,CAAE,SAAQ,uBAAuB,CAAC,KAAK,CAAC;KAAI;CACpF;AAGD,MAAM,WAAW,uBAAuB,CAAC,KAAK,SAAS,OAAO;IAE1D,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IAC9D,qBAAqB,EAAE,MAAM,IAAI,CAAC;IAClC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,IAAI,CAAC;IAGxB,oBAAoB,EAAE,MAAM,OAAO,CAAC;IACpC,qBAAqB,EAAE,MAAM,OAAO,CAAC;IACrC,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAG7C,iBAAiB,EAAE,MAAM,cAAc,CAAC;IACxC,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,eAAe,EAAE,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;IACpC,iBAAiB,EAAE,MAAM,MAAM,EAAE,CAAC;IAGlC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C;AAGD,eAAO,MAAM,sBAAsB,EAAE,YAAY,CAAC,GAAG,CAoOpD,CAAC"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Selection Feature for TanStack Table
|
|
3
|
+
*
|
|
4
|
+
* This feature adds custom selection capabilities to TanStack Table
|
|
5
|
+
* following the official custom features pattern (same as CustomColumnFilterFeature)
|
|
6
|
+
*/
|
|
7
|
+
import { functionalUpdate, makeStateUpdater, } from '@tanstack/react-table';
|
|
8
|
+
// The custom selection feature implementation (same pattern as CustomColumnFilterFeature)
|
|
9
|
+
export const CustomSelectionFeature = {
|
|
10
|
+
// Define the feature's initial state
|
|
11
|
+
getInitialState: (state) => {
|
|
12
|
+
return {
|
|
13
|
+
selectionState: {
|
|
14
|
+
ids: [],
|
|
15
|
+
type: 'include',
|
|
16
|
+
selectMode: 'page',
|
|
17
|
+
},
|
|
18
|
+
...state,
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
// Define the feature's default options
|
|
22
|
+
getDefaultOptions: (table) => {
|
|
23
|
+
return {
|
|
24
|
+
enableCustomSelection: true,
|
|
25
|
+
selectMode: 'page',
|
|
26
|
+
onSelectionStateChange: makeStateUpdater('selectionState', table),
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
// Define the feature's table instance methods
|
|
30
|
+
createTable: (table) => {
|
|
31
|
+
table.setSelectionState = (updater) => {
|
|
32
|
+
const safeUpdater = (old) => {
|
|
33
|
+
const newState = functionalUpdate(updater, old);
|
|
34
|
+
return newState;
|
|
35
|
+
};
|
|
36
|
+
return table.options.onSelectionStateChange?.(safeUpdater);
|
|
37
|
+
};
|
|
38
|
+
// === BASIC SELECTION METHODS ===
|
|
39
|
+
table.selectRow = (rowId) => {
|
|
40
|
+
if (!table.canSelectRow(rowId))
|
|
41
|
+
return;
|
|
42
|
+
table.setSelectionState((old) => {
|
|
43
|
+
if (old.type === 'exclude') {
|
|
44
|
+
// In exclude mode, selecting means removing from exclude list
|
|
45
|
+
return {
|
|
46
|
+
...old,
|
|
47
|
+
ids: old.ids.filter(id => id !== rowId),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// In include mode, selecting means adding to include list
|
|
52
|
+
const newIds = old.ids.includes(rowId) ? old.ids : [...old.ids, rowId];
|
|
53
|
+
return {
|
|
54
|
+
...old,
|
|
55
|
+
ids: newIds,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
table.deselectRow = (rowId) => {
|
|
61
|
+
table.setSelectionState((old) => {
|
|
62
|
+
if (old.type === 'exclude') {
|
|
63
|
+
// In exclude mode, deselecting means adding to exclude list
|
|
64
|
+
const newIds = old.ids.includes(rowId) ? old.ids : [...old.ids, rowId];
|
|
65
|
+
return {
|
|
66
|
+
...old,
|
|
67
|
+
ids: newIds,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// In include mode, deselecting means removing from include list
|
|
72
|
+
return {
|
|
73
|
+
...old,
|
|
74
|
+
ids: old.ids.filter(id => id !== rowId),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
table.toggleRowSelected = (rowId) => {
|
|
80
|
+
if (table.getIsRowSelected(rowId)) {
|
|
81
|
+
table.deselectRow(rowId);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
table.selectRow(rowId);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
table.selectAll = () => {
|
|
88
|
+
const selectMode = table.options.selectMode || 'page';
|
|
89
|
+
if (selectMode === 'all') {
|
|
90
|
+
// In 'all' mode, use exclude type with empty list (select all)
|
|
91
|
+
table.setSelectionState((old) => ({
|
|
92
|
+
...old,
|
|
93
|
+
ids: [],
|
|
94
|
+
type: 'exclude',
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// In 'page' mode, select current page rows
|
|
99
|
+
const currentPageRows = table.getPaginationRowModel().rows;
|
|
100
|
+
const selectableRowIds = currentPageRows
|
|
101
|
+
.filter(row => table.canSelectRow(row.id))
|
|
102
|
+
.map(row => row.id);
|
|
103
|
+
table.setSelectionState((old) => ({
|
|
104
|
+
...old,
|
|
105
|
+
ids: selectableRowIds,
|
|
106
|
+
type: 'include',
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
table.deselectAll = () => {
|
|
111
|
+
table.setSelectionState((old) => ({
|
|
112
|
+
...old,
|
|
113
|
+
ids: [],
|
|
114
|
+
type: 'include',
|
|
115
|
+
}));
|
|
116
|
+
};
|
|
117
|
+
table.toggleAllRowsSelected = () => {
|
|
118
|
+
if (table.getIsAllRowsSelected()) {
|
|
119
|
+
table.deselectAll();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
table.selectAll();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
// === STATE CHECKERS ===
|
|
126
|
+
table.getIsRowSelected = (rowId) => {
|
|
127
|
+
const state = table.getSelectionState();
|
|
128
|
+
if (state.type === 'exclude') {
|
|
129
|
+
// In exclude mode, selected if NOT in exclude list
|
|
130
|
+
return !state.ids.includes(rowId);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// In include mode, selected if in include list
|
|
134
|
+
return state.ids.includes(rowId);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
table.getIsAllRowsSelected = () => {
|
|
138
|
+
const state = table.getSelectionState();
|
|
139
|
+
const selectMode = table.options.selectMode || 'page';
|
|
140
|
+
if (selectMode === 'all') {
|
|
141
|
+
if (state.type === 'exclude') {
|
|
142
|
+
return state.ids.length === 0;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const totalCount = table.getRowCount();
|
|
146
|
+
return state.ids.length === totalCount;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Page mode - check if all selectable rows on current page are selected
|
|
151
|
+
const currentPageRows = table.getPaginationRowModel().rows;
|
|
152
|
+
const selectableRows = currentPageRows.filter(row => table.canSelectRow(row.id));
|
|
153
|
+
if (selectableRows.length === 0)
|
|
154
|
+
return false;
|
|
155
|
+
return selectableRows.every(row => table.getIsRowSelected(row.id));
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
table.getIsSomeRowsSelected = () => {
|
|
159
|
+
const state = table.getSelectionState();
|
|
160
|
+
const selectMode = table.options.selectMode || 'page';
|
|
161
|
+
if (selectMode === 'all' && state.type === 'exclude') {
|
|
162
|
+
// In exclude mode, we have some selected if not all are excluded
|
|
163
|
+
const totalCount = table.getRowCount();
|
|
164
|
+
return state.ids.length < totalCount;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// In include mode, we have some selected if list has items
|
|
168
|
+
return state.ids.length > 0;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
// === GETTERS ===
|
|
172
|
+
table.getSelectionState = () => {
|
|
173
|
+
return table.getState().selectionState || {
|
|
174
|
+
ids: [],
|
|
175
|
+
type: 'include',
|
|
176
|
+
selectMode: 'page',
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
table.getSelectedCount = () => {
|
|
180
|
+
const state = table.getSelectionState();
|
|
181
|
+
const selectMode = table.options.selectMode || 'page';
|
|
182
|
+
if (selectMode === 'all' && state.type === 'exclude') {
|
|
183
|
+
const totalCount = table.getRowCount();
|
|
184
|
+
return totalCount - state.ids.length;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
return state.ids.length;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
table.getSelectedRowIds = () => {
|
|
191
|
+
const state = table.getSelectionState();
|
|
192
|
+
if (state.type === 'exclude') {
|
|
193
|
+
// In exclude mode, return all row IDs minus excluded ones
|
|
194
|
+
// Note: This is simplified - for full functionality, you'd need all possible row IDs
|
|
195
|
+
console.warn('getSelectedRowIds() in exclude mode returns exclude list. Use getSelectionState() instead.');
|
|
196
|
+
return state.ids;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
return state.ids;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
table.getSelectedRows = () => {
|
|
203
|
+
const state = table.getSelectionState();
|
|
204
|
+
const allRows = table.getRowModel().rows;
|
|
205
|
+
if (state.type === 'exclude') {
|
|
206
|
+
// Return all rows except excluded ones
|
|
207
|
+
return allRows.filter(row => !state.ids.includes(row.id));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
// Return only included rows
|
|
211
|
+
return allRows.filter(row => state.ids.includes(row.id));
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
// === HELPER METHODS ===
|
|
215
|
+
table.canSelectRow = (rowId) => {
|
|
216
|
+
if (!table.options.isRowSelectable)
|
|
217
|
+
return true;
|
|
218
|
+
const row = table.getRowModel().rows.find(r => r.id === rowId);
|
|
219
|
+
if (!row)
|
|
220
|
+
return false;
|
|
221
|
+
return table.options.isRowSelectable({ row: row.original, id: rowId });
|
|
222
|
+
};
|
|
223
|
+
},
|
|
224
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/features/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACH,yBAAyB,EACzB,0BAA0B,EAC1B,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,0BAA0B,GAClC,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACH,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,GAC/B,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
export { CustomColumnFilterFeature, matchesCustomColumnFilters, } from './custom-column-filter.feature';
|
|
8
|
+
// Export custom selection feature
|
|
9
|
+
export { CustomSelectionFeature, } from './custom-selection.feature';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-data-table-api.d.ts","sourceRoot":"","sources":["../../../src/lib/hooks/use-data-table-api.ts"],"names":[],"mappings":"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"}
|