tempest-react-sdk 0.53.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (475) hide show
  1. package/README.md +27 -6
  2. package/dist/access/Can.cjs +1 -1
  3. package/dist/access/Can.cjs.map +1 -1
  4. package/dist/access/Can.js +3 -3
  5. package/dist/access/Can.js.map +1 -1
  6. package/dist/audio/level-meter.cjs +1 -1
  7. package/dist/audio/level-meter.cjs.map +1 -1
  8. package/dist/audio/level-meter.js +15 -15
  9. package/dist/audio/level-meter.js.map +1 -1
  10. package/dist/audio/noise-floor.cjs +2 -0
  11. package/dist/audio/noise-floor.cjs.map +1 -0
  12. package/dist/audio/noise-floor.js +27 -0
  13. package/dist/audio/noise-floor.js.map +1 -0
  14. package/dist/audio/voice-activity.cjs +2 -0
  15. package/dist/audio/voice-activity.cjs.map +1 -0
  16. package/dist/audio/voice-activity.js +26 -0
  17. package/dist/audio/voice-activity.js.map +1 -0
  18. package/dist/audio/voice-chain-stages.cjs +2 -0
  19. package/dist/audio/voice-chain-stages.cjs.map +1 -0
  20. package/dist/audio/voice-chain-stages.js +82 -0
  21. package/dist/audio/voice-chain-stages.js.map +1 -0
  22. package/dist/audio/voice-chain.cjs +2 -0
  23. package/dist/audio/voice-chain.cjs.map +1 -0
  24. package/dist/audio/voice-chain.js +74 -0
  25. package/dist/audio/voice-chain.js.map +1 -0
  26. package/dist/auth/lazy-with-retry.cjs.map +1 -1
  27. package/dist/auth/lazy-with-retry.js.map +1 -1
  28. package/dist/auth/passkey.cjs.map +1 -1
  29. package/dist/auth/passkey.js.map +1 -1
  30. package/dist/br/BrazilStateCitySelect.cjs +1 -1
  31. package/dist/br/BrazilStateCitySelect.cjs.map +1 -1
  32. package/dist/br/BrazilStateCitySelect.js +36 -28
  33. package/dist/br/BrazilStateCitySelect.js.map +1 -1
  34. package/dist/br/data/br-centroids.cjs +1 -1
  35. package/dist/br/data/br-centroids.cjs.map +1 -1
  36. package/dist/br/data/br-centroids.js +11105 -11045
  37. package/dist/br/data/br-centroids.js.map +1 -1
  38. package/dist/br/data/br-locations.cjs +1 -1
  39. package/dist/br/data/br-locations.cjs.map +1 -1
  40. package/dist/br/data/br-locations.js +5803 -137
  41. package/dist/br/data/br-locations.js.map +1 -1
  42. package/dist/br/data/br-uf-geo.cjs +1 -1
  43. package/dist/br/data/br-uf-geo.cjs.map +1 -1
  44. package/dist/br/data/br-uf-geo.js +5721 -6777
  45. package/dist/br/data/br-uf-geo.js.map +1 -1
  46. package/dist/br/data/mun/AC.cjs +1 -1
  47. package/dist/br/data/mun/AC.cjs.map +1 -1
  48. package/dist/br/data/mun/AC.js +681 -689
  49. package/dist/br/data/mun/AC.js.map +1 -1
  50. package/dist/br/data/mun/AL.cjs +1 -1
  51. package/dist/br/data/mun/AL.cjs.map +1 -1
  52. package/dist/br/data/mun/AL.js +949 -844
  53. package/dist/br/data/mun/AL.js.map +1 -1
  54. package/dist/br/data/mun/AM.cjs +1 -1
  55. package/dist/br/data/mun/AM.cjs.map +1 -1
  56. package/dist/br/data/mun/AM.js +4337 -4218
  57. package/dist/br/data/mun/AM.js.map +1 -1
  58. package/dist/br/data/mun/AP.cjs +1 -1
  59. package/dist/br/data/mun/AP.cjs.map +1 -1
  60. package/dist/br/data/mun/AP.js +786 -780
  61. package/dist/br/data/mun/AP.js.map +1 -1
  62. package/dist/br/data/mun/BA.cjs +1 -1
  63. package/dist/br/data/mun/BA.cjs.map +1 -1
  64. package/dist/br/data/mun/BA.js +6329 -5633
  65. package/dist/br/data/mun/BA.js.map +1 -1
  66. package/dist/br/data/mun/CE.cjs +1 -1
  67. package/dist/br/data/mun/CE.cjs.map +1 -1
  68. package/dist/br/data/mun/CE.js +2544 -2487
  69. package/dist/br/data/mun/CE.js.map +1 -1
  70. package/dist/br/data/mun/DF.cjs +1 -1
  71. package/dist/br/data/mun/DF.cjs.map +1 -1
  72. package/dist/br/data/mun/DF.js +17 -16
  73. package/dist/br/data/mun/DF.js.map +1 -1
  74. package/dist/br/data/mun/ES.cjs +1 -1
  75. package/dist/br/data/mun/ES.cjs.map +1 -1
  76. package/dist/br/data/mun/ES.js +1101 -1070
  77. package/dist/br/data/mun/ES.js.map +1 -1
  78. package/dist/br/data/mun/GO.cjs +1 -1
  79. package/dist/br/data/mun/GO.cjs.map +1 -1
  80. package/dist/br/data/mun/GO.js +4632 -4636
  81. package/dist/br/data/mun/GO.js.map +1 -1
  82. package/dist/br/data/mun/MA.cjs +1 -1
  83. package/dist/br/data/mun/MA.cjs.map +1 -1
  84. package/dist/br/data/mun/MA.js +3603 -3457
  85. package/dist/br/data/mun/MA.js.map +1 -1
  86. package/dist/br/data/mun/MG.cjs +1 -1
  87. package/dist/br/data/mun/MG.cjs.map +1 -1
  88. package/dist/br/data/mun/MG.js +12716 -11739
  89. package/dist/br/data/mun/MG.js.map +1 -1
  90. package/dist/br/data/mun/MS.cjs +1 -1
  91. package/dist/br/data/mun/MS.cjs.map +1 -1
  92. package/dist/br/data/mun/MS.js +2182 -2335
  93. package/dist/br/data/mun/MS.js.map +1 -1
  94. package/dist/br/data/mun/MT.cjs +1 -1
  95. package/dist/br/data/mun/MT.cjs.map +1 -1
  96. package/dist/br/data/mun/MT.js +4808 -4896
  97. package/dist/br/data/mun/MT.js.map +1 -1
  98. package/dist/br/data/mun/PA.cjs +1 -1
  99. package/dist/br/data/mun/PA.cjs.map +1 -1
  100. package/dist/br/data/mun/PA.js +4981 -4901
  101. package/dist/br/data/mun/PA.js.map +1 -1
  102. package/dist/br/data/mun/PB.cjs +1 -1
  103. package/dist/br/data/mun/PB.cjs.map +1 -1
  104. package/dist/br/data/mun/PB.js +2418 -1956
  105. package/dist/br/data/mun/PB.js.map +1 -1
  106. package/dist/br/data/mun/PE.cjs +1 -1
  107. package/dist/br/data/mun/PE.cjs.map +1 -1
  108. package/dist/br/data/mun/PE.js +2164 -1987
  109. package/dist/br/data/mun/PE.js.map +1 -1
  110. package/dist/br/data/mun/PI.cjs +1 -1
  111. package/dist/br/data/mun/PI.cjs.map +1 -1
  112. package/dist/br/data/mun/PI.js +2982 -2844
  113. package/dist/br/data/mun/PI.js.map +1 -1
  114. package/dist/br/data/mun/PR.cjs +1 -1
  115. package/dist/br/data/mun/PR.cjs.map +1 -1
  116. package/dist/br/data/mun/PR.js +5425 -5240
  117. package/dist/br/data/mun/PR.js.map +1 -1
  118. package/dist/br/data/mun/RJ.cjs +1 -1
  119. package/dist/br/data/mun/RJ.cjs.map +1 -1
  120. package/dist/br/data/mun/RJ.js +1395 -1178
  121. package/dist/br/data/mun/RJ.js.map +1 -1
  122. package/dist/br/data/mun/RN.cjs +1 -1
  123. package/dist/br/data/mun/RN.cjs.map +1 -1
  124. package/dist/br/data/mun/RN.js +1608 -1382
  125. package/dist/br/data/mun/RN.js.map +1 -1
  126. package/dist/br/data/mun/RO.cjs +1 -1
  127. package/dist/br/data/mun/RO.cjs.map +1 -1
  128. package/dist/br/data/mun/RO.js +1392 -1470
  129. package/dist/br/data/mun/RO.js.map +1 -1
  130. package/dist/br/data/mun/RR.cjs +1 -1
  131. package/dist/br/data/mun/RR.cjs.map +1 -1
  132. package/dist/br/data/mun/RR.js +795 -778
  133. package/dist/br/data/mun/RR.js.map +1 -1
  134. package/dist/br/data/mun/RS.cjs +1 -1
  135. package/dist/br/data/mun/RS.cjs.map +1 -1
  136. package/dist/br/data/mun/RS.js +7579 -6424
  137. package/dist/br/data/mun/RS.js.map +1 -1
  138. package/dist/br/data/mun/SC.cjs +1 -1
  139. package/dist/br/data/mun/SC.cjs.map +1 -1
  140. package/dist/br/data/mun/SC.js +3979 -3357
  141. package/dist/br/data/mun/SC.js.map +1 -1
  142. package/dist/br/data/mun/SE.cjs +1 -1
  143. package/dist/br/data/mun/SE.cjs.map +1 -1
  144. package/dist/br/data/mun/SE.js +812 -625
  145. package/dist/br/data/mun/SE.js.map +1 -1
  146. package/dist/br/data/mun/SP.cjs +1 -1
  147. package/dist/br/data/mun/SP.cjs.map +1 -1
  148. package/dist/br/data/mun/SP.js +7866 -6803
  149. package/dist/br/data/mun/SP.js.map +1 -1
  150. package/dist/br/data/mun/TO.cjs +1 -1
  151. package/dist/br/data/mun/TO.cjs.map +1 -1
  152. package/dist/br/data/mun/TO.js +2763 -2817
  153. package/dist/br/data/mun/TO.js.map +1 -1
  154. package/dist/br/locations.cjs +1 -1
  155. package/dist/br/locations.cjs.map +1 -1
  156. package/dist/br/locations.js +97 -24
  157. package/dist/br/locations.js.map +1 -1
  158. package/dist/br/scales.cjs +1 -1
  159. package/dist/br/scales.cjs.map +1 -1
  160. package/dist/br/scales.js +8 -4
  161. package/dist/br/scales.js.map +1 -1
  162. package/dist/br/whatsapp.cjs +2 -0
  163. package/dist/br/whatsapp.cjs.map +1 -0
  164. package/dist/br/whatsapp.js +21 -0
  165. package/dist/br/whatsapp.js.map +1 -0
  166. package/dist/br.cjs +1 -1
  167. package/dist/br.cjs.map +1 -1
  168. package/dist/br.d.ts +188 -10
  169. package/dist/br.js +18 -17
  170. package/dist/br.js.map +1 -1
  171. package/dist/components/BottomSheet/BottomSheet.cjs +1 -1
  172. package/dist/components/BottomSheet/BottomSheet.cjs.map +1 -1
  173. package/dist/components/BottomSheet/BottomSheet.js +30 -27
  174. package/dist/components/BottomSheet/BottomSheet.js.map +1 -1
  175. package/dist/components/Command/Command.cjs +1 -1
  176. package/dist/components/Command/Command.cjs.map +1 -1
  177. package/dist/components/Command/Command.js +78 -75
  178. package/dist/components/Command/Command.js.map +1 -1
  179. package/dist/components/Drawer/Drawer.cjs +1 -1
  180. package/dist/components/Drawer/Drawer.cjs.map +1 -1
  181. package/dist/components/Drawer/Drawer.js +42 -39
  182. package/dist/components/Drawer/Drawer.js.map +1 -1
  183. package/dist/components/DropdownMenu/DropdownMenu.cjs +1 -1
  184. package/dist/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
  185. package/dist/components/DropdownMenu/DropdownMenu.js +111 -67
  186. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  187. package/dist/components/Modal/Modal.cjs +1 -1
  188. package/dist/components/Modal/Modal.cjs.map +1 -1
  189. package/dist/components/Modal/Modal.js +45 -42
  190. package/dist/components/Modal/Modal.js.map +1 -1
  191. package/dist/components/Money/Money.cjs +1 -1
  192. package/dist/components/Money/Money.cjs.map +1 -1
  193. package/dist/components/Money/Money.js +10 -9
  194. package/dist/components/Money/Money.js.map +1 -1
  195. package/dist/components/Pagination/Pagination.cjs +1 -1
  196. package/dist/components/Pagination/Pagination.cjs.map +1 -1
  197. package/dist/components/Pagination/Pagination.js +38 -31
  198. package/dist/components/Pagination/Pagination.js.map +1 -1
  199. package/dist/components/Pagination/Pagination.module.cjs +1 -1
  200. package/dist/components/Pagination/Pagination.module.cjs.map +1 -1
  201. package/dist/components/Pagination/Pagination.module.js +4 -4
  202. package/dist/components/Pagination/Pagination.module.js.map +1 -1
  203. package/dist/components/Portal/Portal.cjs +1 -1
  204. package/dist/components/Portal/Portal.cjs.map +1 -1
  205. package/dist/components/Portal/Portal.js +9 -6
  206. package/dist/components/Portal/Portal.js.map +1 -1
  207. package/dist/components/Portal/portal-host.cjs +2 -0
  208. package/dist/components/Portal/portal-host.cjs.map +1 -0
  209. package/dist/components/Portal/portal-host.js +11 -0
  210. package/dist/components/Portal/portal-host.js.map +1 -0
  211. package/dist/components/Scheduler/Scheduler.cjs +1 -1
  212. package/dist/components/Scheduler/Scheduler.cjs.map +1 -1
  213. package/dist/components/Scheduler/Scheduler.js +83 -82
  214. package/dist/components/Scheduler/Scheduler.js.map +1 -1
  215. package/dist/components/Toast/ToastProvider.cjs +1 -1
  216. package/dist/components/Toast/ToastProvider.cjs.map +1 -1
  217. package/dist/components/Toast/ToastProvider.js +55 -53
  218. package/dist/components/Toast/ToastProvider.js.map +1 -1
  219. package/dist/components/VirtualList/VirtualList.cjs +1 -1
  220. package/dist/components/VirtualList/VirtualList.cjs.map +1 -1
  221. package/dist/components/VirtualList/VirtualList.js +41 -31
  222. package/dist/components/VirtualList/VirtualList.js.map +1 -1
  223. package/dist/components/VirtualTable/VirtualTable.cjs +1 -1
  224. package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -1
  225. package/dist/components/VirtualTable/VirtualTable.js +78 -68
  226. package/dist/components/VirtualTable/VirtualTable.js.map +1 -1
  227. package/dist/forms/masked-inputs.cjs +1 -1
  228. package/dist/forms/masked-inputs.cjs.map +1 -1
  229. package/dist/forms/masked-inputs.js +20 -19
  230. package/dist/forms/masked-inputs.js.map +1 -1
  231. package/dist/hooks/use-fullscreen-element.cjs +2 -0
  232. package/dist/hooks/use-fullscreen-element.cjs.map +1 -0
  233. package/dist/hooks/use-fullscreen-element.js +20 -0
  234. package/dist/hooks/use-fullscreen-element.js.map +1 -0
  235. package/dist/hooks/use-fullscreen.cjs +2 -0
  236. package/dist/hooks/use-fullscreen.cjs.map +1 -0
  237. package/dist/hooks/use-fullscreen.js +66 -0
  238. package/dist/hooks/use-fullscreen.js.map +1 -0
  239. package/dist/hooks/use-push-to-talk.cjs +2 -0
  240. package/dist/hooks/use-push-to-talk.cjs.map +1 -0
  241. package/dist/hooks/use-push-to-talk.js +61 -0
  242. package/dist/hooks/use-push-to-talk.js.map +1 -0
  243. package/dist/http/describe-api-error.cjs.map +1 -1
  244. package/dist/http/describe-api-error.js.map +1 -1
  245. package/dist/http/errors.cjs +1 -1
  246. package/dist/http/errors.cjs.map +1 -1
  247. package/dist/http/errors.js +34 -16
  248. package/dist/http/errors.js.map +1 -1
  249. package/dist/http/parse-response.cjs +2 -2
  250. package/dist/http/parse-response.cjs.map +1 -1
  251. package/dist/http/parse-response.js +9 -8
  252. package/dist/http/parse-response.js.map +1 -1
  253. package/dist/i18n/create-i18n.cjs +1 -1
  254. package/dist/i18n/create-i18n.cjs.map +1 -1
  255. package/dist/i18n/create-i18n.js +28 -27
  256. package/dist/i18n/create-i18n.js.map +1 -1
  257. package/dist/router/AppRouter.cjs +1 -1
  258. package/dist/router/AppRouter.cjs.map +1 -1
  259. package/dist/router/AppRouter.js +28 -21
  260. package/dist/router/AppRouter.js.map +1 -1
  261. package/dist/router/RouteGuard.cjs +1 -1
  262. package/dist/router/RouteGuard.cjs.map +1 -1
  263. package/dist/router/RouteGuard.js +4 -4
  264. package/dist/router/RouteGuard.js.map +1 -1
  265. package/dist/sse/create-event-stream.cjs +1 -1
  266. package/dist/sse/create-event-stream.cjs.map +1 -1
  267. package/dist/sse/create-event-stream.js +34 -38
  268. package/dist/sse/create-event-stream.js.map +1 -1
  269. package/dist/styles/AIChat.css +79 -0
  270. package/dist/styles/Accordion.css +12 -0
  271. package/dist/styles/Alert.css +32 -0
  272. package/dist/styles/AppBar.css +17 -0
  273. package/dist/styles/AppShell.css +8 -0
  274. package/dist/styles/AspectRatio.css +3 -0
  275. package/dist/styles/AudioPlayer.css +13 -0
  276. package/dist/styles/AudioRecorder.css +20 -0
  277. package/dist/styles/Avatar.css +13 -0
  278. package/dist/styles/AvatarGroup.css +18 -0
  279. package/dist/styles/Badge.css +25 -0
  280. package/dist/styles/Banner.css +13 -0
  281. package/dist/styles/BarList.css +10 -0
  282. package/dist/styles/BarcodeScanner.css +21 -0
  283. package/dist/styles/BottomNavigation.css +10 -0
  284. package/dist/styles/BottomSheet.css +11 -0
  285. package/dist/styles/BrazilMap.css +20 -0
  286. package/dist/styles/Breadcrumbs.css +8 -0
  287. package/dist/styles/Button.css +42 -0
  288. package/dist/styles/Calendar.css +16 -0
  289. package/dist/styles/Card.css +15 -0
  290. package/dist/styles/Carousel.css +17 -0
  291. package/dist/styles/Center.css +5 -0
  292. package/dist/styles/Chat.css +35 -0
  293. package/dist/styles/Checkbox.css +11 -0
  294. package/dist/styles/ChipInput.css +13 -0
  295. package/dist/styles/CodeBlock.css +25 -0
  296. package/dist/styles/Collapsible.css +6 -0
  297. package/dist/styles/Combobox.css +18 -0
  298. package/dist/styles/Command.css +20 -0
  299. package/dist/styles/ContextMenu.css +11 -0
  300. package/dist/styles/CopyButton.css +4 -0
  301. package/dist/styles/DataList.css +3 -0
  302. package/dist/styles/DataTable.css +19 -0
  303. package/dist/styles/DateRangePicker.css +22 -0
  304. package/dist/styles/DescriptionList.css +4 -0
  305. package/dist/styles/Divider.css +11 -0
  306. package/dist/styles/Drawer.css +22 -0
  307. package/dist/styles/DropdownMenu.css +16 -0
  308. package/dist/styles/Dropzone.css +7 -0
  309. package/dist/styles/EmptyState.css +6 -0
  310. package/dist/styles/ErrorState.css +6 -0
  311. package/dist/styles/ErrorText.css +2 -0
  312. package/dist/styles/FileUpload.css +17 -0
  313. package/dist/styles/FilterBar.css +19 -0
  314. package/dist/styles/FloatingActionButton.css +23 -0
  315. package/dist/styles/Form.css +21 -0
  316. package/dist/styles/HoverCard.css +11 -0
  317. package/dist/styles/IconPicker.css +6 -0
  318. package/dist/styles/Image.css +2 -0
  319. package/dist/styles/ImageCropper.css +16 -0
  320. package/dist/styles/Input.css +24 -0
  321. package/dist/styles/InstallBanner.css +8 -0
  322. package/dist/styles/Kanban.css +15 -0
  323. package/dist/styles/Kbd.css +5 -0
  324. package/dist/styles/Label.css +3 -0
  325. package/dist/styles/Layout.css +22 -0
  326. package/dist/styles/Lightbox.css +21 -0
  327. package/dist/styles/ListTile.css +14 -0
  328. package/dist/styles/MapLegend.css +9 -0
  329. package/dist/styles/Markdown.css +24 -0
  330. package/dist/styles/Masonry.css +4 -0
  331. package/dist/styles/Menubar.css +17 -0
  332. package/dist/styles/Modal.css +21 -0
  333. package/dist/styles/ModalsManager.css +2 -0
  334. package/dist/styles/MultiSelect.css +21 -0
  335. package/dist/styles/MunicipalitySearch.css +6 -0
  336. package/dist/styles/NProgress.css +2 -0
  337. package/dist/styles/Navbar.css +10 -0
  338. package/dist/styles/NavigationMenu.css +12 -0
  339. package/dist/styles/NavigationRail.css +14 -0
  340. package/dist/styles/NotificationCenter.css +21 -0
  341. package/dist/styles/OfflineIndicator.css +7 -0
  342. package/dist/styles/Page.css +13 -0
  343. package/dist/styles/Pagination.css +12 -0
  344. package/dist/styles/PasswordInput.css +21 -0
  345. package/dist/styles/PinInput.css +13 -0
  346. package/dist/styles/PixQRCode.css +9 -0
  347. package/dist/styles/Popover.css +10 -0
  348. package/dist/styles/Progress.css +11 -0
  349. package/dist/styles/QRCode.css +3 -0
  350. package/dist/styles/Radio.css +14 -0
  351. package/dist/styles/RangeSlider.css +15 -0
  352. package/dist/styles/RatingStars.css +11 -0
  353. package/dist/styles/RefreshIndicator.css +5 -0
  354. package/dist/styles/Resizable.css +13 -0
  355. package/dist/styles/RichTextEditor.css +19 -0
  356. package/dist/styles/SafeArea.css +7 -0
  357. package/dist/styles/Scheduler.css +24 -0
  358. package/dist/styles/ScrollArea.css +7 -0
  359. package/dist/styles/SearchBar.css +11 -0
  360. package/dist/styles/SegmentedControl.css +12 -0
  361. package/dist/styles/Select.css +14 -0
  362. package/dist/styles/Sidebar.css +19 -0
  363. package/dist/styles/SignaturePad.css +6 -0
  364. package/dist/styles/Skeleton.css +7 -0
  365. package/dist/styles/Slider.css +15 -0
  366. package/dist/styles/Spacer.css +4 -0
  367. package/dist/styles/Sparkline.css +4 -0
  368. package/dist/styles/Spinner.css +12 -0
  369. package/dist/styles/Stat.css +12 -0
  370. package/dist/styles/Stepper.css +17 -0
  371. package/dist/styles/StepperInput.css +15 -0
  372. package/dist/styles/Switch.css +11 -0
  373. package/dist/styles/SyncStatusBadge.css +13 -0
  374. package/dist/styles/Table.css +16 -0
  375. package/dist/styles/Tabs.css +14 -0
  376. package/dist/styles/Tag.css +14 -0
  377. package/dist/styles/Textarea.css +13 -0
  378. package/dist/styles/TimePicker.css +14 -0
  379. package/dist/styles/Timeline.css +17 -0
  380. package/dist/styles/Toast.css +21 -0
  381. package/dist/styles/Toggle.css +12 -0
  382. package/dist/styles/ToggleGroup.css +9 -0
  383. package/dist/styles/Tooltip.css +10 -0
  384. package/dist/styles/Tour.css +20 -0
  385. package/dist/styles/TrajectoryMap.css +16 -0
  386. package/dist/styles/Transfer.css +21 -0
  387. package/dist/styles/TreeView.css +17 -0
  388. package/dist/styles/TruncateText.css +2 -0
  389. package/dist/styles/UpdatePrompt.css +10 -0
  390. package/dist/styles/VirtualList.css +5 -0
  391. package/dist/styles/VirtualTable.css +17 -0
  392. package/dist/styles/VisuallyHidden.css +2 -0
  393. package/dist/styles/Wizard.css +4 -0
  394. package/dist/styles/actions.css +104 -0
  395. package/dist/styles/advanced.css +66 -0
  396. package/dist/styles/br.css +41 -0
  397. package/dist/styles/chat.css +113 -0
  398. package/dist/styles/core.css +40 -0
  399. package/dist/styles/data.css +222 -0
  400. package/dist/styles/editor.css +19 -0
  401. package/dist/styles/feedback.css +201 -0
  402. package/dist/styles/forms.css +321 -0
  403. package/dist/styles/geo.css +16 -0
  404. package/dist/styles/icons.css +6 -0
  405. package/dist/styles/identity.css +48 -0
  406. package/dist/styles/layout.css +122 -0
  407. package/dist/styles/media.css +52 -0
  408. package/dist/styles/navigation.css +167 -0
  409. package/dist/styles/overlay.css +73 -0
  410. package/dist/styles/utility.css +39 -0
  411. package/dist/styles.css +1 -1
  412. package/dist/sw/register-service-worker.cjs +1 -1
  413. package/dist/sw/register-service-worker.cjs.map +1 -1
  414. package/dist/sw/register-service-worker.js +21 -17
  415. package/dist/sw/register-service-worker.js.map +1 -1
  416. package/dist/sw.d.ts +17 -1
  417. package/dist/tempest-react-sdk.cjs +1 -1
  418. package/dist/tempest-react-sdk.d.ts +1151 -41
  419. package/dist/tempest-react-sdk.js +295 -286
  420. package/dist/utils/dev-mode.cjs.map +1 -1
  421. package/dist/utils/dev-mode.js.map +1 -1
  422. package/dist/utils/format.cjs +1 -1
  423. package/dist/utils/format.cjs.map +1 -1
  424. package/dist/utils/format.js +22 -14
  425. package/dist/utils/format.js.map +1 -1
  426. package/dist/utils/intl-cache.cjs +2 -0
  427. package/dist/utils/intl-cache.cjs.map +1 -0
  428. package/dist/utils/intl-cache.js +23 -0
  429. package/dist/utils/intl-cache.js.map +1 -0
  430. package/dist/utils/json-frame.cjs +2 -0
  431. package/dist/utils/json-frame.cjs.map +1 -0
  432. package/dist/utils/json-frame.js +30 -0
  433. package/dist/utils/json-frame.js.map +1 -0
  434. package/dist/utils/numbers.cjs +1 -1
  435. package/dist/utils/numbers.cjs.map +1 -1
  436. package/dist/utils/numbers.js +8 -7
  437. package/dist/utils/numbers.js.map +1 -1
  438. package/dist/vision/results.cjs +1 -1
  439. package/dist/vision/results.cjs.map +1 -1
  440. package/dist/vision/results.js +26 -17
  441. package/dist/vision/results.js.map +1 -1
  442. package/dist/vision.d.ts +29 -0
  443. package/dist/vite/tempest-icons.cjs +1 -1
  444. package/dist/vite/tempest-icons.cjs.map +1 -1
  445. package/dist/vite/tempest-icons.js +1 -1
  446. package/dist/vite/tempest-icons.js.map +1 -1
  447. package/dist/webrtc/link-stats.cjs +2 -0
  448. package/dist/webrtc/link-stats.cjs.map +1 -0
  449. package/dist/webrtc/link-stats.js +74 -0
  450. package/dist/webrtc/link-stats.js.map +1 -0
  451. package/dist/webrtc/mesh-quality.cjs +2 -0
  452. package/dist/webrtc/mesh-quality.cjs.map +1 -0
  453. package/dist/webrtc/mesh-quality.js +51 -0
  454. package/dist/webrtc/mesh-quality.js.map +1 -0
  455. package/dist/webrtc/peer-link.cjs +2 -0
  456. package/dist/webrtc/peer-link.cjs.map +1 -0
  457. package/dist/webrtc/peer-link.js +45 -0
  458. package/dist/webrtc/peer-link.js.map +1 -0
  459. package/dist/webrtc/peer-mesh.cjs +2 -0
  460. package/dist/webrtc/peer-mesh.cjs.map +1 -0
  461. package/dist/webrtc/peer-mesh.js +155 -0
  462. package/dist/webrtc/peer-mesh.js.map +1 -0
  463. package/dist/webrtc/use-link-stats.cjs +2 -0
  464. package/dist/webrtc/use-link-stats.cjs.map +1 -0
  465. package/dist/webrtc/use-link-stats.js +22 -0
  466. package/dist/webrtc/use-link-stats.js.map +1 -0
  467. package/dist/ws/create-web-socket.cjs +1 -1
  468. package/dist/ws/create-web-socket.cjs.map +1 -1
  469. package/dist/ws/create-web-socket.js +81 -85
  470. package/dist/ws/create-web-socket.js.map +1 -1
  471. package/dist/ws/use-web-socket.cjs +1 -1
  472. package/dist/ws/use-web-socket.cjs.map +1 -1
  473. package/dist/ws/use-web-socket.js +42 -48
  474. package/dist/ws/use-web-socket.js.map +1 -1
  475. package/package.json +5 -2
@@ -1,14 +1,15 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import { useFocusTrap as t } from "../../hooks/use-focus-trap.js";
3
- import n from "./Command.module.js";
4
- import { useEffect as r, useMemo as i, useRef as a, useState as o } from "react";
5
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
- import { createPortal as l } from "react-dom";
2
+ import { usePortalHost as t } from "../Portal/portal-host.js";
3
+ import { useFocusTrap as n } from "../../hooks/use-focus-trap.js";
4
+ import r from "./Command.module.js";
5
+ import { useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ import { createPortal as u } from "react-dom";
7
8
  //#region src/components/Command/Command.tsx
8
- function u(e, t) {
9
+ function d(e, t) {
9
10
  return t === "" || e.label.toLowerCase().includes(t) ? !0 : (e.keywords ?? []).some((e) => e.toLowerCase().includes(t));
10
11
  }
11
- function d(e) {
12
+ function f(e) {
12
13
  let t = [], n = /* @__PURE__ */ new Map();
13
14
  for (let r of e) {
14
15
  let e = n.get(r.group);
@@ -19,98 +20,100 @@ function d(e) {
19
20
  }
20
21
  return t;
21
22
  }
22
- function f({ open: f, onOpenChange: m, items: h, placeholder: g = "Type a command…", emptyMessage: _ = "No results", className: v }) {
23
- let y = a(null), [b, x] = o(""), [S, C] = o(0), w = "tempest-command-list";
24
- t(y, f);
25
- let T = i(() => {
26
- let e = b.trim().toLowerCase();
27
- return d(h.filter((t) => u(t, e))).flatMap((e) => e.items);
28
- }, [h, b]), E = i(() => d(T), [T]);
29
- if (r(() => {
30
- f && (x(""), C(0));
31
- }, [f]), r(() => {
32
- C((e) => T.length === 0 ? 0 : Math.min(e, T.length - 1));
33
- }, [T.length]), !f || typeof document > "u") return null;
34
- function D(e) {
35
- e.onSelect(), m(!1);
23
+ function p({ open: p, onOpenChange: h, items: g, placeholder: _ = "Type a command…", emptyMessage: v = "No results", className: y }) {
24
+ let b = o(null), [x, S] = s(""), [C, w] = s(0), T = "tempest-command-list";
25
+ n(b, p);
26
+ let E = a(() => {
27
+ let e = x.trim().toLowerCase();
28
+ return f(g.filter((t) => d(t, e))).flatMap((e) => e.items);
29
+ }, [g, x]), D = a(() => f(E), [E]);
30
+ i(() => {
31
+ p && (S(""), w(0));
32
+ }, [p]), i(() => {
33
+ w((e) => E.length === 0 ? 0 : Math.min(e, E.length - 1));
34
+ }, [E.length]);
35
+ let O = t();
36
+ if (!p || !O) return null;
37
+ function k(e) {
38
+ e.onSelect(), h(!1);
36
39
  }
37
- function O(e) {
40
+ function A(e) {
38
41
  if (e.key === "Escape") {
39
- e.preventDefault(), m(!1);
42
+ e.preventDefault(), h(!1);
40
43
  return;
41
44
  }
42
45
  if (e.key === "ArrowDown") {
43
- if (e.preventDefault(), T.length === 0) return;
44
- C((e) => (e + 1) % T.length);
46
+ if (e.preventDefault(), E.length === 0) return;
47
+ w((e) => (e + 1) % E.length);
45
48
  return;
46
49
  }
47
50
  if (e.key === "ArrowUp") {
48
- if (e.preventDefault(), T.length === 0) return;
49
- C((e) => (e - 1 + T.length) % T.length);
51
+ if (e.preventDefault(), E.length === 0) return;
52
+ w((e) => (e - 1 + E.length) % E.length);
50
53
  return;
51
54
  }
52
55
  if (e.key === "Enter") {
53
56
  e.preventDefault();
54
- let t = T[S];
55
- t && D(t);
57
+ let t = E[C];
58
+ t && k(t);
56
59
  }
57
60
  }
58
- let k = -1;
59
- return l(/* @__PURE__ */ s("div", {
60
- className: n.overlay,
61
+ let j = -1;
62
+ return u(/* @__PURE__ */ c("div", {
63
+ className: r.overlay,
61
64
  role: "presentation",
62
- onClick: () => m(!1),
63
- children: /* @__PURE__ */ c("div", {
64
- ref: y,
65
+ onClick: () => h(!1),
66
+ children: /* @__PURE__ */ l("div", {
67
+ ref: b,
65
68
  role: "dialog",
66
69
  "aria-modal": "true",
67
70
  "aria-label": "Command palette",
68
- className: e(n.dialog, v),
71
+ className: e(r.dialog, y),
69
72
  onClick: (e) => e.stopPropagation(),
70
- children: [/* @__PURE__ */ c("div", {
71
- className: n.inputRow,
72
- children: [/* @__PURE__ */ s(p, {}), /* @__PURE__ */ s("input", {
73
+ children: [/* @__PURE__ */ l("div", {
74
+ className: r.inputRow,
75
+ children: [/* @__PURE__ */ c(m, {}), /* @__PURE__ */ c("input", {
73
76
  type: "text",
74
77
  role: "combobox",
75
78
  "aria-expanded": "true",
76
- "aria-controls": w,
79
+ "aria-controls": T,
77
80
  "aria-autocomplete": "list",
78
81
  autoFocus: !0,
79
- className: n.input,
80
- placeholder: g,
81
- value: b,
82
- onChange: (e) => x(e.target.value),
83
- onKeyDown: O
82
+ className: r.input,
83
+ placeholder: _,
84
+ value: x,
85
+ onChange: (e) => S(e.target.value),
86
+ onKeyDown: A
84
87
  })]
85
- }), /* @__PURE__ */ s("ul", {
86
- id: w,
88
+ }), /* @__PURE__ */ c("ul", {
89
+ id: T,
87
90
  role: "listbox",
88
- className: n.list,
89
- children: T.length === 0 ? /* @__PURE__ */ s("li", {
90
- className: n.empty,
91
- children: _
92
- }) : E.map((t) => /* @__PURE__ */ c("li", {
93
- className: n.group,
94
- children: [t.name && /* @__PURE__ */ s("div", {
95
- className: n.groupHeading,
91
+ className: r.list,
92
+ children: E.length === 0 ? /* @__PURE__ */ c("li", {
93
+ className: r.empty,
94
+ children: v
95
+ }) : D.map((t) => /* @__PURE__ */ l("li", {
96
+ className: r.group,
97
+ children: [t.name && /* @__PURE__ */ c("div", {
98
+ className: r.groupHeading,
96
99
  children: t.name
97
- }), /* @__PURE__ */ s("ul", {
100
+ }), /* @__PURE__ */ c("ul", {
98
101
  role: "presentation",
99
- className: n.groupList,
102
+ className: r.groupList,
100
103
  children: t.items.map((t) => {
101
- k += 1;
102
- let r = k === S, i = k;
103
- return /* @__PURE__ */ c("li", {
104
+ j += 1;
105
+ let n = j === C, i = j;
106
+ return /* @__PURE__ */ l("li", {
104
107
  role: "option",
105
- "aria-selected": r,
106
- className: e(n.option, r && n.active),
107
- onMouseMove: () => C(i),
108
- onClick: () => D(t),
109
- children: [t.icon && /* @__PURE__ */ s("span", {
110
- className: n.icon,
108
+ "aria-selected": n,
109
+ className: e(r.option, n && r.active),
110
+ onMouseMove: () => w(i),
111
+ onClick: () => k(t),
112
+ children: [t.icon && /* @__PURE__ */ c("span", {
113
+ className: r.icon,
111
114
  children: t.icon
112
- }), /* @__PURE__ */ s("span", {
113
- className: n.label,
115
+ }), /* @__PURE__ */ c("span", {
116
+ className: r.label,
114
117
  children: t.label
115
118
  })]
116
119
  }, t.id);
@@ -119,23 +122,23 @@ function f({ open: f, onOpenChange: m, items: h, placeholder: g = "Type a comman
119
122
  }, t.name ?? "__ungrouped__"))
120
123
  })]
121
124
  })
122
- }), document.body);
125
+ }), O);
123
126
  }
124
- function p() {
125
- return /* @__PURE__ */ c("svg", {
126
- className: n.searchIcon,
127
+ function m() {
128
+ return /* @__PURE__ */ l("svg", {
129
+ className: r.searchIcon,
127
130
  width: "18",
128
131
  height: "18",
129
132
  viewBox: "0 0 24 24",
130
133
  fill: "none",
131
134
  "aria-hidden": "true",
132
- children: [/* @__PURE__ */ s("circle", {
135
+ children: [/* @__PURE__ */ c("circle", {
133
136
  cx: "11",
134
137
  cy: "11",
135
138
  r: "7",
136
139
  stroke: "currentColor",
137
140
  strokeWidth: "2"
138
- }), /* @__PURE__ */ s("path", {
141
+ }), /* @__PURE__ */ c("path", {
139
142
  d: "M20 20l-3.5-3.5",
140
143
  stroke: "currentColor",
141
144
  strokeWidth: "2",
@@ -144,6 +147,6 @@ function p() {
144
147
  });
145
148
  }
146
149
  //#endregion
147
- export { f as Command };
150
+ export { p as Command };
148
151
 
149
152
  //# sourceMappingURL=Command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Command.js","names":[],"sources":["../../../src/components/Command/Command.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a command palette is a filtered\n * listbox with its own keyboard contract: the body holds the query, the flattened\n * result list, the active index and the Escape/Enter handling, all of which read\n * each other on every keystroke.\n */\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport styles from \"./Command.module.css\";\n\nexport interface CommandItem {\n /** Stable unique identifier for the item. */\n id: string;\n /** Human-readable label shown in the list and matched against the query. */\n label: string;\n /** Optional group heading; items sharing a group are rendered together. */\n group?: string;\n /** Extra terms matched against the query in addition to `label`. */\n keywords?: string[];\n /** Invoked when the item is chosen (click or Enter on the active row). */\n onSelect: () => void;\n /** Optional leading icon. */\n icon?: ReactNode;\n}\n\nexport interface CommandProps {\n /** Whether the palette is visible. */\n open: boolean;\n /** Called with the next open state (Escape, selection, backdrop click). */\n onOpenChange: (open: boolean) => void;\n /** Candidate items to filter and display. */\n items: CommandItem[];\n /** Placeholder for the search input. */\n placeholder?: string;\n /** Rendered when no item matches the current query. */\n emptyMessage?: ReactNode;\n /** Forwarded to the dialog element. */\n className?: string;\n}\n\ninterface CommandGroup {\n name: string | undefined;\n items: CommandItem[];\n}\n\n/**\n * Case-insensitively test whether `item` matches `query` by substring against\n * its label and any keywords. An empty query matches everything.\n *\n * @param item - The candidate command item.\n * @param query - The lowercased search query.\n * @returns `true` when the item should be shown.\n */\nfunction matchesQuery(item: CommandItem, query: string): boolean {\n if (query === \"\") return true;\n if (item.label.toLowerCase().includes(query)) return true;\n return (item.keywords ?? []).some((keyword) => keyword.toLowerCase().includes(query));\n}\n\n/**\n * Partition a flat item list into ordered groups, preserving first-seen order\n * for both groups and the items within them.\n *\n * @param items - The (already filtered) items to group.\n * @returns An ordered list of groups.\n */\nfunction groupItems(items: CommandItem[]): CommandGroup[] {\n const groups: CommandGroup[] = [];\n const indexByName = new Map<string | undefined, number>();\n for (const item of items) {\n const existing = indexByName.get(item.group);\n if (existing === undefined) {\n indexByName.set(item.group, groups.length);\n groups.push({ name: item.group, items: [item] });\n } else {\n groups[existing]!.items.push(item);\n }\n }\n return groups;\n}\n\n/**\n * A ⌘K-style command palette: an overlay dialog with a search input that\n * substring-filters items by label and keywords, groups results, and supports\n * full keyboard navigation (Arrow up/down, Enter, Escape). Traps focus while\n * open.\n */\nexport function Command({\n open,\n onOpenChange,\n items,\n placeholder = \"Type a command…\",\n emptyMessage = \"No results\",\n className,\n}: CommandProps) {\n const dialogRef = useRef<HTMLDivElement>(null);\n const [query, setQuery] = useState<string>(\"\");\n const [activeIndex, setActiveIndex] = useState<number>(0);\n const listId = \"tempest-command-list\";\n\n useFocusTrap(dialogRef, open);\n\n const filtered = useMemo<CommandItem[]>(() => {\n const normalized = query.trim().toLowerCase();\n const visible = items.filter((item) => matchesQuery(item, normalized));\n return groupItems(visible).flatMap((group) => group.items);\n }, [items, query]);\n\n const groups = useMemo<CommandGroup[]>(() => groupItems(filtered), [filtered]);\n\n // Reset transient state whenever the palette opens.\n useEffect(() => {\n if (open) {\n setQuery(\"\");\n setActiveIndex(0);\n }\n }, [open]);\n\n // Keep the active index within bounds as the filtered list shrinks/grows.\n useEffect(() => {\n setActiveIndex((index) => {\n if (filtered.length === 0) return 0;\n return Math.min(index, filtered.length - 1);\n });\n }, [filtered.length]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n function selectItem(item: CommandItem): void {\n item.onSelect();\n onOpenChange(false);\n }\n\n function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Escape\") {\n event.preventDefault();\n onOpenChange(false);\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (filtered.length === 0) return;\n setActiveIndex((index) => (index + 1) % filtered.length);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (filtered.length === 0) return;\n setActiveIndex((index) => (index - 1 + filtered.length) % filtered.length);\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n const item = filtered[activeIndex];\n if (item) selectItem(item);\n }\n }\n\n let renderIndex = -1;\n\n return createPortal(\n <div className={styles.overlay} role=\"presentation\" onClick={() => onOpenChange(false)}>\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Command palette\"\n className={cn(styles.dialog, className)}\n onClick={(event) => event.stopPropagation()}\n >\n <div className={styles.inputRow}>\n <SearchIcon />\n <input\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-autocomplete=\"list\"\n autoFocus\n className={styles.input}\n placeholder={placeholder}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={handleKeyDown}\n />\n </div>\n <ul id={listId} role=\"listbox\" className={styles.list}>\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n groups.map((group) => (\n <li key={group.name ?? \"__ungrouped__\"} className={styles.group}>\n {group.name && (\n <div className={styles.groupHeading}>{group.name}</div>\n )}\n <ul role=\"presentation\" className={styles.groupList}>\n {group.items.map((item) => {\n renderIndex += 1;\n const isActive = renderIndex === activeIndex;\n const index = renderIndex;\n return (\n <li\n key={item.id}\n role=\"option\"\n aria-selected={isActive}\n className={cn(\n styles.option,\n isActive && styles.active,\n )}\n onMouseMove={() => setActiveIndex(index)}\n onClick={() => selectItem(item)}\n >\n {item.icon && (\n <span className={styles.icon}>{item.icon}</span>\n )}\n <span className={styles.label}>{item.label}</span>\n </li>\n );\n })}\n </ul>\n </li>\n ))\n )}\n </ul>\n </div>\n </div>,\n document.body,\n );\n}\n\nfunction SearchIcon() {\n return (\n <svg\n className={styles.searchIcon}\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path d=\"M20 20l-3.5-3.5\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Command.js","names":[],"sources":["../../../src/components/Command/Command.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — a command palette is a filtered\n * listbox with its own keyboard contract: the body holds the query, the flattened\n * result list, the active index and the Escape/Enter handling, all of which read\n * each other on every keystroke.\n */\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useFocusTrap } from \"@/hooks/use-focus-trap\";\nimport styles from \"./Command.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport interface CommandItem {\n /** Stable unique identifier for the item. */\n id: string;\n /** Human-readable label shown in the list and matched against the query. */\n label: string;\n /** Optional group heading; items sharing a group are rendered together. */\n group?: string;\n /** Extra terms matched against the query in addition to `label`. */\n keywords?: string[];\n /** Invoked when the item is chosen (click or Enter on the active row). */\n onSelect: () => void;\n /** Optional leading icon. */\n icon?: ReactNode;\n}\n\nexport interface CommandProps {\n /** Whether the palette is visible. */\n open: boolean;\n /** Called with the next open state (Escape, selection, backdrop click). */\n onOpenChange: (open: boolean) => void;\n /** Candidate items to filter and display. */\n items: CommandItem[];\n /** Placeholder for the search input. */\n placeholder?: string;\n /** Rendered when no item matches the current query. */\n emptyMessage?: ReactNode;\n /** Forwarded to the dialog element. */\n className?: string;\n}\n\ninterface CommandGroup {\n name: string | undefined;\n items: CommandItem[];\n}\n\n/**\n * Case-insensitively test whether `item` matches `query` by substring against\n * its label and any keywords. An empty query matches everything.\n *\n * @param item - The candidate command item.\n * @param query - The lowercased search query.\n * @returns `true` when the item should be shown.\n */\nfunction matchesQuery(item: CommandItem, query: string): boolean {\n if (query === \"\") return true;\n if (item.label.toLowerCase().includes(query)) return true;\n return (item.keywords ?? []).some((keyword) => keyword.toLowerCase().includes(query));\n}\n\n/**\n * Partition a flat item list into ordered groups, preserving first-seen order\n * for both groups and the items within them.\n *\n * @param items - The (already filtered) items to group.\n * @returns An ordered list of groups.\n */\nfunction groupItems(items: CommandItem[]): CommandGroup[] {\n const groups: CommandGroup[] = [];\n const indexByName = new Map<string | undefined, number>();\n for (const item of items) {\n const existing = indexByName.get(item.group);\n if (existing === undefined) {\n indexByName.set(item.group, groups.length);\n groups.push({ name: item.group, items: [item] });\n } else {\n groups[existing]!.items.push(item);\n }\n }\n return groups;\n}\n\n/**\n * A ⌘K-style command palette: an overlay dialog with a search input that\n * substring-filters items by label and keywords, groups results, and supports\n * full keyboard navigation (Arrow up/down, Enter, Escape). Traps focus while\n * open.\n */\nexport function Command({\n open,\n onOpenChange,\n items,\n placeholder = \"Type a command…\",\n emptyMessage = \"No results\",\n className,\n}: CommandProps) {\n const dialogRef = useRef<HTMLDivElement>(null);\n const [query, setQuery] = useState<string>(\"\");\n const [activeIndex, setActiveIndex] = useState<number>(0);\n const listId = \"tempest-command-list\";\n\n useFocusTrap(dialogRef, open);\n\n const filtered = useMemo<CommandItem[]>(() => {\n const normalized = query.trim().toLowerCase();\n const visible = items.filter((item) => matchesQuery(item, normalized));\n return groupItems(visible).flatMap((group) => group.items);\n }, [items, query]);\n\n const groups = useMemo<CommandGroup[]>(() => groupItems(filtered), [filtered]);\n\n // Reset transient state whenever the palette opens.\n useEffect(() => {\n if (open) {\n setQuery(\"\");\n setActiveIndex(0);\n }\n }, [open]);\n\n // Keep the active index within bounds as the filtered list shrinks/grows.\n useEffect(() => {\n setActiveIndex((index) => {\n if (filtered.length === 0) return 0;\n return Math.min(index, filtered.length - 1);\n });\n }, [filtered.length]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n function selectItem(item: CommandItem): void {\n item.onSelect();\n onOpenChange(false);\n }\n\n function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {\n if (event.key === \"Escape\") {\n event.preventDefault();\n onOpenChange(false);\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (filtered.length === 0) return;\n setActiveIndex((index) => (index + 1) % filtered.length);\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (filtered.length === 0) return;\n setActiveIndex((index) => (index - 1 + filtered.length) % filtered.length);\n return;\n }\n if (event.key === \"Enter\") {\n event.preventDefault();\n const item = filtered[activeIndex];\n if (item) selectItem(item);\n }\n }\n\n let renderIndex = -1;\n\n return createPortal(\n <div className={styles.overlay} role=\"presentation\" onClick={() => onOpenChange(false)}>\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Command palette\"\n className={cn(styles.dialog, className)}\n onClick={(event) => event.stopPropagation()}\n >\n <div className={styles.inputRow}>\n <SearchIcon />\n <input\n type=\"text\"\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-autocomplete=\"list\"\n autoFocus\n className={styles.input}\n placeholder={placeholder}\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n onKeyDown={handleKeyDown}\n />\n </div>\n <ul id={listId} role=\"listbox\" className={styles.list}>\n {filtered.length === 0 ? (\n <li className={styles.empty}>{emptyMessage}</li>\n ) : (\n groups.map((group) => (\n <li key={group.name ?? \"__ungrouped__\"} className={styles.group}>\n {group.name && (\n <div className={styles.groupHeading}>{group.name}</div>\n )}\n <ul role=\"presentation\" className={styles.groupList}>\n {group.items.map((item) => {\n renderIndex += 1;\n const isActive = renderIndex === activeIndex;\n const index = renderIndex;\n return (\n <li\n key={item.id}\n role=\"option\"\n aria-selected={isActive}\n className={cn(\n styles.option,\n isActive && styles.active,\n )}\n onMouseMove={() => setActiveIndex(index)}\n onClick={() => selectItem(item)}\n >\n {item.icon && (\n <span className={styles.icon}>{item.icon}</span>\n )}\n <span className={styles.label}>{item.label}</span>\n </li>\n );\n })}\n </ul>\n </li>\n ))\n )}\n </ul>\n </div>\n </div>,\n portalHost,\n );\n}\n\nfunction SearchIcon() {\n return (\n <svg\n className={styles.searchIcon}\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"7\" stroke=\"currentColor\" strokeWidth=\"2\" />\n <path d=\"M20 20l-3.5-3.5\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" />\n </svg>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-breakpoint.cjs"),n=require("./Drawer.module.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("react-dom");function o({open:o,onClose:c,placement:l=`right`,title:u,children:d,footer:f,closeOnBackdrop:p=!0,closeOnEsc:m=!0,hideCloseButton:h=!1,className:g,mobilePlacement:_,showHandle:v=!1}){let{isMobile:y}=t.useBreakpoint(),b=y&&_?_:l;return(0,r.useEffect)(()=>{if(!o)return;let e=document.body.style.overflow;document.body.style.overflow=`hidden`;let t=e=>{m&&e.key===`Escape`&&c()};return window.addEventListener(`keydown`,t),()=>{document.body.style.overflow=e,window.removeEventListener(`keydown`,t)}},[o,m,c]),!o||typeof document>`u`?null:(0,a.createPortal)((0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(`div`,{className:n.default.overlay,onClick:()=>{p&&c()}}),(0,i.jsxs)(`aside`,{role:`dialog`,"aria-modal":`true`,className:e.cn(n.default.panel,n.default[b],g),children:[v&&(b===`bottom`||b===`top`)&&(0,i.jsx)(`div`,{className:e.cn(n.default.handle,b===`top`&&n.default.handleTop),"aria-hidden":!0}),(u||!h)&&(0,i.jsxs)(`header`,{className:n.default.header,children:[(0,i.jsx)(`h3`,{className:n.default.title,children:u}),!h&&(0,i.jsx)(`button`,{type:`button`,"aria-label":`Fechar`,className:n.default.close,onClick:c,children:(0,i.jsx)(s,{})})]}),(0,i.jsx)(`div`,{className:n.default.body,children:d}),f&&(0,i.jsx)(`footer`,{className:n.default.footer,children:f})]})]}),document.body)}function s(){return(0,i.jsx)(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,children:(0,i.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.Drawer=o;
1
+ const e=require("../../utils/cn.cjs"),t=require("../../hooks/use-breakpoint.cjs"),n=require("../Portal/portal-host.cjs"),r=require("./Drawer.module.cjs");let i=require("react"),a=require("react/jsx-runtime"),o=require("react-dom");function s({open:s,onClose:l,placement:u=`right`,title:d,children:f,footer:p,closeOnBackdrop:m=!0,closeOnEsc:h=!0,hideCloseButton:g=!1,className:_,mobilePlacement:v,showHandle:y=!1}){let{isMobile:b}=t.useBreakpoint(),x=b&&v?v:u;(0,i.useEffect)(()=>{if(!s)return;let e=document.body.style.overflow;document.body.style.overflow=`hidden`;let t=e=>{h&&e.key===`Escape`&&l()};return window.addEventListener(`keydown`,t),()=>{document.body.style.overflow=e,window.removeEventListener(`keydown`,t)}},[s,h,l]);let S=n.usePortalHost();return!s||!S?null:(0,o.createPortal)((0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(`div`,{className:r.default.overlay,onClick:()=>{m&&l()}}),(0,a.jsxs)(`aside`,{role:`dialog`,"aria-modal":`true`,className:e.cn(r.default.panel,r.default[x],_),children:[y&&(x===`bottom`||x===`top`)&&(0,a.jsx)(`div`,{className:e.cn(r.default.handle,x===`top`&&r.default.handleTop),"aria-hidden":!0}),(d||!g)&&(0,a.jsxs)(`header`,{className:r.default.header,children:[(0,a.jsx)(`h3`,{className:r.default.title,children:d}),!g&&(0,a.jsx)(`button`,{type:`button`,"aria-label":`Fechar`,className:r.default.close,onClick:l,children:(0,a.jsx)(c,{})})]}),(0,a.jsx)(`div`,{className:r.default.body,children:f}),p&&(0,a.jsx)(`footer`,{className:r.default.footer,children:p})]})]}),S)}function c(){return(0,a.jsx)(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,children:(0,a.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.Drawer=s;
2
2
  //# sourceMappingURL=Drawer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.cjs","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"gMAyCA,SAAgB,EAAO,CACnB,OACA,UACA,YAAY,QACZ,QACA,WACA,SACA,kBAAkB,GAClB,aAAa,GACb,kBAAkB,GAClB,YACA,kBACA,aAAa,IACD,CACZ,GAAM,CAAE,YAAa,EAAA,cAAc,EAC7B,EACF,GAAY,EAAkB,EAAkB,EAkBpD,OAhBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAC/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,CACT,SAAS,KAAK,MAAM,SAAW,EAC/B,OAAO,oBAAoB,UAAW,CAAS,CACnD,CACJ,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE1B,CAAC,GAAQ,OAAO,SAAa,IAAoB,MAErD,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,YAAe,CACP,GAAiB,EAAQ,CACjC,CACH,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,QAAD,CACI,KAAK,SACL,aAAW,OACX,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAqB,CAAS,EAHrE,SAAA,CAKK,IACI,IAAuB,UAAY,IAAuB,SACvD,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAuB,OAAS,EAAA,QAAO,SAC3C,EACA,cAAA,EACH,CAAA,GAEP,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAU,CAAA,EACvC,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,aAAW,SACX,UAAW,EAAA,QAAO,MAClB,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAY,CAAA,CACR,CAAA,CAER,KAEZ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,EAC3C,IAAU,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAAS,SAAA,CAAe,CAAA,CAC1D,CACT,CAAA,CAAA,CAAA,CAAA,EACF,SAAS,IACb,CACJ,CAEA,SAAS,GAAY,CACjB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OACjD,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,uBACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CAEb"}
1
+ {"version":3,"file":"Drawer.cjs","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n portalHost,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":"uOA0CA,SAAgB,EAAO,CACnB,OACA,UACA,YAAY,QACZ,QACA,WACA,SACA,kBAAkB,GAClB,aAAa,GACb,kBAAkB,GAClB,YACA,kBACA,aAAa,IACD,CACZ,GAAM,CAAE,YAAa,EAAA,cAAc,EAC7B,EACF,GAAY,EAAkB,EAAkB,GAEpD,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAC/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAEA,OADA,OAAO,iBAAiB,UAAW,CAAS,MAC/B,CACT,SAAS,KAAK,MAAM,SAAW,EAC/B,OAAO,oBAAoB,UAAW,CAAS,CACnD,CACJ,EAAG,CAAC,EAAM,EAAY,CAAO,CAAC,EAE9B,IAAM,EAAa,EAAA,cAAc,EAIjC,MAFI,CAAC,GAAQ,CAAC,EAAmB,MAEjC,EAAO,EAAA,aAAA,EACH,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,YAAe,CACP,GAAiB,EAAQ,CACjC,CACH,CAAA,GACD,EAAA,EAAA,KAAA,CAAC,QAAD,CACI,KAAK,SACL,aAAW,OACX,UAAW,EAAA,GAAG,EAAA,QAAO,MAAO,EAAA,QAAO,GAAqB,CAAS,EAHrE,SAAA,CAKK,IACI,IAAuB,UAAY,IAAuB,SACvD,EAAA,EAAA,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,GACP,EAAA,QAAO,OACP,IAAuB,OAAS,EAAA,QAAO,SAC3C,EACA,cAAA,EACH,CAAA,GAEP,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAU,CAAA,EACvC,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,aAAW,SACX,UAAW,EAAA,QAAO,MAClB,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAD,CAAY,CAAA,CACR,CAAA,CAER,KAEZ,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAO,UAAc,CAAA,EAC3C,IAAU,EAAA,EAAA,IAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAAS,SAAA,CAAe,CAAA,CAC1D,CACT,CAAA,CAAA,CAAA,CAAA,EACF,CACJ,CACJ,CAEA,SAAS,GAAY,CACjB,OACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OACjD,UAAA,EAAA,EAAA,IAAA,CAAC,OAAD,CACI,EAAE,uBACF,OAAO,eACP,YAAY,IACZ,cAAc,OACjB,CAAA,CACA,CAAA,CAEb"}
@@ -1,71 +1,74 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
2
  import { useBreakpoint as t } from "../../hooks/use-breakpoint.js";
3
- import n from "./Drawer.module.js";
4
- import { useEffect as r } from "react";
5
- import { Fragment as i, jsx as a, jsxs as o } from "react/jsx-runtime";
6
- import { createPortal as s } from "react-dom";
3
+ import { usePortalHost as n } from "../Portal/portal-host.js";
4
+ import r from "./Drawer.module.js";
5
+ import { useEffect as i } from "react";
6
+ import { Fragment as a, jsx as o, jsxs as s } from "react/jsx-runtime";
7
+ import { createPortal as c } from "react-dom";
7
8
  //#region src/components/Drawer/Drawer.tsx
8
- function c({ open: c, onClose: u, placement: d = "right", title: f, children: p, footer: m, closeOnBackdrop: h = !0, closeOnEsc: g = !0, hideCloseButton: _ = !1, className: v, mobilePlacement: y, showHandle: b = !1 }) {
9
- let { isMobile: x } = t(), S = x && y ? y : d;
10
- return r(() => {
11
- if (!c) return;
9
+ function l({ open: l, onClose: d, placement: f = "right", title: p, children: m, footer: h, closeOnBackdrop: g = !0, closeOnEsc: _ = !0, hideCloseButton: v = !1, className: y, mobilePlacement: b, showHandle: x = !1 }) {
10
+ let { isMobile: S } = t(), C = S && b ? b : f;
11
+ i(() => {
12
+ if (!l) return;
12
13
  let e = document.body.style.overflow;
13
14
  document.body.style.overflow = "hidden";
14
15
  let t = (e) => {
15
- g && e.key === "Escape" && u();
16
+ _ && e.key === "Escape" && d();
16
17
  };
17
18
  return window.addEventListener("keydown", t), () => {
18
19
  document.body.style.overflow = e, window.removeEventListener("keydown", t);
19
20
  };
20
21
  }, [
21
- c,
22
- g,
23
- u
24
- ]), !c || typeof document > "u" ? null : s(/* @__PURE__ */ o(i, { children: [/* @__PURE__ */ a("div", {
25
- className: n.overlay,
22
+ l,
23
+ _,
24
+ d
25
+ ]);
26
+ let w = n();
27
+ return !l || !w ? null : c(/* @__PURE__ */ s(a, { children: [/* @__PURE__ */ o("div", {
28
+ className: r.overlay,
26
29
  onClick: () => {
27
- h && u();
30
+ g && d();
28
31
  }
29
- }), /* @__PURE__ */ o("aside", {
32
+ }), /* @__PURE__ */ s("aside", {
30
33
  role: "dialog",
31
34
  "aria-modal": "true",
32
- className: e(n.panel, n[S], v),
35
+ className: e(r.panel, r[C], y),
33
36
  children: [
34
- b && (S === "bottom" || S === "top") && /* @__PURE__ */ a("div", {
35
- className: e(n.handle, S === "top" && n.handleTop),
37
+ x && (C === "bottom" || C === "top") && /* @__PURE__ */ o("div", {
38
+ className: e(r.handle, C === "top" && r.handleTop),
36
39
  "aria-hidden": !0
37
40
  }),
38
- (f || !_) && /* @__PURE__ */ o("header", {
39
- className: n.header,
40
- children: [/* @__PURE__ */ a("h3", {
41
- className: n.title,
42
- children: f
43
- }), !_ && /* @__PURE__ */ a("button", {
41
+ (p || !v) && /* @__PURE__ */ s("header", {
42
+ className: r.header,
43
+ children: [/* @__PURE__ */ o("h3", {
44
+ className: r.title,
45
+ children: p
46
+ }), !v && /* @__PURE__ */ o("button", {
44
47
  type: "button",
45
48
  "aria-label": "Fechar",
46
- className: n.close,
47
- onClick: u,
48
- children: /* @__PURE__ */ a(l, {})
49
+ className: r.close,
50
+ onClick: d,
51
+ children: /* @__PURE__ */ o(u, {})
49
52
  })]
50
53
  }),
51
- /* @__PURE__ */ a("div", {
52
- className: n.body,
53
- children: p
54
- }),
55
- m && /* @__PURE__ */ a("footer", {
56
- className: n.footer,
54
+ /* @__PURE__ */ o("div", {
55
+ className: r.body,
57
56
  children: m
57
+ }),
58
+ h && /* @__PURE__ */ o("footer", {
59
+ className: r.footer,
60
+ children: h
58
61
  })
59
62
  ]
60
- })] }), document.body);
63
+ })] }), w);
61
64
  }
62
- function l() {
63
- return /* @__PURE__ */ a("svg", {
65
+ function u() {
66
+ return /* @__PURE__ */ o("svg", {
64
67
  width: "18",
65
68
  height: "18",
66
69
  viewBox: "0 0 24 24",
67
70
  fill: "none",
68
- children: /* @__PURE__ */ a("path", {
71
+ children: /* @__PURE__ */ o("path", {
69
72
  d: "M6 6l12 12M6 18L18 6",
70
73
  stroke: "currentColor",
71
74
  strokeWidth: "2",
@@ -74,6 +77,6 @@ function l() {
74
77
  });
75
78
  }
76
79
  //#endregion
77
- export { c as Drawer };
80
+ export { l as Drawer };
78
81
 
79
82
  //# sourceMappingURL=Drawer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n if (!open || typeof document === \"undefined\") return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n document.body,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;AAyCA,SAAgB,EAAO,EACnB,SACA,YACA,eAAY,SACZ,UACA,aACA,WACA,qBAAkB,IAClB,gBAAa,IACb,qBAAkB,IAClB,cACA,oBACA,gBAAa,MACD;CACZ,IAAM,EAAE,gBAAa,EAAc,GAC7B,IACF,KAAY,IAAkB,IAAkB;CAkBpD,OAhBA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAC/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B;GAET,AADA,SAAS,KAAK,MAAM,WAAW,GAC/B,OAAO,oBAAoB,WAAW,CAAS;EACnD;CACJ,GAAG;EAAC;EAAM;EAAY;CAAO,CAAC,GAE1B,CAAC,KAAQ,OAAO,WAAa,MAAoB,OAE9C,EACH,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;CACH,CAAA,GACD,kBAAC,SAAD;EACI,MAAK;EACL,cAAW;EACX,WAAW,EAAG,EAAO,OAAO,EAAO,IAAqB,CAAS;EAHrE,UAAA;GAKK,MACI,MAAuB,YAAY,MAAuB,UACvD,kBAAC,OAAD;IACI,WAAW,EACP,EAAO,QACP,MAAuB,SAAS,EAAO,SAC3C;IACA,eAAA;GACH,CAAA;IAEP,KAAS,CAAC,MACR,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAA1B,UAAA,CACI,kBAAC,MAAD;KAAI,WAAW,EAAO;KAAQ,UAAA;IAAU,CAAA,GACvC,CAAC,KACE,kBAAC,UAAD;KACI,MAAK;KACL,cAAW;KACX,WAAW,EAAO;KAClB,SAAS;KAET,UAAA,kBAAC,GAAD,CAAY,CAAA;IACR,CAAA,CAER;;GAEZ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO;GAAc,CAAA;GAC3C,KAAU,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAAS,UAAA;GAAe,CAAA;EAC1D;CACT,CAAA,CAAA,EAAA,CAAA,GACF,SAAS,IACb;AACJ;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
1
+ {"version":3,"file":"Drawer.js","names":[],"sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — placement plus mobilePlacement is\n * the whole point — a drawer that comes from the side on desktop and from the bottom\n * on a phone — and the rest is the dialog contract the SDK's Modal also has (open,\n * onClose, title, children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton)\n * plus showHandle for the sheet affordance.\n */\nimport { useEffect } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport { useBreakpoint } from \"@/hooks/use-breakpoint\";\nimport styles from \"./Drawer.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type DrawerPlacement = \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface DrawerProps {\n open: boolean;\n onClose: () => void;\n placement?: DrawerPlacement;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n hideCloseButton?: boolean;\n className?: string;\n /**\n * Auto-switch to bottom-sheet placement on mobile viewports (< md).\n * Modern mobile apps default to bottom drawers; on desktop the original\n * `placement` is preserved.\n */\n mobilePlacement?: DrawerPlacement;\n /** Render a drag handle indicator at the leading edge (bottom-sheet style). */\n showHandle?: boolean;\n}\n\n/**\n * Sliding side panel. Same building blocks as {@link Modal} but anchored to\n * an edge. Locks body scroll while open.\n */\nexport function Drawer({\n open,\n onClose,\n placement = \"right\",\n title,\n children,\n footer,\n closeOnBackdrop = true,\n closeOnEsc = true,\n hideCloseButton = false,\n className,\n mobilePlacement,\n showHandle = false,\n}: DrawerProps) {\n const { isMobile } = useBreakpoint();\n const effectivePlacement: DrawerPlacement =\n isMobile && mobilePlacement ? mobilePlacement : placement;\n\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n return () => {\n document.body.style.overflow = previousOverflow;\n window.removeEventListener(\"keydown\", handleKey);\n };\n }, [open, closeOnEsc, onClose]);\n\n const portalHost = usePortalHost();\n\n if (!open || !portalHost) return null;\n\n return createPortal(\n <>\n <div\n className={styles.overlay}\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n />\n <aside\n role=\"dialog\"\n aria-modal=\"true\"\n className={cn(styles.panel, styles[effectivePlacement], className)}\n >\n {showHandle &&\n (effectivePlacement === \"bottom\" || effectivePlacement === \"top\") && (\n <div\n className={cn(\n styles.handle,\n effectivePlacement === \"top\" && styles.handleTop,\n )}\n aria-hidden\n />\n )}\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n <h3 className={styles.title}>{title}</h3>\n {!hideCloseButton && (\n <button\n type=\"button\"\n aria-label=\"Fechar\"\n className={styles.close}\n onClick={onClose}\n >\n <CloseIcon />\n </button>\n )}\n </header>\n )}\n <div className={styles.body}>{children}</div>\n {footer && <footer className={styles.footer}>{footer}</footer>}\n </aside>\n </>,\n portalHost,\n );\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path\n d=\"M6 6l12 12M6 18L18 6\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n );\n}\n"],"mappings":";;;;;;;;AA0CA,SAAgB,EAAO,EACnB,SACA,YACA,eAAY,SACZ,UACA,aACA,WACA,qBAAkB,IAClB,gBAAa,IACb,qBAAkB,IAClB,cACA,oBACA,gBAAa,MACD;CACZ,IAAM,EAAE,gBAAa,EAAc,GAC7B,IACF,KAAY,IAAkB,IAAkB;CAEpD,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAC/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAEA,OADA,OAAO,iBAAiB,WAAW,CAAS,SAC/B;GAET,AADA,SAAS,KAAK,MAAM,WAAW,GAC/B,OAAO,oBAAoB,WAAW,CAAS;EACnD;CACJ,GAAG;EAAC;EAAM;EAAY;CAAO,CAAC;CAE9B,IAAM,IAAa,EAAc;CAIjC,OAFI,CAAC,KAAQ,CAAC,IAAmB,OAE1B,EACH,kBAAA,GAAA,EAAA,UAAA,CACI,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;CACH,CAAA,GACD,kBAAC,SAAD;EACI,MAAK;EACL,cAAW;EACX,WAAW,EAAG,EAAO,OAAO,EAAO,IAAqB,CAAS;EAHrE,UAAA;GAKK,MACI,MAAuB,YAAY,MAAuB,UACvD,kBAAC,OAAD;IACI,WAAW,EACP,EAAO,QACP,MAAuB,SAAS,EAAO,SAC3C;IACA,eAAA;GACH,CAAA;IAEP,KAAS,CAAC,MACR,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAA1B,UAAA,CACI,kBAAC,MAAD;KAAI,WAAW,EAAO;KAAQ,UAAA;IAAU,CAAA,GACvC,CAAC,KACE,kBAAC,UAAD;KACI,MAAK;KACL,cAAW;KACX,WAAW,EAAO;KAClB,SAAS;KAET,UAAA,kBAAC,GAAD,CAAY,CAAA;IACR,CAAA,CAER;;GAEZ,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAO;GAAc,CAAA;GAC3C,KAAU,kBAAC,UAAD;IAAQ,WAAW,EAAO;IAAS,UAAA;GAAe,CAAA;EAC1D;CACT,CAAA,CAAA,EAAA,CAAA,GACF,CACJ;AACJ;AAEA,SAAS,IAAY;CACjB,OACI,kBAAC,OAAD;EAAK,OAAM;EAAK,QAAO;EAAK,SAAQ;EAAY,MAAK;EACjD,UAAA,kBAAC,QAAD;GACI,GAAE;GACF,QAAO;GACP,aAAY;GACZ,eAAc;EACjB,CAAA;CACA,CAAA;AAEb"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./DropdownMenu.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e){switch(e){case`bottom-end`:return t.default.bottomEnd;case`top-start`:return t.default.topStart;case`top-end`:return t.default.topEnd;default:return t.default.bottomStart}}function a({trigger:a,items:o,placement:s=`bottom-start`,className:c}){let[l,u]=(0,n.useState)(!1),[d,f]=(0,n.useState)(-1),p=(0,n.useId)(),m=(0,n.useRef)(null),h=(0,n.useRef)([]),g=o.map((e,t)=>e.type===`item`&&!e.disabled?t:-1).filter(e=>e!==-1),_=(0,n.useCallback)(()=>{u(!1),f(-1)},[]);(0,n.useEffect)(()=>{if(!l)return;let e=e=>{if(e.key===`Escape`){_();return}if(e.key===`ArrowDown`){e.preventDefault();let t=g.indexOf(d),n=g[(t+1)%g.length]??-1;f(n),h.current[n]?.focus()}if(e.key===`ArrowUp`){e.preventDefault();let t=g.indexOf(d),n=g[(t-1+g.length)%g.length]??-1;f(n),h.current[n]?.focus()}},t=e=>{m.current&&!m.current.contains(e.target)&&_()};return window.addEventListener(`keydown`,e),window.addEventListener(`mousedown`,t),()=>{window.removeEventListener(`keydown`,e),window.removeEventListener(`mousedown`,t)}},[l,d,g,_]);let v=e=>{a.props.onClick?.(e),u(e=>!e)},y={...a,props:{...a.props,onClick:v,"aria-expanded":l,"aria-controls":p,"aria-haspopup":`menu`}},b=e=>{e.onSelect(),_()};return(0,r.jsxs)(`span`,{ref:m,className:t.default.root,children:[y,l&&(0,r.jsx)(`ul`,{id:p,role:`menu`,className:e.cn(t.default.menu,i(s),c),children:o.map((n,i)=>n.type===`separator`?(0,r.jsx)(`li`,{role:`separator`,className:t.default.separator,"aria-hidden":!0},n.id):n.type===`label`?(0,r.jsx)(`li`,{role:`presentation`,className:t.default.label,children:n.label},n.id):(0,r.jsx)(`li`,{role:`none`,children:(0,r.jsxs)(`button`,{ref:e=>{h.current[i]=e},type:`button`,role:`menuitem`,className:e.cn(t.default.item,n.danger&&t.default.danger,d===i&&t.default.active),disabled:n.disabled,onClick:()=>b(n),onMouseEnter:()=>f(i),children:[n.icon&&(0,r.jsx)(`span`,{"aria-hidden":!0,children:n.icon}),n.label]})},n.id))})]})}exports.DropdownMenu=a;
1
+ const e=require("../../utils/cn.cjs"),t=require("./DropdownMenu.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e){switch(e){case`bottom-end`:return t.default.bottomEnd;case`top-start`:return t.default.topStart;case`top-end`:return t.default.topEnd;default:return t.default.bottomStart}}function a(e){return(e.type===`item`||e.type===`checkbox`)&&!e.disabled}function o({trigger:o,items:s,placement:c=`bottom-start`,className:l}){let[u,d]=(0,n.useState)(!1),[f,p]=(0,n.useState)(-1),m=(0,n.useId)(),h=(0,n.useRef)(null),g=(0,n.useRef)([]),_=(0,n.useRef)(null),v=s.map((e,t)=>a(e)?t:-1).filter(e=>e!==-1),y=(0,n.useCallback)(()=>{h.current?.querySelector(`[aria-haspopup='menu']`)?.focus()},[]),b=(0,n.useCallback)(e=>{d(!1),p(-1),e&&y()},[y]),x=(0,n.useCallback)(e=>{p(e),g.current[e]?.focus()},[]),S=e=>{if(v.length===0)return;let t=(v.indexOf(f)+e+v.length)%v.length;x(v[t]??-1)};(0,n.useEffect)(()=>{if(!u||_.current===null)return;let e=_.current===`last`?v.at(-1):v[0];_.current=null,e!==void 0&&x(e)},[u,v,x]),(0,n.useEffect)(()=>{if(!u)return;let e=e=>{h.current&&!h.current.contains(e.target)&&b(!1)};return window.addEventListener(`mousedown`,e),()=>window.removeEventListener(`mousedown`,e)},[u,b]);let C=e=>{_.current=e,d(!0)},w=e=>{if(o.props.onKeyDown?.(e),!e.defaultPrevented){if(e.key===`ArrowDown`||!u&&(e.key===`Enter`||e.key===` `)){e.preventDefault(),C(`first`);return}e.key===`ArrowUp`&&(e.preventDefault(),C(`last`))}},T=e=>{switch(e.key){case`ArrowDown`:e.preventDefault(),S(1);break;case`ArrowUp`:e.preventDefault(),S(-1);break;case`Home`:e.preventDefault(),v[0]!==void 0&&x(v[0]);break;case`End`:{e.preventDefault();let t=v.at(-1);t!==void 0&&x(t);break}case`Escape`:e.preventDefault(),b(!0);break;case`Tab`:b(!1)}},E=e=>{if(o.props.onClick?.(e),!e.defaultPrevented){if(u){b(!1);return}C(`first`)}},D={...o,props:{...o.props,onClick:E,onKeyDown:w,"aria-expanded":u,"aria-controls":m,"aria-haspopup":`menu`}},O=e=>{e.onSelect(),e.type!==`checkbox`&&b(!0)};return(0,r.jsxs)(`span`,{ref:h,className:t.default.root,children:[D,u&&(0,r.jsx)(`ul`,{id:m,role:`menu`,className:e.cn(t.default.menu,i(c),l),onKeyDown:T,children:s.map((n,i)=>{if(n.type===`separator`)return(0,r.jsx)(`li`,{role:`separator`,className:t.default.separator,"aria-hidden":!0},n.id);if(n.type===`label`)return(0,r.jsx)(`li`,{role:`presentation`,className:t.default.label,children:n.label},n.id);let a=n.type===`checkbox`;return(0,r.jsx)(`li`,{role:`none`,children:(0,r.jsxs)(`button`,{ref:e=>{g.current[i]=e},type:`button`,role:a?`menuitemcheckbox`:`menuitem`,"aria-checked":a?n.checked:void 0,tabIndex:f===i?0:-1,className:e.cn(t.default.item,n.type===`item`&&n.danger&&t.default.danger,f===i&&t.default.active),disabled:n.disabled,onClick:()=>O(n),onMouseEnter:()=>p(i),children:[n.icon&&(0,r.jsx)(`span`,{"aria-hidden":!0,children:n.icon}),n.label]})},n.id)})})]})}exports.DropdownMenu=o;
2
2
  //# sourceMappingURL=DropdownMenu.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, Escape, and roving focus over items that can be disabled or separators —\n * one keyboard model that has to see the whole item list to know where the next stop\n * is.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/**\n * Dropdown menu — list of selectable actions anchored to a trigger.\n *\n * - Toggle on trigger click.\n * - Close on outside click / Escape / item selection.\n * - Arrow keys navigate, Enter activates focused item.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n\n const selectableIndexes = items\n .map((entry, index) => (entry.type === \"item\" && !entry.disabled ? index : -1))\n .filter((i) => i !== -1);\n\n const close = useCallback((): void => {\n setOpen(false);\n setActiveIndex(-1);\n }, []);\n\n useEffect(() => {\n if (!open) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") {\n close();\n return;\n }\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const next = selectableIndexes[(current + 1) % selectableIndexes.length] ?? -1;\n setActiveIndex(next);\n itemRefs.current[next]?.focus();\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n const current = selectableIndexes.indexOf(activeIndex);\n const prev =\n selectableIndexes[\n (current - 1 + selectableIndexes.length) % selectableIndexes.length\n ] ?? -1;\n setActiveIndex(prev);\n itemRefs.current[prev]?.focus();\n }\n };\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close();\n };\n window.addEventListener(\"keydown\", onKey);\n window.addEventListener(\"mousedown\", onDown);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n window.removeEventListener(\"mousedown\", onDown);\n };\n }, [open, activeIndex, selectableIndexes, close]);\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n setOpen((prev) => !prev);\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: Extract<DropdownMenuEntry, { type: \"item\" }>): void => {\n entry.onSelect();\n close();\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role=\"menuitem\"\n className={cn(\n styles.item,\n entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":"mIAsCA,SAAS,EAAe,EAA0C,CAC9D,OAAQ,EAAR,CACI,IAAK,aACD,OAAO,EAAA,QAAO,UAClB,IAAK,YACD,OAAO,EAAA,QAAO,SAClB,IAAK,UACD,OAAO,EAAA,QAAO,OAElB,QACI,OAAO,EAAA,QAAO,WACtB,CACJ,CASA,SAAgB,EAAa,CACzB,UACA,QACA,YAAY,eACZ,aACkB,CAClB,GAAM,CAAC,EAAM,IAAA,EAAW,EAAA,SAAA,CAAS,EAAK,EAChC,CAAC,EAAa,IAAA,EAAkB,EAAA,SAAA,CAAiB,EAAE,EACnD,GAAA,EAAK,EAAA,MAAA,CAAM,EACX,GAAA,EAAU,EAAA,OAAA,CAAwB,IAAI,EACtC,GAAA,EAAW,EAAA,OAAA,CAAwC,CAAC,CAAC,EAErD,EAAoB,EACrB,KAAK,EAAO,IAAW,EAAM,OAAS,QAAU,CAAC,EAAM,SAAW,EAAQ,EAAG,CAAC,CAC9E,OAAQ,GAAM,IAAM,EAAE,EAErB,GAAA,EAAQ,EAAA,YAAA,KAAwB,CAClC,EAAQ,EAAK,EACb,EAAe,EAAE,CACrB,EAAG,CAAC,CAAC,GAEL,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAS,GAA+B,CAC1C,GAAI,EAAM,MAAQ,SAAU,CACxB,EAAM,EACN,MACJ,CACA,GAAI,EAAM,MAAQ,YAAa,CAC3B,EAAM,eAAe,EACrB,IAAM,EAAU,EAAkB,QAAQ,CAAW,EAC/C,EAAO,GAAmB,EAAU,GAAK,EAAkB,SAAW,GAC5E,EAAe,CAAI,EACnB,EAAS,QAAQ,EAAK,EAAE,MAAM,CAClC,CACA,GAAI,EAAM,MAAQ,UAAW,CACzB,EAAM,eAAe,EACrB,IAAM,EAAU,EAAkB,QAAQ,CAAW,EAC/C,EACF,GACK,EAAU,EAAI,EAAkB,QAAU,EAAkB,SAC5D,GACT,EAAe,CAAI,EACnB,EAAS,QAAQ,EAAK,EAAE,MAAM,CAClC,CACJ,EACM,EAAU,GAA4B,CACpC,EAAQ,SAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,GAAG,EAAM,CAClF,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAK,EACxC,OAAO,iBAAiB,YAAa,CAAM,MAC9B,CACT,OAAO,oBAAoB,UAAW,CAAK,EAC3C,OAAO,oBAAoB,YAAa,CAAM,CAClD,CACJ,EAAG,CAAC,EAAM,EAAa,EAAmB,CAAK,CAAC,EAEhD,IAAM,EAAsB,GAAkC,CAC1D,EAAQ,MAAM,UAAU,CAAK,EAC7B,EAAS,GAAS,CAAC,CAAI,CAC3B,EAEM,EAAe,CACjB,GAAG,EACH,MAAO,CACH,GAAG,EAAQ,MACX,QAAS,EACT,gBAAiB,EACjB,gBAAiB,EACjB,gBAAiB,MACrB,CACJ,EAEM,EAAgB,GAA8D,CAChF,EAAM,SAAS,EACf,EAAM,CACV,EAEA,OACI,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,IAAK,EAAS,UAAW,EAAA,QAAO,KAAtC,SAAA,CACK,EACA,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CACQ,KACJ,KAAK,OACL,UAAW,EAAA,GAAG,EAAA,QAAO,KAAM,EAAe,CAAS,EAAG,CAAS,EAE9D,SAAA,EAAM,KAAK,EAAO,IACX,EAAM,OAAS,aAEX,EAAA,EAAA,IAAA,CAAC,KAAD,CAEI,KAAK,YACL,UAAW,EAAA,QAAO,UAClB,cAAA,EACH,EAJQ,EAAM,EAId,EAGL,EAAM,OAAS,SAEX,EAAA,EAAA,IAAA,CAAC,KAAD,CAAmB,KAAK,eAAe,UAAW,EAAA,QAAO,MACpD,SAAA,EAAM,KACP,EAFK,EAAM,EAEX,GAIR,EAAA,EAAA,IAAA,CAAC,KAAD,CAAmB,KAAK,OACpB,UAAA,EAAA,EAAA,KAAA,CAAC,SAAD,CACI,IAAM,GAAO,CACT,EAAS,QAAQ,GAAS,CAC9B,EACA,KAAK,SACL,KAAK,WACL,UAAW,EAAA,GACP,EAAA,QAAO,KACP,EAAM,QAAU,EAAA,QAAO,OACvB,IAAgB,GAAS,EAAA,QAAO,MACpC,EACA,SAAU,EAAM,SAChB,YAAe,EAAa,CAAK,EACjC,iBAAoB,EAAe,CAAK,EAb5C,SAAA,CAeK,EAAM,OAAQ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,cAAA,GAAa,SAAA,EAAM,IAAW,CAAA,EAClD,EAAM,KACH,GACR,EAnBK,EAAM,EAmBX,CAEX,CACD,CAAA,CAEN,GAEd"}
1
+ {"version":3,"file":"DropdownMenu.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.tsx"],"sourcesContent":["/**\n * @tempest-limits file-lines, function-lines — the body owns placement, outside-\n * click, and the whole APG menu-button keyboard model, which has to see the entire\n * entry list to know where the next stop is. Splitting it would move the focus\n * bookkeeping away from the list it indexes into.\n */\nimport { useCallback, useEffect, useId, useRef, useState } from \"react\";\nimport type { KeyboardEvent as ReactKeyboardEvent, ReactElement, ReactNode } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./DropdownMenu.module.css\";\n\nexport type DropdownMenuPlacement = \"bottom-start\" | \"bottom-end\" | \"top-start\" | \"top-end\";\n\nexport type DropdownMenuEntry =\n | {\n type: \"item\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n danger?: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | {\n /**\n * An entry that carries on/off state.\n *\n * Rendered as `role=\"menuitemcheckbox\"` with `aria-checked`, which is how\n * a screen reader announces \"checked\" instead of leaving the state\n * invisible. Selecting it does **not** close the menu: toggling two\n * settings in a row is the ordinary case, and closing after the first\n * would make the second a second trip.\n */\n type: \"checkbox\";\n id: string;\n label: ReactNode;\n icon?: ReactNode;\n checked: boolean;\n disabled?: boolean;\n onSelect: () => void;\n }\n | { type: \"separator\"; id: string }\n | { type: \"label\"; id: string; label: ReactNode };\n\n/** The entry kinds a user can move focus to and activate. */\ntype SelectableEntry = Extract<DropdownMenuEntry, { type: \"item\" | \"checkbox\" }>;\n\nexport interface DropdownMenuProps {\n trigger: ReactElement<{\n onClick?: (e: React.MouseEvent) => void;\n onKeyDown?: (e: ReactKeyboardEvent) => void;\n \"aria-expanded\"?: boolean;\n \"aria-controls\"?: string;\n \"aria-haspopup\"?: boolean | \"menu\";\n }>;\n items: DropdownMenuEntry[];\n placement?: DropdownMenuPlacement;\n className?: string;\n}\n\nfunction placementClass(placement: DropdownMenuPlacement): string {\n switch (placement) {\n case \"bottom-end\":\n return styles.bottomEnd;\n case \"top-start\":\n return styles.topStart;\n case \"top-end\":\n return styles.topEnd;\n case \"bottom-start\":\n default:\n return styles.bottomStart;\n }\n}\n\n/** Whether an entry can take focus — an item or checkbox that is not disabled. */\nfunction isSelectable(entry: DropdownMenuEntry): entry is SelectableEntry {\n return (entry.type === \"item\" || entry.type === \"checkbox\") && !entry.disabled;\n}\n\n/**\n * Dropdown menu — a list of actions anchored to a trigger, following the\n * [APG menu button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/).\n *\n * `role=\"menu\"` is a promise about the keyboard, and this component keeps it:\n *\n * - `Enter`, `Space` or `ArrowDown` on the trigger opens and focuses the first\n * entry; `ArrowUp` opens and focuses the last.\n * - `ArrowUp` / `ArrowDown` move with wrap, `Home` / `End` jump to the ends.\n * - `Escape` closes and returns focus to the trigger, so the next `Tab` continues\n * from where the user was rather than from the top of the document.\n * - `Tab` closes the menu and lets the page's own tab order take over.\n * - Focus is managed: entries carry `tabIndex={-1}` and only the active one is\n * `0`, which is what stops `Tab` from walking the menu one entry at a time.\n *\n * Disabled entries, separators and labels are skipped by every movement.\n */\nexport function DropdownMenu({\n trigger,\n items,\n placement = \"bottom-start\",\n className,\n}: DropdownMenuProps) {\n const [open, setOpen] = useState(false);\n const [activeIndex, setActiveIndex] = useState<number>(-1);\n const id = useId();\n const rootRef = useRef<HTMLSpanElement>(null);\n const itemRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const pendingFocus = useRef<\"first\" | \"last\" | null>(null);\n\n const selectable = items\n .map((entry, index) => (isSelectable(entry) ? index : -1))\n .filter((index) => index !== -1);\n\n /**\n * Focus the trigger again.\n *\n * Found through the `aria-haspopup` this component puts on it, rather than\n * through a ref: `ref` lives in different places in React 18 and 19, and a\n * consumer's custom trigger is under no obligation to forward one. The\n * attribute is on the element either way.\n *\n * @returns Nothing.\n */\n const focusTrigger = useCallback((): void => {\n rootRef.current?.querySelector<HTMLElement>(\"[aria-haspopup='menu']\")?.focus();\n }, []);\n\n const close = useCallback(\n (restoreFocus: boolean): void => {\n setOpen(false);\n setActiveIndex(-1);\n if (restoreFocus) focusTrigger();\n },\n [focusTrigger],\n );\n\n const focusIndex = useCallback((index: number): void => {\n setActiveIndex(index);\n itemRefs.current[index]?.focus();\n }, []);\n\n /**\n * Move focus to the entry `step` positions away, wrapping at both ends.\n *\n * Walks the selectable positions rather than the raw list, so separators,\n * labels and disabled entries are never a stop.\n */\n const move = (step: number): void => {\n if (selectable.length === 0) return;\n const current = selectable.indexOf(activeIndex);\n const next = (current + step + selectable.length) % selectable.length;\n focusIndex(selectable[next] ?? -1);\n };\n\n useEffect(() => {\n if (!open || pendingFocus.current === null) return;\n const target = pendingFocus.current === \"last\" ? selectable.at(-1) : selectable[0];\n pendingFocus.current = null;\n if (target !== undefined) focusIndex(target);\n }, [open, selectable, focusIndex]);\n\n useEffect(() => {\n if (!open) return;\n const onDown = (event: MouseEvent): void => {\n if (rootRef.current && !rootRef.current.contains(event.target as Node)) close(false);\n };\n window.addEventListener(\"mousedown\", onDown);\n return () => window.removeEventListener(\"mousedown\", onDown);\n }, [open, close]);\n\n const openWith = (edge: \"first\" | \"last\"): void => {\n pendingFocus.current = edge;\n setOpen(true);\n };\n\n const handleTriggerKeyDown = (event: ReactKeyboardEvent): void => {\n trigger.props.onKeyDown?.(event);\n if (event.defaultPrevented) return;\n if (event.key === \"ArrowDown\" || (!open && (event.key === \"Enter\" || event.key === \" \"))) {\n event.preventDefault();\n openWith(\"first\");\n return;\n }\n if (event.key === \"ArrowUp\") {\n event.preventDefault();\n openWith(\"last\");\n }\n };\n\n /**\n * Keyboard model of the open menu.\n *\n * Handled on the list rather than on `window`: focus is inside the menu by\n * then, so the keys arrive here by bubbling and cannot be pre-empted by an\n * unrelated global listener — which is how the arrows used to go missing in a\n * host application.\n */\n const handleMenuKeyDown = (event: ReactKeyboardEvent<HTMLUListElement>): void => {\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n move(1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n move(-1);\n break;\n case \"Home\":\n event.preventDefault();\n if (selectable[0] !== undefined) focusIndex(selectable[0]);\n break;\n case \"End\": {\n event.preventDefault();\n const last = selectable.at(-1);\n if (last !== undefined) focusIndex(last);\n break;\n }\n case \"Escape\":\n event.preventDefault();\n close(true);\n break;\n case \"Tab\":\n close(false);\n break;\n default:\n break;\n }\n };\n\n const handleTriggerClick = (event: React.MouseEvent): void => {\n trigger.props.onClick?.(event);\n if (event.defaultPrevented) return;\n if (open) {\n close(false);\n return;\n }\n openWith(\"first\");\n };\n\n const triggerClone = {\n ...trigger,\n props: {\n ...trigger.props,\n onClick: handleTriggerClick,\n onKeyDown: handleTriggerKeyDown,\n \"aria-expanded\": open,\n \"aria-controls\": id,\n \"aria-haspopup\": \"menu\" as const,\n },\n } as ReactElement;\n\n const handleSelect = (entry: SelectableEntry): void => {\n entry.onSelect();\n if (entry.type === \"checkbox\") return;\n close(true);\n };\n\n return (\n <span ref={rootRef} className={styles.root}>\n {triggerClone}\n {open && (\n <ul\n id={id}\n role=\"menu\"\n className={cn(styles.menu, placementClass(placement), className)}\n onKeyDown={handleMenuKeyDown}\n >\n {items.map((entry, index) => {\n if (entry.type === \"separator\") {\n return (\n <li\n key={entry.id}\n role=\"separator\"\n className={styles.separator}\n aria-hidden\n />\n );\n }\n if (entry.type === \"label\") {\n return (\n <li key={entry.id} role=\"presentation\" className={styles.label}>\n {entry.label}\n </li>\n );\n }\n const checkbox = entry.type === \"checkbox\";\n return (\n <li key={entry.id} role=\"none\">\n <button\n ref={(el) => {\n itemRefs.current[index] = el;\n }}\n type=\"button\"\n role={checkbox ? \"menuitemcheckbox\" : \"menuitem\"}\n aria-checked={checkbox ? entry.checked : undefined}\n tabIndex={activeIndex === index ? 0 : -1}\n className={cn(\n styles.item,\n entry.type === \"item\" && entry.danger && styles.danger,\n activeIndex === index && styles.active,\n )}\n disabled={entry.disabled}\n onClick={() => handleSelect(entry)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {entry.icon && <span aria-hidden>{entry.icon}</span>}\n {entry.label}\n </button>\n </li>\n );\n })}\n </ul>\n )}\n </span>\n );\n}\n"],"mappings":"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"}