@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
@@ -0,0 +1,160 @@
1
+ import { ReactElement } from 'react';
2
+ import { ButtonProps } from '../Button';
3
+ import { SearchInputProps } from '../Input';
4
+ import { SelectProps } from '../Select';
5
+ import { NativeElementPropsWithoutKeyAndRef } from '../utils/jsx-types';
6
+ import { IconDefinition } from '@mezzanine-ui/icons';
7
+ import { DropdownProps } from '../Dropdown';
8
+ type SegmentedControlProps = {
9
+ mock: 'SegmentedControlProps';
10
+ };
11
+ type ContentHeaderChild = ReactElement<SearchInputProps> | ReactElement<SelectProps> | ReactElement<SegmentedControlProps> | ReactElement<ButtonProps> | ReactElement<DropdownProps> | ReactElement<{
12
+ href: string;
13
+ }> | null | false | undefined;
14
+ /**
15
+ * Props for the ContentHeader component.
16
+ *
17
+ * Defines a flexible header with title, description, filter, action buttons, and utility icons.
18
+ * Commonly used in page headers for primary user actions.
19
+ */
20
+ export type ContentHeaderProps = Omit<NativeElementPropsWithoutKeyAndRef<'header'>, 'children'> & {
21
+ /**
22
+ * Button configuration for actions. <br />
23
+ * Automatically applies proper styling and order. <br />
24
+ * When conflicting with other props, props take priority over children. <br />
25
+ * Buttons with variants other than primary, secondary, and destructive will not be rendered. <br />
26
+ */
27
+ actions?: ButtonProps[];
28
+ /**
29
+ * Children elements: <br />
30
+ * - Back button which Component with href prop.
31
+ * - example 1: `<a href="..." />` <br />
32
+ * - example 2: `<Link href="..." />` <br />
33
+ * - conflicting prop: `onBackClick` <br />
34
+ * - Filter component (SearchInput, Select, or SegmentedControl) <br />
35
+ * - Action buttons `<Button />` <br />
36
+ * - Icon-only utility buttons `<Button icon="..." />` <br />
37
+ * - Overflow icon utility dropdown `<Dropdown> <Button icon="..." /> </Dropdown>` <br />
38
+ * <br />
39
+ * Can mix and match the above children. <br />
40
+ * The order of children does not matter as they will be arranged properly. <br />
41
+ * When conflicting with other props, props take priority over children. <br />
42
+ */
43
+ children?: ContentHeaderChild[] | ContentHeaderChild;
44
+ /** Optional description text displayed below the title */
45
+ description?: string;
46
+ /** Filter component (SearchInput, Select, or SegmentedControl) */
47
+ filter?: {
48
+ variant: 'search' | 'select' | 'segmentedControl';
49
+ } & (SearchInputProps | SelectProps | SegmentedControlProps);
50
+ /**
51
+ * Optional back button properties. <br />
52
+ * When provided, a back button will be rendered on the left side of the title. <br />
53
+ * href prop from children will be ignored if onBackClick is provided. <br />
54
+ * */
55
+ onBackClick?: () => void;
56
+ /**
57
+ * Size variant of the toolbar. <br />
58
+ * Affects the size of buttons and filter component. <br />
59
+ */
60
+ size?: 'main' | 'sub';
61
+ /** Main title text for the content header */
62
+ title: string;
63
+ /**
64
+ * HTML element type for the title (defaults to 'h1' for main size and 'h2' for sub size)
65
+ */
66
+ titleComponent?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
67
+ /**
68
+ * Icon-only utility buttons `<Button icon="..." />`. <br />
69
+ * They usually appear as smaller buttons with only an icon and no text.
70
+ */
71
+ utilities?: ((ButtonProps & {
72
+ icon: IconDefinition;
73
+ }) | DropdownProps)[];
74
+ };
75
+ /**
76
+ * ContentHeader component. <br />
77
+ * A flexible header with title, description, filter, action buttons, and utility icons. <br />
78
+ * Commonly used in page headers for primary user actions. <br />
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * <ContentHeader
83
+ * size="main"
84
+ * title={'ContentHeader Title'}
85
+ * description="description."
86
+ * >
87
+ * <a href="" title="back" /> <-- Back button
88
+ *
89
+ * <Input variant="search" placeholder="Search..." /> <-- Filter component
90
+ * <Button variant="base-secondary">Secondary</Button> <-- Action buttons
91
+ * <Button>Primary</Button>
92
+ *
93
+ * <Button icon={PlusIcon} /> <-- Utility icon button
94
+ * <Dropdown
95
+ * options={[
96
+ * { id: '1', name: 'Option 1' },
97
+ * { id: '2', name: 'Option 2' },
98
+ * ]}
99
+ * >
100
+ * <Button icon={DotHorizontalIcon} />
101
+ * </Dropdown> <-- Utility dropdown
102
+ * </ContentHeader>
103
+ * ```
104
+ */
105
+ declare const ContentHeader: import("react").ForwardRefExoticComponent<Omit<NativeElementPropsWithoutKeyAndRef<"header">, "children"> & {
106
+ /**
107
+ * Button configuration for actions. <br />
108
+ * Automatically applies proper styling and order. <br />
109
+ * When conflicting with other props, props take priority over children. <br />
110
+ * Buttons with variants other than primary, secondary, and destructive will not be rendered. <br />
111
+ */
112
+ actions?: ButtonProps[];
113
+ /**
114
+ * Children elements: <br />
115
+ * - Back button which Component with href prop.
116
+ * - example 1: `<a href="..." />` <br />
117
+ * - example 2: `<Link href="..." />` <br />
118
+ * - conflicting prop: `onBackClick` <br />
119
+ * - Filter component (SearchInput, Select, or SegmentedControl) <br />
120
+ * - Action buttons `<Button />` <br />
121
+ * - Icon-only utility buttons `<Button icon="..." />` <br />
122
+ * - Overflow icon utility dropdown `<Dropdown> <Button icon="..." /> </Dropdown>` <br />
123
+ * <br />
124
+ * Can mix and match the above children. <br />
125
+ * The order of children does not matter as they will be arranged properly. <br />
126
+ * When conflicting with other props, props take priority over children. <br />
127
+ */
128
+ children?: ContentHeaderChild[] | ContentHeaderChild;
129
+ /** Optional description text displayed below the title */
130
+ description?: string;
131
+ /** Filter component (SearchInput, Select, or SegmentedControl) */
132
+ filter?: {
133
+ variant: "search" | "select" | "segmentedControl";
134
+ } & (SearchInputProps | SelectProps | SegmentedControlProps);
135
+ /**
136
+ * Optional back button properties. <br />
137
+ * When provided, a back button will be rendered on the left side of the title. <br />
138
+ * href prop from children will be ignored if onBackClick is provided. <br />
139
+ * */
140
+ onBackClick?: () => void;
141
+ /**
142
+ * Size variant of the toolbar. <br />
143
+ * Affects the size of buttons and filter component. <br />
144
+ */
145
+ size?: "main" | "sub";
146
+ /** Main title text for the content header */
147
+ title: string;
148
+ /**
149
+ * HTML element type for the title (defaults to 'h1' for main size and 'h2' for sub size)
150
+ */
151
+ titleComponent?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p";
152
+ /**
153
+ * Icon-only utility buttons `<Button icon="..." />`. <br />
154
+ * They usually appear as smaller buttons with only an icon and no text.
155
+ */
156
+ utilities?: ((ButtonProps & {
157
+ icon: IconDefinition;
158
+ }) | DropdownProps)[];
159
+ } & import("react").RefAttributes<HTMLElement>>;
160
+ export default ContentHeader;
@@ -0,0 +1,54 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { forwardRef } from 'react';
3
+ import { contentHeaderClasses } from '@mezzanine-ui/core/content-header';
4
+ import Button from '../Button/Button.js';
5
+ import ButtonGroup from '../Button/ButtonGroup.js';
6
+ import { ChevronLeftIcon } from '@mezzanine-ui/icons';
7
+ import { renderFilterProp, renderActionsProp, renderIconButtonsProp, resolveContentHeaderChild } from './utils.js';
8
+ import Typography from '../Typography/Typography.js';
9
+ import cx from 'clsx';
10
+
11
+ /**
12
+ * ContentHeader component. <br />
13
+ * A flexible header with title, description, filter, action buttons, and utility icons. <br />
14
+ * Commonly used in page headers for primary user actions. <br />
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <ContentHeader
19
+ * size="main"
20
+ * title={'ContentHeader Title'}
21
+ * description="description."
22
+ * >
23
+ * <a href="" title="back" /> <-- Back button
24
+ *
25
+ * <Input variant="search" placeholder="Search..." /> <-- Filter component
26
+ * <Button variant="base-secondary">Secondary</Button> <-- Action buttons
27
+ * <Button>Primary</Button>
28
+ *
29
+ * <Button icon={PlusIcon} /> <-- Utility icon button
30
+ * <Dropdown
31
+ * options={[
32
+ * { id: '1', name: 'Option 1' },
33
+ * { id: '2', name: 'Option 2' },
34
+ * ]}
35
+ * >
36
+ * <Button icon={DotHorizontalIcon} />
37
+ * </Dropdown> <-- Utility dropdown
38
+ * </ContentHeader>
39
+ * ```
40
+ */
41
+ const ContentHeader = forwardRef(function ContentHeader(props, ref) {
42
+ const { actions, children, className, description, filter, onBackClick, size = 'main', title, titleComponent = size === 'main' ? 'h1' : 'h2', utilities, ...rest } = props;
43
+ const renderFilter = renderFilterProp(filter, size);
44
+ const renderActions = actions ? renderActionsProp(actions, size) : null;
45
+ const renderUtilities = utilities
46
+ ? renderIconButtonsProp(utilities, size)
47
+ : null;
48
+ const { backButton, filter: filterFromChildren, actions: actionsFromChildren, utilities: utilitiesFromChildren, } = resolveContentHeaderChild(children, size);
49
+ // prop onBack takes precedence over backButtonOrLink
50
+ const renderBackButton = onBackClick ? (jsx(Button, { iconType: "icon-only", icon: ChevronLeftIcon, onClick: onBackClick, "aria-label": "Back", type: "button", size: "sub", variant: "base-tertiary" })) : (backButton);
51
+ return (jsxs("header", { ...rest, className: cx(contentHeaderClasses.host, contentHeaderClasses.size(size), className), ref: ref, children: [jsxs("span", { className: contentHeaderClasses.titleArea, children: [renderBackButton && (jsx("span", { className: contentHeaderClasses.backButton, children: renderBackButton })), jsxs("span", { className: contentHeaderClasses.textGroup, children: [jsx(Typography, { component: titleComponent, align: "left", color: "text-neutral-solid", variant: size === 'main' ? 'h2' : 'h3', children: title }), description && (jsx(Typography, { align: "left", color: "text-neutral", variant: "caption", children: description }))] })] }), jsxs("span", { className: contentHeaderClasses.actionArea, children: [renderFilter || filterFromChildren, (renderActions || actionsFromChildren.length > 0) && (jsx(ButtonGroup, { children: renderActions || actionsFromChildren })), (renderUtilities || utilitiesFromChildren.length > 0) && (jsx("span", { className: contentHeaderClasses.utilities, children: renderUtilities || utilitiesFromChildren }))] })] }));
52
+ });
53
+
54
+ export { ContentHeader as default };
@@ -0,0 +1,2 @@
1
+ export type { ContentHeaderProps } from './ContentHeader';
2
+ export { default } from './ContentHeader';
@@ -0,0 +1 @@
1
+ export { default } from './ContentHeader.js';
@@ -0,0 +1,23 @@
1
+ import { ReactElement } from 'react';
2
+ import { ButtonProps, ButtonGroupChild } from '../Button';
3
+ import { ContentHeaderProps } from './ContentHeader';
4
+ import { DropdownProps } from '../Dropdown';
5
+ /**
6
+ * Renders a button from either ButtonProps or a React element.
7
+ * Applies the specified size and variant to the button.
8
+ */
9
+ export declare const renderButton: (button: ButtonProps | ButtonGroupChild | undefined, size: ButtonProps["size"]) => ReactElement<ButtonProps> | null;
10
+ export declare const renderFilterProp: (prop: ContentHeaderProps["filter"], size: "main" | "sub") => import("react/jsx-runtime").JSX.Element | null;
11
+ export declare const renderIconButtonWithProps: (child: ReactElement<ButtonProps>, size: ButtonProps["size"]) => ReactElement<ButtonProps>;
12
+ export declare const renderIconButtonsProp: (utilities: ContentHeaderProps["utilities"], size: ButtonProps["size"]) => ButtonGroupChild | ButtonGroupChild[];
13
+ /**
14
+ * Renders action buttons based on the actions configuration.
15
+ * Supports both structured actions object and single button element/props.
16
+ */
17
+ export declare const renderActionsProp: (actions: ContentHeaderProps["actions"], size: ButtonProps["size"]) => ButtonGroupChild | ButtonGroupChild[];
18
+ export declare const resolveContentHeaderChild: (children: ContentHeaderProps["children"], size: "main" | "sub") => {
19
+ filter: null;
20
+ actions: ReactElement<ButtonProps, string | import("react").JSXElementConstructor<any>>[];
21
+ utilities: (ButtonGroupChild | ReactElement<DropdownProps, string | import("react").JSXElementConstructor<any>>)[];
22
+ backButton: null;
23
+ };
@@ -0,0 +1,215 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { isValidElement, cloneElement, Children } from 'react';
3
+ import Button from '../Button/Button.js';
4
+ import { flattenChildren } from '../utils/flatten-children.js';
5
+ import { ChevronLeftIcon } from '@mezzanine-ui/icons';
6
+ import Input from '../Input/Input.js';
7
+ import Select from '../Select/Select.js';
8
+ import Dropdown from '../Dropdown/Dropdown.js';
9
+
10
+ /**
11
+ * Renders a button from either ButtonProps or a React element.
12
+ * Applies the specified size and variant to the button.
13
+ */
14
+ const renderButton = (button, size) => {
15
+ if (!button) {
16
+ return null;
17
+ }
18
+ if (isValidElement(button)) {
19
+ return cloneElement(button, {
20
+ size,
21
+ type: 'button',
22
+ });
23
+ }
24
+ return jsx(Button, { ...button, size: size, type: "button" });
25
+ };
26
+ const withSize = (target, size) => {
27
+ return cloneElement(target, { size });
28
+ };
29
+ const renderFilterProp = (prop, size) => {
30
+ if (!prop) {
31
+ return null;
32
+ }
33
+ const { variant } = prop;
34
+ if (variant === 'search') {
35
+ return (jsx(Input, { ...prop, size: size, variant: "search" }));
36
+ }
37
+ if (variant === 'select') {
38
+ return jsx(Select, { ...prop, size: size });
39
+ }
40
+ if (variant === 'segmentedControl') {
41
+ console.warn('SegmentedControl component is not implemented yet.');
42
+ return null;
43
+ }
44
+ return null;
45
+ };
46
+ const renderIconButtonWithProps = (child, size) => {
47
+ const { icon } = child.props;
48
+ return cloneElement(child, {
49
+ icon,
50
+ iconType: 'icon-only',
51
+ size,
52
+ variant: 'base-secondary',
53
+ type: 'button',
54
+ });
55
+ };
56
+ const renderIconButtonsProp = (utilities, size) => {
57
+ const result = [];
58
+ utilities === null || utilities === void 0 ? void 0 : utilities.forEach((props) => {
59
+ if (props instanceof Object && 'icon' in props) {
60
+ result.push(jsx(Button, { ...props, type: "button", size: size, iconType: "icon-only", variant: "base-secondary" }));
61
+ }
62
+ if (props instanceof Object && 'options' in props) {
63
+ if (!isValidElement(props.children) ||
64
+ !(props.children.type === Button)) {
65
+ console.warn('[Mezzanine][ContentHeader]: Dropdown in utilities should have Button with icon as its children.');
66
+ return;
67
+ }
68
+ result.push(jsx(Dropdown, { ...props, children: cloneElement(props.children, {
69
+ type: 'button',
70
+ size: size,
71
+ iconType: 'icon-only',
72
+ variant: 'base-secondary',
73
+ }) }));
74
+ }
75
+ });
76
+ return result;
77
+ };
78
+ const variantOrder = {
79
+ 'destructive-secondary': 0,
80
+ 'base-secondary': 1,
81
+ 'base-primary': 2,
82
+ // undefined: 2,
83
+ 'base-tertiary': 0,
84
+ 'base-ghost': 0,
85
+ 'base-dashed': 0,
86
+ 'base-text-link': 0,
87
+ 'destructive-primary': 0,
88
+ 'destructive-ghost': 0,
89
+ 'destructive-text-link': 0,
90
+ inverse: 0,
91
+ 'inverse-ghost': 0,
92
+ };
93
+ /**
94
+ * Renders action buttons based on the actions configuration.
95
+ * Supports both structured actions object and single button element/props.
96
+ */
97
+ const renderActionsProp = (actions, size) => {
98
+ if (actions) {
99
+ return actions
100
+ .filter((v) => {
101
+ if (v.variant === 'destructive-secondary' ||
102
+ v.variant === 'base-secondary' ||
103
+ v.variant === 'base-primary' ||
104
+ v.variant === undefined) {
105
+ return true;
106
+ }
107
+ else {
108
+ console.warn(`[Mezzanine][ContentHeader]: Button with variant "${v.variant}" will not be rendered in ContentHeader actions.`);
109
+ return false;
110
+ }
111
+ })
112
+ .sort((a, b) => variantOrder[a.variant || 'base-primary'] -
113
+ variantOrder[b.variant || 'base-primary'])
114
+ .map((v) => renderButton(v, size));
115
+ }
116
+ return null;
117
+ };
118
+ const resolveContentHeaderChild = (children, size) => {
119
+ let filter = null;
120
+ // [destructive-secondary[], base-secondary[], base-primary[]]
121
+ const actionsWithOrder = [[], [], []];
122
+ const utilities = [];
123
+ let backButton = null;
124
+ if (children) {
125
+ const flatChildren = flattenChildren(children);
126
+ Children.forEach(flatChildren, (child) => {
127
+ if (!isValidElement(child)) {
128
+ return;
129
+ }
130
+ const { type, props } = child;
131
+ if (type === 'a' || props.href) {
132
+ backButton = cloneElement(child, {
133
+ children: (jsx(Button, { component: 'span', iconType: "icon-only", icon: ChevronLeftIcon, "aria-label": "Back", type: "button", size: "sub", variant: "base-tertiary" })),
134
+ });
135
+ return;
136
+ }
137
+ const sizeProp = props.size;
138
+ if (sizeProp !== undefined && sizeProp !== size) {
139
+ console.warn('[Mezzanine][ContentHeader]: Input, Button, Select size in ContentHeader utilities is forced to match ContentHeader size.');
140
+ }
141
+ // is filter
142
+ if ((type === Input && props.variant === 'search') ||
143
+ type === Select) {
144
+ if (filter) {
145
+ console.warn('[Mezzanine][ContentHeader]: ContentHeader only accepts one filter component.');
146
+ }
147
+ filter = withSize(child, size);
148
+ }
149
+ else if (type.toString() === 'SegmentedControl') {
150
+ console.warn('SegmentedControl component is not implemented yet.');
151
+ }
152
+ // is utilities (icon button)
153
+ else if ((type === Button && props.iconType === 'icon-only') ||
154
+ (type === Button &&
155
+ props.icon &&
156
+ !props.children)) {
157
+ utilities.push(renderIconButtonWithProps(child, size));
158
+ }
159
+ else if (type === Dropdown) {
160
+ const childProps = child.props
161
+ .children;
162
+ if (!isValidElement(childProps) || childProps.type !== Button) {
163
+ console.warn('[Mezzanine][ContentHeader]: Dropdown in utilities should have Button with icon as its children.');
164
+ return;
165
+ }
166
+ utilities.push(cloneElement(child, {
167
+ children: renderIconButtonWithProps(child.props
168
+ .children, size),
169
+ }));
170
+ }
171
+ // is actions (normal button)
172
+ else if (type === Button) {
173
+ const variant = props.variant;
174
+ if (variant !== 'base-primary' &&
175
+ variant !== 'base-secondary' &&
176
+ variant !== 'destructive-secondary' &&
177
+ variant !== undefined) {
178
+ console.warn(`[Mezzanine][ContentHeader]: Button with variant "${variant}" will not be rendered in ContentHeader actions.`);
179
+ return;
180
+ }
181
+ const buttonElement = renderButton(child, size);
182
+ if (!buttonElement) {
183
+ return;
184
+ }
185
+ if (variant === 'destructive-secondary') {
186
+ actionsWithOrder[0].push(buttonElement);
187
+ }
188
+ else if (variant === 'base-secondary') {
189
+ actionsWithOrder[1].push(buttonElement);
190
+ }
191
+ else if (variant === 'base-primary') {
192
+ actionsWithOrder[2].push(buttonElement);
193
+ }
194
+ else if (variant === undefined) {
195
+ actionsWithOrder[2].push(buttonElement);
196
+ }
197
+ }
198
+ else {
199
+ console.warn('[Mezzanine][ContentHeader]: ContentHeader only accepts Input (search variant), Select, SegmentedControl, Dropdown with Icon Button, or Button as children.');
200
+ }
201
+ });
202
+ }
203
+ return {
204
+ filter,
205
+ actions: [
206
+ ...actionsWithOrder[0],
207
+ ...actionsWithOrder[1],
208
+ ...actionsWithOrder[2],
209
+ ],
210
+ utilities,
211
+ backButton,
212
+ };
213
+ };
214
+
215
+ export { renderActionsProp, renderButton, renderFilterProp, renderIconButtonWithProps, renderIconButtonsProp, resolveContentHeaderChild };
@@ -162,5 +162,22 @@ export interface DropdownProps extends DropdownItemSharedProps {
162
162
  * The icon of the dropdown empty status.
163
163
  */
164
164
  emptyIcon?: IconDefinition;
165
+ /**
166
+ * Whether to disable portal.
167
+ * This prop is only relevant when `inputPosition` is set to 'outside'.
168
+ * Controls whether the dropdown content is rendered within the current hierarchy or portaled to the body.
169
+ * @default false
170
+ */
171
+ disablePortal?: boolean;
172
+ /**
173
+ * Callback fired when the dropdown list reaches the bottom.
174
+ * Only fires when `maxHeight` is set and the list is scrollable.
175
+ */
176
+ onReachBottom?: () => void;
177
+ /**
178
+ * Callback fired when the dropdown list leaves the bottom.
179
+ * Only fires when `maxHeight` is set and the list is scrollable.
180
+ */
181
+ onLeaveBottom?: () => void;
165
182
  }
166
183
  export default function Dropdown(props: DropdownProps): import("react/jsx-runtime").JSX.Element;
@@ -14,7 +14,7 @@ import DropdownItem from './DropdownItem.js';
14
14
  import Popper from '../Popper/Popper.js';
15
15
 
16
16
  function Dropdown(props) {
17
- const { activeIndex: activeIndexProp, id, children, options = [], type = 'default', maxHeight, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, followText: followTextProp, mode, value, } = props;
17
+ const { activeIndex: activeIndexProp, id, children, options = [], type = 'default', maxHeight, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, followText: followTextProp, disablePortal = false, onReachBottom, onLeaveBottom, mode, value, } = props;
18
18
  const isInline = inputPosition === 'inside';
19
19
  const inputId = useId();
20
20
  const defaultListboxId = `${inputId}-listbox`;
@@ -229,6 +229,8 @@ function Dropdown(props) {
229
229
  sameWidth,
230
230
  onHover: handleItemHover,
231
231
  onSelect,
232
+ onReachBottom,
233
+ onLeaveBottom,
232
234
  options,
233
235
  type,
234
236
  status,
@@ -248,6 +250,8 @@ function Dropdown(props) {
248
250
  sameWidth,
249
251
  handleItemHover,
250
252
  onSelect,
253
+ onReachBottom,
254
+ onLeaveBottom,
251
255
  options,
252
256
  type,
253
257
  status,
@@ -349,7 +353,7 @@ function Dropdown(props) {
349
353
  }, [isInline, isOpen, setOpen]);
350
354
  return (jsxs("div", { id: id, ref: containerRef, className: cx(dropdownClasses.root, dropdownClasses.inputPosition(inputPosition)), children: [isInline && (jsxs(TransitionGroup, { component: null, children: [!isOpen && inlineTriggerElement && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-trigger", in: true },
351
355
  jsx("div", { children: inlineTriggerElement }))), isOpen && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-list", in: true },
352
- jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, controllerRef: popperControllerRef, open: isOpen, disablePortal: true, options: {
356
+ jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, className: dropdownClasses.popperWithPortal, controllerRef: popperControllerRef, open: isOpen, disablePortal: disablePortal, options: {
353
357
  placement: popoverPlacement,
354
358
  middleware: [
355
359
  offsetMiddleware,
@@ -73,5 +73,15 @@ export interface DropdownItemProps<T extends DropdownType | undefined = Dropdown
73
73
  * The icon of the dropdown empty status.
74
74
  */
75
75
  emptyIcon?: IconDefinition;
76
+ /**
77
+ * Callback fired when the dropdown list reaches the bottom.
78
+ * Only fires when `maxHeight` is set and the list is scrollable.
79
+ */
80
+ onReachBottom?: () => void;
81
+ /**
82
+ * Callback fired when the dropdown list leaves the bottom.
83
+ * Only fires when `maxHeight` is set and the list is scrollable.
84
+ */
85
+ onLeaveBottom?: () => void;
76
86
  }
77
87
  export default function DropdownItem<T extends DropdownType | undefined = DropdownType>(props: DropdownItemProps<T>): import("react/jsx-runtime").JSX.Element;
@@ -57,9 +57,10 @@ function truncateArrayDepth(input, maxDepth = 3, warn = true) {
57
57
  return truncate(input);
58
58
  }
59
59
  function DropdownItem(props) {
60
- const { activeIndex, disabled = false, listboxId, listboxLabel, mode = 'single', options, value, type, maxHeight, actionConfig, onHover, onSelect, followText, headerContent, status, loadingText, emptyText, emptyIcon, } = props;
60
+ const { activeIndex, disabled = false, listboxId, listboxLabel, mode = 'single', options, value, type, maxHeight, actionConfig, onHover, onSelect, followText, headerContent, status, loadingText, emptyText, emptyIcon, onReachBottom, onLeaveBottom, } = props;
61
61
  const optionsContent = truncateArrayDepth(options, 3);
62
62
  const listRef = useRef(null);
63
+ const listWrapperRef = useRef(null);
63
64
  const [expandedNodes, setExpandedNodes] = useState(new Set());
64
65
  const hasActions = Boolean(actionConfig === null || actionConfig === void 0 ? void 0 : actionConfig.showActions);
65
66
  const hasHeader = Boolean(headerContent);
@@ -242,7 +243,7 @@ function DropdownItem(props) {
242
243
  const renderDefaultOptions = (optionList, startIndex) => {
243
244
  let currentIndex = startIndex;
244
245
  const elements = (optionList !== null && optionList !== void 0 ? optionList : []).flatMap((option) => {
245
- var _a, _b, _c;
246
+ var _a, _b, _c, _d;
246
247
  currentIndex += 1;
247
248
  const optionIndex = currentIndex;
248
249
  const isSelected = Array.isArray(value)
@@ -252,15 +253,12 @@ function DropdownItem(props) {
252
253
  const shortcutText = option.shortcutText
253
254
  ? option.shortcutText
254
255
  : shortcutTextHandler((_a = option.shortcutKeys) !== null && _a !== void 0 ? _a : []);
255
- let checkSite = 'none';
256
- if (option === null || option === void 0 ? void 0 : option.checkSite) {
257
- checkSite = option.checkSite;
258
- }
256
+ const checkSite = (_b = option === null || option === void 0 ? void 0 : option.checkSite) !== null && _b !== void 0 ? _b : 'none';
259
257
  return (jsx(DropdownItemCard, { followText: followText, active: isActive, checked: isSelected, disabled: disabled, id: `${listboxId}-option-${optionIndex}`, label: option.name, mode: mode, name: option.name, onClick: () => {
260
258
  if (disabled)
261
259
  return;
262
260
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
263
- }, onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), prependIcon: option.icon, validate: (_b = option.validate) !== null && _b !== void 0 ? _b : 'default', showUnderline: (_c = option.showUnderline) !== null && _c !== void 0 ? _c : false, checkSite: checkSite, appendContent: shortcutText }, option.id));
261
+ }, onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), prependIcon: option.icon, validate: (_c = option.validate) !== null && _c !== void 0 ? _c : 'default', showUnderline: (_d = option.showUnderline) !== null && _d !== void 0 ? _d : false, checkSite: checkSite, appendContent: shortcutText }, option.id));
264
262
  });
265
263
  return { elements, nextIndex: currentIndex };
266
264
  };
@@ -323,8 +321,39 @@ function DropdownItem(props) {
323
321
  listElement.removeEventListener('keydown', handleKeyDown);
324
322
  };
325
323
  }, [disabled, matchShortcut, onSelect, type, toggleExpand, visibleShortcutOptions]);
324
+ // Handle scroll to bottom detection
325
+ useEffect(() => {
326
+ const listWrapperElement = listWrapperRef.current;
327
+ if (!listWrapperElement || !maxHeight || (!onReachBottom && !onLeaveBottom)) {
328
+ return;
329
+ }
330
+ // Initialize wasAtBottom state by checking current position
331
+ const checkInitialState = () => {
332
+ const { scrollTop, scrollHeight, clientHeight } = listWrapperElement;
333
+ return scrollTop + clientHeight >= scrollHeight - 1;
334
+ };
335
+ let wasAtBottom = checkInitialState();
336
+ const handleScroll = () => {
337
+ const { scrollTop, scrollHeight, clientHeight } = listWrapperElement;
338
+ // Check if scrolled to bottom (with 1px threshold for rounding errors)
339
+ const isAtBottom = scrollTop + clientHeight >= scrollHeight - 1;
340
+ // Trigger onReachBottom when entering bottom state
341
+ if (isAtBottom && !wasAtBottom) {
342
+ onReachBottom === null || onReachBottom === void 0 ? void 0 : onReachBottom();
343
+ }
344
+ // Trigger onLeaveBottom when leaving bottom state
345
+ if (!isAtBottom && wasAtBottom) {
346
+ onLeaveBottom === null || onLeaveBottom === void 0 ? void 0 : onLeaveBottom();
347
+ }
348
+ wasAtBottom = isAtBottom;
349
+ };
350
+ listWrapperElement.addEventListener('scroll', handleScroll);
351
+ return () => {
352
+ listWrapperElement.removeEventListener('scroll', handleScroll);
353
+ };
354
+ }, [maxHeight, onReachBottom, onLeaveBottom]);
326
355
  return (jsxs("ul", { "aria-label": listboxLabel || (optionsContent.length === 0 ? 'Dropdown options' : undefined), className: dropdownClasses.list, id: listboxId, ref: listRef, role: "listbox", style: listStyle, tabIndex: -1, children: [hasHeader && (jsx("li", { className: dropdownClasses.listHeader, role: "presentation", ref: headerRef, children: jsx("div", { className: dropdownClasses.listHeaderInner, children: headerContent }) })), maxHeight
327
- ? (jsx("div", { className: dropdownClasses.listWrapper, style: listWrapperStyle, children: shouldShowStatus ? (jsx(DropdownStatus, { status: status, loadingText: loadingText, emptyText: emptyText, emptyIcon: emptyIcon })) : (renderedOptions) }))
356
+ ? (jsx("div", { ref: listWrapperRef, className: dropdownClasses.listWrapper, style: listWrapperStyle, children: shouldShowStatus ? (jsx(DropdownStatus, { status: status, loadingText: loadingText, emptyText: emptyText, emptyIcon: emptyIcon })) : (renderedOptions) }))
328
357
  : shouldShowStatus ? (jsx(DropdownStatus, { status: status, loadingText: loadingText, emptyText: emptyText, emptyIcon: emptyIcon })) : (renderedOptions), hasActions && (jsx("div", { ref: actionRef, children: jsx(DropdownAction, { ...actionConfig }) }))] }));
329
358
  }
330
359
 
@@ -1,5 +1,5 @@
1
- import { DropdownCheckPosition, DropdownItemLevel, DropdownItemValidate, DropdownMode } from "@mezzanine-ui/core/dropdown/dropdown";
2
- import { type IconDefinition } from "@mezzanine-ui/icons";
1
+ import { DropdownCheckPosition, DropdownItemLevel, DropdownItemValidate, DropdownMode } from '@mezzanine-ui/core/dropdown/dropdown';
2
+ import { type IconDefinition } from '@mezzanine-ui/icons';
3
3
  export interface DropdownItemCardProps {
4
4
  /**
5
5
  * Whether the option is currently active (highlighted by keyboard navigation).