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
@@ -11,90 +11,134 @@ function l(e) {
11
11
  default: return t.bottomStart;
12
12
  }
13
13
  }
14
- function u({ trigger: u, items: d, placement: f = "bottom-start", className: p }) {
15
- let [m, h] = o(!1), [g, _] = o(-1), v = i(), y = a(null), b = a([]), x = d.map((e, t) => e.type === "item" && !e.disabled ? t : -1).filter((e) => e !== -1), S = n(() => {
16
- h(!1), _(-1);
17
- }, []);
14
+ function u(e) {
15
+ return (e.type === "item" || e.type === "checkbox") && !e.disabled;
16
+ }
17
+ function d({ trigger: d, items: f, placement: p = "bottom-start", className: m }) {
18
+ let [h, g] = o(!1), [_, v] = o(-1), y = i(), b = a(null), x = a([]), S = a(null), C = f.map((e, t) => u(e) ? t : -1).filter((e) => e !== -1), w = n(() => {
19
+ b.current?.querySelector("[aria-haspopup='menu']")?.focus();
20
+ }, []), T = n((e) => {
21
+ g(!1), v(-1), e && w();
22
+ }, [w]), E = n((e) => {
23
+ v(e), x.current[e]?.focus();
24
+ }, []), D = (e) => {
25
+ if (C.length === 0) return;
26
+ let t = (C.indexOf(_) + e + C.length) % C.length;
27
+ E(C[t] ?? -1);
28
+ };
18
29
  r(() => {
19
- if (!m) return;
30
+ if (!h || S.current === null) return;
31
+ let e = S.current === "last" ? C.at(-1) : C[0];
32
+ S.current = null, e !== void 0 && E(e);
33
+ }, [
34
+ h,
35
+ C,
36
+ E
37
+ ]), r(() => {
38
+ if (!h) return;
20
39
  let e = (e) => {
21
- if (e.key === "Escape") {
22
- S();
40
+ b.current && !b.current.contains(e.target) && T(!1);
41
+ };
42
+ return window.addEventListener("mousedown", e), () => window.removeEventListener("mousedown", e);
43
+ }, [h, T]);
44
+ let O = (e) => {
45
+ S.current = e, g(!0);
46
+ }, k = (e) => {
47
+ if (d.props.onKeyDown?.(e), !e.defaultPrevented) {
48
+ if (e.key === "ArrowDown" || !h && (e.key === "Enter" || e.key === " ")) {
49
+ e.preventDefault(), O("first");
23
50
  return;
24
51
  }
25
- if (e.key === "ArrowDown") {
52
+ e.key === "ArrowUp" && (e.preventDefault(), O("last"));
53
+ }
54
+ }, A = (e) => {
55
+ switch (e.key) {
56
+ case "ArrowDown":
57
+ e.preventDefault(), D(1);
58
+ break;
59
+ case "ArrowUp":
60
+ e.preventDefault(), D(-1);
61
+ break;
62
+ case "Home":
63
+ e.preventDefault(), C[0] !== void 0 && E(C[0]);
64
+ break;
65
+ case "End": {
26
66
  e.preventDefault();
27
- let t = x.indexOf(g), n = x[(t + 1) % x.length] ?? -1;
28
- _(n), b.current[n]?.focus();
67
+ let t = C.at(-1);
68
+ t !== void 0 && E(t);
69
+ break;
29
70
  }
30
- if (e.key === "ArrowUp") {
31
- e.preventDefault();
32
- let t = x.indexOf(g), n = x[(t - 1 + x.length) % x.length] ?? -1;
33
- _(n), b.current[n]?.focus();
71
+ case "Escape":
72
+ e.preventDefault(), T(!0);
73
+ break;
74
+ case "Tab": T(!1);
75
+ }
76
+ }, j = (e) => {
77
+ if (d.props.onClick?.(e), !e.defaultPrevented) {
78
+ if (h) {
79
+ T(!1);
80
+ return;
34
81
  }
35
- }, t = (e) => {
36
- y.current && !y.current.contains(e.target) && S();
37
- };
38
- return window.addEventListener("keydown", e), window.addEventListener("mousedown", t), () => {
39
- window.removeEventListener("keydown", e), window.removeEventListener("mousedown", t);
40
- };
41
- }, [
42
- m,
43
- g,
44
- x,
45
- S
46
- ]);
47
- let C = (e) => {
48
- u.props.onClick?.(e), h((e) => !e);
49
- }, w = {
50
- ...u,
82
+ O("first");
83
+ }
84
+ }, M = {
85
+ ...d,
51
86
  props: {
52
- ...u.props,
53
- onClick: C,
54
- "aria-expanded": m,
55
- "aria-controls": v,
87
+ ...d.props,
88
+ onClick: j,
89
+ onKeyDown: k,
90
+ "aria-expanded": h,
91
+ "aria-controls": y,
56
92
  "aria-haspopup": "menu"
57
93
  }
58
- }, T = (e) => {
59
- e.onSelect(), S();
94
+ }, N = (e) => {
95
+ e.onSelect(), e.type !== "checkbox" && T(!0);
60
96
  };
61
97
  return /* @__PURE__ */ c("span", {
62
- ref: y,
98
+ ref: b,
63
99
  className: t.root,
64
- children: [w, m && /* @__PURE__ */ s("ul", {
65
- id: v,
100
+ children: [M, h && /* @__PURE__ */ s("ul", {
101
+ id: y,
66
102
  role: "menu",
67
- className: e(t.menu, l(f), p),
68
- children: d.map((n, r) => n.type === "separator" ? /* @__PURE__ */ s("li", {
69
- role: "separator",
70
- className: t.separator,
71
- "aria-hidden": !0
72
- }, n.id) : n.type === "label" ? /* @__PURE__ */ s("li", {
73
- role: "presentation",
74
- className: t.label,
75
- children: n.label
76
- }, n.id) : /* @__PURE__ */ s("li", {
77
- role: "none",
78
- children: /* @__PURE__ */ c("button", {
79
- ref: (e) => {
80
- b.current[r] = e;
81
- },
82
- type: "button",
83
- role: "menuitem",
84
- className: e(t.item, n.danger && t.danger, g === r && t.active),
85
- disabled: n.disabled,
86
- onClick: () => T(n),
87
- onMouseEnter: () => _(r),
88
- children: [n.icon && /* @__PURE__ */ s("span", {
89
- "aria-hidden": !0,
90
- children: n.icon
91
- }), n.label]
92
- })
93
- }, n.id))
103
+ className: e(t.menu, l(p), m),
104
+ onKeyDown: A,
105
+ children: f.map((n, r) => {
106
+ if (n.type === "separator") return /* @__PURE__ */ s("li", {
107
+ role: "separator",
108
+ className: t.separator,
109
+ "aria-hidden": !0
110
+ }, n.id);
111
+ if (n.type === "label") return /* @__PURE__ */ s("li", {
112
+ role: "presentation",
113
+ className: t.label,
114
+ children: n.label
115
+ }, n.id);
116
+ let i = n.type === "checkbox";
117
+ return /* @__PURE__ */ s("li", {
118
+ role: "none",
119
+ children: /* @__PURE__ */ c("button", {
120
+ ref: (e) => {
121
+ x.current[r] = e;
122
+ },
123
+ type: "button",
124
+ role: i ? "menuitemcheckbox" : "menuitem",
125
+ "aria-checked": i ? n.checked : void 0,
126
+ tabIndex: _ === r ? 0 : -1,
127
+ className: e(t.item, n.type === "item" && n.danger && t.danger, _ === r && t.active),
128
+ disabled: n.disabled,
129
+ onClick: () => N(n),
130
+ onMouseEnter: () => v(r),
131
+ children: [n.icon && /* @__PURE__ */ s("span", {
132
+ "aria-hidden": !0,
133
+ children: n.icon
134
+ }), n.label]
135
+ })
136
+ }, n.id);
137
+ })
94
138
  })]
95
139
  });
96
140
  }
97
141
  //#endregion
98
- export { u as DropdownMenu };
142
+ export { d as DropdownMenu };
99
143
 
100
144
  //# sourceMappingURL=DropdownMenu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DropdownMenu.js","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":";;;;;AAsCA,SAAS,EAAe,GAA0C;CAC9D,QAAQ,GAAR;EACI,KAAK,cACD,OAAO,EAAO;EAClB,KAAK,aACD,OAAO,EAAO;EAClB,KAAK,WACD,OAAO,EAAO;EAElB,SACI,OAAO,EAAO;CACtB;AACJ;AASA,SAAgB,EAAa,EACzB,YACA,UACA,eAAY,gBACZ,gBACkB;CAClB,IAAM,CAAC,GAAM,KAAW,EAAS,EAAK,GAChC,CAAC,GAAa,KAAkB,EAAiB,EAAE,GACnD,IAAK,EAAM,GACX,IAAU,EAAwB,IAAI,GACtC,IAAW,EAAwC,CAAC,CAAC,GAErD,IAAoB,EACrB,KAAK,GAAO,MAAW,EAAM,SAAS,UAAU,CAAC,EAAM,WAAW,IAAQ,EAAG,CAAC,CAC9E,QAAQ,MAAM,MAAM,EAAE,GAErB,IAAQ,QAAwB;EAElC,AADA,EAAQ,EAAK,GACb,EAAe,EAAE;CACrB,GAAG,CAAC,CAAC;CAEL,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,KAAS,MAA+B;GAC1C,IAAI,EAAM,QAAQ,UAAU;IACxB,EAAM;IACN;GACJ;GACA,IAAI,EAAM,QAAQ,aAAa;IAC3B,EAAM,eAAe;IACrB,IAAM,IAAU,EAAkB,QAAQ,CAAW,GAC/C,IAAO,GAAmB,IAAU,KAAK,EAAkB,WAAW;IAE5E,AADA,EAAe,CAAI,GACnB,EAAS,QAAQ,EAAK,EAAE,MAAM;GAClC;GACA,IAAI,EAAM,QAAQ,WAAW;IACzB,EAAM,eAAe;IACrB,IAAM,IAAU,EAAkB,QAAQ,CAAW,GAC/C,IACF,GACK,IAAU,IAAI,EAAkB,UAAU,EAAkB,WAC5D;IAET,AADA,EAAe,CAAI,GACnB,EAAS,QAAQ,EAAK,EAAE,MAAM;GAClC;EACJ,GACM,KAAU,MAA4B;GACxC,AAAI,EAAQ,WAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,MAAc,KAAG,EAAM;EAClF;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAK,GACxC,OAAO,iBAAiB,aAAa,CAAM,SAC9B;GAET,AADA,OAAO,oBAAoB,WAAW,CAAK,GAC3C,OAAO,oBAAoB,aAAa,CAAM;EAClD;CACJ,GAAG;EAAC;EAAM;EAAa;EAAmB;CAAK,CAAC;CAEhD,IAAM,KAAsB,MAAkC;EAE1D,AADA,EAAQ,MAAM,UAAU,CAAK,GAC7B,GAAS,MAAS,CAAC,CAAI;CAC3B,GAEM,IAAe;EACjB,GAAG;EACH,OAAO;GACH,GAAG,EAAQ;GACX,SAAS;GACT,iBAAiB;GACjB,iBAAiB;GACjB,iBAAiB;EACrB;CACJ,GAEM,KAAgB,MAA8D;EAEhF,AADA,EAAM,SAAS,GACf,EAAM;CACV;CAEA,OACI,kBAAC,QAAD;EAAM,KAAK;EAAS,WAAW,EAAO;EAAtC,UAAA,CACK,GACA,KACG,kBAAC,MAAD;GACQ;GACJ,MAAK;GACL,WAAW,EAAG,EAAO,MAAM,EAAe,CAAS,GAAG,CAAS;GAE9D,UAAA,EAAM,KAAK,GAAO,MACX,EAAM,SAAS,cAEX,kBAAC,MAAD;IAEI,MAAK;IACL,WAAW,EAAO;IAClB,eAAA;GACH,GAJQ,EAAM,EAId,IAGL,EAAM,SAAS,UAEX,kBAAC,MAAD;IAAmB,MAAK;IAAe,WAAW,EAAO;IACpD,UAAA,EAAM;GACP,GAFK,EAAM,EAEX,IAIR,kBAAC,MAAD;IAAmB,MAAK;IACpB,UAAA,kBAAC,UAAD;KACI,MAAM,MAAO;MACT,EAAS,QAAQ,KAAS;KAC9B;KACA,MAAK;KACL,MAAK;KACL,WAAW,EACP,EAAO,MACP,EAAM,UAAU,EAAO,QACvB,MAAgB,KAAS,EAAO,MACpC;KACA,UAAU,EAAM;KAChB,eAAe,EAAa,CAAK;KACjC,oBAAoB,EAAe,CAAK;KAb5C,UAAA,CAeK,EAAM,QAAQ,kBAAC,QAAD;MAAM,eAAA;MAAa,UAAA,EAAM;KAAW,CAAA,GAClD,EAAM,KACH;;GACR,GAnBK,EAAM,EAmBX,CAEX;EACD,CAAA,CAEN;;AAEd"}
1
+ {"version":3,"file":"DropdownMenu.js","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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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Modal.module.cjs");let n=require("react"),r=require("react/jsx-runtime"),i=require("react-dom");function a({open:a,onClose:c,title:l,children:u,footer:d,size:f=`md`,closeOnBackdrop:p=!0,closeOnEsc:m=!0,className:h,hideCloseButton:g=!1,fullscreen:_=!1,fullscreenOnMobile:v=!1,"aria-label":y}){let b=(0,n.useId)();return(0,n.useEffect)(()=>{if(!a)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)}},[a,m,c]),!a||typeof document>`u`?null:(0,i.createPortal)((0,r.jsx)(`div`,{className:t.default.overlay,role:`presentation`,onClick:()=>{p&&c()},children:(0,r.jsxs)(`div`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":l?b:void 0,"aria-label":l?void 0:y,className:e.cn(t.default.dialog,o(f),_&&t.default.fullscreen,v&&t.default.fullscreenOnMobile,h),onClick:e=>e.stopPropagation(),children:[(l||!g)&&(0,r.jsxs)(`header`,{className:t.default.header,children:[l&&(0,r.jsx)(`h3`,{id:b,className:t.default.title,children:l}),!g&&(0,r.jsx)(`button`,{type:`button`,className:t.default.close,"aria-label":`Fechar`,onClick:c,children:(0,r.jsx)(s,{})})]}),(0,r.jsx)(`div`,{className:t.default.body,children:u}),d&&(0,r.jsx)(`footer`,{className:t.default.footer,children:d})]})}),document.body)}function o(e){return e===`2xl`?t.default.size2xl:e===`3xl`?t.default.size3xl:t.default[e]}function s(){return(0,r.jsx)(`svg`,{width:`18`,height:`18`,viewBox:`0 0 24 24`,fill:`none`,children:(0,r.jsx)(`path`,{d:`M6 6l12 12M6 18L18 6`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`})})}exports.Modal=a;
1
+ const e=require("../../utils/cn.cjs"),t=require("../Portal/portal-host.cjs"),n=require("./Modal.module.cjs");let r=require("react"),i=require("react/jsx-runtime"),a=require("react-dom");function o({open:o,onClose:l,title:u,children:d,footer:f,size:p=`md`,closeOnBackdrop:m=!0,closeOnEsc:h=!0,className:g,hideCloseButton:_=!1,fullscreen:v=!1,fullscreenOnMobile:y=!1,"aria-label":b}){let x=(0,r.useId)();(0,r.useEffect)(()=>{if(!o)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)}},[o,h,l]);let S=t.usePortalHost();return!o||!S?null:(0,a.createPortal)((0,i.jsx)(`div`,{className:n.default.overlay,role:`presentation`,onClick:()=>{m&&l()},children:(0,i.jsxs)(`div`,{role:`dialog`,"aria-modal":`true`,"aria-labelledby":u?x:void 0,"aria-label":u?void 0:b,className:e.cn(n.default.dialog,s(p),v&&n.default.fullscreen,y&&n.default.fullscreenOnMobile,g),onClick:e=>e.stopPropagation(),children:[(u||!_)&&(0,i.jsxs)(`header`,{className:n.default.header,children:[u&&(0,i.jsx)(`h3`,{id:x,className:n.default.title,children:u}),!_&&(0,i.jsx)(`button`,{type:`button`,className:n.default.close,"aria-label":`Fechar`,onClick:l,children:(0,i.jsx)(c,{})})]}),(0,i.jsx)(`div`,{className:n.default.body,children:d}),f&&(0,i.jsx)(`footer`,{className:n.default.footer,children:f})]})}),S)}function s(e){return e===`2xl`?n.default.size2xl:e===`3xl`?n.default.size3xl:n.default[e]}function c(){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.Modal=o;
2
2
  //# sourceMappingURL=Modal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.cjs","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\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 <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\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 </div>\n </div>,\n document.body,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\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":"mJA8CA,SAAgB,EAAM,CAClB,OACA,UACA,QACA,WACA,SACA,OAAO,KACP,kBAAkB,GAClB,aAAa,GACb,YACA,kBAAkB,GAClB,aAAa,GACb,qBAAqB,GACrB,aAAc,GACH,CACX,IAAM,GAAA,EAAU,EAAA,MAAA,CAAM,EAmBtB,OAlBA,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAE/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAS,MAE/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,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,KAAK,eACL,YAAe,CACP,GAAiB,EAAQ,CACjC,EAEA,UAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,KAAK,SACL,aAAW,OACX,kBAAiB,EAAQ,EAAU,IAAA,GACnC,aAAY,EAAQ,IAAA,GAAY,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAc,CAAI,EAClB,GAAc,EAAA,QAAO,WACrB,GAAsB,EAAA,QAAO,mBAC7B,CACJ,EACA,QAAU,GAAU,EAAM,gBAAgB,EAZ9C,SAAA,EAcM,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,UAAW,EAAA,QAAO,MAC9B,SAAA,CACD,CAAA,EAEP,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAW,SACX,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,CAC5D,GACJ,CAAA,EACL,SAAS,IACb,CACJ,CAEA,SAAS,EAAc,EAAqC,CAGxD,OAFI,IAAS,MAAc,EAAA,QAAO,QAC9B,IAAS,MAAc,EAAA,QAAO,QAC3B,EAAA,QAAO,EAClB,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":"Modal.cjs","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\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 <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\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 </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\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":"0LA+CA,SAAgB,EAAM,CAClB,OACA,UACA,QACA,WACA,SACA,OAAO,KACP,kBAAkB,GAClB,aAAa,GACb,YACA,kBAAkB,GAClB,aAAa,GACb,qBAAqB,GACrB,aAAc,GACH,CACX,IAAM,GAAA,EAAU,EAAA,MAAA,CAAM,GACtB,EAAA,EAAA,UAAA,KAAgB,CACZ,GAAI,CAAC,EAAM,OACX,IAAM,EAAmB,SAAS,KAAK,MAAM,SAC7C,SAAS,KAAK,MAAM,SAAW,SAE/B,IAAM,EAAa,GAA+B,CAC1C,GAAc,EAAM,MAAQ,UAAU,EAAQ,CACtD,EAGA,OAFA,OAAO,iBAAiB,UAAW,CAAS,MAE/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,IAAA,CAAC,MAAD,CACI,UAAW,EAAA,QAAO,QAClB,KAAK,eACL,YAAe,CACP,GAAiB,EAAQ,CACjC,EAEA,UAAA,EAAA,EAAA,KAAA,CAAC,MAAD,CACI,KAAK,SACL,aAAW,OACX,kBAAiB,EAAQ,EAAU,IAAA,GACnC,aAAY,EAAQ,IAAA,GAAY,EAChC,UAAW,EAAA,GACP,EAAA,QAAO,OACP,EAAc,CAAI,EAClB,GAAc,EAAA,QAAO,WACrB,GAAsB,EAAA,QAAO,mBAC7B,CACJ,EACA,QAAU,GAAU,EAAM,gBAAgB,EAZ9C,SAAA,EAcM,GAAS,CAAC,KACR,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAA,QAAO,OAA1B,SAAA,CACK,IACG,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,GAAI,EAAS,UAAW,EAAA,QAAO,MAC9B,SAAA,CACD,CAAA,EAEP,CAAC,IACE,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAW,SACX,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,CAC5D,GACJ,CAAA,EACL,CACJ,CACJ,CAEA,SAAS,EAAc,EAAqC,CAGxD,OAFI,IAAS,MAAc,EAAA,QAAO,QAC9B,IAAS,MAAc,EAAA,QAAO,QAC3B,EAAA,QAAO,EAClB,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,75 +1,78 @@
1
1
  import { cn as e } from "../../utils/cn.js";
2
- import t from "./Modal.module.js";
3
- import { useEffect as n, useId as r } from "react";
4
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
- import { createPortal as o } from "react-dom";
2
+ import { usePortalHost as t } from "../Portal/portal-host.js";
3
+ import n from "./Modal.module.js";
4
+ import { useEffect as r, useId as i } from "react";
5
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
+ import { createPortal as s } from "react-dom";
6
7
  //#region src/components/Modal/Modal.tsx
7
- function s({ open: s, onClose: u, title: d, children: f, footer: p, size: m = "md", closeOnBackdrop: h = !0, closeOnEsc: g = !0, className: _, hideCloseButton: v = !1, fullscreen: y = !1, fullscreenOnMobile: b = !1, "aria-label": x }) {
8
- let S = r();
9
- return n(() => {
10
- if (!s) return;
8
+ function c({ open: c, onClose: d, title: f, children: p, footer: m, size: h = "md", closeOnBackdrop: g = !0, closeOnEsc: _ = !0, className: v, hideCloseButton: y = !1, fullscreen: b = !1, fullscreenOnMobile: x = !1, "aria-label": S }) {
9
+ let C = i();
10
+ r(() => {
11
+ if (!c) return;
11
12
  let e = document.body.style.overflow;
12
13
  document.body.style.overflow = "hidden";
13
14
  let t = (e) => {
14
- g && e.key === "Escape" && u();
15
+ _ && e.key === "Escape" && d();
15
16
  };
16
17
  return window.addEventListener("keydown", t), () => {
17
18
  document.body.style.overflow = e, window.removeEventListener("keydown", t);
18
19
  };
19
20
  }, [
20
- s,
21
- g,
22
- u
23
- ]), !s || typeof document > "u" ? null : o(/* @__PURE__ */ i("div", {
24
- className: t.overlay,
21
+ c,
22
+ _,
23
+ d
24
+ ]);
25
+ let w = t();
26
+ return !c || !w ? null : s(/* @__PURE__ */ a("div", {
27
+ className: n.overlay,
25
28
  role: "presentation",
26
29
  onClick: () => {
27
- h && u();
30
+ g && d();
28
31
  },
29
- children: /* @__PURE__ */ a("div", {
32
+ children: /* @__PURE__ */ o("div", {
30
33
  role: "dialog",
31
34
  "aria-modal": "true",
32
- "aria-labelledby": d ? S : void 0,
33
- "aria-label": d ? void 0 : x,
34
- className: e(t.dialog, c(m), y && t.fullscreen, b && t.fullscreenOnMobile, _),
35
+ "aria-labelledby": f ? C : void 0,
36
+ "aria-label": f ? void 0 : S,
37
+ className: e(n.dialog, l(h), b && n.fullscreen, x && n.fullscreenOnMobile, v),
35
38
  onClick: (e) => e.stopPropagation(),
36
39
  children: [
37
- (d || !v) && /* @__PURE__ */ a("header", {
38
- className: t.header,
39
- children: [d && /* @__PURE__ */ i("h3", {
40
- id: S,
41
- className: t.title,
42
- children: d
43
- }), !v && /* @__PURE__ */ i("button", {
40
+ (f || !y) && /* @__PURE__ */ o("header", {
41
+ className: n.header,
42
+ children: [f && /* @__PURE__ */ a("h3", {
43
+ id: C,
44
+ className: n.title,
45
+ children: f
46
+ }), !y && /* @__PURE__ */ a("button", {
44
47
  type: "button",
45
- className: t.close,
48
+ className: n.close,
46
49
  "aria-label": "Fechar",
47
- onClick: u,
48
- children: /* @__PURE__ */ i(l, {})
50
+ onClick: d,
51
+ children: /* @__PURE__ */ a(u, {})
49
52
  })]
50
53
  }),
51
- /* @__PURE__ */ i("div", {
52
- className: t.body,
53
- children: f
54
- }),
55
- p && /* @__PURE__ */ i("footer", {
56
- className: t.footer,
54
+ /* @__PURE__ */ a("div", {
55
+ className: n.body,
57
56
  children: p
57
+ }),
58
+ m && /* @__PURE__ */ a("footer", {
59
+ className: n.footer,
60
+ children: m
58
61
  })
59
62
  ]
60
63
  })
61
- }), document.body);
64
+ }), w);
62
65
  }
63
- function c(e) {
64
- return e === "2xl" ? t.size2xl : e === "3xl" ? t.size3xl : t[e];
66
+ function l(e) {
67
+ return e === "2xl" ? n.size2xl : e === "3xl" ? n.size3xl : n[e];
65
68
  }
66
- function l() {
67
- return /* @__PURE__ */ i("svg", {
69
+ function u() {
70
+ return /* @__PURE__ */ a("svg", {
68
71
  width: "18",
69
72
  height: "18",
70
73
  viewBox: "0 0 24 24",
71
74
  fill: "none",
72
- children: /* @__PURE__ */ i("path", {
75
+ children: /* @__PURE__ */ a("path", {
73
76
  d: "M6 6l12 12M6 18L18 6",
74
77
  stroke: "currentColor",
75
78
  strokeWidth: "2",
@@ -78,6 +81,6 @@ function l() {
78
81
  });
79
82
  }
80
83
  //#endregion
81
- export { s as Modal };
84
+ export { c as Modal };
82
85
 
83
86
  //# sourceMappingURL=Modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\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 <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\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 </div>\n </div>,\n document.body,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\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":";;;;;;AA8CA,SAAgB,EAAM,EAClB,SACA,YACA,UACA,aACA,WACA,UAAO,MACP,qBAAkB,IAClB,gBAAa,IACb,cACA,qBAAkB,IAClB,gBAAa,IACb,wBAAqB,IACrB,cAAc,KACH;CACX,IAAM,IAAU,EAAM;CAmBtB,OAlBA,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAE/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAS,SAE/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,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,MAAK;EACL,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;EAEA,UAAA,kBAAC,OAAD;GACI,MAAK;GACL,cAAW;GACX,mBAAiB,IAAQ,IAAU,KAAA;GACnC,cAAY,IAAQ,KAAA,IAAY;GAChC,WAAW,EACP,EAAO,QACP,EAAc,CAAI,GAClB,KAAc,EAAO,YACrB,KAAsB,EAAO,oBAC7B,CACJ;GACA,UAAU,MAAU,EAAM,gBAAgB;GAZ9C,UAAA;KAcM,KAAS,CAAC,MACR,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAA1B,UAAA,CACK,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,WAAW,EAAO;MAC9B,UAAA;KACD,CAAA,GAEP,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,cAAW;MACX,SAAS;MAET,UAAA,kBAAC,GAAD,CAAY,CAAA;KACR,CAAA,CAER;;IAEZ,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;IAC3C,KAAU,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAAS,UAAA;IAAe,CAAA;GAC5D;;CACJ,CAAA,GACL,SAAS,IACb;AACJ;AAEA,SAAS,EAAc,GAAqC;CAGxD,OAFI,MAAS,QAAc,EAAO,UAC9B,MAAS,QAAc,EAAO,UAC3B,EAAO;AAClB;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":"Modal.js","names":[],"sources":["../../../src/components/Modal/Modal.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count, function-lines — size, fullscreen and\n * fullscreenOnMobile are three distinct decisions (a fixed width, always full, full\n * only on a phone), and the rest is the dialog contract: open, onClose, title,\n * children, footer, closeOnBackdrop, closeOnEsc, hideCloseButton. The body is the\n * focus trap and the scroll lock, which must be installed and torn down together.\n */\nimport { useEffect, useId } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Modal.module.css\";\nimport { usePortalHost } from \"../Portal/portal-host\";\n\nexport type ModalSize = \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\" | \"3xl\";\n\nexport interface ModalProps {\n open: boolean;\n onClose: () => void;\n title?: ReactNode;\n children?: ReactNode;\n footer?: ReactNode;\n size?: ModalSize;\n closeOnBackdrop?: boolean;\n closeOnEsc?: boolean;\n className?: string;\n hideCloseButton?: boolean;\n /** Force fullscreen at all breakpoints. */\n fullscreen?: boolean;\n /** Auto-fullscreen on mobile viewports (< 640px). Default `false`. */\n fullscreenOnMobile?: boolean;\n /**\n * Accessible name for the dialog. Only needed when there is no `title` —\n * with a `title` the dialog is named by it through `aria-labelledby`.\n */\n \"aria-label\"?: string;\n}\n\n/**\n * Portal-rendered modal dialog with backdrop, Esc handler, and slots for\n * header/body/footer. Locks body scroll while open.\n *\n * The `dialog` role needs an accessible name: a `title` supplies it via\n * `aria-labelledby`, and a titleless dialog should pass `aria-label`. The\n * header heading is only rendered when there is a title, so a close-button-only\n * header does not leave an empty `h3` behind.\n */\nexport function Modal({\n open,\n onClose,\n title,\n children,\n footer,\n size = \"md\",\n closeOnBackdrop = true,\n closeOnEsc = true,\n className,\n hideCloseButton = false,\n fullscreen = false,\n fullscreenOnMobile = false,\n \"aria-label\": ariaLabel,\n}: ModalProps) {\n const titleId = useId();\n useEffect(() => {\n if (!open) return;\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n const handleKey = (event: KeyboardEvent): void => {\n if (closeOnEsc && event.key === \"Escape\") onClose();\n };\n window.addEventListener(\"keydown\", handleKey);\n\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 <div\n className={styles.overlay}\n role=\"presentation\"\n onClick={() => {\n if (closeOnBackdrop) onClose();\n }}\n >\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-label={title ? undefined : ariaLabel}\n className={cn(\n styles.dialog,\n sizeClassName(size),\n fullscreen && styles.fullscreen,\n fullscreenOnMobile && styles.fullscreenOnMobile,\n className,\n )}\n onClick={(event) => event.stopPropagation()}\n >\n {(title || !hideCloseButton) && (\n <header className={styles.header}>\n {title && (\n <h3 id={titleId} className={styles.title}>\n {title}\n </h3>\n )}\n {!hideCloseButton && (\n <button\n type=\"button\"\n className={styles.close}\n aria-label=\"Fechar\"\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 </div>\n </div>,\n portalHost,\n );\n}\n\nfunction sizeClassName(size: ModalSize): string | undefined {\n if (size === \"2xl\") return styles.size2xl;\n if (size === \"3xl\") return styles.size3xl;\n return styles[size];\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":";;;;;;;AA+CA,SAAgB,EAAM,EAClB,SACA,YACA,UACA,aACA,WACA,UAAO,MACP,qBAAkB,IAClB,gBAAa,IACb,cACA,qBAAkB,IAClB,gBAAa,IACb,wBAAqB,IACrB,cAAc,KACH;CACX,IAAM,IAAU,EAAM;CACtB,QAAgB;EACZ,IAAI,CAAC,GAAM;EACX,IAAM,IAAmB,SAAS,KAAK,MAAM;EAC7C,SAAS,KAAK,MAAM,WAAW;EAE/B,IAAM,KAAa,MAA+B;GAC9C,AAAI,KAAc,EAAM,QAAQ,YAAU,EAAQ;EACtD;EAGA,OAFA,OAAO,iBAAiB,WAAW,CAAS,SAE/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,kBAAC,OAAD;EACI,WAAW,EAAO;EAClB,MAAK;EACL,eAAe;GACX,AAAI,KAAiB,EAAQ;EACjC;EAEA,UAAA,kBAAC,OAAD;GACI,MAAK;GACL,cAAW;GACX,mBAAiB,IAAQ,IAAU,KAAA;GACnC,cAAY,IAAQ,KAAA,IAAY;GAChC,WAAW,EACP,EAAO,QACP,EAAc,CAAI,GAClB,KAAc,EAAO,YACrB,KAAsB,EAAO,oBAC7B,CACJ;GACA,UAAU,MAAU,EAAM,gBAAgB;GAZ9C,UAAA;KAcM,KAAS,CAAC,MACR,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAA1B,UAAA,CACK,KACG,kBAAC,MAAD;MAAI,IAAI;MAAS,WAAW,EAAO;MAC9B,UAAA;KACD,CAAA,GAEP,CAAC,KACE,kBAAC,UAAD;MACI,MAAK;MACL,WAAW,EAAO;MAClB,cAAW;MACX,SAAS;MAET,UAAA,kBAAC,GAAD,CAAY,CAAA;KACR,CAAA,CAER;;IAEZ,kBAAC,OAAD;KAAK,WAAW,EAAO;KAAO;IAAc,CAAA;IAC3C,KAAU,kBAAC,UAAD;KAAQ,WAAW,EAAO;KAAS,UAAA;IAAe,CAAA;GAC5D;;CACJ,CAAA,GACL,CACJ;AACJ;AAEA,SAAS,EAAc,GAAqC;CAGxD,OAFI,MAAS,QAAc,EAAO,UAC9B,MAAS,QAAc,EAAO,UAC3B,EAAO;AAClB;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
- let e=require("react/jsx-runtime");function t({cents:t,currency:n=`BRL`,locale:r=`pt-BR`,...i}){let a=new Intl.NumberFormat(r,{style:`currency`,currency:n}).format(t/100);return(0,e.jsx)(`span`,{...i,children:a})}exports.Money=t;
1
+ const e=require("../../utils/intl-cache.cjs");let t=require("react/jsx-runtime");function n({cents:n,currency:r=`BRL`,locale:i=`pt-BR`,...a}){let o=e.numberFormat(i,{style:`currency`,currency:r}).format(n/100);return(0,t.jsx)(`span`,{...a,children:o})}exports.Money=n;
2
2
  //# sourceMappingURL=Money.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Money.cjs","names":[],"sources":["../../../src/components/Money/Money.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\n\nexport interface MoneyProps extends HTMLAttributes<HTMLSpanElement> {\n /** Amount in the smallest currency unit (e.g. cents). */\n cents: number;\n /** ISO 4217 currency code. Defaults to \"BRL\". */\n currency?: string;\n /** BCP 47 locale used for formatting. Defaults to \"pt-BR\". */\n locale?: string;\n}\n\n/**\n * Render a monetary amount given in cents as a localized currency string\n * inside a `<span>`.\n *\n * The integer cents are divided by 100 and formatted with `Intl.NumberFormat`.\n *\n * @example\n * <Money cents={1990} /> // \"R$ 19,90\"\n * <Money cents={500} currency=\"USD\" locale=\"en-US\" /> // \"$5.00\"\n */\nexport function Money({ cents, currency = \"BRL\", locale = \"pt-BR\", ...props }: MoneyProps) {\n const formatted = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n }).format(cents / 100);\n\n return <span {...props}>{formatted}</span>;\n}\n"],"mappings":"mCAqBA,SAAgB,EAAM,CAAE,QAAO,WAAW,MAAO,SAAS,QAAS,GAAG,GAAqB,CACvF,IAAM,EAAY,IAAI,KAAK,aAAa,EAAQ,CAC5C,MAAO,WACP,UACJ,CAAC,CAAC,CAAC,OAAO,EAAQ,GAAG,EAErB,OAAO,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,GAAI,EAAQ,SAAA,CAAgB,CAAA,CAC7C"}
1
+ {"version":3,"file":"Money.cjs","names":[],"sources":["../../../src/components/Money/Money.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { numberFormat } from \"@/utils/intl-cache\";\n\nexport interface MoneyProps extends HTMLAttributes<HTMLSpanElement> {\n /** Amount in the smallest currency unit (e.g. cents). */\n cents: number;\n /** ISO 4217 currency code. Defaults to \"BRL\". */\n currency?: string;\n /** BCP 47 locale used for formatting. Defaults to \"pt-BR\". */\n locale?: string;\n}\n\n/**\n * Render a monetary amount given in cents as a localized currency string\n * inside a `<span>`.\n *\n * The integer cents are divided by 100 and formatted with `Intl.NumberFormat`.\n *\n * @example\n * <Money cents={1990} /> // \"R$ 19,90\"\n * <Money cents={500} currency=\"USD\" locale=\"en-US\" /> // \"$5.00\"\n */\nexport function Money({ cents, currency = \"BRL\", locale = \"pt-BR\", ...props }: MoneyProps) {\n const formatted = numberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n\n return <span {...props}>{formatted}</span>;\n}\n"],"mappings":"iFAsBA,SAAgB,EAAM,CAAE,QAAO,WAAW,MAAO,SAAS,QAAS,GAAG,GAAqB,CACvF,IAAM,EAAY,EAAA,aAAa,EAAQ,CAAE,MAAO,WAAY,UAAS,CAAC,CAAC,CAAC,OAAO,EAAQ,GAAG,EAE1F,OAAO,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,GAAI,EAAQ,SAAA,CAAgB,CAAA,CAC7C"}
@@ -1,16 +1,17 @@
1
- import { jsx as e } from "react/jsx-runtime";
1
+ import { numberFormat as e } from "../../utils/intl-cache.js";
2
+ import { jsx as t } from "react/jsx-runtime";
2
3
  //#region src/components/Money/Money.tsx
3
- function t({ cents: t, currency: n = "BRL", locale: r = "pt-BR", ...i }) {
4
- let a = new Intl.NumberFormat(r, {
4
+ function n({ cents: n, currency: r = "BRL", locale: i = "pt-BR", ...a }) {
5
+ let o = e(i, {
5
6
  style: "currency",
6
- currency: n
7
- }).format(t / 100);
8
- return /* @__PURE__ */ e("span", {
9
- ...i,
10
- children: a
7
+ currency: r
8
+ }).format(n / 100);
9
+ return /* @__PURE__ */ t("span", {
10
+ ...a,
11
+ children: o
11
12
  });
12
13
  }
13
14
  //#endregion
14
- export { t as Money };
15
+ export { n as Money };
15
16
 
16
17
  //# sourceMappingURL=Money.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Money.js","names":[],"sources":["../../../src/components/Money/Money.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\n\nexport interface MoneyProps extends HTMLAttributes<HTMLSpanElement> {\n /** Amount in the smallest currency unit (e.g. cents). */\n cents: number;\n /** ISO 4217 currency code. Defaults to \"BRL\". */\n currency?: string;\n /** BCP 47 locale used for formatting. Defaults to \"pt-BR\". */\n locale?: string;\n}\n\n/**\n * Render a monetary amount given in cents as a localized currency string\n * inside a `<span>`.\n *\n * The integer cents are divided by 100 and formatted with `Intl.NumberFormat`.\n *\n * @example\n * <Money cents={1990} /> // \"R$ 19,90\"\n * <Money cents={500} currency=\"USD\" locale=\"en-US\" /> // \"$5.00\"\n */\nexport function Money({ cents, currency = \"BRL\", locale = \"pt-BR\", ...props }: MoneyProps) {\n const formatted = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n }).format(cents / 100);\n\n return <span {...props}>{formatted}</span>;\n}\n"],"mappings":";;AAqBA,SAAgB,EAAM,EAAE,UAAO,cAAW,OAAO,YAAS,SAAS,GAAG,KAAqB;CACvF,IAAM,IAAY,IAAI,KAAK,aAAa,GAAQ;EAC5C,OAAO;EACP;CACJ,CAAC,CAAC,CAAC,OAAO,IAAQ,GAAG;CAErB,OAAO,kBAAC,QAAD;EAAM,GAAI;EAAQ,UAAA;CAAgB,CAAA;AAC7C"}
1
+ {"version":3,"file":"Money.js","names":[],"sources":["../../../src/components/Money/Money.tsx"],"sourcesContent":["import type { HTMLAttributes } from \"react\";\nimport { numberFormat } from \"@/utils/intl-cache\";\n\nexport interface MoneyProps extends HTMLAttributes<HTMLSpanElement> {\n /** Amount in the smallest currency unit (e.g. cents). */\n cents: number;\n /** ISO 4217 currency code. Defaults to \"BRL\". */\n currency?: string;\n /** BCP 47 locale used for formatting. Defaults to \"pt-BR\". */\n locale?: string;\n}\n\n/**\n * Render a monetary amount given in cents as a localized currency string\n * inside a `<span>`.\n *\n * The integer cents are divided by 100 and formatted with `Intl.NumberFormat`.\n *\n * @example\n * <Money cents={1990} /> // \"R$ 19,90\"\n * <Money cents={500} currency=\"USD\" locale=\"en-US\" /> // \"$5.00\"\n */\nexport function Money({ cents, currency = \"BRL\", locale = \"pt-BR\", ...props }: MoneyProps) {\n const formatted = numberFormat(locale, { style: \"currency\", currency }).format(cents / 100);\n\n return <span {...props}>{formatted}</span>;\n}\n"],"mappings":";;;AAsBA,SAAgB,EAAM,EAAE,UAAO,cAAW,OAAO,YAAS,SAAS,GAAG,KAAqB;CACvF,IAAM,IAAY,EAAa,GAAQ;EAAE,OAAO;EAAY;CAAS,CAAC,CAAC,CAAC,OAAO,IAAQ,GAAG;CAE1F,OAAO,kBAAC,QAAD;EAAM,GAAI;EAAQ,UAAA;CAAgB,CAAA;AAC7C"}
@@ -1,2 +1,2 @@
1
- const e=require("../../utils/cn.cjs"),t=require("./Pagination.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e,t,n){if(t<=n+4)return Array.from({length:t},(e,t)=>t+1);let r=Math.max(2,e-Math.floor(n/2)),i=Math.min(t-1,r+n-1),a=[1];r>2&&a.push(`...`);for(let e=r;e<=i;e++)a.push(e);return i<t-1&&a.push(`...`),a.push(t),a}function a({page:a,totalPages:o,onPageChange:s,pageSize:c,onPageSizeChange:l,pageSizeOptions:u=[10,25,50,100],totalItems:d,siblingCount:f=3,className:p}){let m=(0,n.useMemo)(()=>i(a,o,f),[a,o,f]);return o<=1&&!l?null:(0,r.jsxs)(`div`,{className:e.cn(t.default.wrapper,p),children:[(0,r.jsx)(`div`,{className:t.default.summary,children:typeof d==`number`?`${d} resultado${d===1?``:`s`}`:`Página ${a} de ${o}`}),(0,r.jsxs)(`div`,{className:t.default.controls,children:[(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>s(Math.max(1,a-1)),disabled:a<=1,"aria-label":`Página anterior`,children:`‹`}),m.map((n,i)=>n===`...`?(0,r.jsx)(`span`,{className:e.cn(t.default.ellipsis,t.default.numeric),children:`…`},`ellipsis-${i}`):(0,r.jsx)(`button`,{type:`button`,className:e.cn(t.default.page,t.default.numeric,n===a&&t.default.active),onClick:()=>s(n),"aria-current":n===a?`page`:void 0,children:n},n)),(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>s(Math.min(o,a+1)),disabled:a>=o,"aria-label":`Próxima página`,children:`›`}),l&&(0,r.jsx)(`select`,{className:t.default.sizeSelect,value:c,onChange:e=>l(Number(e.target.value)),"aria-label":`Itens por página`,children:u.map(e=>(0,r.jsxs)(`option`,{value:e,children:[e,` / página`]},e))})]})]})}exports.Pagination=a;
1
+ const e=require("../../utils/cn.cjs"),t=require("./Pagination.module.cjs");let n=require("react"),r=require("react/jsx-runtime");function i(e,t,n){if(t<=n+4)return Array.from({length:t},(e,t)=>t+1);let r=Math.max(2,e-Math.floor(n/2)),i=Math.min(t-1,r+n-1),a=[1];r>2&&a.push(`...`);for(let e=r;e<=i;e++)a.push(e);return i<t-1&&a.push(`...`),a.push(t),a}function a({page:a,totalPages:o,onPageChange:s,pageSize:c,onPageSizeChange:l,pageSizeOptions:u=[10,25,50,100],totalItems:d,siblingCount:f=3,compactOnMobile:p=!0,className:m}){let h=(0,n.useMemo)(()=>i(a,o,f),[a,o,f]),g=(0,n.useRef)(null);return(0,n.useEffect)(()=>{p||g.current?.scrollIntoView?.({block:`nearest`,inline:`nearest`})},[a,p]),o<=1&&!l?null:(0,r.jsxs)(`div`,{className:e.cn(t.default.wrapper,m),"data-compact":String(p),children:[(0,r.jsx)(`div`,{className:t.default.summary,children:typeof d==`number`?`${d} resultado${d===1?``:`s`}`:`Página ${a} de ${o}`}),(0,r.jsxs)(`div`,{className:t.default.controls,children:[(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>s(Math.max(1,a-1)),disabled:a<=1,"aria-label":`Página anterior`,children:`‹`}),h.map((n,i)=>n===`...`?(0,r.jsx)(`span`,{className:e.cn(t.default.ellipsis,t.default.numeric),children:`…`},`ellipsis-${i}`):(0,r.jsx)(`button`,{type:`button`,ref:n===a?g:void 0,className:e.cn(t.default.page,t.default.numeric,n===a&&t.default.active),onClick:()=>s(n),"aria-current":n===a?`page`:void 0,children:n},n)),(0,r.jsx)(`button`,{type:`button`,className:t.default.page,onClick:()=>s(Math.min(o,a+1)),disabled:a>=o,"aria-label":`Próxima página`,children:`›`}),l&&(0,r.jsx)(`select`,{className:t.default.sizeSelect,value:c,onChange:e=>l(Number(e.target.value)),"aria-label":`Itens por página`,children:u.map(e=>(0,r.jsxs)(`option`,{value:e,children:[e,` / página`]},e))})]})]})}exports.Pagination=a;
2
2
  //# sourceMappingURL=Pagination.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Pagination.cjs","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useMemo } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Max number of numbered page buttons to show. Default 7. */\n siblingCount?: number;\n className?: string;\n}\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions = [10, 25, 50, 100],\n totalItems,\n siblingCount = 3,\n className,\n}: PaginationProps) {\n const pages = useMemo(\n () => buildRange(page, totalPages, siblingCount),\n [page, totalPages, siblingCount],\n );\n\n if (totalPages <= 1 && !onPageSizeChange) return null;\n\n return (\n <div className={cn(styles.wrapper, className)}>\n <div className={styles.summary}>\n {typeof totalItems === \"number\"\n ? `${totalItems} resultado${totalItems === 1 ? \"\" : \"s\"}`\n : `Página ${page} de ${totalPages}`}\n </div>\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n aria-label=\"Página anterior\"\n >\n ‹\n </button>\n {pages.map((entry, index) =>\n entry === \"...\" ? (\n <span\n key={`ellipsis-${index}`}\n className={cn(styles.ellipsis, styles.numeric)}\n >\n …\n </span>\n ) : (\n <button\n type=\"button\"\n key={entry}\n className={cn(\n styles.page,\n styles.numeric,\n entry === page && styles.active,\n )}\n onClick={() => onPageChange(entry)}\n aria-current={entry === page ? \"page\" : undefined}\n >\n {entry}\n </button>\n ),\n )}\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n aria-label=\"Próxima página\"\n >\n ›\n </button>\n {onPageSizeChange && (\n <select\n className={styles.sizeSelect}\n value={pageSize}\n onChange={(event) => onPageSizeChange(Number(event.target.value))}\n aria-label=\"Itens por página\"\n >\n {pageSizeOptions.map((opt) => (\n <option key={opt} value={opt}>\n {opt} / página\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"iIAyBA,SAAS,EAAW,EAAc,EAAoB,EAAsC,CAExF,GAAI,GADe,EAAW,EAE1B,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAW,GAAI,EAAG,IAAM,EAAI,CAAC,EAE7D,IAAM,EAAQ,KAAK,IAAI,EAAG,EAAO,KAAK,MAAM,EAAW,CAAC,CAAC,EACnD,EAAM,KAAK,IAAI,EAAa,EAAG,EAAQ,EAAW,CAAC,EACnD,EAA4B,CAAC,CAAC,EAChC,EAAQ,GAAG,EAAM,KAAK,KAAK,EAC/B,IAAK,IAAI,EAAI,EAAO,GAAK,EAAK,IAAK,EAAM,KAAK,CAAC,EAG/C,OAFI,EAAM,EAAa,GAAG,EAAM,KAAK,KAAK,EAC1C,EAAM,KAAK,CAAU,EACd,CACX,CAKA,SAAgB,EAAW,CACvB,OACA,aACA,eACA,WACA,mBACA,kBAAkB,CAAC,GAAI,GAAI,GAAI,GAAG,EAClC,aACA,eAAe,EACf,aACgB,CAChB,IAAM,GAAA,EAAQ,EAAA,QAAA,KACJ,EAAW,EAAM,EAAY,CAAY,EAC/C,CAAC,EAAM,EAAY,CAAY,CACnC,EAIA,OAFI,GAAc,GAAK,CAAC,EAAyB,MAG7C,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAA5C,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAClB,SAAA,OAAO,GAAe,SACjB,GAAG,EAAW,YAAY,IAAe,EAAI,GAAK,MAClD,UAAU,EAAK,MAAM,GAC1B,CAAA,GACL,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,SAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,KAClB,YAAe,EAAa,KAAK,IAAI,EAAG,EAAO,CAAC,CAAC,EACjD,SAAU,GAAQ,EAClB,aAAW,kBACd,SAAA,GAEO,CAAA,EACP,EAAM,KAAK,EAAO,IACf,IAAU,OACN,EAAA,EAAA,IAAA,CAAC,OAAD,CAEI,UAAW,EAAA,GAAG,EAAA,QAAO,SAAU,EAAA,QAAO,OAAO,EAChD,SAAA,GAEK,EAJG,YAAY,GAIf,GAEN,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SAEL,UAAW,EAAA,GACP,EAAA,QAAO,KACP,EAAA,QAAO,QACP,IAAU,GAAQ,EAAA,QAAO,MAC7B,EACA,YAAe,EAAa,CAAK,EACjC,eAAc,IAAU,EAAO,OAAS,IAAA,GAEvC,SAAA,CACG,EAVC,CAUD,CAEhB,GACA,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,KAClB,YAAe,EAAa,KAAK,IAAI,EAAY,EAAO,CAAC,CAAC,EAC1D,SAAU,GAAQ,EAClB,aAAW,iBACd,SAAA,GAEO,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,UAAW,EAAA,QAAO,WAClB,MAAO,EACP,SAAW,GAAU,EAAiB,OAAO,EAAM,OAAO,KAAK,CAAC,EAChE,aAAW,mBAEV,SAAA,EAAgB,IAAK,IAClB,EAAA,EAAA,KAAA,CAAC,SAAD,CAAkB,MAAO,EAAzB,SAAA,CACK,EAAI,WACD,CAFK,EAAA,CAEL,CACX,CACG,CAAA,CAEX,CACJ,CAAA,CAAA,GAEb"}
1
+ {"version":3,"file":"Pagination.cjs","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n page: number;\n totalPages: number;\n onPageChange: (page: number) => void;\n pageSize?: number;\n onPageSizeChange?: (size: number) => void;\n pageSizeOptions?: number[];\n /** Total items count; if provided, renders the summary text. */\n totalItems?: number;\n /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n siblingCount?: number;\n /**\n * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n * buttons and the size select. Default `true`.\n *\n * The default suits a desktop-first app, and costs the thing pagination is\n * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n * every width — the row scrolls horizontally by itself rather than pushing\n * the page wider, and the current page is scrolled into view when it\n * changes.\n */\n compactOnMobile?: boolean;\n className?: string;\n}\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n const totalSlots = siblings + 4;\n if (totalPages <= totalSlots) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n const start = Math.max(2, page - Math.floor(siblings / 2));\n const end = Math.min(totalPages - 1, start + siblings - 1);\n const range: (number | \"...\")[] = [1];\n if (start > 2) range.push(\"...\");\n for (let i = start; i <= end; i++) range.push(i);\n if (end < totalPages - 1) range.push(\"...\");\n range.push(totalPages);\n return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n page,\n totalPages,\n onPageChange,\n pageSize,\n onPageSizeChange,\n pageSizeOptions = [10, 25, 50, 100],\n totalItems,\n siblingCount = 3,\n compactOnMobile = true,\n className,\n}: PaginationProps) {\n const pages = useMemo(\n () => buildRange(page, totalPages, siblingCount),\n [page, totalPages, siblingCount],\n );\n const activePage = useRef<HTMLButtonElement | null>(null);\n\n useEffect(() => {\n if (compactOnMobile) return;\n activePage.current?.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n }, [page, compactOnMobile]);\n\n if (totalPages <= 1 && !onPageSizeChange) return null;\n\n return (\n <div className={cn(styles.wrapper, className)} data-compact={String(compactOnMobile)}>\n <div className={styles.summary}>\n {typeof totalItems === \"number\"\n ? `${totalItems} resultado${totalItems === 1 ? \"\" : \"s\"}`\n : `Página ${page} de ${totalPages}`}\n </div>\n <div className={styles.controls}>\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n aria-label=\"Página anterior\"\n >\n ‹\n </button>\n {pages.map((entry, index) =>\n entry === \"...\" ? (\n <span\n key={`ellipsis-${index}`}\n className={cn(styles.ellipsis, styles.numeric)}\n >\n …\n </span>\n ) : (\n <button\n type=\"button\"\n key={entry}\n ref={entry === page ? activePage : undefined}\n className={cn(\n styles.page,\n styles.numeric,\n entry === page && styles.active,\n )}\n onClick={() => onPageChange(entry)}\n aria-current={entry === page ? \"page\" : undefined}\n >\n {entry}\n </button>\n ),\n )}\n <button\n type=\"button\"\n className={styles.page}\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n aria-label=\"Próxima página\"\n >\n ›\n </button>\n {onPageSizeChange && (\n <select\n className={styles.sizeSelect}\n value={pageSize}\n onChange={(event) => onPageSizeChange(Number(event.target.value))}\n aria-label=\"Itens por página\"\n >\n {pageSizeOptions.map((opt) => (\n <option key={opt} value={opt}>\n {opt} / página\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}