@octanejs/aria 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (500) hide show
  1. package/package.json +6 -3
  2. package/src/breadcrumbs/useBreadcrumbItem.ts +95 -0
  3. package/src/breadcrumbs/useBreadcrumbs.ts +51 -0
  4. package/src/button/useButton.ts +273 -0
  5. package/src/button/useToggleButton.ts +131 -0
  6. package/src/button/useToggleButtonGroup.ts +131 -0
  7. package/src/checkbox/useCheckbox.ts +114 -0
  8. package/src/checkbox/useCheckboxGroup.ts +107 -0
  9. package/src/checkbox/useCheckboxGroupItem.ts +132 -0
  10. package/src/checkbox/utils.ts +15 -0
  11. package/src/combobox/useComboBox.ts +602 -0
  12. package/src/dialog/useDialog.ts +147 -0
  13. package/src/disclosure/useDisclosure.ts +180 -0
  14. package/src/focus/FocusRing.ts +69 -0
  15. package/src/focus/FocusScope.ts +1205 -0
  16. package/src/focus/useFocusRing.ts +125 -0
  17. package/src/focus/useHasTabbableChild.ts +82 -0
  18. package/src/focus/virtualFocus.ts +38 -0
  19. package/src/form/useFormValidation.ts +205 -0
  20. package/src/grid/useGridSelectionAnnouncement.ts +177 -0
  21. package/src/grid/useGridSelectionCheckbox.ts +86 -0
  22. package/src/grid/useHighlightSelectionDescription.ts +81 -0
  23. package/src/gridlist/useGridList.ts +236 -0
  24. package/src/gridlist/useGridListItem.ts +512 -0
  25. package/src/gridlist/useGridListSection.ts +78 -0
  26. package/src/gridlist/useGridListSelectionCheckbox.ts +47 -0
  27. package/src/gridlist/utils.ts +36 -0
  28. package/src/i18n/I18nProvider.ts +97 -0
  29. package/src/i18n/server.ts +115 -0
  30. package/src/i18n/useCollator.ts +42 -0
  31. package/src/i18n/useDateFormatter.ts +64 -0
  32. package/src/i18n/useDefaultLocale.ts +93 -0
  33. package/src/i18n/useFilter.ts +107 -0
  34. package/src/i18n/useListFormatter.ts +33 -0
  35. package/src/i18n/useLocalizedStringFormatter.ts +90 -0
  36. package/src/i18n/useNumberFormatter.ts +33 -0
  37. package/src/i18n/utils.ts +67 -0
  38. package/src/index.ts +268 -0
  39. package/src/intl/breadcrumbs/ar-AE.json +3 -0
  40. package/src/intl/breadcrumbs/bg-BG.json +3 -0
  41. package/src/intl/breadcrumbs/cs-CZ.json +3 -0
  42. package/src/intl/breadcrumbs/da-DK.json +3 -0
  43. package/src/intl/breadcrumbs/de-DE.json +3 -0
  44. package/src/intl/breadcrumbs/el-GR.json +3 -0
  45. package/src/intl/breadcrumbs/en-US.json +3 -0
  46. package/src/intl/breadcrumbs/es-ES.json +3 -0
  47. package/src/intl/breadcrumbs/et-EE.json +3 -0
  48. package/src/intl/breadcrumbs/fi-FI.json +3 -0
  49. package/src/intl/breadcrumbs/fr-FR.json +3 -0
  50. package/src/intl/breadcrumbs/he-IL.json +3 -0
  51. package/src/intl/breadcrumbs/hr-HR.json +3 -0
  52. package/src/intl/breadcrumbs/hu-HU.json +3 -0
  53. package/src/intl/breadcrumbs/index.ts +75 -0
  54. package/src/intl/breadcrumbs/it-IT.json +3 -0
  55. package/src/intl/breadcrumbs/ja-JP.json +3 -0
  56. package/src/intl/breadcrumbs/ko-KR.json +3 -0
  57. package/src/intl/breadcrumbs/lt-LT.json +3 -0
  58. package/src/intl/breadcrumbs/lv-LV.json +3 -0
  59. package/src/intl/breadcrumbs/nb-NO.json +3 -0
  60. package/src/intl/breadcrumbs/nl-NL.json +3 -0
  61. package/src/intl/breadcrumbs/pl-PL.json +3 -0
  62. package/src/intl/breadcrumbs/pt-BR.json +3 -0
  63. package/src/intl/breadcrumbs/pt-PT.json +3 -0
  64. package/src/intl/breadcrumbs/ro-RO.json +3 -0
  65. package/src/intl/breadcrumbs/ru-RU.json +3 -0
  66. package/src/intl/breadcrumbs/sk-SK.json +3 -0
  67. package/src/intl/breadcrumbs/sl-SI.json +3 -0
  68. package/src/intl/breadcrumbs/sr-SP.json +3 -0
  69. package/src/intl/breadcrumbs/sv-SE.json +3 -0
  70. package/src/intl/breadcrumbs/tr-TR.json +3 -0
  71. package/src/intl/breadcrumbs/uk-UA.json +3 -0
  72. package/src/intl/breadcrumbs/zh-CN.json +3 -0
  73. package/src/intl/breadcrumbs/zh-TW.json +3 -0
  74. package/src/intl/combobox/ar-AE.json +7 -0
  75. package/src/intl/combobox/bg-BG.json +7 -0
  76. package/src/intl/combobox/cs-CZ.json +7 -0
  77. package/src/intl/combobox/da-DK.json +7 -0
  78. package/src/intl/combobox/de-DE.json +7 -0
  79. package/src/intl/combobox/el-GR.json +7 -0
  80. package/src/intl/combobox/en-US.json +7 -0
  81. package/src/intl/combobox/es-ES.json +7 -0
  82. package/src/intl/combobox/et-EE.json +7 -0
  83. package/src/intl/combobox/fi-FI.json +7 -0
  84. package/src/intl/combobox/fr-FR.json +7 -0
  85. package/src/intl/combobox/he-IL.json +7 -0
  86. package/src/intl/combobox/hr-HR.json +7 -0
  87. package/src/intl/combobox/hu-HU.json +7 -0
  88. package/src/intl/combobox/index.ts +75 -0
  89. package/src/intl/combobox/it-IT.json +7 -0
  90. package/src/intl/combobox/ja-JP.json +7 -0
  91. package/src/intl/combobox/ko-KR.json +7 -0
  92. package/src/intl/combobox/lt-LT.json +7 -0
  93. package/src/intl/combobox/lv-LV.json +7 -0
  94. package/src/intl/combobox/nb-NO.json +7 -0
  95. package/src/intl/combobox/nl-NL.json +7 -0
  96. package/src/intl/combobox/pl-PL.json +7 -0
  97. package/src/intl/combobox/pt-BR.json +7 -0
  98. package/src/intl/combobox/pt-PT.json +7 -0
  99. package/src/intl/combobox/ro-RO.json +7 -0
  100. package/src/intl/combobox/ru-RU.json +7 -0
  101. package/src/intl/combobox/sk-SK.json +7 -0
  102. package/src/intl/combobox/sl-SI.json +7 -0
  103. package/src/intl/combobox/sr-SP.json +7 -0
  104. package/src/intl/combobox/sv-SE.json +7 -0
  105. package/src/intl/combobox/tr-TR.json +7 -0
  106. package/src/intl/combobox/uk-UA.json +7 -0
  107. package/src/intl/combobox/zh-CN.json +7 -0
  108. package/src/intl/combobox/zh-TW.json +7 -0
  109. package/src/intl/grid/ar-AE.json +8 -0
  110. package/src/intl/grid/bg-BG.json +8 -0
  111. package/src/intl/grid/cs-CZ.json +8 -0
  112. package/src/intl/grid/da-DK.json +8 -0
  113. package/src/intl/grid/de-DE.json +8 -0
  114. package/src/intl/grid/el-GR.json +8 -0
  115. package/src/intl/grid/en-US.json +8 -0
  116. package/src/intl/grid/es-ES.json +8 -0
  117. package/src/intl/grid/et-EE.json +8 -0
  118. package/src/intl/grid/fi-FI.json +8 -0
  119. package/src/intl/grid/fr-FR.json +8 -0
  120. package/src/intl/grid/he-IL.json +8 -0
  121. package/src/intl/grid/hr-HR.json +8 -0
  122. package/src/intl/grid/hu-HU.json +8 -0
  123. package/src/intl/grid/index.ts +75 -0
  124. package/src/intl/grid/it-IT.json +8 -0
  125. package/src/intl/grid/ja-JP.json +8 -0
  126. package/src/intl/grid/ko-KR.json +8 -0
  127. package/src/intl/grid/lt-LT.json +8 -0
  128. package/src/intl/grid/lv-LV.json +8 -0
  129. package/src/intl/grid/nb-NO.json +8 -0
  130. package/src/intl/grid/nl-NL.json +8 -0
  131. package/src/intl/grid/pl-PL.json +8 -0
  132. package/src/intl/grid/pt-BR.json +8 -0
  133. package/src/intl/grid/pt-PT.json +8 -0
  134. package/src/intl/grid/ro-RO.json +8 -0
  135. package/src/intl/grid/ru-RU.json +8 -0
  136. package/src/intl/grid/sk-SK.json +8 -0
  137. package/src/intl/grid/sl-SI.json +8 -0
  138. package/src/intl/grid/sr-SP.json +8 -0
  139. package/src/intl/grid/sv-SE.json +8 -0
  140. package/src/intl/grid/tr-TR.json +8 -0
  141. package/src/intl/grid/uk-UA.json +8 -0
  142. package/src/intl/grid/zh-CN.json +8 -0
  143. package/src/intl/grid/zh-TW.json +8 -0
  144. package/src/intl/gridlist/ar-AE.json +4 -0
  145. package/src/intl/gridlist/bg-BG.json +4 -0
  146. package/src/intl/gridlist/cs-CZ.json +4 -0
  147. package/src/intl/gridlist/da-DK.json +4 -0
  148. package/src/intl/gridlist/de-DE.json +4 -0
  149. package/src/intl/gridlist/el-GR.json +4 -0
  150. package/src/intl/gridlist/en-US.json +4 -0
  151. package/src/intl/gridlist/es-ES.json +4 -0
  152. package/src/intl/gridlist/et-EE.json +4 -0
  153. package/src/intl/gridlist/fi-FI.json +4 -0
  154. package/src/intl/gridlist/fr-FR.json +4 -0
  155. package/src/intl/gridlist/he-IL.json +4 -0
  156. package/src/intl/gridlist/hr-HR.json +4 -0
  157. package/src/intl/gridlist/hu-HU.json +4 -0
  158. package/src/intl/gridlist/index.ts +75 -0
  159. package/src/intl/gridlist/it-IT.json +4 -0
  160. package/src/intl/gridlist/ja-JP.json +4 -0
  161. package/src/intl/gridlist/ko-KR.json +4 -0
  162. package/src/intl/gridlist/lt-LT.json +4 -0
  163. package/src/intl/gridlist/lv-LV.json +4 -0
  164. package/src/intl/gridlist/nb-NO.json +4 -0
  165. package/src/intl/gridlist/nl-NL.json +4 -0
  166. package/src/intl/gridlist/pl-PL.json +4 -0
  167. package/src/intl/gridlist/pt-BR.json +4 -0
  168. package/src/intl/gridlist/pt-PT.json +4 -0
  169. package/src/intl/gridlist/ro-RO.json +4 -0
  170. package/src/intl/gridlist/ru-RU.json +4 -0
  171. package/src/intl/gridlist/sk-SK.json +4 -0
  172. package/src/intl/gridlist/sl-SI.json +4 -0
  173. package/src/intl/gridlist/sr-SP.json +4 -0
  174. package/src/intl/gridlist/sv-SE.json +4 -0
  175. package/src/intl/gridlist/tr-TR.json +4 -0
  176. package/src/intl/gridlist/uk-UA.json +4 -0
  177. package/src/intl/gridlist/zh-CN.json +4 -0
  178. package/src/intl/gridlist/zh-TW.json +4 -0
  179. package/src/intl/menu/ar-AE.json +3 -0
  180. package/src/intl/menu/bg-BG.json +3 -0
  181. package/src/intl/menu/cs-CZ.json +3 -0
  182. package/src/intl/menu/da-DK.json +3 -0
  183. package/src/intl/menu/de-DE.json +3 -0
  184. package/src/intl/menu/el-GR.json +3 -0
  185. package/src/intl/menu/en-US.json +3 -0
  186. package/src/intl/menu/es-ES.json +3 -0
  187. package/src/intl/menu/et-EE.json +3 -0
  188. package/src/intl/menu/fi-FI.json +3 -0
  189. package/src/intl/menu/fr-FR.json +3 -0
  190. package/src/intl/menu/he-IL.json +3 -0
  191. package/src/intl/menu/hr-HR.json +3 -0
  192. package/src/intl/menu/hu-HU.json +3 -0
  193. package/src/intl/menu/index.ts +75 -0
  194. package/src/intl/menu/it-IT.json +3 -0
  195. package/src/intl/menu/ja-JP.json +3 -0
  196. package/src/intl/menu/ko-KR.json +3 -0
  197. package/src/intl/menu/lt-LT.json +3 -0
  198. package/src/intl/menu/lv-LV.json +3 -0
  199. package/src/intl/menu/nb-NO.json +3 -0
  200. package/src/intl/menu/nl-NL.json +3 -0
  201. package/src/intl/menu/pl-PL.json +3 -0
  202. package/src/intl/menu/pt-BR.json +3 -0
  203. package/src/intl/menu/pt-PT.json +3 -0
  204. package/src/intl/menu/ro-RO.json +3 -0
  205. package/src/intl/menu/ru-RU.json +3 -0
  206. package/src/intl/menu/sk-SK.json +3 -0
  207. package/src/intl/menu/sl-SI.json +3 -0
  208. package/src/intl/menu/sr-SP.json +3 -0
  209. package/src/intl/menu/sv-SE.json +3 -0
  210. package/src/intl/menu/tr-TR.json +3 -0
  211. package/src/intl/menu/uk-UA.json +3 -0
  212. package/src/intl/menu/zh-CN.json +3 -0
  213. package/src/intl/menu/zh-TW.json +3 -0
  214. package/src/intl/numberfield/ar-AE.json +5 -0
  215. package/src/intl/numberfield/bg-BG.json +5 -0
  216. package/src/intl/numberfield/cs-CZ.json +5 -0
  217. package/src/intl/numberfield/da-DK.json +5 -0
  218. package/src/intl/numberfield/de-DE.json +5 -0
  219. package/src/intl/numberfield/el-GR.json +5 -0
  220. package/src/intl/numberfield/en-US.json +5 -0
  221. package/src/intl/numberfield/es-ES.json +5 -0
  222. package/src/intl/numberfield/et-EE.json +5 -0
  223. package/src/intl/numberfield/fi-FI.json +5 -0
  224. package/src/intl/numberfield/fr-FR.json +5 -0
  225. package/src/intl/numberfield/he-IL.json +5 -0
  226. package/src/intl/numberfield/hr-HR.json +5 -0
  227. package/src/intl/numberfield/hu-HU.json +5 -0
  228. package/src/intl/numberfield/index.ts +75 -0
  229. package/src/intl/numberfield/it-IT.json +5 -0
  230. package/src/intl/numberfield/ja-JP.json +5 -0
  231. package/src/intl/numberfield/ko-KR.json +5 -0
  232. package/src/intl/numberfield/lt-LT.json +5 -0
  233. package/src/intl/numberfield/lv-LV.json +5 -0
  234. package/src/intl/numberfield/nb-NO.json +5 -0
  235. package/src/intl/numberfield/nl-NL.json +5 -0
  236. package/src/intl/numberfield/pl-PL.json +5 -0
  237. package/src/intl/numberfield/pt-BR.json +5 -0
  238. package/src/intl/numberfield/pt-PT.json +5 -0
  239. package/src/intl/numberfield/ro-RO.json +5 -0
  240. package/src/intl/numberfield/ru-RU.json +5 -0
  241. package/src/intl/numberfield/sk-SK.json +5 -0
  242. package/src/intl/numberfield/sl-SI.json +5 -0
  243. package/src/intl/numberfield/sr-SP.json +5 -0
  244. package/src/intl/numberfield/sv-SE.json +5 -0
  245. package/src/intl/numberfield/tr-TR.json +5 -0
  246. package/src/intl/numberfield/uk-UA.json +5 -0
  247. package/src/intl/numberfield/zh-CN.json +5 -0
  248. package/src/intl/numberfield/zh-TW.json +5 -0
  249. package/src/intl/overlays/ar-AE.json +3 -0
  250. package/src/intl/overlays/bg-BG.json +3 -0
  251. package/src/intl/overlays/cs-CZ.json +3 -0
  252. package/src/intl/overlays/da-DK.json +3 -0
  253. package/src/intl/overlays/de-DE.json +3 -0
  254. package/src/intl/overlays/el-GR.json +3 -0
  255. package/src/intl/overlays/en-US.json +3 -0
  256. package/src/intl/overlays/es-ES.json +3 -0
  257. package/src/intl/overlays/et-EE.json +3 -0
  258. package/src/intl/overlays/fi-FI.json +3 -0
  259. package/src/intl/overlays/fr-FR.json +3 -0
  260. package/src/intl/overlays/he-IL.json +3 -0
  261. package/src/intl/overlays/hr-HR.json +3 -0
  262. package/src/intl/overlays/hu-HU.json +3 -0
  263. package/src/intl/overlays/index.ts +75 -0
  264. package/src/intl/overlays/it-IT.json +3 -0
  265. package/src/intl/overlays/ja-JP.json +3 -0
  266. package/src/intl/overlays/ko-KR.json +3 -0
  267. package/src/intl/overlays/lt-LT.json +3 -0
  268. package/src/intl/overlays/lv-LV.json +3 -0
  269. package/src/intl/overlays/nb-NO.json +3 -0
  270. package/src/intl/overlays/nl-NL.json +3 -0
  271. package/src/intl/overlays/pl-PL.json +3 -0
  272. package/src/intl/overlays/pt-BR.json +3 -0
  273. package/src/intl/overlays/pt-PT.json +3 -0
  274. package/src/intl/overlays/ro-RO.json +3 -0
  275. package/src/intl/overlays/ru-RU.json +3 -0
  276. package/src/intl/overlays/sk-SK.json +3 -0
  277. package/src/intl/overlays/sl-SI.json +3 -0
  278. package/src/intl/overlays/sr-SP.json +3 -0
  279. package/src/intl/overlays/sv-SE.json +3 -0
  280. package/src/intl/overlays/tr-TR.json +3 -0
  281. package/src/intl/overlays/uk-UA.json +3 -0
  282. package/src/intl/overlays/zh-CN.json +3 -0
  283. package/src/intl/overlays/zh-TW.json +3 -0
  284. package/src/intl/searchfield/ar-AE.json +3 -0
  285. package/src/intl/searchfield/bg-BG.json +3 -0
  286. package/src/intl/searchfield/cs-CZ.json +3 -0
  287. package/src/intl/searchfield/da-DK.json +3 -0
  288. package/src/intl/searchfield/de-DE.json +3 -0
  289. package/src/intl/searchfield/el-GR.json +3 -0
  290. package/src/intl/searchfield/en-US.json +3 -0
  291. package/src/intl/searchfield/es-ES.json +3 -0
  292. package/src/intl/searchfield/et-EE.json +3 -0
  293. package/src/intl/searchfield/fi-FI.json +3 -0
  294. package/src/intl/searchfield/fr-FR.json +3 -0
  295. package/src/intl/searchfield/he-IL.json +3 -0
  296. package/src/intl/searchfield/hr-HR.json +3 -0
  297. package/src/intl/searchfield/hu-HU.json +3 -0
  298. package/src/intl/searchfield/index.ts +75 -0
  299. package/src/intl/searchfield/it-IT.json +3 -0
  300. package/src/intl/searchfield/ja-JP.json +3 -0
  301. package/src/intl/searchfield/ko-KR.json +3 -0
  302. package/src/intl/searchfield/lt-LT.json +3 -0
  303. package/src/intl/searchfield/lv-LV.json +3 -0
  304. package/src/intl/searchfield/nb-NO.json +3 -0
  305. package/src/intl/searchfield/nl-NL.json +3 -0
  306. package/src/intl/searchfield/pl-PL.json +3 -0
  307. package/src/intl/searchfield/pt-BR.json +3 -0
  308. package/src/intl/searchfield/pt-PT.json +3 -0
  309. package/src/intl/searchfield/ro-RO.json +3 -0
  310. package/src/intl/searchfield/ru-RU.json +3 -0
  311. package/src/intl/searchfield/sk-SK.json +3 -0
  312. package/src/intl/searchfield/sl-SI.json +3 -0
  313. package/src/intl/searchfield/sr-SP.json +3 -0
  314. package/src/intl/searchfield/sv-SE.json +3 -0
  315. package/src/intl/searchfield/tr-TR.json +3 -0
  316. package/src/intl/searchfield/uk-UA.json +3 -0
  317. package/src/intl/searchfield/zh-CN.json +3 -0
  318. package/src/intl/searchfield/zh-TW.json +3 -0
  319. package/src/intl/spinbutton/ar-AE.json +3 -0
  320. package/src/intl/spinbutton/bg-BG.json +3 -0
  321. package/src/intl/spinbutton/cs-CZ.json +3 -0
  322. package/src/intl/spinbutton/da-DK.json +3 -0
  323. package/src/intl/spinbutton/de-DE.json +3 -0
  324. package/src/intl/spinbutton/el-GR.json +3 -0
  325. package/src/intl/spinbutton/en-US.json +3 -0
  326. package/src/intl/spinbutton/es-ES.json +3 -0
  327. package/src/intl/spinbutton/et-EE.json +3 -0
  328. package/src/intl/spinbutton/fi-FI.json +3 -0
  329. package/src/intl/spinbutton/fr-FR.json +3 -0
  330. package/src/intl/spinbutton/he-IL.json +3 -0
  331. package/src/intl/spinbutton/hr-HR.json +3 -0
  332. package/src/intl/spinbutton/hu-HU.json +3 -0
  333. package/src/intl/spinbutton/index.ts +75 -0
  334. package/src/intl/spinbutton/it-IT.json +3 -0
  335. package/src/intl/spinbutton/ja-JP.json +3 -0
  336. package/src/intl/spinbutton/ko-KR.json +3 -0
  337. package/src/intl/spinbutton/lt-LT.json +3 -0
  338. package/src/intl/spinbutton/lv-LV.json +3 -0
  339. package/src/intl/spinbutton/nb-NO.json +3 -0
  340. package/src/intl/spinbutton/nl-NL.json +3 -0
  341. package/src/intl/spinbutton/pl-PL.json +3 -0
  342. package/src/intl/spinbutton/pt-BR.json +3 -0
  343. package/src/intl/spinbutton/pt-PT.json +3 -0
  344. package/src/intl/spinbutton/ro-RO.json +3 -0
  345. package/src/intl/spinbutton/ru-RU.json +3 -0
  346. package/src/intl/spinbutton/sk-SK.json +3 -0
  347. package/src/intl/spinbutton/sl-SI.json +3 -0
  348. package/src/intl/spinbutton/sr-SP.json +3 -0
  349. package/src/intl/spinbutton/sv-SE.json +3 -0
  350. package/src/intl/spinbutton/tr-TR.json +3 -0
  351. package/src/intl/spinbutton/uk-UA.json +3 -0
  352. package/src/intl/spinbutton/zh-CN.json +3 -0
  353. package/src/intl/spinbutton/zh-TW.json +3 -0
  354. package/src/intl/tag/ar-AE.json +4 -0
  355. package/src/intl/tag/bg-BG.json +4 -0
  356. package/src/intl/tag/cs-CZ.json +4 -0
  357. package/src/intl/tag/da-DK.json +4 -0
  358. package/src/intl/tag/de-DE.json +4 -0
  359. package/src/intl/tag/el-GR.json +4 -0
  360. package/src/intl/tag/en-US.json +4 -0
  361. package/src/intl/tag/es-ES.json +4 -0
  362. package/src/intl/tag/et-EE.json +4 -0
  363. package/src/intl/tag/fi-FI.json +4 -0
  364. package/src/intl/tag/fr-FR.json +4 -0
  365. package/src/intl/tag/he-IL.json +4 -0
  366. package/src/intl/tag/hr-HR.json +4 -0
  367. package/src/intl/tag/hu-HU.json +4 -0
  368. package/src/intl/tag/index.ts +75 -0
  369. package/src/intl/tag/it-IT.json +4 -0
  370. package/src/intl/tag/ja-JP.json +4 -0
  371. package/src/intl/tag/ko-KR.json +4 -0
  372. package/src/intl/tag/lt-LT.json +4 -0
  373. package/src/intl/tag/lv-LV.json +4 -0
  374. package/src/intl/tag/nb-NO.json +4 -0
  375. package/src/intl/tag/nl-NL.json +4 -0
  376. package/src/intl/tag/pl-PL.json +4 -0
  377. package/src/intl/tag/pt-BR.json +4 -0
  378. package/src/intl/tag/pt-PT.json +4 -0
  379. package/src/intl/tag/ro-RO.json +4 -0
  380. package/src/intl/tag/ru-RU.json +4 -0
  381. package/src/intl/tag/sk-SK.json +4 -0
  382. package/src/intl/tag/sl-SI.json +4 -0
  383. package/src/intl/tag/sr-SP.json +4 -0
  384. package/src/intl/tag/sv-SE.json +4 -0
  385. package/src/intl/tag/tr-TR.json +4 -0
  386. package/src/intl/tag/uk-UA.json +4 -0
  387. package/src/intl/tag/zh-CN.json +4 -0
  388. package/src/intl/tag/zh-TW.json +4 -0
  389. package/src/label/useField.ts +67 -0
  390. package/src/label/useLabel.ts +78 -0
  391. package/src/link/useLink.ts +108 -0
  392. package/src/listbox/useListBox.ts +224 -0
  393. package/src/listbox/useListBoxSection.ts +74 -0
  394. package/src/listbox/useOption.ts +212 -0
  395. package/src/listbox/utils.ts +38 -0
  396. package/src/live-announcer/LiveAnnouncer.ts +172 -0
  397. package/src/menu/useMenu.ts +139 -0
  398. package/src/menu/useMenuItem.ts +453 -0
  399. package/src/menu/useMenuSection.ts +70 -0
  400. package/src/menu/useMenuTrigger.ts +189 -0
  401. package/src/menu/useSafelyMouseToSubmenu.ts +236 -0
  402. package/src/menu/useSubmenuTrigger.ts +357 -0
  403. package/src/menu/utils.ts +16 -0
  404. package/src/meter/useMeter.ts +43 -0
  405. package/src/numberfield/useNumberField.ts +572 -0
  406. package/src/overlays/DismissButton.ts +50 -0
  407. package/src/overlays/Overlay.ts +106 -0
  408. package/src/overlays/PortalProvider.ts +37 -0
  409. package/src/overlays/ariaHideOutside.ts +299 -0
  410. package/src/overlays/calculatePosition.ts +856 -0
  411. package/src/overlays/useCloseOnScroll.ts +75 -0
  412. package/src/overlays/useModal.ts +224 -0
  413. package/src/overlays/useModalOverlay.ts +103 -0
  414. package/src/overlays/useOverlay.ts +199 -0
  415. package/src/overlays/useOverlayPosition.ts +462 -0
  416. package/src/overlays/useOverlayTrigger.ts +94 -0
  417. package/src/overlays/usePopover.ts +170 -0
  418. package/src/overlays/usePreventScroll.ts +316 -0
  419. package/src/progress/useProgressBar.ts +115 -0
  420. package/src/radio/useRadio.ts +219 -0
  421. package/src/radio/useRadioGroup.ts +184 -0
  422. package/src/radio/utils.ts +15 -0
  423. package/src/searchfield/useSearchField.ts +156 -0
  424. package/src/select/HiddenSelect.ts +261 -0
  425. package/src/select/useSelect.ts +332 -0
  426. package/src/selection/DOMLayoutDelegate.ts +51 -0
  427. package/src/selection/ListKeyboardDelegate.ts +392 -0
  428. package/src/selection/useSelectableCollection.ts +750 -0
  429. package/src/selection/useSelectableItem.ts +499 -0
  430. package/src/selection/useSelectableList.ts +132 -0
  431. package/src/selection/useTypeSelect.ts +192 -0
  432. package/src/selection/utils.ts +50 -0
  433. package/src/separator/useSeparator.ts +41 -0
  434. package/src/slider/useSlider.ts +277 -0
  435. package/src/slider/useSliderThumb.ts +375 -0
  436. package/src/slider/utils.ts +20 -0
  437. package/src/spinbutton/useSpinButton.ts +380 -0
  438. package/src/stately/checkbox/useCheckboxGroupState.ts +177 -0
  439. package/src/stately/collections/CollectionBuilder.ts +332 -0
  440. package/src/stately/collections/Item.ts +78 -0
  441. package/src/stately/collections/Section.ts +52 -0
  442. package/src/stately/collections/getChildNodes.ts +94 -0
  443. package/src/stately/collections/getItemCount.ts +29 -0
  444. package/src/stately/collections/types.ts +20 -0
  445. package/src/stately/collections/useCollection.ts +54 -0
  446. package/src/stately/combobox/useComboBoxState.ts +763 -0
  447. package/src/stately/disclosure/useDisclosureGroupState.ts +108 -0
  448. package/src/stately/disclosure/useDisclosureState.ts +84 -0
  449. package/src/stately/form/useFormValidationState.ts +331 -0
  450. package/src/stately/index.ts +111 -0
  451. package/src/stately/list/ListCollection.ts +104 -0
  452. package/src/stately/list/useListState.ts +178 -0
  453. package/src/stately/list/useSingleSelectListState.ts +101 -0
  454. package/src/stately/menu/useMenuTriggerState.ts +120 -0
  455. package/src/stately/menu/useSubmenuTriggerState.ts +116 -0
  456. package/src/stately/numberfield/useNumberFieldState.ts +430 -0
  457. package/src/stately/overlays/useOverlayTriggerState.ts +85 -0
  458. package/src/stately/radio/useRadioGroupState.ts +150 -0
  459. package/src/stately/searchfield/useSearchFieldState.ts +79 -0
  460. package/src/stately/select/useSelectState.ts +322 -0
  461. package/src/stately/selection/Selection.ts +23 -0
  462. package/src/stately/selection/SelectionManager.ts +541 -0
  463. package/src/stately/selection/types.ts +117 -0
  464. package/src/stately/selection/useMultipleSelectionState.ts +178 -0
  465. package/src/stately/slider/useSliderState.ts +458 -0
  466. package/src/stately/tabs/useTabListState.ts +149 -0
  467. package/src/stately/toggle/useToggleGroupState.ts +109 -0
  468. package/src/stately/toggle/useToggleState.ts +100 -0
  469. package/src/stately/tooltip/useTooltipTriggerState.ts +218 -0
  470. package/src/stately/tree/TreeCollection.ts +92 -0
  471. package/src/stately/tree/useTreeState.ts +121 -0
  472. package/src/stately/utils/useControlledState.ts +7 -0
  473. package/src/switch/useSwitch.ts +77 -0
  474. package/src/tabs/TabsKeyboardDelegate.ts +99 -0
  475. package/src/tabs/useTab.ts +117 -0
  476. package/src/tabs/useTabList.ts +105 -0
  477. package/src/tabs/useTabPanel.ts +75 -0
  478. package/src/tabs/utils.ts +32 -0
  479. package/src/tag/useTag.ts +162 -0
  480. package/src/tag/useTagGroup.ts +192 -0
  481. package/src/textfield/useFormattedTextField.ts +203 -0
  482. package/src/textfield/useTextField.ts +248 -0
  483. package/src/toggle/useToggle.ts +265 -0
  484. package/src/toolbar/useToolbar.ts +147 -0
  485. package/src/tooltip/useTooltip.ts +64 -0
  486. package/src/tooltip/useTooltipTrigger.ts +187 -0
  487. package/src/utils/filterDOMProps.ts +121 -0
  488. package/src/utils/getScrollParent.ts +16 -0
  489. package/src/utils/getScrollParents.ts +20 -0
  490. package/src/utils/keyboard.ts +38 -0
  491. package/src/utils/scrollIntoView.ts +178 -0
  492. package/src/utils/useDeepMemo.ts +30 -0
  493. package/src/utils/useFormReset.ts +54 -0
  494. package/src/utils/useId.ts +5 -0
  495. package/src/utils/useLabels.ts +53 -0
  496. package/src/utils/useResizeObserver.ts +70 -0
  497. package/src/utils/useSlot.ts +78 -0
  498. package/src/utils/useUpdateEffect.ts +54 -0
  499. package/src/utils/useUpdateLayoutEffect.ts +58 -0
  500. package/src/visually-hidden/VisuallyHidden.ts +100 -0
@@ -0,0 +1,763 @@
1
+ // Ported from react-stately (source: .react-spectrum/packages/react-stately/src/combobox/useComboBoxState.ts).
2
+ // octane adaptations: public-hook slot threading (splitSlot/subSlot) per the binding
3
+ // convention; `FocusableProps` comes from the ported interactions area (type-only —
4
+ // upstream's @react-types/shared version is typed over React synthetic events); public
5
+ // value-level callbacks (onChange/onSelectionChange/onInputChange/onOpenChange) are
6
+ // unchanged (the onInput rule applies only to DOM wiring); explicit dependency arrays
7
+ // are kept verbatim, and upstream's intentionally dep-less useEffect becomes an
8
+ // explicit `null` (run after every render).
9
+ import type {
10
+ Collection,
11
+ CollectionBase,
12
+ CollectionStateBase,
13
+ FocusStrategy,
14
+ HelpTextProps,
15
+ InputBase,
16
+ Key,
17
+ LabelableProps,
18
+ Node,
19
+ Selection,
20
+ TextInputBase,
21
+ Validation,
22
+ ValueBase,
23
+ } from '@react-types/shared';
24
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'octane';
25
+
26
+ import type { FocusableProps } from '../../interactions/useFocusable';
27
+
28
+ import { S, splitSlot, subSlot } from '../../internal';
29
+ import { type FormValidationState, useFormValidationState } from '../form/useFormValidationState';
30
+ import { getChildNodes } from '../collections/getChildNodes';
31
+ import { ListCollection } from '../list/ListCollection';
32
+ import { type ListState, useListState } from '../list/useListState';
33
+ import {
34
+ type OverlayTriggerState,
35
+ useOverlayTriggerState,
36
+ } from '../overlays/useOverlayTriggerState';
37
+ import { useControlledState } from '../utils/useControlledState';
38
+
39
+ export type MenuTriggerAction = 'focus' | 'input' | 'manual';
40
+ export type SelectionMode = 'single' | 'multiple';
41
+ export type ValueType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
42
+ export type ChangeValueType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
43
+ type ValidationType<M extends SelectionMode> = M extends 'single' ? Key | null : Key[];
44
+
45
+ export interface ComboBoxValidationValue<M extends SelectionMode = 'single'> {
46
+ /**
47
+ * The selected key in the ComboBox.
48
+ *
49
+ * @deprecated
50
+ */
51
+ selectedKey: Key | null;
52
+ /** The keys of the currently selected items. */
53
+ value: ValidationType<M>;
54
+ /** The value of the ComboBox input. */
55
+ inputValue: string;
56
+ }
57
+
58
+ export interface ComboBoxProps<T, M extends SelectionMode = 'single'>
59
+ extends
60
+ CollectionBase<T>,
61
+ InputBase,
62
+ ValueBase<ValueType<M>, ChangeValueType<M>>,
63
+ TextInputBase,
64
+ Validation<ComboBoxValidationValue<M>>,
65
+ FocusableProps<HTMLInputElement>,
66
+ LabelableProps,
67
+ HelpTextProps {
68
+ /** The list of ComboBox items (uncontrolled). */
69
+ defaultItems?: Iterable<T>;
70
+ /** The list of ComboBox items (controlled). */
71
+ items?: Iterable<T>;
72
+ /**
73
+ * Method that is called when the open state of the menu changes. Returns the new open state and
74
+ * the action that caused the opening of the menu.
75
+ */
76
+ onOpenChange?: (isOpen: boolean, menuTrigger?: MenuTriggerAction) => void;
77
+ /**
78
+ * Whether single or multiple selection is enabled.
79
+ *
80
+ * @default 'single'
81
+ */
82
+ selectionMode?: M;
83
+ /**
84
+ * The currently selected key in the collection (controlled).
85
+ *
86
+ * @deprecated
87
+ */
88
+ selectedKey?: Key | null;
89
+ /**
90
+ * The initial selected key in the collection (uncontrolled).
91
+ *
92
+ * @deprecated
93
+ */
94
+ defaultSelectedKey?: Key | null;
95
+ /**
96
+ * Handler that is called when the selection changes.
97
+ *
98
+ * @deprecated
99
+ */
100
+ onSelectionChange?: (key: Key | null) => void;
101
+ /** The value of the ComboBox input (controlled). */
102
+ inputValue?: string;
103
+ /** The default value of the ComboBox input (uncontrolled). */
104
+ defaultInputValue?: string;
105
+ /** Handler that is called when the ComboBox input value changes. */
106
+ onInputChange?: (value: string) => void;
107
+ /** Whether the ComboBox allows a non-item matching input value to be set. */
108
+ allowsCustomValue?: boolean;
109
+ // /**
110
+ // * Whether the Combobox should only suggest matching options or autocomplete the field with the nearest matching option.
111
+ // * @default 'suggest'
112
+ // */
113
+ // completionMode?: 'suggest' | 'complete',
114
+ /**
115
+ * The interaction required to display the ComboBox menu.
116
+ *
117
+ * @default 'input'
118
+ */
119
+ menuTrigger?: MenuTriggerAction;
120
+ }
121
+
122
+ export interface ComboBoxState<T, M extends SelectionMode = 'single'>
123
+ extends ListState<T>, OverlayTriggerState, FormValidationState {
124
+ /**
125
+ * The key for the first selected item.
126
+ *
127
+ * @deprecated
128
+ */
129
+ readonly selectedKey: Key | null;
130
+
131
+ /**
132
+ * The default selected key.
133
+ *
134
+ * @deprecated
135
+ */
136
+ readonly defaultSelectedKey: Key | null;
137
+ /**
138
+ * Sets the selected key.
139
+ *
140
+ * @deprecated
141
+ */
142
+ setSelectedKey(key: Key | null): void;
143
+ /** The current combobox value. */
144
+ readonly value: ValueType<M>;
145
+ /** The default combobox value. */
146
+ readonly defaultValue: ValueType<M>;
147
+ /** Sets the combobox value. */
148
+ setValue(value: Key | readonly Key[] | null): void;
149
+ /**
150
+ * The value of the first selected item.
151
+ *
152
+ * @deprecated
153
+ */
154
+ readonly selectedItem: Node<T> | null;
155
+ /** The value of the selected items. */
156
+ readonly selectedItems: Node<T>[];
157
+ /** The current value of the combo box input. */
158
+ inputValue: string;
159
+ /** The default value of the combo box input. */
160
+ defaultInputValue: string;
161
+ /** Sets the value of the combo box input. */
162
+ setInputValue(value: string): void;
163
+ /** Selects the currently focused item and updates the input value. */
164
+ commit(): void;
165
+ /** Controls which item will be auto focused when the menu opens. */
166
+ readonly focusStrategy: FocusStrategy | null;
167
+ /** Whether the select is currently focused. */
168
+ readonly isFocused: boolean;
169
+ /** Sets whether the select is focused. */
170
+ setFocused(isFocused: boolean): void;
171
+ /** Opens the menu. */
172
+ open(focusStrategy?: FocusStrategy | null, trigger?: MenuTriggerAction): void;
173
+ /** Toggles the menu. */
174
+ toggle(focusStrategy?: FocusStrategy | null, trigger?: MenuTriggerAction): void;
175
+ /** Resets the input value to the previously selected item's text if any and closes the menu. */
176
+ revert(): void;
177
+ }
178
+
179
+ type FilterFn = (textValue: string, inputValue: string) => boolean;
180
+
181
+ export interface ComboBoxStateOptions<T, M extends SelectionMode = 'single'>
182
+ extends Omit<ComboBoxProps<T, M>, 'children'>, CollectionStateBase<T> {
183
+ /** The filter function used to determine if a option should be included in the combo box list. */
184
+ defaultFilter?: FilterFn;
185
+ /** Whether the combo box allows the menu to be open when the collection is empty. */
186
+ allowsEmptyCollection?: boolean;
187
+ /** Whether the combo box menu should close on blur. */
188
+ shouldCloseOnBlur?: boolean;
189
+ }
190
+
191
+ /**
192
+ * Provides state management for a combo box component. Handles building a collection of items from
193
+ * props and manages the option selection state of the combo box. In addition, it tracks the input
194
+ * value, focus state, and other properties of the combo box.
195
+ */
196
+ export function useComboBoxState<T, M extends SelectionMode = 'single'>(
197
+ props: ComboBoxStateOptions<T, M>,
198
+ ): ComboBoxState<T, M>;
199
+ // Slot-threading form: sibling ported hooks pass their derived sub-slot as the trailing arg.
200
+ export function useComboBoxState<T, M extends SelectionMode = 'single'>(
201
+ props: ComboBoxStateOptions<T, M>,
202
+ slot: symbol | undefined,
203
+ ): ComboBoxState<T, M>;
204
+ export function useComboBoxState(...args: any[]): ComboBoxState<any, any> {
205
+ const [user, slotArg] = splitSlot(args);
206
+ const slot = slotArg ?? S('useComboBoxState');
207
+ const props = user[0] as ComboBoxStateOptions<any, any>;
208
+
209
+ let {
210
+ defaultFilter,
211
+ menuTrigger = 'input',
212
+ allowsEmptyCollection = false,
213
+ allowsCustomValue,
214
+ shouldCloseOnBlur = true,
215
+ selectionMode = 'single' as SelectionMode,
216
+ } = props;
217
+
218
+ let [showAllItems, setShowAllItems] = useState(false, subSlot(slot, 'showAll'));
219
+ let [isFocused, setFocusedState] = useState(false, subSlot(slot, 'focused'));
220
+ let [focusStrategy, setFocusStrategy] = useState<FocusStrategy | null>(
221
+ null,
222
+ subSlot(slot, 'focusStrategy'),
223
+ );
224
+
225
+ let defaultValue = useMemo(
226
+ () => {
227
+ return props.defaultValue !== undefined
228
+ ? props.defaultValue
229
+ : selectionMode === 'single'
230
+ ? (props.defaultSelectedKey ?? null)
231
+ : [];
232
+ },
233
+ [props.defaultValue, props.defaultSelectedKey, selectionMode],
234
+ subSlot(slot, 'defaultValue'),
235
+ );
236
+ let value = useMemo(
237
+ () => {
238
+ return props.value !== undefined
239
+ ? props.value
240
+ : selectionMode === 'single'
241
+ ? props.selectedKey
242
+ : undefined;
243
+ },
244
+ [props.value, props.selectedKey, selectionMode],
245
+ subSlot(slot, 'value'),
246
+ );
247
+ let [controlledValue, setControlledValue] = useControlledState<Key | readonly Key[] | null>(
248
+ value,
249
+ defaultValue,
250
+ props.onChange as any,
251
+ subSlot(slot, 'controlled'),
252
+ );
253
+ // Only display the first selected item if in single selection mode but the value is an array.
254
+ let displayValue =
255
+ selectionMode === 'single' && Array.isArray(controlledValue)
256
+ ? controlledValue[0]
257
+ : controlledValue;
258
+
259
+ let setValue = (value: Key | Key[] | null) => {
260
+ if (selectionMode === 'single') {
261
+ let key = Array.isArray(value) ? (value[0] ?? null) : value;
262
+ setControlledValue(key);
263
+ if (key !== displayValue) {
264
+ props.onSelectionChange?.(key);
265
+ }
266
+ } else {
267
+ let keys: Key[] = [];
268
+ if (Array.isArray(value)) {
269
+ keys = value;
270
+ } else if (value != null) {
271
+ keys = [value];
272
+ }
273
+
274
+ setControlledValue(keys);
275
+ }
276
+ };
277
+
278
+ let { collection, selectionManager, disabledKeys } = useListState(
279
+ {
280
+ ...props,
281
+ items: props.items ?? props.defaultItems,
282
+ selectionMode,
283
+ disallowEmptySelection: selectionMode === 'single',
284
+ allowDuplicateSelectionEvents: true,
285
+ selectedKeys: useMemo(
286
+ () => convertValue(displayValue),
287
+ [displayValue],
288
+ subSlot(slot, 'selectedKeys'),
289
+ ),
290
+ onSelectionChange: (keys: Selection) => {
291
+ // impossible, but TS doesn't know that
292
+ if (keys === 'all') {
293
+ return;
294
+ }
295
+
296
+ if (selectionMode === 'single') {
297
+ let key = keys.values().next().value ?? null;
298
+ if (key === displayValue) {
299
+ props.onSelectionChange?.(key);
300
+ // If key is the same, reset the inputValue and close the menu
301
+ // (scenario: user clicks on already selected option)
302
+ resetInputValue();
303
+ closeMenu();
304
+ } else {
305
+ setValue(key);
306
+ }
307
+ } else {
308
+ setValue([...keys]);
309
+ }
310
+ },
311
+ },
312
+ subSlot(slot, 'list'),
313
+ );
314
+
315
+ let selectedKey = selectionMode === 'single' ? selectionManager.firstSelectedKey : null;
316
+ let selectedItems = useMemo(
317
+ () => {
318
+ return [...selectionManager.selectedKeys]
319
+ .map((key) => collection.getItem(key))
320
+ .filter((item) => item != null);
321
+ },
322
+ [selectionManager.selectedKeys, collection],
323
+ subSlot(slot, 'selectedItems'),
324
+ );
325
+
326
+ let [inputValue, setInputValue] = useControlledState(
327
+ props.inputValue,
328
+ getDefaultInputValue(props.defaultInputValue, selectedKey, collection) || '',
329
+ props.onInputChange,
330
+ subSlot(slot, 'input'),
331
+ );
332
+ let [initialValue] = useState(displayValue, subSlot(slot, 'initial'));
333
+ let [initialInputValue] = useState(inputValue, subSlot(slot, 'initialInput'));
334
+
335
+ // Preserve original collection so we can show all items on demand
336
+ let originalCollection = collection;
337
+ let filteredCollection = useMemo(
338
+ () =>
339
+ // No default filter if items are controlled.
340
+ props.items != null || !defaultFilter
341
+ ? collection
342
+ : filterCollection(collection, inputValue, defaultFilter),
343
+ [collection, inputValue, defaultFilter, props.items],
344
+ subSlot(slot, 'filtered'),
345
+ );
346
+ let [lastCollection, setLastCollection] = useState(filteredCollection, subSlot(slot, 'lastColl'));
347
+
348
+ // Track what action is attempting to open the menu
349
+ let menuOpenTrigger = useRef<MenuTriggerAction | undefined>(
350
+ 'focus',
351
+ subSlot(slot, 'openTrigger'),
352
+ );
353
+ let onOpenChange = (open: boolean) => {
354
+ if (props.onOpenChange) {
355
+ props.onOpenChange(open, open ? menuOpenTrigger.current : undefined);
356
+ }
357
+
358
+ selectionManager.setFocused(open);
359
+ if (!open) {
360
+ selectionManager.setFocusedKey(null);
361
+ }
362
+ };
363
+
364
+ let triggerState = useOverlayTriggerState(
365
+ {
366
+ ...props,
367
+ onOpenChange,
368
+ isOpen: undefined,
369
+ defaultOpen: undefined,
370
+ },
371
+ subSlot(slot, 'trigger'),
372
+ );
373
+ let open = (focusStrategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
374
+ let displayAllItems = trigger === 'manual' || (trigger === 'focus' && menuTrigger === 'focus');
375
+ // Prevent open operations from triggering if there is nothing to display
376
+ // Also prevent open operations from triggering if items are uncontrolled but defaultItems is empty, even if displayAllItems is true.
377
+ // This is to prevent comboboxes with empty defaultItems from opening but allow controlled items comboboxes to open even if the inital list is empty (assumption is user will provide swap the empty list with a base list via onOpenChange returning `menuTrigger` manual)
378
+ if (
379
+ allowsEmptyCollection ||
380
+ filteredCollection.size > 0 ||
381
+ (displayAllItems && originalCollection.size > 0) ||
382
+ props.items
383
+ ) {
384
+ if (displayAllItems && !triggerState.isOpen && props.items === undefined) {
385
+ // Show all items if menu is manually opened. Only care about this if items are undefined
386
+ setShowAllItems(true);
387
+ }
388
+
389
+ menuOpenTrigger.current = trigger;
390
+ setFocusStrategy(focusStrategy);
391
+ triggerState.open();
392
+ }
393
+ };
394
+
395
+ let toggle = (focusStrategy: FocusStrategy | null = null, trigger?: MenuTriggerAction) => {
396
+ let displayAllItems = trigger === 'manual' || (trigger === 'focus' && menuTrigger === 'focus');
397
+ // If the menu is closed and there is nothing to display, early return so toggle isn't called to prevent extraneous onOpenChange
398
+ if (
399
+ !(
400
+ allowsEmptyCollection ||
401
+ filteredCollection.size > 0 ||
402
+ (displayAllItems && originalCollection.size > 0) ||
403
+ props.items
404
+ ) &&
405
+ !triggerState.isOpen
406
+ ) {
407
+ return;
408
+ }
409
+
410
+ if (displayAllItems && !triggerState.isOpen && props.items === undefined) {
411
+ // Show all items if menu is toggled open. Only care about this if items are undefined
412
+ setShowAllItems(true);
413
+ }
414
+
415
+ // Only update the menuOpenTrigger if menu is currently closed
416
+ if (!triggerState.isOpen) {
417
+ menuOpenTrigger.current = trigger;
418
+ }
419
+
420
+ toggleMenu(focusStrategy);
421
+ };
422
+
423
+ let updateLastCollection = useCallback(
424
+ () => {
425
+ setLastCollection(showAllItems ? originalCollection : filteredCollection);
426
+ },
427
+ [showAllItems, originalCollection, filteredCollection],
428
+ subSlot(slot, 'updateLast'),
429
+ );
430
+
431
+ // If menu is going to close, save the current collection so we can freeze the displayed collection when the
432
+ // user clicks outside the popover to close the menu. Prevents the menu contents from updating as the menu closes.
433
+ let toggleMenu = useCallback(
434
+ (focusStrategy: FocusStrategy | null = null) => {
435
+ if (triggerState.isOpen) {
436
+ updateLastCollection();
437
+ }
438
+
439
+ setFocusStrategy(focusStrategy);
440
+ triggerState.toggle();
441
+ },
442
+ [triggerState, updateLastCollection],
443
+ subSlot(slot, 'toggleMenu'),
444
+ );
445
+
446
+ let closeMenu = useCallback(
447
+ () => {
448
+ if (triggerState.isOpen) {
449
+ updateLastCollection();
450
+ triggerState.close();
451
+ }
452
+ },
453
+ [triggerState, updateLastCollection],
454
+ subSlot(slot, 'closeMenu'),
455
+ );
456
+
457
+ let [lastValue, setLastValue] = useState(inputValue, subSlot(slot, 'lastValue'));
458
+ let resetInputValue = () => {
459
+ let itemText = selectedKey != null ? (collection.getItem(selectedKey)?.textValue ?? '') : '';
460
+ setLastValue(itemText);
461
+ setInputValue(itemText);
462
+ };
463
+
464
+ let lastValueRef = useRef(displayValue, subSlot(slot, 'lastValueRef'));
465
+ let lastSelectedKeyText = useRef(
466
+ selectedKey != null ? (collection.getItem(selectedKey)?.textValue ?? '') : '',
467
+ subSlot(slot, 'lastKeyText'),
468
+ );
469
+ // intentional omit dependency array, want this to happen on every render
470
+ useEffect(
471
+ () => {
472
+ // Open and close menu automatically when the input value changes if the input is focused,
473
+ // and there are items in the collection or allowEmptyCollection is true.
474
+ if (
475
+ isFocused &&
476
+ (filteredCollection.size > 0 || allowsEmptyCollection) &&
477
+ !triggerState.isOpen &&
478
+ inputValue !== lastValue &&
479
+ menuTrigger !== 'manual'
480
+ ) {
481
+ open(null, 'input');
482
+ }
483
+
484
+ // Close the menu if the collection is empty. Don't close menu if filtered collection size is 0
485
+ // but we are currently showing all items via button press
486
+ if (
487
+ !showAllItems &&
488
+ !allowsEmptyCollection &&
489
+ triggerState.isOpen &&
490
+ filteredCollection.size === 0
491
+ ) {
492
+ closeMenu();
493
+ }
494
+
495
+ // Close when an item is selected.
496
+ if (
497
+ displayValue != null &&
498
+ displayValue !== lastValueRef.current &&
499
+ selectionMode === 'single'
500
+ ) {
501
+ closeMenu();
502
+ }
503
+
504
+ // Clear focused key when input value changes and display filtered collection again.
505
+ if (inputValue !== lastValue) {
506
+ selectionManager.setFocusedKey(null);
507
+ setShowAllItems(false);
508
+
509
+ // Set value to null when the user clears the input.
510
+ // If controlled, this is the application developer's responsibility.
511
+ if (
512
+ selectionMode === 'single' &&
513
+ inputValue === '' &&
514
+ (props.inputValue === undefined || value === undefined)
515
+ ) {
516
+ setValue(null);
517
+ }
518
+ }
519
+
520
+ // If the value changed, update the input value.
521
+ // Do nothing if both inputValue and value are controlled.
522
+ // In this case, it's the user's responsibility to update inputValue in onSelectionChange.
523
+ if (
524
+ displayValue !== lastValueRef.current &&
525
+ (props.inputValue === undefined || value === undefined)
526
+ ) {
527
+ resetInputValue();
528
+ } else if (lastValue !== inputValue) {
529
+ setLastValue(inputValue);
530
+ }
531
+
532
+ // Update the inputValue if the selected item's text changes from its last tracked value.
533
+ // This is to handle cases where a selectedKey is specified but the items aren't available (async loading) or the selected item's text value updates.
534
+ // Only reset if the user isn't currently within the field so we don't erroneously modify user input.
535
+ // If inputValue is controlled, it is the user's responsibility to update the inputValue when items change.
536
+ let selectedItemText =
537
+ selectedKey != null ? (collection.getItem(selectedKey)?.textValue ?? '') : '';
538
+ if (
539
+ !isFocused &&
540
+ selectedKey != null &&
541
+ props.inputValue === undefined &&
542
+ selectedKey === lastValueRef.current
543
+ ) {
544
+ if (lastSelectedKeyText.current !== selectedItemText) {
545
+ setLastValue(selectedItemText);
546
+ setInputValue(selectedItemText);
547
+ }
548
+ }
549
+
550
+ lastValueRef.current = displayValue;
551
+ lastSelectedKeyText.current = selectedItemText;
552
+ },
553
+ null,
554
+ subSlot(slot, 'sync'),
555
+ );
556
+
557
+ let validation = useFormValidationState(
558
+ {
559
+ ...props,
560
+ value: useMemo(
561
+ () =>
562
+ Array.isArray(displayValue) && displayValue.length === 0
563
+ ? null
564
+ : { inputValue, value: displayValue as any, selectedKey },
565
+ [inputValue, selectedKey, displayValue],
566
+ subSlot(slot, 'validationValue'),
567
+ ),
568
+ },
569
+ subSlot(slot, 'validation'),
570
+ );
571
+
572
+ // Revert input value and close menu
573
+ let revert = () => {
574
+ if (allowsCustomValue && selectedKey == null) {
575
+ commitCustomValue();
576
+ } else {
577
+ commitSelection();
578
+ }
579
+ };
580
+
581
+ let commitCustomValue = () => {
582
+ if (selectionMode === 'multiple') {
583
+ // In multi-select mode, the input's custom text is independent from the selected items.
584
+ // Closing or committing the field should not clear the existing selection.
585
+ setLastValue(inputValue);
586
+ closeMenu();
587
+ return;
588
+ }
589
+
590
+ let value = null;
591
+ lastValueRef.current = value as any;
592
+ setValue(value);
593
+ closeMenu();
594
+ };
595
+
596
+ let commitSelection = (shouldForceSelectionChange = false) => {
597
+ // If multiple things are controlled, call onSelectionChange only when selecting the focused item,
598
+ // or when inputValue needs to be synced back to the selected item on commit/blur.
599
+ if (value !== undefined && props.inputValue !== undefined) {
600
+ let itemText = selectedKey != null ? (collection.getItem(selectedKey)?.textValue ?? '') : '';
601
+ if (shouldForceSelectionChange || selectionMode === 'multiple' || inputValue !== itemText) {
602
+ props.onSelectionChange?.(selectedKey);
603
+ props.onChange?.(displayValue as any);
604
+ }
605
+
606
+ // Stop menu from reopening from useEffect
607
+ setLastValue(itemText);
608
+ closeMenu();
609
+ } else {
610
+ // If only a single aspect of combobox is controlled, reset input value and close menu for the user
611
+ resetInputValue();
612
+ closeMenu();
613
+ }
614
+ };
615
+
616
+ const commitValue = () => {
617
+ if (allowsCustomValue) {
618
+ const itemText =
619
+ selectedKey != null ? (collection.getItem(selectedKey)?.textValue ?? '') : '';
620
+ inputValue === itemText ? commitSelection() : commitCustomValue();
621
+ } else {
622
+ // Reset inputValue and close menu
623
+ commitSelection();
624
+ }
625
+ };
626
+
627
+ let commit = () => {
628
+ if (triggerState.isOpen && selectionManager.focusedKey != null) {
629
+ // Reset inputValue and close menu here if the selected key is already the focused key. Otherwise
630
+ // fire onSelectionChange to allow the application to control the closing.
631
+ if (selectionManager.isSelected(selectionManager.focusedKey) && selectionMode === 'single') {
632
+ commitSelection(true);
633
+ } else {
634
+ selectionManager.select(selectionManager.focusedKey);
635
+ }
636
+ } else {
637
+ commitValue();
638
+ }
639
+ };
640
+
641
+ let valueOnFocus = useRef([inputValue, displayValue], subSlot(slot, 'valueOnFocus'));
642
+ let setFocused = (isFocused: boolean) => {
643
+ if (isFocused) {
644
+ valueOnFocus.current = [inputValue, displayValue];
645
+ if (menuTrigger === 'focus' && !props.isReadOnly) {
646
+ open(null, 'focus');
647
+ }
648
+ } else {
649
+ if (shouldCloseOnBlur) {
650
+ commitValue();
651
+ }
652
+
653
+ // Commit validation if the input value or selected items changed.
654
+ if (inputValue !== valueOnFocus.current[0] || displayValue !== valueOnFocus.current[1]) {
655
+ validation.commitValidation();
656
+ }
657
+ }
658
+
659
+ setFocusedState(isFocused);
660
+ };
661
+
662
+ let displayedCollection = useMemo(
663
+ () => {
664
+ if (triggerState.isOpen) {
665
+ if (showAllItems) {
666
+ return originalCollection;
667
+ } else {
668
+ return filteredCollection;
669
+ }
670
+ } else {
671
+ return lastCollection;
672
+ }
673
+ },
674
+ [triggerState.isOpen, originalCollection, filteredCollection, showAllItems, lastCollection],
675
+ subSlot(slot, 'displayed'),
676
+ );
677
+
678
+ let defaultSelectedKey =
679
+ props.defaultSelectedKey ?? (selectionMode === 'single' ? (initialValue as Key) : null);
680
+
681
+ return {
682
+ ...validation,
683
+ ...triggerState,
684
+ focusStrategy,
685
+ toggle,
686
+ open,
687
+ close: commitValue,
688
+ selectionManager,
689
+ value: displayValue as any,
690
+ defaultValue: defaultValue ?? (initialValue as any),
691
+ setValue,
692
+ selectedKey,
693
+ selectedItems,
694
+ defaultSelectedKey,
695
+ setSelectedKey: setValue,
696
+ disabledKeys,
697
+ isFocused,
698
+ setFocused,
699
+ selectedItem: selectedItems[0] ?? null,
700
+ collection: displayedCollection,
701
+ inputValue,
702
+ defaultInputValue:
703
+ getDefaultInputValue(props.defaultInputValue, defaultSelectedKey, collection) ??
704
+ initialInputValue,
705
+ setInputValue,
706
+ commit,
707
+ revert,
708
+ };
709
+ }
710
+
711
+ function filterCollection<T>(
712
+ collection: Collection<Node<T>>,
713
+ inputValue: string,
714
+ filter: FilterFn,
715
+ ): Collection<Node<T>> {
716
+ return new ListCollection(filterNodes(collection, collection, inputValue, filter));
717
+ }
718
+
719
+ function filterNodes<T>(
720
+ collection: Collection<Node<T>>,
721
+ nodes: Iterable<Node<T>>,
722
+ inputValue: string,
723
+ filter: FilterFn,
724
+ ): Iterable<Node<T>> {
725
+ let filteredNode: Node<T>[] = [];
726
+ for (let node of nodes) {
727
+ if (node.type === 'section' && node.hasChildNodes) {
728
+ let filtered = filterNodes(collection, getChildNodes(node, collection), inputValue, filter);
729
+ if ([...filtered].some((node) => node.type === 'item')) {
730
+ filteredNode.push({ ...node, childNodes: filtered });
731
+ }
732
+ } else if (node.type === 'item' && filter(node.textValue, inputValue)) {
733
+ filteredNode.push({ ...node });
734
+ } else if (node.type !== 'item') {
735
+ filteredNode.push({ ...node });
736
+ }
737
+ }
738
+ return filteredNode;
739
+ }
740
+
741
+ function getDefaultInputValue(
742
+ defaultInputValue: string | null | undefined,
743
+ selectedKey: Key | null,
744
+ collection: Collection<Node<unknown>>,
745
+ ) {
746
+ if (defaultInputValue == null) {
747
+ if (selectedKey != null) {
748
+ return collection.getItem(selectedKey)?.textValue ?? '';
749
+ }
750
+ }
751
+
752
+ return defaultInputValue;
753
+ }
754
+
755
+ function convertValue(value: Key | readonly Key[] | null | undefined) {
756
+ if (value === undefined) {
757
+ return undefined;
758
+ }
759
+ if (value === null) {
760
+ return [];
761
+ }
762
+ return Array.isArray(value) ? value : [value];
763
+ }