@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
@@ -134,6 +134,15 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
134
134
  // | These PropTypes are generated from the TypeScript type definitions |
135
135
  // | To update them edit the TypeScript types and run "yarn proptypes" |
136
136
  // ----------------------------------------------------------------------
137
+ /**
138
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
139
+ */
140
+ apiRef: PropTypes.shape({
141
+ current: PropTypes.shape({
142
+ focusNode: PropTypes.func.isRequired,
143
+ getItem: PropTypes.func.isRequired
144
+ })
145
+ }),
137
146
  /**
138
147
  * Override or extend the styles applied to the component.
139
148
  */
@@ -3,9 +3,10 @@ import { Theme } from '@mui/material/styles';
3
3
  import { SxProps } from '@mui/system';
4
4
  import { SlotComponentProps } from '@mui/base/utils';
5
5
  import { RichTreeViewClasses } from './richTreeViewClasses';
6
- import { DefaultTreeViewPluginParameters, DefaultTreeViewPluginSlotProps, DefaultTreeViewPluginSlots } from '../internals/plugins/defaultPlugins';
6
+ import { DefaultTreeViewPluginParameters, DefaultTreeViewPluginSlotProps, DefaultTreeViewPluginSlots, DefaultTreeViewPlugins } from '../internals/plugins/defaultPlugins';
7
7
  import { TreeItem, TreeItemProps } from '../TreeItem';
8
8
  import { TreeViewItemId } from '../models';
9
+ import { TreeViewPublicAPI } from '../internals/models';
9
10
  interface RichTreeViewItemSlotOwnerState {
10
11
  nodeId: TreeViewItemId;
11
12
  label: string;
@@ -26,6 +27,7 @@ export interface RichTreeViewSlotProps<R extends {}, Multiple extends boolean |
26
27
  root?: SlotComponentProps<'ul', {}, RichTreeViewProps<R, Multiple>>;
27
28
  item?: SlotComponentProps<typeof TreeItem, {}, RichTreeViewItemSlotOwnerState>;
28
29
  }
30
+ export type RichTreeViewApiRef = React.MutableRefObject<TreeViewPublicAPI<DefaultTreeViewPlugins> | undefined>;
29
31
  export interface RichTreeViewPropsBase extends React.HTMLAttributes<HTMLUListElement> {
30
32
  className?: string;
31
33
  /**
@@ -48,5 +50,9 @@ export interface RichTreeViewProps<R extends {}, Multiple extends boolean | unde
48
50
  * @default {}
49
51
  */
50
52
  slotProps?: RichTreeViewSlotProps<R, Multiple>;
53
+ /**
54
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
55
+ */
56
+ apiRef?: RichTreeViewApiRef;
51
57
  }
52
58
  export {};
@@ -91,6 +91,15 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
91
91
  // | These PropTypes are generated from the TypeScript type definitions |
92
92
  // | To update them edit the TypeScript types and run "yarn proptypes" |
93
93
  // ----------------------------------------------------------------------
94
+ /**
95
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
96
+ */
97
+ apiRef: PropTypes.shape({
98
+ current: PropTypes.shape({
99
+ focusNode: PropTypes.func.isRequired,
100
+ getItem: PropTypes.func.isRequired
101
+ })
102
+ }),
94
103
  /**
95
104
  * The content of the component.
96
105
  */
@@ -3,7 +3,8 @@ import { Theme } from '@mui/material/styles';
3
3
  import { SlotComponentProps } from '@mui/base/utils';
4
4
  import { SxProps } from '@mui/system';
5
5
  import { SimpleTreeViewClasses } from './simpleTreeViewClasses';
6
- import { SimpleTreeViewPluginParameters, SimpleTreeViewPluginSlotProps, SimpleTreeViewPluginSlots } from './SimpleTreeView.plugins';
6
+ import { SimpleTreeViewPluginParameters, SimpleTreeViewPluginSlotProps, SimpleTreeViewPluginSlots, SimpleTreeViewPlugins } from './SimpleTreeView.plugins';
7
+ import { TreeViewPublicAPI } from '../internals/models';
7
8
  export interface SimpleTreeViewSlots extends SimpleTreeViewPluginSlots {
8
9
  /**
9
10
  * Element rendered at the root.
@@ -14,6 +15,7 @@ export interface SimpleTreeViewSlots extends SimpleTreeViewPluginSlots {
14
15
  export interface SimpleTreeViewSlotProps extends SimpleTreeViewPluginSlotProps {
15
16
  root?: SlotComponentProps<'ul', {}, {}>;
16
17
  }
18
+ export type SimpleTreeViewApiRef = React.MutableRefObject<TreeViewPublicAPI<SimpleTreeViewPlugins> | undefined>;
17
19
  export interface SimpleTreeViewProps<Multiple extends boolean | undefined> extends SimpleTreeViewPluginParameters<Multiple>, React.HTMLAttributes<HTMLUListElement> {
18
20
  /**
19
21
  * The content of the component.
@@ -36,4 +38,8 @@ export interface SimpleTreeViewProps<Multiple extends boolean | undefined> exten
36
38
  * The system prop that allows defining system overrides as well as additional CSS styles.
37
39
  */
38
40
  sx?: SxProps<Theme>;
41
+ /**
42
+ * The ref object that allows Tree View manipulation. Can be instantiated with `useTreeViewApiRef()`.
43
+ */
44
+ apiRef?: SimpleTreeViewApiRef;
39
45
  }
@@ -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,
@@ -135,7 +135,7 @@ const TreeItemGroup = styled(Collapse, {
135
135
  * - [TreeItem API](https://mui.com/x/api/tree-view/tree-item/)
136
136
  */
137
137
  export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps, inRef) {
138
- var _ref, _inSlots$expandIcon, _ref2, _inSlots$collapseIcon, _inSlots$endIcon;
138
+ var _ref, _inSlots$expandIcon, _ref2, _inSlots$collapseIcon, _inSlots$endIcon, _slots$groupTransitio;
139
139
  const {
140
140
  icons: contextIcons,
141
141
  runItemPlugins,
@@ -168,16 +168,15 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
168
168
  id,
169
169
  label,
170
170
  onClick,
171
- onMouseDown,
172
- TransitionComponent = Collapse,
173
- TransitionProps
171
+ onMouseDown
174
172
  } = props,
175
173
  other = _objectWithoutPropertiesLoose(props, _excluded);
176
174
  const slots = {
177
175
  expandIcon: (_ref = (_inSlots$expandIcon = inSlots == null ? void 0 : inSlots.expandIcon) != null ? _inSlots$expandIcon : contextIcons.slots.expandIcon) != null ? _ref : TreeViewExpandIcon,
178
176
  collapseIcon: (_ref2 = (_inSlots$collapseIcon = inSlots == null ? void 0 : inSlots.collapseIcon) != null ? _inSlots$collapseIcon : contextIcons.slots.collapseIcon) != null ? _ref2 : TreeViewCollapseIcon,
179
177
  endIcon: (_inSlots$endIcon = inSlots == null ? void 0 : inSlots.endIcon) != null ? _inSlots$endIcon : contextIcons.slots.endIcon,
180
- icon: inSlots == null ? void 0 : inSlots.icon
178
+ icon: inSlots == null ? void 0 : inSlots.icon,
179
+ groupTransition: inSlots == null ? void 0 : inSlots.groupTransition
181
180
  };
182
181
  const isExpandable = reactChildren => {
183
182
  if (Array.isArray(reactChildren)) {
@@ -197,6 +196,19 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
197
196
  disabled
198
197
  });
199
198
  const classes = useUtilityClasses(ownerState);
199
+ const GroupTransition = (_slots$groupTransitio = slots.groupTransition) != null ? _slots$groupTransitio : undefined;
200
+ const groupTransitionProps = useSlotProps({
201
+ elementType: GroupTransition,
202
+ ownerState: {},
203
+ externalSlotProps: inSlotProps == null ? void 0 : inSlotProps.groupTransition,
204
+ additionalProps: {
205
+ unmountOnExit: true,
206
+ in: expanded,
207
+ component: 'ul',
208
+ role: 'group'
209
+ },
210
+ className: classes.groupTransition
211
+ });
200
212
  const ExpansionIcon = expanded ? slots.collapseIcon : slots.expandIcon;
201
213
  const _useSlotProps = useSlotProps({
202
214
  elementType: ExpansionIcon,
@@ -286,13 +298,8 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
286
298
  displayIcon: displayIcon,
287
299
  ownerState: ownerState
288
300
  }, ContentProps)), children && /*#__PURE__*/_jsx(TreeItemGroup, _extends({
289
- as: TransitionComponent,
290
- unmountOnExit: true,
291
- className: classes.group,
292
- in: expanded,
293
- component: "ul",
294
- role: "group"
295
- }, TransitionProps, {
301
+ as: GroupTransition
302
+ }, groupTransitionProps, {
296
303
  children: children
297
304
  }))]
298
305
  }));
@@ -352,16 +359,5 @@ TreeItem.propTypes = {
352
359
  /**
353
360
  * The system prop that allows defining system overrides as well as additional CSS styles.
354
361
  */
355
- sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
356
- /**
357
- * The component used for the transition.
358
- * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
359
- * @default Collapse
360
- */
361
- TransitionComponent: PropTypes.elementType,
362
- /**
363
- * Props applied to the transition element.
364
- * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component.
365
- */
366
- TransitionProps: PropTypes.object
362
+ sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
367
363
  };
@@ -6,6 +6,7 @@ import { SxProps } from '@mui/system';
6
6
  import { TreeItemContentProps } from './TreeItemContent';
7
7
  import { TreeItemClasses } from './treeItemClasses';
8
8
  import { TreeViewItemId } from '../models';
9
+ import { SlotComponentPropsFromProps } from '../internals/models';
9
10
  export interface TreeItemSlots {
10
11
  /**
11
12
  * The icon used to collapse the node.
@@ -23,12 +24,18 @@ export interface TreeItemSlots {
23
24
  * The icon to display next to the tree node's label.
24
25
  */
25
26
  icon?: React.ElementType;
27
+ /**
28
+ * The component that animates to appearance / disappearance of the item's children.
29
+ * @default TreeItem2Group
30
+ */
31
+ groupTransition?: React.ElementType;
26
32
  }
27
33
  export interface TreeItemSlotProps {
28
34
  collapseIcon?: SlotComponentProps<'svg', {}, {}>;
29
35
  expandIcon?: SlotComponentProps<'svg', {}, {}>;
30
36
  endIcon?: SlotComponentProps<'svg', {}, {}>;
31
37
  icon?: SlotComponentProps<'svg', {}, {}>;
38
+ groupTransition?: SlotComponentPropsFromProps<TransitionProps, {}, {}>;
32
39
  }
33
40
  export interface TreeItemProps extends Omit<React.HTMLAttributes<HTMLLIElement>, 'onFocus'> {
34
41
  /**
@@ -77,17 +84,6 @@ export interface TreeItemProps extends Omit<React.HTMLAttributes<HTMLLIElement>,
77
84
  * The id of the node.
78
85
  */
79
86
  nodeId: TreeViewItemId;
80
- /**
81
- * The component used for the transition.
82
- * [Follow this guide](/material-ui/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
83
- * @default Collapse
84
- */
85
- TransitionComponent?: React.JSXElementConstructor<TransitionProps>;
86
- /**
87
- * Props applied to the transition element.
88
- * By default, the element is based on this [`Transition`](https://reactcommunity.org/react-transition-group/transition/) component.
89
- */
90
- TransitionProps?: TransitionProps;
91
87
  /**
92
88
  * The system prop that allows defining system overrides as well as additional CSS styles.
93
89
  */
@@ -2,7 +2,7 @@ export interface TreeItemClasses {
2
2
  /** Styles applied to the root element. */
3
3
  root: string;
4
4
  /** Styles applied to the transition component. */
5
- group: string;
5
+ groupTransition: string;
6
6
  /** Styles applied to the content element. */
7
7
  content: string;
8
8
  /** State class applied to the content element when expanded. */
@@ -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';
package/hooks/index.js ADDED
@@ -0,0 +1 @@
1
+ export { useTreeViewApiRef } from './useTreeViewApiRef';
@@ -0,0 +1,6 @@
1
+ {
2
+ "sideEffects": false,
3
+ "module": "./index.js",
4
+ "main": "../node/hooks/index.js",
5
+ "types": "./index.d.ts"
6
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ import { TreeViewAnyPluginSignature, TreeViewPublicAPI } from '../internals/models';
3
+ /**
4
+ * Hook that instantiates a [[TreeViewApiRef]].
5
+ */
6
+ export declare const useTreeViewApiRef: <TPlugins extends readonly TreeViewAnyPluginSignature[] = [import("../internals").UseTreeViewIdSignature, import("../internals").UseTreeViewNodesSignature, import("../internals").UseTreeViewExpansionSignature, import("../internals").UseTreeViewSelectionSignature, import("../internals").UseTreeViewFocusSignature, import("../internals").UseTreeViewKeyboardNavigationSignature, import("../internals").UseTreeViewIconsSignature]>() => React.MutableRefObject<TreeViewPublicAPI<TPlugins> | undefined>;
@@ -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/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export * from './RichTreeView';
5
5
  export { unstable_resetCleanupTracking } from './internals/hooks/useInstanceEventHandler';
6
6
  export * from './models';
7
7
  export * from './icons';
8
+ export * from './hooks';
package/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,9 +1,11 @@
1
- declare class Timeout {
1
+ export declare class Timeout {
2
2
  static create(): Timeout;
3
- currentId: number;
3
+ currentId: ReturnType<typeof setTimeout> | null;
4
+ /**
5
+ * Executes `fn` after `delay`, clearing any previously scheduled call.
6
+ */
4
7
  start(delay: number, fn: Function): void;
5
8
  clear: () => void;
6
9
  disposeEffect: () => () => void;
7
10
  }
8
11
  export declare function useTimeout(): Timeout;
9
- export {};
@@ -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() {
@@ -1,5 +1,6 @@
1
1
  import type { TreeViewAnyPluginSignature, TreeViewPlugin } from './plugin';
2
2
  export type DefaultizedProps<P extends {}, RequiredProps extends keyof P, AdditionalProps extends {} = {}> = Omit<P, RequiredProps | keyof AdditionalProps> & Required<Pick<P, RequiredProps>> & AdditionalProps;
3
+ export type SlotComponentPropsFromProps<TProps extends {}, TOverrides extends {}, TOwnerState extends {}> = (Partial<TProps> & TOverrides) | ((ownerState: TOwnerState) => Partial<TProps> & TOverrides);
3
4
  type IsAny<T> = 0 extends 1 & T ? true : false;
4
5
  export type OptionalIfEmpty<A extends string, B> = keyof B extends never ? Partial<Record<A, B>> : IsAny<B> extends true ? Partial<Record<A, B>> : Record<A, B>;
5
6
  export type MergePluginsProperty<TPlugins extends readonly any[], TProperty extends keyof TreeViewAnyPluginSignature> = TPlugins extends readonly [plugin: infer P, ...otherPlugin: infer R] ? P extends TreeViewAnyPluginSignature ? P[TProperty] & MergePluginsProperty<R, TProperty> : {} : {};
@@ -7,6 +8,7 @@ export type ConvertPluginsIntoSignatures<TPlugins extends readonly any[]> = TPlu
7
8
  export interface MergePlugins<TPlugins extends readonly any[]> {
8
9
  state: MergePluginsProperty<TPlugins, 'state'>;
9
10
  instance: MergePluginsProperty<TPlugins, 'instance'>;
11
+ publicAPI: MergePluginsProperty<TPlugins, 'publicAPI'>;
10
12
  params: MergePluginsProperty<TPlugins, 'params'>;
11
13
  defaultizedParams: MergePluginsProperty<TPlugins, 'defaultizedParams'>;
12
14
  dependantPlugins: MergePluginsProperty<TPlugins, 'dependantPlugins'>;
@@ -7,6 +7,7 @@ import type { TreeViewCorePluginsSignature } from '../corePlugins';
7
7
  import type { TreeItemProps } from '../../TreeItem';
8
8
  export interface TreeViewPluginOptions<TSignature extends TreeViewAnyPluginSignature> {
9
9
  instance: TreeViewUsedInstance<TSignature>;
10
+ publicAPI: TreeViewUsedPublicAPI<TSignature>;
10
11
  params: TreeViewUsedDefaultizedParams<TSignature>;
11
12
  state: TreeViewUsedState<TSignature>;
12
13
  slots: TSignature['slots'];
@@ -27,6 +28,7 @@ export type TreeViewPluginSignature<T extends {
27
28
  params?: {};
28
29
  defaultizedParams?: {};
29
30
  instance?: {};
31
+ publicAPI?: {};
30
32
  events?: {
31
33
  [key in keyof T['events']]: TreeViewEventLookupElement;
32
34
  };
@@ -50,6 +52,9 @@ export type TreeViewPluginSignature<T extends {
50
52
  instance: T extends {
51
53
  instance: {};
52
54
  } ? T['instance'] : {};
55
+ publicAPI: T extends {
56
+ publicAPI: {};
57
+ } ? T['publicAPI'] : {};
53
58
  events: T extends {
54
59
  events: {};
55
60
  } ? T['events'] : {};
@@ -86,6 +91,7 @@ export type TreeViewAnyPluginSignature = {
86
91
  slots: any;
87
92
  slotProps: any;
88
93
  models: any;
94
+ publicAPI: any;
89
95
  };
90
96
  type TreeViewUsedPlugins<TSignature extends TreeViewAnyPluginSignature> = [
91
97
  TreeViewCorePluginsSignature,
@@ -99,6 +105,12 @@ export type TreeViewUsedInstance<TSignature extends TreeViewAnyPluginSignature>
99
105
  */
100
106
  $$signature: TSignature;
101
107
  };
108
+ export type TreeViewUsedPublicAPI<TSignature extends TreeViewAnyPluginSignature> = TSignature['publicAPI'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'publicAPI'> & {
109
+ /**
110
+ * Private property only defined in TypeScript to be able to access the plugin signature from the publicAPI object.
111
+ */
112
+ $$signature: TSignature;
113
+ };
102
114
  type TreeViewUsedState<TSignature extends TreeViewAnyPluginSignature> = TSignature['state'] & MergePluginsProperty<TreeViewUsedPlugins<TSignature>, 'state'>;
103
115
  type RemoveSetValue<Models extends Record<string, TreeViewModel<any>>> = {
104
116
  [K in keyof Models]: Omit<Models[K], 'setValue'>;
@@ -24,3 +24,4 @@ export interface TreeViewModel<TValue> {
24
24
  setControlledValue: (value: TValue | ((prevValue: TValue) => TValue)) => void;
25
25
  }
26
26
  export type TreeViewInstance<TSignatures extends readonly TreeViewAnyPluginSignature[]> = MergePluginsProperty<TSignatures, 'instance'>;
27
+ export type TreeViewPublicAPI<TSignatures extends readonly TreeViewAnyPluginSignature[]> = MergePluginsProperty<TSignatures, 'publicAPI'>;
@@ -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
  var _rootRef$current;
32
60
  (_rootRef$current = rootRef.current) == null || _rootRef$current.focus({
@@ -36,7 +64,11 @@ export const useTreeViewFocus = ({
36
64
  populateInstance(instance, {
37
65
  isNodeFocused,
38
66
  focusNode,
39
- focusRoot
67
+ focusRoot,
68
+ focusDefaultNode
69
+ });
70
+ populatePublicAPI(publicAPI, {
71
+ focusNode
40
72
  });
41
73
  useInstanceEventHandler(instance, 'removeNode', ({
42
74
  id
@@ -51,23 +83,9 @@ export const useTreeViewFocus = ({
51
83
  const createHandleFocus = otherHandlers => event => {
52
84
  var _otherHandlers$onFocu;
53
85
  (_otherHandlers$onFocu = otherHandlers.onFocus) == null || _otherHandlers$onFocu.call(otherHandlers, event);
54
-
55
86
  // if the event bubbled (which is React specific) we don't want to steal focus
56
87
  if (event.target === event.currentTarget) {
57
- const isNodeVisible = nodeId => {
58
- const node = instance.getNode(nodeId);
59
- return node && (node.parentId == null || instance.isNodeExpanded(node.parentId));
60
- };
61
- let nodeToFocusId;
62
- if (Array.isArray(models.selectedNodes.value)) {
63
- nodeToFocusId = models.selectedNodes.value.find(isNodeVisible);
64
- } else if (models.selectedNodes.value != null && isNodeVisible(models.selectedNodes.value)) {
65
- nodeToFocusId = models.selectedNodes.value;
66
- }
67
- if (nodeToFocusId == null) {
68
- nodeToFocusId = instance.getNavigableChildrenIds(null)[0];
69
- }
70
- instance.focusNode(event, nodeToFocusId);
88
+ instance.focusDefaultNode(event);
71
89
  }
72
90
  };
73
91
  const createHandleBlur = otherHandlers => event => {
@@ -7,8 +7,11 @@ import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
7
7
  export interface UseTreeViewFocusInstance {
8
8
  isNodeFocused: (nodeId: string) => boolean;
9
9
  focusNode: (event: React.SyntheticEvent, nodeId: string | null) => void;
10
+ focusDefaultNode: (event: React.SyntheticEvent) => void;
10
11
  focusRoot: () => void;
11
12
  }
13
+ export interface UseTreeViewFocusPublicAPI extends Pick<UseTreeViewFocusInstance, 'focusNode'> {
14
+ }
12
15
  export interface UseTreeViewFocusParameters {
13
16
  /**
14
17
  * Callback fired when tree items are focused.
@@ -26,6 +29,7 @@ export type UseTreeViewFocusSignature = TreeViewPluginSignature<{
26
29
  params: UseTreeViewFocusParameters;
27
30
  defaultizedParams: UseTreeViewFocusDefaultizedParameters;
28
31
  instance: UseTreeViewFocusInstance;
32
+ publicAPI: UseTreeViewFocusPublicAPI;
29
33
  state: UseTreeViewFocusState;
30
34
  dependantPlugins: [
31
35
  UseTreeViewIdSignature,
@@ -13,22 +13,36 @@ export const useTreeViewJSXNodes = ({
13
13
  }) => {
14
14
  const insertJSXNode = useEventCallback(node => {
15
15
  setState(prevState => {
16
- if (prevState.nodeMap[node.id] != null) {
16
+ if (prevState.nodes.nodeMap[node.id] != null) {
17
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.', `Tow items were provided with the same id in the \`items\` prop: "${node.id}"`].join('\n'));
18
18
  }
19
19
  return _extends({}, prevState, {
20
- nodeMap: _extends({}, prevState.nodeMap, {
21
- [node.id]: node
20
+ nodes: _extends({}, prevState.nodes, {
21
+ nodeMap: _extends({}, prevState.nodes.nodeMap, {
22
+ [node.id]: node
23
+ }),
24
+ // For `SimpleTreeView`, we don't have a proper `item` object, so we create a very basic one.
25
+ itemMap: _extends({}, prevState.nodes.itemMap, {
26
+ [node.id]: {
27
+ id: node.id,
28
+ label: node.label
29
+ }
30
+ })
22
31
  })
23
32
  });
24
33
  });
25
34
  });
26
35
  const removeJSXNode = useEventCallback(nodeId => {
27
36
  setState(prevState => {
28
- const newMap = _extends({}, prevState.nodeMap);
29
- delete newMap[nodeId];
37
+ const newNodeMap = _extends({}, prevState.nodes.nodeMap);
38
+ const newItemMap = _extends({}, prevState.nodes.itemMap);
39
+ delete newNodeMap[nodeId];
40
+ delete newItemMap[nodeId];
30
41
  return _extends({}, prevState, {
31
- nodeMap: newMap
42
+ nodes: _extends({}, prevState.nodes, {
43
+ nodeMap: newNodeMap,
44
+ itemMap: newItemMap
45
+ })
32
46
  });
33
47
  });
34
48
  publishTreeViewEvent(instance, 'removeNode', {