@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
package/src/DataGrid.tsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useMemo, type CSSProperties, type ReactNode } from 'react';
|
|
2
|
-
import { Platform, View, type ViewStyle } from 'react-native';
|
|
1
|
+
import { useMemo, useRef, useState, type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import { I18nManager, Platform, View, type ViewStyle } from 'react-native';
|
|
3
3
|
import {
|
|
4
4
|
Checkbox,
|
|
5
|
+
Icon,
|
|
5
6
|
IconButton,
|
|
6
7
|
Input,
|
|
7
8
|
Skeleton,
|
|
@@ -9,10 +10,35 @@ import {
|
|
|
9
10
|
TablePagination,
|
|
10
11
|
Text,
|
|
11
12
|
} from '@cdx-ui/components';
|
|
12
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
Check,
|
|
15
|
+
Close,
|
|
16
|
+
FilterList,
|
|
17
|
+
KeyboardArrowDown,
|
|
18
|
+
KeyboardArrowUp,
|
|
19
|
+
Search,
|
|
20
|
+
} from '@cdx-ui/icons';
|
|
13
21
|
import { dataAttributes, mergeDataAttributes } from '@cdx-ui/primitives';
|
|
14
22
|
import { cn, useControllableState } from '@cdx-ui/utils';
|
|
15
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
normalizePinnedColumns,
|
|
25
|
+
resolveColumnAlign,
|
|
26
|
+
resolveColumnHeaderAlign,
|
|
27
|
+
} from './adapter/columns';
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_PINNED_COLUMN_WIDTH,
|
|
30
|
+
SELECT_COLUMN_WIDTH,
|
|
31
|
+
buildPinnedOffsets,
|
|
32
|
+
isColumnPinningActive,
|
|
33
|
+
pinnedCellStyle,
|
|
34
|
+
pinnedEdgeGuidesImage,
|
|
35
|
+
pinnedFieldSet,
|
|
36
|
+
warnPinnedFlexColumns,
|
|
37
|
+
type ColumnPinEdge,
|
|
38
|
+
type PinnedOffsetMaps,
|
|
39
|
+
} from './adapter/pinning';
|
|
40
|
+
import { getTypedCellText } from './adapter/columnTypes';
|
|
41
|
+
import { DataGridFilterPanel } from './DataGridFilterPanel';
|
|
16
42
|
import {
|
|
17
43
|
dataGridBodyVariants,
|
|
18
44
|
dataGridCellVariants,
|
|
@@ -27,6 +53,8 @@ import {
|
|
|
27
53
|
dataGridOverlayVariants,
|
|
28
54
|
dataGridPaginationRangeVariants,
|
|
29
55
|
dataGridPaginationVariants,
|
|
56
|
+
dataGridPinnedEdgeVariants,
|
|
57
|
+
dataGridPinnedSurfaceVariants,
|
|
30
58
|
dataGridRootVariants,
|
|
31
59
|
dataGridSelectCellVariants,
|
|
32
60
|
dataGridSelectHeadVariants,
|
|
@@ -49,8 +77,14 @@ import { resolveRowId, useDataGridTable } from './useDataGridTable';
|
|
|
49
77
|
|
|
50
78
|
const MAX_SKELETON_ROW_COUNT = 10;
|
|
51
79
|
const DEFAULT_WEB_COLUMN_WIDTH = 100;
|
|
52
|
-
const SELECT_COLUMN_WIDTH = 56;
|
|
53
80
|
const WEB_TABLE_CONTAINER_STYLE: ViewStyle = { overflow: 'visible' };
|
|
81
|
+
// Same token as the pinned cells' `border-stroke-secondary` divider.
|
|
82
|
+
const PINNED_EDGE_COLOR = 'var(--color-stroke-secondary)';
|
|
83
|
+
|
|
84
|
+
interface PinRenderMeta {
|
|
85
|
+
edge: ColumnPinEdge | false;
|
|
86
|
+
isEdgeDivider: boolean;
|
|
87
|
+
}
|
|
54
88
|
|
|
55
89
|
function cellStyle<R extends GridValidRowModel>(
|
|
56
90
|
col: GridColDef<R> | undefined,
|
|
@@ -78,16 +112,32 @@ function cellStyle<R extends GridValidRowModel>(
|
|
|
78
112
|
return Object.keys(style).length > 0 ? style : undefined;
|
|
79
113
|
}
|
|
80
114
|
|
|
115
|
+
function effectiveFlex<R extends GridValidRowModel>(
|
|
116
|
+
column: GridColDef<R>,
|
|
117
|
+
pinned: ReadonlySet<string>,
|
|
118
|
+
): number {
|
|
119
|
+
if (pinned.has(column.field)) {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
return Math.max(column.flex ?? 0, 0);
|
|
123
|
+
}
|
|
124
|
+
|
|
81
125
|
function webColumnWidths<R extends GridValidRowModel>(
|
|
82
126
|
columns: readonly GridColDef<R>[],
|
|
83
127
|
checkboxSelection: boolean,
|
|
128
|
+
pinned: ReadonlySet<string> = new Set(),
|
|
84
129
|
): Map<string, CSSProperties['width']> {
|
|
85
|
-
const totalFlex = columns.reduce((total, column) => total +
|
|
130
|
+
const totalFlex = columns.reduce((total, column) => total + effectiveFlex(column, pinned), 0);
|
|
86
131
|
const widths = new Map<string, CSSProperties['width']>();
|
|
87
132
|
|
|
88
133
|
if (totalFlex === 0) {
|
|
89
134
|
for (const column of columns) {
|
|
90
|
-
if (column.
|
|
135
|
+
if (pinned.has(column.field)) {
|
|
136
|
+
widths.set(
|
|
137
|
+
column.field,
|
|
138
|
+
Math.max(column.width ?? DEFAULT_PINNED_COLUMN_WIDTH, column.minWidth ?? 0),
|
|
139
|
+
);
|
|
140
|
+
} else if (column.width !== undefined) {
|
|
91
141
|
widths.set(column.field, column.width);
|
|
92
142
|
}
|
|
93
143
|
}
|
|
@@ -97,15 +147,19 @@ function webColumnWidths<R extends GridValidRowModel>(
|
|
|
97
147
|
const fixedWidth =
|
|
98
148
|
(checkboxSelection ? SELECT_COLUMN_WIDTH : 0) +
|
|
99
149
|
columns.reduce((total, column) => {
|
|
100
|
-
if ((column
|
|
150
|
+
if (effectiveFlex(column, pinned) > 0) {
|
|
101
151
|
return total;
|
|
102
152
|
}
|
|
103
|
-
|
|
153
|
+
const fallback = pinned.has(column.field)
|
|
154
|
+
? DEFAULT_PINNED_COLUMN_WIDTH
|
|
155
|
+
: DEFAULT_WEB_COLUMN_WIDTH;
|
|
156
|
+
return total + Math.max(column.width ?? fallback, column.minWidth ?? 0);
|
|
104
157
|
}, 0);
|
|
105
158
|
|
|
106
159
|
for (const column of columns) {
|
|
107
|
-
|
|
108
|
-
|
|
160
|
+
const flex = effectiveFlex(column, pinned);
|
|
161
|
+
if (flex > 0) {
|
|
162
|
+
const share = flex / totalFlex;
|
|
109
163
|
const percentage = share * 100;
|
|
110
164
|
const fixedShare = fixedWidth * share;
|
|
111
165
|
const width = `calc(${percentage.toString()}% - ${fixedShare.toString()}px)`;
|
|
@@ -114,16 +168,65 @@ function webColumnWidths<R extends GridValidRowModel>(
|
|
|
114
168
|
column.minWidth === undefined ? width : `max(${column.minWidth.toString()}px, ${width})`,
|
|
115
169
|
);
|
|
116
170
|
} else {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
171
|
+
const fallback = pinned.has(column.field)
|
|
172
|
+
? DEFAULT_PINNED_COLUMN_WIDTH
|
|
173
|
+
: DEFAULT_WEB_COLUMN_WIDTH;
|
|
174
|
+
widths.set(column.field, Math.max(column.width ?? fallback, column.minWidth ?? 0));
|
|
121
175
|
}
|
|
122
176
|
}
|
|
123
177
|
|
|
124
178
|
return widths;
|
|
125
179
|
}
|
|
126
180
|
|
|
181
|
+
function pinMetaFor(columnId: string, pinning: { start: string[]; end: string[] }): PinRenderMeta {
|
|
182
|
+
const startIndex = pinning.start.indexOf(columnId);
|
|
183
|
+
if (startIndex >= 0) {
|
|
184
|
+
return {
|
|
185
|
+
edge: 'start',
|
|
186
|
+
isEdgeDivider: startIndex === pinning.start.length - 1,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const endIndex = pinning.end.indexOf(columnId);
|
|
190
|
+
if (endIndex >= 0) {
|
|
191
|
+
return {
|
|
192
|
+
edge: 'end',
|
|
193
|
+
isEdgeDivider: endIndex === 0,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { edge: false, isEdgeDivider: false };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function mergePinnedStyle(
|
|
200
|
+
base: ViewStyle | undefined,
|
|
201
|
+
pin: PinRenderMeta,
|
|
202
|
+
offsets: PinnedOffsetMaps | null,
|
|
203
|
+
columnId: string,
|
|
204
|
+
isHeader: boolean,
|
|
205
|
+
): ViewStyle | undefined {
|
|
206
|
+
if (!pin.edge || offsets == null) {
|
|
207
|
+
return base;
|
|
208
|
+
}
|
|
209
|
+
const offset =
|
|
210
|
+
pin.edge === 'start' ? (offsets.start.get(columnId) ?? 0) : (offsets.end.get(columnId) ?? 0);
|
|
211
|
+
return { ...base, ...pinnedCellStyle(pin.edge, offset, isHeader) };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function pinnedClassName(pin: PinRenderMeta, isHeader: boolean): string | undefined {
|
|
215
|
+
if (!pin.edge) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
return cn(
|
|
219
|
+
dataGridPinnedSurfaceVariants({ header: isHeader }),
|
|
220
|
+
dataGridPinnedEdgeVariants({ edge: pin.isEdgeDivider ? pin.edge : 'none' }),
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function BooleanCell({ value }: { value: boolean }) {
|
|
225
|
+
return (
|
|
226
|
+
<Icon as={value ? Check : Close} accessibilityLabel={value ? 'Yes' : 'No'} className="size-5" />
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
127
230
|
function defaultCellContent(value: unknown): ReactNode {
|
|
128
231
|
if (value == null) {
|
|
129
232
|
return '';
|
|
@@ -148,12 +251,25 @@ function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
|
148
251
|
checkboxSelection,
|
|
149
252
|
rowCount,
|
|
150
253
|
columnWidths,
|
|
254
|
+
orderedColumnIds,
|
|
255
|
+
pinning,
|
|
256
|
+
offsets,
|
|
151
257
|
}: {
|
|
152
258
|
columns: readonly GridColDef<R>[];
|
|
153
259
|
checkboxSelection: boolean;
|
|
154
260
|
rowCount: number;
|
|
155
261
|
columnWidths: ReadonlyMap<string, CSSProperties['width']>;
|
|
262
|
+
orderedColumnIds?: readonly string[];
|
|
263
|
+
pinning: { start: string[]; end: string[] };
|
|
264
|
+
offsets: PinnedOffsetMaps | null;
|
|
156
265
|
}) {
|
|
266
|
+
const colByField = new Map(columns.map((column) => [column.field, column]));
|
|
267
|
+
const ids =
|
|
268
|
+
orderedColumnIds ??
|
|
269
|
+
(checkboxSelection
|
|
270
|
+
? [SELECT_COLUMN_ID, ...columns.map((column) => column.field)]
|
|
271
|
+
: columns.map((column) => column.field));
|
|
272
|
+
|
|
157
273
|
return (
|
|
158
274
|
<>
|
|
159
275
|
{Array.from({ length: rowCount }, (_, rowIndex) => (
|
|
@@ -162,21 +278,41 @@ function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
|
162
278
|
className="pointer-events-none web:hover:bg-surface-primary"
|
|
163
279
|
testID={rowIndex === 0 ? 'data-grid-default-loading-rows' : undefined}
|
|
164
280
|
>
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
281
|
+
{ids.map((columnId) => {
|
|
282
|
+
const pin = pinMetaFor(columnId, pinning);
|
|
283
|
+
if (columnId === SELECT_COLUMN_ID) {
|
|
284
|
+
return (
|
|
285
|
+
<Table.Cell
|
|
286
|
+
key={columnId}
|
|
287
|
+
align="center"
|
|
288
|
+
className={cn(dataGridSelectCellVariants(), pinnedClassName(pin, false))}
|
|
289
|
+
style={mergePinnedStyle(undefined, pin, offsets, columnId, false)}
|
|
290
|
+
>
|
|
291
|
+
<Skeleton className="size-5" />
|
|
292
|
+
</Table.Cell>
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const column = colByField.get(columnId);
|
|
296
|
+
if (!column) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
return (
|
|
300
|
+
<Table.Cell
|
|
301
|
+
key={column.field}
|
|
302
|
+
align={resolveColumnAlign(column)}
|
|
303
|
+
className={cn(dataGridCellVariants(), pinnedClassName(pin, false))}
|
|
304
|
+
style={mergePinnedStyle(
|
|
305
|
+
cellStyle(column, columnWidths.get(column.field)),
|
|
306
|
+
pin,
|
|
307
|
+
offsets,
|
|
308
|
+
column.field,
|
|
309
|
+
false,
|
|
310
|
+
)}
|
|
311
|
+
>
|
|
312
|
+
<Skeleton className="h-4 w-3/5" />
|
|
313
|
+
</Table.Cell>
|
|
314
|
+
);
|
|
315
|
+
})}
|
|
180
316
|
</Table.Row>
|
|
181
317
|
))}
|
|
182
318
|
</>
|
|
@@ -219,6 +355,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
219
355
|
rowCount,
|
|
220
356
|
showToolbar = false,
|
|
221
357
|
headerFilters = false,
|
|
358
|
+
pinnedColumns,
|
|
222
359
|
pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
|
|
223
360
|
noRowsOverlay,
|
|
224
361
|
loadingOverlay,
|
|
@@ -245,6 +382,11 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
245
382
|
defaultProp: props.defaultFilterModel ?? [],
|
|
246
383
|
onChange: props.onFilterModelChange,
|
|
247
384
|
});
|
|
385
|
+
const [filterLogicOperator, setFilterLogicOperator] = useControllableState({
|
|
386
|
+
prop: props.filterLogicOperator,
|
|
387
|
+
defaultProp: props.defaultFilterLogicOperator ?? 'and',
|
|
388
|
+
onChange: props.onFilterLogicOperatorChange,
|
|
389
|
+
});
|
|
248
390
|
const [quickFilter, setQuickFilter] = useControllableState({
|
|
249
391
|
prop: props.quickFilter,
|
|
250
392
|
defaultProp: props.defaultQuickFilter ?? '',
|
|
@@ -259,13 +401,23 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
259
401
|
const resolvedSortModel = sortModel ?? [];
|
|
260
402
|
const resolvedPagination = paginationModel ?? DEFAULT_PAGINATION_MODEL;
|
|
261
403
|
const resolvedFilterModel = filterModel ?? [];
|
|
404
|
+
const resolvedFilterLogicOperator = filterLogicOperator ?? 'and';
|
|
262
405
|
const resolvedQuickFilter = quickFilter ?? '';
|
|
263
406
|
const resolvedSelection = rowSelectionModel ?? EMPTY_SELECTION;
|
|
264
407
|
|
|
408
|
+
const knownFields = useMemo(() => new Set(columns.map((column) => column.field)), [columns]);
|
|
409
|
+
const columnPinning = useMemo(
|
|
410
|
+
() => normalizePinnedColumns(pinnedColumns, knownFields, checkboxSelection),
|
|
411
|
+
[pinnedColumns, knownFields, checkboxSelection],
|
|
412
|
+
);
|
|
413
|
+
const pinningActive = isColumnPinningActive(columnPinning);
|
|
414
|
+
const pinnedFields = useMemo(() => pinnedFieldSet(columnPinning), [columnPinning]);
|
|
415
|
+
|
|
265
416
|
const {
|
|
266
417
|
table,
|
|
267
418
|
setQuickFilter: setQuickFilterWithPageReset,
|
|
268
419
|
setColumnFilter,
|
|
420
|
+
setFilterModel: setFilterModelWithPageReset,
|
|
269
421
|
} = useDataGridTable({
|
|
270
422
|
rows,
|
|
271
423
|
columns,
|
|
@@ -282,10 +434,12 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
282
434
|
onPaginationModelChange: (model) => setPaginationModel(model),
|
|
283
435
|
filterModel: resolvedFilterModel,
|
|
284
436
|
onFilterModelChange: (model) => setFilterModel(model),
|
|
437
|
+
filterLogicOperator: resolvedFilterLogicOperator,
|
|
285
438
|
quickFilter: resolvedQuickFilter,
|
|
286
439
|
onQuickFilterChange: (value) => setQuickFilter(value),
|
|
287
440
|
rowSelectionModel: resolvedSelection,
|
|
288
441
|
onRowSelectionModelChange: (model) => setRowSelectionModel(model),
|
|
442
|
+
columnPinning,
|
|
289
443
|
});
|
|
290
444
|
|
|
291
445
|
const colByField = useMemo(() => {
|
|
@@ -295,11 +449,47 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
295
449
|
}
|
|
296
450
|
return map;
|
|
297
451
|
}, [columns]);
|
|
452
|
+
|
|
453
|
+
warnPinnedFlexColumns(columns, pinnedFields);
|
|
454
|
+
|
|
298
455
|
const columnWidths = useMemo(
|
|
299
|
-
() => webColumnWidths(columns, checkboxSelection),
|
|
300
|
-
[checkboxSelection, columns],
|
|
456
|
+
() => webColumnWidths(columns, checkboxSelection, pinnedFields),
|
|
457
|
+
[checkboxSelection, columns, pinnedFields],
|
|
458
|
+
);
|
|
459
|
+
const hasFlexColumns = useMemo(
|
|
460
|
+
() => columns.some((column) => effectiveFlex(column, pinnedFields) > 0),
|
|
461
|
+
[columns, pinnedFields],
|
|
301
462
|
);
|
|
302
|
-
const
|
|
463
|
+
const pinnedOffsets = useMemo(
|
|
464
|
+
() => (pinningActive ? buildPinnedOffsets(columnPinning, colByField) : null),
|
|
465
|
+
[pinningActive, columnPinning, colByField],
|
|
466
|
+
);
|
|
467
|
+
const orderedLeafColumnIds = useMemo(() => {
|
|
468
|
+
if (!pinningActive) {
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
return [
|
|
472
|
+
...table.getStartVisibleLeafColumns().map((column) => column.id),
|
|
473
|
+
...table.getCenterVisibleLeafColumns().map((column) => column.id),
|
|
474
|
+
...table.getEndVisibleLeafColumns().map((column) => column.id),
|
|
475
|
+
];
|
|
476
|
+
}, [pinningActive, table]);
|
|
477
|
+
const isEmpty = !loading && rows.length === 0;
|
|
478
|
+
const mainScrollStyle = useMemo((): ViewStyle | undefined => {
|
|
479
|
+
if (!pinningActive || pinnedOffsets == null) {
|
|
480
|
+
return undefined;
|
|
481
|
+
}
|
|
482
|
+
const guides =
|
|
483
|
+
Platform.OS === 'web' && !isEmpty
|
|
484
|
+
? pinnedEdgeGuidesImage(pinnedOffsets, PINNED_EDGE_COLOR, I18nManager.isRTL)
|
|
485
|
+
: undefined;
|
|
486
|
+
// Logical scroll-padding keeps focused cells out from under pinned edges (web).
|
|
487
|
+
return {
|
|
488
|
+
scrollPaddingInlineStart: pinnedOffsets.startTotal,
|
|
489
|
+
scrollPaddingInlineEnd: pinnedOffsets.endTotal,
|
|
490
|
+
...(guides ? { backgroundImage: guides, backgroundRepeat: 'no-repeat' } : {}),
|
|
491
|
+
} as ViewStyle;
|
|
492
|
+
}, [pinningActive, pinnedOffsets, isEmpty]);
|
|
303
493
|
|
|
304
494
|
const visibleRows = table.getRowModel().rows;
|
|
305
495
|
const pageCount = Math.max(table.getPageCount(), 1);
|
|
@@ -313,17 +503,29 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
313
503
|
: { checked: false, indeterminate: true };
|
|
314
504
|
|
|
315
505
|
const accessibleName = ariaLabel ?? label;
|
|
316
|
-
const isEmpty = !loading && rows.length === 0;
|
|
317
506
|
const totalCount = paginationMode === 'server' ? (rowCount ?? rows.length) : filterableCount;
|
|
318
507
|
|
|
319
|
-
const filterValueFor = (field: string) =>
|
|
320
|
-
resolvedFilterModel.find((item) => item.field === field)?.value
|
|
508
|
+
const filterValueFor = (field: string) => {
|
|
509
|
+
const value = resolvedFilterModel.find((item) => item.field === field)?.value;
|
|
510
|
+
return typeof value === 'string' ? value : '';
|
|
511
|
+
};
|
|
321
512
|
|
|
322
513
|
const showToolbarBar = showToolbar || toolbarActions != null;
|
|
514
|
+
const filterPanelColumns = useMemo(
|
|
515
|
+
() => columns.filter((column) => column.filterable !== false),
|
|
516
|
+
[columns],
|
|
517
|
+
);
|
|
518
|
+
const [filterPanelOpen, setFilterPanelOpen] = useState(false);
|
|
519
|
+
const filterPanelButtonRef = useRef<View>(null);
|
|
520
|
+
const showFilterPanelButton = showToolbar && filterPanelColumns.length > 0;
|
|
323
521
|
|
|
324
522
|
return (
|
|
325
523
|
<View
|
|
326
|
-
className={cn(
|
|
524
|
+
className={cn(
|
|
525
|
+
dataGridRootVariants({ autoHeight }),
|
|
526
|
+
className,
|
|
527
|
+
filterPanelOpen && 'overflow-visible',
|
|
528
|
+
)}
|
|
327
529
|
style={style}
|
|
328
530
|
{...mergeDataAttributes({}, dataAttributes({ slot: 'data-grid' }))}
|
|
329
531
|
>
|
|
@@ -340,17 +542,46 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
340
542
|
/>
|
|
341
543
|
</Input>
|
|
342
544
|
) : null}
|
|
545
|
+
{showFilterPanelButton ? (
|
|
546
|
+
<IconButton
|
|
547
|
+
ref={filterPanelButtonRef}
|
|
548
|
+
as={FilterList}
|
|
549
|
+
variant="ghost"
|
|
550
|
+
color="neutral"
|
|
551
|
+
size="small"
|
|
552
|
+
accessibilityLabel="Filters"
|
|
553
|
+
accessibilityState={{ expanded: filterPanelOpen }}
|
|
554
|
+
onPress={() => setFilterPanelOpen((open) => !open)}
|
|
555
|
+
/>
|
|
556
|
+
) : null}
|
|
343
557
|
{toolbarActions ? (
|
|
344
558
|
<View className={dataGridToolbarActionsVariants()}>{toolbarActions}</View>
|
|
345
559
|
) : null}
|
|
346
560
|
</View>
|
|
347
561
|
) : null}
|
|
562
|
+
{showFilterPanelButton && filterPanelOpen ? (
|
|
563
|
+
<DataGridFilterPanel
|
|
564
|
+
columns={filterPanelColumns}
|
|
565
|
+
filterModel={resolvedFilterModel}
|
|
566
|
+
onFilterModelChange={setFilterModelWithPageReset}
|
|
567
|
+
logicOperator={resolvedFilterLogicOperator}
|
|
568
|
+
onLogicOperatorChange={(operator) => {
|
|
569
|
+
setFilterLogicOperator(operator);
|
|
570
|
+
if (resolvedPagination.page !== 0) {
|
|
571
|
+
setPaginationModel({ ...resolvedPagination, page: 0 });
|
|
572
|
+
}
|
|
573
|
+
}}
|
|
574
|
+
onClose={() => setFilterPanelOpen(false)}
|
|
575
|
+
triggerRef={filterPanelButtonRef}
|
|
576
|
+
/>
|
|
577
|
+
) : null}
|
|
348
578
|
|
|
349
579
|
<View
|
|
350
580
|
className={dataGridMainVariants({
|
|
351
581
|
autoHeight,
|
|
352
582
|
overlay: isEmpty || (loading && loadingOverlay != null),
|
|
353
583
|
})}
|
|
584
|
+
style={mainScrollStyle}
|
|
354
585
|
>
|
|
355
586
|
<Table
|
|
356
587
|
className={cn(
|
|
@@ -370,28 +601,50 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
370
601
|
{label ? <Table.Caption>{label}</Table.Caption> : null}
|
|
371
602
|
{Platform.OS === 'web' && hasFlexColumns ? (
|
|
372
603
|
<colgroup>
|
|
373
|
-
{
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}}
|
|
381
|
-
|
|
382
|
-
|
|
604
|
+
{(
|
|
605
|
+
orderedLeafColumnIds ??
|
|
606
|
+
(checkboxSelection
|
|
607
|
+
? [SELECT_COLUMN_ID, ...columns.map((column) => column.field)]
|
|
608
|
+
: columns.map((column) => column.field))
|
|
609
|
+
).map((columnId) =>
|
|
610
|
+
columnId === SELECT_COLUMN_ID ? (
|
|
611
|
+
<col key={columnId} style={{ width: SELECT_COLUMN_WIDTH }} />
|
|
612
|
+
) : (
|
|
613
|
+
<col
|
|
614
|
+
key={columnId}
|
|
615
|
+
style={{
|
|
616
|
+
width: columnWidths.get(columnId),
|
|
617
|
+
minWidth: colByField.get(columnId)?.minWidth,
|
|
618
|
+
}}
|
|
619
|
+
/>
|
|
620
|
+
),
|
|
621
|
+
)}
|
|
383
622
|
</colgroup>
|
|
384
623
|
) : null}
|
|
385
624
|
<Table.Header className={dataGridHeaderVariants()}>
|
|
386
|
-
{
|
|
625
|
+
{(pinningActive
|
|
626
|
+
? [
|
|
627
|
+
{
|
|
628
|
+
id: 'pinned',
|
|
629
|
+
headers: [
|
|
630
|
+
...table.getStartLeafHeaders(),
|
|
631
|
+
...table.getCenterLeafHeaders(),
|
|
632
|
+
...table.getEndLeafHeaders(),
|
|
633
|
+
],
|
|
634
|
+
},
|
|
635
|
+
]
|
|
636
|
+
: table.getHeaderGroups()
|
|
637
|
+
).map((headerGroup) => (
|
|
387
638
|
<Table.Row key={headerGroup.id}>
|
|
388
639
|
{headerGroup.headers.map((header) => {
|
|
640
|
+
const pin = pinMetaFor(header.column.id, columnPinning);
|
|
389
641
|
if (header.id === SELECT_COLUMN_ID) {
|
|
390
642
|
return (
|
|
391
643
|
<Table.Head
|
|
392
644
|
key={header.id}
|
|
393
645
|
align="center"
|
|
394
|
-
className={dataGridSelectHeadVariants()}
|
|
646
|
+
className={cn(dataGridSelectHeadVariants(), pinnedClassName(pin, true))}
|
|
647
|
+
style={mergePinnedStyle(undefined, pin, pinnedOffsets, header.id, true)}
|
|
395
648
|
>
|
|
396
649
|
<Checkbox
|
|
397
650
|
value="select-all"
|
|
@@ -408,7 +661,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
408
661
|
const col = colByField.get(header.column.id);
|
|
409
662
|
const sorted = header.column.getIsSorted();
|
|
410
663
|
const canSort = !disableColumnSorting && header.column.getCanSort();
|
|
411
|
-
const headerAlign =
|
|
664
|
+
const headerAlign = resolveColumnHeaderAlign(col);
|
|
412
665
|
const headerName = col?.headerName ?? header.column.id;
|
|
413
666
|
const SortIcon = sorted === 'asc' ? KeyboardArrowUp : KeyboardArrowDown;
|
|
414
667
|
const sortButton = canSort ? (
|
|
@@ -433,8 +686,14 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
433
686
|
<Table.Head
|
|
434
687
|
key={header.id}
|
|
435
688
|
align={headerAlign}
|
|
436
|
-
className={dataGridHeadVariants()}
|
|
437
|
-
style={
|
|
689
|
+
className={cn(dataGridHeadVariants(), pinnedClassName(pin, true))}
|
|
690
|
+
style={mergePinnedStyle(
|
|
691
|
+
cellStyle(col, columnWidths.get(header.column.id)),
|
|
692
|
+
pin,
|
|
693
|
+
pinnedOffsets,
|
|
694
|
+
header.column.id,
|
|
695
|
+
true,
|
|
696
|
+
)}
|
|
438
697
|
{...(Platform.OS === 'web'
|
|
439
698
|
? {
|
|
440
699
|
title: col?.renderHeader ? undefined : headerName,
|
|
@@ -470,34 +729,54 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
470
729
|
))}
|
|
471
730
|
{headerFilters ? (
|
|
472
731
|
<Table.Row>
|
|
473
|
-
{
|
|
732
|
+
{(pinningActive
|
|
733
|
+
? [
|
|
734
|
+
...table.getStartVisibleLeafColumns(),
|
|
735
|
+
...table.getCenterVisibleLeafColumns(),
|
|
736
|
+
...table.getEndVisibleLeafColumns(),
|
|
737
|
+
]
|
|
738
|
+
: table.getAllLeafColumns()
|
|
739
|
+
).map((column) => {
|
|
740
|
+
const pin = pinMetaFor(column.id, columnPinning);
|
|
474
741
|
if (column.id === SELECT_COLUMN_ID) {
|
|
475
742
|
return (
|
|
476
743
|
<Table.Cell
|
|
477
744
|
key={column.id}
|
|
478
745
|
align="center"
|
|
479
|
-
className={dataGridSelectHeadVariants()}
|
|
746
|
+
className={cn(dataGridSelectHeadVariants(), pinnedClassName(pin, true))}
|
|
747
|
+
style={mergePinnedStyle(undefined, pin, pinnedOffsets, column.id, true)}
|
|
480
748
|
/>
|
|
481
749
|
);
|
|
482
750
|
}
|
|
483
751
|
const col = colByField.get(column.id);
|
|
484
|
-
const headerAlign = col?.headerAlign ?? col?.align;
|
|
485
752
|
if (col?.filterable === false) {
|
|
486
753
|
return (
|
|
487
754
|
<Table.Head
|
|
488
755
|
key={column.id}
|
|
489
|
-
align={
|
|
490
|
-
className={dataGridHeadVariants()}
|
|
491
|
-
style={
|
|
756
|
+
align={resolveColumnHeaderAlign(col)}
|
|
757
|
+
className={cn(dataGridHeadVariants(), pinnedClassName(pin, true))}
|
|
758
|
+
style={mergePinnedStyle(
|
|
759
|
+
cellStyle(col, columnWidths.get(column.id)),
|
|
760
|
+
pin,
|
|
761
|
+
pinnedOffsets,
|
|
762
|
+
column.id,
|
|
763
|
+
true,
|
|
764
|
+
)}
|
|
492
765
|
/>
|
|
493
766
|
);
|
|
494
767
|
}
|
|
495
768
|
return (
|
|
496
769
|
<Table.Head
|
|
497
770
|
key={column.id}
|
|
498
|
-
align={
|
|
499
|
-
className={dataGridHeadVariants()}
|
|
500
|
-
style={
|
|
771
|
+
align={resolveColumnHeaderAlign(col)}
|
|
772
|
+
className={cn(dataGridHeadVariants(), pinnedClassName(pin, true))}
|
|
773
|
+
style={mergePinnedStyle(
|
|
774
|
+
cellStyle(col, columnWidths.get(column.id)),
|
|
775
|
+
pin,
|
|
776
|
+
pinnedOffsets,
|
|
777
|
+
column.id,
|
|
778
|
+
true,
|
|
779
|
+
)}
|
|
501
780
|
>
|
|
502
781
|
<Input size="small" className={dataGridFilterInputVariants()}>
|
|
503
782
|
<Input.Field
|
|
@@ -520,19 +799,40 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
520
799
|
checkboxSelection={checkboxSelection}
|
|
521
800
|
rowCount={Math.min(resolvedPagination.pageSize, MAX_SKELETON_ROW_COUNT)}
|
|
522
801
|
columnWidths={columnWidths}
|
|
802
|
+
orderedColumnIds={orderedLeafColumnIds}
|
|
803
|
+
pinning={columnPinning}
|
|
804
|
+
offsets={pinnedOffsets}
|
|
523
805
|
/>
|
|
524
806
|
) : isEmpty ? null : (
|
|
525
807
|
visibleRows.map((row) => {
|
|
526
808
|
const rowId = resolveRowId(row.original, getRowId);
|
|
809
|
+
const cells = pinningActive
|
|
810
|
+
? [
|
|
811
|
+
...row.getStartVisibleCells(),
|
|
812
|
+
...row.getCenterVisibleCells(),
|
|
813
|
+
...row.getEndVisibleCells(),
|
|
814
|
+
]
|
|
815
|
+
: row.getAllCells();
|
|
527
816
|
return (
|
|
528
817
|
<Table.Row key={row.id}>
|
|
529
|
-
{
|
|
818
|
+
{cells.map((cell) => {
|
|
819
|
+
const pin = pinMetaFor(cell.column.id, columnPinning);
|
|
530
820
|
if (cell.column.id === SELECT_COLUMN_ID) {
|
|
531
821
|
return (
|
|
532
822
|
<Table.Cell
|
|
533
823
|
key={cell.id}
|
|
534
824
|
align="center"
|
|
535
|
-
className={
|
|
825
|
+
className={cn(
|
|
826
|
+
dataGridSelectCellVariants(),
|
|
827
|
+
pinnedClassName(pin, false),
|
|
828
|
+
)}
|
|
829
|
+
style={mergePinnedStyle(
|
|
830
|
+
undefined,
|
|
831
|
+
pin,
|
|
832
|
+
pinnedOffsets,
|
|
833
|
+
cell.column.id,
|
|
834
|
+
false,
|
|
835
|
+
)}
|
|
536
836
|
>
|
|
537
837
|
<Checkbox
|
|
538
838
|
value={String(rowId)}
|
|
@@ -554,19 +854,29 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
554
854
|
row: row.original,
|
|
555
855
|
field: col.field,
|
|
556
856
|
id: rowId,
|
|
857
|
+
col,
|
|
557
858
|
});
|
|
558
859
|
} else if (col?.valueFormatter) {
|
|
559
860
|
content = col.valueFormatter(value, row.original, col);
|
|
861
|
+
} else if (col?.type === 'boolean' && typeof value === 'boolean') {
|
|
862
|
+
content = <BooleanCell value={value} />;
|
|
560
863
|
} else {
|
|
561
|
-
|
|
864
|
+
const typedText = getTypedCellText(col, value, row.original);
|
|
865
|
+
content = typedText !== undefined ? typedText : defaultCellContent(value);
|
|
562
866
|
}
|
|
563
867
|
|
|
564
868
|
return (
|
|
565
869
|
<Table.Cell
|
|
566
870
|
key={cell.id}
|
|
567
|
-
align={
|
|
568
|
-
className={dataGridCellVariants()}
|
|
569
|
-
style={
|
|
871
|
+
align={resolveColumnAlign(col)}
|
|
872
|
+
className={cn(dataGridCellVariants(), pinnedClassName(pin, false))}
|
|
873
|
+
style={mergePinnedStyle(
|
|
874
|
+
cellStyle(col, columnWidths.get(cell.column.id)),
|
|
875
|
+
pin,
|
|
876
|
+
pinnedOffsets,
|
|
877
|
+
cell.column.id,
|
|
878
|
+
false,
|
|
879
|
+
)}
|
|
570
880
|
>
|
|
571
881
|
{content}
|
|
572
882
|
</Table.Cell>
|