@cdx-ui/data-grid 0.3.12 → 0.3.14
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/coverage.svg +1 -1
- package/lib/commonjs/DataGrid.js +279 -40
- package/lib/commonjs/DataGrid.js.map +1 -1
- package/lib/commonjs/DataGridFilterPanel.js +136 -9
- package/lib/commonjs/DataGridFilterPanel.js.map +1 -1
- package/lib/commonjs/adapter/columns.js +16 -8
- package/lib/commonjs/adapter/columns.js.map +1 -1
- package/lib/commonjs/adapter/pinning.js +5 -1
- package/lib/commonjs/adapter/pinning.js.map +1 -1
- package/lib/commonjs/adapter/selectColumn.js +11 -0
- package/lib/commonjs/adapter/selectColumn.js.map +1 -1
- package/lib/commonjs/adapter/treeData.js +195 -0
- package/lib/commonjs/adapter/treeData.js.map +1 -0
- package/lib/commonjs/styles.js +48 -2
- package/lib/commonjs/styles.js.map +1 -1
- package/lib/commonjs/types.js +11 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useDataGridTable.js +143 -12
- package/lib/commonjs/useDataGridTable.js.map +1 -1
- package/lib/module/DataGrid.js +285 -46
- package/lib/module/DataGrid.js.map +1 -1
- package/lib/module/DataGridFilterPanel.js +139 -13
- package/lib/module/DataGridFilterPanel.js.map +1 -1
- package/lib/module/adapter/columns.js +17 -9
- package/lib/module/adapter/columns.js.map +1 -1
- package/lib/module/adapter/pinning.js +5 -1
- package/lib/module/adapter/pinning.js.map +1 -1
- package/lib/module/adapter/selectColumn.js +11 -1
- package/lib/module/adapter/selectColumn.js.map +1 -1
- package/lib/module/adapter/treeData.js +187 -0
- package/lib/module/adapter/treeData.js.map +1 -0
- package/lib/module/styles.js +47 -1
- package/lib/module/styles.js.map +1 -1
- package/lib/module/types.js +10 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDataGridTable.js +147 -16
- package/lib/module/useDataGridTable.js.map +1 -1
- package/lib/typescript/DataGrid.d.ts.map +1 -1
- package/lib/typescript/DataGridFilterPanel.d.ts +39 -4
- package/lib/typescript/DataGridFilterPanel.d.ts.map +1 -1
- package/lib/typescript/adapter/columns.d.ts +1 -1
- package/lib/typescript/adapter/columns.d.ts.map +1 -1
- package/lib/typescript/adapter/pinning.d.ts +1 -0
- package/lib/typescript/adapter/pinning.d.ts.map +1 -1
- package/lib/typescript/adapter/selectColumn.d.ts +8 -0
- package/lib/typescript/adapter/selectColumn.d.ts.map +1 -1
- package/lib/typescript/adapter/treeData.d.ts +36 -0
- package/lib/typescript/adapter/treeData.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/styles.d.ts +12 -1
- package/lib/typescript/styles.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +68 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/useDataGridTable.d.ts +25 -3
- package/lib/typescript/useDataGridTable.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/DataGrid.tsx +399 -59
- package/src/DataGridFilterPanel.tsx +188 -17
- package/src/adapter/columns.ts +21 -7
- package/src/adapter/pinning.ts +10 -1
- package/src/adapter/selectColumn.ts +12 -1
- package/src/adapter/treeData.ts +228 -0
- package/src/index.ts +4 -0
- package/src/styles.ts +57 -1
- package/src/types.ts +76 -0
- package/src/useDataGridTable.ts +217 -15
package/src/DataGrid.tsx
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type CSSProperties,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { I18nManager, Platform, View, type TextInput, type ViewStyle } from 'react-native';
|
|
3
11
|
import {
|
|
4
12
|
Checkbox,
|
|
5
13
|
Icon,
|
|
@@ -15,6 +23,7 @@ import {
|
|
|
15
23
|
Close,
|
|
16
24
|
FilterList,
|
|
17
25
|
KeyboardArrowDown,
|
|
26
|
+
KeyboardArrowRight,
|
|
18
27
|
KeyboardArrowUp,
|
|
19
28
|
Search,
|
|
20
29
|
} from '@cdx-ui/icons';
|
|
@@ -27,6 +36,7 @@ import {
|
|
|
27
36
|
} from './adapter/columns';
|
|
28
37
|
import {
|
|
29
38
|
DEFAULT_PINNED_COLUMN_WIDTH,
|
|
39
|
+
DETAIL_PANEL_TOGGLE_WIDTH,
|
|
30
40
|
SELECT_COLUMN_WIDTH,
|
|
31
41
|
buildPinnedOffsets,
|
|
32
42
|
isColumnPinningActive,
|
|
@@ -38,10 +48,13 @@ import {
|
|
|
38
48
|
type PinnedOffsetMaps,
|
|
39
49
|
} from './adapter/pinning';
|
|
40
50
|
import { getTypedCellText } from './adapter/columnTypes';
|
|
41
|
-
import { DataGridFilterPanel } from './DataGridFilterPanel';
|
|
51
|
+
import { DataGridFilterPanel, useDataGridFilterPanel } from './DataGridFilterPanel';
|
|
42
52
|
import {
|
|
43
53
|
dataGridBodyVariants,
|
|
54
|
+
dataGridCellTextVariants,
|
|
44
55
|
dataGridCellVariants,
|
|
56
|
+
dataGridDetailPanelCellVariants,
|
|
57
|
+
dataGridDetailToggleCellVariants,
|
|
45
58
|
dataGridEmptyOverlayVariants,
|
|
46
59
|
dataGridFilterInputVariants,
|
|
47
60
|
dataGridFooterVariants,
|
|
@@ -63,19 +76,29 @@ import {
|
|
|
63
76
|
dataGridToolbarActionsVariants,
|
|
64
77
|
dataGridToolbarSearchVariants,
|
|
65
78
|
dataGridToolbarVariants,
|
|
79
|
+
dataGridTreeCellInnerVariants,
|
|
80
|
+
dataGridTreeContentVariants,
|
|
81
|
+
dataGridTreeToggleSlotVariants,
|
|
66
82
|
} from './styles';
|
|
67
83
|
import {
|
|
68
84
|
DEFAULT_PAGE_SIZE_OPTIONS,
|
|
69
85
|
DEFAULT_PAGINATION_MODEL,
|
|
86
|
+
DETAIL_PANEL_TOGGLE_ID,
|
|
87
|
+
EMPTY_ROW_ID_SET,
|
|
70
88
|
EMPTY_SELECTION,
|
|
71
89
|
SELECT_COLUMN_ID,
|
|
72
90
|
type DataGridProps,
|
|
73
91
|
type GridColDef,
|
|
92
|
+
type GridRowId,
|
|
74
93
|
type GridValidRowModel,
|
|
75
94
|
} from './types';
|
|
76
95
|
import { resolveRowId, useDataGridTable } from './useDataGridTable';
|
|
77
96
|
|
|
78
97
|
const MAX_SKELETON_ROW_COUNT = 10;
|
|
98
|
+
/** Matches the detail panel cell's `px-4`. */
|
|
99
|
+
const DETAIL_PANEL_INSET = 16;
|
|
100
|
+
|
|
101
|
+
const TREE_INDENT = 24;
|
|
79
102
|
const DEFAULT_WEB_COLUMN_WIDTH = 100;
|
|
80
103
|
const WEB_TABLE_CONTAINER_STYLE: ViewStyle = { overflow: 'visible' };
|
|
81
104
|
// Same token as the pinned cells' `border-stroke-secondary` divider.
|
|
@@ -124,7 +147,7 @@ function effectiveFlex<R extends GridValidRowModel>(
|
|
|
124
147
|
|
|
125
148
|
function webColumnWidths<R extends GridValidRowModel>(
|
|
126
149
|
columns: readonly GridColDef<R>[],
|
|
127
|
-
|
|
150
|
+
leadingWidth: number,
|
|
128
151
|
pinned: ReadonlySet<string> = new Set(),
|
|
129
152
|
): Map<string, CSSProperties['width']> {
|
|
130
153
|
const totalFlex = columns.reduce((total, column) => total + effectiveFlex(column, pinned), 0);
|
|
@@ -145,7 +168,7 @@ function webColumnWidths<R extends GridValidRowModel>(
|
|
|
145
168
|
}
|
|
146
169
|
|
|
147
170
|
const fixedWidth =
|
|
148
|
-
|
|
171
|
+
leadingWidth +
|
|
149
172
|
columns.reduce((total, column) => {
|
|
150
173
|
if (effectiveFlex(column, pinned) > 0) {
|
|
151
174
|
return total;
|
|
@@ -248,7 +271,7 @@ function defaultCellContent(value: unknown): ReactNode {
|
|
|
248
271
|
|
|
249
272
|
function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
250
273
|
columns,
|
|
251
|
-
|
|
274
|
+
leadingColumnIds,
|
|
252
275
|
rowCount,
|
|
253
276
|
columnWidths,
|
|
254
277
|
orderedColumnIds,
|
|
@@ -256,7 +279,7 @@ function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
|
256
279
|
offsets,
|
|
257
280
|
}: {
|
|
258
281
|
columns: readonly GridColDef<R>[];
|
|
259
|
-
|
|
282
|
+
leadingColumnIds: readonly string[];
|
|
260
283
|
rowCount: number;
|
|
261
284
|
columnWidths: ReadonlyMap<string, CSSProperties['width']>;
|
|
262
285
|
orderedColumnIds?: readonly string[];
|
|
@@ -264,11 +287,7 @@ function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
|
264
287
|
offsets: PinnedOffsetMaps | null;
|
|
265
288
|
}) {
|
|
266
289
|
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));
|
|
290
|
+
const ids = orderedColumnIds ?? [...leadingColumnIds, ...columns.map((column) => column.field)];
|
|
272
291
|
|
|
273
292
|
return (
|
|
274
293
|
<>
|
|
@@ -292,6 +311,15 @@ function DefaultLoadingRows<R extends GridValidRowModel>({
|
|
|
292
311
|
</Table.Cell>
|
|
293
312
|
);
|
|
294
313
|
}
|
|
314
|
+
if (columnId === DETAIL_PANEL_TOGGLE_ID) {
|
|
315
|
+
return (
|
|
316
|
+
<Table.Cell
|
|
317
|
+
key={columnId}
|
|
318
|
+
className={cn(dataGridDetailToggleCellVariants(), pinnedClassName(pin, false))}
|
|
319
|
+
style={mergePinnedStyle(undefined, pin, offsets, columnId, false)}
|
|
320
|
+
/>
|
|
321
|
+
);
|
|
322
|
+
}
|
|
295
323
|
const column = colByField.get(columnId);
|
|
296
324
|
if (!column) {
|
|
297
325
|
return null;
|
|
@@ -356,6 +384,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
356
384
|
showToolbar = false,
|
|
357
385
|
headerFilters = false,
|
|
358
386
|
pinnedColumns,
|
|
387
|
+
columnVisibilityModel,
|
|
359
388
|
pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
|
|
360
389
|
noRowsOverlay,
|
|
361
390
|
loadingOverlay,
|
|
@@ -365,8 +394,66 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
365
394
|
label,
|
|
366
395
|
'aria-label': ariaLabel,
|
|
367
396
|
'aria-labelledby': ariaLabelledBy,
|
|
397
|
+
treeData = false,
|
|
398
|
+
getTreeDataPath,
|
|
399
|
+
groupingColDef,
|
|
400
|
+
defaultGroupingExpansionDepth,
|
|
401
|
+
isGroupExpandedByDefault,
|
|
402
|
+
rowSelectionPropagation,
|
|
403
|
+
getDetailPanelContent,
|
|
368
404
|
} = props;
|
|
369
405
|
|
|
406
|
+
const treeActive = treeData && getTreeDataPath !== undefined;
|
|
407
|
+
const isColumnVisible = useCallback(
|
|
408
|
+
(column: GridColDef<R>) => columnVisibilityModel?.[column.field] !== false,
|
|
409
|
+
[columnVisibilityModel],
|
|
410
|
+
);
|
|
411
|
+
const treeField = treeActive ? columns.find(isColumnVisible)?.field : undefined;
|
|
412
|
+
const gridColumns = useMemo((): readonly GridColDef<R>[] => {
|
|
413
|
+
if (treeField === undefined || groupingColDef === undefined) {
|
|
414
|
+
return columns;
|
|
415
|
+
}
|
|
416
|
+
return columns.map((column) =>
|
|
417
|
+
column.field === treeField
|
|
418
|
+
? ({ ...column, ...groupingColDef, field: column.field } as GridColDef<R>)
|
|
419
|
+
: column,
|
|
420
|
+
);
|
|
421
|
+
}, [columns, groupingColDef, treeField]);
|
|
422
|
+
const visibleColumns = useMemo(
|
|
423
|
+
() => gridColumns.filter(isColumnVisible),
|
|
424
|
+
[gridColumns, isColumnVisible],
|
|
425
|
+
);
|
|
426
|
+
const selectColumn = checkboxSelection && !treeActive;
|
|
427
|
+
const treeCheckbox = checkboxSelection && treeActive;
|
|
428
|
+
const detailPanel = getDetailPanelContent !== undefined;
|
|
429
|
+
const leadingColumnIds = useMemo(
|
|
430
|
+
() => [
|
|
431
|
+
...(selectColumn ? [SELECT_COLUMN_ID] : []),
|
|
432
|
+
...(detailPanel ? [DETAIL_PANEL_TOGGLE_ID] : []),
|
|
433
|
+
],
|
|
434
|
+
[detailPanel, selectColumn],
|
|
435
|
+
);
|
|
436
|
+
const leadingWidth =
|
|
437
|
+
(selectColumn ? SELECT_COLUMN_WIDTH : 0) + (detailPanel ? DETAIL_PANEL_TOGGLE_WIDTH : 0);
|
|
438
|
+
|
|
439
|
+
const [detailPanelExpandedRowIds, setDetailPanelExpandedRowIds] = useControllableState<
|
|
440
|
+
ReadonlySet<GridRowId>
|
|
441
|
+
>({
|
|
442
|
+
prop: props.detailPanelExpandedRowIds,
|
|
443
|
+
defaultProp: props.defaultDetailPanelExpandedRowIds ?? EMPTY_ROW_ID_SET,
|
|
444
|
+
onChange: (ids) => props.onDetailPanelExpandedRowIdsChange?.(new Set(ids)),
|
|
445
|
+
});
|
|
446
|
+
const resolvedDetailIds = detailPanelExpandedRowIds ?? EMPTY_ROW_ID_SET;
|
|
447
|
+
const toggleDetailPanel = (id: GridRowId) => {
|
|
448
|
+
const next = new Set(resolvedDetailIds);
|
|
449
|
+
if (next.has(id)) {
|
|
450
|
+
next.delete(id);
|
|
451
|
+
} else {
|
|
452
|
+
next.add(id);
|
|
453
|
+
}
|
|
454
|
+
setDetailPanelExpandedRowIds(next);
|
|
455
|
+
};
|
|
456
|
+
|
|
370
457
|
const [sortModel, setSortModel] = useControllableState({
|
|
371
458
|
prop: props.sortModel,
|
|
372
459
|
defaultProp: props.defaultSortModel ?? [],
|
|
@@ -407,8 +494,8 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
407
494
|
|
|
408
495
|
const knownFields = useMemo(() => new Set(columns.map((column) => column.field)), [columns]);
|
|
409
496
|
const columnPinning = useMemo(
|
|
410
|
-
() => normalizePinnedColumns(pinnedColumns, knownFields,
|
|
411
|
-
[pinnedColumns, knownFields,
|
|
497
|
+
() => normalizePinnedColumns(pinnedColumns, knownFields, selectColumn, detailPanel),
|
|
498
|
+
[pinnedColumns, knownFields, selectColumn, detailPanel],
|
|
412
499
|
);
|
|
413
500
|
const pinningActive = isColumnPinningActive(columnPinning);
|
|
414
501
|
const pinnedFields = useMemo(() => pinnedFieldSet(columnPinning), [columnPinning]);
|
|
@@ -418,10 +505,17 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
418
505
|
setQuickFilter: setQuickFilterWithPageReset,
|
|
419
506
|
setColumnFilter,
|
|
420
507
|
setFilterModel: setFilterModelWithPageReset,
|
|
508
|
+
tree,
|
|
421
509
|
} = useDataGridTable({
|
|
422
510
|
rows,
|
|
423
|
-
columns,
|
|
511
|
+
columns: gridColumns,
|
|
424
512
|
getRowId,
|
|
513
|
+
detailPanel,
|
|
514
|
+
treeData: treeActive,
|
|
515
|
+
getTreeDataPath,
|
|
516
|
+
defaultGroupingExpansionDepth,
|
|
517
|
+
isGroupExpandedByDefault,
|
|
518
|
+
rowSelectionPropagation,
|
|
425
519
|
checkboxSelection,
|
|
426
520
|
disableColumnSorting,
|
|
427
521
|
paginationMode,
|
|
@@ -440,25 +534,26 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
440
534
|
rowSelectionModel: resolvedSelection,
|
|
441
535
|
onRowSelectionModelChange: (model) => setRowSelectionModel(model),
|
|
442
536
|
columnPinning,
|
|
537
|
+
columnVisibility: columnVisibilityModel,
|
|
443
538
|
});
|
|
444
539
|
|
|
445
540
|
const colByField = useMemo(() => {
|
|
446
541
|
const map = new Map<string, GridColDef<R>>();
|
|
447
|
-
for (const col of
|
|
542
|
+
for (const col of gridColumns) {
|
|
448
543
|
map.set(col.field, col);
|
|
449
544
|
}
|
|
450
545
|
return map;
|
|
451
|
-
}, [
|
|
546
|
+
}, [gridColumns]);
|
|
452
547
|
|
|
453
|
-
warnPinnedFlexColumns(
|
|
548
|
+
warnPinnedFlexColumns(visibleColumns, pinnedFields);
|
|
454
549
|
|
|
455
550
|
const columnWidths = useMemo(
|
|
456
|
-
() => webColumnWidths(
|
|
457
|
-
[
|
|
551
|
+
() => webColumnWidths(visibleColumns, leadingWidth, pinnedFields),
|
|
552
|
+
[leadingWidth, visibleColumns, pinnedFields],
|
|
458
553
|
);
|
|
459
554
|
const hasFlexColumns = useMemo(
|
|
460
|
-
() =>
|
|
461
|
-
[
|
|
555
|
+
() => visibleColumns.some((column) => effectiveFlex(column, pinnedFields) > 0),
|
|
556
|
+
[visibleColumns, pinnedFields],
|
|
462
557
|
);
|
|
463
558
|
const pinnedOffsets = useMemo(
|
|
464
559
|
() => (pinningActive ? buildPinnedOffsets(columnPinning, colByField) : null),
|
|
@@ -496,11 +591,13 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
496
591
|
const selectedCount = table.getFilteredSelectedRowModel().rows.length;
|
|
497
592
|
const filterableCount = table.getFilteredRowModel().rows.length;
|
|
498
593
|
const headerSelectState =
|
|
499
|
-
|
|
594
|
+
tree?.headerSelection ??
|
|
595
|
+
(selectedCount === 0
|
|
500
596
|
? { checked: false, indeterminate: false }
|
|
501
597
|
: selectedCount === filterableCount
|
|
502
598
|
? { checked: true, indeterminate: false }
|
|
503
|
-
: { checked: false, indeterminate: true };
|
|
599
|
+
: { checked: false, indeterminate: true });
|
|
600
|
+
const leafColumnCount = table.getVisibleLeafColumns().length;
|
|
504
601
|
|
|
505
602
|
const accessibleName = ariaLabel ?? label;
|
|
506
603
|
const totalCount = paginationMode === 'server' ? (rowCount ?? rows.length) : filterableCount;
|
|
@@ -512,19 +609,84 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
512
609
|
|
|
513
610
|
const showToolbarBar = showToolbar || toolbarActions != null;
|
|
514
611
|
const filterPanelColumns = useMemo(
|
|
515
|
-
() =>
|
|
516
|
-
[
|
|
612
|
+
() => gridColumns.filter((column) => column.filterable !== false),
|
|
613
|
+
[gridColumns],
|
|
517
614
|
);
|
|
518
|
-
|
|
615
|
+
// Header filter cells and the column menu open the panel through `filterPanel.openForField`.
|
|
616
|
+
const filterPanel = useDataGridFilterPanel({
|
|
617
|
+
columns: filterPanelColumns,
|
|
618
|
+
filterModel: resolvedFilterModel,
|
|
619
|
+
onFilterModelChange: setFilterModel,
|
|
620
|
+
});
|
|
621
|
+
const filterPanelOpen = filterPanel.opener !== null && filterPanelColumns.length > 0;
|
|
622
|
+
const filterPanelAnchored = filterPanel.opener?.field !== undefined;
|
|
519
623
|
const filterPanelButtonRef = useRef<View>(null);
|
|
520
624
|
const showFilterPanelButton = showToolbar && filterPanelColumns.length > 0;
|
|
521
625
|
|
|
626
|
+
const mainRef = useRef<View>(null);
|
|
627
|
+
const [viewportWidth, setViewportWidth] = useState<number | undefined>(undefined);
|
|
628
|
+
useEffect(() => {
|
|
629
|
+
const main = mainRef.current as unknown as HTMLElement | null;
|
|
630
|
+
if (
|
|
631
|
+
Platform.OS !== 'web' ||
|
|
632
|
+
!detailPanel ||
|
|
633
|
+
main === null ||
|
|
634
|
+
typeof ResizeObserver === 'undefined'
|
|
635
|
+
) {
|
|
636
|
+
return undefined;
|
|
637
|
+
}
|
|
638
|
+
const observer = new ResizeObserver(() => setViewportWidth(main.clientWidth));
|
|
639
|
+
observer.observe(main);
|
|
640
|
+
return () => observer.disconnect();
|
|
641
|
+
}, [detailPanel]);
|
|
642
|
+
const detailPanelContentStyle = useMemo(
|
|
643
|
+
(): ViewStyle | undefined =>
|
|
644
|
+
viewportWidth === undefined
|
|
645
|
+
? undefined
|
|
646
|
+
: ({
|
|
647
|
+
position: 'sticky',
|
|
648
|
+
insetInlineStart: DETAIL_PANEL_INSET,
|
|
649
|
+
width: viewportWidth - DETAIL_PANEL_INSET * 2,
|
|
650
|
+
} as unknown as ViewStyle),
|
|
651
|
+
[viewportWidth],
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
const rootRef = useRef<View>(null);
|
|
655
|
+
const searchRef = useRef<TextInput>(null);
|
|
656
|
+
const bodyFocusRef = useRef<Element | null>(null);
|
|
657
|
+
useEffect(() => {
|
|
658
|
+
if (Platform.OS !== 'web') {
|
|
659
|
+
return undefined;
|
|
660
|
+
}
|
|
661
|
+
const root = rootRef.current as unknown as HTMLElement | null;
|
|
662
|
+
const onFocusIn = (event: FocusEvent) => {
|
|
663
|
+
const target = event.target instanceof Element ? event.target : null;
|
|
664
|
+
bodyFocusRef.current =
|
|
665
|
+
target !== null && root?.contains(target.closest('tbody')) === true ? target : null;
|
|
666
|
+
};
|
|
667
|
+
document.addEventListener('focusin', onFocusIn);
|
|
668
|
+
return () => document.removeEventListener('focusin', onFocusIn);
|
|
669
|
+
}, []);
|
|
670
|
+
// Only when the focused row control was removed: focus that left the grid stays where it went.
|
|
671
|
+
useEffect(() => {
|
|
672
|
+
const focused = bodyFocusRef.current;
|
|
673
|
+
if (Platform.OS !== 'web' || focused === null || focused.isConnected) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
bodyFocusRef.current = null;
|
|
677
|
+
const active = document.activeElement;
|
|
678
|
+
if (active === null || active === document.body) {
|
|
679
|
+
searchRef.current?.focus();
|
|
680
|
+
}
|
|
681
|
+
}, [visibleRows]);
|
|
682
|
+
|
|
522
683
|
return (
|
|
523
684
|
<View
|
|
685
|
+
ref={rootRef}
|
|
524
686
|
className={cn(
|
|
525
687
|
dataGridRootVariants({ autoHeight }),
|
|
526
688
|
className,
|
|
527
|
-
filterPanelOpen && 'overflow-visible',
|
|
689
|
+
filterPanelOpen && !filterPanelAnchored && 'overflow-visible',
|
|
528
690
|
)}
|
|
529
691
|
style={style}
|
|
530
692
|
{...mergeDataAttributes({}, dataAttributes({ slot: 'data-grid' }))}
|
|
@@ -535,6 +697,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
535
697
|
<Input size="small" className={dataGridToolbarSearchVariants()}>
|
|
536
698
|
<Input.Icon as={Search} />
|
|
537
699
|
<Input.Field
|
|
700
|
+
ref={searchRef}
|
|
538
701
|
value={resolvedQuickFilter}
|
|
539
702
|
onChangeText={setQuickFilterWithPageReset}
|
|
540
703
|
placeholder="Search"
|
|
@@ -551,7 +714,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
551
714
|
size="small"
|
|
552
715
|
accessibilityLabel="Filters"
|
|
553
716
|
accessibilityState={{ expanded: filterPanelOpen }}
|
|
554
|
-
onPress={() =>
|
|
717
|
+
onPress={() => filterPanel.toggle(filterPanelButtonRef)}
|
|
555
718
|
/>
|
|
556
719
|
) : null}
|
|
557
720
|
{toolbarActions ? (
|
|
@@ -559,7 +722,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
559
722
|
) : null}
|
|
560
723
|
</View>
|
|
561
724
|
) : null}
|
|
562
|
-
{
|
|
725
|
+
{filterPanelOpen && filterPanel.opener ? (
|
|
563
726
|
<DataGridFilterPanel
|
|
564
727
|
columns={filterPanelColumns}
|
|
565
728
|
filterModel={resolvedFilterModel}
|
|
@@ -571,12 +734,13 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
571
734
|
setPaginationModel({ ...resolvedPagination, page: 0 });
|
|
572
735
|
}
|
|
573
736
|
}}
|
|
574
|
-
onClose={
|
|
575
|
-
|
|
737
|
+
onClose={filterPanel.close}
|
|
738
|
+
opener={filterPanel.opener}
|
|
576
739
|
/>
|
|
577
740
|
) : null}
|
|
578
741
|
|
|
579
742
|
<View
|
|
743
|
+
ref={mainRef}
|
|
580
744
|
className={dataGridMainVariants({
|
|
581
745
|
autoHeight,
|
|
582
746
|
overlay: isEmpty || (loading && loadingOverlay != null),
|
|
@@ -602,13 +766,15 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
602
766
|
{Platform.OS === 'web' && hasFlexColumns ? (
|
|
603
767
|
<colgroup>
|
|
604
768
|
{(
|
|
605
|
-
orderedLeafColumnIds ??
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
769
|
+
orderedLeafColumnIds ?? [
|
|
770
|
+
...leadingColumnIds,
|
|
771
|
+
...visibleColumns.map((column) => column.field),
|
|
772
|
+
]
|
|
609
773
|
).map((columnId) =>
|
|
610
774
|
columnId === SELECT_COLUMN_ID ? (
|
|
611
775
|
<col key={columnId} style={{ width: SELECT_COLUMN_WIDTH }} />
|
|
776
|
+
) : columnId === DETAIL_PANEL_TOGGLE_ID ? (
|
|
777
|
+
<col key={columnId} style={{ width: DETAIL_PANEL_TOGGLE_WIDTH }} />
|
|
612
778
|
) : (
|
|
613
779
|
<col
|
|
614
780
|
key={columnId}
|
|
@@ -657,8 +823,22 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
657
823
|
</Table.Head>
|
|
658
824
|
);
|
|
659
825
|
}
|
|
826
|
+
if (header.id === DETAIL_PANEL_TOGGLE_ID) {
|
|
827
|
+
return (
|
|
828
|
+
<Table.Cell
|
|
829
|
+
key={header.id}
|
|
830
|
+
className={cn(
|
|
831
|
+
dataGridHeadVariants(),
|
|
832
|
+
dataGridDetailToggleCellVariants(),
|
|
833
|
+
pinnedClassName(pin, true),
|
|
834
|
+
)}
|
|
835
|
+
style={mergePinnedStyle(undefined, pin, pinnedOffsets, header.id, true)}
|
|
836
|
+
/>
|
|
837
|
+
);
|
|
838
|
+
}
|
|
660
839
|
|
|
661
840
|
const col = colByField.get(header.column.id);
|
|
841
|
+
const isTreeColumn = header.column.id === treeField;
|
|
662
842
|
const sorted = header.column.getIsSorted();
|
|
663
843
|
const canSort = !disableColumnSorting && header.column.getCanSort();
|
|
664
844
|
const headerAlign = resolveColumnHeaderAlign(col);
|
|
@@ -681,6 +861,15 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
681
861
|
})}
|
|
682
862
|
/>
|
|
683
863
|
) : null;
|
|
864
|
+
const headerLabel = (
|
|
865
|
+
<>
|
|
866
|
+
{headerAlign === 'end' ? sortButton : null}
|
|
867
|
+
<Text numberOfLines={1} className={dataGridHeaderLabelVariants()}>
|
|
868
|
+
{col?.renderHeader ? col.renderHeader() : headerName}
|
|
869
|
+
</Text>
|
|
870
|
+
{headerAlign === 'end' ? null : sortButton}
|
|
871
|
+
</>
|
|
872
|
+
);
|
|
684
873
|
|
|
685
874
|
return (
|
|
686
875
|
<Table.Head
|
|
@@ -710,18 +899,39 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
710
899
|
}
|
|
711
900
|
: {})}
|
|
712
901
|
>
|
|
713
|
-
|
|
714
|
-
className={
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
902
|
+
{isTreeColumn ? (
|
|
903
|
+
<View className={dataGridTreeCellInnerVariants()}>
|
|
904
|
+
<View className={dataGridTreeToggleSlotVariants()} />
|
|
905
|
+
{treeCheckbox ? (
|
|
906
|
+
<Checkbox
|
|
907
|
+
value="select-all"
|
|
908
|
+
isChecked={headerSelectState.checked}
|
|
909
|
+
isIndeterminate={headerSelectState.indeterminate}
|
|
910
|
+
onChange={() => tree?.toggleAllRowsSelected()}
|
|
911
|
+
accessibilityLabel="Select all"
|
|
912
|
+
aria-label="Select all"
|
|
913
|
+
/>
|
|
914
|
+
) : null}
|
|
915
|
+
<View
|
|
916
|
+
className={cn(
|
|
917
|
+
dataGridHeaderCellInnerVariants({ align: headerAlign }),
|
|
918
|
+
'flex-1',
|
|
919
|
+
)}
|
|
920
|
+
>
|
|
921
|
+
{headerLabel}
|
|
922
|
+
</View>
|
|
923
|
+
</View>
|
|
924
|
+
) : (
|
|
925
|
+
<View
|
|
926
|
+
className={dataGridHeaderCellInnerVariants({
|
|
927
|
+
align: headerAlign,
|
|
928
|
+
fitToColumn:
|
|
929
|
+
Platform.OS === 'web' && columnWidths.has(header.column.id),
|
|
930
|
+
})}
|
|
931
|
+
>
|
|
932
|
+
{headerLabel}
|
|
933
|
+
</View>
|
|
934
|
+
)}
|
|
725
935
|
</Table.Head>
|
|
726
936
|
);
|
|
727
937
|
})}
|
|
@@ -735,15 +945,19 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
735
945
|
...table.getCenterVisibleLeafColumns(),
|
|
736
946
|
...table.getEndVisibleLeafColumns(),
|
|
737
947
|
]
|
|
738
|
-
: table.
|
|
948
|
+
: table.getVisibleLeafColumns()
|
|
739
949
|
).map((column) => {
|
|
740
950
|
const pin = pinMetaFor(column.id, columnPinning);
|
|
741
|
-
if (column.id === SELECT_COLUMN_ID) {
|
|
951
|
+
if (column.id === SELECT_COLUMN_ID || column.id === DETAIL_PANEL_TOGGLE_ID) {
|
|
742
952
|
return (
|
|
743
953
|
<Table.Cell
|
|
744
954
|
key={column.id}
|
|
745
955
|
align="center"
|
|
746
|
-
className={cn(
|
|
956
|
+
className={cn(
|
|
957
|
+
dataGridSelectHeadVariants(),
|
|
958
|
+
column.id === DETAIL_PANEL_TOGGLE_ID && 'w-12 px-2',
|
|
959
|
+
pinnedClassName(pin, true),
|
|
960
|
+
)}
|
|
747
961
|
style={mergePinnedStyle(undefined, pin, pinnedOffsets, column.id, true)}
|
|
748
962
|
/>
|
|
749
963
|
);
|
|
@@ -795,8 +1009,8 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
795
1009
|
<Table.Body className={dataGridBodyVariants()}>
|
|
796
1010
|
{loading && loadingOverlay == null ? (
|
|
797
1011
|
<DefaultLoadingRows
|
|
798
|
-
columns={
|
|
799
|
-
|
|
1012
|
+
columns={visibleColumns}
|
|
1013
|
+
leadingColumnIds={leadingColumnIds}
|
|
800
1014
|
rowCount={Math.min(resolvedPagination.pageSize, MAX_SKELETON_ROW_COUNT)}
|
|
801
1015
|
columnWidths={columnWidths}
|
|
802
1016
|
orderedColumnIds={orderedLeafColumnIds}
|
|
@@ -804,7 +1018,7 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
804
1018
|
offsets={pinnedOffsets}
|
|
805
1019
|
/>
|
|
806
1020
|
) : isEmpty ? null : (
|
|
807
|
-
visibleRows.
|
|
1021
|
+
visibleRows.flatMap((row) => {
|
|
808
1022
|
const rowId = resolveRowId(row.original, getRowId);
|
|
809
1023
|
const cells = pinningActive
|
|
810
1024
|
? [
|
|
@@ -812,11 +1026,53 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
812
1026
|
...row.getCenterVisibleCells(),
|
|
813
1027
|
...row.getEndVisibleCells(),
|
|
814
1028
|
]
|
|
815
|
-
: row.
|
|
816
|
-
|
|
817
|
-
|
|
1029
|
+
: row.getVisibleCells();
|
|
1030
|
+
const detailContent = getDetailPanelContent?.({ id: rowId, row: row.original });
|
|
1031
|
+
const hasDetailPanel = detailContent != null;
|
|
1032
|
+
const detailOpen = hasDetailPanel && resolvedDetailIds.has(rowId);
|
|
1033
|
+
const treeLabel = treeActive
|
|
1034
|
+
? [...getTreeDataPath(row.original)].reverse().join(', ')
|
|
1035
|
+
: '';
|
|
1036
|
+
const dataRow = (
|
|
1037
|
+
<Table.Row
|
|
1038
|
+
key={row.id}
|
|
1039
|
+
className={detailOpen ? 'web:[&>*]:border-b-0' : undefined}
|
|
1040
|
+
>
|
|
818
1041
|
{cells.map((cell) => {
|
|
819
1042
|
const pin = pinMetaFor(cell.column.id, columnPinning);
|
|
1043
|
+
if (cell.column.id === DETAIL_PANEL_TOGGLE_ID) {
|
|
1044
|
+
return (
|
|
1045
|
+
<Table.Cell
|
|
1046
|
+
key={cell.id}
|
|
1047
|
+
align="center"
|
|
1048
|
+
className={cn(
|
|
1049
|
+
dataGridDetailToggleCellVariants(),
|
|
1050
|
+
pinnedClassName(pin, false),
|
|
1051
|
+
)}
|
|
1052
|
+
style={mergePinnedStyle(
|
|
1053
|
+
undefined,
|
|
1054
|
+
pin,
|
|
1055
|
+
pinnedOffsets,
|
|
1056
|
+
cell.column.id,
|
|
1057
|
+
false,
|
|
1058
|
+
)}
|
|
1059
|
+
>
|
|
1060
|
+
{hasDetailPanel ? (
|
|
1061
|
+
<IconButton
|
|
1062
|
+
as={detailOpen ? KeyboardArrowDown : KeyboardArrowRight}
|
|
1063
|
+
variant="ghost"
|
|
1064
|
+
color="neutral"
|
|
1065
|
+
size="small"
|
|
1066
|
+
iconClassName={detailOpen ? undefined : 'rtl:-scale-x-100'}
|
|
1067
|
+
accessibilityLabel={`${detailOpen ? 'Collapse' : 'Expand'} row ${String(rowId)}`}
|
|
1068
|
+
accessibilityState={{ expanded: detailOpen }}
|
|
1069
|
+
aria-expanded={detailOpen}
|
|
1070
|
+
onPress={() => toggleDetailPanel(rowId)}
|
|
1071
|
+
/>
|
|
1072
|
+
) : null}
|
|
1073
|
+
</Table.Cell>
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
820
1076
|
if (cell.column.id === SELECT_COLUMN_ID) {
|
|
821
1077
|
return (
|
|
822
1078
|
<Table.Cell
|
|
@@ -865,10 +1121,63 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
865
1121
|
content = typedText !== undefined ? typedText : defaultCellContent(value);
|
|
866
1122
|
}
|
|
867
1123
|
|
|
1124
|
+
if (tree && cell.column.id === treeField) {
|
|
1125
|
+
const canExpand = row.subRows.length > 0;
|
|
1126
|
+
const expanded = canExpand && tree.isRowExpanded(row.id);
|
|
1127
|
+
const status = tree.selectionStatus(row.id);
|
|
1128
|
+
content = (
|
|
1129
|
+
<View
|
|
1130
|
+
className={dataGridTreeCellInnerVariants()}
|
|
1131
|
+
style={{ paddingStart: row.depth * TREE_INDENT }}
|
|
1132
|
+
>
|
|
1133
|
+
<View className={dataGridTreeToggleSlotVariants()}>
|
|
1134
|
+
{canExpand ? (
|
|
1135
|
+
<IconButton
|
|
1136
|
+
as={expanded ? KeyboardArrowDown : KeyboardArrowRight}
|
|
1137
|
+
variant="ghost"
|
|
1138
|
+
color="neutral"
|
|
1139
|
+
size="small"
|
|
1140
|
+
iconClassName={expanded ? undefined : 'rtl:-scale-x-100'}
|
|
1141
|
+
accessibilityLabel={`${expanded ? 'Collapse' : 'Expand'} ${treeLabel}`}
|
|
1142
|
+
accessibilityState={{ expanded }}
|
|
1143
|
+
aria-expanded={expanded}
|
|
1144
|
+
onPress={() => tree.toggleRowExpanded(row.id)}
|
|
1145
|
+
/>
|
|
1146
|
+
) : null}
|
|
1147
|
+
</View>
|
|
1148
|
+
{treeCheckbox ? (
|
|
1149
|
+
<Checkbox
|
|
1150
|
+
value={String(rowId)}
|
|
1151
|
+
isChecked={status === 'all'}
|
|
1152
|
+
isIndeterminate={status === 'some'}
|
|
1153
|
+
onChange={() => tree.toggleRowSelected(row)}
|
|
1154
|
+
accessibilityLabel={`Select ${treeLabel}`}
|
|
1155
|
+
aria-label={`Select ${treeLabel}`}
|
|
1156
|
+
/>
|
|
1157
|
+
) : null}
|
|
1158
|
+
<View className={dataGridTreeContentVariants()}>
|
|
1159
|
+
{typeof content === 'string' || typeof content === 'number' ? (
|
|
1160
|
+
<Text numberOfLines={1}>{content}</Text>
|
|
1161
|
+
) : (
|
|
1162
|
+
content
|
|
1163
|
+
)}
|
|
1164
|
+
</View>
|
|
1165
|
+
</View>
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
const align = resolveColumnAlign(col);
|
|
1170
|
+
const cellText =
|
|
1171
|
+
!col?.renderCell &&
|
|
1172
|
+
(typeof content === 'string' || typeof content === 'number')
|
|
1173
|
+
? String(content)
|
|
1174
|
+
: undefined;
|
|
1175
|
+
|
|
868
1176
|
return (
|
|
869
1177
|
<Table.Cell
|
|
870
1178
|
key={cell.id}
|
|
871
|
-
align={
|
|
1179
|
+
align={align}
|
|
1180
|
+
{...(Platform.OS === 'web' && cellText ? { title: cellText } : {})}
|
|
872
1181
|
className={cn(dataGridCellVariants(), pinnedClassName(pin, false))}
|
|
873
1182
|
style={mergePinnedStyle(
|
|
874
1183
|
cellStyle(col, columnWidths.get(cell.column.id)),
|
|
@@ -878,12 +1187,43 @@ function DataGridInner<R extends GridValidRowModel>(props: DataGridProps<R>) {
|
|
|
878
1187
|
false,
|
|
879
1188
|
)}
|
|
880
1189
|
>
|
|
881
|
-
{
|
|
1190
|
+
{cellText !== undefined ? (
|
|
1191
|
+
<Text
|
|
1192
|
+
numberOfLines={1}
|
|
1193
|
+
className={dataGridCellTextVariants({
|
|
1194
|
+
align,
|
|
1195
|
+
fitToColumn:
|
|
1196
|
+
Platform.OS === 'web' && columnWidths.has(cell.column.id),
|
|
1197
|
+
})}
|
|
1198
|
+
>
|
|
1199
|
+
{cellText}
|
|
1200
|
+
</Text>
|
|
1201
|
+
) : (
|
|
1202
|
+
content
|
|
1203
|
+
)}
|
|
882
1204
|
</Table.Cell>
|
|
883
1205
|
);
|
|
884
1206
|
})}
|
|
885
1207
|
</Table.Row>
|
|
886
1208
|
);
|
|
1209
|
+
if (!detailOpen) {
|
|
1210
|
+
return [dataRow];
|
|
1211
|
+
}
|
|
1212
|
+
return [
|
|
1213
|
+
dataRow,
|
|
1214
|
+
<Table.Row
|
|
1215
|
+
key={`${row.id}:detail`}
|
|
1216
|
+
className="web:hover:bg-transparent"
|
|
1217
|
+
testID={`data-grid-detail-panel-${String(rowId)}`}
|
|
1218
|
+
>
|
|
1219
|
+
<Table.Cell
|
|
1220
|
+
colSpan={leafColumnCount}
|
|
1221
|
+
className={dataGridDetailPanelCellVariants()}
|
|
1222
|
+
>
|
|
1223
|
+
<View style={detailPanelContentStyle}>{detailContent}</View>
|
|
1224
|
+
</Table.Cell>
|
|
1225
|
+
</Table.Row>,
|
|
1226
|
+
];
|
|
887
1227
|
})
|
|
888
1228
|
)}
|
|
889
1229
|
</Table.Body>
|