@mui/x-tree-view 7.0.0 → 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 +71 -0
- package/RichTreeView/RichTreeView.js +4 -4
- package/SimpleTreeView/SimpleTreeView.plugins.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/TreeItem/TreeItem.js +4 -4
- package/TreeItem/treeItemClasses.d.ts +1 -1
- package/TreeItem/useTreeItemState.js +9 -9
- package/TreeItem2Icon/TreeItem2Icon.types.d.ts +4 -4
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- 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/index.d.ts +2 -2
- package/internals/plugins/defaultPlugins.d.ts +3 -3
- package/internals/plugins/defaultPlugins.js +2 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +9 -9
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +5 -5
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +6 -6
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +6 -6
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
- 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/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +42 -32
- package/internals/plugins/{useTreeViewNodes/useTreeViewNodes.types.d.ts → useTreeViewItems/useTreeViewItems.types.d.ts} +32 -21
- 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} +26 -25
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +18 -0
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +40 -44
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +2 -2
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +34 -34
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +6 -6
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +7 -7
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/internals/useTreeView/useTreeView.utils.d.ts +5 -5
- package/internals/useTreeView/useTreeView.utils.js +15 -15
- package/modern/RichTreeView/RichTreeView.js +4 -4
- package/modern/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/modern/TreeItem/TreeItem.js +4 -4
- package/modern/TreeItem/useTreeItemState.js +9 -9
- package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- 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 +9 -9
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/modern/internals/plugins/useTreeViewItems/index.js +1 -0
- package/modern/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +42 -32
- package/modern/internals/plugins/useTreeViewJSXItems/index.js +1 -0
- package/{internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js → modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js} +26 -25
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +40 -44
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +34 -34
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/modern/internals/useTreeView/useTreeView.utils.js +15 -15
- package/node/RichTreeView/RichTreeView.js +4 -4
- package/node/SimpleTreeView/SimpleTreeView.plugins.js +2 -2
- package/node/TreeItem/TreeItem.js +4 -4
- package/node/TreeItem/useTreeItemState.js +9 -9
- package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +8 -8
- 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 +9 -9
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +35 -33
- package/node/internals/plugins/useTreeViewItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewNodes/useTreeViewNodes.js → useTreeViewItems/useTreeViewItems.js} +44 -34
- package/node/internals/plugins/useTreeViewJSXItems/index.js +12 -0
- package/node/internals/plugins/{useTreeViewJSXNodes/useTreeViewJSXNodes.js → useTreeViewJSXItems/useTreeViewJSXItems.js} +28 -27
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +39 -43
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +33 -33
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +5 -5
- package/node/internals/useTreeView/useTreeView.utils.js +20 -20
- package/package.json +1 -1
- package/useTreeItem2/useTreeItem2.d.ts +1 -1
- 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/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
|
@@ -8,7 +8,7 @@ import { getActiveElement } from '../../utils/utils';
|
|
|
8
8
|
const useTabbableItemId = (instance, selectedItems) => {
|
|
9
9
|
const isItemVisible = itemId => {
|
|
10
10
|
const node = instance.getNode(itemId);
|
|
11
|
-
return node && (node.parentId == null || instance.
|
|
11
|
+
return node && (node.parentId == null || instance.isItemExpanded(node.parentId));
|
|
12
12
|
};
|
|
13
13
|
let tabbableItemId;
|
|
14
14
|
if (Array.isArray(selectedItems)) {
|
|
@@ -32,18 +32,18 @@ export const useTreeViewFocus = ({
|
|
|
32
32
|
}) => {
|
|
33
33
|
const tabbableItemId = useTabbableItemId(instance, models.selectedItems.value);
|
|
34
34
|
const setFocusedItemId = useEventCallback(itemId => {
|
|
35
|
-
const cleanItemId = typeof itemId === 'function' ? itemId(state.
|
|
36
|
-
if (state.
|
|
35
|
+
const cleanItemId = typeof itemId === 'function' ? itemId(state.focusedItemId) : itemId;
|
|
36
|
+
if (state.focusedItemId !== cleanItemId) {
|
|
37
37
|
setState(prevState => _extends({}, prevState, {
|
|
38
|
-
|
|
38
|
+
focusedItemId: cleanItemId
|
|
39
39
|
}));
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
const isTreeViewFocused = React.useCallback(() => !!rootRef.current && rootRef.current.contains(getActiveElement(ownerDocument(rootRef.current))), [rootRef]);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
43
|
+
const isItemFocused = React.useCallback(itemId => state.focusedItemId === itemId && isTreeViewFocused(), [state.focusedItemId, isTreeViewFocused]);
|
|
44
|
+
const isItemVisible = itemId => {
|
|
45
45
|
const node = instance.getNode(itemId);
|
|
46
|
-
return node && (node.parentId == null || instance.
|
|
46
|
+
return node && (node.parentId == null || instance.isItemExpanded(node.parentId));
|
|
47
47
|
};
|
|
48
48
|
const innerFocusItem = (event, itemId) => {
|
|
49
49
|
const node = instance.getNode(itemId);
|
|
@@ -56,62 +56,64 @@ export const useTreeViewFocus = ({
|
|
|
56
56
|
params.onItemFocus(event, itemId);
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
|
-
const focusItem = useEventCallback((event,
|
|
60
|
-
// If we receive
|
|
61
|
-
if (
|
|
62
|
-
innerFocusItem(event,
|
|
59
|
+
const focusItem = useEventCallback((event, itemId) => {
|
|
60
|
+
// If we receive an itemId, and it is visible, the focus will be set to it
|
|
61
|
+
if (isItemVisible(itemId)) {
|
|
62
|
+
innerFocusItem(event, itemId);
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
|
-
const
|
|
66
|
-
let
|
|
65
|
+
const focusDefaultItem = useEventCallback(event => {
|
|
66
|
+
let itemToFocusId;
|
|
67
67
|
if (Array.isArray(models.selectedItems.value)) {
|
|
68
|
-
|
|
69
|
-
} else if (models.selectedItems.value != null &&
|
|
70
|
-
|
|
68
|
+
itemToFocusId = models.selectedItems.value.find(isItemVisible);
|
|
69
|
+
} else if (models.selectedItems.value != null && isItemVisible(models.selectedItems.value)) {
|
|
70
|
+
itemToFocusId = models.selectedItems.value;
|
|
71
71
|
}
|
|
72
|
-
if (
|
|
73
|
-
|
|
72
|
+
if (itemToFocusId == null) {
|
|
73
|
+
itemToFocusId = instance.getNavigableChildrenIds(null)[0];
|
|
74
74
|
}
|
|
75
|
-
innerFocusItem(event,
|
|
75
|
+
innerFocusItem(event, itemToFocusId);
|
|
76
76
|
});
|
|
77
77
|
const removeFocusedItem = useEventCallback(() => {
|
|
78
|
-
if (state.
|
|
78
|
+
if (state.focusedItemId == null) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
|
-
const node = instance.getNode(state.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
itemElement
|
|
81
|
+
const node = instance.getNode(state.focusedItemId);
|
|
82
|
+
if (node) {
|
|
83
|
+
const itemElement = document.getElementById(instance.getTreeItemId(state.focusedItemId, node.idAttribute));
|
|
84
|
+
if (itemElement) {
|
|
85
|
+
itemElement.blur();
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
setFocusedItemId(null);
|
|
87
89
|
});
|
|
88
90
|
const canItemBeTabbed = itemId => itemId === tabbableItemId;
|
|
89
91
|
populateInstance(instance, {
|
|
90
|
-
|
|
92
|
+
isItemFocused,
|
|
91
93
|
canItemBeTabbed,
|
|
92
94
|
focusItem,
|
|
93
|
-
|
|
95
|
+
focusDefaultItem,
|
|
94
96
|
removeFocusedItem
|
|
95
97
|
});
|
|
96
98
|
populatePublicAPI(publicAPI, {
|
|
97
99
|
focusItem
|
|
98
100
|
});
|
|
99
|
-
useInstanceEventHandler(instance, '
|
|
101
|
+
useInstanceEventHandler(instance, 'removeItem', ({
|
|
100
102
|
id
|
|
101
103
|
}) => {
|
|
102
|
-
if (state.
|
|
103
|
-
instance.
|
|
104
|
+
if (state.focusedItemId === id) {
|
|
105
|
+
instance.focusDefaultItem(null);
|
|
104
106
|
}
|
|
105
107
|
});
|
|
106
108
|
const createHandleFocus = otherHandlers => event => {
|
|
107
109
|
otherHandlers.onFocus?.(event);
|
|
108
110
|
// if the event bubbled (which is React specific) we don't want to steal focus
|
|
109
111
|
if (event.target === event.currentTarget) {
|
|
110
|
-
instance.
|
|
112
|
+
instance.focusDefaultItem(event);
|
|
111
113
|
}
|
|
112
114
|
};
|
|
113
|
-
const
|
|
114
|
-
const activeDescendant =
|
|
115
|
+
const focusedItem = instance.getNode(state.focusedItemId);
|
|
116
|
+
const activeDescendant = focusedItem ? instance.getTreeItemId(focusedItem.id, focusedItem.idAttribute) : null;
|
|
115
117
|
return {
|
|
116
118
|
getRootProps: otherHandlers => ({
|
|
117
119
|
onFocus: createHandleFocus(otherHandlers),
|
|
@@ -120,7 +122,7 @@ export const useTreeViewFocus = ({
|
|
|
120
122
|
};
|
|
121
123
|
};
|
|
122
124
|
useTreeViewFocus.getInitialState = () => ({
|
|
123
|
-
|
|
125
|
+
focusedItemId: null
|
|
124
126
|
});
|
|
125
127
|
useTreeViewFocus.params = {
|
|
126
128
|
onItemFocus: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTreeViewItems } from './useTreeViewItems';
|
|
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
|
|
4
4
|
import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
5
|
-
const
|
|
5
|
+
const updateItemsState = ({
|
|
6
6
|
items,
|
|
7
7
|
isItemDisabled,
|
|
8
8
|
getItemLabel,
|
|
@@ -44,86 +44,96 @@ const updateNodesState = ({
|
|
|
44
44
|
itemMap
|
|
45
45
|
};
|
|
46
46
|
};
|
|
47
|
-
export const
|
|
47
|
+
export const useTreeViewItems = ({
|
|
48
48
|
instance,
|
|
49
49
|
publicAPI,
|
|
50
50
|
params,
|
|
51
51
|
state,
|
|
52
52
|
setState
|
|
53
53
|
}) => {
|
|
54
|
-
const getNode = React.useCallback(itemId => state.
|
|
55
|
-
const getItem = React.useCallback(itemId => state.
|
|
56
|
-
const
|
|
54
|
+
const getNode = React.useCallback(itemId => state.items.nodeMap[itemId], [state.items.nodeMap]);
|
|
55
|
+
const getItem = React.useCallback(itemId => state.items.itemMap[itemId], [state.items.itemMap]);
|
|
56
|
+
const isItemDisabled = React.useCallback(itemId => {
|
|
57
57
|
if (itemId == null) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
let
|
|
60
|
+
let node = instance.getNode(itemId);
|
|
61
61
|
|
|
62
|
-
// This can be called before the item has been added to the
|
|
63
|
-
if (!
|
|
62
|
+
// This can be called before the item has been added to the item map.
|
|
63
|
+
if (!node) {
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
|
-
if (
|
|
66
|
+
if (node.disabled) {
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
|
-
while (
|
|
70
|
-
|
|
71
|
-
if (
|
|
69
|
+
while (node.parentId != null) {
|
|
70
|
+
node = instance.getNode(node.parentId);
|
|
71
|
+
if (node.disabled) {
|
|
72
72
|
return true;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return false;
|
|
76
76
|
}, [instance]);
|
|
77
|
-
const getChildrenIds = React.useCallback(itemId => Object.values(state.
|
|
77
|
+
const getChildrenIds = React.useCallback(itemId => Object.values(state.items.nodeMap).filter(item => item.parentId === itemId).sort((a, b) => a.index - b.index).map(child => child.id), [state.items.nodeMap]);
|
|
78
78
|
const getNavigableChildrenIds = itemId => {
|
|
79
79
|
let childrenIds = instance.getChildrenIds(itemId);
|
|
80
80
|
if (!params.disabledItemsFocusable) {
|
|
81
|
-
childrenIds = childrenIds.filter(item => !instance.
|
|
81
|
+
childrenIds = childrenIds.filter(item => !instance.isItemDisabled(item));
|
|
82
82
|
}
|
|
83
83
|
return childrenIds;
|
|
84
84
|
};
|
|
85
|
+
const areItemUpdatesPreventedRef = React.useRef(false);
|
|
86
|
+
const preventItemUpdates = React.useCallback(() => {
|
|
87
|
+
areItemUpdatesPreventedRef.current = true;
|
|
88
|
+
}, []);
|
|
89
|
+
const areItemUpdatesPrevented = React.useCallback(() => areItemUpdatesPreventedRef.current, []);
|
|
85
90
|
React.useEffect(() => {
|
|
91
|
+
if (instance.areItemUpdatesPrevented()) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
86
94
|
setState(prevState => {
|
|
87
|
-
const newState =
|
|
95
|
+
const newState = updateItemsState({
|
|
88
96
|
items: params.items,
|
|
89
97
|
isItemDisabled: params.isItemDisabled,
|
|
90
98
|
getItemId: params.getItemId,
|
|
91
99
|
getItemLabel: params.getItemLabel
|
|
92
100
|
});
|
|
93
|
-
Object.values(prevState.
|
|
94
|
-
if (!newState.nodeMap[
|
|
95
|
-
publishTreeViewEvent(instance, '
|
|
96
|
-
id:
|
|
101
|
+
Object.values(prevState.items.nodeMap).forEach(item => {
|
|
102
|
+
if (!newState.nodeMap[item.id]) {
|
|
103
|
+
publishTreeViewEvent(instance, 'removeItem', {
|
|
104
|
+
id: item.id
|
|
97
105
|
});
|
|
98
106
|
}
|
|
99
107
|
});
|
|
100
108
|
return _extends({}, prevState, {
|
|
101
|
-
|
|
109
|
+
items: newState
|
|
102
110
|
});
|
|
103
111
|
});
|
|
104
112
|
}, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
|
|
105
|
-
const
|
|
113
|
+
const getItemsToRender = () => {
|
|
106
114
|
const getPropsFromItemId = ({
|
|
107
115
|
id,
|
|
108
116
|
children
|
|
109
117
|
}) => {
|
|
110
|
-
const
|
|
118
|
+
const item = state.items.nodeMap[id];
|
|
111
119
|
return {
|
|
112
|
-
label:
|
|
113
|
-
itemId:
|
|
114
|
-
id:
|
|
120
|
+
label: item.label,
|
|
121
|
+
itemId: item.id,
|
|
122
|
+
id: item.idAttribute,
|
|
115
123
|
children: children?.map(getPropsFromItemId)
|
|
116
124
|
};
|
|
117
125
|
};
|
|
118
|
-
return state.
|
|
126
|
+
return state.items.nodeTree.map(getPropsFromItemId);
|
|
119
127
|
};
|
|
120
128
|
populateInstance(instance, {
|
|
121
129
|
getNode,
|
|
122
130
|
getItem,
|
|
123
|
-
|
|
131
|
+
getItemsToRender,
|
|
124
132
|
getChildrenIds,
|
|
125
133
|
getNavigableChildrenIds,
|
|
126
|
-
|
|
134
|
+
isItemDisabled,
|
|
135
|
+
preventItemUpdates,
|
|
136
|
+
areItemUpdatesPrevented
|
|
127
137
|
});
|
|
128
138
|
populatePublicAPI(publicAPI, {
|
|
129
139
|
getItem
|
|
@@ -134,18 +144,18 @@ export const useTreeViewNodes = ({
|
|
|
134
144
|
}
|
|
135
145
|
};
|
|
136
146
|
};
|
|
137
|
-
|
|
138
|
-
|
|
147
|
+
useTreeViewItems.getInitialState = params => ({
|
|
148
|
+
items: updateItemsState({
|
|
139
149
|
items: params.items,
|
|
140
150
|
isItemDisabled: params.isItemDisabled,
|
|
141
151
|
getItemId: params.getItemId,
|
|
142
152
|
getItemLabel: params.getItemLabel
|
|
143
153
|
})
|
|
144
154
|
});
|
|
145
|
-
|
|
155
|
+
useTreeViewItems.getDefaultizedParams = params => _extends({}, params, {
|
|
146
156
|
disabledItemsFocusable: params.disabledItemsFocusable ?? false
|
|
147
157
|
});
|
|
148
|
-
|
|
158
|
+
useTreeViewItems.params = {
|
|
149
159
|
disabledItemsFocusable: true,
|
|
150
160
|
items: true,
|
|
151
161
|
isItemDisabled: true,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTreeViewJSXItems } from './useTreeViewJSXItems';
|
|
@@ -7,45 +7,46 @@ import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
|
|
|
7
7
|
import { useTreeViewContext } from '../../TreeViewProvider/useTreeViewContext';
|
|
8
8
|
import { DescendantProvider, useDescendant } from '../../TreeViewProvider/DescendantProvider';
|
|
9
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
-
export const
|
|
10
|
+
export const useTreeViewJSXItems = ({
|
|
11
11
|
instance,
|
|
12
12
|
setState
|
|
13
13
|
}) => {
|
|
14
|
-
|
|
14
|
+
instance.preventItemUpdates();
|
|
15
|
+
const insertJSXItem = useEventCallback(item => {
|
|
15
16
|
setState(prevState => {
|
|
16
|
-
if (prevState.
|
|
17
|
-
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', `Tow items were provided with the same id in the \`items\` prop: "${
|
|
17
|
+
if (prevState.items.nodeMap[item.id] != null) {
|
|
18
|
+
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', `Tow items were provided with the same id in the \`items\` prop: "${item.id}"`].join('\n'));
|
|
18
19
|
}
|
|
19
20
|
return _extends({}, prevState, {
|
|
20
|
-
|
|
21
|
-
nodeMap: _extends({}, prevState.
|
|
22
|
-
[
|
|
21
|
+
items: _extends({}, prevState.items, {
|
|
22
|
+
nodeMap: _extends({}, prevState.items.nodeMap, {
|
|
23
|
+
[item.id]: item
|
|
23
24
|
}),
|
|
24
25
|
// For `SimpleTreeView`, we don't have a proper `item` object, so we create a very basic one.
|
|
25
|
-
itemMap: _extends({}, prevState.
|
|
26
|
-
[
|
|
27
|
-
id:
|
|
28
|
-
label:
|
|
26
|
+
itemMap: _extends({}, prevState.items.itemMap, {
|
|
27
|
+
[item.id]: {
|
|
28
|
+
id: item.id,
|
|
29
|
+
label: item.label
|
|
29
30
|
}
|
|
30
31
|
})
|
|
31
32
|
})
|
|
32
33
|
});
|
|
33
34
|
});
|
|
34
35
|
});
|
|
35
|
-
const
|
|
36
|
+
const removeJSXItem = useEventCallback(itemId => {
|
|
36
37
|
setState(prevState => {
|
|
37
|
-
const newNodeMap = _extends({}, prevState.
|
|
38
|
-
const newItemMap = _extends({}, prevState.
|
|
38
|
+
const newNodeMap = _extends({}, prevState.items.nodeMap);
|
|
39
|
+
const newItemMap = _extends({}, prevState.items.itemMap);
|
|
39
40
|
delete newNodeMap[itemId];
|
|
40
41
|
delete newItemMap[itemId];
|
|
41
42
|
return _extends({}, prevState, {
|
|
42
|
-
|
|
43
|
+
items: _extends({}, prevState.items, {
|
|
43
44
|
nodeMap: newNodeMap,
|
|
44
45
|
itemMap: newItemMap
|
|
45
46
|
})
|
|
46
47
|
});
|
|
47
48
|
});
|
|
48
|
-
publishTreeViewEvent(instance, '
|
|
49
|
+
publishTreeViewEvent(instance, 'removeItem', {
|
|
49
50
|
id: itemId
|
|
50
51
|
});
|
|
51
52
|
});
|
|
@@ -63,12 +64,12 @@ export const useTreeViewJSXNodes = ({
|
|
|
63
64
|
};
|
|
64
65
|
});
|
|
65
66
|
populateInstance(instance, {
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
insertJSXItem,
|
|
68
|
+
removeJSXItem,
|
|
68
69
|
mapFirstCharFromJSX
|
|
69
70
|
});
|
|
70
71
|
};
|
|
71
|
-
const
|
|
72
|
+
const useTreeViewJSXItemsItemPlugin = ({
|
|
72
73
|
props,
|
|
73
74
|
rootRef,
|
|
74
75
|
contentRef
|
|
@@ -103,9 +104,9 @@ const useTreeViewJSXNodesItemPlugin = ({
|
|
|
103
104
|
parentId
|
|
104
105
|
} = useDescendant(descendant);
|
|
105
106
|
React.useEffect(() => {
|
|
106
|
-
// On the first render a
|
|
107
|
+
// On the first render a item's index will be -1. We want to wait for the real index.
|
|
107
108
|
if (index !== -1) {
|
|
108
|
-
instance.
|
|
109
|
+
instance.insertJSXItem({
|
|
109
110
|
id: itemId,
|
|
110
111
|
idAttribute: id,
|
|
111
112
|
index,
|
|
@@ -113,7 +114,7 @@ const useTreeViewJSXNodesItemPlugin = ({
|
|
|
113
114
|
expandable,
|
|
114
115
|
disabled
|
|
115
116
|
});
|
|
116
|
-
return () => instance.
|
|
117
|
+
return () => instance.removeJSXItem(itemId);
|
|
117
118
|
}
|
|
118
119
|
return undefined;
|
|
119
120
|
}, [instance, parentId, index, itemId, expandable, disabled, id]);
|
|
@@ -128,12 +129,12 @@ const useTreeViewJSXNodesItemPlugin = ({
|
|
|
128
129
|
rootRef: handleRootRef
|
|
129
130
|
};
|
|
130
131
|
};
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
useTreeViewJSXItems.itemPlugin = useTreeViewJSXItemsItemPlugin;
|
|
133
|
+
useTreeViewJSXItems.wrapItem = ({
|
|
133
134
|
children,
|
|
134
135
|
itemId
|
|
135
136
|
}) => /*#__PURE__*/_jsx(DescendantProvider, {
|
|
136
137
|
id: itemId,
|
|
137
138
|
children: children
|
|
138
139
|
});
|
|
139
|
-
|
|
140
|
+
useTreeViewJSXItems.params = {};
|
package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -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
|
}
|
|
@@ -15,30 +15,26 @@ function findNextFirstChar(firstChars, startIndex, char) {
|
|
|
15
15
|
}
|
|
16
16
|
export const useTreeViewKeyboardNavigation = ({
|
|
17
17
|
instance,
|
|
18
|
-
params
|
|
18
|
+
params,
|
|
19
|
+
state
|
|
19
20
|
}) => {
|
|
20
21
|
const theme = useTheme();
|
|
21
22
|
const isRTL = theme.direction === 'rtl';
|
|
22
23
|
const firstCharMap = React.useRef({});
|
|
23
|
-
const hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
|
|
24
24
|
const updateFirstCharMap = useEventCallback(callback => {
|
|
25
|
-
hasFirstCharMapBeenUpdatedImperatively.current = true;
|
|
26
25
|
firstCharMap.current = callback(firstCharMap.current);
|
|
27
26
|
});
|
|
28
27
|
React.useEffect(() => {
|
|
29
|
-
if (
|
|
28
|
+
if (instance.areItemUpdatesPrevented()) {
|
|
30
29
|
return;
|
|
31
30
|
}
|
|
32
31
|
const newFirstCharMap = {};
|
|
33
|
-
const processItem =
|
|
34
|
-
|
|
35
|
-
const itemId = getItemId ? getItemId(item) : item.id;
|
|
36
|
-
newFirstCharMap[itemId] = instance.getNode(itemId).label.substring(0, 1).toLowerCase();
|
|
37
|
-
item.children?.forEach(processItem);
|
|
32
|
+
const processItem = node => {
|
|
33
|
+
newFirstCharMap[node.id] = node.label.substring(0, 1).toLowerCase();
|
|
38
34
|
};
|
|
39
|
-
|
|
35
|
+
Object.values(state.items.nodeMap).forEach(processItem);
|
|
40
36
|
firstCharMap.current = newFirstCharMap;
|
|
41
|
-
}, [
|
|
37
|
+
}, [state.items.nodeMap, params.getItemId, instance]);
|
|
42
38
|
const getFirstMatchingItem = (itemId, firstChar) => {
|
|
43
39
|
let start;
|
|
44
40
|
let index;
|
|
@@ -48,8 +44,8 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
48
44
|
// This really only works since the ids are strings
|
|
49
45
|
Object.keys(firstCharMap.current).forEach(mapItemId => {
|
|
50
46
|
const map = instance.getNode(mapItemId);
|
|
51
|
-
const visible = map.parentId ? instance.
|
|
52
|
-
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.
|
|
47
|
+
const visible = map.parentId ? instance.isItemExpanded(map.parentId) : true;
|
|
48
|
+
const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isItemDisabled(mapItemId);
|
|
53
49
|
if (visible && !shouldBeSkipped) {
|
|
54
50
|
firstCharIds.push(mapItemId);
|
|
55
51
|
firstChars.push(firstCharMap.current[mapItemId]);
|
|
@@ -76,9 +72,9 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
76
72
|
}
|
|
77
73
|
return null;
|
|
78
74
|
};
|
|
79
|
-
const canToggleItemSelection = itemId => !params.disableSelection && !instance.
|
|
75
|
+
const canToggleItemSelection = itemId => !params.disableSelection && !instance.isItemDisabled(itemId);
|
|
80
76
|
const canToggleItemExpansion = itemId => {
|
|
81
|
-
return !instance.
|
|
77
|
+
return !instance.isItemDisabled(itemId) && instance.isItemExpandable(itemId);
|
|
82
78
|
};
|
|
83
79
|
|
|
84
80
|
// ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
|
|
@@ -94,7 +90,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
94
90
|
|
|
95
91
|
// eslint-disable-next-line default-case
|
|
96
92
|
switch (true) {
|
|
97
|
-
// Select the
|
|
93
|
+
// Select the item when pressing "Space"
|
|
98
94
|
case key === ' ' && canToggleItemSelection(itemId):
|
|
99
95
|
{
|
|
100
96
|
event.preventDefault();
|
|
@@ -103,26 +99,26 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
103
99
|
end: itemId
|
|
104
100
|
});
|
|
105
101
|
} else if (params.multiSelect) {
|
|
106
|
-
instance.
|
|
102
|
+
instance.selectItem(event, itemId, true);
|
|
107
103
|
} else {
|
|
108
|
-
instance.
|
|
104
|
+
instance.selectItem(event, itemId);
|
|
109
105
|
}
|
|
110
106
|
break;
|
|
111
107
|
}
|
|
112
108
|
|
|
113
|
-
// If the focused
|
|
114
|
-
// If the focused
|
|
109
|
+
// If the focused item has children, we expand it.
|
|
110
|
+
// If the focused item has no children, we select it.
|
|
115
111
|
case key === 'Enter':
|
|
116
112
|
{
|
|
117
113
|
if (canToggleItemExpansion(itemId)) {
|
|
118
|
-
instance.
|
|
114
|
+
instance.toggleItemExpansion(event, itemId);
|
|
119
115
|
event.preventDefault();
|
|
120
116
|
} else if (canToggleItemSelection(itemId)) {
|
|
121
117
|
if (params.multiSelect) {
|
|
122
118
|
event.preventDefault();
|
|
123
|
-
instance.
|
|
124
|
-
} else if (!instance.
|
|
125
|
-
instance.
|
|
119
|
+
instance.selectItem(event, itemId, true);
|
|
120
|
+
} else if (!instance.isItemSelected(itemId)) {
|
|
121
|
+
instance.selectItem(event, itemId);
|
|
126
122
|
event.preventDefault();
|
|
127
123
|
}
|
|
128
124
|
}
|
|
@@ -132,7 +128,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
132
128
|
// Focus the next focusable item
|
|
133
129
|
case key === 'ArrowDown':
|
|
134
130
|
{
|
|
135
|
-
const nextItem =
|
|
131
|
+
const nextItem = getNextItem(instance, itemId);
|
|
136
132
|
if (nextItem) {
|
|
137
133
|
event.preventDefault();
|
|
138
134
|
instance.focusItem(event, nextItem);
|
|
@@ -152,7 +148,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
152
148
|
// Focuses the previous focusable item
|
|
153
149
|
case key === 'ArrowUp':
|
|
154
150
|
{
|
|
155
|
-
const previousItem =
|
|
151
|
+
const previousItem = getPreviousItem(instance, itemId);
|
|
156
152
|
if (previousItem) {
|
|
157
153
|
event.preventDefault();
|
|
158
154
|
instance.focusItem(event, previousItem);
|
|
@@ -173,14 +169,14 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
173
169
|
// If the focused item is collapsed and has children, we expand it
|
|
174
170
|
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
175
171
|
{
|
|
176
|
-
if (instance.
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
instance.focusItem(event,
|
|
172
|
+
if (instance.isItemExpanded(itemId)) {
|
|
173
|
+
const nextItemId = getNextItem(instance, itemId);
|
|
174
|
+
if (nextItemId) {
|
|
175
|
+
instance.focusItem(event, nextItemId);
|
|
180
176
|
event.preventDefault();
|
|
181
177
|
}
|
|
182
178
|
} else if (canToggleItemExpansion(itemId)) {
|
|
183
|
-
instance.
|
|
179
|
+
instance.toggleItemExpansion(event, itemId);
|
|
184
180
|
event.preventDefault();
|
|
185
181
|
}
|
|
186
182
|
break;
|
|
@@ -190,8 +186,8 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
190
186
|
// If the focused item is collapsed and has a parent, we move the focus to this parent
|
|
191
187
|
case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
|
|
192
188
|
{
|
|
193
|
-
if (canToggleItemExpansion(itemId) && instance.
|
|
194
|
-
instance.
|
|
189
|
+
if (canToggleItemExpansion(itemId) && instance.isItemExpanded(itemId)) {
|
|
190
|
+
instance.toggleItemExpansion(event, itemId);
|
|
195
191
|
event.preventDefault();
|
|
196
192
|
} else {
|
|
197
193
|
const parent = instance.getNode(itemId).parentId;
|
|
@@ -203,13 +199,13 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
203
199
|
break;
|
|
204
200
|
}
|
|
205
201
|
|
|
206
|
-
// Focuses the first
|
|
202
|
+
// Focuses the first item in the tree
|
|
207
203
|
case key === 'Home':
|
|
208
204
|
{
|
|
209
|
-
instance.focusItem(event,
|
|
205
|
+
instance.focusItem(event, getFirstItem(instance));
|
|
210
206
|
|
|
211
207
|
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
212
|
-
// Selects the focused
|
|
208
|
+
// Selects the focused item and all items up to the first item.
|
|
213
209
|
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
214
210
|
instance.rangeSelectToFirst(event, itemId);
|
|
215
211
|
}
|
|
@@ -220,7 +216,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
220
216
|
// Focuses the last item in the tree
|
|
221
217
|
case key === 'End':
|
|
222
218
|
{
|
|
223
|
-
instance.focusItem(event,
|
|
219
|
+
instance.focusItem(event, getLastItem(instance));
|
|
224
220
|
|
|
225
221
|
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
226
222
|
// Selects the focused item and all the items down to the last item.
|
|
@@ -240,12 +236,12 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
240
236
|
}
|
|
241
237
|
|
|
242
238
|
// Multi select behavior when pressing Ctrl + a
|
|
243
|
-
// Selects all the
|
|
239
|
+
// Selects all the items
|
|
244
240
|
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
245
241
|
{
|
|
246
242
|
instance.selectRange(event, {
|
|
247
|
-
start:
|
|
248
|
-
end:
|
|
243
|
+
start: getFirstItem(instance),
|
|
244
|
+
end: getLastItem(instance)
|
|
249
245
|
});
|
|
250
246
|
event.preventDefault();
|
|
251
247
|
break;
|
|
@@ -255,9 +251,9 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
255
251
|
// TODO: Support typing multiple characters
|
|
256
252
|
case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
|
|
257
253
|
{
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
260
|
-
instance.focusItem(event,
|
|
254
|
+
const matchingItem = getFirstMatchingItem(itemId, key);
|
|
255
|
+
if (matchingItem != null) {
|
|
256
|
+
instance.focusItem(event, matchingItem);
|
|
261
257
|
event.preventDefault();
|
|
262
258
|
}
|
|
263
259
|
break;
|