@mui/x-tree-view 7.1.0 → 7.2.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 (98) hide show
  1. package/CHANGELOG.md +261 -4
  2. package/README.md +1 -1
  3. package/RichTreeView/RichTreeView.d.ts +2 -2
  4. package/RichTreeView/RichTreeView.js +7 -5
  5. package/RichTreeView/RichTreeView.types.d.ts +3 -3
  6. package/SimpleTreeView/SimpleTreeView.js +4 -2
  7. package/TreeItem/TreeItem.js +1 -1
  8. package/TreeItem2/TreeItem2.d.ts +5 -1
  9. package/TreeItem2/TreeItem2.js +0 -1
  10. package/TreeView/TreeView.js +2 -1
  11. package/index.js +1 -1
  12. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.d.ts +16 -0
  13. package/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +57 -0
  14. package/internals/TreeViewProvider/TreeViewContext.d.ts +2 -0
  15. package/internals/TreeViewProvider/TreeViewProvider.js +2 -3
  16. package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +3 -1
  17. package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
  18. package/internals/index.d.ts +18 -8
  19. package/internals/index.js +11 -0
  20. package/internals/models/plugin.d.ts +14 -6
  21. package/internals/models/treeView.d.ts +1 -2
  22. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
  23. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +12 -2
  24. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
  25. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +12 -4
  26. package/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
  27. package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +1 -1
  28. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
  29. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +30 -21
  30. package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.d.ts +4 -0
  31. package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
  32. package/internals/plugins/useTreeViewJSXItems/index.d.ts +1 -1
  33. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
  34. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +5 -4
  35. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
  36. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
  37. package/internals/useTreeView/useTreeView.js +21 -3
  38. package/internals/useTreeView/useTreeViewModels.js +2 -2
  39. package/internals/utils/tree.d.ts +8 -0
  40. package/internals/utils/tree.js +137 -0
  41. package/modern/RichTreeView/RichTreeView.js +7 -5
  42. package/modern/SimpleTreeView/SimpleTreeView.js +4 -2
  43. package/modern/TreeItem/TreeItem.js +1 -1
  44. package/modern/TreeItem2/TreeItem2.js +0 -1
  45. package/modern/TreeView/TreeView.js +2 -1
  46. package/modern/index.js +1 -1
  47. package/modern/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +57 -0
  48. package/modern/internals/TreeViewProvider/TreeViewProvider.js +2 -3
  49. package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
  50. package/modern/internals/index.js +11 -0
  51. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
  52. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
  53. package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
  54. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
  55. package/modern/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +8 -0
  56. package/modern/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
  57. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
  58. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
  59. package/modern/internals/useTreeView/useTreeView.js +21 -3
  60. package/modern/internals/useTreeView/useTreeViewModels.js +2 -2
  61. package/modern/internals/utils/tree.js +137 -0
  62. package/modern/useTreeItem2/useTreeItem2.js +1 -1
  63. package/node/RichTreeView/RichTreeView.js +7 -5
  64. package/node/SimpleTreeView/SimpleTreeView.js +4 -2
  65. package/node/TreeItem/TreeItem.js +1 -1
  66. package/node/TreeItem2/TreeItem2.js +0 -1
  67. package/node/TreeView/TreeView.js +2 -1
  68. package/node/index.js +1 -1
  69. package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +67 -0
  70. package/node/internals/TreeViewProvider/TreeViewProvider.js +2 -3
  71. package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +7 -8
  72. package/node/internals/index.js +70 -0
  73. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +33 -19
  74. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +25 -26
  75. package/node/internals/plugins/useTreeViewId/useTreeViewId.js +5 -7
  76. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +61 -51
  77. package/node/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +15 -0
  78. package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +67 -42
  79. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +20 -18
  80. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +11 -22
  81. package/node/internals/useTreeView/useTreeView.js +21 -3
  82. package/node/internals/useTreeView/useTreeViewModels.js +2 -2
  83. package/node/internals/utils/tree.js +148 -0
  84. package/node/useTreeItem2/useTreeItem2.js +1 -1
  85. package/package.json +2 -2
  86. package/useTreeItem2/useTreeItem2.js +1 -1
  87. package/internals/TreeViewProvider/DescendantProvider.d.ts +0 -38
  88. package/internals/TreeViewProvider/DescendantProvider.js +0 -176
  89. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +0 -17
  90. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -55
  91. package/internals/useTreeView/useTreeView.utils.d.ts +0 -9
  92. package/internals/useTreeView/useTreeView.utils.js +0 -46
  93. package/modern/internals/TreeViewProvider/DescendantProvider.js +0 -176
  94. package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -55
  95. package/modern/internals/useTreeView/useTreeView.utils.js +0 -46
  96. package/node/internals/TreeViewProvider/DescendantProvider.js +0 -185
  97. package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +0 -62
  98. package/node/internals/useTreeView/useTreeView.utils.js +0 -58
@@ -1,4 +1,4 @@
1
- import { TreeViewNode, DefaultizedProps, TreeViewPluginSignature } from '../../models';
1
+ import { TreeViewItemMeta, DefaultizedProps, TreeViewPluginSignature } from '../../models';
2
2
  import { TreeViewItemId } from '../../../models';
3
3
  interface TreeViewItemProps {
4
4
  label: string;
@@ -6,27 +6,33 @@ interface TreeViewItemProps {
6
6
  id: string | undefined;
7
7
  children?: TreeViewItemProps[];
8
8
  }
9
- export interface UseTreeViewItemsInstance<R extends {}> {
10
- getNode: (itemId: string) => TreeViewNode;
9
+ export interface UseTreeViewItemsPublicAPI<R extends {}> {
10
+ /**
11
+ * Get the item with the given id.
12
+ * @param {string} itemId The id of the item to return.
13
+ * @returns {R} The item with the given id.
14
+ */
11
15
  getItem: (itemId: string) => R;
16
+ }
17
+ export interface UseTreeViewItemsInstance<R extends {}> extends UseTreeViewItemsPublicAPI<R> {
18
+ getItemMeta: (itemId: string) => TreeViewItemMeta;
12
19
  getItemsToRender: () => TreeViewItemProps[];
13
- getChildrenIds: (itemId: string | null) => string[];
14
- getNavigableChildrenIds: (itemId: string | null) => string[];
15
- isItemDisabled: (itemId: string | null) => itemId is string;
20
+ getItemOrderedChildrenIds: (parentId: string | null) => string[];
21
+ isItemDisabled: (itemId: string) => itemId is string;
22
+ isItemNavigable: (itemId: string) => boolean;
23
+ getItemIndex: (itemId: string) => number;
16
24
  /**
17
25
  * Freeze any future update to the state based on the `items` prop.
18
- * This is useful when `useTreeViewJSXNodes` is used to avoid having conflicting sources of truth.
26
+ * This is useful when `useTreeViewJSXItems` is used to avoid having conflicting sources of truth.
19
27
  */
20
28
  preventItemUpdates: () => void;
21
29
  /**
22
30
  * Check if the updates to the state based on the `items` prop are prevented.
23
- * This is useful when `useTreeViewJSXNodes` is used to avoid having conflicting sources of truth.
31
+ * This is useful when `useTreeViewJSXItems` is used to avoid having conflicting sources of truth.
24
32
  * @returns {boolean} `true` if the updates to the state based on the `items` prop are prevented.
25
33
  */
26
34
  areItemUpdatesPrevented: () => boolean;
27
35
  }
28
- export interface UseTreeViewItemsPublicAPI<R extends {}> extends Pick<UseTreeViewItemsInstance<R>, 'getItem'> {
29
- }
30
36
  export interface UseTreeViewItemsParameters<R extends {}> {
31
37
  /**
32
38
  * If `true`, will allow focus on disabled items.
@@ -47,16 +53,16 @@ export interface UseTreeViewItemsParameters<R extends {}> {
47
53
  * @template R
48
54
  * @param {R} item The item to check.
49
55
  * @returns {string} The label of the item.
50
- * @default `(item) => item.label`
56
+ * @default (item) => item.label
51
57
  */
52
58
  getItemLabel?: (item: R) => string;
53
59
  /**
54
- * Used to determine the string label for a given item.
60
+ * Used to determine the id of a given item.
55
61
  *
56
62
  * @template R
57
63
  * @param {R} item The item to check.
58
64
  * @returns {string} The id of the item.
59
- * @default `(item) => item.id`
65
+ * @default (item) => item.id
60
66
  */
61
67
  getItemId?: (item: R) => TreeViewItemId;
62
68
  }
@@ -68,15 +74,18 @@ interface UseTreeViewItemsEventLookup {
68
74
  };
69
75
  };
70
76
  }
71
- export interface TreeViewItemIdAndChildren {
72
- id: TreeViewItemId;
73
- children?: TreeViewItemIdAndChildren[];
74
- }
75
77
  export interface UseTreeViewItemsState<R extends {}> {
76
78
  items: {
77
- nodeTree: TreeViewItemIdAndChildren[];
78
- nodeMap: TreeViewNodeMap;
79
+ itemMetaMap: TreeViewItemMetaMap;
79
80
  itemMap: TreeViewItemMap<R>;
81
+ itemOrderedChildrenIds: {
82
+ [parentItemId: string]: string[];
83
+ };
84
+ itemChildrenIndexes: {
85
+ [parentItemId: string]: {
86
+ [itemId: string]: number;
87
+ };
88
+ };
80
89
  };
81
90
  }
82
91
  interface UseTreeViewItemsContextValue extends Pick<UseTreeViewItemsDefaultizedParameters<any>, 'disabledItemsFocusable'> {
@@ -90,8 +99,8 @@ export type UseTreeViewItemsSignature = TreeViewPluginSignature<{
90
99
  state: UseTreeViewItemsState<any>;
91
100
  contextValue: UseTreeViewItemsContextValue;
92
101
  }>;
93
- export type TreeViewNodeMap = {
94
- [itemId: string]: TreeViewNode;
102
+ export type TreeViewItemMetaMap = {
103
+ [itemId: string]: TreeViewItemMeta;
95
104
  };
96
105
  export type TreeViewItemMap<R extends {}> = {
97
106
  [itemId: string]: R;
@@ -0,0 +1,4 @@
1
+ export declare const TREE_VIEW_ROOT_PARENT_ID = "__TREE_VIEW_ROOT_PARENT_ID__";
2
+ export declare const buildSiblingIndexes: (siblings: string[]) => {
3
+ [itemId: string]: number;
4
+ };
@@ -0,0 +1,8 @@
1
+ export const TREE_VIEW_ROOT_PARENT_ID = '__TREE_VIEW_ROOT_PARENT_ID__';
2
+ export const buildSiblingIndexes = siblings => {
3
+ const siblingsIndexLookup = {};
4
+ siblings.forEach((childId, index) => {
5
+ siblingsIndexLookup[childId] = index;
6
+ });
7
+ return siblingsIndexLookup;
8
+ };
@@ -1,2 +1,2 @@
1
1
  export { useTreeViewJSXItems } from './useTreeViewJSXItems';
2
- export type { UseTreeViewJSXItemsSignature, UseTreeViewItemsParameters, UseTreeViewItemsDefaultizedParameters, } from './useTreeViewJSXItems.types';
2
+ export type { UseTreeViewJSXItemsSignature, UseTreeViewJSXItemsParameters, UseTreeViewItemsDefaultizedParameters, } from './useTreeViewJSXItems.types';
@@ -2,10 +2,11 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
4
  import useForkRef from '@mui/utils/useForkRef';
5
- import { populateInstance } from '../../useTreeView/useTreeView.utils';
5
+ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
6
6
  import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
7
7
  import { useTreeViewContext } from '../../TreeViewProvider/useTreeViewContext';
8
- import { DescendantProvider, useDescendant } from '../../TreeViewProvider/DescendantProvider';
8
+ import { TreeViewChildrenItemContext, TreeViewChildrenItemProvider } from '../../TreeViewProvider/TreeViewChildrenItemProvider';
9
+ import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from '../useTreeViewItems/useTreeViewItems.utils';
9
10
  import { jsx as _jsx } from "react/jsx-runtime";
10
11
  export const useTreeViewJSXItems = ({
11
12
  instance,
@@ -14,12 +15,12 @@ export const useTreeViewJSXItems = ({
14
15
  instance.preventItemUpdates();
15
16
  const insertJSXItem = useEventCallback(item => {
16
17
  setState(prevState => {
17
- if (prevState.items.nodeMap[item.id] != null) {
18
- 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.', `Tow items were provided with the same id in the \`items\` prop: "${item.id}"`].join('\n'));
18
+ if (prevState.items.itemMetaMap[item.id] != null) {
19
+ 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.', `Two items were provided with the same id in the \`items\` prop: "${item.id}"`].join('\n'));
19
20
  }
20
21
  return _extends({}, prevState, {
21
22
  items: _extends({}, prevState.items, {
22
- nodeMap: _extends({}, prevState.items.nodeMap, {
23
+ itemMetaMap: _extends({}, prevState.items.itemMetaMap, {
23
24
  [item.id]: item
24
25
  }),
25
26
  // For `SimpleTreeView`, we don't have a proper `item` object, so we create a very basic one.
@@ -33,15 +34,28 @@ export const useTreeViewJSXItems = ({
33
34
  });
34
35
  });
35
36
  });
37
+ const setJSXItemsOrderedChildrenIds = (parentId, orderedChildrenIds) => {
38
+ const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
39
+ setState(prevState => _extends({}, prevState, {
40
+ items: _extends({}, prevState.items, {
41
+ itemOrderedChildrenIds: _extends({}, prevState.items.itemOrderedChildrenIds, {
42
+ [parentIdWithDefault]: orderedChildrenIds
43
+ }),
44
+ itemChildrenIndexes: _extends({}, prevState.items.itemChildrenIndexes, {
45
+ [parentIdWithDefault]: buildSiblingIndexes(orderedChildrenIds)
46
+ })
47
+ })
48
+ }));
49
+ };
36
50
  const removeJSXItem = useEventCallback(itemId => {
37
51
  setState(prevState => {
38
- const newNodeMap = _extends({}, prevState.items.nodeMap);
52
+ const newItemMetaMap = _extends({}, prevState.items.itemMetaMap);
39
53
  const newItemMap = _extends({}, prevState.items.itemMap);
40
- delete newNodeMap[itemId];
54
+ delete newItemMetaMap[itemId];
41
55
  delete newItemMap[itemId];
42
56
  return _extends({}, prevState, {
43
57
  items: _extends({}, prevState.items, {
44
- nodeMap: newNodeMap,
58
+ itemMetaMap: newItemMetaMap,
45
59
  itemMap: newItemMap
46
60
  })
47
61
  });
@@ -63,17 +77,23 @@ export const useTreeViewJSXItems = ({
63
77
  });
64
78
  };
65
79
  });
66
- populateInstance(instance, {
67
- insertJSXItem,
68
- removeJSXItem,
69
- mapFirstCharFromJSX
70
- });
80
+ return {
81
+ instance: {
82
+ insertJSXItem,
83
+ removeJSXItem,
84
+ setJSXItemsOrderedChildrenIds,
85
+ mapFirstCharFromJSX
86
+ }
87
+ };
71
88
  };
72
89
  const useTreeViewJSXItemsItemPlugin = ({
73
90
  props,
74
91
  rootRef,
75
92
  contentRef
76
93
  }) => {
94
+ const {
95
+ instance
96
+ } = useTreeViewContext();
77
97
  const {
78
98
  children,
79
99
  disabled = false,
@@ -81,9 +101,15 @@ const useTreeViewJSXItemsItemPlugin = ({
81
101
  itemId,
82
102
  id
83
103
  } = props;
104
+ const parentContext = React.useContext(TreeViewChildrenItemContext);
105
+ if (parentContext == null) {
106
+ throw new Error(['MUI X: Could not find the Tree View Children Item context.', 'It looks like you rendered your component outside of a SimpleTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
107
+ }
84
108
  const {
85
- instance
86
- } = useTreeViewContext();
109
+ registerChild,
110
+ unregisterChild,
111
+ parentId
112
+ } = parentContext;
87
113
  const isExpandable = reactChildren => {
88
114
  if (Array.isArray(reactChildren)) {
89
115
  return reactChildren.length > 0 && reactChildren.some(isExpandable);
@@ -91,33 +117,27 @@ const useTreeViewJSXItemsItemPlugin = ({
91
117
  return Boolean(reactChildren);
92
118
  };
93
119
  const expandable = isExpandable(children);
94
- const [treeItemElement, setTreeItemElement] = React.useState(null);
95
120
  const pluginContentRef = React.useRef(null);
96
- const handleRootRef = useForkRef(setTreeItemElement, rootRef);
97
121
  const handleContentRef = useForkRef(pluginContentRef, contentRef);
98
- const descendant = React.useMemo(() => ({
99
- element: treeItemElement,
100
- id: itemId
101
- }), [itemId, treeItemElement]);
102
- const {
103
- index,
104
- parentId
105
- } = useDescendant(descendant);
122
+
123
+ // Prevent any flashing
124
+ useEnhancedEffect(() => {
125
+ const idAttributeWithDefault = instance.getTreeItemIdAttribute(itemId, id);
126
+ registerChild(idAttributeWithDefault, itemId);
127
+ return () => {
128
+ unregisterChild(idAttributeWithDefault);
129
+ };
130
+ }, [instance, registerChild, unregisterChild, itemId, id]);
106
131
  React.useEffect(() => {
107
- // On the first render a item's index will be -1. We want to wait for the real index.
108
- if (index !== -1) {
109
- instance.insertJSXItem({
110
- id: itemId,
111
- idAttribute: id,
112
- index,
113
- parentId,
114
- expandable,
115
- disabled
116
- });
117
- return () => instance.removeJSXItem(itemId);
118
- }
119
- return undefined;
120
- }, [instance, parentId, index, itemId, expandable, disabled, id]);
132
+ instance.insertJSXItem({
133
+ id: itemId,
134
+ idAttribute: id,
135
+ parentId,
136
+ expandable,
137
+ disabled
138
+ });
139
+ return () => instance.removeJSXItem(itemId);
140
+ }, [instance, parentId, itemId, expandable, disabled, id]);
121
141
  React.useEffect(() => {
122
142
  if (label) {
123
143
  return instance.mapFirstCharFromJSX(itemId, (pluginContentRef.current?.textContent ?? '').substring(0, 1).toLowerCase());
@@ -126,15 +146,20 @@ const useTreeViewJSXItemsItemPlugin = ({
126
146
  }, [instance, itemId, label]);
127
147
  return {
128
148
  contentRef: handleContentRef,
129
- rootRef: handleRootRef
149
+ rootRef
130
150
  };
131
151
  };
132
152
  useTreeViewJSXItems.itemPlugin = useTreeViewJSXItemsItemPlugin;
133
153
  useTreeViewJSXItems.wrapItem = ({
134
154
  children,
135
155
  itemId
136
- }) => /*#__PURE__*/_jsx(DescendantProvider, {
137
- id: itemId,
156
+ }) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
157
+ itemId: itemId,
158
+ children: children
159
+ });
160
+ useTreeViewJSXItems.wrapRoot = ({
161
+ children
162
+ }) => /*#__PURE__*/_jsx(TreeViewChildrenItemProvider, {
138
163
  children: children
139
164
  });
140
165
  useTreeViewJSXItems.params = {};
@@ -1,17 +1,18 @@
1
- import { TreeViewNode, TreeViewPluginSignature } from '../../models';
1
+ import { TreeViewItemMeta, TreeViewPluginSignature } from '../../models';
2
2
  import { UseTreeViewItemsSignature } from '../useTreeViewItems';
3
3
  import { UseTreeViewKeyboardNavigationSignature } from '../useTreeViewKeyboardNavigation';
4
4
  export interface UseTreeViewItemsInstance {
5
- insertJSXItem: (item: TreeViewNode) => void;
5
+ insertJSXItem: (item: TreeViewItemMeta) => void;
6
6
  removeJSXItem: (itemId: string) => void;
7
7
  mapFirstCharFromJSX: (itemId: string, firstChar: string) => () => void;
8
+ setJSXItemsOrderedChildrenIds: (parentId: string | null, orderedChildrenIds: string[]) => void;
8
9
  }
9
- export interface UseTreeViewItemsParameters {
10
+ export interface UseTreeViewJSXItemsParameters {
10
11
  }
11
12
  export interface UseTreeViewItemsDefaultizedParameters {
12
13
  }
13
14
  export type UseTreeViewJSXItemsSignature = TreeViewPluginSignature<{
14
- params: UseTreeViewItemsParameters;
15
+ params: UseTreeViewJSXItemsParameters;
15
16
  defaultizedParams: UseTreeViewItemsDefaultizedParameters;
16
17
  instance: UseTreeViewItemsInstance;
17
18
  dependantPlugins: [UseTreeViewItemsSignature, UseTreeViewKeyboardNavigationSignature];
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { useTheme } from '@mui/material/styles';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
- import { getFirstItem, getLastItem, getNextItem, getPreviousItem, populateInstance } from '../../useTreeView/useTreeView.utils';
4
+ import { getFirstNavigableItem, getLastNavigableItem, getNextNavigableItem, getPreviousNavigableItem } from '../../utils/tree';
5
5
  function isPrintableCharacter(string) {
6
6
  return !!string && string.length === 1 && !!string.match(/\S/);
7
7
  }
@@ -29,12 +29,12 @@ export const useTreeViewKeyboardNavigation = ({
29
29
  return;
30
30
  }
31
31
  const newFirstCharMap = {};
32
- const processItem = node => {
33
- newFirstCharMap[node.id] = node.label.substring(0, 1).toLowerCase();
32
+ const processItem = item => {
33
+ newFirstCharMap[item.id] = item.label.substring(0, 1).toLowerCase();
34
34
  };
35
- Object.values(state.items.nodeMap).forEach(processItem);
35
+ Object.values(state.items.itemMetaMap).forEach(processItem);
36
36
  firstCharMap.current = newFirstCharMap;
37
- }, [state.items.nodeMap, params.getItemId, instance]);
37
+ }, [state.items.itemMetaMap, params.getItemId, instance]);
38
38
  const getFirstMatchingItem = (itemId, firstChar) => {
39
39
  let start;
40
40
  let index;
@@ -43,7 +43,7 @@ export const useTreeViewKeyboardNavigation = ({
43
43
  const firstChars = [];
44
44
  // This really only works since the ids are strings
45
45
  Object.keys(firstCharMap.current).forEach(mapItemId => {
46
- const map = instance.getNode(mapItemId);
46
+ const map = instance.getItemMeta(mapItemId);
47
47
  const visible = map.parentId ? instance.isItemExpanded(map.parentId) : true;
48
48
  const shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isItemDisabled(mapItemId);
49
49
  if (visible && !shouldBeSkipped) {
@@ -128,7 +128,7 @@ export const useTreeViewKeyboardNavigation = ({
128
128
  // Focus the next focusable item
129
129
  case key === 'ArrowDown':
130
130
  {
131
- const nextItem = getNextItem(instance, itemId);
131
+ const nextItem = getNextNavigableItem(instance, itemId);
132
132
  if (nextItem) {
133
133
  event.preventDefault();
134
134
  instance.focusItem(event, nextItem);
@@ -148,7 +148,7 @@ export const useTreeViewKeyboardNavigation = ({
148
148
  // Focuses the previous focusable item
149
149
  case key === 'ArrowUp':
150
150
  {
151
- const previousItem = getPreviousItem(instance, itemId);
151
+ const previousItem = getPreviousNavigableItem(instance, itemId);
152
152
  if (previousItem) {
153
153
  event.preventDefault();
154
154
  instance.focusItem(event, previousItem);
@@ -170,7 +170,7 @@ export const useTreeViewKeyboardNavigation = ({
170
170
  case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
171
171
  {
172
172
  if (instance.isItemExpanded(itemId)) {
173
- const nextItemId = getNextItem(instance, itemId);
173
+ const nextItemId = getNextNavigableItem(instance, itemId);
174
174
  if (nextItemId) {
175
175
  instance.focusItem(event, nextItemId);
176
176
  event.preventDefault();
@@ -190,7 +190,7 @@ export const useTreeViewKeyboardNavigation = ({
190
190
  instance.toggleItemExpansion(event, itemId);
191
191
  event.preventDefault();
192
192
  } else {
193
- const parent = instance.getNode(itemId).parentId;
193
+ const parent = instance.getItemMeta(itemId).parentId;
194
194
  if (parent) {
195
195
  instance.focusItem(event, parent);
196
196
  event.preventDefault();
@@ -202,7 +202,7 @@ export const useTreeViewKeyboardNavigation = ({
202
202
  // Focuses the first item in the tree
203
203
  case key === 'Home':
204
204
  {
205
- instance.focusItem(event, getFirstItem(instance));
205
+ instance.focusItem(event, getFirstNavigableItem(instance));
206
206
 
207
207
  // Multi select behavior when pressing Ctrl + Shift + Home
208
208
  // Selects the focused item and all items up to the first item.
@@ -216,7 +216,7 @@ export const useTreeViewKeyboardNavigation = ({
216
216
  // Focuses the last item in the tree
217
217
  case key === 'End':
218
218
  {
219
- instance.focusItem(event, getLastItem(instance));
219
+ instance.focusItem(event, getLastNavigableItem(instance));
220
220
 
221
221
  // Multi select behavior when pressing Ctrl + Shirt + End
222
222
  // Selects the focused item and all the items down to the last item.
@@ -240,8 +240,8 @@ export const useTreeViewKeyboardNavigation = ({
240
240
  case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
241
241
  {
242
242
  instance.selectRange(event, {
243
- start: getFirstItem(instance),
244
- end: getLastItem(instance)
243
+ start: getFirstNavigableItem(instance),
244
+ end: getLastNavigableItem(instance)
245
245
  });
246
246
  event.preventDefault();
247
247
  break;
@@ -260,9 +260,11 @@ export const useTreeViewKeyboardNavigation = ({
260
260
  }
261
261
  }
262
262
  };
263
- populateInstance(instance, {
264
- updateFirstCharMap,
265
- handleItemKeyDown
266
- });
263
+ return {
264
+ instance: {
265
+ updateFirstCharMap,
266
+ handleItemKeyDown
267
+ }
268
+ };
267
269
  };
268
270
  useTreeViewKeyboardNavigation.params = {};
@@ -1,7 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import * as React from 'react';
3
- import { populateInstance, getNextItem, getFirstItem, getLastItem } from '../../useTreeView/useTreeView.utils';
4
- import { findOrderInTremauxTree } from './useTreeViewSelection.utils';
3
+ import { getFirstNavigableItem, getLastNavigableItem, getNavigableItemsInRange } from '../../utils/tree';
5
4
  export const useTreeViewSelection = ({
6
5
  instance,
7
6
  params,
@@ -58,16 +57,6 @@ export const useTreeViewSelection = ({
58
57
  lastSelectionWasRange.current = false;
59
58
  currentRangeSelection.current = [];
60
59
  };
61
- const getItemsInRange = (itemAId, itemBId) => {
62
- const [first, last] = findOrderInTremauxTree(instance, itemAId, itemBId);
63
- const items = [first];
64
- let current = first;
65
- while (current !== last) {
66
- current = getNextItem(instance, current);
67
- items.push(current);
68
- }
69
- return items;
70
- };
71
60
  const handleRangeArrowSelect = (event, items) => {
72
61
  let base = models.selectedItems.value.slice();
73
62
  const {
@@ -105,7 +94,7 @@ export const useTreeViewSelection = ({
105
94
  if (lastSelectionWasRange.current) {
106
95
  base = base.filter(id => currentRangeSelection.current.indexOf(id) === -1);
107
96
  }
108
- let range = getItemsInRange(start, end);
97
+ let range = getNavigableItemsInRange(instance, start, end);
109
98
  range = range.filter(item => !instance.isItemDisabled(item));
110
99
  currentRangeSelection.current = range;
111
100
  let newSelected = base.concat(range);
@@ -142,7 +131,7 @@ export const useTreeViewSelection = ({
142
131
  const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
143
132
  instance.selectRange(event, {
144
133
  start,
145
- end: getFirstItem(instance)
134
+ end: getFirstNavigableItem(instance)
146
135
  });
147
136
  };
148
137
  const rangeSelectToLast = (event, itemId) => {
@@ -152,20 +141,20 @@ export const useTreeViewSelection = ({
152
141
  const start = lastSelectionWasRange.current ? lastSelectedItem.current : itemId;
153
142
  instance.selectRange(event, {
154
143
  start,
155
- end: getLastItem(instance)
144
+ end: getLastNavigableItem(instance)
156
145
  });
157
146
  };
158
- populateInstance(instance, {
159
- isItemSelected,
160
- selectItem,
161
- selectRange,
162
- rangeSelectToLast,
163
- rangeSelectToFirst
164
- });
165
147
  return {
166
148
  getRootProps: () => ({
167
149
  'aria-multiselectable': params.multiSelect
168
150
  }),
151
+ instance: {
152
+ isItemSelected,
153
+ selectItem,
154
+ selectRange,
155
+ rangeSelectToLast,
156
+ rangeSelectToFirst
157
+ },
169
158
  contextValue: {
170
159
  selection: {
171
160
  multiSelect: params.multiSelect
@@ -39,12 +39,12 @@ export const useTreeView = inParams => {
39
39
  const rootPropsGetters = [];
40
40
  const contextValue = {
41
41
  publicAPI,
42
- instance: instance
42
+ instance: instance,
43
+ rootRef: innerRootRef
43
44
  };
44
45
  const runPlugin = plugin => {
45
46
  const pluginResponse = plugin({
46
47
  instance,
47
- publicAPI,
48
48
  params,
49
49
  slots: params.slots,
50
50
  slotProps: params.slotProps,
@@ -52,10 +52,16 @@ export const useTreeView = inParams => {
52
52
  setState,
53
53
  rootRef: innerRootRef,
54
54
  models
55
- }) || {};
55
+ });
56
56
  if (pluginResponse.getRootProps) {
57
57
  rootPropsGetters.push(pluginResponse.getRootProps);
58
58
  }
59
+ if (pluginResponse.publicAPI) {
60
+ Object.assign(publicAPI, pluginResponse.publicAPI);
61
+ }
62
+ if (pluginResponse.instance) {
63
+ Object.assign(instance, pluginResponse.instance);
64
+ }
59
65
  if (pluginResponse.contextValue) {
60
66
  Object.assign(contextValue, pluginResponse.contextValue);
61
67
  }
@@ -99,6 +105,18 @@ export const useTreeView = inParams => {
99
105
  });
100
106
  return finalChildren;
101
107
  };
108
+ const rootWrappers = plugins.map(plugin => plugin.wrapRoot).filter(wrapRoot => !!wrapRoot);
109
+ contextValue.wrapRoot = ({
110
+ children
111
+ }) => {
112
+ let finalChildren = children;
113
+ rootWrappers.forEach(rootWrapper => {
114
+ finalChildren = rootWrapper({
115
+ children: finalChildren
116
+ });
117
+ });
118
+ return finalChildren;
119
+ };
102
120
  const getRootProps = (otherHandlers = {}) => {
103
121
  const rootProps = _extends({
104
122
  role: 'tree'
@@ -22,7 +22,7 @@ export const useTreeViewModels = (plugins, props) => {
22
22
  return initialState;
23
23
  });
24
24
  const models = Object.fromEntries(Object.entries(modelsRef.current).map(([modelName, model]) => {
25
- const value = model.isControlled ? props[modelName] : modelsState[modelName];
25
+ const value = props[modelName] ?? modelsState[modelName];
26
26
  return [modelName, {
27
27
  value,
28
28
  setControlledValue: newValue => {
@@ -53,7 +53,7 @@ export const useTreeViewModels = (plugins, props) => {
53
53
  if (!model.isControlled && defaultValue !== newDefaultValue) {
54
54
  console.error([`MUI X: A component is changing the default ${modelName} state of an uncontrolled TreeView after being initialized. ` + `To suppress this warning opt to use a controlled TreeView.`].join('\n'));
55
55
  }
56
- }, [JSON.stringify(defaultValue)]);
56
+ }, [JSON.stringify(newDefaultValue)]);
57
57
  });
58
58
  }
59
59
  /* eslint-enable react-hooks/rules-of-hooks, react-hooks/exhaustive-deps */
@@ -0,0 +1,8 @@
1
+ import { TreeViewInstance } from '../models';
2
+ import type { UseTreeViewExpansionSignature } from '../plugins/useTreeViewExpansion';
3
+ import type { UseTreeViewItemsSignature } from '../plugins/useTreeViewItems';
4
+ export declare const getPreviousNavigableItem: (instance: TreeViewInstance<[UseTreeViewItemsSignature, UseTreeViewExpansionSignature]>, itemId: string) => string | null;
5
+ export declare const getNextNavigableItem: (instance: TreeViewInstance<[UseTreeViewExpansionSignature, UseTreeViewItemsSignature]>, itemId: string) => string | null;
6
+ export declare const getLastNavigableItem: (instance: TreeViewInstance<[UseTreeViewExpansionSignature, UseTreeViewItemsSignature]>) => string;
7
+ export declare const getFirstNavigableItem: (instance: TreeViewInstance<[UseTreeViewItemsSignature]>) => string;
8
+ export declare const getNavigableItemsInRange: (instance: TreeViewInstance<[UseTreeViewItemsSignature, UseTreeViewExpansionSignature]>, itemAId: string, itemBId: string) => string[];