carbon-react 114.13.2 → 114.14.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/esm/components/link/link.component.js +7 -2
- package/esm/components/link/link.style.d.ts +1 -0
- package/esm/components/link/link.style.js +3 -4
- package/esm/components/select/filterable-select/filterable-select.component.js +15 -2
- package/esm/components/select/filterable-select/filterable-select.d.ts +7 -0
- package/esm/components/select/list-action-button/list-action-button.style.js +4 -1
- package/esm/components/select/multi-select/multi-select.component.js +15 -2
- package/esm/components/select/multi-select/multi-select.d.ts +7 -0
- package/esm/components/select/option/option.component.js +16 -5
- package/esm/components/select/option/option.style.js +4 -0
- package/esm/components/select/option-group-header/option-group-header.component.js +20 -8
- package/esm/components/select/option-group-header/option-group-header.style.js +3 -2
- package/esm/components/select/option-row/option-row.component.js +16 -5
- package/esm/components/select/option-row/option-row.style.js +4 -0
- package/esm/components/select/select-list/select-list-container.style.js +11 -1
- package/esm/components/select/select-list/select-list.component.js +136 -62
- package/esm/components/select/select-list/select-list.style.js +15 -18
- package/esm/components/select/simple-select/simple-select.component.js +15 -2
- package/esm/components/select/simple-select/simple-select.d.ts +7 -0
- package/lib/components/link/link.component.js +7 -1
- package/lib/components/link/link.style.d.ts +1 -0
- package/lib/components/link/link.style.js +3 -4
- package/lib/components/select/filterable-select/filterable-select.component.js +15 -2
- package/lib/components/select/filterable-select/filterable-select.d.ts +7 -0
- package/lib/components/select/list-action-button/list-action-button.style.js +4 -1
- package/lib/components/select/multi-select/multi-select.component.js +15 -2
- package/lib/components/select/multi-select/multi-select.d.ts +7 -0
- package/lib/components/select/option/option.component.js +16 -5
- package/lib/components/select/option/option.style.js +4 -0
- package/lib/components/select/option-group-header/option-group-header.component.js +20 -6
- package/lib/components/select/option-group-header/option-group-header.style.js +3 -2
- package/lib/components/select/option-row/option-row.component.js +16 -5
- package/lib/components/select/option-row/option-row.style.js +4 -0
- package/lib/components/select/select-list/select-list-container.style.js +11 -1
- package/lib/components/select/select-list/select-list.component.js +139 -63
- package/lib/components/select/select-list/select-list.style.js +15 -18
- package/lib/components/select/simple-select/simple-select.component.js +15 -2
- package/lib/components/select/simple-select/simple-select.d.ts +7 -0
- package/package.json +2 -1
- package/esm/components/select/select-list/update-list-scroll.js +0 -21
- package/lib/components/select/select-list/update-list-scroll.js +0 -28
|
@@ -3,11 +3,12 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
3
3
|
import React, { useEffect, useState, useCallback, useLayoutEffect, useRef, useMemo } from "react";
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
import { flip, offset, size } from "@floating-ui/dom";
|
|
6
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
7
|
+
import findLastIndex from "lodash/findLastIndex";
|
|
6
8
|
import useScrollBlock from "../../../hooks/__internal__/useScrollBlock";
|
|
7
9
|
import { StyledSelectList, StyledSelectLoaderContainer, StyledSelectListTable, StyledSelectListTableHeader, StyledSelectListTableBody } from "./select-list.style";
|
|
8
10
|
import Popover from "../../../__internal__/popover";
|
|
9
11
|
import OptionRow from "../option-row/option-row.component";
|
|
10
|
-
import updateListScrollTop from "./update-list-scroll";
|
|
11
12
|
import getNextChildByText from "../utils/get-next-child-by-text";
|
|
12
13
|
import getNextIndexByKey from "../utils/get-next-index-by-key";
|
|
13
14
|
import isNavigationKey from "../utils/is-navigation-key";
|
|
@@ -17,6 +18,11 @@ import Loader from "../../loader";
|
|
|
17
18
|
import Option from "../option/option.component";
|
|
18
19
|
import guid from "../../../__internal__/utils/helpers/guid";
|
|
19
20
|
import SelectListContext from "../__internal__/select-list-context";
|
|
21
|
+
const TABLE_HEADER_HEIGHT = 48;
|
|
22
|
+
const SCROLL_OPTIONS = {
|
|
23
|
+
smoothScroll: false,
|
|
24
|
+
align: "end"
|
|
25
|
+
};
|
|
20
26
|
const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
21
27
|
listMaxHeight = 180,
|
|
22
28
|
listActionButton,
|
|
@@ -39,8 +45,12 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
39
45
|
flipEnabled = true,
|
|
40
46
|
isOpen,
|
|
41
47
|
multiselectValues,
|
|
48
|
+
enableVirtualScroll,
|
|
49
|
+
virtualScrollOverscan = 5,
|
|
42
50
|
...listProps
|
|
43
51
|
}, listContainerRef) => {
|
|
52
|
+
var _childIdsRef$current;
|
|
53
|
+
|
|
44
54
|
const [currentOptionsListIndex, setCurrentOptionsListIndex] = useState(-1);
|
|
45
55
|
const [scrollbarWidth, setScrollbarWidth] = useState(0);
|
|
46
56
|
const lastFilter = useRef("");
|
|
@@ -51,6 +61,19 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
51
61
|
blockScroll,
|
|
52
62
|
allowScroll
|
|
53
63
|
} = useScrollBlock();
|
|
64
|
+
const actionButtonHeight = useRef(0);
|
|
65
|
+
const overscan = enableVirtualScroll ? virtualScrollOverscan : React.Children.count(children);
|
|
66
|
+
const virtualizer = useVirtualizer({
|
|
67
|
+
count: React.Children.count(children),
|
|
68
|
+
getScrollElement: () => listContainerRef.current,
|
|
69
|
+
estimateSize: () => 40,
|
|
70
|
+
// value doesn't really seem to matter since we're dynamically measuring, but 40px is the height of a single-line option
|
|
71
|
+
overscan,
|
|
72
|
+
paddingStart: multiColumn ? TABLE_HEADER_HEIGHT : 0,
|
|
73
|
+
scrollPaddingEnd: actionButtonHeight.current
|
|
74
|
+
});
|
|
75
|
+
const items = virtualizer.getVirtualItems();
|
|
76
|
+
const listHeight = virtualizer.getTotalSize();
|
|
54
77
|
useEffect(() => {
|
|
55
78
|
if (isOpen) {
|
|
56
79
|
blockScroll();
|
|
@@ -70,43 +93,67 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
70
93
|
const anchorRef = useMemo(() => ({
|
|
71
94
|
current: anchorElement
|
|
72
95
|
}), [anchorElement]);
|
|
73
|
-
const optionRefList = useMemo(() => React.Children.map(children, child => {
|
|
74
|
-
if ((child === null || child === void 0 ? void 0 : child.type) === Option || (child === null || child === void 0 ? void 0 : child.type) === OptionRow) {
|
|
75
|
-
return /*#__PURE__*/React.createRef();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return null;
|
|
79
|
-
}).filter(child => child), [children]);
|
|
80
96
|
const handleSelect = useCallback(optionData => {
|
|
81
97
|
onSelect({ ...optionData,
|
|
82
98
|
selectionType: "click"
|
|
83
99
|
});
|
|
84
100
|
}, [onSelect]);
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const childIdsRef = useRef(null); // childIds should be stable except when children are added or removed - can't use useMemo
|
|
102
|
+
// as that isn't absolutely guaranteed to never rerun when dependencies haven't changed.
|
|
103
|
+
|
|
104
|
+
const setChildIds = () => {
|
|
105
|
+
childIdsRef.current = React.Children.map(children, () => guid());
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (((_childIdsRef$current = childIdsRef.current) === null || _childIdsRef$current === void 0 ? void 0 : _childIdsRef$current.length) !== React.Children.count(children)) {
|
|
109
|
+
setChildIds();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const childIds = childIdsRef.current;
|
|
113
|
+
const childrenList = useMemo(() => React.Children.toArray(children), [children]);
|
|
114
|
+
const optionChildrenList = useMemo(() => childrenList.filter(child => child.type === Option || child.type === OptionRow), [childrenList]);
|
|
115
|
+
const {
|
|
116
|
+
measureElement
|
|
117
|
+
} = virtualizer;
|
|
118
|
+
|
|
119
|
+
const measureCallback = element => {
|
|
120
|
+
// need a guard to prevent crash with too many rerenders when closing the list
|
|
121
|
+
if (isOpen) {
|
|
122
|
+
measureElement(element);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const renderedChildren = items.map(item => {
|
|
127
|
+
const {
|
|
128
|
+
index,
|
|
129
|
+
start
|
|
130
|
+
} = item;
|
|
131
|
+
const child = childrenList[index];
|
|
132
|
+
|
|
133
|
+
if (!child) {
|
|
88
134
|
return child;
|
|
89
135
|
}
|
|
90
136
|
|
|
137
|
+
const optionChildIndex = optionChildrenList.indexOf(child);
|
|
138
|
+
const isOption = optionChildIndex > -1;
|
|
91
139
|
const newProps = {
|
|
92
140
|
index,
|
|
93
141
|
id: childIds[index],
|
|
94
142
|
onSelect: handleSelect,
|
|
95
|
-
hidden: isLoading &&
|
|
96
|
-
|
|
143
|
+
hidden: isLoading && childrenList.length === 1,
|
|
144
|
+
// these need to be inline styles rather than implemented in styled-components to avoid it generating thousands of classes
|
|
145
|
+
style: {
|
|
146
|
+
transform: `translateY(${start}px)`
|
|
147
|
+
},
|
|
148
|
+
"aria-setsize": isOption ? optionChildrenList.length : undefined,
|
|
149
|
+
"aria-posinset": isOption ? optionChildIndex + 1 : undefined,
|
|
150
|
+
// needed to dynamically compute the size
|
|
151
|
+
ref: measureCallback,
|
|
152
|
+
"data-index": index
|
|
97
153
|
};
|
|
98
154
|
return /*#__PURE__*/React.cloneElement(child, newProps);
|
|
99
|
-
})
|
|
100
|
-
const
|
|
101
|
-
const lastOptionIndex = useMemo(() => {
|
|
102
|
-
let lastIndex = 0;
|
|
103
|
-
childrenList.forEach((element, index) => {
|
|
104
|
-
if (element.type === Option || element.type === OptionRow) {
|
|
105
|
-
lastIndex = index;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
return lastIndex;
|
|
109
|
-
}, [childrenList]);
|
|
155
|
+
});
|
|
156
|
+
const lastOptionIndex = findLastIndex(childrenList, child => child.type === Option || child.type === OptionRow);
|
|
110
157
|
const getNextHighlightableItemIndex = useCallback((key, indexOfHighlighted) => {
|
|
111
158
|
const lastIndex = lastOptionIndex;
|
|
112
159
|
let nextIndex = getNextIndexByKey(key, indexOfHighlighted, lastIndex, isLoading);
|
|
@@ -137,16 +184,15 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
137
184
|
|
|
138
185
|
const {
|
|
139
186
|
text,
|
|
140
|
-
value
|
|
141
|
-
id: itemId
|
|
187
|
+
value
|
|
142
188
|
} = childrenList[nextIndex].props;
|
|
143
189
|
onSelect({
|
|
144
190
|
text,
|
|
145
191
|
value,
|
|
146
192
|
selectionType: "navigationKey",
|
|
147
|
-
id:
|
|
193
|
+
id: childIds[nextIndex]
|
|
148
194
|
});
|
|
149
|
-
}, [childrenList, currentOptionsListIndex, getIndexOfMatch, getNextHighlightableItemIndex, highlightedValue, onSelect]);
|
|
195
|
+
}, [childrenList, currentOptionsListIndex, getIndexOfMatch, getNextHighlightableItemIndex, highlightedValue, onSelect, childIds]);
|
|
150
196
|
const handleActionButtonTab = useCallback((event, isActionButtonFocused) => {
|
|
151
197
|
if (isActionButtonFocused) {
|
|
152
198
|
onSelect({
|
|
@@ -186,12 +232,11 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
186
232
|
}
|
|
187
233
|
|
|
188
234
|
const {
|
|
189
|
-
id: itemId,
|
|
190
235
|
text,
|
|
191
236
|
value
|
|
192
237
|
} = currentOption.props;
|
|
193
238
|
onSelect({
|
|
194
|
-
id:
|
|
239
|
+
id: childIds[currentOptionsListIndex],
|
|
195
240
|
text,
|
|
196
241
|
value,
|
|
197
242
|
selectionType: "enterKey"
|
|
@@ -200,9 +245,10 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
200
245
|
focusOnAnchor();
|
|
201
246
|
highlightNextItem(key);
|
|
202
247
|
}
|
|
203
|
-
}, [childrenList, listActionButton, handleActionButtonTab, onSelectListClose, currentOptionsListIndex, onSelect, highlightNextItem, focusOnAnchor, isOpen]);
|
|
248
|
+
}, [childrenList, listActionButton, handleActionButtonTab, onSelectListClose, currentOptionsListIndex, onSelect, highlightNextItem, focusOnAnchor, isOpen, childIds]);
|
|
204
249
|
const handleListScroll = useCallback(event => {
|
|
205
250
|
const element = event.target;
|
|
251
|
+
/* istanbul ignore else */
|
|
206
252
|
|
|
207
253
|
if (onListScrollBottom && element.scrollHeight - element.scrollTop === element.clientHeight) {
|
|
208
254
|
onListScrollBottom();
|
|
@@ -210,14 +256,14 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
210
256
|
}, [onListScrollBottom]);
|
|
211
257
|
useEffect(() => {
|
|
212
258
|
const keyboardEvent = "keydown";
|
|
213
|
-
const listElement =
|
|
259
|
+
const listElement = listContainerRef.current;
|
|
214
260
|
window.addEventListener(keyboardEvent, handleGlobalKeydown);
|
|
215
261
|
listElement.addEventListener("scroll", handleListScroll);
|
|
216
262
|
return function cleanup() {
|
|
217
263
|
window.removeEventListener(keyboardEvent, handleGlobalKeydown);
|
|
218
264
|
listElement.removeEventListener("scroll", handleListScroll);
|
|
219
265
|
};
|
|
220
|
-
}, [handleGlobalKeydown, handleListScroll]);
|
|
266
|
+
}, [handleGlobalKeydown, handleListScroll, listContainerRef]);
|
|
221
267
|
useEffect(() => {
|
|
222
268
|
if (!filterText || filterText === lastFilter.current) {
|
|
223
269
|
lastFilter.current = filterText;
|
|
@@ -233,10 +279,10 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
233
279
|
}
|
|
234
280
|
|
|
235
281
|
const indexOfMatch = getIndexOfMatch(match.props.value);
|
|
236
|
-
|
|
282
|
+
virtualizer.scrollToIndex(indexOfMatch, SCROLL_OPTIONS);
|
|
237
283
|
return indexOfMatch;
|
|
238
284
|
});
|
|
239
|
-
}, [childrenList, filterText, getIndexOfMatch,
|
|
285
|
+
}, [childrenList, filterText, getIndexOfMatch, virtualizer]);
|
|
240
286
|
useEffect(() => {
|
|
241
287
|
if (!highlightedValue) {
|
|
242
288
|
return;
|
|
@@ -244,13 +290,19 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
244
290
|
|
|
245
291
|
const indexOfMatch = getIndexOfMatch(highlightedValue);
|
|
246
292
|
setCurrentOptionsListIndex(indexOfMatch);
|
|
247
|
-
|
|
248
|
-
|
|
293
|
+
virtualizer.scrollToIndex(indexOfMatch, SCROLL_OPTIONS); // TODO: is there a better way than calling handleListScroll manually?
|
|
294
|
+
|
|
295
|
+
handleListScroll({
|
|
296
|
+
target: listContainerRef.current
|
|
297
|
+
});
|
|
298
|
+
}, [getIndexOfMatch, highlightedValue, virtualizer, handleListScroll, listContainerRef]);
|
|
249
299
|
useEffect(() => {
|
|
250
|
-
if (isLoading && currentOptionsListIndex === lastOptionIndex) {
|
|
251
|
-
|
|
300
|
+
if (isLoading && currentOptionsListIndex === lastOptionIndex && lastOptionIndex > -1) {
|
|
301
|
+
virtualizer.scrollToIndex(lastOptionIndex, { ...SCROLL_OPTIONS,
|
|
302
|
+
align: "start"
|
|
303
|
+
});
|
|
252
304
|
}
|
|
253
|
-
}, [children, currentOptionsListIndex, isLoading, lastOptionIndex]);
|
|
305
|
+
}, [children, currentOptionsListIndex, isLoading, lastOptionIndex, listContainerRef, virtualizer]);
|
|
254
306
|
const popoverMiddleware = useMemo(() => [offset(3), size({
|
|
255
307
|
apply({
|
|
256
308
|
rects,
|
|
@@ -265,21 +317,37 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
265
317
|
fallbackStrategy: "initialPlacement"
|
|
266
318
|
})] : [])], [flipEnabled]);
|
|
267
319
|
|
|
268
|
-
const loader = () =>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
320
|
+
const loader = () => {
|
|
321
|
+
return /*#__PURE__*/React.createElement(StyledSelectLoaderContainer, {
|
|
322
|
+
key: "loader"
|
|
323
|
+
}, /*#__PURE__*/React.createElement(Loader, {
|
|
324
|
+
"data-role": loaderDataRole
|
|
325
|
+
}));
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
let selectListContent = renderedChildren;
|
|
329
|
+
const listBoxProps = {
|
|
330
|
+
role: "listbox",
|
|
331
|
+
id,
|
|
332
|
+
"aria-labelledby": labelId,
|
|
333
|
+
"aria-multiselectable": multiselectValues ? true : undefined
|
|
334
|
+
};
|
|
335
|
+
useLayoutEffect(() => {
|
|
336
|
+
if (listActionButton && isOpen) {
|
|
337
|
+
var _listActionButtonRef$, _listActionButtonRef$2;
|
|
274
338
|
|
|
275
|
-
|
|
339
|
+
actionButtonHeight.current = ((_listActionButtonRef$ = listActionButtonRef.current) === null || _listActionButtonRef$ === void 0 ? void 0 : (_listActionButtonRef$2 = _listActionButtonRef$.parentElement) === null || _listActionButtonRef$2 === void 0 ? void 0 : _listActionButtonRef$2.offsetHeight) || 0;
|
|
340
|
+
}
|
|
341
|
+
}, [listActionButton, isOpen]);
|
|
276
342
|
|
|
277
343
|
if (multiColumn) {
|
|
278
344
|
selectListContent = /*#__PURE__*/React.createElement(StyledSelectListTable, null, /*#__PURE__*/React.createElement(StyledSelectListTableHeader, {
|
|
279
345
|
scrollbarWidth: scrollbarWidth
|
|
280
|
-
}, tableHeader), /*#__PURE__*/React.createElement(StyledSelectListTableBody, {
|
|
281
|
-
|
|
282
|
-
|
|
346
|
+
}, tableHeader), /*#__PURE__*/React.createElement(StyledSelectListTableBody, _extends({}, listBoxProps, {
|
|
347
|
+
"aria-labelledby": labelId,
|
|
348
|
+
ref: tableRef,
|
|
349
|
+
listHeight: listHeight - TABLE_HEADER_HEIGHT
|
|
350
|
+
}), renderedChildren));
|
|
283
351
|
}
|
|
284
352
|
|
|
285
353
|
return /*#__PURE__*/React.createElement(SelectListContext.Provider, {
|
|
@@ -297,20 +365,17 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
297
365
|
animationFrame: true
|
|
298
366
|
}, /*#__PURE__*/React.createElement(StyledSelectListContainer, _extends({
|
|
299
367
|
"data-element": "select-list-wrapper",
|
|
300
|
-
ref: listContainerRef
|
|
301
|
-
|
|
302
|
-
|
|
368
|
+
ref: listContainerRef,
|
|
369
|
+
maxHeight: listMaxHeight + actionButtonHeight.current,
|
|
370
|
+
isLoading: isLoading
|
|
371
|
+
}, listProps), /*#__PURE__*/React.createElement(StyledSelectList, _extends({
|
|
303
372
|
as: multiColumn ? "div" : "ul",
|
|
304
|
-
"
|
|
305
|
-
|
|
306
|
-
role: "listbox",
|
|
307
|
-
"aria-multiselectable": multiselectValues ? true : undefined,
|
|
373
|
+
"data-element": "select-list"
|
|
374
|
+
}, multiColumn ? {} : listBoxProps, {
|
|
308
375
|
ref: listRef,
|
|
309
376
|
tabIndex: "-1",
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
maxHeight: listMaxHeight
|
|
313
|
-
}, selectListContent, isLoading && loader()), listActionButton && /*#__PURE__*/React.createElement(ListActionButton, {
|
|
377
|
+
listHeight: multiColumn ? undefined : listHeight
|
|
378
|
+
}), selectListContent), isLoading && loader(), listActionButton && /*#__PURE__*/React.createElement(ListActionButton, {
|
|
314
379
|
ref: listActionButtonRef,
|
|
315
380
|
listActionButton: listActionButton,
|
|
316
381
|
onListAction: onListAction
|
|
@@ -380,6 +445,15 @@ SelectList.propTypes = {
|
|
|
380
445
|
isOpen: PropTypes.bool,
|
|
381
446
|
|
|
382
447
|
/** array of selected values, if rendered as part of a MultiSelect */
|
|
383
|
-
multiselectValues: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.object)])
|
|
448
|
+
multiselectValues: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.arrayOf(PropTypes.object)]),
|
|
449
|
+
|
|
450
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
451
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
452
|
+
enableVirtualScroll: PropTypes.bool,
|
|
453
|
+
|
|
454
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
455
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
456
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
457
|
+
virtualScrollOverscan: PropTypes.number
|
|
384
458
|
};
|
|
385
459
|
export default SelectList;
|
|
@@ -1,39 +1,29 @@
|
|
|
1
1
|
import styled, { css } from "styled-components";
|
|
2
2
|
const StyledSelectList = styled.ul`
|
|
3
3
|
${({
|
|
4
|
-
|
|
5
|
-
multiColumn
|
|
4
|
+
listHeight
|
|
6
5
|
}) => css`
|
|
7
6
|
box-sizing: border-box;
|
|
8
7
|
display: flex;
|
|
9
8
|
align-items: flex-start;
|
|
10
9
|
flex-direction: column;
|
|
11
10
|
list-style-type: none;
|
|
12
|
-
max-height: ${({
|
|
13
|
-
maxHeight
|
|
14
|
-
}) => `${maxHeight}`}px;
|
|
15
11
|
margin: 0;
|
|
16
12
|
outline: none;
|
|
17
|
-
overflow-x: hidden;
|
|
18
|
-
overflow-y: ${multiColumn ? "hidden" : "auto"};
|
|
19
13
|
padding: 0;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`}}
|
|
14
|
+
position: relative;
|
|
15
|
+
width: 100%;
|
|
16
|
+
${listHeight === undefined ? "" : `height: ${listHeight}px;`}
|
|
24
17
|
`}
|
|
25
18
|
`;
|
|
26
|
-
|
|
27
|
-
maxHeight: "180px"
|
|
28
|
-
};
|
|
29
|
-
const StyledSelectLoaderContainer = styled.li`
|
|
19
|
+
const StyledSelectLoaderContainer = styled.div`
|
|
30
20
|
display: flex;
|
|
31
21
|
align-items: center;
|
|
32
22
|
justify-content: center;
|
|
33
|
-
flex-grow: 1;
|
|
34
23
|
padding-top: 24px;
|
|
35
24
|
padding-bottom: 24px;
|
|
36
25
|
width: 100%;
|
|
26
|
+
flex-grow: 1;
|
|
37
27
|
`;
|
|
38
28
|
const StyledSelectListTable = styled.table`
|
|
39
29
|
background-color: var(--colorsUtilityYang100);
|
|
@@ -43,6 +33,7 @@ const StyledSelectListTable = styled.table`
|
|
|
43
33
|
min-width: 100%;
|
|
44
34
|
white-space: nowrap;
|
|
45
35
|
height: 180px;
|
|
36
|
+
overflow-y: auto;
|
|
46
37
|
|
|
47
38
|
thead,
|
|
48
39
|
tr {
|
|
@@ -54,6 +45,10 @@ const StyledSelectListTable = styled.table`
|
|
|
54
45
|
|
|
55
46
|
const StyledSelectListTableHeader = styled.thead`
|
|
56
47
|
border-bottom: 1px solid var(--colorsUtilityMajor050);
|
|
48
|
+
position: sticky;
|
|
49
|
+
top: 0;
|
|
50
|
+
left: 0;
|
|
51
|
+
z-index: 1;
|
|
57
52
|
|
|
58
53
|
tr {
|
|
59
54
|
width: ${({
|
|
@@ -89,9 +84,11 @@ const StyledSelectListTableHeader = styled.thead`
|
|
|
89
84
|
`;
|
|
90
85
|
const StyledSelectListTableBody = styled.tbody`
|
|
91
86
|
display: block;
|
|
92
|
-
overflow-y: auto;
|
|
93
87
|
width: 100%;
|
|
94
88
|
table-layout: fixed;
|
|
95
|
-
|
|
89
|
+
width: 100%;
|
|
90
|
+
height: ${({
|
|
91
|
+
listHeight
|
|
92
|
+
}) => `${listHeight}px`};
|
|
96
93
|
`;
|
|
97
94
|
export { StyledSelectList, StyledSelectLoaderContainer, StyledSelectListTable, StyledSelectListTableHeader, StyledSelectListTableBody };
|
|
@@ -43,6 +43,8 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
43
43
|
listPlacement = "bottom",
|
|
44
44
|
flipEnabled = true,
|
|
45
45
|
inputRef,
|
|
46
|
+
enableVirtualScroll,
|
|
47
|
+
virtualScrollOverscan,
|
|
46
48
|
...props
|
|
47
49
|
}, ref) => {
|
|
48
50
|
const selectListId = useRef(guid());
|
|
@@ -350,7 +352,9 @@ const SimpleSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
350
352
|
loaderDataRole: "simple-select-list-loader",
|
|
351
353
|
listPlacement: listPlacement,
|
|
352
354
|
flipEnabled: flipEnabled,
|
|
353
|
-
isOpen: isOpen
|
|
355
|
+
isOpen: isOpen,
|
|
356
|
+
enableVirtualScroll: enableVirtualScroll,
|
|
357
|
+
virtualScrollOverscan: virtualScrollOverscan
|
|
354
358
|
}, children);
|
|
355
359
|
return /*#__PURE__*/React.createElement(StyledSelect, _extends({
|
|
356
360
|
transparent: transparent,
|
|
@@ -429,7 +433,16 @@ SimpleSelect.propTypes = {
|
|
|
429
433
|
listPlacement: PropTypes.oneOf(["top", "bottom", "right", "left"]),
|
|
430
434
|
|
|
431
435
|
/** Use the opposite list placement if the set placement does not fit */
|
|
432
|
-
flipEnabled: PropTypes.bool
|
|
436
|
+
flipEnabled: PropTypes.bool,
|
|
437
|
+
|
|
438
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
439
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
440
|
+
enableVirtualScroll: PropTypes.bool,
|
|
441
|
+
|
|
442
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
443
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
444
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
445
|
+
virtualScrollOverscan: PropTypes.number
|
|
433
446
|
};
|
|
434
447
|
SimpleSelect.defaultProps = {
|
|
435
448
|
disablePortal: false,
|
|
@@ -44,6 +44,13 @@ export interface SimpleSelectProps
|
|
|
44
44
|
listPlacement?: Side;
|
|
45
45
|
/** Use the opposite list placement if the set placement does not fit */
|
|
46
46
|
flipEnabled?: boolean;
|
|
47
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
48
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
49
|
+
enableVirtualScroll?: boolean;
|
|
50
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
51
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
52
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
53
|
+
virtualScrollOverscan?: number;
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
declare function SimpleSelect(
|
|
@@ -11,6 +11,8 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
11
11
|
|
|
12
12
|
var _icon = _interopRequireDefault(require("../icon"));
|
|
13
13
|
|
|
14
|
+
var _menu = _interopRequireDefault(require("../menu/menu.context"));
|
|
15
|
+
|
|
14
16
|
var _events = _interopRequireDefault(require("../../__internal__/utils/helpers/events"));
|
|
15
17
|
|
|
16
18
|
var _link = require("./link.style");
|
|
@@ -49,6 +51,9 @@ const Link = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
49
51
|
...rest
|
|
50
52
|
}, ref) => {
|
|
51
53
|
const l = (0, _useLocale.default)();
|
|
54
|
+
const {
|
|
55
|
+
inMenu
|
|
56
|
+
} = (0, _react.useContext)(_menu.default);
|
|
52
57
|
|
|
53
58
|
const handleOnKeyDown = ev => {
|
|
54
59
|
if (onKeyDown) {
|
|
@@ -119,7 +124,8 @@ const Link = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
119
124
|
iconAlign: iconAlign,
|
|
120
125
|
hasContent: Boolean(children),
|
|
121
126
|
variant: variant,
|
|
122
|
-
isDarkBackground: isDarkBackground
|
|
127
|
+
isDarkBackground: isDarkBackground,
|
|
128
|
+
isMenuItem: inMenu
|
|
123
129
|
}, (0, _tags.default)("link", rest), isSkipLink && {
|
|
124
130
|
"data-element": "skip-link"
|
|
125
131
|
}), createLinkBasedOnType());
|
|
@@ -13,6 +13,7 @@ export interface StyledLinkProps {
|
|
|
13
13
|
}
|
|
14
14
|
interface PrivateStyledLinkProps {
|
|
15
15
|
hasContent: boolean;
|
|
16
|
+
isMenuItem?: boolean;
|
|
16
17
|
}
|
|
17
18
|
declare const StyledLink: import("styled-components").StyledComponent<"span", any, StyledLinkProps & PrivateStyledLinkProps, never>;
|
|
18
19
|
declare const StyledContent: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
@@ -61,7 +61,8 @@ const StyledLink = _styledComponents.default.span`
|
|
|
61
61
|
hasContent,
|
|
62
62
|
disabled,
|
|
63
63
|
variant,
|
|
64
|
-
isDarkBackground
|
|
64
|
+
isDarkBackground,
|
|
65
|
+
isMenuItem
|
|
65
66
|
}) => {
|
|
66
67
|
const colorMapKey = isDarkBackground ? "dark" : "light";
|
|
67
68
|
const {
|
|
@@ -70,8 +71,6 @@ const StyledLink = _styledComponents.default.span`
|
|
|
70
71
|
disabledColor
|
|
71
72
|
} = colorMap[colorMapKey](variant);
|
|
72
73
|
return (0, _styledComponents.css)`
|
|
73
|
-
display: inline-block;
|
|
74
|
-
|
|
75
74
|
${isSkipLink && (0, _styledComponents.css)`
|
|
76
75
|
a {
|
|
77
76
|
position: absolute;
|
|
@@ -142,7 +141,7 @@ const StyledLink = _styledComponents.default.span`
|
|
|
142
141
|
a,
|
|
143
142
|
button {
|
|
144
143
|
text-decoration: underline;
|
|
145
|
-
display: inline-block;
|
|
144
|
+
${isMenuItem && "display: inline-block;"}
|
|
146
145
|
|
|
147
146
|
${_icon.default} {
|
|
148
147
|
display: inline-block;
|
|
@@ -73,6 +73,8 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
73
73
|
listPlacement = "bottom",
|
|
74
74
|
flipEnabled = true,
|
|
75
75
|
inputRef,
|
|
76
|
+
enableVirtualScroll,
|
|
77
|
+
virtualScrollOverscan,
|
|
76
78
|
...textboxProps
|
|
77
79
|
}, ref) => {
|
|
78
80
|
const [activeDescendantId, setActiveDescendantId] = (0, _react.useState)();
|
|
@@ -455,7 +457,9 @@ const FilterableSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
455
457
|
loaderDataRole: "filterable-select-list-loader",
|
|
456
458
|
listPlacement: listPlacement,
|
|
457
459
|
flipEnabled: flipEnabled,
|
|
458
|
-
isOpen: isOpen
|
|
460
|
+
isOpen: isOpen,
|
|
461
|
+
enableVirtualScroll: enableVirtualScroll,
|
|
462
|
+
virtualScrollOverscan: virtualScrollOverscan
|
|
459
463
|
}, children);
|
|
460
464
|
|
|
461
465
|
return /*#__PURE__*/_react.default.createElement(_select.default, _extends({
|
|
@@ -543,7 +547,16 @@ FilterableSelect.propTypes = { ..._selectTextbox.formInputPropTypes,
|
|
|
543
547
|
listPlacement: _propTypes.default.oneOf(["top", "bottom", "right", "left"]),
|
|
544
548
|
|
|
545
549
|
/** Use the opposite list placement if the set placement does not fit */
|
|
546
|
-
flipEnabled: _propTypes.default.bool
|
|
550
|
+
flipEnabled: _propTypes.default.bool,
|
|
551
|
+
|
|
552
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
553
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
554
|
+
enableVirtualScroll: _propTypes.default.bool,
|
|
555
|
+
|
|
556
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
557
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
558
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
559
|
+
virtualScrollOverscan: _propTypes.default.number
|
|
547
560
|
};
|
|
548
561
|
FilterableSelect.defaultProps = {
|
|
549
562
|
"data-component": "filterable-select"
|
|
@@ -51,6 +51,13 @@ export interface FilterableSelectProps
|
|
|
51
51
|
listPlacement?: Side;
|
|
52
52
|
/** Use the opposite list placement if the set placement does not fit */
|
|
53
53
|
flipEnabled?: boolean;
|
|
54
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
55
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
56
|
+
enableVirtualScroll?: boolean;
|
|
57
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
58
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
59
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
60
|
+
virtualScrollOverscan?: number;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
63
|
declare function FilterableSelect(
|
|
@@ -16,13 +16,16 @@ const StyledListActionButtonWrapper = _styledComponents.default.div`
|
|
|
16
16
|
padding-bottom: var(--spacing100);
|
|
17
17
|
border-top: 1px solid var(--colorsUtilityDisabled600);
|
|
18
18
|
box-shadow: 0 0px 0 0 rgba(0, 0, 0, 0), 0 -8px 8px 0 rgba(0, 0, 0, 0.03);
|
|
19
|
+
width: 100%;
|
|
20
|
+
position: sticky;
|
|
21
|
+
bottom: 0;
|
|
22
|
+
background-color: inherit;
|
|
19
23
|
|
|
20
24
|
${_button.default} {
|
|
21
25
|
border: none;
|
|
22
26
|
justify-content: left;
|
|
23
27
|
padding-left: var(--spacing200);
|
|
24
28
|
padding-right: var(--spacing200);
|
|
25
|
-
width: 100%;
|
|
26
29
|
}
|
|
27
30
|
`;
|
|
28
31
|
var _default = StyledListActionButtonWrapper;
|
|
@@ -76,6 +76,8 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
76
76
|
flipEnabled = true,
|
|
77
77
|
wrapPillText = true,
|
|
78
78
|
inputRef,
|
|
79
|
+
enableVirtualScroll,
|
|
80
|
+
virtualScrollOverscan,
|
|
79
81
|
...textboxProps
|
|
80
82
|
}, ref) => {
|
|
81
83
|
const [activeDescendantId, setActiveDescendantId] = (0, _react.useState)();
|
|
@@ -468,7 +470,9 @@ const MultiSelect = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
468
470
|
flipEnabled: flipEnabled,
|
|
469
471
|
loaderDataRole: "multi-select-list-loader",
|
|
470
472
|
multiselectValues: actualValue,
|
|
471
|
-
isOpen: isOpen
|
|
473
|
+
isOpen: isOpen,
|
|
474
|
+
enableVirtualScroll: enableVirtualScroll,
|
|
475
|
+
virtualScrollOverscan: virtualScrollOverscan
|
|
472
476
|
}, children);
|
|
473
477
|
|
|
474
478
|
return /*#__PURE__*/_react.default.createElement(_multiSelect.StyledSelectMultiSelect, _extends({
|
|
@@ -555,7 +559,16 @@ MultiSelect.propTypes = { ..._selectTextbox.formInputPropTypes,
|
|
|
555
559
|
flipEnabled: _propTypes.default.bool,
|
|
556
560
|
|
|
557
561
|
/** Wraps the pill text when it would overflow the input width */
|
|
558
|
-
wrapPillText: _propTypes.default.bool
|
|
562
|
+
wrapPillText: _propTypes.default.bool,
|
|
563
|
+
|
|
564
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
565
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
566
|
+
enableVirtualScroll: _propTypes.default.bool,
|
|
567
|
+
|
|
568
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
569
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
570
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
571
|
+
virtualScrollOverscan: _propTypes.default.number
|
|
559
572
|
};
|
|
560
573
|
MultiSelect.defaultProps = {
|
|
561
574
|
"data-component": "multiselect"
|
|
@@ -48,6 +48,13 @@ export interface MultiSelectProps
|
|
|
48
48
|
flipEnabled?: boolean;
|
|
49
49
|
/** Wraps the pill text when it would overflow the input width */
|
|
50
50
|
wrapPillText?: boolean;
|
|
51
|
+
/** Set this prop to enable a virtualised list of options. If it is not used then all options will be in the
|
|
52
|
+
* DOM at all times, which may cause performance problems on very large lists */
|
|
53
|
+
enableVirtualScroll?: boolean;
|
|
54
|
+
/** The number of options to render into the DOM at once, either side of the currently-visible ones.
|
|
55
|
+
* Higher values make for smoother scrolling but may impact performance.
|
|
56
|
+
* Only used if the `enableVirtualScroll` prop is set. */
|
|
57
|
+
virtualScrollOverscan?: number;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
declare function MultiSelect(
|