@mui/x-tree-view 7.12.1 → 7.14.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 (90) hide show
  1. package/CHANGELOG.md +162 -1
  2. package/RichTreeView/RichTreeView.js +20 -2
  3. package/RichTreeView/RichTreeView.plugins.d.ts +3 -2
  4. package/RichTreeView/RichTreeView.plugins.js +2 -1
  5. package/TreeItem/TreeItem.js +24 -0
  6. package/TreeItem/TreeItemContent.d.ts +8 -0
  7. package/TreeItem/TreeItemContent.js +48 -8
  8. package/TreeItem/treeItemClasses.d.ts +6 -0
  9. package/TreeItem/treeItemClasses.js +1 -1
  10. package/TreeItem/useTreeItemState.d.ts +6 -0
  11. package/TreeItem/useTreeItemState.js +46 -1
  12. package/TreeItem2/TreeItem2.d.ts +3 -1
  13. package/TreeItem2/TreeItem2.js +29 -5
  14. package/TreeItem2/TreeItem2.types.d.ts +6 -0
  15. package/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
  16. package/TreeItem2Icon/TreeItem2Icon.js +2 -0
  17. package/TreeItem2LabelInput/TreeItem2LabelInput.d.ts +2 -0
  18. package/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
  19. package/TreeItem2LabelInput/TreeItem2LabelInput.types.d.ts +8 -0
  20. package/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
  21. package/TreeItem2LabelInput/index.d.ts +2 -0
  22. package/TreeItem2LabelInput/index.js +1 -0
  23. package/TreeItem2LabelInput/package.json +6 -0
  24. package/hooks/useTreeItem2Utils/useTreeItem2Utils.d.ts +5 -1
  25. package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  26. package/hooks/useTreeViewApiRef.d.ts +1 -1
  27. package/index.js +1 -1
  28. package/internals/index.d.ts +2 -0
  29. package/internals/index.js +1 -0
  30. package/internals/models/itemPlugin.d.ts +2 -1
  31. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  32. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +2 -0
  33. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  34. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +2 -0
  35. package/internals/plugins/useTreeViewLabel/index.d.ts +2 -0
  36. package/internals/plugins/useTreeViewLabel/index.js +1 -0
  37. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.d.ts +3 -0
  38. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.d.ts +3 -0
  39. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
  40. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
  41. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +75 -0
  42. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
  43. package/modern/RichTreeView/RichTreeView.js +20 -2
  44. package/modern/RichTreeView/RichTreeView.plugins.js +2 -1
  45. package/modern/TreeItem/TreeItem.js +24 -0
  46. package/modern/TreeItem/TreeItemContent.js +48 -8
  47. package/modern/TreeItem/treeItemClasses.js +1 -1
  48. package/modern/TreeItem/useTreeItemState.js +46 -1
  49. package/modern/TreeItem2/TreeItem2.js +29 -5
  50. package/modern/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
  51. package/modern/TreeItem2Icon/TreeItem2Icon.js +2 -0
  52. package/modern/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
  53. package/modern/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
  54. package/modern/TreeItem2LabelInput/index.js +1 -0
  55. package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  56. package/modern/index.js +1 -1
  57. package/modern/internals/index.js +1 -0
  58. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  59. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  60. package/modern/internals/plugins/useTreeViewLabel/index.js +1 -0
  61. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
  62. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
  63. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
  64. package/modern/useTreeItem2/useTreeItem2.js +65 -4
  65. package/node/RichTreeView/RichTreeView.js +20 -2
  66. package/node/RichTreeView/RichTreeView.plugins.js +2 -1
  67. package/node/TreeItem/TreeItem.js +24 -0
  68. package/node/TreeItem/TreeItemContent.js +48 -8
  69. package/node/TreeItem/treeItemClasses.js +1 -1
  70. package/node/TreeItem/useTreeItemState.js +46 -1
  71. package/node/TreeItem2/TreeItem2.js +29 -5
  72. package/node/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
  73. package/node/TreeItem2Icon/TreeItem2Icon.js +2 -0
  74. package/node/TreeItem2LabelInput/TreeItem2LabelInput.js +26 -0
  75. package/node/TreeItem2LabelInput/TreeItem2LabelInput.types.js +5 -0
  76. package/node/TreeItem2LabelInput/index.js +12 -0
  77. package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  78. package/node/index.js +1 -1
  79. package/node/internals/index.js +7 -0
  80. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  81. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  82. package/node/internals/plugins/useTreeViewLabel/index.js +12 -0
  83. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +54 -0
  84. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +91 -0
  85. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +5 -0
  86. package/node/useTreeItem2/useTreeItem2.js +65 -4
  87. package/package.json +5 -5
  88. package/useTreeItem2/index.d.ts +1 -1
  89. package/useTreeItem2/useTreeItem2.js +65 -4
  90. package/useTreeItem2/useTreeItem2.types.d.ts +35 -15
@@ -5,6 +5,7 @@ import useForkRef from '@mui/utils/useForkRef';
5
5
  import { useTreeViewContext } from '../internals/TreeViewProvider';
6
6
  import { useTreeItem2Utils } from '../hooks/useTreeItem2Utils';
7
7
  import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
8
+ import { isTargetInDescendants } from '../internals/utils/tree';
8
9
  export const useTreeItem2 = parameters => {
9
10
  const {
10
11
  runItemPlugins,
@@ -50,6 +51,7 @@ export const useTreeItem2 = parameters => {
50
51
  const handleRootRef = useForkRef(rootRef, pluginRootRef, rootRefObject);
51
52
  const handleContentRef = useForkRef(contentRef, contentRefObject);
52
53
  const checkboxRef = React.useRef(null);
54
+ const rootTabIndex = instance.canItemBeTabbed(itemId) ? 0 : -1;
53
55
  const createRootHandleFocus = otherHandlers => event => {
54
56
  otherHandlers.onFocus?.(event);
55
57
  if (event.defaultMuiPrevented) {
@@ -65,15 +67,33 @@ export const useTreeItem2 = parameters => {
65
67
  if (event.defaultMuiPrevented) {
66
68
  return;
67
69
  }
70
+ const rootElement = instance.getItemDOMElement(itemId);
71
+
72
+ // Don't blur the root when switching to editing mode
73
+ // the input that triggers the root blur can be either the relatedTarget (when entering editing state) or the target (when exiting editing state)
74
+ // when we enter the editing state, we focus the input -> we don't want to remove the focused item from the state
75
+ if (status.editing ||
76
+ // we can exit the editing state by clicking outside the input (within the tree item) or by pressing Enter or Escape -> we don't want to remove the focused item from the state in these cases
77
+ // we can also exit the editing state by clicking on the root itself -> want to remove the focused item from the state in this case
78
+ event.relatedTarget && isTargetInDescendants(event.relatedTarget, rootElement) && (event.target && event.target?.dataset?.element === 'labelInput' && isTargetInDescendants(event.target, rootElement) || event.relatedTarget?.dataset?.element === 'labelInput')) {
79
+ return;
80
+ }
68
81
  instance.removeFocusedItem();
69
82
  };
70
83
  const createRootHandleKeyDown = otherHandlers => event => {
71
84
  otherHandlers.onKeyDown?.(event);
72
- if (event.defaultMuiPrevented) {
85
+ if (event.defaultMuiPrevented || event.target?.dataset?.element === 'labelInput') {
73
86
  return;
74
87
  }
75
88
  instance.handleItemKeyDown(event, itemId);
76
89
  };
90
+ const createLabelHandleDoubleClick = otherHandlers => event => {
91
+ otherHandlers.onDoubleClick?.(event);
92
+ if (event.defaultMuiPrevented) {
93
+ return;
94
+ }
95
+ interactions.toggleItemEditing();
96
+ };
77
97
  const createContentHandleClick = otherHandlers => event => {
78
98
  otherHandlers.onClick?.(event);
79
99
  onItemClick?.(event, itemId);
@@ -108,6 +128,27 @@ export const useTreeItem2 = parameters => {
108
128
  }
109
129
  interactions.handleCheckboxSelection(event);
110
130
  };
131
+ const createInputHandleKeydown = otherHandlers => event => {
132
+ otherHandlers.onKeyDown?.(event);
133
+ if (event.defaultMuiPrevented) {
134
+ return;
135
+ }
136
+ const target = event.target;
137
+ if (event.key === 'Enter' && target.value) {
138
+ interactions.handleSaveItemLabel(event, target.value);
139
+ } else if (event.key === 'Escape') {
140
+ interactions.handleCancelItemLabelEditing(event);
141
+ }
142
+ };
143
+ const createInputHandleBlur = otherHandlers => event => {
144
+ otherHandlers.onBlur?.(event);
145
+ if (event.defaultMuiPrevented) {
146
+ return;
147
+ }
148
+ if (event.target.value) {
149
+ interactions.handleSaveItemLabel(event, event.target.value);
150
+ }
151
+ };
111
152
  const createIconContainerHandleClick = otherHandlers => event => {
112
153
  otherHandlers.onClick?.(event);
113
154
  if (event.defaultMuiPrevented) {
@@ -134,7 +175,7 @@ export const useTreeItem2 = parameters => {
134
175
  const props = _extends({}, externalEventHandlers, {
135
176
  ref: handleRootRef,
136
177
  role: 'treeitem',
137
- tabIndex: instance.canItemBeTabbed(itemId) ? 0 : -1,
178
+ tabIndex: rootTabIndex,
138
179
  id: idAttribute,
139
180
  'aria-expanded': status.expandable ? status.expanded : undefined,
140
181
  'aria-selected': ariaSelected,
@@ -188,9 +229,28 @@ export const useTreeItem2 = parameters => {
188
229
  };
189
230
  const getLabelProps = (externalProps = {}) => {
190
231
  const externalEventHandlers = _extends({}, extractEventHandlers(externalProps));
191
- return _extends({}, externalEventHandlers, {
232
+ const props = _extends({}, externalEventHandlers, {
192
233
  children: label
193
- }, externalProps);
234
+ }, externalProps, {
235
+ onDoubleClick: createLabelHandleDoubleClick(externalEventHandlers)
236
+ });
237
+ if (instance.isTreeViewEditable) {
238
+ props.editable = status.editable;
239
+ }
240
+ return props;
241
+ };
242
+ const getLabelInputProps = (externalProps = {}) => {
243
+ const externalEventHandlers = extractEventHandlers(externalProps);
244
+ const props = _extends({}, externalEventHandlers, externalProps, {
245
+ onKeyDown: createInputHandleKeydown(externalEventHandlers),
246
+ onBlur: createInputHandleBlur(externalEventHandlers)
247
+ });
248
+ const enhancedlabelInputProps = propsEnhancers.labelInput?.({
249
+ rootRefObject,
250
+ contentRefObject,
251
+ externalEventHandlers
252
+ }) ?? {};
253
+ return _extends({}, props, enhancedlabelInputProps);
194
254
  };
195
255
  const getIconContainerProps = (externalProps = {}) => {
196
256
  const externalEventHandlers = extractEventHandlers(externalProps);
@@ -228,6 +288,7 @@ export const useTreeItem2 = parameters => {
228
288
  getIconContainerProps,
229
289
  getCheckboxProps,
230
290
  getLabelProps,
291
+ getLabelInputProps,
231
292
  getDragAndDropOverlayProps,
232
293
  rootRef: handleRootRef,
233
294
  status,
@@ -5,6 +5,7 @@ import { UseTreeViewSelectionSignature } from '../internals/plugins/useTreeViewS
5
5
  import { UseTreeViewItemsSignature } from '../internals/plugins/useTreeViewItems';
6
6
  import { UseTreeViewFocusSignature } from '../internals/plugins/useTreeViewFocus';
7
7
  import { UseTreeViewKeyboardNavigationSignature } from '../internals/plugins/useTreeViewKeyboardNavigation';
8
+ import { UseTreeViewLabelSignature } from '../internals/plugins/useTreeViewLabel';
8
9
  import { UseTreeViewExpansionSignature } from '../internals/plugins/useTreeViewExpansion';
9
10
  export interface UseTreeItem2Parameters {
10
11
  /**
@@ -69,8 +70,18 @@ export interface UseTreeItem2IconContainerSlotOwnProps {
69
70
  export type UseTreeItemIconContainerSlotProps<ExternalProps = {}> = ExternalProps & UseTreeItem2IconContainerSlotOwnProps;
70
71
  export interface UseTreeItem2LabelSlotOwnProps {
71
72
  children: React.ReactNode;
73
+ onDoubleClick: MuiCancellableEventHandler<React.MouseEvent>;
74
+ /**
75
+ * Only defined when the `isItemEditable` experimental feature is enabled.
76
+ */
77
+ editable?: boolean;
72
78
  }
73
79
  export type UseTreeItem2LabelSlotProps<ExternalProps = {}> = ExternalProps & UseTreeItem2LabelSlotOwnProps;
80
+ export type UseTreeItem2LabelInputSlotOwnProps = {
81
+ onBlur: MuiCancellableEventHandler<React.FocusEvent<HTMLInputElement>>;
82
+ onKeyDown: MuiCancellableEventHandler<React.KeyboardEvent<HTMLInputElement>>;
83
+ };
84
+ export type UseTreeItem2LabelInputSlotProps<ExternalProps = {}> = ExternalProps & UseTreeItem2LabelInputSlotOwnProps;
74
85
  export interface UseTreeItem2CheckboxSlotOwnProps {
75
86
  visible: boolean;
76
87
  checked: boolean;
@@ -101,49 +112,57 @@ export interface UseTreeItem2Status {
101
112
  focused: boolean;
102
113
  selected: boolean;
103
114
  disabled: boolean;
115
+ editing: boolean;
116
+ editable: boolean;
104
117
  }
105
118
  export interface UseTreeItem2ReturnValue<TSignatures extends UseTreeItem2MinimalPlugins, TOptionalSignatures extends UseTreeItem2OptionalPlugins> {
106
119
  /**
107
120
  * Resolver for the root slot's props.
108
- * @param {ExternalProps} externalProps Additional props for the root slot
109
- * @returns {UseTreeItem2RootSlotProps<ExternalProps>} Props that should be spread on the root slot
121
+ * @param {ExternalProps} externalProps Additional props for the root slot.
122
+ * @returns {UseTreeItem2RootSlotProps<ExternalProps>} Props that should be spread on the root slot.
110
123
  */
111
124
  getRootProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2RootSlotProps<ExternalProps>;
112
125
  /**
113
126
  * Resolver for the content slot's props.
114
- * @param {ExternalProps} externalProps Additional props for the content slot
115
- * @returns {UseTreeItem2ContentSlotProps<ExternalProps>} Props that should be spread on the content slot
127
+ * @param {ExternalProps} externalProps Additional props for the content slot.
128
+ * @returns {UseTreeItem2ContentSlotProps<ExternalProps>} Props that should be spread on the content slot.
116
129
  */
117
130
  getContentProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2ContentSlotProps<ExternalProps>;
118
131
  /**
119
132
  * Resolver for the label slot's props.
120
- * @param {ExternalProps} externalProps Additional props for the label slot
121
- * @returns {UseTreeItem2LabelSlotProps<ExternalProps>} Props that should be spread on the label slot
133
+ * @param {ExternalProps} externalProps Additional props for the label slot.
134
+ * @returns {UseTreeItem2LabelSlotProps<ExternalProps>} Props that should be spread on the label slot.
122
135
  */
123
136
  getLabelProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2LabelSlotProps<ExternalProps>;
137
+ /**
138
+ * Resolver for the labelInput slot's props.
139
+ * @param {ExternalProps} externalProps Additional props for the labelInput slot.
140
+ * @returns {UseTreeItem2LabelInputSlotProps<ExternalProps>} Props that should be spread on the labelInput slot.
141
+ */
142
+ getLabelInputProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2LabelInputSlotProps<ExternalProps>;
124
143
  /**
125
144
  * Resolver for the checkbox slot's props.
126
- * @param {ExternalProps} externalProps Additional props for the checkbox slot
127
- * @returns {UseTreeItem2CheckboxSlotProps<ExternalProps>} Props that should be spread on the checkbox slot
145
+ * @param {ExternalProps} externalProps Additional props for the checkbox slot.
146
+ * @returns {UseTreeItem2CheckboxSlotProps<ExternalProps>} Props that should be spread on the checkbox slot.
128
147
  */
129
148
  getCheckboxProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2CheckboxSlotProps<ExternalProps>;
130
149
  /**
131
150
  * Resolver for the iconContainer slot's props.
132
- * @param {ExternalProps} externalProps Additional props for the iconContainer slot
133
- * @returns {UseTreeItemIconContainerSlotProps<ExternalProps>} Props that should be spread on the iconContainer slot
151
+ * @param {ExternalProps} externalProps Additional props for the iconContainer slot.
152
+ * @returns {UseTreeItemIconContainerSlotProps<ExternalProps>} Props that should be spread on the iconContainer slot.
134
153
  */
135
154
  getIconContainerProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItemIconContainerSlotProps<ExternalProps>;
136
155
  /**
137
156
  * Resolver for the GroupTransition slot's props.
138
- * @param {ExternalProps} externalProps Additional props for the GroupTransition slot
139
- * @returns {UseTreeItem2GroupTransitionSlotProps<ExternalProps>} Props that should be spread on the GroupTransition slot
157
+ * @param {ExternalProps} externalProps Additional props for the GroupTransition slot.
158
+ * @returns {UseTreeItem2GroupTransitionSlotProps<ExternalProps>} Props that should be spread on the GroupTransition slot.
140
159
  */
141
160
  getGroupTransitionProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2GroupTransitionSlotProps<ExternalProps>;
142
161
  /**
143
162
  * Resolver for the DragAndDropOverlay slot's props.
144
163
  * Warning: This slot is only useful when using the `RichTreeViewPro` component.
145
- * @param {ExternalProps} externalProps Additional props for the DragAndDropOverlay slot
146
- * @returns {UseTreeItem2DragAndDropOverlaySlotProps<ExternalProps>} Props that should be spread on the DragAndDropOverlay slot
164
+ * @param {ExternalProps} externalProps Additional props for the DragAndDropOverlay slot.
165
+ * @returns {UseTreeItem2DragAndDropOverlaySlotProps<ExternalProps>} Props that should be spread on the DragAndDropOverlay slot.
147
166
  */
148
167
  getDragAndDropOverlayProps: <ExternalProps extends Record<string, any> = {}>(externalProps?: ExternalProps) => UseTreeItem2DragAndDropOverlaySlotProps<ExternalProps>;
149
168
  /**
@@ -167,7 +186,8 @@ export type UseTreeItem2MinimalPlugins = readonly [
167
186
  UseTreeViewExpansionSignature,
168
187
  UseTreeViewItemsSignature,
169
188
  UseTreeViewFocusSignature,
170
- UseTreeViewKeyboardNavigationSignature
189
+ UseTreeViewKeyboardNavigationSignature,
190
+ UseTreeViewLabelSignature
171
191
  ];
172
192
  /**
173
193
  * Plugins that `useTreeItem2` can use if they are present, but are not required.