@opencorestack/opengridx 2.1.0 → 3.0.1
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/CHANGELOG.md +399 -0
- package/README.md +95 -72
- package/dist/index.d.ts +590 -131
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7891 -5631
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +17 -23
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +348 -166
- package/docs/README.md +6 -1
- package/docs/architecture/datagrid-orchestration.md +81 -0
- package/docs/architecture/grid-row-meta.md +102 -0
- package/docs/architecture/use-grid-columns.md +128 -0
- package/docs/architecture/use-grid-controlled-state.md +157 -0
- package/docs/architecture/use-grid-row-pipeline.md +120 -0
- package/docs/architecture/use-grid-scroll-sync.md +111 -0
- package/docs/architecture/use-grid-state-snapshot.md +90 -0
- package/docs/architecture/use-grid-virtualization.md +115 -0
- package/docs/architecture/use-grid-visible-rows.md +85 -0
- package/docs/assets/banner-alt.png +0 -0
- package/docs/assets/banner.png +0 -0
- package/docs/assets/hero.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/components/aggregation-footer.md +15 -10
- package/docs/components/cell.md +16 -6
- package/docs/components/column-group-header.md +55 -28
- package/docs/components/column-resize.md +17 -13
- package/docs/components/column-visibility.md +6 -7
- package/docs/components/datagrid.md +15 -13
- package/docs/components/empty-state.md +2 -1
- package/docs/components/error-overlay.md +12 -7
- package/docs/components/filter-panel.md +28 -8
- package/docs/components/header.md +28 -9
- package/docs/components/pagination.md +14 -8
- package/docs/components/row.md +21 -5
- package/docs/components/toolbar.md +27 -9
- package/docs/components/tooltip.md +16 -7
- package/docs/customization/slots-api.md +90 -38
- package/docs/customization/theming.md +66 -18
- package/docs/features/aggregation-pivot.md +75 -9
- package/docs/features/cell-spanning.md +80 -125
- package/docs/features/clipboard.md +41 -16
- package/docs/features/custom-pagination.md +38 -18
- package/docs/features/data-source.md +36 -13
- package/docs/features/editing-reordering.md +125 -16
- package/docs/features/export-guide.md +126 -39
- package/docs/features/filtering.md +44 -6
- package/docs/features/infinite-scroll.md +54 -46
- package/docs/features/keyboard-navigation.md +83 -0
- package/docs/features/list-view.md +58 -5
- package/docs/features/loading-states.md +18 -20
- package/docs/features/master-detail.md +18 -5
- package/docs/features/pdf-export.md +72 -19
- package/docs/features/pinning.md +24 -5
- package/docs/features/selection.md +50 -10
- package/docs/features/sorting-pagination.md +64 -14
- package/docs/features/state-persistence.md +78 -10
- package/docs/features/toolbar-customization.md +26 -17
- package/docs/features/tree-data-grouping.md +36 -8
- package/docs/features/virtualization.md +37 -14
- package/docs/migration/v1-to-v2.md +115 -0
- package/docs/migration/v2-to-v3.md +1233 -0
- package/docs/performance.md +124 -0
- package/docs/roadmap.md +5 -5
- package/lib/components/Cell/Cell.css +17 -1
- package/lib/components/Cell/Cell.tsx +129 -57
- package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
- package/lib/components/Cell/GridEditInputCell.tsx +114 -41
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
- package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
- package/lib/components/DataGrid/DataGrid.css +85 -12
- package/lib/components/DataGrid/DataGrid.tsx +582 -876
- package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
- package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
- package/lib/components/DataGrid/GridListView.tsx +165 -24
- package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
- package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
- package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
- package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
- package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
- package/lib/components/DetailPanel/DetailPanel.css +0 -6
- package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
- package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
- package/lib/components/FilterPanel/FilterPanel.css +13 -2
- package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
- package/lib/components/Header/ColumnMenu.tsx +91 -39
- package/lib/components/Header/Header.css +6 -3
- package/lib/components/Header/Header.tsx +181 -160
- package/lib/components/ListView/ListView.css +25 -0
- package/lib/components/ListView/ListViewRow.tsx +76 -10
- package/lib/components/Pagination/Pagination.tsx +15 -7
- package/lib/components/Row/Row.tsx +177 -77
- package/lib/components/Toolbar/GlobalSearch.css +6 -3
- package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
- package/lib/components/Toolbar/GridToolbar.css +12 -11
- package/lib/components/Toolbar/GridToolbar.tsx +133 -89
- package/lib/components/Toolbar/PivotPanel.tsx +89 -49
- package/lib/components/Tooltip/Tooltip.css +20 -0
- package/lib/components/Tooltip/Tooltip.tsx +68 -35
- package/lib/components/ui/Button.tsx +3 -0
- package/lib/components/ui/Checkbox.tsx +2 -1
- package/lib/components/ui/ExpandIcon.css +0 -9
- package/lib/components/ui/ExpandIcon.tsx +4 -1
- package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
- package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
- package/lib/hooks/core/useDataGrid.ts +115 -173
- package/lib/hooks/core/useGridAggregationApi.ts +72 -0
- package/lib/hooks/core/useGridApiMethods.ts +115 -0
- package/lib/hooks/core/useGridApiRef.ts +19 -4
- package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
- package/lib/hooks/core/useGridAriaRows.ts +38 -0
- package/lib/hooks/core/useGridClipboardApi.ts +38 -0
- package/lib/hooks/core/useGridColumnLookup.ts +30 -0
- package/lib/hooks/core/useGridColumns.tsx +279 -76
- package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
- package/lib/hooks/core/useGridControlledState.ts +99 -6
- package/lib/hooks/core/useGridDetailPanel.ts +51 -0
- package/lib/hooks/core/useGridDevWarnings.ts +104 -5
- package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
- package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
- package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
- package/lib/hooks/core/useGridHierarchy.ts +81 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
- package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
- package/lib/hooks/core/useGridPageCorrection.ts +55 -0
- package/lib/hooks/core/useGridRowIdOf.ts +32 -0
- package/lib/hooks/core/useGridRowInteractions.ts +81 -0
- package/lib/hooks/core/useGridRowPipeline.ts +69 -39
- package/lib/hooks/core/useGridRowSelection.ts +91 -0
- package/lib/hooks/core/useGridScrollSync.ts +80 -32
- package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
- package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
- package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
- package/lib/hooks/core/useGridToolbarProps.ts +97 -0
- package/lib/hooks/core/useGridViewport.ts +38 -0
- package/lib/hooks/core/useGridViewportSize.ts +26 -0
- package/lib/hooks/core/useGridVisibleRows.ts +17 -5
- package/lib/hooks/core/useLayout.ts +215 -195
- package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
- package/lib/hooks/core/useStableColumns.ts +34 -0
- package/lib/hooks/features/useAggregation.ts +82 -51
- package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
- package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
- package/lib/hooks/features/useGridClipboard.ts +133 -73
- package/lib/hooks/features/useGridDataSource.ts +354 -128
- package/lib/hooks/features/useGridEditing.ts +211 -51
- package/lib/hooks/features/useGridPivot.ts +92 -0
- package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
- package/lib/hooks/features/useGridSpanning.ts +410 -306
- package/lib/hooks/features/usePivot.ts +0 -0
- package/lib/hooks/features/useServerTreeChildren.ts +50 -0
- package/lib/hooks/useColumnReorder.ts +38 -44
- package/lib/hooks/useRowGrouping.ts +298 -160
- package/lib/hooks/useRowReorder.ts +62 -31
- package/lib/hooks/useTreeData.ts +287 -220
- package/lib/index.ts +28 -2
- package/lib/state/index.ts +1 -0
- package/lib/state/types.ts +7 -1
- package/lib/state/useGridStateStorage.ts +88 -29
- package/lib/theme/DataGridThemeProvider.tsx +143 -38
- package/lib/theme/gridThemeContext.ts +5 -0
- package/lib/theme/index.ts +5 -0
- package/lib/theme/presets.ts +5 -4
- package/lib/theme/types.ts +40 -2
- package/lib/types/index.ts +313 -116
- package/lib/utils/aggregation/index.ts +119 -24
- package/lib/utils/aria/index.ts +40 -0
- package/lib/utils/columnGroups.ts +110 -0
- package/lib/utils/columnLookup.ts +33 -0
- package/lib/utils/columnWidth.ts +22 -0
- package/lib/utils/dataSource/index.ts +46 -0
- package/lib/utils/detailPanel.ts +66 -0
- package/lib/utils/dragData.ts +44 -0
- package/lib/utils/editing/index.ts +30 -0
- package/lib/utils/export/exportShared.ts +197 -0
- package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
- package/lib/utils/export/exportToPdf.ts +143 -92
- package/lib/utils/export/groupLabel.ts +14 -0
- package/lib/utils/export/index.ts +278 -272
- package/lib/utils/export/pdfText.ts +50 -0
- package/lib/utils/filtering/index.ts +277 -148
- package/lib/utils/focus/index.ts +115 -0
- package/lib/utils/gridApiRows.ts +35 -0
- package/lib/utils/gridModes.ts +77 -0
- package/lib/utils/grouping/groupedExportRows.ts +91 -0
- package/lib/utils/pagination/index.ts +56 -0
- package/lib/utils/pinning/index.ts +77 -33
- package/lib/utils/pivot/index.ts +368 -0
- package/lib/utils/scroll/index.ts +55 -5
- package/lib/utils/scroll/scrollEnd.ts +18 -0
- package/lib/utils/sorting/index.ts +154 -30
- package/lib/utils/syntheticRows.ts +12 -0
- package/lib/utils/values.ts +206 -0
- package/lib/utils/viewport.ts +14 -0
- package/llms.txt +261 -164
- package/package.json +13 -5
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
- package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
- package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
- package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
- package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
- package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
- package/lib/components/Header/Header.test.tsx +0 -135
- package/lib/components/Pagination/Pagination.test.tsx +0 -64
- package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
- package/lib/hooks/core/useGridControlledState.test.ts +0 -86
- package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
- package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
- package/lib/hooks/features/useAggregation.test.ts +0 -175
- package/lib/hooks/features/usePivot.test.ts +0 -202
- package/lib/hooks/useRowGrouping.test.ts +0 -298
- package/lib/hooks/useTreeData.test.ts +0 -83
- package/lib/utils/aggregation/aggregation.test.ts +0 -16
- package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
- package/lib/utils/export/exportToPdf.test.ts +0 -120
- package/lib/utils/export/groupedExport.test.ts +0 -144
- package/lib/utils/filtering/filtering.test.ts +0 -178
- package/lib/utils/sorting/sorting.test.ts +0 -88
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Row } from '../Row/Row';
|
|
3
|
+
import { DetailPanelContent } from '../DetailPanel/DetailPanelContent';
|
|
4
|
+
import { callGetDetailPanelHeight, resolveDetailPanelHeight } from '../../utils/detailPanel';
|
|
3
5
|
import type {
|
|
4
6
|
GridRowModel,
|
|
5
7
|
GridRowId,
|
|
@@ -13,6 +15,14 @@ import type {
|
|
|
13
15
|
GridRowMeta,
|
|
14
16
|
} from '../../types';
|
|
15
17
|
import type { CellColSpanInfo, RowSpanningCaches } from '../../hooks/features/useGridSpanning';
|
|
18
|
+
import type { GridEditingState } from '../../hooks/features/useGridEditing';
|
|
19
|
+
|
|
20
|
+
interface PinnedRowEditingHandlers {
|
|
21
|
+
editingCell: GridEditingState['editingCell'];
|
|
22
|
+
startCellEdit: (params: { id: GridRowId; field: string; value: unknown }) => void;
|
|
23
|
+
stopCellEdit: (params?: { cancel?: boolean; id?: GridRowId; field?: string }) => void;
|
|
24
|
+
setEditCellValue: (params: { id: GridRowId; field: string; value: unknown }) => void;
|
|
25
|
+
}
|
|
16
26
|
|
|
17
27
|
export interface GridPinnedRowsProps<R extends GridRowModel> {
|
|
18
28
|
rows: R[];
|
|
@@ -32,15 +42,34 @@ export interface GridPinnedRowsProps<R extends GridRowModel> {
|
|
|
32
42
|
getDetailPanelContent?: (params: GridDetailPanelParams<R>) => React.ReactNode;
|
|
33
43
|
getDetailPanelHeight?: (params: GridDetailPanelParams<R>) => GridDetailPanelHeight;
|
|
34
44
|
onDetailPanelToggle: (rowId: GridRowId) => void;
|
|
45
|
+
/** Receives the rendered height of `'auto'` detail panels. */
|
|
46
|
+
onDetailPanelHeightChange?: (rowId: GridRowId, height: number) => void;
|
|
35
47
|
pinCheckboxColumn?: boolean;
|
|
36
48
|
pinExpandColumn?: boolean;
|
|
37
|
-
focusedCell: { id: GridRowId; field: string } | null;
|
|
49
|
+
focusedCell: { id: GridRowId | null; field: string } | null;
|
|
38
50
|
colspanMap?: Map<GridRowId, Record<string, CellColSpanInfo>>;
|
|
39
51
|
rowSpanningCaches?: RowSpanningCaches;
|
|
40
52
|
rowHeight: number;
|
|
41
53
|
rowMetaMap: Map<GridRowId, GridRowMeta>;
|
|
54
|
+
/** Resolves a row's id; defaults to `row.id`. */
|
|
55
|
+
getRowId?: (row: R) => GridRowId;
|
|
56
|
+
/** Index of the first of these rows in the grid's renderable rows (top-pinned + centre + bottom-pinned). */
|
|
57
|
+
rowIndexOffset?: number;
|
|
58
|
+
/** `aria-rowindex` of the first of these rows. */
|
|
59
|
+
ariaRowIndexBase?: number;
|
|
60
|
+
columnIndexMap?: Map<string, number>;
|
|
61
|
+
/** Pinned rows are edited like any other row. */
|
|
62
|
+
editingHandlers?: PinnedRowEditingHandlers;
|
|
63
|
+
isCellEditable?: (params: GridCellParams<R>) => boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Renders the (empty, non-draggable) reorder cell so the row lines up with the header and the
|
|
66
|
+
* centre rows. Pinned rows cannot be dragged.
|
|
67
|
+
*/
|
|
68
|
+
rowReordering?: boolean;
|
|
42
69
|
}
|
|
43
70
|
|
|
71
|
+
const defaultGetRowId = <R extends GridRowModel>(row: R): GridRowId => row.id;
|
|
72
|
+
|
|
44
73
|
export function GridPinnedRows<R extends GridRowModel>({
|
|
45
74
|
rows,
|
|
46
75
|
position,
|
|
@@ -59,6 +88,7 @@ export function GridPinnedRows<R extends GridRowModel>({
|
|
|
59
88
|
getDetailPanelContent,
|
|
60
89
|
getDetailPanelHeight,
|
|
61
90
|
onDetailPanelToggle,
|
|
91
|
+
onDetailPanelHeightChange,
|
|
62
92
|
pinCheckboxColumn,
|
|
63
93
|
pinExpandColumn,
|
|
64
94
|
focusedCell,
|
|
@@ -66,18 +96,31 @@ export function GridPinnedRows<R extends GridRowModel>({
|
|
|
66
96
|
rowSpanningCaches,
|
|
67
97
|
rowHeight,
|
|
68
98
|
rowMetaMap,
|
|
99
|
+
getRowId = defaultGetRowId,
|
|
100
|
+
rowIndexOffset = 0,
|
|
101
|
+
ariaRowIndexBase,
|
|
102
|
+
columnIndexMap,
|
|
103
|
+
editingHandlers,
|
|
104
|
+
isCellEditable,
|
|
105
|
+
rowReordering = false,
|
|
69
106
|
}: GridPinnedRowsProps<R>) {
|
|
70
107
|
if (rows.length === 0) return null;
|
|
71
108
|
|
|
72
109
|
return (
|
|
73
110
|
<div className={`ogx__pinned-rows ogx__pinned-rows--${position}`} role="rowgroup">
|
|
74
|
-
{rows.map((row, index) =>
|
|
111
|
+
{rows.map((row, index) => {
|
|
112
|
+
const id = getRowId(row);
|
|
113
|
+
const rowIndex = rowIndexOffset + index;
|
|
114
|
+
return (
|
|
75
115
|
<Row<R>
|
|
76
|
-
key={
|
|
116
|
+
key={id}
|
|
77
117
|
row={row}
|
|
118
|
+
rowId={id}
|
|
78
119
|
columns={columns}
|
|
79
|
-
rowIndex={
|
|
80
|
-
|
|
120
|
+
rowIndex={rowIndex}
|
|
121
|
+
ariaRowIndex={ariaRowIndexBase !== undefined ? ariaRowIndexBase + index : undefined}
|
|
122
|
+
columnIndexMap={columnIndexMap}
|
|
123
|
+
isSelected={selectedRowIds.has(id)}
|
|
81
124
|
checkboxSelection={checkboxSelection}
|
|
82
125
|
onRowClick={onRowClick}
|
|
83
126
|
onRowDoubleClick={onRowDoubleClick}
|
|
@@ -87,19 +130,27 @@ export function GridPinnedRows<R extends GridRowModel>({
|
|
|
87
130
|
pinnedColumns={pinnedColumns}
|
|
88
131
|
pinnedRows={pinnedRows}
|
|
89
132
|
hasDetailPanel={hasDetailPanel}
|
|
90
|
-
isDetailPanelExpanded={expandedRowIds.has(
|
|
91
|
-
detailPanelContent={getDetailPanelContent ? getDetailPanelContent
|
|
92
|
-
detailPanelHeight={getDetailPanelHeight
|
|
133
|
+
isDetailPanelExpanded={expandedRowIds.has(id)}
|
|
134
|
+
detailPanelContent={expandedRowIds.has(id) && getDetailPanelContent ? <DetailPanelContent<R> getContent={getDetailPanelContent} params={{ row, id, rowIndex }} /> : null}
|
|
135
|
+
detailPanelHeight={expandedRowIds.has(id) ? resolveDetailPanelHeight(callGetDetailPanelHeight(getDetailPanelHeight, { row, id, rowIndex })) : undefined}
|
|
136
|
+
onDetailPanelHeightChange={onDetailPanelHeightChange}
|
|
93
137
|
onDetailPanelToggle={onDetailPanelToggle}
|
|
94
138
|
pinCheckboxColumn={pinCheckboxColumn}
|
|
95
139
|
pinExpandColumn={pinExpandColumn}
|
|
96
|
-
focusedCellField={focusedCell?.id ===
|
|
140
|
+
focusedCellField={focusedCell?.id === id ? focusedCell.field : null}
|
|
97
141
|
colspanMap={colspanMap}
|
|
98
142
|
rowSpanningCaches={rowSpanningCaches}
|
|
99
143
|
rowHeight={rowHeight}
|
|
100
|
-
rowMeta={rowMetaMap.get(
|
|
144
|
+
rowMeta={rowMetaMap.get(id)}
|
|
145
|
+
editingCell={editingHandlers?.editingCell}
|
|
146
|
+
onEditStart={editingHandlers?.startCellEdit}
|
|
147
|
+
onEditStop={editingHandlers?.stopCellEdit}
|
|
148
|
+
onEditCellValueChange={editingHandlers?.setEditCellValue}
|
|
149
|
+
isCellEditable={isCellEditable}
|
|
150
|
+
rowReordering={rowReordering}
|
|
101
151
|
/>
|
|
102
|
-
|
|
152
|
+
);
|
|
153
|
+
})}
|
|
103
154
|
</div>
|
|
104
155
|
);
|
|
105
156
|
}
|
|
@@ -1,67 +1,67 @@
|
|
|
1
1
|
import React, { useState, useLayoutEffect } from 'react';
|
|
2
2
|
import ReactDOM from 'react-dom';
|
|
3
3
|
import { ColumnVisibilityPanel } from '../ColumnVisibilityPanel/ColumnVisibilityPanel';
|
|
4
|
-
import type { GridColDef, GridRowModel,
|
|
4
|
+
import type { GridColDef, GridRowModel, GridColumnGroupingModel } from '../../types';
|
|
5
|
+
import { canReorderWithinColumnGroups } from '../../utils/columnGroups';
|
|
6
|
+
import { getViewportWidth } from '../../utils/viewport';
|
|
5
7
|
|
|
6
8
|
export interface GridStandaloneColumnPanelProps<R extends GridRowModel> {
|
|
7
9
|
isOpen: boolean;
|
|
8
10
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
9
11
|
panelRef?: React.RefObject<HTMLDivElement | null>;
|
|
10
|
-
|
|
12
|
+
/** Every column, in the grid's current column order. */
|
|
13
|
+
columns: GridColDef<R>[];
|
|
11
14
|
columnVisibilityModel: Record<string, boolean>;
|
|
12
|
-
effectiveColumnOrder: string[];
|
|
13
|
-
columnOrder?: string[];
|
|
14
15
|
disableColumnReorder: boolean;
|
|
15
16
|
onClose: () => void;
|
|
16
17
|
onColumnVisibilityChange: (model: Record<string, boolean>) => void;
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
/** Moves `fromField` to the position of `toField` in the column order. */
|
|
19
|
+
onColumnMove: (fromField: string, toField: string) => void;
|
|
20
|
+
/** Restores the columns' definition order (the panel's Reset). */
|
|
21
|
+
onColumnOrderReset: () => void;
|
|
22
|
+
/** When set, a column can only be moved within its own column group. */
|
|
23
|
+
columnGroupingModel?: GridColumnGroupingModel;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface PanelPlacement {
|
|
27
|
+
top: number;
|
|
28
|
+
right: number;
|
|
29
|
+
target: Element;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
32
|
export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
22
33
|
isOpen,
|
|
23
34
|
containerRef,
|
|
24
35
|
panelRef,
|
|
25
|
-
|
|
36
|
+
columns,
|
|
26
37
|
columnVisibilityModel,
|
|
27
|
-
effectiveColumnOrder,
|
|
28
|
-
columnOrder,
|
|
29
38
|
disableColumnReorder,
|
|
30
39
|
onClose,
|
|
31
40
|
onColumnVisibilityChange,
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
onColumnMove,
|
|
42
|
+
onColumnOrderReset,
|
|
43
|
+
columnGroupingModel,
|
|
34
44
|
}: GridStandaloneColumnPanelProps<R>) {
|
|
35
|
-
|
|
36
|
-
const [
|
|
37
|
-
const [portalTarget, setPortalTarget] = useState<Element>(() => document.body);
|
|
45
|
+
// Resolved from the DOM after mount, never during render, so the grid renders on the server.
|
|
46
|
+
const [placement, setPlacement] = useState<PanelPlacement | null>(null);
|
|
38
47
|
|
|
39
48
|
useLayoutEffect(() => {
|
|
40
49
|
if (!isOpen || !containerRef.current) return;
|
|
41
50
|
const rect = containerRef.current.getBoundingClientRect();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
setPlacement({
|
|
52
|
+
top: rect.top + 8,
|
|
53
|
+
right: getViewportWidth() - rect.right + 8,
|
|
54
|
+
target: containerRef.current.closest('.ogx-theme-provider') || document.body,
|
|
55
|
+
});
|
|
45
56
|
}, [isOpen, containerRef]);
|
|
46
57
|
|
|
47
|
-
if (!isOpen) return null;
|
|
58
|
+
if (!isOpen || !placement) return null;
|
|
48
59
|
|
|
49
60
|
const handleReorder = disableColumnReorder
|
|
50
61
|
? undefined
|
|
51
62
|
: (fromField: string, toField: string) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const toIdx = currentOrder.indexOf(toField);
|
|
55
|
-
if (fromIdx === -1 || toIdx === -1) return;
|
|
56
|
-
const newOrder = [...currentOrder];
|
|
57
|
-
newOrder.splice(fromIdx, 1);
|
|
58
|
-
newOrder.splice(toIdx, 0, fromField);
|
|
59
|
-
if (!columnOrder) setInternalColumnOrder(newOrder);
|
|
60
|
-
onColumnOrderChange?.({
|
|
61
|
-
oldIndex: fromIdx,
|
|
62
|
-
targetIndex: toIdx,
|
|
63
|
-
column: effectiveColumns.find(c => c.field === fromField) as unknown as GridColDef,
|
|
64
|
-
});
|
|
63
|
+
if (!canReorderWithinColumnGroups(columnGroupingModel, fromField, toField)) return;
|
|
64
|
+
onColumnMove(fromField, toField);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
return ReactDOM.createPortal(
|
|
@@ -69,13 +69,14 @@ export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
|
69
69
|
ref={panelRef}
|
|
70
70
|
style={{
|
|
71
71
|
position: 'fixed',
|
|
72
|
-
top:
|
|
73
|
-
right:
|
|
72
|
+
top: placement.top,
|
|
73
|
+
right: placement.right,
|
|
74
74
|
zIndex: 9999,
|
|
75
75
|
display: 'inline-block',
|
|
76
76
|
}}
|
|
77
77
|
>
|
|
78
78
|
<button
|
|
79
|
+
type="button"
|
|
79
80
|
onClick={onClose}
|
|
80
81
|
style={{
|
|
81
82
|
position: 'absolute',
|
|
@@ -99,9 +100,9 @@ export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
|
99
100
|
aria-label="Close"
|
|
100
101
|
>×</button>
|
|
101
102
|
<ColumnVisibilityPanel<R>
|
|
102
|
-
columns={
|
|
103
|
+
columns={columns}
|
|
103
104
|
visibleColumns={new Set(
|
|
104
|
-
|
|
105
|
+
columns
|
|
105
106
|
.filter(col => columnVisibilityModel[col.field] !== false)
|
|
106
107
|
.map(col => col.field)
|
|
107
108
|
)}
|
|
@@ -110,17 +111,18 @@ export function GridStandaloneColumnPanel<R extends GridRowModel>({
|
|
|
110
111
|
}}
|
|
111
112
|
onShowAll={() => {
|
|
112
113
|
const next = { ...columnVisibilityModel };
|
|
113
|
-
|
|
114
|
+
columns.forEach(col => { if (col.hideable !== false) next[col.field] = true; });
|
|
114
115
|
onColumnVisibilityChange(next);
|
|
115
116
|
}}
|
|
116
117
|
onHideAll={() => {
|
|
117
118
|
const next = { ...columnVisibilityModel };
|
|
118
|
-
|
|
119
|
+
columns.forEach(col => { if (col.hideable !== false) next[col.field] = false; });
|
|
119
120
|
onColumnVisibilityChange(next);
|
|
120
121
|
}}
|
|
121
122
|
onColumnReorder={handleReorder}
|
|
123
|
+
onColumnOrderReset={disableColumnReorder ? undefined : onColumnOrderReset}
|
|
122
124
|
/>
|
|
123
125
|
</div>,
|
|
124
|
-
|
|
126
|
+
placement.target
|
|
125
127
|
);
|
|
126
128
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { GridToolbarSlotProps as GridToolbarSlotComponentProps } from '../../types';
|
|
3
|
+
|
|
4
|
+
type ToolbarProps = GridToolbarSlotComponentProps & Record<string, unknown>;
|
|
5
|
+
type ToolbarRender = (props: ToolbarProps) => React.ReactNode;
|
|
6
|
+
|
|
7
|
+
interface GridToolbarSlotProps {
|
|
8
|
+
component: React.ComponentType<ToolbarProps>;
|
|
9
|
+
toolbarProps: ToolbarProps;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface InlineToolbarProps {
|
|
13
|
+
render: ToolbarRender;
|
|
14
|
+
toolbarProps: ToolbarProps;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A plain function component: not a class, and not a memo / forwardRef / lazy object. */
|
|
18
|
+
function isPlainFunctionComponent(component: React.ComponentType<ToolbarProps>): component is ToolbarRender {
|
|
19
|
+
if (typeof component !== 'function') return false;
|
|
20
|
+
const proto: unknown = component.prototype;
|
|
21
|
+
return !(typeof proto === 'object' && proto !== null && 'isReactComponent' in proto);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Runs a plain function toolbar inside its own fiber. The caller keys this element on the
|
|
26
|
+
* function's source, so the fiber (and every hook in it) survives when the consumer redefines
|
|
27
|
+
* the same toolbar inline on each render, but remounts when a different toolbar is passed.
|
|
28
|
+
*/
|
|
29
|
+
function InlineToolbar({ render, toolbarProps }: InlineToolbarProps) {
|
|
30
|
+
return <>{render(toolbarProps)}</>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Renders `slots.toolbar`.
|
|
35
|
+
*
|
|
36
|
+
* Consumers often write the toolbar as an inline function inside their own component, which
|
|
37
|
+
* gives it a new identity on every render. Rendering that as `<Toolbar />` would remount it on
|
|
38
|
+
* every keystroke and lose GridToolbar's state (open panels, typed search). A plain function is
|
|
39
|
+
* therefore called from a wrapper keyed on its source text: the same code keeps its state, and
|
|
40
|
+
* a switch to a toolbar with different code (and different hooks) remounts cleanly.
|
|
41
|
+
*
|
|
42
|
+
* Class, `React.memo`, `forwardRef` and `lazy` components cannot be called as functions, so they
|
|
43
|
+
* are rendered as ordinary elements.
|
|
44
|
+
*/
|
|
45
|
+
export function GridToolbarSlot({ component, toolbarProps }: GridToolbarSlotProps) {
|
|
46
|
+
if (isPlainFunctionComponent(component)) {
|
|
47
|
+
return <InlineToolbar key={`fn:${component.toString()}`} render={component} toolbarProps={toolbarProps} />;
|
|
48
|
+
}
|
|
49
|
+
const Toolbar = component;
|
|
50
|
+
return <Toolbar {...toolbarProps} />;
|
|
51
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Row } from '../Row/Row';
|
|
3
|
+
import { DetailPanelContent } from '../DetailPanel/DetailPanelContent';
|
|
3
4
|
import { SkeletonRow } from '../SkeletonRow';
|
|
4
|
-
import {
|
|
5
|
+
import { callGetDetailPanelHeight, resolveDetailPanelHeight } from '../../utils/detailPanel';
|
|
5
6
|
import type {
|
|
6
7
|
GridRowModel,
|
|
7
8
|
GridRowId,
|
|
@@ -9,7 +10,6 @@ import type {
|
|
|
9
10
|
GridRowParams,
|
|
10
11
|
GridCellParams,
|
|
11
12
|
GridColumnPinning,
|
|
12
|
-
GridRowPinning,
|
|
13
13
|
GridDetailPanelParams,
|
|
14
14
|
GridDetailPanelHeight,
|
|
15
15
|
GridRowMeta,
|
|
@@ -22,7 +22,7 @@ import type { CellColSpanInfo, RowSpanningCaches } from '../../hooks/features/us
|
|
|
22
22
|
interface RowEditingHandlers {
|
|
23
23
|
editingCell: GridEditingState['editingCell'];
|
|
24
24
|
startCellEdit: (params: { id: GridRowId; field: string; value: unknown }) => void;
|
|
25
|
-
stopCellEdit: (params?: { cancel?: boolean }) => void;
|
|
25
|
+
stopCellEdit: (params?: { cancel?: boolean; id?: GridRowId; field?: string }) => void;
|
|
26
26
|
setEditCellValue: (params: { id: GridRowId; field: string; value: unknown }) => void;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -38,7 +38,6 @@ export interface GridVirtualRowsProps<R extends GridRowModel> {
|
|
|
38
38
|
hasDetailPanel: boolean;
|
|
39
39
|
rowReordering: boolean;
|
|
40
40
|
rowHeight: number;
|
|
41
|
-
pinnedRows?: GridRowPinning;
|
|
42
41
|
selectedRowIds: Set<GridRowId>;
|
|
43
42
|
onRowClick: (params: GridRowParams<R>) => void;
|
|
44
43
|
onRowDoubleClick?: (params: GridRowParams<R>) => void;
|
|
@@ -50,11 +49,14 @@ export interface GridVirtualRowsProps<R extends GridRowModel> {
|
|
|
50
49
|
getDetailPanelContent?: (params: GridDetailPanelParams<R>) => React.ReactNode;
|
|
51
50
|
getDetailPanelHeight?: (params: GridDetailPanelParams<R>) => GridDetailPanelHeight;
|
|
52
51
|
onDetailPanelToggle: (rowId: GridRowId) => void;
|
|
52
|
+
/** Receives the rendered height of `'auto'` detail panels. */
|
|
53
|
+
onDetailPanelHeightChange?: (rowId: GridRowId, height: number) => void;
|
|
53
54
|
pinCheckboxColumn?: boolean;
|
|
54
55
|
pinExpandColumn?: boolean;
|
|
55
56
|
rowReorderHandlers: UseRowReorderReturn;
|
|
56
57
|
editingHandlers: RowEditingHandlers;
|
|
57
|
-
|
|
58
|
+
isCellEditable?: (params: GridCellParams<R>) => boolean;
|
|
59
|
+
focusedCell: { id: GridRowId | null; field: string } | null;
|
|
58
60
|
colspanMap?: Map<GridRowId, Record<string, CellColSpanInfo>>;
|
|
59
61
|
rowSpanningCaches?: RowSpanningCaches;
|
|
60
62
|
paginationMode: 'client' | 'server' | 'infinite';
|
|
@@ -62,8 +64,13 @@ export interface GridVirtualRowsProps<R extends GridRowModel> {
|
|
|
62
64
|
sortedUnpinnedRowCount: number;
|
|
63
65
|
infiniteScrollSkeletonCount: number;
|
|
64
66
|
unpinnedRowsLength: number;
|
|
67
|
+
/** Last center row index in the render window: skeleton rows render only once it reaches the end of the data. */
|
|
68
|
+
lastRenderedRowIndex: number;
|
|
65
69
|
rowMetaMap: Map<GridRowId, GridRowMeta>;
|
|
66
70
|
loadingOverlay?: React.ReactNode;
|
|
71
|
+
/** Added to a row's rowIndex to get its `aria-rowindex` (header rows and earlier pages included). */
|
|
72
|
+
ariaRowIndexOffset?: number;
|
|
73
|
+
columnIndexMap?: Map<string, number>;
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
export function GridVirtualRows<R extends GridRowModel>({
|
|
@@ -78,7 +85,6 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
78
85
|
hasDetailPanel,
|
|
79
86
|
rowReordering,
|
|
80
87
|
rowHeight,
|
|
81
|
-
pinnedRows,
|
|
82
88
|
selectedRowIds,
|
|
83
89
|
onRowClick,
|
|
84
90
|
onRowDoubleClick,
|
|
@@ -90,10 +96,12 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
90
96
|
getDetailPanelContent,
|
|
91
97
|
getDetailPanelHeight,
|
|
92
98
|
onDetailPanelToggle,
|
|
99
|
+
onDetailPanelHeightChange,
|
|
93
100
|
pinCheckboxColumn,
|
|
94
101
|
pinExpandColumn,
|
|
95
102
|
rowReorderHandlers,
|
|
96
103
|
editingHandlers,
|
|
104
|
+
isCellEditable,
|
|
97
105
|
focusedCell,
|
|
98
106
|
colspanMap,
|
|
99
107
|
rowSpanningCaches,
|
|
@@ -102,8 +110,11 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
102
110
|
sortedUnpinnedRowCount,
|
|
103
111
|
infiniteScrollSkeletonCount,
|
|
104
112
|
unpinnedRowsLength,
|
|
113
|
+
lastRenderedRowIndex,
|
|
105
114
|
rowMetaMap,
|
|
106
115
|
loadingOverlay,
|
|
116
|
+
ariaRowIndexOffset,
|
|
117
|
+
columnIndexMap,
|
|
107
118
|
}: GridVirtualRowsProps<R>) {
|
|
108
119
|
const skeletonColumns: GridColDef<R>[] = baseColumns.length > 0
|
|
109
120
|
? baseColumns
|
|
@@ -121,11 +132,12 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
121
132
|
})) as GridColDef<R>[];
|
|
122
133
|
})();
|
|
123
134
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
// Pinned rows render in GridPinnedRows; ids are already unique (useGridVisibleRows de-duplicates).
|
|
136
|
+
const centerRows = visibleRows.filter(item => !item.pinned);
|
|
137
|
+
// Placeholders stand in for the next page, after the last data row: only draw them once the
|
|
138
|
+
// render window reaches it, or they would follow the window into the middle of the data.
|
|
139
|
+
const showInfiniteSkeletons = paginationMode === 'infinite' && dataSourceLoading && sortedUnpinnedRowCount > 0
|
|
140
|
+
&& lastRenderedRowIndex >= unpinnedRowsLength - 1;
|
|
129
141
|
|
|
130
142
|
return (
|
|
131
143
|
<div
|
|
@@ -152,13 +164,16 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
152
164
|
/>
|
|
153
165
|
))
|
|
154
166
|
) : (
|
|
155
|
-
centerRows.map(({ row, rowIndex: actualIndex }) => (
|
|
167
|
+
centerRows.map(({ row, id, rowIndex: actualIndex }) => (
|
|
156
168
|
<Row<R>
|
|
157
|
-
key={
|
|
169
|
+
key={id}
|
|
158
170
|
row={row}
|
|
171
|
+
rowId={id}
|
|
159
172
|
columns={virtualColumns}
|
|
160
173
|
rowIndex={actualIndex}
|
|
161
|
-
|
|
174
|
+
ariaRowIndex={ariaRowIndexOffset !== undefined ? actualIndex + ariaRowIndexOffset : undefined}
|
|
175
|
+
columnIndexMap={columnIndexMap}
|
|
176
|
+
isSelected={selectedRowIds.has(id)}
|
|
162
177
|
checkboxSelection={checkboxSelection}
|
|
163
178
|
onRowClick={onRowClick}
|
|
164
179
|
onRowDoubleClick={onRowDoubleClick}
|
|
@@ -166,35 +181,36 @@ export function GridVirtualRows<R extends GridRowModel>({
|
|
|
166
181
|
onSelectionChange={onSelectionChange}
|
|
167
182
|
columnWidths={columnWidths}
|
|
168
183
|
pinnedColumns={pinnedColumns}
|
|
169
|
-
pinnedRows={pinnedRows}
|
|
170
184
|
hasDetailPanel={hasDetailPanel}
|
|
171
|
-
isDetailPanelExpanded={expandedRowIds.has(
|
|
172
|
-
detailPanelContent={getDetailPanelContent ? getDetailPanelContent
|
|
173
|
-
detailPanelHeight={getDetailPanelHeight
|
|
185
|
+
isDetailPanelExpanded={expandedRowIds.has(id)}
|
|
186
|
+
detailPanelContent={expandedRowIds.has(id) && getDetailPanelContent ? <DetailPanelContent<R> getContent={getDetailPanelContent} params={{ row, id, rowIndex: actualIndex }} /> : null}
|
|
187
|
+
detailPanelHeight={expandedRowIds.has(id) ? resolveDetailPanelHeight(callGetDetailPanelHeight(getDetailPanelHeight, { row, id, rowIndex: actualIndex })) : undefined}
|
|
188
|
+
onDetailPanelHeightChange={onDetailPanelHeightChange}
|
|
174
189
|
onDetailPanelToggle={onDetailPanelToggle}
|
|
175
190
|
pinCheckboxColumn={pinCheckboxColumn}
|
|
176
191
|
pinExpandColumn={pinExpandColumn}
|
|
177
192
|
rowReordering={rowReordering}
|
|
178
|
-
onDragStart={rowReorderHandlers.onDragStart}
|
|
193
|
+
onDragStart={rowReorderHandlers.canReorderRow(id) ? rowReorderHandlers.onDragStart : undefined}
|
|
179
194
|
onDragOver={rowReorderHandlers.onDragOver}
|
|
180
195
|
onDragEnd={rowReorderHandlers.onDragEnd}
|
|
181
196
|
onDrop={rowReorderHandlers.onDrop}
|
|
182
|
-
isDragging={rowReorderHandlers.draggedRowId ===
|
|
183
|
-
isDragOver={rowReorderHandlers.dragOverRowId ===
|
|
197
|
+
isDragging={rowReorderHandlers.draggedRowId === id}
|
|
198
|
+
isDragOver={rowReorderHandlers.dragOverRowId === id}
|
|
184
199
|
editingCell={editingHandlers.editingCell}
|
|
185
200
|
onEditStart={editingHandlers.startCellEdit}
|
|
186
201
|
onEditStop={editingHandlers.stopCellEdit}
|
|
187
202
|
onEditCellValueChange={editingHandlers.setEditCellValue}
|
|
188
|
-
|
|
203
|
+
isCellEditable={isCellEditable}
|
|
204
|
+
focusedCellField={focusedCell != null && focusedCell.id === id ? focusedCell.field : null}
|
|
189
205
|
colspanMap={colspanMap}
|
|
190
206
|
rowSpanningCaches={rowSpanningCaches}
|
|
191
207
|
rowHeight={rowHeight}
|
|
192
|
-
rowMeta={rowMetaMap.get(
|
|
208
|
+
rowMeta={rowMetaMap.get(id)}
|
|
193
209
|
/>
|
|
194
210
|
))
|
|
195
211
|
)}
|
|
196
212
|
|
|
197
|
-
{
|
|
213
|
+
{showInfiniteSkeletons && (
|
|
198
214
|
<div className="ogx__skeleton-group">
|
|
199
215
|
{Array.from({ length: infiniteScrollSkeletonCount }).map((_, i) => (
|
|
200
216
|
<Row<R>
|
|
@@ -10,23 +10,52 @@ export interface DetailPanelProps<R extends GridRowModel = GridRowModel> {
|
|
|
10
10
|
content: React.ReactNode;
|
|
11
11
|
height: GridDetailPanelHeight;
|
|
12
12
|
isExpanded: boolean;
|
|
13
|
+
/** Referenced by the expand cell's `aria-controls`. */
|
|
14
|
+
id?: string;
|
|
15
|
+
/** Number of grid columns the panel spans (`aria-colspan` of its cell). */
|
|
16
|
+
colSpan?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Called with the panel's rendered height when `height` is `'auto'`: once when it mounts and
|
|
19
|
+
* again whenever its size changes, so the grid can lay the rows below it out correctly.
|
|
20
|
+
*/
|
|
21
|
+
onHeightChange?: (rowId: GridRowId, height: number) => void;
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
export function DetailPanel<R extends GridRowModel = GridRowModel>(props: DetailPanelProps<R>) {
|
|
16
|
-
const { content, height, isExpanded } = props;
|
|
25
|
+
const { rowId, content, height, isExpanded, onHeightChange, id, colSpan } = props;
|
|
26
|
+
const panelRef = React.useRef<HTMLDivElement>(null);
|
|
27
|
+
const isAuto = height === 'auto';
|
|
28
|
+
|
|
29
|
+
// Measured before paint, so the grid re-lays out rows under the panel before they are shown.
|
|
30
|
+
React.useLayoutEffect(() => {
|
|
31
|
+
const el = panelRef.current;
|
|
32
|
+
if (!el || !isAuto || !isExpanded || !onHeightChange) return;
|
|
33
|
+
const report = () => {
|
|
34
|
+
// Not laid out (hidden ancestor, or no layout engine): keep the previous height.
|
|
35
|
+
if (el.getClientRects().length === 0) return;
|
|
36
|
+
onHeightChange(rowId, el.offsetHeight);
|
|
37
|
+
};
|
|
38
|
+
report();
|
|
39
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
40
|
+
const observer = new ResizeObserver(report);
|
|
41
|
+
observer.observe(el);
|
|
42
|
+
return () => observer.disconnect();
|
|
43
|
+
}, [isAuto, isExpanded, onHeightChange, rowId]);
|
|
17
44
|
|
|
18
45
|
if (!isExpanded) {
|
|
19
46
|
return null;
|
|
20
47
|
}
|
|
21
48
|
|
|
22
49
|
const style: React.CSSProperties = {
|
|
23
|
-
height:
|
|
24
|
-
overflow:
|
|
50
|
+
height: isAuto ? 'auto' : `${height}px`,
|
|
51
|
+
overflow: isAuto ? 'visible' : 'auto'
|
|
25
52
|
};
|
|
26
53
|
|
|
54
|
+
// A detail panel sits between rows of the grid, so it is exposed as a row with one cell
|
|
55
|
+
// spanning every column (a bare div is not allowed inside role="rowgroup").
|
|
27
56
|
return (
|
|
28
|
-
<div className="ogx__detail-panel" style={style}>
|
|
29
|
-
<div className="ogx__detail-panel-content">
|
|
57
|
+
<div ref={panelRef} className="ogx__detail-panel" style={style} role="row" id={id}>
|
|
58
|
+
<div className="ogx__detail-panel-content" role="gridcell" aria-colspan={colSpan}>
|
|
30
59
|
{content}
|
|
31
60
|
</div>
|
|
32
61
|
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CellErrorBoundary } from '../Cell/CellErrorBoundary';
|
|
3
|
+
import type { GridDetailPanelParams, GridRowModel } from '../../types';
|
|
4
|
+
|
|
5
|
+
interface DetailPanelContentProps<R extends GridRowModel> {
|
|
6
|
+
getContent: (params: GridDetailPanelParams<R>) => React.ReactNode;
|
|
7
|
+
params: GridDetailPanelParams<R>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Calls `getDetailPanelContent` while rendering, below an error boundary, so a throwing callback
|
|
12
|
+
* shows an error marker in that one panel instead of unmounting the grid. The row lists render it
|
|
13
|
+
* only for an expanded row, so the callback never runs for collapsed rows.
|
|
14
|
+
*/
|
|
15
|
+
export function DetailPanelContent<R extends GridRowModel>({ getContent, params }: DetailPanelContentProps<R>) {
|
|
16
|
+
return <CellErrorBoundary field="detail panel" renderFn={() => getContent(params)} resetKey={params.row} />;
|
|
17
|
+
}
|
|
@@ -141,6 +141,17 @@
|
|
|
141
141
|
padding-right: 28px;
|
|
142
142
|
cursor: pointer;
|
|
143
143
|
}
|
|
144
|
+
.ogx-filter__value-multiselect {
|
|
145
|
+
padding: 2px 4px;
|
|
146
|
+
cursor: pointer;
|
|
147
|
+
}
|
|
148
|
+
.ogx-filter__hidden-note {
|
|
149
|
+
padding: 6px var(--ogx-spacing-md);
|
|
150
|
+
border-bottom: var(--ogx-border-width-thin) solid var(--ogx-grid-border-color);
|
|
151
|
+
font-family: var(--ogx-font-family);
|
|
152
|
+
font-size: var(--ogx-font-size-xs);
|
|
153
|
+
color: var(--ogx-overlay-text, var(--ogx-color-gray-400));
|
|
154
|
+
}
|
|
144
155
|
.ogx-filter__row-clear {
|
|
145
156
|
display: inline-flex;
|
|
146
157
|
align-items: center;
|
|
@@ -159,8 +170,8 @@
|
|
|
159
170
|
transition: background 0.15s, color 0.15s;
|
|
160
171
|
}
|
|
161
172
|
.ogx-filter__row-clear:hover {
|
|
162
|
-
background: var(--ogx-
|
|
163
|
-
color: var(--ogx-
|
|
173
|
+
background: var(--ogx-overlay-item-danger-bg, rgba(239, 68, 68, 0.08));
|
|
174
|
+
color: var(--ogx-overlay-item-danger-text, #ef4444);
|
|
164
175
|
}
|
|
165
176
|
.ogx-filter__empty {
|
|
166
177
|
padding: var(--ogx-spacing-md);
|