@cdx-ui/data-grid 0.3.10 → 0.3.12
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/README.md +3 -1
- package/coverage.svg +1 -1
- package/lib/commonjs/DataGrid.js +232 -62
- package/lib/commonjs/DataGrid.js.map +1 -1
- package/lib/commonjs/DataGridFilterPanel.js +420 -0
- package/lib/commonjs/DataGridFilterPanel.js.map +1 -0
- package/lib/commonjs/adapter/columnTypes.js +256 -0
- package/lib/commonjs/adapter/columnTypes.js.map +1 -0
- package/lib/commonjs/adapter/columns.js +132 -4
- package/lib/commonjs/adapter/columns.js.map +1 -1
- package/lib/commonjs/adapter/filterOperators.js +422 -0
- package/lib/commonjs/adapter/filterOperators.js.map +1 -0
- package/lib/commonjs/adapter/pinning.js +101 -0
- package/lib/commonjs/adapter/pinning.js.map +1 -0
- package/lib/commonjs/index.js +26 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/numberCells.js +280 -0
- package/lib/commonjs/numberCells.js.map +1 -0
- package/lib/commonjs/styles.js +61 -1
- package/lib/commonjs/styles.js.map +1 -1
- package/lib/commonjs/types.js +29 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useDataGridTable.js +98 -13
- package/lib/commonjs/useDataGridTable.js.map +1 -1
- package/lib/module/DataGrid.js +237 -67
- package/lib/module/DataGrid.js.map +1 -1
- package/lib/module/DataGridFilterPanel.js +414 -0
- package/lib/module/DataGridFilterPanel.js.map +1 -0
- package/lib/module/adapter/columnTypes.js +240 -0
- package/lib/module/adapter/columnTypes.js.map +1 -0
- package/lib/module/adapter/columns.js +129 -4
- package/lib/module/adapter/columns.js.map +1 -1
- package/lib/module/adapter/filterOperators.js +406 -0
- package/lib/module/adapter/filterOperators.js.map +1 -0
- package/lib/module/adapter/pinning.js +90 -0
- package/lib/module/adapter/pinning.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/numberCells.js +275 -0
- package/lib/module/numberCells.js.map +1 -0
- package/lib/module/styles.js +60 -0
- package/lib/module/styles.js.map +1 -1
- package/lib/module/types.js +28 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDataGridTable.js +99 -14
- package/lib/module/useDataGridTable.js.map +1 -1
- package/lib/typescript/DataGrid.d.ts.map +1 -1
- package/lib/typescript/DataGridFilterPanel.d.ts +17 -0
- package/lib/typescript/DataGridFilterPanel.d.ts.map +1 -0
- package/lib/typescript/adapter/columnTypes.d.ts +32 -0
- package/lib/typescript/adapter/columnTypes.d.ts.map +1 -0
- package/lib/typescript/adapter/columns.d.ts +14 -5
- package/lib/typescript/adapter/columns.d.ts.map +1 -1
- package/lib/typescript/adapter/filterOperators.d.ts +43 -0
- package/lib/typescript/adapter/filterOperators.d.ts.map +1 -0
- package/lib/typescript/adapter/pinning.d.ts +25 -0
- package/lib/typescript/adapter/pinning.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/numberCells.d.ts +46 -0
- package/lib/typescript/numberCells.d.ts.map +1 -0
- package/lib/typescript/styles.d.ts +21 -0
- package/lib/typescript/styles.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +86 -7
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/useDataGridTable.d.ts +18 -4
- package/lib/typescript/useDataGridTable.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/DataGrid.tsx +377 -67
- package/src/DataGridFilterPanel.tsx +477 -0
- package/src/adapter/columnTypes.ts +327 -0
- package/src/adapter/columns.test.ts +75 -1
- package/src/adapter/columns.ts +178 -3
- package/src/adapter/filterOperators.test.ts +292 -0
- package/src/adapter/filterOperators.ts +524 -0
- package/src/adapter/pinning.ts +125 -0
- package/src/index.ts +15 -0
- package/src/numberCells.tsx +379 -0
- package/src/styles.ts +75 -0
- package/src/types.ts +114 -7
- package/src/useDataGridTable.test.ts +69 -0
- package/src/useDataGridTable.ts +127 -8
|
@@ -2,10 +2,79 @@ import type { RowSelectionState } from '@tanstack/react-table';
|
|
|
2
2
|
import type { GridRowId } from './types';
|
|
3
3
|
import {
|
|
4
4
|
columnFiltersToFilterModel,
|
|
5
|
+
filterModelToColumnFilters,
|
|
5
6
|
rowSelectionToModel,
|
|
6
7
|
selectionModelToRowSelection,
|
|
7
8
|
} from './useDataGridTable';
|
|
8
9
|
|
|
10
|
+
describe('filterModelToColumnFilters', () => {
|
|
11
|
+
it('keeps items without an operator as text filters and drops blank ones', () => {
|
|
12
|
+
expect(
|
|
13
|
+
filterModelToColumnFilters([
|
|
14
|
+
{ field: 'name', value: 'alice' },
|
|
15
|
+
{ field: 'status', value: ' ' },
|
|
16
|
+
{ field: 'age' },
|
|
17
|
+
]),
|
|
18
|
+
).toEqual([{ id: 'name', value: 'alice' }]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('groups several items on one field and round-trips them', () => {
|
|
22
|
+
const model = [
|
|
23
|
+
{ field: 'amount', operator: '>' as const, value: 1 },
|
|
24
|
+
{ field: 'amount', operator: '<' as const, value: 9 },
|
|
25
|
+
];
|
|
26
|
+
const state = filterModelToColumnFilters(model);
|
|
27
|
+
|
|
28
|
+
expect(state).toEqual([
|
|
29
|
+
{
|
|
30
|
+
id: 'amount',
|
|
31
|
+
value: {
|
|
32
|
+
items: [
|
|
33
|
+
{ operator: '>', value: 1 },
|
|
34
|
+
{ operator: '<', value: 9 },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
expect(columnFiltersToFilterModel(state)).toEqual(model);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('carries the operator and typed operand', () => {
|
|
43
|
+
expect(filterModelToColumnFilters([{ field: 'amount', operator: '>', value: 100 }])).toEqual([
|
|
44
|
+
{ id: 'amount', value: { operator: '>', value: 100 } },
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('keeps isEmpty and isNotEmpty without a value', () => {
|
|
49
|
+
expect(
|
|
50
|
+
filterModelToColumnFilters([
|
|
51
|
+
{ field: 'a', operator: 'isEmpty' },
|
|
52
|
+
{ field: 'b', operator: 'isNotEmpty', value: '' },
|
|
53
|
+
]),
|
|
54
|
+
).toEqual([
|
|
55
|
+
{ id: 'a', value: { operator: 'isEmpty', value: undefined } },
|
|
56
|
+
{ id: 'b', value: { operator: 'isNotEmpty', value: '' } },
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('drops value operators whose operand is blank', () => {
|
|
61
|
+
expect(
|
|
62
|
+
filterModelToColumnFilters([
|
|
63
|
+
{ field: 'a', operator: '>' },
|
|
64
|
+
{ field: 'b', operator: 'isAnyOf', value: [] },
|
|
65
|
+
{ field: 'c', operator: 'contains', value: ' ' },
|
|
66
|
+
]),
|
|
67
|
+
).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('columnFiltersToFilterModel with operators', () => {
|
|
72
|
+
it('round-trips operator filters', () => {
|
|
73
|
+
const model = [{ field: 'amount', operator: '<=' as const, value: 5 }];
|
|
74
|
+
expect(columnFiltersToFilterModel(filterModelToColumnFilters(model))).toEqual(model);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
9
78
|
describe('columnFiltersToFilterModel', () => {
|
|
10
79
|
it('keeps string filter values', () => {
|
|
11
80
|
expect(columnFiltersToFilterModel([{ id: 'name', value: 'alice' }])).toEqual([
|
package/src/useDataGridTable.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
columnFilteringFeature,
|
|
4
|
+
columnPinningFeature,
|
|
4
5
|
createFilteredRowModel,
|
|
5
6
|
createPaginatedRowModel,
|
|
6
7
|
createSortedRowModel,
|
|
@@ -14,16 +15,37 @@ import {
|
|
|
14
15
|
tableFeatures,
|
|
15
16
|
useTable,
|
|
16
17
|
type ColumnFiltersState,
|
|
18
|
+
type ColumnPinningState,
|
|
17
19
|
type PaginationState,
|
|
18
20
|
type RowSelectionState,
|
|
19
21
|
type SortingState,
|
|
20
22
|
} from '@tanstack/react-table';
|
|
21
23
|
import { createSelectColumn } from './adapter/selectColumn';
|
|
22
24
|
import { applyUpdater, gridColDefToColumnDef } from './adapter/columns';
|
|
25
|
+
import {
|
|
26
|
+
filterFn_displayText,
|
|
27
|
+
metaUsesDisplayFilter,
|
|
28
|
+
normalizeFilterQuery,
|
|
29
|
+
sortFn_boolean,
|
|
30
|
+
sortFn_datetime,
|
|
31
|
+
sortFn_number,
|
|
32
|
+
sortFn_selectLabel,
|
|
33
|
+
untypedCellIncludesQuery,
|
|
34
|
+
} from './adapter/columnTypes';
|
|
35
|
+
import {
|
|
36
|
+
filterOperatorNeedsValue,
|
|
37
|
+
isActiveFilterItem,
|
|
38
|
+
isBlankFilterValue,
|
|
39
|
+
isFilterValueGroup,
|
|
40
|
+
isOperatorFilterValue,
|
|
41
|
+
rowMatchesFilterItem,
|
|
42
|
+
} from './adapter/filterOperators';
|
|
23
43
|
import type {
|
|
24
44
|
GridColDef,
|
|
25
45
|
GridFeatureMode,
|
|
46
|
+
GridFilterItem,
|
|
26
47
|
GridFilterModel,
|
|
48
|
+
GridLogicOperator,
|
|
27
49
|
GridPaginationModel,
|
|
28
50
|
GridRowId,
|
|
29
51
|
GridRowSelectionModel,
|
|
@@ -33,8 +55,24 @@ import type {
|
|
|
33
55
|
} from './types';
|
|
34
56
|
import { SELECT_COLUMN_ID } from './types';
|
|
35
57
|
|
|
58
|
+
function gridGlobalFilterFn(
|
|
59
|
+
row: Parameters<typeof filterFn_displayText>[0],
|
|
60
|
+
columnId: string,
|
|
61
|
+
filterValue: unknown,
|
|
62
|
+
): boolean {
|
|
63
|
+
if (columnId === SELECT_COLUMN_ID) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const meta = row.getAllCells().find((cell) => cell.column.id === columnId)?.column.columnDef.meta;
|
|
67
|
+
if (metaUsesDisplayFilter(meta)) {
|
|
68
|
+
return filterFn_displayText(row, columnId, filterValue);
|
|
69
|
+
}
|
|
70
|
+
return untypedCellIncludesQuery(row.getValue(columnId), normalizeFilterQuery(filterValue));
|
|
71
|
+
}
|
|
72
|
+
|
|
36
73
|
const features = tableFeatures({
|
|
37
74
|
columnFilteringFeature,
|
|
75
|
+
columnPinningFeature,
|
|
38
76
|
globalFilteringFeature,
|
|
39
77
|
rowSortingFeature,
|
|
40
78
|
rowPaginationFeature,
|
|
@@ -43,7 +81,14 @@ const features = tableFeatures({
|
|
|
43
81
|
sortedRowModel: createSortedRowModel(),
|
|
44
82
|
paginatedRowModel: createPaginatedRowModel(),
|
|
45
83
|
filterFns: { includesString: filterFn_includesString },
|
|
46
|
-
sortFns: {
|
|
84
|
+
sortFns: {
|
|
85
|
+
alphanumeric: sortFn_alphanumeric,
|
|
86
|
+
text: sortFn_text,
|
|
87
|
+
number: sortFn_number,
|
|
88
|
+
datetime: sortFn_datetime,
|
|
89
|
+
boolean: sortFn_boolean,
|
|
90
|
+
selectLabel: sortFn_selectLabel,
|
|
91
|
+
},
|
|
47
92
|
});
|
|
48
93
|
|
|
49
94
|
export function sortModelToSortingState(model: GridSortModel): SortingState {
|
|
@@ -81,16 +126,55 @@ function columnFilterValueToString(value: unknown): string {
|
|
|
81
126
|
return '';
|
|
82
127
|
}
|
|
83
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Items without an `operator` keep today's text filter. Items with one carry the operator and
|
|
131
|
+
* typed operand into the column filter. An item whose operator needs a value but has none is
|
|
132
|
+
* dropped, so `isEmpty` / `isNotEmpty` survive a blank `value` and nothing else does.
|
|
133
|
+
*/
|
|
84
134
|
export function filterModelToColumnFilters(model: GridFilterModel): ColumnFiltersState {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
135
|
+
const valuesByField = new Map<string, unknown[]>();
|
|
136
|
+
for (const item of model) {
|
|
137
|
+
let value: unknown;
|
|
138
|
+
if (item.operator !== undefined) {
|
|
139
|
+
if (filterOperatorNeedsValue(item.operator) && isBlankFilterValue(item.value)) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
value = { operator: item.operator, value: item.value };
|
|
143
|
+
} else {
|
|
144
|
+
const text = columnFilterValueToString(item.value);
|
|
145
|
+
if (text.trim().length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
value = text;
|
|
149
|
+
}
|
|
150
|
+
const values = valuesByField.get(item.field);
|
|
151
|
+
if (values) {
|
|
152
|
+
values.push(value);
|
|
153
|
+
} else {
|
|
154
|
+
valuesByField.set(item.field, [value]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return [...valuesByField].map(([id, values]) => ({
|
|
158
|
+
id,
|
|
159
|
+
value: values.length === 1 ? values[0] : { items: values },
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function columnFilterValueToItem(field: string, value: unknown): GridFilterItem {
|
|
164
|
+
if (isOperatorFilterValue(value)) {
|
|
165
|
+
return { field, operator: value.operator, value: value.value };
|
|
166
|
+
}
|
|
167
|
+
return { field, value: columnFilterValueToString(value) };
|
|
88
168
|
}
|
|
89
169
|
|
|
90
170
|
export function columnFiltersToFilterModel(state: ColumnFiltersState): GridFilterModel {
|
|
91
171
|
return state
|
|
92
172
|
.filter((item) => item.id !== SELECT_COLUMN_ID)
|
|
93
|
-
.
|
|
173
|
+
.flatMap((item) =>
|
|
174
|
+
isFilterValueGroup(item.value)
|
|
175
|
+
? item.value.items.map((value) => columnFilterValueToItem(item.id, value))
|
|
176
|
+
: [columnFilterValueToItem(item.id, item.value)],
|
|
177
|
+
);
|
|
94
178
|
}
|
|
95
179
|
|
|
96
180
|
function selectionIdKey(id: GridRowId): string {
|
|
@@ -185,10 +269,12 @@ interface UseDataGridTableParams<R extends GridValidRowModel> {
|
|
|
185
269
|
onPaginationModelChange: (model: GridPaginationModel) => void;
|
|
186
270
|
filterModel: GridFilterModel;
|
|
187
271
|
onFilterModelChange: (model: GridFilterModel) => void;
|
|
272
|
+
filterLogicOperator: GridLogicOperator;
|
|
188
273
|
quickFilter: string;
|
|
189
274
|
onQuickFilterChange: (value: string) => void;
|
|
190
275
|
rowSelectionModel: GridRowSelectionModel;
|
|
191
276
|
onRowSelectionModelChange: (model: GridRowSelectionModel) => void;
|
|
277
|
+
columnPinning: ColumnPinningState;
|
|
192
278
|
}
|
|
193
279
|
|
|
194
280
|
export function useDataGridTable<R extends GridValidRowModel>({
|
|
@@ -207,12 +293,35 @@ export function useDataGridTable<R extends GridValidRowModel>({
|
|
|
207
293
|
onPaginationModelChange,
|
|
208
294
|
filterModel,
|
|
209
295
|
onFilterModelChange,
|
|
296
|
+
filterLogicOperator,
|
|
210
297
|
quickFilter,
|
|
211
298
|
onQuickFilterChange,
|
|
212
299
|
rowSelectionModel,
|
|
213
300
|
onRowSelectionModelChange,
|
|
301
|
+
columnPinning,
|
|
214
302
|
}: UseDataGridTableParams<R>) {
|
|
215
|
-
const
|
|
303
|
+
const orFilterItems = useMemo(() => {
|
|
304
|
+
if (filterLogicOperator !== 'or' || filterMode === 'server') {
|
|
305
|
+
return undefined;
|
|
306
|
+
}
|
|
307
|
+
const active = filterModel.filter(isActiveFilterItem);
|
|
308
|
+
return active.length > 1 ? active : undefined;
|
|
309
|
+
}, [filterLogicOperator, filterMode, filterModel]);
|
|
310
|
+
|
|
311
|
+
const data = useMemo(() => {
|
|
312
|
+
if (!orFilterItems) {
|
|
313
|
+
return [...rows];
|
|
314
|
+
}
|
|
315
|
+
const filterableColumns = new Map(
|
|
316
|
+
columns.filter((col) => col.filterable !== false).map((col) => [col.field, col]),
|
|
317
|
+
);
|
|
318
|
+
return rows.filter((row) =>
|
|
319
|
+
orFilterItems.some((item) => {
|
|
320
|
+
const col = filterableColumns.get(item.field);
|
|
321
|
+
return col !== undefined && rowMatchesFilterItem(col, row, item);
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
}, [columns, orFilterItems, rows]);
|
|
216
325
|
const allIds = useMemo(() => data.map((row) => resolveRowId(row, getRowId)), [data, getRowId]);
|
|
217
326
|
|
|
218
327
|
const columnDefs = useMemo(() => {
|
|
@@ -228,7 +337,10 @@ export function useDataGridTable<R extends GridValidRowModel>({
|
|
|
228
337
|
|
|
229
338
|
const sortingState = useMemo(() => sortModelToSortingState(sortModel), [sortModel]);
|
|
230
339
|
const paginationState = useMemo(() => paginationModelToState(paginationModel), [paginationModel]);
|
|
231
|
-
const columnFilterState = useMemo(
|
|
340
|
+
const columnFilterState = useMemo(
|
|
341
|
+
() => (orFilterItems ? [] : filterModelToColumnFilters(filterModel)),
|
|
342
|
+
[filterModel, orFilterItems],
|
|
343
|
+
);
|
|
232
344
|
const rowSelectionState = useMemo(
|
|
233
345
|
() => selectionModelToRowSelection(rowSelectionModel, allIds),
|
|
234
346
|
[rowSelectionModel, allIds],
|
|
@@ -261,6 +373,7 @@ export function useDataGridTable<R extends GridValidRowModel>({
|
|
|
261
373
|
getRowId: (row) => selectionIdKey(resolveRowId(row, getRowId)),
|
|
262
374
|
enableMultiSort: false,
|
|
263
375
|
enableMultiRowSelection: true,
|
|
376
|
+
globalFilterFn: gridGlobalFilterFn,
|
|
264
377
|
manualPagination: paginationMode === 'server',
|
|
265
378
|
manualSorting: sortingMode === 'server',
|
|
266
379
|
manualFiltering: filterMode === 'server',
|
|
@@ -272,6 +385,7 @@ export function useDataGridTable<R extends GridValidRowModel>({
|
|
|
272
385
|
columnFilters: columnFilterState,
|
|
273
386
|
globalFilter: quickFilter,
|
|
274
387
|
rowSelection: rowSelectionState,
|
|
388
|
+
columnPinning,
|
|
275
389
|
},
|
|
276
390
|
onSortingChange: (updater) => {
|
|
277
391
|
const next = applyUpdater(updater, sortingState);
|
|
@@ -297,5 +411,10 @@ export function useDataGridTable<R extends GridValidRowModel>({
|
|
|
297
411
|
},
|
|
298
412
|
});
|
|
299
413
|
|
|
300
|
-
|
|
414
|
+
const setFilterModel = (model: GridFilterModel) => {
|
|
415
|
+
onFilterModelChange(model);
|
|
416
|
+
resetPaginationPage();
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
return { table, setQuickFilter, setColumnFilter, setFilterModel };
|
|
301
420
|
}
|