@mui/x-tree-view 7.0.0-alpha.7 → 7.0.0-alpha.9
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 +554 -51
- package/README.md +1 -1
- package/RichTreeView/RichTreeView.js +14 -79
- package/RichTreeView/RichTreeView.types.d.ts +6 -9
- package/RichTreeView/index.d.ts +1 -1
- package/SimpleTreeView/SimpleTreeView.js +17 -75
- package/SimpleTreeView/SimpleTreeView.plugins.d.ts +4 -2
- package/SimpleTreeView/SimpleTreeView.types.d.ts +8 -7
- package/SimpleTreeView/index.d.ts +1 -1
- package/TreeItem/TreeItem.js +62 -39
- package/TreeItem/TreeItem.types.d.ts +33 -17
- package/TreeItem/TreeItemContent.d.ts +0 -3
- package/TreeItem/TreeItemContent.js +2 -5
- package/TreeItem/index.d.ts +2 -2
- package/TreeItem/index.js +1 -1
- package/TreeItem/{useTreeItem.d.ts → useTreeItemState.d.ts} +1 -1
- package/TreeItem/{useTreeItem.js → useTreeItemState.js} +4 -2
- package/TreeView/TreeView.js +1 -22
- package/TreeView/TreeView.types.d.ts +5 -1
- package/TreeView/index.d.ts +1 -1
- package/icons/icons.d.ts +6 -0
- package/icons/icons.js +9 -0
- package/icons/index.d.ts +1 -0
- package/icons/index.js +1 -0
- package/icons/package.json +6 -0
- package/index.d.ts +1 -0
- package/index.js +3 -2
- package/internals/TreeViewProvider/TreeViewContext.d.ts +4 -2
- package/internals/TreeViewProvider/TreeViewProvider.types.d.ts +3 -11
- package/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.types.d.ts +3 -2
- package/internals/index.d.ts +15 -0
- package/internals/index.js +4 -0
- package/internals/models/MuiCancellableEvent.d.ts +4 -0
- package/internals/models/helpers.d.ts +7 -1
- package/internals/models/plugin.d.ts +65 -20
- package/internals/models/treeView.d.ts +1 -2
- package/internals/package.json +6 -0
- package/internals/plugins/defaultPlugins.d.ts +6 -4
- package/internals/plugins/defaultPlugins.js +2 -2
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -10
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +7 -3
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +2 -5
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.d.ts +12 -6
- package/internals/plugins/useTreeViewIcons/index.d.ts +2 -0
- package/internals/plugins/useTreeViewIcons/index.js +1 -0
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.d.ts +3 -0
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +22 -0
- package/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.d.ts +43 -0
- package/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
- package/internals/plugins/useTreeViewId/useTreeViewId.types.d.ts +6 -2
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.d.ts +6 -4
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +9 -6
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +21 -3
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.d.ts +10 -2
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +17 -5
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +15 -5
- package/internals/useTreeView/useTreeView.js +7 -3
- package/internals/useTreeView/useTreeView.types.d.ts +6 -6
- package/internals/useTreeView/useTreeViewModels.js +12 -13
- package/internals/utils/extractPluginParamsFromProps.d.ts +16 -0
- package/internals/utils/extractPluginParamsFromProps.js +38 -0
- package/legacy/RichTreeView/RichTreeView.js +15 -77
- package/legacy/SimpleTreeView/SimpleTreeView.js +14 -70
- package/legacy/TreeItem/TreeItem.js +63 -39
- package/legacy/TreeItem/TreeItemContent.js +9 -12
- package/legacy/TreeItem/index.js +1 -1
- package/legacy/TreeItem/{useTreeItem.js → useTreeItemState.js} +2 -2
- package/legacy/TreeView/TreeView.js +1 -22
- package/legacy/icons/icons.js +9 -0
- package/legacy/icons/index.js +1 -0
- package/legacy/index.js +3 -2
- package/legacy/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/legacy/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/legacy/internals/index.js +4 -0
- package/legacy/internals/plugins/defaultPlugins.js +2 -2
- package/legacy/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +16 -10
- package/legacy/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +2 -5
- package/legacy/internals/plugins/useTreeViewIcons/index.js +1 -0
- package/legacy/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +21 -0
- package/legacy/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +1 -0
- package/legacy/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
- package/legacy/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
- package/legacy/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +157 -110
- package/legacy/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +20 -2
- package/legacy/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +20 -6
- package/legacy/internals/useTreeView/useTreeView.js +6 -2
- package/legacy/internals/useTreeView/useTreeViewModels.js +12 -13
- package/legacy/internals/utils/extractPluginParamsFromProps.js +36 -0
- package/modern/RichTreeView/RichTreeView.js +14 -79
- package/modern/SimpleTreeView/SimpleTreeView.js +17 -75
- package/modern/TreeItem/TreeItem.js +61 -39
- package/modern/TreeItem/TreeItemContent.js +2 -5
- package/modern/TreeItem/index.js +1 -1
- package/modern/TreeItem/{useTreeItem.js → useTreeItemState.js} +4 -2
- package/modern/TreeView/TreeView.js +1 -22
- package/modern/icons/icons.js +9 -0
- package/modern/icons/index.js +1 -0
- package/modern/index.js +3 -2
- package/modern/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/modern/internals/index.js +4 -0
- package/modern/internals/models/MuiCancellableEvent.js +1 -0
- package/modern/internals/plugins/defaultPlugins.js +2 -2
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -11
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -3
- package/modern/internals/plugins/useTreeViewIcons/index.js +1 -0
- package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +22 -0
- package/modern/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +1 -0
- package/modern/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
- package/modern/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
- package/modern/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +18 -3
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +18 -6
- package/modern/internals/useTreeView/useTreeView.js +7 -3
- package/modern/internals/useTreeView/useTreeViewModels.js +12 -13
- package/modern/internals/utils/extractPluginParamsFromProps.js +38 -0
- package/node/RichTreeView/RichTreeView.js +14 -79
- package/node/SimpleTreeView/SimpleTreeView.js +17 -75
- package/node/TreeItem/TreeItem.js +61 -39
- package/node/TreeItem/TreeItemContent.js +2 -5
- package/node/TreeItem/index.js +4 -4
- package/node/TreeItem/{useTreeItem.js → useTreeItemState.js} +5 -3
- package/node/TreeView/TreeView.js +1 -22
- package/node/icons/icons.js +17 -0
- package/node/icons/index.js +16 -0
- package/node/index.js +13 -1
- package/node/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/node/internals/index.js +33 -0
- package/node/internals/plugins/defaultPlugins.js +2 -2
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +14 -11
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -3
- package/node/internals/plugins/useTreeViewIcons/index.js +12 -0
- package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.js +29 -0
- package/node/internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js +5 -0
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +4 -1
- package/node/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
- package/node/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +18 -3
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +18 -6
- package/node/internals/useTreeView/useTreeView.js +7 -3
- package/node/internals/useTreeView/useTreeViewModels.js +12 -13
- package/node/internals/utils/extractPluginParamsFromProps.js +46 -0
- package/package.json +7 -7
- package/themeAugmentation/components.d.ts +4 -4
- package/internals/plugins/useTreeViewContextValueBuilder/index.d.ts +0 -2
- package/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
- package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.d.ts +0 -3
- package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -26
- package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.d.ts +0 -29
- package/legacy/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
- package/legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -28
- package/modern/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -1
- package/modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -26
- package/node/internals/plugins/useTreeViewContextValueBuilder/index.js +0 -12
- package/node/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +0 -33
- /package/internals/{plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → models/MuiCancellableEvent.js} +0 -0
- /package/{legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → internals/plugins/useTreeViewIcons/useTreeViewIcons.types.js} +0 -0
- /package/{modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → legacy/internals/models/MuiCancellableEvent.js} +0 -0
- /package/node/internals/{plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.types.js → models/MuiCancellableEvent.js} +0 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const useTreeViewIcons = ({
|
|
2
|
+
slots,
|
|
3
|
+
slotProps
|
|
4
|
+
}) => {
|
|
5
|
+
return {
|
|
6
|
+
contextValue: {
|
|
7
|
+
icons: {
|
|
8
|
+
slots: {
|
|
9
|
+
collapseIcon: slots.collapseIcon,
|
|
10
|
+
expandIcon: slots.expandIcon,
|
|
11
|
+
endIcon: slots.endIcon
|
|
12
|
+
},
|
|
13
|
+
slotProps: {
|
|
14
|
+
collapseIcon: slotProps.collapseIcon,
|
|
15
|
+
expandIcon: slotProps.expandIcon,
|
|
16
|
+
endIcon: slotProps.endIcon
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
useTreeViewIcons.params = {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { SlotComponentProps } from '@mui/base/utils';
|
|
3
|
+
import { TreeViewPluginSignature } from '../../models';
|
|
4
|
+
import { UseTreeViewNodesSignature } from '../useTreeViewNodes';
|
|
5
|
+
import { UseTreeViewSelectionSignature } from '../useTreeViewSelection';
|
|
6
|
+
export interface UseTreeViewIconsParameters {
|
|
7
|
+
}
|
|
8
|
+
export type UseTreeViewIconsDefaultizedParameters = UseTreeViewIconsParameters;
|
|
9
|
+
interface UseTreeViewIconsSlots {
|
|
10
|
+
/**
|
|
11
|
+
* The default icon used to collapse the node.
|
|
12
|
+
*/
|
|
13
|
+
collapseIcon?: React.ElementType;
|
|
14
|
+
/**
|
|
15
|
+
* The default icon used to expand the node.
|
|
16
|
+
*/
|
|
17
|
+
expandIcon?: React.ElementType;
|
|
18
|
+
/**
|
|
19
|
+
* The default icon displayed next to an end node.
|
|
20
|
+
* This is applied to all tree nodes and can be overridden by the TreeItem `icon` slot prop.
|
|
21
|
+
*/
|
|
22
|
+
endIcon?: React.ElementType;
|
|
23
|
+
}
|
|
24
|
+
interface UseTreeViewIconsSlotProps {
|
|
25
|
+
collapseIcon?: SlotComponentProps<'svg', {}, {}>;
|
|
26
|
+
expandIcon?: SlotComponentProps<'svg', {}, {}>;
|
|
27
|
+
endIcon?: SlotComponentProps<'svg', {}, {}>;
|
|
28
|
+
}
|
|
29
|
+
interface UseTreeViewIconsContextValue {
|
|
30
|
+
icons: {
|
|
31
|
+
slots: UseTreeViewIconsSlots;
|
|
32
|
+
slotProps: UseTreeViewIconsSlotProps;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export type UseTreeViewIconsSignature = TreeViewPluginSignature<{
|
|
36
|
+
params: UseTreeViewIconsParameters;
|
|
37
|
+
defaultizedParams: UseTreeViewIconsDefaultizedParameters;
|
|
38
|
+
contextValue: UseTreeViewIconsContextValue;
|
|
39
|
+
slots: UseTreeViewIconsSlots;
|
|
40
|
+
slotProps: UseTreeViewIconsSlotProps;
|
|
41
|
+
dependantPlugins: [UseTreeViewNodesSignature, UseTreeViewSelectionSignature];
|
|
42
|
+
}>;
|
|
43
|
+
export {};
|
|
@@ -13,5 +13,9 @@ export type UseTreeViewIdDefaultizedParameters = UseTreeViewIdParameters;
|
|
|
13
13
|
export interface UseTreeViewIdState {
|
|
14
14
|
focusedNodeId: string | null;
|
|
15
15
|
}
|
|
16
|
-
export type UseTreeViewIdSignature = TreeViewPluginSignature<
|
|
17
|
-
|
|
16
|
+
export type UseTreeViewIdSignature = TreeViewPluginSignature<{
|
|
17
|
+
params: UseTreeViewIdParameters;
|
|
18
|
+
defaultizedParams: UseTreeViewIdDefaultizedParameters;
|
|
19
|
+
instance: UseTreeViewIdInstance;
|
|
20
|
+
state: UseTreeViewIdState;
|
|
21
|
+
}>;
|
|
@@ -10,7 +10,9 @@ export interface UseTreeViewNodesParameters {
|
|
|
10
10
|
}
|
|
11
11
|
export interface UseTreeViewNodesDefaultizedParameters {
|
|
12
12
|
}
|
|
13
|
-
export type UseTreeViewJSXNodesSignature = TreeViewPluginSignature<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export type UseTreeViewJSXNodesSignature = TreeViewPluginSignature<{
|
|
14
|
+
params: UseTreeViewNodesParameters;
|
|
15
|
+
defaultizedParams: UseTreeViewNodesDefaultizedParameters;
|
|
16
|
+
instance: UseTreeViewNodesInstance;
|
|
17
|
+
dependantPlugins: [UseTreeViewNodesSignature, UseTreeViewKeyboardNavigationSignature];
|
|
18
|
+
}>;
|
|
@@ -3,7 +3,7 @@ import { useTheme } from '@mui/material/styles';
|
|
|
3
3
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
4
4
|
import { getFirstNode, getLastNode, getNextNode, getPreviousNode, populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
5
5
|
function isPrintableCharacter(string) {
|
|
6
|
-
return string && string.length === 1 && string.match(/\S/);
|
|
6
|
+
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
7
7
|
}
|
|
8
8
|
function findNextFirstChar(firstChars, startIndex, char) {
|
|
9
9
|
for (let i = startIndex; i < firstChars.length; i += 1) {
|
|
@@ -19,7 +19,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
19
19
|
state
|
|
20
20
|
}) => {
|
|
21
21
|
const theme = useTheme();
|
|
22
|
-
const
|
|
22
|
+
const isRTL = theme.direction === 'rtl';
|
|
23
23
|
const firstCharMap = React.useRef({});
|
|
24
24
|
const hasFirstCharMapBeenUpdatedImperatively = React.useRef(false);
|
|
25
25
|
const updateFirstCharMap = useEventCallback(callback => {
|
|
@@ -44,32 +44,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
44
44
|
populateInstance(instance, {
|
|
45
45
|
updateFirstCharMap
|
|
46
46
|
});
|
|
47
|
-
const
|
|
48
|
-
if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
|
|
49
|
-
if (instance.isNodeExpanded(state.focusedNodeId)) {
|
|
50
|
-
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
51
|
-
} else if (!instance.isNodeDisabled(state.focusedNodeId)) {
|
|
52
|
-
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return true;
|
|
56
|
-
};
|
|
57
|
-
const handlePreviousArrow = event => {
|
|
58
|
-
if (state.focusedNodeId == null) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
62
|
-
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
const parent = instance.getNode(state.focusedNodeId).parentId;
|
|
66
|
-
if (parent) {
|
|
67
|
-
instance.focusNode(event, parent);
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
return false;
|
|
71
|
-
};
|
|
72
|
-
const focusByFirstCharacter = (event, nodeId, firstChar) => {
|
|
47
|
+
const getFirstMatchingNode = (nodeId, firstChar) => {
|
|
73
48
|
let start;
|
|
74
49
|
let index;
|
|
75
50
|
const lowercaseChar = firstChar.toLowerCase();
|
|
@@ -100,42 +75,36 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
100
75
|
index = findNextFirstChar(firstChars, 0, lowercaseChar);
|
|
101
76
|
}
|
|
102
77
|
|
|
103
|
-
// If match was found...
|
|
78
|
+
// If a match was found...
|
|
104
79
|
if (index > -1) {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
const selectNextNode = (event, id) => {
|
|
109
|
-
if (!instance.isNodeDisabled(getNextNode(instance, id))) {
|
|
110
|
-
instance.selectRange(event, {
|
|
111
|
-
end: getNextNode(instance, id),
|
|
112
|
-
current: id
|
|
113
|
-
}, true);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
const selectPreviousNode = (event, nodeId) => {
|
|
117
|
-
if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
|
|
118
|
-
instance.selectRange(event, {
|
|
119
|
-
end: getPreviousNode(instance, nodeId),
|
|
120
|
-
current: nodeId
|
|
121
|
-
}, true);
|
|
80
|
+
return firstCharIds[index];
|
|
122
81
|
}
|
|
82
|
+
return null;
|
|
123
83
|
};
|
|
84
|
+
const canToggleNodeSelection = nodeId => !params.disableSelection && !instance.isNodeDisabled(nodeId);
|
|
85
|
+
const canToggleNodeExpansion = nodeId => !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
|
|
86
|
+
|
|
87
|
+
// ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
|
|
124
88
|
const createHandleKeyDown = otherHandlers => event => {
|
|
125
89
|
var _otherHandlers$onKeyD;
|
|
126
90
|
(_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null || _otherHandlers$onKeyD.call(otherHandlers, event);
|
|
127
|
-
|
|
128
|
-
|
|
91
|
+
if (event.defaultMuiPrevented) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
129
94
|
|
|
130
95
|
// If the tree is empty there will be no focused node
|
|
131
96
|
if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
|
|
132
97
|
return;
|
|
133
98
|
}
|
|
134
99
|
const ctrlPressed = event.ctrlKey || event.metaKey;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
100
|
+
const key = event.key;
|
|
101
|
+
|
|
102
|
+
// eslint-disable-next-line default-case
|
|
103
|
+
switch (true) {
|
|
104
|
+
// Select the node when pressing "Space"
|
|
105
|
+
case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
|
|
106
|
+
{
|
|
107
|
+
event.preventDefault();
|
|
139
108
|
if (params.multiSelect && event.shiftKey) {
|
|
140
109
|
instance.selectRange(event, {
|
|
141
110
|
end: state.focusedNodeId
|
|
@@ -145,85 +114,158 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
145
114
|
} else {
|
|
146
115
|
instance.selectNode(event, state.focusedNodeId);
|
|
147
116
|
}
|
|
117
|
+
break;
|
|
148
118
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
119
|
+
|
|
120
|
+
// If the focused node has children, we expand it.
|
|
121
|
+
// If the focused node has no children, we select it.
|
|
122
|
+
case key === 'Enter':
|
|
123
|
+
{
|
|
124
|
+
if (canToggleNodeExpansion(state.focusedNodeId)) {
|
|
154
125
|
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
155
|
-
|
|
156
|
-
} else if (
|
|
157
|
-
flag = true;
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
} else if (canToggleNodeSelection(state.focusedNodeId)) {
|
|
158
128
|
if (params.multiSelect) {
|
|
129
|
+
event.preventDefault();
|
|
159
130
|
instance.selectNode(event, state.focusedNodeId, true);
|
|
160
|
-
} else {
|
|
131
|
+
} else if (!instance.isNodeSelected(state.focusedNodeId)) {
|
|
161
132
|
instance.selectNode(event, state.focusedNodeId);
|
|
133
|
+
event.preventDefault();
|
|
162
134
|
}
|
|
163
135
|
}
|
|
136
|
+
break;
|
|
164
137
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
case 'ArrowDown':
|
|
168
|
-
|
|
169
|
-
|
|
138
|
+
|
|
139
|
+
// Focus the next focusable node
|
|
140
|
+
case key === 'ArrowDown':
|
|
141
|
+
{
|
|
142
|
+
const nextNode = getNextNode(instance, state.focusedNodeId);
|
|
143
|
+
if (nextNode) {
|
|
144
|
+
event.preventDefault();
|
|
145
|
+
instance.focusNode(event, nextNode);
|
|
146
|
+
|
|
147
|
+
// Multi select behavior when pressing Shift + ArrowDown
|
|
148
|
+
// Toggles the selection state of the next node
|
|
149
|
+
if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
|
|
150
|
+
instance.selectRange(event, {
|
|
151
|
+
end: nextNode,
|
|
152
|
+
current: state.focusedNodeId
|
|
153
|
+
}, true);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
170
157
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
158
|
+
|
|
159
|
+
// Focuses the previous focusable node
|
|
160
|
+
case key === 'ArrowUp':
|
|
161
|
+
{
|
|
162
|
+
const previousNode = getPreviousNode(instance, state.focusedNodeId);
|
|
163
|
+
if (previousNode) {
|
|
164
|
+
event.preventDefault();
|
|
165
|
+
instance.focusNode(event, previousNode);
|
|
166
|
+
|
|
167
|
+
// Multi select behavior when pressing Shift + ArrowUp
|
|
168
|
+
// Toggles the selection state of the previous node
|
|
169
|
+
if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
|
|
170
|
+
instance.selectRange(event, {
|
|
171
|
+
end: previousNode,
|
|
172
|
+
current: state.focusedNodeId
|
|
173
|
+
}, true);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
break;
|
|
177
177
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
case 'ArrowRight':
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
178
|
+
|
|
179
|
+
// If the focused node is expanded, we move the focus to its first child
|
|
180
|
+
// If the focused node is collapsed and has children, we expand it
|
|
181
|
+
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
182
|
+
{
|
|
183
|
+
if (instance.isNodeExpanded(state.focusedNodeId)) {
|
|
184
|
+
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
} else if (canToggleNodeExpansion(state.focusedNodeId)) {
|
|
187
|
+
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
188
|
+
event.preventDefault();
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
186
191
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
|
|
193
|
+
// If the focused node is expanded, we collapse it
|
|
194
|
+
// If the focused node is collapsed and has a parent, we move the focus to this parent
|
|
195
|
+
case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
|
|
196
|
+
{
|
|
197
|
+
if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
|
|
198
|
+
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
} else {
|
|
201
|
+
const parent = instance.getNode(state.focusedNodeId).parentId;
|
|
202
|
+
if (parent) {
|
|
203
|
+
instance.focusNode(event, parent);
|
|
204
|
+
event.preventDefault();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
193
208
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
|
|
210
|
+
// Focuses the first node in the tree
|
|
211
|
+
case key === 'Home':
|
|
212
|
+
{
|
|
213
|
+
instance.focusNode(event, getFirstNode(instance));
|
|
214
|
+
|
|
215
|
+
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
216
|
+
// Selects the focused node and all nodes up to the first node.
|
|
217
|
+
if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
218
|
+
instance.rangeSelectToFirst(event, state.focusedNodeId);
|
|
219
|
+
}
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
break;
|
|
198
222
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
223
|
+
|
|
224
|
+
// Focuses the last node in the tree
|
|
225
|
+
case key === 'End':
|
|
226
|
+
{
|
|
227
|
+
instance.focusNode(event, getLastNode(instance));
|
|
228
|
+
|
|
229
|
+
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
230
|
+
// Selects the focused node and all the nodes down to the last node.
|
|
231
|
+
if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
232
|
+
instance.rangeSelectToLast(event, state.focusedNodeId);
|
|
233
|
+
}
|
|
234
|
+
event.preventDefault();
|
|
235
|
+
break;
|
|
205
236
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (key === '*') {
|
|
237
|
+
|
|
238
|
+
// Expand all siblings that are at the same level as the focused node
|
|
239
|
+
case key === '*':
|
|
240
|
+
{
|
|
211
241
|
instance.expandAllSiblings(event, state.focusedNodeId);
|
|
212
|
-
|
|
213
|
-
|
|
242
|
+
event.preventDefault();
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Multi select behavior when pressing Ctrl + a
|
|
247
|
+
// Selects all the nodes
|
|
248
|
+
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
249
|
+
{
|
|
214
250
|
instance.selectRange(event, {
|
|
215
251
|
start: getFirstNode(instance),
|
|
216
252
|
end: getLastNode(instance)
|
|
217
253
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
254
|
+
event.preventDefault();
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Type-ahead
|
|
259
|
+
// TODO: Support typing multiple characters
|
|
260
|
+
case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
|
|
261
|
+
{
|
|
262
|
+
const matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
|
|
263
|
+
if (matchingNode != null) {
|
|
264
|
+
instance.focusNode(event, matchingNode);
|
|
265
|
+
event.preventDefault();
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
222
268
|
}
|
|
223
|
-
}
|
|
224
|
-
if (flag) {
|
|
225
|
-
event.preventDefault();
|
|
226
|
-
event.stopPropagation();
|
|
227
269
|
}
|
|
228
270
|
};
|
|
229
271
|
return {
|
|
@@ -231,4 +273,5 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
231
273
|
onKeyDown: createHandleKeyDown(otherHandlers)
|
|
232
274
|
})
|
|
233
275
|
};
|
|
234
|
-
};
|
|
276
|
+
};
|
|
277
|
+
useTreeViewKeyboardNavigation.params = {};
|
package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts
CHANGED
|
@@ -6,12 +6,15 @@ import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
|
6
6
|
export interface UseTreeViewKeyboardNavigationInstance {
|
|
7
7
|
updateFirstCharMap: (updater: (map: TreeViewFirstCharMap) => TreeViewFirstCharMap) => void;
|
|
8
8
|
}
|
|
9
|
-
export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
10
|
+
instance: UseTreeViewKeyboardNavigationInstance;
|
|
11
|
+
dependantPlugins: [
|
|
12
|
+
UseTreeViewNodesSignature,
|
|
13
|
+
UseTreeViewSelectionSignature,
|
|
14
|
+
UseTreeViewFocusSignature,
|
|
15
|
+
UseTreeViewExpansionSignature
|
|
16
|
+
];
|
|
17
|
+
}>;
|
|
15
18
|
export type TreeViewFirstCharMap = {
|
|
16
19
|
[nodeId: string]: string;
|
|
17
20
|
};
|
|
@@ -14,11 +14,11 @@ const updateState = ({
|
|
|
14
14
|
var _item$children, _item$children2;
|
|
15
15
|
const id = getItemId ? getItemId(item) : item.id;
|
|
16
16
|
if (id == null) {
|
|
17
|
-
throw new Error(['MUI: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
17
|
+
throw new Error(['MUI X: The Tree View component requires all items to have a unique `id` property.', 'Alternatively, you can use the `getItemId` prop to specify a custom id for each item.', 'An item was provided without id in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
18
18
|
}
|
|
19
19
|
const label = getItemLabel ? getItemLabel(item) : item.label;
|
|
20
20
|
if (label == null) {
|
|
21
|
-
throw new Error(['MUI: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
21
|
+
throw new Error(['MUI X: The Tree View component requires all items to have a `label` property.', 'Alternatively, you can use the `getItemLabel` prop to specify a custom label for each item.', 'An item was provided without label in the `items` prop:', JSON.stringify(item)].join('\n'));
|
|
22
22
|
}
|
|
23
23
|
nodeMap[id] = {
|
|
24
24
|
id,
|
|
@@ -116,10 +116,28 @@ export const useTreeViewNodes = ({
|
|
|
116
116
|
getNavigableChildrenIds,
|
|
117
117
|
isNodeDisabled
|
|
118
118
|
});
|
|
119
|
+
return {
|
|
120
|
+
contextValue: {
|
|
121
|
+
disabledItemsFocusable: params.disabledItemsFocusable
|
|
122
|
+
}
|
|
123
|
+
};
|
|
119
124
|
};
|
|
120
125
|
useTreeViewNodes.getInitialState = params => updateState({
|
|
121
126
|
items: params.items,
|
|
122
127
|
isItemDisabled: params.isItemDisabled,
|
|
123
128
|
getItemId: params.getItemId,
|
|
124
129
|
getItemLabel: params.getItemLabel
|
|
125
|
-
});
|
|
130
|
+
});
|
|
131
|
+
useTreeViewNodes.getDefaultizedParams = params => {
|
|
132
|
+
var _params$disabledItems;
|
|
133
|
+
return _extends({}, params, {
|
|
134
|
+
disabledItemsFocusable: (_params$disabledItems = params.disabledItemsFocusable) != null ? _params$disabledItems : false
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
useTreeViewNodes.params = {
|
|
138
|
+
disabledItemsFocusable: true,
|
|
139
|
+
items: true,
|
|
140
|
+
isItemDisabled: true,
|
|
141
|
+
getItemLabel: true,
|
|
142
|
+
getItemId: true
|
|
143
|
+
};
|
|
@@ -62,8 +62,16 @@ export interface UseTreeViewNodesState {
|
|
|
62
62
|
nodeTree: TreeViewNodeIdAndChildren[];
|
|
63
63
|
nodeMap: TreeViewNodeMap;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
interface UseTreeViewNodesContextValue extends Pick<UseTreeViewNodesDefaultizedParameters<any>, 'disabledItemsFocusable'> {
|
|
66
|
+
}
|
|
67
|
+
export type UseTreeViewNodesSignature = TreeViewPluginSignature<{
|
|
68
|
+
params: UseTreeViewNodesParameters<any>;
|
|
69
|
+
defaultizedParams: UseTreeViewNodesDefaultizedParameters<any>;
|
|
70
|
+
instance: UseTreeViewNodesInstance;
|
|
71
|
+
events: UseTreeViewNodesEventLookup;
|
|
72
|
+
state: UseTreeViewNodesState;
|
|
73
|
+
contextValue: UseTreeViewNodesContextValue;
|
|
74
|
+
}>;
|
|
67
75
|
export type TreeViewNodeMap = {
|
|
68
76
|
[nodeId: string]: TreeViewNode;
|
|
69
77
|
};
|
|
@@ -10,7 +10,6 @@ export const useTreeViewSelection = ({
|
|
|
10
10
|
const lastSelectedNode = React.useRef(null);
|
|
11
11
|
const lastSelectionWasRange = React.useRef(false);
|
|
12
12
|
const currentRangeSelection = React.useRef([]);
|
|
13
|
-
const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
|
|
14
13
|
const setSelectedNodes = (event, newSelectedNodes) => {
|
|
15
14
|
if (params.onNodeSelectionToggle) {
|
|
16
15
|
if (params.multiSelect) {
|
|
@@ -34,8 +33,9 @@ export const useTreeViewSelection = ({
|
|
|
34
33
|
if (params.onSelectedNodesChange) {
|
|
35
34
|
params.onSelectedNodesChange(event, newSelectedNodes);
|
|
36
35
|
}
|
|
37
|
-
models.selectedNodes.
|
|
36
|
+
models.selectedNodes.setControlledValue(newSelectedNodes);
|
|
38
37
|
};
|
|
38
|
+
const isNodeSelected = nodeId => Array.isArray(models.selectedNodes.value) ? models.selectedNodes.value.indexOf(nodeId) !== -1 : models.selectedNodes.value === nodeId;
|
|
39
39
|
const selectNode = (event, nodeId, multiple = false) => {
|
|
40
40
|
if (params.disableSelection) {
|
|
41
41
|
return;
|
|
@@ -165,13 +165,17 @@ export const useTreeViewSelection = ({
|
|
|
165
165
|
return {
|
|
166
166
|
getRootProps: () => ({
|
|
167
167
|
'aria-multiselectable': params.multiSelect
|
|
168
|
-
})
|
|
168
|
+
}),
|
|
169
|
+
contextValue: {
|
|
170
|
+
selection: {
|
|
171
|
+
multiSelect: params.multiSelect
|
|
172
|
+
}
|
|
173
|
+
}
|
|
169
174
|
};
|
|
170
175
|
};
|
|
171
176
|
useTreeViewSelection.models = {
|
|
172
177
|
selectedNodes: {
|
|
173
|
-
|
|
174
|
-
defaultProp: 'defaultSelectedNodes'
|
|
178
|
+
getDefaultValue: params => params.defaultSelectedNodes
|
|
175
179
|
}
|
|
176
180
|
};
|
|
177
181
|
const DEFAULT_SELECTED_NODES = [];
|
|
@@ -182,4 +186,12 @@ useTreeViewSelection.getDefaultizedParams = params => {
|
|
|
182
186
|
multiSelect: (_params$multiSelect = params.multiSelect) != null ? _params$multiSelect : false,
|
|
183
187
|
defaultSelectedNodes: (_params$defaultSelect = params.defaultSelectedNodes) != null ? _params$defaultSelect : params.multiSelect ? DEFAULT_SELECTED_NODES : null
|
|
184
188
|
});
|
|
189
|
+
};
|
|
190
|
+
useTreeViewSelection.params = {
|
|
191
|
+
disableSelection: true,
|
|
192
|
+
multiSelect: true,
|
|
193
|
+
defaultSelectedNodes: true,
|
|
194
|
+
selectedNodes: true,
|
|
195
|
+
onSelectedNodesChange: true,
|
|
196
|
+
onNodeSelectionToggle: true
|
|
185
197
|
};
|
|
@@ -48,9 +48,19 @@ export interface UseTreeViewSelectionParameters<Multiple extends boolean | undef
|
|
|
48
48
|
onNodeSelectionToggle?: (event: React.SyntheticEvent, nodeId: string, isSelected: boolean) => void;
|
|
49
49
|
}
|
|
50
50
|
export type UseTreeViewSelectionDefaultizedParameters<Multiple extends boolean> = DefaultizedProps<UseTreeViewSelectionParameters<Multiple>, 'disableSelection' | 'defaultSelectedNodes' | 'multiSelect'>;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
interface UseTreeViewSelectionContextValue {
|
|
52
|
+
selection: Pick<UseTreeViewSelectionDefaultizedParameters<boolean>, 'multiSelect'>;
|
|
53
|
+
}
|
|
54
|
+
export type UseTreeViewSelectionSignature = TreeViewPluginSignature<{
|
|
55
|
+
params: UseTreeViewSelectionParameters<any>;
|
|
56
|
+
defaultizedParams: UseTreeViewSelectionDefaultizedParameters<any>;
|
|
57
|
+
instance: UseTreeViewSelectionInstance;
|
|
58
|
+
contextValue: UseTreeViewSelectionContextValue;
|
|
59
|
+
modelNames: 'selectedNodes';
|
|
60
|
+
dependantPlugins: [
|
|
61
|
+
UseTreeViewNodesSignature,
|
|
62
|
+
UseTreeViewExpansionSignature,
|
|
63
|
+
UseTreeViewNodesSignature
|
|
64
|
+
];
|
|
65
|
+
}>;
|
|
56
66
|
export {};
|
|
@@ -26,11 +26,15 @@ export const useTreeView = inParams => {
|
|
|
26
26
|
return temp;
|
|
27
27
|
});
|
|
28
28
|
const rootPropsGetters = [];
|
|
29
|
-
|
|
29
|
+
const contextValue = {
|
|
30
|
+
instance: instance
|
|
31
|
+
};
|
|
30
32
|
const runPlugin = plugin => {
|
|
31
33
|
const pluginResponse = plugin({
|
|
32
34
|
instance,
|
|
33
35
|
params,
|
|
36
|
+
slots: params.slots,
|
|
37
|
+
slotProps: params.slotProps,
|
|
34
38
|
state,
|
|
35
39
|
setState,
|
|
36
40
|
rootRef: innerRootRef,
|
|
@@ -40,7 +44,7 @@ export const useTreeView = inParams => {
|
|
|
40
44
|
rootPropsGetters.push(pluginResponse.getRootProps);
|
|
41
45
|
}
|
|
42
46
|
if (pluginResponse.contextValue) {
|
|
43
|
-
contextValue
|
|
47
|
+
Object.assign(contextValue, pluginResponse.contextValue);
|
|
44
48
|
}
|
|
45
49
|
};
|
|
46
50
|
plugins.forEach(runPlugin);
|
|
@@ -96,7 +100,7 @@ export const useTreeView = inParams => {
|
|
|
96
100
|
return {
|
|
97
101
|
getRootProps,
|
|
98
102
|
rootRef: handleRootRef,
|
|
99
|
-
contextValue,
|
|
103
|
+
contextValue: contextValue,
|
|
100
104
|
instance: instance
|
|
101
105
|
};
|
|
102
106
|
};
|