@alaarab/ogrid-vue 2.1.9 → 2.1.11

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 { injectGlobalStyles, Z_INDEX, getStatusBarParts, measureRange, flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, validateRowIds, computeRowSelectionState, buildCellIndex, UndoRedoStack, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, computeAggregations, getDataGridStatusBarConfig, computeVisibleRange, computeTotalHeight, buildHeaderRows, ROW_NUMBER_COLUMN_WIDTH, getHeaderFilterConfig, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, measureColumnContentWidth, getPinStateForColumn, parseValue, applyFillValues, applyCellDeletion, computeTabNavigation, computeArrowNavigation, computeNextSortState, mergeFilter, applyRangeRowSelection, getScrollTopForRow, getCellValue, calculateDropTarget, reorderColumnArray, computeAutoScrollSpeed } from '@alaarab/ogrid-core';
1
+ import { injectGlobalStyles, Z_INDEX, getStatusBarParts, measureRange, flattenColumns, getMultiSelectFilterFields, deriveFilterOptionsFromData, processClientSideData, validateColumns, validateRowIds, computeRowSelectionState, buildCellIndex, UndoRedoStack, CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, computeAggregations, getDataGridStatusBarConfig, validateVirtualScrollConfig, computeVisibleRange, computeTotalHeight, buildHeaderRows, ROW_NUMBER_COLUMN_WIDTH, getHeaderFilterConfig, getCellRenderDescriptor, buildInlineEditorProps, buildPopoverEditorProps, resolveCellDisplayContent, resolveCellStyle, rangesEqual, normalizeSelectionRange, formatSelectionAsTsv, parseTsvClipboard, applyPastedValues, applyCutClear, measureColumnContentWidth, getPinStateForColumn, parseValue, applyFillValues, applyCellDeletion, computeTabNavigation, computeArrowNavigation, computeNextSortState, mergeFilter, applyRangeRowSelection, getScrollTopForRow, getCellValue, calculateDropTarget, reorderColumnArray, computeAutoScrollSpeed } from '@alaarab/ogrid-core';
2
2
  export * from '@alaarab/ogrid-core';
3
3
  export { buildInlineEditorProps, buildPopoverEditorProps, getCellRenderDescriptor, getHeaderFilterConfig, isInSelectionRange, normalizeSelectionRange, resolveCellDisplayContent, resolveCellStyle, toUserLike } from '@alaarab/ogrid-core';
4
4
  import { defineComponent, ref, computed, onMounted, watch, toValue, onUnmounted, h, shallowRef, triggerRef, nextTick, Teleport, isRef, isReadonly, unref, customRef } from 'vue';
@@ -1254,6 +1254,12 @@ function useKeyboardNavigation(params) {
1254
1254
  const rowSelection = features.rowSelection.value;
1255
1255
  const wrapperRef = features.wrapperRef;
1256
1256
  const scrollToRow = features.scrollToRow;
1257
+ const { fillDown } = features;
1258
+ const onKeyDown = features.onKeyDown?.value;
1259
+ if (onKeyDown) {
1260
+ onKeyDown(e);
1261
+ if (e.defaultPrevented) return;
1262
+ }
1257
1263
  const maxRowIndex = items.length - 1;
1258
1264
  const maxColIndex = visibleColumnCount - 1 + colOffset;
1259
1265
  if (items.length === 0) return;
@@ -1294,6 +1300,15 @@ function useKeyboardNavigation(params) {
1294
1300
  void handlePaste();
1295
1301
  }
1296
1302
  break;
1303
+ case "d":
1304
+ if (e.ctrlKey || e.metaKey) {
1305
+ if (editingCell != null) break;
1306
+ if (editable !== false && fillDown) {
1307
+ e.preventDefault();
1308
+ fillDown();
1309
+ }
1310
+ }
1311
+ break;
1297
1312
  case "ArrowDown":
1298
1313
  case "ArrowUp":
1299
1314
  case "ArrowRight":
@@ -1622,7 +1637,21 @@ function useFillHandle(params) {
1622
1637
  if (!range) return;
1623
1638
  fillDrag.value = { startRow: range.startRow, startCol: range.startCol };
1624
1639
  };
1625
- return { fillDrag, setFillDrag, handleFillHandleMouseDown };
1640
+ const fillDown = () => {
1641
+ const range = selectionRange.value;
1642
+ if (!range || editable.value === false || !onCellValueChanged.value) return;
1643
+ const norm = normalizeSelectionRange(range);
1644
+ const currentItems = items.value;
1645
+ const currentCols = visibleCols.value;
1646
+ const callback = onCellValueChanged.value;
1647
+ const fillEvents = applyFillValues(norm, norm.startRow, norm.startCol, currentItems, currentCols);
1648
+ if (fillEvents.length > 0) {
1649
+ beginBatch?.();
1650
+ for (const evt of fillEvents) callback(evt);
1651
+ endBatch?.();
1652
+ }
1653
+ };
1654
+ return { fillDrag, setFillDrag, handleFillHandleMouseDown, fillDown };
1626
1655
  }
1627
1656
  function useUndoRedo(params) {
1628
1657
  const { onCellValueChanged, maxUndoDepth = 100 } = params;
@@ -2034,6 +2063,19 @@ function useDataGridState(params) {
2034
2063
  clearClipboardRanges();
2035
2064
  handleCellMouseDownBase(e, rowIndex, globalColIndex);
2036
2065
  };
2066
+ const { handleFillHandleMouseDown, fillDown } = useFillHandle({
2067
+ items,
2068
+ visibleCols,
2069
+ editable: editableProp,
2070
+ onCellValueChanged,
2071
+ selectionRange,
2072
+ setSelectionRange,
2073
+ setActiveCell,
2074
+ colOffset,
2075
+ wrapperRef,
2076
+ beginBatch: undoRedo.beginBatch,
2077
+ endBatch: undoRedo.endBatch
2078
+ });
2037
2079
  const { handleGridKeyDown } = useKeyboardNavigation({
2038
2080
  data: { items, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, getRowId },
2039
2081
  state: { activeCell, selectionRange, editingCell, selectedRowIds: rowSelectionResult.selectedRowIds },
@@ -2054,22 +2096,11 @@ function useDataGridState(params) {
2054
2096
  editable: editableProp,
2055
2097
  onCellValueChanged,
2056
2098
  rowSelection: rowSelectionProp,
2057
- wrapperRef
2099
+ wrapperRef,
2100
+ fillDown,
2101
+ onKeyDown: computed(() => props.value.onKeyDown)
2058
2102
  }
2059
2103
  });
2060
- const { handleFillHandleMouseDown } = useFillHandle({
2061
- items,
2062
- visibleCols,
2063
- editable: editableProp,
2064
- onCellValueChanged,
2065
- selectionRange,
2066
- setSelectionRange,
2067
- setActiveCell,
2068
- colOffset,
2069
- wrapperRef,
2070
- beginBatch: undoRedo.beginBatch,
2071
- endBatch: undoRedo.endBatch
2072
- });
2073
2104
  const {
2074
2105
  containerWidth,
2075
2106
  minTableWidth,
@@ -3050,17 +3081,22 @@ function useColumnReorder(params) {
3050
3081
  };
3051
3082
  return { isDragging, dropIndicatorX, handleHeaderMouseDown };
3052
3083
  }
3084
+ var DEFAULT_PASSTHROUGH_THRESHOLD = 100;
3053
3085
  function useVirtualScroll(params) {
3054
- const { totalRows, rowHeight, enabled, overscan = 5 } = params;
3086
+ const { totalRows, rowHeight, enabled, overscan = 5, threshold = DEFAULT_PASSTHROUGH_THRESHOLD } = params;
3087
+ onMounted(() => {
3088
+ validateVirtualScrollConfig({ enabled: enabled.value, rowHeight });
3089
+ });
3055
3090
  const containerRef = ref(null);
3056
3091
  const scrollTop = ref(0);
3057
3092
  const containerHeight = ref(0);
3058
3093
  let rafId = 0;
3059
3094
  let resizeObserver;
3060
3095
  let prevObservedEl = null;
3096
+ const isActive = computed(() => enabled.value && totalRows.value >= threshold);
3061
3097
  const visibleRange = computed(() => {
3062
- if (!enabled.value) {
3063
- return { startIndex: 0, endIndex: totalRows.value - 1, offsetTop: 0, offsetBottom: 0 };
3098
+ if (!isActive.value) {
3099
+ return { startIndex: 0, endIndex: Math.max(0, totalRows.value - 1), offsetTop: 0, offsetBottom: 0 };
3064
3100
  }
3065
3101
  return computeVisibleRange(
3066
3102
  scrollTop.value,
@@ -3278,10 +3314,7 @@ function createDataGridTable(ui) {
3278
3314
  visibleCols,
3279
3315
  hasCheckboxCol,
3280
3316
  hasRowNumbersCol,
3281
- colOffset: _colOffset,
3282
- containerWidth,
3283
- minTableWidth,
3284
- desiredTableWidth
3317
+ colOffset: _colOffset
3285
3318
  } = layout;
3286
3319
  const currentPage = p.currentPage ?? 1;
3287
3320
  const pageSize = p.pageSize ?? 25;
@@ -3309,7 +3342,7 @@ function createDataGridTable(ui) {
3309
3342
  isDragging: _isDragging
3310
3343
  } = interaction;
3311
3344
  const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
3312
- const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError: _onCellError } = viewModels;
3345
+ const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError } = viewModels;
3313
3346
  const items = p.items;
3314
3347
  const getRowId = p.getRowId;
3315
3348
  const layoutMode = p.layoutMode ?? "fill";
@@ -3321,7 +3354,6 @@ function createDataGridTable(ui) {
3321
3354
  const ariaLabel = p["aria-label"];
3322
3355
  const ariaLabelledBy = p["aria-labelledby"];
3323
3356
  const fitToContent = layoutMode === "content";
3324
- const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
3325
3357
  const headerRows = headerRowsComputed.value;
3326
3358
  const editCallbacks = { commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit };
3327
3359
  const interactionHandlers = { handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu };
@@ -3333,6 +3365,16 @@ function createDataGridTable(ui) {
3333
3365
  rowSel.updateSelection(selectedRowIds.has(rowId) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([rowId]));
3334
3366
  };
3335
3367
  const renderCellContent = (item, col, rowIndex, colIdx) => {
3368
+ try {
3369
+ return renderCellContentInner(item, col, rowIndex, colIdx);
3370
+ } catch (err) {
3371
+ if (onCellError) {
3372
+ onCellError(err instanceof Error ? err : new Error(String(err)), void 0);
3373
+ }
3374
+ return "";
3375
+ }
3376
+ };
3377
+ const renderCellContentInner = (item, col, rowIndex, colIdx) => {
3336
3378
  const descriptor = getCellRenderDescriptor(item, col, rowIndex, colIdx, cellDescriptorInput);
3337
3379
  if (descriptor.mode === "editing-inline") {
3338
3380
  const editorProps = buildInlineEditorProps(item, col, descriptor, editCallbacks);
@@ -3408,7 +3450,7 @@ function createDataGridTable(ui) {
3408
3450
  minHeight: isLoading && items.length === 0 ? "200px" : "0",
3409
3451
  width: fitToContent ? "fit-content" : "100%",
3410
3452
  maxWidth: "100%",
3411
- overflowX: suppressHorizontalScroll ? "hidden" : allowOverflowX ? "auto" : "hidden",
3453
+ overflowX: suppressHorizontalScroll ? "hidden" : "auto",
3412
3454
  overflowY: "auto",
3413
3455
  backgroundColor: "#fff",
3414
3456
  willChange: "scroll-position"
@@ -3430,11 +3472,11 @@ function createDataGridTable(ui) {
3430
3472
  onMousedown: onWrapperMousedown,
3431
3473
  onKeydown: handleGridKeyDown,
3432
3474
  onContextmenu,
3433
- "data-overflow-x": allowOverflowX ? "true" : "false",
3475
+ "data-suppress-scroll": suppressHorizontalScroll ? "true" : void 0,
3434
3476
  style: wrapperStyle
3435
3477
  }, [
3436
3478
  h("div", { class: "ogrid-scroll-wrapper" }, [
3437
- h("div", { style: { minWidth: allowOverflowX ? `${minTableWidth}px` : void 0 } }, [
3479
+ h("div", { style: { width: "max-content", minWidth: "100%", overflow: "clip" } }, [
3438
3480
  h("div", {
3439
3481
  ref: (el) => {
3440
3482
  tableContainerRef.value = el;
@@ -3454,7 +3496,8 @@ function createDataGridTable(ui) {
3454
3496
  tableRef.value = el;
3455
3497
  },
3456
3498
  class: "ogrid-table",
3457
- style: { minWidth: `${minTableWidth}px` }
3499
+ role: "grid",
3500
+ style: { width: "100%", minWidth: "max-content" }
3458
3501
  }, [
3459
3502
  // Header
3460
3503
  h(
@@ -3531,6 +3574,8 @@ function createDataGridTable(ui) {
3531
3574
  if (!cell.columnDef) return null;
3532
3575
  const col = cell.columnDef;
3533
3576
  const { classes: headerClasses, style: headerStyle } = getHeaderClassAndStyle(col);
3577
+ const isSorted = p.sortBy === col.columnId;
3578
+ const ariaSort = isSorted ? p.sortDirection === "asc" ? "ascending" : "descending" : void 0;
3534
3579
  return h("th", {
3535
3580
  key: col.columnId,
3536
3581
  scope: "col",
@@ -3538,6 +3583,7 @@ function createDataGridTable(ui) {
3538
3583
  rowSpan: headerRows.length > 1 ? headerRows.length - rowIdx : void 0,
3539
3584
  class: headerClasses,
3540
3585
  style: headerStyle,
3586
+ "aria-sort": ariaSort,
3541
3587
  onMousedown: (e) => handleReorderMouseDown(col.columnId, e)
3542
3588
  }, [
3543
3589
  h("div", { class: "ogrid-header-content" }, [
@@ -3586,6 +3632,7 @@ function createDataGridTable(ui) {
3586
3632
  rows.push(h("tr", {
3587
3633
  key: rowIdStr,
3588
3634
  "data-row-id": rowIdStr,
3635
+ "aria-selected": isSelected || void 0,
3589
3636
  onClick: handleSingleRowClick,
3590
3637
  style: { cursor: rowSelection === "single" ? "pointer" : void 0 }
3591
3638
  }, [
@@ -1,29 +1,33 @@
1
1
  /* OGrid Shared Layout Styles — consumed by vue-vuetify and vue-primevue */
2
2
 
3
- /* Remove focus outline from scrollable wrapper (keyboard nav is handled via cell outlines) */
3
+ /* Remove focus outline from scrollable wrapper (keyboard nav is handled via cell outlines).
4
+ [role="region"][tabindex] has attribute-selector specificity (0,2,0) which beats any
5
+ framework's single-class or element rule — no !important needed. */
4
6
  [role="region"][tabindex="0"] {
5
- outline: none !important;
7
+ outline: none;
6
8
  }
7
9
 
8
- /* Cell selection highlighting */
9
- .ogrid-cell-in-range {
10
- background: var(--ogrid-bg-range, rgba(33, 115, 70, 0.12)) !important;
10
+ /* Cell selection highlighting.
11
+ Qualify with .ogrid-outer-container (specificity 0,2,0) to beat row-level hover backgrounds. */
12
+ .ogrid-outer-container .ogrid-cell-in-range {
13
+ background: var(--ogrid-bg-range, rgba(33, 115, 70, 0.12));
11
14
  }
12
15
 
13
16
  /* Cut range highlighting */
14
- .ogrid-cell-cut {
15
- background: var(--ogrid-hover-bg) !important;
17
+ .ogrid-outer-container .ogrid-cell-cut {
18
+ background: var(--ogrid-hover-bg);
16
19
  opacity: 0.7;
17
20
  }
18
21
 
19
- /* Drag-range highlight applied via DOM attributes during drag (bypasses Vue for performance) */
20
- [data-drag-range] {
21
- background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12)) !important;
22
+ /* Drag-range highlight applied via DOM attributes during drag (bypasses Vue for performance).
23
+ Qualifier .ogrid-outer-container raises specificity to 0,2,0, enough to beat row-hover rules. */
24
+ .ogrid-outer-container [data-drag-range] {
25
+ background: var(--ogrid-range-bg, rgba(33, 115, 70, 0.12));
22
26
  }
23
27
 
24
28
  /* Anchor cell during drag: white/transparent background (like Excel) */
25
- [data-drag-anchor] {
26
- background: var(--ogrid-bg) !important;
29
+ .ogrid-outer-container [data-drag-anchor] {
30
+ background: var(--ogrid-bg);
27
31
  }
28
32
 
29
33
  /* === Layout === */
@@ -67,7 +71,7 @@
67
71
  /* === Header === */
68
72
 
69
73
  .ogrid-thead {
70
- z-index: 8;
74
+ z-index: var(--ogrid-z-thead, 8);
71
75
  background-color: var(--ogrid-header-bg);
72
76
  }
73
77
 
@@ -78,7 +82,7 @@
78
82
  .ogrid-header-cell {
79
83
  font-weight: 600;
80
84
  background-color: var(--ogrid-header-bg);
81
- z-index: 8;
85
+ z-index: var(--ogrid-z-thead, 8);
82
86
  }
83
87
 
84
88
  .ogrid-sticky-header .ogrid-header-cell {
@@ -87,14 +91,14 @@
87
91
  }
88
92
 
89
93
  .ogrid-header-cell--pinned-left {
90
- z-index: 10;
94
+ z-index: var(--ogrid-z-pinned-header, 10);
91
95
  will-change: transform;
92
96
  border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
93
97
  box-shadow: 2px 0 4px -1px rgba(0, 0, 0, 0.1);
94
98
  }
95
99
 
96
100
  .ogrid-header-cell--pinned-right {
97
- z-index: 10;
101
+ z-index: var(--ogrid-z-pinned-header, 10);
98
102
  will-change: transform;
99
103
  border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
100
104
  box-shadow: -2px 0 4px -1px rgba(0, 0, 0, 0.1);
@@ -189,7 +193,7 @@
189
193
 
190
194
  .ogrid-data-cell--pinned-left {
191
195
  position: sticky;
192
- z-index: 6;
196
+ z-index: var(--ogrid-z-pinned, 6);
193
197
  background-color: var(--ogrid-bg);
194
198
  will-change: transform;
195
199
  border-right: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
@@ -198,7 +202,7 @@
198
202
 
199
203
  .ogrid-data-cell--pinned-right {
200
204
  position: sticky;
201
- z-index: 6;
205
+ z-index: var(--ogrid-z-pinned, 6);
202
206
  background-color: var(--ogrid-bg);
203
207
  will-change: transform;
204
208
  border-left: 1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12));
@@ -211,7 +215,7 @@
211
215
  display: flex;
212
216
  align-items: center;
213
217
  min-width: 0;
214
- padding: 6px 10px;
218
+ padding: var(--ogrid-cell-padding, 6px 10px);
215
219
  box-sizing: border-box;
216
220
  overflow: hidden;
217
221
  text-overflow: ellipsis;
@@ -237,7 +241,7 @@
237
241
  .ogrid-cell-content--active {
238
242
  outline: 2px solid var(--ogrid-selection, #217346);
239
243
  outline-offset: -1px;
240
- z-index: 2;
244
+ z-index: var(--ogrid-z-active-cell, 2);
241
245
  position: relative;
242
246
  overflow: visible;
243
247
  }
@@ -252,7 +256,7 @@
252
256
  box-sizing: border-box;
253
257
  outline: 2px solid var(--ogrid-selection-color, #217346);
254
258
  outline-offset: -1px;
255
- z-index: 2;
259
+ z-index: var(--ogrid-z-active-cell, 2);
256
260
  position: relative;
257
261
  background: var(--ogrid-bg, #fff);
258
262
  overflow: visible;
@@ -272,7 +276,7 @@
272
276
  border-radius: 1px;
273
277
  cursor: crosshair;
274
278
  pointer-events: auto;
275
- z-index: 3;
279
+ z-index: var(--ogrid-z-fill-handle, 3);
276
280
  }
277
281
 
278
282
  /* === Resize handle === */
@@ -296,7 +300,7 @@
296
300
  width: 3px;
297
301
  background: var(--ogrid-primary, #217346);
298
302
  pointer-events: none;
299
- z-index: 100;
303
+ z-index: var(--ogrid-z-drop-indicator, 100);
300
304
  }
301
305
 
302
306
  /* === Empty state === */
@@ -324,7 +328,7 @@
324
328
  .ogrid-loading-overlay {
325
329
  position: absolute;
326
330
  inset: 0;
327
- z-index: 2;
331
+ z-index: var(--ogrid-z-loading, 2);
328
332
  display: flex;
329
333
  align-items: center;
330
334
  justify-content: center;
@@ -6,6 +6,13 @@
6
6
 
7
7
  /* ─── Light Theme (default) ─── */
8
8
  :where(:root) {
9
+ /* Cell padding — override for row density:
10
+ --ogrid-cell-padding : shorthand (default 6px 10px)
11
+ --ogrid-cell-padding-vertical : vertical only (default 6px)
12
+ --ogrid-cell-padding-horizontal: horizontal only (default 10px) */
13
+ --ogrid-cell-padding: 6px 10px;
14
+ --ogrid-cell-padding-vertical: 6px;
15
+ --ogrid-cell-padding-horizontal: 10px;
9
16
  --ogrid-bg: #ffffff;
10
17
  --ogrid-fg: rgba(0, 0, 0, 0.87);
11
18
  --ogrid-fg-secondary: rgba(0, 0, 0, 0.6);
@@ -25,6 +25,8 @@ export interface UseFillHandleResult {
25
25
  startCol: number;
26
26
  } | null) => void;
27
27
  handleFillHandleMouseDown: (e: MouseEvent) => void;
28
+ /** Fill the current selection down from the top row (Ctrl+D). No-op if no selection or editable=false. */
29
+ fillDown: () => void;
28
30
  }
29
31
  /**
30
32
  * Manages Excel-style fill handle drag-to-fill for cell ranges.
@@ -37,6 +37,8 @@ export interface UseKeyboardNavigationParams<T> {
37
37
  rowSelection: Ref<RowSelectionMode>;
38
38
  wrapperRef: MaybeShallowRef<HTMLElement | null>;
39
39
  scrollToRow?: (index: number, align?: 'start' | 'center' | 'end') => void;
40
+ fillDown?: () => void;
41
+ onKeyDown?: Ref<((event: KeyboardEvent) => void) | undefined>;
40
42
  };
41
43
  }
42
44
  export interface UseKeyboardNavigationResult {
@@ -5,6 +5,11 @@ export interface UseVirtualScrollParams {
5
5
  rowHeight: number;
6
6
  enabled: Ref<boolean>;
7
7
  overscan?: number;
8
+ /**
9
+ * Minimum row count before virtual scrolling activates. Default: 100.
10
+ * When totalRows < threshold, all rows render without virtualization.
11
+ */
12
+ threshold?: number;
8
13
  }
9
14
  export interface UseVirtualScrollResult {
10
15
  containerRef: Ref<HTMLElement | null>;
@@ -171,4 +171,6 @@ export interface IOGridDataGridProps<T> {
171
171
  density?: 'compact' | 'normal' | 'comfortable';
172
172
  'aria-label'?: string;
173
173
  'aria-labelledby'?: string;
174
+ /** Custom keydown handler. Called before grid's built-in handling. Call event.preventDefault() to suppress grid default. */
175
+ onKeyDown?: (event: KeyboardEvent) => void;
174
176
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-vue",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "OGrid Vue – Vue 3 composables, headless components, and utilities for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -36,7 +36,7 @@
36
36
  "node": ">=18"
37
37
  },
38
38
  "dependencies": {
39
- "@alaarab/ogrid-core": "2.1.9"
39
+ "@alaarab/ogrid-core": "2.1.11"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "vue": "^3.3.0"