@carbon/react 1.63.1 → 1.64.0-rc.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 (114) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1558 -1265
  2. package/es/components/{Slug → AILabel}/index.js +47 -35
  3. package/es/components/{AiSkeleton/AiSkeletonIcon.d.ts → AISkeleton/AISkeletonIcon.d.ts} +4 -4
  4. package/es/components/{AiSkeleton/AiSkeletonIcon.js → AISkeleton/AISkeletonIcon.js} +5 -5
  5. package/es/components/{AiSkeleton/AiSkeletonPlaceholder.d.ts → AISkeleton/AISkeletonPlaceholder.d.ts} +4 -4
  6. package/es/components/{AiSkeleton/AiSkeletonPlaceholder.js → AISkeleton/AISkeletonPlaceholder.js} +5 -5
  7. package/es/components/{AiSkeleton/AiSkeletonText.d.ts → AISkeleton/AISkeletonText.d.ts} +4 -4
  8. package/es/components/{AiSkeleton/AiSkeletonText.js → AISkeleton/AISkeletonText.js} +3 -3
  9. package/es/components/AISkeleton/index.d.ts +10 -0
  10. package/es/components/CheckboxGroup/CheckboxGroup.js +1 -1
  11. package/es/components/ComboBox/ComboBox.d.ts +21 -6
  12. package/es/components/ComboBox/ComboBox.js +52 -14
  13. package/es/components/ComposedModal/ComposedModal.js +1 -1
  14. package/es/components/DataTable/DataTable.js +1 -1
  15. package/es/components/DataTable/TableSelectRow.js +2 -1
  16. package/es/components/DataTable/TableToolbarSearch.js +2 -2
  17. package/es/components/DatePicker/DatePicker.js +65 -14
  18. package/es/components/DatePicker/plugins/fixEventsPlugin.js +11 -0
  19. package/es/components/DatePickerInput/DatePickerInput.js +1 -1
  20. package/es/components/Dropdown/Dropdown.d.ts +6 -4
  21. package/es/components/Dropdown/Dropdown.js +18 -10
  22. package/es/components/FluidMultiSelect/FluidMultiSelect.js +10 -6
  23. package/es/components/Menu/MenuItem.js +45 -63
  24. package/es/components/Modal/Modal.js +1 -1
  25. package/es/components/MultiSelect/FilterableMultiSelect.d.ts +6 -4
  26. package/es/components/MultiSelect/FilterableMultiSelect.js +50 -27
  27. package/es/components/MultiSelect/MultiSelect.d.ts +6 -4
  28. package/es/components/MultiSelect/MultiSelect.js +12 -8
  29. package/es/components/NumberInput/NumberInput.js +2 -2
  30. package/es/components/OverflowMenu/OverflowMenu.js +2 -1
  31. package/es/components/OverflowMenu/next/index.js +2 -1
  32. package/es/components/RadioButtonGroup/RadioButtonGroup.js +1 -1
  33. package/es/components/RadioTile/RadioTile.js +1 -1
  34. package/es/components/Select/Select.js +1 -1
  35. package/es/components/Tabs/Tabs.js +3 -3
  36. package/es/components/Tag/DismissibleTag.js +1 -1
  37. package/es/components/Tag/OperationalTag.js +1 -1
  38. package/es/components/Tag/SelectableTag.js +3 -1
  39. package/es/components/Tag/Tag.js +1 -1
  40. package/es/components/TextArea/TextArea.js +1 -1
  41. package/es/components/TextInput/PasswordInput.js +5 -0
  42. package/es/components/TextInput/TextInput.js +1 -1
  43. package/es/components/Tile/Tile.js +2 -2
  44. package/es/components/TreeView/TreeNode.d.ts +150 -0
  45. package/es/components/TreeView/TreeNode.js +22 -15
  46. package/es/components/TreeView/TreeView.d.ts +59 -0
  47. package/es/components/TreeView/TreeView.js +37 -23
  48. package/es/components/TreeView/index.d.ts +11 -0
  49. package/es/components/UIShell/SwitcherItem.d.ts +4 -0
  50. package/es/components/UIShell/SwitcherItem.js +7 -1
  51. package/es/index.d.ts +4 -2
  52. package/es/index.js +6 -6
  53. package/es/node_modules/@floating-ui/core/dist/floating-ui.core.mjs.js +143 -0
  54. package/es/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs.js +39 -0
  55. package/lib/components/{Slug → AILabel}/index.js +49 -37
  56. package/lib/components/{AiSkeleton/AiSkeletonIcon.d.ts → AISkeleton/AISkeletonIcon.d.ts} +4 -4
  57. package/lib/components/{AiSkeleton/AiSkeletonIcon.js → AISkeleton/AISkeletonIcon.js} +5 -5
  58. package/lib/components/{AiSkeleton/AiSkeletonPlaceholder.d.ts → AISkeleton/AISkeletonPlaceholder.d.ts} +4 -4
  59. package/lib/components/{AiSkeleton/AiSkeletonPlaceholder.js → AISkeleton/AISkeletonPlaceholder.js} +5 -5
  60. package/lib/components/{AiSkeleton/AiSkeletonText.d.ts → AISkeleton/AISkeletonText.d.ts} +4 -4
  61. package/lib/components/{AiSkeleton/AiSkeletonText.js → AISkeleton/AISkeletonText.js} +3 -3
  62. package/lib/components/AISkeleton/index.d.ts +10 -0
  63. package/lib/components/CheckboxGroup/CheckboxGroup.js +1 -1
  64. package/lib/components/ComboBox/ComboBox.d.ts +21 -6
  65. package/lib/components/ComboBox/ComboBox.js +52 -14
  66. package/lib/components/ComposedModal/ComposedModal.js +1 -1
  67. package/lib/components/DataTable/DataTable.js +2 -2
  68. package/lib/components/DataTable/TableSelectRow.js +2 -1
  69. package/lib/components/DataTable/TableToolbarSearch.js +2 -2
  70. package/lib/components/DatePicker/DatePicker.js +65 -14
  71. package/lib/components/DatePicker/plugins/fixEventsPlugin.js +11 -0
  72. package/lib/components/DatePickerInput/DatePickerInput.js +1 -1
  73. package/lib/components/Dropdown/Dropdown.d.ts +6 -4
  74. package/lib/components/Dropdown/Dropdown.js +18 -10
  75. package/lib/components/FluidMultiSelect/FluidMultiSelect.js +10 -6
  76. package/lib/components/Menu/MenuItem.js +44 -62
  77. package/lib/components/Modal/Modal.js +1 -1
  78. package/lib/components/MultiSelect/FilterableMultiSelect.d.ts +6 -4
  79. package/lib/components/MultiSelect/FilterableMultiSelect.js +49 -26
  80. package/lib/components/MultiSelect/MultiSelect.d.ts +6 -4
  81. package/lib/components/MultiSelect/MultiSelect.js +12 -8
  82. package/lib/components/NumberInput/NumberInput.js +2 -2
  83. package/lib/components/OverflowMenu/OverflowMenu.js +2 -1
  84. package/lib/components/OverflowMenu/next/index.js +2 -1
  85. package/lib/components/RadioButtonGroup/RadioButtonGroup.js +1 -1
  86. package/lib/components/RadioTile/RadioTile.js +1 -1
  87. package/lib/components/Select/Select.js +1 -1
  88. package/lib/components/Tabs/Tabs.js +2 -2
  89. package/lib/components/Tag/DismissibleTag.js +1 -1
  90. package/lib/components/Tag/OperationalTag.js +1 -1
  91. package/lib/components/Tag/SelectableTag.js +3 -1
  92. package/lib/components/Tag/Tag.js +1 -1
  93. package/lib/components/TextArea/TextArea.js +1 -1
  94. package/lib/components/TextInput/PasswordInput.js +5 -0
  95. package/lib/components/TextInput/TextInput.js +1 -1
  96. package/lib/components/Tile/Tile.js +2 -2
  97. package/lib/components/TreeView/TreeNode.d.ts +150 -0
  98. package/lib/components/TreeView/TreeNode.js +24 -17
  99. package/lib/components/TreeView/TreeView.d.ts +59 -0
  100. package/lib/components/TreeView/TreeView.js +39 -25
  101. package/lib/components/TreeView/index.d.ts +11 -0
  102. package/lib/components/UIShell/SwitcherItem.d.ts +4 -0
  103. package/lib/components/UIShell/SwitcherItem.js +7 -1
  104. package/lib/index.d.ts +4 -2
  105. package/lib/index.js +20 -14
  106. package/lib/node_modules/@floating-ui/core/dist/floating-ui.core.mjs.js +148 -0
  107. package/lib/node_modules/@floating-ui/utils/dist/floating-ui.utils.mjs.js +47 -0
  108. package/package.json +4 -4
  109. package/scss/components/ai-label/_ai-label.scss +9 -0
  110. package/scss/components/ai-label/_index.scss +9 -0
  111. package/es/components/AiSkeleton/index.d.ts +0 -10
  112. package/es/components/DataTable/tools/instanceId.js +0 -20
  113. package/lib/components/AiSkeleton/index.d.ts +0 -10
  114. package/lib/components/DataTable/tools/instanceId.js +0 -24
@@ -173,25 +173,42 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
173
173
  }
174
174
  }, []);
175
175
  const lastStartValue = useRef('');
176
+ const [calendarCloseEvent, setCalendarCloseEvent] = useState(null);
176
177
 
177
178
  // fix datepicker deleting the selectedDate when the calendar closes
178
- const onCalendarClose = (selectedDates, dateStr) => {
179
- endInputField?.current?.focus();
180
- calendarRef?.current?.calendarContainer?.classList.remove('open');
181
- setTimeout(() => {
182
- if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
183
- startInputField.current.value = lastStartValue.current;
184
- calendarRef.current.setDate([startInputField.current.value, endInputField?.current?.value], true, calendarRef.current.config.dateFormat);
185
- }
186
- if (onClose) {
187
- onClose(calendarRef.current.selectedDates, dateStr, calendarRef.current);
188
- }
179
+ const handleCalendarClose = useCallback((selectedDates, dateStr, instance) => {
180
+ if (lastStartValue.current && selectedDates[0] && !startInputField.current.value) {
181
+ startInputField.current.value = lastStartValue.current;
182
+ calendarRef.current.setDate([startInputField.current.value, endInputField?.current?.value], true, calendarRef.current.config.dateFormat);
183
+ }
184
+ if (onClose) {
185
+ onClose(selectedDates, dateStr, instance);
186
+ }
187
+ }, [onClose]);
188
+ const onCalendarClose = (selectedDates, dateStr, instance, e) => {
189
+ if (e && e.type === 'clickOutside') {
190
+ return;
191
+ }
192
+ setCalendarCloseEvent({
193
+ selectedDates,
194
+ dateStr,
195
+ instance
189
196
  });
190
197
  };
198
+ useEffect(() => {
199
+ if (calendarCloseEvent) {
200
+ const {
201
+ selectedDates,
202
+ dateStr,
203
+ instance
204
+ } = calendarCloseEvent;
205
+ handleCalendarClose(selectedDates, dateStr, instance);
206
+ setCalendarCloseEvent(null);
207
+ }
208
+ }, [calendarCloseEvent, handleCalendarClose]);
191
209
  const endInputField = useRef(null);
192
210
  const calendarRef = useRef(null);
193
211
  const savedOnChange = useSavedCallback(onChange);
194
- const savedOnClose = useSavedCallback(datePickerType === 'range' ? onCalendarClose : onClose);
195
212
  const savedOnOpen = useSavedCallback(onOpen);
196
213
  const datePickerClasses = cx(`${prefix}--date-picker`, {
197
214
  [`${prefix}--date-picker--short`]: short,
@@ -309,6 +326,7 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
309
326
  } = endInputField;
310
327
  const flatpickerconfig = {
311
328
  inline: inline ?? false,
329
+ onClose: onCalendarClose,
312
330
  disableMobile: true,
313
331
  defaultDate: value,
314
332
  closeOnSelect: closeOnSelect,
@@ -343,7 +361,6 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
343
361
  savedOnChange(...arguments);
344
362
  }
345
363
  },
346
- onClose: savedOnClose,
347
364
  onReady: onHook,
348
365
  onMonthChange: onHook,
349
366
  onYearChange: onHook,
@@ -440,7 +457,7 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
440
457
  }
441
458
  };
442
459
  // eslint-disable-next-line react-hooks/exhaustive-deps
443
- }, [savedOnChange, savedOnClose, savedOnOpen, readOnly, closeOnSelect, hasInput]);
460
+ }, [savedOnChange, savedOnOpen, readOnly, closeOnSelect, hasInput]);
444
461
 
445
462
  // this hook allows consumers to access the flatpickr calendar
446
463
  // instance for cases where functions like open() or close()
@@ -487,6 +504,40 @@ const DatePicker = /*#__PURE__*/React__default.forwardRef(function DatePicker(_r
487
504
  calendarRef.current.set('inline', inline);
488
505
  }
489
506
  }, [inline]);
507
+ useEffect(() => {
508
+ //when value prop is set to empty, this clears the faltpicker's calendar instance and text input
509
+ if (value === '') {
510
+ calendarRef.current?.clear();
511
+ if (startInputField.current) {
512
+ startInputField.current.value = '';
513
+ }
514
+ if (endInputField.current) {
515
+ endInputField.current.value = '';
516
+ }
517
+ }
518
+ }, [value]);
519
+ useEffect(() => {
520
+ const handleMouseDown = event => {
521
+ if (calendarRef.current && calendarRef.current.isOpen && !calendarRef.current.calendarContainer.contains(event.target) && !startInputField.current.contains(event.target) && !endInputField.current?.contains(event.target)) {
522
+ // Close the calendar immediately on mousedown
523
+ closeCalendar();
524
+ }
525
+ };
526
+ const closeCalendar = event => {
527
+ calendarRef.current.close();
528
+ // Remove focus from endDate calendar input
529
+ if (document.activeElement instanceof HTMLElement) {
530
+ document.activeElement.blur();
531
+ }
532
+ onCalendarClose(calendarRef.current.selectedDates, '', calendarRef.current, {
533
+ type: 'clickOutside'
534
+ });
535
+ };
536
+ document.addEventListener('mousedown', handleMouseDown, true);
537
+ return () => {
538
+ document.removeEventListener('mousedown', handleMouseDown, true);
539
+ };
540
+ }, [calendarRef, startInputField, endInputField, onCalendarClose]);
490
541
  useEffect(() => {
491
542
  if (calendarRef?.current?.set) {
492
543
  if (value !== undefined) {
@@ -18,6 +18,15 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
18
18
  inputTo,
19
19
  lastStartValue
20
20
  } = config;
21
+ /**
22
+ * Handles `click` outside to close calendar
23
+ */
24
+ const handleClickOutside = event => {
25
+ if (!fp.isOpen || fp.calendarContainer.contains(event.target) || event.target === inputFrom || event.target === inputTo) {
26
+ return;
27
+ }
28
+ fp.close();
29
+ };
21
30
  /**
22
31
  * Handles `keydown` event.
23
32
  */
@@ -111,6 +120,7 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
111
120
  inputTo.removeEventListener('blur', handleBlur, true);
112
121
  }
113
122
  inputFrom.removeEventListener('keydown', handleKeydown, true);
123
+ document.removeEventListener('click', handleClickOutside, true);
114
124
  };
115
125
 
116
126
  /**
@@ -127,6 +137,7 @@ var carbonFlatpickrFixEventsPlugin = (config => fp => {
127
137
  inputTo.addEventListener('keydown', handleKeydown, true);
128
138
  inputTo.addEventListener('blur', handleBlur, true);
129
139
  }
140
+ document.addEventListener('click', handleClickOutside, true);
130
141
  };
131
142
 
132
143
  /**
@@ -97,7 +97,7 @@ const DatePickerInput = /*#__PURE__*/React__default.forwardRef(function DatePick
97
97
 
98
98
  // Slug is always size `mini`
99
99
  let normalizedSlug;
100
- if (slug && slug['type']?.displayName === 'Slug') {
100
+ if (slug && slug['type']?.displayName === 'AILabel') {
101
101
  normalizedSlug = /*#__PURE__*/React__default.cloneElement(slug, {
102
102
  size: 'mini'
103
103
  });
@@ -36,10 +36,12 @@ export interface DropdownProps<ItemType> extends Omit<ReactAttr<HTMLDivElement>,
36
36
  */
37
37
  disabled?: boolean;
38
38
  /**
39
- * Additional props passed to Downshift. Use with caution: anything you define
40
- * here overrides the components' internal handling of that prop. Downshift
41
- * internals are subject to change, and in some cases they can not be shimmed
42
- * to shield you from potentially breaking changes.
39
+ * Additional props passed to Downshift.
40
+ *
41
+ * **Use with caution:** anything you define here overrides the components'
42
+ * internal handling of that prop. Downshift APIs and internals are subject to
43
+ * change, and in some cases they can not be shimmed by Carbon to shield you
44
+ * from potentially breaking changes.
43
45
  */
44
46
  downshiftProps?: Partial<UseSelectProps<ItemType>>;
45
47
  /**
@@ -19,6 +19,7 @@ import '../FluidForm/FluidForm.js';
19
19
  import { FormContext } from '../FluidForm/FormContext.js';
20
20
  import { useId } from '../../internal/useId.js';
21
21
  import { useFloating, size, flip, autoUpdate } from '@floating-ui/react';
22
+ import { hide } from '../../node_modules/@floating-ui/core/dist/floating-ui.core.mjs.js';
22
23
  import { ListBoxSize, ListBoxType } from '../ListBox/ListBoxPropTypes.js';
23
24
 
24
25
  const {
@@ -72,7 +73,8 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
72
73
  } = _ref;
73
74
  const {
74
75
  refs,
75
- floatingStyles
76
+ floatingStyles,
77
+ middlewareData
76
78
  } = useFloating(autoAlign ? {
77
79
  placement: direction,
78
80
  // The floating element is positioned relative to its nearest
@@ -91,15 +93,19 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
91
93
  width: `${rects.reference.width}px`
92
94
  });
93
95
  }
94
- }), flip()],
96
+ }), flip(), hide()],
95
97
  whileElementsMounted: autoUpdate
96
98
  } : {} // When autoAlign is turned off, floating-ui will not be used
97
99
  );
98
100
  useEffect(() => {
99
101
  if (autoAlign) {
100
- Object.keys(floatingStyles).forEach(style => {
102
+ const updatedFloatingStyles = {
103
+ ...floatingStyles,
104
+ visibility: middlewareData.hide?.referenceHidden ? 'hidden' : 'visible'
105
+ };
106
+ Object.keys(updatedFloatingStyles).forEach(style => {
101
107
  if (refs.floating.current) {
102
- refs.floating.current.style[style] = floatingStyles[style];
108
+ refs.floating.current.style[style] = updatedFloatingStyles[style];
103
109
  }
104
110
  });
105
111
  }
@@ -260,11 +266,11 @@ const Dropdown = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
260
266
  };
261
267
  const menuProps = useMemo(() => getMenuProps({
262
268
  ref: autoAlign ? refs.setFloating : null
263
- }), [autoAlign]);
269
+ }), [autoAlign, getMenuProps, refs.setFloating]);
264
270
 
265
271
  // Slug is always size `mini`
266
272
  let normalizedSlug;
267
- if (slug && slug['type']?.displayName === 'Slug') {
273
+ if (slug && slug['type']?.displayName === 'AILabel') {
268
274
  normalizedSlug = /*#__PURE__*/React__default.cloneElement(slug, {
269
275
  size: 'mini'
270
276
  });
@@ -360,10 +366,12 @@ Dropdown.propTypes = {
360
366
  */
361
367
  disabled: PropTypes.bool,
362
368
  /**
363
- * Additional props passed to Downshift. Use with caution: anything you define
364
- * here overrides the components' internal handling of that prop. Downshift
365
- * internals are subject to change, and in some cases they can not be shimmed
366
- * to shield you from potentially breaking changes.
369
+ * Additional props passed to Downshift.
370
+ *
371
+ * **Use with caution:** anything you define here overrides the components'
372
+ * internal handling of that prop. Downshift APIs and internals are subject to
373
+ * change, and in some cases they can not be shimmed by Carbon to shield you
374
+ * from potentially breaking changes.
367
375
  */
368
376
  downshiftProps: PropTypes.object,
369
377
  /**
@@ -53,7 +53,8 @@ FluidMultiSelect.propTypes = {
53
53
  clearSelectionText: PropTypes.string,
54
54
  /**
55
55
  * Provide a compare function that is used to determine the ordering of
56
- * options. See 'sortItems' for more control.
56
+ * options. See 'sortItems' for more control. Consider declaring function
57
+ * with `useCallback` to prevent unnecessary re-renders.
57
58
  */
58
59
  compareItems: PropTypes.func,
59
60
  /**
@@ -65,10 +66,12 @@ FluidMultiSelect.propTypes = {
65
66
  */
66
67
  disabled: PropTypes.bool,
67
68
  /**
68
- * Additional props passed to Downshift. Use with caution: anything you define
69
- * here overrides the components' internal handling of that prop. Downshift
70
- * internals are subject to change, and in some cases they can not be shimmed
71
- * to shield you from potentially breaking changes.
69
+ * Additional props passed to Downshift.
70
+ *
71
+ * **Use with caution:** anything you define here overrides the components'
72
+ * internal handling of that prop. Downshift APIs and internals are subject to
73
+ * change, and in some cases they can not be shimmed by Carbon to shield you
74
+ * from potentially breaking changes.
72
75
  */
73
76
  downshiftProps: PropTypes.object,
74
77
  /**
@@ -104,7 +107,8 @@ FluidMultiSelect.propTypes = {
104
107
  /**
105
108
  * Helper function passed to downshift that allows the library to render a
106
109
  * given item to a string label. By default, it extracts the `label` field
107
- * from a given item to serve as the item label in the list.
110
+ * from a given item to serve as the item label in the list. Consider
111
+ * declaring function with `useCallback` to prevent unnecessary re-renders.
108
112
  */
109
113
  itemToString: PropTypes.func,
110
114
  /**
@@ -8,7 +8,8 @@
8
8
  import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
9
9
  import cx from 'classnames';
10
10
  import PropTypes from 'prop-types';
11
- import React__default, { forwardRef, useContext, useRef, useState, useEffect } from 'react';
11
+ import React__default, { forwardRef, useState, useContext, useRef, useEffect } from 'react';
12
+ import { useFloating, autoUpdate, offset, useInteractions, useHover, safePolygon, FloatingFocusManager } from '@floating-ui/react';
12
13
  import { CaretLeft, CaretRight, Checkmark } from '@carbon/icons-react';
13
14
  import { useControllableState } from '../../internal/useControllableState.js';
14
15
  import { useMergedRefs } from '../../internal/useMergedRefs.js';
@@ -23,9 +24,6 @@ import { match } from '../../internal/keyboard/match.js';
23
24
  import { ArrowRight, Enter, Space } from '../../internal/keyboard/keys.js';
24
25
 
25
26
  var _CaretLeft, _CaretRight;
26
- const hoverIntentDelay = 150; // in ms
27
- const leaveIntentDelay = 300; // in ms
28
-
29
27
  const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
30
28
  let {
31
29
  children,
@@ -38,19 +36,37 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
38
36
  shortcut,
39
37
  ...rest
40
38
  } = _ref;
39
+ const [submenuOpen, setSubmenuOpen] = useState(false);
40
+ const [rtl, setRtl] = useState(false);
41
+ const {
42
+ refs,
43
+ floatingStyles,
44
+ context: floatingContext
45
+ } = useFloating({
46
+ open: submenuOpen,
47
+ onOpenChange: setSubmenuOpen,
48
+ placement: rtl ? 'left-start' : 'right-start',
49
+ whileElementsMounted: autoUpdate,
50
+ middleware: [offset({
51
+ mainAxis: -6,
52
+ crossAxis: -6
53
+ })]
54
+ });
55
+ const {
56
+ getReferenceProps,
57
+ getFloatingProps
58
+ } = useInteractions([useHover(floatingContext, {
59
+ delay: 100,
60
+ enabled: true,
61
+ handleClose: safePolygon({
62
+ requireIntent: false
63
+ })
64
+ })]);
41
65
  const prefix = usePrefix();
42
66
  const context = useContext(MenuContext);
43
67
  const menuItem = useRef(null);
44
- const ref = useMergedRefs([forwardRef, menuItem]);
45
- const [boundaries, setBoundaries] = useState({
46
- x: -1,
47
- y: -1
48
- });
49
- const [rtl, setRtl] = useState(false);
68
+ const ref = useMergedRefs([forwardRef, menuItem, refs.setReference]);
50
69
  const hasChildren = Boolean(children);
51
- const [submenuOpen, setSubmenuOpen] = useState(false);
52
- const hoverIntentTimeout = useRef(null);
53
- const leaveIntentTimeout = useRef(null);
54
70
  const isDisabled = disabled && !hasChildren;
55
71
  const isDanger = kind === 'danger' && !hasChildren;
56
72
  function registerItem() {
@@ -66,31 +82,10 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
66
82
  if (!menuItem.current) {
67
83
  return;
68
84
  }
69
- const {
70
- x,
71
- y,
72
- width,
73
- height
74
- } = menuItem.current.getBoundingClientRect();
75
- if (rtl) {
76
- setBoundaries({
77
- x: [-x, x - width],
78
- y: [y, y + height]
79
- });
80
- } else {
81
- setBoundaries({
82
- x: [x, x + width],
83
- y: [y, y + height]
84
- });
85
- }
86
85
  setSubmenuOpen(true);
87
86
  }
88
87
  function closeSubmenu() {
89
88
  setSubmenuOpen(false);
90
- setBoundaries({
91
- x: -1,
92
- y: -1
93
- });
94
89
  }
95
90
  function handleClick(e) {
96
91
  if (!isDisabled) {
@@ -104,27 +99,6 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
104
99
  }
105
100
  }
106
101
  }
107
- function handleMouseEnter() {
108
- if (leaveIntentTimeout.current) {
109
- // When mouse reenters before closing keep sub menu open
110
- clearTimeout(leaveIntentTimeout.current);
111
- leaveIntentTimeout.current = null;
112
- }
113
- hoverIntentTimeout.current = setTimeout(() => {
114
- openSubmenu();
115
- }, hoverIntentDelay);
116
- }
117
- function handleMouseLeave() {
118
- if (hoverIntentTimeout.current) {
119
- clearTimeout(hoverIntentTimeout.current);
120
- // Avoid closing the sub menu as soon as mouse leaves
121
- // prevents accidental closure due to scroll bar
122
- leaveIntentTimeout.current = setTimeout(() => {
123
- closeSubmenu();
124
- menuItem.current?.focus();
125
- }, leaveIntentDelay);
126
- }
127
- }
128
102
  function handleKeyDown(e) {
129
103
  if (hasChildren && match(e, ArrowRight)) {
130
104
  openSubmenu();
@@ -169,7 +143,18 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
169
143
  });
170
144
  }
171
145
  }, [iconsAllowed, IconElement, context.state.hasIcons, context]);
172
- return /*#__PURE__*/React__default.createElement("li", _extends({
146
+ useEffect(() => {
147
+ Object.keys(floatingStyles).forEach(style => {
148
+ if (refs.floating.current && style !== 'position') {
149
+ refs.floating.current.style[style] = floatingStyles[style];
150
+ }
151
+ });
152
+ }, [floatingStyles, refs.floating]);
153
+ return /*#__PURE__*/React__default.createElement(FloatingFocusManager, {
154
+ context: floatingContext,
155
+ order: ['reference', 'floating'],
156
+ modal: false
157
+ }, /*#__PURE__*/React__default.createElement("li", _extends({
173
158
  role: "menuitem"
174
159
  }, rest, {
175
160
  ref: ref,
@@ -179,10 +164,8 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
179
164
  "aria-haspopup": hasChildren ?? undefined,
180
165
  "aria-expanded": hasChildren ? submenuOpen : undefined,
181
166
  onClick: handleClick,
182
- onMouseEnter: hasChildren ? handleMouseEnter : undefined,
183
- onMouseLeave: hasChildren ? handleMouseLeave : undefined,
184
167
  onKeyDown: handleKeyDown
185
- }), /*#__PURE__*/React__default.createElement("div", {
168
+ }, getReferenceProps()), /*#__PURE__*/React__default.createElement("div", {
186
169
  className: `${prefix}--menu-item__icon`
187
170
  }, iconsAllowed && IconElement && /*#__PURE__*/React__default.createElement(IconElement, null)), /*#__PURE__*/React__default.createElement(Text, {
188
171
  as: "div",
@@ -192,16 +175,15 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem(_ref, forwardRef) {
192
175
  className: `${prefix}--menu-item__shortcut`
193
176
  }, shortcut), hasChildren && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("div", {
194
177
  className: `${prefix}--menu-item__shortcut`
195
- }, rtl ? _CaretLeft || (_CaretLeft = /*#__PURE__*/React__default.createElement(CaretLeft, null)) : _CaretRight || (_CaretRight = /*#__PURE__*/React__default.createElement(CaretRight, null))), /*#__PURE__*/React__default.createElement(Menu, {
178
+ }, rtl ? _CaretLeft || (_CaretLeft = /*#__PURE__*/React__default.createElement(CaretLeft, null)) : _CaretRight || (_CaretRight = /*#__PURE__*/React__default.createElement(CaretRight, null))), /*#__PURE__*/React__default.createElement(Menu, _extends({
196
179
  label: label,
197
180
  open: submenuOpen,
198
181
  onClose: () => {
199
182
  closeSubmenu();
200
183
  menuItem.current?.focus();
201
184
  },
202
- x: boundaries.x,
203
- y: boundaries.y
204
- }, children)));
185
+ ref: refs.setFloating
186
+ }, getFloatingProps()), children))));
205
187
  });
206
188
  MenuItem.propTypes = {
207
189
  /**
@@ -229,7 +229,7 @@ const Modal = /*#__PURE__*/React__default.forwardRef(function Modal(_ref, ref) {
229
229
 
230
230
  // Slug is always size `sm`
231
231
  let normalizedSlug;
232
- if (slug && slug['type']?.displayName === 'Slug') {
232
+ if (slug && slug['type']?.displayName === 'AILabel') {
233
233
  normalizedSlug = /*#__PURE__*/React__default.cloneElement(slug, {
234
234
  size: 'sm'
235
235
  });
@@ -48,10 +48,12 @@ export interface FilterableMultiSelectProps<ItemType> extends MultiSelectSorting
48
48
  */
49
49
  disabled?: boolean;
50
50
  /**
51
- * Additional props passed to Downshift. Use with caution: anything you define
52
- * here overrides the components' internal handling of that prop. Downshift
53
- * internals are subject to change, and in some cases they can not be shimmed
54
- * to shield you from potentially breaking changes.
51
+ * Additional props passed to Downshift.
52
+ *
53
+ * **Use with caution:** anything you define here overrides the components'
54
+ * internal handling of that prop. Downshift APIs and internals are subject to
55
+ * change, and in some cases they can not be shimmed by Carbon to shield you
56
+ * from potentially breaking changes.
55
57
  */
56
58
  downshiftProps?: UseMultipleSelectionProps<ItemType>;
57
59
  /**
@@ -11,7 +11,7 @@ import cx from 'classnames';
11
11
  import Downshift, { useCombobox, useMultipleSelection } from 'downshift';
12
12
  import isEqual from 'react-fast-compare';
13
13
  import PropTypes from 'prop-types';
14
- import React__default, { useContext, useState, useLayoutEffect, useRef, useEffect, useMemo } from 'react';
14
+ import React__default, { useContext, useState, useLayoutEffect, useRef, useMemo, 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';
@@ -164,20 +164,23 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
164
164
  setPrevOpen(open);
165
165
  }
166
166
 
167
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
168
- const sortedItems = sortItems(filterItems(items, {
169
- itemToString,
170
- inputValue
171
- }), {
172
- selectedItems: {
173
- top: controlledSelectedItems,
174
- fixed: [],
175
- 'top-after-reopen': topItems
176
- }[selectionFeedback],
177
- itemToString,
178
- compareItems,
179
- locale
180
- });
167
+ // memoize sorted items to reduce unnecessary expensive sort on rerender
168
+ const sortedItems = useMemo(() => {
169
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
170
+ return sortItems(filterItems(items, {
171
+ itemToString,
172
+ inputValue
173
+ }), {
174
+ selectedItems: {
175
+ top: controlledSelectedItems,
176
+ fixed: [],
177
+ 'top-after-reopen': topItems
178
+ }[selectionFeedback],
179
+ itemToString,
180
+ compareItems,
181
+ locale
182
+ });
183
+ }, [items, inputValue, controlledSelectedItems, topItems, selectionFeedback, itemToString, compareItems, locale]);
181
184
  const inline = type === 'inline';
182
185
  const showWarning = !invalid && warn;
183
186
  const wrapperClasses = cx(`${prefix}--multi-select__wrapper`, `${prefix}--multi-select--filterable__wrapper`, `${prefix}--list-box__wrapper`, containerClassName, {
@@ -217,9 +220,15 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
217
220
  setTopItems(controlledSelectedItems);
218
221
  }
219
222
  }, [controlledSelectedItems, isOpen, setTopItems]);
223
+ const validateHighlightFocus = () => {
224
+ if (controlledSelectedItems.length > 0) {
225
+ setHighlightedIndex(0);
226
+ }
227
+ };
220
228
  function handleMenuChange(forceIsOpen) {
221
229
  const nextIsOpen = forceIsOpen ?? !isOpen;
222
230
  setIsOpen(nextIsOpen);
231
+ validateHighlightFocus();
223
232
  if (onMenuChange) {
224
233
  onMenuChange(nextIsOpen);
225
234
  }
@@ -237,9 +246,8 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
237
246
  } = useCombobox({
238
247
  isOpen,
239
248
  items: sortedItems,
249
+ // defaultHighlightedIndex: 0, // after selection, highlight the first item.
240
250
  itemToString,
241
- defaultHighlightedIndex: 0,
242
- // after selection, highlight the first item.
243
251
  id,
244
252
  labelId,
245
253
  menuId,
@@ -284,24 +292,37 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
284
292
  return changes;
285
293
  case FunctionToggleMenu:
286
294
  case ToggleButtonClick:
295
+ validateHighlightFocus();
287
296
  if (changes.isOpen && !changes.selectedItem) {
288
297
  return {
289
- ...changes,
290
- highlightedIndex: 0
298
+ ...changes
291
299
  };
292
300
  }
293
- return changes;
301
+ return {
302
+ ...changes,
303
+ highlightedIndex: null
304
+ };
294
305
  case InputChange:
295
306
  if (onInputValueChange) {
296
307
  onInputValueChange(changes.inputValue);
297
308
  }
298
309
  setInputValue(changes.inputValue ?? '');
299
310
  setIsOpen(true);
300
- return changes;
311
+ return {
312
+ ...changes,
313
+ highlightedIndex: 0
314
+ };
301
315
  case InputClick:
316
+ validateHighlightFocus();
317
+ if (changes.isOpen && !changes.selectedItem) {
318
+ return {
319
+ ...changes
320
+ };
321
+ }
302
322
  return {
303
323
  ...changes,
304
- isOpen: false
324
+ isOpen: false,
325
+ highlightedIndex: null
305
326
  };
306
327
  case MenuMouseLeave:
307
328
  return {
@@ -385,7 +406,7 @@ const FilterableMultiSelect = /*#__PURE__*/React__default.forwardRef(function Fi
385
406
 
386
407
  // Slug is always size `mini`
387
408
  let normalizedSlug;
388
- if (slug && slug['type']?.displayName === 'Slug') {
409
+ if (slug && slug['type']?.displayName === 'AILabel') {
389
410
  normalizedSlug = /*#__PURE__*/React__default.cloneElement(slug, {
390
411
  size: 'mini'
391
412
  });
@@ -622,10 +643,12 @@ FilterableMultiSelect.propTypes = {
622
643
  */
623
644
  disabled: PropTypes.bool,
624
645
  /**
625
- * Additional props passed to Downshift. Use with caution: anything you define
626
- * here overrides the components' internal handling of that prop. Downshift
627
- * internals are subject to change, and in some cases they can not be shimmed
628
- * to shield you from potentially breaking changes.
646
+ * Additional props passed to Downshift.
647
+ *
648
+ * **Use with caution:** anything you define here overrides the components'
649
+ * internal handling of that prop. Downshift APIs and internals are subject to
650
+ * change, and in some cases they can not be shimmed by Carbon to shield you
651
+ * from potentially breaking changes.
629
652
  */
630
653
  // @ts-ignore
631
654
  downshiftProps: PropTypes.shape(Downshift.propTypes),
@@ -42,10 +42,12 @@ export interface MultiSelectProps<ItemType> extends MultiSelectSortingProps<Item
42
42
  */
43
43
  disabled?: ListBoxProps['disabled'];
44
44
  /**
45
- * Additional props passed to Downshift. Use with caution: anything you define
46
- * here overrides the components' internal handling of that prop. Downshift
47
- * internals are subject to change, and in some cases they can not be shimmed
48
- * to shield you from potentially breaking changes.
45
+ * Additional props passed to Downshift.
46
+ *
47
+ * **Use with caution:** anything you define here overrides the components'
48
+ * internal handling of that prop. Downshift APIs and internals are subject to
49
+ * change, and in some cases they can not be shimmed by Carbon to shield you
50
+ * from potentially breaking changes.
49
51
  */
50
52
  downshiftProps?: Partial<UseSelectProps<ItemType>>;
51
53
  /**