@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,856 @@
1
+ // Ported from react-aria (source: .react-spectrum/packages/react-aria/src/overlays/calculatePosition.ts).
2
+ // octane adaptations: none behavioral — pure positioning geometry, no hooks. Only the imports
3
+ // are re-pointed: `clamp` from the ported `../stately/utils/number` (upstream reads it from
4
+ // `react-stately/private/utils/number`); `isWebKit`/`nodeContains` from the ported utils.
5
+ import { Axis, Placement, PlacementAxis, SizeAxis } from './useOverlayPosition';
6
+ import { clamp } from '../stately/utils/number';
7
+ import { isWebKit } from '../utils/platform';
8
+ import { nodeContains } from '../utils/shadowdom/DOMFunctions';
9
+
10
+ interface Position {
11
+ top?: number;
12
+ left?: number;
13
+ bottom?: number;
14
+ right?: number;
15
+ // octane adaptation: upstream indexes these bags by dynamic axis strings; the stricter
16
+ // binding tsconfig needs an explicit index signature to allow that (no behavior change).
17
+ [key: string]: any;
18
+ }
19
+
20
+ interface Dimensions {
21
+ width: number;
22
+ height: number;
23
+ totalWidth: number;
24
+ totalHeight: number;
25
+ top: number;
26
+ left: number;
27
+ scroll: Position;
28
+ [key: string]: any;
29
+ }
30
+
31
+ interface ParsedPlacement {
32
+ placement: PlacementAxis;
33
+ crossPlacement: PlacementAxis;
34
+ axis: Axis;
35
+ crossAxis: Axis;
36
+ size: SizeAxis;
37
+ crossSize: SizeAxis;
38
+ }
39
+
40
+ interface Offset {
41
+ top: number;
42
+ left: number;
43
+ width: number;
44
+ height: number;
45
+ [key: string]: number;
46
+ }
47
+
48
+ interface PositionOpts {
49
+ arrowSize: number;
50
+ placement: Placement;
51
+ targetNode: Element;
52
+ overlayNode: Element;
53
+ scrollNode: Element;
54
+ padding: number;
55
+ shouldFlip: boolean;
56
+ boundaryElement: Element;
57
+ offset: number;
58
+ crossOffset: number;
59
+ maxHeight?: number;
60
+ arrowBoundaryOffset?: number;
61
+ targetRect?: Offset | null | undefined;
62
+ }
63
+
64
+ type HeightGrowthDirection = 'top' | 'bottom';
65
+
66
+ export interface PositionResult {
67
+ position: Position;
68
+ arrowOffsetLeft?: number;
69
+ arrowOffsetTop?: number;
70
+ triggerAnchorPoint: { x: number; y: number };
71
+ maxHeight: number;
72
+ placement: PlacementAxis;
73
+ }
74
+
75
+ // octane adaptation: these lookup maps are indexed by dynamic axis strings; typing them as
76
+ // records keeps that legal under the stricter binding tsconfig (upstream's untyped consts
77
+ // resolve to `any` on index, same effect).
78
+ const AXIS: Record<string, any> = {
79
+ top: 'top',
80
+ bottom: 'top',
81
+ left: 'left',
82
+ right: 'left',
83
+ };
84
+
85
+ const FLIPPED_DIRECTION: Record<string, any> = {
86
+ top: 'bottom',
87
+ bottom: 'top',
88
+ left: 'right',
89
+ right: 'left',
90
+ };
91
+
92
+ const CROSS_AXIS: Record<string, any> = {
93
+ top: 'left',
94
+ left: 'top',
95
+ };
96
+
97
+ const AXIS_SIZE: Record<string, any> = {
98
+ top: 'height',
99
+ left: 'width',
100
+ };
101
+
102
+ const TOTAL_SIZE: Record<string, any> = {
103
+ width: 'totalWidth',
104
+ height: 'totalHeight',
105
+ };
106
+
107
+ const PARSED_PLACEMENT_CACHE: Record<string, ParsedPlacement> = {};
108
+
109
+ let getVisualViewport = () => (typeof document !== 'undefined' ? window.visualViewport : null);
110
+
111
+ function getContainerDimensions(
112
+ containerNode: Element,
113
+ visualViewport: VisualViewport | null,
114
+ ): Dimensions {
115
+ let width = 0,
116
+ height = 0,
117
+ totalWidth = 0,
118
+ totalHeight = 0,
119
+ top = 0,
120
+ left = 0;
121
+ let scroll: Position = {};
122
+ let isPinchZoomedIn = (visualViewport?.scale ?? 1) > 1;
123
+
124
+ // In the case where the container is `html` or `body` and the container doesn't have something like `position: relative`,
125
+ // then position absolute will be positioned relative to the viewport, also known as the `initial containing block`.
126
+ // That's why we use the visual viewport instead.
127
+
128
+ if (containerNode.tagName === 'BODY' || containerNode.tagName === 'HTML') {
129
+ let documentElement = document.documentElement;
130
+ totalWidth = documentElement.clientWidth;
131
+ totalHeight = documentElement.clientHeight;
132
+ width = visualViewport?.width ?? totalWidth;
133
+ height = visualViewport?.height ?? totalHeight;
134
+ scroll.top = documentElement.scrollTop || containerNode.scrollTop;
135
+ scroll.left = documentElement.scrollLeft || containerNode.scrollLeft;
136
+
137
+ // The goal of the below is to get a top/left value that represents the top/left of the visual viewport with
138
+ // respect to the layout viewport origin. This combined with the scrollTop/scrollLeft will allow us to calculate
139
+ // coordinates/values with respect to the visual viewport or with respect to the layout viewport.
140
+ if (visualViewport) {
141
+ top = visualViewport.offsetTop;
142
+ left = visualViewport.offsetLeft;
143
+ }
144
+ } else {
145
+ ({ width, height, top, left } = getOffset(containerNode, false));
146
+ scroll.top = containerNode.scrollTop;
147
+ scroll.left = containerNode.scrollLeft;
148
+ totalWidth = width;
149
+ totalHeight = height;
150
+ }
151
+
152
+ if (
153
+ isWebKit() &&
154
+ (containerNode.tagName === 'BODY' || containerNode.tagName === 'HTML') &&
155
+ isPinchZoomedIn
156
+ ) {
157
+ // Safari will report a non-zero scrollTop/Left for the non-scrolling body/HTML element when pinch zoomed in unlike other browsers.
158
+ // Set to zero for parity calculations so we get consistent positioning of overlays across all browsers.
159
+ // Also switch to visualViewport.pageTop/pageLeft so that we still accomodate for scroll positioning for body/HTML elements that are actually scrollable
160
+ // before pinch zoom happens
161
+ scroll.top = 0;
162
+ scroll.left = 0;
163
+ top = visualViewport?.pageTop ?? 0;
164
+ left = visualViewport?.pageLeft ?? 0;
165
+ }
166
+
167
+ return { width, height, totalWidth, totalHeight, scroll, top, left };
168
+ }
169
+
170
+ function getScroll(node: Element): Offset {
171
+ return {
172
+ top: node.scrollTop,
173
+ left: node.scrollLeft,
174
+ width: node.scrollWidth,
175
+ height: node.scrollHeight,
176
+ };
177
+ }
178
+
179
+ // Determines the amount of space required when moving the overlay to ensure it remains in the boundary
180
+ function getDelta(
181
+ axis: Axis,
182
+ offset: number,
183
+ size: number,
184
+ // The dimensions of the boundary element that the popover is
185
+ // positioned within (most of the time this is the <body>).
186
+ boundaryDimensions: Dimensions,
187
+ // The dimensions of the containing block element that the popover is
188
+ // positioned relative to (e.g. parent with position: relative).
189
+ // Usually this is the same as the boundary element, but if the popover
190
+ // is portaled somewhere other than the body and has an ancestor with
191
+ // position: relative/absolute, it will be different.
192
+ containerDimensions: Dimensions,
193
+ padding: number,
194
+ containerOffsetWithBoundary: Offset,
195
+ ) {
196
+ let containerScroll = containerDimensions.scroll[axis] ?? 0;
197
+ // The height/width of the boundary. Matches the axis along which we are adjusting the overlay position
198
+ let boundarySize = boundaryDimensions[AXIS_SIZE[axis]];
199
+ // Calculate the edges of the boundary (accomodating for the boundary padding) and the edges of the overlay.
200
+ // Note that these values are with respect to the visual viewport (aka 0,0 is the top left of the viewport)
201
+
202
+ let boundaryStartEdge =
203
+ containerOffsetWithBoundary[axis] + boundaryDimensions.scroll[AXIS[axis]] + padding;
204
+ let boundaryEndEdge =
205
+ containerOffsetWithBoundary[axis] +
206
+ boundaryDimensions.scroll[AXIS[axis]] +
207
+ boundarySize -
208
+ padding;
209
+ // transformed value of the left edge of the overlay
210
+ let startEdgeOffset =
211
+ offset -
212
+ containerScroll +
213
+ boundaryDimensions.scroll[AXIS[axis]] +
214
+ containerOffsetWithBoundary[axis] -
215
+ boundaryDimensions[AXIS[axis]];
216
+ // transformed value of the right edge of the overlay
217
+ let endEdgeOffset =
218
+ offset -
219
+ containerScroll +
220
+ size +
221
+ boundaryDimensions.scroll[AXIS[axis]] +
222
+ containerOffsetWithBoundary[axis] -
223
+ boundaryDimensions[AXIS[axis]];
224
+
225
+ // If any of the overlay edges falls outside of the boundary, shift the overlay the required amount to align one of the overlay's
226
+ // edges with the closest boundary edge.
227
+ if (startEdgeOffset < boundaryStartEdge) {
228
+ return boundaryStartEdge - startEdgeOffset;
229
+ } else if (endEdgeOffset > boundaryEndEdge) {
230
+ return Math.max(boundaryEndEdge - endEdgeOffset, boundaryStartEdge - startEdgeOffset);
231
+ } else {
232
+ return 0;
233
+ }
234
+ }
235
+
236
+ function getMargins(node: Element): Position {
237
+ let style = window.getComputedStyle(node);
238
+ return {
239
+ top: parseInt(style.marginTop, 10) || 0,
240
+ bottom: parseInt(style.marginBottom, 10) || 0,
241
+ left: parseInt(style.marginLeft, 10) || 0,
242
+ right: parseInt(style.marginRight, 10) || 0,
243
+ };
244
+ }
245
+
246
+ function parsePlacement(input: Placement): ParsedPlacement {
247
+ if (PARSED_PLACEMENT_CACHE[input]) {
248
+ return PARSED_PLACEMENT_CACHE[input];
249
+ }
250
+
251
+ let [placement, crossPlacement] = input.split(' ');
252
+ let axis: Axis = AXIS[placement] || 'right';
253
+ let crossAxis: Axis = CROSS_AXIS[axis];
254
+
255
+ if (!AXIS[crossPlacement]) {
256
+ crossPlacement = 'center';
257
+ }
258
+
259
+ let size = AXIS_SIZE[axis];
260
+ let crossSize = AXIS_SIZE[crossAxis];
261
+ PARSED_PLACEMENT_CACHE[input] = {
262
+ placement,
263
+ crossPlacement,
264
+ axis,
265
+ crossAxis,
266
+ size,
267
+ crossSize,
268
+ } as ParsedPlacement;
269
+ return PARSED_PLACEMENT_CACHE[input];
270
+ }
271
+
272
+ function computePosition(
273
+ childOffset: Offset,
274
+ boundaryDimensions: Dimensions,
275
+ overlaySize: Offset,
276
+ placementInfo: ParsedPlacement,
277
+ offset: number,
278
+ crossOffset: number,
279
+ containerOffsetWithBoundary: Offset,
280
+ isContainerPositioned: boolean,
281
+ arrowSize: number,
282
+ arrowBoundaryOffset: number,
283
+ containerDimensions: Dimensions,
284
+ ) {
285
+ let { placement, crossPlacement, axis, crossAxis, size, crossSize } = placementInfo;
286
+ let position: Position = {};
287
+
288
+ // button position
289
+ position[crossAxis] = childOffset[crossAxis] ?? 0;
290
+ if (crossPlacement === 'center') {
291
+ // + (button size / 2) - (overlay size / 2)
292
+ // at this point the overlay center should match the button center
293
+ position[crossAxis]! += ((childOffset[crossSize] ?? 0) - (overlaySize[crossSize] ?? 0)) / 2;
294
+ } else if (crossPlacement !== crossAxis) {
295
+ // + (button size) - (overlay size)
296
+ // at this point the overlay bottom should match the button bottom
297
+ position[crossAxis]! += (childOffset[crossSize] ?? 0) - (overlaySize[crossSize] ?? 0);
298
+ } /* else {
299
+ the overlay top should match the button top
300
+ } */
301
+
302
+ position[crossAxis]! += crossOffset;
303
+
304
+ // overlay top or left overlapping arrow with button bottom or right
305
+ const minPosition =
306
+ childOffset[crossAxis] - overlaySize[crossSize] + arrowSize + arrowBoundaryOffset;
307
+ // overlay bottom or right overlapping arrow with button top or left
308
+ const maxPosition =
309
+ childOffset[crossAxis] + childOffset[crossSize] - arrowSize - arrowBoundaryOffset;
310
+ position[crossAxis] = clamp(position[crossAxis]!, minPosition, maxPosition);
311
+
312
+ // Floor these so the position isn't placed on a partial pixel, only whole pixels. Shouldn't matter if it was floored or ceiled, so chose one.
313
+ if (placement === axis) {
314
+ // If the container is positioned (non-static), then we use the container's actual
315
+ // height, as `bottom` will be relative to this height. But if the container is static,
316
+ // then it can only be the `document.body`, and `bottom` will be relative to _its_
317
+ // container.
318
+ let containerHeight = isContainerPositioned
319
+ ? containerDimensions[size]
320
+ : containerDimensions[TOTAL_SIZE[size]];
321
+ position[FLIPPED_DIRECTION[axis]] = Math.floor(containerHeight - childOffset[axis] + offset);
322
+ } else {
323
+ position[axis] = Math.floor(childOffset[axis] + childOffset[size] + offset);
324
+ }
325
+ return position;
326
+ }
327
+
328
+ function getMaxHeight(
329
+ position: Position,
330
+ boundaryDimensions: Dimensions,
331
+ containerOffsetWithBoundary: Offset,
332
+ isContainerPositioned: boolean,
333
+ margins: Position,
334
+ padding: number,
335
+ overlayHeight: number,
336
+ heightGrowthDirection: HeightGrowthDirection,
337
+ containerDimensions: Dimensions,
338
+ isContainerDescendentOfBoundary: boolean,
339
+ visualViewport: VisualViewport | null,
340
+ ) {
341
+ // For cases where position is set via "bottom" instead of "top", we need to calculate the true overlay top
342
+ // with respect to the container.
343
+ let overlayTop =
344
+ (position.top != null
345
+ ? position.top
346
+ : containerDimensions[TOTAL_SIZE.height] - (position.bottom ?? 0) - overlayHeight) -
347
+ (containerDimensions.scroll.top ?? 0);
348
+ // calculate the dimentions of the "boundingRect" which is most restrictive top/bottom of the boundaryRect and the visual view port
349
+ let boundaryToContainerTransformOffset = isContainerDescendentOfBoundary
350
+ ? containerOffsetWithBoundary.top
351
+ : 0;
352
+ let boundingRect = {
353
+ // This should be boundary top in container coord system vs viewport top in container coord system
354
+ // For the viewport top, there are several cases
355
+ // 1. pinchzoom case where we want the viewports offset top as top here
356
+ // 2. case where container is offset from the boundary and is contained by the boundary. In this case the top we want here is NOT 0, we want to take boundary's top even though is is a negative number OR the visual viewport, whichever is more restrictive
357
+ top: Math.max(
358
+ boundaryDimensions.top + boundaryToContainerTransformOffset,
359
+ (visualViewport?.offsetTop ?? boundaryDimensions.top) + boundaryToContainerTransformOffset,
360
+ ),
361
+ bottom: Math.min(
362
+ boundaryDimensions.top + boundaryDimensions.height + boundaryToContainerTransformOffset,
363
+ (visualViewport?.offsetTop ?? 0) + (visualViewport?.height ?? 0),
364
+ ),
365
+ };
366
+ let maxHeight =
367
+ heightGrowthDirection !== 'top'
368
+ ? // We want the distance between the top of the overlay to the bottom of the boundary
369
+ Math.max(
370
+ 0,
371
+ boundingRect.bottom - // this is the bottom of the boundary
372
+ overlayTop - // this is the top of the overlay
373
+ ((margins.top ?? 0) + (margins.bottom ?? 0) + padding), // save additional space for margin and padding
374
+ )
375
+ : // We want the distance between the bottom of the overlay to the top of the boundary
376
+ Math.max(
377
+ 0,
378
+ overlayTop +
379
+ overlayHeight - // this is the bottom of the overlay
380
+ boundingRect.top - // this is the top of the boundary
381
+ ((margins.top ?? 0) + (margins.bottom ?? 0) + padding), // save additional space for margin and padding
382
+ );
383
+ return maxHeight;
384
+ }
385
+
386
+ function getAvailableSpace(
387
+ boundaryDimensions: Dimensions, // boundary
388
+ containerOffsetWithBoundary: Offset,
389
+ childOffset: Offset, // trigger, position based of container's non-viewport 0,0
390
+ margins: Position, // overlay
391
+ padding: number, // overlay <-> boundary
392
+ placementInfo: ParsedPlacement,
393
+ containerDimensions: Dimensions,
394
+ isContainerDescendentOfBoundary: boolean,
395
+ ) {
396
+ let { placement, axis, size } = placementInfo;
397
+ if (placement === axis) {
398
+ return Math.max(
399
+ 0,
400
+ childOffset[axis] - // trigger start
401
+ (containerDimensions.scroll[axis] ?? 0) - // transform trigger position to be with respect to viewport 0,0
402
+ (boundaryDimensions[axis] +
403
+ (isContainerDescendentOfBoundary ? containerOffsetWithBoundary[axis] : 0)) - // boundary start
404
+ (margins[axis] ?? 0) - // margins usually for arrows or other decorations
405
+ margins[FLIPPED_DIRECTION[axis]] -
406
+ padding,
407
+ ); // padding between overlay and boundary
408
+ }
409
+
410
+ return Math.max(
411
+ 0,
412
+ boundaryDimensions[size] +
413
+ boundaryDimensions[axis] +
414
+ (isContainerDescendentOfBoundary ? containerOffsetWithBoundary[axis] : 0) -
415
+ childOffset[axis] -
416
+ childOffset[size] +
417
+ (containerDimensions.scroll[axis] ?? 0) -
418
+ (margins[axis] ?? 0) -
419
+ margins[FLIPPED_DIRECTION[axis]] -
420
+ padding,
421
+ );
422
+ }
423
+
424
+ export function calculatePositionInternal(
425
+ placementInput: Placement,
426
+ childOffset: Offset,
427
+ overlaySize: Offset,
428
+ scrollSize: Offset,
429
+ margins: Position,
430
+ padding: number,
431
+ flip: boolean,
432
+ boundaryDimensions: Dimensions,
433
+ containerDimensions: Dimensions,
434
+ containerOffsetWithBoundary: Offset,
435
+ offset: number,
436
+ crossOffset: number,
437
+ isContainerPositioned: boolean,
438
+ userSetMaxHeight: number | undefined,
439
+ arrowSize: number,
440
+ arrowBoundaryOffset: number,
441
+ isContainerDescendentOfBoundary: boolean,
442
+ visualViewport: VisualViewport | null,
443
+ ): PositionResult {
444
+ let placementInfo = parsePlacement(placementInput);
445
+ let { size, crossAxis, crossSize, placement, crossPlacement } = placementInfo;
446
+ let position = computePosition(
447
+ childOffset,
448
+ boundaryDimensions,
449
+ overlaySize,
450
+ placementInfo,
451
+ offset,
452
+ crossOffset,
453
+ containerOffsetWithBoundary,
454
+ isContainerPositioned,
455
+ arrowSize,
456
+ arrowBoundaryOffset,
457
+ containerDimensions,
458
+ );
459
+ let normalizedOffset = offset;
460
+ let space = getAvailableSpace(
461
+ boundaryDimensions,
462
+ containerOffsetWithBoundary,
463
+ childOffset,
464
+ margins,
465
+ padding + offset,
466
+ placementInfo,
467
+ containerDimensions,
468
+ isContainerDescendentOfBoundary,
469
+ );
470
+
471
+ // Check if the scroll size of the overlay is greater than the available space to determine if we need to flip
472
+ if (flip && overlaySize[size] > space) {
473
+ let flippedPlacementInfo = parsePlacement(
474
+ `${FLIPPED_DIRECTION[placement]} ${crossPlacement}` as Placement,
475
+ );
476
+ let flippedPosition = computePosition(
477
+ childOffset,
478
+ boundaryDimensions,
479
+ overlaySize,
480
+ flippedPlacementInfo,
481
+ offset,
482
+ crossOffset,
483
+ containerOffsetWithBoundary,
484
+ isContainerPositioned,
485
+ arrowSize,
486
+ arrowBoundaryOffset,
487
+ containerDimensions,
488
+ );
489
+
490
+ let flippedSpace = getAvailableSpace(
491
+ boundaryDimensions,
492
+ containerOffsetWithBoundary,
493
+ childOffset,
494
+ margins,
495
+ padding + offset,
496
+ flippedPlacementInfo,
497
+ containerDimensions,
498
+ isContainerDescendentOfBoundary,
499
+ );
500
+
501
+ // If the available space for the flipped position is greater than the original available space, flip.
502
+ if (flippedSpace > space) {
503
+ placementInfo = flippedPlacementInfo;
504
+ position = flippedPosition;
505
+ normalizedOffset = offset;
506
+ }
507
+ }
508
+
509
+ // Determine the direction the height of the overlay can grow so that we can choose how to calculate the max height
510
+ let heightGrowthDirection: HeightGrowthDirection = 'bottom';
511
+ if (placementInfo.axis === 'top') {
512
+ if (placementInfo.placement === 'top') {
513
+ heightGrowthDirection = 'top';
514
+ } else if (placementInfo.placement === 'bottom') {
515
+ heightGrowthDirection = 'bottom';
516
+ }
517
+ } else if (placementInfo.crossAxis === 'top') {
518
+ if (placementInfo.crossPlacement === 'top') {
519
+ heightGrowthDirection = 'bottom';
520
+ } else if (placementInfo.crossPlacement === 'bottom') {
521
+ heightGrowthDirection = 'top';
522
+ }
523
+ }
524
+
525
+ let delta = getDelta(
526
+ crossAxis,
527
+ position[crossAxis]!,
528
+ overlaySize[crossSize],
529
+ boundaryDimensions,
530
+ containerDimensions,
531
+ padding,
532
+ containerOffsetWithBoundary,
533
+ );
534
+ position[crossAxis]! += delta;
535
+
536
+ let maxHeight = getMaxHeight(
537
+ position,
538
+ boundaryDimensions,
539
+ containerOffsetWithBoundary,
540
+ isContainerPositioned,
541
+ margins,
542
+ padding,
543
+ overlaySize.height,
544
+ heightGrowthDirection,
545
+ containerDimensions,
546
+ isContainerDescendentOfBoundary,
547
+ visualViewport,
548
+ );
549
+
550
+ if (userSetMaxHeight && userSetMaxHeight < maxHeight) {
551
+ maxHeight = userSetMaxHeight;
552
+ }
553
+
554
+ overlaySize.height = Math.min(overlaySize.height, maxHeight);
555
+
556
+ position = computePosition(
557
+ childOffset,
558
+ boundaryDimensions,
559
+ overlaySize,
560
+ placementInfo,
561
+ normalizedOffset,
562
+ crossOffset,
563
+ containerOffsetWithBoundary,
564
+ isContainerPositioned,
565
+ arrowSize,
566
+ arrowBoundaryOffset,
567
+ containerDimensions,
568
+ );
569
+ delta = getDelta(
570
+ crossAxis,
571
+ position[crossAxis]!,
572
+ overlaySize[crossSize],
573
+ boundaryDimensions,
574
+ containerDimensions,
575
+ padding,
576
+ containerOffsetWithBoundary,
577
+ );
578
+ position[crossAxis]! += delta;
579
+
580
+ let arrowPosition: Position = {};
581
+
582
+ // All values are transformed so that 0 is at the top/left of the overlay depending on the orientation
583
+ // Prefer the arrow being in the center of the trigger/overlay anchor element
584
+ // childOffset[crossAxis] + .5 * childOffset[crossSize] = absolute position with respect to the trigger's coordinate system that would place the arrow in the center of the trigger
585
+ // position[crossAxis] - margins[AXIS[crossAxis]] = value use to transform the position to a value with respect to the overlay's coordinate system. A child element's (aka arrow) position absolute's "0"
586
+ // is positioned after the margin of its parent (aka overlay) so we need to subtract it to get the proper coordinate transform
587
+ let origin = childOffset[crossAxis] - position[crossAxis]! - margins[AXIS[crossAxis]];
588
+ let preferredArrowPosition = origin + 0.5 * childOffset[crossSize];
589
+
590
+ // Min/Max position limits for the arrow with respect to the overlay
591
+ const arrowMinPosition = arrowSize / 2 + arrowBoundaryOffset;
592
+ // overlaySize[crossSize] - margins = true size of the overlay
593
+ const overlayMargin =
594
+ AXIS[crossAxis] === 'left'
595
+ ? (margins.left ?? 0) + (margins.right ?? 0)
596
+ : (margins.top ?? 0) + (margins.bottom ?? 0);
597
+ const arrowMaxPosition =
598
+ overlaySize[crossSize] - overlayMargin - arrowSize / 2 - arrowBoundaryOffset;
599
+
600
+ // Min/Max position limits for the arrow with respect to the trigger/overlay anchor element
601
+ // Same margin accomodation done here as well as for the preferredArrowPosition
602
+ const arrowOverlappingChildMinEdge =
603
+ childOffset[crossAxis] + arrowSize / 2 - (position[crossAxis] + margins[AXIS[crossAxis]]);
604
+ const arrowOverlappingChildMaxEdge =
605
+ childOffset[crossAxis] +
606
+ childOffset[crossSize] -
607
+ arrowSize / 2 -
608
+ (position[crossAxis] + margins[AXIS[crossAxis]]);
609
+
610
+ // Clamp the arrow positioning so that it always is within the bounds of the anchor and the overlay
611
+ const arrowPositionOverlappingChild = clamp(
612
+ preferredArrowPosition,
613
+ arrowOverlappingChildMinEdge,
614
+ arrowOverlappingChildMaxEdge,
615
+ );
616
+ arrowPosition[crossAxis] = clamp(
617
+ arrowPositionOverlappingChild,
618
+ arrowMinPosition,
619
+ arrowMaxPosition,
620
+ );
621
+
622
+ // If there is an arrow, use that as the origin so that animations are smooth.
623
+ // Otherwise use the target edge.
624
+ ({ placement, crossPlacement } = placementInfo);
625
+ if (arrowSize) {
626
+ origin = arrowPosition[crossAxis];
627
+ } else if (crossPlacement === 'right') {
628
+ origin += childOffset[crossSize];
629
+ } else if (crossPlacement === 'center') {
630
+ origin += childOffset[crossSize] / 2;
631
+ }
632
+
633
+ let crossOrigin = placement === 'left' || placement === 'top' ? overlaySize[size] : 0;
634
+ let triggerAnchorPoint = {
635
+ x: placement === 'top' || placement === 'bottom' ? origin : crossOrigin,
636
+ y: placement === 'left' || placement === 'right' ? origin : crossOrigin,
637
+ };
638
+
639
+ return {
640
+ position,
641
+ maxHeight: maxHeight,
642
+ arrowOffsetLeft: arrowPosition.left,
643
+ arrowOffsetTop: arrowPosition.top,
644
+ placement,
645
+ triggerAnchorPoint,
646
+ };
647
+ }
648
+
649
+ /**
650
+ * Determines where to place the overlay with regards to the target and the position of an optional
651
+ * indicator.
652
+ */
653
+ export function calculatePosition(opts: PositionOpts): PositionResult {
654
+ let {
655
+ placement,
656
+ targetNode,
657
+ overlayNode,
658
+ scrollNode,
659
+ padding,
660
+ shouldFlip,
661
+ boundaryElement,
662
+ offset,
663
+ crossOffset,
664
+ maxHeight,
665
+ arrowSize = 0,
666
+ arrowBoundaryOffset = 0,
667
+ targetRect,
668
+ } = opts;
669
+
670
+ let visualViewport = getVisualViewport();
671
+ let container =
672
+ overlayNode instanceof HTMLElement ? getContainingBlock(overlayNode) : document.documentElement;
673
+ let isViewportContainer = container === document.documentElement;
674
+ const containerPositionStyle = window.getComputedStyle(container).position;
675
+ let isContainerPositioned = !!containerPositionStyle && containerPositionStyle !== 'static';
676
+ let childOffset: Offset = isViewportContainer
677
+ ? getOffset(targetNode, false, targetRect)
678
+ : getPosition(targetNode, container, false, targetRect);
679
+
680
+ if (!isViewportContainer) {
681
+ let { marginTop, marginLeft } = window.getComputedStyle(targetNode);
682
+ childOffset.top += parseInt(marginTop, 10) || 0;
683
+ childOffset.left += parseInt(marginLeft, 10) || 0;
684
+ }
685
+
686
+ let overlaySize: Offset = getOffset(overlayNode, true);
687
+ let margins = getMargins(overlayNode);
688
+ overlaySize.width += (margins.left ?? 0) + (margins.right ?? 0);
689
+ overlaySize.height += (margins.top ?? 0) + (margins.bottom ?? 0);
690
+
691
+ let scrollSize = getScroll(scrollNode);
692
+
693
+ // Note that due to logic inside getContainerDimensions, for cases where the boundary element is the body, we will return
694
+ // a height/width that matches the visual viewport size rather than the body's height/width (aka for zoom it will be zoom adjusted size)
695
+ // and a top/left that is adjusted as well (will return the top/left of the zoomed in viewport, or 0,0 for a non-zoomed body)
696
+ // Otherwise this returns the height/width of a arbitrary boundary element, and its top/left with respect to the viewport (NOTE THIS MEANS IT DOESNT INCLUDE SCROLL)
697
+ let boundaryDimensions = getContainerDimensions(boundaryElement, visualViewport);
698
+ let containerDimensions = getContainerDimensions(container, visualViewport);
699
+
700
+ // There are several difference cases of how to calculate the containerOffsetWithBoundary:
701
+ // - boundaryElement is body or HTML and the container is an arbitrary element in the boundary (aka submenu with parent menu as container in v3)
702
+ // - boundaryElement and container are both body or HTML element (aka standard popover case)
703
+ // - boundaryElement is customized by the user. Container can also be arbitrary (either body/HTML or some other element)
704
+ // containerOffsetWithBoundary should always return a value that is the boundary's coordinate offset with respect to the container coord system (container is 0, 0)
705
+ let containerOffsetWithBoundary: Offset;
706
+ if (
707
+ (boundaryElement.tagName === 'BODY' || boundaryElement.tagName === 'HTML') &&
708
+ !isViewportContainer
709
+ ) {
710
+ // Use getRect instead of getOffset because boundaryDimensions for BODY/HTML is in viewport coordinate space,
711
+ // not document coordinate space
712
+ let containerRect = getRect(container, false);
713
+ // the offset should be negative because if container is at viewport position x,y, then viewport top (aka 0)
714
+ // is at position -x,y in container-relative coordinates
715
+ containerOffsetWithBoundary = {
716
+ top: -(containerRect.top - boundaryDimensions.top),
717
+ left: -(containerRect.left - boundaryDimensions.left),
718
+ width: 0,
719
+ height: 0,
720
+ };
721
+ } else if (
722
+ (boundaryElement.tagName === 'BODY' || boundaryElement.tagName === 'HTML') &&
723
+ isViewportContainer
724
+ ) {
725
+ // both are the same viewport container, no offset needed
726
+ containerOffsetWithBoundary = { top: 0, left: 0, width: 0, height: 0 };
727
+ } else {
728
+ // This returns the boundary's coordinate with respect to the container. This case captures cases such as when you provide a custom boundary
729
+ // like in ScrollingBoundaryContainerExample in Popover.stories.
730
+ containerOffsetWithBoundary = getPosition(boundaryElement, container, false);
731
+ }
732
+
733
+ let isContainerDescendentOfBoundary = nodeContains(boundaryElement, container);
734
+ return calculatePositionInternal(
735
+ placement,
736
+ childOffset,
737
+ overlaySize,
738
+ scrollSize,
739
+ margins,
740
+ padding,
741
+ shouldFlip,
742
+ boundaryDimensions,
743
+ containerDimensions,
744
+ containerOffsetWithBoundary,
745
+ offset,
746
+ crossOffset,
747
+ isContainerPositioned,
748
+ maxHeight,
749
+ arrowSize,
750
+ arrowBoundaryOffset,
751
+ isContainerDescendentOfBoundary,
752
+ visualViewport,
753
+ );
754
+ }
755
+
756
+ export function getRect(node: Element, ignoreScale: boolean) {
757
+ let { top, left, width, height } = node.getBoundingClientRect();
758
+
759
+ // Use offsetWidth and offsetHeight if this is an HTML element, so that
760
+ // the size is not affected by scale transforms.
761
+ if (ignoreScale && node instanceof node.ownerDocument.defaultView!.HTMLElement) {
762
+ width = node.offsetWidth;
763
+ height = node.offsetHeight;
764
+ }
765
+
766
+ return { top, left, width, height };
767
+ }
768
+
769
+ function getOffset(
770
+ node: Element,
771
+ ignoreScale: boolean,
772
+ overrideRect?: Offset | null | undefined,
773
+ ): Offset {
774
+ let { top, left, width, height } = overrideRect || getRect(node, ignoreScale);
775
+ let { scrollTop, scrollLeft, clientTop, clientLeft } = document.documentElement;
776
+ return {
777
+ top: top + scrollTop - clientTop,
778
+ left: left + scrollLeft - clientLeft,
779
+ width,
780
+ height,
781
+ };
782
+ }
783
+
784
+ function getPosition(
785
+ node: Element,
786
+ parent: Element,
787
+ ignoreScale: boolean,
788
+ overrideRect?: Offset | null | undefined,
789
+ ): Offset {
790
+ let style = window.getComputedStyle(node);
791
+ let offset: Offset;
792
+ if (style.position === 'fixed') {
793
+ offset = overrideRect || getRect(node, ignoreScale);
794
+ } else {
795
+ offset = getOffset(node, ignoreScale, overrideRect);
796
+ let parentOffset = getOffset(parent, ignoreScale);
797
+ let parentStyle = window.getComputedStyle(parent);
798
+ parentOffset.top += (parseInt(parentStyle.borderTopWidth, 10) || 0) - parent.scrollTop;
799
+ parentOffset.left += (parseInt(parentStyle.borderLeftWidth, 10) || 0) - parent.scrollLeft;
800
+ offset.top -= parentOffset.top;
801
+ offset.left -= parentOffset.left;
802
+ }
803
+
804
+ offset.top -= parseInt(style.marginTop, 10) || 0;
805
+ offset.left -= parseInt(style.marginLeft, 10) || 0;
806
+ return offset;
807
+ }
808
+
809
+ // Returns the containing block of an element, which is the element that
810
+ // this element will be positioned relative to.
811
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block
812
+ function getContainingBlock(node: HTMLElement): Element {
813
+ // The offsetParent of an element in most cases equals the containing block.
814
+ // https://w3c.github.io/csswg-drafts/cssom-view/#dom-htmlelement-offsetparent
815
+ let offsetParent = node.offsetParent;
816
+
817
+ // The offsetParent algorithm terminates at the document body,
818
+ // even if the body is not a containing block. Double check that
819
+ // and use the documentElement if so.
820
+ if (
821
+ offsetParent &&
822
+ offsetParent === document.body &&
823
+ window.getComputedStyle(offsetParent).position === 'static' &&
824
+ !isContainingBlock(offsetParent)
825
+ ) {
826
+ offsetParent = document.documentElement;
827
+ }
828
+
829
+ // TODO(later): handle table elements?
830
+
831
+ // The offsetParent can be null if the element has position: fixed, or a few other cases.
832
+ // We have to walk up the tree manually in this case because fixed positioned elements
833
+ // are still positioned relative to their containing block, which is not always the viewport.
834
+ if (offsetParent == null) {
835
+ offsetParent = node.parentElement;
836
+ while (offsetParent && !isContainingBlock(offsetParent)) {
837
+ offsetParent = offsetParent.parentElement;
838
+ }
839
+ }
840
+
841
+ // Fall back to the viewport.
842
+ return offsetParent || document.documentElement;
843
+ }
844
+
845
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block
846
+ function isContainingBlock(node: Element): boolean {
847
+ let style = window.getComputedStyle(node);
848
+ return (
849
+ style.transform !== 'none' ||
850
+ /transform|perspective/.test(style.willChange) ||
851
+ style.filter !== 'none' ||
852
+ style.contain === 'paint' ||
853
+ ('backdropFilter' in style && style.backdropFilter !== 'none') ||
854
+ ('WebkitBackdropFilter' in style && (style as any).WebkitBackdropFilter !== 'none')
855
+ );
856
+ }