@mezzanine-ui/react 1.0.0-rc.5 → 1.0.0-rc.7

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 (213) hide show
  1. package/Accordion/Accordion.d.ts +36 -0
  2. package/Accordion/Accordion.js +37 -1
  3. package/AlertBanner/AlertBanner.d.ts +30 -1
  4. package/AlertBanner/AlertBanner.js +56 -12
  5. package/AutoComplete/AutoComplete.d.ts +75 -11
  6. package/AutoComplete/AutoComplete.js +145 -25
  7. package/AutoComplete/AutoCompleteInside.d.ts +54 -0
  8. package/AutoComplete/AutoCompleteInside.js +17 -0
  9. package/AutoComplete/useAutoCompleteKeyboard.d.ts +2 -1
  10. package/AutoComplete/useAutoCompleteKeyboard.js +4 -1
  11. package/Backdrop/Backdrop.d.ts +33 -1
  12. package/Backdrop/Backdrop.js +33 -1
  13. package/Badge/Badge.d.ts +26 -1
  14. package/Badge/Badge.js +26 -1
  15. package/Badge/typings.d.ts +15 -0
  16. package/Breadcrumb/BreadcrumbDropdown.d.ts +1 -1
  17. package/Breadcrumb/BreadcrumbOverflowMenuDropdown.d.ts +1 -1
  18. package/Breadcrumb/typings.d.ts +1 -1
  19. package/Button/Button.d.ts +26 -1
  20. package/Button/Button.js +33 -5
  21. package/COMPONENTS.md +267 -0
  22. package/Calendar/useCalendarControls.d.ts +18 -0
  23. package/Calendar/useCalendarControls.js +18 -0
  24. package/Card/FourThumbnailCard.js +1 -1
  25. package/Card/SingleThumbnailCard.js +1 -1
  26. package/Checkbox/Checkbox.d.ts +30 -1
  27. package/Checkbox/Checkbox.js +54 -4
  28. package/Checkbox/CheckboxGroup.d.ts +39 -1
  29. package/Checkbox/CheckboxGroup.js +49 -3
  30. package/Cropper/Cropper.d.ts +2 -2
  31. package/Cropper/Cropper.js +2 -3
  32. package/Cropper/CropperElement.js +1 -1
  33. package/Cropper/utils/cropper-calculations.d.ts +1 -1
  34. package/Cropper/utils/cropper-calculations.js +16 -14
  35. package/DatePicker/DatePicker.d.ts +36 -2
  36. package/DatePicker/DatePicker.js +36 -2
  37. package/DateRangePicker/useDateRangePickerValue.d.ts +24 -0
  38. package/DateRangePicker/useDateRangePickerValue.js +24 -0
  39. package/Description/Description.d.ts +1 -1
  40. package/Description/Description.js +1 -1
  41. package/Description/DescriptionTitle.d.ts +6 -1
  42. package/Description/DescriptionTitle.js +2 -2
  43. package/Drawer/Drawer.d.ts +78 -33
  44. package/Drawer/Drawer.js +72 -34
  45. package/Dropdown/Dropdown.d.ts +63 -1
  46. package/Dropdown/Dropdown.js +203 -9
  47. package/Dropdown/DropdownItem.d.ts +26 -2
  48. package/Dropdown/DropdownItem.js +91 -43
  49. package/Dropdown/DropdownItemCard.d.ts +3 -2
  50. package/Dropdown/DropdownItemCard.js +8 -5
  51. package/Dropdown/DropdownStatus.d.ts +2 -2
  52. package/Dropdown/DropdownStatus.js +3 -2
  53. package/Dropdown/dropdownKeydownHandler.d.ts +6 -0
  54. package/Dropdown/dropdownKeydownHandler.js +14 -7
  55. package/Empty/icons/EmptyMainInitialDataIcon.js +1 -1
  56. package/Empty/icons/EmptyMainNotificationIcon.js +1 -1
  57. package/Empty/icons/EmptyMainResultIcon.js +1 -1
  58. package/Empty/icons/EmptyMainSystemIcon.js +1 -1
  59. package/FilterArea/Filter.d.ts +25 -2
  60. package/FilterArea/Filter.js +43 -2
  61. package/FilterArea/FilterArea.d.ts +43 -4
  62. package/FilterArea/FilterArea.js +39 -4
  63. package/FilterArea/FilterAreaContext.d.ts +6 -0
  64. package/FilterArea/FilterAreaContext.js +6 -0
  65. package/FilterArea/FilterLine.d.ts +19 -0
  66. package/FilterArea/FilterLine.js +19 -0
  67. package/FilterArea/index.d.ts +2 -2
  68. package/FilterArea/index.js +1 -1
  69. package/Form/FormField.d.ts +43 -2
  70. package/Form/FormField.js +40 -3
  71. package/Form/typings.d.ts +2 -0
  72. package/Form/useAutoCompleteValueControl.d.ts +20 -0
  73. package/Form/useAutoCompleteValueControl.js +20 -0
  74. package/Form/useCheckboxControlValue.d.ts +19 -0
  75. package/Form/useCheckboxControlValue.js +19 -0
  76. package/Form/useControlValueState.d.ts +16 -0
  77. package/Form/useControlValueState.js +16 -0
  78. package/Form/useCustomControlValue.d.ts +17 -0
  79. package/Form/useCustomControlValue.js +17 -0
  80. package/Form/useInputControlValue.d.ts +19 -0
  81. package/Form/useInputControlValue.js +19 -0
  82. package/Form/useInputWithClearControlValue.d.ts +20 -0
  83. package/Form/useInputWithClearControlValue.js +20 -0
  84. package/Form/useRadioControlValue.d.ts +19 -0
  85. package/Form/useRadioControlValue.js +19 -0
  86. package/Form/useSelectValueControl.d.ts +19 -0
  87. package/Form/useSelectValueControl.js +19 -0
  88. package/Form/useSwitchControlValue.d.ts +18 -0
  89. package/Form/useSwitchControlValue.js +18 -0
  90. package/Icon/Icon.d.ts +23 -1
  91. package/Icon/Icon.js +23 -1
  92. package/Input/Input.d.ts +31 -1
  93. package/Input/Input.js +31 -1
  94. package/Input/SpinnerButton/SpinnerButton.js +1 -1
  95. package/Message/Message.d.ts +56 -6
  96. package/Message/Message.js +59 -52
  97. package/Modal/Modal.d.ts +76 -4
  98. package/Modal/Modal.js +117 -6
  99. package/Modal/ModalBodyForVerification.js +3 -1
  100. package/Modal/ModalFooter.d.ts +21 -6
  101. package/Modal/ModalHeader.d.ts +50 -11
  102. package/Modal/ModalHeader.js +1 -1
  103. package/Modal/index.d.ts +1 -1
  104. package/Navigation/Navigation.js +20 -11
  105. package/Navigation/NavigationHeader.js +2 -1
  106. package/Navigation/NavigationIconButton.d.ts +8 -0
  107. package/Navigation/NavigationIconButton.js +2 -2
  108. package/Navigation/NavigationOption.js +6 -3
  109. package/Navigation/NavigationOverflowMenu.js +9 -6
  110. package/Navigation/NavigationUserMenu.js +3 -1
  111. package/Navigation/context.d.ts +1 -0
  112. package/NotificationCenter/NotificationCenter.d.ts +59 -9
  113. package/NotificationCenter/NotificationCenter.js +150 -54
  114. package/NotificationCenter/NotificationCenterDrawer.d.ts +56 -6
  115. package/NotificationCenter/NotificationCenterDrawer.js +6 -8
  116. package/NotificationCenter/index.d.ts +1 -1
  117. package/OverflowTooltip/OverflowTooltip.js +46 -5
  118. package/PageFooter/PageFooter.d.ts +1 -1
  119. package/PageFooter/PageFooter.js +8 -16
  120. package/Pagination/Pagination.d.ts +38 -1
  121. package/Pagination/Pagination.js +38 -1
  122. package/Pagination/PaginationPageSize.js +1 -1
  123. package/Pagination/usePagination.d.ts +20 -0
  124. package/Pagination/usePagination.js +20 -0
  125. package/Progress/Progress.d.ts +24 -1
  126. package/Progress/Progress.js +24 -1
  127. package/README.md +82 -0
  128. package/Radio/Radio.d.ts +35 -1
  129. package/Radio/Radio.js +50 -12
  130. package/Radio/RadioGroup.d.ts +2 -2
  131. package/Radio/RadioGroupContext.d.ts +3 -2
  132. package/Section/SectionGroup.d.ts +20 -0
  133. package/Section/SectionGroup.js +16 -0
  134. package/Section/index.d.ts +2 -0
  135. package/Section/index.js +1 -0
  136. package/Select/Select.d.ts +55 -1
  137. package/Select/Select.js +37 -2
  138. package/Select/typings.d.ts +9 -0
  139. package/{Selection/Selection.d.ts → SelectionCard/SelectionCard.d.ts} +23 -11
  140. package/{Selection/Selection.js → SelectionCard/SelectionCard.js} +14 -14
  141. package/SelectionCard/SelectionCardGroup.d.ts +28 -0
  142. package/SelectionCard/index.d.ts +12 -0
  143. package/SelectionCard/index.js +5 -0
  144. package/Separator/Separator.d.ts +19 -1
  145. package/Separator/Separator.js +19 -1
  146. package/Slider/Slider.d.ts +11 -0
  147. package/Slider/useSlider.d.ts +21 -0
  148. package/Spin/Spin.d.ts +14 -5
  149. package/Spin/Spin.js +6 -8
  150. package/Stepper/Stepper.d.ts +34 -1
  151. package/Stepper/Stepper.js +34 -1
  152. package/Stepper/useStepper.d.ts +18 -0
  153. package/Stepper/useStepper.js +18 -0
  154. package/Tab/Tab.d.ts +33 -1
  155. package/Tab/Tab.js +33 -1
  156. package/Table/Table.d.ts +51 -3
  157. package/Table/Table.js +52 -4
  158. package/Table/hooks/useTableDataSource.d.ts +4 -4
  159. package/Table/hooks/useTableDataSource.js +1 -1
  160. package/Tag/Tag.d.ts +27 -1
  161. package/Tag/Tag.js +27 -1
  162. package/Tag/typings.d.ts +28 -0
  163. package/Textarea/Textarea.d.ts +28 -1
  164. package/Textarea/Textarea.js +25 -1
  165. package/TimePicker/TimePicker.js +1 -1
  166. package/TimeRangePicker/TimeRangePicker.js +1 -1
  167. package/Toggle/Toggle.d.ts +34 -1
  168. package/Toggle/Toggle.js +34 -1
  169. package/Tooltip/Tooltip.d.ts +39 -1
  170. package/Tooltip/Tooltip.js +39 -1
  171. package/Tooltip/useDelayMouseEnterLeave.d.ts +17 -0
  172. package/Tooltip/useDelayMouseEnterLeave.js +17 -0
  173. package/Typography/Typography.d.ts +23 -1
  174. package/Typography/Typography.js +23 -1
  175. package/Upload/Upload.d.ts +13 -7
  176. package/Upload/Upload.js +55 -20
  177. package/Upload/UploadItem.d.ts +3 -3
  178. package/Upload/UploadItem.js +11 -8
  179. package/Upload/UploadPictureCard.d.ts +5 -0
  180. package/Upload/UploadPictureCard.js +10 -6
  181. package/Upload/Uploader.d.ts +32 -31
  182. package/Upload/Uploader.js +10 -9
  183. package/hooks/useClickAway.d.ts +16 -0
  184. package/hooks/useClickAway.js +16 -0
  185. package/hooks/useComposeRefs.d.ts +15 -0
  186. package/hooks/useComposeRefs.js +15 -0
  187. package/hooks/useDocumentEscapeKeyDown.d.ts +15 -0
  188. package/hooks/useDocumentEscapeKeyDown.js +15 -0
  189. package/hooks/useDocumentEvents.d.ts +16 -0
  190. package/hooks/useDocumentEvents.js +16 -0
  191. package/hooks/useDocumentTabKeyDown.d.ts +15 -0
  192. package/hooks/useDocumentTabKeyDown.js +15 -0
  193. package/hooks/useIsomorphicLayoutEffect.d.ts +16 -0
  194. package/hooks/useIsomorphicLayoutEffect.js +16 -0
  195. package/hooks/useLastCallback.d.ts +15 -0
  196. package/hooks/useLastCallback.js +15 -0
  197. package/hooks/useLastValue.d.ts +18 -0
  198. package/hooks/useLastValue.js +18 -0
  199. package/hooks/usePreviousValue.d.ts +14 -0
  200. package/hooks/usePreviousValue.js +14 -0
  201. package/hooks/useScrollLock.d.ts +6 -4
  202. package/hooks/useScrollLock.js +6 -4
  203. package/hooks/useTopStack.d.ts +17 -0
  204. package/hooks/useTopStack.js +17 -0
  205. package/hooks/useWindowWidth.d.ts +14 -0
  206. package/hooks/useWindowWidth.js +14 -0
  207. package/index.d.ts +6 -4
  208. package/index.js +3 -2
  209. package/llms.txt +200 -0
  210. package/package.json +5 -4
  211. package/Selection/SelectionGroup.d.ts +0 -28
  212. package/Selection/index.d.ts +0 -12
  213. package/Selection/index.js +0 -5
package/Drawer/Drawer.js CHANGED
@@ -1,19 +1,61 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { forwardRef, useState, useEffect, useMemo } from 'react';
3
2
  import { drawerClasses } from '@mezzanine-ui/core/drawer';
4
- import { useDocumentEscapeKeyDown } from '../hooks/useDocumentEscapeKeyDown.js';
5
- import useTopStack from '../hooks/useTopStack.js';
6
- import ClearActions from '../ClearActions/ClearActions.js';
3
+ import { DotHorizontalIcon } from '@mezzanine-ui/icons';
4
+ import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
5
+ import { forwardRef, useState, useEffect, useMemo } from 'react';
7
6
  import Button from '../Button/Button.js';
7
+ import ClearActions from '../ClearActions/ClearActions.js';
8
8
  import Radio from '../Radio/Radio.js';
9
9
  import RadioGroup from '../Radio/RadioGroup.js';
10
- import { MOTION_EASING, MOTION_DURATION } from '@mezzanine-ui/system/motion';
10
+ import { useDocumentEscapeKeyDown } from '../hooks/useDocumentEscapeKeyDown.js';
11
+ import useTopStack from '../hooks/useTopStack.js';
12
+ import Dropdown from '../Dropdown/Dropdown.js';
11
13
  import Backdrop from '../Backdrop/Backdrop.js';
12
14
  import Slide from '../Transition/Slide.js';
13
15
  import cx from 'clsx';
14
16
 
17
+ /**
18
+ * 從螢幕右側滑入的抽屜面板元件。
19
+ *
20
+ * 使用 `Backdrop` 作為遮罩層,並以 `Slide` 動畫過渡效果呈現開關狀態。
21
+ * 支援標題列、篩選區域(含分頁 Radio)、底部操作按鈕區域,以及按下 Escape 鍵關閉。
22
+ * 當多個 Drawer 同時開啟時,Escape 鍵只會關閉最上層的 Drawer。
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * import Drawer from '@mezzanine-ui/react/Drawer';
27
+ *
28
+ * // 基本用法
29
+ * <Drawer open={open} onClose={() => setOpen(false)} isHeaderDisplay headerTitle="詳細資料">
30
+ * <p>抽屜內容</p>
31
+ * </Drawer>
32
+ *
33
+ * // 帶有底部操作按鈕
34
+ * <Drawer
35
+ * open={open}
36
+ * onClose={() => setOpen(false)}
37
+ * isHeaderDisplay
38
+ * headerTitle="編輯"
39
+ * isBottomDisplay
40
+ * bottomPrimaryActionText="確認"
41
+ * bottomOnPrimaryActionClick={handleSubmit}
42
+ * bottomSecondaryActionText="取消"
43
+ * bottomOnSecondaryActionClick={() => setOpen(false)}
44
+ * >
45
+ * <form>表單內容</form>
46
+ * </Drawer>
47
+ *
48
+ * // 使用 contentKey 強制重新掛載內容
49
+ * <Drawer open={open} onClose={() => setOpen(false)} contentKey={recordId}>
50
+ * <RecordDetail id={recordId} />
51
+ * </Drawer>
52
+ * ```
53
+ *
54
+ * @see {@link Modal} 對話框元件
55
+ * @see {@link Backdrop} 遮罩層元件
56
+ */
15
57
  const Drawer = forwardRef((props, ref) => {
16
- const { bottomGhostActionDisabled, bottomGhostActionIcon, bottomGhostActionIconType, bottomGhostActionLoading, bottomGhostActionSize, bottomGhostActionText, bottomGhostActionVariant = 'base-ghost', bottomOnGhostActionClick, bottomOnPrimaryActionClick, bottomOnSecondaryActionClick, bottomPrimaryActionDisabled, bottomPrimaryActionIcon, bottomPrimaryActionIconType, bottomPrimaryActionLoading, bottomPrimaryActionSize, bottomPrimaryActionText, bottomPrimaryActionVariant = 'base-primary', bottomSecondaryActionDisabled, bottomSecondaryActionIcon, bottomSecondaryActionIconType, bottomSecondaryActionLoading, bottomSecondaryActionSize, bottomSecondaryActionText, bottomSecondaryActionVariant = 'base-secondary', children, className, container, contentKey, controlBarAllRadioLabel, controlBarCustomButtonLabel = '全部已讀', controlBarDefaultValue, controlBarIsEmpty = false, controlBarOnCustomButtonClick, controlBarOnRadioChange, controlBarReadRadioLabel, controlBarShow = false, controlBarShowUnreadButton = false, controlBarUnreadRadioLabel, controlBarValue, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal, headerTitle, isBottomDisplay, isHeaderDisplay, onBackdropClick, onClose, open, renderControlBar: customRenderControlBar, size = 'medium', ...rest } = props;
58
+ const { bottomGhostActionDisabled, bottomGhostActionIcon, bottomGhostActionIconType, bottomGhostActionLoading, bottomGhostActionSize, bottomGhostActionText, bottomGhostActionVariant = 'base-ghost', bottomOnGhostActionClick, bottomOnPrimaryActionClick, bottomOnSecondaryActionClick, bottomPrimaryActionDisabled, bottomPrimaryActionIcon, bottomPrimaryActionIconType, bottomPrimaryActionLoading, bottomPrimaryActionSize, bottomPrimaryActionText, bottomPrimaryActionVariant = 'base-primary', bottomSecondaryActionDisabled, bottomSecondaryActionIcon, bottomSecondaryActionIconType, bottomSecondaryActionLoading, bottomSecondaryActionSize, bottomSecondaryActionText, bottomSecondaryActionVariant = 'base-secondary', filterAreaOnSelect, filterAreaOptions = [], children, className, container, contentKey, filterAreaAllRadioLabel, filterAreaCustomButtonLabel = '全部已讀', filterAreaDefaultValue, filterAreaIsEmpty = false, filterAreaOnCustomButtonClick, filterAreaOnRadioChange, filterAreaReadRadioLabel, filterAreaShow = false, filterAreaShowUnreadButton = false, filterAreaUnreadRadioLabel, filterAreaValue, disableCloseOnBackdropClick = false, disableCloseOnEscapeKeyDown = false, disablePortal, headerTitle, isBottomDisplay, isHeaderDisplay, onBackdropClick, onClose, open, size = 'medium', ...rest } = props;
17
59
  const [exited, setExited] = useState(true);
18
60
  const [openCount, setOpenCount] = useState(0);
19
61
  // Track open state changes to auto-remount content when drawer reopens
@@ -27,47 +69,43 @@ const Drawer = forwardRef((props, ref) => {
27
69
  * Escape keydown close: escape will only close the top drawer
28
70
  */
29
71
  const checkIsOnTheTop = useTopStack(open);
30
- const renderControlBar = useMemo(() => {
31
- // If custom renderControlBar is provided, use it
32
- if (customRenderControlBar) {
33
- return customRenderControlBar;
34
- }
35
- // Default control bar implementation
36
- if (!controlBarShow) {
72
+ const renderFilterArea = useMemo(() => {
73
+ if (!filterAreaShow) {
37
74
  return undefined;
38
75
  }
39
76
  return () => {
40
77
  const radios = [];
41
- if (controlBarAllRadioLabel) {
42
- radios.push(jsx(Radio, { type: "segment", value: "all", children: controlBarAllRadioLabel }, "all"));
78
+ if (filterAreaAllRadioLabel) {
79
+ radios.push(jsx(Radio, { type: "segment", value: "all", children: filterAreaAllRadioLabel }, "all"));
43
80
  }
44
- if (controlBarReadRadioLabel) {
45
- radios.push(jsx(Radio, { type: "segment", value: "read", children: controlBarReadRadioLabel }, "read"));
81
+ if (filterAreaReadRadioLabel) {
82
+ radios.push(jsx(Radio, { type: "segment", value: "read", children: filterAreaReadRadioLabel }, "read"));
46
83
  }
47
- if (controlBarUnreadRadioLabel && controlBarShowUnreadButton) {
48
- radios.push(jsx(Radio, { type: "segment", value: "unread", children: controlBarUnreadRadioLabel }, "unread"));
84
+ if (filterAreaUnreadRadioLabel && filterAreaShowUnreadButton) {
85
+ radios.push(jsx(Radio, { type: "segment", value: "unread", children: filterAreaUnreadRadioLabel }, "unread"));
49
86
  }
50
87
  const hasRadios = radios.length > 0;
51
- const hasButton = controlBarOnCustomButtonClick !== undefined;
88
+ const hasButton = filterAreaOptions.length > 0 || filterAreaOnCustomButtonClick !== undefined;
52
89
  // Don't render if neither radios nor button are provided
53
90
  if (!hasRadios && !hasButton) {
54
91
  return null;
55
92
  }
56
- return (jsxs("div", { className: cx(drawerClasses.controlBar, !hasRadios && hasButton && drawerClasses.controlBarButtonOnly), children: [hasRadios && (jsx(RadioGroup, { defaultValue: controlBarDefaultValue !== null && controlBarDefaultValue !== void 0 ? controlBarDefaultValue : 'all', onChange: controlBarOnRadioChange, size: "minor", type: "segment", value: controlBarValue, children: radios })), hasButton && (jsx(Button, { disabled: controlBarIsEmpty, onClick: controlBarOnCustomButtonClick, size: "minor", type: "button", variant: "base-ghost", children: controlBarCustomButtonLabel }))] }));
93
+ return (jsxs("div", { className: cx(drawerClasses.filterArea, !hasRadios && hasButton && drawerClasses.filterAreaButtonOnly), children: [hasRadios && (jsx(RadioGroup, { defaultValue: filterAreaDefaultValue !== null && filterAreaDefaultValue !== void 0 ? filterAreaDefaultValue : 'all', onChange: filterAreaOnRadioChange, size: "minor", type: "segment", value: filterAreaValue, children: radios })), hasButton && (filterAreaOptions.length > 0 ? (jsx(Dropdown, { onSelect: filterAreaOnSelect, options: filterAreaOptions, placement: "bottom-end", children: jsx(Button, { icon: DotHorizontalIcon, iconType: "icon-only", size: "minor", type: "button", variant: "base-ghost" }) })) : (jsx(Button, { disabled: filterAreaIsEmpty, onClick: filterAreaOnCustomButtonClick, size: "minor", type: "button", variant: "base-ghost", children: filterAreaCustomButtonLabel })))] }));
57
94
  };
58
95
  }, [
59
- controlBarAllRadioLabel,
60
- controlBarCustomButtonLabel,
61
- controlBarDefaultValue,
62
- controlBarIsEmpty,
63
- controlBarOnCustomButtonClick,
64
- controlBarOnRadioChange,
65
- controlBarReadRadioLabel,
66
- controlBarShow,
67
- controlBarShowUnreadButton,
68
- controlBarUnreadRadioLabel,
69
- controlBarValue,
70
- customRenderControlBar,
96
+ filterAreaAllRadioLabel,
97
+ filterAreaCustomButtonLabel,
98
+ filterAreaDefaultValue,
99
+ filterAreaIsEmpty,
100
+ filterAreaOnCustomButtonClick,
101
+ filterAreaOnRadioChange,
102
+ filterAreaReadRadioLabel,
103
+ filterAreaShow,
104
+ filterAreaShowUnreadButton,
105
+ filterAreaUnreadRadioLabel,
106
+ filterAreaValue,
107
+ filterAreaOnSelect,
108
+ filterAreaOptions,
71
109
  ]);
72
110
  useDocumentEscapeKeyDown(() => {
73
111
  if (!open || disableCloseOnEscapeKeyDown || !onClose) {
@@ -89,7 +127,7 @@ const Drawer = forwardRef((props, ref) => {
89
127
  }, easing: {
90
128
  enter: MOTION_EASING.entrance,
91
129
  exit: MOTION_EASING.exit,
92
- }, in: open, onEntered: () => setExited(false), onExited: () => setExited(true), ref: ref, children: jsxs("div", { ...rest, className: cx(drawerClasses.host, drawerClasses.right, drawerClasses.size(size), className), children: [isHeaderDisplay && (jsxs("div", { className: drawerClasses.header, children: [headerTitle, jsx(ClearActions, { onClick: onClose })] })), renderControlBar === null || renderControlBar === void 0 ? void 0 : renderControlBar(), jsx("div", { className: drawerClasses.content, children: children }, contentKey !== undefined ? contentKey : openCount), isBottomDisplay && (jsxs("div", { className: drawerClasses.bottom, children: [jsx("div", { children: bottomGhostActionText && bottomOnGhostActionClick && (jsx(Button, { disabled: bottomGhostActionDisabled, icon: bottomGhostActionIcon, iconType: bottomGhostActionIconType, loading: bottomGhostActionLoading, onClick: bottomOnGhostActionClick, size: bottomGhostActionSize, type: "button", variant: bottomGhostActionVariant, children: bottomGhostActionText })) }), jsxs("div", { className: drawerClasses['bottom__actions'], children: [bottomSecondaryActionText && bottomOnSecondaryActionClick && (jsx(Button, { disabled: bottomSecondaryActionDisabled, icon: bottomSecondaryActionIcon, iconType: bottomSecondaryActionIconType, loading: bottomSecondaryActionLoading, onClick: bottomOnSecondaryActionClick, size: bottomSecondaryActionSize, type: "button", variant: bottomSecondaryActionVariant, children: bottomSecondaryActionText })), bottomPrimaryActionText && bottomOnPrimaryActionClick && (jsx(Button, { disabled: bottomPrimaryActionDisabled, icon: bottomPrimaryActionIcon, iconType: bottomPrimaryActionIconType, loading: bottomPrimaryActionLoading, onClick: bottomOnPrimaryActionClick, size: bottomPrimaryActionSize, type: "button", variant: bottomPrimaryActionVariant, children: bottomPrimaryActionText }))] })] }))] }) }) }));
130
+ }, in: open, onEntered: () => setExited(false), onExited: () => setExited(true), ref: ref, children: jsxs("div", { ...rest, className: cx(drawerClasses.host, drawerClasses.right, drawerClasses.size(size), className), children: [isHeaderDisplay && (jsxs("div", { className: drawerClasses.header, children: [headerTitle, jsx(ClearActions, { onClick: onClose })] })), renderFilterArea === null || renderFilterArea === void 0 ? void 0 : renderFilterArea(), jsx("div", { className: drawerClasses.content, children: children }, contentKey !== undefined ? contentKey : openCount), isBottomDisplay && (jsxs("div", { className: drawerClasses.bottom, children: [jsx("div", { children: bottomGhostActionText && bottomOnGhostActionClick && (jsx(Button, { disabled: bottomGhostActionDisabled, icon: bottomGhostActionIcon, iconType: bottomGhostActionIconType, loading: bottomGhostActionLoading, onClick: bottomOnGhostActionClick, size: bottomGhostActionSize, type: "button", variant: bottomGhostActionVariant, children: bottomGhostActionText })) }), jsxs("div", { className: drawerClasses['bottom__actions'], children: [bottomSecondaryActionText && bottomOnSecondaryActionClick && (jsx(Button, { disabled: bottomSecondaryActionDisabled, icon: bottomSecondaryActionIcon, iconType: bottomSecondaryActionIconType, loading: bottomSecondaryActionLoading, onClick: bottomOnSecondaryActionClick, size: bottomSecondaryActionSize, type: "button", variant: bottomSecondaryActionVariant, children: bottomSecondaryActionText })), bottomPrimaryActionText && bottomOnPrimaryActionClick && (jsx(Button, { disabled: bottomPrimaryActionDisabled, icon: bottomPrimaryActionIcon, iconType: bottomPrimaryActionIconType, loading: bottomPrimaryActionLoading, onClick: bottomOnPrimaryActionClick, size: bottomPrimaryActionSize, type: "button", variant: bottomPrimaryActionVariant, children: bottomPrimaryActionText }))] })] }))] }) }) }));
93
131
  });
94
132
 
95
133
  export { Drawer as default };
@@ -26,9 +26,17 @@ export interface DropdownProps extends DropdownItemSharedProps {
26
26
  */
27
27
  actionText?: string;
28
28
  /**
29
- * The active option index for hover/focus state.
29
+ * The active option index for hover/focus state and Enter selection.
30
+ * Can be set by both keyboard navigation and mouse hover (e.g. in AutoComplete).
30
31
  */
31
32
  activeIndex?: number | null;
33
+ /**
34
+ * The keyboard-only active index.
35
+ * When provided, only this index triggers the focus ring (`--keyboard-active` CSS class).
36
+ * Mouse hover updates `activeIndex` for Enter selection but should not update this.
37
+ * When omitted, falls back to the internal uncontrolled index (set only by built-in keyboard navigation).
38
+ */
39
+ keyboardActiveIndex?: number | null;
32
40
  /**
33
41
  * The children of the dropdown.
34
42
  * This can be a button or an input.
@@ -70,6 +78,13 @@ export interface DropdownProps extends DropdownItemSharedProps {
70
78
  * The max height of the dropdown list.
71
79
  */
72
80
  maxHeight?: number | string;
81
+ /**
82
+ * Override the default `min-width` of the dropdown list.
83
+ * Accepts a number (pixels) or any valid CSS length string.
84
+ * Pass `0` to remove the minimum width constraint entirely — useful when `sameWidth` controls the width.
85
+ * @default spacing token `size-container-tiny`
86
+ */
87
+ minWidth?: number | string;
73
88
  /**
74
89
  * Whether the dropdown is open (controlled).
75
90
  */
@@ -222,4 +237,51 @@ export interface DropdownProps extends DropdownItemSharedProps {
222
237
  */
223
238
  scrollbarOptions?: import('overlayscrollbars').PartialOptions;
224
239
  }
240
+ /**
241
+ * 下拉選單元件,以 `Button` 或 `Input` 作為觸發元素,點擊後展開選項列表。
242
+ *
243
+ * 支援受控(`open`)與非受控兩種開關模式,可透過 `type` 切換單選或多選行為,
244
+ * 並透過 `inputPosition` 選擇彈出(outside)或內嵌(inside)的輸入框位置。
245
+ * 可搭配 `showDropdownActions` 顯示確認/取消操作列,以及 `status` 設定
246
+ * 載入中或空狀態的顯示內容。
247
+ *
248
+ * @example
249
+ * ```tsx
250
+ * import Dropdown from '@mezzanine-ui/react/Dropdown';
251
+ * import Button from '@mezzanine-ui/react/Button';
252
+ * import Input from '@mezzanine-ui/react/Input';
253
+ *
254
+ * // 以 Button 為觸發元素
255
+ * <Dropdown
256
+ * options={[{ label: '選項一', value: '1' }, { label: '選項二', value: '2' }]}
257
+ * onSelect={(option) => console.log(option)}
258
+ * >
259
+ * <Button>請選擇</Button>
260
+ * </Dropdown>
261
+ *
262
+ * // 以 Input 為觸發元素,同寬展開
263
+ * <Dropdown
264
+ * options={[{ label: '台北', value: 'tpe' }, { label: '台中', value: 'txg' }]}
265
+ * sameWidth
266
+ * onSelect={(opt) => setValue(opt.value)}
267
+ * >
268
+ * <Input placeholder="搜尋城市" />
269
+ * </Dropdown>
270
+ *
271
+ * // 多選模式搭配操作列
272
+ * <Dropdown
273
+ * type="multiple"
274
+ * options={options}
275
+ * showDropdownActions
276
+ * actionConfirmText="確認"
277
+ * actionCancelText="取消"
278
+ * onActionConfirm={handleConfirm}
279
+ * >
280
+ * <Button>多選</Button>
281
+ * </Dropdown>
282
+ * ```
283
+ *
284
+ * @see {@link Select} 封裝好選取邏輯的選擇器元件
285
+ * @see {@link AutoComplete} 具備自動補全功能的輸入元件
286
+ */
225
287
  export default function Dropdown(props: DropdownProps): import("react/jsx-runtime").JSX.Element;
@@ -24,8 +24,55 @@ function getElementRef(element) {
24
24
  // React 18: ref is on the element itself
25
25
  return propsRef !== null && propsRef !== void 0 ? propsRef : element.ref;
26
26
  }
27
+ /**
28
+ * 下拉選單元件,以 `Button` 或 `Input` 作為觸發元素,點擊後展開選項列表。
29
+ *
30
+ * 支援受控(`open`)與非受控兩種開關模式,可透過 `type` 切換單選或多選行為,
31
+ * 並透過 `inputPosition` 選擇彈出(outside)或內嵌(inside)的輸入框位置。
32
+ * 可搭配 `showDropdownActions` 顯示確認/取消操作列,以及 `status` 設定
33
+ * 載入中或空狀態的顯示內容。
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * import Dropdown from '@mezzanine-ui/react/Dropdown';
38
+ * import Button from '@mezzanine-ui/react/Button';
39
+ * import Input from '@mezzanine-ui/react/Input';
40
+ *
41
+ * // 以 Button 為觸發元素
42
+ * <Dropdown
43
+ * options={[{ label: '選項一', value: '1' }, { label: '選項二', value: '2' }]}
44
+ * onSelect={(option) => console.log(option)}
45
+ * >
46
+ * <Button>請選擇</Button>
47
+ * </Dropdown>
48
+ *
49
+ * // 以 Input 為觸發元素,同寬展開
50
+ * <Dropdown
51
+ * options={[{ label: '台北', value: 'tpe' }, { label: '台中', value: 'txg' }]}
52
+ * sameWidth
53
+ * onSelect={(opt) => setValue(opt.value)}
54
+ * >
55
+ * <Input placeholder="搜尋城市" />
56
+ * </Dropdown>
57
+ *
58
+ * // 多選模式搭配操作列
59
+ * <Dropdown
60
+ * type="multiple"
61
+ * options={options}
62
+ * showDropdownActions
63
+ * actionConfirmText="確認"
64
+ * actionCancelText="取消"
65
+ * onActionConfirm={handleConfirm}
66
+ * >
67
+ * <Button>多選</Button>
68
+ * </Dropdown>
69
+ * ```
70
+ *
71
+ * @see {@link Select} 封裝好選取邏輯的選擇器元件
72
+ * @see {@link AutoComplete} 具備自動補全功能的輸入元件
73
+ */
27
74
  function Dropdown(props) {
28
- const { activeIndex: activeIndexProp, id, children, options = [], type = 'default', toggleCheckedOnClick, maxHeight, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom-start', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, loadingPosition = 'full', followText: followTextProp, globalPortal = true, onReachBottom, onLeaveBottom, onScroll, mode, value, scrollbarDefer, scrollbarDisabled, scrollbarMaxWidth, scrollbarOptions, } = props;
75
+ const { activeIndex: activeIndexProp, keyboardActiveIndex: keyboardActiveIndexProp, id, children, options = [], type = 'default', toggleCheckedOnClick, maxHeight, minWidth, disabled = false, showDropdownActions = false, actionCancelText, actionConfirmText, actionText, actionClearText, actionCustomButtonProps, showActionShowTopBar, isMatchInputValue = false, inputPosition = 'outside', placement = 'bottom-start', customWidth, sameWidth = false, listboxId: listboxIdProp, listboxLabel, onClose, onOpen, open: openProp, onVisibilityChange, onSelect, onActionConfirm, onActionCancel, onActionCustom, onActionClear, onItemHover, zIndex, status, loadingText, emptyText, emptyIcon, loadingPosition = 'full', followText: followTextProp, globalPortal = true, onReachBottom, onLeaveBottom, onScroll, mode, value, scrollbarDefer, scrollbarDisabled, scrollbarMaxWidth, scrollbarOptions, } = props;
29
76
  const isInline = inputPosition === 'inside';
30
77
  const inputId = useId();
31
78
  const defaultListboxId = `${inputId}-listbox`;
@@ -60,14 +107,49 @@ function Dropdown(props) {
60
107
  const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
61
108
  const isOpenControlled = openProp !== undefined;
62
109
  const isOpen = isOpenControlled ? !!openProp : uncontrolledOpen;
63
- // Keep setter for uncontrolled mode support (e.g., keyboard navigation)
64
- // Currently not used in handleItemHover to prevent style conflicts
65
- const [uncontrolledActiveIndex, _setUncontrolledActiveIndex] = useState(activeIndexProp !== null && activeIndexProp !== void 0 ? activeIndexProp : null);
110
+ const [uncontrolledActiveIndex, setUncontrolledActiveIndex] = useState(activeIndexProp !== null && activeIndexProp !== void 0 ? activeIndexProp : null);
66
111
  const isActiveIndexControlled = activeIndexProp !== undefined;
67
112
  const mergedActiveIndex = isActiveIndexControlled
68
113
  ? activeIndexProp
69
114
  : uncontrolledActiveIndex;
115
+ // For keyboard-only visual focus (focus ring). When not externally controlled,
116
+ // `uncontrolledActiveIndex` is already set only by keyboard (never by hover).
117
+ const mergedKeyboardActiveIndex = keyboardActiveIndexProp !== undefined
118
+ ? keyboardActiveIndexProp
119
+ : uncontrolledActiveIndex;
70
120
  const containerRef = useRef(null);
121
+ // Expansion state for tree type (lifted here so keyboard nav can track visible options)
122
+ const [expandedNodes, setExpandedNodes] = useState(new Set());
123
+ const handleToggleExpand = useCallback((optionId) => {
124
+ setExpandedNodes((prev) => {
125
+ const next = new Set(prev);
126
+ if (next.has(optionId)) {
127
+ next.delete(optionId);
128
+ }
129
+ else {
130
+ next.add(optionId);
131
+ }
132
+ return next;
133
+ });
134
+ }, []);
135
+ // Flat list of navigable options, respecting tree expansion state
136
+ const flatNavigableOptions = useMemo(() => {
137
+ const opts = options;
138
+ if (type === 'grouped') {
139
+ return opts.flatMap((g) => { var _a; return (_a = g.children) !== null && _a !== void 0 ? _a : []; });
140
+ }
141
+ if (type === 'tree') {
142
+ const flatten = (items) => items.flatMap((item) => {
143
+ var _a;
144
+ if (((_a = item.children) === null || _a === void 0 ? void 0 : _a.length) && expandedNodes.has(item.id)) {
145
+ return [item, ...flatten(item.children)];
146
+ }
147
+ return [item];
148
+ });
149
+ return flatten(opts);
150
+ }
151
+ return opts;
152
+ }, [options, type, expandedNodes]);
71
153
  const ariaActivedescendant = useMemo(() => {
72
154
  if (mergedActiveIndex !== null && mergedActiveIndex >= 0) {
73
155
  return `${listboxId}-option-${mergedActiveIndex}`;
@@ -190,10 +272,16 @@ function Dropdown(props) {
190
272
  const popperControllerRef = useRef(null);
191
273
  // Extract combobox props logic to avoid duplication
192
274
  const getComboboxProps = useMemo(() => {
193
- // Only access the type property to check if it's a Button component
194
275
  const isInput = children.type !== Button;
195
- if (!isInput)
196
- return {};
276
+ if (!isInput) {
277
+ // Button trigger: expose listbox ARIA so screen readers can navigate
278
+ return {
279
+ 'aria-haspopup': 'listbox',
280
+ 'aria-expanded': isOpen,
281
+ 'aria-controls': listboxId,
282
+ 'aria-activedescendant': ariaActivedescendant,
283
+ };
284
+ }
197
285
  return {
198
286
  role: 'combobox',
199
287
  'aria-controls': listboxId,
@@ -206,18 +294,99 @@ function Dropdown(props) {
206
294
  const handleItemHover = useCallback((index) => {
207
295
  onItemHover === null || onItemHover === void 0 ? void 0 : onItemHover(index);
208
296
  }, [onItemHover]);
297
+ // Reset active index when dropdown closes (uncontrolled only)
298
+ useEffect(() => {
299
+ if (!isOpen && !isActiveIndexControlled) {
300
+ setUncontrolledActiveIndex(null);
301
+ }
302
+ }, [isOpen, isActiveIndexControlled]);
303
+ // Scroll the active option into view whenever activeIndex changes
304
+ useEffect(() => {
305
+ if (!isOpen || mergedActiveIndex === null)
306
+ return;
307
+ requestAnimationFrame(() => {
308
+ var _a;
309
+ (_a = document
310
+ .getElementById(`${listboxId}-option-${mergedActiveIndex}`)) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'nearest' });
311
+ });
312
+ }, [mergedActiveIndex, isOpen, listboxId]);
313
+ // Built-in keyboard navigation (only when activeIndex is not controlled externally)
314
+ const handleBuiltinKeyDown = useCallback((event) => {
315
+ var _a;
316
+ if (isActiveIndexControlled)
317
+ return;
318
+ const count = flatNavigableOptions.length;
319
+ if (!isOpen) {
320
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
321
+ event.preventDefault();
322
+ setOpen(true);
323
+ }
324
+ return;
325
+ }
326
+ if (count === 0)
327
+ return;
328
+ switch (event.key) {
329
+ case 'ArrowDown': {
330
+ event.preventDefault();
331
+ setUncontrolledActiveIndex((prev) => prev === null ? 0 : (prev + 1) % count);
332
+ break;
333
+ }
334
+ case 'ArrowUp': {
335
+ event.preventDefault();
336
+ setUncontrolledActiveIndex((prev) => prev === null ? count - 1 : (prev - 1 + count) % count);
337
+ break;
338
+ }
339
+ case 'Enter': {
340
+ if (mergedActiveIndex !== null && mergedActiveIndex >= 0) {
341
+ const activeOption = flatNavigableOptions[mergedActiveIndex];
342
+ if (activeOption) {
343
+ event.preventDefault();
344
+ if (type === 'tree' && ((_a = activeOption.children) === null || _a === void 0 ? void 0 : _a.length)) {
345
+ handleToggleExpand(activeOption.id);
346
+ }
347
+ else {
348
+ onSelect === null || onSelect === void 0 ? void 0 : onSelect(activeOption);
349
+ if (mode !== 'multiple') {
350
+ setOpen(false);
351
+ }
352
+ }
353
+ }
354
+ }
355
+ break;
356
+ }
357
+ case 'Escape': {
358
+ event.preventDefault();
359
+ setOpen(false);
360
+ break;
361
+ }
362
+ }
363
+ }, [
364
+ isActiveIndexControlled,
365
+ isOpen,
366
+ flatNavigableOptions,
367
+ mergedActiveIndex,
368
+ type,
369
+ mode,
370
+ handleToggleExpand,
371
+ onSelect,
372
+ setOpen,
373
+ ]);
209
374
  // Extract shared DropdownItem props to avoid duplication
210
375
  const baseDropdownItemProps = useMemo(() => ({
211
376
  actionConfig,
212
377
  activeIndex: mergedActiveIndex,
378
+ keyboardActiveIndex: mergedKeyboardActiveIndex,
213
379
  disabled,
380
+ expandedNodes,
214
381
  followText,
215
382
  listboxId,
216
383
  listboxLabel,
217
384
  maxHeight,
385
+ minWidth,
218
386
  sameWidth,
219
387
  onHover: handleItemHover,
220
388
  onSelect,
389
+ onToggleExpand: handleToggleExpand,
221
390
  onReachBottom,
222
391
  onLeaveBottom,
223
392
  onScroll,
@@ -238,13 +407,17 @@ function Dropdown(props) {
238
407
  }), [
239
408
  actionConfig,
240
409
  mergedActiveIndex,
410
+ mergedKeyboardActiveIndex,
241
411
  disabled,
242
412
  followText,
243
413
  listboxId,
244
414
  listboxLabel,
245
415
  maxHeight,
416
+ minWidth,
246
417
  sameWidth,
247
418
  handleItemHover,
419
+ handleToggleExpand,
420
+ expandedNodes,
248
421
  onSelect,
249
422
  onReachBottom,
250
423
  onLeaveBottom,
@@ -270,6 +443,7 @@ function Dropdown(props) {
270
443
  const childRef = getElementRef(childWithRef);
271
444
  const composedRef = composeRefs([anchorRef, childRef]);
272
445
  const originalOnClick = childProps.onClick;
446
+ const originalOnKeyDown = childProps.onKeyDown;
273
447
  return cloneElement(childWithRef, {
274
448
  ref: composedRef,
275
449
  ...getComboboxProps,
@@ -281,8 +455,14 @@ function Dropdown(props) {
281
455
  setOpen((prev) => !prev);
282
456
  }
283
457
  },
458
+ onKeyDown: (event) => {
459
+ originalOnKeyDown === null || originalOnKeyDown === void 0 ? void 0 : originalOnKeyDown(event);
460
+ if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
461
+ return;
462
+ handleBuiltinKeyDown(event);
463
+ },
284
464
  });
285
- }, [children, getComboboxProps, isInline, setOpen]);
465
+ }, [children, getComboboxProps, handleBuiltinKeyDown, isInline, setOpen]);
286
466
  const inlineTriggerElement = useMemo(() => {
287
467
  if (!isInline) {
288
468
  return null;
@@ -294,6 +474,7 @@ function Dropdown(props) {
294
474
  const originalOnBlur = childProps.onBlur;
295
475
  const originalOnClick = childProps.onClick;
296
476
  const originalOnFocus = childProps.onFocus;
477
+ const originalOnKeyDown = childProps.onKeyDown;
297
478
  return cloneElement(childWithRef, {
298
479
  ref: composedRef,
299
480
  ...getComboboxProps,
@@ -327,8 +508,21 @@ function Dropdown(props) {
327
508
  return;
328
509
  setOpen(true);
329
510
  },
511
+ onKeyDown: (event) => {
512
+ originalOnKeyDown === null || originalOnKeyDown === void 0 ? void 0 : originalOnKeyDown(event);
513
+ if (event === null || event === void 0 ? void 0 : event.defaultPrevented)
514
+ return;
515
+ handleBuiltinKeyDown(event);
516
+ },
330
517
  });
331
- }, [children, getComboboxProps, isInline, isOpenControlled, setOpen]);
518
+ }, [
519
+ children,
520
+ getComboboxProps,
521
+ handleBuiltinKeyDown,
522
+ isInline,
523
+ isOpenControlled,
524
+ setOpen,
525
+ ]);
332
526
  useDocumentEvents(() => {
333
527
  if (!isOpen) {
334
528
  return;
@@ -9,9 +9,26 @@ export interface DropdownItemProps<T extends DropdownType | undefined = Dropdown
9
9
  */
10
10
  actionConfig?: DropdownActionProps;
11
11
  /**
12
- * The active option index for hover/focus state.
12
+ * The active option index for hover/focus state and Enter selection.
13
13
  */
14
14
  activeIndex: number | null;
15
+ /**
16
+ * Keyboard-only active index. When provided, only this index applies the
17
+ * focus ring (`--keyboard-active`) and active background via `DropdownItemCard`'s `active` prop.
18
+ * Mouse hover should update `activeIndex` (for Enter selection) but NOT this value.
19
+ * Falls back to `activeIndex` when not provided (backward-compatible).
20
+ */
21
+ keyboardActiveIndex?: number | null;
22
+ /**
23
+ * Controlled set of expanded node IDs for tree type.
24
+ * When provided, expansion state is managed externally.
25
+ */
26
+ expandedNodes?: Set<string>;
27
+ /**
28
+ * Callback to toggle the expansion of a tree node.
29
+ * Required when `expandedNodes` is provided.
30
+ */
31
+ onToggleExpand?: (id: string) => void;
15
32
  /**
16
33
  * The text to follow.
17
34
  */
@@ -33,6 +50,13 @@ export interface DropdownItemProps<T extends DropdownType | undefined = Dropdown
33
50
  * The max height of the dropdown list.
34
51
  */
35
52
  maxHeight?: number | string;
53
+ /**
54
+ * Override the default `min-width` of the dropdown list.
55
+ * Accepts a number (pixels) or any valid CSS length string.
56
+ * Pass `0` to remove the minimum width constraint entirely.
57
+ * @default spacing token `size-container-tiny`
58
+ */
59
+ minWidth?: number | string;
36
60
  /**
37
61
  * Whether to set the same width as its anchor element.
38
62
  * @default false
@@ -40,7 +64,7 @@ export interface DropdownItemProps<T extends DropdownType | undefined = Dropdown
40
64
  sameWidth?: boolean;
41
65
  /**
42
66
  * Callback when hovering option index changes.
43
- */
67
+ */
44
68
  onHover?: (index: number) => void;
45
69
  /**
46
70
  * Options to render.