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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/Anchor/Anchor.d.ts +51 -18
  2. package/Anchor/Anchor.js +15 -15
  3. package/Anchor/AnchorGroup.d.ts +34 -0
  4. package/Anchor/AnchorGroup.js +37 -0
  5. package/Anchor/AnchorItem.d.ts +30 -0
  6. package/Anchor/AnchorItem.js +65 -0
  7. package/Anchor/index.d.ts +2 -0
  8. package/Anchor/index.js +1 -0
  9. package/Anchor/utils.d.ts +13 -0
  10. package/Anchor/utils.js +95 -0
  11. package/AutoComplete/AutoComplete.d.ts +194 -0
  12. package/AutoComplete/AutoComplete.js +419 -0
  13. package/AutoComplete/index.d.ts +2 -0
  14. package/AutoComplete/index.js +1 -0
  15. package/AutoComplete/useAutoCompleteCreation.d.ts +33 -0
  16. package/AutoComplete/useAutoCompleteCreation.js +201 -0
  17. package/AutoComplete/useAutoCompleteKeyboard.d.ts +31 -0
  18. package/AutoComplete/useAutoCompleteKeyboard.js +149 -0
  19. package/AutoComplete/useAutoCompleteSearch.d.ts +16 -0
  20. package/AutoComplete/useAutoCompleteSearch.js +69 -0
  21. package/AutoComplete/useCreationTracker.d.ts +17 -0
  22. package/AutoComplete/useCreationTracker.js +47 -0
  23. package/Badge/Badge.js +2 -2
  24. package/Breadcrumb/BreadcrumbItem.d.ts +1 -1
  25. package/Button/Button.js +13 -11
  26. package/Button/index.d.ts +1 -1
  27. package/Button/typings.d.ts +27 -4
  28. package/Description/Description.d.ts +30 -0
  29. package/Description/Description.js +13 -0
  30. package/Description/DescriptionContent.d.ts +41 -0
  31. package/Description/DescriptionContent.js +14 -0
  32. package/Description/DescriptionGroup.d.ts +13 -0
  33. package/Description/DescriptionGroup.js +12 -0
  34. package/Description/DescriptionTitle.d.ts +45 -0
  35. package/Description/DescriptionTitle.js +17 -0
  36. package/Description/index.d.ts +8 -0
  37. package/Description/index.js +4 -0
  38. package/Dropdown/Dropdown.d.ts +43 -3
  39. package/Dropdown/Dropdown.js +154 -35
  40. package/Dropdown/DropdownAction.d.ts +1 -1
  41. package/Dropdown/DropdownAction.js +1 -4
  42. package/Dropdown/DropdownItem.d.ts +21 -4
  43. package/Dropdown/DropdownItem.js +23 -10
  44. package/Dropdown/DropdownItemCard.d.ts +5 -5
  45. package/Dropdown/DropdownItemCard.js +11 -10
  46. package/Dropdown/DropdownStatus.d.ts +2 -2
  47. package/Dropdown/DropdownStatus.js +29 -0
  48. package/Dropdown/dropdownKeydownHandler.d.ts +2 -1
  49. package/Dropdown/dropdownKeydownHandler.js +73 -0
  50. package/Dropdown/highlightText.js +5 -1
  51. package/Dropdown/shortcutTextHandler.d.ts +24 -0
  52. package/Dropdown/shortcutTextHandler.js +171 -0
  53. package/Form/FormControlContext.d.ts +2 -2
  54. package/Form/FormField.d.ts +56 -4
  55. package/Form/FormField.js +10 -6
  56. package/Form/FormHintText.d.ts +24 -1
  57. package/Form/FormHintText.js +4 -4
  58. package/Form/FormLabel.d.ts +6 -3
  59. package/Form/FormLabel.js +5 -3
  60. package/Input/Input.d.ts +29 -3
  61. package/Input/Input.js +22 -6
  62. package/Input/PasswordStrengthIndicator/PasswordStrengthIndicator.js +1 -1
  63. package/Modal/Modal.d.ts +103 -11
  64. package/Modal/Modal.js +14 -9
  65. package/Modal/ModalBodyForVerification.d.ts +59 -0
  66. package/Modal/ModalBodyForVerification.js +99 -0
  67. package/Modal/ModalControl.d.ts +2 -2
  68. package/Modal/ModalControl.js +1 -1
  69. package/Modal/ModalFooter.d.ts +119 -1
  70. package/Modal/ModalFooter.js +15 -3
  71. package/Modal/ModalHeader.d.ts +26 -7
  72. package/Modal/ModalHeader.js +33 -7
  73. package/Modal/index.d.ts +4 -5
  74. package/Modal/index.js +1 -2
  75. package/Modal/useModalContainer.d.ts +12 -3
  76. package/Modal/useModalContainer.js +28 -6
  77. package/Navigation/CollapsedMenu.d.ts +6 -0
  78. package/Navigation/CollapsedMenu.js +16 -0
  79. package/Navigation/Navigation.d.ts +17 -3
  80. package/Navigation/Navigation.js +48 -33
  81. package/Navigation/NavigationFooter.js +4 -2
  82. package/Navigation/NavigationHeader.d.ts +11 -1
  83. package/Navigation/NavigationHeader.js +6 -3
  84. package/Navigation/NavigationOption.d.ts +3 -2
  85. package/Navigation/NavigationOption.js +45 -26
  86. package/Navigation/NavigationOptionCategory.js +20 -2
  87. package/Navigation/context.d.ts +2 -0
  88. package/Navigation/useVisibleItems.d.ts +5 -0
  89. package/Navigation/useVisibleItems.js +54 -0
  90. package/NotificationCenter/NotificationCenter.d.ts +124 -0
  91. package/NotificationCenter/NotificationCenter.js +259 -0
  92. package/NotificationCenter/NotificationCenterDrawer.d.ts +89 -0
  93. package/NotificationCenter/index.d.ts +3 -0
  94. package/NotificationCenter/index.js +1 -0
  95. package/PageFooter/PageFooter.d.ts +19 -9
  96. package/PageFooter/PageFooter.js +10 -10
  97. package/PageHeader/PageHeader.js +4 -12
  98. package/PageToolbar/PageToolbar.d.ts +2 -6
  99. package/PageToolbar/utils.js +4 -12
  100. package/Select/index.d.ts +0 -2
  101. package/Select/index.js +0 -1
  102. package/Slider/useSlider.js +1 -1
  103. package/Table/Table.d.ts +53 -15
  104. package/Table/Table.js +178 -82
  105. package/Table/TableContext.d.ts +18 -42
  106. package/Table/components/TableActionsCell.d.ts +26 -0
  107. package/Table/components/TableActionsCell.js +78 -0
  108. package/Table/components/TableBody.d.ts +2 -5
  109. package/Table/components/TableBody.js +16 -19
  110. package/Table/components/TableBulkActions.d.ts +15 -0
  111. package/Table/components/TableBulkActions.js +26 -0
  112. package/Table/components/TableCell.d.ts +2 -0
  113. package/Table/components/TableCell.js +42 -10
  114. package/Table/components/TableColGroup.js +10 -112
  115. package/Table/components/TableColumnTitleMenu.d.ts +6 -0
  116. package/Table/components/TableColumnTitleMenu.js +20 -0
  117. package/Table/components/TableDragHandleCell.d.ts +2 -0
  118. package/Table/components/TableDragHandleCell.js +8 -1
  119. package/Table/components/TableExpandCell.d.ts +2 -0
  120. package/Table/components/TableExpandCell.js +8 -1
  121. package/Table/components/TableExpandedRow.js +3 -2
  122. package/Table/components/TableHeader.d.ts +2 -4
  123. package/Table/components/TableHeader.js +11 -14
  124. package/Table/components/TableResizeHandle.js +3 -7
  125. package/Table/components/TableRow.js +54 -20
  126. package/Table/components/TableSelectionCell.d.ts +5 -0
  127. package/Table/components/TableSelectionCell.js +12 -1
  128. package/Table/components/index.d.ts +1 -0
  129. package/Table/components/index.js +1 -0
  130. package/Table/hooks/index.d.ts +1 -1
  131. package/Table/hooks/index.js +1 -1
  132. package/Table/hooks/useTableDataSource.d.ts +2 -2
  133. package/Table/hooks/useTableExpansion.js +0 -6
  134. package/Table/hooks/useTableFixedOffsets.d.ts +1 -1
  135. package/Table/hooks/useTableFixedOffsets.js +24 -26
  136. package/Table/hooks/useTableResizedColumns.d.ts +2 -0
  137. package/Table/hooks/useTableResizedColumns.js +22 -0
  138. package/Table/hooks/useTableScroll.d.ts +3 -1
  139. package/Table/hooks/useTableScroll.js +25 -19
  140. package/Table/hooks/useTableSelection.js +32 -8
  141. package/Table/hooks/useTableVirtualization.d.ts +1 -1
  142. package/Table/index.d.ts +4 -4
  143. package/Table/index.js +5 -3
  144. package/Table/utils/calculateColumnWidths.d.ts +28 -0
  145. package/Table/utils/calculateColumnWidths.js +80 -0
  146. package/Table/utils/index.d.ts +2 -0
  147. package/Table/utils/index.js +1 -0
  148. package/Table/utils/useTableRowSelection.d.ts +5 -5
  149. package/Table/utils/useTableRowSelection.js +14 -6
  150. package/Tag/TagGroup.d.ts +3 -0
  151. package/Tag/index.d.ts +2 -0
  152. package/Tag/index.js +1 -0
  153. package/Upload/UploadPictureCard.js +1 -1
  154. package/index.d.ts +36 -20
  155. package/index.js +26 -7
  156. package/package.json +4 -4
  157. package/utils/format-number-with-commas.d.ts +4 -0
  158. package/utils/format-number-with-commas.js +27 -0
  159. package/utils/parse-number-with-commas.d.ts +4 -0
  160. package/utils/parse-number-with-commas.js +22 -0
  161. package/Modal/ModalActions.d.ts +0 -9
  162. package/Modal/ModalActions.js +0 -20
  163. package/Modal/ModalBody.d.ts +0 -7
  164. package/Modal/ModalBody.js +0 -14
  165. package/Notification/Notification.d.ts +0 -54
  166. package/Notification/Notification.js +0 -76
  167. package/Notification/index.d.ts +0 -3
  168. package/Notification/index.js +0 -1
  169. package/Select/AutoComplete.d.ts +0 -107
  170. package/Select/AutoComplete.js +0 -114
  171. package/Table/hooks/useTableColumns.d.ts +0 -8
  172. package/Table/hooks/useTableColumns.js +0 -91
@@ -0,0 +1,8 @@
1
+ export { default as Description } from './Description';
2
+ export type { DescriptionProps } from './Description';
3
+ export { default as DescriptionTitle } from './DescriptionTitle';
4
+ export type { DescriptionTitleProps } from './DescriptionTitle';
5
+ export { default as DescriptionContent } from './DescriptionContent';
6
+ export type { DescriptionContentProps } from './DescriptionContent';
7
+ export { default as DescriptionGroup } from './DescriptionGroup';
8
+ export type { DescriptionGroupProps } from './DescriptionGroup';
@@ -0,0 +1,4 @@
1
+ export { default as Description } from './Description.js';
2
+ export { default as DescriptionTitle } from './DescriptionTitle.js';
3
+ export { default as DescriptionContent } from './DescriptionContent.js';
4
+ export { default as DescriptionGroup } from './DescriptionGroup.js';
@@ -1,8 +1,9 @@
1
1
  import { ReactElement } from 'react';
2
- import { dropdownInputPosition, DropdownItemSharedProps, DropdownOption, dropdownType } from '@mezzanine-ui/core/dropdown/dropdown';
2
+ import { DropdownInputPosition, DropdownItemSharedProps, DropdownOption, DropdownStatus as DropdownStatusType, DropdownType } from '@mezzanine-ui/core/dropdown/dropdown';
3
3
  import { ButtonProps } from '../Button';
4
4
  import { InputProps } from '../Input';
5
5
  import { PopperPlacement } from '../Popper';
6
+ import { IconDefinition } from '@mezzanine-ui/icons';
6
7
  export interface DropdownProps extends DropdownItemSharedProps {
7
8
  /**
8
9
  * The text of the cancel button.
@@ -45,12 +46,17 @@ export interface DropdownProps extends DropdownItemSharedProps {
45
46
  * The position of the input.
46
47
  * @default 'outside'
47
48
  */
48
- inputPosition?: dropdownInputPosition;
49
+ inputPosition?: DropdownInputPosition;
49
50
  /**
50
51
  * Whether to match the input value.
51
52
  * @default false
52
53
  */
53
54
  isMatchInputValue?: boolean;
55
+ /**
56
+ * The text to follow for highlighting in dropdown options.
57
+ * If provided, this will be used instead of auto-extracting from children props.
58
+ */
59
+ followText?: string;
54
60
  /**
55
61
  * The listbox id of the dropdown.
56
62
  */
@@ -64,6 +70,10 @@ export interface DropdownProps extends DropdownItemSharedProps {
64
70
  * The max height of the dropdown list.
65
71
  */
66
72
  maxHeight?: number | string;
73
+ /**
74
+ * Whether the dropdown is open (controlled).
75
+ */
76
+ open?: boolean;
67
77
  /**
68
78
  * Callback fired when the action cancel is clicked.
69
79
  */
@@ -92,6 +102,10 @@ export interface DropdownProps extends DropdownItemSharedProps {
92
102
  * Callback fired when the dropdown is opened.
93
103
  */
94
104
  onOpen?: () => void;
105
+ /**
106
+ * Callback fired when the dropdown visibility changes.
107
+ */
108
+ onVisibilityChange?: (open: boolean) => void;
95
109
  /**
96
110
  * Callback fired when the item is selected.
97
111
  */
@@ -104,6 +118,12 @@ export interface DropdownProps extends DropdownItemSharedProps {
104
118
  * The placement of the dropdown.
105
119
  */
106
120
  placement?: PopperPlacement;
121
+ /**
122
+ * Custom width for the dropdown.
123
+ * Can be a number (pixels) or a string (e.g., '200px', '50%').
124
+ * If provided, this takes precedence over `sameWidth`.
125
+ */
126
+ customWidth?: number | string;
107
127
  /**
108
128
  * Whether to set the same width as its anchor element.
109
129
  * @default false
@@ -121,6 +141,26 @@ export interface DropdownProps extends DropdownItemSharedProps {
121
141
  /**
122
142
  * The type of the dropdown.
123
143
  */
124
- type?: dropdownType;
144
+ type?: DropdownType;
145
+ /**
146
+ * The z-index of the dropdown.
147
+ */
148
+ zIndex?: number | string;
149
+ /**
150
+ * The status of the dropdown (loading or empty).
151
+ */
152
+ status?: DropdownStatusType;
153
+ /**
154
+ * The text of the dropdown loading status.
155
+ */
156
+ loadingText?: string;
157
+ /**
158
+ * The text of the dropdown empty status.
159
+ */
160
+ emptyText?: string;
161
+ /**
162
+ * The icon of the dropdown empty status.
163
+ */
164
+ emptyIcon?: IconDefinition;
125
165
  }
126
166
  export default function Dropdown(props: DropdownProps): import("react/jsx-runtime").JSX.Element;
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { useId, useMemo, useState, useRef, useEffect, useCallback, cloneElement, createElement } from 'react';
3
+ import { useId, useMemo, useState, useRef, useCallback, useEffect, cloneElement, createElement } from 'react';
4
4
  import cx from 'clsx';
5
5
  import { dropdownClasses } from '@mezzanine-ui/core/dropdown/dropdown';
6
- import { size, offset } from '@floating-ui/react-dom';
6
+ import { size, offset, autoUpdate } from '@floating-ui/react-dom';
7
7
  import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
8
8
  import { TransitionGroup } from 'react-transition-group';
9
9
  import Button from '../Button/Button.js';
@@ -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', sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, } = 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;
18
18
  const isInline = inputPosition === 'inside';
19
19
  const inputId = useId();
20
20
  const defaultListboxId = `${inputId}-listbox`;
@@ -33,11 +33,29 @@ function Dropdown(props) {
33
33
  onClick: onActionCustom,
34
34
  onClear: onActionClear,
35
35
  };
36
- }, [showDropdownActions, actionCancelText, actionConfirmText, actionClearText, actionText, actionCustomButtonProps, showActionShowTopBar, onActionConfirm, onActionCancel, onActionCustom, onActionClear]);
37
- const [isOpen, setIsOpen] = useState(false);
38
- const [uncontrolledActiveIndex, setUncontrolledActiveIndex] = useState(activeIndexProp !== null && activeIndexProp !== void 0 ? activeIndexProp : null);
36
+ }, [
37
+ showDropdownActions,
38
+ actionCancelText,
39
+ actionConfirmText,
40
+ actionClearText,
41
+ actionText,
42
+ actionCustomButtonProps,
43
+ showActionShowTopBar,
44
+ onActionConfirm,
45
+ onActionCancel,
46
+ onActionCustom,
47
+ onActionClear,
48
+ ]);
49
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
50
+ const isOpenControlled = openProp !== undefined;
51
+ const isOpen = isOpenControlled ? !!openProp : uncontrolledOpen;
52
+ // Keep setter for uncontrolled mode support (e.g., keyboard navigation)
53
+ // Currently not used in handleItemHover to prevent style conflicts
54
+ const [uncontrolledActiveIndex, _setUncontrolledActiveIndex] = useState(activeIndexProp !== null && activeIndexProp !== void 0 ? activeIndexProp : null);
39
55
  const isActiveIndexControlled = activeIndexProp !== undefined;
40
- const mergedActiveIndex = isActiveIndexControlled ? activeIndexProp : uncontrolledActiveIndex;
56
+ const mergedActiveIndex = isActiveIndexControlled
57
+ ? activeIndexProp
58
+ : uncontrolledActiveIndex;
41
59
  const containerRef = useRef(null);
42
60
  const ariaActivedescendant = useMemo(() => {
43
61
  if (mergedActiveIndex !== null && mergedActiveIndex >= 0) {
@@ -57,6 +75,11 @@ function Dropdown(props) {
57
75
  }), []);
58
76
  const followText = useMemo(() => {
59
77
  var _a, _b, _c, _d;
78
+ // If followText is explicitly provided, use it
79
+ if (followTextProp !== undefined) {
80
+ return followTextProp != null ? String(followTextProp) : undefined;
81
+ }
82
+ // Otherwise, auto-extract from children props
60
83
  if (children.type === Button) {
61
84
  return undefined;
62
85
  }
@@ -65,16 +88,35 @@ function Dropdown(props) {
65
88
  }
66
89
  // Try to get value from Input component props
67
90
  const inputValue = (_d = (_b = (_a = children.props) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : (_c = children.props) === null || _c === void 0 ? void 0 : _c.defaultValue) !== null && _d !== void 0 ? _d : '';
68
- return inputValue;
69
- }, [children, isMatchInputValue]);
91
+ // Ensure the value is a string or undefined
92
+ return inputValue != null ? String(inputValue) : undefined;
93
+ }, [children, isMatchInputValue, followTextProp]);
70
94
  const popoverPlacement = useMemo(() => {
71
95
  if (inputPosition === 'outside') {
72
96
  return placement;
73
97
  }
74
98
  return 'bottom';
75
99
  }, [inputPosition, placement]);
100
+ const customWidthMiddleware = useMemo(() => {
101
+ if (!customWidth) {
102
+ return null;
103
+ }
104
+ const widthValue = typeof customWidth === 'number'
105
+ ? `${customWidth}px`
106
+ : customWidth;
107
+ return {
108
+ name: 'customWidth',
109
+ fn: ({ elements }) => {
110
+ Object.assign(elements.floating.style, {
111
+ width: widthValue,
112
+ });
113
+ return {};
114
+ },
115
+ };
116
+ }, [customWidth]);
76
117
  const sameWidthMiddleware = useMemo(() => {
77
- if (!sameWidth) {
118
+ // If customWidth is set, don't apply sameWidth
119
+ if (customWidth || !sameWidth) {
78
120
  return null;
79
121
  }
80
122
  return size({
@@ -84,10 +126,26 @@ function Dropdown(props) {
84
126
  });
85
127
  },
86
128
  });
87
- }, [sameWidth]);
129
+ }, [customWidth, sameWidth]);
88
130
  const offsetMiddleware = useMemo(() => {
89
131
  return offset({ mainAxis: 4 });
90
132
  }, []);
133
+ // Set z-index for popper to ensure it appears above other elements
134
+ const zIndexMiddleware = useMemo(() => {
135
+ const zIndexValue = zIndex !== null && zIndex !== void 0 ? zIndex : 1;
136
+ return {
137
+ name: 'zIndex',
138
+ fn: ({ elements }) => {
139
+ const zIndexNum = typeof zIndexValue === 'number'
140
+ ? zIndexValue
141
+ : (typeof zIndexValue === 'string' ? parseInt(zIndexValue, 10) || zIndexValue : 1);
142
+ Object.assign(elements.floating.style, {
143
+ zIndex: zIndexNum,
144
+ });
145
+ return {};
146
+ },
147
+ };
148
+ }, [zIndex]);
91
149
  const prevIsOpenRef = useRef(isOpen);
92
150
  const translateFrom = useMemo(() => {
93
151
  if (isInline) {
@@ -96,6 +154,15 @@ function Dropdown(props) {
96
154
  const placementBase = popoverPlacement.split('-')[0];
97
155
  return placementBase === 'top' ? 'top' : 'bottom';
98
156
  }, [isInline, popoverPlacement]);
157
+ const setOpen = useCallback((next) => {
158
+ const nextValue = typeof next === 'function'
159
+ ? next(isOpen)
160
+ : next;
161
+ if (!isOpenControlled) {
162
+ setUncontrolledOpen(nextValue);
163
+ }
164
+ onVisibilityChange === null || onVisibilityChange === void 0 ? void 0 : onVisibilityChange(nextValue);
165
+ }, [isOpen, isOpenControlled, onVisibilityChange]);
99
166
  useEffect(() => {
100
167
  if (prevIsOpenRef.current === isOpen)
101
168
  return;
@@ -109,6 +176,29 @@ function Dropdown(props) {
109
176
  }, [isOpen, onClose, onOpen]);
110
177
  const anchorRef = useRef(null);
111
178
  const popperRef = useRef(null);
179
+ const popperControllerRef = useRef(null);
180
+ // Auto-update popper position when anchor element size changes
181
+ useEffect(() => {
182
+ if (!isOpen || isInline || !anchorRef.current || !popperControllerRef.current) {
183
+ return;
184
+ }
185
+ const update = popperControllerRef.current.update;
186
+ if (!update) {
187
+ return;
188
+ }
189
+ // Get floating element from controller refs
190
+ // Check refs exists before accessing nested properties
191
+ const refs = popperControllerRef.current.refs;
192
+ if (!refs) {
193
+ return;
194
+ }
195
+ const floatingElement = refs.floating.current;
196
+ if (!floatingElement) {
197
+ return;
198
+ }
199
+ const cleanup = autoUpdate(anchorRef.current, floatingElement, update);
200
+ return cleanup;
201
+ }, [isOpen, isInline]);
112
202
  // Extract combobox props logic to avoid duplication
113
203
  const getComboboxProps = useMemo(() => {
114
204
  const childWithRef = children;
@@ -125,11 +215,8 @@ function Dropdown(props) {
125
215
  };
126
216
  }, [children, listboxId, isOpen, isMatchInputValue, ariaActivedescendant]);
127
217
  const handleItemHover = useCallback((index) => {
128
- if (!isActiveIndexControlled) {
129
- setUncontrolledActiveIndex(index);
130
- }
131
218
  onItemHover === null || onItemHover === void 0 ? void 0 : onItemHover(index);
132
- }, [isActiveIndexControlled, onItemHover]);
219
+ }, [onItemHover]);
133
220
  // Extract shared DropdownItem props to avoid duplication
134
221
  const baseDropdownItemProps = useMemo(() => ({
135
222
  actionConfig,
@@ -144,13 +231,35 @@ function Dropdown(props) {
144
231
  onSelect,
145
232
  options,
146
233
  type,
147
- }), [actionConfig, mergedActiveIndex, disabled, followText, listboxId, listboxLabel, maxHeight, sameWidth, handleItemHover, onSelect, options, type]);
234
+ status,
235
+ loadingText,
236
+ emptyText,
237
+ emptyIcon,
238
+ mode,
239
+ value,
240
+ }), [
241
+ actionConfig,
242
+ mergedActiveIndex,
243
+ disabled,
244
+ followText,
245
+ listboxId,
246
+ listboxLabel,
247
+ maxHeight,
248
+ sameWidth,
249
+ handleItemHover,
250
+ onSelect,
251
+ options,
252
+ type,
253
+ status,
254
+ loadingText,
255
+ emptyText,
256
+ emptyIcon,
257
+ mode,
258
+ value,
259
+ ]);
148
260
  const triggerElement = useMemo(() => {
149
261
  const childWithRef = children;
150
- const composedRef = composeRefs([
151
- anchorRef,
152
- childWithRef.ref,
153
- ]);
262
+ const composedRef = composeRefs([anchorRef, childWithRef.ref]);
154
263
  return cloneElement(childWithRef, {
155
264
  ref: composedRef,
156
265
  ...getComboboxProps,
@@ -160,19 +269,17 @@ function Dropdown(props) {
160
269
  if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
161
270
  return;
162
271
  if (!isInline) {
163
- setIsOpen((prev) => !prev);
272
+ setOpen((prev) => !prev);
164
273
  }
165
274
  },
166
275
  });
167
- }, [children, isInline, getComboboxProps]);
276
+ }, [children, getComboboxProps, isInline, setOpen]);
168
277
  const inlineTriggerElement = useMemo(() => {
169
278
  if (!isInline) {
170
279
  return null;
171
280
  }
172
281
  const childWithRef = children;
173
- const composedRef = composeRefs([
174
- childWithRef.ref,
175
- ]);
282
+ const composedRef = composeRefs([childWithRef.ref]);
176
283
  return cloneElement(childWithRef, {
177
284
  ref: composedRef,
178
285
  ...getComboboxProps,
@@ -181,29 +288,36 @@ function Dropdown(props) {
181
288
  (_b = (_a = childWithRef.props) === null || _a === void 0 ? void 0 : _a.onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, event);
182
289
  if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
183
290
  return;
291
+ // When open is controlled, don't automatically close on blur
292
+ // Let the controlled state handle the visibility
293
+ if (isOpenControlled) {
294
+ return;
295
+ }
184
296
  const nextFocusTarget = event === null || event === void 0 ? void 0 : event.relatedTarget;
185
297
  const container = containerRef.current;
186
- if (container && nextFocusTarget && container.contains(nextFocusTarget)) {
298
+ if (container &&
299
+ nextFocusTarget &&
300
+ container.contains(nextFocusTarget)) {
187
301
  return;
188
302
  }
189
- setIsOpen(false);
303
+ setOpen(false);
190
304
  },
191
305
  onClick: (event) => {
192
306
  var _a, _b;
193
307
  (_b = (_a = childWithRef.props) === null || _a === void 0 ? void 0 : _a.onClick) === null || _b === void 0 ? void 0 : _b.call(_a, event);
194
308
  if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
195
309
  return;
196
- setIsOpen(true);
310
+ setOpen(true);
197
311
  },
198
312
  onFocus: (event) => {
199
313
  var _a, _b;
200
314
  (_b = (_a = childWithRef.props) === null || _a === void 0 ? void 0 : _a.onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, event);
201
315
  if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
202
316
  return;
203
- setIsOpen(true);
317
+ setOpen(true);
204
318
  },
205
319
  });
206
- }, [children, isInline, isOpen, getComboboxProps]);
320
+ }, [children, getComboboxProps, isInline, setOpen, isOpenControlled]);
207
321
  useDocumentEvents(() => {
208
322
  if (!isOpen) {
209
323
  return;
@@ -217,25 +331,30 @@ function Dropdown(props) {
217
331
  return;
218
332
  if (isInline) {
219
333
  if (container && !container.contains(target)) {
220
- setIsOpen(false);
334
+ setOpen(false);
221
335
  }
222
336
  return;
223
337
  }
224
- if (anchor && popper && !anchor.contains(target) && !popper.contains(target)) {
225
- setIsOpen(false);
338
+ if (anchor &&
339
+ popper &&
340
+ !anchor.contains(target) &&
341
+ !popper.contains(target)) {
342
+ setOpen(false);
226
343
  }
227
344
  };
228
345
  return {
229
346
  click: handleClickAway,
230
347
  touchend: handleClickAway,
231
348
  };
232
- }, [isInline, isOpen]);
349
+ }, [isInline, isOpen, setOpen]);
233
350
  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 },
234
351
  jsx("div", { children: inlineTriggerElement }))), isOpen && (createElement(Translate, { ...translateProps, from: translateFrom, key: "inline-list", in: true },
235
- jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, open: isOpen, disablePortal: true, options: {
352
+ jsx("div", { children: jsx(DropdownItem, { ...baseDropdownItemProps, headerContent: inlineTriggerElement }) })))] })), !isInline && (jsx(Popper, { ref: popperRef, anchor: anchorRef, controllerRef: popperControllerRef, open: isOpen, disablePortal: true, options: {
236
353
  placement: popoverPlacement,
237
354
  middleware: [
238
355
  offsetMiddleware,
356
+ zIndexMiddleware,
357
+ ...(customWidthMiddleware ? [customWidthMiddleware] : []),
239
358
  ...(sameWidthMiddleware ? [sameWidthMiddleware] : []),
240
359
  ],
241
360
  }, children: jsx(TransitionGroup, { component: null, children: isOpen && (createElement(Translate, { ...translateProps, from: translateFrom, key: "popper-list", in: true },
@@ -1,4 +1,4 @@
1
- import { ButtonProps } from "../Button";
1
+ import { ButtonProps } from '../Button';
2
2
  export interface DropdownActionProps {
3
3
  /**
4
4
  * The text of the custom action button.
@@ -17,10 +17,7 @@ function DropdownAction(props) {
17
17
  const isDefaultMode = !isClearMode && !isCustomMode;
18
18
  const hasCancel = Boolean(onCancel && isDefaultMode);
19
19
  const hasConfirm = Boolean(onConfirm && isDefaultMode);
20
- return (jsx(Fragment, { children: showActions && hasAnyEvent && (jsxs("div", { className: dropdownClasses.action, children: [showTopBar && jsx("i", { className: dropdownClasses.actionTopBar }), jsxs("div", { className: dropdownClasses.actionTools, children: [hasCancel && (jsx(Button, { variant: "base-ghost", size: actionButtonSize, onClick: onCancel, children: cancelLabel })), hasConfirm && (jsx(Button, { size: actionButtonSize, style: hasCancel ? undefined : { marginLeft: 'auto' }, onClick: onConfirm, children: confirmLabel })), isCustomMode && (jsx(Button, { size: actionButtonSize, variant: "base-ghost", ...customActionButtonProps, onClick: onClick, children: actionLabel })), isClearMode && (jsx(Button, { size: actionButtonSize, variant: "base-ghost", icon: {
21
- position: 'leading',
22
- src: CloseIcon,
23
- }, onClick: onClear, children: clearLabel }))] })] })) }));
20
+ return (jsx(Fragment, { children: showActions && hasAnyEvent && (jsxs("div", { className: dropdownClasses.action, children: [showTopBar && jsx("i", { className: dropdownClasses.actionTopBar }), jsxs("div", { className: dropdownClasses.actionTools, children: [hasCancel && (jsx(Button, { variant: "base-ghost", size: actionButtonSize, onClick: onCancel, children: cancelLabel })), hasConfirm && (jsx(Button, { size: actionButtonSize, style: hasCancel ? undefined : { marginLeft: 'auto' }, onClick: onConfirm, children: confirmLabel })), isCustomMode && (jsx(Button, { size: actionButtonSize, variant: "base-ghost", ...customActionButtonProps, onClick: onClick, children: actionLabel })), isClearMode && (jsx(Button, { size: actionButtonSize, variant: "base-ghost", iconType: "leading", icon: CloseIcon, onClick: onClear, children: clearLabel }))] })] })) }));
24
21
  }
25
22
 
26
23
  export { DropdownAction as default };
@@ -1,7 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
- import { DropdownItemSharedProps, DropdownOptionsByType, dropdownType } from '@mezzanine-ui/core/dropdown/dropdown';
2
+ import { DropdownItemSharedProps, DropdownOptionsByType, DropdownStatus as DropdownStatusType, DropdownType } from '@mezzanine-ui/core/dropdown/dropdown';
3
+ import { type IconDefinition } from '@mezzanine-ui/icons';
3
4
  import { type DropdownActionProps } from './DropdownAction';
4
- export interface DropdownItemProps<T extends dropdownType | undefined = dropdownType> extends Omit<DropdownItemSharedProps, 'type'> {
5
+ export interface DropdownItemProps<T extends DropdownType | undefined = DropdownType> extends Omit<DropdownItemSharedProps, 'type'> {
5
6
  /**
6
7
  * The action configuration for the dropdown.
7
8
  */
@@ -55,6 +56,22 @@ export interface DropdownItemProps<T extends dropdownType | undefined = dropdown
55
56
  * - 'grouped': array with one level of children (children cannot have children)
56
57
  * - 'tree': array with nested children up to 3 levels
57
58
  */
58
- type?: dropdownType;
59
+ type?: DropdownType;
60
+ /**
61
+ * The status of the dropdown (loading or empty).
62
+ */
63
+ status?: DropdownStatusType;
64
+ /**
65
+ * The text of the dropdown loading status.
66
+ */
67
+ loadingText?: string;
68
+ /**
69
+ * The text of the dropdown empty status.
70
+ */
71
+ emptyText?: string;
72
+ /**
73
+ * The icon of the dropdown empty status.
74
+ */
75
+ emptyIcon?: IconDefinition;
59
76
  }
60
- export default function DropdownItem<T extends dropdownType | undefined = dropdownType>(props: DropdownItemProps<T>): import("react/jsx-runtime").JSX.Element;
77
+ export default function DropdownItem<T extends DropdownType | undefined = DropdownType>(props: DropdownItemProps<T>): import("react/jsx-runtime").JSX.Element;
@@ -8,6 +8,8 @@ import { useElementHeight } from '../hooks/useElementHeight.js';
8
8
  import Typography from '../Typography/Typography.js';
9
9
  import DropdownAction from './DropdownAction.js';
10
10
  import DropdownItemCard from './DropdownItemCard.js';
11
+ import DropdownStatus from './DropdownStatus.js';
12
+ import { shortcutTextHandler } from './shortcutTextHandler.js';
11
13
 
12
14
  /**
13
15
  * Limits DropdownOption array to a maximum depth, truncating extra children levels and showing error message if exceeded.
@@ -55,7 +57,7 @@ function truncateArrayDepth(input, maxDepth = 3, warn = true) {
55
57
  return truncate(input);
56
58
  }
57
59
  function DropdownItem(props) {
58
- const { activeIndex, disabled = false, listboxId, listboxLabel, mode = 'single', options, value, type, maxHeight, actionConfig, onHover, onSelect, followText, headerContent, } = props;
60
+ const { activeIndex, disabled = false, listboxId, listboxLabel, mode = 'single', options, value, type, maxHeight, actionConfig, onHover, onSelect, followText, headerContent, status, loadingText, emptyText, emptyIcon, } = props;
59
61
  const optionsContent = truncateArrayDepth(options, 3);
60
62
  const listRef = useRef(null);
61
63
  const [expandedNodes, setExpandedNodes] = useState(new Set());
@@ -176,18 +178,21 @@ function DropdownItem(props) {
176
178
  if (hasChildren) {
177
179
  groupElements.push(jsx(Typography, { variant: "body", className: dropdownClasses.groupLabel, children: groupOption.name }, groupOption.id));
178
180
  (_a = groupOption.children) === null || _a === void 0 ? void 0 : _a.forEach((option) => {
179
- var _a, _b;
181
+ var _a, _b, _c;
180
182
  currentIndex += 1;
181
183
  const optionIndex = currentIndex;
182
184
  const isActive = optionIndex === activeIndex;
183
185
  const isSelected = Array.isArray(value)
184
186
  ? value.includes(option.id)
185
187
  : value === option.id;
188
+ const shortcutText = option.shortcutText
189
+ ? option.shortcutText
190
+ : shortcutTextHandler((_a = option.shortcutKeys) !== null && _a !== void 0 ? _a : []);
186
191
  groupElements.push(jsx(DropdownItemCard, { followText: followText, active: isActive, checked: isSelected, disabled: disabled, id: `${listboxId}-option-${optionIndex}`, label: option.name, mode: mode, name: option.name, onClick: () => {
187
192
  if (disabled)
188
193
  return;
189
194
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
190
- }, checkSite: "none", validate: (_a = option.validate) !== null && _a !== void 0 ? _a : 'default', onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), showUnderline: (_b = option.showUnderline) !== null && _b !== void 0 ? _b : false, appendContent: option.shortcutText }, option.id));
195
+ }, checkSite: "none", validate: (_b = option.validate) !== null && _b !== void 0 ? _b : 'default', onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), showUnderline: (_c = option.showUnderline) !== null && _c !== void 0 ? _c : false, appendContent: shortcutText }, option.id));
191
196
  });
192
197
  }
193
198
  return groupElements;
@@ -197,7 +202,7 @@ function DropdownItem(props) {
197
202
  const renderTreeOptions = (optionList, depth, startIndex) => {
198
203
  let currentIndex = startIndex;
199
204
  const elements = (optionList !== null && optionList !== void 0 ? optionList : []).flatMap((option) => {
200
- var _a, _b;
205
+ var _a, _b, _c;
201
206
  currentIndex += 1;
202
207
  const optionIndex = currentIndex;
203
208
  const level = Math.min(depth, 2);
@@ -211,7 +216,10 @@ function DropdownItem(props) {
211
216
  if (hasChildren && level !== 2) {
212
217
  prependIcon = isExpanded ? CaretDownIcon : CaretRightIcon;
213
218
  }
214
- const checkSite = option.showCheckbox ? 'prepend' : 'none';
219
+ const checkSite = option.showCheckbox ? 'prefix' : 'none';
220
+ const shortcutText = option.shortcutText
221
+ ? option.shortcutText
222
+ : shortcutTextHandler((_a = option.shortcutKeys) !== null && _a !== void 0 ? _a : []);
215
223
  const card = (jsx(DropdownItemCard, { active: isActive, checked: isSelected, disabled: disabled, id: `${listboxId}-option-${optionIndex}`, label: option.name, level: level, mode: mode, name: option.name, onClick: () => {
216
224
  if (disabled)
217
225
  return;
@@ -221,7 +229,7 @@ function DropdownItem(props) {
221
229
  else {
222
230
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
223
231
  }
224
- }, followText: followText, checkSite: checkSite, onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), prependIcon: prependIcon, showUnderline: (_a = option.showUnderline) !== null && _a !== void 0 ? _a : false, validate: (_b = option.validate) !== null && _b !== void 0 ? _b : 'default', appendContent: option.shortcutText }, option.id));
232
+ }, followText: followText, checkSite: checkSite, onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), prependIcon: prependIcon, showUnderline: (_b = option.showUnderline) !== null && _b !== void 0 ? _b : false, validate: (_c = option.validate) !== null && _c !== void 0 ? _c : 'default', appendContent: shortcutText }, option.id));
225
233
  if (hasChildren && isExpanded && type === 'tree') {
226
234
  const childResult = renderTreeOptions(option.children, depth + 1, currentIndex);
227
235
  currentIndex = childResult.nextIndex;
@@ -234,13 +242,16 @@ function DropdownItem(props) {
234
242
  const renderDefaultOptions = (optionList, startIndex) => {
235
243
  let currentIndex = startIndex;
236
244
  const elements = (optionList !== null && optionList !== void 0 ? optionList : []).flatMap((option) => {
237
- var _a, _b;
245
+ var _a, _b, _c;
238
246
  currentIndex += 1;
239
247
  const optionIndex = currentIndex;
240
248
  const isSelected = Array.isArray(value)
241
249
  ? value.includes(option.id)
242
250
  : value === option.id;
243
251
  const isActive = optionIndex === activeIndex;
252
+ const shortcutText = option.shortcutText
253
+ ? option.shortcutText
254
+ : shortcutTextHandler((_a = option.shortcutKeys) !== null && _a !== void 0 ? _a : []);
244
255
  let checkSite = 'none';
245
256
  if (option === null || option === void 0 ? void 0 : option.checkSite) {
246
257
  checkSite = option.checkSite;
@@ -249,7 +260,7 @@ function DropdownItem(props) {
249
260
  if (disabled)
250
261
  return;
251
262
  onSelect === null || onSelect === void 0 ? void 0 : onSelect(option);
252
- }, onMouseEnter: () => onHover === null || onHover === void 0 ? void 0 : onHover(optionIndex), prependIcon: option.icon, validate: (_a = option.validate) !== null && _a !== void 0 ? _a : 'default', showUnderline: (_b = option.showUnderline) !== null && _b !== void 0 ? _b : false, checkSite: checkSite, appendContent: option.shortcutText }, option.id));
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));
253
264
  });
254
265
  return { elements, nextIndex: currentIndex };
255
266
  };
@@ -263,6 +274,8 @@ function DropdownItem(props) {
263
274
  return renderDefaultOptions(optionList, startIndex);
264
275
  };
265
276
  const { elements: renderedOptions } = renderOptions(optionsContent, 0, -1);
277
+ // Show status when options are empty and status is provided
278
+ const shouldShowStatus = optionsContent.length === 0 && status;
266
279
  const listStyle = useMemo(() => {
267
280
  if (!maxHeight) {
268
281
  return undefined;
@@ -311,8 +324,8 @@ function DropdownItem(props) {
311
324
  };
312
325
  }, [disabled, matchShortcut, onSelect, type, toggleExpand, visibleShortcutOptions]);
313
326
  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
314
- ? (jsx("div", { className: dropdownClasses.listWrapper, style: listWrapperStyle, children: renderedOptions }))
315
- : renderedOptions, hasActions && (jsx("div", { ref: actionRef, children: jsx(DropdownAction, { ...actionConfig }) }))] }));
327
+ ? (jsx("div", { className: dropdownClasses.listWrapper, style: listWrapperStyle, children: shouldShowStatus ? (jsx(DropdownStatus, { status: status, loadingText: loadingText, emptyText: emptyText, emptyIcon: emptyIcon })) : (renderedOptions) }))
328
+ : shouldShowStatus ? (jsx(DropdownStatus, { status: status, loadingText: loadingText, emptyText: emptyText, emptyIcon: emptyIcon })) : (renderedOptions), hasActions && (jsx("div", { ref: actionRef, children: jsx(DropdownAction, { ...actionConfig }) }))] }));
316
329
  }
317
330
 
318
331
  export { DropdownItem as default };
@@ -1,4 +1,4 @@
1
- import { dropdownCheckPosition, dropdownItemLevel, dropdownItemValidate, dropdownMode } from "@mezzanine-ui/core/dropdown/dropdown";
1
+ import { DropdownCheckPosition, DropdownItemLevel, DropdownItemValidate, DropdownMode } from "@mezzanine-ui/core/dropdown/dropdown";
2
2
  import { type IconDefinition } from "@mezzanine-ui/icons";
3
3
  export interface DropdownItemCardProps {
4
4
  /**
@@ -18,7 +18,7 @@ export interface DropdownItemCardProps {
18
18
  /**
19
19
  * The position of the checkbox.
20
20
  */
21
- checkSite?: dropdownCheckPosition;
21
+ checkSite?: DropdownCheckPosition;
22
22
  /**
23
23
  * Controlled: Whether the option is selected/checked.
24
24
  * Controls checkbox state in multiple mode.
@@ -53,11 +53,11 @@ export interface DropdownItemCardProps {
53
53
  /**
54
54
  * The level of the dropdown item card.
55
55
  */
56
- level?: dropdownItemLevel;
56
+ level?: DropdownItemLevel;
57
57
  /**
58
58
  * The mode of the dropdown item card.
59
59
  */
60
- mode: dropdownMode;
60
+ mode: DropdownMode;
61
61
  /**
62
62
  * The accessible name / label for the option.
63
63
  * Falls back to label if not provided.
@@ -91,6 +91,6 @@ export interface DropdownItemCardProps {
91
91
  /**
92
92
  * The validation of the dropdown item card.
93
93
  */
94
- validate?: dropdownItemValidate;
94
+ validate?: DropdownItemValidate;
95
95
  }
96
96
  export default function DropdownItemCard(props: DropdownItemCardProps): import("react/jsx-runtime").JSX.Element;