@alaarab/ogrid-react 2.0.11 → 2.0.13
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 +2 -2
- package/dist/esm/components/CellErrorBoundary.js +1 -1
- package/dist/esm/components/ColumnChooserProps.js +6 -0
- package/dist/esm/components/ColumnHeaderFilterContent.js +35 -0
- package/dist/esm/components/ColumnHeaderFilterRenderers.js +67 -0
- package/dist/esm/components/MarchingAntsOverlay.js +20 -11
- package/dist/esm/components/PaginationControlsProps.js +6 -0
- package/dist/esm/constants/domHelpers.js +16 -0
- package/dist/esm/hooks/index.js +1 -0
- package/dist/esm/hooks/useColumnHeaderMenuState.js +14 -26
- package/dist/esm/hooks/useColumnResize.js +29 -0
- package/dist/esm/hooks/useDataGridState.js +54 -5
- package/dist/esm/hooks/useDataGridTableOrchestration.js +200 -0
- package/dist/esm/hooks/useOGrid.js +3 -6
- package/dist/esm/index.js +6 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/types/components/ColumnChooserProps.d.ts +12 -0
- package/dist/types/components/ColumnHeaderFilterContent.d.ts +62 -0
- package/dist/types/components/ColumnHeaderFilterRenderers.d.ts +71 -0
- package/dist/types/components/PaginationControlsProps.d.ts +15 -0
- package/dist/types/constants/domHelpers.d.ts +17 -0
- package/dist/types/hooks/index.d.ts +3 -1
- package/dist/types/hooks/useColumnHeaderMenuState.d.ts +3 -1
- package/dist/types/hooks/useDataGridState.d.ts +2 -6
- package/dist/types/hooks/useDataGridTableOrchestration.d.ts +131 -0
- package/dist/types/index.d.ts +9 -2
- package/dist/types/types/dataGridTypes.d.ts +1 -1
- package/dist/types/utils/index.d.ts +1 -1
- 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,
|
|
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',
|
|
@@ -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
|
-
|
|
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:
|
|
66
|
-
left:
|
|
67
|
-
width:
|
|
68
|
-
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,
|
|
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:
|
|
75
|
-
left:
|
|
76
|
-
width:
|
|
77
|
-
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,
|
|
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,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(); };
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -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
|
-
|
|
67
|
+
const resizer = onAutosizeColumn ?? onColumnResized;
|
|
68
|
+
if (!openForColumn || !resizer || !isResizable)
|
|
67
69
|
return;
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
-
|
|
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,
|
|
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';
|
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, } 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
|
-
|
|
132
|
-
|
|
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>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "2.0.13",
|
|
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.13"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@tanstack/react-virtual": "^3.0.0",
|