@itwin/itwinui-react 3.19.3 → 3.19.4
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 +8 -0
- package/DEV-cjs/core/Menu/Menu.js +1 -0
- package/DEV-cjs/core/Tree/Tree.js +37 -2
- package/DEV-cjs/core/Tree/TreeContext.js +4 -0
- package/DEV-cjs/core/Tree/TreeNode.js +6 -1
- package/DEV-cjs/styles.js +1 -1
- package/DEV-esm/core/Menu/Menu.js +1 -0
- package/DEV-esm/core/Tree/Tree.js +38 -3
- package/DEV-esm/core/Tree/TreeContext.js +1 -0
- package/DEV-esm/core/Tree/TreeNode.js +7 -2
- package/DEV-esm/styles.js +1 -1
- package/cjs/core/Menu/Menu.js +1 -0
- package/cjs/core/Tree/Tree.js +37 -2
- package/cjs/core/Tree/TreeContext.d.ts +5 -0
- package/cjs/core/Tree/TreeContext.js +4 -0
- package/cjs/core/Tree/TreeNode.js +6 -1
- package/cjs/styles.js +1 -1
- package/esm/core/Menu/Menu.js +1 -0
- package/esm/core/Tree/Tree.js +38 -3
- package/esm/core/Tree/TreeContext.d.ts +5 -0
- package/esm/core/Tree/TreeContext.js +1 -0
- package/esm/core/Tree/TreeNode.js +7 -2
- package/esm/styles.js +1 -1
- package/package.json +1 -1
- package/styles.css +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.19.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#2633](https://github.com/iTwin/iTwinUI/pull/2633): Fixed horizontal scroll in `Tree` with `enableVirtualization` so that all nodes are equally wide instead of using their intrinsic width.
|
|
8
|
+
- c07a7166f1cbf5d8ec6c86389da2f9e1870af6d6: Fixed a theme bridge issue where `<Checkbox indeterminate>` was not displaying correctly in light theme.
|
|
9
|
+
- 6d73dbecc2a5d57d1c4979ddbc7ef931744aebeb: Added `aria-haspopup` attribute to `DropdownMenu`'s trigger.
|
|
10
|
+
|
|
3
11
|
## 3.19.3
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
|
@@ -177,6 +177,7 @@ const Menu = _react.forwardRef((props, ref) => {
|
|
|
177
177
|
let reference = (0, _index.cloneElementWithRef)(trigger, (triggerChild) =>
|
|
178
178
|
getReferenceProps(
|
|
179
179
|
popover.getReferenceProps({
|
|
180
|
+
'aria-haspopup': 'menu',
|
|
180
181
|
...triggerChild.props,
|
|
181
182
|
'aria-expanded': popover.open,
|
|
182
183
|
ref: (0, _index.mergeRefs)(triggerRef, popover.refs.setReference),
|
|
@@ -197,15 +197,32 @@ const TreeElement = _index.polymorphic.div('iui-tree', {
|
|
|
197
197
|
const VirtualizedTree = _react.forwardRef(
|
|
198
198
|
({ flatNodesList, itemRenderer, scrollToIndex, ...rest }, ref) => {
|
|
199
199
|
let parentRef = _react.useRef(null);
|
|
200
|
+
let virtualizerRootRef = _react.useRef(null);
|
|
200
201
|
let getItemKey = _react.useCallback(
|
|
201
202
|
(index) => flatNodesList[index].nodeProps.nodeId,
|
|
202
203
|
[flatNodesList],
|
|
203
204
|
);
|
|
205
|
+
let onVirtualizerChange = _react.useMemo(
|
|
206
|
+
() =>
|
|
207
|
+
debounce((virtualizer) => {
|
|
208
|
+
if (!virtualizer || !virtualizerRootRef.current) return;
|
|
209
|
+
virtualizerRootRef.current.style.width = '';
|
|
210
|
+
let widestNodeWidth = 0;
|
|
211
|
+
virtualizer.elementsCache.forEach((el) => {
|
|
212
|
+
if (el.clientWidth > widestNodeWidth)
|
|
213
|
+
widestNodeWidth = el.clientWidth;
|
|
214
|
+
});
|
|
215
|
+
if (widestNodeWidth)
|
|
216
|
+
virtualizerRootRef.current.style.width = `${widestNodeWidth}px`;
|
|
217
|
+
}, 100),
|
|
218
|
+
[],
|
|
219
|
+
);
|
|
204
220
|
let { virtualizer, css: virtualizerCss } = (0, _index.useVirtualScroll)({
|
|
205
221
|
count: flatNodesList.length,
|
|
206
222
|
getScrollElement: () => parentRef.current,
|
|
207
223
|
estimateSize: () => 39,
|
|
208
224
|
getItemKey,
|
|
225
|
+
onChange: onVirtualizerChange,
|
|
209
226
|
});
|
|
210
227
|
(0, _index.useLayoutEffect)(() => {
|
|
211
228
|
if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex);
|
|
@@ -235,13 +252,22 @@ const VirtualizedTree = _react.forwardRef(
|
|
|
235
252
|
style: {
|
|
236
253
|
minBlockSize: virtualizer.getTotalSize(),
|
|
237
254
|
},
|
|
255
|
+
ref: virtualizerRootRef,
|
|
238
256
|
},
|
|
239
257
|
_react.createElement('slot', null),
|
|
240
258
|
),
|
|
241
259
|
),
|
|
242
260
|
_react.createElement(
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
_TreeContext.VirtualizedTreeContext.Provider,
|
|
262
|
+
{
|
|
263
|
+
value: _react.useMemo(
|
|
264
|
+
() => ({
|
|
265
|
+
virtualizer,
|
|
266
|
+
onVirtualizerChange,
|
|
267
|
+
}),
|
|
268
|
+
[virtualizer, onVirtualizerChange],
|
|
269
|
+
),
|
|
270
|
+
},
|
|
245
271
|
virtualizer
|
|
246
272
|
.getVirtualItems()
|
|
247
273
|
.map((virtualItem) =>
|
|
@@ -252,3 +278,12 @@ const VirtualizedTree = _react.forwardRef(
|
|
|
252
278
|
);
|
|
253
279
|
},
|
|
254
280
|
);
|
|
281
|
+
function debounce(callback, delay) {
|
|
282
|
+
let timeoutId;
|
|
283
|
+
return (...args) => {
|
|
284
|
+
if (timeoutId) window.clearTimeout(timeoutId);
|
|
285
|
+
timeoutId = window.setTimeout(() => {
|
|
286
|
+
callback(...args);
|
|
287
|
+
}, delay);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
@@ -13,6 +13,9 @@ _export(exports, {
|
|
|
13
13
|
TreeContext: function () {
|
|
14
14
|
return TreeContext;
|
|
15
15
|
},
|
|
16
|
+
VirtualizedTreeContext: function () {
|
|
17
|
+
return VirtualizedTreeContext;
|
|
18
|
+
},
|
|
16
19
|
useTreeContext: function () {
|
|
17
20
|
return useTreeContext;
|
|
18
21
|
},
|
|
@@ -26,3 +29,4 @@ const useTreeContext = () => {
|
|
|
26
29
|
throw new Error('TreeContext must be used within a TreeContext.Provider');
|
|
27
30
|
return context;
|
|
28
31
|
};
|
|
32
|
+
const VirtualizedTreeContext = _react.createContext(void 0);
|
|
@@ -52,6 +52,8 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
52
52
|
groupSize,
|
|
53
53
|
indexInGroup,
|
|
54
54
|
} = (0, _TreeContext.useTreeContext)();
|
|
55
|
+
let { virtualizer, onVirtualizerChange } =
|
|
56
|
+
_react.useContext(_TreeContext.VirtualizedTreeContext) ?? {};
|
|
55
57
|
let [isFocused, setIsFocused] = _react.useState(false);
|
|
56
58
|
let nodeRef = _react.useRef(null);
|
|
57
59
|
let onKeyDown = (event) => {
|
|
@@ -64,6 +66,7 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
64
66
|
if (isNodeFocused) {
|
|
65
67
|
if (isExpanded) {
|
|
66
68
|
onExpanded(nodeId, false);
|
|
69
|
+
onVirtualizerChange?.(virtualizer);
|
|
67
70
|
break;
|
|
68
71
|
}
|
|
69
72
|
if (parentNodeId) scrollToParent?.();
|
|
@@ -87,6 +90,7 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
87
90
|
if (isNodeFocused) {
|
|
88
91
|
if (!isExpanded && hasSubNodes) {
|
|
89
92
|
onExpanded(nodeId, true);
|
|
93
|
+
onVirtualizerChange?.(virtualizer);
|
|
90
94
|
break;
|
|
91
95
|
}
|
|
92
96
|
focusableElements[0]?.focus();
|
|
@@ -113,9 +117,10 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
113
117
|
let onExpanderClick = _react.useCallback(
|
|
114
118
|
(event) => {
|
|
115
119
|
onExpanded(nodeId, !isExpanded);
|
|
120
|
+
onVirtualizerChange?.(virtualizer);
|
|
116
121
|
event.stopPropagation();
|
|
117
122
|
},
|
|
118
|
-
[isExpanded, nodeId, onExpanded],
|
|
123
|
+
[isExpanded, nodeId, onExpanded, onVirtualizerChange, virtualizer],
|
|
119
124
|
);
|
|
120
125
|
return _react.createElement(
|
|
121
126
|
_index.Box,
|
package/DEV-cjs/styles.js
CHANGED
|
@@ -162,6 +162,7 @@ export const Menu = React.forwardRef((props, ref) => {
|
|
|
162
162
|
let reference = cloneElementWithRef(trigger, (triggerChild) =>
|
|
163
163
|
getReferenceProps(
|
|
164
164
|
popover.getReferenceProps({
|
|
165
|
+
'aria-haspopup': 'menu',
|
|
165
166
|
...triggerChild.props,
|
|
166
167
|
'aria-expanded': popover.open,
|
|
167
168
|
ref: mergeRefs(triggerRef, popover.refs.setReference),
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useMergedRefs,
|
|
9
9
|
useLayoutEffect,
|
|
10
10
|
} from '../../utils/index.js';
|
|
11
|
-
import { TreeContext } from './TreeContext.js';
|
|
11
|
+
import { TreeContext, VirtualizedTreeContext } from './TreeContext.js';
|
|
12
12
|
export const Tree = (props) => {
|
|
13
13
|
let {
|
|
14
14
|
data,
|
|
@@ -191,15 +191,32 @@ let TreeElement = polymorphic.div('iui-tree', {
|
|
|
191
191
|
let VirtualizedTree = React.forwardRef(
|
|
192
192
|
({ flatNodesList, itemRenderer, scrollToIndex, ...rest }, ref) => {
|
|
193
193
|
let parentRef = React.useRef(null);
|
|
194
|
+
let virtualizerRootRef = React.useRef(null);
|
|
194
195
|
let getItemKey = React.useCallback(
|
|
195
196
|
(index) => flatNodesList[index].nodeProps.nodeId,
|
|
196
197
|
[flatNodesList],
|
|
197
198
|
);
|
|
199
|
+
let onVirtualizerChange = React.useMemo(
|
|
200
|
+
() =>
|
|
201
|
+
debounce((virtualizer) => {
|
|
202
|
+
if (!virtualizer || !virtualizerRootRef.current) return;
|
|
203
|
+
virtualizerRootRef.current.style.width = '';
|
|
204
|
+
let widestNodeWidth = 0;
|
|
205
|
+
virtualizer.elementsCache.forEach((el) => {
|
|
206
|
+
if (el.clientWidth > widestNodeWidth)
|
|
207
|
+
widestNodeWidth = el.clientWidth;
|
|
208
|
+
});
|
|
209
|
+
if (widestNodeWidth)
|
|
210
|
+
virtualizerRootRef.current.style.width = `${widestNodeWidth}px`;
|
|
211
|
+
}, 100),
|
|
212
|
+
[],
|
|
213
|
+
);
|
|
198
214
|
let { virtualizer, css: virtualizerCss } = useVirtualScroll({
|
|
199
215
|
count: flatNodesList.length,
|
|
200
216
|
getScrollElement: () => parentRef.current,
|
|
201
217
|
estimateSize: () => 39,
|
|
202
218
|
getItemKey,
|
|
219
|
+
onChange: onVirtualizerChange,
|
|
203
220
|
});
|
|
204
221
|
useLayoutEffect(() => {
|
|
205
222
|
if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex);
|
|
@@ -229,13 +246,22 @@ let VirtualizedTree = React.forwardRef(
|
|
|
229
246
|
style: {
|
|
230
247
|
minBlockSize: virtualizer.getTotalSize(),
|
|
231
248
|
},
|
|
249
|
+
ref: virtualizerRootRef,
|
|
232
250
|
},
|
|
233
251
|
React.createElement('slot', null),
|
|
234
252
|
),
|
|
235
253
|
),
|
|
236
254
|
React.createElement(
|
|
237
|
-
|
|
238
|
-
|
|
255
|
+
VirtualizedTreeContext.Provider,
|
|
256
|
+
{
|
|
257
|
+
value: React.useMemo(
|
|
258
|
+
() => ({
|
|
259
|
+
virtualizer,
|
|
260
|
+
onVirtualizerChange,
|
|
261
|
+
}),
|
|
262
|
+
[virtualizer, onVirtualizerChange],
|
|
263
|
+
),
|
|
264
|
+
},
|
|
239
265
|
virtualizer
|
|
240
266
|
.getVirtualItems()
|
|
241
267
|
.map((virtualItem) =>
|
|
@@ -246,3 +272,12 @@ let VirtualizedTree = React.forwardRef(
|
|
|
246
272
|
);
|
|
247
273
|
},
|
|
248
274
|
);
|
|
275
|
+
function debounce(callback, delay) {
|
|
276
|
+
let timeoutId;
|
|
277
|
+
return (...args) => {
|
|
278
|
+
if (timeoutId) window.clearTimeout(timeoutId);
|
|
279
|
+
timeoutId = window.setTimeout(() => {
|
|
280
|
+
callback(...args);
|
|
281
|
+
}, delay);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '../../utils/index.js';
|
|
8
8
|
import cx from 'classnames';
|
|
9
9
|
import { TreeNodeExpander } from './TreeNodeExpander.js';
|
|
10
|
-
import { useTreeContext } from './TreeContext.js';
|
|
10
|
+
import { useTreeContext, VirtualizedTreeContext } from './TreeContext.js';
|
|
11
11
|
export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
12
12
|
let {
|
|
13
13
|
nodeId,
|
|
@@ -43,6 +43,8 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
43
43
|
groupSize,
|
|
44
44
|
indexInGroup,
|
|
45
45
|
} = useTreeContext();
|
|
46
|
+
let { virtualizer, onVirtualizerChange } =
|
|
47
|
+
React.useContext(VirtualizedTreeContext) ?? {};
|
|
46
48
|
let [isFocused, setIsFocused] = React.useState(false);
|
|
47
49
|
let nodeRef = React.useRef(null);
|
|
48
50
|
let onKeyDown = (event) => {
|
|
@@ -55,6 +57,7 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
55
57
|
if (isNodeFocused) {
|
|
56
58
|
if (isExpanded) {
|
|
57
59
|
onExpanded(nodeId, false);
|
|
60
|
+
onVirtualizerChange?.(virtualizer);
|
|
58
61
|
break;
|
|
59
62
|
}
|
|
60
63
|
if (parentNodeId) scrollToParent?.();
|
|
@@ -74,6 +77,7 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
74
77
|
if (isNodeFocused) {
|
|
75
78
|
if (!isExpanded && hasSubNodes) {
|
|
76
79
|
onExpanded(nodeId, true);
|
|
80
|
+
onVirtualizerChange?.(virtualizer);
|
|
77
81
|
break;
|
|
78
82
|
}
|
|
79
83
|
focusableElements[0]?.focus();
|
|
@@ -100,9 +104,10 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
100
104
|
let onExpanderClick = React.useCallback(
|
|
101
105
|
(event) => {
|
|
102
106
|
onExpanded(nodeId, !isExpanded);
|
|
107
|
+
onVirtualizerChange?.(virtualizer);
|
|
103
108
|
event.stopPropagation();
|
|
104
109
|
},
|
|
105
|
-
[isExpanded, nodeId, onExpanded],
|
|
110
|
+
[isExpanded, nodeId, onExpanded, onVirtualizerChange, virtualizer],
|
|
106
111
|
);
|
|
107
112
|
return React.createElement(
|
|
108
113
|
Box,
|
package/DEV-esm/styles.js
CHANGED
package/cjs/core/Menu/Menu.js
CHANGED
|
@@ -177,6 +177,7 @@ const Menu = _react.forwardRef((props, ref) => {
|
|
|
177
177
|
let reference = (0, _index.cloneElementWithRef)(trigger, (triggerChild) =>
|
|
178
178
|
getReferenceProps(
|
|
179
179
|
popover.getReferenceProps({
|
|
180
|
+
'aria-haspopup': 'menu',
|
|
180
181
|
...triggerChild.props,
|
|
181
182
|
'aria-expanded': popover.open,
|
|
182
183
|
ref: (0, _index.mergeRefs)(triggerRef, popover.refs.setReference),
|
package/cjs/core/Tree/Tree.js
CHANGED
|
@@ -197,15 +197,32 @@ const TreeElement = _index.polymorphic.div('iui-tree', {
|
|
|
197
197
|
const VirtualizedTree = _react.forwardRef(
|
|
198
198
|
({ flatNodesList, itemRenderer, scrollToIndex, ...rest }, ref) => {
|
|
199
199
|
let parentRef = _react.useRef(null);
|
|
200
|
+
let virtualizerRootRef = _react.useRef(null);
|
|
200
201
|
let getItemKey = _react.useCallback(
|
|
201
202
|
(index) => flatNodesList[index].nodeProps.nodeId,
|
|
202
203
|
[flatNodesList],
|
|
203
204
|
);
|
|
205
|
+
let onVirtualizerChange = _react.useMemo(
|
|
206
|
+
() =>
|
|
207
|
+
debounce((virtualizer) => {
|
|
208
|
+
if (!virtualizer || !virtualizerRootRef.current) return;
|
|
209
|
+
virtualizerRootRef.current.style.width = '';
|
|
210
|
+
let widestNodeWidth = 0;
|
|
211
|
+
virtualizer.elementsCache.forEach((el) => {
|
|
212
|
+
if (el.clientWidth > widestNodeWidth)
|
|
213
|
+
widestNodeWidth = el.clientWidth;
|
|
214
|
+
});
|
|
215
|
+
if (widestNodeWidth)
|
|
216
|
+
virtualizerRootRef.current.style.width = `${widestNodeWidth}px`;
|
|
217
|
+
}, 100),
|
|
218
|
+
[],
|
|
219
|
+
);
|
|
204
220
|
let { virtualizer, css: virtualizerCss } = (0, _index.useVirtualScroll)({
|
|
205
221
|
count: flatNodesList.length,
|
|
206
222
|
getScrollElement: () => parentRef.current,
|
|
207
223
|
estimateSize: () => 39,
|
|
208
224
|
getItemKey,
|
|
225
|
+
onChange: onVirtualizerChange,
|
|
209
226
|
});
|
|
210
227
|
(0, _index.useLayoutEffect)(() => {
|
|
211
228
|
if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex);
|
|
@@ -235,13 +252,22 @@ const VirtualizedTree = _react.forwardRef(
|
|
|
235
252
|
style: {
|
|
236
253
|
minBlockSize: virtualizer.getTotalSize(),
|
|
237
254
|
},
|
|
255
|
+
ref: virtualizerRootRef,
|
|
238
256
|
},
|
|
239
257
|
_react.createElement('slot', null),
|
|
240
258
|
),
|
|
241
259
|
),
|
|
242
260
|
_react.createElement(
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
_TreeContext.VirtualizedTreeContext.Provider,
|
|
262
|
+
{
|
|
263
|
+
value: _react.useMemo(
|
|
264
|
+
() => ({
|
|
265
|
+
virtualizer,
|
|
266
|
+
onVirtualizerChange,
|
|
267
|
+
}),
|
|
268
|
+
[virtualizer, onVirtualizerChange],
|
|
269
|
+
),
|
|
270
|
+
},
|
|
245
271
|
virtualizer
|
|
246
272
|
.getVirtualItems()
|
|
247
273
|
.map((virtualItem) =>
|
|
@@ -252,3 +278,12 @@ const VirtualizedTree = _react.forwardRef(
|
|
|
252
278
|
);
|
|
253
279
|
},
|
|
254
280
|
);
|
|
281
|
+
function debounce(callback, delay) {
|
|
282
|
+
let timeoutId;
|
|
283
|
+
return (...args) => {
|
|
284
|
+
if (timeoutId) window.clearTimeout(timeoutId);
|
|
285
|
+
timeoutId = window.setTimeout(() => {
|
|
286
|
+
callback(...args);
|
|
287
|
+
}, delay);
|
|
288
|
+
};
|
|
289
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import type { Virtualizer } from '@tanstack/react-virtual';
|
|
2
3
|
export type TreeContextProps = {
|
|
3
4
|
/**
|
|
4
5
|
* Depth of the node.
|
|
@@ -31,3 +32,7 @@ export type TreeContextProps = {
|
|
|
31
32
|
};
|
|
32
33
|
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
33
34
|
export declare const useTreeContext: () => TreeContextProps;
|
|
35
|
+
export declare const VirtualizedTreeContext: React.Context<{
|
|
36
|
+
virtualizer?: Virtualizer<Element, Element>;
|
|
37
|
+
onVirtualizerChange?: (virtualizer?: Virtualizer<Element, Element>) => void;
|
|
38
|
+
} | undefined>;
|
|
@@ -13,6 +13,9 @@ _export(exports, {
|
|
|
13
13
|
TreeContext: function () {
|
|
14
14
|
return TreeContext;
|
|
15
15
|
},
|
|
16
|
+
VirtualizedTreeContext: function () {
|
|
17
|
+
return VirtualizedTreeContext;
|
|
18
|
+
},
|
|
16
19
|
useTreeContext: function () {
|
|
17
20
|
return useTreeContext;
|
|
18
21
|
},
|
|
@@ -26,3 +29,4 @@ const useTreeContext = () => {
|
|
|
26
29
|
throw new Error('TreeContext must be used within a TreeContext.Provider');
|
|
27
30
|
return context;
|
|
28
31
|
};
|
|
32
|
+
const VirtualizedTreeContext = _react.createContext(void 0);
|
|
@@ -52,6 +52,8 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
52
52
|
groupSize,
|
|
53
53
|
indexInGroup,
|
|
54
54
|
} = (0, _TreeContext.useTreeContext)();
|
|
55
|
+
let { virtualizer, onVirtualizerChange } =
|
|
56
|
+
_react.useContext(_TreeContext.VirtualizedTreeContext) ?? {};
|
|
55
57
|
let [isFocused, setIsFocused] = _react.useState(false);
|
|
56
58
|
let nodeRef = _react.useRef(null);
|
|
57
59
|
let onKeyDown = (event) => {
|
|
@@ -64,6 +66,7 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
64
66
|
if (isNodeFocused) {
|
|
65
67
|
if (isExpanded) {
|
|
66
68
|
onExpanded(nodeId, false);
|
|
69
|
+
onVirtualizerChange?.(virtualizer);
|
|
67
70
|
break;
|
|
68
71
|
}
|
|
69
72
|
if (parentNodeId) scrollToParent?.();
|
|
@@ -87,6 +90,7 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
87
90
|
if (isNodeFocused) {
|
|
88
91
|
if (!isExpanded && hasSubNodes) {
|
|
89
92
|
onExpanded(nodeId, true);
|
|
93
|
+
onVirtualizerChange?.(virtualizer);
|
|
90
94
|
break;
|
|
91
95
|
}
|
|
92
96
|
focusableElements[0]?.focus();
|
|
@@ -113,9 +117,10 @@ const TreeNode = _react.forwardRef((props, forwardedRef) => {
|
|
|
113
117
|
let onExpanderClick = _react.useCallback(
|
|
114
118
|
(event) => {
|
|
115
119
|
onExpanded(nodeId, !isExpanded);
|
|
120
|
+
onVirtualizerChange?.(virtualizer);
|
|
116
121
|
event.stopPropagation();
|
|
117
122
|
},
|
|
118
|
-
[isExpanded, nodeId, onExpanded],
|
|
123
|
+
[isExpanded, nodeId, onExpanded, onVirtualizerChange, virtualizer],
|
|
119
124
|
);
|
|
120
125
|
return _react.createElement(
|
|
121
126
|
_index.Box,
|
package/cjs/styles.js
CHANGED
package/esm/core/Menu/Menu.js
CHANGED
|
@@ -162,6 +162,7 @@ export const Menu = React.forwardRef((props, ref) => {
|
|
|
162
162
|
let reference = cloneElementWithRef(trigger, (triggerChild) =>
|
|
163
163
|
getReferenceProps(
|
|
164
164
|
popover.getReferenceProps({
|
|
165
|
+
'aria-haspopup': 'menu',
|
|
165
166
|
...triggerChild.props,
|
|
166
167
|
'aria-expanded': popover.open,
|
|
167
168
|
ref: mergeRefs(triggerRef, popover.refs.setReference),
|
package/esm/core/Tree/Tree.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
useMergedRefs,
|
|
9
9
|
useLayoutEffect,
|
|
10
10
|
} from '../../utils/index.js';
|
|
11
|
-
import { TreeContext } from './TreeContext.js';
|
|
11
|
+
import { TreeContext, VirtualizedTreeContext } from './TreeContext.js';
|
|
12
12
|
export const Tree = (props) => {
|
|
13
13
|
let {
|
|
14
14
|
data,
|
|
@@ -191,15 +191,32 @@ let TreeElement = polymorphic.div('iui-tree', {
|
|
|
191
191
|
let VirtualizedTree = React.forwardRef(
|
|
192
192
|
({ flatNodesList, itemRenderer, scrollToIndex, ...rest }, ref) => {
|
|
193
193
|
let parentRef = React.useRef(null);
|
|
194
|
+
let virtualizerRootRef = React.useRef(null);
|
|
194
195
|
let getItemKey = React.useCallback(
|
|
195
196
|
(index) => flatNodesList[index].nodeProps.nodeId,
|
|
196
197
|
[flatNodesList],
|
|
197
198
|
);
|
|
199
|
+
let onVirtualizerChange = React.useMemo(
|
|
200
|
+
() =>
|
|
201
|
+
debounce((virtualizer) => {
|
|
202
|
+
if (!virtualizer || !virtualizerRootRef.current) return;
|
|
203
|
+
virtualizerRootRef.current.style.width = '';
|
|
204
|
+
let widestNodeWidth = 0;
|
|
205
|
+
virtualizer.elementsCache.forEach((el) => {
|
|
206
|
+
if (el.clientWidth > widestNodeWidth)
|
|
207
|
+
widestNodeWidth = el.clientWidth;
|
|
208
|
+
});
|
|
209
|
+
if (widestNodeWidth)
|
|
210
|
+
virtualizerRootRef.current.style.width = `${widestNodeWidth}px`;
|
|
211
|
+
}, 100),
|
|
212
|
+
[],
|
|
213
|
+
);
|
|
198
214
|
let { virtualizer, css: virtualizerCss } = useVirtualScroll({
|
|
199
215
|
count: flatNodesList.length,
|
|
200
216
|
getScrollElement: () => parentRef.current,
|
|
201
217
|
estimateSize: () => 39,
|
|
202
218
|
getItemKey,
|
|
219
|
+
onChange: onVirtualizerChange,
|
|
203
220
|
});
|
|
204
221
|
useLayoutEffect(() => {
|
|
205
222
|
if (scrollToIndex) virtualizer.scrollToIndex(scrollToIndex);
|
|
@@ -229,13 +246,22 @@ let VirtualizedTree = React.forwardRef(
|
|
|
229
246
|
style: {
|
|
230
247
|
minBlockSize: virtualizer.getTotalSize(),
|
|
231
248
|
},
|
|
249
|
+
ref: virtualizerRootRef,
|
|
232
250
|
},
|
|
233
251
|
React.createElement('slot', null),
|
|
234
252
|
),
|
|
235
253
|
),
|
|
236
254
|
React.createElement(
|
|
237
|
-
|
|
238
|
-
|
|
255
|
+
VirtualizedTreeContext.Provider,
|
|
256
|
+
{
|
|
257
|
+
value: React.useMemo(
|
|
258
|
+
() => ({
|
|
259
|
+
virtualizer,
|
|
260
|
+
onVirtualizerChange,
|
|
261
|
+
}),
|
|
262
|
+
[virtualizer, onVirtualizerChange],
|
|
263
|
+
),
|
|
264
|
+
},
|
|
239
265
|
virtualizer
|
|
240
266
|
.getVirtualItems()
|
|
241
267
|
.map((virtualItem) =>
|
|
@@ -246,3 +272,12 @@ let VirtualizedTree = React.forwardRef(
|
|
|
246
272
|
);
|
|
247
273
|
},
|
|
248
274
|
);
|
|
275
|
+
function debounce(callback, delay) {
|
|
276
|
+
let timeoutId;
|
|
277
|
+
return (...args) => {
|
|
278
|
+
if (timeoutId) window.clearTimeout(timeoutId);
|
|
279
|
+
timeoutId = window.setTimeout(() => {
|
|
280
|
+
callback(...args);
|
|
281
|
+
}, delay);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import type { Virtualizer } from '@tanstack/react-virtual';
|
|
2
3
|
export type TreeContextProps = {
|
|
3
4
|
/**
|
|
4
5
|
* Depth of the node.
|
|
@@ -31,3 +32,7 @@ export type TreeContextProps = {
|
|
|
31
32
|
};
|
|
32
33
|
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
33
34
|
export declare const useTreeContext: () => TreeContextProps;
|
|
35
|
+
export declare const VirtualizedTreeContext: React.Context<{
|
|
36
|
+
virtualizer?: Virtualizer<Element, Element>;
|
|
37
|
+
onVirtualizerChange?: (virtualizer?: Virtualizer<Element, Element>) => void;
|
|
38
|
+
} | undefined>;
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from '../../utils/index.js';
|
|
8
8
|
import cx from 'classnames';
|
|
9
9
|
import { TreeNodeExpander } from './TreeNodeExpander.js';
|
|
10
|
-
import { useTreeContext } from './TreeContext.js';
|
|
10
|
+
import { useTreeContext, VirtualizedTreeContext } from './TreeContext.js';
|
|
11
11
|
export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
12
12
|
let {
|
|
13
13
|
nodeId,
|
|
@@ -43,6 +43,8 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
43
43
|
groupSize,
|
|
44
44
|
indexInGroup,
|
|
45
45
|
} = useTreeContext();
|
|
46
|
+
let { virtualizer, onVirtualizerChange } =
|
|
47
|
+
React.useContext(VirtualizedTreeContext) ?? {};
|
|
46
48
|
let [isFocused, setIsFocused] = React.useState(false);
|
|
47
49
|
let nodeRef = React.useRef(null);
|
|
48
50
|
let onKeyDown = (event) => {
|
|
@@ -55,6 +57,7 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
55
57
|
if (isNodeFocused) {
|
|
56
58
|
if (isExpanded) {
|
|
57
59
|
onExpanded(nodeId, false);
|
|
60
|
+
onVirtualizerChange?.(virtualizer);
|
|
58
61
|
break;
|
|
59
62
|
}
|
|
60
63
|
if (parentNodeId) scrollToParent?.();
|
|
@@ -74,6 +77,7 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
74
77
|
if (isNodeFocused) {
|
|
75
78
|
if (!isExpanded && hasSubNodes) {
|
|
76
79
|
onExpanded(nodeId, true);
|
|
80
|
+
onVirtualizerChange?.(virtualizer);
|
|
77
81
|
break;
|
|
78
82
|
}
|
|
79
83
|
focusableElements[0]?.focus();
|
|
@@ -100,9 +104,10 @@ export const TreeNode = React.forwardRef((props, forwardedRef) => {
|
|
|
100
104
|
let onExpanderClick = React.useCallback(
|
|
101
105
|
(event) => {
|
|
102
106
|
onExpanded(nodeId, !isExpanded);
|
|
107
|
+
onVirtualizerChange?.(virtualizer);
|
|
103
108
|
event.stopPropagation();
|
|
104
109
|
},
|
|
105
|
-
[isExpanded, nodeId, onExpanded],
|
|
110
|
+
[isExpanded, nodeId, onExpanded, onVirtualizerChange, virtualizer],
|
|
106
111
|
);
|
|
107
112
|
return React.createElement(
|
|
108
113
|
Box,
|
package/esm/styles.js
CHANGED