@alaarab/ogrid-core 1.4.0 → 1.6.0
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/OGridLayout.js +3 -7
- package/dist/esm/components/SideBar.js +14 -12
- package/dist/esm/hooks/useActiveCell.js +5 -0
- package/dist/esm/hooks/useColumnResize.js +54 -40
- package/dist/esm/hooks/useDataGridState.js +90 -82
- package/dist/esm/hooks/useOGrid.js +43 -117
- package/dist/esm/index.js +2 -2
- package/dist/esm/types/dataGridTypes.js +0 -24
- package/dist/esm/types/index.js +1 -1
- package/dist/esm/utils/aggregationUtils.js +3 -1
- package/dist/esm/utils/clientSideData.js +94 -0
- package/dist/esm/utils/dataGridViewModel.js +9 -14
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/ogridHelpers.js +5 -7
- package/dist/esm/utils/statusBarHelpers.js +4 -2
- package/dist/types/components/OGridLayout.d.ts +0 -7
- package/dist/types/components/SideBar.d.ts +3 -7
- package/dist/types/components/StatusBar.d.ts +1 -0
- package/dist/types/hooks/index.d.ts +1 -1
- package/dist/types/hooks/useDataGridState.d.ts +63 -39
- package/dist/types/hooks/useOGrid.d.ts +0 -1
- package/dist/types/index.d.ts +4 -4
- package/dist/types/types/dataGridTypes.d.ts +31 -20
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/utils/clientSideData.d.ts +13 -0
- package/dist/types/utils/dataGridViewModel.d.ts +4 -10
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/ogridHelpers.d.ts +1 -1
- package/dist/types/utils/statusBarHelpers.d.ts +2 -0
- package/package.json +1 -1
|
@@ -35,6 +35,7 @@ const footerStripStyle = {
|
|
|
35
35
|
const gridAreaFlexStyle = {
|
|
36
36
|
width: '100%',
|
|
37
37
|
minWidth: 0,
|
|
38
|
+
minHeight: 0,
|
|
38
39
|
flex: 1,
|
|
39
40
|
display: 'flex',
|
|
40
41
|
};
|
|
@@ -55,7 +56,6 @@ const gridChildStyle = {
|
|
|
55
56
|
};
|
|
56
57
|
/**
|
|
57
58
|
* Renders OGrid layout as a unified bordered container:
|
|
58
|
-
* [deprecated title above]
|
|
59
59
|
* ┌────────────────────────────────────┐
|
|
60
60
|
* │ [toolbar strip] │
|
|
61
61
|
* ├────────────────────────────────────┤
|
|
@@ -65,18 +65,14 @@ const gridChildStyle = {
|
|
|
65
65
|
* └────────────────────────────────────┘
|
|
66
66
|
*/
|
|
67
67
|
export function OGridLayout(props) {
|
|
68
|
-
const { containerComponent: Container = 'div', containerProps = {},
|
|
68
|
+
const { containerComponent: Container = 'div', containerProps = {}, className, toolbar, toolbarEnd, children, pagination, sideBar, } = props;
|
|
69
69
|
const hasSideBar = sideBar != null;
|
|
70
70
|
const sideBarPosition = sideBar?.position ?? 'right';
|
|
71
|
-
// Backward compat: columnChooser prop → toolbarEnd
|
|
72
|
-
const toolbarEnd = toolbarEndProp ?? columnChooser;
|
|
73
71
|
const hasToolbar = toolbar != null || toolbarEnd != null;
|
|
74
|
-
// Root styles: flex column, fill parent height, gap for deprecated title spacing
|
|
75
72
|
const rootStyle = {
|
|
76
73
|
display: 'flex',
|
|
77
74
|
flexDirection: 'column',
|
|
78
75
|
height: '100%',
|
|
79
|
-
gap: title != null ? (typeof gap === 'number' ? `${gap}px` : gap) : undefined,
|
|
80
76
|
};
|
|
81
|
-
return (
|
|
77
|
+
return (_jsx(Container, { className: className, style: rootStyle, ...containerProps, children: _jsxs("div", { style: borderedContainerStyle, children: [hasToolbar && (_jsxs("div", { style: toolbarStripStyle, children: [_jsx("div", { style: toolbarSectionStyle, children: toolbar }), _jsx("div", { style: toolbarSectionStyle, children: toolbarEnd })] })), hasSideBar ? (_jsxs("div", { style: gridAreaFlexStyle, children: [sideBarPosition === 'left' && _jsx(SideBar, { ...sideBar }), _jsx("div", { style: gridChildStyle, children: children }), sideBarPosition !== 'left' && _jsx(SideBar, { ...sideBar })] })) : (_jsx("div", { style: gridAreaSoloStyle, children: children })), pagination && (_jsx("div", { style: footerStripStyle, children: pagination }))] }) }));
|
|
82
78
|
}
|
|
@@ -6,7 +6,7 @@ const PANEL_LABELS = {
|
|
|
6
6
|
filters: 'Filters',
|
|
7
7
|
};
|
|
8
8
|
export function SideBar(props) {
|
|
9
|
-
const { activePanel, onPanelChange, panels, position, columns, visibleColumns, onVisibilityChange, onSetVisibleColumns, filterableColumns,
|
|
9
|
+
const { activePanel, onPanelChange, panels, position, columns, visibleColumns, onVisibilityChange, onSetVisibleColumns, filterableColumns, filters, onFilterChange, filterOptions, } = props;
|
|
10
10
|
const isOpen = activePanel !== null;
|
|
11
11
|
const handleTabClick = (panel) => {
|
|
12
12
|
onPanelChange(activePanel === panel ? null : panel);
|
|
@@ -47,7 +47,7 @@ export function SideBar(props) {
|
|
|
47
47
|
padding: '8px 12px',
|
|
48
48
|
borderBottom: '1px solid var(--ogrid-border, #e0e0e0)',
|
|
49
49
|
fontWeight: 600,
|
|
50
|
-
}, children: [_jsx("span", { children: PANEL_LABELS[activePanel] }), _jsx("button", { onClick: () => onPanelChange(null), style: { border: 'none', background: 'transparent', cursor: 'pointer', fontSize: 16, color: 'var(--ogrid-fg, #242424)' }, "aria-label": "Close panel", children: "\u00D7" })] }), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '8px 12px' }, children: [activePanel === 'columns' && (_jsx(ColumnsPanel, { columns: columns, visibleColumns: visibleColumns, onVisibilityChange: onVisibilityChange, onSetVisibleColumns: onSetVisibleColumns })), activePanel === 'filters' && (_jsx(FiltersPanel, { filterableColumns: filterableColumns,
|
|
50
|
+
}, children: [_jsx("span", { children: PANEL_LABELS[activePanel] }), _jsx("button", { onClick: () => onPanelChange(null), style: { border: 'none', background: 'transparent', cursor: 'pointer', fontSize: 16, color: 'var(--ogrid-fg, #242424)' }, "aria-label": "Close panel", children: "\u00D7" })] }), _jsxs("div", { style: { flex: 1, overflowY: 'auto', padding: '8px 12px' }, children: [activePanel === 'columns' && (_jsx(ColumnsPanel, { columns: columns, visibleColumns: visibleColumns, onVisibilityChange: onVisibilityChange, onSetVisibleColumns: onSetVisibleColumns })), activePanel === 'filters' && (_jsx(FiltersPanel, { filterableColumns: filterableColumns, filters: filters, onFilterChange: onFilterChange, filterOptions: filterOptions }))] })] })) : null;
|
|
51
51
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'row', flexShrink: 0 }, role: "complementary", "aria-label": "Side bar", children: [position === 'left' && tabStrip, position === 'left' && panelContent, position === 'right' && panelContent, position === 'right' && tabStrip] }));
|
|
52
52
|
}
|
|
53
53
|
// --- Internal sub-components ---
|
|
@@ -70,28 +70,30 @@ function ColumnsPanel(props) {
|
|
|
70
70
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { display: 'flex', gap: 8, marginBottom: 8 }, children: [_jsx("button", { onClick: handleSelectAll, disabled: allVisible, style: { flex: 1, cursor: 'pointer', background: 'var(--ogrid-bg-subtle, #f3f2f1)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4, padding: '4px 8px' }, children: "Select All" }), _jsx("button", { onClick: handleClearAll, style: { flex: 1, cursor: 'pointer', background: 'var(--ogrid-bg-subtle, #f3f2f1)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4, padding: '4px 8px' }, children: "Clear All" })] }), columns.map((col) => (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 6, padding: '2px 0', cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: visibleColumns.has(col.columnId), onChange: (e) => onVisibilityChange(col.columnId, e.target.checked), disabled: col.required }), _jsx("span", { children: col.name })] }, col.columnId)))] }));
|
|
71
71
|
}
|
|
72
72
|
function FiltersPanel(props) {
|
|
73
|
-
const { filterableColumns,
|
|
73
|
+
const { filterableColumns, filters, onFilterChange, filterOptions } = props;
|
|
74
74
|
if (filterableColumns.length === 0) {
|
|
75
75
|
return _jsx("div", { style: { color: 'var(--ogrid-muted, #999)', fontStyle: 'italic' }, children: "No filterable columns" });
|
|
76
76
|
}
|
|
77
77
|
return (_jsx(_Fragment, { children: filterableColumns.map((col) => {
|
|
78
78
|
const filterKey = col.filterField;
|
|
79
|
-
return (_jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 4, fontSize: 13 }, children: col.name }), col.filterType === 'text' && (_jsx("input", { type: "text", value:
|
|
79
|
+
return (_jsxs("div", { style: { marginBottom: 12 }, children: [_jsx("div", { style: { fontWeight: 500, marginBottom: 4, fontSize: 13 }, children: col.name }), col.filterType === 'text' && (_jsx("input", { type: "text", value: filters[filterKey]?.type === 'text' ? filters[filterKey].value : '', onChange: (e) => onFilterChange(filterKey, e.target.value ? { type: 'text', value: e.target.value } : undefined), placeholder: `Filter ${col.name}...`, "aria-label": `Filter ${col.name}`, style: { width: '100%', boxSizing: 'border-box', padding: '4px 6px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4 } })), col.filterType === 'date' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 4 }, children: [_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 }, children: ["From:", _jsx("input", { type: "date", value: filters[filterKey]?.type === 'date' ? (filters[filterKey].value.from ?? '') : '', onChange: (e) => {
|
|
80
80
|
const from = e.target.value || undefined;
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
const existingValue = filters[filterKey]?.type === 'date' ? filters[filterKey].value : {};
|
|
82
|
+
const to = existingValue.to;
|
|
83
|
+
onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
84
|
+
}, "aria-label": `${col.name} from date`, style: { flex: 1, padding: '2px 4px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4 } })] }), _jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 4, fontSize: 12 }, children: ["To:", _jsx("input", { type: "date", value: filters[filterKey]?.type === 'date' ? (filters[filterKey].value.to ?? '') : '', onChange: (e) => {
|
|
84
85
|
const to = e.target.value || undefined;
|
|
85
|
-
const
|
|
86
|
-
|
|
86
|
+
const existingValue = filters[filterKey]?.type === 'date' ? filters[filterKey].value : {};
|
|
87
|
+
const from = existingValue.from;
|
|
88
|
+
onFilterChange(filterKey, from || to ? { type: 'date', value: { from, to } } : undefined);
|
|
87
89
|
}, "aria-label": `${col.name} to date`, style: { flex: 1, padding: '2px 4px', background: 'var(--ogrid-bg, #fff)', color: 'var(--ogrid-fg, #242424)', border: '1px solid var(--ogrid-border, #e0e0e0)', borderRadius: 4 } })] })] })), col.filterType === 'multiSelect' && (_jsx("div", { style: { maxHeight: 120, overflowY: 'auto' }, role: "group", "aria-label": `${col.name} options`, children: (filterOptions[filterKey] ?? []).map((opt) => {
|
|
88
|
-
const selected =
|
|
90
|
+
const selected = filters[filterKey]?.type === 'multiSelect' ? filters[filterKey].value.includes(opt) : false;
|
|
89
91
|
return (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 4, padding: '1px 0', cursor: 'pointer', fontSize: 13 }, children: [_jsx("input", { type: "checkbox", checked: selected, onChange: (e) => {
|
|
90
|
-
const current =
|
|
92
|
+
const current = filters[filterKey]?.type === 'multiSelect' ? filters[filterKey].value : [];
|
|
91
93
|
const next = e.target.checked
|
|
92
94
|
? [...current, opt]
|
|
93
95
|
: current.filter((v) => v !== opt);
|
|
94
|
-
|
|
96
|
+
onFilterChange(filterKey, next.length > 0 ? { type: 'multiSelect', value: next } : undefined);
|
|
95
97
|
} }), _jsx("span", { children: opt })] }, opt));
|
|
96
98
|
}) }))] }, col.columnId));
|
|
97
99
|
}) }));
|
|
@@ -17,6 +17,11 @@ export function useActiveCell(wrapperRef, editingCell) {
|
|
|
17
17
|
const cell = wrapperRef.current.querySelector(selector);
|
|
18
18
|
if (cell) {
|
|
19
19
|
if (typeof cell.scrollIntoView === 'function') {
|
|
20
|
+
// Account for sticky <thead> so scrollIntoView doesn't leave
|
|
21
|
+
// the cell hidden behind the header.
|
|
22
|
+
const thead = wrapperRef.current.querySelector('thead');
|
|
23
|
+
const headerHeight = thead ? thead.getBoundingClientRect().height : 0;
|
|
24
|
+
cell.style.scrollMarginTop = `${headerHeight}px`;
|
|
20
25
|
cell.scrollIntoView({ block: 'nearest', inline: 'nearest' });
|
|
21
26
|
}
|
|
22
27
|
if (document.activeElement !== cell && typeof cell.focus === 'function') {
|
|
@@ -1,50 +1,64 @@
|
|
|
1
|
-
import { useCallback, useRef
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
2
|
export function useColumnResize({ columnSizingOverrides, setColumnSizingOverrides, minWidth = 80, defaultWidth = 120, onColumnResized, }) {
|
|
3
|
-
const
|
|
3
|
+
const rafRef = useRef(0);
|
|
4
|
+
const onColumnResizedRef = useRef(onColumnResized);
|
|
5
|
+
onColumnResizedRef.current = onColumnResized;
|
|
4
6
|
const handleResizeStart = useCallback((e, col) => {
|
|
5
7
|
e.preventDefault();
|
|
6
8
|
e.stopPropagation();
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const startX = e.clientX;
|
|
10
|
+
const columnId = col.columnId;
|
|
11
|
+
// Measure the actual rendered width from the DOM. With table-layout: auto,
|
|
12
|
+
// the browser may have auto-sized the column wider than the config values.
|
|
13
|
+
// The resize handle is a direct child of <th>, so parentElement is the header cell.
|
|
14
|
+
const thEl = e.currentTarget.parentElement;
|
|
15
|
+
const startWidth = thEl
|
|
16
|
+
? thEl.getBoundingClientRect().width
|
|
17
|
+
: columnSizingOverrides[columnId]?.widthPx
|
|
18
|
+
?? col.idealWidth
|
|
19
|
+
?? col.defaultWidth
|
|
20
|
+
?? defaultWidth;
|
|
21
|
+
let latestWidth = startWidth;
|
|
22
|
+
// Lock cursor and prevent text selection during drag
|
|
23
|
+
const prevCursor = document.body.style.cursor;
|
|
24
|
+
const prevUserSelect = document.body.style.userSelect;
|
|
25
|
+
document.body.style.cursor = 'col-resize';
|
|
26
|
+
document.body.style.userSelect = 'none';
|
|
27
|
+
const flushWidth = () => {
|
|
28
|
+
setColumnSizingOverrides((prev) => ({
|
|
29
|
+
...prev,
|
|
30
|
+
[columnId]: { widthPx: latestWidth },
|
|
31
|
+
}));
|
|
15
32
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
const onMove = (moveEvent) => {
|
|
34
|
+
const deltaX = moveEvent.clientX - startX;
|
|
35
|
+
latestWidth = Math.max(minWidth, startWidth + deltaX);
|
|
36
|
+
if (!rafRef.current) {
|
|
37
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
38
|
+
rafRef.current = 0;
|
|
39
|
+
flushWidth();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const onUp = () => {
|
|
44
|
+
document.removeEventListener('mousemove', onMove);
|
|
45
|
+
document.removeEventListener('mouseup', onUp);
|
|
46
|
+
// Restore cursor and user-select
|
|
47
|
+
document.body.style.cursor = prevCursor;
|
|
48
|
+
document.body.style.userSelect = prevUserSelect;
|
|
49
|
+
// Cancel pending RAF and flush final width synchronously
|
|
50
|
+
if (rafRef.current) {
|
|
51
|
+
cancelAnimationFrame(rafRef.current);
|
|
52
|
+
rafRef.current = 0;
|
|
53
|
+
}
|
|
54
|
+
flushWidth();
|
|
55
|
+
if (onColumnResizedRef.current) {
|
|
56
|
+
onColumnResizedRef.current(columnId, latestWidth);
|
|
34
57
|
}
|
|
35
|
-
}
|
|
36
|
-
resizingRef.current = null;
|
|
37
|
-
}, [onColumnResized, columnSizingOverrides]);
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
const handleMouseMove = (e) => handleResizeMove(e);
|
|
40
|
-
const handleMouseUp = () => handleResizeEnd();
|
|
41
|
-
document.addEventListener('mousemove', handleMouseMove);
|
|
42
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
43
|
-
return () => {
|
|
44
|
-
document.removeEventListener('mousemove', handleMouseMove);
|
|
45
|
-
document.removeEventListener('mouseup', handleMouseUp);
|
|
46
58
|
};
|
|
47
|
-
|
|
59
|
+
document.addEventListener('mousemove', onMove);
|
|
60
|
+
document.addEventListener('mouseup', onUp);
|
|
61
|
+
}, [columnSizingOverrides, defaultWidth, minWidth, setColumnSizingOverrides]);
|
|
48
62
|
const getColumnWidth = useCallback((col) => {
|
|
49
63
|
return columnSizingOverrides[col.columnId]?.widthPx
|
|
50
64
|
?? col.idealWidth
|
|
@@ -23,12 +23,25 @@ const NOOP_CTX = (_e) => { };
|
|
|
23
23
|
*/
|
|
24
24
|
export function useDataGridState(params) {
|
|
25
25
|
const { props, wrapperRef } = params;
|
|
26
|
-
const { items, columns, getRowId, visibleColumns, columnOrder, rowSelection = 'none', selectedRows: controlledSelectedRows, onSelectionChange, statusBar, emptyState, editable, cellSelection: cellSelectionProp, onCellValueChanged: onCellValueChangedProp, initialColumnWidths, onColumnResized, } = props;
|
|
26
|
+
const { items, columns, getRowId, visibleColumns, columnOrder, rowSelection = 'none', selectedRows: controlledSelectedRows, onSelectionChange, statusBar, emptyState, editable, cellSelection: cellSelectionProp, onCellValueChanged: onCellValueChangedProp, initialColumnWidths, onColumnResized, pinnedColumns, } = props;
|
|
27
27
|
const cellSelection = cellSelectionProp !== false;
|
|
28
28
|
// Wrap onCellValueChanged with undo/redo tracking — all edits are recorded automatically
|
|
29
29
|
const undoRedo = useUndoRedo({ onCellValueChanged: onCellValueChangedProp });
|
|
30
30
|
const onCellValueChanged = undoRedo.onCellValueChanged;
|
|
31
|
-
const
|
|
31
|
+
const flatColumnsRaw = useMemo(() => flattenColumns(columns), [columns]);
|
|
32
|
+
// Apply runtime pin overrides (from applyColumnState or programmatic changes)
|
|
33
|
+
const flatColumns = useMemo(() => {
|
|
34
|
+
if (!pinnedColumns || Object.keys(pinnedColumns).length === 0)
|
|
35
|
+
return flatColumnsRaw;
|
|
36
|
+
return flatColumnsRaw.map((col) => {
|
|
37
|
+
const override = pinnedColumns[col.columnId];
|
|
38
|
+
if (override && col.pinned !== override) {
|
|
39
|
+
return { ...col, pinned: override };
|
|
40
|
+
}
|
|
41
|
+
// If col was pinned by definition but not in overrides, keep original
|
|
42
|
+
return col;
|
|
43
|
+
});
|
|
44
|
+
}, [flatColumnsRaw, pinnedColumns]);
|
|
32
45
|
const visibleCols = useMemo(() => {
|
|
33
46
|
const filtered = visibleColumns
|
|
34
47
|
? flatColumns.filter((c) => visibleColumns.has(c.columnId))
|
|
@@ -159,13 +172,8 @@ export function useDataGridState(params) {
|
|
|
159
172
|
const minTableWidth = useMemo(() => {
|
|
160
173
|
const PADDING = 16;
|
|
161
174
|
const checkboxW = hasCheckboxCol ? 48 : 0;
|
|
162
|
-
return visibleCols.reduce((sum, c) =>
|
|
163
|
-
|
|
164
|
-
const override = columnSizingOverrides[c.columnId];
|
|
165
|
-
const w = override?.widthPx ?? c.idealWidth ?? c.defaultWidth ?? c.minWidth ?? 80;
|
|
166
|
-
return sum + w + PADDING;
|
|
167
|
-
}, checkboxW);
|
|
168
|
-
}, [visibleCols, hasCheckboxCol, columnSizingOverrides]);
|
|
175
|
+
return visibleCols.reduce((sum, c) => sum + (c.minWidth ?? 80) + PADDING, checkboxW);
|
|
176
|
+
}, [visibleCols, hasCheckboxCol]);
|
|
169
177
|
useEffect(() => {
|
|
170
178
|
const colIds = new Set(flatColumns.map((c) => c.columnId));
|
|
171
179
|
setColumnSizingOverrides((prev) => {
|
|
@@ -201,37 +209,25 @@ export function useDataGridState(params) {
|
|
|
201
209
|
const showEmptyInGrid = items.length === 0 && !!emptyState;
|
|
202
210
|
const hasCellSelection = selectionRange != null || activeCell != null;
|
|
203
211
|
// --- View-model inputs (shared across all 3 DataGridTables) ---
|
|
204
|
-
const { sortBy, sortDirection, onColumnSort,
|
|
212
|
+
const { sortBy, sortDirection, onColumnSort, filters, onFilterChange, filterOptions, loadingFilterOptions, peopleSearch, } = props;
|
|
205
213
|
const headerFilterInput = useMemo(() => ({
|
|
206
214
|
sortBy,
|
|
207
215
|
sortDirection,
|
|
208
216
|
onColumnSort,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
peopleFilters,
|
|
212
|
-
onPeopleFilterChange,
|
|
213
|
-
peopleSearch,
|
|
217
|
+
filters,
|
|
218
|
+
onFilterChange,
|
|
214
219
|
filterOptions,
|
|
215
220
|
loadingFilterOptions,
|
|
216
|
-
|
|
217
|
-
onMultiSelectFilterChange,
|
|
218
|
-
dateFilters,
|
|
219
|
-
onDateFilterChange,
|
|
221
|
+
peopleSearch,
|
|
220
222
|
}), [
|
|
221
223
|
sortBy,
|
|
222
224
|
sortDirection,
|
|
223
225
|
onColumnSort,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
peopleFilters,
|
|
227
|
-
onPeopleFilterChange,
|
|
228
|
-
peopleSearch,
|
|
226
|
+
filters,
|
|
227
|
+
onFilterChange,
|
|
229
228
|
filterOptions,
|
|
230
229
|
loadingFilterOptions,
|
|
231
|
-
|
|
232
|
-
onMultiSelectFilterChange,
|
|
233
|
-
dateFilters,
|
|
234
|
-
onDateFilterChange,
|
|
230
|
+
peopleSearch,
|
|
235
231
|
]);
|
|
236
232
|
const cellDescriptorInput = useMemo(() => ({
|
|
237
233
|
editingCell,
|
|
@@ -297,59 +293,71 @@ export function useDataGridState(params) {
|
|
|
297
293
|
setPendingEditorValue(undefined);
|
|
298
294
|
}, [setEditingCell, setPendingEditorValue]);
|
|
299
295
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
296
|
+
layout: {
|
|
297
|
+
flatColumns,
|
|
298
|
+
visibleCols,
|
|
299
|
+
visibleColumnCount,
|
|
300
|
+
totalColCount,
|
|
301
|
+
colOffset,
|
|
302
|
+
hasCheckboxCol,
|
|
303
|
+
rowIndexByRowId,
|
|
304
|
+
containerWidth,
|
|
305
|
+
minTableWidth,
|
|
306
|
+
desiredTableWidth,
|
|
307
|
+
columnSizingOverrides,
|
|
308
|
+
setColumnSizingOverrides,
|
|
309
|
+
onColumnResized,
|
|
310
|
+
},
|
|
311
|
+
rowSelection: {
|
|
312
|
+
selectedRowIds,
|
|
313
|
+
updateSelection,
|
|
314
|
+
handleRowCheckboxChange,
|
|
315
|
+
handleSelectAll,
|
|
316
|
+
allSelected,
|
|
317
|
+
someSelected,
|
|
318
|
+
},
|
|
319
|
+
editing: {
|
|
320
|
+
editingCell,
|
|
321
|
+
setEditingCell,
|
|
322
|
+
pendingEditorValue,
|
|
323
|
+
setPendingEditorValue,
|
|
324
|
+
commitCellEdit,
|
|
325
|
+
cancelPopoverEdit,
|
|
326
|
+
popoverAnchorEl,
|
|
327
|
+
setPopoverAnchorEl,
|
|
328
|
+
},
|
|
329
|
+
interaction: {
|
|
330
|
+
activeCell: cellSelection ? activeCell : null,
|
|
331
|
+
setActiveCell: cellSelection ? setActiveCell : NOOP,
|
|
332
|
+
selectionRange: cellSelection ? selectionRange : null,
|
|
333
|
+
setSelectionRange: cellSelection ? setSelectionRange : NOOP,
|
|
334
|
+
handleCellMouseDown: cellSelection ? handleCellMouseDown : NOOP_MOUSE,
|
|
335
|
+
handleSelectAllCells: cellSelection ? handleSelectAllCells : NOOP,
|
|
336
|
+
hasCellSelection: cellSelection ? hasCellSelection : false,
|
|
337
|
+
handleGridKeyDown: cellSelection ? handleGridKeyDown : NOOP_KEY,
|
|
338
|
+
handleFillHandleMouseDown: cellSelection ? handleFillHandleMouseDown : NOOP,
|
|
339
|
+
handleCopy: cellSelection ? handleCopy : NOOP,
|
|
340
|
+
handleCut: cellSelection ? handleCut : NOOP,
|
|
341
|
+
handlePaste: cellSelection ? handlePaste : NOOP_ASYNC,
|
|
342
|
+
cutRange: cellSelection ? cutRange : null,
|
|
343
|
+
copyRange: cellSelection ? copyRange : null,
|
|
344
|
+
clearClipboardRanges: cellSelection ? clearClipboardRanges : NOOP,
|
|
345
|
+
canUndo: undoRedo.canUndo,
|
|
346
|
+
canRedo: undoRedo.canRedo,
|
|
347
|
+
onUndo: undoRedo.undo,
|
|
348
|
+
onRedo: undoRedo.redo,
|
|
349
|
+
},
|
|
350
|
+
contextMenu: {
|
|
351
|
+
menuPosition: cellSelection ? contextMenu : null,
|
|
352
|
+
setMenuPosition: cellSelection ? setContextMenu : NOOP,
|
|
353
|
+
handleCellContextMenu: cellSelection ? handleCellContextMenu : NOOP_CTX,
|
|
354
|
+
closeContextMenu: cellSelection ? closeContextMenu : NOOP,
|
|
355
|
+
},
|
|
356
|
+
viewModels: {
|
|
357
|
+
headerFilterInput,
|
|
358
|
+
cellDescriptorInput,
|
|
359
|
+
statusBarConfig,
|
|
360
|
+
showEmptyInGrid,
|
|
361
|
+
},
|
|
354
362
|
};
|
|
355
363
|
}
|