@alaarab/ogrid-react 2.0.14 → 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.
- package/dist/esm/components/BaseInlineCellEditor.js +68 -17
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/useCellSelection.js +9 -24
- package/dist/esm/hooks/useClipboard.js +5 -21
- package/dist/esm/hooks/useDataGridState.js +2 -0
- package/dist/esm/hooks/useDataGridTableOrchestration.js +2 -1
- package/dist/esm/hooks/useKeyboardNavigation.js +6 -51
- package/dist/esm/hooks/useSelectState.js +65 -0
- package/dist/esm/hooks/useUndoRedo.js +31 -36
- package/dist/esm/index.js +3 -3
- package/dist/esm/utils/index.js +1 -1
- package/dist/types/components/BaseInlineCellEditor.d.ts +8 -8
- package/dist/types/hooks/index.d.ts +2 -0
- package/dist/types/hooks/useDataGridState.d.ts +4 -0
- package/dist/types/hooks/useDataGridTableOrchestration.d.ts +1 -0
- package/dist/types/hooks/useSelectState.d.ts +19 -0
- package/dist/types/index.d.ts +5 -5
- package/dist/types/utils/index.d.ts +2 -2
- package/package.json +2 -2
|
@@ -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
|
|
68
|
-
* (which
|
|
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
|
-
*
|
|
71
|
-
*
|
|
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
|
|
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
|
|
80
|
-
const
|
|
91
|
+
const editorValues = column.cellEditorParams?.values ?? [];
|
|
92
|
+
const editorFormatValue = column.cellEditorParams?.formatValue;
|
|
81
93
|
const richSelect = useRichSelectState({
|
|
82
|
-
values:
|
|
83
|
-
formatValue:
|
|
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
|
|
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:
|
|
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 (
|
|
157
|
+
// Select (custom dropdown, shared across all frameworks)
|
|
106
158
|
if (editorType === 'select') {
|
|
107
|
-
|
|
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') {
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -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
|
-
|
|
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 = -
|
|
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 =
|
|
204
|
+
dy = computeAutoScrollSpeed(pos.cy - (rect.bottom - AUTO_SCROLL_EDGE));
|
|
220
205
|
}
|
|
221
206
|
if (pos.cx < rect.left + AUTO_SCROLL_EDGE) {
|
|
222
|
-
dx = -
|
|
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 =
|
|
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 = -
|
|
229
|
+
sdy = -computeAutoScrollSpeed(r.top + AUTO_SCROLL_EDGE - p.cy);
|
|
245
230
|
else if (p.cy > r.bottom - AUTO_SCROLL_EDGE)
|
|
246
|
-
sdy =
|
|
231
|
+
sdy = computeAutoScrollSpeed(p.cy - (r.bottom - AUTO_SCROLL_EDGE));
|
|
247
232
|
if (p.cx < r.left + AUTO_SCROLL_EDGE)
|
|
248
|
-
sdx = -
|
|
233
|
+
sdx = -computeAutoScrollSpeed(r.left + AUTO_SCROLL_EDGE - p.cx);
|
|
249
234
|
else if (p.cx > r.right - AUTO_SCROLL_EDGE)
|
|
250
|
-
sdx =
|
|
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
|
|
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
|
|
79
|
+
const parsedRows = parseTsvClipboard(text);
|
|
96
80
|
beginBatch?.();
|
|
97
|
-
for (let r = 0; r <
|
|
98
|
-
const cells =
|
|
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;
|
|
@@ -327,10 +327,12 @@ export function useDataGridState(params) {
|
|
|
327
327
|
flatColumns, visibleCols, visibleColumnCount, totalColCount, colOffset,
|
|
328
328
|
hasCheckboxCol, hasRowNumbersCol, rowIndexByRowId, containerWidth, minTableWidth,
|
|
329
329
|
desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, onColumnResized,
|
|
330
|
+
measuredColumnWidths,
|
|
330
331
|
}), [
|
|
331
332
|
flatColumns, visibleCols, visibleColumnCount, totalColCount, colOffset,
|
|
332
333
|
hasCheckboxCol, hasRowNumbersCol, rowIndexByRowId, containerWidth, minTableWidth,
|
|
333
334
|
desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, onColumnResized,
|
|
335
|
+
measuredColumnWidths,
|
|
334
336
|
]);
|
|
335
337
|
const rowSelectionState = useMemo(() => ({
|
|
336
338
|
selectedRowIds, updateSelection, handleRowCheckboxChange,
|
|
@@ -25,7 +25,7 @@ export function useDataGridTableOrchestration(params) {
|
|
|
25
25
|
// ── Core state ──────────────────────────────────────────────────────────
|
|
26
26
|
const state = useDataGridState({ props, wrapperRef });
|
|
27
27
|
const { layout, rowSelection: rowSel, editing, interaction, contextMenu: ctxMenu, viewModels, pinning } = state;
|
|
28
|
-
const { visibleCols: visibleColsTyped, totalColCount, hasCheckboxCol, hasRowNumbersCol, colOffset, containerWidth, minTableWidth, desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, } = layout;
|
|
28
|
+
const { visibleCols: visibleColsTyped, totalColCount, hasCheckboxCol, hasRowNumbersCol, colOffset, containerWidth, minTableWidth, desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, measuredColumnWidths, } = layout;
|
|
29
29
|
const visibleCols = visibleColsTyped;
|
|
30
30
|
const { selectedRowIds, updateSelection, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected } = rowSel;
|
|
31
31
|
const { editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl } = editing;
|
|
@@ -158,6 +158,7 @@ export function useDataGridTableOrchestration(params) {
|
|
|
158
158
|
desiredTableWidth,
|
|
159
159
|
columnSizingOverrides,
|
|
160
160
|
setColumnSizingOverrides,
|
|
161
|
+
measuredColumnWidths,
|
|
161
162
|
// Row selection shortcuts
|
|
162
163
|
selectedRowIds,
|
|
163
164
|
updateSelection,
|
|
@@ -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
|
-
?
|
|
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
|
-
?
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
25
|
+
}, [onCellValueChanged]);
|
|
29
26
|
const beginBatch = useCallback(() => {
|
|
30
|
-
|
|
27
|
+
stackRef.current.beginBatch();
|
|
31
28
|
}, []);
|
|
32
29
|
const endBatch = useCallback(() => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
36
|
+
if (!onCellValueChanged)
|
|
37
|
+
return;
|
|
38
|
+
const stack = stackRef.current;
|
|
39
|
+
const lastBatch = stack.undo();
|
|
40
|
+
if (!lastBatch)
|
|
44
41
|
return;
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
55
|
+
if (!onCellValueChanged)
|
|
56
|
+
return;
|
|
57
|
+
const stack = stackRef.current;
|
|
58
|
+
const nextBatch = stack.redo();
|
|
59
|
+
if (!nextBatch)
|
|
62
60
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
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';
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -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
|
-
/**
|
|
22
|
-
renderSelect
|
|
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
|
|
26
|
-
* (which
|
|
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
|
-
*
|
|
29
|
-
*
|
|
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';
|
|
@@ -25,6 +25,10 @@ export interface DataGridLayoutState<T> {
|
|
|
25
25
|
widthPx: number;
|
|
26
26
|
}>>>;
|
|
27
27
|
onColumnResized?: (columnId: string, width: number) => void;
|
|
28
|
+
/** DOM-measured column widths from the previous layout pass.
|
|
29
|
+
* UI packages use these as a minWidth floor to prevent columns from
|
|
30
|
+
* shrinking when new data loads (e.g. during server-side pagination). */
|
|
31
|
+
measuredColumnWidths: Record<string, number>;
|
|
28
32
|
}
|
|
29
33
|
/** Row selection (checkboxes, single-row click). */
|
|
30
34
|
export interface DataGridRowSelectionState {
|
|
@@ -87,6 +87,7 @@ export interface UseDataGridTableOrchestrationResult<T> {
|
|
|
87
87
|
setColumnSizingOverrides: React.Dispatch<React.SetStateAction<Record<string, {
|
|
88
88
|
widthPx: number;
|
|
89
89
|
}>>>;
|
|
90
|
+
measuredColumnWidths: Record<string, number>;
|
|
90
91
|
selectedRowIds: Set<RowId>;
|
|
91
92
|
updateSelection: DataGridRowSelectionState['updateSelection'];
|
|
92
93
|
handleRowCheckboxChange: DataGridRowSelectionState['handleRowCheckboxChange'];
|
|
@@ -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;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"node": ">=18"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@alaarab/ogrid-core": "2.0.
|
|
39
|
+
"@alaarab/ogrid-core": "2.0.15"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@tanstack/react-virtual": "^3.0.0",
|