@opencorestack/opengridx 2.0.4 → 3.0.0

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 (221) hide show
  1. package/CHANGELOG.md +408 -3
  2. package/README.md +93 -72
  3. package/dist/index.d.ts +611 -121
  4. package/dist/opengridx.css +1 -1
  5. package/dist/opengridx.es.js +7992 -5650
  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 -161
  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 +45 -22
  29. package/docs/components/datagrid.md +14 -12
  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 +100 -36
  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 +147 -41
  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 +56 -19
  54. package/docs/features/pinning.md +23 -5
  55. package/docs/features/selection.md +50 -10
  56. package/docs/features/sorting-pagination.md +66 -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 +44 -9
  60. package/docs/features/virtualization.md +64 -15
  61. package/docs/migration/v1-to-v2.md +115 -0
  62. package/docs/migration/v2-to-v3.md +1222 -0
  63. package/docs/performance.md +124 -0
  64. package/docs/roadmap.md +16 -10
  65. package/lib/components/Cell/Cell.css +13 -1
  66. package/lib/components/Cell/Cell.tsx +130 -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 +11 -0
  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 +84 -12
  76. package/lib/components/DataGrid/DataGrid.tsx +606 -872
  77. package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
  78. package/lib/components/DataGrid/GridEmptyState.tsx +7 -1
  79. package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
  80. package/lib/components/DataGrid/GridListView.tsx +166 -23
  81. package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
  82. package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
  83. package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
  84. package/lib/components/DataGrid/GridPinnedRows.tsx +65 -11
  85. package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
  86. package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
  87. package/lib/components/DataGrid/GridVirtualRows.tsx +48 -25
  88. package/lib/components/DetailPanel/DetailPanel.css +0 -6
  89. package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
  90. package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
  91. package/lib/components/FilterPanel/FilterPanel.css +13 -2
  92. package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
  93. package/lib/components/Header/ColumnMenu.tsx +91 -39
  94. package/lib/components/Header/Header.css +3 -3
  95. package/lib/components/Header/Header.tsx +179 -160
  96. package/lib/components/ListView/ListView.css +25 -0
  97. package/lib/components/ListView/ListViewRow.tsx +79 -10
  98. package/lib/components/Pagination/Pagination.tsx +15 -7
  99. package/lib/components/Row/Row.tsx +185 -75
  100. package/lib/components/Toolbar/GlobalSearch.css +6 -3
  101. package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
  102. package/lib/components/Toolbar/GridToolbar.css +12 -11
  103. package/lib/components/Toolbar/GridToolbar.tsx +133 -89
  104. package/lib/components/Toolbar/PivotPanel.tsx +89 -49
  105. package/lib/components/Tooltip/Tooltip.css +20 -0
  106. package/lib/components/Tooltip/Tooltip.tsx +68 -35
  107. package/lib/components/ui/Button.tsx +3 -0
  108. package/lib/components/ui/Checkbox.tsx +2 -1
  109. package/lib/components/ui/ExpandIcon.css +0 -9
  110. package/lib/components/ui/ExpandIcon.tsx +4 -1
  111. package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
  112. package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
  113. package/lib/hooks/core/useDataGrid.ts +115 -173
  114. package/lib/hooks/core/useGridAggregationApi.ts +72 -0
  115. package/lib/hooks/core/useGridApiMethods.ts +115 -0
  116. package/lib/hooks/core/useGridApiRef.ts +15 -3
  117. package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
  118. package/lib/hooks/core/useGridAriaRows.ts +38 -0
  119. package/lib/hooks/core/useGridClipboardApi.ts +38 -0
  120. package/lib/hooks/core/useGridColumnLookup.ts +30 -0
  121. package/lib/hooks/core/useGridColumns.tsx +284 -76
  122. package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
  123. package/lib/hooks/core/useGridControlledState.ts +99 -6
  124. package/lib/hooks/core/useGridDetailPanel.ts +51 -0
  125. package/lib/hooks/core/useGridDevWarnings.ts +123 -0
  126. package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
  127. package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
  128. package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
  129. package/lib/hooks/core/useGridHierarchy.ts +81 -0
  130. package/lib/hooks/core/useGridKeyboardNavigation.ts +452 -250
  131. package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
  132. package/lib/hooks/core/useGridPageCorrection.ts +55 -0
  133. package/lib/hooks/core/useGridRowIdOf.ts +32 -0
  134. package/lib/hooks/core/useGridRowInteractions.ts +81 -0
  135. package/lib/hooks/core/useGridRowPipeline.ts +69 -39
  136. package/lib/hooks/core/useGridRowSelection.ts +91 -0
  137. package/lib/hooks/core/useGridScrollSync.ts +80 -32
  138. package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
  139. package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
  140. package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
  141. package/lib/hooks/core/useGridToolbarProps.ts +97 -0
  142. package/lib/hooks/core/useGridViewport.ts +38 -0
  143. package/lib/hooks/core/useGridViewportSize.ts +26 -0
  144. package/lib/hooks/core/useGridVisibleRows.ts +20 -7
  145. package/lib/hooks/core/useLayout.ts +215 -195
  146. package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
  147. package/lib/hooks/core/useStableColumns.ts +34 -0
  148. package/lib/hooks/features/useAggregation.ts +86 -90
  149. package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
  150. package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
  151. package/lib/hooks/features/useGridClipboard.ts +133 -73
  152. package/lib/hooks/features/useGridDataSource.ts +354 -128
  153. package/lib/hooks/features/useGridEditing.ts +211 -51
  154. package/lib/hooks/features/useGridPivot.ts +92 -0
  155. package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
  156. package/lib/hooks/features/useGridSpanning.ts +410 -306
  157. package/lib/hooks/features/usePivot.ts +0 -0
  158. package/lib/hooks/features/useServerTreeChildren.ts +50 -0
  159. package/lib/hooks/useColumnReorder.ts +38 -44
  160. package/lib/hooks/useRowGrouping.ts +335 -214
  161. package/lib/hooks/useRowReorder.ts +62 -31
  162. package/lib/hooks/useTreeData.ts +330 -262
  163. package/lib/index.ts +30 -2
  164. package/lib/state/index.ts +1 -0
  165. package/lib/state/types.ts +7 -1
  166. package/lib/state/useGridStateStorage.ts +88 -29
  167. package/lib/theme/DataGridThemeProvider.tsx +143 -38
  168. package/lib/theme/gridThemeContext.ts +5 -0
  169. package/lib/theme/index.ts +5 -0
  170. package/lib/theme/presets.ts +5 -4
  171. package/lib/theme/types.ts +40 -2
  172. package/lib/types/index.ts +311 -108
  173. package/lib/utils/aggregation/index.ts +157 -0
  174. package/lib/utils/aria/index.ts +40 -0
  175. package/lib/utils/columnGroups.ts +110 -0
  176. package/lib/utils/columnLookup.ts +33 -0
  177. package/lib/utils/columnWidth.ts +22 -0
  178. package/lib/utils/dataSource/index.ts +46 -0
  179. package/lib/utils/detailPanel.ts +66 -0
  180. package/lib/utils/dragData.ts +44 -0
  181. package/lib/utils/editing/index.ts +30 -0
  182. package/lib/utils/export/exportShared.ts +197 -0
  183. package/lib/utils/export/exportToExcelAdvanced.ts +344 -121
  184. package/lib/utils/export/exportToPdf.ts +143 -92
  185. package/lib/utils/export/groupLabel.ts +14 -0
  186. package/lib/utils/export/index.ts +278 -272
  187. package/lib/utils/export/pdfText.ts +50 -0
  188. package/lib/utils/filtering/index.ts +277 -148
  189. package/lib/utils/focus/index.ts +115 -0
  190. package/lib/utils/gridApiRows.ts +35 -0
  191. package/lib/utils/gridModes.ts +77 -0
  192. package/lib/utils/grouping/groupedExportRows.ts +91 -0
  193. package/lib/utils/pagination/index.ts +56 -0
  194. package/lib/utils/pinning/index.ts +77 -33
  195. package/lib/utils/pivot/index.ts +368 -0
  196. package/lib/utils/scroll/index.ts +78 -0
  197. package/lib/utils/scroll/scrollEnd.ts +18 -0
  198. package/lib/utils/sorting/index.ts +154 -30
  199. package/lib/utils/syntheticRows.ts +12 -0
  200. package/lib/utils/values.ts +206 -0
  201. package/lib/utils/viewport.ts +14 -0
  202. package/llms.txt +272 -166
  203. package/package.json +18 -6
  204. package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
  205. package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
  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/Header/Header.test.tsx +0 -135
  210. package/lib/components/Pagination/Pagination.test.tsx +0 -64
  211. package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
  212. package/lib/hooks/core/useGridControlledState.test.ts +0 -86
  213. package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
  214. package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
  215. package/lib/hooks/features/useAggregation.test.ts +0 -175
  216. package/lib/hooks/features/usePivot.test.ts +0 -202
  217. package/lib/hooks/useRowGrouping.test.ts +0 -137
  218. package/lib/utils/export/exportToPdf.test.ts +0 -120
  219. package/lib/utils/export/groupedExport.test.ts +0 -144
  220. package/lib/utils/filtering/filtering.test.ts +0 -178
  221. 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
@@ -174,17 +174,17 @@
174
174
  }
175
175
  .ogx__header-cell--drag-over {
176
176
  border-left: 2px solid var(--ogx-color-primary) !important;
177
- background: var(--ogx-color-blue-50);
177
+ background: var(--ogx-color-primary-light);
178
178
  }
179
179
  /* Show ring on header cells during keyboard navigation */
180
180
  .ogx--kb .ogx__header-cell--focused {
181
- outline: 2px solid var(--ogx-color-primary, #2563eb);
181
+ outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary, #2563eb));
182
182
  outline-offset: -2px;
183
183
  background-color: var(--ogx-grid-header-hover-background);
184
184
  z-index: 6;
185
185
  }
186
186
  .ogx__header-cell--focus-visible {
187
- outline: 2px solid var(--ogx-color-primary);
187
+ outline: 2px solid var(--ogx-grid-cell-focus-border, var(--ogx-color-primary));
188
188
  outline-offset: -2px;
189
189
  background-color: var(--ogx-grid-header-hover-background);
190
190
  z-index: 6;