@mui/x-tree-view 8.0.0-alpha.0 → 8.0.0-alpha.2
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/CHANGELOG.md +651 -6
- package/README.md +2 -2
- package/RichTreeView/RichTreeView.js +2 -4
- package/RichTreeView/RichTreeView.types.d.ts +5 -18
- package/SimpleTreeView/SimpleTreeView.types.d.ts +2 -2
- package/TreeItem/TreeItem.js +4 -4
- package/TreeItem/TreeItem.types.d.ts +4 -2
- package/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
- package/TreeItemIcon/TreeItemIcon.types.d.ts +1 -1
- package/TreeItemProvider/TreeItemProvider.d.ts +2 -4
- package/TreeItemProvider/TreeItemProvider.js +26 -11
- package/TreeItemProvider/TreeItemProvider.types.d.ts +1 -0
- package/hooks/index.d.ts +1 -0
- package/hooks/index.js +2 -1
- package/hooks/useTreeItemModel.d.ts +2 -0
- package/hooks/useTreeItemModel.js +11 -0
- package/hooks/useTreeItemUtils/useTreeItemUtils.d.ts +2 -1
- package/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
- package/hooks/useTreeViewApiRef.d.ts +1 -0
- package/index.js +1 -1
- package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.d.ts +3 -1
- package/internals/TreeViewProvider/TreeViewChildrenItemProvider.d.ts +2 -1
- package/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
- package/internals/TreeViewProvider/TreeViewProvider.js +1 -2
- package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +4 -2
- package/internals/components/RichTreeViewItems.d.ts +3 -5
- package/internals/components/RichTreeViewItems.js +42 -30
- package/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
- package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.d.ts +36 -0
- package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
- package/internals/corePlugins/useTreeViewId/useTreeViewId.types.d.ts +1 -5
- package/internals/hooks/useSelector.d.ts +4 -0
- package/internals/hooks/useSelector.js +6 -0
- package/internals/index.d.ts +6 -1
- package/internals/index.js +5 -1
- package/internals/models/itemPlugin.d.ts +5 -5
- package/internals/models/plugin.d.ts +20 -8
- package/internals/models/treeView.d.ts +6 -0
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.d.ts +124 -0
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +6 -14
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.d.ts +1 -0
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.d.ts +182 -0
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +4 -16
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +1 -1
- package/internals/plugins/useTreeViewItems/index.d.ts +1 -1
- package/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
- package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +718 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +15 -52
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.d.ts +74 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +7 -24
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.d.ts +32 -0
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +6 -6
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +6 -6
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
- package/internals/useTreeView/useTreeView.js +30 -17
- package/internals/useTreeView/useTreeView.types.d.ts +2 -3
- package/internals/useTreeView/useTreeViewBuildContext.d.ts +3 -1
- package/internals/useTreeView/useTreeViewBuildContext.js +24 -18
- package/internals/utils/TreeViewStore.d.ts +12 -0
- package/internals/utils/TreeViewStore.js +24 -0
- package/internals/utils/selectors.d.ts +9 -0
- package/internals/utils/selectors.js +37 -0
- package/internals/utils/tree.d.ts +8 -8
- package/internals/utils/tree.js +51 -43
- package/models/items.d.ts +3 -2
- package/modern/RichTreeView/RichTreeView.js +2 -4
- package/modern/TreeItem/TreeItem.js +4 -4
- package/modern/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
- package/modern/TreeItemProvider/TreeItemProvider.js +26 -11
- package/modern/hooks/index.js +2 -1
- package/modern/hooks/useTreeItemModel.js +11 -0
- package/modern/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
- package/modern/index.js +1 -1
- package/modern/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
- package/modern/internals/TreeViewProvider/TreeViewProvider.js +1 -2
- package/modern/internals/components/RichTreeViewItems.js +42 -30
- package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
- package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
- package/modern/internals/hooks/useSelector.js +6 -0
- package/modern/internals/index.js +5 -1
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
- package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
- package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
- package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
- package/modern/internals/useTreeView/useTreeView.js +30 -17
- package/modern/internals/useTreeView/useTreeViewBuildContext.js +24 -18
- package/modern/internals/utils/TreeViewStore.js +24 -0
- package/modern/internals/utils/selectors.js +37 -0
- package/modern/internals/utils/tree.js +51 -43
- package/modern/useTreeItem/useTreeItem.js +26 -11
- package/node/RichTreeView/RichTreeView.js +2 -4
- package/node/TreeItem/TreeItem.js +4 -4
- package/node/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +2 -2
- package/node/TreeItemProvider/TreeItemProvider.js +26 -10
- package/node/hooks/index.js +8 -1
- package/node/hooks/useTreeItemModel.js +17 -0
- package/node/hooks/useTreeItemUtils/useTreeItemUtils.js +32 -15
- package/node/index.js +1 -1
- package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
- package/node/internals/TreeViewProvider/TreeViewProvider.js +1 -2
- package/node/internals/components/RichTreeViewItems.js +42 -30
- package/node/internals/corePlugins/useTreeViewId/useTreeViewId.js +12 -13
- package/node/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +15 -0
- package/node/internals/hooks/useSelector.js +13 -0
- package/node/internals/index.js +47 -1
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +23 -0
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +14 -0
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +40 -0
- package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +16 -13
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -100
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +109 -0
- package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +30 -27
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +32 -0
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +46 -35
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +15 -0
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +24 -14
- package/node/internals/useTreeView/useTreeView.js +30 -17
- package/node/internals/useTreeView/useTreeViewBuildContext.js +25 -18
- package/node/internals/utils/TreeViewStore.js +31 -0
- package/node/internals/utils/selectors.js +44 -0
- package/node/internals/utils/tree.js +51 -43
- package/node/useTreeItem/useTreeItem.js +26 -11
- package/package.json +6 -4
- package/useTreeItem/useTreeItem.js +26 -11
- package/useTreeItem/useTreeItem.types.d.ts +9 -0
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
|
|
3
4
|
import { findOrderInTremauxTree, getAllNavigableItems, getFirstNavigableItem, getLastNavigableItem, getNonDisabledItemsInRange } from "../../utils/tree.js";
|
|
4
|
-
import { convertSelectedItemsToArray, propagateSelection, getAddedAndRemovedItems, getLookupFromArray } from "./useTreeViewSelection.utils.js";
|
|
5
|
+
import { convertSelectedItemsToArray, propagateSelection, getAddedAndRemovedItems, getLookupFromArray, createSelectedItemsMap } from "./useTreeViewSelection.utils.js";
|
|
6
|
+
import { selectorIsItemSelected } from "./useTreeViewSelection.selectors.js";
|
|
5
7
|
import { useTreeViewSelectionItemPlugin } from "./useTreeViewSelection.itemPlugin.js";
|
|
6
8
|
export const useTreeViewSelection = ({
|
|
7
|
-
|
|
9
|
+
store,
|
|
8
10
|
params,
|
|
9
11
|
models
|
|
10
12
|
}) => {
|
|
11
13
|
const lastSelectedItem = React.useRef(null);
|
|
12
14
|
const lastSelectedRange = React.useRef({});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
temp.set(models.selectedItems.value, true);
|
|
21
|
-
}
|
|
22
|
-
return temp;
|
|
23
|
-
}, [models.selectedItems.value]);
|
|
15
|
+
useEnhancedEffect(() => {
|
|
16
|
+
store.update(prevState => _extends({}, prevState, {
|
|
17
|
+
selection: {
|
|
18
|
+
selectedItemsMap: createSelectedItemsMap(models.selectedItems.value)
|
|
19
|
+
}
|
|
20
|
+
}));
|
|
21
|
+
}, [store, models.selectedItems.value]);
|
|
24
22
|
const setSelectedItems = (event, newModel, additionalItemsToPropagate) => {
|
|
25
23
|
let cleanModel;
|
|
26
24
|
if (params.multiSelect && (params.selectionPropagation.descendants || params.selectionPropagation.parents)) {
|
|
27
25
|
cleanModel = propagateSelection({
|
|
28
|
-
|
|
26
|
+
store,
|
|
29
27
|
selectionPropagation: params.selectionPropagation,
|
|
30
28
|
newModel: newModel,
|
|
31
29
|
oldModel: models.selectedItems.value,
|
|
@@ -37,7 +35,7 @@ export const useTreeViewSelection = ({
|
|
|
37
35
|
if (params.onItemSelectionToggle) {
|
|
38
36
|
if (params.multiSelect) {
|
|
39
37
|
const changes = getAddedAndRemovedItems({
|
|
40
|
-
|
|
38
|
+
store,
|
|
41
39
|
newModel: cleanModel,
|
|
42
40
|
oldModel: models.selectedItems.value
|
|
43
41
|
});
|
|
@@ -63,7 +61,6 @@ export const useTreeViewSelection = ({
|
|
|
63
61
|
}
|
|
64
62
|
models.selectedItems.setControlledValue(cleanModel);
|
|
65
63
|
};
|
|
66
|
-
const isItemSelected = itemId => selectedItemsMap.has(itemId);
|
|
67
64
|
const selectItem = ({
|
|
68
65
|
event,
|
|
69
66
|
itemId,
|
|
@@ -76,7 +73,7 @@ export const useTreeViewSelection = ({
|
|
|
76
73
|
let newSelected;
|
|
77
74
|
if (keepExistingSelection) {
|
|
78
75
|
const cleanSelectedItems = convertSelectedItemsToArray(models.selectedItems.value);
|
|
79
|
-
const isSelectedBefore =
|
|
76
|
+
const isSelectedBefore = selectorIsItemSelected(store.value, itemId);
|
|
80
77
|
if (isSelectedBefore && (shouldBeSelected === false || shouldBeSelected == null)) {
|
|
81
78
|
newSelected = cleanSelectedItems.filter(id => id !== itemId);
|
|
82
79
|
} else if (!isSelectedBefore && (shouldBeSelected === true || shouldBeSelected == null)) {
|
|
@@ -86,14 +83,14 @@ export const useTreeViewSelection = ({
|
|
|
86
83
|
}
|
|
87
84
|
} else {
|
|
88
85
|
// eslint-disable-next-line no-lonely-if
|
|
89
|
-
if (shouldBeSelected === false || shouldBeSelected == null &&
|
|
86
|
+
if (shouldBeSelected === false || shouldBeSelected == null && selectorIsItemSelected(store.value, itemId)) {
|
|
90
87
|
newSelected = params.multiSelect ? [] : null;
|
|
91
88
|
} else {
|
|
92
89
|
newSelected = params.multiSelect ? [itemId] : itemId;
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
setSelectedItems(event, newSelected,
|
|
96
|
-
// If shouldBeSelected ===
|
|
93
|
+
// If shouldBeSelected === selectorIsItemSelected(store, itemId), we still want to propagate the select.
|
|
97
94
|
// This is useful when the element is in an indeterminate state.
|
|
98
95
|
[itemId]);
|
|
99
96
|
lastSelectedItem.current = itemId;
|
|
@@ -113,7 +110,7 @@ export const useTreeViewSelection = ({
|
|
|
113
110
|
|
|
114
111
|
// Add to the model the items that are part of the new range and not already part of the model.
|
|
115
112
|
const selectedItemsLookup = getLookupFromArray(newSelectedItems);
|
|
116
|
-
const range = getNonDisabledItemsInRange(
|
|
113
|
+
const range = getNonDisabledItemsInRange(store.value, start, end);
|
|
117
114
|
const itemsToAddToModel = range.filter(id => !selectedItemsLookup[id]);
|
|
118
115
|
newSelectedItems = newSelectedItems.concat(itemsToAddToModel);
|
|
119
116
|
setSelectedItems(event, newSelectedItems);
|
|
@@ -121,21 +118,21 @@ export const useTreeViewSelection = ({
|
|
|
121
118
|
};
|
|
122
119
|
const expandSelectionRange = (event, itemId) => {
|
|
123
120
|
if (lastSelectedItem.current != null) {
|
|
124
|
-
const [start, end] = findOrderInTremauxTree(
|
|
121
|
+
const [start, end] = findOrderInTremauxTree(store.value, itemId, lastSelectedItem.current);
|
|
125
122
|
selectRange(event, [start, end]);
|
|
126
123
|
}
|
|
127
124
|
};
|
|
128
125
|
const selectRangeFromStartToItem = (event, itemId) => {
|
|
129
|
-
selectRange(event, [getFirstNavigableItem(
|
|
126
|
+
selectRange(event, [getFirstNavigableItem(store.value), itemId]);
|
|
130
127
|
};
|
|
131
128
|
const selectRangeFromItemToEnd = (event, itemId) => {
|
|
132
|
-
selectRange(event, [itemId, getLastNavigableItem(
|
|
129
|
+
selectRange(event, [itemId, getLastNavigableItem(store.value)]);
|
|
133
130
|
};
|
|
134
131
|
const selectAllNavigableItems = event => {
|
|
135
132
|
if (params.disableSelection || !params.multiSelect) {
|
|
136
133
|
return;
|
|
137
134
|
}
|
|
138
|
-
const navigableItems = getAllNavigableItems(
|
|
135
|
+
const navigableItems = getAllNavigableItems(store.value);
|
|
139
136
|
setSelectedItems(event, navigableItems);
|
|
140
137
|
lastSelectedRange.current = getLookupFromArray(navigableItems);
|
|
141
138
|
};
|
|
@@ -164,6 +161,17 @@ export const useTreeViewSelection = ({
|
|
|
164
161
|
}
|
|
165
162
|
setSelectedItems(event, newSelectedItems);
|
|
166
163
|
};
|
|
164
|
+
const pluginContextValue = React.useMemo(() => ({
|
|
165
|
+
selection: {
|
|
166
|
+
multiSelect: params.multiSelect,
|
|
167
|
+
checkboxSelection: params.checkboxSelection,
|
|
168
|
+
disableSelection: params.disableSelection,
|
|
169
|
+
selectionPropagation: {
|
|
170
|
+
descendants: params.selectionPropagation.descendants,
|
|
171
|
+
parents: params.selectionPropagation.parents
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}), [params.multiSelect, params.checkboxSelection, params.disableSelection, params.selectionPropagation.descendants, params.selectionPropagation.parents]);
|
|
167
175
|
return {
|
|
168
176
|
getRootProps: () => ({
|
|
169
177
|
'aria-multiselectable': params.multiSelect
|
|
@@ -172,7 +180,6 @@ export const useTreeViewSelection = ({
|
|
|
172
180
|
selectItem
|
|
173
181
|
},
|
|
174
182
|
instance: {
|
|
175
|
-
isItemSelected,
|
|
176
183
|
selectItem,
|
|
177
184
|
selectAllNavigableItems,
|
|
178
185
|
expandSelectionRange,
|
|
@@ -180,14 +187,7 @@ export const useTreeViewSelection = ({
|
|
|
180
187
|
selectRangeFromItemToEnd,
|
|
181
188
|
selectItemFromArrowNavigation
|
|
182
189
|
},
|
|
183
|
-
contextValue:
|
|
184
|
-
selection: {
|
|
185
|
-
multiSelect: params.multiSelect,
|
|
186
|
-
checkboxSelection: params.checkboxSelection,
|
|
187
|
-
disableSelection: params.disableSelection,
|
|
188
|
-
selectionPropagation: params.selectionPropagation
|
|
189
|
-
}
|
|
190
|
-
}
|
|
190
|
+
contextValue: pluginContextValue
|
|
191
191
|
};
|
|
192
192
|
};
|
|
193
193
|
useTreeViewSelection.itemPlugin = useTreeViewSelectionItemPlugin;
|
|
@@ -197,6 +197,7 @@ useTreeViewSelection.models = {
|
|
|
197
197
|
}
|
|
198
198
|
};
|
|
199
199
|
const DEFAULT_SELECTED_ITEMS = [];
|
|
200
|
+
const EMPTY_SELECTION_PROPAGATION = {};
|
|
200
201
|
useTreeViewSelection.getDefaultizedParams = ({
|
|
201
202
|
params
|
|
202
203
|
}) => _extends({}, params, {
|
|
@@ -204,7 +205,17 @@ useTreeViewSelection.getDefaultizedParams = ({
|
|
|
204
205
|
multiSelect: params.multiSelect ?? false,
|
|
205
206
|
checkboxSelection: params.checkboxSelection ?? false,
|
|
206
207
|
defaultSelectedItems: params.defaultSelectedItems ?? (params.multiSelect ? DEFAULT_SELECTED_ITEMS : null),
|
|
207
|
-
selectionPropagation: params.selectionPropagation ??
|
|
208
|
+
selectionPropagation: params.selectionPropagation ?? EMPTY_SELECTION_PROPAGATION
|
|
209
|
+
});
|
|
210
|
+
useTreeViewSelection.getInitialState = params => ({
|
|
211
|
+
selection: {
|
|
212
|
+
selectedItemsMap: createSelectedItemsMap(params.selectedItems === undefined ? params.defaultSelectedItems : params.selectedItems)
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
useTreeViewSelection.getInitialState = params => ({
|
|
216
|
+
selection: {
|
|
217
|
+
selectedItemsMap: createSelectedItemsMap(params.selectedItems === undefined ? params.defaultSelectedItems : params.selectedItems)
|
|
218
|
+
}
|
|
208
219
|
});
|
|
209
220
|
useTreeViewSelection.params = {
|
|
210
221
|
disableSelection: true,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createSelector } from "../../utils/selectors.js";
|
|
2
|
+
const selectorTreeViewSelectionState = state => state.selection;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check if an item is selected.
|
|
6
|
+
* @param {TreeViewState<[UseTreeViewSelectionSignature]>} state The state of the tree view.
|
|
7
|
+
* @returns {boolean} `true` if the item is selected, `false` otherwise.
|
|
8
|
+
*/
|
|
9
|
+
export const selectorIsItemSelected = createSelector([selectorTreeViewSelectionState, (_, itemId) => itemId], (selectionState, itemId) => selectionState.selectedItemsMap.has(itemId));
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { selectorIsItemSelected } from "./useTreeViewSelection.selectors.js";
|
|
2
|
+
import { selectorItemOrderedChildrenIds, selectorItemParentId } from "../useTreeViewItems/useTreeViewItems.selectors.js";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Transform the `selectedItems` model to be an array if it was a string or null.
|
|
3
6
|
* @param {string[] | string | null} model The raw model.
|
|
@@ -12,6 +15,13 @@ export const convertSelectedItemsToArray = model => {
|
|
|
12
15
|
}
|
|
13
16
|
return [];
|
|
14
17
|
};
|
|
18
|
+
export const createSelectedItemsMap = selectedItems => {
|
|
19
|
+
const selectedItemsMap = new Map();
|
|
20
|
+
convertSelectedItemsToArray(selectedItems).forEach(id => {
|
|
21
|
+
selectedItemsMap.set(id, true);
|
|
22
|
+
});
|
|
23
|
+
return selectedItemsMap;
|
|
24
|
+
};
|
|
15
25
|
export const getLookupFromArray = array => {
|
|
16
26
|
const lookup = {};
|
|
17
27
|
array.forEach(itemId => {
|
|
@@ -20,18 +30,18 @@ export const getLookupFromArray = array => {
|
|
|
20
30
|
return lookup;
|
|
21
31
|
};
|
|
22
32
|
export const getAddedAndRemovedItems = ({
|
|
23
|
-
|
|
33
|
+
store,
|
|
24
34
|
oldModel,
|
|
25
35
|
newModel
|
|
26
36
|
}) => {
|
|
27
|
-
const newModelLookup =
|
|
37
|
+
const newModelLookup = createSelectedItemsMap(newModel);
|
|
28
38
|
return {
|
|
29
|
-
added: newModel.filter(itemId => !
|
|
30
|
-
removed: oldModel.filter(itemId => !newModelLookup
|
|
39
|
+
added: newModel.filter(itemId => !selectorIsItemSelected(store.value, itemId)),
|
|
40
|
+
removed: oldModel.filter(itemId => !newModelLookup.has(itemId))
|
|
31
41
|
};
|
|
32
42
|
};
|
|
33
43
|
export const propagateSelection = ({
|
|
34
|
-
|
|
44
|
+
store,
|
|
35
45
|
selectionPropagation,
|
|
36
46
|
newModel,
|
|
37
47
|
oldModel,
|
|
@@ -43,7 +53,7 @@ export const propagateSelection = ({
|
|
|
43
53
|
let shouldRegenerateModel = false;
|
|
44
54
|
const newModelLookup = getLookupFromArray(newModel);
|
|
45
55
|
const changes = getAddedAndRemovedItems({
|
|
46
|
-
|
|
56
|
+
store,
|
|
47
57
|
newModel,
|
|
48
58
|
oldModel
|
|
49
59
|
});
|
|
@@ -63,7 +73,7 @@ export const propagateSelection = ({
|
|
|
63
73
|
shouldRegenerateModel = true;
|
|
64
74
|
newModelLookup[itemId] = true;
|
|
65
75
|
}
|
|
66
|
-
|
|
76
|
+
selectorItemOrderedChildrenIds(store.value, itemId).forEach(selectDescendants);
|
|
67
77
|
};
|
|
68
78
|
selectDescendants(addedItemId);
|
|
69
79
|
}
|
|
@@ -72,15 +82,15 @@ export const propagateSelection = ({
|
|
|
72
82
|
if (!newModelLookup[itemId]) {
|
|
73
83
|
return false;
|
|
74
84
|
}
|
|
75
|
-
const children =
|
|
85
|
+
const children = selectorItemOrderedChildrenIds(store.value, itemId);
|
|
76
86
|
return children.every(checkAllDescendantsSelected);
|
|
77
87
|
};
|
|
78
88
|
const selectParents = itemId => {
|
|
79
|
-
const parentId =
|
|
89
|
+
const parentId = selectorItemParentId(store.value, itemId);
|
|
80
90
|
if (parentId == null) {
|
|
81
91
|
return;
|
|
82
92
|
}
|
|
83
|
-
const siblings =
|
|
93
|
+
const siblings = selectorItemOrderedChildrenIds(store.value, parentId);
|
|
84
94
|
if (siblings.every(checkAllDescendantsSelected)) {
|
|
85
95
|
shouldRegenerateModel = true;
|
|
86
96
|
newModelLookup[parentId] = true;
|
|
@@ -92,13 +102,13 @@ export const propagateSelection = ({
|
|
|
92
102
|
});
|
|
93
103
|
changes.removed.forEach(removedItemId => {
|
|
94
104
|
if (selectionPropagation.parents) {
|
|
95
|
-
let parentId =
|
|
105
|
+
let parentId = selectorItemParentId(store.value, removedItemId);
|
|
96
106
|
while (parentId != null) {
|
|
97
107
|
if (newModelLookup[parentId]) {
|
|
98
108
|
shouldRegenerateModel = true;
|
|
99
109
|
delete newModelLookup[parentId];
|
|
100
110
|
}
|
|
101
|
-
parentId =
|
|
111
|
+
parentId = selectorItemParentId(store.value, parentId);
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
if (selectionPropagation.descendants) {
|
|
@@ -107,7 +117,7 @@ export const propagateSelection = ({
|
|
|
107
117
|
shouldRegenerateModel = true;
|
|
108
118
|
delete newModelLookup[itemId];
|
|
109
119
|
}
|
|
110
|
-
|
|
120
|
+
selectorItemOrderedChildrenIds(store.value, itemId).forEach(deSelectDescendants);
|
|
111
121
|
};
|
|
112
122
|
deSelectDescendants(removedItemId);
|
|
113
123
|
}
|
|
@@ -5,6 +5,7 @@ import { useTreeViewModels } from "./useTreeViewModels.js";
|
|
|
5
5
|
import { TREE_VIEW_CORE_PLUGINS } from "../corePlugins/index.js";
|
|
6
6
|
import { extractPluginParamsFromProps } from "./extractPluginParamsFromProps.js";
|
|
7
7
|
import { useTreeViewBuildContext } from "./useTreeViewBuildContext.js";
|
|
8
|
+
import { TreeViewStore } from "../utils/TreeViewStore.js";
|
|
8
9
|
export function useTreeViewApiInitialization(inputApiRef) {
|
|
9
10
|
const fallbackPublicApiRef = React.useRef({});
|
|
10
11
|
if (inputApiRef) {
|
|
@@ -15,12 +16,13 @@ export function useTreeViewApiInitialization(inputApiRef) {
|
|
|
15
16
|
}
|
|
16
17
|
return fallbackPublicApiRef.current;
|
|
17
18
|
}
|
|
19
|
+
let globalId = 0;
|
|
18
20
|
export const useTreeView = ({
|
|
19
21
|
plugins: inPlugins,
|
|
20
22
|
rootRef,
|
|
21
23
|
props
|
|
22
24
|
}) => {
|
|
23
|
-
const plugins = [...TREE_VIEW_CORE_PLUGINS, ...inPlugins];
|
|
25
|
+
const plugins = React.useMemo(() => [...TREE_VIEW_CORE_PLUGINS, ...inPlugins], [inPlugins]);
|
|
24
26
|
const {
|
|
25
27
|
pluginParams,
|
|
26
28
|
forwardedProps,
|
|
@@ -38,22 +40,30 @@ export const useTreeView = ({
|
|
|
38
40
|
const publicAPI = useTreeViewApiInitialization(apiRef);
|
|
39
41
|
const innerRootRef = React.useRef(null);
|
|
40
42
|
const handleRootRef = useForkRef(innerRootRef, rootRef);
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
const storeRef = React.useRef(null);
|
|
44
|
+
if (storeRef.current == null) {
|
|
45
|
+
globalId += 1;
|
|
46
|
+
const initialState = {
|
|
47
|
+
cacheKey: {
|
|
48
|
+
id: globalId
|
|
49
|
+
}
|
|
50
|
+
};
|
|
49
51
|
plugins.forEach(plugin => {
|
|
50
52
|
if (plugin.getInitialState) {
|
|
51
|
-
Object.assign(
|
|
53
|
+
Object.assign(initialState, plugin.getInitialState(pluginParams));
|
|
52
54
|
}
|
|
53
55
|
});
|
|
54
|
-
|
|
56
|
+
storeRef.current = new TreeViewStore(initialState);
|
|
57
|
+
}
|
|
58
|
+
const baseContextValue = useTreeViewBuildContext({
|
|
59
|
+
plugins,
|
|
60
|
+
instance,
|
|
61
|
+
publicAPI,
|
|
62
|
+
store: storeRef.current,
|
|
63
|
+
rootRef: innerRootRef
|
|
55
64
|
});
|
|
56
65
|
const rootPropsGetters = [];
|
|
66
|
+
const pluginContextValues = [];
|
|
57
67
|
const runPlugin = plugin => {
|
|
58
68
|
const pluginResponse = plugin({
|
|
59
69
|
instance,
|
|
@@ -61,11 +71,10 @@ export const useTreeView = ({
|
|
|
61
71
|
slots,
|
|
62
72
|
slotProps,
|
|
63
73
|
experimentalFeatures,
|
|
64
|
-
state,
|
|
65
|
-
setState,
|
|
66
74
|
rootRef: innerRootRef,
|
|
67
75
|
models,
|
|
68
|
-
plugins
|
|
76
|
+
plugins,
|
|
77
|
+
store: storeRef.current
|
|
69
78
|
});
|
|
70
79
|
if (pluginResponse.getRootProps) {
|
|
71
80
|
rootPropsGetters.push(pluginResponse.getRootProps);
|
|
@@ -77,7 +86,7 @@ export const useTreeView = ({
|
|
|
77
86
|
Object.assign(instance, pluginResponse.instance);
|
|
78
87
|
}
|
|
79
88
|
if (pluginResponse.contextValue) {
|
|
80
|
-
|
|
89
|
+
pluginContextValues.push(pluginResponse.contextValue);
|
|
81
90
|
}
|
|
82
91
|
};
|
|
83
92
|
plugins.forEach(runPlugin);
|
|
@@ -92,10 +101,14 @@ export const useTreeView = ({
|
|
|
92
101
|
});
|
|
93
102
|
return rootProps;
|
|
94
103
|
};
|
|
104
|
+
const contextValue = React.useMemo(() => {
|
|
105
|
+
const copiedBaseContextValue = _extends({}, baseContextValue);
|
|
106
|
+
return Object.assign(copiedBaseContextValue, ...pluginContextValues);
|
|
107
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
108
|
+
}, [baseContextValue, ...pluginContextValues]);
|
|
95
109
|
return {
|
|
96
110
|
getRootProps,
|
|
97
111
|
rootRef: handleRootRef,
|
|
98
|
-
contextValue
|
|
99
|
-
instance
|
|
112
|
+
contextValue
|
|
100
113
|
};
|
|
101
114
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
1
2
|
export const useTreeViewBuildContext = ({
|
|
2
3
|
plugins,
|
|
3
4
|
instance,
|
|
4
5
|
publicAPI,
|
|
6
|
+
store,
|
|
5
7
|
rootRef
|
|
6
8
|
}) => {
|
|
7
|
-
const runItemPlugins = itemPluginProps => {
|
|
9
|
+
const runItemPlugins = React.useCallback(itemPluginProps => {
|
|
8
10
|
let finalRootRef = null;
|
|
9
11
|
let finalContentRef = null;
|
|
10
12
|
const pluginPropEnhancers = [];
|
|
@@ -49,10 +51,11 @@ export const useTreeViewBuildContext = ({
|
|
|
49
51
|
rootRef: finalRootRef,
|
|
50
52
|
propsEnhancers
|
|
51
53
|
};
|
|
52
|
-
};
|
|
53
|
-
const wrapItem = ({
|
|
54
|
+
}, [plugins]);
|
|
55
|
+
const wrapItem = React.useCallback(({
|
|
54
56
|
itemId,
|
|
55
|
-
children
|
|
57
|
+
children,
|
|
58
|
+
idAttribute
|
|
56
59
|
}) => {
|
|
57
60
|
let finalChildren = children;
|
|
58
61
|
// The wrappers are reversed to ensure that the first wrapper is the outermost one.
|
|
@@ -60,15 +63,16 @@ export const useTreeViewBuildContext = ({
|
|
|
60
63
|
const plugin = plugins[i];
|
|
61
64
|
if (plugin.wrapItem) {
|
|
62
65
|
finalChildren = plugin.wrapItem({
|
|
66
|
+
instance,
|
|
63
67
|
itemId,
|
|
64
68
|
children: finalChildren,
|
|
65
|
-
|
|
69
|
+
idAttribute
|
|
66
70
|
});
|
|
67
71
|
}
|
|
68
72
|
}
|
|
69
73
|
return finalChildren;
|
|
70
|
-
};
|
|
71
|
-
const wrapRoot = ({
|
|
74
|
+
}, [plugins, instance]);
|
|
75
|
+
const wrapRoot = React.useCallback(({
|
|
72
76
|
children
|
|
73
77
|
}) => {
|
|
74
78
|
let finalChildren = children;
|
|
@@ -77,19 +81,21 @@ export const useTreeViewBuildContext = ({
|
|
|
77
81
|
const plugin = plugins[i];
|
|
78
82
|
if (plugin.wrapRoot) {
|
|
79
83
|
finalChildren = plugin.wrapRoot({
|
|
80
|
-
children: finalChildren
|
|
81
|
-
instance
|
|
84
|
+
children: finalChildren
|
|
82
85
|
});
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
return finalChildren;
|
|
86
|
-
};
|
|
87
|
-
return {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
}, [plugins]);
|
|
90
|
+
return React.useMemo(() => {
|
|
91
|
+
return {
|
|
92
|
+
runItemPlugins,
|
|
93
|
+
wrapItem,
|
|
94
|
+
wrapRoot,
|
|
95
|
+
instance,
|
|
96
|
+
publicAPI,
|
|
97
|
+
store,
|
|
98
|
+
rootRef
|
|
99
|
+
};
|
|
100
|
+
}, [runItemPlugins, wrapItem, wrapRoot, instance, publicAPI, store, rootRef]);
|
|
95
101
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class TreeViewStore {
|
|
2
|
+
constructor(value) {
|
|
3
|
+
this.value = void 0;
|
|
4
|
+
this.listeners = void 0;
|
|
5
|
+
this.subscribe = fn => {
|
|
6
|
+
this.listeners.add(fn);
|
|
7
|
+
return () => {
|
|
8
|
+
this.listeners.delete(fn);
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
this.getSnapshot = () => {
|
|
12
|
+
return this.value;
|
|
13
|
+
};
|
|
14
|
+
this.update = updater => {
|
|
15
|
+
const newState = updater(this.value);
|
|
16
|
+
if (newState !== this.value) {
|
|
17
|
+
this.value = newState;
|
|
18
|
+
this.listeners.forEach(l => l(newState));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
this.value = value;
|
|
22
|
+
this.listeners = new Set();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { lruMemoize, createSelectorCreator } from 'reselect';
|
|
2
|
+
const reselectCreateSelector = createSelectorCreator({
|
|
3
|
+
memoize: lruMemoize,
|
|
4
|
+
memoizeOptions: {
|
|
5
|
+
maxSize: 1,
|
|
6
|
+
equalityCheck: Object.is
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
const cache = new WeakMap();
|
|
10
|
+
/**
|
|
11
|
+
* Method wrapping reselect's createSelector to provide caching for tree view instances.
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
export const createSelector = (...createSelectorArgs) => {
|
|
15
|
+
const selector = (state, selectorArgs) => {
|
|
16
|
+
const cacheKey = state.cacheKey;
|
|
17
|
+
|
|
18
|
+
// If there is no cache for the current tree view instance, create one.
|
|
19
|
+
let cacheForCurrentTreeViewInstance = cache.get(cacheKey);
|
|
20
|
+
if (!cacheForCurrentTreeViewInstance) {
|
|
21
|
+
cacheForCurrentTreeViewInstance = new Map();
|
|
22
|
+
cache.set(cacheKey, cacheForCurrentTreeViewInstance);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// If there is a cached selector, execute it.
|
|
26
|
+
const cachedSelector = cacheForCurrentTreeViewInstance.get(createSelectorArgs);
|
|
27
|
+
if (cachedSelector) {
|
|
28
|
+
return cachedSelector(state, selectorArgs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Otherwise, create a new selector and cache it and execute it.
|
|
32
|
+
const fn = reselectCreateSelector(...createSelectorArgs);
|
|
33
|
+
cacheForCurrentTreeViewInstance.set(createSelectorArgs, fn);
|
|
34
|
+
return fn(state, selectorArgs);
|
|
35
|
+
};
|
|
36
|
+
return selector;
|
|
37
|
+
};
|