@mezzanine-ui/react 1.0.0-rc.6 → 1.0.0-rc.7
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/AutoComplete/AutoComplete.d.ts +25 -9
- package/AutoComplete/AutoComplete.js +84 -17
- package/AutoComplete/AutoCompleteInside.d.ts +54 -0
- package/AutoComplete/AutoCompleteInside.js +17 -0
- package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
- package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
- package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
- package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
- package/Breadcrumb/typings.d.ts +1 -1
- package/COMPONENTS.md +2 -2
- package/Checkbox/Checkbox.js +24 -3
- package/Cropper/Cropper.d.ts +1 -1
- package/Description/Description.d.ts +1 -1
- package/Description/Description.js +1 -1
- package/Description/DescriptionTitle.d.ts +6 -1
- package/Description/DescriptionTitle.js +2 -2
- package/Drawer/Drawer.d.ts +39 -34
- package/Drawer/Drawer.js +33 -35
- package/Dropdown/Dropdown.d.ts +16 -1
- package/Dropdown/Dropdown.js +156 -9
- package/Dropdown/DropdownItem.d.ts +26 -2
- package/Dropdown/DropdownItem.js +91 -43
- package/Dropdown/DropdownItemCard.d.ts +3 -2
- package/Dropdown/DropdownItemCard.js +8 -5
- package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
- package/Dropdown/dropdownKeydownHandler.js +14 -7
- package/FilterArea/Filter.d.ts +25 -2
- package/FilterArea/Filter.js +23 -0
- package/FilterArea/FilterArea.d.ts +43 -4
- package/FilterArea/FilterArea.js +35 -2
- package/FilterArea/FilterLine.d.ts +19 -0
- package/FilterArea/FilterLine.js +19 -0
- package/Input/SpinnerButton/SpinnerButton.js +1 -1
- package/Modal/Modal.d.ts +22 -86
- package/Modal/Modal.js +4 -2
- package/Modal/ModalBodyForVerification.js +3 -1
- package/NotificationCenter/NotificationCenter.d.ts +21 -9
- package/NotificationCenter/NotificationCenter.js +22 -10
- package/NotificationCenter/NotificationCenterDrawer.d.ts +52 -1
- package/NotificationCenter/NotificationCenterDrawer.js +2 -2
- package/OverflowTooltip/OverflowTooltip.js +46 -5
- package/PageFooter/PageFooter.js +6 -14
- package/Pagination/PaginationPageSize.js +1 -1
- package/README.md +34 -4
- package/Radio/Radio.js +16 -2
- package/Table/Table.js +1 -1
- package/TimePicker/TimePicker.js +1 -1
- package/TimeRangePicker/TimeRangePicker.js +1 -1
- package/Toggle/Toggle.d.ts +1 -1
- package/Toggle/Toggle.js +1 -1
- package/Upload/Upload.d.ts +13 -7
- package/Upload/Upload.js +55 -20
- package/Upload/UploadItem.js +4 -1
- package/Upload/UploadPictureCard.d.ts +5 -0
- package/Upload/UploadPictureCard.js +8 -5
- package/Upload/Uploader.d.ts +32 -31
- package/Upload/Uploader.js +10 -9
- package/index.d.ts +3 -3
- package/index.js +1 -1
- package/llms.txt +128 -9
- package/package.json +5 -4
package/Radio/Radio.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef, useContext } from 'react';
|
|
3
|
+
import { forwardRef, useContext, useRef, useCallback } from 'react';
|
|
4
4
|
import { radioClasses } from '@mezzanine-ui/core/radio';
|
|
5
5
|
import InputCheck from '../_internal/InputCheck/InputCheck.js';
|
|
6
6
|
import { useRadioControlValue } from '../Form/useRadioControlValue.js';
|
|
@@ -56,13 +56,27 @@ const Radio = forwardRef(function Radio(props, ref) {
|
|
|
56
56
|
value,
|
|
57
57
|
});
|
|
58
58
|
const size = (_a = sizeProp !== null && sizeProp !== void 0 ? sizeProp : sizeFromGroup) !== null && _a !== void 0 ? _a : 'main';
|
|
59
|
+
const radioInputRef = useRef(null);
|
|
60
|
+
const textInputRef = useRef(null);
|
|
61
|
+
const handleRadioChange = useCallback((event) => {
|
|
62
|
+
var _a;
|
|
63
|
+
onChange(event);
|
|
64
|
+
if (withInputConfig && !withInputConfig.disabled) {
|
|
65
|
+
(_a = textInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
66
|
+
}
|
|
67
|
+
}, [onChange, withInputConfig]);
|
|
59
68
|
return (jsxs("div", { ref: ref, className: cx(radioClasses.wrapper, className), children: [jsx(InputCheck, { ...rest, control: jsxs("span", { className: cx(radioClasses.host, radioClasses.size(size), {
|
|
60
69
|
[radioClasses.segmented]: type === 'segment',
|
|
61
70
|
[radioClasses.checked]: checked,
|
|
62
71
|
[radioClasses.error]: error,
|
|
63
72
|
}), children: [type === 'segment' && (jsxs("span", { className: cx(radioClasses.segmentedContainer, {
|
|
64
73
|
[radioClasses.segmentedContainerWithIconText]: !!children && !!icon,
|
|
65
|
-
}), children: [icon && jsx(Icon, { icon: icon, size: 16 }), children] })), jsx("input", { ...restInputProps, "aria-checked": checked, "aria-disabled": disabled, checked: checked, disabled: disabled, id: inputId, onChange:
|
|
74
|
+
}), children: [icon && jsx(Icon, { icon: icon, size: 16 }), children] })), jsx("input", { ...restInputProps, "aria-checked": checked, "aria-disabled": disabled, checked: checked, disabled: disabled, id: inputId, onChange: handleRadioChange, name: name, ref: radioInputRef, type: "radio", value: value })] }), disabled: disabled, error: error, hint: hint, htmlFor: inputId, segmentedStyle: type === 'segment', size: size, children: type === 'radio' && children }), type === 'radio' && withInputConfig && (jsx("div", { style: { width: (_b = withInputConfig.width) !== null && _b !== void 0 ? _b : 120 }, children: jsx(Input, { ...withInputConfig, inputRef: textInputRef, inputProps: {
|
|
75
|
+
onClick: () => {
|
|
76
|
+
var _a;
|
|
77
|
+
(_a = radioInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
78
|
+
},
|
|
79
|
+
}, variant: "base", placeholder: (_c = withInputConfig.placeholder) !== null && _c !== void 0 ? _c : 'Placeholder' }) }))] }));
|
|
66
80
|
});
|
|
67
81
|
|
|
68
82
|
export { Radio as default };
|
package/Table/Table.js
CHANGED
package/TimePicker/TimePicker.js
CHANGED
|
@@ -204,7 +204,7 @@ const TimePicker = forwardRef(function TimePicker(props, ref) {
|
|
|
204
204
|
}
|
|
205
205
|
};
|
|
206
206
|
const suffixIcon = (jsx(Icon, { "aria-label": "Open time picker", icon: ClockIcon, onClick: readOnly ? undefined : onIconClick }));
|
|
207
|
-
return (jsxs(Fragment, { children: [jsx(PickerTrigger, { ...restTriggerProps, ref: triggerComposedRef, className: className, clearable: clearable, disabled: disabled, error: error, format: resolvedFormat, fullWidth: fullWidth, inputProps: resolvedInputProps, inputRef: inputRef, hoverValue: open && !inputValue && pendingValue
|
|
207
|
+
return (jsxs(Fragment, { children: [jsx(PickerTrigger, { ...restTriggerProps, ref: triggerComposedRef, className: className, clearable: clearable, disabled: disabled, error: error, forceShowClearable: !!internalValue, format: resolvedFormat, fullWidth: fullWidth, hideSuffixWhenClearable: clearable, inputProps: resolvedInputProps, inputRef: inputRef, hoverValue: open && !inputValue && pendingValue
|
|
208
208
|
? ((_b = formatToString(locale, pendingValue, resolvedFormat)) !== null && _b !== void 0 ? _b : undefined)
|
|
209
209
|
: undefined, onChange: (e) => {
|
|
210
210
|
const val = e.target.value;
|
|
@@ -150,7 +150,7 @@ const TimeRangePicker = forwardRef(function TimeRangePicker(props, ref) {
|
|
|
150
150
|
});
|
|
151
151
|
/** Suffix icon */
|
|
152
152
|
const suffixIcon = (jsx(Icon, { "aria-label": "Open time picker", icon: ClockIcon, onClick: readOnly ? undefined : onIconClick }));
|
|
153
|
-
return (jsxs(Fragment, { children: [jsx(RangePickerTrigger, { className: className, clearable: clearable, disabled: disabled, error: error, errorMessagesFrom: errorMessagesFrom, errorMessagesTo: errorMessagesTo, format: resolvedFormat, fullWidth: fullWidth, inputFromPlaceholder: inputFromPlaceholder, inputFromProps: inputFromProps, inputFromRef: inputFromRef, inputFromValue: inputFromValue, inputToPlaceholder: inputToPlaceholder, inputToProps: inputToProps, inputToRef: inputToRef, inputToValue: inputToValue, onClear: onClearHandler, onFromFocus: onFromFocusHandler, onIconClick: onIconClick, onInputFromChange: onInputFromChange, onInputToChange: onInputToChange, onToFocus: onToFocusHandler, prefix: prefix, readOnly: readOnly, ref: triggerComposedRef, required: required, size: size, suffixActionIcon: suffixIcon, validateFrom: validateFrom, validateTo: validateTo }), focusedInput && (jsx(TimePickerPanel, { anchor: panelAnchor, fadeProps: fadeProps, hideHour: hideHour, hideMinute: hideMinute, hideSecond: hideSecond, hourStep: hourStep, minuteStep: minuteStep, onChange: onPanelChange, onCancel: onCancel, onConfirm: onConfirm, open: open, popperProps: popperProps, ref: panelRef, secondStep: secondStep, value: panelValue }))] }));
|
|
153
|
+
return (jsxs(Fragment, { children: [jsx(RangePickerTrigger, { className: className, clearable: clearable, disabled: disabled, error: error, errorMessagesFrom: errorMessagesFrom, errorMessagesTo: errorMessagesTo, forceShowClearable: internalValue.some(Boolean), format: resolvedFormat, fullWidth: fullWidth, hideSuffixWhenClearable: clearable, inputFromPlaceholder: inputFromPlaceholder, inputFromProps: inputFromProps, inputFromRef: inputFromRef, inputFromValue: inputFromValue, inputToPlaceholder: inputToPlaceholder, inputToProps: inputToProps, inputToRef: inputToRef, inputToValue: inputToValue, onClear: onClearHandler, onFromFocus: onFromFocusHandler, onIconClick: onIconClick, onInputFromChange: onInputFromChange, onInputToChange: onInputToChange, onToFocus: onToFocusHandler, prefix: prefix, readOnly: readOnly, ref: triggerComposedRef, required: required, size: size, suffixActionIcon: suffixIcon, validateFrom: validateFrom, validateTo: validateTo }), focusedInput && (jsx(TimePickerPanel, { anchor: panelAnchor, fadeProps: fadeProps, hideHour: hideHour, hideMinute: hideMinute, hideSecond: hideSecond, hourStep: hourStep, minuteStep: minuteStep, onChange: onPanelChange, onCancel: onCancel, onConfirm: onConfirm, open: open, popperProps: popperProps, ref: panelRef, secondStep: secondStep, value: panelValue }))] }));
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
export { TimeRangePicker as default };
|
package/Toggle/Toggle.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface ToggleProps extends Omit<NativeElementPropsWithoutKeyAndRef<'di
|
|
|
40
40
|
supportingText?: string;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
43
|
+
* 切換開關元件,用於表示開/關二元狀態。
|
|
44
44
|
*
|
|
45
45
|
* 支援受控(`checked` + `onChange`)與非受控(`defaultChecked`)兩種用法;
|
|
46
46
|
* `label` 顯示於開關右側,`supportingText` 顯示於 label 下方作為輔助說明。
|
package/Toggle/Toggle.js
CHANGED
|
@@ -8,7 +8,7 @@ import { FormControlContext } from '../Form/FormControlContext.js';
|
|
|
8
8
|
import cx from 'clsx';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* 切換開關元件,用於表示開/關二元狀態。
|
|
12
12
|
*
|
|
13
13
|
* 支援受控(`checked` + `onChange`)與非受控(`defaultChecked`)兩種用法;
|
|
14
14
|
* `label` 顯示於開關右側,`supportingText` 顯示於 label 下方作為輔助說明。
|
package/Upload/Upload.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type UploadItemStatus, type UploadMode, type UploadSize } from '@mezzan
|
|
|
2
2
|
import { type ReactNode } from 'react';
|
|
3
3
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
4
4
|
import type { UploaderProps } from './Uploader';
|
|
5
|
+
import type { UploadPictureCardAriaLabels } from './UploadPictureCard';
|
|
5
6
|
export interface UploadFile {
|
|
6
7
|
/**
|
|
7
8
|
* The file object.
|
|
@@ -51,13 +52,17 @@ export interface UploadProps extends Omit<NativeElementPropsWithoutKeyAndRef<'di
|
|
|
51
52
|
* @default false
|
|
52
53
|
*/
|
|
53
54
|
disabled?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Hints passed into the Uploader dropzone area. Only visible in dropzone modes (`list`, `card-wall`).
|
|
57
|
+
*/
|
|
58
|
+
dropzoneHints?: UploaderProps['hints'];
|
|
54
59
|
/**
|
|
55
60
|
* Controlled file list for the upload component.
|
|
56
61
|
* Provide this along with `onChange` to fully control the file state.
|
|
57
62
|
*/
|
|
58
63
|
files?: UploadFile[];
|
|
59
64
|
/**
|
|
60
|
-
* Array of hints
|
|
65
|
+
* Array of hints displayed outside the uploader area. Visible in all modes.
|
|
61
66
|
*/
|
|
62
67
|
hints?: UploaderProps['hints'];
|
|
63
68
|
/**
|
|
@@ -77,11 +82,6 @@ export interface UploadProps extends Omit<NativeElementPropsWithoutKeyAndRef<'di
|
|
|
77
82
|
* The react ref passed to input element.
|
|
78
83
|
*/
|
|
79
84
|
inputRef?: UploaderProps['inputRef'];
|
|
80
|
-
/**
|
|
81
|
-
* Whether to fill the width of the container.
|
|
82
|
-
* @default false
|
|
83
|
-
*/
|
|
84
|
-
isFillWidth?: boolean;
|
|
85
85
|
/**
|
|
86
86
|
* Maximum number of files allowed to upload.
|
|
87
87
|
* If exceeded, the excess files will be ignored.
|
|
@@ -89,7 +89,8 @@ export interface UploadProps extends Omit<NativeElementPropsWithoutKeyAndRef<'di
|
|
|
89
89
|
maxFiles?: number;
|
|
90
90
|
/**
|
|
91
91
|
* The display mode for the upload component.
|
|
92
|
-
* - 'list': Display files as a list using UploadItem
|
|
92
|
+
* - 'list': Display files as a list using UploadItem (with dropzone)
|
|
93
|
+
* - 'basic-list': Display files as a list without drag-and-drop
|
|
93
94
|
* - 'button-list': Display uploader as a button with files in list format
|
|
94
95
|
* - 'cards': Display image files as picture cards using UploadPictureCard
|
|
95
96
|
* - 'card-wall': Display uploader at top with image files as picture cards below
|
|
@@ -129,6 +130,11 @@ export interface UploadProps extends Omit<NativeElementPropsWithoutKeyAndRef<'di
|
|
|
129
130
|
* This will be used when a file's status becomes 'error' and no errorIcon is provided.
|
|
130
131
|
*/
|
|
131
132
|
errorIcon?: ReactNode;
|
|
133
|
+
/**
|
|
134
|
+
* Aria labels passed to picture cards in `cards` / `card-wall` mode.
|
|
135
|
+
* Useful for customizing text such as "Click to Replace".
|
|
136
|
+
*/
|
|
137
|
+
ariaLabels?: UploadPictureCardAriaLabels;
|
|
132
138
|
/**
|
|
133
139
|
* Fired when files list changes.
|
|
134
140
|
*/
|
package/Upload/Upload.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
3
|
+
import { forwardRef, useRef, useState, useEffect, useMemo, useCallback } from 'react';
|
|
4
4
|
import { defaultUploadPictureCardErrorMessage, uploadClasses } from '@mezzanine-ui/core/upload';
|
|
5
5
|
import { DangerousFilledIcon, InfoFilledIcon } from '@mezzanine-ui/icons';
|
|
6
|
+
import { isImageFile } from './upload-utils.js';
|
|
6
7
|
import Uploader from './Uploader.js';
|
|
7
8
|
import UploadItem from './UploadItem.js';
|
|
8
|
-
import { isImageFile } from './upload-utils.js';
|
|
9
9
|
import UploadPictureCard from './UploadPictureCard.js';
|
|
10
10
|
import Icon from '../Icon/Icon.js';
|
|
11
|
+
import MediaPreviewModal from '../Modal/MediaPreviewModal.js';
|
|
11
12
|
import cx from 'clsx';
|
|
12
13
|
|
|
13
14
|
const Upload = forwardRef(function Upload(props, ref) {
|
|
14
|
-
|
|
15
|
+
var _a, _b;
|
|
16
|
+
const { accept, className, disabled = false, dropzoneHints, mode = 'list', size = 'main', showFileSize = true, files: controlledFiles = [], onUpload, onDelete, onReload, onDownload, onZoomIn, onChange, id, name, multiple = false, maxFiles, hints, uploaderLabel, uploaderIcon, inputRef, inputProps, onMaxFilesExceeded, errorMessage, errorIcon, ariaLabels, ...rest } = props;
|
|
15
17
|
const files = controlledFiles;
|
|
16
18
|
const filesRef = useRef(files);
|
|
17
19
|
const replaceFileIdRef = useRef(null);
|
|
18
20
|
const replaceInputRef = useRef(null);
|
|
21
|
+
const [previewFile, setPreviewFile] = useState(null);
|
|
22
|
+
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
|
19
23
|
useEffect(() => {
|
|
20
24
|
filesRef.current = files;
|
|
21
25
|
}, [files]);
|
|
@@ -224,10 +228,34 @@ const Upload = forwardRef(function Upload(props, ref) {
|
|
|
224
228
|
}, [findFileById, onDownload]);
|
|
225
229
|
const handleZoomIn = useCallback((fileId) => {
|
|
226
230
|
const file = findFileById(fileId);
|
|
227
|
-
if (file
|
|
231
|
+
if (!file)
|
|
232
|
+
return;
|
|
233
|
+
setPreviewFile(file);
|
|
234
|
+
setIsPreviewOpen(true);
|
|
235
|
+
if (file.file) {
|
|
228
236
|
onZoomIn === null || onZoomIn === void 0 ? void 0 : onZoomIn(fileId, file.file);
|
|
229
237
|
}
|
|
230
238
|
}, [findFileById, onZoomIn]);
|
|
239
|
+
const [previewObjectUrl, setPreviewObjectUrl] = useState(null);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (!previewFile || previewFile.url || !previewFile.file) {
|
|
242
|
+
setPreviewObjectUrl(null);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (!isImageFile(previewFile.file, previewFile.url)) {
|
|
246
|
+
setPreviewObjectUrl(null);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const url = URL.createObjectURL(previewFile.file);
|
|
250
|
+
setPreviewObjectUrl(url);
|
|
251
|
+
return () => {
|
|
252
|
+
URL.revokeObjectURL(url);
|
|
253
|
+
};
|
|
254
|
+
}, [previewFile]);
|
|
255
|
+
const previewIsImage = previewFile
|
|
256
|
+
? isImageFile(previewFile.file, previewFile.url)
|
|
257
|
+
: false;
|
|
258
|
+
const previewImageSrc = (_b = (_a = previewFile === null || previewFile === void 0 ? void 0 : previewFile.url) !== null && _a !== void 0 ? _a : previewObjectUrl) !== null && _b !== void 0 ? _b : '';
|
|
231
259
|
const { imageFiles, nonImageFiles } = useMemo(() => {
|
|
232
260
|
const images = [];
|
|
233
261
|
const nonImages = [];
|
|
@@ -246,19 +274,23 @@ const Upload = forwardRef(function Upload(props, ref) {
|
|
|
246
274
|
const uploaderConfig = useMemo(() => {
|
|
247
275
|
const config = {
|
|
248
276
|
list: {
|
|
249
|
-
|
|
277
|
+
mode: 'dropzone',
|
|
278
|
+
type: 'base',
|
|
279
|
+
},
|
|
280
|
+
'basic-list': {
|
|
281
|
+
mode: 'basic',
|
|
250
282
|
type: 'base',
|
|
251
283
|
},
|
|
252
284
|
'button-list': {
|
|
253
285
|
type: 'button',
|
|
254
286
|
},
|
|
255
287
|
cards: {
|
|
288
|
+
mode: 'basic',
|
|
256
289
|
type: 'base',
|
|
257
|
-
isFillWidth: false,
|
|
258
290
|
},
|
|
259
291
|
'card-wall': {
|
|
292
|
+
mode: 'basic',
|
|
260
293
|
type: 'base',
|
|
261
|
-
isFillWidth: false,
|
|
262
294
|
},
|
|
263
295
|
};
|
|
264
296
|
return config[mode];
|
|
@@ -266,7 +298,7 @@ const Upload = forwardRef(function Upload(props, ref) {
|
|
|
266
298
|
const topUploaderConfig = useMemo(() => {
|
|
267
299
|
if (mode === 'card-wall') {
|
|
268
300
|
return {
|
|
269
|
-
|
|
301
|
+
mode: 'dropzone',
|
|
270
302
|
type: 'base',
|
|
271
303
|
};
|
|
272
304
|
}
|
|
@@ -296,18 +328,19 @@ const Upload = forwardRef(function Upload(props, ref) {
|
|
|
296
328
|
handleDownload,
|
|
297
329
|
handleReload,
|
|
298
330
|
]);
|
|
299
|
-
const uploaderElement = (jsx(Uploader, { accept: accept, disabled: effectiveDisabled, id: id, name: name, multiple: multiple, label: uploaderLabel, icon: uploaderIcon, inputRef: inputRef, inputProps: inputProps, hints:
|
|
300
|
-
const topUploaderElement = topUploaderConfig ? (jsx(Uploader, { accept: accept, disabled: effectiveDisabled, id: id ? `${id}-top` : undefined, name: name, multiple: multiple, label: uploaderLabel, icon: uploaderIcon, inputRef: inputRef, inputProps: inputProps, hints:
|
|
331
|
+
const uploaderElement = (jsx(Uploader, { accept: accept, disabled: effectiveDisabled, id: id, name: name, multiple: multiple, label: uploaderLabel, icon: uploaderIcon, inputRef: inputRef, inputProps: inputProps, hints: uploaderConfig.mode === 'dropzone' ? dropzoneHints : undefined, onUpload: handleUpload, ...uploaderConfig }));
|
|
332
|
+
const topUploaderElement = topUploaderConfig ? (jsx(Uploader, { accept: accept, disabled: effectiveDisabled, id: id ? `${id}-top` : undefined, name: name, multiple: multiple, label: uploaderLabel, icon: uploaderIcon, inputRef: inputRef, inputProps: inputProps, hints: dropzoneHints, onUpload: handleUpload, ...topUploaderConfig })) : null;
|
|
333
|
+
// When a top uploader exists (card-wall mode), the inline card uploader is not needed.
|
|
334
|
+
const inlineCardUploaderElement = topUploaderElement ? null : uploaderElement;
|
|
301
335
|
const hintsElement = useMemo(() => {
|
|
302
|
-
if (!hints ||
|
|
303
|
-
hints.length === 0 ||
|
|
304
|
-
mode === 'list' ||
|
|
305
|
-
mode === 'card-wall')
|
|
336
|
+
if (!hints || hints.length === 0)
|
|
306
337
|
return null;
|
|
307
|
-
const hintsClassName = mode === '
|
|
338
|
+
const hintsClassName = mode === 'list' || mode === 'card-wall' || mode === 'cards'
|
|
339
|
+
? uploadClasses.fillWidthHints
|
|
340
|
+
: uploadClasses.hints;
|
|
308
341
|
return (jsx("ul", { className: hintsClassName, children: hints.map((hint) => (jsxs("li", { className: uploadClasses.hint(hint.type || 'info'), children: [jsx(Icon, { icon: hint.type === 'info' ? InfoFilledIcon : DangerousFilledIcon, color: hint.type === 'info' ? 'info' : 'error', size: 14 }), hint.label] }, hint.label))) }));
|
|
309
342
|
}, [hints, mode]);
|
|
310
|
-
return (jsxs("div", { ref: ref, className: cx(uploadClasses.host, className, mode === 'cards' && uploadClasses.hostCards), ...rest, children: [topUploaderElement, !shouldUsePictureCard && (jsxs("div", { className: uploadClasses.uploadButtonList, children: [uploaderElement,
|
|
343
|
+
return (jsxs("div", { ref: ref, className: cx(uploadClasses.host, className, mode === 'cards' && uploadClasses.hostCards), ...rest, children: [topUploaderElement, mode === 'card-wall' && hintsElement, !shouldUsePictureCard && (jsxs("div", { className: uploadClasses.uploadButtonList, children: [uploaderElement, hintsElement] })), jsxs("div", { className: cx(uploadClasses.uploadList, shouldUsePictureCard && uploadClasses.uploadListCards), children: [shouldUsePictureCard && (jsxs(Fragment, { children: [imageFiles.map((uploadFile) => (jsx(UploadPictureCard, { ariaLabels: ariaLabels, file: uploadFile.file, url: uploadFile.url, id: uploadFile.id, status: uploadFile.status, size: size, disabled: disabled, errorMessage: uploadFile.errorMessage, onDelete: () => handleDelete(uploadFile.id), onReload: () => handleReload(uploadFile.id), ...(!isSingleFileCardMode && {
|
|
311
344
|
onDownload: () => handleDownload(uploadFile.id),
|
|
312
345
|
onZoomIn: () => handleZoomIn(uploadFile.id),
|
|
313
346
|
}), ...(isSingleFileCardMode && {
|
|
@@ -317,17 +350,19 @@ const Upload = forwardRef(function Upload(props, ref) {
|
|
|
317
350
|
replaceFileIdRef.current = uploadFile.id;
|
|
318
351
|
(_a = replaceInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
319
352
|
},
|
|
320
|
-
}) }, uploadFile.id))),
|
|
353
|
+
}) }, uploadFile.id))), nonImageFiles.map((uploadFile) => (jsx(UploadPictureCard, { ariaLabels: ariaLabels, file: uploadFile.file, url: uploadFile.url, id: uploadFile.id, status: uploadFile.status, size: size, disabled: disabled, errorMessage: uploadFile.errorMessage, onDelete: () => handleDelete(uploadFile.id), onReload: () => handleReload(uploadFile.id), ...(!isSingleFileCardMode && {
|
|
354
|
+
onDownload: () => handleDownload(uploadFile.id),
|
|
355
|
+
}) }, uploadFile.id))), !isSingleFileCardMode && inlineCardUploaderElement, isSingleFileCardMode &&
|
|
321
356
|
imageFiles.length === 0 &&
|
|
322
|
-
|
|
357
|
+
inlineCardUploaderElement, isSingleFileCardMode && (jsx("input", { ref: replaceInputRef, accept: accept, style: { display: 'none' }, type: "file", onChange: (e) => {
|
|
323
358
|
var _a;
|
|
324
359
|
const selectedFiles = Array.from((_a = e.target.files) !== null && _a !== void 0 ? _a : []);
|
|
325
360
|
e.target.value = '';
|
|
326
361
|
if (selectedFiles.length)
|
|
327
362
|
handleUpload(selectedFiles);
|
|
328
|
-
} }))] })), nonImageFiles.length > 0 && nonImageFiles.map(renderUploadItem), !shouldUsePictureCard &&
|
|
363
|
+
} }))] })), !shouldUsePictureCard && nonImageFiles.length > 0 && nonImageFiles.map(renderUploadItem), !shouldUsePictureCard &&
|
|
329
364
|
imageFiles.length > 0 &&
|
|
330
|
-
imageFiles.map(renderUploadItem)] }), mode === 'cards' && hintsElement] }));
|
|
365
|
+
imageFiles.map(renderUploadItem)] }), mode === 'cards' && hintsElement, previewFile && previewIsImage && previewImageSrc && (jsx(MediaPreviewModal, { open: isPreviewOpen, onClose: () => setIsPreviewOpen(false), mediaItems: [previewImageSrc] }))] }));
|
|
331
366
|
});
|
|
332
367
|
|
|
333
368
|
export { Upload as default };
|
package/Upload/UploadItem.js
CHANGED
|
@@ -91,6 +91,8 @@ const UploadItem = forwardRef(function UploadItem(props, ref) {
|
|
|
91
91
|
const isFinished = useMemo(() => {
|
|
92
92
|
return /done|error/.test(status);
|
|
93
93
|
}, [status]);
|
|
94
|
+
const shouldShowFileSize = Boolean(showFileSize && isFinished && fileSize);
|
|
95
|
+
const shouldSingleLineCenter = Boolean(type === 'thumbnail' && fileName && !shouldShowFileSize);
|
|
94
96
|
useEffect(() => {
|
|
95
97
|
if (!props.file && !props.url) {
|
|
96
98
|
console.warn('UploadItem: Both `file` and `url` props are missing. At least one should be provided to display the upload item.');
|
|
@@ -112,7 +114,8 @@ const UploadItem = forwardRef(function UploadItem(props, ref) {
|
|
|
112
114
|
[uploadItemClasses.alignCenter]: status !== 'done',
|
|
113
115
|
[uploadItemClasses.error]: status === 'error',
|
|
114
116
|
[uploadItemClasses.disabled]: disabled,
|
|
115
|
-
|
|
117
|
+
[uploadItemClasses.singleLineContent]: shouldSingleLineCenter,
|
|
118
|
+
}, className), children: [jsxs("div", { ...rest, "aria-disabled": disabled, className: uploadItemClasses.container, role: "group", tabIndex: disabled ? -1 : 0, children: [jsxs("div", { className: uploadItemClasses.contentWrapper, children: [jsx("div", { className: uploadItemClasses.icon, children: itemIcon }), jsxs("div", { className: uploadItemClasses.content, children: [jsx(Tooltip, { title: isTextTruncated ? fileName : undefined, options: { placement: 'bottom' }, children: ({ onMouseEnter, onMouseLeave, ref: tooltipRef }) => (jsx(Typography, { ref: mergeRefs(tooltipRef), className: uploadItemClasses.name, ellipsis: true, onMouseEnter: onMouseEnter, onMouseLeave: onMouseLeave, children: fileName })) }), shouldShowFileSize && (jsx(Typography, { className: uploadItemClasses.fontSize, children: fileSize }))] })] }), jsxs("div", { className: uploadItemClasses.actions, children: [status === 'loading' && (jsxs(Fragment, { children: [loadingIcon, jsx(ClearActions, { onClick: onCancel, className: uploadItemClasses.closeIcon, role: "button" })] })), status === 'done' && !disabled && (jsx(Icon, { icon: DownloadIcon, size: 16, className: uploadItemClasses.downloadIcon, onClick: onDownload })), status === 'error' && !disabled && (jsx(Icon, { icon: ResetIcon, size: 16, className: uploadItemClasses.resetIcon, onClick: onReload }))] })] }), isFinished && !disabled && (jsx("div", { className: uploadItemClasses.deleteContent, children: jsx(Icon, { icon: TrashIcon, size: 16, className: uploadItemClasses.deleteIcon, color: "neutral-solid", onClick: onDelete }) }))] }), status === 'error' && errorMessage && (jsxs("div", { className: uploadItemClasses.errorMessage, children: [jsx(Icon, { icon: errorIcon !== null && errorIcon !== void 0 ? errorIcon : DangerousFilledIcon, size: 14, color: "error", className: uploadItemClasses.errorIcon }), jsx(Typography, { color: "text-error", variant: "caption", className: uploadItemClasses.errorMessageText, children: errorMessage })] }))] }));
|
|
116
119
|
});
|
|
117
120
|
|
|
118
121
|
export { UploadItem as default };
|
|
@@ -91,6 +91,11 @@ export interface UploadPictureCardProps extends NativeElementPropsWithoutKeyAndR
|
|
|
91
91
|
* Error icon to display when status is 'error'.
|
|
92
92
|
*/
|
|
93
93
|
errorIcon?: IconDefinition;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the upload picture card is readable.
|
|
96
|
+
* @default false
|
|
97
|
+
*/
|
|
98
|
+
readable?: boolean;
|
|
94
99
|
/**
|
|
95
100
|
* When delete icon is clicked, this callback will be fired.
|
|
96
101
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { forwardRef, useMemo, useState, useEffect } from 'react';
|
|
3
|
+
import { forwardRef, useCallback, useMemo, useState, useEffect } from 'react';
|
|
4
4
|
import { uploadPictureCardClasses } from '@mezzanine-ui/core/upload';
|
|
5
5
|
import { ImageIcon, FileIcon, ZoomInIcon, DownloadIcon, TrashIcon, ResetIcon } from '@mezzanine-ui/icons';
|
|
6
6
|
import Button from '../Button/Button.js';
|
|
@@ -16,7 +16,7 @@ import cx from 'clsx';
|
|
|
16
16
|
*/
|
|
17
17
|
const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
18
18
|
var _a;
|
|
19
|
-
const { ariaLabels, className, file, url, status = 'loading', imageFit = 'cover', size = 'main', disabled = false, errorMessage, errorIcon, onDelete, onDownload, onReload, onReplace, onZoomIn, ...rest } = props;
|
|
19
|
+
const { ariaLabels, className, file, url, status = 'loading', imageFit = 'cover', size = 'main', disabled = false, errorMessage, errorIcon, readable = false, onDelete, onDownload, onReload, onReplace, onZoomIn, ...rest } = props;
|
|
20
20
|
const defaultAriaLabels = {
|
|
21
21
|
cancelUpload: 'Cancel upload',
|
|
22
22
|
clickToReplace: 'Click to Replace',
|
|
@@ -27,6 +27,9 @@ const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
|
27
27
|
zoomIn: 'Zoom in image',
|
|
28
28
|
};
|
|
29
29
|
const labels = { ...defaultAriaLabels, ...ariaLabels };
|
|
30
|
+
const handleDelete = useCallback((e) => { e.stopPropagation(); onDelete === null || onDelete === void 0 ? void 0 : onDelete(e); }, [onDelete]);
|
|
31
|
+
const handleDownload = useCallback((e) => { e.stopPropagation(); onDownload === null || onDownload === void 0 ? void 0 : onDownload(e); }, [onDownload]);
|
|
32
|
+
const handleZoomIn = useCallback((e) => { e.stopPropagation(); onZoomIn === null || onZoomIn === void 0 ? void 0 : onZoomIn(e); }, [onZoomIn]);
|
|
30
33
|
const isImage = useMemo(() => {
|
|
31
34
|
return isImageFile(file, url);
|
|
32
35
|
}, [file, url]);
|
|
@@ -94,17 +97,17 @@ const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
|
94
97
|
console.warn('UploadPictureCard: minor size is not supported for non-image files');
|
|
95
98
|
return null;
|
|
96
99
|
}
|
|
97
|
-
return (jsx("div", { "aria-disabled": disabled, className: cx(uploadPictureCardClasses.host, uploadPictureCardClasses.size(size), disabled && uploadPictureCardClasses.disabled, onReplace && status === 'done' && uploadPictureCardClasses.replaceMode, className), onClick: onReplace && status === 'done' ? onReplace : undefined, onKeyDown: onReplace && status === 'done'
|
|
100
|
+
return (jsx("div", { "aria-disabled": disabled, className: cx(uploadPictureCardClasses.host, uploadPictureCardClasses.size(size), disabled && uploadPictureCardClasses.disabled, readable && uploadPictureCardClasses.readable, !readable && onReplace && status === 'done' && uploadPictureCardClasses.replaceMode, className), onClick: !readable && onReplace && status === 'done' ? onReplace : undefined, onKeyDown: !readable && onReplace && status === 'done'
|
|
98
101
|
? (e) => {
|
|
99
102
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
100
103
|
e.preventDefault();
|
|
101
104
|
e.currentTarget.click();
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
|
-
: undefined, ref: ref, role: "group", tabIndex: disabled ? -1 : 0, ...rest, children: jsxs("div", { className: uploadPictureCardClasses.container, children: [isImage && imageUrl && status !== 'error' && (jsx("img", { alt: fileName, src: imageUrl, style: {
|
|
107
|
+
: undefined, ref: ref, role: "group", tabIndex: disabled || readable ? -1 : 0, ...rest, children: jsxs("div", { className: uploadPictureCardClasses.container, children: [isImage && imageUrl && status !== 'error' && (jsx("img", { alt: fileName, src: imageUrl, style: {
|
|
105
108
|
objectFit: imageFit,
|
|
106
109
|
objectPosition: 'center',
|
|
107
|
-
} })), status === 'done' && size !== 'minor' && !isImage && (jsxs("div", { className: uploadPictureCardClasses.content, children: [jsx(Icon, { icon: FileIcon, color: "brand", size: 16 }), jsx(Typography, { className: uploadPictureCardClasses.name, ellipsis: true, children: fileName })] })), status === 'error' && size !== 'minor' && (jsxs("div", { className: uploadPictureCardClasses.errorMessage, role: "alert", "aria-live": "polite", children: [jsx(Icon, { icon: errorIconContent, color: "error", size: 16 }), jsx(Typography, { className: uploadPictureCardClasses.errorMessageText, children: errorMessageContent })] })), jsxs("div", { className: cx(uploadPictureCardClasses.actions, uploadPictureCardClasses.actionsStatus(status)), children: [status === 'loading' && size !== 'minor' && (jsxs(Fragment, { children: [jsx(ClearActions, { type: "embedded", variant: "contrast", onClick: onDelete, className: uploadPictureCardClasses.clearActionsIcon, "aria-label": labels.cancelUpload }), jsx("div", { className: uploadPictureCardClasses.loadingIcon, "aria-label": labels.uploading, children: jsx(Spin, { loading: true, size: "sub" }) })] })), status === 'done' && size !== 'minor' && (jsxs(Fragment, { children: [jsx("div", { className: uploadPictureCardClasses.tools, children: jsxs("div", { className: uploadPictureCardClasses.toolsContent, children: [onZoomIn && (jsx(Button, { variant: "base-secondary", size: "minor", icon: ZoomInIcon, iconType: "icon-only", onClick:
|
|
110
|
+
} })), status === 'done' && size !== 'minor' && !isImage && (jsxs("div", { className: uploadPictureCardClasses.content, children: [jsx(Icon, { icon: FileIcon, color: "brand", size: 16 }), jsx(Typography, { className: uploadPictureCardClasses.name, ellipsis: true, children: fileName })] })), status === 'error' && size !== 'minor' && (jsxs("div", { className: uploadPictureCardClasses.errorMessage, role: "alert", "aria-live": "polite", children: [jsx(Icon, { icon: errorIconContent, color: "error", size: 16 }), jsx(Typography, { className: uploadPictureCardClasses.errorMessageText, children: errorMessageContent })] })), jsxs("div", { className: cx(uploadPictureCardClasses.actions, uploadPictureCardClasses.actionsStatus(status)), children: [status === 'loading' && size !== 'minor' && !readable && (jsxs(Fragment, { children: [jsx(ClearActions, { type: "embedded", variant: "contrast", onClick: onDelete, className: uploadPictureCardClasses.clearActionsIcon, "aria-label": labels.cancelUpload }), jsx("div", { className: uploadPictureCardClasses.loadingIcon, "aria-label": labels.uploading, children: jsx(Spin, { loading: true, size: "sub" }) })] })), status === 'done' && size !== 'minor' && !readable && (jsxs(Fragment, { children: [jsx("div", { className: uploadPictureCardClasses.tools, children: jsxs("div", { className: uploadPictureCardClasses.toolsContent, children: [onZoomIn && (jsx(Button, { variant: "base-secondary", size: "minor", icon: ZoomInIcon, iconType: "icon-only", onClick: handleZoomIn, "aria-label": labels.zoomIn })), onDownload && (jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: DownloadIcon, onClick: handleDownload, "aria-label": labels.download })), jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: TrashIcon, onClick: handleDelete, "aria-label": labels.delete })] }) }), onReplace && (jsx("span", { className: uploadPictureCardClasses.replaceLabel, children: labels.clickToReplace }))] })), status === 'error' && size !== 'minor' && !readable && (jsx(Fragment, { children: jsx("div", { className: uploadPictureCardClasses.tools, children: jsxs("div", { className: uploadPictureCardClasses.toolsContent, children: [jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: ResetIcon, onClick: onReload, "aria-label": labels.reload }), jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: TrashIcon, onClick: onDelete, "aria-label": labels.delete })] }) }) })), size === 'minor' && !readable && (jsx(Icon, { icon: ZoomInIcon, color: "fixed-light", size: 24 }))] })] }) }));
|
|
108
111
|
});
|
|
109
112
|
|
|
110
113
|
export { UploadPictureCard as default };
|
package/Upload/Uploader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ChangeEventHandler, ReactNode, Ref } from 'react';
|
|
2
|
-
import { type UploaderHintType, type UploadPictureControl, type UploadType } from '@mezzanine-ui/core/upload';
|
|
2
|
+
import { type UploaderHintType, type UploaderMode, type UploadPictureControl, type UploadType } from '@mezzanine-ui/core/upload';
|
|
3
3
|
import { type IconDefinition } from '@mezzanine-ui/icons';
|
|
4
4
|
import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
|
|
5
5
|
type UploaderInputElementProps = Omit<NativeElementPropsWithoutKeyAndRef<'input'>, 'accept' | 'disabled' | 'multiple' | 'onChange' | 'type' | `aria-${'disabled'}`> & {
|
|
@@ -42,7 +42,7 @@ export interface UploaderLabel {
|
|
|
42
42
|
*/
|
|
43
43
|
error?: string;
|
|
44
44
|
/**
|
|
45
|
-
* Label text for "Click to upload"
|
|
45
|
+
* Label text for "Click to upload" in `mode="dropzone"`.
|
|
46
46
|
* @default 'Click to upload'
|
|
47
47
|
*/
|
|
48
48
|
clickToUpload?: string;
|
|
@@ -87,20 +87,31 @@ export interface UploaderProps extends Omit<NativeElementPropsWithoutKeyAndRef<'
|
|
|
87
87
|
* @example 'image/*', '.pdf,.doc,.docx'
|
|
88
88
|
*/
|
|
89
89
|
accept?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Provide `controllerRef` if you need detail data of file.
|
|
92
|
+
*/
|
|
93
|
+
controllerRef?: Ref<UploadPictureControl | null>;
|
|
90
94
|
/**
|
|
91
95
|
* Whether the input is disabled.
|
|
92
96
|
* @default false
|
|
93
97
|
*/
|
|
94
98
|
disabled?: boolean;
|
|
95
99
|
/**
|
|
96
|
-
*
|
|
100
|
+
* Array of hints to display outside the uploader (below the label element).
|
|
97
101
|
*/
|
|
98
|
-
|
|
102
|
+
externalHints?: UploaderHint[];
|
|
99
103
|
/**
|
|
100
|
-
*
|
|
101
|
-
|
|
104
|
+
* Array of hints to display with the upload component.
|
|
105
|
+
*/
|
|
106
|
+
hints?: UploaderHint[];
|
|
107
|
+
/**
|
|
108
|
+
* Icon configuration for different actions and states.
|
|
102
109
|
*/
|
|
103
|
-
|
|
110
|
+
icon?: UploaderIcon;
|
|
111
|
+
/**
|
|
112
|
+
* The id of input element.
|
|
113
|
+
*/
|
|
114
|
+
id?: string;
|
|
104
115
|
/**
|
|
105
116
|
* Since at Mezzanine we use a host element to wrap our input, most derived props will be passed to the host element.
|
|
106
117
|
* If you need direct control to the input element, use this prop to provide to it.
|
|
@@ -110,37 +121,25 @@ export interface UploaderProps extends Omit<NativeElementPropsWithoutKeyAndRef<'
|
|
|
110
121
|
* The react ref passed to input element.
|
|
111
122
|
*/
|
|
112
123
|
inputRef?: React.Ref<HTMLInputElement>;
|
|
124
|
+
/**
|
|
125
|
+
* Label configuration for different states.
|
|
126
|
+
*/
|
|
127
|
+
label?: UploaderLabel;
|
|
113
128
|
/**
|
|
114
129
|
* The name attribute of the input element.
|
|
115
130
|
*/
|
|
116
131
|
name?: string;
|
|
132
|
+
/**
|
|
133
|
+
* The mode for upload component.
|
|
134
|
+
* @default 'basic'
|
|
135
|
+
* @example 'basic' | 'dropzone'
|
|
136
|
+
*/
|
|
137
|
+
mode?: UploaderMode;
|
|
117
138
|
/**
|
|
118
139
|
* Whether can select multiple files to upload.
|
|
119
140
|
* @default false
|
|
120
141
|
*/
|
|
121
142
|
multiple?: boolean;
|
|
122
|
-
/**
|
|
123
|
-
* The type for upload component.
|
|
124
|
-
* @default 'base'
|
|
125
|
-
* @example 'base' | 'button'
|
|
126
|
-
*/
|
|
127
|
-
type?: UploadType;
|
|
128
|
-
/**
|
|
129
|
-
* Array of hints to display with the upload component.
|
|
130
|
-
*/
|
|
131
|
-
hints?: UploaderHint[];
|
|
132
|
-
/**
|
|
133
|
-
* Label configuration for different states.
|
|
134
|
-
*/
|
|
135
|
-
label?: UploaderLabel;
|
|
136
|
-
/**
|
|
137
|
-
* Icon configuration for different actions and states.
|
|
138
|
-
*/
|
|
139
|
-
icon?: UploaderIcon;
|
|
140
|
-
/**
|
|
141
|
-
* Provide `controllerRef` if you need detail data of file.
|
|
142
|
-
*/
|
|
143
|
-
controllerRef?: Ref<UploadPictureControl | null>;
|
|
144
143
|
/**
|
|
145
144
|
* Invoked by input change event.
|
|
146
145
|
*/
|
|
@@ -150,9 +149,11 @@ export interface UploaderProps extends Omit<NativeElementPropsWithoutKeyAndRef<'
|
|
|
150
149
|
*/
|
|
151
150
|
onUpload?: (files: File[]) => void;
|
|
152
151
|
/**
|
|
153
|
-
*
|
|
152
|
+
* The type for upload component.
|
|
153
|
+
* @default 'base'
|
|
154
|
+
* @example 'base' | 'button'
|
|
154
155
|
*/
|
|
155
|
-
|
|
156
|
+
type?: UploadType;
|
|
156
157
|
}
|
|
157
158
|
/**
|
|
158
159
|
* The react component for `mezzanine` uploader.
|
package/Upload/Uploader.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { forwardRef, useId, useRef, useState, useMemo } from 'react';
|
|
4
4
|
import { uploaderClasses } from '@mezzanine-ui/core/upload';
|
|
5
|
-
import { UploadIcon } from '@mezzanine-ui/icons';
|
|
5
|
+
import { UploadIcon, InfoFilledIcon, DangerousFilledIcon } from '@mezzanine-ui/icons';
|
|
6
6
|
import Button from '../Button/Button.js';
|
|
7
7
|
import Typography from '../Typography/Typography.js';
|
|
8
8
|
import { composeRefs } from '../utils/composeRefs.js';
|
|
@@ -14,7 +14,7 @@ import cx from 'clsx';
|
|
|
14
14
|
*/
|
|
15
15
|
const Uploader = forwardRef(function Uploader(props, ref) {
|
|
16
16
|
var _a, _b, _c;
|
|
17
|
-
const { accept, className, disabled = false, id, inputProps, inputRef: inputRefProp,
|
|
17
|
+
const { accept, className, controllerRef: _controllerRef, disabled = false, externalHints, id, hints, icon: iconConfig, inputProps, inputRef: inputRefProp, label: labelConfig, mode = 'basic', multiple = false, name, onChange: onChangeProp, onUpload, type, ...rest } = props;
|
|
18
18
|
const { name: nameFromInputProps, id: idFromInputProps, ...restInputProps } = inputProps || {};
|
|
19
19
|
// Generate unique id if not provided
|
|
20
20
|
const generatedId = useId();
|
|
@@ -23,6 +23,7 @@ const Uploader = forwardRef(function Uploader(props, ref) {
|
|
|
23
23
|
const [isDragging, setIsDragging] = useState(false);
|
|
24
24
|
const composedInputRef = useMemo(() => composeRefs([inputRefProp, inputElementRef]), [inputRefProp]);
|
|
25
25
|
const resolvedName = (_b = name !== null && name !== void 0 ? name : nameFromInputProps) !== null && _b !== void 0 ? _b : finalInputId;
|
|
26
|
+
const isDropzone = mode === 'dropzone';
|
|
26
27
|
const handleChange = (event) => {
|
|
27
28
|
if (onChangeProp) {
|
|
28
29
|
onChangeProp(event);
|
|
@@ -98,7 +99,7 @@ const Uploader = forwardRef(function Uploader(props, ref) {
|
|
|
98
99
|
}, [iconConfig]);
|
|
99
100
|
const uploadLabel = (labelConfig === null || labelConfig === void 0 ? void 0 : labelConfig.uploadLabel)
|
|
100
101
|
? labelConfig === null || labelConfig === void 0 ? void 0 : labelConfig.uploadLabel
|
|
101
|
-
:
|
|
102
|
+
: isDropzone
|
|
102
103
|
? 'Drag the file here or'
|
|
103
104
|
: 'Upload';
|
|
104
105
|
const clickToUploadLabel = (_c = labelConfig === null || labelConfig === void 0 ? void 0 : labelConfig.clickToUpload) !== null && _c !== void 0 ? _c : 'Click to upload';
|
|
@@ -109,12 +110,12 @@ const Uploader = forwardRef(function Uploader(props, ref) {
|
|
|
109
110
|
inputElementRef.current.click();
|
|
110
111
|
}
|
|
111
112
|
};
|
|
112
|
-
return (jsx("label", { className: cx(uploaderClasses.host, type && uploaderClasses.type(type), type !== 'button' &&
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
return (jsxs(Fragment, { children: [jsx("label", { className: cx(uploaderClasses.host, type && uploaderClasses.type(type), type !== 'button' && isDropzone && uploaderClasses.fillWidth, isDragging && uploaderClasses.dragging, type !== 'button' && disabled && uploaderClasses.disabled, className), onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, ref: ref, ...rest, children: jsxs(Fragment, { children: [type === 'base'
|
|
114
|
+
&& isDropzone
|
|
115
|
+
&& jsxs("div", { className: uploaderClasses.uploadContent, children: [uploadIcon, jsxs(Typography, { className: uploaderClasses.uploadLabel, children: [uploadLabel && jsxs(Fragment, { children: [uploadLabel, ' '] }), jsx("span", { className: uploaderClasses.clickToUpload, children: clickToUploadLabel })] }), hints === null || hints === void 0 ? void 0 : hints.map((hint, index) => (jsx(Typography, { className: uploaderClasses.fillWidthHints, children: hint.label }, index)))] }), type === 'base'
|
|
116
|
+
&& !isDropzone
|
|
117
|
+
&& jsxs("div", { className: uploaderClasses.uploadContent, children: [uploadIcon, jsx(Typography, { className: uploaderClasses.uploadLabel, children: uploadLabel })] }), type === 'button'
|
|
118
|
+
&& (jsx(Button, { disabled: disabled, iconType: "leading", icon: UploadIcon, onClick: handleClickToUpload, children: jsx(Typography, { align: "center", color: "text-fixed-light", variant: "button-highlight", className: uploaderClasses.uploadButtonText, children: uploadLabel }) })), jsx("input", { ...restInputProps, accept: accept, "aria-disabled": disabled, className: uploaderClasses.input, disabled: disabled, id: finalInputId, multiple: multiple, name: resolvedName, onChange: handleChange, ref: composedInputRef, type: "file" })] }) }), externalHints && externalHints.length > 0 && (jsx("ul", { className: uploaderClasses.externalHints, children: externalHints.map((hint) => (jsxs("li", { className: uploaderClasses.externalHint(hint.type || 'info'), children: [jsx(Icon, { icon: hint.type === 'info' ? InfoFilledIcon : DangerousFilledIcon, color: hint.type === 'info' ? 'info' : 'error', size: 14 }), hint.label] }, hint.label))) }))] }));
|
|
118
119
|
});
|
|
119
120
|
|
|
120
121
|
export { Uploader as default };
|
package/index.d.ts
CHANGED
|
@@ -123,7 +123,7 @@ export type { CascaderOption, CascaderPanelProps, CascaderProps, CascaderSize, }
|
|
|
123
123
|
export { default as Select, SelectControlContext, SelectTrigger, SelectTriggerTags, } from './Select';
|
|
124
124
|
export type { SelectControl, SelectProps, SelectTriggerInputProps, SelectTriggerProps, SelectTriggerTagsProps, SelectValue, } from './Select';
|
|
125
125
|
export { default as SelectionCard } from './SelectionCard';
|
|
126
|
-
export type { SelectionCardProps, SelectionCardPropsBase } from './SelectionCard';
|
|
126
|
+
export type { SelectionCardProps, SelectionCardPropsBase, } from './SelectionCard';
|
|
127
127
|
export { default as Slider, useSlider } from './Slider';
|
|
128
128
|
export type { RangeSliderProps, RangeSliderValue, SingleSliderProps, SingleSliderValue, SliderBaseProps, SliderComponentProps, SliderProps, SliderRect, SliderValue, UseRangeSliderProps, UseSingleSliderProps, UseSliderCommonProps, UseSliderProps, UseSliderResult, } from './Slider';
|
|
129
129
|
export { default as Textarea } from './Textarea';
|
|
@@ -134,8 +134,8 @@ export { default as TimePicker, TimePickerPanel } from './TimePicker';
|
|
|
134
134
|
export type { TimePickerPanelProps, TimePickerProps } from './TimePicker';
|
|
135
135
|
export { default as TimeRangePicker, useTimeRangePickerValue, } from './TimeRangePicker';
|
|
136
136
|
export type { TimeRangePickerProps, TimeRangePickerValue, UseTimeRangePickerValueProps, } from './TimeRangePicker';
|
|
137
|
-
export { default as
|
|
138
|
-
export type { ToggleProps
|
|
137
|
+
export { default as Toggle } from './Toggle';
|
|
138
|
+
export type { ToggleProps, ToggleSize } from './Toggle';
|
|
139
139
|
export { Upload, UploadItem, UploadPictureCard, Uploader } from './Upload';
|
|
140
140
|
export type { UploadFile, UploadItemProps, UploadPictureCardProps, UploadProps, UploaderProps, } from './Upload';
|
|
141
141
|
/**
|
package/index.js
CHANGED
|
@@ -116,7 +116,7 @@ export { default as TextField } from './TextField/TextField.js';
|
|
|
116
116
|
export { default as TimePicker } from './TimePicker/TimePicker.js';
|
|
117
117
|
export { default as TimePickerPanel } from './TimePicker/TimePickerPanel.js';
|
|
118
118
|
export { default as TimeRangePicker } from './TimeRangePicker/TimeRangePicker.js';
|
|
119
|
-
export { default as
|
|
119
|
+
export { default as Toggle } from './Toggle/Toggle.js';
|
|
120
120
|
export { default as Upload } from './Upload/Upload.js';
|
|
121
121
|
export { default as UploadItem } from './Upload/UploadItem.js';
|
|
122
122
|
export { default as UploadPictureCard } from './Upload/UploadPictureCard.js';
|