@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.
@@ -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 = {}, gap = 8, className, title, toolbar, columnChooser, toolbarEnd: toolbarEndProp, children, pagination, sideBar, } = props;
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 (_jsxs(Container, { className: className, style: rootStyle, ...containerProps, children: [title != null && _jsx("div", { style: { margin: 0 }, children: title }), _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 }))] })] }));
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, multiSelectFilters, textFilters, onMultiSelectFilterChange, onTextFilterChange, dateFilters, onDateFilterChange, filterOptions, } = props;
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, multiSelectFilters: multiSelectFilters, textFilters: textFilters, onMultiSelectFilterChange: onMultiSelectFilterChange, onTextFilterChange: onTextFilterChange, dateFilters: dateFilters, onDateFilterChange: onDateFilterChange, filterOptions: filterOptions }))] })] })) : null;
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, multiSelectFilters, textFilters, onMultiSelectFilterChange, onTextFilterChange, dateFilters, onDateFilterChange, filterOptions } = props;
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: textFilters[filterKey] ?? '', onChange: (e) => onTextFilterChange(filterKey, e.target.value), 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: dateFilters[filterKey]?.from ?? '', onChange: (e) => {
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 to = dateFilters[filterKey]?.to;
82
- onDateFilterChange(filterKey, from || to ? { from, to } : undefined);
83
- }, "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: dateFilters[filterKey]?.to ?? '', onChange: (e) => {
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 from = dateFilters[filterKey]?.from;
86
- onDateFilterChange(filterKey, from || to ? { from, to } : undefined);
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 = (multiSelectFilters[filterKey] ?? []).includes(opt);
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 = multiSelectFilters[filterKey] ?? [];
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
- onMultiSelectFilterChange(filterKey, next);
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, useEffect } from 'react';
1
+ import { useCallback, useRef } from 'react';
2
2
  export function useColumnResize({ columnSizingOverrides, setColumnSizingOverrides, minWidth = 80, defaultWidth = 120, onColumnResized, }) {
3
- const resizingRef = useRef(null);
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 currentWidth = columnSizingOverrides[col.columnId]?.widthPx
8
- ?? col.idealWidth
9
- ?? col.defaultWidth
10
- ?? defaultWidth;
11
- resizingRef.current = {
12
- columnId: col.columnId,
13
- startX: e.clientX,
14
- startWidth: currentWidth,
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
- }, [columnSizingOverrides, defaultWidth]);
17
- const handleResizeMove = useCallback((e) => {
18
- if (!resizingRef.current)
19
- return;
20
- const { columnId, startX, startWidth } = resizingRef.current;
21
- const deltaX = e.clientX - startX;
22
- const newWidth = Math.max(minWidth, startWidth + deltaX);
23
- setColumnSizingOverrides((prev) => ({
24
- ...prev,
25
- [columnId]: { widthPx: newWidth },
26
- }));
27
- }, [setColumnSizingOverrides, minWidth]);
28
- const handleResizeEnd = useCallback(() => {
29
- if (resizingRef.current && onColumnResized) {
30
- const { columnId } = resizingRef.current;
31
- const width = columnSizingOverrides[columnId]?.widthPx;
32
- if (width != null) {
33
- onColumnResized(columnId, width);
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
- }, [handleResizeMove, handleResizeEnd]);
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 flatColumns = useMemo(() => flattenColumns(columns), [columns]);
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
- // Use the widest explicit width: resize override > idealWidth > defaultWidth > minWidth > 80
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, textFilters = {}, onTextFilterChange, peopleFilters = {}, onPeopleFilterChange, peopleSearch, filterOptions, loadingFilterOptions, multiSelectFilters, onMultiSelectFilterChange, dateFilters = {}, onDateFilterChange, } = props;
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
- textFilters,
210
- onTextFilterChange,
211
- peopleFilters,
212
- onPeopleFilterChange,
213
- peopleSearch,
217
+ filters,
218
+ onFilterChange,
214
219
  filterOptions,
215
220
  loadingFilterOptions,
216
- multiSelectFilters,
217
- onMultiSelectFilterChange,
218
- dateFilters,
219
- onDateFilterChange,
221
+ peopleSearch,
220
222
  }), [
221
223
  sortBy,
222
224
  sortDirection,
223
225
  onColumnSort,
224
- textFilters,
225
- onTextFilterChange,
226
- peopleFilters,
227
- onPeopleFilterChange,
228
- peopleSearch,
226
+ filters,
227
+ onFilterChange,
229
228
  filterOptions,
230
229
  loadingFilterOptions,
231
- multiSelectFilters,
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
- flatColumns,
301
- visibleCols,
302
- visibleColumnCount,
303
- totalColCount,
304
- colOffset,
305
- hasCheckboxCol,
306
- rowIndexByRowId,
307
- selectedRowIds,
308
- updateSelection,
309
- handleRowCheckboxChange,
310
- handleSelectAll,
311
- allSelected,
312
- someSelected,
313
- editingCell,
314
- setEditingCell,
315
- pendingEditorValue,
316
- setPendingEditorValue,
317
- activeCell: cellSelection ? activeCell : null,
318
- setActiveCell: cellSelection ? setActiveCell : NOOP,
319
- selectionRange: cellSelection ? selectionRange : null,
320
- setSelectionRange: cellSelection ? setSelectionRange : NOOP,
321
- handleCellMouseDown: cellSelection ? handleCellMouseDown : NOOP_MOUSE,
322
- handleSelectAllCells: cellSelection ? handleSelectAllCells : NOOP,
323
- contextMenu: cellSelection ? contextMenu : null,
324
- setContextMenu: cellSelection ? setContextMenu : NOOP,
325
- handleCellContextMenu: cellSelection ? handleCellContextMenu : NOOP_CTX,
326
- closeContextMenu: cellSelection ? closeContextMenu : NOOP,
327
- canUndo: undoRedo.canUndo,
328
- canRedo: undoRedo.canRedo,
329
- onUndo: undoRedo.undo,
330
- onRedo: undoRedo.redo,
331
- handleCopy: cellSelection ? handleCopy : NOOP,
332
- handleCut: cellSelection ? handleCut : NOOP,
333
- handlePaste: cellSelection ? handlePaste : NOOP_ASYNC,
334
- cutRange: cellSelection ? cutRange : null,
335
- copyRange: cellSelection ? copyRange : null,
336
- handleGridKeyDown: cellSelection ? handleGridKeyDown : NOOP_KEY,
337
- handleFillHandleMouseDown: cellSelection ? handleFillHandleMouseDown : NOOP,
338
- containerWidth,
339
- minTableWidth,
340
- desiredTableWidth,
341
- columnSizingOverrides,
342
- setColumnSizingOverrides,
343
- onColumnResized,
344
- headerFilterInput,
345
- cellDescriptorInput,
346
- commitCellEdit,
347
- cancelPopoverEdit,
348
- popoverAnchorEl,
349
- setPopoverAnchorEl,
350
- clearClipboardRanges: cellSelection ? clearClipboardRanges : NOOP,
351
- statusBarConfig,
352
- showEmptyInGrid,
353
- hasCellSelection: cellSelection ? hasCellSelection : false,
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
  }