@cube-dev/ui-kit 0.81.0 → 0.82.1

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 (523) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/es/_internal/hooks/index.js +1 -1
  3. package/es/_internal/hooks/use-chained-callback.js +1 -1
  4. package/es/_internal/hooks/use-debounced-value.js +1 -1
  5. package/es/_internal/hooks/use-deprecation-warning.js +1 -1
  6. package/es/_internal/hooks/use-effect-once.js +1 -1
  7. package/es/_internal/hooks/use-event.js +1 -1
  8. package/es/_internal/hooks/use-is-first-render.js +1 -1
  9. package/es/_internal/hooks/use-sync-ref.js +1 -1
  10. package/es/_internal/hooks/use-timer/index.js +1 -1
  11. package/es/_internal/hooks/use-timer/timer.js +1 -1
  12. package/es/_internal/hooks/use-timer/use-timer.js +1 -1
  13. package/es/_internal/hooks/use-update-effect.js +1 -1
  14. package/es/_internal/hooks/use-warn.js +1 -1
  15. package/es/_internal/index.js +1 -1
  16. package/es/components/Block.js +1 -1
  17. package/es/components/GlobalStyles.js +1 -1
  18. package/es/components/GridProvider.js +1 -1
  19. package/es/components/HiddenInput.js +1 -1
  20. package/es/components/Item.js +1 -1
  21. package/es/components/OpenTrasition.js +1 -1
  22. package/es/components/Root.js +1 -1
  23. package/es/components/actions/Action/Action.js +1 -1
  24. package/es/components/actions/Button/Button.js +1 -1
  25. package/es/components/actions/Button/index.js +1 -1
  26. package/es/components/actions/ButtonGroup/ButtonGroup.js +1 -1
  27. package/es/components/actions/CommandMenu/CommandMenu.js +1 -1
  28. package/es/components/actions/CommandMenu/index.js +1 -1
  29. package/es/components/actions/CommandMenu/styled.js +1 -1
  30. package/es/components/actions/ItemAction/ItemAction.js +7 -2
  31. package/es/components/actions/ItemAction/index.js +1 -1
  32. package/es/components/actions/ItemButton/ItemButton.js +1 -1
  33. package/es/components/actions/ItemButton/index.js +1 -1
  34. package/es/components/actions/Link/Link.js +1 -1
  35. package/es/components/actions/Menu/Menu.js +1 -1
  36. package/es/components/actions/Menu/MenuItem.js +1 -1
  37. package/es/components/actions/Menu/MenuSection.js +1 -1
  38. package/es/components/actions/Menu/MenuTrigger.js +1 -1
  39. package/es/components/actions/Menu/SubMenuTrigger.js +1 -1
  40. package/es/components/actions/Menu/SubmenuTriggerContext.js +1 -1
  41. package/es/components/actions/Menu/context.js +1 -1
  42. package/es/components/actions/Menu/index.js +1 -1
  43. package/es/components/actions/Menu/styled.js +1 -1
  44. package/es/components/actions/index.js +1 -1
  45. package/es/components/actions/use-action.js +1 -1
  46. package/es/components/actions/use-anchored-menu.js +1 -1
  47. package/es/components/actions/use-context-menu.js +1 -1
  48. package/es/components/content/ActiveZone/ActiveZone.js +1 -1
  49. package/es/components/content/Alert/Alert.js +1 -1
  50. package/es/components/content/Alert/index.js +1 -1
  51. package/es/components/content/Alert/types.js +1 -1
  52. package/es/components/content/Alert/use-alert.js +1 -1
  53. package/es/components/content/Avatar/Avatar.js +1 -1
  54. package/es/components/content/Badge/Badge.js +1 -1
  55. package/es/components/content/Card/Card.js +1 -1
  56. package/es/components/content/Content.js +1 -1
  57. package/es/components/content/CopyPasteBlock/CopyPasteBlock.js +1 -1
  58. package/es/components/content/CopyPasteBlock/index.js +1 -1
  59. package/es/components/content/CopySnippet/CopySnippet.js +1 -1
  60. package/es/components/content/CopySnippet/index.js +1 -1
  61. package/es/components/content/Divider.js +1 -1
  62. package/es/components/content/Footer.js +1 -1
  63. package/es/components/content/Header.js +1 -1
  64. package/es/components/content/HotKeys/HotKeys.js +1 -1
  65. package/es/components/content/HotKeys/index.js +1 -1
  66. package/es/components/content/ItemBase/ItemBase.js +1 -1
  67. package/es/components/content/ItemBase/index.js +1 -1
  68. package/es/components/content/List/SectionHeading.js +1 -1
  69. package/es/components/content/List/index.js +1 -1
  70. package/es/components/content/Paragraph.js +1 -1
  71. package/es/components/content/Placeholder/Placeholder.js +1 -1
  72. package/es/components/content/PrismCode/PrismCode.js +1 -1
  73. package/es/components/content/PrismCode/prismSetup.js +1 -1
  74. package/es/components/content/PrismDiffCode/PrismDiffCode.js +1 -1
  75. package/es/components/content/Result/Result.js +1 -1
  76. package/es/components/content/Skeleton/Skeleton.js +1 -1
  77. package/es/components/content/Tag/Tag.js +1 -1
  78. package/es/components/content/Text.js +1 -1
  79. package/es/components/content/Title.js +1 -1
  80. package/es/components/fields/Checkbox/Checkbox.js +2 -2
  81. package/es/components/fields/Checkbox/CheckboxGroup.js +2 -2
  82. package/es/components/fields/Checkbox/context.js +1 -1
  83. package/es/components/fields/Checkbox/index.js +1 -1
  84. package/es/components/fields/ComboBox/ComboBox.js +792 -180
  85. package/es/components/fields/ComboBox/index.js +2 -2
  86. package/es/components/fields/DatePicker/DateInput.js +1 -1
  87. package/es/components/fields/DatePicker/DateInputBase.js +1 -1
  88. package/es/components/fields/DatePicker/DatePicker.js +1 -1
  89. package/es/components/fields/DatePicker/DatePickerButton.js +1 -1
  90. package/es/components/fields/DatePicker/DatePickerElement.js +1 -1
  91. package/es/components/fields/DatePicker/DatePickerInput.js +1 -1
  92. package/es/components/fields/DatePicker/DatePickerSegment.js +1 -1
  93. package/es/components/fields/DatePicker/DateRangePicker.js +1 -1
  94. package/es/components/fields/DatePicker/DateRangeSeparatedPicker.js +1 -1
  95. package/es/components/fields/DatePicker/TimeInput.js +1 -1
  96. package/es/components/fields/DatePicker/index.js +1 -1
  97. package/es/components/fields/DatePicker/intl.js +1 -1
  98. package/es/components/fields/DatePicker/parseDate.js +1 -1
  99. package/es/components/fields/DatePicker/props.js +1 -1
  100. package/es/components/fields/DatePicker/types.js +1 -1
  101. package/es/components/fields/DatePicker/utils.js +1 -1
  102. package/es/components/fields/FileInput/FileInput.js +2 -2
  103. package/es/components/fields/FilterListBox/FilterListBox.js +36 -46
  104. package/es/components/fields/FilterListBox/index.js +1 -1
  105. package/es/components/fields/FilterPicker/FilterPicker.js +83 -315
  106. package/es/components/fields/FilterPicker/index.js +1 -1
  107. package/es/components/fields/Input/Input.js +1 -1
  108. package/es/components/fields/Input/index.js +1 -1
  109. package/es/components/fields/LegacyComboBox/LegacyComboBox.js +290 -0
  110. package/es/components/fields/LegacyComboBox/index.js +10 -0
  111. package/es/components/fields/ListBox/ListBox.js +25 -5
  112. package/es/components/fields/ListBox/index.js +1 -1
  113. package/es/components/fields/NumberInput/NumberInput.js +1 -1
  114. package/es/components/fields/NumberInput/StepButton.js +1 -1
  115. package/es/components/fields/PasswordInput/PasswordInput.js +1 -1
  116. package/es/components/fields/RadioGroup/Radio.js +2 -2
  117. package/es/components/fields/RadioGroup/RadioGroup.js +2 -2
  118. package/es/components/fields/RadioGroup/context.js +1 -1
  119. package/es/components/fields/RadioGroup/index.js +1 -1
  120. package/es/components/fields/SearchInput/SearchInput.js +1 -1
  121. package/es/components/fields/SearchInput/index.js +1 -1
  122. package/es/components/fields/Select/Select.js +73 -54
  123. package/es/components/fields/Select/index.js +1 -1
  124. package/es/components/fields/Slider/Gradation.js +1 -1
  125. package/es/components/fields/Slider/Header.js +1 -1
  126. package/es/components/fields/Slider/RangeSlider.js +1 -1
  127. package/es/components/fields/Slider/Slider.js +1 -1
  128. package/es/components/fields/Slider/SliderBase.js +2 -2
  129. package/es/components/fields/Slider/SliderInput.js +1 -1
  130. package/es/components/fields/Slider/SliderThumb.js +1 -1
  131. package/es/components/fields/Slider/SliderTrack.js +1 -1
  132. package/es/components/fields/Slider/elements.js +1 -1
  133. package/es/components/fields/Slider/index.js +1 -1
  134. package/es/components/fields/Slider/types.js +1 -1
  135. package/es/components/fields/Switch/Switch.js +2 -2
  136. package/es/components/fields/Switch/index.js +1 -1
  137. package/es/components/fields/TextArea/TextArea.js +1 -1
  138. package/es/components/fields/TextArea/index.js +1 -1
  139. package/es/components/fields/TextInput/TextInput.js +1 -1
  140. package/es/components/fields/TextInput/TextInputBase.js +3 -3
  141. package/es/components/fields/TextInput/index.js +1 -1
  142. package/es/components/fields/TextInputMapper/TextInputMapper.js +3 -3
  143. package/es/components/fields/TextInputMapper/index.js +1 -1
  144. package/es/components/fields/index.js +2 -1
  145. package/es/components/form/FieldWrapper/FieldWrapper.js +1 -1
  146. package/es/components/form/FieldWrapper/extract-field-wrapper-props.js +1 -1
  147. package/es/components/form/FieldWrapper/index.js +1 -1
  148. package/es/components/form/FieldWrapper/types.js +1 -1
  149. package/es/components/form/Form/Field.js +1 -1
  150. package/es/components/form/Form/Form.js +1 -1
  151. package/es/components/form/Form/ResetButton/ResetButton.js +1 -1
  152. package/es/components/form/Form/ResetButton/index.js +1 -1
  153. package/es/components/form/Form/SubmitButton/SubmitButton.js +1 -1
  154. package/es/components/form/Form/SubmitButton/index.js +1 -1
  155. package/es/components/form/Form/SubmitError.js +1 -1
  156. package/es/components/form/Form/index.js +1 -1
  157. package/es/components/form/Form/types.js +1 -1
  158. package/es/components/form/Form/use-field/index.js +1 -1
  159. package/es/components/form/Form/use-field/types.js +1 -1
  160. package/es/components/form/Form/use-field/use-field-props.js +21 -5
  161. package/es/components/form/Form/use-field/use-field.js +1 -1
  162. package/es/components/form/Form/use-form.js +1 -1
  163. package/es/components/form/Form/validation.js +1 -1
  164. package/es/components/form/Label.js +1 -1
  165. package/es/components/form/index.js +1 -1
  166. package/es/components/form/wrapper.js +1 -1
  167. package/es/components/helpers/DisplayTransition/DisplayTransition.js +242 -0
  168. package/es/components/helpers/index.js +10 -0
  169. package/es/components/layout/Flex.js +1 -1
  170. package/es/components/layout/Flow.js +1 -1
  171. package/es/components/layout/Grid.js +1 -1
  172. package/es/components/layout/Panel.js +1 -1
  173. package/es/components/layout/Prefix.js +1 -1
  174. package/es/components/layout/ResizablePanel.js +1 -1
  175. package/es/components/layout/Space.js +1 -1
  176. package/es/components/layout/Suffix.js +1 -1
  177. package/es/components/navigation/LegacyTabs/LegacyTabs.js +1 -1
  178. package/es/components/organisms/FileTabs/FileTabs.js +1 -1
  179. package/es/components/organisms/StatsCard/StatsCard.js +1 -1
  180. package/es/components/other/Base64Upload/Base64Upload.js +1 -1
  181. package/es/components/other/Calendar/Calendar.js +1 -1
  182. package/es/components/other/Calendar/CalendarCell.js +1 -1
  183. package/es/components/other/Calendar/CalendarGrid.js +1 -1
  184. package/es/components/other/Calendar/RangeCalendar.js +1 -1
  185. package/es/components/other/CloudLogo/CloudLogo.js +1 -1
  186. package/es/components/overlays/AlertDialog/AlertDialog.js +1 -1
  187. package/es/components/overlays/AlertDialog/AlertDialogApiProvider.js +1 -1
  188. package/es/components/overlays/AlertDialog/AlertDialogZone.js +1 -1
  189. package/es/components/overlays/AlertDialog/index.js +1 -1
  190. package/es/components/overlays/AlertDialog/types.js +1 -1
  191. package/es/components/overlays/Dialog/Dialog.js +1 -1
  192. package/es/components/overlays/Dialog/DialogContainer.js +1 -1
  193. package/es/components/overlays/Dialog/DialogForm.js +1 -1
  194. package/es/components/overlays/Dialog/DialogTrigger.js +1 -1
  195. package/es/components/overlays/Dialog/context.js +1 -1
  196. package/es/components/overlays/Dialog/index.js +1 -1
  197. package/es/components/overlays/Dialog/use-dialog-container.js +1 -1
  198. package/es/components/overlays/Modal/Modal.js +1 -1
  199. package/es/components/overlays/Modal/OpenTransition.js +1 -1
  200. package/es/components/overlays/Modal/Overlay.js +1 -1
  201. package/es/components/overlays/Modal/Popover.js +1 -1
  202. package/es/components/overlays/Modal/Tray.js +1 -1
  203. package/es/components/overlays/Modal/Underlay.js +1 -1
  204. package/es/components/overlays/Modal/index.js +1 -1
  205. package/es/components/overlays/Modal/types.js +1 -1
  206. package/es/components/overlays/NewNotifications/Bar/FloatingNotification.js +1 -1
  207. package/es/components/overlays/NewNotifications/Bar/NotificationsBar.js +1 -1
  208. package/es/components/overlays/NewNotifications/Bar/TransitionComponent.js +1 -1
  209. package/es/components/overlays/NewNotifications/Bar/index.js +1 -1
  210. package/es/components/overlays/NewNotifications/Dialog/NotificationsDialogContext.js +1 -1
  211. package/es/components/overlays/NewNotifications/Dialog/NotificationsDialogTrigger.js +1 -1
  212. package/es/components/overlays/NewNotifications/Dialog/index.js +1 -1
  213. package/es/components/overlays/NewNotifications/Notification.js +1 -1
  214. package/es/components/overlays/NewNotifications/NotificationView/NotificationAction.js +1 -1
  215. package/es/components/overlays/NewNotifications/NotificationView/NotificationCloseButton.js +1 -1
  216. package/es/components/overlays/NewNotifications/NotificationView/NotificationDescription.js +1 -1
  217. package/es/components/overlays/NewNotifications/NotificationView/NotificationFooter.js +1 -1
  218. package/es/components/overlays/NewNotifications/NotificationView/NotificationHeader.js +1 -1
  219. package/es/components/overlays/NewNotifications/NotificationView/NotificationIcon.js +1 -1
  220. package/es/components/overlays/NewNotifications/NotificationView/NotificationProvider.js +1 -1
  221. package/es/components/overlays/NewNotifications/NotificationView/NotificationView.js +1 -1
  222. package/es/components/overlays/NewNotifications/NotificationView/index.js +1 -1
  223. package/es/components/overlays/NewNotifications/NotificationView/types.js +1 -1
  224. package/es/components/overlays/NewNotifications/NotificationsContext/NotificationsContext.js +1 -1
  225. package/es/components/overlays/NewNotifications/NotificationsContext/NotificationsProvider.js +1 -1
  226. package/es/components/overlays/NewNotifications/NotificationsContext/index.js +1 -1
  227. package/es/components/overlays/NewNotifications/NotificationsContext/use-notifications.js +1 -1
  228. package/es/components/overlays/NewNotifications/NotificationsList/NotificationsList.js +1 -1
  229. package/es/components/overlays/NewNotifications/NotificationsList/NotificationsListItem.js +1 -1
  230. package/es/components/overlays/NewNotifications/NotificationsList/index.js +1 -1
  231. package/es/components/overlays/NewNotifications/NotificationsList/types.js +1 -1
  232. package/es/components/overlays/NewNotifications/hooks/index.js +1 -1
  233. package/es/components/overlays/NewNotifications/hooks/types.js +1 -1
  234. package/es/components/overlays/NewNotifications/hooks/use-notification-list-item.js +1 -1
  235. package/es/components/overlays/NewNotifications/hooks/use-notifications-api.js +1 -1
  236. package/es/components/overlays/NewNotifications/hooks/use-notifications-list.js +1 -1
  237. package/es/components/overlays/NewNotifications/hooks/use-notifications-observer.js +1 -1
  238. package/es/components/overlays/NewNotifications/index.js +1 -1
  239. package/es/components/overlays/NewNotifications/types.js +1 -1
  240. package/es/components/overlays/Notification/Notification.js +1 -1
  241. package/es/components/overlays/OverlayWrapper.js +1 -1
  242. package/es/components/overlays/Toasts/Toast.js +1 -1
  243. package/es/components/overlays/Toasts/index.js +1 -1
  244. package/es/components/overlays/Toasts/types.js +1 -1
  245. package/es/components/overlays/Toasts/use-toasts-api.js +1 -1
  246. package/es/components/overlays/Tooltip/Tooltip.js +30 -18
  247. package/es/components/overlays/Tooltip/TooltipProvider.js +2 -2
  248. package/es/components/overlays/Tooltip/TooltipTrigger.js +25 -7
  249. package/es/components/overlays/Tooltip/context.js +1 -1
  250. package/es/components/overlays/Tooltip/index.js +1 -1
  251. package/es/components/portal/Portal.js +5 -2
  252. package/es/components/portal/PortalProvider.js +1 -1
  253. package/es/components/portal/index.js +1 -1
  254. package/es/components/portal/types.js +1 -1
  255. package/es/components/portal/usePortal.js +1 -1
  256. package/es/components/shared/InvalidIcon.js +1 -1
  257. package/es/components/shared/ValidIcon.js +1 -1
  258. package/es/components/status/LoadingAnimation/LoadingAnimation.js +1 -1
  259. package/es/components/status/LoadingAnimation/index.js +1 -1
  260. package/es/components/status/Spin/Cube.js +1 -1
  261. package/es/components/status/Spin/InternalSpinner.js +1 -1
  262. package/es/components/status/Spin/Spin.js +1 -1
  263. package/es/components/status/Spin/SpinsContainer.js +1 -1
  264. package/es/components/status/Spin/index.js +1 -1
  265. package/es/components/status/Spin/types.js +1 -1
  266. package/es/components/status/index.js +1 -1
  267. package/es/data/item-themes.js +1 -1
  268. package/es/data/themes.js +1 -1
  269. package/es/icons/AdjustmentsHorizontalIcon.js +1 -1
  270. package/es/icons/AdjustmentsIcon.js +1 -1
  271. package/es/icons/AiIcon.js +1 -1
  272. package/es/icons/AreaChartIcon.js +1 -1
  273. package/es/icons/BackwardIcon.js +1 -1
  274. package/es/icons/BarChartIcon.js +1 -1
  275. package/es/icons/BellFilledIcon.js +1 -1
  276. package/es/icons/BellIcon.js +1 -1
  277. package/es/icons/BooleanIcon.js +1 -1
  278. package/es/icons/CalendarEditIcon.js +1 -1
  279. package/es/icons/CalendarIcon.js +1 -1
  280. package/es/icons/CaretDownIcon.js +1 -1
  281. package/es/icons/CaretUpIcon.js +1 -1
  282. package/es/icons/ChartAreaStackedIcon.js +1 -1
  283. package/es/icons/ChartAreaStackedPercentageIcon.js +1 -1
  284. package/es/icons/ChartBarGroupedHorizontalIcon.js +1 -1
  285. package/es/icons/ChartBarGroupedIcon.js +1 -1
  286. package/es/icons/ChartBarHorizontalIcon.js +1 -1
  287. package/es/icons/ChartBarLineIcon.js +1 -1
  288. package/es/icons/ChartBarStackedHorizontalIcon.js +1 -1
  289. package/es/icons/ChartBarStackedIcon.js +1 -1
  290. package/es/icons/ChartBarStackedPercentageHorizontalIcon.js +1 -1
  291. package/es/icons/ChartBarStackedPercentageIcon.js +1 -1
  292. package/es/icons/ChartBoxPlot2Icon.js +1 -1
  293. package/es/icons/ChartBoxPlotIcon.js +1 -1
  294. package/es/icons/ChartBubbleIcon.js +1 -1
  295. package/es/icons/ChartDonut2Icon.js +1 -1
  296. package/es/icons/ChartFunnelIcon.js +1 -1
  297. package/es/icons/ChartHeatmapIcon.js +1 -1
  298. package/es/icons/ChartKPIIcon.js +1 -1
  299. package/es/icons/ChartPie2Icon.js +1 -1
  300. package/es/icons/ChartScatterIcon.js +1 -1
  301. package/es/icons/CheckCircleFilledIcon.js +1 -1
  302. package/es/icons/CheckCircleIcon.js +1 -1
  303. package/es/icons/CheckIcon.js +1 -1
  304. package/es/icons/CircleFilledIcon.js +1 -1
  305. package/es/icons/ClearIcon.js +1 -1
  306. package/es/icons/CloseCircleFilledIcon.js +1 -1
  307. package/es/icons/CloseCircleIcon.js +1 -1
  308. package/es/icons/CloseIcon.js +1 -1
  309. package/es/icons/CodeIcon.js +1 -1
  310. package/es/icons/CopyIcon.js +1 -1
  311. package/es/icons/CountIcon.js +1 -1
  312. package/es/icons/CubeIcon.js +1 -1
  313. package/es/icons/CubePauseIcon.js +1 -1
  314. package/es/icons/CubePlayIcon.js +1 -1
  315. package/es/icons/CurrencyDollarIcon.js +1 -1
  316. package/es/icons/DangerIcon.js +1 -1
  317. package/es/icons/DashboardIcon.js +1 -1
  318. package/es/icons/DatabaseIcon.js +1 -1
  319. package/es/icons/DecimalDecreaseIcon.js +1 -1
  320. package/es/icons/DecimalIncreaseIcon.js +1 -1
  321. package/es/icons/DirectionIcon.js +1 -1
  322. package/es/icons/DonutIcon.js +1 -1
  323. package/es/icons/DownIcon.js +1 -1
  324. package/es/icons/EditIcon.js +1 -1
  325. package/es/icons/ExclamationCircleFilledIcon.js +1 -1
  326. package/es/icons/ExclamationCircleIcon.js +1 -1
  327. package/es/icons/ExclamationIcon.js +1 -1
  328. package/es/icons/EyeIcon.js +1 -1
  329. package/es/icons/EyeInvisibleIcon.js +1 -1
  330. package/es/icons/FilterIcon.js +1 -1
  331. package/es/icons/FolderFilledIcon.js +1 -1
  332. package/es/icons/FolderIcon.js +1 -1
  333. package/es/icons/FolderOpenFilledIcon.js +1 -1
  334. package/es/icons/FolderOpenIcon.js +1 -1
  335. package/es/icons/ForwardIcon.js +1 -1
  336. package/es/icons/HierarchyIcon.js +1 -1
  337. package/es/icons/Icon.js +1 -1
  338. package/es/icons/InfoCircleIcon.js +1 -1
  339. package/es/icons/InfoIcon.js +1 -1
  340. package/es/icons/KeyIcon.js +1 -1
  341. package/es/icons/LeftIcon.js +1 -1
  342. package/es/icons/LineChartIcon.js +1 -1
  343. package/es/icons/LoadingIcon.js +1 -1
  344. package/es/icons/LockFilledIcon.js +1 -1
  345. package/es/icons/LockIcon.js +1 -1
  346. package/es/icons/MoreIcon.js +1 -1
  347. package/es/icons/NotAllowedIcon.js +1 -1
  348. package/es/icons/Number123Icon.js +1 -1
  349. package/es/icons/NumberIcon.js +1 -1
  350. package/es/icons/PauseCircleFilledIcon.js +1 -1
  351. package/es/icons/PauseCircleIcon.js +1 -1
  352. package/es/icons/PauseIcon.js +1 -1
  353. package/es/icons/PercentageIcon.js +1 -1
  354. package/es/icons/PieChartIcon.js +1 -1
  355. package/es/icons/PlayCircleIcon.js +1 -1
  356. package/es/icons/PlayIcon.js +1 -1
  357. package/es/icons/PlusIcon.js +1 -1
  358. package/es/icons/ReloadIcon.js +1 -1
  359. package/es/icons/ReportIcon.js +1 -1
  360. package/es/icons/ReturnIcon.js +1 -1
  361. package/es/icons/RightIcon.js +1 -1
  362. package/es/icons/SchemeIcon.js +1 -1
  363. package/es/icons/SearchIcon.js +1 -1
  364. package/es/icons/SettingsIcon.js +1 -1
  365. package/es/icons/ShieldFilledIcon.js +1 -1
  366. package/es/icons/ShieldIcon.js +1 -1
  367. package/es/icons/SlashIcon.js +1 -1
  368. package/es/icons/SparklesIcon.js +1 -1
  369. package/es/icons/SqlIcon.js +1 -1
  370. package/es/icons/StatsIcon.js +1 -1
  371. package/es/icons/StopIcon.js +1 -1
  372. package/es/icons/StringIcon.js +1 -1
  373. package/es/icons/SwitchIcon.js +1 -1
  374. package/es/icons/TableIcon.js +1 -1
  375. package/es/icons/ThumbsDownIcon.js +1 -1
  376. package/es/icons/ThumbsUpIcon.js +1 -1
  377. package/es/icons/ThunderboltCrossedIcon.js +1 -1
  378. package/es/icons/ThunderboltFilledIcon.js +1 -1
  379. package/es/icons/ThunderboltIcon.js +1 -1
  380. package/es/icons/TimeIcon.js +1 -1
  381. package/es/icons/UnlockIcon.js +1 -1
  382. package/es/icons/UpIcon.js +1 -1
  383. package/es/icons/UserGroupIcon.js +1 -1
  384. package/es/icons/UserIcon.js +1 -1
  385. package/es/icons/UserLockIcon.js +1 -1
  386. package/es/icons/ViewIcon.js +1 -1
  387. package/es/icons/WarningFilledIcon.js +1 -1
  388. package/es/icons/WarningIcon.js +1 -1
  389. package/es/icons/add-new-icon.js +1 -1
  390. package/es/icons/index.js +1 -1
  391. package/es/icons/wrap-icon.js +1 -1
  392. package/es/index.js +2 -1
  393. package/es/provider.js +1 -1
  394. package/es/providers/TrackingProvider.js +1 -1
  395. package/es/providers/navigation.types.js +1 -1
  396. package/es/providers/navigationAdapter.default.js +1 -1
  397. package/es/services/notification.js +1 -1
  398. package/es/shared/form.js +1 -1
  399. package/es/shared/index.js +1 -1
  400. package/es/stories/Form.legacy-stories.js +1 -1
  401. package/es/stories/FormFieldArgs.js +1 -1
  402. package/es/stories/Layout.stories.js +1 -1
  403. package/es/stories/Tasty.stories.js +1 -1
  404. package/es/stories/components/ConfirmDeletionDialogForm.js +1 -1
  405. package/es/stories/components/DialogFormApp.js +1 -1
  406. package/es/stories/components/StyledButton.js +1 -1
  407. package/es/stories/lists/baseProps.js +1 -1
  408. package/es/tasty/debug.js +1 -1
  409. package/es/tasty/index.js +1 -1
  410. package/es/tasty/injector/index.js +1 -1
  411. package/es/tasty/injector/injector.js +1 -1
  412. package/es/tasty/injector/sheet-manager.js +1 -1
  413. package/es/tasty/injector/types.js +1 -1
  414. package/es/tasty/parser/classify.js +1 -1
  415. package/es/tasty/parser/const.js +1 -1
  416. package/es/tasty/parser/lru.js +1 -1
  417. package/es/tasty/parser/parser.js +1 -1
  418. package/es/tasty/parser/tokenizer.js +1 -1
  419. package/es/tasty/parser/types.js +1 -1
  420. package/es/tasty/providers/BreakpointsProvider.js +1 -1
  421. package/es/tasty/styles/align.js +1 -1
  422. package/es/tasty/styles/border.js +1 -1
  423. package/es/tasty/styles/boxShadow.combinator.js +1 -1
  424. package/es/tasty/styles/color.js +1 -1
  425. package/es/tasty/styles/createStyle.js +1 -1
  426. package/es/tasty/styles/dimension.js +1 -1
  427. package/es/tasty/styles/display.js +1 -1
  428. package/es/tasty/styles/fade.js +1 -1
  429. package/es/tasty/styles/fill.js +1 -1
  430. package/es/tasty/styles/flow.js +1 -1
  431. package/es/tasty/styles/font.js +1 -1
  432. package/es/tasty/styles/fontStyle.js +1 -1
  433. package/es/tasty/styles/gap.js +1 -1
  434. package/es/tasty/styles/groupRadius.js +1 -1
  435. package/es/tasty/styles/height.js +1 -1
  436. package/es/tasty/styles/index.js +1 -1
  437. package/es/tasty/styles/inset.js +1 -1
  438. package/es/tasty/styles/justify.js +1 -1
  439. package/es/tasty/styles/list.js +1 -1
  440. package/es/tasty/styles/margin.js +1 -1
  441. package/es/tasty/styles/outline.js +1 -1
  442. package/es/tasty/styles/padding.js +1 -1
  443. package/es/tasty/styles/place.js +1 -1
  444. package/es/tasty/styles/predefined.js +1 -1
  445. package/es/tasty/styles/preset.js +1 -1
  446. package/es/tasty/styles/radius.js +1 -1
  447. package/es/tasty/styles/reset.js +1 -1
  448. package/es/tasty/styles/scrollbar.js +1 -1
  449. package/es/tasty/styles/shadow.js +1 -1
  450. package/es/tasty/styles/styledScrollbar.js +1 -1
  451. package/es/tasty/styles/transition.js +1 -1
  452. package/es/tasty/styles/types.js +1 -1
  453. package/es/tasty/styles/width.js +1 -1
  454. package/es/tasty/tasty.js +1 -1
  455. package/es/tasty/types.js +1 -1
  456. package/es/tasty/utils/cache-wrapper.js +1 -1
  457. package/es/tasty/utils/case-converter.js +1 -1
  458. package/es/tasty/utils/colors.js +1 -1
  459. package/es/tasty/utils/dotize.js +1 -1
  460. package/es/tasty/utils/filterBaseProps.js +1 -1
  461. package/es/tasty/utils/getDisplayName.js +1 -1
  462. package/es/tasty/utils/getModCombinations.js +1 -1
  463. package/es/tasty/utils/isDevEnv.js +1 -1
  464. package/es/tasty/utils/mergeStyles.js +1 -1
  465. package/es/tasty/utils/modAttrs.js +1 -1
  466. package/es/tasty/utils/renderStyles.js +1 -1
  467. package/es/tasty/utils/responsive.js +1 -1
  468. package/es/tasty/utils/string.js +1 -1
  469. package/es/tasty/utils/styles.js +1 -1
  470. package/es/tasty/utils/warnings.js +1 -1
  471. package/es/tokens.js +1 -1
  472. package/es/type-checks.js +1 -1
  473. package/es/utils/ResizeSensor.js +1 -1
  474. package/es/utils/modules.js +1 -1
  475. package/es/utils/promise.js +1 -1
  476. package/es/utils/random.js +1 -1
  477. package/es/utils/range.js +1 -1
  478. package/es/utils/react/Slots.js +1 -1
  479. package/es/utils/react/chain.js +1 -1
  480. package/es/utils/react/forwardRefWithGenerics.js +1 -1
  481. package/es/utils/react/index.js +2 -2
  482. package/es/utils/react/interactions.js +1 -1
  483. package/es/utils/react/isTextOnly.js +1 -1
  484. package/es/utils/react/mapProps.js +1 -1
  485. package/es/utils/react/mergeProps.js +1 -1
  486. package/es/utils/react/nullableValue.js +1 -1
  487. package/es/utils/react/sharedStore.js +1 -1
  488. package/es/utils/react/useCombinedRefs.js +11 -6
  489. package/es/utils/react/useControlledFocusVisible.js +1 -1
  490. package/es/utils/react/useEventBus.js +1 -1
  491. package/es/utils/react/useId.js +1 -1
  492. package/es/utils/react/useIsDarwin.js +1 -1
  493. package/es/utils/react/useKeySymbols.js +1 -1
  494. package/es/utils/react/useLayoutEffect.js +1 -1
  495. package/es/utils/react/useQaProps.js +1 -1
  496. package/es/utils/react/useViewportSize.js +1 -1
  497. package/es/utils/react/wrapNodeIfPlain.js +1 -1
  498. package/es/utils/transitions.js +1 -1
  499. package/es/utils/tree.js +1 -1
  500. package/es/utils/warnings.js +1 -1
  501. package/es/version.js +2 -2
  502. package/package.json +1 -1
  503. package/types/components/GridProvider.d.ts +1 -1
  504. package/types/components/fields/ComboBox/ComboBox.d.ts +101 -30
  505. package/types/components/fields/ComboBox/index.d.ts +2 -1
  506. package/types/components/fields/FilterListBox/FilterListBox.d.ts +7 -0
  507. package/types/components/fields/FilterPicker/FilterPicker.d.ts +9 -2
  508. package/types/components/fields/LegacyComboBox/LegacyComboBox.d.ts +49 -0
  509. package/types/components/fields/LegacyComboBox/index.d.ts +1 -0
  510. package/types/components/fields/ListBox/ListBox.d.ts +5 -0
  511. package/types/components/fields/Select/Select.d.ts +3 -1
  512. package/types/components/fields/index.d.ts +1 -0
  513. package/types/components/helpers/DisplayTransition/DisplayTransition.d.ts +24 -0
  514. package/types/components/helpers/index.d.ts +1 -0
  515. package/types/components/layout/Prefix.d.ts +1 -1
  516. package/types/components/layout/Suffix.d.ts +1 -1
  517. package/types/components/overlays/Tooltip/TooltipTrigger.d.ts +5 -0
  518. package/types/components/overlays/Tooltip/context.d.ts +6 -1
  519. package/types/components/portal/Portal.d.ts +1 -1
  520. package/types/index.d.ts +2 -0
  521. package/types/shared/form.d.ts +2 -0
  522. package/types/utils/react/index.d.ts +1 -1
  523. package/types/utils/react/useCombinedRefs.d.ts +3 -2
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * @license MIT
3
3
  * author: Cube Dev Team
4
- * @cube-dev/ui-kit v0.81.0
4
+ * @cube-dev/ui-kit v0.82.1
5
5
  * Released under the MIT license.
6
6
  */
7
7
 
8
8
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
9
- import { cloneElement, forwardRef, useEffect, useMemo, } from 'react';
10
- import { useButton, useComboBox, useFilter, useHover, useOverlayPosition, } from 'react-aria';
11
- import { Section as BaseSection, useComboBoxState } from 'react-stately';
12
- import { useEvent } from '../../../_internal/index';
13
- import { CloseIcon, DownIcon, LoadingIcon } from '../../../icons';
9
+ import React, { cloneElement, forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
10
+ import { useFilter, useKeyboard, useOverlay, useOverlayPosition, } from 'react-aria';
11
+ import { Section as BaseSection, Item } from 'react-stately';
12
+ import { useEvent } from '../../../_internal';
13
+ import { CloseIcon, DirectionIcon, LoadingIcon } from '../../../icons';
14
14
  import { useProviderProps } from '../../../provider';
15
15
  import { BASE_STYLES, COLOR_STYLES, extractStyles, OUTER_STYLES, tasty, } from '../../../tasty';
16
16
  import { generateRandomId } from '../../../utils/random';
@@ -19,12 +19,12 @@ import { useFocus } from '../../../utils/react/interactions';
19
19
  import { useEventBus } from '../../../utils/react/useEventBus';
20
20
  import { ItemAction } from '../../actions';
21
21
  import { useFieldProps, useFormProps, wrapWithField } from '../../form';
22
- import { Item } from '../../Item';
23
- import { OverlayWrapper } from '../../overlays/OverlayWrapper';
22
+ import { DisplayTransition } from '../../helpers';
23
+ import { Portal } from '../../portal';
24
24
  import { InvalidIcon } from '../../shared/InvalidIcon';
25
25
  import { ValidIcon } from '../../shared/ValidIcon';
26
- import { DEFAULT_INPUT_STYLES, INPUT_WRAPPER_STYLES } from '../index';
27
- import { ListBoxPopup } from '../Select';
26
+ import { ListBox } from '../ListBox/ListBox';
27
+ import { DEFAULT_INPUT_STYLES, INPUT_WRAPPER_STYLES, } from '../TextInput/TextInputBase';
28
28
  const ComboBoxWrapperElement = tasty({
29
29
  styles: INPUT_WRAPPER_STYLES,
30
30
  });
@@ -32,91 +32,432 @@ const InputElement = tasty({
32
32
  as: 'input',
33
33
  styles: DEFAULT_INPUT_STYLES,
34
34
  });
35
- const TriggerElement = tasty({
36
- as: 'button',
37
- type: 'neutral',
35
+ const ComboBoxOverlayElement = tasty({
36
+ qa: 'ComboBoxOverlay',
38
37
  styles: {
39
38
  display: 'grid',
40
- placeItems: 'center',
41
- placeContent: 'center',
42
- placeSelf: 'stretch',
43
- radius: '(1r - 1bw) right',
39
+ gridRows: '1sf',
40
+ gridColumns: '1sf',
41
+ width: '$min-width max-content 50vw',
42
+ height: 'initial max-content (50vh - $size)',
43
+ overflow: 'auto',
44
+ background: '#white',
45
+ radius: '1cr',
46
+ shadow: '0 .5x 2x #shadow',
44
47
  padding: '0',
45
- width: '3x',
46
- boxSizing: 'border-box',
47
- color: {
48
- '': '#dark-02',
49
- hovered: '#dark-02',
50
- pressed: '#purple',
51
- disabled: '#dark.30',
48
+ border: '#border',
49
+ hide: {
50
+ '': false,
51
+ hidden: true,
52
52
  },
53
- border: 'left',
54
- reset: 'button',
55
- margin: 0,
56
- fill: {
57
- '': '#dark.0',
58
- hovered: '#dark.04',
59
- pressed: '#purple.10',
60
- disabled: '#clear',
53
+ transition: 'translate $transition ease-out, scale $transition ease-out, theme $transition ease-out',
54
+ translate: {
55
+ '': '0 0',
56
+ 'open & [data-placement="top"]': '0 0',
57
+ '!open & [data-placement="top"]': '0 10%',
58
+ 'open & ([data-placement="bottom"] | ![data-placement]': '0 0',
59
+ '!open & ([data-placement="bottom"] | ![data-placement])': '0 -10%',
61
60
  },
62
- cursor: 'pointer',
61
+ scale: {
62
+ '': '1 1',
63
+ '!open': '1 .9',
64
+ },
65
+ opacity: {
66
+ '': 1,
67
+ '!open': 0.001,
68
+ },
69
+ '$min-width': 'min 30x',
63
70
  },
64
71
  });
65
72
  const PROP_STYLES = [...BASE_STYLES, ...OUTER_STYLES, ...COLOR_STYLES];
73
+ function useComboBoxState({ selectedKey, defaultSelectedKey, inputValue, defaultInputValue, comboBoxId, }) {
74
+ // Get event bus for menu synchronization
75
+ const { emit, on } = useEventBus();
76
+ // Internal state for uncontrolled mode
77
+ const [internalSelectedKey, setInternalSelectedKey] = useState(defaultSelectedKey ?? null);
78
+ const [internalInputValue, setInternalInputValue] = useState(defaultInputValue ?? '');
79
+ const isControlledKey = selectedKey !== undefined;
80
+ const isControlledInput = inputValue !== undefined;
81
+ const effectiveSelectedKey = isControlledKey
82
+ ? selectedKey
83
+ : internalSelectedKey;
84
+ const effectiveInputValue = isControlledInput
85
+ ? inputValue
86
+ : internalInputValue;
87
+ // Popover state
88
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
89
+ // Listen for other menus opening and close this one if needed
90
+ useEffect(() => {
91
+ const unsubscribe = on('popover:open', (data) => {
92
+ if (data.menuId !== comboBoxId && isPopoverOpen) {
93
+ setIsPopoverOpen(false);
94
+ }
95
+ });
96
+ return unsubscribe;
97
+ }, [on, comboBoxId, isPopoverOpen]);
98
+ // Emit event when this combobox opens
99
+ useEffect(() => {
100
+ if (isPopoverOpen) {
101
+ emit('popover:open', { menuId: comboBoxId });
102
+ }
103
+ }, [isPopoverOpen, emit, comboBoxId]);
104
+ return {
105
+ effectiveSelectedKey,
106
+ effectiveInputValue,
107
+ isPopoverOpen,
108
+ setIsPopoverOpen,
109
+ setInternalSelectedKey,
110
+ setInternalInputValue,
111
+ isControlledKey,
112
+ isControlledInput,
113
+ };
114
+ }
115
+ function useComboBoxFiltering({ children, effectiveInputValue, filter, }) {
116
+ const [isFilterActive, setIsFilterActive] = useState(false);
117
+ const { contains } = useFilter({ sensitivity: 'base' });
118
+ const textFilterFn = useMemo(() => (filter === false ? () => true : filter || contains), [filter, contains]);
119
+ // Filter children based on input value
120
+ const filteredChildren = useMemo(() => {
121
+ const term = effectiveInputValue.trim();
122
+ if (!isFilterActive || !term || !children) {
123
+ return children;
124
+ }
125
+ const nodeMatches = (node) => {
126
+ if (!node?.props)
127
+ return false;
128
+ const textValue = node.props.textValue ||
129
+ (typeof node.props.children === 'string' ? node.props.children : '') ||
130
+ String(node.props.children || '');
131
+ return textFilterFn(textValue, term);
132
+ };
133
+ const filterChildren = (childNodes) => {
134
+ if (!childNodes)
135
+ return null;
136
+ const childArray = Array.isArray(childNodes) ? childNodes : [childNodes];
137
+ const filteredNodes = [];
138
+ childArray.forEach((child) => {
139
+ if (!child || typeof child !== 'object') {
140
+ return;
141
+ }
142
+ if (child.type === BaseSection ||
143
+ child.type?.displayName === 'Section') {
144
+ const sectionChildren = Array.isArray(child.props.children)
145
+ ? child.props.children
146
+ : [child.props.children];
147
+ const filteredSectionChildren = sectionChildren.filter((sectionChild) => {
148
+ return (sectionChild &&
149
+ typeof sectionChild === 'object' &&
150
+ nodeMatches(sectionChild));
151
+ });
152
+ if (filteredSectionChildren.length > 0) {
153
+ filteredNodes.push(cloneElement(child, {
154
+ key: child.key,
155
+ children: filteredSectionChildren,
156
+ }));
157
+ }
158
+ }
159
+ else if (child.type === Item) {
160
+ if (nodeMatches(child)) {
161
+ filteredNodes.push(child);
162
+ }
163
+ }
164
+ else if (nodeMatches(child)) {
165
+ filteredNodes.push(child);
166
+ }
167
+ });
168
+ return filteredNodes;
169
+ };
170
+ return filterChildren(children);
171
+ }, [isFilterActive, children, effectiveInputValue, textFilterFn]);
172
+ return {
173
+ filteredChildren,
174
+ isFilterActive,
175
+ setIsFilterActive,
176
+ };
177
+ }
178
+ function useCompositeFocus({ wrapperRef, popoverRef, onFocus, onBlur, isDisabled, }) {
179
+ const wasInsideRef = useRef(false);
180
+ const rafRef = useRef(null);
181
+ const checkFocus = useCallback(() => {
182
+ if (isDisabled)
183
+ return;
184
+ const activeElement = document.activeElement;
185
+ const isInside = (wrapperRef.current?.contains(activeElement) ?? false) ||
186
+ (popoverRef.current?.contains(activeElement) ?? false);
187
+ if (isInside !== wasInsideRef.current) {
188
+ wasInsideRef.current = isInside;
189
+ if (isInside) {
190
+ onFocus?.();
191
+ }
192
+ else {
193
+ onBlur?.();
194
+ }
195
+ }
196
+ }, [wrapperRef, popoverRef, onFocus, onBlur, isDisabled]);
197
+ const handleFocusOrBlur = useCallback((e) => {
198
+ // Cancel any pending check
199
+ if (rafRef.current !== null) {
200
+ cancelAnimationFrame(rafRef.current);
201
+ }
202
+ // Schedule focus check for next frame
203
+ rafRef.current = requestAnimationFrame(() => {
204
+ rafRef.current = null;
205
+ checkFocus();
206
+ });
207
+ }, [checkFocus]);
208
+ // Cleanup on unmount
209
+ useEffect(() => {
210
+ return () => {
211
+ if (rafRef.current !== null) {
212
+ cancelAnimationFrame(rafRef.current);
213
+ }
214
+ };
215
+ }, []);
216
+ return {
217
+ compositeFocusProps: {
218
+ onFocus: handleFocusOrBlur,
219
+ onBlur: handleFocusOrBlur,
220
+ },
221
+ };
222
+ }
223
+ function useComboBoxKeyboard({ isPopoverOpen, listStateRef, hasResults, allowsCustomValue, effectiveInputValue, isClearable, onSelectionChange, onClearValue, onOpenPopover, onClosePopover, inputRef, setIsFilterActive, onKeyDown, }) {
224
+ const { keyboardProps } = useKeyboard({
225
+ onKeyDown: (e) => {
226
+ // Call user's handler first
227
+ onKeyDown?.(e);
228
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
229
+ e.preventDefault();
230
+ // Open popover if closed
231
+ if (!isPopoverOpen) {
232
+ // If opening with no filtered results, disable filter to show all items
233
+ if (!hasResults) {
234
+ setIsFilterActive(false);
235
+ }
236
+ onOpenPopover();
237
+ return;
238
+ }
239
+ const listState = listStateRef.current;
240
+ if (!listState)
241
+ return;
242
+ const { selectionManager, collection, disabledKeys } = listState;
243
+ // Helper to collect visible item keys (supports sections)
244
+ const collectVisibleKeys = (nodes, out) => {
245
+ for (const node of nodes) {
246
+ if (node.type === 'item') {
247
+ if (!disabledKeys?.has(node.key)) {
248
+ out.push(node.key);
249
+ }
250
+ }
251
+ else if (node.childNodes) {
252
+ collectVisibleKeys(node.childNodes, out);
253
+ }
254
+ }
255
+ };
256
+ const visibleKeys = [];
257
+ collectVisibleKeys(collection, visibleKeys);
258
+ if (visibleKeys.length === 0)
259
+ return;
260
+ const isArrowDown = e.key === 'ArrowDown';
261
+ const currentKey = selectionManager.focusedKey;
262
+ let nextKey = null;
263
+ if (currentKey == null) {
264
+ nextKey = isArrowDown
265
+ ? visibleKeys[0]
266
+ : visibleKeys[visibleKeys.length - 1];
267
+ }
268
+ else {
269
+ const currentIndex = visibleKeys.indexOf(currentKey);
270
+ if (currentIndex !== -1) {
271
+ const newIndex = currentIndex + (isArrowDown ? 1 : -1);
272
+ if (newIndex >= 0 && newIndex < visibleKeys.length) {
273
+ nextKey = visibleKeys[newIndex];
274
+ }
275
+ }
276
+ else {
277
+ nextKey = isArrowDown
278
+ ? visibleKeys[0]
279
+ : visibleKeys[visibleKeys.length - 1];
280
+ }
281
+ }
282
+ if (nextKey != null) {
283
+ if (listState.lastFocusSourceRef) {
284
+ listState.lastFocusSourceRef.current = 'keyboard';
285
+ }
286
+ selectionManager.setFocusedKey(nextKey);
287
+ }
288
+ }
289
+ else if (e.key === 'Enter') {
290
+ if (!hasResults) {
291
+ e.preventDefault();
292
+ if (allowsCustomValue) {
293
+ const value = effectiveInputValue;
294
+ onSelectionChange(value ?? null);
295
+ }
296
+ else {
297
+ onSelectionChange(null);
298
+ }
299
+ return;
300
+ }
301
+ if (isPopoverOpen) {
302
+ const listState = listStateRef.current;
303
+ if (!listState)
304
+ return;
305
+ const keyToSelect = listState.selectionManager.focusedKey;
306
+ if (keyToSelect != null) {
307
+ e.preventDefault();
308
+ listState.selectionManager.select(keyToSelect, e);
309
+ // Ensure the popover closes even if selection stays the same
310
+ onClosePopover();
311
+ inputRef.current?.focus();
312
+ }
313
+ }
314
+ }
315
+ else if (e.key === 'Escape') {
316
+ if (isPopoverOpen) {
317
+ e.preventDefault();
318
+ onClosePopover();
319
+ inputRef.current?.focus();
320
+ }
321
+ else if (effectiveInputValue && isClearable) {
322
+ e.preventDefault();
323
+ onClearValue();
324
+ }
325
+ }
326
+ else if (e.key === 'Home' || e.key === 'End') {
327
+ if (isPopoverOpen) {
328
+ e.preventDefault();
329
+ const listState = listStateRef.current;
330
+ if (!listState)
331
+ return;
332
+ const { selectionManager, collection, disabledKeys } = listState;
333
+ // Helper to collect visible item keys (supports sections)
334
+ const collectVisibleKeys = (nodes, out) => {
335
+ for (const node of nodes) {
336
+ if (node.type === 'item') {
337
+ if (!disabledKeys?.has(node.key)) {
338
+ out.push(node.key);
339
+ }
340
+ }
341
+ else if (node.childNodes) {
342
+ collectVisibleKeys(node.childNodes, out);
343
+ }
344
+ }
345
+ };
346
+ const visibleKeys = [];
347
+ collectVisibleKeys(collection, visibleKeys);
348
+ if (visibleKeys.length === 0)
349
+ return;
350
+ const targetKey = e.key === 'Home'
351
+ ? visibleKeys[0]
352
+ : visibleKeys[visibleKeys.length - 1];
353
+ if (listState.lastFocusSourceRef) {
354
+ listState.lastFocusSourceRef.current = 'keyboard';
355
+ }
356
+ selectionManager.setFocusedKey(targetKey);
357
+ }
358
+ }
359
+ },
360
+ });
361
+ return { keyboardProps };
362
+ }
363
+ const ComboBoxInput = forwardRef(function ComboBoxInput({ inputRef, id, value, placeholder, isDisabled, isReadOnly, autoFocus, size, mods, inputStyles, keyboardProps, focusProps, onChange, onFocus, isPopoverOpen, hasResults, comboBoxId, listStateRef, isLoading, allowsCustomValue, }, ref) {
364
+ const combinedRef = useCombinedRefs(ref, inputRef);
365
+ return (_jsx(InputElement, { ref: combinedRef, qa: "Input", id: id, type: "text", value: value, placeholder: placeholder, isDisabled: isDisabled, readOnly: isReadOnly, autoFocus: autoFocus, "data-autofocus": autoFocus ? '' : undefined, onChange: onChange, onFocus: onFocus, onBlur: focusProps.onBlur, ...keyboardProps, styles: inputStyles, mods: mods, "data-size": size, role: "combobox", "aria-expanded": isPopoverOpen && hasResults, "aria-haspopup": "listbox", "aria-controls": isPopoverOpen && hasResults
366
+ ? `ComboBoxListBox-${comboBoxId}`
367
+ : undefined, "aria-activedescendant": isPopoverOpen &&
368
+ hasResults &&
369
+ listStateRef.current?.selectionManager.focusedKey != null
370
+ ? `ListBoxItem-${listStateRef.current?.selectionManager.focusedKey}`
371
+ : undefined }));
372
+ });
373
+ function ComboBoxOverlay({ isOpen, triggerRef, popoverRef, listBoxRef, direction, shouldFlip, overlayOffset, comboBoxWidth, comboBoxId, overlayStyles, listBoxStyles, optionStyles, sectionStyles, headingStyles, effectiveSelectedKey, isDisabled, disabledKeys, items, children, listStateRef, onSelectionChange, onClose, label, ariaLabel, compositeFocusProps, }) {
374
+ // Overlay positioning
375
+ const { overlayProps: overlayPositionProps, placement, updatePosition, } = useOverlayPosition({
376
+ targetRef: triggerRef,
377
+ overlayRef: popoverRef,
378
+ placement: `${direction} start`,
379
+ shouldFlip,
380
+ isOpen,
381
+ offset: overlayOffset,
382
+ });
383
+ // Overlay behavior (dismiss on outside click, escape)
384
+ const { overlayProps: overlayBehaviorProps } = useOverlay({
385
+ onClose,
386
+ shouldCloseOnBlur: true,
387
+ isOpen,
388
+ isDismissable: true,
389
+ shouldCloseOnInteractOutside: (el) => {
390
+ const menuTriggerEl = el.closest('[data-popover-trigger]');
391
+ if (!menuTriggerEl)
392
+ return true;
393
+ if (menuTriggerEl === triggerRef?.current)
394
+ return true;
395
+ return false;
396
+ },
397
+ }, popoverRef);
398
+ // Update position when overlay opens
399
+ useLayoutEffect(() => {
400
+ if (isOpen) {
401
+ // Use double RAF to ensure layout is complete before positioning
402
+ requestAnimationFrame(() => {
403
+ requestAnimationFrame(() => {
404
+ updatePosition?.();
405
+ });
406
+ });
407
+ }
408
+ }, [isOpen, updatePosition]);
409
+ // Extract primary placement direction for consistent styling
410
+ const placementDirection = placement?.split(' ')[0] || direction;
411
+ const overlayContent = (_jsx(DisplayTransition, { exposeUnmounted: true, isShown: isOpen, children: ({ phase, isShown, ref: transitionRef }) => (_jsx(ComboBoxOverlayElement, { ...mergeProps(overlayPositionProps, overlayBehaviorProps, compositeFocusProps), ref: (value) => {
412
+ transitionRef(value);
413
+ popoverRef.current = value;
414
+ }, "data-placement": placementDirection, "data-phase": phase, mods: {
415
+ open: isShown,
416
+ hidden: phase === 'unmounted',
417
+ }, styles: overlayStyles, style: {
418
+ '--min-width': comboBoxWidth ? `${comboBoxWidth}px` : undefined,
419
+ ...overlayPositionProps.style,
420
+ }, children: _jsx(ListBox, { ref: listBoxRef, focusOnHover: true, disableSelectionToggle: true, id: `ComboBoxListBox-${comboBoxId}`, "aria-label": ariaLabel || (typeof label === 'string' ? label : 'Options'), selectedKey: effectiveSelectedKey, selectionMode: "single", isDisabled: isDisabled, disabledKeys: disabledKeys, shouldUseVirtualFocus: true, items: items, styles: listBoxStyles, optionStyles: optionStyles, sectionStyles: sectionStyles, headingStyles: headingStyles, stateRef: listStateRef, mods: {
421
+ popover: true,
422
+ }, onSelectionChange: onSelectionChange, children: children }) })) }));
423
+ return _jsx(Portal, { children: overlayContent });
424
+ }
425
+ // ============================================================================
426
+ // Main Component: ComboBox
427
+ // ============================================================================
66
428
  export const ComboBox = forwardRef(function ComboBox(props, ref) {
67
429
  props = useProviderProps(props);
68
430
  props = useFormProps(props);
69
431
  props = useFieldProps(props, {
70
432
  valuePropsMapper: ({ value, onChange }) => {
71
433
  return {
72
- selectedKey: !props.allowsCustomValue ? value ?? null : undefined,
73
- inputValue: props.allowsCustomValue ? value ?? '' : undefined,
74
- onInputChange(val) {
75
- if (!props.allowsCustomValue) {
76
- return;
77
- }
78
- onChange(val);
79
- },
434
+ // Form value maps to selectedKey (the committed value) in both modes
435
+ selectedKey: value ?? null,
80
436
  onSelectionChange(val) {
81
- if (val == null && props.allowsCustomValue) {
82
- return;
83
- }
437
+ // Commit selection changes to the form
84
438
  onChange(val);
85
439
  },
86
440
  };
87
441
  },
88
442
  });
89
- let { qa, label, extra, labelStyles, isRequired, necessityIndicator, validationState, icon, prefix, isDisabled, multiLine, autoFocus, wrapperRef, inputRef, triggerRef, popoverRef, listBoxRef, isLoading, loadingIndicator, overlayOffset = 8, inputStyles, optionStyles, triggerStyles, listBoxStyles, overlayStyles, wrapperStyles, suffix, hideTrigger, message, description, size = 'medium', autoComplete = 'off', direction = 'bottom', shouldFlip = true, menuTrigger = 'input', suffixPosition = 'before', loadingState, filter, styles, labelSuffix, selectedKey, defaultSelectedKey, isClearable, ...otherProps } = props;
90
- let isAsync = loadingState != null;
91
- let { contains } = useFilter({ sensitivity: 'base' });
92
- let comboBoxStateProps = {
93
- ...props,
94
- defaultFilter: filter || contains,
95
- filter: undefined,
96
- allowsEmptyCollection: isAsync,
97
- menuTrigger,
98
- };
99
- let state = useComboBoxState(comboBoxStateProps);
443
+ let { qa, label, extra, labelStyles, isRequired, necessityIndicator, validationState, id, icon, prefix, isDisabled, autoFocus, wrapperRef, inputRef, triggerRef, popoverRef, listBoxRef, isLoading, inputStyles, optionStyles, triggerStyles, listBoxStyles, overlayStyles, fieldStyles, suffix, hideTrigger, message, description, size = 'medium', direction = 'bottom', shouldFlip = true, popoverTrigger = 'input', suffixPosition = 'before', filter, styles, labelSuffix, selectedKey, defaultSelectedKey, inputValue, defaultInputValue, onInputChange, isClearable, onClear, placeholder, allowsCustomValue, shouldCommitOnBlur = true, clearOnBlur, items, children: renderChildren, sectionStyles, headingStyles, isReadOnly, overlayOffset = 8, onSelectionChange: externalOnSelectionChange, sortSelectedToTop: sortSelectedToTopProp, onFocus, onBlur, onKeyDown, form, ...otherProps } = props;
100
444
  // Generate a unique ID for this combobox instance
101
445
  const comboBoxId = useMemo(() => generateRandomId(), []);
102
- // Get event bus for menu synchronization
103
- const { emit, on } = useEventBus();
104
- // Listen for other menus opening and close this one if needed
105
- useEffect(() => {
106
- const unsubscribe = on('popover:open', (data) => {
107
- // If another menu is opening and this combobox is open, close this one
108
- if (data.menuId !== comboBoxId && state.isOpen) {
109
- state.close();
110
- }
111
- });
112
- return unsubscribe;
113
- }, [on, comboBoxId, state]);
114
- // Emit event when this combobox opens
115
- useEffect(() => {
116
- if (state.isOpen) {
117
- emit('popover:open', { menuId: comboBoxId });
118
- }
119
- }, [state.isOpen, emit, comboBoxId]);
446
+ // State management hook
447
+ const { effectiveSelectedKey, effectiveInputValue, isPopoverOpen, setIsPopoverOpen, setInternalSelectedKey, setInternalInputValue, isControlledKey, isControlledInput, } = useComboBoxState({
448
+ selectedKey,
449
+ defaultSelectedKey,
450
+ inputValue,
451
+ defaultInputValue,
452
+ comboBoxId,
453
+ });
454
+ // Track if sortSelectedToTop was explicitly provided
455
+ const sortSelectedToTopExplicit = sortSelectedToTopProp !== undefined;
456
+ // Default to true if items are provided, false otherwise
457
+ const sortSelectedToTop = sortSelectedToTopProp ?? (items ? true : false);
458
+ // Cache for sorted items array when using `items` prop
459
+ const cachedItemsOrder = useRef(null);
460
+ const selectionWhenClosed = useRef(null);
120
461
  styles = extractStyles(otherProps, PROP_STYLES, styles);
121
462
  ref = useCombinedRefs(ref);
122
463
  wrapperRef = useCombinedRefs(wrapperRef);
@@ -124,62 +465,320 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
124
465
  triggerRef = useCombinedRefs(triggerRef);
125
466
  popoverRef = useCombinedRefs(popoverRef);
126
467
  listBoxRef = useCombinedRefs(listBoxRef);
127
- let { overlayProps, placement, updatePosition } = useOverlayPosition({
128
- targetRef: triggerRef,
129
- overlayRef: popoverRef,
130
- scrollRef: listBoxRef,
131
- placement: `${direction} end`,
132
- shouldFlip: shouldFlip,
133
- isOpen: state.isOpen,
134
- onClose: state.close,
135
- offset: overlayOffset,
468
+ // Sort items with selected on top if enabled
469
+ const getSortedItems = useCallback(() => {
470
+ if (!items)
471
+ return items;
472
+ if (!sortSelectedToTop)
473
+ return items;
474
+ // Reuse cached order if available
475
+ if (cachedItemsOrder.current) {
476
+ return cachedItemsOrder.current;
477
+ }
478
+ // Warn if explicitly requested but not supported
479
+ if (sortSelectedToTopExplicit && !items) {
480
+ console.warn('ComboBox: sortSelectedToTop only works with the items prop. ' +
481
+ 'Sorting will be skipped when using JSX children.');
482
+ return items;
483
+ }
484
+ const selectedKey = isPopoverOpen
485
+ ? effectiveSelectedKey
486
+ : selectionWhenClosed.current;
487
+ if (!selectedKey)
488
+ return items;
489
+ const itemsArray = Array.isArray(items) ? items : Array.from(items);
490
+ const selectedItem = itemsArray.find((item) => {
491
+ const key = item?.key ?? item?.id;
492
+ return key != null && String(key) === String(selectedKey);
493
+ });
494
+ if (!selectedItem)
495
+ return items;
496
+ const sorted = [
497
+ selectedItem,
498
+ ...itemsArray.filter((item) => {
499
+ const key = item?.key ?? item?.id;
500
+ return key == null || String(key) !== String(selectedKey);
501
+ }),
502
+ ];
503
+ if (isPopoverOpen) {
504
+ cachedItemsOrder.current = sorted;
505
+ }
506
+ return sorted;
507
+ }, [
508
+ items,
509
+ sortSelectedToTop,
510
+ sortSelectedToTopExplicit,
511
+ effectiveSelectedKey,
512
+ isPopoverOpen,
513
+ ]);
514
+ const sortedItems = getSortedItems();
515
+ // Preserve the original `children` (may be a render function) before we
516
+ // potentially overwrite it.
517
+ let children = renderChildren;
518
+ const renderFn = renderChildren;
519
+ if (sortedItems && typeof renderFn === 'function') {
520
+ try {
521
+ const itemsArray = Array.from(sortedItems);
522
+ children = itemsArray.map((item, idx) => {
523
+ const rendered = renderFn(item);
524
+ if (React.isValidElement(rendered) &&
525
+ rendered.key == null) {
526
+ return React.cloneElement(rendered, {
527
+ key: rendered?.key ?? item?.key ?? idx,
528
+ });
529
+ }
530
+ return rendered;
531
+ });
532
+ }
533
+ catch {
534
+ // If conversion fails, proceed with the original children
535
+ }
536
+ }
537
+ // Invalidate cached sorting whenever items change
538
+ useEffect(() => {
539
+ cachedItemsOrder.current = null;
540
+ }, [items]);
541
+ // Capture selection when popover closes
542
+ useEffect(() => {
543
+ if (!isPopoverOpen) {
544
+ selectionWhenClosed.current =
545
+ effectiveSelectedKey != null ? String(effectiveSelectedKey) : null;
546
+ cachedItemsOrder.current = null;
547
+ }
548
+ }, [isPopoverOpen, effectiveSelectedKey]);
549
+ // Filtering hook
550
+ const { filteredChildren, isFilterActive, setIsFilterActive } = useComboBoxFiltering({
551
+ children,
552
+ effectiveInputValue,
553
+ filter,
136
554
  });
137
- let { labelProps, inputProps, listBoxProps, buttonProps: triggerProps, } = useComboBox({
138
- ...comboBoxStateProps,
139
- inputRef,
140
- buttonRef: triggerRef,
141
- listBoxRef,
142
- popoverRef,
143
- menuTrigger,
144
- }, state);
145
- let { isFocused, focusProps } = useFocus({ isDisabled });
146
- let { hoverProps, isHovered } = useHover({ isDisabled });
147
- // Get props for the button based on the trigger props from useComboBox
148
- let { buttonProps, isPressed: isTriggerPressed } = useButton(triggerProps, triggerRef);
149
- let { hoverProps: triggerHoverProps, isHovered: isTriggerHovered } = useHover({ isDisabled });
150
- let { focusProps: triggerFocusProps, isFocused: isTriggerFocused } = useFocus({ isDisabled }, true);
151
- useLayoutEffect(() => {
152
- if (state.isOpen) {
153
- updatePosition();
555
+ // Freeze filtered children during close animation to prevent visual jumps
556
+ const frozenFilteredChildrenRef = useRef(null);
557
+ useEffect(() => {
558
+ // Update frozen children only when popover is open
559
+ if (isPopoverOpen) {
560
+ frozenFilteredChildrenRef.current = filteredChildren;
561
+ }
562
+ }, [isPopoverOpen, filteredChildren]);
563
+ // Use frozen children during close animation, fresh children when open
564
+ const displayedFilteredChildren = isPopoverOpen
565
+ ? filteredChildren
566
+ : frozenFilteredChildrenRef.current ?? filteredChildren;
567
+ const { isFocused, focusProps } = useFocus({ isDisabled });
568
+ // Composite blur handler - fires when focus leaves the entire component
569
+ const handleCompositeBlur = useEvent(() => {
570
+ // Always disable filter on blur
571
+ setIsFilterActive(false);
572
+ // In allowsCustomValue mode with shouldCommitOnBlur, commit the input value
573
+ if (allowsCustomValue &&
574
+ shouldCommitOnBlur &&
575
+ effectiveInputValue &&
576
+ effectiveSelectedKey == null) {
577
+ externalOnSelectionChange?.(effectiveInputValue);
578
+ if (!isControlledKey) {
579
+ setInternalSelectedKey(effectiveInputValue);
580
+ }
581
+ // Call user's onBlur callback
582
+ onBlur?.();
583
+ return;
584
+ }
585
+ // In clearOnBlur mode (only for non-custom-value mode), clear selection and input
586
+ if (clearOnBlur && !allowsCustomValue) {
587
+ externalOnSelectionChange?.(null);
588
+ if (!isControlledKey) {
589
+ setInternalSelectedKey(null);
590
+ }
591
+ if (!isControlledInput) {
592
+ setInternalInputValue('');
593
+ }
594
+ onInputChange?.('');
595
+ // Call user's onBlur callback
596
+ onBlur?.();
597
+ return;
598
+ }
599
+ // Reset input to show current selection (or empty if none)
600
+ const nextValue = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : '';
601
+ if (!isControlledInput) {
602
+ setInternalInputValue(nextValue);
154
603
  }
155
- }, [state.isOpen, state.collection.size]);
604
+ onInputChange?.(nextValue);
605
+ // Call user's onBlur callback
606
+ onBlur?.();
607
+ });
608
+ // Composite focus hook - handles focus tracking across wrapper and portaled popover
609
+ const { compositeFocusProps } = useCompositeFocus({
610
+ wrapperRef,
611
+ popoverRef,
612
+ onFocus,
613
+ onBlur: handleCompositeBlur,
614
+ isDisabled,
615
+ });
156
616
  let isInvalid = validationState === 'invalid';
157
617
  let validationIcon = isInvalid ? InvalidIcon : ValidIcon;
158
618
  let validation = cloneElement(validationIcon);
619
+ // Ref to access internal ListBox state
620
+ const listStateRef = useRef(null);
621
+ const focusInitAttemptsRef = useRef(0);
622
+ // Helper to get label from collection item
623
+ const getItemLabel = useCallback((key) => {
624
+ const item = listStateRef.current?.collection?.getItem(key);
625
+ return item?.textValue || String(key);
626
+ }, []);
627
+ // Selection change handler
628
+ const handleSelectionChange = useEvent((selection) => {
629
+ // Extract single key from selection (we only support single selection)
630
+ const key = Array.isArray(selection) ? selection[0] : selection;
631
+ // Update selected key
632
+ if (!isControlledKey) {
633
+ setInternalSelectedKey(key ?? null);
634
+ }
635
+ // Update input value to show selected item label
636
+ if (key != null) {
637
+ setIsFilterActive(false);
638
+ const label = getItemLabel(key);
639
+ if (!isControlledInput) {
640
+ setInternalInputValue(label);
641
+ }
642
+ onInputChange?.(label);
643
+ }
644
+ else {
645
+ // Clear input when selection is cleared
646
+ if (!isControlledInput) {
647
+ setInternalInputValue('');
648
+ }
649
+ onInputChange?.('');
650
+ setIsFilterActive(false);
651
+ }
652
+ externalOnSelectionChange?.(key);
653
+ // Close popover after selection
654
+ setIsPopoverOpen(false);
655
+ // Focus input
656
+ setTimeout(() => {
657
+ inputRef.current?.focus();
658
+ }, 0);
659
+ });
660
+ // Input change handler
661
+ const handleInputChange = useEvent((e) => {
662
+ const value = e.target.value;
663
+ // Update input value
664
+ if (!isControlledInput) {
665
+ setInternalInputValue(value);
666
+ }
667
+ onInputChange?.(value);
668
+ const trimmed = value.trim();
669
+ setIsFilterActive(trimmed.length > 0);
670
+ // Only clear selection in allowsCustomValue mode
671
+ // In normal mode, typing just filters - selection stays until explicitly changed
672
+ if (allowsCustomValue && effectiveSelectedKey != null) {
673
+ if (!isControlledKey) {
674
+ setInternalSelectedKey(null);
675
+ }
676
+ externalOnSelectionChange?.(null);
677
+ }
678
+ // Open popover based on trigger
679
+ if (popoverTrigger !== 'manual' && value && !isPopoverOpen) {
680
+ setIsPopoverOpen(true);
681
+ }
682
+ });
683
+ // Initialize input value from defaultInputValue or defaultSelectedKey (uncontrolled mode only, one-time)
684
+ const [hasInitialized, setHasInitialized] = useState(false);
685
+ useEffect(() => {
686
+ // Only initialize once, in uncontrolled input mode
687
+ if (hasInitialized || isControlledInput)
688
+ return;
689
+ // Priority 1: defaultInputValue takes precedence
690
+ if (defaultInputValue !== undefined) {
691
+ setInternalInputValue(defaultInputValue);
692
+ setHasInitialized(true);
693
+ return;
694
+ }
695
+ // Priority 2: fall back to defaultSelectedKey's label
696
+ if (defaultSelectedKey) {
697
+ // Wait for collection to be ready
698
+ if (!listStateRef.current?.collection)
699
+ return;
700
+ const label = getItemLabel(defaultSelectedKey);
701
+ setInternalInputValue(label);
702
+ setHasInitialized(true);
703
+ }
704
+ }, [
705
+ hasInitialized,
706
+ isControlledInput,
707
+ defaultInputValue,
708
+ defaultSelectedKey,
709
+ getItemLabel,
710
+ children,
711
+ ]);
712
+ // Sync input value with controlled selectedKey
713
+ useEffect(() => {
714
+ // Only run when selectedKey is controlled but inputValue is uncontrolled
715
+ if (!isControlledKey || isControlledInput)
716
+ return;
717
+ // Wait for collection to be ready
718
+ if (!listStateRef.current?.collection)
719
+ return;
720
+ // Get the expected label for the current selection
721
+ const expectedLabel = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : '';
722
+ // Update the input value to match the selected key's label
723
+ setInternalInputValue(expectedLabel);
724
+ }, [isControlledKey, isControlledInput, effectiveSelectedKey, getItemLabel]);
725
+ // Input focus handler
726
+ const handleInputFocus = useEvent((e) => {
727
+ // Call focus props handler if it exists
728
+ focusProps.onFocus?.(e);
729
+ if (popoverTrigger === 'focus' && !isPopoverOpen) {
730
+ setIsPopoverOpen(true);
731
+ }
732
+ });
733
+ // Input blur handler - just handles internal focus props
734
+ const handleInputBlur = useEvent((e) => {
735
+ focusProps.onBlur?.(e);
736
+ });
159
737
  // Clear button logic
160
- let hasValue = props.allowsCustomValue
161
- ? state.inputValue !== ''
162
- : state.selectedKey != null;
163
- let showClearButton = isClearable && hasValue && !isDisabled && !props.isReadOnly;
738
+ let hasValue = allowsCustomValue
739
+ ? effectiveInputValue !== ''
740
+ : effectiveSelectedKey != null;
741
+ let showClearButton = isClearable && hasValue && !isDisabled && !isReadOnly;
742
+ const hasResults = Boolean(displayedFilteredChildren &&
743
+ (Array.isArray(displayedFilteredChildren)
744
+ ? displayedFilteredChildren.length > 0
745
+ : displayedFilteredChildren !== null));
164
746
  // Clear function
165
747
  let clearValue = useEvent(() => {
166
- // Always clear input value in state so UI resets to placeholder
167
- state.setInputValue('');
168
- // Notify external input value only when custom value mode is enabled
169
- if (props.allowsCustomValue) {
170
- props.onInputChange?.('');
748
+ // Clear input
749
+ if (!isControlledInput) {
750
+ setInternalInputValue('');
171
751
  }
172
- props.onSelectionChange?.(null);
173
- state.setSelectedKey(null);
174
- // Close the popup if it's open
175
- if (state.isOpen) {
176
- state.close();
752
+ onInputChange?.('');
753
+ // Clear selection
754
+ if (!isControlledKey) {
755
+ setInternalSelectedKey(null);
177
756
  }
178
- // Focus back to the input
757
+ externalOnSelectionChange?.(null);
758
+ // Close popover
759
+ if (isPopoverOpen) {
760
+ setIsPopoverOpen(false);
761
+ }
762
+ // Focus input
179
763
  inputRef.current?.focus();
180
- props.onClear?.();
764
+ onClear?.();
765
+ });
766
+ // Keyboard navigation hook
767
+ const { keyboardProps } = useComboBoxKeyboard({
768
+ isPopoverOpen,
769
+ listStateRef,
770
+ hasResults,
771
+ allowsCustomValue,
772
+ effectiveInputValue,
773
+ isClearable,
774
+ onSelectionChange: handleSelectionChange,
775
+ onClearValue: clearValue,
776
+ onOpenPopover: () => setIsPopoverOpen(true),
777
+ onClosePopover: () => setIsPopoverOpen(false),
778
+ inputRef,
779
+ setIsFilterActive,
780
+ onKeyDown,
181
781
  });
182
- let comboBoxWidth = wrapperRef?.current?.offsetWidth;
183
782
  if (icon) {
184
783
  icon = _jsx("div", { "data-element": "InputIcon", children: icon });
185
784
  if (prefix) {
@@ -193,7 +792,7 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
193
792
  invalid: isInvalid,
194
793
  valid: validationState === 'valid',
195
794
  disabled: isDisabled,
196
- hovered: isHovered,
795
+ hovered: false,
197
796
  focused: isFocused,
198
797
  loading: isLoading,
199
798
  prefix: !!prefix,
@@ -203,84 +802,97 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
203
802
  isInvalid,
204
803
  validationState,
205
804
  isDisabled,
206
- isHovered,
207
805
  isFocused,
208
806
  isLoading,
209
807
  prefix,
210
808
  showClearButton,
211
809
  ]);
212
- // If input is not full and the user presses Enter, pick the first option.
213
- let onKeyPress = useEvent((e) => {
214
- if (!props.onSelectionChange) {
215
- return;
810
+ const comboBoxWidth = wrapperRef?.current?.offsetWidth;
811
+ const shouldShowPopover = Boolean(isPopoverOpen && hasResults);
812
+ // Close popover if no results
813
+ useEffect(() => {
814
+ if (isPopoverOpen && !hasResults) {
815
+ setIsPopoverOpen(false);
216
816
  }
217
- if (e.key === 'Enter') {
218
- if (!props.allowsCustomValue) {
219
- if (state.isOpen) {
220
- // If there is a selected option then do nothing. It will be selected on Enter anyway.
221
- if (listBoxRef.current?.querySelector('li[aria-selected="true"]')) {
222
- return;
223
- }
224
- const option = [...state.collection][0]?.key;
225
- if (option && selectedKey !== option) {
226
- props.onSelectionChange?.(option);
227
- e.stopPropagation();
228
- e.preventDefault();
229
- }
817
+ }, [isPopoverOpen, hasResults]);
818
+ const ensureInitialFocus = useCallback(() => {
819
+ if (!shouldShowPopover)
820
+ return;
821
+ const listState = listStateRef.current;
822
+ if (!listState)
823
+ return;
824
+ const { selectionManager, collection, disabledKeys, lastFocusSourceRef } = listState;
825
+ if (!selectionManager || !collection)
826
+ return;
827
+ const collectFirstKey = () => {
828
+ for (const node of collection) {
829
+ if (node.type === 'item') {
830
+ if (!disabledKeys?.has(node.key))
831
+ return node.key;
230
832
  }
231
- else if (inputRef.current?.value &&
232
- ![...state.collection]
233
- .map((i) => i.textValue)
234
- .includes(inputRef.current?.value)) {
235
- // If the input value is not in the collection, we need to prevent the submitting of the form.
236
- // Also, we reset value manually.
237
- e.preventDefault();
238
- props.onSelectionChange?.(null);
833
+ else if (node.childNodes) {
834
+ for (const child of node.childNodes) {
835
+ if (child.type === 'item' && !disabledKeys?.has(child.key)) {
836
+ return child.key;
837
+ }
838
+ }
239
839
  }
240
- // If a custom value is allowed, we need to check if the input value is in the collection.
241
840
  }
242
- else if (props.allowsCustomValue) {
243
- const inputValue = inputRef?.current?.value;
244
- const item = [...state.collection].find((item) => item.textValue.toLowerCase() === inputValue?.toLowerCase());
245
- props.onSelectionChange?.(item ? item.key : inputRef?.current?.value ?? '');
246
- }
247
- }
248
- });
249
- let onBlur = useEvent((e) => {
250
- // If the input value is not in the collection, we need to reset the value.
251
- if (!props.allowsCustomValue &&
252
- inputRef.current?.value &&
253
- ![...state.collection]
254
- .map((i) => i.textValue)
255
- .includes(inputRef.current?.value)) {
256
- props.onSelectionChange?.(null);
841
+ return null;
842
+ };
843
+ if (lastFocusSourceRef)
844
+ lastFocusSourceRef.current = 'keyboard';
845
+ if (selectionManager.focusedKey == null) {
846
+ const keyToFocus = effectiveSelectedKey != null ? effectiveSelectedKey : collectFirstKey();
847
+ if (keyToFocus != null)
848
+ selectionManager.setFocusedKey(keyToFocus);
257
849
  }
258
- });
259
- useEffect(() => {
260
- inputRef.current?.addEventListener('keydown', onKeyPress, true);
261
- inputRef.current?.addEventListener('blur', onBlur, true);
262
- return () => {
263
- inputRef.current?.removeEventListener('keydown', onKeyPress, true);
264
- inputRef.current?.removeEventListener('blur', onBlur, true);
850
+ }, [shouldShowPopover, effectiveSelectedKey]);
851
+ useLayoutEffect(() => {
852
+ if (!shouldShowPopover)
853
+ return;
854
+ focusInitAttemptsRef.current = 0;
855
+ const tick = () => {
856
+ if (!shouldShowPopover)
857
+ return;
858
+ ensureInitialFocus();
859
+ focusInitAttemptsRef.current += 1;
860
+ // Try a few frames to wait for collection to mount
861
+ if (focusInitAttemptsRef.current < 8 &&
862
+ listStateRef.current?.selectionManager?.focusedKey == null) {
863
+ requestAnimationFrame(tick);
864
+ }
265
865
  };
266
- }, []);
267
- let allInputProps = useMemo(() => mergeProps(inputProps, hoverProps, focusProps), [inputProps, hoverProps, focusProps]);
268
- let comboBoxField = (_jsxs(ComboBoxWrapperElement, { ref: wrapperRef, qa: qa || 'ComboBox', mods: mods, styles: wrapperStyles, style: {
866
+ requestAnimationFrame(() => requestAnimationFrame(tick));
867
+ }, [shouldShowPopover, ensureInitialFocus]);
868
+ const comboBoxField = (_jsxs(ComboBoxWrapperElement, { ref: wrapperRef, qa: qa || 'ComboBox', mods: mods, styles: styles, style: {
269
869
  zIndex: isFocused ? 1 : 'initial',
270
- }, "data-size": size, children: [prefix ? _jsx("div", { "data-element": "Prefix", children: prefix }) : null, _jsx(InputElement, { ref: inputRef, qa: "Input", autoFocus: autoFocus, "data-autofocus": autoFocus ? '' : undefined, ...allInputProps, autoComplete: autoComplete, styles: inputStyles, mods: mods, "data-size": size }), _jsxs("div", { "data-element": "Suffix", children: [suffixPosition === 'before' ? suffix : null, validationState || isLoading ? (_jsxs(_Fragment, { children: [validationState && !isLoading ? validation : null, isLoading ? _jsx(LoadingIcon, {}) : null] })) : null, suffixPosition === 'after' ? suffix : null, showClearButton && (_jsx(ItemAction, { icon: _jsx(CloseIcon, {}), size: size, theme: validationState === 'invalid' ? 'danger' : undefined, qa: "ComboBoxClearButton", "data-no-trigger": hideTrigger ? '' : undefined, onPress: clearValue })), !hideTrigger ? (_jsx(TriggerElement, { "data-popover-trigger": true, qa: "ComboBoxTrigger", ...mergeProps(buttonProps, triggerFocusProps, triggerHoverProps), ref: triggerRef, mods: {
271
- pressed: isTriggerPressed,
272
- focused: isTriggerFocused,
273
- hovered: isTriggerHovered,
870
+ }, "data-size": size, ...compositeFocusProps, children: [prefix ? _jsx("div", { "data-element": "Prefix", children: prefix }) : null, _jsx(ComboBoxInput, { inputRef: inputRef, id: id, value: effectiveInputValue, placeholder: placeholder, isDisabled: isDisabled, isReadOnly: isReadOnly, autoFocus: autoFocus, size: size, mods: mods, inputStyles: inputStyles, keyboardProps: keyboardProps, focusProps: { ...focusProps, onBlur: handleInputBlur }, isPopoverOpen: isPopoverOpen, hasResults: hasResults, comboBoxId: comboBoxId, listStateRef: listStateRef, isLoading: isLoading, allowsCustomValue: allowsCustomValue, onChange: handleInputChange, onFocus: handleInputFocus }), _jsxs("div", { "data-element": "Suffix", children: [suffixPosition === 'before' ? suffix : null, validationState || isLoading ? (_jsxs(_Fragment, { children: [validationState && !isLoading ? validation : null, isLoading ? _jsx(LoadingIcon, {}) : null] })) : null, suffixPosition === 'after' ? suffix : null, showClearButton && (_jsx(ItemAction, { icon: _jsx(CloseIcon, {}), size: size, theme: validationState === 'invalid' ? 'danger' : undefined, qa: "ComboBoxClearButton", "data-no-trigger": hideTrigger ? '' : undefined, "aria-label": "Clear value", onPress: clearValue })), !hideTrigger ? (_jsx(ItemAction, { ref: triggerRef, "data-popover-trigger": true, icon: _jsx(DirectionIcon, { to: isPopoverOpen ? 'up' : 'down' }), qa: "ComboBoxTrigger", mods: {
871
+ pressed: isPopoverOpen,
274
872
  disabled: isDisabled,
275
873
  loading: isLoading,
276
- }, "data-size": size, isDisabled: isDisabled, styles: triggerStyles, children: _jsx(DownIcon, {}) })) : null] }), _jsx(OverlayWrapper, { isOpen: state.isOpen && !isDisabled, children: _jsx(ListBoxPopup, { ...listBoxProps, shouldUseVirtualFocus: true, listBoxRef: listBoxRef, popoverRef: popoverRef, overlayProps: overlayProps, placement: placement, state: state, listBoxStyles: listBoxStyles, overlayStyles: overlayStyles, optionStyles: optionStyles, minWidth: comboBoxWidth, triggerRef: triggerRef }) })] }));
277
- return wrapWithField(comboBoxField, ref, mergeProps({ ...props, styles }, { labelProps }));
278
- });
279
- const ComboBoxSectionComponent = Object.assign(BaseSection, {
280
- displayName: 'Section',
874
+ }, "data-size": size, isDisabled: isDisabled, styles: triggerStyles, "aria-expanded": isPopoverOpen, "aria-haspopup": "listbox", "aria-label": "Show options", onPress: () => {
875
+ if (!isDisabled) {
876
+ const willOpen = !isPopoverOpen;
877
+ setIsPopoverOpen(willOpen);
878
+ if (willOpen) {
879
+ inputRef.current?.focus();
880
+ // If opening with no filtered results, disable filter to show all items
881
+ if (!hasResults) {
882
+ setIsFilterActive(false);
883
+ }
884
+ }
885
+ }
886
+ } })) : null] }), _jsx(ComboBoxOverlay, { isOpen: shouldShowPopover, triggerRef: wrapperRef, popoverRef: popoverRef, listBoxRef: listBoxRef, direction: direction, shouldFlip: shouldFlip, overlayOffset: overlayOffset, comboBoxWidth: comboBoxWidth, comboBoxId: comboBoxId, overlayStyles: overlayStyles, listBoxStyles: listBoxStyles, optionStyles: optionStyles, sectionStyles: sectionStyles, headingStyles: headingStyles, effectiveSelectedKey: effectiveSelectedKey, isDisabled: isDisabled, disabledKeys: props.disabledKeys, items: sortedItems, listStateRef: listStateRef, label: label, ariaLabel: props['aria-label'], compositeFocusProps: compositeFocusProps, onSelectionChange: handleSelectionChange, onClose: () => setIsPopoverOpen(false), children: displayedFilteredChildren })] }));
887
+ const { children: _, ...propsWithoutChildren } = props;
888
+ const finalProps = {
889
+ ...propsWithoutChildren,
890
+ styles: fieldStyles,
891
+ };
892
+ return wrapWithField(comboBoxField, ref, mergeProps(finalProps, {}));
281
893
  });
282
894
  ComboBox.Item = Item;
283
- ComboBox.Section = ComboBoxSectionComponent;
895
+ ComboBox.Section = BaseSection;
284
896
  Object.defineProperty(ComboBox, 'cubeInputType', {
285
897
  value: 'ComboBox',
286
898
  enumerable: false,