@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
@@ -1,8 +1,10 @@
1
1
  import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext';
2
- export function useTreeItem(nodeId) {
2
+ export function useTreeItemState(nodeId) {
3
3
  const {
4
4
  instance,
5
- multiSelect
5
+ selection: {
6
+ multiSelect
7
+ }
6
8
  } = useTreeViewContext();
7
9
  const expandable = instance.isNodeExpandable(nodeId);
8
10
  const expanded = instance.isNodeExpanded(nodeId);
@@ -23,7 +23,7 @@ const TreeViewRoot = styled(SimpleTreeViewRoot, {
23
23
  let warnedOnce = false;
24
24
  const warn = () => {
25
25
  if (!warnedOnce) {
26
- console.warn(['MUI: The TreeView component was renamed SimpleTreeView.', 'The component with the old naming will be removed in the version v8.0.0.', '', "You should use `import { SimpleTreeView } from '@mui/x-tree-view'`", "or `import { SimpleTreeView } from '@mui/x-tree-view/TreeView'`"].join('\n'));
26
+ console.warn(['MUI X: The TreeView component was renamed SimpleTreeView.', 'The component with the old naming will be removed in the version v8.0.0.', '', "You should use `import { SimpleTreeView } from '@mui/x-tree-view'`", "or `import { SimpleTreeView } from '@mui/x-tree-view/TreeView'`"].join('\n'));
27
27
  warnedOnce = true;
28
28
  }
29
29
  };
@@ -72,34 +72,13 @@ process.env.NODE_ENV !== "production" ? TreeView.propTypes = {
72
72
  * Override or extend the styles applied to the component.
73
73
  */
74
74
  classes: PropTypes.object,
75
- /**
76
- * className applied to the root element.
77
- */
78
75
  className: PropTypes.string,
79
- /**
80
- * The default icon used to collapse the node.
81
- */
82
- defaultCollapseIcon: PropTypes.node,
83
- /**
84
- * The default icon displayed next to a end node. This is applied to all
85
- * tree nodes and can be overridden by the TreeItem `icon` prop.
86
- */
87
- defaultEndIcon: PropTypes.node,
88
76
  /**
89
77
  * Expanded node ids.
90
78
  * Used when the item's expansion is not controlled.
91
79
  * @default []
92
80
  */
93
81
  defaultExpandedNodes: PropTypes.arrayOf(PropTypes.string),
94
- /**
95
- * The default icon used to expand the node.
96
- */
97
- defaultExpandIcon: PropTypes.node,
98
- /**
99
- * The default icon displayed next to a parent node. This is applied to all
100
- * parent nodes and can be overridden by the TreeItem `icon` prop.
101
- */
102
- defaultParentIcon: PropTypes.node,
103
82
  /**
104
83
  * Selected node ids. (Uncontrolled)
105
84
  * When `multiSelect` is true this takes an array of strings; when false (default) a string.
@@ -0,0 +1,9 @@
1
+ import { createSvgIcon } from '@mui/material/utils';
2
+ import * as React from 'react';
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ export const TreeViewExpandIcon = createSvgIcon( /*#__PURE__*/_jsx("path", {
5
+ d: "M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"
6
+ }), 'TreeViewExpandIcon');
7
+ export const TreeViewCollapseIcon = createSvgIcon( /*#__PURE__*/_jsx("path", {
8
+ d: "M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z"
9
+ }), 'TreeViewCollapseIcon');
@@ -0,0 +1 @@
1
+ export * from './icons';
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v7.0.0-alpha.7
2
+ * @mui/x-tree-view v7.0.0-alpha.9
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -10,4 +10,5 @@ export * from './TreeView';
10
10
  export * from './SimpleTreeView';
11
11
  export * from './RichTreeView';
12
12
  export { unstable_resetCleanupTracking } from './internals/hooks/useInstanceEventHandler';
13
- export * from './models';
13
+ export * from './models';
14
+ export * from './icons';
@@ -3,7 +3,7 @@ import { TreeViewContext } from './TreeViewContext';
3
3
  export const useTreeViewContext = () => {
4
4
  const context = React.useContext(TreeViewContext);
5
5
  if (context == null) {
6
- throw new Error(['MUI: Could not find the Tree View context.', 'It looks like you rendered your component outside of a SimpleTreeView or RichTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
6
+ throw new Error(['MUI X: Could not find the Tree View context.', 'It looks like you rendered your component outside of a SimpleTreeView or RichTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
7
7
  }
8
8
  return context;
9
9
  };
@@ -26,4 +26,5 @@ export const useTreeViewInstanceEvents = ({
26
26
  $$publishEvent: publishEvent,
27
27
  $$subscribeEvent: subscribeEvent
28
28
  });
29
- };
29
+ };
30
+ useTreeViewInstanceEvents.params = {};
@@ -0,0 +1,4 @@
1
+ export { useTreeView } from './useTreeView';
2
+ export { TreeViewProvider } from './TreeViewProvider';
3
+ export { DEFAULT_TREE_VIEW_PLUGINS } from './plugins/defaultPlugins';
4
+ export { extractPluginParamsFromProps } from './utils/extractPluginParamsFromProps';
@@ -0,0 +1 @@
1
+ export {};
@@ -4,7 +4,7 @@ import { useTreeViewExpansion } from './useTreeViewExpansion';
4
4
  import { useTreeViewSelection } from './useTreeViewSelection';
5
5
  import { useTreeViewFocus } from './useTreeViewFocus';
6
6
  import { useTreeViewKeyboardNavigation } from './useTreeViewKeyboardNavigation';
7
- import { useTreeViewContextValueBuilder } from './useTreeViewContextValueBuilder';
8
- export const DEFAULT_TREE_VIEW_PLUGINS = [useTreeViewId, useTreeViewNodes, useTreeViewExpansion, useTreeViewSelection, useTreeViewFocus, useTreeViewKeyboardNavigation, useTreeViewContextValueBuilder];
7
+ import { useTreeViewIcons } from './useTreeViewIcons';
8
+ export const DEFAULT_TREE_VIEW_PLUGINS = [useTreeViewId, useTreeViewNodes, useTreeViewExpansion, useTreeViewSelection, useTreeViewFocus, useTreeViewKeyboardNavigation, useTreeViewIcons];
9
9
 
10
10
  // We can't infer this type from the plugin, otherwise we would lose the generics.
@@ -7,6 +7,10 @@ export const useTreeViewExpansion = ({
7
7
  params,
8
8
  models
9
9
  }) => {
10
+ const setExpandedNodes = (event, value) => {
11
+ params.onExpandedNodesChange?.(event, value);
12
+ models.expandedNodes.setControlledValue(value);
13
+ };
10
14
  const isNodeExpanded = React.useCallback(nodeId => {
11
15
  return Array.isArray(models.expandedNodes.value) ? models.expandedNodes.value.indexOf(nodeId) !== -1 : false;
12
16
  }, [models.expandedNodes.value]);
@@ -25,10 +29,7 @@ export const useTreeViewExpansion = ({
25
29
  if (params.onNodeExpansionToggle) {
26
30
  params.onNodeExpansionToggle(event, nodeId, !isExpandedBefore);
27
31
  }
28
- if (params.onExpandedNodesChange) {
29
- params.onExpandedNodesChange(event, newExpanded);
30
- }
31
- models.expandedNodes.setValue(newExpanded);
32
+ setExpandedNodes(event, newExpanded);
32
33
  });
33
34
  const expandAllSiblings = (event, nodeId) => {
34
35
  const node = instance.getNode(nodeId);
@@ -41,10 +42,7 @@ export const useTreeViewExpansion = ({
41
42
  params.onNodeExpansionToggle(event, newlyExpandedNodeId, true);
42
43
  });
43
44
  }
44
- if (params.onExpandedNodesChange) {
45
- params.onExpandedNodesChange(event, newExpanded);
46
- }
47
- models.expandedNodes.setValue(newExpanded);
45
+ setExpandedNodes(event, newExpanded);
48
46
  }
49
47
  };
50
48
  populateInstance(instance, {
@@ -56,11 +54,16 @@ export const useTreeViewExpansion = ({
56
54
  };
57
55
  useTreeViewExpansion.models = {
58
56
  expandedNodes: {
59
- controlledProp: 'expandedNodes',
60
- defaultProp: 'defaultExpandedNodes'
57
+ getDefaultValue: params => params.defaultExpandedNodes
61
58
  }
62
59
  };
63
60
  const DEFAULT_EXPANDED_NODES = [];
64
61
  useTreeViewExpansion.getDefaultizedParams = params => _extends({}, params, {
65
62
  defaultExpandedNodes: params.defaultExpandedNodes ?? DEFAULT_EXPANDED_NODES
66
- });
63
+ });
64
+ useTreeViewExpansion.params = {
65
+ expandedNodes: true,
66
+ defaultExpandedNodes: true,
67
+ onExpandedNodesChange: true,
68
+ onNodeExpansionToggle: true
69
+ };
@@ -85,6 +85,6 @@ export const useTreeViewFocus = ({
85
85
  useTreeViewFocus.getInitialState = () => ({
86
86
  focusedNodeId: null
87
87
  });
88
- useTreeViewFocus.getDefaultizedParams = params => _extends({}, params, {
89
- disabledItemsFocusable: params.disabledItemsFocusable ?? false
90
- });
88
+ useTreeViewFocus.params = {
89
+ onNodeFocus: true
90
+ };
@@ -0,0 +1 @@
1
+ export { useTreeViewIcons } from './useTreeViewIcons';
@@ -0,0 +1,22 @@
1
+ export const useTreeViewIcons = ({
2
+ slots,
3
+ slotProps
4
+ }) => {
5
+ return {
6
+ contextValue: {
7
+ icons: {
8
+ slots: {
9
+ collapseIcon: slots.collapseIcon,
10
+ expandIcon: slots.expandIcon,
11
+ endIcon: slots.endIcon
12
+ },
13
+ slotProps: {
14
+ collapseIcon: slotProps.collapseIcon,
15
+ expandIcon: slotProps.expandIcon,
16
+ endIcon: slotProps.endIcon
17
+ }
18
+ }
19
+ }
20
+ };
21
+ };
22
+ useTreeViewIcons.params = {};
@@ -15,4 +15,7 @@ export const useTreeViewId = ({
15
15
  id: treeId
16
16
  })
17
17
  };
18
+ };
19
+ useTreeViewId.params = {
20
+ id: true
18
21
  };
@@ -110,4 +110,5 @@ const useTreeViewJSXNodesItemPlugin = ({
110
110
  })
111
111
  };
112
112
  };
113
- useTreeViewJSXNodes.itemPlugin = useTreeViewJSXNodesItemPlugin;
113
+ useTreeViewJSXNodes.itemPlugin = useTreeViewJSXNodesItemPlugin;
114
+ useTreeViewJSXNodes.params = {};
@@ -3,7 +3,7 @@ import { useTheme } from '@mui/material/styles';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
4
  import { getFirstNode, getLastNode, getNextNode, getPreviousNode, populateInstance } from '../../useTreeView/useTreeView.utils';
5
5
  function isPrintableCharacter(string) {
6
- return string && string.length === 1 && string.match(/\S/);
6
+ return !!string && string.length === 1 && !!string.match(/\S/);
7
7
  }
8
8
  function findNextFirstChar(firstChars, startIndex, char) {
9
9
  for (let i = startIndex; i < firstChars.length; i += 1) {
@@ -19,7 +19,7 @@ export const useTreeViewKeyboardNavigation = ({
19
19
  state
20
20
  }) => {
21
21
  const theme = useTheme();
22
- const isRtl = theme.direction === 'rtl';
22
+ const isRTL = theme.direction === 'rtl';
23
23
  const firstCharMap = React.useRef({});
24
24
  const hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
25
25
  const updateFirstCharMap = useEventCallback(callback => {
@@ -43,32 +43,7 @@ export const useTreeViewKeyboardNavigation = ({
43
43
  populateInstance(instance, {
44
44
  updateFirstCharMap
45
45
  });
46
- const handleNextArrow = event => {
47
- if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
48
- if (instance.isNodeExpanded(state.focusedNodeId)) {
49
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
50
- } else if (!instance.isNodeDisabled(state.focusedNodeId)) {
51
- instance.toggleNodeExpansion(event, state.focusedNodeId);
52
- }
53
- }
54
- return true;
55
- };
56
- const handlePreviousArrow = event => {
57
- if (state.focusedNodeId == null) {
58
- return false;
59
- }
60
- if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
61
- instance.toggleNodeExpansion(event, state.focusedNodeId);
62
- return true;
63
- }
64
- const parent = instance.getNode(state.focusedNodeId).parentId;
65
- if (parent) {
66
- instance.focusNode(event, parent);
67
- return true;
68
- }
69
- return false;
70
- };
71
- const focusByFirstCharacter = (event, nodeId, firstChar) => {
46
+ const getFirstMatchingNode = (nodeId, firstChar) => {
72
47
  let start;
73
48
  let index;
74
49
  const lowercaseChar = firstChar.toLowerCase();
@@ -99,41 +74,35 @@ export const useTreeViewKeyboardNavigation = ({
99
74
  index = findNextFirstChar(firstChars, 0, lowercaseChar);
100
75
  }
101
76
 
102
- // If match was found...
77
+ // If a match was found...
103
78
  if (index > -1) {
104
- instance.focusNode(event, firstCharIds[index]);
105
- }
106
- };
107
- const selectNextNode = (event, id) => {
108
- if (!instance.isNodeDisabled(getNextNode(instance, id))) {
109
- instance.selectRange(event, {
110
- end: getNextNode(instance, id),
111
- current: id
112
- }, true);
113
- }
114
- };
115
- const selectPreviousNode = (event, nodeId) => {
116
- if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
117
- instance.selectRange(event, {
118
- end: getPreviousNode(instance, nodeId),
119
- current: nodeId
120
- }, true);
79
+ return firstCharIds[index];
121
80
  }
81
+ return null;
122
82
  };
83
+ const canToggleNodeSelection = nodeId => !params.disableSelection && !instance.isNodeDisabled(nodeId);
84
+ const canToggleNodeExpansion = nodeId => !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
85
+
86
+ // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
123
87
  const createHandleKeyDown = otherHandlers => event => {
124
88
  otherHandlers.onKeyDown?.(event);
125
- let flag = false;
126
- const key = event.key;
89
+ if (event.defaultMuiPrevented) {
90
+ return;
91
+ }
127
92
 
128
93
  // If the tree is empty there will be no focused node
129
94
  if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
130
95
  return;
131
96
  }
132
97
  const ctrlPressed = event.ctrlKey || event.metaKey;
133
- switch (key) {
134
- case ' ':
135
- if (!params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
136
- flag = true;
98
+ const key = event.key;
99
+
100
+ // eslint-disable-next-line default-case
101
+ switch (true) {
102
+ // Select the node when pressing "Space"
103
+ case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
104
+ {
105
+ event.preventDefault();
137
106
  if (params.multiSelect && event.shiftKey) {
138
107
  instance.selectRange(event, {
139
108
  end: state.focusedNodeId
@@ -143,85 +112,158 @@ export const useTreeViewKeyboardNavigation = ({
143
112
  } else {
144
113
  instance.selectNode(event, state.focusedNodeId);
145
114
  }
115
+ break;
146
116
  }
147
- event.stopPropagation();
148
- break;
149
- case 'Enter':
150
- if (!instance.isNodeDisabled(state.focusedNodeId)) {
151
- if (instance.isNodeExpandable(state.focusedNodeId)) {
117
+
118
+ // If the focused node has children, we expand it.
119
+ // If the focused node has no children, we select it.
120
+ case key === 'Enter':
121
+ {
122
+ if (canToggleNodeExpansion(state.focusedNodeId)) {
152
123
  instance.toggleNodeExpansion(event, state.focusedNodeId);
153
- flag = true;
154
- } else if (!params.disableSelection) {
155
- flag = true;
124
+ event.preventDefault();
125
+ } else if (canToggleNodeSelection(state.focusedNodeId)) {
156
126
  if (params.multiSelect) {
127
+ event.preventDefault();
157
128
  instance.selectNode(event, state.focusedNodeId, true);
158
- } else {
129
+ } else if (!instance.isNodeSelected(state.focusedNodeId)) {
159
130
  instance.selectNode(event, state.focusedNodeId);
131
+ event.preventDefault();
160
132
  }
161
133
  }
134
+ break;
162
135
  }
163
- event.stopPropagation();
164
- break;
165
- case 'ArrowDown':
166
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
167
- selectNextNode(event, state.focusedNodeId);
136
+
137
+ // Focus the next focusable node
138
+ case key === 'ArrowDown':
139
+ {
140
+ const nextNode = getNextNode(instance, state.focusedNodeId);
141
+ if (nextNode) {
142
+ event.preventDefault();
143
+ instance.focusNode(event, nextNode);
144
+
145
+ // Multi select behavior when pressing Shift + ArrowDown
146
+ // Toggles the selection state of the next node
147
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
148
+ instance.selectRange(event, {
149
+ end: nextNode,
150
+ current: state.focusedNodeId
151
+ }, true);
152
+ }
153
+ }
154
+ break;
168
155
  }
169
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
170
- flag = true;
171
- break;
172
- case 'ArrowUp':
173
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
174
- selectPreviousNode(event, state.focusedNodeId);
156
+
157
+ // Focuses the previous focusable node
158
+ case key === 'ArrowUp':
159
+ {
160
+ const previousNode = getPreviousNode(instance, state.focusedNodeId);
161
+ if (previousNode) {
162
+ event.preventDefault();
163
+ instance.focusNode(event, previousNode);
164
+
165
+ // Multi select behavior when pressing Shift + ArrowUp
166
+ // Toggles the selection state of the previous node
167
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
168
+ instance.selectRange(event, {
169
+ end: previousNode,
170
+ current: state.focusedNodeId
171
+ }, true);
172
+ }
173
+ }
174
+ break;
175
175
  }
176
- instance.focusNode(event, getPreviousNode(instance, state.focusedNodeId));
177
- flag = true;
178
- break;
179
- case 'ArrowRight':
180
- if (isRtl) {
181
- flag = handlePreviousArrow(event);
182
- } else {
183
- flag = handleNextArrow(event);
176
+
177
+ // If the focused node is expanded, we move the focus to its first child
178
+ // If the focused node is collapsed and has children, we expand it
179
+ case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
180
+ {
181
+ if (instance.isNodeExpanded(state.focusedNodeId)) {
182
+ instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
183
+ event.preventDefault();
184
+ } else if (canToggleNodeExpansion(state.focusedNodeId)) {
185
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
186
+ event.preventDefault();
187
+ }
188
+ break;
184
189
  }
185
- break;
186
- case 'ArrowLeft':
187
- if (isRtl) {
188
- flag = handleNextArrow(event);
189
- } else {
190
- flag = handlePreviousArrow(event);
190
+
191
+ // If the focused node is expanded, we collapse it
192
+ // If the focused node is collapsed and has a parent, we move the focus to this parent
193
+ case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
194
+ {
195
+ if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
196
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
197
+ event.preventDefault();
198
+ } else {
199
+ const parent = instance.getNode(state.focusedNodeId).parentId;
200
+ if (parent) {
201
+ instance.focusNode(event, parent);
202
+ event.preventDefault();
203
+ }
204
+ }
205
+ break;
191
206
  }
192
- break;
193
- case 'Home':
194
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
195
- instance.rangeSelectToFirst(event, state.focusedNodeId);
207
+
208
+ // Focuses the first node in the tree
209
+ case key === 'Home':
210
+ {
211
+ instance.focusNode(event, getFirstNode(instance));
212
+
213
+ // Multi select behavior when pressing Ctrl + Shift + Home
214
+ // Selects the focused node and all nodes up to the first node.
215
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
216
+ instance.rangeSelectToFirst(event, state.focusedNodeId);
217
+ }
218
+ event.preventDefault();
219
+ break;
196
220
  }
197
- instance.focusNode(event, getFirstNode(instance));
198
- flag = true;
199
- break;
200
- case 'End':
201
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
202
- instance.rangeSelectToLast(event, state.focusedNodeId);
221
+
222
+ // Focuses the last node in the tree
223
+ case key === 'End':
224
+ {
225
+ instance.focusNode(event, getLastNode(instance));
226
+
227
+ // Multi select behavior when pressing Ctrl + Shirt + End
228
+ // Selects the focused node and all the nodes down to the last node.
229
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
230
+ instance.rangeSelectToLast(event, state.focusedNodeId);
231
+ }
232
+ event.preventDefault();
233
+ break;
203
234
  }
204
- instance.focusNode(event, getLastNode(instance));
205
- flag = true;
206
- break;
207
- default:
208
- if (key === '*') {
235
+
236
+ // Expand all siblings that are at the same level as the focused node
237
+ case key === '*':
238
+ {
209
239
  instance.expandAllSiblings(event, state.focusedNodeId);
210
- flag = true;
211
- } else if (params.multiSelect && ctrlPressed && key.toLowerCase() === 'a' && !params.disableSelection) {
240
+ event.preventDefault();
241
+ break;
242
+ }
243
+
244
+ // Multi select behavior when pressing Ctrl + a
245
+ // Selects all the nodes
246
+ case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
247
+ {
212
248
  instance.selectRange(event, {
213
249
  start: getFirstNode(instance),
214
250
  end: getLastNode(instance)
215
251
  });
216
- flag = true;
217
- } else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
218
- focusByFirstCharacter(event, state.focusedNodeId, key);
219
- flag = true;
252
+ event.preventDefault();
253
+ break;
254
+ }
255
+
256
+ // Type-ahead
257
+ // TODO: Support typing multiple characters
258
+ case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
259
+ {
260
+ const matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
261
+ if (matchingNode != null) {
262
+ instance.focusNode(event, matchingNode);
263
+ event.preventDefault();
264
+ }
265
+ break;
220
266
  }
221
- }
222
- if (flag) {
223
- event.preventDefault();
224
- event.stopPropagation();
225
267
  }
226
268
  };
227
269
  return {
@@ -229,4 +271,5 @@ export const useTreeViewKeyboardNavigation = ({
229
271
  onKeyDown: createHandleKeyDown(otherHandlers)
230
272
  })
231
273
  };
232
- };
274
+ };
275
+ useTreeViewKeyboardNavigation.params = {};
@@ -13,11 +13,11 @@ const updateState = ({
13
13
  const processItem = (item, index, parentId) => {
14
14
  const id = getItemId ? getItemId(item) : item.id;
15
15
  if (id == null) {
16
- 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'));
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
17
  }
18
18
  const label = getItemLabel ? getItemLabel(item) : item.label;
19
19
  if (label == null) {
20
- 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'));
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
21
  }
22
22
  nodeMap[id] = {
23
23
  id,
@@ -115,10 +115,25 @@ export const useTreeViewNodes = ({
115
115
  getNavigableChildrenIds,
116
116
  isNodeDisabled
117
117
  });
118
+ return {
119
+ contextValue: {
120
+ disabledItemsFocusable: params.disabledItemsFocusable
121
+ }
122
+ };
118
123
  };
119
124
  useTreeViewNodes.getInitialState = params => updateState({
120
125
  items: params.items,
121
126
  isItemDisabled: params.isItemDisabled,
122
127
  getItemId: params.getItemId,
123
128
  getItemLabel: params.getItemLabel
124
- });
129
+ });
130
+ useTreeViewNodes.getDefaultizedParams = params => _extends({}, params, {
131
+ disabledItemsFocusable: params.disabledItemsFocusable ?? false
132
+ });
133
+ useTreeViewNodes.params = {
134
+ disabledItemsFocusable: true,
135
+ items: true,
136
+ isItemDisabled: true,
137
+ getItemLabel: true,
138
+ getItemId: true
139
+ };