@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
@@ -18,6 +18,7 @@ import { getTreeItemUtilityClass } from '../TreeItem';
18
18
  import { TreeItem2Icon } from '../TreeItem2Icon';
19
19
  import { TreeItem2DragAndDropOverlay } from '../TreeItem2DragAndDropOverlay';
20
20
  import { TreeItem2Provider } from '../TreeItem2Provider';
21
+ import { TreeItem2LabelInput } from '../TreeItem2LabelInput';
21
22
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
23
  const useThemeProps = createUseThemeProps('MuiTreeItem2');
23
24
  export const TreeItem2Root = styled('li', {
@@ -104,7 +105,8 @@ export const TreeItem2Content = styled('div', {
104
105
  export const TreeItem2Label = styled('div', {
105
106
  name: 'MuiTreeItem2',
106
107
  slot: 'Label',
107
- overridesResolver: (props, styles) => styles.label
108
+ overridesResolver: (props, styles) => styles.label,
109
+ shouldForwardProp: prop => shouldForwardProp(prop) && prop !== 'editable'
108
110
  })(({
109
111
  theme
110
112
  }) => _extends({
@@ -113,8 +115,18 @@ export const TreeItem2Label = styled('div', {
113
115
  // prevent width + padding to overflow
114
116
  // fixes overflow - see https://github.com/mui/material-ui/issues/27372
115
117
  minWidth: 0,
116
- position: 'relative'
117
- }, theme.typography.body1));
118
+ position: 'relative',
119
+ overflow: 'hidden'
120
+ }, theme.typography.body1, {
121
+ variants: [{
122
+ props: ({
123
+ editable
124
+ }) => editable,
125
+ style: {
126
+ paddingLeft: '2px'
127
+ }
128
+ }]
129
+ }));
118
130
  export const TreeItem2IconContainer = styled('div', {
119
131
  name: 'MuiTreeItem2',
120
132
  slot: 'IconContainer',
@@ -172,6 +184,8 @@ const useUtilityClasses = ownerState => {
172
184
  root: ['root'],
173
185
  content: ['content'],
174
186
  expanded: ['expanded'],
187
+ editing: ['editing'],
188
+ editable: ['editable'],
175
189
  selected: ['selected'],
176
190
  focused: ['focused'],
177
191
  disabled: ['disabled'],
@@ -179,6 +193,7 @@ const useUtilityClasses = ownerState => {
179
193
  checkbox: ['checkbox'],
180
194
  label: ['label'],
181
195
  groupTransition: ['groupTransition'],
196
+ labelInput: ['labelInput'],
182
197
  dragAndDropOverlay: ['dragAndDropOverlay']
183
198
  };
184
199
  return composeClasses(slots, getTreeItemUtilityClass, classes);
@@ -215,6 +230,7 @@ export const TreeItem2 = /*#__PURE__*/React.forwardRef(function TreeItem2(inProp
215
230
  getCheckboxProps,
216
231
  getLabelProps,
217
232
  getGroupTransitionProps,
233
+ getLabelInputProps,
218
234
  getDragAndDropOverlayProps,
219
235
  status
220
236
  } = useTreeItem2({
@@ -244,7 +260,7 @@ export const TreeItem2 = /*#__PURE__*/React.forwardRef(function TreeItem2(inProp
244
260
  getSlotProps: getContentProps,
245
261
  externalSlotProps: slotProps.content,
246
262
  ownerState: {},
247
- className: clsx(classes.content, status.expanded && classes.expanded, status.selected && classes.selected, status.focused && classes.focused, status.disabled && classes.disabled)
263
+ className: clsx(classes.content, status.expanded && classes.expanded, status.selected && classes.selected, status.focused && classes.focused, status.disabled && classes.disabled, status.editing && classes.editing, status.editable && classes.editable)
248
264
  });
249
265
  const IconContainer = slots.iconContainer ?? TreeItem2IconContainer;
250
266
  const iconContainerProps = useSlotProps({
@@ -278,6 +294,14 @@ export const TreeItem2 = /*#__PURE__*/React.forwardRef(function TreeItem2(inProp
278
294
  ownerState: {},
279
295
  className: classes.groupTransition
280
296
  });
297
+ const LabelInput = slots.labelInput ?? TreeItem2LabelInput;
298
+ const labelInputProps = useSlotProps({
299
+ elementType: LabelInput,
300
+ getSlotProps: getLabelInputProps,
301
+ externalSlotProps: slotProps.labelInput,
302
+ ownerState: {},
303
+ className: classes.labelInput
304
+ });
281
305
  const DragAndDropOverlay = slots.dragAndDropOverlay ?? TreeItem2DragAndDropOverlay;
282
306
  const dragAndDropOverlayProps = useSlotProps({
283
307
  elementType: DragAndDropOverlay,
@@ -296,7 +320,7 @@ export const TreeItem2 = /*#__PURE__*/React.forwardRef(function TreeItem2(inProp
296
320
  slots: slots,
297
321
  slotProps: slotProps
298
322
  })
299
- })), /*#__PURE__*/_jsx(Checkbox, _extends({}, checkboxProps)), /*#__PURE__*/_jsx(Label, _extends({}, labelProps)), /*#__PURE__*/_jsx(DragAndDropOverlay, _extends({}, dragAndDropOverlayProps))]
323
+ })), /*#__PURE__*/_jsx(Checkbox, _extends({}, checkboxProps)), status.editing ? /*#__PURE__*/_jsx(LabelInput, _extends({}, labelInputProps)) : /*#__PURE__*/_jsx(Label, _extends({}, labelProps)), /*#__PURE__*/_jsx(DragAndDropOverlay, _extends({}, dragAndDropOverlayProps))]
300
324
  })), children && /*#__PURE__*/_jsx(TreeItem2GroupTransition, _extends({
301
325
  as: GroupTransition
302
326
  }, groupTransitionProps))]
@@ -36,6 +36,11 @@ export interface TreeItem2Slots extends TreeItem2IconSlots {
36
36
  * @default TreeItem2Label
37
37
  */
38
38
  label?: React.ElementType;
39
+ /**
40
+ * The component that renders the input to edit the label when the item is editable and is currently being edited.
41
+ * @default TreeItem2LabelInput
42
+ */
43
+ labelInput?: React.ElementType;
39
44
  /**
40
45
  * The component that renders the overlay when an item reordering is ongoing.
41
46
  * Warning: This slot is only useful when using the `RichTreeViewPro` component.
@@ -50,6 +55,7 @@ export interface TreeItem2SlotProps extends TreeItem2IconSlotProps {
50
55
  iconContainer?: SlotComponentProps<'div', {}, {}>;
51
56
  checkbox?: SlotComponentProps<'button', {}, {}>;
52
57
  label?: SlotComponentProps<'div', {}, {}>;
58
+ labelInput?: SlotComponentProps<'input', {}, {}>;
53
59
  dragAndDropOverlay?: SlotComponentProps<'div', {}, {}>;
54
60
  }
55
61
  export interface TreeItem2Props extends Omit<UseTreeItem2Parameters, 'rootRef'>, Omit<React.HTMLAttributes<HTMLLIElement>, 'onFocus'> {
@@ -35,9 +35,9 @@ const TreeItem2DragAndDropOverlayRoot = styled('div', {
35
35
  style: _extends({
36
36
  marginLeft: 'calc(var(--TreeView-indentMultiplier) * var(--TreeView-itemDepth))',
37
37
  borderTop: `1px solid ${alpha((theme.vars || theme).palette.grey[900], 0.6)}`
38
- }, theme.palette.mode === 'dark' && {
39
- borderTop: `1px solid ${alpha((theme.vars || theme).palette.grey[100], 0.6)}`
40
- })
38
+ }, theme.applyStyles('dark', {
39
+ borderTopColor: alpha((theme.vars || theme).palette.grey[100], 0.6)
40
+ }))
41
41
  }, {
42
42
  props: {
43
43
  action: 'reorder-below'
@@ -45,9 +45,9 @@ const TreeItem2DragAndDropOverlayRoot = styled('div', {
45
45
  style: _extends({
46
46
  marginLeft: 'calc(var(--TreeView-indentMultiplier) * var(--TreeView-itemDepth))',
47
47
  borderBottom: `1px solid ${alpha((theme.vars || theme).palette.grey[900], 0.6)}`
48
- }, theme.palette.mode === 'dark' && {
49
- borderBottom: `1px solid ${alpha((theme.vars || theme).palette.grey[100], 0.6)}`
50
- })
48
+ }, theme.applyStyles('dark', {
49
+ borderBottomColor: alpha((theme.vars || theme).palette.grey[100], 0.6)
50
+ }))
51
51
  }, {
52
52
  props: {
53
53
  action: 'move-to-parent'
@@ -55,9 +55,9 @@ const TreeItem2DragAndDropOverlayRoot = styled('div', {
55
55
  style: _extends({
56
56
  marginLeft: 'calc(var(--TreeView-indentMultiplier) * calc(var(--TreeView-itemDepth) - 1))',
57
57
  borderBottom: `1px solid ${alpha((theme.vars || theme).palette.grey[900], 0.6)}`
58
- }, theme.palette.mode === 'dark' && {
59
- borderBottom: `1px solid ${alpha((theme.vars || theme).palette.grey[900], 0.6)}`
60
- })
58
+ }, theme.applyStyles('dark', {
59
+ borderBottomColor: alpha((theme.vars || theme).palette.grey[900], 0.6)
60
+ }))
61
61
  }]
62
62
  }));
63
63
  function TreeItem2DragAndDropOverlay(props) {
@@ -59,6 +59,8 @@ process.env.NODE_ENV !== "production" ? TreeItem2Icon.propTypes = {
59
59
  slots: PropTypes.object,
60
60
  status: PropTypes.shape({
61
61
  disabled: PropTypes.bool.isRequired,
62
+ editable: PropTypes.bool.isRequired,
63
+ editing: PropTypes.bool.isRequired,
62
64
  expandable: PropTypes.bool.isRequired,
63
65
  expanded: PropTypes.bool.isRequired,
64
66
  focused: PropTypes.bool.isRequired,
@@ -0,0 +1,2 @@
1
+ declare const TreeItem2LabelInput: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, Pick<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, keyof import("react").ClassAttributes<HTMLInputElement> | keyof import("react").InputHTMLAttributes<HTMLInputElement>>, {}>;
2
+ export { TreeItem2LabelInput };
@@ -0,0 +1,20 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import { styled } from '../internals/zero-styled';
3
+ const TreeItem2LabelInput = styled('input', {
4
+ name: 'MuiTreeItem2',
5
+ slot: 'LabelInput',
6
+ overridesResolver: (props, styles) => styles.labelInput
7
+ })(({
8
+ theme
9
+ }) => _extends({}, theme.typography.body1, {
10
+ width: '100%',
11
+ backgroundColor: theme.palette.background.paper,
12
+ borderRadius: theme.shape.borderRadius,
13
+ border: 'none',
14
+ padding: '0 2px',
15
+ boxSizing: 'border-box',
16
+ '&:focus': {
17
+ outline: `1px solid ${theme.palette.primary.main}`
18
+ }
19
+ }));
20
+ export { TreeItem2LabelInput };
@@ -0,0 +1,8 @@
1
+ export interface TreeItem2LabelInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
2
+ value?: string;
3
+ onChange?: React.ChangeEventHandler<HTMLInputElement>;
4
+ /**
5
+ * Used to determine if the target of keydown or blur events is the input and prevent the event from propagating to the root.
6
+ */
7
+ 'data-element'?: 'labelInput';
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { TreeItem2LabelInput } from './TreeItem2LabelInput';
2
+ export type { TreeItem2LabelInputProps } from './TreeItem2LabelInput.types';
@@ -0,0 +1 @@
1
+ export { TreeItem2LabelInput } from './TreeItem2LabelInput';
@@ -0,0 +1,6 @@
1
+ {
2
+ "sideEffects": false,
3
+ "module": "./index.js",
4
+ "main": "../node/TreeItem2LabelInput/index.js",
5
+ "types": "./index.d.ts"
6
+ }
@@ -1,9 +1,13 @@
1
1
  import * as React from 'react';
2
+ import { UseTreeViewLabelSignature } from '../../internals/plugins/useTreeViewLabel';
2
3
  import type { UseTreeItem2Status } from '../../useTreeItem2';
3
4
  interface UseTreeItem2Interactions {
4
5
  handleExpansion: (event: React.MouseEvent) => void;
5
6
  handleSelection: (event: React.MouseEvent) => void;
6
7
  handleCheckboxSelection: (event: React.ChangeEvent<HTMLInputElement>) => void;
8
+ toggleItemEditing: () => void;
9
+ handleSaveItemLabel: (event: React.SyntheticEvent, label: string) => void;
10
+ handleCancelItemLabelEditing: (event: React.SyntheticEvent) => void;
7
11
  }
8
12
  interface UseTreeItem2UtilsReturnValue {
9
13
  interactions: UseTreeItem2Interactions;
@@ -12,7 +16,7 @@ interface UseTreeItem2UtilsReturnValue {
12
16
  /**
13
17
  * Plugins that `useTreeItem2Utils` can use if they are present, but are not required.
14
18
  */
15
- export type UseTreeItem2UtilsOptionalPlugins = readonly [];
19
+ export type UseTreeItem2UtilsOptionalPlugins = readonly [UseTreeViewLabelSignature];
16
20
  export declare const useTreeItem2Utils: ({ itemId, children, }: {
17
21
  itemId: string;
18
22
  children: React.ReactNode;
@@ -1,4 +1,6 @@
1
1
  import { useTreeViewContext } from '../../internals/TreeViewProvider';
2
+ import { useTreeViewLabel } from '../../internals/plugins/useTreeViewLabel';
3
+ import { hasPlugin } from '../../internals/utils/plugins';
2
4
  const isItemExpandable = reactChildren => {
3
5
  if (Array.isArray(reactChildren)) {
4
6
  return reactChildren.length > 0 && reactChildren.some(isItemExpandable);
@@ -29,7 +31,9 @@ export const useTreeItem2Utils = ({
29
31
  expanded: instance.isItemExpanded(itemId),
30
32
  focused: instance.isItemFocused(itemId),
31
33
  selected: instance.isItemSelected(itemId),
32
- disabled: instance.isItemDisabled(itemId)
34
+ disabled: instance.isItemDisabled(itemId),
35
+ editing: instance?.isItemBeingEdited ? instance?.isItemBeingEdited(itemId) : false,
36
+ editable: instance.isItemEditable ? instance.isItemEditable(itemId) : false
33
37
  };
34
38
  const handleExpansion = event => {
35
39
  if (status.disabled) {
@@ -84,10 +88,49 @@ export const useTreeItem2Utils = ({
84
88
  });
85
89
  }
86
90
  };
91
+ const toggleItemEditing = () => {
92
+ if (!hasPlugin(instance, useTreeViewLabel)) {
93
+ return;
94
+ }
95
+ if (instance.isItemEditable(itemId)) {
96
+ if (instance.isItemBeingEdited(itemId)) {
97
+ instance.setEditedItemId(null);
98
+ } else {
99
+ instance.setEditedItemId(itemId);
100
+ }
101
+ }
102
+ };
103
+ const handleSaveItemLabel = (event, label) => {
104
+ if (!hasPlugin(instance, useTreeViewLabel)) {
105
+ return;
106
+ }
107
+
108
+ // As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred
109
+ // The `onBlur` event is triggered, which calls `handleSaveItemLabel` again.
110
+ // To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel`
111
+ // using `instance.isItemBeingEditedRef` instead of `instance.isItemBeingEdited` since the state is not yet updated in this point
112
+ if (instance.isItemBeingEditedRef(itemId)) {
113
+ instance.updateItemLabel(itemId, label);
114
+ toggleItemEditing();
115
+ instance.focusItem(event, itemId);
116
+ }
117
+ };
118
+ const handleCancelItemLabelEditing = event => {
119
+ if (!hasPlugin(instance, useTreeViewLabel)) {
120
+ return;
121
+ }
122
+ if (instance.isItemBeingEditedRef(itemId)) {
123
+ toggleItemEditing();
124
+ instance.focusItem(event, itemId);
125
+ }
126
+ };
87
127
  const interactions = {
88
128
  handleExpansion,
89
129
  handleSelection,
90
- handleCheckboxSelection
130
+ handleCheckboxSelection,
131
+ toggleItemEditing,
132
+ handleSaveItemLabel,
133
+ handleCancelItemLabelEditing
91
134
  };
92
135
  return {
93
136
  interactions,
@@ -3,4 +3,4 @@ import { TreeViewAnyPluginSignature, TreeViewPublicAPI } from '../internals/mode
3
3
  /**
4
4
  * Hook that instantiates a [[TreeViewApiRef]].
5
5
  */
6
- export declare const useTreeViewApiRef: <TSignatures extends readonly TreeViewAnyPluginSignature[] = readonly [import("../internals").UseTreeViewItemsSignature, import("../internals").UseTreeViewExpansionSignature, import("../internals").UseTreeViewSelectionSignature, import("../internals").UseTreeViewFocusSignature, import("../internals").UseTreeViewKeyboardNavigationSignature, import("../internals").UseTreeViewIconsSignature]>() => React.MutableRefObject<TreeViewPublicAPI<TSignatures> | undefined>;
6
+ export declare const useTreeViewApiRef: <TSignatures extends readonly TreeViewAnyPluginSignature[] = readonly [import("../internals").UseTreeViewItemsSignature, import("../internals").UseTreeViewExpansionSignature, import("../internals").UseTreeViewSelectionSignature, import("../internals").UseTreeViewFocusSignature, import("../internals").UseTreeViewKeyboardNavigationSignature, import("../internals").UseTreeViewIconsSignature, import("../internals").UseTreeViewLabelSignature]>() => React.MutableRefObject<TreeViewPublicAPI<TSignatures> | undefined>;
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-tree-view v7.12.1
2
+ * @mui/x-tree-view v7.14.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -15,6 +15,8 @@ export { useTreeViewIcons } from './plugins/useTreeViewIcons';
15
15
  export type { UseTreeViewIconsSignature, UseTreeViewIconsParameters, } from './plugins/useTreeViewIcons';
16
16
  export { useTreeViewItems, buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID, } from './plugins/useTreeViewItems';
17
17
  export type { UseTreeViewItemsSignature, UseTreeViewItemsParameters, UseTreeViewItemsState, } from './plugins/useTreeViewItems';
18
+ export { useTreeViewLabel } from './plugins/useTreeViewLabel';
19
+ export type { UseTreeViewLabelSignature, UseTreeViewLabelParameters, } from './plugins/useTreeViewLabel';
18
20
  export { useTreeViewJSXItems } from './plugins/useTreeViewJSXItems';
19
21
  export type { UseTreeViewJSXItemsSignature, UseTreeViewJSXItemsParameters, } from './plugins/useTreeViewJSXItems';
20
22
  export { isTargetInDescendants } from './utils/tree';
@@ -11,6 +11,7 @@ export { useTreeViewFocus } from './plugins/useTreeViewFocus';
11
11
  export { useTreeViewKeyboardNavigation } from './plugins/useTreeViewKeyboardNavigation';
12
12
  export { useTreeViewIcons } from './plugins/useTreeViewIcons';
13
13
  export { useTreeViewItems, buildSiblingIndexes, TREE_VIEW_ROOT_PARENT_ID } from './plugins/useTreeViewItems';
14
+ export { useTreeViewLabel } from './plugins/useTreeViewLabel';
14
15
  export { useTreeViewJSXItems } from './plugins/useTreeViewJSXItems';
15
16
  export { isTargetInDescendants } from './utils/tree';
16
17
  export { warnOnce } from './utils/warning';
@@ -1,6 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { EventHandlers } from '@mui/utils';
3
- import type { UseTreeItem2ContentSlotOwnProps, UseTreeItem2DragAndDropOverlaySlotOwnProps, UseTreeItem2RootSlotOwnProps } from '../../useTreeItem2';
3
+ import type { UseTreeItem2ContentSlotOwnProps, UseTreeItem2DragAndDropOverlaySlotOwnProps, UseTreeItem2LabelInputSlotOwnProps, UseTreeItem2RootSlotOwnProps } from '../../useTreeItem2';
4
4
  export interface TreeViewItemPluginSlotPropsEnhancerParams {
5
5
  rootRefObject: React.MutableRefObject<HTMLLIElement | null>;
6
6
  contentRefObject: React.MutableRefObject<HTMLDivElement | null>;
@@ -11,6 +11,7 @@ export interface TreeViewItemPluginSlotPropsEnhancers {
11
11
  root?: TreeViewItemPluginSlotPropsEnhancer<UseTreeItem2RootSlotOwnProps>;
12
12
  content?: TreeViewItemPluginSlotPropsEnhancer<UseTreeItem2ContentSlotOwnProps>;
13
13
  dragAndDropOverlay?: TreeViewItemPluginSlotPropsEnhancer<UseTreeItem2DragAndDropOverlaySlotOwnProps>;
14
+ labelInput?: TreeViewItemPluginSlotPropsEnhancer<UseTreeItem2LabelInputSlotOwnProps>;
14
15
  }
15
16
  export interface TreeViewItemPluginResponse {
16
17
  /**
@@ -57,8 +57,11 @@ export const useTreeViewExpansion = ({
57
57
  if (params.expansionTrigger) {
58
58
  return params.expansionTrigger;
59
59
  }
60
+ if (instance.isTreeViewEditable) {
61
+ return 'iconContainer';
62
+ }
60
63
  return 'content';
61
- }, [params.expansionTrigger]);
64
+ }, [params.expansionTrigger, instance.isTreeViewEditable]);
62
65
  return {
63
66
  publicAPI: {
64
67
  setItemExpansion
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
  import { DefaultizedProps, TreeViewPluginSignature } from '../../models';
3
3
  import { UseTreeViewItemsSignature } from '../useTreeViewItems';
4
4
  import { TreeViewItemId } from '../../../models';
5
+ import { UseTreeViewLabelSignature } from '../useTreeViewLabel';
5
6
  export interface UseTreeViewExpansionPublicAPI {
6
7
  /**
7
8
  * Change the expansion status of a given item.
@@ -83,5 +84,6 @@ export type UseTreeViewExpansionSignature = TreeViewPluginSignature<{
83
84
  modelNames: 'expandedItems';
84
85
  contextValue: UseTreeViewExpansionContextValue;
85
86
  dependencies: [UseTreeViewItemsSignature];
87
+ optionalDependencies: [UseTreeViewLabelSignature];
86
88
  }>;
87
89
  export {};
@@ -2,6 +2,8 @@ import * as React from 'react';
2
2
  import { useRtl } from '@mui/system/RtlProvider';
3
3
  import useEventCallback from '@mui/utils/useEventCallback';
4
4
  import { getFirstNavigableItem, getLastNavigableItem, getNextNavigableItem, getPreviousNavigableItem, isTargetInDescendants } from '../../utils/tree';
5
+ import { hasPlugin } from '../../utils/plugins';
6
+ import { useTreeViewLabel } from '../useTreeViewLabel';
5
7
  function isPrintableCharacter(string) {
6
8
  return !!string && string.length === 1 && !!string.match(/\S/);
7
9
  }
@@ -89,7 +91,9 @@ export const useTreeViewKeyboardNavigation = ({
89
91
  // If the focused item has no children, we select it.
90
92
  case key === 'Enter':
91
93
  {
92
- if (canToggleItemExpansion(itemId)) {
94
+ if (hasPlugin(instance, useTreeViewLabel) && instance.isItemEditable(itemId) && !instance.isItemBeingEdited(itemId)) {
95
+ instance.setEditedItemId(itemId);
96
+ } else if (canToggleItemExpansion(itemId)) {
93
97
  instance.toggleItemExpansion(event, itemId);
94
98
  event.preventDefault();
95
99
  } else if (canToggleItemSelection(itemId)) {
@@ -5,6 +5,7 @@ import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
5
5
  import { UseTreeViewFocusSignature } from '../useTreeViewFocus';
6
6
  import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
7
7
  import { TreeViewItemId } from '../../../models';
8
+ import { UseTreeViewLabelSignature } from '../useTreeViewLabel';
8
9
  export interface UseTreeViewKeyboardNavigationInstance {
9
10
  /**
10
11
  * Updates the `firstCharMap` to add/remove the first character of some item's labels.
@@ -29,6 +30,7 @@ export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
29
30
  UseTreeViewFocusSignature,
30
31
  UseTreeViewExpansionSignature
31
32
  ];
33
+ optionalDependencies: [UseTreeViewLabelSignature];
32
34
  }>;
33
35
  export type TreeViewFirstCharMap = {
34
36
  [itemId: string]: string;
@@ -0,0 +1,2 @@
1
+ export { useTreeViewLabel } from './useTreeViewLabel';
2
+ export type { UseTreeViewLabelSignature, UseTreeViewLabelParameters, } from './useTreeViewLabel.types';
@@ -0,0 +1 @@
1
+ export { useTreeViewLabel } from './useTreeViewLabel';
@@ -0,0 +1,3 @@
1
+ import { TreeViewPlugin } from '../../models';
2
+ import { UseTreeViewLabelSignature } from './useTreeViewLabel.types';
3
+ export declare const useTreeViewLabel: TreeViewPlugin<UseTreeViewLabelSignature>;
@@ -0,0 +1,3 @@
1
+ import { TreeViewItemPlugin } from '../../models';
2
+ export declare const isAndroid: () => boolean;
3
+ export declare const useTreeViewLabelItemPlugin: TreeViewItemPlugin<any>;
@@ -0,0 +1,44 @@
1
+ import * as React from 'react';
2
+ import { useTreeViewContext } from '../../TreeViewProvider';
3
+ export const isAndroid = () => navigator.userAgent.toLowerCase().includes('android');
4
+ export const useTreeViewLabelItemPlugin = ({
5
+ props
6
+ }) => {
7
+ const {
8
+ instance
9
+ } = useTreeViewContext();
10
+ const {
11
+ label,
12
+ itemId
13
+ } = props;
14
+ const [labelInputValue, setLabelInputValue] = React.useState(label);
15
+ const isItemBeingEdited = instance.isItemBeingEdited(itemId);
16
+ React.useEffect(() => {
17
+ if (!isItemBeingEdited) {
18
+ setLabelInputValue(label);
19
+ }
20
+ }, [isItemBeingEdited, label]);
21
+ return {
22
+ propsEnhancers: {
23
+ labelInput: ({
24
+ externalEventHandlers
25
+ }) => {
26
+ const editable = instance.isItemEditable(itemId);
27
+ if (!editable) {
28
+ return {};
29
+ }
30
+ const handleInputChange = event => {
31
+ externalEventHandlers.onChange?.(event);
32
+ setLabelInputValue(event.target.value);
33
+ };
34
+ return {
35
+ value: labelInputValue ?? '',
36
+ 'data-element': 'labelInput',
37
+ onChange: handleInputChange,
38
+ autoFocus: true,
39
+ type: 'text'
40
+ };
41
+ }
42
+ }
43
+ };
44
+ };
@@ -0,0 +1,81 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import * as React from 'react';
3
+ import { warnOnce } from '../../utils/warning';
4
+ import { useTreeViewLabelItemPlugin } from './useTreeViewLabel.itemPlugin';
5
+ export const useTreeViewLabel = ({
6
+ instance,
7
+ state,
8
+ setState,
9
+ params,
10
+ experimentalFeatures
11
+ }) => {
12
+ if (process.env.NODE_ENV !== 'production') {
13
+ if (params.isItemEditable && !experimentalFeatures?.labelEditing) {
14
+ warnOnce(['MUI X: The label editing feature requires the `labelEditing` experimental feature to be enabled.', 'You can do it by passing `experimentalFeatures={{ labelEditing: true}}` to the `RichTreeViewPro` component.', 'Check the documentation for more details: https://mui.com/x/react-tree-view/rich-tree-view/editing/']);
15
+ }
16
+ }
17
+ const editedItemRef = React.useRef(state.editedItemId);
18
+ const isItemBeingEditedRef = itemId => editedItemRef.current === itemId;
19
+ const setEditedItemId = editedItemId => {
20
+ setState(prevState => _extends({}, prevState, {
21
+ editedItemId
22
+ }));
23
+ editedItemRef.current = editedItemId;
24
+ };
25
+ const isItemBeingEdited = itemId => itemId === state.editedItemId;
26
+ const isTreeViewEditable = Boolean(params.isItemEditable) && !!experimentalFeatures.labelEditing;
27
+ const isItemEditable = itemId => {
28
+ if (itemId == null || !isTreeViewEditable) {
29
+ return false;
30
+ }
31
+ const item = instance.getItem(itemId);
32
+ if (!item) {
33
+ return false;
34
+ }
35
+ return typeof params.isItemEditable === 'function' ? params.isItemEditable(item) : Boolean(params.isItemEditable);
36
+ };
37
+ const updateItemLabel = (itemId, label) => {
38
+ if (!label) {
39
+ throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'The label of an item cannot be empty.', itemId].join('\n'));
40
+ }
41
+ setState(prevState => {
42
+ const item = prevState.items.itemMetaMap[itemId];
43
+ if (item.label !== label) {
44
+ return _extends({}, prevState, {
45
+ items: _extends({}, prevState.items, {
46
+ itemMetaMap: _extends({}, prevState.items.itemMetaMap, {
47
+ [itemId]: _extends({}, item, {
48
+ label
49
+ })
50
+ })
51
+ })
52
+ });
53
+ }
54
+ return prevState;
55
+ });
56
+ if (params.onItemLabelChange) {
57
+ params.onItemLabelChange(itemId, label);
58
+ }
59
+ };
60
+ return {
61
+ instance: {
62
+ setEditedItemId,
63
+ isItemBeingEdited,
64
+ updateItemLabel,
65
+ isItemEditable,
66
+ isTreeViewEditable,
67
+ isItemBeingEditedRef
68
+ },
69
+ publicAPI: {
70
+ updateItemLabel
71
+ }
72
+ };
73
+ };
74
+ useTreeViewLabel.itemPlugin = useTreeViewLabelItemPlugin;
75
+ useTreeViewLabel.getInitialState = () => ({
76
+ editedItemId: null
77
+ });
78
+ useTreeViewLabel.params = {
79
+ onItemLabelChange: true,
80
+ isItemEditable: true
81
+ };
@@ -0,0 +1,75 @@
1
+ import { TreeViewPluginSignature } from '../../models';
2
+ import { TreeViewItemId } from '../../../models';
3
+ import { UseTreeViewItemsSignature } from '../useTreeViewItems';
4
+ import { TreeItem2LabelInputProps } from '../../../TreeItem2LabelInput';
5
+ export interface UseTreeViewLabelPublicAPI {
6
+ /**
7
+ * Used to update the label of an item.
8
+ * @param {TreeViewItemId} itemId The id of the item to update the label of.
9
+ * @param {string} newLabel The new label of the item.
10
+ */
11
+ updateItemLabel: (itemId: TreeViewItemId, newLabel: string) => void;
12
+ }
13
+ export interface UseTreeViewLabelInstance extends UseTreeViewLabelPublicAPI {
14
+ /**
15
+ * Updates which item is currently being edited.
16
+ * @param {TreeViewItemId} itemId The id of the item that is currently being edited.
17
+ * @returns {void}.
18
+ */
19
+ setEditedItemId: (itemId: TreeViewItemId | null) => void;
20
+ /**
21
+ * Checks if an item is being edited or not.
22
+ * @param {TreeViewItemId} itemId The id of the item to check.
23
+ * @returns {boolean}.
24
+ */
25
+ isItemBeingEdited: (itemId: TreeViewItemId) => boolean;
26
+ /**
27
+ * Checks if an item is being edited or not.
28
+ * Purely internal use, used to avoid unnecessarily calling `updateItemLabel` twice.
29
+ * @param {TreeViewItemId} itemId The id of the item to check.
30
+ * @returns {boolean}.
31
+ */
32
+ isItemBeingEditedRef: (itemId: TreeViewItemId) => boolean;
33
+ /**
34
+ * Determines if a given item is editable.
35
+ * @param {TreeViewItemId} itemId The id of the item to check.
36
+ * @returns {boolean} `true` if the item is editable.
37
+ */
38
+ isItemEditable: (itemId: TreeViewItemId) => boolean;
39
+ /**
40
+ * Set to `true` if the tree view is editable.
41
+ */
42
+ isTreeViewEditable: boolean;
43
+ }
44
+ export interface UseTreeViewLabelParameters<R extends {}> {
45
+ /**
46
+ * Callback fired when the label of an item changes.
47
+ * @param {TreeViewItemId} itemId The id of the item that was edited.
48
+ * @param {string} newLabel The new label of the items.
49
+ */
50
+ onItemLabelChange?: (itemId: TreeViewItemId, newLabel: string) => void;
51
+ /**
52
+ * Determines if a given item is editable or not.
53
+ * Make sure to also enable the `labelEditing` experimental feature:
54
+ * `<RichTreeViewPro experimentalFeatures={{ labelEditing: true }} />`.
55
+ * By default, the items are not editable.
56
+ * @template R
57
+ * @param {R} item The item to check.
58
+ * @returns {boolean} `true` if the item is editable.
59
+ */
60
+ isItemEditable?: boolean | ((item: R) => boolean);
61
+ }
62
+ export interface UseTreeViewLabelState {
63
+ editedItemId: string | null;
64
+ }
65
+ export type UseTreeViewLabelSignature = TreeViewPluginSignature<{
66
+ params: UseTreeViewLabelParameters<any>;
67
+ defaultizedParams: UseTreeViewLabelParameters<any>;
68
+ publicAPI: UseTreeViewLabelPublicAPI;
69
+ instance: UseTreeViewLabelInstance;
70
+ state: UseTreeViewLabelState;
71
+ experimentalFeatures: 'labelEditing';
72
+ dependencies: [UseTreeViewItemsSignature];
73
+ }>;
74
+ export interface UseTreeItem2LabelInputSlotPropsFromItemsReordering extends TreeItem2LabelInputProps {
75
+ }