@cube-dev/ui-kit 0.81.0 → 0.82.0

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 (522) hide show
  1. package/CHANGELOG.md +12 -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 +1 -1
  81. package/es/components/fields/Checkbox/CheckboxGroup.js +1 -1
  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 +796 -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 +1 -1
  103. package/es/components/fields/FilterListBox/FilterListBox.js +47 -47
  104. package/es/components/fields/FilterListBox/index.js +1 -1
  105. package/es/components/fields/FilterPicker/FilterPicker.js +93 -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 +35 -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 +1 -1
  117. package/es/components/fields/RadioGroup/RadioGroup.js +1 -1
  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 +72 -53
  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 +1 -1
  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 +1 -1
  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 +1 -1
  141. package/es/components/fields/TextInput/index.js +1 -1
  142. package/es/components/fields/TextInputMapper/TextInputMapper.js +2 -2
  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 +1 -1
  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/utils/react/index.d.ts +1 -1
  522. 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.0
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, useId, 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,435 @@ 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, ...otherProps } = props;
444
+ // Generate ID for label-input linking if not provided
445
+ const generatedId = useId();
446
+ const inputId = id || generatedId;
100
447
  // Generate a unique ID for this combobox instance
101
448
  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]);
449
+ // State management hook
450
+ const { effectiveSelectedKey, effectiveInputValue, isPopoverOpen, setIsPopoverOpen, setInternalSelectedKey, setInternalInputValue, isControlledKey, isControlledInput, } = useComboBoxState({
451
+ selectedKey,
452
+ defaultSelectedKey,
453
+ inputValue,
454
+ defaultInputValue,
455
+ comboBoxId,
456
+ });
457
+ // Track if sortSelectedToTop was explicitly provided
458
+ const sortSelectedToTopExplicit = sortSelectedToTopProp !== undefined;
459
+ // Default to true if items are provided, false otherwise
460
+ const sortSelectedToTop = sortSelectedToTopProp ?? (items ? true : false);
461
+ // Cache for sorted items array when using `items` prop
462
+ const cachedItemsOrder = useRef(null);
463
+ const selectionWhenClosed = useRef(null);
120
464
  styles = extractStyles(otherProps, PROP_STYLES, styles);
121
465
  ref = useCombinedRefs(ref);
122
466
  wrapperRef = useCombinedRefs(wrapperRef);
@@ -124,62 +468,320 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
124
468
  triggerRef = useCombinedRefs(triggerRef);
125
469
  popoverRef = useCombinedRefs(popoverRef);
126
470
  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,
471
+ // Sort items with selected on top if enabled
472
+ const getSortedItems = useCallback(() => {
473
+ if (!items)
474
+ return items;
475
+ if (!sortSelectedToTop)
476
+ return items;
477
+ // Reuse cached order if available
478
+ if (cachedItemsOrder.current) {
479
+ return cachedItemsOrder.current;
480
+ }
481
+ // Warn if explicitly requested but not supported
482
+ if (sortSelectedToTopExplicit && !items) {
483
+ console.warn('ComboBox: sortSelectedToTop only works with the items prop. ' +
484
+ 'Sorting will be skipped when using JSX children.');
485
+ return items;
486
+ }
487
+ const selectedKey = isPopoverOpen
488
+ ? effectiveSelectedKey
489
+ : selectionWhenClosed.current;
490
+ if (!selectedKey)
491
+ return items;
492
+ const itemsArray = Array.isArray(items) ? items : Array.from(items);
493
+ const selectedItem = itemsArray.find((item) => {
494
+ const key = item?.key ?? item?.id;
495
+ return key != null && String(key) === String(selectedKey);
496
+ });
497
+ if (!selectedItem)
498
+ return items;
499
+ const sorted = [
500
+ selectedItem,
501
+ ...itemsArray.filter((item) => {
502
+ const key = item?.key ?? item?.id;
503
+ return key == null || String(key) !== String(selectedKey);
504
+ }),
505
+ ];
506
+ if (isPopoverOpen) {
507
+ cachedItemsOrder.current = sorted;
508
+ }
509
+ return sorted;
510
+ }, [
511
+ items,
512
+ sortSelectedToTop,
513
+ sortSelectedToTopExplicit,
514
+ effectiveSelectedKey,
515
+ isPopoverOpen,
516
+ ]);
517
+ const sortedItems = getSortedItems();
518
+ // Preserve the original `children` (may be a render function) before we
519
+ // potentially overwrite it.
520
+ let children = renderChildren;
521
+ const renderFn = renderChildren;
522
+ if (sortedItems && typeof renderFn === 'function') {
523
+ try {
524
+ const itemsArray = Array.from(sortedItems);
525
+ children = itemsArray.map((item, idx) => {
526
+ const rendered = renderFn(item);
527
+ if (React.isValidElement(rendered) &&
528
+ rendered.key == null) {
529
+ return React.cloneElement(rendered, {
530
+ key: rendered?.key ?? item?.key ?? idx,
531
+ });
532
+ }
533
+ return rendered;
534
+ });
535
+ }
536
+ catch {
537
+ // If conversion fails, proceed with the original children
538
+ }
539
+ }
540
+ // Invalidate cached sorting whenever items change
541
+ useEffect(() => {
542
+ cachedItemsOrder.current = null;
543
+ }, [items]);
544
+ // Capture selection when popover closes
545
+ useEffect(() => {
546
+ if (!isPopoverOpen) {
547
+ selectionWhenClosed.current =
548
+ effectiveSelectedKey != null ? String(effectiveSelectedKey) : null;
549
+ cachedItemsOrder.current = null;
550
+ }
551
+ }, [isPopoverOpen, effectiveSelectedKey]);
552
+ // Filtering hook
553
+ const { filteredChildren, isFilterActive, setIsFilterActive } = useComboBoxFiltering({
554
+ children,
555
+ effectiveInputValue,
556
+ filter,
136
557
  });
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();
558
+ // Freeze filtered children during close animation to prevent visual jumps
559
+ const frozenFilteredChildrenRef = useRef(null);
560
+ useEffect(() => {
561
+ // Update frozen children only when popover is open
562
+ if (isPopoverOpen) {
563
+ frozenFilteredChildrenRef.current = filteredChildren;
564
+ }
565
+ }, [isPopoverOpen, filteredChildren]);
566
+ // Use frozen children during close animation, fresh children when open
567
+ const displayedFilteredChildren = isPopoverOpen
568
+ ? filteredChildren
569
+ : frozenFilteredChildrenRef.current ?? filteredChildren;
570
+ const { isFocused, focusProps } = useFocus({ isDisabled });
571
+ // Composite blur handler - fires when focus leaves the entire component
572
+ const handleCompositeBlur = useEvent(() => {
573
+ // Always disable filter on blur
574
+ setIsFilterActive(false);
575
+ // In allowsCustomValue mode with shouldCommitOnBlur, commit the input value
576
+ if (allowsCustomValue &&
577
+ shouldCommitOnBlur &&
578
+ effectiveInputValue &&
579
+ effectiveSelectedKey == null) {
580
+ externalOnSelectionChange?.(effectiveInputValue);
581
+ if (!isControlledKey) {
582
+ setInternalSelectedKey(effectiveInputValue);
583
+ }
584
+ // Call user's onBlur callback
585
+ onBlur?.();
586
+ return;
587
+ }
588
+ // In clearOnBlur mode (only for non-custom-value mode), clear selection and input
589
+ if (clearOnBlur && !allowsCustomValue) {
590
+ externalOnSelectionChange?.(null);
591
+ if (!isControlledKey) {
592
+ setInternalSelectedKey(null);
593
+ }
594
+ if (!isControlledInput) {
595
+ setInternalInputValue('');
596
+ }
597
+ onInputChange?.('');
598
+ // Call user's onBlur callback
599
+ onBlur?.();
600
+ return;
601
+ }
602
+ // Reset input to show current selection (or empty if none)
603
+ const nextValue = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : '';
604
+ if (!isControlledInput) {
605
+ setInternalInputValue(nextValue);
154
606
  }
155
- }, [state.isOpen, state.collection.size]);
607
+ onInputChange?.(nextValue);
608
+ // Call user's onBlur callback
609
+ onBlur?.();
610
+ });
611
+ // Composite focus hook - handles focus tracking across wrapper and portaled popover
612
+ const { compositeFocusProps } = useCompositeFocus({
613
+ wrapperRef,
614
+ popoverRef,
615
+ onFocus,
616
+ onBlur: handleCompositeBlur,
617
+ isDisabled,
618
+ });
156
619
  let isInvalid = validationState === 'invalid';
157
620
  let validationIcon = isInvalid ? InvalidIcon : ValidIcon;
158
621
  let validation = cloneElement(validationIcon);
622
+ // Ref to access internal ListBox state
623
+ const listStateRef = useRef(null);
624
+ const focusInitAttemptsRef = useRef(0);
625
+ // Helper to get label from collection item
626
+ const getItemLabel = useCallback((key) => {
627
+ const item = listStateRef.current?.collection?.getItem(key);
628
+ return item?.textValue || String(key);
629
+ }, []);
630
+ // Selection change handler
631
+ const handleSelectionChange = useEvent((selection) => {
632
+ // Extract single key from selection (we only support single selection)
633
+ const key = Array.isArray(selection) ? selection[0] : selection;
634
+ // Update selected key
635
+ if (!isControlledKey) {
636
+ setInternalSelectedKey(key ?? null);
637
+ }
638
+ // Update input value to show selected item label
639
+ if (key != null) {
640
+ setIsFilterActive(false);
641
+ const label = getItemLabel(key);
642
+ if (!isControlledInput) {
643
+ setInternalInputValue(label);
644
+ }
645
+ onInputChange?.(label);
646
+ }
647
+ else {
648
+ // Clear input when selection is cleared
649
+ if (!isControlledInput) {
650
+ setInternalInputValue('');
651
+ }
652
+ onInputChange?.('');
653
+ setIsFilterActive(false);
654
+ }
655
+ externalOnSelectionChange?.(key);
656
+ // Close popover after selection
657
+ setIsPopoverOpen(false);
658
+ // Focus input
659
+ setTimeout(() => {
660
+ inputRef.current?.focus();
661
+ }, 0);
662
+ });
663
+ // Input change handler
664
+ const handleInputChange = useEvent((e) => {
665
+ const value = e.target.value;
666
+ // Update input value
667
+ if (!isControlledInput) {
668
+ setInternalInputValue(value);
669
+ }
670
+ onInputChange?.(value);
671
+ const trimmed = value.trim();
672
+ setIsFilterActive(trimmed.length > 0);
673
+ // Only clear selection in allowsCustomValue mode
674
+ // In normal mode, typing just filters - selection stays until explicitly changed
675
+ if (allowsCustomValue && effectiveSelectedKey != null) {
676
+ if (!isControlledKey) {
677
+ setInternalSelectedKey(null);
678
+ }
679
+ externalOnSelectionChange?.(null);
680
+ }
681
+ // Open popover based on trigger
682
+ if (popoverTrigger !== 'manual' && value && !isPopoverOpen) {
683
+ setIsPopoverOpen(true);
684
+ }
685
+ });
686
+ // Initialize input value from defaultInputValue or defaultSelectedKey (uncontrolled mode only, one-time)
687
+ const [hasInitialized, setHasInitialized] = useState(false);
688
+ useEffect(() => {
689
+ // Only initialize once, in uncontrolled input mode
690
+ if (hasInitialized || isControlledInput)
691
+ return;
692
+ // Priority 1: defaultInputValue takes precedence
693
+ if (defaultInputValue !== undefined) {
694
+ setInternalInputValue(defaultInputValue);
695
+ setHasInitialized(true);
696
+ return;
697
+ }
698
+ // Priority 2: fall back to defaultSelectedKey's label
699
+ if (defaultSelectedKey) {
700
+ // Wait for collection to be ready
701
+ if (!listStateRef.current?.collection)
702
+ return;
703
+ const label = getItemLabel(defaultSelectedKey);
704
+ setInternalInputValue(label);
705
+ setHasInitialized(true);
706
+ }
707
+ }, [
708
+ hasInitialized,
709
+ isControlledInput,
710
+ defaultInputValue,
711
+ defaultSelectedKey,
712
+ getItemLabel,
713
+ children,
714
+ ]);
715
+ // Sync input value with controlled selectedKey
716
+ useEffect(() => {
717
+ // Only run when selectedKey is controlled but inputValue is uncontrolled
718
+ if (!isControlledKey || isControlledInput)
719
+ return;
720
+ // Wait for collection to be ready
721
+ if (!listStateRef.current?.collection)
722
+ return;
723
+ // Get the expected label for the current selection
724
+ const expectedLabel = effectiveSelectedKey != null ? getItemLabel(effectiveSelectedKey) : '';
725
+ // Update the input value to match the selected key's label
726
+ setInternalInputValue(expectedLabel);
727
+ }, [isControlledKey, isControlledInput, effectiveSelectedKey, getItemLabel]);
728
+ // Input focus handler
729
+ const handleInputFocus = useEvent((e) => {
730
+ // Call focus props handler if it exists
731
+ focusProps.onFocus?.(e);
732
+ if (popoverTrigger === 'focus' && !isPopoverOpen) {
733
+ setIsPopoverOpen(true);
734
+ }
735
+ });
736
+ // Input blur handler - just handles internal focus props
737
+ const handleInputBlur = useEvent((e) => {
738
+ focusProps.onBlur?.(e);
739
+ });
159
740
  // Clear button logic
160
- let hasValue = props.allowsCustomValue
161
- ? state.inputValue !== ''
162
- : state.selectedKey != null;
163
- let showClearButton = isClearable && hasValue && !isDisabled && !props.isReadOnly;
741
+ let hasValue = allowsCustomValue
742
+ ? effectiveInputValue !== ''
743
+ : effectiveSelectedKey != null;
744
+ let showClearButton = isClearable && hasValue && !isDisabled && !isReadOnly;
745
+ const hasResults = Boolean(displayedFilteredChildren &&
746
+ (Array.isArray(displayedFilteredChildren)
747
+ ? displayedFilteredChildren.length > 0
748
+ : displayedFilteredChildren !== null));
164
749
  // Clear function
165
750
  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?.('');
751
+ // Clear input
752
+ if (!isControlledInput) {
753
+ setInternalInputValue('');
171
754
  }
172
- props.onSelectionChange?.(null);
173
- state.setSelectedKey(null);
174
- // Close the popup if it's open
175
- if (state.isOpen) {
176
- state.close();
755
+ onInputChange?.('');
756
+ // Clear selection
757
+ if (!isControlledKey) {
758
+ setInternalSelectedKey(null);
177
759
  }
178
- // Focus back to the input
760
+ externalOnSelectionChange?.(null);
761
+ // Close popover
762
+ if (isPopoverOpen) {
763
+ setIsPopoverOpen(false);
764
+ }
765
+ // Focus input
179
766
  inputRef.current?.focus();
180
- props.onClear?.();
767
+ onClear?.();
768
+ });
769
+ // Keyboard navigation hook
770
+ const { keyboardProps } = useComboBoxKeyboard({
771
+ isPopoverOpen,
772
+ listStateRef,
773
+ hasResults,
774
+ allowsCustomValue,
775
+ effectiveInputValue,
776
+ isClearable,
777
+ onSelectionChange: handleSelectionChange,
778
+ onClearValue: clearValue,
779
+ onOpenPopover: () => setIsPopoverOpen(true),
780
+ onClosePopover: () => setIsPopoverOpen(false),
781
+ inputRef,
782
+ setIsFilterActive,
783
+ onKeyDown,
181
784
  });
182
- let comboBoxWidth = wrapperRef?.current?.offsetWidth;
183
785
  if (icon) {
184
786
  icon = _jsx("div", { "data-element": "InputIcon", children: icon });
185
787
  if (prefix) {
@@ -193,7 +795,7 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
193
795
  invalid: isInvalid,
194
796
  valid: validationState === 'valid',
195
797
  disabled: isDisabled,
196
- hovered: isHovered,
798
+ hovered: false,
197
799
  focused: isFocused,
198
800
  loading: isLoading,
199
801
  prefix: !!prefix,
@@ -203,84 +805,98 @@ export const ComboBox = forwardRef(function ComboBox(props, ref) {
203
805
  isInvalid,
204
806
  validationState,
205
807
  isDisabled,
206
- isHovered,
207
808
  isFocused,
208
809
  isLoading,
209
810
  prefix,
210
811
  showClearButton,
211
812
  ]);
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;
813
+ const comboBoxWidth = wrapperRef?.current?.offsetWidth;
814
+ const shouldShowPopover = Boolean(isPopoverOpen && hasResults);
815
+ // Close popover if no results
816
+ useEffect(() => {
817
+ if (isPopoverOpen && !hasResults) {
818
+ setIsPopoverOpen(false);
216
819
  }
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
- }
820
+ }, [isPopoverOpen, hasResults]);
821
+ const ensureInitialFocus = useCallback(() => {
822
+ if (!shouldShowPopover)
823
+ return;
824
+ const listState = listStateRef.current;
825
+ if (!listState)
826
+ return;
827
+ const { selectionManager, collection, disabledKeys, lastFocusSourceRef } = listState;
828
+ if (!selectionManager || !collection)
829
+ return;
830
+ const collectFirstKey = () => {
831
+ for (const node of collection) {
832
+ if (node.type === 'item') {
833
+ if (!disabledKeys?.has(node.key))
834
+ return node.key;
230
835
  }
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);
836
+ else if (node.childNodes) {
837
+ for (const child of node.childNodes) {
838
+ if (child.type === 'item' && !disabledKeys?.has(child.key)) {
839
+ return child.key;
840
+ }
841
+ }
239
842
  }
240
- // If a custom value is allowed, we need to check if the input value is in the collection.
241
843
  }
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);
844
+ return null;
845
+ };
846
+ if (lastFocusSourceRef)
847
+ lastFocusSourceRef.current = 'keyboard';
848
+ if (selectionManager.focusedKey == null) {
849
+ const keyToFocus = effectiveSelectedKey != null ? effectiveSelectedKey : collectFirstKey();
850
+ if (keyToFocus != null)
851
+ selectionManager.setFocusedKey(keyToFocus);
257
852
  }
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);
853
+ }, [shouldShowPopover, effectiveSelectedKey]);
854
+ useLayoutEffect(() => {
855
+ if (!shouldShowPopover)
856
+ return;
857
+ focusInitAttemptsRef.current = 0;
858
+ const tick = () => {
859
+ if (!shouldShowPopover)
860
+ return;
861
+ ensureInitialFocus();
862
+ focusInitAttemptsRef.current += 1;
863
+ // Try a few frames to wait for collection to mount
864
+ if (focusInitAttemptsRef.current < 8 &&
865
+ listStateRef.current?.selectionManager?.focusedKey == null) {
866
+ requestAnimationFrame(tick);
867
+ }
265
868
  };
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: {
869
+ requestAnimationFrame(() => requestAnimationFrame(tick));
870
+ }, [shouldShowPopover, ensureInitialFocus]);
871
+ const comboBoxField = (_jsxs(ComboBoxWrapperElement, { ref: wrapperRef, qa: qa || 'ComboBox', mods: mods, styles: styles, style: {
269
872
  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,
873
+ }, "data-size": size, ...compositeFocusProps, children: [prefix ? _jsx("div", { "data-element": "Prefix", children: prefix }) : null, _jsx(ComboBoxInput, { inputRef: inputRef, id: inputId, 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: {
874
+ pressed: isPopoverOpen,
274
875
  disabled: isDisabled,
275
876
  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',
877
+ }, "data-size": size, isDisabled: isDisabled, styles: triggerStyles, "aria-expanded": isPopoverOpen, "aria-haspopup": "listbox", "aria-label": "Show options", onPress: () => {
878
+ if (!isDisabled) {
879
+ const willOpen = !isPopoverOpen;
880
+ setIsPopoverOpen(willOpen);
881
+ if (willOpen) {
882
+ inputRef.current?.focus();
883
+ // If opening with no filtered results, disable filter to show all items
884
+ if (!hasResults) {
885
+ setIsFilterActive(false);
886
+ }
887
+ }
888
+ }
889
+ } })) : 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 })] }));
890
+ const { children: _, ...propsWithoutChildren } = props;
891
+ const finalProps = {
892
+ ...propsWithoutChildren,
893
+ styles: fieldStyles,
894
+ labelProps: { ...props.labelProps, for: inputId },
895
+ };
896
+ return wrapWithField(comboBoxField, ref, mergeProps(finalProps, {}));
281
897
  });
282
898
  ComboBox.Item = Item;
283
- ComboBox.Section = ComboBoxSectionComponent;
899
+ ComboBox.Section = BaseSection;
284
900
  Object.defineProperty(ComboBox, 'cubeInputType', {
285
901
  value: 'ComboBox',
286
902
  enumerable: false,