@mui/x-tree-view 7.0.0-beta.4 → 7.0.0-beta.6

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 (69) hide show
  1. package/CHANGELOG.md +271 -61
  2. package/RichTreeView/RichTreeView.js +9 -0
  3. package/RichTreeView/RichTreeView.types.d.ts +7 -1
  4. package/SimpleTreeView/SimpleTreeView.js +9 -0
  5. package/SimpleTreeView/SimpleTreeView.types.d.ts +7 -1
  6. package/TreeItem/TreeItem.js +24 -28
  7. package/TreeItem/TreeItem.types.d.ts +7 -11
  8. package/TreeItem/treeItemClasses.d.ts +1 -1
  9. package/TreeItem/treeItemClasses.js +1 -1
  10. package/TreeView/TreeView.js +9 -0
  11. package/hooks/index.d.ts +1 -0
  12. package/hooks/index.js +1 -0
  13. package/hooks/package.json +6 -0
  14. package/hooks/useTreeViewApiRef.d.ts +6 -0
  15. package/hooks/useTreeViewApiRef.js +5 -0
  16. package/index.d.ts +1 -0
  17. package/index.js +3 -2
  18. package/internals/hooks/useTimeout.d.ts +5 -3
  19. package/internals/hooks/useTimeout.js +13 -5
  20. package/internals/models/helpers.d.ts +2 -0
  21. package/internals/models/plugin.d.ts +12 -0
  22. package/internals/models/treeView.d.ts +1 -0
  23. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +40 -22
  24. package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +4 -0
  25. package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +20 -6
  26. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +29 -16
  27. package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +16 -6
  28. package/internals/useTreeView/useTreeView.d.ts +2 -0
  29. package/internals/useTreeView/useTreeView.js +12 -0
  30. package/internals/useTreeView/useTreeView.types.d.ts +2 -1
  31. package/internals/useTreeView/useTreeView.utils.d.ts +2 -1
  32. package/internals/useTreeView/useTreeView.utils.js +3 -0
  33. package/internals/utils/extractPluginParamsFromProps.d.ts +3 -2
  34. package/internals/utils/extractPluginParamsFromProps.js +5 -3
  35. package/internals/utils/utils.d.ts +1 -0
  36. package/internals/utils/utils.js +10 -0
  37. package/modern/RichTreeView/RichTreeView.js +9 -0
  38. package/modern/SimpleTreeView/SimpleTreeView.js +9 -0
  39. package/modern/TreeItem/TreeItem.js +23 -27
  40. package/modern/TreeItem/treeItemClasses.js +1 -1
  41. package/modern/TreeView/TreeView.js +9 -0
  42. package/modern/hooks/index.js +1 -0
  43. package/modern/hooks/useTreeViewApiRef.js +5 -0
  44. package/modern/index.js +3 -2
  45. package/modern/internals/hooks/useTimeout.js +13 -5
  46. package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +40 -22
  47. package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +20 -6
  48. package/modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +29 -16
  49. package/modern/internals/useTreeView/useTreeView.js +12 -0
  50. package/modern/internals/useTreeView/useTreeView.utils.js +3 -0
  51. package/modern/internals/utils/extractPluginParamsFromProps.js +5 -3
  52. package/modern/internals/utils/utils.js +10 -0
  53. package/node/RichTreeView/RichTreeView.js +9 -0
  54. package/node/SimpleTreeView/SimpleTreeView.js +9 -0
  55. package/node/TreeItem/TreeItem.js +23 -27
  56. package/node/TreeItem/treeItemClasses.js +1 -1
  57. package/node/TreeView/TreeView.js +9 -0
  58. package/node/hooks/index.js +12 -0
  59. package/node/hooks/useTreeViewApiRef.js +14 -0
  60. package/node/index.js +13 -1
  61. package/node/internals/hooks/useTimeout.js +13 -4
  62. package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +39 -21
  63. package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +20 -6
  64. package/node/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +28 -15
  65. package/node/internals/useTreeView/useTreeView.js +13 -0
  66. package/node/internals/useTreeView/useTreeView.utils.js +6 -2
  67. package/node/internals/utils/extractPluginParamsFromProps.js +5 -3
  68. package/node/internals/utils/utils.js +17 -0
  69. package/package.json +2 -2
@@ -1,15 +1,16 @@
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 { populateInstance } from '../../useTreeView/useTreeView.utils';
4
+ import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
5
5
  import { publishTreeViewEvent } from '../../utils/publishTreeViewEvent';
6
- const updateState = ({
6
+ const updateNodesState = ({
7
7
  items,
8
8
  isItemDisabled,
9
9
  getItemLabel,
10
10
  getItemId
11
11
  }) => {
12
12
  const nodeMap = {};
13
+ const itemMap = {};
13
14
  const processItem = (item, index, parentId) => {
14
15
  var _item$children, _item$children2;
15
16
  const id = getItemId ? getItemId(item) : item.id;
@@ -28,10 +29,11 @@ const updateState = ({
28
29
  label,
29
30
  index,
30
31
  parentId,
31
- idAttribute: id,
32
+ idAttribute: undefined,
32
33
  expandable: !!((_item$children = item.children) != null && _item$children.length),
33
34
  disabled: isItemDisabled ? isItemDisabled(item) : false
34
35
  };
36
+ itemMap[id] = item;
35
37
  return {
36
38
  id,
37
39
  children: (_item$children2 = item.children) == null ? void 0 : _item$children2.map((child, childIndex) => processItem(child, childIndex, id))
@@ -40,16 +42,19 @@ const updateState = ({
40
42
  const nodeTree = items.map((item, itemIndex) => processItem(item, itemIndex, null));
41
43
  return {
42
44
  nodeMap,
43
- nodeTree
45
+ nodeTree,
46
+ itemMap
44
47
  };
45
48
  };
46
49
  export const useTreeViewNodes = ({
47
50
  instance,
51
+ publicAPI,
48
52
  params,
49
53
  state,
50
54
  setState
51
55
  }) => {
52
- const getNode = React.useCallback(nodeId => state.nodeMap[nodeId], [state.nodeMap]);
56
+ const getNode = React.useCallback(nodeId => state.nodes.nodeMap[nodeId], [state.nodes.nodeMap]);
57
+ const getItem = React.useCallback(nodeId => state.nodes.itemMap[nodeId], [state.nodes.itemMap]);
53
58
  const isNodeDisabled = React.useCallback(nodeId => {
54
59
  if (nodeId == null) {
55
60
  return false;
@@ -71,7 +76,7 @@ export const useTreeViewNodes = ({
71
76
  }
72
77
  return false;
73
78
  }, [instance]);
74
- const getChildrenIds = useEventCallback(nodeId => Object.values(state.nodeMap).filter(node => node.parentId === nodeId).sort((a, b) => a.index - b.index).map(child => child.id));
79
+ const getChildrenIds = useEventCallback(nodeId => Object.values(state.nodes.nodeMap).filter(node => node.parentId === nodeId).sort((a, b) => a.index - b.index).map(child => child.id));
75
80
  const getNavigableChildrenIds = nodeId => {
76
81
  let childrenIds = instance.getChildrenIds(nodeId);
77
82
  if (!params.disabledItemsFocusable) {
@@ -81,20 +86,22 @@ export const useTreeViewNodes = ({
81
86
  };
82
87
  React.useEffect(() => {
83
88
  setState(prevState => {
84
- const newState = updateState({
89
+ const newState = updateNodesState({
85
90
  items: params.items,
86
91
  isItemDisabled: params.isItemDisabled,
87
92
  getItemId: params.getItemId,
88
93
  getItemLabel: params.getItemLabel
89
94
  });
90
- Object.values(prevState.nodeMap).forEach(node => {
95
+ Object.values(prevState.nodes.nodeMap).forEach(node => {
91
96
  if (!newState.nodeMap[node.id]) {
92
97
  publishTreeViewEvent(instance, 'removeNode', {
93
98
  id: node.id
94
99
  });
95
100
  }
96
101
  });
97
- return _extends({}, prevState, newState);
102
+ return _extends({}, prevState, {
103
+ nodes: newState
104
+ });
98
105
  });
99
106
  }, [instance, setState, params.items, params.isItemDisabled, params.getItemId, params.getItemLabel]);
100
107
  const getNodesToRender = useEventCallback(() => {
@@ -102,7 +109,7 @@ export const useTreeViewNodes = ({
102
109
  id,
103
110
  children
104
111
  }) => {
105
- const node = state.nodeMap[id];
112
+ const node = state.nodes.nodeMap[id];
106
113
  return {
107
114
  label: node.label,
108
115
  nodeId: node.id,
@@ -110,26 +117,32 @@ export const useTreeViewNodes = ({
110
117
  children: children == null ? void 0 : children.map(getPropsFromNodeId)
111
118
  };
112
119
  };
113
- return state.nodeTree.map(getPropsFromNodeId);
120
+ return state.nodes.nodeTree.map(getPropsFromNodeId);
114
121
  });
115
122
  populateInstance(instance, {
116
123
  getNode,
124
+ getItem,
117
125
  getNodesToRender,
118
126
  getChildrenIds,
119
127
  getNavigableChildrenIds,
120
128
  isNodeDisabled
121
129
  });
130
+ populatePublicAPI(publicAPI, {
131
+ getItem
132
+ });
122
133
  return {
123
134
  contextValue: {
124
135
  disabledItemsFocusable: params.disabledItemsFocusable
125
136
  }
126
137
  };
127
138
  };
128
- useTreeViewNodes.getInitialState = params => updateState({
129
- items: params.items,
130
- isItemDisabled: params.isItemDisabled,
131
- getItemId: params.getItemId,
132
- getItemLabel: params.getItemLabel
139
+ useTreeViewNodes.getInitialState = params => ({
140
+ nodes: updateNodesState({
141
+ items: params.items,
142
+ isItemDisabled: params.isItemDisabled,
143
+ getItemId: params.getItemId,
144
+ getItemLabel: params.getItemLabel
145
+ })
133
146
  });
134
147
  useTreeViewNodes.getDefaultizedParams = params => {
135
148
  var _params$disabledItems;
@@ -6,13 +6,16 @@ interface TreeViewNodeProps {
6
6
  id: string | undefined;
7
7
  children?: TreeViewNodeProps[];
8
8
  }
9
- export interface UseTreeViewNodesInstance {
9
+ export interface UseTreeViewNodesInstance<R extends {}> {
10
10
  getNode: (nodeId: string) => TreeViewNode;
11
+ getItem: (nodeId: string) => R;
11
12
  getNodesToRender: () => TreeViewNodeProps[];
12
13
  getChildrenIds: (nodeId: string | null) => string[];
13
14
  getNavigableChildrenIds: (nodeId: string | null) => string[];
14
15
  isNodeDisabled: (nodeId: string | null) => nodeId is string;
15
16
  }
17
+ export interface UseTreeViewNodesPublicAPI<R extends {}> extends Pick<UseTreeViewNodesInstance<R>, 'getItem'> {
18
+ }
16
19
  export interface UseTreeViewNodesParameters<R extends {}> {
17
20
  /**
18
21
  * If `true`, will allow focus on disabled items.
@@ -58,21 +61,28 @@ export interface TreeViewNodeIdAndChildren {
58
61
  id: TreeViewItemId;
59
62
  children?: TreeViewNodeIdAndChildren[];
60
63
  }
61
- export interface UseTreeViewNodesState {
62
- nodeTree: TreeViewNodeIdAndChildren[];
63
- nodeMap: TreeViewNodeMap;
64
+ export interface UseTreeViewNodesState<R extends {}> {
65
+ nodes: {
66
+ nodeTree: TreeViewNodeIdAndChildren[];
67
+ nodeMap: TreeViewNodeMap;
68
+ itemMap: TreeViewItemMap<R>;
69
+ };
64
70
  }
65
71
  interface UseTreeViewNodesContextValue extends Pick<UseTreeViewNodesDefaultizedParameters<any>, 'disabledItemsFocusable'> {
66
72
  }
67
73
  export type UseTreeViewNodesSignature = TreeViewPluginSignature<{
68
74
  params: UseTreeViewNodesParameters<any>;
69
75
  defaultizedParams: UseTreeViewNodesDefaultizedParameters<any>;
70
- instance: UseTreeViewNodesInstance;
76
+ instance: UseTreeViewNodesInstance<any>;
77
+ publicAPI: UseTreeViewNodesPublicAPI<any>;
71
78
  events: UseTreeViewNodesEventLookup;
72
- state: UseTreeViewNodesState;
79
+ state: UseTreeViewNodesState<any>;
73
80
  contextValue: UseTreeViewNodesContextValue;
74
81
  }>;
75
82
  export type TreeViewNodeMap = {
76
83
  [nodeId: string]: TreeViewNode;
77
84
  };
85
+ export type TreeViewItemMap<R extends {}> = {
86
+ [nodeId: string]: R;
87
+ };
78
88
  export {};
@@ -1,3 +1,5 @@
1
+ import * as React from 'react';
1
2
  import { TreeViewAnyPluginSignature, TreeViewPlugin, ConvertPluginsIntoSignatures } from '../models';
2
3
  import { UseTreeViewParameters, UseTreeViewReturnValue } from './useTreeView.types';
4
+ export declare function useTreeViewApiInitialization<T>(inputApiRef: React.MutableRefObject<T> | undefined): T;
3
5
  export declare const useTreeView: <Plugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]>(inParams: UseTreeViewParameters<Plugins>) => UseTreeViewReturnValue<ConvertPluginsIntoSignatures<Plugins>>;
@@ -3,6 +3,16 @@ import * as React from 'react';
3
3
  import useForkRef from '@mui/utils/useForkRef';
4
4
  import { useTreeViewModels } from './useTreeViewModels';
5
5
  import { TREE_VIEW_CORE_PLUGINS } from '../corePlugins';
6
+ export function useTreeViewApiInitialization(inputApiRef) {
7
+ const fallbackPublicApiRef = React.useRef({});
8
+ if (inputApiRef) {
9
+ if (inputApiRef.current == null) {
10
+ inputApiRef.current = {};
11
+ }
12
+ return inputApiRef.current;
13
+ }
14
+ return fallbackPublicApiRef.current;
15
+ }
6
16
  export const useTreeView = inParams => {
7
17
  const plugins = [...TREE_VIEW_CORE_PLUGINS, ...inParams.plugins];
8
18
  const params = plugins.reduce((acc, plugin) => {
@@ -14,6 +24,7 @@ export const useTreeView = inParams => {
14
24
  const models = useTreeViewModels(plugins, params);
15
25
  const instanceRef = React.useRef({});
16
26
  const instance = instanceRef.current;
27
+ const publicAPI = useTreeViewApiInitialization(inParams.apiRef);
17
28
  const innerRootRef = React.useRef(null);
18
29
  const handleRootRef = useForkRef(innerRootRef, inParams.rootRef);
19
30
  const [state, setState] = React.useState(() => {
@@ -32,6 +43,7 @@ export const useTreeView = inParams => {
32
43
  const runPlugin = plugin => {
33
44
  const pluginResponse = plugin({
34
45
  instance,
46
+ publicAPI,
35
47
  params,
36
48
  slots: params.slots,
37
49
  slotProps: params.slotProps,
@@ -1,9 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import { EventHandlers } from '@mui/base/utils';
3
3
  import type { TreeViewContextValue } from '../TreeViewProvider';
4
- import { TreeViewAnyPluginSignature, TreeViewPlugin, ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewInstance } from '../models';
4
+ import { TreeViewAnyPluginSignature, TreeViewPlugin, ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewInstance, TreeViewPublicAPI } from '../models';
5
5
  export type UseTreeViewParameters<TPlugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]> = UseTreeViewBaseParameters<TPlugins> & MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'params'>;
6
6
  export interface UseTreeViewBaseParameters<TPlugins extends readonly TreeViewPlugin<TreeViewAnyPluginSignature>[]> {
7
+ apiRef: React.MutableRefObject<TreeViewPublicAPI<ConvertPluginsIntoSignatures<TPlugins>>> | undefined;
7
8
  rootRef?: React.Ref<HTMLUListElement> | undefined;
8
9
  plugins: TPlugins;
9
10
  slots: MergePluginsProperty<ConvertPluginsIntoSignatures<TPlugins>, 'slots'>;
@@ -1,4 +1,4 @@
1
- import { TreeViewAnyPluginSignature, TreeViewInstance, TreeViewUsedInstance } from '../models';
1
+ import { TreeViewAnyPluginSignature, TreeViewInstance, TreeViewUsedInstance, TreeViewUsedPublicAPI } from '../models';
2
2
  import type { UseTreeViewExpansionSignature } from '../plugins/useTreeViewExpansion';
3
3
  import type { UseTreeViewNodesSignature } from '../plugins/useTreeViewNodes';
4
4
  export declare const getPreviousNode: (instance: TreeViewInstance<[UseTreeViewNodesSignature, UseTreeViewExpansionSignature]>, nodeId: string) => string | null;
@@ -6,3 +6,4 @@ export declare const getNextNode: (instance: TreeViewInstance<[UseTreeViewExpans
6
6
  export declare const getLastNode: (instance: TreeViewInstance<[UseTreeViewExpansionSignature, UseTreeViewNodesSignature]>) => string;
7
7
  export declare const getFirstNode: (instance: TreeViewInstance<[UseTreeViewNodesSignature]>) => string;
8
8
  export declare const populateInstance: <T extends TreeViewAnyPluginSignature>(instance: TreeViewUsedInstance<T>, methods: T["instance"]) => void;
9
+ export declare const populatePublicAPI: <T extends TreeViewAnyPluginSignature>(publicAPI: TreeViewUsedPublicAPI<T>, methods: T["publicAPI"]) => void;
@@ -40,4 +40,7 @@ export const getLastNode = instance => {
40
40
  export const getFirstNode = instance => instance.getNavigableChildrenIds(null)[0];
41
41
  export const populateInstance = (instance, methods) => {
42
42
  Object.assign(instance, methods);
43
+ };
44
+ export const populatePublicAPI = (publicAPI, methods) => {
45
+ Object.assign(publicAPI, methods);
43
46
  };
@@ -1,10 +1,11 @@
1
1
  import * as React from 'react';
2
- import { ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewPlugin } from '../models';
2
+ import { ConvertPluginsIntoSignatures, MergePluginsProperty, TreeViewPlugin, TreeViewPublicAPI } from '../models';
3
3
  import { UseTreeViewBaseParameters } from '../useTreeView/useTreeView.types';
4
4
  export declare const extractPluginParamsFromProps: <TPlugins extends readonly TreeViewPlugin<any>[], TSlots extends MergePluginsProperty<TPlugins, "slots">, TSlotProps extends MergePluginsProperty<TPlugins, "slotProps">, TProps extends {
5
5
  slots?: TSlots | undefined;
6
6
  slotProps?: TSlotProps | undefined;
7
- }>({ props: { slots, slotProps, ...props }, plugins, rootRef, }: {
7
+ apiRef?: React.MutableRefObject<TreeViewPublicAPI<ConvertPluginsIntoSignatures<TPlugins>> | undefined> | undefined;
8
+ }>({ props: { slots, slotProps, apiRef, ...props }, plugins, rootRef, }: {
8
9
  props: TProps;
9
10
  plugins: TPlugins;
10
11
  rootRef?: React.Ref<HTMLUListElement> | undefined;
@@ -1,10 +1,11 @@
1
1
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2
- const _excluded = ["slots", "slotProps"];
2
+ const _excluded = ["slots", "slotProps", "apiRef"];
3
3
  export const extractPluginParamsFromProps = _ref => {
4
4
  let {
5
5
  props: {
6
6
  slots,
7
- slotProps
7
+ slotProps,
8
+ apiRef
8
9
  },
9
10
  plugins,
10
11
  rootRef
@@ -18,7 +19,8 @@ export const extractPluginParamsFromProps = _ref => {
18
19
  plugins,
19
20
  rootRef,
20
21
  slots: slots != null ? slots : {},
21
- slotProps: slotProps != null ? slotProps : {}
22
+ slotProps: slotProps != null ? slotProps : {},
23
+ apiRef
22
24
  };
23
25
  const otherProps = {};
24
26
  Object.keys(props).forEach(propName => {
@@ -0,0 +1 @@
1
+ export declare const getActiveElement: (root?: Document | ShadowRoot) => Element | null;
@@ -0,0 +1,10 @@
1
+ export const getActiveElement = (root = document) => {
2
+ const activeEl = root.activeElement;
3
+ if (!activeEl) {
4
+ return null;
5
+ }
6
+ if (activeEl.shadowRoot) {
7
+ return getActiveElement(activeEl.shadowRoot);
8
+ }
9
+ return activeEl;
10
+ };
@@ -132,6 +132,15 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
132
132
  // | These PropTypes are generated from the TypeScript type definitions |
133
133
  // | To update them edit the TypeScript types and run "yarn proptypes" |
134
134
  // ----------------------------------------------------------------------
135
+ /**
136
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
137
+ */
138
+ apiRef: PropTypes.shape({
139
+ current: PropTypes.shape({
140
+ focusNode: PropTypes.func.isRequired,
141
+ getItem: PropTypes.func.isRequired
142
+ })
143
+ }),
135
144
  /**
136
145
  * Override or extend the styles applied to the component.
137
146
  */
@@ -90,6 +90,15 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
90
90
  // | These PropTypes are generated from the TypeScript type definitions |
91
91
  // | To update them edit the TypeScript types and run "yarn proptypes" |
92
92
  // ----------------------------------------------------------------------
93
+ /**
94
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
95
+ */
96
+ apiRef: PropTypes.shape({
97
+ current: PropTypes.shape({
98
+ focusNode: PropTypes.func.isRequired,
99
+ getItem: PropTypes.func.isRequired
100
+ })
101
+ }),
93
102
  /**
94
103
  * The content of the component.
95
104
  */
@@ -1,6 +1,6 @@
1
1
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2
2
  import _extends from "@babel/runtime/helpers/esm/extends";
3
- const _excluded = ["children", "className", "slots", "slotProps", "ContentComponent", "ContentProps", "nodeId", "id", "label", "onClick", "onMouseDown", "TransitionComponent", "TransitionProps"],
3
+ const _excluded = ["children", "className", "slots", "slotProps", "ContentComponent", "ContentProps", "nodeId", "id", "label", "onClick", "onMouseDown"],
4
4
  _excluded2 = ["ownerState"],
5
5
  _excluded3 = ["ownerState"],
6
6
  _excluded4 = ["ownerState"];
@@ -32,7 +32,7 @@ const useUtilityClasses = ownerState => {
32
32
  disabled: ['disabled'],
33
33
  iconContainer: ['iconContainer'],
34
34
  label: ['label'],
35
- group: ['group']
35
+ groupTransition: ['groupTransition']
36
36
  };
37
37
  return composeClasses(slots, getTreeItemUtilityClass, classes);
38
38
  };
@@ -116,8 +116,8 @@ const StyledTreeItemContent = styled(TreeItemContent, {
116
116
  }));
117
117
  const TreeItemGroup = styled(Collapse, {
118
118
  name: 'MuiTreeItem',
119
- slot: 'Group',
120
- overridesResolver: (props, styles) => styles.group
119
+ slot: 'GroupTransition',
120
+ overridesResolver: (props, styles) => styles.groupTransition
121
121
  })({
122
122
  margin: 0,
123
123
  padding: 0,
@@ -167,16 +167,15 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
167
167
  id,
168
168
  label,
169
169
  onClick,
170
- onMouseDown,
171
- TransitionComponent = Collapse,
172
- TransitionProps
170
+ onMouseDown
173
171
  } = props,
174
172
  other = _objectWithoutPropertiesLoose(props, _excluded);
175
173
  const slots = {
176
174
  expandIcon: inSlots?.expandIcon ?? contextIcons.slots.expandIcon ?? TreeViewExpandIcon,
177
175
  collapseIcon: inSlots?.collapseIcon ?? contextIcons.slots.collapseIcon ?? TreeViewCollapseIcon,
178
176
  endIcon: inSlots?.endIcon ?? contextIcons.slots.endIcon,
179
- icon: inSlots?.icon
177
+ icon: inSlots?.icon,
178
+ groupTransition: inSlots?.groupTransition
180
179
  };
181
180
  const isExpandable = reactChildren => {
182
181
  if (Array.isArray(reactChildren)) {
@@ -196,6 +195,19 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
196
195
  disabled
197
196
  });
198
197
  const classes = useUtilityClasses(ownerState);
198
+ const GroupTransition = slots.groupTransition ?? undefined;
199
+ const groupTransitionProps = useSlotProps({
200
+ elementType: GroupTransition,
201
+ ownerState: {},
202
+ externalSlotProps: inSlotProps?.groupTransition,
203
+ additionalProps: {
204
+ unmountOnExit: true,
205
+ in: expanded,
206
+ component: 'ul',
207
+ role: 'group'
208
+ },
209
+ className: classes.groupTransition
210
+ });
199
211
  const ExpansionIcon = expanded ? slots.collapseIcon : slots.expandIcon;
200
212
  const _useSlotProps = useSlotProps({
201
213
  elementType: ExpansionIcon,
@@ -285,13 +297,8 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
285
297
  displayIcon: displayIcon,
286
298
  ownerState: ownerState
287
299
  }, ContentProps)), children && /*#__PURE__*/_jsx(TreeItemGroup, _extends({
288
- as: TransitionComponent,
289
- unmountOnExit: true,
290
- className: classes.group,
291
- in: expanded,
292
- component: "ul",
293
- role: "group"
294
- }, TransitionProps, {
300
+ as: GroupTransition
301
+ }, groupTransitionProps, {
295
302
  children: children
296
303
  }))]
297
304
  }));
@@ -351,16 +358,5 @@ TreeItem.propTypes = {
351
358
  /**
352
359
  * The system prop that allows defining system overrides as well as additional CSS styles.
353
360
  */
354
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
355
- /**
356
- * The component used for the transition.
357
- * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
358
- * @default Collapse
359
- */
360
- TransitionComponent: PropTypes.elementType,
361
- /**
362
- * Props applied to the transition element.
363
- * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component.
364
- */
365
- TransitionProps: PropTypes.object
361
+ sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
366
362
  };
@@ -3,4 +3,4 @@ import generateUtilityClasses from '@mui/utils/generateUtilityClasses';
3
3
  export function getTreeItemUtilityClass(slot) {
4
4
  return generateUtilityClass('MuiTreeItem', slot);
5
5
  }
6
- export const treeItemClasses = generateUtilityClasses('MuiTreeItem', ['root', 'group', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label']);
6
+ export const treeItemClasses = generateUtilityClasses('MuiTreeItem', ['root', 'groupTransition', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label']);
@@ -64,6 +64,15 @@ process.env.NODE_ENV !== "production" ? TreeView.propTypes = {
64
64
  // | These PropTypes are generated from the TypeScript type definitions |
65
65
  // | To update them edit the TypeScript types and run "yarn proptypes" |
66
66
  // ----------------------------------------------------------------------
67
+ /**
68
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
69
+ */
70
+ apiRef: PropTypes.shape({
71
+ current: PropTypes.shape({
72
+ focusNode: PropTypes.func.isRequired,
73
+ getItem: PropTypes.func.isRequired
74
+ })
75
+ }),
67
76
  /**
68
77
  * The content of the component.
69
78
  */
@@ -0,0 +1 @@
1
+ export { useTreeViewApiRef } from './useTreeViewApiRef';
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Hook that instantiates a [[TreeViewApiRef]].
4
+ */
5
+ export const useTreeViewApiRef = () => React.useRef(undefined);
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v7.0.0-beta.4
2
+ * @mui/x-tree-view v7.0.0-beta.6
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -11,4 +11,5 @@ export * from './SimpleTreeView';
11
11
  export * from './RichTreeView';
12
12
  export { unstable_resetCleanupTracking } from './internals/hooks/useInstanceEventHandler';
13
13
  export * from './models';
14
- export * from './icons';
14
+ export * from './icons';
15
+ export * from './hooks';
@@ -1,12 +1,14 @@
1
+ 'use client';
2
+
1
3
  import { useLazyRef } from './useLazyRef';
2
4
  import { useOnMount } from './useOnMount';
3
- class Timeout {
5
+ export class Timeout {
4
6
  constructor() {
5
- this.currentId = 0;
7
+ this.currentId = null;
6
8
  this.clear = () => {
7
- if (this.currentId !== 0) {
9
+ if (this.currentId !== null) {
8
10
  clearTimeout(this.currentId);
9
- this.currentId = 0;
11
+ this.currentId = null;
10
12
  }
11
13
  };
12
14
  this.disposeEffect = () => {
@@ -16,9 +18,15 @@ class Timeout {
16
18
  static create() {
17
19
  return new Timeout();
18
20
  }
21
+ /**
22
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
23
+ */
19
24
  start(delay, fn) {
20
25
  this.clear();
21
- this.currentId = setTimeout(fn, delay);
26
+ this.currentId = setTimeout(() => {
27
+ this.currentId = null;
28
+ fn();
29
+ }, delay);
22
30
  }
23
31
  }
24
32
  export function useTimeout() {
@@ -2,10 +2,12 @@ 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 ownerDocument from '@mui/utils/ownerDocument';
5
- import { populateInstance } from '../../useTreeView/useTreeView.utils';
5
+ import { populateInstance, populatePublicAPI } from '../../useTreeView/useTreeView.utils';
6
6
  import { useInstanceEventHandler } from '../../hooks/useInstanceEventHandler';
7
+ import { getActiveElement } from '../../utils/utils';
7
8
  export const useTreeViewFocus = ({
8
9
  instance,
10
+ publicAPI,
9
11
  params,
10
12
  state,
11
13
  setState,
@@ -14,19 +16,45 @@ export const useTreeViewFocus = ({
14
16
  }) => {
15
17
  const setFocusedNodeId = useEventCallback(nodeId => {
16
18
  const cleanNodeId = typeof nodeId === 'function' ? nodeId(state.focusedNodeId) : nodeId;
17
- setState(prevState => _extends({}, prevState, {
18
- focusedNodeId: cleanNodeId
19
- }));
19
+ if (state.focusedNodeId !== cleanNodeId) {
20
+ setState(prevState => _extends({}, prevState, {
21
+ focusedNodeId: cleanNodeId
22
+ }));
23
+ }
20
24
  });
21
- const isNodeFocused = React.useCallback(nodeId => state.focusedNodeId === nodeId, [state.focusedNodeId]);
25
+ const isTreeViewFocused = React.useCallback(() => !!rootRef.current && rootRef.current === getActiveElement(ownerDocument(rootRef.current)), [rootRef]);
26
+ const isNodeFocused = React.useCallback(nodeId => state.focusedNodeId === nodeId && isTreeViewFocused(), [state.focusedNodeId, isTreeViewFocused]);
27
+ const isNodeVisible = nodeId => {
28
+ const node = instance.getNode(nodeId);
29
+ return node && (node.parentId == null || instance.isNodeExpanded(node.parentId));
30
+ };
22
31
  const focusNode = useEventCallback((event, nodeId) => {
23
- if (nodeId) {
32
+ // if we receive a nodeId, and it is visible, the focus will be set to it
33
+ if (nodeId && isNodeVisible(nodeId)) {
34
+ if (!isTreeViewFocused()) {
35
+ instance.focusRoot();
36
+ }
24
37
  setFocusedNodeId(nodeId);
25
38
  if (params.onNodeFocus) {
26
39
  params.onNodeFocus(event, nodeId);
27
40
  }
28
41
  }
29
42
  });
43
+ const focusDefaultNode = useEventCallback(event => {
44
+ let nodeToFocusId;
45
+ if (Array.isArray(models.selectedNodes.value)) {
46
+ nodeToFocusId = models.selectedNodes.value.find(isNodeVisible);
47
+ } else if (models.selectedNodes.value != null && isNodeVisible(models.selectedNodes.value)) {
48
+ nodeToFocusId = models.selectedNodes.value;
49
+ }
50
+ if (nodeToFocusId == null) {
51
+ nodeToFocusId = instance.getNavigableChildrenIds(null)[0];
52
+ }
53
+ setFocusedNodeId(nodeToFocusId);
54
+ if (params.onNodeFocus) {
55
+ params.onNodeFocus(event, nodeToFocusId);
56
+ }
57
+ });
30
58
  const focusRoot = useEventCallback(() => {
31
59
  rootRef.current?.focus({
32
60
  preventScroll: true
@@ -35,7 +63,11 @@ export const useTreeViewFocus = ({
35
63
  populateInstance(instance, {
36
64
  isNodeFocused,
37
65
  focusNode,
38
- focusRoot
66
+ focusRoot,
67
+ focusDefaultNode
68
+ });
69
+ populatePublicAPI(publicAPI, {
70
+ focusNode
39
71
  });
40
72
  useInstanceEventHandler(instance, 'removeNode', ({
41
73
  id
@@ -49,23 +81,9 @@ export const useTreeViewFocus = ({
49
81
  });
50
82
  const createHandleFocus = otherHandlers => event => {
51
83
  otherHandlers.onFocus?.(event);
52
-
53
84
  // if the event bubbled (which is React specific) we don't want to steal focus
54
85
  if (event.target === event.currentTarget) {
55
- const isNodeVisible = nodeId => {
56
- const node = instance.getNode(nodeId);
57
- return node && (node.parentId == null || instance.isNodeExpanded(node.parentId));
58
- };
59
- let nodeToFocusId;
60
- if (Array.isArray(models.selectedNodes.value)) {
61
- nodeToFocusId = models.selectedNodes.value.find(isNodeVisible);
62
- } else if (models.selectedNodes.value != null && isNodeVisible(models.selectedNodes.value)) {
63
- nodeToFocusId = models.selectedNodes.value;
64
- }
65
- if (nodeToFocusId == null) {
66
- nodeToFocusId = instance.getNavigableChildrenIds(null)[0];
67
- }
68
- instance.focusNode(event, nodeToFocusId);
86
+ instance.focusDefaultNode(event);
69
87
  }
70
88
  };
71
89
  const createHandleBlur = otherHandlers => event => {