@mezzanine-ui/react 1.0.0-beta.1 → 1.0.0-beta.3

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 (172) hide show
  1. package/Anchor/Anchor.d.ts +51 -18
  2. package/Anchor/Anchor.js +15 -15
  3. package/Anchor/AnchorGroup.d.ts +34 -0
  4. package/Anchor/AnchorGroup.js +37 -0
  5. package/Anchor/AnchorItem.d.ts +30 -0
  6. package/Anchor/AnchorItem.js +65 -0
  7. package/Anchor/index.d.ts +2 -0
  8. package/Anchor/index.js +1 -0
  9. package/Anchor/utils.d.ts +13 -0
  10. package/Anchor/utils.js +95 -0
  11. package/AutoComplete/AutoComplete.d.ts +194 -0
  12. package/AutoComplete/AutoComplete.js +419 -0
  13. package/AutoComplete/index.d.ts +2 -0
  14. package/AutoComplete/index.js +1 -0
  15. package/AutoComplete/useAutoCompleteCreation.d.ts +33 -0
  16. package/AutoComplete/useAutoCompleteCreation.js +201 -0
  17. package/AutoComplete/useAutoCompleteKeyboard.d.ts +31 -0
  18. package/AutoComplete/useAutoCompleteKeyboard.js +149 -0
  19. package/AutoComplete/useAutoCompleteSearch.d.ts +16 -0
  20. package/AutoComplete/useAutoCompleteSearch.js +69 -0
  21. package/AutoComplete/useCreationTracker.d.ts +17 -0
  22. package/AutoComplete/useCreationTracker.js +47 -0
  23. package/Badge/Badge.js +2 -2
  24. package/Breadcrumb/BreadcrumbItem.d.ts +1 -1
  25. package/Button/Button.js +13 -11
  26. package/Button/index.d.ts +1 -1
  27. package/Button/typings.d.ts +27 -4
  28. package/Description/Description.d.ts +30 -0
  29. package/Description/Description.js +13 -0
  30. package/Description/DescriptionContent.d.ts +41 -0
  31. package/Description/DescriptionContent.js +14 -0
  32. package/Description/DescriptionGroup.d.ts +13 -0
  33. package/Description/DescriptionGroup.js +12 -0
  34. package/Description/DescriptionTitle.d.ts +45 -0
  35. package/Description/DescriptionTitle.js +17 -0
  36. package/Description/index.d.ts +8 -0
  37. package/Description/index.js +4 -0
  38. package/Dropdown/Dropdown.d.ts +43 -3
  39. package/Dropdown/Dropdown.js +154 -35
  40. package/Dropdown/DropdownAction.d.ts +1 -1
  41. package/Dropdown/DropdownAction.js +1 -4
  42. package/Dropdown/DropdownItem.d.ts +21 -4
  43. package/Dropdown/DropdownItem.js +23 -10
  44. package/Dropdown/DropdownItemCard.d.ts +5 -5
  45. package/Dropdown/DropdownItemCard.js +11 -10
  46. package/Dropdown/DropdownStatus.d.ts +2 -2
  47. package/Dropdown/DropdownStatus.js +29 -0
  48. package/Dropdown/dropdownKeydownHandler.d.ts +2 -1
  49. package/Dropdown/dropdownKeydownHandler.js +73 -0
  50. package/Dropdown/highlightText.js +5 -1
  51. package/Dropdown/shortcutTextHandler.d.ts +24 -0
  52. package/Dropdown/shortcutTextHandler.js +171 -0
  53. package/Form/FormControlContext.d.ts +2 -2
  54. package/Form/FormField.d.ts +56 -4
  55. package/Form/FormField.js +10 -6
  56. package/Form/FormHintText.d.ts +24 -1
  57. package/Form/FormHintText.js +4 -4
  58. package/Form/FormLabel.d.ts +6 -3
  59. package/Form/FormLabel.js +5 -3
  60. package/Input/Input.d.ts +29 -3
  61. package/Input/Input.js +22 -6
  62. package/Input/PasswordStrengthIndicator/PasswordStrengthIndicator.js +1 -1
  63. package/Modal/Modal.d.ts +103 -11
  64. package/Modal/Modal.js +14 -9
  65. package/Modal/ModalBodyForVerification.d.ts +59 -0
  66. package/Modal/ModalBodyForVerification.js +99 -0
  67. package/Modal/ModalControl.d.ts +2 -2
  68. package/Modal/ModalControl.js +1 -1
  69. package/Modal/ModalFooter.d.ts +119 -1
  70. package/Modal/ModalFooter.js +15 -3
  71. package/Modal/ModalHeader.d.ts +26 -7
  72. package/Modal/ModalHeader.js +33 -7
  73. package/Modal/index.d.ts +4 -5
  74. package/Modal/index.js +1 -2
  75. package/Modal/useModalContainer.d.ts +12 -3
  76. package/Modal/useModalContainer.js +28 -6
  77. package/Navigation/CollapsedMenu.d.ts +6 -0
  78. package/Navigation/CollapsedMenu.js +16 -0
  79. package/Navigation/Navigation.d.ts +17 -3
  80. package/Navigation/Navigation.js +48 -33
  81. package/Navigation/NavigationFooter.js +4 -2
  82. package/Navigation/NavigationHeader.d.ts +11 -1
  83. package/Navigation/NavigationHeader.js +6 -3
  84. package/Navigation/NavigationOption.d.ts +3 -2
  85. package/Navigation/NavigationOption.js +45 -26
  86. package/Navigation/NavigationOptionCategory.js +20 -2
  87. package/Navigation/context.d.ts +2 -0
  88. package/Navigation/useVisibleItems.d.ts +5 -0
  89. package/Navigation/useVisibleItems.js +54 -0
  90. package/NotificationCenter/NotificationCenter.d.ts +124 -0
  91. package/NotificationCenter/NotificationCenter.js +259 -0
  92. package/NotificationCenter/NotificationCenterDrawer.d.ts +89 -0
  93. package/NotificationCenter/index.d.ts +3 -0
  94. package/NotificationCenter/index.js +1 -0
  95. package/PageFooter/PageFooter.d.ts +19 -9
  96. package/PageFooter/PageFooter.js +10 -10
  97. package/PageHeader/PageHeader.js +4 -12
  98. package/PageToolbar/PageToolbar.d.ts +2 -6
  99. package/PageToolbar/utils.js +4 -12
  100. package/Select/index.d.ts +0 -2
  101. package/Select/index.js +0 -1
  102. package/Slider/useSlider.js +1 -1
  103. package/Table/Table.d.ts +53 -15
  104. package/Table/Table.js +178 -82
  105. package/Table/TableContext.d.ts +18 -42
  106. package/Table/components/TableActionsCell.d.ts +26 -0
  107. package/Table/components/TableActionsCell.js +78 -0
  108. package/Table/components/TableBody.d.ts +2 -5
  109. package/Table/components/TableBody.js +16 -19
  110. package/Table/components/TableBulkActions.d.ts +15 -0
  111. package/Table/components/TableBulkActions.js +26 -0
  112. package/Table/components/TableCell.d.ts +2 -0
  113. package/Table/components/TableCell.js +42 -10
  114. package/Table/components/TableColGroup.js +10 -112
  115. package/Table/components/TableColumnTitleMenu.d.ts +6 -0
  116. package/Table/components/TableColumnTitleMenu.js +20 -0
  117. package/Table/components/TableDragHandleCell.d.ts +2 -0
  118. package/Table/components/TableDragHandleCell.js +8 -1
  119. package/Table/components/TableExpandCell.d.ts +2 -0
  120. package/Table/components/TableExpandCell.js +8 -1
  121. package/Table/components/TableExpandedRow.js +3 -2
  122. package/Table/components/TableHeader.d.ts +2 -4
  123. package/Table/components/TableHeader.js +11 -14
  124. package/Table/components/TableResizeHandle.js +3 -7
  125. package/Table/components/TableRow.js +54 -20
  126. package/Table/components/TableSelectionCell.d.ts +5 -0
  127. package/Table/components/TableSelectionCell.js +12 -1
  128. package/Table/components/index.d.ts +1 -0
  129. package/Table/components/index.js +1 -0
  130. package/Table/hooks/index.d.ts +1 -1
  131. package/Table/hooks/index.js +1 -1
  132. package/Table/hooks/useTableDataSource.d.ts +2 -2
  133. package/Table/hooks/useTableExpansion.js +0 -6
  134. package/Table/hooks/useTableFixedOffsets.d.ts +1 -1
  135. package/Table/hooks/useTableFixedOffsets.js +24 -26
  136. package/Table/hooks/useTableResizedColumns.d.ts +2 -0
  137. package/Table/hooks/useTableResizedColumns.js +22 -0
  138. package/Table/hooks/useTableScroll.d.ts +3 -1
  139. package/Table/hooks/useTableScroll.js +25 -19
  140. package/Table/hooks/useTableSelection.js +32 -8
  141. package/Table/hooks/useTableVirtualization.d.ts +1 -1
  142. package/Table/index.d.ts +4 -4
  143. package/Table/index.js +5 -3
  144. package/Table/utils/calculateColumnWidths.d.ts +28 -0
  145. package/Table/utils/calculateColumnWidths.js +80 -0
  146. package/Table/utils/index.d.ts +2 -0
  147. package/Table/utils/index.js +1 -0
  148. package/Table/utils/useTableRowSelection.d.ts +5 -5
  149. package/Table/utils/useTableRowSelection.js +14 -6
  150. package/Tag/TagGroup.d.ts +3 -0
  151. package/Tag/index.d.ts +2 -0
  152. package/Tag/index.js +1 -0
  153. package/Upload/UploadPictureCard.js +1 -1
  154. package/index.d.ts +36 -20
  155. package/index.js +26 -7
  156. package/package.json +4 -4
  157. package/utils/format-number-with-commas.d.ts +4 -0
  158. package/utils/format-number-with-commas.js +27 -0
  159. package/utils/parse-number-with-commas.d.ts +4 -0
  160. package/utils/parse-number-with-commas.js +22 -0
  161. package/Modal/ModalActions.d.ts +0 -9
  162. package/Modal/ModalActions.js +0 -20
  163. package/Modal/ModalBody.d.ts +0 -7
  164. package/Modal/ModalBody.js +0 -14
  165. package/Notification/Notification.d.ts +0 -54
  166. package/Notification/Notification.js +0 -76
  167. package/Notification/index.d.ts +0 -3
  168. package/Notification/index.js +0 -1
  169. package/Select/AutoComplete.d.ts +0 -107
  170. package/Select/AutoComplete.js +0 -114
  171. package/Table/hooks/useTableColumns.d.ts +0 -8
  172. package/Table/hooks/useTableColumns.js +0 -91
package/Input/Input.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- import { Ref, ChangeEventHandler } from 'react';
2
- import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
1
+ import { DropdownOption } from '@mezzanine-ui/core/dropdown';
2
+ import { ChangeEventHandler, Ref } from 'react';
3
+ import { PopperPlacement } from '../Popper';
3
4
  import { TextFieldAffixProps, TextFieldProps } from '../TextField';
5
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
6
  import { ActionButtonProps } from './ActionButton';
5
- import { SelectButtonProps } from './SelectButton';
6
7
  import { PasswordStrengthIndicatorProps } from './PasswordStrengthIndicator';
8
+ import { SelectButtonProps } from './SelectButton';
7
9
  /**
8
10
  * Base props shared by all Input variants
9
11
  */
@@ -152,6 +154,30 @@ export type SelectInputProps = InputBaseProps & {
152
154
  selectButton: SelectButtonProps & {
153
155
  position: 'prefix' | 'suffix' | 'both';
154
156
  };
157
+ /**
158
+ * The options of the dropdown.
159
+ */
160
+ options?: DropdownOption[];
161
+ /**
162
+ * The selected value of the dropdown.
163
+ */
164
+ selectedValue?: string;
165
+ /**
166
+ * The onChange event handler of the dropdown.
167
+ */
168
+ onSelect?: (value: string) => void;
169
+ /**
170
+ * The width of the dropdown.
171
+ */
172
+ dropdownWidth?: number | string;
173
+ /**
174
+ * The max height of the dropdown.
175
+ */
176
+ dropdownMaxHeight?: number | string;
177
+ /**
178
+ * The placement of the dropdown.
179
+ */
180
+ dropdownPlacement?: PopperPlacement;
155
181
  };
156
182
  /**
157
183
  * 8. Password Input - Password input with visibility toggle
package/Input/Input.js CHANGED
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
- import { forwardRef, useRef, useState, useCallback } from 'react';
4
3
  import { inputClasses } from '@mezzanine-ui/core/input';
5
- import { useComposeRefs } from '../hooks/useComposeRefs.js';
6
- import { useInputWithClearControlValue } from '../Form/useInputWithClearControlValue.js';
7
4
  import { EyeIcon, EyeInvisibleIcon, SearchIcon } from '@mezzanine-ui/icons';
5
+ import { forwardRef, useRef, useState, useCallback } from 'react';
6
+ import { useInputWithClearControlValue } from '../Form/useInputWithClearControlValue.js';
7
+ import { useComposeRefs } from '../hooks/useComposeRefs.js';
8
8
  import Icon from '../Icon/Icon.js';
9
+ import Dropdown from '../Dropdown/Dropdown.js';
9
10
  import SelectButton from './SelectButton/SelectButton.js';
10
11
  import ActionButton from './ActionButton/ActionButton.js';
11
12
  import SpinnerButton from './SpinnerButton/SpinnerButton.js';
@@ -158,16 +159,31 @@ const Input = forwardRef(function Input(props, ref) {
158
159
  }
159
160
  case 'select': {
160
161
  const selectProps = props;
161
- const { selectButton } = selectProps;
162
+ const { selectButton, options, dropdownWidth = 120, dropdownMaxHeight = 114 } = selectProps;
163
+ const defaultOptions = options || [];
164
+ const selectedOptions = defaultOptions.length > 0
165
+ ? defaultOptions.map((option) => ({
166
+ ...option,
167
+ ...(option.id === selectProps.selectedValue
168
+ ? { checkSite: 'suffix' }
169
+ : {}),
170
+ }))
171
+ : [];
162
172
  if (selectButton.position === 'both' ||
163
173
  selectButton.position === 'prefix') {
164
174
  const { ...restSelectButtonProps } = selectButton;
165
- prefixExternalButton = (jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }));
175
+ prefixExternalButton = (jsx(Dropdown, { options: selectedOptions, value: selectProps.selectedValue, customWidth: dropdownWidth, maxHeight: dropdownMaxHeight, placement: "bottom-start", onSelect: (option) => {
176
+ var _a;
177
+ (_a = selectProps.onSelect) === null || _a === void 0 ? void 0 : _a.call(selectProps, option.id);
178
+ }, children: jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }) }));
166
179
  }
167
180
  if (selectButton.position === 'both' ||
168
181
  selectButton.position === 'suffix') {
169
182
  const { ...restSelectButtonProps } = selectButton;
170
- suffixExternalButton = (jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }));
183
+ suffixExternalButton = (jsx(Dropdown, { options: selectedOptions, value: selectProps.selectedValue, customWidth: dropdownWidth, maxHeight: dropdownMaxHeight, placement: "bottom-start", onSelect: (option) => {
184
+ var _a;
185
+ (_a = selectProps.onSelect) === null || _a === void 0 ? void 0 : _a.call(selectProps, option.id);
186
+ }, children: jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }) }));
171
187
  }
172
188
  break;
173
189
  }
@@ -12,7 +12,7 @@ const PasswordStrengthIndicator = forwardRef(function PasswordStrengthIndicator(
12
12
  const { className, strength = 'weak', strengthText: strengthTextProp, strengthTextPrefix = '密碼強度:', hintTexts, ...rest } = props;
13
13
  const strengthText = strengthTextProp ||
14
14
  (strength === 'weak' ? '低' : strength === 'medium' ? '中' : '高');
15
- return (jsxs("div", { ref: ref, className: cx(inputPasswordStrengthIndicatorClasses.host, className), ...rest, children: [jsx("div", { className: cx(inputPasswordStrengthIndicatorClasses.bar, inputPasswordStrengthIndicatorClasses.barState(strength)) }), jsxs("span", { className: inputPasswordStrengthIndicatorClasses.text, children: [strengthTextPrefix, jsx("mark", { children: strengthText })] }), hintTexts && hintTexts.length > 0 && (jsx("div", { className: inputPasswordStrengthIndicatorClasses.hintTextGroup, children: hintTexts.map((hintText, idx) => (jsx(FormHintText, { severity: hintText.severity, children: hintText.hint }, idx))) }))] }));
15
+ return (jsxs("div", { ref: ref, className: cx(inputPasswordStrengthIndicatorClasses.host, className), ...rest, children: [jsx("div", { className: cx(inputPasswordStrengthIndicatorClasses.bar, inputPasswordStrengthIndicatorClasses.barState(strength)) }), jsxs("span", { className: inputPasswordStrengthIndicatorClasses.text, children: [strengthTextPrefix, jsx("mark", { children: strengthText })] }), hintTexts && hintTexts.length > 0 && (jsx("div", { className: inputPasswordStrengthIndicatorClasses.hintTextGroup, children: hintTexts.map((hintText, idx) => (jsx(FormHintText, { hintText: hintText.hint, severity: hintText.severity }, idx))) }))] }));
16
16
  });
17
17
 
18
18
  export { PasswordStrengthIndicator as default };
package/Modal/Modal.d.ts CHANGED
@@ -1,34 +1,126 @@
1
- import { ModalSeverity, ModalSize } from '@mezzanine-ui/core/modal';
2
- import { SlideFadeOverlayProps } from '../_internal/SlideFadeOverlay';
1
+ import { ModalSize, ModalStatusType } from '@mezzanine-ui/core/modal';
2
+ import { ModalContainerProps } from './useModalContainer';
3
3
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
- export interface ModalProps extends Omit<SlideFadeOverlayProps, 'children'>, Pick<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'> {
4
+ import { ModalHeaderProps } from './ModalHeader';
5
+ import { ModalFooterProps } from './ModalFooter';
6
+ interface CommonModalProps extends Omit<ModalContainerProps, 'children'>, Pick<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'>, Partial<Omit<ModalHeaderProps, 'children' | 'className' | 'title'>>, Partial<Omit<ModalFooterProps, 'children' | 'className' | 'confirmText'>> {
5
7
  /**
6
8
  * Whether to force full screen on any breakpoint.
7
9
  * @default false
8
10
  */
9
11
  fullScreen?: boolean;
10
- /**
11
- * Controls whether or not to hide close button at top-end.
12
- * @default false
13
- */
14
- hideCloseIcon?: boolean;
15
12
  /**
16
13
  * Whether the modal is loading.
17
14
  * Controls the loading prop of confirm button in modal actions.
18
15
  */
19
16
  loading?: boolean;
20
17
  /**
21
- * Controlls whether or not to display status icon before title. <br />
18
+ * Controls whether or not to display status icon before title.
22
19
  * Notice that giving a status will only display the regular title.
23
20
  * @default 'info'
24
21
  */
25
- severity?: ModalSeverity;
22
+ modalStatusType?: ModalStatusType;
23
+ /**
24
+ * Controls whether or not to show dismiss button at top-end.
25
+ * @default false
26
+ */
27
+ showDismissButton?: boolean;
28
+ }
29
+ interface ExtendedSplitModalProps extends CommonModalProps {
30
+ /**
31
+ * Content for the left side in extendedSplit layout.
32
+ * Required when modalType is 'extendedSplit'.
33
+ */
34
+ extendedSplitLeftSideContent: React.ReactNode;
35
+ /**
36
+ * Content for the right side in extendedSplit layout.
37
+ * Required when modalType is 'extendedSplit'.
38
+ */
39
+ extendedSplitRightSideContent: React.ReactNode;
40
+ /**
41
+ * Controls the type/layout of the modal.
42
+ * - 'extendedSplit': Modal with split layout (footer inside left content)
43
+ */
44
+ modalType: 'extendedSplit';
26
45
  /**
27
46
  * Controls the size of the modal.
28
- * @default "medium"
47
+ * For extendedSplit type, only 'wide' is allowed.
48
+ * @default 'wide'
49
+ */
50
+ size?: 'wide';
51
+ }
52
+ interface OtherModalProps extends CommonModalProps {
53
+ /**
54
+ * Content for the left side in extendedSplit layout.
55
+ * Cannot be provided when modalType is not 'extendedSplit'.
56
+ */
57
+ extendedSplitLeftSideContent?: never;
58
+ /**
59
+ * Content for the right side in extendedSplit layout.
60
+ * Cannot be provided when modalType is not 'extendedSplit'.
61
+ */
62
+ extendedSplitRightSideContent?: never;
63
+ /**
64
+ * Controls the type/layout of the modal.
65
+ * - 'standard': Default modal with body container
66
+ * - 'extended': Modal with left and right content areas
67
+ * - 'mediaPreview': Modal for media preview
68
+ * - 'verification': Modal for verification flows
69
+ * @default 'standard'
70
+ */
71
+ modalType: 'extended' | 'standard' | 'mediaPreview' | 'verification';
72
+ /**
73
+ * Controls the size of the modal.
74
+ * @default 'regular'
29
75
  */
30
76
  size?: ModalSize;
31
77
  }
78
+ type BaseModalProps = ExtendedSplitModalProps | OtherModalProps;
79
+ type ModalHeaderPropsWithHeader = {
80
+ /**
81
+ * Whether to show modal header.
82
+ */
83
+ showModalHeader: true;
84
+ /**
85
+ * The title of the modal header (required when showModalHeader is true).
86
+ */
87
+ title: string;
88
+ };
89
+ type ModalHeaderPropsWithoutHeader = {
90
+ /**
91
+ * Whether to show modal header.
92
+ * @default false
93
+ */
94
+ showModalHeader?: false;
95
+ /**
96
+ * The title of the modal header.
97
+ * Cannot be provided when showModalHeader is false.
98
+ */
99
+ title?: never;
100
+ };
101
+ type ModalFooterPropsWithFooter = {
102
+ /**
103
+ * Whether to show modal footer.
104
+ */
105
+ showModalFooter: true;
106
+ /**
107
+ * The confirm button text of the modal footer (required when showModalFooter is true).
108
+ */
109
+ confirmText: string;
110
+ };
111
+ type ModalFooterPropsWithoutFooter = {
112
+ /**
113
+ * Whether to show modal footer.
114
+ * @default false
115
+ */
116
+ showModalFooter?: false;
117
+ /**
118
+ * The confirm button text of the modal footer.
119
+ * Cannot be provided when showModalFooter is false.
120
+ */
121
+ confirmText?: never;
122
+ };
123
+ export type ModalProps = BaseModalProps & ((ModalHeaderPropsWithHeader & ModalFooterPropsWithFooter) | (ModalHeaderPropsWithHeader & ModalFooterPropsWithoutFooter) | (ModalHeaderPropsWithoutHeader & ModalFooterPropsWithFooter) | (ModalHeaderPropsWithoutHeader & ModalFooterPropsWithoutFooter));
32
124
  /**
33
125
  * The react component for `mezzanine` modal.
34
126
  */
package/Modal/Modal.js CHANGED
@@ -1,26 +1,31 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { modalClasses } from '@mezzanine-ui/core/modal';
3
- import { TimesIcon } from '@mezzanine-ui/icons';
4
3
  import { forwardRef, useMemo } from 'react';
5
4
  import { ModalControlContext } from './ModalControl.js';
6
5
  import useModalContainer from './useModalContainer.js';
7
- import Icon from '../Icon/Icon.js';
6
+ import ModalHeader from './ModalHeader.js';
7
+ import ModalFooter from './ModalFooter.js';
8
+ import ClearActions from '../ClearActions/ClearActions.js';
8
9
  import cx from 'clsx';
9
10
 
10
11
  /**
11
12
  * The react component for `mezzanine` modal.
12
13
  */
13
14
  const Modal = forwardRef(function Modal(props, ref) {
14
- const { children, className, container, direction = 'down', disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal = false, fullScreen = false, hideCloseIcon = false, loading = false, onBackdropClick, onClose, open, severity = 'info', size = 'medium', ...rest } = props;
15
+ const { actionsButtonLayout, annotation, auxiliaryContentButtonProps, auxiliaryContentButtonText, auxiliaryContentChecked, auxiliaryContentLabel, auxiliaryContentOnChange, auxiliaryContentOnClick, auxiliaryContentType, cancelButtonProps, cancelText, children, className, confirmButtonProps, confirmText, container, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal = false, extendedSplitLeftSideContent, extendedSplitRightSideContent, fullScreen = false, loading = false, modalStatusType = 'info', modalType = 'standard', onBackdropClick, onCancel, onClose, onConfirm, open, passwordButtonProps, passwordButtonText, passwordChecked, passwordCheckedLabel, passwordCheckedOnChange, passwordOnClick, showCancelButton, showDismissButton = false, showModalFooter = false, showModalHeader, showStatusTypeIcon, size = 'regular', statusTypeIconLayout, supportingText, supportingTextAlign, title, titleAlign, ...rest } = props;
15
16
  const modalControl = useMemo(() => ({
16
17
  loading,
17
- severity,
18
- }), [loading, severity]);
18
+ modalStatusType: modalStatusType,
19
+ }), [loading, modalStatusType]);
19
20
  const { Container: ModalContainer } = useModalContainer();
20
- return (jsx(ModalControlContext.Provider, { value: modalControl, children: jsx(ModalContainer, { className: modalClasses.overlay, container: container, direction: direction, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown: disableCloseOnEscapeKeyDown, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, ref: ref, children: jsxs("div", { ...rest, className: cx(modalClasses.host, modalClasses.severity(severity), modalClasses.size(size), {
21
+ const renderModalFooter = () => (jsx(ModalFooter, { actionsButtonLayout: actionsButtonLayout, annotation: annotation, auxiliaryContentButtonProps: auxiliaryContentButtonProps, auxiliaryContentButtonText: auxiliaryContentButtonText, auxiliaryContentChecked: auxiliaryContentChecked, auxiliaryContentLabel: auxiliaryContentLabel, auxiliaryContentOnChange: auxiliaryContentOnChange, auxiliaryContentOnClick: auxiliaryContentOnClick, auxiliaryContentType: auxiliaryContentType, cancelButtonProps: cancelButtonProps, cancelText: cancelText, confirmButtonProps: confirmButtonProps, confirmText: confirmText, loading: loading, onCancel: onCancel, onConfirm: onConfirm, passwordButtonProps: passwordButtonProps, passwordButtonText: passwordButtonText, passwordChecked: passwordChecked, passwordCheckedLabel: passwordCheckedLabel, passwordCheckedOnChange: passwordCheckedOnChange, passwordOnClick: passwordOnClick, showCancelButton: showCancelButton }));
22
+ return (jsx(ModalControlContext.Provider, { value: modalControl, children: jsx(ModalContainer, { className: modalClasses.overlay, container: container, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown: disableCloseOnEscapeKeyDown, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, ref: ref, children: jsxs("div", { ...rest, className: cx(modalClasses.host, modalClasses.modalStatusType(modalStatusType), modalClasses.size(size), {
21
23
  [modalClasses.fullScreen]: fullScreen,
22
- [modalClasses.withCloseIcon]: !hideCloseIcon,
23
- }, className), role: "dialog", children: [children, !hideCloseIcon && (jsx(Icon, { className: modalClasses.closeIcon, icon: TimesIcon, onClick: onClose }))] }) }) }));
24
+ [modalClasses.withCloseIcon]: showDismissButton,
25
+ }, className), role: "dialog", children: [showModalHeader && (jsx(ModalHeader, { showStatusTypeIcon: showStatusTypeIcon, statusTypeIconLayout: statusTypeIconLayout, supportingText: supportingText, supportingTextAlign: supportingTextAlign, title: title, titleAlign: titleAlign })), modalType === 'extendedSplit' && (jsxs("div", { className: modalClasses.modalBodyContainerExtendedSplit, children: [jsx("div", { className: modalClasses.modalBodyContainerExtendedSplitRight, children: extendedSplitRightSideContent }), jsxs("div", { className: modalClasses.modalBodyContainerExtendedSplitLeft, children: [jsx("div", { className: modalClasses.modalBodyContainerExtendedSplitLeftSideContent, children: extendedSplitLeftSideContent }), showModalFooter && renderModalFooter()] })] })), (modalType === 'standard' ||
26
+ modalType === 'verification' ||
27
+ modalType === 'extended' ||
28
+ modalType === 'mediaPreview') && (jsxs(Fragment, { children: [jsx("div", { className: modalClasses.modalBodyContainer, children: children }), showModalFooter && renderModalFooter()] })), showDismissButton && (jsx(ClearActions, { onClick: onClose, className: modalClasses.closeIcon, variant: "base" }))] }) }) }));
24
29
  });
25
30
 
26
31
  export { Modal as default };
@@ -0,0 +1,59 @@
1
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
2
+ export interface ModalBodyForVerificationProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'onChange'> {
3
+ /**
4
+ * Whether to auto focus the first input when mounted.
5
+ * @default true
6
+ */
7
+ autoFocus?: boolean;
8
+ /**
9
+ * Whether the inputs are disabled.
10
+ * @default false
11
+ */
12
+ disabled?: boolean;
13
+ /**
14
+ * Whether the inputs are in error state.
15
+ * @default false
16
+ */
17
+ error?: boolean;
18
+ /**
19
+ * Number of verification code digits.
20
+ * @default 4
21
+ */
22
+ length?: number;
23
+ /**
24
+ * Called when verification code changes.
25
+ */
26
+ onChange?: (value: string) => void;
27
+ /**
28
+ * Called when all digits are filled.
29
+ */
30
+ onComplete?: (value: string) => void;
31
+ /**
32
+ * Called when resend link is clicked.
33
+ */
34
+ onResend?: () => void;
35
+ /**
36
+ * Whether the inputs are readonly.
37
+ * @default false
38
+ */
39
+ readOnly?: boolean;
40
+ /**
41
+ * Prompt text before resend link.
42
+ * @default "收不到驗證碼?"
43
+ */
44
+ resendPrompt?: string;
45
+ /**
46
+ * Resend link text.
47
+ * @default "點此重新寄送"
48
+ */
49
+ resendText?: string;
50
+ /**
51
+ * Current verification code value.
52
+ */
53
+ value?: string;
54
+ }
55
+ /**
56
+ * The react component for verification code input in modal.
57
+ */
58
+ declare const ModalBodyForVerification: import("react").ForwardRefExoticComponent<ModalBodyForVerificationProps & import("react").RefAttributes<HTMLDivElement>>;
59
+ export default ModalBodyForVerification;
@@ -0,0 +1,99 @@
1
+ 'use client';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useState, useRef, useEffect } from 'react';
4
+ import { modalClasses } from '@mezzanine-ui/core/modal';
5
+ import Typography from '../Typography/Typography.js';
6
+ import cx from 'clsx';
7
+
8
+ /**
9
+ * The react component for verification code input in modal.
10
+ */
11
+ const ModalBodyForVerification = forwardRef(function ModalBodyForVerification(props, ref) {
12
+ const { autoFocus = true, className, disabled = false, error = false, length = 4, onChange, onComplete, onResend, readOnly = false, resendPrompt = '收不到驗證碼?', resendText = '點此重新寄送', value = '', ...rest } = props;
13
+ const [codes, setCodes] = useState(value
14
+ .split('')
15
+ .slice(0, length)
16
+ .concat(Array(length - value.length).fill('')));
17
+ const inputRefs = useRef([]);
18
+ // Auto focus first input when mounted
19
+ useEffect(() => {
20
+ var _a;
21
+ if (autoFocus && !disabled && !readOnly) {
22
+ (_a = inputRefs.current[0]) === null || _a === void 0 ? void 0 : _a.focus();
23
+ }
24
+ }, [autoFocus, disabled, readOnly]);
25
+ const handleChange = (index, newValue) => {
26
+ var _a;
27
+ if (disabled || readOnly)
28
+ return;
29
+ // Only allow single digit/letter
30
+ const sanitized = newValue.slice(-1);
31
+ const newCodes = [...codes];
32
+ newCodes[index] = sanitized;
33
+ setCodes(newCodes);
34
+ const fullValue = newCodes.join('');
35
+ onChange === null || onChange === void 0 ? void 0 : onChange(fullValue);
36
+ // Auto focus next input
37
+ if (sanitized && index < length - 1) {
38
+ (_a = inputRefs.current[index + 1]) === null || _a === void 0 ? void 0 : _a.focus();
39
+ }
40
+ // Call onComplete when all filled
41
+ if (fullValue.length === length) {
42
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(fullValue);
43
+ }
44
+ };
45
+ const handleKeyDown = (index, e) => {
46
+ var _a, _b, _c;
47
+ // Handle backspace
48
+ if (e.key === 'Backspace') {
49
+ if (disabled || readOnly)
50
+ return;
51
+ if (!codes[index] && index > 0) {
52
+ // If current is empty, focus previous
53
+ (_a = inputRefs.current[index - 1]) === null || _a === void 0 ? void 0 : _a.focus();
54
+ }
55
+ else {
56
+ // Clear current
57
+ const newCodes = [...codes];
58
+ newCodes[index] = '';
59
+ setCodes(newCodes);
60
+ onChange === null || onChange === void 0 ? void 0 : onChange(newCodes.join(''));
61
+ }
62
+ }
63
+ // Handle arrow keys
64
+ else if (e.key === 'ArrowLeft' && index > 0) {
65
+ (_b = inputRefs.current[index - 1]) === null || _b === void 0 ? void 0 : _b.focus();
66
+ }
67
+ else if (e.key === 'ArrowRight' && index < length - 1) {
68
+ (_c = inputRefs.current[index + 1]) === null || _c === void 0 ? void 0 : _c.focus();
69
+ }
70
+ };
71
+ const handlePaste = (e) => {
72
+ var _a;
73
+ if (disabled || readOnly)
74
+ return;
75
+ e.preventDefault();
76
+ const pastedData = e.clipboardData.getData('text').slice(0, length);
77
+ const newCodes = pastedData
78
+ .split('')
79
+ .concat(Array(length - pastedData.length).fill(''));
80
+ setCodes(newCodes);
81
+ onChange === null || onChange === void 0 ? void 0 : onChange(pastedData);
82
+ // Focus the next empty input or the last input
83
+ const nextEmptyIndex = Math.min(pastedData.length, length - 1);
84
+ (_a = inputRefs.current[nextEmptyIndex]) === null || _a === void 0 ? void 0 : _a.focus();
85
+ if (pastedData.length === length) {
86
+ onComplete === null || onComplete === void 0 ? void 0 : onComplete(pastedData);
87
+ }
88
+ };
89
+ const handleInputChange = (index, e) => {
90
+ handleChange(index, e.target.value);
91
+ };
92
+ return (jsxs("div", { ...rest, ref: ref, className: cx(modalClasses.modalBodyVerification, className), children: [jsx("div", { className: modalClasses.modalBodyVerificationInputs, children: Array.from({ length }).map((_, index) => (jsx("input", { ref: (el) => {
93
+ inputRefs.current[index] = el;
94
+ }, type: "text", inputMode: "numeric", maxLength: 1, value: codes[index] || '', onChange: (e) => handleInputChange(index, e), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, className: cx(modalClasses.modalBodyVerificationInput, {
95
+ [modalClasses.modalBodyVerificationInputError]: error,
96
+ }), autoComplete: "off", disabled: disabled, readOnly: readOnly }, index))) }), onResend && (jsxs("div", { className: modalClasses.modalBodyVerificationResend, children: [jsx(Typography, { variant: "caption", color: "text-neutral", children: resendPrompt }), jsx(Typography, { variant: "caption", color: "text-neutral", className: modalClasses.modalBodyVerificationResendLink, onClick: onResend, component: "span", style: { cursor: 'pointer', textDecoration: 'underline' }, children: resendText })] }))] }));
97
+ });
98
+
99
+ export { ModalBodyForVerification as default };
@@ -1,6 +1,6 @@
1
- import { ModalSeverity } from '@mezzanine-ui/core/modal';
1
+ import { ModalStatusType } from '@mezzanine-ui/core/modal';
2
2
  export interface ModalControl {
3
3
  loading: boolean;
4
- severity: ModalSeverity;
4
+ modalStatusType: ModalStatusType;
5
5
  }
6
6
  export declare const ModalControlContext: import("react").Context<ModalControl>;
@@ -3,7 +3,7 @@ import { createContext } from 'react';
3
3
 
4
4
  const ModalControlContext = createContext({
5
5
  loading: false,
6
- severity: 'info',
6
+ modalStatusType: 'info',
7
7
  });
8
8
 
9
9
  export { ModalControlContext };
@@ -1,5 +1,123 @@
1
+ import { ReactNode } from 'react';
1
2
  import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
2
- export type ModalFooterProps = NativeElementPropsWithoutKeyAndRef<'div'>;
3
+ import { ButtonProps } from '../Button/Button';
4
+ export interface ModalFooterProps extends NativeElementPropsWithoutKeyAndRef<'div'> {
5
+ /**
6
+ * Layout of action buttons.
7
+ * - 'fixed': Buttons maintain fixed width
8
+ * - 'fill': Buttons expand to fill available space equally
9
+ * @default 'fixed'
10
+ */
11
+ actionsButtonLayout?: 'fill' | 'fixed';
12
+ /**
13
+ * Text to display as annotation on the left side.
14
+ * Only used when auxiliaryContentType is 'annotation'.
15
+ */
16
+ annotation?: ReactNode;
17
+ /**
18
+ * Props for the auxiliary content button.
19
+ * Only used when auxiliaryContentType is 'button'.
20
+ */
21
+ auxiliaryContentButtonProps?: ButtonProps;
22
+ /**
23
+ * Text for the auxiliary content button.
24
+ * Only used when auxiliaryContentType is 'button'.
25
+ */
26
+ auxiliaryContentButtonText?: ReactNode;
27
+ /**
28
+ * Whether the auxiliary content control (checkbox/toggle) is checked.
29
+ * Only used when auxiliaryContentType is 'checkbox' or 'toggle'.
30
+ */
31
+ auxiliaryContentChecked?: boolean;
32
+ /**
33
+ * Label text for the auxiliary content control (checkbox/toggle).
34
+ * Only used when auxiliaryContentType is 'checkbox' or 'toggle'.
35
+ */
36
+ auxiliaryContentLabel?: string;
37
+ /**
38
+ * Change handler for auxiliary content control (checkbox/toggle).
39
+ * Only used when auxiliaryContentType is 'checkbox' or 'toggle'.
40
+ */
41
+ auxiliaryContentOnChange?: (checked: boolean) => void;
42
+ /**
43
+ * Click handler for the auxiliary content button.
44
+ * Only used when auxiliaryContentType is 'button'.
45
+ */
46
+ auxiliaryContentOnClick?: ButtonProps['onClick'];
47
+ /**
48
+ * Type of auxiliary content to show on the left side of the footer.
49
+ * - 'annotation': Display text annotation
50
+ * - 'button': Display a button
51
+ * - 'checkbox': Display a checkbox control
52
+ * - 'toggle': Display a toggle control
53
+ * - 'password': Display password-specific controls (remember me + forgot password)
54
+ * @default undefined (no auxiliary content)
55
+ */
56
+ auxiliaryContentType?: 'annotation' | 'button' | 'checkbox' | 'toggle' | 'password';
57
+ /**
58
+ * Additional props for the cancel button.
59
+ */
60
+ cancelButtonProps?: ButtonProps;
61
+ /**
62
+ * Text content of the cancel button.
63
+ */
64
+ cancelText?: ReactNode;
65
+ /**
66
+ * Additional props for the confirm button.
67
+ */
68
+ confirmButtonProps?: ButtonProps;
69
+ /**
70
+ * Text content of the confirm button.
71
+ */
72
+ confirmText?: ReactNode;
73
+ /**
74
+ * Whether confirm button is loading and cancel button is disabled.
75
+ */
76
+ loading?: boolean;
77
+ /**
78
+ * Click handler for the cancel button.
79
+ */
80
+ onCancel?: ButtonProps['onClick'];
81
+ /**
82
+ * Click handler for the confirm button.
83
+ */
84
+ onConfirm?: ButtonProps['onClick'];
85
+ /**
86
+ * Props for the password auxiliary button.
87
+ * Only used when auxiliaryContentType is 'password'.
88
+ */
89
+ passwordButtonProps?: ButtonProps;
90
+ /**
91
+ * Text for the password auxiliary button (e.g., "Forgot password?").
92
+ * Only used when auxiliaryContentType is 'password'.
93
+ */
94
+ passwordButtonText?: ReactNode;
95
+ /**
96
+ * Whether the password checkbox is checked (e.g., "Remember me").
97
+ * Only used when auxiliaryContentType is 'password'.
98
+ */
99
+ passwordChecked?: boolean;
100
+ /**
101
+ * Label for the password checkbox (e.g., "Remember me").
102
+ * Only used when auxiliaryContentType is 'password'.
103
+ */
104
+ passwordCheckedLabel?: string;
105
+ /**
106
+ * Change handler for the password checkbox.
107
+ * Only used when auxiliaryContentType is 'password'.
108
+ */
109
+ passwordCheckedOnChange?: (checked: boolean) => void;
110
+ /**
111
+ * Click handler for the password auxiliary button.
112
+ * Only used when auxiliaryContentType is 'password'.
113
+ */
114
+ passwordOnClick?: ButtonProps['onClick'];
115
+ /**
116
+ * Whether to show the cancel button.
117
+ * @default true
118
+ */
119
+ showCancelButton?: boolean;
120
+ }
3
121
  /**
4
122
  * The react component for `mezzanine` modal footer.
5
123
  */
@@ -1,14 +1,26 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef } from 'react';
3
3
  import { modalClasses } from '@mezzanine-ui/core/modal';
4
+ import Button from '../Button/Button.js';
5
+ import ButtonGroup from '../Button/ButtonGroup.js';
6
+ import Checkbox from '../Checkbox/Checkbox.js';
7
+ import Toggle from '../Toggle/Toggle.js';
8
+ import Typography from '../Typography/Typography.js';
4
9
  import cx from 'clsx';
5
10
 
6
11
  /**
7
12
  * The react component for `mezzanine` modal footer.
8
13
  */
9
14
  const ModalFooter = forwardRef(function ModalFooter(props, ref) {
10
- const { children, className, ...rest } = props;
11
- return (jsx("div", { ...rest, ref: ref, className: cx(modalClasses.footer, className), children: children }));
15
+ const { children, className, actionsButtonLayout = 'fixed', annotation, auxiliaryContentButtonProps, auxiliaryContentButtonText, auxiliaryContentChecked, auxiliaryContentLabel, auxiliaryContentOnChange, auxiliaryContentOnClick, auxiliaryContentType, cancelButtonProps, cancelText, confirmButtonProps, confirmText, loading, onCancel, onConfirm, passwordButtonProps, passwordButtonText, passwordChecked, passwordCheckedLabel, passwordCheckedOnChange, passwordOnClick, showCancelButton = true, ...rest } = props;
16
+ const { disabled: cancelButtonDisabled = loading } = cancelButtonProps || {};
17
+ const isActionsButtonFillLayout = actionsButtonLayout === 'fill' && !auxiliaryContentType;
18
+ return (jsxs("div", { ...rest, ref: ref, className: cx(modalClasses.modalFooter, {
19
+ [modalClasses.modalFooter + '--password-mode']: auxiliaryContentType === 'password',
20
+ [modalClasses.modalFooter + '--with-auxiliary-content']: !!auxiliaryContentType && auxiliaryContentType !== 'password',
21
+ }, className), children: [auxiliaryContentType === 'password' && (jsxs("div", { className: modalClasses.modalFooterPasswordContainer, children: [jsx(Checkbox, { checked: passwordChecked, label: passwordCheckedLabel, onChange: (e) => passwordCheckedOnChange === null || passwordCheckedOnChange === void 0 ? void 0 : passwordCheckedOnChange(e.target.checked) }), jsx(Button, { variant: "base-text-link", ...passwordButtonProps, onClick: passwordOnClick, children: passwordButtonText })] })), auxiliaryContentType && auxiliaryContentType !== 'password' && (jsxs("div", { className: modalClasses.modalFooterAuxiliaryContentContainer, children: [auxiliaryContentType === 'annotation' && (jsx(Typography, { variant: "caption", color: "text-neutral", children: annotation })), auxiliaryContentType === 'button' && (jsx(Button, { variant: "base-text-link", ...auxiliaryContentButtonProps, onClick: auxiliaryContentOnClick, children: auxiliaryContentButtonText })), auxiliaryContentType === 'checkbox' && (jsx(Checkbox, { checked: auxiliaryContentChecked, label: auxiliaryContentLabel, onChange: (e) => auxiliaryContentOnChange === null || auxiliaryContentOnChange === void 0 ? void 0 : auxiliaryContentOnChange(e.target.checked) })), auxiliaryContentType === 'toggle' && (jsx(Toggle, { checked: auxiliaryContentChecked, label: auxiliaryContentLabel, onChange: (e) => auxiliaryContentOnChange === null || auxiliaryContentOnChange === void 0 ? void 0 : auxiliaryContentOnChange(e.target.checked) }))] })), jsxs(ButtonGroup, { className: cx(modalClasses.modalFooterActionsButtonContainer, {
22
+ [modalClasses.modalFooterActionsButtonContainer + '--fill-layout']: isActionsButtonFillLayout,
23
+ }), children: [showCancelButton && (jsx(Button, { variant: "base-secondary", ...cancelButtonProps, disabled: cancelButtonDisabled, onClick: onCancel, className: modalClasses.modalFooterActionsButton, children: cancelText })), jsx(Button, { variant: "base-primary", ...confirmButtonProps, loading: loading, onClick: onConfirm, className: modalClasses.modalFooterActionsButton, children: confirmText })] }), children] }));
12
24
  });
13
25
 
14
26
  export { ModalFooter as default };