@mui/x-tree-view 7.12.1 → 7.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/CHANGELOG.md +81 -0
  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/TreeItem2Icon/TreeItem2Icon.js +2 -0
  16. package/TreeItem2LabelInput/TreeItem2LabelInput.d.ts +2 -0
  17. package/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
  18. package/TreeItem2LabelInput/TreeItem2LabelInput.types.d.ts +8 -0
  19. package/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
  20. package/TreeItem2LabelInput/index.d.ts +2 -0
  21. package/TreeItem2LabelInput/index.js +1 -0
  22. package/TreeItem2LabelInput/package.json +6 -0
  23. package/hooks/useTreeItem2Utils/useTreeItem2Utils.d.ts +5 -1
  24. package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  25. package/hooks/useTreeViewApiRef.d.ts +1 -1
  26. package/index.js +1 -1
  27. package/internals/index.d.ts +2 -0
  28. package/internals/index.js +1 -0
  29. package/internals/models/itemPlugin.d.ts +2 -1
  30. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  31. package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +2 -0
  32. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  33. package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +2 -0
  34. package/internals/plugins/useTreeViewLabel/index.d.ts +2 -0
  35. package/internals/plugins/useTreeViewLabel/index.js +1 -0
  36. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.d.ts +3 -0
  37. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.d.ts +3 -0
  38. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
  39. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
  40. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +75 -0
  41. package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
  42. package/modern/RichTreeView/RichTreeView.js +20 -2
  43. package/modern/RichTreeView/RichTreeView.plugins.js +2 -1
  44. package/modern/TreeItem/TreeItem.js +24 -0
  45. package/modern/TreeItem/TreeItemContent.js +48 -8
  46. package/modern/TreeItem/treeItemClasses.js +1 -1
  47. package/modern/TreeItem/useTreeItemState.js +46 -1
  48. package/modern/TreeItem2/TreeItem2.js +29 -5
  49. package/modern/TreeItem2Icon/TreeItem2Icon.js +2 -0
  50. package/modern/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
  51. package/modern/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
  52. package/modern/TreeItem2LabelInput/index.js +1 -0
  53. package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  54. package/modern/index.js +1 -1
  55. package/modern/internals/index.js +1 -0
  56. package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  57. package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  58. package/modern/internals/plugins/useTreeViewLabel/index.js +1 -0
  59. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
  60. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
  61. package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
  62. package/modern/useTreeItem2/useTreeItem2.js +65 -4
  63. package/node/RichTreeView/RichTreeView.js +20 -2
  64. package/node/RichTreeView/RichTreeView.plugins.js +2 -1
  65. package/node/TreeItem/TreeItem.js +24 -0
  66. package/node/TreeItem/TreeItemContent.js +48 -8
  67. package/node/TreeItem/treeItemClasses.js +1 -1
  68. package/node/TreeItem/useTreeItemState.js +46 -1
  69. package/node/TreeItem2/TreeItem2.js +29 -5
  70. package/node/TreeItem2Icon/TreeItem2Icon.js +2 -0
  71. package/node/TreeItem2LabelInput/TreeItem2LabelInput.js +26 -0
  72. package/node/TreeItem2LabelInput/TreeItem2LabelInput.types.js +5 -0
  73. package/node/TreeItem2LabelInput/index.js +12 -0
  74. package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
  75. package/node/index.js +1 -1
  76. package/node/internals/index.js +7 -0
  77. package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
  78. package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
  79. package/node/internals/plugins/useTreeViewLabel/index.js +12 -0
  80. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +54 -0
  81. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +91 -0
  82. package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +5 -0
  83. package/node/useTreeItem2/useTreeItem2.js +65 -4
  84. package/package.json +2 -2
  85. package/useTreeItem2/index.d.ts +1 -1
  86. package/useTreeItem2/useTreeItem2.js +65 -4
  87. package/useTreeItem2/useTreeItem2.types.d.ts +35 -15
package/CHANGELOG.md CHANGED
@@ -3,6 +3,87 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 7.13.0
7
+
8
+ _Aug 16, 2024_
9
+
10
+ We'd like to offer a big thanks to the 12 contributors who made this release possible. Here are some highlights ✨:
11
+
12
+ - 💫 Allow to [edit the label](https://mui.com/x/react-tree-view/rich-tree-view/editing/) of Tree View's items.
13
+
14
+ <img width="344" src="https://github.com/user-attachments/assets/1a6cf765-2dc8-4906-bd93-139086eed148" alt="Item label editing" />
15
+
16
+ - 🔧 Improve rows accessibility on the Data Grid features "Tree Data" and "Row Grouping". Certain "Row Grouping" accessibility updates will only be applied if experimental feature flag is enabled. See the [documentation](https://mui.com/x/react-data-grid/row-grouping/#accessibility-changes-in-v8) for more information.
17
+ - 🌍 Improve Vietnamese (vi-VN) locale on the Data Grid
18
+ - 🐞 Bugfixes
19
+
20
+ <!--/ HIGHLIGHT_ABOVE_SEPARATOR /-->
21
+
22
+ ### Data Grid
23
+
24
+ #### `@mui/x-data-grid@7.13.0`
25
+
26
+ - [DataGrid] Fix CSV export for `null` and `undefined` values (#14166) @k-rajat19
27
+ - [DataGrid] Fix error logged during skeleton loading with nested data grid (#14186) @KenanYusuf
28
+ - [DataGrid] Remove needless check in `useGridStateInitialization` (#14181) @k-rajat19
29
+ - [DataGrid] Add recipe for persisting filters in local storage (#14208) @cherniavskii
30
+ - [l10n] Improve Vietnamese (vi-VN) locale (#14216) @hungnd-casso
31
+
32
+ #### `@mui/x-data-grid-pro@7.13.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
33
+
34
+ Same changes as in `@mui/x-data-grid@7.13.0`, plus:
35
+
36
+ - [DataGridPro] Fix Tree Data and Row Grouping rows accessibility (#13623) @arminmeh
37
+
38
+ #### `@mui/x-data-grid-premium@7.13.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
39
+
40
+ Same changes as in `@mui/x-data-grid-pro@7.13.0`.
41
+
42
+ ### Date and Time Pickers
43
+
44
+ #### `@mui/x-date-pickers@7.13.0`
45
+
46
+ - [pickers] Fix date and time merging to retain milliseconds (#14173) @LukasTy
47
+
48
+ #### `@mui/x-date-pickers-pro@7.13.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
49
+
50
+ Same changes as in `@mui/x-date-pickers@7.13.0`.
51
+
52
+ ### Charts
53
+
54
+ #### `@mui/x-charts@7.13.0`
55
+
56
+ - [charts] Add `baseline` property to the `LineChart` `series` (#14153) @JCQuintas
57
+ - [charts] Fix issue where tooltip would disappear on mouse click (#14187) @alexfauquette
58
+ - [charts] Rename `CartesianContextProvider` to `CartesianProvider` (#14102) @JCQuintas
59
+ - [charts] Support axis with the same value for all data points (#14191) @alexfauquette
60
+
61
+ #### `@mui/x-date-charts-pro@7.0.0-alpha.1` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
62
+
63
+ Same changes as in `@mui/x-charts@7.13.0`.
64
+
65
+ ### Tree View
66
+
67
+ #### `@mui/x-tree-view@7.13.0`
68
+
69
+ - [TreeView] Add label editing feature (#13388) @noraleonte
70
+ - [TreeView] Fix the parameters passed for the `canMoveItemToNewPosition` prop (#14176) @flaviendelangle
71
+
72
+ ### Docs
73
+
74
+ - [docs] Extract dataset in the Line chart docs (#14034) @alexfauquette
75
+ - [docs] Remove redundant encoding in the mock data source server (#14185) @MBilalShafi
76
+ - [docs] Use Netflix financial results to document bar charts (#13991) @alexfauquette
77
+ - [docs] Remove relience of abbreviations (#14226) @oliviertassinari
78
+
79
+ ### Core
80
+
81
+ - [core] Bump monorepo (#14141) @Janpot
82
+ - [core] Fix ESLint issue (#14207) @LukasTy
83
+ - [core] Fix Netlify build cache issue (#14182) @cherniavskii
84
+ - [code-infra] Refactor Netlify `cache-docs` plugin setup (#14105) @LukasTy
85
+ - [internals] Move utils needed for tree view virtualization to shared package (#14202) @flaviendelangle
86
+
6
87
  ## 7.12.1
7
88
 
8
89
  _Aug 8, 2024_
@@ -140,7 +140,8 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
140
140
  getItemOrderedChildrenIds: PropTypes.func.isRequired,
141
141
  getItemTree: PropTypes.func.isRequired,
142
142
  selectItem: PropTypes.func.isRequired,
143
- setItemExpansion: PropTypes.func.isRequired
143
+ setItemExpansion: PropTypes.func.isRequired,
144
+ updateItemLabel: PropTypes.func.isRequired
144
145
  })
145
146
  }),
146
147
  /**
@@ -191,7 +192,8 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
191
192
  * the feature will be fully disabled and any property / method call will not have any effect.
192
193
  */
193
194
  experimentalFeatures: PropTypes.shape({
194
- indentationAtItemLevel: PropTypes.bool
195
+ indentationAtItemLevel: PropTypes.bool,
196
+ labelEditing: PropTypes.bool
195
197
  }),
196
198
  /**
197
199
  * Used to determine the id of a given item.
@@ -223,6 +225,16 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
223
225
  * @returns {boolean} `true` if the item should be disabled.
224
226
  */
225
227
  isItemDisabled: PropTypes.func,
228
+ /**
229
+ * Determines if a given item is editable or not.
230
+ * Make sure to also enable the `labelEditing` experimental feature:
231
+ * `<RichTreeViewPro experimentalFeatures={{ labelEditing: true }} />`.
232
+ * By default, the items are not editable.
233
+ * @template R
234
+ * @param {R} item The item to check.
235
+ * @returns {boolean} `true` if the item is editable.
236
+ */
237
+ isItemEditable: PropTypes.oneOfType([PropTypes.func, PropTypes.bool]),
226
238
  /**
227
239
  * Horizontal indentation between an item and its children.
228
240
  * Examples: 24, "24px", "2rem", "2em".
@@ -260,6 +272,12 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
260
272
  * @param {string} itemId The id of the focused item.
261
273
  */
262
274
  onItemFocus: PropTypes.func,
275
+ /**
276
+ * Callback fired when the label of an item changes.
277
+ * @param {TreeViewItemId} itemId The id of the item that was edited.
278
+ * @param {string} newLabel The new label of the items.
279
+ */
280
+ onItemLabelChange: PropTypes.func,
263
281
  /**
264
282
  * Callback fired when a tree item is selected or deselected.
265
283
  * @param {React.SyntheticEvent} event The DOM event that triggered the change.
@@ -5,9 +5,10 @@ import { UseTreeViewSelectionParameters } from '../internals/plugins/useTreeView
5
5
  import { UseTreeViewFocusParameters } from '../internals/plugins/useTreeViewFocus';
6
6
  import { UseTreeViewIconsParameters } from '../internals/plugins/useTreeViewIcons';
7
7
  import { ConvertPluginsIntoSignatures, MergeSignaturesProperty } from '../internals/models';
8
- export declare const RICH_TREE_VIEW_PLUGINS: readonly [import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewItems").UseTreeViewItemsSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewExpansion").UseTreeViewExpansionSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewSelection").UseTreeViewSelectionSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewFocus").UseTreeViewFocusSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewKeyboardNavigation").UseTreeViewKeyboardNavigationSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewIcons").UseTreeViewIconsSignature>];
8
+ import { UseTreeViewLabelParameters } from '../internals/plugins/useTreeViewLabel';
9
+ export declare const RICH_TREE_VIEW_PLUGINS: readonly [import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewItems").UseTreeViewItemsSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewExpansion").UseTreeViewExpansionSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewSelection").UseTreeViewSelectionSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewFocus").UseTreeViewFocusSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewKeyboardNavigation").UseTreeViewKeyboardNavigationSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewIcons").UseTreeViewIconsSignature>, import("../internals/models").TreeViewPlugin<import("../internals/plugins/useTreeViewLabel").UseTreeViewLabelSignature>];
9
10
  export type RichTreeViewPluginSignatures = ConvertPluginsIntoSignatures<typeof RICH_TREE_VIEW_PLUGINS>;
10
11
  export type RichTreeViewPluginSlots = MergeSignaturesProperty<RichTreeViewPluginSignatures, 'slots'>;
11
12
  export type RichTreeViewPluginSlotProps = MergeSignaturesProperty<RichTreeViewPluginSignatures, 'slotProps'>;
12
- export interface RichTreeViewPluginParameters<R extends {}, Multiple extends boolean | undefined> extends TreeViewCorePluginParameters, UseTreeViewItemsParameters<R>, UseTreeViewExpansionParameters, UseTreeViewFocusParameters, UseTreeViewSelectionParameters<Multiple>, UseTreeViewIconsParameters {
13
+ export interface RichTreeViewPluginParameters<R extends {}, Multiple extends boolean | undefined> extends TreeViewCorePluginParameters, UseTreeViewItemsParameters<R>, UseTreeViewExpansionParameters, UseTreeViewFocusParameters, UseTreeViewSelectionParameters<Multiple>, UseTreeViewIconsParameters, UseTreeViewLabelParameters<R> {
13
14
  }
@@ -4,6 +4,7 @@ import { useTreeViewSelection } from '../internals/plugins/useTreeViewSelection'
4
4
  import { useTreeViewFocus } from '../internals/plugins/useTreeViewFocus';
5
5
  import { useTreeViewKeyboardNavigation } from '../internals/plugins/useTreeViewKeyboardNavigation';
6
6
  import { useTreeViewIcons } from '../internals/plugins/useTreeViewIcons';
7
- export const RICH_TREE_VIEW_PLUGINS = [useTreeViewItems, useTreeViewExpansion, useTreeViewSelection, useTreeViewFocus, useTreeViewKeyboardNavigation, useTreeViewIcons];
7
+ import { useTreeViewLabel } from '../internals/plugins/useTreeViewLabel';
8
+ export const RICH_TREE_VIEW_PLUGINS = [useTreeViewItems, useTreeViewExpansion, useTreeViewSelection, useTreeViewFocus, useTreeViewKeyboardNavigation, useTreeViewIcons, useTreeViewLabel];
8
9
 
9
10
  // We can't infer this type from the plugin, otherwise we would lose the generics.
@@ -25,6 +25,7 @@ import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons';
25
25
  import { TreeItem2Provider } from '../TreeItem2Provider';
26
26
  import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
27
27
  import { useTreeItemState } from './useTreeItemState';
28
+ import { isTargetInDescendants } from '../internals/utils/tree';
28
29
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
29
30
  const useThemeProps = createUseThemeProps('MuiTreeItem');
30
31
  const useUtilityClasses = ownerState => {
@@ -41,6 +42,9 @@ const useUtilityClasses = ownerState => {
41
42
  iconContainer: ['iconContainer'],
42
43
  checkbox: ['checkbox'],
43
44
  label: ['label'],
45
+ labelInput: ['labelInput'],
46
+ editing: ['editing'],
47
+ editable: ['editable'],
44
48
  groupTransition: ['groupTransition']
45
49
  };
46
50
  return composeClasses(slots, getTreeItemUtilityClass, classes);
@@ -207,6 +211,7 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
207
211
  focused,
208
212
  selected,
209
213
  disabled,
214
+ editing,
210
215
  handleExpansion
211
216
  } = useTreeItemState(itemId);
212
217
  const {
@@ -317,10 +322,19 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
317
322
  }
318
323
  function handleBlur(event) {
319
324
  onBlur?.(event);
325
+ if (editing ||
326
+ // 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
327
+ // 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
328
+ event.relatedTarget && isTargetInDescendants(event.relatedTarget, rootRefObject.current) && (event.target && event.target?.dataset?.element === 'labelInput' && isTargetInDescendants(event.target, rootRefObject.current) || event.relatedTarget?.dataset?.element === 'labelInput')) {
329
+ return;
330
+ }
320
331
  instance.removeFocusedItem();
321
332
  }
322
333
  const handleKeyDown = event => {
323
334
  onKeyDown?.(event);
335
+ if (event.target?.dataset?.element === 'labelInput') {
336
+ return;
337
+ }
324
338
  instance.handleItemKeyDown(event, itemId);
325
339
  };
326
340
  const idAttribute = instance.getTreeItemIdAttribute(itemId, id);
@@ -340,6 +354,11 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
340
354
  contentRefObject,
341
355
  externalEventHandlers: {}
342
356
  }) ?? {};
357
+ const enhancedLabelInputProps = propsEnhancers.labelInput?.({
358
+ rootRefObject,
359
+ contentRefObject,
360
+ externalEventHandlers: {}
361
+ }) ?? {};
343
362
  return /*#__PURE__*/_jsx(TreeItem2Provider, {
344
363
  itemId: itemId,
345
364
  children: /*#__PURE__*/_jsxs(TreeItemRoot, _extends({
@@ -368,8 +387,11 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
368
387
  selected: classes.selected,
369
388
  focused: classes.focused,
370
389
  disabled: classes.disabled,
390
+ editable: classes.editable,
391
+ editing: classes.editing,
371
392
  iconContainer: classes.iconContainer,
372
393
  label: classes.label,
394
+ labelInput: classes.labelInput,
373
395
  checkbox: classes.checkbox
374
396
  },
375
397
  label: label,
@@ -382,6 +404,8 @@ export const TreeItem = /*#__PURE__*/React.forwardRef(function TreeItem(inProps,
382
404
  ownerState: ownerState
383
405
  }, ContentProps, enhancedContentProps, enhancedDragAndDropOverlayProps.action == null ? {} : {
384
406
  dragAndDropOverlayProps: enhancedDragAndDropOverlayProps
407
+ }, enhancedLabelInputProps.value == null ? {} : {
408
+ labelInputProps: enhancedLabelInputProps
385
409
  }, {
386
410
  ref: handleContentRef
387
411
  })), children && /*#__PURE__*/_jsx(TreeItemGroup, _extends({
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { TreeItem2DragAndDropOverlayProps } from '../TreeItem2DragAndDropOverlay';
3
+ import { TreeItem2LabelInputProps } from '../TreeItem2LabelInput';
3
4
  export interface TreeItemContentProps extends React.HTMLAttributes<HTMLElement> {
4
5
  className?: string;
5
6
  /**
@@ -22,6 +23,12 @@ export interface TreeItemContentProps extends React.HTMLAttributes<HTMLElement>
22
23
  label: string;
23
24
  /** Styles applied to the checkbox element. */
24
25
  checkbox: string;
26
+ /** Styles applied to the input element that is visible when editing is enabled. */
27
+ labelInput: string;
28
+ /** Styles applied to the content element when editing is enabled. */
29
+ editing: string;
30
+ /** Styles applied to the content of the items that are editable. */
31
+ editable: string;
25
32
  };
26
33
  /**
27
34
  * The tree item label.
@@ -44,6 +51,7 @@ export interface TreeItemContentProps extends React.HTMLAttributes<HTMLElement>
44
51
  */
45
52
  displayIcon?: React.ReactNode;
46
53
  dragAndDropOverlayProps?: TreeItem2DragAndDropOverlayProps;
54
+ labelInputProps?: TreeItem2LabelInputProps;
47
55
  }
48
56
  export type TreeItemContentClassKey = keyof NonNullable<TreeItemContentProps['classes']>;
49
57
  /**
@@ -1,12 +1,13 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3
- const _excluded = ["classes", "className", "displayIcon", "expansionIcon", "icon", "label", "itemId", "onClick", "onMouseDown", "dragAndDropOverlayProps"];
3
+ const _excluded = ["classes", "className", "displayIcon", "expansionIcon", "icon", "label", "itemId", "onClick", "onMouseDown", "dragAndDropOverlayProps", "labelInputProps"];
4
4
  import * as React from 'react';
5
5
  import PropTypes from 'prop-types';
6
6
  import clsx from 'clsx';
7
7
  import Checkbox from '@mui/material/Checkbox';
8
8
  import { useTreeItemState } from './useTreeItemState';
9
9
  import { TreeItem2DragAndDropOverlay } from '../TreeItem2DragAndDropOverlay';
10
+ import { TreeItem2LabelInput } from '../TreeItem2LabelInput';
10
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
12
  /**
12
13
  * @ignore - internal component.
@@ -22,7 +23,8 @@ const TreeItemContent = /*#__PURE__*/React.forwardRef(function TreeItemContent(p
22
23
  itemId,
23
24
  onClick,
24
25
  onMouseDown,
25
- dragAndDropOverlayProps
26
+ dragAndDropOverlayProps,
27
+ labelInputProps
26
28
  } = props,
27
29
  other = _objectWithoutPropertiesLoose(props, _excluded);
28
30
  const {
@@ -30,6 +32,8 @@ const TreeItemContent = /*#__PURE__*/React.forwardRef(function TreeItemContent(p
30
32
  expanded,
31
33
  selected,
32
34
  focused,
35
+ editing,
36
+ editable,
33
37
  disableSelection,
34
38
  checkboxSelection,
35
39
  handleExpansion,
@@ -37,7 +41,10 @@ const TreeItemContent = /*#__PURE__*/React.forwardRef(function TreeItemContent(p
37
41
  handleCheckboxSelection,
38
42
  handleContentClick,
39
43
  preventSelection,
40
- expansionTrigger
44
+ expansionTrigger,
45
+ toggleItemEditing,
46
+ handleSaveItemLabel,
47
+ handleCancelItemLabelEditing
41
48
  } = useTreeItemState(itemId);
42
49
  const icon = iconProp || expansionIcon || displayIcon;
43
50
  const checkboxRef = React.useRef(null);
@@ -62,11 +69,36 @@ const TreeItemContent = /*#__PURE__*/React.forwardRef(function TreeItemContent(p
62
69
  onClick(event);
63
70
  }
64
71
  };
72
+ const handleLabelDoubleClick = event => {
73
+ if (event.defaultMuiPrevented) {
74
+ return;
75
+ }
76
+ toggleItemEditing();
77
+ };
78
+ const handleLabelInputBlur = event => {
79
+ if (event.defaultMuiPrevented) {
80
+ return;
81
+ }
82
+ if (event.target.value) {
83
+ handleSaveItemLabel(event, event.target.value);
84
+ }
85
+ };
86
+ const handleLabelInputKeydown = event => {
87
+ if (event.defaultMuiPrevented) {
88
+ return;
89
+ }
90
+ const target = event.target;
91
+ if (event.key === 'Enter' && target.value) {
92
+ handleSaveItemLabel(event, target.value);
93
+ } else if (event.key === 'Escape') {
94
+ handleCancelItemLabelEditing(event);
95
+ }
96
+ };
65
97
  return (
66
98
  /*#__PURE__*/
67
99
  /* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions -- Key event is handled by the TreeView */
68
100
  _jsxs("div", _extends({}, other, {
69
- className: clsx(className, classes.root, expanded && classes.expanded, selected && classes.selected, focused && classes.focused, disabled && classes.disabled),
101
+ className: clsx(className, classes.root, expanded && classes.expanded, selected && classes.selected, focused && classes.focused, disabled && classes.disabled, editing && classes.editing, editable && classes.editable),
70
102
  onClick: handleClick,
71
103
  onMouseDown: handleMouseDown,
72
104
  ref: ref,
@@ -80,10 +112,17 @@ const TreeItemContent = /*#__PURE__*/React.forwardRef(function TreeItemContent(p
80
112
  disabled: disabled || disableSelection,
81
113
  ref: checkboxRef,
82
114
  tabIndex: -1
83
- }), /*#__PURE__*/_jsx("div", {
84
- className: classes.label,
115
+ }), editing ? /*#__PURE__*/_jsx(TreeItem2LabelInput, _extends({}, labelInputProps, {
116
+ className: classes.labelInput,
117
+ onBlur: handleLabelInputBlur,
118
+ onKeyDown: handleLabelInputKeydown
119
+ })) : /*#__PURE__*/_jsx("div", _extends({
120
+ className: classes.label
121
+ }, editable && {
122
+ onDoubleClick: handleLabelDoubleClick
123
+ }, {
85
124
  children: label
86
- }), dragAndDropOverlayProps && /*#__PURE__*/_jsx(TreeItem2DragAndDropOverlay, _extends({}, dragAndDropOverlayProps))]
125
+ })), dragAndDropOverlayProps && /*#__PURE__*/_jsx(TreeItem2DragAndDropOverlay, _extends({}, dragAndDropOverlayProps))]
87
126
  }))
88
127
  );
89
128
  });
@@ -120,6 +159,7 @@ process.env.NODE_ENV !== "production" ? TreeItemContent.propTypes = {
120
159
  /**
121
160
  * The tree item label.
122
161
  */
123
- label: PropTypes.node
162
+ label: PropTypes.node,
163
+ labelInputProps: PropTypes.object
124
164
  } : void 0;
125
165
  export { TreeItemContent };
@@ -19,6 +19,12 @@ export interface TreeItemClasses {
19
19
  label: string;
20
20
  /** Styles applied to the checkbox element. */
21
21
  checkbox: string;
22
+ /** Styles applied to the input element that is visible when editing is enabled. */
23
+ labelInput: string;
24
+ /** Styles applied to the content element when editing is enabled. */
25
+ editing: string;
26
+ /** Styles applied to the content of the items that are editable. */
27
+ editable: string;
22
28
  /** Styles applied to the drag and drop overlay. */
23
29
  dragAndDropOverlay: string;
24
30
  }
@@ -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', 'groupTransition', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label', 'checkbox', 'dragAndDropOverlay']);
6
+ export const treeItemClasses = generateUtilityClasses('MuiTreeItem', ['root', 'groupTransition', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label', 'checkbox', 'labelInput', 'editable', 'editing', 'dragAndDropOverlay']);
@@ -1,9 +1,12 @@
1
1
  import * as React from 'react';
2
+ import { MuiCancellableEvent } from '../internals/models/MuiCancellableEvent';
2
3
  export declare function useTreeItemState(itemId: string): {
3
4
  disabled: boolean;
4
5
  expanded: boolean;
5
6
  selected: boolean;
6
7
  focused: boolean;
8
+ editable: boolean;
9
+ editing: boolean;
7
10
  disableSelection: boolean;
8
11
  checkboxSelection: boolean;
9
12
  handleExpansion: (event: React.MouseEvent<HTMLDivElement>) => void;
@@ -12,4 +15,7 @@ export declare function useTreeItemState(itemId: string): {
12
15
  handleContentClick: ((event: React.MouseEvent, itemId: string) => void) | undefined;
13
16
  preventSelection: (event: React.MouseEvent<HTMLDivElement>) => void;
14
17
  expansionTrigger: "content" | "iconContainer" | undefined;
18
+ toggleItemEditing: () => void;
19
+ handleSaveItemLabel: (event: React.SyntheticEvent & MuiCancellableEvent, label: string) => void;
20
+ handleCancelItemLabelEditing: (event: React.SyntheticEvent) => void;
15
21
  };
@@ -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
  export function useTreeItemState(itemId) {
3
5
  const {
4
6
  instance,
@@ -19,6 +21,8 @@ export function useTreeItemState(itemId) {
19
21
  const focused = instance.isItemFocused(itemId);
20
22
  const selected = instance.isItemSelected(itemId);
21
23
  const disabled = instance.isItemDisabled(itemId);
24
+ const editing = instance?.isItemBeingEdited ? instance?.isItemBeingEdited(itemId) : false;
25
+ const editable = instance.isItemEditable ? instance.isItemEditable(itemId) : false;
22
26
  const handleExpansion = event => {
23
27
  if (!disabled) {
24
28
  if (!focused) {
@@ -79,11 +83,49 @@ export function useTreeItemState(itemId) {
79
83
  event.preventDefault();
80
84
  }
81
85
  };
86
+ const toggleItemEditing = () => {
87
+ if (!hasPlugin(instance, useTreeViewLabel)) {
88
+ return;
89
+ }
90
+ if (instance.isItemEditable(itemId)) {
91
+ if (instance.isItemBeingEdited(itemId)) {
92
+ instance.setEditedItemId(null);
93
+ } else {
94
+ instance.setEditedItemId(itemId);
95
+ }
96
+ }
97
+ };
98
+ const handleSaveItemLabel = (event, label) => {
99
+ if (!hasPlugin(instance, useTreeViewLabel)) {
100
+ return;
101
+ }
102
+
103
+ // As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred
104
+ // The `onBlur` event is triggered, which calls `handleSaveItemLabel` again.
105
+ // To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel`
106
+ // using `instance.isItemBeingEditedRef` instead of `instance.isItemBeingEdited` since the state is not yet updated in this point
107
+ if (instance.isItemBeingEditedRef(itemId)) {
108
+ instance.updateItemLabel(itemId, label);
109
+ toggleItemEditing();
110
+ instance.focusItem(event, itemId);
111
+ }
112
+ };
113
+ const handleCancelItemLabelEditing = event => {
114
+ if (!hasPlugin(instance, useTreeViewLabel)) {
115
+ return;
116
+ }
117
+ if (instance.isItemBeingEditedRef(itemId)) {
118
+ toggleItemEditing();
119
+ instance.focusItem(event, itemId);
120
+ }
121
+ };
82
122
  return {
83
123
  disabled,
84
124
  expanded,
85
125
  selected,
86
126
  focused,
127
+ editable,
128
+ editing,
87
129
  disableSelection,
88
130
  checkboxSelection,
89
131
  handleExpansion,
@@ -91,6 +133,9 @@ export function useTreeItemState(itemId) {
91
133
  handleCheckboxSelection,
92
134
  handleContentClick: onItemClick,
93
135
  preventSelection,
94
- expansionTrigger
136
+ expansionTrigger,
137
+ toggleItemEditing,
138
+ handleSaveItemLabel,
139
+ handleCancelItemLabelEditing
95
140
  };
96
141
  }
@@ -7,7 +7,9 @@ export declare const TreeItem2Content: import("@emotion/styled").StyledComponent
7
7
  status: UseTreeItem2Status;
8
8
  indentationAtItemLevel?: true;
9
9
  }, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
10
- export declare const TreeItem2Label: import("@emotion/styled").StyledComponent<import("@mui/system/createStyled").MUIStyledCommonProps<import("@mui/material/styles").Theme>, Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.ClassAttributes<HTMLDivElement> | keyof React.HTMLAttributes<HTMLDivElement>>, {}>;
10
+ export declare const TreeItem2Label: import("@emotion/styled").StyledComponent<import("@mui/system/createStyled").MUIStyledCommonProps<import("@mui/material/styles").Theme> & {
11
+ editable?: boolean;
12
+ }, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
11
13
  export declare const TreeItem2IconContainer: import("@emotion/styled").StyledComponent<import("@mui/system/createStyled").MUIStyledCommonProps<import("@mui/material/styles").Theme>, Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.ClassAttributes<HTMLDivElement> | keyof React.HTMLAttributes<HTMLDivElement>>, {}>;
12
14
  export declare const TreeItem2GroupTransition: import("@emotion/styled").StyledComponent<import("@mui/material/Collapse").CollapseProps & import("@mui/system/createStyled").MUIStyledCommonProps<import("@mui/material/styles").Theme> & {
13
15
  indentationAtItemLevel?: true;
@@ -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'> {
@@ -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 };