@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.
- package/CHANGELOG.md +162 -1
- 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/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
- 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/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
- 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/TreeItem2DragAndDropOverlay/TreeItem2DragAndDropOverlay.js +9 -9
- 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 +5 -5
- package/useTreeItem2/index.d.ts +1 -1
- package/useTreeItem2/useTreeItem2.js +65 -4
- package/useTreeItem2/useTreeItem2.types.d.ts +35 -15
|
@@ -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 {};
|
|
@@ -5,6 +5,7 @@ import useForkRef from '@mui/utils/useForkRef';
|
|
|
5
5
|
import { useTreeViewContext } from '../internals/TreeViewProvider';
|
|
6
6
|
import { useTreeItem2Utils } from '../hooks/useTreeItem2Utils';
|
|
7
7
|
import { TreeViewItemDepthContext } from '../internals/TreeViewItemDepthContext';
|
|
8
|
+
import { isTargetInDescendants } from '../internals/utils/tree';
|
|
8
9
|
export const useTreeItem2 = parameters => {
|
|
9
10
|
const {
|
|
10
11
|
runItemPlugins,
|
|
@@ -50,6 +51,7 @@ export const useTreeItem2 = parameters => {
|
|
|
50
51
|
const handleRootRef = useForkRef(rootRef, pluginRootRef, rootRefObject);
|
|
51
52
|
const handleContentRef = useForkRef(contentRef, contentRefObject);
|
|
52
53
|
const checkboxRef = React.useRef(null);
|
|
54
|
+
const rootTabIndex = instance.canItemBeTabbed(itemId) ? 0 : -1;
|
|
53
55
|
const createRootHandleFocus = otherHandlers => event => {
|
|
54
56
|
otherHandlers.onFocus?.(event);
|
|
55
57
|
if (event.defaultMuiPrevented) {
|
|
@@ -65,15 +67,33 @@ export const useTreeItem2 = parameters => {
|
|
|
65
67
|
if (event.defaultMuiPrevented) {
|
|
66
68
|
return;
|
|
67
69
|
}
|
|
70
|
+
const rootElement = instance.getItemDOMElement(itemId);
|
|
71
|
+
|
|
72
|
+
// Don't blur the root when switching to editing mode
|
|
73
|
+
// the input that triggers the root blur can be either the relatedTarget (when entering editing state) or the target (when exiting editing state)
|
|
74
|
+
// when we enter the editing state, we focus the input -> we don't want to remove the focused item from the state
|
|
75
|
+
if (status.editing ||
|
|
76
|
+
// we can exit the editing state by clicking outside the input (within the tree item) or by pressing Enter or Escape -> we don't want to remove the focused item from the state in these cases
|
|
77
|
+
// we can also exit the editing state by clicking on the root itself -> want to remove the focused item from the state in this case
|
|
78
|
+
event.relatedTarget && isTargetInDescendants(event.relatedTarget, rootElement) && (event.target && event.target?.dataset?.element === 'labelInput' && isTargetInDescendants(event.target, rootElement) || event.relatedTarget?.dataset?.element === 'labelInput')) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
68
81
|
instance.removeFocusedItem();
|
|
69
82
|
};
|
|
70
83
|
const createRootHandleKeyDown = otherHandlers => event => {
|
|
71
84
|
otherHandlers.onKeyDown?.(event);
|
|
72
|
-
if (event.defaultMuiPrevented) {
|
|
85
|
+
if (event.defaultMuiPrevented || event.target?.dataset?.element === 'labelInput') {
|
|
73
86
|
return;
|
|
74
87
|
}
|
|
75
88
|
instance.handleItemKeyDown(event, itemId);
|
|
76
89
|
};
|
|
90
|
+
const createLabelHandleDoubleClick = otherHandlers => event => {
|
|
91
|
+
otherHandlers.onDoubleClick?.(event);
|
|
92
|
+
if (event.defaultMuiPrevented) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
interactions.toggleItemEditing();
|
|
96
|
+
};
|
|
77
97
|
const createContentHandleClick = otherHandlers => event => {
|
|
78
98
|
otherHandlers.onClick?.(event);
|
|
79
99
|
onItemClick?.(event, itemId);
|
|
@@ -108,6 +128,27 @@ export const useTreeItem2 = parameters => {
|
|
|
108
128
|
}
|
|
109
129
|
interactions.handleCheckboxSelection(event);
|
|
110
130
|
};
|
|
131
|
+
const createInputHandleKeydown = otherHandlers => event => {
|
|
132
|
+
otherHandlers.onKeyDown?.(event);
|
|
133
|
+
if (event.defaultMuiPrevented) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const target = event.target;
|
|
137
|
+
if (event.key === 'Enter' && target.value) {
|
|
138
|
+
interactions.handleSaveItemLabel(event, target.value);
|
|
139
|
+
} else if (event.key === 'Escape') {
|
|
140
|
+
interactions.handleCancelItemLabelEditing(event);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
const createInputHandleBlur = otherHandlers => event => {
|
|
144
|
+
otherHandlers.onBlur?.(event);
|
|
145
|
+
if (event.defaultMuiPrevented) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (event.target.value) {
|
|
149
|
+
interactions.handleSaveItemLabel(event, event.target.value);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
111
152
|
const createIconContainerHandleClick = otherHandlers => event => {
|
|
112
153
|
otherHandlers.onClick?.(event);
|
|
113
154
|
if (event.defaultMuiPrevented) {
|
|
@@ -134,7 +175,7 @@ export const useTreeItem2 = parameters => {
|
|
|
134
175
|
const props = _extends({}, externalEventHandlers, {
|
|
135
176
|
ref: handleRootRef,
|
|
136
177
|
role: 'treeitem',
|
|
137
|
-
tabIndex:
|
|
178
|
+
tabIndex: rootTabIndex,
|
|
138
179
|
id: idAttribute,
|
|
139
180
|
'aria-expanded': status.expandable ? status.expanded : undefined,
|
|
140
181
|
'aria-selected': ariaSelected,
|
|
@@ -188,9 +229,28 @@ export const useTreeItem2 = parameters => {
|
|
|
188
229
|
};
|
|
189
230
|
const getLabelProps = (externalProps = {}) => {
|
|
190
231
|
const externalEventHandlers = _extends({}, extractEventHandlers(externalProps));
|
|
191
|
-
|
|
232
|
+
const props = _extends({}, externalEventHandlers, {
|
|
192
233
|
children: label
|
|
193
|
-
}, externalProps
|
|
234
|
+
}, externalProps, {
|
|
235
|
+
onDoubleClick: createLabelHandleDoubleClick(externalEventHandlers)
|
|
236
|
+
});
|
|
237
|
+
if (instance.isTreeViewEditable) {
|
|
238
|
+
props.editable = status.editable;
|
|
239
|
+
}
|
|
240
|
+
return props;
|
|
241
|
+
};
|
|
242
|
+
const getLabelInputProps = (externalProps = {}) => {
|
|
243
|
+
const externalEventHandlers = extractEventHandlers(externalProps);
|
|
244
|
+
const props = _extends({}, externalEventHandlers, externalProps, {
|
|
245
|
+
onKeyDown: createInputHandleKeydown(externalEventHandlers),
|
|
246
|
+
onBlur: createInputHandleBlur(externalEventHandlers)
|
|
247
|
+
});
|
|
248
|
+
const enhancedlabelInputProps = propsEnhancers.labelInput?.({
|
|
249
|
+
rootRefObject,
|
|
250
|
+
contentRefObject,
|
|
251
|
+
externalEventHandlers
|
|
252
|
+
}) ?? {};
|
|
253
|
+
return _extends({}, props, enhancedlabelInputProps);
|
|
194
254
|
};
|
|
195
255
|
const getIconContainerProps = (externalProps = {}) => {
|
|
196
256
|
const externalEventHandlers = extractEventHandlers(externalProps);
|
|
@@ -228,6 +288,7 @@ export const useTreeItem2 = parameters => {
|
|
|
228
288
|
getIconContainerProps,
|
|
229
289
|
getCheckboxProps,
|
|
230
290
|
getLabelProps,
|
|
291
|
+
getLabelInputProps,
|
|
231
292
|
getDragAndDropOverlayProps,
|
|
232
293
|
rootRef: handleRootRef,
|
|
233
294
|
status,
|
|
@@ -149,7 +149,8 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
149
149
|
getItemOrderedChildrenIds: _propTypes.default.func.isRequired,
|
|
150
150
|
getItemTree: _propTypes.default.func.isRequired,
|
|
151
151
|
selectItem: _propTypes.default.func.isRequired,
|
|
152
|
-
setItemExpansion: _propTypes.default.func.isRequired
|
|
152
|
+
setItemExpansion: _propTypes.default.func.isRequired,
|
|
153
|
+
updateItemLabel: _propTypes.default.func.isRequired
|
|
153
154
|
})
|
|
154
155
|
}),
|
|
155
156
|
/**
|
|
@@ -200,7 +201,8 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
200
201
|
* the feature will be fully disabled and any property / method call will not have any effect.
|
|
201
202
|
*/
|
|
202
203
|
experimentalFeatures: _propTypes.default.shape({
|
|
203
|
-
indentationAtItemLevel: _propTypes.default.bool
|
|
204
|
+
indentationAtItemLevel: _propTypes.default.bool,
|
|
205
|
+
labelEditing: _propTypes.default.bool
|
|
204
206
|
}),
|
|
205
207
|
/**
|
|
206
208
|
* Used to determine the id of a given item.
|
|
@@ -232,6 +234,16 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
232
234
|
* @returns {boolean} `true` if the item should be disabled.
|
|
233
235
|
*/
|
|
234
236
|
isItemDisabled: _propTypes.default.func,
|
|
237
|
+
/**
|
|
238
|
+
* Determines if a given item is editable or not.
|
|
239
|
+
* Make sure to also enable the `labelEditing` experimental feature:
|
|
240
|
+
* `<RichTreeViewPro experimentalFeatures={{ labelEditing: true }} />`.
|
|
241
|
+
* By default, the items are not editable.
|
|
242
|
+
* @template R
|
|
243
|
+
* @param {R} item The item to check.
|
|
244
|
+
* @returns {boolean} `true` if the item is editable.
|
|
245
|
+
*/
|
|
246
|
+
isItemEditable: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.bool]),
|
|
235
247
|
/**
|
|
236
248
|
* Horizontal indentation between an item and its children.
|
|
237
249
|
* Examples: 24, "24px", "2rem", "2em".
|
|
@@ -269,6 +281,12 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
269
281
|
* @param {string} itemId The id of the focused item.
|
|
270
282
|
*/
|
|
271
283
|
onItemFocus: _propTypes.default.func,
|
|
284
|
+
/**
|
|
285
|
+
* Callback fired when the label of an item changes.
|
|
286
|
+
* @param {TreeViewItemId} itemId The id of the item that was edited.
|
|
287
|
+
* @param {string} newLabel The new label of the items.
|
|
288
|
+
*/
|
|
289
|
+
onItemLabelChange: _propTypes.default.func,
|
|
272
290
|
/**
|
|
273
291
|
* Callback fired when a tree item is selected or deselected.
|
|
274
292
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change.
|
|
@@ -10,6 +10,7 @@ var _useTreeViewSelection = require("../internals/plugins/useTreeViewSelection")
|
|
|
10
10
|
var _useTreeViewFocus = require("../internals/plugins/useTreeViewFocus");
|
|
11
11
|
var _useTreeViewKeyboardNavigation = require("../internals/plugins/useTreeViewKeyboardNavigation");
|
|
12
12
|
var _useTreeViewIcons = require("../internals/plugins/useTreeViewIcons");
|
|
13
|
-
|
|
13
|
+
var _useTreeViewLabel = require("../internals/plugins/useTreeViewLabel");
|
|
14
|
+
const RICH_TREE_VIEW_PLUGINS = exports.RICH_TREE_VIEW_PLUGINS = [_useTreeViewItems.useTreeViewItems, _useTreeViewExpansion.useTreeViewExpansion, _useTreeViewSelection.useTreeViewSelection, _useTreeViewFocus.useTreeViewFocus, _useTreeViewKeyboardNavigation.useTreeViewKeyboardNavigation, _useTreeViewIcons.useTreeViewIcons, _useTreeViewLabel.useTreeViewLabel];
|
|
14
15
|
|
|
15
16
|
// We can't infer this type from the plugin, otherwise we would lose the generics.
|
|
@@ -28,6 +28,7 @@ var _icons = require("../icons");
|
|
|
28
28
|
var _TreeItem2Provider = require("../TreeItem2Provider");
|
|
29
29
|
var _TreeViewItemDepthContext = require("../internals/TreeViewItemDepthContext");
|
|
30
30
|
var _useTreeItemState = require("./useTreeItemState");
|
|
31
|
+
var _tree = require("../internals/utils/tree");
|
|
31
32
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
32
33
|
const _excluded = ["children", "className", "slots", "slotProps", "ContentComponent", "ContentProps", "itemId", "id", "label", "onClick", "onMouseDown", "onFocus", "onBlur", "onKeyDown"],
|
|
33
34
|
_excluded2 = ["ownerState"],
|
|
@@ -50,6 +51,9 @@ const useUtilityClasses = ownerState => {
|
|
|
50
51
|
iconContainer: ['iconContainer'],
|
|
51
52
|
checkbox: ['checkbox'],
|
|
52
53
|
label: ['label'],
|
|
54
|
+
labelInput: ['labelInput'],
|
|
55
|
+
editing: ['editing'],
|
|
56
|
+
editable: ['editable'],
|
|
53
57
|
groupTransition: ['groupTransition']
|
|
54
58
|
};
|
|
55
59
|
return (0, _composeClasses.default)(slots, _treeItemClasses.getTreeItemUtilityClass, classes);
|
|
@@ -216,6 +220,7 @@ const TreeItem = exports.TreeItem = /*#__PURE__*/React.forwardRef(function TreeI
|
|
|
216
220
|
focused,
|
|
217
221
|
selected,
|
|
218
222
|
disabled,
|
|
223
|
+
editing,
|
|
219
224
|
handleExpansion
|
|
220
225
|
} = (0, _useTreeItemState.useTreeItemState)(itemId);
|
|
221
226
|
const {
|
|
@@ -326,10 +331,19 @@ const TreeItem = exports.TreeItem = /*#__PURE__*/React.forwardRef(function TreeI
|
|
|
326
331
|
}
|
|
327
332
|
function handleBlur(event) {
|
|
328
333
|
onBlur?.(event);
|
|
334
|
+
if (editing ||
|
|
335
|
+
// 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
|
|
336
|
+
// 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
|
|
337
|
+
event.relatedTarget && (0, _tree.isTargetInDescendants)(event.relatedTarget, rootRefObject.current) && (event.target && event.target?.dataset?.element === 'labelInput' && (0, _tree.isTargetInDescendants)(event.target, rootRefObject.current) || event.relatedTarget?.dataset?.element === 'labelInput')) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
329
340
|
instance.removeFocusedItem();
|
|
330
341
|
}
|
|
331
342
|
const handleKeyDown = event => {
|
|
332
343
|
onKeyDown?.(event);
|
|
344
|
+
if (event.target?.dataset?.element === 'labelInput') {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
333
347
|
instance.handleItemKeyDown(event, itemId);
|
|
334
348
|
};
|
|
335
349
|
const idAttribute = instance.getTreeItemIdAttribute(itemId, id);
|
|
@@ -349,6 +363,11 @@ const TreeItem = exports.TreeItem = /*#__PURE__*/React.forwardRef(function TreeI
|
|
|
349
363
|
contentRefObject,
|
|
350
364
|
externalEventHandlers: {}
|
|
351
365
|
}) ?? {};
|
|
366
|
+
const enhancedLabelInputProps = propsEnhancers.labelInput?.({
|
|
367
|
+
rootRefObject,
|
|
368
|
+
contentRefObject,
|
|
369
|
+
externalEventHandlers: {}
|
|
370
|
+
}) ?? {};
|
|
352
371
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeItem2Provider.TreeItem2Provider, {
|
|
353
372
|
itemId: itemId,
|
|
354
373
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(TreeItemRoot, (0, _extends2.default)({
|
|
@@ -377,8 +396,11 @@ const TreeItem = exports.TreeItem = /*#__PURE__*/React.forwardRef(function TreeI
|
|
|
377
396
|
selected: classes.selected,
|
|
378
397
|
focused: classes.focused,
|
|
379
398
|
disabled: classes.disabled,
|
|
399
|
+
editable: classes.editable,
|
|
400
|
+
editing: classes.editing,
|
|
380
401
|
iconContainer: classes.iconContainer,
|
|
381
402
|
label: classes.label,
|
|
403
|
+
labelInput: classes.labelInput,
|
|
382
404
|
checkbox: classes.checkbox
|
|
383
405
|
},
|
|
384
406
|
label: label,
|
|
@@ -391,6 +413,8 @@ const TreeItem = exports.TreeItem = /*#__PURE__*/React.forwardRef(function TreeI
|
|
|
391
413
|
ownerState: ownerState
|
|
392
414
|
}, ContentProps, enhancedContentProps, enhancedDragAndDropOverlayProps.action == null ? {} : {
|
|
393
415
|
dragAndDropOverlayProps: enhancedDragAndDropOverlayProps
|
|
416
|
+
}, enhancedLabelInputProps.value == null ? {} : {
|
|
417
|
+
labelInputProps: enhancedLabelInputProps
|
|
394
418
|
}, {
|
|
395
419
|
ref: handleContentRef
|
|
396
420
|
})), children && /*#__PURE__*/(0, _jsxRuntime.jsx)(TreeItemGroup, (0, _extends2.default)({
|
|
@@ -13,8 +13,9 @@ var _clsx = _interopRequireDefault(require("clsx"));
|
|
|
13
13
|
var _Checkbox = _interopRequireDefault(require("@mui/material/Checkbox"));
|
|
14
14
|
var _useTreeItemState = require("./useTreeItemState");
|
|
15
15
|
var _TreeItem2DragAndDropOverlay = require("../TreeItem2DragAndDropOverlay");
|
|
16
|
+
var _TreeItem2LabelInput = require("../TreeItem2LabelInput");
|
|
16
17
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
-
const _excluded = ["classes", "className", "displayIcon", "expansionIcon", "icon", "label", "itemId", "onClick", "onMouseDown", "dragAndDropOverlayProps"];
|
|
18
|
+
const _excluded = ["classes", "className", "displayIcon", "expansionIcon", "icon", "label", "itemId", "onClick", "onMouseDown", "dragAndDropOverlayProps", "labelInputProps"];
|
|
18
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
19
20
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
20
21
|
/**
|
|
@@ -31,7 +32,8 @@ const TreeItemContent = exports.TreeItemContent = /*#__PURE__*/React.forwardRef(
|
|
|
31
32
|
itemId,
|
|
32
33
|
onClick,
|
|
33
34
|
onMouseDown,
|
|
34
|
-
dragAndDropOverlayProps
|
|
35
|
+
dragAndDropOverlayProps,
|
|
36
|
+
labelInputProps
|
|
35
37
|
} = props,
|
|
36
38
|
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
|
|
37
39
|
const {
|
|
@@ -39,6 +41,8 @@ const TreeItemContent = exports.TreeItemContent = /*#__PURE__*/React.forwardRef(
|
|
|
39
41
|
expanded,
|
|
40
42
|
selected,
|
|
41
43
|
focused,
|
|
44
|
+
editing,
|
|
45
|
+
editable,
|
|
42
46
|
disableSelection,
|
|
43
47
|
checkboxSelection,
|
|
44
48
|
handleExpansion,
|
|
@@ -46,7 +50,10 @@ const TreeItemContent = exports.TreeItemContent = /*#__PURE__*/React.forwardRef(
|
|
|
46
50
|
handleCheckboxSelection,
|
|
47
51
|
handleContentClick,
|
|
48
52
|
preventSelection,
|
|
49
|
-
expansionTrigger
|
|
53
|
+
expansionTrigger,
|
|
54
|
+
toggleItemEditing,
|
|
55
|
+
handleSaveItemLabel,
|
|
56
|
+
handleCancelItemLabelEditing
|
|
50
57
|
} = (0, _useTreeItemState.useTreeItemState)(itemId);
|
|
51
58
|
const icon = iconProp || expansionIcon || displayIcon;
|
|
52
59
|
const checkboxRef = React.useRef(null);
|
|
@@ -71,11 +78,36 @@ const TreeItemContent = exports.TreeItemContent = /*#__PURE__*/React.forwardRef(
|
|
|
71
78
|
onClick(event);
|
|
72
79
|
}
|
|
73
80
|
};
|
|
81
|
+
const handleLabelDoubleClick = event => {
|
|
82
|
+
if (event.defaultMuiPrevented) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
toggleItemEditing();
|
|
86
|
+
};
|
|
87
|
+
const handleLabelInputBlur = event => {
|
|
88
|
+
if (event.defaultMuiPrevented) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (event.target.value) {
|
|
92
|
+
handleSaveItemLabel(event, event.target.value);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const handleLabelInputKeydown = event => {
|
|
96
|
+
if (event.defaultMuiPrevented) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const target = event.target;
|
|
100
|
+
if (event.key === 'Enter' && target.value) {
|
|
101
|
+
handleSaveItemLabel(event, target.value);
|
|
102
|
+
} else if (event.key === 'Escape') {
|
|
103
|
+
handleCancelItemLabelEditing(event);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
74
106
|
return (
|
|
75
107
|
/*#__PURE__*/
|
|
76
108
|
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions -- Key event is handled by the TreeView */
|
|
77
109
|
(0, _jsxRuntime.jsxs)("div", (0, _extends2.default)({}, other, {
|
|
78
|
-
className: (0, _clsx.default)(className, classes.root, expanded && classes.expanded, selected && classes.selected, focused && classes.focused, disabled && classes.disabled),
|
|
110
|
+
className: (0, _clsx.default)(className, classes.root, expanded && classes.expanded, selected && classes.selected, focused && classes.focused, disabled && classes.disabled, editing && classes.editing, editable && classes.editable),
|
|
79
111
|
onClick: handleClick,
|
|
80
112
|
onMouseDown: handleMouseDown,
|
|
81
113
|
ref: ref,
|
|
@@ -89,10 +121,17 @@ const TreeItemContent = exports.TreeItemContent = /*#__PURE__*/React.forwardRef(
|
|
|
89
121
|
disabled: disabled || disableSelection,
|
|
90
122
|
ref: checkboxRef,
|
|
91
123
|
tabIndex: -1
|
|
92
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(
|
|
93
|
-
className: classes.
|
|
124
|
+
}), editing ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeItem2LabelInput.TreeItem2LabelInput, (0, _extends2.default)({}, labelInputProps, {
|
|
125
|
+
className: classes.labelInput,
|
|
126
|
+
onBlur: handleLabelInputBlur,
|
|
127
|
+
onKeyDown: handleLabelInputKeydown
|
|
128
|
+
})) : /*#__PURE__*/(0, _jsxRuntime.jsx)("div", (0, _extends2.default)({
|
|
129
|
+
className: classes.label
|
|
130
|
+
}, editable && {
|
|
131
|
+
onDoubleClick: handleLabelDoubleClick
|
|
132
|
+
}, {
|
|
94
133
|
children: label
|
|
95
|
-
}), dragAndDropOverlayProps && /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeItem2DragAndDropOverlay.TreeItem2DragAndDropOverlay, (0, _extends2.default)({}, dragAndDropOverlayProps))]
|
|
134
|
+
})), dragAndDropOverlayProps && /*#__PURE__*/(0, _jsxRuntime.jsx)(_TreeItem2DragAndDropOverlay.TreeItem2DragAndDropOverlay, (0, _extends2.default)({}, dragAndDropOverlayProps))]
|
|
96
135
|
}))
|
|
97
136
|
);
|
|
98
137
|
});
|
|
@@ -129,5 +168,6 @@ process.env.NODE_ENV !== "production" ? TreeItemContent.propTypes = {
|
|
|
129
168
|
/**
|
|
130
169
|
* The tree item label.
|
|
131
170
|
*/
|
|
132
|
-
label: _propTypes.default.node
|
|
171
|
+
label: _propTypes.default.node,
|
|
172
|
+
labelInputProps: _propTypes.default.object
|
|
133
173
|
} : void 0;
|
|
@@ -11,4 +11,4 @@ var _generateUtilityClasses = _interopRequireDefault(require("@mui/utils/generat
|
|
|
11
11
|
function getTreeItemUtilityClass(slot) {
|
|
12
12
|
return (0, _generateUtilityClass.default)('MuiTreeItem', slot);
|
|
13
13
|
}
|
|
14
|
-
const treeItemClasses = exports.treeItemClasses = (0, _generateUtilityClasses.default)('MuiTreeItem', ['root', 'groupTransition', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label', 'checkbox', 'dragAndDropOverlay']);
|
|
14
|
+
const treeItemClasses = exports.treeItemClasses = (0, _generateUtilityClasses.default)('MuiTreeItem', ['root', 'groupTransition', 'content', 'expanded', 'selected', 'focused', 'disabled', 'iconContainer', 'label', 'checkbox', 'labelInput', 'editable', 'editing', 'dragAndDropOverlay']);
|
|
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useTreeItemState = useTreeItemState;
|
|
7
7
|
var _TreeViewProvider = require("../internals/TreeViewProvider");
|
|
8
|
+
var _useTreeViewLabel = require("../internals/plugins/useTreeViewLabel");
|
|
9
|
+
var _plugins = require("../internals/utils/plugins");
|
|
8
10
|
function useTreeItemState(itemId) {
|
|
9
11
|
const {
|
|
10
12
|
instance,
|
|
@@ -25,6 +27,8 @@ function useTreeItemState(itemId) {
|
|
|
25
27
|
const focused = instance.isItemFocused(itemId);
|
|
26
28
|
const selected = instance.isItemSelected(itemId);
|
|
27
29
|
const disabled = instance.isItemDisabled(itemId);
|
|
30
|
+
const editing = instance?.isItemBeingEdited ? instance?.isItemBeingEdited(itemId) : false;
|
|
31
|
+
const editable = instance.isItemEditable ? instance.isItemEditable(itemId) : false;
|
|
28
32
|
const handleExpansion = event => {
|
|
29
33
|
if (!disabled) {
|
|
30
34
|
if (!focused) {
|
|
@@ -85,11 +89,49 @@ function useTreeItemState(itemId) {
|
|
|
85
89
|
event.preventDefault();
|
|
86
90
|
}
|
|
87
91
|
};
|
|
92
|
+
const toggleItemEditing = () => {
|
|
93
|
+
if (!(0, _plugins.hasPlugin)(instance, _useTreeViewLabel.useTreeViewLabel)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (instance.isItemEditable(itemId)) {
|
|
97
|
+
if (instance.isItemBeingEdited(itemId)) {
|
|
98
|
+
instance.setEditedItemId(null);
|
|
99
|
+
} else {
|
|
100
|
+
instance.setEditedItemId(itemId);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const handleSaveItemLabel = (event, label) => {
|
|
105
|
+
if (!(0, _plugins.hasPlugin)(instance, _useTreeViewLabel.useTreeViewLabel)) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// As a side effect of `instance.focusItem` called here and in `handleCancelItemLabelEditing` the `labelInput` is blurred
|
|
110
|
+
// The `onBlur` event is triggered, which calls `handleSaveItemLabel` again.
|
|
111
|
+
// To avoid creating an unwanted behavior we need to check if the item is being edited before calling `updateItemLabel`
|
|
112
|
+
// using `instance.isItemBeingEditedRef` instead of `instance.isItemBeingEdited` since the state is not yet updated in this point
|
|
113
|
+
if (instance.isItemBeingEditedRef(itemId)) {
|
|
114
|
+
instance.updateItemLabel(itemId, label);
|
|
115
|
+
toggleItemEditing();
|
|
116
|
+
instance.focusItem(event, itemId);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const handleCancelItemLabelEditing = event => {
|
|
120
|
+
if (!(0, _plugins.hasPlugin)(instance, _useTreeViewLabel.useTreeViewLabel)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (instance.isItemBeingEditedRef(itemId)) {
|
|
124
|
+
toggleItemEditing();
|
|
125
|
+
instance.focusItem(event, itemId);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
88
128
|
return {
|
|
89
129
|
disabled,
|
|
90
130
|
expanded,
|
|
91
131
|
selected,
|
|
92
132
|
focused,
|
|
133
|
+
editable,
|
|
134
|
+
editing,
|
|
93
135
|
disableSelection,
|
|
94
136
|
checkboxSelection,
|
|
95
137
|
handleExpansion,
|
|
@@ -97,6 +139,9 @@ function useTreeItemState(itemId) {
|
|
|
97
139
|
handleCheckboxSelection,
|
|
98
140
|
handleContentClick: onItemClick,
|
|
99
141
|
preventSelection,
|
|
100
|
-
expansionTrigger
|
|
142
|
+
expansionTrigger,
|
|
143
|
+
toggleItemEditing,
|
|
144
|
+
handleSaveItemLabel,
|
|
145
|
+
handleCancelItemLabelEditing
|
|
101
146
|
};
|
|
102
147
|
}
|