@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.
- package/CHANGELOG.md +81 -0
- package/RichTreeView/RichTreeView.js +20 -2
- package/RichTreeView/RichTreeView.plugins.d.ts +3 -2
- package/RichTreeView/RichTreeView.plugins.js +2 -1
- package/TreeItem/TreeItem.js +24 -0
- package/TreeItem/TreeItemContent.d.ts +8 -0
- package/TreeItem/TreeItemContent.js +48 -8
- package/TreeItem/treeItemClasses.d.ts +6 -0
- package/TreeItem/treeItemClasses.js +1 -1
- package/TreeItem/useTreeItemState.d.ts +6 -0
- package/TreeItem/useTreeItemState.js +46 -1
- package/TreeItem2/TreeItem2.d.ts +3 -1
- package/TreeItem2/TreeItem2.js +29 -5
- package/TreeItem2/TreeItem2.types.d.ts +6 -0
- package/TreeItem2Icon/TreeItem2Icon.js +2 -0
- package/TreeItem2LabelInput/TreeItem2LabelInput.d.ts +2 -0
- package/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
- package/TreeItem2LabelInput/TreeItem2LabelInput.types.d.ts +8 -0
- package/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
- package/TreeItem2LabelInput/index.d.ts +2 -0
- package/TreeItem2LabelInput/index.js +1 -0
- package/TreeItem2LabelInput/package.json +6 -0
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.d.ts +5 -1
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
- package/hooks/useTreeViewApiRef.d.ts +1 -1
- package/index.js +1 -1
- package/internals/index.d.ts +2 -0
- package/internals/index.js +1 -0
- package/internals/models/itemPlugin.d.ts +2 -1
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +2 -0
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +2 -0
- package/internals/plugins/useTreeViewLabel/index.d.ts +2 -0
- package/internals/plugins/useTreeViewLabel/index.js +1 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.d.ts +3 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.d.ts +3 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.d.ts +75 -0
- package/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
- package/modern/RichTreeView/RichTreeView.js +20 -2
- package/modern/RichTreeView/RichTreeView.plugins.js +2 -1
- package/modern/TreeItem/TreeItem.js +24 -0
- package/modern/TreeItem/TreeItemContent.js +48 -8
- package/modern/TreeItem/treeItemClasses.js +1 -1
- package/modern/TreeItem/useTreeItemState.js +46 -1
- package/modern/TreeItem2/TreeItem2.js +29 -5
- package/modern/TreeItem2Icon/TreeItem2Icon.js +2 -0
- package/modern/TreeItem2LabelInput/TreeItem2LabelInput.js +20 -0
- package/modern/TreeItem2LabelInput/TreeItem2LabelInput.types.js +1 -0
- package/modern/TreeItem2LabelInput/index.js +1 -0
- package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
- package/modern/index.js +1 -1
- package/modern/internals/index.js +1 -0
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
- package/modern/internals/plugins/useTreeViewLabel/index.js +1 -0
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +44 -0
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +81 -0
- package/modern/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +1 -0
- package/modern/useTreeItem2/useTreeItem2.js +65 -4
- package/node/RichTreeView/RichTreeView.js +20 -2
- package/node/RichTreeView/RichTreeView.plugins.js +2 -1
- package/node/TreeItem/TreeItem.js +24 -0
- package/node/TreeItem/TreeItemContent.js +48 -8
- package/node/TreeItem/treeItemClasses.js +1 -1
- package/node/TreeItem/useTreeItemState.js +46 -1
- package/node/TreeItem2/TreeItem2.js +29 -5
- package/node/TreeItem2Icon/TreeItem2Icon.js +2 -0
- package/node/TreeItem2LabelInput/TreeItem2LabelInput.js +26 -0
- package/node/TreeItem2LabelInput/TreeItem2LabelInput.types.js +5 -0
- package/node/TreeItem2LabelInput/index.js +12 -0
- package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +45 -2
- package/node/index.js +1 -1
- package/node/internals/index.js +7 -0
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +4 -1
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +5 -1
- package/node/internals/plugins/useTreeViewLabel/index.js +12 -0
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.itemPlugin.js +54 -0
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.js +91 -0
- package/node/internals/plugins/useTreeViewLabel/useTreeViewLabel.types.js +5 -0
- package/node/useTreeItem2/useTreeItem2.js +65 -4
- package/package.json +2 -2
- package/useTreeItem2/index.d.ts +1 -1
- package/useTreeItem2/useTreeItem2.js +65 -4
- package/useTreeItem2/useTreeItem2.types.d.ts +35 -15
|
@@ -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(
|
|
84
|
-
className: classes.
|
|
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 };
|
|
@@ -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,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
|
}
|
|
@@ -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
|
-
|
|
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))]
|
|
@@ -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,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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TreeItem2LabelInput } from './TreeItem2LabelInput';
|
|
@@ -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,
|
package/modern/index.js
CHANGED
|
@@ -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';
|
|
@@ -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
|
package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -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 (
|
|
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)) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useTreeViewLabel } from './useTreeViewLabel';
|
|
@@ -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 @@
|
|
|
1
|
+
export {};
|