carbon-react 114.13.1 → 114.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
- package/esm/components/link/link.component.d.ts +2 -0
- package/esm/components/link/link.component.js +7 -1
- package/esm/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
- package/esm/components/menu/__internal__/keyboard-navigation/index.js +16 -15
- package/esm/components/menu/__internal__/locators.d.ts +6 -0
- package/esm/components/menu/__internal__/locators.js +6 -0
- package/esm/components/menu/__internal__/submenu/submenu.component.js +109 -108
- package/esm/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
- package/esm/components/menu/menu-item/index.js +0 -1
- package/esm/components/menu/menu-item/menu-item.component.js +77 -51
- package/esm/components/menu/menu-item/menu-item.d.ts +7 -3
- package/esm/components/menu/menu.component.js +33 -37
- package/esm/components/menu/menu.context.d.ts +2 -2
- package/esm/components/menu/menu.context.js +2 -2
- package/esm/components/menu/scrollable-block/scrollable-block.component.js +6 -24
- 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/__internal__/input-icon-toggle/input-icon-toggle.component.js +2 -1
- package/lib/components/link/link.component.d.ts +2 -0
- package/lib/components/link/link.component.js +7 -1
- package/lib/components/menu/__internal__/keyboard-navigation/index.d.ts +4 -2
- package/lib/components/menu/__internal__/keyboard-navigation/index.js +16 -15
- package/lib/components/menu/__internal__/locators.d.ts +6 -0
- package/lib/components/menu/__internal__/locators.js +18 -0
- package/lib/components/menu/__internal__/submenu/submenu.component.js +111 -113
- package/lib/components/menu/menu-full-screen/menu-full-screen.component.js +1 -2
- package/lib/components/menu/menu-item/menu-item.component.js +76 -52
- package/lib/components/menu/menu-item/menu-item.d.ts +7 -3
- package/lib/components/menu/menu.component.js +33 -37
- package/lib/components/menu/menu.context.d.ts +2 -2
- package/lib/components/menu/menu.context.js +2 -2
- package/lib/components/menu/scrollable-block/scrollable-block.component.js +6 -25
- 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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
2
|
+
|
|
1
3
|
import React, { useContext } from "react";
|
|
2
4
|
import PropTypes from "prop-types";
|
|
3
5
|
import StyledOptionRow from "./option-row.style";
|
|
@@ -9,7 +11,9 @@ const OptionRow = /*#__PURE__*/React.forwardRef(({
|
|
|
9
11
|
onSelect,
|
|
10
12
|
value,
|
|
11
13
|
index,
|
|
12
|
-
hidden
|
|
14
|
+
hidden,
|
|
15
|
+
style,
|
|
16
|
+
...rest
|
|
13
17
|
}, ref) => {
|
|
14
18
|
const handleClick = () => {
|
|
15
19
|
onSelect({
|
|
@@ -26,7 +30,7 @@ const OptionRow = /*#__PURE__*/React.forwardRef(({
|
|
|
26
30
|
isSelected = selectListContext.multiselectValues.includes(value);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
return /*#__PURE__*/React.createElement(StyledOptionRow, {
|
|
33
|
+
return /*#__PURE__*/React.createElement(StyledOptionRow, _extends({
|
|
30
34
|
id: id,
|
|
31
35
|
ref: ref,
|
|
32
36
|
"aria-selected": isSelected,
|
|
@@ -34,8 +38,9 @@ const OptionRow = /*#__PURE__*/React.forwardRef(({
|
|
|
34
38
|
onClick: handleClick,
|
|
35
39
|
isHighlighted: selectListContext.currentOptionsListIndex === index,
|
|
36
40
|
role: "option",
|
|
37
|
-
hidden: hidden
|
|
38
|
-
|
|
41
|
+
hidden: hidden,
|
|
42
|
+
style: style
|
|
43
|
+
}, rest), children);
|
|
39
44
|
});
|
|
40
45
|
OptionRow.propTypes = {
|
|
41
46
|
/** The option's visible text, displayed within Textbox of Select */
|
|
@@ -70,6 +75,12 @@ OptionRow.propTypes = {
|
|
|
70
75
|
* @private
|
|
71
76
|
* @ignore
|
|
72
77
|
* True when option should be hidden from the view (prop added by the SelectList component) */
|
|
73
|
-
hidden: PropTypes.bool
|
|
78
|
+
hidden: PropTypes.bool,
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @private
|
|
82
|
+
* @ignore
|
|
83
|
+
* object containing CSS styles to be passed to the underlying DOM element */
|
|
84
|
+
style: PropTypes.object
|
|
74
85
|
};
|
|
75
86
|
export default OptionRow;
|
|
@@ -3,12 +3,22 @@ import { baseTheme } from "../../../style/themes";
|
|
|
3
3
|
const StyledSelectListContainer = styled.div`
|
|
4
4
|
background-color: white;
|
|
5
5
|
box-shadow: var(--boxShadow100);
|
|
6
|
-
overflow: hidden;
|
|
7
6
|
animation: fadeIn 250ms ease-out;
|
|
8
7
|
position: absolute;
|
|
9
8
|
z-index: ${({
|
|
10
9
|
theme
|
|
11
10
|
}) => theme.zIndex.popover};
|
|
11
|
+
max-height: ${({
|
|
12
|
+
maxHeight
|
|
13
|
+
}) => `${maxHeight}px`};
|
|
14
|
+
overflow-y: auto;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-wrap: wrap;
|
|
17
|
+
align-items: flex-start;
|
|
18
|
+
|
|
19
|
+
${({
|
|
20
|
+
isLoading
|
|
21
|
+
}) => isLoading && "min-height: 150px"};
|
|
12
22
|
|
|
13
23
|
@keyframes fadeIn {
|
|
14
24
|
0% {
|
|
@@ -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(
|
|
@@ -72,7 +72,8 @@ const InputIconToggle = ({
|
|
|
72
72
|
onFocus: onFocus,
|
|
73
73
|
onBlur: onBlur,
|
|
74
74
|
onMouseDown: onMouseDown,
|
|
75
|
-
tabIndex: iconTabIndex
|
|
75
|
+
tabIndex: iconTabIndex,
|
|
76
|
+
"data-element": "input-icon-toggle"
|
|
76
77
|
}, /*#__PURE__*/_react.default.createElement(_icon.default, {
|
|
77
78
|
type: type
|
|
78
79
|
}));
|
|
@@ -26,6 +26,8 @@ export interface LinkProps extends StyledLinkProps, React.AriaAttributes {
|
|
|
26
26
|
ariaLabel?: string;
|
|
27
27
|
/** allows to set rel property in <a> tag */
|
|
28
28
|
rel?: string;
|
|
29
|
+
/** @ignore @private internal prop to be set when no href or onClick passed */
|
|
30
|
+
placeholderTabIndex?: boolean;
|
|
29
31
|
}
|
|
30
32
|
export declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLButtonElement | HTMLLinkElement>>;
|
|
31
33
|
export default Link;
|
|
@@ -45,6 +45,7 @@ const Link = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
45
45
|
target,
|
|
46
46
|
variant = "default",
|
|
47
47
|
isDarkBackground,
|
|
48
|
+
placeholderTabIndex,
|
|
48
49
|
...rest
|
|
49
50
|
}, ref) => {
|
|
50
51
|
const l = (0, _useLocale.default)();
|
|
@@ -104,7 +105,11 @@ const Link = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
104
105
|
type = "button";
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
return /*#__PURE__*/_react.default.createElement(type, componentProps,
|
|
108
|
+
return /*#__PURE__*/_react.default.createElement(type, { ...componentProps,
|
|
109
|
+
...(placeholderTabIndex && href === undefined && !onClick && {
|
|
110
|
+
tabIndex: -1
|
|
111
|
+
})
|
|
112
|
+
}, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, renderLinkIcon(), /*#__PURE__*/_react.default.createElement(_link.StyledContent, null, isSkipLink ? l.link.skipLinkLabel() : children), renderLinkIcon("right")));
|
|
108
113
|
};
|
|
109
114
|
|
|
110
115
|
return /*#__PURE__*/_react.default.createElement(_link.StyledLink, _extends({
|
|
@@ -182,6 +187,7 @@ Link.propTypes = {
|
|
|
182
187
|
"onClick": _propTypes.default.func,
|
|
183
188
|
"onKeyDown": _propTypes.default.func,
|
|
184
189
|
"onMouseDown": _propTypes.default.func,
|
|
190
|
+
"placeholderTabIndex": _propTypes.default.bool,
|
|
185
191
|
"rel": _propTypes.default.string,
|
|
186
192
|
"target": _propTypes.default.string,
|
|
187
193
|
"tooltipMessage": _propTypes.default.string,
|
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React from "react";
|
|
2
|
+
declare function characterNavigation(inputString?: string, focusableItems?: Element[]): Element | undefined;
|
|
3
|
+
declare function menuKeyboardNavigation(event: React.KeyboardEvent, focusableItems: Element[]): number | undefined;
|
|
4
|
+
export { characterNavigation, menuKeyboardNavigation };
|
|
@@ -8,31 +8,32 @@ exports.menuKeyboardNavigation = menuKeyboardNavigation;
|
|
|
8
8
|
|
|
9
9
|
var _events = _interopRequireDefault(require("../../../../__internal__/utils/helpers/events"));
|
|
10
10
|
|
|
11
|
-
var
|
|
11
|
+
var _locators = require("../locators");
|
|
12
12
|
|
|
13
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
14
14
|
|
|
15
|
-
function characterNavigation(inputString, focusableItems
|
|
16
|
-
if (!inputString) return
|
|
15
|
+
function characterNavigation(inputString, focusableItems) {
|
|
16
|
+
if (!inputString || !focusableItems) return undefined;
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return
|
|
18
|
+
const getInnerText = element => {
|
|
19
|
+
var _element$textContent, _element$textContent$;
|
|
20
|
+
|
|
21
|
+
return element === null || element === void 0 ? void 0 : (_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : (_element$textContent$ = _element$textContent.split("\n")) === null || _element$textContent$ === void 0 ? void 0 : _element$textContent$.map(text => text.trim()).join(" ");
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const getMenuText = element => {
|
|
25
|
-
|
|
26
|
-
return element.submenu;
|
|
27
|
-
}
|
|
25
|
+
var _getInnerText;
|
|
28
26
|
|
|
29
|
-
return
|
|
27
|
+
return (_getInnerText = getInnerText(element)) === null || _getInnerText === void 0 ? void 0 : _getInnerText.toLowerCase();
|
|
30
28
|
};
|
|
31
29
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const matchingItem = focusableItems.find(item => {
|
|
31
|
+
var _getMenuText;
|
|
32
|
+
|
|
33
|
+
if (!(item !== null && item !== void 0 && item.getAttribute("data-component"))) return false;
|
|
34
|
+
return [_locators.MENU_ITEM, _locators.SCROLLABLE_BLOCK_PARENT].includes(item.getAttribute("data-component")) && ((_getMenuText = getMenuText(item)) === null || _getMenuText === void 0 ? void 0 : _getMenuText.startsWith(inputString.toLowerCase()));
|
|
35
|
+
});
|
|
36
|
+
return matchingItem;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
function menuKeyboardNavigation(event, focusableItems) {
|