@mui/x-tree-view 7.0.0-alpha.7 → 7.0.0-alpha.9

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 (164) hide show
  1. package/CHANGELOG.md +554 -51
  2. package/README.md +1 -1
  3. package/RichTreeView/RichTreeView.js +14 -79
  4. package/RichTreeView/RichTreeView.types.d.ts +6 -9
  5. package/RichTreeView/index.d.ts +1 -1
  6. package/SimpleTreeView/SimpleTreeView.js +17 -75
  7. package/SimpleTreeView/SimpleTreeView.plugins.d.ts +4 -2
  8. package/SimpleTreeView/SimpleTreeView.types.d.ts +8 -7
  9. package/SimpleTreeView/index.d.ts +1 -1
  10. package/TreeItem/TreeItem.js +62 -39
  11. package/TreeItem/TreeItem.types.d.ts +33 -17
  12. package/TreeItem/TreeItemContent.d.ts +0 -3
  13. package/TreeItem/TreeItemContent.js +2 -5
  14. package/TreeItem/index.d.ts +2 -2
  15. package/TreeItem/index.js +1 -1
  16. package/TreeItem/{useTreeItem.d.ts → useTreeItemState.d.ts} +1 -1
  17. package/TreeItem/{useTreeItem.js → useTreeItemState.js} +4 -2
  18. package/TreeView/TreeView.js +1 -22
  19. package/TreeView/TreeView.types.d.ts +5 -1
  20. package/TreeView/index.d.ts +1 -1
  21. package/icons/icons.d.ts +6 -0
  22. package/icons/icons.js +9 -0
  23. package/icons/index.d.ts +1 -0
  24. package/icons/index.js +1 -0
  25. package/icons/package.json +6 -0
  26. package/index.d.ts +1 -0
  27. package/index.js +3 -2
  28. package/internals/TreeViewProvider/TreeViewContext.d.ts +4 -2
  29. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +3 -11
  30. package/internals/TreeViewProvider/useTreeViewContext.js +1 -1
  31. package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
  32. package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.types.d.ts +3 -2
  33. package/internals/index.d.ts +15 -0
  34. package/internals/index.js +4 -0
  35. package/internals/models/MuiCancellableEvent.d.ts +4 -0
  36. package/internals/models/helpers.d.ts +7 -1
  37. package/internals/models/plugin.d.ts +65 -20
  38. package/internals/models/treeView.d.ts +1 -2
  39. package/internals/package.json +6 -0
  40. package/internals/plugins/defaultPlugins.d.ts +6 -4
  41. package/internals/plugins/defaultPlugins.js +2 -2
  42. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -10
  43. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +7 -3
  44. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +2 -5
  45. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +12 -6
  46. package/internals/plugins/useTreeViewIcons/index.d.ts +2 -0
  47. package/internals/plugins/useTreeViewIcons/index.js +1 -0
  48. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.d.ts +3 -0
  49. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +22 -0
  50. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +43 -0
  51. package/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
  52. package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +6 -2
  53. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
  54. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +6 -4
  55. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
  56. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +9 -6
  57. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +21 -3
  58. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +10 -2
  59. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +17 -5
  60. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +15 -5
  61. package/internals/useTreeView/useTreeView.js +7 -3
  62. package/internals/useTreeView/useTreeView.types.d.ts +6 -6
  63. package/internals/useTreeView/useTreeViewModels.js +12 -13
  64. package/internals/utils/extractPluginParamsFromProps.d.ts +16 -0
  65. package/internals/utils/extractPluginParamsFromProps.js +38 -0
  66. package/legacy/RichTreeView/RichTreeView.js +15 -77
  67. package/legacy/SimpleTreeView/SimpleTreeView.js +14 -70
  68. package/legacy/TreeItem/TreeItem.js +63 -39
  69. package/legacy/TreeItem/TreeItemContent.js +9 -12
  70. package/legacy/TreeItem/index.js +1 -1
  71. package/legacy/TreeItem/{useTreeItem.js → useTreeItemState.js} +2 -2
  72. package/legacy/TreeView/TreeView.js +1 -22
  73. package/legacy/icons/icons.js +9 -0
  74. package/legacy/icons/index.js +1 -0
  75. package/legacy/index.js +3 -2
  76. package/legacy/internals/TreeViewProvider/useTreeViewContext.js +1 -1
  77. package/legacy/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
  78. package/legacy/internals/index.js +4 -0
  79. package/legacy/internals/plugins/defaultPlugins.js +2 -2
  80. package/legacy/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +16 -10
  81. package/legacy/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +2 -5
  82. package/legacy/internals/plugins/useTreeViewIcons/index.js +1 -0
  83. package/legacy/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +21 -0
  84. package/legacy/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +1 -0
  85. package/legacy/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
  86. package/legacy/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
  87. package/legacy/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +157 -110
  88. package/legacy/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +20 -2
  89. package/legacy/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +20 -6
  90. package/legacy/internals/useTreeView/useTreeView.js +6 -2
  91. package/legacy/internals/useTreeView/useTreeViewModels.js +12 -13
  92. package/legacy/internals/utils/extractPluginParamsFromProps.js +36 -0
  93. package/modern/RichTreeView/RichTreeView.js +14 -79
  94. package/modern/SimpleTreeView/SimpleTreeView.js +17 -75
  95. package/modern/TreeItem/TreeItem.js +61 -39
  96. package/modern/TreeItem/TreeItemContent.js +2 -5
  97. package/modern/TreeItem/index.js +1 -1
  98. package/modern/TreeItem/{useTreeItem.js → useTreeItemState.js} +4 -2
  99. package/modern/TreeView/TreeView.js +1 -22
  100. package/modern/icons/icons.js +9 -0
  101. package/modern/icons/index.js +1 -0
  102. package/modern/index.js +3 -2
  103. package/modern/internals/TreeViewProvider/useTreeViewContext.js +1 -1
  104. package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
  105. package/modern/internals/index.js +4 -0
  106. package/modern/internals/models/MuiCancellableEvent.js +1 -0
  107. package/modern/internals/plugins/defaultPlugins.js +2 -2
  108. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -11
  109. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -3
  110. package/modern/internals/plugins/useTreeViewIcons/index.js +1 -0
  111. package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +22 -0
  112. package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +1 -0
  113. package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
  114. package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
  115. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
  116. package/modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +18 -3
  117. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +18 -6
  118. package/modern/internals/useTreeView/useTreeView.js +7 -3
  119. package/modern/internals/useTreeView/useTreeViewModels.js +12 -13
  120. package/modern/internals/utils/extractPluginParamsFromProps.js +38 -0
  121. package/node/RichTreeView/RichTreeView.js +14 -79
  122. package/node/SimpleTreeView/SimpleTreeView.js +17 -75
  123. package/node/TreeItem/TreeItem.js +61 -39
  124. package/node/TreeItem/TreeItemContent.js +2 -5
  125. package/node/TreeItem/index.js +4 -4
  126. package/node/TreeItem/{useTreeItem.js → useTreeItemState.js} +5 -3
  127. package/node/TreeView/TreeView.js +1 -22
  128. package/node/icons/icons.js +17 -0
  129. package/node/icons/index.js +16 -0
  130. package/node/index.js +13 -1
  131. package/node/internals/TreeViewProvider/useTreeViewContext.js +1 -1
  132. package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
  133. package/node/internals/index.js +33 -0
  134. package/node/internals/plugins/defaultPlugins.js +2 -2
  135. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -11
  136. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -3
  137. package/node/internals/plugins/useTreeViewIcons/index.js +12 -0
  138. package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +29 -0
  139. package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +5 -0
  140. package/node/internals/plugins/useTreeViewId/useTreeViewId.js +4 -1
  141. package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
  142. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
  143. package/node/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +18 -3
  144. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +18 -6
  145. package/node/internals/useTreeView/useTreeView.js +7 -3
  146. package/node/internals/useTreeView/useTreeViewModels.js +12 -13
  147. package/node/internals/utils/extractPluginParamsFromProps.js +46 -0
  148. package/package.json +7 -7
  149. package/themeAugmentation/components.d.ts +4 -4
  150. package/internals/plugins/useTreeViewContextValueBuilder/index.d.ts +0 -2
  151. package/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
  152. package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.d.ts +0 -3
  153. package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -26
  154. package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.d.ts +0 -29
  155. package/legacy/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
  156. package/legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -28
  157. package/modern/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
  158. package/modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -26
  159. package/node/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -12
  160. package/node/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -33
  161. /package/internals/{plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → models/MuiCancellableEvent.js} +0 -0
  162. /package/{legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js} +0 -0
  163. /package/{modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → legacy/internals/models/MuiCancellableEvent.js} +0 -0
  164. /package/node/internals/{plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → models/MuiCancellableEvent.js} +0 -0
@@ -12,7 +12,7 @@ var _useTreeView = require("../../useTreeView/useTreeView.utils");
12
12
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
13
13
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
14
  function isPrintableCharacter(string) {
15
- return string && string.length === 1 && string.match(/\S/);
15
+ return !!string && string.length === 1 && !!string.match(/\S/);
16
16
  }
17
17
  function findNextFirstChar(firstChars, startIndex, char) {
18
18
  for (let i = startIndex; i < firstChars.length; i += 1) {
@@ -28,7 +28,7 @@ const useTreeViewKeyboardNavigation = ({
28
28
  state
29
29
  }) => {
30
30
  const theme = (0, _styles.useTheme)();
31
- const isRtl = theme.direction === 'rtl';
31
+ const isRTL = theme.direction === 'rtl';
32
32
  const firstCharMap = React.useRef({});
33
33
  const hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
34
34
  const updateFirstCharMap = (0, _useEventCallback.default)(callback => {
@@ -52,32 +52,7 @@ const useTreeViewKeyboardNavigation = ({
52
52
  (0, _useTreeView.populateInstance)(instance, {
53
53
  updateFirstCharMap
54
54
  });
55
- const handleNextArrow = event => {
56
- if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
57
- if (instance.isNodeExpanded(state.focusedNodeId)) {
58
- instance.focusNode(event, (0, _useTreeView.getNextNode)(instance, state.focusedNodeId));
59
- } else if (!instance.isNodeDisabled(state.focusedNodeId)) {
60
- instance.toggleNodeExpansion(event, state.focusedNodeId);
61
- }
62
- }
63
- return true;
64
- };
65
- const handlePreviousArrow = event => {
66
- if (state.focusedNodeId == null) {
67
- return false;
68
- }
69
- if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
70
- instance.toggleNodeExpansion(event, state.focusedNodeId);
71
- return true;
72
- }
73
- const parent = instance.getNode(state.focusedNodeId).parentId;
74
- if (parent) {
75
- instance.focusNode(event, parent);
76
- return true;
77
- }
78
- return false;
79
- };
80
- const focusByFirstCharacter = (event, nodeId, firstChar) => {
55
+ const getFirstMatchingNode = (nodeId, firstChar) => {
81
56
  let start;
82
57
  let index;
83
58
  const lowercaseChar = firstChar.toLowerCase();
@@ -108,41 +83,35 @@ const useTreeViewKeyboardNavigation = ({
108
83
  index = findNextFirstChar(firstChars, 0, lowercaseChar);
109
84
  }
110
85
 
111
- // If match was found...
86
+ // If a match was found...
112
87
  if (index > -1) {
113
- instance.focusNode(event, firstCharIds[index]);
114
- }
115
- };
116
- const selectNextNode = (event, id) => {
117
- if (!instance.isNodeDisabled((0, _useTreeView.getNextNode)(instance, id))) {
118
- instance.selectRange(event, {
119
- end: (0, _useTreeView.getNextNode)(instance, id),
120
- current: id
121
- }, true);
122
- }
123
- };
124
- const selectPreviousNode = (event, nodeId) => {
125
- if (!instance.isNodeDisabled((0, _useTreeView.getPreviousNode)(instance, nodeId))) {
126
- instance.selectRange(event, {
127
- end: (0, _useTreeView.getPreviousNode)(instance, nodeId),
128
- current: nodeId
129
- }, true);
88
+ return firstCharIds[index];
130
89
  }
90
+ return null;
131
91
  };
92
+ const canToggleNodeSelection = nodeId => !params.disableSelection && !instance.isNodeDisabled(nodeId);
93
+ const canToggleNodeExpansion = nodeId => !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
94
+
95
+ // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
132
96
  const createHandleKeyDown = otherHandlers => event => {
133
97
  otherHandlers.onKeyDown?.(event);
134
- let flag = false;
135
- const key = event.key;
98
+ if (event.defaultMuiPrevented) {
99
+ return;
100
+ }
136
101
 
137
102
  // If the tree is empty there will be no focused node
138
103
  if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
139
104
  return;
140
105
  }
141
106
  const ctrlPressed = event.ctrlKey || event.metaKey;
142
- switch (key) {
143
- case ' ':
144
- if (!params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
145
- flag = true;
107
+ const key = event.key;
108
+
109
+ // eslint-disable-next-line default-case
110
+ switch (true) {
111
+ // Select the node when pressing "Space"
112
+ case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
113
+ {
114
+ event.preventDefault();
146
115
  if (params.multiSelect && event.shiftKey) {
147
116
  instance.selectRange(event, {
148
117
  end: state.focusedNodeId
@@ -152,85 +121,158 @@ const useTreeViewKeyboardNavigation = ({
152
121
  } else {
153
122
  instance.selectNode(event, state.focusedNodeId);
154
123
  }
124
+ break;
155
125
  }
156
- event.stopPropagation();
157
- break;
158
- case 'Enter':
159
- if (!instance.isNodeDisabled(state.focusedNodeId)) {
160
- if (instance.isNodeExpandable(state.focusedNodeId)) {
126
+
127
+ // If the focused node has children, we expand it.
128
+ // If the focused node has no children, we select it.
129
+ case key === 'Enter':
130
+ {
131
+ if (canToggleNodeExpansion(state.focusedNodeId)) {
161
132
  instance.toggleNodeExpansion(event, state.focusedNodeId);
162
- flag = true;
163
- } else if (!params.disableSelection) {
164
- flag = true;
133
+ event.preventDefault();
134
+ } else if (canToggleNodeSelection(state.focusedNodeId)) {
165
135
  if (params.multiSelect) {
136
+ event.preventDefault();
166
137
  instance.selectNode(event, state.focusedNodeId, true);
167
- } else {
138
+ } else if (!instance.isNodeSelected(state.focusedNodeId)) {
168
139
  instance.selectNode(event, state.focusedNodeId);
140
+ event.preventDefault();
169
141
  }
170
142
  }
143
+ break;
171
144
  }
172
- event.stopPropagation();
173
- break;
174
- case 'ArrowDown':
175
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
176
- selectNextNode(event, state.focusedNodeId);
145
+
146
+ // Focus the next focusable node
147
+ case key === 'ArrowDown':
148
+ {
149
+ const nextNode = (0, _useTreeView.getNextNode)(instance, state.focusedNodeId);
150
+ if (nextNode) {
151
+ event.preventDefault();
152
+ instance.focusNode(event, nextNode);
153
+
154
+ // Multi select behavior when pressing Shift + ArrowDown
155
+ // Toggles the selection state of the next node
156
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
157
+ instance.selectRange(event, {
158
+ end: nextNode,
159
+ current: state.focusedNodeId
160
+ }, true);
161
+ }
162
+ }
163
+ break;
177
164
  }
178
- instance.focusNode(event, (0, _useTreeView.getNextNode)(instance, state.focusedNodeId));
179
- flag = true;
180
- break;
181
- case 'ArrowUp':
182
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
183
- selectPreviousNode(event, state.focusedNodeId);
165
+
166
+ // Focuses the previous focusable node
167
+ case key === 'ArrowUp':
168
+ {
169
+ const previousNode = (0, _useTreeView.getPreviousNode)(instance, state.focusedNodeId);
170
+ if (previousNode) {
171
+ event.preventDefault();
172
+ instance.focusNode(event, previousNode);
173
+
174
+ // Multi select behavior when pressing Shift + ArrowUp
175
+ // Toggles the selection state of the previous node
176
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
177
+ instance.selectRange(event, {
178
+ end: previousNode,
179
+ current: state.focusedNodeId
180
+ }, true);
181
+ }
182
+ }
183
+ break;
184
184
  }
185
- instance.focusNode(event, (0, _useTreeView.getPreviousNode)(instance, state.focusedNodeId));
186
- flag = true;
187
- break;
188
- case 'ArrowRight':
189
- if (isRtl) {
190
- flag = handlePreviousArrow(event);
191
- } else {
192
- flag = handleNextArrow(event);
185
+
186
+ // If the focused node is expanded, we move the focus to its first child
187
+ // If the focused node is collapsed and has children, we expand it
188
+ case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
189
+ {
190
+ if (instance.isNodeExpanded(state.focusedNodeId)) {
191
+ instance.focusNode(event, (0, _useTreeView.getNextNode)(instance, state.focusedNodeId));
192
+ event.preventDefault();
193
+ } else if (canToggleNodeExpansion(state.focusedNodeId)) {
194
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
195
+ event.preventDefault();
196
+ }
197
+ break;
193
198
  }
194
- break;
195
- case 'ArrowLeft':
196
- if (isRtl) {
197
- flag = handleNextArrow(event);
198
- } else {
199
- flag = handlePreviousArrow(event);
199
+
200
+ // If the focused node is expanded, we collapse it
201
+ // If the focused node is collapsed and has a parent, we move the focus to this parent
202
+ case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
203
+ {
204
+ if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
205
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
206
+ event.preventDefault();
207
+ } else {
208
+ const parent = instance.getNode(state.focusedNodeId).parentId;
209
+ if (parent) {
210
+ instance.focusNode(event, parent);
211
+ event.preventDefault();
212
+ }
213
+ }
214
+ break;
200
215
  }
201
- break;
202
- case 'Home':
203
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
204
- instance.rangeSelectToFirst(event, state.focusedNodeId);
216
+
217
+ // Focuses the first node in the tree
218
+ case key === 'Home':
219
+ {
220
+ instance.focusNode(event, (0, _useTreeView.getFirstNode)(instance));
221
+
222
+ // Multi select behavior when pressing Ctrl + Shift + Home
223
+ // Selects the focused node and all nodes up to the first node.
224
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
225
+ instance.rangeSelectToFirst(event, state.focusedNodeId);
226
+ }
227
+ event.preventDefault();
228
+ break;
205
229
  }
206
- instance.focusNode(event, (0, _useTreeView.getFirstNode)(instance));
207
- flag = true;
208
- break;
209
- case 'End':
210
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
211
- instance.rangeSelectToLast(event, state.focusedNodeId);
230
+
231
+ // Focuses the last node in the tree
232
+ case key === 'End':
233
+ {
234
+ instance.focusNode(event, (0, _useTreeView.getLastNode)(instance));
235
+
236
+ // Multi select behavior when pressing Ctrl + Shirt + End
237
+ // Selects the focused node and all the nodes down to the last node.
238
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
239
+ instance.rangeSelectToLast(event, state.focusedNodeId);
240
+ }
241
+ event.preventDefault();
242
+ break;
212
243
  }
213
- instance.focusNode(event, (0, _useTreeView.getLastNode)(instance));
214
- flag = true;
215
- break;
216
- default:
217
- if (key === '*') {
244
+
245
+ // Expand all siblings that are at the same level as the focused node
246
+ case key === '*':
247
+ {
218
248
  instance.expandAllSiblings(event, state.focusedNodeId);
219
- flag = true;
220
- } else if (params.multiSelect && ctrlPressed && key.toLowerCase() === 'a' && !params.disableSelection) {
249
+ event.preventDefault();
250
+ break;
251
+ }
252
+
253
+ // Multi select behavior when pressing Ctrl + a
254
+ // Selects all the nodes
255
+ case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
256
+ {
221
257
  instance.selectRange(event, {
222
258
  start: (0, _useTreeView.getFirstNode)(instance),
223
259
  end: (0, _useTreeView.getLastNode)(instance)
224
260
  });
225
- flag = true;
226
- } else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
227
- focusByFirstCharacter(event, state.focusedNodeId, key);
228
- flag = true;
261
+ event.preventDefault();
262
+ break;
263
+ }
264
+
265
+ // Type-ahead
266
+ // TODO: Support typing multiple characters
267
+ case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
268
+ {
269
+ const matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
270
+ if (matchingNode != null) {
271
+ instance.focusNode(event, matchingNode);
272
+ event.preventDefault();
273
+ }
274
+ break;
229
275
  }
230
- }
231
- if (flag) {
232
- event.preventDefault();
233
- event.stopPropagation();
234
276
  }
235
277
  };
236
278
  return {
@@ -239,4 +281,5 @@ const useTreeViewKeyboardNavigation = ({
239
281
  })
240
282
  };
241
283
  };
242
- exports.useTreeViewKeyboardNavigation = useTreeViewKeyboardNavigation;
284
+ exports.useTreeViewKeyboardNavigation = useTreeViewKeyboardNavigation;
285
+ useTreeViewKeyboardNavigation.params = {};
@@ -22,11 +22,11 @@ const updateState = ({
22
22
  const processItem = (item, index, parentId) => {
23
23
  const id = getItemId ? getItemId(item) : item.id;
24
24
  if (id == null) {
25
- throw new Error(['MUI: 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'));
25
+ 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'));
26
26
  }
27
27
  const label = getItemLabel ? getItemLabel(item) : item.label;
28
28
  if (label == null) {
29
- throw new Error(['MUI: 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'));
29
+ 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'));
30
30
  }
31
31
  nodeMap[id] = {
32
32
  id,
@@ -124,6 +124,11 @@ const useTreeViewNodes = ({
124
124
  getNavigableChildrenIds,
125
125
  isNodeDisabled
126
126
  });
127
+ return {
128
+ contextValue: {
129
+ disabledItemsFocusable: params.disabledItemsFocusable
130
+ }
131
+ };
127
132
  };
128
133
  exports.useTreeViewNodes = useTreeViewNodes;
129
134
  useTreeViewNodes.getInitialState = params => updateState({
@@ -131,4 +136,14 @@ useTreeViewNodes.getInitialState = params => updateState({
131
136
  isItemDisabled: params.isItemDisabled,
132
137
  getItemId: params.getItemId,
133
138
  getItemLabel: params.getItemLabel
134
- });
139
+ });
140
+ useTreeViewNodes.getDefaultizedParams = params => (0, _extends2.default)({}, params, {
141
+ disabledItemsFocusable: params.disabledItemsFocusable ?? false
142
+ });
143
+ useTreeViewNodes.params = {
144
+ disabledItemsFocusable: true,
145
+ items: true,
146
+ isItemDisabled: true,
147
+ getItemLabel: true,
148
+ getItemId: true
149
+ };
@@ -19,7 +19,6 @@ const useTreeViewSelection = ({
19
19
  const lastSelectedNode = React.useRef(null);
20
20
  const lastSelectionWasRange = React.useRef(false);
21
21
  const currentRangeSelection = React.useRef([]);
22
- const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
23
22
  const setSelectedNodes = (event, newSelectedNodes) => {
24
23
  if (params.onNodeSelectionToggle) {
25
24
  if (params.multiSelect) {
@@ -43,8 +42,9 @@ const useTreeViewSelection = ({
43
42
  if (params.onSelectedNodesChange) {
44
43
  params.onSelectedNodesChange(event, newSelectedNodes);
45
44
  }
46
- models.selectedNodes.setValue(newSelectedNodes);
45
+ models.selectedNodes.setControlledValue(newSelectedNodes);
47
46
  };
47
+ const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
48
48
  const selectNode = (event, nodeId, multiple = false) => {
49
49
  if (params.disableSelection) {
50
50
  return;
@@ -174,14 +174,18 @@ const useTreeViewSelection = ({
174
174
  return {
175
175
  getRootProps: () => ({
176
176
  'aria-multiselectable': params.multiSelect
177
- })
177
+ }),
178
+ contextValue: {
179
+ selection: {
180
+ multiSelect: params.multiSelect
181
+ }
182
+ }
178
183
  };
179
184
  };
180
185
  exports.useTreeViewSelection = useTreeViewSelection;
181
186
  useTreeViewSelection.models = {
182
187
  selectedNodes: {
183
- controlledProp: 'selectedNodes',
184
- defaultProp: 'defaultSelectedNodes'
188
+ getDefaultValue: params => params.defaultSelectedNodes
185
189
  }
186
190
  };
187
191
  const DEFAULT_SELECTED_NODES = [];
@@ -189,4 +193,12 @@ useTreeViewSelection.getDefaultizedParams = params => (0, _extends2.default)({},
189
193
  disableSelection: params.disableSelection ?? false,
190
194
  multiSelect: params.multiSelect ?? false,
191
195
  defaultSelectedNodes: params.defaultSelectedNodes ?? (params.multiSelect ? DEFAULT_SELECTED_NODES : null)
192
- });
196
+ });
197
+ useTreeViewSelection.params = {
198
+ disableSelection: true,
199
+ multiSelect: true,
200
+ defaultSelectedNodes: true,
201
+ selectedNodes: true,
202
+ onSelectedNodesChange: true,
203
+ onNodeSelectionToggle: true
204
+ };
@@ -35,11 +35,15 @@ const useTreeView = inParams => {
35
35
  return temp;
36
36
  });
37
37
  const rootPropsGetters = [];
38
- let contextValue = {};
38
+ const contextValue = {
39
+ instance: instance
40
+ };
39
41
  const runPlugin = plugin => {
40
42
  const pluginResponse = plugin({
41
43
  instance,
42
44
  params,
45
+ slots: params.slots,
46
+ slotProps: params.slotProps,
43
47
  state,
44
48
  setState,
45
49
  rootRef: innerRootRef,
@@ -49,7 +53,7 @@ const useTreeView = inParams => {
49
53
  rootPropsGetters.push(pluginResponse.getRootProps);
50
54
  }
51
55
  if (pluginResponse.contextValue) {
52
- contextValue = pluginResponse.contextValue;
56
+ Object.assign(contextValue, pluginResponse.contextValue);
53
57
  }
54
58
  };
55
59
  plugins.forEach(runPlugin);
@@ -105,7 +109,7 @@ const useTreeView = inParams => {
105
109
  return {
106
110
  getRootProps,
107
111
  rootRef: handleRootRef,
108
- contextValue,
112
+ contextValue: contextValue,
109
113
  instance: instance
110
114
  };
111
115
  };
@@ -19,23 +19,22 @@ const useTreeViewModels = (plugins, props) => {
19
19
  const initialState = {};
20
20
  plugins.forEach(plugin => {
21
21
  if (plugin.models) {
22
- Object.entries(plugin.models).forEach(([modelName, model]) => {
22
+ Object.entries(plugin.models).forEach(([modelName, modelInitializer]) => {
23
23
  modelsRef.current[modelName] = {
24
- controlledProp: model.controlledProp,
25
- defaultProp: model.defaultProp,
26
- isControlled: props[model.controlledProp] !== undefined
24
+ isControlled: props[modelName] !== undefined,
25
+ getDefaultValue: modelInitializer.getDefaultValue
27
26
  };
28
- initialState[modelName] = props[model.defaultProp];
27
+ initialState[modelName] = modelInitializer.getDefaultValue(props);
29
28
  });
30
29
  }
31
30
  });
32
31
  return initialState;
33
32
  });
34
33
  const models = Object.fromEntries(Object.entries(modelsRef.current).map(([modelName, model]) => {
35
- const value = model.isControlled ? props[model.controlledProp] : modelsState[modelName];
34
+ const value = model.isControlled ? props[modelName] : modelsState[modelName];
36
35
  return [modelName, {
37
36
  value,
38
- setValue: newValue => {
37
+ setControlledValue: newValue => {
39
38
  if (!model.isControlled) {
40
39
  setModelsState(prevState => (0, _extends2.default)({}, prevState, {
41
40
  [modelName]: newValue
@@ -49,19 +48,19 @@ const useTreeViewModels = (plugins, props) => {
49
48
  /* eslint-disable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */
50
49
  if (process.env.NODE_ENV !== 'production') {
51
50
  Object.entries(modelsRef.current).forEach(([modelName, model]) => {
52
- const controlled = props[model.controlledProp];
53
- const defaultProp = props[model.defaultProp];
51
+ const controlled = props[modelName];
52
+ const newDefaultValue = model.getDefaultValue(props);
54
53
  React.useEffect(() => {
55
54
  if (model.isControlled !== (controlled !== undefined)) {
56
- console.error([`MUI: A component is changing the ${model.isControlled ? '' : 'un'}controlled ${modelName} state of TreeView to be ${model.isControlled ? 'un' : ''}controlled.`, 'Elements should not switch from uncontrolled to controlled (or vice versa).', `Decide between using a controlled or uncontrolled ${modelName} ` + 'element for the lifetime of the component.', "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", 'More info: https://fb.me/react-controlled-components'].join('\n'));
55
+ console.error([`MUI X: A component is changing the ${model.isControlled ? '' : 'un'}controlled ${modelName} state of TreeView to be ${model.isControlled ? 'un' : ''}controlled.`, 'Elements should not switch from uncontrolled to controlled (or vice versa).', `Decide between using a controlled or uncontrolled ${modelName} ` + 'element for the lifetime of the component.', "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", 'More info: https://fb.me/react-controlled-components'].join('\n'));
57
56
  }
58
57
  }, [controlled]);
59
58
  const {
60
59
  current: defaultValue
61
- } = React.useRef(defaultProp);
60
+ } = React.useRef(newDefaultValue);
62
61
  React.useEffect(() => {
63
- if (!model.isControlled && defaultValue !== defaultProp) {
64
- console.error([`MUI: A component is changing the default ${modelName} state of an uncontrolled TreeView after being initialized. ` + `To suppress this warning opt to use a controlled TreeView.`].join('\n'));
62
+ if (!model.isControlled && defaultValue !== newDefaultValue) {
63
+ console.error([`MUI X: A component is changing the default ${modelName} state of an uncontrolled TreeView after being initialized. ` + `To suppress this warning opt to use a controlled TreeView.`].join('\n'));
65
64
  }
66
65
  }, [JSON.stringify(defaultValue)]);
67
66
  });
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.extractPluginParamsFromProps = void 0;
8
+ var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
9
+ const _excluded = ["slots", "slotProps"];
10
+ const extractPluginParamsFromProps = _ref => {
11
+ let {
12
+ props: {
13
+ slots,
14
+ slotProps
15
+ },
16
+ plugins,
17
+ rootRef
18
+ } = _ref,
19
+ props = (0, _objectWithoutPropertiesLoose2.default)(_ref.props, _excluded);
20
+ const paramsLookup = {};
21
+ plugins.forEach(plugin => {
22
+ Object.assign(paramsLookup, plugin.params);
23
+ });
24
+ const pluginParams = {
25
+ plugins,
26
+ rootRef,
27
+ slots: slots ?? {},
28
+ slotProps: slotProps ?? {}
29
+ };
30
+ const otherProps = {};
31
+ Object.keys(props).forEach(propName => {
32
+ const prop = props[propName];
33
+ if (paramsLookup[propName]) {
34
+ pluginParams[propName] = prop;
35
+ } else {
36
+ otherProps[propName] = prop;
37
+ }
38
+ });
39
+ return {
40
+ pluginParams,
41
+ slots,
42
+ slotProps,
43
+ otherProps
44
+ };
45
+ };
46
+ exports.extractPluginParamsFromProps = extractPluginParamsFromProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-tree-view",
3
- "version": "7.0.0-alpha.7",
3
+ "version": "7.0.0-alpha.9",
4
4
  "description": "The community edition of the tree view components (MUI X).",
5
5
  "author": "MUI Team",
6
6
  "main": "./node/index.js",
@@ -32,19 +32,19 @@
32
32
  "directory": "packages/x-tree-view"
33
33
  },
34
34
  "dependencies": {
35
- "@babel/runtime": "^7.23.7",
36
- "@mui/base": "^5.0.0-beta.29",
37
- "@mui/system": "^5.15.2",
38
- "@mui/utils": "^5.15.2",
35
+ "@babel/runtime": "^7.23.8",
36
+ "@mui/base": "^5.0.0-beta.32",
37
+ "@mui/system": "^5.15.5",
38
+ "@mui/utils": "^5.15.5",
39
39
  "@types/react-transition-group": "^4.4.10",
40
- "clsx": "^2.0.0",
40
+ "clsx": "^2.1.0",
41
41
  "prop-types": "^15.8.1",
42
42
  "react-transition-group": "^4.4.5"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@emotion/react": "^11.9.0",
46
46
  "@emotion/styled": "^11.8.1",
47
- "@mui/material": "^5.8.6",
47
+ "@mui/material": "^5.15.0",
48
48
  "react": "^17.0.0 || ^18.0.0",
49
49
  "react-dom": "^17.0.0 || ^18.0.0"
50
50
  },
@@ -4,22 +4,22 @@ export interface TreeViewComponents<Theme = unknown> {
4
4
  MuiSimpleTreeView?: {
5
5
  defaultProps?: ComponentsProps['MuiSimpleTreeView'];
6
6
  styleOverrides?: ComponentsOverrides<Theme>['MuiSimpleTreeView'];
7
- variants?: ComponentsVariants['MuiSimpleTreeView'];
7
+ variants?: ComponentsVariants<Theme>['MuiSimpleTreeView'];
8
8
  };
9
9
  MuiRichTreeView?: {
10
10
  defaultProps?: ComponentsProps['MuiRichTreeView'];
11
11
  styleOverrides?: ComponentsOverrides<Theme>['MuiRichTreeView'];
12
- variants?: ComponentsVariants['MuiRichTreeView'];
12
+ variants?: ComponentsVariants<Theme>['MuiRichTreeView'];
13
13
  };
14
14
  MuiTreeView?: {
15
15
  defaultProps?: ComponentsProps['MuiTreeView'];
16
16
  styleOverrides?: ComponentsOverrides<Theme>['MuiTreeView'];
17
- variants?: ComponentsVariants['MuiTreeView'];
17
+ variants?: ComponentsVariants<Theme>['MuiTreeView'];
18
18
  };
19
19
  MuiTreeItem?: {
20
20
  defaultProps?: ComponentsProps['MuiTreeItem'];
21
21
  styleOverrides?: ComponentsOverrides<Theme>['MuiTreeItem'];
22
- variants?: ComponentsVariants['MuiTreeItem'];
22
+ variants?: ComponentsVariants<Theme>['MuiTreeItem'];
23
23
  };
24
24
  }
25
25
 
@@ -1,2 +0,0 @@
1
- export { useTreeViewContextValueBuilder } from './useTreeViewContextValueBuilder';
2
- export type { UseTreeViewContextValueBuilderSignature, UseTreeViewContextValueBuilderParameters, UseTreeViewContextValueBuilderDefaultizedParameters, } from './useTreeViewContextValueBuilder.types';
@@ -1 +0,0 @@
1
- export { useTreeViewContextValueBuilder } from './useTreeViewContextValueBuilder';
@@ -1,3 +0,0 @@
1
- import { TreeViewPlugin } from '../../models';
2
- import { UseTreeViewContextValueBuilderSignature } from './useTreeViewContextValueBuilder.types';
3
- export declare const useTreeViewContextValueBuilder: TreeViewPlugin<UseTreeViewContextValueBuilderSignature>;