@carbon/react 1.60.3 → 1.61.0-rc.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.
Files changed (34) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +980 -1062
  2. package/es/components/ComposedModal/ComposedModal.js +4 -2
  3. package/es/components/DatePicker/DatePicker.js +6 -1
  4. package/es/components/Dropdown/Dropdown.d.ts +4 -0
  5. package/es/components/Dropdown/Dropdown.js +53 -8
  6. package/es/components/Modal/Modal.js +4 -2
  7. package/es/components/MultiSelect/FilterableMultiSelect.d.ts +20 -14
  8. package/es/components/MultiSelect/FilterableMultiSelect.js +54 -5
  9. package/es/components/MultiSelect/MultiSelect.d.ts +7 -38
  10. package/es/components/MultiSelect/MultiSelect.js +52 -5
  11. package/es/components/MultiSelect/MultiSelectPropTypes.d.ts +19 -16
  12. package/es/components/OverflowMenu/OverflowMenu.js +0 -1
  13. package/es/components/OverflowMenu/next/index.d.ts +4 -0
  14. package/es/components/OverflowMenu/next/index.js +41 -2
  15. package/es/components/Pagination/Pagination.js +2 -2
  16. package/es/components/UIShell/HeaderContainer.d.ts +11 -9
  17. package/es/components/UIShell/HeaderContainer.js +9 -7
  18. package/lib/components/ComposedModal/ComposedModal.js +6 -4
  19. package/lib/components/DatePicker/DatePicker.js +6 -1
  20. package/lib/components/Dropdown/Dropdown.d.ts +4 -0
  21. package/lib/components/Dropdown/Dropdown.js +49 -4
  22. package/lib/components/Modal/Modal.js +6 -4
  23. package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +20 -14
  24. package/lib/components/MultiSelect/FilterableMultiSelect.js +51 -2
  25. package/lib/components/MultiSelect/MultiSelect.d.ts +7 -38
  26. package/lib/components/MultiSelect/MultiSelect.js +49 -2
  27. package/lib/components/MultiSelect/MultiSelectPropTypes.d.ts +19 -16
  28. package/lib/components/OverflowMenu/OverflowMenu.js +0 -1
  29. package/lib/components/OverflowMenu/next/index.d.ts +4 -0
  30. package/lib/components/OverflowMenu/next/index.js +40 -1
  31. package/lib/components/Pagination/Pagination.js +2 -2
  32. package/lib/components/UIShell/HeaderContainer.d.ts +11 -9
  33. package/lib/components/UIShell/HeaderContainer.js +9 -7
  34. package/package.json +7 -7
@@ -9,6 +9,7 @@ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js
9
9
  import React__default, { useRef, useState, useEffect } from 'react';
10
10
  import { isElement } from 'react-is';
11
11
  import PropTypes from 'prop-types';
12
+ import { Layer } from '../Layer/index.js';
12
13
  import { ModalHeader } from './ModalHeader.js';
13
14
  import { ModalFooter } from './ModalFooter.js';
14
15
  import debounce from 'lodash.debounce';
@@ -59,7 +60,7 @@ const ModalBody = /*#__PURE__*/React__default.forwardRef(function ModalBody(_ref
59
60
  tabIndex: 0,
60
61
  role: 'region'
61
62
  } : {};
62
- return /*#__PURE__*/React__default.createElement("div", _extends({
63
+ return /*#__PURE__*/React__default.createElement(Layer, _extends({
63
64
  className: contentClass
64
65
  }, hasScrollingContentProps, rest, {
65
66
  ref: mergeRefs(contentRef, ref)
@@ -249,7 +250,8 @@ const ComposedModal = /*#__PURE__*/React__default.forwardRef(function ComposedMo
249
250
  size: 'sm'
250
251
  });
251
252
  }
252
- return /*#__PURE__*/React__default.createElement("div", _extends({}, rest, {
253
+ return /*#__PURE__*/React__default.createElement(Layer, _extends({}, rest, {
254
+ level: 0,
253
255
  role: "presentation",
254
256
  ref: ref,
255
257
  "aria-hidden": !open,
@@ -176,6 +176,8 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
176
176
 
177
177
  // fix datepicker deleting the selectedDate when the calendar closes
178
178
  const onCalendarClose = (selectedDates, dateStr) => {
179
+ endInputField?.current?.focus();
180
+ calendarRef?.current?.calendarContainer?.classList.remove('open');
179
181
  setTimeout(() => {
180
182
  if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
181
183
  startInputField.current.value = lastStartValue.current;
@@ -355,9 +357,12 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
355
357
  calendarRef.current = calendar;
356
358
  function handleArrowDown(event) {
357
359
  if (match(event, Escape)) {
358
- calendar.calendarContainer.classList.remove('open');
360
+ calendar?.calendarContainer?.classList.remove('open');
359
361
  }
360
362
  if (match(event, ArrowDown)) {
363
+ if (event.target == endInputField.current) {
364
+ calendar?.calendarContainer?.classList.add('open');
365
+ }
361
366
  const {
362
367
  calendarContainer,
363
368
  selectedDateElem: fpSelectedDateElem,
@@ -23,6 +23,10 @@ export interface DropdownProps<ItemType> extends Omit<ReactAttr<HTMLDivElement>,
23
23
  * Specify a label to be read by screen readers on the container note.
24
24
  */
25
25
  ariaLabel?: string;
26
+ /**
27
+ * **Experimental**: Will attempt to automatically align the floating element to avoid collisions with the viewport and being clipped by ancestor elements.
28
+ */
29
+ autoAlign?: boolean;
26
30
  /**
27
31
  * Specify the direction of the dropdown. Can be either top or bottom.
28
32
  */
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
- import React__default, { useContext, useRef, useState } from 'react';
9
+ import React__default, { useEffect, useContext, useRef, useState } from 'react';
10
10
  import { useSelect } from 'downshift';
11
11
  import cx from 'classnames';
12
12
  import PropTypes from 'prop-types';
@@ -18,6 +18,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
18
18
  import '../FluidForm/FluidForm.js';
19
19
  import { FormContext } from '../FluidForm/FormContext.js';
20
20
  import setupGetInstanceId from '../../tools/setupGetInstanceId.js';
21
+ import { useFloating, size, flip, autoUpdate } from '@floating-ui/react';
21
22
  import { ListBoxSize, ListBoxType } from '../ListBox/ListBoxPropTypes.js';
22
23
 
23
24
  const getInstanceId = setupGetInstanceId();
@@ -43,6 +44,7 @@ const defaultItemToString = item => {
43
44
  };
44
45
  const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
45
46
  let {
47
+ autoAlign = false,
46
48
  className: containerClassName,
47
49
  disabled = false,
48
50
  direction = 'bottom',
@@ -54,7 +56,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
54
56
  itemToElement = null,
55
57
  renderSelectedItem,
56
58
  type = 'default',
57
- size,
59
+ size: size$1,
58
60
  onChange,
59
61
  id,
60
62
  titleText = '',
@@ -73,6 +75,40 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
73
75
  slug,
74
76
  ...other
75
77
  } = _ref;
78
+ const {
79
+ refs,
80
+ floatingStyles
81
+ } = useFloating(autoAlign ? {
82
+ placement: direction,
83
+ // The floating element is positioned relative to its nearest
84
+ // containing block (usually the viewport). It will in many cases also
85
+ // “break” the floating element out of a clipping ancestor.
86
+ // https://floating-ui.com/docs/misc#clipping
87
+ strategy: 'fixed',
88
+ // Middleware order matters, arrow should be last
89
+ middleware: [size({
90
+ apply(_ref2) {
91
+ let {
92
+ rects,
93
+ elements
94
+ } = _ref2;
95
+ Object.assign(elements.floating.style, {
96
+ width: `${rects.reference.width}px`
97
+ });
98
+ }
99
+ }), flip()],
100
+ whileElementsMounted: autoUpdate
101
+ } : {} // When autoAlign is turned off, floating-ui will not be used
102
+ );
103
+ useEffect(() => {
104
+ if (autoAlign) {
105
+ Object.keys(floatingStyles).forEach(style => {
106
+ if (refs.floating.current) {
107
+ refs.floating.current.style[style] = floatingStyles[style];
108
+ }
109
+ });
110
+ }
111
+ }, [floatingStyles, autoAlign, refs.floating]);
76
112
  const prefix = usePrefix();
77
113
  const {
78
114
  isFluid
@@ -146,8 +182,9 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
146
182
  [`${prefix}--dropdown--disabled`]: disabled,
147
183
  [`${prefix}--dropdown--light`]: light,
148
184
  [`${prefix}--dropdown--readonly`]: readOnly,
149
- [`${prefix}--dropdown--${size}`]: size,
150
- [`${prefix}--list-box--up`]: direction === 'top'
185
+ [`${prefix}--dropdown--${size$1}`]: size$1,
186
+ [`${prefix}--list-box--up`]: direction === 'top',
187
+ [`${prefix}--dropdown--autoalign`]: autoAlign
151
188
  });
152
189
  const titleClasses = cx(`${prefix}--label`, {
153
190
  [`${prefix}--label--disabled`]: disabled,
@@ -174,10 +211,10 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
174
211
  id: helperId,
175
212
  className: helperClasses
176
213
  }, helperText) : null;
177
- function onSelectedItemChange(_ref2) {
214
+ function onSelectedItemChange(_ref3) {
178
215
  let {
179
216
  selectedItem
180
- } = _ref2;
217
+ } = _ref3;
181
218
  if (onChange) {
182
219
  onChange({
183
220
  selectedItem: selectedItem ?? null
@@ -227,6 +264,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
227
264
  }
228
265
  };
229
266
  const menuProps = getMenuProps();
267
+ const menuRef = mergeRefs(menuProps.ref, refs.setFloating);
230
268
 
231
269
  // Slug is always size `mini`
232
270
  let normalizedSlug;
@@ -243,7 +281,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
243
281
  onFocus: handleFocus,
244
282
  onBlur: handleFocus,
245
283
  "aria-label": deprecatedAriaLabel || ariaLabel,
246
- size: size,
284
+ size: size$1,
247
285
  className: className,
248
286
  invalid: invalid,
249
287
  invalidText: invalidText,
@@ -251,6 +289,7 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
251
289
  warnText: warnText,
252
290
  light: light,
253
291
  isOpen: isOpen,
292
+ ref: refs.setReference,
254
293
  id: id
255
294
  }, invalid && /*#__PURE__*/React__default.createElement(WarningFilled, {
256
295
  className: `${prefix}--list-box__invalid-icon`
@@ -273,7 +312,9 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
273
312
  }, selectedItem ? renderSelectedItem ? renderSelectedItem(selectedItem) : itemToString(selectedItem) : label), /*#__PURE__*/React__default.createElement(ListBox.MenuIcon, {
274
313
  isOpen: isOpen,
275
314
  translateWithId: translateWithId
276
- })), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, menuProps, isOpen && items.map((item, index) => {
315
+ })), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, _extends({}, menuProps, {
316
+ ref: menuRef
317
+ }), isOpen && items.map((item, index) => {
277
318
  const isObject = item !== null && typeof item === 'object';
278
319
  const itemProps = getItemProps({
279
320
  item,
@@ -308,6 +349,10 @@ Dropdown.propTypes = {
308
349
  * Specify a label to be read by screen readers on the container note.
309
350
  */
310
351
  ariaLabel: deprecate(PropTypes.string, 'This prop syntax has been deprecated. Please use the new `aria-label`.'),
352
+ /**
353
+ * **Experimental**: Will attempt to automatically align the floating element to avoid collisions with the viewport and being clipped by ancestor elements.
354
+ */
355
+ autoAlign: PropTypes.bool,
311
356
  /**
312
357
  * Provide a custom className to be applied on the cds--dropdown node
313
358
  */
@@ -15,6 +15,7 @@ import Button from '../Button/Button.js';
15
15
  import '../Button/Button.Skeleton.js';
16
16
  import ButtonSet from '../ButtonSet/ButtonSet.js';
17
17
  import InlineLoading from '../InlineLoading/InlineLoading.js';
18
+ import { Layer } from '../Layer/index.js';
18
19
  import requiredIfGivenPropIsTruthy from '../../prop-types/requiredIfGivenPropIsTruthy.js';
19
20
  import wrapFocus, { wrapFocusWithoutSentinels, elementOrParentIsFloatingMenu } from '../../internal/wrapFocus.js';
20
21
  import debounce from 'lodash.debounce';
@@ -266,7 +267,7 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
266
267
  as: "h3",
267
268
  id: modalHeadingId,
268
269
  className: `${prefix}--modal-header__heading`
269
- }, modalHeading), normalizedSlug, !passiveModal && modalButton), /*#__PURE__*/React__default.createElement("div", _extends({
270
+ }, modalHeading), normalizedSlug, !passiveModal && modalButton), /*#__PURE__*/React__default.createElement(Layer, _extends({
270
271
  ref: contentRef,
271
272
  id: modalBodyId,
272
273
  className: contentClasses
@@ -301,7 +302,8 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
301
302
  className: `${prefix}--inline-loading--btn`,
302
303
  onSuccess: onLoadingSuccess
303
304
  }))));
304
- return /*#__PURE__*/React__default.createElement("div", _extends({}, rest, {
305
+ return /*#__PURE__*/React__default.createElement(Layer, _extends({}, rest, {
306
+ level: 0,
305
307
  onKeyDown: handleKeyDown,
306
308
  onMouseDown: handleMousedown,
307
309
  onBlur: !focusTrapWithoutSentinels ? handleBlur : () => {},
@@ -6,8 +6,8 @@
6
6
  */
7
7
  import { type UseComboboxProps, type UseMultipleSelectionProps } from 'downshift';
8
8
  import { ReactNode, FunctionComponent, ReactElement } from 'react';
9
- import { type ItemBase, type SortingPropTypes } from './MultiSelectPropTypes';
10
- export interface FilterableMultiSelectProps<Item extends ItemBase> extends SortingPropTypes<Item> {
9
+ import { type MultiSelectSortingProps } from './MultiSelectPropTypes';
10
+ export interface FilterableMultiSelectProps<ItemType> extends MultiSelectSortingProps<ItemType> {
11
11
  /**
12
12
  * Specify a label to be read by screen readers on the container node
13
13
  * @deprecated
@@ -15,6 +15,12 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
15
15
  'aria-label'?: string;
16
16
  /** @deprecated */
17
17
  ariaLabel?: string;
18
+ /**
19
+ * **Experimental**: Will attempt to automatically align the floating
20
+ * element to avoid collisions with the viewport and being clipped by
21
+ * ancestor elements.
22
+ */
23
+ autoAlign?: boolean;
18
24
  className?: string;
19
25
  /**
20
26
  * Specify the text that should be read for screen readers that describes total items selected
@@ -35,14 +41,14 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
35
41
  /**
36
42
  * Additional props passed to Downshift
37
43
  */
38
- downshiftProps?: UseMultipleSelectionProps<Item>;
44
+ downshiftProps?: UseMultipleSelectionProps<ItemType>;
39
45
  /**
40
46
  * Default sorter is assigned if not provided.
41
47
  */
42
- filterItems(items: readonly Item[], extra: {
48
+ filterItems(items: readonly ItemType[], extra: {
43
49
  inputValue: string | null;
44
- itemToString: NonNullable<UseMultipleSelectionProps<Item>['itemToString']>;
45
- }): Item[];
50
+ itemToString: NonNullable<UseMultipleSelectionProps<ItemType>['itemToString']>;
51
+ }): ItemType[];
46
52
  /**
47
53
  * Specify whether the title text should be hidden or not
48
54
  */
@@ -60,7 +66,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
60
66
  * Allow users to pass in arbitrary items from their collection that are
61
67
  * pre-selected
62
68
  */
63
- initialSelectedItems?: Item[];
69
+ initialSelectedItems?: ItemType[];
64
70
  /**
65
71
  * Is the current selection invalid?
66
72
  */
@@ -73,7 +79,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
73
79
  * Function to render items as custom components instead of strings.
74
80
  * Defaults to null and is overridden by a getter
75
81
  */
76
- itemToElement?: FunctionComponent<Item>;
82
+ itemToElement?: FunctionComponent<ItemType>;
77
83
  /**
78
84
  * Helper function passed to downshift that allows the library to render
79
85
  * a given item to a string label.
@@ -81,12 +87,12 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
81
87
  * By default, it extracts the `label` field from a given item
82
88
  * to serve as the item label in the list.
83
89
  */
84
- itemToString?(item: Item | null): string;
90
+ itemToString?(item: ItemType | null): string;
85
91
  /**
86
92
  * We try to stay as generic as possible here to allow individuals to pass
87
93
  * in a collection of whatever kind of data structure they prefer
88
94
  */
89
- items: Item[];
95
+ items: ItemType[];
90
96
  /**
91
97
  * @deprecated `true` to use the light version.
92
98
  */
@@ -102,13 +108,13 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
102
108
  * consuming component what kind of internal state changes are occurring.
103
109
  */
104
110
  onChange?(changes: {
105
- selectedItems: Item[];
111
+ selectedItems: ItemType[];
106
112
  }): void;
107
113
  /**
108
114
  * A utility for this controlled component
109
115
  * to communicate to the currently typed input.
110
116
  */
111
- onInputValueChange?: UseComboboxProps<Item>['onInputValueChange'];
117
+ onInputValueChange?: UseComboboxProps<ItemType>['onInputValueChange'];
112
118
  /**
113
119
  * `onMenuChange` is a utility for this controlled component to communicate to a
114
120
  * consuming component that the menu was opened(`true`)/closed(`false`).
@@ -133,7 +139,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
133
139
  /**
134
140
  * For full control of the selected items
135
141
  */
136
- selectedItems?: Item[];
142
+ selectedItems?: ItemType[];
137
143
  /**
138
144
  * Specify the size of the ListBox.
139
145
  * Currently, supports either `sm`, `md` or `lg` as an option.
@@ -167,7 +173,7 @@ export interface FilterableMultiSelectProps<Item extends ItemBase> extends Sorti
167
173
  warnText?: ReactNode;
168
174
  }
169
175
  declare const FilterableMultiSelect: {
170
- <Item extends ItemBase>(props: FilterableMultiSelectProps<Item>): ReactElement;
176
+ <ItemType>(props: FilterableMultiSelectProps<ItemType>): ReactElement;
171
177
  propTypes?: any;
172
178
  contextTypes?: any;
173
179
  defaultProps?: any;
@@ -11,7 +11,7 @@ import cx from 'classnames';
11
11
  import Downshift, { useCombobox, useMultipleSelection } from 'downshift';
12
12
  import isEqual from 'lodash.isequal';
13
13
  import PropTypes from 'prop-types';
14
- import React__default, { useContext, useState, useRef, useEffect } from 'react';
14
+ import React__default, { useContext, useState, useLayoutEffect, 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';
@@ -24,6 +24,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
24
24
  import '../FluidForm/FluidForm.js';
25
25
  import { FormContext } from '../FluidForm/FormContext.js';
26
26
  import { useSelection } from '../../internal/Selection.js';
27
+ import { useFloating, flip, size, autoUpdate } from '@floating-ui/react';
27
28
  import { match } from '../../internal/keyboard/match.js';
28
29
  import ListBoxSelection from '../ListBox/next/ListBoxSelection.js';
29
30
  import ListBoxTrigger from '../ListBox/next/ListBoxTrigger.js';
@@ -53,6 +54,7 @@ const {
53
54
  } = useMultipleSelection.stateChangeTypes;
54
55
  const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function FilterableMultiSelect(_ref, ref) {
55
56
  let {
57
+ autoAlign = false,
56
58
  className: containerClassName,
57
59
  clearSelectionDescription = 'Total items selected: ',
58
60
  clearSelectionText = 'To clear selection, press Delete or Backspace',
@@ -82,7 +84,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
82
84
  type,
83
85
  selectionFeedback = 'top-after-reopen',
84
86
  selectedItems: selected,
85
- size,
87
+ size: size$1,
86
88
  sortItems = defaultSortItems,
87
89
  translateWithId,
88
90
  useTitleInItem,
@@ -109,6 +111,42 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
109
111
  onChange,
110
112
  selectedItems: selected
111
113
  });
114
+ const {
115
+ refs,
116
+ floatingStyles,
117
+ middlewareData
118
+ } = useFloating(autoAlign ? {
119
+ placement: direction,
120
+ // The floating element is positioned relative to its nearest
121
+ // containing block (usually the viewport). It will in many cases also
122
+ // “break” the floating element out of a clipping ancestor.
123
+ // https://floating-ui.com/docs/misc#clipping
124
+ strategy: 'fixed',
125
+ // Middleware order matters, arrow should be last
126
+ middleware: [flip({
127
+ crossAxis: false
128
+ }), size({
129
+ apply(_ref2) {
130
+ let {
131
+ rects,
132
+ elements
133
+ } = _ref2;
134
+ Object.assign(elements.floating.style, {
135
+ width: `${rects.reference.width}px`
136
+ });
137
+ }
138
+ })],
139
+ whileElementsMounted: autoUpdate
140
+ } : {});
141
+ useLayoutEffect(() => {
142
+ if (autoAlign) {
143
+ Object.keys(floatingStyles).forEach(style => {
144
+ if (refs.floating.current) {
145
+ refs.floating.current.style[style] = floatingStyles[style];
146
+ }
147
+ });
148
+ }
149
+ }, [autoAlign, floatingStyles, refs.floating, middlewareData, open]);
112
150
  const textInput = useRef(null);
113
151
  const filterableMultiSelectInstanceId = useId();
114
152
  const prefix = usePrefix();
@@ -116,6 +154,8 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
116
154
  setIsOpen(open);
117
155
  setPrevOpen(open);
118
156
  }
157
+
158
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
119
159
  const sortedItems = sortItems(filterItems(items, {
120
160
  itemToString,
121
161
  inputValue
@@ -453,9 +493,10 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
453
493
  warn: warn,
454
494
  warnText: warnText,
455
495
  isOpen: isOpen,
456
- size: size
496
+ size: size$1
457
497
  }, /*#__PURE__*/React__default.createElement("div", {
458
- className: `${prefix}--list-box__field`
498
+ className: `${prefix}--list-box__field`,
499
+ ref: refs.setReference
459
500
  }, controlledSelectedItems.length > 0 &&
460
501
  /*#__PURE__*/
461
502
  // @ts-expect-error: It is expecting a non-required prop called: "onClearSelection"
@@ -496,7 +537,9 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
496
537
  // @ts-expect-error
497
538
  isOpen: isOpen,
498
539
  translateWithId: translateWithId
499
- }))), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, menuProps, isOpen ? sortedItems.map((item, index) => {
540
+ }))), normalizedSlug, /*#__PURE__*/React__default.createElement(ListBox.Menu, _extends({}, menuProps, {
541
+ ref: refs.setFloating
542
+ }), isOpen ? sortedItems.map((item, index) => {
500
543
  const isChecked = controlledSelectedItems.filter(selected => isEqual(selected, item)).length > 0;
501
544
  const itemProps = getItemProps({
502
545
  item,
@@ -544,6 +587,12 @@ FilterableMultiSelect.propTypes = {
544
587
  * Specify a label to be read by screen readers on the container note.
545
588
  */
546
589
  ariaLabel: deprecate(PropTypes.string, 'ariaLabel / aria-label props are no longer required for FilterableMultiSelect'),
590
+ /**
591
+ * **Experimental**: Will attempt to automatically align the floating
592
+ * element to avoid collisions with the viewport and being clipped by
593
+ * ancestor elements.
594
+ */
595
+ autoAlign: PropTypes.bool,
547
596
  /**
548
597
  * Specify the text that should be read for screen readers that describes total items selected
549
598
  */
@@ -7,50 +7,19 @@
7
7
  import { UseSelectProps } from 'downshift';
8
8
  import React, { ReactNode } from 'react';
9
9
  import { ListBoxSize, ListBoxType } from '../ListBox';
10
+ import { MultiSelectSortingProps } from './MultiSelectPropTypes';
10
11
  import { ListBoxProps } from '../ListBox/ListBox';
11
12
  import type { InternationalProps } from '../../types/common';
12
- interface SharedOptions {
13
- locale: string;
14
- }
15
- interface DownshiftTypedProps<ItemType> {
16
- itemToString?(item: ItemType): string;
17
- }
18
- interface SortItemsOptions<ItemType> extends SharedOptions, DownshiftTypedProps<ItemType> {
19
- compareItems(item1: ItemType, item2: ItemType, options: SharedOptions): number;
20
- selectedItems: ItemType[];
21
- }
22
- interface MultiSelectSortingProps<ItemType> {
23
- /**
24
- * Provide a compare function that is used to determine the ordering of
25
- * options. See 'sortItems' for more control.
26
- */
27
- compareItems?(item1: ItemType, item2: ItemType, options: SharedOptions): number;
28
- /**
29
- * Provide a method that sorts all options in the control. Overriding this
30
- * prop means that you also have to handle the sort logic for selected versus
31
- * un-selected items. If you just want to control ordering, consider the
32
- * `compareItems` prop instead.
33
- *
34
- * The return value should be a number whose sign indicates the relative order
35
- * of the two elements: negative if a is less than b, positive if a is greater
36
- * than b, and zero if they are equal.
37
- *
38
- * sortItems :
39
- * (items: Array<Item>, {
40
- * selectedItems: Array<Item>,
41
- * itemToString: Item => string,
42
- * compareItems: (itemA: string, itemB: string, {
43
- * locale: string
44
- * }) => number,
45
- * locale: string,
46
- * }) => Array<Item>
47
- */
48
- sortItems?(items: ReadonlyArray<ItemType>, options: SortItemsOptions<ItemType>): ItemType[];
49
- }
50
13
  interface OnChangeData<ItemType> {
51
14
  selectedItems: ItemType[] | null;
52
15
  }
53
16
  export interface MultiSelectProps<ItemType> extends MultiSelectSortingProps<ItemType>, InternationalProps<'close.menu' | 'open.menu' | 'clear.all' | 'clear.selection'> {
17
+ /**
18
+ * **Experimental**: Will attempt to automatically align the floating
19
+ * element to avoid collisions with the viewport and being clipped by
20
+ * ancestor elements.
21
+ */
22
+ autoAlign?: boolean;
54
23
  className?: string;
55
24
  /**
56
25
  * Specify the text that should be read for screen readers that describes that all items are deleted
@@ -11,7 +11,7 @@ import cx from 'classnames';
11
11
  import { useSelect } from 'downshift';
12
12
  import isEqual from 'lodash.isequal';
13
13
  import PropTypes from 'prop-types';
14
- import React__default, { useContext, useRef, useState, useMemo } from 'react';
14
+ import React__default, { useContext, useRef, useState, useLayoutEffect, useMemo } from 'react';
15
15
  import ListBox from '../ListBox/index.js';
16
16
  import { sortingPropTypes } from './MultiSelectPropTypes.js';
17
17
  import { defaultSortItems, defaultCompareItems } from './tools/sorting.js';
@@ -23,6 +23,7 @@ import { usePrefix } from '../../internal/usePrefix.js';
23
23
  import '../FluidForm/FluidForm.js';
24
24
  import { FormContext } from '../FluidForm/FormContext.js';
25
25
  import { noopFn } from '../../internal/noopFn.js';
26
+ import { useFloating, flip, size, autoUpdate } from '@floating-ui/react';
26
27
  import { match } from '../../internal/keyboard/match.js';
27
28
  import { ListBoxSize } from '../ListBox/ListBoxPropTypes.js';
28
29
  import { Delete, Escape, Space, ArrowDown, Enter } from '../../internal/keyboard/keys.js';
@@ -57,6 +58,7 @@ const defaultItemToString = item => {
57
58
  };
58
59
  const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
59
60
  let {
61
+ autoAlign = false,
60
62
  className: containerClassName,
61
63
  id,
62
64
  items,
@@ -67,7 +69,7 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
67
69
  helperText,
68
70
  label,
69
71
  type = 'default',
70
- size,
72
+ size: size$1,
71
73
  disabled = false,
72
74
  initialSelectedItems = [],
73
75
  sortItems = defaultSortItems,
@@ -116,6 +118,42 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
116
118
  onChange,
117
119
  selectedItems: selected
118
120
  });
121
+ const {
122
+ refs,
123
+ floatingStyles,
124
+ middlewareData
125
+ } = useFloating(autoAlign ? {
126
+ placement: direction,
127
+ // The floating element is positioned relative to its nearest
128
+ // containing block (usually the viewport). It will in many cases also
129
+ // “break” the floating element out of a clipping ancestor.
130
+ // https://floating-ui.com/docs/misc#clipping
131
+ strategy: 'fixed',
132
+ // Middleware order matters, arrow should be last
133
+ middleware: [flip({
134
+ crossAxis: false
135
+ }), size({
136
+ apply(_ref2) {
137
+ let {
138
+ rects,
139
+ elements
140
+ } = _ref2;
141
+ Object.assign(elements.floating.style, {
142
+ width: `${rects.reference.width}px`
143
+ });
144
+ }
145
+ })],
146
+ whileElementsMounted: autoUpdate
147
+ } : {});
148
+ useLayoutEffect(() => {
149
+ if (autoAlign) {
150
+ Object.keys(floatingStyles).forEach(style => {
151
+ if (refs.floating.current) {
152
+ refs.floating.current.style[style] = floatingStyles[style];
153
+ }
154
+ });
155
+ }
156
+ }, [autoAlign, floatingStyles, refs.floating, middlewareData, open]);
119
157
 
120
158
  // Filter out items with an object having undefined values
121
159
  const filteredItems = useMemo(() => {
@@ -367,7 +405,7 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
367
405
  onFocus: isFluid ? handleFocus : undefined,
368
406
  onBlur: isFluid ? handleFocus : undefined,
369
407
  type: type,
370
- size: size,
408
+ size: size$1,
371
409
  className: className,
372
410
  disabled: disabled,
373
411
  light: light,
@@ -382,7 +420,8 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
382
420
  }), showWarning && /*#__PURE__*/React__default.createElement(WarningAltFilled, {
383
421
  className: `${prefix}--list-box__invalid-icon ${prefix}--list-box__invalid-icon--warning`
384
422
  }), /*#__PURE__*/React__default.createElement("div", {
385
- className: multiSelectFieldWrapperClasses
423
+ className: multiSelectFieldWrapperClasses,
424
+ ref: refs.setReference
386
425
  }, selectedItems.length > 0 && /*#__PURE__*/React__default.createElement(ListBox.Selection, {
387
426
  readOnly: readOnly,
388
427
  clearSelection: !disabled && !readOnly ? clearSelection : noopFn,
@@ -405,7 +444,9 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
405
444
  }, label), /*#__PURE__*/React__default.createElement(ListBox.MenuIcon, {
406
445
  isOpen: isOpen,
407
446
  translateWithId: translateWithId
408
- })), normalizedSlug), /*#__PURE__*/React__default.createElement(ListBox.Menu, getMenuProps(), isOpen &&
447
+ })), normalizedSlug), /*#__PURE__*/React__default.createElement(ListBox.Menu, getMenuProps({
448
+ ref: refs.setFloating
449
+ }), isOpen &&
409
450
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
410
451
  sortItems(filteredItems, sortOptions).map((item, index) => {
411
452
  const isChecked = selectedItems.filter(selected => isEqual(selected, item)).length > 0;
@@ -444,6 +485,12 @@ const MultiSelect = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
444
485
  MultiSelect.displayName = 'MultiSelect';
445
486
  MultiSelect.propTypes = {
446
487
  ...sortingPropTypes,
488
+ /**
489
+ * **Experimental**: Will attempt to automatically align the floating
490
+ * element to avoid collisions with the viewport and being clipped by
491
+ * ancestor elements.
492
+ */
493
+ autoAlign: PropTypes.bool,
447
494
  /**
448
495
  * Provide a custom class name to be added to the outermost node in the
449
496
  * component