@carbon/react 1.97.0 → 1.99.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 +1002 -967
- package/es/components/ButtonSet/ButtonSet.d.ts +5 -0
- package/es/components/ButtonSet/ButtonSet.js +68 -4
- package/es/components/CodeSnippet/CodeSnippet.d.ts +1 -1
- package/es/components/Copy/Copy.d.ts +1 -1
- package/es/components/CopyButton/CopyButton.d.ts +1 -1
- package/es/components/DataTable/DataTable.d.ts +2 -1
- package/es/components/DataTable/TableContainer.d.ts +10 -2
- package/es/components/DataTable/TableContainer.js +15 -3
- package/es/components/DataTable/state/sorting.d.ts +2 -4
- package/es/components/DatePicker/DatePicker.d.ts +3 -2
- package/es/components/DatePicker/DatePicker.js +18 -135
- package/es/components/DatePicker/DatePickerLocales.d.ts +12 -0
- package/es/components/DatePicker/DatePickerLocales.js +135 -0
- package/es/components/DatePickerInput/DatePickerInput.js +50 -28
- package/es/components/Dropdown/Dropdown.js +13 -1
- package/es/components/FileUploader/FileUploader.d.ts +23 -8
- package/es/components/FileUploader/FileUploader.js +53 -33
- package/es/components/FileUploader/FileUploaderButton.js +2 -2
- package/es/components/FileUploader/FileUploaderDropContainer.d.ts +13 -2
- package/es/components/FileUploader/FileUploaderDropContainer.js +15 -6
- package/es/components/FileUploader/FileUploaderItem.js +9 -6
- package/es/components/Link/Link.js +1 -1
- package/es/components/Menu/index.d.ts +4 -3
- package/es/components/MultiSelect/FilterableMultiSelect.js +1 -0
- package/es/components/MultiSelect/MultiSelect.js +1 -0
- package/es/components/NumberInput/NumberInput.d.ts +6 -2
- package/es/components/NumberInput/NumberInput.js +17 -5
- package/es/components/Pagination/Pagination.js +5 -5
- package/es/components/Popover/index.d.ts +1 -1
- package/es/components/Popover/index.js +27 -13
- package/es/components/Select/Select.js +27 -33
- package/es/components/Tabs/Tabs.js +1 -1
- package/es/components/TextArea/TextArea.js +14 -4
- package/es/components/TextInput/PasswordInput.js +3 -2
- package/es/components/Toggletip/index.d.ts +1 -0
- package/es/components/Toggletip/index.js +1 -1
- package/es/components/Tooltip/index.d.ts +3 -2
- package/es/internal/FloatingMenu.js +6 -5
- package/es/internal/OptimizedResize.js +4 -5
- package/es/tools/events.d.ts +1 -1
- package/lib/components/ButtonSet/ButtonSet.d.ts +5 -0
- package/lib/components/ButtonSet/ButtonSet.js +67 -3
- package/lib/components/CodeSnippet/CodeSnippet.d.ts +1 -1
- package/lib/components/Copy/Copy.d.ts +1 -1
- package/lib/components/CopyButton/CopyButton.d.ts +1 -1
- package/lib/components/DataTable/DataTable.d.ts +2 -1
- package/lib/components/DataTable/TableContainer.d.ts +10 -2
- package/lib/components/DataTable/TableContainer.js +15 -3
- package/lib/components/DataTable/state/sorting.d.ts +2 -4
- package/lib/components/DatePicker/DatePicker.d.ts +3 -2
- package/lib/components/DatePicker/DatePicker.js +18 -135
- package/lib/components/DatePicker/DatePickerLocales.d.ts +12 -0
- package/lib/components/DatePicker/DatePickerLocales.js +137 -0
- package/lib/components/DatePickerInput/DatePickerInput.js +49 -27
- package/lib/components/Dropdown/Dropdown.js +13 -1
- package/lib/components/FileUploader/FileUploader.d.ts +23 -8
- package/lib/components/FileUploader/FileUploader.js +53 -33
- package/lib/components/FileUploader/FileUploaderButton.js +2 -2
- package/lib/components/FileUploader/FileUploaderDropContainer.d.ts +13 -2
- package/lib/components/FileUploader/FileUploaderDropContainer.js +15 -6
- package/lib/components/FileUploader/FileUploaderItem.js +9 -6
- package/lib/components/Link/Link.js +1 -1
- package/lib/components/Menu/index.d.ts +4 -3
- package/lib/components/MultiSelect/FilterableMultiSelect.js +1 -0
- package/lib/components/MultiSelect/MultiSelect.js +1 -0
- package/lib/components/NumberInput/NumberInput.d.ts +6 -2
- package/lib/components/NumberInput/NumberInput.js +17 -5
- package/lib/components/Pagination/Pagination.js +5 -5
- package/lib/components/Popover/index.d.ts +1 -1
- package/lib/components/Popover/index.js +27 -13
- package/lib/components/Select/Select.js +27 -33
- package/lib/components/Tabs/Tabs.js +1 -1
- package/lib/components/TextArea/TextArea.js +14 -4
- package/lib/components/TextInput/PasswordInput.js +3 -2
- package/lib/components/Toggletip/index.d.ts +1 -0
- package/lib/components/Toggletip/index.js +3 -0
- package/lib/components/Tooltip/index.d.ts +3 -2
- package/lib/internal/FloatingMenu.js +6 -5
- package/lib/internal/OptimizedResize.js +4 -5
- package/lib/tools/events.d.ts +1 -1
- package/package.json +21 -29
- package/telemetry.yml +3 -0
|
@@ -8,7 +8,9 @@ import React, { type HTMLAttributes } from 'react';
|
|
|
8
8
|
interface FileItem {
|
|
9
9
|
name: string;
|
|
10
10
|
uuid: string;
|
|
11
|
-
file: File
|
|
11
|
+
file: File & {
|
|
12
|
+
invalidFileType?: boolean;
|
|
13
|
+
};
|
|
12
14
|
}
|
|
13
15
|
export interface FileChangeData {
|
|
14
16
|
addedFiles: FileItem[];
|
|
@@ -58,6 +60,10 @@ export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
58
60
|
* Specify the title text of this `<FileUploader>`
|
|
59
61
|
*/
|
|
60
62
|
labelTitle?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Maximum file size allowed in bytes. Files larger than this will be marked invalid
|
|
65
|
+
*/
|
|
66
|
+
maxFileSize?: number;
|
|
61
67
|
/**
|
|
62
68
|
* Specify if the component should accept multiple files to upload
|
|
63
69
|
*/
|
|
@@ -66,24 +72,32 @@ export interface FileUploaderProps extends HTMLAttributes<HTMLSpanElement> {
|
|
|
66
72
|
* Provide a name for the underlying `<input>` node
|
|
67
73
|
*/
|
|
68
74
|
name?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Event handler that is called after files are added to the uploader
|
|
77
|
+
*/
|
|
78
|
+
onAddFiles?: (event: React.SyntheticEvent<HTMLElement>, content: {
|
|
79
|
+
addedFiles: Array<File & {
|
|
80
|
+
invalidFileType?: boolean;
|
|
81
|
+
}>;
|
|
82
|
+
}) => void;
|
|
69
83
|
/**
|
|
70
84
|
* Provide an optional `onChange` hook that is called each time the input is changed.
|
|
71
85
|
* When 'enable-enhanced-file-uploader' feature flag is enabled:
|
|
72
86
|
* - Also fires for file deletions and clearFiles operations
|
|
73
87
|
* - Event includes enhanced file information in event.target
|
|
74
88
|
*/
|
|
75
|
-
onChange?: (event:
|
|
89
|
+
onChange?: (event: React.SyntheticEvent<HTMLElement>, data?: FileChangeData) => void;
|
|
76
90
|
/**
|
|
77
91
|
* Provide an optional `onClick` hook that is called each time the
|
|
78
92
|
* FileUploader is clicked
|
|
79
93
|
*/
|
|
80
|
-
onClick?: (event:
|
|
94
|
+
onClick?: (event: React.SyntheticEvent<HTMLElement>) => void;
|
|
81
95
|
/**
|
|
82
96
|
* Provide an optional `onDelete` hook that is called when an uploaded item is removed.
|
|
83
97
|
* When 'enable-enhanced-file-uploader' feature flag is enabled:
|
|
84
98
|
* - Event includes deleted file information in event.target
|
|
85
99
|
*/
|
|
86
|
-
onDelete?: (event:
|
|
100
|
+
onDelete?: (event: React.SyntheticEvent<HTMLElement>, data?: FileDeleteData) => void;
|
|
87
101
|
/**
|
|
88
102
|
* Specify the size of the FileUploaderButton, from a list of available
|
|
89
103
|
* sizes.
|
|
@@ -101,9 +115,10 @@ export interface FileUploaderHandle {
|
|
|
101
115
|
getCurrentFiles?: () => FileItem[];
|
|
102
116
|
}
|
|
103
117
|
declare const FileUploader: {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
(props: FileUploaderProps): React.ReactElement;
|
|
119
|
+
displayName?: string;
|
|
120
|
+
propTypes?: unknown;
|
|
121
|
+
contextTypes?: unknown;
|
|
122
|
+
defaultProps?: unknown;
|
|
108
123
|
};
|
|
109
124
|
export default FileUploader;
|
|
@@ -24,7 +24,6 @@ require('../Text/TextDirection.js');
|
|
|
24
24
|
var useId = require('../../internal/useId.js');
|
|
25
25
|
var index = require('../FeatureFlags/index.js');
|
|
26
26
|
|
|
27
|
-
// eslint-disable-next-line react/display-name -- https://github.com/carbon-design-system/carbon/issues/20452
|
|
28
27
|
const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
29
28
|
accept,
|
|
30
29
|
buttonKind,
|
|
@@ -35,8 +34,10 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
35
34
|
iconDescription,
|
|
36
35
|
labelDescription,
|
|
37
36
|
labelTitle,
|
|
37
|
+
maxFileSize,
|
|
38
38
|
multiple,
|
|
39
39
|
name,
|
|
40
|
+
onAddFiles,
|
|
40
41
|
onChange,
|
|
41
42
|
onClick,
|
|
42
43
|
onDelete,
|
|
@@ -48,20 +49,51 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
48
49
|
const enhancedFileUploaderEnabled = index.useFeatureFlag('enable-enhanced-file-uploader');
|
|
49
50
|
const [fileItems, setFileItems] = React.useState([]);
|
|
50
51
|
const [legacyFileNames, setLegacyFileNames] = React.useState([]);
|
|
51
|
-
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20452
|
|
53
|
-
const [fileObjects, setFileObjects] = React.useState(new Map());
|
|
52
|
+
const uploaderButton = /*#__PURE__*/React.createRef();
|
|
54
53
|
const nodes = [];
|
|
55
|
-
const createFileItem = file => ({
|
|
54
|
+
const createFileItem = React.useCallback(file => ({
|
|
56
55
|
name: file.name,
|
|
57
56
|
uuid: `${fileUploaderInstanceId}-${Date.now()}-${Array.from(crypto.getRandomValues(new Uint8Array(8))).map(b => b.toString(36)).join('')}`,
|
|
58
57
|
file
|
|
59
|
-
});
|
|
58
|
+
}), [fileUploaderInstanceId]);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validates files based on file size restrictions.
|
|
62
|
+
* Marks invalid files with `invalidFileType: true` but includes them in the result.
|
|
63
|
+
*
|
|
64
|
+
* Note: The `accept` prop is passed to the native HTML input element (`FileUploaderButton`),
|
|
65
|
+
* which provides UI-level filtering in the file picker dialog, but there is no JavaScript validation
|
|
66
|
+
* for file types - users can bypass this by changing the file type filter in the dialog.
|
|
67
|
+
* https://github.com/carbon-design-system/carbon/issues/21166
|
|
68
|
+
*/
|
|
69
|
+
const validateFiles = React.useCallback(files => {
|
|
70
|
+
return files.map(file => {
|
|
71
|
+
if (maxFileSize && file.size > maxFileSize) {
|
|
72
|
+
file.invalidFileType = true;
|
|
73
|
+
}
|
|
74
|
+
return file;
|
|
75
|
+
});
|
|
76
|
+
}, [maxFileSize]);
|
|
60
77
|
const handleChange = React.useCallback(evt => {
|
|
61
78
|
evt.stopPropagation();
|
|
62
|
-
const
|
|
79
|
+
const incomingFiles = Array.from(evt.target.files);
|
|
80
|
+
const filesToValidate = multiple ? incomingFiles : [incomingFiles[0]];
|
|
81
|
+
const validatedFiles = validateFiles(filesToValidate);
|
|
82
|
+
if (onAddFiles) {
|
|
83
|
+
onAddFiles(evt, {
|
|
84
|
+
addedFiles: validatedFiles
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Filter out invalid files since FileUploader cannot display them
|
|
89
|
+
// (FileUploaderDropContainer returns all files because parent uses FileUploaderItem to display errors)
|
|
90
|
+
// https://github.com/carbon-design-system/carbon/issues/21166
|
|
91
|
+
const validFiles = validatedFiles.filter(file => !file.invalidFileType);
|
|
92
|
+
if (validFiles.length === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
63
95
|
if (enhancedFileUploaderEnabled) {
|
|
64
|
-
const newFileItems =
|
|
96
|
+
const newFileItems = validFiles.map(createFileItem);
|
|
65
97
|
let updatedFileItems;
|
|
66
98
|
if (multiple) {
|
|
67
99
|
const existingNames = new Set(fileItems.map(item => item.name));
|
|
@@ -88,23 +120,14 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
88
120
|
onChange(enhancedEvent);
|
|
89
121
|
}
|
|
90
122
|
} else {
|
|
91
|
-
const filenames =
|
|
123
|
+
const filenames = validFiles.map(file => file.name);
|
|
92
124
|
const updatedFileNames = multiple ? [...new Set([...legacyFileNames, ...filenames])] : filenames;
|
|
93
125
|
setLegacyFileNames(updatedFileNames);
|
|
94
|
-
setFileObjects(prevMap => {
|
|
95
|
-
const newMap = multiple ? new Map(prevMap) : new Map();
|
|
96
|
-
newFiles.forEach(file => {
|
|
97
|
-
newMap.set(file.name, file);
|
|
98
|
-
});
|
|
99
|
-
return newMap;
|
|
100
|
-
});
|
|
101
126
|
if (onChange) {
|
|
102
127
|
onChange(evt);
|
|
103
128
|
}
|
|
104
129
|
}
|
|
105
|
-
},
|
|
106
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- https://github.com/carbon-design-system/carbon/issues/20452
|
|
107
|
-
[enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onChange]);
|
|
130
|
+
}, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, multiple, onAddFiles, onChange, createFileItem, validateFiles]);
|
|
108
131
|
const handleClick = React.useCallback((evt, {
|
|
109
132
|
index,
|
|
110
133
|
filenameStatus
|
|
@@ -141,15 +164,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
141
164
|
const deletedFileName = legacyFileNames[index];
|
|
142
165
|
const filteredArray = legacyFileNames.filter(filename => filename !== deletedFileName);
|
|
143
166
|
setLegacyFileNames(filteredArray);
|
|
144
|
-
|
|
145
|
-
// Update File objects
|
|
146
|
-
setFileObjects(prevMap => {
|
|
147
|
-
const newMap = new Map(prevMap);
|
|
148
|
-
if (deletedFileName) {
|
|
149
|
-
newMap.delete(deletedFileName);
|
|
150
|
-
}
|
|
151
|
-
return newMap;
|
|
152
|
-
});
|
|
153
167
|
if (onDelete) {
|
|
154
168
|
onDelete(evt);
|
|
155
169
|
}
|
|
@@ -159,9 +173,7 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
159
173
|
}
|
|
160
174
|
uploaderButton.current?.focus?.();
|
|
161
175
|
}
|
|
162
|
-
},
|
|
163
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps -- https://github.com/carbon-design-system/carbon/issues/20452
|
|
164
|
-
[enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick]);
|
|
176
|
+
}, [enhancedFileUploaderEnabled, fileItems, legacyFileNames, onDelete, onChange, onClick, uploaderButton]);
|
|
165
177
|
React.useImperativeHandle(ref, () => ({
|
|
166
178
|
clearFiles() {
|
|
167
179
|
if (enhancedFileUploaderEnabled) {
|
|
@@ -184,7 +196,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
184
196
|
}
|
|
185
197
|
} else {
|
|
186
198
|
setLegacyFileNames([]);
|
|
187
|
-
setFileObjects(new Map());
|
|
188
199
|
}
|
|
189
200
|
},
|
|
190
201
|
...(enhancedFileUploaderEnabled && {
|
|
@@ -193,7 +204,6 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
193
204
|
}
|
|
194
205
|
})
|
|
195
206
|
}), [enhancedFileUploaderEnabled, fileItems, onChange]);
|
|
196
|
-
const uploaderButton = /*#__PURE__*/React.createRef();
|
|
197
207
|
const classes = cx({
|
|
198
208
|
[`${prefix}--form-item`]: true,
|
|
199
209
|
[className]: className
|
|
@@ -267,6 +277,7 @@ const FileUploader = /*#__PURE__*/React.forwardRef(({
|
|
|
267
277
|
})
|
|
268
278
|
}))))));
|
|
269
279
|
});
|
|
280
|
+
FileUploader.displayName = 'FileUploader';
|
|
270
281
|
FileUploader.propTypes = {
|
|
271
282
|
/**
|
|
272
283
|
* Specify the types of files that this input should be able to receive
|
|
@@ -305,6 +316,10 @@ FileUploader.propTypes = {
|
|
|
305
316
|
* Specify the title text of this `<FileUploader>`
|
|
306
317
|
*/
|
|
307
318
|
labelTitle: PropTypes.string,
|
|
319
|
+
/**
|
|
320
|
+
* Maximum file size allowed in bytes. Files larger than this will be marked invalid
|
|
321
|
+
*/
|
|
322
|
+
maxFileSize: PropTypes.number,
|
|
308
323
|
/**
|
|
309
324
|
* Specify if the component should accept multiple files to upload
|
|
310
325
|
*/
|
|
@@ -313,6 +328,11 @@ FileUploader.propTypes = {
|
|
|
313
328
|
* Provide a name for the underlying `<input>` node
|
|
314
329
|
*/
|
|
315
330
|
name: PropTypes.string,
|
|
331
|
+
/**
|
|
332
|
+
* Event handler that is called after files are added to the uploader
|
|
333
|
+
* The event handler signature looks like `onAddFiles(evt, { addedFiles })`
|
|
334
|
+
*/
|
|
335
|
+
onAddFiles: PropTypes.func,
|
|
316
336
|
/**
|
|
317
337
|
* Provide an optional `onChange` hook that is called each time the input is
|
|
318
338
|
* changed
|
|
@@ -40,10 +40,10 @@ function FileUploaderButton({
|
|
|
40
40
|
const prefix = usePrefix.usePrefix();
|
|
41
41
|
const [labelText, setLabelText] = React.useState(ownerLabelText);
|
|
42
42
|
const [prevOwnerLabelText, setPrevOwnerLabelText] = React.useState(ownerLabelText);
|
|
43
|
-
|
|
43
|
+
const generatedId = useId.useId();
|
|
44
44
|
const {
|
|
45
45
|
current: inputId
|
|
46
|
-
} = React.useRef(id ||
|
|
46
|
+
} = React.useRef(id || generatedId);
|
|
47
47
|
const inputNode = React.useRef(null);
|
|
48
48
|
const classes = cx(`${prefix}--btn`, className, {
|
|
49
49
|
[`${prefix}--btn--${buttonKind}`]: buttonKind,
|
|
@@ -28,6 +28,10 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
|
|
|
28
28
|
* this control
|
|
29
29
|
*/
|
|
30
30
|
labelText?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum file size allowed in bytes. Files larger than this will be marked invalid
|
|
33
|
+
*/
|
|
34
|
+
maxFileSize?: number;
|
|
31
35
|
/**
|
|
32
36
|
* Specify if the component should accept multiple files to upload
|
|
33
37
|
*/
|
|
@@ -40,7 +44,7 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
|
|
|
40
44
|
* Event handler that is called after files are added to the uploader
|
|
41
45
|
*/
|
|
42
46
|
onAddFiles?: (event: React.SyntheticEvent<HTMLElement>, content: {
|
|
43
|
-
addedFiles:
|
|
47
|
+
addedFiles: AddedFile[];
|
|
44
48
|
}) => void;
|
|
45
49
|
/**
|
|
46
50
|
* Provide an optional function to be called when the button element
|
|
@@ -64,7 +68,10 @@ export interface FileUploaderDropContainerProps extends Omit<HTMLAttributes<HTML
|
|
|
64
68
|
*/
|
|
65
69
|
tabIndex?: number | string;
|
|
66
70
|
}
|
|
67
|
-
|
|
71
|
+
interface AddedFile extends File {
|
|
72
|
+
invalidFileType?: boolean;
|
|
73
|
+
}
|
|
74
|
+
declare function FileUploaderDropContainer({ accept, className, id, disabled, labelText, maxFileSize, multiple, name, onAddFiles, onClick, pattern, innerRef, ...rest }: FileUploaderDropContainerProps): import("react/jsx-runtime").JSX.Element;
|
|
68
75
|
declare namespace FileUploaderDropContainer {
|
|
69
76
|
var propTypes: {
|
|
70
77
|
/**
|
|
@@ -88,6 +95,10 @@ declare namespace FileUploaderDropContainer {
|
|
|
88
95
|
* this control
|
|
89
96
|
*/
|
|
90
97
|
labelText: PropTypes.Validator<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Maximum file size allowed in bytes. Files larger than this will be marked invalid
|
|
100
|
+
*/
|
|
101
|
+
maxFileSize: PropTypes.Requireable<number>;
|
|
91
102
|
/**
|
|
92
103
|
* Specify if the component should accept multiple files to upload
|
|
93
104
|
*/
|
|
@@ -27,6 +27,7 @@ function FileUploaderDropContainer({
|
|
|
27
27
|
id,
|
|
28
28
|
disabled,
|
|
29
29
|
labelText = 'Add file',
|
|
30
|
+
maxFileSize,
|
|
30
31
|
multiple = false,
|
|
31
32
|
name,
|
|
32
33
|
onAddFiles = noopFn.noopFn,
|
|
@@ -37,10 +38,10 @@ function FileUploaderDropContainer({
|
|
|
37
38
|
}) {
|
|
38
39
|
const prefix = usePrefix.usePrefix();
|
|
39
40
|
const inputRef = React.useRef(null);
|
|
40
|
-
|
|
41
|
+
const generatedId = useId.useId();
|
|
41
42
|
const {
|
|
42
43
|
current: uid
|
|
43
|
-
} = React.useRef(id ||
|
|
44
|
+
} = React.useRef(id || generatedId);
|
|
44
45
|
const [isActive, setActive] = React.useState(false);
|
|
45
46
|
const dropareaClasses = cx(`${prefix}--file__drop-container`, `${prefix}--file-browse-btn`, {
|
|
46
47
|
[`${prefix}--file__drop-container--drag-over`]: isActive,
|
|
@@ -48,12 +49,9 @@ function FileUploaderDropContainer({
|
|
|
48
49
|
}, className);
|
|
49
50
|
|
|
50
51
|
/**
|
|
51
|
-
* Filters the array of added files based on file type restrictions
|
|
52
|
+
* Filters the array of added files based on file type and size restrictions
|
|
52
53
|
*/
|
|
53
54
|
function validateFiles(transferredFiles) {
|
|
54
|
-
if (!accept.length) {
|
|
55
|
-
return transferredFiles;
|
|
56
|
-
}
|
|
57
55
|
const acceptedTypes = new Set(accept);
|
|
58
56
|
return transferredFiles.reduce((acc, curr) => {
|
|
59
57
|
const {
|
|
@@ -62,6 +60,13 @@ function FileUploaderDropContainer({
|
|
|
62
60
|
} = curr;
|
|
63
61
|
const fileExtensionRegExp = new RegExp(pattern, 'i');
|
|
64
62
|
const [fileExtension] = name.match(fileExtensionRegExp) ?? [];
|
|
63
|
+
if (maxFileSize && curr.size > maxFileSize) {
|
|
64
|
+
curr.invalidFileType = true;
|
|
65
|
+
return acc.concat([curr]);
|
|
66
|
+
}
|
|
67
|
+
if (!accept.length) {
|
|
68
|
+
return acc.concat([curr]);
|
|
69
|
+
}
|
|
65
70
|
if (fileExtension === undefined) {
|
|
66
71
|
return acc;
|
|
67
72
|
}
|
|
@@ -174,6 +179,10 @@ FileUploaderDropContainer.propTypes = {
|
|
|
174
179
|
* this control
|
|
175
180
|
*/
|
|
176
181
|
labelText: PropTypes.string.isRequired,
|
|
182
|
+
/**
|
|
183
|
+
* Maximum file size allowed in bytes. Files larger than this will be marked invalid
|
|
184
|
+
*/
|
|
185
|
+
maxFileSize: PropTypes.number,
|
|
177
186
|
/**
|
|
178
187
|
* Specify if the component should accept multiple files to upload
|
|
179
188
|
*/
|
|
@@ -40,10 +40,10 @@ function FileUploaderItem({
|
|
|
40
40
|
const textRef = React.useRef(null);
|
|
41
41
|
const [isEllipsisApplied, setIsEllipsisApplied] = React.useState(false);
|
|
42
42
|
const prefix = usePrefix.usePrefix();
|
|
43
|
-
|
|
43
|
+
const generatedId = useId.useId();
|
|
44
44
|
const {
|
|
45
45
|
current: id
|
|
46
|
-
} = React.useRef(uuid ||
|
|
46
|
+
} = React.useRef(uuid || generatedId);
|
|
47
47
|
const classes = cx(`${prefix}--file__selected-file`, className, {
|
|
48
48
|
[`${prefix}--file__selected-file--invalid`]: invalid,
|
|
49
49
|
[`${prefix}--file__selected-file--md`]: size === 'md',
|
|
@@ -53,11 +53,14 @@ function FileUploaderItem({
|
|
|
53
53
|
const filterSpaceName = name => {
|
|
54
54
|
return name?.replace(/\s+/g, '');
|
|
55
55
|
};
|
|
56
|
-
|
|
57
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452
|
|
58
56
|
const isEllipsisActive = element => {
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
if (!element) {
|
|
58
|
+
setIsEllipsisApplied(false);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
const isActive = element.offsetWidth < element.scrollWidth;
|
|
62
|
+
setIsEllipsisApplied(isActive);
|
|
63
|
+
return isActive;
|
|
61
64
|
};
|
|
62
65
|
React.useLayoutEffect(() => {
|
|
63
66
|
isEllipsisActive(textRef.current);
|
|
@@ -71,7 +71,7 @@ const LinkBase = /*#__PURE__*/React.forwardRef(({
|
|
|
71
71
|
ref: ref
|
|
72
72
|
}, linkProps, rest, {
|
|
73
73
|
onClick: handleOnClick
|
|
74
|
-
}), children, !inline && Icon && /*#__PURE__*/React.createElement("
|
|
74
|
+
}), children, !inline && Icon && /*#__PURE__*/React.createElement("span", {
|
|
75
75
|
className: `${prefix}--link__icon`
|
|
76
76
|
}, /*#__PURE__*/React.createElement(Icon, null)));
|
|
77
77
|
});
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This source code is licensed under the Apache-2.0 license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import { Menu } from './Menu';
|
|
8
|
-
import { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable } from './MenuItem';
|
|
9
|
-
export { Menu, MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, };
|
|
7
|
+
import { Menu, type MenuProps } from './Menu';
|
|
8
|
+
import { MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, type MenuItemProps, type MenuItemDividerProps, type MenuItemGroupProps, type MenuItemRadioGroupProps, type MenuItemSelectableProps } from './MenuItem';
|
|
9
|
+
export { Menu, MenuItem, MenuItemDivider, MenuItemGroup, MenuItemRadioGroup, MenuItemSelectable, type MenuProps, type MenuItemProps, type MenuItemDividerProps, type MenuItemGroupProps, type MenuItemRadioGroupProps, type MenuItemSelectableProps, };
|
|
10
|
+
export default Menu;
|
|
@@ -726,6 +726,7 @@ const FilterableMultiSelect = /*#__PURE__*/React.forwardRef(function FilterableM
|
|
|
726
726
|
return /*#__PURE__*/React.createElement(index$1.default.MenuItem, _rollupPluginBabelHelpers.extends({
|
|
727
727
|
key: itemProps.id,
|
|
728
728
|
"aria-label": itemText,
|
|
729
|
+
"aria-checked": isIndeterminate ? 'mixed' : isChecked,
|
|
729
730
|
isActive: isChecked && !item['isSelectAll'],
|
|
730
731
|
isHighlighted: highlightedIndex === index,
|
|
731
732
|
title: itemText,
|
|
@@ -511,6 +511,7 @@ const MultiSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
511
511
|
key: itemProps.id,
|
|
512
512
|
isActive: isChecked && !item['isSelectAll'],
|
|
513
513
|
"aria-label": itemText,
|
|
514
|
+
"aria-checked": isIndeterminate ? 'mixed' : isChecked,
|
|
514
515
|
isHighlighted: highlightedIndex === index,
|
|
515
516
|
title: itemText,
|
|
516
517
|
disabled: itemProps['aria-disabled']
|
|
@@ -135,10 +135,14 @@ export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInp
|
|
|
135
135
|
*/
|
|
136
136
|
min?: number;
|
|
137
137
|
/**
|
|
138
|
-
* Provide an optional handler that is called when the input
|
|
138
|
+
* Provide an optional handler that is called when the input is blurred.
|
|
139
|
+
*/
|
|
140
|
+
onBlur?: (event: React.FocusEvent<HTMLInputElement>, value?: string | number) => void;
|
|
141
|
+
/**
|
|
142
|
+
* Provide an optional handler that is called when the stepper
|
|
139
143
|
* buttons are blurred.
|
|
140
144
|
*/
|
|
141
|
-
|
|
145
|
+
onStepperBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
|
142
146
|
/**
|
|
143
147
|
* Provide an optional handler that is called when the internal state of
|
|
144
148
|
* NumberInput changes. This handler is called with event and state info.
|
|
@@ -128,6 +128,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
128
128
|
max,
|
|
129
129
|
min,
|
|
130
130
|
onBlur,
|
|
131
|
+
onStepperBlur,
|
|
131
132
|
onChange,
|
|
132
133
|
onClick,
|
|
133
134
|
onKeyUp,
|
|
@@ -428,6 +429,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
428
429
|
if (disableWheelProp) {
|
|
429
430
|
e.target.removeEventListener('wheel', disableWheel);
|
|
430
431
|
}
|
|
432
|
+
let parsedValueForBlur;
|
|
431
433
|
if (type === 'text') {
|
|
432
434
|
// When isControlled, the current inputValue needs re-parsed
|
|
433
435
|
// because the consumer's onChange hasn't been called yet and
|
|
@@ -443,6 +445,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
443
445
|
// causing the _numberValue to mismatch the formatted value in
|
|
444
446
|
// the input. To avoid this, formattedValue is re-parsed.
|
|
445
447
|
const parsedFormattedNewValue = numberParser.parse(formattedValue);
|
|
448
|
+
parsedValueForBlur = parsedFormattedNewValue;
|
|
446
449
|
if (onChange && isValid) {
|
|
447
450
|
const state = {
|
|
448
451
|
value: parsedFormattedNewValue,
|
|
@@ -468,7 +471,12 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
468
471
|
}
|
|
469
472
|
}
|
|
470
473
|
if (onBlur) {
|
|
471
|
-
|
|
474
|
+
if (type === 'number') {
|
|
475
|
+
onBlur(e, value);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const parsedTextValue = parsedValueForBlur ?? (isControlled ? numberParser.parse(inputValue) : numberValue);
|
|
479
|
+
onBlur(e, parsedTextValue);
|
|
472
480
|
}
|
|
473
481
|
},
|
|
474
482
|
pattern: pattern,
|
|
@@ -488,7 +496,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
488
496
|
className: `${prefix}--number__control-btn down-icon`,
|
|
489
497
|
disabled: disabled || readOnly,
|
|
490
498
|
onClick: event => handleStepperClick(event, 'down'),
|
|
491
|
-
onBlur:
|
|
499
|
+
onBlur: onStepperBlur,
|
|
492
500
|
tabIndex: -1,
|
|
493
501
|
title: decrementNumLabel || iconDescription,
|
|
494
502
|
type: "button"
|
|
@@ -501,7 +509,7 @@ const NumberInput = /*#__PURE__*/React.forwardRef((props, forwardRef) => {
|
|
|
501
509
|
className: `${prefix}--number__control-btn up-icon`,
|
|
502
510
|
disabled: disabled || readOnly,
|
|
503
511
|
onClick: event => handleStepperClick(event, 'up'),
|
|
504
|
-
onBlur:
|
|
512
|
+
onBlur: onStepperBlur,
|
|
505
513
|
tabIndex: -1,
|
|
506
514
|
title: incrementNumLabel || iconDescription,
|
|
507
515
|
type: "button"
|
|
@@ -612,10 +620,14 @@ NumberInput.propTypes = {
|
|
|
612
620
|
*/
|
|
613
621
|
stepStartValue: PropTypes.number,
|
|
614
622
|
/**
|
|
615
|
-
* Provide an optional handler that is called when the input
|
|
616
|
-
* buttons are blurred.
|
|
623
|
+
* Provide an optional handler that is called when the input is blurred.
|
|
617
624
|
*/
|
|
618
625
|
onBlur: PropTypes.func,
|
|
626
|
+
/**
|
|
627
|
+
* Provide an optional handler that is called when the stepper
|
|
628
|
+
* buttons are blurred.
|
|
629
|
+
*/
|
|
630
|
+
onStepperBlur: PropTypes.func,
|
|
619
631
|
/**
|
|
620
632
|
* Provide an optional handler that is called when the internal state of
|
|
621
633
|
* NumberInput changes. This handler is called with event and state info.
|
|
@@ -11,16 +11,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
11
11
|
|
|
12
12
|
var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js');
|
|
13
13
|
var iconsReact = require('@carbon/icons-react');
|
|
14
|
-
var cx = require('classnames');
|
|
15
|
-
var PropTypes = require('prop-types');
|
|
16
14
|
var React = require('react');
|
|
15
|
+
var index = require('../IconButton/index.js');
|
|
16
|
+
var PropTypes = require('prop-types');
|
|
17
17
|
var Select = require('../Select/Select.js');
|
|
18
18
|
require('../Select/Select.Skeleton.js');
|
|
19
19
|
var SelectItem = require('../SelectItem/SelectItem.js');
|
|
20
|
+
var cx = require('classnames');
|
|
21
|
+
var isEqual = require('react-fast-compare');
|
|
20
22
|
var useId = require('../../internal/useId.js');
|
|
21
23
|
var usePrefix = require('../../internal/usePrefix.js');
|
|
22
|
-
var index = require('../IconButton/index.js');
|
|
23
|
-
var isEqual = require('react-fast-compare');
|
|
24
24
|
|
|
25
25
|
var _CaretLeft, _CaretRight;
|
|
26
26
|
function mapPageSizesToObject(sizes) {
|
|
@@ -270,7 +270,7 @@ const Pagination = /*#__PURE__*/React.forwardRef(({
|
|
|
270
270
|
onClick: decrementPage,
|
|
271
271
|
ref: backBtnRef
|
|
272
272
|
}, _CaretLeft || (_CaretLeft = /*#__PURE__*/React.createElement(iconsReact.CaretLeft, null))), /*#__PURE__*/React.createElement(index.IconButton, {
|
|
273
|
-
align: "top
|
|
273
|
+
align: "top",
|
|
274
274
|
disabled: forwardButtonDisabled || isLastPage,
|
|
275
275
|
kind: "ghost",
|
|
276
276
|
className: forwardButtonClasses,
|
|
@@ -62,27 +62,41 @@ forwardRef) {
|
|
|
62
62
|
const caretRef = React.useRef(null);
|
|
63
63
|
const popover = React.useRef(null);
|
|
64
64
|
const enableFloatingStyles = index.useFeatureFlag('enable-v12-dynamic-floating-styles') || autoAlign;
|
|
65
|
+
const lastClickWasInsidePopoverContent = React.useRef(false);
|
|
65
66
|
let align = mapPopoverAlign.mapPopoverAlign(initialAlign);
|
|
66
67
|
|
|
68
|
+
// Tracks clicks inside PopoverContent to prevent it from closing when clicked, this handles an edge
|
|
69
|
+
// case where the popover will close when clicking non-focusable elements (e.g. text)
|
|
70
|
+
useEvent.useEvent(popover, 'mousedown', event => {
|
|
71
|
+
const target = event.target;
|
|
72
|
+
lastClickWasInsidePopoverContent.current = refs.floating.current?.contains(target) || false;
|
|
73
|
+
|
|
74
|
+
// reset flag
|
|
75
|
+
if (lastClickWasInsidePopoverContent.current) {
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
lastClickWasInsidePopoverContent.current = false;
|
|
78
|
+
}, 0);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
67
82
|
// The `Popover` should close whenever it and its children loses focus
|
|
68
83
|
useEvent.useEvent(popover, 'focusout', event => {
|
|
69
84
|
const relatedTarget = event.relatedTarget;
|
|
70
|
-
if (isTabTip) {
|
|
71
|
-
if (relatedTarget && !popover.current?.contains(relatedTarget)) {
|
|
72
|
-
onRequestClose?.();
|
|
73
|
-
}
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
85
|
if (!relatedTarget) {
|
|
86
|
+
// do not close if PopoverContent was clicked
|
|
87
|
+
if (lastClickWasInsidePopoverContent.current) {
|
|
88
|
+
lastClickWasInsidePopoverContent.current = false;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
77
91
|
onRequestClose?.();
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const isOutsideFloating = enableFloatingStyles && refs.floating.current ? !refs.floating.current.contains(relatedTarget) : true;
|
|
92
|
+
} else if (relatedTarget && !popover.current?.contains(relatedTarget)) {
|
|
93
|
+
const isOutsideFloating = enableFloatingStyles && refs.floating.current ? !refs.floating.current.contains(relatedTarget) : true;
|
|
94
|
+
const isFocusableWrapper = relatedTarget && popover.current && relatedTarget.contains(popover.current);
|
|
82
95
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
96
|
+
// Only close if focus moved outside both containers and not to an interactive parent wrapper
|
|
97
|
+
if (isOutsideFloating && !isFocusableWrapper) {
|
|
98
|
+
onRequestClose?.();
|
|
99
|
+
}
|
|
86
100
|
}
|
|
87
101
|
});
|
|
88
102
|
useEvent.useWindowEvent('click', ({
|