@itwin/itwinui-react 1.38.0 → 1.38.1
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 +6 -0
- package/cjs/core/ComboBox/ComboBox.d.ts +7 -0
- package/cjs/core/ComboBox/ComboBox.js +18 -11
- package/cjs/core/ComboBox/ComboBoxMenu.js +38 -2
- package/cjs/core/ComboBox/ComboBoxMenuItem.js +2 -3
- package/cjs/core/ComboBox/helpers.d.ts +8 -3
- package/cjs/core/Select/Select.js +1 -1
- package/cjs/core/Toast/Toaster.d.ts +1 -1
- package/cjs/core/Toast/Toaster.js +28 -64
- package/cjs/core/utils/components/VirtualScroll.d.ts +35 -1
- package/cjs/core/utils/components/VirtualScroll.js +159 -26
- package/esm/core/ComboBox/ComboBox.d.ts +7 -0
- package/esm/core/ComboBox/ComboBox.js +18 -11
- package/esm/core/ComboBox/ComboBoxMenu.js +39 -3
- package/esm/core/ComboBox/ComboBoxMenuItem.js +2 -3
- package/esm/core/ComboBox/helpers.d.ts +8 -3
- package/esm/core/Select/Select.js +1 -1
- package/esm/core/Toast/Toaster.d.ts +1 -1
- package/esm/core/Toast/Toaster.js +28 -64
- package/esm/core/utils/components/VirtualScroll.d.ts +35 -1
- package/esm/core/utils/components/VirtualScroll.js +157 -25
- package/package.json +1 -1
|
@@ -25,12 +25,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.VirtualScroll = void 0;
|
|
28
|
+
exports.useVirtualization = exports.VirtualScroll = void 0;
|
|
29
29
|
/*---------------------------------------------------------------------------------------------
|
|
30
30
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
31
31
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
32
32
|
*--------------------------------------------------------------------------------------------*/
|
|
33
33
|
var react_1 = __importDefault(require("react"));
|
|
34
|
+
var hooks_1 = require("../hooks");
|
|
34
35
|
var useResizeObserver_1 = require("../hooks/useResizeObserver");
|
|
35
36
|
var getScrollableParent = function (element, ownerDocument) {
|
|
36
37
|
if (ownerDocument === void 0) { ownerDocument = document; }
|
|
@@ -52,6 +53,14 @@ var getElementHeight = function (element) {
|
|
|
52
53
|
var _a;
|
|
53
54
|
return (_a = element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height) !== null && _a !== void 0 ? _a : 0;
|
|
54
55
|
};
|
|
56
|
+
var getElementHeightWithMargins = function (element) {
|
|
57
|
+
if (!element) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
var margin = parseFloat(getElementStyle(element, 'margin-top')) +
|
|
61
|
+
parseFloat(getElementStyle(element, 'margin-bottom'));
|
|
62
|
+
return getElementHeight(element) + (isNaN(margin) ? 0 : margin);
|
|
63
|
+
};
|
|
55
64
|
var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
|
|
56
65
|
if (!childHeight) {
|
|
57
66
|
return 0;
|
|
@@ -59,7 +68,10 @@ var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
|
|
|
59
68
|
return Math.floor(totalHeight / childHeight);
|
|
60
69
|
};
|
|
61
70
|
var getTranslateValue = function (childHeight, startIndex) {
|
|
62
|
-
|
|
71
|
+
if (startIndex > 0) {
|
|
72
|
+
return childHeight * startIndex;
|
|
73
|
+
}
|
|
74
|
+
return 0;
|
|
63
75
|
};
|
|
64
76
|
var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scrollContainer) {
|
|
65
77
|
return Math.min(childrenLength - startIndex, getNumberOfNodesInHeight(childHeight, getElementHeight(scrollContainer)));
|
|
@@ -85,21 +97,55 @@ var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scr
|
|
|
85
97
|
* />
|
|
86
98
|
* @private
|
|
87
99
|
*/
|
|
88
|
-
exports.VirtualScroll = react_1.default.forwardRef(function (
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
exports.VirtualScroll = react_1.default.forwardRef(function (props, ref) {
|
|
101
|
+
var _a = (0, exports.useVirtualization)(props), innerProps = _a.innerProps, outerProps = _a.outerProps, visibleChildren = _a.visibleChildren;
|
|
102
|
+
return (react_1.default.createElement("div", __assign({}, outerProps, { ref: ref }),
|
|
103
|
+
react_1.default.createElement("div", __assign({}, innerProps), visibleChildren)));
|
|
104
|
+
});
|
|
105
|
+
/**
|
|
106
|
+
* `useVirtualization` is used for efficiently rendering only the visible rows from a large list.
|
|
107
|
+
* It returns `outerProps` and `innerProps`, which need to be applied on 2 container elements and `visibleChildren` which is a list of virtualized items.
|
|
108
|
+
* @example
|
|
109
|
+
* const itemRenderer = React.useCallback((index: number) => (
|
|
110
|
+
* <li key={index}>
|
|
111
|
+
* This is my item #{index}
|
|
112
|
+
* </li>
|
|
113
|
+
* ), [])
|
|
114
|
+
*
|
|
115
|
+
* const { outerProps, innerProps, visibleChildren } = useVirtualization({itemsLength: 1000, itemRenderer: itemRenderer});
|
|
116
|
+
* return (
|
|
117
|
+
* <div {...outerProps}>
|
|
118
|
+
* <ul {...innerProps}>
|
|
119
|
+
* {visibleChildren}
|
|
120
|
+
* </ul>
|
|
121
|
+
* </div>
|
|
122
|
+
* );
|
|
123
|
+
* @private
|
|
124
|
+
*/
|
|
125
|
+
var useVirtualization = function (props) {
|
|
126
|
+
var itemsLength = props.itemsLength, itemRenderer = props.itemRenderer, _a = props.bufferSize, bufferSize = _a === void 0 ? 10 : _a, scrollToIndex = props.scrollToIndex, style = props.style, rest = __rest(props, ["itemsLength", "itemRenderer", "bufferSize", "scrollToIndex", "style"]);
|
|
127
|
+
var _b = react_1.default.useState(0), startNode = _b[0], setStartNode = _b[1];
|
|
128
|
+
var _c = react_1.default.useState(0), visibleNodeCount = _c[0], setVisibleNodeCount = _c[1];
|
|
92
129
|
var scrollContainer = react_1.default.useRef();
|
|
93
130
|
var parentRef = react_1.default.useRef(null);
|
|
94
|
-
var childHeight = react_1.default.useRef(0);
|
|
131
|
+
var childHeight = react_1.default.useRef({ first: 0, middle: 0, last: 0 });
|
|
95
132
|
var onScrollRef = react_1.default.useRef();
|
|
96
133
|
// Used only to recalculate on resize
|
|
97
|
-
var
|
|
134
|
+
var _d = react_1.default.useState(0), scrollContainerHeight = _d[0], setScrollContainerHeight = _d[1];
|
|
135
|
+
var visibleIndex = react_1.default.useRef({ start: 0, end: 0 });
|
|
136
|
+
// Used to mark when scroll container has height (updated by resize observer)
|
|
137
|
+
// because before that calculations are not right
|
|
138
|
+
var _e = react_1.default.useState(false), isMounted = _e[0], setIsMounted = _e[1];
|
|
98
139
|
var onResize = react_1.default.useCallback(function (_a) {
|
|
99
140
|
var height = _a.height;
|
|
141
|
+
// Initial value returned by resize observer is 0
|
|
142
|
+
// So wait for the next one
|
|
143
|
+
if (height > 0) {
|
|
144
|
+
setIsMounted(true);
|
|
145
|
+
}
|
|
100
146
|
setScrollContainerHeight(height);
|
|
101
147
|
}, []);
|
|
102
|
-
var
|
|
148
|
+
var _f = (0, useResizeObserver_1.useResizeObserver)(onResize), resizeRef = _f[0], resizeObserver = _f[1];
|
|
103
149
|
// Find scrollable parent
|
|
104
150
|
// Needed only on init
|
|
105
151
|
react_1.default.useLayoutEffect(function () {
|
|
@@ -108,6 +154,14 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
|
|
|
108
154
|
scrollContainer.current = scrollableParent;
|
|
109
155
|
resizeRef(scrollableParent);
|
|
110
156
|
}, [resizeRef]);
|
|
157
|
+
// Stop watching resize, when virtual scroll is unmounted
|
|
158
|
+
react_1.default.useLayoutEffect(function () {
|
|
159
|
+
return function () { return resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect(); };
|
|
160
|
+
}, [resizeObserver]);
|
|
161
|
+
var getScrollableContainer = function () {
|
|
162
|
+
var _a, _b;
|
|
163
|
+
return (_a = scrollContainer.current) !== null && _a !== void 0 ? _a : (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.scrollingElement;
|
|
164
|
+
};
|
|
111
165
|
var visibleChildren = react_1.default.useMemo(function () {
|
|
112
166
|
var arr = [];
|
|
113
167
|
var endIndex = Math.min(itemsLength, startNode + visibleNodeCount + bufferSize * 2);
|
|
@@ -118,27 +172,42 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
|
|
|
118
172
|
}, [itemsLength, itemRenderer, bufferSize, startNode, visibleNodeCount]);
|
|
119
173
|
// Get child height when children available
|
|
120
174
|
react_1.default.useLayoutEffect(function () {
|
|
175
|
+
var _a, _b, _c, _d, _e, _f;
|
|
121
176
|
if (!parentRef.current || !visibleChildren.length) {
|
|
122
177
|
return;
|
|
123
178
|
}
|
|
124
179
|
var firstChild = parentRef.current.children.item(0);
|
|
125
|
-
|
|
180
|
+
var secondChild = parentRef.current.children.item(1);
|
|
181
|
+
var lastChild = parentRef.current.children.item(parentRef.current.children.length - 1);
|
|
182
|
+
var firstChildHeight = Number((_b = (_a = getElementHeightWithMargins(firstChild)) === null || _a === void 0 ? void 0 : _a.toFixed(2)) !== null && _b !== void 0 ? _b : 0);
|
|
183
|
+
childHeight.current = {
|
|
184
|
+
first: firstChildHeight,
|
|
185
|
+
middle: Number((_d = (_c = getElementHeightWithMargins(secondChild)) === null || _c === void 0 ? void 0 : _c.toFixed(2)) !== null && _d !== void 0 ? _d : firstChildHeight),
|
|
186
|
+
last: Number((_f = (_e = getElementHeightWithMargins(lastChild)) === null || _e === void 0 ? void 0 : _e.toFixed(2)) !== null && _f !== void 0 ? _f : firstChildHeight),
|
|
187
|
+
};
|
|
126
188
|
}, [visibleChildren.length]);
|
|
127
189
|
var updateVirtualScroll = react_1.default.useCallback(function () {
|
|
128
|
-
var
|
|
129
|
-
var scrollableContainer = (_a = scrollContainer.current) !== null && _a !== void 0 ? _a : (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.scrollingElement;
|
|
190
|
+
var scrollableContainer = getScrollableContainer();
|
|
130
191
|
if (!scrollableContainer) {
|
|
131
192
|
return;
|
|
132
193
|
}
|
|
133
|
-
var start = getNumberOfNodesInHeight(childHeight.current, scrollableContainer.scrollTop);
|
|
134
|
-
var
|
|
194
|
+
var start = getNumberOfNodesInHeight(childHeight.current.middle, Math.round(scrollableContainer.scrollTop));
|
|
195
|
+
var visibleNodes = getVisibleNodeCount(childHeight.current.middle, start, itemsLength, scrollableContainer);
|
|
196
|
+
// If there are less items at the end than buffer size
|
|
197
|
+
// show more items at the start.
|
|
198
|
+
// Have boundaries for edge cases, e.g. 1 item length
|
|
199
|
+
var startIndex = Math.min(Math.max(0, start - bufferSize), Math.max(0, itemsLength - bufferSize * 2 - visibleNodes));
|
|
200
|
+
visibleIndex.current = { start: start, end: start + visibleNodes };
|
|
135
201
|
setStartNode(startIndex);
|
|
136
|
-
setVisibleNodeCount(
|
|
202
|
+
setVisibleNodeCount(visibleNodes);
|
|
137
203
|
if (!parentRef.current) {
|
|
138
204
|
return;
|
|
139
205
|
}
|
|
140
|
-
parentRef.current.style.transform = "translateY(".concat(getTranslateValue(childHeight.current, startIndex), "px)");
|
|
206
|
+
parentRef.current.style.transform = "translateY(".concat(getTranslateValue(childHeight.current.middle, startIndex), "px)");
|
|
141
207
|
}, [bufferSize, itemsLength]);
|
|
208
|
+
var onScroll = react_1.default.useCallback(function () {
|
|
209
|
+
updateVirtualScroll();
|
|
210
|
+
}, [updateVirtualScroll]);
|
|
142
211
|
var removeScrollListener = react_1.default.useCallback(function () {
|
|
143
212
|
var _a, _b;
|
|
144
213
|
if (!onScrollRef.current) {
|
|
@@ -153,22 +222,86 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
|
|
|
153
222
|
react_1.default.useLayoutEffect(function () {
|
|
154
223
|
var _a, _b;
|
|
155
224
|
removeScrollListener();
|
|
156
|
-
onScrollRef.current =
|
|
225
|
+
onScrollRef.current = onScroll;
|
|
157
226
|
if (!scrollContainer.current ||
|
|
158
227
|
scrollContainer.current === ((_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.body)) {
|
|
159
|
-
(_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.addEventListener('scroll',
|
|
228
|
+
(_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.addEventListener('scroll', onScroll);
|
|
160
229
|
}
|
|
161
230
|
else {
|
|
162
|
-
scrollContainer.current.addEventListener('scroll',
|
|
231
|
+
scrollContainer.current.addEventListener('scroll', onScroll);
|
|
163
232
|
}
|
|
164
233
|
return removeScrollListener;
|
|
165
|
-
}, [
|
|
234
|
+
}, [onScroll, removeScrollListener]);
|
|
235
|
+
react_1.default.useLayoutEffect(function () {
|
|
236
|
+
if (!isMounted) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
var scrollableContainer = getScrollableContainer();
|
|
240
|
+
if (!scrollableContainer || scrollToIndex == null) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// if `scrollToIndex` is not visible, scroll to it
|
|
244
|
+
if (scrollToIndex > visibleIndex.current.end ||
|
|
245
|
+
scrollToIndex < visibleIndex.current.start) {
|
|
246
|
+
var indexDiff = scrollToIndex > visibleIndex.current.end
|
|
247
|
+
? scrollToIndex - visibleIndex.current.end
|
|
248
|
+
: scrollToIndex - visibleIndex.current.start;
|
|
249
|
+
if (scrollToIndex === 0) {
|
|
250
|
+
scrollableContainer.scrollTo({ top: 0 });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// If go down: add to the existing scrollTop needed height
|
|
254
|
+
// If go up: calculate the exact scroll top
|
|
255
|
+
scrollableContainer.scrollTo({
|
|
256
|
+
top: indexDiff > 0
|
|
257
|
+
? Math.ceil(scrollableContainer.scrollTop) +
|
|
258
|
+
indexDiff * childHeight.current.middle
|
|
259
|
+
: scrollToIndex * childHeight.current.middle,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
// if `scrollToIndex` is the first visible node
|
|
263
|
+
// ensure it is fully visible
|
|
264
|
+
if (scrollToIndex === visibleIndex.current.start) {
|
|
265
|
+
var roundedScrollTop = Math.round(scrollableContainer.scrollTop);
|
|
266
|
+
var diff = roundedScrollTop % childHeight.current.middle;
|
|
267
|
+
diff > 0 &&
|
|
268
|
+
scrollableContainer.scrollTo({
|
|
269
|
+
top: roundedScrollTop - diff,
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// if `scrollToIndex` is the last visible node
|
|
274
|
+
// ensure it is fully visible
|
|
275
|
+
if (scrollToIndex === visibleIndex.current.end) {
|
|
276
|
+
var diff = (scrollableContainer.offsetHeight - childHeight.current.first) %
|
|
277
|
+
childHeight.current.middle;
|
|
278
|
+
var roundedScrollTop = Math.ceil(scrollableContainer.scrollTop);
|
|
279
|
+
var scrollTopMod = roundedScrollTop % childHeight.current.middle;
|
|
280
|
+
if (diff > 0 && scrollTopMod === 0) {
|
|
281
|
+
scrollableContainer.scrollTo({
|
|
282
|
+
top: roundedScrollTop + childHeight.current.middle - diff,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}, [scrollToIndex, isMounted]);
|
|
166
287
|
react_1.default.useLayoutEffect(function () {
|
|
288
|
+
if (!scrollContainerHeight) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
167
291
|
updateVirtualScroll();
|
|
168
|
-
}, [scrollContainerHeight,
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
292
|
+
}, [scrollContainerHeight, updateVirtualScroll]);
|
|
293
|
+
return {
|
|
294
|
+
outerProps: __assign({ style: __assign({ overflow: 'hidden', minHeight: itemsLength > 1
|
|
295
|
+
? Math.max(itemsLength - 2, 0) * childHeight.current.middle +
|
|
296
|
+
childHeight.current.first +
|
|
297
|
+
childHeight.current.last
|
|
298
|
+
: childHeight.current.middle, width: '100%' }, style) }, rest),
|
|
299
|
+
innerProps: {
|
|
300
|
+
style: { willChange: 'transform' },
|
|
301
|
+
ref: (0, hooks_1.mergeRefs)(parentRef), // convert object ref to callback ref for better types
|
|
302
|
+
},
|
|
303
|
+
visibleChildren: visibleChildren,
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
exports.useVirtualization = useVirtualization;
|
|
174
307
|
exports.default = exports.VirtualScroll;
|
|
@@ -51,6 +51,13 @@ export declare type ComboBoxProps<T> = {
|
|
|
51
51
|
id: string;
|
|
52
52
|
index: number;
|
|
53
53
|
}) => JSX.Element;
|
|
54
|
+
/**
|
|
55
|
+
* If enabled, virtualization is used for the scrollable dropdown list.
|
|
56
|
+
* Use it if you expect a very long list of items.
|
|
57
|
+
* @default false
|
|
58
|
+
* @beta
|
|
59
|
+
*/
|
|
60
|
+
enableVirtualization?: boolean;
|
|
54
61
|
} & Pick<InputContainerProps, 'status'> & Omit<CommonProps, 'title'>;
|
|
55
62
|
/**
|
|
56
63
|
* ComboBox component that allows typing a value to filter the options in dropdown list.
|
|
@@ -57,7 +57,7 @@ var getOptionId = function (option, idPrefix) {
|
|
|
57
57
|
*/
|
|
58
58
|
export var ComboBox = function (props) {
|
|
59
59
|
var _a;
|
|
60
|
-
var options = props.options, valueProp = props.value, onChange = props.onChange, filterFunction = props.filterFunction, inputProps = props.inputProps, dropdownMenuProps = props.dropdownMenuProps, _b = props.emptyStateMessage, emptyStateMessage = _b === void 0 ? 'No options found' : _b, itemRenderer = props.itemRenderer, rest = __rest(props, ["options", "value", "onChange", "filterFunction", "inputProps", "dropdownMenuProps", "emptyStateMessage", "itemRenderer"]);
|
|
60
|
+
var options = props.options, valueProp = props.value, onChange = props.onChange, filterFunction = props.filterFunction, inputProps = props.inputProps, dropdownMenuProps = props.dropdownMenuProps, _b = props.emptyStateMessage, emptyStateMessage = _b === void 0 ? 'No options found' : _b, itemRenderer = props.itemRenderer, _c = props.enableVirtualization, enableVirtualization = _c === void 0 ? false : _c, rest = __rest(props, ["options", "value", "onChange", "filterFunction", "inputProps", "dropdownMenuProps", "emptyStateMessage", "itemRenderer", "enableVirtualization"]);
|
|
61
61
|
// Generate a stateful random id if not specified
|
|
62
62
|
var id = React.useState(function () {
|
|
63
63
|
var _a, _b;
|
|
@@ -89,11 +89,11 @@ export var ComboBox = function (props) {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
// Reducer where all the component-wide state is stored
|
|
92
|
-
var
|
|
92
|
+
var _d = React.useReducer(comboBoxReducer, {
|
|
93
93
|
isOpen: false,
|
|
94
94
|
selectedIndex: -1,
|
|
95
95
|
focusedIndex: -1,
|
|
96
|
-
}),
|
|
96
|
+
}), _e = _d[0], isOpen = _e.isOpen, selectedIndex = _e.selectedIndex, focusedIndex = _e.focusedIndex, dispatch = _d[1];
|
|
97
97
|
React.useEffect(function () {
|
|
98
98
|
var _a, _b;
|
|
99
99
|
// When the dropdown opens
|
|
@@ -112,19 +112,19 @@ export var ComboBox = function (props) {
|
|
|
112
112
|
}
|
|
113
113
|
}, [isOpen, options, selectedIndex]);
|
|
114
114
|
// Set min-width of menu to be same as input
|
|
115
|
-
var
|
|
115
|
+
var _f = React.useState(0), minWidth = _f[0], setMinWidth = _f[1];
|
|
116
116
|
React.useEffect(function () {
|
|
117
117
|
if (inputRef.current) {
|
|
118
118
|
setMinWidth(inputRef.current.offsetWidth);
|
|
119
119
|
}
|
|
120
120
|
}, [isOpen]);
|
|
121
121
|
// Initialize filtered options to the latest value options
|
|
122
|
-
var
|
|
122
|
+
var _g = React.useState(options), filteredOptions = _g[0], setFilteredOptions = _g[1];
|
|
123
123
|
React.useEffect(function () {
|
|
124
124
|
setFilteredOptions(options);
|
|
125
125
|
}, [options]);
|
|
126
126
|
// Filter options based on input value
|
|
127
|
-
var
|
|
127
|
+
var _h = React.useState((_a = inputProps === null || inputProps === void 0 ? void 0 : inputProps.value) !== null && _a !== void 0 ? _a : ''), inputValue = _h[0], setInputValue = _h[1];
|
|
128
128
|
var handleOnInput = React.useCallback(function (event) {
|
|
129
129
|
var _a, _b;
|
|
130
130
|
var value = event.currentTarget.value;
|
|
@@ -181,21 +181,28 @@ export var ComboBox = function (props) {
|
|
|
181
181
|
}),
|
|
182
182
|
'data-iui-index': __originalIndex,
|
|
183
183
|
ref: mergeRefs(customItem.props.ref, function (el) {
|
|
184
|
-
|
|
185
|
-
if (focusedIndex === __originalIndex) {
|
|
184
|
+
if (!enableVirtualization && focusedIndex === __originalIndex) {
|
|
186
185
|
el === null || el === void 0 ? void 0 : el.scrollIntoView({ block: 'nearest' });
|
|
187
186
|
}
|
|
188
187
|
}),
|
|
189
188
|
})) : (React.createElement(ComboBoxMenuItem, __assign({ key: optionId, id: optionId }, option, { isSelected: selectedIndex === __originalIndex, onClick: function () { return dispatch(['select', __originalIndex]); }, index: __originalIndex }), option.label));
|
|
190
|
-
}, [focusedIndex, id, itemRenderer, selectedIndex]);
|
|
189
|
+
}, [enableVirtualization, focusedIndex, id, itemRenderer, selectedIndex]);
|
|
191
190
|
return (React.createElement(ComboBoxRefsContext.Provider, { value: { inputRef: inputRef, menuRef: menuRef, toggleButtonRef: toggleButtonRef, optionsExtraInfoRef: optionsExtraInfoRef } },
|
|
192
191
|
React.createElement(ComboBoxActionContext.Provider, { value: dispatch },
|
|
193
|
-
React.createElement(ComboBoxStateContext.Provider, { value: {
|
|
192
|
+
React.createElement(ComboBoxStateContext.Provider, { value: {
|
|
193
|
+
id: id,
|
|
194
|
+
minWidth: minWidth,
|
|
195
|
+
isOpen: isOpen,
|
|
196
|
+
focusedIndex: focusedIndex,
|
|
197
|
+
enableVirtualization: enableVirtualization,
|
|
198
|
+
filteredOptions: filteredOptions,
|
|
199
|
+
getMenuItem: getMenuItem,
|
|
200
|
+
} },
|
|
194
201
|
React.createElement(ComboBoxInputContainer, __assign({ disabled: inputProps === null || inputProps === void 0 ? void 0 : inputProps.disabled }, rest),
|
|
195
202
|
React.createElement(ComboBoxInput, __assign({ value: inputValue }, inputProps, { onChange: handleOnInput })),
|
|
196
203
|
React.createElement(ComboBoxEndIcon, { disabled: inputProps === null || inputProps === void 0 ? void 0 : inputProps.disabled, isOpen: isOpen })),
|
|
197
204
|
React.createElement(ComboBoxDropdown, __assign({}, dropdownMenuProps),
|
|
198
|
-
React.createElement(ComboBoxMenu, null, filteredOptions.length > 0 ? (filteredOptions.map(getMenuItem)) : (React.createElement(MenuExtraContent, null,
|
|
205
|
+
React.createElement(ComboBoxMenu, null, filteredOptions.length > 0 && !enableVirtualization ? (filteredOptions.map(getMenuItem)) : (React.createElement(MenuExtraContent, null,
|
|
199
206
|
React.createElement(Text, { isMuted: true }, emptyStateMessage)))))))));
|
|
200
207
|
};
|
|
201
208
|
export default ComboBox;
|
|
@@ -27,13 +27,49 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
27
27
|
import cx from 'classnames';
|
|
28
28
|
import React from 'react';
|
|
29
29
|
import { Menu } from '../Menu';
|
|
30
|
-
import {
|
|
30
|
+
import { Surface } from '../Surface';
|
|
31
|
+
import { useSafeContext, useMergedRefs, useVirtualization, mergeRefs, getWindow, } from '../utils';
|
|
31
32
|
import { ComboBoxStateContext, ComboBoxRefsContext } from './helpers';
|
|
33
|
+
var VirtualizedComboBoxMenu = React.forwardRef(function (_a, forwardedRef) {
|
|
34
|
+
var _b, _c, _d;
|
|
35
|
+
var children = _a.children, style = _a.style, rest = __rest(_a, ["children", "style"]);
|
|
36
|
+
var _e = useSafeContext(ComboBoxStateContext), minWidth = _e.minWidth, id = _e.id, filteredOptions = _e.filteredOptions, getMenuItem = _e.getMenuItem, focusedIndex = _e.focusedIndex;
|
|
37
|
+
var menuRef = useSafeContext(ComboBoxRefsContext).menuRef;
|
|
38
|
+
var virtualItemRenderer = React.useCallback(function (index) {
|
|
39
|
+
return filteredOptions.length > 0
|
|
40
|
+
? getMenuItem(filteredOptions[index])
|
|
41
|
+
: children;
|
|
42
|
+
}, // Here is expected empty state content
|
|
43
|
+
[filteredOptions, getMenuItem, children]);
|
|
44
|
+
var _f = useVirtualization({
|
|
45
|
+
// 'Fool' VirtualScroll by passing length 1
|
|
46
|
+
// whenever there is no elements, to show empty state message
|
|
47
|
+
itemsLength: filteredOptions.length || 1,
|
|
48
|
+
itemRenderer: virtualItemRenderer,
|
|
49
|
+
scrollToIndex: focusedIndex,
|
|
50
|
+
}), outerProps = _f.outerProps, innerProps = _f.innerProps, visibleChildren = _f.visibleChildren;
|
|
51
|
+
var overflowY = ((_d = (_c = (_b = getWindow()) === null || _b === void 0 ? void 0 : _b.CSS) === null || _c === void 0 ? void 0 : _c.supports) === null || _d === void 0 ? void 0 : _d.call(_c, 'overflow-x: overlay'))
|
|
52
|
+
? { overflowY: 'overlay' }
|
|
53
|
+
: { overflowY: 'auto' };
|
|
54
|
+
var styles = React.useMemo(function () { return ({
|
|
55
|
+
minWidth: minWidth,
|
|
56
|
+
maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"),
|
|
57
|
+
maxHeight: 315,
|
|
58
|
+
}); }, [minWidth]);
|
|
59
|
+
return (React.createElement(Surface, __assign({ elevation: 1, style: __assign(__assign(__assign({}, styles), overflowY), style) }, rest),
|
|
60
|
+
React.createElement("div", __assign({}, outerProps),
|
|
61
|
+
React.createElement(Menu, { id: "".concat(id, "-list"), setFocus: false, role: 'listbox', ref: mergeRefs(menuRef, innerProps.ref, forwardedRef), style: innerProps.style }, visibleChildren))));
|
|
62
|
+
});
|
|
32
63
|
export var ComboBoxMenu = React.forwardRef(function (props, forwardedRef) {
|
|
33
64
|
var className = props.className, style = props.style, rest = __rest(props, ["className", "style"]);
|
|
34
|
-
var _a = useSafeContext(ComboBoxStateContext), minWidth = _a.minWidth, id = _a.id;
|
|
65
|
+
var _a = useSafeContext(ComboBoxStateContext), minWidth = _a.minWidth, id = _a.id, enableVirtualization = _a.enableVirtualization;
|
|
35
66
|
var menuRef = useSafeContext(ComboBoxRefsContext).menuRef;
|
|
36
67
|
var refs = useMergedRefs(menuRef, forwardedRef);
|
|
37
|
-
|
|
68
|
+
var styles = React.useMemo(function () { return ({
|
|
69
|
+
minWidth: minWidth,
|
|
70
|
+
maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"),
|
|
71
|
+
maxHeight: 315,
|
|
72
|
+
}); }, [minWidth]);
|
|
73
|
+
return (React.createElement(React.Fragment, null, !enableVirtualization ? (React.createElement(Menu, __assign({ id: "".concat(id, "-list"), style: __assign(__assign({}, styles), style), setFocus: false, role: 'listbox', ref: refs, className: cx('iui-scroll', className) }, rest))) : (React.createElement(VirtualizedComboBoxMenu, __assign({ ref: forwardedRef }, props)))));
|
|
38
74
|
});
|
|
39
75
|
ComboBoxMenu.displayName = 'ComboBoxMenu';
|
|
@@ -30,10 +30,9 @@ import { useSafeContext, useMergedRefs } from '../utils';
|
|
|
30
30
|
import { ComboBoxStateContext } from './helpers';
|
|
31
31
|
export var ComboBoxMenuItem = React.memo(React.forwardRef(function (props, forwardedRef) {
|
|
32
32
|
var children = props.children, isSelected = props.isSelected, disabled = props.disabled, value = props.value, onClick = props.onClick, sublabel = props.sublabel, _a = props.size, size = _a === void 0 ? !!sublabel ? 'large' : 'default' : _a, icon = props.icon, badge = props.badge, className = props.className, _b = props.role, role = _b === void 0 ? 'menuitem' : _b, index = props.index, rest = __rest(props, ["children", "isSelected", "disabled", "value", "onClick", "sublabel", "size", "icon", "badge", "className", "role", "index"]);
|
|
33
|
-
var
|
|
33
|
+
var _c = useSafeContext(ComboBoxStateContext), focusedIndex = _c.focusedIndex, enableVirtualization = _c.enableVirtualization;
|
|
34
34
|
var focusRef = function (el) {
|
|
35
|
-
|
|
36
|
-
if (focusedIndex === index) {
|
|
35
|
+
if (!enableVirtualization && focusedIndex === index) {
|
|
37
36
|
el === null || el === void 0 ? void 0 : el.scrollIntoView({ block: 'nearest' });
|
|
38
37
|
}
|
|
39
38
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { SelectOption } from '../Select/Select';
|
|
2
3
|
declare type ComboBoxAction = 'open' | 'close' | 'select' | 'focus';
|
|
3
4
|
export declare const comboBoxReducer: (state: {
|
|
4
5
|
isOpen: boolean;
|
|
@@ -17,11 +18,15 @@ export declare const ComboBoxRefsContext: React.Context<{
|
|
|
17
18
|
__originalIndex: number;
|
|
18
19
|
}>>;
|
|
19
20
|
} | undefined>;
|
|
20
|
-
|
|
21
|
+
declare type ComboBoxStateContextProps<T = unknown> = {
|
|
21
22
|
isOpen: boolean;
|
|
22
23
|
id: string;
|
|
23
24
|
minWidth: number;
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
enableVirtualization: boolean;
|
|
26
|
+
filteredOptions: SelectOption<T>[];
|
|
27
|
+
getMenuItem: (option: SelectOption<T>) => JSX.Element;
|
|
28
|
+
focusedIndex?: number;
|
|
29
|
+
};
|
|
30
|
+
export declare const ComboBoxStateContext: React.Context<ComboBoxStateContextProps<unknown> | undefined>;
|
|
26
31
|
export declare const ComboBoxActionContext: React.Context<((x: [ComboBoxAction] | [ComboBoxAction, number]) => void) | undefined>;
|
|
27
32
|
export {};
|
|
@@ -140,7 +140,7 @@ export var Select = function (props) {
|
|
|
140
140
|
return options.find(function (option) { return option.value === value; });
|
|
141
141
|
}, [options, value]);
|
|
142
142
|
return (React.createElement("div", __assign({ className: cx('iui-input-with-icon', className), "aria-expanded": isOpen, "aria-haspopup": 'listbox', style: style }, rest),
|
|
143
|
-
React.createElement(DropdownMenu, __assign({ menuItems: menuItems, placement: 'bottom-start', className: cx('iui-scroll', menuClassName), style: __assign({ minWidth: minWidth, maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"), maxHeight:
|
|
143
|
+
React.createElement(DropdownMenu, __assign({ menuItems: menuItems, placement: 'bottom-start', className: cx('iui-scroll', menuClassName), style: __assign({ minWidth: minWidth, maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"), maxHeight: 315 }, menuStyle), role: 'listbox', onShow: onShowHandler, onHide: onHideHandler, disabled: disabled }, popoverProps, { visible: isOpen, onClickOutside: function (_, _a) {
|
|
144
144
|
var _b;
|
|
145
145
|
var target = _a.target;
|
|
146
146
|
if (!((_b = toggleButtonRef.current) === null || _b === void 0 ? void 0 : _b.contains(target))) {
|
|
@@ -22,6 +22,7 @@ export default class Toaster {
|
|
|
22
22
|
private settings;
|
|
23
23
|
private toastsRef;
|
|
24
24
|
private isInitialized;
|
|
25
|
+
private asyncInit;
|
|
25
26
|
/**
|
|
26
27
|
* Set global Toaster settings for toasts order and placement.
|
|
27
28
|
* Settings will be applied to new toasts on the page.
|
|
@@ -41,7 +42,6 @@ export default class Toaster {
|
|
|
41
42
|
};
|
|
42
43
|
private createToast;
|
|
43
44
|
private removeToast;
|
|
44
|
-
private createContainer;
|
|
45
45
|
private updateView;
|
|
46
46
|
private closeToast;
|
|
47
47
|
closeAll(): void;
|
|
@@ -9,42 +9,6 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
15
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
16
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
17
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
18
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
22
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
23
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
24
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
25
|
-
function step(op) {
|
|
26
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
27
|
-
while (_) try {
|
|
28
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
29
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
30
|
-
switch (op[0]) {
|
|
31
|
-
case 0: case 1: t = op; break;
|
|
32
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
33
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
34
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
35
|
-
default:
|
|
36
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
37
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
38
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
39
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
40
|
-
if (t[2]) _.ops.pop();
|
|
41
|
-
_.trys.pop(); continue;
|
|
42
|
-
}
|
|
43
|
-
op = body.call(thisArg, _);
|
|
44
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
45
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
12
|
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
49
13
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
50
14
|
if (ar || !(i in from)) {
|
|
@@ -65,6 +29,7 @@ import { ToastWrapper } from './ToastWrapper';
|
|
|
65
29
|
var TOASTS_CONTAINER_ID = 'iui-toasts-container';
|
|
66
30
|
var Toaster = /** @class */ (function () {
|
|
67
31
|
function Toaster() {
|
|
32
|
+
var _this = this;
|
|
68
33
|
this.toasts = [];
|
|
69
34
|
this.lastId = 0;
|
|
70
35
|
this.settings = {
|
|
@@ -73,19 +38,40 @@ var Toaster = /** @class */ (function () {
|
|
|
73
38
|
};
|
|
74
39
|
this.toastsRef = React.createRef();
|
|
75
40
|
this.isInitialized = false;
|
|
41
|
+
// Create container on demand.
|
|
42
|
+
// Cannot do it in constructor, because SSG/SSR apps would fail.
|
|
43
|
+
this.asyncInit = new Promise(function (resolve) {
|
|
44
|
+
if (_this.isInitialized) {
|
|
45
|
+
resolve();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
var container = getContainer(TOASTS_CONTAINER_ID);
|
|
49
|
+
if (!container) {
|
|
50
|
+
// should never happen
|
|
51
|
+
resolve();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
_this.isInitialized = true;
|
|
55
|
+
ReactDOM.render(React.createElement(ToastWrapper, { ref: _this.toastsRef }), container);
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
76
58
|
}
|
|
77
59
|
/**
|
|
78
60
|
* Set global Toaster settings for toasts order and placement.
|
|
79
61
|
* Settings will be applied to new toasts on the page.
|
|
80
62
|
*/
|
|
81
63
|
Toaster.prototype.setSettings = function (newSettings) {
|
|
82
|
-
var
|
|
64
|
+
var _this = this;
|
|
65
|
+
var _a, _b, _c;
|
|
83
66
|
(_a = newSettings.placement) !== null && _a !== void 0 ? _a : (newSettings.placement = this.settings.placement);
|
|
84
67
|
(_b = newSettings.order) !== null && _b !== void 0 ? _b : (newSettings.order = ((_c = newSettings.placement) === null || _c === void 0 ? void 0 : _c.startsWith('bottom'))
|
|
85
68
|
? 'ascending'
|
|
86
69
|
: 'descending');
|
|
87
70
|
this.settings = newSettings;
|
|
88
|
-
|
|
71
|
+
this.asyncInit.then(function () {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
(_a = _this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setPlacement((_b = _this.settings.placement) !== null && _b !== void 0 ? _b : 'top');
|
|
74
|
+
});
|
|
89
75
|
};
|
|
90
76
|
Toaster.prototype.positive = function (content, options) {
|
|
91
77
|
return this.createToast(content, 'positive', options);
|
|
@@ -117,34 +103,12 @@ var Toaster = /** @class */ (function () {
|
|
|
117
103
|
this.toasts = this.toasts.filter(function (toast) { return toast.id !== id; });
|
|
118
104
|
this.updateView();
|
|
119
105
|
};
|
|
120
|
-
// Create container on demand.
|
|
121
|
-
// Cannot do it in constructor, because SSG/SSR apps would fail.
|
|
122
|
-
Toaster.prototype.createContainer = function () {
|
|
123
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
124
|
-
var container;
|
|
125
|
-
return __generator(this, function (_a) {
|
|
126
|
-
container = getContainer(TOASTS_CONTAINER_ID);
|
|
127
|
-
if (!container) {
|
|
128
|
-
return [2 /*return*/];
|
|
129
|
-
}
|
|
130
|
-
ReactDOM.render(React.createElement(ToastWrapper, { ref: this.toastsRef }), container);
|
|
131
|
-
return [2 /*return*/];
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
};
|
|
135
106
|
Toaster.prototype.updateView = function () {
|
|
136
107
|
var _this = this;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
_this.isInitialized = true;
|
|
142
|
-
(_a = _this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setToasts(_this.toasts);
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
(_a = this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setToasts(this.toasts);
|
|
147
|
-
}
|
|
108
|
+
this.asyncInit.then(function () {
|
|
109
|
+
var _a;
|
|
110
|
+
(_a = _this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setToasts(_this.toasts);
|
|
111
|
+
});
|
|
148
112
|
};
|
|
149
113
|
Toaster.prototype.closeToast = function (toastId) {
|
|
150
114
|
this.toasts = this.toasts.map(function (toast) {
|