@mui/x-tree-view 7.0.0-alpha.7 → 7.0.0-alpha.8
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 +175 -49
- package/RichTreeView/RichTreeView.js +18 -57
- package/SimpleTreeView/SimpleTreeView.js +20 -53
- package/TreeView/TreeView.js +1 -1
- package/index.js +1 -1
- package/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/internals/models/MuiCancellableEvent.d.ts +4 -0
- package/internals/models/MuiCancellableEvent.js +1 -0
- package/internals/models/plugin.d.ts +1 -0
- package/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +6 -0
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +6 -0
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -0
- package/internals/plugins/useTreeViewId/useTreeViewId.js +3 -0
- package/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.js +2 -1
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +155 -112
- package/internals/plugins/useTreeViewNodes/useTreeViewNodes.js +10 -3
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +8 -0
- package/internals/useTreeView/useTreeViewModels.js +2 -2
- package/internals/utils/extractPluginParamsFromProps.d.ts +13 -0
- package/internals/utils/extractPluginParamsFromProps.js +27 -0
- package/legacy/RichTreeView/RichTreeView.js +17 -55
- package/legacy/SimpleTreeView/SimpleTreeView.js +15 -47
- package/legacy/TreeView/TreeView.js +1 -1
- package/legacy/index.js +1 -1
- package/legacy/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/legacy/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/legacy/internals/models/MuiCancellableEvent.js +1 -0
- package/legacy/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +6 -0
- package/legacy/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +6 -0
- package/legacy/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +3 -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 +9 -2
- package/legacy/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +8 -0
- package/legacy/internals/useTreeView/useTreeViewModels.js +2 -2
- package/legacy/internals/utils/extractPluginParamsFromProps.js +27 -0
- package/modern/RichTreeView/RichTreeView.js +18 -57
- package/modern/SimpleTreeView/SimpleTreeView.js +20 -53
- package/modern/TreeView/TreeView.js +1 -1
- package/modern/index.js +1 -1
- package/modern/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/modern/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/modern/internals/models/MuiCancellableEvent.js +1 -0
- package/modern/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +6 -0
- package/modern/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +7 -1
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +4 -1
- 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 +10 -3
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +9 -1
- package/modern/internals/useTreeView/useTreeViewModels.js +2 -2
- package/modern/internals/utils/extractPluginParamsFromProps.js +27 -0
- package/node/RichTreeView/RichTreeView.js +18 -57
- package/node/SimpleTreeView/SimpleTreeView.js +20 -53
- package/node/TreeView/TreeView.js +1 -1
- package/node/index.js +1 -1
- package/node/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +2 -1
- package/node/internals/models/MuiCancellableEvent.js +5 -0
- package/node/internals/plugins/useTreeViewContextValueBuilder/useTreeViewContextValueBuilder.js +7 -1
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +7 -1
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +4 -1
- 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 +10 -3
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +9 -1
- package/node/internals/useTreeView/useTreeViewModels.js +2 -2
- package/node/internals/utils/extractPluginParamsFromProps.js +34 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
2
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
-
const _excluded = ["
|
|
3
|
+
const _excluded = ["slots", "slotProps"];
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import { styled, useThemeProps } from '@mui/material/styles';
|
|
@@ -12,6 +12,7 @@ import { TreeViewProvider } from '../internals/TreeViewProvider';
|
|
|
12
12
|
import { DEFAULT_TREE_VIEW_PLUGINS } from '../internals/plugins';
|
|
13
13
|
import { TreeItem } from '../TreeItem';
|
|
14
14
|
import { buildWarning } from '../internals/utils/warning';
|
|
15
|
+
import { extractPluginParamsFromProps } from '../internals/utils/extractPluginParamsFromProps';
|
|
15
16
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
17
|
const useUtilityClasses = ownerState => {
|
|
17
18
|
const {
|
|
@@ -58,7 +59,7 @@ function WrappedTreeItem({
|
|
|
58
59
|
children: children
|
|
59
60
|
}));
|
|
60
61
|
}
|
|
61
|
-
const childrenWarning = buildWarning(['MUI: The `RichTreeView` component does not support JSX children.', 'If you want to add items, you need to use the `items` prop', 'Check the documentation for more details: https://next.mui.com/x/react-tree-view/rich-tree-view/items/']);
|
|
62
|
+
const childrenWarning = buildWarning(['MUI X: The `RichTreeView` component does not support JSX children.', 'If you want to add items, you need to use the `items` prop', 'Check the documentation for more details: https://next.mui.com/x/react-tree-view/rich-tree-view/items/']);
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
65
|
*
|
|
@@ -75,75 +76,35 @@ const RichTreeView = /*#__PURE__*/React.forwardRef(function RichTreeView(inProps
|
|
|
75
76
|
props: inProps,
|
|
76
77
|
name: 'MuiRichTreeView'
|
|
77
78
|
});
|
|
78
|
-
const _ref = props,
|
|
79
|
-
{
|
|
80
|
-
// Headless implementation
|
|
81
|
-
disabledItemsFocusable,
|
|
82
|
-
expandedNodes,
|
|
83
|
-
defaultExpandedNodes,
|
|
84
|
-
onExpandedNodesChange,
|
|
85
|
-
onNodeExpansionToggle,
|
|
86
|
-
onNodeFocus,
|
|
87
|
-
disableSelection,
|
|
88
|
-
defaultSelectedNodes,
|
|
89
|
-
selectedNodes,
|
|
90
|
-
multiSelect,
|
|
91
|
-
onSelectedNodesChange,
|
|
92
|
-
onNodeSelectionToggle,
|
|
93
|
-
id: treeId,
|
|
94
|
-
defaultCollapseIcon,
|
|
95
|
-
defaultEndIcon,
|
|
96
|
-
defaultExpandIcon,
|
|
97
|
-
defaultParentIcon,
|
|
98
|
-
items,
|
|
99
|
-
getItemId,
|
|
100
|
-
getItemLabel,
|
|
101
|
-
isItemDisabled,
|
|
102
|
-
// Component implementation
|
|
103
|
-
slots,
|
|
104
|
-
slotProps
|
|
105
|
-
} = _ref,
|
|
106
|
-
other = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
107
79
|
if (process.env.NODE_ENV !== 'production') {
|
|
108
80
|
if (props.children != null) {
|
|
109
81
|
childrenWarning();
|
|
110
82
|
}
|
|
111
83
|
}
|
|
84
|
+
const _extractPluginParamsF = extractPluginParamsFromProps({
|
|
85
|
+
props,
|
|
86
|
+
plugins: DEFAULT_TREE_VIEW_PLUGINS,
|
|
87
|
+
rootRef: ref
|
|
88
|
+
}),
|
|
89
|
+
{
|
|
90
|
+
pluginParams,
|
|
91
|
+
otherProps: {
|
|
92
|
+
slots,
|
|
93
|
+
slotProps
|
|
94
|
+
}
|
|
95
|
+
} = _extractPluginParamsF,
|
|
96
|
+
otherProps = _objectWithoutPropertiesLoose(_extractPluginParamsF.otherProps, _excluded);
|
|
112
97
|
const {
|
|
113
98
|
getRootProps,
|
|
114
99
|
contextValue,
|
|
115
100
|
instance
|
|
116
|
-
} = useTreeView(
|
|
117
|
-
disabledItemsFocusable,
|
|
118
|
-
expandedNodes,
|
|
119
|
-
defaultExpandedNodes,
|
|
120
|
-
onExpandedNodesChange,
|
|
121
|
-
onNodeExpansionToggle,
|
|
122
|
-
onNodeFocus,
|
|
123
|
-
disableSelection,
|
|
124
|
-
defaultSelectedNodes,
|
|
125
|
-
selectedNodes,
|
|
126
|
-
multiSelect,
|
|
127
|
-
onSelectedNodesChange,
|
|
128
|
-
onNodeSelectionToggle,
|
|
129
|
-
id: treeId,
|
|
130
|
-
defaultCollapseIcon,
|
|
131
|
-
defaultEndIcon,
|
|
132
|
-
defaultExpandIcon,
|
|
133
|
-
defaultParentIcon,
|
|
134
|
-
items,
|
|
135
|
-
getItemId,
|
|
136
|
-
getItemLabel,
|
|
137
|
-
isItemDisabled,
|
|
138
|
-
plugins: DEFAULT_TREE_VIEW_PLUGINS,
|
|
139
|
-
rootRef: ref
|
|
140
|
-
});
|
|
101
|
+
} = useTreeView(pluginParams);
|
|
141
102
|
const classes = useUtilityClasses(props);
|
|
142
103
|
const Root = slots?.root ?? RichTreeViewRoot;
|
|
143
104
|
const rootProps = useSlotProps({
|
|
144
105
|
elementType: Root,
|
|
145
106
|
externalSlotProps: slotProps?.root,
|
|
146
|
-
externalForwardedProps:
|
|
107
|
+
externalForwardedProps: otherProps,
|
|
147
108
|
className: classes.root,
|
|
148
109
|
getSlotProps: getRootProps,
|
|
149
110
|
ownerState: props
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
3
|
-
const _excluded = ["
|
|
3
|
+
const _excluded = ["slots", "slotProps"];
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import { styled, useThemeProps } from '@mui/material/styles';
|
|
@@ -11,6 +11,7 @@ import { useTreeView } from '../internals/useTreeView';
|
|
|
11
11
|
import { TreeViewProvider } from '../internals/TreeViewProvider';
|
|
12
12
|
import { SIMPLE_TREE_VIEW_PLUGINS } from './SimpleTreeView.plugins';
|
|
13
13
|
import { buildWarning } from '../internals/utils/warning';
|
|
14
|
+
import { extractPluginParamsFromProps } from '../internals/utils/extractPluginParamsFromProps';
|
|
14
15
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
16
|
const useUtilityClasses = ownerState => {
|
|
16
17
|
const {
|
|
@@ -32,7 +33,7 @@ export const SimpleTreeViewRoot = styled('ul', {
|
|
|
32
33
|
outline: 0
|
|
33
34
|
});
|
|
34
35
|
const EMPTY_ITEMS = [];
|
|
35
|
-
const itemsPropWarning = buildWarning(['MUI: The `SimpleTreeView` component does not support the `items` prop.', 'If you want to add items, you need to pass them as JSX children.', 'Check the documentation for more details: https://next.mui.com/x/react-tree-view/simple-tree-view/items/']);
|
|
36
|
+
const itemsPropWarning = buildWarning(['MUI X: The `SimpleTreeView` component does not support the `items` prop.', 'If you want to add items, you need to pass them as JSX children.', 'Check the documentation for more details: https://next.mui.com/x/react-tree-view/simple-tree-view/items/']);
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
*
|
|
@@ -50,76 +51,42 @@ const SimpleTreeView = /*#__PURE__*/React.forwardRef(function SimpleTreeView(inP
|
|
|
50
51
|
name: 'MuiSimpleTreeView'
|
|
51
52
|
});
|
|
52
53
|
const ownerState = props;
|
|
53
|
-
const _ref = props,
|
|
54
|
-
{
|
|
55
|
-
// Headless implementation
|
|
56
|
-
disabledItemsFocusable,
|
|
57
|
-
expandedNodes,
|
|
58
|
-
defaultExpandedNodes,
|
|
59
|
-
onExpandedNodesChange,
|
|
60
|
-
onNodeExpansionToggle,
|
|
61
|
-
onNodeFocus,
|
|
62
|
-
disableSelection,
|
|
63
|
-
defaultSelectedNodes,
|
|
64
|
-
selectedNodes,
|
|
65
|
-
multiSelect,
|
|
66
|
-
onSelectedNodesChange,
|
|
67
|
-
onNodeSelectionToggle,
|
|
68
|
-
id,
|
|
69
|
-
defaultCollapseIcon,
|
|
70
|
-
defaultEndIcon,
|
|
71
|
-
defaultExpandIcon,
|
|
72
|
-
defaultParentIcon,
|
|
73
|
-
// Component implementation
|
|
74
|
-
children,
|
|
75
|
-
slots
|
|
76
|
-
} = _ref,
|
|
77
|
-
other = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
78
54
|
if (process.env.NODE_ENV !== 'production') {
|
|
79
55
|
if (props.items != null) {
|
|
80
56
|
itemsPropWarning();
|
|
81
57
|
}
|
|
82
58
|
}
|
|
59
|
+
const _extractPluginParamsF = extractPluginParamsFromProps({
|
|
60
|
+
props: _extends({}, props, {
|
|
61
|
+
items: EMPTY_ITEMS
|
|
62
|
+
}),
|
|
63
|
+
plugins: SIMPLE_TREE_VIEW_PLUGINS,
|
|
64
|
+
rootRef: ref
|
|
65
|
+
}),
|
|
66
|
+
{
|
|
67
|
+
pluginParams,
|
|
68
|
+
otherProps: {
|
|
69
|
+
slots
|
|
70
|
+
}
|
|
71
|
+
} = _extractPluginParamsF,
|
|
72
|
+
otherProps = _objectWithoutPropertiesLoose(_extractPluginParamsF.otherProps, _excluded);
|
|
83
73
|
const {
|
|
84
74
|
getRootProps,
|
|
85
75
|
contextValue
|
|
86
|
-
} = useTreeView(
|
|
87
|
-
disabledItemsFocusable,
|
|
88
|
-
expandedNodes,
|
|
89
|
-
defaultExpandedNodes,
|
|
90
|
-
onExpandedNodesChange,
|
|
91
|
-
onNodeExpansionToggle,
|
|
92
|
-
onNodeFocus,
|
|
93
|
-
disableSelection,
|
|
94
|
-
defaultSelectedNodes,
|
|
95
|
-
selectedNodes,
|
|
96
|
-
multiSelect,
|
|
97
|
-
onSelectedNodesChange,
|
|
98
|
-
onNodeSelectionToggle,
|
|
99
|
-
id,
|
|
100
|
-
defaultCollapseIcon,
|
|
101
|
-
defaultEndIcon,
|
|
102
|
-
defaultExpandIcon,
|
|
103
|
-
defaultParentIcon,
|
|
104
|
-
items: EMPTY_ITEMS,
|
|
105
|
-
plugins: SIMPLE_TREE_VIEW_PLUGINS,
|
|
106
|
-
rootRef: ref
|
|
107
|
-
});
|
|
76
|
+
} = useTreeView(pluginParams);
|
|
108
77
|
const classes = useUtilityClasses(props);
|
|
109
78
|
const Root = slots?.root ?? SimpleTreeViewRoot;
|
|
110
79
|
const rootProps = useSlotProps({
|
|
111
80
|
elementType: Root,
|
|
112
81
|
externalSlotProps: {},
|
|
113
|
-
externalForwardedProps:
|
|
82
|
+
externalForwardedProps: otherProps,
|
|
114
83
|
className: classes.root,
|
|
115
84
|
getSlotProps: getRootProps,
|
|
116
85
|
ownerState
|
|
117
86
|
});
|
|
118
87
|
return /*#__PURE__*/_jsx(TreeViewProvider, {
|
|
119
88
|
value: contextValue,
|
|
120
|
-
children: /*#__PURE__*/_jsx(Root, _extends({}, rootProps
|
|
121
|
-
children: children
|
|
122
|
-
}))
|
|
89
|
+
children: /*#__PURE__*/_jsx(Root, _extends({}, rootProps))
|
|
123
90
|
});
|
|
124
91
|
});
|
|
125
92
|
process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
|
|
@@ -23,7 +23,7 @@ const TreeViewRoot = styled(SimpleTreeViewRoot, {
|
|
|
23
23
|
let warnedOnce = false;
|
|
24
24
|
const warn = () => {
|
|
25
25
|
if (!warnedOnce) {
|
|
26
|
-
console.warn(['MUI: The TreeView component was renamed SimpleTreeView.', 'The component with the old naming will be removed in the version v8.0.0.', '', "You should use `import { SimpleTreeView } from '@mui/x-tree-view'`", "or `import { SimpleTreeView } from '@mui/x-tree-view/TreeView'`"].join('\n'));
|
|
26
|
+
console.warn(['MUI X: The TreeView component was renamed SimpleTreeView.', 'The component with the old naming will be removed in the version v8.0.0.', '', "You should use `import { SimpleTreeView } from '@mui/x-tree-view'`", "or `import { SimpleTreeView } from '@mui/x-tree-view/TreeView'`"].join('\n'));
|
|
27
27
|
warnedOnce = true;
|
|
28
28
|
}
|
|
29
29
|
};
|
package/modern/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { TreeViewContext } from './TreeViewContext';
|
|
|
3
3
|
export const useTreeViewContext = () => {
|
|
4
4
|
const context = React.useContext(TreeViewContext);
|
|
5
5
|
if (context == null) {
|
|
6
|
-
throw new Error(['MUI: Could not find the Tree View context.', 'It looks like you rendered your component outside of a SimpleTreeView or RichTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
|
|
6
|
+
throw new Error(['MUI X: Could not find the Tree View context.', 'It looks like you rendered your component outside of a SimpleTreeView or RichTreeView parent component.', 'This can also happen if you are bundling multiple versions of the Tree View.'].join('\n'));
|
|
7
7
|
}
|
|
8
8
|
return context;
|
|
9
9
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -63,4 +63,10 @@ useTreeViewExpansion.models = {
|
|
|
63
63
|
const DEFAULT_EXPANDED_NODES = [];
|
|
64
64
|
useTreeViewExpansion.getDefaultizedParams = params => _extends({}, params, {
|
|
65
65
|
defaultExpandedNodes: params.defaultExpandedNodes ?? DEFAULT_EXPANDED_NODES
|
|
66
|
-
});
|
|
66
|
+
});
|
|
67
|
+
useTreeViewExpansion.params = {
|
|
68
|
+
expandedNodes: true,
|
|
69
|
+
defaultExpandedNodes: true,
|
|
70
|
+
onExpandedNodesChange: true,
|
|
71
|
+
onNodeExpansionToggle: true
|
|
72
|
+
};
|
|
@@ -87,4 +87,7 @@ useTreeViewFocus.getInitialState = () => ({
|
|
|
87
87
|
});
|
|
88
88
|
useTreeViewFocus.getDefaultizedParams = params => _extends({}, params, {
|
|
89
89
|
disabledItemsFocusable: params.disabledItemsFocusable ?? false
|
|
90
|
-
});
|
|
90
|
+
});
|
|
91
|
+
useTreeViewFocus.params = {
|
|
92
|
+
onNodeFocus: true
|
|
93
|
+
};
|
package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -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 => {
|
|
@@ -43,32 +43,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
43
43
|
populateInstance(instance, {
|
|
44
44
|
updateFirstCharMap
|
|
45
45
|
});
|
|
46
|
-
const
|
|
47
|
-
if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
|
|
48
|
-
if (instance.isNodeExpanded(state.focusedNodeId)) {
|
|
49
|
-
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
50
|
-
} else if (!instance.isNodeDisabled(state.focusedNodeId)) {
|
|
51
|
-
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
const handlePreviousArrow = event => {
|
|
57
|
-
if (state.focusedNodeId == null) {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
61
|
-
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
const parent = instance.getNode(state.focusedNodeId).parentId;
|
|
65
|
-
if (parent) {
|
|
66
|
-
instance.focusNode(event, parent);
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
};
|
|
71
|
-
const focusByFirstCharacter = (event, nodeId, firstChar) => {
|
|
46
|
+
const getFirstMatchingNode = (nodeId, firstChar) => {
|
|
72
47
|
let start;
|
|
73
48
|
let index;
|
|
74
49
|
const lowercaseChar = firstChar.toLowerCase();
|
|
@@ -99,41 +74,35 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
99
74
|
index = findNextFirstChar(firstChars, 0, lowercaseChar);
|
|
100
75
|
}
|
|
101
76
|
|
|
102
|
-
// If match was found...
|
|
77
|
+
// If a match was found...
|
|
103
78
|
if (index > -1) {
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
const selectNextNode = (event, id) => {
|
|
108
|
-
if (!instance.isNodeDisabled(getNextNode(instance, id))) {
|
|
109
|
-
instance.selectRange(event, {
|
|
110
|
-
end: getNextNode(instance, id),
|
|
111
|
-
current: id
|
|
112
|
-
}, true);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const selectPreviousNode = (event, nodeId) => {
|
|
116
|
-
if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
|
|
117
|
-
instance.selectRange(event, {
|
|
118
|
-
end: getPreviousNode(instance, nodeId),
|
|
119
|
-
current: nodeId
|
|
120
|
-
}, true);
|
|
79
|
+
return firstCharIds[index];
|
|
121
80
|
}
|
|
81
|
+
return null;
|
|
122
82
|
};
|
|
83
|
+
const canToggleNodeSelection = nodeId => !params.disableSelection && !instance.isNodeDisabled(nodeId);
|
|
84
|
+
const canToggleNodeExpansion = nodeId => !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId);
|
|
85
|
+
|
|
86
|
+
// ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction
|
|
123
87
|
const createHandleKeyDown = otherHandlers => event => {
|
|
124
88
|
otherHandlers.onKeyDown?.(event);
|
|
125
|
-
|
|
126
|
-
|
|
89
|
+
if (event.defaultMuiPrevented) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
127
92
|
|
|
128
93
|
// If the tree is empty there will be no focused node
|
|
129
94
|
if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
|
|
130
95
|
return;
|
|
131
96
|
}
|
|
132
97
|
const ctrlPressed = event.ctrlKey || event.metaKey;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
98
|
+
const key = event.key;
|
|
99
|
+
|
|
100
|
+
// eslint-disable-next-line default-case
|
|
101
|
+
switch (true) {
|
|
102
|
+
// Select the node when pressing "Space"
|
|
103
|
+
case key === ' ' && canToggleNodeSelection(state.focusedNodeId):
|
|
104
|
+
{
|
|
105
|
+
event.preventDefault();
|
|
137
106
|
if (params.multiSelect && event.shiftKey) {
|
|
138
107
|
instance.selectRange(event, {
|
|
139
108
|
end: state.focusedNodeId
|
|
@@ -143,85 +112,158 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
143
112
|
} else {
|
|
144
113
|
instance.selectNode(event, state.focusedNodeId);
|
|
145
114
|
}
|
|
115
|
+
break;
|
|
146
116
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
117
|
+
|
|
118
|
+
// If the focused node has children, we expand it.
|
|
119
|
+
// If the focused node has no children, we select it.
|
|
120
|
+
case key === 'Enter':
|
|
121
|
+
{
|
|
122
|
+
if (canToggleNodeExpansion(state.focusedNodeId)) {
|
|
152
123
|
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
153
|
-
|
|
154
|
-
} else if (
|
|
155
|
-
flag = true;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
} else if (canToggleNodeSelection(state.focusedNodeId)) {
|
|
156
126
|
if (params.multiSelect) {
|
|
127
|
+
event.preventDefault();
|
|
157
128
|
instance.selectNode(event, state.focusedNodeId, true);
|
|
158
|
-
} else {
|
|
129
|
+
} else if (!instance.isNodeSelected(state.focusedNodeId)) {
|
|
159
130
|
instance.selectNode(event, state.focusedNodeId);
|
|
131
|
+
event.preventDefault();
|
|
160
132
|
}
|
|
161
133
|
}
|
|
134
|
+
break;
|
|
162
135
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
case 'ArrowDown':
|
|
166
|
-
|
|
167
|
-
|
|
136
|
+
|
|
137
|
+
// Focus the next focusable node
|
|
138
|
+
case key === 'ArrowDown':
|
|
139
|
+
{
|
|
140
|
+
const nextNode = getNextNode(instance, state.focusedNodeId);
|
|
141
|
+
if (nextNode) {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
instance.focusNode(event, nextNode);
|
|
144
|
+
|
|
145
|
+
// Multi select behavior when pressing Shift + ArrowDown
|
|
146
|
+
// Toggles the selection state of the next node
|
|
147
|
+
if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) {
|
|
148
|
+
instance.selectRange(event, {
|
|
149
|
+
end: nextNode,
|
|
150
|
+
current: state.focusedNodeId
|
|
151
|
+
}, true);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
168
155
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
156
|
+
|
|
157
|
+
// Focuses the previous focusable node
|
|
158
|
+
case key === 'ArrowUp':
|
|
159
|
+
{
|
|
160
|
+
const previousNode = getPreviousNode(instance, state.focusedNodeId);
|
|
161
|
+
if (previousNode) {
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
instance.focusNode(event, previousNode);
|
|
164
|
+
|
|
165
|
+
// Multi select behavior when pressing Shift + ArrowUp
|
|
166
|
+
// Toggles the selection state of the previous node
|
|
167
|
+
if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) {
|
|
168
|
+
instance.selectRange(event, {
|
|
169
|
+
end: previousNode,
|
|
170
|
+
current: state.focusedNodeId
|
|
171
|
+
}, true);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
break;
|
|
175
175
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
case 'ArrowRight':
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
176
|
+
|
|
177
|
+
// If the focused node is expanded, we move the focus to its first child
|
|
178
|
+
// If the focused node is collapsed and has children, we expand it
|
|
179
|
+
case key === 'ArrowRight' && !isRTL || key === 'ArrowLeft' && isRTL:
|
|
180
|
+
{
|
|
181
|
+
if (instance.isNodeExpanded(state.focusedNodeId)) {
|
|
182
|
+
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
} else if (canToggleNodeExpansion(state.focusedNodeId)) {
|
|
185
|
+
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
184
189
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
|
|
191
|
+
// If the focused node is expanded, we collapse it
|
|
192
|
+
// If the focused node is collapsed and has a parent, we move the focus to this parent
|
|
193
|
+
case key === 'ArrowLeft' && !isRTL || key === 'ArrowRight' && isRTL:
|
|
194
|
+
{
|
|
195
|
+
if (canToggleNodeExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId)) {
|
|
196
|
+
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
} else {
|
|
199
|
+
const parent = instance.getNode(state.focusedNodeId).parentId;
|
|
200
|
+
if (parent) {
|
|
201
|
+
instance.focusNode(event, parent);
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
191
206
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
207
|
+
|
|
208
|
+
// Focuses the first node in the tree
|
|
209
|
+
case key === 'Home':
|
|
210
|
+
{
|
|
211
|
+
instance.focusNode(event, getFirstNode(instance));
|
|
212
|
+
|
|
213
|
+
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
214
|
+
// Selects the focused node and all nodes up to the first node.
|
|
215
|
+
if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
216
|
+
instance.rangeSelectToFirst(event, state.focusedNodeId);
|
|
217
|
+
}
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
break;
|
|
196
220
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
221
|
+
|
|
222
|
+
// Focuses the last node in the tree
|
|
223
|
+
case key === 'End':
|
|
224
|
+
{
|
|
225
|
+
instance.focusNode(event, getLastNode(instance));
|
|
226
|
+
|
|
227
|
+
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
228
|
+
// Selects the focused node and all the nodes down to the last node.
|
|
229
|
+
if (canToggleNodeSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
230
|
+
instance.rangeSelectToLast(event, state.focusedNodeId);
|
|
231
|
+
}
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
break;
|
|
203
234
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (key === '*') {
|
|
235
|
+
|
|
236
|
+
// Expand all siblings that are at the same level as the focused node
|
|
237
|
+
case key === '*':
|
|
238
|
+
{
|
|
209
239
|
instance.expandAllSiblings(event, state.focusedNodeId);
|
|
210
|
-
|
|
211
|
-
|
|
240
|
+
event.preventDefault();
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Multi select behavior when pressing Ctrl + a
|
|
245
|
+
// Selects all the nodes
|
|
246
|
+
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
247
|
+
{
|
|
212
248
|
instance.selectRange(event, {
|
|
213
249
|
start: getFirstNode(instance),
|
|
214
250
|
end: getLastNode(instance)
|
|
215
251
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
252
|
+
event.preventDefault();
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Type-ahead
|
|
257
|
+
// TODO: Support typing multiple characters
|
|
258
|
+
case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key):
|
|
259
|
+
{
|
|
260
|
+
const matchingNode = getFirstMatchingNode(state.focusedNodeId, key);
|
|
261
|
+
if (matchingNode != null) {
|
|
262
|
+
instance.focusNode(event, matchingNode);
|
|
263
|
+
event.preventDefault();
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
220
266
|
}
|
|
221
|
-
}
|
|
222
|
-
if (flag) {
|
|
223
|
-
event.preventDefault();
|
|
224
|
-
event.stopPropagation();
|
|
225
267
|
}
|
|
226
268
|
};
|
|
227
269
|
return {
|
|
@@ -229,4 +271,5 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
229
271
|
onKeyDown: createHandleKeyDown(otherHandlers)
|
|
230
272
|
})
|
|
231
273
|
};
|
|
232
|
-
};
|
|
274
|
+
};
|
|
275
|
+
useTreeViewKeyboardNavigation.params = {};
|