@carbon/react 1.56.0 → 1.57.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.
Files changed (79) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +902 -908
  2. package/es/components/ComboBox/ComboBox.js +1 -0
  3. package/es/components/ContainedList/ContainedList.d.ts +45 -0
  4. package/es/components/ContainedList/ContainedList.js +18 -3
  5. package/es/components/ContainedList/ContainedListItem/ContainedListItem.d.ts +35 -0
  6. package/es/components/ContainedList/ContainedListItem/ContainedListItem.js +3 -2
  7. package/es/components/ContainedList/ContainedListItem/index.d.ts +9 -0
  8. package/es/components/ContainedList/index.d.ts +11 -0
  9. package/es/components/ContainedList/index.js +13 -0
  10. package/es/components/DataTable/TableToolbarSearch.js +1 -1
  11. package/es/components/DataTable/stories/examples/TableToolbarFilter.d.ts +51 -0
  12. package/es/components/DatePicker/DatePicker.d.ts +5 -4
  13. package/es/components/DatePicker/plugins/rangePlugin.js +5 -6
  14. package/es/components/Dialog/index.d.ts +31 -0
  15. package/es/components/ListBox/next/ListBoxTrigger.js +4 -3
  16. package/es/components/MultiSelect/FilterableMultiSelect.d.ts +177 -0
  17. package/es/components/MultiSelect/FilterableMultiSelect.js +388 -313
  18. package/es/components/MultiSelect/MultiSelect.js +1 -2
  19. package/es/components/MultiSelect/MultiSelectPropTypes.d.ts +62 -0
  20. package/es/components/MultiSelect/index.d.ts +10 -0
  21. package/es/components/MultiSelect/index.js +2 -0
  22. package/es/components/OverflowMenu/OverflowMenu.d.ts +300 -0
  23. package/es/components/OverflowMenu/OverflowMenu.js +22 -14
  24. package/es/components/OverflowMenu/next/index.d.ts +39 -0
  25. package/es/components/OverflowMenu/next/index.js +4 -3
  26. package/es/components/Pagination/Pagination.js +9 -9
  27. package/es/components/RadioButton/RadioButton.d.ts +4 -0
  28. package/es/components/RadioButton/RadioButton.js +7 -1
  29. package/es/components/RadioButtonGroup/RadioButtonGroup.d.ts +4 -0
  30. package/es/components/RadioButtonGroup/RadioButtonGroup.js +7 -1
  31. package/es/components/RadioTile/RadioTile.d.ts +4 -0
  32. package/es/components/RadioTile/RadioTile.js +7 -1
  33. package/es/components/Slider/Slider.js +3 -3
  34. package/es/components/StructuredList/StructuredList.js +6 -2
  35. package/es/components/TileGroup/TileGroup.d.ts +8 -0
  36. package/es/components/TileGroup/TileGroup.js +7 -1
  37. package/es/components/TimePicker/TimePicker.js +1 -1
  38. package/es/index.js +2 -0
  39. package/es/internal/Selection.js +4 -1
  40. package/lib/components/ComboBox/ComboBox.js +1 -0
  41. package/lib/components/ContainedList/ContainedList.d.ts +45 -0
  42. package/lib/components/ContainedList/ContainedList.js +18 -3
  43. package/lib/components/ContainedList/ContainedListItem/ContainedListItem.d.ts +35 -0
  44. package/lib/components/ContainedList/ContainedListItem/ContainedListItem.js +3 -2
  45. package/lib/components/ContainedList/ContainedListItem/index.d.ts +9 -0
  46. package/lib/components/ContainedList/index.d.ts +11 -0
  47. package/lib/components/ContainedList/index.js +19 -0
  48. package/lib/components/DataTable/TableToolbarSearch.js +1 -1
  49. package/lib/components/DataTable/stories/examples/TableToolbarFilter.d.ts +51 -0
  50. package/lib/components/DatePicker/DatePicker.d.ts +5 -4
  51. package/lib/components/DatePicker/plugins/rangePlugin.js +5 -6
  52. package/lib/components/Dialog/index.d.ts +31 -0
  53. package/lib/components/ListBox/next/ListBoxTrigger.js +4 -3
  54. package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +177 -0
  55. package/lib/components/MultiSelect/FilterableMultiSelect.js +387 -312
  56. package/lib/components/MultiSelect/MultiSelect.js +1 -2
  57. package/lib/components/MultiSelect/MultiSelectPropTypes.d.ts +62 -0
  58. package/lib/components/MultiSelect/index.d.ts +10 -0
  59. package/lib/components/MultiSelect/index.js +2 -0
  60. package/lib/components/OverflowMenu/OverflowMenu.d.ts +300 -0
  61. package/lib/components/OverflowMenu/OverflowMenu.js +21 -13
  62. package/lib/components/OverflowMenu/next/index.d.ts +39 -0
  63. package/lib/components/OverflowMenu/next/index.js +4 -3
  64. package/lib/components/Pagination/Pagination.js +9 -9
  65. package/lib/components/RadioButton/RadioButton.d.ts +4 -0
  66. package/lib/components/RadioButton/RadioButton.js +7 -1
  67. package/lib/components/RadioButtonGroup/RadioButtonGroup.d.ts +4 -0
  68. package/lib/components/RadioButtonGroup/RadioButtonGroup.js +7 -1
  69. package/lib/components/RadioTile/RadioTile.d.ts +4 -0
  70. package/lib/components/RadioTile/RadioTile.js +7 -1
  71. package/lib/components/Slider/Slider.js +3 -3
  72. package/lib/components/StructuredList/StructuredList.js +6 -2
  73. package/lib/components/TileGroup/TileGroup.d.ts +8 -0
  74. package/lib/components/TileGroup/TileGroup.js +7 -1
  75. package/lib/components/TimePicker/TimePicker.js +1 -1
  76. package/lib/index.js +2 -0
  77. package/lib/internal/Selection.js +4 -1
  78. package/package.json +8 -8
  79. package/telemetry.yml +1 -1
@@ -8,14 +8,13 @@
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import { WarningFilled, WarningAltFilled } from '@carbon/icons-react';
10
10
  import cx from 'classnames';
11
- import Downshift from 'downshift';
11
+ import Downshift, { useCombobox, useMultipleSelection } from 'downshift';
12
12
  import isEqual from 'lodash.isequal';
13
13
  import PropTypes from 'prop-types';
14
14
  import React__default, { useContext, useState, useRef, useEffect } from 'react';
15
15
  import { defaultFilterItems } from '../ComboBox/tools/filter.js';
16
16
  import { sortingPropTypes } from './MultiSelectPropTypes.js';
17
17
  import ListBox from '../ListBox/index.js';
18
- import Selection from '../../internal/Selection.js';
19
18
  import { defaultItemToString } from './tools/itemToString.js';
20
19
  import mergeRefs from '../../tools/mergeRefs.js';
21
20
  import deprecate from '../../prop-types/deprecate.js';
@@ -24,12 +23,34 @@ import { defaultCompareItems, defaultSortItems } from './tools/sorting.js';
24
23
  import { usePrefix } from '../../internal/usePrefix.js';
25
24
  import '../FluidForm/FluidForm.js';
26
25
  import { FormContext } from '../FluidForm/FormContext.js';
26
+ import { useSelection } from '../../internal/Selection.js';
27
27
  import { match } from '../../internal/keyboard/match.js';
28
28
  import ListBoxSelection from '../ListBox/next/ListBoxSelection.js';
29
29
  import ListBoxTrigger from '../ListBox/next/ListBoxTrigger.js';
30
30
  import { ListBoxSize } from '../ListBox/ListBoxPropTypes.js';
31
31
  import { Escape, Space, Enter, Delete, Tab, Home, End } from '../../internal/keyboard/keys.js';
32
32
 
33
+ const {
34
+ InputBlur,
35
+ InputKeyDownEnter,
36
+ ItemClick,
37
+ MenuMouseLeave,
38
+ InputKeyDownArrowUp,
39
+ InputKeyDownArrowDown,
40
+ ItemMouseMove,
41
+ InputClick,
42
+ ToggleButtonClick,
43
+ FunctionToggleMenu,
44
+ InputChange,
45
+ InputKeyDownEscape,
46
+ FunctionSetHighlightedIndex
47
+ } = useCombobox.stateChangeTypes;
48
+ const {
49
+ SelectedItemKeyDownBackspace,
50
+ SelectedItemKeyDownDelete,
51
+ DropdownKeyDownBackspace,
52
+ FunctionRemoveSelectedItem
53
+ } = useMultipleSelection.stateChangeTypes;
33
54
  const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function FilterableMultiSelect(_ref, ref) {
34
55
  let {
35
56
  className: containerClassName,
@@ -60,6 +81,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
60
81
  titleText,
61
82
  type,
62
83
  selectionFeedback = 'top-after-reopen',
84
+ selectedItems: selected,
63
85
  size,
64
86
  sortItems = defaultSortItems,
65
87
  translateWithId,
@@ -72,20 +94,41 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
72
94
  isFluid
73
95
  } = useContext(FormContext);
74
96
  const [isFocused, setIsFocused] = useState(false);
75
- const [isOpen, setIsOpen] = useState(open);
76
- const [prevOpen, setPrevOpen] = useState(open);
97
+ const [isOpen, setIsOpen] = useState(!!open);
98
+ const [prevOpen, setPrevOpen] = useState(!!open);
77
99
  const [inputValue, setInputValue] = useState('');
78
100
  const [topItems, setTopItems] = useState(initialSelectedItems ?? []);
79
101
  const [inputFocused, setInputFocused] = useState(false);
80
- const [highlightedIndex, setHighlightedIndex] = useState(null);
81
- const [currentSelectedItems, setCurrentSelectedItems] = useState(initialSelectedItems ?? []);
82
- const textInput = useRef();
102
+ const {
103
+ selectedItems: controlledSelectedItems,
104
+ onItemChange,
105
+ clearSelection
106
+ } = useSelection({
107
+ disabled,
108
+ initialSelectedItems,
109
+ onChange,
110
+ selectedItems: selected
111
+ });
112
+ const textInput = useRef(null);
83
113
  const filterableMultiSelectInstanceId = useId();
84
114
  const prefix = usePrefix();
85
115
  if (prevOpen !== open) {
86
116
  setIsOpen(open);
87
117
  setPrevOpen(open);
88
118
  }
119
+ const sortedItems = sortItems(filterItems(items, {
120
+ itemToString,
121
+ inputValue
122
+ }), {
123
+ selectedItems: {
124
+ top: controlledSelectedItems,
125
+ fixed: [],
126
+ 'top-after-reopen': topItems
127
+ }[selectionFeedback],
128
+ itemToString,
129
+ compareItems,
130
+ locale
131
+ });
89
132
  const inline = type === 'inline';
90
133
  const showWarning = !invalid && warn;
91
134
  const wrapperClasses = cx(`${prefix}--multi-select__wrapper`, `${prefix}--multi-select--filterable__wrapper`, `${prefix}--list-box__wrapper`, containerClassName, {
@@ -122,101 +165,170 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
122
165
  const inputId = `${id}-input`;
123
166
  useEffect(() => {
124
167
  if (!isOpen) {
125
- setTopItems(currentSelectedItems);
168
+ setTopItems(controlledSelectedItems);
126
169
  }
127
- }, [currentSelectedItems, isOpen, setTopItems]);
128
- function handleOnChange(changes) {
129
- setCurrentSelectedItems(changes.selectedItems);
130
- if (onChange) {
131
- onChange(changes);
132
- }
133
- }
134
- function handleOnMenuChange(forceIsOpen) {
170
+ }, [controlledSelectedItems, isOpen, setTopItems]);
171
+ function handleMenuChange(forceIsOpen) {
135
172
  const nextIsOpen = forceIsOpen ?? !isOpen;
136
173
  setIsOpen(nextIsOpen);
137
174
  if (onMenuChange) {
138
175
  onMenuChange(nextIsOpen);
139
176
  }
140
- if (!isOpen) {
141
- setHighlightedIndex(0);
142
- }
143
177
  }
144
- function handleOnOuterClick() {
145
- handleOnMenuChange(false);
146
- }
147
- function handleOnStateChange(changes) {
178
+ const {
179
+ getToggleButtonProps,
180
+ getLabelProps,
181
+ getMenuProps,
182
+ getInputProps,
183
+ highlightedIndex,
184
+ setHighlightedIndex,
185
+ getItemProps,
186
+ openMenu,
187
+ isOpen: isMenuOpen
188
+ } = useCombobox({
189
+ isOpen,
190
+ items: sortedItems,
191
+ itemToString,
192
+ defaultHighlightedIndex: 0,
193
+ // after selection, highlight the first item.
194
+ id,
195
+ labelId,
196
+ menuId,
197
+ inputId,
198
+ inputValue,
199
+ stateReducer,
200
+ isItemDisabled(item, _index) {
201
+ return item.disabled;
202
+ }
203
+ });
204
+ function stateReducer(state, actionAndChanges) {
148
205
  const {
149
- type
150
- } = changes;
206
+ type,
207
+ props,
208
+ changes
209
+ } = actionAndChanges;
151
210
  const {
152
- stateChangeTypes
153
- } = Downshift;
211
+ highlightedIndex
212
+ } = changes;
213
+ if (changes.isOpen && !isOpen) {
214
+ setTopItems(controlledSelectedItems);
215
+ }
154
216
  switch (type) {
155
- case stateChangeTypes.keyDownArrowDown:
156
- case stateChangeTypes.keyDownArrowUp:
157
- case stateChangeTypes.keyDownHome:
158
- case stateChangeTypes.keyDownEnd:
159
- setHighlightedIndex(changes.highlightedIndex !== undefined ? changes.highlightedIndex : 0);
160
- if (stateChangeTypes.keyDownArrowDown === type && !isOpen) {
161
- handleOnMenuChange(true);
217
+ case InputKeyDownEnter:
218
+ if (changes.selectedItem && changes.selectedItem.disabled !== true) {
219
+ onItemChange(changes.selectedItem);
162
220
  }
163
- break;
164
- case stateChangeTypes.keyDownEscape:
165
- handleOnMenuChange(false);
166
- setHighlightedIndex(0);
167
- break;
168
- case stateChangeTypes.changeInput:
169
- setHighlightedIndex(0);
170
- break;
171
- case stateChangeTypes.keyDownEnter:
172
- if (!isOpen) {
173
- setHighlightedIndex(0);
221
+ setHighlightedIndex(changes.selectedItem);
222
+ return {
223
+ ...changes,
224
+ highlightedIndex: state.highlightedIndex
225
+ };
226
+ case ItemClick:
227
+ if (changes.selectedItem) {
228
+ onItemChange(changes.selectedItem);
229
+ }
230
+ setHighlightedIndex(changes.selectedItem);
231
+ return changes;
232
+ case InputBlur:
233
+ case InputKeyDownEscape:
234
+ setIsOpen(false);
235
+ return changes;
236
+ case FunctionToggleMenu:
237
+ case ToggleButtonClick:
238
+ if (changes.isOpen && !changes.selectedItem) {
239
+ return {
240
+ ...changes,
241
+ highlightedIndex: 0
242
+ };
243
+ }
244
+ return changes;
245
+ case InputChange:
246
+ if (onInputValueChange) {
247
+ onInputValueChange(changes.inputValue);
248
+ }
249
+ setInputValue(changes.inputValue ?? '');
250
+ setIsOpen(true);
251
+ return changes;
252
+ case InputClick:
253
+ return {
254
+ ...changes,
255
+ isOpen: false
256
+ };
257
+ case MenuMouseLeave:
258
+ return {
259
+ ...changes,
260
+ highlightedIndex: state.highlightedIndex
261
+ };
262
+ case InputKeyDownArrowUp:
263
+ case InputKeyDownArrowDown:
264
+ if (InputKeyDownArrowDown === type && !isOpen) {
265
+ setIsOpen(true);
174
266
  }
175
- break;
176
- case stateChangeTypes.clickItem:
177
- if (isOpen) {
178
- const sortedItems = sortItems(filterItems(items, {
179
- itemToString,
180
- inputValue
181
- }), {
182
- selectedItems: {
183
- top: changes.selectedItems,
184
- fixed: [],
185
- 'top-after-reopen': topItems
186
- }[selectionFeedback],
187
- itemToString,
188
- compareItems,
189
- locale
190
- });
191
- const sortedSelectedIndex = sortedItems.indexOf(changes.selectedItem);
192
- setHighlightedIndex(sortedSelectedIndex);
267
+ if (highlightedIndex > -1) {
268
+ const itemArray = document.querySelectorAll(`li.${prefix}--list-box__menu-item[role="option"]`);
269
+ props.scrollIntoView(itemArray[highlightedIndex]);
193
270
  }
194
- break;
271
+ if (highlightedIndex === -1) {
272
+ return {
273
+ ...changes,
274
+ highlightedIndex: 0
275
+ };
276
+ }
277
+ return changes;
278
+ case ItemMouseMove:
279
+ return {
280
+ ...changes,
281
+ highlightedIndex: state.highlightedIndex
282
+ };
283
+ case FunctionSetHighlightedIndex:
284
+ if (!isOpen) {
285
+ return {
286
+ ...changes,
287
+ highlightedIndex: 0
288
+ };
289
+ } else {
290
+ return {
291
+ ...changes,
292
+ highlightedIndex: props.items.indexOf(highlightedIndex)
293
+ };
294
+ }
295
+ default:
296
+ return changes;
195
297
  }
196
298
  }
197
- function handleOnInputValueChange(inputValue, _ref2) {
198
- let {
199
- type
200
- } = _ref2;
201
- if (onInputValueChange) {
202
- onInputValueChange(inputValue);
299
+ const {
300
+ getDropdownProps
301
+ } = useMultipleSelection({
302
+ ...downshiftProps,
303
+ activeIndex: highlightedIndex,
304
+ initialSelectedItems,
305
+ selectedItems: controlledSelectedItems,
306
+ itemToString,
307
+ onStateChange(changes) {
308
+ switch (changes.type) {
309
+ case SelectedItemKeyDownBackspace:
310
+ case SelectedItemKeyDownDelete:
311
+ case DropdownKeyDownBackspace:
312
+ case FunctionRemoveSelectedItem:
313
+ {
314
+ clearSelection();
315
+ break;
316
+ }
317
+ }
203
318
  }
204
- if (type !== Downshift.stateChangeTypes.changeInput) {
205
- return;
319
+ });
320
+ useEffect(() => {
321
+ if (isOpen && !isMenuOpen) {
322
+ openMenu();
206
323
  }
207
- if (Array.isArray(inputValue)) {
208
- clearInputValue();
324
+ });
325
+ function clearInputValue(event) {
326
+ const value = textInput.current?.value;
327
+ if (value?.length === 1 || event && match(event, Escape)) {
328
+ setInputValue('');
209
329
  } else {
210
- setInputValue(inputValue);
330
+ setInputValue(value ?? '');
211
331
  }
212
- if (inputValue && !isOpen) {
213
- handleOnMenuChange(true);
214
- } else if (!inputValue && isOpen) {
215
- handleOnMenuChange(false);
216
- }
217
- }
218
- function clearInputValue(event) {
219
- textInput.current.value.length === 1 || match(event, Escape) ? setInputValue('') : setInputValue(textInput.current.value);
220
332
  if (textInput.current) {
221
333
  textInput.current.focus();
222
334
  }
@@ -229,239 +341,197 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
229
341
  size: 'mini'
230
342
  });
231
343
  }
232
- return /*#__PURE__*/React__default.createElement(Selection, {
233
- disabled: disabled,
234
- onChange: handleOnChange,
235
- initialSelectedItems: initialSelectedItems,
236
- render: _ref3 => {
237
- let {
238
- selectedItems,
239
- onItemChange,
240
- clearSelection
241
- } = _ref3;
242
- return /*#__PURE__*/React__default.createElement(Downshift, _extends({}, downshiftProps, {
243
- highlightedIndex: highlightedIndex,
244
- id: id,
245
- isOpen: isOpen,
246
- inputValue: inputValue,
247
- onInputValueChange: handleOnInputValueChange,
248
- onChange: selectedItem => {
249
- if (selectedItem !== null) {
250
- onItemChange(selectedItem);
251
- }
252
- },
253
- itemToString: itemToString,
254
- onStateChange: handleOnStateChange,
255
- onOuterClick: handleOnOuterClick,
256
- selectedItem: selectedItems,
257
- labelId: labelId,
258
- menuId: menuId,
259
- inputId: inputId
260
- }), _ref4 => {
261
- let {
262
- getInputProps,
263
- getItemProps,
264
- getLabelProps,
265
- getMenuProps,
266
- getRootProps,
267
- getToggleButtonProps,
268
- isOpen,
269
- inputValue,
270
- selectedItem
271
- } = _ref4;
272
- const className = cx(`${prefix}--multi-select`, `${prefix}--combo-box`, `${prefix}--multi-select--filterable`, {
273
- [`${prefix}--multi-select--invalid`]: invalid,
274
- [`${prefix}--multi-select--invalid--focused`]: invalid && inputFocused,
275
- [`${prefix}--multi-select--open`]: isOpen,
276
- [`${prefix}--multi-select--inline`]: inline,
277
- [`${prefix}--multi-select--selected`]: selectedItem.length > 0,
278
- [`${prefix}--multi-select--filterable--input-focused`]: inputFocused
279
- });
280
- const rootProps = getRootProps({}, {
281
- suppressRefError: true
282
- });
283
- const labelProps = getLabelProps();
284
- const buttonProps = getToggleButtonProps({
285
- disabled,
286
- onClick: () => {
287
- handleOnMenuChange(!isOpen);
288
- if (textInput.current) {
289
- textInput.current.focus();
290
- }
291
- },
292
- // When we moved the "root node" of Downshift to the <input> for
293
- // ARIA 1.2 compliance, we unfortunately hit this branch for the
294
- // "mouseup" event that downshift listens to:
295
- // https://github.com/downshift-js/downshift/blob/v5.2.1/src/downshift.js#L1051-L1065
296
- //
297
- // As a result, it will reset the state of the component and so we
298
- // stop the event from propagating to prevent this. This allows the
299
- // toggleMenu behavior for the toggleButton to correctly open and
300
- // close the menu.
301
- onMouseUp(event) {
302
- if (isOpen) {
303
- event.stopPropagation();
304
- }
305
- }
306
- });
307
- const inputProps = getInputProps({
308
- 'aria-controls': isOpen ? menuId : null,
309
- 'aria-describedby': helperText ? helperId : null,
310
- // Remove excess aria `aria-labelledby`. HTML <label for>
311
- // provides this aria information.
312
- 'aria-labelledby': null,
313
- disabled,
314
- placeholder,
315
- onClick: () => {
316
- handleOnMenuChange(true);
317
- },
318
- onKeyDown: event => {
319
- if (match(event, Space)) {
320
- event.stopPropagation();
321
- }
322
- if (match(event, Enter)) {
323
- handleOnMenuChange(true);
324
- }
325
- if (!disabled) {
326
- if (match(event, Delete) || match(event, Escape)) {
327
- if (isOpen) {
328
- handleOnMenuChange(true);
329
- clearInputValue(event);
330
- event.stopPropagation();
331
- } else if (!isOpen) {
332
- clearInputValue(event);
333
- clearSelection();
334
- event.stopPropagation();
335
- }
336
- }
337
- }
338
- if (match(event, Tab)) {
339
- handleOnMenuChange(false);
340
- }
341
- if (match(event, Home) && event.code !== 'Numpad7') {
342
- event.target.setSelectionRange(0, 0);
343
- }
344
- if (match(event, End) && event.code !== 'Numpad1') {
345
- event.target.setSelectionRange(event.target.value.length, event.target.value.length);
346
- }
347
- },
348
- onFocus: () => {
349
- setInputFocused(true);
350
- },
351
- onBlur: () => {
352
- setInputFocused(false);
353
- setInputValue('');
354
- }
355
- });
356
- const menuProps = getMenuProps({}, {
357
- suppressRefError: true
358
- });
359
- const handleFocus = evt => {
360
- if (evt.target.classList.contains(`${prefix}--tag__close-icon`) || evt.target.classList.contains(`${prefix}--list-box__selection`)) {
361
- setIsFocused(false);
362
- } else {
363
- setIsFocused(evt.type === 'focus' ? true : false);
364
- }
365
- };
366
- const clearSelectionContent = selectedItems.length > 0 ? /*#__PURE__*/React__default.createElement("span", {
367
- className: `${prefix}--visually-hidden`
368
- }, clearSelectionDescription, " ", selectedItems.length, ",", clearSelectionText) : /*#__PURE__*/React__default.createElement("span", {
369
- className: `${prefix}--visually-hidden`
370
- }, clearSelectionDescription, ": 0");
371
- return /*#__PURE__*/React__default.createElement("div", {
372
- className: wrapperClasses
373
- }, titleText ? /*#__PURE__*/React__default.createElement("label", _extends({
374
- className: titleClasses
375
- }, labelProps), titleText, /*#__PURE__*/React__default.createElement("span", {
376
- className: `${prefix}--visually-hidden`
377
- }, clearSelectionContent)) : null, /*#__PURE__*/React__default.createElement(ListBox, {
378
- onFocus: isFluid ? handleFocus : null,
379
- onBlur: isFluid ? handleFocus : null,
380
- className: className,
381
- disabled: disabled,
382
- light: light,
383
- ref: ref,
384
- invalid: invalid,
385
- invalidText: invalidText,
386
- warn: warn,
387
- warnText: warnText,
388
- isOpen: isOpen,
389
- size: size
390
- }, /*#__PURE__*/React__default.createElement("div", {
391
- className: `${prefix}--list-box__field`
392
- }, selectedItem.length > 0 && /*#__PURE__*/React__default.createElement(ListBoxSelection, {
393
- clearSelection: () => {
344
+ const className = cx(`${prefix}--multi-select`, `${prefix}--combo-box`, `${prefix}--multi-select--filterable`, {
345
+ [`${prefix}--multi-select--invalid`]: invalid,
346
+ [`${prefix}--multi-select--invalid--focused`]: invalid && inputFocused,
347
+ [`${prefix}--multi-select--open`]: isOpen,
348
+ [`${prefix}--multi-select--inline`]: inline,
349
+ [`${prefix}--multi-select--selected`]: controlledSelectedItems?.length > 0,
350
+ [`${prefix}--multi-select--filterable--input-focused`]: inputFocused
351
+ });
352
+ const labelProps = getLabelProps();
353
+ const buttonProps = getToggleButtonProps({
354
+ disabled,
355
+ onClick: () => {
356
+ handleMenuChange(!isOpen);
357
+ textInput.current?.focus();
358
+ },
359
+ // When we moved the "root node" of Downshift to the <input> for
360
+ // ARIA 1.2 compliance, we unfortunately hit this branch for the
361
+ // "mouseup" event that downshift listens to:
362
+ // https://github.com/downshift-js/downshift/blob/v5.2.1/src/downshift.js#L1051-L1065
363
+ //
364
+ // As a result, it will reset the state of the component and so we
365
+ // stop the event from propagating to prevent this. This allows the
366
+ // toggleMenu behavior for the toggleButton to correctly open and
367
+ // close the menu.
368
+ onMouseUp(event) {
369
+ if (isOpen) {
370
+ event.stopPropagation();
371
+ }
372
+ }
373
+ });
374
+ const inputProps = getInputProps(getDropdownProps({
375
+ 'aria-controls': isOpen ? menuId : undefined,
376
+ 'aria-describedby': helperText ? helperId : undefined,
377
+ 'aria-haspopup': 'listbox',
378
+ // Remove excess aria `aria-labelledby`. HTML <label for>
379
+ // provides this aria information.
380
+ 'aria-labelledby': undefined,
381
+ disabled,
382
+ placeholder,
383
+ preventKeyAction: isOpen,
384
+ onClick: () => handleMenuChange(true),
385
+ onKeyDown(event) {
386
+ const $input = event.target;
387
+ const $value = $input.value;
388
+ if (match(event, Space)) {
389
+ event.stopPropagation();
390
+ }
391
+ if (match(event, Enter)) {
392
+ handleMenuChange(true);
393
+ }
394
+ if (!disabled) {
395
+ if (match(event, Delete) || match(event, Escape)) {
396
+ if (isOpen) {
397
+ handleMenuChange(true);
398
+ clearInputValue(event);
399
+ event.stopPropagation();
400
+ } else if (!isOpen) {
401
+ clearInputValue(event);
394
402
  clearSelection();
395
- if (textInput.current) {
396
- textInput.current.focus();
397
- }
398
- },
399
- selectionCount: selectedItem.length,
400
- translateWithId: translateWithId,
401
- disabled: disabled
402
- }), /*#__PURE__*/React__default.createElement("input", _extends({
403
- className: inputClasses
404
- }, rootProps, inputProps, {
405
- ref: mergeRefs(textInput, rootProps.ref)
406
- })), invalid && /*#__PURE__*/React__default.createElement(WarningFilled, {
407
- className: `${prefix}--list-box__invalid-icon`
408
- }), showWarning && /*#__PURE__*/React__default.createElement(WarningAltFilled, {
409
- className: `${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`
410
- }), inputValue && /*#__PURE__*/React__default.createElement(ListBoxSelection, {
411
- clearSelection: clearInputValue,
412
- disabled: disabled,
413
- translateWithId: translateWithId,
414
- onMouseUp: event => {
415
- // If we do not stop this event from propagating,
416
- // it seems like Downshift takes our event and
417
- // prevents us from getting `onClick` /
418
- // `clearSelection` from the underlying <button> in
419
- // ListBoxSelection
420
403
  event.stopPropagation();
421
404
  }
422
- }), /*#__PURE__*/React__default.createElement(ListBoxTrigger, _extends({}, buttonProps, {
423
- isOpen: isOpen,
424
- translateWithId: translateWithId
425
- }))), normalizedSlug, isOpen ? /*#__PURE__*/React__default.createElement(ListBox.Menu, menuProps, sortItems(filterItems(items, {
426
- itemToString,
427
- inputValue
428
- }), {
429
- selectedItems: {
430
- top: selectedItems,
431
- fixed: [],
432
- 'top-after-reopen': topItems
433
- }[selectionFeedback],
434
- itemToString,
435
- compareItems,
436
- locale
437
- }).map((item, index) => {
438
- const isChecked = selectedItem.filter(selected => isEqual(selected, item)).length > 0;
439
- const itemProps = getItemProps({
440
- item,
441
- disabled: item.disabled,
442
- ['aria-selected']: isChecked
443
- });
444
- const itemText = itemToString(item);
445
- return /*#__PURE__*/React__default.createElement(ListBox.MenuItem, _extends({
446
- key: itemProps.id,
447
- "aria-label": itemText,
448
- isActive: isChecked,
449
- isHighlighted: highlightedIndex === index,
450
- title: itemText
451
- }, itemProps), /*#__PURE__*/React__default.createElement("div", {
452
- className: `${prefix}--checkbox-wrapper`
453
- }, /*#__PURE__*/React__default.createElement("span", {
454
- title: useTitleInItem ? itemText : null,
455
- className: `${prefix}--checkbox-label`,
456
- "data-contained-checkbox-state": isChecked,
457
- id: `${itemProps.id}-item`
458
- }, ItemToElement ? /*#__PURE__*/React__default.createElement(ItemToElement, _extends({
459
- key: itemProps.id
460
- }, item)) : itemText)));
461
- })) : null), !inline && !invalid && !warn ? helper : null);
462
- });
405
+ }
406
+ }
407
+ if (match(event, Tab)) {
408
+ handleMenuChange(false);
409
+ }
410
+ if (match(event, Home)) {
411
+ $input.setSelectionRange(0, 0);
412
+ }
413
+ if (match(event, End)) {
414
+ $input.setSelectionRange($value.length, $value.length);
415
+ }
416
+ },
417
+ onFocus: () => setInputFocused(true),
418
+ onBlur() {
419
+ setInputFocused(false);
420
+ setInputValue('');
463
421
  }
422
+ }));
423
+ const menuProps = getMenuProps({}, {
424
+ suppressRefError: true
464
425
  });
426
+ const handleFocus = evt => {
427
+ if (evt?.target.classList.contains(`${prefix}--tag__close-icon`) || evt?.target.classList.contains(`${prefix}--list-box__selection`)) {
428
+ setIsFocused(false);
429
+ } else {
430
+ setIsFocused(evt?.type === 'focus' ? true : false);
431
+ }
432
+ };
433
+ const clearSelectionContent = controlledSelectedItems.length > 0 ? /*#__PURE__*/React__default.createElement("span", {
434
+ className: `${prefix}--visually-hidden`
435
+ }, clearSelectionDescription, " ", controlledSelectedItems.length, ",", clearSelectionText) : /*#__PURE__*/React__default.createElement("span", {
436
+ className: `${prefix}--visually-hidden`
437
+ }, clearSelectionDescription, ": 0");
438
+ return /*#__PURE__*/React__default.createElement("div", {
439
+ className: wrapperClasses
440
+ }, titleText ? /*#__PURE__*/React__default.createElement("label", _extends({
441
+ className: titleClasses
442
+ }, labelProps), titleText, /*#__PURE__*/React__default.createElement("span", {
443
+ className: `${prefix}--visually-hidden`
444
+ }, clearSelectionContent)) : null, /*#__PURE__*/React__default.createElement(ListBox, {
445
+ onFocus: isFluid ? handleFocus : undefined,
446
+ onBlur: isFluid ? handleFocus : undefined,
447
+ className: className,
448
+ disabled: disabled,
449
+ light: light,
450
+ ref: ref,
451
+ invalid: invalid,
452
+ invalidText: invalidText,
453
+ warn: warn,
454
+ warnText: warnText,
455
+ isOpen: isOpen,
456
+ size: size
457
+ }, /*#__PURE__*/React__default.createElement("div", {
458
+ className: `${prefix}--list-box__field`
459
+ }, controlledSelectedItems.length > 0 &&
460
+ /*#__PURE__*/
461
+ // @ts-expect-error: It is expecting a non-required prop called: "onClearSelection"
462
+ React__default.createElement(ListBoxSelection, {
463
+ clearSelection: () => {
464
+ clearSelection();
465
+ if (textInput.current) {
466
+ textInput.current.focus();
467
+ }
468
+ },
469
+ selectionCount: controlledSelectedItems.length,
470
+ translateWithId: translateWithId,
471
+ disabled: disabled
472
+ }), /*#__PURE__*/React__default.createElement("input", _extends({
473
+ className: inputClasses
474
+ }, inputProps, {
475
+ ref: mergeRefs(textInput, inputProps.ref)
476
+ })), invalid && /*#__PURE__*/React__default.createElement(WarningFilled, {
477
+ className: `${prefix}--list-box__invalid-icon`
478
+ }), showWarning && /*#__PURE__*/React__default.createElement(WarningAltFilled, {
479
+ className: `${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`
480
+ }), inputValue &&
481
+ /*#__PURE__*/
482
+ // @ts-expect-error: It is expecting two non-required prop called: "onClearSelection" & "selectionCount"
483
+ React__default.createElement(ListBoxSelection, {
484
+ clearSelection: clearInputValue,
485
+ disabled: disabled,
486
+ translateWithId: translateWithId,
487
+ onMouseUp: event => {
488
+ // If we do not stop this event from propagating,
489
+ // it seems like Downshift takes our event and
490
+ // prevents us from getting `onClick` /
491
+ // `clearSelection` from the underlying <button> in
492
+ // ListBoxSelection
493
+ event.stopPropagation();
494
+ }
495
+ }), /*#__PURE__*/React__default.createElement(ListBoxTrigger, _extends({}, buttonProps, {
496
+ // @ts-expect-error
497
+ isOpen: isOpen,
498
+ translateWithId: translateWithId
499
+ }))), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, menuProps, isOpen ? sortedItems.map((item, index) => {
500
+ const isChecked = controlledSelectedItems.filter(selected => isEqual(selected, item)).length > 0;
501
+ const itemProps = getItemProps({
502
+ item,
503
+ ['aria-selected']: isChecked
504
+ });
505
+ const itemText = itemToString(item);
506
+
507
+ // The initial implementation using <Downshift> would place the disabled attribute
508
+ // on disabled menu items. Conversely, useCombobox places aria-disabled instead.
509
+ // To avoid any potential breaking changes, we avoid placing aria-disabled and
510
+ // instead match the old behavior of placing the disabled attribute.
511
+ const disabled = itemProps['aria-disabled'];
512
+ const {
513
+ 'aria-disabled': unusedAriaDisabled,
514
+ // eslint-disable-line @typescript-eslint/no-unused-vars
515
+ ...modifiedItemProps
516
+ } = itemProps;
517
+ return /*#__PURE__*/React__default.createElement(ListBox.MenuItem, _extends({
518
+ key: itemProps.id,
519
+ "aria-label": itemText,
520
+ isActive: isChecked,
521
+ isHighlighted: highlightedIndex === index,
522
+ title: itemText,
523
+ disabled: disabled
524
+ }, modifiedItemProps), /*#__PURE__*/React__default.createElement("div", {
525
+ className: `${prefix}--checkbox-wrapper`
526
+ }, /*#__PURE__*/React__default.createElement("span", {
527
+ title: useTitleInItem ? itemText : undefined,
528
+ className: `${prefix}--checkbox-label`,
529
+ "data-contained-checkbox-state": isChecked,
530
+ id: `${itemProps.id}-item`
531
+ }, ItemToElement ? /*#__PURE__*/React__default.createElement(ItemToElement, _extends({
532
+ key: itemProps.id
533
+ }, item)) : itemText)));
534
+ }) : null)), !inline && !invalid && !warn ? helper : null);
465
535
  });
466
536
  FilterableMultiSelect.propTypes = {
467
537
  /**
@@ -493,6 +563,7 @@ FilterableMultiSelect.propTypes = {
493
563
  /**
494
564
  * Additional props passed to Downshift
495
565
  */
566
+ // @ts-ignore
496
567
  downshiftProps: PropTypes.shape(Downshift.propTypes),
497
568
  /**
498
569
  * Specify whether the title text should be hidden or not
@@ -580,6 +651,11 @@ FilterableMultiSelect.propTypes = {
580
651
  */
581
652
  slug: PropTypes.node,
582
653
  ...sortingPropTypes,
654
+ /**
655
+ * Provide text to be used in a `<label>` element that is tied to the
656
+ * combobox via ARIA attributes.
657
+ */
658
+ titleText: PropTypes.node,
583
659
  /**
584
660
  * Callback function for translating ListBoxMenuIcon SVG title
585
661
  */
@@ -597,6 +673,5 @@ FilterableMultiSelect.propTypes = {
597
673
  */
598
674
  warnText: PropTypes.node
599
675
  };
600
- var FilterableMultiSelect$1 = FilterableMultiSelect;
601
676
 
602
- export { FilterableMultiSelect$1 as default };
677
+ export { FilterableMultiSelect as default };