@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
@@ -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 = {};
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { SlotComponentProps } from '@mui/base/utils';
3
+ import { TreeViewPluginSignature } from '../../models';
4
+ import { UseTreeViewNodesSignature } from '../useTreeViewNodes';
5
+ import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
6
+ export interface UseTreeViewIconsParameters {
7
+ }
8
+ export type UseTreeViewIconsDefaultizedParameters = UseTreeViewIconsParameters;
9
+ interface UseTreeViewIconsSlots {
10
+ /**
11
+ * The default icon used to collapse the node.
12
+ */
13
+ collapseIcon?: React.ElementType;
14
+ /**
15
+ * The default icon used to expand the node.
16
+ */
17
+ expandIcon?: React.ElementType;
18
+ /**
19
+ * The default icon displayed next to an end node.
20
+ * This is applied to all tree nodes and can be overridden by the TreeItem `icon` slot prop.
21
+ */
22
+ endIcon?: React.ElementType;
23
+ }
24
+ interface UseTreeViewIconsSlotProps {
25
+ collapseIcon?: SlotComponentProps<'svg', {}, {}>;
26
+ expandIcon?: SlotComponentProps<'svg', {}, {}>;
27
+ endIcon?: SlotComponentProps<'svg', {}, {}>;
28
+ }
29
+ interface UseTreeViewIconsContextValue {
30
+ icons: {
31
+ slots: UseTreeViewIconsSlots;
32
+ slotProps: UseTreeViewIconsSlotProps;
33
+ };
34
+ }
35
+ export type UseTreeViewIconsSignature = TreeViewPluginSignature<{
36
+ params: UseTreeViewIconsParameters;
37
+ defaultizedParams: UseTreeViewIconsDefaultizedParameters;
38
+ contextValue: UseTreeViewIconsContextValue;
39
+ slots: UseTreeViewIconsSlots;
40
+ slotProps: UseTreeViewIconsSlotProps;
41
+ dependantPlugins: [UseTreeViewNodesSignature, UseTreeViewSelectionSignature];
42
+ }>;
43
+ export {};
@@ -15,4 +15,7 @@ export const useTreeViewId = ({
15
15
  id: treeId
16
16
  })
17
17
  };
18
+ };
19
+ useTreeViewId.params = {
20
+ id: true
18
21
  };
@@ -13,5 +13,9 @@ export type UseTreeViewIdDefaultizedParameters = UseTreeViewIdParameters;
13
13
  export interface UseTreeViewIdState {
14
14
  focusedNodeId: string | null;
15
15
  }
16
- export type UseTreeViewIdSignature = TreeViewPluginSignature<UseTreeViewIdParameters, UseTreeViewIdDefaultizedParameters, UseTreeViewIdInstance, {}, UseTreeViewIdState, never, [
17
- ]>;
16
+ export type UseTreeViewIdSignature = TreeViewPluginSignature<{
17
+ params: UseTreeViewIdParameters;
18
+ defaultizedParams: UseTreeViewIdDefaultizedParameters;
19
+ instance: UseTreeViewIdInstance;
20
+ state: UseTreeViewIdState;
21
+ }>;
@@ -111,4 +111,5 @@ const useTreeViewJSXNodesItemPlugin = ({
111
111
  })
112
112
  };
113
113
  };
114
- useTreeViewJSXNodes.itemPlugin = useTreeViewJSXNodesItemPlugin;
114
+ useTreeViewJSXNodes.itemPlugin = useTreeViewJSXNodesItemPlugin;
115
+ useTreeViewJSXNodes.params = {};
@@ -10,7 +10,9 @@ export interface UseTreeViewNodesParameters {
10
10
  }
11
11
  export interface UseTreeViewNodesDefaultizedParameters {
12
12
  }
13
- export type UseTreeViewJSXNodesSignature = TreeViewPluginSignature<UseTreeViewNodesParameters, UseTreeViewNodesDefaultizedParameters, UseTreeViewNodesInstance, {}, {}, never, [
14
- UseTreeViewNodesSignature,
15
- UseTreeViewKeyboardNavigationSignature
16
- ]>;
13
+ export type UseTreeViewJSXNodesSignature = TreeViewPluginSignature<{
14
+ params: UseTreeViewNodesParameters;
15
+ defaultizedParams: UseTreeViewNodesDefaultizedParameters;
16
+ instance: UseTreeViewNodesInstance;
17
+ dependantPlugins: [UseTreeViewNodesSignature, UseTreeViewKeyboardNavigationSignature];
18
+ }>;
@@ -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 => {
@@ -44,32 +44,7 @@ export const useTreeViewKeyboardNavigation = ({
44
44
  populateInstance(instance, {
45
45
  updateFirstCharMap
46
46
  });
47
- const handleNextArrow = event => {
48
- if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
49
- if (instance.isNodeExpanded(state.focusedNodeId)) {
50
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
51
- } else if (!instance.isNodeDisabled(state.focusedNodeId)) {
52
- instance.toggleNodeExpansion(event, state.focusedNodeId);
53
- }
54
- }
55
- return true;
56
- };
57
- const handlePreviousArrow = event => {
58
- if (state.focusedNodeId == null) {
59
- return false;
60
- }
61
- if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
62
- instance.toggleNodeExpansion(event, state.focusedNodeId);
63
- return true;
64
- }
65
- const parent = instance.getNode(state.focusedNodeId).parentId;
66
- if (parent) {
67
- instance.focusNode(event, parent);
68
- return true;
69
- }
70
- return false;
71
- };
72
- const focusByFirstCharacter = (event, nodeId, firstChar) => {
47
+ const getFirstMatchingNode = (nodeId, firstChar) => {
73
48
  let start;
74
49
  let index;
75
50
  const lowercaseChar = firstChar.toLowerCase();
@@ -100,42 +75,36 @@ export const useTreeViewKeyboardNavigation = ({
100
75
  index = findNextFirstChar(firstChars, 0, lowercaseChar);
101
76
  }
102
77
 
103
- // If match was found...
78
+ // If a match was found...
104
79
  if (index > -1) {
105
- instance.focusNode(event, firstCharIds[index]);
106
- }
107
- };
108
- const selectNextNode = (event, id) => {
109
- if (!instance.isNodeDisabled(getNextNode(instance, id))) {
110
- instance.selectRange(event, {
111
- end: getNextNode(instance, id),
112
- current: id
113
- }, true);
114
- }
115
- };
116
- const selectPreviousNode = (event, nodeId) => {
117
- if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
118
- instance.selectRange(event, {
119
- end: getPreviousNode(instance, nodeId),
120
- current: nodeId
121
- }, true);
80
+ return firstCharIds[index];
122
81
  }
82
+ return null;
123
83
  };
84
+ const canToggleNodeSelection = nodeId => !params.disableSelection && !instance.isNodeDisabled(nodeId);
85
+ const canToggleNodeExpansion = nodeId => !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
86
+
87
+ // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
124
88
  const createHandleKeyDown = otherHandlers => event => {
125
89
  var _otherHandlers$onKeyD;
126
90
  (_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null || _otherHandlers$onKeyD.call(otherHandlers, event);
127
- let flag = false;
128
- const key = event.key;
91
+ if (event.defaultMuiPrevented) {
92
+ return;
93
+ }
129
94
 
130
95
  // If the tree is empty there will be no focused node
131
96
  if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
132
97
  return;
133
98
  }
134
99
  const ctrlPressed = event.ctrlKey || event.metaKey;
135
- switch (key) {
136
- case ' ':
137
- if (!params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
138
- flag = true;
100
+ const key = event.key;
101
+
102
+ // eslint-disable-next-line default-case
103
+ switch (true) {
104
+ // Select the node when pressing "Space"
105
+ case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
106
+ {
107
+ event.preventDefault();
139
108
  if (params.multiSelect && event.shiftKey) {
140
109
  instance.selectRange(event, {
141
110
  end: state.focusedNodeId
@@ -145,85 +114,158 @@ export const useTreeViewKeyboardNavigation = ({
145
114
  } else {
146
115
  instance.selectNode(event, state.focusedNodeId);
147
116
  }
117
+ break;
148
118
  }
149
- event.stopPropagation();
150
- break;
151
- case 'Enter':
152
- if (!instance.isNodeDisabled(state.focusedNodeId)) {
153
- if (instance.isNodeExpandable(state.focusedNodeId)) {
119
+
120
+ // If the focused node has children, we expand it.
121
+ // If the focused node has no children, we select it.
122
+ case key === 'Enter':
123
+ {
124
+ if (canToggleNodeExpansion(state.focusedNodeId)) {
154
125
  instance.toggleNodeExpansion(event, state.focusedNodeId);
155
- flag = true;
156
- } else if (!params.disableSelection) {
157
- flag = true;
126
+ event.preventDefault();
127
+ } else if (canToggleNodeSelection(state.focusedNodeId)) {
158
128
  if (params.multiSelect) {
129
+ event.preventDefault();
159
130
  instance.selectNode(event, state.focusedNodeId, true);
160
- } else {
131
+ } else if (!instance.isNodeSelected(state.focusedNodeId)) {
161
132
  instance.selectNode(event, state.focusedNodeId);
133
+ event.preventDefault();
162
134
  }
163
135
  }
136
+ break;
164
137
  }
165
- event.stopPropagation();
166
- break;
167
- case 'ArrowDown':
168
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
169
- selectNextNode(event, state.focusedNodeId);
138
+
139
+ // Focus the next focusable node
140
+ case key === 'ArrowDown':
141
+ {
142
+ const nextNode = getNextNode(instance, state.focusedNodeId);
143
+ if (nextNode) {
144
+ event.preventDefault();
145
+ instance.focusNode(event, nextNode);
146
+
147
+ // Multi select behavior when pressing Shift + ArrowDown
148
+ // Toggles the selection state of the next node
149
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
150
+ instance.selectRange(event, {
151
+ end: nextNode,
152
+ current: state.focusedNodeId
153
+ }, true);
154
+ }
155
+ }
156
+ break;
170
157
  }
171
- instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
172
- flag = true;
173
- break;
174
- case 'ArrowUp':
175
- if (params.multiSelect && event.shiftKey && !params.disableSelection) {
176
- selectPreviousNode(event, state.focusedNodeId);
158
+
159
+ // Focuses the previous focusable node
160
+ case key === 'ArrowUp':
161
+ {
162
+ const previousNode = getPreviousNode(instance, state.focusedNodeId);
163
+ if (previousNode) {
164
+ event.preventDefault();
165
+ instance.focusNode(event, previousNode);
166
+
167
+ // Multi select behavior when pressing Shift + ArrowUp
168
+ // Toggles the selection state of the previous node
169
+ if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
170
+ instance.selectRange(event, {
171
+ end: previousNode,
172
+ current: state.focusedNodeId
173
+ }, true);
174
+ }
175
+ }
176
+ break;
177
177
  }
178
- instance.focusNode(event, getPreviousNode(instance, state.focusedNodeId));
179
- flag = true;
180
- break;
181
- case 'ArrowRight':
182
- if (isRtl) {
183
- flag = handlePreviousArrow(event);
184
- } else {
185
- flag = handleNextArrow(event);
178
+
179
+ // If the focused node is expanded, we move the focus to its first child
180
+ // If the focused node is collapsed and has children, we expand it
181
+ case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
182
+ {
183
+ if (instance.isNodeExpanded(state.focusedNodeId)) {
184
+ instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
185
+ event.preventDefault();
186
+ } else if (canToggleNodeExpansion(state.focusedNodeId)) {
187
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
188
+ event.preventDefault();
189
+ }
190
+ break;
186
191
  }
187
- break;
188
- case 'ArrowLeft':
189
- if (isRtl) {
190
- flag = handleNextArrow(event);
191
- } else {
192
- flag = handlePreviousArrow(event);
192
+
193
+ // If the focused node is expanded, we collapse it
194
+ // If the focused node is collapsed and has a parent, we move the focus to this parent
195
+ case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
196
+ {
197
+ if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
198
+ instance.toggleNodeExpansion(event, state.focusedNodeId);
199
+ event.preventDefault();
200
+ } else {
201
+ const parent = instance.getNode(state.focusedNodeId).parentId;
202
+ if (parent) {
203
+ instance.focusNode(event, parent);
204
+ event.preventDefault();
205
+ }
206
+ }
207
+ break;
193
208
  }
194
- break;
195
- case 'Home':
196
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
197
- instance.rangeSelectToFirst(event, state.focusedNodeId);
209
+
210
+ // Focuses the first node in the tree
211
+ case key === 'Home':
212
+ {
213
+ instance.focusNode(event, getFirstNode(instance));
214
+
215
+ // Multi select behavior when pressing Ctrl + Shift + Home
216
+ // Selects the focused node and all nodes up to the first node.
217
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
218
+ instance.rangeSelectToFirst(event, state.focusedNodeId);
219
+ }
220
+ event.preventDefault();
221
+ break;
198
222
  }
199
- instance.focusNode(event, getFirstNode(instance));
200
- flag = true;
201
- break;
202
- case 'End':
203
- if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
204
- instance.rangeSelectToLast(event, state.focusedNodeId);
223
+
224
+ // Focuses the last node in the tree
225
+ case key === 'End':
226
+ {
227
+ instance.focusNode(event, getLastNode(instance));
228
+
229
+ // Multi select behavior when pressing Ctrl + Shirt + End
230
+ // Selects the focused node and all the nodes down to the last node.
231
+ if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
232
+ instance.rangeSelectToLast(event, state.focusedNodeId);
233
+ }
234
+ event.preventDefault();
235
+ break;
205
236
  }
206
- instance.focusNode(event, getLastNode(instance));
207
- flag = true;
208
- break;
209
- default:
210
- if (key === '*') {
237
+
238
+ // Expand all siblings that are at the same level as the focused node
239
+ case key === '*':
240
+ {
211
241
  instance.expandAllSiblings(event, state.focusedNodeId);
212
- flag = true;
213
- } else if (params.multiSelect && ctrlPressed && key.toLowerCase() === 'a' && !params.disableSelection) {
242
+ event.preventDefault();
243
+ break;
244
+ }
245
+
246
+ // Multi select behavior when pressing Ctrl + a
247
+ // Selects all the nodes
248
+ case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
249
+ {
214
250
  instance.selectRange(event, {
215
251
  start: getFirstNode(instance),
216
252
  end: getLastNode(instance)
217
253
  });
218
- flag = true;
219
- } else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
220
- focusByFirstCharacter(event, state.focusedNodeId, key);
221
- flag = true;
254
+ event.preventDefault();
255
+ break;
256
+ }
257
+
258
+ // Type-ahead
259
+ // TODO: Support typing multiple characters
260
+ case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
261
+ {
262
+ const matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
263
+ if (matchingNode != null) {
264
+ instance.focusNode(event, matchingNode);
265
+ event.preventDefault();
266
+ }
267
+ break;
222
268
  }
223
- }
224
- if (flag) {
225
- event.preventDefault();
226
- event.stopPropagation();
227
269
  }
228
270
  };
229
271
  return {
@@ -231,4 +273,5 @@ export const useTreeViewKeyboardNavigation = ({
231
273
  onKeyDown: createHandleKeyDown(otherHandlers)
232
274
  })
233
275
  };
234
- };
276
+ };
277
+ useTreeViewKeyboardNavigation.params = {};
@@ -6,12 +6,15 @@ import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
6
6
  export interface UseTreeViewKeyboardNavigationInstance {
7
7
  updateFirstCharMap: (updater: (map: TreeViewFirstCharMap) => TreeViewFirstCharMap) => void;
8
8
  }
9
- export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{}, {}, UseTreeViewKeyboardNavigationInstance, {}, {}, never, [
10
- UseTreeViewNodesSignature,
11
- UseTreeViewSelectionSignature,
12
- UseTreeViewFocusSignature,
13
- UseTreeViewExpansionSignature
14
- ]>;
9
+ export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
10
+ instance: UseTreeViewKeyboardNavigationInstance;
11
+ dependantPlugins: [
12
+ UseTreeViewNodesSignature,
13
+ UseTreeViewSelectionSignature,
14
+ UseTreeViewFocusSignature,
15
+ UseTreeViewExpansionSignature
16
+ ];
17
+ }>;
15
18
  export type TreeViewFirstCharMap = {
16
19
  [nodeId: string]: string;
17
20
  };
@@ -14,11 +14,11 @@ const updateState = ({
14
14
  var _item$children, _item$children2;
15
15
  const id = getItemId ? getItemId(item) : item.id;
16
16
  if (id == null) {
17
- 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'));
17
+ throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
18
18
  }
19
19
  const label = getItemLabel ? getItemLabel(item) : item.label;
20
20
  if (label == null) {
21
- 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'));
21
+ 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'));
22
22
  }
23
23
  nodeMap[id] = {
24
24
  id,
@@ -116,10 +116,28 @@ export const useTreeViewNodes = ({
116
116
  getNavigableChildrenIds,
117
117
  isNodeDisabled
118
118
  });
119
+ return {
120
+ contextValue: {
121
+ disabledItemsFocusable: params.disabledItemsFocusable
122
+ }
123
+ };
119
124
  };
120
125
  useTreeViewNodes.getInitialState = params => updateState({
121
126
  items: params.items,
122
127
  isItemDisabled: params.isItemDisabled,
123
128
  getItemId: params.getItemId,
124
129
  getItemLabel: params.getItemLabel
125
- });
130
+ });
131
+ useTreeViewNodes.getDefaultizedParams = params => {
132
+ var _params$disabledItems;
133
+ return _extends({}, params, {
134
+ disabledItemsFocusable: (_params$disabledItems = params.disabledItemsFocusable) != null ? _params$disabledItems : false
135
+ });
136
+ };
137
+ useTreeViewNodes.params = {
138
+ disabledItemsFocusable: true,
139
+ items: true,
140
+ isItemDisabled: true,
141
+ getItemLabel: true,
142
+ getItemId: true
143
+ };
@@ -62,8 +62,16 @@ export interface UseTreeViewNodesState {
62
62
  nodeTree: TreeViewNodeIdAndChildren[];
63
63
  nodeMap: TreeViewNodeMap;
64
64
  }
65
- export type UseTreeViewNodesSignature = TreeViewPluginSignature<UseTreeViewNodesParameters<any>, UseTreeViewNodesDefaultizedParameters<any>, UseTreeViewNodesInstance, UseTreeViewNodesEventLookup, UseTreeViewNodesState, never, [
66
- ]>;
65
+ interface UseTreeViewNodesContextValue extends Pick<UseTreeViewNodesDefaultizedParameters<any>, 'disabledItemsFocusable'> {
66
+ }
67
+ export type UseTreeViewNodesSignature = TreeViewPluginSignature<{
68
+ params: UseTreeViewNodesParameters<any>;
69
+ defaultizedParams: UseTreeViewNodesDefaultizedParameters<any>;
70
+ instance: UseTreeViewNodesInstance;
71
+ events: UseTreeViewNodesEventLookup;
72
+ state: UseTreeViewNodesState;
73
+ contextValue: UseTreeViewNodesContextValue;
74
+ }>;
67
75
  export type TreeViewNodeMap = {
68
76
  [nodeId: string]: TreeViewNode;
69
77
  };
@@ -10,7 +10,6 @@ export const useTreeViewSelection = ({
10
10
  const lastSelectedNode = React.useRef(null);
11
11
  const lastSelectionWasRange = React.useRef(false);
12
12
  const currentRangeSelection = React.useRef([]);
13
- const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
14
13
  const setSelectedNodes = (event, newSelectedNodes) => {
15
14
  if (params.onNodeSelectionToggle) {
16
15
  if (params.multiSelect) {
@@ -34,8 +33,9 @@ export const useTreeViewSelection = ({
34
33
  if (params.onSelectedNodesChange) {
35
34
  params.onSelectedNodesChange(event, newSelectedNodes);
36
35
  }
37
- models.selectedNodes.setValue(newSelectedNodes);
36
+ models.selectedNodes.setControlledValue(newSelectedNodes);
38
37
  };
38
+ const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
39
39
  const selectNode = (event, nodeId, multiple = false) => {
40
40
  if (params.disableSelection) {
41
41
  return;
@@ -165,13 +165,17 @@ export const useTreeViewSelection = ({
165
165
  return {
166
166
  getRootProps: () => ({
167
167
  'aria-multiselectable': params.multiSelect
168
- })
168
+ }),
169
+ contextValue: {
170
+ selection: {
171
+ multiSelect: params.multiSelect
172
+ }
173
+ }
169
174
  };
170
175
  };
171
176
  useTreeViewSelection.models = {
172
177
  selectedNodes: {
173
- controlledProp: 'selectedNodes',
174
- defaultProp: 'defaultSelectedNodes'
178
+ getDefaultValue: params => params.defaultSelectedNodes
175
179
  }
176
180
  };
177
181
  const DEFAULT_SELECTED_NODES = [];
@@ -182,4 +186,12 @@ useTreeViewSelection.getDefaultizedParams = params => {
182
186
  multiSelect: (_params$multiSelect = params.multiSelect) != null ? _params$multiSelect : false,
183
187
  defaultSelectedNodes: (_params$defaultSelect = params.defaultSelectedNodes) != null ? _params$defaultSelect : params.multiSelect ? DEFAULT_SELECTED_NODES : null
184
188
  });
189
+ };
190
+ useTreeViewSelection.params = {
191
+ disableSelection: true,
192
+ multiSelect: true,
193
+ defaultSelectedNodes: true,
194
+ selectedNodes: true,
195
+ onSelectedNodesChange: true,
196
+ onNodeSelectionToggle: true
185
197
  };
@@ -48,9 +48,19 @@ export interface UseTreeViewSelectionParameters<Multiple extends boolean | undef
48
48
  onNodeSelectionToggle?: (event: React.SyntheticEvent, nodeId: string, isSelected: boolean) => void;
49
49
  }
50
50
  export type UseTreeViewSelectionDefaultizedParameters<Multiple extends boolean> = DefaultizedProps<UseTreeViewSelectionParameters<Multiple>, 'disableSelection' | 'defaultSelectedNodes' | 'multiSelect'>;
51
- export type UseTreeViewSelectionSignature = TreeViewPluginSignature<UseTreeViewSelectionParameters<any>, UseTreeViewSelectionDefaultizedParameters<any>, UseTreeViewSelectionInstance, {}, {}, 'selectedNodes', [
52
- UseTreeViewNodesSignature,
53
- UseTreeViewExpansionSignature,
54
- UseTreeViewNodesSignature
55
- ]>;
51
+ interface UseTreeViewSelectionContextValue {
52
+ selection: Pick<UseTreeViewSelectionDefaultizedParameters<boolean>, 'multiSelect'>;
53
+ }
54
+ export type UseTreeViewSelectionSignature = TreeViewPluginSignature<{
55
+ params: UseTreeViewSelectionParameters<any>;
56
+ defaultizedParams: UseTreeViewSelectionDefaultizedParameters<any>;
57
+ instance: UseTreeViewSelectionInstance;
58
+ contextValue: UseTreeViewSelectionContextValue;
59
+ modelNames: 'selectedNodes';
60
+ dependantPlugins: [
61
+ UseTreeViewNodesSignature,
62
+ UseTreeViewExpansionSignature,
63
+ UseTreeViewNodesSignature
64
+ ];
65
+ }>;
56
66
  export {};
@@ -26,11 +26,15 @@ export const useTreeView = inParams => {
26
26
  return temp;
27
27
  });
28
28
  const rootPropsGetters = [];
29
- let contextValue = {};
29
+ const contextValue = {
30
+ instance: instance
31
+ };
30
32
  const runPlugin = plugin => {
31
33
  const pluginResponse = plugin({
32
34
  instance,
33
35
  params,
36
+ slots: params.slots,
37
+ slotProps: params.slotProps,
34
38
  state,
35
39
  setState,
36
40
  rootRef: innerRootRef,
@@ -40,7 +44,7 @@ export const useTreeView = inParams => {
40
44
  rootPropsGetters.push(pluginResponse.getRootProps);
41
45
  }
42
46
  if (pluginResponse.contextValue) {
43
- contextValue = pluginResponse.contextValue;
47
+ Object.assign(contextValue, pluginResponse.contextValue);
44
48
  }
45
49
  };
46
50
  plugins.forEach(runPlugin);
@@ -96,7 +100,7 @@ export const useTreeView = inParams => {
96
100
  return {
97
101
  getRootProps,
98
102
  rootRef: handleRootRef,
99
- contextValue,
103
+ contextValue: contextValue,
100
104
  instance: instance
101
105
  };
102
106
  };