@alaarab/ogrid-react 2.0.15 → 2.0.16

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.
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import { useInlineCellEditorState, useRichSelectState } from '../hooks';
3
+ import { useInlineCellEditorState, useRichSelectState, useSelectState } from '../hooks';
4
4
  // ── Shared editor style constants (used across all 3 UI packages) ──
5
5
  export const editorWrapperStyle = {
6
6
  width: '100%',
@@ -63,49 +63,100 @@ export const selectEditorStyle = {
63
63
  cursor: 'pointer',
64
64
  outline: 'none',
65
65
  };
66
+ export const selectDisplayStyle = {
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ justifyContent: 'space-between',
70
+ width: '100%',
71
+ cursor: 'pointer',
72
+ fontSize: '13px',
73
+ color: 'inherit',
74
+ };
75
+ export const selectChevronStyle = {
76
+ marginLeft: 4,
77
+ fontSize: '10px',
78
+ opacity: 0.5,
79
+ };
66
80
  /**
67
- * Base inline cell editor with shared logic for all editor types except checkbox and select
68
- * (which are framework-specific). Used by all 3 UI packages to avoid duplication.
81
+ * Base inline cell editor with shared logic for all editor types except checkbox
82
+ * (which is framework-specific). Used by all 3 UI packages to avoid duplication.
69
83
  *
70
- * Usage:
71
- * - Radix: Pass Radix Checkbox/native select via render props
72
- * - Fluent: Pass Fluent Checkbox/Select via render props
73
- * - Material: Pass MUI Checkbox/Select via render props
84
+ * Text, date, select, and richSelect editors are fully shared.
85
+ * Checkbox is delegated via renderCheckbox render prop.
74
86
  */
75
87
  export function BaseInlineCellEditor(props) {
76
- const { value, column, editorType, onCommit, onCancel, renderCheckbox, renderSelect } = props;
88
+ const { value, column, editorType, onCommit, onCancel, renderCheckbox } = props;
77
89
  const wrapperRef = React.useRef(null);
78
90
  const { localValue, setLocalValue, handleKeyDown, handleBlur, commit, cancel } = useInlineCellEditorState({ value, editorType, onCommit, onCancel });
79
- const richSelectValues = column.cellEditorParams?.values ?? [];
80
- const richSelectFormatValue = column.cellEditorParams?.formatValue;
91
+ const editorValues = column.cellEditorParams?.values ?? [];
92
+ const editorFormatValue = column.cellEditorParams?.formatValue;
81
93
  const richSelect = useRichSelectState({
82
- values: richSelectValues,
83
- formatValue: richSelectFormatValue,
94
+ values: editorValues,
95
+ formatValue: editorFormatValue,
96
+ initialValue: value,
97
+ onCommit,
98
+ onCancel,
99
+ });
100
+ const selectState = useSelectState({
101
+ values: editorValues,
102
+ formatValue: editorFormatValue,
84
103
  initialValue: value,
85
104
  onCommit,
86
105
  onCancel,
87
106
  });
107
+ // Fixed dropdown positioning to escape ancestor overflow clipping (.tableWrapper)
108
+ const [fixedDropdownStyle, setFixedDropdownStyle] = React.useState(null);
109
+ React.useLayoutEffect(() => {
110
+ if (editorType !== 'select' && editorType !== 'richSelect')
111
+ return;
112
+ const wrapper = wrapperRef.current;
113
+ if (!wrapper)
114
+ return;
115
+ const rect = wrapper.getBoundingClientRect();
116
+ const maxH = 200;
117
+ const spaceBelow = window.innerHeight - rect.bottom;
118
+ const flipUp = spaceBelow < maxH && rect.top > spaceBelow;
119
+ setFixedDropdownStyle({
120
+ position: 'fixed',
121
+ ...(flipUp ? { bottom: window.innerHeight - rect.top } : { top: rect.bottom }),
122
+ left: rect.left,
123
+ width: rect.width,
124
+ maxHeight: maxH,
125
+ overflowY: 'auto',
126
+ background: 'var(--ogrid-bg, #fff)',
127
+ border: '1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))',
128
+ zIndex: 9999,
129
+ boxShadow: 'var(--ogrid-shadow, 0 4px 16px rgba(0,0,0,0.2))',
130
+ });
131
+ }, [editorType]);
132
+ const computedDropdownStyle = fixedDropdownStyle ?? richSelectDropdownStyle;
88
133
  React.useEffect(() => {
89
- const input = wrapperRef.current?.querySelector('input');
134
+ const wrapper = wrapperRef.current;
135
+ if (!wrapper)
136
+ return;
137
+ const input = wrapper.querySelector('input');
90
138
  if (input) {
91
139
  input.focus();
92
140
  // Select all text for easy replacement (like Excel)
93
141
  input.select();
94
142
  }
143
+ else {
144
+ // Focus the wrapper for keyboard events (select editor has no input)
145
+ wrapper.focus();
146
+ }
95
147
  }, []);
96
148
  // Rich select (shared across all frameworks)
97
149
  if (editorType === 'richSelect') {
98
- return (_jsxs("div", { ref: wrapperRef, style: richSelectWrapperStyle, children: [_jsx("input", { type: "text", value: richSelect.searchText, onChange: (e) => richSelect.setSearchText(e.target.value), onKeyDown: richSelect.handleKeyDown, placeholder: "Search...", autoFocus: true, style: editorInputStyle }), _jsxs("div", { style: richSelectDropdownStyle, role: "listbox", children: [richSelect.filteredValues.map((v, i) => (_jsx("div", { role: "option", "aria-selected": i === richSelect.highlightedIndex, onClick: () => richSelect.selectValue(v), style: i === richSelect.highlightedIndex ? richSelectOptionHighlightedStyle : richSelectOptionStyle, children: richSelect.getDisplayText(v) }, String(v)))), richSelect.filteredValues.length === 0 && (_jsx("div", { style: richSelectNoMatchesStyle, children: "No matches" }))] })] }));
150
+ return (_jsxs("div", { ref: wrapperRef, style: richSelectWrapperStyle, children: [_jsx("input", { type: "text", value: richSelect.searchText, onChange: (e) => richSelect.setSearchText(e.target.value), onKeyDown: richSelect.handleKeyDown, placeholder: "Search...", autoFocus: true, style: editorInputStyle }), _jsxs("div", { style: computedDropdownStyle, role: "listbox", children: [richSelect.filteredValues.map((v, i) => (_jsx("div", { role: "option", "aria-selected": i === richSelect.highlightedIndex, onClick: () => richSelect.selectValue(v), style: i === richSelect.highlightedIndex ? richSelectOptionHighlightedStyle : richSelectOptionStyle, children: richSelect.getDisplayText(v) }, String(v)))), richSelect.filteredValues.length === 0 && (_jsx("div", { style: richSelectNoMatchesStyle, children: "No matches" }))] })] }));
99
151
  }
100
152
  // Checkbox (framework-specific)
101
153
  if (editorType === 'checkbox') {
102
154
  const checked = value === true;
103
155
  return _jsx(_Fragment, { children: renderCheckbox(checked, (val) => commit(val), cancel) });
104
156
  }
105
- // Select (framework-specific)
157
+ // Select (custom dropdown, shared across all frameworks)
106
158
  if (editorType === 'select') {
107
- const values = column.cellEditorParams?.values ?? [];
108
- return _jsx(_Fragment, { children: renderSelect(value, values, commit, cancel) });
159
+ return (_jsxs("div", { ref: wrapperRef, style: richSelectWrapperStyle, onKeyDown: selectState.handleKeyDown, tabIndex: 0, children: [_jsxs("div", { style: selectDisplayStyle, children: [_jsx("span", { children: selectState.getDisplayText(value) }), _jsx("span", { style: selectChevronStyle, children: "\u25BE" })] }), _jsx("div", { style: computedDropdownStyle, ref: selectState.dropdownRef, role: "listbox", children: editorValues.map((v, i) => (_jsx("div", { role: "option", "aria-selected": i === selectState.highlightedIndex, onClick: () => selectState.selectValue(v), style: i === selectState.highlightedIndex ? richSelectOptionHighlightedStyle : richSelectOptionStyle, children: selectState.getDisplayText(v) }, String(v)))) })] }));
109
160
  }
110
161
  // Date editor (shared across all frameworks)
111
162
  if (editorType === 'date') {
@@ -22,6 +22,7 @@ export { useColumnResize } from './useColumnResize';
22
22
  export { useColumnPinning } from './useColumnPinning';
23
23
  export { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
24
24
  export { useRichSelectState } from './useRichSelectState';
25
+ export { useSelectState } from './useSelectState';
25
26
  export { useSideBarState } from './useSideBarState';
26
27
  export { useTableLayout } from './useTableLayout';
27
28
  export { useColumnReorder } from './useColumnReorder';
@@ -1,27 +1,12 @@
1
1
  import { useState, useCallback, useRef, useEffect } from 'react';
2
2
  import { normalizeSelectionRange } from '../types';
3
- /** Compares two selection ranges by value. */
4
- function rangesEqual(a, b) {
5
- if (a === b)
6
- return true;
7
- if (!a || !b)
8
- return false;
9
- return a.startRow === b.startRow && a.endRow === b.endRow &&
10
- a.startCol === b.startCol && a.endCol === b.endCol;
11
- }
3
+ import { rangesEqual, computeAutoScrollSpeed } from '../utils';
12
4
  /** DOM attribute names used for drag-range highlighting (bypasses React). */
13
5
  const DRAG_ATTR = 'data-drag-range';
14
6
  const DRAG_ANCHOR_ATTR = 'data-drag-anchor';
15
7
  /** Auto-scroll config */
16
8
  const AUTO_SCROLL_EDGE = 40; // px from wrapper edge to trigger
17
- const AUTO_SCROLL_MIN_SPEED = 2;
18
- const AUTO_SCROLL_MAX_SPEED = 20;
19
9
  const AUTO_SCROLL_INTERVAL = 16; // ~60fps
20
- /** Compute scroll speed proportional to distance past the edge, capped. */
21
- function autoScrollSpeed(distance) {
22
- const t = Math.min(distance / AUTO_SCROLL_EDGE, 1);
23
- return AUTO_SCROLL_MIN_SPEED + t * (AUTO_SCROLL_MAX_SPEED - AUTO_SCROLL_MIN_SPEED);
24
- }
25
10
  /**
26
11
  * Manages cell selection range with drag-to-select and select-all support.
27
12
  * @param params - Row/col counts, active cell setter, and wrapper ref for auto-scroll.
@@ -213,16 +198,16 @@ export function useCellSelection(params) {
213
198
  let dx = 0;
214
199
  let dy = 0;
215
200
  if (pos.cy < rect.top + AUTO_SCROLL_EDGE) {
216
- dy = -autoScrollSpeed(rect.top + AUTO_SCROLL_EDGE - pos.cy);
201
+ dy = -computeAutoScrollSpeed(rect.top + AUTO_SCROLL_EDGE - pos.cy);
217
202
  }
218
203
  else if (pos.cy > rect.bottom - AUTO_SCROLL_EDGE) {
219
- dy = autoScrollSpeed(pos.cy - (rect.bottom - AUTO_SCROLL_EDGE));
204
+ dy = computeAutoScrollSpeed(pos.cy - (rect.bottom - AUTO_SCROLL_EDGE));
220
205
  }
221
206
  if (pos.cx < rect.left + AUTO_SCROLL_EDGE) {
222
- dx = -autoScrollSpeed(rect.left + AUTO_SCROLL_EDGE - pos.cx);
207
+ dx = -computeAutoScrollSpeed(rect.left + AUTO_SCROLL_EDGE - pos.cx);
223
208
  }
224
209
  else if (pos.cx > rect.right - AUTO_SCROLL_EDGE) {
225
- dx = autoScrollSpeed(pos.cx - (rect.right - AUTO_SCROLL_EDGE));
210
+ dx = computeAutoScrollSpeed(pos.cx - (rect.right - AUTO_SCROLL_EDGE));
226
211
  }
227
212
  if (dx === 0 && dy === 0) {
228
213
  stopAutoScroll();
@@ -241,13 +226,13 @@ export function useCellSelection(params) {
241
226
  let sdx = 0;
242
227
  let sdy = 0;
243
228
  if (p.cy < r.top + AUTO_SCROLL_EDGE)
244
- sdy = -autoScrollSpeed(r.top + AUTO_SCROLL_EDGE - p.cy);
229
+ sdy = -computeAutoScrollSpeed(r.top + AUTO_SCROLL_EDGE - p.cy);
245
230
  else if (p.cy > r.bottom - AUTO_SCROLL_EDGE)
246
- sdy = autoScrollSpeed(p.cy - (r.bottom - AUTO_SCROLL_EDGE));
231
+ sdy = computeAutoScrollSpeed(p.cy - (r.bottom - AUTO_SCROLL_EDGE));
247
232
  if (p.cx < r.left + AUTO_SCROLL_EDGE)
248
- sdx = -autoScrollSpeed(r.left + AUTO_SCROLL_EDGE - p.cx);
233
+ sdx = -computeAutoScrollSpeed(r.left + AUTO_SCROLL_EDGE - p.cx);
249
234
  else if (p.cx > r.right - AUTO_SCROLL_EDGE)
250
- sdx = autoScrollSpeed(p.cx - (r.right - AUTO_SCROLL_EDGE));
235
+ sdx = computeAutoScrollSpeed(p.cx - (r.right - AUTO_SCROLL_EDGE));
251
236
  if (sdx === 0 && sdy === 0) {
252
237
  stopAutoScroll();
253
238
  return;
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
- import { getCellValue, parseValue } from '../utils';
2
+ import { getCellValue, parseValue, formatSelectionAsTsv, parseTsvClipboard } from '../utils';
3
3
  import { normalizeSelectionRange } from '../types';
4
4
  import { useLatestRef } from './useLatestRef';
5
5
  /**
@@ -34,23 +34,7 @@ export function useClipboard(params) {
34
34
  if (range == null)
35
35
  return;
36
36
  const norm = normalizeSelectionRange(range);
37
- const items = itemsRef.current;
38
- const visibleCols = visibleColsRef.current;
39
- const rows = [];
40
- for (let r = norm.startRow; r <= norm.endRow; r++) {
41
- const cells = [];
42
- for (let c = norm.startCol; c <= norm.endCol; c++) {
43
- if (r >= items.length || c >= visibleCols.length)
44
- break;
45
- const item = items[r];
46
- const col = visibleCols[c];
47
- const raw = getCellValue(item, col);
48
- const val = col.valueFormatter ? col.valueFormatter(raw, item) : raw;
49
- cells.push(val != null && val !== '' ? String(val).replace(/\t/g, ' ').replace(/\n/g, ' ') : '');
50
- }
51
- rows.push(cells.join('\t'));
52
- }
53
- const tsv = rows.join('\r\n');
37
+ const tsv = formatSelectionAsTsv(itemsRef.current, visibleColsRef.current, norm);
54
38
  internalClipboardRef.current = tsv;
55
39
  setCopyRange(norm);
56
40
  void navigator.clipboard.writeText(tsv).catch(() => { });
@@ -92,10 +76,10 @@ export function useClipboard(params) {
92
76
  const anchorCol = norm ? norm.startCol : 0;
93
77
  const items = itemsRef.current;
94
78
  const visibleCols = visibleColsRef.current;
95
- const lines = text.split(/\r?\n/).filter((l) => l.length > 0);
79
+ const parsedRows = parseTsvClipboard(text);
96
80
  beginBatch?.();
97
- for (let r = 0; r < lines.length; r++) {
98
- const cells = lines[r].split('\t');
81
+ for (let r = 0; r < parsedRows.length; r++) {
82
+ const cells = parsedRows[r];
99
83
  for (let c = 0; c < cells.length; c++) {
100
84
  const targetRow = anchorRow + r;
101
85
  const targetCol = anchorCol + c;
@@ -1,32 +1,6 @@
1
1
  import { useCallback, useRef } from 'react';
2
2
  import { normalizeSelectionRange } from '../types';
3
- import { getCellValue, parseValue } from '../utils';
4
- /**
5
- * Excel-style Ctrl+Arrow: find the target position along a 1D axis.
6
- * - Non-empty current + non-empty next → scan through non-empties, stop at last before empty/edge.
7
- * - Otherwise → skip empties, land on next non-empty or edge.
8
- */
9
- function findCtrlTarget(pos, edge, step, isEmpty) {
10
- if (pos === edge)
11
- return pos;
12
- const next = pos + step;
13
- if (!isEmpty(pos) && !isEmpty(next)) {
14
- let p = next;
15
- while (p !== edge) {
16
- if (isEmpty(p + step))
17
- return p;
18
- p += step;
19
- }
20
- return edge;
21
- }
22
- let p = next;
23
- while (p !== edge) {
24
- if (!isEmpty(p))
25
- return p;
26
- p += step;
27
- }
28
- return edge;
29
- }
3
+ import { getCellValue, parseValue, findCtrlArrowTarget, computeTabNavigation } from '../utils';
30
4
  /**
31
5
  * Handles all keyboard navigation, shortcuts, and cell editing triggers for the grid.
32
6
  * @param params - Grouped data, state, handlers, and feature flags for keyboard interactions.
@@ -100,7 +74,7 @@ export function useKeyboardNavigation(params) {
100
74
  e.preventDefault();
101
75
  const ctrl = e.ctrlKey || e.metaKey;
102
76
  const newRow = ctrl
103
- ? findCtrlTarget(rowIndex, maxRowIndex, 1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
77
+ ? findCtrlArrowTarget(rowIndex, maxRowIndex, 1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
104
78
  : Math.min(rowIndex + 1, maxRowIndex);
105
79
  if (shift) {
106
80
  setSelectionRange(normalizeSelectionRange({
@@ -125,7 +99,7 @@ export function useKeyboardNavigation(params) {
125
99
  e.preventDefault();
126
100
  const ctrl = e.ctrlKey || e.metaKey;
127
101
  const newRowUp = ctrl
128
- ? findCtrlTarget(rowIndex, 0, -1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
102
+ ? findCtrlArrowTarget(rowIndex, 0, -1, (r) => isEmptyAt(r, Math.max(0, dataColIndex)))
129
103
  : Math.max(rowIndex - 1, 0);
130
104
  if (shift) {
131
105
  setSelectionRange(normalizeSelectionRange({
@@ -151,7 +125,7 @@ export function useKeyboardNavigation(params) {
151
125
  const ctrl = e.ctrlKey || e.metaKey;
152
126
  let newCol;
153
127
  if (ctrl && dataColIndex >= 0) {
154
- newCol = findCtrlTarget(dataColIndex, visibleCols.length - 1, 1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
128
+ newCol = findCtrlArrowTarget(dataColIndex, visibleCols.length - 1, 1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
155
129
  }
156
130
  else {
157
131
  newCol = Math.min(columnIndex + 1, maxColIndex);
@@ -181,7 +155,7 @@ export function useKeyboardNavigation(params) {
181
155
  const ctrl = e.ctrlKey || e.metaKey;
182
156
  let newColLeft;
183
157
  if (ctrl && dataColIndex >= 0) {
184
- newColLeft = findCtrlTarget(dataColIndex, 0, -1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
158
+ newColLeft = findCtrlArrowTarget(dataColIndex, 0, -1, (c) => isEmptyAt(rowIndex, c)) + colOffset;
185
159
  }
186
160
  else {
187
161
  newColLeft = Math.max(columnIndex - 1, colOffset);
@@ -208,26 +182,7 @@ export function useKeyboardNavigation(params) {
208
182
  }
209
183
  case 'Tab': {
210
184
  e.preventDefault();
211
- let newRowTab = rowIndex;
212
- let newColTab = columnIndex;
213
- if (e.shiftKey) {
214
- if (columnIndex > colOffset) {
215
- newColTab = columnIndex - 1;
216
- }
217
- else if (rowIndex > 0) {
218
- newRowTab = rowIndex - 1;
219
- newColTab = maxColIndex;
220
- }
221
- }
222
- else {
223
- if (columnIndex < maxColIndex) {
224
- newColTab = columnIndex + 1;
225
- }
226
- else if (rowIndex < maxRowIndex) {
227
- newRowTab = rowIndex + 1;
228
- newColTab = colOffset;
229
- }
230
- }
185
+ const { rowIndex: newRowTab, columnIndex: newColTab } = computeTabNavigation(rowIndex, columnIndex, maxRowIndex, maxColIndex, colOffset, e.shiftKey);
231
186
  const newDataColTab = newColTab - colOffset;
232
187
  setSelectionRange({
233
188
  startRow: newRowTab,
@@ -0,0 +1,65 @@
1
+ import { useState, useCallback, useEffect, useRef } from 'react';
2
+ /**
3
+ * Manages select editor state with keyboard navigation (arrow keys, enter, escape).
4
+ * Simpler than useRichSelectState — no search, just a dropdown list.
5
+ */
6
+ export function useSelectState(params) {
7
+ const { values, formatValue, initialValue, onCommit, onCancel } = params;
8
+ const dropdownRef = useRef(null);
9
+ const getDisplayText = useCallback((value) => {
10
+ if (formatValue)
11
+ return formatValue(value);
12
+ return value != null ? String(value) : '';
13
+ }, [formatValue]);
14
+ // Start highlighted on current value
15
+ const initialIndex = values.findIndex((v) => String(v) === String(initialValue));
16
+ const [highlightedIndex, setHighlightedIndex] = useState(Math.max(initialIndex, 0));
17
+ // Scroll highlighted option into view
18
+ useEffect(() => {
19
+ const dropdown = dropdownRef.current;
20
+ if (!dropdown)
21
+ return;
22
+ const highlighted = dropdown.children[highlightedIndex];
23
+ highlighted?.scrollIntoView({ block: 'nearest' });
24
+ }, [highlightedIndex]);
25
+ const selectValue = useCallback((value) => {
26
+ onCommit(value);
27
+ }, [onCommit]);
28
+ const handleKeyDown = useCallback((e) => {
29
+ switch (e.key) {
30
+ case 'ArrowDown':
31
+ e.preventDefault();
32
+ setHighlightedIndex((prev) => Math.min(prev + 1, values.length - 1));
33
+ break;
34
+ case 'ArrowUp':
35
+ e.preventDefault();
36
+ setHighlightedIndex((prev) => Math.max(prev - 1, 0));
37
+ break;
38
+ case 'Enter':
39
+ e.preventDefault();
40
+ e.stopPropagation();
41
+ if (values.length > 0 && highlightedIndex < values.length) {
42
+ selectValue(values[highlightedIndex]);
43
+ }
44
+ break;
45
+ case 'Tab':
46
+ e.preventDefault();
47
+ if (values.length > 0 && highlightedIndex < values.length) {
48
+ selectValue(values[highlightedIndex]);
49
+ }
50
+ break;
51
+ case 'Escape':
52
+ e.preventDefault();
53
+ e.stopPropagation();
54
+ onCancel();
55
+ break;
56
+ }
57
+ }, [values, highlightedIndex, selectValue, onCancel]);
58
+ return {
59
+ highlightedIndex,
60
+ handleKeyDown,
61
+ selectValue,
62
+ getDisplayText,
63
+ dropdownRef,
64
+ };
65
+ }
@@ -1,52 +1,46 @@
1
1
  import { useCallback, useRef, useState } from 'react';
2
+ import { UndoRedoStack } from '../utils';
2
3
  /**
3
4
  * Wraps onCellValueChanged with an undo/redo history stack.
4
5
  * Supports batch operations: changes between beginBatch/endBatch are one undo step.
5
6
  */
6
7
  export function useUndoRedo(params) {
7
8
  const { onCellValueChanged, maxUndoDepth = 100 } = params;
8
- // Each history entry is an array of events (batch). Single edits are [event].
9
- const historyRef = useRef([]);
10
- const redoStackRef = useRef([]);
11
- const batchRef = useRef(null);
9
+ const stackRef = useRef(null);
10
+ if (stackRef.current === null) {
11
+ stackRef.current = new UndoRedoStack(maxUndoDepth);
12
+ }
12
13
  const [historyLength, setHistoryLength] = useState(0);
13
14
  const [redoLength, setRedoLength] = useState(0);
14
15
  const wrapped = useCallback((event) => {
15
16
  if (!onCellValueChanged)
16
17
  return;
17
- if (batchRef.current !== null) {
18
- // Accumulate into the current batch — don't push to history yet
19
- batchRef.current.push(event);
20
- }
21
- else {
22
- historyRef.current = [...historyRef.current, [event]].slice(-maxUndoDepth);
23
- redoStackRef.current = [];
24
- setHistoryLength(historyRef.current.length);
25
- setRedoLength(0);
18
+ const stack = stackRef.current;
19
+ stack.record(event);
20
+ if (!stack.isBatching) {
21
+ setHistoryLength(stack.historyLength);
22
+ setRedoLength(stack.redoLength);
26
23
  }
27
24
  onCellValueChanged(event);
28
- }, [onCellValueChanged, maxUndoDepth]);
25
+ }, [onCellValueChanged]);
29
26
  const beginBatch = useCallback(() => {
30
- batchRef.current = [];
27
+ stackRef.current.beginBatch();
31
28
  }, []);
32
29
  const endBatch = useCallback(() => {
33
- const batch = batchRef.current;
34
- batchRef.current = null;
35
- if (!batch || batch.length === 0)
36
- return;
37
- historyRef.current = [...historyRef.current, batch].slice(-maxUndoDepth);
38
- redoStackRef.current = [];
39
- setHistoryLength(historyRef.current.length);
40
- setRedoLength(0);
41
- }, [maxUndoDepth]);
30
+ const stack = stackRef.current;
31
+ stack.endBatch();
32
+ setHistoryLength(stack.historyLength);
33
+ setRedoLength(stack.redoLength);
34
+ }, []);
42
35
  const undo = useCallback(() => {
43
- if (!onCellValueChanged || historyRef.current.length === 0)
36
+ if (!onCellValueChanged)
37
+ return;
38
+ const stack = stackRef.current;
39
+ const lastBatch = stack.undo();
40
+ if (!lastBatch)
44
41
  return;
45
- const lastBatch = historyRef.current[historyRef.current.length - 1];
46
- historyRef.current = historyRef.current.slice(0, -1);
47
- redoStackRef.current = [...redoStackRef.current, lastBatch];
48
- setHistoryLength(historyRef.current.length);
49
- setRedoLength(redoStackRef.current.length);
42
+ setHistoryLength(stack.historyLength);
43
+ setRedoLength(stack.redoLength);
50
44
  // Revert in reverse order so multi-cell undo is applied correctly
51
45
  for (let i = lastBatch.length - 1; i >= 0; i--) {
52
46
  const ev = lastBatch[i];
@@ -58,13 +52,14 @@ export function useUndoRedo(params) {
58
52
  }
59
53
  }, [onCellValueChanged]);
60
54
  const redo = useCallback(() => {
61
- if (!onCellValueChanged || redoStackRef.current.length === 0)
55
+ if (!onCellValueChanged)
56
+ return;
57
+ const stack = stackRef.current;
58
+ const nextBatch = stack.redo();
59
+ if (!nextBatch)
62
60
  return;
63
- const nextBatch = redoStackRef.current[redoStackRef.current.length - 1];
64
- redoStackRef.current = redoStackRef.current.slice(0, -1);
65
- historyRef.current = [...historyRef.current, nextBatch];
66
- setRedoLength(redoStackRef.current.length);
67
- setHistoryLength(historyRef.current.length);
61
+ setHistoryLength(stack.historyLength);
62
+ setRedoLength(stack.redoLength);
68
63
  // Replay in original order
69
64
  for (const ev of nextBatch) {
70
65
  onCellValueChanged(ev);
package/dist/esm/index.js CHANGED
@@ -2,13 +2,13 @@
2
2
  export { CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from '@alaarab/ogrid-core';
3
3
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
4
  // Hooks
5
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, usePaginationControls, useDataGridTableOrchestration, } from './hooks';
5
+ export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, usePaginationControls, useDataGridTableOrchestration, } from './hooks';
6
6
  // Constants
7
7
  export { GRID_ROOT_STYLE, CURSOR_CELL_STYLE, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, NOOP, STOP_PROPAGATION, } from './constants/domHelpers';
8
8
  // Components
9
9
  export { OGridLayout } from './components/OGridLayout';
10
10
  export { StatusBar } from './components/StatusBar';
11
- export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, } from './components/BaseInlineCellEditor';
11
+ export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, selectDisplayStyle, selectChevronStyle, } from './components/BaseInlineCellEditor';
12
12
  export { GridContextMenu } from './components/GridContextMenu';
13
13
  export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
14
14
  export { SideBar } from './components/SideBar';
@@ -16,6 +16,6 @@ export { CellErrorBoundary } from './components/CellErrorBoundary';
16
16
  export { EmptyState } from './components/EmptyState';
17
17
  export { DateFilterContent, getColumnHeaderFilterStateParams, getDateFilterContentProps, } from './components/ColumnHeaderFilterContent';
18
18
  // Utilities
19
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, areGridRowPropsEqual, } from './utils';
19
+ export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, areGridRowPropsEqual, findCtrlArrowTarget, computeTabNavigation, rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, UndoRedoStack, } from './utils';
20
20
  // Shared component props & renderers (for UI packages to consume)
21
21
  export { renderFilterContent } from './components/ColumnHeaderFilterRenderers';
@@ -1,5 +1,5 @@
1
1
  // Shared utilities re-exported from core
2
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, } from '@alaarab/ogrid-core';
2
+ export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, findCtrlArrowTarget, computeTabNavigation, rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, UndoRedoStack, } from '@alaarab/ogrid-core';
3
3
  // View model utilities (re-exported from core + React-specific getCellInteractionProps)
4
4
  export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './dataGridViewModel';
5
5
  export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
@@ -8,6 +8,8 @@ export declare const richSelectOptionStyle: React.CSSProperties;
8
8
  export declare const richSelectOptionHighlightedStyle: React.CSSProperties;
9
9
  export declare const richSelectNoMatchesStyle: React.CSSProperties;
10
10
  export declare const selectEditorStyle: React.CSSProperties;
11
+ export declare const selectDisplayStyle: React.CSSProperties;
12
+ export declare const selectChevronStyle: React.CSSProperties;
11
13
  export interface BaseInlineCellEditorProps<T> {
12
14
  value: unknown;
13
15
  item: T;
@@ -18,16 +20,14 @@ export interface BaseInlineCellEditorProps<T> {
18
20
  onCancel: () => void;
19
21
  /** Framework-specific checkbox renderer */
20
22
  renderCheckbox: (checked: boolean, onCommit: (value: boolean) => void, onCancel: () => void) => React.ReactNode;
21
- /** Framework-specific select renderer */
22
- renderSelect: (value: unknown, values: unknown[], onCommit: (value: unknown) => void, onCancel: () => void) => React.ReactNode;
23
+ /** @deprecated Built-in custom dropdown is now used. Kept for backward compatibility. */
24
+ renderSelect?: (value: unknown, values: unknown[], onCommit: (value: unknown) => void, onCancel: () => void) => React.ReactNode;
23
25
  }
24
26
  /**
25
- * Base inline cell editor with shared logic for all editor types except checkbox and select
26
- * (which are framework-specific). Used by all 3 UI packages to avoid duplication.
27
+ * Base inline cell editor with shared logic for all editor types except checkbox
28
+ * (which is framework-specific). Used by all 3 UI packages to avoid duplication.
27
29
  *
28
- * Usage:
29
- * - Radix: Pass Radix Checkbox/native select via render props
30
- * - Fluent: Pass Fluent Checkbox/Select via render props
31
- * - Material: Pass MUI Checkbox/Select via render props
30
+ * Text, date, select, and richSelect editors are fully shared.
31
+ * Checkbox is delegated via renderCheckbox render prop.
32
32
  */
33
33
  export declare function BaseInlineCellEditor<T>(props: BaseInlineCellEditorProps<T>): React.ReactElement;
@@ -45,6 +45,8 @@ export { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
45
45
  export type { UseColumnHeaderMenuStateParams, UseColumnHeaderMenuStateResult, } from './useColumnHeaderMenuState';
46
46
  export { useRichSelectState } from './useRichSelectState';
47
47
  export type { UseRichSelectStateParams, UseRichSelectStateResult } from './useRichSelectState';
48
+ export { useSelectState } from './useSelectState';
49
+ export type { UseSelectStateParams, UseSelectStateResult } from './useSelectState';
48
50
  export { useSideBarState } from './useSideBarState';
49
51
  export type { UseSideBarStateParams, UseSideBarStateResult } from './useSideBarState';
50
52
  export { useTableLayout } from './useTableLayout';
@@ -0,0 +1,19 @@
1
+ export interface UseSelectStateParams {
2
+ values: unknown[];
3
+ formatValue?: (value: unknown) => string;
4
+ initialValue: unknown;
5
+ onCommit: (value: unknown) => void;
6
+ onCancel: () => void;
7
+ }
8
+ export interface UseSelectStateResult {
9
+ highlightedIndex: number;
10
+ handleKeyDown: (e: React.KeyboardEvent) => void;
11
+ selectValue: (value: unknown) => void;
12
+ getDisplayText: (value: unknown) => string;
13
+ dropdownRef: React.RefObject<HTMLDivElement | null>;
14
+ }
15
+ /**
16
+ * Manages select editor state with keyboard navigation (arrow keys, enter, escape).
17
+ * Simpler than useRichSelectState — no search, just a dropdown list.
18
+ */
19
+ export declare function useSelectState(params: UseSelectStateParams): UseSelectStateResult;
@@ -1,14 +1,14 @@
1
1
  export { CHECKBOX_COLUMN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH, CELL_PADDING, GRID_BORDER_RADIUS, } from '@alaarab/ogrid-core';
2
2
  export type { ColumnFilterType, IColumnFilterDef, IColumnMeta, IColumnDef, IColumnGroupDef, IColumnDefinition, ICellValueChangedEvent, ICellEditorProps, CellEditorParams, IValueParserParams, UserLike, UserLikeInput, FilterValue, IFilters, IFetchParams, IPageResult, IDataSource, IGridColumnState, IOGridApi, IOGridProps, IOGridDataGridProps, RowSelectionMode, RowId, IRowSelectionChangeEvent, StatusBarPanel, IStatusBarProps, IActiveCell, ISelectionRange, HeaderCell, HeaderRow, SideBarPanelId, ISideBarDef, IDateFilterValue, IVirtualScrollConfig, IColumnReorderConfig, } from './types';
3
3
  export { toUserLike, isInSelectionRange, normalizeSelectionRange } from './types';
4
- export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, usePaginationControls, useDataGridTableOrchestration, } from './hooks';
5
- export type { UseFilterOptionsResult, UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, UsePaginationControlsProps, UsePaginationControlsResult, UseDataGridTableOrchestrationParams, UseDataGridTableOrchestrationResult, } from './hooks';
4
+ export { useFilterOptions, useOGrid, useActiveCell, useCellEditing, useContextMenu, useCellSelection, useClipboard, useRowSelection, useKeyboardNavigation, useUndoRedo, useDebounce, useFillHandle, useDataGridState, useColumnHeaderFilterState, useTextFilterState, useMultiSelectFilterState, usePeopleFilterState, useDateFilterState, useColumnChooserState, useInlineCellEditorState, useColumnResize, useRichSelectState, useSelectState, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, usePaginationControls, useDataGridTableOrchestration, } from './hooks';
5
+ export type { UseFilterOptionsResult, UseOGridResult, UseOGridPagination, UseOGridColumnChooser, UseOGridLayout, UseOGridFilters, ColumnChooserPlacement, UseActiveCellResult, UseCellEditingResult, EditingCell, UseContextMenuResult, ContextMenuPosition, UseCellSelectionResult, UseCellSelectionParams, UseClipboardResult, UseClipboardParams, UseRowSelectionResult, UseRowSelectionParams, UseKeyboardNavigationResult, UseKeyboardNavigationParams, UseUndoRedoResult, UseUndoRedoParams, UseFillHandleResult, UseFillHandleParams, UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, UseTextFilterStateParams, UseTextFilterStateResult, UseMultiSelectFilterStateParams, UseMultiSelectFilterStateResult, UsePeopleFilterStateParams, UsePeopleFilterStateResult, UseDateFilterStateParams, UseDateFilterStateResult, UseColumnChooserStateParams, UseColumnChooserStateResult, UseInlineCellEditorStateParams, UseInlineCellEditorStateResult, InlineCellEditorType, UseColumnResizeParams, UseColumnResizeResult, UseRichSelectStateParams, UseRichSelectStateResult, UseSelectStateParams, UseSelectStateResult, UseSideBarStateParams, UseSideBarStateResult, UseTableLayoutParams, UseTableLayoutResult, UseColumnReorderParams, UseColumnReorderResult, UseVirtualScrollParams, UseVirtualScrollResult, UsePaginationControlsProps, UsePaginationControlsResult, UseDataGridTableOrchestrationParams, UseDataGridTableOrchestrationResult, } from './hooks';
6
6
  export { GRID_ROOT_STYLE, CURSOR_CELL_STYLE, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, NOOP, STOP_PROPAGATION, } from './constants/domHelpers';
7
7
  export { OGridLayout } from './components/OGridLayout';
8
8
  export type { OGridLayoutProps } from './components/OGridLayout';
9
9
  export { StatusBar } from './components/StatusBar';
10
10
  export type { StatusBarProps, StatusBarClassNames } from './components/StatusBar';
11
- export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, } from './components/BaseInlineCellEditor';
11
+ export { BaseInlineCellEditor, editorWrapperStyle, editorInputStyle, richSelectWrapperStyle, richSelectDropdownStyle, richSelectOptionStyle, richSelectOptionHighlightedStyle, richSelectNoMatchesStyle, selectEditorStyle, selectDisplayStyle, selectChevronStyle, } from './components/BaseInlineCellEditor';
12
12
  export type { BaseInlineCellEditorProps } from './components/BaseInlineCellEditor';
13
13
  export { GridContextMenu } from './components/GridContextMenu';
14
14
  export type { GridContextMenuProps, GridContextMenuClassNames } from './components/GridContextMenu';
@@ -22,8 +22,8 @@ export { EmptyState } from './components/EmptyState';
22
22
  export type { EmptyStateProps } from './components/EmptyState';
23
23
  export { DateFilterContent, getColumnHeaderFilterStateParams, getDateFilterContentProps, } from './components/ColumnHeaderFilterContent';
24
24
  export type { IColumnHeaderFilterProps, DateFilterContentProps, DateFilterClassNames, } from './components/ColumnHeaderFilterContent';
25
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, areGridRowPropsEqual, } from './utils';
26
- export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, GridRowComparatorProps, } from './utils';
25
+ export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, getHeaderFilterConfig, getCellRenderDescriptor, isRowInRange, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, areGridRowPropsEqual, findCtrlArrowTarget, computeTabNavigation, rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, UndoRedoStack, } from './utils';
26
+ export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, GridRowComparatorProps, IColumnHeaderMenuItem, ColumnHeaderMenuInput, ColumnHeaderMenuHandlers, } from './utils';
27
27
  export { renderFilterContent } from './components/ColumnHeaderFilterRenderers';
28
28
  export type { FilterContentRenderers, MultiSelectRendererProps, TextRendererProps, PeopleRendererProps, DateRendererProps, } from './components/ColumnHeaderFilterRenderers';
29
29
  export type { IColumnChooserProps } from './components/ColumnChooserProps';
@@ -1,5 +1,5 @@
1
- export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, GRID_CONTEXT_MENU_ITEMS, getContextMenuHandlers, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, } from '@alaarab/ogrid-core';
2
- export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, ParseValueResult, AggregationResult, } from '@alaarab/ogrid-core';
1
+ export { escapeCsvValue, buildCsvHeader, buildCsvRows, exportToCsv, triggerCsvDownload, getCellValue, flattenColumns, buildHeaderRows, getFilterField, mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, getStatusBarParts, getDataGridStatusBarConfig, getPaginationViewModel, PAGE_SIZE_OPTIONS, MAX_PAGE_BUTTONS, GRID_CONTEXT_MENU_ITEMS, COLUMN_HEADER_MENU_ITEMS, getContextMenuHandlers, getColumnHeaderMenuItems, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, findCtrlArrowTarget, computeTabNavigation, rangesEqual, clampSelectionToBounds, computeAutoScrollSpeed, formatCellValueForTsv, formatSelectionAsTsv, parseTsvClipboard, UndoRedoStack, } from '@alaarab/ogrid-core';
2
+ export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, ParseValueResult, AggregationResult, IColumnHeaderMenuItem, ColumnHeaderMenuInput, ColumnHeaderMenuHandlers, } from '@alaarab/ogrid-core';
3
3
  export { getHeaderFilterConfig, getCellRenderDescriptor, resolveCellDisplayContent, resolveCellStyle, buildInlineEditorProps, buildPopoverEditorProps, getCellInteractionProps, } from './dataGridViewModel';
4
4
  export type { HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, } from './dataGridViewModel';
5
5
  export { areGridRowPropsEqual, isRowInRange } from './gridRowComparator';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "description": "OGrid React – React hooks, headless components, and utilities for OGrid data grids.",
5
5
  "main": "dist/esm/index.js",
6
6
  "module": "dist/esm/index.js",