@alaarab/ogrid-react 2.0.11 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/esm/components/BaseInlineCellEditor.js +2 -2
  2. package/dist/esm/components/CellErrorBoundary.js +1 -1
  3. package/dist/esm/components/ColumnChooserProps.js +6 -0
  4. package/dist/esm/components/ColumnHeaderFilterContent.js +35 -0
  5. package/dist/esm/components/ColumnHeaderFilterRenderers.js +67 -0
  6. package/dist/esm/components/MarchingAntsOverlay.js +20 -11
  7. package/dist/esm/components/PaginationControlsProps.js +6 -0
  8. package/dist/esm/constants/domHelpers.js +16 -0
  9. package/dist/esm/hooks/index.js +1 -0
  10. package/dist/esm/hooks/useColumnHeaderMenuState.js +14 -26
  11. package/dist/esm/hooks/useColumnResize.js +29 -0
  12. package/dist/esm/hooks/useDataGridState.js +54 -5
  13. package/dist/esm/hooks/useDataGridTableOrchestration.js +200 -0
  14. package/dist/esm/hooks/useOGrid.js +3 -6
  15. package/dist/esm/index.js +6 -1
  16. package/dist/esm/utils/index.js +1 -1
  17. package/dist/types/components/ColumnChooserProps.d.ts +12 -0
  18. package/dist/types/components/ColumnHeaderFilterContent.d.ts +62 -0
  19. package/dist/types/components/ColumnHeaderFilterRenderers.d.ts +71 -0
  20. package/dist/types/components/PaginationControlsProps.d.ts +15 -0
  21. package/dist/types/constants/domHelpers.d.ts +17 -0
  22. package/dist/types/hooks/index.d.ts +3 -1
  23. package/dist/types/hooks/useColumnHeaderMenuState.d.ts +3 -1
  24. package/dist/types/hooks/useDataGridState.d.ts +2 -6
  25. package/dist/types/hooks/useDataGridTableOrchestration.d.ts +131 -0
  26. package/dist/types/index.d.ts +9 -2
  27. package/dist/types/types/dataGridTypes.d.ts +1 -1
  28. package/dist/types/utils/index.d.ts +1 -1
  29. package/package.json +2 -2
@@ -35,9 +35,9 @@ export const richSelectDropdownStyle = {
35
35
  maxHeight: 200,
36
36
  overflowY: 'auto',
37
37
  background: 'var(--ogrid-bg, #fff)',
38
- border: '1px solid var(--ogrid-border, #ccc)',
38
+ border: '1px solid var(--ogrid-border, rgba(0, 0, 0, 0.12))',
39
39
  zIndex: 10,
40
- boxShadow: '0 4px 16px rgba(0,0,0,0.2)',
40
+ boxShadow: 'var(--ogrid-shadow, 0 4px 16px rgba(0,0,0,0.2))',
41
41
  };
42
42
  export const richSelectOptionStyle = {
43
43
  padding: '6px 8px',
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  const DEFAULT_FALLBACK_STYLE = {
4
- color: '#d32f2f',
4
+ color: 'var(--ogrid-error, #d32f2f)',
5
5
  fontSize: '0.75rem',
6
6
  padding: '2px 4px',
7
7
  };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared props interface for ColumnChooser across all React UI packages.
3
+ * Each UI package renders its own framework-specific trigger, popover, and checkboxes
4
+ * but shares this common prop shape.
5
+ */
6
+ export {};
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ export const DateFilterContent = ({ tempDateFrom, setTempDateFrom, tempDateTo, setTempDateTo, onApply, onClear, classNames, }) => (_jsxs(_Fragment, { children: [_jsxs("div", { style: { padding: '8px 12px', display: 'flex', flexDirection: 'column', gap: 6 }, children: [_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 }, children: ["From:", _jsx("input", { type: "date", value: tempDateFrom, onChange: (e) => setTempDateFrom(e.target.value), style: { flex: 1 } })] }), _jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 }, children: ["To:", _jsx("input", { type: "date", value: tempDateTo, onChange: (e) => setTempDateTo(e.target.value), style: { flex: 1 } })] })] }), _jsxs("div", { className: classNames?.popoverActions, children: [_jsx("button", { className: classNames?.clearButton, onClick: onClear, disabled: !tempDateFrom && !tempDateTo, children: "Clear" }), _jsx("button", { className: classNames?.applyButton, onClick: onApply, children: "Apply" })] })] }));
3
+ DateFilterContent.displayName = 'DateFilterContent';
4
+ // ---- Utility to extract useColumnHeaderFilterState params from props ----
5
+ export function getColumnHeaderFilterStateParams(props) {
6
+ return {
7
+ filterType: props.filterType,
8
+ isSorted: props.isSorted ?? false,
9
+ isSortedDescending: props.isSortedDescending ?? false,
10
+ onSort: props.onSort,
11
+ selectedValues: props.selectedValues,
12
+ onFilterChange: props.onFilterChange,
13
+ options: props.options,
14
+ isLoadingOptions: props.isLoadingOptions ?? false,
15
+ textValue: props.textValue ?? '',
16
+ onTextChange: props.onTextChange,
17
+ selectedUser: props.selectedUser,
18
+ onUserChange: props.onUserChange,
19
+ peopleSearch: props.peopleSearch,
20
+ dateValue: props.dateValue,
21
+ onDateChange: props.onDateChange,
22
+ };
23
+ }
24
+ // ---- Helper to build date filter props from state ----
25
+ export function getDateFilterContentProps(state, classNames) {
26
+ return {
27
+ tempDateFrom: state.tempDateFrom,
28
+ setTempDateFrom: state.setTempDateFrom,
29
+ tempDateTo: state.tempDateTo,
30
+ setTempDateTo: state.setTempDateTo,
31
+ onApply: state.handlers.handleDateApply,
32
+ onClear: state.handlers.handleDateClear,
33
+ classNames,
34
+ };
35
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Shared filter content dispatching for ColumnHeaderFilter across all React UI packages.
3
+ *
4
+ * Each UI package provides framework-specific sub-filter components (TextFilterPopover,
5
+ * MultiSelectFilterPopover, PeopleFilterPopover, date content). This utility dispatches
6
+ * to the correct renderer based on filterType, eliminating the duplicated if/switch chain
7
+ * that was previously in each UI package's ColumnHeaderFilter component.
8
+ */
9
+ /**
10
+ * Dispatches to the appropriate filter content renderer based on filterType.
11
+ * Eliminates the duplicated if/switch chain in each UI package's ColumnHeaderFilter.
12
+ *
13
+ * @param filterType - The column's filter type
14
+ * @param state - The result from useColumnHeaderFilterState
15
+ * @param options - The filter options array (for multiSelect)
16
+ * @param isLoadingOptions - Whether options are loading
17
+ * @param selectedUser - The currently selected user (for people filter)
18
+ * @param renderers - Framework-specific renderer functions
19
+ * @returns The rendered filter content, or null for unsupported filter types
20
+ */
21
+ export function renderFilterContent(filterType, state, options, isLoadingOptions, selectedUser, renderers) {
22
+ if (filterType === 'multiSelect') {
23
+ return renderers.renderMultiSelect({
24
+ searchText: state.searchText,
25
+ onSearchChange: state.setSearchText,
26
+ options,
27
+ filteredOptions: state.filteredOptions,
28
+ selected: state.tempSelected,
29
+ onOptionToggle: state.handlers.handleCheckboxChange,
30
+ onSelectAll: state.handlers.handleSelectAll,
31
+ onClearSelection: state.handlers.handleClearSelection,
32
+ onApply: state.handlers.handleApplyMultiSelect,
33
+ isLoading: isLoadingOptions,
34
+ });
35
+ }
36
+ if (filterType === 'text') {
37
+ return renderers.renderText({
38
+ value: state.tempTextValue,
39
+ onValueChange: state.setTempTextValue,
40
+ onApply: state.handlers.handleTextApply,
41
+ onClear: state.handlers.handleTextClear,
42
+ });
43
+ }
44
+ if (filterType === 'people') {
45
+ return renderers.renderPeople({
46
+ selectedUser,
47
+ searchText: state.peopleSearchText,
48
+ onSearchChange: state.setPeopleSearchText,
49
+ suggestions: state.peopleSuggestions,
50
+ isLoading: state.isPeopleLoading,
51
+ onUserSelect: state.handlers.handleUserSelect,
52
+ onClearUser: state.handlers.handleClearUser,
53
+ inputRef: state.peopleInputRef,
54
+ });
55
+ }
56
+ if (filterType === 'date') {
57
+ return renderers.renderDate({
58
+ tempDateFrom: state.tempDateFrom,
59
+ setTempDateFrom: state.setTempDateFrom,
60
+ tempDateTo: state.tempDateTo,
61
+ setTempDateTo: state.setTempDateTo,
62
+ onApply: state.handlers.handleDateApply,
63
+ onClear: state.handlers.handleDateClear,
64
+ });
65
+ }
66
+ return null;
67
+ }
@@ -60,23 +60,32 @@ export function MarchingAntsOverlay({ containerRef, selectionRange, copyRange, c
60
60
  selectionRange.startCol === clipRange.startCol &&
61
61
  selectionRange.endRow === clipRange.endRow &&
62
62
  selectionRange.endCol === clipRange.endCol;
63
- return (_jsxs(_Fragment, { children: [selRect && !clipRangeMatchesSel && (_jsx("svg", { style: {
63
+ // Round to integer pixels so the stroke aligns to the pixel grid and corners connect cleanly
64
+ const roundRect = (r) => ({
65
+ top: Math.round(r.top),
66
+ left: Math.round(r.left),
67
+ width: Math.round(r.width),
68
+ height: Math.round(r.height),
69
+ });
70
+ const selR = selRect ? roundRect(selRect) : null;
71
+ const clipR = clipRect ? roundRect(clipRect) : null;
72
+ return (_jsxs(_Fragment, { children: [selR && !clipRangeMatchesSel && (_jsx("svg", { style: {
64
73
  position: 'absolute',
65
- top: selRect.top,
66
- left: selRect.left,
67
- width: selRect.width,
68
- height: selRect.height,
74
+ top: selR.top,
75
+ left: selR.left,
76
+ width: selR.width,
77
+ height: selR.height,
69
78
  pointerEvents: 'none',
70
79
  zIndex: 4,
71
80
  overflow: 'visible',
72
- }, "aria-hidden": "true", children: _jsx("rect", { x: "1", y: "1", width: Math.max(0, selRect.width - 2), height: Math.max(0, selRect.height - 2), fill: "none", stroke: "var(--ogrid-selection, #217346)", strokeWidth: "2" }) })), clipRect && (_jsx("svg", { style: {
81
+ }, "aria-hidden": "true", children: _jsx("rect", { x: "1", y: "1", width: Math.max(0, selR.width - 2), height: Math.max(0, selR.height - 2), fill: "none", stroke: "var(--ogrid-selection, #217346)", strokeWidth: "2", style: { shapeRendering: 'crispEdges' } }) })), clipR && (_jsx("svg", { style: {
73
82
  position: 'absolute',
74
- top: clipRect.top,
75
- left: clipRect.left,
76
- width: clipRect.width,
77
- height: clipRect.height,
83
+ top: clipR.top,
84
+ left: clipR.left,
85
+ width: clipR.width,
86
+ height: clipR.height,
78
87
  pointerEvents: 'none',
79
88
  zIndex: 5,
80
89
  overflow: 'visible',
81
- }, "aria-hidden": "true", children: _jsx("rect", { x: "1", y: "1", width: Math.max(0, clipRect.width - 2), height: Math.max(0, clipRect.height - 2), fill: "none", stroke: "var(--ogrid-selection, #217346)", strokeWidth: "2", strokeDasharray: "4 4", style: MARCHING_ANTS_ANIMATION }) }))] }));
90
+ }, "aria-hidden": "true", children: _jsx("rect", { x: "1", y: "1", width: Math.max(0, clipR.width - 2), height: Math.max(0, clipR.height - 2), fill: "none", stroke: "var(--ogrid-selection, #217346)", strokeWidth: "2", strokeDasharray: "4 4", style: { ...MARCHING_ANTS_ANIMATION, shapeRendering: 'crispEdges' } }) }))] }));
82
91
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared props interface for PaginationControls across all React UI packages.
3
+ * Each UI package renders its own framework-specific buttons, selects, and layout
4
+ * but shares this common prop shape.
5
+ */
6
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Module-scope stable constants shared across all React UI DataGridTable implementations.
3
+ * Avoid per-render allocations by keeping these at module scope.
4
+ */
5
+ /** Root container style for the DataGridTable (flex column layout). */
6
+ export const GRID_ROOT_STYLE = { position: 'relative', flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' };
7
+ /** Applied to cells that support editing — shows the cell cursor. */
8
+ export const CURSOR_CELL_STYLE = { cursor: 'cell' };
9
+ /** Minimum size for popover anchor elements. */
10
+ export const POPOVER_ANCHOR_STYLE = { minHeight: '100%', minWidth: 40 };
11
+ /** Prevents the default browser action for mouse events. */
12
+ export const PREVENT_DEFAULT = (e) => { e.preventDefault(); };
13
+ /** No-operation function. */
14
+ export const NOOP = () => { };
15
+ /** Stops event propagation (e.g. click on checkbox inside a row). */
16
+ export const STOP_PROPAGATION = (e) => { e.stopPropagation(); };
@@ -28,3 +28,4 @@ export { useColumnReorder } from './useColumnReorder';
28
28
  export { useVirtualScroll } from './useVirtualScroll';
29
29
  export { useLatestRef } from './useLatestRef';
30
30
  export { usePaginationControls } from './usePaginationControls';
31
+ export { useDataGridTableOrchestration } from './useDataGridTableOrchestration';
@@ -1,10 +1,11 @@
1
1
  import { useState, useCallback } from 'react';
2
+ import { measureColumnContentWidth } from '../utils';
2
3
  /**
3
4
  * Manages state for the column header menu (pin, sort, autosize actions).
4
5
  * Tracks which column's menu is open, anchor element, and action handlers.
5
6
  */
6
7
  export function useColumnHeaderMenuState(params) {
7
- const { pinnedColumns, onPinColumn, onUnpinColumn, sortBy, sortDirection, onColumnSort, onColumnResized, columns, data: _data, getRowId: _getRowId, } = params;
8
+ const { pinnedColumns, onPinColumn, onUnpinColumn, sortBy, sortDirection, onColumnSort, onColumnResized, onAutosizeColumn, columns, data: _data, getRowId: _getRowId, } = params;
8
9
  const [isOpen, setIsOpen] = useState(false);
9
10
  const [openForColumn, setOpenForColumn] = useState(null);
10
11
  const [anchorElement, setAnchorElement] = useState(null);
@@ -46,54 +47,41 @@ export function useColumnHeaderMenuState(params) {
46
47
  }, [openForColumn, canUnpin, onUnpinColumn, close]);
47
48
  const handleSortAsc = useCallback(() => {
48
49
  if (openForColumn && isSortable) {
49
- onColumnSort(openForColumn);
50
+ onColumnSort(openForColumn, 'asc');
50
51
  close();
51
52
  }
52
53
  }, [openForColumn, isSortable, onColumnSort, close]);
53
54
  const handleSortDesc = useCallback(() => {
54
55
  if (openForColumn && isSortable) {
55
- onColumnSort(openForColumn);
56
+ onColumnSort(openForColumn, 'desc');
56
57
  close();
57
58
  }
58
59
  }, [openForColumn, isSortable, onColumnSort, close]);
59
60
  const handleClearSort = useCallback(() => {
60
61
  if (openForColumn && isSortable) {
61
- onColumnSort(openForColumn);
62
+ onColumnSort(openForColumn, null);
62
63
  close();
63
64
  }
64
65
  }, [openForColumn, isSortable, onColumnSort, close]);
65
66
  const handleAutosizeThis = useCallback(() => {
66
- if (!openForColumn || !onColumnResized || !isResizable)
67
+ const resizer = onAutosizeColumn ?? onColumnResized;
68
+ if (!openForColumn || !resizer || !isResizable)
67
69
  return;
68
- // Measure column content width
69
- const cells = document.querySelectorAll(`[data-column-id="${openForColumn}"]`);
70
- let maxWidth = 100; // Minimum width
71
- cells.forEach((cell) => {
72
- const textContent = cell.textContent || '';
73
- // Rough estimate: 8px per character + 32px padding
74
- const estimatedWidth = Math.min(textContent.length * 8 + 32, 500);
75
- maxWidth = Math.max(maxWidth, estimatedWidth);
76
- });
77
- onColumnResized(openForColumn, maxWidth);
70
+ const col = columns.find((c) => c.columnId === openForColumn);
71
+ resizer(openForColumn, measureColumnContentWidth(openForColumn, col?.minWidth));
78
72
  close();
79
- }, [openForColumn, onColumnResized, isResizable, close]);
73
+ }, [openForColumn, onAutosizeColumn, onColumnResized, isResizable, columns, close]);
80
74
  const handleAutosizeAll = useCallback(() => {
81
- if (!onColumnResized)
75
+ const resizer = onAutosizeColumn ?? onColumnResized;
76
+ if (!resizer)
82
77
  return;
83
78
  columns.forEach((col) => {
84
79
  if (col.resizable === false)
85
80
  return;
86
- const cells = document.querySelectorAll(`[data-column-id="${col.columnId}"]`);
87
- let maxWidth = 100;
88
- cells.forEach((cell) => {
89
- const textContent = cell.textContent || '';
90
- const estimatedWidth = Math.min(textContent.length * 8 + 32, 500);
91
- maxWidth = Math.max(maxWidth, estimatedWidth);
92
- });
93
- onColumnResized(col.columnId, maxWidth);
81
+ resizer(col.columnId, measureColumnContentWidth(col.columnId, col.minWidth));
94
82
  });
95
83
  close();
96
- }, [columns, onColumnResized, close]);
84
+ }, [columns, onAutosizeColumn, onColumnResized, close]);
97
85
  return {
98
86
  isOpen,
99
87
  openForColumn,
@@ -36,6 +36,26 @@ export function useColumnResize({ columnSizingOverrides, setColumnSizingOverride
36
36
  ?? col.defaultWidth
37
37
  ?? defaultWidth;
38
38
  let latestWidth = startWidth;
39
+ // Lock all column widths to their current DOM widths on first resize.
40
+ // With table-layout:auto, resizing one column causes the browser to compress others.
41
+ // Snapshotting all widths prevents this — only the dragged column changes.
42
+ const thead = thEl?.closest('thead');
43
+ if (thead) {
44
+ const allThs = thead.querySelectorAll('th[data-column-id]');
45
+ if (allThs.length > 0) {
46
+ setColumnSizingOverrides((prev) => {
47
+ const next = { ...prev };
48
+ allThs.forEach((th) => {
49
+ const colId = th.dataset.columnId;
50
+ if (colId && !next[colId]) {
51
+ next[colId] = { widthPx: th.getBoundingClientRect().width };
52
+ }
53
+ });
54
+ next[columnId] = { widthPx: startWidth };
55
+ return next;
56
+ });
57
+ }
58
+ }
39
59
  // Lock cursor and prevent text selection during drag
40
60
  const prevCursor = document.body.style.cursor;
41
61
  const prevUserSelect = document.body.style.userSelect;
@@ -73,6 +93,15 @@ export function useColumnResize({ columnSizingOverrides, setColumnSizingOverride
73
93
  const onUp = () => {
74
94
  cleanup();
75
95
  flushWidth();
96
+ // Remove any rogue :focus-visible outlines that appeared during the drag.
97
+ // Re-focus the grid wrapper so keyboard navigation still works.
98
+ const wrapper = thEl?.closest('[tabindex]');
99
+ if (wrapper) {
100
+ wrapper.focus({ preventScroll: true });
101
+ }
102
+ else if (document.activeElement instanceof HTMLElement) {
103
+ document.activeElement.blur();
104
+ }
76
105
  if (onColumnResizedRef.current) {
77
106
  onColumnResizedRef.current(columnId, latestWidth);
78
107
  }
@@ -1,5 +1,6 @@
1
- import { useMemo, useCallback, useState } from 'react';
1
+ import { useMemo, useCallback, useState, useLayoutEffect } from 'react';
2
2
  import { flattenColumns, getDataGridStatusBarConfig, parseValue, computeAggregations } from '../utils';
3
+ import { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH } from '@alaarab/ogrid-core';
3
4
  import { useRowSelection } from './useRowSelection';
4
5
  import { useCellEditing } from './useCellEditing';
5
6
  import { useActiveCell } from './useActiveCell';
@@ -150,6 +151,48 @@ export function useDataGridState(params) {
150
151
  pinnedColumns,
151
152
  onColumnPinned,
152
153
  });
154
+ // Measure actual column widths from the DOM for accurate pinning offsets.
155
+ // With table-layout: auto, rendered widths can exceed declared minimums.
156
+ const [measuredColumnWidths, setMeasuredColumnWidths] = useState({});
157
+ useLayoutEffect(() => {
158
+ const wrapper = wrapperRef.current;
159
+ if (!wrapper)
160
+ return;
161
+ const headerCells = wrapper.querySelectorAll('th[data-column-id]');
162
+ if (headerCells.length === 0)
163
+ return;
164
+ const measured = {};
165
+ headerCells.forEach((cell) => {
166
+ const colId = cell.getAttribute('data-column-id');
167
+ if (colId)
168
+ measured[colId] = cell.offsetWidth;
169
+ });
170
+ setMeasuredColumnWidths((prev) => {
171
+ // Only update if widths actually changed to avoid render loops
172
+ for (const key in measured) {
173
+ if (prev[key] !== measured[key])
174
+ return measured;
175
+ }
176
+ if (Object.keys(prev).length !== Object.keys(measured).length)
177
+ return measured;
178
+ return prev;
179
+ });
180
+ // Re-measure when columns, container size, or resize overrides change
181
+ // eslint-disable-next-line react-hooks/exhaustive-deps
182
+ }, [visibleCols, containerWidth, columnSizingOverrides]);
183
+ // Build column width map for pinning offset computation
184
+ const columnWidthMap = useMemo(() => {
185
+ const map = {};
186
+ for (const col of visibleCols) {
187
+ const override = columnSizingOverrides[col.columnId];
188
+ map[col.columnId] = override
189
+ ? override.widthPx
190
+ : (measuredColumnWidths[col.columnId] ?? col.idealWidth ?? col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH);
191
+ }
192
+ return map;
193
+ }, [visibleCols, columnSizingOverrides, measuredColumnWidths]);
194
+ const leftOffsets = useMemo(() => pinningResult.computeLeftOffsets(visibleCols, columnWidthMap, DEFAULT_MIN_COLUMN_WIDTH, hasCheckboxCol, CHECKBOX_COLUMN_WIDTH), [pinningResult, visibleCols, columnWidthMap, hasCheckboxCol]);
195
+ const rightOffsets = useMemo(() => pinningResult.computeRightOffsets(visibleCols, columnWidthMap, DEFAULT_MIN_COLUMN_WIDTH), [pinningResult, visibleCols, columnWidthMap]);
153
196
  const aggregation = useMemo(() => computeAggregations(items, visibleCols, cellSelection ? selectionRange : null), [items, visibleCols, selectionRange, cellSelection]);
154
197
  const statusBarConfig = useMemo(() => {
155
198
  const base = getDataGridStatusBarConfig(statusBar, items.length, selectedRowIds.size);
@@ -167,9 +210,14 @@ export function useDataGridState(params) {
167
210
  const onFilterChangeRef = useLatestRef(onFilterChange);
168
211
  const peopleSearchRef = useLatestRef(peopleSearch);
169
212
  // Stable callback wrappers that delegate to refs
170
- const stableOnColumnSort = useCallback((columnKey) => onColumnSortRef.current?.(columnKey),
213
+ const stableOnColumnSort = useCallback((columnKey, direction) => onColumnSortRef.current?.(columnKey, direction),
171
214
  // eslint-disable-next-line react-hooks/exhaustive-deps
172
215
  []);
216
+ // Autosize callback — updates internal column sizing state + notifies external listener
217
+ const handleAutosizeColumn = useCallback((columnId, width) => {
218
+ setColumnSizingOverrides((prev) => ({ ...prev, [columnId]: { widthPx: width } }));
219
+ onColumnResized?.(columnId, width);
220
+ }, [setColumnSizingOverrides, onColumnResized]);
173
221
  const headerMenuResult = useColumnHeaderMenuState({
174
222
  pinnedColumns: pinningResult.pinnedColumns,
175
223
  onPinColumn: pinningResult.pinColumn,
@@ -178,6 +226,7 @@ export function useDataGridState(params) {
178
226
  sortDirection,
179
227
  onColumnSort: stableOnColumnSort,
180
228
  onColumnResized,
229
+ onAutosizeColumn: handleAutosizeColumn,
181
230
  columns: flatColumns,
182
231
  data: items,
183
232
  getRowId: getRowId,
@@ -333,8 +382,8 @@ export function useDataGridState(params) {
333
382
  pinColumn: pinningResult.pinColumn,
334
383
  unpinColumn: pinningResult.unpinColumn,
335
384
  isPinned: pinningResult.isPinned,
336
- computeLeftOffsets: pinningResult.computeLeftOffsets,
337
- computeRightOffsets: pinningResult.computeRightOffsets,
385
+ leftOffsets,
386
+ rightOffsets,
338
387
  headerMenu: {
339
388
  isOpen: headerMenuResult.isOpen,
340
389
  openForColumn: headerMenuResult.openForColumn,
@@ -358,7 +407,7 @@ export function useDataGridState(params) {
358
407
  },
359
408
  }), [
360
409
  pinningResult.pinnedColumns, pinningResult.pinColumn, pinningResult.unpinColumn,
361
- pinningResult.isPinned, pinningResult.computeLeftOffsets, pinningResult.computeRightOffsets,
410
+ pinningResult.isPinned, leftOffsets, rightOffsets,
362
411
  headerMenuResult.isOpen, headerMenuResult.openForColumn, headerMenuResult.anchorElement,
363
412
  headerMenuResult.open, headerMenuResult.close, headerMenuResult.handlePinLeft,
364
413
  headerMenuResult.handlePinRight, headerMenuResult.handleUnpin,
@@ -0,0 +1,200 @@
1
+ import { useCallback, useRef, useMemo } from 'react';
2
+ import { useDataGridState } from './useDataGridState';
3
+ import { useColumnResize } from './useColumnResize';
4
+ import { useColumnReorder } from './useColumnReorder';
5
+ import { useVirtualScroll } from './useVirtualScroll';
6
+ import { useLatestRef } from './useLatestRef';
7
+ import { buildHeaderRows } from '../utils';
8
+ // ---------------------------------------------------------------------------
9
+ // Hook
10
+ // ---------------------------------------------------------------------------
11
+ /**
12
+ * Shared orchestration hook for DataGridTable.
13
+ *
14
+ * Encapsulates all state management and computation that is identical across
15
+ * Radix, Fluent, and Material DataGridTable implementations. Each UI package
16
+ * calls this hook, then renders its own framework-specific JSX using the
17
+ * returned values.
18
+ */
19
+ export function useDataGridTableOrchestration(params) {
20
+ const { props } = params;
21
+ // ── Refs ────────────────────────────────────────────────────────────────
22
+ const wrapperRef = useRef(null);
23
+ const tableContainerRef = useRef(null);
24
+ const lastMouseShiftRef = useRef(false);
25
+ // ── Core state ──────────────────────────────────────────────────────────
26
+ const state = useDataGridState({ props, wrapperRef });
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;
29
+ const visibleCols = visibleColsTyped;
30
+ const { selectedRowIds, updateSelection, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected } = rowSel;
31
+ const { editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl } = editing;
32
+ const { setActiveCell, handleCellMouseDown, selectionRange, hasCellSelection, handleGridKeyDown, handleFillHandleMouseDown, handleCopy, handleCut, handlePaste, cutRange, copyRange, canUndo, canRedo, onUndo, onRedo, isDragging } = interaction;
33
+ const { menuPosition, handleCellContextMenu, closeContextMenu } = ctxMenu;
34
+ const { headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError } = viewModels;
35
+ const { headerMenu } = pinning;
36
+ const handlePasteVoid = useCallback(() => { void handlePaste(); }, [handlePaste]);
37
+ // ── Props destructuring ─────────────────────────────────────────────────
38
+ const { items, columns, getRowId, emptyState, layoutMode = 'fill', rowSelection = 'none', freezeRows, freezeCols, suppressHorizontalScroll, isLoading = false, loadingMessage = 'Loading\u2026', 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, visibleColumns, columnOrder, onColumnOrderChange, columnReorder, virtualScroll, density = 'normal', pinnedColumns, currentPage = 1, pageSize: propPageSize = 25, } = props;
39
+ // ── Derived values ──────────────────────────────────────────────────────
40
+ const rowNumberOffset = hasRowNumbersCol ? (currentPage - 1) * propPageSize : 0;
41
+ const headerRows = useMemo(() => buildHeaderRows(columns, visibleColumns), [columns, visibleColumns]);
42
+ const allowOverflowX = !suppressHorizontalScroll && containerWidth > 0 && (minTableWidth > containerWidth || desiredTableWidth > containerWidth);
43
+ const fitToContent = layoutMode === 'content';
44
+ // ── Column resize ──────────────────────────────────────────────────────
45
+ const { handleResizeStart, getColumnWidth } = useColumnResize({
46
+ columnSizingOverrides,
47
+ setColumnSizingOverrides,
48
+ });
49
+ // ── Column reorder ─────────────────────────────────────────────────────
50
+ const { isDragging: isReorderDragging, dropIndicatorX, handleHeaderMouseDown } = useColumnReorder({
51
+ columns: visibleCols,
52
+ columnOrder,
53
+ onColumnOrderChange,
54
+ enabled: columnReorder === true,
55
+ pinnedColumns,
56
+ wrapperRef,
57
+ });
58
+ // ── Virtual scroll ─────────────────────────────────────────────────────
59
+ const virtualScrollEnabled = virtualScroll?.enabled === true;
60
+ const virtualRowHeight = virtualScroll?.rowHeight ?? 36;
61
+ const { visibleRange } = useVirtualScroll({
62
+ totalRows: items.length,
63
+ rowHeight: virtualRowHeight,
64
+ enabled: virtualScrollEnabled,
65
+ overscan: virtualScroll?.overscan,
66
+ containerRef: wrapperRef,
67
+ });
68
+ // ── Memoized callback groups ───────────────────────────────────────────
69
+ const editCallbacks = useMemo(() => ({ commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit }), [commitCellEdit, setEditingCell, setPendingEditorValue, cancelPopoverEdit]);
70
+ const interactionHandlers = useMemo(() => ({ handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu }), [handleCellMouseDown, setActiveCell, setEditingCell, handleCellContextMenu]);
71
+ // ── Stable refs for volatile state ─────────────────────────────────────
72
+ const cellDescriptorInputRef = useLatestRef(cellDescriptorInput);
73
+ const pendingEditorValueRef = useLatestRef(pendingEditorValue);
74
+ const popoverAnchorElRef = useLatestRef(popoverAnchorEl);
75
+ const selectedRowIdsRef = useLatestRef(selectedRowIds);
76
+ // ── Stable row-click handler ───────────────────────────────────────────
77
+ const handleSingleRowClick = useCallback((e) => {
78
+ if (rowSelection !== 'single')
79
+ return;
80
+ const rowId = e.currentTarget.dataset.rowId;
81
+ if (!rowId)
82
+ return;
83
+ const ids = selectedRowIdsRef.current;
84
+ updateSelection(ids.has(rowId) ? new Set() : new Set([rowId]));
85
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- selectedRowIdsRef is a stable ref
86
+ }, [rowSelection, updateSelection]);
87
+ // ── Return ─────────────────────────────────────────────────────────────
88
+ return {
89
+ // Refs
90
+ wrapperRef,
91
+ tableContainerRef,
92
+ lastMouseShiftRef,
93
+ // Full state sub-objects
94
+ state,
95
+ layout,
96
+ rowSel,
97
+ editing,
98
+ interaction,
99
+ ctxMenu,
100
+ viewModels,
101
+ pinning,
102
+ // Column resize
103
+ handleResizeStart,
104
+ getColumnWidth,
105
+ // Column reorder
106
+ isReorderDragging,
107
+ dropIndicatorX,
108
+ handleHeaderMouseDown,
109
+ // Virtual scroll
110
+ virtualScrollEnabled,
111
+ virtualRowHeight,
112
+ visibleRange,
113
+ // Derived from props
114
+ items,
115
+ columns,
116
+ getRowId,
117
+ emptyState,
118
+ layoutMode,
119
+ rowSelection,
120
+ freezeRows,
121
+ freezeCols,
122
+ suppressHorizontalScroll,
123
+ isLoading,
124
+ loadingMessage,
125
+ ariaLabel,
126
+ ariaLabelledBy,
127
+ visibleColumns,
128
+ columnOrder,
129
+ columnReorder,
130
+ density,
131
+ pinnedColumns,
132
+ currentPage,
133
+ propPageSize,
134
+ // Computed values
135
+ rowNumberOffset,
136
+ headerRows,
137
+ allowOverflowX,
138
+ fitToContent,
139
+ // Memoized callback groups
140
+ editCallbacks,
141
+ interactionHandlers,
142
+ // Stable refs for volatile state
143
+ cellDescriptorInputRef,
144
+ pendingEditorValueRef,
145
+ popoverAnchorElRef,
146
+ selectedRowIdsRef,
147
+ // Convenience handlers
148
+ handleSingleRowClick,
149
+ handlePasteVoid,
150
+ // Layout-derived references
151
+ visibleCols,
152
+ totalColCount,
153
+ hasCheckboxCol,
154
+ hasRowNumbersCol,
155
+ colOffset,
156
+ containerWidth,
157
+ minTableWidth,
158
+ desiredTableWidth,
159
+ columnSizingOverrides,
160
+ setColumnSizingOverrides,
161
+ // Row selection shortcuts
162
+ selectedRowIds,
163
+ updateSelection,
164
+ handleRowCheckboxChange,
165
+ handleSelectAll,
166
+ allSelected,
167
+ someSelected,
168
+ // Editing shortcuts
169
+ editingCell,
170
+ setPopoverAnchorEl,
171
+ cancelPopoverEdit,
172
+ // Interaction shortcuts
173
+ setActiveCell,
174
+ selectionRange,
175
+ hasCellSelection,
176
+ handleGridKeyDown,
177
+ handleFillHandleMouseDown,
178
+ handleCopy,
179
+ handleCut,
180
+ cutRange,
181
+ copyRange,
182
+ canUndo,
183
+ canRedo,
184
+ onUndo,
185
+ onRedo,
186
+ isDragging,
187
+ // Context menu shortcuts
188
+ menuPosition,
189
+ handleCellContextMenu,
190
+ closeContextMenu,
191
+ // ViewModel shortcuts
192
+ headerFilterInput,
193
+ cellDescriptorInput,
194
+ statusBarConfig,
195
+ showEmptyInGrid,
196
+ onCellError,
197
+ // Pinning shortcuts
198
+ headerMenu,
199
+ };
200
+ }
@@ -1,5 +1,5 @@
1
1
  import { useMemo, useCallback, useState, useEffect, useRef, useImperativeHandle, } from 'react';
2
- import { mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, flattenColumns, processClientSideData, } from '../utils';
2
+ import { mergeFilter, deriveFilterOptionsFromData, getMultiSelectFilterFields, flattenColumns, processClientSideData, computeNextSortState, } from '../utils';
3
3
  import { useFilterOptions } from './useFilterOptions';
4
4
  import { useSideBarState } from './useSideBarState';
5
5
  const DEFAULT_PAGE_SIZE = 25;
@@ -72,11 +72,8 @@ export function useOGrid(props, ref) {
72
72
  setInternalVisibleColumns(cols);
73
73
  onVisibleColumnsChange?.(cols);
74
74
  }, [controlledVisibleColumns, onVisibleColumnsChange]);
75
- const handleSort = useCallback((columnKey) => {
76
- setSort({
77
- field: columnKey,
78
- direction: sort.field === columnKey && sort.direction === 'asc' ? 'desc' : 'asc',
79
- });
75
+ const handleSort = useCallback((columnKey, direction) => {
76
+ setSort(computeNextSortState(sort, columnKey, direction));
80
77
  }, [sort, setSort]);
81
78
  /** Single filter change handler — wraps discriminated FilterValue into mergeFilter. */
82
79
  const handleFilterChange = useCallback((key, value) => {
package/dist/esm/index.js CHANGED
@@ -2,7 +2,9 @@
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, } 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, useSideBarState, useTableLayout, useColumnReorder, useVirtualScroll, useLatestRef, usePaginationControls, useDataGridTableOrchestration, } from './hooks';
6
+ // Constants
7
+ export { GRID_ROOT_STYLE, CURSOR_CELL_STYLE, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, NOOP, STOP_PROPAGATION, } from './constants/domHelpers';
6
8
  // Components
7
9
  export { OGridLayout } from './components/OGridLayout';
8
10
  export { StatusBar } from './components/StatusBar';
@@ -12,5 +14,8 @@ export { MarchingAntsOverlay } from './components/MarchingAntsOverlay';
12
14
  export { SideBar } from './components/SideBar';
13
15
  export { CellErrorBoundary } from './components/CellErrorBoundary';
14
16
  export { EmptyState } from './components/EmptyState';
17
+ export { DateFilterContent, getColumnHeaderFilterStateParams, getDateFilterContentProps, } from './components/ColumnHeaderFilterContent';
15
18
  // Utilities
16
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';
20
+ // Shared component props & renderers (for UI packages to consume)
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, } 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, getContextMenuHandlers, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, } 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';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared props interface for ColumnChooser across all React UI packages.
3
+ * Each UI package renders its own framework-specific trigger, popover, and checkboxes
4
+ * but shares this common prop shape.
5
+ */
6
+ import type { IColumnDefinition } from '../types/columnTypes';
7
+ export interface IColumnChooserProps {
8
+ columns: IColumnDefinition[];
9
+ visibleColumns: Set<string>;
10
+ onVisibilityChange: (columnKey: string, visible: boolean) => void;
11
+ className?: string;
12
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Shared filter content rendering for ColumnHeaderFilter across all React UI packages.
3
+ * Each UI package provides its own popover wrapper + trigger; this component renders
4
+ * the inner filter content (text, multiselect, people, date) given the shared state
5
+ * from useColumnHeaderFilterState.
6
+ */
7
+ import * as React from 'react';
8
+ import type { ColumnFilterType, IDateFilterValue } from '../types/columnTypes';
9
+ import type { UserLike } from '../types/dataGridTypes';
10
+ import type { UseColumnHeaderFilterStateResult } from '../hooks/useColumnHeaderFilterState';
11
+ export interface IColumnHeaderFilterProps {
12
+ columnKey: string;
13
+ columnName: string;
14
+ filterType: ColumnFilterType;
15
+ isSorted?: boolean;
16
+ isSortedDescending?: boolean;
17
+ onSort?: () => void;
18
+ selectedValues?: string[];
19
+ onFilterChange?: (values: string[]) => void;
20
+ options?: string[];
21
+ isLoadingOptions?: boolean;
22
+ textValue?: string;
23
+ onTextChange?: (value: string) => void;
24
+ selectedUser?: UserLike;
25
+ onUserChange?: (user: UserLike | undefined) => void;
26
+ peopleSearch?: (query: string) => Promise<UserLike[]>;
27
+ dateValue?: IDateFilterValue;
28
+ onDateChange?: (value: IDateFilterValue | undefined) => void;
29
+ }
30
+ export interface DateFilterContentProps {
31
+ tempDateFrom: string;
32
+ setTempDateFrom: (v: string) => void;
33
+ tempDateTo: string;
34
+ setTempDateTo: (v: string) => void;
35
+ onApply: () => void;
36
+ onClear: () => void;
37
+ classNames?: DateFilterClassNames;
38
+ }
39
+ export interface DateFilterClassNames {
40
+ popoverActions?: string;
41
+ clearButton?: string;
42
+ applyButton?: string;
43
+ }
44
+ export declare const DateFilterContent: React.FC<DateFilterContentProps>;
45
+ export declare function getColumnHeaderFilterStateParams(props: IColumnHeaderFilterProps): {
46
+ filterType: ColumnFilterType;
47
+ isSorted: boolean;
48
+ isSortedDescending: boolean;
49
+ onSort: (() => void) | undefined;
50
+ selectedValues: string[] | undefined;
51
+ onFilterChange: ((values: string[]) => void) | undefined;
52
+ options: string[] | undefined;
53
+ isLoadingOptions: boolean;
54
+ textValue: string;
55
+ onTextChange: ((value: string) => void) | undefined;
56
+ selectedUser: UserLike | undefined;
57
+ onUserChange: ((user: UserLike | undefined) => void) | undefined;
58
+ peopleSearch: ((query: string) => Promise<UserLike[]>) | undefined;
59
+ dateValue: IDateFilterValue | undefined;
60
+ onDateChange: ((value: IDateFilterValue | undefined) => void) | undefined;
61
+ };
62
+ export declare function getDateFilterContentProps(state: UseColumnHeaderFilterStateResult, classNames?: DateFilterClassNames): DateFilterContentProps;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Shared filter content dispatching for ColumnHeaderFilter across all React UI packages.
3
+ *
4
+ * Each UI package provides framework-specific sub-filter components (TextFilterPopover,
5
+ * MultiSelectFilterPopover, PeopleFilterPopover, date content). This utility dispatches
6
+ * to the correct renderer based on filterType, eliminating the duplicated if/switch chain
7
+ * that was previously in each UI package's ColumnHeaderFilter component.
8
+ */
9
+ import type * as React from 'react';
10
+ import type { ColumnFilterType } from '../types/columnTypes';
11
+ import type { UserLike } from '../types/dataGridTypes';
12
+ import type { UseColumnHeaderFilterStateResult } from '../hooks/useColumnHeaderFilterState';
13
+ /**
14
+ * Framework-specific renderers for each filter type.
15
+ * Each UI package maps its own sub-filter components to these slots.
16
+ */
17
+ export interface FilterContentRenderers {
18
+ renderMultiSelect: (props: MultiSelectRendererProps) => React.ReactNode;
19
+ renderText: (props: TextRendererProps) => React.ReactNode;
20
+ renderPeople: (props: PeopleRendererProps) => React.ReactNode;
21
+ renderDate: (props: DateRendererProps) => React.ReactNode;
22
+ }
23
+ export interface MultiSelectRendererProps {
24
+ searchText: string;
25
+ onSearchChange: (value: string) => void;
26
+ options: string[];
27
+ filteredOptions: string[];
28
+ selected: Set<string>;
29
+ onOptionToggle: (option: string, checked: boolean) => void;
30
+ onSelectAll: () => void;
31
+ onClearSelection: () => void;
32
+ onApply: () => void;
33
+ isLoading: boolean;
34
+ }
35
+ export interface TextRendererProps {
36
+ value: string;
37
+ onValueChange: (value: string) => void;
38
+ onApply: () => void;
39
+ onClear: () => void;
40
+ }
41
+ export interface PeopleRendererProps {
42
+ selectedUser: UserLike | undefined;
43
+ searchText: string;
44
+ onSearchChange: (value: string) => void;
45
+ suggestions: UserLike[];
46
+ isLoading: boolean;
47
+ onUserSelect: (user: UserLike) => void;
48
+ onClearUser: () => void;
49
+ inputRef: React.RefObject<HTMLInputElement | null>;
50
+ }
51
+ export interface DateRendererProps {
52
+ tempDateFrom: string;
53
+ setTempDateFrom: (v: string) => void;
54
+ tempDateTo: string;
55
+ setTempDateTo: (v: string) => void;
56
+ onApply: () => void;
57
+ onClear: () => void;
58
+ }
59
+ /**
60
+ * Dispatches to the appropriate filter content renderer based on filterType.
61
+ * Eliminates the duplicated if/switch chain in each UI package's ColumnHeaderFilter.
62
+ *
63
+ * @param filterType - The column's filter type
64
+ * @param state - The result from useColumnHeaderFilterState
65
+ * @param options - The filter options array (for multiSelect)
66
+ * @param isLoadingOptions - Whether options are loading
67
+ * @param selectedUser - The currently selected user (for people filter)
68
+ * @param renderers - Framework-specific renderer functions
69
+ * @returns The rendered filter content, or null for unsupported filter types
70
+ */
71
+ export declare function renderFilterContent(filterType: ColumnFilterType, state: UseColumnHeaderFilterStateResult, options: string[], isLoadingOptions: boolean, selectedUser: UserLike | undefined, renderers: FilterContentRenderers): React.ReactNode;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared props interface for PaginationControls across all React UI packages.
3
+ * Each UI package renders its own framework-specific buttons, selects, and layout
4
+ * but shares this common prop shape.
5
+ */
6
+ export interface IPaginationControlsProps {
7
+ currentPage: number;
8
+ pageSize: number;
9
+ totalCount: number;
10
+ onPageChange: (page: number) => void;
11
+ onPageSizeChange: (pageSize: number) => void;
12
+ pageSizeOptions?: number[];
13
+ entityLabelPlural?: string;
14
+ className?: string;
15
+ }
@@ -0,0 +1,17 @@
1
+ import type * as React from 'react';
2
+ /**
3
+ * Module-scope stable constants shared across all React UI DataGridTable implementations.
4
+ * Avoid per-render allocations by keeping these at module scope.
5
+ */
6
+ /** Root container style for the DataGridTable (flex column layout). */
7
+ export declare const GRID_ROOT_STYLE: React.CSSProperties;
8
+ /** Applied to cells that support editing — shows the cell cursor. */
9
+ export declare const CURSOR_CELL_STYLE: React.CSSProperties;
10
+ /** Minimum size for popover anchor elements. */
11
+ export declare const POPOVER_ANCHOR_STYLE: React.CSSProperties;
12
+ /** Prevents the default browser action for mouse events. */
13
+ export declare const PREVENT_DEFAULT: (e: React.MouseEvent) => void;
14
+ /** No-operation function. */
15
+ export declare const NOOP: () => void;
16
+ /** Stops event propagation (e.g. click on checkbox inside a row). */
17
+ export declare const STOP_PROPAGATION: (e: React.MouseEvent) => void;
@@ -22,7 +22,7 @@ export { useDebounce } from './useDebounce';
22
22
  export { useFillHandle } from './useFillHandle';
23
23
  export type { UseFillHandleResult, UseFillHandleParams } from './useFillHandle';
24
24
  export { useDataGridState } from './useDataGridState';
25
- export type { UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, } from './useDataGridState';
25
+ export type { UseDataGridStateParams, UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState, } from './useDataGridState';
26
26
  export { useColumnHeaderFilterState } from './useColumnHeaderFilterState';
27
27
  export type { UseColumnHeaderFilterStateParams, UseColumnHeaderFilterStateResult, } from './useColumnHeaderFilterState';
28
28
  export { useTextFilterState } from './useTextFilterState';
@@ -56,3 +56,5 @@ export type { IVirtualScrollConfig, UseVirtualScrollParams, UseVirtualScrollResu
56
56
  export { useLatestRef } from './useLatestRef';
57
57
  export { usePaginationControls } from './usePaginationControls';
58
58
  export type { UsePaginationControlsProps, UsePaginationControlsResult, } from './usePaginationControls';
59
+ export { useDataGridTableOrchestration } from './useDataGridTableOrchestration';
60
+ export type { UseDataGridTableOrchestrationParams, UseDataGridTableOrchestrationResult, } from './useDataGridTableOrchestration';
@@ -4,11 +4,13 @@ export interface UseColumnHeaderMenuStateParams {
4
4
  onUnpinColumn: (columnId: string) => void;
5
5
  sortBy?: string;
6
6
  sortDirection: 'asc' | 'desc';
7
- onColumnSort: (columnKey: string) => void;
7
+ onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
8
8
  onColumnResized?: (columnId: string, width: number) => void;
9
+ onAutosizeColumn?: (columnId: string, width: number) => void;
9
10
  columns: Array<{
10
11
  columnId: string;
11
12
  width?: number;
13
+ minWidth?: number;
12
14
  sortable?: boolean;
13
15
  resizable?: boolean;
14
16
  }>;
@@ -128,12 +128,8 @@ export interface DataGridPinningState {
128
128
  pinColumn: (columnId: string, side: 'left' | 'right') => void;
129
129
  unpinColumn: (columnId: string) => void;
130
130
  isPinned: (columnId: string) => 'left' | 'right' | undefined;
131
- computeLeftOffsets: (visibleCols: {
132
- columnId: string;
133
- }[], columnWidths: Record<string, number>, defaultWidth: number, hasCheckboxColumn: boolean, checkboxColumnWidth: number) => Record<string, number>;
134
- computeRightOffsets: (visibleCols: {
135
- columnId: string;
136
- }[], columnWidths: Record<string, number>, defaultWidth: number) => Record<string, number>;
131
+ leftOffsets: Record<string, number>;
132
+ rightOffsets: Record<string, number>;
137
133
  headerMenu: {
138
134
  isOpen: boolean;
139
135
  openForColumn: string | null;
@@ -0,0 +1,131 @@
1
+ import type { RefObject } from 'react';
2
+ import type { IOGridDataGridProps, IColumnDef } from '../types';
3
+ import type { UseDataGridStateResult, DataGridLayoutState, DataGridRowSelectionState, DataGridEditingState, DataGridCellInteractionState, DataGridContextMenuState, DataGridViewModelState, DataGridPinningState } from './useDataGridState';
4
+ import type { UseColumnResizeResult } from './useColumnResize';
5
+ import type { UseColumnReorderResult } from './useColumnReorder';
6
+ import type { UseVirtualScrollResult } from './useVirtualScroll';
7
+ import type { HeaderFilterConfigInput, CellRenderDescriptorInput } from '../utils';
8
+ import type { IStatusBarProps, RowId, HeaderRow } from '../types';
9
+ /** Parameters for the orchestration hook. */
10
+ export interface UseDataGridTableOrchestrationParams<T> {
11
+ props: IOGridDataGridProps<T>;
12
+ }
13
+ /** Everything the framework-specific view layer needs to render. */
14
+ export interface UseDataGridTableOrchestrationResult<T> {
15
+ wrapperRef: RefObject<HTMLDivElement | null>;
16
+ tableContainerRef: RefObject<HTMLDivElement | null>;
17
+ lastMouseShiftRef: React.MutableRefObject<boolean>;
18
+ state: UseDataGridStateResult<T>;
19
+ layout: DataGridLayoutState<T>;
20
+ rowSel: DataGridRowSelectionState;
21
+ editing: DataGridEditingState<T>;
22
+ interaction: DataGridCellInteractionState;
23
+ ctxMenu: DataGridContextMenuState;
24
+ viewModels: DataGridViewModelState<T>;
25
+ pinning: DataGridPinningState;
26
+ handleResizeStart: UseColumnResizeResult<T>['handleResizeStart'];
27
+ getColumnWidth: UseColumnResizeResult<T>['getColumnWidth'];
28
+ isReorderDragging: UseColumnReorderResult['isDragging'];
29
+ dropIndicatorX: UseColumnReorderResult['dropIndicatorX'];
30
+ handleHeaderMouseDown: UseColumnReorderResult['handleHeaderMouseDown'];
31
+ virtualScrollEnabled: boolean;
32
+ virtualRowHeight: number;
33
+ visibleRange: UseVirtualScrollResult['visibleRange'];
34
+ items: T[];
35
+ columns: IOGridDataGridProps<T>['columns'];
36
+ getRowId: IOGridDataGridProps<T>['getRowId'];
37
+ emptyState: IOGridDataGridProps<T>['emptyState'];
38
+ layoutMode: 'fill' | 'content';
39
+ rowSelection: IOGridDataGridProps<T>['rowSelection'];
40
+ freezeRows: IOGridDataGridProps<T>['freezeRows'];
41
+ freezeCols: IOGridDataGridProps<T>['freezeCols'];
42
+ suppressHorizontalScroll: IOGridDataGridProps<T>['suppressHorizontalScroll'];
43
+ isLoading: boolean;
44
+ loadingMessage: string;
45
+ ariaLabel: string | undefined;
46
+ ariaLabelledBy: string | undefined;
47
+ visibleColumns: IOGridDataGridProps<T>['visibleColumns'];
48
+ columnOrder: IOGridDataGridProps<T>['columnOrder'];
49
+ columnReorder: IOGridDataGridProps<T>['columnReorder'];
50
+ density: 'compact' | 'normal' | 'comfortable';
51
+ pinnedColumns: IOGridDataGridProps<T>['pinnedColumns'];
52
+ currentPage: number;
53
+ propPageSize: number;
54
+ rowNumberOffset: number;
55
+ headerRows: HeaderRow<T>[];
56
+ allowOverflowX: boolean;
57
+ fitToContent: boolean;
58
+ editCallbacks: {
59
+ commitCellEdit: DataGridEditingState<T>['commitCellEdit'];
60
+ setEditingCell: DataGridEditingState<T>['setEditingCell'];
61
+ setPendingEditorValue: DataGridEditingState<T>['setPendingEditorValue'];
62
+ cancelPopoverEdit: DataGridEditingState<T>['cancelPopoverEdit'];
63
+ };
64
+ interactionHandlers: {
65
+ handleCellMouseDown: DataGridCellInteractionState['handleCellMouseDown'];
66
+ setActiveCell: DataGridCellInteractionState['setActiveCell'];
67
+ setEditingCell: DataGridEditingState<T>['setEditingCell'];
68
+ handleCellContextMenu: DataGridContextMenuState['handleCellContextMenu'];
69
+ };
70
+ cellDescriptorInputRef: React.MutableRefObject<CellRenderDescriptorInput<T>>;
71
+ pendingEditorValueRef: React.MutableRefObject<unknown>;
72
+ popoverAnchorElRef: React.MutableRefObject<HTMLElement | null>;
73
+ selectedRowIdsRef: React.MutableRefObject<Set<RowId>>;
74
+ handleSingleRowClick: (e: React.MouseEvent<HTMLTableRowElement>) => void;
75
+ handlePasteVoid: () => void;
76
+ visibleCols: IColumnDef<T>[];
77
+ totalColCount: number;
78
+ hasCheckboxCol: boolean;
79
+ hasRowNumbersCol: boolean;
80
+ colOffset: number;
81
+ containerWidth: number;
82
+ minTableWidth: number;
83
+ desiredTableWidth: number;
84
+ columnSizingOverrides: Record<string, {
85
+ widthPx: number;
86
+ }>;
87
+ setColumnSizingOverrides: React.Dispatch<React.SetStateAction<Record<string, {
88
+ widthPx: number;
89
+ }>>>;
90
+ selectedRowIds: Set<RowId>;
91
+ updateSelection: DataGridRowSelectionState['updateSelection'];
92
+ handleRowCheckboxChange: DataGridRowSelectionState['handleRowCheckboxChange'];
93
+ handleSelectAll: DataGridRowSelectionState['handleSelectAll'];
94
+ allSelected: boolean;
95
+ someSelected: boolean;
96
+ editingCell: DataGridEditingState<T>['editingCell'];
97
+ setPopoverAnchorEl: DataGridEditingState<T>['setPopoverAnchorEl'];
98
+ cancelPopoverEdit: DataGridEditingState<T>['cancelPopoverEdit'];
99
+ setActiveCell: DataGridCellInteractionState['setActiveCell'];
100
+ selectionRange: DataGridCellInteractionState['selectionRange'];
101
+ hasCellSelection: boolean;
102
+ handleGridKeyDown: DataGridCellInteractionState['handleGridKeyDown'];
103
+ handleFillHandleMouseDown: DataGridCellInteractionState['handleFillHandleMouseDown'];
104
+ handleCopy: DataGridCellInteractionState['handleCopy'];
105
+ handleCut: DataGridCellInteractionState['handleCut'];
106
+ cutRange: DataGridCellInteractionState['cutRange'];
107
+ copyRange: DataGridCellInteractionState['copyRange'];
108
+ canUndo: boolean;
109
+ canRedo: boolean;
110
+ onUndo: DataGridCellInteractionState['onUndo'];
111
+ onRedo: DataGridCellInteractionState['onRedo'];
112
+ isDragging: boolean;
113
+ menuPosition: DataGridContextMenuState['menuPosition'];
114
+ handleCellContextMenu: DataGridContextMenuState['handleCellContextMenu'];
115
+ closeContextMenu: DataGridContextMenuState['closeContextMenu'];
116
+ headerFilterInput: HeaderFilterConfigInput;
117
+ cellDescriptorInput: CellRenderDescriptorInput<T>;
118
+ statusBarConfig: IStatusBarProps | null;
119
+ showEmptyInGrid: boolean;
120
+ onCellError: DataGridViewModelState<T>['onCellError'];
121
+ headerMenu: DataGridPinningState['headerMenu'];
122
+ }
123
+ /**
124
+ * Shared orchestration hook for DataGridTable.
125
+ *
126
+ * Encapsulates all state management and computation that is identical across
127
+ * Radix, Fluent, and Material DataGridTable implementations. Each UI package
128
+ * calls this hook, then renders its own framework-specific JSX using the
129
+ * returned values.
130
+ */
131
+ export declare function useDataGridTableOrchestration<T>(params: UseDataGridTableOrchestrationParams<T>): UseDataGridTableOrchestrationResult<T>;
@@ -1,8 +1,9 @@
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, } 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, 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, } 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, 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';
6
+ export { GRID_ROOT_STYLE, CURSOR_CELL_STYLE, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, NOOP, STOP_PROPAGATION, } from './constants/domHelpers';
6
7
  export { OGridLayout } from './components/OGridLayout';
7
8
  export type { OGridLayoutProps } from './components/OGridLayout';
8
9
  export { StatusBar } from './components/StatusBar';
@@ -19,5 +20,11 @@ export { CellErrorBoundary } from './components/CellErrorBoundary';
19
20
  export type { CellErrorBoundaryProps } from './components/CellErrorBoundary';
20
21
  export { EmptyState } from './components/EmptyState';
21
22
  export type { EmptyStateProps } from './components/EmptyState';
23
+ export { DateFilterContent, getColumnHeaderFilterStateParams, getDateFilterContentProps, } from './components/ColumnHeaderFilterContent';
24
+ export type { IColumnHeaderFilterProps, DateFilterContentProps, DateFilterClassNames, } from './components/ColumnHeaderFilterContent';
22
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';
23
26
  export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, HeaderFilterConfigInput, HeaderFilterConfig, CellRenderDescriptorInput, CellRenderDescriptor, CellRenderMode, CellInteractionHandlers, ParseValueResult, AggregationResult, GridRowComparatorProps, } from './utils';
27
+ export { renderFilterContent } from './components/ColumnHeaderFilterRenderers';
28
+ export type { FilterContentRenderers, MultiSelectRendererProps, TextRendererProps, PeopleRendererProps, DateRendererProps, } from './components/ColumnHeaderFilterRenderers';
29
+ export type { IColumnChooserProps } from './components/ColumnChooserProps';
30
+ export type { IPaginationControlsProps } from './components/PaginationControlsProps';
@@ -105,7 +105,7 @@ export interface IOGridDataGridProps<T> {
105
105
  getRowId: (item: T) => RowId;
106
106
  sortBy?: string;
107
107
  sortDirection: 'asc' | 'desc';
108
- onColumnSort: (columnKey: string) => void;
108
+ onColumnSort: (columnKey: string, direction?: 'asc' | 'desc' | null) => void;
109
109
  visibleColumns: Set<string>;
110
110
  /** Optional column display order (column ids). When set, visible columns are ordered by this array. */
111
111
  columnOrder?: string[];
@@ -1,4 +1,4 @@
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, } 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, getContextMenuHandlers, formatShortcut, parseValue, numberParser, currencyParser, dateParser, emailParser, booleanParser, computeAggregations, processClientSideData, computeNextSortState, measureColumnContentWidth, AUTOSIZE_EXTRA_PX, AUTOSIZE_MAX_PX, } from '@alaarab/ogrid-core';
2
2
  export type { CsvColumn, StatusBarPart, StatusBarPartsInput, GridContextMenuItem, GridContextMenuHandlerProps, PaginationViewModel, ParseValueResult, AggregationResult, } 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';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alaarab/ogrid-react",
3
- "version": "2.0.11",
3
+ "version": "2.0.12",
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.11"
39
+ "@alaarab/ogrid-core": "2.0.12"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@tanstack/react-virtual": "^3.0.0",