@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>;
|
|
@@ -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 };
|
|
@@ -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';
|
|
@@ -38,7 +38,7 @@ import { debounce } from '../../node_modules/es-toolkit/dist/compat/function/deb
|
|
|
38
38
|
import { Text } from '../Text/Text.js';
|
|
39
39
|
|
|
40
40
|
const ModalSizes = ['xs', 'sm', 'md', 'lg'];
|
|
41
|
-
const invalidOutsideClickMessage = '
|
|
41
|
+
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';
|
|
42
42
|
const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
43
43
|
'aria-label': ariaLabelProp,
|
|
44
44
|
children,
|
|
@@ -64,7 +64,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
64
64
|
size,
|
|
65
65
|
hasScrollingContent = false,
|
|
66
66
|
closeButtonLabel = 'Close',
|
|
67
|
-
preventCloseOnClickOutside
|
|
67
|
+
preventCloseOnClickOutside,
|
|
68
68
|
isFullWidth,
|
|
69
69
|
launcherButtonRef,
|
|
70
70
|
loadingStatus = 'inactive',
|
|
@@ -96,9 +96,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
96
96
|
const focusTrapWithoutSentinels = useFeatureFlag('enable-experimental-focus-wrap-without-sentinels');
|
|
97
97
|
const enableDialogElement = useFeatureFlag('enable-dialog-element');
|
|
98
98
|
process.env.NODE_ENV !== "production" ? 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;
|
|
99
|
-
|
|
100
|
-
console.error(invalidOutsideClickMessage);
|
|
101
|
-
}
|
|
99
|
+
process.env.NODE_ENV !== "production" ? warning(!(!passiveModal && preventCloseOnClickOutside === false), invalidOutsideClickMessage) : void 0;
|
|
102
100
|
function isCloseButton(element) {
|
|
103
101
|
return !onSecondarySubmit && element === secondaryButton.current || element.classList.contains(modalCloseButtonClass);
|
|
104
102
|
}
|
|
@@ -125,7 +123,15 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
125
123
|
target
|
|
126
124
|
} = evt;
|
|
127
125
|
evt.stopPropagation();
|
|
128
|
-
|
|
126
|
+
const shouldCloseOnOutsideClick =
|
|
127
|
+
// Passive modals can close on clicks outside the modal when
|
|
128
|
+
// preventCloseOnClickOutside is undefined or explicitly set to false.
|
|
129
|
+
passiveModal && !preventCloseOnClickOutside ||
|
|
130
|
+
// Non-passive modals have to explicitly opt-in for close on outside
|
|
131
|
+
// behavior by explicitly setting preventCloseOnClickOutside to false,
|
|
132
|
+
// rather than just leaving it undefined.
|
|
133
|
+
!passiveModal && preventCloseOnClickOutside === false;
|
|
134
|
+
if (shouldCloseOnOutsideClick && target instanceof Node && !elementOrParentIsFloatingMenu(target, selectorsFloatingMenus) && innerModal.current && !innerModal.current.contains(target)) {
|
|
129
135
|
onRequestClose(evt);
|
|
130
136
|
}
|
|
131
137
|
}
|
|
@@ -314,7 +320,7 @@ const Modal = /*#__PURE__*/React.forwardRef(function Modal({
|
|
|
314
320
|
// alertdialog is the only permitted aria role for a native dialog element
|
|
315
321
|
// https://www.w3.org/TR/html-aria/#docconformance:~:text=Role%3A-,alertdialog,-.%20(dialog%20is
|
|
316
322
|
const isAlertDialog = alert && !passiveModal;
|
|
317
|
-
const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(
|
|
323
|
+
const modalBody = enableDialogElement ? /*#__PURE__*/React.createElement(Dialog, {
|
|
318
324
|
open: open,
|
|
319
325
|
focusAfterCloseRef: launcherButtonRef,
|
|
320
326
|
modal: true,
|
|
@@ -9,7 +9,7 @@ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js
|
|
|
9
9
|
import { Subtract, Add } from '@carbon/icons-react';
|
|
10
10
|
import cx from 'classnames';
|
|
11
11
|
import PropTypes from 'prop-types';
|
|
12
|
-
import React, { useContext, useState, useMemo, useCallback, useRef,
|
|
12
|
+
import React, { useContext, useState, useMemo, useCallback, useRef, useEffect, cloneElement } from 'react';
|
|
13
13
|
import { useMergedRefs } from '../../internal/useMergedRefs.js';
|
|
14
14
|
import { useNormalizedInputProps } from '../../internal/useNormalizedInputProps.js';
|
|
15
15
|
import { usePrefix } from '../../internal/usePrefix.js';
|
|
@@ -173,11 +173,16 @@ const NumberInput = /*#__PURE__*/React.forwardRef(function NumberInput(props, fo
|
|
|
173
173
|
[`${prefix}--number__invalid`]: normalizedProps.invalid || normalizedProps.warn,
|
|
174
174
|
[`${prefix}--number__invalid--warning`]: normalizedProps.warn
|
|
175
175
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
useEffect(() => {
|
|
177
|
+
if (type === 'number' && controlledValue !== undefined) {
|
|
178
|
+
if (allowEmpty && controlledValue === '') {
|
|
179
|
+
setValue('');
|
|
180
|
+
} else {
|
|
181
|
+
setValue(controlledValue);
|
|
182
|
+
}
|
|
183
|
+
setPrevControlledValue(controlledValue);
|
|
184
|
+
}
|
|
185
|
+
}, [controlledValue, type, allowEmpty]);
|
|
181
186
|
let ariaDescribedBy = undefined;
|
|
182
187
|
if (normalizedProps.invalid) {
|
|
183
188
|
ariaDescribedBy = normalizedProps.invalidId;
|
|
@@ -61,8 +61,12 @@ forwardRef) {
|
|
|
61
61
|
// The `Popover` should close whenever it and its children loses focus
|
|
62
62
|
useEvent(popover, 'focusout', event => {
|
|
63
63
|
const relatedTarget = event.relatedTarget;
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
if (isTabTip) {
|
|
65
|
+
if (relatedTarget && !popover.current?.contains(relatedTarget)) {
|
|
66
|
+
onRequestClose?.();
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
66
70
|
if (!relatedTarget) {
|
|
67
71
|
onRequestClose?.();
|
|
68
72
|
return;
|
|
@@ -189,8 +189,10 @@ function StructuredListRow(props) {
|
|
|
189
189
|
setHasFocusWithin(true);
|
|
190
190
|
}
|
|
191
191
|
},
|
|
192
|
-
onFocus:
|
|
193
|
-
|
|
192
|
+
onFocus: event => {
|
|
193
|
+
if (selection || event.currentTarget === event.target) {
|
|
194
|
+
setHasFocusWithin(true);
|
|
195
|
+
}
|
|
194
196
|
},
|
|
195
197
|
onBlur: () => {
|
|
196
198
|
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
|
*/
|
|
@@ -37,6 +37,7 @@ const DismissibleTag = /*#__PURE__*/forwardRef(({
|
|
|
37
37
|
text,
|
|
38
38
|
tagTitle,
|
|
39
39
|
type,
|
|
40
|
+
dismissTooltipAlignment = 'bottom',
|
|
40
41
|
dismissTooltipLabel = 'Dismiss tag',
|
|
41
42
|
...other
|
|
42
43
|
}, forwardRef) => {
|
|
@@ -88,7 +89,7 @@ const DismissibleTag = /*#__PURE__*/forwardRef(({
|
|
|
88
89
|
className: `${prefix}--tag__decorator`
|
|
89
90
|
}, normalizedDecorator) : '', /*#__PURE__*/React.createElement(Tooltip, {
|
|
90
91
|
label: dismissActionLabel,
|
|
91
|
-
align:
|
|
92
|
+
align: dismissTooltipAlignment,
|
|
92
93
|
className: tooltipClasses,
|
|
93
94
|
leaveDelayMs: 0,
|
|
94
95
|
closeOnActivation: true
|
|
@@ -113,6 +114,10 @@ DismissibleTag.propTypes = {
|
|
|
113
114
|
* Specify if the `DismissibleTag` is disabled
|
|
114
115
|
*/
|
|
115
116
|
disabled: PropTypes.bool,
|
|
117
|
+
/**
|
|
118
|
+
* Specify the tooltip alignment for the dismiss button
|
|
119
|
+
*/
|
|
120
|
+
dismissTooltipAlignment: PropTypes.oneOf(['top', 'bottom', 'left', 'right', 'top-start', 'top-end', 'bottom-start', 'bottom-end', 'left-end', 'left-start', 'right-end', 'right-start']),
|
|
116
121
|
/**
|
|
117
122
|
* Provide a custom tooltip label for the dismiss button
|
|
118
123
|
*/
|
|
@@ -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, { useContext, useRef, useState } from 'react';
|
|
11
|
+
import React, { useContext, useRef, useState, useEffect } from 'react';
|
|
12
12
|
import { Popover, PopoverContent } from '../Popover/index.js';
|
|
13
13
|
import { Escape } from '../../internal/keyboard/keys.js';
|
|
14
14
|
import { match } from '../../internal/keyboard/match.js';
|
|
@@ -100,6 +100,7 @@ function Toggletip({
|
|
|
100
100
|
};
|
|
101
101
|
const onKeyDown = event => {
|
|
102
102
|
if (open && match(event, Escape)) {
|
|
103
|
+
event.stopPropagation();
|
|
103
104
|
actions.close();
|
|
104
105
|
|
|
105
106
|
// If the menu is closed while focus is still inside the menu, it should return to the trigger button (#12922)
|
|
@@ -126,13 +127,24 @@ function Toggletip({
|
|
|
126
127
|
actions.close();
|
|
127
128
|
}
|
|
128
129
|
});
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (!ref.current) return;
|
|
132
|
+
const targetDocument = ref.current.ownerDocument || document;
|
|
133
|
+
const eventType = 'PointerEvent' in window ? 'pointerdown' : 'mousedown';
|
|
134
|
+
const handleOutsideClick = event => {
|
|
135
|
+
const node = event.target;
|
|
136
|
+
if (open && node && !ref.current.contains(node)) {
|
|
137
|
+
setOpen(false);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const options = {
|
|
141
|
+
capture: true
|
|
142
|
+
};
|
|
143
|
+
targetDocument.addEventListener(eventType, handleOutsideClick, options);
|
|
144
|
+
return () => {
|
|
145
|
+
targetDocument.removeEventListener(eventType, handleOutsideClick, options);
|
|
146
|
+
};
|
|
147
|
+
}, [open]);
|
|
136
148
|
return /*#__PURE__*/React.createElement(ToggletipContext.Provider, {
|
|
137
149
|
value: value
|
|
138
150
|
}, /*#__PURE__*/React.createElement(Popover, _extends({
|
|
@@ -11,6 +11,9 @@ export type TreeNodeProps = {
|
|
|
11
11
|
/**
|
|
12
12
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
13
13
|
* The ID of the active node in the tree
|
|
14
|
+
*
|
|
15
|
+
* @deprecated The `active` prop for `TreeNode` has
|
|
16
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
14
17
|
*/
|
|
15
18
|
active?: string | number;
|
|
16
19
|
/**
|
|
@@ -29,6 +32,9 @@ export type TreeNodeProps = {
|
|
|
29
32
|
/**
|
|
30
33
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
31
34
|
* TreeNode depth to determine spacing
|
|
35
|
+
*
|
|
36
|
+
* @deprecated The `depth` prop for `TreeNode` has
|
|
37
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
32
38
|
*/
|
|
33
39
|
depth?: number;
|
|
34
40
|
/**
|
|
@@ -49,6 +55,9 @@ export type TreeNodeProps = {
|
|
|
49
55
|
label: React.ReactNode;
|
|
50
56
|
/**
|
|
51
57
|
* Callback function for when the node receives or loses focus
|
|
58
|
+
*
|
|
59
|
+
* @deprecated The `onNodeFocusEvent` prop for `TreeNode` has
|
|
60
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
52
61
|
*/
|
|
53
62
|
onNodeFocusEvent?: (event: React.FocusEvent<HTMLElement>) => void;
|
|
54
63
|
/**
|
|
@@ -61,6 +70,9 @@ export type TreeNodeProps = {
|
|
|
61
70
|
onToggle?: UncontrolledOnToggle | ControlledOnToggle;
|
|
62
71
|
/**
|
|
63
72
|
* Callback function for when any node in the tree is selected
|
|
73
|
+
*
|
|
74
|
+
* @deprecated The `onTreeSelect` prop for `TreeNode` has
|
|
75
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
64
76
|
*/
|
|
65
77
|
onTreeSelect?: (event: React.MouseEvent | React.KeyboardEvent, node: Pick<TreeNodeProps, 'id' | 'label' | 'value'>) => void;
|
|
66
78
|
/**
|
|
@@ -70,6 +82,8 @@ export type TreeNodeProps = {
|
|
|
70
82
|
/**
|
|
71
83
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
72
84
|
* Array containing all selected node IDs in the tree
|
|
85
|
+
* @deprecated The `selected` prop for `TreeNode` has
|
|
86
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
73
87
|
*/
|
|
74
88
|
selected?: Array<string | number>;
|
|
75
89
|
/**
|
|
@@ -97,6 +111,9 @@ declare const TreeNode: React.ForwardRefExoticComponent<{
|
|
|
97
111
|
/**
|
|
98
112
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
99
113
|
* The ID of the active node in the tree
|
|
114
|
+
*
|
|
115
|
+
* @deprecated The `active` prop for `TreeNode` has
|
|
116
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
100
117
|
*/
|
|
101
118
|
active?: string | number;
|
|
102
119
|
/**
|
|
@@ -115,6 +132,9 @@ declare const TreeNode: React.ForwardRefExoticComponent<{
|
|
|
115
132
|
/**
|
|
116
133
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
117
134
|
* TreeNode depth to determine spacing
|
|
135
|
+
*
|
|
136
|
+
* @deprecated The `depth` prop for `TreeNode` has
|
|
137
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
118
138
|
*/
|
|
119
139
|
depth?: number;
|
|
120
140
|
/**
|
|
@@ -135,6 +155,9 @@ declare const TreeNode: React.ForwardRefExoticComponent<{
|
|
|
135
155
|
label: React.ReactNode;
|
|
136
156
|
/**
|
|
137
157
|
* Callback function for when the node receives or loses focus
|
|
158
|
+
*
|
|
159
|
+
* @deprecated The `onNodeFocusEvent` prop for `TreeNode` has
|
|
160
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
138
161
|
*/
|
|
139
162
|
onNodeFocusEvent?: (event: React.FocusEvent<HTMLElement>) => void;
|
|
140
163
|
/**
|
|
@@ -147,6 +170,9 @@ declare const TreeNode: React.ForwardRefExoticComponent<{
|
|
|
147
170
|
onToggle?: UncontrolledOnToggle | ControlledOnToggle;
|
|
148
171
|
/**
|
|
149
172
|
* Callback function for when any node in the tree is selected
|
|
173
|
+
*
|
|
174
|
+
* @deprecated The `onTreeSelect` prop for `TreeNode` has
|
|
175
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
150
176
|
*/
|
|
151
177
|
onTreeSelect?: (event: React.MouseEvent | React.KeyboardEvent, node: Pick<TreeNodeProps, "id" | "label" | "value">) => void;
|
|
152
178
|
/**
|
|
@@ -156,6 +182,8 @@ declare const TreeNode: React.ForwardRefExoticComponent<{
|
|
|
156
182
|
/**
|
|
157
183
|
* **Note:** this is controlled by the parent TreeView component, do not set manually.
|
|
158
184
|
* Array containing all selected node IDs in the tree
|
|
185
|
+
* @deprecated The `selected` prop for `TreeNode` has
|
|
186
|
+
* been deprecated after the introduction of context. It will be removed in the next major release.
|
|
159
187
|
*/
|
|
160
188
|
selected?: Array<string | number>;
|
|
161
189
|
/**
|