@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.
Files changed (225) hide show
  1. package/CHANGELOG.md +399 -0
  2. package/README.md +95 -72
  3. package/dist/index.d.ts +590 -131
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +7891 -5631
  6. package/dist/opengridx.es.js.map +1 -1
  7. package/dist/opengridx.umd.js +17 -23
  8. package/dist/opengridx.umd.js.map +1 -1
  9. package/docs/API_REFERENCE.md +348 -166
  10. package/docs/README.md +6 -1
  11. package/docs/architecture/datagrid-orchestration.md +81 -0
  12. package/docs/architecture/grid-row-meta.md +102 -0
  13. package/docs/architecture/use-grid-columns.md +128 -0
  14. package/docs/architecture/use-grid-controlled-state.md +157 -0
  15. package/docs/architecture/use-grid-row-pipeline.md +120 -0
  16. package/docs/architecture/use-grid-scroll-sync.md +111 -0
  17. package/docs/architecture/use-grid-state-snapshot.md +90 -0
  18. package/docs/architecture/use-grid-virtualization.md +115 -0
  19. package/docs/architecture/use-grid-visible-rows.md +85 -0
  20. package/docs/assets/banner-alt.png +0 -0
  21. package/docs/assets/banner.png +0 -0
  22. package/docs/assets/hero.png +0 -0
  23. package/docs/assets/logo.png +0 -0
  24. package/docs/components/aggregation-footer.md +15 -10
  25. package/docs/components/cell.md +16 -6
  26. package/docs/components/column-group-header.md +55 -28
  27. package/docs/components/column-resize.md +17 -13
  28. package/docs/components/column-visibility.md +6 -7
  29. package/docs/components/datagrid.md +15 -13
  30. package/docs/components/empty-state.md +2 -1
  31. package/docs/components/error-overlay.md +12 -7
  32. package/docs/components/filter-panel.md +28 -8
  33. package/docs/components/header.md +28 -9
  34. package/docs/components/pagination.md +14 -8
  35. package/docs/components/row.md +21 -5
  36. package/docs/components/toolbar.md +27 -9
  37. package/docs/components/tooltip.md +16 -7
  38. package/docs/customization/slots-api.md +90 -38
  39. package/docs/customization/theming.md +66 -18
  40. package/docs/features/aggregation-pivot.md +75 -9
  41. package/docs/features/cell-spanning.md +80 -125
  42. package/docs/features/clipboard.md +41 -16
  43. package/docs/features/custom-pagination.md +38 -18
  44. package/docs/features/data-source.md +36 -13
  45. package/docs/features/editing-reordering.md +125 -16
  46. package/docs/features/export-guide.md +126 -39
  47. package/docs/features/filtering.md +44 -6
  48. package/docs/features/infinite-scroll.md +54 -46
  49. package/docs/features/keyboard-navigation.md +83 -0
  50. package/docs/features/list-view.md +58 -5
  51. package/docs/features/loading-states.md +18 -20
  52. package/docs/features/master-detail.md +18 -5
  53. package/docs/features/pdf-export.md +72 -19
  54. package/docs/features/pinning.md +24 -5
  55. package/docs/features/selection.md +50 -10
  56. package/docs/features/sorting-pagination.md +64 -14
  57. package/docs/features/state-persistence.md +78 -10
  58. package/docs/features/toolbar-customization.md +26 -17
  59. package/docs/features/tree-data-grouping.md +36 -8
  60. package/docs/features/virtualization.md +37 -14
  61. package/docs/migration/v1-to-v2.md +115 -0
  62. package/docs/migration/v2-to-v3.md +1233 -0
  63. package/docs/performance.md +124 -0
  64. package/docs/roadmap.md +5 -5
  65. package/lib/components/Cell/Cell.css +17 -1
  66. package/lib/components/Cell/Cell.tsx +129 -57
  67. package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
  68. package/lib/components/Cell/GridEditInputCell.tsx +114 -41
  69. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
  70. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
  71. package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
  72. package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
  73. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
  74. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
  75. package/lib/components/DataGrid/DataGrid.css +85 -12
  76. package/lib/components/DataGrid/DataGrid.tsx +582 -876
  77. package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
  78. package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
  79. package/lib/components/DataGrid/GridListView.tsx +165 -24
  80. package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
  81. package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
  82. package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
  83. package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
  84. package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
  85. package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
  86. package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
  87. package/lib/components/DetailPanel/DetailPanel.css +0 -6
  88. package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
  89. package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
  90. package/lib/components/FilterPanel/FilterPanel.css +13 -2
  91. package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
  92. package/lib/components/Header/ColumnMenu.tsx +91 -39
  93. package/lib/components/Header/Header.css +6 -3
  94. package/lib/components/Header/Header.tsx +181 -160
  95. package/lib/components/ListView/ListView.css +25 -0
  96. package/lib/components/ListView/ListViewRow.tsx +76 -10
  97. package/lib/components/Pagination/Pagination.tsx +15 -7
  98. package/lib/components/Row/Row.tsx +177 -77
  99. package/lib/components/Toolbar/GlobalSearch.css +6 -3
  100. package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
  101. package/lib/components/Toolbar/GridToolbar.css +12 -11
  102. package/lib/components/Toolbar/GridToolbar.tsx +133 -89
  103. package/lib/components/Toolbar/PivotPanel.tsx +89 -49
  104. package/lib/components/Tooltip/Tooltip.css +20 -0
  105. package/lib/components/Tooltip/Tooltip.tsx +68 -35
  106. package/lib/components/ui/Button.tsx +3 -0
  107. package/lib/components/ui/Checkbox.tsx +2 -1
  108. package/lib/components/ui/ExpandIcon.css +0 -9
  109. package/lib/components/ui/ExpandIcon.tsx +4 -1
  110. package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
  111. package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
  112. package/lib/hooks/core/useDataGrid.ts +115 -173
  113. package/lib/hooks/core/useGridAggregationApi.ts +72 -0
  114. package/lib/hooks/core/useGridApiMethods.ts +115 -0
  115. package/lib/hooks/core/useGridApiRef.ts +19 -4
  116. package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
  117. package/lib/hooks/core/useGridAriaRows.ts +38 -0
  118. package/lib/hooks/core/useGridClipboardApi.ts +38 -0
  119. package/lib/hooks/core/useGridColumnLookup.ts +30 -0
  120. package/lib/hooks/core/useGridColumns.tsx +279 -76
  121. package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
  122. package/lib/hooks/core/useGridControlledState.ts +99 -6
  123. package/lib/hooks/core/useGridDetailPanel.ts +51 -0
  124. package/lib/hooks/core/useGridDevWarnings.ts +104 -5
  125. package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
  126. package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
  127. package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
  128. package/lib/hooks/core/useGridHierarchy.ts +81 -0
  129. package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
  130. package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
  131. package/lib/hooks/core/useGridPageCorrection.ts +55 -0
  132. package/lib/hooks/core/useGridRowIdOf.ts +32 -0
  133. package/lib/hooks/core/useGridRowInteractions.ts +81 -0
  134. package/lib/hooks/core/useGridRowPipeline.ts +69 -39
  135. package/lib/hooks/core/useGridRowSelection.ts +91 -0
  136. package/lib/hooks/core/useGridScrollSync.ts +80 -32
  137. package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
  138. package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
  139. package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
  140. package/lib/hooks/core/useGridToolbarProps.ts +97 -0
  141. package/lib/hooks/core/useGridViewport.ts +38 -0
  142. package/lib/hooks/core/useGridViewportSize.ts +26 -0
  143. package/lib/hooks/core/useGridVisibleRows.ts +17 -5
  144. package/lib/hooks/core/useLayout.ts +215 -195
  145. package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
  146. package/lib/hooks/core/useStableColumns.ts +34 -0
  147. package/lib/hooks/features/useAggregation.ts +82 -51
  148. package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
  149. package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
  150. package/lib/hooks/features/useGridClipboard.ts +133 -73
  151. package/lib/hooks/features/useGridDataSource.ts +354 -128
  152. package/lib/hooks/features/useGridEditing.ts +211 -51
  153. package/lib/hooks/features/useGridPivot.ts +92 -0
  154. package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
  155. package/lib/hooks/features/useGridSpanning.ts +410 -306
  156. package/lib/hooks/features/usePivot.ts +0 -0
  157. package/lib/hooks/features/useServerTreeChildren.ts +50 -0
  158. package/lib/hooks/useColumnReorder.ts +38 -44
  159. package/lib/hooks/useRowGrouping.ts +298 -160
  160. package/lib/hooks/useRowReorder.ts +62 -31
  161. package/lib/hooks/useTreeData.ts +287 -220
  162. package/lib/index.ts +28 -2
  163. package/lib/state/index.ts +1 -0
  164. package/lib/state/types.ts +7 -1
  165. package/lib/state/useGridStateStorage.ts +88 -29
  166. package/lib/theme/DataGridThemeProvider.tsx +143 -38
  167. package/lib/theme/gridThemeContext.ts +5 -0
  168. package/lib/theme/index.ts +5 -0
  169. package/lib/theme/presets.ts +5 -4
  170. package/lib/theme/types.ts +40 -2
  171. package/lib/types/index.ts +313 -116
  172. package/lib/utils/aggregation/index.ts +119 -24
  173. package/lib/utils/aria/index.ts +40 -0
  174. package/lib/utils/columnGroups.ts +110 -0
  175. package/lib/utils/columnLookup.ts +33 -0
  176. package/lib/utils/columnWidth.ts +22 -0
  177. package/lib/utils/dataSource/index.ts +46 -0
  178. package/lib/utils/detailPanel.ts +66 -0
  179. package/lib/utils/dragData.ts +44 -0
  180. package/lib/utils/editing/index.ts +30 -0
  181. package/lib/utils/export/exportShared.ts +197 -0
  182. package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
  183. package/lib/utils/export/exportToPdf.ts +143 -92
  184. package/lib/utils/export/groupLabel.ts +14 -0
  185. package/lib/utils/export/index.ts +278 -272
  186. package/lib/utils/export/pdfText.ts +50 -0
  187. package/lib/utils/filtering/index.ts +277 -148
  188. package/lib/utils/focus/index.ts +115 -0
  189. package/lib/utils/gridApiRows.ts +35 -0
  190. package/lib/utils/gridModes.ts +77 -0
  191. package/lib/utils/grouping/groupedExportRows.ts +91 -0
  192. package/lib/utils/pagination/index.ts +56 -0
  193. package/lib/utils/pinning/index.ts +77 -33
  194. package/lib/utils/pivot/index.ts +368 -0
  195. package/lib/utils/scroll/index.ts +55 -5
  196. package/lib/utils/scroll/scrollEnd.ts +18 -0
  197. package/lib/utils/sorting/index.ts +154 -30
  198. package/lib/utils/syntheticRows.ts +12 -0
  199. package/lib/utils/values.ts +206 -0
  200. package/lib/utils/viewport.ts +14 -0
  201. package/llms.txt +261 -164
  202. package/package.json +13 -5
  203. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
  204. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
  205. package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
  206. package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
  207. package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
  208. package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
  209. package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
  210. package/lib/components/Header/Header.test.tsx +0 -135
  211. package/lib/components/Pagination/Pagination.test.tsx +0 -64
  212. package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
  213. package/lib/hooks/core/useGridControlledState.test.ts +0 -86
  214. package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
  215. package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
  216. package/lib/hooks/features/useAggregation.test.ts +0 -175
  217. package/lib/hooks/features/usePivot.test.ts +0 -202
  218. package/lib/hooks/useRowGrouping.test.ts +0 -298
  219. package/lib/hooks/useTreeData.test.ts +0 -83
  220. package/lib/utils/aggregation/aggregation.test.ts +0 -16
  221. package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
  222. package/lib/utils/export/exportToPdf.test.ts +0 -120
  223. package/lib/utils/export/groupedExport.test.ts +0 -144
  224. package/lib/utils/filtering/filtering.test.ts +0 -178
  225. package/lib/utils/sorting/sorting.test.ts +0 -88
@@ -1,6 +1,7 @@
1
1
  import React, { useRef, useEffect, useState } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
  import type { GridColDef, GridRowModel, GridSortDirection, GridColumnPinning } from '../../types';
4
+ import { getViewportWidth, getViewportHeight } from '../../utils/viewport';
4
5
 
5
6
 
6
7
  interface ColumnMenuProps {
@@ -116,8 +117,41 @@ function UnpinIcon() {
116
117
  );
117
118
  }
118
119
 
120
+ const menuItemsOf = (menu: HTMLElement | null): HTMLElement[] =>
121
+ menu ? Array.from(menu.querySelectorAll<HTMLElement>('[role="menuitem"], [role="menuitemradio"]')) : [];
122
+
119
123
  export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedColumns, onManageColumns, onClose, anchorEl }: ColumnMenuProps) {
120
124
  const menuRef = useRef<HTMLDivElement>(null);
125
+
126
+ // Closes the menu; when it was left from the keyboard or by choosing an item, focus goes back
127
+ // to the header cell that owns it (WAI-ARIA menu button pattern).
128
+ const close = React.useCallback((restoreFocus: boolean) => {
129
+ if (restoreFocus) {
130
+ const headerCell = anchorEl?.closest<HTMLElement>('[role="columnheader"]') ?? anchorEl;
131
+ headerCell?.focus({ preventScroll: true });
132
+ }
133
+ onClose();
134
+ }, [anchorEl, onClose]);
135
+
136
+ const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
137
+ const items = menuItemsOf(menuRef.current);
138
+ const current = items.indexOf(event.target as HTMLElement);
139
+ let next = -1;
140
+ if (event.key === 'ArrowDown') next = current + 1 >= items.length ? 0 : current + 1;
141
+ else if (event.key === 'ArrowUp') next = current <= 0 ? items.length - 1 : current - 1;
142
+ else if (event.key === 'Home') next = 0;
143
+ else if (event.key === 'End') next = items.length - 1;
144
+ else if (event.key === 'Tab') {
145
+ event.preventDefault();
146
+ close(true);
147
+ return;
148
+ } else {
149
+ return;
150
+ }
151
+ event.preventDefault();
152
+ items[next]?.focus();
153
+ };
154
+
121
155
  const [style, setStyle] = useState<React.CSSProperties>({ position: 'fixed', zIndex: 1300, top: 0, left: 0, visibility: 'hidden' });
122
156
 
123
157
  React.useLayoutEffect(() => {
@@ -125,8 +159,8 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
125
159
 
126
160
  const anchorRect = anchorEl.getBoundingClientRect();
127
161
  const menuRect = menuRef.current.getBoundingClientRect();
128
- const vw = window.innerWidth;
129
- const vh = window.innerHeight;
162
+ const vw = getViewportWidth();
163
+ const vh = getViewportHeight();
130
164
  const margin = 8;
131
165
 
132
166
  let left = anchorRect.left;
@@ -143,6 +177,12 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
143
177
  setStyle({ position: 'fixed', zIndex: 1300, top, left, visibility: 'visible' });
144
178
  }, [anchorEl]);
145
179
 
180
+ // Move focus into the menu once it is positioned (a visibility:hidden element cannot take focus).
181
+ const isPositioned = style.visibility === 'visible';
182
+ React.useLayoutEffect(() => {
183
+ if (isPositioned) menuItemsOf(menuRef.current)[0]?.focus({ preventScroll: true });
184
+ }, [isPositioned]);
185
+
146
186
 
147
187
  useEffect(() => {
148
188
  function handleClickOutside(event: MouseEvent) {
@@ -152,7 +192,7 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
152
192
  ) onClose();
153
193
  }
154
194
  function handleKey(e: KeyboardEvent) {
155
- if (e.key === 'Escape') onClose();
195
+ if (e.key === 'Escape') close(true);
156
196
  }
157
197
  document.addEventListener('mousedown', handleClickOutside);
158
198
  document.addEventListener('keydown', handleKey);
@@ -165,11 +205,16 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
165
205
  window.removeEventListener('scroll', onClose, true);
166
206
  window.removeEventListener('resize', onClose);
167
207
  };
168
- }, [anchorEl, onClose]);
208
+ }, [anchorEl, onClose, close]);
169
209
 
170
- const handleSort = (direction: GridSortDirection) => { onSort?.(colDef.field, direction); onClose(); };
171
- const handleHide = () => { onHide?.(colDef.field); onClose(); };
172
- const handlePin = (side: 'left' | 'right' | null) => { onPin?.(colDef.field, side); onClose(); };
210
+ const handleSort = (direction: GridSortDirection) => { onSort?.(colDef.field, direction); close(true); };
211
+ // `hideable: false` / `pinnable: false` mean the column cannot be hidden / pinned from the UI,
212
+ // so the menu leaves those actions out (this also covers the synthetic __group__ column).
213
+ const canHide = Boolean(onHide) && colDef.hideable !== false;
214
+ const canPin = Boolean(onPin) && colDef.pinnable !== false;
215
+
216
+ const handleHide = () => { if (canHide) onHide?.(colDef.field); close(true); };
217
+ const handlePin = (side: 'left' | 'right' | null) => { if (canPin) onPin?.(colDef.field, side); close(true); };
173
218
 
174
219
  const currentSort = sortModel?.find(item => item.field === colDef.field)?.sort;
175
220
  const isUnsorted = currentSort === undefined || currentSort === null;
@@ -181,24 +226,24 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
181
226
  const colLabel = colDef.headerName || colDef.field;
182
227
 
183
228
  return ReactDOM.createPortal(
184
- <div ref={menuRef} className="ogx-column-menu" style={style} role="menu" aria-label={`Column options for ${colLabel}`}>
229
+ <div ref={menuRef} className="ogx-column-menu" style={style} role="menu" aria-label={`Column options for ${colLabel}`} onKeyDown={handleMenuKeyDown}>
185
230
 
186
231
  { }
187
232
  {onSort && colDef.sortable !== false && (
188
233
  <>
189
- <button className={`ogx-menu-item${currentSort === 'asc' ? ' ogx-menu-item--active' : ''}`}
234
+ <button type="button" className={`ogx-menu-item${currentSort === 'asc' ? ' ogx-menu-item--active' : ''}`}
190
235
  onClick={() => handleSort('asc')} role="menuitemradio" aria-checked={currentSort === 'asc'}>
191
236
  <span className="ogx-menu-item__indicator" aria-hidden="true">{currentSort === 'asc' ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
192
237
  <span className="ogx-menu-item__icon" aria-hidden="true"><AscIcon /></span>
193
238
  Sort Ascending
194
239
  </button>
195
- <button className={`ogx-menu-item${currentSort === 'desc' ? ' ogx-menu-item--active' : ''}`}
240
+ <button type="button" className={`ogx-menu-item${currentSort === 'desc' ? ' ogx-menu-item--active' : ''}`}
196
241
  onClick={() => handleSort('desc')} role="menuitemradio" aria-checked={currentSort === 'desc'}>
197
242
  <span className="ogx-menu-item__indicator" aria-hidden="true">{currentSort === 'desc' ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
198
243
  <span className="ogx-menu-item__icon" aria-hidden="true"><DescIcon /></span>
199
244
  Sort Descending
200
245
  </button>
201
- <button className={`ogx-menu-item${isUnsorted ? ' ogx-menu-item--active' : ''}`}
246
+ <button type="button" className={`ogx-menu-item${isUnsorted ? ' ogx-menu-item--active' : ''}`}
202
247
  onClick={() => handleSort(null)} role="menuitemradio" aria-checked={isUnsorted}>
203
248
  <span className="ogx-menu-item__indicator" aria-hidden="true">{isUnsorted ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
204
249
  <span className="ogx-menu-item__icon" aria-hidden="true"><UnsortIcon /></span>
@@ -209,36 +254,43 @@ export function ColumnMenu({ colDef, sortModel, onSort, onHide, onPin, pinnedCol
209
254
  )}
210
255
 
211
256
  { }
212
- <div className="ogx-menu-section-label" aria-hidden="true">Pin column</div>
213
- <button className={`ogx-menu-item${isPinnedLeft ? ' ogx-menu-item--active' : ''}`}
214
- onClick={() => handlePin('left')} role="menuitemradio" aria-checked={isPinnedLeft} aria-label="Pin to left">
215
- <span className="ogx-menu-item__indicator" aria-hidden="true">{isPinnedLeft ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
216
- <span className="ogx-menu-item__icon" aria-hidden="true"><PinLeftIcon /></span>
217
- Pin to Left
218
- </button>
219
- <button className={`ogx-menu-item${isPinnedRight ? ' ogx-menu-item--active' : ''}`}
220
- onClick={() => handlePin('right')} role="menuitemradio" aria-checked={isPinnedRight} aria-label="Pin to right">
221
- <span className="ogx-menu-item__indicator" aria-hidden="true">{isPinnedRight ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
222
- <span className="ogx-menu-item__icon" aria-hidden="true"><PinRightIcon /></span>
223
- Pin to Right
224
- </button>
225
- <button className={`ogx-menu-item${isUnpinned ? ' ogx-menu-item--active' : ''}`}
226
- onClick={() => handlePin(null)} role="menuitemradio" aria-checked={isUnpinned} aria-label="Unpin column">
227
- <span className="ogx-menu-item__indicator" aria-hidden="true">{isUnpinned ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
228
- <span className="ogx-menu-item__icon" aria-hidden="true"><UnpinIcon /></span>
229
- Unpin
230
- </button>
231
- <div className="ogx-menu-divider" />
257
+ {canPin && (
258
+ <>
259
+ <div className="ogx-menu-section-label" aria-hidden="true">Pin column</div>
260
+ <button type="button" className={`ogx-menu-item${isPinnedLeft ? ' ogx-menu-item--active' : ''}`}
261
+ onClick={() => handlePin('left')} role="menuitemradio" aria-checked={isPinnedLeft} aria-label="Pin to left">
262
+ <span className="ogx-menu-item__indicator" aria-hidden="true">{isPinnedLeft ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
263
+ <span className="ogx-menu-item__icon" aria-hidden="true"><PinLeftIcon /></span>
264
+ Pin to Left
265
+ </button>
266
+ <button type="button" className={`ogx-menu-item${isPinnedRight ? ' ogx-menu-item--active' : ''}`}
267
+ onClick={() => handlePin('right')} role="menuitemradio" aria-checked={isPinnedRight} aria-label="Pin to right">
268
+ <span className="ogx-menu-item__indicator" aria-hidden="true">{isPinnedRight ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
269
+ <span className="ogx-menu-item__icon" aria-hidden="true"><PinRightIcon /></span>
270
+ Pin to Right
271
+ </button>
272
+ <button type="button" className={`ogx-menu-item${isUnpinned ? ' ogx-menu-item--active' : ''}`}
273
+ onClick={() => handlePin(null)} role="menuitemradio" aria-checked={isUnpinned} aria-label="Unpin column">
274
+ <span className="ogx-menu-item__indicator" aria-hidden="true">{isUnpinned ? <ActiveDotIcon /> : <InactiveDotIcon />}</span>
275
+ <span className="ogx-menu-item__icon" aria-hidden="true"><UnpinIcon /></span>
276
+ Unpin
277
+ </button>
278
+ <div className="ogx-menu-divider" />
279
+ </>
280
+ )}
232
281
 
233
- { }
234
- <button className="ogx-menu-item" onClick={handleHide} role="menuitem" aria-label={`Hide ${colLabel} column`}>
235
- <span className="ogx-menu-item__indicator" aria-hidden="true" />
236
- <span className="ogx-menu-item__icon" aria-hidden="true"><HideIcon /></span>
237
- Hide Column
238
- </button>
282
+ {canHide && (
283
+ <>
284
+ <button type="button" className="ogx-menu-item" onClick={handleHide} role="menuitem" aria-label={`Hide ${colLabel} column`}>
285
+ <span className="ogx-menu-item__indicator" aria-hidden="true" />
286
+ <span className="ogx-menu-item__icon" aria-hidden="true"><HideIcon /></span>
287
+ Hide Column
288
+ </button>
289
+ <div className="ogx-menu-divider" />
290
+ </>
291
+ )}
239
292
 
240
- <div className="ogx-menu-divider" />
241
- <button className="ogx-menu-item" onClick={() => { onManageColumns?.(); onClose(); }} role="menuitem" aria-label="Manage all columns">
293
+ <button type="button" className="ogx-menu-item" onClick={() => { onManageColumns?.(); close(false); }} role="menuitem" aria-label="Manage all columns">
242
294
  <span className="ogx-menu-item__indicator" aria-hidden="true" />
243
295
  <span className="ogx-menu-item__icon" aria-hidden="true"><ManageColumnsIcon /></span>
244
296
  Manage columns
@@ -148,6 +148,9 @@
148
148
  .ogx__header-cell--pinned-left {
149
149
  box-shadow: var(--ogx-grid-pinned-left-shadow, 4px 0 12px -4px rgba(0, 0, 0, 0.05));
150
150
  }
151
+ .ogx__header-cell--pinned-right-first {
152
+ margin-left: auto;
153
+ }
151
154
  .ogx__header-cell--pinned-right {
152
155
  box-shadow: var(--ogx-grid-pinned-right-shadow, -4px 0 12px -4px rgba(0, 0, 0, 0.05));
153
156
  }
@@ -174,17 +177,17 @@
174
177
  }
175
178
  .ogx__header-cell--drag-over {
176
179
  border-left: 2px solid var(--ogx-color-primary) !important;
177
- background: var(--ogx-color-blue-50);
180
+ background: var(--ogx-color-primary-light);
178
181
  }
179
182
  /* Show ring on header cells during keyboard navigation */
180
183
  .ogx--kb .ogx__header-cell--focused {
181
- outline: 2px solid var(--ogx-color-primary, #2563eb);
184
+ outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary, #2563eb));
182
185
  outline-offset: -2px;
183
186
  background-color: var(--ogx-grid-header-hover-background);
184
187
  z-index: 6;
185
188
  }
186
189
  .ogx__header-cell--focus-visible {
187
- outline: 2px solid var(--ogx-color-primary);
190
+ outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary));
188
191
  outline-offset: -2px;
189
192
  background-color: var(--ogx-grid-header-hover-background);
190
193
  z-index: 6;