@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.
- package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1113 -851
- package/es/components/AILabel/index.d.ts +2 -5
- package/es/components/AILabel/index.js +2 -42
- package/es/components/ComposedModal/ComposedModal.js +2 -2
- package/es/components/Copy/Copy.js +1 -0
- package/es/components/DataTable/DataTable.js +10 -3
- package/es/components/Dialog/Dialog.d.ts +245 -0
- package/es/components/Dialog/Dialog.js +593 -0
- package/es/components/Dialog/index.d.ts +3 -251
- package/es/components/Dialog/index.js +1 -609
- package/es/components/FeatureFlags/index.d.ts +3 -1
- package/es/components/FeatureFlags/index.js +5 -2
- package/es/components/FileUploader/FileUploader.d.ts +28 -6
- package/es/components/FileUploader/FileUploader.js +152 -38
- package/es/components/Link/Link.js +15 -1
- package/es/components/Menu/MenuContext.d.ts +1 -1
- package/es/components/Menu/MenuContext.js +11 -4
- package/es/components/Menu/MenuItem.js +4 -10
- package/es/components/Modal/Modal.js +17 -9
- package/es/components/MultiSelect/FilterableMultiSelect.js +1 -5
- package/es/components/MultiSelect/MultiSelect.js +3 -7
- package/es/components/Popover/index.d.ts +4 -0
- package/es/components/Popover/index.js +8 -2
- package/es/components/StructuredList/StructuredList.d.ts +16 -0
- package/es/components/StructuredList/StructuredList.js +23 -11
- package/es/components/Toggletip/index.d.ts +13 -36
- package/es/components/Toggletip/index.js +12 -51
- package/es/index.d.ts +2 -1
- package/es/index.js +2 -0
- package/es/internal/Selection.js +1 -1
- package/es/internal/useMergedRefs.js +4 -3
- package/lib/components/AILabel/index.d.ts +2 -5
- package/lib/components/AILabel/index.js +1 -41
- package/lib/components/ComposedModal/ComposedModal.js +2 -2
- package/lib/components/Copy/Copy.js +1 -0
- package/lib/components/DataTable/DataTable.js +10 -3
- package/lib/components/Dialog/Dialog.d.ts +245 -0
- package/lib/components/Dialog/Dialog.js +602 -0
- package/lib/components/Dialog/index.d.ts +3 -251
- package/lib/components/Dialog/index.js +9 -614
- package/lib/components/FeatureFlags/index.d.ts +3 -1
- package/lib/components/FeatureFlags/index.js +5 -2
- package/lib/components/FileUploader/FileUploader.d.ts +28 -6
- package/lib/components/FileUploader/FileUploader.js +151 -37
- package/lib/components/Link/Link.js +15 -1
- package/lib/components/Menu/MenuContext.d.ts +1 -1
- package/lib/components/Menu/MenuContext.js +11 -4
- package/lib/components/Menu/MenuItem.js +4 -10
- package/lib/components/Modal/Modal.js +24 -16
- package/lib/components/MultiSelect/FilterableMultiSelect.js +1 -5
- package/lib/components/MultiSelect/MultiSelect.js +3 -7
- package/lib/components/Popover/index.d.ts +4 -0
- package/lib/components/Popover/index.js +8 -2
- package/lib/components/StructuredList/StructuredList.d.ts +16 -0
- package/lib/components/StructuredList/StructuredList.js +23 -11
- package/lib/components/Toggletip/index.d.ts +13 -36
- package/lib/components/Toggletip/index.js +11 -50
- package/lib/index.d.ts +2 -1
- package/lib/index.js +60 -58
- package/lib/internal/Selection.js +1 -1
- package/lib/internal/useMergedRefs.js +3 -2
- package/package.json +15 -16
- 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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
151
|
+
uploaderButton.current?.focus?.();
|
|
71
152
|
}
|
|
72
|
-
};
|
|
153
|
+
}, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick]);
|
|
73
154
|
useImperativeHandle(ref, () => ({
|
|
74
155
|
clearFiles() {
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
},
|
|
116
|
-
key:
|
|
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
|
-
}
|
|
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:
|
|
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
|
|
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
|
});
|
|
@@ -28,10 +28,17 @@ function menuReducer(state, action) {
|
|
|
28
28
|
hasSelectableItems: true
|
|
29
29
|
};
|
|
30
30
|
case 'registerItem':
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
items
|
|
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
|
-
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 {
|