@mui/x-tree-view 7.0.0-beta.7 → 7.1.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 +266 -12
- package/README.md +1 -1
- package/RichTreeView/RichTreeView.js +15 -17
- package/RichTreeView/RichTreeView.types.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.js +3 -4
- package/SimpleTreeView/SimpleTreeView.plugins.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- 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/treeItemClasses.d.ts +1 -1
- 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/TreeItem2Icon/TreeItem2Icon.types.d.ts +4 -4
- 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/hooks/useTreeViewApiRef.d.ts +1 -1
- package/index.js +1 -1
- package/internals/TreeViewProvider/DescendantProvider.d.ts +1 -1
- package/internals/TreeViewProvider/DescendantProvider.js +1 -1
- package/internals/hooks/useInstanceEventHandler.js +5 -10
- package/internals/index.d.ts +2 -2
- package/internals/models/plugin.d.ts +1 -1
- package/internals/plugins/defaultPlugins.d.ts +3 -3
- package/internals/plugins/defaultPlugins.js +2 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +17 -24
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +6 -6
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +76 -58
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +9 -8
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +6 -6
- package/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +2 -2
- package/internals/plugins/useTreeViewItems/index.d.ts +2 -0
- package/internals/plugins/useTreeViewItems/index.js +1 -0
- package/internals/plugins/useTreeViewItems/useTreeViewItems.d.ts +3 -0
- package/{modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js → internals/plugins/useTreeViewItems/useTreeViewItems.js} +42 -33
- package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +99 -0
- package/internals/plugins/useTreeViewJSXItems/index.d.ts +2 -0
- package/internals/plugins/useTreeViewJSXItems/index.js +1 -0
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.d.ts +3 -0
- package/{modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js → internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js} +41 -40
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +18 -0
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +85 -96
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +6 -3
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +44 -47
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +8 -8
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +7 -7
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/internals/useTreeView/useTreeView.js +5 -6
- package/internals/useTreeView/useTreeView.utils.d.ts +5 -5
- package/internals/useTreeView/useTreeView.utils.js +18 -18
- package/internals/utils/extractPluginParamsFromProps.js +2 -2
- package/internals/utils/utils.js +1 -0
- package/modern/RichTreeView/RichTreeView.js +11 -11
- package/modern/SimpleTreeView/SimpleTreeView.js +1 -1
- package/modern/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- 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/TreeViewProvider/DescendantProvider.js +1 -1
- package/modern/internals/plugins/defaultPlugins.js +2 -2
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -14
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +74 -53
- package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/modern/internals/plugins/useTreeViewItems/index.js +1 -0
- package/{internals/plugins/useTreeViewNodes/useTreeViewNodes.js → modern/internals/plugins/useTreeViewItems/useTreeViewItems.js} +46 -41
- package/modern/internals/plugins/useTreeViewJSXItems/index.js +1 -0
- package/{internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js → modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js} +41 -41
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +85 -94
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +40 -40
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/modern/internals/useTreeView/useTreeView.js +3 -4
- package/modern/internals/useTreeView/useTreeView.utils.js +18 -18
- package/modern/internals/utils/utils.js +1 -0
- package/modern/useTreeItem2/useTreeItem2.js +23 -12
- package/node/RichTreeView/RichTreeView.js +11 -11
- package/node/SimpleTreeView/SimpleTreeView.js +1 -1
- package/node/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- 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/TreeViewProvider/DescendantProvider.js +1 -1
- package/node/internals/plugins/defaultPlugins.js +2 -2
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -14
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +74 -53
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/node/internals/plugins/useTreeViewItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +44 -35
- package/node/internals/plugins/useTreeViewJSXItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.js → useTreeViewJSXItems/useTreeViewJSXItems.js} +43 -42
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +84 -93
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +39 -39
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/node/internals/useTreeView/useTreeView.js +3 -4
- package/node/internals/useTreeView/useTreeView.utils.js +23 -23
- package/node/internals/utils/utils.js +1 -0
- package/node/useTreeItem2/useTreeItem2.js +23 -12
- package/package.json +5 -5
- package/useTreeItem2/useTreeItem2.d.ts +1 -1
- package/useTreeItem2/useTreeItem2.js +26 -18
- package/useTreeItem2/useTreeItem2.types.d.ts +9 -7
- package/internals/plugins/useTreeViewJSXNodes/index.d.ts +0 -2
- package/internals/plugins/useTreeViewJSXNodes/index.js +0 -1
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.d.ts +0 -3
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +0 -18
- package/internals/plugins/useTreeViewNodes/index.d.ts +0 -2
- package/internals/plugins/useTreeViewNodes/index.js +0 -1
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.d.ts +0 -3
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +0 -88
- package/modern/internals/plugins/useTreeViewJSXNodes/index.js +0 -1
- package/modern/internals/plugins/useTreeViewNodes/index.js +0 -1
- package/node/internals/plugins/useTreeViewJSXNodes/index.js +0 -12
- package/node/internals/plugins/useTreeViewNodes/index.js +0 -12
- /package/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
- /package/modern/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/modern/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
- /package/node/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.types.js → useTreeViewItems/useTreeViewItems.types.js} +0 -0
- /package/node/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.js → useTreeViewJSXItems/useTreeViewJSXItems.types.js} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useTheme } from '@mui/material/styles';
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
|
-
import {
|
|
4
|
+
import { getFirstItem, getLastItem, getNextItem, getPreviousItem, populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
5
5
|
function isPrintableCharacter(string) {
|
|
6
6
|
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
7
7
|
}
|
|
@@ -21,48 +21,39 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
21
21
|
const theme = useTheme();
|
|
22
22
|
const isRTL = theme.direction === 'rtl';
|
|
23
23
|
const firstCharMap = React.useRef({});
|
|
24
|
-
const hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
|
|
25
24
|
const updateFirstCharMap = useEventCallback(callback => {
|
|
26
|
-
hasFirstCharMapBeenUpdatedImperatively.current = true;
|
|
27
25
|
firstCharMap.current = callback(firstCharMap.current);
|
|
28
26
|
});
|
|
29
27
|
React.useEffect(() => {
|
|
30
|
-
if (
|
|
28
|
+
if (instance.areItemUpdatesPrevented()) {
|
|
31
29
|
return;
|
|
32
30
|
}
|
|
33
31
|
const newFirstCharMap = {};
|
|
34
|
-
const processItem =
|
|
35
|
-
|
|
36
|
-
const getItemId = params.getItemId;
|
|
37
|
-
const nodeId = getItemId ? getItemId(item) : item.id;
|
|
38
|
-
newFirstCharMap[nodeId] = instance.getNode(nodeId).label.substring(0, 1).toLowerCase();
|
|
39
|
-
(_item$children = item.children) == null || _item$children.forEach(processItem);
|
|
32
|
+
const processItem = node => {
|
|
33
|
+
newFirstCharMap[node.id] = node.label.substring(0, 1).toLowerCase();
|
|
40
34
|
};
|
|
41
|
-
|
|
35
|
+
Object.values(state.items.nodeMap).forEach(processItem);
|
|
42
36
|
firstCharMap.current = newFirstCharMap;
|
|
43
|
-
}, [
|
|
44
|
-
|
|
45
|
-
updateFirstCharMap
|
|
46
|
-
});
|
|
47
|
-
const getFirstMatchingNode = (nodeId, firstChar) => {
|
|
37
|
+
}, [state.items.nodeMap, params.getItemId, instance]);
|
|
38
|
+
const getFirstMatchingItem = (itemId, firstChar) => {
|
|
48
39
|
let start;
|
|
49
40
|
let index;
|
|
50
41
|
const lowercaseChar = firstChar.toLowerCase();
|
|
51
42
|
const firstCharIds = [];
|
|
52
43
|
const firstChars = [];
|
|
53
44
|
// This really only works since the ids are strings
|
|
54
|
-
Object.keys(firstCharMap.current).forEach(
|
|
55
|
-
const map = instance.getNode(
|
|
56
|
-
const visible = map.parentId ? instance.
|
|
57
|
-
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.
|
|
45
|
+
Object.keys(firstCharMap.current).forEach(mapItemId => {
|
|
46
|
+
const map = instance.getNode(mapItemId);
|
|
47
|
+
const visible = map.parentId ? instance.isItemExpanded(map.parentId) : true;
|
|
48
|
+
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isItemDisabled(mapItemId);
|
|
58
49
|
if (visible && !shouldBeSkipped) {
|
|
59
|
-
firstCharIds.push(
|
|
60
|
-
firstChars.push(firstCharMap.current[
|
|
50
|
+
firstCharIds.push(mapItemId);
|
|
51
|
+
firstChars.push(firstCharMap.current[mapItemId]);
|
|
61
52
|
}
|
|
62
53
|
});
|
|
63
54
|
|
|
64
55
|
// Get start index for search based on position of currentItem
|
|
65
|
-
start = firstCharIds.indexOf(
|
|
56
|
+
start = firstCharIds.indexOf(itemId) + 1;
|
|
66
57
|
if (start >= firstCharIds.length) {
|
|
67
58
|
start = 0;
|
|
68
59
|
}
|
|
@@ -81,21 +72,17 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
81
72
|
}
|
|
82
73
|
return null;
|
|
83
74
|
};
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
return !instance.
|
|
75
|
+
const canToggleItemSelection = itemId => !params.disableSelection && !instance.isItemDisabled(itemId);
|
|
76
|
+
const canToggleItemExpansion = itemId => {
|
|
77
|
+
return !instance.isItemDisabled(itemId) && instance.isItemExpandable(itemId);
|
|
87
78
|
};
|
|
88
79
|
|
|
89
80
|
// ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
|
|
90
|
-
const
|
|
91
|
-
var _otherHandlers$onKeyD;
|
|
92
|
-
(_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null || _otherHandlers$onKeyD.call(otherHandlers, event);
|
|
81
|
+
const handleItemKeyDown = (event, itemId) => {
|
|
93
82
|
if (event.defaultMuiPrevented) {
|
|
94
83
|
return;
|
|
95
84
|
}
|
|
96
|
-
|
|
97
|
-
// If the tree is empty, there will be no focused node
|
|
98
|
-
if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
|
|
85
|
+
if (event.altKey || event.currentTarget !== event.target) {
|
|
99
86
|
return;
|
|
100
87
|
}
|
|
101
88
|
const ctrlPressed = event.ctrlKey || event.metaKey;
|
|
@@ -103,104 +90,107 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
103
90
|
|
|
104
91
|
// eslint-disable-next-line default-case
|
|
105
92
|
switch (true) {
|
|
106
|
-
// Select the
|
|
107
|
-
case key === ' ' &&
|
|
93
|
+
// Select the item when pressing "Space"
|
|
94
|
+
case key === ' ' && canToggleItemSelection(itemId):
|
|
108
95
|
{
|
|
109
96
|
event.preventDefault();
|
|
110
97
|
if (params.multiSelect && event.shiftKey) {
|
|
111
98
|
instance.selectRange(event, {
|
|
112
|
-
end:
|
|
99
|
+
end: itemId
|
|
113
100
|
});
|
|
114
101
|
} else if (params.multiSelect) {
|
|
115
|
-
instance.
|
|
102
|
+
instance.selectItem(event, itemId, true);
|
|
116
103
|
} else {
|
|
117
|
-
instance.
|
|
104
|
+
instance.selectItem(event, itemId);
|
|
118
105
|
}
|
|
119
106
|
break;
|
|
120
107
|
}
|
|
121
108
|
|
|
122
|
-
// If the focused
|
|
123
|
-
// If the focused
|
|
109
|
+
// If the focused item has children, we expand it.
|
|
110
|
+
// If the focused item has no children, we select it.
|
|
124
111
|
case key === 'Enter':
|
|
125
112
|
{
|
|
126
|
-
if (
|
|
127
|
-
instance.
|
|
113
|
+
if (canToggleItemExpansion(itemId)) {
|
|
114
|
+
instance.toggleItemExpansion(event, itemId);
|
|
128
115
|
event.preventDefault();
|
|
129
|
-
} else if (
|
|
116
|
+
} else if (canToggleItemSelection(itemId)) {
|
|
130
117
|
if (params.multiSelect) {
|
|
131
118
|
event.preventDefault();
|
|
132
|
-
instance.
|
|
133
|
-
} else if (!instance.
|
|
134
|
-
instance.
|
|
119
|
+
instance.selectItem(event, itemId, true);
|
|
120
|
+
} else if (!instance.isItemSelected(itemId)) {
|
|
121
|
+
instance.selectItem(event, itemId);
|
|
135
122
|
event.preventDefault();
|
|
136
123
|
}
|
|
137
124
|
}
|
|
138
125
|
break;
|
|
139
126
|
}
|
|
140
127
|
|
|
141
|
-
// Focus the next focusable
|
|
128
|
+
// Focus the next focusable item
|
|
142
129
|
case key === 'ArrowDown':
|
|
143
130
|
{
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
131
|
+
const nextItem = getNextItem(instance, itemId);
|
|
132
|
+
if (nextItem) {
|
|
146
133
|
event.preventDefault();
|
|
147
|
-
instance.focusItem(event,
|
|
134
|
+
instance.focusItem(event, nextItem);
|
|
148
135
|
|
|
149
136
|
// Multi select behavior when pressing Shift + ArrowDown
|
|
150
|
-
// Toggles the selection state of the next
|
|
151
|
-
if (params.multiSelect && event.shiftKey &&
|
|
137
|
+
// Toggles the selection state of the next item
|
|
138
|
+
if (params.multiSelect && event.shiftKey && canToggleItemSelection(nextItem)) {
|
|
152
139
|
instance.selectRange(event, {
|
|
153
|
-
end:
|
|
154
|
-
current:
|
|
140
|
+
end: nextItem,
|
|
141
|
+
current: itemId
|
|
155
142
|
}, true);
|
|
156
143
|
}
|
|
157
144
|
}
|
|
158
145
|
break;
|
|
159
146
|
}
|
|
160
147
|
|
|
161
|
-
// Focuses the previous focusable
|
|
148
|
+
// Focuses the previous focusable item
|
|
162
149
|
case key === 'ArrowUp':
|
|
163
150
|
{
|
|
164
|
-
const
|
|
165
|
-
if (
|
|
151
|
+
const previousItem = getPreviousItem(instance, itemId);
|
|
152
|
+
if (previousItem) {
|
|
166
153
|
event.preventDefault();
|
|
167
|
-
instance.focusItem(event,
|
|
154
|
+
instance.focusItem(event, previousItem);
|
|
168
155
|
|
|
169
156
|
// Multi select behavior when pressing Shift + ArrowUp
|
|
170
|
-
// Toggles the selection state of the previous
|
|
171
|
-
if (params.multiSelect && event.shiftKey &&
|
|
157
|
+
// Toggles the selection state of the previous item
|
|
158
|
+
if (params.multiSelect && event.shiftKey && canToggleItemSelection(previousItem)) {
|
|
172
159
|
instance.selectRange(event, {
|
|
173
|
-
end:
|
|
174
|
-
current:
|
|
160
|
+
end: previousItem,
|
|
161
|
+
current: itemId
|
|
175
162
|
}, true);
|
|
176
163
|
}
|
|
177
164
|
}
|
|
178
165
|
break;
|
|
179
166
|
}
|
|
180
167
|
|
|
181
|
-
// If the focused
|
|
182
|
-
// If the focused
|
|
168
|
+
// If the focused item is expanded, we move the focus to its first child
|
|
169
|
+
// If the focused item is collapsed and has children, we expand it
|
|
183
170
|
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
184
171
|
{
|
|
185
|
-
if (instance.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
172
|
+
if (instance.isItemExpanded(itemId)) {
|
|
173
|
+
const nextItemId = getNextItem(instance, itemId);
|
|
174
|
+
if (nextItemId) {
|
|
175
|
+
instance.focusItem(event, nextItemId);
|
|
176
|
+
event.preventDefault();
|
|
177
|
+
}
|
|
178
|
+
} else if (canToggleItemExpansion(itemId)) {
|
|
179
|
+
instance.toggleItemExpansion(event, itemId);
|
|
190
180
|
event.preventDefault();
|
|
191
181
|
}
|
|
192
182
|
break;
|
|
193
183
|
}
|
|
194
184
|
|
|
195
|
-
// If the focused
|
|
196
|
-
// If the focused
|
|
185
|
+
// If the focused item is expanded, we collapse it
|
|
186
|
+
// If the focused item is collapsed and has a parent, we move the focus to this parent
|
|
197
187
|
case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
|
|
198
188
|
{
|
|
199
|
-
if (
|
|
200
|
-
instance.
|
|
189
|
+
if (canToggleItemExpansion(itemId) && instance.isItemExpanded(itemId)) {
|
|
190
|
+
instance.toggleItemExpansion(event, itemId);
|
|
201
191
|
event.preventDefault();
|
|
202
192
|
} else {
|
|
203
|
-
const parent = instance.getNode(
|
|
193
|
+
const parent = instance.getNode(itemId).parentId;
|
|
204
194
|
if (parent) {
|
|
205
195
|
instance.focusItem(event, parent);
|
|
206
196
|
event.preventDefault();
|
|
@@ -209,49 +199,49 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
209
199
|
break;
|
|
210
200
|
}
|
|
211
201
|
|
|
212
|
-
// Focuses the first
|
|
202
|
+
// Focuses the first item in the tree
|
|
213
203
|
case key === 'Home':
|
|
214
204
|
{
|
|
215
|
-
instance.focusItem(event,
|
|
205
|
+
instance.focusItem(event, getFirstItem(instance));
|
|
216
206
|
|
|
217
207
|
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
218
|
-
// Selects the focused
|
|
219
|
-
if (
|
|
220
|
-
instance.rangeSelectToFirst(event,
|
|
208
|
+
// Selects the focused item and all items up to the first item.
|
|
209
|
+
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
210
|
+
instance.rangeSelectToFirst(event, itemId);
|
|
221
211
|
}
|
|
222
212
|
event.preventDefault();
|
|
223
213
|
break;
|
|
224
214
|
}
|
|
225
215
|
|
|
226
|
-
// Focuses the last
|
|
216
|
+
// Focuses the last item in the tree
|
|
227
217
|
case key === 'End':
|
|
228
218
|
{
|
|
229
|
-
instance.focusItem(event,
|
|
219
|
+
instance.focusItem(event, getLastItem(instance));
|
|
230
220
|
|
|
231
221
|
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
232
|
-
// Selects the focused
|
|
233
|
-
if (
|
|
234
|
-
instance.rangeSelectToLast(event,
|
|
222
|
+
// Selects the focused item and all the items down to the last item.
|
|
223
|
+
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
224
|
+
instance.rangeSelectToLast(event, itemId);
|
|
235
225
|
}
|
|
236
226
|
event.preventDefault();
|
|
237
227
|
break;
|
|
238
228
|
}
|
|
239
229
|
|
|
240
|
-
// Expand all siblings that are at the same level as the focused
|
|
230
|
+
// Expand all siblings that are at the same level as the focused item
|
|
241
231
|
case key === '*':
|
|
242
232
|
{
|
|
243
|
-
instance.expandAllSiblings(event,
|
|
233
|
+
instance.expandAllSiblings(event, itemId);
|
|
244
234
|
event.preventDefault();
|
|
245
235
|
break;
|
|
246
236
|
}
|
|
247
237
|
|
|
248
238
|
// Multi select behavior when pressing Ctrl + a
|
|
249
|
-
// Selects all the
|
|
239
|
+
// Selects all the items
|
|
250
240
|
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
251
241
|
{
|
|
252
242
|
instance.selectRange(event, {
|
|
253
|
-
start:
|
|
254
|
-
end:
|
|
243
|
+
start: getFirstItem(instance),
|
|
244
|
+
end: getLastItem(instance)
|
|
255
245
|
});
|
|
256
246
|
event.preventDefault();
|
|
257
247
|
break;
|
|
@@ -261,19 +251,18 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
261
251
|
// TODO: Support typing multiple characters
|
|
262
252
|
case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
|
|
263
253
|
{
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
266
|
-
instance.focusItem(event,
|
|
254
|
+
const matchingItem = getFirstMatchingItem(itemId, key);
|
|
255
|
+
if (matchingItem != null) {
|
|
256
|
+
instance.focusItem(event, matchingItem);
|
|
267
257
|
event.preventDefault();
|
|
268
258
|
}
|
|
269
259
|
break;
|
|
270
260
|
}
|
|
271
261
|
}
|
|
272
262
|
};
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
};
|
|
263
|
+
populateInstance(instance, {
|
|
264
|
+
updateFirstCharMap,
|
|
265
|
+
handleItemKeyDown
|
|
266
|
+
});
|
|
278
267
|
};
|
|
279
268
|
useTreeViewKeyboardNavigation.params = {};
|
package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
1
2
|
import { TreeViewPluginSignature } from '../../models';
|
|
2
|
-
import {
|
|
3
|
+
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
3
4
|
import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
4
5
|
import { UseTreeViewFocusSignature } from '../useTreeViewFocus';
|
|
5
6
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
7
|
+
import { MuiCancellableEvent } from '../../models/MuiCancellableEvent';
|
|
6
8
|
export interface UseTreeViewKeyboardNavigationInstance {
|
|
7
9
|
updateFirstCharMap: (updater: (map: TreeViewFirstCharMap) => TreeViewFirstCharMap) => void;
|
|
10
|
+
handleItemKeyDown: (event: React.KeyboardEvent<HTMLElement> & MuiCancellableEvent, itemId: string) => void;
|
|
8
11
|
}
|
|
9
12
|
export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
10
13
|
instance: UseTreeViewKeyboardNavigationInstance;
|
|
11
14
|
dependantPlugins: [
|
|
12
|
-
|
|
15
|
+
UseTreeViewItemsSignature,
|
|
13
16
|
UseTreeViewSelectionSignature,
|
|
14
17
|
UseTreeViewFocusSignature,
|
|
15
18
|
UseTreeViewExpansionSignature
|
|
16
19
|
];
|
|
17
20
|
}>;
|
|
18
21
|
export type TreeViewFirstCharMap = {
|
|
19
|
-
[
|
|
22
|
+
[itemId: string]: string;
|
|
20
23
|
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { populateInstance,
|
|
3
|
+
import { populateInstance, getNextItem, getFirstItem, getLastItem } from '../../useTreeView/useTreeView.utils';
|
|
4
4
|
import { findOrderInTremauxTree } from './useTreeViewSelection.utils';
|
|
5
5
|
export const useTreeViewSelection = ({
|
|
6
6
|
instance,
|
|
7
7
|
params,
|
|
8
8
|
models
|
|
9
9
|
}) => {
|
|
10
|
-
const
|
|
10
|
+
const lastSelectedItem = React.useRef(null);
|
|
11
11
|
const lastSelectionWasRange = React.useRef(false);
|
|
12
12
|
const currentRangeSelection = React.useRef([]);
|
|
13
13
|
const setSelectedItems = (event, newSelectedItems) => {
|
|
14
14
|
if (params.onItemSelectionToggle) {
|
|
15
15
|
if (params.multiSelect) {
|
|
16
|
-
const addedItems = newSelectedItems.filter(itemId => !instance.
|
|
16
|
+
const addedItems = newSelectedItems.filter(itemId => !instance.isItemSelected(itemId));
|
|
17
17
|
const removedItems = models.selectedItems.value.filter(itemId => !newSelectedItems.includes(itemId));
|
|
18
18
|
addedItems.forEach(itemId => {
|
|
19
19
|
params.onItemSelectionToggle(event, itemId, true);
|
|
@@ -35,46 +35,46 @@ export const useTreeViewSelection = ({
|
|
|
35
35
|
}
|
|
36
36
|
models.selectedItems.setControlledValue(newSelectedItems);
|
|
37
37
|
};
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const isItemSelected = itemId => Array.isArray(models.selectedItems.value) ? models.selectedItems.value.indexOf(itemId) !== -1 : models.selectedItems.value === itemId;
|
|
39
|
+
const selectItem = (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
|
-
|
|
57
|
+
lastSelectedItem.current = itemId;
|
|
58
58
|
lastSelectionWasRange.current = false;
|
|
59
59
|
currentRangeSelection.current = [];
|
|
60
60
|
};
|
|
61
|
-
const
|
|
62
|
-
const [first, last] = findOrderInTremauxTree(instance,
|
|
63
|
-
const
|
|
61
|
+
const getItemsInRange = (itemAId, itemBId) => {
|
|
62
|
+
const [first, last] = findOrderInTremauxTree(instance, itemAId, itemBId);
|
|
63
|
+
const items = [first];
|
|
64
64
|
let current = first;
|
|
65
65
|
while (current !== last) {
|
|
66
|
-
current =
|
|
67
|
-
|
|
66
|
+
current = getNextItem(instance, current);
|
|
67
|
+
items.push(current);
|
|
68
68
|
}
|
|
69
|
-
return
|
|
69
|
+
return items;
|
|
70
70
|
};
|
|
71
|
-
const handleRangeArrowSelect = (event,
|
|
71
|
+
const handleRangeArrowSelect = (event, items) => {
|
|
72
72
|
let base = models.selectedItems.value.slice();
|
|
73
73
|
const {
|
|
74
74
|
start,
|
|
75
75
|
next,
|
|
76
76
|
current
|
|
77
|
-
} =
|
|
77
|
+
} = items;
|
|
78
78
|
if (!next || !current) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
@@ -95,32 +95,32 @@ export const useTreeViewSelection = ({
|
|
|
95
95
|
}
|
|
96
96
|
setSelectedItems(event, base);
|
|
97
97
|
};
|
|
98
|
-
const handleRangeSelect = (event,
|
|
98
|
+
const handleRangeSelect = (event, items) => {
|
|
99
99
|
let base = models.selectedItems.value.slice();
|
|
100
100
|
const {
|
|
101
101
|
start,
|
|
102
102
|
end
|
|
103
|
-
} =
|
|
104
|
-
// If last selection was a range selection ignore
|
|
103
|
+
} = items;
|
|
104
|
+
// If last selection was a range selection ignore items that were selected.
|
|
105
105
|
if (lastSelectionWasRange.current) {
|
|
106
106
|
base = base.filter(id => currentRangeSelection.current.indexOf(id) === -1);
|
|
107
107
|
}
|
|
108
|
-
let range =
|
|
109
|
-
range = range.filter(
|
|
108
|
+
let range = getItemsInRange(start, end);
|
|
109
|
+
range = range.filter(item => !instance.isItemDisabled(item));
|
|
110
110
|
currentRangeSelection.current = range;
|
|
111
111
|
let newSelected = base.concat(range);
|
|
112
112
|
newSelected = newSelected.filter((id, i) => newSelected.indexOf(id) === i);
|
|
113
113
|
setSelectedItems(event, newSelected);
|
|
114
114
|
};
|
|
115
|
-
const selectRange = (event,
|
|
115
|
+
const selectRange = (event, items, stacked = false) => {
|
|
116
116
|
if (params.disableSelection) {
|
|
117
117
|
return;
|
|
118
118
|
}
|
|
119
119
|
const {
|
|
120
|
-
start =
|
|
120
|
+
start = lastSelectedItem.current,
|
|
121
121
|
end,
|
|
122
122
|
current
|
|
123
|
-
} =
|
|
123
|
+
} = items;
|
|
124
124
|
if (stacked) {
|
|
125
125
|
handleRangeArrowSelect(event, {
|
|
126
126
|
start,
|
|
@@ -135,29 +135,29 @@ export const useTreeViewSelection = ({
|
|
|
135
135
|
}
|
|
136
136
|
lastSelectionWasRange.current = true;
|
|
137
137
|
};
|
|
138
|
-
const rangeSelectToFirst = (event,
|
|
139
|
-
if (!
|
|
140
|
-
|
|
138
|
+
const rangeSelectToFirst = (event, itemId) => {
|
|
139
|
+
if (!lastSelectedItem.current) {
|
|
140
|
+
lastSelectedItem.current = itemId;
|
|
141
141
|
}
|
|
142
|
-
const start = lastSelectionWasRange.current ?
|
|
142
|
+
const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
|
|
143
143
|
instance.selectRange(event, {
|
|
144
144
|
start,
|
|
145
|
-
end:
|
|
145
|
+
end: getFirstItem(instance)
|
|
146
146
|
});
|
|
147
147
|
};
|
|
148
|
-
const rangeSelectToLast = (event,
|
|
149
|
-
if (!
|
|
150
|
-
|
|
148
|
+
const rangeSelectToLast = (event, itemId) => {
|
|
149
|
+
if (!lastSelectedItem.current) {
|
|
150
|
+
lastSelectedItem.current = itemId;
|
|
151
151
|
}
|
|
152
|
-
const start = lastSelectionWasRange.current ?
|
|
152
|
+
const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
|
|
153
153
|
instance.selectRange(event, {
|
|
154
154
|
start,
|
|
155
|
-
end:
|
|
155
|
+
end: getLastItem(instance)
|
|
156
156
|
});
|
|
157
157
|
};
|
|
158
158
|
populateInstance(instance, {
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
isItemSelected,
|
|
160
|
+
selectItem,
|
|
161
161
|
selectRange,
|
|
162
162
|
rangeSelectToLast,
|
|
163
163
|
rangeSelectToFirst
|
|
@@ -178,15 +178,12 @@ useTreeViewSelection.models = {
|
|
|
178
178
|
getDefaultValue: params => params.defaultSelectedItems
|
|
179
179
|
}
|
|
180
180
|
};
|
|
181
|
-
const
|
|
182
|
-
useTreeViewSelection.getDefaultizedParams = params => {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
defaultSelectedItems: (_params$defaultSelect = params.defaultSelectedItems) != null ? _params$defaultSelect : params.multiSelect ? DEFAULT_SELECTED_NODES : null
|
|
188
|
-
});
|
|
189
|
-
};
|
|
181
|
+
const DEFAULT_SELECTED_ITEMS = [];
|
|
182
|
+
useTreeViewSelection.getDefaultizedParams = params => _extends({}, params, {
|
|
183
|
+
disableSelection: params.disableSelection ?? false,
|
|
184
|
+
multiSelect: params.multiSelect ?? false,
|
|
185
|
+
defaultSelectedItems: params.defaultSelectedItems ?? (params.multiSelect ? DEFAULT_SELECTED_ITEMS : null)
|
|
186
|
+
});
|
|
190
187
|
useTreeViewSelection.params = {
|
|
191
188
|
disableSelection: true,
|
|
192
189
|
multiSelect: true,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import type { DefaultizedProps, TreeViewItemRange, TreeViewPluginSignature } from '../../models';
|
|
3
|
-
import {
|
|
3
|
+
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
4
4
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
5
5
|
export interface UseTreeViewSelectionInstance {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
selectRange: (event: React.SyntheticEvent,
|
|
9
|
-
rangeSelectToFirst: (event: React.KeyboardEvent
|
|
10
|
-
rangeSelectToLast: (event: React.KeyboardEvent
|
|
6
|
+
isItemSelected: (itemId: string) => boolean;
|
|
7
|
+
selectItem: (event: React.SyntheticEvent, itemId: string, multiple?: boolean) => void;
|
|
8
|
+
selectRange: (event: React.SyntheticEvent, items: TreeViewItemRange, stacked?: boolean) => void;
|
|
9
|
+
rangeSelectToFirst: (event: React.KeyboardEvent, itemId: string) => void;
|
|
10
|
+
rangeSelectToLast: (event: React.KeyboardEvent, itemId: string) => void;
|
|
11
11
|
}
|
|
12
12
|
type TreeViewSelectionValue<Multiple extends boolean | undefined> = Multiple extends true ? string[] : string | null;
|
|
13
13
|
export interface UseTreeViewSelectionParameters<Multiple extends boolean | undefined> {
|
|
@@ -58,9 +58,9 @@ export type UseTreeViewSelectionSignature = TreeViewPluginSignature<{
|
|
|
58
58
|
contextValue: UseTreeViewSelectionContextValue;
|
|
59
59
|
modelNames: 'selectedItems';
|
|
60
60
|
dependantPlugins: [
|
|
61
|
-
|
|
61
|
+
UseTreeViewItemsSignature,
|
|
62
62
|
UseTreeViewExpansionSignature,
|
|
63
|
-
|
|
63
|
+
UseTreeViewItemsSignature
|
|
64
64
|
];
|
|
65
65
|
}>;
|
|
66
66
|
export {};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { TreeViewInstance } from '../../models';
|
|
2
|
-
import {
|
|
2
|
+
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
3
3
|
/**
|
|
4
4
|
* This is used to determine the start and end of a selection range so
|
|
5
|
-
* we can get the
|
|
5
|
+
* we can get the items between the two border items.
|
|
6
6
|
*
|
|
7
|
-
* It finds the
|
|
7
|
+
* It finds the items' common ancestor using
|
|
8
8
|
* a naive implementation of a lowest common ancestor algorithm
|
|
9
9
|
* (https://en.wikipedia.org/wiki/Lowest_common_ancestor).
|
|
10
|
-
* Then compares the ancestor's 2 children that are ancestors of
|
|
11
|
-
* so we can compare their indexes to work out which
|
|
10
|
+
* Then compares the ancestor's 2 children that are ancestors of itemA and ItemB
|
|
11
|
+
* so we can compare their indexes to work out which item comes first in a depth first search.
|
|
12
12
|
* (https://en.wikipedia.org/wiki/Depth-first_search)
|
|
13
13
|
*
|
|
14
|
-
* Another way to put it is which
|
|
14
|
+
* Another way to put it is which item is shallower in a trémaux tree
|
|
15
15
|
* https://en.wikipedia.org/wiki/Tr%C3%A9maux_tree
|
|
16
16
|
*/
|
|
17
|
-
export declare const findOrderInTremauxTree: (instance: TreeViewInstance<[
|
|
17
|
+
export declare const findOrderInTremauxTree: (instance: TreeViewInstance<[UseTreeViewItemsSignature]>, nodeAId: string, nodeBId: string) => string[];
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This is used to determine the start and end of a selection range so
|
|
3
|
-
* we can get the
|
|
3
|
+
* we can get the items between the two border items.
|
|
4
4
|
*
|
|
5
|
-
* It finds the
|
|
5
|
+
* It finds the items' common ancestor using
|
|
6
6
|
* a naive implementation of a lowest common ancestor algorithm
|
|
7
7
|
* (https://en.wikipedia.org/wiki/Lowest_common_ancestor).
|
|
8
|
-
* Then compares the ancestor's 2 children that are ancestors of
|
|
9
|
-
* so we can compare their indexes to work out which
|
|
8
|
+
* Then compares the ancestor's 2 children that are ancestors of itemA and ItemB
|
|
9
|
+
* so we can compare their indexes to work out which item comes first in a depth first search.
|
|
10
10
|
* (https://en.wikipedia.org/wiki/Depth-first_search)
|
|
11
11
|
*
|
|
12
|
-
* Another way to put it is which
|
|
12
|
+
* Another way to put it is which item is shallower in a trémaux tree
|
|
13
13
|
* https://en.wikipedia.org/wiki/Tr%C3%A9maux_tree
|
|
14
14
|
*/
|
|
15
15
|
export const findOrderInTremauxTree = (instance, nodeAId, nodeBId) => {
|