@alaarab/ogrid-react 2.1.2 → 2.1.4
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/index.js +7233 -26
- package/package.json +7 -4
- package/dist/esm/components/BaseColumnHeaderMenu.js +0 -78
- package/dist/esm/components/BaseDropIndicator.js +0 -4
- package/dist/esm/components/BaseEmptyState.js +0 -4
- package/dist/esm/components/BaseInlineCellEditor.js +0 -167
- package/dist/esm/components/BaseLoadingOverlay.js +0 -4
- package/dist/esm/components/CellErrorBoundary.js +0 -43
- package/dist/esm/components/ColumnChooserProps.js +0 -6
- package/dist/esm/components/ColumnHeaderFilterContent.js +0 -33
- package/dist/esm/components/ColumnHeaderFilterRenderers.js +0 -67
- package/dist/esm/components/EmptyState.js +0 -19
- package/dist/esm/components/GridContextMenu.js +0 -35
- package/dist/esm/components/MarchingAntsOverlay.js +0 -90
- package/dist/esm/components/OGridLayout.js +0 -136
- package/dist/esm/components/PaginationControlsProps.js +0 -6
- package/dist/esm/components/SideBar.js +0 -123
- package/dist/esm/components/StatusBar.js +0 -6
- package/dist/esm/components/createOGrid.js +0 -19
- package/dist/esm/constants/domHelpers.js +0 -16
- package/dist/esm/hooks/index.js +0 -43
- package/dist/esm/hooks/useActiveCell.js +0 -75
- package/dist/esm/hooks/useCellEditing.js +0 -15
- package/dist/esm/hooks/useCellSelection.js +0 -389
- package/dist/esm/hooks/useClipboard.js +0 -106
- package/dist/esm/hooks/useColumnChooserState.js +0 -74
- package/dist/esm/hooks/useColumnHeaderFilterState.js +0 -191
- package/dist/esm/hooks/useColumnHeaderMenuState.js +0 -106
- package/dist/esm/hooks/useColumnMeta.js +0 -61
- package/dist/esm/hooks/useColumnPinning.js +0 -67
- package/dist/esm/hooks/useColumnReorder.js +0 -143
- package/dist/esm/hooks/useColumnResize.js +0 -127
- package/dist/esm/hooks/useContextMenu.js +0 -21
- package/dist/esm/hooks/useDataGridContextMenu.js +0 -24
- package/dist/esm/hooks/useDataGridEditing.js +0 -56
- package/dist/esm/hooks/useDataGridInteraction.js +0 -109
- package/dist/esm/hooks/useDataGridLayout.js +0 -172
- package/dist/esm/hooks/useDataGridState.js +0 -169
- package/dist/esm/hooks/useDataGridTableOrchestration.js +0 -199
- package/dist/esm/hooks/useDateFilterState.js +0 -34
- package/dist/esm/hooks/useDebounce.js +0 -35
- package/dist/esm/hooks/useFillHandle.js +0 -200
- package/dist/esm/hooks/useFilterOptions.js +0 -55
- package/dist/esm/hooks/useInlineCellEditorState.js +0 -38
- package/dist/esm/hooks/useKeyboardNavigation.js +0 -261
- package/dist/esm/hooks/useLatestRef.js +0 -11
- package/dist/esm/hooks/useListVirtualizer.js +0 -29
- package/dist/esm/hooks/useMultiSelectFilterState.js +0 -59
- package/dist/esm/hooks/useOGrid.js +0 -355
- package/dist/esm/hooks/useOGridDataFetching.js +0 -74
- package/dist/esm/hooks/useOGridFilters.js +0 -59
- package/dist/esm/hooks/useOGridPagination.js +0 -24
- package/dist/esm/hooks/useOGridSorting.js +0 -24
- package/dist/esm/hooks/usePaginationControls.js +0 -16
- package/dist/esm/hooks/usePeopleFilterState.js +0 -73
- package/dist/esm/hooks/useRichSelectState.js +0 -60
- package/dist/esm/hooks/useRowSelection.js +0 -69
- package/dist/esm/hooks/useSelectState.js +0 -62
- package/dist/esm/hooks/useShallowEqualMemo.js +0 -14
- package/dist/esm/hooks/useSideBarState.js +0 -39
- package/dist/esm/hooks/useTableLayout.js +0 -69
- package/dist/esm/hooks/useTextFilterState.js +0 -25
- package/dist/esm/hooks/useUndoRedo.js +0 -84
- package/dist/esm/hooks/useVirtualScroll.js +0 -69
- package/dist/esm/storybook/index.js +0 -1
- package/dist/esm/storybook/mockData.js +0 -73
- package/dist/esm/types/columnTypes.js +0 -1
- package/dist/esm/types/dataGridTypes.js +0 -1
- package/dist/esm/types/index.js +0 -1
- package/dist/esm/utils/dataGridViewModel.js +0 -54
- package/dist/esm/utils/gridRowComparator.js +0 -2
- package/dist/esm/utils/index.js +0 -5
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
-
import { useLatestRef } from './useLatestRef';
|
|
3
|
-
/**
|
|
4
|
-
* Manages column resize drag interactions with RAF-throttled state updates.
|
|
5
|
-
* @param params - Sizing overrides, setter, min/default widths, and resize callback.
|
|
6
|
-
* @returns Resize start handler and column width getter.
|
|
7
|
-
*/
|
|
8
|
-
export function useColumnResize({ columnSizingOverrides, setColumnSizingOverrides, minWidth = 80, defaultWidth = 120, onColumnResized, }) {
|
|
9
|
-
const rafRef = useRef(0);
|
|
10
|
-
const onColumnResizedRef = useRef(onColumnResized);
|
|
11
|
-
onColumnResizedRef.current = onColumnResized;
|
|
12
|
-
const columnSizingOverridesRef = useLatestRef(columnSizingOverrides);
|
|
13
|
-
// Track active drag listeners so we can clean up on unmount
|
|
14
|
-
const cleanupRef = useRef(null);
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
return () => {
|
|
17
|
-
if (cleanupRef.current) {
|
|
18
|
-
cleanupRef.current();
|
|
19
|
-
cleanupRef.current = null;
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
}, []);
|
|
23
|
-
const handleResizeStart = useCallback((e, col) => {
|
|
24
|
-
e.preventDefault();
|
|
25
|
-
e.stopPropagation();
|
|
26
|
-
// Clean up any in-progress drag before starting a new one
|
|
27
|
-
if (cleanupRef.current) {
|
|
28
|
-
cleanupRef.current();
|
|
29
|
-
cleanupRef.current = null;
|
|
30
|
-
}
|
|
31
|
-
const startX = e.clientX;
|
|
32
|
-
const columnId = col.columnId;
|
|
33
|
-
// Measure the actual rendered width from the DOM. With table-layout: auto,
|
|
34
|
-
// the browser may have auto-sized the column wider than the config values.
|
|
35
|
-
// The resize handle is a direct child of <th>, so parentElement is the header cell.
|
|
36
|
-
// Use closest('th') instead of parentElement to handle frameworks (e.g. Fluent UI)
|
|
37
|
-
// that wrap header cell children in an internal <button> element.
|
|
38
|
-
const thEl = e.currentTarget.closest('th');
|
|
39
|
-
const startWidth = thEl
|
|
40
|
-
? thEl.getBoundingClientRect().width
|
|
41
|
-
: columnSizingOverridesRef.current[columnId]?.widthPx
|
|
42
|
-
?? col.idealWidth
|
|
43
|
-
?? col.defaultWidth
|
|
44
|
-
?? defaultWidth;
|
|
45
|
-
let latestWidth = startWidth;
|
|
46
|
-
// Lock all column widths to their current DOM widths on first resize.
|
|
47
|
-
// With table-layout:auto, resizing one column causes the browser to compress others.
|
|
48
|
-
// Snapshotting all widths prevents this — only the dragged column changes.
|
|
49
|
-
const thead = thEl?.closest('thead');
|
|
50
|
-
if (thead) {
|
|
51
|
-
const allThs = thead.querySelectorAll('th[data-column-id]');
|
|
52
|
-
if (allThs.length > 0) {
|
|
53
|
-
setColumnSizingOverrides((prev) => {
|
|
54
|
-
const next = { ...prev };
|
|
55
|
-
allThs.forEach((th) => {
|
|
56
|
-
const colId = th.dataset.columnId;
|
|
57
|
-
if (colId && !next[colId]) {
|
|
58
|
-
next[colId] = { widthPx: th.getBoundingClientRect().width };
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
next[columnId] = { widthPx: startWidth };
|
|
62
|
-
return next;
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// Lock cursor and prevent text selection during drag
|
|
67
|
-
const prevCursor = document.body.style.cursor;
|
|
68
|
-
const prevUserSelect = document.body.style.userSelect;
|
|
69
|
-
document.body.style.cursor = 'col-resize';
|
|
70
|
-
document.body.style.userSelect = 'none';
|
|
71
|
-
const flushWidth = () => {
|
|
72
|
-
setColumnSizingOverrides((prev) => ({
|
|
73
|
-
...prev,
|
|
74
|
-
[columnId]: { widthPx: latestWidth },
|
|
75
|
-
}));
|
|
76
|
-
};
|
|
77
|
-
const onMove = (moveEvent) => {
|
|
78
|
-
const deltaX = moveEvent.clientX - startX;
|
|
79
|
-
latestWidth = Math.max(minWidth, startWidth + deltaX);
|
|
80
|
-
if (!rafRef.current) {
|
|
81
|
-
rafRef.current = requestAnimationFrame(() => {
|
|
82
|
-
rafRef.current = 0;
|
|
83
|
-
flushWidth();
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
const cleanup = () => {
|
|
88
|
-
document.removeEventListener('mousemove', onMove);
|
|
89
|
-
document.removeEventListener('mouseup', onUp);
|
|
90
|
-
cleanupRef.current = null;
|
|
91
|
-
// Restore cursor and user-select
|
|
92
|
-
document.body.style.cursor = prevCursor;
|
|
93
|
-
document.body.style.userSelect = prevUserSelect;
|
|
94
|
-
// Cancel pending RAF and flush final width synchronously
|
|
95
|
-
if (rafRef.current) {
|
|
96
|
-
cancelAnimationFrame(rafRef.current);
|
|
97
|
-
rafRef.current = 0;
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
const onUp = () => {
|
|
101
|
-
cleanup();
|
|
102
|
-
flushWidth();
|
|
103
|
-
// Remove any rogue :focus-visible outlines that appeared during the drag.
|
|
104
|
-
// Re-focus the grid wrapper so keyboard navigation still works.
|
|
105
|
-
const wrapper = thEl?.closest('[tabindex]');
|
|
106
|
-
if (wrapper) {
|
|
107
|
-
wrapper.focus({ preventScroll: true });
|
|
108
|
-
}
|
|
109
|
-
else if (document.activeElement instanceof HTMLElement) {
|
|
110
|
-
document.activeElement.blur();
|
|
111
|
-
}
|
|
112
|
-
if (onColumnResizedRef.current) {
|
|
113
|
-
onColumnResizedRef.current(columnId, latestWidth);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
document.addEventListener('mousemove', onMove);
|
|
117
|
-
document.addEventListener('mouseup', onUp);
|
|
118
|
-
cleanupRef.current = cleanup;
|
|
119
|
-
}, [defaultWidth, minWidth, setColumnSizingOverrides, columnSizingOverridesRef]);
|
|
120
|
-
const getColumnWidth = useCallback((col) => {
|
|
121
|
-
return columnSizingOverrides[col.columnId]?.widthPx
|
|
122
|
-
?? col.idealWidth
|
|
123
|
-
?? col.defaultWidth
|
|
124
|
-
?? defaultWidth;
|
|
125
|
-
}, [columnSizingOverrides, defaultWidth]);
|
|
126
|
-
return { handleResizeStart, getColumnWidth };
|
|
127
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Manages context menu position state for right-click menus.
|
|
4
|
-
* @returns Menu position, setter, right-click handler, and close handler.
|
|
5
|
-
*/
|
|
6
|
-
export function useContextMenu() {
|
|
7
|
-
const [contextMenuPosition, setContextMenuPosition] = useState(null);
|
|
8
|
-
const handleCellContextMenu = useCallback((e) => {
|
|
9
|
-
e.preventDefault?.();
|
|
10
|
-
setContextMenuPosition({ x: e.clientX, y: e.clientY });
|
|
11
|
-
}, []);
|
|
12
|
-
const closeContextMenu = useCallback(() => {
|
|
13
|
-
setContextMenuPosition(null);
|
|
14
|
-
}, []);
|
|
15
|
-
return {
|
|
16
|
-
contextMenuPosition,
|
|
17
|
-
setContextMenuPosition,
|
|
18
|
-
handleCellContextMenu,
|
|
19
|
-
closeContextMenu,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
2
|
-
import { useContextMenu } from './useContextMenu';
|
|
3
|
-
// Stable no-op handlers used when cellSelection is disabled
|
|
4
|
-
const NOOP = () => { };
|
|
5
|
-
const NOOP_CTX = (_e) => { };
|
|
6
|
-
/**
|
|
7
|
-
* Manages context menu position and handlers.
|
|
8
|
-
* Extracted from useDataGridState for modularity.
|
|
9
|
-
*/
|
|
10
|
-
export function useDataGridContextMenu(params) {
|
|
11
|
-
const { cellSelection } = params;
|
|
12
|
-
const { contextMenuPosition, setContextMenuPosition, handleCellContextMenu, closeContextMenu } = useContextMenu();
|
|
13
|
-
const contextMenuState = useMemo(() => ({
|
|
14
|
-
menuPosition: cellSelection ? contextMenuPosition : null,
|
|
15
|
-
setMenuPosition: cellSelection ? setContextMenuPosition : NOOP,
|
|
16
|
-
handleCellContextMenu: cellSelection ? handleCellContextMenu : NOOP_CTX,
|
|
17
|
-
closeContextMenu: cellSelection ? closeContextMenu : NOOP,
|
|
18
|
-
}), [cellSelection, contextMenuPosition, setContextMenuPosition, handleCellContextMenu, closeContextMenu]);
|
|
19
|
-
return {
|
|
20
|
-
contextMenu: contextMenuState,
|
|
21
|
-
contextMenuPosition,
|
|
22
|
-
setContextMenuPosition,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { useMemo, useCallback, useState } from 'react';
|
|
2
|
-
import { parseValue } from '../utils';
|
|
3
|
-
import { useLatestRef } from './useLatestRef';
|
|
4
|
-
/**
|
|
5
|
-
* Manages cell editing commit/cancel logic and popover editor state.
|
|
6
|
-
* Extracted from useDataGridState for modularity.
|
|
7
|
-
*
|
|
8
|
-
* The editingCell/setEditingCell/pendingEditorValue/setPendingEditorValue are
|
|
9
|
-
* passed in from useCellEditing() (called at the orchestrator level) to avoid
|
|
10
|
-
* circular dependencies with useDataGridInteraction.
|
|
11
|
-
*/
|
|
12
|
-
export function useDataGridEditing(params) {
|
|
13
|
-
const { editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, onCellValueChanged, setActiveCell, } = params;
|
|
14
|
-
const [popoverAnchorEl, setPopoverAnchorEl] = useState(null);
|
|
15
|
-
const visibleColsRef = useLatestRef(params.visibleCols);
|
|
16
|
-
const itemsLengthRef = useLatestRef(params.itemsLength);
|
|
17
|
-
const commitCellEdit = useCallback((item, columnId, oldValue, newValue, rowIndex, globalColIndex) => {
|
|
18
|
-
// Validate via valueParser before committing
|
|
19
|
-
const col = visibleColsRef.current.find((c) => c.columnId === columnId);
|
|
20
|
-
if (col) {
|
|
21
|
-
const result = parseValue(newValue, oldValue, item, col);
|
|
22
|
-
if (!result.valid) {
|
|
23
|
-
// Reject -- cancel the edit
|
|
24
|
-
setEditingCell(null);
|
|
25
|
-
setPopoverAnchorEl(null);
|
|
26
|
-
setPendingEditorValue(undefined);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
newValue = result.value;
|
|
30
|
-
}
|
|
31
|
-
onCellValueChanged?.({
|
|
32
|
-
item,
|
|
33
|
-
columnId,
|
|
34
|
-
oldValue,
|
|
35
|
-
newValue,
|
|
36
|
-
rowIndex,
|
|
37
|
-
});
|
|
38
|
-
setEditingCell(null);
|
|
39
|
-
setPopoverAnchorEl(null);
|
|
40
|
-
setPendingEditorValue(undefined);
|
|
41
|
-
// Advance to next row for inline editors
|
|
42
|
-
if (rowIndex < itemsLengthRef.current - 1) {
|
|
43
|
-
setActiveCell({ rowIndex: rowIndex + 1, columnIndex: globalColIndex });
|
|
44
|
-
}
|
|
45
|
-
}, [onCellValueChanged, setEditingCell, setPendingEditorValue, setActiveCell, visibleColsRef, itemsLengthRef]);
|
|
46
|
-
const cancelPopoverEdit = useCallback(() => {
|
|
47
|
-
setEditingCell(null);
|
|
48
|
-
setPopoverAnchorEl(null);
|
|
49
|
-
setPendingEditorValue(undefined);
|
|
50
|
-
}, [setEditingCell, setPendingEditorValue]);
|
|
51
|
-
const editingState = useMemo(() => ({
|
|
52
|
-
editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue,
|
|
53
|
-
commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl,
|
|
54
|
-
}), [editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, commitCellEdit, cancelPopoverEdit, popoverAnchorEl, setPopoverAnchorEl]);
|
|
55
|
-
return { editing: editingState };
|
|
56
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { useMemo, useCallback } from 'react';
|
|
2
|
-
import { useCellSelection } from './useCellSelection';
|
|
3
|
-
import { useClipboard } from './useClipboard';
|
|
4
|
-
import { useKeyboardNavigation } from './useKeyboardNavigation';
|
|
5
|
-
import { useFillHandle } from './useFillHandle';
|
|
6
|
-
import { useUndoRedo } from './useUndoRedo';
|
|
7
|
-
// Stable no-op handlers used when cellSelection is disabled (module-scope = no re-renders)
|
|
8
|
-
const NOOP = () => { };
|
|
9
|
-
const NOOP_ASYNC = async () => { };
|
|
10
|
-
const NOOP_MOUSE = (_e, _r, _c) => { };
|
|
11
|
-
const NOOP_KEY = (_e) => { };
|
|
12
|
-
/**
|
|
13
|
-
* Manages cell selection, keyboard navigation, clipboard, fill handle, and undo/redo.
|
|
14
|
-
* Extracted from useDataGridState for modularity.
|
|
15
|
-
*
|
|
16
|
-
* activeCell/setActiveCell and editingCell/setEditingCell are passed in from the
|
|
17
|
-
* orchestrator level to avoid circular dependencies with useDataGridEditing.
|
|
18
|
-
*/
|
|
19
|
-
export function useDataGridInteraction(params) {
|
|
20
|
-
const { items, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, getRowId, editable, onCellValueChangedProp, cellSelection, rowSelection, selectedRowIds, editingCell, setEditingCell, activeCell, setActiveCell, handleRowCheckboxChange, setContextMenuPosition, wrapperRef, } = params;
|
|
21
|
-
// Wrap onCellValueChanged with undo/redo tracking
|
|
22
|
-
const undoRedo = useUndoRedo({ onCellValueChanged: onCellValueChangedProp });
|
|
23
|
-
const onCellValueChanged = undoRedo.onCellValueChanged;
|
|
24
|
-
const { selectionRange, setSelectionRange, handleCellMouseDown: handleCellMouseDownBase, handleSelectAllCells, isDragging, } = useCellSelection({
|
|
25
|
-
colOffset,
|
|
26
|
-
rowCount: items.length,
|
|
27
|
-
visibleColCount: visibleCols.length,
|
|
28
|
-
setActiveCell,
|
|
29
|
-
wrapperRef,
|
|
30
|
-
});
|
|
31
|
-
const { handleCopy, handleCut, handlePaste, cutRange, copyRange, clearClipboardRanges } = useClipboard({
|
|
32
|
-
items,
|
|
33
|
-
visibleCols,
|
|
34
|
-
colOffset,
|
|
35
|
-
selectionRange,
|
|
36
|
-
activeCell,
|
|
37
|
-
editable,
|
|
38
|
-
onCellValueChanged,
|
|
39
|
-
beginBatch: undoRedo.beginBatch,
|
|
40
|
-
endBatch: undoRedo.endBatch,
|
|
41
|
-
});
|
|
42
|
-
const handleCellMouseDown = useCallback((e, rowIndex, globalColIndex) => {
|
|
43
|
-
if (e.button !== 0)
|
|
44
|
-
return;
|
|
45
|
-
wrapperRef.current?.focus({ preventScroll: true });
|
|
46
|
-
clearClipboardRanges();
|
|
47
|
-
handleCellMouseDownBase(e, rowIndex, globalColIndex);
|
|
48
|
-
}, [handleCellMouseDownBase, clearClipboardRanges, wrapperRef]);
|
|
49
|
-
const { handleGridKeyDown } = useKeyboardNavigation({
|
|
50
|
-
data: { items, visibleCols, colOffset, hasCheckboxCol, visibleColumnCount, getRowId },
|
|
51
|
-
state: { activeCell, selectionRange, editingCell, selectedRowIds },
|
|
52
|
-
handlers: { setActiveCell, setSelectionRange, setEditingCell, handleRowCheckboxChange, handleCopy, handleCut, handlePaste, setContextMenu: setContextMenuPosition, onUndo: undoRedo.undo, onRedo: undoRedo.redo, clearClipboardRanges },
|
|
53
|
-
features: { editable, onCellValueChanged, rowSelection: rowSelection ?? 'none', wrapperRef },
|
|
54
|
-
});
|
|
55
|
-
const { handleFillHandleMouseDown } = useFillHandle({
|
|
56
|
-
items,
|
|
57
|
-
visibleCols,
|
|
58
|
-
editable,
|
|
59
|
-
onCellValueChanged,
|
|
60
|
-
selectionRange,
|
|
61
|
-
setSelectionRange,
|
|
62
|
-
setActiveCell,
|
|
63
|
-
colOffset,
|
|
64
|
-
wrapperRef,
|
|
65
|
-
beginBatch: undoRedo.beginBatch,
|
|
66
|
-
endBatch: undoRedo.endBatch,
|
|
67
|
-
});
|
|
68
|
-
const hasCellSelection = selectionRange != null || activeCell != null;
|
|
69
|
-
const interactionState = useMemo(() => ({
|
|
70
|
-
activeCell: cellSelection ? activeCell : null,
|
|
71
|
-
setActiveCell: cellSelection ? setActiveCell : NOOP,
|
|
72
|
-
selectionRange: cellSelection ? selectionRange : null,
|
|
73
|
-
setSelectionRange: cellSelection ? setSelectionRange : NOOP,
|
|
74
|
-
handleCellMouseDown: cellSelection ? handleCellMouseDown : NOOP_MOUSE,
|
|
75
|
-
handleSelectAllCells: cellSelection ? handleSelectAllCells : NOOP,
|
|
76
|
-
hasCellSelection: cellSelection ? hasCellSelection : false,
|
|
77
|
-
handleGridKeyDown: cellSelection ? handleGridKeyDown : NOOP_KEY,
|
|
78
|
-
handleFillHandleMouseDown: cellSelection ? handleFillHandleMouseDown : NOOP,
|
|
79
|
-
handleCopy: cellSelection ? handleCopy : NOOP,
|
|
80
|
-
handleCut: cellSelection ? handleCut : NOOP,
|
|
81
|
-
handlePaste: cellSelection ? handlePaste : NOOP_ASYNC,
|
|
82
|
-
cutRange: cellSelection ? cutRange : null,
|
|
83
|
-
copyRange: cellSelection ? copyRange : null,
|
|
84
|
-
clearClipboardRanges: cellSelection ? clearClipboardRanges : NOOP,
|
|
85
|
-
canUndo: undoRedo.canUndo,
|
|
86
|
-
canRedo: undoRedo.canRedo,
|
|
87
|
-
onUndo: undoRedo.undo,
|
|
88
|
-
onRedo: undoRedo.redo,
|
|
89
|
-
isDragging: cellSelection ? isDragging : false,
|
|
90
|
-
}), [
|
|
91
|
-
cellSelection, activeCell, setActiveCell, selectionRange, setSelectionRange,
|
|
92
|
-
handleCellMouseDown, handleSelectAllCells, hasCellSelection, handleGridKeyDown,
|
|
93
|
-
handleFillHandleMouseDown, handleCopy, handleCut, handlePaste, cutRange, copyRange,
|
|
94
|
-
clearClipboardRanges, undoRedo.canUndo, undoRedo.canRedo, undoRedo.undo, undoRedo.redo,
|
|
95
|
-
isDragging,
|
|
96
|
-
]);
|
|
97
|
-
return {
|
|
98
|
-
interaction: interactionState,
|
|
99
|
-
selectionRange,
|
|
100
|
-
setSelectionRange,
|
|
101
|
-
cutRange,
|
|
102
|
-
copyRange,
|
|
103
|
-
clearClipboardRanges,
|
|
104
|
-
isDragging,
|
|
105
|
-
onCellValueChanged,
|
|
106
|
-
canUndo: undoRedo.canUndo,
|
|
107
|
-
canRedo: undoRedo.canRedo,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { useMemo, useState, useLayoutEffect, useCallback } from 'react';
|
|
2
|
-
import { flattenColumns } from '../utils';
|
|
3
|
-
import { CHECKBOX_COLUMN_WIDTH, DEFAULT_MIN_COLUMN_WIDTH } from '@alaarab/ogrid-core';
|
|
4
|
-
import { useTableLayout } from './useTableLayout';
|
|
5
|
-
import { useColumnPinning } from './useColumnPinning';
|
|
6
|
-
import { useColumnHeaderMenuState } from './useColumnHeaderMenuState';
|
|
7
|
-
import { useLatestRef } from './useLatestRef';
|
|
8
|
-
/**
|
|
9
|
-
* Manages column layout, visibility, sizing, pinning, and header menu state.
|
|
10
|
-
* Extracted from useDataGridState for modularity.
|
|
11
|
-
*/
|
|
12
|
-
export function useDataGridLayout(params) {
|
|
13
|
-
const { columns, items, getRowId, visibleColumns, columnOrder, rowSelection = 'none', showRowNumbers, initialColumnWidths, onColumnResized, onAutosizeColumn, pinnedColumns, onColumnPinned, sortBy, sortDirection, onColumnSort, wrapperRef, } = params;
|
|
14
|
-
// Cast is safe: input columns are React.IColumnDef instances; flattenColumns only extracts leaves.
|
|
15
|
-
const flatColumnsRaw = useMemo(() => flattenColumns(columns), [columns]);
|
|
16
|
-
// Apply runtime pin overrides (from applyColumnState or programmatic changes)
|
|
17
|
-
const flatColumns = useMemo(() => {
|
|
18
|
-
if (!pinnedColumns || Object.keys(pinnedColumns).length === 0)
|
|
19
|
-
return flatColumnsRaw;
|
|
20
|
-
return flatColumnsRaw.map((col) => {
|
|
21
|
-
const override = pinnedColumns[col.columnId];
|
|
22
|
-
if (override && col.pinned !== override) {
|
|
23
|
-
return { ...col, pinned: override };
|
|
24
|
-
}
|
|
25
|
-
return col;
|
|
26
|
-
});
|
|
27
|
-
}, [flatColumnsRaw, pinnedColumns]);
|
|
28
|
-
const visibleCols = useMemo(() => {
|
|
29
|
-
const filtered = visibleColumns
|
|
30
|
-
? flatColumns.filter((c) => visibleColumns.has(c.columnId))
|
|
31
|
-
: flatColumns;
|
|
32
|
-
if (!columnOrder?.length)
|
|
33
|
-
return filtered;
|
|
34
|
-
const orderMap = new Map();
|
|
35
|
-
for (let i = 0; i < columnOrder.length; i++) {
|
|
36
|
-
orderMap.set(columnOrder[i], i);
|
|
37
|
-
}
|
|
38
|
-
return [...filtered].sort((a, b) => {
|
|
39
|
-
const ia = orderMap.get(a.columnId) ?? -1;
|
|
40
|
-
const ib = orderMap.get(b.columnId) ?? -1;
|
|
41
|
-
if (ia === -1 && ib === -1)
|
|
42
|
-
return 0;
|
|
43
|
-
if (ia === -1)
|
|
44
|
-
return 1;
|
|
45
|
-
if (ib === -1)
|
|
46
|
-
return -1;
|
|
47
|
-
return ia - ib;
|
|
48
|
-
});
|
|
49
|
-
}, [flatColumns, visibleColumns, columnOrder]);
|
|
50
|
-
const visibleColumnCount = visibleCols.length;
|
|
51
|
-
const hasCheckboxCol = rowSelection === 'multiple';
|
|
52
|
-
const hasRowNumbersCol = !!showRowNumbers;
|
|
53
|
-
const specialColsCount = (hasCheckboxCol ? 1 : 0) + (hasRowNumbersCol ? 1 : 0);
|
|
54
|
-
const totalColCount = visibleColumnCount + specialColsCount;
|
|
55
|
-
const colOffset = specialColsCount;
|
|
56
|
-
const rowIndexByRowId = useMemo(() => {
|
|
57
|
-
const m = new Map();
|
|
58
|
-
items.forEach((item, idx) => m.set(getRowId(item), idx));
|
|
59
|
-
return m;
|
|
60
|
-
}, [items, getRowId]);
|
|
61
|
-
const { containerWidth, minTableWidth, desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, } = useTableLayout({
|
|
62
|
-
wrapperRef,
|
|
63
|
-
visibleCols,
|
|
64
|
-
flatColumns,
|
|
65
|
-
hasCheckboxCol,
|
|
66
|
-
initialColumnWidths,
|
|
67
|
-
onColumnResized,
|
|
68
|
-
});
|
|
69
|
-
const pinningResult = useColumnPinning({
|
|
70
|
-
columns: flatColumns,
|
|
71
|
-
pinnedColumns,
|
|
72
|
-
onColumnPinned,
|
|
73
|
-
});
|
|
74
|
-
// Measure actual column widths from the DOM for accurate pinning offsets.
|
|
75
|
-
const [measuredColumnWidths, setMeasuredColumnWidths] = useState({});
|
|
76
|
-
useLayoutEffect(() => {
|
|
77
|
-
const wrapper = wrapperRef.current;
|
|
78
|
-
if (!wrapper)
|
|
79
|
-
return;
|
|
80
|
-
const headerCells = wrapper.querySelectorAll('th[data-column-id]');
|
|
81
|
-
if (headerCells.length === 0)
|
|
82
|
-
return;
|
|
83
|
-
const measured = {};
|
|
84
|
-
headerCells.forEach((cell) => {
|
|
85
|
-
const colId = cell.getAttribute('data-column-id');
|
|
86
|
-
if (colId)
|
|
87
|
-
measured[colId] = cell.offsetWidth;
|
|
88
|
-
});
|
|
89
|
-
setMeasuredColumnWidths((prev) => {
|
|
90
|
-
for (const key in measured) {
|
|
91
|
-
if (prev[key] !== measured[key])
|
|
92
|
-
return measured;
|
|
93
|
-
}
|
|
94
|
-
if (Object.keys(prev).length !== Object.keys(measured).length)
|
|
95
|
-
return measured;
|
|
96
|
-
return prev;
|
|
97
|
-
});
|
|
98
|
-
}, [visibleCols, containerWidth, columnSizingOverrides, wrapperRef]);
|
|
99
|
-
// Build column width map for pinning offset computation
|
|
100
|
-
const columnWidthMap = useMemo(() => {
|
|
101
|
-
const map = {};
|
|
102
|
-
for (const col of visibleCols) {
|
|
103
|
-
const override = columnSizingOverrides[col.columnId];
|
|
104
|
-
map[col.columnId] = override
|
|
105
|
-
? override.widthPx
|
|
106
|
-
: (measuredColumnWidths[col.columnId] ?? col.idealWidth ?? col.defaultWidth ?? col.minWidth ?? DEFAULT_MIN_COLUMN_WIDTH);
|
|
107
|
-
}
|
|
108
|
-
return map;
|
|
109
|
-
}, [visibleCols, columnSizingOverrides, measuredColumnWidths]);
|
|
110
|
-
const leftOffsets = useMemo(() => pinningResult.computeLeftOffsets(visibleCols, columnWidthMap, DEFAULT_MIN_COLUMN_WIDTH, hasCheckboxCol, CHECKBOX_COLUMN_WIDTH), [pinningResult, visibleCols, columnWidthMap, hasCheckboxCol]);
|
|
111
|
-
const rightOffsets = useMemo(() => pinningResult.computeRightOffsets(visibleCols, columnWidthMap, DEFAULT_MIN_COLUMN_WIDTH), [pinningResult, visibleCols, columnWidthMap]);
|
|
112
|
-
// Stabilize onColumnSort via ref
|
|
113
|
-
const onColumnSortRef = useLatestRef(onColumnSort);
|
|
114
|
-
const stableOnColumnSort = useCallback((columnKey, direction) => onColumnSortRef.current?.(columnKey, direction), [onColumnSortRef]);
|
|
115
|
-
// Autosize callback
|
|
116
|
-
const handleAutosizeColumn = useCallback((columnId, width) => {
|
|
117
|
-
setColumnSizingOverrides((prev) => ({ ...prev, [columnId]: { widthPx: width } }));
|
|
118
|
-
(onAutosizeColumn ?? onColumnResized)?.(columnId, width);
|
|
119
|
-
}, [setColumnSizingOverrides, onAutosizeColumn, onColumnResized]);
|
|
120
|
-
const headerMenuResult = useColumnHeaderMenuState({
|
|
121
|
-
pinnedColumns: pinningResult.pinnedColumns,
|
|
122
|
-
onPinColumn: pinningResult.pinColumn,
|
|
123
|
-
onUnpinColumn: pinningResult.unpinColumn,
|
|
124
|
-
sortBy,
|
|
125
|
-
sortDirection: sortDirection ?? 'asc',
|
|
126
|
-
onColumnSort: stableOnColumnSort,
|
|
127
|
-
onColumnResized,
|
|
128
|
-
onAutosizeColumn: handleAutosizeColumn,
|
|
129
|
-
columns: flatColumns,
|
|
130
|
-
});
|
|
131
|
-
// Memoize layout sub-object
|
|
132
|
-
const layoutState = useMemo(() => ({
|
|
133
|
-
flatColumns, visibleCols, visibleColumnCount, totalColCount, colOffset,
|
|
134
|
-
hasCheckboxCol, hasRowNumbersCol, rowIndexByRowId, containerWidth, minTableWidth,
|
|
135
|
-
desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, onColumnResized,
|
|
136
|
-
measuredColumnWidths,
|
|
137
|
-
}), [
|
|
138
|
-
flatColumns, visibleCols, visibleColumnCount, totalColCount, colOffset,
|
|
139
|
-
hasCheckboxCol, hasRowNumbersCol, rowIndexByRowId, containerWidth, minTableWidth,
|
|
140
|
-
desiredTableWidth, columnSizingOverrides, setColumnSizingOverrides, onColumnResized,
|
|
141
|
-
measuredColumnWidths,
|
|
142
|
-
]);
|
|
143
|
-
// Memoize pinning sub-object
|
|
144
|
-
const pinningState = useMemo(() => ({
|
|
145
|
-
pinnedColumns: pinningResult.pinnedColumns,
|
|
146
|
-
pinColumn: pinningResult.pinColumn,
|
|
147
|
-
unpinColumn: pinningResult.unpinColumn,
|
|
148
|
-
isPinned: pinningResult.isPinned,
|
|
149
|
-
leftOffsets,
|
|
150
|
-
rightOffsets,
|
|
151
|
-
headerMenu: headerMenuResult,
|
|
152
|
-
}), [
|
|
153
|
-
pinningResult.pinnedColumns, pinningResult.pinColumn, pinningResult.unpinColumn,
|
|
154
|
-
pinningResult.isPinned, leftOffsets, rightOffsets,
|
|
155
|
-
headerMenuResult,
|
|
156
|
-
]);
|
|
157
|
-
return {
|
|
158
|
-
layout: layoutState,
|
|
159
|
-
pinning: pinningState,
|
|
160
|
-
flatColumns,
|
|
161
|
-
visibleCols,
|
|
162
|
-
visibleColumnCount,
|
|
163
|
-
totalColCount,
|
|
164
|
-
colOffset,
|
|
165
|
-
hasCheckboxCol,
|
|
166
|
-
hasRowNumbersCol,
|
|
167
|
-
columnSizingOverrides,
|
|
168
|
-
setColumnSizingOverrides,
|
|
169
|
-
handleAutosizeColumn,
|
|
170
|
-
stableOnColumnSort,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { useMemo, useCallback } from 'react';
|
|
2
|
-
import { getDataGridStatusBarConfig, computeAggregations } from '../utils';
|
|
3
|
-
import { useRowSelection } from './useRowSelection';
|
|
4
|
-
import { useCellEditing } from './useCellEditing';
|
|
5
|
-
import { useActiveCell } from './useActiveCell';
|
|
6
|
-
import { useLatestRef } from './useLatestRef';
|
|
7
|
-
import { useDataGridLayout } from './useDataGridLayout';
|
|
8
|
-
import { useDataGridEditing } from './useDataGridEditing';
|
|
9
|
-
import { useDataGridInteraction } from './useDataGridInteraction';
|
|
10
|
-
import { useDataGridContextMenu } from './useDataGridContextMenu';
|
|
11
|
-
/**
|
|
12
|
-
* Single orchestration hook for DataGridTable. Takes grid props and wrapper ref,
|
|
13
|
-
* returns all derived state and handlers so Fluent/Material/Radix can be thin view layers.
|
|
14
|
-
*
|
|
15
|
-
* Internally delegates to focused sub-hooks:
|
|
16
|
-
* - useDataGridLayout -- column layout, sizing, pinning, header menu
|
|
17
|
-
* - useDataGridEditing -- cell editing commit/cancel, popover editor
|
|
18
|
-
* - useDataGridInteraction -- cell selection, keyboard nav, clipboard, fill handle, undo/redo
|
|
19
|
-
* - useDataGridContextMenu -- context menu state
|
|
20
|
-
*/
|
|
21
|
-
export function useDataGridState(params) {
|
|
22
|
-
const { props, wrapperRef } = params;
|
|
23
|
-
const { items, columns, getRowId, visibleColumns, columnOrder, rowSelection = 'none', selectedRows: controlledSelectedRows, onSelectionChange, showRowNumbers, statusBar, emptyState, editable, cellSelection: cellSelectionProp, onCellValueChanged: onCellValueChangedProp, initialColumnWidths, onColumnResized, onAutosizeColumn, pinnedColumns, onColumnPinned, onCellError, } = props;
|
|
24
|
-
const cellSelection = cellSelectionProp !== false;
|
|
25
|
-
// --- Shared state hooks (called at orchestrator level to break circular deps) ---
|
|
26
|
-
const { editingCell, setEditingCell, pendingEditorValue, setPendingEditorValue, } = useCellEditing();
|
|
27
|
-
const { activeCell, setActiveCell } = useActiveCell(wrapperRef, editingCell);
|
|
28
|
-
// --- 1. Layout, pinning, header menu ---
|
|
29
|
-
const layoutResult = useDataGridLayout({
|
|
30
|
-
columns,
|
|
31
|
-
items,
|
|
32
|
-
getRowId,
|
|
33
|
-
visibleColumns,
|
|
34
|
-
columnOrder,
|
|
35
|
-
rowSelection,
|
|
36
|
-
showRowNumbers,
|
|
37
|
-
initialColumnWidths,
|
|
38
|
-
onColumnResized,
|
|
39
|
-
onAutosizeColumn,
|
|
40
|
-
pinnedColumns,
|
|
41
|
-
onColumnPinned,
|
|
42
|
-
sortBy: props.sortBy,
|
|
43
|
-
sortDirection: props.sortDirection,
|
|
44
|
-
onColumnSort: props.onColumnSort,
|
|
45
|
-
wrapperRef,
|
|
46
|
-
});
|
|
47
|
-
const { visibleCols, visibleColumnCount, colOffset, hasCheckboxCol, } = layoutResult;
|
|
48
|
-
// --- 2. Row selection ---
|
|
49
|
-
const rowSelectionResult = useRowSelection({
|
|
50
|
-
items,
|
|
51
|
-
getRowId,
|
|
52
|
-
rowSelection,
|
|
53
|
-
controlledSelectedRows,
|
|
54
|
-
onSelectionChange,
|
|
55
|
-
});
|
|
56
|
-
const { selectedRowIds, updateSelection, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected, } = rowSelectionResult;
|
|
57
|
-
// --- 3. Context menu ---
|
|
58
|
-
const contextMenuResult = useDataGridContextMenu({ cellSelection });
|
|
59
|
-
const { setContextMenuPosition } = contextMenuResult;
|
|
60
|
-
// --- 4. Interaction (selection, keyboard, clipboard, fill handle, undo/redo) ---
|
|
61
|
-
const interactionResult = useDataGridInteraction({
|
|
62
|
-
items,
|
|
63
|
-
visibleCols,
|
|
64
|
-
colOffset,
|
|
65
|
-
hasCheckboxCol,
|
|
66
|
-
visibleColumnCount,
|
|
67
|
-
getRowId,
|
|
68
|
-
editable,
|
|
69
|
-
onCellValueChangedProp,
|
|
70
|
-
cellSelection,
|
|
71
|
-
rowSelection,
|
|
72
|
-
selectedRowIds,
|
|
73
|
-
editingCell,
|
|
74
|
-
setEditingCell,
|
|
75
|
-
activeCell,
|
|
76
|
-
setActiveCell,
|
|
77
|
-
handleRowCheckboxChange,
|
|
78
|
-
setContextMenuPosition,
|
|
79
|
-
wrapperRef,
|
|
80
|
-
});
|
|
81
|
-
const { selectionRange, cutRange, copyRange, isDragging, onCellValueChanged, } = interactionResult;
|
|
82
|
-
// --- 5. Editing (commit/cancel logic) ---
|
|
83
|
-
const editingResult = useDataGridEditing({
|
|
84
|
-
editingCell,
|
|
85
|
-
setEditingCell,
|
|
86
|
-
pendingEditorValue,
|
|
87
|
-
setPendingEditorValue,
|
|
88
|
-
visibleCols,
|
|
89
|
-
itemsLength: items.length,
|
|
90
|
-
onCellValueChanged,
|
|
91
|
-
setActiveCell,
|
|
92
|
-
});
|
|
93
|
-
// --- 6. View models ---
|
|
94
|
-
const { sortBy, sortDirection, filters, onFilterChange, filterOptions, loadingFilterOptions, peopleSearch, } = props;
|
|
95
|
-
const onFilterChangeRef = useLatestRef(onFilterChange);
|
|
96
|
-
const peopleSearchRef = useLatestRef(peopleSearch);
|
|
97
|
-
const stableOnFilterChange = useCallback((...args) => onFilterChangeRef.current?.(...args), [onFilterChangeRef]);
|
|
98
|
-
const stablePeopleSearch = useCallback((...args) => peopleSearchRef.current?.(...args) ?? Promise.resolve([]), [peopleSearchRef]);
|
|
99
|
-
const headerFilterInput = useMemo(() => ({
|
|
100
|
-
sortBy,
|
|
101
|
-
sortDirection,
|
|
102
|
-
onColumnSort: layoutResult.stableOnColumnSort,
|
|
103
|
-
filters,
|
|
104
|
-
onFilterChange: stableOnFilterChange,
|
|
105
|
-
filterOptions,
|
|
106
|
-
loadingFilterOptions,
|
|
107
|
-
peopleSearch: peopleSearch ? stablePeopleSearch : undefined,
|
|
108
|
-
}), [
|
|
109
|
-
sortBy,
|
|
110
|
-
sortDirection,
|
|
111
|
-
layoutResult.stableOnColumnSort,
|
|
112
|
-
filters,
|
|
113
|
-
stableOnFilterChange,
|
|
114
|
-
filterOptions,
|
|
115
|
-
loadingFilterOptions,
|
|
116
|
-
peopleSearch, stablePeopleSearch,
|
|
117
|
-
]);
|
|
118
|
-
const cellDescriptorInput = useMemo(() => ({
|
|
119
|
-
editingCell,
|
|
120
|
-
activeCell: cellSelection ? activeCell : null,
|
|
121
|
-
selectionRange: cellSelection ? selectionRange : null,
|
|
122
|
-
cutRange: cellSelection ? cutRange : null,
|
|
123
|
-
copyRange: cellSelection ? copyRange : null,
|
|
124
|
-
colOffset,
|
|
125
|
-
itemsLength: items.length,
|
|
126
|
-
getRowId,
|
|
127
|
-
editable,
|
|
128
|
-
onCellValueChanged,
|
|
129
|
-
isDragging: cellSelection ? isDragging : false,
|
|
130
|
-
}), [
|
|
131
|
-
editingCell,
|
|
132
|
-
activeCell,
|
|
133
|
-
selectionRange,
|
|
134
|
-
cutRange,
|
|
135
|
-
copyRange,
|
|
136
|
-
colOffset,
|
|
137
|
-
items.length,
|
|
138
|
-
getRowId,
|
|
139
|
-
editable,
|
|
140
|
-
onCellValueChanged,
|
|
141
|
-
cellSelection,
|
|
142
|
-
isDragging,
|
|
143
|
-
]);
|
|
144
|
-
const aggregation = useMemo(() => computeAggregations(items, visibleCols, cellSelection ? selectionRange : null), [items, visibleCols, selectionRange, cellSelection]);
|
|
145
|
-
const statusBarConfig = useMemo(() => {
|
|
146
|
-
const base = getDataGridStatusBarConfig(statusBar, items.length, selectedRowIds.size);
|
|
147
|
-
if (!base)
|
|
148
|
-
return null;
|
|
149
|
-
return { ...base, aggregation: aggregation ?? undefined };
|
|
150
|
-
}, [statusBar, items.length, selectedRowIds.size, aggregation]);
|
|
151
|
-
const showEmptyInGrid = items.length === 0 && !!emptyState && !props.isLoading;
|
|
152
|
-
// --- Memoize remaining sub-objects ---
|
|
153
|
-
const rowSelectionState = useMemo(() => ({
|
|
154
|
-
selectedRowIds, updateSelection, handleRowCheckboxChange,
|
|
155
|
-
handleSelectAll, allSelected, someSelected,
|
|
156
|
-
}), [selectedRowIds, updateSelection, handleRowCheckboxChange, handleSelectAll, allSelected, someSelected]);
|
|
157
|
-
const viewModelsState = useMemo(() => ({
|
|
158
|
-
headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError,
|
|
159
|
-
}), [headerFilterInput, cellDescriptorInput, statusBarConfig, showEmptyInGrid, onCellError]);
|
|
160
|
-
return {
|
|
161
|
-
layout: layoutResult.layout,
|
|
162
|
-
rowSelection: rowSelectionState,
|
|
163
|
-
editing: editingResult.editing,
|
|
164
|
-
interaction: interactionResult.interaction,
|
|
165
|
-
contextMenu: contextMenuResult.contextMenu,
|
|
166
|
-
viewModels: viewModelsState,
|
|
167
|
-
pinning: layoutResult.pinning,
|
|
168
|
-
};
|
|
169
|
-
}
|