@mui/x-tree-view 7.0.0-alpha.1 → 7.0.0-alpha.8

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.
Files changed (217) hide show
  1. package/CHANGELOG.md +1380 -188
  2. package/README.md +0 -1
  3. package/RichTreeView/RichTreeView.d.ts +20 -0
  4. package/RichTreeView/RichTreeView.js +285 -0
  5. package/RichTreeView/RichTreeView.types.d.ts +55 -0
  6. package/RichTreeView/RichTreeView.types.js +1 -0
  7. package/RichTreeView/index.d.ts +3 -0
  8. package/RichTreeView/index.js +3 -0
  9. package/RichTreeView/package.json +6 -0
  10. package/RichTreeView/richTreeViewClasses.d.ts +7 -0
  11. package/RichTreeView/richTreeViewClasses.js +6 -0
  12. package/SimpleTreeView/SimpleTreeView.d.ts +20 -0
  13. package/SimpleTreeView/SimpleTreeView.js +235 -0
  14. package/SimpleTreeView/SimpleTreeView.plugins.d.ts +6 -0
  15. package/SimpleTreeView/SimpleTreeView.plugins.js +5 -0
  16. package/SimpleTreeView/SimpleTreeView.types.d.ts +38 -0
  17. package/SimpleTreeView/SimpleTreeView.types.js +1 -0
  18. package/SimpleTreeView/index.d.ts +3 -0
  19. package/SimpleTreeView/index.js +3 -0
  20. package/SimpleTreeView/package.json +6 -0
  21. package/SimpleTreeView/simpleTreeViewClasses.d.ts +7 -0
  22. package/SimpleTreeView/simpleTreeViewClasses.js +6 -0
  23. package/TreeItem/TreeItem.js +44 -89
  24. package/TreeItem/TreeItem.types.d.ts +2 -1
  25. package/TreeItem/index.d.ts +2 -2
  26. package/TreeItem/index.js +2 -2
  27. package/TreeItem/useTreeItem.js +5 -5
  28. package/TreeView/TreeView.d.ts +4 -0
  29. package/TreeView/TreeView.js +80 -87
  30. package/TreeView/TreeView.types.d.ts +4 -26
  31. package/TreeView/index.d.ts +1 -1
  32. package/index.d.ts +3 -0
  33. package/index.js +5 -2
  34. package/internals/TreeViewProvider/TreeViewContext.d.ts +1 -2
  35. package/internals/TreeViewProvider/TreeViewContext.js +1 -14
  36. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +3 -3
  37. package/internals/TreeViewProvider/useTreeViewContext.js +7 -1
  38. package/internals/corePlugins/corePlugins.d.ts +1 -1
  39. package/internals/corePlugins/corePlugins.js +1 -1
  40. package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.d.ts +0 -5
  41. package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -7
  42. package/internals/hooks/useLazyRef.d.ts +2 -0
  43. package/internals/hooks/useLazyRef.js +11 -0
  44. package/internals/hooks/useOnMount.d.ts +2 -0
  45. package/internals/hooks/useOnMount.js +7 -0
  46. package/internals/hooks/useTimeout.d.ts +9 -0
  47. package/internals/hooks/useTimeout.js +28 -0
  48. package/internals/models/MuiCancellableEvent.d.ts +4 -0
  49. package/internals/models/MuiCancellableEvent.js +1 -0
  50. package/internals/models/plugin.d.ts +24 -0
  51. package/internals/models/treeView.d.ts +5 -1
  52. package/internals/plugins/defaultPlugins.d.ts +3 -2
  53. package/internals/plugins/defaultPlugins.js +2 -1
  54. package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +14 -6
  55. package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.d.ts +1 -6
  56. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +32 -17
  57. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +12 -5
  58. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +16 -6
  59. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +4 -1
  60. package/internals/plugins/useTreeViewId/index.d.ts +2 -0
  61. package/internals/plugins/useTreeViewId/index.js +1 -0
  62. package/internals/plugins/useTreeViewId/useTreeViewId.d.ts +3 -0
  63. package/internals/plugins/useTreeViewId/useTreeViewId.js +21 -0
  64. package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +17 -0
  65. package/internals/plugins/useTreeViewId/useTreeViewId.types.js +1 -0
  66. package/internals/plugins/useTreeViewJSXNodes/index.d.ts +2 -0
  67. package/internals/plugins/useTreeViewJSXNodes/index.js +1 -0
  68. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.d.ts +3 -0
  69. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +115 -0
  70. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +16 -0
  71. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.js +1 -0
  72. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +175 -121
  73. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +5 -2
  74. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +89 -17
  75. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +48 -5
  76. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.d.ts +1 -1
  77. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +49 -28
  78. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +15 -8
  79. package/internals/useTreeView/useTreeView.js +40 -3
  80. package/internals/useTreeView/useTreeView.types.d.ts +2 -1
  81. package/internals/useTreeView/useTreeViewModels.js +2 -2
  82. package/internals/utils/extractPluginParamsFromProps.d.ts +13 -0
  83. package/internals/utils/extractPluginParamsFromProps.js +27 -0
  84. package/internals/utils/warning.d.ts +1 -0
  85. package/internals/utils/warning.js +14 -0
  86. package/legacy/RichTreeView/RichTreeView.js +279 -0
  87. package/legacy/RichTreeView/RichTreeView.types.js +1 -0
  88. package/legacy/RichTreeView/index.js +3 -0
  89. package/legacy/RichTreeView/richTreeViewClasses.js +6 -0
  90. package/legacy/SimpleTreeView/SimpleTreeView.js +232 -0
  91. package/legacy/SimpleTreeView/SimpleTreeView.plugins.js +6 -0
  92. package/legacy/SimpleTreeView/SimpleTreeView.types.js +1 -0
  93. package/legacy/SimpleTreeView/index.js +3 -0
  94. package/legacy/SimpleTreeView/simpleTreeViewClasses.js +6 -0
  95. package/legacy/TreeItem/TreeItem.js +49 -103
  96. package/legacy/TreeItem/index.js +2 -2
  97. package/legacy/TreeItem/useTreeItem.js +5 -5
  98. package/legacy/TreeView/TreeView.js +80 -82
  99. package/legacy/index.js +5 -2
  100. package/legacy/internals/TreeViewProvider/TreeViewContext.js +1 -14
  101. package/legacy/internals/TreeViewProvider/useTreeViewContext.js +5 -1
  102. package/legacy/internals/corePlugins/corePlugins.js +1 -1
  103. package/legacy/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -7
  104. package/legacy/internals/hooks/useLazyRef.js +11 -0
  105. package/legacy/internals/hooks/useOnMount.js +7 -0
  106. package/legacy/internals/hooks/useTimeout.js +38 -0
  107. package/legacy/internals/models/MuiCancellableEvent.js +1 -0
  108. package/legacy/internals/plugins/defaultPlugins.js +2 -1
  109. package/legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +17 -8
  110. package/legacy/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +32 -17
  111. package/legacy/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +16 -6
  112. package/legacy/internals/plugins/useTreeViewId/index.js +1 -0
  113. package/legacy/internals/plugins/useTreeViewId/useTreeViewId.js +24 -0
  114. package/legacy/internals/plugins/useTreeViewId/useTreeViewId.types.js +1 -0
  115. package/legacy/internals/plugins/useTreeViewJSXNodes/index.js +1 -0
  116. package/legacy/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +121 -0
  117. package/legacy/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.js +1 -0
  118. package/legacy/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +177 -119
  119. package/legacy/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +96 -20
  120. package/legacy/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +53 -28
  121. package/legacy/internals/useTreeView/useTreeView.js +39 -3
  122. package/legacy/internals/useTreeView/useTreeViewModels.js +2 -2
  123. package/legacy/internals/utils/extractPluginParamsFromProps.js +27 -0
  124. package/legacy/internals/utils/warning.js +15 -0
  125. package/legacy/models/index.js +1 -0
  126. package/legacy/models/items.js +1 -0
  127. package/models/index.d.ts +1 -0
  128. package/models/index.js +1 -0
  129. package/models/items.d.ts +7 -0
  130. package/models/items.js +1 -0
  131. package/models/package.json +6 -0
  132. package/modern/RichTreeView/RichTreeView.js +283 -0
  133. package/modern/RichTreeView/RichTreeView.types.js +1 -0
  134. package/modern/RichTreeView/index.js +3 -0
  135. package/modern/RichTreeView/richTreeViewClasses.js +6 -0
  136. package/modern/SimpleTreeView/SimpleTreeView.js +234 -0
  137. package/modern/SimpleTreeView/SimpleTreeView.plugins.js +5 -0
  138. package/modern/SimpleTreeView/SimpleTreeView.types.js +1 -0
  139. package/modern/SimpleTreeView/index.js +3 -0
  140. package/modern/SimpleTreeView/simpleTreeViewClasses.js +6 -0
  141. package/modern/TreeItem/TreeItem.js +44 -88
  142. package/modern/TreeItem/index.js +2 -2
  143. package/modern/TreeItem/useTreeItem.js +5 -5
  144. package/modern/TreeView/TreeView.js +80 -87
  145. package/modern/index.js +5 -2
  146. package/modern/internals/TreeViewProvider/TreeViewContext.js +1 -14
  147. package/modern/internals/TreeViewProvider/useTreeViewContext.js +7 -1
  148. package/modern/internals/corePlugins/corePlugins.js +1 -1
  149. package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -7
  150. package/modern/internals/hooks/useLazyRef.js +11 -0
  151. package/modern/internals/hooks/useOnMount.js +7 -0
  152. package/modern/internals/hooks/useTimeout.js +28 -0
  153. package/modern/internals/models/MuiCancellableEvent.js +1 -0
  154. package/modern/internals/plugins/defaultPlugins.js +2 -1
  155. package/modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +14 -6
  156. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -18
  157. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +16 -7
  158. package/modern/internals/plugins/useTreeViewId/index.js +1 -0
  159. package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +21 -0
  160. package/modern/internals/plugins/useTreeViewId/useTreeViewId.types.js +1 -0
  161. package/modern/internals/plugins/useTreeViewJSXNodes/index.js +1 -0
  162. package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +114 -0
  163. package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.js +1 -0
  164. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +174 -121
  165. package/modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +88 -17
  166. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +50 -29
  167. package/modern/internals/useTreeView/useTreeView.js +40 -3
  168. package/modern/internals/useTreeView/useTreeViewModels.js +2 -2
  169. package/modern/internals/utils/extractPluginParamsFromProps.js +27 -0
  170. package/modern/internals/utils/warning.js +14 -0
  171. package/modern/models/index.js +1 -0
  172. package/modern/models/items.js +1 -0
  173. package/node/RichTreeView/RichTreeView.js +291 -0
  174. package/node/RichTreeView/RichTreeView.types.js +5 -0
  175. package/node/RichTreeView/index.js +27 -0
  176. package/node/RichTreeView/richTreeViewClasses.js +14 -0
  177. package/node/SimpleTreeView/SimpleTreeView.js +242 -0
  178. package/node/SimpleTreeView/SimpleTreeView.plugins.js +11 -0
  179. package/node/SimpleTreeView/SimpleTreeView.types.js +5 -0
  180. package/node/SimpleTreeView/index.js +27 -0
  181. package/node/SimpleTreeView/simpleTreeViewClasses.js +14 -0
  182. package/node/TreeItem/TreeItem.js +44 -88
  183. package/node/TreeItem/index.js +11 -15
  184. package/node/TreeItem/useTreeItem.js +5 -5
  185. package/node/TreeView/TreeView.js +80 -87
  186. package/node/index.js +38 -2
  187. package/node/internals/TreeViewProvider/TreeViewContext.js +2 -15
  188. package/node/internals/TreeViewProvider/useTreeViewContext.js +7 -1
  189. package/node/internals/corePlugins/corePlugins.js +1 -1
  190. package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -7
  191. package/node/internals/hooks/useLazyRef.js +19 -0
  192. package/node/internals/hooks/useOnMount.js +15 -0
  193. package/node/internals/hooks/useTimeout.js +34 -0
  194. package/node/internals/models/MuiCancellableEvent.js +5 -0
  195. package/node/internals/plugins/defaultPlugins.js +2 -1
  196. package/node/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +15 -8
  197. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -18
  198. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +16 -7
  199. package/node/internals/plugins/useTreeViewId/index.js +12 -0
  200. package/node/internals/plugins/useTreeViewId/useTreeViewId.js +31 -0
  201. package/node/internals/plugins/useTreeViewId/useTreeViewId.types.js +5 -0
  202. package/node/internals/plugins/useTreeViewJSXNodes/index.js +12 -0
  203. package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +124 -0
  204. package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.js +5 -0
  205. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +174 -121
  206. package/node/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +89 -18
  207. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +50 -29
  208. package/node/internals/useTreeView/useTreeView.js +40 -3
  209. package/node/internals/useTreeView/useTreeViewModels.js +2 -2
  210. package/node/internals/utils/extractPluginParamsFromProps.js +34 -0
  211. package/node/internals/utils/warning.js +21 -0
  212. package/node/models/index.js +16 -0
  213. package/node/models/items.js +5 -0
  214. package/package.json +8 -7
  215. package/themeAugmentation/components.d.ts +14 -4
  216. package/themeAugmentation/overrides.d.ts +8 -4
  217. package/themeAugmentation/props.d.ts +7 -3
@@ -1,10 +1,9 @@
1
- import _extends from "@babel/runtime/helpers/esm/extends";
2
1
  import * as React from 'react';
3
2
  import { useTheme } from '@mui/material/styles';
4
3
  import useEventCallback from '@mui/utils/useEventCallback';
5
4
  import { getFirstNode, getLastNode, getNextNode, getPreviousNode, populateInstance } from '../../useTreeView/useTreeView.utils';
6
5
  function isPrintableCharacter(string) {
7
- return string && string.length === 1 && string.match(/\S/);
6
+ return !!string && string.length === 1 && !!string.match(/\S/);
8
7
  }
9
8
  function findNextFirstChar(firstChars, startIndex, char) {
10
9
  for (var i = startIndex; i < firstChars.length; i += 1) {
@@ -19,45 +18,32 @@ export var useTreeViewKeyboardNavigation = function useTreeViewKeyboardNavigatio
19
18
  params = _ref.params,
20
19
  state = _ref.state;
21
20
  var theme = useTheme();
22
- var isRtl = theme.direction === 'rtl';
21
+ var isRTL = theme.direction === 'rtl';
23
22
  var firstCharMap = React.useRef({});
24
- var mapFirstChar = useEventCallback(function (nodeId, firstChar) {
25
- firstCharMap.current[nodeId] = firstChar;
26
- return function () {
27
- var newMap = _extends({}, firstCharMap.current);
28
- delete newMap[nodeId];
29
- firstCharMap.current = newMap;
30
- };
23
+ var hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
24
+ var updateFirstCharMap = useEventCallback(function (callback) {
25
+ hasFirstCharMapBeenUpdatedImperatively.current = true;
26
+ firstCharMap.current = callback(firstCharMap.current);
31
27
  });
28
+ React.useEffect(function () {
29
+ if (hasFirstCharMapBeenUpdatedImperatively.current) {
30
+ return;
31
+ }
32
+ var newFirstCharMap = {};
33
+ var processItem = function processItem(item) {
34
+ var _item$children;
35
+ var getItemId = params.getItemId;
36
+ var nodeId = getItemId ? getItemId(item) : item.id;
37
+ newFirstCharMap[nodeId] = instance.getNode(nodeId).label.substring(0, 1).toLowerCase();
38
+ (_item$children = item.children) == null || _item$children.forEach(processItem);
39
+ };
40
+ params.items.forEach(processItem);
41
+ firstCharMap.current = newFirstCharMap;
42
+ }, [params.items, params.getItemId, instance]);
32
43
  populateInstance(instance, {
33
- mapFirstChar: mapFirstChar
44
+ updateFirstCharMap: updateFirstCharMap
34
45
  });
35
- var handleNextArrow = function handleNextArrow(event) {
36
- if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
37
- if (instance.isNodeExpanded(state.focusedNodeId)) {
38
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
39
- } else if (!instance.isNodeDisabled(state.focusedNodeId)) {
40
- instance.toggleNodeExpansion(event, state.focusedNodeId);
41
- }
42
- }
43
- return true;
44
- };
45
- var handlePreviousArrow = function handlePreviousArrow(event) {
46
- if (state.focusedNodeId == null) {
47
- return false;
48
- }
49
- if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
50
- instance.toggleNodeExpansion(event, state.focusedNodeId);
51
- return true;
52
- }
53
- var parent = instance.getNode(state.focusedNodeId).parentId;
54
- if (parent) {
55
- instance.focusNode(event, parent);
56
- return true;
57
- }
58
- return false;
59
- };
60
- var focusByFirstCharacter = function focusByFirstCharacter(event, nodeId, firstChar) {
46
+ var getFirstMatchingNode = function getFirstMatchingNode(nodeId, firstChar) {
61
47
  var start;
62
48
  var index;
63
49
  var lowercaseChar = firstChar.toLowerCase();
@@ -88,43 +74,41 @@ export var useTreeViewKeyboardNavigation = function useTreeViewKeyboardNavigatio
88
74
  index = findNextFirstChar(firstChars, 0, lowercaseChar);
89
75
  }
90
76
 
91
- // If match was found...
77
+ // If a match was found...
92
78
  if (index > -1) {
93
- instance.focusNode(event, firstCharIds[index]);
79
+ return firstCharIds[index];
94
80
  }
81
+ return null;
95
82
  };
96
- var selectNextNode = function selectNextNode(event, id) {
97
- if (!instance.isNodeDisabled(getNextNode(instance, id))) {
98
- instance.selectRange(event, {
99
- end: getNextNode(instance, id),
100
- current: id
101
- }, true);
102
- }
83
+ var canToggleNodeSelection = function canToggleNodeSelection(nodeId) {
84
+ return !params.disableSelection && !instance.isNodeDisabled(nodeId);
103
85
  };
104
- var selectPreviousNode = function selectPreviousNode(event, nodeId) {
105
- if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
106
- instance.selectRange(event, {
107
- end: getPreviousNode(instance, nodeId),
108
- current: nodeId
109
- }, true);
110
- }
86
+ var canToggleNodeExpansion = function canToggleNodeExpansion(nodeId) {
87
+ return !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
111
88
  };
89
+
90
+ // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
112
91
  var createHandleKeyDown = function createHandleKeyDown(otherHandlers) {
113
92
  return function (event) {
114
93
  var _otherHandlers$onKeyD;
115
94
  (_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null || _otherHandlers$onKeyD.call(otherHandlers, event);
116
- var flag = false;
117
- var key = event.key;
95
+ if (event.defaultMuiPrevented) {
96
+ return;
97
+ }
118
98
 
119
99
  // If the tree is empty there will be no focused node
120
100
  if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
121
101
  return;
122
102
  }
123
103
  var ctrlPressed = event.ctrlKey || event.metaKey;
124
- switch (key) {
125
- case ' ':
126
- if (!params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
127
- flag = true;
104
+ var key = event.key;
105
+
106
+ // eslint-disable-next-line default-case
107
+ switch (true) {
108
+ // Select the node when pressing "Space"
109
+ case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
110
+ {
111
+ event.preventDefault();
128
112
  if (params.multiSelect && event.shiftKey) {
129
113
  instance.selectRange(event, {
130
114
  end: state.focusedNodeId
@@ -134,85 +118,158 @@ export var useTreeViewKeyboardNavigation = function useTreeViewKeyboardNavigatio
134
118
  } else {
135
119
  instance.selectNode(event, state.focusedNodeId);
136
120
  }
121
+ break;
137
122
  }
138
- event.stopPropagation();
139
- break;
140
- case 'Enter':
141
- if (!instance.isNodeDisabled(state.focusedNodeId)) {
142
- if (instance.isNodeExpandable(state.focusedNodeId)) {
123
+
124
+ // If the focused node has children, we expand it.
125
+ // If the focused node has no children, we select it.
126
+ case key === 'Enter':
127
+ {
128
+ if (canToggleNodeExpansion(state.focusedNodeId)) {
143
129
  instance.toggleNodeExpansion(event, state.focusedNodeId);
144
- flag = true;
145
- } else if (!params.disableSelection) {
146
- flag = true;
130
+ event.preventDefault();
131
+ } else if (canToggleNodeSelection(state.focusedNodeId)) {
147
132
  if (params.multiSelect) {
133
+ event.preventDefault();
148
134
  instance.selectNode(event, state.focusedNodeId, true);
149
- } else {
135
+ } else if (!instance.isNodeSelected(state.focusedNodeId)) {
150
136
  instance.selectNode(event, state.focusedNodeId);
137
+ event.preventDefault();
151
138
  }
152
139
  }
140
+ break;
153
141
  }
154
- event.stopPropagation();
155
- break;
156
- case 'ArrowDown':
157
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
158
- selectNextNode(event, state.focusedNodeId);
142
+
143
+ // Focus the next focusable node
144
+ case key === 'ArrowDown':
145
+ {
146
+ var nextNode = getNextNode(instance, state.focusedNodeId);
147
+ if (nextNode) {
148
+ event.preventDefault();
149
+ instance.focusNode(event, nextNode);
150
+
151
+ // Multi select behavior when pressing Shift + ArrowDown
152
+ // Toggles the selection state of the next node
153
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
154
+ instance.selectRange(event, {
155
+ end: nextNode,
156
+ current: state.focusedNodeId
157
+ }, true);
158
+ }
159
+ }
160
+ break;
159
161
  }
160
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
161
- flag = true;
162
- break;
163
- case 'ArrowUp':
164
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
165
- selectPreviousNode(event, state.focusedNodeId);
162
+
163
+ // Focuses the previous focusable node
164
+ case key === 'ArrowUp':
165
+ {
166
+ var previousNode = getPreviousNode(instance, state.focusedNodeId);
167
+ if (previousNode) {
168
+ event.preventDefault();
169
+ instance.focusNode(event, previousNode);
170
+
171
+ // Multi select behavior when pressing Shift + ArrowUp
172
+ // Toggles the selection state of the previous node
173
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
174
+ instance.selectRange(event, {
175
+ end: previousNode,
176
+ current: state.focusedNodeId
177
+ }, true);
178
+ }
179
+ }
180
+ break;
166
181
  }
167
- instance.focusNode(event, getPreviousNode(instance, state.focusedNodeId));
168
- flag = true;
169
- break;
170
- case 'ArrowRight':
171
- if (isRtl) {
172
- flag = handlePreviousArrow(event);
173
- } else {
174
- flag = handleNextArrow(event);
182
+
183
+ // If the focused node is expanded, we move the focus to its first child
184
+ // If the focused node is collapsed and has children, we expand it
185
+ case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
186
+ {
187
+ if (instance.isNodeExpanded(state.focusedNodeId)) {
188
+ instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
189
+ event.preventDefault();
190
+ } else if (canToggleNodeExpansion(state.focusedNodeId)) {
191
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
192
+ event.preventDefault();
193
+ }
194
+ break;
175
195
  }
176
- break;
177
- case 'ArrowLeft':
178
- if (isRtl) {
179
- flag = handleNextArrow(event);
180
- } else {
181
- flag = handlePreviousArrow(event);
196
+
197
+ // If the focused node is expanded, we collapse it
198
+ // If the focused node is collapsed and has a parent, we move the focus to this parent
199
+ case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
200
+ {
201
+ if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
202
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
203
+ event.preventDefault();
204
+ } else {
205
+ var parent = instance.getNode(state.focusedNodeId).parentId;
206
+ if (parent) {
207
+ instance.focusNode(event, parent);
208
+ event.preventDefault();
209
+ }
210
+ }
211
+ break;
182
212
  }
183
- break;
184
- case 'Home':
185
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
186
- instance.rangeSelectToFirst(event, state.focusedNodeId);
213
+
214
+ // Focuses the first node in the tree
215
+ case key === 'Home':
216
+ {
217
+ instance.focusNode(event, getFirstNode(instance));
218
+
219
+ // Multi select behavior when pressing Ctrl + Shift + Home
220
+ // Selects the focused node and all nodes up to the first node.
221
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
222
+ instance.rangeSelectToFirst(event, state.focusedNodeId);
223
+ }
224
+ event.preventDefault();
225
+ break;
187
226
  }
188
- instance.focusNode(event, getFirstNode(instance));
189
- flag = true;
190
- break;
191
- case 'End':
192
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
193
- instance.rangeSelectToLast(event, state.focusedNodeId);
227
+
228
+ // Focuses the last node in the tree
229
+ case key === 'End':
230
+ {
231
+ instance.focusNode(event, getLastNode(instance));
232
+
233
+ // Multi select behavior when pressing Ctrl + Shirt + End
234
+ // Selects the focused node and all the nodes down to the last node.
235
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
236
+ instance.rangeSelectToLast(event, state.focusedNodeId);
237
+ }
238
+ event.preventDefault();
239
+ break;
194
240
  }
195
- instance.focusNode(event, getLastNode(instance));
196
- flag = true;
197
- break;
198
- default:
199
- if (key === '*') {
241
+
242
+ // Expand all siblings that are at the same level as the focused node
243
+ case key === '*':
244
+ {
200
245
  instance.expandAllSiblings(event, state.focusedNodeId);
201
- flag = true;
202
- } else if (params.multiSelect && ctrlPressed && key.toLowerCase() === 'a' && !params.disableSelection) {
246
+ event.preventDefault();
247
+ break;
248
+ }
249
+
250
+ // Multi select behavior when pressing Ctrl + a
251
+ // Selects all the nodes
252
+ case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
253
+ {
203
254
  instance.selectRange(event, {
204
255
  start: getFirstNode(instance),
205
256
  end: getLastNode(instance)
206
257
  });
207
- flag = true;
208
- } else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
209
- focusByFirstCharacter(event, state.focusedNodeId, key);
210
- flag = true;
258
+ event.preventDefault();
259
+ break;
260
+ }
261
+
262
+ // Type-ahead
263
+ // TODO: Support typing multiple characters
264
+ case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
265
+ {
266
+ var matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
267
+ if (matchingNode != null) {
268
+ instance.focusNode(event, matchingNode);
269
+ event.preventDefault();
270
+ }
271
+ break;
211
272
  }
212
- }
213
- if (flag) {
214
- event.preventDefault();
215
- event.stopPropagation();
216
273
  }
217
274
  };
218
275
  };
@@ -223,4 +280,5 @@ export var useTreeViewKeyboardNavigation = function useTreeViewKeyboardNavigatio
223
280
  };
224
281
  }
225
282
  };
226
- };
283
+ };
284
+ useTreeViewKeyboardNavigation.params = {};
@@ -3,24 +3,54 @@ import * as React from 'react';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
4
  import { populateInstance } from '../../useTreeView/useTreeView.utils';
5
5
  import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
6
- export var useTreeViewNodes = function useTreeViewNodes(_ref) {
7
- var instance = _ref.instance,
8
- params = _ref.params;
9
- var nodeMap = React.useRef({});
6
+ var updateState = function updateState(_ref) {
7
+ var items = _ref.items,
8
+ isItemDisabled = _ref.isItemDisabled,
9
+ getItemLabel = _ref.getItemLabel,
10
+ getItemId = _ref.getItemId;
11
+ var nodeMap = {};
12
+ var processItem = function processItem(item, index, parentId) {
13
+ var _item$children, _item$children2;
14
+ var id = getItemId ? getItemId(item) : item.id;
15
+ if (id == null) {
16
+ 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.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
17
+ }
18
+ var label = getItemLabel ? getItemLabel(item) : item.label;
19
+ if (label == null) {
20
+ throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
21
+ }
22
+ nodeMap[id] = {
23
+ id: id,
24
+ label: label,
25
+ index: index,
26
+ parentId: parentId,
27
+ idAttribute: id,
28
+ expandable: !!((_item$children = item.children) != null && _item$children.length),
29
+ disabled: isItemDisabled ? isItemDisabled(item) : false
30
+ };
31
+ return {
32
+ id: id,
33
+ children: (_item$children2 = item.children) == null ? void 0 : _item$children2.map(function (child, childIndex) {
34
+ return processItem(child, childIndex, id);
35
+ })
36
+ };
37
+ };
38
+ var nodeTree = items.map(function (item, itemIndex) {
39
+ return processItem(item, itemIndex, null);
40
+ });
41
+ return {
42
+ nodeMap: nodeMap,
43
+ nodeTree: nodeTree
44
+ };
45
+ };
46
+ export var useTreeViewNodes = function useTreeViewNodes(_ref2) {
47
+ var instance = _ref2.instance,
48
+ params = _ref2.params,
49
+ state = _ref2.state,
50
+ setState = _ref2.setState;
10
51
  var getNode = React.useCallback(function (nodeId) {
11
- return nodeMap.current[nodeId];
12
- }, []);
13
- var insertNode = React.useCallback(function (node) {
14
- nodeMap.current[node.id] = node;
15
- }, []);
16
- var removeNode = React.useCallback(function (nodeId) {
17
- var newMap = _extends({}, nodeMap.current);
18
- delete newMap[nodeId];
19
- nodeMap.current = newMap;
20
- publishTreeViewEvent(instance, 'removeNode', {
21
- id: nodeId
22
- });
23
- }, [instance]);
52
+ return state.nodeMap[nodeId];
53
+ }, [state.nodeMap]);
24
54
  var isNodeDisabled = React.useCallback(function (nodeId) {
25
55
  if (nodeId == null) {
26
56
  return false;
@@ -43,7 +73,7 @@ export var useTreeViewNodes = function useTreeViewNodes(_ref) {
43
73
  return false;
44
74
  }, [instance]);
45
75
  var getChildrenIds = useEventCallback(function (nodeId) {
46
- return Object.values(nodeMap.current).filter(function (node) {
76
+ return Object.values(state.nodeMap).filter(function (node) {
47
77
  return node.parentId === nodeId;
48
78
  }).sort(function (a, b) {
49
79
  return a.index - b.index;
@@ -60,12 +90,58 @@ export var useTreeViewNodes = function useTreeViewNodes(_ref) {
60
90
  }
61
91
  return childrenIds;
62
92
  };
93
+ React.useEffect(function () {
94
+ setState(function (prevState) {
95
+ var newState = updateState({
96
+ items: params.items,
97
+ isItemDisabled: params.isItemDisabled,
98
+ getItemId: params.getItemId,
99
+ getItemLabel: params.getItemLabel
100
+ });
101
+ Object.values(prevState.nodeMap).forEach(function (node) {
102
+ if (!newState.nodeMap[node.id]) {
103
+ publishTreeViewEvent(instance, 'removeNode', {
104
+ id: node.id
105
+ });
106
+ }
107
+ });
108
+ return _extends({}, prevState, newState);
109
+ });
110
+ }, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
111
+ var getNodesToRender = useEventCallback(function () {
112
+ var getPropsFromNodeId = function getPropsFromNodeId(_ref3) {
113
+ var id = _ref3.id,
114
+ children = _ref3.children;
115
+ var node = state.nodeMap[id];
116
+ return {
117
+ label: node.label,
118
+ nodeId: node.id,
119
+ id: node.idAttribute,
120
+ children: children == null ? void 0 : children.map(getPropsFromNodeId)
121
+ };
122
+ };
123
+ return state.nodeTree.map(getPropsFromNodeId);
124
+ });
63
125
  populateInstance(instance, {
64
126
  getNode: getNode,
65
- updateNode: insertNode,
66
- removeNode: removeNode,
127
+ getNodesToRender: getNodesToRender,
67
128
  getChildrenIds: getChildrenIds,
68
129
  getNavigableChildrenIds: getNavigableChildrenIds,
69
130
  isNodeDisabled: isNodeDisabled
70
131
  });
132
+ };
133
+ useTreeViewNodes.getInitialState = function (params) {
134
+ return updateState({
135
+ items: params.items,
136
+ isItemDisabled: params.isItemDisabled,
137
+ getItemId: params.getItemId,
138
+ getItemLabel: params.getItemLabel
139
+ });
140
+ };
141
+ useTreeViewNodes.params = {
142
+ disabledItemsFocusable: true,
143
+ items: true,
144
+ isItemDisabled: true,
145
+ getItemLabel: true,
146
+ getItemId: true
71
147
  };
@@ -11,7 +11,36 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
11
11
  var lastSelectionWasRange = React.useRef(false);
12
12
  var currentRangeSelection = React.useRef([]);
13
13
  var isNodeSelected = function isNodeSelected(nodeId) {
14
- return Array.isArray(models.selected.value) ? models.selected.value.indexOf(nodeId) !== -1 : models.selected.value === nodeId;
14
+ return Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
15
+ };
16
+ var setSelectedNodes = function setSelectedNodes(event, newSelectedNodes) {
17
+ if (params.onNodeSelectionToggle) {
18
+ if (params.multiSelect) {
19
+ var addedNodes = newSelectedNodes.filter(function (nodeId) {
20
+ return !instance.isNodeSelected(nodeId);
21
+ });
22
+ var removedNodes = models.selectedNodes.value.filter(function (nodeId) {
23
+ return !newSelectedNodes.includes(nodeId);
24
+ });
25
+ addedNodes.forEach(function (nodeId) {
26
+ params.onNodeSelectionToggle(event, nodeId, true);
27
+ });
28
+ removedNodes.forEach(function (nodeId) {
29
+ params.onNodeSelectionToggle(event, nodeId, false);
30
+ });
31
+ } else if (newSelectedNodes !== models.selectedNodes.value) {
32
+ if (models.selectedNodes.value != null) {
33
+ params.onNodeSelectionToggle(event, models.selectedNodes.value, false);
34
+ }
35
+ if (newSelectedNodes != null) {
36
+ params.onNodeSelectionToggle(event, newSelectedNodes, true);
37
+ }
38
+ }
39
+ }
40
+ if (params.onSelectedNodesChange) {
41
+ params.onSelectedNodesChange(event, newSelectedNodes);
42
+ }
43
+ models.selectedNodes.setValue(newSelectedNodes);
15
44
  };
16
45
  var selectNode = function selectNode(event, nodeId) {
17
46
  var multiple = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
@@ -19,26 +48,20 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
19
48
  return;
20
49
  }
21
50
  if (multiple) {
22
- if (Array.isArray(models.selected.value)) {
51
+ if (Array.isArray(models.selectedNodes.value)) {
23
52
  var newSelected;
24
- if (models.selected.value.indexOf(nodeId) !== -1) {
25
- newSelected = models.selected.value.filter(function (id) {
53
+ if (models.selectedNodes.value.indexOf(nodeId) !== -1) {
54
+ newSelected = models.selectedNodes.value.filter(function (id) {
26
55
  return id !== nodeId;
27
56
  });
28
57
  } else {
29
- newSelected = [nodeId].concat(models.selected.value);
58
+ newSelected = [nodeId].concat(models.selectedNodes.value);
30
59
  }
31
- if (params.onNodeSelect) {
32
- params.onNodeSelect(event, newSelected);
33
- }
34
- models.selected.setValue(newSelected);
60
+ setSelectedNodes(event, newSelected);
35
61
  }
36
62
  } else {
37
63
  var _newSelected = params.multiSelect ? [nodeId] : nodeId;
38
- if (params.onNodeSelect) {
39
- params.onNodeSelect(event, _newSelected);
40
- }
41
- models.selected.setValue(_newSelected);
64
+ setSelectedNodes(event, _newSelected);
42
65
  }
43
66
  lastSelectedNode.current = nodeId;
44
67
  lastSelectionWasRange.current = false;
@@ -58,7 +81,7 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
58
81
  return nodes;
59
82
  };
60
83
  var handleRangeArrowSelect = function handleRangeArrowSelect(event, nodes) {
61
- var base = models.selected.value.slice();
84
+ var base = models.selectedNodes.value.slice();
62
85
  var start = nodes.start,
63
86
  next = nodes.next,
64
87
  current = nodes.current;
@@ -84,13 +107,10 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
84
107
  base.push(next);
85
108
  currentRangeSelection.current.push(current, next);
86
109
  }
87
- if (params.onNodeSelect) {
88
- params.onNodeSelect(event, base);
89
- }
90
- models.selected.setValue(base);
110
+ setSelectedNodes(event, base);
91
111
  };
92
112
  var handleRangeSelect = function handleRangeSelect(event, nodes) {
93
- var base = models.selected.value.slice();
113
+ var base = models.selectedNodes.value.slice();
94
114
  var start = nodes.start,
95
115
  end = nodes.end;
96
116
  // If last selection was a range selection ignore nodes that were selected.
@@ -108,10 +128,7 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
108
128
  newSelected = newSelected.filter(function (id, i) {
109
129
  return newSelected.indexOf(id) === i;
110
130
  });
111
- if (params.onNodeSelect) {
112
- params.onNodeSelect(event, newSelected);
113
- }
114
- models.selected.setValue(newSelected);
131
+ setSelectedNodes(event, newSelected);
115
132
  };
116
133
  var selectRange = function selectRange(event, nodes) {
117
134
  var stacked = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
@@ -172,17 +189,25 @@ export var useTreeViewSelection = function useTreeViewSelection(_ref) {
172
189
  };
173
190
  };
174
191
  useTreeViewSelection.models = {
175
- selected: {
176
- controlledProp: 'selected',
177
- defaultProp: 'defaultSelected'
192
+ selectedNodes: {
193
+ controlledProp: 'selectedNodes',
194
+ defaultProp: 'defaultSelectedNodes'
178
195
  }
179
196
  };
180
- var DEFAULT_SELECTED = [];
197
+ var DEFAULT_SELECTED_NODES = [];
181
198
  useTreeViewSelection.getDefaultizedParams = function (params) {
182
199
  var _params$disableSelect, _params$multiSelect, _params$defaultSelect;
183
200
  return _extends({}, params, {
184
201
  disableSelection: (_params$disableSelect = params.disableSelection) != null ? _params$disableSelect : false,
185
202
  multiSelect: (_params$multiSelect = params.multiSelect) != null ? _params$multiSelect : false,
186
- defaultSelected: (_params$defaultSelect = params.defaultSelected) != null ? _params$defaultSelect : params.multiSelect ? DEFAULT_SELECTED : null
203
+ defaultSelectedNodes: (_params$defaultSelect = params.defaultSelectedNodes) != null ? _params$defaultSelect : params.multiSelect ? DEFAULT_SELECTED_NODES : null
187
204
  });
205
+ };
206
+ useTreeViewSelection.params = {
207
+ disableSelection: true,
208
+ multiSelect: true,
209
+ defaultSelectedNodes: true,
210
+ selectedNodes: true,
211
+ onSelectedNodesChange: true,
212
+ onNodeSelectionToggle: true
188
213
  };