@alaarab/ogrid-angular 2.1.10 → 2.1.12

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/dist/esm/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, computeTabNavigation, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, getScrollTopForRow, GRID_BORDER_RADIUS, getStatusBarParts, GRID_CONTEXT_MENU_ITEMS, formatShortcut, injectGlobalStyles, buildHeaderRows, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, measureColumnContentWidth, getPaginationViewModel, ROW_NUMBER_COLUMN_WIDTH, reorderColumnArray, findCtrlArrowTarget, measureRange } from '@alaarab/ogrid-core';
1
+ import { flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, validateRowIds, computeNextSortState, mergeFilter, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, parseValue, UndoRedoStack, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, getCellValue, computeTabNavigation, applyFillValues, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, getScrollTopForRow, validateVirtualScrollConfig, GRID_BORDER_RADIUS, getStatusBarParts, GRID_CONTEXT_MENU_ITEMS, formatShortcut, injectGlobalStyles, buildHeaderRows, getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildPopoverEditorProps, measureColumnContentWidth, getPaginationViewModel, ROW_NUMBER_COLUMN_WIDTH, reorderColumnArray, findCtrlArrowTarget, measureRange } from '@alaarab/ogrid-core';
2
2
  export * from '@alaarab/ogrid-core';
3
3
  export { CELL_PADDING, CHECKBOX_COLUMN_WIDTH, DEFAULT_DEBOUNCE_MS, DEFAULT_MIN_COLUMN_WIDTH, GRID_BORDER_RADIUS, PEOPLE_SEARCH_DEBOUNCE_MS, ROW_NUMBER_COLUMN_WIDTH, SIDEBAR_TRANSITION_MS, Z_INDEX, debounce, getCellRenderDescriptor, getHeaderFilterConfig, isInSelectionRange, normalizeSelectionRange, resolveCellDisplayContent, resolveCellStyle, toUserLike } from '@alaarab/ogrid-core';
4
4
  import { Injectable, Input, Component, ChangeDetectionStrategy, ViewEncapsulation, Output, ViewChild, inject, DestroyRef, signal, computed, effect, NgZone, EventEmitter, Injector, EnvironmentInjector, createComponent } from '@angular/core';
@@ -1054,7 +1054,11 @@ var DataGridInteractionHelper = class {
1054
1054
  this.fillDragStart = { startRow: range.startRow, startCol: range.startCol };
1055
1055
  }
1056
1056
  // --- Keyboard navigation ---
1057
- handleGridKeyDown(e, items, getRowId, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, editable, wrappedOnCellValueChanged, originalOnCellValueChanged, rowSelection, selectedRowIds, wrapperEl, handleRowCheckboxChange, editingCell, setEditingCell) {
1057
+ handleGridKeyDown(e, items, getRowId, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, editable, wrappedOnCellValueChanged, originalOnCellValueChanged, rowSelection, selectedRowIds, wrapperEl, handleRowCheckboxChange, editingCell, setEditingCell, onKeyDown) {
1058
+ if (onKeyDown) {
1059
+ onKeyDown(e);
1060
+ if (e.defaultPrevented) return;
1061
+ }
1058
1062
  const activeCell = this.activeCellSig();
1059
1063
  const selectionRange = this.selectionRangeSig();
1060
1064
  const maxRowIndex = items.length - 1;
@@ -1099,6 +1103,26 @@ var DataGridInteractionHelper = class {
1099
1103
  void this.handlePaste(items, visibleCols, colOffset, editable, wrappedOnCellValueChanged);
1100
1104
  }
1101
1105
  break;
1106
+ case "d":
1107
+ if (ctrl) {
1108
+ if (editingCell != null) break;
1109
+ if (editable !== false && wrappedOnCellValueChanged != null) {
1110
+ const fillRange = selectionRange ?? (activeCell != null ? { startRow: activeCell.rowIndex, startCol: activeCell.columnIndex - colOffset, endRow: activeCell.rowIndex, endCol: activeCell.columnIndex - colOffset } : null);
1111
+ if (fillRange != null) {
1112
+ e.preventDefault();
1113
+ const norm = normalizeSelectionRange(fillRange);
1114
+ const fillEvents = applyFillValues(norm, norm.startRow, norm.startCol, items, visibleCols);
1115
+ if (fillEvents.length > 0) {
1116
+ this.undoRedoStack.beginBatch();
1117
+ for (const evt of fillEvents) wrappedOnCellValueChanged(evt);
1118
+ this.undoRedoStack.endBatch();
1119
+ this.undoLengthSig.set(this.undoRedoStack.historyLength);
1120
+ this.redoLengthSig.set(this.undoRedoStack.redoLength);
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ break;
1102
1126
  case "ArrowDown": {
1103
1127
  e.preventDefault();
1104
1128
  const newRow = ctrl ? findCtrlTarget(rowIndex, maxRowIndex, 1, (r) => isEmptyAt(r, Math.max(0, dataColIndex))) : Math.min(rowIndex + 1, maxRowIndex);
@@ -1545,6 +1569,43 @@ var DataGridStateService = class {
1545
1569
  if (!p) return false;
1546
1570
  return p.items.length === 0 && !!p.emptyState && !p.isLoading;
1547
1571
  });
1572
+ // --- Stable handler closures (defined once, reused in getState()) ---
1573
+ // These arrow properties ensure Angular change detection receives the same
1574
+ // function reference on every getState() call, avoiding unnecessary re-renders.
1575
+ this._setColumnSizingOverrides = (overrides) => this.layoutHelper.columnSizingOverridesSig.set(overrides);
1576
+ this._updateSelection = (ids) => this.updateSelection(ids);
1577
+ this._handleRowCheckboxChange = (rowId, checked, rowIndex, shiftKey) => this.handleRowCheckboxChange(rowId, checked, rowIndex, shiftKey);
1578
+ this._handleSelectAll = (checked) => this.handleSelectAll(checked);
1579
+ this._setEditingCell = (cell) => this.setEditingCell(cell);
1580
+ this._setPendingEditorValue = (v) => this.setPendingEditorValue(v);
1581
+ this._commitCellEdit = (item, colId, oldVal, newVal, rowIdx, globalColIdx) => this.commitCellEdit(item, colId, oldVal, newVal, rowIdx, globalColIdx);
1582
+ this._cancelPopoverEdit = () => this.cancelPopoverEdit();
1583
+ this._setPopoverAnchorEl = (el) => this.editingHelper.popoverAnchorElSig.set(el);
1584
+ this._setActiveCell = (cell) => this.setActiveCell(cell);
1585
+ this._setSelectionRange = (range) => this.setSelectionRange(range);
1586
+ this._handleCellMouseDown = (e, r, c) => this.handleCellMouseDown(e, r, c);
1587
+ this._handleSelectAllCells = () => this.handleSelectAllCells();
1588
+ this._handleGridKeyDown = (e) => this.handleGridKeyDown(e);
1589
+ this._handleFillHandleMouseDown = (e) => this.handleFillHandleMouseDown(e);
1590
+ this._handleCopy = () => this.handleCopy();
1591
+ this._handleCut = () => this.handleCut();
1592
+ this._handlePaste = () => this.handlePaste();
1593
+ this._clearClipboardRanges = () => this.clearClipboardRanges();
1594
+ this._onUndo = () => this.undo();
1595
+ this._onRedo = () => this.redo();
1596
+ this._setContextMenuPosition = (pos) => this.setContextMenuPosition(pos);
1597
+ this._handleCellContextMenu = (e) => this.handleCellContextMenu(e);
1598
+ this._closeContextMenu = () => this.closeContextMenu();
1599
+ this._headerFilterOnColumnSort = (columnKey, direction) => this.props()?.onColumnSort(columnKey, direction);
1600
+ this._headerFilterOnFilterChange = (key, value) => this.props()?.onFilterChange(key, value);
1601
+ this._pinColumn = (columnId, side) => this.pinColumn(columnId, side);
1602
+ this._unpinColumn = (columnId) => this.unpinColumn(columnId);
1603
+ this._isPinned = (columnId) => this.isPinned(columnId);
1604
+ this._openHeaderMenu = (columnId, anchorEl) => this.openHeaderMenu(columnId, anchorEl);
1605
+ this._closeHeaderMenu = () => this.closeHeaderMenu();
1606
+ this._headerMenuPinLeft = () => this.headerMenuPinLeft();
1607
+ this._headerMenuPinRight = () => this.headerMenuPinRight();
1608
+ this._headerMenuUnpin = () => this.headerMenuUnpin();
1548
1609
  this.layoutHelper = new DataGridLayoutHelper(this.props, this.wrapperEl, this.ngZone);
1549
1610
  this.interactionHelper = new DataGridInteractionHelper();
1550
1611
  this.editingHelper = new DataGridEditingHelper(
@@ -1724,7 +1785,8 @@ var DataGridStateService = class {
1724
1785
  this.wrapperEl(),
1725
1786
  (rowId, checked, rowIndex, shiftKey) => this.handleRowCheckboxChange(rowId, checked, rowIndex, shiftKey),
1726
1787
  this.editingHelper.editingCellSig(),
1727
- (cell) => this.setEditingCell(cell)
1788
+ (cell) => this.setEditingCell(cell),
1789
+ p.onKeyDown
1728
1790
  );
1729
1791
  }
1730
1792
  // --- Fill handle (delegated to interactionHelper + setupFillHandleDrag) ---
@@ -1802,63 +1864,63 @@ var DataGridStateService = class {
1802
1864
  minTableWidth: this.minTableWidth(),
1803
1865
  desiredTableWidth: this.desiredTableWidth(),
1804
1866
  columnSizingOverrides: this.layoutHelper.columnSizingOverridesSig(),
1805
- setColumnSizingOverrides: (overrides) => this.layoutHelper.columnSizingOverridesSig.set(overrides),
1867
+ setColumnSizingOverrides: this._setColumnSizingOverrides,
1806
1868
  onColumnResized: p?.onColumnResized,
1807
1869
  onAutosizeColumn: p?.onAutosizeColumn
1808
1870
  };
1809
1871
  const rowSelection = {
1810
1872
  selectedRowIds: this.selectedRowIds(),
1811
- updateSelection: (ids) => this.updateSelection(ids),
1812
- handleRowCheckboxChange: (rowId, checked, rowIndex, shiftKey) => this.handleRowCheckboxChange(rowId, checked, rowIndex, shiftKey),
1813
- handleSelectAll: (checked) => this.handleSelectAll(checked),
1873
+ updateSelection: this._updateSelection,
1874
+ handleRowCheckboxChange: this._handleRowCheckboxChange,
1875
+ handleSelectAll: this._handleSelectAll,
1814
1876
  allSelected: this.allSelected(),
1815
1877
  someSelected: this.someSelected()
1816
1878
  };
1817
1879
  const editing = {
1818
1880
  editingCell: this.editingHelper.editingCellSig(),
1819
- setEditingCell: (cell) => this.setEditingCell(cell),
1881
+ setEditingCell: this._setEditingCell,
1820
1882
  pendingEditorValue: this.editingHelper.pendingEditorValueSig(),
1821
- setPendingEditorValue: (v) => this.setPendingEditorValue(v),
1822
- commitCellEdit: (item, colId, oldVal, newVal, rowIdx, globalColIdx) => this.commitCellEdit(item, colId, oldVal, newVal, rowIdx, globalColIdx),
1823
- cancelPopoverEdit: () => this.cancelPopoverEdit(),
1883
+ setPendingEditorValue: this._setPendingEditorValue,
1884
+ commitCellEdit: this._commitCellEdit,
1885
+ cancelPopoverEdit: this._cancelPopoverEdit,
1824
1886
  popoverAnchorEl: this.editingHelper.popoverAnchorElSig(),
1825
- setPopoverAnchorEl: (el) => this.editingHelper.popoverAnchorElSig.set(el)
1887
+ setPopoverAnchorEl: this._setPopoverAnchorEl
1826
1888
  };
1827
1889
  const interaction = {
1828
1890
  activeCell: cellSel ? this.interactionHelper.activeCellSig() : null,
1829
- setActiveCell: cellSel ? (cell) => this.setActiveCell(cell) : void 0,
1891
+ setActiveCell: cellSel ? this._setActiveCell : void 0,
1830
1892
  selectionRange: cellSel ? this.interactionHelper.selectionRangeSig() : null,
1831
- setSelectionRange: cellSel ? (range) => this.setSelectionRange(range) : void 0,
1832
- handleCellMouseDown: cellSel ? (e, r, c) => this.handleCellMouseDown(e, r, c) : NOOP_MOUSE,
1833
- handleSelectAllCells: cellSel ? () => this.handleSelectAllCells() : NOOP,
1893
+ setSelectionRange: cellSel ? this._setSelectionRange : void 0,
1894
+ handleCellMouseDown: cellSel ? this._handleCellMouseDown : NOOP_MOUSE,
1895
+ handleSelectAllCells: cellSel ? this._handleSelectAllCells : NOOP,
1834
1896
  hasCellSelection: cellSel ? this.hasCellSelection() : false,
1835
- handleGridKeyDown: cellSel ? (e) => this.handleGridKeyDown(e) : NOOP_KEY,
1836
- handleFillHandleMouseDown: cellSel ? (e) => this.handleFillHandleMouseDown(e) : void 0,
1837
- handleCopy: cellSel ? () => this.handleCopy() : NOOP,
1838
- handleCut: cellSel ? () => this.handleCut() : NOOP,
1839
- handlePaste: cellSel ? () => this.handlePaste() : NOOP_ASYNC,
1897
+ handleGridKeyDown: cellSel ? this._handleGridKeyDown : NOOP_KEY,
1898
+ handleFillHandleMouseDown: cellSel ? this._handleFillHandleMouseDown : void 0,
1899
+ handleCopy: cellSel ? this._handleCopy : NOOP,
1900
+ handleCut: cellSel ? this._handleCut : NOOP,
1901
+ handlePaste: cellSel ? this._handlePaste : NOOP_ASYNC,
1840
1902
  cutRange: cellSel ? this.interactionHelper.cutRangeSig() : null,
1841
1903
  copyRange: cellSel ? this.interactionHelper.copyRangeSig() : null,
1842
- clearClipboardRanges: cellSel ? () => this.clearClipboardRanges() : NOOP,
1904
+ clearClipboardRanges: cellSel ? this._clearClipboardRanges : NOOP,
1843
1905
  canUndo: this.canUndo(),
1844
1906
  canRedo: this.canRedo(),
1845
- onUndo: () => this.undo(),
1846
- onRedo: () => this.redo(),
1907
+ onUndo: this._onUndo,
1908
+ onRedo: this._onRedo,
1847
1909
  isDragging: cellSel ? this.interactionHelper.isDraggingSig() : false
1848
1910
  };
1849
1911
  const contextMenu = {
1850
1912
  menuPosition: cellSel ? this.interactionHelper.contextMenuPositionSig() : null,
1851
- setMenuPosition: cellSel ? (pos) => this.setContextMenuPosition(pos) : void 0,
1852
- handleCellContextMenu: cellSel ? (e) => this.handleCellContextMenu(e) : NOOP_CTX,
1853
- closeContextMenu: cellSel ? () => this.closeContextMenu() : NOOP
1913
+ setMenuPosition: cellSel ? this._setContextMenuPosition : void 0,
1914
+ handleCellContextMenu: cellSel ? this._handleCellContextMenu : NOOP_CTX,
1915
+ closeContextMenu: cellSel ? this._closeContextMenu : NOOP
1854
1916
  };
1855
1917
  const viewModels = {
1856
1918
  headerFilterInput: {
1857
1919
  sortBy: p?.sortBy,
1858
1920
  sortDirection: p?.sortDirection ?? "asc",
1859
- onColumnSort: (columnKey, direction) => p?.onColumnSort(columnKey, direction),
1921
+ onColumnSort: this._headerFilterOnColumnSort,
1860
1922
  filters: p?.filters ?? {},
1861
- onFilterChange: (key, value) => p?.onFilterChange(key, value),
1923
+ onFilterChange: this._headerFilterOnFilterChange,
1862
1924
  filterOptions: p?.filterOptions ?? {},
1863
1925
  loadingFilterOptions: p?.loadingFilterOptions ?? {},
1864
1926
  peopleSearch: p?.peopleSearch
@@ -1884,18 +1946,18 @@ var DataGridStateService = class {
1884
1946
  const currentPinState = openForColumn ? p?.pinnedColumns?.[openForColumn] : void 0;
1885
1947
  const pinning = {
1886
1948
  pinnedColumns: p?.pinnedColumns ?? {},
1887
- pinColumn: (columnId, side) => this.pinColumn(columnId, side),
1888
- unpinColumn: (columnId) => this.unpinColumn(columnId),
1889
- isPinned: (columnId) => this.isPinned(columnId),
1949
+ pinColumn: this._pinColumn,
1950
+ unpinColumn: this._unpinColumn,
1951
+ isPinned: this._isPinned,
1890
1952
  headerMenu: {
1891
1953
  isOpen: this.headerMenuIsOpenSig(),
1892
1954
  openForColumn,
1893
1955
  anchorElement: this.headerMenuAnchorElementSig(),
1894
- open: (columnId, anchorEl) => this.openHeaderMenu(columnId, anchorEl),
1895
- close: () => this.closeHeaderMenu(),
1896
- handlePinLeft: () => this.headerMenuPinLeft(),
1897
- handlePinRight: () => this.headerMenuPinRight(),
1898
- handleUnpin: () => this.headerMenuUnpin(),
1956
+ open: this._openHeaderMenu,
1957
+ close: this._closeHeaderMenu,
1958
+ handlePinLeft: this._headerMenuPinLeft,
1959
+ handlePinRight: this._headerMenuPinRight,
1960
+ handleUnpin: this._headerMenuUnpin,
1899
1961
  canPinLeft: currentPinState !== "left",
1900
1962
  canPinRight: currentPinState !== "right",
1901
1963
  canUnpin: !!currentPinState
@@ -2153,7 +2215,7 @@ var ColumnReorderService = class {
2153
2215
  ColumnReorderService = __decorateClass([
2154
2216
  Injectable()
2155
2217
  ], ColumnReorderService);
2156
- var PASSTHROUGH_THRESHOLD = 100;
2218
+ var DEFAULT_PASSTHROUGH_THRESHOLD = 100;
2157
2219
  var VirtualScrollService = class {
2158
2220
  constructor() {
2159
2221
  this.destroyRef = inject(DestroyRef);
@@ -2169,8 +2231,9 @@ var VirtualScrollService = class {
2169
2231
  this.rowHeight = computed(() => this.config().rowHeight ?? 36);
2170
2232
  this.overscan = computed(() => this.config().overscan ?? 5);
2171
2233
  this.enabled = computed(() => this.config().enabled !== false);
2234
+ this.threshold = computed(() => this.config().threshold ?? DEFAULT_PASSTHROUGH_THRESHOLD);
2172
2235
  /** Whether virtual scrolling is actually active (enabled + enough rows). */
2173
- this.isActive = computed(() => this.enabled() && this.totalRows() >= PASSTHROUGH_THRESHOLD);
2236
+ this.isActive = computed(() => this.enabled() && this.totalRows() >= this.threshold());
2174
2237
  /** The visible range of rows with spacer offsets. */
2175
2238
  this.visibleRange = computed(() => {
2176
2239
  if (!this.isActive()) {
@@ -2229,7 +2292,11 @@ var VirtualScrollService = class {
2229
2292
  * Update the virtual scroll configuration.
2230
2293
  */
2231
2294
  updateConfig(updates) {
2232
- this.config.update((prev) => ({ ...prev, ...updates }));
2295
+ this.config.update((prev) => {
2296
+ const next = { ...prev, ...updates };
2297
+ validateVirtualScrollConfig(next);
2298
+ return next;
2299
+ });
2233
2300
  }
2234
2301
  };
2235
2302
  VirtualScrollService = __decorateClass([
@@ -2679,7 +2746,7 @@ OGridLayoutComponent = __decorateClass([
2679
2746
  color: var(--ogrid-fg, rgba(0, 0, 0, 0.87));
2680
2747
  }
2681
2748
  .ogrid-layout-root--fullscreen .ogrid-layout-container {
2682
- border: none; border-radius: 0 !important;
2749
+ border: none;
2683
2750
  }
2684
2751
  .ogrid-layout-toolbar {
2685
2752
  display: flex; justify-content: space-between; align-items: center;
@@ -2710,7 +2777,7 @@ OGridLayoutComponent = __decorateClass([
2710
2777
  `],
2711
2778
  template: `
2712
2779
  <div [class]="rootClass">
2713
- <div class="ogrid-layout-container" [style.border-radius.px]="borderRadius">
2780
+ <div class="ogrid-layout-container" [style.border-radius.px]="isFullScreen ? 0 : borderRadius">
2714
2781
  <!-- Toolbar strip -->
2715
2782
  @if (hasToolbar || fullScreen) {
2716
2783
  <div
@@ -3573,7 +3640,15 @@ var BaseDataGridTableComponent = class {
3573
3640
  return getCellRenderDescriptor(item, col, rowIndex, colIdx, this.cellDescriptorInput());
3574
3641
  }
3575
3642
  resolveCellContent(col, item, displayValue) {
3576
- return resolveCellDisplayContent(col, item, displayValue);
3643
+ try {
3644
+ return resolveCellDisplayContent(col, item, displayValue);
3645
+ } catch (err) {
3646
+ const onCellError = this.getProps()?.onCellError;
3647
+ if (onCellError) {
3648
+ onCellError(err instanceof Error ? err : new Error(String(err)), void 0);
3649
+ }
3650
+ return "";
3651
+ }
3577
3652
  }
3578
3653
  resolveCellStyleFn(col, item) {
3579
3654
  return resolveCellStyle(col, item);
@@ -74,7 +74,7 @@ export declare class DataGridInteractionHelper<T> {
74
74
  } | null, setEditingCell: (cell: {
75
75
  rowId: RowId;
76
76
  columnId: string;
77
- } | null) => void): void;
77
+ } | null) => void, onKeyDown?: (event: KeyboardEvent) => void): void;
78
78
  onWindowMouseMove(e: MouseEvent, colOffset: number, wrapperEl: HTMLElement | null): void;
79
79
  onWindowMouseUp(colOffset: number, wrapperEl: HTMLElement | null): void;
80
80
  resolveRangeFromMouse(cx: number, cy: number, colOffset: number): ISelectionRange | null;
@@ -255,6 +255,40 @@ export declare class DataGridStateService<T> {
255
255
  headerMenuPinLeft(): void;
256
256
  headerMenuPinRight(): void;
257
257
  headerMenuUnpin(): void;
258
+ private readonly _setColumnSizingOverrides;
259
+ private readonly _updateSelection;
260
+ private readonly _handleRowCheckboxChange;
261
+ private readonly _handleSelectAll;
262
+ private readonly _setEditingCell;
263
+ private readonly _setPendingEditorValue;
264
+ private readonly _commitCellEdit;
265
+ private readonly _cancelPopoverEdit;
266
+ private readonly _setPopoverAnchorEl;
267
+ private readonly _setActiveCell;
268
+ private readonly _setSelectionRange;
269
+ private readonly _handleCellMouseDown;
270
+ private readonly _handleSelectAllCells;
271
+ private readonly _handleGridKeyDown;
272
+ private readonly _handleFillHandleMouseDown;
273
+ private readonly _handleCopy;
274
+ private readonly _handleCut;
275
+ private readonly _handlePaste;
276
+ private readonly _clearClipboardRanges;
277
+ private readonly _onUndo;
278
+ private readonly _onRedo;
279
+ private readonly _setContextMenuPosition;
280
+ private readonly _handleCellContextMenu;
281
+ private readonly _closeContextMenu;
282
+ private readonly _headerFilterOnColumnSort;
283
+ private readonly _headerFilterOnFilterChange;
284
+ private readonly _pinColumn;
285
+ private readonly _unpinColumn;
286
+ private readonly _isPinned;
287
+ private readonly _openHeaderMenu;
288
+ private readonly _closeHeaderMenu;
289
+ private readonly _headerMenuPinLeft;
290
+ private readonly _headerMenuPinRight;
291
+ private readonly _headerMenuUnpin;
258
292
  getState(): DataGridStateResult<T>;
259
293
  private onWindowMouseMove;
260
294
  private onWindowMouseUp;
@@ -17,6 +17,7 @@ export declare class VirtualScrollService {
17
17
  readonly rowHeight: import("@angular/core").Signal<number>;
18
18
  readonly overscan: import("@angular/core").Signal<number>;
19
19
  readonly enabled: import("@angular/core").Signal<boolean>;
20
+ readonly threshold: import("@angular/core").Signal<number>;
20
21
  /** Whether virtual scrolling is actually active (enabled + enough rows). */
21
22
  readonly isActive: import("@angular/core").Signal<boolean>;
22
23
  /** The visible range of rows with spacer offsets. */
@@ -143,4 +143,6 @@ export interface IOGridDataGridProps<T> {
143
143
  onCellError?: (error: Error, info: unknown) => void;
144
144
  'aria-label'?: string;
145
145
  'aria-labelledby'?: string;
146
+ /** Custom keydown handler. Called before grid's built-in handling. Call event.preventDefault() to suppress grid default. */
147
+ onKeyDown?: (event: KeyboardEvent) => void;
146
148
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-angular",
3
- "version": "2.1.10",
3
+ "version": "2.1.12",
4
4
  "description": "OGrid Angular – Angular services, signals, and headless components for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -35,7 +35,7 @@
35
35
  "node": ">=18"
36
36
  },
37
37
  "dependencies": {
38
- "@alaarab/ogrid-core": "2.1.10"
38
+ "@alaarab/ogrid-core": "2.1.12"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@angular/core": "^21.0.0",