@mui/x-tree-view 8.0.0-alpha.0 → 8.0.0-alpha.2

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 (159) hide show
  1. package/CHANGELOG.md +651 -6
  2. package/README.md +2 -2
  3. package/RichTreeView/RichTreeView.js +2 -4
  4. package/RichTreeView/RichTreeView.types.d.ts +5 -18
  5. package/SimpleTreeView/SimpleTreeView.types.d.ts +2 -2
  6. package/TreeItem/TreeItem.js +4 -4
  7. package/TreeItem/TreeItem.types.d.ts +4 -2
  8. package/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
  9. package/TreeItemIcon/TreeItemIcon.types.d.ts +1 -1
  10. package/TreeItemProvider/TreeItemProvider.d.ts +2 -4
  11. package/TreeItemProvider/TreeItemProvider.js +26 -11
  12. package/TreeItemProvider/TreeItemProvider.types.d.ts +1 -0
  13. package/hooks/index.d.ts +1 -0
  14. package/hooks/index.js +2 -1
  15. package/hooks/useTreeItemModel.d.ts +2 -0
  16. package/hooks/useTreeItemModel.js +11 -0
  17. package/hooks/useTreeItemUtils/useTreeItemUtils.d.ts +2 -1
  18. package/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
  19. package/hooks/useTreeViewApiRef.d.ts +1 -0
  20. package/index.js +1 -1
  21. package/internals/TreeViewItemDepthContext/TreeViewItemDepthContext.d.ts +3 -1
  22. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.d.ts +2 -1
  23. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  24. package/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  25. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +4 -2
  26. package/internals/components/RichTreeViewItems.d.ts +3 -5
  27. package/internals/components/RichTreeViewItems.js +42 -30
  28. package/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
  29. package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.d.ts +36 -0
  30. package/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
  31. package/internals/corePlugins/useTreeViewId/useTreeViewId.types.d.ts +1 -5
  32. package/internals/hooks/useSelector.d.ts +4 -0
  33. package/internals/hooks/useSelector.js +6 -0
  34. package/internals/index.d.ts +6 -1
  35. package/internals/index.js +5 -1
  36. package/internals/models/itemPlugin.d.ts +5 -5
  37. package/internals/models/plugin.d.ts +20 -8
  38. package/internals/models/treeView.d.ts +6 -0
  39. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  40. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.d.ts +124 -0
  41. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
  42. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +6 -14
  43. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.d.ts +1 -0
  44. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
  45. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  46. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.d.ts +182 -0
  47. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
  48. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +4 -16
  49. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
  50. package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +1 -1
  51. package/internals/plugins/useTreeViewItems/index.d.ts +1 -1
  52. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
  53. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +718 -0
  54. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
  55. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +15 -52
  56. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
  57. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  58. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  59. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  60. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.d.ts +74 -0
  61. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
  62. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +7 -24
  63. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  64. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
  65. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.d.ts +32 -0
  66. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
  67. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +6 -6
  68. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +6 -6
  69. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
  70. package/internals/useTreeView/useTreeView.js +30 -17
  71. package/internals/useTreeView/useTreeView.types.d.ts +2 -3
  72. package/internals/useTreeView/useTreeViewBuildContext.d.ts +3 -1
  73. package/internals/useTreeView/useTreeViewBuildContext.js +24 -18
  74. package/internals/utils/TreeViewStore.d.ts +12 -0
  75. package/internals/utils/TreeViewStore.js +24 -0
  76. package/internals/utils/selectors.d.ts +9 -0
  77. package/internals/utils/selectors.js +37 -0
  78. package/internals/utils/tree.d.ts +8 -8
  79. package/internals/utils/tree.js +51 -43
  80. package/models/items.d.ts +3 -2
  81. package/modern/RichTreeView/RichTreeView.js +2 -4
  82. package/modern/TreeItem/TreeItem.js +4 -4
  83. package/modern/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +1 -1
  84. package/modern/TreeItemProvider/TreeItemProvider.js +26 -11
  85. package/modern/hooks/index.js +2 -1
  86. package/modern/hooks/useTreeItemModel.js +11 -0
  87. package/modern/hooks/useTreeItemUtils/useTreeItemUtils.js +31 -15
  88. package/modern/index.js +1 -1
  89. package/modern/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  90. package/modern/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  91. package/modern/internals/components/RichTreeViewItems.js +42 -30
  92. package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.js +10 -11
  93. package/modern/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +9 -0
  94. package/modern/internals/hooks/useSelector.js +6 -0
  95. package/modern/internals/index.js +5 -1
  96. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  97. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +17 -0
  98. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +7 -0
  99. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  100. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +34 -0
  101. package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +15 -13
  102. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +58 -98
  103. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +103 -0
  104. package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +29 -26
  105. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  106. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  107. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  108. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +26 -0
  109. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  110. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +45 -34
  111. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +9 -0
  112. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +23 -13
  113. package/modern/internals/useTreeView/useTreeView.js +30 -17
  114. package/modern/internals/useTreeView/useTreeViewBuildContext.js +24 -18
  115. package/modern/internals/utils/TreeViewStore.js +24 -0
  116. package/modern/internals/utils/selectors.js +37 -0
  117. package/modern/internals/utils/tree.js +51 -43
  118. package/modern/useTreeItem/useTreeItem.js +26 -11
  119. package/node/RichTreeView/RichTreeView.js +2 -4
  120. package/node/TreeItem/TreeItem.js +4 -4
  121. package/node/TreeItemDragAndDropOverlay/TreeItemDragAndDropOverlay.js +2 -2
  122. package/node/TreeItemProvider/TreeItemProvider.js +26 -10
  123. package/node/hooks/index.js +8 -1
  124. package/node/hooks/useTreeItemModel.js +17 -0
  125. package/node/hooks/useTreeItemUtils/useTreeItemUtils.js +32 -15
  126. package/node/index.js +1 -1
  127. package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +6 -22
  128. package/node/internals/TreeViewProvider/TreeViewProvider.js +1 -2
  129. package/node/internals/components/RichTreeViewItems.js +42 -30
  130. package/node/internals/corePlugins/useTreeViewId/useTreeViewId.js +12 -13
  131. package/node/internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js +15 -0
  132. package/node/internals/hooks/useSelector.js +13 -0
  133. package/node/internals/index.js +47 -1
  134. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +36 -24
  135. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js +23 -0
  136. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.utils.js +14 -0
  137. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +62 -40
  138. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js +40 -0
  139. package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +16 -13
  140. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +60 -100
  141. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +109 -0
  142. package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +30 -27
  143. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +27 -18
  144. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +13 -5
  145. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +19 -30
  146. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js +32 -0
  147. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +8 -6
  148. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +46 -35
  149. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js +15 -0
  150. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +24 -14
  151. package/node/internals/useTreeView/useTreeView.js +30 -17
  152. package/node/internals/useTreeView/useTreeViewBuildContext.js +25 -18
  153. package/node/internals/utils/TreeViewStore.js +31 -0
  154. package/node/internals/utils/selectors.js +44 -0
  155. package/node/internals/utils/tree.js +51 -43
  156. package/node/useTreeItem/useTreeItem.js +26 -11
  157. package/package.json +6 -4
  158. package/useTreeItem/useTreeItem.js +26 -11
  159. package/useTreeItem/useTreeItem.types.d.ts +9 -0
@@ -1,30 +1,45 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
1
4
  import PropTypes from 'prop-types';
2
5
  import { useTreeViewContext } from "../internals/TreeViewProvider/index.js";
3
-
4
- /**
5
- * @ignore - internal component.
6
- */
6
+ import { generateTreeItemIdAttribute } from "../internals/corePlugins/useTreeViewId/useTreeViewId.utils.js";
7
+ import { useSelector } from "../internals/hooks/useSelector.js";
8
+ import { selectorTreeViewId } from "../internals/corePlugins/useTreeViewId/useTreeViewId.selectors.js";
9
+ import { jsx as _jsx } from "react/jsx-runtime";
7
10
  function TreeItemProvider(props) {
8
11
  const {
9
12
  children,
10
- itemId
13
+ itemId,
14
+ id
11
15
  } = props;
12
16
  const {
13
17
  wrapItem,
14
- instance
18
+ instance,
19
+ store
15
20
  } = useTreeViewContext();
16
- return wrapItem({
17
- children,
21
+ const treeId = useSelector(store, selectorTreeViewId);
22
+ const idAttribute = generateTreeItemIdAttribute({
18
23
  itemId,
19
- instance
24
+ treeId,
25
+ id
26
+ });
27
+ return /*#__PURE__*/_jsx(React.Fragment, {
28
+ children: wrapItem({
29
+ children,
30
+ itemId,
31
+ instance,
32
+ idAttribute
33
+ })
20
34
  });
21
35
  }
22
- TreeItemProvider.propTypes = {
36
+ process.env.NODE_ENV !== "production" ? TreeItemProvider.propTypes = {
23
37
  // ----------------------------- Warning --------------------------------
24
38
  // | These PropTypes are generated from the TypeScript type definitions |
25
39
  // | To update them edit the TypeScript types and run "pnpm proptypes" |
26
40
  // ----------------------------------------------------------------------
27
41
  children: PropTypes.node,
42
+ id: PropTypes.string,
28
43
  itemId: PropTypes.string.isRequired
29
- };
44
+ } : void 0;
30
45
  export { TreeItemProvider };
@@ -1,2 +1,3 @@
1
1
  export { useTreeViewApiRef } from "./useTreeViewApiRef.js";
2
- export { useTreeItemUtils } from "./useTreeItemUtils/index.js";
2
+ export { useTreeItemUtils } from "./useTreeItemUtils/index.js";
3
+ export { useTreeItemModel } from "./useTreeItemModel.js";
@@ -0,0 +1,11 @@
1
+ 'use client';
2
+
3
+ import { useTreeViewContext } from "../internals/TreeViewProvider/index.js";
4
+ import { useSelector } from "../internals/hooks/useSelector.js";
5
+ import { selectorItemModel } from "../internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js";
6
+ export const useTreeItemModel = itemId => {
7
+ const {
8
+ store
9
+ } = useTreeViewContext();
10
+ return useSelector(store, selectorItemModel, itemId);
11
+ };
@@ -3,6 +3,12 @@
3
3
  import { useTreeViewContext } from "../../internals/TreeViewProvider/index.js";
4
4
  import { useTreeViewLabel } from "../../internals/plugins/useTreeViewLabel/index.js";
5
5
  import { hasPlugin } from "../../internals/utils/plugins.js";
6
+ import { useSelector } from "../../internals/hooks/useSelector.js";
7
+ import { selectorIsItemExpanded } from "../../internals/plugins/useTreeViewExpansion/useTreeViewExpansion.selectors.js";
8
+ import { selectorIsItemFocused } from "../../internals/plugins/useTreeViewFocus/useTreeViewFocus.selectors.js";
9
+ import { selectorIsItemDisabled } from "../../internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js";
10
+ import { selectorIsItemSelected } from "../../internals/plugins/useTreeViewSelection/useTreeViewSelection.selectors.js";
11
+ import { selectorIsItemBeingEdited, selectorIsItemEditable } from "../../internals/plugins/useTreeViewLabel/useTreeViewLabel.selectors.js";
6
12
 
7
13
  /**
8
14
  * Plugins that need to be present in the Tree View in order for `useTreeItemUtils` to work correctly.
@@ -12,7 +18,7 @@ import { hasPlugin } from "../../internals/utils/plugins.js";
12
18
  * Plugins that `useTreeItemUtils` can use if they are present, but are not required.
13
19
  */
14
20
 
15
- const isItemExpandable = reactChildren => {
21
+ export const isItemExpandable = reactChildren => {
16
22
  if (Array.isArray(reactChildren)) {
17
23
  return reactChildren.length > 0 && reactChildren.some(isItemExpandable);
18
24
  }
@@ -24,19 +30,30 @@ export const useTreeItemUtils = ({
24
30
  }) => {
25
31
  const {
26
32
  instance,
33
+ label,
34
+ store,
27
35
  selection: {
28
36
  multiSelect
29
37
  },
30
38
  publicAPI
31
39
  } = useTreeViewContext();
40
+ const isExpanded = useSelector(store, selectorIsItemExpanded, itemId);
41
+ const isFocused = useSelector(store, selectorIsItemFocused, itemId);
42
+ const isSelected = useSelector(store, selectorIsItemSelected, itemId);
43
+ const isDisabled = useSelector(store, selectorIsItemDisabled, itemId);
44
+ const isEditing = useSelector(store, state => label == null ? false : selectorIsItemBeingEdited(state, itemId));
45
+ const isEditable = useSelector(store, state => label == null ? false : selectorIsItemEditable(state, {
46
+ itemId,
47
+ isItemEditable: label.isItemEditable
48
+ }));
32
49
  const status = {
33
50
  expandable: isItemExpandable(children),
34
- expanded: instance.isItemExpanded(itemId),
35
- focused: instance.isItemFocused(itemId),
36
- selected: instance.isItemSelected(itemId),
37
- disabled: instance.isItemDisabled(itemId),
38
- editing: instance?.isItemBeingEdited ? instance?.isItemBeingEdited(itemId) : false,
39
- editable: instance.isItemEditable ? instance.isItemEditable(itemId) : false
51
+ expanded: isExpanded,
52
+ focused: isFocused,
53
+ selected: isSelected,
54
+ disabled: isDisabled,
55
+ editing: isEditing,
56
+ editable: isEditable
40
57
  };
41
58
  const handleExpansion = event => {
42
59
  if (status.disabled) {
@@ -48,7 +65,7 @@ export const useTreeItemUtils = ({
48
65
  const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey);
49
66
 
50
67
  // If already expanded and trying to toggle selection don't close
51
- if (status.expandable && !(multiple && instance.isItemExpanded(itemId))) {
68
+ if (status.expandable && !(multiple && selectorIsItemExpanded(store.value, itemId))) {
52
69
  instance.toggleItemExpansion(event, itemId);
53
70
  }
54
71
  };
@@ -95,15 +112,15 @@ export const useTreeItemUtils = ({
95
112
  if (!hasPlugin(instance, useTreeViewLabel)) {
96
113
  return;
97
114
  }
98
- if (instance.isItemEditable(itemId)) {
99
- if (instance.isItemBeingEdited(itemId)) {
115
+ if (isEditable) {
116
+ if (isEditing) {
100
117
  instance.setEditedItemId(null);
101
118
  } else {
102
119
  instance.setEditedItemId(itemId);
103
120
  }
104
121
  }
105
122
  };
106
- const handleSaveItemLabel = (event, label) => {
123
+ const handleSaveItemLabel = (event, newLabel) => {
107
124
  if (!hasPlugin(instance, useTreeViewLabel)) {
108
125
  return;
109
126
  }
@@ -111,9 +128,8 @@ export const useTreeItemUtils = ({
111
128
  // As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred
112
129
  // The `onBlur` event is triggered, which calls `handleSaveItemLabel` again.
113
130
  // To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel`
114
- // using `instance.isItemBeingEditedRef` instead of `instance.isItemBeingEdited` since the state is not yet updated in this point
115
- if (instance.isItemBeingEditedRef(itemId)) {
116
- instance.updateItemLabel(itemId, label);
131
+ if (selectorIsItemBeingEdited(store.value, itemId)) {
132
+ instance.updateItemLabel(itemId, newLabel);
117
133
  toggleItemEditing();
118
134
  instance.focusItem(event, itemId);
119
135
  }
@@ -122,7 +138,7 @@ export const useTreeItemUtils = ({
122
138
  if (!hasPlugin(instance, useTreeViewLabel)) {
123
139
  return;
124
140
  }
125
- if (instance.isItemBeingEditedRef(itemId)) {
141
+ if (selectorIsItemBeingEdited(store.value, itemId)) {
126
142
  toggleItemEditing();
127
143
  instance.focusItem(event, itemId);
128
144
  }
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v8.0.0-alpha.0
2
+ * @mui/x-tree-view v8.0.0-alpha.2
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useTreeViewContext } from "./useTreeViewContext.js";
4
4
  import { escapeOperandAttributeSelector } from "../utils/utils.js";
5
- import { generateTreeItemIdAttribute } from "../corePlugins/useTreeViewId/useTreeViewId.utils.js";
5
+ import { selectorItemOrderedChildrenIds } from "../plugins/useTreeViewItems/useTreeViewItems.selectors.js";
6
6
  import { jsx as _jsx } from "react/jsx-runtime";
7
7
  export const TreeViewChildrenItemContext = /*#__PURE__*/React.createContext(null);
8
8
  if (process.env.NODE_ENV !== 'production') {
@@ -11,11 +11,12 @@ if (process.env.NODE_ENV !== 'production') {
11
11
  export function TreeViewChildrenItemProvider(props) {
12
12
  const {
13
13
  children,
14
- itemId = null
14
+ itemId = null,
15
+ idAttribute
15
16
  } = props;
16
17
  const {
17
18
  instance,
18
- treeId,
19
+ store,
19
20
  rootRef
20
21
  } = useTreeViewContext();
21
22
  const childrenIdAttrToIdRef = React.useRef(new Map());
@@ -23,25 +24,8 @@ export function TreeViewChildrenItemProvider(props) {
23
24
  if (!rootRef.current) {
24
25
  return;
25
26
  }
26
- let idAttr = null;
27
- if (itemId == null) {
28
- idAttr = rootRef.current.id;
29
- } else {
30
- // Undefined during 1st render
31
- const itemMeta = instance.getItemMeta(itemId);
32
- if (itemMeta !== undefined) {
33
- idAttr = generateTreeItemIdAttribute({
34
- itemId,
35
- treeId,
36
- id: itemMeta.idAttribute
37
- });
38
- }
39
- }
40
- if (idAttr == null) {
41
- return;
42
- }
43
- const previousChildrenIds = instance.getItemOrderedChildrenIds(itemId ?? null) ?? [];
44
- const escapedIdAttr = escapeOperandAttributeSelector(idAttr);
27
+ const previousChildrenIds = selectorItemOrderedChildrenIds(store.value, itemId ?? null) ?? [];
28
+ const escapedIdAttr = escapeOperandAttributeSelector(idAttribute ?? rootRef.current.id);
45
29
  const childrenElements = rootRef.current.querySelectorAll(`${itemId == null ? '' : `*[id="${escapedIdAttr}"] `}[role="treeitem"]:not(*[id="${escapedIdAttr}"] [role="treeitem"] [role="treeitem"])`);
46
30
  const childrenIds = Array.from(childrenElements).map(child => childrenIdAttrToIdRef.current.get(child.id));
47
31
  const hasChanged = childrenIds.length !== previousChildrenIds.length || childrenIds.some((childId, index) => childId !== previousChildrenIds[index]);
@@ -14,8 +14,7 @@ export function TreeViewProvider(props) {
14
14
  return /*#__PURE__*/_jsx(TreeViewContext.Provider, {
15
15
  value: value,
16
16
  children: value.wrapRoot({
17
- children,
18
- instance: value.instance
17
+ children
19
18
  })
20
19
  });
21
20
  }
@@ -3,54 +3,66 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
3
3
  const _excluded = ["ownerState"];
4
4
  import * as React from 'react';
5
5
  import useSlotProps from '@mui/utils/useSlotProps';
6
+ import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowCompare';
6
7
  import { TreeItem } from "../../TreeItem/index.js";
8
+ import { useSelector } from "../hooks/useSelector.js";
9
+ import { selectorItemMeta, selectorItemOrderedChildrenIds } from "../plugins/useTreeViewItems/useTreeViewItems.selectors.js";
10
+ import { useTreeViewContext } from "../TreeViewProvider/index.js";
7
11
  import { jsx as _jsx } from "react/jsx-runtime";
8
- function WrappedTreeItem({
9
- slots,
10
- slotProps,
11
- label,
12
- id,
13
- itemId,
14
- itemsToRender
12
+ const RichTreeViewItemsContext = /*#__PURE__*/React.createContext(null);
13
+ if (process.env.NODE_ENV !== 'production') {
14
+ RichTreeViewItemsContext.displayName = 'RichTreeViewItemsProvider';
15
+ }
16
+ const WrappedTreeItem = /*#__PURE__*/React.memo(function WrappedTreeItem({
17
+ itemSlot,
18
+ itemSlotProps,
19
+ itemId
15
20
  }) {
16
- const Item = slots?.item ?? TreeItem;
21
+ const renderItemForRichTreeView = React.useContext(RichTreeViewItemsContext);
22
+ const {
23
+ store
24
+ } = useTreeViewContext();
25
+ const itemMeta = useSelector(store, selectorItemMeta, itemId);
26
+ const children = useSelector(store, selectorItemOrderedChildrenIds, itemId);
27
+ const Item = itemSlot ?? TreeItem;
17
28
  const _useSlotProps = useSlotProps({
18
29
  elementType: Item,
19
- externalSlotProps: slotProps?.item,
30
+ externalSlotProps: itemSlotProps,
20
31
  additionalProps: {
21
- itemId,
22
- id,
23
- label
32
+ label: itemMeta?.label,
33
+ id: itemMeta?.idAttribute,
34
+ itemId
24
35
  },
25
36
  ownerState: {
26
37
  itemId,
27
- label
38
+ label: itemMeta?.label
28
39
  }
29
40
  }),
30
41
  itemProps = _objectWithoutPropertiesLoose(_useSlotProps, _excluded);
31
- const children = React.useMemo(() => itemsToRender ? /*#__PURE__*/_jsx(RichTreeViewItems, {
32
- itemsToRender: itemsToRender,
33
- slots: slots,
34
- slotProps: slotProps
35
- }) : null, [itemsToRender, slots, slotProps]);
36
42
  return /*#__PURE__*/_jsx(Item, _extends({}, itemProps, {
37
- children: children
43
+ children: children?.map(renderItemForRichTreeView)
38
44
  }));
39
- }
45
+ }, fastObjectShallowCompare);
40
46
  export function RichTreeViewItems(props) {
41
47
  const {
42
- itemsToRender,
43
48
  slots,
44
49
  slotProps
45
50
  } = props;
46
- return /*#__PURE__*/_jsx(React.Fragment, {
47
- children: itemsToRender.map(item => /*#__PURE__*/_jsx(WrappedTreeItem, {
48
- slots: slots,
49
- slotProps: slotProps,
50
- label: item.label,
51
- id: item.id,
52
- itemId: item.itemId,
53
- itemsToRender: item.children
54
- }, item.itemId))
51
+ const {
52
+ store
53
+ } = useTreeViewContext();
54
+ const itemSlot = slots?.item;
55
+ const itemSlotProps = slotProps?.item;
56
+ const items = useSelector(store, selectorItemOrderedChildrenIds, null);
57
+ const renderItem = React.useCallback(itemId => {
58
+ return /*#__PURE__*/_jsx(WrappedTreeItem, {
59
+ itemSlot: itemSlot,
60
+ itemSlotProps: itemSlotProps,
61
+ itemId: itemId
62
+ }, itemId);
63
+ }, [itemSlot, itemSlotProps]);
64
+ return /*#__PURE__*/_jsx(RichTreeViewItemsContext.Provider, {
65
+ value: renderItem,
66
+ children: items.map(renderItem)
55
67
  });
56
68
  }
@@ -1,14 +1,15 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
+ import { useSelector } from "../../hooks/useSelector.js";
4
+ import { selectorTreeViewId } from "./useTreeViewId.selectors.js";
3
5
  import { createTreeViewDefaultId } from "./useTreeViewId.utils.js";
4
6
  export const useTreeViewId = ({
5
7
  params,
6
- state,
7
- setState
8
+ store
8
9
  }) => {
9
10
  React.useEffect(() => {
10
- setState(prevState => {
11
- if (prevState.id.treeId === params.id && prevState.id.treeId !== undefined) {
11
+ store.update(prevState => {
12
+ if (params.id === prevState.id.providedTreeId && prevState.id.treeId !== undefined) {
12
13
  return prevState;
13
14
  }
14
15
  return _extends({}, prevState, {
@@ -17,15 +18,12 @@ export const useTreeViewId = ({
17
18
  })
18
19
  });
19
20
  });
20
- }, [setState, params.id]);
21
- const treeId = params.id ?? state.id.treeId;
21
+ }, [store, params.id]);
22
+ const treeId = useSelector(store, selectorTreeViewId);
22
23
  return {
23
24
  getRootProps: () => ({
24
25
  id: treeId
25
- }),
26
- contextValue: {
27
- treeId
28
- }
26
+ })
29
27
  };
30
28
  };
31
29
  useTreeViewId.params = {
@@ -35,6 +33,7 @@ useTreeViewId.getInitialState = ({
35
33
  id
36
34
  }) => ({
37
35
  id: {
38
- treeId: id ?? undefined
36
+ treeId: undefined,
37
+ providedTreeId: id
39
38
  }
40
39
  });
@@ -0,0 +1,9 @@
1
+ import { createSelector } from "../../utils/selectors.js";
2
+ const selectorTreeViewIdState = state => state.id;
3
+
4
+ /**
5
+ * Get the id attribute of the tree view.
6
+ * @param {TreeViewState<[UseTreeViewIdSignature]>} state The state of the tree view.
7
+ * @returns {string} The id attribute of the tree view.
8
+ */
9
+ export const selectorTreeViewId = createSelector(selectorTreeViewIdState, idState => idState.treeId);
@@ -0,0 +1,6 @@
1
+ import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
2
+ const defaultCompare = Object.is;
3
+ export const useSelector = (store, selector, args = undefined, equals = defaultCompare) => {
4
+ const selectorWithArgs = state => selector(state, args);
5
+ return useSyncExternalStoreWithSelector(store.subscribe, store.getSnapshot, store.getSnapshot, selectorWithArgs, equals);
6
+ };
@@ -2,6 +2,7 @@ export { useTreeView } from "./useTreeView/index.js";
2
2
  export { TreeViewProvider, useTreeViewContext } from "./TreeViewProvider/index.js";
3
3
  export { RichTreeViewItems } from "./components/RichTreeViewItems.js";
4
4
  export { unstable_resetCleanupTracking } from "./hooks/useInstanceEventHandler.js";
5
+ export { useSelector } from "./hooks/useSelector.js";
5
6
 
6
7
  // Core plugins
7
8
 
@@ -12,6 +13,9 @@ export { useTreeViewFocus } from "./plugins/useTreeViewFocus/index.js";
12
13
  export { useTreeViewKeyboardNavigation } from "./plugins/useTreeViewKeyboardNavigation/index.js";
13
14
  export { useTreeViewIcons } from "./plugins/useTreeViewIcons/index.js";
14
15
  export { useTreeViewItems, buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from "./plugins/useTreeViewItems/index.js";
16
+ export { selectorItemMetaLookup, selectorItemMeta, selectorItemIndex, selectorItemOrderedChildrenIds } from "./plugins/useTreeViewItems/useTreeViewItems.selectors.js";
15
17
  export { useTreeViewLabel } from "./plugins/useTreeViewLabel/index.js";
16
18
  export { useTreeViewJSXItems } from "./plugins/useTreeViewJSXItems/index.js";
17
- export { isTargetInDescendants } from "./utils/tree.js";
19
+ export { createSelector } from "./utils/selectors.js";
20
+ export { isTargetInDescendants } from "./utils/tree.js";
21
+ export { TreeViewStore } from "./utils/TreeViewStore.js";
@@ -1,30 +1,35 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
+ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
5
+ import { selectorIsItemExpandable, selectorIsItemExpanded } from "./useTreeViewExpansion.selectors.js";
6
+ import { createExpandedItemsMap } from "./useTreeViewExpansion.utils.js";
7
+ import { selectorItemMeta, selectorItemOrderedChildrenIds } from "../useTreeViewItems/useTreeViewItems.selectors.js";
4
8
  export const useTreeViewExpansion = ({
5
9
  instance,
10
+ store,
6
11
  params,
7
- models
12
+ models,
13
+ experimentalFeatures
8
14
  }) => {
9
- const expandedItemsMap = React.useMemo(() => {
10
- const temp = new Map();
11
- models.expandedItems.value.forEach(id => {
12
- temp.set(id, true);
13
- });
14
- return temp;
15
- }, [models.expandedItems.value]);
15
+ const isTreeViewEditable = Boolean(params.isItemEditable) && !!experimentalFeatures.labelEditing;
16
+ useEnhancedEffect(() => {
17
+ store.update(prevState => _extends({}, prevState, {
18
+ expansion: {
19
+ expandedItemsMap: createExpandedItemsMap(models.expandedItems.value)
20
+ }
21
+ }));
22
+ }, [store, models.expandedItems.value]);
16
23
  const setExpandedItems = (event, value) => {
17
24
  params.onExpandedItemsChange?.(event, value);
18
25
  models.expandedItems.setControlledValue(value);
19
26
  };
20
- const isItemExpanded = React.useCallback(itemId => expandedItemsMap.has(itemId), [expandedItemsMap]);
21
- const isItemExpandable = React.useCallback(itemId => !!instance.getItemMeta(itemId)?.expandable, [instance]);
22
27
  const toggleItemExpansion = useEventCallback((event, itemId) => {
23
- const isExpandedBefore = instance.isItemExpanded(itemId);
28
+ const isExpandedBefore = selectorIsItemExpanded(store.value, itemId);
24
29
  instance.setItemExpansion(event, itemId, !isExpandedBefore);
25
30
  });
26
31
  const setItemExpansion = useEventCallback((event, itemId, isExpanded) => {
27
- const isExpandedBefore = instance.isItemExpanded(itemId);
32
+ const isExpandedBefore = selectorIsItemExpanded(store.value, itemId);
28
33
  if (isExpandedBefore === isExpanded) {
29
34
  return;
30
35
  }
@@ -40,9 +45,12 @@ export const useTreeViewExpansion = ({
40
45
  setExpandedItems(event, newExpanded);
41
46
  });
42
47
  const expandAllSiblings = (event, itemId) => {
43
- const itemMeta = instance.getItemMeta(itemId);
44
- const siblings = instance.getItemOrderedChildrenIds(itemMeta.parentId);
45
- const diff = siblings.filter(child => instance.isItemExpandable(child) && !instance.isItemExpanded(child));
48
+ const itemMeta = selectorItemMeta(store.value, itemId);
49
+ if (itemMeta == null) {
50
+ return;
51
+ }
52
+ const siblings = selectorItemOrderedChildrenIds(store.value, itemMeta.parentId);
53
+ const diff = siblings.filter(child => selectorIsItemExpandable(store.value, child) && !selectorIsItemExpanded(store.value, child));
46
54
  const newExpanded = models.expandedItems.value.concat(diff);
47
55
  if (diff.length > 0) {
48
56
  if (params.onItemExpansionToggle) {
@@ -57,27 +65,26 @@ export const useTreeViewExpansion = ({
57
65
  if (params.expansionTrigger) {
58
66
  return params.expansionTrigger;
59
67
  }
60
- if (instance.isTreeViewEditable) {
68
+ if (isTreeViewEditable) {
61
69
  return 'iconContainer';
62
70
  }
63
71
  return 'content';
64
- }, [params.expansionTrigger, instance.isTreeViewEditable]);
72
+ }, [params.expansionTrigger, isTreeViewEditable]);
73
+ const pluginContextValue = React.useMemo(() => ({
74
+ expansion: {
75
+ expansionTrigger
76
+ }
77
+ }), [expansionTrigger]);
65
78
  return {
66
79
  publicAPI: {
67
80
  setItemExpansion
68
81
  },
69
82
  instance: {
70
- isItemExpanded,
71
- isItemExpandable,
72
83
  setItemExpansion,
73
84
  toggleItemExpansion,
74
85
  expandAllSiblings
75
86
  },
76
- contextValue: {
77
- expansion: {
78
- expansionTrigger
79
- }
80
- }
87
+ contextValue: pluginContextValue
81
88
  };
82
89
  };
83
90
  useTreeViewExpansion.models = {
@@ -91,6 +98,11 @@ useTreeViewExpansion.getDefaultizedParams = ({
91
98
  }) => _extends({}, params, {
92
99
  defaultExpandedItems: params.defaultExpandedItems ?? DEFAULT_EXPANDED_ITEMS
93
100
  });
101
+ useTreeViewExpansion.getInitialState = params => ({
102
+ expansion: {
103
+ expandedItemsMap: createExpandedItemsMap(params.expandedItems === undefined ? params.defaultExpandedItems : params.expandedItems)
104
+ }
105
+ });
94
106
  useTreeViewExpansion.params = {
95
107
  expandedItems: true,
96
108
  defaultExpandedItems: true,
@@ -0,0 +1,17 @@
1
+ import { createSelector } from "../../utils/selectors.js";
2
+ import { selectorItemMeta } from "../useTreeViewItems/useTreeViewItems.selectors.js";
3
+ const selectorExpansion = state => state.expansion;
4
+
5
+ /**
6
+ * Check if an item is expanded.
7
+ * @param {TreeViewState<[UseTreeViewExpansionSignature]>} state The state of the tree view.
8
+ * @returns {boolean} `true` if the item is expanded, `false` otherwise.
9
+ */
10
+ export const selectorIsItemExpanded = createSelector([selectorExpansion, (_, itemId) => itemId], (expansionState, itemId) => expansionState.expandedItemsMap.has(itemId));
11
+
12
+ /**
13
+ * Check if an item is expandable.
14
+ * @param {TreeViewState<[UseTreeViewItemsSignature]>} state The state of the tree view.
15
+ * @returns {boolean} `true` if the item is expandable, `false` otherwise.
16
+ */
17
+ export const selectorIsItemExpandable = createSelector([selectorItemMeta], itemMeta => itemMeta?.expandable ?? false);
@@ -0,0 +1,7 @@
1
+ export const createExpandedItemsMap = expandedItems => {
2
+ const expandedItemsMap = new Map();
3
+ expandedItems.forEach(id => {
4
+ expandedItemsMap.set(id, true);
5
+ });
6
+ return expandedItemsMap;
7
+ };