@carbon/react 1.89.0-rc.1 → 1.90.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.
- package/.playwright/INTERNAL_AVT_REPORT_DO_NOT_USE.json +1152 -849
- package/README.md +2 -2
- package/es/components/ComposedModal/ComposedModal.js +16 -5
- package/es/components/DataTable/DataTable.d.ts +3 -8
- package/es/components/DataTable/DataTable.js +10 -3
- package/es/components/DataTable/TableExpandRow.d.ts +33 -5
- package/es/components/DataTable/TableExpandRow.js +4 -2
- package/es/components/DataTable/TableHeader.d.ts +1 -2
- package/es/components/DataTable/TableHeader.js +1 -2
- package/es/components/DataTable/TableRow.d.ts +3 -6
- package/es/components/DataTable/TableRow.js +35 -22
- package/es/components/DataTable/state/sorting.d.ts +55 -14
- package/es/components/DataTable/state/sorting.js +40 -50
- package/es/components/DataTable/tools/sorting.js +4 -0
- 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/Menu/MenuItem.js +2 -1
- package/es/components/Modal/Modal.js +14 -8
- package/es/components/NumberInput/NumberInput.js +11 -6
- package/es/components/Popover/index.js +6 -2
- package/es/components/StructuredList/StructuredList.js +4 -2
- package/es/components/Tag/DismissibleTag.d.ts +5 -0
- package/es/components/Tag/DismissibleTag.js +6 -1
- package/es/components/Toggletip/index.js +20 -8
- package/es/components/TreeView/TreeNode.d.ts +28 -0
- package/es/components/TreeView/TreeNode.js +6 -5
- package/es/index.d.ts +2 -1
- package/es/index.js +2 -0
- package/lib/components/ComposedModal/ComposedModal.js +16 -5
- package/lib/components/DataTable/DataTable.d.ts +3 -8
- package/lib/components/DataTable/DataTable.js +10 -3
- package/lib/components/DataTable/TableExpandRow.d.ts +33 -5
- package/lib/components/DataTable/TableExpandRow.js +4 -2
- package/lib/components/DataTable/TableHeader.d.ts +1 -2
- package/lib/components/DataTable/TableHeader.js +1 -2
- package/lib/components/DataTable/TableRow.d.ts +3 -6
- package/lib/components/DataTable/TableRow.js +34 -21
- package/lib/components/DataTable/state/sorting.d.ts +55 -14
- package/lib/components/DataTable/state/sorting.js +39 -50
- package/lib/components/DataTable/tools/sorting.js +4 -0
- 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/Menu/MenuItem.js +2 -1
- package/lib/components/Modal/Modal.js +21 -15
- package/lib/components/NumberInput/NumberInput.js +10 -5
- package/lib/components/Popover/index.js +6 -2
- package/lib/components/StructuredList/StructuredList.js +4 -2
- package/lib/components/Tag/DismissibleTag.d.ts +5 -0
- package/lib/components/Tag/DismissibleTag.js +6 -1
- package/lib/components/Toggletip/index.js +19 -7
- package/lib/components/TreeView/TreeNode.d.ts +28 -0
- package/lib/components/TreeView/TreeNode.js +6 -5
- package/lib/index.d.ts +2 -1
- package/lib/index.js +60 -58
- package/package.json +15 -15
- package/telemetry.yml +16 -0
|
@@ -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>;
|
|
@@ -21,6 +21,7 @@ var match = require('../../internal/keyboard/match.js');
|
|
|
21
21
|
var usePrefix = require('../../internal/usePrefix.js');
|
|
22
22
|
require('../Text/index.js');
|
|
23
23
|
var useId = require('../../internal/useId.js');
|
|
24
|
+
var index = require('../FeatureFlags/index.js');
|
|
24
25
|
var Text = require('../Text/Text.js');
|
|
25
26
|
|
|
26
27
|
const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
@@ -42,45 +43,149 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
42
43
|
...other
|
|
43
44
|
}, ref) => {
|
|
44
45
|
const fileUploaderInstanceId = useId.useId('file-uploader');
|
|
45
|
-
const [state, updateState] = React.useState({
|
|
46
|
-
fileNames: []
|
|
47
|
-
});
|
|
48
|
-
const nodes = [];
|
|
49
46
|
const prefix = usePrefix.usePrefix();
|
|
50
|
-
const
|
|
47
|
+
const enhancedFileUploaderEnabled = index.useFeatureFlag('enable-enhanced-file-uploader');
|
|
48
|
+
const [fileItems, setFileItems] = React.useState([]);
|
|
49
|
+
const [legacyFileNames, setLegacyFileNames] = React.useState([]);
|
|
50
|
+
const [fileObjects, setFileObjects] = React.useState(new Map());
|
|
51
|
+
const nodes = [];
|
|
52
|
+
const createFileItem = file => ({
|
|
53
|
+
name: file.name,
|
|
54
|
+
uuid: `${fileUploaderInstanceId}-${Date.now()}-${Array.from(crypto.getRandomValues(new Uint8Array(8))).map(b => b.toString(36)).join('')}`,
|
|
55
|
+
file
|
|
56
|
+
});
|
|
57
|
+
const handleChange = React.useCallback(evt => {
|
|
51
58
|
evt.stopPropagation();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const newFiles = Array.from(evt.target.files);
|
|
60
|
+
if (enhancedFileUploaderEnabled) {
|
|
61
|
+
const newFileItems = newFiles.map(createFileItem);
|
|
62
|
+
let updatedFileItems;
|
|
63
|
+
if (multiple) {
|
|
64
|
+
const existingNames = new Set(fileItems.map(item => item.name));
|
|
65
|
+
const uniqueNewItems = newFileItems.filter(item => !existingNames.has(item.name));
|
|
66
|
+
updatedFileItems = [...fileItems, ...uniqueNewItems];
|
|
67
|
+
} else {
|
|
68
|
+
updatedFileItems = newFileItems;
|
|
69
|
+
}
|
|
70
|
+
setFileItems(updatedFileItems);
|
|
71
|
+
if (onChange) {
|
|
72
|
+
const allFiles = updatedFileItems.map(item => item.file);
|
|
73
|
+
const enhancedEvent = {
|
|
74
|
+
...evt,
|
|
75
|
+
target: {
|
|
76
|
+
...evt.target,
|
|
77
|
+
files: Object.assign(allFiles, {
|
|
78
|
+
item: index => allFiles[index] || null
|
|
79
|
+
}),
|
|
80
|
+
addedFiles: newFileItems,
|
|
81
|
+
currentFiles: updatedFileItems,
|
|
82
|
+
action: 'add'
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
onChange(enhancedEvent);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
const filenames = newFiles.map(file => file.name);
|
|
89
|
+
const updatedFileNames = multiple ? [...new Set([...legacyFileNames, ...filenames])] : filenames;
|
|
90
|
+
setLegacyFileNames(updatedFileNames);
|
|
91
|
+
setFileObjects(prevMap => {
|
|
92
|
+
const newMap = multiple ? new Map(prevMap) : new Map();
|
|
93
|
+
newFiles.forEach(file => {
|
|
94
|
+
newMap.set(file.name, file);
|
|
95
|
+
});
|
|
96
|
+
return newMap;
|
|
97
|
+
});
|
|
98
|
+
if (onChange) {
|
|
99
|
+
onChange(evt);
|
|
100
|
+
}
|
|
58
101
|
}
|
|
59
|
-
};
|
|
60
|
-
const handleClick = (evt, {
|
|
102
|
+
}, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onChange]);
|
|
103
|
+
const handleClick = React.useCallback((evt, {
|
|
61
104
|
index,
|
|
62
105
|
filenameStatus
|
|
63
106
|
}) => {
|
|
64
107
|
if (filenameStatus === 'edit') {
|
|
65
108
|
evt.stopPropagation();
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
109
|
+
if (enhancedFileUploaderEnabled) {
|
|
110
|
+
const deletedItem = fileItems[index];
|
|
111
|
+
if (!deletedItem) return;
|
|
112
|
+
const remainingItems = fileItems.filter((_, i) => i !== index);
|
|
113
|
+
setFileItems(remainingItems);
|
|
114
|
+
const remainingFiles = remainingItems.map(item => item.file);
|
|
115
|
+
const enhancedEvent = {
|
|
116
|
+
...evt,
|
|
117
|
+
target: {
|
|
118
|
+
...evt.target,
|
|
119
|
+
files: Object.assign(remainingFiles, {
|
|
120
|
+
item: index => remainingFiles[index] || null
|
|
121
|
+
}),
|
|
122
|
+
deletedFile: deletedItem,
|
|
123
|
+
deletedFileName: deletedItem.name,
|
|
124
|
+
remainingFiles: remainingItems,
|
|
125
|
+
currentFiles: remainingItems,
|
|
126
|
+
action: 'remove'
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
if (onDelete) {
|
|
130
|
+
onDelete(enhancedEvent);
|
|
131
|
+
}
|
|
132
|
+
if (onChange) {
|
|
133
|
+
onChange(enhancedEvent);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
const deletedFileName = legacyFileNames[index];
|
|
137
|
+
const filteredArray = legacyFileNames.filter(filename => filename !== deletedFileName);
|
|
138
|
+
setLegacyFileNames(filteredArray);
|
|
139
|
+
|
|
140
|
+
// Update File objects
|
|
141
|
+
setFileObjects(prevMap => {
|
|
142
|
+
const newMap = new Map(prevMap);
|
|
143
|
+
if (deletedFileName) {
|
|
144
|
+
newMap.delete(deletedFileName);
|
|
145
|
+
}
|
|
146
|
+
return newMap;
|
|
147
|
+
});
|
|
148
|
+
if (onDelete) {
|
|
149
|
+
onDelete(evt);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (onClick) {
|
|
153
|
+
onClick(evt);
|
|
73
154
|
}
|
|
74
|
-
|
|
155
|
+
uploaderButton.current?.focus?.();
|
|
75
156
|
}
|
|
76
|
-
};
|
|
157
|
+
}, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick]);
|
|
77
158
|
React.useImperativeHandle(ref, () => ({
|
|
78
159
|
clearFiles() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
160
|
+
if (enhancedFileUploaderEnabled) {
|
|
161
|
+
const previousItems = [...fileItems];
|
|
162
|
+
setFileItems([]);
|
|
163
|
+
if (onChange && previousItems.length > 0) {
|
|
164
|
+
const enhancedEvent = {
|
|
165
|
+
target: {
|
|
166
|
+
files: Object.assign([], {
|
|
167
|
+
item: () => null
|
|
168
|
+
}),
|
|
169
|
+
clearedFiles: previousItems,
|
|
170
|
+
currentFiles: [],
|
|
171
|
+
action: 'clear'
|
|
172
|
+
},
|
|
173
|
+
preventDefault: () => {},
|
|
174
|
+
stopPropagation: () => {}
|
|
175
|
+
};
|
|
176
|
+
onChange(enhancedEvent);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
setLegacyFileNames([]);
|
|
180
|
+
setFileObjects(new Map());
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
...(enhancedFileUploaderEnabled && {
|
|
184
|
+
getCurrentFiles() {
|
|
185
|
+
return [...fileItems];
|
|
186
|
+
}
|
|
187
|
+
})
|
|
188
|
+
}), [enhancedFileUploaderEnabled, fileItems, onChange]);
|
|
84
189
|
const uploaderButton = /*#__PURE__*/React.createRef();
|
|
85
190
|
const classes = cx({
|
|
86
191
|
[`${prefix}--form-item`]: true,
|
|
@@ -93,6 +198,15 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
93
198
|
[`${prefix}--file__selected-file--md`]: size === 'field' || size === 'md',
|
|
94
199
|
[`${prefix}--file__selected-file--sm`]: size === 'small' || size === 'sm'
|
|
95
200
|
});
|
|
201
|
+
const displayFiles = enhancedFileUploaderEnabled ? fileItems.map((item, index) => ({
|
|
202
|
+
name: item.name,
|
|
203
|
+
key: item.uuid,
|
|
204
|
+
index
|
|
205
|
+
})) : legacyFileNames.map((name, index) => ({
|
|
206
|
+
name,
|
|
207
|
+
key: index,
|
|
208
|
+
index
|
|
209
|
+
}));
|
|
96
210
|
return /*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({
|
|
97
211
|
className: classes
|
|
98
212
|
}, other), !labelTitle ? null : /*#__PURE__*/React.createElement(Text.Text, {
|
|
@@ -116,32 +230,32 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
116
230
|
"aria-describedby": fileUploaderInstanceId
|
|
117
231
|
}), /*#__PURE__*/React.createElement("div", {
|
|
118
232
|
className: `${prefix}--file-container`
|
|
119
|
-
},
|
|
120
|
-
key:
|
|
233
|
+
}, displayFiles.length === 0 ? null : displayFiles.map(file => /*#__PURE__*/React.createElement("span", _rollupPluginBabelHelpers.extends({
|
|
234
|
+
key: file.key,
|
|
121
235
|
className: selectedFileClasses,
|
|
122
236
|
ref: node => {
|
|
123
|
-
nodes[index] = node;
|
|
124
|
-
}
|
|
237
|
+
nodes[file.index] = node;
|
|
238
|
+
}
|
|
125
239
|
}, other), /*#__PURE__*/React.createElement(Text.Text, {
|
|
126
240
|
as: "p",
|
|
127
241
|
className: `${prefix}--file-filename`,
|
|
128
|
-
id:
|
|
129
|
-
}, name), /*#__PURE__*/React.createElement("span", {
|
|
242
|
+
id: enhancedFileUploaderEnabled ? `${fileUploaderInstanceId}-file-${fileItems[file.index]?.uuid || file.index}` : `${fileUploaderInstanceId}-file-${file.index}`
|
|
243
|
+
}, file.name), /*#__PURE__*/React.createElement("span", {
|
|
130
244
|
className: `${prefix}--file__state-container`
|
|
131
245
|
}, /*#__PURE__*/React.createElement(Filename.default, {
|
|
132
|
-
name: name,
|
|
246
|
+
name: file.name,
|
|
133
247
|
iconDescription: iconDescription,
|
|
134
248
|
status: filenameStatus,
|
|
135
249
|
onKeyDown: evt => {
|
|
136
250
|
if (match.matches(evt, [keys.Enter, keys.Space])) {
|
|
137
251
|
handleClick(evt, {
|
|
138
|
-
index,
|
|
252
|
+
index: file.index,
|
|
139
253
|
filenameStatus
|
|
140
254
|
});
|
|
141
255
|
}
|
|
142
256
|
},
|
|
143
257
|
onClick: evt => handleClick(evt, {
|
|
144
|
-
index,
|
|
258
|
+
index: file.index,
|
|
145
259
|
filenameStatus
|
|
146
260
|
})
|
|
147
261
|
}))))));
|
|
@@ -211,7 +325,7 @@ FileUploader.propTypes = {
|
|
|
211
325
|
* Specify the size of the FileUploaderButton, from a list of available
|
|
212
326
|
* sizes.
|
|
213
327
|
*/
|
|
214
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg'])
|
|
328
|
+
size: PropTypes.oneOf(['sm', 'small', 'md', 'field', 'lg'])
|
|
215
329
|
};
|
|
216
330
|
|
|
217
331
|
exports.default = FileUploader;
|
|
@@ -19,7 +19,7 @@ var Button = require('../Button/Button.js');
|
|
|
19
19
|
require('../Button/Button.Skeleton.js');
|
|
20
20
|
var ButtonSet = require('../ButtonSet/ButtonSet.js');
|
|
21
21
|
var InlineLoading = require('../InlineLoading/InlineLoading.js');
|
|
22
|
-
var index$
|
|
22
|
+
var index$3 = require('../Layer/index.js');
|
|
23
23
|
var requiredIfGivenPropIsTruthy = require('../../prop-types/requiredIfGivenPropIsTruthy.js');
|
|
24
24
|
var wrapFocus = require('../../internal/wrapFocus.js');
|
|
25
25
|
var useIsomorphicEffect = require('../../internal/useIsomorphicEffect.js');
|
|
@@ -28,13 +28,13 @@ var usePrefix = require('../../internal/usePrefix.js');
|
|
|
28
28
|
var usePreviousValue = require('../../internal/usePreviousValue.js');
|
|
29
29
|
var keys = require('../../internal/keyboard/keys.js');
|
|
30
30
|
var match = require('../../internal/keyboard/match.js');
|
|
31
|
-
var index$
|
|
31
|
+
var index$2 = require('../IconButton/index.js');
|
|
32
32
|
var noopFn = require('../../internal/noopFn.js');
|
|
33
33
|
require('../Text/index.js');
|
|
34
34
|
var index = require('../FeatureFlags/index.js');
|
|
35
35
|
var events = require('../../tools/events.js');
|
|
36
36
|
var deprecate = require('../../prop-types/deprecate.js');
|
|
37
|
-
var
|
|
37
|
+
var Dialog = require('../Dialog/Dialog.js');
|
|
38
38
|
var index$1 = require('../AILabel/index.js');
|
|
39
39
|
var utils = require('../../internal/utils.js');
|
|
40
40
|
var warning = require('../../internal/warning.js');
|
|
@@ -42,7 +42,7 @@ var debounce = require('../../node_modules/es-toolkit/dist/compat/function/debou
|
|
|
42
42
|
var Text = require('../Text/Text.js');
|
|
43
43
|
|
|
44
44
|
const ModalSizes = ['xs', 'sm', 'md', 'lg'];
|
|
45
|
-
const invalidOutsideClickMessage = '
|
|
45
|
+
const invalidOutsideClickMessage = '`<Modal>` prop `preventCloseOnClickOutside` should not be `false` when ' + '`passiveModal` is `false`. Transactional, non-passive Modals should ' + 'not be dissmissable by clicking outside. ' + 'See: https://carbondesignsystem.com/components/modal/usage/#transactional-modal';
|
|
46
46
|
const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
47
47
|
'aria-label': ariaLabelProp,
|
|
48
48
|
children,
|
|
@@ -68,7 +68,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
68
68
|
size,
|
|
69
69
|
hasScrollingContent = false,
|
|
70
70
|
closeButtonLabel = 'Close',
|
|
71
|
-
preventCloseOnClickOutside
|
|
71
|
+
preventCloseOnClickOutside,
|
|
72
72
|
isFullWidth,
|
|
73
73
|
launcherButtonRef,
|
|
74
74
|
loadingStatus = 'inactive',
|
|
@@ -100,9 +100,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
100
100
|
const focusTrapWithoutSentinels = index.useFeatureFlag('enable-experimental-focus-wrap-without-sentinels');
|
|
101
101
|
const enableDialogElement = index.useFeatureFlag('enable-dialog-element');
|
|
102
102
|
process.env.NODE_ENV !== "production" ? warning.warning(!(focusTrapWithoutSentinels && enableDialogElement), '`<Modal>` detected both `focusTrapWithoutSentinels` and ' + '`enableDialogElement` feature flags are enabled. The native dialog ' + 'element handles focus, so `enableDialogElement` must be off for ' + '`focusTrapWithoutSentinels` to have any effect.') : void 0;
|
|
103
|
-
|
|
104
|
-
console.error(invalidOutsideClickMessage);
|
|
105
|
-
}
|
|
103
|
+
process.env.NODE_ENV !== "production" ? warning.warning(!(!passiveModal && preventCloseOnClickOutside === false), invalidOutsideClickMessage) : void 0;
|
|
106
104
|
function isCloseButton(element) {
|
|
107
105
|
return !onSecondarySubmit && element === secondaryButton.current || element.classList.contains(modalCloseButtonClass);
|
|
108
106
|
}
|
|
@@ -129,7 +127,15 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
129
127
|
target
|
|
130
128
|
} = evt;
|
|
131
129
|
evt.stopPropagation();
|
|
132
|
-
|
|
130
|
+
const shouldCloseOnOutsideClick =
|
|
131
|
+
// Passive modals can close on clicks outside the modal when
|
|
132
|
+
// preventCloseOnClickOutside is undefined or explicitly set to false.
|
|
133
|
+
passiveModal && !preventCloseOnClickOutside ||
|
|
134
|
+
// Non-passive modals have to explicitly opt-in for close on outside
|
|
135
|
+
// behavior by explicitly setting preventCloseOnClickOutside to false,
|
|
136
|
+
// rather than just leaving it undefined.
|
|
137
|
+
!passiveModal && preventCloseOnClickOutside === false;
|
|
138
|
+
if (shouldCloseOnOutsideClick && target instanceof Node && !wrapFocus.elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
133
139
|
onRequestClose(evt);
|
|
134
140
|
}
|
|
135
141
|
}
|
|
@@ -301,7 +307,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
301
307
|
}) : null;
|
|
302
308
|
const modalButton = /*#__PURE__*/React.createElement("div", {
|
|
303
309
|
className: `${prefix}--modal-close-button`
|
|
304
|
-
}, /*#__PURE__*/React.createElement(index$
|
|
310
|
+
}, /*#__PURE__*/React.createElement(index$2.IconButton, {
|
|
305
311
|
className: modalCloseButtonClass,
|
|
306
312
|
label: closeButtonLabel,
|
|
307
313
|
onClick: onRequestClose,
|
|
@@ -318,7 +324,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
318
324
|
// alertdialog is the only permitted aria role for a native dialog element
|
|
319
325
|
// https://www.w3.org/TR/html-aria/#docconformance:~:text=Role%3A-,alertdialog,-.%20(dialog%20is
|
|
320
326
|
const isAlertDialog = alert && !passiveModal;
|
|
321
|
-
const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(
|
|
327
|
+
const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(Dialog.Dialog, {
|
|
322
328
|
open: open,
|
|
323
329
|
focusAfterCloseRef: launcherButtonRef,
|
|
324
330
|
modal: true,
|
|
@@ -341,7 +347,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
341
347
|
className: `${prefix}--modal--inner__decorator`
|
|
342
348
|
}, normalizedDecorator) : '', /*#__PURE__*/React.createElement("div", {
|
|
343
349
|
className: `${prefix}--modal-close-button`
|
|
344
|
-
}, /*#__PURE__*/React.createElement(index$
|
|
350
|
+
}, /*#__PURE__*/React.createElement(index$2.IconButton, {
|
|
345
351
|
className: modalCloseButtonClass,
|
|
346
352
|
label: closeButtonLabel,
|
|
347
353
|
onClick: onRequestClose,
|
|
@@ -353,7 +359,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
353
359
|
"aria-hidden": "true",
|
|
354
360
|
tabIndex: "-1",
|
|
355
361
|
className: `${modalCloseButtonClass}__icon`
|
|
356
|
-
})))), /*#__PURE__*/React.createElement(index$
|
|
362
|
+
})))), /*#__PURE__*/React.createElement(index$3.Layer, _rollupPluginBabelHelpers.extends({
|
|
357
363
|
ref: contentRef,
|
|
358
364
|
id: modalBodyId,
|
|
359
365
|
className: contentClasses
|
|
@@ -409,7 +415,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
409
415
|
className: `${prefix}--modal-header__heading`
|
|
410
416
|
}, modalHeading), slug ? normalizedDecorator : decorator ? /*#__PURE__*/React.createElement("div", {
|
|
411
417
|
className: `${prefix}--modal--inner__decorator`
|
|
412
|
-
}, normalizedDecorator) : '', !passiveModal && modalButton), /*#__PURE__*/React.createElement(index$
|
|
418
|
+
}, normalizedDecorator) : '', !passiveModal && modalButton), /*#__PURE__*/React.createElement(index$3.Layer, _rollupPluginBabelHelpers.extends({
|
|
413
419
|
ref: contentRef,
|
|
414
420
|
id: modalBodyId,
|
|
415
421
|
className: contentClasses
|
|
@@ -446,7 +452,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
446
452
|
role: "link",
|
|
447
453
|
className: `${prefix}--visually-hidden`
|
|
448
454
|
}, "Focus sentinel"));
|
|
449
|
-
return /*#__PURE__*/React.createElement(index$
|
|
455
|
+
return /*#__PURE__*/React.createElement(index$3.Layer, _rollupPluginBabelHelpers.extends({}, rest, {
|
|
450
456
|
level: 0,
|
|
451
457
|
onKeyDown: handleKeyDown,
|
|
452
458
|
onClick: events.composeEventHandlers([rest?.onClick, handleOnClick]),
|
|
@@ -175,11 +175,16 @@ const NumberInput = /*#__PURE__*/React.forwardRef(function NumberInput(props, fo
|
|
|
175
175
|
[`${prefix}--number__invalid`]: normalizedProps.invalid || normalizedProps.warn,
|
|
176
176
|
[`${prefix}--number__invalid--warning`]: normalizedProps.warn
|
|
177
177
|
});
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
React.useEffect(() => {
|
|
179
|
+
if (type === 'number' && controlledValue !== undefined) {
|
|
180
|
+
if (allowEmpty && controlledValue === '') {
|
|
181
|
+
setValue('');
|
|
182
|
+
} else {
|
|
183
|
+
setValue(controlledValue);
|
|
184
|
+
}
|
|
185
|
+
setPrevControlledValue(controlledValue);
|
|
186
|
+
}
|
|
187
|
+
}, [controlledValue, type, allowEmpty]);
|
|
183
188
|
let ariaDescribedBy = undefined;
|
|
184
189
|
if (normalizedProps.invalid) {
|
|
185
190
|
ariaDescribedBy = normalizedProps.invalidId;
|
|
@@ -63,8 +63,12 @@ forwardRef) {
|
|
|
63
63
|
// The `Popover` should close whenever it and its children loses focus
|
|
64
64
|
useEvent.useEvent(popover, 'focusout', event => {
|
|
65
65
|
const relatedTarget = event.relatedTarget;
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if (isTabTip) {
|
|
67
|
+
if (relatedTarget && !popover.current?.contains(relatedTarget)) {
|
|
68
|
+
onRequestClose?.();
|
|
69
|
+
}
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
68
72
|
if (!relatedTarget) {
|
|
69
73
|
onRequestClose?.();
|
|
70
74
|
return;
|
|
@@ -191,8 +191,10 @@ function StructuredListRow(props) {
|
|
|
191
191
|
setHasFocusWithin(true);
|
|
192
192
|
}
|
|
193
193
|
},
|
|
194
|
-
onFocus:
|
|
195
|
-
|
|
194
|
+
onFocus: event => {
|
|
195
|
+
if (selection || event.currentTarget === event.target) {
|
|
196
|
+
setHasFocusWithin(true);
|
|
197
|
+
}
|
|
196
198
|
},
|
|
197
199
|
onBlur: () => {
|
|
198
200
|
setHasFocusWithin(false);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import React, { type ReactNode } from 'react';
|
|
8
8
|
import { PolymorphicProps } from '../../types/common';
|
|
9
9
|
import { SIZES, TYPES } from './Tag';
|
|
10
|
+
import { PopoverAlignment } from '../Popover';
|
|
10
11
|
export interface DismissibleTagBaseProps {
|
|
11
12
|
/**
|
|
12
13
|
* Provide a custom className that is applied to the containing <span>
|
|
@@ -20,6 +21,10 @@ export interface DismissibleTagBaseProps {
|
|
|
20
21
|
* Specify if the `DismissibleTag` is disabled
|
|
21
22
|
*/
|
|
22
23
|
disabled?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Specify the tooltip alignment for the dismiss button
|
|
26
|
+
*/
|
|
27
|
+
dismissTooltipAlignment?: PopoverAlignment;
|
|
23
28
|
/**
|
|
24
29
|
* Provide a custom tooltip label for the dismiss button
|
|
25
30
|
*/
|
|
@@ -41,6 +41,7 @@ const DismissibleTag = /*#__PURE__*/React.forwardRef(({
|
|
|
41
41
|
text,
|
|
42
42
|
tagTitle,
|
|
43
43
|
type,
|
|
44
|
+
dismissTooltipAlignment = 'bottom',
|
|
44
45
|
dismissTooltipLabel = 'Dismiss tag',
|
|
45
46
|
...other
|
|
46
47
|
}, forwardRef) => {
|
|
@@ -92,7 +93,7 @@ const DismissibleTag = /*#__PURE__*/React.forwardRef(({
|
|
|
92
93
|
className: `${prefix}--tag__decorator`
|
|
93
94
|
}, normalizedDecorator) : '', /*#__PURE__*/React.createElement(Tooltip.Tooltip, {
|
|
94
95
|
label: dismissActionLabel,
|
|
95
|
-
align:
|
|
96
|
+
align: dismissTooltipAlignment,
|
|
96
97
|
className: tooltipClasses,
|
|
97
98
|
leaveDelayMs: 0,
|
|
98
99
|
closeOnActivation: true
|
|
@@ -117,6 +118,10 @@ DismissibleTag.propTypes = {
|
|
|
117
118
|
* Specify if the `DismissibleTag` is disabled
|
|
118
119
|
*/
|
|
119
120
|
disabled: PropTypes.bool,
|
|
121
|
+
/**
|
|
122
|
+
* Specify the tooltip alignment for the dismiss button
|
|
123
|
+
*/
|
|
124
|
+
dismissTooltipAlignment: PropTypes.oneOf(['top', 'bottom', 'left', 'right', 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']),
|
|
120
125
|
/**
|
|
121
126
|
* Provide a custom tooltip label for the dismiss button
|
|
122
127
|
*/
|
|
@@ -102,6 +102,7 @@ function Toggletip({
|
|
|
102
102
|
};
|
|
103
103
|
const onKeyDown = event => {
|
|
104
104
|
if (open && match.match(event, keys.Escape)) {
|
|
105
|
+
event.stopPropagation();
|
|
105
106
|
actions.close();
|
|
106
107
|
|
|
107
108
|
// If the menu is closed while focus is still inside the menu, it should return to the trigger button (#12922)
|
|
@@ -128,13 +129,24 @@ function Toggletip({
|
|
|
128
129
|
actions.close();
|
|
129
130
|
}
|
|
130
131
|
});
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
React.useEffect(() => {
|
|
133
|
+
if (!ref.current) return;
|
|
134
|
+
const targetDocument = ref.current.ownerDocument || document;
|
|
135
|
+
const eventType = 'PointerEvent' in window ? 'pointerdown' : 'mousedown';
|
|
136
|
+
const handleOutsideClick = event => {
|
|
137
|
+
const node = event.target;
|
|
138
|
+
if (open && node && !ref.current.contains(node)) {
|
|
139
|
+
setOpen(false);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const options = {
|
|
143
|
+
capture: true
|
|
144
|
+
};
|
|
145
|
+
targetDocument.addEventListener(eventType, handleOutsideClick, options);
|
|
146
|
+
return () => {
|
|
147
|
+
targetDocument.removeEventListener(eventType, handleOutsideClick, options);
|
|
148
|
+
};
|
|
149
|
+
}, [open]);
|
|
138
150
|
return /*#__PURE__*/React.createElement(ToggletipContext.Provider, {
|
|
139
151
|
value: value
|
|
140
152
|
}, /*#__PURE__*/React.createElement(index.Popover, _rollupPluginBabelHelpers.extends({
|