@basic-ui/core 0.0.54 → 0.0.57

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 (410) hide show
  1. package/README.md +3 -3
  2. package/build/cjs/index.js +364 -591
  3. package/build/cjs/index.js.map +1 -1
  4. package/build/esm/Accordion/Accordion.d.ts +9 -9
  5. package/build/esm/Accordion/Accordion.js +20 -29
  6. package/build/esm/Accordion/Accordion.js.map +1 -1
  7. package/build/esm/Accordion/AccordionBody.d.ts +6 -6
  8. package/build/esm/Accordion/AccordionBody.js +18 -32
  9. package/build/esm/Accordion/AccordionBody.js.map +1 -1
  10. package/build/esm/Accordion/AccordionHeader.d.ts +7 -7
  11. package/build/esm/Accordion/AccordionHeader.js +37 -74
  12. package/build/esm/Accordion/AccordionHeader.js.map +1 -1
  13. package/build/esm/Accordion/AccordionItem.d.ts +9 -9
  14. package/build/esm/Accordion/AccordionItem.js +19 -22
  15. package/build/esm/Accordion/AccordionItem.js.map +1 -1
  16. package/build/esm/Accordion/context.d.ts +19 -19
  17. package/build/esm/Accordion/context.js +16 -13
  18. package/build/esm/Accordion/context.js.map +1 -1
  19. package/build/esm/Accordion/index.d.ts +4 -4
  20. package/build/esm/Accordion/index.js.map +1 -1
  21. package/build/esm/Accordion/scopeQuery.d.ts +2 -2
  22. package/build/esm/Accordion/scopeQuery.js.map +1 -1
  23. package/build/esm/CheckBox/CheckBox.d.ts +7 -7
  24. package/build/esm/CheckBox/CheckBox.js +15 -25
  25. package/build/esm/CheckBox/CheckBox.js.map +1 -1
  26. package/build/esm/CheckBox/index.d.ts +1 -1
  27. package/build/esm/CheckBox/index.js.map +1 -1
  28. package/build/esm/ComboBox/Combobox.d.ts +18 -18
  29. package/build/esm/ComboBox/Combobox.js +52 -59
  30. package/build/esm/ComboBox/Combobox.js.map +1 -1
  31. package/build/esm/ComboBox/ComboboxButton.d.ts +9 -9
  32. package/build/esm/ComboBox/ComboboxButton.js +23 -28
  33. package/build/esm/ComboBox/ComboboxButton.js.map +1 -1
  34. package/build/esm/ComboBox/ComboboxInput.d.ts +17 -17
  35. package/build/esm/ComboBox/ComboboxInput.js +67 -70
  36. package/build/esm/ComboBox/ComboboxInput.js.map +1 -1
  37. package/build/esm/ComboBox/ComboboxLabel.d.ts +7 -7
  38. package/build/esm/ComboBox/ComboboxLabel.js +15 -17
  39. package/build/esm/ComboBox/ComboboxLabel.js.map +1 -1
  40. package/build/esm/ComboBox/ComboboxList.d.ts +8 -8
  41. package/build/esm/ComboBox/ComboboxList.js +19 -20
  42. package/build/esm/ComboBox/ComboboxList.js.map +1 -1
  43. package/build/esm/ComboBox/ComboboxOption.d.ts +11 -11
  44. package/build/esm/ComboBox/ComboboxOption.js +41 -45
  45. package/build/esm/ComboBox/ComboboxOption.js.map +1 -1
  46. package/build/esm/ComboBox/ComboboxPopover.d.ts +9 -9
  47. package/build/esm/ComboBox/ComboboxPopover.js +22 -21
  48. package/build/esm/ComboBox/ComboboxPopover.js.map +1 -1
  49. package/build/esm/ComboBox/cities.d.ts +5 -5
  50. package/build/esm/ComboBox/cities.js.map +1 -1
  51. package/build/esm/ComboBox/context.d.ts +30 -30
  52. package/build/esm/ComboBox/context.js +5 -6
  53. package/build/esm/ComboBox/context.js.map +1 -1
  54. package/build/esm/ComboBox/hooks.d.ts +37 -37
  55. package/build/esm/ComboBox/hooks.js +175 -148
  56. package/build/esm/ComboBox/hooks.js.map +1 -1
  57. package/build/esm/ComboBox/index.d.ts +8 -8
  58. package/build/esm/ComboBox/index.js.map +1 -1
  59. package/build/esm/ComboBox/makeHash.d.ts +1 -1
  60. package/build/esm/ComboBox/makeHash.js +3 -6
  61. package/build/esm/ComboBox/makeHash.js.map +1 -1
  62. package/build/esm/ComboBox/scopeQuery.d.ts +1 -1
  63. package/build/esm/ComboBox/scopeQuery.js.map +1 -1
  64. package/build/esm/FocusLock/FocusLock.d.ts +9 -9
  65. package/build/esm/FocusLock/FocusLock.js +26 -32
  66. package/build/esm/FocusLock/FocusLock.js.map +1 -1
  67. package/build/esm/FocusLock/index.d.ts +1 -1
  68. package/build/esm/FocusLock/index.js.map +1 -1
  69. package/build/esm/FocusLock/tabUtils.d.ts +3 -3
  70. package/build/esm/FocusLock/tabUtils.js +5 -7
  71. package/build/esm/FocusLock/tabUtils.js.map +1 -1
  72. package/build/esm/FocusLock/useFocusLock.d.ts +7 -7
  73. package/build/esm/FocusLock/useFocusLock.js +14 -19
  74. package/build/esm/FocusLock/useFocusLock.js.map +1 -1
  75. package/build/esm/List/List.d.ts +7 -7
  76. package/build/esm/List/List.js +9 -11
  77. package/build/esm/List/List.js.map +1 -1
  78. package/build/esm/List/ListItem.d.ts +7 -7
  79. package/build/esm/List/ListItem.js +9 -11
  80. package/build/esm/List/ListItem.js.map +1 -1
  81. package/build/esm/List/context.d.ts +4 -4
  82. package/build/esm/List/context.js +6 -6
  83. package/build/esm/List/context.js.map +1 -1
  84. package/build/esm/List/index.d.ts +2 -2
  85. package/build/esm/List/index.js.map +1 -1
  86. package/build/esm/Menu/ContextMenuTrigger.d.ts +11 -11
  87. package/build/esm/Menu/ContextMenuTrigger.js +32 -37
  88. package/build/esm/Menu/ContextMenuTrigger.js.map +1 -1
  89. package/build/esm/Menu/Menu.d.ts +10 -10
  90. package/build/esm/Menu/Menu.js +33 -49
  91. package/build/esm/Menu/Menu.js.map +1 -1
  92. package/build/esm/Menu/MenuButton.d.ts +11 -11
  93. package/build/esm/Menu/MenuButton.js +28 -44
  94. package/build/esm/Menu/MenuButton.js.map +1 -1
  95. package/build/esm/Menu/MenuItem.d.ts +8 -8
  96. package/build/esm/Menu/MenuItem.js +29 -38
  97. package/build/esm/Menu/MenuItem.js.map +1 -1
  98. package/build/esm/Menu/MenuList.d.ts +7 -7
  99. package/build/esm/Menu/MenuList.js +61 -116
  100. package/build/esm/Menu/MenuList.js.map +1 -1
  101. package/build/esm/Menu/MenuPopover.d.ts +8 -8
  102. package/build/esm/Menu/MenuPopover.js +16 -19
  103. package/build/esm/Menu/MenuPopover.js.map +1 -1
  104. package/build/esm/Menu/context.d.ts +25 -25
  105. package/build/esm/Menu/context.js +14 -12
  106. package/build/esm/Menu/context.js.map +1 -1
  107. package/build/esm/Menu/fixtures/countryList.d.ts +1 -1
  108. package/build/esm/Menu/fixtures/countryList.js +1 -1
  109. package/build/esm/Menu/fixtures/countryList.js.map +1 -1
  110. package/build/esm/Menu/index.d.ts +6 -6
  111. package/build/esm/Menu/index.js.map +1 -1
  112. package/build/esm/Menu/scope.d.ts +1 -1
  113. package/build/esm/Menu/scope.js.map +1 -1
  114. package/build/esm/Modal/Modal.d.ts +9 -9
  115. package/build/esm/Modal/Modal.js +13 -18
  116. package/build/esm/Modal/Modal.js.map +1 -1
  117. package/build/esm/Modal/ModalBackdrop.d.ts +10 -10
  118. package/build/esm/Modal/ModalBackdrop.js +24 -33
  119. package/build/esm/Modal/ModalBackdrop.js.map +1 -1
  120. package/build/esm/Modal/index.d.ts +2 -2
  121. package/build/esm/Modal/index.js.map +1 -1
  122. package/build/esm/Popper/Popper.d.ts +35 -35
  123. package/build/esm/Popper/Popper.js +44 -60
  124. package/build/esm/Popper/Popper.js.map +1 -1
  125. package/build/esm/Popper/PopperArrow.d.ts +6 -6
  126. package/build/esm/Popper/PopperArrow.js +11 -16
  127. package/build/esm/Popper/PopperArrow.js.map +1 -1
  128. package/build/esm/Popper/context.d.ts +6 -6
  129. package/build/esm/Popper/context.js +3 -5
  130. package/build/esm/Popper/context.js.map +1 -1
  131. package/build/esm/Popper/index.d.ts +3 -3
  132. package/build/esm/Popper/index.js.map +1 -1
  133. package/build/esm/Portal/Portal.d.ts +7 -7
  134. package/build/esm/Portal/Portal.js +9 -11
  135. package/build/esm/Portal/Portal.js.map +1 -1
  136. package/build/esm/Portal/PortalSelectorProvider.d.ts +8 -8
  137. package/build/esm/Portal/PortalSelectorProvider.js +6 -4
  138. package/build/esm/Portal/PortalSelectorProvider.js.map +1 -1
  139. package/build/esm/Portal/index.d.ts +2 -2
  140. package/build/esm/Portal/index.js.map +1 -1
  141. package/build/esm/RadioButton/RadioButton.d.ts +10 -10
  142. package/build/esm/RadioButton/RadioButton.js +17 -23
  143. package/build/esm/RadioButton/RadioButton.js.map +1 -1
  144. package/build/esm/RadioButton/RadioGroup.d.ts +12 -12
  145. package/build/esm/RadioButton/RadioGroup.js +19 -28
  146. package/build/esm/RadioButton/RadioGroup.js.map +1 -1
  147. package/build/esm/RadioButton/context.d.ts +9 -9
  148. package/build/esm/RadioButton/context.js +8 -6
  149. package/build/esm/RadioButton/context.js.map +1 -1
  150. package/build/esm/RadioButton/index.d.ts +2 -2
  151. package/build/esm/RadioButton/index.js.map +1 -1
  152. package/build/esm/SkipNav/SkipNav.d.ts +7 -7
  153. package/build/esm/SkipNav/SkipNav.js +9 -11
  154. package/build/esm/SkipNav/SkipNav.js.map +1 -1
  155. package/build/esm/SkipNav/index.d.ts +1 -1
  156. package/build/esm/SkipNav/index.js.map +1 -1
  157. package/build/esm/Slider/Slider.d.ts +197 -197
  158. package/build/esm/Slider/Slider.js +422 -489
  159. package/build/esm/Slider/Slider.js.map +1 -1
  160. package/build/esm/Slider/index.d.ts +1 -1
  161. package/build/esm/Slider/index.js.map +1 -1
  162. package/build/esm/Spinner/Spinner.d.ts +12 -12
  163. package/build/esm/Spinner/Spinner.js +31 -59
  164. package/build/esm/Spinner/Spinner.js.map +1 -1
  165. package/build/esm/Spinner/SpinnerButton.d.ts +8 -8
  166. package/build/esm/Spinner/SpinnerButton.js +14 -19
  167. package/build/esm/Spinner/SpinnerButton.js.map +1 -1
  168. package/build/esm/Spinner/context.d.ts +12 -12
  169. package/build/esm/Spinner/context.js +8 -7
  170. package/build/esm/Spinner/context.js.map +1 -1
  171. package/build/esm/Spinner/index.d.ts +2 -2
  172. package/build/esm/Spinner/index.js.map +1 -1
  173. package/build/esm/Tabs/Tab.d.ts +7 -7
  174. package/build/esm/Tabs/Tab.js +32 -50
  175. package/build/esm/Tabs/Tab.js.map +1 -1
  176. package/build/esm/Tabs/TabList.d.ts +9 -9
  177. package/build/esm/Tabs/TabList.js +24 -34
  178. package/build/esm/Tabs/TabList.js.map +1 -1
  179. package/build/esm/Tabs/TabPanel.d.ts +8 -8
  180. package/build/esm/Tabs/TabPanel.js +16 -23
  181. package/build/esm/Tabs/TabPanel.js.map +1 -1
  182. package/build/esm/Tabs/TabPanels.d.ts +8 -8
  183. package/build/esm/Tabs/TabPanels.js +15 -20
  184. package/build/esm/Tabs/TabPanels.js.map +1 -1
  185. package/build/esm/Tabs/Tabs.d.ts +10 -10
  186. package/build/esm/Tabs/Tabs.js +17 -33
  187. package/build/esm/Tabs/Tabs.js.map +1 -1
  188. package/build/esm/Tabs/context.d.ts +17 -17
  189. package/build/esm/Tabs/context.js +16 -13
  190. package/build/esm/Tabs/context.js.map +1 -1
  191. package/build/esm/Tabs/index.d.ts +5 -5
  192. package/build/esm/Tabs/index.js.map +1 -1
  193. package/build/esm/Tabs/scopeQuery.d.ts +1 -1
  194. package/build/esm/Tabs/scopeQuery.js.map +1 -1
  195. package/build/esm/Tooltip/Tooltip.d.ts +10 -10
  196. package/build/esm/Tooltip/Tooltip.js +20 -30
  197. package/build/esm/Tooltip/Tooltip.js.map +1 -1
  198. package/build/esm/Tooltip/index.d.ts +1 -1
  199. package/build/esm/Tooltip/index.js.map +1 -1
  200. package/build/esm/Tooltip/stateMachine.d.ts +28 -28
  201. package/build/esm/Tooltip/stateMachine.js +95 -81
  202. package/build/esm/Tooltip/stateMachine.js.map +1 -1
  203. package/build/esm/Tooltip/useTooltip.d.ts +10 -10
  204. package/build/esm/Tooltip/useTooltip.js +38 -50
  205. package/build/esm/Tooltip/useTooltip.js.map +1 -1
  206. package/build/esm/hooks/index.d.ts +13 -13
  207. package/build/esm/hooks/index.js.map +1 -1
  208. package/build/esm/hooks/useAutoFocus.d.ts +2 -2
  209. package/build/esm/hooks/useAutoFocus.js +3 -3
  210. package/build/esm/hooks/useAutoFocus.js.map +1 -1
  211. package/build/esm/hooks/useChildrenCounter.d.ts +7 -7
  212. package/build/esm/hooks/useChildrenCounter.js +6 -8
  213. package/build/esm/hooks/useChildrenCounter.js.map +1 -1
  214. package/build/esm/hooks/useControlledState.d.ts +3 -3
  215. package/build/esm/hooks/useControlledState.js +6 -16
  216. package/build/esm/hooks/useControlledState.js.map +1 -1
  217. package/build/esm/hooks/useFocusReturn.d.ts +2 -2
  218. package/build/esm/hooks/useFocusReturn.js +8 -12
  219. package/build/esm/hooks/useFocusReturn.js.map +1 -1
  220. package/build/esm/hooks/useFocusState.d.ts +11 -11
  221. package/build/esm/hooks/useFocusState.js +9 -15
  222. package/build/esm/hooks/useFocusState.js.map +1 -1
  223. package/build/esm/hooks/useGestureHandlers.d.ts +52 -52
  224. package/build/esm/hooks/useGestureHandlers.js +80 -100
  225. package/build/esm/hooks/useGestureHandlers.js.map +1 -1
  226. package/build/esm/hooks/useMeasure.d.ts +7 -7
  227. package/build/esm/hooks/useMeasure.js +7 -15
  228. package/build/esm/hooks/useMeasure.js.map +1 -1
  229. package/build/esm/hooks/useOnClickOutside.d.ts +2 -2
  230. package/build/esm/hooks/useOnClickOutside.js +4 -6
  231. package/build/esm/hooks/useOnClickOutside.js.map +1 -1
  232. package/build/esm/hooks/useOnKeyDown.d.ts +1 -1
  233. package/build/esm/hooks/useOnKeyDown.js +3 -4
  234. package/build/esm/hooks/useOnKeyDown.js.map +1 -1
  235. package/build/esm/hooks/useReducerMachine.d.ts +24 -24
  236. package/build/esm/hooks/useReducerMachine.js +15 -26
  237. package/build/esm/hooks/useReducerMachine.js.map +1 -1
  238. package/build/esm/hooks/useRemoveBodyScroll.d.ts +2 -2
  239. package/build/esm/hooks/useRemoveBodyScroll.js +9 -14
  240. package/build/esm/hooks/useRemoveBodyScroll.js.map +1 -1
  241. package/build/esm/hooks/useScope.d.ts +11 -11
  242. package/build/esm/hooks/useScope.js +12 -14
  243. package/build/esm/hooks/useScope.js.map +1 -1
  244. package/build/esm/hooks/useThrottle.d.ts +1 -1
  245. package/build/esm/hooks/useThrottle.js +5 -10
  246. package/build/esm/hooks/useThrottle.js.map +1 -1
  247. package/build/esm/index.d.ts +15 -15
  248. package/build/esm/index.js +4 -2
  249. package/build/esm/index.js.map +1 -1
  250. package/build/esm/styles.d.js +2 -0
  251. package/build/esm/styles.d.js.map +1 -0
  252. package/build/esm/utils/assign-ref.d.ts +3 -3
  253. package/build/esm/utils/assign-ref.js +3 -5
  254. package/build/esm/utils/assign-ref.js.map +1 -1
  255. package/build/esm/utils/can-use-dom.d.ts +1 -1
  256. package/build/esm/utils/can-use-dom.js.map +1 -1
  257. package/build/esm/utils/clamp.d.ts +1 -1
  258. package/build/esm/utils/clamp.js.map +1 -1
  259. package/build/esm/utils/context.d.ts +7 -7
  260. package/build/esm/utils/context.js +13 -20
  261. package/build/esm/utils/context.js.map +1 -1
  262. package/build/esm/utils/create-subscription.d.ts +4 -4
  263. package/build/esm/utils/create-subscription.js +5 -10
  264. package/build/esm/utils/create-subscription.js.map +1 -1
  265. package/build/esm/utils/get-circular-index.d.ts +1 -1
  266. package/build/esm/utils/get-circular-index.js +0 -1
  267. package/build/esm/utils/get-circular-index.js.map +1 -1
  268. package/build/esm/utils/index.d.ts +10 -10
  269. package/build/esm/utils/index.js.map +1 -1
  270. package/build/esm/utils/is-right-click.d.ts +6 -6
  271. package/build/esm/utils/is-right-click.js +4 -4
  272. package/build/esm/utils/is-right-click.js.map +1 -1
  273. package/build/esm/utils/owner-document.d.ts +7 -7
  274. package/build/esm/utils/owner-document.js +6 -6
  275. package/build/esm/utils/owner-document.js.map +1 -1
  276. package/build/esm/utils/polymorphic.d.ts +39 -39
  277. package/build/esm/utils/polymorphic.js.map +1 -1
  278. package/build/esm/utils/rubber-band-clamp.d.ts +2 -2
  279. package/build/esm/utils/rubber-band-clamp.js +2 -5
  280. package/build/esm/utils/rubber-band-clamp.js.map +1 -1
  281. package/build/esm/utils/use-stable-callback.d.ts +16 -16
  282. package/build/esm/utils/use-stable-callback.js +24 -26
  283. package/build/esm/utils/use-stable-callback.js.map +1 -1
  284. package/build/esm/utils/wrap-event.d.ts +3 -3
  285. package/build/esm/utils/wrap-event.js +2 -5
  286. package/build/esm/utils/wrap-event.js.map +1 -1
  287. package/build/tsconfig-build.tsbuildinfo +1 -1
  288. package/package.json +7 -11
  289. package/src/Accordion/Accordion.story.tsx +74 -74
  290. package/src/Accordion/Accordion.tsx +59 -59
  291. package/src/Accordion/AccordionBody.tsx +52 -52
  292. package/src/Accordion/AccordionHeader.tsx +166 -167
  293. package/src/Accordion/AccordionItem.tsx +50 -50
  294. package/src/Accordion/context.ts +37 -37
  295. package/src/Accordion/index.ts +4 -4
  296. package/src/Accordion/scopeQuery.ts +7 -7
  297. package/src/Accordion/styles.css +21 -21
  298. package/src/CheckBox/CheckBox.tsx +41 -41
  299. package/src/CheckBox/index.ts +1 -1
  300. package/src/ComboBox/ComboBox.story.tsx +120 -120
  301. package/src/ComboBox/Combobox.tsx +148 -148
  302. package/src/ComboBox/ComboboxButton.tsx +61 -61
  303. package/src/ComboBox/ComboboxInput.tsx +187 -187
  304. package/src/ComboBox/ComboboxLabel.tsx +33 -33
  305. package/src/ComboBox/ComboboxList.tsx +47 -47
  306. package/src/ComboBox/ComboboxOption.tsx +110 -111
  307. package/src/ComboBox/ComboboxPopover.tsx +64 -64
  308. package/src/ComboBox/cities.ts +23194 -23194
  309. package/src/ComboBox/context.ts +35 -35
  310. package/src/ComboBox/hooks.tsx +448 -451
  311. package/src/ComboBox/index.ts +8 -8
  312. package/src/ComboBox/makeHash.ts +19 -19
  313. package/src/ComboBox/scopeQuery.ts +6 -6
  314. package/src/ComboBox/styles.css +32 -32
  315. package/src/FocusLock/FocusLock.tsx +66 -66
  316. package/src/FocusLock/index.ts +1 -1
  317. package/src/FocusLock/tabUtils.ts +40 -40
  318. package/src/FocusLock/useFocusLock.ts +55 -56
  319. package/src/List/List.story.tsx +18 -18
  320. package/src/List/List.tsx +17 -17
  321. package/src/List/ListItem.tsx +23 -23
  322. package/src/List/context.ts +19 -19
  323. package/src/List/index.ts +2 -2
  324. package/src/Menu/ContextMenu.story.tsx +73 -73
  325. package/src/Menu/ContextMenuTrigger.tsx +76 -76
  326. package/src/Menu/Menu.story.tsx +160 -160
  327. package/src/Menu/Menu.tsx +82 -83
  328. package/src/Menu/MenuButton.tsx +83 -83
  329. package/src/Menu/MenuComplex.story.tsx +58 -58
  330. package/src/Menu/MenuItem.tsx +87 -88
  331. package/src/Menu/MenuList.tsx +254 -254
  332. package/src/Menu/MenuPopover.tsx +35 -35
  333. package/src/Menu/context.ts +44 -44
  334. package/src/Menu/fixtures/countryList.ts +198 -198
  335. package/src/Menu/index.ts +6 -6
  336. package/src/Menu/scope.ts +7 -7
  337. package/src/Menu/styles.css +42 -42
  338. package/src/Modal/Modal.story.tsx +262 -258
  339. package/src/Modal/Modal.tsx +48 -48
  340. package/src/Modal/ModalBackdrop.tsx +78 -78
  341. package/src/Modal/NavDrawer.story.tsx +161 -158
  342. package/src/Modal/index.ts +2 -2
  343. package/src/Modal/styles.css +46 -46
  344. package/src/Popper/Popper.story.tsx +279 -263
  345. package/src/Popper/Popper.tsx +1 -1
  346. package/src/Popper/PopperArrow.tsx +35 -35
  347. package/src/Popper/context.ts +10 -10
  348. package/src/Popper/index.ts +3 -3
  349. package/src/Popper/styles.css +60 -60
  350. package/src/RadioButton/RadioButton.story.tsx +78 -77
  351. package/src/RadioButton/RadioButton.tsx +55 -55
  352. package/src/RadioButton/RadioGroup.tsx +60 -60
  353. package/src/RadioButton/context.ts +17 -17
  354. package/src/RadioButton/index.ts +2 -2
  355. package/src/SkipNav/SkipNav.tsx +16 -16
  356. package/src/SkipNav/index.tsx +1 -1
  357. package/src/Slider/Slider.story.tsx +45 -45
  358. package/src/Slider/Slider.tsx +1115 -1120
  359. package/src/Slider/index.ts +1 -1
  360. package/src/Slider/styles.css +131 -131
  361. package/src/Spinner/Spinner.story.tsx +31 -31
  362. package/src/Spinner/Spinner.tsx +117 -117
  363. package/src/Spinner/SpinnerButton.tsx +54 -54
  364. package/src/Spinner/context.ts +20 -20
  365. package/src/Spinner/index.ts +2 -2
  366. package/src/Spinner/styles.css +23 -23
  367. package/src/Tabs/Tab.story.tsx +80 -80
  368. package/src/Tabs/Tab.tsx +136 -136
  369. package/src/Tabs/TabList.tsx +71 -71
  370. package/src/Tabs/TabPanel.tsx +53 -53
  371. package/src/Tabs/TabPanels.tsx +30 -30
  372. package/src/Tabs/Tabs.tsx +46 -46
  373. package/src/Tabs/context.ts +30 -30
  374. package/src/Tabs/index.tsx +5 -5
  375. package/src/Tabs/scopeQuery.ts +6 -6
  376. package/src/Tooltip/Tooltip.story.tsx +61 -61
  377. package/src/Tooltip/Tooltip.tsx +53 -50
  378. package/src/Tooltip/index.ts +1 -1
  379. package/src/Tooltip/stateMachine.ts +192 -192
  380. package/src/Tooltip/styles.css +17 -17
  381. package/src/Tooltip/useTooltip.ts +136 -136
  382. package/src/hooks/index.ts +13 -13
  383. package/src/hooks/useAutoFocus.ts +22 -22
  384. package/src/hooks/useChildrenCounter.ts +51 -51
  385. package/src/hooks/useControlledState.ts +1 -6
  386. package/src/hooks/useFocusReturn.ts +43 -43
  387. package/src/hooks/useFocusState.ts +30 -30
  388. package/src/hooks/useGestureHandlers.ts +282 -286
  389. package/src/hooks/useMeasure.ts +33 -33
  390. package/src/hooks/useOnClickOutside.ts +32 -32
  391. package/src/hooks/useOnKeyDown.ts +19 -19
  392. package/src/hooks/useReducerMachine.ts +60 -60
  393. package/src/hooks/useRemoveBodyScroll.ts +38 -39
  394. package/src/hooks/useScope.ts +52 -52
  395. package/src/hooks/useThrottle.ts +19 -19
  396. package/src/index.ts +20 -20
  397. package/src/styles.d.ts +1 -0
  398. package/src/utils/assign-ref.ts +27 -27
  399. package/src/utils/can-use-dom.ts +7 -7
  400. package/src/utils/clamp.ts +3 -3
  401. package/src/utils/context.tsx +48 -48
  402. package/src/utils/create-subscription.ts +16 -16
  403. package/src/utils/get-circular-index.ts +7 -7
  404. package/src/utils/index.ts +10 -10
  405. package/src/utils/is-right-click.ts +14 -14
  406. package/src/utils/owner-document.ts +13 -13
  407. package/src/utils/polymorphic.ts +82 -78
  408. package/src/utils/rubber-band-clamp.ts +25 -25
  409. package/src/utils/use-stable-callback.ts +57 -58
  410. package/src/utils/wrap-event.ts +22 -22
@@ -1,451 +1,448 @@
1
- /* eslint-disable @typescript-eslint/no-use-before-define */
2
- /* eslint-disable default-case */
3
- import type { KeyboardEvent, MutableRefObject } from 'react';
4
- import { useEffect } from 'react';
5
-
6
- import type {
7
- StateChart as GenericStateChart,
8
- StateMachineState,
9
- } from '../hooks/useReducerMachine';
10
- import { getCircularIndex } from '../utils/get-circular-index';
11
- import { useComboBoxContext } from './context';
12
- import { scopeQuery } from './scopeQuery';
13
-
14
- ////////////////////////////////////////////////////////////////////////////////
15
- // States
16
-
17
- // Nothing going on, waiting for the user to type or use the arrow keys
18
- export const IDLE = 'IDLE';
19
-
20
- // The component is suggesting options as the user types
21
- const SUGGESTING = 'SUGGESTING';
22
-
23
- // The user is using the keyboard to navigate the list, not typing
24
- export const NAVIGATING = 'NAVIGATING';
25
-
26
- export type StateTypes = typeof IDLE | typeof SUGGESTING | typeof NAVIGATING;
27
-
28
- ////////////////////////////////////////////////////////////////////////////////
29
- // Actions:
30
-
31
- // Used to sync the state with controlled state, right after mounting
32
- export const INIT = 'INIT';
33
-
34
- // User cleared the value w/ backspace, but input still has focus
35
- export const CLEAR = 'CLEAR';
36
-
37
- // User cleared the value w/ backspace, but input still has focus
38
- export const CLEAR_SELECTION = 'CLEAR_SELECTION';
39
-
40
- // User is typing
41
- export const CHANGE = 'CHANGE';
42
-
43
- // User is navigating w/ the keyboard
44
- export const NAVIGATE = 'NAVIGATE';
45
-
46
- // User can be navigating with keyboard and then click instead, we want the
47
- // value from the click, not the current nav item
48
- const SELECT_WITH_KEYBOARD = 'SELECT_WITH_KEYBOARD';
49
- export const SELECT_WITH_CLICK = 'SELECT_WITH_CLICK';
50
-
51
- // Pretty self-explanatory, user can hit escape or blur to close the popover
52
- const ESCAPE = 'ESCAPE';
53
- const BLUR = 'BLUR';
54
-
55
- export const FOCUS = 'FOCUS';
56
-
57
- export const OPEN_WITH_BUTTON = 'OPEN_WITH_BUTTON';
58
-
59
- export const CLOSE_WITH_BUTTON = 'CLOSE_WITH_BUTTON';
60
-
61
- export type ActionTypes =
62
- | typeof CLEAR
63
- | typeof CLEAR_SELECTION
64
- | typeof CHANGE
65
- | typeof INIT
66
- | typeof NAVIGATE
67
- | typeof SELECT_WITH_KEYBOARD
68
- | typeof SELECT_WITH_CLICK
69
- | typeof ESCAPE
70
- | typeof BLUR
71
- | typeof FOCUS
72
- | typeof OPEN_WITH_BUTTON
73
- | typeof CLOSE_WITH_BUTTON;
74
-
75
- ////////////////////////////////////////////////////////////////////////////////
76
- export const stateChart: GenericStateChart<StateTypes, ActionTypes> = {
77
- initial: IDLE,
78
- states: {
79
- [IDLE]: {
80
- on: {
81
- [BLUR]: IDLE,
82
- [CLEAR]: IDLE,
83
- [INIT]: IDLE,
84
- [CLEAR_SELECTION]: IDLE,
85
- [CHANGE]: SUGGESTING,
86
- [FOCUS]: SUGGESTING,
87
- [NAVIGATE]: NAVIGATING,
88
- [OPEN_WITH_BUTTON]: SUGGESTING,
89
- },
90
- },
91
- [SUGGESTING]: {
92
- on: {
93
- [CHANGE]: SUGGESTING,
94
- [FOCUS]: SUGGESTING,
95
- [INIT]: SUGGESTING,
96
- [NAVIGATE]: NAVIGATING,
97
- [CLEAR]: IDLE,
98
- [CLEAR_SELECTION]: SUGGESTING,
99
- [ESCAPE]: IDLE,
100
- [BLUR]: IDLE,
101
- [SELECT_WITH_CLICK]: IDLE,
102
- [CLOSE_WITH_BUTTON]: IDLE,
103
- },
104
- },
105
- [NAVIGATING]: {
106
- on: {
107
- [CHANGE]: SUGGESTING,
108
- [FOCUS]: SUGGESTING,
109
- [INIT]: NAVIGATING,
110
- [CLEAR]: IDLE,
111
- [CLEAR_SELECTION]: NAVIGATING,
112
- [BLUR]: IDLE,
113
- [ESCAPE]: IDLE,
114
- [NAVIGATE]: NAVIGATING,
115
- [SELECT_WITH_KEYBOARD]: IDLE,
116
- [SELECT_WITH_CLICK]: IDLE,
117
- [CLOSE_WITH_BUTTON]: IDLE,
118
- },
119
- },
120
- },
121
- };
122
-
123
- export interface ReducerState
124
- extends StateMachineState<StateTypes, ActionTypes> {
125
- item: string;
126
- navigationItem: string;
127
- text: string;
128
- }
129
-
130
- interface ActionObject {
131
- type: ActionTypes;
132
- state: StateTypes;
133
- nextState: StateTypes;
134
- [rest: string]: any;
135
- }
136
-
137
- export function comboboxReducer(
138
- data: Readonly<ReducerState>,
139
- action: ActionObject
140
- ): ReducerState {
141
- const nextState = {
142
- ...data,
143
- state: action.nextState,
144
- lastActionType: action.type,
145
- };
146
-
147
- switch (action.type) {
148
- case INIT:
149
- case CHANGE:
150
- return {
151
- ...nextState,
152
- text: action.text,
153
- navigationItem: '',
154
- item: '',
155
- };
156
- case NAVIGATE:
157
- case OPEN_WITH_BUTTON:
158
- if (action.persistSelection) {
159
- return {
160
- ...nextState,
161
- navigationItem: data.item,
162
- };
163
- }
164
-
165
- return {
166
- ...nextState,
167
- navigationItem: action.item,
168
- };
169
- case CLEAR_SELECTION:
170
- return {
171
- ...nextState,
172
- navigationItem: '',
173
- };
174
- case CLEAR:
175
- return {
176
- ...nextState,
177
- text: '',
178
- navigationItem: '',
179
- item: '',
180
- };
181
- case BLUR:
182
- return {
183
- ...nextState,
184
- text: action.text,
185
- navigationItem: '',
186
- item: action.item,
187
- };
188
- case CLOSE_WITH_BUTTON:
189
- case ESCAPE:
190
- return {
191
- ...nextState,
192
- navigationItem: '',
193
- item: '',
194
- };
195
- case SELECT_WITH_CLICK:
196
- case SELECT_WITH_KEYBOARD:
197
- return {
198
- ...nextState,
199
- text: action.text,
200
- item: action.item,
201
- navigationItem: '',
202
- };
203
- case FOCUS:
204
- return {
205
- ...nextState,
206
- navigationItem: action.item,
207
- };
208
-
209
- default:
210
- throw new Error(`Unknown action ${action.type}`);
211
- }
212
- }
213
-
214
- const visibleStates = [SUGGESTING, NAVIGATING];
215
- export const isVisible = (state: any) => visibleStates.indexOf(state) >= 0;
216
-
217
- ////////////////////////////////////////////////////////////////////////////////
218
- // The rest is all implementation details
219
-
220
- // Move focus back to the input if we start navigating w/ the
221
- // keyboard after focus has moved to any focusable content in
222
- // the popup.
223
- export function useFocusManagement(
224
- lastActionType: ActionTypes,
225
- inputRef: MutableRefObject<HTMLInputElement | null>
226
- ) {
227
- // useEffect so that the cursor goes to the end of the input instead
228
- // of awkwardly at the beginning, unclear to me why ...
229
- useEffect(() => {
230
- if (
231
- lastActionType === NAVIGATE ||
232
- lastActionType === ESCAPE ||
233
- lastActionType === SELECT_WITH_CLICK ||
234
- lastActionType === OPEN_WITH_BUTTON
235
- ) {
236
- inputRef.current && inputRef.current.focus();
237
- }
238
- });
239
- }
240
-
241
- function getNextItem(
242
- currentItem: string,
243
- key: 'ArrowDown' | 'ArrowUp' | 'PageDown' | 'PageUp' | 'Home' | 'End',
244
- optionsItems: HTMLElement[],
245
- autocomplete: boolean
246
- ): HTMLElement | null {
247
- const jumpToStartOrEnd = key === 'Home' || key === 'End';
248
- let incr = 1;
249
- switch (key) {
250
- case 'PageUp':
251
- incr = -10;
252
- break;
253
- case 'PageDown':
254
- incr = 10;
255
- break;
256
- case 'End':
257
- case 'ArrowUp':
258
- incr = -1;
259
- break;
260
- case 'Home':
261
- case 'ArrowDown':
262
- incr = 1;
263
- break;
264
- }
265
-
266
- const index =
267
- currentItem === ''
268
- ? -1
269
- : optionsItems.findIndex((n) => String(n.id) === currentItem);
270
-
271
- const optionsLen = optionsItems.length;
272
-
273
- // Nothing selected, either go to start, or end
274
- if (index < 0 || jumpToStartOrEnd) {
275
- if (incr > 0) {
276
- // Go to start
277
- return optionsItems[0];
278
- } else {
279
- // Go to end
280
- return optionsItems[optionsLen - 1];
281
- }
282
- } else if (autocomplete) {
283
- const nextIndex = index + incr;
284
-
285
- if (nextIndex < 0 || nextIndex >= optionsLen) {
286
- // Next is outside the bounds of list, return nothing selected
287
- return null;
288
- }
289
- }
290
-
291
- // I'm sure it won't be null, we already check optionsLen above
292
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
293
- return optionsItems[getCircularIndex(index + incr, optionsLen)!];
294
- }
295
-
296
- // We want the same events when the input or the popup have focus (HOW COOL ARE
297
- // HOOKS BTW?) This is probably the hairiest piece but it's not bad.
298
- export function useKeyDown() {
299
- const {
300
- data: { text, navigationItem },
301
- onSelect,
302
- optionsRef,
303
- inputRef,
304
- state,
305
- transition,
306
- autocompletePropRef,
307
- clearOnEscapeRef,
308
- persistSelectionRef,
309
- listScope,
310
- } = useComboBoxContext();
311
-
312
- return function handleKeyDown(event: KeyboardEvent<any>) {
313
- const optionNodes = listScope.current.queryAllNodes(scopeQuery);
314
-
315
- switch (event.key) {
316
- case 'Home':
317
- case 'End':
318
- case 'PageUp':
319
- case 'PageDown':
320
- case 'ArrowUp':
321
- case 'ArrowDown': {
322
- // Don't scroll the page
323
- event.preventDefault();
324
-
325
- const optionsLen = optionNodes.length;
326
-
327
- // If the developer didn't render any options, there's no point in
328
- // trying to navigate--but seriously what the heck? Give us some
329
- // options fam.
330
- if (optionsLen === 0) {
331
- return;
332
- }
333
-
334
- if (state === IDLE) {
335
- // Opening a closed list
336
- transition(NAVIGATE, {
337
- persistSelection: persistSelectionRef.current,
338
- });
339
- } else {
340
- // When autocompletting, we'll not cycle through the list directly
341
- const autocomplete = autocompletePropRef.current;
342
-
343
- // Get next selected item
344
- const nextItem = getNextItem(
345
- navigationItem,
346
- event.key,
347
- optionNodes,
348
- autocomplete
349
- );
350
-
351
- if (nextItem !== null) {
352
- nextItem.scrollIntoView({ behavior: 'auto', block: 'nearest' });
353
- transition(NAVIGATE, {
354
- value: optionsRef.current[nextItem.id].text,
355
- item: nextItem.id,
356
- });
357
- } else {
358
- transition(NAVIGATE, { value: null, item: '' });
359
- }
360
- }
361
- break;
362
- }
363
- case 'Escape': {
364
- if (state !== IDLE) {
365
- transition(ESCAPE);
366
- } else if (state === IDLE && text !== '') {
367
- if (!inputRef.current || !clearOnEscapeRef.current) {
368
- break;
369
- }
370
-
371
- // emulate a inputRef change event, might not work in future versions of React
372
- const lastValue = inputRef.current.value;
373
- inputRef.current.value = '';
374
-
375
- const tracker = (inputRef.current as any)._valueTracker;
376
- if (tracker) {
377
- tracker.setValue(lastValue);
378
- }
379
-
380
- const event = new Event('change', { bubbles: true });
381
- inputRef.current.dispatchEvent(event);
382
- }
383
- break;
384
- }
385
- case 'Enter': {
386
- if (state === NAVIGATING && navigationItem !== '') {
387
- const { value: navigationValue, text: navigationText } =
388
- optionsRef.current[navigationItem];
389
-
390
- // don't want to submit forms
391
- event.preventDefault();
392
- onSelect && onSelect(navigationText, navigationItem, navigationValue);
393
- transition(SELECT_WITH_KEYBOARD, {
394
- text: navigationText,
395
- item: navigationItem,
396
- });
397
- }
398
- break;
399
- }
400
- }
401
- };
402
- }
403
-
404
- export function useBlur() {
405
- const {
406
- data: { navigationItem, text: stateText },
407
- transition,
408
- optionsRef,
409
- popoverRef,
410
- inputRef,
411
- buttonRef,
412
- onSelect,
413
- selectOnBlur, // not implemented yet
414
- } = useComboBoxContext();
415
-
416
- return function handleBlur() {
417
- requestAnimationFrame(() => {
418
- // we on want to close only if focus rests outside the combobox
419
- if (
420
- document.activeElement !== inputRef.current &&
421
- document.activeElement !== buttonRef.current &&
422
- popoverRef.current
423
- ) {
424
- if (popoverRef.current.contains(document.activeElement)) {
425
- // focus landed inside the combobox, keep it open
426
- // in the future, we can make it not close, select, or anything
427
- // this way we can have like... checkboxes available in the
428
- // menu item, etc.
429
- } else {
430
- // focus landed outside the combobox, close it.
431
- if (!selectOnBlur || navigationItem === '') {
432
- // we don't wanna select on blur, or navigationIndex is invalid
433
- transition(BLUR, { text: stateText, item: '' });
434
- } else {
435
- // select the currently selected item
436
- const { value: navigationValue, text: navigationText } =
437
- optionsRef.current[navigationItem];
438
-
439
- onSelect &&
440
- onSelect(navigationText, navigationItem, navigationValue);
441
-
442
- transition(BLUR, {
443
- text: navigationText,
444
- item: navigationItem,
445
- });
446
- }
447
- }
448
- }
449
- });
450
- };
451
- }
1
+ import type { KeyboardEvent, MutableRefObject } from 'react';
2
+ import { useEffect } from 'react';
3
+
4
+ import type {
5
+ StateChart as GenericStateChart,
6
+ StateMachineState,
7
+ } from '../hooks/useReducerMachine';
8
+ import { getCircularIndex } from '../utils/get-circular-index';
9
+ import { useComboBoxContext } from './context';
10
+ import { scopeQuery } from './scopeQuery';
11
+
12
+ ////////////////////////////////////////////////////////////////////////////////
13
+ // States
14
+
15
+ // Nothing going on, waiting for the user to type or use the arrow keys
16
+ export const IDLE = 'IDLE';
17
+
18
+ // The component is suggesting options as the user types
19
+ const SUGGESTING = 'SUGGESTING';
20
+
21
+ // The user is using the keyboard to navigate the list, not typing
22
+ export const NAVIGATING = 'NAVIGATING';
23
+
24
+ export type StateTypes = typeof IDLE | typeof SUGGESTING | typeof NAVIGATING;
25
+
26
+ ////////////////////////////////////////////////////////////////////////////////
27
+ // Actions:
28
+
29
+ // Used to sync the state with controlled state, right after mounting
30
+ export const INIT = 'INIT';
31
+
32
+ // User cleared the value w/ backspace, but input still has focus
33
+ export const CLEAR = 'CLEAR';
34
+
35
+ // User cleared the value w/ backspace, but input still has focus
36
+ export const CLEAR_SELECTION = 'CLEAR_SELECTION';
37
+
38
+ // User is typing
39
+ export const CHANGE = 'CHANGE';
40
+
41
+ // User is navigating w/ the keyboard
42
+ export const NAVIGATE = 'NAVIGATE';
43
+
44
+ // User can be navigating with keyboard and then click instead, we want the
45
+ // value from the click, not the current nav item
46
+ const SELECT_WITH_KEYBOARD = 'SELECT_WITH_KEYBOARD';
47
+ export const SELECT_WITH_CLICK = 'SELECT_WITH_CLICK';
48
+
49
+ // Pretty self-explanatory, user can hit escape or blur to close the popover
50
+ const ESCAPE = 'ESCAPE';
51
+ const BLUR = 'BLUR';
52
+
53
+ export const FOCUS = 'FOCUS';
54
+
55
+ export const OPEN_WITH_BUTTON = 'OPEN_WITH_BUTTON';
56
+
57
+ export const CLOSE_WITH_BUTTON = 'CLOSE_WITH_BUTTON';
58
+
59
+ export type ActionTypes =
60
+ | typeof CLEAR
61
+ | typeof CLEAR_SELECTION
62
+ | typeof CHANGE
63
+ | typeof INIT
64
+ | typeof NAVIGATE
65
+ | typeof SELECT_WITH_KEYBOARD
66
+ | typeof SELECT_WITH_CLICK
67
+ | typeof ESCAPE
68
+ | typeof BLUR
69
+ | typeof FOCUS
70
+ | typeof OPEN_WITH_BUTTON
71
+ | typeof CLOSE_WITH_BUTTON;
72
+
73
+ ////////////////////////////////////////////////////////////////////////////////
74
+ export const stateChart: GenericStateChart<StateTypes, ActionTypes> = {
75
+ initial: IDLE,
76
+ states: {
77
+ [IDLE]: {
78
+ on: {
79
+ [BLUR]: IDLE,
80
+ [CLEAR]: IDLE,
81
+ [INIT]: IDLE,
82
+ [CLEAR_SELECTION]: IDLE,
83
+ [CHANGE]: SUGGESTING,
84
+ [FOCUS]: SUGGESTING,
85
+ [NAVIGATE]: NAVIGATING,
86
+ [OPEN_WITH_BUTTON]: SUGGESTING,
87
+ },
88
+ },
89
+ [SUGGESTING]: {
90
+ on: {
91
+ [CHANGE]: SUGGESTING,
92
+ [FOCUS]: SUGGESTING,
93
+ [INIT]: SUGGESTING,
94
+ [NAVIGATE]: NAVIGATING,
95
+ [CLEAR]: IDLE,
96
+ [CLEAR_SELECTION]: SUGGESTING,
97
+ [ESCAPE]: IDLE,
98
+ [BLUR]: IDLE,
99
+ [SELECT_WITH_CLICK]: IDLE,
100
+ [CLOSE_WITH_BUTTON]: IDLE,
101
+ },
102
+ },
103
+ [NAVIGATING]: {
104
+ on: {
105
+ [CHANGE]: SUGGESTING,
106
+ [FOCUS]: SUGGESTING,
107
+ [INIT]: NAVIGATING,
108
+ [CLEAR]: IDLE,
109
+ [CLEAR_SELECTION]: NAVIGATING,
110
+ [BLUR]: IDLE,
111
+ [ESCAPE]: IDLE,
112
+ [NAVIGATE]: NAVIGATING,
113
+ [SELECT_WITH_KEYBOARD]: IDLE,
114
+ [SELECT_WITH_CLICK]: IDLE,
115
+ [CLOSE_WITH_BUTTON]: IDLE,
116
+ },
117
+ },
118
+ },
119
+ };
120
+
121
+ export interface ReducerState
122
+ extends StateMachineState<StateTypes, ActionTypes> {
123
+ item: string;
124
+ navigationItem: string;
125
+ text: string;
126
+ }
127
+
128
+ interface ActionObject {
129
+ type: ActionTypes;
130
+ state: StateTypes;
131
+ nextState: StateTypes;
132
+ [rest: string]: any;
133
+ }
134
+
135
+ export function comboboxReducer(
136
+ data: Readonly<ReducerState>,
137
+ action: ActionObject
138
+ ): ReducerState {
139
+ const nextState = {
140
+ ...data,
141
+ state: action.nextState,
142
+ lastActionType: action.type,
143
+ };
144
+
145
+ switch (action.type) {
146
+ case INIT:
147
+ case CHANGE:
148
+ return {
149
+ ...nextState,
150
+ text: action.text,
151
+ navigationItem: '',
152
+ item: '',
153
+ };
154
+ case NAVIGATE:
155
+ case OPEN_WITH_BUTTON:
156
+ if (action.persistSelection) {
157
+ return {
158
+ ...nextState,
159
+ navigationItem: data.item,
160
+ };
161
+ }
162
+
163
+ return {
164
+ ...nextState,
165
+ navigationItem: action.item,
166
+ };
167
+ case CLEAR_SELECTION:
168
+ return {
169
+ ...nextState,
170
+ navigationItem: '',
171
+ };
172
+ case CLEAR:
173
+ return {
174
+ ...nextState,
175
+ text: '',
176
+ navigationItem: '',
177
+ item: '',
178
+ };
179
+ case BLUR:
180
+ return {
181
+ ...nextState,
182
+ text: action.text,
183
+ navigationItem: '',
184
+ item: action.item,
185
+ };
186
+ case CLOSE_WITH_BUTTON:
187
+ case ESCAPE:
188
+ return {
189
+ ...nextState,
190
+ navigationItem: '',
191
+ item: '',
192
+ };
193
+ case SELECT_WITH_CLICK:
194
+ case SELECT_WITH_KEYBOARD:
195
+ return {
196
+ ...nextState,
197
+ text: action.text,
198
+ item: action.item,
199
+ navigationItem: '',
200
+ };
201
+ case FOCUS:
202
+ return {
203
+ ...nextState,
204
+ navigationItem: action.item,
205
+ };
206
+
207
+ default:
208
+ throw new Error(`Unknown action ${action.type}`);
209
+ }
210
+ }
211
+
212
+ const visibleStates = [SUGGESTING, NAVIGATING];
213
+ export const isVisible = (state: any) => visibleStates.indexOf(state) >= 0;
214
+
215
+ ////////////////////////////////////////////////////////////////////////////////
216
+ // The rest is all implementation details
217
+
218
+ // Move focus back to the input if we start navigating w/ the
219
+ // keyboard after focus has moved to any focusable content in
220
+ // the popup.
221
+ export function useFocusManagement(
222
+ lastActionType: ActionTypes,
223
+ inputRef: MutableRefObject<HTMLInputElement | null>
224
+ ) {
225
+ // useEffect so that the cursor goes to the end of the input instead
226
+ // of awkwardly at the beginning, unclear to me why ...
227
+ useEffect(() => {
228
+ if (
229
+ lastActionType === NAVIGATE ||
230
+ lastActionType === ESCAPE ||
231
+ lastActionType === SELECT_WITH_CLICK ||
232
+ lastActionType === OPEN_WITH_BUTTON
233
+ ) {
234
+ inputRef.current && inputRef.current.focus();
235
+ }
236
+ });
237
+ }
238
+
239
+ function getNextItem(
240
+ currentItem: string,
241
+ key: 'ArrowDown' | 'ArrowUp' | 'PageDown' | 'PageUp' | 'Home' | 'End',
242
+ optionsItems: HTMLElement[],
243
+ autocomplete: boolean
244
+ ): HTMLElement | null {
245
+ const jumpToStartOrEnd = key === 'Home' || key === 'End';
246
+ let incr = 1;
247
+ switch (key) {
248
+ case 'PageUp':
249
+ incr = -10;
250
+ break;
251
+ case 'PageDown':
252
+ incr = 10;
253
+ break;
254
+ case 'End':
255
+ case 'ArrowUp':
256
+ incr = -1;
257
+ break;
258
+ case 'Home':
259
+ case 'ArrowDown':
260
+ incr = 1;
261
+ break;
262
+ }
263
+
264
+ const index =
265
+ currentItem === ''
266
+ ? -1
267
+ : optionsItems.findIndex((n) => String(n.id) === currentItem);
268
+
269
+ const optionsLen = optionsItems.length;
270
+
271
+ // Nothing selected, either go to start, or end
272
+ if (index < 0 || jumpToStartOrEnd) {
273
+ if (incr > 0) {
274
+ // Go to start
275
+ return optionsItems[0];
276
+ } else {
277
+ // Go to end
278
+ return optionsItems[optionsLen - 1];
279
+ }
280
+ } else if (autocomplete) {
281
+ const nextIndex = index + incr;
282
+
283
+ if (nextIndex < 0 || nextIndex >= optionsLen) {
284
+ // Next is outside the bounds of list, return nothing selected
285
+ return null;
286
+ }
287
+ }
288
+
289
+ // I'm sure it won't be null, we already check optionsLen above
290
+ return optionsItems[getCircularIndex(index + incr, optionsLen)!];
291
+ }
292
+
293
+ // We want the same events when the input or the popup have focus (HOW COOL ARE
294
+ // HOOKS BTW?) This is probably the hairiest piece but it's not bad.
295
+ export function useKeyDown() {
296
+ const {
297
+ data: { text, navigationItem },
298
+ onSelect,
299
+ optionsRef,
300
+ inputRef,
301
+ state,
302
+ transition,
303
+ autocompletePropRef,
304
+ clearOnEscapeRef,
305
+ persistSelectionRef,
306
+ listScope,
307
+ } = useComboBoxContext();
308
+
309
+ return function handleKeyDown(event: KeyboardEvent<any>) {
310
+ const optionNodes = listScope.current.queryAllNodes(scopeQuery);
311
+
312
+ switch (event.key) {
313
+ case 'Home':
314
+ case 'End':
315
+ case 'PageUp':
316
+ case 'PageDown':
317
+ case 'ArrowUp':
318
+ case 'ArrowDown': {
319
+ // Don't scroll the page
320
+ event.preventDefault();
321
+
322
+ const optionsLen = optionNodes.length;
323
+
324
+ // If the developer didn't render any options, there's no point in
325
+ // trying to navigate--but seriously what the heck? Give us some
326
+ // options fam.
327
+ if (optionsLen === 0) {
328
+ return;
329
+ }
330
+
331
+ if (state === IDLE) {
332
+ // Opening a closed list
333
+ transition(NAVIGATE, {
334
+ persistSelection: persistSelectionRef.current,
335
+ });
336
+ } else {
337
+ // When autocompletting, we'll not cycle through the list directly
338
+ const autocomplete = autocompletePropRef.current;
339
+
340
+ // Get next selected item
341
+ const nextItem = getNextItem(
342
+ navigationItem,
343
+ event.key,
344
+ optionNodes,
345
+ autocomplete
346
+ );
347
+
348
+ if (nextItem !== null) {
349
+ nextItem.scrollIntoView({ behavior: 'auto', block: 'nearest' });
350
+ transition(NAVIGATE, {
351
+ value: optionsRef.current[nextItem.id].text,
352
+ item: nextItem.id,
353
+ });
354
+ } else {
355
+ transition(NAVIGATE, { value: null, item: '' });
356
+ }
357
+ }
358
+ break;
359
+ }
360
+ case 'Escape': {
361
+ if (state !== IDLE) {
362
+ transition(ESCAPE);
363
+ } else if (state === IDLE && text !== '') {
364
+ if (!inputRef.current || !clearOnEscapeRef.current) {
365
+ break;
366
+ }
367
+
368
+ // emulate a inputRef change event, might not work in future versions of React
369
+ const lastValue = inputRef.current.value;
370
+ inputRef.current.value = '';
371
+
372
+ const tracker = (inputRef.current as any)._valueTracker;
373
+ if (tracker) {
374
+ tracker.setValue(lastValue);
375
+ }
376
+
377
+ const event = new Event('change', { bubbles: true });
378
+ inputRef.current.dispatchEvent(event);
379
+ }
380
+ break;
381
+ }
382
+ case 'Enter': {
383
+ if (state === NAVIGATING && navigationItem !== '') {
384
+ const { value: navigationValue, text: navigationText } =
385
+ optionsRef.current[navigationItem];
386
+
387
+ // don't want to submit forms
388
+ event.preventDefault();
389
+ onSelect && onSelect(navigationText, navigationItem, navigationValue);
390
+ transition(SELECT_WITH_KEYBOARD, {
391
+ text: navigationText,
392
+ item: navigationItem,
393
+ });
394
+ }
395
+ break;
396
+ }
397
+ }
398
+ };
399
+ }
400
+
401
+ export function useBlur() {
402
+ const {
403
+ data: { navigationItem, text: stateText },
404
+ transition,
405
+ optionsRef,
406
+ popoverRef,
407
+ inputRef,
408
+ buttonRef,
409
+ onSelect,
410
+ selectOnBlur, // not implemented yet
411
+ } = useComboBoxContext();
412
+
413
+ return function handleBlur() {
414
+ requestAnimationFrame(() => {
415
+ // we on want to close only if focus rests outside the combobox
416
+ if (
417
+ document.activeElement !== inputRef.current &&
418
+ document.activeElement !== buttonRef.current &&
419
+ popoverRef.current
420
+ ) {
421
+ if (popoverRef.current.contains(document.activeElement)) {
422
+ // focus landed inside the combobox, keep it open
423
+ // in the future, we can make it not close, select, or anything
424
+ // this way we can have like... checkboxes available in the
425
+ // menu item, etc.
426
+ } else {
427
+ // focus landed outside the combobox, close it.
428
+ if (!selectOnBlur || navigationItem === '') {
429
+ // we don't wanna select on blur, or navigationIndex is invalid
430
+ transition(BLUR, { text: stateText, item: '' });
431
+ } else {
432
+ // select the currently selected item
433
+ const { value: navigationValue, text: navigationText } =
434
+ optionsRef.current[navigationItem];
435
+
436
+ onSelect &&
437
+ onSelect(navigationText, navigationItem, navigationValue);
438
+
439
+ transition(BLUR, {
440
+ text: navigationText,
441
+ item: navigationItem,
442
+ });
443
+ }
444
+ }
445
+ }
446
+ });
447
+ };
448
+ }