@mezzanine-ui/react 1.0.0-rc.5 → 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/Accordion/Accordion.d.ts +36 -0
- package/Accordion/Accordion.js +37 -1
- package/AlertBanner/AlertBanner.d.ts +30 -1
- package/AlertBanner/AlertBanner.js +56 -12
- package/AutoComplete/AutoComplete.d.ts +75 -11
- package/AutoComplete/AutoComplete.js +145 -25
- 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/Backdrop/Backdrop.d.ts +33 -1
- package/Backdrop/Backdrop.js +33 -1
- package/Badge/Badge.d.ts +26 -1
- package/Badge/Badge.js +26 -1
- package/Badge/typings.d.ts +15 -0
- package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
- package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
- package/Breadcrumb/typings.d.ts +1 -1
- package/Button/Button.d.ts +26 -1
- package/Button/Button.js +33 -5
- package/COMPONENTS.md +267 -0
- package/Calendar/useCalendarControls.d.ts +18 -0
- package/Calendar/useCalendarControls.js +18 -0
- package/Card/FourThumbnailCard.js +1 -1
- package/Card/SingleThumbnailCard.js +1 -1
- package/Checkbox/Checkbox.d.ts +30 -1
- package/Checkbox/Checkbox.js +54 -4
- package/Checkbox/CheckboxGroup.d.ts +39 -1
- package/Checkbox/CheckboxGroup.js +49 -3
- package/Cropper/Cropper.d.ts +2 -2
- package/Cropper/Cropper.js +2 -3
- package/Cropper/CropperElement.js +1 -1
- package/Cropper/utils/cropper-calculations.d.ts +1 -1
- package/Cropper/utils/cropper-calculations.js +16 -14
- package/DatePicker/DatePicker.d.ts +36 -2
- package/DatePicker/DatePicker.js +36 -2
- package/DateRangePicker/useDateRangePickerValue.d.ts +24 -0
- package/DateRangePicker/useDateRangePickerValue.js +24 -0
- 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 +78 -33
- package/Drawer/Drawer.js +72 -34
- package/Dropdown/Dropdown.d.ts +63 -1
- package/Dropdown/Dropdown.js +203 -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/DropdownStatus.d.ts +2 -2
- package/Dropdown/DropdownStatus.js +3 -2
- package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
- package/Dropdown/dropdownKeydownHandler.js +14 -7
- package/Empty/icons/EmptyMainInitialDataIcon.js +1 -1
- package/Empty/icons/EmptyMainNotificationIcon.js +1 -1
- package/Empty/icons/EmptyMainResultIcon.js +1 -1
- package/Empty/icons/EmptyMainSystemIcon.js +1 -1
- package/FilterArea/Filter.d.ts +25 -2
- package/FilterArea/Filter.js +43 -2
- package/FilterArea/FilterArea.d.ts +43 -4
- package/FilterArea/FilterArea.js +39 -4
- package/FilterArea/FilterAreaContext.d.ts +6 -0
- package/FilterArea/FilterAreaContext.js +6 -0
- package/FilterArea/FilterLine.d.ts +19 -0
- package/FilterArea/FilterLine.js +19 -0
- package/FilterArea/index.d.ts +2 -2
- package/FilterArea/index.js +1 -1
- package/Form/FormField.d.ts +43 -2
- package/Form/FormField.js +40 -3
- package/Form/typings.d.ts +2 -0
- package/Form/useAutoCompleteValueControl.d.ts +20 -0
- package/Form/useAutoCompleteValueControl.js +20 -0
- package/Form/useCheckboxControlValue.d.ts +19 -0
- package/Form/useCheckboxControlValue.js +19 -0
- package/Form/useControlValueState.d.ts +16 -0
- package/Form/useControlValueState.js +16 -0
- package/Form/useCustomControlValue.d.ts +17 -0
- package/Form/useCustomControlValue.js +17 -0
- package/Form/useInputControlValue.d.ts +19 -0
- package/Form/useInputControlValue.js +19 -0
- package/Form/useInputWithClearControlValue.d.ts +20 -0
- package/Form/useInputWithClearControlValue.js +20 -0
- package/Form/useRadioControlValue.d.ts +19 -0
- package/Form/useRadioControlValue.js +19 -0
- package/Form/useSelectValueControl.d.ts +19 -0
- package/Form/useSelectValueControl.js +19 -0
- package/Form/useSwitchControlValue.d.ts +18 -0
- package/Form/useSwitchControlValue.js +18 -0
- package/Icon/Icon.d.ts +23 -1
- package/Icon/Icon.js +23 -1
- package/Input/Input.d.ts +31 -1
- package/Input/Input.js +31 -1
- package/Input/SpinnerButton/SpinnerButton.js +1 -1
- package/Message/Message.d.ts +56 -6
- package/Message/Message.js +59 -52
- package/Modal/Modal.d.ts +76 -4
- package/Modal/Modal.js +117 -6
- package/Modal/ModalBodyForVerification.js +3 -1
- package/Modal/ModalFooter.d.ts +21 -6
- package/Modal/ModalHeader.d.ts +50 -11
- package/Modal/ModalHeader.js +1 -1
- package/Modal/index.d.ts +1 -1
- package/Navigation/Navigation.js +20 -11
- package/Navigation/NavigationHeader.js +2 -1
- package/Navigation/NavigationIconButton.d.ts +8 -0
- package/Navigation/NavigationIconButton.js +2 -2
- package/Navigation/NavigationOption.js +6 -3
- package/Navigation/NavigationOverflowMenu.js +9 -6
- package/Navigation/NavigationUserMenu.js +3 -1
- package/Navigation/context.d.ts +1 -0
- package/NotificationCenter/NotificationCenter.d.ts +59 -9
- package/NotificationCenter/NotificationCenter.js +150 -54
- package/NotificationCenter/NotificationCenterDrawer.d.ts +56 -6
- package/NotificationCenter/NotificationCenterDrawer.js +6 -8
- package/NotificationCenter/index.d.ts +1 -1
- package/OverflowTooltip/OverflowTooltip.js +46 -5
- package/PageFooter/PageFooter.d.ts +1 -1
- package/PageFooter/PageFooter.js +8 -16
- package/Pagination/Pagination.d.ts +38 -1
- package/Pagination/Pagination.js +38 -1
- package/Pagination/PaginationPageSize.js +1 -1
- package/Pagination/usePagination.d.ts +20 -0
- package/Pagination/usePagination.js +20 -0
- package/Progress/Progress.d.ts +24 -1
- package/Progress/Progress.js +24 -1
- package/README.md +82 -0
- package/Radio/Radio.d.ts +35 -1
- package/Radio/Radio.js +50 -12
- package/Radio/RadioGroup.d.ts +2 -2
- package/Radio/RadioGroupContext.d.ts +3 -2
- package/Section/SectionGroup.d.ts +20 -0
- package/Section/SectionGroup.js +16 -0
- package/Section/index.d.ts +2 -0
- package/Section/index.js +1 -0
- package/Select/Select.d.ts +55 -1
- package/Select/Select.js +37 -2
- package/Select/typings.d.ts +9 -0
- package/{Selection/Selection.d.ts → SelectionCard/SelectionCard.d.ts} +23 -11
- package/{Selection/Selection.js → SelectionCard/SelectionCard.js} +14 -14
- package/SelectionCard/SelectionCardGroup.d.ts +28 -0
- package/SelectionCard/index.d.ts +12 -0
- package/SelectionCard/index.js +5 -0
- package/Separator/Separator.d.ts +19 -1
- package/Separator/Separator.js +19 -1
- package/Slider/Slider.d.ts +11 -0
- package/Slider/useSlider.d.ts +21 -0
- package/Spin/Spin.d.ts +14 -5
- package/Spin/Spin.js +6 -8
- package/Stepper/Stepper.d.ts +34 -1
- package/Stepper/Stepper.js +34 -1
- package/Stepper/useStepper.d.ts +18 -0
- package/Stepper/useStepper.js +18 -0
- package/Tab/Tab.d.ts +33 -1
- package/Tab/Tab.js +33 -1
- package/Table/Table.d.ts +51 -3
- package/Table/Table.js +52 -4
- package/Table/hooks/useTableDataSource.d.ts +4 -4
- package/Table/hooks/useTableDataSource.js +1 -1
- package/Tag/Tag.d.ts +27 -1
- package/Tag/Tag.js +27 -1
- package/Tag/typings.d.ts +28 -0
- package/Textarea/Textarea.d.ts +28 -1
- package/Textarea/Textarea.js +25 -1
- package/TimePicker/TimePicker.js +1 -1
- package/TimeRangePicker/TimeRangePicker.js +1 -1
- package/Toggle/Toggle.d.ts +34 -1
- package/Toggle/Toggle.js +34 -1
- package/Tooltip/Tooltip.d.ts +39 -1
- package/Tooltip/Tooltip.js +39 -1
- package/Tooltip/useDelayMouseEnterLeave.d.ts +17 -0
- package/Tooltip/useDelayMouseEnterLeave.js +17 -0
- package/Typography/Typography.d.ts +23 -1
- package/Typography/Typography.js +23 -1
- package/Upload/Upload.d.ts +13 -7
- package/Upload/Upload.js +55 -20
- package/Upload/UploadItem.d.ts +3 -3
- package/Upload/UploadItem.js +11 -8
- package/Upload/UploadPictureCard.d.ts +5 -0
- package/Upload/UploadPictureCard.js +10 -6
- package/Upload/Uploader.d.ts +32 -31
- package/Upload/Uploader.js +10 -9
- package/hooks/useClickAway.d.ts +16 -0
- package/hooks/useClickAway.js +16 -0
- package/hooks/useComposeRefs.d.ts +15 -0
- package/hooks/useComposeRefs.js +15 -0
- package/hooks/useDocumentEscapeKeyDown.d.ts +15 -0
- package/hooks/useDocumentEscapeKeyDown.js +15 -0
- package/hooks/useDocumentEvents.d.ts +16 -0
- package/hooks/useDocumentEvents.js +16 -0
- package/hooks/useDocumentTabKeyDown.d.ts +15 -0
- package/hooks/useDocumentTabKeyDown.js +15 -0
- package/hooks/useIsomorphicLayoutEffect.d.ts +16 -0
- package/hooks/useIsomorphicLayoutEffect.js +16 -0
- package/hooks/useLastCallback.d.ts +15 -0
- package/hooks/useLastCallback.js +15 -0
- package/hooks/useLastValue.d.ts +18 -0
- package/hooks/useLastValue.js +18 -0
- package/hooks/usePreviousValue.d.ts +14 -0
- package/hooks/usePreviousValue.js +14 -0
- package/hooks/useScrollLock.d.ts +6 -4
- package/hooks/useScrollLock.js +6 -4
- package/hooks/useTopStack.d.ts +17 -0
- package/hooks/useTopStack.js +17 -0
- package/hooks/useWindowWidth.d.ts +14 -0
- package/hooks/useWindowWidth.js +14 -0
- package/index.d.ts +6 -4
- package/index.js +3 -2
- package/llms.txt +200 -0
- package/package.json +5 -4
- package/Selection/SelectionGroup.d.ts +0 -28
- package/Selection/index.d.ts +0 -12
- package/Selection/index.js +0 -5
package/Tooltip/Tooltip.js
CHANGED
|
@@ -12,7 +12,45 @@ import Popper from '../Popper/Popper.js';
|
|
|
12
12
|
import cx from 'clsx';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* 滑鼠懸停時顯示的提示框元件。
|
|
16
|
+
*
|
|
17
|
+
* 採用 render prop 模式,`children` 為接收 `ref`、`onMouseEnter`、`onMouseLeave` 的函式。
|
|
18
|
+
* 內部使用 `Popper` 進行定位,並整合 `flip`、`shift` 等 floating-ui middleware 自動調整位置以避免溢出視窗。
|
|
19
|
+
* 支援自訂偏移量、顯示箭頭及滑鼠離開延遲時間。
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* import Tooltip from '@mezzanine-ui/react/Tooltip';
|
|
24
|
+
*
|
|
25
|
+
* // 基本用法
|
|
26
|
+
* <Tooltip title="這是提示文字">
|
|
27
|
+
* {({ ref, onMouseEnter, onMouseLeave }) => (
|
|
28
|
+
* <button ref={ref} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
|
29
|
+
* Hover me
|
|
30
|
+
* </button>
|
|
31
|
+
* )}
|
|
32
|
+
* </Tooltip>
|
|
33
|
+
*
|
|
34
|
+
* // 自訂位置與關閉箭頭
|
|
35
|
+
* <Tooltip title="底部提示" arrow={false} options={{ placement: 'bottom' }}>
|
|
36
|
+
* {({ ref, onMouseEnter, onMouseLeave }) => (
|
|
37
|
+
* <span ref={ref} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
|
38
|
+
* 文字
|
|
39
|
+
* </span>
|
|
40
|
+
* )}
|
|
41
|
+
* </Tooltip>
|
|
42
|
+
*
|
|
43
|
+
* // 增加滑鼠離開延遲
|
|
44
|
+
* <Tooltip title="可懸停的提示" mouseLeaveDelay={0.3}>
|
|
45
|
+
* {({ ref, onMouseEnter, onMouseLeave }) => (
|
|
46
|
+
* <button ref={ref} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
|
47
|
+
* 按鈕
|
|
48
|
+
* </button>
|
|
49
|
+
* )}
|
|
50
|
+
* </Tooltip>
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @see {@link Popper} 浮動定位元件
|
|
16
54
|
*/
|
|
17
55
|
const Tooltip = forwardRef(function Tooltip(props, ref) {
|
|
18
56
|
const { anchor: anchorProp, arrow: showArrow = true, children, className, disablePortal = true, mouseLeaveDelay = 0.1, offsetMainAxis, open = false, options = {}, title, ...rest } = props;
|
|
@@ -7,4 +7,21 @@ export interface DelayMouseEnterLeave {
|
|
|
7
7
|
onTargetEnter(v: React.MouseEvent<HTMLElement, MouseEvent>): void;
|
|
8
8
|
visible: boolean;
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* 管理帶有延遲的滑鼠移入/移出可見性狀態的 Hook。
|
|
12
|
+
*
|
|
13
|
+
* 在滑鼠離開目標或 Popper 時,會等待 `mouseLeaveDelay` 秒後才隱藏,
|
|
14
|
+
* 讓使用者有時間將游標移入 Popper 內容區域而不觸發消失。
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { useDelayMouseEnterLeave } from '@mezzanine-ui/react';
|
|
19
|
+
*
|
|
20
|
+
* const { visible, onTargetEnter, onLeave, onPopperEnter } = useDelayMouseEnterLeave({
|
|
21
|
+
* mouseLeaveDelay: 0.1,
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @see {@link Tooltip} 搭配的元件
|
|
26
|
+
*/
|
|
10
27
|
export declare function useDelayMouseEnterLeave(opt: UseDelayMouseEnterLeave): DelayMouseEnterLeave;
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { useState, useRef, useCallback } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 管理帶有延遲的滑鼠移入/移出可見性狀態的 Hook。
|
|
5
|
+
*
|
|
6
|
+
* 在滑鼠離開目標或 Popper 時,會等待 `mouseLeaveDelay` 秒後才隱藏,
|
|
7
|
+
* 讓使用者有時間將游標移入 Popper 內容區域而不觸發消失。
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useDelayMouseEnterLeave } from '@mezzanine-ui/react';
|
|
12
|
+
*
|
|
13
|
+
* const { visible, onTargetEnter, onLeave, onPopperEnter } = useDelayMouseEnterLeave({
|
|
14
|
+
* mouseLeaveDelay: 0.1,
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @see {@link Tooltip} 搭配的元件
|
|
19
|
+
*/
|
|
3
20
|
function useDelayMouseEnterLeave(opt) {
|
|
4
21
|
const { mouseLeaveDelay = 0.1 } = opt || {};
|
|
5
22
|
/** state that control tooltip visible/invisible */
|
|
@@ -36,7 +36,29 @@ interface TypographyPropsBase {
|
|
|
36
36
|
}
|
|
37
37
|
export type TypographyProps<C extends TypographyComponent = 'p'> = ComponentOverridableForwardRefComponentPropsFactory<TypographyComponent, C, TypographyPropsBase>;
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* 文字排版元件,提供一致的語意化文字樣式。
|
|
40
|
+
*
|
|
41
|
+
* 透過 `variant` 套用設計系統中定義的語意排版類型(如 `h1`、`h2`、`h3`、`body`、`caption` 等),
|
|
42
|
+
* 並自動推斷對應的 HTML 標籤(`h1–h3` → `<h1>–<h3>`,body 系列 → `<p>`,其餘 → `<span>`)。
|
|
43
|
+
* 可透過 `color` 套用調色盤中的文字色彩,`align` 控制對齊方式,`ellipsis` 啟用單行截斷省略號。
|
|
44
|
+
* 支援透過 `component` prop 覆寫根元素標籤。
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* import Typography from '@mezzanine-ui/react/Typography';
|
|
49
|
+
*
|
|
50
|
+
* // 標題
|
|
51
|
+
* <Typography variant="h1">頁面標題</Typography>
|
|
52
|
+
*
|
|
53
|
+
* // 本文
|
|
54
|
+
* <Typography variant="body">這是一段說明文字。</Typography>
|
|
55
|
+
*
|
|
56
|
+
* // 套用色彩
|
|
57
|
+
* <Typography variant="body" color="error">錯誤提示訊息</Typography>
|
|
58
|
+
*
|
|
59
|
+
* // 單行截斷(需搭配 block 或 inline-block 容器)
|
|
60
|
+
* <Typography variant="caption" ellipsis display="block">超長文字會在此被截斷顯示省略號...</Typography>
|
|
61
|
+
* ```
|
|
40
62
|
*/
|
|
41
63
|
declare const Typography: import("react").ForwardRefExoticComponent<Omit<Omit<import("../utils/jsx-types").ComponentPropsWithoutKeyAndRef<"p">, keyof TypographyPropsBase> & TypographyPropsBase, "component"> & {
|
|
42
64
|
component?: TypographyComponent | undefined;
|
package/Typography/Typography.js
CHANGED
|
@@ -13,7 +13,29 @@ function getComponentFromType(type) {
|
|
|
13
13
|
return 'span';
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* 文字排版元件,提供一致的語意化文字樣式。
|
|
17
|
+
*
|
|
18
|
+
* 透過 `variant` 套用設計系統中定義的語意排版類型(如 `h1`、`h2`、`h3`、`body`、`caption` 等),
|
|
19
|
+
* 並自動推斷對應的 HTML 標籤(`h1–h3` → `<h1>–<h3>`,body 系列 → `<p>`,其餘 → `<span>`)。
|
|
20
|
+
* 可透過 `color` 套用調色盤中的文字色彩,`align` 控制對齊方式,`ellipsis` 啟用單行截斷省略號。
|
|
21
|
+
* 支援透過 `component` prop 覆寫根元素標籤。
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* import Typography from '@mezzanine-ui/react/Typography';
|
|
26
|
+
*
|
|
27
|
+
* // 標題
|
|
28
|
+
* <Typography variant="h1">頁面標題</Typography>
|
|
29
|
+
*
|
|
30
|
+
* // 本文
|
|
31
|
+
* <Typography variant="body">這是一段說明文字。</Typography>
|
|
32
|
+
*
|
|
33
|
+
* // 套用色彩
|
|
34
|
+
* <Typography variant="body" color="error">錯誤提示訊息</Typography>
|
|
35
|
+
*
|
|
36
|
+
* // 單行截斷(需搭配 block 或 inline-block 容器)
|
|
37
|
+
* <Typography variant="caption" ellipsis display="block">超長文字會在此被截斷顯示省略號...</Typography>
|
|
38
|
+
* ```
|
|
17
39
|
*/
|
|
18
40
|
const Typography = forwardRef(function Typography(props, ref) {
|
|
19
41
|
const { align, children, className, color, component, display, ellipsis = false, noWrap = false, variant = 'body', style: styleProp, ...rest } = props;
|
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.d.ts
CHANGED
|
@@ -46,9 +46,9 @@ export interface UploadItemProps extends NativeElementPropsWithoutKeyAndRef<'div
|
|
|
46
46
|
*/
|
|
47
47
|
disabled?: boolean;
|
|
48
48
|
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
* The status of the item.
|
|
50
|
+
* @default 'loading'
|
|
51
|
+
*/
|
|
52
52
|
status: UploadItemStatus;
|
|
53
53
|
/**
|
|
54
54
|
* The error message to display when status is 'error'.
|
package/Upload/UploadItem.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { uploadItemClasses } from '@mezzanine-ui/core/upload';
|
|
4
4
|
import { forwardRef, useMemo, useRef, useState, useCallback, useEffect } from 'react';
|
|
5
|
-
import { FileIcon,
|
|
5
|
+
import { FileIcon, DownloadIcon, ResetIcon, TrashIcon, DangerousFilledIcon } from '@mezzanine-ui/icons';
|
|
6
6
|
import ClearActions from '../ClearActions/ClearActions.js';
|
|
7
7
|
import Typography from '../Typography/Typography.js';
|
|
8
8
|
import { useIsomorphicLayoutEffect } from '../hooks/useIsomorphicLayoutEffect.js';
|
|
9
9
|
import UploadPictureCard from './UploadPictureCard.js';
|
|
10
10
|
import { isImageFile } from './upload-utils.js';
|
|
11
11
|
import Icon from '../Icon/Icon.js';
|
|
12
|
+
import Spin from '../Spin/Spin.js';
|
|
12
13
|
import Tooltip from '../Tooltip/Tooltip.js';
|
|
13
14
|
import cx from 'clsx';
|
|
14
15
|
|
|
@@ -41,14 +42,12 @@ const UploadItem = forwardRef(function UploadItem(props, ref) {
|
|
|
41
42
|
}, [(_a = props.file) === null || _a === void 0 ? void 0 : _a.name, props.url]);
|
|
42
43
|
const itemIcon = useMemo(() => {
|
|
43
44
|
if (type === 'thumbnail') {
|
|
44
|
-
return isImage
|
|
45
|
-
? (jsx(UploadPictureCard, { file: props.file, url: props.url, size: "minor", className: uploadItemClasses.thumbnail }))
|
|
46
|
-
: (jsx("div", { className: uploadItemClasses.thumbnail, children: jsx(Icon, { icon: FileIcon, className: uploadItemClasses.icon, size: 16 }) }));
|
|
45
|
+
return isImage ? (jsx(UploadPictureCard, { file: props.file, url: props.url, size: "minor", className: uploadItemClasses.thumbnail })) : (jsx("div", { className: uploadItemClasses.thumbnail, children: jsx(Icon, { icon: FileIcon, className: uploadItemClasses.icon, size: 16 }) }));
|
|
47
46
|
}
|
|
48
|
-
return jsx(Icon, { icon: icon !== null && icon !== void 0 ? icon : FileIcon, className: uploadItemClasses.icon, size: 16 });
|
|
47
|
+
return (jsx(Icon, { icon: icon !== null && icon !== void 0 ? icon : FileIcon, className: uploadItemClasses.icon, size: 16 }));
|
|
49
48
|
}, [type, icon, props.file, props.url, isImage]);
|
|
50
49
|
const loadingIcon = useMemo(() => {
|
|
51
|
-
return (jsx("div", { className: uploadItemClasses.loadingIcon, children: jsx(
|
|
50
|
+
return (jsx("div", { className: uploadItemClasses.loadingIcon, children: jsx(Spin, { loading: true, size: "minor" }) }));
|
|
52
51
|
}, []);
|
|
53
52
|
const fileNameRef = useRef(null);
|
|
54
53
|
const tooltipRefRef = useRef(null);
|
|
@@ -61,7 +60,8 @@ const UploadItem = forwardRef(function UploadItem(props, ref) {
|
|
|
61
60
|
tooltipRef(node);
|
|
62
61
|
}
|
|
63
62
|
else if (tooltipRef && 'current' in tooltipRef) {
|
|
64
|
-
tooltipRef.current =
|
|
63
|
+
tooltipRef.current =
|
|
64
|
+
node;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
}, []);
|
|
@@ -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,13 +1,14 @@
|
|
|
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
|
-
import { ImageIcon, FileIcon,
|
|
5
|
+
import { ImageIcon, FileIcon, ZoomInIcon, DownloadIcon, TrashIcon, ResetIcon } from '@mezzanine-ui/icons';
|
|
6
6
|
import Button from '../Button/Button.js';
|
|
7
7
|
import ClearActions from '../ClearActions/ClearActions.js';
|
|
8
8
|
import Typography from '../Typography/Typography.js';
|
|
9
9
|
import { isImageFile } from './upload-utils.js';
|
|
10
10
|
import Icon from '../Icon/Icon.js';
|
|
11
|
+
import Spin from '../Spin/Spin.js';
|
|
11
12
|
import cx from 'clsx';
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -15,7 +16,7 @@ import cx from 'clsx';
|
|
|
15
16
|
*/
|
|
16
17
|
const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
17
18
|
var _a;
|
|
18
|
-
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;
|
|
19
20
|
const defaultAriaLabels = {
|
|
20
21
|
cancelUpload: 'Cancel upload',
|
|
21
22
|
clickToReplace: 'Click to Replace',
|
|
@@ -26,6 +27,9 @@ const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
|
26
27
|
zoomIn: 'Zoom in image',
|
|
27
28
|
};
|
|
28
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]);
|
|
29
33
|
const isImage = useMemo(() => {
|
|
30
34
|
return isImageFile(file, url);
|
|
31
35
|
}, [file, url]);
|
|
@@ -93,17 +97,17 @@ const UploadPictureCard = forwardRef(function UploadPictureCard(props, ref) {
|
|
|
93
97
|
console.warn('UploadPictureCard: minor size is not supported for non-image files');
|
|
94
98
|
return null;
|
|
95
99
|
}
|
|
96
|
-
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'
|
|
97
101
|
? (e) => {
|
|
98
102
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
99
103
|
e.preventDefault();
|
|
100
104
|
e.currentTarget.click();
|
|
101
105
|
}
|
|
102
106
|
}
|
|
103
|
-
: 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: {
|
|
104
108
|
objectFit: imageFit,
|
|
105
109
|
objectPosition: 'center',
|
|
106
|
-
} })), 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(
|
|
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 }))] })] }) }));
|
|
107
111
|
});
|
|
108
112
|
|
|
109
113
|
export { UploadPictureCard as default };
|