@mezzanine-ui/react 1.0.0-beta.3 → 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 (121) hide show
  1. package/AutoComplete/AutoComplete.d.ts +23 -0
  2. package/AutoComplete/AutoComplete.js +23 -9
  3. package/Breadcrumb/Breadcrumb.js +16 -21
  4. package/Breadcrumb/BreadcrumbDropdown.d.ts +11 -0
  5. package/Breadcrumb/BreadcrumbDropdown.js +22 -0
  6. package/Breadcrumb/BreadcrumbItem.d.ts +2 -3
  7. package/Breadcrumb/BreadcrumbItem.js +13 -31
  8. package/Breadcrumb/BreadcrumbOverflowMenu.d.ts +7 -0
  9. package/Breadcrumb/BreadcrumbOverflowMenu.js +77 -0
  10. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +11 -0
  11. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.js +21 -0
  12. package/Breadcrumb/BreadcrumbOverflowMenuItem.d.ts +3 -0
  13. package/Breadcrumb/BreadcrumbOverflowMenuItem.js +27 -0
  14. package/Breadcrumb/typings.d.ts +21 -39
  15. package/Checkbox/index.d.ts +4 -5
  16. package/Checkbox/index.js +1 -5
  17. package/ContentHeader/ContentHeader.d.ts +160 -0
  18. package/ContentHeader/ContentHeader.js +54 -0
  19. package/ContentHeader/index.d.ts +2 -0
  20. package/ContentHeader/index.js +1 -0
  21. package/ContentHeader/utils.d.ts +23 -0
  22. package/ContentHeader/utils.js +215 -0
  23. package/Dropdown/Dropdown.d.ts +17 -0
  24. package/Dropdown/Dropdown.js +6 -2
  25. package/Dropdown/DropdownItem.d.ts +10 -0
  26. package/Dropdown/DropdownItem.js +37 -8
  27. package/Dropdown/DropdownItemCard.d.ts +2 -2
  28. package/Dropdown/DropdownItemCard.js +11 -8
  29. package/Empty/Empty.js +2 -1
  30. package/Empty/icons/EmptyMainNotificationIcon.d.ts +4 -0
  31. package/Empty/icons/EmptyMainNotificationIcon.js +9 -0
  32. package/Empty/typings.d.ts +2 -2
  33. package/FilterArea/Filter.d.ts +32 -0
  34. package/FilterArea/Filter.js +23 -0
  35. package/FilterArea/FilterArea.d.ts +58 -0
  36. package/FilterArea/FilterArea.js +31 -0
  37. package/FilterArea/FilterLine.d.ts +11 -0
  38. package/FilterArea/FilterLine.js +13 -0
  39. package/FilterArea/index.d.ts +6 -0
  40. package/FilterArea/index.js +3 -0
  41. package/Input/Input.d.ts +6 -4
  42. package/Input/Input.js +28 -10
  43. package/Input/index.d.ts +1 -1
  44. package/Modal/MediaPreviewModal.d.ts +54 -0
  45. package/Modal/MediaPreviewModal.js +158 -0
  46. package/Modal/Modal.js +1 -1
  47. package/Modal/index.d.ts +2 -0
  48. package/Modal/index.js +1 -0
  49. package/Navigation/Navigation.js +6 -5
  50. package/Navigation/NavigationOption.d.ts +6 -2
  51. package/Navigation/NavigationOption.js +19 -9
  52. package/Navigation/NavigationOverflowMenu.d.ts +6 -0
  53. package/Navigation/NavigationOverflowMenu.js +90 -0
  54. package/Navigation/NavigationOverflowMenuOption.d.ts +7 -0
  55. package/Navigation/NavigationOverflowMenuOption.js +68 -0
  56. package/Navigation/NavigationUserMenu.d.ts +4 -2
  57. package/Navigation/NavigationUserMenu.js +13 -5
  58. package/Navigation/context.d.ts +3 -2
  59. package/NotificationCenter/NotificationCenter.d.ts +1 -1
  60. package/NotificationCenter/NotificationCenter.js +34 -14
  61. package/NotificationCenter/NotificationCenterDrawer.d.ts +20 -0
  62. package/PageHeader/PageHeader.d.ts +32 -25
  63. package/PageHeader/PageHeader.js +49 -35
  64. package/ResultState/ResultState.d.ts +9 -0
  65. package/ResultState/ResultState.js +36 -4
  66. package/Scrollbar/Scrollbar.d.ts +9 -0
  67. package/Scrollbar/Scrollbar.js +78 -0
  68. package/Scrollbar/index.d.ts +2 -0
  69. package/Scrollbar/index.js +1 -0
  70. package/Scrollbar/typings.d.ts +47 -0
  71. package/Select/SelectTrigger.js +5 -4
  72. package/Select/typings.d.ts +6 -1
  73. package/Selection/Selection.js +1 -1
  74. package/Selection/SelectionGroup.d.ts +28 -0
  75. package/Table/Table.d.ts +2 -120
  76. package/Table/Table.js +148 -53
  77. package/Table/TableContext.d.ts +11 -12
  78. package/Table/components/TableActionsCell.js +12 -4
  79. package/Table/components/TableBody.js +2 -1
  80. package/Table/components/TableColGroup.d.ts +1 -4
  81. package/Table/components/TableColGroup.js +15 -16
  82. package/Table/components/TableCollectableCell.d.ts +17 -0
  83. package/Table/components/TableCollectableCell.js +54 -0
  84. package/Table/components/TableDragOrPinHandleCell.d.ts +20 -0
  85. package/Table/components/TableDragOrPinHandleCell.js +58 -0
  86. package/Table/components/TableExpandedRow.js +11 -2
  87. package/Table/components/TableHeader.js +12 -10
  88. package/Table/components/TableRow.js +38 -13
  89. package/Table/components/TableSelectionCell.js +1 -1
  90. package/Table/components/TableToggleableCell.d.ts +16 -0
  91. package/Table/components/TableToggleableCell.js +51 -0
  92. package/Table/components/index.d.ts +4 -1
  93. package/Table/components/index.js +3 -0
  94. package/Table/hooks/typings.d.ts +18 -4
  95. package/Table/hooks/useTableExpansion.d.ts +2 -2
  96. package/Table/hooks/useTableExpansion.js +5 -5
  97. package/Table/hooks/useTableFixedOffsets.d.ts +6 -2
  98. package/Table/hooks/useTableFixedOffsets.js +58 -24
  99. package/Table/hooks/useTableScroll.d.ts +9 -3
  100. package/Table/hooks/useTableScroll.js +34 -7
  101. package/Table/hooks/useTableVirtualization.d.ts +2 -1
  102. package/Table/hooks/useTableVirtualization.js +2 -8
  103. package/Table/index.d.ts +4 -3
  104. package/Table/index.js +3 -0
  105. package/Table/typings.d.ts +172 -0
  106. package/Transition/Slide.d.ts +9 -2
  107. package/Transition/Slide.js +7 -4
  108. package/Tree/TreeNode.js +1 -1
  109. package/index.d.ts +4 -2
  110. package/index.js +6 -3
  111. package/package.json +6 -4
  112. package/Navigation/CollapsedMenu.d.ts +0 -6
  113. package/Navigation/CollapsedMenu.js +0 -16
  114. package/PageToolbar/PageToolbar.d.ts +0 -110
  115. package/PageToolbar/PageToolbar.js +0 -23
  116. package/PageToolbar/index.d.ts +0 -2
  117. package/PageToolbar/index.js +0 -1
  118. package/PageToolbar/utils.d.ts +0 -23
  119. package/PageToolbar/utils.js +0 -157
  120. package/Table/components/TableDragHandleCell.d.ts +0 -11
  121. package/Table/components/TableDragHandleCell.js +0 -44
@@ -4,10 +4,10 @@ import cx from 'clsx';
4
4
  import { useMemo, useState } from 'react';
5
5
  import { dropdownClasses } from '@mezzanine-ui/core/dropdown/dropdown';
6
6
  import { CheckedIcon } from '@mezzanine-ui/icons';
7
- import Checkbox from '../Checkbox/Checkbox.js';
8
7
  import Typography from '../Typography/Typography.js';
9
8
  import { highlightText } from './highlightText.js';
10
9
  import Icon from '../Icon/Icon.js';
10
+ import Checkbox from '../Checkbox/Checkbox.js';
11
11
 
12
12
  function DropdownItemCard(props) {
13
13
  const { active = false, appendIcon, appendContent, followText, id, label, level: levelProp, mode, name: _name, prependIcon, subTitle, validate, disabled, checked, defaultChecked, checkSite, onCheckedChange, onClick, className, onMouseEnter, showUnderline, } = props;
@@ -51,8 +51,8 @@ function DropdownItemCard(props) {
51
51
  : [
52
52
  {
53
53
  text: cardLabel,
54
- highlight: false
55
- }
54
+ highlight: false,
55
+ },
56
56
  ];
57
57
  }, [cardLabel, followText]);
58
58
  const showPrependContent = useMemo(() => {
@@ -68,13 +68,15 @@ function DropdownItemCard(props) {
68
68
  ? [
69
69
  {
70
70
  text: subTitle,
71
- highlight: false
72
- }
71
+ highlight: false,
72
+ },
73
73
  ]
74
74
  : [];
75
75
  }, [subTitle, followText]);
76
76
  const renderHighlightedText = (parts, className, id) => {
77
- return (jsx(Typography, { className: className, id: id, children: parts.map((part, index) => (jsx("span", { className: part.highlight && validate !== 'danger' ? dropdownClasses.cardHighlightedText : '', children: part.text }, index))) }));
77
+ return (jsx(Typography, { className: className, id: id, children: parts.map((part, index) => (jsx("span", { className: part.highlight && validate !== 'danger'
78
+ ? dropdownClasses.cardHighlightedText
79
+ : '', children: part.text }, index))) }));
78
80
  };
79
81
  const toggleChecked = () => {
80
82
  if (disabled)
@@ -109,8 +111,9 @@ function DropdownItemCard(props) {
109
111
  [dropdownClasses.cardActive]: active || isChecked,
110
112
  [dropdownClasses.cardDisabled]: disabled,
111
113
  [dropdownClasses.cardDanger]: validate === 'danger',
112
- }, className), id: id, role: "option", tabIndex: -1, onMouseEnter: onMouseEnter, onClick: handleClick, onKeyDown: handleKeyDown, children: jsxs("div", { className: dropdownClasses.cardContainer, children: [showPrependContent && (jsxs("div", { className: dropdownClasses.cardPrependContent, children: [prependIcon
113
- && jsx(Icon, { icon: prependIcon, color: iconColor }), checkSite === 'prefix' && mode === 'multiple' && (jsx(Checkbox, { checked: isChecked, disabled: disabled, onChange: handleCheckboxChange }))] })), jsxs("div", { className: dropdownClasses.cardBody, children: [cardLabel && renderHighlightedText(labelParts, dropdownClasses.cardTitle, labelId), subTitleParts.length > 0 && renderHighlightedText(subTitleParts, dropdownClasses.cardDescription)] }), showAppendContent && (jsxs("div", { className: dropdownClasses.cardAppendContent, children: [appendContent && jsx(Typography, { color: "text-neutral-light", children: appendContent }), appendIcon && jsx(Icon, { icon: appendIcon, color: iconColor }), checkSite === 'suffix' && isChecked && jsx(Icon, { icon: CheckedIcon, color: appendIconColor, size: 16 })] }))] }) }), showUnderline && jsx("div", { className: dropdownClasses.cardUnderline })] }));
114
+ }, className), id: id, role: "option", tabIndex: -1, onMouseEnter: onMouseEnter, onClick: handleClick, onKeyDown: handleKeyDown, children: jsxs("div", { className: dropdownClasses.cardContainer, children: [showPrependContent && (jsxs("div", { className: dropdownClasses.cardPrependContent, children: [prependIcon && jsx(Icon, { icon: prependIcon, color: iconColor }), checkSite === 'prefix' && mode === 'multiple' && (jsx(Checkbox, { checked: isChecked, disabled: disabled, onChange: handleCheckboxChange }))] })), jsxs("div", { className: dropdownClasses.cardBody, children: [cardLabel &&
115
+ renderHighlightedText(labelParts, dropdownClasses.cardTitle, labelId), subTitleParts.length > 0 &&
116
+ renderHighlightedText(subTitleParts, dropdownClasses.cardDescription)] }), showAppendContent && (jsxs("div", { className: dropdownClasses.cardAppendContent, children: [appendContent && (jsx(Typography, { color: "text-neutral-light", children: appendContent })), appendIcon && jsx(Icon, { icon: appendIcon, color: iconColor }), checkSite === 'suffix' && isChecked && (jsx(Icon, { icon: CheckedIcon, color: appendIconColor, size: 16 }))] }))] }) }), showUnderline && jsx("div", { className: dropdownClasses.cardUnderline })] }));
114
117
  }
115
118
 
116
119
  export { DropdownItemCard as default };
package/Empty/Empty.js CHANGED
@@ -8,6 +8,7 @@ import { EmptyMainInitialDataIcon } from './icons/EmptyMainInitialDataIcon.js';
8
8
  import { EmptyMainResultIcon } from './icons/EmptyMainResultIcon.js';
9
9
  import { EmptyMainSystemIcon } from './icons/EmptyMainSystemIcon.js';
10
10
  import { flattenChildren } from '../utils/flatten-children.js';
11
+ import { EmptyMainNotificationIcon } from './icons/EmptyMainNotificationIcon.js';
11
12
  import Icon from '../Icon/Icon.js';
12
13
  import cx from 'clsx';
13
14
 
@@ -21,7 +22,7 @@ const iconMap = {
21
22
  const mainIconMap = {
22
23
  custom: null,
23
24
  'initial-data': jsx(EmptyMainInitialDataIcon, { className: emptyClasses.icon }),
24
- notification: null,
25
+ notification: jsx(EmptyMainNotificationIcon, { className: emptyClasses.icon }),
25
26
  result: jsx(EmptyMainResultIcon, { className: emptyClasses.icon }),
26
27
  system: jsx(EmptyMainSystemIcon, { className: emptyClasses.icon }),
27
28
  };
@@ -0,0 +1,4 @@
1
+ export interface EmptyMainNotificationIconProps extends React.SVGProps<SVGSVGElement> {
2
+ size?: number;
3
+ }
4
+ export declare const EmptyMainNotificationIcon: import("react").ForwardRefExoticComponent<Omit<EmptyMainNotificationIconProps, "ref"> & import("react").RefAttributes<SVGSVGElement>>;
@@ -0,0 +1,9 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { forwardRef } from 'react';
3
+
4
+ const EmptyMainNotificationIcon = forwardRef(function EmptyMainNotificationIcon(props, ref) {
5
+ const { className, size = 64, ...rest } = props;
6
+ return (jsxs("svg", { ...rest, className: className, fill: "none", height: size, ref: ref, viewBox: "0 0 44 47", width: size, xmlns: "http://www.w3.org/2000/svg", children: [jsxs("defs", { children: [jsxs("linearGradient", { gradientUnits: "userSpaceOnUse", id: "paint0_linear_8482_10235", x1: "22", x2: "20.7012", y1: "33", y2: "46.8785", children: [jsx("stop", { stopColor: "#9DA4AE" }), jsx("stop", { offset: "1", stopColor: "#E5E7EB" })] }), jsxs("linearGradient", { gradientUnits: "userSpaceOnUse", id: "paint1_linear_8482_10235", x1: "44", x2: "-3.06232", y1: "-1.43647e-06", y2: "35.9963", children: [jsx("stop", { stopColor: "#E5E7EB" }), jsx("stop", { offset: "1", stopColor: "#9DA4AE" })] })] }), jsx("circle", { cx: "22", cy: "40", fill: "url(#paint0_linear_8482_10235)", r: "7" }), jsx("path", { d: "M6.69446 15.305C6.69446 6.85228 13.5467 0 21.9995 0C30.4522 0 37.3045 6.85228 37.3045 15.305V26.5731C37.3045 26.6145 37.3141 26.6552 37.3326 26.6922L43.2746 38.5521C43.6077 39.217 43.1242 40 42.3805 40H1.61938C0.875674 40 0.392197 39.2171 0.725289 38.5521L6.66632 26.6922C6.68483 26.6552 6.69446 26.6145 6.69446 26.5731V15.305Z", fill: "url(#paint1_linear_8482_10235)" })] }));
7
+ });
8
+
9
+ export { EmptyMainNotificationIcon };
@@ -13,14 +13,14 @@ export interface PresetPictogramEmptyProps {
13
13
  * The type of empty state, which determines the icon and color theme.
14
14
  * @default 'initial-data'
15
15
  */
16
- type?: 'initial-data' | 'result' | 'system';
16
+ type?: 'initial-data' | 'result' | 'system' | 'notification';
17
17
  /**
18
18
  * Custom pictogram element.
19
19
  */
20
20
  pictogram?: ReactNode;
21
21
  }
22
22
  export interface CustomPictogramEmptyProps {
23
- type?: 'notification' | 'custom';
23
+ type?: 'custom';
24
24
  pictogram?: ReactNode;
25
25
  }
26
26
  export interface MainOrSubEmptyProps {
@@ -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/Input/Input.d.ts CHANGED
@@ -17,6 +17,7 @@ export interface InputBaseProps extends Omit<TextFieldProps, 'children' | 'clear
17
17
  /**
18
18
  * Formatter function to transform the value for display.
19
19
  * Common use cases: currency formatting (1000 → "1,000"), phone numbers, etc.
20
+ * Default to formatting number with commas for "currency" variant.
20
21
  * @example
21
22
  * formatter={(value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
22
23
  */
@@ -49,6 +50,7 @@ export interface InputBaseProps extends Omit<TextFieldProps, 'children' | 'clear
49
50
  /**
50
51
  * Parser function to extract the raw value from formatted display value.
51
52
  * Should reverse the formatter transformation.
53
+ * Default to removing commas for "currency" formatting.
52
54
  * @example
53
55
  * parser={(value) => value.replace(/,/g, '')}
54
56
  */
@@ -113,10 +115,10 @@ export type NumberInputProps = InputBaseProps & NumberInput & {
113
115
  variant: 'number';
114
116
  };
115
117
  /**
116
- * 5. Unit Input - Input with unit text and spinner buttons
118
+ * 5. Currency Input - Input with unit text and spinner buttons
117
119
  */
118
- export type UnitInputProps = InputBaseProps & NumberInput & TextFieldAffixProps & {
119
- variant: 'unit';
120
+ export type CurrencyInputProps = InputBaseProps & NumberInput & TextFieldAffixProps & {
121
+ variant: 'currency';
120
122
  /**
121
123
  * Whether to show spinner buttons.
122
124
  * @default false
@@ -201,7 +203,7 @@ export type WithPasswordStrengthIndicator = {
201
203
  export type PasswordInputProps = InputBaseProps & ClearableInput & WithPasswordStrengthIndicator & {
202
204
  variant: 'password';
203
205
  };
204
- export type InputProps = BaseInputProps | WithAffixInputProps | SearchInputProps | NumberInputProps | UnitInputProps | ActionInputProps | SelectInputProps | PasswordInputProps;
206
+ export type InputProps = BaseInputProps | WithAffixInputProps | SearchInputProps | NumberInputProps | CurrencyInputProps | ActionInputProps | SelectInputProps | PasswordInputProps;
205
207
  /**
206
208
  * The react component for `mezzanine` input.
207
209
  */
package/Input/Input.js CHANGED
@@ -2,9 +2,11 @@
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { inputClasses } from '@mezzanine-ui/core/input';
4
4
  import { EyeIcon, EyeInvisibleIcon, SearchIcon } from '@mezzanine-ui/icons';
5
- import { forwardRef, useRef, useState, useCallback } from 'react';
5
+ import { forwardRef, useRef, useState, useMemo, useCallback } from 'react';
6
6
  import { useInputWithClearControlValue } from '../Form/useInputWithClearControlValue.js';
7
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';
9
11
  import Dropdown from '../Dropdown/Dropdown.js';
10
12
  import SelectButton from './SelectButton/SelectButton.js';
@@ -18,7 +20,7 @@ import cx from 'clsx';
18
20
  * The react component for `mezzanine` input.
19
21
  */
20
22
  const Input = forwardRef(function Input(props, ref) {
21
- 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;
22
24
  const inputRef = useRef(null);
23
25
  const [showPassword, setShowPassword] = useState(false);
24
26
  const [value, onChange, onClearFromHook] = useInputWithClearControlValue({
@@ -28,6 +30,22 @@ const Input = forwardRef(function Input(props, ref) {
28
30
  value: valueProp,
29
31
  });
30
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]);
31
49
  const handleChange = useCallback((event) => {
32
50
  let newValue = event.target.value;
33
51
  // Parse the formatted value back to raw value if parser is provided
@@ -106,20 +124,20 @@ const Input = forwardRef(function Input(props, ref) {
106
124
  };
107
125
  break;
108
126
  }
109
- case 'unit': {
110
- const unitProps = props;
111
- const { step = 1, max, min, onSpinUp, onSpinDown } = unitProps;
127
+ case 'currency': {
128
+ const currencyProps = props;
129
+ const { step = 1, max, min, onSpinUp, onSpinDown } = currencyProps;
112
130
  // 預設置右對齊
113
131
  inputStyle = { textAlign: 'right' };
114
132
  // 允許填入 prefix/suffix
115
- prefix = unitProps.prefix;
116
- suffix = unitProps.suffix;
133
+ prefix = currencyProps.prefix;
134
+ suffix = currencyProps.suffix;
117
135
  defaultInputProps = {
118
136
  min: min,
119
137
  max: max,
120
138
  step: step,
121
139
  };
122
- if (unitProps.showSpinner) {
140
+ if (currencyProps.showSpinner) {
123
141
  const handleSpinUp = () => {
124
142
  const currentValue = parseFloat(value || '0');
125
143
  const newValue = currentValue + step;
@@ -140,7 +158,7 @@ const Input = forwardRef(function Input(props, ref) {
140
158
  }
141
159
  onSpinDown === null || onSpinDown === void 0 ? void 0 : onSpinDown();
142
160
  };
143
- 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 })] })] }));
144
162
  }
145
163
  break;
146
164
  }
@@ -159,7 +177,7 @@ const Input = forwardRef(function Input(props, ref) {
159
177
  }
160
178
  case 'select': {
161
179
  const selectProps = props;
162
- const { selectButton, options, dropdownWidth = 120, dropdownMaxHeight = 114 } = selectProps;
180
+ const { selectButton, options, dropdownWidth = 120, dropdownMaxHeight = 114, } = selectProps;
163
181
  const defaultOptions = options || [];
164
182
  const selectedOptions = defaultOptions.length > 0
165
183
  ? defaultOptions.map((option) => ({
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 };