@mui/x-tree-view 8.14.0 → 8.15.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 +202 -0
- package/RichTreeView/RichTreeView.js +2 -2
- package/SimpleTreeView/SimpleTreeView.js +2 -2
- package/TreeItemIcon/TreeItemIcon.js +11 -8
- package/esm/RichTreeView/RichTreeView.js +2 -2
- package/esm/SimpleTreeView/SimpleTreeView.js +2 -2
- package/esm/TreeItemIcon/TreeItemIcon.js +9 -6
- package/esm/index.js +1 -1
- package/esm/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +1 -1
- package/esm/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +8 -8
- package/esm/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +5 -5
- package/esm/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +50 -20
- package/esm/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +4 -4
- package/esm/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +1 -1
- package/index.js +1 -1
- package/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.d.ts +1 -1
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +8 -8
- package/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.types.d.ts +5 -5
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +50 -20
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts +4 -4
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,208 @@
|
|
|
5
5
|
All notable changes to this project will be documented in this file.
|
|
6
6
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
7
7
|
|
|
8
|
+
## 8.15.0
|
|
9
|
+
|
|
10
|
+
_Oct 23, 2025_
|
|
11
|
+
|
|
12
|
+
We'd like to extend a big thank you to the 14 contributors who made this release possible. Here are some highlights ✨:
|
|
13
|
+
|
|
14
|
+
- 🖌️ Add new [`brush` charts interaction](https://mui.com/x/react-charts/brush/) for building custom behavior.
|
|
15
|
+

|
|
16
|
+
- ⚡️ Performance improvements for large bar charts
|
|
17
|
+
- 🤖 Data Grid AI assistant can now [visualize the query results](https://mui.com/x/react-data-grid/ai-assistant/#data-visualization) by controlling the chart integration settings
|
|
18
|
+
- 📦 DataGrid uses an internal MUI fork of ExcelJS that does not depend on vulnerable versions of NPM packages
|
|
19
|
+
- 🐞 Bugfixes
|
|
20
|
+
- 📚 Documentation improvements
|
|
21
|
+
|
|
22
|
+
Special thanks go out to the community members for their valuable contributions:
|
|
23
|
+
@ZagrebaAlex
|
|
24
|
+
|
|
25
|
+
The following are all team members who have contributed to this release:
|
|
26
|
+
@alexfauquette, @bernardobelchior, @cherniavskii, @flaviendelangle, @Janpot, @JCQuintas, @KenanYusuf, @prakhargupta1, @rita-codes, @siriwatknp, @arminmeh, @brijeshb42, @noraleonte
|
|
27
|
+
|
|
28
|
+
### Data Grid
|
|
29
|
+
|
|
30
|
+
#### `@mui/x-data-grid@8.15.0`
|
|
31
|
+
|
|
32
|
+
- [DataGrid] Fix `dataSource.fetchRows` API's return type (#20068) @arminmeh
|
|
33
|
+
|
|
34
|
+
#### `@mui/x-data-grid-pro@8.15.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
35
|
+
|
|
36
|
+
Same changes as in `@mui/x-data-grid@8.15.0`, plus:
|
|
37
|
+
|
|
38
|
+
- [DataGridPro] Keep children in the tree after parent row is re-fetched with the data source (#19934) @arminmeh
|
|
39
|
+
- [DataGridPro] Support scroll shadows customization (#19982) @KenanYusuf
|
|
40
|
+
|
|
41
|
+
#### `@mui/x-data-grid-premium@8.15.0` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
42
|
+
|
|
43
|
+
Same changes as in `@mui/x-data-grid-pro@8.15.0`, plus:
|
|
44
|
+
|
|
45
|
+
- [DataGridPremium] Use ExcelJS fork (#19796) @cherniavskii
|
|
46
|
+
- [DataGridPremium] Support data visualization in AI Assistant (#19831) @arminmeh
|
|
47
|
+
|
|
48
|
+
### Date and Time Pickers
|
|
49
|
+
|
|
50
|
+
#### `@mui/x-date-pickers@8.15.0`
|
|
51
|
+
|
|
52
|
+
Internal changes.
|
|
53
|
+
|
|
54
|
+
#### `@mui/x-date-pickers-pro@8.15.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
55
|
+
|
|
56
|
+
Same changes as in `@mui/x-date-pickers@8.15.0`.
|
|
57
|
+
|
|
58
|
+
### Charts
|
|
59
|
+
|
|
60
|
+
#### `@mui/x-charts@8.15.0`
|
|
61
|
+
|
|
62
|
+
- [charts] Add `ChartsBrushOverlay` and allow brush configuration (#19956) @JCQuintas
|
|
63
|
+
- [charts] Add `getStringSize` benchmark. Remove benchmarks from built package. (#19995) @bernardobelchior
|
|
64
|
+
- [charts] Batch string size measurement (#19994) @bernardobelchior
|
|
65
|
+
- [charts] Fix console issue (#20025) @JCQuintas
|
|
66
|
+
- [charts] Fix is[ZoomFeature]Enabled type (#20058) @alexfauquette
|
|
67
|
+
- [charts] Fix reference line middle spacing (#20004) @JCQuintas
|
|
68
|
+
- [charts] Improve `getStringSize` and `batchMeasureStrings` performance (#19996) @bernardobelchior
|
|
69
|
+
- [charts] Improve deep export script (#20007) @JCQuintas
|
|
70
|
+
- [charts] Improve string measurement benchmarks (#19999) @bernardobelchior
|
|
71
|
+
- [charts] Measure string sizes using SVG elements (#19981) @bernardobelchior
|
|
72
|
+
- [l10n] Improve Greek (gr-GR) locale (#20060) @ZagrebaAlex
|
|
73
|
+
|
|
74
|
+
#### `@mui/x-charts-pro@8.15.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
75
|
+
|
|
76
|
+
Same changes as in `@mui/x-charts@8.15.0`, plus:
|
|
77
|
+
|
|
78
|
+
- [charts-pro] Fix pan with `axis.reverse` (#20031) @JCQuintas
|
|
79
|
+
|
|
80
|
+
#### `@mui/x-charts-premium@8.15.0` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
81
|
+
|
|
82
|
+
Same changes as in `@mui/x-charts-pro@8.15.0`.
|
|
83
|
+
|
|
84
|
+
### Tree View
|
|
85
|
+
|
|
86
|
+
#### `@mui/x-tree-view@8.15.0`
|
|
87
|
+
|
|
88
|
+
- [tree view] Multi character type-ahead (#19942) @noraleonte
|
|
89
|
+
|
|
90
|
+
#### `@mui/x-tree-view-pro@8.15.0` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
91
|
+
|
|
92
|
+
Same changes as in `@mui/x-tree-view@8.15.0`.
|
|
93
|
+
|
|
94
|
+
### Codemod
|
|
95
|
+
|
|
96
|
+
#### `@mui/x-codemod@8.14.0`
|
|
97
|
+
|
|
98
|
+
Internal changes.
|
|
99
|
+
|
|
100
|
+
### Docs
|
|
101
|
+
|
|
102
|
+
- [docs] Add overview section for scatter chart and heatmap (#19888) @prakhargupta1
|
|
103
|
+
- [docs] Add charts bell curve example (#20003) @JCQuintas
|
|
104
|
+
- [docs] Add grouped multiple fields for Data Grid row grouping recipe (#19964) @siriwatknp
|
|
105
|
+
- [docs] Add Data Grid loading state recipe (#19958) @siriwatknp
|
|
106
|
+
|
|
107
|
+
### Core
|
|
108
|
+
|
|
109
|
+
- [code-infra] Remove @mui/monorepo usage for react versioning (#19894) @Janpot
|
|
110
|
+
- [code-infra] Remove invalid `environment: 'browser'` from vitest browser config (#19993) @bernardobelchior
|
|
111
|
+
- [code-infra] Remove unused babel aliases (#19987) @Janpot
|
|
112
|
+
- [code-infra] Turn on all testing-library eslint rules (#19946) @brijeshb42
|
|
113
|
+
- [docs-infra] Fix broken hash link (#20062) @Janpot
|
|
114
|
+
|
|
115
|
+
## 8.14.1
|
|
116
|
+
|
|
117
|
+
_Oct 16, 2025_
|
|
118
|
+
|
|
119
|
+
We'd like to extend a big thank you to the 14 contributors who made this release possible. Here are some highlights ✨:
|
|
120
|
+
|
|
121
|
+
- 🚀 Charts have optimized data structures for closest point calculations — initial render times reduced by ~25% for 1,000+ data points, with greater gains at larger scales (#19790) @bernardobelchior
|
|
122
|
+
- 🐞 Bugfixes
|
|
123
|
+
- 📚 Documentation improvements
|
|
124
|
+
|
|
125
|
+
Special thanks go out to the community members for their valuable contributions:
|
|
126
|
+
@djpremier, @jacknot, @justdoit1897, @mellis481, @sai6855
|
|
127
|
+
|
|
128
|
+
The following are all team members who have contributed to this release:
|
|
129
|
+
@arminmeh, @bernardobelchior, @brijeshb42, @cherniavskii, @flaviendelangle, @Janpot, @JCQuintas, @noraleonte, @siriwatknp
|
|
130
|
+
|
|
131
|
+
### Data Grid
|
|
132
|
+
|
|
133
|
+
#### `@mui/x-data-grid@8.14.1`
|
|
134
|
+
|
|
135
|
+
- [DataGrid] Fix cell not rerendering on `isCellEditable` prop change (#19898) @cherniavskii
|
|
136
|
+
- [DataGrid] Fix virtualizer memory leaks (#19886) @cherniavskii
|
|
137
|
+
- [DataGrid] Fix tree data unable to deselect row for exclude model (#19846) @siriwatknp
|
|
138
|
+
- [l10n] Improve Italian (it-IT) locale (#19322) @jacknot and (#19940) @justdoit1897
|
|
139
|
+
|
|
140
|
+
#### `@mui/x-data-grid-pro@8.14.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
141
|
+
|
|
142
|
+
Same changes as in `@mui/x-data-grid@8.14.1`, plus:
|
|
143
|
+
|
|
144
|
+
- [DataGridPro] Clear cache before new request to the nested request queue after a row has been edited (#19873) @arminmeh
|
|
145
|
+
|
|
146
|
+
#### `@mui/x-data-grid-premium@8.14.1` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
147
|
+
|
|
148
|
+
Same changes as in `@mui/x-data-grid-pro@8.14.1`.
|
|
149
|
+
|
|
150
|
+
### Date and Time Pickers
|
|
151
|
+
|
|
152
|
+
#### `@mui/x-date-pickers@8.14.1`
|
|
153
|
+
|
|
154
|
+
Internal changes.
|
|
155
|
+
|
|
156
|
+
#### `@mui/x-date-pickers-pro@8.14.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
157
|
+
|
|
158
|
+
Same changes as in `@mui/x-date-pickers@8.14.1`.
|
|
159
|
+
|
|
160
|
+
### Charts
|
|
161
|
+
|
|
162
|
+
#### `@mui/x-charts@8.14.1`
|
|
163
|
+
|
|
164
|
+
- [charts] Fix `minBarSize` when y-axis is reversed (#19932) @bernardobelchior
|
|
165
|
+
- [charts] Fix bar chart border radius when axis is reversed (#19895) @bernardobelchior
|
|
166
|
+
- [charts] Fix scatter chart `datasetKeys.id` not being optional (#19897) @bernardobelchior
|
|
167
|
+
- [charts] Use more performant data structure for closest point (#19790) @bernardobelchior
|
|
168
|
+
- [charts] Fix `GaugeValueArc` having wrong class (#19965) @bernardobelchior
|
|
169
|
+
- [charts] Fix `undefined` path when highlight empty line chart axis (#19969) @bernardobelchior
|
|
170
|
+
|
|
171
|
+
#### `@mui/x-charts-pro@8.14.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
172
|
+
|
|
173
|
+
Same changes as in `@mui/x-charts@8.14.1`, plus:
|
|
174
|
+
|
|
175
|
+
- [charts-pro] Add `highlighting` to Sankey chart (#19662) @JCQuintas
|
|
176
|
+
|
|
177
|
+
#### `@mui/x-charts-premium@8.14.1` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
178
|
+
|
|
179
|
+
Same changes as in `@mui/x-charts-pro@8.14.1`.
|
|
180
|
+
|
|
181
|
+
### Tree View
|
|
182
|
+
|
|
183
|
+
#### `@mui/x-tree-view@8.14.1`
|
|
184
|
+
|
|
185
|
+
- [tree view] Do not forward the `ownerState` to the icon (#19772) @flaviendelangle
|
|
186
|
+
|
|
187
|
+
#### `@mui/x-tree-view-pro@8.14.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
188
|
+
|
|
189
|
+
Same changes as in `@mui/x-tree-view@8.14.1`.
|
|
190
|
+
|
|
191
|
+
### Codemod
|
|
192
|
+
|
|
193
|
+
#### `@mui/x-codemod@8.14.0`
|
|
194
|
+
|
|
195
|
+
Internal changes.
|
|
196
|
+
|
|
197
|
+
### Docs
|
|
198
|
+
|
|
199
|
+
- [docs] Add `'bumpX'` and `'bumpY'` curve types to the interpolation demo (#19676) @djpremier
|
|
200
|
+
- [docs] Add scatter chart with linear regression demo (#19900) @bernardobelchior
|
|
201
|
+
- [docs] Correctly describe Data Grid's row selection behavior (#19968) @arminmeh
|
|
202
|
+
- [docs] Fix `isExpanded` type in tree view docs (#19092) @mellis481
|
|
203
|
+
|
|
204
|
+
### Core
|
|
205
|
+
|
|
206
|
+
- [code-infra] Disable Netlify cache plugin (#19950) @Janpot
|
|
207
|
+
- [code-infra] Lint json through eslint (#19890) @Janpot
|
|
208
|
+
- [docs-infra] Use published netlify cache plugin package (#19929) @brijeshb42
|
|
209
|
+
|
|
8
210
|
## 8.14.0
|
|
9
211
|
|
|
10
212
|
_Oct 9, 2025_
|
|
@@ -267,7 +267,7 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
267
267
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
268
268
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
269
269
|
* @param {array} itemId The itemId of the modified item.
|
|
270
|
-
* @param {
|
|
270
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
271
271
|
*/
|
|
272
272
|
onItemExpansionToggle: _propTypes.default.func,
|
|
273
273
|
/**
|
|
@@ -286,7 +286,7 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
286
286
|
* Callback fired when a Tree Item is selected or deselected.
|
|
287
287
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
288
288
|
* @param {array} itemId The itemId of the modified item.
|
|
289
|
-
* @param {
|
|
289
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
290
290
|
*/
|
|
291
291
|
onItemSelectionToggle: _propTypes.default.func,
|
|
292
292
|
/**
|
|
@@ -207,7 +207,7 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
|
|
|
207
207
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
208
208
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
209
209
|
* @param {array} itemId The itemId of the modified item.
|
|
210
|
-
* @param {
|
|
210
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
211
211
|
*/
|
|
212
212
|
onItemExpansionToggle: _propTypes.default.func,
|
|
213
213
|
/**
|
|
@@ -220,7 +220,7 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
|
|
|
220
220
|
* Callback fired when a Tree Item is selected or deselected.
|
|
221
221
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
222
222
|
* @param {array} itemId The itemId of the modified item.
|
|
223
|
-
* @param {
|
|
223
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
224
224
|
*/
|
|
225
225
|
onItemSelectionToggle: _propTypes.default.func,
|
|
226
226
|
/**
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
5
4
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
|
|
5
|
+
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
|
|
6
6
|
Object.defineProperty(exports, "__esModule", {
|
|
7
7
|
value: true
|
|
8
8
|
});
|
|
9
9
|
exports.TreeItemIcon = TreeItemIcon;
|
|
10
10
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
11
|
+
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
|
|
11
12
|
var React = _interopRequireWildcard(require("react"));
|
|
12
13
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
13
14
|
var _resolveComponentProps = _interopRequireDefault(require("@mui/utils/resolveComponentProps"));
|
|
14
|
-
var
|
|
15
|
+
var _useSlotProps2 = _interopRequireDefault(require("@mui/utils/useSlotProps"));
|
|
15
16
|
var _TreeViewStyleContext = require("../internals/TreeViewProvider/TreeViewStyleContext");
|
|
16
17
|
var _icons = require("../icons");
|
|
17
18
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
19
|
+
const _excluded = ["ownerState"];
|
|
18
20
|
function pickIcon(treeItemIcon, treeViewIcon, fallback) {
|
|
19
21
|
if (treeItemIcon !== undefined) {
|
|
20
22
|
return treeItemIcon;
|
|
@@ -53,12 +55,13 @@ function TreeItemIcon(props) {
|
|
|
53
55
|
iconName = 'endIcon';
|
|
54
56
|
}
|
|
55
57
|
const Icon = slots[iconName];
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
const _useSlotProps = (0, _useSlotProps2.default)({
|
|
59
|
+
elementType: Icon,
|
|
60
|
+
externalSlotProps: tempOwnerState => (0, _extends2.default)({}, (0, _resolveComponentProps.default)(slotPropsFromTreeView[iconName], tempOwnerState), (0, _resolveComponentProps.default)(slotPropsFromTreeItem?.[iconName], tempOwnerState)),
|
|
61
|
+
// TODO: Add proper ownerState
|
|
62
|
+
ownerState: {}
|
|
63
|
+
}),
|
|
64
|
+
iconProps = (0, _objectWithoutPropertiesLoose2.default)(_useSlotProps, _excluded);
|
|
62
65
|
if (!Icon) {
|
|
63
66
|
return null;
|
|
64
67
|
}
|
|
@@ -260,7 +260,7 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
260
260
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
261
261
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
262
262
|
* @param {array} itemId The itemId of the modified item.
|
|
263
|
-
* @param {
|
|
263
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
264
264
|
*/
|
|
265
265
|
onItemExpansionToggle: PropTypes.func,
|
|
266
266
|
/**
|
|
@@ -279,7 +279,7 @@ process.env.NODE_ENV !== "production" ? RichTreeView.propTypes = {
|
|
|
279
279
|
* Callback fired when a Tree Item is selected or deselected.
|
|
280
280
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
281
281
|
* @param {array} itemId The itemId of the modified item.
|
|
282
|
-
* @param {
|
|
282
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
283
283
|
*/
|
|
284
284
|
onItemSelectionToggle: PropTypes.func,
|
|
285
285
|
/**
|
|
@@ -200,7 +200,7 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
|
|
|
200
200
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
201
201
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
202
202
|
* @param {array} itemId The itemId of the modified item.
|
|
203
|
-
* @param {
|
|
203
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
204
204
|
*/
|
|
205
205
|
onItemExpansionToggle: PropTypes.func,
|
|
206
206
|
/**
|
|
@@ -213,7 +213,7 @@ process.env.NODE_ENV !== "production" ? SimpleTreeView.propTypes = {
|
|
|
213
213
|
* Callback fired when a Tree Item is selected or deselected.
|
|
214
214
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
215
215
|
* @param {array} itemId The itemId of the modified item.
|
|
216
|
-
* @param {
|
|
216
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
217
217
|
*/
|
|
218
218
|
onItemSelectionToggle: PropTypes.func,
|
|
219
219
|
/**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
4
|
+
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
5
|
+
const _excluded = ["ownerState"];
|
|
4
6
|
import * as React from 'react';
|
|
5
7
|
import PropTypes from 'prop-types';
|
|
6
8
|
import resolveComponentProps from '@mui/utils/resolveComponentProps';
|
|
@@ -46,12 +48,13 @@ function TreeItemIcon(props) {
|
|
|
46
48
|
iconName = 'endIcon';
|
|
47
49
|
}
|
|
48
50
|
const Icon = slots[iconName];
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const _useSlotProps = useSlotProps({
|
|
52
|
+
elementType: Icon,
|
|
53
|
+
externalSlotProps: tempOwnerState => _extends({}, resolveComponentProps(slotPropsFromTreeView[iconName], tempOwnerState), resolveComponentProps(slotPropsFromTreeItem?.[iconName], tempOwnerState)),
|
|
54
|
+
// TODO: Add proper ownerState
|
|
55
|
+
ownerState: {}
|
|
56
|
+
}),
|
|
57
|
+
iconProps = _objectWithoutPropertiesLoose(_useSlotProps, _excluded);
|
|
55
58
|
if (!Icon) {
|
|
56
59
|
return null;
|
|
57
60
|
}
|
package/esm/index.js
CHANGED
|
@@ -72,7 +72,7 @@ export interface UseTreeViewExpansionParameters {
|
|
|
72
72
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
73
73
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
74
74
|
* @param {array} itemId The itemId of the modified item.
|
|
75
|
-
* @param {
|
|
75
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
76
76
|
*/
|
|
77
77
|
onItemExpansionToggle?: (event: React.SyntheticEvent | null, itemId: string, isExpanded: boolean) => void;
|
|
78
78
|
/**
|
|
@@ -57,14 +57,14 @@ export const useTreeViewJSXItems = ({
|
|
|
57
57
|
})
|
|
58
58
|
}));
|
|
59
59
|
};
|
|
60
|
-
const
|
|
61
|
-
instance.
|
|
62
|
-
|
|
63
|
-
return
|
|
60
|
+
const mapLabelFromJSX = useEventCallback((itemId, label) => {
|
|
61
|
+
instance.updateLabelMap(labelMap => {
|
|
62
|
+
labelMap[itemId] = label;
|
|
63
|
+
return labelMap;
|
|
64
64
|
});
|
|
65
65
|
return () => {
|
|
66
|
-
instance.
|
|
67
|
-
const newMap = _extends({},
|
|
66
|
+
instance.updateLabelMap(labelMap => {
|
|
67
|
+
const newMap = _extends({}, labelMap);
|
|
68
68
|
delete newMap[itemId];
|
|
69
69
|
return newMap;
|
|
70
70
|
});
|
|
@@ -74,7 +74,7 @@ export const useTreeViewJSXItems = ({
|
|
|
74
74
|
instance: {
|
|
75
75
|
insertJSXItem,
|
|
76
76
|
setJSXItemsOrderedChildrenIds,
|
|
77
|
-
|
|
77
|
+
mapLabelFromJSX
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
};
|
|
@@ -132,7 +132,7 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
132
132
|
}, [instance, parentId, itemId, expandable, disabled, id]);
|
|
133
133
|
React.useEffect(() => {
|
|
134
134
|
if (label) {
|
|
135
|
-
return instance.
|
|
135
|
+
return instance.mapLabelFromJSX(itemId, (pluginContentRef.current?.textContent ?? '').toLowerCase());
|
|
136
136
|
}
|
|
137
137
|
return undefined;
|
|
138
138
|
}, [instance, itemId, label]);
|
|
@@ -10,13 +10,13 @@ export interface UseTreeViewItemsInstance {
|
|
|
10
10
|
*/
|
|
11
11
|
insertJSXItem: (item: TreeViewItemMeta) => () => void;
|
|
12
12
|
/**
|
|
13
|
-
* Updates the `
|
|
13
|
+
* Updates the `labelMap` to register the first character of the given item's label.
|
|
14
14
|
* This map is used to navigate the tree using type-ahead search.
|
|
15
|
-
* @param {TreeViewItemId} itemId The id of the item to map the
|
|
16
|
-
* @param {string}
|
|
17
|
-
* @returns {() => void} A function to remove the item from the `
|
|
15
|
+
* @param {TreeViewItemId} itemId The id of the item to map the label of.
|
|
16
|
+
* @param {string} label The item's label.
|
|
17
|
+
* @returns {() => void} A function to remove the item from the `labelMap`.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
mapLabelFromJSX: (itemId: TreeViewItemId, label: string) => () => void;
|
|
20
20
|
/**
|
|
21
21
|
* Store the ids of a given item's children in the state.
|
|
22
22
|
* Those ids must be passed in the order they should be rendered.
|
package/esm/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { useStore } from '@mui/x-internals/store';
|
|
5
5
|
import { useRtl } from '@mui/system/RtlProvider';
|
|
6
|
+
import { useTimeout } from '@base-ui-components/utils/useTimeout';
|
|
6
7
|
import { useEventCallback } from '@base-ui-components/utils/useEventCallback';
|
|
7
8
|
import { getFirstNavigableItem, getLastNavigableItem, getNextNavigableItem, getPreviousNavigableItem, isTargetInDescendants } from "../../utils/tree.js";
|
|
8
9
|
import { hasPlugin } from "../../utils/plugins.js";
|
|
@@ -14,44 +15,48 @@ import { expansionSelectors } from "../useTreeViewExpansion/useTreeViewExpansion
|
|
|
14
15
|
function isPrintableKey(string) {
|
|
15
16
|
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
16
17
|
}
|
|
18
|
+
const TYPEAHEAD_TIMEOUT = 500;
|
|
17
19
|
export const useTreeViewKeyboardNavigation = ({
|
|
18
20
|
instance,
|
|
19
21
|
store,
|
|
20
22
|
params
|
|
21
23
|
}) => {
|
|
22
24
|
const isRtl = useRtl();
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
25
|
+
const labelMap = React.useRef({});
|
|
26
|
+
const typeaheadQueryRef = React.useRef('');
|
|
27
|
+
const typeaheadTimeout = useTimeout();
|
|
28
|
+
const updateLabelMap = useEventCallback(callback => {
|
|
29
|
+
labelMap.current = callback(labelMap.current);
|
|
26
30
|
});
|
|
27
31
|
const itemMetaLookup = useStore(store, itemsSelectors.itemMetaLookup);
|
|
28
32
|
React.useEffect(() => {
|
|
29
33
|
if (instance.areItemUpdatesPrevented()) {
|
|
30
34
|
return;
|
|
31
35
|
}
|
|
32
|
-
const
|
|
36
|
+
const newLabelMap = {};
|
|
33
37
|
const processItem = item => {
|
|
34
|
-
|
|
38
|
+
newLabelMap[item.id] = item.label.toLowerCase();
|
|
35
39
|
};
|
|
36
40
|
Object.values(itemMetaLookup).forEach(processItem);
|
|
37
|
-
|
|
41
|
+
labelMap.current = newLabelMap;
|
|
38
42
|
}, [itemMetaLookup, params.getItemId, instance]);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
};
|
|
43
|
+
const getNextItem = itemIdToCheck => {
|
|
44
|
+
const nextItemId = getNextNavigableItem(store.state, itemIdToCheck);
|
|
45
|
+
// We reached the end of the tree, check from the beginning
|
|
46
|
+
if (nextItemId === null) {
|
|
47
|
+
return getFirstNavigableItem(store.state);
|
|
48
|
+
}
|
|
49
|
+
return nextItemId;
|
|
50
|
+
};
|
|
51
|
+
const getNextMatchingItemId = (itemId, query) => {
|
|
49
52
|
let matchingItemId = null;
|
|
50
|
-
let currentItemId = getNextItem(itemId);
|
|
51
53
|
const checkedItems = {};
|
|
54
|
+
// If query length > 1, first check if current item matches
|
|
55
|
+
let currentItemId = query.length > 1 ? itemId : getNextItem(itemId);
|
|
52
56
|
// The "!checkedItems[currentItemId]" condition avoids an infinite loop when there is no matching item.
|
|
53
57
|
while (matchingItemId == null && !checkedItems[currentItemId]) {
|
|
54
|
-
|
|
58
|
+
const itemLabel = labelMap.current[currentItemId];
|
|
59
|
+
if (itemLabel?.startsWith(query)) {
|
|
55
60
|
matchingItemId = currentItemId;
|
|
56
61
|
} else {
|
|
57
62
|
checkedItems[currentItemId] = true;
|
|
@@ -60,6 +65,26 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
60
65
|
}
|
|
61
66
|
return matchingItemId;
|
|
62
67
|
};
|
|
68
|
+
const getFirstMatchingItem = (itemId, newKey) => {
|
|
69
|
+
const cleanNewKey = newKey.toLowerCase();
|
|
70
|
+
|
|
71
|
+
// Try matching with accumulated query + new key
|
|
72
|
+
const concatenatedQuery = `${typeaheadQueryRef.current}${cleanNewKey}`;
|
|
73
|
+
|
|
74
|
+
// check if the entire typed query matches an item
|
|
75
|
+
const concatenatedQueryMatchingItemId = getNextMatchingItemId(itemId, concatenatedQuery);
|
|
76
|
+
if (concatenatedQueryMatchingItemId != null) {
|
|
77
|
+
typeaheadQueryRef.current = concatenatedQuery;
|
|
78
|
+
return concatenatedQueryMatchingItemId;
|
|
79
|
+
}
|
|
80
|
+
const newKeyMatchingItemId = getNextMatchingItemId(itemId, cleanNewKey);
|
|
81
|
+
if (newKeyMatchingItemId != null) {
|
|
82
|
+
typeaheadQueryRef.current = cleanNewKey;
|
|
83
|
+
return newKeyMatchingItemId;
|
|
84
|
+
}
|
|
85
|
+
typeaheadQueryRef.current = '';
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
63
88
|
const canToggleItemSelection = itemId => selectionSelectors.enabled(store.state) && !itemsSelectors.isItemDisabled(store.state, itemId);
|
|
64
89
|
const canToggleItemExpansion = itemId => {
|
|
65
90
|
return !itemsSelectors.isItemDisabled(store.state, itemId) && expansionSelectors.isItemExpandable(store.state, itemId);
|
|
@@ -253,21 +278,26 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
253
278
|
}
|
|
254
279
|
|
|
255
280
|
// Type-ahead
|
|
256
|
-
// TODO: Support typing multiple characters
|
|
257
281
|
case !ctrlPressed && !event.shiftKey && isPrintableKey(key):
|
|
258
282
|
{
|
|
283
|
+
typeaheadTimeout.clear();
|
|
259
284
|
const matchingItem = getFirstMatchingItem(itemId, key);
|
|
260
285
|
if (matchingItem != null) {
|
|
261
286
|
instance.focusItem(event, matchingItem);
|
|
262
287
|
event.preventDefault();
|
|
288
|
+
} else {
|
|
289
|
+
typeaheadQueryRef.current = '';
|
|
263
290
|
}
|
|
291
|
+
typeaheadTimeout.start(TYPEAHEAD_TIMEOUT, () => {
|
|
292
|
+
typeaheadQueryRef.current = '';
|
|
293
|
+
});
|
|
264
294
|
break;
|
|
265
295
|
}
|
|
266
296
|
}
|
|
267
297
|
};
|
|
268
298
|
return {
|
|
269
299
|
instance: {
|
|
270
|
-
|
|
300
|
+
updateLabelMap,
|
|
271
301
|
handleItemKeyDown
|
|
272
302
|
}
|
|
273
303
|
};
|
package/esm/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts
CHANGED
|
@@ -8,12 +8,12 @@ import { UseTreeViewLabelSignature } from "../useTreeViewLabel/index.js";
|
|
|
8
8
|
import { TreeViewItemId, TreeViewCancellableEvent } from "../../../models/index.js";
|
|
9
9
|
export interface UseTreeViewKeyboardNavigationInstance {
|
|
10
10
|
/**
|
|
11
|
-
* Updates the `
|
|
11
|
+
* Updates the `labelMap` to add/remove the first character of some item's labels.
|
|
12
12
|
* This map is used to navigate the tree using type-ahead search.
|
|
13
13
|
* This method is only used by the `useTreeViewJSXItems` plugin, otherwise the updates are handled internally.
|
|
14
|
-
* @param {(map:
|
|
14
|
+
* @param {(map: TreeViewLabelMap) => TreeViewLabelMap} updater The function to update the map.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
updateLabelMap: (updater: (map: TreeViewLabelMap) => TreeViewLabelMap) => void;
|
|
17
17
|
/**
|
|
18
18
|
* Callback fired when a key is pressed on an item.
|
|
19
19
|
* Handles all the keyboard navigation logic.
|
|
@@ -27,6 +27,6 @@ export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
|
27
27
|
dependencies: [UseTreeViewItemsSignature, UseTreeViewSelectionSignature, UseTreeViewFocusSignature, UseTreeViewExpansionSignature];
|
|
28
28
|
optionalDependencies: [UseTreeViewLabelSignature];
|
|
29
29
|
}>;
|
|
30
|
-
export type
|
|
30
|
+
export type TreeViewLabelMap = {
|
|
31
31
|
[itemId: string]: string;
|
|
32
32
|
};
|
|
@@ -108,7 +108,7 @@ export interface UseTreeViewSelectionParameters<Multiple extends boolean | undef
|
|
|
108
108
|
* Callback fired when a Tree Item is selected or deselected.
|
|
109
109
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
110
110
|
* @param {array} itemId The itemId of the modified item.
|
|
111
|
-
* @param {
|
|
111
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
112
112
|
*/
|
|
113
113
|
onItemSelectionToggle?: (event: React.SyntheticEvent | null, itemId: string, isSelected: boolean) => void;
|
|
114
114
|
}
|
package/index.js
CHANGED
|
@@ -72,7 +72,7 @@ export interface UseTreeViewExpansionParameters {
|
|
|
72
72
|
* Callback fired when a Tree Item is expanded or collapsed.
|
|
73
73
|
* @param {React.SyntheticEvent | null} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemExpansion()` method.
|
|
74
74
|
* @param {array} itemId The itemId of the modified item.
|
|
75
|
-
* @param {
|
|
75
|
+
* @param {boolean} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed.
|
|
76
76
|
*/
|
|
77
77
|
onItemExpansionToggle?: (event: React.SyntheticEvent | null, itemId: string, isExpanded: boolean) => void;
|
|
78
78
|
/**
|
|
@@ -64,14 +64,14 @@ const useTreeViewJSXItems = ({
|
|
|
64
64
|
})
|
|
65
65
|
}));
|
|
66
66
|
};
|
|
67
|
-
const
|
|
68
|
-
instance.
|
|
69
|
-
|
|
70
|
-
return
|
|
67
|
+
const mapLabelFromJSX = (0, _useEventCallback.useEventCallback)((itemId, label) => {
|
|
68
|
+
instance.updateLabelMap(labelMap => {
|
|
69
|
+
labelMap[itemId] = label;
|
|
70
|
+
return labelMap;
|
|
71
71
|
});
|
|
72
72
|
return () => {
|
|
73
|
-
instance.
|
|
74
|
-
const newMap = (0, _extends2.default)({},
|
|
73
|
+
instance.updateLabelMap(labelMap => {
|
|
74
|
+
const newMap = (0, _extends2.default)({}, labelMap);
|
|
75
75
|
delete newMap[itemId];
|
|
76
76
|
return newMap;
|
|
77
77
|
});
|
|
@@ -81,7 +81,7 @@ const useTreeViewJSXItems = ({
|
|
|
81
81
|
instance: {
|
|
82
82
|
insertJSXItem,
|
|
83
83
|
setJSXItemsOrderedChildrenIds,
|
|
84
|
-
|
|
84
|
+
mapLabelFromJSX
|
|
85
85
|
}
|
|
86
86
|
};
|
|
87
87
|
};
|
|
@@ -140,7 +140,7 @@ const useTreeViewJSXItemsItemPlugin = ({
|
|
|
140
140
|
}, [instance, parentId, itemId, expandable, disabled, id]);
|
|
141
141
|
React.useEffect(() => {
|
|
142
142
|
if (label) {
|
|
143
|
-
return instance.
|
|
143
|
+
return instance.mapLabelFromJSX(itemId, (pluginContentRef.current?.textContent ?? '').toLowerCase());
|
|
144
144
|
}
|
|
145
145
|
return undefined;
|
|
146
146
|
}, [instance, itemId, label]);
|
|
@@ -10,13 +10,13 @@ export interface UseTreeViewItemsInstance {
|
|
|
10
10
|
*/
|
|
11
11
|
insertJSXItem: (item: TreeViewItemMeta) => () => void;
|
|
12
12
|
/**
|
|
13
|
-
* Updates the `
|
|
13
|
+
* Updates the `labelMap` to register the first character of the given item's label.
|
|
14
14
|
* This map is used to navigate the tree using type-ahead search.
|
|
15
|
-
* @param {TreeViewItemId} itemId The id of the item to map the
|
|
16
|
-
* @param {string}
|
|
17
|
-
* @returns {() => void} A function to remove the item from the `
|
|
15
|
+
* @param {TreeViewItemId} itemId The id of the item to map the label of.
|
|
16
|
+
* @param {string} label The item's label.
|
|
17
|
+
* @returns {() => void} A function to remove the item from the `labelMap`.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
mapLabelFromJSX: (itemId: TreeViewItemId, label: string) => () => void;
|
|
20
20
|
/**
|
|
21
21
|
* Store the ids of a given item's children in the state.
|
|
22
22
|
* Those ids must be passed in the order they should be rendered.
|
|
@@ -9,6 +9,7 @@ exports.useTreeViewKeyboardNavigation = void 0;
|
|
|
9
9
|
var React = _interopRequireWildcard(require("react"));
|
|
10
10
|
var _store = require("@mui/x-internals/store");
|
|
11
11
|
var _RtlProvider = require("@mui/system/RtlProvider");
|
|
12
|
+
var _useTimeout = require("@base-ui-components/utils/useTimeout");
|
|
12
13
|
var _useEventCallback = require("@base-ui-components/utils/useEventCallback");
|
|
13
14
|
var _tree = require("../../utils/tree");
|
|
14
15
|
var _plugins = require("../../utils/plugins");
|
|
@@ -20,44 +21,48 @@ var _useTreeViewExpansion = require("../useTreeViewExpansion/useTreeViewExpansio
|
|
|
20
21
|
function isPrintableKey(string) {
|
|
21
22
|
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
22
23
|
}
|
|
24
|
+
const TYPEAHEAD_TIMEOUT = 500;
|
|
23
25
|
const useTreeViewKeyboardNavigation = ({
|
|
24
26
|
instance,
|
|
25
27
|
store,
|
|
26
28
|
params
|
|
27
29
|
}) => {
|
|
28
30
|
const isRtl = (0, _RtlProvider.useRtl)();
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
31
|
+
const labelMap = React.useRef({});
|
|
32
|
+
const typeaheadQueryRef = React.useRef('');
|
|
33
|
+
const typeaheadTimeout = (0, _useTimeout.useTimeout)();
|
|
34
|
+
const updateLabelMap = (0, _useEventCallback.useEventCallback)(callback => {
|
|
35
|
+
labelMap.current = callback(labelMap.current);
|
|
32
36
|
});
|
|
33
37
|
const itemMetaLookup = (0, _store.useStore)(store, _useTreeViewItems.itemsSelectors.itemMetaLookup);
|
|
34
38
|
React.useEffect(() => {
|
|
35
39
|
if (instance.areItemUpdatesPrevented()) {
|
|
36
40
|
return;
|
|
37
41
|
}
|
|
38
|
-
const
|
|
42
|
+
const newLabelMap = {};
|
|
39
43
|
const processItem = item => {
|
|
40
|
-
|
|
44
|
+
newLabelMap[item.id] = item.label.toLowerCase();
|
|
41
45
|
};
|
|
42
46
|
Object.values(itemMetaLookup).forEach(processItem);
|
|
43
|
-
|
|
47
|
+
labelMap.current = newLabelMap;
|
|
44
48
|
}, [itemMetaLookup, params.getItemId, instance]);
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
49
|
+
const getNextItem = itemIdToCheck => {
|
|
50
|
+
const nextItemId = (0, _tree.getNextNavigableItem)(store.state, itemIdToCheck);
|
|
51
|
+
// We reached the end of the tree, check from the beginning
|
|
52
|
+
if (nextItemId === null) {
|
|
53
|
+
return (0, _tree.getFirstNavigableItem)(store.state);
|
|
54
|
+
}
|
|
55
|
+
return nextItemId;
|
|
56
|
+
};
|
|
57
|
+
const getNextMatchingItemId = (itemId, query) => {
|
|
55
58
|
let matchingItemId = null;
|
|
56
|
-
let currentItemId = getNextItem(itemId);
|
|
57
59
|
const checkedItems = {};
|
|
60
|
+
// If query length > 1, first check if current item matches
|
|
61
|
+
let currentItemId = query.length > 1 ? itemId : getNextItem(itemId);
|
|
58
62
|
// The "!checkedItems[currentItemId]" condition avoids an infinite loop when there is no matching item.
|
|
59
63
|
while (matchingItemId == null && !checkedItems[currentItemId]) {
|
|
60
|
-
|
|
64
|
+
const itemLabel = labelMap.current[currentItemId];
|
|
65
|
+
if (itemLabel?.startsWith(query)) {
|
|
61
66
|
matchingItemId = currentItemId;
|
|
62
67
|
} else {
|
|
63
68
|
checkedItems[currentItemId] = true;
|
|
@@ -66,6 +71,26 @@ const useTreeViewKeyboardNavigation = ({
|
|
|
66
71
|
}
|
|
67
72
|
return matchingItemId;
|
|
68
73
|
};
|
|
74
|
+
const getFirstMatchingItem = (itemId, newKey) => {
|
|
75
|
+
const cleanNewKey = newKey.toLowerCase();
|
|
76
|
+
|
|
77
|
+
// Try matching with accumulated query + new key
|
|
78
|
+
const concatenatedQuery = `${typeaheadQueryRef.current}${cleanNewKey}`;
|
|
79
|
+
|
|
80
|
+
// check if the entire typed query matches an item
|
|
81
|
+
const concatenatedQueryMatchingItemId = getNextMatchingItemId(itemId, concatenatedQuery);
|
|
82
|
+
if (concatenatedQueryMatchingItemId != null) {
|
|
83
|
+
typeaheadQueryRef.current = concatenatedQuery;
|
|
84
|
+
return concatenatedQueryMatchingItemId;
|
|
85
|
+
}
|
|
86
|
+
const newKeyMatchingItemId = getNextMatchingItemId(itemId, cleanNewKey);
|
|
87
|
+
if (newKeyMatchingItemId != null) {
|
|
88
|
+
typeaheadQueryRef.current = cleanNewKey;
|
|
89
|
+
return newKeyMatchingItemId;
|
|
90
|
+
}
|
|
91
|
+
typeaheadQueryRef.current = '';
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
69
94
|
const canToggleItemSelection = itemId => _useTreeViewSelection.selectionSelectors.enabled(store.state) && !_useTreeViewItems.itemsSelectors.isItemDisabled(store.state, itemId);
|
|
70
95
|
const canToggleItemExpansion = itemId => {
|
|
71
96
|
return !_useTreeViewItems.itemsSelectors.isItemDisabled(store.state, itemId) && _useTreeViewExpansion.expansionSelectors.isItemExpandable(store.state, itemId);
|
|
@@ -259,21 +284,26 @@ const useTreeViewKeyboardNavigation = ({
|
|
|
259
284
|
}
|
|
260
285
|
|
|
261
286
|
// Type-ahead
|
|
262
|
-
// TODO: Support typing multiple characters
|
|
263
287
|
case !ctrlPressed && !event.shiftKey && isPrintableKey(key):
|
|
264
288
|
{
|
|
289
|
+
typeaheadTimeout.clear();
|
|
265
290
|
const matchingItem = getFirstMatchingItem(itemId, key);
|
|
266
291
|
if (matchingItem != null) {
|
|
267
292
|
instance.focusItem(event, matchingItem);
|
|
268
293
|
event.preventDefault();
|
|
294
|
+
} else {
|
|
295
|
+
typeaheadQueryRef.current = '';
|
|
269
296
|
}
|
|
297
|
+
typeaheadTimeout.start(TYPEAHEAD_TIMEOUT, () => {
|
|
298
|
+
typeaheadQueryRef.current = '';
|
|
299
|
+
});
|
|
270
300
|
break;
|
|
271
301
|
}
|
|
272
302
|
}
|
|
273
303
|
};
|
|
274
304
|
return {
|
|
275
305
|
instance: {
|
|
276
|
-
|
|
306
|
+
updateLabelMap,
|
|
277
307
|
handleItemKeyDown
|
|
278
308
|
}
|
|
279
309
|
};
|
package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.d.ts
CHANGED
|
@@ -8,12 +8,12 @@ import { UseTreeViewLabelSignature } from "../useTreeViewLabel/index.js";
|
|
|
8
8
|
import { TreeViewItemId, TreeViewCancellableEvent } from "../../../models/index.js";
|
|
9
9
|
export interface UseTreeViewKeyboardNavigationInstance {
|
|
10
10
|
/**
|
|
11
|
-
* Updates the `
|
|
11
|
+
* Updates the `labelMap` to add/remove the first character of some item's labels.
|
|
12
12
|
* This map is used to navigate the tree using type-ahead search.
|
|
13
13
|
* This method is only used by the `useTreeViewJSXItems` plugin, otherwise the updates are handled internally.
|
|
14
|
-
* @param {(map:
|
|
14
|
+
* @param {(map: TreeViewLabelMap) => TreeViewLabelMap} updater The function to update the map.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
updateLabelMap: (updater: (map: TreeViewLabelMap) => TreeViewLabelMap) => void;
|
|
17
17
|
/**
|
|
18
18
|
* Callback fired when a key is pressed on an item.
|
|
19
19
|
* Handles all the keyboard navigation logic.
|
|
@@ -27,6 +27,6 @@ export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{
|
|
|
27
27
|
dependencies: [UseTreeViewItemsSignature, UseTreeViewSelectionSignature, UseTreeViewFocusSignature, UseTreeViewExpansionSignature];
|
|
28
28
|
optionalDependencies: [UseTreeViewLabelSignature];
|
|
29
29
|
}>;
|
|
30
|
-
export type
|
|
30
|
+
export type TreeViewLabelMap = {
|
|
31
31
|
[itemId: string]: string;
|
|
32
32
|
};
|
|
@@ -108,7 +108,7 @@ export interface UseTreeViewSelectionParameters<Multiple extends boolean | undef
|
|
|
108
108
|
* Callback fired when a Tree Item is selected or deselected.
|
|
109
109
|
* @param {React.SyntheticEvent} event The DOM event that triggered the change. Can be null when the change is caused by the `publicAPI.setItemSelection()` method.
|
|
110
110
|
* @param {array} itemId The itemId of the modified item.
|
|
111
|
-
* @param {
|
|
111
|
+
* @param {boolean} isSelected `true` if the item has just been selected, `false` if it has just been deselected.
|
|
112
112
|
*/
|
|
113
113
|
onItemSelectionToggle?: (event: React.SyntheticEvent | null, itemId: string, isSelected: boolean) => void;
|
|
114
114
|
}
|