@mui/x-tree-view 8.11.1 → 8.11.3

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 (32) hide show
  1. package/CHANGELOG.md +181 -0
  2. package/TreeItem/TreeItem.js +3 -1
  3. package/esm/TreeItem/TreeItem.js +3 -1
  4. package/esm/hooks/index.d.ts +2 -1
  5. package/esm/hooks/index.js +2 -1
  6. package/esm/hooks/useApplyPropagationToSelectedItemsOnMount.d.ts +53 -0
  7. package/esm/hooks/useApplyPropagationToSelectedItemsOnMount.js +80 -0
  8. package/esm/index.js +1 -1
  9. package/esm/internals/components/RichTreeViewItems.js +12 -5
  10. package/esm/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.d.ts +5 -3
  11. package/esm/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +19 -3
  12. package/esm/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +15 -6
  13. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.js +6 -22
  14. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +10 -0
  15. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +8 -0
  16. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +8 -11
  17. package/esm/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +0 -4
  18. package/hooks/index.d.ts +2 -1
  19. package/hooks/index.js +8 -1
  20. package/hooks/useApplyPropagationToSelectedItemsOnMount.d.ts +53 -0
  21. package/hooks/useApplyPropagationToSelectedItemsOnMount.js +87 -0
  22. package/index.js +1 -1
  23. package/internals/components/RichTreeViewItems.js +12 -5
  24. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.d.ts +5 -3
  25. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +19 -3
  26. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +15 -6
  27. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +6 -22
  28. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +10 -0
  29. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +8 -0
  30. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +8 -11
  31. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +0 -4
  32. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -5,6 +5,187 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.11.3
9
+
10
+ _Sep 16, 2025_
11
+
12
+ We'd like to extend a big thank you to the 11 contributors who made this release possible. Here are some highlights ✨:
13
+
14
+ - 🐞 Bugfixes
15
+ - 📚 Documentation improvements
16
+
17
+ Special thanks go out to the community members for their valuable contributions:
18
+ @sai6855
19
+
20
+ The following are all team members who have contributed to this release:
21
+ @alexfauquette, @bernardobelchior, @brijeshb42, @cherniavskii, @flaviendelangle, @Janpot, @JCQuintas, @LukasTy, @rita-codes, @siriwatknp
22
+
23
+ ### Data Grid
24
+
25
+ #### `@mui/x-data-grid@8.11.3`
26
+
27
+ - [DataGrid] Fix numeric font size not being applied (#19552) @cherniavskii
28
+ - [DataGrid] Improve `operator` types to display literal values (#19529) @siriwatknp
29
+
30
+ #### `@mui/x-data-grid-pro@8.11.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
31
+
32
+ Same changes as in `@mui/x-data-grid@8.11.3`.
33
+
34
+ #### `@mui/x-data-grid-premium@8.11.3` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link "Premium plan")
35
+
36
+ Same changes as in `@mui/x-data-grid-pro@8.11.3`.
37
+
38
+ ### Date and Time Pickers
39
+
40
+ #### `@mui/x-date-pickers@8.11.3`
41
+
42
+ - [pickers] Refactor `slots` and `slotProps` propagation strategy (#18867) @LukasTy
43
+
44
+ #### `@mui/x-date-pickers-pro@8.11.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
45
+
46
+ Same changes as in `@mui/x-date-pickers@8.11.3`.
47
+
48
+ ### Charts
49
+
50
+ #### `@mui/x-charts@8.11.3`
51
+
52
+ - [charts] Add `inline-` piecewise legend options (#19382) @JCQuintas
53
+ - [charts] Add bar gradient example (#19174) @bernardobelchior
54
+ - [charts] Ignore empty tick labels in label overlap computation (#19547) @alexfauquette
55
+ - [charts] Rename `isBandScale` to `isDiscreteScale` (#19514) @bernardobelchior
56
+ - [charts] Fix legend position affecting toolbar's position (#19257) @sai6855
57
+
58
+ #### `@mui/x-charts-pro@8.11.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
59
+
60
+ Same changes as in `@mui/x-charts@8.11.3`, plus:
61
+
62
+ - [charts-pro] Add chart title and caption to export demo (#19524) @bernardobelchior
63
+
64
+ ### Tree View
65
+
66
+ #### `@mui/x-tree-view@8.11.3`
67
+
68
+ - [tree view] Stop looping through all items to publish the `removeItem` event (#19500) @flaviendelangle
69
+ - [tree view] Support flat DOM structure (#19350) @flaviendelangle
70
+ - [tree view] Update cursor styles for disabled TreeItem (#19548) @sai6855
71
+
72
+ #### `@mui/x-tree-view-pro@8.11.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
73
+
74
+ Same changes as in `@mui/x-tree-view@8.11.3`.
75
+
76
+ ### Codemod
77
+
78
+ #### `@mui/x-codemod@8.11.3`
79
+
80
+ Internal changes.
81
+
82
+ ### Docs
83
+
84
+ - [docs] Add styling row group recipe (#19349) @siriwatknp
85
+ - [docs] Group demos data into the dataset folder (#19549) @alexfauquette
86
+ - [docs] Add `shiny` bar chart example at the top (#19416) @JCQuintas
87
+
88
+ ### Core
89
+
90
+ - [code-infra] Axios update (#19577) @Janpot
91
+ - [code-infra] Remove usage of copy-files command from code-infra (#19518) @brijeshb42
92
+ - [internal] Fix naming to match convention @oliviertassinari
93
+
94
+ ## 8.11.2
95
+
96
+ _Sep 10, 2025_
97
+
98
+ We'd like to extend a big thank you to the 13 contributors who made this release possible. Here are some highlights ✨:
99
+
100
+ - 🐞 Bugfixes
101
+ - 📚 Documentation improvements
102
+
103
+ Special thanks go out to the community members for their valuable contributions:
104
+ @ludvigeriksson, @sai6855
105
+
106
+ The following are all team members who have contributed to this release:
107
+ @alexfauquette, @bernardobelchior, @brijeshb42, @flaviendelangle, @Janpot, @LukasTy, @MBilalShafi, @noraleonte, @rita-codes, @romgrk, @siriwatknp
108
+
109
+ ### Data Grid
110
+
111
+ #### `@mui/x-data-grid@8.11.2`
112
+
113
+ - [DataGrid] Allow opting out of the exclude row selection model (#19423) @MBilalShafi
114
+ - [DataGrid] Fix column dropdown menu text alignment for the "Unpin" menu item (#19462) @MBilalShafi
115
+ - [DataGrid] Fix indeterminate state for "Select all" checkbox with exclude model (#19466) @MBilalShafi
116
+ - [DataGrid] Fix styled API arguments error (#19460) @MBilalShafi
117
+ - [DataGrid] Fix `stringify()` for theme objects (#19427) @romgrk
118
+
119
+ #### `@mui/x-data-grid-pro@8.11.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
120
+
121
+ Same changes as in `@mui/x-data-grid@8.11.2`.
122
+
123
+ #### `@mui/x-data-grid-premium@8.11.2` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
124
+
125
+ Same changes as in `@mui/x-data-grid-pro@8.11.2`, plus:
126
+
127
+ - [DataGridPremium] Fallback to the regular reorder method for plain data (#19467) @MBilalShafi
128
+ - [DataGridPremium] Fix showing `0` as total aggregation value when aggregation position is set to `null` for row groups (#19515) @cherniavskii
129
+
130
+ ### Date and Time Pickers
131
+
132
+ #### `@mui/x-date-pickers@8.11.2`
133
+
134
+ - [pickers] Gracefully handle `textField.slotProps` (#18980) @LukasTy
135
+ - [pickers] Improve hour and minute placement in Date Time Picker (#19227) @MBilalShafi
136
+ - [pickers] Use `calendarState.currentMonth` in Month Calendar when available (#19073) @LukasTy
137
+ - [pickers] Improve invalid value pasting into a section (#19357) @sai6855
138
+ - [fields] Remove redundant `id` and `aria-labelledby` attributes from spinbuttons (#19523) @LukasTy
139
+
140
+ #### `@mui/x-date-pickers-pro@8.11.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
141
+
142
+ Same changes as in `@mui/x-date-pickers@8.11.2`.
143
+
144
+ ### Charts
145
+
146
+ #### `@mui/x-charts@8.11.2`
147
+
148
+ - [charts] Fix highlight regression (#19486) @alexfauquette
149
+ - [charts] Improve code reuse in `ChartsXAxis` and `ChartsYAxis` (#19198) @bernardobelchior
150
+ - [charts] Simplify params in `getRange` and `createDateFormatter` (#19517) @bernardobelchior
151
+ - [charts] Handle domain edge cases with `filterMode: 'discard'` (#19199) @bernardobelchior
152
+ - [l10n] Add Swedish (sv-SE) locale (#19489) @ludvigeriksson
153
+
154
+ #### `@mui/x-charts-pro@8.11.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
155
+
156
+ Same changes as in `@mui/x-charts@8.11.2`.
157
+
158
+ ### Tree View
159
+
160
+ #### `@mui/x-tree-view@8.11.2`
161
+
162
+ - [TreeView] Do not mutate `props.items` in the `getItemTree()` method (#19483) @flaviendelangle
163
+ - [TreeView] Expose a new hook to apply selection propagation on the selected items (#19402) @flaviendelangle
164
+
165
+ #### `@mui/x-tree-view-pro@8.11.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
166
+
167
+ Same changes as in `@mui/x-tree-view@8.11.2`.
168
+
169
+ ### Codemod
170
+
171
+ #### `@mui/x-codemod@8.11.2`
172
+
173
+ Internal changes.
174
+
175
+ ### Docs
176
+
177
+ - [docs] Add recipe for save and manage filters from the panel (#19361) @siriwatknp
178
+
179
+ ### Core
180
+
181
+ - [code-infra] Remove log when restarting dev server (#19490) @bernardobelchior
182
+ - [code-infra] Store test results in CI (#19507) @Janpot
183
+
184
+ ### Miscellaneous
185
+
186
+ - [infra] Set nodejs versions of various CIs to 22.18 (#19503) @brijeshb42
187
+ - [test] Split infinitive @romgrk
188
+
8
189
  ## 8.11.1
9
190
 
10
191
  _Sep 4, 2025_
@@ -69,7 +69,8 @@ const TreeItemContent = exports.TreeItemContent = (0, _zeroStyled.styled)('div',
69
69
  },
70
70
  '&[data-disabled]': {
71
71
  opacity: (theme.vars || theme).palette.action.disabledOpacity,
72
- backgroundColor: 'transparent'
72
+ backgroundColor: 'transparent',
73
+ cursor: 'auto'
73
74
  },
74
75
  '&[data-focused]': {
75
76
  backgroundColor: (theme.vars || theme).palette.action.focus
@@ -121,6 +122,7 @@ const TreeItemIconContainer = exports.TreeItemIconContainer = (0, _zeroStyled.st
121
122
  flexShrink: 0,
122
123
  justifyContent: 'center',
123
124
  position: 'relative',
125
+ cursor: 'inherit',
124
126
  '& svg': {
125
127
  fontSize: 18
126
128
  }
@@ -62,7 +62,8 @@ export const TreeItemContent = styled('div', {
62
62
  },
63
63
  '&[data-disabled]': {
64
64
  opacity: (theme.vars || theme).palette.action.disabledOpacity,
65
- backgroundColor: 'transparent'
65
+ backgroundColor: 'transparent',
66
+ cursor: 'auto'
66
67
  },
67
68
  '&[data-focused]': {
68
69
  backgroundColor: (theme.vars || theme).palette.action.focus
@@ -114,6 +115,7 @@ export const TreeItemIconContainer = styled('div', {
114
115
  flexShrink: 0,
115
116
  justifyContent: 'center',
116
117
  position: 'relative',
118
+ cursor: 'inherit',
117
119
  '& svg': {
118
120
  fontSize: 18
119
121
  }
@@ -1,3 +1,4 @@
1
1
  export { useTreeViewApiRef } from "./useTreeViewApiRef.js";
2
2
  export { useTreeItemUtils } from "./useTreeItemUtils/index.js";
3
- export { useTreeItemModel } from "./useTreeItemModel.js";
3
+ export { useTreeItemModel } from "./useTreeItemModel.js";
4
+ export { useApplyPropagationToSelectedItemsOnMount } from "./useApplyPropagationToSelectedItemsOnMount.js";
@@ -1,3 +1,4 @@
1
1
  export { useTreeViewApiRef } from "./useTreeViewApiRef.js";
2
2
  export { useTreeItemUtils } from "./useTreeItemUtils/index.js";
3
- export { useTreeItemModel } from "./useTreeItemModel.js";
3
+ export { useTreeItemModel } from "./useTreeItemModel.js";
4
+ export { useApplyPropagationToSelectedItemsOnMount } from "./useApplyPropagationToSelectedItemsOnMount.js";
@@ -0,0 +1,53 @@
1
+ import { TreeViewItemId, TreeViewSelectionPropagation } from "../models/index.js";
2
+ /**
3
+ * Applies the selection propagation rules to the selected items.
4
+ * The value is only computed during the first render, any update of the parameters will be ignored.
5
+ *
6
+ * Uncontrolled example:
7
+ * ```tsx
8
+ * const defaultSelectedItems = useApplyPropagationToSelectedItemsOnMount({
9
+ * items: props.items,
10
+ * selectionPropagation: props.selectionPropagation,
11
+ * selectedItems: ['10', '11', '13', '14'],
12
+ * });
13
+ *
14
+ * return (
15
+ * <RichTreeView
16
+ * items={props.items}
17
+ * selectionPropagation={props.selectionPropagation}
18
+ * defaultSelectedItems={defaultSelectedItems}
19
+ * />
20
+ * );
21
+ * ```
22
+ *
23
+ * Controlled example:
24
+ * ```tsx
25
+ * const initialSelectedItems = useApplyPropagationToSelectedItemsOnMount({
26
+ * items: props.items,
27
+ * selectionPropagation: props.selectionPropagation,
28
+ * selectedItems: ['10', '11', '13', '14'],
29
+ * });
30
+ *
31
+ * const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems);
32
+ *
33
+ * return (
34
+ * <RichTreeView
35
+ * items={props.items}
36
+ * selectionPropagation={props.selectionPropagation}
37
+ * selectedItems={selectedItems}
38
+ * onSelectedItemsChange={setSelectedItems}
39
+ * />
40
+ * );
41
+ * ```
42
+ */
43
+ export declare function useApplyPropagationToSelectedItemsOnMount(parameters: UseApplyPropagationToDefaultSelectedItemsParameters<any>): string[];
44
+ interface UseApplyPropagationToDefaultSelectedItemsParameters<R extends {
45
+ children?: R[];
46
+ }> {
47
+ items: R[];
48
+ getItemId?: (item: R) => TreeViewItemId;
49
+ getItemChildren?: (item: R) => R[] | undefined;
50
+ selectedItems: string[];
51
+ selectionPropagation: TreeViewSelectionPropagation;
52
+ }
53
+ export {};
@@ -0,0 +1,80 @@
1
+ import useLazyRef from '@mui/utils/useLazyRef';
2
+ import { getLookupFromArray } from "../internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js";
3
+ const defaultGetItemId = item => item.id;
4
+ const defaultGetItemChildren = item => item.children;
5
+
6
+ /**
7
+ * Applies the selection propagation rules to the selected items.
8
+ * The value is only computed during the first render, any update of the parameters will be ignored.
9
+ *
10
+ * Uncontrolled example:
11
+ * ```tsx
12
+ * const defaultSelectedItems = useApplyPropagationToSelectedItemsOnMount({
13
+ * items: props.items,
14
+ * selectionPropagation: props.selectionPropagation,
15
+ * selectedItems: ['10', '11', '13', '14'],
16
+ * });
17
+ *
18
+ * return (
19
+ * <RichTreeView
20
+ * items={props.items}
21
+ * selectionPropagation={props.selectionPropagation}
22
+ * defaultSelectedItems={defaultSelectedItems}
23
+ * />
24
+ * );
25
+ * ```
26
+ *
27
+ * Controlled example:
28
+ * ```tsx
29
+ * const initialSelectedItems = useApplyPropagationToSelectedItemsOnMount({
30
+ * items: props.items,
31
+ * selectionPropagation: props.selectionPropagation,
32
+ * selectedItems: ['10', '11', '13', '14'],
33
+ * });
34
+ *
35
+ * const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems);
36
+ *
37
+ * return (
38
+ * <RichTreeView
39
+ * items={props.items}
40
+ * selectionPropagation={props.selectionPropagation}
41
+ * selectedItems={selectedItems}
42
+ * onSelectedItemsChange={setSelectedItems}
43
+ * />
44
+ * );
45
+ * ```
46
+ */
47
+ export function useApplyPropagationToSelectedItemsOnMount(parameters) {
48
+ const {
49
+ items: itemsParam,
50
+ getItemId = defaultGetItemId,
51
+ getItemChildren = defaultGetItemChildren,
52
+ selectedItems,
53
+ selectionPropagation
54
+ } = parameters;
55
+ return useLazyRef(() => {
56
+ const lookup = getLookupFromArray(selectedItems);
57
+ function walk(items, isParentSelected) {
58
+ for (const item of items) {
59
+ const itemId = getItemId(item);
60
+ let isSelected = lookup[itemId];
61
+ if (!isSelected && selectionPropagation.descendants && isParentSelected) {
62
+ lookup[itemId] = true;
63
+ isSelected = true;
64
+ }
65
+ const children = getItemChildren(item) ?? [];
66
+ if (children.length > 0) {
67
+ walk(children, isSelected);
68
+ if (!isSelected && selectionPropagation.parents) {
69
+ const areAllChildrenSelected = children.every(childId => lookup[getItemId(childId)]);
70
+ if (areAllChildrenSelected) {
71
+ lookup[itemId] = true;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ walk(itemsParam, false);
78
+ return Object.keys(lookup);
79
+ }).current;
80
+ }
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v8.11.1
2
+ * @mui/x-tree-view v8.11.3
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -10,20 +10,24 @@ import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowComp
10
10
  import { TreeItem } from "../../TreeItem/index.js";
11
11
  import { itemsSelectors } from "../plugins/useTreeViewItems/index.js";
12
12
  import { useTreeViewContext } from "../TreeViewProvider/index.js";
13
+ import { expansionSelectors } from "../plugins/useTreeViewExpansion/index.js";
13
14
  import { jsx as _jsx } from "react/jsx-runtime";
14
15
  const RichTreeViewItemsContext = /*#__PURE__*/React.createContext(null);
15
16
  if (process.env.NODE_ENV !== "production") RichTreeViewItemsContext.displayName = "RichTreeViewItemsContext";
17
+ const EMPTY_ARRAY = [];
18
+ const selectorNoChildren = () => EMPTY_ARRAY;
16
19
  const WrappedTreeItem = /*#__PURE__*/React.memo(function WrappedTreeItem({
17
20
  itemSlot,
18
21
  itemSlotProps,
19
- itemId
22
+ itemId,
23
+ skipChildren
20
24
  }) {
21
25
  const renderItemForRichTreeView = React.useContext(RichTreeViewItemsContext);
22
26
  const {
23
27
  store
24
28
  } = useTreeViewContext();
25
29
  const itemMeta = useStore(store, itemsSelectors.itemMeta, itemId);
26
- const children = useStore(store, itemsSelectors.itemOrderedChildrenIds, itemId);
30
+ const children = useStore(store, skipChildren ? selectorNoChildren : itemsSelectors.itemOrderedChildrenIds, itemId);
27
31
  const Item = itemSlot ?? TreeItem;
28
32
  const _useSlotProps = useSlotProps({
29
33
  elementType: Item,
@@ -54,14 +58,17 @@ export function RichTreeViewItems(props) {
54
58
  } = useTreeViewContext();
55
59
  const itemSlot = slots?.item;
56
60
  const itemSlotProps = slotProps?.item;
57
- const items = useStore(store, itemsSelectors.itemOrderedChildrenIds, null);
61
+ const domStructure = useStore(store, itemsSelectors.domStructure);
62
+ const items = useStore(store, domStructure === 'flat' ? state => expansionSelectors.flatList(state) : state => itemsSelectors.itemOrderedChildrenIds(state, null));
63
+ const skipChildren = domStructure === 'flat';
58
64
  const renderItem = React.useCallback(itemId => {
59
65
  return /*#__PURE__*/_jsx(WrappedTreeItem, {
60
66
  itemSlot: itemSlot,
61
67
  itemSlotProps: itemSlotProps,
62
- itemId: itemId
68
+ itemId: itemId,
69
+ skipChildren: skipChildren
63
70
  }, itemId);
64
- }, [itemSlot, itemSlotProps]);
71
+ }, [itemSlot, itemSlotProps, skipChildren]);
65
72
  return /*#__PURE__*/_jsx(RichTreeViewItemsContext.Provider, {
66
73
  value: renderItem,
67
74
  children: items.map(renderItem)
@@ -6,11 +6,13 @@ export declare const expansionSelectors: {
6
6
  */
7
7
  expandedItemsRaw: (state: TreeViewState<[UseTreeViewExpansionSignature]>) => string[];
8
8
  /**
9
- * Get the expanded items as a Map.
10
- * @param {TreeViewState<[UseTreeViewExpansionSignature]>} state The state of the tree view.
11
- * @returns {TreeViewExpansionValue} The expanded items as a Map.
9
+ * Gets the expanded items as a Map.
12
10
  */
13
11
  expandedItemsMap: (args_0: import("../../corePlugins/useTreeViewId/useTreeViewId.types.js").UseTreeViewIdState & import("./useTreeViewExpansion.types.js").UseTreeViewExpansionState & Partial<{}>) => Map<string, true>;
12
+ /**
13
+ * Gets the items to render as a flat list (the descendants of an expanded item are listed as siblings of the item).
14
+ */
15
+ flatList: (args_0: import("../../corePlugins/useTreeViewId/useTreeViewId.types.js").UseTreeViewIdState & import("./useTreeViewExpansion.types.js").UseTreeViewExpansionState & Partial<{}>) => string[];
14
16
  /**
15
17
  * Gets the slot that triggers the item's expansion when clicked.
16
18
  */
@@ -1,5 +1,6 @@
1
1
  import { createSelector, createSelectorMemoized } from '@mui/x-internals/store';
2
2
  import { itemsSelectors } from "../useTreeViewItems/useTreeViewItems.selectors.js";
3
+ import { TREE_VIEW_ROOT_PARENT_ID } from "../useTreeViewItems/index.js";
3
4
  const expandedItemMapSelector = createSelectorMemoized(state => state.expansion.expandedItems, expandedItems => {
4
5
  const expandedItemsMap = new Map();
5
6
  expandedItems.forEach(id => {
@@ -13,11 +14,26 @@ export const expansionSelectors = {
13
14
  */
14
15
  expandedItemsRaw: createSelector(state => state.expansion.expandedItems),
15
16
  /**
16
- * Get the expanded items as a Map.
17
- * @param {TreeViewState<[UseTreeViewExpansionSignature]>} state The state of the tree view.
18
- * @returns {TreeViewExpansionValue} The expanded items as a Map.
17
+ * Gets the expanded items as a Map.
19
18
  */
20
19
  expandedItemsMap: expandedItemMapSelector,
20
+ /**
21
+ * Gets the items to render as a flat list (the descendants of an expanded item are listed as siblings of the item).
22
+ */
23
+ flatList: createSelectorMemoized(itemsSelectors.itemOrderedChildrenIdsLookup, expandedItemMapSelector, (itemOrderedChildrenIds, expandedItemsMap) => {
24
+ function appendChildren(itemId) {
25
+ if (!expandedItemsMap.has(itemId)) {
26
+ return [itemId];
27
+ }
28
+ const itemsWithDescendants = [itemId];
29
+ const children = itemOrderedChildrenIds[itemId] || [];
30
+ for (const childId of children) {
31
+ itemsWithDescendants.push(...appendChildren(childId));
32
+ }
33
+ return itemsWithDescendants;
34
+ }
35
+ return (itemOrderedChildrenIds[TREE_VIEW_ROOT_PARENT_ID] ?? []).flatMap(appendChildren);
36
+ }),
21
37
  /**
22
38
  * Gets the slot that triggers the item's expansion when clicked.
23
39
  */
@@ -1,6 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import useEventCallback from '@mui/utils/useEventCallback';
3
- import { useInstanceEventHandler } from "../../hooks/useInstanceEventHandler.js";
3
+ import { useStoreEffect } from '@mui/x-internals/store';
4
4
  import { focusSelectors } from "./useTreeViewFocus.selectors.js";
5
5
  import { expansionSelectors } from "../useTreeViewExpansion/useTreeViewExpansion.selectors.js";
6
6
  import { itemsSelectors } from "../useTreeViewItems/useTreeViewItems.selectors.js";
@@ -52,14 +52,23 @@ export const useTreeViewFocus = ({
52
52
  }
53
53
  setFocusedItemId(null);
54
54
  });
55
- useInstanceEventHandler(instance, 'removeItem', ({
56
- id
57
- }) => {
55
+
56
+ // Whenever the items change, we need to ensure the focused item is still present.
57
+ useStoreEffect(store, itemsSelectors.itemMetaLookup, () => {
58
58
  const focusedItemId = focusSelectors.focusedItemId(store.state);
59
+ if (focusedItemId == null) {
60
+ return;
61
+ }
62
+ const hasItemBeenRemoved = !itemsSelectors.itemMeta(store.state, focusedItemId);
63
+ if (!hasItemBeenRemoved) {
64
+ return;
65
+ }
59
66
  const defaultFocusableItemId = focusSelectors.defaultFocusableItemId(store.state);
60
- if (focusedItemId === id && defaultFocusableItemId != null) {
61
- innerFocusItem(null, defaultFocusableItemId);
67
+ if (defaultFocusableItemId == null) {
68
+ setFocusedItemId(null);
69
+ return;
62
70
  }
71
+ innerFocusItem(null, defaultFocusableItemId);
63
72
  });
64
73
  const createRootHandleFocus = otherHandlers => event => {
65
74
  otherHandlers.onFocus?.(event);
@@ -3,7 +3,6 @@
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
5
  import useEventCallback from '@mui/utils/useEventCallback';
6
- import { publishTreeViewEvent } from "../../utils/publishTreeViewEvent.js";
7
6
  import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from "./useTreeViewItems.utils.js";
8
7
  import { TreeViewItemDepthContext } from "../../TreeViewItemDepthContext/index.js";
9
8
  import { itemsSelectors } from "./useTreeViewItems.selectors.js";
@@ -115,13 +114,14 @@ export const useTreeViewItems = ({
115
114
  const getItemTree = React.useCallback(() => {
116
115
  const getItemFromItemId = itemId => {
117
116
  const item = itemsSelectors.itemModel(store.state, itemId);
117
+ const itemToMutate = _extends({}, item);
118
118
  const newChildren = itemsSelectors.itemOrderedChildrenIds(store.state, itemId);
119
119
  if (newChildren.length > 0) {
120
- item.children = newChildren.map(getItemFromItemId);
120
+ itemToMutate.children = newChildren.map(getItemFromItemId);
121
121
  } else {
122
- delete item.children;
122
+ delete itemToMutate.children;
123
123
  }
124
- return item;
124
+ return itemToMutate;
125
125
  };
126
126
  return itemsSelectors.itemOrderedChildrenIds(store.state, null).map(getItemFromItemId);
127
127
  }, [store]);
@@ -178,13 +178,6 @@ export const useTreeViewItems = ({
178
178
  itemChildrenIndexesLookup: newState.itemChildrenIndexesLookup
179
179
  };
180
180
  }
181
- Object.values(store.state.items.itemMetaLookup).forEach(item => {
182
- if (!newItems.itemMetaLookup[item.id]) {
183
- publishTreeViewEvent(instance, 'removeItem', {
184
- id: item.id
185
- });
186
- }
187
- });
188
181
  store.set('items', _extends({}, store.state.items, newItems));
189
182
  }
190
183
  };
@@ -199,9 +192,6 @@ export const useTreeViewItems = ({
199
192
  const newMetaMap = Object.keys(store.state.items.itemMetaLookup).reduce((acc, key) => {
200
193
  const item = store.state.items.itemMetaLookup[key];
201
194
  if (item.parentId === parentId) {
202
- publishTreeViewEvent(instance, 'removeItem', {
203
- id: item.id
204
- });
205
195
  return acc;
206
196
  }
207
197
  return _extends({}, acc, {
@@ -231,13 +221,6 @@ export const useTreeViewItems = ({
231
221
  getItemLabel: params.getItemLabel,
232
222
  getItemChildren: params.getItemChildren
233
223
  });
234
- Object.values(store.state.items.itemMetaLookup).forEach(item => {
235
- if (!newState.itemMetaLookup[item.id]) {
236
- publishTreeViewEvent(instance, 'removeItem', {
237
- id: item.id
238
- });
239
- }
240
- });
241
224
  store.set('items', _extends({}, store.state.items, newState));
242
225
  }, [instance, store, params.items, params.disabledItemsFocusable, params.isItemDisabled, params.getItemId, params.getItemLabel, params.getItemChildren]);
243
226
 
@@ -283,7 +266,8 @@ useTreeViewItems.getInitialState = params => ({
283
266
  getItemChildren: params.getItemChildren
284
267
  }), {
285
268
  loading: false,
286
- error: null
269
+ error: null,
270
+ domStructure: 'nested'
287
271
  })
288
272
  });
289
273
  useTreeViewItems.applyDefaultValuesToParams = ({
@@ -9,6 +9,10 @@ export declare const itemsSelectors: {
9
9
  * Gets the error state for the Tree View.
10
10
  */
11
11
  error: (state: TreeViewState<[UseTreeViewItemsSignature]>) => Error | null;
12
+ /**
13
+ * Gets the DOM structure of the Tree View.
14
+ */
15
+ domStructure: (state: TreeViewState<[UseTreeViewItemsSignature]>) => "flat" | "nested";
12
16
  /**
13
17
  * Checks whether the disabled items are focusable.
14
18
  */
@@ -19,6 +23,12 @@ export declare const itemsSelectors: {
19
23
  itemMetaLookup: (state: TreeViewState<[UseTreeViewItemsSignature]>) => {
20
24
  [itemId: string]: TreeViewItemMeta;
21
25
  };
26
+ /**
27
+ * Gets the ordered children ids of all items.
28
+ */
29
+ itemOrderedChildrenIdsLookup: (state: TreeViewState<[UseTreeViewItemsSignature]>) => {
30
+ [parentItemId: string]: string[];
31
+ };
22
32
  /**
23
33
  * Gets the meta-information of an item.
24
34
  */
@@ -10,6 +10,10 @@ export const itemsSelectors = {
10
10
  * Gets the error state for the Tree View.
11
11
  */
12
12
  error: createSelector(state => state.items.error),
13
+ /**
14
+ * Gets the DOM structure of the Tree View.
15
+ */
16
+ domStructure: createSelector(state => state.items.domStructure),
13
17
  /**
14
18
  * Checks whether the disabled items are focusable.
15
19
  */
@@ -18,6 +22,10 @@ export const itemsSelectors = {
18
22
  * Gets the meta-information of all items.
19
23
  */
20
24
  itemMetaLookup: createSelector(state => state.items.itemMetaLookup),
25
+ /**
26
+ * Gets the ordered children ids of all items.
27
+ */
28
+ itemOrderedChildrenIdsLookup: createSelector(state => state.items.itemOrderedChildrenIdsLookup),
21
29
  /**
22
30
  * Gets the meta-information of an item.
23
31
  */
@@ -149,13 +149,6 @@ export interface UseTreeViewItemsParameters<R extends {
149
149
  export type UseTreeViewItemsParametersWithDefaults<R extends {
150
150
  children?: R[];
151
151
  }> = DefaultizedProps<UseTreeViewItemsParameters<R>, 'disabledItemsFocusable' | 'itemChildrenIndentation'>;
152
- interface UseTreeViewItemsEventLookup {
153
- removeItem: {
154
- params: {
155
- id: string;
156
- };
157
- };
158
- }
159
152
  export interface UseTreeViewItemsState<R extends {}> {
160
153
  items: {
161
154
  /**
@@ -183,7 +176,7 @@ export interface UseTreeViewItemsState<R extends {}> {
183
176
  * Ordered children ids of each item.
184
177
  */
185
178
  itemOrderedChildrenIdsLookup: {
186
- [parentItemId: string]: string[];
179
+ [parentItemId: string]: TreeViewItemId[];
187
180
  };
188
181
  /**
189
182
  * Index of each child in the ordered children ids of its parent.
@@ -201,6 +194,12 @@ export interface UseTreeViewItemsState<R extends {}> {
201
194
  * The error state of the tree.
202
195
  */
203
196
  error: Error | null;
197
+ /**
198
+ * When equal to 'flat', the tree is rendered as a flat list (children are rendered as siblings of their parents).
199
+ * When equal to 'nested', the tree is rendered with nested children (children are rendered inside the groupTransition slot of their children).
200
+ * Nested DOM structure is not compatible with collapse / expansion animations.
201
+ */
202
+ domStructure: 'flat' | 'nested';
204
203
  };
205
204
  }
206
205
  export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
@@ -208,7 +207,5 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
208
207
  paramsWithDefaults: UseTreeViewItemsParametersWithDefaults<any>;
209
208
  instance: UseTreeViewItemsInstance<any>;
210
209
  publicAPI: UseTreeViewItemsPublicAPI<any>;
211
- events: UseTreeViewItemsEventLookup;
212
210
  state: UseTreeViewItemsState<TreeViewDefaultItemModelProperties>;
213
- }>;
214
- export {};
211
+ }>;
@@ -6,7 +6,6 @@ import { useStore } from '@mui/x-internals/store';
6
6
  import useEventCallback from '@mui/utils/useEventCallback';
7
7
  import useForkRef from '@mui/utils/useForkRef';
8
8
  import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
9
- import { publishTreeViewEvent } from "../../utils/publishTreeViewEvent.js";
10
9
  import { useTreeViewContext } from "../../TreeViewProvider/index.js";
11
10
  import { TreeViewChildrenItemContext, TreeViewChildrenItemProvider } from "../../TreeViewProvider/TreeViewChildrenItemProvider.js";
12
11
  import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from "../useTreeViewItems/useTreeViewItems.utils.js";
@@ -45,9 +44,6 @@ export const useTreeViewJSXItems = ({
45
44
  itemMetaLookup: newItemMetaLookup,
46
45
  itemModelLookup: newItemModelLookup
47
46
  }));
48
- publishTreeViewEvent(instance, 'removeItem', {
49
- id: item.id
50
- });
51
47
  };
52
48
  });
53
49
  const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
package/hooks/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { useTreeViewApiRef } from "./useTreeViewApiRef.js";
2
2
  export { useTreeItemUtils } from "./useTreeItemUtils/index.js";
3
- export { useTreeItemModel } from "./useTreeItemModel.js";
3
+ export { useTreeItemModel } from "./useTreeItemModel.js";
4
+ export { useApplyPropagationToSelectedItemsOnMount } from "./useApplyPropagationToSelectedItemsOnMount.js";
package/hooks/index.js CHANGED
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "useApplyPropagationToSelectedItemsOnMount", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _useApplyPropagationToSelectedItemsOnMount.useApplyPropagationToSelectedItemsOnMount;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "useTreeItemModel", {
7
13
  enumerable: true,
8
14
  get: function () {
@@ -23,4 +29,5 @@ Object.defineProperty(exports, "useTreeViewApiRef", {
23
29
  });
24
30
  var _useTreeViewApiRef = require("./useTreeViewApiRef");
25
31
  var _useTreeItemUtils = require("./useTreeItemUtils");
26
- var _useTreeItemModel = require("./useTreeItemModel");
32
+ var _useTreeItemModel = require("./useTreeItemModel");
33
+ var _useApplyPropagationToSelectedItemsOnMount = require("./useApplyPropagationToSelectedItemsOnMount");
@@ -0,0 +1,53 @@
1
+ import { TreeViewItemId, TreeViewSelectionPropagation } from "../models/index.js";
2
+ /**
3
+ * Applies the selection propagation rules to the selected items.
4
+ * The value is only computed during the first render, any update of the parameters will be ignored.
5
+ *
6
+ * Uncontrolled example:
7
+ * ```tsx
8
+ * const defaultSelectedItems = useApplyPropagationToSelectedItemsOnMount({
9
+ * items: props.items,
10
+ * selectionPropagation: props.selectionPropagation,
11
+ * selectedItems: ['10', '11', '13', '14'],
12
+ * });
13
+ *
14
+ * return (
15
+ * <RichTreeView
16
+ * items={props.items}
17
+ * selectionPropagation={props.selectionPropagation}
18
+ * defaultSelectedItems={defaultSelectedItems}
19
+ * />
20
+ * );
21
+ * ```
22
+ *
23
+ * Controlled example:
24
+ * ```tsx
25
+ * const initialSelectedItems = useApplyPropagationToSelectedItemsOnMount({
26
+ * items: props.items,
27
+ * selectionPropagation: props.selectionPropagation,
28
+ * selectedItems: ['10', '11', '13', '14'],
29
+ * });
30
+ *
31
+ * const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems);
32
+ *
33
+ * return (
34
+ * <RichTreeView
35
+ * items={props.items}
36
+ * selectionPropagation={props.selectionPropagation}
37
+ * selectedItems={selectedItems}
38
+ * onSelectedItemsChange={setSelectedItems}
39
+ * />
40
+ * );
41
+ * ```
42
+ */
43
+ export declare function useApplyPropagationToSelectedItemsOnMount(parameters: UseApplyPropagationToDefaultSelectedItemsParameters<any>): string[];
44
+ interface UseApplyPropagationToDefaultSelectedItemsParameters<R extends {
45
+ children?: R[];
46
+ }> {
47
+ items: R[];
48
+ getItemId?: (item: R) => TreeViewItemId;
49
+ getItemChildren?: (item: R) => R[] | undefined;
50
+ selectedItems: string[];
51
+ selectionPropagation: TreeViewSelectionPropagation;
52
+ }
53
+ export {};
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.useApplyPropagationToSelectedItemsOnMount = useApplyPropagationToSelectedItemsOnMount;
8
+ var _useLazyRef = _interopRequireDefault(require("@mui/utils/useLazyRef"));
9
+ var _useTreeViewSelection = require("../internals/plugins/useTreeViewSelection/useTreeViewSelection.utils");
10
+ const defaultGetItemId = item => item.id;
11
+ const defaultGetItemChildren = item => item.children;
12
+
13
+ /**
14
+ * Applies the selection propagation rules to the selected items.
15
+ * The value is only computed during the first render, any update of the parameters will be ignored.
16
+ *
17
+ * Uncontrolled example:
18
+ * ```tsx
19
+ * const defaultSelectedItems = useApplyPropagationToSelectedItemsOnMount({
20
+ * items: props.items,
21
+ * selectionPropagation: props.selectionPropagation,
22
+ * selectedItems: ['10', '11', '13', '14'],
23
+ * });
24
+ *
25
+ * return (
26
+ * <RichTreeView
27
+ * items={props.items}
28
+ * selectionPropagation={props.selectionPropagation}
29
+ * defaultSelectedItems={defaultSelectedItems}
30
+ * />
31
+ * );
32
+ * ```
33
+ *
34
+ * Controlled example:
35
+ * ```tsx
36
+ * const initialSelectedItems = useApplyPropagationToSelectedItemsOnMount({
37
+ * items: props.items,
38
+ * selectionPropagation: props.selectionPropagation,
39
+ * selectedItems: ['10', '11', '13', '14'],
40
+ * });
41
+ *
42
+ * const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems);
43
+ *
44
+ * return (
45
+ * <RichTreeView
46
+ * items={props.items}
47
+ * selectionPropagation={props.selectionPropagation}
48
+ * selectedItems={selectedItems}
49
+ * onSelectedItemsChange={setSelectedItems}
50
+ * />
51
+ * );
52
+ * ```
53
+ */
54
+ function useApplyPropagationToSelectedItemsOnMount(parameters) {
55
+ const {
56
+ items: itemsParam,
57
+ getItemId = defaultGetItemId,
58
+ getItemChildren = defaultGetItemChildren,
59
+ selectedItems,
60
+ selectionPropagation
61
+ } = parameters;
62
+ return (0, _useLazyRef.default)(() => {
63
+ const lookup = (0, _useTreeViewSelection.getLookupFromArray)(selectedItems);
64
+ function walk(items, isParentSelected) {
65
+ for (const item of items) {
66
+ const itemId = getItemId(item);
67
+ let isSelected = lookup[itemId];
68
+ if (!isSelected && selectionPropagation.descendants && isParentSelected) {
69
+ lookup[itemId] = true;
70
+ isSelected = true;
71
+ }
72
+ const children = getItemChildren(item) ?? [];
73
+ if (children.length > 0) {
74
+ walk(children, isSelected);
75
+ if (!isSelected && selectionPropagation.parents) {
76
+ const areAllChildrenSelected = children.every(childId => lookup[getItemId(childId)]);
77
+ if (areAllChildrenSelected) {
78
+ lookup[itemId] = true;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ walk(itemsParam, false);
85
+ return Object.keys(lookup);
86
+ }).current;
87
+ }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v8.11.1
2
+ * @mui/x-tree-view v8.11.3
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -16,21 +16,25 @@ var _fastObjectShallowCompare = require("@mui/x-internals/fastObjectShallowCompa
16
16
  var _TreeItem = require("../../TreeItem");
17
17
  var _useTreeViewItems = require("../plugins/useTreeViewItems");
18
18
  var _TreeViewProvider = require("../TreeViewProvider");
19
+ var _useTreeViewExpansion = require("../plugins/useTreeViewExpansion");
19
20
  var _jsxRuntime = require("react/jsx-runtime");
20
21
  const _excluded = ["ownerState"];
21
22
  const RichTreeViewItemsContext = /*#__PURE__*/React.createContext(null);
22
23
  if (process.env.NODE_ENV !== "production") RichTreeViewItemsContext.displayName = "RichTreeViewItemsContext";
24
+ const EMPTY_ARRAY = [];
25
+ const selectorNoChildren = () => EMPTY_ARRAY;
23
26
  const WrappedTreeItem = /*#__PURE__*/React.memo(function WrappedTreeItem({
24
27
  itemSlot,
25
28
  itemSlotProps,
26
- itemId
29
+ itemId,
30
+ skipChildren
27
31
  }) {
28
32
  const renderItemForRichTreeView = React.useContext(RichTreeViewItemsContext);
29
33
  const {
30
34
  store
31
35
  } = (0, _TreeViewProvider.useTreeViewContext)();
32
36
  const itemMeta = (0, _store.useStore)(store, _useTreeViewItems.itemsSelectors.itemMeta, itemId);
33
- const children = (0, _store.useStore)(store, _useTreeViewItems.itemsSelectors.itemOrderedChildrenIds, itemId);
37
+ const children = (0, _store.useStore)(store, skipChildren ? selectorNoChildren : _useTreeViewItems.itemsSelectors.itemOrderedChildrenIds, itemId);
34
38
  const Item = itemSlot ?? _TreeItem.TreeItem;
35
39
  const _useSlotProps = (0, _useSlotProps2.default)({
36
40
  elementType: Item,
@@ -61,14 +65,17 @@ function RichTreeViewItems(props) {
61
65
  } = (0, _TreeViewProvider.useTreeViewContext)();
62
66
  const itemSlot = slots?.item;
63
67
  const itemSlotProps = slotProps?.item;
64
- const items = (0, _store.useStore)(store, _useTreeViewItems.itemsSelectors.itemOrderedChildrenIds, null);
68
+ const domStructure = (0, _store.useStore)(store, _useTreeViewItems.itemsSelectors.domStructure);
69
+ const items = (0, _store.useStore)(store, domStructure === 'flat' ? state => _useTreeViewExpansion.expansionSelectors.flatList(state) : state => _useTreeViewItems.itemsSelectors.itemOrderedChildrenIds(state, null));
70
+ const skipChildren = domStructure === 'flat';
65
71
  const renderItem = React.useCallback(itemId => {
66
72
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(WrappedTreeItem, {
67
73
  itemSlot: itemSlot,
68
74
  itemSlotProps: itemSlotProps,
69
- itemId: itemId
75
+ itemId: itemId,
76
+ skipChildren: skipChildren
70
77
  }, itemId);
71
- }, [itemSlot, itemSlotProps]);
78
+ }, [itemSlot, itemSlotProps, skipChildren]);
72
79
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(RichTreeViewItemsContext.Provider, {
73
80
  value: renderItem,
74
81
  children: items.map(renderItem)
@@ -6,11 +6,13 @@ export declare const expansionSelectors: {
6
6
  */
7
7
  expandedItemsRaw: (state: TreeViewState<[UseTreeViewExpansionSignature]>) => string[];
8
8
  /**
9
- * Get the expanded items as a Map.
10
- * @param {TreeViewState<[UseTreeViewExpansionSignature]>} state The state of the tree view.
11
- * @returns {TreeViewExpansionValue} The expanded items as a Map.
9
+ * Gets the expanded items as a Map.
12
10
  */
13
11
  expandedItemsMap: (args_0: import("../../corePlugins/useTreeViewId/useTreeViewId.types.js").UseTreeViewIdState & import("./useTreeViewExpansion.types.js").UseTreeViewExpansionState & Partial<{}>) => Map<string, true>;
12
+ /**
13
+ * Gets the items to render as a flat list (the descendants of an expanded item are listed as siblings of the item).
14
+ */
15
+ flatList: (args_0: import("../../corePlugins/useTreeViewId/useTreeViewId.types.js").UseTreeViewIdState & import("./useTreeViewExpansion.types.js").UseTreeViewExpansionState & Partial<{}>) => string[];
14
16
  /**
15
17
  * Gets the slot that triggers the item's expansion when clicked.
16
18
  */
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.expansionSelectors = void 0;
7
7
  var _store = require("@mui/x-internals/store");
8
8
  var _useTreeViewItems = require("../useTreeViewItems/useTreeViewItems.selectors");
9
+ var _useTreeViewItems2 = require("../useTreeViewItems");
9
10
  const expandedItemMapSelector = (0, _store.createSelectorMemoized)(state => state.expansion.expandedItems, expandedItems => {
10
11
  const expandedItemsMap = new Map();
11
12
  expandedItems.forEach(id => {
@@ -19,11 +20,26 @@ const expansionSelectors = exports.expansionSelectors = {
19
20
  */
20
21
  expandedItemsRaw: (0, _store.createSelector)(state => state.expansion.expandedItems),
21
22
  /**
22
- * Get the expanded items as a Map.
23
- * @param {TreeViewState<[UseTreeViewExpansionSignature]>} state The state of the tree view.
24
- * @returns {TreeViewExpansionValue} The expanded items as a Map.
23
+ * Gets the expanded items as a Map.
25
24
  */
26
25
  expandedItemsMap: expandedItemMapSelector,
26
+ /**
27
+ * Gets the items to render as a flat list (the descendants of an expanded item are listed as siblings of the item).
28
+ */
29
+ flatList: (0, _store.createSelectorMemoized)(_useTreeViewItems.itemsSelectors.itemOrderedChildrenIdsLookup, expandedItemMapSelector, (itemOrderedChildrenIds, expandedItemsMap) => {
30
+ function appendChildren(itemId) {
31
+ if (!expandedItemsMap.has(itemId)) {
32
+ return [itemId];
33
+ }
34
+ const itemsWithDescendants = [itemId];
35
+ const children = itemOrderedChildrenIds[itemId] || [];
36
+ for (const childId of children) {
37
+ itemsWithDescendants.push(...appendChildren(childId));
38
+ }
39
+ return itemsWithDescendants;
40
+ }
41
+ return (itemOrderedChildrenIds[_useTreeViewItems2.TREE_VIEW_ROOT_PARENT_ID] ?? []).flatMap(appendChildren);
42
+ }),
27
43
  /**
28
44
  * Gets the slot that triggers the item's expansion when clicked.
29
45
  */
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.useTreeViewFocus = void 0;
8
8
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9
9
  var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
10
- var _useInstanceEventHandler = require("../../hooks/useInstanceEventHandler");
10
+ var _store = require("@mui/x-internals/store");
11
11
  var _useTreeViewFocus = require("./useTreeViewFocus.selectors");
12
12
  var _useTreeViewExpansion = require("../useTreeViewExpansion/useTreeViewExpansion.selectors");
13
13
  var _useTreeViewItems = require("../useTreeViewItems/useTreeViewItems.selectors");
@@ -59,14 +59,23 @@ const useTreeViewFocus = ({
59
59
  }
60
60
  setFocusedItemId(null);
61
61
  });
62
- (0, _useInstanceEventHandler.useInstanceEventHandler)(instance, 'removeItem', ({
63
- id
64
- }) => {
62
+
63
+ // Whenever the items change, we need to ensure the focused item is still present.
64
+ (0, _store.useStoreEffect)(store, _useTreeViewItems.itemsSelectors.itemMetaLookup, () => {
65
65
  const focusedItemId = _useTreeViewFocus.focusSelectors.focusedItemId(store.state);
66
+ if (focusedItemId == null) {
67
+ return;
68
+ }
69
+ const hasItemBeenRemoved = !_useTreeViewItems.itemsSelectors.itemMeta(store.state, focusedItemId);
70
+ if (!hasItemBeenRemoved) {
71
+ return;
72
+ }
66
73
  const defaultFocusableItemId = _useTreeViewFocus.focusSelectors.defaultFocusableItemId(store.state);
67
- if (focusedItemId === id && defaultFocusableItemId != null) {
68
- innerFocusItem(null, defaultFocusableItemId);
74
+ if (defaultFocusableItemId == null) {
75
+ setFocusedItemId(null);
76
+ return;
69
77
  }
78
+ innerFocusItem(null, defaultFocusableItemId);
70
79
  });
71
80
  const createRootHandleFocus = otherHandlers => event => {
72
81
  otherHandlers.onFocus?.(event);
@@ -10,7 +10,6 @@ exports.useTreeViewItems = void 0;
10
10
  var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
11
  var React = _interopRequireWildcard(require("react"));
12
12
  var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
13
- var _publishTreeViewEvent = require("../../utils/publishTreeViewEvent");
14
13
  var _useTreeViewItems = require("./useTreeViewItems.utils");
15
14
  var _TreeViewItemDepthContext = require("../../TreeViewItemDepthContext");
16
15
  var _useTreeViewItems2 = require("./useTreeViewItems.selectors");
@@ -122,13 +121,14 @@ const useTreeViewItems = ({
122
121
  const getItemTree = React.useCallback(() => {
123
122
  const getItemFromItemId = itemId => {
124
123
  const item = _useTreeViewItems2.itemsSelectors.itemModel(store.state, itemId);
124
+ const itemToMutate = (0, _extends2.default)({}, item);
125
125
  const newChildren = _useTreeViewItems2.itemsSelectors.itemOrderedChildrenIds(store.state, itemId);
126
126
  if (newChildren.length > 0) {
127
- item.children = newChildren.map(getItemFromItemId);
127
+ itemToMutate.children = newChildren.map(getItemFromItemId);
128
128
  } else {
129
- delete item.children;
129
+ delete itemToMutate.children;
130
130
  }
131
- return item;
131
+ return itemToMutate;
132
132
  };
133
133
  return _useTreeViewItems2.itemsSelectors.itemOrderedChildrenIds(store.state, null).map(getItemFromItemId);
134
134
  }, [store]);
@@ -185,13 +185,6 @@ const useTreeViewItems = ({
185
185
  itemChildrenIndexesLookup: newState.itemChildrenIndexesLookup
186
186
  };
187
187
  }
188
- Object.values(store.state.items.itemMetaLookup).forEach(item => {
189
- if (!newItems.itemMetaLookup[item.id]) {
190
- (0, _publishTreeViewEvent.publishTreeViewEvent)(instance, 'removeItem', {
191
- id: item.id
192
- });
193
- }
194
- });
195
188
  store.set('items', (0, _extends2.default)({}, store.state.items, newItems));
196
189
  }
197
190
  };
@@ -206,9 +199,6 @@ const useTreeViewItems = ({
206
199
  const newMetaMap = Object.keys(store.state.items.itemMetaLookup).reduce((acc, key) => {
207
200
  const item = store.state.items.itemMetaLookup[key];
208
201
  if (item.parentId === parentId) {
209
- (0, _publishTreeViewEvent.publishTreeViewEvent)(instance, 'removeItem', {
210
- id: item.id
211
- });
212
202
  return acc;
213
203
  }
214
204
  return (0, _extends2.default)({}, acc, {
@@ -238,13 +228,6 @@ const useTreeViewItems = ({
238
228
  getItemLabel: params.getItemLabel,
239
229
  getItemChildren: params.getItemChildren
240
230
  });
241
- Object.values(store.state.items.itemMetaLookup).forEach(item => {
242
- if (!newState.itemMetaLookup[item.id]) {
243
- (0, _publishTreeViewEvent.publishTreeViewEvent)(instance, 'removeItem', {
244
- id: item.id
245
- });
246
- }
247
- });
248
231
  store.set('items', (0, _extends2.default)({}, store.state.items, newState));
249
232
  }, [instance, store, params.items, params.disabledItemsFocusable, params.isItemDisabled, params.getItemId, params.getItemLabel, params.getItemChildren]);
250
233
 
@@ -291,7 +274,8 @@ useTreeViewItems.getInitialState = params => ({
291
274
  getItemChildren: params.getItemChildren
292
275
  }), {
293
276
  loading: false,
294
- error: null
277
+ error: null,
278
+ domStructure: 'nested'
295
279
  })
296
280
  });
297
281
  useTreeViewItems.applyDefaultValuesToParams = ({
@@ -9,6 +9,10 @@ export declare const itemsSelectors: {
9
9
  * Gets the error state for the Tree View.
10
10
  */
11
11
  error: (state: TreeViewState<[UseTreeViewItemsSignature]>) => Error | null;
12
+ /**
13
+ * Gets the DOM structure of the Tree View.
14
+ */
15
+ domStructure: (state: TreeViewState<[UseTreeViewItemsSignature]>) => "flat" | "nested";
12
16
  /**
13
17
  * Checks whether the disabled items are focusable.
14
18
  */
@@ -19,6 +23,12 @@ export declare const itemsSelectors: {
19
23
  itemMetaLookup: (state: TreeViewState<[UseTreeViewItemsSignature]>) => {
20
24
  [itemId: string]: TreeViewItemMeta;
21
25
  };
26
+ /**
27
+ * Gets the ordered children ids of all items.
28
+ */
29
+ itemOrderedChildrenIdsLookup: (state: TreeViewState<[UseTreeViewItemsSignature]>) => {
30
+ [parentItemId: string]: string[];
31
+ };
22
32
  /**
23
33
  * Gets the meta-information of an item.
24
34
  */
@@ -16,6 +16,10 @@ const itemsSelectors = exports.itemsSelectors = {
16
16
  * Gets the error state for the Tree View.
17
17
  */
18
18
  error: (0, _store.createSelector)(state => state.items.error),
19
+ /**
20
+ * Gets the DOM structure of the Tree View.
21
+ */
22
+ domStructure: (0, _store.createSelector)(state => state.items.domStructure),
19
23
  /**
20
24
  * Checks whether the disabled items are focusable.
21
25
  */
@@ -24,6 +28,10 @@ const itemsSelectors = exports.itemsSelectors = {
24
28
  * Gets the meta-information of all items.
25
29
  */
26
30
  itemMetaLookup: (0, _store.createSelector)(state => state.items.itemMetaLookup),
31
+ /**
32
+ * Gets the ordered children ids of all items.
33
+ */
34
+ itemOrderedChildrenIdsLookup: (0, _store.createSelector)(state => state.items.itemOrderedChildrenIdsLookup),
27
35
  /**
28
36
  * Gets the meta-information of an item.
29
37
  */
@@ -149,13 +149,6 @@ export interface UseTreeViewItemsParameters<R extends {
149
149
  export type UseTreeViewItemsParametersWithDefaults<R extends {
150
150
  children?: R[];
151
151
  }> = DefaultizedProps<UseTreeViewItemsParameters<R>, 'disabledItemsFocusable' | 'itemChildrenIndentation'>;
152
- interface UseTreeViewItemsEventLookup {
153
- removeItem: {
154
- params: {
155
- id: string;
156
- };
157
- };
158
- }
159
152
  export interface UseTreeViewItemsState<R extends {}> {
160
153
  items: {
161
154
  /**
@@ -183,7 +176,7 @@ export interface UseTreeViewItemsState<R extends {}> {
183
176
  * Ordered children ids of each item.
184
177
  */
185
178
  itemOrderedChildrenIdsLookup: {
186
- [parentItemId: string]: string[];
179
+ [parentItemId: string]: TreeViewItemId[];
187
180
  };
188
181
  /**
189
182
  * Index of each child in the ordered children ids of its parent.
@@ -201,6 +194,12 @@ export interface UseTreeViewItemsState<R extends {}> {
201
194
  * The error state of the tree.
202
195
  */
203
196
  error: Error | null;
197
+ /**
198
+ * When equal to 'flat', the tree is rendered as a flat list (children are rendered as siblings of their parents).
199
+ * When equal to 'nested', the tree is rendered with nested children (children are rendered inside the groupTransition slot of their children).
200
+ * Nested DOM structure is not compatible with collapse / expansion animations.
201
+ */
202
+ domStructure: 'flat' | 'nested';
204
203
  };
205
204
  }
206
205
  export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
@@ -208,7 +207,5 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
208
207
  paramsWithDefaults: UseTreeViewItemsParametersWithDefaults<any>;
209
208
  instance: UseTreeViewItemsInstance<any>;
210
209
  publicAPI: UseTreeViewItemsPublicAPI<any>;
211
- events: UseTreeViewItemsEventLookup;
212
210
  state: UseTreeViewItemsState<TreeViewDefaultItemModelProperties>;
213
- }>;
214
- export {};
211
+ }>;
@@ -13,7 +13,6 @@ var _store = require("@mui/x-internals/store");
13
13
  var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
14
14
  var _useForkRef = _interopRequireDefault(require("@mui/utils/useForkRef"));
15
15
  var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
16
- var _publishTreeViewEvent = require("../../utils/publishTreeViewEvent");
17
16
  var _TreeViewProvider = require("../../TreeViewProvider");
18
17
  var _TreeViewChildrenItemProvider = require("../../TreeViewProvider/TreeViewChildrenItemProvider");
19
18
  var _useTreeViewItems = require("../useTreeViewItems/useTreeViewItems.utils");
@@ -52,9 +51,6 @@ const useTreeViewJSXItems = ({
52
51
  itemMetaLookup: newItemMetaLookup,
53
52
  itemModelLookup: newItemModelLookup
54
53
  }));
55
- (0, _publishTreeViewEvent.publishTreeViewEvent)(instance, 'removeItem', {
56
- id: item.id
57
- });
58
54
  };
59
55
  });
60
56
  const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-tree-view",
3
- "version": "8.11.1",
3
+ "version": "8.11.3",
4
4
  "author": "MUI Team",
5
5
  "description": "The community edition of the MUI X Tree View components.",
6
6
  "license": "MIT",
@@ -32,13 +32,13 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@babel/runtime": "^7.28.2",
35
- "@base-ui-components/utils": "0.1.0",
36
- "@mui/utils": "^7.3.1",
35
+ "@base-ui-components/utils": "0.1.1",
36
+ "@mui/utils": "^7.3.2",
37
37
  "@types/react-transition-group": "^4.4.12",
38
38
  "clsx": "^2.1.1",
39
39
  "prop-types": "^15.8.1",
40
40
  "react-transition-group": "^4.4.5",
41
- "@mui/x-internals": "8.11.1"
41
+ "@mui/x-internals": "8.11.3"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@emotion/react": "^11.9.0",