@mui/x-tree-view 7.5.1 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/RichTreeView/RichTreeView.js +14 -0
  3. package/RichTreeView/RichTreeView.types.d.ts +7 -1
  4. package/SimpleTreeView/SimpleTreeView.js +14 -0
  5. package/SimpleTreeView/SimpleTreeView.types.d.ts +7 -1
  6. package/TreeItem/TreeItem.js +35 -7
  7. package/TreeItem/TreeItem.types.d.ts +1 -0
  8. package/TreeItem2/TreeItem2.d.ts +12 -6
  9. package/TreeItem2/TreeItem2.js +21 -10
  10. package/TreeItem2Provider/TreeItem2Provider.js +4 -2
  11. package/TreeView/TreeView.js +14 -0
  12. package/index.js +1 -1
  13. package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.d.ts +3 -0
  14. package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +5 -0
  15. package/internals/TreeViewItemDepthContext/index.d.ts +1 -0
  16. package/internals/TreeViewItemDepthContext/index.js +1 -0
  17. package/internals/TreeViewProvider/TreeViewContext.d.ts +2 -2
  18. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +2 -2
  19. package/internals/index.d.ts +1 -1
  20. package/internals/models/helpers.d.ts +1 -0
  21. package/internals/models/plugin.d.ts +12 -5
  22. package/internals/models/treeView.d.ts +8 -1
  23. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +28 -4
  24. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
  25. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +20 -5
  26. package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +10 -1
  27. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
  28. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +50 -7
  29. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
  30. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +22 -4
  31. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +14 -1
  32. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +5 -0
  33. package/internals/useTreeView/useTreeView.js +8 -3
  34. package/internals/useTreeView/useTreeView.types.d.ts +2 -1
  35. package/internals/utils/extractPluginParamsFromProps.d.ts +3 -2
  36. package/internals/utils/extractPluginParamsFromProps.js +4 -2
  37. package/internals/utils/tree.js +18 -1
  38. package/modern/RichTreeView/RichTreeView.js +14 -0
  39. package/modern/SimpleTreeView/SimpleTreeView.js +14 -0
  40. package/modern/TreeItem/TreeItem.js +35 -7
  41. package/modern/TreeItem2/TreeItem2.js +21 -10
  42. package/modern/TreeItem2Provider/TreeItem2Provider.js +4 -2
  43. package/modern/TreeView/TreeView.js +14 -0
  44. package/modern/index.js +1 -1
  45. package/modern/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +5 -0
  46. package/modern/internals/TreeViewItemDepthContext/index.js +1 -0
  47. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
  48. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
  49. package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
  50. package/modern/internals/useTreeView/useTreeView.js +8 -3
  51. package/modern/internals/utils/extractPluginParamsFromProps.js +4 -2
  52. package/modern/internals/utils/tree.js +18 -1
  53. package/modern/useTreeItem2/useTreeItem2.js +20 -3
  54. package/node/RichTreeView/RichTreeView.js +14 -0
  55. package/node/SimpleTreeView/SimpleTreeView.js +14 -0
  56. package/node/TreeItem/TreeItem.js +35 -7
  57. package/node/TreeItem2/TreeItem2.js +20 -9
  58. package/node/TreeItem2Provider/TreeItem2Provider.js +4 -2
  59. package/node/TreeView/TreeView.js +14 -0
  60. package/node/index.js +1 -1
  61. package/node/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.js +13 -0
  62. package/node/internals/TreeViewItemDepthContext/index.js +12 -0
  63. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +11 -26
  64. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +29 -8
  65. package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +34 -25
  66. package/node/internals/useTreeView/useTreeView.js +8 -3
  67. package/node/internals/utils/extractPluginParamsFromProps.js +4 -2
  68. package/node/internals/utils/tree.js +18 -1
  69. package/node/useTreeItem2/useTreeItem2.js +20 -3
  70. package/package.json +3 -3
  71. package/useTreeItem2/useTreeItem2.js +20 -3
  72. package/useTreeItem2/useTreeItem2.types.d.ts +12 -0
@@ -1,11 +1,12 @@
1
1
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2
- const _excluded = ["slots", "slotProps", "apiRef"];
2
+ const _excluded = ["slots", "slotProps", "apiRef", "experimentalFeatures"];
3
3
  export const extractPluginParamsFromProps = _ref => {
4
4
  let {
5
5
  props: {
6
6
  slots,
7
7
  slotProps,
8
- apiRef
8
+ apiRef,
9
+ experimentalFeatures
9
10
  },
10
11
  plugins,
11
12
  rootRef
@@ -20,6 +21,7 @@ export const extractPluginParamsFromProps = _ref => {
20
21
  rootRef,
21
22
  slots: slots ?? {},
22
23
  slotProps: slotProps ?? {},
24
+ experimentalFeatures: experimentalFeatures ?? {},
23
25
  apiRef
24
26
  };
25
27
  const otherProps = {};
@@ -18,7 +18,24 @@ export const getPreviousNavigableItem = (instance, itemId) => {
18
18
  if (itemIndex === 0) {
19
19
  return itemMeta.parentId;
20
20
  }
21
- let currentItemId = siblings[itemIndex - 1];
21
+
22
+ // Finds the previous navigable sibling.
23
+ let previousNavigableSiblingIndex = itemIndex - 1;
24
+ while (!instance.isItemNavigable(siblings[previousNavigableSiblingIndex]) && previousNavigableSiblingIndex >= 0) {
25
+ previousNavigableSiblingIndex -= 1;
26
+ }
27
+ if (previousNavigableSiblingIndex === -1) {
28
+ // If we are at depth 0, then it means all the items above the current item are not navigable.
29
+ if (itemMeta.parentId == null) {
30
+ return null;
31
+ }
32
+
33
+ // Otherwise, we can try to go up a level and find the previous navigable item.
34
+ return getPreviousNavigableItem(instance, itemMeta.parentId);
35
+ }
36
+
37
+ // Finds the last navigable ancestor of the previous navigable sibling.
38
+ let currentItemId = siblings[previousNavigableSiblingIndex];
22
39
  let lastNavigableChild = getLastNavigableItemInArray(instance, instance.getItemOrderedChildrenIds(currentItemId));
23
40
  while (instance.isItemExpanded(currentItemId) && lastNavigableChild != null) {
24
41
  currentItemId = lastNavigableChild;
@@ -180,6 +180,14 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
180
180
  * Used when the item's expansion is controlled.
181
181
  */
182
182
  expandedItems: PropTypes.arrayOf(PropTypes.string),
183
+ /**
184
+ * Unstable features, breaking changes might be introduced.
185
+ * For each feature, if the flag is not explicitly set to `true`,
186
+ * the feature will be fully disabled and any property / method call will not have any effect.
187
+ */
188
+ experimentalFeatures: PropTypes.shape({
189
+ indentationAtItemLevel: PropTypes.bool
190
+ }),
183
191
  /**
184
192
  * Used to determine the id of a given item.
185
193
  *
@@ -210,6 +218,12 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
210
218
  * @returns {boolean} `true` if the item should be disabled.
211
219
  */
212
220
  isItemDisabled: PropTypes.func,
221
+ /**
222
+ * Horizontal indentation between an item and its children.
223
+ * Examples: 24, "24px", "2rem", "2em".
224
+ * @default 12px
225
+ */
226
+ itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
213
227
  items: PropTypes.array.isRequired,
214
228
  /**
215
229
  * If `true`, `ctrl` and `shift` will trigger multiselect.
@@ -142,11 +142,25 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
142
142
  * Used when the item's expansion is controlled.
143
143
  */
144
144
  expandedItems: PropTypes.arrayOf(PropTypes.string),
145
+ /**
146
+ * Unstable features, breaking changes might be introduced.
147
+ * For each feature, if the flag is not explicitly set to `true`,
148
+ * the feature will be fully disabled and any property / method call will not have any effect.
149
+ */
150
+ experimentalFeatures: PropTypes.shape({
151
+ indentationAtItemLevel: PropTypes.bool
152
+ }),
145
153
  /**
146
154
  * This prop is used to help implement the accessibility logic.
147
155
  * If you don't provide this prop. It falls back to a randomly generated id.
148
156
  */
149
157
  id: PropTypes.string,
158
+ /**
159
+ * Horizontal indentation between an item and its children.
160
+ * Examples: 24, "24px", "2rem", "2em".
161
+ * @default 12px
162
+ */
163
+ itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
150
164
  /**
151
165
  * If `true`, `ctrl` and `shift` will trigger multiselect.
152
166
  * @default false
@@ -10,6 +10,7 @@ import clsx from 'clsx';
10
10
  import Collapse from '@mui/material/Collapse';
11
11
  import { resolveComponentProps, useSlotProps } from '@mui/base/utils';
12
12
  import useForkRef from '@mui/utils/useForkRef';
13
+ import { shouldForwardProp } from '@mui/system/createStyled';
13
14
  import { alpha, styled, useThemeProps } from '@mui/material/styles';
14
15
  import unsupportedProp from '@mui/utils/unsupportedProp';
15
16
  import elementTypeAcceptingRef from '@mui/utils/elementTypeAcceptingRef';
@@ -19,6 +20,7 @@ import { treeItemClasses, getTreeItemUtilityClass } from './treeItemClasses';
19
20
  import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext';
20
21
  import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons';
21
22
  import { TreeItem2Provider } from '../TreeItem2Provider';
23
+ import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
22
24
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
25
  const useUtilityClasses = ownerState => {
24
26
  const {
@@ -57,7 +59,8 @@ const StyledTreeItemContent = styled(TreeItemContent, {
57
59
  }, styles.label && {
58
60
  [`& .${treeItemClasses.label}`]: styles.label
59
61
  }];
60
- }
62
+ },
63
+ shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel'
61
64
  })(({
62
65
  theme
63
66
  }) => ({
@@ -117,16 +120,33 @@ const StyledTreeItemContent = styled(TreeItemContent, {
117
120
  }, theme.typography.body1),
118
121
  [`& .${treeItemClasses.checkbox}`]: {
119
122
  padding: 0
120
- }
123
+ },
124
+ variants: [{
125
+ props: {
126
+ indentationAtItemLevel: true
127
+ },
128
+ style: {
129
+ paddingLeft: `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`
130
+ }
131
+ }]
121
132
  }));
122
133
  const TreeItemGroup = styled(Collapse, {
123
134
  name: 'MuiTreeItem',
124
135
  slot: 'GroupTransition',
125
- overridesResolver: (props, styles) => styles.groupTransition
136
+ overridesResolver: (props, styles) => styles.groupTransition,
137
+ shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel'
126
138
  })({
127
139
  margin: 0,
128
140
  padding: 0,
129
- paddingLeft: 12
141
+ paddingLeft: 'var(--TreeView-itemChildrenIndentation)',
142
+ variants: [{
143
+ props: {
144
+ indentationAtItemLevel: true
145
+ },
146
+ style: {
147
+ paddingLeft: 0
148
+ }
149
+ }]
130
150
  });
131
151
 
132
152
  /**
@@ -147,8 +167,10 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
147
167
  multiSelect
148
168
  },
149
169
  disabledItemsFocusable,
170
+ indentationAtItemLevel,
150
171
  instance
151
172
  } = useTreeViewContext();
173
+ const depthContext = React.useContext(TreeViewItemDepthContext);
152
174
  const props = useThemeProps({
153
175
  props: inProps,
154
176
  name: 'MuiTreeItem'
@@ -197,7 +219,8 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
197
219
  expanded,
198
220
  focused,
199
221
  selected,
200
- disabled
222
+ disabled,
223
+ indentationAtItemLevel
201
224
  });
202
225
  const classes = useUtilityClasses(ownerState);
203
226
  const GroupTransition = slots.groupTransition ?? undefined;
@@ -205,12 +228,14 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
205
228
  elementType: GroupTransition,
206
229
  ownerState: {},
207
230
  externalSlotProps: inSlotProps?.groupTransition,
208
- additionalProps: {
231
+ additionalProps: _extends({
209
232
  unmountOnExit: true,
210
233
  in: expanded,
211
234
  component: 'ul',
212
235
  role: 'group'
213
- },
236
+ }, indentationAtItemLevel ? {
237
+ indentationAtItemLevel: true
238
+ } : {}),
214
239
  className: classes.groupTransition
215
240
  });
216
241
  const ExpansionIcon = expanded ? slots.collapseIcon : slots.expandIcon;
@@ -291,6 +316,9 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
291
316
  onBlur: handleBlur,
292
317
  onKeyDown: handleKeyDown,
293
318
  ref: handleRootRef,
319
+ style: indentationAtItemLevel ? _extends({}, other.style, {
320
+ '--TreeView-itemDepth': typeof depthContext === 'function' ? depthContext(itemId) : depthContext
321
+ }) : other.style,
294
322
  children: [/*#__PURE__*/_jsx(StyledTreeItemContent, _extends({
295
323
  as: ContentComponent,
296
324
  classes: {
@@ -10,10 +10,10 @@ import { alpha, styled, useThemeProps } from '@mui/material/styles';
10
10
  import Collapse from '@mui/material/Collapse';
11
11
  import MuiCheckbox from '@mui/material/Checkbox';
12
12
  import { useSlotProps } from '@mui/base/utils';
13
- import { shouldForwardProp } from '@mui/system';
13
+ import { shouldForwardProp } from '@mui/system/createStyled';
14
14
  import composeClasses from '@mui/utils/composeClasses';
15
15
  import { unstable_useTreeItem2 as useTreeItem2 } from '../useTreeItem2';
16
- import { getTreeItemUtilityClass, treeItemClasses } from '../TreeItem';
16
+ import { getTreeItemUtilityClass } from '../TreeItem';
17
17
  import { TreeItem2Icon } from '../TreeItem2Icon';
18
18
  import { TreeItem2Provider } from '../TreeItem2Provider';
19
19
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
@@ -31,7 +31,7 @@ export const TreeItem2Content = styled('div', {
31
31
  name: 'MuiTreeItem2',
32
32
  slot: 'Content',
33
33
  overridesResolver: (props, styles) => styles.content,
34
- shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'status'
34
+ shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'status' && prop !== 'indentationAtItemLevel'
35
35
  })(({
36
36
  theme
37
37
  }) => ({
@@ -52,12 +52,14 @@ export const TreeItem2Content = styled('div', {
52
52
  backgroundColor: 'transparent'
53
53
  }
54
54
  },
55
- [`& .${treeItemClasses.groupTransition}`]: {
56
- margin: 0,
57
- padding: 0,
58
- paddingLeft: 12
59
- },
60
55
  variants: [{
56
+ props: {
57
+ indentationAtItemLevel: true
58
+ },
59
+ style: {
60
+ paddingLeft: `calc(${theme.spacing(1)} + var(--TreeView-itemChildrenIndentation) * var(--TreeView-itemDepth))`
61
+ }
62
+ }, {
61
63
  props: ({
62
64
  status
63
65
  }) => status.disabled,
@@ -125,11 +127,20 @@ export const TreeItem2IconContainer = styled('div', {
125
127
  export const TreeItem2GroupTransition = styled(Collapse, {
126
128
  name: 'MuiTreeItem2',
127
129
  slot: 'GroupTransition',
128
- overridesResolver: (props, styles) => styles.groupTransition
130
+ overridesResolver: (props, styles) => styles.groupTransition,
131
+ shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'indentationAtItemLevel'
129
132
  })({
130
133
  margin: 0,
131
134
  padding: 0,
132
- paddingLeft: 12
135
+ paddingLeft: 'var(--TreeView-itemChildrenIndentation)',
136
+ variants: [{
137
+ props: {
138
+ indentationAtItemLevel: true
139
+ },
140
+ style: {
141
+ paddingLeft: 0
142
+ }
143
+ }]
133
144
  });
134
145
  export const TreeItem2Checkbox = styled( /*#__PURE__*/React.forwardRef((props, ref) => {
135
146
  const {
@@ -6,11 +6,13 @@ function TreeItem2Provider(props) {
6
6
  itemId
7
7
  } = props;
8
8
  const {
9
- wrapItem
9
+ wrapItem,
10
+ instance
10
11
  } = useTreeViewContext();
11
12
  return wrapItem({
12
13
  children,
13
- itemId
14
+ itemId,
15
+ instance
14
16
  });
15
17
  }
16
18
  TreeItem2Provider.propTypes = {
@@ -115,11 +115,25 @@ process.env.NODE_ENV !== "production" ? TreeView.propTypes = {
115
115
  * Used when the item's expansion is controlled.
116
116
  */
117
117
  expandedItems: PropTypes.arrayOf(PropTypes.string),
118
+ /**
119
+ * Unstable features, breaking changes might be introduced.
120
+ * For each feature, if the flag is not explicitly set to `true`,
121
+ * the feature will be fully disabled and any property / method call will not have any effect.
122
+ */
123
+ experimentalFeatures: PropTypes.shape({
124
+ indentationAtItemLevel: PropTypes.bool
125
+ }),
118
126
  /**
119
127
  * This prop is used to help implement the accessibility logic.
120
128
  * If you don't provide this prop. It falls back to a randomly generated id.
121
129
  */
122
130
  id: PropTypes.string,
131
+ /**
132
+ * Horizontal indentation between an item and its children.
133
+ * Examples: 24, "24px", "2rem", "2em".
134
+ * @default 12px
135
+ */
136
+ itemChildrenIndentation: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
123
137
  /**
124
138
  * If `true`, `ctrl` and `shift` will trigger multiselect.
125
139
  * @default false
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v7.5.1
2
+ * @mui/x-tree-view v7.6.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ export const TreeViewItemDepthContext = /*#__PURE__*/React.createContext(() => -1);
3
+ if (process.env.NODE_ENV !== 'production') {
4
+ TreeViewItemDepthContext.displayName = 'TreeViewItemDepthContext';
5
+ }
@@ -0,0 +1 @@
1
+ export { TreeViewItemDepthContext } from './TreeViewItemDepthContext';
@@ -5,17 +5,15 @@ import ownerDocument from '@mui/utils/ownerDocument';
5
5
  import { useInstanceEventHandler } from '../../hooks/useInstanceEventHandler';
6
6
  import { getActiveElement } from '../../utils/utils';
7
7
  import { getFirstNavigableItem } from '../../utils/tree';
8
- const useTabbableItemId = (instance, selectedItems) => {
9
- const isItemVisible = itemId => {
8
+ import { convertSelectedItemsToArray } from '../useTreeViewSelection/useTreeViewSelection.utils';
9
+ const useDefaultFocusableItemId = (instance, selectedItems) => {
10
+ let tabbableItemId = convertSelectedItemsToArray(selectedItems).find(itemId => {
11
+ if (!instance.isItemNavigable(itemId)) {
12
+ return false;
13
+ }
10
14
  const itemMeta = instance.getItemMeta(itemId);
11
15
  return itemMeta && (itemMeta.parentId == null || instance.isItemExpanded(itemMeta.parentId));
12
- };
13
- let tabbableItemId;
14
- if (Array.isArray(selectedItems)) {
15
- tabbableItemId = selectedItems.find(isItemVisible);
16
- } else if (selectedItems != null && isItemVisible(selectedItems)) {
17
- tabbableItemId = selectedItems;
18
- }
16
+ });
19
17
  if (tabbableItemId == null) {
20
18
  tabbableItemId = getFirstNavigableItem(instance);
21
19
  }
@@ -29,7 +27,7 @@ export const useTreeViewFocus = ({
29
27
  models,
30
28
  rootRef
31
29
  }) => {
32
- const tabbableItemId = useTabbableItemId(instance, models.selectedItems.value);
30
+ const defaultFocusableItemId = useDefaultFocusableItemId(instance, models.selectedItems.value);
33
31
  const setFocusedItemId = useEventCallback(itemId => {
34
32
  const cleanItemId = typeof itemId === 'function' ? itemId(state.focusedItemId) : itemId;
35
33
  if (state.focusedItemId !== cleanItemId) {
@@ -61,18 +59,6 @@ export const useTreeViewFocus = ({
61
59
  innerFocusItem(event, itemId);
62
60
  }
63
61
  });
64
- const focusDefaultItem = useEventCallback(event => {
65
- let itemToFocusId;
66
- if (Array.isArray(models.selectedItems.value)) {
67
- itemToFocusId = models.selectedItems.value.find(isItemVisible);
68
- } else if (models.selectedItems.value != null && isItemVisible(models.selectedItems.value)) {
69
- itemToFocusId = models.selectedItems.value;
70
- }
71
- if (itemToFocusId == null) {
72
- itemToFocusId = getFirstNavigableItem(instance);
73
- }
74
- innerFocusItem(event, itemToFocusId);
75
- });
76
62
  const removeFocusedItem = useEventCallback(() => {
77
63
  if (state.focusedItemId == null) {
78
64
  return;
@@ -86,12 +72,12 @@ export const useTreeViewFocus = ({
86
72
  }
87
73
  setFocusedItemId(null);
88
74
  });
89
- const canItemBeTabbed = itemId => itemId === tabbableItemId;
75
+ const canItemBeTabbed = itemId => itemId === defaultFocusableItemId;
90
76
  useInstanceEventHandler(instance, 'removeItem', ({
91
77
  id
92
78
  }) => {
93
79
  if (state.focusedItemId === id) {
94
- instance.focusDefaultItem(null);
80
+ innerFocusItem(null, defaultFocusableItemId);
95
81
  }
96
82
  });
97
83
  const createRootHandleFocus = otherHandlers => event => {
@@ -102,7 +88,7 @@ export const useTreeViewFocus = ({
102
88
 
103
89
  // if the event bubbled (which is React specific) we don't want to steal focus
104
90
  if (event.target === event.currentTarget) {
105
- instance.focusDefaultItem(event);
91
+ innerFocusItem(event, defaultFocusableItemId);
106
92
  }
107
93
  };
108
94
  return {
@@ -116,7 +102,6 @@ export const useTreeViewFocus = ({
116
102
  isItemFocused,
117
103
  canItemBeTabbed,
118
104
  focusItem,
119
- focusDefaultItem,
120
105
  removeFocusedItem
121
106
  }
122
107
  };
@@ -2,6 +2,8 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
4
4
  import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './useTreeViewItems.utils';
5
+ import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
5
7
  const updateItemsState = ({
6
8
  items,
7
9
  isItemDisabled,
@@ -13,7 +15,7 @@ const updateItemsState = ({
13
15
  const itemOrderedChildrenIds = {
14
16
  [TREE_VIEW_ROOT_PARENT_ID]: []
15
17
  };
16
- const processItem = (item, parentId) => {
18
+ const processItem = (item, depth, parentId) => {
17
19
  const id = getItemId ? getItemId(item) : item.id;
18
20
  if (id == null) {
19
21
  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'));
@@ -31,7 +33,8 @@ const updateItemsState = ({
31
33
  parentId,
32
34
  idAttribute: undefined,
33
35
  expandable: !!item.children?.length,
34
- disabled: isItemDisabled ? isItemDisabled(item) : false
36
+ disabled: isItemDisabled ? isItemDisabled(item) : false,
37
+ depth
35
38
  };
36
39
  itemMap[id] = item;
37
40
  itemOrderedChildrenIds[id] = [];
@@ -40,9 +43,9 @@ const updateItemsState = ({
40
43
  itemOrderedChildrenIds[parentIdWithDefault] = [];
41
44
  }
42
45
  itemOrderedChildrenIds[parentIdWithDefault].push(id);
43
- item.children?.forEach(child => processItem(child, id));
46
+ item.children?.forEach(child => processItem(child, depth + 1, id));
44
47
  };
45
- items.forEach(item => processItem(item, null));
48
+ items.forEach(item => processItem(item, 0, null));
46
49
  const itemChildrenIndexes = {};
47
50
  Object.keys(itemOrderedChildrenIds).forEach(parentId => {
48
51
  itemChildrenIndexes[parentId] = buildSiblingIndexes(itemOrderedChildrenIds[parentId]);
@@ -58,7 +61,8 @@ export const useTreeViewItems = ({
58
61
  instance,
59
62
  params,
60
63
  state,
61
- setState
64
+ setState,
65
+ experimentalFeatures
62
66
  }) => {
63
67
  const getItemMeta = React.useCallback(itemId => state.items.itemMetaMap[itemId], [state.items.itemMetaMap]);
64
68
  const getItem = React.useCallback(itemId => state.items.itemMap[itemId], [state.items.itemMap]);
@@ -135,6 +139,11 @@ export const useTreeViewItems = ({
135
139
  return state.items.itemOrderedChildrenIds[TREE_VIEW_ROOT_PARENT_ID].map(getPropsFromItemId);
136
140
  };
137
141
  return {
142
+ getRootProps: () => ({
143
+ style: {
144
+ '--TreeView-itemChildrenIndentation': typeof params.itemChildrenIndentation === 'number' ? `${params.itemChildrenIndentation}px` : params.itemChildrenIndentation
145
+ }
146
+ }),
138
147
  publicAPI: {
139
148
  getItem
140
149
  },
@@ -150,7 +159,8 @@ export const useTreeViewItems = ({
150
159
  areItemUpdatesPrevented
151
160
  },
152
161
  contextValue: {
153
- disabledItemsFocusable: params.disabledItemsFocusable
162
+ disabledItemsFocusable: params.disabledItemsFocusable,
163
+ indentationAtItemLevel: experimentalFeatures.indentationAtItemLevel ?? false
154
164
  }
155
165
  };
156
166
  };
@@ -163,12 +173,23 @@ useTreeViewItems.getInitialState = params => ({
163
173
  })
164
174
  });
165
175
  useTreeViewItems.getDefaultizedParams = params => _extends({}, params, {
166
- disabledItemsFocusable: params.disabledItemsFocusable ?? false
176
+ disabledItemsFocusable: params.disabledItemsFocusable ?? false,
177
+ itemChildrenIndentation: params.itemChildrenIndentation ?? '12px'
167
178
  });
179
+ useTreeViewItems.wrapRoot = ({
180
+ children,
181
+ instance
182
+ }) => {
183
+ return /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
184
+ value: itemId => instance.getItemMeta(itemId)?.depth ?? 0,
185
+ children: children
186
+ });
187
+ };
168
188
  useTreeViewItems.params = {
169
189
  disabledItemsFocusable: true,
170
190
  items: true,
171
191
  isItemDisabled: true,
172
192
  getItemLabel: true,
173
- getItemId: true
193
+ getItemId: true,
194
+ itemChildrenIndentation: true
174
195
  };
@@ -7,6 +7,7 @@ import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
7
7
  import { useTreeViewContext } from '../../TreeViewProvider/useTreeViewContext';
8
8
  import { TreeViewChildrenItemContext, TreeViewChildrenItemProvider } from '../../TreeViewProvider/TreeViewChildrenItemProvider';
9
9
  import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from '../useTreeViewItems/useTreeViewItems.utils';
10
+ import { TreeViewItemDepthContext } from '../../TreeViewItemDepthContext';
10
11
  import { jsx as _jsx } from "react/jsx-runtime";
11
12
  export const useTreeViewJSXItems = ({
12
13
  instance,
@@ -33,6 +34,23 @@ export const useTreeViewJSXItems = ({
33
34
  })
34
35
  });
35
36
  });
37
+ return () => {
38
+ setState(prevState => {
39
+ const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
40
+ const newItemMap = _extends({}, prevState.items.itemMap);
41
+ delete newItemMetaMap[item.id];
42
+ delete newItemMap[item.id];
43
+ return _extends({}, prevState, {
44
+ items: _extends({}, prevState.items, {
45
+ itemMetaMap: newItemMetaMap,
46
+ itemMap: newItemMap
47
+ })
48
+ });
49
+ });
50
+ publishTreeViewEvent(instance, 'removeItem', {
51
+ id: item.id
52
+ });
53
+ };
36
54
  });
37
55
  const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
38
56
  const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
@@ -47,23 +65,6 @@ export const useTreeViewJSXItems = ({
47
65
  })
48
66
  }));
49
67
  };
50
- const removeJSXItem = useEventCallback(itemId => {
51
- setState(prevState => {
52
- const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
53
- const newItemMap = _extends({}, prevState.items.itemMap);
54
- delete newItemMetaMap[itemId];
55
- delete newItemMap[itemId];
56
- return _extends({}, prevState, {
57
- items: _extends({}, prevState.items, {
58
- itemMetaMap: newItemMetaMap,
59
- itemMap: newItemMap
60
- })
61
- });
62
- });
63
- publishTreeViewEvent(instance, 'removeItem', {
64
- id: itemId
65
- });
66
- });
67
68
  const mapFirstCharFromJSX = useEventCallback((itemId, firstChar) => {
68
69
  instance.updateFirstCharMap(firstCharMap => {
69
70
  firstCharMap[itemId] = firstChar;
@@ -80,7 +81,6 @@ export const useTreeViewJSXItems = ({
80
81
  return {
81
82
  instance: {
82
83
  insertJSXItem,
83
- removeJSXItem,
84
84
  setJSXItemsOrderedChildrenIds,
85
85
  mapFirstCharFromJSX
86
86
  }
@@ -129,14 +129,13 @@ const useTreeViewJSXItemsItemPlugin = ({
129
129
  };
130
130
  }, [instance, registerChild, unregisterChild, itemId, id]);
131
131
  React.useEffect(() => {
132
- instance.insertJSXItem({
132
+ return instance.insertJSXItem({
133
133
  id: itemId,
134
134
  idAttribute: id,
135
135
  parentId,
136
136
  expandable,
137
137
  disabled
138
138
  });
139
- return () => instance.removeJSXItem(itemId);
140
139
  }, [instance, parentId, itemId, expandable, disabled, id]);
141
140
  React.useEffect(() => {
142
141
  if (label) {
@@ -153,13 +152,23 @@ useTreeViewJSXItems.itemPlugin = useTreeViewJSXItemsItemPlugin;
153
152
  useTreeViewJSXItems.wrapItem = ({
154
153
  children,
155
154
  itemId
156
- }) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
157
- itemId: itemId,
158
- children: children
159
- });
155
+ }) => {
156
+ // eslint-disable-next-line react-hooks/rules-of-hooks
157
+ const depthContext = React.useContext(TreeViewItemDepthContext);
158
+ return /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
159
+ itemId: itemId,
160
+ children: /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
161
+ value: depthContext + 1,
162
+ children: children
163
+ })
164
+ });
165
+ };
160
166
  useTreeViewJSXItems.wrapRoot = ({
161
167
  children
162
168
  }) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
163
- children: children
169
+ children: /*#__PURE__*/_jsx(TreeViewItemDepthContext.Provider, {
170
+ value: 0,
171
+ children: children
172
+ })
164
173
  });
165
174
  useTreeViewJSXItems.params = {};
@@ -48,6 +48,7 @@ export const useTreeView = inParams => {
48
48
  params,
49
49
  slots: params.slots,
50
50
  slotProps: params.slotProps,
51
+ experimentalFeatures: params.experimentalFeatures,
51
52
  state,
52
53
  setState,
53
54
  rootRef: innerRootRef,
@@ -100,19 +101,23 @@ export const useTreeView = inParams => {
100
101
  itemWrappers.forEach(itemWrapper => {
101
102
  finalChildren = itemWrapper({
102
103
  itemId,
103
- children: finalChildren
104
+ children: finalChildren,
105
+ instance
104
106
  });
105
107
  });
106
108
  return finalChildren;
107
109
  };
108
- const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot);
110
+ const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot)
111
+ // The wrappers are reversed to ensure that the first wrapper is the outermost one.
112
+ .reverse();
109
113
  contextValue.wrapRoot = ({
110
114
  children
111
115
  }) => {
112
116
  let finalChildren = children;
113
117
  rootWrappers.forEach(rootWrapper => {
114
118
  finalChildren = rootWrapper({
115
- children: finalChildren
119
+ children: finalChildren,
120
+ instance
116
121
  });
117
122
  });
118
123
  return finalChildren;