@mui/x-tree-view 8.11.3 → 8.13.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 (76) hide show
  1. package/CHANGELOG.md +296 -6
  2. package/RichTreeView/RichTreeView.js +5 -5
  3. package/RichTreeView/RichTreeView.types.d.ts +2 -3
  4. package/RichTreeView/index.d.ts +1 -1
  5. package/SimpleTreeView/SimpleTreeView.types.d.ts +2 -3
  6. package/SimpleTreeView/index.d.ts +1 -1
  7. package/TreeItemIcon/TreeItemIcon.js +12 -3
  8. package/TreeItemIcon/TreeItemIcon.types.d.ts +4 -4
  9. package/esm/RichTreeView/RichTreeView.js +5 -5
  10. package/esm/RichTreeView/RichTreeView.types.d.ts +2 -3
  11. package/esm/RichTreeView/index.d.ts +1 -1
  12. package/esm/SimpleTreeView/SimpleTreeView.types.d.ts +2 -3
  13. package/esm/SimpleTreeView/index.d.ts +1 -1
  14. package/esm/TreeItemIcon/TreeItemIcon.js +12 -3
  15. package/esm/TreeItemIcon/TreeItemIcon.types.d.ts +4 -4
  16. package/esm/hooks/useApplyPropagationToSelectedItemsOnMount.js +2 -2
  17. package/esm/index.js +1 -1
  18. package/esm/internals/TreeViewProvider/TreeViewStyleContext.d.ts +3 -3
  19. package/esm/internals/index.d.ts +2 -2
  20. package/esm/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +5 -5
  21. package/esm/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +1 -1
  22. package/esm/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +2 -2
  23. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.js +79 -158
  24. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +0 -8
  25. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +0 -8
  26. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +12 -26
  27. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.utils.d.ts +36 -2
  28. package/esm/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +115 -1
  29. package/esm/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +6 -6
  30. package/esm/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +1 -1
  31. package/esm/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +2 -2
  32. package/esm/internals/plugins/useTreeViewLazyLoading/index.d.ts +1 -1
  33. package/esm/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.selectors.d.ts +6 -2
  34. package/esm/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.selectors.js +7 -2
  35. package/esm/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.types.d.ts +8 -9
  36. package/esm/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +12 -3
  37. package/esm/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +2 -2
  38. package/esm/internals/useTreeView/useTreeView.js +2 -2
  39. package/esm/useTreeItem/useTreeItem.js +3 -3
  40. package/hooks/useApplyPropagationToSelectedItemsOnMount.js +2 -3
  41. package/index.js +1 -1
  42. package/internals/TreeViewProvider/TreeViewStyleContext.d.ts +3 -3
  43. package/internals/index.d.ts +2 -2
  44. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +9 -9
  45. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +4 -4
  46. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +2 -2
  47. package/internals/plugins/useTreeViewItems/useTreeViewItems.js +80 -159
  48. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.d.ts +0 -8
  49. package/internals/plugins/useTreeViewItems/useTreeViewItems.selectors.js +0 -8
  50. package/internals/plugins/useTreeViewItems/useTreeViewItems.types.d.ts +12 -26
  51. package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.d.ts +36 -2
  52. package/internals/plugins/useTreeViewItems/useTreeViewItems.utils.js +119 -2
  53. package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +8 -8
  54. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +2 -3
  55. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +2 -2
  56. package/internals/plugins/useTreeViewLazyLoading/index.d.ts +1 -1
  57. package/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.selectors.d.ts +6 -2
  58. package/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.selectors.js +7 -2
  59. package/internals/plugins/useTreeViewLazyLoading/useTreeViewLazyLoading.types.d.ts +8 -9
  60. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.itemPlugin.js +12 -3
  61. package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +2 -2
  62. package/internals/useTreeView/useTreeView.js +2 -2
  63. package/package.json +3 -3
  64. package/useTreeItem/useTreeItem.js +3 -3
  65. package/esm/internals/hooks/useLazyRef.d.ts +0 -1
  66. package/esm/internals/hooks/useLazyRef.js +0 -1
  67. package/esm/internals/hooks/useOnMount.d.ts +0 -1
  68. package/esm/internals/hooks/useOnMount.js +0 -1
  69. package/esm/internals/hooks/useTimeout.d.ts +0 -1
  70. package/esm/internals/hooks/useTimeout.js +0 -1
  71. package/internals/hooks/useLazyRef.d.ts +0 -1
  72. package/internals/hooks/useLazyRef.js +0 -13
  73. package/internals/hooks/useOnMount.d.ts +0 -1
  74. package/internals/hooks/useOnMount.js +0 -13
  75. package/internals/hooks/useTimeout.d.ts +0 -1
  76. package/internals/hooks/useTimeout.js +0 -13
@@ -28,16 +28,16 @@ export interface TreeViewSlots {
28
28
  /**
29
29
  * The default icon used to collapse the item.
30
30
  */
31
- collapseIcon?: React.ElementType;
31
+ collapseIcon?: React.ElementType | null;
32
32
  /**
33
33
  * The default icon used to expand the item.
34
34
  */
35
- expandIcon?: React.ElementType;
35
+ expandIcon?: React.ElementType | null;
36
36
  /**
37
37
  * The default icon displayed next to an end item.
38
38
  * This is applied to all Tree Items and can be overridden by the TreeItem `icon` slot prop.
39
39
  */
40
- endIcon?: React.ElementType;
40
+ endIcon?: React.ElementType | null;
41
41
  }
42
42
  export interface TreeViewSlotProps {
43
43
  collapseIcon?: SlotComponentProps<'svg', {}, {}>;
@@ -3,7 +3,7 @@ export { TreeViewProvider, useTreeViewContext } from "./TreeViewProvider/index.j
3
3
  export { RichTreeViewItems } from "./components/RichTreeViewItems.js";
4
4
  export type { RichTreeViewItemsSlots, RichTreeViewItemsSlotProps } from "./components/RichTreeViewItems.js";
5
5
  export { unstable_resetCleanupTracking, useInstanceEventHandler } from "./hooks/useInstanceEventHandler.js";
6
- export type { TreeViewPlugin, TreeViewPluginSignature, ConvertPluginsIntoSignatures, MergeSignaturesProperty, TreeViewPublicAPI, TreeViewState, TreeViewItemMeta, TreeViewInstance, TreeViewItemPlugin, TreeViewUsedStore } from "./models/index.js";
6
+ export type { TreeViewPlugin, TreeViewPluginSignature, ConvertPluginsIntoSignatures, MergeSignaturesProperty, TreeViewPublicAPI, TreeViewState, TreeViewItemMeta, TreeViewInstance, TreeViewItemPlugin, TreeViewUsedStore, TreeViewUsedInstance, TreeViewUsedParamsWithDefaults } from "./models/index.js";
7
7
  export type { TreeViewCorePluginParameters } from "./corePlugins/index.js";
8
8
  export { useTreeViewExpansion, expansionSelectors } from "./plugins/useTreeViewExpansion/index.js";
9
9
  export type { UseTreeViewExpansionSignature, UseTreeViewExpansionParameters } from "./plugins/useTreeViewExpansion/index.js";
@@ -17,7 +17,7 @@ export { useTreeViewItems, buildSiblingIndexes, itemsSelectors, TREE_VIEW_ROOT_P
17
17
  export type { UseTreeViewItemsSignature, UseTreeViewItemsParameters, UseTreeViewItemsState } from "./plugins/useTreeViewItems/index.js";
18
18
  export { useTreeViewLabel, labelSelectors } from "./plugins/useTreeViewLabel/index.js";
19
19
  export type { UseTreeViewLabelSignature, UseTreeViewLabelParameters } from "./plugins/useTreeViewLabel/index.js";
20
- export type { UseTreeViewLazyLoadingSignature, UseTreeViewLazyLoadingParameters, UseTreeViewLazyLoadingInstance } from "./plugins/useTreeViewLazyLoading/index.js";
20
+ export type { UseTreeViewLazyLoadingSignature, UseTreeViewLazyLoadingParameters, UseTreeViewLazyLoadingInstance, DataSource } from "./plugins/useTreeViewLazyLoading/index.js";
21
21
  export { lazyLoadingSelectors } from "./plugins/useTreeViewLazyLoading/index.js";
22
22
  export { useTreeViewJSXItems } from "./plugins/useTreeViewJSXItems/index.js";
23
23
  export type { UseTreeViewJSXItemsSignature, UseTreeViewJSXItemsParameters } from "./plugins/useTreeViewJSXItems/index.js";
@@ -1,7 +1,7 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { useAssertModelConsistency } from '@mui/x-internals/useAssertModelConsistency';
3
- import useEventCallback from '@mui/utils/useEventCallback';
4
- import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
3
+ import { useEventCallback } from '@base-ui-components/utils/useEventCallback';
4
+ import { useIsoLayoutEffect } from '@base-ui-components/utils/useIsoLayoutEffect';
5
5
  import { expansionSelectors } from "./useTreeViewExpansion.selectors.js";
6
6
  import { getExpansionTrigger } from "./useTreeViewExpansion.utils.js";
7
7
  import { itemsSelectors } from "../useTreeViewItems/useTreeViewItems.selectors.js";
@@ -17,7 +17,7 @@ export const useTreeViewExpansion = ({
17
17
  controlled: params.expandedItems,
18
18
  defaultValue: params.defaultExpandedItems
19
19
  });
20
- useEnhancedEffect(() => {
20
+ useIsoLayoutEffect(() => {
21
21
  const newExpansionTrigger = getExpansionTrigger({
22
22
  isItemEditable: params.isItemEditable,
23
23
  expansionTrigger: params.expansionTrigger
@@ -84,7 +84,7 @@ export const useTreeViewExpansion = ({
84
84
  });
85
85
  });
86
86
  const isItemExpanded = useEventCallback(itemId => {
87
- return expansionSelectors.isItemExpandable(store.state, itemId);
87
+ return expansionSelectors.isItemExpanded(store.state, itemId);
88
88
  });
89
89
  const expandAllSiblings = (event, itemId) => {
90
90
  const itemMeta = itemsSelectors.itemMeta(store.state, itemId);
@@ -107,7 +107,7 @@ export const useTreeViewExpansion = ({
107
107
  /**
108
108
  * Update the controlled model when the `expandedItems` prop changes.
109
109
  */
110
- useEnhancedEffect(() => {
110
+ useIsoLayoutEffect(() => {
111
111
  const expandedItems = params.expandedItems;
112
112
  if (expandedItems !== undefined) {
113
113
  store.set('expansion', _extends({}, store.state.expansion, {
@@ -1,5 +1,5 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
- import useEventCallback from '@mui/utils/useEventCallback';
2
+ import { useEventCallback } from '@base-ui-components/utils/useEventCallback';
3
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";
@@ -9,10 +9,10 @@ export interface UseTreeViewFocusPublicAPI {
9
9
  *
10
10
  * If the item is the child of a collapsed item, then this method will do nothing.
11
11
  * Make sure to expand the ancestors of the item before calling this method if needed.
12
- * @param {React.SyntheticEvent} event The DOM event that triggered the change.
12
+ * @param {React.SyntheticEvent | null} event The DOM event that triggered the change.
13
13
  * @param {TreeViewItemId} itemId The id of the item to focus.
14
14
  */
15
- focusItem: (event: React.SyntheticEvent, itemId: string) => void;
15
+ focusItem: (event: React.SyntheticEvent | null, itemId: string) => void;
16
16
  }
17
17
  export interface UseTreeViewFocusInstance extends UseTreeViewFocusPublicAPI {
18
18
  /**
@@ -2,100 +2,29 @@
2
2
 
3
3
  import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
- import useEventCallback from '@mui/utils/useEventCallback';
6
- import { buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from "./useTreeViewItems.utils.js";
5
+ import { useEventCallback } from '@base-ui-components/utils/useEventCallback';
6
+ import { buildItemsLookups, buildItemsState, TREE_VIEW_ROOT_PARENT_ID } from "./useTreeViewItems.utils.js";
7
7
  import { TreeViewItemDepthContext } from "../../TreeViewItemDepthContext/index.js";
8
8
  import { itemsSelectors } from "./useTreeViewItems.selectors.js";
9
9
  import { idSelectors } from "../../corePlugins/useTreeViewId/index.js";
10
10
  import { generateTreeItemIdAttribute } from "../../corePlugins/useTreeViewId/useTreeViewId.utils.js";
11
11
  import { jsx as _jsx } from "react/jsx-runtime";
12
- const checkId = (id, item, itemMetaLookup) => {
13
- if (id == null) {
14
- throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
15
- }
16
- if (itemMetaLookup[id] != null) {
17
- throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', `Two items were provided with the same id in the \`items\` prop: "${id}"`].join('\n'));
18
- }
19
- };
20
- const processItemsLookups = ({
21
- disabledItemsFocusable,
22
- items,
23
- isItemDisabled,
24
- getItemLabel,
25
- getItemChildren,
26
- getItemId,
27
- initialDepth = 0,
28
- initialParentId = null,
29
- getChildrenCount,
30
- ignoreChildren = false
31
- }) => {
32
- const itemMetaLookup = {};
33
- const itemModelLookup = {};
34
- const itemOrderedChildrenIdsLookup = {
35
- [TREE_VIEW_ROOT_PARENT_ID]: []
36
- };
37
- const processItem = (item, depth, parentId) => {
38
- const id = getItemId ? getItemId(item) : item.id;
39
- checkId(id, item, itemMetaLookup);
40
- const label = getItemLabel ? getItemLabel(item) : item.label;
41
- if (label == null) {
42
- throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
43
- }
44
- const children = getItemChildren ? getItemChildren(item) : item.children;
45
- itemMetaLookup[id] = {
46
- id,
47
- label,
48
- parentId,
49
- idAttribute: undefined,
50
- expandable: getChildrenCount ? getChildrenCount(item) > 0 : !!children?.length,
51
- disabled: isItemDisabled ? isItemDisabled(item) : false,
52
- depth
53
- };
54
- itemModelLookup[id] = item;
55
- const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
56
- if (!itemOrderedChildrenIdsLookup[parentIdWithDefault]) {
57
- itemOrderedChildrenIdsLookup[parentIdWithDefault] = [];
58
- }
59
- itemOrderedChildrenIdsLookup[parentIdWithDefault].push(id);
60
-
61
- // if lazy loading is enabled, we don't want to process children passed through the `items` prop
62
- if (!ignoreChildren) {
63
- children?.forEach(child => processItem(child, depth + 1, id));
64
- }
65
- };
66
- items?.forEach(item => processItem(item, initialDepth, initialParentId));
67
- const itemChildrenIndexesLookup = {};
68
- Object.keys(itemOrderedChildrenIdsLookup).forEach(parentId => {
69
- itemChildrenIndexesLookup[parentId] = buildSiblingIndexes(itemOrderedChildrenIdsLookup[parentId]);
70
- });
71
- return {
72
- disabledItemsFocusable,
73
- itemMetaLookup,
74
- itemModelLookup,
75
- itemOrderedChildrenIdsLookup,
76
- itemChildrenIndexesLookup
77
- };
78
- };
79
12
  export const useTreeViewItems = ({
80
13
  instance,
81
14
  params,
82
15
  store
83
16
  }) => {
17
+ const itemsConfig = React.useMemo(() => ({
18
+ isItemDisabled: params.isItemDisabled,
19
+ getItemLabel: params.getItemLabel,
20
+ getItemChildren: params.getItemChildren,
21
+ getItemId: params.getItemId
22
+ }), [params.isItemDisabled, params.getItemLabel, params.getItemChildren, params.getItemId]);
84
23
  const getItem = React.useCallback(itemId => itemsSelectors.itemModel(store.state, itemId), [store]);
85
24
  const getParentId = React.useCallback(itemId => {
86
25
  const itemMeta = itemsSelectors.itemMeta(store.state, itemId);
87
26
  return itemMeta?.parentId || null;
88
27
  }, [store]);
89
- const setTreeViewLoading = useEventCallback(isLoading => {
90
- store.set('items', _extends({}, store.state.items, {
91
- loading: isLoading
92
- }));
93
- });
94
- const setTreeViewError = useEventCallback(error => {
95
- store.set('items', _extends({}, store.state.items, {
96
- error
97
- }));
98
- });
99
28
  const setIsItemDisabled = useEventCallback(({
100
29
  itemId,
101
30
  shouldBeDisabled
@@ -143,86 +72,81 @@ export const useTreeViewItems = ({
143
72
  areItemUpdatesPreventedRef.current = true;
144
73
  }, []);
145
74
  const areItemUpdatesPrevented = React.useCallback(() => areItemUpdatesPreventedRef.current, []);
146
- const addItems = ({
75
+ const setItemChildren = ({
147
76
  items,
148
77
  parentId,
149
- depth,
150
78
  getChildrenCount
151
79
  }) => {
152
- if (items) {
153
- const newState = processItemsLookups({
154
- disabledItemsFocusable: params.disabledItemsFocusable,
155
- items,
156
- isItemDisabled: params.isItemDisabled,
157
- getItemId: params.getItemId,
158
- getItemLabel: params.getItemLabel,
159
- getItemChildren: params.getItemChildren,
160
- getChildrenCount,
161
- initialDepth: depth,
162
- initialParentId: parentId,
163
- ignoreChildren: true
164
- });
165
- let newItems;
166
- if (parentId) {
167
- newItems = {
168
- itemModelLookup: _extends({}, store.state.items.itemModelLookup, newState.itemModelLookup),
169
- itemMetaLookup: _extends({}, store.state.items.itemMetaLookup, newState.itemMetaLookup),
170
- itemOrderedChildrenIdsLookup: _extends({}, newState.itemOrderedChildrenIdsLookup, store.state.items.itemOrderedChildrenIdsLookup),
171
- itemChildrenIndexesLookup: _extends({}, newState.itemChildrenIndexesLookup, store.state.items.itemChildrenIndexesLookup)
172
- };
173
- } else {
174
- newItems = {
175
- itemModelLookup: newState.itemModelLookup,
176
- itemMetaLookup: newState.itemMetaLookup,
177
- itemOrderedChildrenIdsLookup: newState.itemOrderedChildrenIdsLookup,
178
- itemChildrenIndexesLookup: newState.itemChildrenIndexesLookup
179
- };
180
- }
181
- store.set('items', _extends({}, store.state.items, newItems));
182
- }
80
+ const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
81
+ const parentDepth = parentId == null ? -1 : itemsSelectors.itemDepth(store.state, parentId);
82
+ const {
83
+ metaLookup,
84
+ modelLookup,
85
+ orderedChildrenIds,
86
+ childrenIndexes
87
+ } = buildItemsLookups({
88
+ config: itemsConfig,
89
+ items,
90
+ parentId,
91
+ depth: parentDepth + 1,
92
+ isItemExpandable: getChildrenCount ? item => getChildrenCount(item) > 0 : () => false,
93
+ otherItemsMetaLookup: itemsSelectors.itemMetaLookup(store.state)
94
+ });
95
+ const lookups = {
96
+ itemModelLookup: _extends({}, store.state.items.itemModelLookup, modelLookup),
97
+ itemMetaLookup: _extends({}, store.state.items.itemMetaLookup, metaLookup),
98
+ itemOrderedChildrenIdsLookup: _extends({}, store.state.items.itemOrderedChildrenIdsLookup, {
99
+ [parentIdWithDefault]: orderedChildrenIds
100
+ }),
101
+ itemChildrenIndexesLookup: _extends({}, store.state.items.itemChildrenIndexesLookup, {
102
+ [parentIdWithDefault]: childrenIndexes
103
+ })
104
+ };
105
+ store.set('items', _extends({}, store.state.items, lookups));
183
106
  };
184
- const removeChildren = parentId => {
185
- if (parentId == null) {
186
- store.set('items', _extends({}, store.state.items, {
187
- itemMetaLookup: {},
188
- itemOrderedChildrenIdsLookup: {},
189
- itemChildrenIndexesLookup: {}
190
- }));
191
- } else {
192
- const newMetaMap = Object.keys(store.state.items.itemMetaLookup).reduce((acc, key) => {
193
- const item = store.state.items.itemMetaLookup[key];
194
- if (item.parentId === parentId) {
195
- return acc;
196
- }
197
- return _extends({}, acc, {
198
- [item.id]: item
199
- });
200
- }, {});
201
- const newItemOrderedChildrenIdsLookup = _extends({}, store.state.items.itemOrderedChildrenIdsLookup);
202
- const newItemChildrenIndexesLookup = _extends({}, store.state.items.itemChildrenIndexesLookup);
203
- delete newItemChildrenIndexesLookup[parentId];
204
- delete newItemOrderedChildrenIdsLookup[parentId];
205
- store.set('items', _extends({}, store.state.items, {
206
- itemMetaLookup: newMetaMap,
207
- itemOrderedChildrenIdsLookup: newItemOrderedChildrenIdsLookup,
208
- itemChildrenIndexesLookup: newItemChildrenIndexesLookup
209
- }));
107
+ const removeChildren = useEventCallback(parentId => {
108
+ const newMetaMap = Object.keys(store.state.items.itemMetaLookup).reduce((acc, key) => {
109
+ const item = store.state.items.itemMetaLookup[key];
110
+ if (item.parentId === parentId) {
111
+ return acc;
112
+ }
113
+ return _extends({}, acc, {
114
+ [item.id]: item
115
+ });
116
+ }, {});
117
+ const newItemOrderedChildrenIdsLookup = _extends({}, store.state.items.itemOrderedChildrenIdsLookup);
118
+ const newItemChildrenIndexesLookup = _extends({}, store.state.items.itemChildrenIndexesLookup);
119
+ const cleanId = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
120
+ delete newItemChildrenIndexesLookup[cleanId];
121
+ delete newItemOrderedChildrenIdsLookup[cleanId];
122
+ store.set('items', _extends({}, store.state.items, {
123
+ itemMetaLookup: newMetaMap,
124
+ itemOrderedChildrenIdsLookup: newItemOrderedChildrenIdsLookup,
125
+ itemChildrenIndexesLookup: newItemChildrenIndexesLookup
126
+ }));
127
+ });
128
+ const addExpandableItems = useEventCallback(items => {
129
+ const newItemMetaLookup = _extends({}, store.state.items.itemMetaLookup);
130
+ for (const itemId of items) {
131
+ newItemMetaLookup[itemId] = _extends({}, newItemMetaLookup[itemId], {
132
+ expandable: true
133
+ });
210
134
  }
211
- };
135
+ store.set('items', _extends({}, store.state.items, {
136
+ itemMetaLookup: newItemMetaLookup
137
+ }));
138
+ });
212
139
  React.useEffect(() => {
213
140
  if (instance.areItemUpdatesPrevented()) {
214
141
  return;
215
142
  }
216
- const newState = processItemsLookups({
143
+ const newState = buildItemsState({
217
144
  disabledItemsFocusable: params.disabledItemsFocusable,
218
145
  items: params.items,
219
- isItemDisabled: params.isItemDisabled,
220
- getItemId: params.getItemId,
221
- getItemLabel: params.getItemLabel,
222
- getItemChildren: params.getItemChildren
146
+ config: itemsConfig
223
147
  });
224
148
  store.set('items', _extends({}, store.state.items, newState));
225
- }, [instance, store, params.items, params.disabledItemsFocusable, params.isItemDisabled, params.getItemId, params.getItemLabel, params.getItemChildren]);
149
+ }, [instance, store, params.items, params.disabledItemsFocusable, itemsConfig]);
226
150
 
227
151
  // Wrap `props.onItemClick` with `useEventCallback` to prevent unneeded context updates.
228
152
  const handleItemClick = useEventCallback((event, itemId) => {
@@ -248,26 +172,23 @@ export const useTreeViewItems = ({
248
172
  getItemDOMElement,
249
173
  preventItemUpdates,
250
174
  areItemUpdatesPrevented,
251
- addItems,
252
- setTreeViewLoading,
253
- setTreeViewError,
175
+ setItemChildren,
254
176
  removeChildren,
177
+ addExpandableItems,
255
178
  handleItemClick
256
179
  }
257
180
  };
258
181
  };
259
182
  useTreeViewItems.getInitialState = params => ({
260
- items: _extends({}, processItemsLookups({
261
- disabledItemsFocusable: params.disabledItemsFocusable,
183
+ items: buildItemsState({
262
184
  items: params.items,
263
- isItemDisabled: params.isItemDisabled,
264
- getItemId: params.getItemId,
265
- getItemLabel: params.getItemLabel,
266
- getItemChildren: params.getItemChildren
267
- }), {
268
- loading: false,
269
- error: null,
270
- domStructure: 'nested'
185
+ disabledItemsFocusable: params.disabledItemsFocusable,
186
+ config: {
187
+ isItemDisabled: params.isItemDisabled,
188
+ getItemId: params.getItemId,
189
+ getItemLabel: params.getItemLabel,
190
+ getItemChildren: params.getItemChildren
191
+ }
271
192
  })
272
193
  });
273
194
  useTreeViewItems.applyDefaultValuesToParams = ({
@@ -1,14 +1,6 @@
1
1
  import { TreeViewItemMeta, TreeViewState } from "../../models/index.js";
2
2
  import { UseTreeViewItemsSignature } from "./useTreeViewItems.types.js";
3
3
  export declare const itemsSelectors: {
4
- /**
5
- * Gets the loading state for the Tree View.
6
- */
7
- isLoading: (state: TreeViewState<[UseTreeViewItemsSignature]>) => boolean;
8
- /**
9
- * Gets the error state for the Tree View.
10
- */
11
- error: (state: TreeViewState<[UseTreeViewItemsSignature]>) => Error | null;
12
4
  /**
13
5
  * Gets the DOM structure of the Tree View.
14
6
  */
@@ -2,14 +2,6 @@ import { createSelector } from '@mui/x-internals/store';
2
2
  import { isItemDisabled, TREE_VIEW_ROOT_PARENT_ID } from "./useTreeViewItems.utils.js";
3
3
  const EMPTY_CHILDREN = [];
4
4
  export const itemsSelectors = {
5
- /**
6
- * Gets the loading state for the Tree View.
7
- */
8
- isLoading: createSelector(state => state.items.loading),
9
- /**
10
- * Gets the error state for the Tree View.
11
- */
12
- error: createSelector(state => state.items.error),
13
5
  /**
14
6
  * Gets the DOM structure of the Tree View.
15
7
  */
@@ -2,11 +2,10 @@ import * as React from 'react';
2
2
  import { DefaultizedProps } from '@mui/x-internals/types';
3
3
  import { TreeViewItemMeta, TreeViewPluginSignature } from "../../models/index.js";
4
4
  import { TreeViewBaseItem, TreeViewDefaultItemModelProperties, TreeViewItemId } from "../../../models/index.js";
5
- export type AddItemsParameters<R> = {
5
+ export type SetItemChildrenParameters<R> = {
6
6
  items: readonly R[];
7
- parentId?: TreeViewItemId;
8
- depth: number;
9
- getChildrenCount?: (item: R) => number;
7
+ parentId: TreeViewItemId | null;
8
+ getChildrenCount: (item: R) => number;
10
9
  };
11
10
  export interface UseTreeViewItemsPublicAPI<R extends {}> {
12
11
  /**
@@ -65,30 +64,25 @@ export interface UseTreeViewItemsInstance<R extends {}> extends Pick<UseTreeView
65
64
  areItemUpdatesPrevented: () => boolean;
66
65
  /**
67
66
  * Add an array of items to the tree.
68
- * @param {AddItemsParameters<R>} args The items to add to the tree and information about their ancestors.
67
+ * @param {SetItemChildrenParameters<R>} args The items to add to the tree and information about their ancestors.
69
68
  */
70
- addItems: (args: AddItemsParameters<R>) => void;
69
+ setItemChildren: (args: SetItemChildrenParameters<R>) => void;
71
70
  /**
72
71
  * Remove the children of an item.
73
- * @param {TreeViewItemId} parentId The id of the item to remove the children of.
72
+ * @param {TreeViewItemId | null} parentId The id of the item to remove the children of.
74
73
  */
75
- removeChildren: (parentId?: TreeViewItemId) => void;
76
- /**
77
- * Set the loading state of the tree.
78
- * @param {boolean} loading True if the tree view is loading.
79
- */
80
- setTreeViewLoading: (loading: boolean) => void;
81
- /**
82
- * Set the error state of the tree.
83
- * @param {Error | null} error The error on the tree view.
84
- */
85
- setTreeViewError: (error: Error | null) => void;
74
+ removeChildren: (parentId: TreeViewItemId | null) => void;
86
75
  /**
87
76
  * Event handler to fire when the `content` slot of a given Tree Item is clicked.
88
77
  * @param {React.MouseEvent} event The DOM event that triggered the change.
89
78
  * @param {TreeViewItemId} itemId The id of the item being clicked.
90
79
  */
91
80
  handleItemClick: (event: React.MouseEvent, itemId: TreeViewItemId) => void;
81
+ /**
82
+ * Mark a list of items as expandable.
83
+ * @param {TreeViewItemId[]} items The ids of the items to mark as expandable.
84
+ */
85
+ addExpandableItems: (items: TreeViewItemId[]) => void;
92
86
  }
93
87
  export interface UseTreeViewItemsParameters<R extends {
94
88
  children?: R[];
@@ -186,14 +180,6 @@ export interface UseTreeViewItemsState<R extends {}> {
186
180
  [itemId: string]: number;
187
181
  };
188
182
  };
189
- /**
190
- * The loading state of the tree.
191
- */
192
- loading: boolean;
193
- /**
194
- * The error state of the tree.
195
- */
196
- error: Error | null;
197
183
  /**
198
184
  * When equal to 'flat', the tree is rendered as a flat list (children are rendered as siblings of their parents).
199
185
  * When equal to 'nested', the tree is rendered with nested children (children are rendered inside the groupTransition slot of their children).
@@ -1,5 +1,6 @@
1
- import { TreeViewItemId } from "../../../models/index.js";
1
+ import { TreeViewBaseItem, TreeViewItemId } from "../../../models/index.js";
2
2
  import { TreeViewItemMeta } from "../../models/index.js";
3
+ import { UseTreeViewItemsParametersWithDefaults, UseTreeViewItemsState } from "./useTreeViewItems.types.js";
3
4
  export declare const TREE_VIEW_ROOT_PARENT_ID = "__TREE_VIEW_ROOT_PARENT_ID__";
4
5
  export declare const buildSiblingIndexes: (siblings: string[]) => {
5
6
  [itemId: string]: number;
@@ -12,4 +13,37 @@ export declare const buildSiblingIndexes: (siblings: string[]) => {
12
13
  */
13
14
  export declare const isItemDisabled: (itemMetaLookup: {
14
15
  [itemId: string]: TreeViewItemMeta;
15
- }, itemId: TreeViewItemId) => boolean;
16
+ }, itemId: TreeViewItemId) => boolean;
17
+ type State = UseTreeViewItemsState<any>['items'];
18
+ export declare function buildItemsState(parameters: BuildItemsStateParameters): State;
19
+ interface BuildItemsStateParameters extends Pick<BuildItemsLookupsParameters, 'items' | 'config'> {
20
+ disabledItemsFocusable: boolean;
21
+ }
22
+ export declare function buildItemsLookups(parameters: BuildItemsLookupsParameters): {
23
+ metaLookup: {
24
+ [itemId: string]: TreeViewItemMeta;
25
+ };
26
+ modelLookup: {
27
+ [itemId: string]: any;
28
+ };
29
+ orderedChildrenIds: string[];
30
+ childrenIndexes: {
31
+ [itemId: string]: number;
32
+ };
33
+ itemsChildren: {
34
+ id: string | null;
35
+ children: TreeViewBaseItem[];
36
+ }[];
37
+ };
38
+ interface BuildItemsLookupsParameters {
39
+ items: readonly TreeViewBaseItem[];
40
+ config: BuildItemsLookupConfig;
41
+ parentId: string | null;
42
+ depth: number;
43
+ isItemExpandable: (item: TreeViewBaseItem, children: TreeViewBaseItem[] | undefined) => boolean;
44
+ otherItemsMetaLookup: {
45
+ [itemId: string]: TreeViewItemMeta;
46
+ };
47
+ }
48
+ export interface BuildItemsLookupConfig extends Pick<UseTreeViewItemsParametersWithDefaults<TreeViewBaseItem>, 'isItemDisabled' | 'getItemLabel' | 'getItemChildren' | 'getItemId'> {}
49
+ export {};
@@ -36,4 +36,118 @@ export const isItemDisabled = (itemMetaLookup, itemId) => {
36
36
  }
37
37
  }
38
38
  return false;
39
- };
39
+ };
40
+ export function buildItemsState(parameters) {
41
+ const {
42
+ config,
43
+ items: itemsParam,
44
+ disabledItemsFocusable
45
+ } = parameters;
46
+ const itemMetaLookup = {};
47
+ const itemModelLookup = {};
48
+ const itemOrderedChildrenIdsLookup = {};
49
+ const itemChildrenIndexesLookup = {};
50
+ function processSiblings(items, parentId, depth) {
51
+ const parentIdWithDefault = parentId ?? TREE_VIEW_ROOT_PARENT_ID;
52
+ const {
53
+ metaLookup,
54
+ modelLookup,
55
+ orderedChildrenIds,
56
+ childrenIndexes,
57
+ itemsChildren
58
+ } = buildItemsLookups({
59
+ config,
60
+ items,
61
+ parentId,
62
+ depth,
63
+ isItemExpandable: (item, children) => !!children && children.length > 0,
64
+ otherItemsMetaLookup: itemMetaLookup
65
+ });
66
+ Object.assign(itemMetaLookup, metaLookup);
67
+ Object.assign(itemModelLookup, modelLookup);
68
+ itemOrderedChildrenIdsLookup[parentIdWithDefault] = orderedChildrenIds;
69
+ itemChildrenIndexesLookup[parentIdWithDefault] = childrenIndexes;
70
+ for (const item of itemsChildren) {
71
+ processSiblings(item.children || [], item.id, depth + 1);
72
+ }
73
+ }
74
+ processSiblings(itemsParam, null, 0);
75
+ return {
76
+ disabledItemsFocusable,
77
+ itemMetaLookup,
78
+ itemModelLookup,
79
+ itemOrderedChildrenIdsLookup,
80
+ itemChildrenIndexesLookup,
81
+ domStructure: 'nested'
82
+ };
83
+ }
84
+ export function buildItemsLookups(parameters) {
85
+ const {
86
+ config,
87
+ items,
88
+ parentId,
89
+ depth,
90
+ isItemExpandable,
91
+ otherItemsMetaLookup
92
+ } = parameters;
93
+ const metaLookup = {};
94
+ const modelLookup = {};
95
+ const orderedChildrenIds = [];
96
+ const itemsChildren = [];
97
+ const processItem = item => {
98
+ const id = config.getItemId ? config.getItemId(item) : item.id;
99
+ checkId({
100
+ id,
101
+ parentId,
102
+ item,
103
+ itemMetaLookup: otherItemsMetaLookup,
104
+ siblingsMetaLookup: metaLookup
105
+ });
106
+ const label = config.getItemLabel ? config.getItemLabel(item) : item.label;
107
+ if (label == null) {
108
+ throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
109
+ }
110
+ const children = (config.getItemChildren ? config.getItemChildren(item) : item.children) || [];
111
+ itemsChildren.push({
112
+ id,
113
+ children
114
+ });
115
+ modelLookup[id] = item;
116
+ metaLookup[id] = {
117
+ id,
118
+ label,
119
+ parentId,
120
+ idAttribute: undefined,
121
+ expandable: isItemExpandable(item, children),
122
+ disabled: config.isItemDisabled ? config.isItemDisabled(item) : false,
123
+ depth
124
+ };
125
+ orderedChildrenIds.push(id);
126
+ };
127
+ for (const item of items) {
128
+ processItem(item);
129
+ }
130
+ return {
131
+ metaLookup,
132
+ modelLookup,
133
+ orderedChildrenIds,
134
+ childrenIndexes: buildSiblingIndexes(orderedChildrenIds),
135
+ itemsChildren
136
+ };
137
+ }
138
+ function checkId({
139
+ id,
140
+ parentId,
141
+ item,
142
+ itemMetaLookup,
143
+ siblingsMetaLookup
144
+ }) {
145
+ if (id == null) {
146
+ throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
147
+ }
148
+ if (siblingsMetaLookup[id] != null ||
149
+ // Ignore items with the same parent id, because it's the same item from the previous generation.
150
+ itemMetaLookup[id] != null && itemMetaLookup[id].parentId !== parentId) {
151
+ 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: "${id}"`].join('\n'));
152
+ }
153
+ }