@carbon/react 1.89.0-rc.0 → 1.89.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 (63) hide show
  1. package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1113 -851
  2. package/es/components/AILabel/index.d.ts +2 -5
  3. package/es/components/AILabel/index.js +2 -42
  4. package/es/components/ComposedModal/ComposedModal.js +2 -2
  5. package/es/components/Copy/Copy.js +1 -0
  6. package/es/components/DataTable/DataTable.js +10 -3
  7. package/es/components/Dialog/Dialog.d.ts +245 -0
  8. package/es/components/Dialog/Dialog.js +593 -0
  9. package/es/components/Dialog/index.d.ts +3 -251
  10. package/es/components/Dialog/index.js +1 -609
  11. package/es/components/FeatureFlags/index.d.ts +3 -1
  12. package/es/components/FeatureFlags/index.js +5 -2
  13. package/es/components/FileUploader/FileUploader.d.ts +28 -6
  14. package/es/components/FileUploader/FileUploader.js +152 -38
  15. package/es/components/Link/Link.js +15 -1
  16. package/es/components/Menu/MenuContext.d.ts +1 -1
  17. package/es/components/Menu/MenuContext.js +11 -4
  18. package/es/components/Menu/MenuItem.js +4 -10
  19. package/es/components/Modal/Modal.js +17 -9
  20. package/es/components/MultiSelect/FilterableMultiSelect.js +1 -5
  21. package/es/components/MultiSelect/MultiSelect.js +3 -7
  22. package/es/components/Popover/index.d.ts +4 -0
  23. package/es/components/Popover/index.js +8 -2
  24. package/es/components/StructuredList/StructuredList.d.ts +16 -0
  25. package/es/components/StructuredList/StructuredList.js +23 -11
  26. package/es/components/Toggletip/index.d.ts +13 -36
  27. package/es/components/Toggletip/index.js +12 -51
  28. package/es/index.d.ts +2 -1
  29. package/es/index.js +2 -0
  30. package/es/internal/Selection.js +1 -1
  31. package/es/internal/useMergedRefs.js +4 -3
  32. package/lib/components/AILabel/index.d.ts +2 -5
  33. package/lib/components/AILabel/index.js +1 -41
  34. package/lib/components/ComposedModal/ComposedModal.js +2 -2
  35. package/lib/components/Copy/Copy.js +1 -0
  36. package/lib/components/DataTable/DataTable.js +10 -3
  37. package/lib/components/Dialog/Dialog.d.ts +245 -0
  38. package/lib/components/Dialog/Dialog.js +602 -0
  39. package/lib/components/Dialog/index.d.ts +3 -251
  40. package/lib/components/Dialog/index.js +9 -614
  41. package/lib/components/FeatureFlags/index.d.ts +3 -1
  42. package/lib/components/FeatureFlags/index.js +5 -2
  43. package/lib/components/FileUploader/FileUploader.d.ts +28 -6
  44. package/lib/components/FileUploader/FileUploader.js +151 -37
  45. package/lib/components/Link/Link.js +15 -1
  46. package/lib/components/Menu/MenuContext.d.ts +1 -1
  47. package/lib/components/Menu/MenuContext.js +11 -4
  48. package/lib/components/Menu/MenuItem.js +4 -10
  49. package/lib/components/Modal/Modal.js +24 -16
  50. package/lib/components/MultiSelect/FilterableMultiSelect.js +1 -5
  51. package/lib/components/MultiSelect/MultiSelect.js +3 -7
  52. package/lib/components/Popover/index.d.ts +4 -0
  53. package/lib/components/Popover/index.js +8 -2
  54. package/lib/components/StructuredList/StructuredList.d.ts +16 -0
  55. package/lib/components/StructuredList/StructuredList.js +23 -11
  56. package/lib/components/Toggletip/index.d.ts +13 -36
  57. package/lib/components/Toggletip/index.js +11 -50
  58. package/lib/index.d.ts +2 -1
  59. package/lib/index.js +60 -58
  60. package/lib/internal/Selection.js +1 -1
  61. package/lib/internal/useMergedRefs.js +3 -2
  62. package/package.json +15 -16
  63. package/telemetry.yml +5 -2
@@ -5,6 +5,21 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import React, { type HTMLAttributes } from 'react';
8
+ interface FileItem {
9
+ name: string;
10
+ uuid: string;
11
+ file: File;
12
+ }
13
+ export interface FileChangeData {
14
+ addedFiles: FileItem[];
15
+ removedFiles: FileItem[];
16
+ currentFiles: FileItem[];
17
+ action: 'add' | 'remove' | 'clear';
18
+ }
19
+ export interface FileDeleteData {
20
+ deletedFile: FileItem;
21
+ remainingFiles: FileItem[];
22
+ }
8
23
  export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
9
24
  /**
10
25
  * Specify the types of files that this input should be able to receive
@@ -52,20 +67,23 @@ export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
52
67
  */
53
68
  name?: string;
54
69
  /**
55
- * Provide an optional `onChange` hook that is called each time the input is
56
- * changed
70
+ * Provide an optional `onChange` hook that is called each time the input is changed.
71
+ * When 'enable-enhanced-file-uploader' feature flag is enabled:
72
+ * - Also fires for file deletions and clearFiles operations
73
+ * - Event includes enhanced file information in event.target
57
74
  */
58
- onChange?: (event: any) => void;
75
+ onChange?: (event: any, data?: FileChangeData) => void;
59
76
  /**
60
77
  * Provide an optional `onClick` hook that is called each time the
61
78
  * FileUploader is clicked
62
79
  */
63
80
  onClick?: (event: any) => void;
64
81
  /**
65
- * Provide an optional `onDelete` hook that is called when an uploaded item
66
- * is removed
82
+ * Provide an optional `onDelete` hook that is called when an uploaded item is removed.
83
+ * When 'enable-enhanced-file-uploader' feature flag is enabled:
84
+ * - Event includes deleted file information in event.target
67
85
  */
68
- onDelete?: (event: any) => void;
86
+ onDelete?: (event: any, data?: FileDeleteData) => void;
69
87
  /**
70
88
  * Specify the size of the FileUploaderButton, from a list of available
71
89
  * sizes.
@@ -77,6 +95,10 @@ export interface FileUploaderHandle {
77
95
  * Clear internal state
78
96
  */
79
97
  clearFiles: () => void;
98
+ /**
99
+ * Get current files (only available when 'enable-enhanced-file-uploader' feature flag is enabled)
100
+ */
101
+ getCurrentFiles?: () => FileItem[];
80
102
  }
81
103
  declare const FileUploader: {
82
104
  <ItemType>(props: FileUploaderProps): React.ReactElement<any>;
@@ -8,7 +8,7 @@
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, { useState, useImperativeHandle } from 'react';
11
+ import React, { useState, useCallback, useImperativeHandle } from 'react';
12
12
  import Filename from './Filename.js';
13
13
  import FileUploaderButton from './FileUploaderButton.js';
14
14
  import { ButtonKinds } from '../Button/Button.js';
@@ -17,6 +17,7 @@ import { matches } from '../../internal/keyboard/match.js';
17
17
  import { usePrefix } from '../../internal/usePrefix.js';
18
18
  import '../Text/index.js';
19
19
  import { useId } from '../../internal/useId.js';
20
+ import { useFeatureFlag } from '../FeatureFlags/index.js';
20
21
  import { Text } from '../Text/Text.js';
21
22
 
22
23
  const FileUploader = /*#__PURE__*/React.forwardRef(({
@@ -38,45 +39,149 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
38
39
  ...other
39
40
  }, ref) => {
40
41
  const fileUploaderInstanceId = useId('file-uploader');
41
- const [state, updateState] = useState({
42
- fileNames: []
43
- });
44
- const nodes = [];
45
42
  const prefix = usePrefix();
46
- const handleChange = evt => {
43
+ const enhancedFileUploaderEnabled = useFeatureFlag('enable-enhanced-file-uploader');
44
+ const [fileItems, setFileItems] = useState([]);
45
+ const [legacyFileNames, setLegacyFileNames] = useState([]);
46
+ const [fileObjects, setFileObjects] = useState(new Map());
47
+ const nodes = [];
48
+ const createFileItem = file => ({
49
+ name: file.name,
50
+ uuid: `${fileUploaderInstanceId}-${Date.now()}-${Array.from(crypto.getRandomValues(new Uint8Array(8))).map(b => b.toString(36)).join('')}`,
51
+ file
52
+ });
53
+ const handleChange = useCallback(evt => {
47
54
  evt.stopPropagation();
48
- const filenames = Array.prototype.map.call(evt.target.files, file => file.name);
49
- updateState(prevState => ({
50
- fileNames: multiple ? [...new Set([...prevState.fileNames, ...filenames])] : filenames
51
- }));
52
- if (onChange) {
53
- onChange(evt);
55
+ const newFiles = Array.from(evt.target.files);
56
+ if (enhancedFileUploaderEnabled) {
57
+ const newFileItems = newFiles.map(createFileItem);
58
+ let updatedFileItems;
59
+ if (multiple) {
60
+ const existingNames = new Set(fileItems.map(item => item.name));
61
+ const uniqueNewItems = newFileItems.filter(item => !existingNames.has(item.name));
62
+ updatedFileItems = [...fileItems, ...uniqueNewItems];
63
+ } else {
64
+ updatedFileItems = newFileItems;
65
+ }
66
+ setFileItems(updatedFileItems);
67
+ if (onChange) {
68
+ const allFiles = updatedFileItems.map(item => item.file);
69
+ const enhancedEvent = {
70
+ ...evt,
71
+ target: {
72
+ ...evt.target,
73
+ files: Object.assign(allFiles, {
74
+ item: index => allFiles[index] || null
75
+ }),
76
+ addedFiles: newFileItems,
77
+ currentFiles: updatedFileItems,
78
+ action: 'add'
79
+ }
80
+ };
81
+ onChange(enhancedEvent);
82
+ }
83
+ } else {
84
+ const filenames = newFiles.map(file => file.name);
85
+ const updatedFileNames = multiple ? [...new Set([...legacyFileNames, ...filenames])] : filenames;
86
+ setLegacyFileNames(updatedFileNames);
87
+ setFileObjects(prevMap => {
88
+ const newMap = multiple ? new Map(prevMap) : new Map();
89
+ newFiles.forEach(file => {
90
+ newMap.set(file.name, file);
91
+ });
92
+ return newMap;
93
+ });
94
+ if (onChange) {
95
+ onChange(evt);
96
+ }
54
97
  }
55
- };
56
- const handleClick = (evt, {
98
+ }, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onChange]);
99
+ const handleClick = useCallback((evt, {
57
100
  index,
58
101
  filenameStatus
59
102
  }) => {
60
103
  if (filenameStatus === 'edit') {
61
104
  evt.stopPropagation();
62
- const filteredArray = state.fileNames.filter(filename => filename !== nodes[index]?.innerText?.trim());
63
- updateState({
64
- fileNames: filteredArray
65
- });
66
- if (onDelete) {
67
- onDelete(evt);
68
- uploaderButton.current?.focus?.();
105
+ if (enhancedFileUploaderEnabled) {
106
+ const deletedItem = fileItems[index];
107
+ if (!deletedItem) return;
108
+ const remainingItems = fileItems.filter((_, i) => i !== index);
109
+ setFileItems(remainingItems);
110
+ const remainingFiles = remainingItems.map(item => item.file);
111
+ const enhancedEvent = {
112
+ ...evt,
113
+ target: {
114
+ ...evt.target,
115
+ files: Object.assign(remainingFiles, {
116
+ item: index => remainingFiles[index] || null
117
+ }),
118
+ deletedFile: deletedItem,
119
+ deletedFileName: deletedItem.name,
120
+ remainingFiles: remainingItems,
121
+ currentFiles: remainingItems,
122
+ action: 'remove'
123
+ }
124
+ };
125
+ if (onDelete) {
126
+ onDelete(enhancedEvent);
127
+ }
128
+ if (onChange) {
129
+ onChange(enhancedEvent);
130
+ }
131
+ } else {
132
+ const deletedFileName = legacyFileNames[index];
133
+ const filteredArray = legacyFileNames.filter(filename => filename !== deletedFileName);
134
+ setLegacyFileNames(filteredArray);
135
+
136
+ // Update File objects
137
+ setFileObjects(prevMap => {
138
+ const newMap = new Map(prevMap);
139
+ if (deletedFileName) {
140
+ newMap.delete(deletedFileName);
141
+ }
142
+ return newMap;
143
+ });
144
+ if (onDelete) {
145
+ onDelete(evt);
146
+ }
147
+ }
148
+ if (onClick) {
149
+ onClick(evt);
69
150
  }
70
- onClick?.(evt);
151
+ uploaderButton.current?.focus?.();
71
152
  }
72
- };
153
+ }, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick]);
73
154
  useImperativeHandle(ref, () => ({
74
155
  clearFiles() {
75
- updateState({
76
- fileNames: []
77
- });
78
- }
79
- }));
156
+ if (enhancedFileUploaderEnabled) {
157
+ const previousItems = [...fileItems];
158
+ setFileItems([]);
159
+ if (onChange && previousItems.length > 0) {
160
+ const enhancedEvent = {
161
+ target: {
162
+ files: Object.assign([], {
163
+ item: () => null
164
+ }),
165
+ clearedFiles: previousItems,
166
+ currentFiles: [],
167
+ action: 'clear'
168
+ },
169
+ preventDefault: () => {},
170
+ stopPropagation: () => {}
171
+ };
172
+ onChange(enhancedEvent);
173
+ }
174
+ } else {
175
+ setLegacyFileNames([]);
176
+ setFileObjects(new Map());
177
+ }
178
+ },
179
+ ...(enhancedFileUploaderEnabled && {
180
+ getCurrentFiles() {
181
+ return [...fileItems];
182
+ }
183
+ })
184
+ }), [enhancedFileUploaderEnabled, fileItems, onChange]);
80
185
  const uploaderButton = /*#__PURE__*/React.createRef();
81
186
  const classes = cx({
82
187
  [`${prefix}--form-item`]: true,
@@ -89,6 +194,15 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
89
194
  [`${prefix}--file__selected-file--md`]: size === 'field' || size === 'md',
90
195
  [`${prefix}--file__selected-file--sm`]: size === 'small' || size === 'sm'
91
196
  });
197
+ const displayFiles = enhancedFileUploaderEnabled ? fileItems.map((item, index) => ({
198
+ name: item.name,
199
+ key: item.uuid,
200
+ index
201
+ })) : legacyFileNames.map((name, index) => ({
202
+ name,
203
+ key: index,
204
+ index
205
+ }));
92
206
  return /*#__PURE__*/React.createElement("div", _extends({
93
207
  className: classes
94
208
  }, other), !labelTitle ? null : /*#__PURE__*/React.createElement(Text, {
@@ -112,32 +226,32 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
112
226
  "aria-describedby": fileUploaderInstanceId
113
227
  }), /*#__PURE__*/React.createElement("div", {
114
228
  className: `${prefix}--file-container`
115
- }, state.fileNames.length === 0 ? null : state.fileNames.map((name, index) => /*#__PURE__*/React.createElement("span", _extends({
116
- key: index,
229
+ }, displayFiles.length === 0 ? null : displayFiles.map(file => /*#__PURE__*/React.createElement("span", _extends({
230
+ key: file.key,
117
231
  className: selectedFileClasses,
118
232
  ref: node => {
119
- nodes[index] = node;
120
- } // eslint-disable-line
233
+ nodes[file.index] = node;
234
+ }
121
235
  }, other), /*#__PURE__*/React.createElement(Text, {
122
236
  as: "p",
123
237
  className: `${prefix}--file-filename`,
124
- id: name
125
- }, name), /*#__PURE__*/React.createElement("span", {
238
+ id: enhancedFileUploaderEnabled ? `${fileUploaderInstanceId}-file-${fileItems[file.index]?.uuid || file.index}` : `${fileUploaderInstanceId}-file-${file.index}`
239
+ }, file.name), /*#__PURE__*/React.createElement("span", {
126
240
  className: `${prefix}--file__state-container`
127
241
  }, /*#__PURE__*/React.createElement(Filename, {
128
- name: name,
242
+ name: file.name,
129
243
  iconDescription: iconDescription,
130
244
  status: filenameStatus,
131
245
  onKeyDown: evt => {
132
246
  if (matches(evt, [Enter, Space])) {
133
247
  handleClick(evt, {
134
- index,
248
+ index: file.index,
135
249
  filenameStatus
136
250
  });
137
251
  }
138
252
  },
139
253
  onClick: evt => handleClick(evt, {
140
- index,
254
+ index: file.index,
141
255
  filenameStatus
142
256
  })
143
257
  }))))));
@@ -207,7 +321,7 @@ FileUploader.propTypes = {
207
321
  * Specify the size of the FileUploaderButton, from a list of available
208
322
  * sizes.
209
323
  */
210
- size: PropTypes.oneOf(['sm', 'md', 'lg'])
324
+ size: PropTypes.oneOf(['sm', 'small', 'md', 'field', 'lg'])
211
325
  };
212
326
 
213
327
  export { FileUploader as default };
@@ -48,9 +48,23 @@ const LinkBase = /*#__PURE__*/React.forwardRef(({
48
48
  linkProps['aria-disabled'] = true;
49
49
  }
50
50
  const BaseComponentAsAny = BaseComponent ?? 'a';
51
+ const handleOnClick = event => {
52
+ if (disabled) {
53
+ event.preventDefault();
54
+ event.stopPropagation();
55
+ } else {
56
+ // If the link is not disabled, we allow the onClick event to propagate
57
+ // so that any parent handlers can also respond to the click.
58
+ if (rest.onClick) {
59
+ rest.onClick(event);
60
+ }
61
+ }
62
+ };
51
63
  return /*#__PURE__*/React.createElement(BaseComponentAsAny, _extends({
52
64
  ref: ref
53
- }, linkProps, rest), children, !inline && Icon && /*#__PURE__*/React.createElement("div", {
65
+ }, linkProps, rest, {
66
+ onClick: handleOnClick
67
+ }), children, !inline && Icon && /*#__PURE__*/React.createElement("div", {
54
68
  className: `${prefix}--link__icon`
55
69
  }, /*#__PURE__*/React.createElement(Icon, null)));
56
70
  });
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright IBM Corp. 2023
2
+ * Copyright IBM Corp. 2023, 2025
3
3
  *
4
4
  * This source code is licensed under the Apache-2.0 license found in the
5
5
  * LICENSE file in the root directory of this source tree.
@@ -28,10 +28,17 @@ function menuReducer(state, action) {
28
28
  hasSelectableItems: true
29
29
  };
30
30
  case 'registerItem':
31
- return {
32
- ...state,
33
- items: [...state.items, action.payload].filter(item => item.ref.current !== null)
34
- };
31
+ {
32
+ const newItem = action.payload;
33
+ const items = state.items.filter(item => item.ref.current);
34
+ const next = newItem.ref.current?.nextElementSibling;
35
+ const idx = items.findIndex(item => item.ref.current === next);
36
+ items.splice(idx < 0 ? items.length : idx, 0, newItem);
37
+ return {
38
+ ...state,
39
+ items
40
+ };
41
+ }
35
42
  }
36
43
  }
37
44
  const MenuContext = /*#__PURE__*/createContext({
@@ -49,7 +49,8 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
49
49
  middleware: [offset({
50
50
  mainAxis: -6,
51
51
  crossAxis: -6
52
- })]
52
+ })],
53
+ strategy: 'fixed'
53
54
  });
54
55
  const {
55
56
  getReferenceProps,
@@ -128,18 +129,11 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
128
129
  [`${prefix}--menu-item--disabled`]: isDisabled,
129
130
  [`${prefix}--menu-item--danger`]: isDanger
130
131
  });
131
- const [isFocusable, setIsFocusable] = useState(false);
132
+
132
133
  // on first render, register this menuitem in the context's state
133
134
  // (used for keyboard navigation)
134
135
  useEffect(() => {
135
136
  registerItem();
136
-
137
- // Detects if this is the first focusable item
138
- const currentItems = context.state.items;
139
- if (!disabled && menuItem.current && currentItems.length === 0) {
140
- setIsFocusable(true);
141
- }
142
-
143
137
  // eslint-disable-next-line react-hooks/exhaustive-deps
144
138
  }, []);
145
139
 
@@ -178,7 +172,7 @@ const MenuItem = /*#__PURE__*/forwardRef(function MenuItem({
178
172
  }, rest, {
179
173
  ref: ref,
180
174
  className: classNames,
181
- tabIndex: isFocusable ? 0 : -1,
175
+ tabIndex: !disabled ? 0 : -1,
182
176
  "aria-disabled": isDisabled ?? undefined,
183
177
  "aria-haspopup": hasChildren ?? undefined,
184
178
  "aria-expanded": hasChildren ? submenuOpen : undefined,
@@ -30,7 +30,7 @@ import '../Text/index.js';
30
30
  import { useFeatureFlag } from '../FeatureFlags/index.js';
31
31
  import { composeEventHandlers } from '../../tools/events.js';
32
32
  import { deprecate } from '../../prop-types/deprecate.js';
33
- import { unstable__Dialog } from '../Dialog/index.js';
33
+ import { Dialog } from '../Dialog/Dialog.js';
34
34
  import { AILabel } from '../AILabel/index.js';
35
35
  import { isComponentElement } from '../../internal/utils.js';
36
36
  import { warning } from '../../internal/warning.js';
@@ -81,6 +81,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
81
81
  const innerModal = useRef(null);
82
82
  const startTrap = useRef(null);
83
83
  const endTrap = useRef(null);
84
+ const wrapFocusTimeout = useRef(null);
84
85
  const [isScrollable, setIsScrollable] = useState(false);
85
86
  const prevOpen = usePreviousValue(open);
86
87
  const modalInstanceId = `modal-${useId()}`;
@@ -142,13 +143,20 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
142
143
  const {
143
144
  current: endTrapNode
144
145
  } = endTrap;
145
- wrapFocus({
146
- bodyNode,
147
- startTrapNode,
148
- endTrapNode,
149
- currentActiveNode,
150
- oldActiveNode,
151
- selectorsFloatingMenus
146
+ // use setTimeout to ensure focus is set after all browser default focus behavior. Fixes issue of
147
+ // focus not wrapping in Firefox
148
+ wrapFocusTimeout.current = setTimeout(() => {
149
+ wrapFocus({
150
+ bodyNode,
151
+ startTrapNode,
152
+ endTrapNode,
153
+ currentActiveNode,
154
+ oldActiveNode,
155
+ selectorsFloatingMenus
156
+ });
157
+ if (wrapFocusTimeout.current) {
158
+ clearTimeout(wrapFocusTimeout.current);
159
+ }
152
160
  });
153
161
  }
154
162
 
@@ -306,7 +314,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
306
314
  // alertdialog is the only permitted aria role for a native dialog element
307
315
  // https://www.w3.org/TR/html-aria/#docconformance:~:text=Role%3A-,alertdialog,-.%20(dialog%20is
308
316
  const isAlertDialog = alert && !passiveModal;
309
- const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(unstable__Dialog, {
317
+ const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(Dialog, {
310
318
  open: open,
311
319
  focusAfterCloseRef: launcherButtonRef,
312
320
  modal: true,
@@ -122,11 +122,7 @@ const FilterableMultiSelect = /*#__PURE__*/forwardRef(function FilterableMultiSe
122
122
  inputValue
123
123
  }), [items, inputValue, itemToString, filterItems]);
124
124
  const nonSelectAllItems = useMemo(() => filteredItems.filter(item => !item.isSelectAll), [filteredItems]);
125
- let selectAll = filteredItems.some(item => item.isSelectAll);
126
- if ((selected ?? []).length > 0 && selectAll) {
127
- console.warn('Warning: `selectAll` should not be used when `selectedItems` is provided. Please pass either `selectAll` or `selectedItems`, not both.');
128
- selectAll = false;
129
- }
125
+ const selectAll = filteredItems.some(item => item.isSelectAll);
130
126
  const {
131
127
  selectedItems: controlledSelectedItems,
132
128
  onItemChange,
@@ -111,11 +111,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
111
111
  return true; // Return true if item is not an object with undefined values
112
112
  });
113
113
  }, [items]);
114
- let selectAll = filteredItems.some(item => item.isSelectAll);
115
- if ((selected ?? []).length > 0 && selectAll) {
116
- console.warn('Warning: `selectAll` should not be used when `selectedItems` is provided. Please pass either `selectAll` or `selectedItems`, not both.');
117
- selectAll = false;
118
- }
114
+ const selectAll = filteredItems.some(item => item.isSelectAll);
119
115
  const prefix = usePrefix();
120
116
  const {
121
117
  isFluid
@@ -489,13 +485,13 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
489
485
  })), slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
490
486
  className: `${prefix}--list-box__inner-wrapper--decorator`
491
487
  }, normalizedDecorator) : ''), /*#__PURE__*/React.createElement(ListBox.Menu, menuProps, isOpen && sortItems(filteredItems, sortOptions).map((item, index) => {
492
- const isChecked = selectedItems.filter(selected => isEqual(selected, item)).length > 0;
493
488
  const {
494
489
  hasIndividualSelections,
495
490
  nonSelectAllSelectedCount,
496
491
  totalSelectableCount
497
492
  } = getSelectionStats(selectedItems, filteredItems);
498
- const isIndeterminate = item['isSelectAll'] && hasIndividualSelections && nonSelectAllSelectedCount < totalSelectableCount;
493
+ const isChecked = item.isSelectAll ? nonSelectAllSelectedCount === totalSelectableCount && totalSelectableCount > 0 : selectedItems.some(selected => isEqual(selected, item));
494
+ const isIndeterminate = item.isSelectAll && hasIndividualSelections && nonSelectAllSelectedCount < totalSelectableCount;
499
495
  const itemProps = getItemProps({
500
496
  item,
501
497
  // we don't want Downshift to set aria-selected for us
@@ -25,6 +25,10 @@ export interface PopoverBaseProps {
25
25
  * Specify how the popover should align with the trigger element.
26
26
  */
27
27
  align?: PopoverAlignment;
28
+ /**
29
+ * **Experimental:** Provide an offset value for alignment axis. Only takes effect when `autoalign` is enabled.
30
+ */
31
+ alignmentAxisOffset?: number;
28
32
  /**
29
33
  * Will auto-align the popover on first render if it is not visible. This prop
30
34
  * is currently experimental and is subject to future changes. Requires
@@ -17,7 +17,6 @@ import { useEvent, useWindowEvent } from '../../internal/useEvent.js';
17
17
  import { mapPopoverAlign } from '../../tools/mapPopoverAlign.js';
18
18
  import { useFloating, autoUpdate, offset, flip, arrow, hide } from '@floating-ui/react';
19
19
  import { useFeatureFlag } from '../FeatureFlags/index.js';
20
- import { ToggletipButton } from '../Toggletip/index.js';
21
20
 
22
21
  const PopoverContext = /*#__PURE__*/React.createContext({
23
22
  setFloating: {
@@ -62,7 +61,10 @@ forwardRef) {
62
61
  // The `Popover` should close whenever it and its children loses focus
63
62
  useEvent(popover, 'focusout', event => {
64
63
  const relatedTarget = event.relatedTarget;
64
+
65
+ // No relatedTarget, focus moved to nowhere, so close the popover
65
66
  if (!relatedTarget) {
67
+ onRequestClose?.();
66
68
  return;
67
69
  }
68
70
  const isOutsideMainContainer = !popover.current?.contains(relatedTarget);
@@ -235,7 +237,7 @@ forwardRef) {
235
237
  // For a toggletip there is a specific trigger component, ToggletipButton.
236
238
  // In either of these cases we want to set this as the reference node for floating-ui autoAlign
237
239
  // positioning.
238
- if (enableFloatingStyles && item?.type !== PopoverContent || enableFloatingStyles && item?.type === ToggletipButton) {
240
+ if (enableFloatingStyles && item?.type !== PopoverContent || enableFloatingStyles && item?.type['displayName'] === 'ToggletipButton') {
239
241
  // Set the reference element for floating-ui
240
242
  refs.setReference(node);
241
243
  }
@@ -292,6 +294,10 @@ Popover.propTypes = {
292
294
 
293
295
  // new values to match floating-ui
294
296
  'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']), ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end'], mapPopoverAlign),
297
+ /**
298
+ * **Experimental:** Provide an offset value for alignment axis. Only takes effect when `autoalign` is enabled.
299
+ */
300
+ alignmentAxisOffset: PropTypes.number,
295
301
  /**
296
302
  * Provide a custom element or component to render the top-level node for the
297
303
  * component.
@@ -32,6 +32,10 @@ export interface StructuredListWrapperProps extends DivAttrs {
32
32
  * Specify whether your StructuredListWrapper should have selections
33
33
  */
34
34
  selection?: boolean;
35
+ /**
36
+ * Specify which row will be selected initially
37
+ */
38
+ selectedInitialRow?: string;
35
39
  }
36
40
  export declare function StructuredListWrapper(props: StructuredListWrapperProps): import("react/jsx-runtime").JSX.Element;
37
41
  export declare namespace StructuredListWrapper {
@@ -65,6 +69,10 @@ export declare namespace StructuredListWrapper {
65
69
  * Specify whether your StructuredListWrapper should have selections
66
70
  */
67
71
  selection: PropTypes.Requireable<boolean>;
72
+ /**
73
+ * Specify which row will be selected initially
74
+ */
75
+ selectedInitialRow: PropTypes.Requireable<string>;
68
76
  };
69
77
  }
70
78
  export interface StructuredListHeadProps extends DivAttrs {
@@ -148,6 +156,10 @@ export interface StructuredListRowProps extends DivAttrs {
148
156
  * Mark if this row should be selectable
149
157
  */
150
158
  selection?: boolean;
159
+ /**
160
+ * Specify row id so that it can be used for initial selection
161
+ */
162
+ id?: string;
151
163
  }
152
164
  export declare function StructuredListRow(props: StructuredListRowProps): import("react/jsx-runtime").JSX.Element;
153
165
  export declare namespace StructuredListRow {
@@ -180,6 +192,10 @@ export declare namespace StructuredListRow {
180
192
  * Mark if this row should be selectable
181
193
  */
182
194
  selection: PropTypes.Requireable<boolean>;
195
+ /**
196
+ * Specify row id so that it can be used for initial selection
197
+ */
198
+ id: PropTypes.Requireable<string>;
183
199
  };
184
200
  }
185
201
  export interface StructuredListInputProps extends DivAttrs {