@mui/x-tree-view 7.0.0-beta.7 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +195 -12
- package/README.md +1 -1
- package/RichTreeView/RichTreeView.js +12 -14
- package/RichTreeView/RichTreeView.types.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.js +3 -4
- package/TreeItem/TreeItem.js +43 -35
- package/TreeItem/TreeItem.types.d.ts +3 -3
- package/TreeItem/TreeItemContent.d.ts +7 -7
- package/TreeItem/TreeItemContent.js +10 -10
- package/TreeItem/useTreeItemState.d.ts +1 -1
- package/TreeItem/useTreeItemState.js +13 -13
- package/TreeItem2/TreeItem2.js +16 -17
- package/TreeItem2Icon/TreeItem2Icon.js +5 -6
- package/TreeItem2Provider/TreeItem2Provider.js +3 -3
- package/TreeItem2Provider/TreeItem2Provider.types.d.ts +1 -1
- package/TreeView/TreeView.d.ts +1 -1
- package/TreeView/TreeView.js +1 -1
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.d.ts +2 -2
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +12 -12
- package/index.js +1 -1
- package/internals/hooks/useInstanceEventHandler.js +5 -10
- package/internals/models/plugin.d.ts +1 -1
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +11 -18
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +3 -3
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +59 -43
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +6 -5
- package/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +17 -18
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +2 -2
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +70 -77
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +4 -1
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +24 -29
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +11 -11
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +18 -21
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +4 -4
- package/internals/useTreeView/useTreeView.js +5 -6
- package/internals/useTreeView/useTreeView.utils.d.ts +2 -2
- package/internals/useTreeView/useTreeView.utils.js +22 -22
- package/internals/utils/extractPluginParamsFromProps.js +2 -2
- package/internals/utils/utils.js +1 -0
- package/modern/RichTreeView/RichTreeView.js +7 -7
- package/modern/SimpleTreeView/SimpleTreeView.js +1 -1
- package/modern/TreeItem/TreeItem.js +31 -22
- package/modern/TreeItem/TreeItemContent.js +10 -10
- package/modern/TreeItem/useTreeItemState.js +13 -13
- package/modern/TreeItem2/TreeItem2.js +11 -11
- package/modern/TreeItem2Provider/TreeItem2Provider.js +3 -3
- package/modern/TreeView/TreeView.js +1 -1
- package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +12 -12
- package/modern/index.js +1 -1
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +7 -7
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +57 -38
- package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +17 -17
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +69 -74
- package/modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +19 -20
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +13 -13
- package/modern/internals/useTreeView/useTreeView.js +3 -4
- package/modern/internals/useTreeView/useTreeView.utils.js +22 -22
- package/modern/internals/utils/utils.js +1 -0
- package/modern/useTreeItem2/useTreeItem2.js +23 -12
- package/node/RichTreeView/RichTreeView.js +7 -7
- package/node/SimpleTreeView/SimpleTreeView.js +1 -1
- package/node/TreeItem/TreeItem.js +31 -22
- package/node/TreeItem/TreeItemContent.js +10 -10
- package/node/TreeItem/useTreeItemState.js +13 -13
- package/node/TreeItem2/TreeItem2.js +11 -11
- package/node/TreeItem2Provider/TreeItem2Provider.js +3 -3
- package/node/TreeView/TreeView.js +1 -1
- package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +12 -12
- package/node/index.js +1 -1
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +7 -7
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +57 -38
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +17 -17
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +69 -74
- package/node/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +19 -20
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +13 -13
- package/node/internals/useTreeView/useTreeView.js +3 -4
- package/node/internals/useTreeView/useTreeView.utils.js +22 -22
- package/node/internals/utils/utils.js +1 -0
- package/node/useTreeItem2/useTreeItem2.js +23 -12
- package/package.json +5 -5
- package/useTreeItem2/useTreeItem2.js +26 -18
- package/useTreeItem2/useTreeItem2.types.d.ts +9 -7
package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -15,8 +15,7 @@ function findNextFirstChar(firstChars, startIndex, char) {
|
|
|
15
15
|
}
|
|
16
16
|
export const useTreeViewKeyboardNavigation = ({
|
|
17
17
|
instance,
|
|
18
|
-
params
|
|
19
|
-
state
|
|
18
|
+
params
|
|
20
19
|
}) => {
|
|
21
20
|
const theme = useTheme();
|
|
22
21
|
const isRTL = theme.direction === 'rtl';
|
|
@@ -33,35 +32,32 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
33
32
|
const newFirstCharMap = {};
|
|
34
33
|
const processItem = item => {
|
|
35
34
|
const getItemId = params.getItemId;
|
|
36
|
-
const
|
|
37
|
-
newFirstCharMap[
|
|
35
|
+
const itemId = getItemId ? getItemId(item) : item.id;
|
|
36
|
+
newFirstCharMap[itemId] = instance.getNode(itemId).label.substring(0, 1).toLowerCase();
|
|
38
37
|
item.children?.forEach(processItem);
|
|
39
38
|
};
|
|
40
39
|
params.items.forEach(processItem);
|
|
41
40
|
firstCharMap.current = newFirstCharMap;
|
|
42
41
|
}, [params.items, params.getItemId, instance]);
|
|
43
|
-
|
|
44
|
-
updateFirstCharMap
|
|
45
|
-
});
|
|
46
|
-
const getFirstMatchingNode = (nodeId, firstChar) => {
|
|
42
|
+
const getFirstMatchingItem = (itemId, firstChar) => {
|
|
47
43
|
let start;
|
|
48
44
|
let index;
|
|
49
45
|
const lowercaseChar = firstChar.toLowerCase();
|
|
50
46
|
const firstCharIds = [];
|
|
51
47
|
const firstChars = [];
|
|
52
48
|
// This really only works since the ids are strings
|
|
53
|
-
Object.keys(firstCharMap.current).forEach(
|
|
54
|
-
const map = instance.getNode(
|
|
49
|
+
Object.keys(firstCharMap.current).forEach(mapItemId => {
|
|
50
|
+
const map = instance.getNode(mapItemId);
|
|
55
51
|
const visible = map.parentId ? instance.isNodeExpanded(map.parentId) : true;
|
|
56
|
-
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isNodeDisabled(
|
|
52
|
+
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isNodeDisabled(mapItemId);
|
|
57
53
|
if (visible && !shouldBeSkipped) {
|
|
58
|
-
firstCharIds.push(
|
|
59
|
-
firstChars.push(firstCharMap.current[
|
|
54
|
+
firstCharIds.push(mapItemId);
|
|
55
|
+
firstChars.push(firstCharMap.current[mapItemId]);
|
|
60
56
|
}
|
|
61
57
|
});
|
|
62
58
|
|
|
63
59
|
// Get start index for search based on position of currentItem
|
|
64
|
-
start = firstCharIds.indexOf(
|
|
60
|
+
start = firstCharIds.indexOf(itemId) + 1;
|
|
65
61
|
if (start >= firstCharIds.length) {
|
|
66
62
|
start = 0;
|
|
67
63
|
}
|
|
@@ -80,20 +76,17 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
80
76
|
}
|
|
81
77
|
return null;
|
|
82
78
|
};
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
return !instance.isNodeDisabled(
|
|
79
|
+
const canToggleItemSelection = itemId => !params.disableSelection && !instance.isNodeDisabled(itemId);
|
|
80
|
+
const canToggleItemExpansion = itemId => {
|
|
81
|
+
return !instance.isNodeDisabled(itemId) && instance.isNodeExpandable(itemId);
|
|
86
82
|
};
|
|
87
83
|
|
|
88
84
|
// ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
|
|
89
|
-
const
|
|
90
|
-
otherHandlers.onKeyDown?.(event);
|
|
85
|
+
const handleItemKeyDown = (event, itemId) => {
|
|
91
86
|
if (event.defaultMuiPrevented) {
|
|
92
87
|
return;
|
|
93
88
|
}
|
|
94
|
-
|
|
95
|
-
// If the tree is empty, there will be no focused node
|
|
96
|
-
if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
|
|
89
|
+
if (event.altKey || event.currentTarget !== event.target) {
|
|
97
90
|
return;
|
|
98
91
|
}
|
|
99
92
|
const ctrlPressed = event.ctrlKey || event.metaKey;
|
|
@@ -102,17 +95,17 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
102
95
|
// eslint-disable-next-line default-case
|
|
103
96
|
switch (true) {
|
|
104
97
|
// Select the node when pressing "Space"
|
|
105
|
-
case key === ' ' &&
|
|
98
|
+
case key === ' ' && canToggleItemSelection(itemId):
|
|
106
99
|
{
|
|
107
100
|
event.preventDefault();
|
|
108
101
|
if (params.multiSelect && event.shiftKey) {
|
|
109
102
|
instance.selectRange(event, {
|
|
110
|
-
end:
|
|
103
|
+
end: itemId
|
|
111
104
|
});
|
|
112
105
|
} else if (params.multiSelect) {
|
|
113
|
-
instance.selectNode(event,
|
|
106
|
+
instance.selectNode(event, itemId, true);
|
|
114
107
|
} else {
|
|
115
|
-
instance.selectNode(event,
|
|
108
|
+
instance.selectNode(event, itemId);
|
|
116
109
|
}
|
|
117
110
|
break;
|
|
118
111
|
}
|
|
@@ -121,84 +114,87 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
121
114
|
// If the focused node has no children, we select it.
|
|
122
115
|
case key === 'Enter':
|
|
123
116
|
{
|
|
124
|
-
if (
|
|
125
|
-
instance.toggleNodeExpansion(event,
|
|
117
|
+
if (canToggleItemExpansion(itemId)) {
|
|
118
|
+
instance.toggleNodeExpansion(event, itemId);
|
|
126
119
|
event.preventDefault();
|
|
127
|
-
} else if (
|
|
120
|
+
} else if (canToggleItemSelection(itemId)) {
|
|
128
121
|
if (params.multiSelect) {
|
|
129
122
|
event.preventDefault();
|
|
130
|
-
instance.selectNode(event,
|
|
131
|
-
} else if (!instance.isNodeSelected(
|
|
132
|
-
instance.selectNode(event,
|
|
123
|
+
instance.selectNode(event, itemId, true);
|
|
124
|
+
} else if (!instance.isNodeSelected(itemId)) {
|
|
125
|
+
instance.selectNode(event, itemId);
|
|
133
126
|
event.preventDefault();
|
|
134
127
|
}
|
|
135
128
|
}
|
|
136
129
|
break;
|
|
137
130
|
}
|
|
138
131
|
|
|
139
|
-
// Focus the next focusable
|
|
132
|
+
// Focus the next focusable item
|
|
140
133
|
case key === 'ArrowDown':
|
|
141
134
|
{
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
135
|
+
const nextItem = getNextNode(instance, itemId);
|
|
136
|
+
if (nextItem) {
|
|
144
137
|
event.preventDefault();
|
|
145
|
-
instance.focusItem(event,
|
|
138
|
+
instance.focusItem(event, nextItem);
|
|
146
139
|
|
|
147
140
|
// Multi select behavior when pressing Shift + ArrowDown
|
|
148
|
-
// Toggles the selection state of the next
|
|
149
|
-
if (params.multiSelect && event.shiftKey &&
|
|
141
|
+
// Toggles the selection state of the next item
|
|
142
|
+
if (params.multiSelect && event.shiftKey && canToggleItemSelection(nextItem)) {
|
|
150
143
|
instance.selectRange(event, {
|
|
151
|
-
end:
|
|
152
|
-
current:
|
|
144
|
+
end: nextItem,
|
|
145
|
+
current: itemId
|
|
153
146
|
}, true);
|
|
154
147
|
}
|
|
155
148
|
}
|
|
156
149
|
break;
|
|
157
150
|
}
|
|
158
151
|
|
|
159
|
-
// Focuses the previous focusable
|
|
152
|
+
// Focuses the previous focusable item
|
|
160
153
|
case key === 'ArrowUp':
|
|
161
154
|
{
|
|
162
|
-
const
|
|
163
|
-
if (
|
|
155
|
+
const previousItem = getPreviousNode(instance, itemId);
|
|
156
|
+
if (previousItem) {
|
|
164
157
|
event.preventDefault();
|
|
165
|
-
instance.focusItem(event,
|
|
158
|
+
instance.focusItem(event, previousItem);
|
|
166
159
|
|
|
167
160
|
// Multi select behavior when pressing Shift + ArrowUp
|
|
168
|
-
// Toggles the selection state of the previous
|
|
169
|
-
if (params.multiSelect && event.shiftKey &&
|
|
161
|
+
// Toggles the selection state of the previous item
|
|
162
|
+
if (params.multiSelect && event.shiftKey && canToggleItemSelection(previousItem)) {
|
|
170
163
|
instance.selectRange(event, {
|
|
171
|
-
end:
|
|
172
|
-
current:
|
|
164
|
+
end: previousItem,
|
|
165
|
+
current: itemId
|
|
173
166
|
}, true);
|
|
174
167
|
}
|
|
175
168
|
}
|
|
176
169
|
break;
|
|
177
170
|
}
|
|
178
171
|
|
|
179
|
-
// If the focused
|
|
180
|
-
// If the focused
|
|
172
|
+
// If the focused item is expanded, we move the focus to its first child
|
|
173
|
+
// If the focused item is collapsed and has children, we expand it
|
|
181
174
|
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
182
175
|
{
|
|
183
|
-
if (instance.isNodeExpanded(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
176
|
+
if (instance.isNodeExpanded(itemId)) {
|
|
177
|
+
const nextNodeId = getNextNode(instance, itemId);
|
|
178
|
+
if (nextNodeId) {
|
|
179
|
+
instance.focusItem(event, nextNodeId);
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
}
|
|
182
|
+
} else if (canToggleItemExpansion(itemId)) {
|
|
183
|
+
instance.toggleNodeExpansion(event, itemId);
|
|
188
184
|
event.preventDefault();
|
|
189
185
|
}
|
|
190
186
|
break;
|
|
191
187
|
}
|
|
192
188
|
|
|
193
|
-
// If the focused
|
|
194
|
-
// If the focused
|
|
189
|
+
// If the focused item is expanded, we collapse it
|
|
190
|
+
// If the focused item is collapsed and has a parent, we move the focus to this parent
|
|
195
191
|
case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
|
|
196
192
|
{
|
|
197
|
-
if (
|
|
198
|
-
instance.toggleNodeExpansion(event,
|
|
193
|
+
if (canToggleItemExpansion(itemId) && instance.isNodeExpanded(itemId)) {
|
|
194
|
+
instance.toggleNodeExpansion(event, itemId);
|
|
199
195
|
event.preventDefault();
|
|
200
196
|
} else {
|
|
201
|
-
const parent = instance.getNode(
|
|
197
|
+
const parent = instance.getNode(itemId).parentId;
|
|
202
198
|
if (parent) {
|
|
203
199
|
instance.focusItem(event, parent);
|
|
204
200
|
event.preventDefault();
|
|
@@ -214,31 +210,31 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
214
210
|
|
|
215
211
|
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
216
212
|
// Selects the focused node and all nodes up to the first node.
|
|
217
|
-
if (
|
|
218
|
-
instance.rangeSelectToFirst(event,
|
|
213
|
+
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
214
|
+
instance.rangeSelectToFirst(event, itemId);
|
|
219
215
|
}
|
|
220
216
|
event.preventDefault();
|
|
221
217
|
break;
|
|
222
218
|
}
|
|
223
219
|
|
|
224
|
-
// Focuses the last
|
|
220
|
+
// Focuses the last item in the tree
|
|
225
221
|
case key === 'End':
|
|
226
222
|
{
|
|
227
223
|
instance.focusItem(event, getLastNode(instance));
|
|
228
224
|
|
|
229
225
|
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
230
|
-
// Selects the focused
|
|
231
|
-
if (
|
|
232
|
-
instance.rangeSelectToLast(event,
|
|
226
|
+
// Selects the focused item and all the items down to the last item.
|
|
227
|
+
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
228
|
+
instance.rangeSelectToLast(event, itemId);
|
|
233
229
|
}
|
|
234
230
|
event.preventDefault();
|
|
235
231
|
break;
|
|
236
232
|
}
|
|
237
233
|
|
|
238
|
-
// Expand all siblings that are at the same level as the focused
|
|
234
|
+
// Expand all siblings that are at the same level as the focused item
|
|
239
235
|
case key === '*':
|
|
240
236
|
{
|
|
241
|
-
instance.expandAllSiblings(event,
|
|
237
|
+
instance.expandAllSiblings(event, itemId);
|
|
242
238
|
event.preventDefault();
|
|
243
239
|
break;
|
|
244
240
|
}
|
|
@@ -259,7 +255,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
259
255
|
// TODO: Support typing multiple characters
|
|
260
256
|
case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
|
|
261
257
|
{
|
|
262
|
-
const matchingNode =
|
|
258
|
+
const matchingNode = getFirstMatchingItem(itemId, key);
|
|
263
259
|
if (matchingNode != null) {
|
|
264
260
|
instance.focusItem(event, matchingNode);
|
|
265
261
|
event.preventDefault();
|
|
@@ -268,10 +264,9 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
268
264
|
}
|
|
269
265
|
}
|
|
270
266
|
};
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
};
|
|
267
|
+
populateInstance(instance, {
|
|
268
|
+
updateFirstCharMap,
|
|
269
|
+
handleItemKeyDown
|
|
270
|
+
});
|
|
276
271
|
};
|
|
277
272
|
useTreeViewKeyboardNavigation.params = {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
3
|
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
5
4
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
6
5
|
const updateNodesState = ({
|
|
@@ -52,34 +51,34 @@ export const useTreeViewNodes = ({
|
|
|
52
51
|
state,
|
|
53
52
|
setState
|
|
54
53
|
}) => {
|
|
55
|
-
const getNode = React.useCallback(
|
|
56
|
-
const getItem = React.useCallback(
|
|
57
|
-
const isNodeDisabled = React.useCallback(
|
|
58
|
-
if (
|
|
54
|
+
const getNode = React.useCallback(itemId => state.nodes.nodeMap[itemId], [state.nodes.nodeMap]);
|
|
55
|
+
const getItem = React.useCallback(itemId => state.nodes.itemMap[itemId], [state.nodes.itemMap]);
|
|
56
|
+
const isNodeDisabled = React.useCallback(itemId => {
|
|
57
|
+
if (itemId == null) {
|
|
59
58
|
return false;
|
|
60
59
|
}
|
|
61
|
-
let
|
|
60
|
+
let item = instance.getNode(itemId);
|
|
62
61
|
|
|
63
|
-
// This can be called before the
|
|
64
|
-
if (!
|
|
62
|
+
// This can be called before the item has been added to the node map.
|
|
63
|
+
if (!item) {
|
|
65
64
|
return false;
|
|
66
65
|
}
|
|
67
|
-
if (
|
|
66
|
+
if (item.disabled) {
|
|
68
67
|
return true;
|
|
69
68
|
}
|
|
70
|
-
while (
|
|
71
|
-
|
|
72
|
-
if (
|
|
69
|
+
while (item.parentId != null) {
|
|
70
|
+
item = instance.getNode(item.parentId);
|
|
71
|
+
if (item.disabled) {
|
|
73
72
|
return true;
|
|
74
73
|
}
|
|
75
74
|
}
|
|
76
75
|
return false;
|
|
77
76
|
}, [instance]);
|
|
78
|
-
const getChildrenIds =
|
|
79
|
-
const getNavigableChildrenIds =
|
|
80
|
-
let childrenIds = instance.getChildrenIds(
|
|
77
|
+
const getChildrenIds = React.useCallback(itemId => Object.values(state.nodes.nodeMap).filter(item => item.parentId === itemId).sort((a, b) => a.index - b.index).map(child => child.id), [state.nodes.nodeMap]);
|
|
78
|
+
const getNavigableChildrenIds = itemId => {
|
|
79
|
+
let childrenIds = instance.getChildrenIds(itemId);
|
|
81
80
|
if (!params.disabledItemsFocusable) {
|
|
82
|
-
childrenIds = childrenIds.filter(
|
|
81
|
+
childrenIds = childrenIds.filter(item => !instance.isNodeDisabled(item));
|
|
83
82
|
}
|
|
84
83
|
return childrenIds;
|
|
85
84
|
};
|
|
@@ -104,19 +103,19 @@ export const useTreeViewNodes = ({
|
|
|
104
103
|
});
|
|
105
104
|
}, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
|
|
106
105
|
const getNodesToRender = () => {
|
|
107
|
-
const
|
|
106
|
+
const getPropsFromItemId = ({
|
|
108
107
|
id,
|
|
109
108
|
children
|
|
110
109
|
}) => {
|
|
111
110
|
const node = state.nodes.nodeMap[id];
|
|
112
111
|
return {
|
|
113
112
|
label: node.label,
|
|
114
|
-
|
|
113
|
+
itemId: node.id,
|
|
115
114
|
id: node.idAttribute,
|
|
116
|
-
children: children?.map(
|
|
115
|
+
children: children?.map(getPropsFromItemId)
|
|
117
116
|
};
|
|
118
117
|
};
|
|
119
|
-
return state.nodes.nodeTree.map(
|
|
118
|
+
return state.nodes.nodeTree.map(getPropsFromItemId);
|
|
120
119
|
};
|
|
121
120
|
populateInstance(instance, {
|
|
122
121
|
getNode,
|
|
@@ -35,26 +35,26 @@ export const useTreeViewSelection = ({
|
|
|
35
35
|
}
|
|
36
36
|
models.selectedItems.setControlledValue(newSelectedItems);
|
|
37
37
|
};
|
|
38
|
-
const isNodeSelected =
|
|
39
|
-
const selectNode = (event,
|
|
38
|
+
const isNodeSelected = itemId => Array.isArray(models.selectedItems.value) ? models.selectedItems.value.indexOf(itemId) !== -1 : models.selectedItems.value === itemId;
|
|
39
|
+
const selectNode = (event, itemId, multiple = false) => {
|
|
40
40
|
if (params.disableSelection) {
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
if (multiple) {
|
|
44
44
|
if (Array.isArray(models.selectedItems.value)) {
|
|
45
45
|
let newSelected;
|
|
46
|
-
if (models.selectedItems.value.indexOf(
|
|
47
|
-
newSelected = models.selectedItems.value.filter(id => id !==
|
|
46
|
+
if (models.selectedItems.value.indexOf(itemId) !== -1) {
|
|
47
|
+
newSelected = models.selectedItems.value.filter(id => id !== itemId);
|
|
48
48
|
} else {
|
|
49
|
-
newSelected = [
|
|
49
|
+
newSelected = [itemId].concat(models.selectedItems.value);
|
|
50
50
|
}
|
|
51
51
|
setSelectedItems(event, newSelected);
|
|
52
52
|
}
|
|
53
53
|
} else {
|
|
54
|
-
const newSelected = params.multiSelect ? [
|
|
54
|
+
const newSelected = params.multiSelect ? [itemId] : itemId;
|
|
55
55
|
setSelectedItems(event, newSelected);
|
|
56
56
|
}
|
|
57
|
-
lastSelectedNode.current =
|
|
57
|
+
lastSelectedNode.current = itemId;
|
|
58
58
|
lastSelectionWasRange.current = false;
|
|
59
59
|
currentRangeSelection.current = [];
|
|
60
60
|
};
|
|
@@ -135,21 +135,21 @@ export const useTreeViewSelection = ({
|
|
|
135
135
|
}
|
|
136
136
|
lastSelectionWasRange.current = true;
|
|
137
137
|
};
|
|
138
|
-
const rangeSelectToFirst = (event,
|
|
138
|
+
const rangeSelectToFirst = (event, itemId) => {
|
|
139
139
|
if (!lastSelectedNode.current) {
|
|
140
|
-
lastSelectedNode.current =
|
|
140
|
+
lastSelectedNode.current = itemId;
|
|
141
141
|
}
|
|
142
|
-
const start = lastSelectionWasRange.current ? lastSelectedNode.current :
|
|
142
|
+
const start = lastSelectionWasRange.current ? lastSelectedNode.current : itemId;
|
|
143
143
|
instance.selectRange(event, {
|
|
144
144
|
start,
|
|
145
145
|
end: getFirstNode(instance)
|
|
146
146
|
});
|
|
147
147
|
};
|
|
148
|
-
const rangeSelectToLast = (event,
|
|
148
|
+
const rangeSelectToLast = (event, itemId) => {
|
|
149
149
|
if (!lastSelectedNode.current) {
|
|
150
|
-
lastSelectedNode.current =
|
|
150
|
+
lastSelectedNode.current = itemId;
|
|
151
151
|
}
|
|
152
|
-
const start = lastSelectionWasRange.current ? lastSelectedNode.current :
|
|
152
|
+
const start = lastSelectionWasRange.current ? lastSelectedNode.current : itemId;
|
|
153
153
|
instance.selectRange(event, {
|
|
154
154
|
start,
|
|
155
155
|
end: getLastNode(instance)
|
|
@@ -87,13 +87,13 @@ export const useTreeView = inParams => {
|
|
|
87
87
|
};
|
|
88
88
|
const itemWrappers = plugins.map(plugin => plugin.wrapItem).filter(wrapItem => !!wrapItem);
|
|
89
89
|
contextValue.wrapItem = ({
|
|
90
|
-
|
|
90
|
+
itemId,
|
|
91
91
|
children
|
|
92
92
|
}) => {
|
|
93
93
|
let finalChildren = children;
|
|
94
94
|
itemWrappers.forEach(itemWrapper => {
|
|
95
95
|
finalChildren = itemWrapper({
|
|
96
|
-
|
|
96
|
+
itemId,
|
|
97
97
|
children: finalChildren
|
|
98
98
|
});
|
|
99
99
|
});
|
|
@@ -101,8 +101,7 @@ export const useTreeView = inParams => {
|
|
|
101
101
|
};
|
|
102
102
|
const getRootProps = (otherHandlers = {}) => {
|
|
103
103
|
const rootProps = _extends({
|
|
104
|
-
role: 'tree'
|
|
105
|
-
tabIndex: 0
|
|
104
|
+
role: 'tree'
|
|
106
105
|
}, otherHandlers, {
|
|
107
106
|
ref: handleRootRef
|
|
108
107
|
});
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
export const getPreviousNode = (instance,
|
|
2
|
-
const
|
|
3
|
-
const siblings = instance.getNavigableChildrenIds(
|
|
4
|
-
const
|
|
5
|
-
if (
|
|
6
|
-
return
|
|
1
|
+
export const getPreviousNode = (instance, itemId) => {
|
|
2
|
+
const item = instance.getNode(itemId);
|
|
3
|
+
const siblings = instance.getNavigableChildrenIds(item.parentId);
|
|
4
|
+
const itemIndex = siblings.indexOf(itemId);
|
|
5
|
+
if (itemIndex === 0) {
|
|
6
|
+
return item.parentId;
|
|
7
7
|
}
|
|
8
|
-
let
|
|
9
|
-
while (instance.isNodeExpanded(
|
|
10
|
-
|
|
8
|
+
let currentItem = siblings[itemIndex - 1];
|
|
9
|
+
while (instance.isNodeExpanded(currentItem) && instance.getNavigableChildrenIds(currentItem).length > 0) {
|
|
10
|
+
currentItem = instance.getNavigableChildrenIds(currentItem).pop();
|
|
11
11
|
}
|
|
12
|
-
return
|
|
12
|
+
return currentItem;
|
|
13
13
|
};
|
|
14
|
-
export const getNextNode = (instance,
|
|
14
|
+
export const getNextNode = (instance, itemId) => {
|
|
15
15
|
// If expanded get first child
|
|
16
|
-
if (instance.isNodeExpanded(
|
|
17
|
-
return instance.getNavigableChildrenIds(
|
|
16
|
+
if (instance.isNodeExpanded(itemId) && instance.getNavigableChildrenIds(itemId).length > 0) {
|
|
17
|
+
return instance.getNavigableChildrenIds(itemId)[0];
|
|
18
18
|
}
|
|
19
|
-
let
|
|
20
|
-
while (
|
|
19
|
+
let item = instance.getNode(itemId);
|
|
20
|
+
while (item != null) {
|
|
21
21
|
// Try to get next sibling
|
|
22
|
-
const siblings = instance.getNavigableChildrenIds(
|
|
23
|
-
const nextSibling = siblings[siblings.indexOf(
|
|
22
|
+
const siblings = instance.getNavigableChildrenIds(item.parentId);
|
|
23
|
+
const nextSibling = siblings[siblings.indexOf(item.id) + 1];
|
|
24
24
|
if (nextSibling) {
|
|
25
25
|
return nextSibling;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// If the sibling does not exist, go up a level to the parent and try again.
|
|
29
|
-
|
|
29
|
+
item = instance.getNode(item.parentId);
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
32
32
|
};
|
|
33
33
|
export const getLastNode = instance => {
|
|
34
|
-
let
|
|
35
|
-
while (instance.isNodeExpanded(
|
|
36
|
-
|
|
34
|
+
let lastItem = instance.getNavigableChildrenIds(null).pop();
|
|
35
|
+
while (instance.isNodeExpanded(lastItem)) {
|
|
36
|
+
lastItem = instance.getNavigableChildrenIds(lastItem).pop();
|
|
37
37
|
}
|
|
38
|
-
return
|
|
38
|
+
return lastItem;
|
|
39
39
|
};
|
|
40
40
|
export const getFirstNode = instance => instance.getNavigableChildrenIds(null)[0];
|
|
41
41
|
export const populateInstance = (instance, methods) => {
|
|
@@ -15,7 +15,7 @@ export const useTreeItem2 = parameters => {
|
|
|
15
15
|
} = useTreeViewContext();
|
|
16
16
|
const {
|
|
17
17
|
id,
|
|
18
|
-
|
|
18
|
+
itemId,
|
|
19
19
|
label,
|
|
20
20
|
children,
|
|
21
21
|
rootRef
|
|
@@ -28,25 +28,34 @@ export const useTreeItem2 = parameters => {
|
|
|
28
28
|
interactions,
|
|
29
29
|
status
|
|
30
30
|
} = useTreeItem2Utils({
|
|
31
|
-
|
|
31
|
+
itemId,
|
|
32
32
|
children
|
|
33
33
|
});
|
|
34
|
-
const idAttribute = instance.getTreeItemId(
|
|
34
|
+
const idAttribute = instance.getTreeItemId(itemId, id);
|
|
35
35
|
const handleRootRef = useForkRef(rootRef, pluginRootRef);
|
|
36
36
|
const createRootHandleFocus = otherHandlers => event => {
|
|
37
37
|
otherHandlers.onFocus?.(event);
|
|
38
38
|
if (event.defaultMuiPrevented) {
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
// DOM focus stays on the tree which manages focus with aria-activedescendant
|
|
43
|
-
if (event.target === event.currentTarget) {
|
|
44
|
-
instance.focusRoot();
|
|
45
|
-
}
|
|
46
41
|
const canBeFocused = !status.disabled || disabledItemsFocusable;
|
|
47
42
|
if (!status.focused && canBeFocused && event.currentTarget === event.target) {
|
|
48
|
-
instance.focusItem(event,
|
|
43
|
+
instance.focusItem(event, itemId);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const createRootHandleBlur = otherHandlers => event => {
|
|
47
|
+
otherHandlers.onBlur?.(event);
|
|
48
|
+
if (event.defaultMuiPrevented) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
instance.removeFocusedItem();
|
|
52
|
+
};
|
|
53
|
+
const createRootHandleKeyDown = otherHandlers => event => {
|
|
54
|
+
otherHandlers.onKeyDown?.(event);
|
|
55
|
+
if (event.defaultMuiPrevented) {
|
|
56
|
+
return;
|
|
49
57
|
}
|
|
58
|
+
instance.handleItemKeyDown(event, itemId);
|
|
50
59
|
};
|
|
51
60
|
const createContentHandleClick = otherHandlers => event => {
|
|
52
61
|
otherHandlers.onClick?.(event);
|
|
@@ -76,7 +85,7 @@ export const useTreeItem2 = parameters => {
|
|
|
76
85
|
/* single-selection trees unset aria-selected on un-selected items.
|
|
77
86
|
*
|
|
78
87
|
* If the tree does not support multiple selection, aria-selected
|
|
79
|
-
* is set to true for the selected
|
|
88
|
+
* is set to true for the selected item and it is not present on any other item in the tree.
|
|
80
89
|
* Source: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/
|
|
81
90
|
*/
|
|
82
91
|
ariaSelected = true;
|
|
@@ -84,13 +93,15 @@ export const useTreeItem2 = parameters => {
|
|
|
84
93
|
return _extends({}, externalEventHandlers, {
|
|
85
94
|
ref: handleRootRef,
|
|
86
95
|
role: 'treeitem',
|
|
87
|
-
tabIndex: -1,
|
|
96
|
+
tabIndex: instance.canItemBeTabbed(itemId) ? 0 : -1,
|
|
88
97
|
id: idAttribute,
|
|
89
98
|
'aria-expanded': status.expandable ? status.expanded : undefined,
|
|
90
99
|
'aria-selected': ariaSelected,
|
|
91
100
|
'aria-disabled': status.disabled || undefined
|
|
92
101
|
}, externalProps, {
|
|
93
|
-
onFocus: createRootHandleFocus(externalEventHandlers)
|
|
102
|
+
onFocus: createRootHandleFocus(externalEventHandlers),
|
|
103
|
+
onBlur: createRootHandleBlur(externalEventHandlers),
|
|
104
|
+
onKeyDown: createRootHandleKeyDown(externalEventHandlers)
|
|
94
105
|
});
|
|
95
106
|
};
|
|
96
107
|
const getContentProps = (externalProps = {}) => {
|
|
@@ -45,7 +45,7 @@ function WrappedTreeItem({
|
|
|
45
45
|
slotProps,
|
|
46
46
|
label,
|
|
47
47
|
id,
|
|
48
|
-
|
|
48
|
+
itemId,
|
|
49
49
|
children
|
|
50
50
|
}) {
|
|
51
51
|
const Item = slots?.item ?? _TreeItem.TreeItem;
|
|
@@ -53,12 +53,12 @@ function WrappedTreeItem({
|
|
|
53
53
|
elementType: Item,
|
|
54
54
|
externalSlotProps: slotProps?.item,
|
|
55
55
|
additionalProps: {
|
|
56
|
-
|
|
56
|
+
itemId,
|
|
57
57
|
id,
|
|
58
58
|
label
|
|
59
59
|
},
|
|
60
60
|
ownerState: {
|
|
61
|
-
|
|
61
|
+
itemId,
|
|
62
62
|
label
|
|
63
63
|
}
|
|
64
64
|
});
|
|
@@ -66,7 +66,7 @@ function WrappedTreeItem({
|
|
|
66
66
|
children: children
|
|
67
67
|
}));
|
|
68
68
|
}
|
|
69
|
-
const childrenWarning = (0, _warning.buildWarning)(['MUI X: The `RichTreeView` component does not support JSX children.', 'If you want to add items, you need to use the `items` prop', 'Check the documentation for more details: https://
|
|
69
|
+
const childrenWarning = (0, _warning.buildWarning)(['MUI X: The `RichTreeView` component does not support JSX children.', 'If you want to add items, you need to use the `items` prop', 'Check the documentation for more details: https://mui.com/x/react-tree-view/rich-tree-view/items/']);
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
*
|
|
@@ -116,7 +116,7 @@ const RichTreeView = exports.RichTreeView = /*#__PURE__*/React.forwardRef(functi
|
|
|
116
116
|
const nodesToRender = instance.getNodesToRender();
|
|
117
117
|
const renderNode = ({
|
|
118
118
|
label,
|
|
119
|
-
|
|
119
|
+
itemId,
|
|
120
120
|
id,
|
|
121
121
|
children
|
|
122
122
|
}) => {
|
|
@@ -125,9 +125,9 @@ const RichTreeView = exports.RichTreeView = /*#__PURE__*/React.forwardRef(functi
|
|
|
125
125
|
slotProps: slotProps,
|
|
126
126
|
label: label,
|
|
127
127
|
id: id,
|
|
128
|
-
|
|
128
|
+
itemId: itemId,
|
|
129
129
|
children: children?.map(renderNode)
|
|
130
|
-
},
|
|
130
|
+
}, itemId);
|
|
131
131
|
};
|
|
132
132
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeViewProvider.TreeViewProvider, {
|
|
133
133
|
value: contextValue,
|