@opencorestack/opengridx 3.6.0 → 3.7.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.
Files changed (74) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/ai.cjs +3 -3
  3. package/dist/ai.cjs.map +1 -1
  4. package/dist/ai.es.js +3 -3
  5. package/dist/ai.es.js.map +1 -1
  6. package/dist/index.d.ts +30 -0
  7. package/dist/opengridx.css +1 -1
  8. package/dist/opengridx.es.js +8756 -8244
  9. package/dist/opengridx.es.js.map +1 -1
  10. package/dist/opengridx.umd.js +15 -15
  11. package/dist/opengridx.umd.js.map +1 -1
  12. package/docs/API_REFERENCE.md +3 -2
  13. package/docs/architecture/datagrid-orchestration.md +46 -29
  14. package/docs/architecture/grid-row-meta.md +1 -1
  15. package/docs/customization/slots-api.md +1 -1
  16. package/docs/customization/theming.md +43 -0
  17. package/docs/features/virtualization.md +16 -2
  18. package/docs/performance.md +3 -1
  19. package/lib/components/AiAssistant/GridAiAssistantPanel.css +3 -1
  20. package/lib/components/Cell/Cell.css +4 -2
  21. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +2 -2
  22. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +3 -1
  23. package/lib/components/DataGrid/DataGrid.css +9 -12
  24. package/lib/components/DataGrid/DataGrid.tsx +163 -671
  25. package/lib/components/DataGrid/GridRoot.tsx +239 -0
  26. package/lib/components/DataGrid/GridViewport.tsx +314 -0
  27. package/lib/components/FilterPanel/FilterPanel.css +9 -8
  28. package/lib/components/Header/ColumnMenu.css +4 -3
  29. package/lib/components/Header/Header.css +5 -4
  30. package/lib/components/HeaderFilter/HeaderFilter.css +1 -1
  31. package/lib/components/ListView/ListView.css +4 -4
  32. package/lib/components/Pagination/Pagination.css +1 -1
  33. package/lib/components/Row/Row.css +2 -2
  34. package/lib/components/Toolbar/GridToolbar.css +8 -7
  35. package/lib/components/Toolbar/PivotPanel.css +3 -3
  36. package/lib/hooks/core/useGridDevWarnings.ts +3 -1
  37. package/lib/hooks/core/useGridEditPipeline.ts +55 -0
  38. package/lib/hooks/core/useGridRangeEditing.ts +129 -0
  39. package/lib/hooks/core/useGridRenderWindow.ts +66 -0
  40. package/lib/hooks/core/useGridScrollViewport.ts +42 -0
  41. package/lib/hooks/core/useGridSelectionAndStateApi.ts +61 -0
  42. package/lib/hooks/core/useGridToolbarStack.ts +49 -0
  43. package/lib/hooks/core/useGridViewApis.ts +46 -0
  44. package/lib/hooks/core/useLayout.ts +34 -24
  45. package/lib/styles/base/reset.css +5 -3
  46. package/lib/styles/base/variables.css +12 -0
  47. package/lib/theme/DataGridThemeProvider.tsx +15 -0
  48. package/lib/theme/__baseline__/default-theme.darwin.chromium.json +1503 -0
  49. package/lib/theme/__baseline__/default-theme.darwin.firefox.json +1503 -0
  50. package/lib/theme/__baseline__/default-theme.darwin.webkit.json +1503 -0
  51. package/lib/theme/types.ts +31 -0
  52. package/lib/types/ai.ts +109 -0
  53. package/lib/types/api.ts +169 -0
  54. package/lib/types/columns.ts +341 -0
  55. package/lib/types/dataSource.ts +40 -0
  56. package/lib/types/editing.ts +77 -0
  57. package/lib/types/export.ts +164 -0
  58. package/lib/types/filtering.ts +52 -0
  59. package/lib/types/grouping.ts +27 -0
  60. package/lib/types/index.ts +18 -1702
  61. package/lib/types/locale.ts +51 -0
  62. package/lib/types/props.ts +405 -0
  63. package/lib/types/rows.ts +132 -0
  64. package/lib/types/selection.ts +66 -0
  65. package/lib/types/slots.ts +56 -0
  66. package/lib/types/sorting.ts +25 -0
  67. package/lib/types/state.ts +35 -0
  68. package/lib/utils/export/exportShared.ts +22 -4
  69. package/lib/utils/export/index.ts +8 -4
  70. package/lib/utils/filtering/index.ts +8 -2
  71. package/lib/utils/parsing.ts +24 -1
  72. package/lib/utils/values.ts +31 -0
  73. package/llms.txt +1 -1
  74. package/package.json +4 -2
@@ -0,0 +1,239 @@
1
+ import React from 'react';
2
+ import { GridToolbarHostContext } from '../../hooks/core/gridToolbarHostContext';
3
+ import { GridToolbarSlot } from './GridToolbarSlot';
4
+ import { GridAiAssistantArea } from '../AiAssistant/GridAiAssistantArea';
5
+ import { GridStandaloneColumnPanel, type GridStandaloneColumnPanelProps } from './GridStandaloneColumnPanel';
6
+ import { GridListView, type GridListViewProps } from './GridListView';
7
+ import { GridViewport, type GridViewportProps } from './GridViewport';
8
+ import { GridCellSelectionStatsArea } from '../CellSelectionStats/GridCellSelectionStatsArea';
9
+ import { GridPaginationArea } from './GridPaginationArea';
10
+ import { GridLiveRegion } from './GridLiveRegion';
11
+ import { GridLoadingOverlay } from './GridLoadingOverlay';
12
+ import { GridErrorOverlay } from './GridErrorOverlay';
13
+ import { pickPaginationLocaleText } from '../../utils/pagination';
14
+ import type { DataGridProps, GridApi, GridColDef, GridRowModel } from '../../types';
15
+ import type { UseGridAiAssistantResult } from '../../hooks/features/useGridAiAssistant';
16
+ import type { useGridToolbarProps } from '../../hooks/core/useGridToolbarProps';
17
+
18
+ /**
19
+ * Named after the `DataGrid` values they carry. The viewport's props are passed through to
20
+ * `GridViewport`; the list view, footer and panels read some of them too.
21
+ */
22
+ export interface GridRootProps<R extends GridRowModel> extends Omit<GridViewportProps<R>, 'loadingOverlay' | 'noRowsOverlay'> {
23
+ containerRef: React.RefObject<HTMLDivElement | null>;
24
+ className: string;
25
+ autoHeight: boolean;
26
+ height: DataGridProps<R>['height'];
27
+ style: DataGridProps<R>['style'];
28
+ hasRowSpanning: boolean;
29
+ listView: boolean;
30
+ headerFilterHeight: number;
31
+ slots: DataGridProps<R>['slots'];
32
+ slotProps: DataGridProps<R>['slotProps'];
33
+ toolbarProps: ReturnType<typeof useGridToolbarProps>;
34
+ aiAssistantPanel: UseGridAiAssistantResult;
35
+ // Standalone columns panel
36
+ orderedColumns: GridColDef<R>[];
37
+ columnVisibilityModel: GridStandaloneColumnPanelProps<R>['columnVisibilityModel'];
38
+ disableColumnReorder: boolean;
39
+ handleColumnVisibilityModelChange: GridStandaloneColumnPanelProps<R>['onColumnVisibilityChange'];
40
+ moveColumn: GridStandaloneColumnPanelProps<R>['onColumnMove'];
41
+ resetColumnOrder: GridStandaloneColumnPanelProps<R>['onColumnOrderReset'];
42
+ // List view
43
+ listViewColumn: DataGridProps<R>['listViewColumn'];
44
+ allRenderableRows: R[];
45
+ dataRows: R[];
46
+ pagination: boolean;
47
+ effectivePaginationModel: GridListViewProps<R>['effectivePaginationModel'];
48
+ currentPage: number;
49
+ pageSizeOptions: number[];
50
+ columnsByField: GridListViewProps<R>['columnsByField'];
51
+ paginationRowCount: number;
52
+ handlePaginationModelChange: GridListViewProps<R>['onPaginationModelChange'];
53
+ onRowsScrollEnd: GridListViewProps<R>['onRowsScrollEnd'];
54
+ toggleExpansion: GridListViewProps<R>['onToggleExpansion'];
55
+ // Footer and status
56
+ showCellSelectionStats: boolean;
57
+ apiRef: React.MutableRefObject<GridApi>;
58
+ hasAggregation: boolean;
59
+ isHierarchyEnabled: boolean;
60
+ dataSourceError: unknown;
61
+ filteredRowCount: number;
62
+ /** Retries a failed dataSource load; undefined without a dataSource. */
63
+ onRetry: (() => void) | undefined;
64
+ }
65
+
66
+ /**
67
+ * The grid's root element and everything in it: the toolbar, the AI assistant panel, the standalone
68
+ * columns panel, the list view or the viewport, the cell selection stats, the footer slot or the pager,
69
+ * the live region and the loading and error overlays.
70
+ */
71
+ export function GridRoot<R extends GridRowModel>(props: GridRootProps<R>) {
72
+ const {
73
+ containerRef, className, autoHeight, height, style, hasRowSpanning, listView, headerFilterHeight, slots, slotProps,
74
+ toolbarProps, aiAssistantPanel, orderedColumns, columnVisibilityModel, disableColumnReorder,
75
+ handleColumnVisibilityModelChange, moveColumn, resetColumnOrder, listViewColumn, allRenderableRows, dataRows,
76
+ pagination, effectivePaginationModel, currentPage, pageSizeOptions, columnsByField,
77
+ paginationRowCount, handlePaginationModelChange,
78
+ onRowsScrollEnd, toggleExpansion, showCellSelectionStats, apiRef, hasAggregation, isHierarchyEnabled,
79
+ dataSourceError, filteredRowCount, onRetry,
80
+ ...viewport
81
+ } = props;
82
+ const {
83
+ effectiveRowHeight, headerHeight, showHeaderFilters, effectiveLoading, columnsPanel, columnGroupingModel, ariaLabel,
84
+ pinnedTopRows, pinnedBottomRows, sortedUnpinnedRowCount, effectiveNoRowsLabel, checkboxSelection, paginationMode,
85
+ localeText, getRowIdOf, rowSelectionEnabled, disableMultipleRowSelection, cellSelection, aggregationModel,
86
+ aggregationResult, filterModel, selectedRowIds, rowMetaMap, handleRowClick, onRowDoubleClick, handleSelectionChange,
87
+ } = viewport;
88
+
89
+ const NoRowsOverlaySlot = slots?.noRowsOverlay;
90
+ const LoadingOverlaySlot = slots?.loadingOverlay;
91
+ const loadingOverlay = LoadingOverlaySlot ? <LoadingOverlaySlot {...slotProps?.loadingOverlay} /> : undefined;
92
+ const noRowsOverlay = NoRowsOverlaySlot ? <NoRowsOverlaySlot {...slotProps?.noRowsOverlay} /> : undefined;
93
+ const FooterSlot = slots?.footer;
94
+ // Loading with rows on screen: the rows stay and an indicator runs over them. With no rows the
95
+ // body shows skeletons or the loading overlay instead, and infinite scroll appends skeleton rows.
96
+ const showLoadingOverRows = effectiveLoading && allRenderableRows.length > 0 && paginationMode !== 'infinite';
97
+ const ToolbarSlot = slots?.toolbar;
98
+
99
+ return (
100
+ <div
101
+ ref={containerRef}
102
+ className={['ogx', className, autoHeight && 'ogx--auto-height', !autoHeight && height === undefined && style?.height === undefined && 'ogx--fill', hasRowSpanning && 'ogx--row-spanning', listView && 'ogx--list-view'].filter(Boolean).join(' ')}
103
+ style={{
104
+ ...style,
105
+ height: height ?? style?.height,
106
+ '--ogx-row-height': `${effectiveRowHeight}px`,
107
+ '--ogx-header-height': `${headerHeight}px`,
108
+ ...(showHeaderFilters ? { '--ogx-header-filter-height': `${headerFilterHeight}px` } : undefined),
109
+ } as unknown as React.CSSProperties}
110
+ aria-busy={effectiveLoading}
111
+ >
112
+ {ToolbarSlot && toolbarProps && (
113
+ <GridToolbarHostContext.Provider value={columnsPanel.toolbarHost}>
114
+ <GridToolbarSlot component={ToolbarSlot} toolbarProps={toolbarProps} />
115
+ </GridToolbarHostContext.Provider>
116
+ )}
117
+
118
+ {aiAssistantPanel.panelProps && (
119
+ <GridAiAssistantArea
120
+ panelProps={aiAssistantPanel.panelProps}
121
+ slot={slots?.aiAssistantPanel}
122
+ slotProps={slotProps?.aiAssistantPanel}
123
+ />
124
+ )}
125
+
126
+ {/* Used by the column menu's Manage columns when no GridToolbar can show the panel */}
127
+ <GridStandaloneColumnPanel<R>
128
+ isOpen={columnsPanel.standalonePanelOpen}
129
+ containerRef={containerRef}
130
+ panelRef={columnsPanel.standalonePanelRef}
131
+ columns={orderedColumns}
132
+ columnVisibilityModel={columnVisibilityModel}
133
+ disableColumnReorder={disableColumnReorder}
134
+ onClose={columnsPanel.closeStandalonePanel}
135
+ onColumnVisibilityChange={handleColumnVisibilityModelChange}
136
+ onColumnMove={moveColumn}
137
+ onColumnOrderReset={resetColumnOrder}
138
+ columnGroupingModel={columnGroupingModel}
139
+ />
140
+
141
+
142
+ {listView && listViewColumn && (
143
+ <GridListView<R>
144
+ ariaLabel={ariaLabel}
145
+ allRenderableRows={allRenderableRows}
146
+ dataRowCount={dataRows.length}
147
+ pinnedTopRowCount={pinnedTopRows.length}
148
+ pinnedBottomRowCount={pinnedBottomRows.length}
149
+ unpinnedRowCount={sortedUnpinnedRowCount}
150
+ pagination={pagination}
151
+ effectivePaginationModel={effectivePaginationModel}
152
+ currentPage={currentPage}
153
+ pageSizeOptions={pageSizeOptions}
154
+ selectedRowIds={selectedRowIds}
155
+ listViewColumn={listViewColumn}
156
+ columnsByField={columnsByField}
157
+ rowMetaMap={rowMetaMap}
158
+ noRowsLabel={effectiveNoRowsLabel}
159
+ rowHeight={effectiveRowHeight}
160
+ checkboxSelection={checkboxSelection}
161
+ paginationMode={paginationMode}
162
+ serverRowCount={paginationRowCount}
163
+ paginationSlot={slots?.pagination}
164
+ paginationSlotProps={slotProps?.pagination}
165
+ localeText={pickPaginationLocaleText(localeText)}
166
+ onRowClick={handleRowClick}
167
+ onRowDoubleClick={onRowDoubleClick}
168
+ onSelectionChange={handleSelectionChange}
169
+ onPaginationModelChange={handlePaginationModelChange}
170
+ onRowsScrollEnd={onRowsScrollEnd}
171
+ getRowId={getRowIdOf}
172
+ multiselectable={rowSelectionEnabled && !disableMultipleRowSelection}
173
+ selectable={rowSelectionEnabled}
174
+ onToggleExpansion={toggleExpansion}
175
+ loading={effectiveLoading}
176
+ loadingOverlay={loadingOverlay}
177
+ noRowsOverlay={noRowsOverlay}
178
+ showPaginationControls={!FooterSlot}
179
+ />
180
+ )}
181
+
182
+ {/* Standard grid viewport (hidden when listView=true) */}
183
+ {!listView && <GridViewport<R> {...viewport} loadingOverlay={loadingOverlay} noRowsOverlay={noRowsOverlay} />}
184
+
185
+ {cellSelection.enabled && showCellSelectionStats && (
186
+ <GridCellSelectionStatsArea
187
+ stats={cellSelection.stats}
188
+ apiRef={apiRef}
189
+ slot={slots?.cellSelectionStats}
190
+ slotProps={slotProps?.cellSelectionStats}
191
+ />
192
+ )}
193
+
194
+ {FooterSlot && (
195
+ <FooterSlot
196
+ apiRef={apiRef}
197
+ aggregationModel={aggregationModel}
198
+ aggregationResult={hasAggregation ? aggregationResult : null}
199
+ rowCount={isHierarchyEnabled && paginationMode !== 'server' ? dataRows.length : paginationRowCount}
200
+ pagination={pagination}
201
+ paginationModel={effectivePaginationModel}
202
+ pageSizeOptions={pageSizeOptions}
203
+ onPaginationModelChange={handlePaginationModelChange}
204
+ {...slotProps?.footer}
205
+ />
206
+ )}
207
+
208
+ {!listView && !FooterSlot && pagination && (
209
+ <GridPaginationArea
210
+ paginationSlot={slots?.pagination}
211
+ paginationSlotProps={slotProps?.pagination}
212
+ page={currentPage}
213
+ paginationModel={effectivePaginationModel}
214
+ rowCount={paginationRowCount}
215
+ pageSizeOptions={pageSizeOptions}
216
+ onPaginationModelChange={handlePaginationModelChange}
217
+ localeText={localeText}
218
+ />
219
+ )}
220
+
221
+ <GridLiveRegion
222
+ loading={effectiveLoading}
223
+ error={dataSourceError}
224
+ noRowsLabel={effectiveNoRowsLabel}
225
+ filteredRowCount={filteredRowCount}
226
+ dataRowCount={dataRows.length}
227
+ filterModel={filterModel}
228
+ cellSelectionAnnouncement={cellSelection.announcement}
229
+ aiAssistantAnnouncement={aiAssistantPanel.announcement}
230
+ />
231
+
232
+ {showLoadingOverRows && (
233
+ <GridLoadingOverlay overlay={loadingOverlay} />
234
+ )}
235
+
236
+ <GridErrorOverlay error={dataSourceError} onRetry={onRetry} />
237
+ </div>
238
+ );
239
+ }
@@ -0,0 +1,314 @@
1
+ import React from 'react';
2
+ import { Header, type HeaderProps } from '../Header/Header';
3
+ import { GridPinnedRows, type GridPinnedRowsProps } from './GridPinnedRows';
4
+ import { GridVirtualRows, type GridVirtualRowsProps } from './GridVirtualRows';
5
+ import { GridEmptyState } from './GridEmptyState';
6
+ import { GridAggregationFooter } from './GridAggregationFooter';
7
+ import type { GridAggregationModel, GridAggregationResult, GridColDef, GridColumnPinning, GridRowId, GridRowModel } from '../../types';
8
+ import type { GridAriaRowLayout } from '../../utils/aria';
9
+ import type { UseGridCellSelectionReturn } from '../../hooks/core/useGridCellSelection';
10
+ import type { UseGridKeyboardNavigationReturn } from '../../hooks/core/useGridKeyboardNavigation';
11
+ import type { UseGridRowSelectionReturn } from '../../hooks/core/useGridRowSelection';
12
+ import type { UseGridColumnsPanelResult } from '../../hooks/core/useGridColumnsPanel';
13
+ import type { UseGridViewportResult } from '../../hooks/core/useGridViewport';
14
+ import type { GridVirtualizationResult } from '../../hooks/core/useGridVirtualization';
15
+ import type { UseColumnReorderReturn } from '../../hooks/useColumnReorder';
16
+ import type { GridSpanningResult } from '../../hooks/features/useGridSpanning';
17
+
18
+ /** The props the row renderers (top-pinned, centre and bottom-pinned rows) all take. */
19
+ export type GridRowRenderProps<R extends GridRowModel> = Pick<GridVirtualRowsProps<R>,
20
+ 'rowReordering' | 'columnIndexMap' | 'editingHandlers' | 'isCellEditable' | 'selectedRowIds' | 'checkboxSelection' |
21
+ 'onRowClick' | 'onRowDoubleClick' | 'onCellClick' | 'onSelectionChange' | 'columnWidths' | 'pinnedColumns' |
22
+ 'hasDetailPanel' | 'expandedRowIds' | 'getDetailPanelContent' | 'getDetailPanelHeight' | 'onDetailPanelToggle' |
23
+ 'onDetailPanelHeightChange' | 'pinCheckboxColumn' | 'pinExpandColumn' | 'focusedCell' | 'colspanMap' |
24
+ 'rowSpanningCaches' | 'rowHeight' | 'rowMetaMap' | 'cellRange' | 'cellRangeHasRowSpan' | 'fillHandle'>;
25
+
26
+ /** Named after the `DataGrid` values they carry. */
27
+ export interface GridViewportProps<R extends GridRowModel> {
28
+ // The viewport element
29
+ setViewportElement: UseGridViewportResult['setViewportElement'];
30
+ handleScroll: UseGridViewportResult['handleScroll'];
31
+ ariaLabel: string | undefined;
32
+ ariaRows: GridAriaRowLayout;
33
+ navigationColumnCount: number;
34
+ rowSelectionEnabled: boolean;
35
+ disableMultipleRowSelection: boolean;
36
+ cellSelection: UseGridCellSelectionReturn;
37
+ effectiveLoading: boolean;
38
+ viewportTabIndex: number;
39
+ setKeyboardMode: (on: boolean) => void;
40
+ handleMouseDownCapture: UseGridKeyboardNavigationReturn['handleMouseDownCapture'];
41
+ handleKeyDown: UseGridKeyboardNavigationReturn['handleKeyDown'];
42
+ handleFocus: UseGridKeyboardNavigationReturn['handleFocus'];
43
+ handleBlur: UseGridKeyboardNavigationReturn['handleBlur'];
44
+ effectivePinnedColumns: GridColumnPinning;
45
+ virtualization: GridVirtualizationResult<R>;
46
+ // Header
47
+ renderColumns: GridColDef<R>[];
48
+ renderedDataColumns: GridColDef<R>[];
49
+ columnGroupingModel: HeaderProps<R>['columnGroupingModel'];
50
+ checkboxSelection: boolean;
51
+ rowSelection: UseGridRowSelectionReturn;
52
+ sortModel: HeaderProps<R>['sortModel'];
53
+ handleSort: HeaderProps<R>['onSort'];
54
+ handleSortAdd: HeaderProps<R>['onSortAdd'];
55
+ multiSort: boolean;
56
+ handleColumnResize: HeaderProps<R>['onColumnResize'];
57
+ handleColumnAutosize: HeaderProps<R>['onColumnAutosize'];
58
+ columnWidths: Record<string, number>;
59
+ focusedCell: UseGridKeyboardNavigationReturn['focusedCell'];
60
+ columnIndexMap: Map<string, number>;
61
+ handleHeaderClick: HeaderProps<R>['onHeaderClick'];
62
+ headerReorderHandlers: UseColumnReorderReturn;
63
+ rowReordering: boolean;
64
+ hasDetailPanel: boolean;
65
+ pinCheckboxColumn: boolean;
66
+ pinExpandColumn: boolean;
67
+ aggregationModel: GridAggregationModel;
68
+ handleHideColumn: HeaderProps<R>['onHideColumn'];
69
+ columnsPanel: UseGridColumnsPanelResult;
70
+ handlePinColumn: HeaderProps<R>['onPinColumn'];
71
+ wrapHeaderText: boolean;
72
+ headerHeight: number;
73
+ showHeaderFilters: boolean;
74
+ filterModel: HeaderProps<R>['filterModel'];
75
+ handleFilterModelChange: HeaderProps<R>['onFilterModelChange'];
76
+ localeText: HeaderProps<R>['localeText'];
77
+ // Rows
78
+ editingHandlers: GridRowRenderProps<R>['editingHandlers'];
79
+ isCellEditable: GridRowRenderProps<R>['isCellEditable'];
80
+ selectedRowIds: GridRowRenderProps<R>['selectedRowIds'];
81
+ handleRowClick: GridRowRenderProps<R>['onRowClick'];
82
+ onRowDoubleClick: GridRowRenderProps<R>['onRowDoubleClick'];
83
+ handleCellClick: GridRowRenderProps<R>['onCellClick'];
84
+ handleSelectionChange: GridRowRenderProps<R>['onSelectionChange'];
85
+ expandedRowIds: GridRowRenderProps<R>['expandedRowIds'];
86
+ getDetailPanelContent: GridRowRenderProps<R>['getDetailPanelContent'];
87
+ getDetailPanelHeight: GridRowRenderProps<R>['getDetailPanelHeight'];
88
+ handleDetailPanelToggle: GridRowRenderProps<R>['onDetailPanelToggle'];
89
+ reportDetailPanelHeight: GridRowRenderProps<R>['onDetailPanelHeightChange'];
90
+ spanning: Pick<GridSpanningResult, 'colspanMap' | 'rowSpanningCaches'>;
91
+ rowMetaMap: GridRowRenderProps<R>['rowMetaMap'];
92
+ fillHandle: { handle: GridRowRenderProps<R>['fillHandle'] };
93
+ pinnedTopRows: R[];
94
+ pinnedBottomRows: R[];
95
+ pinnedRows: GridPinnedRowsProps<R>['pinnedRows'];
96
+ getRowIdOf: (row: R) => GridRowId;
97
+ showEmptyState: boolean;
98
+ effectiveNoRowsLabel: string;
99
+ noRowsOverlay: React.ReactNode;
100
+ loadingOverlay: React.ReactNode;
101
+ spanRowWindow: { renderContext: { lastRowIndex: number }; offsetTop: number };
102
+ visibleRows: GridVirtualRowsProps<R>['visibleRows'];
103
+ baseColumns: GridColDef<R>[];
104
+ viewportWidth: number;
105
+ rowReorderHandlers: GridVirtualRowsProps<R>['rowReorderHandlers'];
106
+ paginationMode: GridVirtualRowsProps<R>['paginationMode'];
107
+ dataSourceLoading: boolean;
108
+ sortedUnpinnedRowCount: number;
109
+ pageSize: number;
110
+ unpinnedRowsLength: number;
111
+ // Aggregation footer
112
+ showAggregationFooter: boolean;
113
+ aggregationResult: GridAggregationResult;
114
+ effectiveRowHeight: number;
115
+ isAggregationLoading: boolean;
116
+ }
117
+
118
+ /**
119
+ * The standard (non-list) view: the scrolling `role="grid"` viewport with the header, pinned rows, the
120
+ * empty state, the virtual centre rows and the aggregation footer.
121
+ */
122
+ export function GridViewport<R extends GridRowModel>(props: GridViewportProps<R>) {
123
+ const {
124
+ setViewportElement, handleScroll, ariaLabel, ariaRows, navigationColumnCount, rowSelectionEnabled,
125
+ disableMultipleRowSelection, cellSelection, effectiveLoading, viewportTabIndex, setKeyboardMode,
126
+ handleMouseDownCapture, handleKeyDown, handleFocus, handleBlur, effectivePinnedColumns, virtualization,
127
+ renderColumns, renderedDataColumns, columnGroupingModel, checkboxSelection, rowSelection, sortModel, handleSort,
128
+ handleSortAdd, multiSort, handleColumnResize, handleColumnAutosize, columnWidths, focusedCell, columnIndexMap,
129
+ handleHeaderClick, headerReorderHandlers, rowReordering, hasDetailPanel, pinCheckboxColumn, pinExpandColumn,
130
+ aggregationModel, handleHideColumn, columnsPanel, handlePinColumn, wrapHeaderText, headerHeight, showHeaderFilters,
131
+ filterModel, handleFilterModelChange, localeText, editingHandlers, isCellEditable, selectedRowIds, handleRowClick,
132
+ onRowDoubleClick, handleCellClick, handleSelectionChange, expandedRowIds, getDetailPanelContent, getDetailPanelHeight,
133
+ handleDetailPanelToggle, reportDetailPanelHeight, spanning, rowMetaMap, fillHandle, pinnedTopRows, pinnedBottomRows, pinnedRows,
134
+ getRowIdOf, showEmptyState, effectiveNoRowsLabel, noRowsOverlay, loadingOverlay, spanRowWindow, visibleRows,
135
+ baseColumns, viewportWidth, rowReorderHandlers, paginationMode, dataSourceLoading, sortedUnpinnedRowCount,
136
+ pageSize, unpinnedRowsLength, showAggregationFooter, aggregationResult, effectiveRowHeight, isAggregationLoading,
137
+ } = props;
138
+ const { allSelected, someSelected } = rowSelection;
139
+
140
+ // Props the row renderers (top-pinned, centre and bottom-pinned rows) all take.
141
+ const rowRenderProps: GridRowRenderProps<R> = {
142
+ rowReordering, columnIndexMap, editingHandlers, isCellEditable, selectedRowIds, checkboxSelection,
143
+ onRowClick: handleRowClick,
144
+ onRowDoubleClick,
145
+ onCellClick: handleCellClick,
146
+ onSelectionChange: handleSelectionChange,
147
+ columnWidths,
148
+ pinnedColumns: effectivePinnedColumns,
149
+ hasDetailPanel, expandedRowIds, getDetailPanelContent, getDetailPanelHeight,
150
+ onDetailPanelToggle: handleDetailPanelToggle,
151
+ onDetailPanelHeightChange: reportDetailPanelHeight,
152
+ pinCheckboxColumn, pinExpandColumn, focusedCell,
153
+ colspanMap: spanning.colspanMap,
154
+ rowSpanningCaches: spanning.rowSpanningCaches,
155
+ rowHeight: effectiveRowHeight,
156
+ rowMetaMap,
157
+ cellRange: cellSelection.displayRange,
158
+ cellRangeHasRowSpan: cellSelection.hasRowSpan,
159
+ fillHandle: fillHandle.handle,
160
+ };
161
+
162
+ return (
163
+ <div
164
+ ref={setViewportElement}
165
+ className="ogx__viewport"
166
+ onScroll={handleScroll}
167
+ role="grid"
168
+ aria-label={ariaLabel || 'Data grid'}
169
+ aria-rowcount={ariaRows.rowCount}
170
+ aria-colcount={navigationColumnCount}
171
+ aria-multiselectable={(rowSelectionEnabled && !disableMultipleRowSelection) || cellSelection.enabled}
172
+ aria-busy={effectiveLoading}
173
+ tabIndex={viewportTabIndex}
174
+ onKeyDownCapture={() => { setKeyboardMode(true); }}
175
+ onMouseDownCapture={handleMouseDownCapture}
176
+ onMouseDown={cellSelection.handleMouseDown}
177
+ onClickCapture={cellSelection.handleClickCapture}
178
+ onKeyDown={handleKeyDown}
179
+ onFocus={handleFocus}
180
+ onBlur={handleBlur}
181
+ >
182
+ <div
183
+ className="ogx__content"
184
+ style={{
185
+ // With right-pinned columns the content spans at least the viewport, so they sit at its right edge.
186
+ width: effectivePinnedColumns?.right?.length
187
+ ? `max(100%, ${virtualization.totalWidth}px)`
188
+ : virtualization.totalWidth
189
+ }}
190
+ role="presentation"
191
+ >
192
+ {/* Header and top-pinned rows stick together, so pinned rows sit below every header row. */}
193
+ <div className="ogx__sticky-top" role="presentation">
194
+ <Header
195
+ columns={renderColumns}
196
+ allColumns={renderedDataColumns}
197
+ columnGroupingModel={columnGroupingModel}
198
+ checkboxSelection={checkboxSelection}
199
+ allSelected={allSelected}
200
+ someSelected={someSelected}
201
+ onSelectAll={rowSelection.selectAll}
202
+ sortModel={sortModel}
203
+ onSort={handleSort}
204
+ onSortAdd={handleSortAdd}
205
+ multiSort={multiSort}
206
+
207
+ onColumnResize={handleColumnResize}
208
+ onColumnAutosize={handleColumnAutosize}
209
+ columnWidths={columnWidths}
210
+ pinnedColumns={effectivePinnedColumns}
211
+
212
+ focusedCell={focusedCell}
213
+ columnIndexMap={columnIndexMap}
214
+ onHeaderClick={handleHeaderClick}
215
+ onDragStart={headerReorderHandlers.onDragStart}
216
+ onDragOver={headerReorderHandlers.onDragOver}
217
+ onDragEnd={headerReorderHandlers.onDragEnd}
218
+ onDrop={headerReorderHandlers.onDrop}
219
+ draggedColumn={headerReorderHandlers.draggedColumn}
220
+ dragOverColumn={headerReorderHandlers.dragOverColumn}
221
+ rowReordering={rowReordering}
222
+ hasDetailPanel={hasDetailPanel}
223
+ pinCheckboxColumn={pinCheckboxColumn}
224
+ pinExpandColumn={pinExpandColumn}
225
+ aggregationModel={aggregationModel}
226
+ onHideColumn={handleHideColumn}
227
+ onManageColumns={columnsPanel.openColumnsPanel}
228
+ onPinColumn={handlePinColumn}
229
+ wrapHeaderText={wrapHeaderText}
230
+ headerHeight={headerHeight}
231
+ headerFilters={showHeaderFilters}
232
+ filterModel={filterModel}
233
+ onFilterModelChange={handleFilterModelChange}
234
+ onOpenFilterPanel={columnsPanel.openFiltersPanel}
235
+ localeText={localeText}
236
+ />
237
+
238
+ <GridPinnedRows<R>
239
+ {...rowRenderProps}
240
+ rows={pinnedTopRows}
241
+ position="top"
242
+ ariaRowIndexBase={ariaRows.topBase}
243
+ columns={renderColumns}
244
+ pinnedRows={pinnedRows}
245
+ getRowId={getRowIdOf}
246
+ />
247
+ </div>
248
+
249
+ {/* Empty State Overlay (Standard View) — showing after header */}
250
+ {showEmptyState && (
251
+ <GridEmptyState
252
+ noRowsLabel={effectiveNoRowsLabel}
253
+ width={virtualization.totalWidth}
254
+ overlay={noRowsOverlay}
255
+ />
256
+ )}
257
+
258
+ <GridVirtualRows<R>
259
+ {...rowRenderProps}
260
+ virtualContainerHeight={virtualization.virtualContainerHeight}
261
+ offsetTop={spanRowWindow.offsetTop}
262
+ effectiveLoading={effectiveLoading}
263
+ visibleRows={visibleRows}
264
+ baseColumns={baseColumns}
265
+ virtualColumns={renderColumns}
266
+ viewportWidth={viewportWidth}
267
+ rowReorderHandlers={rowReorderHandlers}
268
+ paginationMode={paginationMode}
269
+ dataSourceLoading={dataSourceLoading}
270
+ sortedUnpinnedRowCount={sortedUnpinnedRowCount}
271
+ infiniteScrollSkeletonCount={Math.min(pageSize, 20)}
272
+ unpinnedRowsLength={unpinnedRowsLength}
273
+ lastRenderedRowIndex={spanRowWindow.renderContext.lastRowIndex}
274
+ ariaRowIndexOffset={ariaRows.centerOffset}
275
+ loadingOverlay={loadingOverlay}
276
+ />
277
+
278
+ {/* Bottom-pinned rows and the aggregation footer stick together, footer last. */}
279
+ <div className="ogx__sticky-bottom" role="presentation">
280
+ <GridPinnedRows<R>
281
+ {...rowRenderProps}
282
+ rows={pinnedBottomRows}
283
+ position="bottom"
284
+ rowIndexOffset={ariaRows.bottomRowIndexOffset}
285
+ ariaRowIndexBase={ariaRows.bottomBase}
286
+ columns={renderColumns}
287
+ pinnedRows={pinnedRows}
288
+ getRowId={getRowIdOf}
289
+ />
290
+
291
+ {/* Aggregation Footer Row — laid out from the same virtual columns as the rows, so
292
+ hidden, pinned and scrolled columns line up. Off in pivot mode (hasAggregation). */}
293
+ {showAggregationFooter && (
294
+ <GridAggregationFooter
295
+ columns={virtualization.virtualColumns as unknown as GridColDef[]}
296
+ aggregationModel={aggregationModel}
297
+ aggregationResult={aggregationResult}
298
+ columnWidths={columnWidths}
299
+ rowHeight={effectiveRowHeight}
300
+ checkboxSelection={checkboxSelection}
301
+ hasDetailPanel={hasDetailPanel}
302
+ rowReordering={rowReordering}
303
+ pinCheckboxColumn={pinCheckboxColumn}
304
+ pinExpandColumn={pinExpandColumn}
305
+ pinnedColumns={effectivePinnedColumns}
306
+ loading={isAggregationLoading}
307
+ />
308
+ )}
309
+ </div>
310
+
311
+ </div>
312
+ </div>
313
+ );
314
+ }
@@ -7,7 +7,7 @@
7
7
  background: var(--ogx-grid-header-background);
8
8
  }
9
9
  .ogx-filter__logic-label {
10
- font-family: var(--ogx-font-family);
10
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family));
11
11
  font-size: var(--ogx-font-size-xs);
12
12
  font-weight: 500;
13
13
  color: var(--ogx-grid-header-text);
@@ -64,9 +64,9 @@
64
64
  }
65
65
  .ogx-filter__col-label {
66
66
  flex: 0 0 140px;
67
- font-family: var(--ogx-font-family);
68
- font-size: var(--ogx-font-size-xs);
69
- font-weight: 500;
67
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family));
68
+ font-size: var(--ogx-overlay-font-size, var(--ogx-font-size-xs));
69
+ font-weight: var(--ogx-overlay-font-weight, 500);
70
70
  color: var(--ogx-grid-row-text);
71
71
  overflow: hidden;
72
72
  text-overflow: ellipsis;
@@ -113,8 +113,9 @@
113
113
  padding: 4px 8px;
114
114
  border: var(--ogx-border-width-thin) solid var(--ogx-border-color);
115
115
  border-radius: var(--ogx-border-radius-sm);
116
- font-family: var(--ogx-font-family);
117
- font-size: var(--ogx-font-size-xs);
116
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family));
117
+ font-size: var(--ogx-overlay-font-size, var(--ogx-font-size-xs));
118
+ font-weight: var(--ogx-overlay-font-weight, 400);
118
119
  line-height: revert;
119
120
  background: var(--ogx-grid-background);
120
121
  color: var(--ogx-grid-row-text);
@@ -162,7 +163,7 @@
162
163
  .ogx-filter__hidden-note {
163
164
  padding: 6px var(--ogx-spacing-md);
164
165
  border-bottom: var(--ogx-border-width-thin) solid var(--ogx-grid-border-color);
165
- font-family: var(--ogx-font-family);
166
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family));
166
167
  font-size: var(--ogx-font-size-xs);
167
168
  color: var(--ogx-overlay-text, var(--ogx-color-gray-400));
168
169
  }
@@ -189,7 +190,7 @@
189
190
  }
190
191
  .ogx-filter__empty {
191
192
  padding: var(--ogx-spacing-md);
192
- font-family: var(--ogx-font-family);
193
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family));
193
194
  font-size: var(--ogx-font-size-sm);
194
195
  color: var(--ogx-overlay-text, var(--ogx-color-gray-400));
195
196
  opacity: 0.7;
@@ -9,7 +9,7 @@
9
9
  }
10
10
  .ogx-menu-section-label {
11
11
  padding: 6px 14px 2px;
12
- font-family: var(--ogx-font-family, system-ui, sans-serif);
12
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family, system-ui, sans-serif));
13
13
  font-size: 11px;
14
14
  font-weight: 600;
15
15
  text-transform: uppercase;
@@ -25,8 +25,9 @@
25
25
  border: none;
26
26
  background: transparent;
27
27
  cursor: pointer;
28
- font-family: var(--ogx-font-family, system-ui, sans-serif);
29
- font-size: var(--ogx-font-size-xs);
28
+ font-family: var(--ogx-overlay-font-family, var(--ogx-font-family, system-ui, sans-serif));
29
+ font-size: var(--ogx-overlay-font-size, var(--ogx-font-size-xs));
30
+ font-weight: var(--ogx-overlay-font-weight, 400);
30
31
  color: var(--ogx-overlay-text, #334155);
31
32
  text-align: start;
32
33
  gap: 6px;
@@ -12,7 +12,7 @@
12
12
  display: flex;
13
13
  align-items: stretch;
14
14
  background: var(--ogx-grid-header-background);
15
- border-bottom: 1px solid var(--ogx-grid-border-color);
15
+ border-bottom: var(--ogx-grid-row-divider-width) solid var(--ogx-grid-row-divider-color, var(--ogx-grid-border-color));
16
16
  min-height: var(--ogx-header-height);
17
17
  box-shadow: var(--ogx-shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.03));
18
18
  }
@@ -25,10 +25,11 @@
25
25
  .ogx__header-cell {
26
26
  display: flex;
27
27
  align-items: center;
28
- padding: var(--ogx-grid-cell-padding-y) var(--ogx-grid-cell-padding-x);
28
+ padding: var(--ogx-grid-cell-padding-y) var(--ogx-grid-header-padding-x, var(--ogx-grid-cell-padding-x));
29
29
  position: relative;
30
+ font-family: var(--ogx-grid-header-font-family, inherit);
30
31
  font-size: var(--ogx-grid-header-font-size);
31
- font-weight: 600;
32
+ font-weight: var(--ogx-grid-header-font-weight, 600);
32
33
  color: var(--ogx-grid-header-text);
33
34
  overflow: hidden;
34
35
  box-sizing: border-box;
@@ -42,7 +43,7 @@
42
43
  inset-inline-end: 0;
43
44
  top: 25%;
44
45
  height: 50%;
45
- width: 1px;
46
+ width: var(--ogx-grid-header-column-divider-width);
46
47
  background-color: var(--ogx-color-gray-300);
47
48
  opacity: 0.5;
48
49
  }
@@ -7,7 +7,7 @@
7
7
  height: var(--ogx-header-filter-height, 40px);
8
8
  min-height: var(--ogx-header-filter-height, 40px);
9
9
  background: var(--ogx-grid-header-background);
10
- border-bottom: 1px solid var(--ogx-grid-border-color);
10
+ border-bottom: var(--ogx-grid-row-divider-width) solid var(--ogx-grid-row-divider-color, var(--ogx-grid-border-color));
11
11
  }
12
12
  .ogx__header-filter-cell {
13
13
  display: flex;