@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.
Files changed (213) hide show
  1. package/Accordion/Accordion.d.ts +36 -0
  2. package/Accordion/Accordion.js +37 -1
  3. package/AlertBanner/AlertBanner.d.ts +30 -1
  4. package/AlertBanner/AlertBanner.js +56 -12
  5. package/AutoComplete/AutoComplete.d.ts +75 -11
  6. package/AutoComplete/AutoComplete.js +145 -25
  7. package/AutoComplete/AutoCompleteInside.d.ts +54 -0
  8. package/AutoComplete/AutoCompleteInside.js +17 -0
  9. package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
  10. package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
  11. package/Backdrop/Backdrop.d.ts +33 -1
  12. package/Backdrop/Backdrop.js +33 -1
  13. package/Badge/Badge.d.ts +26 -1
  14. package/Badge/Badge.js +26 -1
  15. package/Badge/typings.d.ts +15 -0
  16. package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
  17. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
  18. package/Breadcrumb/typings.d.ts +1 -1
  19. package/Button/Button.d.ts +26 -1
  20. package/Button/Button.js +33 -5
  21. package/COMPONENTS.md +267 -0
  22. package/Calendar/useCalendarControls.d.ts +18 -0
  23. package/Calendar/useCalendarControls.js +18 -0
  24. package/Card/FourThumbnailCard.js +1 -1
  25. package/Card/SingleThumbnailCard.js +1 -1
  26. package/Checkbox/Checkbox.d.ts +30 -1
  27. package/Checkbox/Checkbox.js +54 -4
  28. package/Checkbox/CheckboxGroup.d.ts +39 -1
  29. package/Checkbox/CheckboxGroup.js +49 -3
  30. package/Cropper/Cropper.d.ts +2 -2
  31. package/Cropper/Cropper.js +2 -3
  32. package/Cropper/CropperElement.js +1 -1
  33. package/Cropper/utils/cropper-calculations.d.ts +1 -1
  34. package/Cropper/utils/cropper-calculations.js +16 -14
  35. package/DatePicker/DatePicker.d.ts +36 -2
  36. package/DatePicker/DatePicker.js +36 -2
  37. package/DateRangePicker/useDateRangePickerValue.d.ts +24 -0
  38. package/DateRangePicker/useDateRangePickerValue.js +24 -0
  39. package/Description/Description.d.ts +1 -1
  40. package/Description/Description.js +1 -1
  41. package/Description/DescriptionTitle.d.ts +6 -1
  42. package/Description/DescriptionTitle.js +2 -2
  43. package/Drawer/Drawer.d.ts +78 -33
  44. package/Drawer/Drawer.js +72 -34
  45. package/Dropdown/Dropdown.d.ts +63 -1
  46. package/Dropdown/Dropdown.js +203 -9
  47. package/Dropdown/DropdownItem.d.ts +26 -2
  48. package/Dropdown/DropdownItem.js +91 -43
  49. package/Dropdown/DropdownItemCard.d.ts +3 -2
  50. package/Dropdown/DropdownItemCard.js +8 -5
  51. package/Dropdown/DropdownStatus.d.ts +2 -2
  52. package/Dropdown/DropdownStatus.js +3 -2
  53. package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
  54. package/Dropdown/dropdownKeydownHandler.js +14 -7
  55. package/Empty/icons/EmptyMainInitialDataIcon.js +1 -1
  56. package/Empty/icons/EmptyMainNotificationIcon.js +1 -1
  57. package/Empty/icons/EmptyMainResultIcon.js +1 -1
  58. package/Empty/icons/EmptyMainSystemIcon.js +1 -1
  59. package/FilterArea/Filter.d.ts +25 -2
  60. package/FilterArea/Filter.js +43 -2
  61. package/FilterArea/FilterArea.d.ts +43 -4
  62. package/FilterArea/FilterArea.js +39 -4
  63. package/FilterArea/FilterAreaContext.d.ts +6 -0
  64. package/FilterArea/FilterAreaContext.js +6 -0
  65. package/FilterArea/FilterLine.d.ts +19 -0
  66. package/FilterArea/FilterLine.js +19 -0
  67. package/FilterArea/index.d.ts +2 -2
  68. package/FilterArea/index.js +1 -1
  69. package/Form/FormField.d.ts +43 -2
  70. package/Form/FormField.js +40 -3
  71. package/Form/typings.d.ts +2 -0
  72. package/Form/useAutoCompleteValueControl.d.ts +20 -0
  73. package/Form/useAutoCompleteValueControl.js +20 -0
  74. package/Form/useCheckboxControlValue.d.ts +19 -0
  75. package/Form/useCheckboxControlValue.js +19 -0
  76. package/Form/useControlValueState.d.ts +16 -0
  77. package/Form/useControlValueState.js +16 -0
  78. package/Form/useCustomControlValue.d.ts +17 -0
  79. package/Form/useCustomControlValue.js +17 -0
  80. package/Form/useInputControlValue.d.ts +19 -0
  81. package/Form/useInputControlValue.js +19 -0
  82. package/Form/useInputWithClearControlValue.d.ts +20 -0
  83. package/Form/useInputWithClearControlValue.js +20 -0
  84. package/Form/useRadioControlValue.d.ts +19 -0
  85. package/Form/useRadioControlValue.js +19 -0
  86. package/Form/useSelectValueControl.d.ts +19 -0
  87. package/Form/useSelectValueControl.js +19 -0
  88. package/Form/useSwitchControlValue.d.ts +18 -0
  89. package/Form/useSwitchControlValue.js +18 -0
  90. package/Icon/Icon.d.ts +23 -1
  91. package/Icon/Icon.js +23 -1
  92. package/Input/Input.d.ts +31 -1
  93. package/Input/Input.js +31 -1
  94. package/Input/SpinnerButton/SpinnerButton.js +1 -1
  95. package/Message/Message.d.ts +56 -6
  96. package/Message/Message.js +59 -52
  97. package/Modal/Modal.d.ts +76 -4
  98. package/Modal/Modal.js +117 -6
  99. package/Modal/ModalBodyForVerification.js +3 -1
  100. package/Modal/ModalFooter.d.ts +21 -6
  101. package/Modal/ModalHeader.d.ts +50 -11
  102. package/Modal/ModalHeader.js +1 -1
  103. package/Modal/index.d.ts +1 -1
  104. package/Navigation/Navigation.js +20 -11
  105. package/Navigation/NavigationHeader.js +2 -1
  106. package/Navigation/NavigationIconButton.d.ts +8 -0
  107. package/Navigation/NavigationIconButton.js +2 -2
  108. package/Navigation/NavigationOption.js +6 -3
  109. package/Navigation/NavigationOverflowMenu.js +9 -6
  110. package/Navigation/NavigationUserMenu.js +3 -1
  111. package/Navigation/context.d.ts +1 -0
  112. package/NotificationCenter/NotificationCenter.d.ts +59 -9
  113. package/NotificationCenter/NotificationCenter.js +150 -54
  114. package/NotificationCenter/NotificationCenterDrawer.d.ts +56 -6
  115. package/NotificationCenter/NotificationCenterDrawer.js +6 -8
  116. package/NotificationCenter/index.d.ts +1 -1
  117. package/OverflowTooltip/OverflowTooltip.js +46 -5
  118. package/PageFooter/PageFooter.d.ts +1 -1
  119. package/PageFooter/PageFooter.js +8 -16
  120. package/Pagination/Pagination.d.ts +38 -1
  121. package/Pagination/Pagination.js +38 -1
  122. package/Pagination/PaginationPageSize.js +1 -1
  123. package/Pagination/usePagination.d.ts +20 -0
  124. package/Pagination/usePagination.js +20 -0
  125. package/Progress/Progress.d.ts +24 -1
  126. package/Progress/Progress.js +24 -1
  127. package/README.md +82 -0
  128. package/Radio/Radio.d.ts +35 -1
  129. package/Radio/Radio.js +50 -12
  130. package/Radio/RadioGroup.d.ts +2 -2
  131. package/Radio/RadioGroupContext.d.ts +3 -2
  132. package/Section/SectionGroup.d.ts +20 -0
  133. package/Section/SectionGroup.js +16 -0
  134. package/Section/index.d.ts +2 -0
  135. package/Section/index.js +1 -0
  136. package/Select/Select.d.ts +55 -1
  137. package/Select/Select.js +37 -2
  138. package/Select/typings.d.ts +9 -0
  139. package/{Selection/Selection.d.ts → SelectionCard/SelectionCard.d.ts} +23 -11
  140. package/{Selection/Selection.js → SelectionCard/SelectionCard.js} +14 -14
  141. package/SelectionCard/SelectionCardGroup.d.ts +28 -0
  142. package/SelectionCard/index.d.ts +12 -0
  143. package/SelectionCard/index.js +5 -0
  144. package/Separator/Separator.d.ts +19 -1
  145. package/Separator/Separator.js +19 -1
  146. package/Slider/Slider.d.ts +11 -0
  147. package/Slider/useSlider.d.ts +21 -0
  148. package/Spin/Spin.d.ts +14 -5
  149. package/Spin/Spin.js +6 -8
  150. package/Stepper/Stepper.d.ts +34 -1
  151. package/Stepper/Stepper.js +34 -1
  152. package/Stepper/useStepper.d.ts +18 -0
  153. package/Stepper/useStepper.js +18 -0
  154. package/Tab/Tab.d.ts +33 -1
  155. package/Tab/Tab.js +33 -1
  156. package/Table/Table.d.ts +51 -3
  157. package/Table/Table.js +52 -4
  158. package/Table/hooks/useTableDataSource.d.ts +4 -4
  159. package/Table/hooks/useTableDataSource.js +1 -1
  160. package/Tag/Tag.d.ts +27 -1
  161. package/Tag/Tag.js +27 -1
  162. package/Tag/typings.d.ts +28 -0
  163. package/Textarea/Textarea.d.ts +28 -1
  164. package/Textarea/Textarea.js +25 -1
  165. package/TimePicker/TimePicker.js +1 -1
  166. package/TimeRangePicker/TimeRangePicker.js +1 -1
  167. package/Toggle/Toggle.d.ts +34 -1
  168. package/Toggle/Toggle.js +34 -1
  169. package/Tooltip/Tooltip.d.ts +39 -1
  170. package/Tooltip/Tooltip.js +39 -1
  171. package/Tooltip/useDelayMouseEnterLeave.d.ts +17 -0
  172. package/Tooltip/useDelayMouseEnterLeave.js +17 -0
  173. package/Typography/Typography.d.ts +23 -1
  174. package/Typography/Typography.js +23 -1
  175. package/Upload/Upload.d.ts +13 -7
  176. package/Upload/Upload.js +55 -20
  177. package/Upload/UploadItem.d.ts +3 -3
  178. package/Upload/UploadItem.js +11 -8
  179. package/Upload/UploadPictureCard.d.ts +5 -0
  180. package/Upload/UploadPictureCard.js +10 -6
  181. package/Upload/Uploader.d.ts +32 -31
  182. package/Upload/Uploader.js +10 -9
  183. package/hooks/useClickAway.d.ts +16 -0
  184. package/hooks/useClickAway.js +16 -0
  185. package/hooks/useComposeRefs.d.ts +15 -0
  186. package/hooks/useComposeRefs.js +15 -0
  187. package/hooks/useDocumentEscapeKeyDown.d.ts +15 -0
  188. package/hooks/useDocumentEscapeKeyDown.js +15 -0
  189. package/hooks/useDocumentEvents.d.ts +16 -0
  190. package/hooks/useDocumentEvents.js +16 -0
  191. package/hooks/useDocumentTabKeyDown.d.ts +15 -0
  192. package/hooks/useDocumentTabKeyDown.js +15 -0
  193. package/hooks/useIsomorphicLayoutEffect.d.ts +16 -0
  194. package/hooks/useIsomorphicLayoutEffect.js +16 -0
  195. package/hooks/useLastCallback.d.ts +15 -0
  196. package/hooks/useLastCallback.js +15 -0
  197. package/hooks/useLastValue.d.ts +18 -0
  198. package/hooks/useLastValue.js +18 -0
  199. package/hooks/usePreviousValue.d.ts +14 -0
  200. package/hooks/usePreviousValue.js +14 -0
  201. package/hooks/useScrollLock.d.ts +6 -4
  202. package/hooks/useScrollLock.js +6 -4
  203. package/hooks/useTopStack.d.ts +17 -0
  204. package/hooks/useTopStack.js +17 -0
  205. package/hooks/useWindowWidth.d.ts +14 -0
  206. package/hooks/useWindowWidth.js +14 -0
  207. package/index.d.ts +6 -4
  208. package/index.js +3 -2
  209. package/llms.txt +200 -0
  210. package/package.json +5 -4
  211. package/Selection/SelectionGroup.d.ts +0 -28
  212. package/Selection/index.d.ts +0 -12
  213. package/Selection/index.js +0 -5
@@ -12,7 +12,45 @@ import Popper from '../Popper/Popper.js';
12
12
  import cx from 'clsx';
13
13
 
14
14
  /**
15
- * The react component for `mezzanine` tooltip.
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
- * The react component for `mezzanine` typography.
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;
@@ -13,7 +13,29 @@ function getComponentFromType(type) {
13
13
  return 'span';
14
14
  }
15
15
  /**
16
- * The react component for `mezzanine` typography.
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;
@@ -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 to display with the upload component.
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
- const { accept, className, disabled = false, 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, ...rest } = props;
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 && file.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
- isFillWidth: true,
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
- isFillWidth: true,
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: hints, onUpload: handleUpload, ...uploaderConfig }));
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: hints, onUpload: handleUpload, ...topUploaderConfig })) : null;
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 === 'cards' ? uploadClasses.fillWidthHints : uploadClasses.hints;
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, mode === 'button-list' && hintsElement] })), jsxs("div", { className: cx(uploadClasses.uploadList, shouldUsePictureCard && uploadClasses.uploadListCards), children: [shouldUsePictureCard && (jsxs(Fragment, { children: [imageFiles.map((uploadFile) => (jsx(UploadPictureCard, { 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 && {
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))), !isSingleFileCardMode && uploaderElement, isSingleFileCardMode &&
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
- uploaderElement, isSingleFileCardMode && (jsx("input", { ref: replaceInputRef, accept: accept, style: { display: 'none' }, type: "file", onChange: (e) => {
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 };
@@ -46,9 +46,9 @@ export interface UploadItemProps extends NativeElementPropsWithoutKeyAndRef<'div
46
46
  */
47
47
  disabled?: boolean;
48
48
  /**
49
- * The status of the item.
50
- * @default 'loading'
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'.
@@ -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, SpinnerIcon, DownloadIcon, ResetIcon, TrashIcon, DangerousFilledIcon } from '@mezzanine-ui/icons';
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(Icon, { icon: SpinnerIcon, color: "brand", spin: true, size: 16 }) }));
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 = node;
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
- }, 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 })) }), showFileSize && isFinished && fileSize && (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, 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 })] }))] }));
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, SpinnerIcon, ZoomInIcon, DownloadIcon, TrashIcon, ResetIcon } from '@mezzanine-ui/icons';
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(Icon, { icon: SpinnerIcon, color: "fixed-light", spin: true, size: 32 }) })] })), 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: onZoomIn, "aria-label": labels.zoomIn })), onDownload && (jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: DownloadIcon, onClick: onDownload, "aria-label": labels.download })), jsx(Button, { variant: "base-secondary", size: "minor", iconType: "icon-only", icon: TrashIcon, onClick: onDelete, "aria-label": labels.delete })] }) }), onReplace && (jsx("span", { className: uploadPictureCardClasses.replaceLabel, children: labels.clickToReplace }))] })), status === 'error' && size !== 'minor' && (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' && (jsx(Icon, { icon: ZoomInIcon, color: "fixed-light", size: 24 }))] })] }) }));
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 };