@mezzanine-ui/react 1.0.0-beta.2 → 1.0.0-beta.4

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 (193) 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 +217 -0
  12. package/AutoComplete/AutoComplete.js +433 -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/Breadcrumb/Breadcrumb.js +16 -21
  24. package/Breadcrumb/BreadcrumbDropdown.d.ts +11 -0
  25. package/Breadcrumb/BreadcrumbDropdown.js +22 -0
  26. package/Breadcrumb/BreadcrumbItem.d.ts +2 -3
  27. package/Breadcrumb/BreadcrumbItem.js +13 -31
  28. package/Breadcrumb/BreadcrumbOverflowMenu.d.ts +7 -0
  29. package/Breadcrumb/BreadcrumbOverflowMenu.js +77 -0
  30. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +11 -0
  31. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.js +21 -0
  32. package/Breadcrumb/BreadcrumbOverflowMenuItem.d.ts +3 -0
  33. package/Breadcrumb/BreadcrumbOverflowMenuItem.js +27 -0
  34. package/Breadcrumb/typings.d.ts +21 -39
  35. package/Button/Button.js +13 -11
  36. package/Button/index.d.ts +1 -1
  37. package/Button/typings.d.ts +27 -4
  38. package/Checkbox/index.d.ts +4 -5
  39. package/Checkbox/index.js +1 -5
  40. package/ContentHeader/ContentHeader.d.ts +160 -0
  41. package/ContentHeader/ContentHeader.js +54 -0
  42. package/ContentHeader/index.d.ts +2 -0
  43. package/ContentHeader/index.js +1 -0
  44. package/ContentHeader/utils.d.ts +23 -0
  45. package/ContentHeader/utils.js +215 -0
  46. package/Description/Description.d.ts +12 -22
  47. package/Description/Description.js +4 -24
  48. package/Dropdown/Dropdown.d.ts +46 -1
  49. package/Dropdown/Dropdown.js +99 -14
  50. package/Dropdown/DropdownAction.d.ts +1 -1
  51. package/Dropdown/DropdownAction.js +1 -4
  52. package/Dropdown/DropdownItem.d.ts +28 -1
  53. package/Dropdown/DropdownItem.js +56 -14
  54. package/Dropdown/DropdownItemCard.d.ts +2 -2
  55. package/Dropdown/DropdownItemCard.js +20 -16
  56. package/Dropdown/DropdownStatus.js +29 -0
  57. package/Dropdown/dropdownKeydownHandler.d.ts +2 -1
  58. package/Dropdown/dropdownKeydownHandler.js +73 -0
  59. package/Dropdown/highlightText.js +5 -1
  60. package/Dropdown/shortcutTextHandler.d.ts +24 -0
  61. package/Dropdown/shortcutTextHandler.js +171 -0
  62. package/Empty/Empty.js +2 -1
  63. package/Empty/icons/EmptyMainNotificationIcon.d.ts +4 -0
  64. package/Empty/icons/EmptyMainNotificationIcon.js +9 -0
  65. package/Empty/typings.d.ts +2 -2
  66. package/FilterArea/Filter.d.ts +32 -0
  67. package/FilterArea/Filter.js +23 -0
  68. package/FilterArea/FilterArea.d.ts +58 -0
  69. package/FilterArea/FilterArea.js +31 -0
  70. package/FilterArea/FilterLine.d.ts +11 -0
  71. package/FilterArea/FilterLine.js +13 -0
  72. package/FilterArea/index.d.ts +6 -0
  73. package/FilterArea/index.js +3 -0
  74. package/Form/FormField.js +3 -1
  75. package/Input/Input.d.ts +35 -7
  76. package/Input/Input.js +48 -14
  77. package/Input/index.d.ts +1 -1
  78. package/Modal/MediaPreviewModal.d.ts +54 -0
  79. package/Modal/MediaPreviewModal.js +158 -0
  80. package/Modal/Modal.d.ts +103 -11
  81. package/Modal/Modal.js +14 -9
  82. package/Modal/ModalBodyForVerification.d.ts +59 -0
  83. package/Modal/ModalBodyForVerification.js +99 -0
  84. package/Modal/ModalControl.d.ts +2 -2
  85. package/Modal/ModalControl.js +1 -1
  86. package/Modal/ModalFooter.d.ts +119 -1
  87. package/Modal/ModalFooter.js +15 -3
  88. package/Modal/ModalHeader.d.ts +26 -7
  89. package/Modal/ModalHeader.js +33 -7
  90. package/Modal/index.d.ts +6 -5
  91. package/Modal/index.js +2 -2
  92. package/Modal/useModalContainer.d.ts +12 -3
  93. package/Modal/useModalContainer.js +28 -6
  94. package/Navigation/Navigation.d.ts +7 -2
  95. package/Navigation/Navigation.js +36 -35
  96. package/Navigation/NavigationHeader.d.ts +4 -0
  97. package/Navigation/NavigationHeader.js +3 -2
  98. package/Navigation/NavigationOption.d.ts +8 -3
  99. package/Navigation/NavigationOption.js +46 -11
  100. package/Navigation/NavigationOptionCategory.js +1 -0
  101. package/Navigation/NavigationOverflowMenu.d.ts +6 -0
  102. package/Navigation/NavigationOverflowMenu.js +90 -0
  103. package/Navigation/NavigationOverflowMenuOption.d.ts +7 -0
  104. package/Navigation/NavigationOverflowMenuOption.js +68 -0
  105. package/Navigation/NavigationUserMenu.d.ts +4 -2
  106. package/Navigation/NavigationUserMenu.js +13 -5
  107. package/Navigation/context.d.ts +3 -2
  108. package/Navigation/useVisibleItems.d.ts +5 -0
  109. package/Navigation/useVisibleItems.js +54 -0
  110. package/NotificationCenter/NotificationCenter.d.ts +124 -0
  111. package/NotificationCenter/NotificationCenter.js +279 -0
  112. package/NotificationCenter/NotificationCenterDrawer.d.ts +109 -0
  113. package/NotificationCenter/index.d.ts +3 -0
  114. package/NotificationCenter/index.js +1 -0
  115. package/PageFooter/PageFooter.d.ts +19 -9
  116. package/PageFooter/PageFooter.js +10 -10
  117. package/PageHeader/PageHeader.d.ts +32 -25
  118. package/PageHeader/PageHeader.js +49 -43
  119. package/ResultState/ResultState.d.ts +9 -0
  120. package/ResultState/ResultState.js +36 -4
  121. package/Scrollbar/Scrollbar.d.ts +9 -0
  122. package/Scrollbar/Scrollbar.js +78 -0
  123. package/Scrollbar/index.d.ts +2 -0
  124. package/Scrollbar/index.js +1 -0
  125. package/Scrollbar/typings.d.ts +47 -0
  126. package/Select/SelectTrigger.js +5 -4
  127. package/Select/index.d.ts +0 -2
  128. package/Select/index.js +0 -1
  129. package/Select/typings.d.ts +6 -1
  130. package/Selection/Selection.js +1 -1
  131. package/Selection/SelectionGroup.d.ts +28 -0
  132. package/Slider/useSlider.js +1 -1
  133. package/Table/Table.d.ts +2 -120
  134. package/Table/Table.js +148 -53
  135. package/Table/TableContext.d.ts +11 -12
  136. package/Table/components/TableActionsCell.js +12 -4
  137. package/Table/components/TableBody.js +2 -1
  138. package/Table/components/TableBulkActions.js +1 -19
  139. package/Table/components/TableColGroup.d.ts +1 -4
  140. package/Table/components/TableColGroup.js +15 -16
  141. package/Table/components/TableCollectableCell.d.ts +17 -0
  142. package/Table/components/TableCollectableCell.js +54 -0
  143. package/Table/components/TableDragOrPinHandleCell.d.ts +20 -0
  144. package/Table/components/TableDragOrPinHandleCell.js +58 -0
  145. package/Table/components/TableExpandedRow.js +11 -2
  146. package/Table/components/TableHeader.js +12 -10
  147. package/Table/components/TableRow.js +38 -13
  148. package/Table/components/TableSelectionCell.js +1 -1
  149. package/Table/components/TableToggleableCell.d.ts +16 -0
  150. package/Table/components/TableToggleableCell.js +51 -0
  151. package/Table/components/index.d.ts +4 -1
  152. package/Table/components/index.js +3 -0
  153. package/Table/hooks/typings.d.ts +18 -4
  154. package/Table/hooks/useTableExpansion.d.ts +2 -2
  155. package/Table/hooks/useTableExpansion.js +5 -5
  156. package/Table/hooks/useTableFixedOffsets.d.ts +6 -2
  157. package/Table/hooks/useTableFixedOffsets.js +60 -26
  158. package/Table/hooks/useTableScroll.d.ts +9 -3
  159. package/Table/hooks/useTableScroll.js +34 -7
  160. package/Table/hooks/useTableVirtualization.d.ts +2 -1
  161. package/Table/hooks/useTableVirtualization.js +2 -8
  162. package/Table/index.d.ts +4 -3
  163. package/Table/index.js +3 -0
  164. package/Table/typings.d.ts +172 -0
  165. package/Table/utils/useTableRowSelection.js +13 -5
  166. package/Tag/TagGroup.d.ts +3 -0
  167. package/Tag/index.d.ts +2 -0
  168. package/Tag/index.js +1 -0
  169. package/Transition/Slide.d.ts +9 -2
  170. package/Transition/Slide.js +7 -4
  171. package/Tree/TreeNode.js +1 -1
  172. package/Upload/UploadPictureCard.js +1 -1
  173. package/index.d.ts +37 -21
  174. package/index.js +25 -11
  175. package/package.json +6 -4
  176. package/Modal/ModalActions.d.ts +0 -9
  177. package/Modal/ModalActions.js +0 -20
  178. package/Modal/ModalBody.d.ts +0 -7
  179. package/Modal/ModalBody.js +0 -14
  180. package/Notification/Notification.d.ts +0 -54
  181. package/Notification/Notification.js +0 -76
  182. package/Notification/index.d.ts +0 -3
  183. package/Notification/index.js +0 -1
  184. package/PageToolbar/PageToolbar.d.ts +0 -114
  185. package/PageToolbar/PageToolbar.js +0 -23
  186. package/PageToolbar/index.d.ts +0 -2
  187. package/PageToolbar/index.js +0 -1
  188. package/PageToolbar/utils.d.ts +0 -23
  189. package/PageToolbar/utils.js +0 -165
  190. package/Select/AutoComplete.d.ts +0 -107
  191. package/Select/AutoComplete.js +0 -114
  192. package/Table/components/TableDragHandleCell.d.ts +0 -11
  193. package/Table/components/TableDragHandleCell.js +0 -44
@@ -0,0 +1,32 @@
1
+ import { ReactElement } from 'react';
2
+ import { FilterAlign, FilterSpan } from '@mezzanine-ui/core/filter-area';
3
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
+ import { FormFieldProps } from '../Form';
5
+ export interface FilterProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'> {
6
+ /**
7
+ * Layout control - Vertical alignment of the field.
8
+ * @default 'stretch'
9
+ */
10
+ align?: FilterAlign;
11
+ /**
12
+ * The content of the filter field.
13
+ */
14
+ children: ReactElement<FormFieldProps> | ReactElement<FormFieldProps>[];
15
+ /**
16
+ * Layout control - Whether the field should automatically expand to fill the entire row (equivalent to span={12}).
17
+ * @default false
18
+ */
19
+ grow?: boolean;
20
+ /**
21
+ * Layout control - Minimum width of the field.
22
+ */
23
+ minWidth?: string | number;
24
+ /**
25
+ * Layout control - Number of columns the field occupies in the Grid (1-12, Grid has 12 columns total).
26
+ * This property is ignored when grow is true.
27
+ * @default 2
28
+ */
29
+ span?: FilterSpan;
30
+ }
31
+ declare const Filter: import("react").ForwardRefExoticComponent<FilterProps & import("react").RefAttributes<HTMLDivElement>>;
32
+ export default Filter;
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useMemo } from 'react';
4
+ import { filterAreaClasses, filterAreaPrefix } from '@mezzanine-ui/core/filter-area';
5
+ import cx from 'clsx';
6
+
7
+ const Filter = forwardRef(function Filter(props, ref) {
8
+ const { align = 'stretch', children, className, grow = false, minWidth, span = 2, ...rest } = props;
9
+ const filterClassName = useMemo(() => cx(filterAreaClasses.filter, {
10
+ [filterAreaClasses.filterGrow]: grow,
11
+ [filterAreaClasses.filterAlign(align)]: align,
12
+ }, className), [align, className, grow]);
13
+ const style = useMemo(() => ({
14
+ ...(minWidth && { minWidth: typeof minWidth === 'number' ? `${minWidth}px` : minWidth }),
15
+ ...(!grow && {
16
+ [`--${filterAreaPrefix}-filter-span`]: span,
17
+ }),
18
+ ...rest.style,
19
+ }), [grow, minWidth, rest.style, span]);
20
+ return (jsx("div", { ...rest, ref: ref, className: filterClassName, style: style, children: children }));
21
+ });
22
+
23
+ export { Filter as default };
@@ -0,0 +1,58 @@
1
+ import { ComponentPropsWithoutRef, ReactElement } from 'react';
2
+ import { FilterAreaActionsAlign, FilterAreaSize } from '@mezzanine-ui/core/filter-area';
3
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
4
+ import { FilterLineProps } from './FilterLine';
5
+ export interface FilterAreaProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'children' | 'onSubmit' | 'onReset'> {
6
+ /**
7
+ * The alignment of the actions.
8
+ * @default 'end'
9
+ */
10
+ actionsAlign?: FilterAreaActionsAlign;
11
+ /**
12
+ * The content of the filter area, must be FilterLine component(s).
13
+ */
14
+ children: ReactElement<FilterLineProps> | ReactElement<FilterLineProps>[];
15
+ /**
16
+ * Whether the form has been modified from its initial state.
17
+ * When false, the reset button will be disabled.
18
+ * @default true
19
+ */
20
+ isDirty?: boolean;
21
+ /**
22
+ * Callback function triggered when the form is reset.
23
+ * Used to clear all filter conditions and restore to initial state.
24
+ */
25
+ onReset?: () => void;
26
+ /**
27
+ * Callback function triggered when the form is submitted.
28
+ * FilterArea itself does not manage form state; the parent component should collect
29
+ * filter values and handle submission logic. If using react-hook-form, values will be
30
+ * handled through FormProvider's handleSubmit.
31
+ */
32
+ onSubmit?: () => void;
33
+ /**
34
+ * The text of the reset button.
35
+ */
36
+ resetText?: string;
37
+ /**
38
+ * The size of the filter area.
39
+ * @default 'main'
40
+ */
41
+ size?: FilterAreaSize;
42
+ /**
43
+ * The text of the submit button.
44
+ */
45
+ submitText?: string;
46
+ /**
47
+ * The type of the reset button.
48
+ * @default 'button'
49
+ */
50
+ resetButtonType?: ComponentPropsWithoutRef<'button'>['type'];
51
+ /**
52
+ * The type of the submit button.
53
+ * @default 'button'
54
+ */
55
+ submitButtonType?: ComponentPropsWithoutRef<'button'>['type'];
56
+ }
57
+ declare const FilterArea: import("react").ForwardRefExoticComponent<FilterAreaProps & import("react").RefAttributes<HTMLDivElement>>;
58
+ export default FilterArea;
@@ -0,0 +1,31 @@
1
+ 'use client';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { forwardRef, useMemo, Children, useState, useCallback } from 'react';
4
+ import { filterAreaClasses } from '@mezzanine-ui/core/filter-area';
5
+ import { ChevronUpIcon, ChevronDownIcon } from '@mezzanine-ui/icons';
6
+ import Button from '../Button/Button.js';
7
+ import cx from 'clsx';
8
+
9
+ const FilterArea = forwardRef(function FilterArea(props, ref) {
10
+ const { actionsAlign = 'end', children, className, isDirty = true, onReset, onSubmit, resetText = 'Reset', size = 'main', submitText = 'Search', resetButtonType = 'button', submitButtonType = 'button', ...rest } = props;
11
+ const filterLines = useMemo(() => Children.toArray(children), [children]);
12
+ const hasMultipleLines = filterLines.length > 1;
13
+ const [expanded, setExpanded] = useState(false);
14
+ const handleToggleExpanded = useCallback(() => {
15
+ setExpanded((prev) => !prev);
16
+ }, []);
17
+ const handleSubmit = useCallback(() => {
18
+ if (onSubmit) {
19
+ onSubmit();
20
+ }
21
+ }, [onSubmit]);
22
+ const renderAction = () => {
23
+ return (jsxs("div", { className: cx(filterAreaClasses.actions, filterAreaClasses.actionsAlign(actionsAlign), { [filterAreaClasses.actionsExpanded]: expanded }), children: [jsx(Button, { onClick: handleSubmit, size: size, type: submitButtonType, children: submitText }), jsx(Button, { disabled: !isDirty, onClick: onReset, size: size, type: resetButtonType, variant: "base-secondary", children: resetText }), hasMultipleLines && (jsx(Button, { "aria-expanded": expanded, "aria-label": expanded ? 'Collapse filters' : 'Expand filters', icon: expanded ? ChevronUpIcon : ChevronDownIcon, iconType: "icon-only", onClick: handleToggleExpanded, size: size, title: expanded ? 'Collapse filters' : 'Expand filters', type: "button", variant: "base-ghost" }))] }));
24
+ };
25
+ const firstLine = filterLines[0];
26
+ return (jsx("div", { ...rest, ref: ref, className: cx(filterAreaClasses.host, className, {
27
+ [filterAreaClasses.size(size)]: size,
28
+ }), children: expanded ? (jsxs(Fragment, { children: [filterLines, renderAction()] })) : (jsx(Fragment, { children: firstLine && (jsxs("div", { className: filterAreaClasses.row, children: [firstLine, renderAction()] })) })) }));
29
+ });
30
+
31
+ export { FilterArea as default };
@@ -0,0 +1,11 @@
1
+ import { ReactElement } from 'react';
2
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
3
+ import { FilterProps } from './Filter';
4
+ export interface FilterLineProps extends Omit<NativeElementPropsWithoutKeyAndRef<'div'>, 'children'> {
5
+ /**
6
+ * The content of the filter line, must be Filter component(s).
7
+ */
8
+ children: ReactElement<FilterProps> | ReactElement<FilterProps>[];
9
+ }
10
+ declare const FilterLine: import("react").ForwardRefExoticComponent<FilterLineProps & import("react").RefAttributes<HTMLDivElement>>;
11
+ export default FilterLine;
@@ -0,0 +1,13 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useMemo } from 'react';
4
+ import { filterAreaClasses } from '@mezzanine-ui/core/filter-area';
5
+ import cx from 'clsx';
6
+
7
+ const FilterLine = forwardRef(function FilterLine(props, ref) {
8
+ const { className, children, ...rest } = props;
9
+ const lineClassName = useMemo(() => cx(filterAreaClasses.line, className), [className]);
10
+ return (jsx("div", { ...rest, ref: ref, className: lineClassName, children: children }));
11
+ });
12
+
13
+ export { FilterLine as default };
@@ -0,0 +1,6 @@
1
+ export { default as FilterArea } from './FilterArea';
2
+ export type { FilterAreaProps } from './FilterArea';
3
+ export { default as FilterLine } from './FilterLine';
4
+ export type { FilterLineProps } from './FilterLine';
5
+ export { default as Filter } from './Filter';
6
+ export type { FilterProps } from './Filter';
@@ -0,0 +1,3 @@
1
+ export { default as FilterArea } from './FilterArea.js';
2
+ export { default as FilterLine } from './FilterLine.js';
3
+ export { default as Filter } from './Filter.js';
package/Form/FormField.js CHANGED
@@ -20,7 +20,9 @@ const FormField = forwardRef(function FormField(props, ref) {
20
20
  return (jsx("div", { ...rest, ref: ref, className: cx(formFieldClasses.host, formFieldClasses.size(size), {
21
21
  [formFieldClasses.disabled]: disabled,
22
22
  [formFieldClasses.fullWidth]: fullWidth,
23
- }, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [jsx(FormLabel, { className: formFieldClasses.labelArea, htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker }), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [children, jsxs("div", { className: cx(formFieldClasses.hintTextAndCounterArea), children: [(hintText || hintTextIcon) && (jsx(FormHintText, { hintText: hintText, hintTextIcon: hintTextIcon, severity: severity })), counter && (jsx("span", { className: cx(formFieldClasses.counter, formFieldClasses.counterColor(counterColor || FormFieldCounterColor.INFO)), children: counter }))] })] })] }) }));
23
+ }, className), children: jsxs(FormControlContext.Provider, { value: formControl, children: [jsx(FormLabel, { className: formFieldClasses.labelArea, htmlFor: name, informationIcon: labelInformationIcon, informationText: labelInformationText, labelText: label, optionalMarker: labelOptionalMarker }), jsxs("div", { className: cx(formFieldClasses.dataEntry), children: [children, hintText || hintTextIcon || counter ? (jsxs("div", { className: cx(formFieldClasses.hintTextAndCounterArea, {
24
+ [formFieldClasses.hintTextAndCounterArea + '--align-right']: !(hintText || hintTextIcon) && counter,
25
+ }), children: [(hintText || hintTextIcon) && (jsx(FormHintText, { hintText: hintText, hintTextIcon: hintTextIcon, severity: severity })), counter && (jsx("span", { className: cx(formFieldClasses.counter, formFieldClasses.counterColor(counterColor || FormFieldCounterColor.INFO)), children: counter }))] })) : null] })] }) }));
24
26
  });
25
27
 
26
28
  export { FormField as default };
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
  */
@@ -15,6 +17,7 @@ export interface InputBaseProps extends Omit<TextFieldProps, 'children' | 'clear
15
17
  /**
16
18
  * Formatter function to transform the value for display.
17
19
  * Common use cases: currency formatting (1000 → "1,000"), phone numbers, etc.
20
+ * Default to formatting number with commas for "currency" variant.
18
21
  * @example
19
22
  * formatter={(value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
20
23
  */
@@ -47,6 +50,7 @@ export interface InputBaseProps extends Omit<TextFieldProps, 'children' | 'clear
47
50
  /**
48
51
  * Parser function to extract the raw value from formatted display value.
49
52
  * Should reverse the formatter transformation.
53
+ * Default to removing commas for "currency" formatting.
50
54
  * @example
51
55
  * parser={(value) => value.replace(/,/g, '')}
52
56
  */
@@ -111,10 +115,10 @@ export type NumberInputProps = InputBaseProps & NumberInput & {
111
115
  variant: 'number';
112
116
  };
113
117
  /**
114
- * 5. Unit Input - Input with unit text and spinner buttons
118
+ * 5. Currency Input - Input with unit text and spinner buttons
115
119
  */
116
- export type UnitInputProps = InputBaseProps & NumberInput & TextFieldAffixProps & {
117
- variant: 'unit';
120
+ export type CurrencyInputProps = InputBaseProps & NumberInput & TextFieldAffixProps & {
121
+ variant: 'currency';
118
122
  /**
119
123
  * Whether to show spinner buttons.
120
124
  * @default false
@@ -152,6 +156,30 @@ export type SelectInputProps = InputBaseProps & {
152
156
  selectButton: SelectButtonProps & {
153
157
  position: 'prefix' | 'suffix' | 'both';
154
158
  };
159
+ /**
160
+ * The options of the dropdown.
161
+ */
162
+ options?: DropdownOption[];
163
+ /**
164
+ * The selected value of the dropdown.
165
+ */
166
+ selectedValue?: string;
167
+ /**
168
+ * The onChange event handler of the dropdown.
169
+ */
170
+ onSelect?: (value: string) => void;
171
+ /**
172
+ * The width of the dropdown.
173
+ */
174
+ dropdownWidth?: number | string;
175
+ /**
176
+ * The max height of the dropdown.
177
+ */
178
+ dropdownMaxHeight?: number | string;
179
+ /**
180
+ * The placement of the dropdown.
181
+ */
182
+ dropdownPlacement?: PopperPlacement;
155
183
  };
156
184
  /**
157
185
  * 8. Password Input - Password input with visibility toggle
@@ -175,7 +203,7 @@ export type WithPasswordStrengthIndicator = {
175
203
  export type PasswordInputProps = InputBaseProps & ClearableInput & WithPasswordStrengthIndicator & {
176
204
  variant: 'password';
177
205
  };
178
- export type InputProps = BaseInputProps | WithAffixInputProps | SearchInputProps | NumberInputProps | UnitInputProps | ActionInputProps | SelectInputProps | PasswordInputProps;
206
+ export type InputProps = BaseInputProps | WithAffixInputProps | SearchInputProps | NumberInputProps | CurrencyInputProps | ActionInputProps | SelectInputProps | PasswordInputProps;
179
207
  /**
180
208
  * The react component for `mezzanine` input.
181
209
  */
package/Input/Input.js CHANGED
@@ -1,11 +1,14 @@
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, useMemo, useCallback } from 'react';
6
+ import { useInputWithClearControlValue } from '../Form/useInputWithClearControlValue.js';
7
+ import { useComposeRefs } from '../hooks/useComposeRefs.js';
8
+ import { formatNumberWithCommas } from '../utils/format-number-with-commas.js';
9
+ import { parseNumberWithCommas } from '../utils/parse-number-with-commas.js';
8
10
  import Icon from '../Icon/Icon.js';
11
+ import Dropdown from '../Dropdown/Dropdown.js';
9
12
  import SelectButton from './SelectButton/SelectButton.js';
10
13
  import ActionButton from './ActionButton/ActionButton.js';
11
14
  import SpinnerButton from './SpinnerButton/SpinnerButton.js';
@@ -17,7 +20,7 @@ import cx from 'clsx';
17
20
  * The react component for `mezzanine` input.
18
21
  */
19
22
  const Input = forwardRef(function Input(props, ref) {
20
- const { active, className, defaultValue, disabled = false, error = false, formatter, fullWidth = true, id, inputProps, inputType, inputRef: inputRefProp, name, onChange: onChangeProp, parser, placeholder, readonly, size = 'main', typing, variant = 'base', value: valueProp, } = props;
23
+ const { active, className, defaultValue, disabled = false, error = false, formatter: formatterProp, fullWidth = true, id, inputProps, inputType, inputRef: inputRefProp, name, onChange: onChangeProp, parser: parserProp, placeholder, readonly, size = 'main', typing, variant = 'base', value: valueProp, } = props;
21
24
  const inputRef = useRef(null);
22
25
  const [showPassword, setShowPassword] = useState(false);
23
26
  const [value, onChange, onClearFromHook] = useInputWithClearControlValue({
@@ -27,6 +30,22 @@ const Input = forwardRef(function Input(props, ref) {
27
30
  value: valueProp,
28
31
  });
29
32
  // Handle formatter/parser logic
33
+ const formatter = useMemo(() => {
34
+ if (formatterProp)
35
+ return formatterProp;
36
+ if (variant === 'currency') {
37
+ return (value) => formatNumberWithCommas(value);
38
+ }
39
+ return undefined;
40
+ }, [formatterProp, variant]);
41
+ const parser = useMemo(() => {
42
+ if (parserProp)
43
+ return parserProp;
44
+ if (variant === 'currency') {
45
+ return (value) => { var _a, _b; return (_b = (_a = parseNumberWithCommas(value)) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : ''; };
46
+ }
47
+ return undefined;
48
+ }, [parserProp, variant]);
30
49
  const handleChange = useCallback((event) => {
31
50
  let newValue = event.target.value;
32
51
  // Parse the formatted value back to raw value if parser is provided
@@ -105,20 +124,20 @@ const Input = forwardRef(function Input(props, ref) {
105
124
  };
106
125
  break;
107
126
  }
108
- case 'unit': {
109
- const unitProps = props;
110
- const { step = 1, max, min, onSpinUp, onSpinDown } = unitProps;
127
+ case 'currency': {
128
+ const currencyProps = props;
129
+ const { step = 1, max, min, onSpinUp, onSpinDown } = currencyProps;
111
130
  // 預設置右對齊
112
131
  inputStyle = { textAlign: 'right' };
113
132
  // 允許填入 prefix/suffix
114
- prefix = unitProps.prefix;
115
- suffix = unitProps.suffix;
133
+ prefix = currencyProps.prefix;
134
+ suffix = currencyProps.suffix;
116
135
  defaultInputProps = {
117
136
  min: min,
118
137
  max: max,
119
138
  step: step,
120
139
  };
121
- if (unitProps.showSpinner) {
140
+ if (currencyProps.showSpinner) {
122
141
  const handleSpinUp = () => {
123
142
  const currentValue = parseFloat(value || '0');
124
143
  const newValue = currentValue + step;
@@ -139,7 +158,7 @@ const Input = forwardRef(function Input(props, ref) {
139
158
  }
140
159
  onSpinDown === null || onSpinDown === void 0 ? void 0 : onSpinDown();
141
160
  };
142
- suffix = (jsxs(Fragment, { children: [unitProps.suffix, jsxs("div", { className: inputClasses.spinners, children: [jsx(SpinnerButton, { type: "up", size: size, disabled: disabled, onClick: handleSpinUp }), jsx(SpinnerButton, { type: "down", size: size, disabled: disabled, onClick: handleSpinDown })] })] }));
161
+ suffix = (jsxs(Fragment, { children: [currencyProps.suffix, jsxs("div", { className: inputClasses.spinners, children: [jsx(SpinnerButton, { type: "up", size: size, disabled: disabled, onClick: handleSpinUp }), jsx(SpinnerButton, { type: "down", size: size, disabled: disabled, onClick: handleSpinDown })] })] }));
143
162
  }
144
163
  break;
145
164
  }
@@ -158,16 +177,31 @@ const Input = forwardRef(function Input(props, ref) {
158
177
  }
159
178
  case 'select': {
160
179
  const selectProps = props;
161
- const { selectButton } = selectProps;
180
+ const { selectButton, options, dropdownWidth = 120, dropdownMaxHeight = 114, } = selectProps;
181
+ const defaultOptions = options || [];
182
+ const selectedOptions = defaultOptions.length > 0
183
+ ? defaultOptions.map((option) => ({
184
+ ...option,
185
+ ...(option.id === selectProps.selectedValue
186
+ ? { checkSite: 'suffix' }
187
+ : {}),
188
+ }))
189
+ : [];
162
190
  if (selectButton.position === 'both' ||
163
191
  selectButton.position === 'prefix') {
164
192
  const { ...restSelectButtonProps } = selectButton;
165
- prefixExternalButton = (jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }));
193
+ prefixExternalButton = (jsx(Dropdown, { options: selectedOptions, value: selectProps.selectedValue, customWidth: dropdownWidth, maxHeight: dropdownMaxHeight, placement: "bottom-start", onSelect: (option) => {
194
+ var _a;
195
+ (_a = selectProps.onSelect) === null || _a === void 0 ? void 0 : _a.call(selectProps, option.id);
196
+ }, children: jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }) }));
166
197
  }
167
198
  if (selectButton.position === 'both' ||
168
199
  selectButton.position === 'suffix') {
169
200
  const { ...restSelectButtonProps } = selectButton;
170
- suffixExternalButton = (jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }));
201
+ suffixExternalButton = (jsx(Dropdown, { options: selectedOptions, value: selectProps.selectedValue, customWidth: dropdownWidth, maxHeight: dropdownMaxHeight, placement: "bottom-start", onSelect: (option) => {
202
+ var _a;
203
+ (_a = selectProps.onSelect) === null || _a === void 0 ? void 0 : _a.call(selectProps, option.id);
204
+ }, children: jsx(SelectButton, { ...restSelectButtonProps, disabled: restSelectButtonProps.disabled || disabled, size: size }) }));
171
205
  }
172
206
  break;
173
207
  }
package/Input/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export type { InputSize, InputStrength } from '@mezzanine-ui/core/input';
2
- export type { InputBaseProps, ClearableInput, NumberInput, BaseInputProps, WithAffixInputProps, SearchInputProps, NumberInputProps, UnitInputProps, ActionInputProps, SelectInputProps, WithPasswordStrengthIndicator, PasswordInputProps, InputProps, } from './Input';
2
+ export type { InputBaseProps, ClearableInput, NumberInput, BaseInputProps, WithAffixInputProps, SearchInputProps, NumberInputProps, CurrencyInputProps, ActionInputProps, SelectInputProps, WithPasswordStrengthIndicator, PasswordInputProps, InputProps, } from './Input';
3
3
  export { default } from './Input';
@@ -0,0 +1,54 @@
1
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
2
+ import { ModalContainerProps } from './useModalContainer';
3
+ export interface MediaPreviewModalProps extends Omit<ModalContainerProps, 'children'>, NativeElementPropsWithoutKeyAndRef<'div'> {
4
+ /**
5
+ * The current index of the media being displayed (controlled mode).
6
+ * If provided along with onNext/onPrev, the component operates in controlled mode.
7
+ */
8
+ currentIndex?: number;
9
+ /**
10
+ * The default index when the modal opens (uncontrolled mode).
11
+ * @default 0
12
+ */
13
+ defaultIndex?: number;
14
+ /**
15
+ * Whether to disable the next navigation button.
16
+ * @default false
17
+ */
18
+ disableNext?: boolean;
19
+ /**
20
+ * Whether to disable the previous navigation button.
21
+ * @default false
22
+ */
23
+ disablePrev?: boolean;
24
+ /**
25
+ * Array of media items to display.
26
+ * Each item should be a valid image URL or React node.
27
+ */
28
+ mediaItems: (string | React.ReactNode)[];
29
+ /**
30
+ * Callback fired when the index changes (uncontrolled mode).
31
+ */
32
+ onIndexChange?: (index: number) => void;
33
+ /**
34
+ * Callback fired when the next navigation button is clicked (controlled mode).
35
+ * If provided, the component operates in controlled mode.
36
+ */
37
+ onNext?: () => void;
38
+ /**
39
+ * Callback fired when the previous navigation button is clicked (controlled mode).
40
+ * If provided, the component operates in controlled mode.
41
+ */
42
+ onPrev?: () => void;
43
+ /**
44
+ * Whether to show the pagination indicator.
45
+ * @default true
46
+ */
47
+ showPaginationIndicator?: boolean;
48
+ }
49
+ /**
50
+ * The react component for `mezzanine` media preview modal.
51
+ * Displays media items with navigation controls and a close button.
52
+ */
53
+ declare const MediaPreviewModal: import("react").ForwardRefExoticComponent<MediaPreviewModalProps & import("react").RefAttributes<HTMLDivElement>>;
54
+ export default MediaPreviewModal;
@@ -0,0 +1,158 @@
1
+ 'use client';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { forwardRef, useState, useEffect, useRef, useMemo } from 'react';
4
+ import { ChevronLeftIcon, ChevronRightIcon } from '@mezzanine-ui/icons';
5
+ import { modalClasses } from '@mezzanine-ui/core/modal';
6
+ import { MOTION_DURATION, MOTION_EASING } from '@mezzanine-ui/system/motion';
7
+ import useModalContainer from './useModalContainer.js';
8
+ import ClearActions from '../ClearActions/ClearActions.js';
9
+ import Icon from '../Icon/Icon.js';
10
+ import Fade from '../Transition/Fade.js';
11
+ import cx from 'clsx';
12
+
13
+ /**
14
+ * The react component for `mezzanine` media preview modal.
15
+ * Displays media items with navigation controls and a close button.
16
+ */
17
+ const MediaPreviewModal = forwardRef(function MediaPreviewModal(props, ref) {
18
+ const { className, container, currentIndex: controlledIndex, defaultIndex = 0, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disableNext = false, disablePortal = false, disablePrev = false, mediaItems, onBackdropClick, onClose, onIndexChange, onNext, onPrev, open, showPaginationIndicator = true, ...rest } = props;
19
+ const { Container: ModalContainer } = useModalContainer();
20
+ // Determine if component is in controlled mode
21
+ const isControlled = controlledIndex !== undefined;
22
+ // Internal state for uncontrolled mode
23
+ const [internalIndex, setInternalIndex] = useState(defaultIndex);
24
+ // Use controlled index if provided, otherwise use internal state
25
+ const currentIndex = isControlled ? controlledIndex : internalIndex;
26
+ // Reset internal index when modal opens in uncontrolled mode
27
+ useEffect(() => {
28
+ if (open && !isControlled) {
29
+ setInternalIndex(defaultIndex);
30
+ }
31
+ }, [open, isControlled, defaultIndex]);
32
+ // Built-in navigation handlers for uncontrolled mode
33
+ const handleNext = () => {
34
+ if (onNext) {
35
+ // Controlled mode: use provided callback
36
+ onNext();
37
+ }
38
+ else {
39
+ // Uncontrolled mode: update internal state
40
+ const nextIndex = Math.min(currentIndex + 1, mediaItems.length - 1);
41
+ setInternalIndex(nextIndex);
42
+ onIndexChange === null || onIndexChange === void 0 ? void 0 : onIndexChange(nextIndex);
43
+ }
44
+ };
45
+ const handlePrev = () => {
46
+ if (onPrev) {
47
+ // Controlled mode: use provided callback
48
+ onPrev();
49
+ }
50
+ else {
51
+ // Uncontrolled mode: update internal state
52
+ const prevIndex = Math.max(currentIndex - 1, 0);
53
+ setInternalIndex(prevIndex);
54
+ onIndexChange === null || onIndexChange === void 0 ? void 0 : onIndexChange(prevIndex);
55
+ }
56
+ };
57
+ // Auto-calculate disable states for uncontrolled mode
58
+ const isNextDisabled = disableNext || currentIndex >= mediaItems.length - 1;
59
+ const isPrevDisabled = disablePrev || currentIndex <= 0;
60
+ const [displayedIndices, setDisplayedIndices] = useState([
61
+ currentIndex,
62
+ ]);
63
+ const [activeIndex, setActiveIndex] = useState(currentIndex);
64
+ const preloadedUrls = useRef(new Set());
65
+ // Helper function to preload a single image
66
+ const preloadImage = (url) => {
67
+ if (preloadedUrls.current.has(url))
68
+ return;
69
+ const img = new Image();
70
+ img.src = url;
71
+ preloadedUrls.current.add(url);
72
+ };
73
+ // Preload images: prioritize current and adjacent, then load others
74
+ useEffect(() => {
75
+ if (!open)
76
+ return;
77
+ // Priority 1: Current and adjacent images
78
+ const priorityIndices = [
79
+ currentIndex - 1,
80
+ currentIndex,
81
+ currentIndex + 1,
82
+ ].filter((idx) => idx >= 0 && idx < mediaItems.length);
83
+ priorityIndices.forEach((idx) => {
84
+ const media = mediaItems[idx];
85
+ if (typeof media === 'string') {
86
+ preloadImage(media);
87
+ }
88
+ });
89
+ // Priority 2: All other images (load after a short delay)
90
+ const loadRemainingTimer = setTimeout(() => {
91
+ mediaItems.forEach((media, idx) => {
92
+ if (typeof media === 'string' && !priorityIndices.includes(idx)) {
93
+ preloadImage(media);
94
+ }
95
+ });
96
+ }, 500);
97
+ return () => clearTimeout(loadRemainingTimer);
98
+ }, [open, currentIndex, mediaItems]);
99
+ useEffect(() => {
100
+ if (currentIndex !== activeIndex) {
101
+ // First, add new index to displayedIndices (will render with in=false)
102
+ setDisplayedIndices((prev) => prev.includes(currentIndex) ? prev : [...prev, currentIndex]);
103
+ // Use requestAnimationFrame to ensure DOM is updated before triggering animation
104
+ let rafId1 = null;
105
+ let rafId2 = null;
106
+ rafId1 = requestAnimationFrame(() => {
107
+ rafId2 = requestAnimationFrame(() => {
108
+ setActiveIndex(currentIndex);
109
+ });
110
+ });
111
+ // Clean up old images after transition completes
112
+ const cleanupTimer = setTimeout(() => {
113
+ setDisplayedIndices([currentIndex]);
114
+ }, MOTION_DURATION.fast + 100);
115
+ return () => {
116
+ clearTimeout(cleanupTimer);
117
+ if (rafId1 !== null) {
118
+ cancelAnimationFrame(rafId1);
119
+ }
120
+ if (rafId2 !== null) {
121
+ cancelAnimationFrame(rafId2);
122
+ }
123
+ };
124
+ }
125
+ return undefined;
126
+ }, [currentIndex, activeIndex]);
127
+ // Memoize media elements to prevent unnecessary re-renders
128
+ const mediaElements = useMemo(() => {
129
+ return mediaItems.map((media, index) => {
130
+ if (typeof media === 'string') {
131
+ return {
132
+ index,
133
+ element: (jsx("img", { alt: `Media ${index + 1}`, className: modalClasses.mediaPreviewImage, src: media })),
134
+ };
135
+ }
136
+ return {
137
+ index,
138
+ element: jsx("div", { className: modalClasses.mediaPreviewImage, children: media }),
139
+ };
140
+ });
141
+ }, [mediaItems]);
142
+ const renderMedia = (index) => {
143
+ const mediaElement = mediaElements.find((item) => item.index === index);
144
+ if (!mediaElement)
145
+ return null;
146
+ const isCurrent = index === activeIndex;
147
+ return (jsx(Fade, { duration: {
148
+ enter: MOTION_DURATION.fast,
149
+ exit: MOTION_DURATION.fast,
150
+ }, easing: {
151
+ enter: MOTION_EASING.entrance,
152
+ exit: MOTION_EASING.exit,
153
+ }, in: isCurrent, children: mediaElement.element }, index));
154
+ };
155
+ return (jsxs(ModalContainer, { className: modalClasses.overlay, container: container, disableCloseOnBackdropClick: disableCloseOnBackdropClick, disableCloseOnEscapeKeyDown: disableCloseOnEscapeKeyDown, disablePortal: disablePortal, onBackdropClick: onBackdropClick, onClose: onClose, open: open, ref: ref, children: [jsx("div", { ...rest, className: cx(modalClasses.host, modalClasses.mediaPreview, className), role: "dialog", children: jsx("div", { className: modalClasses.mediaPreviewContent, children: jsx("div", { className: modalClasses.mediaPreviewMediaContainer, children: displayedIndices.map((index) => renderMedia(index)) }) }) }), jsx(ClearActions, { className: modalClasses.mediaPreviewCloseButton, onClick: onClose, type: "embedded", variant: "contrast" }), mediaItems.length > 1 && (jsx("button", { "aria-disabled": isPrevDisabled, "aria-label": "Previous media", className: cx(modalClasses.mediaPreviewNavButton, modalClasses.mediaPreviewNavButtonPrev), disabled: isPrevDisabled, onClick: handlePrev, title: "Previous", type: "button", children: jsx(Icon, { icon: ChevronLeftIcon, size: 16, color: "fixed-light" }) })), mediaItems.length > 1 && (jsx("button", { "aria-disabled": isNextDisabled, "aria-label": "Next media", className: cx(modalClasses.mediaPreviewNavButton, modalClasses.mediaPreviewNavButtonNext), disabled: isNextDisabled, onClick: handleNext, title: "Next", type: "button", children: jsx(Icon, { icon: ChevronRightIcon, size: 16, color: "fixed-light" }) })), showPaginationIndicator && mediaItems.length > 1 && (jsxs("div", { "aria-label": `Page ${currentIndex + 1} of ${mediaItems.length}`, className: modalClasses.mediaPreviewPaginationIndicator, children: [currentIndex + 1, "/", mediaItems.length] }))] }));
156
+ });
157
+
158
+ export { MediaPreviewModal as default };